@tea-agent/loop-agent 0.44.0-next.5 → 0.44.0-next.7

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 (111) hide show
  1. package/CHANGELOG.md +299 -445
  2. package/dist/build-stamp.json +3 -3
  3. package/dist/executors/pi-sdk-executor.js +7 -7
  4. package/dist/infrastructure/console/operation-store.js +46 -14
  5. package/dist/shared/git-workspace-status.js +2 -0
  6. package/dist/shared/wizard-local-telemetry.js +2 -1
  7. package/dist/worker/console/chat/assistant-content.js +5 -34
  8. package/dist/worker/console/chat/chat-event-store.js +27 -10
  9. package/dist/worker/console/chat/db-connections.js +72 -8
  10. package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +52 -24
  11. package/dist/worker/console/chat/pi-runtime.js +251 -18
  12. package/dist/worker/console/chat/repo-browser.js +49 -0
  13. package/dist/worker/console/chat/routes.js +215 -66
  14. package/dist/worker/console/chat/session-store.js +147 -19
  15. package/dist/worker/console/chat/turn-process.js +69 -23
  16. package/dist/worker/console/chat/user-turn-outline.js +19 -0
  17. package/dist/worker/console/console-update-runtime.js +2 -2
  18. package/dist/worker/console/operator-user-error.js +28 -1
  19. package/dist/worker/console/pi-readiness.js +4 -4
  20. package/dist/worker/console/recovery-cta.js +14 -14
  21. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BHuhGM1e.js → abnfDiagram-N423BO3Z-DTuku_-7.js} +1 -1
  22. package/dist/worker/console/static/assets/{arc-CKh5E_sr.js → arc-CQbCsSqb.js} +1 -1
  23. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DizhiBEv.js → architectureDiagram-T3A2C74G-C-xUntTR.js} +1 -1
  24. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-3Neea-Z-.js → blockDiagram-VBNYF7ZC-DhNlwGAl.js} +1 -1
  25. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCP_Qrgi.js → c4Diagram-5PPSVZJV-B7j3nx8o.js} +1 -1
  26. package/dist/worker/console/static/assets/channel-5_os7t_x.js +1 -0
  27. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C_wydftK.js → chunk-2GRJ4B5K-BCrcxEh2.js} +1 -1
  28. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-jRPXZbzx.js → chunk-2Q5K7J3B-CsaePDb3.js} +1 -1
  29. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BLQf2OYO.js → chunk-5RXB4S5H--SRQc1rN.js} +1 -1
  30. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-0qRq64rI.js → chunk-5VM5RSS4-C5BFY2Po.js} +1 -1
  31. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-ClMg8xmV.js → chunk-6Q2QTUOP-Caso6uN7.js} +1 -1
  32. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Coop2FlL.js → chunk-GF5L2VYU-AY2ZSAx2.js} +1 -1
  33. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CDdO-jPJ.js → chunk-JWPE2WC7-DiMqZQ_8.js} +1 -1
  34. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-D8Gkppjl.js → chunk-KBJHAD2P-gpvvriOp.js} +1 -1
  35. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DA9HIySf.js → chunk-RYQCIY6F-CUdyoJhI.js} +1 -1
  36. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DZNDRX1k.js → chunk-XXDRQBXY-Bp1xW3T4.js} +1 -1
  37. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-loz0n18W.js +1 -0
  38. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-loz0n18W.js +1 -0
  39. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CZ1zGyjq.js → cose-bilkent-JH36ORCC-D-JIcOIT.js} +1 -1
  40. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-B8u1_FtM.js → cynefin-VYW2F7L2-C-NKFflx.js} +1 -1
  41. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-CPBB6lfo.js → cynefinDiagram-MW4NZA55-CSOcvOAA.js} +1 -1
  42. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CG3Eov3E.js → dagre-VZM6K2ZE-BQSBltMy.js} +1 -1
  43. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ycI-dOjh.js → diagram-7IWD3JNH-B0LVC45G.js} +1 -1
  44. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bx_GyQ7Y.js → diagram-B4RE2ZJO-Cypg3nqy.js} +1 -1
  45. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-m0G6mP4d.js → diagram-LBJQPF4R-BBDU88XJ.js} +1 -1
  46. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DTDJkqcJ.js → diagram-Q27KOJAE-BZusLqSb.js} +1 -1
  47. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BCfFrG-p.js → diagram-UB23O5K3-Cfolotcr.js} +1 -1
  48. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BIkTNPpp.js → ebnfDiagram-BXEA7PRR-CjfyUy0m.js} +1 -1
  49. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DTcMWrd2.js → erDiagram-JOGREHBK-DJdz7txE.js} +1 -1
  50. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BqzDaNQ7.js → flowDiagram-UKHOOZJN-CQqgPO6y.js} +1 -1
  51. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BSl_vGFe.js → ganttDiagram-PKOTCBZU-BOpGfS2m.js} +1 -1
  52. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DCA5HXM6.js → gitGraphDiagram-DS77QQ5N-C6UCu0qx.js} +1 -1
  53. package/dist/worker/console/static/assets/index-CUT440A8.css +1 -0
  54. package/dist/worker/console/static/assets/index-DWKejNVJ.js +486 -0
  55. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DvqT18ge.js → infoDiagram-6WML65LV-BWeaVEUB.js} +1 -1
  56. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B6zGKrQ3.js → ishikawaDiagram-WSZJBQD7-C8gmlBbk.js} +1 -1
  57. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BpLQPI9C.js → journeyDiagram-NVQOT4AX-DF3bm8Bp.js} +1 -1
  58. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CzIz_-Lt.js → kanban-definition-27J2QSJJ-D2fG2Vxi.js} +1 -1
  59. package/dist/worker/console/static/assets/{linear-CMQ8Jpp-.js → linear-BGKgMc2T.js} +1 -1
  60. package/dist/worker/console/static/assets/{mermaid.core-BdAMrTf7.js → mermaid.core-DF6mMPJ9.js} +5 -5
  61. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-hSwF6sOq.js → mindmap-definition-FAOFIHXS-AMhePheC.js} +1 -1
  62. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DZdv509B.js → pegDiagram-VL7TDLO6-DN1Cl0W0.js} +1 -1
  63. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CmD4-b0K.js → pieDiagram-7S7Q4E2Y-Cx9ngUeN.js} +1 -1
  64. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DtKxNxlS.js → quadrantDiagram-CIZ2JOQS-BcYPTQJb.js} +1 -1
  65. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-D48iIoiS.js → railroadDiagram-AXF67PYL-BqHUZWnH.js} +1 -1
  66. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C3N0K-2p.js → requirementDiagram-LRYGKXZP-CmqWZ0-B.js} +1 -1
  67. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CACWR-Xs.js → sankeyDiagram-W5VNT64P--4YetH1y.js} +1 -1
  68. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-g_QgugvZ.js → sequenceDiagram-SI44F4Z6-DGD4VMfD.js} +1 -1
  69. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DqLoRPGK.js → sizeCapture-X5ZJPWSS-QVw9q7rh.js} +1 -1
  70. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-ES59VgMG.js → stateDiagram-OKZ733FA-CuemV9pe.js} +1 -1
  71. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BdlGdIJw.js +1 -0
  72. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-fjyLVNWo.js → swimlanes-SLNWSIFB-C7oKzZKs.js} +2 -2
  73. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DjjbHbde.js +8 -0
  74. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cd5-biKv.js → timeline-definition-Z64GVDOM-C2Of60nT.js} +1 -1
  75. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CZTu6mPf.js → vennDiagram-T6HMQDX7-DHtpQHyL.js} +1 -1
  76. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DR01jjbe.js → wardleyDiagram-T6FBY63Y-COi9OhSy.js} +1 -1
  77. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-iZcuGVGa.js → xychartDiagram-ELKLHX3M-DPHQfRrt.js} +1 -1
  78. package/dist/worker/console/static/index.html +2 -2
  79. package/dist/worker/console/static-src/app/console-types.js +9 -1
  80. package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
  81. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +58 -3
  82. package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
  83. package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
  84. package/dist/worker/console/static-src/operator-chat/process-label.js +19 -6
  85. package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
  86. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +2 -0
  87. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
  88. package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
  89. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
  90. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +202 -41
  91. package/dist/worker/console/static-src/operator-chat/useChatStream.js +78 -23
  92. package/dist/worker/console/static-src/operator-chat/useChatThread.js +55 -18
  93. package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
  94. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
  95. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
  96. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
  97. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  98. package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
  99. package/dist/worker/console/workspace-initialization.js +3 -3
  100. package/dist/worker/observability/read-model.js +23 -2
  101. package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
  102. package/dist/worker/scheduler/scheduled-goal-evidence.js +6 -1
  103. package/docs/templates/backend-test-dag.json +1 -0
  104. package/package.json +4 -4
  105. package/dist/worker/console/static/assets/channel-DxgzA1GF.js +0 -1
  106. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BZcm7fs6.js +0 -1
  107. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BZcm7fs6.js +0 -1
  108. package/dist/worker/console/static/assets/index-BAEDjTbD.css +0 -1
  109. package/dist/worker/console/static/assets/index-wKEJUaP5.js +0 -482
  110. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyCIq6jx.js +0 -1
  111. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CsKtO34q.js +0 -8
@@ -191,6 +191,25 @@ async function bindSessionExtensions(session) {
191
191
  return;
192
192
  await session.bindExtensions(createConsoleExtensionBindings());
193
193
  }
194
+ export function branchForkedSessionManager(sessionManager, throughUserMessageIndex) {
195
+ const userEntries = (sessionManager.getBranch?.() ?? []).filter((entry) => {
196
+ if (!entry || typeof entry !== "object")
197
+ return false;
198
+ const candidate = entry;
199
+ return candidate.type === "message" && candidate.message?.role === "user";
200
+ });
201
+ const target = userEntries[throughUserMessageIndex];
202
+ if (!target || typeof target.id !== "string") {
203
+ throw new Error("FORK_MESSAGE_ENTRY_NOT_FOUND");
204
+ }
205
+ sessionManager.branch(target.id);
206
+ if (typeof sessionManager.appendCustomEntry !== "function") {
207
+ throw new Error("FORK_MESSAGE_ENTRY_PERSIST_UNSUPPORTED");
208
+ }
209
+ sessionManager.appendCustomEntry("loop-agent-fork-point", {
210
+ throughUserMessageIndex,
211
+ });
212
+ }
194
213
  function pinOperatorChatTools(session, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
195
214
  session.setActiveToolsByName(computeChatActiveToolNames(session, sessionMode));
196
215
  }
@@ -296,6 +315,14 @@ export function createWizardLocalHeadersExtension(options) {
296
315
  }
297
316
  export { THINKING_LEVELS, RuntimeSnapshotUnavailableError, projectRuntimeSnapshot, };
298
317
  export { createDefaultPiSdkBindings } from "./pi-runtime/sdk-bindings.js";
318
+ /** Catalog listing must not load skills/extensions/context — those belong to reopen. */
319
+ const CATALOG_RESOURCE_LOADER_OPTIONS = Object.freeze({
320
+ noContextFiles: true,
321
+ noSkills: true,
322
+ noExtensions: true,
323
+ noPromptTemplates: true,
324
+ noThemes: true,
325
+ });
299
326
  export { extractAssistantContentBlocks, extractAssistantText, isAssistantSdkMessage };
300
327
  export function buildRequestContextEvent(input) {
301
328
  const systemPrompt = input.session.state?.systemPrompt ?? input.session.systemPrompt ?? "";
@@ -440,6 +467,8 @@ export class ConsolePiRuntime {
440
467
  mainlineLeaves = new Map();
441
468
  /** Per-sessionId single-flight materialization (create fast-path / readiness fence). */
442
469
  sessionInit = new Map();
470
+ /** Concurrent reopen of the same historical JSONL shares one in-flight promise. */
471
+ reopenInFlight = new Map();
443
472
  /** Resolved model binding after materialization (create may return before this is known). */
444
473
  sessionModels = new Map();
445
474
  /** Durable session mode; record is the source of truth, this is the live handle cache. */
@@ -1188,6 +1217,9 @@ export class ConsolePiRuntime {
1188
1217
  targetSessionDir: this.options.sessionDir,
1189
1218
  targetSessionId: sessionId,
1190
1219
  });
1220
+ if (init.throughUserMessageIndex !== undefined) {
1221
+ branchForkedSessionManager(sessionManager, init.throughUserMessageIndex);
1222
+ }
1191
1223
  const built = await this.buildSessionWithServices({
1192
1224
  sessionId,
1193
1225
  sessionManager,
@@ -1251,11 +1283,68 @@ export class ConsolePiRuntime {
1251
1283
  entries: manager.getBranch?.() ?? [],
1252
1284
  };
1253
1285
  }
1286
+ /**
1287
+ * Workspace-scoped model catalog. A live session's ModelRuntime is reused
1288
+ * when present; otherwise services are created without opening the Pi
1289
+ * conversation JSONL so the picker is not blocked on cold reopen.
1290
+ */
1291
+ workspaceModelRuntime;
1292
+ workspaceInspection;
1293
+ async getWorkspaceModelRuntime() {
1294
+ if (!this.workspaceModelRuntime) {
1295
+ this.workspaceModelRuntime = (async () => {
1296
+ const agentDir = await safeGetAgentDir(this.bindings);
1297
+ const { services } = await this.bindings.createServices({
1298
+ cwd: this.options.cwd,
1299
+ agentDir,
1300
+ resourceLoaderOptions: { ...CATALOG_RESOURCE_LOADER_OPTIONS },
1301
+ });
1302
+ return services.modelRuntime;
1303
+ })().catch((error) => {
1304
+ this.workspaceModelRuntime = undefined;
1305
+ throw error;
1306
+ });
1307
+ }
1308
+ return this.workspaceModelRuntime;
1309
+ }
1310
+ /**
1311
+ * Full OPEN-surface services for inspect UIs. No SessionManager / JSONL.
1312
+ * Cached without sessionId so todo/goal extensions stay off the shared scope.
1313
+ */
1314
+ async getWorkspaceInspectionServices() {
1315
+ if (!this.workspaceInspection) {
1316
+ this.workspaceInspection = (async () => {
1317
+ const agentDir = await safeGetAgentDir(this.bindings);
1318
+ const appendSystemPrompt = composeChatSystemPromptParts({
1319
+ sessionMode: DEFAULT_CONSOLE_CHAT_SESSION_MODE,
1320
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1321
+ });
1322
+ const { services } = await this.bindings.createServices({
1323
+ cwd: this.options.cwd,
1324
+ agentDir,
1325
+ resourceLoaderOptions: await this.buildResourceLoaderOptions({
1326
+ appendSystemPrompt,
1327
+ }),
1328
+ });
1329
+ return {
1330
+ modelRuntime: services.modelRuntime,
1331
+ resourceLoader: services
1332
+ .resourceLoader,
1333
+ settingsManager: services.settingsManager,
1334
+ };
1335
+ })().catch((error) => {
1336
+ this.workspaceInspection = undefined;
1337
+ throw error;
1338
+ });
1339
+ }
1340
+ return this.workspaceInspection;
1341
+ }
1254
1342
  async listModels(sessionId) {
1255
- await this.ensureSessionReady(sessionId);
1256
- const modelRuntime = this.modelRuntimes.get(sessionId);
1257
- if (!this.sessions.has(sessionId) || !modelRuntime)
1258
- throw new Error(`chat session not active: ${sessionId}`);
1343
+ const live = this.modelRuntimes.get(sessionId);
1344
+ if (live && this.sessions.has(sessionId)) {
1345
+ return this.bindings.listAvailableModels({ modelRuntime: live });
1346
+ }
1347
+ const modelRuntime = await this.getWorkspaceModelRuntime();
1259
1348
  return this.bindings.listAvailableModels({ modelRuntime });
1260
1349
  }
1261
1350
  async applyModel(sessionId, model) {
@@ -1518,11 +1607,11 @@ export class ConsolePiRuntime {
1518
1607
  return { ok: true, revision: this.revisions.get(sessionId) ?? 0 };
1519
1608
  }
1520
1609
  /**
1521
- * Project the unified live runtime snapshot (design §2.2). The current Pi
1522
- * session is the single source of truth: real system prompt, real model /
1523
- * thinking selection (auto + effective), the session modelRuntime model
1524
- * list, and the session ResourceLoader inventories. Undefined when the
1525
- * session is not active — callers fail closed instead of fabricating data.
1610
+ * Project the unified runtime snapshot (design §2.2). A live Pi session is
1611
+ * the source of truth when present. Inspect GETs may pass
1612
+ * `workspaceProjection` to render ResourceLoader inventories without
1613
+ * opening the conversation JSONL. Unknown ids without that hint stay
1614
+ * undefined so callers fail closed.
1526
1615
  */
1527
1616
  /**
1528
1617
  * ConsolePiRuntime.listSlashCommands
@@ -1541,18 +1630,27 @@ export class ConsolePiRuntime {
1541
1630
  return collectSlashCommands({ session, resourceLoader });
1542
1631
  }
1543
1632
  async getRuntimeSnapshot(sessionId, options) {
1544
- try {
1545
- await this.ensureSessionReady(sessionId);
1546
- }
1547
- catch (error) {
1548
- if (error instanceof PiSessionInitFailedError)
1549
- throw error;
1550
- // Inactive / unknown session: fail closed with undefined (no fabricated snapshot).
1551
- return undefined;
1633
+ const initializing = this.sessionInit.get(sessionId)?.status === "initializing";
1634
+ if (this.sessions.has(sessionId) || initializing) {
1635
+ try {
1636
+ await this.ensureSessionReady(sessionId);
1637
+ }
1638
+ catch (error) {
1639
+ if (error instanceof PiSessionInitFailedError)
1640
+ throw error;
1641
+ if (options?.workspaceProjection) {
1642
+ return this.projectWorkspaceRuntimeSnapshot(sessionId, options);
1643
+ }
1644
+ return undefined;
1645
+ }
1552
1646
  }
1553
1647
  const session = this.sessions.get(sessionId);
1554
- if (!session)
1648
+ if (!session) {
1649
+ if (options?.workspaceProjection) {
1650
+ return this.projectWorkspaceRuntimeSnapshot(sessionId, options);
1651
+ }
1555
1652
  return undefined;
1653
+ }
1556
1654
  const services = this.serviceScopes.get(sessionId);
1557
1655
  const modelRuntime = this.modelRuntimes.get(sessionId);
1558
1656
  const resourceLoader = services?.resourceLoader;
@@ -1653,6 +1751,116 @@ export class ConsolePiRuntime {
1653
1751
  hasBash: true,
1654
1752
  });
1655
1753
  }
1754
+ resolveWorkspaceThinkingSelection(sessionId, stored) {
1755
+ const live = this.thinkingSelections.get(sessionId);
1756
+ if (live)
1757
+ return live;
1758
+ if (!stored || stored === "auto")
1759
+ return "auto";
1760
+ if (THINKING_LEVELS.includes(stored)) {
1761
+ return stored;
1762
+ }
1763
+ return "auto";
1764
+ }
1765
+ /**
1766
+ * Inspect projection without SessionManager.open. Used only when the Agent
1767
+ * is not live and the caller confirmed a durable session record.
1768
+ */
1769
+ async projectWorkspaceRuntimeSnapshot(sessionId, options) {
1770
+ const hint = options.workspaceProjection ?? {};
1771
+ const sessionMode = hint.sessionMode ?? this.sessionModeOf(sessionId);
1772
+ let inspection;
1773
+ try {
1774
+ inspection = await this.getWorkspaceInspectionServices();
1775
+ }
1776
+ catch (error) {
1777
+ throw new RuntimeSnapshotUnavailableError(`workspace runtime projection failed: ${error instanceof Error ? error.message : String(error)}`);
1778
+ }
1779
+ const resourceLoader = inspection.resourceLoader;
1780
+ const settingsManager = inspection.settingsManager;
1781
+ const thinkingSelection = this.resolveWorkspaceThinkingSelection(sessionId, hint.thinkingLevel);
1782
+ const effectiveThinkingLevel = settingsManager?.getDefaultThinkingLevel?.() ??
1783
+ (thinkingSelection === "auto" ? undefined : thinkingSelection);
1784
+ const rawSystemPrompt = resourceLoader?.getSystemPrompt?.() ||
1785
+ composeChatSystemPrompt({
1786
+ sessionMode,
1787
+ workflowPrompt: OPERATOR_CHAT_SYSTEM_PROMPT_BASE,
1788
+ });
1789
+ if (!rawSystemPrompt) {
1790
+ throw new RuntimeSnapshotUnavailableError("system prompt projection failed: workspace prompt was empty");
1791
+ }
1792
+ let models = [];
1793
+ try {
1794
+ models = this.bindings
1795
+ .listAvailableModels({ modelRuntime: inspection.modelRuntime })
1796
+ .map((entry) => ({
1797
+ provider: entry.provider,
1798
+ id: entry.id,
1799
+ ...(entry.name ? { name: entry.name } : {}),
1800
+ hasCredentials: entry.hasCredentials,
1801
+ }));
1802
+ }
1803
+ catch (error) {
1804
+ throw new RuntimeSnapshotUnavailableError(`model enumeration failed: ${error instanceof Error ? error.message : String(error)}`);
1805
+ }
1806
+ const thinkingLevels = [
1807
+ { value: "auto" },
1808
+ ...THINKING_LEVELS.map((value) => ({ value })),
1809
+ ];
1810
+ let skillPrefs = { schemaVersion: 1, skills: {} };
1811
+ let skillPrefsError;
1812
+ if (resourceLoader) {
1813
+ try {
1814
+ skillPrefs = await this.loadSkillAutoInvocationPreferences();
1815
+ }
1816
+ catch (error) {
1817
+ skillPrefsError = error;
1818
+ }
1819
+ }
1820
+ const activeTools = chatActiveToolBaseline(sessionMode);
1821
+ const inventories = await collectRuntimeInventories({
1822
+ resourceLoader,
1823
+ skillPrefs,
1824
+ skillPrefsError,
1825
+ agentDir: await safeGetAgentDir(this.bindings),
1826
+ cwd: this.options.cwd,
1827
+ tools: [],
1828
+ activeToolNames: activeTools,
1829
+ });
1830
+ const storedModel = this.sessionModels.get(sessionId);
1831
+ const provider = storedModel?.provider ?? hint.modelProvider;
1832
+ const modelId = storedModel?.modelId ?? hint.modelId;
1833
+ return projectRuntimeSnapshot({
1834
+ sessionId,
1835
+ revision: this.revisions.get(sessionId) ?? 0,
1836
+ loadedAt: new Date().toISOString(),
1837
+ selection: {
1838
+ ...(provider && modelId
1839
+ ? { model: { provider, id: modelId } }
1840
+ : {}),
1841
+ thinkingSelection,
1842
+ ...(effectiveThinkingLevel
1843
+ ? { effectiveThinkingLevel: effectiveThinkingLevel }
1844
+ : {}),
1845
+ },
1846
+ models,
1847
+ thinkingLevels,
1848
+ systemPrompt: {
1849
+ ...(options.includeRaw ? { raw: rawSystemPrompt } : {}),
1850
+ text: rawSystemPrompt,
1851
+ sha256: createHash("sha256").update(rawSystemPrompt).digest("hex"),
1852
+ characters: rawSystemPrompt.length,
1853
+ },
1854
+ activeTools,
1855
+ contextFiles: inventories.contextFiles,
1856
+ skills: inventories.skills,
1857
+ extensions: inventories.extensions,
1858
+ packages: inventories.packages,
1859
+ mcp: inventories.mcp,
1860
+ diagnostics: inventories.diagnostics,
1861
+ hasBash: true,
1862
+ });
1863
+ }
1656
1864
  /**
1657
1865
  * Re-open a previously persisted Chat session after Console restart or
1658
1866
  * browser refresh (roadmap M1-S04 / S05).
@@ -1667,6 +1875,19 @@ export class ConsolePiRuntime {
1667
1875
  * than silently creating a fresh empty session and losing prior context.
1668
1876
  */
1669
1877
  async reopenSession(init) {
1878
+ const sessionId = init.sessionId;
1879
+ const existing = this.reopenInFlight.get(sessionId);
1880
+ if (existing)
1881
+ return existing;
1882
+ const pending = this.reopenSessionExclusive(init).finally(() => {
1883
+ if (this.reopenInFlight.get(sessionId) === pending) {
1884
+ this.reopenInFlight.delete(sessionId);
1885
+ }
1886
+ });
1887
+ this.reopenInFlight.set(sessionId, pending);
1888
+ return pending;
1889
+ }
1890
+ async reopenSessionExclusive(init) {
1670
1891
  await this.syncAimaxAuthToken();
1671
1892
  const sessionId = init.sessionId;
1672
1893
  const sessionMode = this.rememberSessionMode(sessionId, init.sessionMode ?? this.sessionModeOf(sessionId));
@@ -2493,6 +2714,18 @@ export function mapSdkEvent(sessionId, event, compactionId, step = 1) {
2493
2714
  ? { parentToolCallId: event.parentToolCallId }
2494
2715
  : {}),
2495
2716
  };
2717
+ case "tool_execution_update":
2718
+ return {
2719
+ type: "tool_update",
2720
+ sessionId,
2721
+ toolCallId: event.toolCallId,
2722
+ toolName: event.toolName,
2723
+ partialResult: event.partialResult,
2724
+ step,
2725
+ ...(event.parentToolCallId
2726
+ ? { parentToolCallId: event.parentToolCallId }
2727
+ : {}),
2728
+ };
2496
2729
  case "tool_execution_end":
2497
2730
  return {
2498
2731
  type: "tool_result",
@@ -127,6 +127,55 @@ export async function listRepoDirectory(repoRoot, rawPath) {
127
127
  throw error;
128
128
  }
129
129
  }
130
+ /** Composer `@` palette keeps the first page of a directory listing scannable. */
131
+ export const FILE_PALETTE_LIMIT = 50;
132
+ /**
133
+ * Split an `@` token query into a directory listing target and basename filter.
134
+ * Empty or slash-free queries stay on the workspace root (M3 fuzzy is out of
135
+ * scope). A trailing slash lists that directory with an empty fragment.
136
+ */
137
+ export function parseFilePaletteQuery(rawQuery) {
138
+ const query = rawQuery.replaceAll("\\", "/").replace(/^\/+/u, "");
139
+ const slash = query.lastIndexOf("/");
140
+ if (slash < 0)
141
+ return { directory: "", fragment: query };
142
+ return {
143
+ directory: query.slice(0, slash),
144
+ fragment: query.slice(slash + 1),
145
+ };
146
+ }
147
+ export function projectFilePaletteEntries(entries, fragment) {
148
+ const showDots = fragment.startsWith(".");
149
+ const needle = fragment.toLowerCase();
150
+ const projected = [];
151
+ for (const entry of entries) {
152
+ if (!showDots && entry.name.startsWith("."))
153
+ continue;
154
+ if (needle !== "" && !entry.name.toLowerCase().includes(needle))
155
+ continue;
156
+ const isDir = entry.kind === "directory";
157
+ projected.push({
158
+ path: isDir ? `${entry.path}/` : entry.path,
159
+ isDir,
160
+ });
161
+ if (projected.length >= FILE_PALETTE_LIMIT)
162
+ break;
163
+ }
164
+ return projected;
165
+ }
166
+ /** One-level `@` file palette listing. Unreadable or out-of-repo queries are empty. */
167
+ export async function listChatFilePalette(repoRoot, rawQuery) {
168
+ const { directory, fragment } = parseFilePaletteQuery(rawQuery);
169
+ try {
170
+ const listed = await listRepoDirectory(repoRoot, directory);
171
+ return projectFilePaletteEntries(listed.entries, fragment);
172
+ }
173
+ catch (error) {
174
+ if (error instanceof RepoBrowserError)
175
+ return [];
176
+ throw error;
177
+ }
178
+ }
130
179
  export async function readRepoPreview(repoRoot, rawPath) {
131
180
  const guard = await guardReadPath(repoRoot, rawPath);
132
181
  if (!guard.ok)