@tea-agent/loop-agent 0.42.0-next.9 → 0.42.1

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 (236) hide show
  1. package/CHANGELOG.md +111 -45
  2. package/dist/application/dag/run-dag.js +8 -2
  3. package/dist/application/evaluation/budget.js +19 -1
  4. package/dist/application/task-lifecycle/advance.js +26 -8
  5. package/dist/application/task-lifecycle/observe.js +43 -29
  6. package/dist/application/task-lifecycle/plan-transitions.js +5 -4
  7. package/dist/build-stamp.json +3 -3
  8. package/dist/cli/program.js +1 -1
  9. package/dist/commands/dag-rerun-task.js +2 -0
  10. package/dist/commands/task-source-prepare.js +3 -1
  11. package/dist/executors/dag-pi-executor.js +2584 -600
  12. package/dist/executors/pi-executor.js +22 -1
  13. package/dist/executors/pi-extension-resolver.js +14 -2
  14. package/dist/executors/pi-sdk-executor.js +140 -39
  15. package/dist/executors/shell-executor.js +135 -55
  16. package/dist/shared/dag-failure-category.js +6 -0
  17. package/dist/shared/frontend-execution-policy.js +23 -0
  18. package/dist/task/config-types.js +4 -0
  19. package/dist/task/contract/apply.js +36 -2
  20. package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
  21. package/dist/task/source-prepare/ledger-review.js +6 -9
  22. package/dist/task/source-prepare/parse-intent.js +7 -0
  23. package/dist/task/source-prepare/semantic-intake.js +16 -26
  24. package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
  25. package/dist/task/source-references.js +48 -23
  26. package/dist/worker/console/chat/assistant-content.js +23 -2
  27. package/dist/worker/console/chat/browser-policy.js +143 -0
  28. package/dist/worker/console/chat/browser-routes.js +148 -0
  29. package/dist/worker/console/chat/chat-event-store.js +4 -2
  30. package/dist/worker/console/chat/explore-tools.js +13 -0
  31. package/dist/worker/console/chat/pi-runtime.js +173 -94
  32. package/dist/worker/console/chat/resource-loader.js +4 -1
  33. package/dist/worker/console/chat/routes.js +208 -66
  34. package/dist/worker/console/chat/scm-routes.js +217 -0
  35. package/dist/worker/console/chat/scm-service.js +283 -0
  36. package/dist/worker/console/chat/scm-tools.js +111 -0
  37. package/dist/worker/console/chat/sdd-data-alignment.js +78 -11
  38. package/dist/worker/console/chat/session-catalog.js +32 -0
  39. package/dist/worker/console/chat/session-mode-view.js +48 -0
  40. package/dist/worker/console/chat/session-mode.js +218 -0
  41. package/dist/worker/console/chat/session-store.js +27 -7
  42. package/dist/worker/console/chat/shortcuts.js +6 -0
  43. package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
  44. package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
  45. package/dist/worker/console/chat/subagents/index.js +5 -0
  46. package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
  47. package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
  48. package/dist/worker/console/chat/subagents/types.js +13 -0
  49. package/dist/worker/console/chat/terminal-routes.js +216 -0
  50. package/dist/worker/console/chat/terminal-sessions.js +284 -0
  51. package/dist/worker/console/chat/terminal-tools.js +199 -0
  52. package/dist/worker/console/chat/tool-preview.js +21 -0
  53. package/dist/worker/console/chat/tools.js +13 -1
  54. package/dist/worker/console/chat/turn-process.js +1 -0
  55. package/dist/worker/console/interview/tools.js +1 -0
  56. package/dist/worker/console/server.js +2 -28
  57. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-C9eI7nEo.js} +1 -1
  58. package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-VJWsxWhB.js} +1 -1
  59. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-BGcyiMSo.js} +1 -1
  60. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-BScnFwyi.js} +1 -1
  61. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Bl9_BsLi.js} +1 -1
  62. package/dist/worker/console/static/assets/channel-B6sQYuOE.js +1 -0
  63. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-F1uKxiUt.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-rSRMynVu.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-DAsJm1WD.js} +1 -1
  66. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-DQIdVI0a.js} +1 -1
  67. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-4A_8rJ-Q.js} +1 -1
  68. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-BHnT-vjh.js} +1 -1
  69. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-Rq7QKBkn.js} +1 -1
  70. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BXh-AI2u.js} +1 -1
  71. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-BblcLy8m.js} +1 -1
  72. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-zHZmVnB6.js} +1 -1
  73. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Z6s5QoeI.js +1 -0
  74. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Z6s5QoeI.js +1 -0
  75. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-Cmwz0Wi4.js} +1 -1
  76. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-Dq76MHQU.js} +1 -1
  77. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-zPahV1fM.js} +1 -1
  78. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-DQJ3xPc_.js} +1 -1
  79. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BwX7u6fv.js} +1 -1
  80. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CodnDCco.js} +1 -1
  81. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-gAjtWkxG.js} +1 -1
  82. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-CGCqz0cS.js} +1 -1
  83. package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-RHwYEywd.js} +1 -1
  84. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-B6seb_p_.js} +1 -1
  85. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-CmwCKYe8.js} +1 -1
  86. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-BfNsaZ0V.js} +1 -1
  87. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-zmYgi_Z3.js} +1 -1
  88. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-BDEzzSKF.js} +1 -1
  89. package/dist/worker/console/static/assets/index-DgenUAfc.js +468 -0
  90. package/dist/worker/console/static/assets/index-aF4u-Y__.css +1 -0
  91. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-DeSz8l42.js} +1 -1
  92. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-B1LV29aH.js} +1 -1
  93. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-CCw4W2oU.js} +1 -1
  94. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BozaMoRJ.js} +1 -1
  95. package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-G1JR40SX.js} +1 -1
  96. package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-CCgPF0oA.js} +5 -5
  97. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-CEqnCHnD.js} +1 -1
  98. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-DpmQPKuI.js} +1 -1
  99. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-BP501AxR.js} +1 -1
  100. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-D1dBhl_A.js} +1 -1
  101. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-DgZDS5pY.js} +1 -1
  102. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-BzB3pyIq.js} +1 -1
  103. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-B9ZprFFP.js} +1 -1
  104. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CC2-_XrA.js} +1 -1
  105. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-UVtAUqgB.js} +1 -1
  106. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-CWcS5JYG.js} +1 -1
  107. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BooX8u1Q.js +1 -0
  108. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-C4p-dn06.js} +2 -2
  109. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D64_eqqE.js +8 -0
  110. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-Dnz52OHX.js} +1 -1
  111. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-CzTE0Nfu.js} +1 -1
  112. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-NAR9cjQQ.js} +1 -1
  113. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-DVMk8413.js} +1 -1
  114. package/dist/worker/console/static/index.html +2 -2
  115. package/dist/worker/console/static-src/chat-view-types.js +1 -1
  116. package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
  117. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +142 -7
  118. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +44 -0
  119. package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
  120. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
  121. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
  122. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
  123. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
  124. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +33 -18
  125. package/dist/worker/console/static-src/operator-chat/useChatStream.js +48 -16
  126. package/dist/worker/console/static-src/operator-chat/useChatThread.js +170 -7
  127. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -7
  128. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
  129. package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
  130. package/dist/worker/console/workspace-context.js +15 -1
  131. package/dist/worker/observe/node-transparency.js +81 -72
  132. package/dist/worker/observe/routes.js +20 -1
  133. package/dist/worker/observe/static/constants.js +22 -22
  134. package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
  135. package/dist/worker/observe/static/dag-history-labels.js +1 -0
  136. package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
  137. package/dist/worker/observe/static/dag-inspector-humanize.js +342 -0
  138. package/dist/worker/observe/static/dag-node-purpose.js +10 -10
  139. package/dist/worker/observe/static/dom.js +20 -1
  140. package/dist/worker/observe/static/format-pool.d.ts +2 -0
  141. package/dist/worker/observe/static/format-pool.js +6 -0
  142. package/dist/worker/observe/static/format.js +7 -0
  143. package/dist/worker/observe/static/index.html +4 -4
  144. package/dist/worker/observe/static/inspect-workspace.js +34 -7
  145. package/dist/worker/observe/static/inspector-submission.js +32 -0
  146. package/dist/worker/observe/static/kpi.js +1 -0
  147. package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
  148. package/dist/worker/observe/static/relations.js +7 -5
  149. package/dist/worker/observe/static/router.js +13 -0
  150. package/dist/worker/observe/static/run-processing.js +2 -0
  151. package/dist/worker/observe/static/shell-chrome.js +36 -3
  152. package/dist/worker/observe/static/state.js +35 -2
  153. package/dist/worker/observe/static/styles.css +431 -39
  154. package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
  155. package/dist/worker/observe/static/task-failure-labels.js +67 -0
  156. package/dist/worker/observe/static/task-history.js +12 -0
  157. package/dist/worker/observe/static/views/batch.js +6 -13
  158. package/dist/worker/observe/static/views/dag-graph.js +51 -4
  159. package/dist/worker/observe/static/views/dag-inspector.js +1069 -426
  160. package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
  161. package/dist/worker/observe/static/views/dag.d.ts +6 -0
  162. package/dist/worker/observe/static/views/dag.js +64 -22
  163. package/dist/worker/observe/static/views/dags.js +2 -0
  164. package/dist/worker/observe/static/views/dashboard.js +21 -12
  165. package/dist/worker/observe/static/views/failures.js +21 -11
  166. package/dist/worker/observe/static/views/feature.js +11 -29
  167. package/dist/worker/observe/static/views/pool.js +37 -28
  168. package/dist/worker/observe/static/views/run.js +48 -5
  169. package/dist/worker/observe/static/views/session-timeline.js +194 -243
  170. package/dist/worker/observe/static/views/task.js +81 -62
  171. package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
  172. package/dist/workflows/dag/budget-enforcement.js +53 -3
  173. package/dist/workflows/dag/dag-retry-schema.js +3 -0
  174. package/dist/workflows/dag/frontend-capacity.js +9 -0
  175. package/dist/workflows/dag/frontend-contract-facts.js +130 -0
  176. package/dist/workflows/dag/frontend-design-policy.js +103 -17
  177. package/dist/workflows/dag/frontend-durable-tools.js +193 -0
  178. package/dist/workflows/dag/frontend-execution-groups.js +24 -0
  179. package/dist/workflows/dag/frontend-implementation-contract.js +423 -41
  180. package/dist/workflows/dag/frontend-input-projection.js +76 -0
  181. package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
  182. package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
  183. package/dist/workflows/dag/frontend-plan-render.js +21 -3
  184. package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
  185. package/dist/workflows/dag/frontend-recovery-controller.js +32 -8
  186. package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
  187. package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
  188. package/dist/workflows/dag/frontend-recovery-run.js +146 -16
  189. package/dist/workflows/dag/frontend-review-scopes.js +139 -0
  190. package/dist/workflows/dag/frontend-risk.js +2 -0
  191. package/dist/workflows/dag/frontend-session-budget.js +249 -0
  192. package/dist/workflows/dag/frontend-shadow-dual-write.js +37 -3
  193. package/dist/workflows/dag/frontend-shape-facts.js +12 -2
  194. package/dist/workflows/dag/frontend-shape.js +46 -7
  195. package/dist/workflows/dag/frontend-test-execution-evidence.js +49 -0
  196. package/dist/workflows/dag/frontend-typed-event-store.js +14 -0
  197. package/dist/workflows/dag/frontend-verification-trace.js +38 -4
  198. package/dist/workflows/dag/frontend-writer-admission.js +2 -2
  199. package/dist/workflows/dag/init-hybrid.js +70 -35
  200. package/dist/workflows/dag/node-execution.js +225 -226
  201. package/dist/workflows/dag/prompt.js +4 -0
  202. package/dist/workflows/dag/recovery-lease.js +106 -16
  203. package/dist/workflows/dag/rerun-feedback.js +1 -0
  204. package/dist/workflows/dag/rerun-plan.js +96 -4
  205. package/dist/workflows/dag/rerun-run.js +28 -6
  206. package/dist/workflows/dag/rerun-task.js +221 -18
  207. package/dist/workflows/dag/retry-policy.js +27 -10
  208. package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
  209. package/dist/workflows/dag/runner.js +246 -19
  210. package/dist/workflows/dag/structured-output-repair.js +4 -1
  211. package/dist/workflows/dag/types.js +10 -3
  212. package/dist/workflows/dag/validate.js +10 -8
  213. package/dist/workflows/dag/workspace-checkpoint.js +66 -0
  214. package/docs/operations/README.md +2 -0
  215. package/docs/templates/agent-dag.schema.json +2 -2
  216. package/docs/templates/backend-test-dag.json +6 -4
  217. package/docs/templates/frontend-design-contract.md +4 -4
  218. package/docs/templates/frontend-implementation-contract.schema.json +68 -4
  219. package/docs/templates/frontend-implementation-dag.json +5 -5
  220. package/harness.json +1 -1
  221. package/package.json +8 -6
  222. package/skills/frontend-contract/SKILL.md +2 -1
  223. package/skills/frontend-contract/references/contract-protocol.md +19 -3
  224. package/skills/frontend-design-review/SKILL.md +12 -11
  225. package/skills/frontend-plan/SKILL.md +22 -2
  226. package/skills/frontend-plan/references/decision-contract.md +114 -5
  227. package/skills/frontend-plan/references/design-decisions.md +32 -0
  228. package/skills/frontend-review/SKILL.md +10 -11
  229. package/skills/frontend-scout/references/scout-evidence.md +4 -0
  230. package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
  231. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
  232. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
  233. package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
  234. package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
  235. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
  236. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
@@ -33,6 +33,8 @@ import { parseProviderError, } from "./provider-error.js";
33
33
  import { projectCompactSnapshot, } from "./chat-event-store.js";
34
34
  import { extractUsageSample } from "./usage.js";
35
35
  import { OPERATOR_CHAT_ALLOWED_TOOLS, authorizeOperatorChatTool, assertNoWriteToolInList, } from "./tools.js";
36
+ import { authorizeChatSessionTool, DEFAULT_CONSOLE_CHAT_SESSION_MODE, PI_TODO_WRITE_DESCRIPTION, chatActiveToolBaseline, composeChatSystemPrompt, composeChatSystemPromptParts, computeChatActiveToolNames, shouldInjectGoalContinuation, shouldLoadWorkflowBusinessTools, } from "./session-mode.js";
37
+ import { buildAgentCustomTool, ExploreOrchestrator, } from "./subagents/index.js";
36
38
  import { createOperatorChatResourceLoader, } from "./resource-loader.js";
37
39
  import { applySkillAutoInvocationPreferences, isAutoInvocationEnabled, loadResourcePreferences, skillResourceId, } from "./resource-preferences-store.js";
38
40
  import { buildModelCallableToolSchemas } from "./tool-adapter.js";
@@ -42,6 +44,8 @@ import { resolveDefaultChatModel, resolveLowChatModel, } from "./model-resolver.
42
44
  import { filterActiveInterviewTools } from "../interview/tools.js";
43
45
  import { RUNTIME_CONTEXT_TEXT_MAX, redactRuntimeText, } from "./runtime-context.js";
44
46
  import { buildTodoContinuationContext, } from "./todos.js";
47
+ import { buildPersistentTerminalTools } from "./terminal-tools.js";
48
+ import { buildScmTools } from "./scm-tools.js";
45
49
  import { buildGoalContinuationContext, } from "./goals.js";
46
50
  import { injectWizardLocalHeaders, } from "./sdd-data-alignment.js";
47
51
  /**
@@ -74,15 +78,6 @@ export const OPERATOR_CHAT_SYSTEM_PROMPT_BASE = [
74
78
  "Continue until success, user stop, or no safe recovery remains; then stop and report failure/evidence. Never create a new task for a transient failure or replace repair-pi with direct edits.",
75
79
  "Narrate long-DAG progress briefly at start, meaningful phase changes/findings, verification failure/recovery, prolonged quiet and terminal state: say what finished and what is next. Never narrate unchanged operationWait polls or heartbeats, or claim beyond structured facts.",
76
80
  ].join("\n");
77
- /** Compose the inspectable system prompt actually injected into Operator Chat. */
78
- export function composeOperatorChatSystemPrompt(input) {
79
- return [
80
- OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
81
- ...(input.systemPromptSuffix?.trim()
82
- ? [input.systemPromptSuffix.trim()]
83
- : []),
84
- ].join("\n\n");
85
- }
86
81
  /**
87
82
  * Built-in SDK tool names registered for every Chat session (ADR 0011).
88
83
  * Full Pi builtins: read/write/edit/bash/grep/find/ls. safe-* custom tools
@@ -111,26 +106,8 @@ export const OPERATOR_CHAT_SAFE_EXPLORE_TOOL_IDS = Object.freeze([
111
106
  "git-diff",
112
107
  ]);
113
108
  export function OPERATOR_CHAT_ACTIVE_TOOL_NAMES() {
114
- return [
115
- ...OPERATOR_CHAT_ALLOWED_TOOLS,
116
- ...OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS,
117
- ...OPERATOR_CHAT_SAFE_EXPLORE_TOOL_IDS,
118
- ];
109
+ return chatActiveToolBaseline("workflow");
119
110
  }
120
- /**
121
- * Non-Pi write / coding channels that must never be activated in Operator
122
- * Chat even when a user/project extension registers them (ADR 0011 / 0012).
123
- * Mirrors the NON_PI_WRITE_CHANNELS guard in tools.ts for the Gate 2 pin.
124
- */
125
- const OPERATOR_CHAT_EXTENSION_DENIED_TOOL_IDS = new Set([
126
- "apply_patch",
127
- "apply-patch",
128
- "full-tools",
129
- "full_tools",
130
- "coding-chat",
131
- "coding_chat",
132
- "shell",
133
- ].map((t) => t.toLowerCase()));
134
111
  /**
135
112
  * Gate 2 active-tool set for a live session (ADR 0012): the operator-chat
136
113
  * baseline (operator actions + Pi builtins + safe explore) PLUS extension
@@ -141,24 +118,8 @@ const OPERATOR_CHAT_EXTENSION_DENIED_TOOL_IDS = new Set([
141
118
  * coding-chat). Unknown/denied names are never activated; Gate 3 still
142
119
  * re-authorizes every tool call per invocation.
143
120
  */
144
- export function computeOperatorChatActiveToolNames(session) {
145
- const baseline = OPERATOR_CHAT_ACTIVE_TOOL_NAMES();
146
- if (!session?.getAllTools)
147
- return baseline;
148
- const baselineLower = new Set(baseline.map((t) => t.toLowerCase()));
149
- const extras = [];
150
- for (const tool of session.getAllTools()) {
151
- const name = tool?.name;
152
- if (!name || typeof name !== "string")
153
- continue;
154
- const lower = name.toLowerCase();
155
- if (baselineLower.has(lower))
156
- continue;
157
- if (OPERATOR_CHAT_EXTENSION_DENIED_TOOL_IDS.has(lower))
158
- continue;
159
- extras.push(name);
160
- }
161
- return extras.length > 0 ? [...baseline, ...extras] : baseline;
121
+ export function computeOperatorChatActiveToolNames(session, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
122
+ return computeChatActiveToolNames(session, sessionMode);
162
123
  }
163
124
  const OPERATOR_CHAT_SESSION_EXCLUDE_TOOLS = Object.freeze([
164
125
  "apply_patch",
@@ -214,8 +175,8 @@ async function bindSessionExtensions(session) {
214
175
  return;
215
176
  await session.bindExtensions(createConsoleExtensionBindings());
216
177
  }
217
- function pinOperatorChatTools(session) {
218
- session.setActiveToolsByName(computeOperatorChatActiveToolNames(session));
178
+ function pinOperatorChatTools(session, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
179
+ session.setActiveToolsByName(computeChatActiveToolNames(session, sessionMode));
219
180
  }
220
181
  /**
221
182
  * Pi has no hook result that merely appends instructions to its built-in
@@ -367,7 +328,7 @@ async function buildCustomOperatorTools(options) {
367
328
  promptSnippet: schema.description,
368
329
  parameters: parameters,
369
330
  async execute(toolCallId, params) {
370
- const decision = authorizeOperatorChatTool(schema.toolId);
331
+ const decision = authorizeChatSessionTool(schema.toolId, options.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE);
371
332
  if (!decision.ok) {
372
333
  return {
373
334
  content: [
@@ -384,7 +345,7 @@ async function buildCustomOperatorTools(options) {
384
345
  }
385
346
  // Defer require to avoid circular import at module load.
386
347
  const { dispatchChatToolCall } = await import("./session-store.js");
387
- const result = await dispatchChatToolCall(ctx, decision.toolId, params, toolCallId);
348
+ const result = await dispatchChatToolCall(ctx, decision.toolId, params, toolCallId, options.sessionMode);
388
349
  const payload = result.ok
389
350
  ? result.result
390
351
  : { error: result.message, code: result.errorCode };
@@ -442,11 +403,14 @@ async function buildTodoWriteTool(options) {
442
403
  import("typebox"),
443
404
  import("@earendil-works/pi-coding-agent"),
444
405
  ]);
406
+ const description = options.sessionMode === "pi"
407
+ ? PI_TODO_WRITE_DESCRIPTION
408
+ : TODO_WRITE_DESCRIPTION;
445
409
  return defineTool({
446
410
  name: "todo_write",
447
411
  label: "Update todo list",
448
- description: TODO_WRITE_DESCRIPTION,
449
- promptSnippet: TODO_WRITE_DESCRIPTION,
412
+ description,
413
+ promptSnippet: description,
450
414
  parameters: Type.Object({
451
415
  todos: Type.Array(Type.Object({
452
416
  content: Type.String({ description: "Short imperative task description." }),
@@ -1019,19 +983,43 @@ export class ConsolePiRuntime {
1019
983
  sessionInit = new Map();
1020
984
  /** Resolved model binding after materialization (create may return before this is known). */
1021
985
  sessionModels = new Map();
986
+ /** Durable session mode; record is the source of truth, this is the live handle cache. */
987
+ sessionModes = new Map();
1022
988
  /** SessionIds disposed while materialization was still in flight. */
1023
989
  disposedSessions = new Set();
1024
990
  compactionControllers = new Map();
1025
991
  siftStates = new Map();
1026
992
  /** Session-less tool definition caches (no AgentSession state). */
1027
- /** Session-less tool definition caches (no AgentSession state). */
1028
- operatorCustomToolsPromise;
993
+ operatorCustomToolsByMode = new Map();
1029
994
  exploreCustomToolsPromise;
995
+ exploreOrchestrator;
1030
996
  bindings;
1031
997
  constructor(options) {
1032
998
  this.options = options;
1033
999
  this.loader = createOperatorChatResourceLoader();
1034
1000
  this.bindings = options.bindings ?? createDefaultPiSdkBindings();
1001
+ this.exploreOrchestrator = new ExploreOrchestrator({
1002
+ cwd: this.options.cwd,
1003
+ bindings: this.bindings,
1004
+ loadSafeExploreCustomTools: () => this.loadExploreCustomTools(),
1005
+ // Fallback when the parent session's model is unknown at Agent-call
1006
+ // time (e.g. turn started before materialization finished): resolve
1007
+ // the default Chat model from the child services' modelRuntime.
1008
+ resolveModel: async (modelRuntime) => {
1009
+ const available = this.bindings.listAvailableModels({ modelRuntime });
1010
+ const descriptor = await resolveDefaultChatModel(this.options.cwd, available);
1011
+ if (!descriptor)
1012
+ return undefined;
1013
+ return this.bindings.resolveModel({
1014
+ modelRuntime,
1015
+ provider: descriptor.provider,
1016
+ modelId: descriptor.modelId,
1017
+ });
1018
+ },
1019
+ });
1020
+ }
1021
+ async syncAimaxAuthToken() {
1022
+ await this.options.sddDataAlignment?.auth?.getToken();
1035
1023
  }
1036
1024
  /** Absolute path for repo-scoped resource preferences (may be undefined in unit tests). */
1037
1025
  get resourcePreferencesPath() {
@@ -1061,10 +1049,20 @@ export class ConsolePiRuntime {
1061
1049
  * after a PATCH sees the latest resource-preferences.json (not create-time
1062
1050
  * closure state).
1063
1051
  */
1052
+ sessionModeOf(sessionId) {
1053
+ if (!sessionId)
1054
+ return DEFAULT_CONSOLE_CHAT_SESSION_MODE;
1055
+ return this.sessionModes.get(sessionId) ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE;
1056
+ }
1057
+ rememberSessionMode(sessionId, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
1058
+ this.sessionModes.set(sessionId, sessionMode);
1059
+ return sessionMode;
1060
+ }
1064
1061
  async buildResourceLoaderOptions(base = {}, sessionId) {
1065
1062
  const inheritedFactories = Array.isArray(base.extensionFactories)
1066
1063
  ? base.extensionFactories
1067
1064
  : [];
1065
+ const sessionMode = this.sessionModeOf(sessionId);
1068
1066
  const todoContextExtension = sessionId && this.options.todoRegistry
1069
1067
  ? createTodoCompactionContextExtension({
1070
1068
  sessionId,
@@ -1074,7 +1072,9 @@ export class ConsolePiRuntime {
1074
1072
  const pressureExtension = sessionId
1075
1073
  ? this.createContextPressureInlineExtension(sessionId)
1076
1074
  : undefined;
1077
- const goalContextExtension = sessionId && this.options.goalRegistry
1075
+ const goalContextExtension = sessionId &&
1076
+ this.options.goalRegistry &&
1077
+ shouldInjectGoalContinuation(sessionMode)
1078
1078
  ? createGoalCompactionContextExtension({
1079
1079
  sessionId,
1080
1080
  registry: this.options.goalRegistry,
@@ -1320,8 +1320,10 @@ export class ConsolePiRuntime {
1320
1320
  * `initializing: true`; dependent routes wait on the same promise.
1321
1321
  */
1322
1322
  async createSession(init) {
1323
+ await this.syncAimaxAuthToken();
1323
1324
  const sessionId = `operator-chat-${randomBytes(12).toString("hex")}`;
1324
1325
  this.disposedSessions.delete(sessionId);
1326
+ const sessionMode = this.rememberSessionMode(sessionId, init?.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE);
1325
1327
  const sessionManager = await this.bindings.createSessionManager({
1326
1328
  cwd: this.options.cwd,
1327
1329
  sessionDir: this.options.sessionDir,
@@ -1347,6 +1349,7 @@ export class ConsolePiRuntime {
1347
1349
  sessionManager,
1348
1350
  model: init?.model,
1349
1351
  systemPromptSuffix: init?.systemPromptSuffix,
1352
+ sessionMode,
1350
1353
  // Create path must resolve default model from the SAME services object
1351
1354
  // used for formal materialization (no throwaway createServices).
1352
1355
  resolveDefaultModel: !init?.model,
@@ -1356,7 +1359,8 @@ export class ConsolePiRuntime {
1356
1359
  sessionFile,
1357
1360
  createdAt: new Date().toISOString(),
1358
1361
  model: init?.model,
1359
- activeTools: OPERATOR_CHAT_ACTIVE_TOOL_NAMES(),
1362
+ activeTools: chatActiveToolBaseline(sessionMode),
1363
+ sessionMode,
1360
1364
  initializing: true,
1361
1365
  };
1362
1366
  }
@@ -1417,14 +1421,17 @@ export class ConsolePiRuntime {
1417
1421
  async materializeSession(sessionId, init) {
1418
1422
  if (this.disposedSessions.has(sessionId))
1419
1423
  return;
1424
+ await this.syncAimaxAuthToken();
1425
+ const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? this.sessionModeOf(sessionId));
1420
1426
  const agentDir = await safeGetAgentDir(this.bindings);
1421
1427
  // Capture before services parse models.json. A concurrent edit can therefore
1422
1428
  // cause at most one harmless extra refresh, never a missed next-turn refresh.
1423
1429
  const modelConfigFile = await captureModelConfigFile(agentDir);
1424
- const appendSystemPrompt = [
1425
- OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1426
- ...(init.systemPromptSuffix ? [init.systemPromptSuffix] : []),
1427
- ];
1430
+ const appendSystemPrompt = composeChatSystemPromptParts({
1431
+ sessionMode,
1432
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1433
+ systemPromptSuffix: init.systemPromptSuffix,
1434
+ });
1428
1435
  // Single services construction for formal materialization (+ optional
1429
1436
  // default-model resolve from the same modelRuntime). Repo-scoped skill
1430
1437
  // auto-invocation prefs inject skillsOverride without touching SKILL.md.
@@ -1475,7 +1482,7 @@ export class ConsolePiRuntime {
1475
1482
  return;
1476
1483
  }
1477
1484
  await bindSessionExtensions(session);
1478
- pinOperatorChatTools(session);
1485
+ pinOperatorChatTools(session, sessionMode);
1479
1486
  // Register then re-check dispose race: a concurrent dispose must win.
1480
1487
  this.sessions.set(sessionId, session);
1481
1488
  this.sessionManagers.set(sessionId, init.sessionManager);
@@ -1503,6 +1510,7 @@ export class ConsolePiRuntime {
1503
1510
  this.modelRuntimes.delete(sessionId);
1504
1511
  this.modelConfigFiles.delete(sessionId);
1505
1512
  this.sessionModels.delete(sessionId);
1513
+ this.sessionModes.delete(sessionId);
1506
1514
  this.serviceScopes.delete(sessionId);
1507
1515
  this.revisions.delete(sessionId);
1508
1516
  this.thinkingSelections.delete(sessionId);
@@ -1510,8 +1518,12 @@ export class ConsolePiRuntime {
1510
1518
  }
1511
1519
  /** Cached session-less custom tool definitions (operator + explore). */
1512
1520
  async loadCustomTools(sessionId) {
1521
+ const sessionMode = this.sessionModeOf(sessionId);
1522
+ const loadWorkflowTools = shouldLoadWorkflowBusinessTools(sessionMode);
1513
1523
  const [operatorCustomTools, exploreCustomTools] = await Promise.all([
1514
- this.loadOperatorCustomTools(),
1524
+ loadWorkflowTools
1525
+ ? this.loadOperatorCustomTools(sessionMode)
1526
+ : Promise.resolve([]),
1515
1527
  this.loadExploreCustomTools(),
1516
1528
  ]);
1517
1529
  const questionTool = this.options.questionRegistry
@@ -1521,33 +1533,77 @@ export class ConsolePiRuntime {
1521
1533
  })
1522
1534
  : undefined;
1523
1535
  const todoTool = this.options.todoRegistry
1524
- ? await buildTodoWriteTool({ sessionId, registry: this.options.todoRegistry })
1536
+ ? await buildTodoWriteTool({
1537
+ sessionId,
1538
+ registry: this.options.todoRegistry,
1539
+ sessionMode,
1540
+ })
1525
1541
  : undefined;
1526
- const goalTools = this.options.goalRegistry
1542
+ const goalTools = loadWorkflowTools && this.options.goalRegistry
1527
1543
  ? await buildGoalTools({ sessionId, registry: this.options.goalRegistry })
1528
1544
  : [];
1545
+ const terminalTools = this.options.terminalRegistry
1546
+ ? await buildPersistentTerminalTools({
1547
+ ownerSessionId: sessionId,
1548
+ registry: this.options.terminalRegistry,
1549
+ defaultCwd: this.options.cwd,
1550
+ }).catch((error) => {
1551
+ process.stderr.write(`[console] chat terminal-tools build failed: ${error instanceof Error ? error.message : String(error)}\n`);
1552
+ return [];
1553
+ })
1554
+ : [];
1555
+ const scmTools = this.options.scmService
1556
+ ? await buildScmTools({ scm: this.options.scmService }).catch((error) => {
1557
+ process.stderr.write(`[console] chat scm-tools build failed: ${error instanceof Error ? error.message : String(error)}\n`);
1558
+ return [];
1559
+ })
1560
+ : [];
1561
+ const agentTool = await buildAgentCustomTool({
1562
+ parentSessionId: sessionId,
1563
+ orchestrator: this.exploreOrchestrator,
1564
+ resolveModel: () => {
1565
+ const model = this.sessionModels.get(sessionId);
1566
+ const modelRuntime = this.modelRuntimes.get(sessionId);
1567
+ if (!model || !modelRuntime)
1568
+ return undefined;
1569
+ return this.bindings.resolveModel({
1570
+ modelRuntime,
1571
+ provider: model.provider,
1572
+ modelId: model.modelId,
1573
+ });
1574
+ },
1575
+ }).catch((error) => {
1576
+ process.stderr.write(`[console] chat Agent tool build failed: ${error instanceof Error ? error.message : String(error)}\n`);
1577
+ return undefined;
1578
+ });
1529
1579
  return [
1530
1580
  ...operatorCustomTools,
1531
1581
  ...exploreCustomTools,
1532
1582
  ...(questionTool ? [questionTool] : []),
1533
1583
  ...(todoTool ? [todoTool] : []),
1534
1584
  ...goalTools,
1585
+ ...terminalTools,
1586
+ ...scmTools,
1587
+ ...(agentTool ? [agentTool] : []),
1535
1588
  ];
1536
1589
  }
1537
- loadOperatorCustomTools() {
1590
+ loadOperatorCustomTools(sessionMode) {
1538
1591
  if (!this.options.actionContext)
1539
1592
  return Promise.resolve([]);
1540
- if (!this.operatorCustomToolsPromise) {
1541
- this.operatorCustomToolsPromise = buildCustomOperatorTools({
1542
- actionContext: this.options.actionContext,
1543
- }).catch((error) => {
1544
- process.stderr.write(`[console] chat custom-tools build failed: ${error instanceof Error ? error.message : String(error)}\n`);
1545
- // Allow a later create/reopen to retry after a transient failure.
1546
- this.operatorCustomToolsPromise = undefined;
1547
- return [];
1548
- });
1549
- }
1550
- return this.operatorCustomToolsPromise;
1593
+ const cached = this.operatorCustomToolsByMode.get(sessionMode);
1594
+ if (cached)
1595
+ return cached;
1596
+ const promise = buildCustomOperatorTools({
1597
+ actionContext: this.options.actionContext,
1598
+ sessionMode,
1599
+ }).catch((error) => {
1600
+ process.stderr.write(`[console] chat custom-tools build failed: ${error instanceof Error ? error.message : String(error)}\n`);
1601
+ // Allow a later create/reopen to retry after a transient failure.
1602
+ this.operatorCustomToolsByMode.delete(sessionMode);
1603
+ return [];
1604
+ });
1605
+ this.operatorCustomToolsByMode.set(sessionMode, promise);
1606
+ return promise;
1551
1607
  }
1552
1608
  loadExploreCustomTools() {
1553
1609
  if (!this.exploreCustomToolsPromise) {
@@ -1565,6 +1621,7 @@ export class ConsolePiRuntime {
1565
1621
  if (!this.bindings.forkSessionManager)
1566
1622
  throw new Error("SESSION_FORK_UNSUPPORTED");
1567
1623
  const sessionId = `operator-chat-${randomBytes(12).toString("hex")}`;
1624
+ const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE);
1568
1625
  const sessionManager = await this.bindings.forkSessionManager({
1569
1626
  sourcePath: init.sourceSessionFile,
1570
1627
  targetCwd: this.options.cwd,
@@ -1575,8 +1632,8 @@ export class ConsolePiRuntime {
1575
1632
  sessionId,
1576
1633
  sessionManager,
1577
1634
  model: init.model,
1635
+ sessionMode,
1578
1636
  });
1579
- built.session.setActiveToolsByName(computeOperatorChatActiveToolNames(built.session));
1580
1637
  this.sessions.set(sessionId, built.session);
1581
1638
  this.sessionManagers.set(sessionId, sessionManager);
1582
1639
  this.modelRuntimes.set(sessionId, built.modelRuntime);
@@ -1592,6 +1649,7 @@ export class ConsolePiRuntime {
1592
1649
  createdAt: new Date().toISOString(),
1593
1650
  model: init.model,
1594
1651
  activeTools: built.session.getActiveToolNames(),
1652
+ sessionMode,
1595
1653
  };
1596
1654
  }
1597
1655
  async branchSession(input) {
@@ -1770,15 +1828,22 @@ export class ConsolePiRuntime {
1770
1828
  async getRuntimeSystemPrompt(sessionId) {
1771
1829
  await this.ensureSessionReady(sessionId).catch(() => undefined);
1772
1830
  const session = this.sessions.get(sessionId);
1773
- if (!session)
1774
- return composeOperatorChatSystemPrompt({});
1831
+ if (!session) {
1832
+ return composeChatSystemPrompt({
1833
+ sessionMode: this.sessionModeOf(sessionId),
1834
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1835
+ });
1836
+ }
1775
1837
  const fromState = session.state?.systemPrompt;
1776
1838
  const fromGetter = session.systemPrompt;
1777
1839
  const loader = this.serviceScopes.get(sessionId)?.resourceLoader;
1778
1840
  return (fromState ||
1779
1841
  fromGetter ||
1780
1842
  loader?.getSystemPrompt?.() ||
1781
- composeOperatorChatSystemPrompt({}));
1843
+ composeChatSystemPrompt({
1844
+ sessionMode: this.sessionModeOf(sessionId),
1845
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1846
+ }));
1782
1847
  }
1783
1848
  /**
1784
1849
  * Read the RAW system prompt for the snapshot, converting any projection
@@ -1871,6 +1936,7 @@ export class ConsolePiRuntime {
1871
1936
  if (services)
1872
1937
  this.applySessionCompactionOverride(sessionId, services, session);
1873
1938
  await this.refreshModelConfiguration(sessionId, "force");
1939
+ pinOperatorChatTools(session, this.sessionModeOf(sessionId));
1874
1940
  }
1875
1941
  catch (error) {
1876
1942
  return {
@@ -2307,7 +2373,9 @@ export class ConsolePiRuntime {
2307
2373
  * than silently creating a fresh empty session and losing prior context.
2308
2374
  */
2309
2375
  async reopenSession(init) {
2376
+ await this.syncAimaxAuthToken();
2310
2377
  const sessionId = init.sessionId;
2378
+ const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? this.sessionModeOf(sessionId));
2311
2379
  // Wait for in-flight create materialization rather than racing reopen.
2312
2380
  const initState = this.sessionInit.get(sessionId);
2313
2381
  if (initState?.status === "initializing") {
@@ -2384,6 +2452,7 @@ export class ConsolePiRuntime {
2384
2452
  sessionManager,
2385
2453
  model: init.model,
2386
2454
  systemPromptSuffix: init.systemPromptSuffix,
2455
+ sessionMode,
2387
2456
  });
2388
2457
  const session = built.session;
2389
2458
  // Prefer the SessionManager id (from the JSONL header). Some SDK
@@ -2406,7 +2475,6 @@ export class ConsolePiRuntime {
2406
2475
  message: `reopened Pi sessionId mismatch: expected ${sessionId}, got ${reportedId}`,
2407
2476
  };
2408
2477
  }
2409
- session.setActiveToolsByName(computeOperatorChatActiveToolNames(session));
2410
2478
  this.sessions.set(sessionId, session);
2411
2479
  this.sessionManagers.set(sessionId, sessionManager);
2412
2480
  this.modelRuntimes.set(sessionId, built.modelRuntime);
@@ -2424,6 +2492,7 @@ export class ConsolePiRuntime {
2424
2492
  createdAt: new Date().toISOString(),
2425
2493
  model: init.model,
2426
2494
  activeTools: session.getActiveToolNames(),
2495
+ sessionMode,
2427
2496
  },
2428
2497
  };
2429
2498
  }
@@ -2455,6 +2524,7 @@ export class ConsolePiRuntime {
2455
2524
  createdAt: new Date().toISOString(),
2456
2525
  model,
2457
2526
  activeTools: session.getActiveToolNames(),
2527
+ sessionMode: this.sessionModeOf(sessionId),
2458
2528
  };
2459
2529
  }
2460
2530
  /**
@@ -2463,12 +2533,14 @@ export class ConsolePiRuntime {
2463
2533
  * before AgentSession is ready. Tool definitions reuse runtime-level caches.
2464
2534
  */
2465
2535
  async buildSessionWithServices(init) {
2536
+ const sessionMode = this.rememberSessionMode(init.sessionId, init.sessionMode ?? this.sessionModeOf(init.sessionId));
2466
2537
  const agentDir = await safeGetAgentDir(this.bindings);
2467
2538
  const modelConfigFile = await captureModelConfigFile(agentDir);
2468
- const appendSystemPrompt = [
2469
- OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
2470
- ...(init.systemPromptSuffix ? [init.systemPromptSuffix] : []),
2471
- ];
2539
+ const appendSystemPrompt = composeChatSystemPromptParts({
2540
+ sessionMode,
2541
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
2542
+ systemPromptSuffix: init.systemPromptSuffix,
2543
+ });
2472
2544
  await this.refreshSkillPrefsCache().catch(() => undefined);
2473
2545
  const { services } = await this.bindings.createServices({
2474
2546
  cwd: this.options.cwd,
@@ -2493,7 +2565,7 @@ export class ConsolePiRuntime {
2493
2565
  ...(resolvedModel ? { model: resolvedModel } : {}),
2494
2566
  });
2495
2567
  await bindSessionExtensions(session);
2496
- pinOperatorChatTools(session);
2568
+ pinOperatorChatTools(session, sessionMode);
2497
2569
  this.applySessionCompactionOverride(init.sessionId, services, session);
2498
2570
  return {
2499
2571
  session,
@@ -2562,7 +2634,7 @@ export class ConsolePiRuntime {
2562
2634
  }
2563
2635
  /**
2564
2636
  * Send a prompt and stream events. Gate 3: each tool invocation is
2565
- * re-authorized by authorizeOperatorChatTool before the dispatcher runs it
2637
+ * re-authorized by authorizeChatSessionTool(mode) before the dispatcher runs it
2566
2638
  * (the dispatcher is wired by the HTTP layer, see chat-session.ts).
2567
2639
  *
2568
2640
  * Prompt ownership is acquired synchronously before the first await so two
@@ -2595,6 +2667,7 @@ export class ConsolePiRuntime {
2595
2667
  catch {
2596
2668
  // ignore
2597
2669
  }
2670
+ void this.exploreOrchestrator.abortByParent(sessionId);
2598
2671
  };
2599
2672
  try {
2600
2673
  if (options?.turnId && this.options.questionRegistry) {
@@ -2603,7 +2676,8 @@ export class ConsolePiRuntime {
2603
2676
  if (options?.turnId && this.options.todoRegistry) {
2604
2677
  this.options.todoRegistry.startTurn(sessionId, options.turnId);
2605
2678
  }
2606
- if (this.options.goalRegistry) {
2679
+ if (this.options.goalRegistry &&
2680
+ shouldInjectGoalContinuation(this.sessionModeOf(sessionId))) {
2607
2681
  this.options.goalRegistry.startTurn(sessionId, text);
2608
2682
  }
2609
2683
  // AC-FIX-002: if Stop already fired before readiness, never start model work.
@@ -2624,10 +2698,11 @@ export class ConsolePiRuntime {
2624
2698
  // existing closed interview allow/deny registry. The next ordinary turn
2625
2699
  // explicitly restores the normal Operator Chat set (baseline + extension
2626
2700
  // tools, ADR 0012).
2627
- const requestedTools = OPERATOR_CHAT_ACTIVE_TOOL_NAMES();
2628
- const activeTools = options?.mode === "requirement-interview"
2701
+ const sessionMode = this.sessionModeOf(sessionId);
2702
+ const requestedTools = chatActiveToolBaseline(sessionMode);
2703
+ const activeTools = options?.mode === "requirement-interview" && sessionMode === "workflow"
2629
2704
  ? filterActiveInterviewTools(requestedTools).allowed
2630
- : computeOperatorChatActiveToolNames(session);
2705
+ : computeChatActiveToolNames(session, sessionMode);
2631
2706
  session.setActiveToolsByName(activeTools);
2632
2707
  // Final pre-prompt fence: abort after tool pin must still skip session.prompt.
2633
2708
  if (options?.signal?.aborted) {
@@ -2965,15 +3040,18 @@ export class ConsolePiRuntime {
2965
3040
  this.modelRuntimes.delete(sessionId);
2966
3041
  this.modelConfigFiles.delete(sessionId);
2967
3042
  this.sessionModels.delete(sessionId);
3043
+ this.sessionModes.delete(sessionId);
2968
3044
  this.serviceScopes.delete(sessionId);
2969
3045
  this.revisions.delete(sessionId);
2970
3046
  this.thinkingSelections.delete(sessionId);
2971
3047
  this.reloadInFlight.delete(sessionId);
2972
3048
  this.pendingSteering.delete(sessionId);
3049
+ void this.exploreOrchestrator.abortByParent(sessionId);
2973
3050
  }
2974
3051
  disposeAll() {
2975
3052
  for (const id of [...this.sessions.keys()])
2976
3053
  this.dispose(id);
3054
+ void this.exploreOrchestrator.abortAll();
2977
3055
  }
2978
3056
  }
2979
3057
  function mapAssistantMessageEvent(type, sessionId, message) {
@@ -3278,7 +3356,8 @@ export function createDefaultPiSdkBindings() {
3278
3356
  };
3279
3357
  }
3280
3358
  /**
3281
- * Re-export the tool gate for the dispatcher layer so the per-tool-call gate
3282
- * (Gate 3) lives in exactly one place.
3359
+ * Re-export the tool gates for the dispatcher layer so the per-tool-call gate
3360
+ * (Gate 3) lives in exactly one place. authorizeChatSessionTool is the
3361
+ * mode-aware gate; the legacy name stays for workflow-only callers.
3283
3362
  */
3284
- export { authorizeOperatorChatTool, assertNoWriteToolInList, OPERATOR_CHAT_ALLOWED_TOOLS, };
3363
+ export { authorizeChatSessionTool, authorizeOperatorChatTool, assertNoWriteToolInList, OPERATOR_CHAT_ALLOWED_TOOLS, };
@@ -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);