@tea-agent/loop-agent 0.40.0-next.7 → 0.40.0-next.9

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 (90) hide show
  1. package/CHANGELOG.md +106 -140
  2. package/dist/build-stamp.json +3 -3
  3. package/dist/executors/dag-pi-executor.js +3 -0
  4. package/dist/executors/pi-executor.js +4 -2
  5. package/dist/executors/pi-sdk-executor.js +46 -1
  6. package/dist/worker/console/chat/chat-event-store.js +13 -1
  7. package/dist/worker/console/chat/pi-runtime.js +70 -5
  8. package/dist/worker/console/chat/routes.js +99 -29
  9. package/dist/worker/console/chat/workspace-landing.js +65 -0
  10. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DGt5TRsk.js → abnfDiagram-N423BO3Z-Bs-CDAXM.js} +1 -1
  11. package/dist/worker/console/static/assets/{arc-DKBSJsXm.js → arc-CAkA3We3.js} +1 -1
  12. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BnhooRBs.js → architectureDiagram-T3A2C74G-DEmI_zqr.js} +1 -1
  13. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bm9qkrJP.js → blockDiagram-VBNYF7ZC-uDHANebE.js} +1 -1
  14. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B2cFh4bO.js → c4Diagram-5PPSVZJV-Bv97b4_B.js} +1 -1
  15. package/dist/worker/console/static/assets/channel-amxUpk7o.js +1 -0
  16. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DhvWRI03.js → chunk-2GRJ4B5K-shmQKsFz.js} +1 -1
  17. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-chzTP_2w.js → chunk-2Q5K7J3B-BAraNvcN.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-SdWIR12m.js → chunk-5RXB4S5H-BirhxUop.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C2-8Eo_A.js → chunk-5VM5RSS4-C9LgUJpR.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DvbE1yQW.js → chunk-6Q2QTUOP-DrU-s-t7.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CEenyPTz.js → chunk-GF5L2VYU-Tx-H_FA2.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CcaZrq0k.js → chunk-JWPE2WC7-BjLtNFrR.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-FGngOTto.js → chunk-KBJHAD2P-UNLVwSsy.js} +1 -1
  24. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-jT_yX3sf.js → chunk-RYQCIY6F-CBEUX8XD.js} +1 -1
  25. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CjE9LQOM.js → chunk-XXDRQBXY-WTL7IZOq.js} +1 -1
  26. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +1 -0
  27. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +1 -0
  28. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-GQWns1tb.js → cose-bilkent-JH36ORCC-CkHG6W3s.js} +1 -1
  29. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CGRpxWNJ.js → cynefin-VYW2F7L2-BRzFqTaL.js} +1 -1
  30. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Cd3uFGE4.js → cynefinDiagram-MW4NZA55-2_CFp_i7.js} +1 -1
  31. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-DmDHuUW0.js → dagre-VZM6K2ZE-BJtsmdkE.js} +1 -1
  32. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-kAr--hF3.js → diagram-7IWD3JNH-DNEFFo6w.js} +1 -1
  33. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-woahL2Oh.js → diagram-B4RE2ZJO-B_IWV7eo.js} +1 -1
  34. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-7syNVaNf.js → diagram-LBJQPF4R-DvvRx2dz.js} +1 -1
  35. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BELPXiaY.js → diagram-Q27KOJAE-CsibsWLg.js} +1 -1
  36. package/dist/worker/console/static/assets/{diagram-UB23O5K3-CrPa4aXi.js → diagram-UB23O5K3-les_djBQ.js} +1 -1
  37. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-B48u2VVe.js → ebnfDiagram-BXEA7PRR-BEm2n7qJ.js} +1 -1
  38. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DhBTfwTF.js → erDiagram-JOGREHBK-BI7vHHVm.js} +1 -1
  39. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CAeZ5nDU.js → flowDiagram-UKHOOZJN-CUFCERrB.js} +1 -1
  40. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CkIFeFLN.js → ganttDiagram-PKOTCBZU-wxyLiGWt.js} +1 -1
  41. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-t0HGYDel.js → gitGraphDiagram-DS77QQ5N-Bt897K8Z.js} +1 -1
  42. package/dist/worker/console/static/assets/index-BHhUOTri.css +1 -0
  43. package/dist/worker/console/static/assets/index-DqJbO3-p.js +407 -0
  44. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-WM0HZhe4.js → infoDiagram-6WML65LV-DYNx_IPv.js} +1 -1
  45. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CV5byHe0.js → ishikawaDiagram-WSZJBQD7-CzpLNEu6.js} +1 -1
  46. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-lIyWkgnw.js → journeyDiagram-NVQOT4AX-97f9owNU.js} +1 -1
  47. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BIH4YNRY.js → kanban-definition-27J2QSJJ-BxM9hyBn.js} +1 -1
  48. package/dist/worker/console/static/assets/{linear-lQ0ZGrWc.js → linear-ByuxcHvp.js} +1 -1
  49. package/dist/worker/console/static/assets/{mermaid.core-BYdXtke8.js → mermaid.core-DwPGvpCv.js} +5 -5
  50. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-k93vkIa4.js → mindmap-definition-FAOFIHXS-DCoqG9RT.js} +1 -1
  51. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Bm6AqvSy.js → pegDiagram-VL7TDLO6-BLSBmBMx.js} +1 -1
  52. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnJW6Xpf.js → pieDiagram-7S7Q4E2Y-B0t_0oJW.js} +1 -1
  53. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CMzm4ObL.js → quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js} +1 -1
  54. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-fwFF5dFQ.js → railroadDiagram-AXF67PYL-B3jRdATC.js} +1 -1
  55. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-tzl9VDUc.js → requirementDiagram-LRYGKXZP-D-cTtXEN.js} +1 -1
  56. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B26V09DD.js → sankeyDiagram-W5VNT64P-B25igJRE.js} +1 -1
  57. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-D2npyCeP.js → sequenceDiagram-SI44F4Z6-jW_lM1_D.js} +1 -1
  58. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CTJtkRU2.js → sizeCapture-X5ZJPWSS-CrzTgsHE.js} +1 -1
  59. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-B8lk4Lzu.js → stateDiagram-OKZ733FA-B01MvlqE.js} +1 -1
  60. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +1 -0
  61. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-C3yP53hH.js → swimlanes-SLNWSIFB-YcF12FiU.js} +2 -2
  62. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +8 -0
  63. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Bck7xjhe.js → timeline-definition-Z64GVDOM-ClnRcg2s.js} +1 -1
  64. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CCntr1w7.js → vennDiagram-T6HMQDX7-B6gr_z-q.js} +1 -1
  65. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DsCqCBZz.js → wardleyDiagram-T6FBY63Y-CvSgguGH.js} +1 -1
  66. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-Br2l90OM.js → xychartDiagram-ELKLHX3M-23OtSZCb.js} +1 -1
  67. package/dist/worker/console/static/index.html +2 -2
  68. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +33 -32
  69. package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
  70. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  71. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
  72. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +54 -56
  73. package/dist/worker/console/static-src/operator-chat/useChatStream.js +81 -27
  74. package/dist/worker/console/static-src/operator-chat/useChatThread.js +14 -25
  75. package/dist/worker/console/static-src/shell/workspace-route.js +7 -5
  76. package/dist/workflows/dag/backend-test-writer-completeness.js +7 -12
  77. package/dist/workflows/dag/init-hybrid.js +27 -5
  78. package/dist/workflows/dag/types.js +13 -0
  79. package/docs/templates/agent-dag.schema.json +18 -0
  80. package/docs/templates/backend-test-dag.json +22 -5
  81. package/package.json +1 -1
  82. package/dist/worker/console/static/assets/channel-DSgFX07x.js +0 -1
  83. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-unJObRls.js +0 -1
  84. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-unJObRls.js +0 -1
  85. package/dist/worker/console/static/assets/index-B4l5ag79.js +0 -407
  86. package/dist/worker/console/static/assets/index-CwVHWB71.css +0 -1
  87. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BmOcMTJE.js +0 -1
  88. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Beah-dq0.js +0 -8
  89. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  90. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
@@ -376,7 +376,7 @@ async function buildAskUserQuestionTool(options) {
376
376
  },
377
377
  });
378
378
  }
379
- const TODO_WRITE_DESCRIPTION = "Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list. Use it for multi-step work; skip trivial single-step tasks. Mark tasks pending, in_progress, or completed, and mark completed immediately when done.";
379
+ const TODO_WRITE_DESCRIPTION = "Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list (no partial updates, no per-item edits). Before starting multi-step work, add one todo per concrete step. Keep at least one todo `in_progress` while work remains. Mark a todo `completed` immediately when it is done (do not batch completions) and resend the full list as progress or DAG state advances. Skip the list for simple Q&A, read-only lookups, and single-step tasks. Use it for multi-step implementation and whenever you prepare, run, or supervise a DAG — call `todo_write` before entering the DAG tool chain. Phrase DAG todos as user-facing phases users can understand, not copies of internal DAG nodes. Statuses: `pending` / `in_progress` / `completed`.";
380
380
  async function buildTodoWriteTool(options) {
381
381
  const [{ Type }, { defineTool }] = await Promise.all([
382
382
  import("typebox"),
@@ -2216,6 +2216,7 @@ export class ConsolePiRuntime {
2216
2216
  let firstTokenAt;
2217
2217
  let pendingProviderError;
2218
2218
  let providerErrorEmitted = false;
2219
+ let activeCompaction;
2219
2220
  const emitPendingProviderError = () => {
2220
2221
  if (!pendingProviderError)
2221
2222
  return;
@@ -2225,7 +2226,18 @@ export class ConsolePiRuntime {
2225
2226
  };
2226
2227
  unsub = session.subscribe((event) => {
2227
2228
  const observedAt = Date.now();
2228
- const mapped = mapSdkEvent(sessionId, event);
2229
+ if (event.type === "compaction_start") {
2230
+ activeCompaction = {
2231
+ compactionId: newCompactionId(sessionId),
2232
+ reason: event.reason,
2233
+ };
2234
+ }
2235
+ const compactionId = event.type === "compaction_start" || event.type === "compaction_end"
2236
+ ? activeCompaction?.compactionId ?? newCompactionId(sessionId)
2237
+ : undefined;
2238
+ const mapped = mapSdkEvent(sessionId, event, compactionId);
2239
+ if (event.type === "compaction_end")
2240
+ activeCompaction = undefined;
2229
2241
  if (mapped?.type === "error") {
2230
2242
  // Pi represents provider failures as an assistant message_end and may
2231
2243
  // retry internally. Hold the candidate until the SDK says the attempt
@@ -2237,8 +2249,15 @@ export class ConsolePiRuntime {
2237
2249
  }
2238
2250
  if (mapped?.type === "message_end")
2239
2251
  pendingProviderError = undefined;
2240
- if ((mapped?.type === "agent_end" && !mapped.willRetry) ||
2241
- mapped?.type === "agent_settled") {
2252
+ if (mapped?.type === "compaction_end" &&
2253
+ mapped.reason === "overflow" &&
2254
+ mapped.status === "completed" &&
2255
+ mapped.willRetry) {
2256
+ // The overflow error was a recoverable attempt fact. Pi has already
2257
+ // committed the compacted session and scheduled the continuation.
2258
+ pendingProviderError = undefined;
2259
+ }
2260
+ if (mapped?.type === "agent_settled") {
2242
2261
  emitPendingProviderError();
2243
2262
  }
2244
2263
  if (mapped?.type === "agent_start") {
@@ -2499,7 +2518,7 @@ function mapAssistantMessageEvent(type, sessionId, message) {
2499
2518
  ...(content.length > 0 ? { content } : {}),
2500
2519
  };
2501
2520
  }
2502
- function mapSdkEvent(sessionId, event) {
2521
+ function mapSdkEvent(sessionId, event, compactionId) {
2503
2522
  switch (event.type) {
2504
2523
  case "agent_start":
2505
2524
  return { type: "agent_start", sessionId };
@@ -2540,10 +2559,56 @@ function mapSdkEvent(sessionId, event) {
2540
2559
  };
2541
2560
  case "agent_settled":
2542
2561
  return { type: "agent_settled", sessionId };
2562
+ case "compaction_start":
2563
+ return {
2564
+ type: "compaction_start",
2565
+ sessionId,
2566
+ compactionId: compactionId ?? newCompactionId(sessionId),
2567
+ reason: event.reason,
2568
+ };
2569
+ case "compaction_end": {
2570
+ const snapshot = event.result
2571
+ ? projectCompactSnapshot(event.result)
2572
+ : undefined;
2573
+ return {
2574
+ type: "compaction_end",
2575
+ sessionId,
2576
+ compactionId: compactionId ?? newCompactionId(sessionId),
2577
+ reason: event.reason,
2578
+ status: event.aborted
2579
+ ? "aborted"
2580
+ : snapshot
2581
+ ? "completed"
2582
+ : "failed",
2583
+ ...(snapshot
2584
+ ? {
2585
+ summary: snapshot.summary,
2586
+ ...(snapshot.firstKeptEntryId
2587
+ ? { firstKeptEntryId: snapshot.firstKeptEntryId }
2588
+ : {}),
2589
+ ...(snapshot.tokensBefore === undefined
2590
+ ? {}
2591
+ : { tokensBefore: snapshot.tokensBefore }),
2592
+ ...(snapshot.estimatedTokensAfter === undefined
2593
+ ? {}
2594
+ : {
2595
+ estimatedTokensAfter: snapshot.estimatedTokensAfter,
2596
+ }),
2597
+ }
2598
+ : {}),
2599
+ willRetry: Boolean(event.willRetry),
2600
+ ...(event.errorMessage
2601
+ ? { errorMessage: redactRuntimeText(event.errorMessage, 600) }
2602
+ : {}),
2603
+ };
2604
+ }
2543
2605
  default:
2544
2606
  return undefined;
2545
2607
  }
2546
2608
  }
2609
+ function newCompactionId(sessionId) {
2610
+ return `${sessionId}:compaction:${randomBytes(8).toString("hex")}`;
2611
+ }
2547
2612
  async function waitForIdle(session, options) {
2548
2613
  // Default 45s — shorter than the previous 120s so a stuck SDK session does
2549
2614
  // not pin an HTTP connection for two full minutes. The SDK agent turn
@@ -12,6 +12,7 @@
12
12
  * Mutation routes go through the Console mutation gate (boot token + Origin).
13
13
  * Read routes (GET capabilities / sessions) are open to loopback.
14
14
  */
15
+ import { randomUUID } from "node:crypto";
15
16
  import { parseAssistantContent } from "./assistant-content.js";
16
17
  import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
17
18
  import { buildModelCallableToolSchemas } from "./tool-adapter.js";
@@ -54,7 +55,6 @@ function turnSummary(turn) {
54
55
  function isTerminalTurnState(state) {
55
56
  return state === "settled" || state === "aborted" || state === "failed";
56
57
  }
57
- const DEFAULT_ABORT_WAIT_MS = 2_500;
58
58
  /** Parse Chat's `<epoch>:<seq>` SSE cursor. Legacy `<sessionId>:<seq>` has
59
59
  * the same wire shape; its prefix intentionally fails the current process
60
60
  * epoch comparison and triggers safe snapshot recovery (M5.1). */
@@ -162,6 +162,40 @@ function turnEventToStorePartial(event) {
162
162
  };
163
163
  case "agent_settled":
164
164
  return { kind: "agent_settled", data: {} };
165
+ case "compaction_start":
166
+ case "compaction_end": {
167
+ const reason = event.reason === "threshold" || event.reason === "overflow"
168
+ ? event.reason
169
+ : "manual";
170
+ const status = event.type === "compaction_start"
171
+ ? "running"
172
+ : event.status === "aborted" || event.status === "failed"
173
+ ? event.status
174
+ : "completed";
175
+ const data = {
176
+ schemaVersion: 1,
177
+ compactionId: String(event.compactionId ?? ""),
178
+ reason,
179
+ status,
180
+ ...(typeof event.summary === "string"
181
+ ? { summary: event.summary }
182
+ : {}),
183
+ ...(typeof event.firstKeptEntryId === "string"
184
+ ? { firstKeptEntryId: event.firstKeptEntryId }
185
+ : {}),
186
+ ...(typeof event.tokensBefore === "number"
187
+ ? { tokensBefore: event.tokensBefore }
188
+ : {}),
189
+ ...(typeof event.estimatedTokensAfter === "number"
190
+ ? { estimatedTokensAfter: event.estimatedTokensAfter }
191
+ : {}),
192
+ willRetry: Boolean(event.willRetry),
193
+ ...(typeof event.errorMessage === "string"
194
+ ? { errorMessage: event.errorMessage }
195
+ : {}),
196
+ };
197
+ return { kind: event.type, data };
198
+ }
165
199
  case "error": {
166
200
  const content = parseAssistantContent(event.content);
167
201
  return {
@@ -726,9 +760,12 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
726
760
  sessionStats: telemetry.stats,
727
761
  usage: telemetry.usage,
728
762
  compactions: snapshot
729
- .filter((event) => event.kind === "compact")
763
+ .filter((event) => event.kind === "compact" ||
764
+ event.kind === "compaction_start" ||
765
+ event.kind === "compaction_end")
730
766
  .map((event) => ({
731
767
  eventId: event.eventId,
768
+ kind: event.kind,
732
769
  at: event.at,
733
770
  data: event.data,
734
771
  })),
@@ -1975,35 +2012,24 @@ export async function handleAbortChatTurn(req, res, deps, sessionId, turnId) {
1975
2012
  });
1976
2013
  return;
1977
2014
  }
1978
- deps.events.markAbortRequested?.(sessionId, turnId);
1979
- await deps.questionRegistry?.cancelForSession(sessionId, "TURN_ABORTED").catch(() => undefined);
2015
+ const stopping = deps.events.markAbortRequested?.(sessionId, turnId) ?? turn;
2016
+ // DSH-style acknowledgement: deliver the AbortSignal synchronously, then let
2017
+ // the detached Turn closeout own SDK/process cleanup and terminal persistence.
2018
+ // The HTTP request reports that Stop was accepted; it does not wait for Pi to
2019
+ // become idle. This keeps cancellation latency independent of slow providers,
2020
+ // tools and process-tree cleanup while preserving the active-Turn lease.
1980
2021
  if (!execution.controller.signal.aborted)
1981
2022
  execution.controller.abort();
1982
- const waited = await (deps.turnExecutions?.waitForCompletion(sessionId, turnId, DEFAULT_ABORT_WAIT_MS) ?? Promise.resolve({ status: "missing" }));
1983
- if (waited.status === "completed") {
1984
- const finalTurn = deps.events.getTurn(sessionId, turnId) ??
1985
- {
1986
- ...turn,
1987
- state: waited.outcome.state,
1988
- };
1989
- sendJson(res, 200, {
1990
- ok: true,
1991
- accepted: true,
1992
- idempotent: false,
1993
- turn: turnSummary(finalTurn),
1994
- });
1995
- return;
1996
- }
1997
- // Timeout or missing completion: keep non-terminal; client reconciles via /state.
1998
- const pending = deps.events.getTurn(sessionId, turnId) ?? turn;
2023
+ const questionCancellation = deps.questionRegistry?.cancelForSession(sessionId, "TURN_ABORTED");
2024
+ if (questionCancellation)
2025
+ void questionCancellation.catch(() => undefined);
2026
+ const acceptedTurn = deps.events.getTurn(sessionId, turnId) ?? stopping;
1999
2027
  sendJson(res, 202, {
2000
- ok: false,
2001
- accepted: false,
2002
- error: {
2003
- code: "TURN_ABORT_PENDING",
2004
- message: "abort requested; turn has not yet reached a terminal state",
2005
- },
2006
- turn: turnSummary(pending),
2028
+ ok: true,
2029
+ accepted: true,
2030
+ idempotent: false,
2031
+ cleanupPending: !isTerminalTurnState(acceptedTurn.state),
2032
+ turn: turnSummary(acceptedTurn),
2007
2033
  });
2008
2034
  }
2009
2035
  export async function handleGetChatTurn(_req, res, deps, sessionId, turnId) {
@@ -3487,13 +3513,57 @@ export async function handleChatCompact(req, res, deps, sessionId) {
3487
3513
  const rawInstructions = typeof body.instructions === "string" ? body.instructions : undefined;
3488
3514
  const currentTodos = projectCurrentTodos(deps.events.snapshot(sessionId));
3489
3515
  const customInstructions = buildTodoCompactionInstructions(rawInstructions, currentTodos);
3516
+ const turnId = deps.events.latestTurnId(sessionId) ?? `${sessionId}:compact`;
3517
+ const compactionId = `${sessionId}:compaction:${randomUUID()}`;
3518
+ deps.events.append(sessionId, turnId, {
3519
+ kind: "compaction_start",
3520
+ data: {
3521
+ schemaVersion: 1,
3522
+ compactionId,
3523
+ reason: "manual",
3524
+ status: "running",
3525
+ willRetry: false,
3526
+ },
3527
+ });
3490
3528
  try {
3491
3529
  const snapshot = await deps.runtime.compact(sessionId, customInstructions);
3492
- const event = deps.events.append(sessionId, deps.events.latestTurnId(sessionId) ?? `${sessionId}:compact`, { kind: "compact", data: snapshot });
3530
+ const event = deps.events.append(sessionId, turnId, {
3531
+ kind: "compaction_end",
3532
+ data: {
3533
+ schemaVersion: 1,
3534
+ compactionId,
3535
+ reason: "manual",
3536
+ status: "completed",
3537
+ summary: snapshot.summary,
3538
+ ...(snapshot.firstKeptEntryId
3539
+ ? { firstKeptEntryId: snapshot.firstKeptEntryId }
3540
+ : {}),
3541
+ ...(snapshot.tokensBefore === undefined
3542
+ ? {}
3543
+ : { tokensBefore: snapshot.tokensBefore }),
3544
+ ...(snapshot.estimatedTokensAfter === undefined
3545
+ ? {}
3546
+ : { estimatedTokensAfter: snapshot.estimatedTokensAfter }),
3547
+ willRetry: false,
3548
+ },
3549
+ });
3493
3550
  sendJson(res, 200, { ok: true, snapshot, eventId: event.eventId });
3494
3551
  }
3495
3552
  catch (error) {
3496
3553
  const classified = classifyChatCompactionFailure(error);
3554
+ const aborted = error instanceof Error &&
3555
+ (error.name === "AbortError" || /cancel(?:led|ed)|aborted/i.test(error.message));
3556
+ deps.events.append(sessionId, turnId, {
3557
+ kind: "compaction_end",
3558
+ data: {
3559
+ schemaVersion: 1,
3560
+ compactionId,
3561
+ reason: "manual",
3562
+ status: aborted ? "aborted" : "failed",
3563
+ willRetry: false,
3564
+ ...(aborted ? {} : { errorMessage: classified.message }),
3565
+ },
3566
+ });
3497
3567
  sendJson(res, classified.status, {
3498
3568
  ok: false,
3499
3569
  error: { code: classified.code, message: classified.message },
@@ -1,5 +1,70 @@
1
1
  export const ACTIVE_CHAT_SESSION_STORAGE_KEY = "loop-console.operator-chat.active-session.v1";
2
+ export const ACTIVE_CHAT_SESSION_BY_WORKSPACE_STORAGE_KEY = "loop-console.operator-chat.active-session-by-workspace.v1";
2
3
  export const DEFAULT_LANDING_STORAGE_KEY = "loop-console.operator-chat.default-landing.v1";
4
+ function readWorkspaceSessionMap(storage) {
5
+ try {
6
+ const parsed = JSON.parse(storage.getItem(ACTIVE_CHAT_SESSION_BY_WORKSPACE_STORAGE_KEY) ?? "{}");
7
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
8
+ return {};
9
+ return Object.fromEntries(Object.entries(parsed).filter((entry) => Boolean(entry[0].trim()) &&
10
+ typeof entry[1] === "string" &&
11
+ Boolean(entry[1].trim())));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ /** Resolve the selected Chat session from client state. The workspace map is
18
+ * authoritative; the former global key is only a safe, server-validated
19
+ * migration fallback for users upgrading from the single-selection layout. */
20
+ export function readActiveChatSessionId(storage, workspaceId) {
21
+ const scope = workspaceId?.trim();
22
+ if (scope) {
23
+ const map = readWorkspaceSessionMap(storage);
24
+ const scoped = map[scope]?.trim();
25
+ if (scoped)
26
+ return scoped;
27
+ // Once the map exists, an absent workspace means "no selected session";
28
+ // never fall back to another workspace's global legacy selection.
29
+ if (Object.keys(map).length > 0)
30
+ return null;
31
+ }
32
+ return storage.getItem(ACTIVE_CHAT_SESSION_STORAGE_KEY)?.trim() || null;
33
+ }
34
+ /** Remember the active session for its owning workspace while retaining the
35
+ * global key for existing non-Chat consumers that refer to the visible session. */
36
+ export function rememberActiveChatSession(storage, workspaceId, sessionId) {
37
+ const id = sessionId.trim();
38
+ if (!id)
39
+ return;
40
+ storage.setItem(ACTIVE_CHAT_SESSION_STORAGE_KEY, id);
41
+ const scope = workspaceId?.trim();
42
+ if (!scope)
43
+ return;
44
+ const map = readWorkspaceSessionMap(storage);
45
+ map[scope] = id;
46
+ storage.setItem(ACTIVE_CHAT_SESSION_BY_WORKSPACE_STORAGE_KEY, JSON.stringify(map));
47
+ }
48
+ /** Forget one workspace's invalid/deleted selection without disturbing the
49
+ * last session remembered for any other workspace. */
50
+ export function forgetActiveChatSession(storage, workspaceId, sessionId) {
51
+ const scope = workspaceId?.trim();
52
+ if (scope) {
53
+ const map = readWorkspaceSessionMap(storage);
54
+ if (!sessionId || map[scope] === sessionId)
55
+ delete map[scope];
56
+ if (Object.keys(map).length > 0) {
57
+ storage.setItem(ACTIVE_CHAT_SESSION_BY_WORKSPACE_STORAGE_KEY, JSON.stringify(map));
58
+ }
59
+ else {
60
+ storage.removeItem(ACTIVE_CHAT_SESSION_BY_WORKSPACE_STORAGE_KEY);
61
+ }
62
+ }
63
+ const global = storage.getItem(ACTIVE_CHAT_SESSION_STORAGE_KEY)?.trim();
64
+ if (!sessionId || global === sessionId) {
65
+ storage.removeItem(ACTIVE_CHAT_SESSION_STORAGE_KEY);
66
+ }
67
+ }
3
68
  function configuredLanding(value) {
4
69
  const normalized = value?.trim().toLowerCase();
5
70
  if (normalized === "chat")
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-DvbE1yQW.js";import{p as f}from"./chunk-JWPE2WC7-CcaZrq0k.js";import{_ as n,l as o}from"./mermaid.core-BYdXtke8.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CGRpxWNJ.js";import"./index-B4l5ag79.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-DrU-s-t7.js";import{p as f}from"./chunk-JWPE2WC7-BjLtNFrR.js";import{_ as n,l as o}from"./mermaid.core-DwPGvpCv.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BRzFqTaL.js";import"./index-DqJbO3-p.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-BYdXtke8.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-DwPGvpCv.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};