@tea-agent/loop-agent 0.43.0-next.1 → 0.43.0-next.11

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 (207) hide show
  1. package/CHANGELOG.md +144 -9
  2. package/dist/application/dag/run-dag.js +5 -0
  3. package/dist/application/evaluation/budget.js +19 -1
  4. package/dist/application/task-lifecycle/advance.js +17 -4
  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/executors/dag-pi-executor.js +1237 -586
  9. package/dist/executors/pi-executor.js +22 -1
  10. package/dist/executors/pi-sdk-executor.js +140 -39
  11. package/dist/executors/shell-executor.js +8 -4
  12. package/dist/shared/frontend-execution-policy.js +23 -0
  13. package/dist/task/config-types.js +4 -0
  14. package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
  15. package/dist/task/source-prepare/ledger-review.js +6 -9
  16. package/dist/task/source-prepare/semantic-intake.js +16 -26
  17. package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
  18. package/dist/task/source-references.js +48 -23
  19. package/dist/worker/console/chat/assistant-content.js +23 -2
  20. package/dist/worker/console/chat/browser-policy.js +113 -0
  21. package/dist/worker/console/chat/browser-routes.js +140 -0
  22. package/dist/worker/console/chat/explore-tools.js +13 -0
  23. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
  24. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
  25. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
  26. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +86 -0
  27. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
  28. package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
  29. package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
  30. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
  31. package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
  32. package/dist/worker/console/chat/pi-runtime.js +207 -1178
  33. package/dist/worker/console/chat/routes.js +208 -71
  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 +65 -9
  38. package/dist/worker/console/chat/session-catalog.js +32 -0
  39. package/dist/worker/console/chat/session-mode-view.js +49 -0
  40. package/dist/worker/console/chat/session-mode.js +219 -0
  41. package/dist/worker/console/chat/session-store.js +22 -6
  42. package/dist/worker/console/chat/shortcuts.js +6 -0
  43. package/dist/worker/console/chat/terminal-routes.js +216 -0
  44. package/dist/worker/console/chat/terminal-sessions.js +348 -0
  45. package/dist/worker/console/chat/terminal-tools.js +211 -0
  46. package/dist/worker/console/chat/tool-preview.js +11 -0
  47. package/dist/worker/console/chat/tools.js +2 -0
  48. package/dist/worker/console/server.js +2 -28
  49. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B0Q0nClf.js → abnfDiagram-N423BO3Z-CTSK0xbU.js} +1 -1
  50. package/dist/worker/console/static/assets/{arc-DCPjC19G.js → arc-_2MfctFP.js} +1 -1
  51. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CP5n9jmG.js → architectureDiagram-T3A2C74G-DsDe2wTg.js} +1 -1
  52. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CmaWi_Wg.js → blockDiagram-VBNYF7ZC-Dtz_779U.js} +1 -1
  53. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Ct5tcMfZ.js → c4Diagram-5PPSVZJV-LaULi-gw.js} +1 -1
  54. package/dist/worker/console/static/assets/channel-CqS4yBWT.js +1 -0
  55. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C0osm1Zf.js → chunk-2GRJ4B5K-DuHBqqX8.js} +1 -1
  56. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CDWviED7.js → chunk-2Q5K7J3B-BZgZs1dn.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-mtSk1-j7.js → chunk-5RXB4S5H-tzwmlIO_.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D5J9PHC7.js → chunk-5VM5RSS4-CeYrLkSn.js} +1 -1
  59. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Begg4WAa.js → chunk-6Q2QTUOP-C92cgilm.js} +1 -1
  60. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-sF1AEy7T.js → chunk-GF5L2VYU-XOXpGC0F.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BqbsWXZb.js → chunk-JWPE2WC7-CGHbJyCH.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BKJrPcJ6.js → chunk-KBJHAD2P-Bs2-LTzf.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CcwNMRho.js → chunk-RYQCIY6F-rN7up7E4.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BEJi3iIs.js → chunk-XXDRQBXY-B_fWjKpR.js} +1 -1
  65. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DVOxNOq0.js +1 -0
  66. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DVOxNOq0.js +1 -0
  67. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-DRLgTvVu.js → cose-bilkent-JH36ORCC-DE3SdJvB.js} +1 -1
  68. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BoAVG3cs.js → cynefin-VYW2F7L2-B-4yuRAX.js} +1 -1
  69. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bzol06hR.js → cynefinDiagram-MW4NZA55-TiCPFuO8.js} +1 -1
  70. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UmliJM77.js → dagre-VZM6K2ZE-7crzLMzA.js} +1 -1
  71. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BWLcJkFo.js → diagram-7IWD3JNH-Cvr-x6Fd.js} +1 -1
  72. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CgApOm-O.js → diagram-B4RE2ZJO-BzcRHUjH.js} +1 -1
  73. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Bh3RgTLs.js → diagram-LBJQPF4R-BjkAXilh.js} +1 -1
  74. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Dsh-D5nE.js → diagram-Q27KOJAE-BkzyATK7.js} +1 -1
  75. package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dkbbpcpb.js → diagram-UB23O5K3-rZuTarYP.js} +1 -1
  76. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-3EBaA3t3.js → ebnfDiagram-BXEA7PRR-CAYENaN1.js} +1 -1
  77. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bq2Dc5ok.js → erDiagram-JOGREHBK-DCEam0Uq.js} +1 -1
  78. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-De7y55ha.js → flowDiagram-UKHOOZJN-e_dH4wVO.js} +1 -1
  79. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-fLiDbQRh.js → ganttDiagram-PKOTCBZU-CvZa2zOK.js} +1 -1
  80. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Du617mp1.js → gitGraphDiagram-DS77QQ5N-CbT5TuKp.js} +1 -1
  81. package/dist/worker/console/static/assets/index-CUPeoE8W.js +468 -0
  82. package/dist/worker/console/static/assets/index-SAANIK8g.css +1 -0
  83. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BwmlF-XP.js → infoDiagram-6WML65LV-BWzHQa_Z.js} +1 -1
  84. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-zsGtS59u.js → ishikawaDiagram-WSZJBQD7-BTjlovtU.js} +1 -1
  85. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DTTPTe3f.js → journeyDiagram-NVQOT4AX-DkvQoCc1.js} +1 -1
  86. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BkXg9aP-.js → kanban-definition-27J2QSJJ-CHSzGi3W.js} +1 -1
  87. package/dist/worker/console/static/assets/{linear-7U2ue5IE.js → linear-DZMkTCWH.js} +1 -1
  88. package/dist/worker/console/static/assets/{mermaid.core-BUuGHmWO.js → mermaid.core-BIMhty7A.js} +5 -5
  89. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-7q6fX0Sv.js → mindmap-definition-FAOFIHXS-Dx-tyMl3.js} +1 -1
  90. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkQPJN55.js → pegDiagram-VL7TDLO6-C6G34rbY.js} +1 -1
  91. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CCal_tfX.js → pieDiagram-7S7Q4E2Y-CHK9XOfm.js} +1 -1
  92. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js → quadrantDiagram-CIZ2JOQS-CDARkI0w.js} +1 -1
  93. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CJmww7D-.js → railroadDiagram-AXF67PYL-DiuUe0ZQ.js} +1 -1
  94. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D6ldWJEv.js → requirementDiagram-LRYGKXZP-RE61UR56.js} +1 -1
  95. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-VGh33I9e.js → sankeyDiagram-W5VNT64P-KDNLHMEG.js} +1 -1
  96. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CCkDrIjU.js → sequenceDiagram-SI44F4Z6-CeVgXnD9.js} +1 -1
  97. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-NC8f5Otb.js → sizeCapture-X5ZJPWSS-CXwGyjjG.js} +1 -1
  98. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-_92ezZdF.js → stateDiagram-OKZ733FA-BjTfozab.js} +1 -1
  99. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BXoYsyr2.js +1 -0
  100. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BdUCGtbP.js → swimlanes-SLNWSIFB-BaXOB8GT.js} +2 -2
  101. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DfKliS4e.js +8 -0
  102. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CdD1H8ia.js → timeline-definition-Z64GVDOM-C3r2hqUU.js} +1 -1
  103. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-Dkdk3oFo.js → vennDiagram-T6HMQDX7-MWKgl1S9.js} +1 -1
  104. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CKu2uPPY.js → wardleyDiagram-T6FBY63Y-C9iFcZPk.js} +1 -1
  105. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-ClqAnuG8.js → xychartDiagram-ELKLHX3M-DqnM24Jp.js} +1 -1
  106. package/dist/worker/console/static/index.html +2 -2
  107. package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +10 -0
  108. package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
  109. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +122 -10
  110. package/dist/worker/console/static-src/operator-chat/compaction-message.js +7 -0
  111. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +27 -15
  112. package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
  113. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
  114. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +10 -0
  115. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
  116. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +44 -24
  117. package/dist/worker/console/static-src/operator-chat/useChatStream.js +18 -2
  118. package/dist/worker/console/static-src/operator-chat/useChatThread.js +41 -8
  119. package/dist/worker/console/static-src/operator-chat/useComposer.js +26 -11
  120. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
  121. package/dist/worker/console/workspace-context.js +15 -1
  122. package/dist/worker/observe/node-transparency.js +81 -72
  123. package/dist/worker/observe/routes.js +20 -1
  124. package/dist/worker/observe/static/dag-inspector-humanize.js +3 -0
  125. package/dist/worker/observe/static/dom.js +20 -1
  126. package/dist/worker/observe/static/format-pool.d.ts +2 -0
  127. package/dist/worker/observe/static/format-pool.js +6 -0
  128. package/dist/worker/observe/static/format.js +7 -0
  129. package/dist/worker/observe/static/inspect-workspace.js +34 -7
  130. package/dist/worker/observe/static/inspector-submission.js +32 -0
  131. package/dist/worker/observe/static/kpi.js +1 -0
  132. package/dist/worker/observe/static/relations.js +2 -0
  133. package/dist/worker/observe/static/router.js +13 -0
  134. package/dist/worker/observe/static/run-processing.js +2 -0
  135. package/dist/worker/observe/static/shell-chrome.js +36 -3
  136. package/dist/worker/observe/static/state.js +35 -2
  137. package/dist/worker/observe/static/styles.css +263 -39
  138. package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
  139. package/dist/worker/observe/static/task-failure-labels.js +67 -0
  140. package/dist/worker/observe/static/task-history.js +12 -0
  141. package/dist/worker/observe/static/views/batch.js +6 -13
  142. package/dist/worker/observe/static/views/dag-graph.js +50 -3
  143. package/dist/worker/observe/static/views/dag-inspector.js +742 -268
  144. package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
  145. package/dist/worker/observe/static/views/dag.d.ts +6 -0
  146. package/dist/worker/observe/static/views/dag.js +49 -11
  147. package/dist/worker/observe/static/views/dags.js +2 -0
  148. package/dist/worker/observe/static/views/dashboard.js +21 -12
  149. package/dist/worker/observe/static/views/failures.js +21 -11
  150. package/dist/worker/observe/static/views/feature.js +11 -29
  151. package/dist/worker/observe/static/views/pool.js +37 -28
  152. package/dist/worker/observe/static/views/run.js +48 -5
  153. package/dist/worker/observe/static/views/session-timeline.js +189 -240
  154. package/dist/worker/observe/static/views/task.js +81 -62
  155. package/dist/workflows/dag/budget-enforcement.js +53 -3
  156. package/dist/workflows/dag/frontend-capacity.js +9 -0
  157. package/dist/workflows/dag/frontend-closeout.js +1 -1
  158. package/dist/workflows/dag/frontend-design-policy.js +2 -1
  159. package/dist/workflows/dag/frontend-durable-tools.js +193 -0
  160. package/dist/workflows/dag/frontend-execution-groups.js +24 -0
  161. package/dist/workflows/dag/frontend-implementation-contract.js +13 -0
  162. package/dist/workflows/dag/frontend-input-projection.js +76 -0
  163. package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
  164. package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
  165. package/dist/workflows/dag/frontend-plan-render.js +10 -3
  166. package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
  167. package/dist/workflows/dag/frontend-recovery-controller.js +7 -7
  168. package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
  169. package/dist/workflows/dag/frontend-recovery-run.js +4 -0
  170. package/dist/workflows/dag/frontend-review-context.js +2 -1
  171. package/dist/workflows/dag/frontend-review-scopes.js +139 -0
  172. package/dist/workflows/dag/frontend-session-budget.js +249 -0
  173. package/dist/workflows/dag/frontend-shadow-dual-write.js +20 -2
  174. package/dist/workflows/dag/frontend-shape-facts.js +12 -2
  175. package/dist/workflows/dag/frontend-shape.js +30 -1
  176. package/dist/workflows/dag/frontend-test-execution-evidence.js +3 -2
  177. package/dist/workflows/dag/frontend-typed-event-store.js +11 -0
  178. package/dist/workflows/dag/frontend-verification-trace.js +1 -1
  179. package/dist/workflows/dag/init-hybrid.js +65 -167
  180. package/dist/workflows/dag/node-execution.js +38 -155
  181. package/dist/workflows/dag/prompt.js +4 -0
  182. package/dist/workflows/dag/rerun-plan.js +7 -1
  183. package/dist/workflows/dag/runner.js +26 -1
  184. package/dist/workflows/dag/types.js +6 -0
  185. package/docs/operations/README.md +1 -0
  186. package/docs/templates/frontend-design-contract.md +4 -4
  187. package/docs/templates/frontend-implementation-contract.schema.json +34 -2
  188. package/docs/templates/frontend-implementation-dag.json +14 -13
  189. package/package.json +6 -3
  190. package/skills/frontend-bounded-implement/SKILL.md +3 -3
  191. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  192. package/skills/frontend-contract/SKILL.md +2 -1
  193. package/skills/frontend-contract/references/contract-protocol.md +22 -4
  194. package/skills/frontend-design-review/SKILL.md +22 -16
  195. package/skills/frontend-design-review/references/review-checklist.md +7 -7
  196. package/skills/frontend-plan/SKILL.md +21 -2
  197. package/skills/frontend-plan/references/decision-contract.md +42 -23
  198. package/skills/frontend-review/SKILL.md +22 -29
  199. package/skills/frontend-review/references/review-findings.md +24 -43
  200. package/skills/frontend-scout/references/scout-evidence.md +4 -0
  201. package/dist/worker/console/static/assets/channel-DAS07MdS.js +0 -1
  202. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +0 -1
  203. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +0 -1
  204. package/dist/worker/console/static/assets/index-C0O48S_P.js +0 -449
  205. package/dist/worker/console/static/assets/index-CzKf4U8P.css +0 -1
  206. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +0 -1
  207. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +0 -8
@@ -16,6 +16,7 @@ import { randomUUID } from "node:crypto";
16
16
  import { access } from "node:fs/promises";
17
17
  import { parseAssistantContent } from "./assistant-content.js";
18
18
  import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
19
+ import { InvalidChatSessionModeError, SESSION_MODE_ACTION_DENIED_CODE, SESSION_MODE_ACTION_DENIED_MESSAGE, SESSION_MODE_IMMUTABLE_CODE, UnknownChatSessionModeError, isWorkflowSessionMode, parseCreateSessionMode, readStoredSessionMode, shouldAcceptOperatorSnapshot, } from "./session-mode.js";
19
20
  import { buildModelCallableToolSchemas } from "./tool-adapter.js";
20
21
  import { cleanChatTitle, acceptedOperationIdFromResult, isBlankChatSession } from "./session-store.js";
21
22
  import { hashChatTurnPayload, projectChatToolEventPayload, projectCompactSnapshot, } from "./chat-event-store.js";
@@ -44,11 +45,80 @@ import { buildGoalCompactionInstructions, buildGoalContinuationContext, } from "
44
45
  import { classifyGoalRoundAdmission, } from "./goal-round-driver.js";
45
46
  import { isTerminalOperationState } from "../../../infrastructure/console/operation-store.js";
46
47
  import { SddDataAlignmentError, } from "./sdd-data-alignment.js";
48
+ import { TerminalSessionRegistry } from "./terminal-sessions.js";
49
+ import { handleTerminalRequest } from "./terminal-routes.js";
50
+ import { handleScmRequest } from "./scm-routes.js";
51
+ import { handleBrowserRequest } from "./browser-routes.js";
47
52
  function projectBrowserChatSession(record) {
48
53
  const { telemetrySessionId, ...projected } = record;
49
54
  void telemetrySessionId;
50
55
  return projected;
51
56
  }
57
+ function reopenInitFromRecord(record) {
58
+ return {
59
+ sessionId: record.sessionId,
60
+ sessionFile: record.sessionFile,
61
+ ...(record.modelProvider && record.modelId
62
+ ? { model: { provider: record.modelProvider, modelId: record.modelId } }
63
+ : {}),
64
+ sessionMode: record.sessionMode,
65
+ };
66
+ }
67
+ async function readChatSessionMode(deps, sessionId) {
68
+ let record;
69
+ try {
70
+ record = await deps.store.get(sessionId);
71
+ }
72
+ catch (error) {
73
+ if (error instanceof UnknownChatSessionModeError) {
74
+ return {
75
+ ok: false,
76
+ status: 409,
77
+ code: error.code,
78
+ message: error.message,
79
+ };
80
+ }
81
+ throw error;
82
+ }
83
+ if (!record) {
84
+ return {
85
+ ok: false,
86
+ status: 404,
87
+ code: "NOT_FOUND",
88
+ message: `chat session not found: ${sessionId}`,
89
+ };
90
+ }
91
+ // Normalize: the store contract always fills sessionMode, but handler-level
92
+ // fakes / partial records may omit it — treat missing as workflow (the
93
+ // stock-record read rule), while a present-but-unknown value still fails
94
+ // closed in the store layer.
95
+ return {
96
+ ok: true,
97
+ record,
98
+ sessionMode: readStoredSessionMode(record.sessionMode),
99
+ };
100
+ }
101
+ async function requireWorkflowSession(res, deps, sessionId) {
102
+ const resolved = await readChatSessionMode(deps, sessionId);
103
+ if (!resolved.ok) {
104
+ sendJson(res, resolved.status, {
105
+ ok: false,
106
+ error: { code: resolved.code, message: resolved.message },
107
+ });
108
+ return undefined;
109
+ }
110
+ if (!isWorkflowSessionMode(resolved.sessionMode)) {
111
+ sendJson(res, 409, {
112
+ ok: false,
113
+ error: {
114
+ code: SESSION_MODE_ACTION_DENIED_CODE,
115
+ message: SESSION_MODE_ACTION_DENIED_MESSAGE,
116
+ },
117
+ });
118
+ return undefined;
119
+ }
120
+ return resolved.record;
121
+ }
52
122
  /**
53
123
  * Reattach the durable event-ring order to session-store rows. The session
54
124
  * store remains the content authority; the ring only supplies chronology.
@@ -565,6 +635,22 @@ workspaceId) {
565
635
  });
566
636
  return;
567
637
  }
638
+ let sessionMode;
639
+ try {
640
+ sessionMode = parseCreateSessionMode(body.sessionMode);
641
+ }
642
+ catch (error) {
643
+ sendJson(res, 400, {
644
+ ok: false,
645
+ error: {
646
+ code: error instanceof InvalidChatSessionModeError
647
+ ? error.code
648
+ : "INVALID_SESSION_MODE",
649
+ message: error instanceof Error ? error.message : String(error),
650
+ },
651
+ });
652
+ return;
653
+ }
568
654
  const provider = typeof body.modelProvider === "string" ? body.modelProvider : undefined;
569
655
  const modelId = typeof body.modelId === "string" ? body.modelId : undefined;
570
656
  const snapshotInput = body.snapshot;
@@ -586,6 +672,16 @@ workspaceId) {
586
672
  let createdSessionId;
587
673
  let persisted = false;
588
674
  try {
675
+ if (snapshotInput !== undefined && !shouldAcceptOperatorSnapshot(sessionMode)) {
676
+ sendJson(res, 409, {
677
+ ok: false,
678
+ error: {
679
+ code: SESSION_MODE_ACTION_DENIED_CODE,
680
+ message: "Operator snapshots cannot be imported into Pi sessions",
681
+ },
682
+ });
683
+ return;
684
+ }
589
685
  const snapshot = snapshotInput === undefined ? undefined : projectCompactSnapshot(snapshotInput);
590
686
  // A Console record promises that its Pi JSONL can be reopened after a
591
687
  // Console restart. Do not make that promise until Pi has finished
@@ -593,6 +689,7 @@ workspaceId) {
593
689
  // future JSONL path, leaving an unrecoverable orphan when init failed or
594
690
  // the process restarted in that window.
595
691
  const handle = await deps.runtime.createSession({
692
+ sessionMode,
596
693
  ...(provider && modelId ? { model: { provider, modelId } } : {}),
597
694
  ...(snapshot
598
695
  ? {
@@ -619,6 +716,7 @@ workspaceId) {
619
716
  sessionFile: handle.sessionFile,
620
717
  modelProvider: selection.model?.provider ?? handle.model?.provider,
621
718
  modelId: selection.model?.modelId ?? handle.model?.modelId,
719
+ sessionMode,
622
720
  // AC-R7-002: explicit parameter from the dispatcher — the runtime
623
721
  // store.create wrapper is gone; body workspaceId (if any) only acts as
624
722
  // a fallback for direct (non-dispatcher) handler tests.
@@ -689,6 +787,7 @@ export async function handleForkChatSession(req, res, deps, sourceSessionId) {
689
787
  try {
690
788
  const handle = await deps.runtime.forkSession({
691
789
  sourceSessionFile: source.sessionFile ?? "",
790
+ sessionMode: source.sessionMode,
692
791
  ...(source.modelProvider && source.modelId
693
792
  ? { model: { provider: source.modelProvider, modelId: source.modelId } }
694
793
  : {}),
@@ -755,10 +854,7 @@ export async function handleBranchChatSession(req, res, deps, sessionId) {
755
854
  }
756
855
  try {
757
856
  if (!deps.runtime.hasSession(sessionId)) {
758
- const reopened = await deps.runtime.reopenSession({
759
- sessionId,
760
- sessionFile: record.sessionFile,
761
- });
857
+ const reopened = await deps.runtime.reopenSession(reopenInitFromRecord(record));
762
858
  if (!reopened.ok) {
763
859
  const mapped = await mapFailedReopen(deps, sessionId, reopened);
764
860
  sendJson(res, mapped.status, {
@@ -1231,13 +1327,8 @@ export async function handleChatGoal(req, res, deps, sessionId) {
1231
1327
  });
1232
1328
  return;
1233
1329
  }
1234
- if (!(await deps.store.get(sessionId))) {
1235
- sendJson(res, 404, {
1236
- ok: false,
1237
- error: { code: "NOT_FOUND", message: `chat session not found: ${sessionId}` },
1238
- });
1330
+ if (!(await requireChatSession(res, deps, sessionId)))
1239
1331
  return;
1240
- }
1241
1332
  if (req.method === "GET") {
1242
1333
  sendJson(res, 200, { ok: true, goal: registry.get(sessionId) });
1243
1334
  return;
@@ -1451,6 +1542,7 @@ export async function listChatSessionSummaries(deps, ownerWorkspaceId) {
1451
1542
  ...(session.thinkingLevel !== undefined
1452
1543
  ? { thinkingLevel: session.thinkingLevel }
1453
1544
  : {}),
1545
+ sessionMode: session.sessionMode,
1454
1546
  activeTurnId: active?.turnId,
1455
1547
  turnPhase: active?.abortRequestedAt
1456
1548
  ? "stopping"
@@ -1474,6 +1566,16 @@ export async function handlePatchChatSession(req, res, deps, sessionId) {
1474
1566
  }
1475
1567
  try {
1476
1568
  const body = await readJsonBody(req);
1569
+ if ("sessionMode" in body) {
1570
+ sendJson(res, 409, {
1571
+ ok: false,
1572
+ error: {
1573
+ code: SESSION_MODE_IMMUTABLE_CODE,
1574
+ message: "sessionMode cannot be changed after create",
1575
+ },
1576
+ });
1577
+ return;
1578
+ }
1477
1579
  let record;
1478
1580
  if (typeof body.title === "string")
1479
1581
  record = await deps.store.rename(sessionId, body.title);
@@ -1532,13 +1634,7 @@ export async function handleReopenChatSession(req, res, deps, sessionId) {
1532
1634
  });
1533
1635
  return;
1534
1636
  }
1535
- const result = await deps.runtime.reopenSession({
1536
- sessionId,
1537
- sessionFile: record.sessionFile,
1538
- ...(record.modelProvider && record.modelId
1539
- ? { model: { provider: record.modelProvider, modelId: record.modelId } }
1540
- : {}),
1541
- });
1637
+ const result = await deps.runtime.reopenSession(reopenInitFromRecord(record));
1542
1638
  if (!result.ok) {
1543
1639
  const mapped = await mapFailedReopen(deps, sessionId, result);
1544
1640
  sendJson(res, mapped.status, {
@@ -1618,13 +1714,7 @@ export async function handleChatPrompt(req, res, deps, sessionId) {
1618
1714
  });
1619
1715
  return;
1620
1716
  }
1621
- const reopen = await deps.runtime.reopenSession({
1622
- sessionId,
1623
- sessionFile: record.sessionFile,
1624
- ...(record.modelProvider && record.modelId
1625
- ? { model: { provider: record.modelProvider, modelId: record.modelId } }
1626
- : {}),
1627
- });
1717
+ const reopen = await deps.runtime.reopenSession(reopenInitFromRecord(record));
1628
1718
  if (!reopen.ok) {
1629
1719
  const mapped = await mapFailedReopen(deps, sessionId, reopen);
1630
1720
  sendJson(res, mapped.status, {
@@ -1888,13 +1978,7 @@ export async function handleCreateChatTurn(req, res, deps, sessionId, trustedPay
1888
1978
  });
1889
1979
  return;
1890
1980
  }
1891
- const reopen = await deps.runtime.reopenSession({
1892
- sessionId,
1893
- sessionFile: record.sessionFile,
1894
- ...(record.modelProvider && record.modelId
1895
- ? { model: { provider: record.modelProvider, modelId: record.modelId } }
1896
- : {}),
1897
- });
1981
+ const reopen = await deps.runtime.reopenSession(reopenInitFromRecord(record));
1898
1982
  if (!reopen.ok) {
1899
1983
  const mapped = await mapFailedReopen(deps, sessionId, reopen);
1900
1984
  sendJson(res, mapped.status, {
@@ -2820,6 +2904,7 @@ export async function handleChatCommands(res, deps, sessionId) {
2820
2904
  const warning = typeof listed.warning === "string" && listed.warning.trim()
2821
2905
  ? listed.warning.trim().slice(0, 160)
2822
2906
  : undefined;
2907
+ const resolvedMode = await readChatSessionMode(deps, sessionId);
2823
2908
  res.statusCode = 200;
2824
2909
  res.setHeader("content-type", "application/json; charset=utf-8");
2825
2910
  res.end(JSON.stringify({
@@ -2827,18 +2912,25 @@ export async function handleChatCommands(res, deps, sessionId) {
2827
2912
  commands,
2828
2913
  capabilities: {
2829
2914
  sddBingo: Boolean(await deps.sddDataAlignment?.isEligible()),
2915
+ ...(resolvedMode.ok ? { sessionMode: resolvedMode.sessionMode } : {}),
2830
2916
  },
2831
2917
  ...(warning ? { warning } : {}),
2832
2918
  }));
2833
2919
  }
2834
2920
  catch (error) {
2835
2921
  // Whole-list / readiness failures stay fail-soft with empty commands + warning.
2922
+ // Keep the two capability shapes symmetric: the fallback mode mirrors the
2923
+ // client-side prop fallback in useComposer.
2924
+ const fallbackMode = await readChatSessionMode(deps, sessionId).catch(() => undefined);
2836
2925
  res.statusCode = 200;
2837
2926
  res.setHeader("content-type", "application/json; charset=utf-8");
2838
2927
  res.end(JSON.stringify({
2839
2928
  ok: true,
2840
2929
  commands: [],
2841
- capabilities: { sddBingo: false },
2930
+ capabilities: {
2931
+ sddBingo: false,
2932
+ ...(fallbackMode?.ok ? { sessionMode: fallbackMode.sessionMode } : {}),
2933
+ },
2842
2934
  warning: error instanceof Error ? error.message : "commands unavailable",
2843
2935
  }));
2844
2936
  }
@@ -3083,13 +3175,7 @@ export async function ensureActiveSession(deps, sessionId) {
3083
3175
  message: `chat session not found: ${sessionId}`,
3084
3176
  };
3085
3177
  }
3086
- const reopened = await deps.runtime.reopenSession({
3087
- sessionId,
3088
- sessionFile: record.sessionFile,
3089
- ...(record.modelProvider && record.modelId
3090
- ? { model: { provider: record.modelProvider, modelId: record.modelId } }
3091
- : {}),
3092
- });
3178
+ const reopened = await deps.runtime.reopenSession(reopenInitFromRecord(record));
3093
3179
  if (!reopened.ok) {
3094
3180
  return { ok: false, ...(await mapFailedReopen(deps, sessionId, reopened)) };
3095
3181
  }
@@ -3418,7 +3504,7 @@ async function handleStartInterview(req, res, deps, sessionId) {
3418
3504
  });
3419
3505
  return;
3420
3506
  }
3421
- if (!(await requireChatSession(res, deps, sessionId)))
3507
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3422
3508
  return;
3423
3509
  try {
3424
3510
  const body = await readJsonBody(req);
@@ -3460,7 +3546,7 @@ async function handleAnswerInterview(req, res, deps, sessionId, interviewSession
3460
3546
  });
3461
3547
  return;
3462
3548
  }
3463
- if (!(await requireChatSession(res, deps, sessionId)))
3549
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3464
3550
  return;
3465
3551
  try {
3466
3552
  const body = await readJsonBody(req);
@@ -3496,7 +3582,7 @@ async function handleAnswerInterview(req, res, deps, sessionId, interviewSession
3496
3582
  }
3497
3583
  }
3498
3584
  async function handleGetInterviewFact(res, deps, sessionId, interviewSessionId, fact) {
3499
- if (!(await requireChatSession(res, deps, sessionId)))
3585
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3500
3586
  return;
3501
3587
  const state = await new ChatInterviewAdapter(deps.appData, deps.events).get(interviewSessionId);
3502
3588
  if (!state || state.interview.operatorSessionId !== sessionId) {
@@ -3594,7 +3680,7 @@ async function handleContractApplyHumanGate(req, res, deps, sessionId, step) {
3594
3680
  });
3595
3681
  return;
3596
3682
  }
3597
- if (!(await requireChatSession(res, deps, sessionId)))
3683
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3598
3684
  return;
3599
3685
  const ctx = deps.runtime.actionContext;
3600
3686
  if (!ctx) {
@@ -3776,7 +3862,7 @@ async function handleDagHumanGate(req, res, deps, sessionId, step) {
3776
3862
  });
3777
3863
  return;
3778
3864
  }
3779
- if (!(await requireChatSession(res, deps, sessionId)))
3865
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3780
3866
  return;
3781
3867
  const ctx = deps.runtime.actionContext;
3782
3868
  if (!ctx) {
@@ -3885,7 +3971,7 @@ async function handleMutationHumanGate(req, res, deps, sessionId) {
3885
3971
  });
3886
3972
  return;
3887
3973
  }
3888
- if (!(await requireChatSession(res, deps, sessionId)))
3974
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3889
3975
  return;
3890
3976
  const ctx = deps.runtime.actionContext;
3891
3977
  if (!ctx) {
@@ -3974,7 +4060,7 @@ async function handleConfirmTaskKind(req, res, deps, sessionId, interviewSession
3974
4060
  });
3975
4061
  return;
3976
4062
  }
3977
- if (!(await requireChatSession(res, deps, sessionId)))
4063
+ if (!(await requireWorkflowSession(res, deps, sessionId)))
3978
4064
  return;
3979
4065
  try {
3980
4066
  const body = await readJsonBody(req);
@@ -4030,13 +4116,7 @@ export async function handleChatCompact(req, res, deps, sessionId) {
4030
4116
  return;
4031
4117
  }
4032
4118
  if (!deps.runtime.hasSession(sessionId)) {
4033
- const reopened = await deps.runtime.reopenSession({
4034
- sessionId,
4035
- sessionFile: record.sessionFile,
4036
- ...(record.modelProvider && record.modelId
4037
- ? { model: { provider: record.modelProvider, modelId: record.modelId } }
4038
- : {}),
4039
- });
4119
+ const reopened = await deps.runtime.reopenSession(reopenInitFromRecord(record));
4040
4120
  if (!reopened.ok) {
4041
4121
  const mapped = await mapFailedReopen(deps, sessionId, reopened);
4042
4122
  sendJson(res, mapped.status, {
@@ -4060,7 +4140,9 @@ export async function handleChatCompact(req, res, deps, sessionId) {
4060
4140
  const rawInstructions = typeof body.instructions === "string" ? body.instructions : undefined;
4061
4141
  const currentTodos = projectCurrentTodos(deps.events.snapshot(sessionId));
4062
4142
  const todoInstructions = buildTodoCompactionInstructions(rawInstructions, currentTodos);
4063
- const customInstructions = buildGoalCompactionInstructions(todoInstructions, deps.goalRegistry?.get(sessionId) ?? null);
4143
+ const customInstructions = isWorkflowSessionMode(record.sessionMode)
4144
+ ? buildGoalCompactionInstructions(todoInstructions, deps.goalRegistry?.get(sessionId) ?? null)
4145
+ : todoInstructions;
4064
4146
  const turnId = deps.events.latestTurnId(sessionId) ?? `${sessionId}:compact`;
4065
4147
  const compactionId = `${sessionId}:compaction:${randomUUID()}`;
4066
4148
  deps.events.append(sessionId, turnId, {
@@ -4075,27 +4157,42 @@ export async function handleChatCompact(req, res, deps, sessionId) {
4075
4157
  });
4076
4158
  try {
4077
4159
  const snapshot = await deps.runtime.compact(sessionId, customInstructions);
4160
+ const completedAt = new Date().toISOString();
4161
+ const completedData = {
4162
+ schemaVersion: 1,
4163
+ compactionId,
4164
+ reason: "manual",
4165
+ status: "completed",
4166
+ summary: snapshot.summary,
4167
+ ...(snapshot.firstKeptEntryId
4168
+ ? { firstKeptEntryId: snapshot.firstKeptEntryId }
4169
+ : {}),
4170
+ ...(snapshot.tokensBefore === undefined
4171
+ ? {}
4172
+ : { tokensBefore: snapshot.tokensBefore }),
4173
+ ...(snapshot.estimatedTokensAfter === undefined
4174
+ ? {}
4175
+ : { estimatedTokensAfter: snapshot.estimatedTokensAfter }),
4176
+ willRetry: false,
4177
+ };
4078
4178
  const event = deps.events.append(sessionId, turnId, {
4079
4179
  kind: "compaction_end",
4080
- data: {
4081
- schemaVersion: 1,
4082
- compactionId,
4083
- reason: "manual",
4084
- status: "completed",
4085
- summary: snapshot.summary,
4086
- ...(snapshot.firstKeptEntryId
4087
- ? { firstKeptEntryId: snapshot.firstKeptEntryId }
4088
- : {}),
4089
- ...(snapshot.tokensBefore === undefined
4090
- ? {}
4091
- : { tokensBefore: snapshot.tokensBefore }),
4092
- ...(snapshot.estimatedTokensAfter === undefined
4093
- ? {}
4094
- : { estimatedTokensAfter: snapshot.estimatedTokensAfter }),
4095
- willRetry: false,
4180
+ at: completedAt,
4181
+ data: completedData,
4182
+ });
4183
+ sendJson(res, 200, {
4184
+ ok: true,
4185
+ snapshot,
4186
+ eventId: event.eventId,
4187
+ compaction: {
4188
+ eventId: event.eventId,
4189
+ kind: "compaction_end",
4190
+ at: completedAt,
4191
+ seq: event.seq,
4192
+ turnId,
4193
+ data: completedData,
4096
4194
  },
4097
4195
  });
4098
- sendJson(res, 200, { ok: true, snapshot, eventId: event.eventId });
4099
4196
  }
4100
4197
  catch (error) {
4101
4198
  const classified = classifyChatCompactionFailure(error);
@@ -4146,6 +4243,46 @@ export async function handleChatMessages(res, deps, sessionId, url) {
4146
4243
  export async function handleChatRequest(req, res, deps, pathname, workspaceScope) {
4147
4244
  if (!deps)
4148
4245
  return false;
4246
+ try {
4247
+ return await dispatchChatRequest(req, res, deps, pathname, workspaceScope);
4248
+ }
4249
+ catch (error) {
4250
+ // Store-level fail-closed for corrupt sessionMode: bounded 409 instead of
4251
+ // a generic 500, for any handler that reads the record without its own
4252
+ // UnknownChatSessionModeError mapping (readChatSessionMode covers the rest).
4253
+ if (error instanceof UnknownChatSessionModeError) {
4254
+ sendJson(res, 409, {
4255
+ ok: false,
4256
+ error: { code: error.code, message: error.message },
4257
+ });
4258
+ return true;
4259
+ }
4260
+ throw error;
4261
+ }
4262
+ }
4263
+ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
4264
+ // Terminal management API (model + Console UI shared PTY registry).
4265
+ if (await handleTerminalRequest(req, res, {
4266
+ terminals: deps.terminals ?? new TerminalSessionRegistry(),
4267
+ gateMutation: (r) => gateMutation(r, deps),
4268
+ // Terminals open in the workspace repo root, not the Console process cwd.
4269
+ defaultCwd: deps.appData.repoRoot,
4270
+ }, pathname)) {
4271
+ return true;
4272
+ }
4273
+ // Source control API (model + Console UI shared workspace ScmService).
4274
+ if (deps.scm) {
4275
+ if (await handleScmRequest(req, res, {
4276
+ scm: deps.scm,
4277
+ gateMutation: (r) => gateMutation(r, deps),
4278
+ }, pathname)) {
4279
+ return true;
4280
+ }
4281
+ }
4282
+ // Browser probe API (read-only; explains iframe refusals to the panel).
4283
+ if (await handleBrowserRequest(req, res, {}, pathname)) {
4284
+ return true;
4285
+ }
4149
4286
  if (!pathname.startsWith("/api/operator/v1/chat/") &&
4150
4287
  !pathname.startsWith("/api/operator/v1/pi/"))
4151
4288
  return false;