@ai-agent-forge/agent-forge 0.88.4 → 0.88.6

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 (198) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/bundled-plugins/agent-loop-policy/agent-forge.json +1 -1
  3. package/bundled-plugins/agent-loop-policy/plugin.json +1 -1
  4. package/bundled-plugins/alignment/agent-forge.json +19 -0
  5. package/bundled-plugins/alignment/dist/baseline.d.ts +12 -0
  6. package/bundled-plugins/alignment/dist/baseline.d.ts.map +1 -0
  7. package/bundled-plugins/alignment/dist/baseline.js +68 -0
  8. package/bundled-plugins/alignment/dist/baseline.js.map +1 -0
  9. package/bundled-plugins/alignment/dist/engine.d.ts +102 -0
  10. package/bundled-plugins/alignment/dist/engine.d.ts.map +1 -0
  11. package/bundled-plugins/alignment/dist/engine.js +654 -0
  12. package/bundled-plugins/alignment/dist/engine.js.map +1 -0
  13. package/bundled-plugins/alignment/dist/entry.d.ts +14 -0
  14. package/bundled-plugins/alignment/dist/entry.d.ts.map +1 -0
  15. package/bundled-plugins/alignment/dist/entry.js +23 -0
  16. package/bundled-plugins/alignment/dist/entry.js.map +1 -0
  17. package/bundled-plugins/alignment/dist/export.d.ts +15 -0
  18. package/bundled-plugins/alignment/dist/export.d.ts.map +1 -0
  19. package/bundled-plugins/alignment/dist/export.js +122 -0
  20. package/bundled-plugins/alignment/dist/export.js.map +1 -0
  21. package/bundled-plugins/alignment/dist/http-server.d.ts +33 -0
  22. package/bundled-plugins/alignment/dist/http-server.d.ts.map +1 -0
  23. package/bundled-plugins/alignment/dist/http-server.js +119 -0
  24. package/bundled-plugins/alignment/dist/http-server.js.map +1 -0
  25. package/bundled-plugins/alignment/dist/index.d.ts +24 -0
  26. package/bundled-plugins/alignment/dist/index.d.ts.map +1 -0
  27. package/bundled-plugins/alignment/dist/index.js +719 -0
  28. package/bundled-plugins/alignment/dist/index.js.map +1 -0
  29. package/bundled-plugins/alignment/dist/page.d.ts +15 -0
  30. package/bundled-plugins/alignment/dist/page.d.ts.map +1 -0
  31. package/bundled-plugins/alignment/dist/page.js +832 -0
  32. package/bundled-plugins/alignment/dist/page.js.map +1 -0
  33. package/bundled-plugins/alignment/dist/render.d.ts +40 -0
  34. package/bundled-plugins/alignment/dist/render.d.ts.map +1 -0
  35. package/bundled-plugins/alignment/dist/render.js +194 -0
  36. package/bundled-plugins/alignment/dist/render.js.map +1 -0
  37. package/bundled-plugins/alignment/dist/types.d.ts +217 -0
  38. package/bundled-plugins/alignment/dist/types.d.ts.map +1 -0
  39. package/bundled-plugins/alignment/dist/types.js +30 -0
  40. package/bundled-plugins/alignment/dist/types.js.map +1 -0
  41. package/bundled-plugins/alignment/plugin.json +12 -0
  42. package/bundled-plugins/browser-playwright/MANUAL.md +60 -0
  43. package/bundled-plugins/browser-playwright/agent-forge.json +25 -0
  44. package/bundled-plugins/browser-playwright/dist/capability.d.ts +25 -0
  45. package/bundled-plugins/browser-playwright/dist/capability.d.ts.map +1 -0
  46. package/bundled-plugins/browser-playwright/dist/capability.js +612 -0
  47. package/bundled-plugins/browser-playwright/dist/capability.js.map +1 -0
  48. package/bundled-plugins/browser-playwright/dist/entry.d.ts +13 -0
  49. package/bundled-plugins/browser-playwright/dist/entry.d.ts.map +1 -0
  50. package/bundled-plugins/browser-playwright/dist/entry.js +14 -0
  51. package/bundled-plugins/browser-playwright/dist/entry.js.map +1 -0
  52. package/bundled-plugins/browser-playwright/dist/index.d.ts +7 -0
  53. package/bundled-plugins/browser-playwright/dist/index.d.ts.map +1 -0
  54. package/bundled-plugins/browser-playwright/dist/index.js +5 -0
  55. package/bundled-plugins/browser-playwright/dist/index.js.map +1 -0
  56. package/bundled-plugins/browser-playwright/dist/operations.d.ts +69 -0
  57. package/bundled-plugins/browser-playwright/dist/operations.d.ts.map +1 -0
  58. package/bundled-plugins/browser-playwright/dist/operations.js +149 -0
  59. package/bundled-plugins/browser-playwright/dist/operations.js.map +1 -0
  60. package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts +275 -0
  61. package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts.map +1 -0
  62. package/bundled-plugins/browser-playwright/dist/playwright-driver.js +953 -0
  63. package/bundled-plugins/browser-playwright/dist/playwright-driver.js.map +1 -0
  64. package/bundled-plugins/browser-playwright/dist/setup.d.ts +88 -0
  65. package/bundled-plugins/browser-playwright/dist/setup.d.ts.map +1 -0
  66. package/bundled-plugins/browser-playwright/dist/setup.js +252 -0
  67. package/bundled-plugins/browser-playwright/dist/setup.js.map +1 -0
  68. package/bundled-plugins/browser-playwright/dist/types.d.ts +270 -0
  69. package/bundled-plugins/browser-playwright/dist/types.d.ts.map +1 -0
  70. package/bundled-plugins/browser-playwright/dist/types.js +2 -0
  71. package/bundled-plugins/browser-playwright/dist/types.js.map +1 -0
  72. package/bundled-plugins/browser-playwright/plugin.json +20 -0
  73. package/bundled-plugins/coding-tools/agent-forge.json +1 -1
  74. package/bundled-plugins/coding-tools/dist/bash.d.ts +2 -7
  75. package/bundled-plugins/coding-tools/dist/bash.d.ts.map +1 -1
  76. package/bundled-plugins/coding-tools/dist/bash.js +2 -34
  77. package/bundled-plugins/coding-tools/dist/bash.js.map +1 -1
  78. package/bundled-plugins/coding-tools/plugin.json +1 -1
  79. package/bundled-plugins/compaction-policy/agent-forge.json +1 -1
  80. package/bundled-plugins/compaction-policy/plugin.json +1 -1
  81. package/bundled-plugins/mcp-http/dist/mcp-http.d.ts.map +1 -1
  82. package/bundled-plugins/mcp-http/dist/mcp-http.js +84 -31
  83. package/bundled-plugins/mcp-http/dist/mcp-http.js.map +1 -1
  84. package/bundled-plugins/overflow-policy/agent-forge.json +1 -1
  85. package/bundled-plugins/overflow-policy/plugin.json +1 -1
  86. package/bundled-plugins/provider-failover/agent-forge.json +1 -1
  87. package/bundled-plugins/provider-failover/plugin.json +1 -1
  88. package/bundled-plugins/retry-policy/agent-forge.json +1 -1
  89. package/bundled-plugins/retry-policy/plugin.json +1 -1
  90. package/dist/bundle/.build-manifest.json +60 -58
  91. package/dist/bundle/chunks/{anthropic-messages-3FJHRAJL.js → anthropic-messages-NL4Y6WPB.js} +1 -1
  92. package/dist/bundle/chunks/{azure-openai-responses-GCL47IDS.js → azure-openai-responses-CPXC42HO.js} +1 -1
  93. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  94. package/dist/bundle/chunks/chunk-5LRQFZFE.js +2 -0
  95. package/dist/bundle/chunks/{chunk-HTEVUY7F.js → chunk-7HSJPQKN.js} +1 -1
  96. package/dist/bundle/chunks/{chunk-AC2FIS4G.js → chunk-EDO7Q6XR.js} +1 -1
  97. package/dist/bundle/chunks/{chunk-755VNWIF.js → chunk-ISLHFRER.js} +6 -6
  98. package/dist/bundle/chunks/{chunk-HV64ASMT.js → chunk-K3S6GTL7.js} +1 -1
  99. package/dist/bundle/chunks/{chunk-HEL6TAWE.js → chunk-POLDAUYA.js} +37 -36
  100. package/dist/bundle/chunks/{chunk-V3ACW7VD.js → chunk-WJSKB3FC.js} +1 -1
  101. package/dist/bundle/chunks/{chunk-32YDZI4J.js → chunk-ZS5JEI76.js} +1 -1
  102. package/dist/bundle/chunks/{google-generative-ai-6BMXWA44.js → google-generative-ai-UMZ53EFA.js} +1 -1
  103. package/dist/bundle/chunks/{google-vertex-6H7NLRTK.js → google-vertex-ZNR5NS6H.js} +1 -1
  104. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  105. package/dist/bundle/chunks/{mistral-conversations-IXUHSFSJ.js → mistral-conversations-IT66WUNN.js} +1 -1
  106. package/dist/bundle/chunks/{openai-codex-responses-YHUAEVCM.js → openai-codex-responses-FOVCVG6I.js} +1 -1
  107. package/dist/bundle/chunks/{openai-completions-ULQCFC72.js → openai-completions-FLQ7CBOL.js} +1 -1
  108. package/dist/bundle/chunks/{openai-responses-NLLAJDPS.js → openai-responses-ABGYHS2T.js} +1 -1
  109. package/dist/bundle/chunks/{pi-messages-EJTI2EJ3.js → pi-messages-6K5W6LUN.js} +1 -1
  110. package/dist/bundle/chunks/{virtual-modules-TQWEGNNZ.js → virtual-modules-2QGILFV7.js} +1 -1
  111. package/dist/bundle/cli.js +1 -1
  112. package/dist/bundle/client.js +1 -1
  113. package/dist/bundle/index.js +1 -1
  114. package/dist/bundle/rpc-entry.js +1 -1
  115. package/dist/client/remote-session.d.ts.map +1 -1
  116. package/dist/client/remote-session.js +65 -6
  117. package/dist/client/remote-session.js.map +1 -1
  118. package/dist/core/agent-session-store.d.ts.map +1 -1
  119. package/dist/core/agent-session-store.js +20 -3
  120. package/dist/core/agent-session-store.js.map +1 -1
  121. package/dist/core/extensions/agent-composition.d.ts.map +1 -1
  122. package/dist/core/extensions/agent-composition.js +20 -1
  123. package/dist/core/extensions/agent-composition.js.map +1 -1
  124. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -1
  125. package/dist/core/i18n/locales/zh-cn/core-errors.js +1 -0
  126. package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -1
  127. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -1
  128. package/dist/core/i18n/locales/zh-cn/interactive-mode.js +10 -3
  129. package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -1
  130. package/dist/core/keybindings.d.ts +35 -0
  131. package/dist/core/keybindings.d.ts.map +1 -1
  132. package/dist/core/keybindings.js +34 -0
  133. package/dist/core/keybindings.js.map +1 -1
  134. package/dist/core/sdk.d.ts.map +1 -1
  135. package/dist/core/sdk.js +62 -15
  136. package/dist/core/sdk.js.map +1 -1
  137. package/dist/core/session-manager.d.ts.map +1 -1
  138. package/dist/core/session-manager.js +11 -3
  139. package/dist/core/session-manager.js.map +1 -1
  140. package/dist/core/system-prompt.d.ts.map +1 -1
  141. package/dist/core/system-prompt.js +2 -2
  142. package/dist/core/system-prompt.js.map +1 -1
  143. package/dist/core/tools/capability-mount.d.ts.map +1 -1
  144. package/dist/core/tools/capability-mount.js +4 -21
  145. package/dist/core/tools/capability-mount.js.map +1 -1
  146. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  147. package/dist/modes/interactive/components/assistant-message.js +2 -2
  148. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  149. package/dist/modes/interactive/components/bash-execution.d.ts +2 -0
  150. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  151. package/dist/modes/interactive/components/bash-execution.js +25 -0
  152. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  153. package/dist/modes/interactive/components/changelog-pager.d.ts +53 -0
  154. package/dist/modes/interactive/components/changelog-pager.d.ts.map +1 -0
  155. package/dist/modes/interactive/components/changelog-pager.js +239 -0
  156. package/dist/modes/interactive/components/changelog-pager.js.map +1 -0
  157. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  158. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/tool-execution.js +12 -0
  160. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/tool-run-group.d.ts +41 -11
  162. package/dist/modes/interactive/components/tool-run-group.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/tool-run-group.js +126 -14
  164. package/dist/modes/interactive/components/tool-run-group.js.map +1 -1
  165. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  166. package/dist/modes/interactive/interactive-commands.js +23 -13
  167. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  168. package/dist/modes/interactive/interactive-events.d.ts +14 -4
  169. package/dist/modes/interactive/interactive-events.d.ts.map +1 -1
  170. package/dist/modes/interactive/interactive-events.js +94 -12
  171. package/dist/modes/interactive/interactive-events.js.map +1 -1
  172. package/dist/modes/interactive/interactive-mode.d.ts +6 -1
  173. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  174. package/dist/modes/interactive/interactive-mode.js +21 -5
  175. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  176. package/dist/plugins/ecosystem/node-local-package-source.d.ts +2 -1
  177. package/dist/plugins/ecosystem/node-local-package-source.d.ts.map +1 -1
  178. package/dist/plugins/ecosystem/node-local-package-source.js +110 -4
  179. package/dist/plugins/ecosystem/node-local-package-source.js.map +1 -1
  180. package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
  181. package/dist/plugins/ecosystem/node-package-install-host.js +106 -39
  182. package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
  183. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  184. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +59 -2
  185. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  186. package/dist/suites/builtin-suites.d.ts.map +1 -1
  187. package/dist/suites/builtin-suites.js +2 -2
  188. package/dist/suites/builtin-suites.js.map +1 -1
  189. package/dist/utils/changelog.d.ts +27 -0
  190. package/dist/utils/changelog.d.ts.map +1 -1
  191. package/dist/utils/changelog.js +110 -0
  192. package/dist/utils/changelog.js.map +1 -1
  193. package/docs/keybindings.md +14 -0
  194. package/examples/extensions/with-deps/package-lock.json +2 -2
  195. package/examples/extensions/with-deps/package.json +1 -1
  196. package/examples/extensions/with-deps/plugin.json +1 -1
  197. package/package.json +1 -1
  198. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
@@ -6,6 +6,15 @@ class RemoteSessionDisposedError extends Error {
6
6
  this.name = "RemoteSessionDisposedError";
7
7
  }
8
8
  }
9
+ /**
10
+ * Server leases expire unless renewed (no sliding renewal on the server), so an
11
+ * attached session renews at one third of the remaining TTL. A failing renewal
12
+ * is retried at a fixed short interval; after three consecutive failures the
13
+ * lease is presumed reclaimed and the session is unbound like a server-removed
14
+ * session.
15
+ */
16
+ const RENEW_RETRY_INTERVAL_MS = 5_000;
17
+ const MAX_CONSECUTIVE_RENEW_FAILURES = 3;
9
18
  async function settleRemoteSessionDisposal(cleanup) {
10
19
  const results = await Promise.allSettled(cleanup);
11
20
  const errors = results.flatMap((result) => result.status === "rejected" && !(result.reason instanceof RemoteSessionDisposedError) ? [result.reason] : []);
@@ -22,6 +31,8 @@ export class RemoteSession {
22
31
  #transcript;
23
32
  #unsubscribeSnapshot;
24
33
  #unsubscribeEvents;
34
+ #renewTimer;
35
+ #renewFailures = 0;
25
36
  #listeners = new Set();
26
37
  #pendingAttachmentOperations = new Set();
27
38
  #activeOperationStates = new Set();
@@ -153,6 +164,7 @@ export class RemoteSession {
153
164
  const handle = this.#handle;
154
165
  this.#lifecycle = { status: "disposed" };
155
166
  this.#resolveDisposeSignal();
167
+ this.#stopLeaseRenewal();
156
168
  this.#clearSubscriptions();
157
169
  this.#handle = undefined;
158
170
  this.#transcript = undefined;
@@ -265,6 +277,7 @@ export class RemoteSession {
265
277
  if (!snapshot)
266
278
  throw new Error(`Session ${handle.id} did not provide a snapshot`);
267
279
  this.#clearSubscriptions();
280
+ this.#stopLeaseRenewal();
268
281
  this.#handle = handle;
269
282
  this.#transcript = createTranscriptState(snapshot);
270
283
  this.#unsubscribeSnapshot = handle.subscribe((next) => {
@@ -274,15 +287,11 @@ export class RemoteSession {
274
287
  this.#notify();
275
288
  });
276
289
  this.#unsubscribeEvents = handle.onEvent((event) => this.#handleEvent(event));
290
+ this.#scheduleLeaseRenewal(handle);
277
291
  }
278
292
  #handleEvent(event) {
279
293
  if (event.type === "session_removed") {
280
- this.#clearSubscriptions();
281
- this.#handle = undefined;
282
- this.#transcript = undefined;
283
- if (this.#lifecycle.status !== "busy")
284
- this.#lifecycle = { status: "unbound" };
285
- this.#notify();
294
+ this.#unbindSession();
286
295
  return;
287
296
  }
288
297
  if (event.type !== "session_progress" || !this.#transcript)
@@ -290,6 +299,56 @@ export class RemoteSession {
290
299
  this.#transcript = applyTranscriptProgress(this.#transcript, event.progress);
291
300
  this.#notify();
292
301
  }
302
+ /**
303
+ * Session-level invalidation shared by the server `session_removed` event and
304
+ * unrecoverable lease renewals: subscriptions stop, the handle is dropped and
305
+ * the session returns to the unbound lifecycle.
306
+ */
307
+ #unbindSession() {
308
+ this.#clearSubscriptions();
309
+ this.#stopLeaseRenewal();
310
+ this.#handle = undefined;
311
+ this.#transcript = undefined;
312
+ if (this.#lifecycle.status !== "busy")
313
+ this.#lifecycle = { status: "unbound" };
314
+ this.#notify();
315
+ }
316
+ #scheduleLeaseRenewal(handle) {
317
+ const expiresAt = handle.leaseExpiresAt;
318
+ if (expiresAt === undefined)
319
+ return;
320
+ const remaining = Math.max(0, expiresAt - Date.now());
321
+ this.#renewTimer = setTimeout(() => void this.#renewLease(handle), Math.floor(remaining / 3));
322
+ }
323
+ async #renewLease(handle) {
324
+ this.#renewTimer = undefined;
325
+ if (this.disposed || this.#handle !== handle)
326
+ return;
327
+ try {
328
+ await handle.renew();
329
+ if (this.disposed || this.#handle !== handle)
330
+ return;
331
+ this.#renewFailures = 0;
332
+ this.#scheduleLeaseRenewal(handle);
333
+ }
334
+ catch {
335
+ if (this.disposed || this.#handle !== handle)
336
+ return;
337
+ this.#renewFailures += 1;
338
+ if (this.#renewFailures >= MAX_CONSECUTIVE_RENEW_FAILURES) {
339
+ this.#unbindSession();
340
+ return;
341
+ }
342
+ this.#renewTimer = setTimeout(() => void this.#renewLease(handle), RENEW_RETRY_INTERVAL_MS);
343
+ }
344
+ }
345
+ #stopLeaseRenewal() {
346
+ if (this.#renewTimer !== undefined) {
347
+ clearTimeout(this.#renewTimer);
348
+ this.#renewTimer = undefined;
349
+ }
350
+ this.#renewFailures = 0;
351
+ }
293
352
  #notify() {
294
353
  const state = this.state;
295
354
  for (const listener of this.#listeners)
@@ -1 +1 @@
1
- {"version":3,"file":"remote-session.js","sourceRoot":"","sources":["../../src/client/remote-session.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,uBAAuB,CAAC;AAC1C,OAAO,EACN,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,GAEhB,MAAM,iBAAiB,CAAC;AA0BzB,MAAM,0BAA2B,SAAQ,KAAK;IAC7C,cAAc;QACb,KAAK,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAAA,CACzC;CACD;AAED,KAAK,UAAU,2BAA2B,CAAC,OAAiC,EAAiB;IAC5F,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CACzC,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,YAAY,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAC7G,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,kCAAkC,CAAC,CAAC;AAAA,CAC5F;AAED,MAAM,OAAO,aAAa;IAChB,OAAO,CAAW;IAClB,gBAAgB,CAAuC;IAChE,UAAU,GAA2B,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC3D,OAAO,CAA2B;IAClC,WAAW,CAA8B;IACzC,oBAAoB,CAA0B;IAC9C,kBAAkB,CAA0B;IACnC,UAAU,GAAG,IAAI,GAAG,EAAuC,CAAC;IAC5D,4BAA4B,GAAG,IAAI,GAAG,EAAiB,CAAC;IACxD,sBAAsB,GAAG,IAAI,GAAG,EAA0B,CAAC;IACpE,eAAe,CAA4B;IAC3C,qBAAqB,GAAe,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC;IACpC,cAAc,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC;IAAA,CACrC,CAAC,CAAC;IAEH,YAAoB,MAAgB,EAAE,OAAO,GAAyB,EAAE,EAAE;QACzE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAAA,CAChD;IAED,IAAI,EAAE,GAAuB;QAC5B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;IAAA,CACxB;IAED,IAAI,KAAK,GAAuB;QAC/B,OAAO;YACN,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ;YACpC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE;SACtE,CAAC;IAAA,CACF;IAED,IAAI,QAAQ,GAAgC;QAC3C,OAAO,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC;IAAA,CAClC;IAED,IAAI,KAAK,GAA6B;QACrC,OAAO,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAA,CAC5B;IAED,IAAI,SAAS,GAAuC;QACnD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAAA,CACjF;IAED,IAAI,MAAM,GAA6B;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;IAAA,CAC3C;IAED,IAAI,QAAQ,GAA+B;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE,CAAC;IAAA,CAC7C;IAED,IAAI,eAAe,GAAoB;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;IAAA,CACpC;IAED,IAAI,QAAQ,GAAY;QACvB,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;IAAA,CAC7C;IAED,SAAS,CAAC,QAA6C,EAAe;QACrE,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9B,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAAA,CAC9C;IAED,uBAAuB,CAAC,QAAiD,EAAe;QACvF,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAAA,CACtD;IAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAgB,EAAE,SAAiB,EAAE,OAAO,GAAyB,EAAE,EAA0B;QAClH,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9B,OAAO,OAAO,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,MAAM,KAAK,CAAC;QACb,CAAC;IAAA,CACD;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB,EAAiB;QAC5C,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO;QACjF,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IAAA,CACjG;IAED,MAAM,CAAC,KAAK,CAAC,MAAM,CAClB,MAAgB,EAChB,aAAyC,EACzC,OAAO,GAAyB,EAAE,EACT;QACzB,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACpC,OAAO,OAAO,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,MAAM,KAAK,CAAC;QACb,CAAC;IAAA,CACD;IAED,KAAK,CAAC,MAAM,CAAC,OAAmC,EAAiB;QAChE,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IAAA,CACzE;IAED,KAAK,CAAC,MAAM,CAAC,IAAY,EAAiB;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,kDAAkD,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;QAC5G,CAAC;QACD,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,EAAE,CACvC,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CACpG,CAAC;IAAA,CACF;IAED,KAAK,CAAC,KAAK,GAAkB;QAC5B,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,KAAK,QAAQ,CAAC;QACrG,IAAI,gBAAgB;YAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;;YAC3C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,gBAAgB;YAAE,OAAO;QACvD,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAAA,CAChG;IAED,KAAK,CAAC,QAAQ,CAAC,KAAe,EAAiB;QAC9C,MAAM,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,EAAE,CAC7D,IAAI,CAAC,cAAc,EAAE;aACnB,QAAQ,CAAC,KAAK,CAAC;aACf,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CACvB,CAAC;IAAA,CACF;IAED,KAAK,CAAC,WAAW,CAAC,aAA4B,EAAiB;QAC9D,MAAM,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,uBAAuB,EAAE,GAAG,EAAE,CACzE,IAAI,CAAC,cAAc,EAAE;aACnB,WAAW,CAAC,aAAa,CAAC;aAC1B,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CACvB,CAAC;IAAA,CACF;IAED,KAAK,CAAC,SAAS,GAAkB;QAChC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,GAAG,EAAE,CAC1C,IAAI,CAAC,yBAAyB,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1C,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACnF,MAAM,IAAI,CAAC,4BAA4B,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAA,CACnB,CAAC,CACF,CAAC;IAAA,CACF;IAED,OAAO,GAAkB;QACxB,IAAI,IAAI,CAAC,eAAe;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QACzC,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,4BAA4B,CAAC,CAAC;QACvD,IAAI,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,eAAe,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,eAAe,CAAC;IAAA,CAC5B;IAED,CAAC,MAAM,CAAC,YAAY,CAAC,GAAkB;QACtC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IAAA,CACtB;IAED,KAAK,CAAC,QAAQ,CAAC,SAA4B,EAAE,OAAoC,EAAiB;QACjG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACd,CAAC,CAAC,uDAAuD,EAAE;gBAC1D,SAAS;gBACT,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,aAAa;aAClC,CAAC,CACF,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,GAAG,EAAE,CACxC,IAAI,CAAC,yBAAyB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAClF,CAAC;IAAA,CACF;IAED,KAAK,CAAC,yBAAyB,CAAC,GAAwB,EAAiB;QACxE,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,4BAA4B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC;QACf,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,4BAA4B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnD,CAAC;IAAA,CACD;IAED,KAAK,CAAC,mBAAmB,CAAC,SAA4B,EAAE,OAAoC,EAAiB;QAC5G,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,yCAAyC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,QAAQ,IAAI,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACvF,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,IAAI,KAAK,CACd,CAAC,CAAC,uDAAuD,EAAE;gBAC1D,SAAS;gBACT,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,aAAa;aAClC,CAAC,CACF,CAAC;QACH,CAAC;QACD,IAAI,QAAQ,IAAI,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAC9D,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;YACzB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC;oBACJ,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC1B,CAAC;gBAAC,OAAO,YAAY,EAAE,CAAC;oBACvB,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,6CAA6C,CAAC,CAAC;gBAChG,CAAC;gBACD,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;QACD,MAAM,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAAA,CAC3B;IAED,KAAK,CAAC,iBAAiB,CACtB,SAAqC,EACrC,WAAmB,EACnB,GAAwB,EACR;QAChB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACd,CAAC,CAAC,+CAA+C,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,aAAa,EAAE,CAAC,CACvG,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAAA,CACzC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiC,EAAE,GAAwB,EAAE,OAAO,GAAG,KAAK,EAAiB;QAChH,IAAI,OAAO;YAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;;YAClC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;QACjC,MAAM,IAAI,GAA2B,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACnE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,IAAI,CAAC;gBAClB,OAAO;gBACP,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;oBAC9B,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC;gBAAA,CACjD,CAAC;aACF,CAAC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBAChD,IAAI,CAAC,UAAU;oBACd,OAAO,IAAI,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC;wBACnD,CAAC,CAAC,QAAQ;wBACV,CAAC,CAAC,IAAI,CAAC,OAAO;4BACb,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE;4BACrB,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,CAAC;QACF,CAAC;IAAA,CACD;IAED,KAAK,CAAC,MAAoB,EAAE,aAA+B,EAAQ;QAClE,MAAM,QAAQ,GAAG,aAAa,IAAI,MAAM,CAAC,QAAQ,CAAC;QAClD,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,CAAC,EAAE,6BAA6B,CAAC,CAAC;QAClF,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,oBAAoB,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACtD,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,OAAO;YAC9B,IAAI,CAAC,WAAW,GAAG,uBAAuB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACnE,IAAI,CAAC,OAAO,EAAE,CAAC;QAAA,CACf,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IAAA,CAC9E;IAED,YAAY,CAAC,KAAkB,EAAQ;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YACtC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC7B,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM;gBAAE,IAAI,CAAC,UAAU,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC/E,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO;QACR,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QACnE,IAAI,CAAC,WAAW,GAAG,uBAAuB,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7E,IAAI,CAAC,OAAO,EAAE,CAAC;IAAA,CACf;IAED,OAAO,GAAS;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAAA,CAC5E;IAED,aAAa,CAAC,QAA6C,EAAE,KAAyB,EAAQ;QAC7F,IAAI,CAAC;YACJ,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;IAAA,CACD;IAED,oBAAoB,CAAC,KAAc,EAAQ;QAC1C,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAO;QACnC,IAAI,CAAC;YACJ,IAAI,CAAC,gBAAgB,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClF,CAAC;QAAC,MAAM,CAAC;YACR,wDAAwD;QACzD,CAAC;IAAA,CACD;IAED,mBAAmB,GAAS;QAC3B,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;QACtC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAAA,CACpC;IAED,cAAc,GAAiB;QAC9B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC,OAAO,CAAC;IAAA,CACpB;IAED,gBAAgB,GAAS;QACxB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,yCAAyC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACzG,CAAC;IAAA,CACD;IAED,kBAAkB,GAAS;QAC1B,IAAI,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC;IAAA,CACpE;IAED,KAAK,CAAC,4BAA4B,CAAC,MAAoB,EAAiB;QACvE,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,IAAI,0BAA0B,EAAE,CAAC;IAAA,CACvC;IAED,KAAK,CAAC,OAAO,CAAC,MAAoB,EAAiB;QAClD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IAAA,CACvB;CACD","sourcesContent":["import type { ConnectionState, ConnectionStateChange, PiClient, SessionLease, Unsubscribe } from \"@agent-forge/client\";\nimport type {\n\tModelMetadata,\n\tModelRef,\n\tServerEvent,\n\tSessionMetadata,\n\tSessionPhase,\n\tSessionSnapshot,\n\tThinkingLevel,\n\tTranscriptItem,\n} from \"@agent-forge/protocol\";\nimport { t } from \"../core/i18n/index.ts\";\nimport {\n\tapplyTranscriptProgress,\n\tapplyTranscriptSnapshot,\n\tcreateTranscriptState,\n\tselectTranscript,\n\ttype TranscriptState,\n} from \"./transcript.ts\";\n\nexport type RemoteSessionOperation = \"open\" | \"create\" | \"submit\" | \"abort\" | \"setModel\" | \"setThinking\" | \"reconnect\";\n\nexport type RemoteSessionLifecycle =\n\t| { readonly status: \"unbound\" }\n\t| { readonly status: \"ready\" }\n\t| { readonly status: \"busy\"; readonly operation: RemoteSessionOperation }\n\t| { readonly status: \"disposed\" };\n\nexport interface RemoteSessionState {\n\treadonly lifecycle: RemoteSessionLifecycle;\n\treadonly snapshot?: SessionSnapshot;\n\treadonly transcript: readonly TranscriptItem[];\n}\n\nexport interface CreateRemoteSessionOptions {\n\tcwd: string;\n\tmodel?: ModelRef;\n\tthinkingLevel?: ThinkingLevel;\n}\n\nexport interface RemoteSessionOptions {\n\tonListenerError?: (error: Error) => void;\n}\n\nclass RemoteSessionDisposedError extends Error {\n\tconstructor() {\n\t\tsuper(t(\"Remote session is disposed\"));\n\t\tthis.name = \"RemoteSessionDisposedError\";\n\t}\n}\n\nasync function settleRemoteSessionDisposal(cleanup: readonly Promise<void>[]): Promise<void> {\n\tconst results = await Promise.allSettled(cleanup);\n\tconst errors = results.flatMap((result) =>\n\t\tresult.status === \"rejected\" && !(result.reason instanceof RemoteSessionDisposedError) ? [result.reason] : [],\n\t);\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose remote session\");\n}\n\nexport class RemoteSession {\n\treadonly #client: PiClient;\n\treadonly #onListenerError: ((error: Error) => void) | undefined;\n\t#lifecycle: RemoteSessionLifecycle = { status: \"unbound\" };\n\t#handle: SessionLease | undefined;\n\t#transcript: TranscriptState | undefined;\n\t#unsubscribeSnapshot: Unsubscribe | undefined;\n\t#unsubscribeEvents: Unsubscribe | undefined;\n\treadonly #listeners = new Set<(state: RemoteSessionState) => void>();\n\treadonly #pendingAttachmentOperations = new Set<Promise<void>>();\n\treadonly #activeOperationStates = new Set<RemoteSessionLifecycle>();\n\t#disposePromise: Promise<void> | undefined;\n\t#resolveDisposeSignal: () => void = () => {};\n\treadonly #disposeSignal = new Promise<void>((resolve) => {\n\t\tthis.#resolveDisposeSignal = resolve;\n\t});\n\n\tprivate constructor(client: PiClient, options: RemoteSessionOptions = {}) {\n\t\tthis.#client = client;\n\t\tthis.#onListenerError = options.onListenerError;\n\t}\n\n\tget id(): string | undefined {\n\t\treturn this.#handle?.id;\n\t}\n\n\tget state(): RemoteSessionState {\n\t\treturn {\n\t\t\tlifecycle: this.#lifecycle,\n\t\t\tsnapshot: this.#transcript?.snapshot,\n\t\t\ttranscript: this.#transcript ? selectTranscript(this.#transcript) : [],\n\t\t};\n\t}\n\n\tget snapshot(): SessionSnapshot | undefined {\n\t\treturn this.#transcript?.snapshot;\n\t}\n\n\tget phase(): SessionPhase | undefined {\n\t\treturn this.snapshot?.phase;\n\t}\n\n\tget operation(): RemoteSessionOperation | undefined {\n\t\treturn this.#lifecycle.status === \"busy\" ? this.#lifecycle.operation : undefined;\n\t}\n\n\tget models(): readonly ModelMetadata[] {\n\t\treturn this.#client.snapshot?.models ?? [];\n\t}\n\n\tget sessions(): readonly SessionMetadata[] {\n\t\treturn this.#client.snapshot?.sessions ?? [];\n\t}\n\n\tget connectionState(): ConnectionState {\n\t\treturn this.#client.connectionState;\n\t}\n\n\tget disposed(): boolean {\n\t\treturn this.#lifecycle.status === \"disposed\";\n\t}\n\n\tsubscribe(listener: (state: RemoteSessionState) => void): Unsubscribe {\n\t\tthis.#assertNotDisposed();\n\t\tthis.#listeners.add(listener);\n\t\tthis.#callListener(listener, this.state);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\tonConnectionStateChange(listener: (change: ConnectionStateChange) => void): Unsubscribe {\n\t\tthis.#assertNotDisposed();\n\t\treturn this.#client.onConnectionStateChange(listener);\n\t}\n\n\tstatic async open(client: PiClient, sessionId: string, options: RemoteSessionOptions = {}): Promise<RemoteSession> {\n\t\tconst session = new RemoteSession(client, options);\n\t\ttry {\n\t\t\tawait session.open(sessionId);\n\t\t\treturn session;\n\t\t} catch (error) {\n\t\t\tawait session.dispose();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync open(sessionId: string): Promise<void> {\n\t\tif (this.#handle?.id === sessionId && this.#lifecycle.status === \"ready\") return;\n\t\tawait this.#replace(\"open\", () => this.#client.acquireSession(sessionId, { mode: \"exclusive\" }));\n\t}\n\n\tstatic async create(\n\t\tclient: PiClient,\n\t\tcreateOptions: CreateRemoteSessionOptions,\n\t\toptions: RemoteSessionOptions = {},\n\t): Promise<RemoteSession> {\n\t\tconst session = new RemoteSession(client, options);\n\t\ttry {\n\t\t\tawait session.create(createOptions);\n\t\t\treturn session;\n\t\t} catch (error) {\n\t\t\tawait session.dispose();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync create(options: CreateRemoteSessionOptions): Promise<void> {\n\t\tawait this.#replace(\"create\", () => this.#client.createSession(options));\n\t}\n\n\tasync submit(text: string): Promise<void> {\n\t\tconst normalized = text.trim();\n\t\tif (!normalized) return;\n\t\tthis.#assertAvailable();\n\t\tconst handle = this.#requireHandle();\n\t\tif (this.phase !== \"idle\" && this.phase !== \"turn\") {\n\t\t\tthrow new Error(t(\"Session cannot accept input during {phase} phase\", { phase: this.phase ?? \"unknown\" }));\n\t\t}\n\t\tawait this.#runOperation(\"submit\", () =>\n\t\t\t(this.phase === \"idle\" ? handle.prompt(normalized) : handle.steer(normalized)).then(() => undefined),\n\t\t);\n\t}\n\n\tasync abort(): Promise<void> {\n\t\tconst preemptingSubmit = this.#lifecycle.status === \"busy\" && this.#lifecycle.operation === \"submit\";\n\t\tif (preemptingSubmit) this.#assertNotDisposed();\n\t\telse this.#assertAvailable();\n\t\tconst handle = this.#requireHandle();\n\t\tif (this.phase === \"idle\" && !preemptingSubmit) return;\n\t\tawait this.#runOperation(\"abort\", () => handle.abort().then(() => undefined), preemptingSubmit);\n\t}\n\n\tasync setModel(model: ModelRef): Promise<void> {\n\t\tawait this.#runIdleOperation(\"setModel\", \"change model\", () =>\n\t\t\tthis.#requireHandle()\n\t\t\t\t.setModel(model)\n\t\t\t\t.then(() => undefined),\n\t\t);\n\t}\n\n\tasync setThinking(thinkingLevel: ThinkingLevel): Promise<void> {\n\t\tawait this.#runIdleOperation(\"setThinking\", \"change thinking level\", () =>\n\t\t\tthis.#requireHandle()\n\t\t\t\t.setThinking(thinkingLevel)\n\t\t\t\t.then(() => undefined),\n\t\t);\n\t}\n\n\tasync reconnect(): Promise<void> {\n\t\tthis.#assertAvailable();\n\t\tconst sessionId = this.#requireHandle().id;\n\t\tawait this.#runOperation(\"reconnect\", () =>\n\t\t\tthis.#trackAttachmentOperation(async () => {\n\t\t\t\tawait this.#client.reconnect();\n\t\t\t\tconst handle = await this.#client.acquireSession(sessionId, { mode: \"exclusive\" });\n\t\t\t\tawait this.#assertNotDisposedAfterAwait(handle);\n\t\t\t\tthis.#bind(handle);\n\t\t\t}),\n\t\t);\n\t}\n\n\tdispose(): Promise<void> {\n\t\tif (this.#disposePromise) return this.#disposePromise;\n\t\tconst handle = this.#handle;\n\t\tthis.#lifecycle = { status: \"disposed\" };\n\t\tthis.#resolveDisposeSignal();\n\t\tthis.#clearSubscriptions();\n\t\tthis.#handle = undefined;\n\t\tthis.#transcript = undefined;\n\t\tconst cleanup = [...this.#pendingAttachmentOperations];\n\t\tif (handle) cleanup.push(handle.dispose());\n\t\tthis.#disposePromise = settleRemoteSessionDisposal(cleanup);\n\t\tthis.#notify();\n\t\tthis.#listeners.clear();\n\t\treturn this.#disposePromise;\n\t}\n\n\t[Symbol.asyncDispose](): Promise<void> {\n\t\treturn this.dispose();\n\t}\n\n\tasync #replace(operation: \"open\" | \"create\", prepare: () => Promise<SessionLease>): Promise<void> {\n\t\tthis.#assertAvailable();\n\t\tif (this.#handle && this.phase !== \"idle\") {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Cannot {operation} a session while session is {phase}\", {\n\t\t\t\t\toperation,\n\t\t\t\t\tphase: this.phase ?? \"unavailable\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tawait this.#runOperation(operation, () =>\n\t\t\tthis.#trackAttachmentOperation(() => this.#prepareReplacement(operation, prepare)),\n\t\t);\n\t}\n\n\tasync #trackAttachmentOperation(run: () => Promise<void>): Promise<void> {\n\t\tconst pending = run();\n\t\tthis.#pendingAttachmentOperations.add(pending);\n\t\ttry {\n\t\t\tawait pending;\n\t\t} finally {\n\t\t\tthis.#pendingAttachmentOperations.delete(pending);\n\t\t}\n\t}\n\n\tasync #prepareReplacement(operation: \"open\" | \"create\", prepare: () => Promise<SessionLease>): Promise<void> {\n\t\tconst previous = this.#handle;\n\t\tconst next = await prepare();\n\t\tawait this.#assertNotDisposedAfterAwait(next);\n\t\tconst snapshot = next.snapshot;\n\t\tif (!snapshot) {\n\t\t\tawait this.#detach(next);\n\t\t\tthrow new Error(t(\"Session {id} did not provide a snapshot\", { id: next.id }));\n\t\t}\n\t\tif (previous && previous.id !== next.id && previous.attached && this.phase !== \"idle\") {\n\t\t\tawait this.#detach(next);\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Cannot {operation} a session while session is {phase}\", {\n\t\t\t\t\toperation,\n\t\t\t\t\tphase: this.phase ?? \"unavailable\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (previous && previous.id !== next.id && previous.attached) {\n\t\t\ttry {\n\t\t\t\tawait previous.detach();\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#detach(next);\n\t\t\t\t} catch (cleanupError) {\n\t\t\t\t\tthrow new AggregateError([error, cleanupError], \"Failed to replace remote session attachment\");\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tawait this.#assertNotDisposedAfterAwait(next);\n\t\tthis.#bind(next, snapshot);\n\t}\n\n\tasync #runIdleOperation(\n\t\toperation: \"setModel\" | \"setThinking\",\n\t\tdescription: string,\n\t\trun: () => Promise<void>,\n\t): Promise<void> {\n\t\tthis.#assertAvailable();\n\t\tthis.#requireHandle();\n\t\tif (this.phase !== \"idle\") {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Cannot {description} while session is {phase}\", { description, phase: this.phase ?? \"unavailable\" }),\n\t\t\t);\n\t\t}\n\t\tawait this.#runOperation(operation, run);\n\t}\n\n\tasync #runOperation(operation: RemoteSessionOperation, run: () => Promise<void>, preempt = false): Promise<void> {\n\t\tif (preempt) this.#assertNotDisposed();\n\t\telse this.#assertAvailable();\n\t\tconst previous = this.#lifecycle;\n\t\tconst busy: RemoteSessionLifecycle = { status: \"busy\", operation };\n\t\tthis.#lifecycle = busy;\n\t\tthis.#activeOperationStates.add(busy);\n\t\tthis.#notify();\n\t\tconst running = run();\n\t\ttry {\n\t\t\tawait Promise.race([\n\t\t\t\trunning,\n\t\t\t\tthis.#disposeSignal.then(() => {\n\t\t\t\t\tthrow new Error(t(\"Remote session is disposed\"));\n\t\t\t\t}),\n\t\t\t]);\n\t\t} finally {\n\t\t\tthis.#activeOperationStates.delete(busy);\n\t\t\tif (!this.disposed && this.#lifecycle === busy) {\n\t\t\t\tthis.#lifecycle =\n\t\t\t\t\tpreempt && this.#activeOperationStates.has(previous)\n\t\t\t\t\t\t? previous\n\t\t\t\t\t\t: this.#handle\n\t\t\t\t\t\t\t? { status: \"ready\" }\n\t\t\t\t\t\t\t: { status: \"unbound\" };\n\t\t\t\tthis.#notify();\n\t\t\t}\n\t\t}\n\t}\n\n\t#bind(handle: SessionLease, knownSnapshot?: SessionSnapshot): void {\n\t\tconst snapshot = knownSnapshot ?? handle.snapshot;\n\t\tif (!snapshot) throw new Error(`Session ${handle.id} did not provide a snapshot`);\n\t\tthis.#clearSubscriptions();\n\t\tthis.#handle = handle;\n\t\tthis.#transcript = createTranscriptState(snapshot);\n\t\tthis.#unsubscribeSnapshot = handle.subscribe((next) => {\n\t\t\tif (!this.#transcript) return;\n\t\t\tthis.#transcript = applyTranscriptSnapshot(this.#transcript, next);\n\t\t\tthis.#notify();\n\t\t});\n\t\tthis.#unsubscribeEvents = handle.onEvent((event) => this.#handleEvent(event));\n\t}\n\n\t#handleEvent(event: ServerEvent): void {\n\t\tif (event.type === \"session_removed\") {\n\t\t\tthis.#clearSubscriptions();\n\t\t\tthis.#handle = undefined;\n\t\t\tthis.#transcript = undefined;\n\t\t\tif (this.#lifecycle.status !== \"busy\") this.#lifecycle = { status: \"unbound\" };\n\t\t\tthis.#notify();\n\t\t\treturn;\n\t\t}\n\t\tif (event.type !== \"session_progress\" || !this.#transcript) return;\n\t\tthis.#transcript = applyTranscriptProgress(this.#transcript, event.progress);\n\t\tthis.#notify();\n\t}\n\n\t#notify(): void {\n\t\tconst state = this.state;\n\t\tfor (const listener of this.#listeners) this.#callListener(listener, state);\n\t}\n\n\t#callListener(listener: (state: RemoteSessionState) => void, state: RemoteSessionState): void {\n\t\ttry {\n\t\t\tlistener(state);\n\t\t} catch (error) {\n\t\t\tthis.#reportListenerError(error);\n\t\t}\n\t}\n\n\t#reportListenerError(error: unknown): void {\n\t\tif (!this.#onListenerError) return;\n\t\ttry {\n\t\t\tthis.#onListenerError(error instanceof Error ? error : new Error(String(error)));\n\t\t} catch {\n\t\t\t// Diagnostics cannot affect session or transport state.\n\t\t}\n\t}\n\n\t#clearSubscriptions(): void {\n\t\tthis.#unsubscribeSnapshot?.();\n\t\tthis.#unsubscribeEvents?.();\n\t\tthis.#unsubscribeSnapshot = undefined;\n\t\tthis.#unsubscribeEvents = undefined;\n\t}\n\n\t#requireHandle(): SessionLease {\n\t\tif (!this.#handle) throw new Error(\"No remote session is attached\");\n\t\treturn this.#handle;\n\t}\n\n\t#assertAvailable(): void {\n\t\tthis.#assertNotDisposed();\n\t\tif (this.#lifecycle.status === \"busy\") {\n\t\t\tthrow new Error(t(\"Remote session is busy with {operation}\", { operation: this.#lifecycle.operation }));\n\t\t}\n\t}\n\n\t#assertNotDisposed(): void {\n\t\tif (this.disposed) throw new Error(t(\"Remote session is disposed\"));\n\t}\n\n\tasync #assertNotDisposedAfterAwait(handle: SessionLease): Promise<void> {\n\t\tif (!this.disposed) return;\n\t\tawait this.#detach(handle);\n\t\tthrow new RemoteSessionDisposedError();\n\t}\n\n\tasync #detach(handle: SessionLease): Promise<void> {\n\t\tawait handle.dispose();\n\t}\n}\n"]}
1
+ {"version":3,"file":"remote-session.js","sourceRoot":"","sources":["../../src/client/remote-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConnectionState, ConnectionStateChange, PiClient, SessionLease, Unsubscribe } from \"@agent-forge/client\";\nimport type {\n\tModelMetadata,\n\tModelRef,\n\tServerEvent,\n\tSessionMetadata,\n\tSessionPhase,\n\tSessionSnapshot,\n\tThinkingLevel,\n\tTranscriptItem,\n} from \"@agent-forge/protocol\";\nimport { t } from \"../core/i18n/index.ts\";\nimport {\n\tapplyTranscriptProgress,\n\tapplyTranscriptSnapshot,\n\tcreateTranscriptState,\n\tselectTranscript,\n\ttype TranscriptState,\n} from \"./transcript.ts\";\n\nexport type RemoteSessionOperation = \"open\" | \"create\" | \"submit\" | \"abort\" | \"setModel\" | \"setThinking\" | \"reconnect\";\n\nexport type RemoteSessionLifecycle =\n\t| { readonly status: \"unbound\" }\n\t| { readonly status: \"ready\" }\n\t| { readonly status: \"busy\"; readonly operation: RemoteSessionOperation }\n\t| { readonly status: \"disposed\" };\n\nexport interface RemoteSessionState {\n\treadonly lifecycle: RemoteSessionLifecycle;\n\treadonly snapshot?: SessionSnapshot;\n\treadonly transcript: readonly TranscriptItem[];\n}\n\nexport interface CreateRemoteSessionOptions {\n\tcwd: string;\n\tmodel?: ModelRef;\n\tthinkingLevel?: ThinkingLevel;\n}\n\nexport interface RemoteSessionOptions {\n\tonListenerError?: (error: Error) => void;\n}\n\nclass RemoteSessionDisposedError extends Error {\n\tconstructor() {\n\t\tsuper(t(\"Remote session is disposed\"));\n\t\tthis.name = \"RemoteSessionDisposedError\";\n\t}\n}\n\n/**\n * Server leases expire unless renewed (no sliding renewal on the server), so an\n * attached session renews at one third of the remaining TTL. A failing renewal\n * is retried at a fixed short interval; after three consecutive failures the\n * lease is presumed reclaimed and the session is unbound like a server-removed\n * session.\n */\nconst RENEW_RETRY_INTERVAL_MS = 5_000;\nconst MAX_CONSECUTIVE_RENEW_FAILURES = 3;\n\nasync function settleRemoteSessionDisposal(cleanup: readonly Promise<void>[]): Promise<void> {\n\tconst results = await Promise.allSettled(cleanup);\n\tconst errors = results.flatMap((result) =>\n\t\tresult.status === \"rejected\" && !(result.reason instanceof RemoteSessionDisposedError) ? [result.reason] : [],\n\t);\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose remote session\");\n}\n\nexport class RemoteSession {\n\treadonly #client: PiClient;\n\treadonly #onListenerError: ((error: Error) => void) | undefined;\n\t#lifecycle: RemoteSessionLifecycle = { status: \"unbound\" };\n\t#handle: SessionLease | undefined;\n\t#transcript: TranscriptState | undefined;\n\t#unsubscribeSnapshot: Unsubscribe | undefined;\n\t#unsubscribeEvents: Unsubscribe | undefined;\n\t#renewTimer: ReturnType<typeof setTimeout> | undefined;\n\t#renewFailures = 0;\n\treadonly #listeners = new Set<(state: RemoteSessionState) => void>();\n\treadonly #pendingAttachmentOperations = new Set<Promise<void>>();\n\treadonly #activeOperationStates = new Set<RemoteSessionLifecycle>();\n\t#disposePromise: Promise<void> | undefined;\n\t#resolveDisposeSignal: () => void = () => {};\n\treadonly #disposeSignal = new Promise<void>((resolve) => {\n\t\tthis.#resolveDisposeSignal = resolve;\n\t});\n\n\tprivate constructor(client: PiClient, options: RemoteSessionOptions = {}) {\n\t\tthis.#client = client;\n\t\tthis.#onListenerError = options.onListenerError;\n\t}\n\n\tget id(): string | undefined {\n\t\treturn this.#handle?.id;\n\t}\n\n\tget state(): RemoteSessionState {\n\t\treturn {\n\t\t\tlifecycle: this.#lifecycle,\n\t\t\tsnapshot: this.#transcript?.snapshot,\n\t\t\ttranscript: this.#transcript ? selectTranscript(this.#transcript) : [],\n\t\t};\n\t}\n\n\tget snapshot(): SessionSnapshot | undefined {\n\t\treturn this.#transcript?.snapshot;\n\t}\n\n\tget phase(): SessionPhase | undefined {\n\t\treturn this.snapshot?.phase;\n\t}\n\n\tget operation(): RemoteSessionOperation | undefined {\n\t\treturn this.#lifecycle.status === \"busy\" ? this.#lifecycle.operation : undefined;\n\t}\n\n\tget models(): readonly ModelMetadata[] {\n\t\treturn this.#client.snapshot?.models ?? [];\n\t}\n\n\tget sessions(): readonly SessionMetadata[] {\n\t\treturn this.#client.snapshot?.sessions ?? [];\n\t}\n\n\tget connectionState(): ConnectionState {\n\t\treturn this.#client.connectionState;\n\t}\n\n\tget disposed(): boolean {\n\t\treturn this.#lifecycle.status === \"disposed\";\n\t}\n\n\tsubscribe(listener: (state: RemoteSessionState) => void): Unsubscribe {\n\t\tthis.#assertNotDisposed();\n\t\tthis.#listeners.add(listener);\n\t\tthis.#callListener(listener, this.state);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\tonConnectionStateChange(listener: (change: ConnectionStateChange) => void): Unsubscribe {\n\t\tthis.#assertNotDisposed();\n\t\treturn this.#client.onConnectionStateChange(listener);\n\t}\n\n\tstatic async open(client: PiClient, sessionId: string, options: RemoteSessionOptions = {}): Promise<RemoteSession> {\n\t\tconst session = new RemoteSession(client, options);\n\t\ttry {\n\t\t\tawait session.open(sessionId);\n\t\t\treturn session;\n\t\t} catch (error) {\n\t\t\tawait session.dispose();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync open(sessionId: string): Promise<void> {\n\t\tif (this.#handle?.id === sessionId && this.#lifecycle.status === \"ready\") return;\n\t\tawait this.#replace(\"open\", () => this.#client.acquireSession(sessionId, { mode: \"exclusive\" }));\n\t}\n\n\tstatic async create(\n\t\tclient: PiClient,\n\t\tcreateOptions: CreateRemoteSessionOptions,\n\t\toptions: RemoteSessionOptions = {},\n\t): Promise<RemoteSession> {\n\t\tconst session = new RemoteSession(client, options);\n\t\ttry {\n\t\t\tawait session.create(createOptions);\n\t\t\treturn session;\n\t\t} catch (error) {\n\t\t\tawait session.dispose();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync create(options: CreateRemoteSessionOptions): Promise<void> {\n\t\tawait this.#replace(\"create\", () => this.#client.createSession(options));\n\t}\n\n\tasync submit(text: string): Promise<void> {\n\t\tconst normalized = text.trim();\n\t\tif (!normalized) return;\n\t\tthis.#assertAvailable();\n\t\tconst handle = this.#requireHandle();\n\t\tif (this.phase !== \"idle\" && this.phase !== \"turn\") {\n\t\t\tthrow new Error(t(\"Session cannot accept input during {phase} phase\", { phase: this.phase ?? \"unknown\" }));\n\t\t}\n\t\tawait this.#runOperation(\"submit\", () =>\n\t\t\t(this.phase === \"idle\" ? handle.prompt(normalized) : handle.steer(normalized)).then(() => undefined),\n\t\t);\n\t}\n\n\tasync abort(): Promise<void> {\n\t\tconst preemptingSubmit = this.#lifecycle.status === \"busy\" && this.#lifecycle.operation === \"submit\";\n\t\tif (preemptingSubmit) this.#assertNotDisposed();\n\t\telse this.#assertAvailable();\n\t\tconst handle = this.#requireHandle();\n\t\tif (this.phase === \"idle\" && !preemptingSubmit) return;\n\t\tawait this.#runOperation(\"abort\", () => handle.abort().then(() => undefined), preemptingSubmit);\n\t}\n\n\tasync setModel(model: ModelRef): Promise<void> {\n\t\tawait this.#runIdleOperation(\"setModel\", \"change model\", () =>\n\t\t\tthis.#requireHandle()\n\t\t\t\t.setModel(model)\n\t\t\t\t.then(() => undefined),\n\t\t);\n\t}\n\n\tasync setThinking(thinkingLevel: ThinkingLevel): Promise<void> {\n\t\tawait this.#runIdleOperation(\"setThinking\", \"change thinking level\", () =>\n\t\t\tthis.#requireHandle()\n\t\t\t\t.setThinking(thinkingLevel)\n\t\t\t\t.then(() => undefined),\n\t\t);\n\t}\n\n\tasync reconnect(): Promise<void> {\n\t\tthis.#assertAvailable();\n\t\tconst sessionId = this.#requireHandle().id;\n\t\tawait this.#runOperation(\"reconnect\", () =>\n\t\t\tthis.#trackAttachmentOperation(async () => {\n\t\t\t\tawait this.#client.reconnect();\n\t\t\t\tconst handle = await this.#client.acquireSession(sessionId, { mode: \"exclusive\" });\n\t\t\t\tawait this.#assertNotDisposedAfterAwait(handle);\n\t\t\t\tthis.#bind(handle);\n\t\t\t}),\n\t\t);\n\t}\n\n\tdispose(): Promise<void> {\n\t\tif (this.#disposePromise) return this.#disposePromise;\n\t\tconst handle = this.#handle;\n\t\tthis.#lifecycle = { status: \"disposed\" };\n\t\tthis.#resolveDisposeSignal();\n\t\tthis.#stopLeaseRenewal();\n\t\tthis.#clearSubscriptions();\n\t\tthis.#handle = undefined;\n\t\tthis.#transcript = undefined;\n\t\tconst cleanup = [...this.#pendingAttachmentOperations];\n\t\tif (handle) cleanup.push(handle.dispose());\n\t\tthis.#disposePromise = settleRemoteSessionDisposal(cleanup);\n\t\tthis.#notify();\n\t\tthis.#listeners.clear();\n\t\treturn this.#disposePromise;\n\t}\n\n\t[Symbol.asyncDispose](): Promise<void> {\n\t\treturn this.dispose();\n\t}\n\n\tasync #replace(operation: \"open\" | \"create\", prepare: () => Promise<SessionLease>): Promise<void> {\n\t\tthis.#assertAvailable();\n\t\tif (this.#handle && this.phase !== \"idle\") {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Cannot {operation} a session while session is {phase}\", {\n\t\t\t\t\toperation,\n\t\t\t\t\tphase: this.phase ?? \"unavailable\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tawait this.#runOperation(operation, () =>\n\t\t\tthis.#trackAttachmentOperation(() => this.#prepareReplacement(operation, prepare)),\n\t\t);\n\t}\n\n\tasync #trackAttachmentOperation(run: () => Promise<void>): Promise<void> {\n\t\tconst pending = run();\n\t\tthis.#pendingAttachmentOperations.add(pending);\n\t\ttry {\n\t\t\tawait pending;\n\t\t} finally {\n\t\t\tthis.#pendingAttachmentOperations.delete(pending);\n\t\t}\n\t}\n\n\tasync #prepareReplacement(operation: \"open\" | \"create\", prepare: () => Promise<SessionLease>): Promise<void> {\n\t\tconst previous = this.#handle;\n\t\tconst next = await prepare();\n\t\tawait this.#assertNotDisposedAfterAwait(next);\n\t\tconst snapshot = next.snapshot;\n\t\tif (!snapshot) {\n\t\t\tawait this.#detach(next);\n\t\t\tthrow new Error(t(\"Session {id} did not provide a snapshot\", { id: next.id }));\n\t\t}\n\t\tif (previous && previous.id !== next.id && previous.attached && this.phase !== \"idle\") {\n\t\t\tawait this.#detach(next);\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Cannot {operation} a session while session is {phase}\", {\n\t\t\t\t\toperation,\n\t\t\t\t\tphase: this.phase ?? \"unavailable\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (previous && previous.id !== next.id && previous.attached) {\n\t\t\ttry {\n\t\t\t\tawait previous.detach();\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#detach(next);\n\t\t\t\t} catch (cleanupError) {\n\t\t\t\t\tthrow new AggregateError([error, cleanupError], \"Failed to replace remote session attachment\");\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tawait this.#assertNotDisposedAfterAwait(next);\n\t\tthis.#bind(next, snapshot);\n\t}\n\n\tasync #runIdleOperation(\n\t\toperation: \"setModel\" | \"setThinking\",\n\t\tdescription: string,\n\t\trun: () => Promise<void>,\n\t): Promise<void> {\n\t\tthis.#assertAvailable();\n\t\tthis.#requireHandle();\n\t\tif (this.phase !== \"idle\") {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Cannot {description} while session is {phase}\", { description, phase: this.phase ?? \"unavailable\" }),\n\t\t\t);\n\t\t}\n\t\tawait this.#runOperation(operation, run);\n\t}\n\n\tasync #runOperation(operation: RemoteSessionOperation, run: () => Promise<void>, preempt = false): Promise<void> {\n\t\tif (preempt) this.#assertNotDisposed();\n\t\telse this.#assertAvailable();\n\t\tconst previous = this.#lifecycle;\n\t\tconst busy: RemoteSessionLifecycle = { status: \"busy\", operation };\n\t\tthis.#lifecycle = busy;\n\t\tthis.#activeOperationStates.add(busy);\n\t\tthis.#notify();\n\t\tconst running = run();\n\t\ttry {\n\t\t\tawait Promise.race([\n\t\t\t\trunning,\n\t\t\t\tthis.#disposeSignal.then(() => {\n\t\t\t\t\tthrow new Error(t(\"Remote session is disposed\"));\n\t\t\t\t}),\n\t\t\t]);\n\t\t} finally {\n\t\t\tthis.#activeOperationStates.delete(busy);\n\t\t\tif (!this.disposed && this.#lifecycle === busy) {\n\t\t\t\tthis.#lifecycle =\n\t\t\t\t\tpreempt && this.#activeOperationStates.has(previous)\n\t\t\t\t\t\t? previous\n\t\t\t\t\t\t: this.#handle\n\t\t\t\t\t\t\t? { status: \"ready\" }\n\t\t\t\t\t\t\t: { status: \"unbound\" };\n\t\t\t\tthis.#notify();\n\t\t\t}\n\t\t}\n\t}\n\n\t#bind(handle: SessionLease, knownSnapshot?: SessionSnapshot): void {\n\t\tconst snapshot = knownSnapshot ?? handle.snapshot;\n\t\tif (!snapshot) throw new Error(`Session ${handle.id} did not provide a snapshot`);\n\t\tthis.#clearSubscriptions();\n\t\tthis.#stopLeaseRenewal();\n\t\tthis.#handle = handle;\n\t\tthis.#transcript = createTranscriptState(snapshot);\n\t\tthis.#unsubscribeSnapshot = handle.subscribe((next) => {\n\t\t\tif (!this.#transcript) return;\n\t\t\tthis.#transcript = applyTranscriptSnapshot(this.#transcript, next);\n\t\t\tthis.#notify();\n\t\t});\n\t\tthis.#unsubscribeEvents = handle.onEvent((event) => this.#handleEvent(event));\n\t\tthis.#scheduleLeaseRenewal(handle);\n\t}\n\n\t#handleEvent(event: ServerEvent): void {\n\t\tif (event.type === \"session_removed\") {\n\t\t\tthis.#unbindSession();\n\t\t\treturn;\n\t\t}\n\t\tif (event.type !== \"session_progress\" || !this.#transcript) return;\n\t\tthis.#transcript = applyTranscriptProgress(this.#transcript, event.progress);\n\t\tthis.#notify();\n\t}\n\n\t/**\n\t * Session-level invalidation shared by the server `session_removed` event and\n\t * unrecoverable lease renewals: subscriptions stop, the handle is dropped and\n\t * the session returns to the unbound lifecycle.\n\t */\n\t#unbindSession(): void {\n\t\tthis.#clearSubscriptions();\n\t\tthis.#stopLeaseRenewal();\n\t\tthis.#handle = undefined;\n\t\tthis.#transcript = undefined;\n\t\tif (this.#lifecycle.status !== \"busy\") this.#lifecycle = { status: \"unbound\" };\n\t\tthis.#notify();\n\t}\n\n\t#scheduleLeaseRenewal(handle: SessionLease): void {\n\t\tconst expiresAt = handle.leaseExpiresAt;\n\t\tif (expiresAt === undefined) return;\n\t\tconst remaining = Math.max(0, expiresAt - Date.now());\n\t\tthis.#renewTimer = setTimeout(() => void this.#renewLease(handle), Math.floor(remaining / 3));\n\t}\n\n\tasync #renewLease(handle: SessionLease): Promise<void> {\n\t\tthis.#renewTimer = undefined;\n\t\tif (this.disposed || this.#handle !== handle) return;\n\t\ttry {\n\t\t\tawait handle.renew();\n\t\t\tif (this.disposed || this.#handle !== handle) return;\n\t\t\tthis.#renewFailures = 0;\n\t\t\tthis.#scheduleLeaseRenewal(handle);\n\t\t} catch {\n\t\t\tif (this.disposed || this.#handle !== handle) return;\n\t\t\tthis.#renewFailures += 1;\n\t\t\tif (this.#renewFailures >= MAX_CONSECUTIVE_RENEW_FAILURES) {\n\t\t\t\tthis.#unbindSession();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#renewTimer = setTimeout(() => void this.#renewLease(handle), RENEW_RETRY_INTERVAL_MS);\n\t\t}\n\t}\n\n\t#stopLeaseRenewal(): void {\n\t\tif (this.#renewTimer !== undefined) {\n\t\t\tclearTimeout(this.#renewTimer);\n\t\t\tthis.#renewTimer = undefined;\n\t\t}\n\t\tthis.#renewFailures = 0;\n\t}\n\n\t#notify(): void {\n\t\tconst state = this.state;\n\t\tfor (const listener of this.#listeners) this.#callListener(listener, state);\n\t}\n\n\t#callListener(listener: (state: RemoteSessionState) => void, state: RemoteSessionState): void {\n\t\ttry {\n\t\t\tlistener(state);\n\t\t} catch (error) {\n\t\t\tthis.#reportListenerError(error);\n\t\t}\n\t}\n\n\t#reportListenerError(error: unknown): void {\n\t\tif (!this.#onListenerError) return;\n\t\ttry {\n\t\t\tthis.#onListenerError(error instanceof Error ? error : new Error(String(error)));\n\t\t} catch {\n\t\t\t// Diagnostics cannot affect session or transport state.\n\t\t}\n\t}\n\n\t#clearSubscriptions(): void {\n\t\tthis.#unsubscribeSnapshot?.();\n\t\tthis.#unsubscribeEvents?.();\n\t\tthis.#unsubscribeSnapshot = undefined;\n\t\tthis.#unsubscribeEvents = undefined;\n\t}\n\n\t#requireHandle(): SessionLease {\n\t\tif (!this.#handle) throw new Error(\"No remote session is attached\");\n\t\treturn this.#handle;\n\t}\n\n\t#assertAvailable(): void {\n\t\tthis.#assertNotDisposed();\n\t\tif (this.#lifecycle.status === \"busy\") {\n\t\t\tthrow new Error(t(\"Remote session is busy with {operation}\", { operation: this.#lifecycle.operation }));\n\t\t}\n\t}\n\n\t#assertNotDisposed(): void {\n\t\tif (this.disposed) throw new Error(t(\"Remote session is disposed\"));\n\t}\n\n\tasync #assertNotDisposedAfterAwait(handle: SessionLease): Promise<void> {\n\t\tif (!this.disposed) return;\n\t\tawait this.#detach(handle);\n\t\tthrow new RemoteSessionDisposedError();\n\t}\n\n\tasync #detach(handle: SessionLease): Promise<void> {\n\t\tawait handle.dispose();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-store.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-store.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qGAA6C;IAC7C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,qFAAmC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0FAA0C;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yEAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAMD,+FAAuD;AACvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAED,gIAAoE;AACpE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUlF;AAoBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,kBAAkB,GAAG,SAAS,CAyCjC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,SAAS,qBAAqB,EAAE,CAwCnC","sourcesContent":["/**\n * 子代理会话存取(D-060 S2):子会话与 root 会话一样落 JSONL 文件,但放在\n * `<agentDir>/sessions/_agents/` —— 与用户的会话列表分开(子会话不进用户会话选择器),\n * 同时仍可按 `sessionId` 精确回读、可按父会话 id 反查。\n *\n * 三条纪律:\n * - **文件是持久层**:内存索引(调度器的 `maxRetainedRecords`)淘汰不影响这里的可读性;\n * - **有界**:回读按字节上限截断并给 `truncated` 标记,绝不把整份 transcript 无声塞给调用方;\n * - **寻址安全**:`sessionId` 先过形状校验(UUID/`st:` 形态、无路径分隔符),再做路径拼接,\n * 杜绝 `../` 逃逸;跨父作用域由调用方(宿主)用返回的 `parentAgentSession` 判定。\n */\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { agentSessionsDir } from \"./agent-session-paths.ts\";\n\nexport interface AgentSessionReadV1 {\n\treadonly sessionId: string;\n\t/** 血缘:父 agent 会话 id(子会话必有;缺失说明文件非本次设计产物)。 */\n\treadonly parentAgentSession?: string;\n\t/** 血缘:创建该会话的角色 id(审计用途,不是权限判据)。 */\n\treadonly agentRole?: string;\n\treadonly cwd?: string;\n\treadonly createdAt?: string;\n\t/** 文件字节数(整份 transcript,而不是返回的片段)。 */\n\treadonly bytes: number;\n\t/** 解析出的 entry 条数(不含头行);截断时是**已返回**的条数。 */\n\treadonly entryCount: number;\n\t/** 达到 `maxBytes` 上限、只返回了前段 entry 时为 true。 */\n\treadonly truncated: boolean;\n\treadonly entries: readonly unknown[];\n}\n\nexport interface AgentSessionSummaryV1 {\n\treadonly sessionId: string;\n\treadonly parentAgentSession?: string;\n\treadonly agentRole?: string;\n\treadonly createdAt?: string;\n\treadonly modifiedAt: number;\n\treadonly bytes: number;\n}\n\nconst DEFAULT_MAX_BYTES = 262_144;\nconst DEFAULT_LIST_LIMIT = 200;\nconst HEADER_SCAN_BYTES = 8192;\n\n/** 会话 id 形状:UUID(root/子会话)或池化 wire 任务 id(`st:` 前缀)。 */\nexport function isAgentSessionId(value: string): boolean {\n\tif (value.length === 0 || value.length > 128) return false;\n\tif (value.startsWith(\"st:\")) return /^st:[0-9]+:[a-z0-9-]+$/i.test(value);\n\tif (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n\t// 产品面允许自定义 session id(--session-id <id>;2026-10-05 dogfood 实证:\n\t// 非 UUID 会话 id 的子代理委派被此处拒掉)。寻址安全只需要单段、无路径\n\t// 逃逸语义(\".\" / \"..\" / 分隔符),不必是 UUID 形态。\n\treturn /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) && value !== \".\" && value !== \"..\";\n}\n\n/** 目录内定位某会话文件:命名形如 `<时间戳>_<sessionId>.jsonl`。导出给宿主的 resume 打开路径。 */\nexport function findSessionFile(dir: string, sessionId: string): string | undefined {\n\tlet names: string[];\n\ttry {\n\t\tnames = readdirSync(dir);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst suffix = `_${sessionId}.jsonl`;\n\tconst match = names.find((name) => name === `${sessionId}.jsonl` || name.endsWith(suffix));\n\treturn match === undefined ? undefined : join(dir, match);\n}\n\nfunction parseHeader(line: string): Record<string, unknown> | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(line) as unknown;\n\t\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\treturn record.type === \"session\" ? record : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readHeader(filePath: string): { header?: Record<string, unknown>; bytes: number } {\n\tconst bytes = statSync(filePath).size;\n\tconst buffer = readFileSync(filePath, { encoding: \"utf-8\", flag: \"r\" });\n\tconst firstLine = buffer.slice(0, HEADER_SCAN_BYTES).split(\"\\n\", 1)[0] ?? \"\";\n\treturn { header: parseHeader(firstLine), bytes };\n}\n\n/**\n * 按 `sessionId` 有界回读一份子会话 transcript。不存在/形状非法/无头行都返回 undefined\n * (与\"无权\"同型,不区分原因)。\n */\nexport function readAgentSession(options: {\n\treadonly agentDir: string;\n\treadonly sessionId: string;\n\treadonly maxBytes?: number;\n}): AgentSessionReadV1 | undefined {\n\tif (!isAgentSessionId(options.sessionId)) return undefined;\n\tconst dir = agentSessionsDir(options.agentDir);\n\tconst filePath = findSessionFile(dir, options.sessionId);\n\tif (filePath === undefined) return undefined;\n\tlet text: string;\n\tlet bytes: number;\n\ttry {\n\t\tbytes = statSync(filePath).size;\n\t\ttext = readFileSync(filePath, { encoding: \"utf-8\", flag: \"r\" });\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\tconst truncated = text.length > maxBytes;\n\tconst bounded = truncated ? text.slice(0, maxBytes) : text;\n\tconst lines = bounded.split(\"\\n\").filter((line) => line.trim() !== \"\");\n\tconst header = lines.length > 0 ? parseHeader(lines[0] as string) : undefined;\n\tconst entries: unknown[] = [];\n\tfor (const line of lines.slice(header === undefined ? 0 : 1)) {\n\t\ttry {\n\t\t\tentries.push(JSON.parse(line) as unknown);\n\t\t} catch {\n\t\t\t// 截断可能切在行中间:忽略无法解析的尾行(truncated 已如实置位)。\n\t\t}\n\t}\n\tconst parentAgentSession = typeof header?.parentAgentSession === \"string\" ? header.parentAgentSession : undefined;\n\tconst agentRole = typeof header?.agentRole === \"string\" ? header.agentRole : undefined;\n\tconst cwd = typeof header?.cwd === \"string\" ? header.cwd : undefined;\n\tconst createdAt = typeof header?.timestamp === \"string\" ? header.timestamp : undefined;\n\treturn {\n\t\tsessionId: options.sessionId,\n\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t...(cwd === undefined ? {} : { cwd }),\n\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\tbytes,\n\t\tentryCount: entries.length,\n\t\ttruncated,\n\t\tentries: Object.freeze(entries),\n\t};\n}\n\n/**\n * 列子会话摘要(只读头行,不解析正文):`parentAgentSession` 提供时只返回该父会话的子会话——\n * \"按父反查\"是审计入口;`limit` 默认 200,超过按修改时间倒序截断。\n */\nexport function listAgentSessions(options: {\n\treadonly agentDir: string;\n\treadonly parentAgentSession?: string;\n\treadonly limit?: number;\n}): readonly AgentSessionSummaryV1[] {\n\tlet names: string[];\n\tlet dir: string;\n\ttry {\n\t\tdir = agentSessionsDir(options.agentDir);\n\t\tnames = readdirSync(dir).filter((name) => name.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn Object.freeze([]);\n\t}\n\tconst summaries: AgentSessionSummaryV1[] = [];\n\tfor (const name of names) {\n\t\tconst stem = name.slice(0, -\".jsonl\".length);\n\t\tconst separator = stem.lastIndexOf(\"_\");\n\t\tconst sessionId = separator === -1 ? stem : stem.slice(separator + 1);\n\t\tif (!isAgentSessionId(sessionId)) continue;\n\t\tconst filePath = join(dir, name);\n\t\tlet scanned: { header?: Record<string, unknown>; bytes: number };\n\t\tlet modifiedAt: number;\n\t\ttry {\n\t\t\tscanned = readHeader(filePath);\n\t\t\tmodifiedAt = statSync(filePath).mtimeMs;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parentAgentSession =\n\t\t\ttypeof scanned.header?.parentAgentSession === \"string\" ? scanned.header.parentAgentSession : undefined;\n\t\tif (options.parentAgentSession !== undefined && parentAgentSession !== options.parentAgentSession) continue;\n\t\tconst agentRole = typeof scanned.header?.agentRole === \"string\" ? scanned.header.agentRole : undefined;\n\t\tconst createdAt = typeof scanned.header?.timestamp === \"string\" ? scanned.header.timestamp : undefined;\n\t\tsummaries.push({\n\t\t\tsessionId,\n\t\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\t\tmodifiedAt,\n\t\t\tbytes: scanned.bytes,\n\t\t});\n\t}\n\tsummaries.sort((left, right) => right.modifiedAt - left.modifiedAt);\n\treturn Object.freeze(summaries.slice(0, options.limit ?? DEFAULT_LIST_LIMIT));\n}\n"]}
1
+ {"version":3,"file":"agent-session-store.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-store.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qGAA6C;IAC7C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,qFAAmC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0FAA0C;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yEAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAMD,+FAAuD;AACvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAED,gIAAoE;AACpE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUlF;AAoBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,kBAAkB,GAAG,SAAS,CAwDjC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,SAAS,qBAAqB,EAAE,CAwCnC","sourcesContent":["/**\n * 子代理会话存取(D-060 S2):子会话与 root 会话一样落 JSONL 文件,但放在\n * `<agentDir>/sessions/_agents/` —— 与用户的会话列表分开(子会话不进用户会话选择器),\n * 同时仍可按 `sessionId` 精确回读、可按父会话 id 反查。\n *\n * 三条纪律:\n * - **文件是持久层**:内存索引(调度器的 `maxRetainedRecords`)淘汰不影响这里的可读性;\n * - **有界**:回读按字节上限截断并给 `truncated` 标记,绝不把整份 transcript 无声塞给调用方;\n * - **寻址安全**:`sessionId` 先过形状校验(UUID/`st:` 形态、无路径分隔符),再做路径拼接,\n * 杜绝 `../` 逃逸;跨父作用域由调用方(宿主)用返回的 `parentAgentSession` 判定。\n */\nimport { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { agentSessionsDir } from \"./agent-session-paths.ts\";\n\nexport interface AgentSessionReadV1 {\n\treadonly sessionId: string;\n\t/** 血缘:父 agent 会话 id(子会话必有;缺失说明文件非本次设计产物)。 */\n\treadonly parentAgentSession?: string;\n\t/** 血缘:创建该会话的角色 id(审计用途,不是权限判据)。 */\n\treadonly agentRole?: string;\n\treadonly cwd?: string;\n\treadonly createdAt?: string;\n\t/** 文件字节数(整份 transcript,而不是返回的片段)。 */\n\treadonly bytes: number;\n\t/** 解析出的 entry 条数(不含头行);截断时是**已返回**的条数。 */\n\treadonly entryCount: number;\n\t/** 达到 `maxBytes` 上限、只返回了前段 entry 时为 true。 */\n\treadonly truncated: boolean;\n\treadonly entries: readonly unknown[];\n}\n\nexport interface AgentSessionSummaryV1 {\n\treadonly sessionId: string;\n\treadonly parentAgentSession?: string;\n\treadonly agentRole?: string;\n\treadonly createdAt?: string;\n\treadonly modifiedAt: number;\n\treadonly bytes: number;\n}\n\nconst DEFAULT_MAX_BYTES = 262_144;\nconst DEFAULT_LIST_LIMIT = 200;\nconst HEADER_SCAN_BYTES = 8192;\n\n/** 会话 id 形状:UUID(root/子会话)或池化 wire 任务 id(`st:` 前缀)。 */\nexport function isAgentSessionId(value: string): boolean {\n\tif (value.length === 0 || value.length > 128) return false;\n\tif (value.startsWith(\"st:\")) return /^st:[0-9]+:[a-z0-9-]+$/i.test(value);\n\tif (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n\t// 产品面允许自定义 session id(--session-id <id>;2026-10-05 dogfood 实证:\n\t// 非 UUID 会话 id 的子代理委派被此处拒掉)。寻址安全只需要单段、无路径\n\t// 逃逸语义(\".\" / \"..\" / 分隔符),不必是 UUID 形态。\n\treturn /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) && value !== \".\" && value !== \"..\";\n}\n\n/** 目录内定位某会话文件:命名形如 `<时间戳>_<sessionId>.jsonl`。导出给宿主的 resume 打开路径。 */\nexport function findSessionFile(dir: string, sessionId: string): string | undefined {\n\tlet names: string[];\n\ttry {\n\t\tnames = readdirSync(dir);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst suffix = `_${sessionId}.jsonl`;\n\tconst match = names.find((name) => name === `${sessionId}.jsonl` || name.endsWith(suffix));\n\treturn match === undefined ? undefined : join(dir, match);\n}\n\nfunction parseHeader(line: string): Record<string, unknown> | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(line) as unknown;\n\t\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\treturn record.type === \"session\" ? record : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readHeader(filePath: string): { header?: Record<string, unknown>; bytes: number } {\n\tconst bytes = statSync(filePath).size;\n\tconst buffer = readFileSync(filePath, { encoding: \"utf-8\", flag: \"r\" });\n\tconst firstLine = buffer.slice(0, HEADER_SCAN_BYTES).split(\"\\n\", 1)[0] ?? \"\";\n\treturn { header: parseHeader(firstLine), bytes };\n}\n\n/**\n * 按 `sessionId` 有界回读一份子会话 transcript。不存在/形状非法/无头行都返回 undefined\n * (与\"无权\"同型,不区分原因)。\n */\nexport function readAgentSession(options: {\n\treadonly agentDir: string;\n\treadonly sessionId: string;\n\treadonly maxBytes?: number;\n}): AgentSessionReadV1 | undefined {\n\tif (!isAgentSessionId(options.sessionId)) return undefined;\n\tconst dir = agentSessionsDir(options.agentDir);\n\tconst filePath = findSessionFile(dir, options.sessionId);\n\tif (filePath === undefined) return undefined;\n\tlet text: string;\n\tlet bytes: number;\n\ttry {\n\t\tbytes = statSync(filePath).size;\n\t\t// 只从文件头部读 maxBytes 字节:超大 transcript 不再整文件进内存\n\t\t//(详情视图只消费头部窗口,尾部本来就要截断)。\n\t\tconst maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\tconst buffer = Buffer.alloc(Math.min(bytes, maxBytes));\n\t\t\tlet read = 0;\n\t\t\twhile (read < buffer.length) {\n\t\t\t\tconst chunk = readSync(fd, buffer, read, buffer.length - read, read);\n\t\t\t\tif (chunk <= 0) break;\n\t\t\t\tread += chunk;\n\t\t\t}\n\t\t\ttext = buffer.subarray(0, read).toString(\"utf-8\");\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\tconst truncated = bytes > maxBytes;\n\tconst bounded = truncated ? text.slice(0, maxBytes) : text;\n\tconst lines = bounded.split(\"\\n\").filter((line) => line.trim() !== \"\");\n\tconst header = lines.length > 0 ? parseHeader(lines[0] as string) : undefined;\n\tconst entries: unknown[] = [];\n\tfor (const line of lines.slice(header === undefined ? 0 : 1)) {\n\t\ttry {\n\t\t\tentries.push(JSON.parse(line) as unknown);\n\t\t} catch {\n\t\t\t// 截断可能切在行中间:忽略无法解析的尾行(truncated 已如实置位)。\n\t\t}\n\t}\n\tconst parentAgentSession = typeof header?.parentAgentSession === \"string\" ? header.parentAgentSession : undefined;\n\tconst agentRole = typeof header?.agentRole === \"string\" ? header.agentRole : undefined;\n\tconst cwd = typeof header?.cwd === \"string\" ? header.cwd : undefined;\n\tconst createdAt = typeof header?.timestamp === \"string\" ? header.timestamp : undefined;\n\treturn {\n\t\tsessionId: options.sessionId,\n\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t...(cwd === undefined ? {} : { cwd }),\n\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\tbytes,\n\t\tentryCount: entries.length,\n\t\ttruncated,\n\t\tentries: Object.freeze(entries),\n\t};\n}\n\n/**\n * 列子会话摘要(只读头行,不解析正文):`parentAgentSession` 提供时只返回该父会话的子会话——\n * \"按父反查\"是审计入口;`limit` 默认 200,超过按修改时间倒序截断。\n */\nexport function listAgentSessions(options: {\n\treadonly agentDir: string;\n\treadonly parentAgentSession?: string;\n\treadonly limit?: number;\n}): readonly AgentSessionSummaryV1[] {\n\tlet names: string[];\n\tlet dir: string;\n\ttry {\n\t\tdir = agentSessionsDir(options.agentDir);\n\t\tnames = readdirSync(dir).filter((name) => name.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn Object.freeze([]);\n\t}\n\tconst summaries: AgentSessionSummaryV1[] = [];\n\tfor (const name of names) {\n\t\tconst stem = name.slice(0, -\".jsonl\".length);\n\t\tconst separator = stem.lastIndexOf(\"_\");\n\t\tconst sessionId = separator === -1 ? stem : stem.slice(separator + 1);\n\t\tif (!isAgentSessionId(sessionId)) continue;\n\t\tconst filePath = join(dir, name);\n\t\tlet scanned: { header?: Record<string, unknown>; bytes: number };\n\t\tlet modifiedAt: number;\n\t\ttry {\n\t\t\tscanned = readHeader(filePath);\n\t\t\tmodifiedAt = statSync(filePath).mtimeMs;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parentAgentSession =\n\t\t\ttypeof scanned.header?.parentAgentSession === \"string\" ? scanned.header.parentAgentSession : undefined;\n\t\tif (options.parentAgentSession !== undefined && parentAgentSession !== options.parentAgentSession) continue;\n\t\tconst agentRole = typeof scanned.header?.agentRole === \"string\" ? scanned.header.agentRole : undefined;\n\t\tconst createdAt = typeof scanned.header?.timestamp === \"string\" ? scanned.header.timestamp : undefined;\n\t\tsummaries.push({\n\t\t\tsessionId,\n\t\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\t\tmodifiedAt,\n\t\t\tbytes: scanned.bytes,\n\t\t});\n\t}\n\tsummaries.sort((left, right) => right.modifiedAt - left.modifiedAt);\n\treturn Object.freeze(summaries.slice(0, options.limit ?? DEFAULT_LIST_LIMIT));\n}\n"]}
@@ -9,7 +9,7 @@
9
9
  * - **寻址安全**:`sessionId` 先过形状校验(UUID/`st:` 形态、无路径分隔符),再做路径拼接,
10
10
  * 杜绝 `../` 逃逸;跨父作用域由调用方(宿主)用返回的 `parentAgentSession` 判定。
11
11
  */
12
- import { readdirSync, readFileSync, statSync } from "node:fs";
12
+ import { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from "node:fs";
13
13
  import { join } from "node:path";
14
14
  import { agentSessionsDir } from "./agent-session-paths.js";
15
15
  const DEFAULT_MAX_BYTES = 262_144;
@@ -74,13 +74,30 @@ export function readAgentSession(options) {
74
74
  let bytes;
75
75
  try {
76
76
  bytes = statSync(filePath).size;
77
- text = readFileSync(filePath, { encoding: "utf-8", flag: "r" });
77
+ // 只从文件头部读 maxBytes 字节:超大 transcript 不再整文件进内存
78
+ //(详情视图只消费头部窗口,尾部本来就要截断)。
79
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
80
+ const fd = openSync(filePath, "r");
81
+ try {
82
+ const buffer = Buffer.alloc(Math.min(bytes, maxBytes));
83
+ let read = 0;
84
+ while (read < buffer.length) {
85
+ const chunk = readSync(fd, buffer, read, buffer.length - read, read);
86
+ if (chunk <= 0)
87
+ break;
88
+ read += chunk;
89
+ }
90
+ text = buffer.subarray(0, read).toString("utf-8");
91
+ }
92
+ finally {
93
+ closeSync(fd);
94
+ }
78
95
  }
79
96
  catch {
80
97
  return undefined;
81
98
  }
82
99
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
83
- const truncated = text.length > maxBytes;
100
+ const truncated = bytes > maxBytes;
84
101
  const bounded = truncated ? text.slice(0, maxBytes) : text;
85
102
  const lines = bounded.split("\n").filter((line) => line.trim() !== "");
86
103
  const header = lines.length > 0 ? parseHeader(lines[0]) : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-store.js","sourceRoot":"","sources":["../../src/core/agent-session-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 子代理会话存取(D-060 S2):子会话与 root 会话一样落 JSONL 文件,但放在\n * `<agentDir>/sessions/_agents/` —— 与用户的会话列表分开(子会话不进用户会话选择器),\n * 同时仍可按 `sessionId` 精确回读、可按父会话 id 反查。\n *\n * 三条纪律:\n * - **文件是持久层**:内存索引(调度器的 `maxRetainedRecords`)淘汰不影响这里的可读性;\n * - **有界**:回读按字节上限截断并给 `truncated` 标记,绝不把整份 transcript 无声塞给调用方;\n * - **寻址安全**:`sessionId` 先过形状校验(UUID/`st:` 形态、无路径分隔符),再做路径拼接,\n * 杜绝 `../` 逃逸;跨父作用域由调用方(宿主)用返回的 `parentAgentSession` 判定。\n */\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { agentSessionsDir } from \"./agent-session-paths.ts\";\n\nexport interface AgentSessionReadV1 {\n\treadonly sessionId: string;\n\t/** 血缘:父 agent 会话 id(子会话必有;缺失说明文件非本次设计产物)。 */\n\treadonly parentAgentSession?: string;\n\t/** 血缘:创建该会话的角色 id(审计用途,不是权限判据)。 */\n\treadonly agentRole?: string;\n\treadonly cwd?: string;\n\treadonly createdAt?: string;\n\t/** 文件字节数(整份 transcript,而不是返回的片段)。 */\n\treadonly bytes: number;\n\t/** 解析出的 entry 条数(不含头行);截断时是**已返回**的条数。 */\n\treadonly entryCount: number;\n\t/** 达到 `maxBytes` 上限、只返回了前段 entry 时为 true。 */\n\treadonly truncated: boolean;\n\treadonly entries: readonly unknown[];\n}\n\nexport interface AgentSessionSummaryV1 {\n\treadonly sessionId: string;\n\treadonly parentAgentSession?: string;\n\treadonly agentRole?: string;\n\treadonly createdAt?: string;\n\treadonly modifiedAt: number;\n\treadonly bytes: number;\n}\n\nconst DEFAULT_MAX_BYTES = 262_144;\nconst DEFAULT_LIST_LIMIT = 200;\nconst HEADER_SCAN_BYTES = 8192;\n\n/** 会话 id 形状:UUID(root/子会话)或池化 wire 任务 id(`st:` 前缀)。 */\nexport function isAgentSessionId(value: string): boolean {\n\tif (value.length === 0 || value.length > 128) return false;\n\tif (value.startsWith(\"st:\")) return /^st:[0-9]+:[a-z0-9-]+$/i.test(value);\n\tif (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n\t// 产品面允许自定义 session id(--session-id <id>;2026-10-05 dogfood 实证:\n\t// 非 UUID 会话 id 的子代理委派被此处拒掉)。寻址安全只需要单段、无路径\n\t// 逃逸语义(\".\" / \"..\" / 分隔符),不必是 UUID 形态。\n\treturn /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) && value !== \".\" && value !== \"..\";\n}\n\n/** 目录内定位某会话文件:命名形如 `<时间戳>_<sessionId>.jsonl`。导出给宿主的 resume 打开路径。 */\nexport function findSessionFile(dir: string, sessionId: string): string | undefined {\n\tlet names: string[];\n\ttry {\n\t\tnames = readdirSync(dir);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst suffix = `_${sessionId}.jsonl`;\n\tconst match = names.find((name) => name === `${sessionId}.jsonl` || name.endsWith(suffix));\n\treturn match === undefined ? undefined : join(dir, match);\n}\n\nfunction parseHeader(line: string): Record<string, unknown> | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(line) as unknown;\n\t\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\treturn record.type === \"session\" ? record : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readHeader(filePath: string): { header?: Record<string, unknown>; bytes: number } {\n\tconst bytes = statSync(filePath).size;\n\tconst buffer = readFileSync(filePath, { encoding: \"utf-8\", flag: \"r\" });\n\tconst firstLine = buffer.slice(0, HEADER_SCAN_BYTES).split(\"\\n\", 1)[0] ?? \"\";\n\treturn { header: parseHeader(firstLine), bytes };\n}\n\n/**\n * 按 `sessionId` 有界回读一份子会话 transcript。不存在/形状非法/无头行都返回 undefined\n * (与\"无权\"同型,不区分原因)。\n */\nexport function readAgentSession(options: {\n\treadonly agentDir: string;\n\treadonly sessionId: string;\n\treadonly maxBytes?: number;\n}): AgentSessionReadV1 | undefined {\n\tif (!isAgentSessionId(options.sessionId)) return undefined;\n\tconst dir = agentSessionsDir(options.agentDir);\n\tconst filePath = findSessionFile(dir, options.sessionId);\n\tif (filePath === undefined) return undefined;\n\tlet text: string;\n\tlet bytes: number;\n\ttry {\n\t\tbytes = statSync(filePath).size;\n\t\ttext = readFileSync(filePath, { encoding: \"utf-8\", flag: \"r\" });\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\tconst truncated = text.length > maxBytes;\n\tconst bounded = truncated ? text.slice(0, maxBytes) : text;\n\tconst lines = bounded.split(\"\\n\").filter((line) => line.trim() !== \"\");\n\tconst header = lines.length > 0 ? parseHeader(lines[0] as string) : undefined;\n\tconst entries: unknown[] = [];\n\tfor (const line of lines.slice(header === undefined ? 0 : 1)) {\n\t\ttry {\n\t\t\tentries.push(JSON.parse(line) as unknown);\n\t\t} catch {\n\t\t\t// 截断可能切在行中间:忽略无法解析的尾行(truncated 已如实置位)。\n\t\t}\n\t}\n\tconst parentAgentSession = typeof header?.parentAgentSession === \"string\" ? header.parentAgentSession : undefined;\n\tconst agentRole = typeof header?.agentRole === \"string\" ? header.agentRole : undefined;\n\tconst cwd = typeof header?.cwd === \"string\" ? header.cwd : undefined;\n\tconst createdAt = typeof header?.timestamp === \"string\" ? header.timestamp : undefined;\n\treturn {\n\t\tsessionId: options.sessionId,\n\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t...(cwd === undefined ? {} : { cwd }),\n\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\tbytes,\n\t\tentryCount: entries.length,\n\t\ttruncated,\n\t\tentries: Object.freeze(entries),\n\t};\n}\n\n/**\n * 列子会话摘要(只读头行,不解析正文):`parentAgentSession` 提供时只返回该父会话的子会话——\n * \"按父反查\"是审计入口;`limit` 默认 200,超过按修改时间倒序截断。\n */\nexport function listAgentSessions(options: {\n\treadonly agentDir: string;\n\treadonly parentAgentSession?: string;\n\treadonly limit?: number;\n}): readonly AgentSessionSummaryV1[] {\n\tlet names: string[];\n\tlet dir: string;\n\ttry {\n\t\tdir = agentSessionsDir(options.agentDir);\n\t\tnames = readdirSync(dir).filter((name) => name.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn Object.freeze([]);\n\t}\n\tconst summaries: AgentSessionSummaryV1[] = [];\n\tfor (const name of names) {\n\t\tconst stem = name.slice(0, -\".jsonl\".length);\n\t\tconst separator = stem.lastIndexOf(\"_\");\n\t\tconst sessionId = separator === -1 ? stem : stem.slice(separator + 1);\n\t\tif (!isAgentSessionId(sessionId)) continue;\n\t\tconst filePath = join(dir, name);\n\t\tlet scanned: { header?: Record<string, unknown>; bytes: number };\n\t\tlet modifiedAt: number;\n\t\ttry {\n\t\t\tscanned = readHeader(filePath);\n\t\t\tmodifiedAt = statSync(filePath).mtimeMs;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parentAgentSession =\n\t\t\ttypeof scanned.header?.parentAgentSession === \"string\" ? scanned.header.parentAgentSession : undefined;\n\t\tif (options.parentAgentSession !== undefined && parentAgentSession !== options.parentAgentSession) continue;\n\t\tconst agentRole = typeof scanned.header?.agentRole === \"string\" ? scanned.header.agentRole : undefined;\n\t\tconst createdAt = typeof scanned.header?.timestamp === \"string\" ? scanned.header.timestamp : undefined;\n\t\tsummaries.push({\n\t\t\tsessionId,\n\t\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\t\tmodifiedAt,\n\t\t\tbytes: scanned.bytes,\n\t\t});\n\t}\n\tsummaries.sort((left, right) => right.modifiedAt - left.modifiedAt);\n\treturn Object.freeze(summaries.slice(0, options.limit ?? DEFAULT_LIST_LIMIT));\n}\n"]}
1
+ {"version":3,"file":"agent-session-store.js","sourceRoot":"","sources":["../../src/core/agent-session-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 子代理会话存取(D-060 S2):子会话与 root 会话一样落 JSONL 文件,但放在\n * `<agentDir>/sessions/_agents/` —— 与用户的会话列表分开(子会话不进用户会话选择器),\n * 同时仍可按 `sessionId` 精确回读、可按父会话 id 反查。\n *\n * 三条纪律:\n * - **文件是持久层**:内存索引(调度器的 `maxRetainedRecords`)淘汰不影响这里的可读性;\n * - **有界**:回读按字节上限截断并给 `truncated` 标记,绝不把整份 transcript 无声塞给调用方;\n * - **寻址安全**:`sessionId` 先过形状校验(UUID/`st:` 形态、无路径分隔符),再做路径拼接,\n * 杜绝 `../` 逃逸;跨父作用域由调用方(宿主)用返回的 `parentAgentSession` 判定。\n */\nimport { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { agentSessionsDir } from \"./agent-session-paths.ts\";\n\nexport interface AgentSessionReadV1 {\n\treadonly sessionId: string;\n\t/** 血缘:父 agent 会话 id(子会话必有;缺失说明文件非本次设计产物)。 */\n\treadonly parentAgentSession?: string;\n\t/** 血缘:创建该会话的角色 id(审计用途,不是权限判据)。 */\n\treadonly agentRole?: string;\n\treadonly cwd?: string;\n\treadonly createdAt?: string;\n\t/** 文件字节数(整份 transcript,而不是返回的片段)。 */\n\treadonly bytes: number;\n\t/** 解析出的 entry 条数(不含头行);截断时是**已返回**的条数。 */\n\treadonly entryCount: number;\n\t/** 达到 `maxBytes` 上限、只返回了前段 entry 时为 true。 */\n\treadonly truncated: boolean;\n\treadonly entries: readonly unknown[];\n}\n\nexport interface AgentSessionSummaryV1 {\n\treadonly sessionId: string;\n\treadonly parentAgentSession?: string;\n\treadonly agentRole?: string;\n\treadonly createdAt?: string;\n\treadonly modifiedAt: number;\n\treadonly bytes: number;\n}\n\nconst DEFAULT_MAX_BYTES = 262_144;\nconst DEFAULT_LIST_LIMIT = 200;\nconst HEADER_SCAN_BYTES = 8192;\n\n/** 会话 id 形状:UUID(root/子会话)或池化 wire 任务 id(`st:` 前缀)。 */\nexport function isAgentSessionId(value: string): boolean {\n\tif (value.length === 0 || value.length > 128) return false;\n\tif (value.startsWith(\"st:\")) return /^st:[0-9]+:[a-z0-9-]+$/i.test(value);\n\tif (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n\t// 产品面允许自定义 session id(--session-id <id>;2026-10-05 dogfood 实证:\n\t// 非 UUID 会话 id 的子代理委派被此处拒掉)。寻址安全只需要单段、无路径\n\t// 逃逸语义(\".\" / \"..\" / 分隔符),不必是 UUID 形态。\n\treturn /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) && value !== \".\" && value !== \"..\";\n}\n\n/** 目录内定位某会话文件:命名形如 `<时间戳>_<sessionId>.jsonl`。导出给宿主的 resume 打开路径。 */\nexport function findSessionFile(dir: string, sessionId: string): string | undefined {\n\tlet names: string[];\n\ttry {\n\t\tnames = readdirSync(dir);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst suffix = `_${sessionId}.jsonl`;\n\tconst match = names.find((name) => name === `${sessionId}.jsonl` || name.endsWith(suffix));\n\treturn match === undefined ? undefined : join(dir, match);\n}\n\nfunction parseHeader(line: string): Record<string, unknown> | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(line) as unknown;\n\t\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\treturn record.type === \"session\" ? record : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readHeader(filePath: string): { header?: Record<string, unknown>; bytes: number } {\n\tconst bytes = statSync(filePath).size;\n\tconst buffer = readFileSync(filePath, { encoding: \"utf-8\", flag: \"r\" });\n\tconst firstLine = buffer.slice(0, HEADER_SCAN_BYTES).split(\"\\n\", 1)[0] ?? \"\";\n\treturn { header: parseHeader(firstLine), bytes };\n}\n\n/**\n * 按 `sessionId` 有界回读一份子会话 transcript。不存在/形状非法/无头行都返回 undefined\n * (与\"无权\"同型,不区分原因)。\n */\nexport function readAgentSession(options: {\n\treadonly agentDir: string;\n\treadonly sessionId: string;\n\treadonly maxBytes?: number;\n}): AgentSessionReadV1 | undefined {\n\tif (!isAgentSessionId(options.sessionId)) return undefined;\n\tconst dir = agentSessionsDir(options.agentDir);\n\tconst filePath = findSessionFile(dir, options.sessionId);\n\tif (filePath === undefined) return undefined;\n\tlet text: string;\n\tlet bytes: number;\n\ttry {\n\t\tbytes = statSync(filePath).size;\n\t\t// 只从文件头部读 maxBytes 字节:超大 transcript 不再整文件进内存\n\t\t//(详情视图只消费头部窗口,尾部本来就要截断)。\n\t\tconst maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\tconst buffer = Buffer.alloc(Math.min(bytes, maxBytes));\n\t\t\tlet read = 0;\n\t\t\twhile (read < buffer.length) {\n\t\t\t\tconst chunk = readSync(fd, buffer, read, buffer.length - read, read);\n\t\t\t\tif (chunk <= 0) break;\n\t\t\t\tread += chunk;\n\t\t\t}\n\t\t\ttext = buffer.subarray(0, read).toString(\"utf-8\");\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\tconst truncated = bytes > maxBytes;\n\tconst bounded = truncated ? text.slice(0, maxBytes) : text;\n\tconst lines = bounded.split(\"\\n\").filter((line) => line.trim() !== \"\");\n\tconst header = lines.length > 0 ? parseHeader(lines[0] as string) : undefined;\n\tconst entries: unknown[] = [];\n\tfor (const line of lines.slice(header === undefined ? 0 : 1)) {\n\t\ttry {\n\t\t\tentries.push(JSON.parse(line) as unknown);\n\t\t} catch {\n\t\t\t// 截断可能切在行中间:忽略无法解析的尾行(truncated 已如实置位)。\n\t\t}\n\t}\n\tconst parentAgentSession = typeof header?.parentAgentSession === \"string\" ? header.parentAgentSession : undefined;\n\tconst agentRole = typeof header?.agentRole === \"string\" ? header.agentRole : undefined;\n\tconst cwd = typeof header?.cwd === \"string\" ? header.cwd : undefined;\n\tconst createdAt = typeof header?.timestamp === \"string\" ? header.timestamp : undefined;\n\treturn {\n\t\tsessionId: options.sessionId,\n\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t...(cwd === undefined ? {} : { cwd }),\n\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\tbytes,\n\t\tentryCount: entries.length,\n\t\ttruncated,\n\t\tentries: Object.freeze(entries),\n\t};\n}\n\n/**\n * 列子会话摘要(只读头行,不解析正文):`parentAgentSession` 提供时只返回该父会话的子会话——\n * \"按父反查\"是审计入口;`limit` 默认 200,超过按修改时间倒序截断。\n */\nexport function listAgentSessions(options: {\n\treadonly agentDir: string;\n\treadonly parentAgentSession?: string;\n\treadonly limit?: number;\n}): readonly AgentSessionSummaryV1[] {\n\tlet names: string[];\n\tlet dir: string;\n\ttry {\n\t\tdir = agentSessionsDir(options.agentDir);\n\t\tnames = readdirSync(dir).filter((name) => name.endsWith(\".jsonl\"));\n\t} catch {\n\t\treturn Object.freeze([]);\n\t}\n\tconst summaries: AgentSessionSummaryV1[] = [];\n\tfor (const name of names) {\n\t\tconst stem = name.slice(0, -\".jsonl\".length);\n\t\tconst separator = stem.lastIndexOf(\"_\");\n\t\tconst sessionId = separator === -1 ? stem : stem.slice(separator + 1);\n\t\tif (!isAgentSessionId(sessionId)) continue;\n\t\tconst filePath = join(dir, name);\n\t\tlet scanned: { header?: Record<string, unknown>; bytes: number };\n\t\tlet modifiedAt: number;\n\t\ttry {\n\t\t\tscanned = readHeader(filePath);\n\t\t\tmodifiedAt = statSync(filePath).mtimeMs;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parentAgentSession =\n\t\t\ttypeof scanned.header?.parentAgentSession === \"string\" ? scanned.header.parentAgentSession : undefined;\n\t\tif (options.parentAgentSession !== undefined && parentAgentSession !== options.parentAgentSession) continue;\n\t\tconst agentRole = typeof scanned.header?.agentRole === \"string\" ? scanned.header.agentRole : undefined;\n\t\tconst createdAt = typeof scanned.header?.timestamp === \"string\" ? scanned.header.timestamp : undefined;\n\t\tsummaries.push({\n\t\t\tsessionId,\n\t\t\t...(parentAgentSession === undefined ? {} : { parentAgentSession }),\n\t\t\t...(agentRole === undefined ? {} : { agentRole }),\n\t\t\t...(createdAt === undefined ? {} : { createdAt }),\n\t\t\tmodifiedAt,\n\t\t\tbytes: scanned.bytes,\n\t\t});\n\t}\n\tsummaries.sort((left, right) => right.modifiedAt - left.modifiedAt);\n\treturn Object.freeze(summaries.slice(0, options.limit ?? DEFAULT_LIST_LIMIT));\n}\n"]}