@sammorrowdrums/mcpi-agent-core 0.85.0-bootstrap.0

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 (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +513 -0
  3. package/dist/agent-loop.d.ts +24 -0
  4. package/dist/agent-loop.d.ts.map +1 -0
  5. package/dist/agent-loop.js +553 -0
  6. package/dist/agent-loop.js.map +1 -0
  7. package/dist/agent.d.ts +122 -0
  8. package/dist/agent.d.ts.map +1 -0
  9. package/dist/agent.js +422 -0
  10. package/dist/agent.js.map +1 -0
  11. package/dist/harness/agent-harness.d.ts +461 -0
  12. package/dist/harness/agent-harness.d.ts.map +1 -0
  13. package/dist/harness/agent-harness.js +252 -0
  14. package/dist/harness/agent-harness.js.map +1 -0
  15. package/dist/harness/compaction/branch-summarization.d.ts +62 -0
  16. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  17. package/dist/harness/compaction/branch-summarization.js +169 -0
  18. package/dist/harness/compaction/branch-summarization.js.map +1 -0
  19. package/dist/harness/compaction/compaction.d.ts +104 -0
  20. package/dist/harness/compaction/compaction.d.ts.map +1 -0
  21. package/dist/harness/compaction/compaction.js +570 -0
  22. package/dist/harness/compaction/compaction.js.map +1 -0
  23. package/dist/harness/compaction/utils.d.ts +25 -0
  24. package/dist/harness/compaction/utils.d.ts.map +1 -0
  25. package/dist/harness/compaction/utils.js +120 -0
  26. package/dist/harness/compaction/utils.js.map +1 -0
  27. package/dist/harness/env/nodejs.d.ts +46 -0
  28. package/dist/harness/env/nodejs.d.ts.map +1 -0
  29. package/dist/harness/env/nodejs.js +639 -0
  30. package/dist/harness/env/nodejs.js.map +1 -0
  31. package/dist/harness/events.d.ts +43 -0
  32. package/dist/harness/events.d.ts.map +1 -0
  33. package/dist/harness/events.js +65 -0
  34. package/dist/harness/events.js.map +1 -0
  35. package/dist/harness/messages.d.ts +51 -0
  36. package/dist/harness/messages.d.ts.map +1 -0
  37. package/dist/harness/messages.js +102 -0
  38. package/dist/harness/messages.js.map +1 -0
  39. package/dist/harness/prompt-templates.d.ts +48 -0
  40. package/dist/harness/prompt-templates.d.ts.map +1 -0
  41. package/dist/harness/prompt-templates.js +225 -0
  42. package/dist/harness/prompt-templates.js.map +1 -0
  43. package/dist/harness/reducer.d.ts +100 -0
  44. package/dist/harness/reducer.d.ts.map +1 -0
  45. package/dist/harness/reducer.js +415 -0
  46. package/dist/harness/reducer.js.map +1 -0
  47. package/dist/harness/result.d.ts +40 -0
  48. package/dist/harness/result.d.ts.map +1 -0
  49. package/dist/harness/result.js +41 -0
  50. package/dist/harness/result.js.map +1 -0
  51. package/dist/harness/session/context.d.ts +22 -0
  52. package/dist/harness/session/context.d.ts.map +1 -0
  53. package/dist/harness/session/context.js +67 -0
  54. package/dist/harness/session/context.js.map +1 -0
  55. package/dist/harness/session/index.d.ts +7 -0
  56. package/dist/harness/session/index.d.ts.map +1 -0
  57. package/dist/harness/session/index.js +6 -0
  58. package/dist/harness/session/index.js.map +1 -0
  59. package/dist/harness/session/jsonl/codec.d.ts +10 -0
  60. package/dist/harness/session/jsonl/codec.d.ts.map +1 -0
  61. package/dist/harness/session/jsonl/codec.js +229 -0
  62. package/dist/harness/session/jsonl/codec.js.map +1 -0
  63. package/dist/harness/session/jsonl/errors.d.ts +9 -0
  64. package/dist/harness/session/jsonl/errors.d.ts.map +1 -0
  65. package/dist/harness/session/jsonl/errors.js +19 -0
  66. package/dist/harness/session/jsonl/errors.js.map +1 -0
  67. package/dist/harness/session/jsonl/repo.d.ts +27 -0
  68. package/dist/harness/session/jsonl/repo.d.ts.map +1 -0
  69. package/dist/harness/session/jsonl/repo.js +175 -0
  70. package/dist/harness/session/jsonl/repo.js.map +1 -0
  71. package/dist/harness/session/jsonl/storage.d.ts +43 -0
  72. package/dist/harness/session/jsonl/storage.d.ts.map +1 -0
  73. package/dist/harness/session/jsonl/storage.js +217 -0
  74. package/dist/harness/session/jsonl/storage.js.map +1 -0
  75. package/dist/harness/session/jsonl/types.d.ts +38 -0
  76. package/dist/harness/session/jsonl/types.d.ts.map +1 -0
  77. package/dist/harness/session/jsonl/types.js +2 -0
  78. package/dist/harness/session/jsonl/types.js.map +1 -0
  79. package/dist/harness/session/jsonl.d.ts +3 -0
  80. package/dist/harness/session/jsonl.d.ts.map +1 -0
  81. package/dist/harness/session/jsonl.js +2 -0
  82. package/dist/harness/session/jsonl.js.map +1 -0
  83. package/dist/harness/session/memory.d.ts +44 -0
  84. package/dist/harness/session/memory.d.ts.map +1 -0
  85. package/dist/harness/session/memory.js +141 -0
  86. package/dist/harness/session/memory.js.map +1 -0
  87. package/dist/harness/session/session.d.ts +50 -0
  88. package/dist/harness/session/session.d.ts.map +1 -0
  89. package/dist/harness/session/session.js +227 -0
  90. package/dist/harness/session/session.js.map +1 -0
  91. package/dist/harness/session/state.d.ts +65 -0
  92. package/dist/harness/session/state.d.ts.map +1 -0
  93. package/dist/harness/session/state.js +319 -0
  94. package/dist/harness/session/state.js.map +1 -0
  95. package/dist/harness/session/testing/conformance.d.ts +4 -0
  96. package/dist/harness/session/testing/conformance.d.ts.map +1 -0
  97. package/dist/harness/session/testing/conformance.js +762 -0
  98. package/dist/harness/session/testing/conformance.js.map +1 -0
  99. package/dist/harness/session/testing/index.d.ts +3 -0
  100. package/dist/harness/session/testing/index.d.ts.map +1 -0
  101. package/dist/harness/session/testing/index.js +2 -0
  102. package/dist/harness/session/testing/index.js.map +1 -0
  103. package/dist/harness/session/testing/types.d.ts +14 -0
  104. package/dist/harness/session/testing/types.d.ts.map +1 -0
  105. package/dist/harness/session/testing/types.js +2 -0
  106. package/dist/harness/session/testing/types.js.map +1 -0
  107. package/dist/harness/session/types.d.ts +346 -0
  108. package/dist/harness/session/types.d.ts.map +1 -0
  109. package/dist/harness/session/types.js +10 -0
  110. package/dist/harness/session/types.js.map +1 -0
  111. package/dist/harness/skills.d.ts +44 -0
  112. package/dist/harness/skills.d.ts.map +1 -0
  113. package/dist/harness/skills.js +313 -0
  114. package/dist/harness/skills.js.map +1 -0
  115. package/dist/harness/system-prompt.d.ts +3 -0
  116. package/dist/harness/system-prompt.d.ts.map +1 -0
  117. package/dist/harness/system-prompt.js +30 -0
  118. package/dist/harness/system-prompt.js.map +1 -0
  119. package/dist/harness/telemetry.d.ts +1221 -0
  120. package/dist/harness/telemetry.d.ts.map +1 -0
  121. package/dist/harness/telemetry.js +508 -0
  122. package/dist/harness/telemetry.js.map +1 -0
  123. package/dist/harness/tools/bash.d.ts +27 -0
  124. package/dist/harness/tools/bash.d.ts.map +1 -0
  125. package/dist/harness/tools/bash.js +132 -0
  126. package/dist/harness/tools/bash.js.map +1 -0
  127. package/dist/harness/tools/edit-diff.d.ts +89 -0
  128. package/dist/harness/tools/edit-diff.d.ts.map +1 -0
  129. package/dist/harness/tools/edit-diff.js +386 -0
  130. package/dist/harness/tools/edit-diff.js.map +1 -0
  131. package/dist/harness/tools/edit.d.ts +19 -0
  132. package/dist/harness/tools/edit.d.ts.map +1 -0
  133. package/dist/harness/tools/edit.js +95 -0
  134. package/dist/harness/tools/edit.js.map +1 -0
  135. package/dist/harness/tools/file-mutation-queue.d.ts +4 -0
  136. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -0
  137. package/dist/harness/tools/file-mutation-queue.js +46 -0
  138. package/dist/harness/tools/file-mutation-queue.js.map +1 -0
  139. package/dist/harness/tools/image.d.ts +3 -0
  140. package/dist/harness/tools/image.d.ts.map +1 -0
  141. package/dist/harness/tools/image.js +106 -0
  142. package/dist/harness/tools/image.js.map +1 -0
  143. package/dist/harness/tools/index.d.ts +6 -0
  144. package/dist/harness/tools/index.d.ts.map +1 -0
  145. package/dist/harness/tools/index.js +5 -0
  146. package/dist/harness/tools/index.js.map +1 -0
  147. package/dist/harness/tools/path-utils.d.ts +4 -0
  148. package/dist/harness/tools/path-utils.d.ts.map +1 -0
  149. package/dist/harness/tools/path-utils.js +26 -0
  150. package/dist/harness/tools/path-utils.js.map +1 -0
  151. package/dist/harness/tools/read.d.ts +34 -0
  152. package/dist/harness/tools/read.d.ts.map +1 -0
  153. package/dist/harness/tools/read.js +110 -0
  154. package/dist/harness/tools/read.js.map +1 -0
  155. package/dist/harness/tools/tool-context.d.ts +6 -0
  156. package/dist/harness/tools/tool-context.d.ts.map +1 -0
  157. package/dist/harness/tools/tool-context.js +2 -0
  158. package/dist/harness/tools/tool-context.js.map +1 -0
  159. package/dist/harness/tools/write.d.ts +11 -0
  160. package/dist/harness/tools/write.d.ts.map +1 -0
  161. package/dist/harness/tools/write.js +31 -0
  162. package/dist/harness/tools/write.js.map +1 -0
  163. package/dist/harness/types.d.ts +228 -0
  164. package/dist/harness/types.d.ts.map +1 -0
  165. package/dist/harness/types.js +75 -0
  166. package/dist/harness/types.js.map +1 -0
  167. package/dist/harness/utils/shell-output.d.ts +22 -0
  168. package/dist/harness/utils/shell-output.d.ts.map +1 -0
  169. package/dist/harness/utils/shell-output.js +181 -0
  170. package/dist/harness/utils/shell-output.js.map +1 -0
  171. package/dist/harness/utils/truncate.d.ts +70 -0
  172. package/dist/harness/utils/truncate.d.ts.map +1 -0
  173. package/dist/harness/utils/truncate.js +296 -0
  174. package/dist/harness/utils/truncate.js.map +1 -0
  175. package/dist/index.d.ts +25 -0
  176. package/dist/index.d.ts.map +1 -0
  177. package/dist/index.js +29 -0
  178. package/dist/index.js.map +1 -0
  179. package/dist/node.d.ts +3 -0
  180. package/dist/node.d.ts.map +1 -0
  181. package/dist/node.js +3 -0
  182. package/dist/node.js.map +1 -0
  183. package/dist/proxy.d.ts +70 -0
  184. package/dist/proxy.d.ts.map +1 -0
  185. package/dist/proxy.js +280 -0
  186. package/dist/proxy.js.map +1 -0
  187. package/dist/search/index.d.ts +21 -0
  188. package/dist/search/index.d.ts.map +1 -0
  189. package/dist/search/index.js +2 -0
  190. package/dist/search/index.js.map +1 -0
  191. package/dist/search/scanning.d.ts +29 -0
  192. package/dist/search/scanning.d.ts.map +1 -0
  193. package/dist/search/scanning.js +103 -0
  194. package/dist/search/scanning.js.map +1 -0
  195. package/dist/stream-fn.d.ts +10 -0
  196. package/dist/stream-fn.d.ts.map +1 -0
  197. package/dist/stream-fn.js +17 -0
  198. package/dist/stream-fn.js.map +1 -0
  199. package/dist/types.d.ts +413 -0
  200. package/dist/types.d.ts.map +1 -0
  201. package/dist/types.js +2 -0
  202. package/dist/types.js.map +1 -0
  203. package/package.json +77 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/harness/session/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EAAmB,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,KAAK,SAAS,KAAK,EAAE,CAAC;AAEpF,MAAM,MAAM,kCAAkC,GAAG,CAChD,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,KAAK,EAAE,KACrB,SAAS,YAAY,EAAE,GAAG,SAAS,CAAC;AAEzC,MAAM,WAAW,0BAA0B;IAC1C,eAAe,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kCAAkC,CAAC,CAAC,CAAC;CAC/E;AAsBD,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,SAAS,KAAK,EAAE,GAAG,KAAK,EAAE,CAYnF;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,SAAS,KAAK,EAAE,EAAE,OAAO,GAAE,0BAA+B,GAAG,KAAK,EAAE,CAIpH;AAED,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,KAAK,EAAE,EACzB,OAAO,GAAE,0BAA+B,GACtC,YAAY,EAAE,CAkBhB;AAED,wBAAgB,mBAAmB,CAClC,WAAW,EAAE,SAAS,KAAK,EAAE,EAC7B,OAAO,GAAE,0BAA+B,GACtC,cAAc,CAOhB","sourcesContent":["import type { AgentMessage } from \"../../types.ts\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage } from \"../messages.ts\";\nimport type { CompactionEntry, CustomEntry, Entry } from \"./types.ts\";\n\nexport interface SessionContext {\n\tmessages: AgentMessage[];\n\tthinkingLevel: string;\n\tmodel: { provider: string; modelId: string } | null;\n\tactiveToolNames: string[] | null;\n}\n\nexport type ContextEntryTransform = (entries: readonly Entry[]) => readonly Entry[];\n\nexport type CustomEntryContextMessageProjector = (\n\tentry: CustomEntry,\n\tindex: number,\n\tentries: readonly Entry[],\n) => readonly AgentMessage[] | undefined;\n\nexport interface SessionContextBuildOptions {\n\tentryTransforms?: readonly ContextEntryTransform[];\n\tentryProjectors?: Readonly<Record<string, CustomEntryContextMessageProjector>>;\n}\n\nfunction deriveSessionContextState(pathEntries: readonly Entry[]): 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 Entry[]): Entry[] {\n\tlet compaction: CompactionEntry | undefined;\n\tlet compactionIndex = -1;\n\tfor (let index = pathEntries.length - 1; index >= 0; index--) {\n\t\tconst entry = pathEntries[index]!;\n\t\tif (entry.type === \"compaction\") {\n\t\t\tcompaction = entry;\n\t\t\tcompactionIndex = index;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn compaction === undefined ? [...pathEntries] : [compaction, ...pathEntries.slice(compactionIndex + 1)];\n}\n\nexport function buildContextEntries(pathEntries: readonly Entry[], options: SessionContextBuildOptions = {}): Entry[] {\n\tlet entries = defaultContextEntryTransform(pathEntries);\n\tfor (const transform of options.entryTransforms ?? []) entries = [...transform(entries)];\n\treturn entries;\n}\n\nexport function sessionEntryToContextMessages(\n\tentry: Entry,\n\tindex: number,\n\tentries: readonly Entry[],\n\toptions: SessionContextBuildOptions = {},\n): AgentMessage[] {\n\tif (entry.type === \"message\") {\n\t\tif (entry.message.role === \"assistant\" && entry.message.stopReason === \"deferred\") return [];\n\t\treturn [entry.message];\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn [\n\t\t\tcreateCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp),\n\t\t\t...entry.retainedTail,\n\t\t];\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 Entry[],\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"]}
@@ -0,0 +1,67 @@
1
+ import { createBranchSummaryMessage, createCompactionSummaryMessage } from "../messages.js";
2
+ function deriveSessionContextState(pathEntries) {
3
+ let thinkingLevel = "off";
4
+ let model = null;
5
+ let activeToolNames = null;
6
+ for (const entry of pathEntries) {
7
+ if (entry.type === "thinking_level_change") {
8
+ thinkingLevel = entry.thinkingLevel;
9
+ }
10
+ else if (entry.type === "model_change") {
11
+ model = { provider: entry.provider, modelId: entry.modelId };
12
+ }
13
+ else if (entry.type === "message" && entry.message.role === "assistant") {
14
+ model = { provider: entry.message.provider, modelId: entry.message.model };
15
+ }
16
+ else if (entry.type === "active_tools_change") {
17
+ activeToolNames = [...entry.activeToolNames];
18
+ }
19
+ }
20
+ return { thinkingLevel, model, activeToolNames };
21
+ }
22
+ export function defaultContextEntryTransform(pathEntries) {
23
+ let compaction;
24
+ let compactionIndex = -1;
25
+ for (let index = pathEntries.length - 1; index >= 0; index--) {
26
+ const entry = pathEntries[index];
27
+ if (entry.type === "compaction") {
28
+ compaction = entry;
29
+ compactionIndex = index;
30
+ break;
31
+ }
32
+ }
33
+ return compaction === undefined ? [...pathEntries] : [compaction, ...pathEntries.slice(compactionIndex + 1)];
34
+ }
35
+ export function buildContextEntries(pathEntries, options = {}) {
36
+ let entries = defaultContextEntryTransform(pathEntries);
37
+ for (const transform of options.entryTransforms ?? [])
38
+ entries = [...transform(entries)];
39
+ return entries;
40
+ }
41
+ export function sessionEntryToContextMessages(entry, index, entries, options = {}) {
42
+ if (entry.type === "message") {
43
+ if (entry.message.role === "assistant" && entry.message.stopReason === "deferred")
44
+ return [];
45
+ return [entry.message];
46
+ }
47
+ if (entry.type === "compaction") {
48
+ return [
49
+ createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp),
50
+ ...entry.retainedTail,
51
+ ];
52
+ }
53
+ if (entry.type === "branch_summary" && entry.summary) {
54
+ return [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];
55
+ }
56
+ if (entry.type === "custom") {
57
+ return [...(options.entryProjectors?.[entry.customType]?.(entry, index, entries) ?? [])];
58
+ }
59
+ return [];
60
+ }
61
+ export function buildSessionContext(pathEntries, options = {}) {
62
+ const state = deriveSessionContextState(pathEntries);
63
+ const contextEntries = buildContextEntries(pathEntries, options);
64
+ const messages = contextEntries.flatMap((entry, index) => sessionEntryToContextMessages(entry, index, contextEntries, options));
65
+ return { ...state, messages };
66
+ }
67
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/harness/session/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"../../types.ts\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage } from \"../messages.ts\";\nimport type { CompactionEntry, CustomEntry, Entry } from \"./types.ts\";\n\nexport interface SessionContext {\n\tmessages: AgentMessage[];\n\tthinkingLevel: string;\n\tmodel: { provider: string; modelId: string } | null;\n\tactiveToolNames: string[] | null;\n}\n\nexport type ContextEntryTransform = (entries: readonly Entry[]) => readonly Entry[];\n\nexport type CustomEntryContextMessageProjector = (\n\tentry: CustomEntry,\n\tindex: number,\n\tentries: readonly Entry[],\n) => readonly AgentMessage[] | undefined;\n\nexport interface SessionContextBuildOptions {\n\tentryTransforms?: readonly ContextEntryTransform[];\n\tentryProjectors?: Readonly<Record<string, CustomEntryContextMessageProjector>>;\n}\n\nfunction deriveSessionContextState(pathEntries: readonly Entry[]): 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 Entry[]): Entry[] {\n\tlet compaction: CompactionEntry | undefined;\n\tlet compactionIndex = -1;\n\tfor (let index = pathEntries.length - 1; index >= 0; index--) {\n\t\tconst entry = pathEntries[index]!;\n\t\tif (entry.type === \"compaction\") {\n\t\t\tcompaction = entry;\n\t\t\tcompactionIndex = index;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn compaction === undefined ? [...pathEntries] : [compaction, ...pathEntries.slice(compactionIndex + 1)];\n}\n\nexport function buildContextEntries(pathEntries: readonly Entry[], options: SessionContextBuildOptions = {}): Entry[] {\n\tlet entries = defaultContextEntryTransform(pathEntries);\n\tfor (const transform of options.entryTransforms ?? []) entries = [...transform(entries)];\n\treturn entries;\n}\n\nexport function sessionEntryToContextMessages(\n\tentry: Entry,\n\tindex: number,\n\tentries: readonly Entry[],\n\toptions: SessionContextBuildOptions = {},\n): AgentMessage[] {\n\tif (entry.type === \"message\") {\n\t\tif (entry.message.role === \"assistant\" && entry.message.stopReason === \"deferred\") return [];\n\t\treturn [entry.message];\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn [\n\t\t\tcreateCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp),\n\t\t\t...entry.retainedTail,\n\t\t];\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 Entry[],\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"]}
@@ -0,0 +1,7 @@
1
+ export * from "./context.ts";
2
+ export type { JsonlSessionCreateOptions, JsonlSessionListOptions, JsonlSessionMetadata, JsonlSessionRepoFileSystem, JsonlSessionRepoOptions, JsonlV4Header, } from "./jsonl.ts";
3
+ export { JsonlSessionRepo } from "./jsonl.ts";
4
+ export * from "./memory.ts";
5
+ export * from "./session.ts";
6
+ export * from "./types.ts";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/harness/session/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,YAAY,EACX,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,uBAAuB,EACvB,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./context.ts\";\nexport type {\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tJsonlSessionRepoFileSystem,\n\tJsonlSessionRepoOptions,\n\tJsonlV4Header,\n} from \"./jsonl.ts\";\nexport { JsonlSessionRepo } from \"./jsonl.ts\";\nexport * from \"./memory.ts\";\nexport * from \"./session.ts\";\nexport * from \"./types.ts\";\n"]}
@@ -0,0 +1,6 @@
1
+ export * from "./context.js";
2
+ export { JsonlSessionRepo } from "./jsonl.js";
3
+ export * from "./memory.js";
4
+ export * from "./session.js";
5
+ export * from "./types.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/harness/session/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAS7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./context.ts\";\nexport type {\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tJsonlSessionRepoFileSystem,\n\tJsonlSessionRepoOptions,\n\tJsonlV4Header,\n} from \"./jsonl.ts\";\nexport { JsonlSessionRepo } from \"./jsonl.ts\";\nexport * from \"./memory.ts\";\nexport * from \"./session.ts\";\nexport * from \"./types.ts\";\n"]}
@@ -0,0 +1,10 @@
1
+ import { type Result } from "../../types.ts";
2
+ import type { SessionMutation } from "../state.ts";
3
+ import { JsonlDecodeError } from "./errors.ts";
4
+ import type { JsonlSessionMetadata, JsonlV4Header } from "./types.ts";
5
+ export declare function parseHeader(line: string): Result<JsonlV4Header, JsonlDecodeError>;
6
+ export declare function encodeHeader(header: JsonlV4Header): string;
7
+ export declare function metadataFromHeader(header: JsonlV4Header, path: string, modifiedAt: number): JsonlSessionMetadata;
8
+ export declare function parseMutation(line: string): Result<SessionMutation, JsonlDecodeError>;
9
+ export declare function encodeMutation(mutation: SessionMutation): string;
10
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiGtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAOjF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE1D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,oBAAoB,CAchH;AA2FD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAOrF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAWhE","sourcesContent":["import { err, ok, type Result } from \"../../types.ts\";\nimport type { SessionMutation } from \"../state.ts\";\nimport type { Entry, LaneRecord } from \"../types.ts\";\nimport { JsonlDecodeError } from \"./errors.ts\";\nimport type { JsonlSessionMetadata, JsonlV4Header } from \"./types.ts\";\n\nconst ENTRY_TYPES = new Set<Entry[\"type\"]>([\n\t\"message\",\n\t\"model_change\",\n\t\"thinking_level_change\",\n\t\"active_tools_change\",\n\t\"compaction\",\n\t\"branch_summary\",\n\t\"custom\",\n]);\nconst RECORD_TYPES = new Set<LaneRecord[\"type\"]>([\n\t\"operation_started\",\n\t\"abort_requested\",\n\t\"operation_finished\",\n\t\"step_attempt\",\n\t\"tool_started\",\n\t\"queue_enqueued\",\n\t\"queue_cancelled\",\n\t\"write_deferred\",\n\t\"usage\",\n]);\nconst OPERATION_KINDS = new Set([\"run\", \"compaction\", \"navigation\"]);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseObject(line: string): Record<string, unknown> {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow new JsonlDecodeError(\"syntax\", \"is not valid JSON\", error instanceof Error ? error : undefined);\n\t}\n\tif (!isObject(value)) throw new JsonlDecodeError(\"schema\", \"is not a JSON object\");\n\treturn value;\n}\n\nfunction requireString(value: unknown, field: string): string {\n\tif (typeof value !== \"string\") throw new JsonlDecodeError(\"schema\", `has invalid ${field}`);\n\treturn value;\n}\n\nfunction requireSequence(value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) <= 0) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid seq\");\n\t}\n\treturn value as number;\n}\n\nfunction requireTimestamp(value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid timestamp\");\n\t}\n\treturn value as number;\n}\n\nfunction requireNullableId(value: unknown, field: string): string | null {\n\tif (value !== null && typeof value !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", `has invalid ${field}`);\n\t}\n\treturn value as string | null;\n}\n\nfunction decodeHeader(line: string): JsonlV4Header {\n\tconst value = parseObject(line);\n\tif (value.kind !== \"header\") throw new JsonlDecodeError(\"schema\", \"is not a header\");\n\tif (value.version !== 4) throw new JsonlDecodeError(\"schema\", \"has unsupported session version\");\n\tconst parentSessionId = value.parentSessionId;\n\tif (parentSessionId !== undefined && typeof parentSessionId !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid parentSessionId\");\n\t}\n\tconst legacyParentSessionPath = value.legacyParentSessionPath;\n\tif (legacyParentSessionPath !== undefined && typeof legacyParentSessionPath !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid legacyParentSessionPath\");\n\t}\n\tif (parentSessionId !== undefined && legacyParentSessionPath !== undefined) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has both parentSessionId and legacyParentSessionPath\");\n\t}\n\tconst metadataValue = value.metadata;\n\tif (metadataValue !== undefined && !isObject(metadataValue)) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid metadata\");\n\t}\n\tconst metadata = metadataValue as JsonlV4Header[\"metadata\"];\n\treturn {\n\t\tkind: \"header\",\n\t\tversion: 4,\n\t\tid: requireString(value.id, \"id\"),\n\t\tcreatedAt: requireTimestamp(value.createdAt),\n\t\tcwd: requireString(value.cwd, \"cwd\"),\n\t\tparentSessionId,\n\t\tlegacyParentSessionPath,\n\t\tmetadata,\n\t};\n}\n\nexport function parseHeader(line: string): Result<JsonlV4Header, JsonlDecodeError> {\n\ttry {\n\t\treturn ok<JsonlV4Header, JsonlDecodeError>(decodeHeader(line));\n\t} catch (error) {\n\t\tif (error instanceof JsonlDecodeError) return err<JsonlV4Header, JsonlDecodeError>(error);\n\t\tthrow error;\n\t}\n}\n\nexport function encodeHeader(header: JsonlV4Header): string {\n\treturn `${JSON.stringify(header)}\\n`;\n}\n\nexport function metadataFromHeader(header: JsonlV4Header, path: string, modifiedAt: number): JsonlSessionMetadata {\n\treturn {\n\t\tid: header.id,\n\t\tcreatedAt: header.createdAt,\n\t\tcwd: header.cwd,\n\t\tpath,\n\t\tmodifiedAt,\n\t\tsourceFormat: 4,\n\t\t...(header.parentSessionId === undefined ? {} : { parentSessionId: header.parentSessionId }),\n\t\t...(header.legacyParentSessionPath === undefined\n\t\t\t? {}\n\t\t\t: { legacyParentSessionPath: header.legacyParentSessionPath }),\n\t\t...(header.metadata === undefined ? {} : { metadata: header.metadata }),\n\t};\n}\n\nfunction parseEntryMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"entry\" }> {\n\tconst lane = value.lane === undefined ? undefined : requireString(value.lane, \"lane\");\n\tconst id = requireString(value.id, \"id\");\n\tconst type = requireString(value.type, \"entry type\");\n\tif (!ENTRY_TYPES.has(type as Entry[\"type\"])) {\n\t\tthrow new JsonlDecodeError(\"schema\", `has unknown entry type ${type}`);\n\t}\n\tconst parentId = requireNullableId(value.parentId, \"parentId\");\n\tconst timestamp = requireTimestamp(value.timestamp);\n\tif (type === \"custom\") requireString(value.customType, \"customType\");\n\tconst { kind: _kind, lane: _lane, ...entryFields } = value;\n\tconst entry = { ...entryFields, id, type, parentId, seq, timestamp } as unknown as Entry;\n\treturn lane === undefined ? { kind: \"entry\", entry } : { kind: \"entry\", lane, entry };\n}\n\nfunction parseRecordMutation(\n\tvalue: Record<string, unknown>,\n\tseq: number,\n): Extract<SessionMutation, { kind: \"record\" }> {\n\tconst id = requireString(value.id, \"id\");\n\tconst lane = requireString(value.lane, \"lane\");\n\tconst type = requireString(value.type, \"record type\");\n\tif (!RECORD_TYPES.has(type as LaneRecord[\"type\"])) {\n\t\tthrow new JsonlDecodeError(\"schema\", `has unknown record type ${type}`);\n\t}\n\tconst timestamp = requireTimestamp(value.timestamp);\n\tif (type === \"operation_started\") {\n\t\tif (!isObject(value.intent)) throw new JsonlDecodeError(\"schema\", \"has invalid intent\");\n\t\tconst operationKind = requireString(value.intent.kind, \"operation kind\");\n\t\tif (!OPERATION_KINDS.has(operationKind)) {\n\t\t\tthrow new JsonlDecodeError(\"schema\", `has unknown operation kind ${operationKind}`);\n\t\t}\n\t}\n\tif (type === \"operation_finished\") requireString(value.runId, \"runId\");\n\tconst { kind: _kind, ...recordFields } = value;\n\treturn {\n\t\tkind: \"record\",\n\t\trecord: { ...recordFields, id, lane, type, seq, timestamp } as unknown as LaneRecord,\n\t};\n}\n\nfunction parseLaneMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"lane\" }> {\n\treturn {\n\t\tkind: \"lane\",\n\t\tseq,\n\t\tlane: requireString(value.lane, \"lane\"),\n\t\tleafId: requireNullableId(value.leafId, \"leafId\"),\n\t};\n}\n\nfunction parseFactMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"fact\" }> {\n\tif (value.fact === \"name\") {\n\t\tif (value.name !== undefined && typeof value.name !== \"string\") {\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid name\");\n\t\t}\n\t\treturn { kind: \"fact\", seq, fact: \"name\", name: value.name };\n\t}\n\tif (value.fact === \"label\") {\n\t\tif (value.label !== undefined && typeof value.label !== \"string\") {\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid label\");\n\t\t}\n\t\treturn {\n\t\t\tkind: \"fact\",\n\t\t\tseq,\n\t\t\tfact: \"label\",\n\t\t\ttargetId: requireString(value.targetId, \"targetId\"),\n\t\t\tlabel: value.label,\n\t\t};\n\t}\n\tthrow new JsonlDecodeError(\"schema\", \"has unknown fact type\");\n}\n\nfunction decodeMutation(line: string): SessionMutation {\n\tconst value = parseObject(line);\n\tconst seq = requireSequence(value.seq);\n\tswitch (value.kind) {\n\t\tcase \"entry\":\n\t\t\treturn parseEntryMutation(value, seq);\n\t\tcase \"record\":\n\t\t\treturn parseRecordMutation(value, seq);\n\t\tcase \"lane\":\n\t\t\treturn parseLaneMutation(value, seq);\n\t\tcase \"fact\":\n\t\t\treturn parseFactMutation(value, seq);\n\t\tdefault:\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has unknown mutation kind\");\n\t}\n}\n\nexport function parseMutation(line: string): Result<SessionMutation, JsonlDecodeError> {\n\ttry {\n\t\treturn ok<SessionMutation, JsonlDecodeError>(decodeMutation(line));\n\t} catch (error) {\n\t\tif (error instanceof JsonlDecodeError) return err<SessionMutation, JsonlDecodeError>(error);\n\t\tthrow error;\n\t}\n}\n\nexport function encodeMutation(mutation: SessionMutation): string {\n\tswitch (mutation.kind) {\n\t\tcase \"entry\":\n\t\t\treturn `${JSON.stringify({ kind: \"entry\", lane: mutation.lane, ...mutation.entry })}\\n`;\n\t\tcase \"record\":\n\t\t\treturn `${JSON.stringify({ kind: \"record\", ...mutation.record })}\\n`;\n\t\tcase \"lane\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t\tcase \"fact\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t}\n}\n"]}
@@ -0,0 +1,229 @@
1
+ import { err, ok } from "../../types.js";
2
+ import { JsonlDecodeError } from "./errors.js";
3
+ const ENTRY_TYPES = new Set([
4
+ "message",
5
+ "model_change",
6
+ "thinking_level_change",
7
+ "active_tools_change",
8
+ "compaction",
9
+ "branch_summary",
10
+ "custom",
11
+ ]);
12
+ const RECORD_TYPES = new Set([
13
+ "operation_started",
14
+ "abort_requested",
15
+ "operation_finished",
16
+ "step_attempt",
17
+ "tool_started",
18
+ "queue_enqueued",
19
+ "queue_cancelled",
20
+ "write_deferred",
21
+ "usage",
22
+ ]);
23
+ const OPERATION_KINDS = new Set(["run", "compaction", "navigation"]);
24
+ function isObject(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+ function parseObject(line) {
28
+ let value;
29
+ try {
30
+ value = JSON.parse(line);
31
+ }
32
+ catch (error) {
33
+ throw new JsonlDecodeError("syntax", "is not valid JSON", error instanceof Error ? error : undefined);
34
+ }
35
+ if (!isObject(value))
36
+ throw new JsonlDecodeError("schema", "is not a JSON object");
37
+ return value;
38
+ }
39
+ function requireString(value, field) {
40
+ if (typeof value !== "string")
41
+ throw new JsonlDecodeError("schema", `has invalid ${field}`);
42
+ return value;
43
+ }
44
+ function requireSequence(value) {
45
+ if (!Number.isSafeInteger(value) || value <= 0) {
46
+ throw new JsonlDecodeError("schema", "has invalid seq");
47
+ }
48
+ return value;
49
+ }
50
+ function requireTimestamp(value) {
51
+ if (!Number.isSafeInteger(value) || value < 0) {
52
+ throw new JsonlDecodeError("schema", "has invalid timestamp");
53
+ }
54
+ return value;
55
+ }
56
+ function requireNullableId(value, field) {
57
+ if (value !== null && typeof value !== "string") {
58
+ throw new JsonlDecodeError("schema", `has invalid ${field}`);
59
+ }
60
+ return value;
61
+ }
62
+ function decodeHeader(line) {
63
+ const value = parseObject(line);
64
+ if (value.kind !== "header")
65
+ throw new JsonlDecodeError("schema", "is not a header");
66
+ if (value.version !== 4)
67
+ throw new JsonlDecodeError("schema", "has unsupported session version");
68
+ const parentSessionId = value.parentSessionId;
69
+ if (parentSessionId !== undefined && typeof parentSessionId !== "string") {
70
+ throw new JsonlDecodeError("schema", "has invalid parentSessionId");
71
+ }
72
+ const legacyParentSessionPath = value.legacyParentSessionPath;
73
+ if (legacyParentSessionPath !== undefined && typeof legacyParentSessionPath !== "string") {
74
+ throw new JsonlDecodeError("schema", "has invalid legacyParentSessionPath");
75
+ }
76
+ if (parentSessionId !== undefined && legacyParentSessionPath !== undefined) {
77
+ throw new JsonlDecodeError("schema", "has both parentSessionId and legacyParentSessionPath");
78
+ }
79
+ const metadataValue = value.metadata;
80
+ if (metadataValue !== undefined && !isObject(metadataValue)) {
81
+ throw new JsonlDecodeError("schema", "has invalid metadata");
82
+ }
83
+ const metadata = metadataValue;
84
+ return {
85
+ kind: "header",
86
+ version: 4,
87
+ id: requireString(value.id, "id"),
88
+ createdAt: requireTimestamp(value.createdAt),
89
+ cwd: requireString(value.cwd, "cwd"),
90
+ parentSessionId,
91
+ legacyParentSessionPath,
92
+ metadata,
93
+ };
94
+ }
95
+ export function parseHeader(line) {
96
+ try {
97
+ return ok(decodeHeader(line));
98
+ }
99
+ catch (error) {
100
+ if (error instanceof JsonlDecodeError)
101
+ return err(error);
102
+ throw error;
103
+ }
104
+ }
105
+ export function encodeHeader(header) {
106
+ return `${JSON.stringify(header)}\n`;
107
+ }
108
+ export function metadataFromHeader(header, path, modifiedAt) {
109
+ return {
110
+ id: header.id,
111
+ createdAt: header.createdAt,
112
+ cwd: header.cwd,
113
+ path,
114
+ modifiedAt,
115
+ sourceFormat: 4,
116
+ ...(header.parentSessionId === undefined ? {} : { parentSessionId: header.parentSessionId }),
117
+ ...(header.legacyParentSessionPath === undefined
118
+ ? {}
119
+ : { legacyParentSessionPath: header.legacyParentSessionPath }),
120
+ ...(header.metadata === undefined ? {} : { metadata: header.metadata }),
121
+ };
122
+ }
123
+ function parseEntryMutation(value, seq) {
124
+ const lane = value.lane === undefined ? undefined : requireString(value.lane, "lane");
125
+ const id = requireString(value.id, "id");
126
+ const type = requireString(value.type, "entry type");
127
+ if (!ENTRY_TYPES.has(type)) {
128
+ throw new JsonlDecodeError("schema", `has unknown entry type ${type}`);
129
+ }
130
+ const parentId = requireNullableId(value.parentId, "parentId");
131
+ const timestamp = requireTimestamp(value.timestamp);
132
+ if (type === "custom")
133
+ requireString(value.customType, "customType");
134
+ const { kind: _kind, lane: _lane, ...entryFields } = value;
135
+ const entry = { ...entryFields, id, type, parentId, seq, timestamp };
136
+ return lane === undefined ? { kind: "entry", entry } : { kind: "entry", lane, entry };
137
+ }
138
+ function parseRecordMutation(value, seq) {
139
+ const id = requireString(value.id, "id");
140
+ const lane = requireString(value.lane, "lane");
141
+ const type = requireString(value.type, "record type");
142
+ if (!RECORD_TYPES.has(type)) {
143
+ throw new JsonlDecodeError("schema", `has unknown record type ${type}`);
144
+ }
145
+ const timestamp = requireTimestamp(value.timestamp);
146
+ if (type === "operation_started") {
147
+ if (!isObject(value.intent))
148
+ throw new JsonlDecodeError("schema", "has invalid intent");
149
+ const operationKind = requireString(value.intent.kind, "operation kind");
150
+ if (!OPERATION_KINDS.has(operationKind)) {
151
+ throw new JsonlDecodeError("schema", `has unknown operation kind ${operationKind}`);
152
+ }
153
+ }
154
+ if (type === "operation_finished")
155
+ requireString(value.runId, "runId");
156
+ const { kind: _kind, ...recordFields } = value;
157
+ return {
158
+ kind: "record",
159
+ record: { ...recordFields, id, lane, type, seq, timestamp },
160
+ };
161
+ }
162
+ function parseLaneMutation(value, seq) {
163
+ return {
164
+ kind: "lane",
165
+ seq,
166
+ lane: requireString(value.lane, "lane"),
167
+ leafId: requireNullableId(value.leafId, "leafId"),
168
+ };
169
+ }
170
+ function parseFactMutation(value, seq) {
171
+ if (value.fact === "name") {
172
+ if (value.name !== undefined && typeof value.name !== "string") {
173
+ throw new JsonlDecodeError("schema", "has invalid name");
174
+ }
175
+ return { kind: "fact", seq, fact: "name", name: value.name };
176
+ }
177
+ if (value.fact === "label") {
178
+ if (value.label !== undefined && typeof value.label !== "string") {
179
+ throw new JsonlDecodeError("schema", "has invalid label");
180
+ }
181
+ return {
182
+ kind: "fact",
183
+ seq,
184
+ fact: "label",
185
+ targetId: requireString(value.targetId, "targetId"),
186
+ label: value.label,
187
+ };
188
+ }
189
+ throw new JsonlDecodeError("schema", "has unknown fact type");
190
+ }
191
+ function decodeMutation(line) {
192
+ const value = parseObject(line);
193
+ const seq = requireSequence(value.seq);
194
+ switch (value.kind) {
195
+ case "entry":
196
+ return parseEntryMutation(value, seq);
197
+ case "record":
198
+ return parseRecordMutation(value, seq);
199
+ case "lane":
200
+ return parseLaneMutation(value, seq);
201
+ case "fact":
202
+ return parseFactMutation(value, seq);
203
+ default:
204
+ throw new JsonlDecodeError("schema", "has unknown mutation kind");
205
+ }
206
+ }
207
+ export function parseMutation(line) {
208
+ try {
209
+ return ok(decodeMutation(line));
210
+ }
211
+ catch (error) {
212
+ if (error instanceof JsonlDecodeError)
213
+ return err(error);
214
+ throw error;
215
+ }
216
+ }
217
+ export function encodeMutation(mutation) {
218
+ switch (mutation.kind) {
219
+ case "entry":
220
+ return `${JSON.stringify({ kind: "entry", lane: mutation.lane, ...mutation.entry })}\n`;
221
+ case "record":
222
+ return `${JSON.stringify({ kind: "record", ...mutation.record })}\n`;
223
+ case "lane":
224
+ return `${JSON.stringify(mutation)}\n`;
225
+ case "fact":
226
+ return `${JSON.stringify(mutation)}\n`;
227
+ }
228
+ }
229
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,gBAAgB,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,WAAW,GAAG,IAAI,GAAG,CAAgB;IAC1C,SAAS;IACT,cAAc;IACd,uBAAuB;IACvB,qBAAqB;IACrB,YAAY;IACZ,gBAAgB;IAChB,QAAQ;CACR,CAAC,CAAC;AACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAqB;IAChD,mBAAmB;IACnB,iBAAiB;IACjB,oBAAoB;IACpB,cAAc;IACd,cAAc;IACd,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,OAAO;CACP,CAAC,CAAC;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;AAErE,SAAS,QAAQ,CAAC,KAAc,EAAoC;IACnE,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,SAAS,WAAW,CAAC,IAAY,EAA2B;IAC3D,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvG,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;IACnF,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,aAAa,CAAC,KAAc,EAAE,KAAa,EAAU;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,eAAe,KAAK,EAAE,CAAC,CAAC;IAC5F,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,eAAe,CAAC,KAAc,EAAU;IAChD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAK,KAAgB,IAAI,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAe,CAAC;AAAA,CACvB;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAU;IACjD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAK,KAAgB,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAe,CAAC;AAAA,CACvB;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAE,KAAa,EAAiB;IACxE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACjD,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,eAAe,KAAK,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,KAAsB,CAAC;AAAA,CAC9B;AAED,SAAS,YAAY,CAAC,IAAY,EAAiB;IAClD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IACrF,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,iCAAiC,CAAC,CAAC;IACjG,MAAM,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;IAC9C,IAAI,eAAe,KAAK,SAAS,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QAC1E,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,6BAA6B,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,uBAAuB,GAAG,KAAK,CAAC,uBAAuB,CAAC;IAC9D,IAAI,uBAAuB,KAAK,SAAS,IAAI,OAAO,uBAAuB,KAAK,QAAQ,EAAE,CAAC;QAC1F,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,qCAAqC,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,eAAe,KAAK,SAAS,IAAI,uBAAuB,KAAK,SAAS,EAAE,CAAC;QAC5E,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,sDAAsD,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,CAAC;IACrC,IAAI,aAAa,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,QAAQ,GAAG,aAA0C,CAAC;IAC5D,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,CAAC;QACV,EAAE,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC;QACjC,SAAS,EAAE,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC;QAC5C,GAAG,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC;QACpC,eAAe;QACf,uBAAuB;QACvB,QAAQ;KACR,CAAC;AAAA,CACF;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAA2C;IAClF,IAAI,CAAC;QACJ,OAAO,EAAE,CAAkC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,gBAAgB;YAAE,OAAO,GAAG,CAAkC,KAAK,CAAC,CAAC;QAC1F,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD;AAED,MAAM,UAAU,YAAY,CAAC,MAAqB,EAAU;IAC3D,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;AAAA,CACrC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAqB,EAAE,IAAY,EAAE,UAAkB,EAAwB;IACjH,OAAO;QACN,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,IAAI;QACJ,UAAU;QACV,YAAY,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC;QAC5F,GAAG,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS;YAC/C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,EAAE,CAAC;QAC/D,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;KACvE,CAAC;AAAA,CACF;AAED,SAAS,kBAAkB,CAAC,KAA8B,EAAE,GAAW,EAA+C;IACrH,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtF,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACrD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAqB,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,IAAI,KAAK,QAAQ;QAAE,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACrE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,WAAW,EAAE,GAAG,KAAK,CAAC;IAC3D,MAAM,KAAK,GAAG,EAAE,GAAG,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAsB,CAAC;IACzF,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAAA,CACtF;AAED,SAAS,mBAAmB,CAC3B,KAA8B,EAC9B,GAAW,EACoC;IAC/C,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACtD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAA0B,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,IAAI,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QACxF,MAAM,aAAa,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QACzE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,8BAA8B,aAAa,EAAE,CAAC,CAAC;QACrF,CAAC;IACF,CAAC;IACD,IAAI,IAAI,KAAK,oBAAoB;QAAE,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACvE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,YAAY,EAAE,GAAG,KAAK,CAAC;IAC/C,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAA2B;KACpF,CAAC;AAAA,CACF;AAED,SAAS,iBAAiB,CAAC,KAA8B,EAAE,GAAW,EAA8C;IACnH,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,GAAG;QACH,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;QACvC,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;KACjD,CAAC;AAAA,CACF;AAED,SAAS,iBAAiB,CAAC,KAA8B,EAAE,GAAW,EAA8C;IACnH,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAClE,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO;YACN,IAAI,EAAE,MAAM;YACZ,GAAG;YACH,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC;YACnD,KAAK,EAAE,KAAK,CAAC,KAAK;SAClB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC;AAAA,CAC9D;AAED,SAAS,cAAc,CAAC,IAAY,EAAmB;IACtD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,OAAO;YACX,OAAO,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACvC,KAAK,QAAQ;YACZ,OAAO,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACxC,KAAK,MAAM;YACV,OAAO,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,KAAK,MAAM;YACV,OAAO,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC;YACC,MAAM,IAAI,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;IACpE,CAAC;AAAA,CACD;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAA6C;IACtF,IAAI,CAAC;QACJ,OAAO,EAAE,CAAoC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,gBAAgB;YAAE,OAAO,GAAG,CAAoC,KAAK,CAAC,CAAC;QAC5F,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD;AAED,MAAM,UAAU,cAAc,CAAC,QAAyB,EAAU;IACjE,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,OAAO;YACX,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC;QACzF,KAAK,QAAQ;YACZ,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC;QACtE,KAAK,MAAM;YACV,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;QACxC,KAAK,MAAM;YACV,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;IACzC,CAAC;AAAA,CACD","sourcesContent":["import { err, ok, type Result } from \"../../types.ts\";\nimport type { SessionMutation } from \"../state.ts\";\nimport type { Entry, LaneRecord } from \"../types.ts\";\nimport { JsonlDecodeError } from \"./errors.ts\";\nimport type { JsonlSessionMetadata, JsonlV4Header } from \"./types.ts\";\n\nconst ENTRY_TYPES = new Set<Entry[\"type\"]>([\n\t\"message\",\n\t\"model_change\",\n\t\"thinking_level_change\",\n\t\"active_tools_change\",\n\t\"compaction\",\n\t\"branch_summary\",\n\t\"custom\",\n]);\nconst RECORD_TYPES = new Set<LaneRecord[\"type\"]>([\n\t\"operation_started\",\n\t\"abort_requested\",\n\t\"operation_finished\",\n\t\"step_attempt\",\n\t\"tool_started\",\n\t\"queue_enqueued\",\n\t\"queue_cancelled\",\n\t\"write_deferred\",\n\t\"usage\",\n]);\nconst OPERATION_KINDS = new Set([\"run\", \"compaction\", \"navigation\"]);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseObject(line: string): Record<string, unknown> {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch (error) {\n\t\tthrow new JsonlDecodeError(\"syntax\", \"is not valid JSON\", error instanceof Error ? error : undefined);\n\t}\n\tif (!isObject(value)) throw new JsonlDecodeError(\"schema\", \"is not a JSON object\");\n\treturn value;\n}\n\nfunction requireString(value: unknown, field: string): string {\n\tif (typeof value !== \"string\") throw new JsonlDecodeError(\"schema\", `has invalid ${field}`);\n\treturn value;\n}\n\nfunction requireSequence(value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) <= 0) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid seq\");\n\t}\n\treturn value as number;\n}\n\nfunction requireTimestamp(value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid timestamp\");\n\t}\n\treturn value as number;\n}\n\nfunction requireNullableId(value: unknown, field: string): string | null {\n\tif (value !== null && typeof value !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", `has invalid ${field}`);\n\t}\n\treturn value as string | null;\n}\n\nfunction decodeHeader(line: string): JsonlV4Header {\n\tconst value = parseObject(line);\n\tif (value.kind !== \"header\") throw new JsonlDecodeError(\"schema\", \"is not a header\");\n\tif (value.version !== 4) throw new JsonlDecodeError(\"schema\", \"has unsupported session version\");\n\tconst parentSessionId = value.parentSessionId;\n\tif (parentSessionId !== undefined && typeof parentSessionId !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid parentSessionId\");\n\t}\n\tconst legacyParentSessionPath = value.legacyParentSessionPath;\n\tif (legacyParentSessionPath !== undefined && typeof legacyParentSessionPath !== \"string\") {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid legacyParentSessionPath\");\n\t}\n\tif (parentSessionId !== undefined && legacyParentSessionPath !== undefined) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has both parentSessionId and legacyParentSessionPath\");\n\t}\n\tconst metadataValue = value.metadata;\n\tif (metadataValue !== undefined && !isObject(metadataValue)) {\n\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid metadata\");\n\t}\n\tconst metadata = metadataValue as JsonlV4Header[\"metadata\"];\n\treturn {\n\t\tkind: \"header\",\n\t\tversion: 4,\n\t\tid: requireString(value.id, \"id\"),\n\t\tcreatedAt: requireTimestamp(value.createdAt),\n\t\tcwd: requireString(value.cwd, \"cwd\"),\n\t\tparentSessionId,\n\t\tlegacyParentSessionPath,\n\t\tmetadata,\n\t};\n}\n\nexport function parseHeader(line: string): Result<JsonlV4Header, JsonlDecodeError> {\n\ttry {\n\t\treturn ok<JsonlV4Header, JsonlDecodeError>(decodeHeader(line));\n\t} catch (error) {\n\t\tif (error instanceof JsonlDecodeError) return err<JsonlV4Header, JsonlDecodeError>(error);\n\t\tthrow error;\n\t}\n}\n\nexport function encodeHeader(header: JsonlV4Header): string {\n\treturn `${JSON.stringify(header)}\\n`;\n}\n\nexport function metadataFromHeader(header: JsonlV4Header, path: string, modifiedAt: number): JsonlSessionMetadata {\n\treturn {\n\t\tid: header.id,\n\t\tcreatedAt: header.createdAt,\n\t\tcwd: header.cwd,\n\t\tpath,\n\t\tmodifiedAt,\n\t\tsourceFormat: 4,\n\t\t...(header.parentSessionId === undefined ? {} : { parentSessionId: header.parentSessionId }),\n\t\t...(header.legacyParentSessionPath === undefined\n\t\t\t? {}\n\t\t\t: { legacyParentSessionPath: header.legacyParentSessionPath }),\n\t\t...(header.metadata === undefined ? {} : { metadata: header.metadata }),\n\t};\n}\n\nfunction parseEntryMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"entry\" }> {\n\tconst lane = value.lane === undefined ? undefined : requireString(value.lane, \"lane\");\n\tconst id = requireString(value.id, \"id\");\n\tconst type = requireString(value.type, \"entry type\");\n\tif (!ENTRY_TYPES.has(type as Entry[\"type\"])) {\n\t\tthrow new JsonlDecodeError(\"schema\", `has unknown entry type ${type}`);\n\t}\n\tconst parentId = requireNullableId(value.parentId, \"parentId\");\n\tconst timestamp = requireTimestamp(value.timestamp);\n\tif (type === \"custom\") requireString(value.customType, \"customType\");\n\tconst { kind: _kind, lane: _lane, ...entryFields } = value;\n\tconst entry = { ...entryFields, id, type, parentId, seq, timestamp } as unknown as Entry;\n\treturn lane === undefined ? { kind: \"entry\", entry } : { kind: \"entry\", lane, entry };\n}\n\nfunction parseRecordMutation(\n\tvalue: Record<string, unknown>,\n\tseq: number,\n): Extract<SessionMutation, { kind: \"record\" }> {\n\tconst id = requireString(value.id, \"id\");\n\tconst lane = requireString(value.lane, \"lane\");\n\tconst type = requireString(value.type, \"record type\");\n\tif (!RECORD_TYPES.has(type as LaneRecord[\"type\"])) {\n\t\tthrow new JsonlDecodeError(\"schema\", `has unknown record type ${type}`);\n\t}\n\tconst timestamp = requireTimestamp(value.timestamp);\n\tif (type === \"operation_started\") {\n\t\tif (!isObject(value.intent)) throw new JsonlDecodeError(\"schema\", \"has invalid intent\");\n\t\tconst operationKind = requireString(value.intent.kind, \"operation kind\");\n\t\tif (!OPERATION_KINDS.has(operationKind)) {\n\t\t\tthrow new JsonlDecodeError(\"schema\", `has unknown operation kind ${operationKind}`);\n\t\t}\n\t}\n\tif (type === \"operation_finished\") requireString(value.runId, \"runId\");\n\tconst { kind: _kind, ...recordFields } = value;\n\treturn {\n\t\tkind: \"record\",\n\t\trecord: { ...recordFields, id, lane, type, seq, timestamp } as unknown as LaneRecord,\n\t};\n}\n\nfunction parseLaneMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"lane\" }> {\n\treturn {\n\t\tkind: \"lane\",\n\t\tseq,\n\t\tlane: requireString(value.lane, \"lane\"),\n\t\tleafId: requireNullableId(value.leafId, \"leafId\"),\n\t};\n}\n\nfunction parseFactMutation(value: Record<string, unknown>, seq: number): Extract<SessionMutation, { kind: \"fact\" }> {\n\tif (value.fact === \"name\") {\n\t\tif (value.name !== undefined && typeof value.name !== \"string\") {\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid name\");\n\t\t}\n\t\treturn { kind: \"fact\", seq, fact: \"name\", name: value.name };\n\t}\n\tif (value.fact === \"label\") {\n\t\tif (value.label !== undefined && typeof value.label !== \"string\") {\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has invalid label\");\n\t\t}\n\t\treturn {\n\t\t\tkind: \"fact\",\n\t\t\tseq,\n\t\t\tfact: \"label\",\n\t\t\ttargetId: requireString(value.targetId, \"targetId\"),\n\t\t\tlabel: value.label,\n\t\t};\n\t}\n\tthrow new JsonlDecodeError(\"schema\", \"has unknown fact type\");\n}\n\nfunction decodeMutation(line: string): SessionMutation {\n\tconst value = parseObject(line);\n\tconst seq = requireSequence(value.seq);\n\tswitch (value.kind) {\n\t\tcase \"entry\":\n\t\t\treturn parseEntryMutation(value, seq);\n\t\tcase \"record\":\n\t\t\treturn parseRecordMutation(value, seq);\n\t\tcase \"lane\":\n\t\t\treturn parseLaneMutation(value, seq);\n\t\tcase \"fact\":\n\t\t\treturn parseFactMutation(value, seq);\n\t\tdefault:\n\t\t\tthrow new JsonlDecodeError(\"schema\", \"has unknown mutation kind\");\n\t}\n}\n\nexport function parseMutation(line: string): Result<SessionMutation, JsonlDecodeError> {\n\ttry {\n\t\treturn ok<SessionMutation, JsonlDecodeError>(decodeMutation(line));\n\t} catch (error) {\n\t\tif (error instanceof JsonlDecodeError) return err<SessionMutation, JsonlDecodeError>(error);\n\t\tthrow error;\n\t}\n}\n\nexport function encodeMutation(mutation: SessionMutation): string {\n\tswitch (mutation.kind) {\n\t\tcase \"entry\":\n\t\t\treturn `${JSON.stringify({ kind: \"entry\", lane: mutation.lane, ...mutation.entry })}\\n`;\n\t\tcase \"record\":\n\t\t\treturn `${JSON.stringify({ kind: \"record\", ...mutation.record })}\\n`;\n\t\tcase \"lane\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t\tcase \"fact\":\n\t\t\treturn `${JSON.stringify(mutation)}\\n`;\n\t}\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { FileError, Result } from "../../types.ts";
2
+ import { SessionError } from "../types.ts";
3
+ export declare class JsonlDecodeError extends Error {
4
+ readonly kind: "syntax" | "schema";
5
+ constructor(kind: "syntax" | "schema", message: string, cause?: Error);
6
+ }
7
+ export declare function fileResult<T>(result: Result<T, FileError>, message: string): T;
8
+ export declare function invalidFile(path: string, line: number, cause: Error): SessionError;
9
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAEnC,YAAY,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAIpE;CACD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAS9E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,CAElF","sourcesContent":["import type { FileError, Result } from \"../../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport class JsonlDecodeError extends Error {\n\treadonly kind: \"syntax\" | \"schema\";\n\n\tconstructor(kind: \"syntax\" | \"schema\", message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"JsonlDecodeError\";\n\t\tthis.kind = kind;\n\t}\n}\n\nexport function fileResult<T>(result: Result<T, FileError>, message: string): T {\n\tif (!result.ok) {\n\t\tthrow new SessionError(\n\t\t\tresult.error.code === \"not_found\" ? \"not_found\" : \"storage\",\n\t\t\t`${message}: ${result.error.message}`,\n\t\t\tresult.error,\n\t\t);\n\t}\n\treturn result.value;\n}\n\nexport function invalidFile(path: string, line: number, cause: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL v4 session ${path}: line ${line} ${cause.message}`, cause);\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import { SessionError } from "../types.js";
2
+ export class JsonlDecodeError extends Error {
3
+ kind;
4
+ constructor(kind, message, cause) {
5
+ super(message, cause === undefined ? undefined : { cause });
6
+ this.name = "JsonlDecodeError";
7
+ this.kind = kind;
8
+ }
9
+ }
10
+ export function fileResult(result, message) {
11
+ if (!result.ok) {
12
+ throw new SessionError(result.error.code === "not_found" ? "not_found" : "storage", `${message}: ${result.error.message}`, result.error);
13
+ }
14
+ return result.value;
15
+ }
16
+ export function invalidFile(path, line, cause) {
17
+ return new SessionError("invalid_entry", `Invalid JSONL v4 session ${path}: line ${line} ${cause.message}`, cause);
18
+ }
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACjC,IAAI,CAAsB;IAEnC,YAAY,IAAyB,EAAE,OAAe,EAAE,KAAa,EAAE;QACtE,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;CACD;AAED,MAAM,UAAU,UAAU,CAAI,MAA4B,EAAE,OAAe,EAAK;IAC/E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,YAAY,CACrB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,EAC3D,GAAG,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EACrC,MAAM,CAAC,KAAK,CACZ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC;AAAA,CACpB;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY,EAAE,KAAY,EAAgB;IACnF,OAAO,IAAI,YAAY,CAAC,eAAe,EAAE,4BAA4B,IAAI,UAAU,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAAA,CACnH","sourcesContent":["import type { FileError, Result } from \"../../types.ts\";\nimport { SessionError } from \"../types.ts\";\n\nexport class JsonlDecodeError extends Error {\n\treadonly kind: \"syntax\" | \"schema\";\n\n\tconstructor(kind: \"syntax\" | \"schema\", message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"JsonlDecodeError\";\n\t\tthis.kind = kind;\n\t}\n}\n\nexport function fileResult<T>(result: Result<T, FileError>, message: string): T {\n\tif (!result.ok) {\n\t\tthrow new SessionError(\n\t\t\tresult.error.code === \"not_found\" ? \"not_found\" : \"storage\",\n\t\t\t`${message}: ${result.error.message}`,\n\t\t\tresult.error,\n\t\t);\n\t}\n\treturn result.value;\n}\n\nexport function invalidFile(path: string, line: number, cause: Error): SessionError {\n\treturn new SessionError(\"invalid_entry\", `Invalid JSONL v4 session ${path}: line ${line} ${cause.message}`, cause);\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { Session } from "../session.ts";
2
+ import { type ForkOptions, type SessionRepo } from "../types.ts";
3
+ import { JsonlSessionStorage } from "./storage.ts";
4
+ import type { JsonlSessionCreateOptions, JsonlSessionListOptions, JsonlSessionMetadata, JsonlSessionRepoOptions } from "./types.ts";
5
+ export declare function listJsonlSessionMetadata(options: JsonlSessionRepoOptions, query?: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
6
+ export declare function loadJsonlSessionStorage(options: JsonlSessionRepoOptions, metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage>;
7
+ export declare class JsonlSessionRepo implements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions> {
8
+ private readonly fs;
9
+ private readonly sessionsRootInput;
10
+ private readonly activeCreateDestinations;
11
+ private rootPromise;
12
+ constructor(options: JsonlSessionRepoOptions);
13
+ create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>>;
14
+ open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>>;
15
+ list(options?: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
16
+ delete(metadata: JsonlSessionMetadata): Promise<void>;
17
+ fork(source: JsonlSessionMetadata, options: ForkOptions & JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>>;
18
+ private loadStorage;
19
+ private resolveCreateDestination;
20
+ private claimCreateDestination;
21
+ private prepareCreate;
22
+ private listDirect;
23
+ private sessionIdExists;
24
+ private sessionDirectory;
25
+ private root;
26
+ }
27
+ //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/repo.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,OAAO,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAgB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EACX,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EAEpB,uBAAuB,EAEvB,MAAM,YAAY,CAAC;AAmDpB,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,uBAA4B,GACjC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAmBjC;AAED,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAOD,qBAAa,gBACZ,YAAW,WAAW,CAAC,oBAAoB,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;IAEhG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA6B;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAqB;IAC9D,OAAO,CAAC,WAAW,CAA8B;IAEjD,YAAY,OAAO,EAAE,uBAAuB,EAG3C;IAEK,MAAM,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAMvF;IAEK,IAAI,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAEjF;IAEK,IAAI,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAEjF;IAEK,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;IAEK,IAAI,CACT,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,WAAW,GAAG,yBAAyB,GAC9C,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAWxC;YAEa,WAAW;YAIX,wBAAwB;YAYxB,sBAAsB;YAgBtB,aAAa;YAgCb,UAAU;YAIV,eAAe;YAQf,gBAAgB;IAO9B,OAAO,CAAC,IAAI;CAMZ","sourcesContent":["import { uuidv7 } from \"@sammorrowdrums/mcpi-ai\";\nimport { assertJsonSerializable, Session } from \"../session.ts\";\nimport { type ForkOptions, SessionError, type SessionRepo } from \"../types.ts\";\nimport { metadataFromHeader, parseHeader } from \"./codec.ts\";\nimport { fileResult } from \"./errors.ts\";\nimport { JsonlSessionStorage } from \"./storage.ts\";\nimport type {\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tJsonlSessionRepoFileSystem,\n\tJsonlSessionRepoOptions,\n\tJsonlV4Header,\n} from \"./types.ts\";\n\nconst SESSION_ID_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;\n\nfunction validateSessionId(id: string): void {\n\tif (!SESSION_ID_PATTERN.test(id)) {\n\t\tthrow new SessionError(\n\t\t\t\"invalid_payload\",\n\t\t\t\"Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t}\n}\n\nfunction jsonlSessionDirectoryName(cwd: string): string {\n\treturn `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n}\n\nasync function jsonlSessionsRoot(options: JsonlSessionRepoOptions): Promise<string> {\n\treturn fileResult(\n\t\tawait options.fs.absolutePath(options.sessionsRoot),\n\t\t`Failed to resolve sessions root ${options.sessionsRoot}`,\n\t);\n}\n\nasync function jsonlSessionDirectory(\n\tfs: JsonlSessionRepoFileSystem,\n\tsessionsRoot: string,\n\tcwd: string,\n): Promise<string> {\n\treturn fileResult(\n\t\tawait fs.joinPath([sessionsRoot, jsonlSessionDirectoryName(cwd)]),\n\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t);\n}\n\nasync function jsonlSessionDirectories(options: JsonlSessionRepoOptions, cwd?: string): Promise<string[]> {\n\tconst sessionsRoot = await jsonlSessionsRoot(options);\n\tif (cwd !== undefined) {\n\t\tconst resolvedCwd = fileResult(await options.fs.absolutePath(cwd), `Failed to resolve session cwd ${cwd}`);\n\t\tconst directory = await jsonlSessionDirectory(options.fs, sessionsRoot, resolvedCwd);\n\t\treturn fileResult(await options.fs.exists(directory), `Failed to check sessions directory ${directory}`)\n\t\t\t? [directory]\n\t\t\t: [];\n\t}\n\tif (!fileResult(await options.fs.exists(sessionsRoot), `Failed to check sessions directory ${sessionsRoot}`))\n\t\treturn [];\n\treturn fileResult(await options.fs.listDir(sessionsRoot), `Failed to list sessions directory ${sessionsRoot}`)\n\t\t.filter((entry) => entry.kind === \"directory\" || entry.kind === \"symlink\")\n\t\t.map((entry) => entry.path);\n}\n\nexport async function listJsonlSessionMetadata(\n\toptions: JsonlSessionRepoOptions,\n\tquery: JsonlSessionListOptions = {},\n): Promise<JsonlSessionMetadata[]> {\n\tconst metadata: JsonlSessionMetadata[] = [];\n\tfor (const directory of await jsonlSessionDirectories(options, query.cwd)) {\n\t\tconst files = fileResult(\n\t\t\tawait options.fs.listDir(directory),\n\t\t\t`Failed to list sessions directory ${directory}`,\n\t\t).filter((entry) => entry.kind !== \"directory\" && entry.name.endsWith(\".jsonl\"));\n\t\tfor (const file of files) {\n\t\t\tconst [firstLine] = fileResult(\n\t\t\t\tawait options.fs.readTextLines(file.path, { maxLines: 1 }),\n\t\t\t\t`Failed to read session header ${file.path}`,\n\t\t\t);\n\t\t\tif (!firstLine) continue;\n\t\t\tconst headerResult = parseHeader(firstLine);\n\t\t\tif (!headerResult.ok) continue;\n\t\t\tmetadata.push(metadataFromHeader(headerResult.value, file.path, file.mtimeMs));\n\t\t}\n\t}\n\treturn metadata.sort((left, right) => right.modifiedAt - left.modifiedAt);\n}\n\nexport async function loadJsonlSessionStorage(\n\toptions: JsonlSessionRepoOptions,\n\tmetadata: JsonlSessionMetadata,\n): Promise<JsonlSessionStorage> {\n\tif (!fileResult(await options.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)) {\n\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t}\n\tconst storage = await JsonlSessionStorage.load(options.fs, metadata.path);\n\tconst loadedMetadata = await storage.getMetadata();\n\tif (loadedMetadata.id !== metadata.id) {\n\t\tthrow new SessionError(\"invalid_entry\", `Session id does not match header: ${metadata.id}`);\n\t}\n\treturn storage;\n}\n\nfunction sessionFileName(createdAt: number, id: string): string {\n\tconst timestamp = new Date(createdAt).toISOString().replace(/[:.]/g, \"-\");\n\treturn `${timestamp}_${id}.jsonl`;\n}\n\nexport class JsonlSessionRepo\n\timplements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions>\n{\n\tprivate readonly fs: JsonlSessionRepoFileSystem;\n\tprivate readonly sessionsRootInput: string;\n\tprivate readonly activeCreateDestinations = new Set<string>();\n\tprivate rootPromise: Promise<string> | undefined;\n\n\tconstructor(options: JsonlSessionRepoOptions) {\n\t\tthis.fs = options.fs;\n\t\tthis.sessionsRootInput = options.sessionsRoot;\n\t}\n\n\tasync create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst destination = await this.resolveCreateDestination(options);\n\t\treturn this.claimCreateDestination(destination, async () => {\n\t\t\tconst { header, path } = await this.prepareCreate(destination, options);\n\t\t\treturn new Session(await JsonlSessionStorage.create(this.fs, path, header));\n\t\t});\n\t}\n\n\tasync open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>> {\n\t\treturn new Session(await this.loadStorage(metadata));\n\t}\n\n\tasync list(options: JsonlSessionListOptions = {}): Promise<JsonlSessionMetadata[]> {\n\t\treturn this.listDirect(options);\n\t}\n\n\tasync delete(metadata: JsonlSessionMetadata): Promise<void> {\n\t\tfileResult(await this.fs.remove(metadata.path, { force: true }), `Failed to delete session ${metadata.path}`);\n\t}\n\n\tasync fork(\n\t\tsource: JsonlSessionMetadata,\n\t\toptions: ForkOptions & JsonlSessionCreateOptions,\n\t): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst sourceStorage = await this.loadStorage(source);\n\t\tconst createOptions = {\n\t\t\t...options,\n\t\t\tparentSessionId: options.parentSessionId ?? source.id,\n\t\t};\n\t\tconst destination = await this.resolveCreateDestination(createOptions);\n\t\treturn this.claimCreateDestination(destination, async () => {\n\t\t\tconst { header, path } = await this.prepareCreate(destination, createOptions);\n\t\t\treturn new Session(await sourceStorage.fork(path, header, options));\n\t\t});\n\t}\n\n\tprivate async loadStorage(metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage> {\n\t\treturn loadJsonlSessionStorage({ fs: this.fs, sessionsRoot: this.sessionsRootInput }, metadata);\n\t}\n\n\tprivate async resolveCreateDestination(options: JsonlSessionCreateOptions): Promise<{ id: string; cwd: string }> {\n\t\tconst id = options.id ?? uuidv7();\n\t\tvalidateSessionId(id);\n\t\tconst cwd = fileResult(await this.fs.absolutePath(options.cwd), `Failed to resolve session cwd ${options.cwd}`);\n\t\treturn { id, cwd };\n\t}\n\n\t/**\n\t * Prevent same-process create/fork races for one logical destination. The durable filename includes a\n\t * timestamp, so the async filesystem existence check alone can let two concurrent calls both decide the\n\t * same {cwd, id} is free and publish duplicate sessions.\n\t */\n\tprivate async claimCreateDestination<T>(\n\t\tdestination: { id: string; cwd: string },\n\t\toperation: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst key = `${destination.cwd}\\0${destination.id}`;\n\t\tif (this.activeCreateDestinations.has(key)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${destination.id}`);\n\t\t}\n\t\tthis.activeCreateDestinations.add(key);\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} finally {\n\t\t\tthis.activeCreateDestinations.delete(key);\n\t\t}\n\t}\n\n\tprivate async prepareCreate(\n\t\tdestination: { id: string; cwd: string },\n\t\toptions: JsonlSessionCreateOptions,\n\t): Promise<{\n\t\theader: JsonlV4Header;\n\t\tpath: string;\n\t}> {\n\t\tconst { id, cwd } = destination;\n\t\tif (await this.sessionIdExists(id, cwd)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\t}\n\n\t\tconst createdAt = Date.now();\n\t\tconst sessionDirectory = await this.sessionDirectory(cwd);\n\t\tconst path = fileResult(\n\t\t\tawait this.fs.joinPath([sessionDirectory, sessionFileName(createdAt, id)]),\n\t\t\t`Failed to resolve path for session ${id}`,\n\t\t);\n\t\tif (options.metadata !== undefined) assertJsonSerializable(options.metadata);\n\t\tconst header: JsonlV4Header = {\n\t\t\tkind: \"header\",\n\t\t\tversion: 4,\n\t\t\tid,\n\t\t\tcreatedAt,\n\t\t\tcwd,\n\t\t\tparentSessionId: options.parentSessionId,\n\t\t\tmetadata: options.metadata,\n\t\t};\n\t\tfileResult(await this.fs.createDir(sessionDirectory, { recursive: true }), `Failed to create sessions directory`);\n\t\treturn { header, path };\n\t}\n\n\tprivate async listDirect(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]> {\n\t\treturn listJsonlSessionMetadata({ fs: this.fs, sessionsRoot: this.sessionsRootInput }, options);\n\t}\n\n\tprivate async sessionIdExists(id: string, cwd: string): Promise<boolean> {\n\t\tconst suffix = `_${id}.jsonl`;\n\t\tconst directory = await this.sessionDirectory(cwd);\n\t\tif (!fileResult(await this.fs.exists(directory), `Failed to check sessions directory ${directory}`)) return false;\n\t\tconst files = fileResult(await this.fs.listDir(directory), `Failed to list sessions directory ${directory}`);\n\t\treturn files.some((entry) => entry.kind !== \"directory\" && entry.name.endsWith(suffix));\n\t}\n\n\tprivate async sessionDirectory(cwd: string): Promise<string> {\n\t\treturn fileResult(\n\t\t\tawait this.fs.joinPath([await this.root(), jsonlSessionDirectoryName(cwd)]),\n\t\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t\t);\n\t}\n\n\tprivate root(): Promise<string> {\n\t\tthis.rootPromise ??= this.fs\n\t\t\t.absolutePath(this.sessionsRootInput)\n\t\t\t.then((result) => fileResult(result, `Failed to resolve sessions root ${this.sessionsRootInput}`));\n\t\treturn this.rootPromise;\n\t}\n}\n"]}