Skip to content

feat: add Ask AI in-plan Q&A via a dedicated read-only subagent (fixes #1) - #3

Open
YeKc1M wants to merge 14 commits into
titanwings:mainfrom
YeKc1M:feat/ask-dsh-subagent
Open

feat: add Ask AI in-plan Q&A via a dedicated read-only subagent (fixes #1)#3
YeKc1M wants to merge 14 commits into
titanwings:mainfrom
YeKc1M:feat/ask-dsh-subagent

Conversation

@YeKc1M

@YeKc1M YeKc1M commented Aug 17, 2026

Copy link
Copy Markdown

Closes #1

摘要

实现 #1 请求的 Ask AI 功能:审阅 plan 时选中一段内容(或直接提问),
由 agent 即时给出上下文相关的解释性回答,不必解散 gate 或写成修改意见。

变更

Host 半区

  • 注册 /dsh-plannotator RPC 通道(单 ask 端点,trusted-host 权威,复用
    loopback 放行/跨站 403 的信任栅栏)。
  • ctx.agents.get(sessionId) 取被审阅会话的活 agent,用一次性只读子 Agent
    (label plan-ask,优先 fork 继承模型/preset/已完成轮次,无 fork 时退化为全新 spawn)回答。
  • 只读由 toolFilter.allow 逐项探测(read/grep/glob/web_search/web_fetch)+
    persona 约束双保险,子 Agent 无法 edit/write/bash、无法调用 exit_plan_mode。
  • 取消语义:用户停止即移除待回答条目;Host 侧取消保留为带重试按钮的错误条目。
  • 卫生上限:回答 32k(含截断标记)可作追问历史往返;客户端按 Host 预算截断;
    bad-request 返回 schema 合规的 details.issues

Client 半区

  • 审阅面板内 plan 预览左侧常驻 Ask AI 侧栏;选中 plan 文本点 ✦ Ask AI 摘录直达输入框。
  • 问答线程本地存储,跨面板导航与刷新存活,追问携带此前上下文。

测试

  • pnpm check 全绿(typecheck + 52 个 node:test);已在本地 dsh web profile 装
    载验证(v0.1.4,通道启动即挂载)。

备注

  • 版本 0.1.3 → 0.1.4;client inject 新增 @deepseek-ai/dsh-client-connection。

YeKc1M added 14 commits August 15, 2026 13:31
- Add Ask AI channel and endpoint to handle plan-related queries.
- Define interfaces for request and response structures.
- Implement request validation and history management.
- Create a handler to manage subagent interactions and responses.
- Add tests for client and host functionalities, including error handling and request processing.
- Enhance UI tests to cover Ask AI interactions and responses.
bad-request responses carried details:{} while DSH's closed RPC taxonomy
requires details.issues for that code; the composed web client validates
the envelope with serverResponseSchema.parse and rejected the whole
response, surfacing a raw ZodError in the panel instead of the friendly
message. Emit per-field zod-shaped issues and collect every violation at
once; the client now also maps any ZodError from the connection caller to
a friendly internal error instead of leaking the dump.
An over-long answer was stored verbatim and re-sent as the next follow-up's
history entry, where the host rejects answers beyond 32k chars — silently
poisoning every later question. Cap the returned answer at the history
budget with a visible marker (marker counted inside the cap), and slice
history entries defensively on the client as well.
An over-long pasted question failed once and then kept failing on every
Retry, because sendAsk and retryAsk sent the entry values verbatim while
the host rejects anything beyond 8k chars. Slice question and quote to the
host budgets on send and on retry, and hoist the budget constants shared
with the history slice. The selection anchor already caps quotes at 800
chars, so the quote slice is budget alignment; the question slice fixes the
reachable dead-end.
Two related cancellation defects: a host-side cancelled error was mapped to
AbortError and silently deleted the question, and the thread was component
state with an unmount abort, so navigating into the answering subagent
cancelled it and lost the thread. Move the thread into a module-level store
that persists to local storage, stop aborting on unmount so in-flight
answers land after the panel remounts, and keep host cancellations visible
as error entries while only the user's own Stop drops the question.
…n accept

busy is already derived from the thread store's entries (state), not a ref;
this documents the gate invariant so a future timeout/cancel path cannot
desync the send gate from the button states, and makes send return whether
the question was accepted so the panel clears the composer only on accept.
Ask AI no longer opens as an on-demand overlay sidebar; the review panel
workspace is now a horizontal split with the Ask AI thread and composer
permanently on the left of the plan preview and annotations. Selecting plan
text and choosing the Ask AI action stages the excerpt straight into the
always-visible composer. The docked panel widens to keep both halves
readable.
The Ask AI budgets, channel/endpoint names, and request types were defined twice (src/client vs src/host) and kept in sync by comments alone. Move them into a single src/shared/limits.ts that both the browser bundle and the Host import and re-export, so the client slicing and the Host validation can never drift. The magic history slice (-20) now names MAX_HISTORY_ENTRIES; build.mjs needs no change since esbuild and the TS program follow imports.
Three client modules carried identical randomUUID-with-fallback ID generators (ask-thread, ask-ai, PlannotatorPanel); replace them with one shared newId(prefix) in src/shared/id.ts, preserving the legacy fallback formats (ask-/annotation-). PlannotatorPanel repeated a 3-line selection reset at six sites; extract clearSelection() (removeAllRanges + selection + comment) and adoptSelection() (set selection + clear comment + disarm approve-confirm). Deliberately untouched: Escape/Cancel keep their 2-line clear without removeAllRanges, and the delete-annotation/overall-feedback paths keep confirmApprove-only resets, so behavior is byte-identical.
@YeKc1M

YeKc1M commented Aug 17, 2026

Copy link
Copy Markdown
Author

选择文本后会弹出选择comment或者ask ai
image
选择ask ai后会fork出一个新的subagent,等待agent完成工作后会在侧边栏看到回复
image

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.

功能请求 — Ask AI:审阅计划时向 AI 提问

1 participant