@earendil-works/pi-agent-core 0.84.0 → 0.84.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 (70) hide show
  1. package/README.md +4 -4
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +5 -1
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +3 -0
  7. package/dist/agent.js.map +1 -1
  8. package/dist/harness/events.d.ts +43 -0
  9. package/dist/harness/events.d.ts.map +1 -0
  10. package/dist/harness/events.js +65 -0
  11. package/dist/harness/events.js.map +1 -0
  12. package/dist/harness/session/jsonl/codec.d.ts +4 -2
  13. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  14. package/dist/harness/session/jsonl/codec.js +127 -88
  15. package/dist/harness/session/jsonl/codec.js.map +1 -1
  16. package/dist/harness/session/jsonl/errors.d.ts +5 -1
  17. package/dist/harness/session/jsonl/errors.d.ts.map +1 -1
  18. package/dist/harness/session/jsonl/errors.js +10 -2
  19. package/dist/harness/session/jsonl/errors.js.map +1 -1
  20. package/dist/harness/session/jsonl/repo.d.ts +7 -3
  21. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  22. package/dist/harness/session/jsonl/repo.js +88 -45
  23. package/dist/harness/session/jsonl/repo.js.map +1 -1
  24. package/dist/harness/session/jsonl/storage.d.ts +1 -1
  25. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  26. package/dist/harness/session/jsonl/storage.js +28 -19
  27. package/dist/harness/session/jsonl/storage.js.map +1 -1
  28. package/dist/harness/session/jsonl/types.d.ts +1 -1
  29. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  30. package/dist/harness/session/jsonl/types.js.map +1 -1
  31. package/dist/harness/session/memory.d.ts +1 -1
  32. package/dist/harness/session/memory.d.ts.map +1 -1
  33. package/dist/harness/session/memory.js.map +1 -1
  34. package/dist/harness/session/session.d.ts +1 -1
  35. package/dist/harness/session/session.d.ts.map +1 -1
  36. package/dist/harness/session/session.js +7 -2
  37. package/dist/harness/session/session.js.map +1 -1
  38. package/dist/harness/session/state.d.ts +1 -1
  39. package/dist/harness/session/state.d.ts.map +1 -1
  40. package/dist/harness/session/state.js.map +1 -1
  41. package/dist/harness/session/testing/conformance.d.ts.map +1 -1
  42. package/dist/harness/session/testing/conformance.js +19 -0
  43. package/dist/harness/session/testing/conformance.js.map +1 -1
  44. package/dist/harness/session/types.d.ts +18 -5
  45. package/dist/harness/session/types.d.ts.map +1 -1
  46. package/dist/harness/session/types.js.map +1 -1
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/proxy.d.ts +2 -1
  52. package/dist/proxy.d.ts.map +1 -1
  53. package/dist/proxy.js +1 -0
  54. package/dist/proxy.js.map +1 -1
  55. package/dist/search/index.d.ts +21 -0
  56. package/dist/search/index.d.ts.map +1 -0
  57. package/dist/search/index.js +2 -0
  58. package/dist/search/index.js.map +1 -0
  59. package/dist/search/scanning.d.ts +29 -0
  60. package/dist/search/scanning.d.ts.map +1 -0
  61. package/dist/search/scanning.js +103 -0
  62. package/dist/search/scanning.js.map +1 -0
  63. package/dist/types.d.ts +6 -0
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/types.js.map +1 -1
  66. package/package.json +3 -3
  67. package/dist/harness/session/search.d.ts +0 -19
  68. package/dist/harness/session/search.d.ts.map +0 -1
  69. package/dist/harness/session/search.js +0 -42
  70. package/dist/harness/session/search.js.map +0 -1
@@ -1,4 +1,5 @@
1
- import { invalidFile } from "./errors.js";
1
+ import { err, ok } from "../../types.js";
2
+ import { JsonlDecodeError } from "./errors.js";
2
3
  const ENTRY_TYPES = new Set([
3
4
  "message",
4
5
  "model_change",
@@ -23,71 +24,84 @@ const OPERATION_KINDS = new Set(["run", "compaction", "navigation"]);
23
24
  function isObject(value) {
24
25
  return typeof value === "object" && value !== null && !Array.isArray(value);
25
26
  }
26
- function parseObject(line, path, lineNumber) {
27
+ function parseObject(line) {
27
28
  let value;
28
29
  try {
29
30
  value = JSON.parse(line);
30
31
  }
31
32
  catch (error) {
32
- throw invalidFile(path, lineNumber, "is not valid JSON", error instanceof Error ? error : undefined);
33
+ throw new JsonlDecodeError("syntax", "is not valid JSON", error instanceof Error ? error : undefined);
33
34
  }
34
35
  if (!isObject(value))
35
- throw invalidFile(path, lineNumber, "is not a JSON object");
36
+ throw new JsonlDecodeError("schema", "is not a JSON object");
36
37
  return value;
37
38
  }
38
- function requireString(value, path, line, field) {
39
+ function requireString(value, field) {
39
40
  if (typeof value !== "string")
40
- throw invalidFile(path, line, `has invalid ${field}`);
41
+ throw new JsonlDecodeError("schema", `has invalid ${field}`);
41
42
  return value;
42
43
  }
43
- function requireSequence(value, path, line) {
44
- if (!Number.isSafeInteger(value) || value <= 0)
45
- throw invalidFile(path, line, "has invalid seq");
44
+ function requireSequence(value) {
45
+ if (!Number.isSafeInteger(value) || value <= 0) {
46
+ throw new JsonlDecodeError("schema", "has invalid seq");
47
+ }
46
48
  return value;
47
49
  }
48
- function requireTimestamp(value, path, line) {
49
- if (!Number.isSafeInteger(value) || value < 0)
50
- throw invalidFile(path, line, "has invalid timestamp");
50
+ function requireTimestamp(value) {
51
+ if (!Number.isSafeInteger(value) || value < 0) {
52
+ throw new JsonlDecodeError("schema", "has invalid timestamp");
53
+ }
51
54
  return value;
52
55
  }
53
- function requireNullableId(value, path, line, field) {
56
+ function requireNullableId(value, field) {
54
57
  if (value !== null && typeof value !== "string") {
55
- throw invalidFile(path, line, `has invalid ${field}`);
58
+ throw new JsonlDecodeError("schema", `has invalid ${field}`);
56
59
  }
57
60
  return value;
58
61
  }
59
- export function parseHeader(line, path) {
60
- const value = parseObject(line, path, 1);
62
+ function decodeHeader(line) {
63
+ const value = parseObject(line);
61
64
  if (value.kind !== "header")
62
- throw invalidFile(path, 1, "is not a header");
65
+ throw new JsonlDecodeError("schema", "is not a header");
63
66
  if (value.version !== 4)
64
- throw invalidFile(path, 1, "has unsupported session version");
67
+ throw new JsonlDecodeError("schema", "has unsupported session version");
65
68
  const parentSessionId = value.parentSessionId;
66
69
  if (parentSessionId !== undefined && typeof parentSessionId !== "string") {
67
- throw invalidFile(path, 1, "has invalid parentSessionId");
70
+ throw new JsonlDecodeError("schema", "has invalid parentSessionId");
68
71
  }
69
72
  const legacyParentSessionPath = value.legacyParentSessionPath;
70
73
  if (legacyParentSessionPath !== undefined && typeof legacyParentSessionPath !== "string") {
71
- throw invalidFile(path, 1, "has invalid legacyParentSessionPath");
74
+ throw new JsonlDecodeError("schema", "has invalid legacyParentSessionPath");
72
75
  }
73
76
  if (parentSessionId !== undefined && legacyParentSessionPath !== undefined) {
74
- throw invalidFile(path, 1, "has both parentSessionId and legacyParentSessionPath");
77
+ throw new JsonlDecodeError("schema", "has both parentSessionId and legacyParentSessionPath");
75
78
  }
76
79
  const metadataValue = value.metadata;
77
- if (metadataValue !== undefined && !isObject(metadataValue))
78
- throw invalidFile(path, 1, "has invalid metadata");
80
+ if (metadataValue !== undefined && !isObject(metadataValue)) {
81
+ throw new JsonlDecodeError("schema", "has invalid metadata");
82
+ }
79
83
  const metadata = metadataValue;
80
84
  return {
81
85
  kind: "header",
82
86
  version: 4,
83
- id: requireString(value.id, path, 1, "id"),
84
- createdAt: requireTimestamp(value.createdAt, path, 1),
85
- cwd: requireString(value.cwd, path, 1, "cwd"),
87
+ id: requireString(value.id, "id"),
88
+ createdAt: requireTimestamp(value.createdAt),
89
+ cwd: requireString(value.cwd, "cwd"),
86
90
  parentSessionId,
87
91
  legacyParentSessionPath,
88
92
  metadata,
89
93
  };
90
94
  }
95
+ export function parseHeader(line) {
96
+ try {
97
+ return ok(decodeHeader(line));
98
+ }
99
+ catch (error) {
100
+ if (error instanceof JsonlDecodeError)
101
+ return err(error);
102
+ throw error;
103
+ }
104
+ }
91
105
  export function encodeHeader(header) {
92
106
  return `${JSON.stringify(header)}\n`;
93
107
  }
@@ -106,73 +120,98 @@ export function metadataFromHeader(header, path, modifiedAt) {
106
120
  ...(header.metadata === undefined ? {} : { metadata: header.metadata }),
107
121
  };
108
122
  }
109
- export function parseMutation(line, path, lineNumber) {
110
- const value = parseObject(line, path, lineNumber);
111
- const seq = requireSequence(value.seq, path, lineNumber);
112
- switch (value.kind) {
113
- case "entry": {
114
- const lane = value.lane === undefined ? undefined : requireString(value.lane, path, lineNumber, "lane");
115
- const id = requireString(value.id, path, lineNumber, "id");
116
- const type = requireString(value.type, path, lineNumber, "entry type");
117
- if (!ENTRY_TYPES.has(type))
118
- throw invalidFile(path, lineNumber, `has unknown entry type ${type}`);
119
- const parentId = requireNullableId(value.parentId, path, lineNumber, "parentId");
120
- const timestamp = requireTimestamp(value.timestamp, path, lineNumber);
121
- if (type === "custom")
122
- requireString(value.customType, path, lineNumber, "customType");
123
- const { kind: _kind, lane: _lane, ...entryFields } = value;
124
- const entry = { ...entryFields, id, type, parentId, seq, timestamp };
125
- return lane === undefined ? { kind: "entry", entry } : { kind: "entry", lane, entry };
123
+ function parseEntryMutation(value, seq) {
124
+ const lane = value.lane === undefined ? undefined : requireString(value.lane, "lane");
125
+ const id = requireString(value.id, "id");
126
+ const type = requireString(value.type, "entry type");
127
+ if (!ENTRY_TYPES.has(type)) {
128
+ throw new JsonlDecodeError("schema", `has unknown entry type ${type}`);
129
+ }
130
+ const parentId = requireNullableId(value.parentId, "parentId");
131
+ const timestamp = requireTimestamp(value.timestamp);
132
+ if (type === "custom")
133
+ requireString(value.customType, "customType");
134
+ const { kind: _kind, lane: _lane, ...entryFields } = value;
135
+ const entry = { ...entryFields, id, type, parentId, seq, timestamp };
136
+ return lane === undefined ? { kind: "entry", entry } : { kind: "entry", lane, entry };
137
+ }
138
+ function parseRecordMutation(value, seq) {
139
+ const id = requireString(value.id, "id");
140
+ const lane = requireString(value.lane, "lane");
141
+ const type = requireString(value.type, "record type");
142
+ if (!RECORD_TYPES.has(type)) {
143
+ throw new JsonlDecodeError("schema", `has unknown record type ${type}`);
144
+ }
145
+ const timestamp = requireTimestamp(value.timestamp);
146
+ if (type === "operation_started") {
147
+ if (!isObject(value.intent))
148
+ throw new JsonlDecodeError("schema", "has invalid intent");
149
+ const operationKind = requireString(value.intent.kind, "operation kind");
150
+ if (!OPERATION_KINDS.has(operationKind)) {
151
+ throw new JsonlDecodeError("schema", `has unknown operation kind ${operationKind}`);
126
152
  }
127
- case "record": {
128
- const id = requireString(value.id, path, lineNumber, "id");
129
- const lane = requireString(value.lane, path, lineNumber, "lane");
130
- const type = requireString(value.type, path, lineNumber, "record type");
131
- if (!RECORD_TYPES.has(type))
132
- throw invalidFile(path, lineNumber, `has unknown record type ${type}`);
133
- const timestamp = requireTimestamp(value.timestamp, path, lineNumber);
134
- if (type === "operation_started") {
135
- if (!isObject(value.intent))
136
- throw invalidFile(path, lineNumber, "has invalid intent");
137
- const operationKind = requireString(value.intent.kind, path, lineNumber, "operation kind");
138
- if (!OPERATION_KINDS.has(operationKind)) {
139
- throw invalidFile(path, lineNumber, `has unknown operation kind ${operationKind}`);
140
- }
141
- }
142
- if (type === "operation_finished")
143
- requireString(value.runId, path, lineNumber, "runId");
144
- const { kind: _kind, ...recordFields } = value;
145
- return {
146
- kind: "record",
147
- record: { ...recordFields, id, lane, type, seq, timestamp },
148
- };
153
+ }
154
+ if (type === "operation_finished")
155
+ requireString(value.runId, "runId");
156
+ const { kind: _kind, ...recordFields } = value;
157
+ return {
158
+ kind: "record",
159
+ record: { ...recordFields, id, lane, type, seq, timestamp },
160
+ };
161
+ }
162
+ function parseLaneMutation(value, seq) {
163
+ return {
164
+ kind: "lane",
165
+ seq,
166
+ lane: requireString(value.lane, "lane"),
167
+ leafId: requireNullableId(value.leafId, "leafId"),
168
+ };
169
+ }
170
+ function parseFactMutation(value, seq) {
171
+ if (value.fact === "name") {
172
+ if (value.name !== undefined && typeof value.name !== "string") {
173
+ throw new JsonlDecodeError("schema", "has invalid name");
174
+ }
175
+ return { kind: "fact", seq, fact: "name", name: value.name };
176
+ }
177
+ if (value.fact === "label") {
178
+ if (value.label !== undefined && typeof value.label !== "string") {
179
+ throw new JsonlDecodeError("schema", "has invalid label");
149
180
  }
181
+ return {
182
+ kind: "fact",
183
+ seq,
184
+ fact: "label",
185
+ targetId: requireString(value.targetId, "targetId"),
186
+ label: value.label,
187
+ };
188
+ }
189
+ throw new JsonlDecodeError("schema", "has unknown fact type");
190
+ }
191
+ function decodeMutation(line) {
192
+ const value = parseObject(line);
193
+ const seq = requireSequence(value.seq);
194
+ switch (value.kind) {
195
+ case "entry":
196
+ return parseEntryMutation(value, seq);
197
+ case "record":
198
+ return parseRecordMutation(value, seq);
150
199
  case "lane":
151
- return {
152
- kind: "lane",
153
- seq,
154
- lane: requireString(value.lane, path, lineNumber, "lane"),
155
- leafId: requireNullableId(value.leafId, path, lineNumber, "leafId"),
156
- };
200
+ return parseLaneMutation(value, seq);
157
201
  case "fact":
158
- if (value.fact === "name") {
159
- return { kind: "fact", seq, fact: "name", name: requireString(value.name, path, lineNumber, "name") };
160
- }
161
- if (value.fact === "label") {
162
- if (value.label !== undefined && typeof value.label !== "string") {
163
- throw invalidFile(path, lineNumber, "has invalid label");
164
- }
165
- return {
166
- kind: "fact",
167
- seq,
168
- fact: "label",
169
- targetId: requireString(value.targetId, path, lineNumber, "targetId"),
170
- label: value.label,
171
- };
172
- }
173
- throw invalidFile(path, lineNumber, "has unknown fact type");
202
+ return parseFactMutation(value, seq);
174
203
  default:
175
- throw invalidFile(path, lineNumber, "has unknown mutation kind");
204
+ throw new JsonlDecodeError("schema", "has unknown mutation kind");
205
+ }
206
+ }
207
+ export function parseMutation(line) {
208
+ try {
209
+ return ok(decodeMutation(line));
210
+ }
211
+ catch (error) {
212
+ if (error instanceof JsonlDecodeError)
213
+ return err(error);
214
+ throw error;
176
215
  }
177
216
  }
178
217
  export function encodeMutation(mutation) {
@@ -1 +1 @@
1
- {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/codec.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionMutation } from \"../state.ts\";\nimport type { Entry, LaneRecord } from \"../types.ts\";\nimport { invalidFile } from \"./errors.ts\";\nimport type { JsonlSessionMetadata, JsonlV4Header } from \"./types.ts\";\n\nconst ENTRY_TYPES = new Set<Entry[\"type\"]>([\n\t\"message\",\n\t\"model_change\",\n\t\"thinking_level_change\",\n\t\"active_tools_change\",\n\t\"compaction\",\n\t\"branch_summary\",\n\t\"custom\",\n]);\nconst RECORD_TYPES = new Set<LaneRecord[\"type\"]>([\n\t\"operation_started\",\n\t\"abort_requested\",\n\t\"operation_finished\",\n\t\"step_attempt\",\n\t\"tool_started\",\n\t\"queue_enqueued\",\n\t\"queue_cancelled\",\n\t\"write_deferred\",\n\t\"usage\",\n]);\nconst OPERATION_KINDS = new Set([\"run\", \"compaction\", \"navigation\"]);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseObject(line: string, path: string, lineNumber: number): Record<string, unknown> {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidFile(path, lineNumber, \"is not valid JSON\", error instanceof Error ? error : undefined);\n\t}\n\tif (!isObject(value)) throw invalidFile(path, lineNumber, \"is not a JSON object\");\n\treturn value;\n}\n\nfunction requireString(value: unknown, path: string, line: number, field: string): string {\n\tif (typeof value !== \"string\") throw invalidFile(path, line, `has invalid ${field}`);\n\treturn value;\n}\n\nfunction requireSequence(value: unknown, path: string, line: number): number {\n\tif (!Number.isSafeInteger(value) || (value as number) <= 0) throw invalidFile(path, line, \"has invalid seq\");\n\treturn value as number;\n}\n\nfunction requireTimestamp(value: unknown, path: string, line: number): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) throw invalidFile(path, line, \"has invalid timestamp\");\n\treturn value as number;\n}\n\nfunction requireNullableId(value: unknown, path: string, line: number, field: string): string | null {\n\tif (value !== null && typeof value !== \"string\") {\n\t\tthrow invalidFile(path, line, `has invalid ${field}`);\n\t}\n\treturn value as string | null;\n}\n\nexport function parseHeader(line: string, path: string): JsonlV4Header {\n\tconst value = parseObject(line, path, 1);\n\tif (value.kind !== \"header\") throw invalidFile(path, 1, \"is not a header\");\n\tif (value.version !== 4) throw invalidFile(path, 1, \"has unsupported session version\");\n\tconst parentSessionId = value.parentSessionId;\n\tif (parentSessionId !== undefined && typeof parentSessionId !== \"string\") {\n\t\tthrow invalidFile(path, 1, \"has invalid parentSessionId\");\n\t}\n\tconst legacyParentSessionPath = value.legacyParentSessionPath;\n\tif (legacyParentSessionPath !== undefined && typeof legacyParentSessionPath !== \"string\") {\n\t\tthrow invalidFile(path, 1, \"has invalid legacyParentSessionPath\");\n\t}\n\tif (parentSessionId !== undefined && legacyParentSessionPath !== undefined) {\n\t\tthrow invalidFile(path, 1, \"has both parentSessionId and legacyParentSessionPath\");\n\t}\n\tconst metadataValue = value.metadata;\n\tif (metadataValue !== undefined && !isObject(metadataValue)) throw invalidFile(path, 1, \"has invalid metadata\");\n\tconst metadata = metadataValue as JsonlV4Header[\"metadata\"];\n\treturn {\n\t\tkind: \"header\",\n\t\tversion: 4,\n\t\tid: requireString(value.id, path, 1, \"id\"),\n\t\tcreatedAt: requireTimestamp(value.createdAt, path, 1),\n\t\tcwd: requireString(value.cwd, path, 1, \"cwd\"),\n\t\tparentSessionId,\n\t\tlegacyParentSessionPath,\n\t\tmetadata,\n\t};\n}\n\nexport function encodeHeader(header: JsonlV4Header): string {\n\treturn `${JSON.stringify(header)}\\n`;\n}\n\nexport function metadataFromHeader(header: JsonlV4Header, path: string, modifiedAt: number): JsonlSessionMetadata {\n\treturn {\n\t\tid: header.id,\n\t\tcreatedAt: header.createdAt,\n\t\tcwd: header.cwd,\n\t\tpath,\n\t\tmodifiedAt,\n\t\tsourceFormat: 4,\n\t\t...(header.parentSessionId === undefined ? {} : { parentSessionId: header.parentSessionId }),\n\t\t...(header.legacyParentSessionPath === undefined\n\t\t\t? {}\n\t\t\t: { legacyParentSessionPath: header.legacyParentSessionPath }),\n\t\t...(header.metadata === undefined ? {} : { metadata: header.metadata }),\n\t};\n}\n\nexport function parseMutation(line: string, path: string, lineNumber: number): SessionMutation {\n\tconst value = parseObject(line, path, lineNumber);\n\tconst seq = requireSequence(value.seq, path, lineNumber);\n\tswitch (value.kind) {\n\t\tcase \"entry\": {\n\t\t\tconst lane = value.lane === undefined ? undefined : requireString(value.lane, path, lineNumber, \"lane\");\n\t\t\tconst id = requireString(value.id, path, lineNumber, \"id\");\n\t\t\tconst type = requireString(value.type, path, lineNumber, \"entry type\");\n\t\t\tif (!ENTRY_TYPES.has(type as Entry[\"type\"]))\n\t\t\t\tthrow invalidFile(path, lineNumber, `has unknown entry type ${type}`);\n\t\t\tconst parentId = requireNullableId(value.parentId, path, lineNumber, \"parentId\");\n\t\t\tconst timestamp = requireTimestamp(value.timestamp, path, lineNumber);\n\t\t\tif (type === \"custom\") requireString(value.customType, path, lineNumber, \"customType\");\n\t\t\tconst { kind: _kind, lane: _lane, ...entryFields } = value;\n\t\t\tconst entry = { ...entryFields, id, type, parentId, seq, timestamp } as unknown as Entry;\n\t\t\treturn lane === undefined ? { kind: \"entry\", entry } : { kind: \"entry\", lane, entry };\n\t\t}\n\t\tcase \"record\": {\n\t\t\tconst id = requireString(value.id, path, lineNumber, \"id\");\n\t\t\tconst lane = requireString(value.lane, path, lineNumber, \"lane\");\n\t\t\tconst type = requireString(value.type, path, lineNumber, \"record type\");\n\t\t\tif (!RECORD_TYPES.has(type as LaneRecord[\"type\"]))\n\t\t\t\tthrow invalidFile(path, lineNumber, `has unknown record type ${type}`);\n\t\t\tconst timestamp = requireTimestamp(value.timestamp, path, lineNumber);\n\t\t\tif (type === \"operation_started\") {\n\t\t\t\tif (!isObject(value.intent)) throw invalidFile(path, lineNumber, \"has invalid intent\");\n\t\t\t\tconst operationKind = requireString(value.intent.kind, path, lineNumber, \"operation kind\");\n\t\t\t\tif (!OPERATION_KINDS.has(operationKind)) {\n\t\t\t\t\tthrow invalidFile(path, lineNumber, `has unknown operation kind ${operationKind}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (type === \"operation_finished\") requireString(value.runId, path, lineNumber, \"runId\");\n\t\t\tconst { kind: _kind, ...recordFields } = value;\n\t\t\treturn {\n\t\t\t\tkind: \"record\",\n\t\t\t\trecord: { ...recordFields, id, lane, type, seq, timestamp } as unknown as LaneRecord,\n\t\t\t};\n\t\t}\n\t\tcase \"lane\":\n\t\t\treturn {\n\t\t\t\tkind: \"lane\",\n\t\t\t\tseq,\n\t\t\t\tlane: requireString(value.lane, path, lineNumber, \"lane\"),\n\t\t\t\tleafId: requireNullableId(value.leafId, path, lineNumber, \"leafId\"),\n\t\t\t};\n\t\tcase \"fact\":\n\t\t\tif (value.fact === \"name\") {\n\t\t\t\treturn { kind: \"fact\", seq, fact: \"name\", name: requireString(value.name, path, lineNumber, \"name\") };\n\t\t\t}\n\t\t\tif (value.fact === \"label\") {\n\t\t\t\tif (value.label !== undefined && typeof value.label !== \"string\") {\n\t\t\t\t\tthrow invalidFile(path, lineNumber, \"has invalid label\");\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"fact\",\n\t\t\t\t\tseq,\n\t\t\t\t\tfact: \"label\",\n\t\t\t\t\ttargetId: requireString(value.targetId, path, lineNumber, \"targetId\"),\n\t\t\t\t\tlabel: value.label,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow invalidFile(path, lineNumber, \"has unknown fact type\");\n\t\tdefault:\n\t\t\tthrow invalidFile(path, lineNumber, \"has unknown mutation kind\");\n\t}\n}\n\nexport function encodeMutation(mutation: SessionMutation): string {\n\tswitch (mutation.kind) {\n\t\tcase \"entry\":\n\t\t\treturn `${JSON.stringify({ kind: \"entry\", lane: mutation.lane, ...mutation.entry })}\\n`;\n\t\tcase \"record\":\n\t\t\treturn `${JSON.stringify({ kind: \"record\", ...mutation.record })}\\n`;\n\t\tcase \"lane\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t\tcase \"fact\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t}\n}\n"]}
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/codec.ts"],"names":[],"mappings":"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","sourcesContent":["import { err, ok, type Result } from \"../../types.ts\";\nimport type { SessionMutation } from \"../state.ts\";\nimport type { Entry, LaneRecord } from \"../types.ts\";\nimport { JsonlDecodeError } from \"./errors.ts\";\nimport type { JsonlSessionMetadata, JsonlV4Header } from \"./types.ts\";\n\nconst ENTRY_TYPES = new Set<Entry[\"type\"]>([\n\t\"message\",\n\t\"model_change\",\n\t\"thinking_level_change\",\n\t\"active_tools_change\",\n\t\"compaction\",\n\t\"branch_summary\",\n\t\"custom\",\n]);\nconst RECORD_TYPES = new Set<LaneRecord[\"type\"]>([\n\t\"operation_started\",\n\t\"abort_requested\",\n\t\"operation_finished\",\n\t\"step_attempt\",\n\t\"tool_started\",\n\t\"queue_enqueued\",\n\t\"queue_cancelled\",\n\t\"write_deferred\",\n\t\"usage\",\n]);\nconst OPERATION_KINDS = new Set([\"run\", \"compaction\", \"navigation\"]);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseObject(line: string): Record<string, unknown> {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow new JsonlDecodeError(\"syntax\", \"is not valid JSON\", error instanceof Error ? error : undefined);\n\t}\n\tif (!isObject(value)) throw new JsonlDecodeError(\"schema\", \"is not a JSON object\");\n\treturn value;\n}\n\nfunction requireString(value: unknown, field: string): string {\n\tif (typeof value !== \"string\") throw new JsonlDecodeError(\"schema\", `has invalid ${field}`);\n\treturn value;\n}\n\nfunction requireSequence(value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) <= 0) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid seq\");\n\t}\n\treturn value as number;\n}\n\nfunction requireTimestamp(value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid timestamp\");\n\t}\n\treturn value as number;\n}\n\nfunction requireNullableId(value: unknown, field: string): string | null {\n\tif (value !== null && typeof value !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", `has invalid ${field}`);\n\t}\n\treturn value as string | null;\n}\n\nfunction decodeHeader(line: string): JsonlV4Header {\n\tconst value = parseObject(line);\n\tif (value.kind !== \"header\") throw new JsonlDecodeError(\"schema\", \"is not a header\");\n\tif (value.version !== 4) throw new JsonlDecodeError(\"schema\", \"has unsupported session version\");\n\tconst parentSessionId = value.parentSessionId;\n\tif (parentSessionId !== undefined && typeof parentSessionId !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid parentSessionId\");\n\t}\n\tconst legacyParentSessionPath = value.legacyParentSessionPath;\n\tif (legacyParentSessionPath !== undefined && typeof legacyParentSessionPath !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid legacyParentSessionPath\");\n\t}\n\tif (parentSessionId !== undefined && legacyParentSessionPath !== undefined) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has both parentSessionId and legacyParentSessionPath\");\n\t}\n\tconst metadataValue = value.metadata;\n\tif (metadataValue !== undefined && !isObject(metadataValue)) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid metadata\");\n\t}\n\tconst metadata = metadataValue as JsonlV4Header[\"metadata\"];\n\treturn {\n\t\tkind: \"header\",\n\t\tversion: 4,\n\t\tid: requireString(value.id, \"id\"),\n\t\tcreatedAt: requireTimestamp(value.createdAt),\n\t\tcwd: requireString(value.cwd, \"cwd\"),\n\t\tparentSessionId,\n\t\tlegacyParentSessionPath,\n\t\tmetadata,\n\t};\n}\n\nexport function parseHeader(line: string): Result<JsonlV4Header, JsonlDecodeError> {\n\ttry {\n\t\treturn ok<JsonlV4Header, JsonlDecodeError>(decodeHeader(line));\n\t} catch (error) {\n\t\tif (error instanceof JsonlDecodeError) return err<JsonlV4Header, JsonlDecodeError>(error);\n\t\tthrow error;\n\t}\n}\n\nexport function encodeHeader(header: JsonlV4Header): string {\n\treturn `${JSON.stringify(header)}\\n`;\n}\n\nexport function metadataFromHeader(header: JsonlV4Header, path: string, modifiedAt: number): JsonlSessionMetadata {\n\treturn {\n\t\tid: header.id,\n\t\tcreatedAt: header.createdAt,\n\t\tcwd: header.cwd,\n\t\tpath,\n\t\tmodifiedAt,\n\t\tsourceFormat: 4,\n\t\t...(header.parentSessionId === undefined ? {} : { parentSessionId: header.parentSessionId }),\n\t\t...(header.legacyParentSessionPath === undefined\n\t\t\t? {}\n\t\t\t: { legacyParentSessionPath: header.legacyParentSessionPath }),\n\t\t...(header.metadata === undefined ? {} : { metadata: header.metadata }),\n\t};\n}\n\nfunction parseEntryMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"entry\" }> {\n\tconst lane = value.lane === undefined ? undefined : requireString(value.lane, \"lane\");\n\tconst id = requireString(value.id, \"id\");\n\tconst type = requireString(value.type, \"entry type\");\n\tif (!ENTRY_TYPES.has(type as Entry[\"type\"])) {\n\t\tthrow new JsonlDecodeError(\"schema\", `has unknown entry type ${type}`);\n\t}\n\tconst parentId = requireNullableId(value.parentId, \"parentId\");\n\tconst timestamp = requireTimestamp(value.timestamp);\n\tif (type === \"custom\") requireString(value.customType, \"customType\");\n\tconst { kind: _kind, lane: _lane, ...entryFields } = value;\n\tconst entry = { ...entryFields, id, type, parentId, seq, timestamp } as unknown as Entry;\n\treturn lane === undefined ? { kind: \"entry\", entry } : { kind: \"entry\", lane, entry };\n}\n\nfunction parseRecordMutation(\n\tvalue: Record<string, unknown>,\n\tseq: number,\n): Extract<SessionMutation, { kind: \"record\" }> {\n\tconst id = requireString(value.id, \"id\");\n\tconst lane = requireString(value.lane, \"lane\");\n\tconst type = requireString(value.type, \"record type\");\n\tif (!RECORD_TYPES.has(type as LaneRecord[\"type\"])) {\n\t\tthrow new JsonlDecodeError(\"schema\", `has unknown record type ${type}`);\n\t}\n\tconst timestamp = requireTimestamp(value.timestamp);\n\tif (type === \"operation_started\") {\n\t\tif (!isObject(value.intent)) throw new JsonlDecodeError(\"schema\", \"has invalid intent\");\n\t\tconst operationKind = requireString(value.intent.kind, \"operation kind\");\n\t\tif (!OPERATION_KINDS.has(operationKind)) {\n\t\t\tthrow new JsonlDecodeError(\"schema\", `has unknown operation kind ${operationKind}`);\n\t\t}\n\t}\n\tif (type === \"operation_finished\") requireString(value.runId, \"runId\");\n\tconst { kind: _kind, ...recordFields } = value;\n\treturn {\n\t\tkind: \"record\",\n\t\trecord: { ...recordFields, id, lane, type, seq, timestamp } as unknown as LaneRecord,\n\t};\n}\n\nfunction parseLaneMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"lane\" }> {\n\treturn {\n\t\tkind: \"lane\",\n\t\tseq,\n\t\tlane: requireString(value.lane, \"lane\"),\n\t\tleafId: requireNullableId(value.leafId, \"leafId\"),\n\t};\n}\n\nfunction parseFactMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"fact\" }> {\n\tif (value.fact === \"name\") {\n\t\tif (value.name !== undefined && typeof value.name !== \"string\") {\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid name\");\n\t\t}\n\t\treturn { kind: \"fact\", seq, fact: \"name\", name: value.name };\n\t}\n\tif (value.fact === \"label\") {\n\t\tif (value.label !== undefined && typeof value.label !== \"string\") {\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid label\");\n\t\t}\n\t\treturn {\n\t\t\tkind: \"fact\",\n\t\t\tseq,\n\t\t\tfact: \"label\",\n\t\t\ttargetId: requireString(value.targetId, \"targetId\"),\n\t\t\tlabel: value.label,\n\t\t};\n\t}\n\tthrow new JsonlDecodeError(\"schema\", \"has unknown fact type\");\n}\n\nfunction decodeMutation(line: string): SessionMutation {\n\tconst value = parseObject(line);\n\tconst seq = requireSequence(value.seq);\n\tswitch (value.kind) {\n\t\tcase \"entry\":\n\t\t\treturn parseEntryMutation(value, seq);\n\t\tcase \"record\":\n\t\t\treturn parseRecordMutation(value, seq);\n\t\tcase \"lane\":\n\t\t\treturn parseLaneMutation(value, seq);\n\t\tcase \"fact\":\n\t\t\treturn parseFactMutation(value, seq);\n\t\tdefault:\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has unknown mutation kind\");\n\t}\n}\n\nexport function parseMutation(line: string): Result<SessionMutation, JsonlDecodeError> {\n\ttry {\n\t\treturn ok<SessionMutation, JsonlDecodeError>(decodeMutation(line));\n\t} catch (error) {\n\t\tif (error instanceof JsonlDecodeError) return err<SessionMutation, JsonlDecodeError>(error);\n\t\tthrow error;\n\t}\n}\n\nexport function encodeMutation(mutation: SessionMutation): string {\n\tswitch (mutation.kind) {\n\t\tcase \"entry\":\n\t\t\treturn `${JSON.stringify({ kind: \"entry\", lane: mutation.lane, ...mutation.entry })}\\n`;\n\t\tcase \"record\":\n\t\t\treturn `${JSON.stringify({ kind: \"record\", ...mutation.record })}\\n`;\n\t\tcase \"lane\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t\tcase \"fact\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t}\n}\n"]}
@@ -1,5 +1,9 @@
1
1
  import type { FileError, Result } from "../../types.ts";
2
2
  import { SessionError } from "../types.ts";
3
+ export declare class JsonlDecodeError extends Error {
4
+ readonly kind: "syntax" | "schema";
5
+ constructor(kind: "syntax" | "schema", message: string, cause?: Error);
6
+ }
3
7
  export declare function fileResult<T>(result: Result<T, FileError>, message: string): T;
4
- export declare function invalidFile(path: string, line: number, message: string, cause?: Error): SessionError;
8
+ export declare function invalidFile(path: string, line: number, cause: Error): SessionError;
5
9
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAS9E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,YAAY,CAEpG","sourcesContent":["import type { FileError, Result } from \"../../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport function fileResult<T>(result: Result<T, FileError>, message: string): T {\n\tif (!result.ok) {\n\t\tthrow new SessionError(\n\t\t\tresult.error.code === \"not_found\" ? \"not_found\" : \"storage\",\n\t\t\t`${message}: ${result.error.message}`,\n\t\t\tresult.error,\n\t\t);\n\t}\n\treturn result.value;\n}\n\nexport function invalidFile(path: string, line: number, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL v4 session ${path}: line ${line} ${message}`, cause);\n}\n"]}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAEnC,YAAY,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAIpE;CACD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAS9E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,CAElF","sourcesContent":["import type { FileError, Result } from \"../../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport class JsonlDecodeError extends Error {\n\treadonly kind: \"syntax\" | \"schema\";\n\n\tconstructor(kind: \"syntax\" | \"schema\", message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"JsonlDecodeError\";\n\t\tthis.kind = kind;\n\t}\n}\n\nexport function fileResult<T>(result: Result<T, FileError>, message: string): T {\n\tif (!result.ok) {\n\t\tthrow new SessionError(\n\t\t\tresult.error.code === \"not_found\" ? \"not_found\" : \"storage\",\n\t\t\t`${message}: ${result.error.message}`,\n\t\t\tresult.error,\n\t\t);\n\t}\n\treturn result.value;\n}\n\nexport function invalidFile(path: string, line: number, cause: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL v4 session ${path}: line ${line} ${cause.message}`, cause);\n}\n"]}
@@ -1,11 +1,19 @@
1
1
  import { SessionError } from "../types.js";
2
+ export class JsonlDecodeError extends Error {
3
+ kind;
4
+ constructor(kind, message, cause) {
5
+ super(message, cause === undefined ? undefined : { cause });
6
+ this.name = "JsonlDecodeError";
7
+ this.kind = kind;
8
+ }
9
+ }
2
10
  export function fileResult(result, message) {
3
11
  if (!result.ok) {
4
12
  throw new SessionError(result.error.code === "not_found" ? "not_found" : "storage", `${message}: ${result.error.message}`, result.error);
5
13
  }
6
14
  return result.value;
7
15
  }
8
- export function invalidFile(path, line, message, cause) {
9
- return new SessionError("invalid_entry", `Invalid JSONL v4 session ${path}: line ${line} ${message}`, cause);
16
+ export function invalidFile(path, line, cause) {
17
+ return new SessionError("invalid_entry", `Invalid JSONL v4 session ${path}: line ${line} ${cause.message}`, cause);
10
18
  }
11
19
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,UAAU,UAAU,CAAI,MAA4B,EAAE,OAAe,EAAK;IAC/E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,YAAY,CACrB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,EAC3D,GAAG,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EACrC,MAAM,CAAC,KAAK,CACZ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC;AAAA,CACpB;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY,EAAE,OAAe,EAAE,KAAa,EAAgB;IACrG,OAAO,IAAI,YAAY,CAAC,eAAe,EAAE,4BAA4B,IAAI,UAAU,IAAI,IAAI,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAAA,CAC7G","sourcesContent":["import type { FileError, Result } from \"../../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport function fileResult<T>(result: Result<T, FileError>, message: string): T {\n\tif (!result.ok) {\n\t\tthrow new SessionError(\n\t\t\tresult.error.code === \"not_found\" ? \"not_found\" : \"storage\",\n\t\t\t`${message}: ${result.error.message}`,\n\t\t\tresult.error,\n\t\t);\n\t}\n\treturn result.value;\n}\n\nexport function invalidFile(path: string, line: number, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL v4 session ${path}: line ${line} ${message}`, cause);\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACjC,IAAI,CAAsB;IAEnC,YAAY,IAAyB,EAAE,OAAe,EAAE,KAAa,EAAE;QACtE,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;CACD;AAED,MAAM,UAAU,UAAU,CAAI,MAA4B,EAAE,OAAe,EAAK;IAC/E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,YAAY,CACrB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,EAC3D,GAAG,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EACrC,MAAM,CAAC,KAAK,CACZ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC;AAAA,CACpB;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY,EAAE,KAAY,EAAgB;IACnF,OAAO,IAAI,YAAY,CAAC,eAAe,EAAE,4BAA4B,IAAI,UAAU,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAAA,CACnH","sourcesContent":["import type { FileError, Result } from \"../../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport class JsonlDecodeError extends Error {\n\treadonly kind: \"syntax\" | \"schema\";\n\n\tconstructor(kind: \"syntax\" | \"schema\", message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"JsonlDecodeError\";\n\t\tthis.kind = kind;\n\t}\n}\n\nexport function fileResult<T>(result: Result<T, FileError>, message: string): T {\n\tif (!result.ok) {\n\t\tthrow new SessionError(\n\t\t\tresult.error.code === \"not_found\" ? \"not_found\" : \"storage\",\n\t\t\t`${message}: ${result.error.message}`,\n\t\t\tresult.error,\n\t\t);\n\t}\n\treturn result.value;\n}\n\nexport function invalidFile(path: string, line: number, cause: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL v4 session ${path}: line ${line} ${cause.message}`, cause);\n}\n"]}
@@ -1,22 +1,26 @@
1
1
  import { Session } from "../session.ts";
2
2
  import { type ForkOptions, type SessionRepo } from "../types.ts";
3
+ import { JsonlSessionStorage } from "./storage.ts";
3
4
  import type { JsonlSessionCreateOptions, JsonlSessionListOptions, JsonlSessionMetadata, JsonlSessionRepoOptions } from "./types.ts";
5
+ export declare function listJsonlSessionMetadata(options: JsonlSessionRepoOptions, query?: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
6
+ export declare function loadJsonlSessionStorage(options: JsonlSessionRepoOptions, metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage>;
4
7
  export declare class JsonlSessionRepo implements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions> {
5
8
  private readonly fs;
6
9
  private readonly sessionsRootInput;
10
+ private readonly activeCreateDestinations;
7
11
  private rootPromise;
8
12
  constructor(options: JsonlSessionRepoOptions);
9
13
  create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>>;
10
14
  open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>>;
11
- list(): Promise<JsonlSessionMetadata[]>;
12
- list(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
15
+ list(options?: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
13
16
  delete(metadata: JsonlSessionMetadata): Promise<void>;
14
17
  fork(source: JsonlSessionMetadata, options: ForkOptions & JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>>;
15
18
  private loadStorage;
19
+ private resolveCreateDestination;
20
+ private claimCreateDestination;
16
21
  private prepareCreate;
17
22
  private listDirect;
18
23
  private sessionIdExists;
19
- private sessionDirectories;
20
24
  private sessionDirectory;
21
25
  private root;
22
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/repo.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,OAAO,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAgB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/E,OAAO,KAAK,EACX,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EAEpB,uBAAuB,EAEvB,MAAM,YAAY,CAAC;AAsBpB,qBAAa,gBACZ,YAAW,WAAW,CAAC,oBAAoB,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;IAEhG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA6B;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,WAAW,CAA8B;IAEjD,YAAY,OAAO,EAAE,uBAAuB,EAG3C;IAEK,MAAM,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAGvF;IAEK,IAAI,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAEjF;IAED,IAAI,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAKlE,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;IAEK,IAAI,CACT,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,WAAW,GAAG,yBAAyB,GAC9C,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAOxC;YAEa,WAAW;YAYX,aAAa;YA+Bb,UAAU;YAqBV,eAAe;YAQf,kBAAkB;YAelB,gBAAgB;IAO9B,OAAO,CAAC,IAAI;CAMZ","sourcesContent":["import { uuidv7 } from \"@earendil-works/pi-ai\";\nimport { assertJsonSerializable, Session } from \"../session.ts\";\nimport { type ForkOptions, SessionError, type SessionRepo } from \"../types.ts\";\nimport { metadataFromHeader, parseHeader } from \"./codec.ts\";\nimport { fileResult, invalidFile } from \"./errors.ts\";\nimport { JsonlSessionStorage } from \"./storage.ts\";\nimport type {\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tJsonlSessionRepoFileSystem,\n\tJsonlSessionRepoOptions,\n\tJsonlV4Header,\n} from \"./types.ts\";\n\nconst SESSION_ID_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;\n\nfunction validateSessionId(id: string): void {\n\tif (!SESSION_ID_PATTERN.test(id)) {\n\t\tthrow new SessionError(\n\t\t\t\"invalid_payload\",\n\t\t\t\"Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t}\n}\n\nfunction sessionDirectoryName(cwd: string): string {\n\treturn `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n}\n\nfunction sessionFileName(createdAt: number, id: string): string {\n\tconst timestamp = new Date(createdAt).toISOString().replace(/[:.]/g, \"-\");\n\treturn `${timestamp}_${id}.jsonl`;\n}\n\nexport class JsonlSessionRepo\n\timplements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions>\n{\n\tprivate readonly fs: JsonlSessionRepoFileSystem;\n\tprivate readonly sessionsRootInput: string;\n\tprivate rootPromise: Promise<string> | undefined;\n\n\tconstructor(options: JsonlSessionRepoOptions) {\n\t\tthis.fs = options.fs;\n\t\tthis.sessionsRootInput = options.sessionsRoot;\n\t}\n\n\tasync create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst { header, path } = await this.prepareCreate(options);\n\t\treturn new Session(await JsonlSessionStorage.create(this.fs, path, header));\n\t}\n\n\tasync open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>> {\n\t\treturn new Session(await this.loadStorage(metadata));\n\t}\n\n\tlist(): Promise<JsonlSessionMetadata[]>;\n\tlist(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;\n\tasync list(options: JsonlSessionListOptions = {}): Promise<JsonlSessionMetadata[]> {\n\t\treturn this.listDirect(options);\n\t}\n\n\tasync delete(metadata: JsonlSessionMetadata): Promise<void> {\n\t\tfileResult(await this.fs.remove(metadata.path, { force: true }), `Failed to delete session ${metadata.path}`);\n\t}\n\n\tasync fork(\n\t\tsource: JsonlSessionMetadata,\n\t\toptions: ForkOptions & JsonlSessionCreateOptions,\n\t): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst sourceStorage = await this.loadStorage(source);\n\t\tconst { header, path } = await this.prepareCreate({\n\t\t\t...options,\n\t\t\tparentSessionId: options.parentSessionId ?? source.id,\n\t\t});\n\t\treturn new Session(await sourceStorage.fork(path, header, options));\n\t}\n\n\tprivate async loadStorage(metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage> {\n\t\tif (!fileResult(await this.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)) {\n\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t\t}\n\t\tconst storage = await JsonlSessionStorage.load(this.fs, metadata.path);\n\t\tconst loadedMetadata = await storage.getMetadata();\n\t\tif (loadedMetadata.id !== metadata.id) {\n\t\t\tthrow new SessionError(\"invalid_entry\", `Session id does not match header: ${metadata.id}`);\n\t\t}\n\t\treturn storage;\n\t}\n\n\tprivate async prepareCreate(options: JsonlSessionCreateOptions): Promise<{\n\t\theader: JsonlV4Header;\n\t\tpath: string;\n\t}> {\n\t\tconst id = options.id ?? uuidv7();\n\t\tvalidateSessionId(id);\n\t\tconst cwd = fileResult(await this.fs.absolutePath(options.cwd), `Failed to resolve session cwd ${options.cwd}`);\n\t\tif (await this.sessionIdExists(id, cwd)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\t}\n\n\t\tconst createdAt = Date.now();\n\t\tconst sessionDirectory = await this.sessionDirectory(cwd);\n\t\tconst path = fileResult(\n\t\t\tawait this.fs.joinPath([sessionDirectory, sessionFileName(createdAt, id)]),\n\t\t\t`Failed to resolve path for session ${id}`,\n\t\t);\n\t\tif (options.metadata !== undefined) assertJsonSerializable(options.metadata);\n\t\tconst header: JsonlV4Header = {\n\t\t\tkind: \"header\",\n\t\t\tversion: 4,\n\t\t\tid,\n\t\t\tcreatedAt,\n\t\t\tcwd,\n\t\t\tparentSessionId: options.parentSessionId,\n\t\t\tmetadata: options.metadata,\n\t\t};\n\t\tfileResult(await this.fs.createDir(sessionDirectory, { recursive: true }), `Failed to create sessions directory`);\n\t\treturn { header, path };\n\t}\n\n\tprivate async listDirect(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]> {\n\t\tconst directories = await this.sessionDirectories(options.cwd);\n\t\tconst metadata: JsonlSessionMetadata[] = [];\n\t\tfor (const directory of directories) {\n\t\t\tconst files = fileResult(\n\t\t\t\tawait this.fs.listDir(directory),\n\t\t\t\t`Failed to list sessions directory ${directory}`,\n\t\t\t).filter((entry) => entry.kind !== \"directory\" && entry.name.endsWith(\".jsonl\"));\n\t\t\tfor (const file of files) {\n\t\t\t\tconst content = fileResult(\n\t\t\t\t\tawait this.fs.readTextFile(file.path),\n\t\t\t\t\t`Failed to read session header ${file.path}`,\n\t\t\t\t);\n\t\t\t\tconst firstLine = content.split(\"\\n\", 1)[0];\n\t\t\t\tif (!firstLine) throw invalidFile(file.path, 1, \"is missing a header\");\n\t\t\t\tmetadata.push(metadataFromHeader(parseHeader(firstLine, file.path), file.path, file.mtimeMs));\n\t\t\t}\n\t\t}\n\t\treturn metadata.sort((left, right) => right.modifiedAt - left.modifiedAt);\n\t}\n\n\tprivate async sessionIdExists(id: string, cwd: string): Promise<boolean> {\n\t\tconst suffix = `_${id}.jsonl`;\n\t\tconst directory = await this.sessionDirectory(cwd);\n\t\tif (!fileResult(await this.fs.exists(directory), `Failed to check sessions directory ${directory}`)) return false;\n\t\tconst files = fileResult(await this.fs.listDir(directory), `Failed to list sessions directory ${directory}`);\n\t\treturn files.some((entry) => entry.kind !== \"directory\" && entry.name.endsWith(suffix));\n\t}\n\n\tprivate async sessionDirectories(cwd?: string): Promise<string[]> {\n\t\tconst root = await this.root();\n\t\tif (cwd !== undefined) {\n\t\t\tconst resolvedCwd = fileResult(await this.fs.absolutePath(cwd), `Failed to resolve session cwd ${cwd}`);\n\t\t\tconst directory = await this.sessionDirectory(resolvedCwd);\n\t\t\treturn fileResult(await this.fs.exists(directory), `Failed to check sessions directory ${directory}`)\n\t\t\t\t? [directory]\n\t\t\t\t: [];\n\t\t}\n\t\tif (!fileResult(await this.fs.exists(root), `Failed to check sessions directory ${root}`)) return [];\n\t\treturn fileResult(await this.fs.listDir(root), `Failed to list sessions directory ${root}`)\n\t\t\t.filter((entry) => entry.kind === \"directory\" || entry.kind === \"symlink\")\n\t\t\t.map((entry) => entry.path);\n\t}\n\n\tprivate async sessionDirectory(cwd: string): Promise<string> {\n\t\treturn fileResult(\n\t\t\tawait this.fs.joinPath([await this.root(), sessionDirectoryName(cwd)]),\n\t\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t\t);\n\t}\n\n\tprivate root(): Promise<string> {\n\t\tthis.rootPromise ??= this.fs\n\t\t\t.absolutePath(this.sessionsRootInput)\n\t\t\t.then((result) => fileResult(result, `Failed to resolve sessions root ${this.sessionsRootInput}`));\n\t\treturn this.rootPromise;\n\t}\n}\n"]}
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/repo.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,OAAO,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAgB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EACX,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EAEpB,uBAAuB,EAEvB,MAAM,YAAY,CAAC;AAmDpB,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,uBAA4B,GACjC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAmBjC;AAED,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAOD,qBAAa,gBACZ,YAAW,WAAW,CAAC,oBAAoB,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;IAEhG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA6B;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAqB;IAC9D,OAAO,CAAC,WAAW,CAA8B;IAEjD,YAAY,OAAO,EAAE,uBAAuB,EAG3C;IAEK,MAAM,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAMvF;IAEK,IAAI,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAEjF;IAEK,IAAI,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAEjF;IAEK,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;IAEK,IAAI,CACT,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,WAAW,GAAG,yBAAyB,GAC9C,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAWxC;YAEa,WAAW;YAIX,wBAAwB;YAYxB,sBAAsB;YAgBtB,aAAa;YAgCb,UAAU;YAIV,eAAe;YAQf,gBAAgB;IAO9B,OAAO,CAAC,IAAI;CAMZ","sourcesContent":["import { uuidv7 } from \"@earendil-works/pi-ai\";\nimport { assertJsonSerializable, Session } from \"../session.ts\";\nimport { type ForkOptions, SessionError, type SessionRepo } from \"../types.ts\";\nimport { metadataFromHeader, parseHeader } from \"./codec.ts\";\nimport { fileResult } from \"./errors.ts\";\nimport { JsonlSessionStorage } from \"./storage.ts\";\nimport type {\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tJsonlSessionRepoFileSystem,\n\tJsonlSessionRepoOptions,\n\tJsonlV4Header,\n} from \"./types.ts\";\n\nconst SESSION_ID_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;\n\nfunction validateSessionId(id: string): void {\n\tif (!SESSION_ID_PATTERN.test(id)) {\n\t\tthrow new SessionError(\n\t\t\t\"invalid_payload\",\n\t\t\t\"Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t}\n}\n\nfunction jsonlSessionDirectoryName(cwd: string): string {\n\treturn `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n}\n\nasync function jsonlSessionsRoot(options: JsonlSessionRepoOptions): Promise<string> {\n\treturn fileResult(\n\t\tawait options.fs.absolutePath(options.sessionsRoot),\n\t\t`Failed to resolve sessions root ${options.sessionsRoot}`,\n\t);\n}\n\nasync function jsonlSessionDirectory(\n\tfs: JsonlSessionRepoFileSystem,\n\tsessionsRoot: string,\n\tcwd: string,\n): Promise<string> {\n\treturn fileResult(\n\t\tawait fs.joinPath([sessionsRoot, jsonlSessionDirectoryName(cwd)]),\n\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t);\n}\n\nasync function jsonlSessionDirectories(options: JsonlSessionRepoOptions, cwd?: string): Promise<string[]> {\n\tconst sessionsRoot = await jsonlSessionsRoot(options);\n\tif (cwd !== undefined) {\n\t\tconst resolvedCwd = fileResult(await options.fs.absolutePath(cwd), `Failed to resolve session cwd ${cwd}`);\n\t\tconst directory = await jsonlSessionDirectory(options.fs, sessionsRoot, resolvedCwd);\n\t\treturn fileResult(await options.fs.exists(directory), `Failed to check sessions directory ${directory}`)\n\t\t\t? [directory]\n\t\t\t: [];\n\t}\n\tif (!fileResult(await options.fs.exists(sessionsRoot), `Failed to check sessions directory ${sessionsRoot}`))\n\t\treturn [];\n\treturn fileResult(await options.fs.listDir(sessionsRoot), `Failed to list sessions directory ${sessionsRoot}`)\n\t\t.filter((entry) => entry.kind === \"directory\" || entry.kind === \"symlink\")\n\t\t.map((entry) => entry.path);\n}\n\nexport async function listJsonlSessionMetadata(\n\toptions: JsonlSessionRepoOptions,\n\tquery: JsonlSessionListOptions = {},\n): Promise<JsonlSessionMetadata[]> {\n\tconst metadata: JsonlSessionMetadata[] = [];\n\tfor (const directory of await jsonlSessionDirectories(options, query.cwd)) {\n\t\tconst files = fileResult(\n\t\t\tawait options.fs.listDir(directory),\n\t\t\t`Failed to list sessions directory ${directory}`,\n\t\t).filter((entry) => entry.kind !== \"directory\" && entry.name.endsWith(\".jsonl\"));\n\t\tfor (const file of files) {\n\t\t\tconst [firstLine] = fileResult(\n\t\t\t\tawait options.fs.readTextLines(file.path, { maxLines: 1 }),\n\t\t\t\t`Failed to read session header ${file.path}`,\n\t\t\t);\n\t\t\tif (!firstLine) continue;\n\t\t\tconst headerResult = parseHeader(firstLine);\n\t\t\tif (!headerResult.ok) continue;\n\t\t\tmetadata.push(metadataFromHeader(headerResult.value, file.path, file.mtimeMs));\n\t\t}\n\t}\n\treturn metadata.sort((left, right) => right.modifiedAt - left.modifiedAt);\n}\n\nexport async function loadJsonlSessionStorage(\n\toptions: JsonlSessionRepoOptions,\n\tmetadata: JsonlSessionMetadata,\n): Promise<JsonlSessionStorage> {\n\tif (!fileResult(await options.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)) {\n\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t}\n\tconst storage = await JsonlSessionStorage.load(options.fs, metadata.path);\n\tconst loadedMetadata = await storage.getMetadata();\n\tif (loadedMetadata.id !== metadata.id) {\n\t\tthrow new SessionError(\"invalid_entry\", `Session id does not match header: ${metadata.id}`);\n\t}\n\treturn storage;\n}\n\nfunction sessionFileName(createdAt: number, id: string): string {\n\tconst timestamp = new Date(createdAt).toISOString().replace(/[:.]/g, \"-\");\n\treturn `${timestamp}_${id}.jsonl`;\n}\n\nexport class JsonlSessionRepo\n\timplements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions>\n{\n\tprivate readonly fs: JsonlSessionRepoFileSystem;\n\tprivate readonly sessionsRootInput: string;\n\tprivate readonly activeCreateDestinations = new Set<string>();\n\tprivate rootPromise: Promise<string> | undefined;\n\n\tconstructor(options: JsonlSessionRepoOptions) {\n\t\tthis.fs = options.fs;\n\t\tthis.sessionsRootInput = options.sessionsRoot;\n\t}\n\n\tasync create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst destination = await this.resolveCreateDestination(options);\n\t\treturn this.claimCreateDestination(destination, async () => {\n\t\t\tconst { header, path } = await this.prepareCreate(destination, options);\n\t\t\treturn new Session(await JsonlSessionStorage.create(this.fs, path, header));\n\t\t});\n\t}\n\n\tasync open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>> {\n\t\treturn new Session(await this.loadStorage(metadata));\n\t}\n\n\tasync list(options: JsonlSessionListOptions = {}): Promise<JsonlSessionMetadata[]> {\n\t\treturn this.listDirect(options);\n\t}\n\n\tasync delete(metadata: JsonlSessionMetadata): Promise<void> {\n\t\tfileResult(await this.fs.remove(metadata.path, { force: true }), `Failed to delete session ${metadata.path}`);\n\t}\n\n\tasync fork(\n\t\tsource: JsonlSessionMetadata,\n\t\toptions: ForkOptions & JsonlSessionCreateOptions,\n\t): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst sourceStorage = await this.loadStorage(source);\n\t\tconst createOptions = {\n\t\t\t...options,\n\t\t\tparentSessionId: options.parentSessionId ?? source.id,\n\t\t};\n\t\tconst destination = await this.resolveCreateDestination(createOptions);\n\t\treturn this.claimCreateDestination(destination, async () => {\n\t\t\tconst { header, path } = await this.prepareCreate(destination, createOptions);\n\t\t\treturn new Session(await sourceStorage.fork(path, header, options));\n\t\t});\n\t}\n\n\tprivate async loadStorage(metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage> {\n\t\treturn loadJsonlSessionStorage({ fs: this.fs, sessionsRoot: this.sessionsRootInput }, metadata);\n\t}\n\n\tprivate async resolveCreateDestination(options: JsonlSessionCreateOptions): Promise<{ id: string; cwd: string }> {\n\t\tconst id = options.id ?? uuidv7();\n\t\tvalidateSessionId(id);\n\t\tconst cwd = fileResult(await this.fs.absolutePath(options.cwd), `Failed to resolve session cwd ${options.cwd}`);\n\t\treturn { id, cwd };\n\t}\n\n\t/**\n\t * Prevent same-process create/fork races for one logical destination. The durable filename includes a\n\t * timestamp, so the async filesystem existence check alone can let two concurrent calls both decide the\n\t * same {cwd, id} is free and publish duplicate sessions.\n\t */\n\tprivate async claimCreateDestination<T>(\n\t\tdestination: { id: string; cwd: string },\n\t\toperation: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst key = `${destination.cwd}\\0${destination.id}`;\n\t\tif (this.activeCreateDestinations.has(key)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${destination.id}`);\n\t\t}\n\t\tthis.activeCreateDestinations.add(key);\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} finally {\n\t\t\tthis.activeCreateDestinations.delete(key);\n\t\t}\n\t}\n\n\tprivate async prepareCreate(\n\t\tdestination: { id: string; cwd: string },\n\t\toptions: JsonlSessionCreateOptions,\n\t): Promise<{\n\t\theader: JsonlV4Header;\n\t\tpath: string;\n\t}> {\n\t\tconst { id, cwd } = destination;\n\t\tif (await this.sessionIdExists(id, cwd)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\t}\n\n\t\tconst createdAt = Date.now();\n\t\tconst sessionDirectory = await this.sessionDirectory(cwd);\n\t\tconst path = fileResult(\n\t\t\tawait this.fs.joinPath([sessionDirectory, sessionFileName(createdAt, id)]),\n\t\t\t`Failed to resolve path for session ${id}`,\n\t\t);\n\t\tif (options.metadata !== undefined) assertJsonSerializable(options.metadata);\n\t\tconst header: JsonlV4Header = {\n\t\t\tkind: \"header\",\n\t\t\tversion: 4,\n\t\t\tid,\n\t\t\tcreatedAt,\n\t\t\tcwd,\n\t\t\tparentSessionId: options.parentSessionId,\n\t\t\tmetadata: options.metadata,\n\t\t};\n\t\tfileResult(await this.fs.createDir(sessionDirectory, { recursive: true }), `Failed to create sessions directory`);\n\t\treturn { header, path };\n\t}\n\n\tprivate async listDirect(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]> {\n\t\treturn listJsonlSessionMetadata({ fs: this.fs, sessionsRoot: this.sessionsRootInput }, options);\n\t}\n\n\tprivate async sessionIdExists(id: string, cwd: string): Promise<boolean> {\n\t\tconst suffix = `_${id}.jsonl`;\n\t\tconst directory = await this.sessionDirectory(cwd);\n\t\tif (!fileResult(await this.fs.exists(directory), `Failed to check sessions directory ${directory}`)) return false;\n\t\tconst files = fileResult(await this.fs.listDir(directory), `Failed to list sessions directory ${directory}`);\n\t\treturn files.some((entry) => entry.kind !== \"directory\" && entry.name.endsWith(suffix));\n\t}\n\n\tprivate async sessionDirectory(cwd: string): Promise<string> {\n\t\treturn fileResult(\n\t\t\tawait this.fs.joinPath([await this.root(), jsonlSessionDirectoryName(cwd)]),\n\t\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t\t);\n\t}\n\n\tprivate root(): Promise<string> {\n\t\tthis.rootPromise ??= this.fs\n\t\t\t.absolutePath(this.sessionsRootInput)\n\t\t\t.then((result) => fileResult(result, `Failed to resolve sessions root ${this.sessionsRootInput}`));\n\t\treturn this.rootPromise;\n\t}\n}\n"]}