@inflexa-ai/harness 0.42.2 → 0.43.1

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 (199) hide show
  1. package/README.md +2 -2
  2. package/dist/agents/sandbox/shared.d.ts +1 -6
  3. package/dist/agents/sandbox/shared.js +0 -15
  4. package/dist/app/chat-turn.js +13 -1
  5. package/dist/config/environment-stores.d.ts +4 -2
  6. package/dist/contracts/chat-events.d.ts +9 -1
  7. package/dist/contracts/chat-frame.d.ts +3 -3
  8. package/dist/contracts/chat-frame.js +8 -3
  9. package/dist/contracts/chat-parts.d.ts +4 -1
  10. package/dist/contracts/data-profile.d.ts +4 -6
  11. package/dist/contracts/index.d.ts +1 -1
  12. package/dist/contracts/report-blocks.d.ts +1364 -75
  13. package/dist/contracts/report-blocks.js +221 -29
  14. package/dist/contracts/report-reference.js +1 -1
  15. package/dist/contracts/schemas/chat-events.d.ts +13 -0
  16. package/dist/contracts/schemas/chat-events.js +5 -0
  17. package/dist/contracts/schemas/chat-parts.d.ts +2 -2
  18. package/dist/contracts/schemas/index.d.ts +1 -1
  19. package/dist/contracts/schemas/index.js +1 -1
  20. package/dist/execution/input-signature.d.ts +7 -13
  21. package/dist/execution/input-signature.js +0 -0
  22. package/dist/execution/run-canceler.d.ts +3 -2
  23. package/dist/execution/run-canceler.js +3 -2
  24. package/dist/execution/run-event-stream.d.ts +16 -2
  25. package/dist/execution/run-event-stream.js +54 -3
  26. package/dist/index.d.ts +1 -8
  27. package/dist/index.js +5 -16
  28. package/dist/input-scan/enrich.d.ts +0 -2
  29. package/dist/input-scan/enrich.js +0 -1
  30. package/dist/lib/metrics.js +6 -7
  31. package/dist/lib/otel-spans.d.ts +4 -9
  32. package/dist/lib/otel-spans.js +3 -8
  33. package/dist/loop/run-agent.d.ts +4 -2
  34. package/dist/loop/run-agent.js +38 -23
  35. package/dist/memory/thread-history.d.ts +1 -1
  36. package/dist/prompts/report-session.d.ts +1 -1
  37. package/dist/prompts/report-session.js +17 -0
  38. package/dist/provenance/exec-frame.d.ts +1 -1
  39. package/dist/report-model/authoring-grammar.d.ts +416 -17
  40. package/dist/report-model/authoring-grammar.js +25 -4
  41. package/dist/report-model/block-walk.d.ts +20 -4
  42. package/dist/report-model/block-walk.js +64 -7
  43. package/dist/report-model/draft-finish.d.ts +6 -2
  44. package/dist/report-model/draft-finish.js +6 -1
  45. package/dist/report-model/production-resolver.d.ts +8 -0
  46. package/dist/report-model/production-resolver.js +1 -1
  47. package/dist/report-model/validate.d.ts +20 -10
  48. package/dist/report-model/validate.js +29 -13
  49. package/dist/report-render/chart-export.d.ts +52 -0
  50. package/dist/report-render/chart-export.js +192 -0
  51. package/dist/report-render/chart-presets.d.ts +46 -47
  52. package/dist/report-render/chart-presets.js +31 -101
  53. package/dist/report-render/chart-renderers.d.ts +163 -0
  54. package/dist/report-render/chart-renderers.js +1055 -0
  55. package/dist/report-render/chart-toolbox.d.ts +124 -0
  56. package/dist/report-render/chart-toolbox.js +595 -0
  57. package/dist/report-render/chart.d.ts +60 -27
  58. package/dist/report-render/chart.js +2323 -524
  59. package/dist/report-render/design.d.ts +476 -16
  60. package/dist/report-render/design.js +390 -56
  61. package/dist/report-render/figures/common.d.ts +321 -0
  62. package/dist/report-render/figures/common.js +759 -0
  63. package/dist/report-render/figures/dendrogram.d.ts +36 -0
  64. package/dist/report-render/figures/dendrogram.js +130 -0
  65. package/dist/report-render/figures/dense.d.ts +111 -0
  66. package/dist/report-render/figures/dense.js +204 -0
  67. package/dist/report-render/figures/dotplot.d.ts +28 -0
  68. package/dist/report-render/figures/dotplot.js +234 -0
  69. package/dist/report-render/figures/embedding.d.ts +43 -0
  70. package/dist/report-render/figures/embedding.js +360 -0
  71. package/dist/report-render/figures/equal-units.d.ts +101 -0
  72. package/dist/report-render/figures/equal-units.js +215 -0
  73. package/dist/report-render/figures/forest.d.ts +20 -0
  74. package/dist/report-render/figures/forest.js +398 -0
  75. package/dist/report-render/figures/gsea.d.ts +30 -0
  76. package/dist/report-render/figures/gsea.js +391 -0
  77. package/dist/report-render/figures/heatmap.d.ts +40 -0
  78. package/dist/report-render/figures/heatmap.js +395 -0
  79. package/dist/report-render/figures/index.d.ts +135 -0
  80. package/dist/report-render/figures/index.js +100 -0
  81. package/dist/report-render/figures/km.d.ts +16 -0
  82. package/dist/report-render/figures/km.js +406 -0
  83. package/dist/report-render/figures/label-room.d.ts +130 -0
  84. package/dist/report-render/figures/label-room.js +341 -0
  85. package/dist/report-render/figures/locuszoom.d.ts +50 -0
  86. package/dist/report-render/figures/locuszoom.js +675 -0
  87. package/dist/report-render/figures/lollipop.d.ts +16 -0
  88. package/dist/report-render/figures/lollipop.js +247 -0
  89. package/dist/report-render/figures/ma.d.ts +20 -0
  90. package/dist/report-render/figures/ma.js +114 -0
  91. package/dist/report-render/figures/manhattan.d.ts +60 -0
  92. package/dist/report-render/figures/manhattan.js +343 -0
  93. package/dist/report-render/figures/oncoprint.d.ts +41 -0
  94. package/dist/report-render/figures/oncoprint.js +420 -0
  95. package/dist/report-render/figures/pca.d.ts +37 -0
  96. package/dist/report-render/figures/pca.js +241 -0
  97. package/dist/report-render/figures/qq.d.ts +30 -0
  98. package/dist/report-render/figures/qq.js +182 -0
  99. package/dist/report-render/figures/roc.d.ts +17 -0
  100. package/dist/report-render/figures/roc.js +116 -0
  101. package/dist/report-render/figures/sankey.d.ts +48 -0
  102. package/dist/report-render/figures/sankey.js +353 -0
  103. package/dist/report-render/figures/upset.d.ts +60 -0
  104. package/dist/report-render/figures/upset.js +316 -0
  105. package/dist/report-render/figures/volcano.d.ts +25 -0
  106. package/dist/report-render/figures/volcano.js +166 -0
  107. package/dist/report-render/hybrid-svg.d.ts +75 -0
  108. package/dist/report-render/hybrid-svg.js +504 -0
  109. package/dist/report-render/number-format.d.ts +19 -0
  110. package/dist/report-render/number-format.js +25 -1
  111. package/dist/report-render/page.d.ts +41 -4
  112. package/dist/report-render/page.js +247 -12
  113. package/dist/report-render/render.d.ts +13 -4
  114. package/dist/report-render/render.js +37 -6
  115. package/dist/report-render/types.d.ts +23 -2
  116. package/dist/report-render/value-bridge.d.ts +38 -3
  117. package/dist/report-render/value-bridge.js +137 -6
  118. package/dist/report-render/views/chart-view.d.ts +42 -6
  119. package/dist/report-render/views/chart-view.js +70 -9
  120. package/dist/report-render/views/page-view.js +9 -10
  121. package/dist/runtime/assemble.js +2 -2
  122. package/dist/sandbox/client.d.ts +23 -32
  123. package/dist/sandbox/client.js +3 -11
  124. package/dist/sandbox/create-sandbox.d.ts +9 -24
  125. package/dist/sandbox/create-sandbox.js +15 -30
  126. package/dist/sandbox/docker-client.d.ts +12 -22
  127. package/dist/sandbox/docker-client.js +29 -43
  128. package/dist/sandbox/exec.d.ts +58 -0
  129. package/dist/sandbox/exec.js +197 -0
  130. package/dist/sandbox/identity.d.ts +5 -5
  131. package/dist/sandbox/identity.js +7 -10
  132. package/dist/sandbox/k8s-client.d.ts +4 -10
  133. package/dist/sandbox/k8s-client.js +5 -12
  134. package/dist/sandbox/liveness.d.ts +5 -12
  135. package/dist/sandbox/liveness.js +5 -12
  136. package/dist/sandbox/notification-sweep.d.ts +4 -7
  137. package/dist/sandbox/notification-sweep.js +4 -7
  138. package/dist/sandbox/reaper.d.ts +3 -3
  139. package/dist/sandbox/reaper.js +3 -3
  140. package/dist/sandbox/sandbox-error.d.ts +10 -11
  141. package/dist/sandbox/sandbox-error.js +10 -11
  142. package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
  143. package/dist/sandbox/sandbox-step-translate.js +1 -1
  144. package/dist/sandbox/types.d.ts +31 -99
  145. package/dist/sandbox/types.js +17 -84
  146. package/dist/state/active-sandboxes.d.ts +7 -32
  147. package/dist/state/active-sandboxes.js +11 -46
  148. package/dist/state/index.d.ts +1 -2
  149. package/dist/state/index.js +1 -1
  150. package/dist/state/migrations/20261005120000_drop_the_active_exec_tracking.d.ts +7 -0
  151. package/dist/state/migrations/20261005120000_drop_the_active_exec_tracking.js +12 -0
  152. package/dist/state/migrations/index.js +2 -0
  153. package/dist/state/schema.d.ts +1 -5
  154. package/dist/state/schema.js +1 -6
  155. package/dist/state/step-executions.js +1 -2
  156. package/dist/tasks/data-profile.js +7 -20
  157. package/dist/tasks/derive-table-exec.d.ts +4 -14
  158. package/dist/tasks/derive-table-exec.js +9 -24
  159. package/dist/tasks/extract-values.d.ts +2 -2
  160. package/dist/tasks/extract-values.js +4 -7
  161. package/dist/tools/define-tool.d.ts +56 -13
  162. package/dist/tools/define-tool.js +28 -1
  163. package/dist/tools/execute-analysis.js +3 -3
  164. package/dist/tools/report-session/derive-table.d.ts +8 -9
  165. package/dist/tools/report-session/derive-table.js +3 -4
  166. package/dist/tools/report-session/preview-report.js +4 -49
  167. package/dist/tools/sandbox/list-available-packages.d.ts +1 -1
  168. package/dist/tools/sandbox/list-available-packages.js +5 -4
  169. package/dist/tools/workspace/edit-file.d.ts +5 -4
  170. package/dist/tools/workspace/edit-file.js +10 -13
  171. package/dist/tools/workspace/execute-command.d.ts +27 -42
  172. package/dist/tools/workspace/execute-command.js +42 -38
  173. package/dist/tools/workspace/index.d.ts +5 -6
  174. package/dist/tools/workspace/index.js +5 -6
  175. package/dist/tools/workspace/mutator.d.ts +43 -8
  176. package/dist/tools/workspace/mutator.js +45 -34
  177. package/dist/tools/workspace/run-exec.d.ts +3 -12
  178. package/dist/tools/workspace/run-exec.js +7 -24
  179. package/dist/tools/workspace/scan-inputs.d.ts +0 -3
  180. package/dist/tools/workspace/scan-inputs.js +0 -1
  181. package/dist/tools/workspace/write-file.d.ts +12 -3
  182. package/dist/tools/workspace/write-file.js +17 -13
  183. package/dist/workflows/sandbox-step.d.ts +7 -11
  184. package/dist/workflows/sandbox-step.js +24 -27
  185. package/package.json +5 -3
  186. package/dist/sandbox/await-exec.d.ts +0 -143
  187. package/dist/sandbox/await-exec.js +0 -417
  188. package/dist/sandbox/deliver-exec-event.d.ts +0 -25
  189. package/dist/sandbox/deliver-exec-event.js +0 -29
  190. package/dist/sandbox/digest.d.ts +0 -14
  191. package/dist/sandbox/digest.js +0 -17
  192. package/dist/sandbox/exec-id.d.ts +0 -12
  193. package/dist/sandbox/exec-id.js +0 -20
  194. package/dist/sandbox/hmac.d.ts +0 -62
  195. package/dist/sandbox/hmac.js +0 -72
  196. package/dist/sandbox/submit-exec.d.ts +0 -38
  197. package/dist/sandbox/submit-exec.js +0 -80
  198. package/dist/sandbox/watchdog.d.ts +0 -71
  199. package/dist/sandbox/watchdog.js +0 -161
@@ -4,11 +4,12 @@
4
4
  *
5
5
  * A thin adapter over the `WorkspaceMutator` seam (see the harness-durable-runtime / harness-workspace-tools specs): the
6
6
  * mutator owns resolve + confine + hardened host write + provenance. This tool
7
- * only declares the input schema and forwards. `edit_file` rides the same seam.
7
+ * declares the input schema, forwards the write, and carries the record of the
8
+ * write to the fold. `edit_file` rides the same seam.
8
9
  */
9
10
  import { ok } from "neverthrow";
10
11
  import { z } from "zod";
11
- import { defineTool } from "../define-tool.js";
12
+ import { defineTool, withToolCallRecord } from "../define-tool.js";
12
13
  const WriteFileInputSchema = z.object({
13
14
  path: z
14
15
  .string()
@@ -21,9 +22,6 @@ const WriteFileInputSchema = z.object({
21
22
  export function createWriteFileTool(deps) {
22
23
  return defineTool({
23
24
  id: "write_file",
24
- // The mutator wraps the disk mutation in `ctx.runStep` itself, so the
25
- // body runs unwrapped in the workflow body (see the harness-tools spec).
26
- executionMode: "workflow",
27
25
  description: "Write a UTF-8 text file in your working directory. Relative paths " +
28
26
  "resolve against it. The write replaces an existing file whole, and it makes " +
29
27
  "missing parent directories. On success it returns `status: ok`, the absolute " +
@@ -34,13 +32,19 @@ export function createWriteFileTool(deps) {
34
32
  // The path only. `content` is a whole file and must never ride a display
35
33
  // channel, which the emit-site length cap enforces regardless.
36
34
  describeCall: ({ path }) => path,
37
- execute: async ({ path, content }, ctx) => ok(await deps.mutator.writeFile({
38
- path,
39
- content,
40
- toolName: "write_file",
41
- invocationId: ctx.invocationId,
42
- runStep: ctx.runStep,
43
- session: ctx.session,
44
- })),
35
+ execute: async ({ path, content }, ctx) => {
36
+ const write = await deps.mutator.writeFile({
37
+ path,
38
+ content,
39
+ toolName: "write_file",
40
+ invocationId: ctx.invocationId,
41
+ session: ctx.session,
42
+ });
43
+ if (write.status !== "ok")
44
+ return ok(write);
45
+ const { record, ...result } = write;
46
+ return ok(withToolCallRecord(result, record));
47
+ },
48
+ foldCallRecord: (record) => deps.mutator.recordWrite(record),
45
49
  });
46
50
  }
@@ -91,8 +91,7 @@ export interface SandboxStepInput {
91
91
  readonly resources: ResourceSpec;
92
92
  /**
93
93
  * Per-step execution budget in seconds. The body translates this into the
94
- * absolute unix-ms deadline that `awaitExec` honours as its durable
95
- * backstop. Comes from the plan's `step.timeout` (defaulted on the parent
94
+ * absolute unix-ms deadline that each exec of the step honours. Comes from the plan's `step.timeout` (defaulted on the parent
96
95
  * side); falls back to `DEFAULT_STEP_TIMEOUT_SECONDS` when unset so a
97
96
  * client that omits the field still gets a non-pathological ceiling.
98
97
  */
@@ -184,17 +183,13 @@ export interface PostStepArtifacts {
184
183
  /**
185
184
  * Per-step coordinates the workflow body computes once and threads to the
186
185
  * deps factory. The parent supplies the durable inputs; the factory
187
- * supplies the live sandbox ref, the per-call function-id minter, and the
188
- * deadline.
186
+ * supplies the live sandbox ref and the deadline.
189
187
  */
190
188
  export interface SandboxAgentBuildContext {
191
189
  readonly input: SandboxStepInput;
192
190
  readonly session: RunSession;
193
191
  readonly sandbox: SandboxRef;
194
- /** Workflow-id used to scope sandbox callbacks + workspace mutate provenance. */
195
- readonly workflowId: string;
196
192
  readonly stepWritePrefix: string;
197
- readonly nextFunctionId: () => string;
198
193
  readonly deadlineMs: () => number;
199
194
  /**
200
195
  * Step-scoped lineage collector. The body owns one per step and threads
@@ -302,10 +297,11 @@ export interface PostStepContext {
302
297
  export declare function readStepFileMetadata(checkpoint: StepFileMetadata | readonly FileMetadataEntry[]): StepFileMetadata;
303
298
  /**
304
299
  * Build the step's lineage collector from its durable input. The agent loop
305
- * feeds each exec's provenance frame into it; post-step registration reads the
306
- * resulting input/script edges. Reconstructed deterministically on replay — the
307
- * frames it consumes are cached recv outputs and the command strings are cached
308
- * LLM tool-use (see the harness-thread-store and harness-durable-runtime specs).
300
+ * feeds each exec's provenance frame and each file-tool write into it;
301
+ * post-step registration reads the resulting input/script edges.
302
+ * Reconstructed deterministically on replay — the loop folds the call record
303
+ * of each cached tool call into it again, in call order (see the
304
+ * harness-durable-runtime spec).
309
305
  *
310
306
  * Split out of the body, and taking only the fields it reads, so the seeding is
311
307
  * exercisable without a live workflow: `dependsOn` is what makes a same-run edge
@@ -69,10 +69,6 @@ export const CAPPED_OUT_EMPTY_REASON = "The step hit its iteration cap with no o
69
69
  */
70
70
  export const CHILD_SUSPENDED_TOPIC = "child-suspended";
71
71
  // ── Helpers ───────────────────────────────────────────────────────────
72
- function nextFunctionIdFactory() {
73
- let n = 0;
74
- return () => `fn-${(n++).toString(36)}`;
75
- }
76
72
  const NO_STEP_FILE_METADATA = { entries: [], messages: [] };
77
73
  /** Normalizes a pre-`messages` checkpoint (a bare entries array) for replay. */
78
74
  export function readStepFileMetadata(checkpoint) {
@@ -95,10 +91,11 @@ function userFacingStepFailure(errorClass) {
95
91
  }
96
92
  /**
97
93
  * Build the step's lineage collector from its durable input. The agent loop
98
- * feeds each exec's provenance frame into it; post-step registration reads the
99
- * resulting input/script edges. Reconstructed deterministically on replay — the
100
- * frames it consumes are cached recv outputs and the command strings are cached
101
- * LLM tool-use (see the harness-thread-store and harness-durable-runtime specs).
94
+ * feeds each exec's provenance frame and each file-tool write into it;
95
+ * post-step registration reads the resulting input/script edges.
96
+ * Reconstructed deterministically on replay — the loop folds the call record
97
+ * of each cached tool call into it again, in call order (see the
98
+ * harness-durable-runtime spec).
102
99
  *
103
100
  * Split out of the body, and taking only the fields it reads, so the seeding is
104
101
  * exercisable without a live workflow: `dependsOn` is what makes a same-run edge
@@ -181,15 +178,15 @@ export async function runSandboxStepBody(input, deps) {
181
178
  const session = forSubAgent(input.runSession, input.agentId);
182
179
  const writePrefix = deps.resolveWritePrefix(input);
183
180
  const lineageCollector = createLineageCollector(input);
184
- // (2a) sandbox.mint — checkpoint the machine's identity (name + HMAC secret)
181
+ // (2a) sandbox.mint — checkpoint the machine's identity (its name)
185
182
  // BEFORE it is spawned (see the harness-sandbox-exec spec). Durable-before-create is what makes (2b)
186
183
  // idempotent: a crash between spawn and the spawn checkpoint re-runs (2b)
187
184
  // with the same identity, which adopts the already-created machine instead
188
185
  // of leaking a second one.
189
186
  const identity = await DBOS.runStep(() => Promise.resolve(mintSandboxIdentity(input.runId)), { name: "sandbox.mint" });
190
187
  // (2b) sandbox.create — spawn (or adopt) the machine under the minted
191
- // identity. The handle (secret included) is cached so recovery picks the
192
- // same machine back up without re-provisioning.
188
+ // identity. The handle is cached so recovery picks the same machine back up
189
+ // without re-provisioning.
193
190
  const sandboxSpec = {
194
191
  childWorkflowId,
195
192
  image: input.image,
@@ -215,9 +212,8 @@ export async function runSandboxStepBody(input, deps) {
215
212
  }, { name: "sandbox.recheck-alive" });
216
213
  // (3) runAgent — the harness loop with durableStep.
217
214
  // The step deadline is captured ONCE at step start from the checkpointed
218
- // clock so it reproduces on replay — the `awaitExec` recv loop gates on this
219
- // absolute deadline, so a raw `Date.now()` here would shift the recorded
220
- // `DBOS.recv` sequence on recovery (see the harness-durable-runtime spec). `deadlineMs` is a getter so
215
+ // clock so it reproduces on replay — a raw `Date.now()` here would grow the
216
+ // deadline on each recovery (see the harness-durable-runtime spec). `deadlineMs` is a getter so
221
217
  // deps read the absolute ms without holding a captured value; returning a
222
218
  // fresh `now + timeout` per call would make a step with N tool calls runnable
223
219
  // for up to N × `timeoutSeconds`.
@@ -228,9 +224,7 @@ export async function runSandboxStepBody(input, deps) {
228
224
  input,
229
225
  session,
230
226
  sandbox,
231
- workflowId: childWorkflowId,
232
227
  stepWritePrefix: writePrefix,
233
- nextFunctionId: nextFunctionIdFactory(),
234
228
  deadlineMs: () => stepDeadlineMs,
235
229
  lineageCollector,
236
230
  blockerHolder,
@@ -251,15 +245,17 @@ export async function runSandboxStepBody(input, deps) {
251
245
  // `tool-finished`) and model-output deltas are wrapped under the
252
246
  // `data-loop-event` envelope so the SSE consumer can route them.
253
247
  //
254
- // Every emit is `await`ed in body order (the loop awaits its own emits, and
255
- // `awaitExec` awaits its sandbox-event emits), so each body-path
256
- // `DBOS.writeStream` lands at a deterministic function-ID on replay
257
- // (see the harness-durable-runtime spec) — no fire-and-forget tail racing the next real op for the
258
- // counter. `safeEmit` swallows a stream-write failure: a dropped UI frame
259
- // is non-fatal and must not fail the step.
248
+ // Every emit is `await`ed in order (the loop awaits its own emits, and the
249
+ // exec awaits its sandbox-event emits). A body-path `DBOS.writeStream` lands
250
+ // at a deterministic function-ID on replay (see the harness-durable-runtime
251
+ // spec) — no fire-and-forget tail racing the next real op for the counter.
252
+ // A sandbox event arrives from inside the step of its tool call; a write from
253
+ // a step takes no function-ID, and a recovered step writes its events again.
254
+ // `safeEmit` swallows a stream-write failure: a dropped UI frame is
255
+ // non-fatal and must not fail the step.
260
256
  // Pattern A (fan-in at route): write to the CHILD's own `"events"` stream.
261
- // `DBOS.writeStream` is body-only, so cross-workflow writes aren't supported;
262
- // the SSE route reads the parent's stream plus every active child's, addressed
257
+ // `DBOS.writeStream` writes only to the stream of its own workflow, so the
258
+ // SSE route reads the parent's stream plus every active child's, addressed
263
259
  // by `cortex_step_executions.child_workflow_id`.
264
260
  const emitToParentStream = (part) => DBOS.writeStream("events", part);
265
261
  const safeEmit = async (part) => {
@@ -328,9 +324,10 @@ export async function runSandboxStepBody(input, deps) {
328
324
  // of the step) and emits the FULL tree under a stable per-step reconciling
329
325
  // id — the terminal `walkArtifacts` tree reconciles onto the same id at step
330
326
  // end. Full-tree + reconciling means an observer reconnecting from offset 0
331
- // still converges (raw deltas alone would not). REPLAY: the fold is a 1:1
332
- // pure function of the checkpointed `awaitExec` recv sequence — do NOT add a
333
- // timer/debounce here, that would re-break the harness-durable-runtime spec.
327
+ // still converges (raw deltas alone would not). The fold is live view only:
328
+ // a replayed tool call returns its cached result and folds no delta, thus
329
+ // after a recovery the live tree holds the deltas of the later execs until
330
+ // the terminal tree reconciles it.
334
331
  const treeFileId = stepPartId("step-file-tree", input.runId, input.stepId);
335
332
  const treeFiles = new Set();
336
333
  const emitFileTree = () => safeEmit({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inflexa-ai/harness",
3
- "version": "0.42.2",
3
+ "version": "0.43.1",
4
4
  "description": "Host-agnostic bioinformatics agent harness — hand-rolled agent loop, DBOS-durable workflows, sandboxed compute.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -35,8 +35,10 @@
35
35
  "scripts": {
36
36
  "build": "tsc -p tsconfig.json && node scripts/copy-assets.mjs",
37
37
  "design:fixture": "bun scripts/render-fixture.ts",
38
+ "design:gallery": "bun scripts/render-gallery.ts",
38
39
  "format": "prettier --write src/",
39
40
  "format:file": "prettier --write",
41
+ "gallery:data": "bash scripts/gallery-data.sh",
40
42
  "lint": "eslint .",
41
43
  "lint:python": "ruff check src/input-scan/decoder && ruff format --check src/input-scan/decoder",
42
44
  "prepare": "tsc -p tsconfig.json && node scripts/copy-assets.mjs",
@@ -68,6 +70,8 @@
68
70
  "@opentelemetry/sdk-trace-base": "^2.9.0",
69
71
  "@opentelemetry/sdk-trace-node": "^2.9.0",
70
72
  "@opentelemetry/semantic-conventions": "^1.43.0",
73
+ "@types/dockerode": "^3.3.47",
74
+ "@types/pg": "^8.20.0",
71
75
  "ag-grid-community": "36.1.0",
72
76
  "ai": "^7.0.113",
73
77
  "cheerio": "^1.2.0",
@@ -99,10 +103,8 @@
99
103
  "@ai-sdk/amazon-bedrock": "^5.0.93",
100
104
  "@eslint/js": "^10.0.1",
101
105
  "@types/bun": "latest",
102
- "@types/dockerode": "^3.3.47",
103
106
  "@types/node": "^24.13.3",
104
107
  "@types/pdf-parse": "^1.1.5",
105
- "@types/pg": "^8.20.0",
106
108
  "csstree-validator": "^4.0.1",
107
109
  "eslint": "^10.7.0",
108
110
  "eslint-plugin-neverthrow": "^1.1.4",
@@ -1,143 +0,0 @@
1
- /**
2
- * `awaitExec` — awaits a submitted exec's terminal result, forwarding progress
3
- * events, under one of two transports selected by `options.transport`:
4
- *
5
- * - `poll` (default): {@link awaitExecPoll} loops durable pull steps against
6
- * `GET /exec/{execId}?since={cursor}`. The sandbox initiates nothing; the
7
- * host asks. Inherently restart-proof — a recovered workflow just resumes
8
- * polling from its current identity — so the recovery wedge (#41) does not
9
- * apply. A machine that dies mid-exec fast-fails in-loop: sustained
10
- * `unavailable` polls escalate to a backend-inspect probe (`liveness.ts`)
11
- * and an observably dead machine returns the synthetic-failure result
12
- * instead of waiting out the deadline.
13
- * - `callback`: {@link awaitExecCallback} is the recv loop below, with the
14
- * pull as its recovery backstop.
15
- *
16
- * Both run in the workflow body (NOT a step) — `DBOS.recv`, `DBOS.writeStream`,
17
- * and `DBOS.sleepms` are body-only — and both verify every result against the
18
- * per-sandbox HMAC, treating a bad/stale signature as a hard cancel.
19
- *
20
- * ## Callback mode: the recv loop
21
- *
22
- * On each received envelope:
23
- *
24
- * 1. If it carries `signature: null` and the payload is a
25
- * `synthetic-failure` done-marker, accept it — the watchdog is an
26
- * in-process trusted sender. Any other null-signature message
27
- * hard-cancels.
28
- * 2. Otherwise HMAC-verify against the per-sandbox `callbackSecret`.
29
- * A bad or stale signature throws `HardCancelError`, which DBOS
30
- * treats as a fatal workflow error (no retry).
31
- * 3. If the verified payload is a done-marker, return its `result`.
32
- * 4. Otherwise forward the payload via `emit` and continue.
33
- *
34
- * `deadline` is an absolute unix-ms timestamp — the durable backstop
35
- * (typically `step.timeout`). `T` is liveness-agnostic recv pacing: we
36
- * use `min(5s, remaining)` so the loop returns often enough to honour
37
- * the deadline.
38
- *
39
- * ## Recv is not the only way home
40
- *
41
- * The completion callback is a *push* to an address baked into the container at
42
- * creation. A host that dies mid-exec comes back with a different ingress port,
43
- * so the push can never land — the sandbox retries into a void while the
44
- * recovered recv waits for a message that will never arrive. That is the
45
- * recovery wedge (#41), and it is silent: a `running` row that never advances.
46
- *
47
- * So whenever the topic falls quiet, this loop stops waiting and *asks*:
48
- * `GET /exec/{execId}` on the sandbox itself returns the terminal result,
49
- * signed fresh at request time. The bytes are the ones the callback would have
50
- * carried, so the provenance frame survives the recovery path intact and one
51
- * verification path serves both. Push remains the fast path; pull is what makes
52
- * the protocol durable rather than merely optimistic.
53
- *
54
- * The pull is a DBOS step, not a bare `fetch`: a body-level call whose result
55
- * varied between replays would desynchronise the recorded function-ID sequence.
56
- */
57
- import { type ExecEmit, type ExecResult, type SandboxLiveness, type SandboxRef, type SandboxTransport } from "./types.js";
58
- export declare class HardCancelError extends Error {
59
- readonly execId: string;
60
- readonly reason: "bad-signature" | "stale-timestamp" | "missing" | "synthetic-without-marker";
61
- constructor(execId: string, reason: "bad-signature" | "stale-timestamp" | "missing" | "synthetic-without-marker", detail?: string);
62
- }
63
- export declare class ExecTimeoutError extends Error {
64
- readonly execId: string;
65
- constructor(execId: string);
66
- }
67
- /**
68
- * Dependency seams of the await loops — every member is an injectable effect
69
- * (clock, recv, fetch, durable step, durable sleep, warn sink) plus the
70
- * transport selector. Tuning values (freshness window, pull threshold, poll
71
- * cadence) are module constants, not options.
72
- */
73
- export interface AwaitExecOptions {
74
- /**
75
- * Clock source for the deadline check and HMAC freshness. Defaults to the
76
- * checkpointed `DBOS.now()` so the loop's recv count is replay-stable (a raw
77
- * `Date.now()` here drifts the recorded `DBOS.recv` function-ID sequence on
78
- * recovery — see the harness-durable-runtime spec).
79
- */
80
- now?: () => number | Promise<number>;
81
- /** Replaces `DBOS.recv` (callback mode). */
82
- recv?: <T>(topic: string, timeoutSec: number) => Promise<T | null>;
83
- /** Defaults to `globalThis.fetch`. */
84
- fetch?: typeof fetch;
85
- /** Defaults to `DBOS.runStep`. */
86
- runStep?: <T>(fn: () => Promise<T>, config: {
87
- name: string;
88
- }) => Promise<T>;
89
- /**
90
- * Result transport. `poll` (default) runs the durable poll loop; `callback`
91
- * runs the `DBOS.recv` loop with the pull as its recovery backstop. The
92
- * composition root passes the same value it hands the container.
93
- */
94
- transport?: SandboxTransport;
95
- /** Replaces the durable inter-poll sleep. Defaults to `DBOS.sleepms`. */
96
- sleep?: (ms: number) => Promise<void>;
97
- /**
98
- * Sink for advisory warnings (poll-mode progress-event loss). Defaults to
99
- * `DBOS.logger.warn`. Advisory only: the terminal result — not the event
100
- * stream — is the authoritative outcome.
101
- */
102
- warn?: (message: string) => void;
103
- /**
104
- * Backend inspect for the poll loop's liveness escalation (`liveness.ts`).
105
- * Wired by `createSandboxClient` from its backend ops; when absent the
106
- * loop skips escalation and stays purely deadline-bounded. Throws on
107
- * transient backend API errors — the escalation treats a throw as an
108
- * inconclusive probe, never a failed exec.
109
- */
110
- isAlive?: (ref: SandboxRef) => Promise<SandboxLiveness>;
111
- }
112
- /**
113
- * Await a submitted exec's terminal result. Dispatches on transport — `poll`
114
- * (default) or `callback`. Both verify every result against the per-sandbox
115
- * HMAC and are bounded by `deadlineMs`.
116
- */
117
- export declare function awaitExec(ref: SandboxRef, execId: string, emit: ExecEmit, deadlineMs: number, options?: AwaitExecOptions): Promise<ExecResult>;
118
- /**
119
- * Body of the recv loop (callback transport). Hoisted so it's testable without
120
- * spinning up DBOS — `recv` is injectable and `now` is overridable.
121
- */
122
- export declare function awaitExecCallback(ref: SandboxRef, execId: string, emit: ExecEmit, deadlineMs: number, options?: AwaitExecOptions): Promise<ExecResult>;
123
- /**
124
- * Body of the poll loop (poll transport). No `DBOS.recv`, no per-exec topic, no
125
- * callback handler: a sequence of durable pull steps named
126
- * `sandbox.poll-exec-result.${execId}.${n}`, each fetching the signed
127
- * `{ status, events, cursor, result? }`, verifying it exactly as a pushed
128
- * completion is verified, forwarding new events via `emit`, advancing the
129
- * cursor, and returning `result` once terminal. `sleep`, `now`, `fetch`,
130
- * `runStep`, and `isAlive` are injectable so the loop is testable without DBOS.
131
- *
132
- * Liveness: when the `isAlive` seam is wired (the client always wires it),
133
- * consecutive `unavailable` polls past the module threshold escalate to a
134
- * durable probe step `sandbox.probe-liveness.${execId}.${k}`; a dead verdict
135
- * returns the synthetic-failure result in-loop — the poll-mode analogue of the
136
- * watchdog's synthetic-complete, minus the topic and the race gate, since the
137
- * verdict is produced and consumed by the same workflow in sequence.
138
- *
139
- * The `cursor`, `pollAttempt`, and `probeAttempt` counters are pure functions
140
- * of the cached step sequence, so a replay issues the same polls and probes in
141
- * the same order and the DBOS function-ID sequence stays stable.
142
- */
143
- export declare function awaitExecPoll(ref: SandboxRef, execId: string, emit: ExecEmit, deadlineMs: number, options?: AwaitExecOptions): Promise<ExecResult>;