@tea-agent/loop-agent 0.41.1 → 0.41.2

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 (72) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/build-stamp.json +3 -3
  3. package/dist/executors/pi-extension-resolver.js +14 -2
  4. package/dist/worker/console/chat/pi-runtime.js +60 -5
  5. package/dist/worker/console/chat/routes.js +44 -22
  6. package/dist/worker/console/chat/session-store.js +16 -0
  7. package/dist/worker/console/server.js +19 -1
  8. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D7oU983t.js → abnfDiagram-N423BO3Z-DQ5jlYVU.js} +1 -1
  9. package/dist/worker/console/static/assets/{arc-BrKSRdFS.js → arc-BeTVCzH9.js} +1 -1
  10. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CiXV7L56.js → architectureDiagram-T3A2C74G-DassREBi.js} +1 -1
  11. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D_Hs_gIG.js → blockDiagram-VBNYF7ZC-DyeNX8Ek.js} +1 -1
  12. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DbM-Lqqh.js → c4Diagram-5PPSVZJV-BXb_zxGx.js} +1 -1
  13. package/dist/worker/console/static/assets/channel-B_lofstT.js +1 -0
  14. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D1M8uqJw.js → chunk-2GRJ4B5K-B9F9gxGC.js} +1 -1
  15. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-4D3RFHT6.js → chunk-2Q5K7J3B-Byd5zHEQ.js} +1 -1
  16. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CdwpY_n9.js → chunk-5RXB4S5H-BqoxMJk9.js} +1 -1
  17. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BLeLZ9PG.js → chunk-5VM5RSS4-CT7d6YZw.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CeRNEMvc.js → chunk-6Q2QTUOP-CdiNyN0N.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjkHP8Vy.js → chunk-GF5L2VYU-BGw9bpVc.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B54ZUQY-.js → chunk-JWPE2WC7-DiDVlVB1.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-4bKaMVmx.js → chunk-KBJHAD2P-BhHf-4yK.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cyjfznu_.js → chunk-RYQCIY6F-B79pEgwv.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CDN-zGFV.js → chunk-XXDRQBXY-Cb70EcUV.js} +1 -1
  24. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-ieUI0-eY.js +1 -0
  25. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-ieUI0-eY.js +1 -0
  26. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-HxJmz411.js → cose-bilkent-JH36ORCC-DBHLnAf1.js} +1 -1
  27. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DtSt5K2w.js → cynefin-VYW2F7L2-BT_JRsOH.js} +1 -1
  28. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-VtkSpRmG.js → cynefinDiagram-MW4NZA55-Bss-msa3.js} +1 -1
  29. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bj4DT4du.js → dagre-VZM6K2ZE-B8Gr24pt.js} +1 -1
  30. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Bpuk6BmP.js → diagram-7IWD3JNH-BrjU70Jw.js} +1 -1
  31. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BHYGv_yO.js → diagram-B4RE2ZJO-Cp_JpPGd.js} +1 -1
  32. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DEx3WwnT.js → diagram-LBJQPF4R-BWKICEP-.js} +1 -1
  33. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cj0dS5e8.js → diagram-Q27KOJAE-D3z_UNR_.js} +1 -1
  34. package/dist/worker/console/static/assets/{diagram-UB23O5K3-B97h8l21.js → diagram-UB23O5K3-_Ywm390x.js} +1 -1
  35. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DMSFAhBd.js → ebnfDiagram-BXEA7PRR-D9QYQQMB.js} +1 -1
  36. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DhVOCbt6.js → erDiagram-JOGREHBK-BNgXNKAr.js} +1 -1
  37. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-GlakITWL.js → flowDiagram-UKHOOZJN-QRLnkPCg.js} +1 -1
  38. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-VxPpu9eQ.js → ganttDiagram-PKOTCBZU-B9fOduNi.js} +1 -1
  39. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js → gitGraphDiagram-DS77QQ5N-FSEdtPN2.js} +1 -1
  40. package/dist/worker/console/static/assets/{index-BLzcAykx.js → index-DtPrwu7M.js} +71 -71
  41. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BXYuU4Uw.js → infoDiagram-6WML65LV-D5Bjg9dm.js} +1 -1
  42. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C34erCbU.js → ishikawaDiagram-WSZJBQD7-BlNRkWvf.js} +1 -1
  43. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DZCAiOU3.js → journeyDiagram-NVQOT4AX-BdJiw3SL.js} +1 -1
  44. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C9gaTdUm.js → kanban-definition-27J2QSJJ-CsDNlYht.js} +1 -1
  45. package/dist/worker/console/static/assets/{linear-CvI0Z17U.js → linear-8g9dbEmX.js} +1 -1
  46. package/dist/worker/console/static/assets/{mermaid.core-DMMSIx7j.js → mermaid.core-ChyQH_ut.js} +5 -5
  47. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BKKP0EO6.js → mindmap-definition-FAOFIHXS-DwYoCTYt.js} +1 -1
  48. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-C3LiFbl5.js → pegDiagram-VL7TDLO6-T_b2rS17.js} +1 -1
  49. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js → pieDiagram-7S7Q4E2Y-1Cr7WMhH.js} +1 -1
  50. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DNthMjzc.js → quadrantDiagram-CIZ2JOQS-Bq4qM59o.js} +1 -1
  51. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-miFsr4Tv.js → railroadDiagram-AXF67PYL-ytFgR1RL.js} +1 -1
  52. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-yXUncmn6.js → requirementDiagram-LRYGKXZP-CnUdUVYX.js} +1 -1
  53. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DLeWKLak.js → sankeyDiagram-W5VNT64P-_5rJqG6R.js} +1 -1
  54. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CI3he1R3.js → sequenceDiagram-SI44F4Z6-D5imyGbt.js} +1 -1
  55. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B7jfDkS5.js → sizeCapture-X5ZJPWSS-DvgDn3wL.js} +1 -1
  56. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BmrZod7J.js → stateDiagram-OKZ733FA-BViu7duc.js} +1 -1
  57. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-kNxnG4_w.js +1 -0
  58. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CDOlHVSY.js → swimlanes-SLNWSIFB-BcULKg2P.js} +2 -2
  59. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DyTzlcWH.js +8 -0
  60. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cz-XX1gy.js → timeline-definition-Z64GVDOM-DmPveIjt.js} +1 -1
  61. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-caV4cI8C.js → vennDiagram-T6HMQDX7-9r-iFy0R.js} +1 -1
  62. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-dImPlnIO.js → wardleyDiagram-T6FBY63Y-D3fIXdvM.js} +1 -1
  63. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CtRYVJcn.js → xychartDiagram-ELKLHX3M-BCSS9SBg.js} +1 -1
  64. package/dist/worker/console/static/index.html +1 -1
  65. package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
  66. package/dist/worker/observe/routes.js +4 -0
  67. package/package.json +1 -1
  68. package/dist/worker/console/static/assets/channel-D1xajGpe.js +0 -1
  69. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +0 -1
  70. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +0 -1
  71. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +0 -1
  72. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +0 -8
package/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [0.41.1] - 2026-09-02
5
+ ## [0.41.2] - 2026-09-04
6
6
 
7
7
  ### 新增
8
8
 
@@ -16,6 +16,20 @@
16
16
 
17
17
  - 修复了影响使用体验或运行稳定性的问题。
18
18
 
19
+ ## [0.41.1] - 2026-09-02
20
+
21
+ ### 修复
22
+
23
+ - 修复 Next Release 发布 workflow 的 io-heavy 测试并发不稳定:将该测试池固定为单 worker,避免全部测试通过后因过高并发触发 Vitest `onTaskUpdate` RPC settlement timeout,并消除 controller identity / package fingerprint 测试之间的共享状态竞态。
24
+ - 修复 Operator Chat 旧会话继续使用过期模型认证的问题:模型配置变化后,下一轮发送或手动重新加载会自动刷新模型目录和当前模型认证。
25
+ - 修复 Operator Chat 长工具链的消息错序:模型过程文字、思考和工具卡片现在按服务端事件顺序稳定交替展示,刷新历史会话后也保持相同顺序。
26
+ - 统一工作区、Todo 和其他滚动区域的滚动条样式,默认更轻巧,鼠标悬浮时也更容易辨认。
27
+ - HTML 文件预览现在支持页面内的标签切换等交互;较大的自包含 HTML 可以在新窗口中直接完整加载,不再受源码摘要长度限制。
28
+
29
+ ### 测试
30
+
31
+ - `harness.json` Pi MED 档测试不再 pin 具体模型 id,改为校验 tier 形状,避免每次 dogfood 档位切换都造成测试失败。
32
+
19
33
  ## [0.41.0] - 2026-08-31
20
34
 
21
35
  ### 新增
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.41.1",
4
- "gitSha": "d5dec271d8ae9c579c423d9ea7b98fb0270baa5a",
5
- "builtAt": "2026-09-02T04:40:44.741Z"
3
+ "version": "0.41.2",
4
+ "gitSha": "25160b4bb4a3933642d33e6d00d860d31c6b370b",
5
+ "builtAt": "2026-09-04T11:09:28.745Z"
6
6
  }
@@ -62,9 +62,21 @@ function readSettingsPackages(settingsPath) {
62
62
  return undefined;
63
63
  }
64
64
  }
65
- /** Strip an `npm:` prefix from a settings package spec, e.g. `npm:pi-lens`. */
65
+ /**
66
+ * Strip an `npm:` prefix and a trailing `@<version>`/`@<dist-tag>` suffix
67
+ * from a settings package spec, e.g. `npm:pi-lens@3.8.72` → `pi-lens`,
68
+ * `npm:@scope/pkg@1.0.0` → `@scope/pkg`. The leading `@` of a scoped
69
+ * package name is preserved (the version separator is the LAST `@` at a
70
+ * positive index). Mirrors the console-side spec parsing semantics so a
71
+ * version-pinned install (e.g. via `pi install npm:pi-lens@3.8.72`) resolves
72
+ * identically in both surfaces.
73
+ */
66
74
  export function packageNameFromSpec(spec) {
67
- return spec.startsWith("npm:") ? spec.slice(4) : spec;
75
+ const base = spec.startsWith("npm:") ? spec.slice(4) : spec;
76
+ const at = base.lastIndexOf("@");
77
+ if (at > 0)
78
+ return base.slice(0, at);
79
+ return base;
68
80
  }
69
81
  function loadPackageInventories(repoRoot) {
70
82
  const agentDir = getPiAgentDir();
@@ -26,7 +26,7 @@
26
26
  * `createDefaultPiSdkBindings()`.
27
27
  */
28
28
  import { createHash, randomBytes } from "node:crypto";
29
- import { access, stat } from "node:fs/promises";
29
+ import { access, mkdir, rm, stat, writeFile } from "node:fs/promises";
30
30
  import path from "node:path";
31
31
  import { extractAssistantContentBlocks, extractAssistantText, isAssistantSdkMessage, } from "./assistant-content.js";
32
32
  import { parseProviderError, } from "./provider-error.js";
@@ -1305,6 +1305,15 @@ export class ConsolePiRuntime {
1305
1305
  if (!sessionFile) {
1306
1306
  throw new Error(`chat session shell missing sessionFile for ${sessionId}`);
1307
1307
  }
1308
+ // Pi's SessionManager.create deliberately postpones writing a JSONL until
1309
+ // an assistant response. Materialize a valid header now, otherwise Console
1310
+ // could persist a path that disappears on a restart before first reply.
1311
+ await this.bindings.materializeSessionShell?.({
1312
+ sessionManager,
1313
+ sessionId,
1314
+ sessionFile,
1315
+ cwd: this.options.cwd,
1316
+ });
1308
1317
  this.sessionManagers.set(sessionId, sessionManager);
1309
1318
  this.thinkingSelections.set(sessionId, "auto");
1310
1319
  // Detached single-flight materialization; dependent routes await the same promise.
@@ -2312,6 +2321,16 @@ export class ConsolePiRuntime {
2312
2321
  });
2313
2322
  }
2314
2323
  catch (error) {
2324
+ const code = error && typeof error === "object" && "code" in error
2325
+ ? String(error.code ?? "")
2326
+ : "";
2327
+ if (code === "SESSION_FILE_MISSING" || code === "ENOENT") {
2328
+ return {
2329
+ ok: false,
2330
+ code: "NO_SESSION_FILE",
2331
+ message: `Pi session file missing or not openable: ${init.sessionFile}`,
2332
+ };
2333
+ }
2315
2334
  return {
2316
2335
  ok: false,
2317
2336
  code: "REOPEN_FAILED",
@@ -3147,6 +3166,36 @@ export function createDefaultPiSdkBindings() {
3147
3166
  id: opts.sessionId,
3148
3167
  });
3149
3168
  },
3169
+ materializeSessionShell: async (opts) => {
3170
+ const sdk = (await loadSdk());
3171
+ const manager = opts.sessionManager;
3172
+ if (typeof manager.setSessionFile !== "function") {
3173
+ throw new Error("Pi SessionManager cannot materialize a session file");
3174
+ }
3175
+ let created = false;
3176
+ try {
3177
+ await mkdir(path.dirname(opts.sessionFile), { recursive: true });
3178
+ // Match Pi's documented session header. Rebinding through the public
3179
+ // SessionManager API marks it flushed, so Pi appends the first user
3180
+ // entry instead of trying to create the already-present file later.
3181
+ await writeFile(opts.sessionFile, `${JSON.stringify({
3182
+ type: "session",
3183
+ version: typeof sdk.CURRENT_SESSION_VERSION === "number"
3184
+ ? sdk.CURRENT_SESSION_VERSION
3185
+ : 3,
3186
+ id: opts.sessionId,
3187
+ timestamp: new Date().toISOString(),
3188
+ cwd: opts.cwd,
3189
+ })}\n`, { encoding: "utf8", flag: "wx", mode: 0o600 });
3190
+ created = true;
3191
+ manager.setSessionFile(opts.sessionFile);
3192
+ }
3193
+ catch (error) {
3194
+ if (created)
3195
+ await rm(opts.sessionFile, { force: true }).catch(() => undefined);
3196
+ throw error;
3197
+ }
3198
+ },
3150
3199
  createInMemorySessionManager: async (opts) => {
3151
3200
  const sdk = (await loadSdk());
3152
3201
  return sdk.SessionManager.inMemory(opts.cwd);
@@ -3163,13 +3212,19 @@ export function createDefaultPiSdkBindings() {
3163
3212
  // wrong conversation under the durable Console sessionId. Fail closed
3164
3213
  // before open when the persisted file is absent.
3165
3214
  await access(opts.sessionFile);
3166
- return sdk.SessionManager.open(opts.sessionFile);
3167
3215
  }
3168
- catch {
3169
- // Missing/corrupt session file — caller fails closed rather than
3170
- // silently creating a fresh session and losing prior context.
3216
+ catch (error) {
3217
+ const code = error && typeof error === "object" && "code" in error
3218
+ ? String(error.code ?? "")
3219
+ : "";
3220
+ if (code === "ENOENT") {
3221
+ throw Object.assign(new Error(`SESSION_FILE_MISSING: ${opts.sessionFile}`), { code: "SESSION_FILE_MISSING", cause: error });
3222
+ }
3223
+ // File exists but is unreadable (permissions / I/O) — caller maps
3224
+ // undefined to 503 rather than silently creating a fresh session.
3171
3225
  return undefined;
3172
3226
  }
3227
+ return sdk.SessionManager.open(opts.sessionFile);
3173
3228
  },
3174
3229
  resolveModel: (opts) => {
3175
3230
  const rt = opts.modelRuntime;
@@ -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";
@@ -468,7 +469,7 @@ function sendRuntimeUnavailable(res, error) {
468
469
  * handled here — only reopen/init readiness failures.
469
470
  */
470
471
  function mapReopenSessionFailure(reopen) {
471
- if (reopen.code === "NO_SESSION_FILE") {
472
+ if (reopen.code === "NO_SESSION_FILE" || reopen.code === "SESSION_FILE_MISSING") {
472
473
  return {
473
474
  status: 404,
474
475
  code: "PI_SESSION_NOT_FOUND",
@@ -488,6 +489,19 @@ function mapReopenSessionFailure(reopen) {
488
489
  message: reopen.message,
489
490
  };
490
491
  }
492
+ /** Drop an orphan catalog record whose promised Pi JSONL is gone. */
493
+ async function healMissingSessionFile(deps, sessionId, reopen) {
494
+ if (reopen && reopen.code !== "NO_SESSION_FILE" && reopen.code !== "SESSION_FILE_MISSING") {
495
+ return;
496
+ }
497
+ if (typeof deps.store.remove !== "function")
498
+ return;
499
+ await deps.store.remove(sessionId).catch(() => undefined);
500
+ }
501
+ async function mapFailedReopen(deps, sessionId, reopen) {
502
+ await healMissingSessionFile(deps, sessionId, reopen);
503
+ return mapReopenSessionFailure(reopen);
504
+ }
491
505
  export async function handleChatCapabilities(_req, res, deps) {
492
506
  const toolSchemas = buildModelCallableToolSchemas();
493
507
  sendJson(res, 200, {
@@ -731,10 +745,7 @@ export async function handleBranchChatSession(req, res, deps, sessionId) {
731
745
  sessionFile: record.sessionFile,
732
746
  });
733
747
  if (!reopened.ok) {
734
- const mapped = mapReopenSessionFailure({
735
- code: reopened.code,
736
- message: reopened.message,
737
- });
748
+ const mapped = await mapFailedReopen(deps, sessionId, reopened);
738
749
  sendJson(res, mapped.status, {
739
750
  ok: false,
740
751
  error: { code: mapped.code, message: mapped.message },
@@ -809,6 +820,29 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
809
820
  });
810
821
  return;
811
822
  }
823
+ if (record.sessionFile) {
824
+ try {
825
+ await access(record.sessionFile);
826
+ }
827
+ catch (error) {
828
+ const code = error && typeof error === "object" && "code" in error
829
+ ? String(error.code ?? "")
830
+ : "";
831
+ if (code === "ENOENT") {
832
+ await healMissingSessionFile(deps, sessionId, {
833
+ code: "NO_SESSION_FILE",
834
+ });
835
+ sendJson(res, 404, {
836
+ ok: false,
837
+ error: {
838
+ code: "PI_SESSION_NOT_FOUND",
839
+ message: `Pi session file missing or not openable: ${record.sessionFile}`,
840
+ },
841
+ });
842
+ return;
843
+ }
844
+ }
845
+ }
812
846
  await deps.operationLinker.recoverSession(sessionId);
813
847
  const composerDraft = await new ComposerDraftStore(deps.appData).get(sessionId);
814
848
  const snapshot = deps.events.snapshot(sessionId);
@@ -1486,10 +1520,7 @@ export async function handleReopenChatSession(req, res, deps, sessionId) {
1486
1520
  : {}),
1487
1521
  });
1488
1522
  if (!result.ok) {
1489
- const mapped = mapReopenSessionFailure({
1490
- code: result.code,
1491
- message: result.message,
1492
- });
1523
+ const mapped = await mapFailedReopen(deps, sessionId, result);
1493
1524
  sendJson(res, mapped.status, {
1494
1525
  ok: false,
1495
1526
  error: { code: mapped.code, message: mapped.message },
@@ -1575,10 +1606,7 @@ export async function handleChatPrompt(req, res, deps, sessionId) {
1575
1606
  : {}),
1576
1607
  });
1577
1608
  if (!reopen.ok) {
1578
- const mapped = mapReopenSessionFailure({
1579
- code: reopen.code,
1580
- message: reopen.message,
1581
- });
1609
+ const mapped = await mapFailedReopen(deps, sessionId, reopen);
1582
1610
  sendJson(res, mapped.status, {
1583
1611
  ok: false,
1584
1612
  error: { code: mapped.code, message: mapped.message },
@@ -1847,10 +1875,7 @@ export async function handleCreateChatTurn(req, res, deps, sessionId, trustedPay
1847
1875
  : {}),
1848
1876
  });
1849
1877
  if (!reopen.ok) {
1850
- const mapped = mapReopenSessionFailure({
1851
- code: reopen.code,
1852
- message: reopen.message,
1853
- });
1878
+ const mapped = await mapFailedReopen(deps, sessionId, reopen);
1854
1879
  sendJson(res, mapped.status, {
1855
1880
  ok: false,
1856
1881
  accepted: false,
@@ -2999,7 +3024,7 @@ export async function ensureActiveSession(deps, sessionId) {
2999
3024
  : {}),
3000
3025
  });
3001
3026
  if (!reopened.ok) {
3002
- return { ok: false, ...mapReopenSessionFailure(reopened) };
3027
+ return { ok: false, ...(await mapFailedReopen(deps, sessionId, reopened)) };
3003
3028
  }
3004
3029
  return { ok: true };
3005
3030
  }
@@ -3946,10 +3971,7 @@ export async function handleChatCompact(req, res, deps, sessionId) {
3946
3971
  : {}),
3947
3972
  });
3948
3973
  if (!reopened.ok) {
3949
- const mapped = mapReopenSessionFailure({
3950
- code: reopened.code,
3951
- message: reopened.message,
3952
- });
3974
+ const mapped = await mapFailedReopen(deps, sessionId, reopened);
3953
3975
  sendJson(res, mapped.status, {
3954
3976
  ok: false,
3955
3977
  error: { code: mapped.code, message: mapped.message },
@@ -38,6 +38,16 @@ export function cleanChatTitle(value) {
38
38
  function recordPath(appData, sessionId) {
39
39
  return path.join(appData.chats, `${sessionId}.json`);
40
40
  }
41
+ /** True when `sessionFile` lives inside this store's dedicated Pi JSONL dir. */
42
+ export function isConsoleOwnedSessionFile(appData, sessionFile) {
43
+ const value = sessionFile?.trim();
44
+ if (!value)
45
+ return false;
46
+ const root = path.resolve(appData.chatSessions);
47
+ const target = path.resolve(value);
48
+ const relative = path.relative(root, target);
49
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
50
+ }
41
51
  export class ChatSessionStore {
42
52
  appData;
43
53
  options;
@@ -237,8 +247,14 @@ export class ChatSessionStore {
237
247
  const record = await this.get(sessionId);
238
248
  if (!record)
239
249
  return false;
250
+ const sessionFile = record.sessionFile;
240
251
  await rm(recordPath(this.appData, sessionId), { force: true });
241
252
  this.options.onRecordChange?.(sessionId, undefined);
253
+ // Record first, then best-effort JSONL unlink. Final consistency is
254
+ // "both gone" — a leftover JSONL is harmless, a leftover record is not.
255
+ if (isConsoleOwnedSessionFile(this.appData, sessionFile) && sessionFile) {
256
+ await rm(sessionFile, { force: true }).catch(() => undefined);
257
+ }
242
258
  return true;
243
259
  });
244
260
  }
@@ -566,8 +566,26 @@ export async function createConsoleServer(options) {
566
566
  const stored = await context.chatRouteDeps.store.get(sessionId);
567
567
  if (stored)
568
568
  owner = context;
569
+ else if (!catalogEntry) {
570
+ // Catalog and store both miss: the session was deleted, not
571
+ // retargeted. Do not scan other workspaces or leak existence.
572
+ sendJson(res, 404, {
573
+ ok: false,
574
+ error: {
575
+ code: "PI_SESSION_NOT_FOUND",
576
+ message: `chat session not found: ${sessionId}`,
577
+ },
578
+ });
579
+ return;
580
+ }
569
581
  else {
570
- // Do not scan another workspace and disclose whether the id exists there.
582
+ chatSessionCatalog.remove(sessionId);
583
+ await registry
584
+ .detachSession(ownerRecord.workspaceId, sessionId)
585
+ .catch(() => undefined);
586
+ // Catalog had the id, but this workspace store does not.
587
+ // Keep the conflict code so a mismatched selection cannot
588
+ // discover whether the session lives elsewhere.
571
589
  sendJson(res, 403, {
572
590
  ok: false,
573
591
  error: {
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-CeRNEMvc.js";import{p as f}from"./chunk-JWPE2WC7-B54ZUQY-.js";import{_ as n,l as o}from"./mermaid.core-DMMSIx7j.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-DtSt5K2w.js";import"./index-BLzcAykx.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-CdiNyN0N.js";import{p as f}from"./chunk-JWPE2WC7-DiDVlVB1.js";import{_ as n,l as o}from"./mermaid.core-ChyQH_ut.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BT_JRsOH.js";import"./index-DtPrwu7M.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-DMMSIx7j.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-ChyQH_ut.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};