@tea-agent/loop-agent 0.42.0-next.6 → 0.42.0-next.8

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 (115) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/build-stamp.json +3 -3
  4. package/dist/executors/dag-pi-executor.js +74 -13
  5. package/dist/executors/shell-executor.js +3 -1
  6. package/dist/task/source-prepare/fragment-inventory.js +49 -16
  7. package/dist/task/source-prepare/prepare.js +37 -20
  8. package/dist/worker/console/chat/model-resolver.js +7 -4
  9. package/dist/worker/console/chat/pi-runtime.js +60 -5
  10. package/dist/worker/console/chat/routes.js +69 -46
  11. package/dist/worker/console/chat/sdd-data-alignment.js +6 -3
  12. package/dist/worker/console/chat/session-store.js +16 -0
  13. package/dist/worker/console/pi-readiness.js +3 -2
  14. package/dist/worker/console/server.js +19 -1
  15. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BJpsGgAv.js → abnfDiagram-N423BO3Z-C6n9_m0P.js} +1 -1
  16. package/dist/worker/console/static/assets/{arc-DjMFPsEd.js → arc-DQh-IfZ1.js} +1 -1
  17. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DqdZlr1O.js → architectureDiagram-T3A2C74G-54NnrwUC.js} +1 -1
  18. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BmZnfFEO.js → blockDiagram-VBNYF7ZC-pivRALGK.js} +1 -1
  19. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-3cKberpL.js → c4Diagram-5PPSVZJV-BR7OV2NJ.js} +1 -1
  20. package/dist/worker/console/static/assets/channel-DsZxrgqe.js +1 -0
  21. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-q-0yjQ1h.js → chunk-2GRJ4B5K-B1Aq6BcK.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DnONBaDW.js → chunk-2Q5K7J3B-CbdU0rjo.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-v3a6PCOS.js → chunk-5RXB4S5H-Dqi2GJeD.js} +1 -1
  24. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D1kLrlU_.js → chunk-5VM5RSS4-BYn1Hu4R.js} +1 -1
  25. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BPelzihi.js → chunk-6Q2QTUOP-DRLjDL0k.js} +1 -1
  26. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DauNE69H.js → chunk-GF5L2VYU-CY9Xx2jV.js} +1 -1
  27. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-RIg5_bFJ.js → chunk-JWPE2WC7-BNCZs7_z.js} +1 -1
  28. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BuILuoxs.js → chunk-KBJHAD2P-DZ8AStLL.js} +1 -1
  29. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-VHORV5oW.js → chunk-RYQCIY6F-DsxjYrzz.js} +1 -1
  30. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BJYr4xHC.js → chunk-XXDRQBXY-Ddx1KC1l.js} +1 -1
  31. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +1 -0
  32. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +1 -0
  33. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BS6nuqQn.js → cose-bilkent-JH36ORCC-W9TveCnK.js} +1 -1
  34. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BBhF0NAT.js → cynefin-VYW2F7L2-l9j_ztZH.js} +1 -1
  35. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D1135Ifg.js → cynefinDiagram-MW4NZA55-BMJMKi4G.js} +1 -1
  36. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-vAOYPUWJ.js → dagre-VZM6K2ZE-Bb4mG9pH.js} +1 -1
  37. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DuaSPkfa.js → diagram-7IWD3JNH-BMgL_Qv0.js} +1 -1
  38. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-mMvXvGU6.js → diagram-B4RE2ZJO-Bvo2T4OQ.js} +1 -1
  39. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CDIEJtlL.js → diagram-LBJQPF4R-_5kWRN9c.js} +1 -1
  40. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Bi_6JvCW.js → diagram-Q27KOJAE-DqdMrltM.js} +1 -1
  41. package/dist/worker/console/static/assets/{diagram-UB23O5K3-DAqj57yN.js → diagram-UB23O5K3-CDDYsNkp.js} +1 -1
  42. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DfWaxNEo.js → ebnfDiagram-BXEA7PRR-c4nfnZUV.js} +1 -1
  43. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt7QqkNG.js → erDiagram-JOGREHBK-DnkUcNMs.js} +1 -1
  44. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CRRhQwT2.js → flowDiagram-UKHOOZJN-Dz0KGLZE.js} +1 -1
  45. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DAV1O5B6.js → ganttDiagram-PKOTCBZU-Cj1t1uka.js} +1 -1
  46. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-0X2Qo4Lp.js → gitGraphDiagram-DS77QQ5N-tp2FrBHd.js} +1 -1
  47. package/dist/worker/console/static/assets/index-D9Sc0f0y.js +449 -0
  48. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C5W5eILC.js → infoDiagram-6WML65LV-DQwJS8DH.js} +1 -1
  49. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-5SfqU-Tb.js → ishikawaDiagram-WSZJBQD7-eimCQWD4.js} +1 -1
  50. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CKDRcaKf.js → journeyDiagram-NVQOT4AX-Dd9Gbwgv.js} +1 -1
  51. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cc7_TuSb.js → kanban-definition-27J2QSJJ-Qve3cyft.js} +1 -1
  52. package/dist/worker/console/static/assets/{linear-BcQYYmeT.js → linear-UXKSe36Z.js} +1 -1
  53. package/dist/worker/console/static/assets/{mermaid.core-CwLZPtQo.js → mermaid.core-CWhj4JXN.js} +5 -5
  54. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D29M025z.js → mindmap-definition-FAOFIHXS-yTcwf4SJ.js} +1 -1
  55. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CV-ZAWYe.js → pegDiagram-VL7TDLO6-_7qToaZP.js} +1 -1
  56. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C6iPS_o5.js → pieDiagram-7S7Q4E2Y-CXmrKmDm.js} +1 -1
  57. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BFCD1sTg.js → quadrantDiagram-CIZ2JOQS-hMmrJyaS.js} +1 -1
  58. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CztNj4O9.js → railroadDiagram-AXF67PYL-BXw8tCBe.js} +1 -1
  59. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BWIELSNd.js → requirementDiagram-LRYGKXZP-BmjgnLZl.js} +1 -1
  60. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CPtxrjJQ.js → sankeyDiagram-W5VNT64P-Bj77SPpN.js} +1 -1
  61. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BEWVSFsu.js → sequenceDiagram-SI44F4Z6-B2FDVysL.js} +1 -1
  62. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DyhzoX_J.js → sizeCapture-X5ZJPWSS-UChNY9ZZ.js} +1 -1
  63. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-xLox0nu7.js → stateDiagram-OKZ733FA-wbAEkbd7.js} +1 -1
  64. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +1 -0
  65. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CLC_ow9A.js → swimlanes-SLNWSIFB-DCEkU8HR.js} +2 -2
  66. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +8 -0
  67. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DmJZ4uyY.js → timeline-definition-Z64GVDOM-BGp7H06U.js} +1 -1
  68. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DD7GGi9Z.js → vennDiagram-T6HMQDX7-DsCIQuWR.js} +1 -1
  69. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bi8PQoGt.js → wardleyDiagram-T6FBY63Y-cRZLP4Xe.js} +1 -1
  70. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-uKdAY8Dp.js → xychartDiagram-ELKLHX3M-CP0xLR9Y.js} +1 -1
  71. package/dist/worker/console/static/index.html +1 -1
  72. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  73. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  74. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
  75. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +31 -2
  76. package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -3
  77. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
  78. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
  79. package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
  80. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  81. package/dist/worker/observe/static/dag-helpers.js +4 -2
  82. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  83. package/dist/worker/observe/static/kpi.js +2 -2
  84. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  85. package/dist/worker/observe/static/operator-chrome.js +37 -23
  86. package/dist/worker/observe/static/relations.js +2 -2
  87. package/dist/worker/observe/static/router.d.ts +12 -1
  88. package/dist/worker/observe/static/router.js +48 -6
  89. package/dist/worker/observe/static/run-processing.js +4 -2
  90. package/dist/worker/observe/static/shell-chrome.js +2 -2
  91. package/dist/worker/observe/static/state.js +5 -0
  92. package/dist/worker/observe/static/styles.css +95 -0
  93. package/dist/worker/observe/static/views/dag-inspector.js +14 -7
  94. package/dist/worker/observe/static/views/dag.js +10 -2
  95. package/dist/worker/observe/static/views/dags.js +3 -1
  96. package/dist/worker/observe/static/views/dashboard.js +14 -6
  97. package/dist/worker/observe/static/views/pool.js +7 -2
  98. package/dist/worker/observe/static/views/run.js +12 -3
  99. package/dist/worker/observe/static/views/session-timeline.js +69 -10
  100. package/dist/worker/observe/static/views/task.js +7 -2
  101. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +40 -9
  102. package/dist/workflows/dag/backend-test-scenario-param.js +126 -19
  103. package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
  104. package/dist/workflows/dag/backend-test-writer-completeness.js +7 -3
  105. package/dist/workflows/dag/frontend-shadow-dual-write.js +33 -6
  106. package/dist/workflows/dag/init-hybrid.js +175 -27
  107. package/dist/workflows/dag/node-execution.js +1 -1
  108. package/dist/workflows/dag/types.js +1 -1
  109. package/package.json +3 -2
  110. package/dist/worker/console/static/assets/channel-DWYwciiv.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D6jOf_l4.js +0 -1
  112. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D6jOf_l4.js +0 -1
  113. package/dist/worker/console/static/assets/index-CVdddQsA.js +0 -451
  114. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DS5ZRvJG.js +0 -1
  115. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Dvpxn-bf.js +0 -8
@@ -13,6 +13,7 @@
13
13
  * Read routes (GET capabilities / sessions) are open to loopback.
14
14
  */
15
15
  import { randomUUID } from "node:crypto";
16
+ import { access } from "node:fs/promises";
16
17
  import { parseAssistantContent } from "./assistant-content.js";
17
18
  import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
18
19
  import { buildModelCallableToolSchemas } from "./tool-adapter.js";
@@ -474,7 +475,7 @@ function sendRuntimeUnavailable(res, error) {
474
475
  * handled here — only reopen/init readiness failures.
475
476
  */
476
477
  function mapReopenSessionFailure(reopen) {
477
- if (reopen.code === "NO_SESSION_FILE") {
478
+ if (reopen.code === "NO_SESSION_FILE" || reopen.code === "SESSION_FILE_MISSING") {
478
479
  return {
479
480
  status: 404,
480
481
  code: "PI_SESSION_NOT_FOUND",
@@ -494,6 +495,19 @@ function mapReopenSessionFailure(reopen) {
494
495
  message: reopen.message,
495
496
  };
496
497
  }
498
+ /** Drop an orphan catalog record whose promised Pi JSONL is gone. */
499
+ async function healMissingSessionFile(deps, sessionId, reopen) {
500
+ if (reopen && reopen.code !== "NO_SESSION_FILE" && reopen.code !== "SESSION_FILE_MISSING") {
501
+ return;
502
+ }
503
+ if (typeof deps.store.remove !== "function")
504
+ return;
505
+ await deps.store.remove(sessionId).catch(() => undefined);
506
+ }
507
+ async function mapFailedReopen(deps, sessionId, reopen) {
508
+ await healMissingSessionFile(deps, sessionId, reopen);
509
+ return mapReopenSessionFailure(reopen);
510
+ }
497
511
  export async function handleChatCapabilities(_req, res, deps) {
498
512
  const toolSchemas = buildModelCallableToolSchemas();
499
513
  sendJson(res, 200, {
@@ -561,9 +575,15 @@ workspaceId) {
561
575
  });
562
576
  return;
563
577
  }
578
+ let createdSessionId;
579
+ let persisted = false;
564
580
  try {
565
581
  const snapshot = snapshotInput === undefined ? undefined : projectCompactSnapshot(snapshotInput);
566
- // Fast path: durable shell + Console record before full AgentSession ready.
582
+ // A Console record promises that its Pi JSONL can be reopened after a
583
+ // Console restart. Do not make that promise until Pi has finished
584
+ // materializing the session: the old fast path wrote a record for a
585
+ // future JSONL path, leaving an unrecoverable orphan when init failed or
586
+ // the process restarted in that window.
567
587
  const handle = await deps.runtime.createSession({
568
588
  ...(provider && modelId ? { model: { provider, modelId } } : {}),
569
589
  ...(snapshot
@@ -583,40 +603,26 @@ workspaceId) {
583
603
  });
584
604
  return;
585
605
  }
606
+ createdSessionId = handle.sessionId;
607
+ await deps.runtime.ensureSessionReady(handle.sessionId);
608
+ const selection = deps.runtime.getRuntimeSelection(handle.sessionId);
586
609
  const session = await deps.store.create({
587
610
  sessionId: handle.sessionId,
588
611
  sessionFile: handle.sessionFile,
589
- modelProvider: handle.model?.provider,
590
- modelId: handle.model?.modelId,
612
+ modelProvider: selection.model?.provider ?? handle.model?.provider,
613
+ modelId: selection.model?.modelId ?? handle.model?.modelId,
591
614
  // AC-R7-002: explicit parameter from the dispatcher — the runtime
592
615
  // store.create wrapper is gone; body workspaceId (if any) only acts as
593
616
  // a fallback for direct (non-dispatcher) handler tests.
594
617
  workspaceId: workspaceId ?? (typeof body.workspaceId === "string" && body.workspaceId ? body.workspaceId : undefined),
595
618
  });
619
+ persisted = true;
596
620
  await deps.attachSession?.(handle.sessionId).catch(() => undefined);
597
621
  if (snapshot)
598
622
  deps.events.append(handle.sessionId, `${handle.sessionId}:snapshot`, {
599
623
  kind: "compact",
600
624
  data: snapshot,
601
625
  });
602
- // Best-effort: after materialization, patch resolved default model onto the
603
- // durable record. Failures stay on the init fence (PI_SESSION_INIT_FAILED).
604
- if (typeof deps.runtime.ensureSessionReady === "function") {
605
- void deps.runtime
606
- .ensureSessionReady(handle.sessionId)
607
- .then(async () => {
608
- if (typeof deps.runtime.getRuntimeSelection !== "function")
609
- return;
610
- const selection = deps.runtime.getRuntimeSelection(handle.sessionId);
611
- if (!selection?.model)
612
- return;
613
- await deps.store.setRuntimeSelection(handle.sessionId, {
614
- modelProvider: selection.model.provider,
615
- modelId: selection.model.modelId,
616
- });
617
- })
618
- .catch(() => undefined);
619
- }
620
626
  sendJson(res, 201, {
621
627
  ok: true,
622
628
  sessionId: handle.sessionId,
@@ -625,14 +631,23 @@ workspaceId) {
625
631
  session: { ...session, isBlank: isBlankChatSession(session) },
626
632
  activeTools: handle.activeTools,
627
633
  hasBash: true,
628
- ...(handle.initializing ? { initializing: true } : {}),
629
634
  });
630
635
  }
631
636
  catch (error) {
637
+ // Before store.create succeeds, this handle has no durable owner. Dispose
638
+ // it so a failed create cannot survive in-memory until the next restart.
639
+ if (createdSessionId && !persisted)
640
+ deps.runtime.dispose(createdSessionId);
632
641
  const message = error instanceof Error ? error.message : String(error);
633
- sendJson(res, 500, {
642
+ const code = error && typeof error === "object" && "code" in error
643
+ ? String(error.code ?? "")
644
+ : "";
645
+ sendJson(res, code === "PI_SESSION_INIT_FAILED" ? 503 : 500, {
634
646
  ok: false,
635
- error: { code: "CHAT_SESSION_CREATE_FAILED", message },
647
+ error: {
648
+ code: code === "PI_SESSION_INIT_FAILED" ? code : "CHAT_SESSION_CREATE_FAILED",
649
+ message,
650
+ },
636
651
  });
637
652
  }
638
653
  }
@@ -737,10 +752,7 @@ export async function handleBranchChatSession(req, res, deps, sessionId) {
737
752
  sessionFile: record.sessionFile,
738
753
  });
739
754
  if (!reopened.ok) {
740
- const mapped = mapReopenSessionFailure({
741
- code: reopened.code,
742
- message: reopened.message,
743
- });
755
+ const mapped = await mapFailedReopen(deps, sessionId, reopened);
744
756
  sendJson(res, mapped.status, {
745
757
  ok: false,
746
758
  error: { code: mapped.code, message: mapped.message },
@@ -815,6 +827,29 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
815
827
  });
816
828
  return;
817
829
  }
830
+ if (record.sessionFile) {
831
+ try {
832
+ await access(record.sessionFile);
833
+ }
834
+ catch (error) {
835
+ const code = error && typeof error === "object" && "code" in error
836
+ ? String(error.code ?? "")
837
+ : "";
838
+ if (code === "ENOENT") {
839
+ await healMissingSessionFile(deps, sessionId, {
840
+ code: "NO_SESSION_FILE",
841
+ });
842
+ sendJson(res, 404, {
843
+ ok: false,
844
+ error: {
845
+ code: "PI_SESSION_NOT_FOUND",
846
+ message: `Pi session file missing or not openable: ${record.sessionFile}`,
847
+ },
848
+ });
849
+ return;
850
+ }
851
+ }
852
+ }
818
853
  await deps.operationLinker.recoverSession(sessionId);
819
854
  const composerDraft = await new ComposerDraftStore(deps.appData).get(sessionId);
820
855
  const snapshot = deps.events.snapshot(sessionId);
@@ -1493,10 +1528,7 @@ export async function handleReopenChatSession(req, res, deps, sessionId) {
1493
1528
  : {}),
1494
1529
  });
1495
1530
  if (!result.ok) {
1496
- const mapped = mapReopenSessionFailure({
1497
- code: result.code,
1498
- message: result.message,
1499
- });
1531
+ const mapped = await mapFailedReopen(deps, sessionId, result);
1500
1532
  sendJson(res, mapped.status, {
1501
1533
  ok: false,
1502
1534
  error: { code: mapped.code, message: mapped.message },
@@ -1582,10 +1614,7 @@ export async function handleChatPrompt(req, res, deps, sessionId) {
1582
1614
  : {}),
1583
1615
  });
1584
1616
  if (!reopen.ok) {
1585
- const mapped = mapReopenSessionFailure({
1586
- code: reopen.code,
1587
- message: reopen.message,
1588
- });
1617
+ const mapped = await mapFailedReopen(deps, sessionId, reopen);
1589
1618
  sendJson(res, mapped.status, {
1590
1619
  ok: false,
1591
1620
  error: { code: mapped.code, message: mapped.message },
@@ -1854,10 +1883,7 @@ export async function handleCreateChatTurn(req, res, deps, sessionId, trustedPay
1854
1883
  : {}),
1855
1884
  });
1856
1885
  if (!reopen.ok) {
1857
- const mapped = mapReopenSessionFailure({
1858
- code: reopen.code,
1859
- message: reopen.message,
1860
- });
1886
+ const mapped = await mapFailedReopen(deps, sessionId, reopen);
1861
1887
  sendJson(res, mapped.status, {
1862
1888
  ok: false,
1863
1889
  accepted: false,
@@ -3049,7 +3075,7 @@ export async function ensureActiveSession(deps, sessionId) {
3049
3075
  : {}),
3050
3076
  });
3051
3077
  if (!reopened.ok) {
3052
- return { ok: false, ...mapReopenSessionFailure(reopened) };
3078
+ return { ok: false, ...(await mapFailedReopen(deps, sessionId, reopened)) };
3053
3079
  }
3054
3080
  return { ok: true };
3055
3081
  }
@@ -3996,10 +4022,7 @@ export async function handleChatCompact(req, res, deps, sessionId) {
3996
4022
  : {}),
3997
4023
  });
3998
4024
  if (!reopened.ok) {
3999
- const mapped = mapReopenSessionFailure({
4000
- code: reopened.code,
4001
- message: reopened.message,
4002
- });
4025
+ const mapped = await mapFailedReopen(deps, sessionId, reopened);
4003
4026
  sendJson(res, mapped.status, {
4004
4027
  ok: false,
4005
4028
  error: { code: mapped.code, message: mapped.message },
@@ -203,10 +203,13 @@ export async function injectWizardLocalHeaders(options) {
203
203
  return;
204
204
  if (!(await options.service.isEligible()))
205
205
  return;
206
- const getAuth = options.context.modelRegistry?.getApiKeyAndHeaders;
207
- if (!getAuth)
206
+ const modelRegistry = options.context.modelRegistry;
207
+ if (!modelRegistry?.getApiKeyAndHeaders)
208
208
  return;
209
- const auth = await getAuth(model);
209
+ // Keep the method call attached to ModelRegistry. Its implementation reads
210
+ // the private runtime through `this`; extracting the function would make the
211
+ // SDK path fail closed and silently drop all custom headers.
212
+ const auth = await modelRegistry.getApiKeyAndHeaders(model);
210
213
  if (!auth.ok || typeof auth.apiKey !== "string" || !auth.apiKey)
211
214
  return;
212
215
  setStaticHeader(options.headers, auth.headers, "client-code", "OpenCode");
@@ -47,6 +47,16 @@ export function cleanChatTitle(value) {
47
47
  function recordPath(appData, sessionId) {
48
48
  return path.join(appData.chats, `${sessionId}.json`);
49
49
  }
50
+ /** True when `sessionFile` lives inside this store's dedicated Pi JSONL dir. */
51
+ export function isConsoleOwnedSessionFile(appData, sessionFile) {
52
+ const value = sessionFile?.trim();
53
+ if (!value)
54
+ return false;
55
+ const root = path.resolve(appData.chatSessions);
56
+ const target = path.resolve(value);
57
+ const relative = path.relative(root, target);
58
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
59
+ }
50
60
  export class ChatSessionStore {
51
61
  appData;
52
62
  options;
@@ -263,8 +273,14 @@ export class ChatSessionStore {
263
273
  const record = await this.get(sessionId);
264
274
  if (!record)
265
275
  return false;
276
+ const sessionFile = record.sessionFile;
266
277
  await rm(recordPath(this.appData, sessionId), { force: true });
267
278
  this.options.onRecordChange?.(sessionId, undefined);
279
+ // Record first, then best-effort JSONL unlink. Final consistency is
280
+ // "both gone" — a leftover JSONL is harmless, a leftover record is not.
281
+ if (isConsoleOwnedSessionFile(this.appData, sessionFile) && sessionFile) {
282
+ await rm(sessionFile, { force: true }).catch(() => undefined);
283
+ }
268
284
  return true;
269
285
  });
270
286
  }
@@ -170,11 +170,12 @@ export async function discoverPiReadinessProbe(options) {
170
170
  const availableCount = countAvailableModels(snapshot);
171
171
  const providerConfigured = hasProviderConfig(snapshot);
172
172
  const availableModels = mapSnapshotAvailableModels(snapshot);
173
+ const credentialedAvailableCount = availableModels.filter((model) => model.hasCredentials !== false).length;
173
174
  const harnessMatrix = await resolveHarnessPiExecutorMatrix(repoRoot, availableModels);
174
175
  const resolvedHarnessModels = [];
175
176
  if (typeof runtime.getModel === "function") {
176
177
  for (const row of harnessMatrix) {
177
- if (!row.provider)
178
+ if (!row.provider || !row.resolved)
178
179
  continue;
179
180
  try {
180
181
  if (runtime.getModel(row.provider, row.modelId)) {
@@ -195,7 +196,7 @@ export async function discoverPiReadinessProbe(options) {
195
196
  .join(",")
196
197
  : "none";
197
198
  const authReady = availableCount > 0 || providerConfigured;
198
- const modelsAvailable = availableCount > 0 || resolvedHarnessModels.length > 0;
199
+ const modelsAvailable = credentialedAvailableCount > 0 || resolvedHarnessModels.length > 0;
199
200
  let interviewSessionOk = false;
200
201
  let sessionMessage;
201
202
  if (!authReady || !modelsAvailable) {
@@ -567,8 +567,26 @@ export async function createConsoleServer(options) {
567
567
  const stored = await context.chatRouteDeps.store.get(sessionId);
568
568
  if (stored)
569
569
  owner = context;
570
+ else if (!catalogEntry) {
571
+ // Catalog and store both miss: the session was deleted, not
572
+ // retargeted. Do not scan other workspaces or leak existence.
573
+ sendJson(res, 404, {
574
+ ok: false,
575
+ error: {
576
+ code: "PI_SESSION_NOT_FOUND",
577
+ message: `chat session not found: ${sessionId}`,
578
+ },
579
+ });
580
+ return;
581
+ }
570
582
  else {
571
- // Do not scan another workspace and disclose whether the id exists there.
583
+ chatSessionCatalog.remove(sessionId);
584
+ await registry
585
+ .detachSession(ownerRecord.workspaceId, sessionId)
586
+ .catch(() => undefined);
587
+ // Catalog had the id, but this workspace store does not.
588
+ // Keep the conflict code so a mismatched selection cannot
589
+ // discover whether the session lives elsewhere.
572
590
  sendJson(res, 403, {
573
591
  ok: false,
574
592
  error: {
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-BPelzihi.js";import{p as f}from"./chunk-JWPE2WC7-RIg5_bFJ.js";import{_ as n,l as o}from"./mermaid.core-CwLZPtQo.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BBhF0NAT.js";import"./index-CVdddQsA.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-DRLjDL0k.js";import{p as f}from"./chunk-JWPE2WC7-BNCZs7_z.js";import{_ as n,l as o}from"./mermaid.core-CWhj4JXN.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-l9j_ztZH.js";import"./index-D9Sc0f0y.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-CwLZPtQo.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-CWhj4JXN.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};