@sapiom/harness 0.1.5 → 0.2.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 (245) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/dist/cli/args.d.ts +16 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +80 -0
  5. package/dist/cli/args.js.map +1 -0
  6. package/dist/cli/bin.js +30 -66
  7. package/dist/cli/bin.js.map +1 -1
  8. package/dist/cli/consent.js +1 -1
  9. package/dist/cli/settings.d.ts.map +1 -1
  10. package/dist/cli/settings.js +33 -1
  11. package/dist/cli/settings.js.map +1 -1
  12. package/dist/core/adapters/adapter.d.ts +2 -2
  13. package/dist/core/adapters/claude-code.d.ts +60 -2
  14. package/dist/core/adapters/claude-code.d.ts.map +1 -1
  15. package/dist/core/adapters/claude-code.js +119 -32
  16. package/dist/core/adapters/claude-code.js.map +1 -1
  17. package/dist/core/adapters/codex.d.ts +21 -2
  18. package/dist/core/adapters/codex.d.ts.map +1 -1
  19. package/dist/core/adapters/codex.js +53 -2
  20. package/dist/core/adapters/codex.js.map +1 -1
  21. package/dist/core/adapters/detect.d.ts +9 -0
  22. package/dist/core/adapters/detect.d.ts.map +1 -1
  23. package/dist/core/adapters/detect.js +9 -0
  24. package/dist/core/adapters/detect.js.map +1 -1
  25. package/dist/core/api-key-provider.d.ts +79 -0
  26. package/dist/core/api-key-provider.d.ts.map +1 -0
  27. package/dist/core/api-key-provider.js +91 -0
  28. package/dist/core/api-key-provider.js.map +1 -0
  29. package/dist/core/canvas-body.d.ts +28 -28
  30. package/dist/core/canvas-body.d.ts.map +1 -1
  31. package/dist/core/canvas-body.js +117 -55
  32. package/dist/core/canvas-body.js.map +1 -1
  33. package/dist/core/canvas-derive.d.ts +43 -0
  34. package/dist/core/canvas-derive.d.ts.map +1 -0
  35. package/dist/core/canvas-derive.js +95 -0
  36. package/dist/core/canvas-derive.js.map +1 -0
  37. package/dist/core/canvas-enrichment.d.ts +52 -127
  38. package/dist/core/canvas-enrichment.d.ts.map +1 -1
  39. package/dist/core/canvas-enrichment.js +17 -178
  40. package/dist/core/canvas-enrichment.js.map +1 -1
  41. package/dist/core/canvas-graph.d.ts +40 -1
  42. package/dist/core/canvas-graph.d.ts.map +1 -1
  43. package/dist/core/canvas-graph.js +71 -9
  44. package/dist/core/canvas-graph.js.map +1 -1
  45. package/dist/core/canvas-interconnections.d.ts +26 -8
  46. package/dist/core/canvas-interconnections.d.ts.map +1 -1
  47. package/dist/core/canvas-interconnections.js +132 -24
  48. package/dist/core/canvas-interconnections.js.map +1 -1
  49. package/dist/core/canvas-manifest-check.d.ts.map +1 -1
  50. package/dist/core/canvas-manifest-check.js +15 -2
  51. package/dist/core/canvas-manifest-check.js.map +1 -1
  52. package/dist/core/canvas-render.d.ts +8 -15
  53. package/dist/core/canvas-render.d.ts.map +1 -1
  54. package/dist/core/canvas-render.js +29 -41
  55. package/dist/core/canvas-render.js.map +1 -1
  56. package/dist/core/canvas-run-state.d.ts +50 -5
  57. package/dist/core/canvas-run-state.d.ts.map +1 -1
  58. package/dist/core/canvas-run-state.js +176 -11
  59. package/dist/core/canvas-run-state.js.map +1 -1
  60. package/dist/core/canvas-svg.d.ts +9 -4
  61. package/dist/core/canvas-svg.d.ts.map +1 -1
  62. package/dist/core/canvas-svg.js +84 -13
  63. package/dist/core/canvas-svg.js.map +1 -1
  64. package/dist/core/canvas-template.d.ts +9 -10
  65. package/dist/core/canvas-template.d.ts.map +1 -1
  66. package/dist/core/canvas-template.js +57 -20
  67. package/dist/core/canvas-template.js.map +1 -1
  68. package/dist/core/canvas-watcher.d.ts +22 -1
  69. package/dist/core/canvas-watcher.d.ts.map +1 -1
  70. package/dist/core/canvas-watcher.js +111 -30
  71. package/dist/core/canvas-watcher.js.map +1 -1
  72. package/dist/core/collector/normalizer.d.ts +8 -0
  73. package/dist/core/collector/normalizer.d.ts.map +1 -1
  74. package/dist/core/collector/normalizer.js +8 -0
  75. package/dist/core/collector/normalizer.js.map +1 -1
  76. package/dist/core/collector/store.d.ts +75 -1
  77. package/dist/core/collector/store.d.ts.map +1 -1
  78. package/dist/core/collector/store.js +334 -2
  79. package/dist/core/collector/store.js.map +1 -1
  80. package/dist/core/definition-slug-resolver.d.ts +13 -0
  81. package/dist/core/definition-slug-resolver.d.ts.map +1 -1
  82. package/dist/core/definition-slug-resolver.js +29 -0
  83. package/dist/core/definition-slug-resolver.js.map +1 -1
  84. package/dist/core/errors.d.ts +10 -4
  85. package/dist/core/errors.d.ts.map +1 -1
  86. package/dist/core/errors.js +11 -5
  87. package/dist/core/errors.js.map +1 -1
  88. package/dist/core/example-seed.d.ts +8 -0
  89. package/dist/core/example-seed.d.ts.map +1 -1
  90. package/dist/core/example-seed.js +47 -10
  91. package/dist/core/example-seed.js.map +1 -1
  92. package/dist/core/inject/system-prompt.d.ts +8 -0
  93. package/dist/core/inject/system-prompt.d.ts.map +1 -1
  94. package/dist/core/inject/system-prompt.js +3 -1
  95. package/dist/core/inject/system-prompt.js.map +1 -1
  96. package/dist/core/macros.d.ts.map +1 -1
  97. package/dist/core/macros.js +5 -6
  98. package/dist/core/macros.js.map +1 -1
  99. package/dist/core/paths.d.ts +1 -0
  100. package/dist/core/paths.d.ts.map +1 -1
  101. package/dist/core/paths.js +1 -0
  102. package/dist/core/paths.js.map +1 -1
  103. package/dist/core/record-archive.d.ts +187 -0
  104. package/dist/core/record-archive.d.ts.map +1 -0
  105. package/dist/core/record-archive.js +465 -0
  106. package/dist/core/record-archive.js.map +1 -0
  107. package/dist/core/rehydration.d.ts +72 -0
  108. package/dist/core/rehydration.d.ts.map +1 -0
  109. package/dist/core/rehydration.js +72 -0
  110. package/dist/core/rehydration.js.map +1 -0
  111. package/dist/core/render-local-run.d.ts +82 -0
  112. package/dist/core/render-local-run.d.ts.map +1 -0
  113. package/dist/core/render-local-run.js +105 -0
  114. package/dist/core/render-local-run.js.map +1 -0
  115. package/dist/core/render-log-slice.d.ts +30 -0
  116. package/dist/core/render-log-slice.d.ts.map +1 -0
  117. package/dist/core/render-log-slice.js +49 -0
  118. package/dist/core/render-log-slice.js.map +1 -0
  119. package/dist/core/render-run-state.d.ts.map +1 -1
  120. package/dist/core/render-run-state.js +30 -49
  121. package/dist/core/render-run-state.js.map +1 -1
  122. package/dist/core/resume-brief.d.ts +130 -0
  123. package/dist/core/resume-brief.d.ts.map +1 -0
  124. package/dist/core/resume-brief.js +404 -0
  125. package/dist/core/resume-brief.js.map +1 -0
  126. package/dist/core/rolling-summary.d.ts +130 -0
  127. package/dist/core/rolling-summary.d.ts.map +1 -0
  128. package/dist/core/rolling-summary.js +227 -0
  129. package/dist/core/rolling-summary.js.map +1 -0
  130. package/dist/core/run-local-bootstrap.d.ts +93 -0
  131. package/dist/core/run-local-bootstrap.d.ts.map +1 -0
  132. package/dist/core/run-local-bootstrap.js +155 -0
  133. package/dist/core/run-local-bootstrap.js.map +1 -0
  134. package/dist/core/run-state.d.ts +16 -1
  135. package/dist/core/run-state.d.ts.map +1 -1
  136. package/dist/core/run-state.js +75 -25
  137. package/dist/core/run-state.js.map +1 -1
  138. package/dist/core/session-manager.d.ts +11 -3
  139. package/dist/core/session-manager.d.ts.map +1 -1
  140. package/dist/core/session-manager.js +56 -7
  141. package/dist/core/session-manager.js.map +1 -1
  142. package/dist/core/session-record.d.ts +165 -0
  143. package/dist/core/session-record.d.ts.map +1 -0
  144. package/dist/core/session-record.js +470 -0
  145. package/dist/core/session-record.js.map +1 -0
  146. package/dist/core/spawn-target.d.ts +12 -0
  147. package/dist/core/spawn-target.d.ts.map +1 -0
  148. package/dist/core/spawn-target.js +187 -0
  149. package/dist/core/spawn-target.js.map +1 -0
  150. package/dist/core/stub-feedback.d.ts +52 -0
  151. package/dist/core/stub-feedback.d.ts.map +1 -0
  152. package/dist/core/stub-feedback.js +33 -0
  153. package/dist/core/stub-feedback.js.map +1 -0
  154. package/dist/core/task-manager.d.ts.map +1 -1
  155. package/dist/core/task-manager.js +9 -1
  156. package/dist/core/task-manager.js.map +1 -1
  157. package/dist/core/template-catalog.d.ts +48 -0
  158. package/dist/core/template-catalog.d.ts.map +1 -0
  159. package/dist/core/template-catalog.js +338 -0
  160. package/dist/core/template-catalog.js.map +1 -0
  161. package/dist/core/workflow-registry.d.ts.map +1 -1
  162. package/dist/core/workflow-registry.js +2 -2
  163. package/dist/core/workflow-registry.js.map +1 -1
  164. package/dist/core/workspace-watcher.d.ts.map +1 -1
  165. package/dist/core/workspace-watcher.js +4 -3
  166. package/dist/core/workspace-watcher.js.map +1 -1
  167. package/dist/index.d.ts +12 -0
  168. package/dist/index.d.ts.map +1 -1
  169. package/dist/index.js +21 -0
  170. package/dist/index.js.map +1 -1
  171. package/dist/profiles/default.d.ts.map +1 -1
  172. package/dist/profiles/default.js +9 -14
  173. package/dist/profiles/default.js.map +1 -1
  174. package/dist/server/actions.d.ts +139 -0
  175. package/dist/server/actions.d.ts.map +1 -0
  176. package/dist/server/actions.js +430 -0
  177. package/dist/server/actions.js.map +1 -0
  178. package/dist/server/auth-routes.d.ts +92 -0
  179. package/dist/server/auth-routes.d.ts.map +1 -0
  180. package/dist/server/auth-routes.js +151 -0
  181. package/dist/server/auth-routes.js.map +1 -0
  182. package/dist/server/canvas.d.ts.map +1 -1
  183. package/dist/server/canvas.js +7 -7
  184. package/dist/server/canvas.js.map +1 -1
  185. package/dist/server/fs.d.ts.map +1 -1
  186. package/dist/server/fs.js +17 -1
  187. package/dist/server/fs.js.map +1 -1
  188. package/dist/server/index.d.ts +12 -3
  189. package/dist/server/index.d.ts.map +1 -1
  190. package/dist/server/index.js +0 -0
  191. package/dist/server/index.js.map +1 -1
  192. package/dist/server/ingest.d.ts +11 -0
  193. package/dist/server/ingest.d.ts.map +1 -1
  194. package/dist/server/ingest.js +1 -0
  195. package/dist/server/ingest.js.map +1 -1
  196. package/dist/server/macros.d.ts +4 -6
  197. package/dist/server/macros.d.ts.map +1 -1
  198. package/dist/server/macros.js.map +1 -1
  199. package/dist/server/rest.d.ts +18 -6
  200. package/dist/server/rest.d.ts.map +1 -1
  201. package/dist/server/rest.js +185 -50
  202. package/dist/server/rest.js.map +1 -1
  203. package/dist/server/runs.d.ts +16 -15
  204. package/dist/server/runs.d.ts.map +1 -1
  205. package/dist/server/runs.js +15 -80
  206. package/dist/server/runs.js.map +1 -1
  207. package/dist/server/static.d.ts +15 -1
  208. package/dist/server/static.d.ts.map +1 -1
  209. package/dist/server/static.js +54 -4
  210. package/dist/server/static.js.map +1 -1
  211. package/dist/server/templates.d.ts +24 -0
  212. package/dist/server/templates.d.ts.map +1 -0
  213. package/dist/server/templates.js +54 -0
  214. package/dist/server/templates.js.map +1 -0
  215. package/dist/shared/types.d.ts +558 -66
  216. package/dist/shared/types.d.ts.map +1 -1
  217. package/dist/shared/types.js +19 -11
  218. package/dist/shared/types.js.map +1 -1
  219. package/dist/web/assets/index-DC65cskq.js +446 -0
  220. package/dist/web/assets/index-xtD-3FFa.css +32 -0
  221. package/dist/web/canvas/sess-boot/index.html +305 -0
  222. package/dist/web/index.html +4 -9
  223. package/package.json +8 -4
  224. package/dist/core/canvas-enrich.d.ts +0 -115
  225. package/dist/core/canvas-enrich.d.ts.map +0 -1
  226. package/dist/core/canvas-enrich.js +0 -186
  227. package/dist/core/canvas-enrich.js.map +0 -1
  228. package/dist/core/run-spend.d.ts +0 -55
  229. package/dist/core/run-spend.d.ts.map +0 -1
  230. package/dist/core/run-spend.js +0 -109
  231. package/dist/core/run-spend.js.map +0 -1
  232. package/dist/core/run-transactions.d.ts +0 -60
  233. package/dist/core/run-transactions.d.ts.map +0 -1
  234. package/dist/core/run-transactions.js +0 -129
  235. package/dist/core/run-transactions.js.map +0 -1
  236. package/dist/profiles/canvas-guidelines.d.ts +0 -20
  237. package/dist/profiles/canvas-guidelines.d.ts.map +0 -1
  238. package/dist/profiles/canvas-guidelines.js +0 -48
  239. package/dist/profiles/canvas-guidelines.js.map +0 -1
  240. package/dist/server/skills.d.ts +0 -51
  241. package/dist/server/skills.d.ts.map +0 -1
  242. package/dist/server/skills.js +0 -312
  243. package/dist/server/skills.js.map +0 -1
  244. package/dist/web/assets/index-DM84J83Y.js +0 -234
  245. package/dist/web/assets/index-DemtWW4L.css +0 -32
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Rebuilds a past session's transcript from OUR OWN recorded events, not from
3
+ * any vendor's transcript file — which is what makes it work identically for
4
+ * claude-code and codex (both normalize into the same AnalyticsEvent stream;
5
+ * see core/collector/normalizer.ts and core/collector/codex-tailer.ts) and
6
+ * makes it survive the vendor deleting, rotating, or never writing its own
7
+ * history.
8
+ *
9
+ * The fold: `prompt.submitted` opens a turn, `tool.call`s attach to the open
10
+ * turn, `turn.completed` closes it with assistantText / model / usage. Events
11
+ * that arrive with no open turn still get a turn of their own (with a null
12
+ * prompt) rather than being dropped — a record whose recording started
13
+ * mid-turn is a real case, and silently discarding those events would show a
14
+ * session as emptier than it was.
15
+ *
16
+ * ORDERING — the one non-obvious rule. `AnalyticsEvent.seq` is NOT a
17
+ * session-lifetime ordering key: it comes from an in-memory per-boot counter
18
+ * (core/collector/seq.ts), so it restarts at 1 on every harness boot and every
19
+ * fresh pty. A real resumed session reads seq 1, 2 then 1, 1, 1. Sorting by
20
+ * `seq` alone would interleave a resume's events into the middle of the first
21
+ * run. Sort by `(ts, seq)`: wall-clock time orders the epochs, and `seq` only
22
+ * breaks ties inside one of them. A restart is a new epoch, never loss.
23
+ *
24
+ * HONESTY — what this can never recover, surfaced as `limitations` on the
25
+ * record so the UI can say it out loud:
26
+ * - `tool.call` stores a size-capped `toolResponseSummary`; large tool
27
+ * outputs are gone from our log.
28
+ * - `turn.completed` carries only the Stop hook's LAST assistant message
29
+ * (see the header of core/collector/transcript.ts for why), so assistant
30
+ * narration *between* tool calls is absent. The tool-call stream fills the
31
+ * chronology, not the prose.
32
+ * - Codex's rollout has no equivalent of that field at all, so its turns
33
+ * carry tool calls and prompts but no assistant text.
34
+ *
35
+ * RETENTION — why a record can outlive the events it was folded from. The
36
+ * ndjson is capped at 50 MB / 30 days (collector/store-retention.ts), so a
37
+ * record built only from it would silently disappear a month later. At session
38
+ * end a compacted copy is written to `~/.sapiom/harness/records/` and the
39
+ * reader falls back to it once the log no longer covers the conversation
40
+ * (core/record-archive.ts; the rule is spelled out on `read` below).
41
+ */
42
+ import * as os from "node:os";
43
+ import * as path from "node:path";
44
+ import { projectDirsFor } from "./adapters/claude-code.js";
45
+ import { PAYLOAD_TRUNCATION_MARKER } from "./collector/normalizer.js";
46
+ import { readLastAssistantTurn } from "./collector/transcript.js";
47
+ function stringOrNull(value) {
48
+ return typeof value === "string" && value.length > 0 ? value : null;
49
+ }
50
+ function numberOrNull(value) {
51
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
52
+ }
53
+ function readUsage(value) {
54
+ if (typeof value !== "object" || value === null)
55
+ return null;
56
+ const usage = value;
57
+ const inputTokens = numberOrNull(usage.inputTokens);
58
+ const outputTokens = numberOrNull(usage.outputTokens);
59
+ if (inputTokens === null && outputTokens === null)
60
+ return null;
61
+ return { inputTokens, outputTokens };
62
+ }
63
+ /**
64
+ * Order events for folding: `ts` first, `seq` only as a tiebreak within the
65
+ * same millisecond. See the module header for why `seq` alone is wrong.
66
+ * Array.prototype.sort is stable, so events matching on both keep file order.
67
+ */
68
+ export function sortEventsForFold(events) {
69
+ return [...events].sort((a, b) => {
70
+ if (a.ts !== b.ts)
71
+ return a.ts < b.ts ? -1 : 1;
72
+ const seqA = typeof a.seq === "number" ? a.seq : 0;
73
+ const seqB = typeof b.seq === "number" ? b.seq : 0;
74
+ return seqA - seqB;
75
+ });
76
+ }
77
+ /**
78
+ * Fold an event stream into an ordered {@link SessionRecord}. Pure and
79
+ * synchronous: hand it the events for one session (or for a set of sessions
80
+ * that share an agent session — a resumed conversation) and it returns the
81
+ * transcript. Events for other sessions are tolerated and folded in; the
82
+ * caller is responsible for filtering, which `EventStore.read` does by index.
83
+ */
84
+ export function foldSessionRecord(events, options = {}) {
85
+ const ordered = sortEventsForFold(events);
86
+ const turns = [];
87
+ let open = null;
88
+ let startedAt = null;
89
+ let endedAt = null;
90
+ let cwd = null;
91
+ let harness = options.harness ?? null;
92
+ let agentSessionId = null;
93
+ const seenSessionIds = [];
94
+ /** Move the open turn into `turns`, closed by `completion` or incomplete. */
95
+ function close(completion) {
96
+ if (!open)
97
+ return;
98
+ const payload = completion?.payload ?? {};
99
+ turns.push({
100
+ index: turns.length + 1,
101
+ prompt: open.prompt,
102
+ promptAt: open.promptAt,
103
+ toolCalls: open.toolCalls,
104
+ assistantText: stringOrNull(payload.assistantText),
105
+ model: stringOrNull(payload.model),
106
+ usage: readUsage(payload.usage),
107
+ completedAt: completion?.ts ?? null,
108
+ incomplete: completion === null,
109
+ });
110
+ open = null;
111
+ }
112
+ for (const event of ordered) {
113
+ if (!seenSessionIds.includes(event.harnessSessionId))
114
+ seenSessionIds.push(event.harnessSessionId);
115
+ if (startedAt === null)
116
+ startedAt = event.ts;
117
+ if (harness === null && typeof event.harness === "string")
118
+ harness = event.harness;
119
+ if (agentSessionId === null)
120
+ agentSessionId = stringOrNull(event.agentSessionId);
121
+ const payload = event.payload ?? {};
122
+ switch (event.type) {
123
+ case "session.start":
124
+ if (cwd === null)
125
+ cwd = stringOrNull(payload.cwd);
126
+ break;
127
+ case "prompt.submitted":
128
+ // A prompt arriving while a turn is open means that turn never
129
+ // completed (killed mid-turn, or the user queued another prompt).
130
+ // Keep it, marked incomplete — dropping it would lose real tool calls.
131
+ close(null);
132
+ open = {
133
+ prompt: typeof payload.prompt === "string" ? payload.prompt : "",
134
+ promptAt: event.ts,
135
+ toolCalls: [],
136
+ };
137
+ break;
138
+ case "tool.call": {
139
+ // No enclosing turn: the recording started mid-turn (a resume attaches
140
+ // its hooks after the prompt was already submitted). Open an anonymous
141
+ // turn so the calls are in the chronology, honestly promptless.
142
+ if (!open)
143
+ open = { prompt: null, promptAt: null, toolCalls: [] };
144
+ const responseSummary = stringOrNull(payload.toolResponseSummary);
145
+ open.toolCalls.push({
146
+ name: stringOrNull(payload.toolName),
147
+ input: stringOrNull(payload.toolInput),
148
+ responseSummary,
149
+ responseTruncated: responseSummary !== null && PAYLOAD_TRUNCATION_MARKER.test(responseSummary),
150
+ at: event.ts,
151
+ });
152
+ break;
153
+ }
154
+ case "turn.completed":
155
+ // A completion with nothing open is an agent-initiated turn (or one
156
+ // whose prompt predates our recording) — give it a promptless turn.
157
+ if (!open)
158
+ open = { prompt: null, promptAt: null, toolCalls: [] };
159
+ close(event);
160
+ break;
161
+ case "session.end":
162
+ // Never closes the open turn: a session that ended mid-turn HAS an
163
+ // incomplete trailing turn, and that's what the record should say.
164
+ endedAt = event.ts;
165
+ break;
166
+ default:
167
+ // Every other type (UI-interaction analytics: session.switched,
168
+ // macro.invoked, …) is not part of the conversation. Counted in
169
+ // eventCount, deliberately not rendered as a turn.
170
+ break;
171
+ }
172
+ }
173
+ close(null);
174
+ const merged = options.mergedSessionIds ? [...options.mergedSessionIds] : seenSessionIds;
175
+ return {
176
+ harnessSessionId: options.harnessSessionId ?? merged[0] ?? "",
177
+ mergedSessionIds: merged,
178
+ agentSessionId,
179
+ harness: harness ?? "claude-code",
180
+ cwd,
181
+ startedAt,
182
+ endedAt,
183
+ turns,
184
+ turnCount: turns.filter((turn) => turn.prompt !== null).length,
185
+ eventCount: ordered.length,
186
+ reconstructed: true,
187
+ // Folded from the live log, not read from the archive. The archive is the
188
+ // only thing that sets this (core/record-archive.ts).
189
+ archivedAt: null,
190
+ limitations: computeLimitations(turns),
191
+ };
192
+ }
193
+ /**
194
+ * Derive the record's honest gaps from the turns themselves.
195
+ *
196
+ * A pure function of the turn list, and called again by the reader after any
197
+ * enrichment — deliberately, because gaps can APPEAR as well as disappear when
198
+ * a turn is filled in. Giving a turn that has tool calls its assistant text
199
+ * back closes `missing-assistant-text` and simultaneously opens
200
+ * `assistant-narration-gap` (only the turn's *final* message was ever
201
+ * recorded). Patching individual codes after the fact got that wrong and
202
+ * under-reported a real gap.
203
+ */
204
+ function computeLimitations(turns) {
205
+ const limitations = [];
206
+ if (turns.some((turn) => turn.toolCalls.some((call) => call.responseTruncated))) {
207
+ limitations.push("truncated-tool-output");
208
+ }
209
+ if (turns.some((turn) => turn.toolCalls.length > 0 && turn.assistantText !== null)) {
210
+ limitations.push("assistant-narration-gap");
211
+ }
212
+ if (turns.some((turn) => turn.completedAt !== null && turn.assistantText === null)) {
213
+ limitations.push("missing-assistant-text");
214
+ }
215
+ if (turns.length > 0 && turns[turns.length - 1].incomplete) {
216
+ limitations.push("incomplete-final-turn");
217
+ }
218
+ return limitations;
219
+ }
220
+ /**
221
+ * Resolve a requested id to every harnessSessionId whose events belong in its
222
+ * record. A conversation can span several harness sessions (resuming the same
223
+ * agent session id), and the record should be the conversation, not one of its
224
+ * segments. One hop through the agent-session alias map — deliberately not a
225
+ * transitive closure, which could chain unrelated sessions together.
226
+ */
227
+ function resolveSessionIds(index, id) {
228
+ const ids = [];
229
+ const add = (candidate) => {
230
+ if (!ids.includes(candidate) && index.bySession.has(candidate))
231
+ ids.push(candidate);
232
+ };
233
+ const direct = index.bySession.get(id);
234
+ if (direct) {
235
+ add(id);
236
+ for (const agentSessionId of direct.agentSessionIds) {
237
+ for (const sibling of index.byAgentSession.get(agentSessionId) ?? [])
238
+ add(sibling);
239
+ }
240
+ }
241
+ else {
242
+ // Not a harnessSessionId we know — try it as an agent session id.
243
+ for (const sibling of index.byAgentSession.get(id) ?? [])
244
+ add(sibling);
245
+ }
246
+ // Earliest first, so the record's primary id is where the conversation began.
247
+ return ids.sort((a, b) => {
248
+ const tsA = index.bySession.get(a)?.firstTs ?? "";
249
+ const tsB = index.bySession.get(b)?.firstTs ?? "";
250
+ if (tsA !== tsB)
251
+ return tsA < tsB ? -1 : 1;
252
+ return a < b ? -1 : 1;
253
+ });
254
+ }
255
+ /**
256
+ * Reader over the local event store, and — when one is wired in — over the
257
+ * durable record archive that outlives it (core/record-archive.ts). `read`
258
+ * touches only the byte spans the index attributes to the session, so opening a
259
+ * record never rescans the (up to 50 MB) ndjson; see `read`'s own comment for
260
+ * how it chooses between the two sources.
261
+ *
262
+ * MEMORY CEILING — stated, because a global `(ts, seq)` order can't be produced
263
+ * from a stream: `read` holds the whole session's events at once. That is ONE
264
+ * copy, not two — `sortEventsForFold` copies the array of references, and the
265
+ * turns reference the same payload strings the events do. Measured against a
266
+ * 50 MB log owned by a single session at real event density (~4.3 KB/event):
267
+ * 12k events, ~50 MB of record content against ~52 MB on disk (see
268
+ * session-record.perf.test.ts, which asserts that ratio so a regression that
269
+ * duplicates the data or buffers the raw file gets caught).
270
+ *
271
+ * So the ceiling is "one session's share of a file capped at 50 MB", which for
272
+ * the pathological single-session case is ~50 MB of transient heap in a local
273
+ * server. That is deliberate and bounded rather than unbounded; if a future
274
+ * caller needs a tighter bound (H3's rehydration reading only recent context,
275
+ * say), the fix is a `limit`/most-recent-N on the wire, not a change of order.
276
+ */
277
+ export function createSessionRecordReader(store, options = {}) {
278
+ const archive = options.archive;
279
+ const reader = {
280
+ /**
281
+ * SOURCE SELECTION — the one rule worth reading twice.
282
+ *
283
+ * The ticket asked for "prefer the archive when it exists, fall back to
284
+ * scanning events". Taken literally that is wrong in two directions, so
285
+ * this prefers whichever source still holds the WHOLE conversation, which
286
+ * is the archive exactly when the log no longer does:
287
+ *
288
+ * - Preferring the archive for a session that ended a minute ago serves a
289
+ * compacted excerpt (clipped tool payloads) when the full events are
290
+ * right there. Scanning them is not the expensive thing it would have
291
+ * been before H2's byte-offset index — a record open reads only that
292
+ * session's spans.
293
+ * - A conversation that gets RESUMED keeps writing into the log after its
294
+ * first archive. "Always prefer the archive" would freeze its record at
295
+ * the first exit and quietly hide every later turn.
296
+ *
297
+ * The log holds the whole conversation when it still holds the beginning of
298
+ * it: the sweep truncates oldest-first (store-retention.ts), so coverage is
299
+ * always a suffix — an intact first event means nothing in between was
300
+ * dropped. Events newer than the archive mean the conversation continued,
301
+ * which makes the log the fuller source regardless.
302
+ *
303
+ * Neither source → null. A record that was never recorded and one that was
304
+ * swept both answer 404 today; what changed is that far fewer sessions
305
+ * reach that state.
306
+ */
307
+ async read(id) {
308
+ const archived = archive ? await archive.read(id).catch(() => null) : null;
309
+ if (!archived)
310
+ return reader.readFromEvents(id);
311
+ const index = await store.index();
312
+ const sessionIds = resolveSessionIds(index, id);
313
+ const entries = sessionIds.map((sessionId) => index.bySession.get(sessionId)).filter(isPresent);
314
+ const firstTs = earliest(entries.map((entry) => entry.firstTs));
315
+ const lastTs = latest(entries.map((entry) => entry.lastTs));
316
+ const logHoldsTheBeginning = firstTs !== null && (archived.startedAt === null || firstTs <= archived.startedAt);
317
+ const logHasNewerEvents = lastTs !== null && archived.archivedAt !== null && lastTs > archived.archivedAt;
318
+ if (!logHoldsTheBeginning && !logHasNewerEvents)
319
+ return archived;
320
+ return (await reader.readFromEvents(id)) ?? archived;
321
+ },
322
+ async readFromEvents(id) {
323
+ const index = await store.index();
324
+ const sessionIds = resolveSessionIds(index, id);
325
+ if (sessionIds.length === 0)
326
+ return null;
327
+ const events = [];
328
+ for await (const event of store.read({ harnessSessionId: sessionIds }))
329
+ events.push(event);
330
+ if (events.length === 0)
331
+ return null;
332
+ const record = foldSessionRecord(events, {
333
+ harnessSessionId: sessionIds[0],
334
+ mergedSessionIds: sessionIds,
335
+ });
336
+ if (!options.enrichFinalTurn)
337
+ return record;
338
+ const final = record.turns[record.turns.length - 1];
339
+ // Only ever fills gaps in the last COMPLETED turn — that is the one the
340
+ // vendor's tail-read can speak to (see readLastAssistantTurn). An
341
+ // incomplete trailing turn is skipped on purpose: the transcript's last
342
+ // assistant message then belongs to some earlier turn, and attaching it
343
+ // here would be a fabrication dressed as an enhancement. Nothing our own
344
+ // events recorded is ever overwritten.
345
+ if (!final || final.completedAt === null)
346
+ return record;
347
+ if (final.assistantText !== null && final.model !== null && final.usage !== null)
348
+ return record;
349
+ const enrichment = await options.enrichFinalTurn(record).catch(() => null);
350
+ if (!enrichment)
351
+ return record;
352
+ const enriched = {
353
+ ...final,
354
+ assistantText: final.assistantText ?? enrichment.assistantText,
355
+ model: final.model ?? enrichment.model,
356
+ usage: final.usage ?? enrichment.usage,
357
+ };
358
+ const turns = [...record.turns.slice(0, -1), enriched];
359
+ // Recomputed, not patched: filling a turn can open a gap as well as close
360
+ // one — see computeLimitations.
361
+ return { ...record, turns, limitations: computeLimitations(turns) };
362
+ },
363
+ async turnCounts() {
364
+ const index = await store.index();
365
+ const counts = new Map();
366
+ // Every key — harness-session and agent-session alike — counts the whole
367
+ // CONVERSATION that `read(id)` would fold for it, not the one segment the
368
+ // index happens to file it under. A conversation resumed under a second
369
+ // harness session must not report two different turn counts depending on
370
+ // which id a history row was keyed by; a row's count has to match the
371
+ // record it links to.
372
+ const totalFor = (id) => {
373
+ let total = 0;
374
+ for (const sessionId of resolveSessionIds(index, id)) {
375
+ total += index.bySession.get(sessionId)?.turnCount ?? 0;
376
+ }
377
+ return total;
378
+ };
379
+ for (const harnessSessionId of index.bySession.keys()) {
380
+ counts.set(harnessSessionId, totalFor(harnessSessionId));
381
+ }
382
+ for (const agentSessionId of index.byAgentSession.keys()) {
383
+ counts.set(agentSessionId, totalFor(agentSessionId));
384
+ }
385
+ // Then the archive, for every key the log can no longer speak for. `max`
386
+ // rather than "fill the gaps": a swept conversation's live count has
387
+ // decayed to a fraction of what happened (or to nothing at all), and a
388
+ // resumed one's live count is the larger, more current number. Taking the
389
+ // greater of the two is right in both directions.
390
+ if (archive) {
391
+ const archived = await archive.list().catch((err) => {
392
+ console.error("[harness] archived record counts failed:", err);
393
+ return [];
394
+ });
395
+ for (const entry of archived) {
396
+ for (const key of entry.keys) {
397
+ counts.set(key, Math.max(counts.get(key) ?? 0, entry.turnCount));
398
+ }
399
+ }
400
+ }
401
+ return counts;
402
+ },
403
+ async conversationIds() {
404
+ const index = await store.index();
405
+ const primaries = new Map();
406
+ for (const harnessSessionId of index.bySession.keys()) {
407
+ // resolveSessionIds returns the conversation earliest-first, so its
408
+ // head is the primary id every segment agrees on — which is what
409
+ // dedupes a resumed conversation down to one entry here.
410
+ const [primary] = resolveSessionIds(index, harnessSessionId);
411
+ if (primary === undefined || primaries.has(primary))
412
+ continue;
413
+ primaries.set(primary, latest(resolveSessionIds(index, primary).map((id) => index.bySession.get(id)?.lastTs ?? null)));
414
+ }
415
+ return [...primaries.entries()]
416
+ .sort(([idA, tsA], [idB, tsB]) => {
417
+ if (tsA !== tsB)
418
+ return (tsB ?? "") < (tsA ?? "") ? -1 : 1;
419
+ return idA < idB ? -1 : 1;
420
+ })
421
+ .map(([id]) => id);
422
+ },
423
+ };
424
+ return reader;
425
+ }
426
+ function isPresent(value) {
427
+ return value !== undefined;
428
+ }
429
+ /** The smallest / largest non-null ISO timestamp, or null when there is none.
430
+ * ISO-8601 in UTC sorts lexicographically, which is why these compare strings
431
+ * rather than parsing dates. */
432
+ function earliest(timestamps) {
433
+ return timestamps.reduce((best, ts) => (ts === null ? best : best === null || ts < best ? ts : best), null);
434
+ }
435
+ function latest(timestamps) {
436
+ return timestamps.reduce((best, ts) => (ts === null ? best : best === null || ts > best ? ts : best), null);
437
+ }
438
+ /**
439
+ * Fills the LAST turn's model / usage / assistant text from Claude Code's own
440
+ * transcript, when that file happens to still be on disk — a pure enhancement
441
+ * over what our events recorded, wired in via
442
+ * {@link createSessionRecordReader}'s `enrichFinalTurn`.
443
+ *
444
+ * Everything about it is best-effort by design: wrong harness, no cwd, no
445
+ * agent session id, transcript deleted or rotated → null, and the record
446
+ * renders from our events alone. Reuses `readLastAssistantTurn`, so it reads
447
+ * only the tail of a transcript that can be hundreds of MB.
448
+ */
449
+ export function createClaudeTranscriptEnricher(options = {}) {
450
+ const homeDir = options.homeDir ?? os.homedir();
451
+ return async (record) => {
452
+ if (record.harness !== "claude-code")
453
+ return null;
454
+ if (!record.cwd || !record.agentSessionId)
455
+ return null;
456
+ // projectDirsFor (shared with the adapter's canResume / listPastSessions)
457
+ // handles the trap here: Claude Code stores transcripts under the REALPATH
458
+ // of the project, so a session opened in /tmp/proj lives under
459
+ // `-private-tmp-proj` and a hand-built path silently finds nothing. It
460
+ // returns the resolved candidate first and the raw one as a fallback, so
461
+ // this tries both rather than betting on either.
462
+ for (const projectDir of await projectDirsFor(homeDir, record.cwd)) {
463
+ const turn = await readLastAssistantTurn(path.join(projectDir, `${record.agentSessionId}.jsonl`));
464
+ if (turn)
465
+ return turn;
466
+ }
467
+ return null;
468
+ };
469
+ }
470
+ //# sourceMappingURL=session-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-record.js","sourceRoot":"","sources":["../../src/core/session-record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface SpawnTarget {
2
+ command: string;
3
+ args: string[];
4
+ }
5
+ export interface SpawnTargetDeps {
6
+ platform?: NodeJS.Platform;
7
+ env?: NodeJS.ProcessEnv;
8
+ fileExists?: (p: string) => boolean;
9
+ readText?: (p: string) => string;
10
+ }
11
+ export declare function resolveSpawnTarget(command: string, args: readonly string[], deps?: SpawnTargetDeps): SpawnTarget;
12
+ //# sourceMappingURL=spawn-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-target.d.ts","sourceRoot":"","sources":["../../src/core/spawn-target.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AA8HD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,GAAE,eAAoB,GACzB,WAAW,CAyCb"}