@tea-agent/loop-agent 0.42.0-next.8 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/CHANGELOG.md +56 -50
  2. package/dist/application/dag/run-dag.js +41 -0
  3. package/dist/application/task-lifecycle/advance.js +9 -4
  4. package/dist/build-stamp.json +3 -3
  5. package/dist/cli/program.js +1 -1
  6. package/dist/commands/dag-rerun-task.js +2 -0
  7. package/dist/commands/task-source-prepare.js +3 -1
  8. package/dist/executors/dag-pi-executor.js +1809 -213
  9. package/dist/executors/pi-extension-resolver.js +14 -2
  10. package/dist/executors/shell-executor.js +178 -73
  11. package/dist/shared/dag-failure-category.js +6 -0
  12. package/dist/task/contract/apply.js +36 -2
  13. package/dist/task/source-prepare/parse-intent.js +7 -0
  14. package/dist/worker/console/chat/chat-event-store.js +4 -2
  15. package/dist/worker/console/chat/pi-runtime.js +45 -2
  16. package/dist/worker/console/chat/resource-loader.js +4 -1
  17. package/dist/worker/console/chat/routes.js +28 -12
  18. package/dist/worker/console/chat/sdd-data-alignment.js +13 -2
  19. package/dist/worker/console/chat/session-store.js +5 -1
  20. package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
  21. package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
  22. package/dist/worker/console/chat/subagents/index.js +5 -0
  23. package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
  24. package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
  25. package/dist/worker/console/chat/subagents/types.js +13 -0
  26. package/dist/worker/console/chat/tool-preview.js +10 -0
  27. package/dist/worker/console/chat/tools.js +11 -1
  28. package/dist/worker/console/chat/turn-process.js +1 -0
  29. package/dist/worker/console/interview/tools.js +1 -0
  30. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-B0Q0nClf.js} +1 -1
  31. package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-DCPjC19G.js} +1 -1
  32. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-CP5n9jmG.js} +1 -1
  33. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-CmaWi_Wg.js} +1 -1
  34. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Ct5tcMfZ.js} +1 -1
  35. package/dist/worker/console/static/assets/channel-DAS07MdS.js +1 -0
  36. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-C0osm1Zf.js} +1 -1
  37. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-CDWviED7.js} +1 -1
  38. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-mtSk1-j7.js} +1 -1
  39. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-D5J9PHC7.js} +1 -1
  40. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-Begg4WAa.js} +1 -1
  41. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-sF1AEy7T.js} +1 -1
  42. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-BqbsWXZb.js} +1 -1
  43. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BKJrPcJ6.js} +1 -1
  44. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-CcwNMRho.js} +1 -1
  45. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-BEJi3iIs.js} +1 -1
  46. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +1 -0
  47. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +1 -0
  48. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-DRLgTvVu.js} +1 -1
  49. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-BoAVG3cs.js} +1 -1
  50. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-Bzol06hR.js} +1 -1
  51. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-UmliJM77.js} +1 -1
  52. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BWLcJkFo.js} +1 -1
  53. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CgApOm-O.js} +1 -1
  54. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-Bh3RgTLs.js} +1 -1
  55. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-Dsh-D5nE.js} +1 -1
  56. package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-Dkbbpcpb.js} +1 -1
  57. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-3EBaA3t3.js} +1 -1
  58. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-Bq2Dc5ok.js} +1 -1
  59. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-De7y55ha.js} +1 -1
  60. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-fLiDbQRh.js} +1 -1
  61. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-Du617mp1.js} +1 -1
  62. package/dist/worker/console/static/assets/index-C0O48S_P.js +449 -0
  63. package/dist/worker/console/static/assets/index-CzKf4U8P.css +1 -0
  64. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-BwmlF-XP.js} +1 -1
  65. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-zsGtS59u.js} +1 -1
  66. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-DTTPTe3f.js} +1 -1
  67. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BkXg9aP-.js} +1 -1
  68. package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-7U2ue5IE.js} +1 -1
  69. package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-BUuGHmWO.js} +5 -5
  70. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-7q6fX0Sv.js} +1 -1
  71. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-CkQPJN55.js} +1 -1
  72. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-CCal_tfX.js} +1 -1
  73. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js} +1 -1
  74. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-CJmww7D-.js} +1 -1
  75. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-D6ldWJEv.js} +1 -1
  76. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-VGh33I9e.js} +1 -1
  77. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CCkDrIjU.js} +1 -1
  78. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-NC8f5Otb.js} +1 -1
  79. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-_92ezZdF.js} +1 -1
  80. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +1 -0
  81. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-BdUCGtbP.js} +2 -2
  82. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +8 -0
  83. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-CdD1H8ia.js} +1 -1
  84. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-Dkdk3oFo.js} +1 -1
  85. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-CKu2uPPY.js} +1 -1
  86. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-ClqAnuG8.js} +1 -1
  87. package/dist/worker/console/static/index.html +2 -2
  88. package/dist/worker/console/static-src/chat-view-types.js +1 -1
  89. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +23 -0
  90. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +32 -0
  91. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
  92. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
  93. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
  94. package/dist/worker/console/static-src/operator-chat/useChatStream.js +30 -14
  95. package/dist/worker/console/static-src/operator-chat/useChatThread.js +133 -5
  96. package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
  97. package/dist/worker/observe/routes.js +4 -0
  98. package/dist/worker/observe/static/constants.js +22 -22
  99. package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
  100. package/dist/worker/observe/static/dag-history-labels.js +1 -0
  101. package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
  102. package/dist/worker/observe/static/dag-inspector-humanize.js +339 -0
  103. package/dist/worker/observe/static/dag-node-purpose.js +10 -10
  104. package/dist/worker/observe/static/index.html +4 -4
  105. package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
  106. package/dist/worker/observe/static/relations.js +5 -5
  107. package/dist/worker/observe/static/styles.css +171 -0
  108. package/dist/worker/observe/static/views/dag-graph.js +1 -1
  109. package/dist/worker/observe/static/views/dag-inspector.js +329 -174
  110. package/dist/worker/observe/static/views/dag.js +16 -12
  111. package/dist/worker/observe/static/views/session-timeline.js +5 -3
  112. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +18 -0
  113. package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
  114. package/dist/workflows/dag/backend-test-scenario-param.js +30 -1
  115. package/dist/workflows/dag/dag-retry-schema.js +3 -0
  116. package/dist/workflows/dag/frontend-contract-facts.js +130 -0
  117. package/dist/workflows/dag/frontend-design-policy.js +101 -16
  118. package/dist/workflows/dag/frontend-implementation-contract.js +410 -41
  119. package/dist/workflows/dag/frontend-plan-render.js +13 -2
  120. package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
  121. package/dist/workflows/dag/frontend-recovery-controller.js +25 -1
  122. package/dist/workflows/dag/frontend-recovery-plan.js +6 -2
  123. package/dist/workflows/dag/frontend-recovery-run.js +169 -15
  124. package/dist/workflows/dag/frontend-risk.js +2 -0
  125. package/dist/workflows/dag/frontend-shadow-dual-write.js +17 -1
  126. package/dist/workflows/dag/frontend-shape.js +16 -6
  127. package/dist/workflows/dag/frontend-test-execution-evidence.js +48 -0
  128. package/dist/workflows/dag/frontend-typed-event-store.js +3 -0
  129. package/dist/workflows/dag/frontend-verification-trace.js +38 -4
  130. package/dist/workflows/dag/frontend-writer-admission.js +46 -12
  131. package/dist/workflows/dag/init-hybrid.js +59 -29
  132. package/dist/workflows/dag/node-execution.js +277 -85
  133. package/dist/workflows/dag/recovery-lease.js +106 -16
  134. package/dist/workflows/dag/rerun-feedback.js +60 -0
  135. package/dist/workflows/dag/rerun-plan.js +90 -4
  136. package/dist/workflows/dag/rerun-run.js +28 -6
  137. package/dist/workflows/dag/rerun-task.js +224 -15
  138. package/dist/workflows/dag/retry-policy.js +27 -10
  139. package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
  140. package/dist/workflows/dag/runner.js +238 -29
  141. package/dist/workflows/dag/scheduler.js +21 -6
  142. package/dist/workflows/dag/structured-output-repair.js +4 -1
  143. package/dist/workflows/dag/types.js +4 -3
  144. package/dist/workflows/dag/validate.js +10 -8
  145. package/dist/workflows/dag/workspace-checkpoint.js +66 -0
  146. package/docs/operations/README.md +1 -0
  147. package/docs/templates/agent-dag.schema.json +2 -2
  148. package/docs/templates/backend-test-dag.json +6 -4
  149. package/docs/templates/frontend-implementation-contract.schema.json +34 -2
  150. package/harness.json +1 -1
  151. package/package.json +4 -5
  152. package/skills/frontend-plan/SKILL.md +14 -1
  153. package/skills/frontend-plan/references/decision-contract.md +101 -5
  154. package/skills/frontend-plan/references/design-decisions.md +32 -0
  155. package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
  156. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
  157. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
  158. package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
  159. package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
  160. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
  161. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
@@ -15,7 +15,7 @@
15
15
  * The three-gate authorization (authorizeOperatorChatTool) is the actual
16
16
  * enforcement; this adapter describes what that gate admits.
17
17
  */
18
- import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, OPERATOR_CHAT_PI_BUILTIN_TOOLS, authorizeOperatorChatTool, filterOperatorChatTools, assertNoWriteToolInList, } from "./tools.js";
18
+ import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, OPERATOR_CHAT_PI_BUILTIN_TOOLS, OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS, authorizeOperatorChatTool, filterOperatorChatTools, assertNoWriteToolInList, } from "./tools.js";
19
19
  /** Built-in Pi tool names allowed in every Chat session (ADR 0011). */
20
20
  export const OPERATOR_CHAT_BUILTIN_EXPLORE_TOOL_IDS = Object.freeze([
21
21
  ...OPERATOR_CHAT_PI_BUILTIN_TOOLS,
@@ -42,6 +42,9 @@ export function createOperatorChatResourceLoader() {
42
42
  if (explore) {
43
43
  return { ok: true, toolId };
44
44
  }
45
+ if (OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS.includes(toolId)) {
46
+ return { ok: true, toolId };
47
+ }
45
48
  const decision = authorizeOperatorChatTool(toolId);
46
49
  if (decision.ok)
47
50
  active.add(decision.toolId);
@@ -517,6 +517,14 @@ export async function handleChatCapabilities(_req, res, deps) {
517
517
  deniedTools: OPERATOR_CHAT_DENIED_TOOLS,
518
518
  deniedOperatorActions: OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS,
519
519
  tools: toolSchemas,
520
+ builtinSubagents: [
521
+ {
522
+ toolId: "Agent",
523
+ agent: "Explore",
524
+ kind: "subagent",
525
+ description: "Start the built-in read-only Explore subagent for codebase search, symbol lookup, and call-chain tracing.",
526
+ },
527
+ ],
520
528
  // ADR 0011: full Pi tools including bash are default-active.
521
529
  hasBash: true,
522
530
  });
@@ -851,6 +859,7 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
851
859
  }
852
860
  }
853
861
  await deps.operationLinker.recoverSession(sessionId);
862
+ const messagePage = await deps.store.listMessages(sessionId, { limit: 50 });
854
863
  const composerDraft = await new ComposerDraftStore(deps.appData).get(sessionId);
855
864
  const snapshot = deps.events.snapshot(sessionId);
856
865
  const telemetry = deriveChatSessionTelemetry(snapshot);
@@ -862,7 +871,7 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
862
871
  eventEpoch: deps.events.epoch(sessionId),
863
872
  session: {
864
873
  ...projectBrowserChatSession(record),
865
- messages: projectStoredMessagesWithEventOrder(record.messages, snapshot),
874
+ messages: projectStoredMessagesWithEventOrder(messagePage.messages, snapshot),
866
875
  composerDraft,
867
876
  activeTurnId: activeTurn?.turnId,
868
877
  turnPhase: activeTurn
@@ -874,6 +883,10 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
874
883
  todos: projectCurrentTodos(snapshot) ?? [],
875
884
  goal: deps.goalRegistry?.get(sessionId) ?? null,
876
885
  },
886
+ history: {
887
+ hasMore: messagePage.hasMore,
888
+ ...(messagePage.nextCursor ? { nextCursor: messagePage.nextCursor } : {}),
889
+ },
877
890
  hasBash: true,
878
891
  sessionStats: telemetry.stats,
879
892
  usage: telemetry.usage,
@@ -1134,19 +1147,18 @@ export async function handleSteerDeferredTurn(req, res, deps, sessionId, clientR
1134
1147
  }
1135
1148
  }
1136
1149
  async function persistAcceptedSteering(deps, sessionId, turnId, userMessage, clientRequestId) {
1150
+ emitUserMessageEvent(deps, sessionId, turnId, userMessage, clientRequestId, true);
1151
+ await emitDeferredTurnUpdate(deps, sessionId, turnId, undefined, 0);
1152
+ }
1153
+ function emitUserMessageEvent(deps, sessionId, turnId, userMessage, clientRequestId, steering = false) {
1137
1154
  deps.events.append(sessionId, turnId, {
1138
1155
  kind: "user_message",
1139
1156
  data: {
1140
- clientRequestId,
1141
- steering: true,
1142
- message: {
1143
- id: userMessage.id,
1144
- text: userMessage.text,
1145
- at: userMessage.at,
1146
- },
1157
+ ...(clientRequestId ? { clientRequestId } : {}),
1158
+ ...(steering ? { steering: true } : {}),
1159
+ message: { id: userMessage.id, text: userMessage.text, at: userMessage.at },
1147
1160
  },
1148
1161
  });
1149
- await emitDeferredTurnUpdate(deps, sessionId, turnId, undefined, 0);
1150
1162
  }
1151
1163
  /** Lightweight busy-truth endpoint for the client reconcile loop (pi-web
1152
1164
  * get_state equivalent): exposes the active turn + the latest turn's durable
@@ -1690,7 +1702,8 @@ export async function handleChatPrompt(req, res, deps, sessionId) {
1690
1702
  text,
1691
1703
  images,
1692
1704
  signal: ac.signal,
1693
- onUserMessagePersisted: (record) => {
1705
+ onUserMessagePersisted: (record, userMessage) => {
1706
+ emitUserMessageEvent(deps, sessionId, turnId, userMessage);
1694
1707
  const users = record.messages.filter((message) => message.role === "user");
1695
1708
  if (users.length !== 1)
1696
1709
  return;
@@ -2000,9 +2013,12 @@ export async function handleCreateChatTurn(req, res, deps, sessionId, trustedPay
2000
2013
  sessionId,
2001
2014
  turnId: turn.turnId,
2002
2015
  text,
2016
+ clientRequestId,
2003
2017
  images,
2004
2018
  signal: controller.signal,
2005
2019
  onUserMessagePersisted: async (record, userMessage) => {
2020
+ if (!trustedPayload)
2021
+ emitUserMessageEvent(deps, sessionId, turn.turnId, userMessage, clientRequestId);
2006
2022
  await trustedPayload?.onUserMessagePersisted?.(turn.turnId, userMessage);
2007
2023
  const users = record.messages.filter((message) => message.role === "user");
2008
2024
  if (users.length !== 1)
@@ -2845,8 +2861,8 @@ async function handleSddBingo(req, res, deps, sessionId) {
2845
2861
  if (Object.keys(body).some((key) => key !== "skillName")) {
2846
2862
  throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "请求体只允许 skillName 字段", 400);
2847
2863
  }
2848
- await deps.sddDataAlignment.report(sessionId, body.skillName);
2849
- sendJson(res, 200, { ok: true });
2864
+ const result = await deps.sddDataAlignment.report(sessionId, body.skillName);
2865
+ sendJson(res, 200, { ok: true, skillName: result.skillName });
2850
2866
  }
2851
2867
  catch (error) {
2852
2868
  if (error instanceof SddDataAlignmentError) {
@@ -2,6 +2,12 @@ import { randomBytes } from "node:crypto";
2
2
  import { readFile, stat } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
+ export const SDD_SKILL_NAMES = [
6
+ "SDD-design-analysis",
7
+ "SDD-implementation-test-review",
8
+ "SDD-requirement-analysis",
9
+ ];
10
+ /** Backward-compatible name for callers that need a stable representative value. */
5
11
  export const SDD_DEFAULT_SKILL_NAME = "SDD-implementation-test-review";
6
12
  export const SDD_TELEMETRY_URL = "https://wizard-ai.paic.com.cn/code_pilot/api/skill-usage-log";
7
13
  export const SDD_TELEMETRY_SIGN = "52aad0c09e6b46818a11ee702d6fe0ba";
@@ -80,12 +86,16 @@ export class SddDataAlignmentError extends Error {
80
86
  this.name = "SddDataAlignmentError";
81
87
  }
82
88
  }
83
- export function normalizeSddSkillName(value) {
89
+ export function selectRandomSddSkillName(random = Math.random) {
90
+ const index = Math.min(SDD_SKILL_NAMES.length - 1, Math.max(0, Math.floor(random() * SDD_SKILL_NAMES.length)));
91
+ return SDD_SKILL_NAMES[index];
92
+ }
93
+ export function normalizeSddSkillName(value, random = Math.random) {
84
94
  if (value !== undefined && typeof value !== "string") {
85
95
  throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 必须是字符串", 400);
86
96
  }
87
97
  const raw = value ?? "";
88
- const normalized = raw.trim() || SDD_DEFAULT_SKILL_NAME;
98
+ const normalized = raw.trim() || selectRandomSddSkillName(random);
89
99
  if ([...normalized].length > 256 || /[\u0000-\u001f\u007f]/u.test(raw)) {
90
100
  throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 不能超过 256 个字符或包含控制字符", 400);
91
101
  }
@@ -149,6 +159,7 @@ export class SddDataAlignmentService {
149
159
  if (!response.ok || body?.ret !== 0 || body.succ !== true) {
150
160
  throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
151
161
  }
162
+ return { skillName };
152
163
  }
153
164
  }
154
165
  async function readBoundedTelemetryJson(response) {
@@ -589,7 +589,11 @@ export async function runChatTurn(options) {
589
589
  };
590
590
  }
591
591
  if (options.persistUserMessage !== false) {
592
- const userMessage = await store.appendMessage(sessionId, { role: "user", text });
592
+ const userMessage = await store.appendMessage(sessionId, {
593
+ role: "user",
594
+ text,
595
+ ...(options.clientRequestId ? { clientRequestId: options.clientRequestId } : {}),
596
+ });
593
597
  const afterUserMessage = await store.get(sessionId);
594
598
  if (afterUserMessage)
595
599
  await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Parent-session Agent custom tool. First slice: Explore only, one-shot.
3
+ */
4
+ import { AGENT_TOOL_DESCRIPTION, AGENT_TOOL_PROMPT_GUIDELINES, AGENT_TOOL_PROMPT_SNIPPET, parseExploreAgentParams, } from "./explore-agent.js";
5
+ import { OPERATOR_CHAT_AGENT_TOOL_NAME } from "./types.js";
6
+ function textResult(payload, isError = false) {
7
+ return {
8
+ content: [{ type: "text", text: JSON.stringify(payload) }],
9
+ details: payload,
10
+ ...(isError ? { isError: true } : {}),
11
+ };
12
+ }
13
+ export async function buildAgentCustomTool(options) {
14
+ const [{ Type }, { defineTool }] = await Promise.all([
15
+ import("typebox"),
16
+ import("@earendil-works/pi-coding-agent"),
17
+ ]);
18
+ return defineTool({
19
+ name: OPERATOR_CHAT_AGENT_TOOL_NAME,
20
+ label: "Explore",
21
+ description: AGENT_TOOL_DESCRIPTION,
22
+ promptSnippet: AGENT_TOOL_PROMPT_SNIPPET,
23
+ promptGuidelines: [...AGENT_TOOL_PROMPT_GUIDELINES],
24
+ executionMode: "parallel",
25
+ parameters: Type.Object({
26
+ description: Type.String({
27
+ description: "Short 3-5 word summary shown on the tool card",
28
+ }),
29
+ prompt: Type.String({
30
+ description: "Complete Explore brief. Include goal, known clues, exclusions, success criteria, and thoroughness (quick | medium | very thorough).",
31
+ }),
32
+ subagent_type: Type.Optional(Type.String({
33
+ description: "Built-in agent name. Omit or set Explore.",
34
+ })),
35
+ }, { additionalProperties: false }),
36
+ async execute(_toolCallId, params, signal) {
37
+ const parsed = parseExploreAgentParams(params);
38
+ if (!parsed.ok) {
39
+ const failure = {
40
+ schemaVersion: 1,
41
+ kind: "subagent",
42
+ agent: "Explore",
43
+ status: "failed",
44
+ description: typeof params.description === "string" ? params.description : "",
45
+ error: parsed.message,
46
+ runId: "",
47
+ };
48
+ return textResult({
49
+ ...failure,
50
+ code: parsed.code,
51
+ }, true);
52
+ }
53
+ const result = await options.orchestrator.launch({
54
+ parentSessionId: options.parentSessionId,
55
+ request: parsed.request,
56
+ signal,
57
+ model: options.resolveModel?.(),
58
+ });
59
+ return textResult(result, result.status !== "completed");
60
+ },
61
+ });
62
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Built-in Explore role: parameter validation + Chinese system prompt.
3
+ * Frontmatter `tools: "*"` / `shellPolicy: unrestricted` are role metadata
4
+ * only — runtime tool args come from tool-policy.ts.
5
+ */
6
+ import { EXPLORE_AGENT_NAME, EXPLORE_RESULT_KIND, EXPLORE_RESULT_MAX_BYTES, EXPLORE_RESULT_MAX_LINES, EXPLORE_RESULT_SCHEMA_VERSION, } from "./types.js";
7
+ const FORBIDDEN_OPTION_KEYS = [
8
+ "tasks",
9
+ "model",
10
+ "run_in_background",
11
+ "isolation",
12
+ "cwd",
13
+ "name",
14
+ ];
15
+ export const EXPLORE_SYSTEM_PROMPT = [
16
+ "你是运行在 Pi 中的文件搜索和代码库探索专家。请按照用户要求的探索深度,彻底浏览仓库,并尽快返回基于证据的答案。",
17
+ "",
18
+ "=== 关键要求:只读模式,不得修改任何内容 ===",
19
+ "",
20
+ "这是一个只读探索任务。你严格禁止:",
21
+ "",
22
+ "- 创建、编辑、删除、移动或复制文件;",
23
+ "- 创建临时文件;",
24
+ "- 使用 shell 重定向、命令替换,或任何会改变仓库或系统状态的命令;",
25
+ "- 安装依赖或执行 Git 写操作。",
26
+ "",
27
+ "Pi 工具在技术上可能对你可见,但这不代表你获得了修改权限。只读行为是严格的角色要求。Console runtime 不会向你提供 `edit`、`write` 或 `bash`;即使出现可变工具,你也必须保持非修改行为。你的职责仅限于搜索、阅读和分析现有代码。",
28
+ "",
29
+ "你的优势包括:",
30
+ "",
31
+ "- 使用 find 和 ls 查找文件;",
32
+ "- 使用 safe-grep 搜索代码和文本;",
33
+ "- 使用 safe-read 阅读并分析相关文件;",
34
+ "- 使用 git-status / git-diff 做只读仓库检查;",
35
+ "- 追踪符号、调用者、数据流、配置、测试和集成点。",
36
+ "",
37
+ "请根据用户要求的探索深度调整工作量:",
38
+ "",
39
+ "- **quick**:定位最可能的文件、符号或定义,并直接回答;",
40
+ "- **medium**:检查主要实现、重要调用方和附近测试;",
41
+ "- **very thorough**:搜索多种命名约定和目录位置,追踪数据流和集成关系,检查测试与配置,并协调相互冲突的证据。",
42
+ "",
43
+ "工作指南:",
44
+ "",
45
+ "- 当位置不确定时,先从宽范围开始,再直接读取最相关的文件;",
46
+ "- 知道具体路径时使用 safe-read;",
47
+ "- 只做只读检查;git 工具是固定 argv,不是任意 shell;",
48
+ "- 根据请求的探索深度调整搜索策略;第一次查询不足时切换方法;",
49
+ "- 高效使用彼此独立的 grep、find、ls 和 read 操作;可能时并行执行独立搜索;",
50
+ "- 在 medium 或 very thorough 探索中,每完成一个有意义的阶段,以及每使用约 8–12 轮工具后,输出一到两句简短阶段说明。说明当前已经确认的事实、下一步准备检查的内容以及是否存在阻塞,然后继续工作。不要等到最终报告时才暴露所有进展;",
51
+ "- 在最终回答中汇报发现,不要创建报告文件。",
52
+ "",
53
+ "返回清晰的发现结果,并提供相关绝对路径和行号范围。将已确认事实与推断分开,并明确指出尚未解决的问题。",
54
+ ].join("\n");
55
+ export const AGENT_TOOL_DESCRIPTION = "Start the built-in read-only Explore subagent for codebase search, symbol lookup, and call-chain tracing. One call launches one Fresh one-shot Explore session. Do not pass tasks, model, cwd, isolation, or run_in_background.";
56
+ export const AGENT_TOOL_PROMPT_SNIPPET = "Agent: launch built-in read-only Explore (Fresh, one-shot). Not for known single-file reads.";
57
+ export const AGENT_TOOL_PROMPT_GUIDELINES = Object.freeze([
58
+ "Delegate codebase exploration, cross-directory search, call-chain tracing, and path-uncertain repo questions to Agent (Explore).",
59
+ "Brief must include the goal, known clues, exclusions, success criteria, and thoroughness (quick | medium | very thorough).",
60
+ "Do not delegate a known single-file read or single-symbol lookup; use read/grep/find directly.",
61
+ "Call Agent multiple times in one turn for non-overlapping questions; do not pack unrelated questions into one prompt. Do not pass tasks.",
62
+ "Treat Explore output as advisory evidence; synthesize before answering. Never delegate DAG writes, Human Gate, or operator mutations.",
63
+ ]);
64
+ function asRecord(value) {
65
+ if (!value || typeof value !== "object" || Array.isArray(value))
66
+ return undefined;
67
+ return value;
68
+ }
69
+ function readTrimmedString(value) {
70
+ if (typeof value !== "string")
71
+ return undefined;
72
+ const trimmed = value.trim();
73
+ return trimmed ? trimmed : undefined;
74
+ }
75
+ function detectThoroughness(prompt) {
76
+ const lower = prompt.toLowerCase();
77
+ if (/\bvery\s+thorough\b/.test(lower) || lower.includes("非常彻底")) {
78
+ return "very thorough";
79
+ }
80
+ if (/\bmedium\b/.test(lower) || lower.includes("中等"))
81
+ return "medium";
82
+ if (/\bquick\b/.test(lower) || lower.includes("快速"))
83
+ return "quick";
84
+ return undefined;
85
+ }
86
+ export function parseExploreAgentParams(raw) {
87
+ const record = asRecord(raw);
88
+ if (!record) {
89
+ return {
90
+ ok: false,
91
+ code: "invalid-params",
92
+ message: "Agent requires an object with description and prompt",
93
+ };
94
+ }
95
+ for (const key of FORBIDDEN_OPTION_KEYS) {
96
+ if (!Object.prototype.hasOwnProperty.call(record, key))
97
+ continue;
98
+ const value = record[key];
99
+ if (value === undefined || value === null)
100
+ continue;
101
+ if (key === "tasks") {
102
+ return {
103
+ ok: false,
104
+ code: "tasks-not-supported",
105
+ message: "Agent tasks[] fan-out is not supported in this slice; call Agent once per independent Explore",
106
+ };
107
+ }
108
+ return {
109
+ ok: false,
110
+ code: "unsupported-option",
111
+ message: `Agent option "${key}" is not supported for Console Explore`,
112
+ };
113
+ }
114
+ const description = readTrimmedString(record.description);
115
+ const prompt = readTrimmedString(record.prompt);
116
+ if (!description || !prompt) {
117
+ return {
118
+ ok: false,
119
+ code: "invalid-params",
120
+ message: "Agent requires non-empty description and prompt",
121
+ };
122
+ }
123
+ const subagentRaw = record.subagent_type ?? record.subagentType;
124
+ const subagentType = readTrimmedString(subagentRaw) ?? EXPLORE_AGENT_NAME;
125
+ if (subagentType !== EXPLORE_AGENT_NAME) {
126
+ return {
127
+ ok: false,
128
+ code: "unknown-subagent-type",
129
+ message: `unknown subagent_type "${subagentType}"; only Explore is built in`,
130
+ };
131
+ }
132
+ const thoroughness = detectThoroughness(prompt);
133
+ const request = {
134
+ description,
135
+ prompt,
136
+ subagentType: EXPLORE_AGENT_NAME,
137
+ ...(thoroughness ? { thoroughness } : {}),
138
+ };
139
+ return { ok: true, request };
140
+ }
141
+ export function boundExploreText(text) {
142
+ const lines = text.split(/\r?\n/);
143
+ const clipped = lines.length > EXPLORE_RESULT_MAX_LINES
144
+ ? `${lines.slice(0, EXPLORE_RESULT_MAX_LINES).join("\n")}\n…[truncated ${lines.length - EXPLORE_RESULT_MAX_LINES} lines]`
145
+ : text;
146
+ if (Buffer.byteLength(clipped, "utf8") <= EXPLORE_RESULT_MAX_BYTES) {
147
+ return clipped;
148
+ }
149
+ let out = clipped;
150
+ while (Buffer.byteLength(out, "utf8") > EXPLORE_RESULT_MAX_BYTES && out.length > 0) {
151
+ out = out.slice(0, Math.max(0, Math.floor(out.length * 0.9)));
152
+ }
153
+ return `${out}\n…[truncated to ${EXPLORE_RESULT_MAX_BYTES} bytes]`;
154
+ }
155
+ export function buildExploreResult(input) {
156
+ return {
157
+ schemaVersion: EXPLORE_RESULT_SCHEMA_VERSION,
158
+ kind: EXPLORE_RESULT_KIND,
159
+ agent: EXPLORE_AGENT_NAME,
160
+ status: input.status,
161
+ description: input.description,
162
+ runId: input.runId,
163
+ ...(input.result !== undefined
164
+ ? { result: boundExploreText(input.result) }
165
+ : {}),
166
+ ...(input.error !== undefined ? { error: input.error } : {}),
167
+ ...(input.usage ? { usage: input.usage } : {}),
168
+ };
169
+ }
@@ -0,0 +1,5 @@
1
+ export { AGENT_TOOL_DESCRIPTION, AGENT_TOOL_PROMPT_GUIDELINES, AGENT_TOOL_PROMPT_SNIPPET, EXPLORE_SYSTEM_PROMPT, boundExploreText, buildExploreResult, parseExploreAgentParams, } from "./explore-agent.js";
2
+ export { buildAgentCustomTool } from "./agent-tool.js";
3
+ export { ExploreOrchestrator } from "./orchestrator.js";
4
+ export { EXPLORE_ALLOWED_TOOLS, EXPLORE_CHILD_RESOURCE_LOADER_OPTIONS, EXPLORE_EXCLUDE_TOOLS, extrasOutsideExploreAllowlist, filterExploreCustomTools, isExploreAllowedTool, pinExploreToolNames, } from "./tool-policy.js";
5
+ export { EXPLORE_AGENT_NAME, OPERATOR_CHAT_AGENT_TOOL_NAME, } from "./types.js";