@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
@@ -0,0 +1,48 @@
1
+ import { projectDeferredTurnSummaries, projectDeferredTurnSummary, } from "./deferred-turn.js";
2
+ /**
3
+ * Process-wide Session baseline, mirroring DSH's independent session.list.
4
+ * Full records stay in their fingerprint-isolated stores; this catalog only
5
+ * removes repeated filesystem discovery and resolves Session ownership.
6
+ */
7
+ export class ChatSessionCatalog {
8
+ entries = new Map();
9
+ upsert(record, ownerWorkspaceId) {
10
+ const workspaceId = ownerWorkspaceId?.trim() || record.workspaceId?.trim();
11
+ const existing = this.entries.get(record.sessionId);
12
+ if (existing &&
13
+ existing.repoFingerprint !== record.repoFingerprint) {
14
+ throw new Error(`chat session catalog conflict: ${record.sessionId} belongs to multiple repositories`);
15
+ }
16
+ this.entries.set(record.sessionId, {
17
+ sessionId: record.sessionId,
18
+ repoFingerprint: record.repoFingerprint,
19
+ repoRootDisplay: record.repoRootDisplay,
20
+ state: record.state,
21
+ ...(record.title !== undefined ? { title: record.title } : {}),
22
+ ...(record.displayTitle !== undefined
23
+ ? { displayTitle: record.displayTitle }
24
+ : {}),
25
+ createdAt: record.createdAt,
26
+ updatedAt: record.updatedAt,
27
+ ...(record.modelProvider !== undefined
28
+ ? { modelProvider: record.modelProvider }
29
+ : {}),
30
+ ...(record.modelId !== undefined ? { modelId: record.modelId } : {}),
31
+ ...(record.thinkingLevel !== undefined
32
+ ? { thinkingLevel: record.thinkingLevel }
33
+ : {}),
34
+ deferredTurn: projectDeferredTurnSummary(record.deferredTurns?.[0]),
35
+ deferredTurns: projectDeferredTurnSummaries(record.deferredTurns ?? []),
36
+ ...(workspaceId ? { workspaceId } : {}),
37
+ });
38
+ }
39
+ remove(sessionId) {
40
+ this.entries.delete(sessionId);
41
+ }
42
+ get(sessionId) {
43
+ return this.entries.get(sessionId);
44
+ }
45
+ list() {
46
+ return [...this.entries.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
47
+ }
48
+ }
@@ -40,14 +40,20 @@ function recordPath(appData, sessionId) {
40
40
  }
41
41
  export class ChatSessionStore {
42
42
  appData;
43
+ options;
43
44
  /**
44
45
  * Per-session write lock. Each mutating op chains onto the previous one so
45
46
  * concurrent appendMessage / recordToolInvocation calls do not lose updates
46
47
  * via last-write-wins on the same JSON record.
47
48
  */
48
49
  writeChains = new Map();
49
- constructor(appData) {
50
+ constructor(appData, options = {}) {
50
51
  this.appData = appData;
52
+ this.options = options;
53
+ }
54
+ async persist(record) {
55
+ await writeSecureJson(recordPath(this.appData, record.sessionId), record);
56
+ this.options.onRecordChange?.(record.sessionId, record);
51
57
  }
52
58
  /** Read + migrate legacy V1 records into the V2 in-memory shape. */
53
59
  async get(sessionId) {
@@ -147,7 +153,7 @@ export class ChatSessionStore {
147
153
  messages: [],
148
154
  toolInvocations: [],
149
155
  };
150
- await writeSecureJson(recordPath(this.appData, input.sessionId), record);
156
+ await this.persist(record);
151
157
  return record;
152
158
  });
153
159
  }
@@ -169,7 +175,7 @@ export class ChatSessionStore {
169
175
  messages: structuredClone(source.messages),
170
176
  toolInvocations: structuredClone(source.toolInvocations),
171
177
  };
172
- await writeSecureJson(recordPath(this.appData, input.targetSessionId), record);
178
+ await this.persist(record);
173
179
  return record;
174
180
  });
175
181
  }
@@ -180,7 +186,7 @@ export class ChatSessionStore {
180
186
  throw new Error(`chat session not found: ${sessionId}`);
181
187
  record.title = title.trim().slice(0, 160) || undefined;
182
188
  record.updatedAt = new Date().toISOString();
183
- await writeSecureJson(recordPath(this.appData, sessionId), record);
189
+ await this.persist(record);
184
190
  return record;
185
191
  });
186
192
  }
@@ -195,7 +201,7 @@ export class ChatSessionStore {
195
201
  return undefined;
196
202
  record.title = cleaned;
197
203
  record.updatedAt = new Date().toISOString();
198
- await writeSecureJson(recordPath(this.appData, sessionId), record);
204
+ await this.persist(record);
199
205
  return record;
200
206
  });
201
207
  }
@@ -211,7 +217,7 @@ export class ChatSessionStore {
211
217
  if (selection.thinkingLevel !== undefined)
212
218
  record.thinkingLevel = selection.thinkingLevel;
213
219
  record.updatedAt = new Date().toISOString();
214
- await writeSecureJson(recordPath(this.appData, sessionId), record);
220
+ await this.persist(record);
215
221
  return record;
216
222
  });
217
223
  }
@@ -222,7 +228,7 @@ export class ChatSessionStore {
222
228
  throw new Error(`chat session not found: ${sessionId}`);
223
229
  record.state = state;
224
230
  record.updatedAt = new Date().toISOString();
225
- await writeSecureJson(recordPath(this.appData, sessionId), record);
231
+ await this.persist(record);
226
232
  return record;
227
233
  });
228
234
  }
@@ -232,6 +238,7 @@ export class ChatSessionStore {
232
238
  if (!record)
233
239
  return false;
234
240
  await rm(recordPath(this.appData, sessionId), { force: true });
241
+ this.options.onRecordChange?.(sessionId, undefined);
235
242
  return true;
236
243
  });
237
244
  }
@@ -285,7 +292,7 @@ export class ChatSessionStore {
285
292
  record.deferredTurn = queue[0];
286
293
  record.deferredQueueRevision = (record.deferredQueueRevision ?? 0) + 1;
287
294
  record.updatedAt = now;
288
- await writeSecureJson(recordPath(this.appData, sessionId), record);
295
+ await this.persist(record);
289
296
  return { ok: true, deferredTurn, idempotent: false };
290
297
  });
291
298
  }
@@ -313,7 +320,7 @@ export class ChatSessionStore {
313
320
  record.deferredTurn = queue[0];
314
321
  record.deferredQueueRevision = (record.deferredQueueRevision ?? 0) + 1;
315
322
  record.updatedAt = next.updatedAt;
316
- await writeSecureJson(recordPath(this.appData, sessionId), record);
323
+ await this.persist(record);
317
324
  return { ok: true, deferredTurn: next };
318
325
  });
319
326
  }
@@ -335,7 +342,7 @@ export class ChatSessionStore {
335
342
  record.deferredTurn = queue[0];
336
343
  record.deferredQueueRevision = (record.deferredQueueRevision ?? 0) + 1;
337
344
  record.updatedAt = next.updatedAt;
338
- await writeSecureJson(recordPath(this.appData, sessionId), record);
345
+ await this.persist(record);
339
346
  return next;
340
347
  });
341
348
  }
@@ -356,7 +363,7 @@ export class ChatSessionStore {
356
363
  record.deferredTurn = queue[0];
357
364
  record.deferredQueueRevision = (record.deferredQueueRevision ?? 0) + 1;
358
365
  record.updatedAt = new Date().toISOString();
359
- await writeSecureJson(recordPath(this.appData, sessionId), record);
366
+ await this.persist(record);
360
367
  return { ok: true, deferredTurn: existing };
361
368
  });
362
369
  }
@@ -373,7 +380,7 @@ export class ChatSessionStore {
373
380
  record.deferredTurn = queue[0];
374
381
  record.deferredQueueRevision = (record.deferredQueueRevision ?? 0) + 1;
375
382
  record.updatedAt = new Date().toISOString();
376
- await writeSecureJson(recordPath(this.appData, sessionId), record);
383
+ await this.persist(record);
377
384
  return { deferredTurn: existing, clearedRevision: existing.revision + 1 };
378
385
  });
379
386
  }
@@ -392,7 +399,7 @@ export class ChatSessionStore {
392
399
  record.displayTitle = cleanChatTitle(stored.text);
393
400
  }
394
401
  record.updatedAt = stored.at;
395
- await writeSecureJson(recordPath(this.appData, sessionId), record);
402
+ await this.persist(record);
396
403
  return stored;
397
404
  });
398
405
  }
@@ -403,7 +410,7 @@ export class ChatSessionStore {
403
410
  return;
404
411
  record.toolInvocations.push({ ...entry, at: new Date().toISOString() });
405
412
  record.updatedAt = new Date().toISOString();
406
- await writeSecureJson(recordPath(this.appData, sessionId), record);
413
+ await this.persist(record);
407
414
  });
408
415
  }
409
416
  }
@@ -501,10 +508,12 @@ export async function runChatTurn(options) {
501
508
  },
502
509
  };
503
510
  }
504
- const userMessage = await store.appendMessage(sessionId, { role: "user", text });
505
- const afterUserMessage = await store.get(sessionId);
506
- if (afterUserMessage)
507
- await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
511
+ if (options.persistUserMessage !== false) {
512
+ const userMessage = await store.appendMessage(sessionId, { role: "user", text });
513
+ const afterUserMessage = await store.get(sessionId);
514
+ if (afterUserMessage)
515
+ await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
516
+ }
508
517
  let turnError;
509
518
  // Collect persistence promises so runChatTurn does not resolve before the
510
519
  // chat history / audit trail is durably written (also makes the outcome
@@ -44,6 +44,13 @@ export const SHORTCUTS = [
44
44
  ];
45
45
  /** Pi Web commands with real browser-equivalent behavior (UI-11). */
46
46
  export const WEB_SLASH_COMMANDS = [
47
+ {
48
+ command: "/goal",
49
+ label: "持续目标",
50
+ description: "创建或编辑一个跨多个回合持续推进的目标",
51
+ action: "goal",
52
+ source: "web",
53
+ },
47
54
  {
48
55
  command: "/compact",
49
56
  label: "压缩上下文",
@@ -34,17 +34,21 @@ export function hydrateSessionThread(raw) {
34
34
  for (const item of raw) {
35
35
  const createdAt = Date.parse(item.at) || Date.now();
36
36
  if (item.role === "user") {
37
- turnId = `restored-${item.id}`;
37
+ turnId = item.turnId ?? `restored-${item.id}`;
38
38
  pendingTools = [];
39
39
  messages.push({
40
40
  id: item.id,
41
41
  role: "user",
42
42
  text: item.text,
43
43
  createdAt,
44
+ ...(item.eventSeq === undefined ? {} : { eventSeq: item.eventSeq }),
45
+ ...(item.turnId ? { turnId: item.turnId } : {}),
44
46
  });
45
47
  continue;
46
48
  }
47
49
  if (item.role === "assistant") {
50
+ if (item.turnId)
51
+ turnId = item.turnId;
48
52
  if (!turnId)
49
53
  turnId = `restored-${item.id}`;
50
54
  const content = parseAssistantContent(item.content);
@@ -53,6 +57,8 @@ export function hydrateSessionThread(raw) {
53
57
  role: "assistant",
54
58
  text: item.text,
55
59
  createdAt,
60
+ ...(item.eventSeq === undefined ? {} : { eventSeq: item.eventSeq }),
61
+ ...(item.turnId ? { turnId: item.turnId } : {}),
56
62
  ...(item.kind === "provider-error" && item.providerError
57
63
  ? { kind: item.kind, providerError: item.providerError }
58
64
  : {}),
@@ -62,6 +68,8 @@ export function hydrateSessionThread(raw) {
62
68
  continue;
63
69
  }
64
70
  if (item.role === "tool") {
71
+ if (item.turnId)
72
+ turnId = item.turnId;
65
73
  if (!turnId)
66
74
  turnId = `restored-tool-${item.id}`;
67
75
  const entry = {
@@ -72,7 +80,11 @@ export function hydrateSessionThread(raw) {
72
80
  result: item.text,
73
81
  createdAt,
74
82
  finishedAt: createdAt,
75
- turnId,
83
+ turnId: item.turnId ?? turnId,
84
+ ...(item.eventSeq === undefined ? {} : { eventSeq: item.eventSeq }),
85
+ ...(item.finishedSeq === undefined
86
+ ? {}
87
+ : { finishedSeq: item.finishedSeq }),
76
88
  ...(item.operationId ? { operationId: item.operationId } : {}),
77
89
  };
78
90
  toolCalls.push(entry);
@@ -196,6 +208,8 @@ function fillProcessDetails(group) {
196
208
  group.answer = finalAssistant;
197
209
  return;
198
210
  }
211
+ if (split.answerBlocks.length === 0)
212
+ return;
199
213
  const answerText = joinAssistantText(split.answerBlocks);
200
214
  group.answer = {
201
215
  ...finalAssistant,
@@ -207,7 +221,22 @@ function fillProcessDetails(group) {
207
221
  * Build user → process → answer groups for the message timeline.
208
222
  */
209
223
  export function buildTurnProcessGroups(input) {
210
- const visible = input.messages.filter((m) => m.role === "user" || m.role === "assistant");
224
+ const visible = input.messages
225
+ .filter((m) => m.role === "user" || m.role === "assistant")
226
+ .sort((a, b) => {
227
+ if (a.eventSeq !== undefined &&
228
+ b.eventSeq !== undefined &&
229
+ a.eventSeq !== b.eventSeq) {
230
+ return a.eventSeq - b.eventSeq;
231
+ }
232
+ if (a.createdAt !== b.createdAt)
233
+ return a.createdAt - b.createdAt;
234
+ if (a.eventSeq !== undefined && b.eventSeq === undefined)
235
+ return 1;
236
+ if (a.eventSeq === undefined && b.eventSeq !== undefined)
237
+ return -1;
238
+ return 0;
239
+ });
211
240
  const groups = [];
212
241
  let current = null;
213
242
  const startGroup = (key) => {
@@ -1148,14 +1148,14 @@ export async function dispatchOperatorAction(ctx, req) {
1148
1148
  if (usesUnifiedSurface) {
1149
1149
  const inspectPath = target
1150
1150
  ? buildInspectDeepLink(consoleOrigin, target).slice(consoleOrigin.length)
1151
- : "/inspect/#/";
1151
+ : "/#/inspect";
1152
1152
  return {
1153
1153
  kind: "sync",
1154
1154
  status: 200,
1155
1155
  body: operatorSucceeded("observeLink", {
1156
1156
  url: target
1157
1157
  ? buildInspectDeepLink(consoleOrigin, target)
1158
- : `${consoleOrigin.replace(/\/+$/, "")}/inspect/#/`,
1158
+ : `${consoleOrigin.replace(/\/+$/, "")}/#/inspect`,
1159
1159
  inspectPath,
1160
1160
  status: "same-origin",
1161
1161
  requiredCapability: requiredCapability ?? null,
@@ -1817,6 +1817,18 @@ export async function dispatchOperatorAction(ctx, req) {
1817
1817
  const taskIdError = taskIdInputError(taskId);
1818
1818
  if (taskIdError)
1819
1819
  return invalid(action, taskIdError);
1820
+ // Optional write-set gate approval: the token is the server-issued
1821
+ // `<gate-id>:sha256:<hex>` that `task status` reports in
1822
+ // gate.approveCommand. Forwarded as --approve-gate so an operator can
1823
+ // complete the human approval through the Console operation path
1824
+ // instead of dropping to a raw terminal. This dispatch already sits
1825
+ // behind the Console mutation gate (boot cookie + confirmation
1826
+ // header), so it carries the same authority as typing the CLI flag.
1827
+ const approveGateRaw = str(p.approveGate);
1828
+ if (approveGateRaw &&
1829
+ !/^[A-Za-z0-9._-]+:sha256:[0-9a-f]{64}$/.test(approveGateRaw)) {
1830
+ return invalid(action, "approveGate must look like <gate-id>:sha256:<64-hex> (copy the token from task status gate.approveCommand)");
1831
+ }
1820
1832
  const args = ["task", "advance", taskId];
1821
1833
  const title = str(p.title);
1822
1834
  if (title)
@@ -1830,6 +1842,8 @@ export async function dispatchOperatorAction(ctx, req) {
1830
1842
  const profile = str(p.profile);
1831
1843
  if (profile)
1832
1844
  args.push("--profile", profile);
1845
+ if (approveGateRaw)
1846
+ args.push("--approve-gate", approveGateRaw);
1833
1847
  appendEngineeringCliArgs(args, parseEngineeringBoundaryFromParams(p));
1834
1848
  args.push("--json");
1835
1849
  return acceptOperation(ctx, {
@@ -27,7 +27,11 @@ export function encodeInspectHash(selection) {
27
27
  }
28
28
  }
29
29
  export function buildInspectPath(selection) {
30
- return `/inspect/${selection ? encodeInspectHash(selection) : "#/"}`;
30
+ if (!selection)
31
+ return "/#/inspect";
32
+ const hash = encodeInspectHash(selection);
33
+ const internal = hash.startsWith("#") ? hash.slice(1) : hash;
34
+ return `/#/inspect${internal}`;
31
35
  }
32
36
  export function buildInspectUrl(origin, selection) {
33
37
  const base = origin.trim().replace(/\/+$/, "");
@@ -99,7 +103,7 @@ export function selectionFromObserveLinkTarget(target) {
99
103
  export function normalizeLegacyConsoleDagHash(pathnameAndHash) {
100
104
  const [pathname, hash = ""] = pathnameAndHash.split("#", 2);
101
105
  if ((pathname === "/" || pathname === "") && /^\/dag\/[^/]+$/.test(hash)) {
102
- return `/inspect/#${hash}`;
106
+ return `/#/inspect${hash}`;
103
107
  }
104
108
  return pathnameAndHash;
105
109
  }