From daed5ccf6216c23d6ecfe4d97cd7ecdfe83bea3d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 17 Sep 2026 06:14:14 +0000 Subject: [PATCH 1/4] fix(chat): surface Kiro slash commands from vendor ACP notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kiro declares native commands on `_kiro.dev/commands/available` after session/new, not standard available_commands_update. Merge commands[] into Options so a ready Kiro session lists 对方命令 like Grok. Keep prompts/skills/tools out of bare `/` and fail-closed when undeclared. Co-authored-by: NiceChen --- .../src/services/chat_runtime/actor_tests.rs | 116 ++++++++++++++++++ .../src/services/chat_runtime/mod.rs | 41 +++++-- .../src/services/chat_runtime/tests.rs | 40 ++++++ .../src/utils/stream_parse/acp.rs | 20 ++- .../src/utils/stream_parse/grok/tests.rs | 39 ++++++ docs/STATUS.md | 2 +- docs/concepts/chat-process-events.md | 4 +- docs/reference/chat-session-options.md | 4 +- src/pages/chat/chat-actions.test.ts | 20 +++ 9 files changed, 269 insertions(+), 17 deletions(-) diff --git a/crates/agenthub-core/src/services/chat_runtime/actor_tests.rs b/crates/agenthub-core/src/services/chat_runtime/actor_tests.rs index 50517bf6..7f3ff93e 100644 --- a/crates/agenthub-core/src/services/chat_runtime/actor_tests.rs +++ b/crates/agenthub-core/src/services/chat_runtime/actor_tests.rs @@ -358,6 +358,122 @@ fn grok_available_commands_update_fills_catalog_not_timeline() { assert!(cache.catalog_epoch >= 1); } +#[test] +fn kiro_vendor_commands_available_fills_catalog_not_timeline() { + let db = Database::open_in_memory().unwrap(); + conversation_with(&db, "kiro-cmds", AgentId::Kiro, &std::env::temp_dir()); + let mut worker = worker(&db, "kiro-cmds"); + worker.agent = AgentId::Kiro; + worker.store.enable_if_new("kiro-cmds").unwrap(); + start_placeholder(&mut worker); + + worker + .notification( + "_kiro.dev/commands/available", + &json!({ + "sessionId": "sess-kiro", + "commands": [{ + "name": "/context", + "description": "Add context", + "meta": { "hint": "path" } + }, { + "name": "/compact", + "description": "Compact context", + "meta": { "hint": "[instructions]" } + }], + "prompts": [{ + "name": "my-skill", + "description": "A skill", + "serverName": "skill:config" + }], + "tools": [{ "name": "read" }], + "mcpServers": [] + }), + ) + .unwrap(); + + let snapshot = worker.store.snapshot("kiro-cmds", None).unwrap(); + assert!(!snapshot.events.iter().any(|event| matches!( + &event.event, + ChatEvent::AgentProcess { .. } + ))); + let cache = worker + .catalogs + .lock() + .unwrap() + .get("kiro-cmds") + .cloned() + .expect("catalog"); + assert_eq!( + cache + .native_commands + .iter() + .map(|command| command.name.as_str()) + .collect::>(), + ["context", "compact"] + ); + assert_eq!(cache.native_commands[0].hint.as_deref(), Some("path")); + assert_eq!( + cache.native_commands[1].hint.as_deref(), + Some("[instructions]") + ); + assert!(cache.catalog_epoch >= 1); + + worker + .notification( + "_kiro.dev/commands/available", + &json!({ + "sessionId": "sess-kiro", + "commands": [], + "prompts": [{ "name": "my-skill" }] + }), + ) + .unwrap(); + let cleared = worker + .catalogs + .lock() + .unwrap() + .get("kiro-cmds") + .cloned() + .expect("catalog"); + assert!(cleared.native_commands.is_empty()); +} + +#[test] +fn kiro_standard_available_commands_update_also_fills_catalog() { + let db = Database::open_in_memory().unwrap(); + conversation_with(&db, "kiro-acp-cmds", AgentId::Kiro, &std::env::temp_dir()); + let mut worker = worker(&db, "kiro-acp-cmds"); + worker.agent = AgentId::Kiro; + worker.store.enable_if_new("kiro-acp-cmds").unwrap(); + start_placeholder(&mut worker); + + worker + .notification( + "session/update", + &json!({ + "update": { + "sessionUpdate": "available_commands_update", + "availableCommands": [{ + "name": "compact", + "description": "Compact context" + }] + } + }), + ) + .unwrap(); + + let cache = worker + .catalogs + .lock() + .unwrap() + .get("kiro-acp-cmds") + .cloned() + .expect("catalog"); + assert_eq!(cache.native_commands.len(), 1); + assert_eq!(cache.native_commands[0].name, "compact"); +} + #[test] fn grok_config_option_update_fills_models_not_timeline() { let db = Database::open_in_memory().unwrap(); diff --git a/crates/agenthub-core/src/services/chat_runtime/mod.rs b/crates/agenthub-core/src/services/chat_runtime/mod.rs index 459ba340..8effa3f4 100644 --- a/crates/agenthub-core/src/services/chat_runtime/mod.rs +++ b/crates/agenthub-core/src/services/chat_runtime/mod.rs @@ -2673,6 +2673,9 @@ impl ActorWorker { "session/update" | "session_update" | "_x.ai/session/update" => { self.grok_session_update(params)?; } + "_kiro.dev/commands/available" => { + self.apply_kiro_available_commands(params); + } "_x.ai/session/prompt_complete" => { self.turn_completed(params)?; } @@ -3099,19 +3102,37 @@ impl ActorWorker { } } + fn apply_native_commands( + &self, + commands: Vec, + ) { + self.patch_catalog(|cache| { + cache.native_commands = commands + .into_iter() + .map(|command| RuntimeNativeCommand { + name: command.name, + description: command.description, + hint: command.hint, + }) + .collect(); + }); + } + + /// Kiro declares slash commands on this vendor notification after + /// `session/new`. Standard ACP `available_commands_update` is still + /// accepted via [`Self::grok_session_update`]. Skills/prompts are ignored. + fn apply_kiro_available_commands(&self, params: &Value) { + if let Some(commands) = + crate::utils::stream_parse::acp::extract_kiro_available_commands(params) + { + self.apply_native_commands(commands); + } + } + fn grok_session_update(&mut self, params: &Value) -> Result<()> { if let Some(commands) = crate::utils::stream_parse::acp::extract_available_commands(params) { - self.patch_catalog(|cache| { - cache.native_commands = commands - .into_iter() - .map(|command| RuntimeNativeCommand { - name: command.name, - description: command.description, - hint: command.hint, - }) - .collect(); - }); + self.apply_native_commands(commands); } if let Some(catalog) = crate::utils::stream_parse::acp::extract_config_catalog(params) { self.apply_acp_config_catalog(catalog); diff --git a/crates/agenthub-core/src/services/chat_runtime/tests.rs b/crates/agenthub-core/src/services/chat_runtime/tests.rs index 248d28ac..49fd690f 100644 --- a/crates/agenthub-core/src/services/chat_runtime/tests.rs +++ b/crates/agenthub-core/src/services/chat_runtime/tests.rs @@ -594,6 +594,46 @@ fn grok_options_surface_seeded_native_commands_and_handshake_image() { assert_eq!(again.catalog_epoch, first.catalog_epoch); } +#[test] +fn kiro_options_keep_injected_native_commands_across_catalog_refresh() { + let db = Database::open_in_memory().unwrap(); + let now = "2026-01-01T00:00:00Z".to_string(); + ChatRepo::new(db.clone()) + .create_conversation(&Conversation { + id: "kiro-opts".into(), + title: String::new(), + agent_ids: vec![AgentId::Kiro], + cwd: Some(std::env::temp_dir().to_string_lossy().into_owned()), + allow_dangerous: false, + created_at: now.clone(), + updated_at: now, + native_session_id: None, + sending: false, + first_user_content: None, + }) + .unwrap(); + let run = Arc::new(RunService::new(AdapterRegistry::default())); + let runtime = Arc::new(ChatRuntime::new(db, run)); + let empty = runtime.options("kiro-opts").unwrap(); + assert_eq!(empty.transport, RuntimeChannel::Acp); + assert!(empty.native_commands.is_empty()); + assert!(!empty.session_ready); + + runtime.seed_native_commands_for_test( + "kiro-opts", + vec![RuntimeNativeCommand { + name: "context".into(), + description: "Add context".into(), + hint: Some("path".into()), + }], + ); + let refreshed = runtime.refresh_options("kiro-opts").unwrap(); + assert_eq!(refreshed.transport, RuntimeChannel::Acp); + assert_eq!(refreshed.native_commands.len(), 1); + assert_eq!(refreshed.native_commands[0].name, "context"); + assert!(!refreshed.session_ready); +} + #[test] fn started_runtime_rejects_agent_and_cwd_changes() { let dir = tempdir().unwrap(); diff --git a/crates/agenthub-core/src/utils/stream_parse/acp.rs b/crates/agenthub-core/src/utils/stream_parse/acp.rs index 31f63b42..76d174db 100644 --- a/crates/agenthub-core/src/utils/stream_parse/acp.rs +++ b/crates/agenthub-core/src/utils/stream_parse/acp.rs @@ -128,6 +128,17 @@ pub(crate) fn extract_available_commands(v: &Value) -> Option Option> { + let payload = v.get("data").or_else(|| v.get("params")).unwrap_or(v); + let list = payload.get("commands").and_then(|value| value.as_array())?; + Some(list.iter().filter_map(parse_available_command).collect()) +} + /// Model/effort lists from ACP config options. Not a `ProcessStep`. #[derive(Debug, Clone, PartialEq, Eq, Default)] pub(crate) struct AcpConfigCatalog { @@ -267,17 +278,22 @@ fn parse_plan_entry(value: &Value) -> Option { } fn parse_available_command(value: &Value) -> Option { - let name = first_str(value, &["name", "command"])?; + let raw = first_str(value, &["name", "command"])?; + let name = raw.trim_start_matches('/').trim(); + if name.is_empty() { + return None; + } let description = first_str(value, &["description"]).unwrap_or_default(); let hint = value .pointer("/input/hint") + .or_else(|| value.pointer("/meta/hint")) .and_then(|h| h.as_str()) .or_else(|| value.get("hint").and_then(|h| h.as_str())) .map(str::trim) .filter(|s| !s.is_empty()) .map(str::to_string); Some(AcpAvailableCommand { - name, + name: name.to_string(), description, hint, }) diff --git a/crates/agenthub-core/src/utils/stream_parse/grok/tests.rs b/crates/agenthub-core/src/utils/stream_parse/grok/tests.rs index 218e6043..70aab9f5 100644 --- a/crates/agenthub-core/src/utils/stream_parse/grok/tests.rs +++ b/crates/agenthub-core/src/utils/stream_parse/grok/tests.rs @@ -284,6 +284,45 @@ fn extract_available_commands_is_none_for_thought() -> bool { .is_none() } +#[test] +fn kiro_vendor_commands_available_is_catalog_not_process_step() { + let payload = serde_json::json!({ + "sessionId": "sess-1", + "commands": [ + { + "name": "/context", + "description": "Add context", + "meta": { "hint": "path" } + }, + { "name": "/", "description": "empty name" } + ], + "prompts": [ + { + "name": "my-skill", + "description": "A skill", + "serverName": "skill:config" + } + ], + "tools": [{ "name": "read" }], + "mcpServers": [] + }); + let commands = super::super::acp::extract_kiro_available_commands(&payload).unwrap(); + assert_eq!(commands.len(), 1); + assert_eq!(commands[0].name, "context"); + assert_eq!(commands[0].description, "Add context"); + assert_eq!(commands[0].hint.as_deref(), Some("path")); + assert!(super::super::acp::extract_available_commands(&payload).is_none()); + assert!(super::super::acp::extract_kiro_available_commands(&serde_json::json!({ + "prompts": [{ "name": "my-skill" }] + })) + .is_none()); + let empty = super::super::acp::extract_kiro_available_commands(&serde_json::json!({ + "commands": [] + })) + .unwrap(); + assert!(empty.is_empty()); +} + #[test] fn tool_kind_without_title_maps_to_read_edit_or_execute() { let read = parse_line( diff --git a/docs/STATUS.md b/docs/STATUS.md index 46a0186d..280eafa0 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -26,7 +26,7 @@ updated: 2026-09-17 - **其余 Agent 与旧会话**:仍走原发送方式。 - **过程面板**:主列一行过程摘要(正在读取 / 正在修改 / 正在执行,完成则已读取 / 已修改 / 已执行)。点开后在右侧栏看你说了什么、思考、工具行、等待允许或拒绝、本轮用量;工具名、状态和 JSON 进折叠的「细节」;命令、过程日志、退出码和状态事件仍在「运行详情」。允许 / 拒绝按钮仍在卡片上,不在过程行上造假按钮。右侧栏不随发送自动打开;点 Markdown 仍预览文件。种类约定见 [过程事件](concepts/chat-process-events.md)。斜杠目录和模型目录更新不进过程时间线。Grok / Kiro 若推了当前轮 `plan`,输入区上方出现计划条,换轮丢掉。ACP 若声明 `terminal`,对方跑的那条命令一张卡片,可停这一条(不是对话页终端)。仍约 80ms 读快照,过程行按序号增量挂现有面板,不另开总线。 - **过程内用量**:新空 Codex 会话仍解析 `thread/tokenUsage/updated` 的当前轮 `last`(累计 `total` / 窗口只留在总览等用量页)。新空 Grok 会话解析 **当前轮**(`turn_completed.usage`);ACP 没有会话累计字段,不把各轮相加冒充累计。Grok / Kiro 的 `context_usage` 有数字才进用量小字(窗口用量),全 0 不画。解析路径已接;真窗 2026-09-09 见过部分轮次 **没有** `turn_completed.usage`,此时界面不画假数字。对话里只在本轮结束后用小字写输入 / 输出(有缓存才写缓存);生成中不画用量。只显示协议里的数字,不估算费用。Kiro 没有 token 累计数据源。 - - **`/` 菜单**:立刻执行的动作(新建对话、复制最近回复;换模型/思考/技能要搜到才列出,避免把整份目录摊在 `/` 上)。Grok / Kiro 会话就绪且对方声明了命令时,另列对方斜杠项:选中后当作一轮正常发出(无必填参数则直接发送 `/名字`;必填参数则插入 `/名字 ` 供补全后再发)。目录变了会重拉;未就绪或未声明则不画,不猜菜单。没有真窗证据前,不宣称「对方命令已验收」。有可启动的命令行时,`/` 可列出「启动命令行」(DeepSeek 为「打开网页会话」),在外部打开,不标成对话页能力。 + - **`/` 菜单**:立刻执行的动作(新建对话、复制最近回复;换模型/思考/技能要搜到才列出,避免把整份目录摊在 `/` 上)。Grok / Kiro 会话就绪且对方声明了命令时,另列对方斜杠项:选中后当作一轮正常发出(无必填参数则直接发送 `/名字`;必填参数则插入 `/名字 ` 供补全后再发)。Grok 走标准 ACP `available_commands_update`;Kiro 走 `_kiro.dev/commands/available` 的 `commands[]`(不把技能/工具目录摊进 `/`)。目录变了会重拉;未就绪或未声明则不画,不猜菜单。没有真窗证据前,不宣称「对方命令已验收」。有可启动的命令行时,`/` 可列出「启动命令行」(DeepSeek 为「打开网页会话」),在外部打开,不标成对话页能力。 - **本机对接与本会话**:本机持续通道按 Agent 写死:新空 Codex 走 app-server,新空 Grok / Kiro 走 ACP,新空 Claude 走 stream-json;其余与旧会话仍走原发送方式。Cursor 默认软隐藏,不在允许/拒绝之列,也不进持续聊天白名单。**Agents 详情**写这份 Agent 的新对话怎么接(ACP / 持续对话 / 原来的发送方式),不是一份可改的「ACP 总表」。**Chat 顶栏和会话设置**写这次对话实际在走哪条;点了卡片上的一直允许之后,会话设置里可以关掉「本会话已一直允许」(不能在这里假装打开)。一次对话是否在用持续通道,看这次会话是不是上述新空路径。Kiro 旧对话没有切到 ACP 的入口。会话字段见 [会话身份](concepts/chat-session-identity.md)。 - **新空 Claude 会话(B3 首片)**:走 Claude Code `-p --input-format stream-json --output-format stream-json` 持续通道(同进程多轮、本地图片 base64、模型/思考强度参数);**不支持**生成中补充;本片**不**接可点允许/拒绝(默认 `dontAsk`,危险模式 `bypassPermissions`)。有历史的旧 Claude 会话仍走 print+resume。print 路径在已经出过 assistant 正文后不再把最终 `result` 再拼进气泡(短回复不会同一句写两遍);只有没见过 assistant 文本时才用 `result` 当正文。Linux 真窗短回复已验不双写。见 [Claude B3](archive/chat-claude-b3.md)。 - **对话标题**:新建对话先用首条消息提炼的短句。一轮结束后读对方写在自己会话记录里的标题并改用它:Codex(app-server 汇总的 `sqlite/*.db` 里 `local_thread_catalog.display_title`,退回 `session_index.jsonl` 的 `thread_name`)、Grok(`summary.json` 的 `generated_title`)、Kiro(`sessions/cli/.json` 的 `title`)、DSH(会话日志的 `session/title` 行)。Claude 没有标题来源,保持首条消息推导。手动改过名字的对话不再被覆盖(不新增「谁起的名字」列,也不做迁移);连续通道取运行时线程 id,旧会话取 `native_session_id`。AgentHub 不进协议里要标题。实机核对:Grok 与 Codex 的真实会话都能取到;Codex 的 `session_index.jsonl` 只收 IDE / 桌面端自己建的会话,所以优先读 `local_thread_catalog`;Kiro 常见只有 1–2 字的占位;DSH 普通发送不带会话 id,实际触发不到;Claude 无来源。 diff --git a/docs/concepts/chat-process-events.md b/docs/concepts/chat-process-events.md index c6a69df5..389cf6ec 100644 --- a/docs/concepts/chat-process-events.md +++ b/docs/concepts/chat-process-events.md @@ -5,7 +5,7 @@ status: current owner: maintainers audience: chat and core contributors source-of-truth: ProcessStep, ChatEvent, RuntimeSnapshot -updated: 2026-09-16 +updated: 2026-09-17 --- # Chat 过程事件 @@ -33,7 +33,7 @@ updated: 2026-09-16 下列进 Options 或专用条,**禁止**推进过程时间线冒充「对方做了一步」: -- 斜杠命令目录(`available_commands` / `nativeCommands`) +- 斜杠命令目录(`available_commands` / Kiro `_kiro.dev/commands/available` / `nativeCommands`) - ACP `config_option_update`(模型 / 思考目录) - 世代号 `catalogEpoch`(只用来重拉 Options) diff --git a/docs/reference/chat-session-options.md b/docs/reference/chat-session-options.md index 9c1a6141..5adde4fc 100644 --- a/docs/reference/chat-session-options.md +++ b/docs/reference/chat-session-options.md @@ -4,7 +4,7 @@ description: Runtime Options 的 seed / 探测来源与 fail-closed 边界。 type: reference status: current owner: maintainers -updated: 2026-09-16 +updated: 2026-09-17 --- # Chat 会话选项目录 @@ -29,7 +29,7 @@ Chat「模型 / 思考 / 扩展 / 斜杠原生命令」等来自会话 **Options | Claude | `claude_fallback_catalog`(seed) | 无活探测权威时仅用 seed | | Codex(及其他走 app-server 探测的路径) | `model/list`、`skills/list`、`plugin/installed` | 探测失败 → 空/缓存策略;不伪装厂商未声明的项 | -原生命令目录(斜杠)由持续通道在会话就绪后注入 `native_commands`;未就绪或目录空 → UI fail-closed(不假装有对方命令)。立刻执行的动作(如新建对话)与对方命令分来源,见 STATUS / Chat 概念页。`transport` 会画在 Chat 顶栏和会话设置「这次对话怎么接」,不进过程时间线。 +原生命令目录(斜杠)由持续通道在会话就绪后注入 `native_commands`;未就绪或目录空 → UI fail-closed(不假装有对方命令)。Grok 走标准 ACP `available_commands_update`;Kiro 在 `session/new` 之后用厂商通知 `_kiro.dev/commands/available` 的 `commands[]`(不要把 `prompts` / 技能、`tools`、`mcpServers` 摊进 `/`)。立刻执行的动作(如新建对话)与对方命令分来源,见 STATUS / Chat 概念页。`transport` 会画在 Chat 顶栏和会话设置「这次对话怎么接」,不进过程时间线。 ## 纪律 diff --git a/src/pages/chat/chat-actions.test.ts b/src/pages/chat/chat-actions.test.ts index 69838c72..023fb421 100644 --- a/src/pages/chat/chat-actions.test.ts +++ b/src/pages/chat/chat-actions.test.ts @@ -112,6 +112,26 @@ describe('chat action command search', () => { expect(chatOverflowMenuActions(extra).some((item) => item.kind === 'native')).toBe(true); }); + it('merges Kiro-style leading-slash names without dumping skills onto bare /', () => { + const extra = nativeCommandActions([ + { name: '/context', description: 'Add context', hint: 'path' }, + { name: '/compact', description: 'Compact context', hint: '[instructions]' }, + ]); + expect(extra.map((item) => item.id)).toEqual([ + 'native-command:context', + 'native-command:compact', + ]); + expect(extra[0]?.draftText).toBe('/context '); + expect(extra[1]?.draftText).toBe('/compact'); + expect(filterChatActions('/', extra).map((item) => item.id)).toEqual([ + 'native-command:context', + 'native-command:compact', + 'new-session', + 'copy-latest-reply', + ]); + expect(filterChatActions('/', extra).some((item) => /skill|技能|my-skill/i.test(item.id + item.label))).toBe(false); + }); + it('lists the external command-line escape hatch without sending', () => { const extra = [openExternalCliAction({ label: '启动命令行', From 6754bc05503db14c0793992c07e6346ecbeb6c00 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 17 Sep 2026 06:24:40 +0000 Subject: [PATCH 2/4] fix(catalog): align Grok MCP reason fixture with product copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shared catalog-mirror expected "待验证接入" while core already advertises writable local MCP config. Update the fixture; do not revert product text. Co-authored-by: NiceChen --- src/lib/backend/contracts/catalog-mirror-contract.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/backend/contracts/catalog-mirror-contract.json b/src/lib/backend/contracts/catalog-mirror-contract.json index b1c8acd6..52abceb7 100644 --- a/src/lib/backend/contracts/catalog-mirror-contract.json +++ b/src/lib/backend/contracts/catalog-mirror-contract.json @@ -105,7 +105,7 @@ "projectDelete": null, "providerPresets": null, "usage": null, - "mcp": "待验证接入", + "mcp": "可写入本机 MCP 配置(目录/探测/启用;无 OAuth)", "modelSelect": "对话页可选模型和思考等级", "sessionResume": "Chat 后续轮次走 print+resume;终端可复制官方续接命令" }, @@ -267,7 +267,7 @@ "projectDelete": null, "providerPresets": null, "usage": null, - "mcp": "待验证接入", + "mcp": "可写入本机 MCP 配置(目录/探测/启用;无 OAuth)", "modelSelect": "对话页可选模型和思考等级", "sessionResume": "Chat 后续轮次走 print+resume;终端可复制官方续接命令" }, From 932d0c7b2b51649af99df0eb1dc71fcff678c9ae Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 17 Sep 2026 06:25:09 +0000 Subject: [PATCH 3/4] fix(catalog): align mock Grok MCP reason with core copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mock capabilities still said planned/待验证接入 after the shared catalog fixture adopted writable local MCP text. Co-authored-by: NiceChen --- src/dev/mocks/capabilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/mocks/capabilities.ts b/src/dev/mocks/capabilities.ts index 7430e7d4..437fcef8 100644 --- a/src/dev/mocks/capabilities.ts +++ b/src/dev/mocks/capabilities.ts @@ -74,7 +74,7 @@ export const MOCK_CAPABILITIES: Record = { projectDelete: { level: 'full' }, providerPresets: { level: 'full' }, usage: { level: 'full' }, - mcp: { level: 'planned', reason: '待验证接入' }, + mcp: { level: 'partial', reason: '可写入本机 MCP 配置(目录/探测/启用;无 OAuth)' }, modelSelect: { level: 'partial', reason: '对话页可选模型和思考等级' }, sessionResume: { level: 'partial', From d55ec0f330130de1bac3eb7ce6b9dc09f75ac2a0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 17 Sep 2026 06:37:08 +0000 Subject: [PATCH 4/4] =?UTF-8?q?docs(status):=20record=20Kiro=20slash=20?= =?UTF-8?q?=E5=AF=B9=E6=96=B9=E5=91=BD=E4=BB=A4=20true-window=20PASS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the stale “no true-window evidence” line. Ready Kiro lists declared native commands on tip daed5ccf; skills catalog stays off bare `/`. Point to the QA retest note, without overclaiming send or other agents. Co-authored-by: NiceChen --- docs/STATUS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/STATUS.md b/docs/STATUS.md index 280eafa0..1c7f63a6 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -26,7 +26,7 @@ updated: 2026-09-17 - **其余 Agent 与旧会话**:仍走原发送方式。 - **过程面板**:主列一行过程摘要(正在读取 / 正在修改 / 正在执行,完成则已读取 / 已修改 / 已执行)。点开后在右侧栏看你说了什么、思考、工具行、等待允许或拒绝、本轮用量;工具名、状态和 JSON 进折叠的「细节」;命令、过程日志、退出码和状态事件仍在「运行详情」。允许 / 拒绝按钮仍在卡片上,不在过程行上造假按钮。右侧栏不随发送自动打开;点 Markdown 仍预览文件。种类约定见 [过程事件](concepts/chat-process-events.md)。斜杠目录和模型目录更新不进过程时间线。Grok / Kiro 若推了当前轮 `plan`,输入区上方出现计划条,换轮丢掉。ACP 若声明 `terminal`,对方跑的那条命令一张卡片,可停这一条(不是对话页终端)。仍约 80ms 读快照,过程行按序号增量挂现有面板,不另开总线。 - **过程内用量**:新空 Codex 会话仍解析 `thread/tokenUsage/updated` 的当前轮 `last`(累计 `total` / 窗口只留在总览等用量页)。新空 Grok 会话解析 **当前轮**(`turn_completed.usage`);ACP 没有会话累计字段,不把各轮相加冒充累计。Grok / Kiro 的 `context_usage` 有数字才进用量小字(窗口用量),全 0 不画。解析路径已接;真窗 2026-09-09 见过部分轮次 **没有** `turn_completed.usage`,此时界面不画假数字。对话里只在本轮结束后用小字写输入 / 输出(有缓存才写缓存);生成中不画用量。只显示协议里的数字,不估算费用。Kiro 没有 token 累计数据源。 - - **`/` 菜单**:立刻执行的动作(新建对话、复制最近回复;换模型/思考/技能要搜到才列出,避免把整份目录摊在 `/` 上)。Grok / Kiro 会话就绪且对方声明了命令时,另列对方斜杠项:选中后当作一轮正常发出(无必填参数则直接发送 `/名字`;必填参数则插入 `/名字 ` 供补全后再发)。Grok 走标准 ACP `available_commands_update`;Kiro 走 `_kiro.dev/commands/available` 的 `commands[]`(不把技能/工具目录摊进 `/`)。目录变了会重拉;未就绪或未声明则不画,不猜菜单。没有真窗证据前,不宣称「对方命令已验收」。有可启动的命令行时,`/` 可列出「启动命令行」(DeepSeek 为「打开网页会话」),在外部打开,不标成对话页能力。 + - **`/` 菜单**:立刻执行的动作(新建对话、复制最近回复;换模型/思考/技能要搜到才列出,避免把整份目录摊在 `/` 上)。Grok / Kiro 会话就绪且对方声明了命令时,另列对方斜杠项:选中后当作一轮正常发出(无必填参数则直接发送 `/名字`;必填参数则插入 `/名字 ` 供补全后再发)。Grok 走标准 ACP `available_commands_update`;Kiro 走 `_kiro.dev/commands/available` 的 `commands[]`(不把技能/工具目录摊进 `/`)。目录变了会重拉;未就绪或未声明则不画,不猜菜单。Kiro 就绪会话「对方命令」Linux 真窗已 PASS(修复 tip `daed5ccf`,现行 tip 仍含该修复):列出对方声明的斜杠项,裸 `/` 不摊技能目录;记录见 `/workspace/qa-issues/CHAT-SLASH-PR365-RETEST-daed5ccf.md`。本条只记这次验过的展示,不把选中发送或其它 Agent 写成已验收。有可启动的命令行时,`/` 可列出「启动命令行」(DeepSeek 为「打开网页会话」),在外部打开,不标成对话页能力。 - **本机对接与本会话**:本机持续通道按 Agent 写死:新空 Codex 走 app-server,新空 Grok / Kiro 走 ACP,新空 Claude 走 stream-json;其余与旧会话仍走原发送方式。Cursor 默认软隐藏,不在允许/拒绝之列,也不进持续聊天白名单。**Agents 详情**写这份 Agent 的新对话怎么接(ACP / 持续对话 / 原来的发送方式),不是一份可改的「ACP 总表」。**Chat 顶栏和会话设置**写这次对话实际在走哪条;点了卡片上的一直允许之后,会话设置里可以关掉「本会话已一直允许」(不能在这里假装打开)。一次对话是否在用持续通道,看这次会话是不是上述新空路径。Kiro 旧对话没有切到 ACP 的入口。会话字段见 [会话身份](concepts/chat-session-identity.md)。 - **新空 Claude 会话(B3 首片)**:走 Claude Code `-p --input-format stream-json --output-format stream-json` 持续通道(同进程多轮、本地图片 base64、模型/思考强度参数);**不支持**生成中补充;本片**不**接可点允许/拒绝(默认 `dontAsk`,危险模式 `bypassPermissions`)。有历史的旧 Claude 会话仍走 print+resume。print 路径在已经出过 assistant 正文后不再把最终 `result` 再拼进气泡(短回复不会同一句写两遍);只有没见过 assistant 文本时才用 `result` 当正文。Linux 真窗短回复已验不双写。见 [Claude B3](archive/chat-claude-b3.md)。 - **对话标题**:新建对话先用首条消息提炼的短句。一轮结束后读对方写在自己会话记录里的标题并改用它:Codex(app-server 汇总的 `sqlite/*.db` 里 `local_thread_catalog.display_title`,退回 `session_index.jsonl` 的 `thread_name`)、Grok(`summary.json` 的 `generated_title`)、Kiro(`sessions/cli/.json` 的 `title`)、DSH(会话日志的 `session/title` 行)。Claude 没有标题来源,保持首条消息推导。手动改过名字的对话不再被覆盖(不新增「谁起的名字」列,也不做迁移);连续通道取运行时线程 id,旧会话取 `native_session_id`。AgentHub 不进协议里要标题。实机核对:Grok 与 Codex 的真实会话都能取到;Codex 的 `session_index.jsonl` 只收 IDE / 桌面端自己建的会话,所以优先读 `local_thread_catalog`;Kiro 常见只有 1–2 字的占位;DSH 普通发送不带会话 id,实际触发不到;Claude 无来源。