@boardwalk-labs/runner 0.2.17 → 0.3.0-alpha.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.
- package/README.md +1 -1
- package/dist/runtime/agent/budget.d.ts +43 -24
- package/dist/runtime/agent/budget.js +108 -57
- package/dist/runtime/agent/events.d.ts +1 -1
- package/dist/runtime/broker_child_dispatcher.d.ts +5 -2
- package/dist/runtime/broker_child_dispatcher.js +24 -6
- package/dist/runtime/budget_gate.d.ts +58 -21
- package/dist/runtime/budget_gate.js +227 -46
- package/dist/runtime/host_capabilities.d.ts +4 -0
- package/dist/runtime/host_capabilities.js +25 -0
- package/dist/runtime/host_server.d.ts +137 -0
- package/dist/runtime/host_server.js +562 -0
- package/dist/runtime/index.js +45 -12
- package/dist/runtime/leaf_executor.d.ts +7 -4
- package/dist/runtime/leaf_executor.js +11 -6
- package/dist/runtime/program_runner.d.ts +62 -42
- package/dist/runtime/program_runner.js +156 -101
- package/dist/runtime/program_worker.d.ts +22 -11
- package/dist/runtime/program_worker.js +26 -48
- package/dist/runtime/python_program.d.ts +68 -0
- package/dist/runtime/python_program.js +270 -0
- package/dist/runtime/run_context.d.ts +13 -0
- package/dist/runtime/run_context.js +77 -0
- package/dist/runtime/runner_control_client.d.ts +23 -0
- package/dist/runtime/runner_control_client.js +69 -147
- package/dist/runtime/shell_exec.d.ts +17 -0
- package/dist/runtime/shell_exec.js +144 -0
- package/dist/runtime/support/index.d.ts +6 -0
- package/dist/runtime/support/index.js +14 -0
- package/dist/runtime/wire/inference_proxy.js +1 -1
- package/dist/runtime/wire/run.d.ts +5 -2
- package/dist/runtime/workflow_host.d.ts +53 -7
- package/dist/runtime/workflow_host.js +82 -42
- package/package.json +4 -3
package/dist/runtime/index.js
CHANGED
|
@@ -34,13 +34,15 @@ import { BUDGET_GUARDRAIL_RATE } from "./agent/model_rates.js";
|
|
|
34
34
|
import { SecretRedactor } from "./agent/secret_redactor.js";
|
|
35
35
|
import { RecordingSecretResolver } from "./recording_secret_resolver.js";
|
|
36
36
|
import { EngineLeafExecutor } from "./leaf_executor.js";
|
|
37
|
-
import { BudgetGate } from "./budget_gate.js";
|
|
37
|
+
import { BudgetGate, ComputeBreachWatcher } from "./budget_gate.js";
|
|
38
38
|
import { parseByoProviders } from "./direct_inference.js";
|
|
39
39
|
import { applyIdentityToEnv, connectIdentityRelayFd, relayFdFromEnv, workerDiagnostics, } from "./identity_relay.js";
|
|
40
40
|
import { FreezeCoordinator } from "./freeze_coordinator.js";
|
|
41
41
|
import { reseedUserspaceCsprng } from "./uniqueness_reseed.js";
|
|
42
42
|
import { resetHttpConnectionPool } from "./http_pool_reset.js";
|
|
43
43
|
import { WorkerWorkflowHost } from "./workflow_host.js";
|
|
44
|
+
import { buildHostCapabilities } from "./host_capabilities.js";
|
|
45
|
+
import { runShell } from "./shell_exec.js";
|
|
44
46
|
import { BrowserSessionManager } from "./browser_session.js";
|
|
45
47
|
import { loadGuestBrowserConfig, makeGuestBrowserBackend, connectSessionMcp, } from "./browser_session_backend.js";
|
|
46
48
|
import { ScreenCapture } from "./screen_capture.js";
|
|
@@ -165,10 +167,6 @@ export function assembleWorkerDeps(runtime) {
|
|
|
165
167
|
// the bill — the platform meters the actual per-leaf usage.
|
|
166
168
|
rate: BUDGET_GUARDRAIL_RATE,
|
|
167
169
|
startedAt: Date.now(),
|
|
168
|
-
// deadline_seconds is WALL-CLOCK from the run's ORIGINAL start (incl. suspended idle), so a run
|
|
169
|
-
// resumed past its deadline trips on its first cap check. Falls back to now on the first session
|
|
170
|
-
// (startedAt is being set this session anyway).
|
|
171
|
-
deadlineStartedAt: run.startedAt ?? Date.now(),
|
|
172
170
|
});
|
|
173
171
|
// Per-session id for per-turn metering identifiers (`<runId>:<sessionId>:<leafIndex>:<turnSeq>`) —
|
|
174
172
|
// a fresh value per worker session keeps a resumed run's events from colliding with a prior
|
|
@@ -239,10 +237,11 @@ export function assembleWorkerDeps(runtime) {
|
|
|
239
237
|
// it). Workspace-rooted at the same `/workspace` the leaf + tools use. Closed on the run's teardown
|
|
240
238
|
// (returned below as `lsp` → program_worker's finally) so no language-server process leaks.
|
|
241
239
|
const lspService = new LspService({ workspaceDir: runtime.workspaceRoot });
|
|
242
|
-
// Budget gate (docs/SUSPEND_POLICY.md Decision 3): a
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
240
|
+
// Budget gate (docs/SUSPEND_POLICY.md Decision 3): a breach of ANY budget cap (`max_usd` /
|
|
241
|
+
// `max_tokens` / `max_compute_seconds`) PARKS the run for approval instead of failing it. The
|
|
242
|
+
// gate needs the HOST (to park) but the host is constructed below with `leaf` — a genuine
|
|
243
|
+
// cycle, broken with a late-bound ref: `clear()` only ever runs mid-run, long after both exist.
|
|
244
|
+
// Wired to `budgetHost` immediately after the host is built.
|
|
246
245
|
let budgetHost = null;
|
|
247
246
|
const budgetGate = new BudgetGate(budget, {
|
|
248
247
|
budgetClearance: (gate) => {
|
|
@@ -253,6 +252,13 @@ export function assembleWorkerDeps(runtime) {
|
|
|
253
252
|
return budgetHost.budgetClearance(gate);
|
|
254
253
|
},
|
|
255
254
|
});
|
|
255
|
+
// Compute burns continuously (unlike usd/tokens, which only move at model calls), so a
|
|
256
|
+
// `max_compute_seconds` breach between seams is DETECTED on this timer and logged; the park
|
|
257
|
+
// itself lands at the run's next capability seam (see the watcher's doc block for why a
|
|
258
|
+
// timer-initiated park needs fleet-host coordination that doesn't exist yet). Stopped by the
|
|
259
|
+
// orchestrator on every terminal path via the returned `budgetWatch` handle.
|
|
260
|
+
const computeWatch = new ComputeBreachWatcher(budget, { runId: run.id });
|
|
261
|
+
computeWatch.start();
|
|
256
262
|
const leaf = new EngineLeafExecutor({
|
|
257
263
|
inference: broker,
|
|
258
264
|
// Direct BYO (D7): the claim's registry + the run's RECORDING resolver, so a provider key
|
|
@@ -404,9 +410,20 @@ export function assembleWorkerDeps(runtime) {
|
|
|
404
410
|
// (absent ⇒ the host throws "not available on this runner image").
|
|
405
411
|
...(browserSessions !== undefined ? { browserSessions } : {}),
|
|
406
412
|
phases: phaseTracker,
|
|
413
|
+
// The protocol's `shell` capability: host-side execution in the run's workspace (the old
|
|
414
|
+
// in-process execSync moved behind the wire with the redesign).
|
|
415
|
+
shell: (cmd, opts) => runShell(cmd, opts, { workspaceRoot: runtime.workspaceRoot, signal }),
|
|
416
|
+
// Live budget state for `usage.get` — read off the run-level meter (run-cumulative).
|
|
417
|
+
usage: () => budget.usageSnapshot(),
|
|
418
|
+
// Budget clearance at the host's blocking/spending seams (sleep/shell/workflows.call): the
|
|
419
|
+
// park points for a compute breach that lands between model calls.
|
|
420
|
+
budgetGate,
|
|
407
421
|
});
|
|
408
422
|
// Close the budget gate's cycle: the leaf (built above) parks THROUGH the host (built just now).
|
|
409
423
|
budgetHost = host;
|
|
424
|
+
// The protocol server's dispatch seam — a thin adapter over the host (workflows.call gains
|
|
425
|
+
// its output_schema slot, null until the broker delivers the callee's schema).
|
|
426
|
+
const capabilities = buildHostCapabilities(host);
|
|
410
427
|
// Late-bind the coordinator's per-run hooks now that the run-scoped objects exist.
|
|
411
428
|
if (freeze !== undefined) {
|
|
412
429
|
const coordinator = freeze;
|
|
@@ -452,6 +469,11 @@ export function assembleWorkerDeps(runtime) {
|
|
|
452
469
|
redactor.record(wake.api_token);
|
|
453
470
|
}
|
|
454
471
|
activeFlusher?.excludeIdle(wake.wall_clock_ms - freezeWallMs);
|
|
472
|
+
// The budget meter excludes the same frozen window: `max_compute_seconds` is ACTIVE
|
|
473
|
+
// on-CPU compute by definition (it "EXCLUDES sleep/wait" — the manifest schema's own
|
|
474
|
+
// words), so a sleep/humanInput/child-wait freeze must not count toward the cap any
|
|
475
|
+
// more than it counts toward billing.
|
|
476
|
+
budget.excludeIdle(wake.wall_clock_ms - freezeWallMs);
|
|
455
477
|
// Only now that the frozen window is excluded may the flush timer run again.
|
|
456
478
|
activeFlusher?.resume();
|
|
457
479
|
// Resume capture in a fresh segment epoch (a suspend/resume boundary is a segment boundary).
|
|
@@ -462,7 +484,7 @@ export function assembleWorkerDeps(runtime) {
|
|
|
462
484
|
// Hand the redactor back too: the worker scrubs a terminal error's message with it;
|
|
463
485
|
// workspace (when opted in) hydrates at start + persists at terminal.
|
|
464
486
|
return Promise.resolve({
|
|
465
|
-
|
|
487
|
+
capabilities,
|
|
466
488
|
redactor,
|
|
467
489
|
phases: phaseTracker,
|
|
468
490
|
// The orchestrator closes the per-run LSP on every terminal path (close() is idempotent + never
|
|
@@ -479,6 +501,9 @@ export function assembleWorkerDeps(runtime) {
|
|
|
479
501
|
setProgramDir: (dir) => {
|
|
480
502
|
programDir = dir;
|
|
481
503
|
},
|
|
504
|
+
// The orchestrator stops the compute-breach watcher on every terminal path (the interval is
|
|
505
|
+
// unref'd, so this is hygiene, not liveness).
|
|
506
|
+
budgetWatch: { stop: () => computeWatch.stop() },
|
|
482
507
|
// Always present now: hydrate must run BEFORE the program, but whether anything compounds
|
|
483
508
|
// isn't known until the run's agent() calls have registered their memory dirs. A run that
|
|
484
509
|
// selects nothing persists nothing and pays for nothing (WorkspaceStore.persist returns early).
|
|
@@ -508,7 +533,15 @@ export function assembleWorkerDeps(runtime) {
|
|
|
508
533
|
// lifecycle transition the claim IS — the wire's `running` frame.
|
|
509
534
|
runEvents.resumeAfter(claimed.lastEventCursor);
|
|
510
535
|
runEvents.emit({ kind: "run_status", status: "running" });
|
|
511
|
-
return
|
|
536
|
+
return {
|
|
537
|
+
run: claimed.run,
|
|
538
|
+
context: {
|
|
539
|
+
...(claimed.workflowVersion !== undefined
|
|
540
|
+
? { workflowVersion: claimed.workflowVersion }
|
|
541
|
+
: {}),
|
|
542
|
+
...(claimed.environment !== undefined ? { environment: claimed.environment } : {}),
|
|
543
|
+
},
|
|
544
|
+
};
|
|
512
545
|
},
|
|
513
546
|
},
|
|
514
547
|
versions: {
|
|
@@ -528,7 +561,7 @@ export function assembleWorkerDeps(runtime) {
|
|
|
528
561
|
// Extract the verified artifact tarball via system `tar` (same impl as workspace snapshots).
|
|
529
562
|
extractArchive: (tgzPath, destDir) => new TarWorkspaceArchiver().extract(tgzPath, destDir),
|
|
530
563
|
// Guarantee /workspace exists before the program runs (override-safe; the image also pre-creates
|
|
531
|
-
// it). Lets authors write to /workspace without a defensive mkdir — see
|
|
564
|
+
// it). Lets authors write to /workspace without a defensive mkdir — see the descriptor's `workspace` field.
|
|
532
565
|
ensureWorkspace: async () => {
|
|
533
566
|
await mkdir(runtime.workspaceRoot, { recursive: true });
|
|
534
567
|
},
|
|
@@ -31,14 +31,17 @@ export interface EngineLeafExecutorDeps {
|
|
|
31
31
|
* breach so the loop terminates mid-flight (the budget authority). */
|
|
32
32
|
budget: BudgetMeter;
|
|
33
33
|
/**
|
|
34
|
-
* Budget clearance, awaited before EVERY model call (docs/SUSPEND_POLICY.md Decision 3). When
|
|
35
|
-
* run's `max_usd`
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* Budget clearance, awaited before EVERY model call (docs/SUSPEND_POLICY.md Decision 3). When ANY
|
|
35
|
+
* of the run's budget caps (`max_usd` / `max_tokens` / `max_compute_seconds`) is breached this
|
|
36
|
+
* PARKS the run at a gate — the engine just sees a model call that took a long time, because the
|
|
37
|
+
* VM froze and resumed underneath it — and resolves once a responder raises the breached cap.
|
|
38
|
+
* Rejects with `BudgetGateCancelled` if they decline.
|
|
38
39
|
*
|
|
39
40
|
* Why here and not at the `reportUsage` breach check below: that callback is synchronous and fires
|
|
40
41
|
* mid-turn, and per SUSPEND_POLICY Decision 1 a partial turn is never discarded. The model-call
|
|
41
42
|
* boundary is the first safe place to park, which bounds overrun at one in-flight turn per leaf.
|
|
43
|
+
* (The workflow host adds further park points at its `sleep`/`shell`/`workflows.call` seams for
|
|
44
|
+
* the continuously-burning compute cap.)
|
|
42
45
|
*
|
|
43
46
|
* Absent ⇒ the legacy behavior: a breach fails the run at `reportUsage`.
|
|
44
47
|
*/
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// In the JS-body model the run is the workflow PROGRAM; the agent loop is no longer "the run" — it
|
|
4
4
|
// is an ephemeral leaf the program invokes via `agent(prompt, opts)`. This is the LeafExecutor the
|
|
5
5
|
// worker's WorkflowHost delegates `agent()` to. It runs ONE leaf to completion via the engine's
|
|
6
|
-
// `runAgentLeaf` (`@boardwalk-labs/engine/core`) — the SAME loop
|
|
7
|
-
// server
|
|
6
|
+
// `runAgentLeaf` (`@boardwalk-labs/engine/core`) — the SAME loop the self-hosted
|
|
7
|
+
// server runs — supplying a broker-backed `LeafIo`: the model call routes through the Runner Control
|
|
8
8
|
// API (the worker holds no model creds), events flow onto the run's v1 event stream, usage meters
|
|
9
9
|
// per-leaf + per-model through the broker, and secrets are redacted out of all model-bound content.
|
|
10
10
|
//
|
|
@@ -138,9 +138,14 @@ export class EngineLeafExecutor {
|
|
|
138
138
|
currentTurnId = turnId;
|
|
139
139
|
sink.emit(toRunEventBody(body, identity), turnId);
|
|
140
140
|
},
|
|
141
|
-
// Usage flows to the budget authority after EVERY model call. Feed the run-level meter
|
|
142
|
-
//
|
|
143
|
-
//
|
|
141
|
+
// Usage flows to the budget authority after EVERY model call. Feed the run-level meter; what a
|
|
142
|
+
// breach then does depends on whether the budget gate is wired. With a gate (production): do
|
|
143
|
+
// NOT throw — the in-flight turn completes (its tools run; SUSPEND_POLICY Decision 1 never
|
|
144
|
+
// discards a partial turn) and the run PARKS at its next park point (the next `streamModel`
|
|
145
|
+
// clearance, or a host capability seam). Without a gate (legacy/test paths): THROW — the
|
|
146
|
+
// engine loop propagates it and the run fails BUDGET_EXCEEDED before another model call is
|
|
147
|
+
// dispatched. On the gate path the breach turn ALSO meters to the broker below — its tokens
|
|
148
|
+
// were genuinely spent and must be billed (the old always-throw path silently skipped that).
|
|
144
149
|
reportUsage: (modelRefForUsage, usage) => {
|
|
145
150
|
const delta = toUsageDelta(usage);
|
|
146
151
|
// Cap on the turn's REAL upstream cost when the broker reported one (the result frame's
|
|
@@ -151,7 +156,7 @@ export class EngineLeafExecutor {
|
|
|
151
156
|
pendingCostMicros = null;
|
|
152
157
|
this.deps.budget.addUsage(delta, realCostUsd);
|
|
153
158
|
const breach = this.deps.budget.capBreachReason();
|
|
154
|
-
if (breach !== null) {
|
|
159
|
+
if (breach !== null && this.deps.budgetGate === undefined) {
|
|
155
160
|
throw new EngineError("BUDGET_EXCEEDED", `agent() leaf exceeded the run budget cap (${breach})`);
|
|
156
161
|
}
|
|
157
162
|
this.deps.meterUsage?.({
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ContextData } from "@boardwalk-labs/workflow/runtime";
|
|
2
|
+
import { type HostCapabilities } from "./host_server.js";
|
|
2
3
|
/**
|
|
3
4
|
* Link THIS runtime's `@boardwalk-labs/workflow` into the exec dir's `node_modules` so the program's
|
|
4
5
|
* bare import resolves from ANY program root — this link, not an ancestor `node_modules`, is what
|
|
5
|
-
* makes resolution work.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* resolves it to the REAL path, so the program gets the same module instance the host adapter was
|
|
10
|
-
* installed on (the singleton contract). `junction` covers Windows without elevation; a failed link
|
|
11
|
-
* is only logged, since a resolvable ancestor may still exist.
|
|
6
|
+
* makes resolution work. A symlink — not a copy — is load-bearing: Node resolves it to the REAL
|
|
7
|
+
* path, so the program gets the same module instance the loader's protocol client was installed
|
|
8
|
+
* on (the active-host singleton in the SDK's host_client). `junction` covers Windows without
|
|
9
|
+
* elevation; a failed link is only logged, since a resolvable ancestor may still exist.
|
|
12
10
|
*/
|
|
13
11
|
export declare function ensureSdkLink(execDir: string): Promise<void>;
|
|
14
12
|
/** Resolve the program's entry module inside the extraction dir, refusing any path that escapes it.
|
|
@@ -17,10 +15,18 @@ export declare function ensureSdkLink(execDir: string): Promise<void>;
|
|
|
17
15
|
* outside `dir` throws rather than importing code from elsewhere on the machine. */
|
|
18
16
|
export declare function resolveEntryPath(dir: string, entry: string): string;
|
|
19
17
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* Resolve a program entry by LANGUAGE, binding the ratified artifact layout: a TS/JS entry is a
|
|
19
|
+
* BUILT module at the artifact root (`index.mjs` — the CLI bundles, the api-server type-strips);
|
|
20
|
+
* a `.py` entry is a SOURCE path under `.bw-src/` (`main.py` ⇒ `<dir>/.bw-src/main.py` — Python
|
|
21
|
+
* has no bundle step, the shipped source is what runs). A leading `./` on the stored entry is
|
|
22
|
+
* tolerated (`join` collapses it). Both lanes keep the containment guard; for Python the guard's
|
|
23
|
+
* base is the `.bw-src` tree itself, so an escaping entry (`../evil.py`) is rejected even when
|
|
24
|
+
* it would still land inside the extract dir.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveProgramEntryPath(dir: string, entry: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Enforce I2: the extracted program must not live inside the run's workspace. Compares RESOLVED
|
|
29
|
+
* paths, and treats "the workspace itself" as inside.
|
|
24
30
|
*/
|
|
25
31
|
export declare function assertProgramRootOutsideWorkspace(programRoot: string, workspaceRoot: string): void;
|
|
26
32
|
export interface RunProgramArgs {
|
|
@@ -28,17 +34,24 @@ export interface RunProgramArgs {
|
|
|
28
34
|
runId: string;
|
|
29
35
|
/** The VERIFIED program artifact tarball (sha256 already checked against the pinned digest). */
|
|
30
36
|
tarball: Uint8Array;
|
|
31
|
-
/** Entry module to import after extraction (a safe relative POSIX path
|
|
37
|
+
/** Entry module to import after extraction (a safe relative POSIX path). TS/JS: the BUILT
|
|
38
|
+
* module at the artifact root (`index.mjs`). Python: the SOURCE path relative to the
|
|
39
|
+
* artifact's `.bw-src/` tree (`main.py` ⇒ `.bw-src/main.py` on disk). */
|
|
32
40
|
entry: string;
|
|
33
|
-
/**
|
|
41
|
+
/** The run's RAW JSON input (trigger payload / inline input). The revival pass is CLIENT-side. */
|
|
34
42
|
input: unknown;
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
|
|
43
|
+
/** The stored derived input schema (`null` for an untyped workflow) — carried on `bootstrap`
|
|
44
|
+
* so the SDK revives rich fields (`date-time` → `Date`, base64 → `Uint8Array`, …). */
|
|
45
|
+
inputSchema: Record<string, unknown> | null;
|
|
46
|
+
/** The stored derived output schema (`null` ⇒ the return persists unvalidated). */
|
|
47
|
+
outputSchema: Record<string, unknown> | null;
|
|
48
|
+
/** The context DATA for `bootstrap` (P3.3) — the client builds the live `Context` from it. */
|
|
49
|
+
context: ContextData;
|
|
38
50
|
}
|
|
39
51
|
export interface ProgramRunnerDeps {
|
|
40
|
-
/** The
|
|
41
|
-
|
|
52
|
+
/** The capability seam the host server dispatches onto (agent leaf, sleep hold, child calls,
|
|
53
|
+
* secrets, shell, usage, auth, browser, phase). */
|
|
54
|
+
capabilities: HostCapabilities;
|
|
42
55
|
/**
|
|
43
56
|
* The run's `/workspace` — the working directory AND `HOME` for author code (I1). Must already
|
|
44
57
|
* exist (the orchestrator's `ensureWorkspace` guarantees it); a missing workspace fails the run
|
|
@@ -46,42 +59,49 @@ export interface ProgramRunnerDeps {
|
|
|
46
59
|
*/
|
|
47
60
|
workspaceRoot: string;
|
|
48
61
|
/**
|
|
49
|
-
* Root the program artifact extracts under. MUST be outside {@link workspaceRoot} (I2) —
|
|
50
|
-
* because a bundle inside the workspace is tarred into every pre-sleep snapshot
|
|
51
|
-
* run's dir name is unique, accumulates there forever.
|
|
62
|
+
* Root the program artifact extracts under. MUST be outside {@link workspaceRoot} (I2) —
|
|
63
|
+
* enforced, because a bundle inside the workspace is tarred into every pre-sleep snapshot.
|
|
52
64
|
*/
|
|
53
65
|
programRoot: string;
|
|
54
66
|
/**
|
|
55
67
|
* Extract a gzipped tar file into a directory (created already). System `tar` in production
|
|
56
|
-
* (matches WorkspaceArchiver); injected in tests.
|
|
57
|
-
* on-disk assets (markdown skills, templates) sit where the program expects them.
|
|
68
|
+
* (matches WorkspaceArchiver); injected in tests.
|
|
58
69
|
*/
|
|
59
70
|
extract: (tgzPath: string, destDir: string) => Promise<void>;
|
|
60
71
|
/**
|
|
61
|
-
* Called once with the extracted program directory, right after the artifact is unpacked
|
|
62
|
-
*
|
|
63
|
-
* (`<dir>/skills/<name>.md`).
|
|
72
|
+
* Called once with the extracted program directory, right after the artifact is unpacked
|
|
73
|
+
* (before `run()` is invoked). The worker uses it to point the `agent()` leaf at the run's
|
|
74
|
+
* bundled files (`<dir>/skills/<name>.md`).
|
|
64
75
|
*/
|
|
65
76
|
onExtracted?: (programDir: string) => void;
|
|
66
77
|
/**
|
|
67
|
-
* Scrubs known secret values out of a string (the run's `SecretRedactor.redactText`). Applied
|
|
68
|
-
*
|
|
69
|
-
* that resolves a secret and then throws it in an error message must NOT land that secret raw in the
|
|
70
|
-
* logs or the finalized run output. Defaults to identity (tests/local).
|
|
78
|
+
* Scrubs known secret values out of a string (the run's `SecretRedactor.redactText`). Applied
|
|
79
|
+
* to a thrown error's message/code/hint before logging + finalize. Defaults to identity.
|
|
71
80
|
*/
|
|
72
81
|
redactText?: (text: string) => string;
|
|
73
82
|
/**
|
|
74
|
-
* Called once with the
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* throw (a telemetry hiccup can't change the run's result). Absent ⇒ no output entry.
|
|
83
|
+
* Called once with the run's reported return IFF it is non-null (a void return sends null,
|
|
84
|
+
* which is not an author-declared output). The worker wires this to emit an `output` activity
|
|
85
|
+
* entry into the run's event log. Best-effort: it must not throw.
|
|
78
86
|
*/
|
|
79
87
|
onOutput?: (value: unknown) => void;
|
|
88
|
+
/**
|
|
89
|
+
* The run's cooperative-cancellation signal. The host server pushes the `cancel` notification
|
|
90
|
+
* to the program when it fires (the SDK aborts `context.signal`); the capability layer already
|
|
91
|
+
* honors it server-side at every hook.
|
|
92
|
+
*/
|
|
93
|
+
signal?: AbortSignal | undefined;
|
|
94
|
+
/** Override the socket directory (tests). Default `os.tmpdir()` (short paths — sun_path cap). */
|
|
95
|
+
sockDir?: string | undefined;
|
|
96
|
+
/** Interpreter a `.py` entry is launched with (P5.5). Default `python3`, resolved on PATH —
|
|
97
|
+
* the base image bakes one CPython (P5.3). May be an absolute path. */
|
|
98
|
+
pythonInterpreter?: string | undefined;
|
|
99
|
+
/** How long after SIGTERM an aborted Python child gets before SIGKILL. Default 5s. */
|
|
100
|
+
pythonKillGraceMs?: number | undefined;
|
|
80
101
|
}
|
|
81
|
-
/** Terminal result of running a workflow program. `output` is
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* holds the process, and the body simply continues when the wait is over. */
|
|
102
|
+
/** Terminal result of running a workflow program. `output` is the validated value `run()`
|
|
103
|
+
* returned (`null` for void); for a failure it's null and `error` is set. A waiting seam never
|
|
104
|
+
* surfaces here — it freezes with the VM or holds the process, and `run()` simply continues. */
|
|
85
105
|
export type ProgramResult = {
|
|
86
106
|
kind: "completed";
|
|
87
107
|
output: unknown;
|
|
@@ -95,8 +115,8 @@ export type ProgramResult = {
|
|
|
95
115
|
};
|
|
96
116
|
};
|
|
97
117
|
/**
|
|
98
|
-
* Run a workflow program to completion
|
|
99
|
-
*
|
|
100
|
-
*
|
|
118
|
+
* Run a workflow program to completion: start the host-protocol server, extract the VERIFIED
|
|
119
|
+
* artifact, drive the loader (`bootstrap` → import entry → `run(input, context)` →
|
|
120
|
+
* `reportReturn`), and return the terminal result. Always tears the server + temp tree down.
|
|
101
121
|
*/
|
|
102
122
|
export declare function runWorkflowProgram(args: RunProgramArgs, deps: ProgramRunnerDeps): Promise<ProgramResult>;
|