@code-yeongyu/senpi-agent-core 2026.7.31 → 2026.8.3

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 (69) hide show
  1. package/dist/agent-loop.d.ts.map +1 -1
  2. package/dist/agent-loop.js +1 -0
  3. package/dist/agent-loop.js.map +1 -1
  4. package/dist/harness/agent-harness.d.ts +14 -3
  5. package/dist/harness/agent-harness.d.ts.map +1 -1
  6. package/dist/harness/agent-harness.js +156 -67
  7. package/dist/harness/agent-harness.js.map +1 -1
  8. package/dist/harness/session/array-session-reader.d.ts +3 -0
  9. package/dist/harness/session/array-session-reader.d.ts.map +1 -0
  10. package/dist/harness/session/array-session-reader.js +70 -0
  11. package/dist/harness/session/array-session-reader.js.map +1 -0
  12. package/dist/harness/session/fork.d.ts +4 -0
  13. package/dist/harness/session/fork.d.ts.map +1 -0
  14. package/dist/harness/session/fork.js +22 -0
  15. package/dist/harness/session/fork.js.map +1 -0
  16. package/dist/harness/session/jsonl-store.d.ts +13 -0
  17. package/dist/harness/session/jsonl-store.d.ts.map +1 -0
  18. package/dist/harness/session/jsonl-store.js +304 -0
  19. package/dist/harness/session/jsonl-store.js.map +1 -0
  20. package/dist/harness/session/keyed-operation-queue.d.ts +17 -0
  21. package/dist/harness/session/keyed-operation-queue.d.ts.map +1 -0
  22. package/dist/harness/session/keyed-operation-queue.js +60 -0
  23. package/dist/harness/session/keyed-operation-queue.js.map +1 -0
  24. package/dist/harness/session/memory-store.d.ts +6 -0
  25. package/dist/harness/session/memory-store.d.ts.map +1 -0
  26. package/dist/harness/session/memory-store.js +107 -0
  27. package/dist/harness/session/memory-store.js.map +1 -0
  28. package/dist/harness/session/repository.d.ts +28 -0
  29. package/dist/harness/session/repository.d.ts.map +1 -0
  30. package/dist/harness/session/repository.js +61 -0
  31. package/dist/harness/session/repository.js.map +1 -0
  32. package/dist/harness/session/search-backend.d.ts +8 -0
  33. package/dist/harness/session/search-backend.d.ts.map +1 -0
  34. package/dist/harness/session/search-backend.js +22 -0
  35. package/dist/harness/session/search-backend.js.map +1 -0
  36. package/dist/harness/session/session.d.ts +6 -10
  37. package/dist/harness/session/session.d.ts.map +1 -1
  38. package/dist/harness/session/session.js +154 -77
  39. package/dist/harness/session/session.js.map +1 -1
  40. package/dist/harness/types.d.ts +49 -23
  41. package/dist/harness/types.d.ts.map +1 -1
  42. package/dist/harness/types.js.map +1 -1
  43. package/dist/index.d.ts +6 -6
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +6 -6
  46. package/dist/index.js.map +1 -1
  47. package/dist/proxy.js +1 -1
  48. package/dist/proxy.js.map +1 -1
  49. package/package.json +4 -4
  50. package/dist/harness/session/jsonl-repo.d.ts +0 -26
  51. package/dist/harness/session/jsonl-repo.d.ts.map +0 -1
  52. package/dist/harness/session/jsonl-repo.js +0 -100
  53. package/dist/harness/session/jsonl-repo.js.map +0 -1
  54. package/dist/harness/session/jsonl-storage.d.ts +0 -42
  55. package/dist/harness/session/jsonl-storage.d.ts.map +0 -1
  56. package/dist/harness/session/jsonl-storage.js +0 -286
  57. package/dist/harness/session/jsonl-storage.js.map +0 -1
  58. package/dist/harness/session/memory-repo.d.ts +0 -18
  59. package/dist/harness/session/memory-repo.d.ts.map +0 -1
  60. package/dist/harness/session/memory-repo.js +0 -44
  61. package/dist/harness/session/memory-repo.js.map +0 -1
  62. package/dist/harness/session/memory-storage.d.ts +0 -33
  63. package/dist/harness/session/memory-storage.d.ts.map +0 -1
  64. package/dist/harness/session/memory-storage.js +0 -163
  65. package/dist/harness/session/memory-storage.js.map +0 -1
  66. package/dist/harness/session/repo-utils.d.ts +0 -11
  67. package/dist/harness/session/repo-utils.d.ts.map +0 -1
  68. package/dist/harness/session/repo-utils.js +0 -39
  69. package/dist/harness/session/repo-utils.js.map +0 -1
@@ -0,0 +1,304 @@
1
+ import { SessionError, toError } from "../types.js";
2
+ import { createArraySessionReader } from "./array-session-reader.js";
3
+ import { readSessionEntriesForFork } from "./fork.js";
4
+ import { KeyedOperationQueue } from "./keyed-operation-queue.js";
5
+ import { createSessionId, createTimestamp, getFileSystemResultOrThrow } from "./repository.js";
6
+ const DEFAULT_MAX_CONCURRENT_OPERATIONS = 4;
7
+ function invalidSession(path, message, cause) {
8
+ return new SessionError("invalid_session", `Invalid JSONL session file ${path}: ${message}`, cause);
9
+ }
10
+ function invalidEntry(path, line, message, cause) {
11
+ return new SessionError("invalid_entry", `Invalid JSONL session file ${path}: line ${line} ${message}`, cause);
12
+ }
13
+ function parseHeader(line, path) {
14
+ let value;
15
+ try {
16
+ value = JSON.parse(line);
17
+ }
18
+ catch (error) {
19
+ throw invalidSession(path, "first line is not a valid session header", toError(error));
20
+ }
21
+ if (typeof value !== "object" || value === null)
22
+ throw invalidSession(path, "first line is not a valid session header");
23
+ const header = value;
24
+ if (header.type !== "session" || header.version !== 3) {
25
+ throw invalidSession(path, header.type === "session" ? "unsupported session version" : "first line is not a valid session header");
26
+ }
27
+ if (typeof header.id !== "string" || !header.id)
28
+ throw invalidSession(path, "session header is missing id");
29
+ if (typeof header.timestamp !== "string" || !header.timestamp)
30
+ throw invalidSession(path, "session header is missing timestamp");
31
+ if (typeof header.cwd !== "string" || !header.cwd)
32
+ throw invalidSession(path, "session header is missing cwd");
33
+ if (header.parentSession !== undefined && typeof header.parentSession !== "string") {
34
+ throw invalidSession(path, "session header parentSession must be a string");
35
+ }
36
+ if (header.metadata !== undefined &&
37
+ (typeof header.metadata !== "object" || header.metadata === null || Array.isArray(header.metadata))) {
38
+ throw invalidSession(path, "session header metadata must be an object");
39
+ }
40
+ return {
41
+ type: "session",
42
+ version: 3,
43
+ id: header.id,
44
+ timestamp: header.timestamp,
45
+ cwd: header.cwd,
46
+ parentSession: header.parentSession,
47
+ metadata: header.metadata,
48
+ };
49
+ }
50
+ function parseEntry(line, path, lineNumber) {
51
+ let value;
52
+ try {
53
+ value = JSON.parse(line);
54
+ }
55
+ catch (error) {
56
+ throw invalidEntry(path, lineNumber, "is not valid JSON", toError(error));
57
+ }
58
+ if (typeof value !== "object" || value === null)
59
+ throw invalidEntry(path, lineNumber, "is not a valid session entry");
60
+ const entry = value;
61
+ if (typeof entry.type !== "string")
62
+ throw invalidEntry(path, lineNumber, "is missing entry type");
63
+ if (typeof entry.id !== "string" || !entry.id)
64
+ throw invalidEntry(path, lineNumber, "is missing entry id");
65
+ if (entry.parentId !== null && typeof entry.parentId !== "string")
66
+ throw invalidEntry(path, lineNumber, "has invalid parentId");
67
+ if (typeof entry.timestamp !== "string" || !entry.timestamp)
68
+ throw invalidEntry(path, lineNumber, "is missing timestamp");
69
+ if (entry.type === "leaf" && entry.targetId !== null && typeof entry.targetId !== "string") {
70
+ throw invalidEntry(path, lineNumber, "has invalid targetId");
71
+ }
72
+ return entry;
73
+ }
74
+ function metadataFromHeader(header, path) {
75
+ return {
76
+ id: header.id,
77
+ createdAt: header.timestamp,
78
+ cwd: header.cwd,
79
+ path,
80
+ parentSessionPath: header.parentSession,
81
+ metadata: header.metadata,
82
+ };
83
+ }
84
+ export async function loadJsonlSessionMetadata(fs, path) {
85
+ const lines = getFileSystemResultOrThrow(await fs.readTextLines(path, { maxLines: 1 }), `Failed to read session header ${path}`);
86
+ if (!lines[0]?.trim())
87
+ throw invalidSession(path, "missing session header");
88
+ return metadataFromHeader(parseHeader(lines[0], path), path);
89
+ }
90
+ async function loadJsonlSession(fs, path) {
91
+ const content = getFileSystemResultOrThrow(await fs.readTextFile(path), `Failed to read session ${path}`);
92
+ const lines = content.split("\n").filter((line) => line.trim());
93
+ if (lines.length === 0)
94
+ throw invalidSession(path, "missing session header");
95
+ const header = parseHeader(lines[0], path);
96
+ const entries = lines.slice(1).map((line, index) => parseEntry(line, path, index + 2));
97
+ const entryIds = new Set();
98
+ for (const entry of entries) {
99
+ if (entryIds.has(entry.id))
100
+ throw invalidSession(path, `duplicate entry id ${entry.id}`);
101
+ entryIds.add(entry.id);
102
+ }
103
+ return {
104
+ metadata: metadataFromHeader(header, path),
105
+ entries,
106
+ };
107
+ }
108
+ function encodeCwd(cwd) {
109
+ return `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
110
+ }
111
+ function createDocumentDescriptor(options) {
112
+ const id = options.id ?? createSessionId();
113
+ if (!id)
114
+ throw new SessionError("invalid_session", "Session id cannot be empty");
115
+ let encodedId;
116
+ try {
117
+ encodedId = encodeURIComponent(id);
118
+ }
119
+ catch (error) {
120
+ throw new SessionError("invalid_session", `Invalid session id ${JSON.stringify(id)}`, toError(error));
121
+ }
122
+ const timestamp = createTimestamp();
123
+ const fileName = `${timestamp.replace(/[:.]/g, "-")}_${encodedId}.jsonl`;
124
+ return {
125
+ id,
126
+ timestamp,
127
+ fileName,
128
+ operationKey: `document:${JSON.stringify([encodeCwd(options.cwd), fileName])}`,
129
+ };
130
+ }
131
+ class JsonlSessionStore {
132
+ constructor(options) {
133
+ this.entryIdsByPath = new Map();
134
+ this.entriesByPath = new Map();
135
+ this.operationKeysByPath = new Map();
136
+ this.disposed = false;
137
+ this.fs = options.fs;
138
+ this.sessionsRootInput = options.sessionsRoot;
139
+ this.operations = new KeyedOperationQueue({
140
+ maxConcurrentOperations: options.maxConcurrentOperations ?? DEFAULT_MAX_CONCURRENT_OPERATIONS,
141
+ });
142
+ }
143
+ create(options) {
144
+ this.assertOpen();
145
+ const descriptor = createDocumentDescriptor(options);
146
+ return this.operations.enqueue(descriptor.operationKey, () => this.createDocument(descriptor, options, options.parentSessionPath, options.metadata, []));
147
+ }
148
+ load(metadata) {
149
+ this.assertOpen();
150
+ return this.operations.enqueue(this.operationKey(metadata), () => this.loadDocument(metadata));
151
+ }
152
+ async loadDocument(metadata) {
153
+ if (!getFileSystemResultOrThrow(await this.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)) {
154
+ throw new SessionError("not_found", `Session not found: ${metadata.path}`);
155
+ }
156
+ const document = await loadJsonlSession(this.fs, metadata.path);
157
+ this.entriesByPath.set(metadata.path, document.entries);
158
+ this.entryIdsByPath.set(metadata.path, new Set(document.entries.map((entry) => entry.id)));
159
+ return this.reader(document.metadata);
160
+ }
161
+ list(options = {}) {
162
+ this.assertOpen();
163
+ return this.operations.enqueueBarrier(() => this.listSessions(options));
164
+ }
165
+ async listSessions(options) {
166
+ const dirs = options.cwd ? [await this.getSessionDir(options.cwd)] : await this.listSessionDirs();
167
+ const sessions = [];
168
+ for (const dir of dirs) {
169
+ if (!getFileSystemResultOrThrow(await this.fs.exists(dir), `Failed to check session directory ${dir}`))
170
+ continue;
171
+ const files = getFileSystemResultOrThrow(await this.fs.listDir(dir), `Failed to list sessions in ${dir}`).filter((file) => file.kind !== "directory" && file.name.endsWith(".jsonl"));
172
+ for (const file of files)
173
+ sessions.push(await loadJsonlSessionMetadata(this.fs, file.path));
174
+ }
175
+ return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
176
+ }
177
+ appendEntry(metadata, entry) {
178
+ this.assertOpen();
179
+ return this.operations.enqueue(this.operationKey(metadata), async () => {
180
+ if (!getFileSystemResultOrThrow(await this.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)) {
181
+ throw new SessionError("not_found", `Session not found: ${metadata.path}`);
182
+ }
183
+ let entryIds = this.entryIdsByPath.get(metadata.path);
184
+ if (!entryIds) {
185
+ await this.loadDocument(metadata);
186
+ entryIds = this.entryIdsByPath.get(metadata.path);
187
+ }
188
+ if (entryIds.has(entry.id))
189
+ throw new SessionError("invalid_entry", `Entry ${entry.id} already exists`);
190
+ getFileSystemResultOrThrow(await this.fs.appendFile(metadata.path, `${JSON.stringify(entry)}\n`), `Failed to append session entry ${entry.id}`);
191
+ entryIds.add(entry.id);
192
+ this.entryIdsByPath.set(metadata.path, entryIds);
193
+ this.entriesByPath.get(metadata.path).push(entry);
194
+ });
195
+ }
196
+ delete(metadata) {
197
+ this.assertOpen();
198
+ return this.operations.enqueue(this.operationKey(metadata), async () => {
199
+ getFileSystemResultOrThrow(await this.fs.remove(metadata.path, { force: true }), `Failed to delete session ${metadata.path}`);
200
+ this.entryIdsByPath.delete(metadata.path);
201
+ this.entriesByPath.delete(metadata.path);
202
+ this.operationKeysByPath.delete(metadata.path);
203
+ });
204
+ }
205
+ fork(source, options, selection) {
206
+ this.assertOpen();
207
+ const descriptor = createDocumentDescriptor(options);
208
+ const sourceEntries = this.operations.enqueue(this.operationKey(source), async () => {
209
+ if (!getFileSystemResultOrThrow(await this.fs.exists(source.path), `Failed to check session ${source.path}`)) {
210
+ throw new SessionError("not_found", `Session not found: ${source.path}`);
211
+ }
212
+ const document = await loadJsonlSession(this.fs, source.path);
213
+ this.entriesByPath.set(source.path, document.entries);
214
+ this.entryIdsByPath.set(source.path, new Set(document.entries.map((entry) => entry.id)));
215
+ return readSessionEntriesForFork(createArraySessionReader(document.metadata, () => document.entries), selection);
216
+ });
217
+ return this.operations.enqueue(descriptor.operationKey, async () => this.createDocument(descriptor, options, options.parentSessionPath ?? source.path, options.metadata ?? source.metadata, await sourceEntries));
218
+ }
219
+ async [Symbol.asyncDispose]() {
220
+ if (!this.disposePromise) {
221
+ this.disposed = true;
222
+ this.disposePromise = this.operations.drain();
223
+ }
224
+ await this.disposePromise;
225
+ }
226
+ assertOpen() {
227
+ if (this.disposed)
228
+ throw new SessionError("storage", "JSONL session store is disposed");
229
+ }
230
+ operationKey(metadata) {
231
+ return this.operationKeysByPath.get(metadata.path) ?? metadata.path;
232
+ }
233
+ async createDocument(descriptor, options, parentSessionPath, metadata, entries) {
234
+ const dir = await this.getSessionDir(options.cwd);
235
+ getFileSystemResultOrThrow(await this.fs.createDir(dir, { recursive: true }), `Failed to create session directory ${dir}`);
236
+ const path = getFileSystemResultOrThrow(await this.fs.joinPath([dir, descriptor.fileName]), `Failed to resolve session file path for ${descriptor.id}`);
237
+ if (getFileSystemResultOrThrow(await this.fs.exists(path), `Failed to check session ${path}`)) {
238
+ throw new SessionError("invalid_session", `Session already exists: ${path}`);
239
+ }
240
+ const header = {
241
+ type: "session",
242
+ version: 3,
243
+ id: descriptor.id,
244
+ timestamp: descriptor.timestamp,
245
+ cwd: options.cwd,
246
+ parentSession: parentSessionPath,
247
+ metadata,
248
+ };
249
+ const content = [JSON.stringify(header), ...entries.map((entry) => JSON.stringify(entry)), ""].join("\n");
250
+ getFileSystemResultOrThrow(await this.fs.writeFile(path, content), `Failed to create session ${path}`);
251
+ const storedEntries = [...entries];
252
+ this.entriesByPath.set(path, storedEntries);
253
+ this.entryIdsByPath.set(path, new Set(storedEntries.map((entry) => entry.id)));
254
+ this.operationKeysByPath.set(path, descriptor.operationKey);
255
+ return this.reader(metadataFromHeader(header, path));
256
+ }
257
+ reader(metadata) {
258
+ const reader = createArraySessionReader(metadata, () => {
259
+ const entries = this.entriesByPath.get(metadata.path);
260
+ if (!entries)
261
+ throw new SessionError("not_found", `Session not found: ${metadata.path}`);
262
+ return entries;
263
+ });
264
+ const operationKey = this.operationKey(metadata);
265
+ return {
266
+ metadata: reader.metadata,
267
+ readHead: () => {
268
+ this.assertOpen();
269
+ return this.operations.enqueue(operationKey, () => reader.readHead());
270
+ },
271
+ readEntry: (id) => {
272
+ this.assertOpen();
273
+ return this.operations.enqueue(operationKey, () => reader.readEntry(id));
274
+ },
275
+ readEntries: (options) => {
276
+ this.assertOpen();
277
+ return this.operations.enqueue(operationKey, () => reader.readEntries(options));
278
+ },
279
+ readPathToRootOrCompaction: (leafId) => {
280
+ this.assertOpen();
281
+ return this.operations.enqueue(operationKey, () => reader.readPathToRootOrCompaction(leafId));
282
+ },
283
+ };
284
+ }
285
+ async getSessionsRoot() {
286
+ this.sessionsRoot ??= getFileSystemResultOrThrow(await this.fs.absolutePath(this.sessionsRootInput), `Failed to resolve sessions root ${this.sessionsRootInput}`);
287
+ return this.sessionsRoot;
288
+ }
289
+ async getSessionDir(cwd) {
290
+ return getFileSystemResultOrThrow(await this.fs.joinPath([await this.getSessionsRoot(), encodeCwd(cwd)]), `Failed to resolve session directory for ${cwd}`);
291
+ }
292
+ async listSessionDirs() {
293
+ const root = await this.getSessionsRoot();
294
+ if (!getFileSystemResultOrThrow(await this.fs.exists(root), `Failed to check sessions root ${root}`))
295
+ return [];
296
+ return getFileSystemResultOrThrow(await this.fs.listDir(root), `Failed to list sessions root ${root}`)
297
+ .filter((entry) => entry.kind === "directory")
298
+ .map((entry) => entry.path);
299
+ }
300
+ }
301
+ export function createJsonlSessionStore(options) {
302
+ return new JsonlSessionStore(options);
303
+ }
304
+ //# sourceMappingURL=jsonl-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl-store.js","sourceRoot":"","sources":["../../../src/harness/session/jsonl-store.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tFileSystem,\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tSessionForkSelection,\n\tSessionReader,\n\tSessionStore,\n\tSessionTreeEntry,\n} from \"../types.ts\";\nimport { SessionError, toError } from \"../types.ts\";\nimport { createArraySessionReader } from \"./array-session-reader.ts\";\nimport { readSessionEntriesForFork } from \"./fork.ts\";\nimport { KeyedOperationQueue } from \"./keyed-operation-queue.ts\";\nimport { createSessionId, createTimestamp, getFileSystemResultOrThrow } from \"./repository.ts\";\n\nexport interface JsonlSessionStoreOptions {\n\tfs: JsonlSessionStoreFileSystem;\n\tsessionsRoot: string;\n\t/** Maximum active operations across session keys. Defaults to 4. */\n\tmaxConcurrentOperations?: number;\n}\nexport type JsonlSessionStoreFileSystem = Pick<\n\tFileSystem,\n\t| \"absolutePath\"\n\t| \"joinPath\"\n\t| \"readTextFile\"\n\t| \"readTextLines\"\n\t| \"writeFile\"\n\t| \"appendFile\"\n\t| \"listDir\"\n\t| \"exists\"\n\t| \"createDir\"\n\t| \"remove\"\n>;\n\ntype JsonlSessionFileSystem = Pick<FileSystem, \"readTextFile\" | \"readTextLines\" | \"writeFile\" | \"appendFile\">;\n\nconst DEFAULT_MAX_CONCURRENT_OPERATIONS = 4;\n\ninterface SessionHeader {\n\ttype: \"session\";\n\tversion: 3;\n\tid: string;\n\ttimestamp: string;\n\tcwd: string;\n\tparentSession?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\ninterface SessionDocumentDescriptor {\n\tid: string;\n\ttimestamp: string;\n\tfileName: string;\n\toperationKey: string;\n}\n\ninterface JsonlSessionDocument {\n\tmetadata: JsonlSessionMetadata;\n\tentries: SessionTreeEntry[];\n}\n\nfunction invalidSession(path: string, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\"invalid_session\", `Invalid JSONL session file ${path}: ${message}`, cause);\n}\n\nfunction invalidEntry(path: string, line: number, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL session file ${path}: line ${line} ${message}`, cause);\n}\n\nfunction parseHeader(line: string, path: string): SessionHeader {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidSession(path, \"first line is not a valid session header\", toError(error));\n\t}\n\tif (typeof value !== \"object\" || value === null)\n\t\tthrow invalidSession(path, \"first line is not a valid session header\");\n\tconst header = value as Partial<SessionHeader>;\n\tif (header.type !== \"session\" || header.version !== 3) {\n\t\tthrow invalidSession(\n\t\t\tpath,\n\t\t\theader.type === \"session\" ? \"unsupported session version\" : \"first line is not a valid session header\",\n\t\t);\n\t}\n\tif (typeof header.id !== \"string\" || !header.id) throw invalidSession(path, \"session header is missing id\");\n\tif (typeof header.timestamp !== \"string\" || !header.timestamp)\n\t\tthrow invalidSession(path, \"session header is missing timestamp\");\n\tif (typeof header.cwd !== \"string\" || !header.cwd) throw invalidSession(path, \"session header is missing cwd\");\n\tif (header.parentSession !== undefined && typeof header.parentSession !== \"string\") {\n\t\tthrow invalidSession(path, \"session header parentSession must be a string\");\n\t}\n\tif (\n\t\theader.metadata !== undefined &&\n\t\t(typeof header.metadata !== \"object\" || header.metadata === null || Array.isArray(header.metadata))\n\t) {\n\t\tthrow invalidSession(path, \"session header metadata must be an object\");\n\t}\n\treturn {\n\t\ttype: \"session\",\n\t\tversion: 3,\n\t\tid: header.id,\n\t\ttimestamp: header.timestamp,\n\t\tcwd: header.cwd,\n\t\tparentSession: header.parentSession,\n\t\tmetadata: header.metadata,\n\t};\n}\n\nfunction parseEntry(line: string, path: string, lineNumber: number): SessionTreeEntry {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidEntry(path, lineNumber, \"is not valid JSON\", toError(error));\n\t}\n\tif (typeof value !== \"object\" || value === null)\n\t\tthrow invalidEntry(path, lineNumber, \"is not a valid session entry\");\n\tconst entry = value as { type?: unknown; id?: unknown; parentId?: unknown; timestamp?: unknown; targetId?: unknown };\n\tif (typeof entry.type !== \"string\") throw invalidEntry(path, lineNumber, \"is missing entry type\");\n\tif (typeof entry.id !== \"string\" || !entry.id) throw invalidEntry(path, lineNumber, \"is missing entry id\");\n\tif (entry.parentId !== null && typeof entry.parentId !== \"string\")\n\t\tthrow invalidEntry(path, lineNumber, \"has invalid parentId\");\n\tif (typeof entry.timestamp !== \"string\" || !entry.timestamp)\n\t\tthrow invalidEntry(path, lineNumber, \"is missing timestamp\");\n\tif (entry.type === \"leaf\" && entry.targetId !== null && typeof entry.targetId !== \"string\") {\n\t\tthrow invalidEntry(path, lineNumber, \"has invalid targetId\");\n\t}\n\treturn entry as SessionTreeEntry;\n}\n\nfunction metadataFromHeader(header: SessionHeader, path: string): JsonlSessionMetadata {\n\treturn {\n\t\tid: header.id,\n\t\tcreatedAt: header.timestamp,\n\t\tcwd: header.cwd,\n\t\tpath,\n\t\tparentSessionPath: header.parentSession,\n\t\tmetadata: header.metadata,\n\t};\n}\n\nexport async function loadJsonlSessionMetadata(\n\tfs: JsonlSessionFileSystem,\n\tpath: string,\n): Promise<JsonlSessionMetadata> {\n\tconst lines = getFileSystemResultOrThrow(\n\t\tawait fs.readTextLines(path, { maxLines: 1 }),\n\t\t`Failed to read session header ${path}`,\n\t);\n\tif (!lines[0]?.trim()) throw invalidSession(path, \"missing session header\");\n\treturn metadataFromHeader(parseHeader(lines[0], path), path);\n}\n\nasync function loadJsonlSession(fs: JsonlSessionFileSystem, path: string): Promise<JsonlSessionDocument> {\n\tconst content = getFileSystemResultOrThrow(await fs.readTextFile(path), `Failed to read session ${path}`);\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim());\n\tif (lines.length === 0) throw invalidSession(path, \"missing session header\");\n\tconst header = parseHeader(lines[0]!, path);\n\tconst entries = lines.slice(1).map((line, index) => parseEntry(line, path, index + 2));\n\tconst entryIds = new Set<string>();\n\tfor (const entry of entries) {\n\t\tif (entryIds.has(entry.id)) throw invalidSession(path, `duplicate entry id ${entry.id}`);\n\t\tentryIds.add(entry.id);\n\t}\n\treturn {\n\t\tmetadata: metadataFromHeader(header, path),\n\t\tentries,\n\t};\n}\n\nfunction encodeCwd(cwd: string): string {\n\treturn `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n}\n\nfunction createDocumentDescriptor(options: JsonlSessionCreateOptions): SessionDocumentDescriptor {\n\tconst id = options.id ?? createSessionId();\n\tif (!id) throw new SessionError(\"invalid_session\", \"Session id cannot be empty\");\n\tlet encodedId: string;\n\ttry {\n\t\tencodedId = encodeURIComponent(id);\n\t} catch (error) {\n\t\tthrow new SessionError(\"invalid_session\", `Invalid session id ${JSON.stringify(id)}`, toError(error));\n\t}\n\tconst timestamp = createTimestamp();\n\tconst fileName = `${timestamp.replace(/[:.]/g, \"-\")}_${encodedId}.jsonl`;\n\treturn {\n\t\tid,\n\t\ttimestamp,\n\t\tfileName,\n\t\toperationKey: `document:${JSON.stringify([encodeCwd(options.cwd), fileName])}`,\n\t};\n}\n\nclass JsonlSessionStore\n\timplements SessionStore<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions>\n{\n\tprivate readonly fs: JsonlSessionStoreFileSystem;\n\tprivate readonly sessionsRootInput: string;\n\tprivate sessionsRoot: string | undefined;\n\tprivate readonly entryIdsByPath = new Map<string, Set<string>>();\n\tprivate readonly entriesByPath = new Map<string, SessionTreeEntry[]>();\n\tprivate readonly operationKeysByPath = new Map<string, string>();\n\tprivate readonly operations: KeyedOperationQueue<string>;\n\tprivate disposed = false;\n\tprivate disposePromise: Promise<void> | undefined;\n\n\tconstructor(options: JsonlSessionStoreOptions) {\n\t\tthis.fs = options.fs;\n\t\tthis.sessionsRootInput = options.sessionsRoot;\n\t\tthis.operations = new KeyedOperationQueue({\n\t\t\tmaxConcurrentOperations: options.maxConcurrentOperations ?? DEFAULT_MAX_CONCURRENT_OPERATIONS,\n\t\t});\n\t}\n\n\tcreate(options: JsonlSessionCreateOptions): Promise<SessionReader<JsonlSessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst descriptor = createDocumentDescriptor(options);\n\t\treturn this.operations.enqueue(descriptor.operationKey, () =>\n\t\t\tthis.createDocument(descriptor, options, options.parentSessionPath, options.metadata, []),\n\t\t);\n\t}\n\n\tload(metadata: JsonlSessionMetadata): Promise<SessionReader<JsonlSessionMetadata>> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueue(this.operationKey(metadata), () => this.loadDocument(metadata));\n\t}\n\n\tprivate async loadDocument(metadata: JsonlSessionMetadata): Promise<SessionReader<JsonlSessionMetadata>> {\n\t\tif (\n\t\t\t!getFileSystemResultOrThrow(await this.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)\n\t\t) {\n\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.path}`);\n\t\t}\n\t\tconst document = await loadJsonlSession(this.fs, metadata.path);\n\t\tthis.entriesByPath.set(metadata.path, document.entries);\n\t\tthis.entryIdsByPath.set(metadata.path, new Set(document.entries.map((entry) => entry.id)));\n\t\treturn this.reader(document.metadata);\n\t}\n\n\tlist(options: JsonlSessionListOptions = {}): Promise<JsonlSessionMetadata[]> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueueBarrier(() => this.listSessions(options));\n\t}\n\n\tprivate async listSessions(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]> {\n\t\tconst dirs = options.cwd ? [await this.getSessionDir(options.cwd)] : await this.listSessionDirs();\n\t\tconst sessions: JsonlSessionMetadata[] = [];\n\t\tfor (const dir of dirs) {\n\t\t\tif (!getFileSystemResultOrThrow(await this.fs.exists(dir), `Failed to check session directory ${dir}`))\n\t\t\t\tcontinue;\n\t\t\tconst files = getFileSystemResultOrThrow(\n\t\t\t\tawait this.fs.listDir(dir),\n\t\t\t\t`Failed to list sessions in ${dir}`,\n\t\t\t).filter((file) => file.kind !== \"directory\" && file.name.endsWith(\".jsonl\"));\n\t\t\tfor (const file of files) sessions.push(await loadJsonlSessionMetadata(this.fs, file.path));\n\t\t}\n\t\treturn sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n\t}\n\n\tappendEntry(metadata: JsonlSessionMetadata, entry: SessionTreeEntry): Promise<void> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueue(this.operationKey(metadata), async () => {\n\t\t\tif (\n\t\t\t\t!getFileSystemResultOrThrow(await this.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)\n\t\t\t) {\n\t\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.path}`);\n\t\t\t}\n\t\t\tlet entryIds = this.entryIdsByPath.get(metadata.path);\n\t\t\tif (!entryIds) {\n\t\t\t\tawait this.loadDocument(metadata);\n\t\t\t\tentryIds = this.entryIdsByPath.get(metadata.path)!;\n\t\t\t}\n\t\t\tif (entryIds.has(entry.id)) throw new SessionError(\"invalid_entry\", `Entry ${entry.id} already exists`);\n\t\t\tgetFileSystemResultOrThrow(\n\t\t\t\tawait this.fs.appendFile(metadata.path, `${JSON.stringify(entry)}\\n`),\n\t\t\t\t`Failed to append session entry ${entry.id}`,\n\t\t\t);\n\t\t\tentryIds.add(entry.id);\n\t\t\tthis.entryIdsByPath.set(metadata.path, entryIds);\n\t\t\tthis.entriesByPath.get(metadata.path)!.push(entry);\n\t\t});\n\t}\n\n\tdelete(metadata: JsonlSessionMetadata): Promise<void> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueue(this.operationKey(metadata), async () => {\n\t\t\tgetFileSystemResultOrThrow(\n\t\t\t\tawait this.fs.remove(metadata.path, { force: true }),\n\t\t\t\t`Failed to delete session ${metadata.path}`,\n\t\t\t);\n\t\t\tthis.entryIdsByPath.delete(metadata.path);\n\t\t\tthis.entriesByPath.delete(metadata.path);\n\t\t\tthis.operationKeysByPath.delete(metadata.path);\n\t\t});\n\t}\n\n\tfork(\n\t\tsource: JsonlSessionMetadata,\n\t\toptions: JsonlSessionCreateOptions,\n\t\tselection: SessionForkSelection,\n\t): Promise<SessionReader<JsonlSessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst descriptor = createDocumentDescriptor(options);\n\t\tconst sourceEntries = this.operations.enqueue(this.operationKey(source), async () => {\n\t\t\tif (!getFileSystemResultOrThrow(await this.fs.exists(source.path), `Failed to check session ${source.path}`)) {\n\t\t\t\tthrow new SessionError(\"not_found\", `Session not found: ${source.path}`);\n\t\t\t}\n\t\t\tconst document = await loadJsonlSession(this.fs, source.path);\n\t\t\tthis.entriesByPath.set(source.path, document.entries);\n\t\t\tthis.entryIdsByPath.set(source.path, new Set(document.entries.map((entry) => entry.id)));\n\t\t\treturn readSessionEntriesForFork(\n\t\t\t\tcreateArraySessionReader(document.metadata, () => document.entries),\n\t\t\t\tselection,\n\t\t\t);\n\t\t});\n\t\treturn this.operations.enqueue(descriptor.operationKey, async () =>\n\t\t\tthis.createDocument(\n\t\t\t\tdescriptor,\n\t\t\t\toptions,\n\t\t\t\toptions.parentSessionPath ?? source.path,\n\t\t\t\toptions.metadata ?? source.metadata,\n\t\t\t\tawait sourceEntries,\n\t\t\t),\n\t\t);\n\t}\n\n\tasync [Symbol.asyncDispose](): Promise<void> {\n\t\tif (!this.disposePromise) {\n\t\t\tthis.disposed = true;\n\t\t\tthis.disposePromise = this.operations.drain();\n\t\t}\n\t\tawait this.disposePromise;\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.disposed) throw new SessionError(\"storage\", \"JSONL session store is disposed\");\n\t}\n\n\tprivate operationKey(metadata: JsonlSessionMetadata): string {\n\t\treturn this.operationKeysByPath.get(metadata.path) ?? metadata.path;\n\t}\n\n\tprivate async createDocument(\n\t\tdescriptor: SessionDocumentDescriptor,\n\t\toptions: JsonlSessionCreateOptions,\n\t\tparentSessionPath: string | undefined,\n\t\tmetadata: Record<string, unknown> | undefined,\n\t\tentries: readonly SessionTreeEntry[],\n\t): Promise<SessionReader<JsonlSessionMetadata>> {\n\t\tconst dir = await this.getSessionDir(options.cwd);\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.fs.createDir(dir, { recursive: true }),\n\t\t\t`Failed to create session directory ${dir}`,\n\t\t);\n\t\tconst path = getFileSystemResultOrThrow(\n\t\t\tawait this.fs.joinPath([dir, descriptor.fileName]),\n\t\t\t`Failed to resolve session file path for ${descriptor.id}`,\n\t\t);\n\t\tif (getFileSystemResultOrThrow(await this.fs.exists(path), `Failed to check session ${path}`)) {\n\t\t\tthrow new SessionError(\"invalid_session\", `Session already exists: ${path}`);\n\t\t}\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: 3,\n\t\t\tid: descriptor.id,\n\t\t\ttimestamp: descriptor.timestamp,\n\t\t\tcwd: options.cwd,\n\t\t\tparentSession: parentSessionPath,\n\t\t\tmetadata,\n\t\t};\n\t\tconst content = [JSON.stringify(header), ...entries.map((entry) => JSON.stringify(entry)), \"\"].join(\"\\n\");\n\t\tgetFileSystemResultOrThrow(await this.fs.writeFile(path, content), `Failed to create session ${path}`);\n\t\tconst storedEntries = [...entries];\n\t\tthis.entriesByPath.set(path, storedEntries);\n\t\tthis.entryIdsByPath.set(path, new Set(storedEntries.map((entry) => entry.id)));\n\t\tthis.operationKeysByPath.set(path, descriptor.operationKey);\n\t\treturn this.reader(metadataFromHeader(header, path));\n\t}\n\n\tprivate reader(metadata: JsonlSessionMetadata): SessionReader<JsonlSessionMetadata> {\n\t\tconst reader = createArraySessionReader(metadata, () => {\n\t\t\tconst entries = this.entriesByPath.get(metadata.path);\n\t\t\tif (!entries) throw new SessionError(\"not_found\", `Session not found: ${metadata.path}`);\n\t\t\treturn entries;\n\t\t});\n\t\tconst operationKey = this.operationKey(metadata);\n\t\treturn {\n\t\t\tmetadata: reader.metadata,\n\t\t\treadHead: () => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(operationKey, () => reader.readHead());\n\t\t\t},\n\t\t\treadEntry: (id) => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(operationKey, () => reader.readEntry(id));\n\t\t\t},\n\t\t\treadEntries: (options) => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(operationKey, () => reader.readEntries(options));\n\t\t\t},\n\t\t\treadPathToRootOrCompaction: (leafId) => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(operationKey, () => reader.readPathToRootOrCompaction(leafId));\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async getSessionsRoot(): Promise<string> {\n\t\tthis.sessionsRoot ??= getFileSystemResultOrThrow(\n\t\t\tawait this.fs.absolutePath(this.sessionsRootInput),\n\t\t\t`Failed to resolve sessions root ${this.sessionsRootInput}`,\n\t\t);\n\t\treturn this.sessionsRoot;\n\t}\n\n\tprivate async getSessionDir(cwd: string): Promise<string> {\n\t\treturn getFileSystemResultOrThrow(\n\t\t\tawait this.fs.joinPath([await this.getSessionsRoot(), encodeCwd(cwd)]),\n\t\t\t`Failed to resolve session directory for ${cwd}`,\n\t\t);\n\t}\n\n\tprivate async listSessionDirs(): Promise<string[]> {\n\t\tconst root = await this.getSessionsRoot();\n\t\tif (!getFileSystemResultOrThrow(await this.fs.exists(root), `Failed to check sessions root ${root}`)) return [];\n\t\treturn getFileSystemResultOrThrow(await this.fs.listDir(root), `Failed to list sessions root ${root}`)\n\t\t\t.filter((entry) => entry.kind === \"directory\")\n\t\t\t.map((entry) => entry.path);\n\t}\n}\n\nexport function createJsonlSessionStore(\n\toptions: JsonlSessionStoreOptions,\n): SessionStore<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions> {\n\treturn new JsonlSessionStore(options);\n}\n"]}
@@ -0,0 +1,17 @@
1
+ export declare class KeyedOperationQueue<TKey> {
2
+ private readonly tails;
3
+ private readonly maxConcurrentOperations;
4
+ private readonly permitWaiters;
5
+ private activeOperations;
6
+ private barrier;
7
+ constructor(options?: {
8
+ maxConcurrentOperations?: number;
9
+ });
10
+ enqueue<T>(key: TKey, operation: () => Promise<T> | T): Promise<T>;
11
+ enqueueBarrier<T>(operation: () => Promise<T> | T): Promise<T>;
12
+ drain(): Promise<void>;
13
+ private runOperation;
14
+ private acquirePermit;
15
+ private releasePermit;
16
+ }
17
+ //# sourceMappingURL=keyed-operation-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyed-operation-queue.d.ts","sourceRoot":"","sources":["../../../src/harness/session/keyed-operation-queue.ts"],"names":[],"mappings":"AAAA,qBAAa,mBAAmB,CAAC,IAAI;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAqB;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,OAAO,CAAoC;IAEnD,YAAY,OAAO,GAAE;QAAE,uBAAuB,CAAC,EAAE,MAAM,CAAA;KAAO,EAQ7D;IAED,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAYjE;IAED,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO7D;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;YAEa,YAAY;YASZ,aAAa;IAS3B,OAAO,CAAC,aAAa;CAMrB"}
@@ -0,0 +1,60 @@
1
+ export class KeyedOperationQueue {
2
+ constructor(options = {}) {
3
+ this.tails = new Map();
4
+ this.permitWaiters = [];
5
+ this.activeOperations = 0;
6
+ this.barrier = Promise.resolve();
7
+ if (options.maxConcurrentOperations !== undefined &&
8
+ (!Number.isInteger(options.maxConcurrentOperations) || options.maxConcurrentOperations < 1)) {
9
+ throw new RangeError("maxConcurrentOperations must be a positive integer");
10
+ }
11
+ this.maxConcurrentOperations = options.maxConcurrentOperations;
12
+ }
13
+ enqueue(key, operation) {
14
+ const previous = this.tails.get(key) ?? Promise.resolve();
15
+ const result = Promise.all([this.barrier, previous]).then(() => this.runOperation(operation));
16
+ const tail = result.then(() => undefined, () => undefined);
17
+ this.tails.set(key, tail);
18
+ void tail.then(() => {
19
+ if (this.tails.get(key) === tail)
20
+ this.tails.delete(key);
21
+ });
22
+ return result;
23
+ }
24
+ enqueueBarrier(operation) {
25
+ const result = Promise.all([this.barrier, ...this.tails.values()]).then(() => this.runOperation(operation));
26
+ this.barrier = result.then(() => undefined, () => undefined);
27
+ return result;
28
+ }
29
+ async drain() {
30
+ await Promise.all([this.barrier, ...this.tails.values()]);
31
+ }
32
+ async runOperation(operation) {
33
+ await this.acquirePermit();
34
+ try {
35
+ return await operation();
36
+ }
37
+ finally {
38
+ this.releasePermit();
39
+ }
40
+ }
41
+ async acquirePermit() {
42
+ if (this.maxConcurrentOperations === undefined)
43
+ return;
44
+ if (this.activeOperations < this.maxConcurrentOperations) {
45
+ this.activeOperations += 1;
46
+ return;
47
+ }
48
+ await new Promise((resolve) => this.permitWaiters.push(resolve));
49
+ }
50
+ releasePermit() {
51
+ if (this.maxConcurrentOperations === undefined)
52
+ return;
53
+ const next = this.permitWaiters.shift();
54
+ if (next)
55
+ next();
56
+ else
57
+ this.activeOperations -= 1;
58
+ }
59
+ }
60
+ //# sourceMappingURL=keyed-operation-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyed-operation-queue.js","sourceRoot":"","sources":["../../../src/harness/session/keyed-operation-queue.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,mBAAmB;IAO/B,YAAY,OAAO,GAAyC,EAAE;QAN7C,UAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;QAEvC,kBAAa,GAAsB,EAAE,CAAC;QAC/C,qBAAgB,GAAG,CAAC,CAAC;QACrB,YAAO,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;QAGlD,IACC,OAAO,CAAC,uBAAuB,KAAK,SAAS;YAC7C,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,uBAAuB,CAAC,IAAI,OAAO,CAAC,uBAAuB,GAAG,CAAC,CAAC,EAC1F,CAAC;YACF,MAAM,IAAI,UAAU,CAAC,oDAAoD,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAChE,CAAC;IAED,OAAO,CAAI,GAAS,EAAE,SAA+B;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CACvB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;YACnB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IACf,CAAC;IAED,cAAc,CAAI,SAA+B;QAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;QAC5G,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CACzB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;QACF,OAAO,MAAM,CAAC;IACf,CAAC;IAED,KAAK,CAAC,KAAK;QACV,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;IAEO,KAAK,CAAC,YAAY,CAAI,SAA+B;QAC5D,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3B,IAAI,CAAC;YACJ,OAAO,MAAM,SAAS,EAAE,CAAC;QAC1B,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,aAAa,EAAE,CAAC;QACtB,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,aAAa;QAC1B,IAAI,IAAI,CAAC,uBAAuB,KAAK,SAAS;YAAE,OAAO;QACvD,IAAI,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC1D,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC;YAC3B,OAAO;QACR,CAAC;QACD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACxE,CAAC;IAEO,aAAa;QACpB,IAAI,IAAI,CAAC,uBAAuB,KAAK,SAAS;YAAE,OAAO;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACxC,IAAI,IAAI;YAAE,IAAI,EAAE,CAAC;;YACZ,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC;IACjC,CAAC;CACD","sourcesContent":["export class KeyedOperationQueue<TKey> {\n\tprivate readonly tails = new Map<TKey, Promise<void>>();\n\tprivate readonly maxConcurrentOperations: number | undefined;\n\tprivate readonly permitWaiters: Array<() => void> = [];\n\tprivate activeOperations = 0;\n\tprivate barrier: Promise<void> = Promise.resolve();\n\n\tconstructor(options: { maxConcurrentOperations?: number } = {}) {\n\t\tif (\n\t\t\toptions.maxConcurrentOperations !== undefined &&\n\t\t\t(!Number.isInteger(options.maxConcurrentOperations) || options.maxConcurrentOperations < 1)\n\t\t) {\n\t\t\tthrow new RangeError(\"maxConcurrentOperations must be a positive integer\");\n\t\t}\n\t\tthis.maxConcurrentOperations = options.maxConcurrentOperations;\n\t}\n\n\tenqueue<T>(key: TKey, operation: () => Promise<T> | T): Promise<T> {\n\t\tconst previous = this.tails.get(key) ?? Promise.resolve();\n\t\tconst result = Promise.all([this.barrier, previous]).then(() => this.runOperation(operation));\n\t\tconst tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\tthis.tails.set(key, tail);\n\t\tvoid tail.then(() => {\n\t\t\tif (this.tails.get(key) === tail) this.tails.delete(key);\n\t\t});\n\t\treturn result;\n\t}\n\n\tenqueueBarrier<T>(operation: () => Promise<T> | T): Promise<T> {\n\t\tconst result = Promise.all([this.barrier, ...this.tails.values()]).then(() => this.runOperation(operation));\n\t\tthis.barrier = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tasync drain(): Promise<void> {\n\t\tawait Promise.all([this.barrier, ...this.tails.values()]);\n\t}\n\n\tprivate async runOperation<T>(operation: () => Promise<T> | T): Promise<T> {\n\t\tawait this.acquirePermit();\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} finally {\n\t\t\tthis.releasePermit();\n\t\t}\n\t}\n\n\tprivate async acquirePermit(): Promise<void> {\n\t\tif (this.maxConcurrentOperations === undefined) return;\n\t\tif (this.activeOperations < this.maxConcurrentOperations) {\n\t\t\tthis.activeOperations += 1;\n\t\t\treturn;\n\t\t}\n\t\tawait new Promise<void>((resolve) => this.permitWaiters.push(resolve));\n\t}\n\n\tprivate releasePermit(): void {\n\t\tif (this.maxConcurrentOperations === undefined) return;\n\t\tconst next = this.permitWaiters.shift();\n\t\tif (next) next();\n\t\telse this.activeOperations -= 1;\n\t}\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { SessionMetadata, SessionStore } from "../types.ts";
2
+ export type InMemorySessionCreateOptions = {
3
+ id?: string;
4
+ };
5
+ export declare function createInMemorySessionStore(): SessionStore<SessionMetadata, InMemorySessionCreateOptions, void>;
6
+ //# sourceMappingURL=memory-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/harness/session/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,eAAe,EAAiB,YAAY,EAAoB,MAAM,aAAa,CAAC;AAOxH,MAAM,MAAM,4BAA4B,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAwH3D,wBAAgB,0BAA0B,IAAI,YAAY,CAAC,eAAe,EAAE,4BAA4B,EAAE,IAAI,CAAC,CAE9G"}
@@ -0,0 +1,107 @@
1
+ import { SessionError } from "../types.js";
2
+ import { createArraySessionReader } from "./array-session-reader.js";
3
+ import { readSessionEntriesForFork } from "./fork.js";
4
+ import { KeyedOperationQueue } from "./keyed-operation-queue.js";
5
+ import { createSessionId, createTimestamp } from "./repository.js";
6
+ class InMemorySessionStore {
7
+ constructor() {
8
+ this.sessions = new Map();
9
+ this.operations = new KeyedOperationQueue();
10
+ this.disposed = false;
11
+ }
12
+ create(options = {}) {
13
+ this.assertOpen();
14
+ const id = options.id ?? createSessionId();
15
+ return this.operations.enqueue(id, () => {
16
+ const state = {
17
+ metadata: { id, createdAt: createTimestamp() },
18
+ entries: [],
19
+ };
20
+ this.sessions.set(state.metadata.id, state);
21
+ return this.reader(state);
22
+ });
23
+ }
24
+ load(metadata) {
25
+ this.assertOpen();
26
+ return this.operations.enqueue(metadata.id, () => this.reader(this.getState(metadata)));
27
+ }
28
+ list() {
29
+ this.assertOpen();
30
+ return this.operations.enqueueBarrier(() => [...this.sessions.values()].map((state) => state.metadata));
31
+ }
32
+ appendEntry(metadata, entry) {
33
+ this.assertOpen();
34
+ return this.operations.enqueue(metadata.id, () => {
35
+ const state = this.getState(metadata);
36
+ if (state.entries.some((existing) => existing.id === entry.id)) {
37
+ throw new SessionError("invalid_entry", `Entry ${entry.id} already exists`);
38
+ }
39
+ state.entries.push(entry);
40
+ });
41
+ }
42
+ delete(metadata) {
43
+ this.assertOpen();
44
+ return this.operations.enqueue(metadata.id, () => {
45
+ this.sessions.delete(metadata.id);
46
+ });
47
+ }
48
+ fork(source, options, selection) {
49
+ this.assertOpen();
50
+ const id = options.id ?? createSessionId();
51
+ const sourceEntries = this.operations.enqueue(source.id, () => {
52
+ const sourceState = this.getState(source);
53
+ return readSessionEntriesForFork(createArraySessionReader(sourceState.metadata, () => sourceState.entries), selection);
54
+ });
55
+ return this.operations.enqueue(id, async () => {
56
+ const state = {
57
+ metadata: { id, createdAt: createTimestamp() },
58
+ entries: [...(await sourceEntries)],
59
+ };
60
+ this.sessions.set(state.metadata.id, state);
61
+ return this.reader(state);
62
+ });
63
+ }
64
+ async [Symbol.asyncDispose]() {
65
+ if (!this.disposePromise) {
66
+ this.disposed = true;
67
+ this.disposePromise = this.operations.drain();
68
+ }
69
+ await this.disposePromise;
70
+ }
71
+ assertOpen() {
72
+ if (this.disposed)
73
+ throw new SessionError("storage", "In-memory session store is disposed");
74
+ }
75
+ getState(metadata) {
76
+ const state = this.sessions.get(metadata.id);
77
+ if (!state)
78
+ throw new SessionError("not_found", `Session not found: ${metadata.id}`);
79
+ return state;
80
+ }
81
+ reader(state) {
82
+ const reader = createArraySessionReader(state.metadata, () => state.entries);
83
+ return {
84
+ metadata: reader.metadata,
85
+ readHead: () => {
86
+ this.assertOpen();
87
+ return this.operations.enqueue(state.metadata.id, () => reader.readHead());
88
+ },
89
+ readEntry: (id) => {
90
+ this.assertOpen();
91
+ return this.operations.enqueue(state.metadata.id, () => reader.readEntry(id));
92
+ },
93
+ readEntries: (options) => {
94
+ this.assertOpen();
95
+ return this.operations.enqueue(state.metadata.id, () => reader.readEntries(options));
96
+ },
97
+ readPathToRootOrCompaction: (leafId) => {
98
+ this.assertOpen();
99
+ return this.operations.enqueue(state.metadata.id, () => reader.readPathToRootOrCompaction(leafId));
100
+ },
101
+ };
102
+ }
103
+ }
104
+ export function createInMemorySessionStore() {
105
+ return new InMemorySessionStore();
106
+ }
107
+ //# sourceMappingURL=memory-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/harness/session/memory-store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionForkSelection, SessionMetadata, SessionReader, SessionStore, SessionTreeEntry } from \"../types.ts\";\nimport { SessionError } from \"../types.ts\";\nimport { createArraySessionReader } from \"./array-session-reader.ts\";\nimport { readSessionEntriesForFork } from \"./fork.ts\";\nimport { KeyedOperationQueue } from \"./keyed-operation-queue.ts\";\nimport { createSessionId, createTimestamp } from \"./repository.ts\";\n\nexport type InMemorySessionCreateOptions = { id?: string };\n\ninterface InMemorySessionState {\n\tmetadata: SessionMetadata;\n\tentries: SessionTreeEntry[];\n}\n\nclass InMemorySessionStore implements SessionStore<SessionMetadata, InMemorySessionCreateOptions, void> {\n\tprivate readonly sessions = new Map<string, InMemorySessionState>();\n\tprivate readonly operations = new KeyedOperationQueue<string>();\n\tprivate disposed = false;\n\tprivate disposePromise: Promise<void> | undefined;\n\n\tcreate(options: InMemorySessionCreateOptions = {}): Promise<SessionReader<SessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst id = options.id ?? createSessionId();\n\t\treturn this.operations.enqueue(id, () => {\n\t\t\tconst state: InMemorySessionState = {\n\t\t\t\tmetadata: { id, createdAt: createTimestamp() },\n\t\t\t\tentries: [],\n\t\t\t};\n\t\t\tthis.sessions.set(state.metadata.id, state);\n\t\t\treturn this.reader(state);\n\t\t});\n\t}\n\n\tload(metadata: SessionMetadata): Promise<SessionReader<SessionMetadata>> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueue(metadata.id, () => this.reader(this.getState(metadata)));\n\t}\n\n\tlist(): Promise<SessionMetadata[]> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueueBarrier(() => [...this.sessions.values()].map((state) => state.metadata));\n\t}\n\n\tappendEntry(metadata: SessionMetadata, entry: SessionTreeEntry): Promise<void> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueue(metadata.id, () => {\n\t\t\tconst state = this.getState(metadata);\n\t\t\tif (state.entries.some((existing) => existing.id === entry.id)) {\n\t\t\t\tthrow new SessionError(\"invalid_entry\", `Entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tstate.entries.push(entry);\n\t\t});\n\t}\n\n\tdelete(metadata: SessionMetadata): Promise<void> {\n\t\tthis.assertOpen();\n\t\treturn this.operations.enqueue(metadata.id, () => {\n\t\t\tthis.sessions.delete(metadata.id);\n\t\t});\n\t}\n\n\tfork(\n\t\tsource: SessionMetadata,\n\t\toptions: InMemorySessionCreateOptions,\n\t\tselection: SessionForkSelection,\n\t): Promise<SessionReader<SessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst id = options.id ?? createSessionId();\n\t\tconst sourceEntries = this.operations.enqueue(source.id, () => {\n\t\t\tconst sourceState = this.getState(source);\n\t\t\treturn readSessionEntriesForFork(\n\t\t\t\tcreateArraySessionReader(sourceState.metadata, () => sourceState.entries),\n\t\t\t\tselection,\n\t\t\t);\n\t\t});\n\t\treturn this.operations.enqueue(id, async () => {\n\t\t\tconst state: InMemorySessionState = {\n\t\t\t\tmetadata: { id, createdAt: createTimestamp() },\n\t\t\t\tentries: [...(await sourceEntries)],\n\t\t\t};\n\t\t\tthis.sessions.set(state.metadata.id, state);\n\t\t\treturn this.reader(state);\n\t\t});\n\t}\n\n\tasync [Symbol.asyncDispose](): Promise<void> {\n\t\tif (!this.disposePromise) {\n\t\t\tthis.disposed = true;\n\t\t\tthis.disposePromise = this.operations.drain();\n\t\t}\n\t\tawait this.disposePromise;\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.disposed) throw new SessionError(\"storage\", \"In-memory session store is disposed\");\n\t}\n\n\tprivate getState(metadata: SessionMetadata): InMemorySessionState {\n\t\tconst state = this.sessions.get(metadata.id);\n\t\tif (!state) throw new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t\treturn state;\n\t}\n\n\tprivate reader(state: InMemorySessionState): SessionReader<SessionMetadata> {\n\t\tconst reader = createArraySessionReader(state.metadata, () => state.entries);\n\t\treturn {\n\t\t\tmetadata: reader.metadata,\n\t\t\treadHead: () => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(state.metadata.id, () => reader.readHead());\n\t\t\t},\n\t\t\treadEntry: (id) => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(state.metadata.id, () => reader.readEntry(id));\n\t\t\t},\n\t\t\treadEntries: (options) => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(state.metadata.id, () => reader.readEntries(options));\n\t\t\t},\n\t\t\treadPathToRootOrCompaction: (leafId) => {\n\t\t\t\tthis.assertOpen();\n\t\t\t\treturn this.operations.enqueue(state.metadata.id, () => reader.readPathToRootOrCompaction(leafId));\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport function createInMemorySessionStore(): SessionStore<SessionMetadata, InMemorySessionCreateOptions, void> {\n\treturn new InMemorySessionStore();\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { type FileError, type Result, type SessionCreateOptions, type SessionForkOptions, type SessionMetadata, type SessionSearch, type SessionSearchHit, type SessionStore, type SessionTreeEntry } from "../types.ts";
2
+ import { type Session, type SessionContextBuildOptions } from "./session.ts";
3
+ export declare function createSessionId(): string;
4
+ export declare function createTimestamp(): string;
5
+ export declare class SessionRepository<TMetadata extends SessionMetadata = SessionMetadata, TCreateOptions extends SessionCreateOptions = SessionCreateOptions, TListOptions = void> {
6
+ private readonly store;
7
+ private readonly searchStore;
8
+ private readonly contextBuildOptions;
9
+ constructor(options: {
10
+ store: SessionStore<TMetadata, TCreateOptions, TListOptions>;
11
+ search?: SessionSearch<TMetadata> | null;
12
+ contextBuildOptions?: SessionContextBuildOptions;
13
+ });
14
+ create(options: TCreateOptions): Promise<Session<TMetadata>>;
15
+ open(metadata: TMetadata): Promise<Session<TMetadata>>;
16
+ list(options?: TListOptions): Promise<TMetadata[]>;
17
+ delete(metadata: TMetadata): Promise<void>;
18
+ fork(source: TMetadata, options: SessionForkOptions & TCreateOptions): Promise<Session<TMetadata>>;
19
+ search(options: Parameters<SessionSearch<TMetadata>["search"]>[0]): Promise<SessionSearchHit<TMetadata>[]>;
20
+ }
21
+ export declare function createSessionRepository<TMetadata extends SessionMetadata = SessionMetadata, TCreateOptions extends SessionCreateOptions = SessionCreateOptions, TListOptions = void>(options: {
22
+ store: SessionStore<TMetadata, TCreateOptions, TListOptions>;
23
+ search?: SessionSearch<TMetadata> | null;
24
+ contextBuildOptions?: SessionContextBuildOptions;
25
+ }): SessionRepository<TMetadata, TCreateOptions, TListOptions>;
26
+ export declare function findSessionEntryMatches<TMetadata extends SessionMetadata>(metadata: TMetadata, entries: readonly SessionTreeEntry[], text: string): SessionSearchHit<TMetadata>[];
27
+ export declare function getFileSystemResultOrThrow<TValue>(result: Result<TValue, FileError>, message: string): TValue;
28
+ //# sourceMappingURL=repository.d.ts.map