feat(agent): 抽屉与会话页统一成一个对话界面,子会话在父会话里实时可见

- 抽屉和会话页渲染同一套 GoalComposer / AgentRunPanel;抽屉标题即最近会话切换,
  头部只有新对话、在页面打开、关闭;去掉右下角悬浮按钮和最小化小窗
- 会话列表合一:对话与巡检混排带类型小标签,搜索 + 一个状态下拉,只列顶层会话
  (GET /agent/runs?roots=true,新增可选参数)
- 子会话卡片:由镜像的 subagent_* 事件折成,显示任务、状态、当前步骤、用时、调用次数、费用,
  可展开看过程、可打开;顶部「N 个子会话运行中」跳到卡片;subagent_completed 加可选 durationMs
- 回答去内部字段:汇报规则禁止照抄键值与字段表;展示层 stripInternalFields 把字段行 / 字段表移进技术信息
- 开场建议按页面给 2 条完整问法、点了直接发;指令只留 /先给方案,/重跑 改成「重新运行」按钮

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-08 13:56:38 +08:00
1 parent dd0f6e2c09
commit 3692793dff
35 files changed
+1454 -1137

No files matched your search

@@ -9,6 +9,7 @@ export const REPORT_FORMAT: readonly string[] = [
"不超过 8 行;不复述过程,不写下一步打算,除非需要用户做什么。",
"单位说人话:大小写成 GB / TB(如 79.4 GB),时间写相对时间(3 分钟前、昨天 16:12);不写原始字节数和 ISO 时间。",
"对象用名字:作品名、站点名、下载器名、资源标题、文件名;不写函数名、工具名、字段名、参数、UUID、hash、adapter 或英文状态码,状态用中文(正常、下载中、已整理)。",
"工具和子会话返回的原始字段不照抄:不写「childRunId:…」「decisionId: null」这类键值,不列「返回字段」表,不写 null / true / false;子会话的汇报是给你看的,转述它的结论。",
"异常和需要用户做的事放前面;正常的项合并成一句带过,数量为 0 的不单独列。",
"没做成的动作每个一句:「没做:检测站点,需要你批准」;不展开授权细节。",
"数量以工具返回为准:派出几个子会话、提交几个下载,只数成功的调用,失败或被拒绝的不算。",
+2 -1
View File
@@ -204,7 +204,8 @@ describe("⑤ 汇报格式", () => {
expect(f).toContain("不写函数名、工具名、字段名、参数、UUID、hash、adapter 或英文状态码");
expect(f).toContain("只有列举 3 个以上同类对象");
expect(f).toContain("不写 HTML");
expect(REPORT_FORMAT.length).toBe(9);
expect(f).toContain("工具和子会话返回的原始字段不照抄");
expect(REPORT_FORMAT.length).toBe(10);
});
it("用户汇报不要求引用对象 ID", () => {
+2
View File
@@ -114,6 +114,8 @@ const SubagentCompleted = z.object({
summary: z.string(),
usage: AgentUsageTotals,
toolCalls: NonNegInt,
/** 子会话运行时长(毫秒,开始到结束);旧事件没有 */
durationMs: NonNegInt.optional(),
});
// 自动化优先的会话交互(AG-2):模型用 agent.plan 维护步骤清单,用 agent.propose_options 让用户在方案里选;
+2
View File
@@ -385,6 +385,8 @@ export const AgentRunListQuery = z.object({
group: z.enum(["conversation", "auto"]).optional(),
/** 只列某个会话直接派生的子会话 */
parentRunId: Id.optional(),
/** "true" = 只列顶层会话(不含子会话);子会话在父会话里看 */
roots: z.enum(["true", "false"]).optional(),
});
export type AgentRunListQuery = z.infer<typeof AgentRunListQuery>;