@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
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * K8s-backed `createSandbox` / `teardown` / `isAlive`.
3
3
  *
4
- * Launches a sandbox Job with `CORTEX_BASE_URL` and `SANDBOX_CALLBACK_SECRET`
5
- * env vars; pod IP + port 8765 are the host/port Cortex POSTs `/exec` to.
4
+ * Launches a sandbox Job; pod IP + port 8765 are the host/port the host POSTs
5
+ * `/exec` to and polls. The pod initiates nothing: its confinement is a
6
+ * cluster-side NetworkPolicy, not an in-pod firewall.
6
7
  * Storage is wired via the shared session PVC: a flat read-only `volumeMount`
7
8
  * of the analysis tree at `/{resourceId}` plus a nested read-write mount of the
8
9
  * step's artifact dir, with the lib/ref stores mounted read-only at `/mnt/libs`
@@ -17,19 +18,12 @@ import { ResultAsync } from "neverthrow";
17
18
  import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
18
19
  import { type SandboxError } from "./sandbox-error.js";
19
20
  import type { SpawnSession } from "../auth/types.js";
20
- import type { FarmSource, ManagedSandbox, SandboxIdentity, SandboxLabels, SandboxLiveness, SandboxRef, SandboxSpec, SandboxTransport, ToolchainSource } from "./types.js";
21
+ import type { FarmSource, ManagedSandbox, SandboxIdentity, SandboxLabels, SandboxLiveness, SandboxRef, SandboxSpec, ToolchainSource } from "./types.js";
21
22
  import type { Logger } from "../lib/logger.js";
22
23
  export interface K8sClientConfig {
23
24
  /** Operational logging seam; omitted falls back to no-op. */
24
25
  readonly logger?: Logger;
25
26
  image: string;
26
- cortexBaseUrl: string;
27
- /**
28
- * Result transport, threaded to the pod as `SANDBOX_TRANSPORT`. Poll-mode
29
- * confinement on K8s is a cluster-side NetworkPolicy, not an in-pod firewall.
30
- * Defaults to `poll`.
31
- */
32
- transport?: SandboxTransport;
33
27
  namespace: string;
34
28
  /** PVC claim backing the shared session PVC the workspace roots live under. */
35
29
  sessionPvc?: string;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * K8s-backed `createSandbox` / `teardown` / `isAlive`.
3
3
  *
4
- * Launches a sandbox Job with `CORTEX_BASE_URL` and `SANDBOX_CALLBACK_SECRET`
5
- * env vars; pod IP + port 8765 are the host/port Cortex POSTs `/exec` to.
4
+ * Launches a sandbox Job; pod IP + port 8765 are the host/port the host POSTs
5
+ * `/exec` to and polls. The pod initiates nothing: its confinement is a
6
+ * cluster-side NetworkPolicy, not an in-pod firewall.
6
7
  * Storage is wired via the shared session PVC: a flat read-only `volumeMount`
7
8
  * of the analysis tree at `/{resourceId}` plus a nested read-write mount of the
8
9
  * step's artifact dir, with the lib/ref stores mounted read-only at `/mnt/libs`
@@ -124,18 +125,11 @@ function buildJobSpec(session, sandboxSpec, hostLabels, config, identity, farm,
124
125
  toolchainSource: config.toolchainSource,
125
126
  cache: libsMounted && farm?.cachePath !== undefined,
126
127
  });
127
- const transport = config.transport ?? "poll";
128
128
  const spec = sandboxSpec.resources;
129
129
  // Composed as one record, not as a list of `{name, value}`. A duplicate name
130
130
  // in the env of a container resolves at the kubelet, thus the later spread
131
131
  // must win here, not there.
132
132
  const env = Object.entries({
133
- SANDBOX_TRANSPORT: transport,
134
- // Poll mode never dials out, so the URL is omitted (matching the Docker
135
- // backend) — the pod spec itself then documents that no callback egress
136
- // is expected. sandbox-server neither reads nor requires it in poll mode.
137
- ...(transport === "callback" ? { CORTEX_BASE_URL: config.cortexBaseUrl } : {}),
138
- SANDBOX_CALLBACK_SECRET: identity.callbackSecret,
139
133
  ...threadLimitEnv(spec),
140
134
  ...plan.env,
141
135
  ...(sandboxSpec.extraEnv ?? {}),
@@ -400,8 +394,8 @@ function waitForJobGone(batchApi, namespace, name) {
400
394
  * owner workflow id must match this step's `ownerWorkflowId`.
401
395
  * Only a recovery re-run carries the same checkpointed identity, so a mismatch
402
396
  * means an (astronomically rare) name collision with a *different* step —
403
- * adopting its pod would HMAC-fail every callback, and deleting its Job would
404
- * kill a live sibling. Refuse loudly instead.
397
+ * adopting its pod would send the execs of this step to a sibling, and deleting
398
+ * its Job would kill a live sibling. Refuse loudly instead.
405
399
  */
406
400
  function createOrAdoptJob(batchApi, coreApi, namespace, sandboxId, ownerWorkflowId, job) {
407
401
  const createFailed = (status, cause) => ({
@@ -514,7 +508,6 @@ export function createK8sSandboxOps(config) {
514
508
  host: ready.value.podIP,
515
509
  port: SANDBOX_SERVER_PORT,
516
510
  backend: "k8s",
517
- callbackSecret: identity.callbackSecret,
518
511
  };
519
512
  const registered = await trySandbox(() => config.registerSandbox(session, spec, ref), (status, cause) => ({
520
513
  type: "container_create_failed",
@@ -1,18 +1,13 @@
1
1
  /**
2
- * Transport-agnostic liveness escalation (the harness-sandbox-exec spec,
2
+ * Liveness escalation of the exec poll loop (the harness-sandbox-exec spec,
3
3
  * "sustained unavailability escalates to a liveness probe").
4
4
  *
5
- * The await loops observe the raw unreachability signal (consecutive
6
- * `unavailable` poll outcomes) but never adjudicate it themselves — poll
5
+ * The poll loop observes the raw unreachability signal (consecutive
6
+ * `unavailable` poll outcomes) but never adjudicates it itself — poll
7
7
  * failures conflate "unreachable" with "unknown execId / non-200". The
8
8
  * backend inspect (`SandboxClient.isAlive`) is the sole arbiter of dead
9
9
  * versus live-but-slow, invoked through {@link probeLiveness} once
10
10
  * {@link createEscalationPolicy} arms.
11
- *
12
- * Shared with the watchdog: {@link syntheticFailureReason} and
13
- * {@link syntheticFailureResult} are the one constructor for synthetic
14
- * failures, so reasons and result shape are identical no matter which
15
- * adjudicator (in-loop escalation or watchdog) produced them.
16
11
  */
17
12
  import type { ExecResult, SandboxLiveness, SandboxRef } from "./types.js";
18
13
  /**
@@ -35,9 +30,7 @@ export type ProbeVerdict = {
35
30
  };
36
31
  /**
37
32
  * The consecutive-unavailable counter: `unavailable` increments, `ok` resets,
38
- * and crossing `threshold` arms one probe and re-arms from zero. Pure state
39
- * over checkpointed poll outcomes, so a replaying loop walks the identical
40
- * poll/probe sequence.
33
+ * and crossing `threshold` arms one probe and re-arms from zero.
41
34
  */
42
35
  export declare function createEscalationPolicy(threshold?: number): {
43
36
  /** Record a poll outcome; true means "run the probe now". */
@@ -46,7 +39,7 @@ export declare function createEscalationPolicy(threshold?: number): {
46
39
  /**
47
40
  * Run the backend inspect and collapse it to a three-valued verdict. Never
48
41
  * throws: `isAlive` throws on transient backend API errors by contract, but
49
- * inside an await loop a failed probe is not a failed exec — the same
42
+ * inside the poll loop a failed probe is not a failed exec — the same
50
43
  * discipline as a failed poll — so a throw maps to `inconclusive` and the
51
44
  * caller resumes polling.
52
45
  */
@@ -1,18 +1,13 @@
1
1
  /**
2
- * Transport-agnostic liveness escalation (the harness-sandbox-exec spec,
2
+ * Liveness escalation of the exec poll loop (the harness-sandbox-exec spec,
3
3
  * "sustained unavailability escalates to a liveness probe").
4
4
  *
5
- * The await loops observe the raw unreachability signal (consecutive
6
- * `unavailable` poll outcomes) but never adjudicate it themselves — poll
5
+ * The poll loop observes the raw unreachability signal (consecutive
6
+ * `unavailable` poll outcomes) but never adjudicates it itself — poll
7
7
  * failures conflate "unreachable" with "unknown execId / non-200". The
8
8
  * backend inspect (`SandboxClient.isAlive`) is the sole arbiter of dead
9
9
  * versus live-but-slow, invoked through {@link probeLiveness} once
10
10
  * {@link createEscalationPolicy} arms.
11
- *
12
- * Shared with the watchdog: {@link syntheticFailureReason} and
13
- * {@link syntheticFailureResult} are the one constructor for synthetic
14
- * failures, so reasons and result shape are identical no matter which
15
- * adjudicator (in-loop escalation or watchdog) produced them.
16
11
  */
17
12
  /**
18
13
  * Consecutive `unavailable` polls tolerated before probing. On the fast poll
@@ -24,9 +19,7 @@
24
19
  export const PROBE_AFTER_UNAVAILABLE_POLLS = 4;
25
20
  /**
26
21
  * The consecutive-unavailable counter: `unavailable` increments, `ok` resets,
27
- * and crossing `threshold` arms one probe and re-arms from zero. Pure state
28
- * over checkpointed poll outcomes, so a replaying loop walks the identical
29
- * poll/probe sequence.
22
+ * and crossing `threshold` arms one probe and re-arms from zero.
30
23
  */
31
24
  export function createEscalationPolicy(threshold = PROBE_AFTER_UNAVAILABLE_POLLS) {
32
25
  let consecutiveUnavailable = 0;
@@ -47,7 +40,7 @@ export function createEscalationPolicy(threshold = PROBE_AFTER_UNAVAILABLE_POLLS
47
40
  /**
48
41
  * Run the backend inspect and collapse it to a three-valued verdict. Never
49
42
  * throws: `isAlive` throws on transient backend API errors by contract, but
50
- * inside an await loop a failed probe is not a failed exec — the same
43
+ * inside the poll loop a failed probe is not a failed exec — the same
51
44
  * discipline as a failed poll — so a throw maps to `inconclusive` and the
52
45
  * caller resumes polling.
53
46
  */
@@ -3,14 +3,11 @@
3
3
  * rows whose owning workflow has reached a terminal status (SUCCESS,
4
4
  * ERROR, CANCELLED).
5
5
  *
6
- * Background: a `DBOS.send` to an already-completed workflow accumulates
7
- * forever as `consumed=f`. The watchdog's
8
- * synthetic-failure send guarantees this happens at the protocol level —
9
- * a real `complete` may arrive microseconds before the watchdog and the
10
- * loser becomes a stuck notification.
6
+ * Background: a `DBOS.send` to a workflow that ends before it reads the
7
+ * message accumulates forever as `consumed=f` — for example the suspension
8
+ * notice of a child (`CHILD_SUSPENDED_TOPIC`) to a parent that already failed.
11
9
  *
12
- * Cadence is separate from the liveness watchdog (~5 minutes vs ~1
13
- * minute). Bounded delete batch keeps the lock window short.
10
+ * Cadence ~5 minutes. Bounded delete batch keeps the lock window short.
14
11
  *
15
12
  * Production reads the DBOS system DB via the app pool (DBOS by default
16
13
  * shares the database, only the pool is separate). The query function is
@@ -3,14 +3,11 @@
3
3
  * rows whose owning workflow has reached a terminal status (SUCCESS,
4
4
  * ERROR, CANCELLED).
5
5
  *
6
- * Background: a `DBOS.send` to an already-completed workflow accumulates
7
- * forever as `consumed=f`. The watchdog's
8
- * synthetic-failure send guarantees this happens at the protocol level —
9
- * a real `complete` may arrive microseconds before the watchdog and the
10
- * loser becomes a stuck notification.
6
+ * Background: a `DBOS.send` to a workflow that ends before it reads the
7
+ * message accumulates forever as `consumed=f` — for example the suspension
8
+ * notice of a child (`CHILD_SUSPENDED_TOPIC`) to a parent that already failed.
11
9
  *
12
- * Cadence is separate from the liveness watchdog (~5 minutes vs ~1
13
- * minute). Bounded delete batch keeps the lock window short.
10
+ * Cadence ~5 minutes. Bounded delete batch keeps the lock window short.
14
11
  *
15
12
  * Production reads the DBOS system DB via the app pool (DBOS by default
16
13
  * shares the database, only the pool is separate). The query function is
@@ -2,9 +2,9 @@
2
2
  * Sandbox reaper — the sole cleanup for orphaned sandboxes and stale registry
3
3
  * rows (see the harness-sandbox-exec spec, CONTEXT.md "Sandbox reaper").
4
4
  *
5
- * Distinct from the liveness watchdog (`sandbox/watchdog.ts`): the watchdog
6
- * sweeps registry→cluster and unblocks stuck recvs; the reaper sweeps
7
- * cluster→registry and garbage-collects. It lists every Cortex-managed sandbox
5
+ * The reaper sweeps cluster→registry and garbage-collects. A machine that dies
6
+ * under a running exec needs no sweep: the exec's own liveness probe ends it
7
+ * (`exec.ts`). The reaper lists every Cortex-managed sandbox
8
8
  * machine in the configured namespace, resolves each machine's recorded owner
9
9
  * workflow, and reaps the ones whose owner is terminal — tearing the machine
10
10
  * down and reconciling its step row. A machine whose owner does not resolve at
@@ -2,9 +2,9 @@
2
2
  * Sandbox reaper — the sole cleanup for orphaned sandboxes and stale registry
3
3
  * rows (see the harness-sandbox-exec spec, CONTEXT.md "Sandbox reaper").
4
4
  *
5
- * Distinct from the liveness watchdog (`sandbox/watchdog.ts`): the watchdog
6
- * sweeps registry→cluster and unblocks stuck recvs; the reaper sweeps
7
- * cluster→registry and garbage-collects. It lists every Cortex-managed sandbox
5
+ * The reaper sweeps cluster→registry and garbage-collects. A machine that dies
6
+ * under a running exec needs no sweep: the exec's own liveness probe ends it
7
+ * (`exec.ts`). The reaper lists every Cortex-managed sandbox
8
8
  * machine in the configured namespace, resolves each machine's recorded owner
9
9
  * workflow, and reaps the ones whose owner is terminal — tearing the machine
10
10
  * down and reconciling its step row. A machine whose owner does not resolve at
@@ -5,10 +5,9 @@
5
5
  * the K8s client ops (`k8s-client.ts`) — model failure as values: each external
6
6
  * op returns `ResultAsync<T, SandboxError>`. The ONLY `try/catch` against the
7
7
  * SDK/driver call lives in those files (via the `trySandbox` helper here);
8
- * everything up to the composition seam flows `ResultAsync`. (`submitExec`
9
- * (`submit-exec.ts`) is NOT on this channel — it wraps its own `/exec` POST in a
10
- * `DBOS.runStep` and throws on a non-202 so the step boundary records the
11
- * failure.)
8
+ * everything up to the composition seam flows `ResultAsync`. (The exec
9
+ * (`exec.ts`) is NOT on this channel — it runs as its own DBOS step and throws
10
+ * on a non-202 submit, so the step boundary records the failure.)
12
11
  *
13
12
  * House rules realized here (see `lib/result.ts`):
14
13
  * - Absence / "already gone" is NOT an error. A 404 on inspect/isAlive is the
@@ -19,10 +18,10 @@
19
18
  * owner-guard REFUSES, a submit the server rejects, a teardown the driver
20
19
  * errors on, a liveness probe that errors.
21
20
  * - Control-flow exceptions are NOT failures and are never captured as a
22
- * `SandboxError`. The recv loop's `HardCancelError` / `ExecTimeoutError`
23
- * (`await-exec.ts`) and DBOS's `DBOSWorkflowCancelledError` / `AbortError`
21
+ * `SandboxError`. The exec's `ExecTimeoutError`
22
+ * (`exec.ts`) and DBOS's `DBOSWorkflowCancelledError` / `AbortError`
24
23
  * are control-flow signalling — they live OUTSIDE this error channel and
25
- * propagate untouched. `await-exec.ts` is NOT converted; `trySandbox` only
24
+ * propagate untouched. `exec.ts` is NOT converted; `trySandbox` only
26
25
  * ever wraps the single SDK/HTTP call handed to it.
27
26
  *
28
27
  * `createSandbox` of the public `SandboxClient` gives each `SandboxError` as
@@ -34,8 +33,8 @@
34
33
  * composition seam: it builds the backend ops, maps each `err` of such an op to
35
34
  * a `SandboxFailure`, and `unwrapOrThrow`s it, so the seam throws the described
36
35
  * failure with the variant on `.cause` and on `.error`. A `catch` never sees
37
- * the raw variant. (`submitExec` is the exception — it is already its own DBOS
38
- * step and throws directly, so the seam forwards its `Promise<void>` as-is.)
36
+ * the raw variant. (`exec` is the exception — it is already its own DBOS
37
+ * step and throws directly, so the seam forwards its promise as-is.)
39
38
  */
40
39
  import { ResultAsync, type Result } from "neverthrow";
41
40
  import type { FarmLockError } from "./farm.js";
@@ -168,8 +167,8 @@ export declare function keepSuspendingRefusal(result: Result<SandboxRef, Sandbox
168
167
  *
169
168
  * `fn` runs the single SDK/HTTP call and returns the already-mapped value
170
169
  * (`T`). Keep `fn` to that one call plus trivial mapping; do NOT embed
171
- * control-flow that could throw a non-backend error (the recv loop's
172
- * `HardCancelError`/`ExecTimeoutError` and DBOS cancellation must stay outside
170
+ * control-flow that could throw a non-backend error (the exec's
171
+ * `ExecTimeoutError` and DBOS cancellation must stay outside
173
172
  * any `trySandbox` so they propagate as control-flow, never become an `err`).
174
173
  */
175
174
  export declare function trySandbox<T>(fn: () => Promise<T>, toError: (status: number | undefined, cause: unknown) => SandboxError): ResultAsync<T, SandboxError>;
@@ -5,10 +5,9 @@
5
5
  * the K8s client ops (`k8s-client.ts`) — model failure as values: each external
6
6
  * op returns `ResultAsync<T, SandboxError>`. The ONLY `try/catch` against the
7
7
  * SDK/driver call lives in those files (via the `trySandbox` helper here);
8
- * everything up to the composition seam flows `ResultAsync`. (`submitExec`
9
- * (`submit-exec.ts`) is NOT on this channel — it wraps its own `/exec` POST in a
10
- * `DBOS.runStep` and throws on a non-202 so the step boundary records the
11
- * failure.)
8
+ * everything up to the composition seam flows `ResultAsync`. (The exec
9
+ * (`exec.ts`) is NOT on this channel — it runs as its own DBOS step and throws
10
+ * on a non-202 submit, so the step boundary records the failure.)
12
11
  *
13
12
  * House rules realized here (see `lib/result.ts`):
14
13
  * - Absence / "already gone" is NOT an error. A 404 on inspect/isAlive is the
@@ -19,10 +18,10 @@
19
18
  * owner-guard REFUSES, a submit the server rejects, a teardown the driver
20
19
  * errors on, a liveness probe that errors.
21
20
  * - Control-flow exceptions are NOT failures and are never captured as a
22
- * `SandboxError`. The recv loop's `HardCancelError` / `ExecTimeoutError`
23
- * (`await-exec.ts`) and DBOS's `DBOSWorkflowCancelledError` / `AbortError`
21
+ * `SandboxError`. The exec's `ExecTimeoutError`
22
+ * (`exec.ts`) and DBOS's `DBOSWorkflowCancelledError` / `AbortError`
24
23
  * are control-flow signalling — they live OUTSIDE this error channel and
25
- * propagate untouched. `await-exec.ts` is NOT converted; `trySandbox` only
24
+ * propagate untouched. `exec.ts` is NOT converted; `trySandbox` only
26
25
  * ever wraps the single SDK/HTTP call handed to it.
27
26
  *
28
27
  * `createSandbox` of the public `SandboxClient` gives each `SandboxError` as
@@ -34,8 +33,8 @@
34
33
  * composition seam: it builds the backend ops, maps each `err` of such an op to
35
34
  * a `SandboxFailure`, and `unwrapOrThrow`s it, so the seam throws the described
36
35
  * failure with the variant on `.cause` and on `.error`. A `catch` never sees
37
- * the raw variant. (`submitExec` is the exception — it is already its own DBOS
38
- * step and throws directly, so the seam forwards its `Promise<void>` as-is.)
36
+ * the raw variant. (`exec` is the exception — it is already its own DBOS
37
+ * step and throws directly, so the seam forwards its promise as-is.)
39
38
  */
40
39
  import { ResultAsync, err, ok } from "neverthrow";
41
40
  import { ZodError } from "zod";
@@ -167,8 +166,8 @@ export function keepSuspendingRefusal(result) {
167
166
  *
168
167
  * `fn` runs the single SDK/HTTP call and returns the already-mapped value
169
168
  * (`T`). Keep `fn` to that one call plus trivial mapping; do NOT embed
170
- * control-flow that could throw a non-backend error (the recv loop's
171
- * `HardCancelError`/`ExecTimeoutError` and DBOS cancellation must stay outside
169
+ * control-flow that could throw a non-backend error (the exec's
170
+ * `ExecTimeoutError` and DBOS cancellation must stay outside
172
171
  * any `trySandbox` so they propagate as control-flow, never become an `err`).
173
172
  */
174
173
  export function trySandbox(fn, toError) {
@@ -49,7 +49,7 @@ export interface SandboxTreeDelta {
49
49
  }
50
50
  /** Extract the tree delta from a `data-sandbox-event` part, or null when the
51
51
  * part is some other sandbox event. The wrapper shape is set by
52
- * `run-exec.ts`: `{ type: "data-sandbox-event", data: { execId, event } }`
52
+ * `run-exec.ts`: `{ type: "data-sandbox-event", data: { event } }`
53
53
  * where `event` is the executor's `eventPayload`. */
54
54
  export declare function sandboxTreeDelta(part: {
55
55
  type: string;
@@ -45,7 +45,7 @@ export function activityForTool(name, input, resolveDetail) {
45
45
  }
46
46
  /** Extract the tree delta from a `data-sandbox-event` part, or null when the
47
47
  * part is some other sandbox event. The wrapper shape is set by
48
- * `run-exec.ts`: `{ type: "data-sandbox-event", data: { execId, event } }`
48
+ * `run-exec.ts`: `{ type: "data-sandbox-event", data: { event } }`
49
49
  * where `event` is the executor's `eventPayload`. */
50
50
  export function sandboxTreeDelta(part) {
51
51
  if (part.type !== "data-sandbox-event")
@@ -1,12 +1,10 @@
1
1
  /**
2
2
  * Sandbox-client types — the wire/persistence shapes that cross the
3
- * submit/recv protocol (see the harness-sandbox-exec spec) and the active-sandbox registry.
3
+ * submit/poll protocol (see the harness-sandbox-exec spec) and the active-sandbox registry.
4
4
  *
5
- * `SandboxRef` is the in-memory handle the harness uses to talk to a live
6
- * sandbox; it carries the per-sandbox `callbackSecret` (see the harness-sandbox-exec spec). The
7
- * persistable subset is `PersistedSandboxRef` (from `harness/state/schema.ts`)
8
- * — that one OMITS the secret, which lives only in the cached
9
- * `createSandbox` DBOS step output.
5
+ * `SandboxRef` is the handle the harness uses to talk to a live sandbox. The
6
+ * active-sandbox registry persists the same shape (`PersistedSandboxRef`, from
7
+ * `harness/state/schema.ts`).
10
8
  */
11
9
  import type { ResultAsync } from "neverthrow";
12
10
  import { z } from "zod";
@@ -14,26 +12,12 @@ import type { RunSession } from "../auth/types.js";
14
12
  import type { ResourceSpec } from "../config/resource-limits.js";
15
13
  import type { GateFailure } from "../lib/hooks.js";
16
14
  import type { PackageQuery } from "./package-identity.js";
15
+ import type { PersistedSandboxRef } from "../state/schema.js";
17
16
  export declare const SandboxBackend: z.ZodEnum<{
18
17
  docker: "docker";
19
18
  k8s: "k8s";
20
19
  }>;
21
20
  export type SandboxBackend = z.infer<typeof SandboxBackend>;
22
- /**
23
- * How a command's progress events and terminal result reach the host. Chosen by
24
- * the embedder at its composition root and carried into the container as
25
- * `SANDBOX_TRANSPORT`; backend-independent. The OSS default is `poll`.
26
- *
27
- * - `poll`: the host polls `GET /exec/{execId}?since={cursor}`; the sandbox
28
- * never dials out and needs no egress.
29
- * - `callback`: the sandbox POSTs signed event/completion callbacks; the
30
- * embedder runs an ingress. `GET /exec/{execId}` stays the recovery backstop.
31
- */
32
- export declare const SandboxTransportSchema: z.ZodEnum<{
33
- poll: "poll";
34
- callback: "callback";
35
- }>;
36
- export type SandboxTransport = z.infer<typeof SandboxTransportSchema>;
37
21
  /**
38
22
  * The declared owner of the sandbox toolchain. `"image"` states that the
39
23
  * image owns the interpreters, conda, and Node. `"store"` states that the
@@ -154,31 +138,14 @@ export type ExtendAnalysisFarm = (analysisId: string, queries: readonly PackageQ
154
138
  * Per-sandbox-machine liveness verdict. `oomKilled` is meaningful only when
155
139
  * `alive` is false: true when the backend reports the machine was killed for
156
140
  * exceeding its memory limit (Docker `State.OOMKilled`; K8s container
157
- * terminated reason `OOMKilled`) — the watchdog surfaces it as the
141
+ * terminated reason `OOMKilled`) — the exec surfaces it as the
158
142
  * `sandbox-oom-killed` failure reason instead of the generic `sandbox-dead`.
159
143
  */
160
144
  export interface SandboxLiveness {
161
145
  readonly alive: boolean;
162
146
  readonly oomKilled: boolean;
163
147
  }
164
- export declare const SandboxRefSchema: z.ZodObject<{
165
- sandboxId: z.ZodString;
166
- host: z.ZodString;
167
- port: z.ZodNumber;
168
- backend: z.ZodEnum<{
169
- docker: "docker";
170
- k8s: "k8s";
171
- }>;
172
- callbackSecret: z.ZodString;
173
- }, z.core.$strip>;
174
- export type SandboxRef = z.infer<typeof SandboxRefSchema>;
175
- /** Strip the secret before persisting to the active-sandbox registry. */
176
- export declare function toPersistedRef(ref: SandboxRef): {
177
- sandboxId: string;
178
- host: string;
179
- port: number;
180
- backend: SandboxBackend;
181
- };
148
+ export type SandboxRef = PersistedSandboxRef;
182
149
  /**
183
150
  * One tracked file operation in the sandbox-server provenance frame.
184
151
  * Mirrors Go's `ProvenanceEntry` (`images/sandbox-base/server/provenance.go`):
@@ -190,8 +157,8 @@ export declare const ProvenanceFrameEntrySchema: z.ZodObject<{
190
157
  }, z.core.$strip>;
191
158
  export type ProvenanceFrameEntry = z.infer<typeof ProvenanceFrameEntrySchema>;
192
159
  /**
193
- * Runtime file-I/O frame sandbox-server attaches to the `/complete`
194
- * callback. Mirrors Go's `provenancePayload` — every field is
160
+ * Runtime file-I/O frame sandbox-server attaches to the terminal result.
161
+ * Mirrors Go's `provenancePayload` — every field is
195
162
  * `omitempty` on the wire, so each arm defaults so a completion that
196
163
  * omits the frame (or any arm) still parses.
197
164
  */
@@ -228,9 +195,9 @@ export declare const ExecUsageSchema: z.ZodCatch<z.ZodOptional<z.ZodObject<{
228
195
  }, z.core.$strip>>>;
229
196
  export type ExecUsage = z.infer<typeof ExecUsageSchema>;
230
197
  /**
231
- * Final outcome of a single exec, returned by `awaitExec`. Mirrors the
232
- * sandbox-server completion payload plus a discriminant for synthetic
233
- * (watchdog-emitted) failures.
198
+ * Final outcome of a single exec, returned by `SandboxClient.exec`. Mirrors
199
+ * the sandbox-server completion payload plus a discriminant for a synthetic
200
+ * failure: the machine died under the exec.
234
201
  */
235
202
  export declare const ExecResultSchema: z.ZodObject<{
236
203
  execId: z.ZodString;
@@ -267,35 +234,10 @@ export declare const ExecResultSchema: z.ZodObject<{
267
234
  }, z.core.$strip>>>;
268
235
  }, z.core.$strip>;
269
236
  export type ExecResult = z.infer<typeof ExecResultSchema>;
270
- /**
271
- * Per-message envelope on the per-exec DBOS topic. Real callbacks carry
272
- * a non-null `signature` and `timestamp`; the in-process watchdog uses a
273
- * `null` signature + a `synthetic-failure` payload (see `await-exec.ts`).
274
- *
275
- * `payloadDigest` is the hex SHA-256 of the exact bytes sandbox-server POSTed.
276
- * The HMAC signs that digest rather than the body itself, so the digest is all
277
- * verification needs — and carrying it instead of the bytes keeps the message
278
- * from holding a second copy of a payload that can be arbitrarily large.
279
- * Re-serializing the parsed payload would not do: Go's `encoding/json`
280
- * HTML-escapes `<`, `>`, `&` by default, so a JS re-serialization diverges for
281
- * any output containing those characters (common in bioinformatics: FASTA
282
- * headers, shell stderr, command pipelines).
283
- *
284
- * `payloadRaw` is the superseded form, kept optional so messages already in
285
- * flight or persisted still verify.
286
- */
287
- export declare const ExecEventMessageSchema: z.ZodObject<{
288
- payload: z.ZodUnknown;
289
- payloadDigest: z.ZodOptional<z.ZodString>;
290
- payloadRaw: z.ZodOptional<z.ZodString>;
291
- signature: z.ZodNullable<z.ZodString>;
292
- timestamp: z.ZodNullable<z.ZodNumber>;
293
- }, z.core.$strip>;
294
- export type ExecEventMessage = z.infer<typeof ExecEventMessageSchema>;
295
237
  /**
296
238
  * One buffered progress event in a poll response: the sandbox's monotonic
297
239
  * per-exec sequence number (the poll cursor) plus the event payload the host
298
- * forwards via `emit` — the same payload a callback-mode event POST carries.
240
+ * forwards via `emit`.
299
241
  */
300
242
  export declare const PollEventSchema: z.ZodObject<{
301
243
  seq: z.ZodNumber;
@@ -303,15 +245,18 @@ export declare const PollEventSchema: z.ZodObject<{
303
245
  }, z.core.$strip>;
304
246
  export type PollEvent = z.infer<typeof PollEventSchema>;
305
247
  /**
306
- * Body of `GET /exec/{execId}?since={cursor}` in poll mode. Mirrors Go's
248
+ * Body of `GET /exec/{execId}?since={cursor}`. Mirrors Go's
307
249
  * `pollResponseBody`: the events newer than the caller's cursor, the new
308
250
  * high-water `cursor`, a `truncated` marker set once the ring shed an event,
309
251
  * and — once the exec is terminal — the completion `result` (with its
310
- * provenance frame). The whole body is HMAC-signed, verified exactly as a
311
- * pushed completion is.
252
+ * provenance frame).
312
253
  */
313
254
  export declare const PollResponseSchema: z.ZodObject<{
314
- status: z.ZodString;
255
+ status: z.ZodEnum<{
256
+ failed: "failed";
257
+ running: "running";
258
+ completed: "completed";
259
+ }>;
315
260
  events: z.ZodDefault<z.ZodArray<z.ZodObject<{
316
261
  seq: z.ZodNumber;
317
262
  payload: z.ZodUnknown;
@@ -355,23 +300,16 @@ export declare const PollResponseSchema: z.ZodObject<{
355
300
  }, z.core.$strip>;
356
301
  export type PollResponse = z.infer<typeof PollResponseSchema>;
357
302
  /**
358
- * Done-marker shape the recv loop unwraps. Both real completion POSTs
359
- * (wrapped by the `/complete` handler) and watchdog synthetic-failure
360
- * sends use this discriminant.
303
+ * One command for `SandboxClient.exec`. It carries no exec id: the exec
304
+ * derives its id from the durable step it runs in.
361
305
  */
362
- export interface DoneMarker {
363
- done: true;
364
- result: ExecResult;
365
- }
366
- export interface SyntheticFailureMarker {
367
- done: true;
368
- result: ExecResult;
369
- kind: "synthetic-failure";
370
- reason: string;
306
+ export interface ExecRequest {
307
+ readonly command: readonly string[];
308
+ readonly cwd?: string;
309
+ readonly env?: Readonly<Record<string, string>>;
310
+ readonly timeoutSeconds?: number;
371
311
  }
372
- export declare function isDoneMarker(value: unknown): value is DoneMarker;
373
- export declare function isSyntheticFailure(value: unknown): value is SyntheticFailureMarker;
374
- /** Wire shape `submitExec` POSTs to sandbox-server's `/exec` (change 4). */
312
+ /** Wire shape the exec POSTs to sandbox-server's `/exec`. */
375
313
  export interface SubmitExecBody {
376
314
  command: string[];
377
315
  execId: string;
@@ -389,9 +327,6 @@ export interface SubmitExecBody {
389
327
  }
390
328
  /** What a spawn needs beside its session. */
391
329
  export interface SandboxSpec {
392
- /** The first `execId` that will fire against this sandbox; nullable for
393
- * early-create flows where the workflow mints the first execId later. */
394
- execId?: string | null;
395
330
  /** Owning DBOS child workflow id (`"${parentRunId}-${N}"`). Recorded verbatim
396
331
  * on the sandbox machine under `cortex/owner-workflow-id` so the reaper can
397
332
  * map a cluster-side machine back to its workflow and check liveness. */
@@ -425,14 +360,12 @@ export type ResolveSandboxLabels = (session: RunSession) => ResultAsync<SandboxL
425
360
  * The identity minted for a sandbox machine *before* it is spawned — the
426
361
  * durable half of the two-step create (see the harness-sandbox-exec spec). Step 1 checkpoints this so a
427
362
  * recovery re-run of the spawn step adopts the already-created machine under
428
- * the same name and HMAC secret instead of leaking a second one.
363
+ * the same name instead of leaking a second one.
429
364
  */
430
365
  export interface SandboxIdentity {
431
366
  /** `sbx-{run8}-{uuid4}` — informative for `kubectl`, not load-bearing; the
432
367
  * checkpoint, not the name, is what makes create idempotent. */
433
368
  sandboxId: string;
434
- /** 32-byte base64 HMAC secret (see the harness-sandbox-exec spec); rides into the machine's env. */
435
- callbackSecret: string;
436
369
  }
437
370
  /**
438
371
  * A sandbox machine the backend is running that Cortex *manages*
@@ -448,8 +381,7 @@ export interface ManagedSandbox {
448
381
  createdAtMs: number | null;
449
382
  }
450
383
  /**
451
- * Per-step `emit` callback handed to `awaitExec`. May be async — `awaitExec`
452
- * runs in the workflow body (see the harness-tools spec) and `await`s each emit so the body-path
453
- * `DBOS.writeStream` it drives lands at a deterministic function-ID (see the harness-durable-runtime spec).
384
+ * Progress callback of `SandboxClient.exec`, called with each sandbox event in
385
+ * sequence order. The exec awaits each call before it polls again.
454
386
  */
455
387
  export type ExecEmit = (event: unknown) => void | Promise<void>;