@draht/agent-core 2026.7.7-1 → 2026.7.11

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.
@@ -61,6 +61,10 @@ function parseHeaderLine(line, filePath) {
61
61
  if (header.parentSession !== undefined && typeof header.parentSession !== "string") {
62
62
  throw invalidSession(filePath, "session header parentSession must be a string");
63
63
  }
64
+ if (header.metadata !== undefined &&
65
+ (typeof header.metadata !== "object" || header.metadata === null || Array.isArray(header.metadata))) {
66
+ throw invalidSession(filePath, "session header metadata must be an object");
67
+ }
64
68
  return {
65
69
  type: "session",
66
70
  version: 3,
@@ -68,6 +72,7 @@ function parseHeaderLine(line, filePath) {
68
72
  timestamp: header.timestamp,
69
73
  cwd: header.cwd,
70
74
  parentSession: header.parentSession,
75
+ metadata: header.metadata,
71
76
  };
72
77
  }
73
78
  function parseEntryLine(line, filePath, lineNumber) {
@@ -107,6 +112,7 @@ function headerToSessionMetadata(header, path) {
107
112
  cwd: header.cwd,
108
113
  path,
109
114
  parentSessionPath: header.parentSession,
115
+ metadata: header.metadata,
110
116
  };
111
117
  }
112
118
  export async function loadJsonlSessionMetadata(fs, filePath) {
@@ -161,6 +167,7 @@ export class JsonlSessionStorage {
161
167
  timestamp: new Date().toISOString(),
162
168
  cwd: options.cwd,
163
169
  parentSession: options.parentSessionPath,
170
+ metadata: options.metadata,
164
171
  };
165
172
  getFileSystemResultOrThrow(await fs.writeFile(filePath, `${JSON.stringify(header)}\n`), `Failed to create session ${filePath}`);
166
173
  return new JsonlSessionStorage(fs, filePath, header, [], null);
@@ -1 +1 @@
1
- {"version":3,"file":"jsonl-storage.js","sourceRoot":"","sources":["../../../src/harness/session/jsonl-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FileSystem, JsonlSessionMetadata, LeafEntry, SessionStorage, SessionTreeEntry } from \"../types.ts\";\nimport { SessionError, toError } from \"../types.ts\";\nimport { getFileSystemResultOrThrow } from \"./repo-utils.ts\";\nimport { uuidv7 } from \"./uuid.ts\";\n\ntype JsonlSessionStorageFileSystem = Pick<FileSystem, \"readTextFile\" | \"readTextLines\" | \"writeFile\" | \"appendFile\">;\n\ninterface SessionHeader {\n\ttype: \"session\";\n\tversion: 3;\n\tid: string;\n\ttimestamp: string;\n\tcwd: string;\n\tparentSession?: string;\n}\n\nfunction updateLabelCache(labelsById: Map<string, string>, entry: SessionTreeEntry): void {\n\tif (entry.type !== \"label\") return;\n\tconst label = entry.label?.trim();\n\tif (label) {\n\t\tlabelsById.set(entry.targetId, label);\n\t} else {\n\t\tlabelsById.delete(entry.targetId);\n\t}\n}\n\nfunction buildLabelsById(entries: SessionTreeEntry[]): Map<string, string> {\n\tconst labelsById = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tupdateLabelCache(labelsById, entry);\n\t}\n\treturn labelsById;\n}\n\nfunction generateEntryId(byId: { has(id: string): boolean }): string {\n\tfor (let i = 0; i < 100; i++) {\n\t\t// The uuidv7 prefix is timestamp-derived and nearly constant between calls,\n\t\t// so short ids must come from the random tail.\n\t\tconst id = uuidv7().slice(-8);\n\t\tif (!byId.has(id)) return id;\n\t}\n\treturn uuidv7();\n}\n\nfunction invalidSession(filePath: string, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\"invalid_session\", `Invalid JSONL session file ${filePath}: ${message}`, cause);\n}\n\nfunction invalidEntry(filePath: string, lineNumber: number, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\n\t\t\"invalid_entry\",\n\t\t`Invalid JSONL session file ${filePath}: line ${lineNumber} ${message}`,\n\t\tcause,\n\t);\n}\n\nfunction parseHeaderLine(line: string, filePath: string): SessionHeader {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidSession(filePath, \"first line is not a valid session header\", toError(error));\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null) {\n\t\tthrow invalidSession(filePath, \"first line is not a valid session header\");\n\t}\n\tconst header = parsed as Partial<SessionHeader>;\n\tif (header.type !== \"session\") throw invalidSession(filePath, \"first line is not a valid session header\");\n\tif (header.version !== 3) throw invalidSession(filePath, \"unsupported session version\");\n\tif (typeof header.id !== \"string\" || !header.id) throw invalidSession(filePath, \"session header is missing id\");\n\tif (typeof header.timestamp !== \"string\" || !header.timestamp) {\n\t\tthrow invalidSession(filePath, \"session header is missing timestamp\");\n\t}\n\tif (typeof header.cwd !== \"string\" || !header.cwd) throw invalidSession(filePath, \"session header is missing cwd\");\n\tif (header.parentSession !== undefined && typeof header.parentSession !== \"string\") {\n\t\tthrow invalidSession(filePath, \"session header parentSession must be a string\");\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};\n}\n\nfunction parseEntryLine(line: string, filePath: string, lineNumber: number): SessionTreeEntry {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidEntry(filePath, lineNumber, \"is not valid JSON\", toError(error));\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null) {\n\t\tthrow invalidEntry(filePath, lineNumber, \"is not a valid session entry\");\n\t}\n\tconst entry = parsed as {\n\t\ttype?: unknown;\n\t\tid?: unknown;\n\t\tparentId?: unknown;\n\t\ttimestamp?: unknown;\n\t\ttargetId?: unknown;\n\t};\n\tif (typeof entry.type !== \"string\") throw invalidEntry(filePath, lineNumber, \"is missing entry type\");\n\tif (typeof entry.id !== \"string\" || !entry.id) throw invalidEntry(filePath, lineNumber, \"is missing entry id\");\n\tif (entry.parentId !== null && typeof entry.parentId !== \"string\") {\n\t\tthrow invalidEntry(filePath, lineNumber, \"has invalid parentId\");\n\t}\n\tif (typeof entry.timestamp !== \"string\" || !entry.timestamp) {\n\t\tthrow invalidEntry(filePath, lineNumber, \"is missing timestamp\");\n\t}\n\tif (entry.type === \"leaf\" && entry.targetId !== null && typeof entry.targetId !== \"string\") {\n\t\tthrow invalidEntry(filePath, lineNumber, \"has invalid targetId\");\n\t}\n\treturn entry as SessionTreeEntry;\n}\n\nfunction leafIdAfterEntry(entry: SessionTreeEntry): string | null {\n\treturn entry.type === \"leaf\" ? entry.targetId : entry.id;\n}\n\nfunction headerToSessionMetadata(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};\n}\n\nexport async function loadJsonlSessionMetadata(\n\tfs: JsonlSessionStorageFileSystem,\n\tfilePath: string,\n): Promise<JsonlSessionMetadata> {\n\tconst lines = getFileSystemResultOrThrow(\n\t\tawait fs.readTextLines(filePath, { maxLines: 1 }),\n\t\t`Failed to read session header ${filePath}`,\n\t);\n\tconst line = lines[0];\n\tif (line?.trim()) return headerToSessionMetadata(parseHeaderLine(line, filePath), filePath);\n\tthrow invalidSession(filePath, \"missing session header\");\n}\n\nasync function loadJsonlStorage(\n\tfs: JsonlSessionStorageFileSystem,\n\tfilePath: string,\n): Promise<{\n\theader: SessionHeader;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n}> {\n\tconst content = getFileSystemResultOrThrow(await fs.readTextFile(filePath), `Failed to read session ${filePath}`);\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim());\n\tif (lines.length === 0) {\n\t\tthrow invalidSession(filePath, \"missing session header\");\n\t}\n\n\tconst header = parseHeaderLine(lines[0]!, filePath);\n\tconst entries: SessionTreeEntry[] = [];\n\tlet leafId: string | null = null;\n\tfor (let i = 1; i < lines.length; i++) {\n\t\tconst entry = parseEntryLine(lines[i]!, filePath, i + 1);\n\t\tentries.push(entry);\n\t\tleafId = leafIdAfterEntry(entry);\n\t}\n\treturn { header, entries, leafId };\n}\n\nexport class JsonlSessionStorage implements SessionStorage<JsonlSessionMetadata> {\n\tprivate readonly fs: JsonlSessionStorageFileSystem;\n\tprivate readonly filePath: string;\n\tprivate readonly metadata: JsonlSessionMetadata;\n\tprivate entries: SessionTreeEntry[];\n\tprivate byId: Map<string, SessionTreeEntry>;\n\tprivate labelsById: Map<string, string>;\n\tprivate currentLeafId: string | null;\n\n\tprivate constructor(\n\t\tfs: JsonlSessionStorageFileSystem,\n\t\tfilePath: string,\n\t\theader: SessionHeader,\n\t\tentries: SessionTreeEntry[],\n\t\tleafId: string | null,\n\t) {\n\t\tthis.fs = fs;\n\t\tthis.filePath = filePath;\n\t\tthis.metadata = headerToSessionMetadata(header, this.filePath);\n\t\tthis.entries = entries;\n\t\tthis.byId = new Map(entries.map((entry) => [entry.id, entry]));\n\t\tthis.labelsById = buildLabelsById(entries);\n\t\tthis.currentLeafId = leafId;\n\t}\n\n\tstatic async open(fs: JsonlSessionStorageFileSystem, filePath: string): Promise<JsonlSessionStorage> {\n\t\tconst loaded = await loadJsonlStorage(fs, filePath);\n\t\treturn new JsonlSessionStorage(fs, filePath, loaded.header, loaded.entries, loaded.leafId);\n\t}\n\n\tstatic async create(\n\t\tfs: JsonlSessionStorageFileSystem,\n\t\tfilePath: string,\n\t\toptions: {\n\t\t\tcwd: string;\n\t\t\tsessionId: string;\n\t\t\tparentSessionPath?: string;\n\t\t},\n\t): Promise<JsonlSessionStorage> {\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: 3,\n\t\t\tid: options.sessionId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcwd: options.cwd,\n\t\t\tparentSession: options.parentSessionPath,\n\t\t};\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait fs.writeFile(filePath, `${JSON.stringify(header)}\\n`),\n\t\t\t`Failed to create session ${filePath}`,\n\t\t);\n\t\treturn new JsonlSessionStorage(fs, filePath, header, [], null);\n\t}\n\n\tasync getMetadata(): Promise<JsonlSessionMetadata> {\n\t\treturn this.metadata;\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\tif (this.currentLeafId !== null && !this.byId.has(this.currentLeafId)) {\n\t\t\tthrow new SessionError(\"invalid_session\", `Entry ${this.currentLeafId} not found`);\n\t\t}\n\t\treturn this.currentLeafId;\n\t}\n\n\tasync setLeafId(leafId: string | null): Promise<void> {\n\t\tif (leafId !== null && !this.byId.has(leafId)) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${leafId} not found`);\n\t\t}\n\t\tconst entry: LeafEntry = {\n\t\t\ttype: \"leaf\",\n\t\t\tid: generateEntryId(this.byId),\n\t\t\tparentId: this.currentLeafId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttargetId: leafId,\n\t\t};\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.fs.appendFile(this.filePath, `${JSON.stringify(entry)}\\n`),\n\t\t\t`Failed to append session leaf ${entry.id}`,\n\t\t);\n\t\tthis.entries.push(entry);\n\t\tthis.byId.set(entry.id, entry);\n\t\tthis.currentLeafId = leafId;\n\t}\n\n\tasync createEntryId(): Promise<string> {\n\t\treturn generateEntryId(this.byId);\n\t}\n\n\tasync appendEntry(entry: SessionTreeEntry): Promise<void> {\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.fs.appendFile(this.filePath, `${JSON.stringify(entry)}\\n`),\n\t\t\t`Failed to append session entry ${entry.id}`,\n\t\t);\n\t\tthis.entries.push(entry);\n\t\tthis.byId.set(entry.id, entry);\n\t\tupdateLabelCache(this.labelsById, entry);\n\t\tthis.currentLeafId = leafIdAfterEntry(entry);\n\t}\n\n\tasync getEntry(id: string): Promise<SessionTreeEntry | undefined> {\n\t\treturn this.byId.get(id);\n\t}\n\n\tasync findEntries<TType extends SessionTreeEntry[\"type\"]>(\n\t\ttype: TType,\n\t): Promise<Array<Extract<SessionTreeEntry, { type: TType }>>> {\n\t\treturn this.entries.filter((entry): entry is Extract<SessionTreeEntry, { type: TType }> => entry.type === type);\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.labelsById.get(id);\n\t}\n\n\tasync getPathToRoot(leafId: string | null): Promise<SessionTreeEntry[]> {\n\t\tif (leafId === null) return [];\n\t\tconst path: SessionTreeEntry[] = [];\n\t\tlet current = this.byId.get(leafId);\n\t\tif (!current) throw new SessionError(\"not_found\", `Entry ${leafId} not found`);\n\t\twhile (current) {\n\t\t\tpath.unshift(current);\n\t\t\tif (!current.parentId) break;\n\t\t\tconst parent = this.byId.get(current.parentId);\n\t\t\tif (!parent) throw new SessionError(\"invalid_session\", `Entry ${current.parentId} not found`);\n\t\t\tcurrent = parent;\n\t\t}\n\t\treturn path;\n\t}\n\n\tasync getEntries(): Promise<SessionTreeEntry[]> {\n\t\treturn [...this.entries];\n\t}\n}\n"]}
1
+ {"version":3,"file":"jsonl-storage.js","sourceRoot":"","sources":["../../../src/harness/session/jsonl-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FileSystem, JsonlSessionMetadata, LeafEntry, SessionStorage, SessionTreeEntry } from \"../types.ts\";\nimport { SessionError, toError } from \"../types.ts\";\nimport { getFileSystemResultOrThrow } from \"./repo-utils.ts\";\nimport { uuidv7 } from \"./uuid.ts\";\n\ntype JsonlSessionStorageFileSystem = Pick<FileSystem, \"readTextFile\" | \"readTextLines\" | \"writeFile\" | \"appendFile\">;\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\nfunction updateLabelCache(labelsById: Map<string, string>, entry: SessionTreeEntry): void {\n\tif (entry.type !== \"label\") return;\n\tconst label = entry.label?.trim();\n\tif (label) {\n\t\tlabelsById.set(entry.targetId, label);\n\t} else {\n\t\tlabelsById.delete(entry.targetId);\n\t}\n}\n\nfunction buildLabelsById(entries: SessionTreeEntry[]): Map<string, string> {\n\tconst labelsById = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tupdateLabelCache(labelsById, entry);\n\t}\n\treturn labelsById;\n}\n\nfunction generateEntryId(byId: { has(id: string): boolean }): string {\n\tfor (let i = 0; i < 100; i++) {\n\t\t// The uuidv7 prefix is timestamp-derived and nearly constant between calls,\n\t\t// so short ids must come from the random tail.\n\t\tconst id = uuidv7().slice(-8);\n\t\tif (!byId.has(id)) return id;\n\t}\n\treturn uuidv7();\n}\n\nfunction invalidSession(filePath: string, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\"invalid_session\", `Invalid JSONL session file ${filePath}: ${message}`, cause);\n}\n\nfunction invalidEntry(filePath: string, lineNumber: number, message: string, cause?: Error): SessionError {\n\treturn new SessionError(\n\t\t\"invalid_entry\",\n\t\t`Invalid JSONL session file ${filePath}: line ${lineNumber} ${message}`,\n\t\tcause,\n\t);\n}\n\nfunction parseHeaderLine(line: string, filePath: string): SessionHeader {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidSession(filePath, \"first line is not a valid session header\", toError(error));\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null) {\n\t\tthrow invalidSession(filePath, \"first line is not a valid session header\");\n\t}\n\tconst header = parsed as Partial<SessionHeader>;\n\tif (header.type !== \"session\") throw invalidSession(filePath, \"first line is not a valid session header\");\n\tif (header.version !== 3) throw invalidSession(filePath, \"unsupported session version\");\n\tif (typeof header.id !== \"string\" || !header.id) throw invalidSession(filePath, \"session header is missing id\");\n\tif (typeof header.timestamp !== \"string\" || !header.timestamp) {\n\t\tthrow invalidSession(filePath, \"session header is missing timestamp\");\n\t}\n\tif (typeof header.cwd !== \"string\" || !header.cwd) throw invalidSession(filePath, \"session header is missing cwd\");\n\tif (header.parentSession !== undefined && typeof header.parentSession !== \"string\") {\n\t\tthrow invalidSession(filePath, \"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(filePath, \"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 parseEntryLine(line: string, filePath: string, lineNumber: number): SessionTreeEntry {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow invalidEntry(filePath, lineNumber, \"is not valid JSON\", toError(error));\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null) {\n\t\tthrow invalidEntry(filePath, lineNumber, \"is not a valid session entry\");\n\t}\n\tconst entry = parsed as {\n\t\ttype?: unknown;\n\t\tid?: unknown;\n\t\tparentId?: unknown;\n\t\ttimestamp?: unknown;\n\t\ttargetId?: unknown;\n\t};\n\tif (typeof entry.type !== \"string\") throw invalidEntry(filePath, lineNumber, \"is missing entry type\");\n\tif (typeof entry.id !== \"string\" || !entry.id) throw invalidEntry(filePath, lineNumber, \"is missing entry id\");\n\tif (entry.parentId !== null && typeof entry.parentId !== \"string\") {\n\t\tthrow invalidEntry(filePath, lineNumber, \"has invalid parentId\");\n\t}\n\tif (typeof entry.timestamp !== \"string\" || !entry.timestamp) {\n\t\tthrow invalidEntry(filePath, lineNumber, \"is missing timestamp\");\n\t}\n\tif (entry.type === \"leaf\" && entry.targetId !== null && typeof entry.targetId !== \"string\") {\n\t\tthrow invalidEntry(filePath, lineNumber, \"has invalid targetId\");\n\t}\n\treturn entry as SessionTreeEntry;\n}\n\nfunction leafIdAfterEntry(entry: SessionTreeEntry): string | null {\n\treturn entry.type === \"leaf\" ? entry.targetId : entry.id;\n}\n\nfunction headerToSessionMetadata(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: JsonlSessionStorageFileSystem,\n\tfilePath: string,\n): Promise<JsonlSessionMetadata> {\n\tconst lines = getFileSystemResultOrThrow(\n\t\tawait fs.readTextLines(filePath, { maxLines: 1 }),\n\t\t`Failed to read session header ${filePath}`,\n\t);\n\tconst line = lines[0];\n\tif (line?.trim()) return headerToSessionMetadata(parseHeaderLine(line, filePath), filePath);\n\tthrow invalidSession(filePath, \"missing session header\");\n}\n\nasync function loadJsonlStorage(\n\tfs: JsonlSessionStorageFileSystem,\n\tfilePath: string,\n): Promise<{\n\theader: SessionHeader;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n}> {\n\tconst content = getFileSystemResultOrThrow(await fs.readTextFile(filePath), `Failed to read session ${filePath}`);\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim());\n\tif (lines.length === 0) {\n\t\tthrow invalidSession(filePath, \"missing session header\");\n\t}\n\n\tconst header = parseHeaderLine(lines[0]!, filePath);\n\tconst entries: SessionTreeEntry[] = [];\n\tlet leafId: string | null = null;\n\tfor (let i = 1; i < lines.length; i++) {\n\t\tconst entry = parseEntryLine(lines[i]!, filePath, i + 1);\n\t\tentries.push(entry);\n\t\tleafId = leafIdAfterEntry(entry);\n\t}\n\treturn { header, entries, leafId };\n}\n\nexport class JsonlSessionStorage implements SessionStorage<JsonlSessionMetadata> {\n\tprivate readonly fs: JsonlSessionStorageFileSystem;\n\tprivate readonly filePath: string;\n\tprivate readonly metadata: JsonlSessionMetadata;\n\tprivate entries: SessionTreeEntry[];\n\tprivate byId: Map<string, SessionTreeEntry>;\n\tprivate labelsById: Map<string, string>;\n\tprivate currentLeafId: string | null;\n\n\tprivate constructor(\n\t\tfs: JsonlSessionStorageFileSystem,\n\t\tfilePath: string,\n\t\theader: SessionHeader,\n\t\tentries: SessionTreeEntry[],\n\t\tleafId: string | null,\n\t) {\n\t\tthis.fs = fs;\n\t\tthis.filePath = filePath;\n\t\tthis.metadata = headerToSessionMetadata(header, this.filePath);\n\t\tthis.entries = entries;\n\t\tthis.byId = new Map(entries.map((entry) => [entry.id, entry]));\n\t\tthis.labelsById = buildLabelsById(entries);\n\t\tthis.currentLeafId = leafId;\n\t}\n\n\tstatic async open(fs: JsonlSessionStorageFileSystem, filePath: string): Promise<JsonlSessionStorage> {\n\t\tconst loaded = await loadJsonlStorage(fs, filePath);\n\t\treturn new JsonlSessionStorage(fs, filePath, loaded.header, loaded.entries, loaded.leafId);\n\t}\n\n\tstatic async create(\n\t\tfs: JsonlSessionStorageFileSystem,\n\t\tfilePath: string,\n\t\toptions: {\n\t\t\tcwd: string;\n\t\t\tsessionId: string;\n\t\t\tparentSessionPath?: string;\n\t\t\tmetadata?: Record<string, unknown>;\n\t\t},\n\t): Promise<JsonlSessionStorage> {\n\t\tconst header: SessionHeader = {\n\t\t\ttype: \"session\",\n\t\t\tversion: 3,\n\t\t\tid: options.sessionId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcwd: options.cwd,\n\t\t\tparentSession: options.parentSessionPath,\n\t\t\tmetadata: options.metadata,\n\t\t};\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait fs.writeFile(filePath, `${JSON.stringify(header)}\\n`),\n\t\t\t`Failed to create session ${filePath}`,\n\t\t);\n\t\treturn new JsonlSessionStorage(fs, filePath, header, [], null);\n\t}\n\n\tasync getMetadata(): Promise<JsonlSessionMetadata> {\n\t\treturn this.metadata;\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\tif (this.currentLeafId !== null && !this.byId.has(this.currentLeafId)) {\n\t\t\tthrow new SessionError(\"invalid_session\", `Entry ${this.currentLeafId} not found`);\n\t\t}\n\t\treturn this.currentLeafId;\n\t}\n\n\tasync setLeafId(leafId: string | null): Promise<void> {\n\t\tif (leafId !== null && !this.byId.has(leafId)) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${leafId} not found`);\n\t\t}\n\t\tconst entry: LeafEntry = {\n\t\t\ttype: \"leaf\",\n\t\t\tid: generateEntryId(this.byId),\n\t\t\tparentId: this.currentLeafId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttargetId: leafId,\n\t\t};\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.fs.appendFile(this.filePath, `${JSON.stringify(entry)}\\n`),\n\t\t\t`Failed to append session leaf ${entry.id}`,\n\t\t);\n\t\tthis.entries.push(entry);\n\t\tthis.byId.set(entry.id, entry);\n\t\tthis.currentLeafId = leafId;\n\t}\n\n\tasync createEntryId(): Promise<string> {\n\t\treturn generateEntryId(this.byId);\n\t}\n\n\tasync appendEntry(entry: SessionTreeEntry): Promise<void> {\n\t\tgetFileSystemResultOrThrow(\n\t\t\tawait this.fs.appendFile(this.filePath, `${JSON.stringify(entry)}\\n`),\n\t\t\t`Failed to append session entry ${entry.id}`,\n\t\t);\n\t\tthis.entries.push(entry);\n\t\tthis.byId.set(entry.id, entry);\n\t\tupdateLabelCache(this.labelsById, entry);\n\t\tthis.currentLeafId = leafIdAfterEntry(entry);\n\t}\n\n\tasync getEntry(id: string): Promise<SessionTreeEntry | undefined> {\n\t\treturn this.byId.get(id);\n\t}\n\n\tasync findEntries<TType extends SessionTreeEntry[\"type\"]>(\n\t\ttype: TType,\n\t): Promise<Array<Extract<SessionTreeEntry, { type: TType }>>> {\n\t\treturn this.entries.filter((entry): entry is Extract<SessionTreeEntry, { type: TType }> => entry.type === type);\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.labelsById.get(id);\n\t}\n\n\tasync getPathToRoot(leafId: string | null): Promise<SessionTreeEntry[]> {\n\t\tif (leafId === null) return [];\n\t\tconst path: SessionTreeEntry[] = [];\n\t\tlet current = this.byId.get(leafId);\n\t\tif (!current) throw new SessionError(\"not_found\", `Entry ${leafId} not found`);\n\t\twhile (current) {\n\t\t\tpath.unshift(current);\n\t\t\tif (!current.parentId) break;\n\t\t\tconst parent = this.byId.get(current.parentId);\n\t\t\tif (!parent) throw new SessionError(\"invalid_session\", `Entry ${current.parentId} not found`);\n\t\t\tcurrent = parent;\n\t\t}\n\t\treturn path;\n\t}\n\n\tasync getEntries(): Promise<SessionTreeEntry[]> {\n\t\treturn [...this.entries];\n\t}\n}\n"]}
@@ -1,17 +1,31 @@
1
1
  import type { ImageContent, TextContent } from "@draht/ai";
2
2
  import type { AgentMessage } from "../../types.js";
3
- import type { SessionContext, SessionMetadata, SessionStorage, SessionTreeEntry } from "../types.ts";
4
- export declare function buildSessionContext(pathEntries: SessionTreeEntry[]): SessionContext;
3
+ import type { CustomEntry, SessionContext, SessionMetadata, SessionStorage, SessionTreeEntry } from "../types.ts";
4
+ export type ContextEntryTransform = (entries: readonly SessionTreeEntry[]) => readonly SessionTreeEntry[];
5
+ export type CustomEntryContextMessageProjector = (entry: CustomEntry, index: number, entries: readonly SessionTreeEntry[]) => readonly AgentMessage[] | undefined;
6
+ export interface SessionContextBuildOptions {
7
+ /** Additional entry transforms applied after the default compaction transform. */
8
+ entryTransforms?: readonly ContextEntryTransform[];
9
+ /** Optional custom-entry projectors. Custom entries are omitted from model context by default. */
10
+ entryProjectors?: Readonly<Record<string, CustomEntryContextMessageProjector>>;
11
+ }
12
+ export declare function defaultContextEntryTransform(pathEntries: readonly SessionTreeEntry[]): SessionTreeEntry[];
13
+ export declare function buildContextEntries(pathEntries: readonly SessionTreeEntry[], options?: SessionContextBuildOptions): SessionTreeEntry[];
14
+ export declare function sessionEntryToContextMessages(entry: SessionTreeEntry, index: number, entries: readonly SessionTreeEntry[], options?: SessionContextBuildOptions): AgentMessage[];
15
+ export declare function buildSessionContext(pathEntries: readonly SessionTreeEntry[], options?: SessionContextBuildOptions): SessionContext;
5
16
  export declare class Session<TMetadata extends SessionMetadata = SessionMetadata> {
6
17
  private storage;
7
- constructor(storage: SessionStorage<TMetadata>);
18
+ private contextBuildOptions;
19
+ constructor(storage: SessionStorage<TMetadata>, contextBuildOptions?: SessionContextBuildOptions);
8
20
  getMetadata(): Promise<TMetadata>;
9
21
  getStorage(): SessionStorage<TMetadata>;
10
22
  getLeafId(): Promise<string | null>;
11
23
  getEntry(id: string): Promise<SessionTreeEntry | undefined>;
12
24
  getEntries(): Promise<SessionTreeEntry[]>;
13
25
  getBranch(fromId?: string): Promise<SessionTreeEntry[]>;
14
- buildContext(): Promise<SessionContext>;
26
+ buildContextEntries(options?: SessionContextBuildOptions): Promise<SessionTreeEntry[]>;
27
+ buildContext(options?: SessionContextBuildOptions): Promise<SessionContext>;
28
+ private mergeContextBuildOptions;
15
29
  getLabel(id: string): Promise<string | undefined>;
16
30
  getSessionName(): Promise<string | undefined>;
17
31
  private appendTypedEntry;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EASX,cAAc,EAEd,eAAe,EACf,cAAc,EACd,gBAAgB,EAEhB,MAAM,aAAa,CAAC;AAGrB,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,cAAc,CA0DnF;AAED,qBAAa,OAAO,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;IACvE,OAAO,CAAC,OAAO,CAA4B;IAE3C,YAAY,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,EAE7C;IAED,WAAW,IAAI,OAAO,CAAC,SAAS,CAAC,CAEhC;IAED,UAAU,IAAI,cAAc,CAAC,SAAS,CAAC,CAEtC;IAED,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAElC;IAED,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAE1D;IAED,UAAU,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAExC;IAEK,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAG5D;IAEK,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC,CAE5C;IAED,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAEhD;IAEK,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGlD;YAEa,gBAAgB;IAKxB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ1D;IAEK,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQtE;IAEK,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS1E;IAEK,uBAAuB,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxE;IAEK,gBAAgB,CAAC,CAAC,GAAG,OAAO,EACjC,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,CAAC,EACX,QAAQ,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,MAAM,CAAC,CAYjB;IAEK,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAS3E;IAEK,wBAAwB,CAAC,CAAC,GAAG,OAAO,EACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,EAChD,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,CAAC,GACT,OAAO,CAAC,MAAM,CAAC,CAWjB;IAEK,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAY9E;IAEK,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASrD;IAEK,MAAM,CACX,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAClE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB7B;CACD","sourcesContent":["import type { ImageContent, TextContent } from \"@draht/ai\";\nimport type { AgentMessage } from \"../../types.js\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.js\";\nimport type {\n\tActiveToolsChangeEntry,\n\tBranchSummaryEntry,\n\tCompactionEntry,\n\tCustomEntry,\n\tCustomMessageEntry,\n\tLabelEntry,\n\tMessageEntry,\n\tModelChangeEntry,\n\tSessionContext,\n\tSessionInfoEntry,\n\tSessionMetadata,\n\tSessionStorage,\n\tSessionTreeEntry,\n\tThinkingLevelChangeEntry,\n} from \"../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport function buildSessionContext(pathEntries: SessionTreeEntry[]): SessionContext {\n\tlet thinkingLevel = \"off\";\n\tlet model: { provider: string; modelId: string } | null = null;\n\tlet activeToolNames: string[] | null = null;\n\tlet compaction: CompactionEntry | null = null;\n\n\tfor (const entry of pathEntries) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t} else if (entry.type === \"active_tools_change\") {\n\t\t\tactiveToolNames = [...entry.activeToolNames];\n\t\t} else if (entry.type === \"compaction\") {\n\t\t\tcompaction = entry;\n\t\t}\n\t}\n\n\tconst messages: AgentMessage[] = [];\n\tconst appendMessage = (entry: SessionTreeEntry) => {\n\t\tif (entry.type === \"message\") {\n\t\t\tmessages.push(entry.message as AgentMessage);\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessages.push(\n\t\t\t\tcreateCustomMessage(\n\t\t\t\t\tentry.customType,\n\t\t\t\t\tentry.content as string | (TextContent | ImageContent)[],\n\t\t\t\t\tentry.display,\n\t\t\t\t\tentry.details,\n\t\t\t\t\tentry.timestamp,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessages.push(createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp));\n\t\t}\n\t};\n\n\tif (compaction) {\n\t\tmessages.push(createCompactionSummaryMessage(compaction.summary, compaction.tokensBefore, compaction.timestamp));\n\t\tconst compactionIdx = pathEntries.findIndex((e) => e.type === \"compaction\" && e.id === compaction.id);\n\t\tlet foundFirstKept = false;\n\t\tfor (let i = 0; i < compactionIdx; i++) {\n\t\t\tconst entry = pathEntries[i]!;\n\t\t\tif (entry.id === compaction.firstKeptEntryId) foundFirstKept = true;\n\t\t\tif (foundFirstKept) appendMessage(entry);\n\t\t}\n\t\tfor (let i = compactionIdx + 1; i < pathEntries.length; i++) {\n\t\t\tappendMessage(pathEntries[i]!);\n\t\t}\n\t} else {\n\t\tfor (const entry of pathEntries) {\n\t\t\tappendMessage(entry);\n\t\t}\n\t}\n\n\treturn { messages, thinkingLevel, model, activeToolNames };\n}\n\nexport class Session<TMetadata extends SessionMetadata = SessionMetadata> {\n\tprivate storage: SessionStorage<TMetadata>;\n\n\tconstructor(storage: SessionStorage<TMetadata>) {\n\t\tthis.storage = storage;\n\t}\n\n\tgetMetadata(): Promise<TMetadata> {\n\t\treturn this.storage.getMetadata();\n\t}\n\n\tgetStorage(): SessionStorage<TMetadata> {\n\t\treturn this.storage;\n\t}\n\n\tgetLeafId(): Promise<string | null> {\n\t\treturn this.storage.getLeafId();\n\t}\n\n\tgetEntry(id: string): Promise<SessionTreeEntry | undefined> {\n\t\treturn this.storage.getEntry(id);\n\t}\n\n\tgetEntries(): Promise<SessionTreeEntry[]> {\n\t\treturn this.storage.getEntries();\n\t}\n\n\tasync getBranch(fromId?: string): Promise<SessionTreeEntry[]> {\n\t\tconst leafId = fromId ?? (await this.storage.getLeafId());\n\t\treturn this.storage.getPathToRoot(leafId);\n\t}\n\n\tasync buildContext(): Promise<SessionContext> {\n\t\treturn buildSessionContext(await this.getBranch());\n\t}\n\n\tgetLabel(id: string): Promise<string | undefined> {\n\t\treturn this.storage.getLabel(id);\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\tconst entries = await this.storage.findEntries(\"session_info\");\n\t\treturn entries[entries.length - 1]?.name?.trim() || undefined;\n\t}\n\n\tprivate async appendTypedEntry<TEntry extends SessionTreeEntry>(entry: TEntry): Promise<string> {\n\t\tawait this.storage.appendEntry(entry);\n\t\treturn entry.id;\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tmessage,\n\t\t} satisfies MessageEntry);\n\t}\n\n\tasync appendThinkingLevelChange(thinkingLevel: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"thinking_level_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tthinkingLevel,\n\t\t} satisfies ThinkingLevelChangeEntry);\n\t}\n\n\tasync appendModelChange(provider: string, modelId: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"model_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t} satisfies ModelChangeEntry);\n\t}\n\n\tasync appendActiveToolsChange(activeToolNames: string[]): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"active_tools_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tactiveToolNames: [...activeToolNames],\n\t\t} satisfies ActiveToolsChangeEntry);\n\t}\n\n\tasync appendCompaction<T = unknown>(\n\t\tsummary: string,\n\t\tfirstKeptEntryId: string,\n\t\ttokensBefore: number,\n\t\tdetails?: T,\n\t\tfromHook?: boolean,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"compaction\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tsummary,\n\t\t\tfirstKeptEntryId,\n\t\t\ttokensBefore,\n\t\t\tdetails,\n\t\t\tfromHook,\n\t\t} satisfies CompactionEntry<T>);\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tdata,\n\t\t} satisfies CustomEntry);\n\t}\n\n\tasync appendCustomMessageEntry<T = unknown>(\n\t\tcustomType: string,\n\t\tcontent: string | (TextContent | ImageContent)[],\n\t\tdisplay: boolean,\n\t\tdetails?: T,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom_message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tcontent,\n\t\t\tdisplay,\n\t\t\tdetails,\n\t\t} satisfies CustomMessageEntry<T>);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<string> {\n\t\tif (!(await this.storage.getEntry(targetId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${targetId} not found`);\n\t\t}\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"label\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttargetId,\n\t\t\tlabel,\n\t\t} satisfies LabelEntry);\n\t}\n\n\tasync appendSessionName(name: string): Promise<string> {\n\t\tconst sanitizedName = name.replace(/[\\r\\n]+/g, \" \").trim();\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"session_info\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tname: sanitizedName,\n\t\t} satisfies SessionInfoEntry);\n\t}\n\n\tasync moveTo(\n\t\tentryId: string | null,\n\t\tsummary?: { summary: string; details?: unknown; fromHook?: boolean },\n\t): Promise<string | undefined> {\n\t\tif (entryId !== null && !(await this.storage.getEntry(entryId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${entryId} not found`);\n\t\t}\n\t\tawait this.storage.setLeafId(entryId);\n\t\tif (!summary) return undefined;\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"branch_summary\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: entryId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tfromId: entryId ?? \"root\",\n\t\t\tsummary: summary.summary,\n\t\t\tdetails: summary.details,\n\t\t\tfromHook: summary.fromHook,\n\t\t} satisfies BranchSummaryEntry);\n\t}\n}\n"]}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@draht/ai\";\nimport type { AgentMessage } from \"../../types.js\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.js\";\nimport type {\n\tActiveToolsChangeEntry,\n\tBranchSummaryEntry,\n\tCompactionEntry,\n\tCustomEntry,\n\tCustomMessageEntry,\n\tLabelEntry,\n\tMessageEntry,\n\tModelChangeEntry,\n\tSessionContext,\n\tSessionInfoEntry,\n\tSessionMetadata,\n\tSessionStorage,\n\tSessionTreeEntry,\n\tThinkingLevelChangeEntry,\n} from \"../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport type ContextEntryTransform = (entries: readonly SessionTreeEntry[]) => readonly SessionTreeEntry[];\n\nexport type CustomEntryContextMessageProjector = (\n\tentry: CustomEntry,\n\tindex: number,\n\tentries: readonly SessionTreeEntry[],\n) => readonly AgentMessage[] | undefined;\n\nexport interface SessionContextBuildOptions {\n\t/** Additional entry transforms applied after the default compaction transform. */\n\tentryTransforms?: readonly ContextEntryTransform[];\n\t/** Optional custom-entry projectors. Custom entries are omitted from model context by default. */\n\tentryProjectors?: Readonly<Record<string, CustomEntryContextMessageProjector>>;\n}\n\nfunction deriveSessionContextState(pathEntries: readonly SessionTreeEntry[]): Omit<SessionContext, \"messages\"> {\n\tlet thinkingLevel = \"off\";\n\tlet model: { provider: string; modelId: string } | null = null;\n\tlet activeToolNames: string[] | null = null;\n\n\tfor (const entry of pathEntries) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t} else if (entry.type === \"active_tools_change\") {\n\t\t\tactiveToolNames = [...entry.activeToolNames];\n\t\t}\n\t}\n\n\treturn { thinkingLevel, model, activeToolNames };\n}\n\nexport function defaultContextEntryTransform(pathEntries: readonly SessionTreeEntry[]): SessionTreeEntry[] {\n\tlet compaction: CompactionEntry | null = null;\n\tfor (const entry of pathEntries) {\n\t\tif (entry.type === \"compaction\") {\n\t\t\tcompaction = entry;\n\t\t}\n\t}\n\tif (!compaction) {\n\t\treturn [...pathEntries];\n\t}\n\n\tconst entries: SessionTreeEntry[] = [compaction];\n\tconst compactionIdx = pathEntries.findIndex((entry) => entry.type === \"compaction\" && entry.id === compaction.id);\n\tlet foundFirstKept = false;\n\tfor (let i = 0; i < compactionIdx; i++) {\n\t\tconst entry = pathEntries[i]!;\n\t\tif (entry.id === compaction.firstKeptEntryId) foundFirstKept = true;\n\t\tif (foundFirstKept) entries.push(entry);\n\t}\n\tfor (let i = compactionIdx + 1; i < pathEntries.length; i++) {\n\t\tentries.push(pathEntries[i]!);\n\t}\n\treturn entries;\n}\n\nexport function buildContextEntries(\n\tpathEntries: readonly SessionTreeEntry[],\n\toptions: SessionContextBuildOptions = {},\n): SessionTreeEntry[] {\n\tlet entries = defaultContextEntryTransform(pathEntries);\n\tfor (const transform of options.entryTransforms ?? []) {\n\t\tentries = [...transform(entries)];\n\t}\n\treturn entries;\n}\n\nexport function sessionEntryToContextMessages(\n\tentry: SessionTreeEntry,\n\tindex: number,\n\tentries: readonly SessionTreeEntry[],\n\toptions: SessionContextBuildOptions = {},\n): AgentMessage[] {\n\tif (entry.type === \"message\") {\n\t\treturn [entry.message as AgentMessage];\n\t}\n\tif (entry.type === \"custom_message\") {\n\t\treturn [\n\t\t\tcreateCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content as string | (TextContent | ImageContent)[],\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t),\n\t\t];\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];\n\t}\n\tif (entry.type === \"branch_summary\" && entry.summary) {\n\t\treturn [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];\n\t}\n\tif (entry.type === \"custom\") {\n\t\treturn [...(options.entryProjectors?.[entry.customType]?.(entry, index, entries) ?? [])];\n\t}\n\treturn [];\n}\n\nexport function buildSessionContext(\n\tpathEntries: readonly SessionTreeEntry[],\n\toptions: SessionContextBuildOptions = {},\n): SessionContext {\n\tconst state = deriveSessionContextState(pathEntries);\n\tconst contextEntries = buildContextEntries(pathEntries, options);\n\tconst messages = contextEntries.flatMap((entry, index) =>\n\t\tsessionEntryToContextMessages(entry, index, contextEntries, options),\n\t);\n\treturn { ...state, messages };\n}\n\nexport class Session<TMetadata extends SessionMetadata = SessionMetadata> {\n\tprivate storage: SessionStorage<TMetadata>;\n\tprivate contextBuildOptions: SessionContextBuildOptions;\n\n\tconstructor(storage: SessionStorage<TMetadata>, contextBuildOptions: SessionContextBuildOptions = {}) {\n\t\tthis.storage = storage;\n\t\tthis.contextBuildOptions = contextBuildOptions;\n\t}\n\n\tgetMetadata(): Promise<TMetadata> {\n\t\treturn this.storage.getMetadata();\n\t}\n\n\tgetStorage(): SessionStorage<TMetadata> {\n\t\treturn this.storage;\n\t}\n\n\tgetLeafId(): Promise<string | null> {\n\t\treturn this.storage.getLeafId();\n\t}\n\n\tgetEntry(id: string): Promise<SessionTreeEntry | undefined> {\n\t\treturn this.storage.getEntry(id);\n\t}\n\n\tgetEntries(): Promise<SessionTreeEntry[]> {\n\t\treturn this.storage.getEntries();\n\t}\n\n\tasync getBranch(fromId?: string): Promise<SessionTreeEntry[]> {\n\t\tconst leafId = fromId ?? (await this.storage.getLeafId());\n\t\treturn this.storage.getPathToRoot(leafId);\n\t}\n\n\tasync buildContextEntries(options: SessionContextBuildOptions = {}): Promise<SessionTreeEntry[]> {\n\t\treturn buildContextEntries(await this.getBranch(), this.mergeContextBuildOptions(options));\n\t}\n\n\tasync buildContext(options: SessionContextBuildOptions = {}): Promise<SessionContext> {\n\t\treturn buildSessionContext(await this.getBranch(), this.mergeContextBuildOptions(options));\n\t}\n\n\tprivate mergeContextBuildOptions(options: SessionContextBuildOptions): SessionContextBuildOptions {\n\t\treturn {\n\t\t\tentryTransforms: [...(this.contextBuildOptions.entryTransforms ?? []), ...(options.entryTransforms ?? [])],\n\t\t\tentryProjectors: {\n\t\t\t\t...(this.contextBuildOptions.entryProjectors ?? {}),\n\t\t\t\t...(options.entryProjectors ?? {}),\n\t\t\t},\n\t\t};\n\t}\n\n\tgetLabel(id: string): Promise<string | undefined> {\n\t\treturn this.storage.getLabel(id);\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\tconst entries = await this.storage.findEntries(\"session_info\");\n\t\treturn entries[entries.length - 1]?.name?.trim() || undefined;\n\t}\n\n\tprivate async appendTypedEntry<TEntry extends SessionTreeEntry>(entry: TEntry): Promise<string> {\n\t\tawait this.storage.appendEntry(entry);\n\t\treturn entry.id;\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tmessage,\n\t\t} satisfies MessageEntry);\n\t}\n\n\tasync appendThinkingLevelChange(thinkingLevel: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"thinking_level_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tthinkingLevel,\n\t\t} satisfies ThinkingLevelChangeEntry);\n\t}\n\n\tasync appendModelChange(provider: string, modelId: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"model_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t} satisfies ModelChangeEntry);\n\t}\n\n\tasync appendActiveToolsChange(activeToolNames: string[]): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"active_tools_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tactiveToolNames: [...activeToolNames],\n\t\t} satisfies ActiveToolsChangeEntry);\n\t}\n\n\tasync appendCompaction<T = unknown>(\n\t\tsummary: string,\n\t\tfirstKeptEntryId: string,\n\t\ttokensBefore: number,\n\t\tdetails?: T,\n\t\tfromHook?: boolean,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"compaction\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tsummary,\n\t\t\tfirstKeptEntryId,\n\t\t\ttokensBefore,\n\t\t\tdetails,\n\t\t\tfromHook,\n\t\t} satisfies CompactionEntry<T>);\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tdata,\n\t\t} satisfies CustomEntry);\n\t}\n\n\tasync appendCustomMessageEntry<T = unknown>(\n\t\tcustomType: string,\n\t\tcontent: string | (TextContent | ImageContent)[],\n\t\tdisplay: boolean,\n\t\tdetails?: T,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom_message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tcontent,\n\t\t\tdisplay,\n\t\t\tdetails,\n\t\t} satisfies CustomMessageEntry<T>);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<string> {\n\t\tif (!(await this.storage.getEntry(targetId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${targetId} not found`);\n\t\t}\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"label\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttargetId,\n\t\t\tlabel,\n\t\t} satisfies LabelEntry);\n\t}\n\n\tasync appendSessionName(name: string): Promise<string> {\n\t\tconst sanitizedName = name.replace(/[\\r\\n]+/g, \" \").trim();\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"session_info\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tname: sanitizedName,\n\t\t} satisfies SessionInfoEntry);\n\t}\n\n\tasync moveTo(\n\t\tentryId: string | null,\n\t\tsummary?: { summary: string; details?: unknown; fromHook?: boolean },\n\t): Promise<string | undefined> {\n\t\tif (entryId !== null && !(await this.storage.getEntry(entryId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${entryId} not found`);\n\t\t}\n\t\tawait this.storage.setLeafId(entryId);\n\t\tif (!summary) return undefined;\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"branch_summary\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: entryId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tfromId: entryId ?? \"root\",\n\t\t\tsummary: summary.summary,\n\t\t\tdetails: summary.details,\n\t\t\tfromHook: summary.fromHook,\n\t\t} satisfies BranchSummaryEntry);\n\t}\n}\n"]}
@@ -1,10 +1,9 @@
1
1
  import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from "../messages.js";
2
2
  import { SessionError } from "../types.js";
3
- export function buildSessionContext(pathEntries) {
3
+ function deriveSessionContextState(pathEntries) {
4
4
  let thinkingLevel = "off";
5
5
  let model = null;
6
6
  let activeToolNames = null;
7
- let compaction = null;
8
7
  for (const entry of pathEntries) {
9
8
  if (entry.type === "thinking_level_change") {
10
9
  thinkingLevel = entry.thinkingLevel;
@@ -18,48 +17,73 @@ export function buildSessionContext(pathEntries) {
18
17
  else if (entry.type === "active_tools_change") {
19
18
  activeToolNames = [...entry.activeToolNames];
20
19
  }
21
- else if (entry.type === "compaction") {
20
+ }
21
+ return { thinkingLevel, model, activeToolNames };
22
+ }
23
+ export function defaultContextEntryTransform(pathEntries) {
24
+ let compaction = null;
25
+ for (const entry of pathEntries) {
26
+ if (entry.type === "compaction") {
22
27
  compaction = entry;
23
28
  }
24
29
  }
25
- const messages = [];
26
- const appendMessage = (entry) => {
27
- if (entry.type === "message") {
28
- messages.push(entry.message);
29
- }
30
- else if (entry.type === "custom_message") {
31
- messages.push(createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp));
32
- }
33
- else if (entry.type === "branch_summary" && entry.summary) {
34
- messages.push(createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp));
35
- }
36
- };
37
- if (compaction) {
38
- messages.push(createCompactionSummaryMessage(compaction.summary, compaction.tokensBefore, compaction.timestamp));
39
- const compactionIdx = pathEntries.findIndex((e) => e.type === "compaction" && e.id === compaction.id);
40
- let foundFirstKept = false;
41
- for (let i = 0; i < compactionIdx; i++) {
42
- const entry = pathEntries[i];
43
- if (entry.id === compaction.firstKeptEntryId)
44
- foundFirstKept = true;
45
- if (foundFirstKept)
46
- appendMessage(entry);
47
- }
48
- for (let i = compactionIdx + 1; i < pathEntries.length; i++) {
49
- appendMessage(pathEntries[i]);
50
- }
30
+ if (!compaction) {
31
+ return [...pathEntries];
51
32
  }
52
- else {
53
- for (const entry of pathEntries) {
54
- appendMessage(entry);
55
- }
33
+ const entries = [compaction];
34
+ const compactionIdx = pathEntries.findIndex((entry) => entry.type === "compaction" && entry.id === compaction.id);
35
+ let foundFirstKept = false;
36
+ for (let i = 0; i < compactionIdx; i++) {
37
+ const entry = pathEntries[i];
38
+ if (entry.id === compaction.firstKeptEntryId)
39
+ foundFirstKept = true;
40
+ if (foundFirstKept)
41
+ entries.push(entry);
42
+ }
43
+ for (let i = compactionIdx + 1; i < pathEntries.length; i++) {
44
+ entries.push(pathEntries[i]);
56
45
  }
57
- return { messages, thinkingLevel, model, activeToolNames };
46
+ return entries;
47
+ }
48
+ export function buildContextEntries(pathEntries, options = {}) {
49
+ let entries = defaultContextEntryTransform(pathEntries);
50
+ for (const transform of options.entryTransforms ?? []) {
51
+ entries = [...transform(entries)];
52
+ }
53
+ return entries;
54
+ }
55
+ export function sessionEntryToContextMessages(entry, index, entries, options = {}) {
56
+ if (entry.type === "message") {
57
+ return [entry.message];
58
+ }
59
+ if (entry.type === "custom_message") {
60
+ return [
61
+ createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp),
62
+ ];
63
+ }
64
+ if (entry.type === "compaction") {
65
+ return [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];
66
+ }
67
+ if (entry.type === "branch_summary" && entry.summary) {
68
+ return [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];
69
+ }
70
+ if (entry.type === "custom") {
71
+ return [...(options.entryProjectors?.[entry.customType]?.(entry, index, entries) ?? [])];
72
+ }
73
+ return [];
74
+ }
75
+ export function buildSessionContext(pathEntries, options = {}) {
76
+ const state = deriveSessionContextState(pathEntries);
77
+ const contextEntries = buildContextEntries(pathEntries, options);
78
+ const messages = contextEntries.flatMap((entry, index) => sessionEntryToContextMessages(entry, index, contextEntries, options));
79
+ return { ...state, messages };
58
80
  }
59
81
  export class Session {
60
82
  storage;
61
- constructor(storage) {
83
+ contextBuildOptions;
84
+ constructor(storage, contextBuildOptions = {}) {
62
85
  this.storage = storage;
86
+ this.contextBuildOptions = contextBuildOptions;
63
87
  }
64
88
  getMetadata() {
65
89
  return this.storage.getMetadata();
@@ -80,8 +104,20 @@ export class Session {
80
104
  const leafId = fromId ?? (await this.storage.getLeafId());
81
105
  return this.storage.getPathToRoot(leafId);
82
106
  }
83
- async buildContext() {
84
- return buildSessionContext(await this.getBranch());
107
+ async buildContextEntries(options = {}) {
108
+ return buildContextEntries(await this.getBranch(), this.mergeContextBuildOptions(options));
109
+ }
110
+ async buildContext(options = {}) {
111
+ return buildSessionContext(await this.getBranch(), this.mergeContextBuildOptions(options));
112
+ }
113
+ mergeContextBuildOptions(options) {
114
+ return {
115
+ entryTransforms: [...(this.contextBuildOptions.entryTransforms ?? []), ...(options.entryTransforms ?? [])],
116
+ entryProjectors: {
117
+ ...(this.contextBuildOptions.entryProjectors ?? {}),
118
+ ...(options.entryProjectors ?? {}),
119
+ },
120
+ };
85
121
  }
86
122
  getLabel(id) {
87
123
  return this.storage.getLabel(id);
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@draht/ai\";\nimport type { AgentMessage } from \"../../types.js\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.js\";\nimport type {\n\tActiveToolsChangeEntry,\n\tBranchSummaryEntry,\n\tCompactionEntry,\n\tCustomEntry,\n\tCustomMessageEntry,\n\tLabelEntry,\n\tMessageEntry,\n\tModelChangeEntry,\n\tSessionContext,\n\tSessionInfoEntry,\n\tSessionMetadata,\n\tSessionStorage,\n\tSessionTreeEntry,\n\tThinkingLevelChangeEntry,\n} from \"../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport function buildSessionContext(pathEntries: SessionTreeEntry[]): SessionContext {\n\tlet thinkingLevel = \"off\";\n\tlet model: { provider: string; modelId: string } | null = null;\n\tlet activeToolNames: string[] | null = null;\n\tlet compaction: CompactionEntry | null = null;\n\n\tfor (const entry of pathEntries) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t} else if (entry.type === \"active_tools_change\") {\n\t\t\tactiveToolNames = [...entry.activeToolNames];\n\t\t} else if (entry.type === \"compaction\") {\n\t\t\tcompaction = entry;\n\t\t}\n\t}\n\n\tconst messages: AgentMessage[] = [];\n\tconst appendMessage = (entry: SessionTreeEntry) => {\n\t\tif (entry.type === \"message\") {\n\t\t\tmessages.push(entry.message as AgentMessage);\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessages.push(\n\t\t\t\tcreateCustomMessage(\n\t\t\t\t\tentry.customType,\n\t\t\t\t\tentry.content as string | (TextContent | ImageContent)[],\n\t\t\t\t\tentry.display,\n\t\t\t\t\tentry.details,\n\t\t\t\t\tentry.timestamp,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && entry.summary) {\n\t\t\tmessages.push(createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp));\n\t\t}\n\t};\n\n\tif (compaction) {\n\t\tmessages.push(createCompactionSummaryMessage(compaction.summary, compaction.tokensBefore, compaction.timestamp));\n\t\tconst compactionIdx = pathEntries.findIndex((e) => e.type === \"compaction\" && e.id === compaction.id);\n\t\tlet foundFirstKept = false;\n\t\tfor (let i = 0; i < compactionIdx; i++) {\n\t\t\tconst entry = pathEntries[i]!;\n\t\t\tif (entry.id === compaction.firstKeptEntryId) foundFirstKept = true;\n\t\t\tif (foundFirstKept) appendMessage(entry);\n\t\t}\n\t\tfor (let i = compactionIdx + 1; i < pathEntries.length; i++) {\n\t\t\tappendMessage(pathEntries[i]!);\n\t\t}\n\t} else {\n\t\tfor (const entry of pathEntries) {\n\t\t\tappendMessage(entry);\n\t\t}\n\t}\n\n\treturn { messages, thinkingLevel, model, activeToolNames };\n}\n\nexport class Session<TMetadata extends SessionMetadata = SessionMetadata> {\n\tprivate storage: SessionStorage<TMetadata>;\n\n\tconstructor(storage: SessionStorage<TMetadata>) {\n\t\tthis.storage = storage;\n\t}\n\n\tgetMetadata(): Promise<TMetadata> {\n\t\treturn this.storage.getMetadata();\n\t}\n\n\tgetStorage(): SessionStorage<TMetadata> {\n\t\treturn this.storage;\n\t}\n\n\tgetLeafId(): Promise<string | null> {\n\t\treturn this.storage.getLeafId();\n\t}\n\n\tgetEntry(id: string): Promise<SessionTreeEntry | undefined> {\n\t\treturn this.storage.getEntry(id);\n\t}\n\n\tgetEntries(): Promise<SessionTreeEntry[]> {\n\t\treturn this.storage.getEntries();\n\t}\n\n\tasync getBranch(fromId?: string): Promise<SessionTreeEntry[]> {\n\t\tconst leafId = fromId ?? (await this.storage.getLeafId());\n\t\treturn this.storage.getPathToRoot(leafId);\n\t}\n\n\tasync buildContext(): Promise<SessionContext> {\n\t\treturn buildSessionContext(await this.getBranch());\n\t}\n\n\tgetLabel(id: string): Promise<string | undefined> {\n\t\treturn this.storage.getLabel(id);\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\tconst entries = await this.storage.findEntries(\"session_info\");\n\t\treturn entries[entries.length - 1]?.name?.trim() || undefined;\n\t}\n\n\tprivate async appendTypedEntry<TEntry extends SessionTreeEntry>(entry: TEntry): Promise<string> {\n\t\tawait this.storage.appendEntry(entry);\n\t\treturn entry.id;\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tmessage,\n\t\t} satisfies MessageEntry);\n\t}\n\n\tasync appendThinkingLevelChange(thinkingLevel: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"thinking_level_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tthinkingLevel,\n\t\t} satisfies ThinkingLevelChangeEntry);\n\t}\n\n\tasync appendModelChange(provider: string, modelId: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"model_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t} satisfies ModelChangeEntry);\n\t}\n\n\tasync appendActiveToolsChange(activeToolNames: string[]): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"active_tools_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tactiveToolNames: [...activeToolNames],\n\t\t} satisfies ActiveToolsChangeEntry);\n\t}\n\n\tasync appendCompaction<T = unknown>(\n\t\tsummary: string,\n\t\tfirstKeptEntryId: string,\n\t\ttokensBefore: number,\n\t\tdetails?: T,\n\t\tfromHook?: boolean,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"compaction\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tsummary,\n\t\t\tfirstKeptEntryId,\n\t\t\ttokensBefore,\n\t\t\tdetails,\n\t\t\tfromHook,\n\t\t} satisfies CompactionEntry<T>);\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tdata,\n\t\t} satisfies CustomEntry);\n\t}\n\n\tasync appendCustomMessageEntry<T = unknown>(\n\t\tcustomType: string,\n\t\tcontent: string | (TextContent | ImageContent)[],\n\t\tdisplay: boolean,\n\t\tdetails?: T,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom_message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tcontent,\n\t\t\tdisplay,\n\t\t\tdetails,\n\t\t} satisfies CustomMessageEntry<T>);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<string> {\n\t\tif (!(await this.storage.getEntry(targetId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${targetId} not found`);\n\t\t}\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"label\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttargetId,\n\t\t\tlabel,\n\t\t} satisfies LabelEntry);\n\t}\n\n\tasync appendSessionName(name: string): Promise<string> {\n\t\tconst sanitizedName = name.replace(/[\\r\\n]+/g, \" \").trim();\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"session_info\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tname: sanitizedName,\n\t\t} satisfies SessionInfoEntry);\n\t}\n\n\tasync moveTo(\n\t\tentryId: string | null,\n\t\tsummary?: { summary: string; details?: unknown; fromHook?: boolean },\n\t): Promise<string | undefined> {\n\t\tif (entryId !== null && !(await this.storage.getEntry(entryId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${entryId} not found`);\n\t\t}\n\t\tawait this.storage.setLeafId(entryId);\n\t\tif (!summary) return undefined;\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"branch_summary\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: entryId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tfromId: entryId ?? \"root\",\n\t\t\tsummary: summary.summary,\n\t\t\tdetails: summary.details,\n\t\t\tfromHook: summary.fromHook,\n\t\t} satisfies BranchSummaryEntry);\n\t}\n}\n"]}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@draht/ai\";\nimport type { AgentMessage } from \"../../types.js\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.js\";\nimport type {\n\tActiveToolsChangeEntry,\n\tBranchSummaryEntry,\n\tCompactionEntry,\n\tCustomEntry,\n\tCustomMessageEntry,\n\tLabelEntry,\n\tMessageEntry,\n\tModelChangeEntry,\n\tSessionContext,\n\tSessionInfoEntry,\n\tSessionMetadata,\n\tSessionStorage,\n\tSessionTreeEntry,\n\tThinkingLevelChangeEntry,\n} from \"../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport type ContextEntryTransform = (entries: readonly SessionTreeEntry[]) => readonly SessionTreeEntry[];\n\nexport type CustomEntryContextMessageProjector = (\n\tentry: CustomEntry,\n\tindex: number,\n\tentries: readonly SessionTreeEntry[],\n) => readonly AgentMessage[] | undefined;\n\nexport interface SessionContextBuildOptions {\n\t/** Additional entry transforms applied after the default compaction transform. */\n\tentryTransforms?: readonly ContextEntryTransform[];\n\t/** Optional custom-entry projectors. Custom entries are omitted from model context by default. */\n\tentryProjectors?: Readonly<Record<string, CustomEntryContextMessageProjector>>;\n}\n\nfunction deriveSessionContextState(pathEntries: readonly SessionTreeEntry[]): Omit<SessionContext, \"messages\"> {\n\tlet thinkingLevel = \"off\";\n\tlet model: { provider: string; modelId: string } | null = null;\n\tlet activeToolNames: string[] | null = null;\n\n\tfor (const entry of pathEntries) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t} else if (entry.type === \"active_tools_change\") {\n\t\t\tactiveToolNames = [...entry.activeToolNames];\n\t\t}\n\t}\n\n\treturn { thinkingLevel, model, activeToolNames };\n}\n\nexport function defaultContextEntryTransform(pathEntries: readonly SessionTreeEntry[]): SessionTreeEntry[] {\n\tlet compaction: CompactionEntry | null = null;\n\tfor (const entry of pathEntries) {\n\t\tif (entry.type === \"compaction\") {\n\t\t\tcompaction = entry;\n\t\t}\n\t}\n\tif (!compaction) {\n\t\treturn [...pathEntries];\n\t}\n\n\tconst entries: SessionTreeEntry[] = [compaction];\n\tconst compactionIdx = pathEntries.findIndex((entry) => entry.type === \"compaction\" && entry.id === compaction.id);\n\tlet foundFirstKept = false;\n\tfor (let i = 0; i < compactionIdx; i++) {\n\t\tconst entry = pathEntries[i]!;\n\t\tif (entry.id === compaction.firstKeptEntryId) foundFirstKept = true;\n\t\tif (foundFirstKept) entries.push(entry);\n\t}\n\tfor (let i = compactionIdx + 1; i < pathEntries.length; i++) {\n\t\tentries.push(pathEntries[i]!);\n\t}\n\treturn entries;\n}\n\nexport function buildContextEntries(\n\tpathEntries: readonly SessionTreeEntry[],\n\toptions: SessionContextBuildOptions = {},\n): SessionTreeEntry[] {\n\tlet entries = defaultContextEntryTransform(pathEntries);\n\tfor (const transform of options.entryTransforms ?? []) {\n\t\tentries = [...transform(entries)];\n\t}\n\treturn entries;\n}\n\nexport function sessionEntryToContextMessages(\n\tentry: SessionTreeEntry,\n\tindex: number,\n\tentries: readonly SessionTreeEntry[],\n\toptions: SessionContextBuildOptions = {},\n): AgentMessage[] {\n\tif (entry.type === \"message\") {\n\t\treturn [entry.message as AgentMessage];\n\t}\n\tif (entry.type === \"custom_message\") {\n\t\treturn [\n\t\t\tcreateCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content as string | (TextContent | ImageContent)[],\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t),\n\t\t];\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];\n\t}\n\tif (entry.type === \"branch_summary\" && entry.summary) {\n\t\treturn [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];\n\t}\n\tif (entry.type === \"custom\") {\n\t\treturn [...(options.entryProjectors?.[entry.customType]?.(entry, index, entries) ?? [])];\n\t}\n\treturn [];\n}\n\nexport function buildSessionContext(\n\tpathEntries: readonly SessionTreeEntry[],\n\toptions: SessionContextBuildOptions = {},\n): SessionContext {\n\tconst state = deriveSessionContextState(pathEntries);\n\tconst contextEntries = buildContextEntries(pathEntries, options);\n\tconst messages = contextEntries.flatMap((entry, index) =>\n\t\tsessionEntryToContextMessages(entry, index, contextEntries, options),\n\t);\n\treturn { ...state, messages };\n}\n\nexport class Session<TMetadata extends SessionMetadata = SessionMetadata> {\n\tprivate storage: SessionStorage<TMetadata>;\n\tprivate contextBuildOptions: SessionContextBuildOptions;\n\n\tconstructor(storage: SessionStorage<TMetadata>, contextBuildOptions: SessionContextBuildOptions = {}) {\n\t\tthis.storage = storage;\n\t\tthis.contextBuildOptions = contextBuildOptions;\n\t}\n\n\tgetMetadata(): Promise<TMetadata> {\n\t\treturn this.storage.getMetadata();\n\t}\n\n\tgetStorage(): SessionStorage<TMetadata> {\n\t\treturn this.storage;\n\t}\n\n\tgetLeafId(): Promise<string | null> {\n\t\treturn this.storage.getLeafId();\n\t}\n\n\tgetEntry(id: string): Promise<SessionTreeEntry | undefined> {\n\t\treturn this.storage.getEntry(id);\n\t}\n\n\tgetEntries(): Promise<SessionTreeEntry[]> {\n\t\treturn this.storage.getEntries();\n\t}\n\n\tasync getBranch(fromId?: string): Promise<SessionTreeEntry[]> {\n\t\tconst leafId = fromId ?? (await this.storage.getLeafId());\n\t\treturn this.storage.getPathToRoot(leafId);\n\t}\n\n\tasync buildContextEntries(options: SessionContextBuildOptions = {}): Promise<SessionTreeEntry[]> {\n\t\treturn buildContextEntries(await this.getBranch(), this.mergeContextBuildOptions(options));\n\t}\n\n\tasync buildContext(options: SessionContextBuildOptions = {}): Promise<SessionContext> {\n\t\treturn buildSessionContext(await this.getBranch(), this.mergeContextBuildOptions(options));\n\t}\n\n\tprivate mergeContextBuildOptions(options: SessionContextBuildOptions): SessionContextBuildOptions {\n\t\treturn {\n\t\t\tentryTransforms: [...(this.contextBuildOptions.entryTransforms ?? []), ...(options.entryTransforms ?? [])],\n\t\t\tentryProjectors: {\n\t\t\t\t...(this.contextBuildOptions.entryProjectors ?? {}),\n\t\t\t\t...(options.entryProjectors ?? {}),\n\t\t\t},\n\t\t};\n\t}\n\n\tgetLabel(id: string): Promise<string | undefined> {\n\t\treturn this.storage.getLabel(id);\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\tconst entries = await this.storage.findEntries(\"session_info\");\n\t\treturn entries[entries.length - 1]?.name?.trim() || undefined;\n\t}\n\n\tprivate async appendTypedEntry<TEntry extends SessionTreeEntry>(entry: TEntry): Promise<string> {\n\t\tawait this.storage.appendEntry(entry);\n\t\treturn entry.id;\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tmessage,\n\t\t} satisfies MessageEntry);\n\t}\n\n\tasync appendThinkingLevelChange(thinkingLevel: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"thinking_level_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tthinkingLevel,\n\t\t} satisfies ThinkingLevelChangeEntry);\n\t}\n\n\tasync appendModelChange(provider: string, modelId: string): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"model_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t} satisfies ModelChangeEntry);\n\t}\n\n\tasync appendActiveToolsChange(activeToolNames: string[]): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"active_tools_change\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tactiveToolNames: [...activeToolNames],\n\t\t} satisfies ActiveToolsChangeEntry);\n\t}\n\n\tasync appendCompaction<T = unknown>(\n\t\tsummary: string,\n\t\tfirstKeptEntryId: string,\n\t\ttokensBefore: number,\n\t\tdetails?: T,\n\t\tfromHook?: boolean,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"compaction\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tsummary,\n\t\t\tfirstKeptEntryId,\n\t\t\ttokensBefore,\n\t\t\tdetails,\n\t\t\tfromHook,\n\t\t} satisfies CompactionEntry<T>);\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tdata,\n\t\t} satisfies CustomEntry);\n\t}\n\n\tasync appendCustomMessageEntry<T = unknown>(\n\t\tcustomType: string,\n\t\tcontent: string | (TextContent | ImageContent)[],\n\t\tdisplay: boolean,\n\t\tdetails?: T,\n\t): Promise<string> {\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"custom_message\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tcustomType,\n\t\t\tcontent,\n\t\t\tdisplay,\n\t\t\tdetails,\n\t\t} satisfies CustomMessageEntry<T>);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<string> {\n\t\tif (!(await this.storage.getEntry(targetId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${targetId} not found`);\n\t\t}\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"label\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\ttargetId,\n\t\t\tlabel,\n\t\t} satisfies LabelEntry);\n\t}\n\n\tasync appendSessionName(name: string): Promise<string> {\n\t\tconst sanitizedName = name.replace(/[\\r\\n]+/g, \" \").trim();\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"session_info\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: await this.storage.getLeafId(),\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tname: sanitizedName,\n\t\t} satisfies SessionInfoEntry);\n\t}\n\n\tasync moveTo(\n\t\tentryId: string | null,\n\t\tsummary?: { summary: string; details?: unknown; fromHook?: boolean },\n\t): Promise<string | undefined> {\n\t\tif (entryId !== null && !(await this.storage.getEntry(entryId))) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry ${entryId} not found`);\n\t\t}\n\t\tawait this.storage.setLeafId(entryId);\n\t\tif (!summary) return undefined;\n\t\treturn this.appendTypedEntry({\n\t\t\ttype: \"branch_summary\",\n\t\t\tid: await this.storage.createEntryId(),\n\t\t\tparentId: entryId,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tfromId: entryId ?? \"root\",\n\t\t\tsummary: summary.summary,\n\t\t\tdetails: summary.details,\n\t\t\tfromHook: summary.fromHook,\n\t\t} satisfies BranchSummaryEntry);\n\t}\n}\n"]}
@@ -308,6 +308,7 @@ export interface JsonlSessionMetadata extends SessionMetadata {
308
308
  cwd: string;
309
309
  path: string;
310
310
  parentSessionPath?: string;
311
+ metadata?: Record<string, unknown>;
311
312
  }
312
313
  export interface SessionStorage<TMetadata extends SessionMetadata = SessionMetadata> {
313
314
  getMetadata(): Promise<TMetadata>;
@@ -343,6 +344,7 @@ export interface SessionRepo<TMetadata extends SessionMetadata = SessionMetadata
343
344
  export interface JsonlSessionCreateOptions extends SessionCreateOptions {
344
345
  cwd: string;
345
346
  parentSessionPath?: string;
347
+ metadata?: Record<string, unknown>;
346
348
  }
347
349
  export interface JsonlSessionListOptions {
348
350
  cwd?: string;