@tea-agent/loop-agent 0.40.0-next.12 → 0.40.0-next.14

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 (116) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +2 -2
  3. package/dist/build-stamp.json +3 -3
  4. package/dist/cli/command-definitions.js +9 -5
  5. package/dist/cli/program.js +38 -3
  6. package/dist/commands/init-model-catalog.js +22 -0
  7. package/dist/commands/task-advance.js +23 -1
  8. package/dist/commands/task-source-prepare.js +5 -0
  9. package/dist/commands/task-status.js +1 -0
  10. package/dist/shared/operator/capabilities.js +9 -1
  11. package/dist/worker/console/chat/assistant-content.js +7 -0
  12. package/dist/worker/console/chat/chat-event-store.js +82 -11
  13. package/dist/worker/console/chat/goal-round-driver.js +72 -0
  14. package/dist/worker/console/chat/goals.js +300 -0
  15. package/dist/worker/console/chat/pi-runtime.js +140 -4
  16. package/dist/worker/console/chat/repo-browser.js +94 -12
  17. package/dist/worker/console/chat/routes.js +482 -29
  18. package/dist/worker/console/chat/session-catalog.js +48 -0
  19. package/dist/worker/console/chat/session-store.js +27 -18
  20. package/dist/worker/console/chat/shortcuts.js +7 -0
  21. package/dist/worker/console/chat/turn-process.js +32 -3
  22. package/dist/worker/console/operator-actions.js +16 -2
  23. package/dist/worker/console/operator-selection.js +6 -2
  24. package/dist/worker/console/pi-plugins.catalog.json +73658 -0
  25. package/dist/worker/console/pi-plugins.js +100 -0
  26. package/dist/worker/console/routes.js +221 -6
  27. package/dist/worker/console/server.js +172 -42
  28. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C_hkwt2J.js → abnfDiagram-N423BO3Z-B0gRLXJs.js} +1 -1
  29. package/dist/worker/console/static/assets/{arc-s2Q6r96B.js → arc-DldY0Hsk.js} +1 -1
  30. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CtgsrwU1.js → architectureDiagram-T3A2C74G-4xHhMRM9.js} +1 -1
  31. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-DZ-A7EiJ.js → blockDiagram-VBNYF7ZC-DPym7n0M.js} +1 -1
  32. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-D2qv0Nyo.js → c4Diagram-5PPSVZJV-cSFzY5yS.js} +1 -1
  33. package/dist/worker/console/static/assets/channel-4xBA5kwn.js +1 -0
  34. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D-UYpSaW.js → chunk-2GRJ4B5K-w-64RMuc.js} +1 -1
  35. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DAS3jrrt.js → chunk-2Q5K7J3B-Bvue--jh.js} +1 -1
  36. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CSbuRcGB.js → chunk-5RXB4S5H-DxqkQnCh.js} +1 -1
  37. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BDH1ZO40.js → chunk-5VM5RSS4-C83gOKLK.js} +1 -1
  38. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Dc2cqQIb.js → chunk-6Q2QTUOP-C_V6tKAV.js} +1 -1
  39. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CkZuemIW.js → chunk-GF5L2VYU-CPvZQuL9.js} +1 -1
  40. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-URwjQnte.js → chunk-JWPE2WC7-w31Z_ATM.js} +1 -1
  41. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-04RHylJK.js → chunk-KBJHAD2P-DermQC9f.js} +1 -1
  42. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CvQX56Hh.js → chunk-RYQCIY6F-CmwaYcnB.js} +1 -1
  43. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-dUF7qura.js → chunk-XXDRQBXY-DLwZqUBf.js} +1 -1
  44. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CkefOueI.js +1 -0
  45. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CkefOueI.js +1 -0
  46. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-COwbfxJE.js → cose-bilkent-JH36ORCC-Dzh7bT73.js} +1 -1
  47. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CaBEJekQ.js → cynefin-VYW2F7L2-CfOcBHtp.js} +1 -1
  48. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-COWUBdet.js → cynefinDiagram-MW4NZA55-D0EbCVDZ.js} +1 -1
  49. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BLxVqTn4.js → dagre-VZM6K2ZE-MfOFA6PZ.js} +1 -1
  50. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-WL0yKfEQ.js → diagram-7IWD3JNH-BOa5-9PN.js} +1 -1
  51. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DaVkKCle.js → diagram-B4RE2ZJO-B6zvJ-aL.js} +1 -1
  52. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DdTgnRGf.js → diagram-LBJQPF4R-BKwr5bRF.js} +1 -1
  53. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-PG2Axo6A.js → diagram-Q27KOJAE-PLI5QLpX.js} +1 -1
  54. package/dist/worker/console/static/assets/{diagram-UB23O5K3-6vuQzlzI.js → diagram-UB23O5K3-BgYYME3o.js} +1 -1
  55. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BpXeBz-g.js → ebnfDiagram-BXEA7PRR-D-SpjXk2.js} +1 -1
  56. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-PxtUnaa1.js → erDiagram-JOGREHBK--JHpEOka.js} +1 -1
  57. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B--Qgvs1.js → flowDiagram-UKHOOZJN-CbkQgWUz.js} +1 -1
  58. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BIfQCgtx.js → ganttDiagram-PKOTCBZU-CTZMAMVE.js} +1 -1
  59. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BRaX_E4j.js → gitGraphDiagram-DS77QQ5N-BaUGSDig.js} +1 -1
  60. package/dist/worker/console/static/assets/index-C4mO0i6P.js +437 -0
  61. package/dist/worker/console/static/assets/index-C86Y55Or.css +1 -0
  62. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Cd93r4Ab.js → infoDiagram-6WML65LV-DA3WbWjf.js} +1 -1
  63. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C4c80iHt.js → ishikawaDiagram-WSZJBQD7-DbhI82U4.js} +1 -1
  64. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Ch5ajf3s.js → journeyDiagram-NVQOT4AX-CJKA3OBG.js} +1 -1
  65. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DbpyW1Ut.js → kanban-definition-27J2QSJJ-CF6dIVRa.js} +1 -1
  66. package/dist/worker/console/static/assets/{linear-BHVjBnTW.js → linear-D4dEtOUL.js} +1 -1
  67. package/dist/worker/console/static/assets/{mermaid.core-DO70kJ34.js → mermaid.core-ClhMic6O.js} +5 -5
  68. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-_P44Zpvs.js → mindmap-definition-FAOFIHXS-NfRin7S2.js} +1 -1
  69. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Bgi9FmEd.js → pegDiagram-VL7TDLO6-DlBtBwW-.js} +1 -1
  70. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C9InDfJn.js → pieDiagram-7S7Q4E2Y-DdIkNHZO.js} +1 -1
  71. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DtKrUouv.js → quadrantDiagram-CIZ2JOQS-YD8T98WX.js} +1 -1
  72. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-zgAD_6Yo.js → railroadDiagram-AXF67PYL-CS4e5zGK.js} +1 -1
  73. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CQNO6kj4.js → requirementDiagram-LRYGKXZP-BpbWoL9r.js} +1 -1
  74. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-knKu111P.js → sankeyDiagram-W5VNT64P-DvWy-Mc9.js} +1 -1
  75. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-D6tshiQ6.js → sequenceDiagram-SI44F4Z6-DmoXH5pJ.js} +1 -1
  76. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BMIWJcir.js → sizeCapture-X5ZJPWSS-BY89V1SW.js} +1 -1
  77. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BP9wRDqc.js → stateDiagram-OKZ733FA-DaK2Iuqr.js} +1 -1
  78. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DLc5WD-z.js +1 -0
  79. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DEkM5Dhe.js → swimlanes-SLNWSIFB-VVcozat-.js} +2 -2
  80. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DhjxpI7d.js +8 -0
  81. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ByBKsL35.js → timeline-definition-Z64GVDOM-D4l1E4Mu.js} +1 -1
  82. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-waFbNCUy.js → vennDiagram-T6HMQDX7-CJmewqcq.js} +1 -1
  83. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-1ljiY0eZ.js → wardleyDiagram-T6FBY63Y-CsCCYorj.js} +1 -1
  84. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B4ZLHr_L.js → xychartDiagram-ELKLHX3M-CwjILi0N.js} +1 -1
  85. package/dist/worker/console/static/index.html +2 -2
  86. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +95 -25
  87. package/dist/worker/console/static-src/operator-chat/compaction-message.js +35 -1
  88. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +3 -1
  89. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +24 -19
  90. package/dist/worker/console/static-src/operator-chat/useChatStream.js +0 -11
  91. package/dist/worker/console/static-src/operator-chat/useChatThread.js +8 -1
  92. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +34 -4
  93. package/dist/worker/console/static-src/operator-chat/workspace-reorder.js +18 -0
  94. package/dist/worker/console/static-src/shell/useWorkspaces.js +150 -34
  95. package/dist/worker/console/workspace-context.js +28 -3
  96. package/dist/worker/console/workspace-registry.js +333 -60
  97. package/dist/worker/observe/static/inspect-bootstrap.d.ts +8 -0
  98. package/dist/worker/observe/static/inspect-bootstrap.js +57 -0
  99. package/dist/worker/observe/static/inspect-workspace.js +54 -10
  100. package/dist/worker/observe/static/operator-chrome.css +23 -4
  101. package/dist/worker/observe/static/operator-chrome.js +6 -2
  102. package/dist/worker/observe/static/styles.css +78 -25
  103. package/dist/worker/observe/static/views/dag-inspector.js +2 -1
  104. package/dist/worker/observe/static/views/dags.js +7 -5
  105. package/dist/workflows/dag/init-hybrid.js +3 -3
  106. package/docs/templates/backend-test-dag.json +3 -3
  107. package/docs/templates/init-managed-agents.md +1 -1
  108. package/package.json +1 -1
  109. package/skills/loop-agent/references/command-reference.md +15 -1
  110. package/dist/worker/console/static/assets/channel-BluaMT3-.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-H-ZpSafr.js +0 -1
  112. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-H-ZpSafr.js +0 -1
  113. package/dist/worker/console/static/assets/index-Crrgl8Mx.css +0 -1
  114. package/dist/worker/console/static/assets/index-vPsp-idE.js +0 -437
  115. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-ClEhytQG.js +0 -1
  116. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPg5IMuu.js +0 -8
@@ -8,6 +8,9 @@ import { readPackageVersion } from "../../shared/package-metadata.js";
8
8
  import { DAG_ABSOLUTE_MAX_WALL_CLOCK_MS } from "../../shared/timeout-policy.js";
9
9
  import { openConsoleAppData } from "./app-data.js";
10
10
  import { isLoopbackHost } from "./loopback.js";
11
+ import { ChatSessionStore } from "./chat/session-store.js";
12
+ import { ChatSessionCatalog } from "./chat/session-catalog.js";
13
+ import { matchRepoHtmlPreviewRoute } from "./chat/repo-browser.js";
11
14
  import { handleConsoleHealthRequest, handleWorkspaceRoutes, sendJson, serveConsoleStatic, } from "./routes.js";
12
15
  import { issueBootCapabilityToken, injectConfirmationTokenScript, } from "./security.js";
13
16
  import { resolvePackageRootFromConsole } from "./sibling-controller.js";
@@ -74,6 +77,35 @@ export async function createConsoleServer(options) {
74
77
  });
75
78
  const registry = new WorkspaceRegistry(bootstrapAppData);
76
79
  const startupRecord = await registry.ensureStartupWorkspace(repoRoot);
80
+ const chatSessionCatalog = new ChatSessionCatalog();
81
+ // DSH-style dual baseline: Workspace records own grouping/order, while the
82
+ // Session catalog is rebuilt once at Host startup from durable Session
83
+ // persistence. Requests never rediscover every Workspace store.
84
+ for (const record of await registry.listAll()) {
85
+ try {
86
+ const store = new ChatSessionStore(openConsoleAppData({
87
+ repoRoot: record.repoRoot,
88
+ ...(options.appDataRoot
89
+ ? { appDataRoot: options.appDataRoot }
90
+ : {}),
91
+ }));
92
+ const sessions = await store.list({ includeArchived: true });
93
+ for (const session of sessions) {
94
+ if (session.repoFingerprint !== record.repoFingerprint)
95
+ continue;
96
+ chatSessionCatalog.upsert(session, record.workspaceId);
97
+ }
98
+ if (!record.hidden) {
99
+ await registry.reconcileSessions(record.workspaceId, sessions
100
+ .filter((session) => session.repoFingerprint === record.repoFingerprint)
101
+ .map((session) => session.sessionId));
102
+ }
103
+ }
104
+ catch {
105
+ // A removed or externally unavailable Workspace is not an empty Session
106
+ // baseline. Its prior durable membership remains untouched.
107
+ }
108
+ }
77
109
  const instanceNonce = configuredPort === 0 ? randomBytes(16).toString("hex") : undefined;
78
110
  const bootToken = issueBootCapabilityToken({
79
111
  repoFingerprint: startupRecord.repoFingerprint,
@@ -96,28 +128,10 @@ export async function createConsoleServer(options) {
96
128
  // ---- Per-workspace lazily-created contexts -------------------------------
97
129
  const workspaceContexts = new Map();
98
130
  let sharedConsoleUpdateExecutor = options.consoleUpdateExecutor;
99
- const resolveContext = async (workspaceId) => {
100
- const id = workspaceId?.trim();
101
- if (!id) {
102
- return {
103
- ok: false,
104
- status: 400,
105
- code: "WORKSPACE_REQUIRED",
106
- message: "workspaceId is required",
107
- };
108
- }
109
- const existing = workspaceContexts.get(id);
131
+ const materializeContext = async (record) => {
132
+ const existing = workspaceContexts.get(record.workspaceId);
110
133
  if (existing)
111
- return { ok: true, context: existing };
112
- const record = await registry.getVisible(id);
113
- if (!record) {
114
- return {
115
- ok: false,
116
- status: 404,
117
- code: "WORKSPACE_NOT_FOUND",
118
- message: `unknown workspace: ${id}`,
119
- };
120
- }
134
+ return existing;
121
135
  const context = await createWorkspaceContext(record, {
122
136
  appDataRoot: options.appDataRoot,
123
137
  staticDir,
@@ -132,12 +146,35 @@ export async function createConsoleServer(options) {
132
146
  createClient: options.createClient,
133
147
  defaultTimeoutMs: options.defaultTimeoutMs,
134
148
  skipReconcile: options.skipReconcile,
149
+ chatSessionCatalog,
135
150
  });
136
151
  if (sharedConsoleUpdateExecutor) {
137
152
  context.actionContext.consoleUpdateExecutor = sharedConsoleUpdateExecutor;
138
153
  }
139
- workspaceContexts.set(id, context);
154
+ workspaceContexts.set(record.workspaceId, context);
140
155
  void context.recoverDeferredTurns().catch(() => undefined);
156
+ return context;
157
+ };
158
+ const resolveContext = async (workspaceId) => {
159
+ const id = workspaceId?.trim();
160
+ if (!id) {
161
+ return {
162
+ ok: false,
163
+ status: 400,
164
+ code: "WORKSPACE_REQUIRED",
165
+ message: "workspaceId is required",
166
+ };
167
+ }
168
+ const record = await registry.getVisible(id);
169
+ if (!record) {
170
+ return {
171
+ ok: false,
172
+ status: 404,
173
+ code: "WORKSPACE_NOT_FOUND",
174
+ message: `unknown workspace: ${id}`,
175
+ };
176
+ }
177
+ const context = await materializeContext(record);
141
178
  return { ok: true, context };
142
179
  };
143
180
  const startupResolution = await resolveContext(startupRecord.workspaceId);
@@ -383,6 +420,52 @@ export async function createConsoleServer(options) {
383
420
  // the JSON body; all other chat routes resolve via Session ownership
384
421
  // (durable) with optional query workspaceId conflict check.
385
422
  const { handleChatRequest: dispatchChat } = await import("./chat/routes.js");
423
+ const htmlPreviewRoute = matchRepoHtmlPreviewRoute(pathname);
424
+ if (method === "GET" &&
425
+ pathname === "/api/operator/v1/chat/sessions" &&
426
+ !url.searchParams.has("workspaceId")) {
427
+ const sessions = chatSessionCatalog.list().map((entry) => {
428
+ const active = entry.workspaceId
429
+ ? workspaceContexts
430
+ .get(entry.workspaceId)
431
+ ?.chatRouteDeps.events.getActiveTurn(entry.sessionId)
432
+ : undefined;
433
+ return {
434
+ sessionId: entry.sessionId,
435
+ ...(entry.workspaceId ? { workspaceId: entry.workspaceId } : {}),
436
+ state: entry.state,
437
+ createdAt: entry.createdAt,
438
+ updatedAt: entry.updatedAt,
439
+ ...(entry.title !== undefined ? { title: entry.title } : {}),
440
+ ...(entry.displayTitle !== undefined
441
+ ? { displayTitle: entry.displayTitle }
442
+ : {}),
443
+ ...(entry.modelProvider !== undefined
444
+ ? { modelProvider: entry.modelProvider }
445
+ : {}),
446
+ ...(entry.modelId !== undefined ? { modelId: entry.modelId } : {}),
447
+ ...(entry.thinkingLevel !== undefined
448
+ ? { thinkingLevel: entry.thinkingLevel }
449
+ : {}),
450
+ activeTurnId: active?.turnId,
451
+ turnPhase: active?.abortRequestedAt
452
+ ? "stopping"
453
+ : active
454
+ ? "running"
455
+ : "idle",
456
+ deferredTurn: entry.deferredTurn,
457
+ deferredTurns: entry.deferredTurns,
458
+ };
459
+ });
460
+ sendJson(res, 200, {
461
+ ok: true,
462
+ schemaVersion: 2,
463
+ sessions,
464
+ hasBash: true,
465
+ workspaceErrors: [],
466
+ });
467
+ return;
468
+ }
386
469
  if (method === "POST" && pathname === "/api/operator/v1/chat/sessions") {
387
470
  // AC-R5-006: the mutation gate fires BEFORE body pre-read/workspace
388
471
  // resolution — an unauthenticated mutation gets exactly 403, never a
@@ -445,30 +528,44 @@ export async function createConsoleServer(options) {
445
528
  }
446
529
  }
447
530
  const requestedId = url.searchParams.get("workspaceId")?.trim();
531
+ const catalogEntry = chatSessionCatalog.get(sessionId);
448
532
  let owner;
449
533
  if (requestedId) {
534
+ if (catalogEntry?.workspaceId &&
535
+ catalogEntry.workspaceId !== requestedId) {
536
+ sendJson(res, 403, {
537
+ ok: false,
538
+ error: {
539
+ code: "WORKSPACE_CONFLICT",
540
+ message: "workspaceId does not match the session's owning workspace",
541
+ },
542
+ });
543
+ return;
544
+ }
450
545
  // The current UI always sends its selected workspace. Resolve that one
451
546
  // context directly: scanning every historical registry entry first can
452
547
  // fail on an old, deleted temp directory before reaching the real owner.
453
- const resolution = await resolveContext(requestedId);
454
- if (!resolution.ok) {
548
+ const ownerRecord = catalogEntry
549
+ ? await registry.get(requestedId)
550
+ : await registry.getVisible(requestedId);
551
+ if (!ownerRecord ||
552
+ (catalogEntry &&
553
+ ownerRecord.repoFingerprint !== catalogEntry.repoFingerprint)) {
455
554
  // A session-scoped workspace hint is a conflict boundary. Unknown
456
555
  // workspace ids must not reveal whether the session exists elsewhere.
457
- const workspaceConflict = resolution.code === "WORKSPACE_NOT_FOUND";
458
- sendJson(res, workspaceConflict ? 403 : resolution.status, {
556
+ sendJson(res, 403, {
459
557
  ok: false,
460
- error: workspaceConflict
461
- ? {
462
- code: "WORKSPACE_CONFLICT",
463
- message: "workspaceId does not match the session's owning workspace",
464
- }
465
- : { code: resolution.code, message: resolution.message },
558
+ error: {
559
+ code: "WORKSPACE_CONFLICT",
560
+ message: "workspaceId does not match the session's owning workspace",
561
+ },
466
562
  });
467
563
  return;
468
564
  }
469
- const stored = await resolution.context.chatRouteDeps.store.get(sessionId);
565
+ const context = await materializeContext(ownerRecord);
566
+ const stored = await context.chatRouteDeps.store.get(sessionId);
470
567
  if (stored)
471
- owner = resolution.context;
568
+ owner = context;
472
569
  else {
473
570
  // Do not scan another workspace and disclose whether the id exists there.
474
571
  sendJson(res, 403, {
@@ -481,12 +578,18 @@ export async function createConsoleServer(options) {
481
578
  return;
482
579
  }
483
580
  }
581
+ else if (catalogEntry?.workspaceId) {
582
+ const ownerRecord = await registry.get(catalogEntry.workspaceId);
583
+ if (ownerRecord &&
584
+ ownerRecord.repoFingerprint === catalogEntry.repoFingerprint) {
585
+ owner = await materializeContext(ownerRecord);
586
+ }
587
+ }
484
588
  else {
485
- // Legacy callers may omit workspaceId. Session ids live in per-workspace
486
- // namespaces, so scan visible workspaces but tolerate stale registry
487
- // records whose directories were removed outside the Console.
488
- const visible = await registry.listVisible();
489
- for (const record of visible) {
589
+ // Catalog misses are limited to out-of-process/legacy writes after Host
590
+ // startup. Recover once, then publish the discovered Session into the
591
+ // independent baseline for subsequent requests.
592
+ for (const record of await registry.listVisible()) {
490
593
  try {
491
594
  const resolution = await resolveContext(record.workspaceId);
492
595
  if (!resolution.ok)
@@ -494,6 +597,7 @@ export async function createConsoleServer(options) {
494
597
  const stored = await resolution.context.chatRouteDeps.store.get(sessionId);
495
598
  if (stored) {
496
599
  owner = resolution.context;
600
+ chatSessionCatalog.upsert(stored, record.workspaceId);
497
601
  break;
498
602
  }
499
603
  }
@@ -519,9 +623,24 @@ export async function createConsoleServer(options) {
519
623
  await dispatchChat(req, res, owner.chatRouteDeps, pathname, undefined);
520
624
  return;
521
625
  }
522
- // Non-session chat surface (capabilities, repo browser) resolves from
523
- // query workspaceId; fail closed when missing.
524
- const workspaceId = url.searchParams.get("workspaceId") ?? undefined;
626
+ // Non-session chat surfaces normally resolve from ?workspaceId=. The
627
+ // HTML preview route carries it in the URL path so browser-relative
628
+ // CSS/image requests retain the same workspace scope after dropping a
629
+ // document query string.
630
+ const queryWorkspaceId = url.searchParams.get("workspaceId")?.trim() || undefined;
631
+ if (htmlPreviewRoute &&
632
+ queryWorkspaceId &&
633
+ queryWorkspaceId !== htmlPreviewRoute.workspaceId) {
634
+ sendJson(res, 403, {
635
+ ok: false,
636
+ error: {
637
+ code: "WORKSPACE_CONFLICT",
638
+ message: "workspaceId does not match the HTML preview route",
639
+ },
640
+ });
641
+ return;
642
+ }
643
+ const workspaceId = htmlPreviewRoute?.workspaceId || queryWorkspaceId;
525
644
  const resolution = await resolveContext(workspaceId);
526
645
  if (!resolution.ok) {
527
646
  sendJson(res, resolution.status, {
@@ -595,6 +714,13 @@ export async function createConsoleServer(options) {
595
714
  await handleConsoleOperatorRequest(req, res, operatorRouteContext(startup));
596
715
  return;
597
716
  }
717
+ // Pi 插件目录 / 安装 / 卸载:全局用户级(~/.pi/agent),非工作区作用域,用 startup context。
718
+ if (pathname === "/api/operator/v1/pi-plugins" ||
719
+ pathname === "/api/operator/v1/pi-plugins/install" ||
720
+ pathname === "/api/operator/v1/pi-plugins/uninstall") {
721
+ await handleConsoleOperatorRequest(req, res, operatorRouteContext(startup));
722
+ return;
723
+ }
598
724
  // Unknown operator route: fail closed (never a startup-workspace proxy).
599
725
  sendJson(res, 404, {
600
726
  ok: false,
@@ -670,7 +796,11 @@ export async function createConsoleServer(options) {
670
796
  }
671
797
  }
672
798
  workspaceContexts.clear();
799
+ registry.close();
673
800
  server.close((err) => (err ? closeReject(err) : closeResolve()));
801
+ // Workspace/event SSE responses are intentionally long-lived. Force them
802
+ // closed after initiating shutdown so Console close never hangs on a tab.
803
+ server.closeAllConnections();
674
804
  }),
675
805
  });
676
806
  });
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-Dc2cqQIb.js";import{p as f}from"./chunk-JWPE2WC7-URwjQnte.js";import{_ as n,l as o}from"./mermaid.core-DO70kJ34.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CaBEJekQ.js";import"./index-vPsp-idE.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-C_V6tKAV.js";import{p as f}from"./chunk-JWPE2WC7-w31Z_ATM.js";import{_ as n,l as o}from"./mermaid.core-ClhMic6O.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CfOcBHtp.js";import"./index-C4mO0i6P.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-DO70kJ34.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-ClhMic6O.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};