Skip to content

feat(acp): surface dropped SessionUpdate variants + UI (slash commands, plan, config options)#979

Open
harryfan1985 wants to merge 6 commits into
GCWing:mainfrom
harryfan1985:feat/acp-available-commands
Open

feat(acp): surface dropped SessionUpdate variants + UI (slash commands, plan, config options)#979
harryfan1985 wants to merge 6 commits into
GCWing:mainfrom
harryfan1985:feat/acp-available-commands

Conversation

@harryfan1985
Copy link
Copy Markdown
Contributor

@harryfan1985 harryfan1985 commented May 30, 2026

概要

让 BitFun 的 ACP host 把此前收到即丢弃的几个 session/update 通知接进来,并补上前端视图层,benefits 所有 ACP agent(opencode / claude-code / codex / omp)。实现 #967P1 两项(完整三层) + P2 一项

之前 BitFun 的 ACP client 只处理 AgentMessageChunk / AgentThoughtChunk / ToolCall / ToolCallUpdate / UsageUpdate,其余 SessionUpdate 变体全部丢弃。

相关:#967(ACP host 完整度跟踪)。

改动

1. Slash 命令(AvailableCommandsUpdate)— 后端 + 逻辑 + UI 全套

ACP 里 slash 命令没有专门 RPC:发现靠 agent 的 AvailableCommandsUpdate,调用靠普通 session/prompt/<name> <args> 文本。

  • 后端:AcpAvailableCommand DTO、stream.rs 解析、manager.rs 按 session 存储 + get_session_commands 查询、desktop get_acp_session_commands 命令 + agentic://acp-available-commands-updated 实时事件。
  • 前端逻辑:ACPClientAPI.getSessionCommands() + 类型;useAcpSlashCommands hook + filterSlashCommands / acpSessionRef / acpSlashCommandText 纯函数(已测)。
  • 前端 UI:接入 ChatInput 已有的 slash picker(与原生 action / MCP prompt 同一套),新增 SlashAcpCommandItem,agent 命令置顶、可点击/键盘选中,选中后插入 /<name> 文本。

2. 执行计划(Plan)— 后端 + UI

  • 后端:AcpPlanEntry DTO + From(priority→high/medium/low,status→pending/in_progress/completed);stream.rs 解析;desktop agentic://acp-plan-updated 实时事件;前端类型。
  • 前端 UI:useAcpPlan(sessionId) 订阅事件;AcpPlanPanel 渲染实时任务清单(状态图标、done/total、完成项删除线),挂在输入框上方,空时不渲染。

3. 配置项保鲜(ConfigOptionUpdate)

  • session 早就存 config_options 并被 model/options UI 消费,但 ConfigOptionUpdate 通知被忽略 → 中途更新会变陈旧。本 PR 接通,manager.rs 替换整套;desktop emit agentic://acp-session-options-changed 让前端 refetch。

测试

  • cargo test -p bitfun-acp —— 48 passed(新增 commands / plan / config-option 三个 dispatch 测试 + command 转换测试)。
  • vitest —— 151 passed(slash 命令消费逻辑、acpSession 工具等);ChatInput 布局测试通过。
  • cargo check -p bitfun-desktoptsc --noEmit —— clean。

说明 / 后续

  • In-app 验证:slash 菜单和 plan 面板是严格照搬现有 slash picker(MCP prompt 路径)与事件订阅模式写的,类型/测试全过;但最终视觉与 contenteditable 交互建议在跑起来的 app 内确认。
  • ACP host 完整度:补全 fs/terminal 能力与 SessionUpdate 处理(当前为 V1 子集) #967 未含项:CurrentModeUpdate(需配 available_modes + set_mode)、SessionInfoUpdate(标题)、以及 P0 的 fs/terminal 宿主能力 —— 更重或需产品决策,留作后续单独 PR。

🤖 Generated with Claude Code

harryfan1985 and others added 4 commits May 30, 2026 16:51
Common ACP-host mechanism (not agent-specific): capture the slash commands an
ACP agent advertises via `session/update` → `AvailableCommandsUpdate`, store
them per session, and expose them to the frontend so it can build a `/` command
menu. Benefits every ACP agent (opencode / claude-code / codex / omp).

Background: BitFun's ACP client previously ignored every SessionUpdate variant
except message/thought/tool/usage. Slash commands are not a dedicated RPC in
ACP — discovery is `AvailableCommandsUpdate`; invocation is a plain
`session/prompt` whose text starts with `/<name>`. This wires the discovery half.

- session_options.rs: `AcpAvailableCommand { name, description, inputHint }` DTO
  + `From<schema::AvailableCommand>` (maps the Unstructured input hint).
- stream.rs: new `AcpClientStreamEvent::AvailableCommandsUpdated`; parse the
  `SessionUpdate::AvailableCommandsUpdate` notification; round-tracker passes it
  through.
- manager.rs: store latest commands on the session (`available_commands`), update
  it in the prompt loop, and add `get_session_commands(...)` (read-after-ensure,
  mirrors get_session_options).
- desktop acp_client_api.rs: emit `agentic://acp-available-commands-updated`
  live during a turn, and add the `get_acp_session_commands` query command
  (registered in lib.rs).
- ACPClientAPI.ts: `AcpAvailableCommand` type + `getSessionCommands()` + the
  live-event payload type.

Not included (follow-up): the actual `/` menu UI in ChatInput consuming this
data. This commit is the reusable host-side mechanism only.

Verified: cargo test -p bitfun-acp (46 passed, incl. new conversion +
dispatch tests); cargo check -p bitfun-desktop; tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds the data/logic layer the chat input will use to offer a `/` command menu
for ACP sessions, consuming the host-side mechanism added in the previous commit.

- acpSession.ts: `AcpSessionRef` + `acpSessionRef(session)` (derive the
  client/session/workspace identifiers from a flow-chat session, mirroring
  ModelSelector's ACP resolution) + `acpSlashCommandText(name)` (the `/name `
  prompt text that actually invokes a command — ACP has no command RPC).
- useAcpSlashCommands.ts:
  - `filterSlashCommands(commands, query)` — pure, case-insensitive match over
    name + description, tolerant of a leading slash.
  - `useAcpSlashCommands(acpSession)` — fetches `get_acp_session_commands` on
    session change and stays live via the
    `agentic://acp-available-commands-updated` event; empty for non-ACP sessions.

Tested: vitest (10) over the pure helpers (filter, acpSessionRef, command text).

Not included (needs in-app verification): the contenteditable `/` popup wiring
in ChatInput/RichTextInput (the `@`-mention system is hardcoded to `@`, so the
trigger + insert path must be built and verified with the app running). This
commit is the testable logic that wiring will consume.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second ACP-host SessionUpdate surfacing (after available-commands): capture the
agent's execution plan (ACP `Plan` / agent-plan) and forward it live to the
frontend so it can render a task checklist during a turn. Benefits every ACP
agent. The plan replaces the prior one in full on each update (per spec).

- session_options.rs: `AcpPlanEntry { content, priority, status }` DTO +
  `From<schema::PlanEntry>` (priority -> high/medium/low,
  status -> pending/in_progress/completed).
- stream.rs: `AcpClientStreamEvent::PlanUpdated(Vec<AcpPlanEntry>)`; parse
  `SessionUpdate::Plan`; round-tracker passthrough.
- desktop acp_client_api.rs: emit `agentic://acp-plan-updated` live during a turn.
- ACPClientAPI.ts: `AcpPlanEntry` + `AcpPlanUpdatedEvent` types.

Plan is live turn-progress (not queried at input time like slash commands), so
there is no query command — the frontend consumes the live event only.

Verified: cargo test -p bitfun-acp (47 passed, incl. exposes_plan_updates
dispatch test); cargo check -p bitfun-desktop; tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Third ACP-host SessionUpdate surfacing. The session already stored
`config_options` (consumed by the model/options UI via get_acp_session_options),
but the client ignored the agent's `ConfigOptionUpdate` notification — so the
stored options went stale when the agent changed them mid-session. This wires
that notification through so the stored set stays accurate.

- stream.rs: `AcpClientStreamEvent::ConfigOptionsUpdated(Vec<SessionConfigOption>)`;
  parse `SessionUpdate::ConfigOptionUpdate`; round-tracker passthrough.
- manager.rs: `update_session_config_options` replaces the stored full set in the
  prompt loop (the agent always sends the complete set).
- desktop acp_client_api.rs: emit `agentic://acp-session-options-changed` so the
  frontend can refetch the combined session options live.
- ACPClientAPI.ts: `AcpSessionOptionsChangedEvent` type.

Verified: cargo test -p bitfun-acp (48 passed, incl. exposes_config_option_updates);
cargo check -p bitfun-desktop; tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@GCWing GCWing requested a review from wgqqqqq May 30, 2026 12:18
Completes the P1 view layer that consumes the host-side mechanism and the
consumer-side logic added earlier.

Slash commands — integrated into the EXISTING ChatInput slash picker (the same
one that already lists native actions and MCP prompts), so no new contenteditable
trigger was needed:
- new SlashAcpCommandItem item kind + `getFilteredAcpCommands()`, fed by
  `useAcpSlashCommands(acpSessionRef(session))`.
- agent commands are prepended in `getSlashPickerItems()` (primary for ACP
  sessions) and selectable by click / keyboard (Enter, Tab) like the other kinds.
- `selectSlashAcpCommand` inserts `/<name> ` via the existing SET_VALUE dispatch
  (ACP has no command RPC — a command is sent as normal prompt text).

Plan — live execution-plan checklist:
- `useAcpPlan(sessionId)` subscribes to `agentic://acp-plan-updated`.
- `AcpPlanPanel` renders the entries (status icons, done/total, strike-through
  for completed); mounted above the chat input box; renders nothing when empty.

Verified: tsc clean; vitest 151 passed (incl. acpSession + slash-command logic),
ChatInput layout test passes.

Note: behavior was built by faithfully mirroring the existing slash picker (MCP
prompts) and event-subscription patterns; final visual/interaction behavior
should still be confirmed in the running app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@harryfan1985 harryfan1985 changed the title feat(acp): surface dropped SessionUpdate variants (slash commands, plan, config options) feat(acp): surface dropped SessionUpdate variants + UI (slash commands, plan, config options) May 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant