@di-code/coding-agent 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +5 -0
  2. package/dist/core/session/session-manager.d.ts +18 -1
  3. package/dist/core/session/session-manager.d.ts.map +1 -1
  4. package/dist/core/session/session-manager.js +90 -1
  5. package/dist/core/session/session-manager.js.map +1 -1
  6. package/dist/core/session/session-storage.d.ts +2 -0
  7. package/dist/core/session/session-storage.d.ts.map +1 -1
  8. package/dist/core/session/session-storage.js +22 -1
  9. package/dist/core/session/session-storage.js.map +1 -1
  10. package/dist/core/session/types.d.ts +16 -1
  11. package/dist/core/session/types.d.ts.map +1 -1
  12. package/dist/core/session/types.js.map +1 -1
  13. package/dist/core/session.d.ts +27 -2
  14. package/dist/core/session.d.ts.map +1 -1
  15. package/dist/core/session.js +59 -0
  16. package/dist/core/session.js.map +1 -1
  17. package/dist/entry.js +1 -1
  18. package/dist/entry.js.map +1 -1
  19. package/dist/i18n.js +4 -4
  20. package/dist/i18n.js.map +1 -1
  21. package/dist/interactive-host-entry.d.ts.map +1 -1
  22. package/dist/interactive-host-entry.js +107 -12
  23. package/dist/interactive-host-entry.js.map +1 -1
  24. package/dist/main.d.ts.map +1 -1
  25. package/dist/main.js +3 -0
  26. package/dist/main.js.map +1 -1
  27. package/dist/modes/interactive-components.d.ts +8 -0
  28. package/dist/modes/interactive-components.d.ts.map +1 -1
  29. package/dist/modes/interactive-components.js +7 -2
  30. package/dist/modes/interactive-components.js.map +1 -1
  31. package/dist/modes/interactive.d.ts +4 -0
  32. package/dist/modes/interactive.d.ts.map +1 -1
  33. package/dist/modes/interactive.js +69 -0
  34. package/dist/modes/interactive.js.map +1 -1
  35. package/dist/modes/tree-selector.d.ts.map +1 -1
  36. package/dist/modes/tree-selector.js +4 -0
  37. package/dist/modes/tree-selector.js.map +1 -1
  38. package/dist/rpc/client.d.ts +2 -0
  39. package/dist/rpc/client.d.ts.map +1 -1
  40. package/dist/rpc/client.js +3 -0
  41. package/dist/rpc/client.js.map +1 -1
  42. package/dist/rpc/dispatcher.d.ts +14 -0
  43. package/dist/rpc/dispatcher.d.ts.map +1 -1
  44. package/dist/rpc/dispatcher.js +222 -7
  45. package/dist/rpc/dispatcher.js.map +1 -1
  46. package/dist/rpc/protocol.d.ts +12 -5
  47. package/dist/rpc/protocol.d.ts.map +1 -1
  48. package/dist/rpc/protocol.js +34 -0
  49. package/dist/rpc/protocol.js.map +1 -1
  50. package/dist/rpc/server.d.ts +5 -0
  51. package/dist/rpc/server.d.ts.map +1 -1
  52. package/dist/rpc/server.js +4 -0
  53. package/dist/rpc/server.js.map +1 -1
  54. package/dist/rpc-server-entry.d.ts.map +1 -1
  55. package/dist/rpc-server-entry.js +15 -1
  56. package/dist/rpc-server-entry.js.map +1 -1
  57. package/dist/runtime/product-host.d.ts +7 -0
  58. package/dist/runtime/product-host.d.ts.map +1 -1
  59. package/dist/runtime/product-host.js +29 -9
  60. package/dist/runtime/product-host.js.map +1 -1
  61. package/dist/runtime/session-factory.d.ts +18 -2
  62. package/dist/runtime/session-factory.d.ts.map +1 -1
  63. package/dist/runtime/session-factory.js +286 -24
  64. package/dist/runtime/session-factory.js.map +1 -1
  65. package/dist/runtime/session-host.d.ts +46 -1
  66. package/dist/runtime/session-host.d.ts.map +1 -1
  67. package/dist/runtime/session-host.js +38 -6
  68. package/dist/runtime/session-host.js.map +1 -1
  69. package/dist/ui-host.d.ts.map +1 -1
  70. package/dist/ui-host.js +3 -0
  71. package/dist/ui-host.js.map +1 -1
  72. package/dist/web/assets/index-C2SOU925.css +1 -0
  73. package/dist/web/assets/index-knXkInvO.js +41 -0
  74. package/dist/web/assets/index-knXkInvO.js.map +1 -0
  75. package/dist/web/index.html +2 -2
  76. package/dist/web-command.d.ts.map +1 -1
  77. package/dist/web-command.js +8 -1
  78. package/dist/web-command.js.map +1 -1
  79. package/dist/webui-entry.js +9 -1
  80. package/dist/webui-entry.js.map +1 -1
  81. package/dist/webui.d.ts +10 -0
  82. package/dist/webui.d.ts.map +1 -1
  83. package/dist/webui.js +21 -0
  84. package/dist/webui.js.map +1 -1
  85. package/package.json +11 -9
  86. package/dist/web/assets/index-C-NyCPxd.css +0 -1
  87. package/dist/web/assets/index-DgyxuNNG.js +0 -41
  88. package/dist/web/assets/index-DgyxuNNG.js.map +0 -1
package/README.md CHANGED
@@ -78,6 +78,8 @@ npm run dev
78
78
  npm run dev -- --interactive
79
79
  ```
80
80
 
81
+ 交互模式中可使用 `/plan` 进入 Plan Mode,`/plan <message>` 会先启用模式并提交该需求,`/plan off` 退出。命令会显示在 `/` 补全和 `/help` 列表中,不会把命令本身作为用户消息发送。
82
+
81
83
  ### 首次 Provider 向导
82
84
 
83
85
  在真实 TTY 中启动,且没有 `DI_CODE_PROVIDER`、默认 Provider 或唯一已配置 Provider 时,会自动打开向导。向导依次让你:
@@ -104,3 +106,6 @@ README 只保留最短启动路径,其他功能请按主题阅读 GitHub `docs
104
106
  - [插件使用指南](https://github.com/qddidi/di-code/tree/master/docs/插件使用指南.md)
105
107
  - [开发教程](https://github.com/qddidi/di-code/tree/master/docs/开发教程.md)
106
108
  - [运行时与 RPC 架构](https://github.com/qddidi/di-code/tree/master/docs/架构/运行时与RPC.md)
109
+ # User interaction
110
+
111
+ RPC/WebUI hosts expose versioned `interaction_request` events and accept `respond_interaction` replies. Requests correlate `requestId` and optional `toolCallId`; duplicate replies are idempotent, and unavailable channels fail fast. Legacy tool approval remains deny-by-default without a negotiated UI channel.
@@ -1,7 +1,8 @@
1
1
  import type { JsonValue, Message } from "@di-code/ai";
2
+ import type { SessionEventRegistry, SessionProjectionRegistry } from "@di-code/plugin-sdk";
2
3
  import { type BuiltSessionContext } from "../context-builder.ts";
3
4
  import { type SessionAppendOptions } from "./session-storage.ts";
4
- import { type SessionDiagnostic, type SessionEntry, type SessionHeader, type SessionMessageEntry, type SessionPluginEntry, type SessionSummaryEntry, type SessionTreeNode } from "./types.ts";
5
+ import { type SessionDiagnostic, type SessionEntry, type SessionEventEntry, type SessionHeader, type SessionMessageEntry, type SessionPluginEntry, type SessionSummaryEntry, type SessionTreeNode } from "./types.ts";
5
6
  export interface SessionManagerCreateOptions {
6
7
  readonly filePath: string;
7
8
  readonly cwd: string;
@@ -9,11 +10,13 @@ export interface SessionManagerCreateOptions {
9
10
  readonly createId?: () => string;
10
11
  readonly appendOptions?: SessionAppendOptions;
11
12
  readonly deferCreate?: boolean;
13
+ readonly eventRegistry?: SessionEventRegistry;
12
14
  }
13
15
  export interface SessionManagerOpenOptions {
14
16
  readonly now?: () => number;
15
17
  readonly createId?: () => string;
16
18
  readonly appendOptions?: SessionAppendOptions;
19
+ readonly eventRegistry?: SessionEventRegistry;
17
20
  }
18
21
  export declare class SessionManager {
19
22
  readonly filePath: string;
@@ -21,6 +24,7 @@ export declare class SessionManager {
21
24
  private readonly now;
22
25
  private readonly createId;
23
26
  private readonly appendOptions;
27
+ private eventRegistry?;
24
28
  private readonly sessionEntries;
25
29
  private readonly entriesById;
26
30
  private readonly childrenByParentId;
@@ -34,6 +38,9 @@ export declare class SessionManager {
34
38
  get entries(): readonly SessionEntry[];
35
39
  get header(): SessionHeader;
36
40
  get messages(): readonly Message[];
41
+ get events(): readonly SessionEventEntry[];
42
+ /** Cold-reads the complete durable event log; no in-memory mirror is used as the source of truth. */
43
+ project(registry: SessionProjectionRegistry): readonly unknown[];
37
44
  getEntry(id: string): SessionEntry | undefined;
38
45
  getTree(): readonly SessionTreeNode[];
39
46
  getBranch(leafId?: string): readonly SessionEntry[];
@@ -42,6 +49,8 @@ export declare class SessionManager {
42
49
  buildContext(leafId?: string): BuiltSessionContext;
43
50
  get latestSummary(): SessionSummaryEntry | undefined;
44
51
  get diagnostics(): readonly SessionDiagnostic[];
52
+ /** Binds the active plugin registry and diagnoses already-loaded extension events. */
53
+ bindEventRegistry(registry: SessionEventRegistry): void;
45
54
  get leafId(): string;
46
55
  private ensureFileCreated;
47
56
  appendMessage(message: Message): Promise<SessionMessageEntry>;
@@ -57,6 +66,14 @@ export declare class SessionManager {
57
66
  readonly schemaVersion: number;
58
67
  readonly data: JsonValue;
59
68
  }): Promise<SessionPluginEntry>;
69
+ /** Appends a versioned plugin event through the same queue and file lock as messages. */
70
+ appendEvent(input: {
71
+ readonly namespace: string;
72
+ readonly eventName: string;
73
+ readonly schemaVersion: number;
74
+ readonly payload: JsonValue;
75
+ readonly signal?: AbortSignal;
76
+ }): Promise<SessionEventEntry>;
60
77
  private addToIndexes;
61
78
  }
62
79
  //# sourceMappingURL=session-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@ export class SessionManager {
21
21
  now;
22
22
  createId;
23
23
  appendOptions;
24
+ eventRegistry;
24
25
  sessionEntries;
25
26
  entriesById = new Map();
26
27
  childrenByParentId = new Map();
@@ -39,6 +40,7 @@ export class SessionManager {
39
40
  this.now = options.now ?? Date.now;
40
41
  this.createId = options.createId ?? randomUUID;
41
42
  this.appendOptions = options.appendOptions ?? {};
43
+ this.eventRegistry = options.eventRegistry;
42
44
  this.fileCreated = fileCreated;
43
45
  }
44
46
  static async create(options) {
@@ -58,7 +60,7 @@ export class SessionManager {
58
60
  };
59
61
  if (!options.deferCreate)
60
62
  await createSessionFile(filePath, header);
61
- return new SessionManager(filePath, { header, entries: [], messages: [], diagnostics: [] }, { now, createId, appendOptions: options.appendOptions }, !options.deferCreate);
63
+ return new SessionManager(filePath, { header, entries: [], messages: [], diagnostics: [] }, { now, createId, appendOptions: options.appendOptions, eventRegistry: options.eventRegistry }, !options.deferCreate);
62
64
  }
63
65
  static async open(filePath, options = {}) {
64
66
  const resolvedPath = resolve(filePath);
@@ -75,6 +77,18 @@ export class SessionManager {
75
77
  .filter((entry) => entry.type === "message")
76
78
  .map((entry) => entry.message));
77
79
  }
80
+ get events() {
81
+ return structuredClone(this.sessionEntries.filter((entry) => entry.type === "event"));
82
+ }
83
+ /** Cold-reads the complete durable event log; no in-memory mirror is used as the source of truth. */
84
+ project(registry) {
85
+ return registry.replay(this.events.map((event) => ({
86
+ namespace: event.namespace,
87
+ eventName: event.eventName,
88
+ schemaVersion: event.schemaVersion,
89
+ payload: event.payload,
90
+ })));
91
+ }
78
92
  getEntry(id) {
79
93
  const entry = this.entriesById.get(id);
80
94
  return entry ? structuredClone(entry) : undefined;
@@ -132,6 +146,32 @@ export class SessionManager {
132
146
  get diagnostics() {
133
147
  return structuredClone(this.sessionDiagnostics);
134
148
  }
149
+ /** Binds the active plugin registry and diagnoses already-loaded extension events. */
150
+ bindEventRegistry(registry) {
151
+ this.eventRegistry = registry;
152
+ for (let index = this.sessionDiagnostics.length - 1; index >= 0; index--)
153
+ if (this.sessionDiagnostics[index]?.kind === "event_validation")
154
+ this.sessionDiagnostics.splice(index, 1);
155
+ for (const event of this.events) {
156
+ try {
157
+ registry.migrate({
158
+ namespace: event.namespace,
159
+ eventName: event.eventName,
160
+ schemaVersion: event.schemaVersion,
161
+ payload: event.payload,
162
+ });
163
+ }
164
+ catch (error) {
165
+ this.sessionDiagnostics.push({
166
+ kind: "event_validation",
167
+ namespace: event.namespace,
168
+ eventName: event.eventName,
169
+ schemaVersion: event.schemaVersion,
170
+ reason: error instanceof Error ? error.message : String(error),
171
+ });
172
+ }
173
+ }
174
+ }
135
175
  get leafId() {
136
176
  return this.activeLeafId;
137
177
  }
@@ -219,6 +259,55 @@ export class SessionManager {
219
259
  this.appendQueue = operation.then(() => undefined, () => undefined);
220
260
  return operation;
221
261
  }
262
+ /** Appends a versioned plugin event through the same queue and file lock as messages. */
263
+ appendEvent(input) {
264
+ const inputSnapshot = {
265
+ namespace: input.namespace,
266
+ eventName: input.eventName,
267
+ schemaVersion: input.schemaVersion,
268
+ payload: structuredClone(input.payload),
269
+ signal: input.signal,
270
+ };
271
+ const validated = this.eventRegistry?.migrate({
272
+ namespace: inputSnapshot.namespace,
273
+ eventName: inputSnapshot.eventName,
274
+ schemaVersion: inputSnapshot.schemaVersion,
275
+ payload: inputSnapshot.payload,
276
+ });
277
+ if (validated) {
278
+ inputSnapshot.schemaVersion = validated.schemaVersion;
279
+ inputSnapshot.payload = validated.payload;
280
+ }
281
+ const operation = this.appendQueue.then(async () => {
282
+ if (inputSnapshot.signal?.aborted)
283
+ throw inputSnapshot.signal.reason ?? new DOMException("The operation was aborted", "AbortError");
284
+ await this.ensureFileCreated();
285
+ const id = this.createId();
286
+ assertRecordId(id);
287
+ const expectedParentId = this.leafId;
288
+ const entry = {
289
+ type: "event",
290
+ version: SESSION_FORMAT_VERSION,
291
+ id,
292
+ parentId: expectedParentId,
293
+ timestamp: createIsoTimestamp(this.now),
294
+ namespace: inputSnapshot.namespace,
295
+ eventName: inputSnapshot.eventName,
296
+ schemaVersion: inputSnapshot.schemaVersion,
297
+ payload: inputSnapshot.payload,
298
+ };
299
+ await appendSessionEntry(this.filePath, entry, expectedParentId, {
300
+ ...this.appendOptions,
301
+ signal: inputSnapshot.signal,
302
+ });
303
+ this.sessionEntries.push(structuredClone(entry));
304
+ this.addToIndexes(entry);
305
+ this.activeLeafId = entry.id;
306
+ return structuredClone(entry);
307
+ });
308
+ this.appendQueue = operation.then(() => undefined, () => undefined);
309
+ return operation;
310
+ }
222
311
  addToIndexes(entry) {
223
312
  this.entriesById.set(entry.id, structuredClone(entry));
224
313
  const children = this.childrenByParentId.get(entry.parentId) ?? [];
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { resolve } from \"node:path\";\nimport type { JsonValue, Message } from \"@di-code/ai\";\nimport { type BuiltSessionContext, buildSessionContext } from \"../context-builder.ts\";\nimport {\n\tappendSessionEntry,\n\tcreateSessionFile,\n\tloadSessionFile,\n\ttype SessionAppendOptions,\n} from \"./session-storage.ts\";\nimport {\n\ttype LoadedSession,\n\tSESSION_FORMAT_VERSION,\n\ttype SessionDiagnostic,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionMessageEntry,\n\ttype SessionPluginEntry,\n\ttype SessionSummaryEntry,\n\ttype SessionTreeNode,\n} from \"./types.ts\";\n\nexport interface SessionManagerCreateOptions {\n\treadonly filePath: string;\n\treadonly cwd: string;\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n\treadonly deferCreate?: boolean;\n}\n\nexport interface SessionManagerOpenOptions {\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n}\n\nfunction assertRecordId(id: string): void {\n\tif (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(id)) {\n\t\tthrow new Error(`Generated session record id is invalid: ${JSON.stringify(id)}.`);\n\t}\n}\n\nfunction createIsoTimestamp(now: () => number): string {\n\tconst milliseconds = now();\n\tif (!Number.isFinite(milliseconds)) {\n\t\tthrow new Error(\"Session clock must return a finite Unix millisecond timestamp.\");\n\t}\n\treturn new Date(milliseconds).toISOString();\n}\n\nexport class SessionManager {\n\treadonly filePath: string;\n\tprivate readonly sessionHeader: SessionHeader;\n\tprivate readonly now: () => number;\n\tprivate readonly createId: () => string;\n\tprivate readonly appendOptions: SessionAppendOptions;\n\tprivate readonly sessionEntries: SessionEntry[];\n\tprivate readonly entriesById = new Map<string, SessionEntry>();\n\tprivate readonly childrenByParentId = new Map<string, string[]>();\n\tprivate readonly sessionDiagnostics: SessionDiagnostic[];\n\tprivate activeLeafId: string;\n\tprivate fileCreated: boolean;\n\tprivate appendQueue: Promise<void> = Promise.resolve();\n\n\tprivate constructor(filePath: string, loaded: LoadedSession, options: SessionManagerOpenOptions, fileCreated = true) {\n\t\tthis.filePath = filePath;\n\t\tthis.sessionHeader = structuredClone(loaded.header);\n\t\tthis.sessionEntries = structuredClone([...loaded.entries]);\n\t\tfor (const entry of this.sessionEntries) this.addToIndexes(entry);\n\t\tthis.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t\tthis.sessionDiagnostics = structuredClone([...loaded.diagnostics]);\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.createId = options.createId ?? randomUUID;\n\t\tthis.appendOptions = options.appendOptions ?? {};\n\t\tthis.fileCreated = fileCreated;\n\t}\n\n\tstatic async create(options: SessionManagerCreateOptions): Promise<SessionManager> {\n\t\tconst filePath = resolve(options.filePath);\n\t\tconst cwd = resolve(options.cwd);\n\t\tconst now = options.now ?? Date.now;\n\t\tconst createId = options.createId ?? randomUUID;\n\t\tconst id = createId();\n\t\tassertRecordId(id);\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\tid,\n\t\t\tparentId: null,\n\t\t\ttimestamp: createIsoTimestamp(now),\n\t\t\tcwd,\n\t\t};\n\t\tif (!options.deferCreate) await createSessionFile(filePath, header);\n\t\treturn new SessionManager(\n\t\t\tfilePath,\n\t\t\t{ header, entries: [], messages: [], diagnostics: [] },\n\t\t\t{ now, createId, appendOptions: options.appendOptions },\n\t\t\t!options.deferCreate,\n\t\t);\n\t}\n\n\tstatic async open(filePath: string, options: SessionManagerOpenOptions = {}): Promise<SessionManager> {\n\t\tconst resolvedPath = resolve(filePath);\n\t\treturn new SessionManager(resolvedPath, await loadSessionFile(resolvedPath), options);\n\t}\n\n\tget entries(): readonly SessionEntry[] {\n\t\treturn structuredClone(this.sessionEntries);\n\t}\n\n\tget header(): SessionHeader {\n\t\treturn structuredClone(this.sessionHeader);\n\t}\n\n\tget messages(): readonly Message[] {\n\t\treturn structuredClone(\n\t\t\tthis.sessionEntries\n\t\t\t\t.filter((entry): entry is SessionMessageEntry => entry.type === \"message\")\n\t\t\t\t.map((entry) => entry.message),\n\t\t);\n\t}\n\n\tgetEntry(id: string): SessionEntry | undefined {\n\t\tconst entry = this.entriesById.get(id);\n\t\treturn entry ? structuredClone(entry) : undefined;\n\t}\n\n\tgetTree(): readonly SessionTreeNode[] {\n\t\tconst nodes = new Map<string, { entry: SessionEntry; children: SessionTreeNode[] }>();\n\t\tconst roots: SessionTreeNode[] = [];\n\t\tfor (const entry of this.sessionEntries) {\n\t\t\tconst node = { entry: structuredClone(entry), children: [] as SessionTreeNode[] };\n\t\t\tnodes.set(entry.id, node);\n\t\t\tif (entry.parentId === this.sessionHeader.id) {\n\t\t\t\troots.push(node);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst parent = nodes.get(entry.parentId);\n\t\t\tif (!parent) throw new Error(`Session tree parent \"${entry.parentId}\" is missing.`);\n\t\t\tparent.children.push(node);\n\t\t}\n\t\treturn roots;\n\t}\n\n\tgetBranch(leafId = this.activeLeafId): readonly SessionEntry[] {\n\t\tconst branch: SessionEntry[] = [];\n\t\tlet currentId = leafId;\n\t\twhile (currentId !== this.sessionHeader.id) {\n\t\t\tconst entry = this.entriesById.get(currentId);\n\t\t\tif (!entry) throw new Error(`Unknown session leaf \"${currentId}\".`);\n\t\t\tbranch.push(structuredClone(entry));\n\t\t\tcurrentId = entry.parentId;\n\t\t}\n\t\tbranch.reverse();\n\t\treturn branch;\n\t}\n\n\tsetLeaf(id: string): void {\n\t\tif (id !== this.sessionHeader.id && !this.entriesById.has(id)) throw new Error(`Unknown session leaf \"${id}\".`);\n\t\tthis.activeLeafId = id;\n\t}\n\n\tresetLeaf(): void {\n\t\tthis.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t}\n\n\tbuildContext(leafId = this.activeLeafId): BuiltSessionContext {\n\t\treturn buildSessionContext(this.getBranch(leafId));\n\t}\n\n\tget latestSummary(): SessionSummaryEntry | undefined {\n\t\tconst branch = this.getBranch();\n\t\tfor (let index = branch.length - 1; index >= 0; index--) {\n\t\t\tconst entry = branch[index];\n\t\t\tif (entry?.type === \"summary\") return structuredClone(entry);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tget diagnostics(): readonly SessionDiagnostic[] {\n\t\treturn structuredClone(this.sessionDiagnostics);\n\t}\n\n\tget leafId(): string {\n\t\treturn this.activeLeafId;\n\t}\n\n\tprivate async ensureFileCreated(): Promise<void> {\n\t\tif (this.fileCreated) return;\n\t\tawait createSessionFile(this.filePath, this.sessionHeader);\n\t\tthis.fileCreated = true;\n\t}\n\n\tappendMessage(message: Message): Promise<SessionMessageEntry> {\n\t\tconst messageSnapshot = structuredClone(message);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionMessageEntry = {\n\t\t\t\ttype: \"message\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tmessage: messageSnapshot,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tappendSummary(input: {\n\t\treadonly summary: string;\n\t\treadonly firstKeptEntryId: string;\n\t\treadonly tokensBefore: number;\n\t}): Promise<SessionSummaryEntry> {\n\t\tconst inputSnapshot = structuredClone(input);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionSummaryEntry = {\n\t\t\t\ttype: \"summary\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tsummary: inputSnapshot.summary,\n\t\t\t\tfirstKeptEntryId: inputSnapshot.firstKeptEntryId,\n\t\t\t\ttokensBefore: inputSnapshot.tokensBefore,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\t/** Appends host-owned metadata without exposing it as a transcript message. */\n\tappendPlugin(input: {\n\t\treadonly pluginId: string;\n\t\treadonly pluginVersion: string;\n\t\treadonly schemaVersion: number;\n\t\treadonly data: JsonValue;\n\t}): Promise<SessionPluginEntry> {\n\t\tconst inputSnapshot = structuredClone(input);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionPluginEntry = {\n\t\t\t\ttype: \"plugin\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tpluginId: inputSnapshot.pluginId,\n\t\t\t\tpluginVersion: inputSnapshot.pluginVersion,\n\t\t\t\tschemaVersion: inputSnapshot.schemaVersion,\n\t\t\t\tdata: inputSnapshot.data,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tprivate addToIndexes(entry: SessionEntry): void {\n\t\tthis.entriesById.set(entry.id, structuredClone(entry));\n\t\tconst children = this.childrenByParentId.get(entry.parentId) ?? [];\n\t\tchildren.push(entry.id);\n\t\tthis.childrenByParentId.set(entry.parentId, children);\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/core/session/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { resolve } from \"node:path\";\nimport type { JsonValue, Message } from \"@di-code/ai\";\nimport type { SessionEventRegistry, SessionProjectionRegistry } from \"@di-code/plugin-sdk\";\nimport { type BuiltSessionContext, buildSessionContext } from \"../context-builder.ts\";\nimport {\n\tappendSessionEntry,\n\tcreateSessionFile,\n\tloadSessionFile,\n\ttype SessionAppendOptions,\n} from \"./session-storage.ts\";\nimport {\n\ttype LoadedSession,\n\tSESSION_FORMAT_VERSION,\n\ttype SessionDiagnostic,\n\ttype SessionEntry,\n\ttype SessionEventEntry,\n\ttype SessionHeader,\n\ttype SessionMessageEntry,\n\ttype SessionPluginEntry,\n\ttype SessionSummaryEntry,\n\ttype SessionTreeNode,\n} from \"./types.ts\";\n\nexport interface SessionManagerCreateOptions {\n\treadonly filePath: string;\n\treadonly cwd: string;\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n\treadonly deferCreate?: boolean;\n\treadonly eventRegistry?: SessionEventRegistry;\n}\n\nexport interface SessionManagerOpenOptions {\n\treadonly now?: () => number;\n\treadonly createId?: () => string;\n\treadonly appendOptions?: SessionAppendOptions;\n\treadonly eventRegistry?: SessionEventRegistry;\n}\n\nfunction assertRecordId(id: string): void {\n\tif (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(id)) {\n\t\tthrow new Error(`Generated session record id is invalid: ${JSON.stringify(id)}.`);\n\t}\n}\n\nfunction createIsoTimestamp(now: () => number): string {\n\tconst milliseconds = now();\n\tif (!Number.isFinite(milliseconds)) {\n\t\tthrow new Error(\"Session clock must return a finite Unix millisecond timestamp.\");\n\t}\n\treturn new Date(milliseconds).toISOString();\n}\n\nexport class SessionManager {\n\treadonly filePath: string;\n\tprivate readonly sessionHeader: SessionHeader;\n\tprivate readonly now: () => number;\n\tprivate readonly createId: () => string;\n\tprivate readonly appendOptions: SessionAppendOptions;\n\tprivate eventRegistry?: SessionEventRegistry;\n\tprivate readonly sessionEntries: SessionEntry[];\n\tprivate readonly entriesById = new Map<string, SessionEntry>();\n\tprivate readonly childrenByParentId = new Map<string, string[]>();\n\tprivate readonly sessionDiagnostics: SessionDiagnostic[];\n\tprivate activeLeafId: string;\n\tprivate fileCreated: boolean;\n\tprivate appendQueue: Promise<void> = Promise.resolve();\n\n\tprivate constructor(filePath: string, loaded: LoadedSession, options: SessionManagerOpenOptions, fileCreated = true) {\n\t\tthis.filePath = filePath;\n\t\tthis.sessionHeader = structuredClone(loaded.header);\n\t\tthis.sessionEntries = structuredClone([...loaded.entries]);\n\t\tfor (const entry of this.sessionEntries) this.addToIndexes(entry);\n\t\tthis.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t\tthis.sessionDiagnostics = structuredClone([...loaded.diagnostics]);\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.createId = options.createId ?? randomUUID;\n\t\tthis.appendOptions = options.appendOptions ?? {};\n\t\tthis.eventRegistry = options.eventRegistry;\n\t\tthis.fileCreated = fileCreated;\n\t}\n\n\tstatic async create(options: SessionManagerCreateOptions): Promise<SessionManager> {\n\t\tconst filePath = resolve(options.filePath);\n\t\tconst cwd = resolve(options.cwd);\n\t\tconst now = options.now ?? Date.now;\n\t\tconst createId = options.createId ?? randomUUID;\n\t\tconst id = createId();\n\t\tassertRecordId(id);\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\tid,\n\t\t\tparentId: null,\n\t\t\ttimestamp: createIsoTimestamp(now),\n\t\t\tcwd,\n\t\t};\n\t\tif (!options.deferCreate) await createSessionFile(filePath, header);\n\t\treturn new SessionManager(\n\t\t\tfilePath,\n\t\t\t{ header, entries: [], messages: [], diagnostics: [] },\n\t\t\t{ now, createId, appendOptions: options.appendOptions, eventRegistry: options.eventRegistry },\n\t\t\t!options.deferCreate,\n\t\t);\n\t}\n\n\tstatic async open(filePath: string, options: SessionManagerOpenOptions = {}): Promise<SessionManager> {\n\t\tconst resolvedPath = resolve(filePath);\n\t\treturn new SessionManager(resolvedPath, await loadSessionFile(resolvedPath), options);\n\t}\n\n\tget entries(): readonly SessionEntry[] {\n\t\treturn structuredClone(this.sessionEntries);\n\t}\n\n\tget header(): SessionHeader {\n\t\treturn structuredClone(this.sessionHeader);\n\t}\n\n\tget messages(): readonly Message[] {\n\t\treturn structuredClone(\n\t\t\tthis.sessionEntries\n\t\t\t\t.filter((entry): entry is SessionMessageEntry => entry.type === \"message\")\n\t\t\t\t.map((entry) => entry.message),\n\t\t);\n\t}\n\n\tget events(): readonly SessionEventEntry[] {\n\t\treturn structuredClone(this.sessionEntries.filter((entry): entry is SessionEventEntry => entry.type === \"event\"));\n\t}\n\n\t/** Cold-reads the complete durable event log; no in-memory mirror is used as the source of truth. */\n\tproject(registry: SessionProjectionRegistry): readonly unknown[] {\n\t\treturn registry.replay(\n\t\t\tthis.events.map((event) => ({\n\t\t\t\tnamespace: event.namespace,\n\t\t\t\teventName: event.eventName,\n\t\t\t\tschemaVersion: event.schemaVersion,\n\t\t\t\tpayload: event.payload,\n\t\t\t})),\n\t\t);\n\t}\n\n\tgetEntry(id: string): SessionEntry | undefined {\n\t\tconst entry = this.entriesById.get(id);\n\t\treturn entry ? structuredClone(entry) : undefined;\n\t}\n\n\tgetTree(): readonly SessionTreeNode[] {\n\t\tconst nodes = new Map<string, { entry: SessionEntry; children: SessionTreeNode[] }>();\n\t\tconst roots: SessionTreeNode[] = [];\n\t\tfor (const entry of this.sessionEntries) {\n\t\t\tconst node = { entry: structuredClone(entry), children: [] as SessionTreeNode[] };\n\t\t\tnodes.set(entry.id, node);\n\t\t\tif (entry.parentId === this.sessionHeader.id) {\n\t\t\t\troots.push(node);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst parent = nodes.get(entry.parentId);\n\t\t\tif (!parent) throw new Error(`Session tree parent \"${entry.parentId}\" is missing.`);\n\t\t\tparent.children.push(node);\n\t\t}\n\t\treturn roots;\n\t}\n\n\tgetBranch(leafId = this.activeLeafId): readonly SessionEntry[] {\n\t\tconst branch: SessionEntry[] = [];\n\t\tlet currentId = leafId;\n\t\twhile (currentId !== this.sessionHeader.id) {\n\t\t\tconst entry = this.entriesById.get(currentId);\n\t\t\tif (!entry) throw new Error(`Unknown session leaf \"${currentId}\".`);\n\t\t\tbranch.push(structuredClone(entry));\n\t\t\tcurrentId = entry.parentId;\n\t\t}\n\t\tbranch.reverse();\n\t\treturn branch;\n\t}\n\n\tsetLeaf(id: string): void {\n\t\tif (id !== this.sessionHeader.id && !this.entriesById.has(id)) throw new Error(`Unknown session leaf \"${id}\".`);\n\t\tthis.activeLeafId = id;\n\t}\n\n\tresetLeaf(): void {\n\t\tthis.activeLeafId = this.sessionEntries.at(-1)?.id ?? this.sessionHeader.id;\n\t}\n\n\tbuildContext(leafId = this.activeLeafId): BuiltSessionContext {\n\t\treturn buildSessionContext(this.getBranch(leafId));\n\t}\n\n\tget latestSummary(): SessionSummaryEntry | undefined {\n\t\tconst branch = this.getBranch();\n\t\tfor (let index = branch.length - 1; index >= 0; index--) {\n\t\t\tconst entry = branch[index];\n\t\t\tif (entry?.type === \"summary\") return structuredClone(entry);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tget diagnostics(): readonly SessionDiagnostic[] {\n\t\treturn structuredClone(this.sessionDiagnostics);\n\t}\n\n\t/** Binds the active plugin registry and diagnoses already-loaded extension events. */\n\tbindEventRegistry(registry: SessionEventRegistry): void {\n\t\tthis.eventRegistry = registry;\n\t\tfor (let index = this.sessionDiagnostics.length - 1; index >= 0; index--)\n\t\t\tif (this.sessionDiagnostics[index]?.kind === \"event_validation\") this.sessionDiagnostics.splice(index, 1);\n\t\tfor (const event of this.events) {\n\t\t\ttry {\n\t\t\t\tregistry.migrate({\n\t\t\t\t\tnamespace: event.namespace,\n\t\t\t\t\teventName: event.eventName,\n\t\t\t\t\tschemaVersion: event.schemaVersion,\n\t\t\t\t\tpayload: event.payload,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthis.sessionDiagnostics.push({\n\t\t\t\t\tkind: \"event_validation\",\n\t\t\t\t\tnamespace: event.namespace,\n\t\t\t\t\teventName: event.eventName,\n\t\t\t\t\tschemaVersion: event.schemaVersion,\n\t\t\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tget leafId(): string {\n\t\treturn this.activeLeafId;\n\t}\n\n\tprivate async ensureFileCreated(): Promise<void> {\n\t\tif (this.fileCreated) return;\n\t\tawait createSessionFile(this.filePath, this.sessionHeader);\n\t\tthis.fileCreated = true;\n\t}\n\n\tappendMessage(message: Message): Promise<SessionMessageEntry> {\n\t\tconst messageSnapshot = structuredClone(message);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionMessageEntry = {\n\t\t\t\ttype: \"message\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tmessage: messageSnapshot,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tappendSummary(input: {\n\t\treadonly summary: string;\n\t\treadonly firstKeptEntryId: string;\n\t\treadonly tokensBefore: number;\n\t}): Promise<SessionSummaryEntry> {\n\t\tconst inputSnapshot = structuredClone(input);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionSummaryEntry = {\n\t\t\t\ttype: \"summary\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tsummary: inputSnapshot.summary,\n\t\t\t\tfirstKeptEntryId: inputSnapshot.firstKeptEntryId,\n\t\t\t\ttokensBefore: inputSnapshot.tokensBefore,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\t/** Appends host-owned metadata without exposing it as a transcript message. */\n\tappendPlugin(input: {\n\t\treadonly pluginId: string;\n\t\treadonly pluginVersion: string;\n\t\treadonly schemaVersion: number;\n\t\treadonly data: JsonValue;\n\t}): Promise<SessionPluginEntry> {\n\t\tconst inputSnapshot = structuredClone(input);\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionPluginEntry = {\n\t\t\t\ttype: \"plugin\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tpluginId: inputSnapshot.pluginId,\n\t\t\t\tpluginVersion: inputSnapshot.pluginVersion,\n\t\t\t\tschemaVersion: inputSnapshot.schemaVersion,\n\t\t\t\tdata: inputSnapshot.data,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, this.appendOptions);\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\t/** Appends a versioned plugin event through the same queue and file lock as messages. */\n\tappendEvent(input: {\n\t\treadonly namespace: string;\n\t\treadonly eventName: string;\n\t\treadonly schemaVersion: number;\n\t\treadonly payload: JsonValue;\n\t\treadonly signal?: AbortSignal;\n\t}): Promise<SessionEventEntry> {\n\t\tconst inputSnapshot = {\n\t\t\tnamespace: input.namespace,\n\t\t\teventName: input.eventName,\n\t\t\tschemaVersion: input.schemaVersion,\n\t\t\tpayload: structuredClone(input.payload),\n\t\t\tsignal: input.signal,\n\t\t};\n\t\tconst validated = this.eventRegistry?.migrate({\n\t\t\tnamespace: inputSnapshot.namespace,\n\t\t\teventName: inputSnapshot.eventName,\n\t\t\tschemaVersion: inputSnapshot.schemaVersion,\n\t\t\tpayload: inputSnapshot.payload,\n\t\t});\n\t\tif (validated) {\n\t\t\tinputSnapshot.schemaVersion = validated.schemaVersion;\n\t\t\tinputSnapshot.payload = validated.payload;\n\t\t}\n\t\tconst operation = this.appendQueue.then(async () => {\n\t\t\tif (inputSnapshot.signal?.aborted)\n\t\t\t\tthrow inputSnapshot.signal.reason ?? new DOMException(\"The operation was aborted\", \"AbortError\");\n\t\t\tawait this.ensureFileCreated();\n\t\t\tconst id = this.createId();\n\t\t\tassertRecordId(id);\n\t\t\tconst expectedParentId = this.leafId;\n\t\t\tconst entry: SessionEventEntry = {\n\t\t\t\ttype: \"event\",\n\t\t\t\tversion: SESSION_FORMAT_VERSION,\n\t\t\t\tid,\n\t\t\t\tparentId: expectedParentId,\n\t\t\t\ttimestamp: createIsoTimestamp(this.now),\n\t\t\t\tnamespace: inputSnapshot.namespace,\n\t\t\t\teventName: inputSnapshot.eventName,\n\t\t\t\tschemaVersion: inputSnapshot.schemaVersion,\n\t\t\t\tpayload: inputSnapshot.payload,\n\t\t\t};\n\t\t\tawait appendSessionEntry(this.filePath, entry, expectedParentId, {\n\t\t\t\t...this.appendOptions,\n\t\t\t\tsignal: inputSnapshot.signal,\n\t\t\t});\n\t\t\tthis.sessionEntries.push(structuredClone(entry));\n\t\t\tthis.addToIndexes(entry);\n\t\t\tthis.activeLeafId = entry.id;\n\t\t\treturn structuredClone(entry);\n\t\t});\n\t\tthis.appendQueue = operation.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn operation;\n\t}\n\n\tprivate addToIndexes(entry: SessionEntry): void {\n\t\tthis.entriesById.set(entry.id, structuredClone(entry));\n\t\tconst children = this.childrenByParentId.get(entry.parentId) ?? [];\n\t\tchildren.push(entry.id);\n\t\tthis.childrenByParentId.set(entry.parentId, children);\n\t}\n}\n"]}
@@ -1,10 +1,12 @@
1
1
  import { type LoadedSession, type SessionEntry, type SessionHeader } from "./types.ts";
2
+ export declare const MAX_SESSION_EVENT_PAYLOAD_BYTES: number;
2
3
  export declare function loadSessionFile(filePath: string): Promise<LoadedSession>;
3
4
  export interface SessionAppendOptions {
4
5
  readonly lockTimeoutMs?: number;
5
6
  readonly lockRetryMs?: number;
6
7
  readonly now?: () => number;
7
8
  readonly sleep?: (milliseconds: number) => Promise<void>;
9
+ readonly signal?: AbortSignal;
8
10
  }
9
11
  export declare function createSessionFile(filePath: string, header: SessionHeader): Promise<void>;
10
12
  export declare function appendSessionEntry(filePath: string, entry: SessionEntry, expectedParentId: string, options?: SessionAppendOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-storage.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,YAAY,EACjB,KAAK,aAAa,EAMlB,MAAM,YAAY,CAAC;AAsOpB,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiF9E;AAkBD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAaD,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F;AAoDD,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CAmFf"}
1
+ {"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-storage.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,YAAY,EAEjB,KAAK,aAAa,EAMlB,MAAM,YAAY,CAAC;AAIpB,eAAO,MAAM,+BAA+B,QAAa,CAAC;AAwP1D,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiF9E;AAkBD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B;AAaD,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F;AAsDD,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CAqFf"}
@@ -2,6 +2,7 @@ import { appendFile, mkdir, open, readFile, unlink } from "node:fs/promises";
2
2
  import { dirname, posix, win32 } from "node:path";
3
3
  import { SESSION_FORMAT_VERSION, SessionLoadError, SessionWriteError, } from "./types.js";
4
4
  const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
5
+ export const MAX_SESSION_EVENT_PAYLOAD_BYTES = 256 * 1024;
5
6
  function isObject(value) {
6
7
  return typeof value === "object" && value !== null && !Array.isArray(value);
7
8
  }
@@ -35,6 +36,9 @@ function isJsonValue(value) {
35
36
  return value.every(isJsonValue);
36
37
  return isObject(value) && Object.values(value).every(isJsonValue);
37
38
  }
39
+ function isBoundedJsonValue(value) {
40
+ return isJsonValue(value) && Buffer.byteLength(JSON.stringify(value), "utf8") <= MAX_SESSION_EVENT_PAYLOAD_BYTES;
41
+ }
38
42
  function isProviderReplay(value) {
39
43
  return isObject(value) && isNonEmptyString(value.api) && isJsonValue(value.data);
40
44
  }
@@ -154,7 +158,8 @@ function decodeHeader(value, filePath) {
154
158
  return value;
155
159
  }
156
160
  function decodeSessionEntry(value) {
157
- if (!isObject(value) || (value.type !== "message" && value.type !== "summary" && value.type !== "plugin")) {
161
+ if (!isObject(value) ||
162
+ (value.type !== "message" && value.type !== "summary" && value.type !== "plugin" && value.type !== "event")) {
158
163
  return { reason: 'record type must be "message", "summary", or "plugin"' };
159
164
  }
160
165
  if (value.version !== SESSION_FORMAT_VERSION)
@@ -182,6 +187,18 @@ function decodeSessionEntry(value) {
182
187
  return { reason: "record plugin fields are invalid" };
183
188
  return { entry: value };
184
189
  }
190
+ if (value.type === "event") {
191
+ if (typeof value.namespace !== "string" ||
192
+ !/^[a-z0-9][a-z0-9._-]*$/.test(value.namespace) ||
193
+ typeof value.eventName !== "string" ||
194
+ !/^[a-z0-9][a-z0-9._-]*$/.test(value.eventName) ||
195
+ typeof value.schemaVersion !== "number" ||
196
+ !Number.isSafeInteger(value.schemaVersion) ||
197
+ value.schemaVersion < 0 ||
198
+ !isBoundedJsonValue(value.payload))
199
+ return { reason: "record event fields are invalid or payload exceeds the limit" };
200
+ return { entry: value };
201
+ }
185
202
  if (typeof value.summary !== "string" || value.summary.trim().length === 0) {
186
203
  return { reason: "record summary must be a non-empty string" };
187
204
  }
@@ -327,6 +344,8 @@ async function acquireLock(filePath, options) {
327
344
  const sleep = options.sleep ?? delay;
328
345
  const startedAt = now();
329
346
  while (true) {
347
+ if (options.signal?.aborted)
348
+ throw options.signal.reason ?? new DOMException("The operation was aborted", "AbortError");
330
349
  try {
331
350
  return { handle: await open(lockPath, "wx"), lockPath };
332
351
  }
@@ -369,6 +388,8 @@ async function releaseLock(handle, lockPath) {
369
388
  throw closeError;
370
389
  }
371
390
  export async function appendSessionEntry(filePath, entry, expectedParentId, options = {}) {
391
+ if (options.signal?.aborted)
392
+ throw options.signal.reason ?? new DOMException("The operation was aborted", "AbortError");
372
393
  const entrySnapshot = structuredClone(entry);
373
394
  const decodedEntry = decodeSessionEntry(entrySnapshot);
374
395
  if (!decodedEntry.entry) {
@@ -1 +1 @@
1
- {"version":3,"file":"session-storage.js","sourceRoot":"","sources":["../../../src/core/session/session-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFile, mkdir, open, readFile, unlink } from \"node:fs/promises\";\nimport { dirname, posix, win32 } from \"node:path\";\nimport type { AssistantContent, AssistantMessage, Message, ToolResultContent, Usage, UserContent } from \"@di-code/ai\";\nimport {\n\ttype LoadedSession,\n\tSESSION_FORMAT_VERSION,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\tSessionLoadError,\n\ttype SessionMessageEntry,\n\ttype SessionPluginEntry,\n\ttype SessionSummaryEntry,\n\tSessionWriteError,\n} from \"./types.ts\";\n\ntype JsonObject = Record<string, unknown>;\nconst MAX_IMAGE_BYTES = 5 * 1024 * 1024;\n\nfunction isObject(value: unknown): value is JsonObject {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.length > 0;\n}\n\nfunction isNonNegativeNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value >= 0;\n}\n\nfunction isTimestamp(value: unknown): value is number {\n\treturn Number.isSafeInteger(value) && isNonNegativeNumber(value);\n}\n\nfunction isIsoTimestamp(value: unknown): value is string {\n\tif (typeof value !== \"string\") return false;\n\tconst milliseconds = Date.parse(value);\n\treturn Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === value;\n}\n\nfunction isRecordId(value: unknown): value is string {\n\treturn typeof value === \"string\" && /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(value);\n}\n\nfunction isAbsolutePath(value: unknown): value is string {\n\treturn typeof value === \"string\" && (win32.isAbsolute(value) || posix.isAbsolute(value));\n}\n\nfunction isJsonValue(value: unknown): boolean {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return true;\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\tif (Array.isArray(value)) return value.every(isJsonValue);\n\treturn isObject(value) && Object.values(value).every(isJsonValue);\n}\n\nfunction isProviderReplay(value: unknown): boolean {\n\treturn isObject(value) && isNonEmptyString(value.api) && isJsonValue(value.data);\n}\n\nfunction isImageContent(value: unknown): value is Extract<UserContent | ToolResultContent, { type: \"image\" }> {\n\tif (!isObject(value) || value.type !== \"image\" || typeof value.data !== \"string\" || !isNonEmptyString(value.mimeType))\n\t\treturn false;\n\tconst padding = value.data.endsWith(\"==\") ? 2 : value.data.endsWith(\"=\") ? 1 : 0;\n\tconst decodedBytes = Math.floor((value.data.length * 3) / 4) - padding;\n\treturn (\n\t\tvalue.data.length % 4 === 0 &&\n\t\t/^[A-Za-z0-9+/]*={0,2}$/.test(value.data) &&\n\t\tdecodedBytes > 0 &&\n\t\tdecodedBytes <= MAX_IMAGE_BYTES &&\n\t\t/^image\\/[A-Za-z0-9.+-]+$/.test(value.mimeType)\n\t);\n}\n\nfunction isTextContent(value: unknown): value is Extract<UserContent, { type: \"text\" }> {\n\treturn isObject(value) && value.type === \"text\" && typeof value.text === \"string\";\n}\n\nfunction isUserContent(value: unknown): value is UserContent {\n\treturn isTextContent(value) || isImageContent(value);\n}\n\nfunction isAssistantContent(value: unknown): value is AssistantContent {\n\tif (!isObject(value)) return false;\n\tswitch (value.type) {\n\t\tcase \"text\":\n\t\t\treturn typeof value.text === \"string\";\n\t\tcase \"thinking\":\n\t\t\treturn typeof value.thinking === \"string\";\n\t\tcase \"image\":\n\t\t\treturn isImageContent(value);\n\t\tcase \"tool_call\":\n\t\t\treturn (\n\t\t\t\tisNonEmptyString(value.id) &&\n\t\t\t\tisNonEmptyString(value.name) &&\n\t\t\t\tisObject(value.arguments) &&\n\t\t\t\tisJsonValue(value.arguments)\n\t\t\t);\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nfunction isToolResultContent(value: unknown): value is ToolResultContent {\n\treturn isTextContent(value) || isImageContent(value);\n}\n\nfunction isUsage(value: unknown): value is Usage {\n\tif (!isObject(value) || !isObject(value.cost)) return false;\n\treturn (\n\t\tisNonNegativeNumber(value.input) &&\n\t\tisNonNegativeNumber(value.output) &&\n\t\tisNonNegativeNumber(value.cacheRead) &&\n\t\tisNonNegativeNumber(value.cacheWrite) &&\n\t\tisNonNegativeNumber(value.totalTokens) &&\n\t\tisNonNegativeNumber(value.cost.input) &&\n\t\tisNonNegativeNumber(value.cost.output) &&\n\t\tisNonNegativeNumber(value.cost.cacheRead) &&\n\t\tisNonNegativeNumber(value.cost.cacheWrite) &&\n\t\tisNonNegativeNumber(value.cost.total)\n\t);\n}\n\nfunction isAssistantMessage(value: JsonObject): value is JsonObject & AssistantMessage {\n\tif (\n\t\tvalue.role !== \"assistant\" ||\n\t\t!Array.isArray(value.content) ||\n\t\t!value.content.every(isAssistantContent) ||\n\t\t!isNonEmptyString(value.provider) ||\n\t\t!isNonEmptyString(value.model) ||\n\t\t(value.providerReplay !== undefined && !isProviderReplay(value.providerReplay)) ||\n\t\t!isUsage(value.usage) ||\n\t\t!isTimestamp(value.timestamp)\n\t) {\n\t\treturn false;\n\t}\n\n\tif (value.stopReason === \"stop\" || value.stopReason === \"length\" || value.stopReason === \"tool_use\") {\n\t\treturn value.errorMessage === undefined;\n\t}\n\tif (value.stopReason === \"error\" || value.stopReason === \"aborted\") {\n\t\treturn typeof value.errorMessage === \"string\";\n\t}\n\treturn false;\n}\n\nfunction isMessage(value: unknown): value is Message {\n\tif (!isObject(value) || !isTimestamp(value.timestamp)) return false;\n\n\tswitch (value.role) {\n\t\tcase \"user\":\n\t\t\treturn Array.isArray(value.content) && value.content.every(isUserContent);\n\t\tcase \"assistant\":\n\t\t\treturn isAssistantMessage(value);\n\t\tcase \"tool_result\":\n\t\t\treturn (\n\t\t\t\tisNonEmptyString(value.toolCallId) &&\n\t\t\t\tisNonEmptyString(value.toolName) &&\n\t\t\t\tArray.isArray(value.content) &&\n\t\t\t\tvalue.content.every(isToolResultContent) &&\n\t\t\t\t(value.details === undefined || isJsonValue(value.details)) &&\n\t\t\t\ttypeof value.isError === \"boolean\"\n\t\t\t);\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nfunction decodeHeader(value: unknown, filePath: string): SessionHeader {\n\tif (!isObject(value) || value.type !== \"session\") {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Invalid session header in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\tif (!(\"version\" in value)) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Session header has no version in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\tif (value.version !== SESSION_FORMAT_VERSION) {\n\t\tthrow new SessionLoadError(\n\t\t\t\"UNSUPPORTED_VERSION\",\n\t\t\tfilePath,\n\t\t\t`Unsupported session version ${JSON.stringify(value.version)} in \"${filePath}\".`,\n\t\t\t{ lineNumber: 1 },\n\t\t);\n\t}\n\tif (\n\t\t!isRecordId(value.id) ||\n\t\tvalue.parentId !== null ||\n\t\t!isIsoTimestamp(value.timestamp) ||\n\t\t!isAbsolutePath(value.cwd)\n\t) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Invalid session header fields in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\treturn value as unknown as SessionHeader;\n}\n\nfunction decodeSessionEntry(value: unknown): { readonly entry?: SessionEntry; readonly reason?: string } {\n\tif (!isObject(value) || (value.type !== \"message\" && value.type !== \"summary\" && value.type !== \"plugin\")) {\n\t\treturn { reason: 'record type must be \"message\", \"summary\", or \"plugin\"' };\n\t}\n\tif (value.version !== SESSION_FORMAT_VERSION) return { reason: `record version must be ${SESSION_FORMAT_VERSION}` };\n\tif (!isRecordId(value.id)) return { reason: \"record id is invalid\" };\n\tif (!isRecordId(value.parentId)) return { reason: \"record parentId is invalid\" };\n\tif (!isIsoTimestamp(value.timestamp)) return { reason: \"record timestamp is not canonical ISO 8601 UTC\" };\n\tif (value.type === \"message\") {\n\t\tif (!isMessage(value.message)) return { reason: \"record message does not match the Message contract\" };\n\t\treturn { entry: value as unknown as SessionMessageEntry };\n\t}\n\tif (value.type === \"plugin\") {\n\t\tif (\n\t\t\ttypeof value.pluginId !== \"string\" ||\n\t\t\tvalue.pluginId.length === 0 ||\n\t\t\ttypeof value.pluginVersion !== \"string\" ||\n\t\t\tvalue.pluginVersion.length === 0 ||\n\t\t\ttypeof value.schemaVersion !== \"number\" ||\n\t\t\t!Number.isSafeInteger(value.schemaVersion) ||\n\t\t\tvalue.schemaVersion < 0 ||\n\t\t\t!isJsonValue(value.data)\n\t\t)\n\t\t\treturn { reason: \"record plugin fields are invalid\" };\n\t\treturn { entry: value as unknown as SessionPluginEntry };\n\t}\n\tif (typeof value.summary !== \"string\" || value.summary.trim().length === 0) {\n\t\treturn { reason: \"record summary must be a non-empty string\" };\n\t}\n\tif (!isRecordId(value.firstKeptEntryId)) return { reason: \"record firstKeptEntryId is invalid\" };\n\tif (!Number.isSafeInteger(value.tokensBefore) || !isNonNegativeNumber(value.tokensBefore)) {\n\t\treturn { reason: \"record tokensBefore must be a non-negative safe integer\" };\n\t}\n\treturn { entry: value as unknown as SessionSummaryEntry };\n}\n\nfunction withoutTrailingCarriageReturn(line: string): string {\n\treturn line.endsWith(\"\\r\") ? line.slice(0, -1) : line;\n}\n\nfunction errorText(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nexport async function loadSessionFile(filePath: string): Promise<LoadedSession> {\n\tlet content: string;\n\ttry {\n\t\tcontent = await readFile(filePath, \"utf8\");\n\t} catch (cause) {\n\t\tthrow new SessionLoadError(\"READ_FAILED\", filePath, `Failed to read session file \"${filePath}\".`, { cause });\n\t}\n\n\tconst physicalLines = content.split(\"\\n\");\n\tlet partialLineNumber: number | undefined;\n\tif (content.endsWith(\"\\n\")) {\n\t\tphysicalLines.pop();\n\t} else {\n\t\tphysicalLines.pop();\n\t\tpartialLineNumber = physicalLines.length + 1;\n\t}\n\n\tif (physicalLines.length === 0) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Session file has no committed header: \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\n\tlet headerValue: unknown;\n\ttry {\n\t\theaderValue = JSON.parse(withoutTrailingCarriageReturn(physicalLines[0] ?? \"\")) as unknown;\n\t} catch (cause) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Session header is not valid JSON in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t\tcause,\n\t\t});\n\t}\n\n\tconst header = decodeHeader(headerValue, filePath);\n\tconst entries: SessionEntry[] = [];\n\tconst diagnostics: LoadedSession[\"diagnostics\"][number][] = [];\n\tconst seenIds = new Set<string>([header.id]);\n\tconst entriesById = new Map<string, SessionEntry>();\n\n\tfor (let index = 1; index < physicalLines.length; index++) {\n\t\tconst lineNumber = index + 1;\n\t\ttry {\n\t\t\tconst value = JSON.parse(withoutTrailingCarriageReturn(physicalLines[index] ?? \"\")) as unknown;\n\t\t\tconst decoded = decodeSessionEntry(value);\n\t\t\tif (!decoded.entry) throw new Error(decoded.reason ?? \"Invalid session message record.\");\n\t\t\tif (seenIds.has(decoded.entry.id)) throw new Error(\"record id is duplicated\");\n\t\t\tif (!seenIds.has(decoded.entry.parentId)) throw new Error(\"record parentId must reference an earlier record\");\n\t\t\tif (decoded.entry.type === \"summary\") {\n\t\t\t\tconst firstKept = entriesById.get(decoded.entry.firstKeptEntryId);\n\t\t\t\tif (\n\t\t\t\t\tfirstKept?.type !== \"message\" ||\n\t\t\t\t\t!isAncestor(decoded.entry.parentId, firstKept.id, entriesById, header.id)\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"record firstKeptEntryId must reference an earlier message on the summary branch\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tentries.push(decoded.entry);\n\t\t\tseenIds.add(decoded.entry.id);\n\t\t\tentriesById.set(decoded.entry.id, decoded.entry);\n\t\t} catch (cause) {\n\t\t\tdiagnostics.push({ kind: \"corrupt_record\", lineNumber, reason: errorText(cause) });\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (diagnostics.length === 0 && partialLineNumber !== undefined) {\n\t\tdiagnostics.push({\n\t\t\tkind: \"trailing_partial_line\",\n\t\t\tlineNumber: partialLineNumber,\n\t\t\treason: \"final line has no newline commit marker\",\n\t\t});\n\t}\n\n\treturn {\n\t\theader,\n\t\tentries,\n\t\tmessages: entries\n\t\t\t.filter((entry): entry is SessionMessageEntry => entry.type === \"message\")\n\t\t\t.map((entry) => entry.message),\n\t\tdiagnostics,\n\t};\n}\n\nfunction isAncestor(\n\tleafId: string,\n\tancestorId: string,\n\tentriesById: ReadonlyMap<string, SessionEntry>,\n\theaderId: string,\n): boolean {\n\tlet currentId = leafId;\n\twhile (currentId !== headerId) {\n\t\tif (currentId === ancestorId) return true;\n\t\tconst current = entriesById.get(currentId);\n\t\tif (!current) return false;\n\t\tcurrentId = current.parentId;\n\t}\n\treturn false;\n}\n\nexport interface SessionAppendOptions {\n\treadonly lockTimeoutMs?: number;\n\treadonly lockRetryMs?: number;\n\treadonly now?: () => number;\n\treadonly sleep?: (milliseconds: number) => Promise<void>;\n}\n\nconst DEFAULT_LOCK_TIMEOUT_MS = 5_000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\n\nfunction isNodeError(cause: unknown): cause is NodeJS.ErrnoException {\n\treturn cause instanceof Error && \"code\" in cause;\n}\n\nfunction delay(milliseconds: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport async function createSessionFile(filePath: string, header: SessionHeader): Promise<void> {\n\tconst headerSnapshot = structuredClone(header);\n\ttry {\n\t\tdecodeHeader(headerSnapshot, filePath);\n\t\tawait mkdir(dirname(filePath), { recursive: true });\n\t\tconst handle = await open(filePath, \"wx\");\n\t\ttry {\n\t\t\tawait handle.writeFile(`${JSON.stringify(headerSnapshot)}\\n`, \"utf8\");\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t} catch (cause) {\n\t\tif (cause instanceof SessionWriteError) throw cause;\n\t\tthrow new SessionWriteError(\"CREATE_FAILED\", filePath, `Failed to create session file \"${filePath}\".`, {\n\t\t\tcause,\n\t\t});\n\t}\n}\n\nasync function acquireLock(filePath: string, options: SessionAppendOptions) {\n\tconst lockPath = `${filePath}.lock`;\n\tconst timeout = options.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\tconst retry = options.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;\n\tconst now = options.now ?? Date.now;\n\tconst sleep = options.sleep ?? delay;\n\tconst startedAt = now();\n\n\twhile (true) {\n\t\ttry {\n\t\t\treturn { handle: await open(lockPath, \"wx\"), lockPath };\n\t\t} catch (cause) {\n\t\t\t// Windows can report EPERM/EACCES while another process closes or removes the lock file.\n\t\t\t// Treat that brief transition as contention so a writer does not fail merely because the lock vanished.\n\t\t\tconst lockBusy =\n\t\t\t\tisNodeError(cause) &&\n\t\t\t\t(cause.code === \"EEXIST\" ||\n\t\t\t\t\t(process.platform === \"win32\" && (cause.code === \"EPERM\" || cause.code === \"EACCES\")));\n\t\t\tif (!lockBusy) {\n\t\t\t\tthrow new SessionWriteError(\"APPEND_FAILED\", filePath, `Failed to acquire session lock for \"${filePath}\".`, {\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (now() - startedAt >= timeout) {\n\t\t\t\tthrow new SessionWriteError(\"LOCK_TIMEOUT\", filePath, `Timed out waiting for session lock \"${lockPath}\".`, {\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait sleep(retry);\n\t\t}\n\t}\n}\n\nasync function releaseLock(handle: Awaited<ReturnType<typeof open>>, lockPath: string): Promise<void> {\n\tlet closeError: unknown;\n\ttry {\n\t\tawait handle.close();\n\t} catch (cause) {\n\t\tcloseError = cause;\n\t}\n\n\ttry {\n\t\tawait unlink(lockPath);\n\t} catch (cause) {\n\t\tif (!isNodeError(cause) || cause.code !== \"ENOENT\") throw cause;\n\t}\n\n\tif (closeError !== undefined) throw closeError;\n}\n\nexport async function appendSessionEntry(\n\tfilePath: string,\n\tentry: SessionEntry,\n\texpectedParentId: string,\n\toptions: SessionAppendOptions = {},\n): Promise<void> {\n\tconst entrySnapshot = structuredClone(entry);\n\tconst decodedEntry = decodeSessionEntry(entrySnapshot);\n\tif (!decodedEntry.entry) {\n\t\tthrow new SessionWriteError(\n\t\t\t\"APPEND_FAILED\",\n\t\t\tfilePath,\n\t\t\t`Refusing to append an invalid session entry to \"${filePath}\": ${decodedEntry.reason ?? \"invalid record\"}.`,\n\t\t);\n\t}\n\tconst { handle, lockPath } = await acquireLock(filePath, options);\n\tlet operationError: unknown;\n\ttry {\n\t\tconst loaded = await loadSessionFile(filePath);\n\t\tif (loaded.diagnostics.length > 0) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"CORRUPT_SESSION\",\n\t\t\t\tfilePath,\n\t\t\t\t`Refusing to append to a session with recovery diagnostics: \"${filePath}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst entriesById = new Map(loaded.entries.map((existing) => [existing.id, existing] as const));\n\t\tif (\n\t\t\tentrySnapshot.type === \"summary\" &&\n\t\t\t(entriesById.get(entrySnapshot.firstKeptEntryId)?.type !== \"message\" ||\n\t\t\t\t!isAncestor(expectedParentId, entrySnapshot.firstKeptEntryId, entriesById, loaded.header.id))\n\t\t) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"APPEND_FAILED\",\n\t\t\t\tfilePath,\n\t\t\t\t`Refusing to append a summary with an invalid firstKeptEntryId to \"${filePath}\".`,\n\t\t\t);\n\t\t}\n\t\tif (loaded.header.id === entrySnapshot.id || loaded.entries.some((existing) => existing.id === entrySnapshot.id)) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"APPEND_FAILED\",\n\t\t\t\tfilePath,\n\t\t\t\t`Refusing to append a duplicate session record id to \"${filePath}\".`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tentrySnapshot.parentId !== expectedParentId ||\n\t\t\t(expectedParentId !== loaded.header.id && !entriesById.has(expectedParentId))\n\t\t) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"CONCURRENT_MODIFICATION\",\n\t\t\t\tfilePath,\n\t\t\t\t`Session parent is no longer available in \"${filePath}\".`,\n\t\t\t\t{ expectedParentId },\n\t\t\t);\n\t\t}\n\n\t\tawait appendFile(filePath, `${JSON.stringify(entrySnapshot)}\\n`, \"utf8\");\n\t} catch (cause) {\n\t\toperationError = cause;\n\t} finally {\n\t\ttry {\n\t\t\tawait releaseLock(handle, lockPath);\n\t\t} catch (cause) {\n\t\t\toperationError ??= new SessionWriteError(\n\t\t\t\t\"APPEND_FAILED\",\n\t\t\t\tfilePath,\n\t\t\t\t`Failed to release session lock for \"${filePath}\".`,\n\t\t\t\t{ cause },\n\t\t\t);\n\t\t}\n\t}\n\n\tif (operationError !== undefined) {\n\t\tif (operationError instanceof SessionWriteError) {\n\t\t\tthrow operationError;\n\t\t}\n\t\tif (operationError instanceof SessionLoadError) {\n\t\t\tconst code = operationError.code === \"READ_FAILED\" ? \"APPEND_FAILED\" : \"CORRUPT_SESSION\";\n\t\t\tthrow new SessionWriteError(code, filePath, `Unable to load session before append: \"${filePath}\".`, {\n\t\t\t\tcause: operationError,\n\t\t\t});\n\t\t}\n\t\tthrow new SessionWriteError(\"APPEND_FAILED\", filePath, `Failed to append session entry to \"${filePath}\".`, {\n\t\t\tcause: operationError,\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-storage.js","sourceRoot":"","sources":["../../../src/core/session/session-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFile, mkdir, open, readFile, unlink } from \"node:fs/promises\";\nimport { dirname, posix, win32 } from \"node:path\";\nimport type { AssistantContent, AssistantMessage, Message, ToolResultContent, Usage, UserContent } from \"@di-code/ai\";\nimport {\n\ttype LoadedSession,\n\tSESSION_FORMAT_VERSION,\n\ttype SessionEntry,\n\ttype SessionEventEntry,\n\ttype SessionHeader,\n\tSessionLoadError,\n\ttype SessionMessageEntry,\n\ttype SessionPluginEntry,\n\ttype SessionSummaryEntry,\n\tSessionWriteError,\n} from \"./types.ts\";\n\ntype JsonObject = Record<string, unknown>;\nconst MAX_IMAGE_BYTES = 5 * 1024 * 1024;\nexport const MAX_SESSION_EVENT_PAYLOAD_BYTES = 256 * 1024;\n\nfunction isObject(value: unknown): value is JsonObject {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.length > 0;\n}\n\nfunction isNonNegativeNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value >= 0;\n}\n\nfunction isTimestamp(value: unknown): value is number {\n\treturn Number.isSafeInteger(value) && isNonNegativeNumber(value);\n}\n\nfunction isIsoTimestamp(value: unknown): value is string {\n\tif (typeof value !== \"string\") return false;\n\tconst milliseconds = Date.parse(value);\n\treturn Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === value;\n}\n\nfunction isRecordId(value: unknown): value is string {\n\treturn typeof value === \"string\" && /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(value);\n}\n\nfunction isAbsolutePath(value: unknown): value is string {\n\treturn typeof value === \"string\" && (win32.isAbsolute(value) || posix.isAbsolute(value));\n}\n\nfunction isJsonValue(value: unknown): boolean {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return true;\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\tif (Array.isArray(value)) return value.every(isJsonValue);\n\treturn isObject(value) && Object.values(value).every(isJsonValue);\n}\n\nfunction isBoundedJsonValue(value: unknown): value is import(\"@di-code/ai\").JsonValue {\n\treturn isJsonValue(value) && Buffer.byteLength(JSON.stringify(value), \"utf8\") <= MAX_SESSION_EVENT_PAYLOAD_BYTES;\n}\n\nfunction isProviderReplay(value: unknown): boolean {\n\treturn isObject(value) && isNonEmptyString(value.api) && isJsonValue(value.data);\n}\n\nfunction isImageContent(value: unknown): value is Extract<UserContent | ToolResultContent, { type: \"image\" }> {\n\tif (!isObject(value) || value.type !== \"image\" || typeof value.data !== \"string\" || !isNonEmptyString(value.mimeType))\n\t\treturn false;\n\tconst padding = value.data.endsWith(\"==\") ? 2 : value.data.endsWith(\"=\") ? 1 : 0;\n\tconst decodedBytes = Math.floor((value.data.length * 3) / 4) - padding;\n\treturn (\n\t\tvalue.data.length % 4 === 0 &&\n\t\t/^[A-Za-z0-9+/]*={0,2}$/.test(value.data) &&\n\t\tdecodedBytes > 0 &&\n\t\tdecodedBytes <= MAX_IMAGE_BYTES &&\n\t\t/^image\\/[A-Za-z0-9.+-]+$/.test(value.mimeType)\n\t);\n}\n\nfunction isTextContent(value: unknown): value is Extract<UserContent, { type: \"text\" }> {\n\treturn isObject(value) && value.type === \"text\" && typeof value.text === \"string\";\n}\n\nfunction isUserContent(value: unknown): value is UserContent {\n\treturn isTextContent(value) || isImageContent(value);\n}\n\nfunction isAssistantContent(value: unknown): value is AssistantContent {\n\tif (!isObject(value)) return false;\n\tswitch (value.type) {\n\t\tcase \"text\":\n\t\t\treturn typeof value.text === \"string\";\n\t\tcase \"thinking\":\n\t\t\treturn typeof value.thinking === \"string\";\n\t\tcase \"image\":\n\t\t\treturn isImageContent(value);\n\t\tcase \"tool_call\":\n\t\t\treturn (\n\t\t\t\tisNonEmptyString(value.id) &&\n\t\t\t\tisNonEmptyString(value.name) &&\n\t\t\t\tisObject(value.arguments) &&\n\t\t\t\tisJsonValue(value.arguments)\n\t\t\t);\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nfunction isToolResultContent(value: unknown): value is ToolResultContent {\n\treturn isTextContent(value) || isImageContent(value);\n}\n\nfunction isUsage(value: unknown): value is Usage {\n\tif (!isObject(value) || !isObject(value.cost)) return false;\n\treturn (\n\t\tisNonNegativeNumber(value.input) &&\n\t\tisNonNegativeNumber(value.output) &&\n\t\tisNonNegativeNumber(value.cacheRead) &&\n\t\tisNonNegativeNumber(value.cacheWrite) &&\n\t\tisNonNegativeNumber(value.totalTokens) &&\n\t\tisNonNegativeNumber(value.cost.input) &&\n\t\tisNonNegativeNumber(value.cost.output) &&\n\t\tisNonNegativeNumber(value.cost.cacheRead) &&\n\t\tisNonNegativeNumber(value.cost.cacheWrite) &&\n\t\tisNonNegativeNumber(value.cost.total)\n\t);\n}\n\nfunction isAssistantMessage(value: JsonObject): value is JsonObject & AssistantMessage {\n\tif (\n\t\tvalue.role !== \"assistant\" ||\n\t\t!Array.isArray(value.content) ||\n\t\t!value.content.every(isAssistantContent) ||\n\t\t!isNonEmptyString(value.provider) ||\n\t\t!isNonEmptyString(value.model) ||\n\t\t(value.providerReplay !== undefined && !isProviderReplay(value.providerReplay)) ||\n\t\t!isUsage(value.usage) ||\n\t\t!isTimestamp(value.timestamp)\n\t) {\n\t\treturn false;\n\t}\n\n\tif (value.stopReason === \"stop\" || value.stopReason === \"length\" || value.stopReason === \"tool_use\") {\n\t\treturn value.errorMessage === undefined;\n\t}\n\tif (value.stopReason === \"error\" || value.stopReason === \"aborted\") {\n\t\treturn typeof value.errorMessage === \"string\";\n\t}\n\treturn false;\n}\n\nfunction isMessage(value: unknown): value is Message {\n\tif (!isObject(value) || !isTimestamp(value.timestamp)) return false;\n\n\tswitch (value.role) {\n\t\tcase \"user\":\n\t\t\treturn Array.isArray(value.content) && value.content.every(isUserContent);\n\t\tcase \"assistant\":\n\t\t\treturn isAssistantMessage(value);\n\t\tcase \"tool_result\":\n\t\t\treturn (\n\t\t\t\tisNonEmptyString(value.toolCallId) &&\n\t\t\t\tisNonEmptyString(value.toolName) &&\n\t\t\t\tArray.isArray(value.content) &&\n\t\t\t\tvalue.content.every(isToolResultContent) &&\n\t\t\t\t(value.details === undefined || isJsonValue(value.details)) &&\n\t\t\t\ttypeof value.isError === \"boolean\"\n\t\t\t);\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nfunction decodeHeader(value: unknown, filePath: string): SessionHeader {\n\tif (!isObject(value) || value.type !== \"session\") {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Invalid session header in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\tif (!(\"version\" in value)) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Session header has no version in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\tif (value.version !== SESSION_FORMAT_VERSION) {\n\t\tthrow new SessionLoadError(\n\t\t\t\"UNSUPPORTED_VERSION\",\n\t\t\tfilePath,\n\t\t\t`Unsupported session version ${JSON.stringify(value.version)} in \"${filePath}\".`,\n\t\t\t{ lineNumber: 1 },\n\t\t);\n\t}\n\tif (\n\t\t!isRecordId(value.id) ||\n\t\tvalue.parentId !== null ||\n\t\t!isIsoTimestamp(value.timestamp) ||\n\t\t!isAbsolutePath(value.cwd)\n\t) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Invalid session header fields in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\treturn value as unknown as SessionHeader;\n}\n\nfunction decodeSessionEntry(value: unknown): { readonly entry?: SessionEntry; readonly reason?: string } {\n\tif (\n\t\t!isObject(value) ||\n\t\t(value.type !== \"message\" && value.type !== \"summary\" && value.type !== \"plugin\" && value.type !== \"event\")\n\t) {\n\t\treturn { reason: 'record type must be \"message\", \"summary\", or \"plugin\"' };\n\t}\n\tif (value.version !== SESSION_FORMAT_VERSION) return { reason: `record version must be ${SESSION_FORMAT_VERSION}` };\n\tif (!isRecordId(value.id)) return { reason: \"record id is invalid\" };\n\tif (!isRecordId(value.parentId)) return { reason: \"record parentId is invalid\" };\n\tif (!isIsoTimestamp(value.timestamp)) return { reason: \"record timestamp is not canonical ISO 8601 UTC\" };\n\tif (value.type === \"message\") {\n\t\tif (!isMessage(value.message)) return { reason: \"record message does not match the Message contract\" };\n\t\treturn { entry: value as unknown as SessionMessageEntry };\n\t}\n\tif (value.type === \"plugin\") {\n\t\tif (\n\t\t\ttypeof value.pluginId !== \"string\" ||\n\t\t\tvalue.pluginId.length === 0 ||\n\t\t\ttypeof value.pluginVersion !== \"string\" ||\n\t\t\tvalue.pluginVersion.length === 0 ||\n\t\t\ttypeof value.schemaVersion !== \"number\" ||\n\t\t\t!Number.isSafeInteger(value.schemaVersion) ||\n\t\t\tvalue.schemaVersion < 0 ||\n\t\t\t!isJsonValue(value.data)\n\t\t)\n\t\t\treturn { reason: \"record plugin fields are invalid\" };\n\t\treturn { entry: value as unknown as SessionPluginEntry };\n\t}\n\tif (value.type === \"event\") {\n\t\tif (\n\t\t\ttypeof value.namespace !== \"string\" ||\n\t\t\t!/^[a-z0-9][a-z0-9._-]*$/.test(value.namespace) ||\n\t\t\ttypeof value.eventName !== \"string\" ||\n\t\t\t!/^[a-z0-9][a-z0-9._-]*$/.test(value.eventName) ||\n\t\t\ttypeof value.schemaVersion !== \"number\" ||\n\t\t\t!Number.isSafeInteger(value.schemaVersion) ||\n\t\t\tvalue.schemaVersion < 0 ||\n\t\t\t!isBoundedJsonValue(value.payload)\n\t\t)\n\t\t\treturn { reason: \"record event fields are invalid or payload exceeds the limit\" };\n\t\treturn { entry: value as unknown as SessionEventEntry };\n\t}\n\tif (typeof value.summary !== \"string\" || value.summary.trim().length === 0) {\n\t\treturn { reason: \"record summary must be a non-empty string\" };\n\t}\n\tif (!isRecordId(value.firstKeptEntryId)) return { reason: \"record firstKeptEntryId is invalid\" };\n\tif (!Number.isSafeInteger(value.tokensBefore) || !isNonNegativeNumber(value.tokensBefore)) {\n\t\treturn { reason: \"record tokensBefore must be a non-negative safe integer\" };\n\t}\n\treturn { entry: value as unknown as SessionSummaryEntry };\n}\n\nfunction withoutTrailingCarriageReturn(line: string): string {\n\treturn line.endsWith(\"\\r\") ? line.slice(0, -1) : line;\n}\n\nfunction errorText(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nexport async function loadSessionFile(filePath: string): Promise<LoadedSession> {\n\tlet content: string;\n\ttry {\n\t\tcontent = await readFile(filePath, \"utf8\");\n\t} catch (cause) {\n\t\tthrow new SessionLoadError(\"READ_FAILED\", filePath, `Failed to read session file \"${filePath}\".`, { cause });\n\t}\n\n\tconst physicalLines = content.split(\"\\n\");\n\tlet partialLineNumber: number | undefined;\n\tif (content.endsWith(\"\\n\")) {\n\t\tphysicalLines.pop();\n\t} else {\n\t\tphysicalLines.pop();\n\t\tpartialLineNumber = physicalLines.length + 1;\n\t}\n\n\tif (physicalLines.length === 0) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Session file has no committed header: \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t});\n\t}\n\n\tlet headerValue: unknown;\n\ttry {\n\t\theaderValue = JSON.parse(withoutTrailingCarriageReturn(physicalLines[0] ?? \"\")) as unknown;\n\t} catch (cause) {\n\t\tthrow new SessionLoadError(\"INVALID_HEADER\", filePath, `Session header is not valid JSON in \"${filePath}\".`, {\n\t\t\tlineNumber: 1,\n\t\t\tcause,\n\t\t});\n\t}\n\n\tconst header = decodeHeader(headerValue, filePath);\n\tconst entries: SessionEntry[] = [];\n\tconst diagnostics: LoadedSession[\"diagnostics\"][number][] = [];\n\tconst seenIds = new Set<string>([header.id]);\n\tconst entriesById = new Map<string, SessionEntry>();\n\n\tfor (let index = 1; index < physicalLines.length; index++) {\n\t\tconst lineNumber = index + 1;\n\t\ttry {\n\t\t\tconst value = JSON.parse(withoutTrailingCarriageReturn(physicalLines[index] ?? \"\")) as unknown;\n\t\t\tconst decoded = decodeSessionEntry(value);\n\t\t\tif (!decoded.entry) throw new Error(decoded.reason ?? \"Invalid session message record.\");\n\t\t\tif (seenIds.has(decoded.entry.id)) throw new Error(\"record id is duplicated\");\n\t\t\tif (!seenIds.has(decoded.entry.parentId)) throw new Error(\"record parentId must reference an earlier record\");\n\t\t\tif (decoded.entry.type === \"summary\") {\n\t\t\t\tconst firstKept = entriesById.get(decoded.entry.firstKeptEntryId);\n\t\t\t\tif (\n\t\t\t\t\tfirstKept?.type !== \"message\" ||\n\t\t\t\t\t!isAncestor(decoded.entry.parentId, firstKept.id, entriesById, header.id)\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"record firstKeptEntryId must reference an earlier message on the summary branch\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tentries.push(decoded.entry);\n\t\t\tseenIds.add(decoded.entry.id);\n\t\t\tentriesById.set(decoded.entry.id, decoded.entry);\n\t\t} catch (cause) {\n\t\t\tdiagnostics.push({ kind: \"corrupt_record\", lineNumber, reason: errorText(cause) });\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (diagnostics.length === 0 && partialLineNumber !== undefined) {\n\t\tdiagnostics.push({\n\t\t\tkind: \"trailing_partial_line\",\n\t\t\tlineNumber: partialLineNumber,\n\t\t\treason: \"final line has no newline commit marker\",\n\t\t});\n\t}\n\n\treturn {\n\t\theader,\n\t\tentries,\n\t\tmessages: entries\n\t\t\t.filter((entry): entry is SessionMessageEntry => entry.type === \"message\")\n\t\t\t.map((entry) => entry.message),\n\t\tdiagnostics,\n\t};\n}\n\nfunction isAncestor(\n\tleafId: string,\n\tancestorId: string,\n\tentriesById: ReadonlyMap<string, SessionEntry>,\n\theaderId: string,\n): boolean {\n\tlet currentId = leafId;\n\twhile (currentId !== headerId) {\n\t\tif (currentId === ancestorId) return true;\n\t\tconst current = entriesById.get(currentId);\n\t\tif (!current) return false;\n\t\tcurrentId = current.parentId;\n\t}\n\treturn false;\n}\n\nexport interface SessionAppendOptions {\n\treadonly lockTimeoutMs?: number;\n\treadonly lockRetryMs?: number;\n\treadonly now?: () => number;\n\treadonly sleep?: (milliseconds: number) => Promise<void>;\n\treadonly signal?: AbortSignal;\n}\n\nconst DEFAULT_LOCK_TIMEOUT_MS = 5_000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\n\nfunction isNodeError(cause: unknown): cause is NodeJS.ErrnoException {\n\treturn cause instanceof Error && \"code\" in cause;\n}\n\nfunction delay(milliseconds: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport async function createSessionFile(filePath: string, header: SessionHeader): Promise<void> {\n\tconst headerSnapshot = structuredClone(header);\n\ttry {\n\t\tdecodeHeader(headerSnapshot, filePath);\n\t\tawait mkdir(dirname(filePath), { recursive: true });\n\t\tconst handle = await open(filePath, \"wx\");\n\t\ttry {\n\t\t\tawait handle.writeFile(`${JSON.stringify(headerSnapshot)}\\n`, \"utf8\");\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t} catch (cause) {\n\t\tif (cause instanceof SessionWriteError) throw cause;\n\t\tthrow new SessionWriteError(\"CREATE_FAILED\", filePath, `Failed to create session file \"${filePath}\".`, {\n\t\t\tcause,\n\t\t});\n\t}\n}\n\nasync function acquireLock(filePath: string, options: SessionAppendOptions) {\n\tconst lockPath = `${filePath}.lock`;\n\tconst timeout = options.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\tconst retry = options.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;\n\tconst now = options.now ?? Date.now;\n\tconst sleep = options.sleep ?? delay;\n\tconst startedAt = now();\n\n\twhile (true) {\n\t\tif (options.signal?.aborted)\n\t\t\tthrow options.signal.reason ?? new DOMException(\"The operation was aborted\", \"AbortError\");\n\t\ttry {\n\t\t\treturn { handle: await open(lockPath, \"wx\"), lockPath };\n\t\t} catch (cause) {\n\t\t\t// Windows can report EPERM/EACCES while another process closes or removes the lock file.\n\t\t\t// Treat that brief transition as contention so a writer does not fail merely because the lock vanished.\n\t\t\tconst lockBusy =\n\t\t\t\tisNodeError(cause) &&\n\t\t\t\t(cause.code === \"EEXIST\" ||\n\t\t\t\t\t(process.platform === \"win32\" && (cause.code === \"EPERM\" || cause.code === \"EACCES\")));\n\t\t\tif (!lockBusy) {\n\t\t\t\tthrow new SessionWriteError(\"APPEND_FAILED\", filePath, `Failed to acquire session lock for \"${filePath}\".`, {\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (now() - startedAt >= timeout) {\n\t\t\t\tthrow new SessionWriteError(\"LOCK_TIMEOUT\", filePath, `Timed out waiting for session lock \"${lockPath}\".`, {\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait sleep(retry);\n\t\t}\n\t}\n}\n\nasync function releaseLock(handle: Awaited<ReturnType<typeof open>>, lockPath: string): Promise<void> {\n\tlet closeError: unknown;\n\ttry {\n\t\tawait handle.close();\n\t} catch (cause) {\n\t\tcloseError = cause;\n\t}\n\n\ttry {\n\t\tawait unlink(lockPath);\n\t} catch (cause) {\n\t\tif (!isNodeError(cause) || cause.code !== \"ENOENT\") throw cause;\n\t}\n\n\tif (closeError !== undefined) throw closeError;\n}\n\nexport async function appendSessionEntry(\n\tfilePath: string,\n\tentry: SessionEntry,\n\texpectedParentId: string,\n\toptions: SessionAppendOptions = {},\n): Promise<void> {\n\tif (options.signal?.aborted)\n\t\tthrow options.signal.reason ?? new DOMException(\"The operation was aborted\", \"AbortError\");\n\tconst entrySnapshot = structuredClone(entry);\n\tconst decodedEntry = decodeSessionEntry(entrySnapshot);\n\tif (!decodedEntry.entry) {\n\t\tthrow new SessionWriteError(\n\t\t\t\"APPEND_FAILED\",\n\t\t\tfilePath,\n\t\t\t`Refusing to append an invalid session entry to \"${filePath}\": ${decodedEntry.reason ?? \"invalid record\"}.`,\n\t\t);\n\t}\n\tconst { handle, lockPath } = await acquireLock(filePath, options);\n\tlet operationError: unknown;\n\ttry {\n\t\tconst loaded = await loadSessionFile(filePath);\n\t\tif (loaded.diagnostics.length > 0) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"CORRUPT_SESSION\",\n\t\t\t\tfilePath,\n\t\t\t\t`Refusing to append to a session with recovery diagnostics: \"${filePath}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst entriesById = new Map(loaded.entries.map((existing) => [existing.id, existing] as const));\n\t\tif (\n\t\t\tentrySnapshot.type === \"summary\" &&\n\t\t\t(entriesById.get(entrySnapshot.firstKeptEntryId)?.type !== \"message\" ||\n\t\t\t\t!isAncestor(expectedParentId, entrySnapshot.firstKeptEntryId, entriesById, loaded.header.id))\n\t\t) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"APPEND_FAILED\",\n\t\t\t\tfilePath,\n\t\t\t\t`Refusing to append a summary with an invalid firstKeptEntryId to \"${filePath}\".`,\n\t\t\t);\n\t\t}\n\t\tif (loaded.header.id === entrySnapshot.id || loaded.entries.some((existing) => existing.id === entrySnapshot.id)) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"APPEND_FAILED\",\n\t\t\t\tfilePath,\n\t\t\t\t`Refusing to append a duplicate session record id to \"${filePath}\".`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tentrySnapshot.parentId !== expectedParentId ||\n\t\t\t(expectedParentId !== loaded.header.id && !entriesById.has(expectedParentId))\n\t\t) {\n\t\t\tthrow new SessionWriteError(\n\t\t\t\t\"CONCURRENT_MODIFICATION\",\n\t\t\t\tfilePath,\n\t\t\t\t`Session parent is no longer available in \"${filePath}\".`,\n\t\t\t\t{ expectedParentId },\n\t\t\t);\n\t\t}\n\n\t\tawait appendFile(filePath, `${JSON.stringify(entrySnapshot)}\\n`, \"utf8\");\n\t} catch (cause) {\n\t\toperationError = cause;\n\t} finally {\n\t\ttry {\n\t\t\tawait releaseLock(handle, lockPath);\n\t\t} catch (cause) {\n\t\t\toperationError ??= new SessionWriteError(\n\t\t\t\t\"APPEND_FAILED\",\n\t\t\t\tfilePath,\n\t\t\t\t`Failed to release session lock for \"${filePath}\".`,\n\t\t\t\t{ cause },\n\t\t\t);\n\t\t}\n\t}\n\n\tif (operationError !== undefined) {\n\t\tif (operationError instanceof SessionWriteError) {\n\t\t\tthrow operationError;\n\t\t}\n\t\tif (operationError instanceof SessionLoadError) {\n\t\t\tconst code = operationError.code === \"READ_FAILED\" ? \"APPEND_FAILED\" : \"CORRUPT_SESSION\";\n\t\t\tthrow new SessionWriteError(code, filePath, `Unable to load session before append: \"${filePath}\".`, {\n\t\t\t\tcause: operationError,\n\t\t\t});\n\t\t}\n\t\tthrow new SessionWriteError(\"APPEND_FAILED\", filePath, `Failed to append session entry to \"${filePath}\".`, {\n\t\t\tcause: operationError,\n\t\t});\n\t}\n}\n"]}