Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion console/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions console/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import { workspaceApi } from "./modules/workspace";
import { localModelApi } from "./modules/localModel";
import { ollamaModelApi } from "./modules/ollamaModel";
import { mcpApi } from "./modules/mcp";
import { acpApi } from "./modules/acp";
import { tokenUsageApi } from "./modules/tokenUsage";
import { toolsApi } from "./modules/tools";
import { securityApi } from "./modules/security";
import { approvalApi } from "./modules/approval";

export const api = {
// Root
Expand Down Expand Up @@ -64,13 +66,19 @@ export const api = {
// MCP Clients
...mcpApi,

// ACP
...acpApi,

// Token Usage
...tokenUsageApi,
// Tools
...toolsApi,

// Security
...securityApi,

// Approvals
...approvalApi,
};

export default api;
27 changes: 27 additions & 0 deletions console/src/api/modules/acp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { request } from "../request";
import type { ACPConfig, ParsedExternalAgent } from "../types";

export const acpApi = {
/**
* Get ACP configuration
*/
getACPConfig: () => request<ACPConfig>("/config/acp"),

/**
* Update ACP configuration
*/
updateACPConfig: (config: ACPConfig) =>
request<ACPConfig>("/config/acp", {
method: "PUT",
body: JSON.stringify(config),
}),

/**
* Parse external agent text to extract configuration
*/
parseExternalAgentText: (text: string) =>
request<ParsedExternalAgent>("/config/acp/parse-text", {
method: "POST",
body: JSON.stringify({ text }),
}),
};
37 changes: 37 additions & 0 deletions console/src/api/modules/approval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { request } from "../request";

export interface PendingApproval {
request_id: string;
session_id: string;
user_id: string;
channel: string;
tool_name: string;
status: string;
created_at: number;
result_summary: string;
findings_count: number;
extra: Record<string, unknown>;
}

export const approvalApi = {
getPendingApproval: (sessionId: string) =>
request<PendingApproval | null>(
`/approvals/pending?session_id=${encodeURIComponent(sessionId)}`,
),

approveRequest: (requestId: string) =>
request<PendingApproval>(
`/approvals/${encodeURIComponent(requestId)}/approve`,
{
method: "POST",
},
),

denyRequest: (requestId: string) =>
request<PendingApproval>(
`/approvals/${encodeURIComponent(requestId)}/deny`,
{
method: "POST",
},
),
};
51 changes: 51 additions & 0 deletions console/src/api/types/acp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* ACP (Agent Client Protocol) types
*/

/** Configuration for a single ACP harness */
export interface ACPHarnessConfig {
/** Whether this harness is enabled */
enabled: boolean;
/** Command to launch the harness */
command: string;
/** Arguments for the command */
args: string[];
/** Environment variables for the harness process */
env: Record<string, string>;
}

/** ACP (Agent Client Protocol) configuration */
export interface ACPConfig {
/** Global switch to enable/disable ACP functionality */
enabled: boolean;
/** Whether to require user approval before executing ACP tasks */
require_approval: boolean;
/** Directory to save ACP session states */
save_dir: string;
/** Available ACP harnesses */
harnesses: Record<string, ACPHarnessConfig>;
}

/** Harness info with key for UI display */
export interface ACPHarnessInfo extends ACPHarnessConfig {
/** Unique harness key identifier */
key: string;
/** Harness display name */
name: string;
}

/** Parsed external agent configuration from text */
export interface ParsedExternalAgent {
/** Whether external agent is enabled */
enabled: boolean;
/** Harness identifier (e.g., 'opencode', 'qwen') */
harness: string | null;
/** Whether to keep session alive */
keep_session: boolean;
/** Working directory for the agent */
cwd: string | null;
/** Existing session ID to resume */
existing_session_id: string | null;
/** Cleaned prompt text */
prompt: string | null;
}
1 change: 1 addition & 0 deletions console/src/api/types/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface ChatSpec {
id: string; // Chat UUID identifier
name?: string; // Optional chat display name
session_id: string; // Session identifier (channel:user_id format)
user_id: string; // User identifier
channel: string; // Channel name, default: "default"
Expand Down
1 change: 1 addition & 0 deletions console/src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from "./chat";
export * from "./cronjob";
export * from "./env";
export * from "./mcp";
export * from "./acp";
export * from "./provider";
export * from "./skill";
export * from "./workspace";
Expand Down
4 changes: 2 additions & 2 deletions console/src/components/MarkdownCopy/MarkdownCopy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ export function MarkdownCopy({
localShowMarkdown && !(editable && !textareaProps.disabled)
? content
: editable
? editContent
: content;
? editContent
: content;

if (!contentToCopy) return;

Expand Down
3 changes: 3 additions & 0 deletions console/src/layouts/MainLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import SkillsPage from "../../pages/Agent/Skills";
import ToolsPage from "../../pages/Agent/Tools";
import WorkspacePage from "../../pages/Agent/Workspace";
import MCPPage from "../../pages/Agent/MCP";
import ACPPage from "../../pages/Agent/ACP";
import ModelsPage from "../../pages/Settings/Models";
import EnvironmentsPage from "../../pages/Settings/Environments";
import SecurityPage from "../../pages/Settings/Security";
Expand All @@ -31,6 +32,7 @@ const pathToKey: Record<string, string> = {
"/skills": "skills",
"/tools": "tools",
"/mcp": "mcp",
"/acp": "acp",
"/workspace": "workspace",
"/agents": "agents",
"/models": "models",
Expand Down Expand Up @@ -78,6 +80,7 @@ export default function MainLayout() {
<Route path="/skills" element={<SkillsPage />} />
<Route path="/tools" element={<ToolsPage />} />
<Route path="/mcp" element={<MCPPage />} />
<Route path="/acp" element={<ACPPage />} />
<Route path="/workspace" element={<WorkspacePage />} />
<Route path="/models" element={<ModelsPage />} />
<Route path="/environments" element={<EnvironmentsPage />} />
Expand Down
9 changes: 6 additions & 3 deletions console/src/layouts/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
Copy,
Check,
BarChart3,
Bot,
} from "lucide-react";
import api from "../api";
import styles from "./index.module.less";
Expand All @@ -60,6 +61,7 @@ const KEY_TO_PATH: Record<string, string> = {
skills: "/skills",
tools: "/tools",
mcp: "/mcp",
acp: "/acp",
workspace: "/workspace",
models: "/models",
environments: "/environments",
Expand Down Expand Up @@ -255,8 +257,8 @@ export default function Sidebar({ selectedKey }: SidebarProps) {
const lang = i18n.language?.startsWith("zh")
? "zh"
: i18n.language?.startsWith("ru")
? "ru"
: "en";
? "ru"
: "en";
const faqLang = lang === "zh" ? "zh" : "en";
const url = `https://copaw.agentscope.io/docs/faq.${faqLang}.md`;
fetch(url, { cache: "no-cache" })
Expand All @@ -268,7 +270,7 @@ export default function Sidebar({ selectedKey }: SidebarProps) {
setUpdateMarkdown(
match && lang !== "ru"
? match[0].trim()
: UPDATE_MD[lang] ?? UPDATE_MD.en,
: (UPDATE_MD[lang] ?? UPDATE_MD.en),
);
})
.catch(() => {
Expand Down Expand Up @@ -325,6 +327,7 @@ export default function Sidebar({ selectedKey }: SidebarProps) {
{ key: "skills", label: t("nav.skills"), icon: <Sparkles size={16} /> },
{ key: "tools", label: t("nav.tools"), icon: <Wrench size={16} /> },
{ key: "mcp", label: t("nav.mcp"), icon: <Plug size={16} /> },
{ key: "acp", label: t("nav.acp"), icon: <Bot size={16} /> },
{
key: "agent-config",
label: t("nav.agentConfig"),
Expand Down
45 changes: 45 additions & 0 deletions console/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"skills": "Skills",
"tools": "Tools",
"mcp": "MCP",
"acp": "ACP",
"agentConfig": "Configuration",
"settings": "Settings",
"models": "Models",
Expand Down Expand Up @@ -121,6 +122,50 @@
"deleteSuccess": "MCP client deleted successfully",
"deleteError": "Failed to delete MCP client"
},
"acp": {
"title": "ACP Configuration",
"description": "Manage ACP (Agent Client Protocol) external agent protocol configuration.",
"globalSettings": "Global Settings",
"enabled": "Enable ACP",
"enabledDescription": "When enabled, CoPaw can invoke external agents via ACP protocol (e.g., OpenCode, Qwen-code)",
"requireApproval": "Require user approval",
"requireApprovalDescription": "When enabled, ACP tasks require user confirmation before execution",
"saveDir": "Session Save Directory",
"saveDirDescription": "Directory path for saving ACP session states",
"harnesses": "Harness List",
"createHarness": "Add Harness",
"editHarness": "Edit Harness",
"harnessKey": "Harness Key",
"harnessKeyHelp": "Unique harness identifier, e.g., opencode, qwen",
"command": "Command",
"commandHelp": "Command to launch the harness, e.g., npx",
"args": "Arguments",
"argsHelp": "Command arguments, separated by spaces",
"envVars": "Environment Variables",
"envVarsCount": "variables",
"addEnvVar": "Add Environment Variable",
"envKeyPlaceholder": "Variable name",
"envValuePlaceholder": "Variable value",
"emptyState": "No harnesses configured yet",
"loadError": "Failed to load ACP configuration",
"saveSuccess": "ACP configuration saved successfully",
"saveError": "Failed to save ACP configuration",
"createSuccess": "Harness created successfully",
"enableSuccess": "Harness enabled successfully",
"disableSuccess": "Harness disabled successfully",
"deleteSuccess": "Harness deleted successfully",
"deleteConfirm": "Are you sure you want to delete this harness?",
"keyExists": "This harness key already exists",
"approval": {
"title": "Waiting for external agent approval",
"defaultMessage": "External agent is waiting for approval.",
"harness": "Harness",
"tool": "Tool",
"kind": "Kind",
"target": "Target",
"instruction": "Type /approve to allow, or send any other message to deny."
}
},
"heartbeat": {
"title": "Heartbeat",
"description": "Run HEARTBEAT.md at a fixed interval for self-checks. By default runs silently without affecting current conversations, or optionally send replies to the last chat channel.",
Expand Down
45 changes: 45 additions & 0 deletions console/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"workspace": "ワークスペース",
"skills": "スキル",
"mcp": "MCP",
"acp": "ACP",
"agentConfig": "設定",
"settings": "設定",
"models": "モデル",
Expand Down Expand Up @@ -115,6 +116,50 @@
"deleteSuccess": "MCPクライアントを削除しました",
"deleteError": "MCPクライアントの削除に失敗しました"
},
"acp": {
"title": "ACP設定",
"description": "ACP(Agent Client Protocol)外部エージェントプロトコル設定を管理します。",
"globalSettings": "グローバル設定",
"enabled": "ACPを有効化",
"enabledDescription": "有効にすると、CoPawはACPプロトコルで外部エージェント(OpenCode、Qwen-codeなど)を呼び出せます",
"requireApproval": "実行前にユーザー承認が必要",
"requireApprovalDescription": "有効にすると、ACPタスク実行前にユーザー確認が必要です",
"saveDir": "セッション保存ディレクトリ",
"saveDirDescription": "ACPセッション状態を保存するディレクトリパス",
"harnesses": "Harnessリスト",
"createHarness": "Harnessを追加",
"editHarness": "Harnessを編集",
"harnessKey": "Harness識別子",
"harnessKeyHelp": "一意のHarness識別子(例:opencode、qwen)",
"command": "起動コマンド",
"commandHelp": "Harnessを起動するコマンド(例:npx)",
"args": "コマンド引数",
"argsHelp": "起動コマンドの引数(スペース区切り)",
"envVars": "環境変数",
"envVarsCount": "個の変数",
"addEnvVar": "環境変数を追加",
"envKeyPlaceholder": "変数名",
"envValuePlaceholder": "変数値",
"emptyState": "Harnessが設定されていません",
"loadError": "ACP設定の読み込みに失敗しました",
"saveSuccess": "ACP設定を保存しました",
"saveError": "ACP設定の保存に失敗しました",
"createSuccess": "Harnessを作成しました",
"enableSuccess": "Harnessを有効化しました",
"disableSuccess": "Harnessを無効化しました",
"deleteSuccess": "Harnessを削除しました",
"deleteConfirm": "このHarnessを削除してもよろしいですか?",
"keyExists": "このHarness識別子は既に存在します",
"approval": {
"title": "外部エージェントの承認待ち",
"defaultMessage": "外部エージェントが承認を待っています。",
"harness": "Harness",
"tool": "ツール",
"kind": "種類",
"target": "ターゲット",
"instruction": "/approve と入力して許可するか、他のメッセージを送信して拒否してください。"
}
},
"heartbeat": {
"title": "ハートビート",
"description": "一定間隔でHEARTBEAT.mdを実行してセルフチェックを行います。デフォルトでは現在の会話に影響を与えずサイレントに動作しますが、オプションで最後のチャットチャンネルに返信を送ることもできます。",
Expand Down
Loading