@tea-agent/loop-agent 0.43.0-next.3 → 0.43.0-next.5

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 (109) hide show
  1. package/CHANGELOG.md +81 -9
  2. package/dist/application/task-lifecycle/advance.js +17 -4
  3. package/dist/application/task-lifecycle/observe.js +43 -29
  4. package/dist/application/task-lifecycle/plan-transitions.js +5 -4
  5. package/dist/build-stamp.json +3 -3
  6. package/dist/executors/dag-pi-executor.js +244 -33
  7. package/dist/executors/pi-executor.js +2 -0
  8. package/dist/executors/pi-sdk-executor.js +75 -39
  9. package/dist/task/source-prepare/source-fidelity-pi.js +2 -2
  10. package/dist/task/source-references.js +48 -23
  11. package/dist/worker/console/chat/assistant-content.js +23 -2
  12. package/dist/worker/console/chat/explore-tools.js +13 -0
  13. package/dist/worker/console/chat/pi-runtime.js +131 -95
  14. package/dist/worker/console/chat/routes.js +175 -54
  15. package/dist/worker/console/chat/scm-routes.js +193 -0
  16. package/dist/worker/console/chat/scm-service.js +253 -0
  17. package/dist/worker/console/chat/scm-tools.js +111 -0
  18. package/dist/worker/console/chat/sdd-data-alignment.js +65 -9
  19. package/dist/worker/console/chat/session-catalog.js +1 -0
  20. package/dist/worker/console/chat/session-mode-view.js +48 -0
  21. package/dist/worker/console/chat/session-mode.js +218 -0
  22. package/dist/worker/console/chat/session-store.js +22 -6
  23. package/dist/worker/console/chat/shortcuts.js +6 -0
  24. package/dist/worker/console/chat/terminal-routes.js +216 -0
  25. package/dist/worker/console/chat/terminal-sessions.js +284 -0
  26. package/dist/worker/console/chat/terminal-tools.js +199 -0
  27. package/dist/worker/console/chat/tool-preview.js +11 -0
  28. package/dist/worker/console/chat/tools.js +2 -0
  29. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-hUmM_GRf.js → abnfDiagram-N423BO3Z-BuyrlONK.js} +1 -1
  30. package/dist/worker/console/static/assets/{arc-Bt-xuieW.js → arc-BvCl_wpi.js} +1 -1
  31. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DTqkL4DJ.js → architectureDiagram-T3A2C74G-BkBpBaRq.js} +1 -1
  32. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Cy0MNnzV.js → blockDiagram-VBNYF7ZC-yve4Mcb_.js} +1 -1
  33. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DDdlT0Pd.js → c4Diagram-5PPSVZJV-9yLGboBx.js} +1 -1
  34. package/dist/worker/console/static/assets/channel-BRahIhjw.js +1 -0
  35. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-3JawwNZ6.js → chunk-2GRJ4B5K-BKP69b8r.js} +1 -1
  36. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DoTp7Cri.js → chunk-2Q5K7J3B-NWFRnXju.js} +1 -1
  37. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CcirhZaS.js → chunk-5RXB4S5H-BiweqLEc.js} +1 -1
  38. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-VrG21gnP.js → chunk-5VM5RSS4-CP9GHmRY.js} +1 -1
  39. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DDOgIS70.js → chunk-6Q2QTUOP-BiGDkBtj.js} +1 -1
  40. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CBjmgvJ_.js → chunk-GF5L2VYU-DkJ4ZjQS.js} +1 -1
  41. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CCQSht8M.js → chunk-JWPE2WC7-B5jjn7zq.js} +1 -1
  42. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BpGMkUUV.js → chunk-KBJHAD2P-C6KoK9O0.js} +1 -1
  43. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DNDT41QL.js → chunk-RYQCIY6F-BvNIfyn-.js} +1 -1
  44. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-euXnDFbn.js → chunk-XXDRQBXY-BZ33iX4W.js} +1 -1
  45. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-GOC80mhq.js +1 -0
  46. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-GOC80mhq.js +1 -0
  47. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BJ6NAeql.js → cose-bilkent-JH36ORCC-CP8eIuZd.js} +1 -1
  48. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-F5JKIvIq.js → cynefin-VYW2F7L2-DZECjpOe.js} +1 -1
  49. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Cwmkl_Gi.js → cynefinDiagram-MW4NZA55-CvVJoFqf.js} +1 -1
  50. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BmCZ8815.js → dagre-VZM6K2ZE-D_JTqdAn.js} +1 -1
  51. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-zWA3hUnX.js → diagram-7IWD3JNH-Db7AQ0z0.js} +1 -1
  52. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BzA-888F.js → diagram-B4RE2ZJO-AZPVo5CZ.js} +1 -1
  53. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-tPNheoch.js → diagram-LBJQPF4R-BgXqI3E6.js} +1 -1
  54. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cdi7Fyqp.js → diagram-Q27KOJAE-BYjFn3Ba.js} +1 -1
  55. package/dist/worker/console/static/assets/{diagram-UB23O5K3-DFK9VBf1.js → diagram-UB23O5K3-B4rJgGGO.js} +1 -1
  56. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-xCUr1kpg.js → ebnfDiagram-BXEA7PRR-C6XWZhpw.js} +1 -1
  57. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BANQHY_A.js → erDiagram-JOGREHBK-1YdQRIQW.js} +1 -1
  58. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-VHJYEMkP.js → flowDiagram-UKHOOZJN-l3a0AwN0.js} +1 -1
  59. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DiyZQ0C6.js → ganttDiagram-PKOTCBZU-CQL96F2Z.js} +1 -1
  60. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CZtIeBBa.js → gitGraphDiagram-DS77QQ5N-BxCvafRb.js} +1 -1
  61. package/dist/worker/console/static/assets/index-4wCGsXuU.js +465 -0
  62. package/dist/worker/console/static/assets/index-CiF9WBoR.css +1 -0
  63. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BuSspC0X.js → infoDiagram-6WML65LV-BUipIeDO.js} +1 -1
  64. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DRJZyPFy.js → ishikawaDiagram-WSZJBQD7-CRfomVMX.js} +1 -1
  65. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DIfXIVuE.js → journeyDiagram-NVQOT4AX-_UFvyviX.js} +1 -1
  66. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CT10_7Lt.js → kanban-definition-27J2QSJJ-CKIRoqKv.js} +1 -1
  67. package/dist/worker/console/static/assets/{linear-ufemj74H.js → linear-CjttJDCM.js} +1 -1
  68. package/dist/worker/console/static/assets/{mermaid.core-DEQe8-_m.js → mermaid.core-DPvHX155.js} +5 -5
  69. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BRLUrbrC.js → mindmap-definition-FAOFIHXS-Qmj-qwBe.js} +1 -1
  70. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BgibKtiF.js → pegDiagram-VL7TDLO6-Auw6DRgq.js} +1 -1
  71. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-D3eyfdV9.js → pieDiagram-7S7Q4E2Y-CqMrfD4c.js} +1 -1
  72. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DXeTk5Px.js → quadrantDiagram-CIZ2JOQS-BAddeggH.js} +1 -1
  73. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BgXdFEe7.js → railroadDiagram-AXF67PYL-DG1JuOu2.js} +1 -1
  74. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CseC2A7R.js → requirementDiagram-LRYGKXZP-DLYGMKlC.js} +1 -1
  75. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-D80PFzfJ.js → sankeyDiagram-W5VNT64P-g68SbLM9.js} +1 -1
  76. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BWzHW7LS.js → sequenceDiagram-SI44F4Z6-CmszLKv8.js} +1 -1
  77. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-xWQYTLAn.js → sizeCapture-X5ZJPWSS-D2tEKUCj.js} +1 -1
  78. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-CLxtpZeL.js → stateDiagram-OKZ733FA-Syc5E_Jf.js} +1 -1
  79. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CwWwuPI-.js +1 -0
  80. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DwugtmWV.js → swimlanes-SLNWSIFB-Ibn0IRCa.js} +2 -2
  81. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-A7fqemr6.js +8 -0
  82. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DHEOwHIe.js → timeline-definition-Z64GVDOM-2ZW9W3iS.js} +1 -1
  83. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-pmbZKlBK.js → vennDiagram-T6HMQDX7-v50HsKUh.js} +1 -1
  84. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CivHoM5q.js → wardleyDiagram-T6FBY63Y-DqCXEoDH.js} +1 -1
  85. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BfHIE7o6.js → xychartDiagram-ELKLHX3M-vIP8Vo9d.js} +1 -1
  86. package/dist/worker/console/static/index.html +2 -2
  87. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +122 -10
  88. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +27 -15
  89. package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
  90. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
  91. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
  92. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +20 -4
  93. package/dist/worker/console/static-src/operator-chat/useChatStream.js +18 -2
  94. package/dist/worker/console/static-src/operator-chat/useChatThread.js +39 -4
  95. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -7
  96. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
  97. package/dist/worker/console/workspace-context.js +15 -1
  98. package/dist/workflows/dag/frontend-capacity.js +9 -0
  99. package/dist/workflows/dag/frontend-design-policy.js +2 -1
  100. package/dist/workflows/dag/frontend-shape-facts.js +12 -2
  101. package/package.json +6 -3
  102. package/skills/frontend-plan/SKILL.md +7 -0
  103. package/dist/worker/console/static/assets/channel-BpzRnZmU.js +0 -1
  104. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlMVqCc.js +0 -1
  105. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlMVqCc.js +0 -1
  106. package/dist/worker/console/static/assets/index-BlN6ytLr.css +0 -1
  107. package/dist/worker/console/static/assets/index-CA-jfWlE.js +0 -449
  108. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CiVoq4G-.js +0 -1
  109. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BkdgdpfR.js +0 -8
@@ -32,7 +32,8 @@ import { extractAssistantContentBlocks, extractAssistantText, isAssistantSdkMess
32
32
  import { parseProviderError, } from "./provider-error.js";
33
33
  import { projectCompactSnapshot, } from "./chat-event-store.js";
34
34
  import { extractUsageSample } from "./usage.js";
35
- import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS, authorizeOperatorChatTool, assertNoWriteToolInList, } from "./tools.js";
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";
36
37
  import { buildAgentCustomTool, ExploreOrchestrator, } from "./subagents/index.js";
37
38
  import { createOperatorChatResourceLoader, } from "./resource-loader.js";
38
39
  import { applySkillAutoInvocationPreferences, isAutoInvocationEnabled, loadResourcePreferences, skillResourceId, } from "./resource-preferences-store.js";
@@ -43,6 +44,8 @@ import { resolveDefaultChatModel, resolveLowChatModel, } from "./model-resolver.
43
44
  import { filterActiveInterviewTools } from "../interview/tools.js";
44
45
  import { RUNTIME_CONTEXT_TEXT_MAX, redactRuntimeText, } from "./runtime-context.js";
45
46
  import { buildTodoContinuationContext, } from "./todos.js";
47
+ import { buildPersistentTerminalTools } from "./terminal-tools.js";
48
+ import { buildScmTools } from "./scm-tools.js";
46
49
  import { buildGoalContinuationContext, } from "./goals.js";
47
50
  import { injectWizardLocalHeaders, } from "./sdd-data-alignment.js";
48
51
  /**
@@ -75,15 +78,6 @@ export const OPERATOR_CHAT_SYSTEM_PROMPT_BASE = [
75
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.",
76
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.",
77
80
  ].join("\n");
78
- /** Compose the inspectable system prompt actually injected into Operator Chat. */
79
- export function composeOperatorChatSystemPrompt(input) {
80
- return [
81
- OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
82
- ...(input.systemPromptSuffix?.trim()
83
- ? [input.systemPromptSuffix.trim()]
84
- : []),
85
- ].join("\n\n");
86
- }
87
81
  /**
88
82
  * Built-in SDK tool names registered for every Chat session (ADR 0011).
89
83
  * Full Pi builtins: read/write/edit/bash/grep/find/ls. safe-* custom tools
@@ -112,27 +106,8 @@ export const OPERATOR_CHAT_SAFE_EXPLORE_TOOL_IDS = Object.freeze([
112
106
  "git-diff",
113
107
  ]);
114
108
  export function OPERATOR_CHAT_ACTIVE_TOOL_NAMES() {
115
- return [
116
- ...OPERATOR_CHAT_ALLOWED_TOOLS,
117
- ...OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS,
118
- ...OPERATOR_CHAT_SAFE_EXPLORE_TOOL_IDS,
119
- ...OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS,
120
- ];
109
+ return chatActiveToolBaseline("workflow");
121
110
  }
122
- /**
123
- * Non-Pi write / coding channels that must never be activated in Operator
124
- * Chat even when a user/project extension registers them (ADR 0011 / 0012).
125
- * Mirrors the NON_PI_WRITE_CHANNELS guard in tools.ts for the Gate 2 pin.
126
- */
127
- const OPERATOR_CHAT_EXTENSION_DENIED_TOOL_IDS = new Set([
128
- "apply_patch",
129
- "apply-patch",
130
- "full-tools",
131
- "full_tools",
132
- "coding-chat",
133
- "coding_chat",
134
- "shell",
135
- ].map((t) => t.toLowerCase()));
136
111
  /**
137
112
  * Gate 2 active-tool set for a live session (ADR 0012): the operator-chat
138
113
  * baseline (operator actions + Pi builtins + safe explore) PLUS extension
@@ -143,24 +118,8 @@ const OPERATOR_CHAT_EXTENSION_DENIED_TOOL_IDS = new Set([
143
118
  * coding-chat). Unknown/denied names are never activated; Gate 3 still
144
119
  * re-authorizes every tool call per invocation.
145
120
  */
146
- export function computeOperatorChatActiveToolNames(session) {
147
- const baseline = OPERATOR_CHAT_ACTIVE_TOOL_NAMES();
148
- if (!session?.getAllTools)
149
- return baseline;
150
- const baselineLower = new Set(baseline.map((t) => t.toLowerCase()));
151
- const extras = [];
152
- for (const tool of session.getAllTools()) {
153
- const name = tool?.name;
154
- if (!name || typeof name !== "string")
155
- continue;
156
- const lower = name.toLowerCase();
157
- if (baselineLower.has(lower))
158
- continue;
159
- if (OPERATOR_CHAT_EXTENSION_DENIED_TOOL_IDS.has(lower))
160
- continue;
161
- extras.push(name);
162
- }
163
- return extras.length > 0 ? [...baseline, ...extras] : baseline;
121
+ export function computeOperatorChatActiveToolNames(session, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
122
+ return computeChatActiveToolNames(session, sessionMode);
164
123
  }
165
124
  const OPERATOR_CHAT_SESSION_EXCLUDE_TOOLS = Object.freeze([
166
125
  "apply_patch",
@@ -216,8 +175,8 @@ async function bindSessionExtensions(session) {
216
175
  return;
217
176
  await session.bindExtensions(createConsoleExtensionBindings());
218
177
  }
219
- function pinOperatorChatTools(session) {
220
- session.setActiveToolsByName(computeOperatorChatActiveToolNames(session));
178
+ function pinOperatorChatTools(session, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
179
+ session.setActiveToolsByName(computeChatActiveToolNames(session, sessionMode));
221
180
  }
222
181
  /**
223
182
  * Pi has no hook result that merely appends instructions to its built-in
@@ -369,7 +328,7 @@ async function buildCustomOperatorTools(options) {
369
328
  promptSnippet: schema.description,
370
329
  parameters: parameters,
371
330
  async execute(toolCallId, params) {
372
- const decision = authorizeOperatorChatTool(schema.toolId);
331
+ const decision = authorizeChatSessionTool(schema.toolId, options.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE);
373
332
  if (!decision.ok) {
374
333
  return {
375
334
  content: [
@@ -386,7 +345,7 @@ async function buildCustomOperatorTools(options) {
386
345
  }
387
346
  // Defer require to avoid circular import at module load.
388
347
  const { dispatchChatToolCall } = await import("./session-store.js");
389
- const result = await dispatchChatToolCall(ctx, decision.toolId, params, toolCallId);
348
+ const result = await dispatchChatToolCall(ctx, decision.toolId, params, toolCallId, options.sessionMode);
390
349
  const payload = result.ok
391
350
  ? result.result
392
351
  : { error: result.message, code: result.errorCode };
@@ -444,11 +403,14 @@ async function buildTodoWriteTool(options) {
444
403
  import("typebox"),
445
404
  import("@earendil-works/pi-coding-agent"),
446
405
  ]);
406
+ const description = options.sessionMode === "pi"
407
+ ? PI_TODO_WRITE_DESCRIPTION
408
+ : TODO_WRITE_DESCRIPTION;
447
409
  return defineTool({
448
410
  name: "todo_write",
449
411
  label: "Update todo list",
450
- description: TODO_WRITE_DESCRIPTION,
451
- promptSnippet: TODO_WRITE_DESCRIPTION,
412
+ description,
413
+ promptSnippet: description,
452
414
  parameters: Type.Object({
453
415
  todos: Type.Array(Type.Object({
454
416
  content: Type.String({ description: "Short imperative task description." }),
@@ -1021,12 +983,14 @@ export class ConsolePiRuntime {
1021
983
  sessionInit = new Map();
1022
984
  /** Resolved model binding after materialization (create may return before this is known). */
1023
985
  sessionModels = new Map();
986
+ /** Durable session mode; record is the source of truth, this is the live handle cache. */
987
+ sessionModes = new Map();
1024
988
  /** SessionIds disposed while materialization was still in flight. */
1025
989
  disposedSessions = new Set();
1026
990
  compactionControllers = new Map();
1027
991
  siftStates = new Map();
1028
992
  /** Session-less tool definition caches (no AgentSession state). */
1029
- operatorCustomToolsPromise;
993
+ operatorCustomToolsByMode = new Map();
1030
994
  exploreCustomToolsPromise;
1031
995
  exploreOrchestrator;
1032
996
  bindings;
@@ -1054,6 +1018,9 @@ export class ConsolePiRuntime {
1054
1018
  },
1055
1019
  });
1056
1020
  }
1021
+ async syncAimaxAuthToken() {
1022
+ await this.options.sddDataAlignment?.auth?.getToken();
1023
+ }
1057
1024
  /** Absolute path for repo-scoped resource preferences (may be undefined in unit tests). */
1058
1025
  get resourcePreferencesPath() {
1059
1026
  return this.options.resourcePreferencesPath;
@@ -1082,10 +1049,20 @@ export class ConsolePiRuntime {
1082
1049
  * after a PATCH sees the latest resource-preferences.json (not create-time
1083
1050
  * closure state).
1084
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
+ }
1085
1061
  async buildResourceLoaderOptions(base = {}, sessionId) {
1086
1062
  const inheritedFactories = Array.isArray(base.extensionFactories)
1087
1063
  ? base.extensionFactories
1088
1064
  : [];
1065
+ const sessionMode = this.sessionModeOf(sessionId);
1089
1066
  const todoContextExtension = sessionId && this.options.todoRegistry
1090
1067
  ? createTodoCompactionContextExtension({
1091
1068
  sessionId,
@@ -1095,7 +1072,9 @@ export class ConsolePiRuntime {
1095
1072
  const pressureExtension = sessionId
1096
1073
  ? this.createContextPressureInlineExtension(sessionId)
1097
1074
  : undefined;
1098
- const goalContextExtension = sessionId && this.options.goalRegistry
1075
+ const goalContextExtension = sessionId &&
1076
+ this.options.goalRegistry &&
1077
+ shouldInjectGoalContinuation(sessionMode)
1099
1078
  ? createGoalCompactionContextExtension({
1100
1079
  sessionId,
1101
1080
  registry: this.options.goalRegistry,
@@ -1341,8 +1320,10 @@ export class ConsolePiRuntime {
1341
1320
  * `initializing: true`; dependent routes wait on the same promise.
1342
1321
  */
1343
1322
  async createSession(init) {
1323
+ await this.syncAimaxAuthToken();
1344
1324
  const sessionId = `operator-chat-${randomBytes(12).toString("hex")}`;
1345
1325
  this.disposedSessions.delete(sessionId);
1326
+ const sessionMode = this.rememberSessionMode(sessionId, init?.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE);
1346
1327
  const sessionManager = await this.bindings.createSessionManager({
1347
1328
  cwd: this.options.cwd,
1348
1329
  sessionDir: this.options.sessionDir,
@@ -1368,6 +1349,7 @@ export class ConsolePiRuntime {
1368
1349
  sessionManager,
1369
1350
  model: init?.model,
1370
1351
  systemPromptSuffix: init?.systemPromptSuffix,
1352
+ sessionMode,
1371
1353
  // Create path must resolve default model from the SAME services object
1372
1354
  // used for formal materialization (no throwaway createServices).
1373
1355
  resolveDefaultModel: !init?.model,
@@ -1377,7 +1359,8 @@ export class ConsolePiRuntime {
1377
1359
  sessionFile,
1378
1360
  createdAt: new Date().toISOString(),
1379
1361
  model: init?.model,
1380
- activeTools: OPERATOR_CHAT_ACTIVE_TOOL_NAMES(),
1362
+ activeTools: chatActiveToolBaseline(sessionMode),
1363
+ sessionMode,
1381
1364
  initializing: true,
1382
1365
  };
1383
1366
  }
@@ -1438,14 +1421,17 @@ export class ConsolePiRuntime {
1438
1421
  async materializeSession(sessionId, init) {
1439
1422
  if (this.disposedSessions.has(sessionId))
1440
1423
  return;
1424
+ await this.syncAimaxAuthToken();
1425
+ const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? this.sessionModeOf(sessionId));
1441
1426
  const agentDir = await safeGetAgentDir(this.bindings);
1442
1427
  // Capture before services parse models.json. A concurrent edit can therefore
1443
1428
  // cause at most one harmless extra refresh, never a missed next-turn refresh.
1444
1429
  const modelConfigFile = await captureModelConfigFile(agentDir);
1445
- const appendSystemPrompt = [
1446
- OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1447
- ...(init.systemPromptSuffix ? [init.systemPromptSuffix] : []),
1448
- ];
1430
+ const appendSystemPrompt = composeChatSystemPromptParts({
1431
+ sessionMode,
1432
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1433
+ systemPromptSuffix: init.systemPromptSuffix,
1434
+ });
1449
1435
  // Single services construction for formal materialization (+ optional
1450
1436
  // default-model resolve from the same modelRuntime). Repo-scoped skill
1451
1437
  // auto-invocation prefs inject skillsOverride without touching SKILL.md.
@@ -1496,7 +1482,7 @@ export class ConsolePiRuntime {
1496
1482
  return;
1497
1483
  }
1498
1484
  await bindSessionExtensions(session);
1499
- pinOperatorChatTools(session);
1485
+ pinOperatorChatTools(session, sessionMode);
1500
1486
  // Register then re-check dispose race: a concurrent dispose must win.
1501
1487
  this.sessions.set(sessionId, session);
1502
1488
  this.sessionManagers.set(sessionId, init.sessionManager);
@@ -1524,6 +1510,7 @@ export class ConsolePiRuntime {
1524
1510
  this.modelRuntimes.delete(sessionId);
1525
1511
  this.modelConfigFiles.delete(sessionId);
1526
1512
  this.sessionModels.delete(sessionId);
1513
+ this.sessionModes.delete(sessionId);
1527
1514
  this.serviceScopes.delete(sessionId);
1528
1515
  this.revisions.delete(sessionId);
1529
1516
  this.thinkingSelections.delete(sessionId);
@@ -1531,8 +1518,12 @@ export class ConsolePiRuntime {
1531
1518
  }
1532
1519
  /** Cached session-less custom tool definitions (operator + explore). */
1533
1520
  async loadCustomTools(sessionId) {
1521
+ const sessionMode = this.sessionModeOf(sessionId);
1522
+ const loadWorkflowTools = shouldLoadWorkflowBusinessTools(sessionMode);
1534
1523
  const [operatorCustomTools, exploreCustomTools] = await Promise.all([
1535
- this.loadOperatorCustomTools(),
1524
+ loadWorkflowTools
1525
+ ? this.loadOperatorCustomTools(sessionMode)
1526
+ : Promise.resolve([]),
1536
1527
  this.loadExploreCustomTools(),
1537
1528
  ]);
1538
1529
  const questionTool = this.options.questionRegistry
@@ -1542,11 +1533,31 @@ export class ConsolePiRuntime {
1542
1533
  })
1543
1534
  : undefined;
1544
1535
  const todoTool = this.options.todoRegistry
1545
- ? await buildTodoWriteTool({ sessionId, registry: this.options.todoRegistry })
1536
+ ? await buildTodoWriteTool({
1537
+ sessionId,
1538
+ registry: this.options.todoRegistry,
1539
+ sessionMode,
1540
+ })
1546
1541
  : undefined;
1547
- const goalTools = this.options.goalRegistry
1542
+ const goalTools = loadWorkflowTools && this.options.goalRegistry
1548
1543
  ? await buildGoalTools({ sessionId, registry: this.options.goalRegistry })
1549
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
+ : [];
1550
1561
  const agentTool = await buildAgentCustomTool({
1551
1562
  parentSessionId: sessionId,
1552
1563
  orchestrator: this.exploreOrchestrator,
@@ -1571,23 +1582,28 @@ export class ConsolePiRuntime {
1571
1582
  ...(questionTool ? [questionTool] : []),
1572
1583
  ...(todoTool ? [todoTool] : []),
1573
1584
  ...goalTools,
1585
+ ...terminalTools,
1586
+ ...scmTools,
1574
1587
  ...(agentTool ? [agentTool] : []),
1575
1588
  ];
1576
1589
  }
1577
- loadOperatorCustomTools() {
1590
+ loadOperatorCustomTools(sessionMode) {
1578
1591
  if (!this.options.actionContext)
1579
1592
  return Promise.resolve([]);
1580
- if (!this.operatorCustomToolsPromise) {
1581
- this.operatorCustomToolsPromise = buildCustomOperatorTools({
1582
- actionContext: this.options.actionContext,
1583
- }).catch((error) => {
1584
- process.stderr.write(`[console] chat custom-tools build failed: ${error instanceof Error ? error.message : String(error)}\n`);
1585
- // Allow a later create/reopen to retry after a transient failure.
1586
- this.operatorCustomToolsPromise = undefined;
1587
- return [];
1588
- });
1589
- }
1590
- 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;
1591
1607
  }
1592
1608
  loadExploreCustomTools() {
1593
1609
  if (!this.exploreCustomToolsPromise) {
@@ -1605,6 +1621,7 @@ export class ConsolePiRuntime {
1605
1621
  if (!this.bindings.forkSessionManager)
1606
1622
  throw new Error("SESSION_FORK_UNSUPPORTED");
1607
1623
  const sessionId = `operator-chat-${randomBytes(12).toString("hex")}`;
1624
+ const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE);
1608
1625
  const sessionManager = await this.bindings.forkSessionManager({
1609
1626
  sourcePath: init.sourceSessionFile,
1610
1627
  targetCwd: this.options.cwd,
@@ -1615,8 +1632,8 @@ export class ConsolePiRuntime {
1615
1632
  sessionId,
1616
1633
  sessionManager,
1617
1634
  model: init.model,
1635
+ sessionMode,
1618
1636
  });
1619
- built.session.setActiveToolsByName(computeOperatorChatActiveToolNames(built.session));
1620
1637
  this.sessions.set(sessionId, built.session);
1621
1638
  this.sessionManagers.set(sessionId, sessionManager);
1622
1639
  this.modelRuntimes.set(sessionId, built.modelRuntime);
@@ -1632,6 +1649,7 @@ export class ConsolePiRuntime {
1632
1649
  createdAt: new Date().toISOString(),
1633
1650
  model: init.model,
1634
1651
  activeTools: built.session.getActiveToolNames(),
1652
+ sessionMode,
1635
1653
  };
1636
1654
  }
1637
1655
  async branchSession(input) {
@@ -1810,15 +1828,22 @@ export class ConsolePiRuntime {
1810
1828
  async getRuntimeSystemPrompt(sessionId) {
1811
1829
  await this.ensureSessionReady(sessionId).catch(() => undefined);
1812
1830
  const session = this.sessions.get(sessionId);
1813
- if (!session)
1814
- return composeOperatorChatSystemPrompt({});
1831
+ if (!session) {
1832
+ return composeChatSystemPrompt({
1833
+ sessionMode: this.sessionModeOf(sessionId),
1834
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1835
+ });
1836
+ }
1815
1837
  const fromState = session.state?.systemPrompt;
1816
1838
  const fromGetter = session.systemPrompt;
1817
1839
  const loader = this.serviceScopes.get(sessionId)?.resourceLoader;
1818
1840
  return (fromState ||
1819
1841
  fromGetter ||
1820
1842
  loader?.getSystemPrompt?.() ||
1821
- composeOperatorChatSystemPrompt({}));
1843
+ composeChatSystemPrompt({
1844
+ sessionMode: this.sessionModeOf(sessionId),
1845
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1846
+ }));
1822
1847
  }
1823
1848
  /**
1824
1849
  * Read the RAW system prompt for the snapshot, converting any projection
@@ -1911,6 +1936,7 @@ export class ConsolePiRuntime {
1911
1936
  if (services)
1912
1937
  this.applySessionCompactionOverride(sessionId, services, session);
1913
1938
  await this.refreshModelConfiguration(sessionId, "force");
1939
+ pinOperatorChatTools(session, this.sessionModeOf(sessionId));
1914
1940
  }
1915
1941
  catch (error) {
1916
1942
  return {
@@ -2347,7 +2373,9 @@ export class ConsolePiRuntime {
2347
2373
  * than silently creating a fresh empty session and losing prior context.
2348
2374
  */
2349
2375
  async reopenSession(init) {
2376
+ await this.syncAimaxAuthToken();
2350
2377
  const sessionId = init.sessionId;
2378
+ const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? this.sessionModeOf(sessionId));
2351
2379
  // Wait for in-flight create materialization rather than racing reopen.
2352
2380
  const initState = this.sessionInit.get(sessionId);
2353
2381
  if (initState?.status === "initializing") {
@@ -2424,6 +2452,7 @@ export class ConsolePiRuntime {
2424
2452
  sessionManager,
2425
2453
  model: init.model,
2426
2454
  systemPromptSuffix: init.systemPromptSuffix,
2455
+ sessionMode,
2427
2456
  });
2428
2457
  const session = built.session;
2429
2458
  // Prefer the SessionManager id (from the JSONL header). Some SDK
@@ -2446,7 +2475,6 @@ export class ConsolePiRuntime {
2446
2475
  message: `reopened Pi sessionId mismatch: expected ${sessionId}, got ${reportedId}`,
2447
2476
  };
2448
2477
  }
2449
- session.setActiveToolsByName(computeOperatorChatActiveToolNames(session));
2450
2478
  this.sessions.set(sessionId, session);
2451
2479
  this.sessionManagers.set(sessionId, sessionManager);
2452
2480
  this.modelRuntimes.set(sessionId, built.modelRuntime);
@@ -2464,6 +2492,7 @@ export class ConsolePiRuntime {
2464
2492
  createdAt: new Date().toISOString(),
2465
2493
  model: init.model,
2466
2494
  activeTools: session.getActiveToolNames(),
2495
+ sessionMode,
2467
2496
  },
2468
2497
  };
2469
2498
  }
@@ -2495,6 +2524,7 @@ export class ConsolePiRuntime {
2495
2524
  createdAt: new Date().toISOString(),
2496
2525
  model,
2497
2526
  activeTools: session.getActiveToolNames(),
2527
+ sessionMode: this.sessionModeOf(sessionId),
2498
2528
  };
2499
2529
  }
2500
2530
  /**
@@ -2503,12 +2533,14 @@ export class ConsolePiRuntime {
2503
2533
  * before AgentSession is ready. Tool definitions reuse runtime-level caches.
2504
2534
  */
2505
2535
  async buildSessionWithServices(init) {
2536
+ const sessionMode = this.rememberSessionMode(init.sessionId, init.sessionMode ?? this.sessionModeOf(init.sessionId));
2506
2537
  const agentDir = await safeGetAgentDir(this.bindings);
2507
2538
  const modelConfigFile = await captureModelConfigFile(agentDir);
2508
- const appendSystemPrompt = [
2509
- OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
2510
- ...(init.systemPromptSuffix ? [init.systemPromptSuffix] : []),
2511
- ];
2539
+ const appendSystemPrompt = composeChatSystemPromptParts({
2540
+ sessionMode,
2541
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
2542
+ systemPromptSuffix: init.systemPromptSuffix,
2543
+ });
2512
2544
  await this.refreshSkillPrefsCache().catch(() => undefined);
2513
2545
  const { services } = await this.bindings.createServices({
2514
2546
  cwd: this.options.cwd,
@@ -2533,7 +2565,7 @@ export class ConsolePiRuntime {
2533
2565
  ...(resolvedModel ? { model: resolvedModel } : {}),
2534
2566
  });
2535
2567
  await bindSessionExtensions(session);
2536
- pinOperatorChatTools(session);
2568
+ pinOperatorChatTools(session, sessionMode);
2537
2569
  this.applySessionCompactionOverride(init.sessionId, services, session);
2538
2570
  return {
2539
2571
  session,
@@ -2602,7 +2634,7 @@ export class ConsolePiRuntime {
2602
2634
  }
2603
2635
  /**
2604
2636
  * Send a prompt and stream events. Gate 3: each tool invocation is
2605
- * re-authorized by authorizeOperatorChatTool before the dispatcher runs it
2637
+ * re-authorized by authorizeChatSessionTool(mode) before the dispatcher runs it
2606
2638
  * (the dispatcher is wired by the HTTP layer, see chat-session.ts).
2607
2639
  *
2608
2640
  * Prompt ownership is acquired synchronously before the first await so two
@@ -2644,7 +2676,8 @@ export class ConsolePiRuntime {
2644
2676
  if (options?.turnId && this.options.todoRegistry) {
2645
2677
  this.options.todoRegistry.startTurn(sessionId, options.turnId);
2646
2678
  }
2647
- if (this.options.goalRegistry) {
2679
+ if (this.options.goalRegistry &&
2680
+ shouldInjectGoalContinuation(this.sessionModeOf(sessionId))) {
2648
2681
  this.options.goalRegistry.startTurn(sessionId, text);
2649
2682
  }
2650
2683
  // AC-FIX-002: if Stop already fired before readiness, never start model work.
@@ -2665,10 +2698,11 @@ export class ConsolePiRuntime {
2665
2698
  // existing closed interview allow/deny registry. The next ordinary turn
2666
2699
  // explicitly restores the normal Operator Chat set (baseline + extension
2667
2700
  // tools, ADR 0012).
2668
- const requestedTools = OPERATOR_CHAT_ACTIVE_TOOL_NAMES();
2669
- 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"
2670
2704
  ? filterActiveInterviewTools(requestedTools).allowed
2671
- : computeOperatorChatActiveToolNames(session);
2705
+ : computeChatActiveToolNames(session, sessionMode);
2672
2706
  session.setActiveToolsByName(activeTools);
2673
2707
  // Final pre-prompt fence: abort after tool pin must still skip session.prompt.
2674
2708
  if (options?.signal?.aborted) {
@@ -3006,6 +3040,7 @@ export class ConsolePiRuntime {
3006
3040
  this.modelRuntimes.delete(sessionId);
3007
3041
  this.modelConfigFiles.delete(sessionId);
3008
3042
  this.sessionModels.delete(sessionId);
3043
+ this.sessionModes.delete(sessionId);
3009
3044
  this.serviceScopes.delete(sessionId);
3010
3045
  this.revisions.delete(sessionId);
3011
3046
  this.thinkingSelections.delete(sessionId);
@@ -3321,7 +3356,8 @@ export function createDefaultPiSdkBindings() {
3321
3356
  };
3322
3357
  }
3323
3358
  /**
3324
- * Re-export the tool gate for the dispatcher layer so the per-tool-call gate
3325
- * (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.
3326
3362
  */
3327
- export { authorizeOperatorChatTool, assertNoWriteToolInList, OPERATOR_CHAT_ALLOWED_TOOLS, };
3363
+ export { authorizeChatSessionTool, authorizeOperatorChatTool, assertNoWriteToolInList, OPERATOR_CHAT_ALLOWED_TOOLS, };