diff --git a/mateclaw-server/src/main/java/vip/mate/acp/service/AcpRuntimeSupport.java b/mateclaw-server/src/main/java/vip/mate/acp/service/AcpRuntimeSupport.java index 53a237c6..b91aa096 100644 --- a/mateclaw-server/src/main/java/vip/mate/acp/service/AcpRuntimeSupport.java +++ b/mateclaw-server/src/main/java/vip/mate/acp/service/AcpRuntimeSupport.java @@ -84,22 +84,6 @@ public class AcpRuntimeSupport { * and the cost of a false positive (a slightly more verbose error * banner) is much smaller than a false negative (user staring at a * raw JSON-RPC blob). - * - *

Special case: a claude-code endpoint returning {@code 403 - * "Request not allowed"} is almost always the keychain-hijack - * scenario rather than a wrong API key. The third-party - * {@code @zed-industries/claude-agent-acp} package wraps - * {@code @anthropic-ai/claude-agent-sdk}, whose auth dispatcher - * checks the macOS keychain ({@code Claude Code-credentials}) / - * {@code ~/.claude/credentials.json} BEFORE the - * {@code ANTHROPIC_API_KEY} env var. So a host that's done - * {@code claude login} silently shadows whatever API key the user - * configured in the endpoint env, and Anthropic's API rejects the - * subscription OAuth token (first-party-only) with the very - * specific {@code "Request not allowed"} error string. We detect - * that exact combination and surface the keychain-clearing remedy - * instead of the generic "set ANTHROPIC_API_KEY" hint, which - * doesn't apply here. */ public String translateAuthError(AcpEndpointEntity endpoint, String originalMessage) { if (originalMessage == null) return null; @@ -115,32 +99,8 @@ public class AcpRuntimeSupport { || lower.contains("authenticate"); if (!looksLikeAuth) return null; - String name = endpoint != null && endpoint.getName() != null ? endpoint.getName() : "(unknown)"; - String slug = lower(name); - String command = endpoint != null ? lower(endpoint.getCommand()) : ""; - - // Keychain-hijack detection — must come before the generic env- - // missing branch because both would superficially match. - boolean keychainHijack = lower.contains("request not allowed") - && (slug.contains("claude") || command.contains("claude-agent-acp")); - if (keychainHijack) { - StringBuilder sb = new StringBuilder(); - sb.append("ACP endpoint '").append(name).append("' upstream auth failed with "); - sb.append("'Request not allowed' — almost always means the host CLI's OAuth "); - sb.append("credentials are hijacking the SDK auth path. "); - sb.append("The Claude Agent SDK reads ~/.claude/ / macOS keychain BEFORE the "); - sb.append("ANTHROPIC_API_KEY env var, so the API key you configured here is "); - sb.append("never sent — Anthropic rejects the subscription OAuth token because "); - sb.append("third-party processes aren't allowed to use it. "); - sb.append("To fix: "); - sb.append("(macOS) run `claude logout`, or `security delete-generic-password "); - sb.append("-s \"Claude Code-credentials\"`; "); - sb.append("(Linux / Windows) delete ~/.claude/credentials.json. "); - sb.append("Then click Test connection again. Original: ").append(originalMessage); - return sb.toString(); - } - String envVar = expectedAuthEnvVar(endpoint); + String name = endpoint != null && endpoint.getName() != null ? endpoint.getName() : "(unknown)"; StringBuilder sb = new StringBuilder(); sb.append("ACP endpoint '").append(name).append("' upstream auth failed. "); sb.append("Most likely the endpoint env has no API key. "); diff --git a/mateclaw-server/src/main/java/vip/mate/agent/binding/service/AgentBindingService.java b/mateclaw-server/src/main/java/vip/mate/agent/binding/service/AgentBindingService.java index 9ecf1166..198a074b 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/binding/service/AgentBindingService.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/binding/service/AgentBindingService.java @@ -228,41 +228,25 @@ public class AgentBindingService { * {@link #getEffectiveToolNames} allowlist completely. */ private static final Set SYSTEM_LEVEL_TOOLS = Set.of( - // Structured memory primitives — used by every agent regardless - // of skill bindings, otherwise the self-evolution path collapses - // (§11.3 / §11.4). + // Memory write/read primitives — every agent needs these + // regardless of skill bindings, otherwise the self-evolution + // path collapses (§11.3 / §11.4). "record_lesson", "remember", "remember_structured", "recall_structured", "forget_structured", - // Workspace memory file CRUD (PROFILE.md / MEMORY.md / SOUL.md / - // memory/YYYY-MM-DD.md). Prior versions whitelisted - // "read_workspace_file" / "write_workspace_file" / - // "list_workspace_files" — those names match no @Tool bean; the - // actual function names carry the "_memory" segment, so the - // earlier carve-out was silently dead. - "list_workspace_memory_files", - "read_workspace_memory_file", - "write_workspace_memory_file", - "edit_workspace_memory_file", + // Workspace memory file CRUD (PROFILE.md / MEMORY.md / SOUL.md) + "read_workspace_file", + "write_workspace_file", + "list_workspace_files", // Skill discovery / dispatch — skills are docs, not callables; // these helpers let the LLM read SKILL.md / run scripts. "readSkillFile", "runSkillScript", - "listSkillFiles", - "listAvailableSkills", - // Date / time — prior whitelist had a fictional "datetime"; the - // real DateTimeTool exposes three separate methods. - "getCurrentDate", - "getCurrentDateTime", - "getCurrentTime", - // Multi-agent delegation — prior whitelist had "delegate_agent", - // but DelegateAgentTool's @Tool methods are delegateToAgent / - // delegateParallel / listAvailableAgents. Same dead-name bug. - "delegateToAgent", - "delegateParallel", - "listAvailableAgents", + // Date/time + delegate — fundamental cross-skill utilities + "datetime", + "delegate_agent", // Document / media generation — agent-wide capabilities, never // declared inside any skill manifest. Pre-Phase-2b these were // universally visible; the new gate silently strips them whenever @@ -276,26 +260,7 @@ public class AgentBindingService { "renderDocxFromFiles", "image_generate", "music_generate", - "video_generate", - // Universal capabilities the global system prompts (SOUL.md / - // AGENTS.md / "Web Search Capability" / "File Reading Guidelines") - // explicitly tell the LLM exist. Pre-Phase-2b they were globally - // available; the new gate silently hid them on any agent with - // skills bound, so the prompt promises a tool the registry then - // refuses ("Tool not found: search"). Observed 2026-05-01 on the - // Code Reviewer agent — the model called search → got - // not-found → gave up before ever reaching renderDocx. - "search", - "browser_use", - "read_file", - "write_file", - "edit_file", - "execute_shell_command", - "detect_file_type", - "extract_document_text", - "extract_pdf_text", - "extract_docx_text", - "readMateClawDoc" + "video_generate" ); private ResolvedSkill findResolvedSkillById(Long skillId) { diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index ec2d22e2..b9a6cdef 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -1168,7 +1168,7 @@ export default { openai: 'OpenAI API key (sk-…). Get one at https://platform.openai.com/api-keys', dashscope: 'Aliyun DashScope key — same value as MateClaw .env DASHSCOPE_API_KEY', google: 'Google AI Studio API key. Get one at https://aistudio.google.com/apikey', - claudeOauth: 'Heads up: claude-code OAuth login (the one stored in ~/.claude/) does NOT work here, and worse — the Claude Agent SDK reads it BEFORE this env var, silently shadowing your API key. If you have ever run `claude login` on this host, clear the keychain first: (macOS) `claude logout` or `security delete-generic-password -s "Claude Code-credentials"`; (Linux/Windows) delete ~/.claude/credentials.json. Then put a real API key (sk-ant-…) above. Or switch to qwen-code / opencode to reuse a CLI login.', + claudeOauth: 'Heads up: claude-code OAuth login (the one stored in ~/.claude/) does NOT work here. The third-party Zed wrapper uses the public Anthropic API, which only accepts a real API key. Get one at https://console.anthropic.com/settings/keys, or switch to qwen-code / opencode if you want to reuse a CLI login.', }, }, modal: { diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index dd04f824..fea23f43 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -277,7 +277,7 @@ export default { mcpConnections: 'MCP 连接', toolsCatalog: '工具目录', activity: '活动记录', - acpEndpoints: 'ACP 端点', + acpEndpoints: 'ACP Endpoints', settingsGroup: '设置', agents: '智能体', security: '安全', @@ -1121,23 +1121,23 @@ export default { }, acp: { kicker: '外部 Agent', - title: 'ACP 端点', + title: 'ACP Endpoints', desc: '通过 stdio 委派给外部编码 Agent(codex / claude-code / opencode / qwen-code)。安装对应 CLI 后开启该入口。', - addEndpoint: '新增端点', + addEndpoint: '新增 Endpoint', builtin: '内置', test: '测试', testing: '测试中…', statusUnknown: '未测试', - empty: '暂无 ACP 端点。', - loadFailed: '加载 ACP 端点列表失败', - saveFailed: '保存端点失败', + empty: '暂无 ACP endpoint。', + loadFailed: '加载 ACP endpoint 列表失败', + saveFailed: '保存 endpoint 失败', deleteTitle: '确认删除', - deleteConfirm: '确认删除 ACP 端点 "{name}"?内置端点不可删除。', - deleteFailed: '删除端点失败', - toggleFailed: '切换端点状态失败', + deleteConfirm: '确认删除 ACP endpoint "{name}"?内置 endpoint 不可删除。', + deleteFailed: '删除 endpoint 失败', + toggleFailed: '切换 endpoint 状态失败', invalidJson: 'JSON 格式错误', columns: { - name: '端点', + name: 'Endpoint', command: '命令', status: '最近一次测试', enabled: '启用', @@ -1168,12 +1168,12 @@ export default { openai: 'OpenAI API key(sk-…)。申请: https://platform.openai.com/api-keys', dashscope: '阿里云 DashScope key — 跟 MateClaw .env 里的 DASHSCOPE_API_KEY 同一个值', google: 'Google AI Studio API key。申请: https://aistudio.google.com/apikey', - claudeOauth: '注意: claude-code 的 OAuth 登录(~/.claude/ 里那个)在这里不能用——更糟的是 Claude Agent SDK 会"优先读 OAuth、再读 API key",导致你下面填的 key 被静默忽略。如果你这台机器上跑过 `claude login`,先清钥匙串: (macOS) `claude logout` 或 `security delete-generic-password -s "Claude Code-credentials"`;(Linux/Windows) 删 ~/.claude/credentials.json。然后再回来填真正的 API key(sk-ant-…)。或者改用 qwen-code / opencode 复用各自 CLI 的本地登录态。', + claudeOauth: '注意: claude-code 的 OAuth 登录(~/.claude/ 里那个)在这里不能用。第三方 Zed wrapper 直接调 Anthropic 公开 API,只认真正的 API key。请到 https://console.anthropic.com/settings/keys 申请;或改用 qwen-code / opencode 复用各自 CLI 的本地登录态。', }, }, modal: { - newTitle: '新增 ACP 端点', - editTitle: '编辑 ACP 端点', + newTitle: '新增 ACP Endpoint', + editTitle: '编辑 ACP Endpoint', }, }, mcp: {