@inflexa-ai/harness 0.46.2 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/agents/conversation-agent.d.ts +7 -6
- package/dist/agents/conversation-agent.js +26 -4
- package/dist/agents/report-session-agent.d.ts +14 -14
- package/dist/agents/report-session-agent.js +40 -26
- package/dist/agents/sandbox/shared.d.ts +18 -13
- package/dist/agents/sandbox/shared.js +42 -27
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/sandbox-catalog.d.ts +2 -0
- package/dist/agents/sandbox-catalog.js +30 -1
- package/dist/app/synthesize-run.d.ts +10 -6
- package/dist/app/synthesize-run.js +4 -2
- package/dist/contracts/analogy-report.d.ts +46 -2
- package/dist/contracts/analogy-report.js +31 -25
- package/dist/contracts/chat-parts.d.ts +3 -14
- package/dist/contracts/report-reference.d.ts +2 -1
- package/dist/contracts/report-reference.js +2 -1
- package/dist/contracts/schemas/chat-parts.d.ts +0 -27
- package/dist/contracts/schemas/chat-parts.js +1 -3
- package/dist/execution/run-canceler.d.ts +2 -0
- package/dist/execution/run-canceler.js +9 -0
- package/dist/execution/run-synthesis.d.ts +36 -8
- package/dist/execution/run-synthesis.js +114 -32
- package/dist/index.d.ts +5 -6
- package/dist/index.js +5 -7
- package/dist/input-scan/enrich.d.ts +5 -10
- package/dist/input-scan/enrich.js +12 -8
- package/dist/lib/hooks.d.ts +1 -1
- package/dist/lib/markdown-pdf.d.ts +27 -0
- package/dist/lib/markdown-pdf.js +236 -0
- package/dist/lib/metrics.d.ts +31 -0
- package/dist/lib/metrics.js +64 -0
- package/dist/lib/pdf-text.d.ts +23 -0
- package/dist/lib/pdf-text.js +34 -0
- package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +3 -5
- package/dist/lib/regulatory-corpus/fetch-ich.js +3 -5
- package/dist/loop/chat-stream.js +2 -1
- package/dist/loop/run-loop.d.ts +5 -0
- package/dist/loop/run-loop.js +30 -2
- package/dist/loop/tool-name-repair.d.ts +11 -0
- package/dist/loop/tool-name-repair.js +23 -0
- package/dist/loop/types.d.ts +19 -0
- package/dist/memory/card-builders.d.ts +1 -1
- package/dist/prompts/SOUL.d.ts +6 -6
- package/dist/prompts/SOUL.js +22 -121
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +35 -101
- package/dist/prompts/briefing.d.ts +20 -11
- package/dist/prompts/briefing.js +36 -19
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +103 -383
- package/dist/prompts/planner.d.ts +1 -5
- package/dist/prompts/planner.js +45 -128
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +29 -58
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +3 -3
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +12 -47
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +4 -5
- package/dist/prompts/sandbox/data-profiler.js +11 -34
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +3 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +23 -73
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +3 -1
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +3 -4
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +5 -4
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +3 -1
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +3 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +3 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +3 -1
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +3 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +4 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +3 -1
- package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
- package/dist/prompts/sandbox/scientific-executor.js +0 -12
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +3 -1
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +4 -4
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +10 -29
- package/dist/prompts/sandbox-standards.d.ts +15 -18
- package/dist/prompts/sandbox-standards.js +96 -305
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +24 -90
- package/dist/provenance/collector.d.ts +2 -5
- package/dist/provenance/collector.js +2 -5
- package/dist/provenance/seam.d.ts +36 -1
- package/dist/provenance/workspace-reads.d.ts +36 -0
- package/dist/provenance/workspace-reads.js +33 -0
- package/dist/report-model/production-resolver.d.ts +22 -52
- package/dist/report-model/production-resolver.js +43 -112
- package/dist/report-render/chart-export.d.ts +9 -0
- package/dist/report-render/chart-export.js +1 -1
- package/dist/report-render/views/chart-view.d.ts +1 -0
- package/dist/report-render/views/chart-view.js +1 -0
- package/dist/report-render/views/lineage.d.ts +1 -0
- package/dist/report-render/views/page-view.d.ts +1 -0
- package/dist/report-render/views/page-view.js +1 -0
- package/dist/report-render/views/prose.d.ts +1 -0
- package/dist/report-render/views/prose.js +1 -0
- package/dist/report-render/views/references-view.d.ts +1 -0
- package/dist/report-render/views/values.d.ts +1 -0
- package/dist/report-render/views/values.js +1 -0
- package/dist/runtime/assemble.d.ts +32 -12
- package/dist/runtime/assemble.js +49 -38
- package/dist/sandbox/client.d.ts +5 -3
- package/dist/sandbox/create-sandbox.d.ts +7 -12
- package/dist/sandbox/create-sandbox.js +15 -37
- package/dist/sandbox/docker-client.d.ts +1 -3
- package/dist/sandbox/docker-client.js +3 -5
- package/dist/sandbox/exec-outcome.d.ts +11 -11
- package/dist/sandbox/exec-outcome.js +22 -22
- package/dist/sandbox/exec.d.ts +5 -0
- package/dist/sandbox/exec.js +9 -1
- package/dist/sandbox/identity.d.ts +4 -16
- package/dist/sandbox/identity.js +5 -21
- package/dist/sandbox/k8s-client.d.ts +0 -1
- package/dist/sandbox/k8s-client.js +8 -13
- package/dist/sandbox/lease-names.d.ts +6 -0
- package/dist/sandbox/lease-names.js +12 -0
- package/dist/sandbox/leases.d.ts +105 -0
- package/dist/sandbox/leases.js +519 -0
- package/dist/sandbox/liveness.d.ts +5 -2
- package/dist/sandbox/mount-plan.d.ts +10 -1
- package/dist/sandbox/mount-plan.js +7 -1
- package/dist/sandbox/reconciler.d.ts +58 -0
- package/dist/sandbox/reconciler.js +153 -0
- package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
- package/dist/sandbox/types.d.ts +25 -18
- package/dist/sandbox/types.js +10 -4
- package/dist/schemas/plan-schemas.d.ts +0 -21
- package/dist/schemas/plan-schemas.js +2 -3
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/schemas/run-synthesis.js +3 -3
- package/dist/schemas/validate-plan.d.ts +1 -14
- package/dist/schemas/validate-plan.js +5 -24
- package/dist/schemas/workflow-state.d.ts +3 -32
- package/dist/schemas/workflow-state.js +4 -13
- package/dist/state/index.d.ts +2 -3
- package/dist/state/index.js +1 -2
- package/dist/state/migrations/20261030120000_lease_the_sandboxes.d.ts +7 -0
- package/dist/state/migrations/20261030120000_lease_the_sandboxes.js +34 -0
- package/dist/state/migrations/20261030120100_drop_the_report_derivations.d.ts +5 -0
- package/dist/state/migrations/20261030120100_drop_the_report_derivations.js +9 -0
- package/dist/state/migrations/20261030120200_keep_the_report_derivations.d.ts +6 -0
- package/dist/state/migrations/20261030120200_keep_the_report_derivations.js +10 -0
- package/dist/state/migrations/index.js +6 -0
- package/dist/state/purge-analysis.d.ts +4 -2
- package/dist/state/purge-analysis.js +22 -11
- package/dist/state/sandbox-leases.d.ts +81 -0
- package/dist/state/sandbox-leases.js +247 -0
- package/dist/state/schema.d.ts +1 -13
- package/dist/state/schema.js +1 -6
- package/dist/state/step-executions.d.ts +13 -2
- package/dist/state/step-executions.js +23 -4
- package/dist/tasks/data-profile.d.ts +40 -2
- package/dist/tasks/data-profile.js +89 -87
- package/dist/tools/ad-hoc-router.d.ts +1 -6
- package/dist/tools/ad-hoc-router.js +5 -48
- package/dist/tools/bio/comptox.js +1 -1
- package/dist/tools/bio/drug-gene-interactions.js +1 -1
- package/dist/tools/bio/gene-disease-evidence.js +1 -1
- package/dist/tools/bio/keys.d.ts +1 -1
- package/dist/tools/bio/pubmed.js +31 -0
- package/dist/tools/bio/search-gene.js +17 -0
- package/dist/tools/bio/search-geo-datasets.js +13 -0
- package/dist/tools/bio/search-regulatory-approvals.js +1 -1
- package/dist/tools/define-tool.d.ts +25 -2
- package/dist/tools/define-tool.js +11 -1
- package/dist/tools/display/show-user.js +1 -1
- package/dist/tools/execute-analysis.d.ts +1 -3
- package/dist/tools/execute-analysis.js +5 -33
- package/dist/tools/report-authoring/authoring-tools.d.ts +2 -2
- package/dist/tools/report-authoring/authoring-tools.js +31 -6
- package/dist/tools/report-session/index.d.ts +0 -1
- package/dist/tools/report-session/index.js +0 -1
- package/dist/tools/report-session/script-tools.d.ts +33 -0
- package/dist/tools/report-session/script-tools.js +186 -0
- package/dist/tools/research/generate-analogy-report.d.ts +16 -9
- package/dist/tools/research/generate-analogy-report.js +111 -49
- package/dist/tools/research/generate-plan.d.ts +102 -7
- package/dist/tools/research/generate-plan.js +74 -43
- package/dist/tools/research/index.d.ts +4 -3
- package/dist/tools/research/index.js +4 -3
- package/dist/tools/research/inspect-run.d.ts +0 -1
- package/dist/tools/research/inspect-run.js +5 -10
- package/dist/tools/research/search-arxiv.js +15 -0
- package/dist/tools/research/search-github-repos.js +13 -0
- package/dist/tools/research/search-semantic-scholar.js +16 -0
- package/dist/tools/research/synthesis-lookups.d.ts +21 -0
- package/dist/tools/research/synthesis-lookups.js +29 -0
- package/dist/tools/research/web-fetch.d.ts +1 -1
- package/dist/tools/research/web-fetch.js +14 -12
- package/dist/tools/sandbox/lease-tools.d.ts +32 -0
- package/dist/tools/sandbox/lease-tools.js +60 -0
- package/dist/tools/script/archive.d.ts +34 -0
- package/dist/tools/script/archive.js +182 -0
- package/dist/tools/script/codemode-worker.d.ts +5 -0
- package/dist/tools/script/codemode-worker.js +5 -0
- package/dist/tools/script/file-tools.d.ts +106 -0
- package/dist/tools/script/file-tools.js +491 -0
- package/dist/tools/script/metrics.d.ts +30 -0
- package/dist/tools/script/metrics.js +53 -0
- package/dist/tools/script/quickjs.wasm +0 -0
- package/dist/tools/script/run-script.d.ts +76 -0
- package/dist/tools/script/run-script.js +413 -0
- package/dist/tools/script/scratch.d.ts +84 -0
- package/dist/tools/script/scratch.js +145 -0
- package/dist/tools/start-report-session.js +1 -1
- package/dist/tools/workspace/edit-file.js +2 -1
- package/dist/tools/workspace/execute-command.d.ts +9 -13
- package/dist/tools/workspace/execute-command.js +23 -19
- package/dist/tools/workspace/file-stat.js +1 -1
- package/dist/tools/workspace/grep.d.ts +5 -2
- package/dist/tools/workspace/grep.js +19 -10
- package/dist/tools/workspace/mutator.d.ts +51 -4
- package/dist/tools/workspace/mutator.js +54 -13
- package/dist/tools/workspace/read-file.d.ts +5 -2
- package/dist/tools/workspace/read-file.js +15 -11
- package/dist/tools/workspace/read-surface.d.ts +17 -0
- package/dist/tools/workspace/read-surface.js +17 -0
- package/dist/tools/workspace/result-bounds.d.ts +1 -0
- package/dist/tools/workspace/run-exec.d.ts +12 -13
- package/dist/tools/workspace/run-exec.js +13 -13
- package/dist/tools/workspace/scan-inputs.d.ts +2 -5
- package/dist/tools/workspace/scan-inputs.js +2 -3
- package/dist/tools/workspace/show-file.js +1 -1
- package/dist/tools/workspace/show-plan.js +4 -3
- package/dist/tools/workspace/write-file.js +2 -1
- package/dist/workflows/execute-analysis-scheduler.d.ts +0 -26
- package/dist/workflows/execute-analysis-scheduler.js +12 -32
- package/dist/workflows/execute-analysis.d.ts +17 -35
- package/dist/workflows/execute-analysis.js +13 -107
- package/dist/workflows/pause.js +29 -22
- package/dist/workflows/sandbox-step.d.ts +16 -31
- package/dist/workflows/sandbox-step.js +36 -71
- package/dist/workflows/test/sql-writes.d.ts +0 -1
- package/dist/workflows/test/sql-writes.js +0 -2
- package/dist/workspace/filesystem.d.ts +2 -2
- package/dist/workspace/filesystem.js +10 -5
- package/dist/workspace/paths.d.ts +19 -1
- package/dist/workspace/paths.js +22 -1
- package/dist/workspace/session-files.d.ts +35 -0
- package/dist/workspace/session-files.js +16 -0
- package/package.json +7 -3
- package/dist/prompts/literature-reviewer.d.ts +0 -1
- package/dist/prompts/literature-reviewer.js +0 -114
- package/dist/sandbox/reaper.d.ts +0 -81
- package/dist/sandbox/reaper.js +0 -161
- package/dist/sandbox/step-sandbox.d.ts +0 -47
- package/dist/sandbox/step-sandbox.js +0 -53
- package/dist/state/active-sandboxes.d.ts +0 -33
- package/dist/state/active-sandboxes.js +0 -65
- package/dist/tasks/extract-values-script.d.ts +0 -64
- package/dist/tasks/extract-values-script.js +0 -180
- package/dist/tasks/extract-values.d.ts +0 -77
- package/dist/tasks/extract-values.js +0 -146
- package/dist/tools/report-session/derive-table.d.ts +0 -164
- package/dist/tools/report-session/derive-table.js +0 -419
- package/dist/tools/research/literature-reviewer.d.ts +0 -42
- package/dist/tools/research/literature-reviewer.js +0 -107
- package/dist/workflows/metrics.d.ts +0 -23
- package/dist/workflows/metrics.js +0 -38
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Internal helper
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* of `scan_inputs` report progress in one shape.
|
|
2
|
+
* Internal helper of the tools that run a sandbox command through the lease of their step. It wraps each sandbox
|
|
3
|
+
* event in a `data-sandbox-event` part, thus `execute_command` and the container readout of `scan_inputs` report
|
|
4
|
+
* progress in one shape.
|
|
6
5
|
*
|
|
7
|
-
* Not a registered tool — `defineTool` is called by the user-facing tools
|
|
8
|
-
* (`execute-command.ts`) which wrap this.
|
|
6
|
+
* Not a registered tool — `defineTool` is called by the user-facing tools (`execute-command.ts`) which wrap this.
|
|
9
7
|
*/
|
|
10
|
-
import type
|
|
11
|
-
import type {
|
|
12
|
-
import type {
|
|
8
|
+
import { type Result } from "neverthrow";
|
|
9
|
+
import type { BoundLease } from "../../sandbox/leases.js";
|
|
10
|
+
import type { ExecResult } from "../../sandbox/types.js";
|
|
11
|
+
import type { EmitFn, ToolError } from "../define-tool.js";
|
|
13
12
|
export type RunExecArgs = {
|
|
14
|
-
readonly
|
|
15
|
-
readonly sandbox: SandboxRef;
|
|
13
|
+
readonly lease: BoundLease;
|
|
16
14
|
readonly command: readonly string[];
|
|
17
15
|
readonly cwd?: string;
|
|
18
16
|
readonly env?: Record<string, string>;
|
|
19
17
|
readonly timeoutSeconds?: number;
|
|
20
|
-
readonly deadlineMs: number;
|
|
21
18
|
readonly emit: EmitFn;
|
|
22
19
|
};
|
|
23
|
-
|
|
20
|
+
/** The error of an exec with no lease. The order "acquire, then exec" is a rule of the code, not of a prompt. */
|
|
21
|
+
export declare const NO_SANDBOX_ERROR = "No sandbox is held. Call acquire_sandbox with the size that the command needs, then run the command again.";
|
|
22
|
+
export declare function runSandboxExec(args: RunExecArgs): Promise<Result<ExecResult, ToolError>>;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Internal helper
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* of `scan_inputs` report progress in one shape.
|
|
2
|
+
* Internal helper of the tools that run a sandbox command through the lease of their step. It wraps each sandbox
|
|
3
|
+
* event in a `data-sandbox-event` part, thus `execute_command` and the container readout of `scan_inputs` report
|
|
4
|
+
* progress in one shape.
|
|
6
5
|
*
|
|
7
|
-
* Not a registered tool — `defineTool` is called by the user-facing tools
|
|
8
|
-
* (`execute-command.ts`) which wrap this.
|
|
6
|
+
* Not a registered tool — `defineTool` is called by the user-facing tools (`execute-command.ts`) which wrap this.
|
|
9
7
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
import { err, ok } from "neverthrow";
|
|
9
|
+
/** The error of an exec with no lease. The order "acquire, then exec" is a rule of the code, not of a prompt. */
|
|
10
|
+
export const NO_SANDBOX_ERROR = "No sandbox is held. Call acquire_sandbox with the size that the command needs, then run the command again.";
|
|
11
|
+
export async function runSandboxExec(args) {
|
|
12
|
+
const ran = await args.lease.exec({ command: args.command, cwd: args.cwd, env: args.env, timeoutSeconds: args.timeoutSeconds }, (event) => args.emit({ type: "data-sandbox-event", data: { event } }));
|
|
13
|
+
if (ran.isOk())
|
|
14
|
+
return ok(ran.value);
|
|
15
|
+
const detail = ran.error.detail === undefined ? "" : ` (${ran.error.detail})`;
|
|
16
|
+
return err({ error: `${NO_SANDBOX_ERROR}${detail}`, retryable: false });
|
|
17
17
|
}
|
|
@@ -12,17 +12,14 @@
|
|
|
12
12
|
* footer-indexed container reaches into the sandbox (see `input-scan/enrich.ts`).
|
|
13
13
|
*/
|
|
14
14
|
import { z } from "zod";
|
|
15
|
-
import type {
|
|
16
|
-
import type { SandboxRef } from "../../sandbox/types.js";
|
|
15
|
+
import type { BoundLease } from "../../sandbox/leases.js";
|
|
17
16
|
import type { WorkspaceFilesystem } from "../../workspace/filesystem.js";
|
|
18
17
|
import { type Tool } from "../define-tool.js";
|
|
19
18
|
export type ScanInputsDeps = {
|
|
20
19
|
readonly workspaceFs: WorkspaceFilesystem;
|
|
21
20
|
readonly analysisId: string;
|
|
22
21
|
/** Omit to skip the container readouts; the prefix readouts run regardless. */
|
|
23
|
-
readonly
|
|
24
|
-
readonly sandbox?: SandboxRef;
|
|
25
|
-
readonly deadlineMs: () => number;
|
|
22
|
+
readonly lease?: BoundLease;
|
|
26
23
|
};
|
|
27
24
|
type ScanInputsOutput = {
|
|
28
25
|
readonly path: string;
|
|
@@ -32,6 +32,7 @@ const ScanInputsInputSchema = z.object({
|
|
|
32
32
|
export function createScanInputsTool(deps) {
|
|
33
33
|
return defineTool({
|
|
34
34
|
id: "scan_inputs",
|
|
35
|
+
ownStep: true,
|
|
35
36
|
description: "Scan a directory of input files and report what is OBSERVABLE about it: per-format counts, " +
|
|
36
37
|
"the shapes its filenames form (sets of files whose names differ only at marked positions), " +
|
|
37
38
|
"the distinct values each varying position takes, how those positions co-occur, value overlap " +
|
|
@@ -55,10 +56,8 @@ export function createScanInputsTool(deps) {
|
|
|
55
56
|
shapes: scan.manifest.shapes,
|
|
56
57
|
session: ctx.session,
|
|
57
58
|
fs: deps.workspaceFs,
|
|
58
|
-
|
|
59
|
-
sandbox: deps.sandbox,
|
|
59
|
+
lease: deps.lease,
|
|
60
60
|
mountRoot: `/${deps.analysisId}`,
|
|
61
|
-
deadlineMs: deps.deadlineMs(),
|
|
62
61
|
emit: ctx.emit,
|
|
63
62
|
});
|
|
64
63
|
const manifest = { ...scan.manifest, shapes };
|
|
@@ -30,7 +30,7 @@ const ShowFileInputSchema = z.object({
|
|
|
30
30
|
});
|
|
31
31
|
export const showFileTool = defineTool({
|
|
32
32
|
id: "show_file",
|
|
33
|
-
description: "Display EXISTING analysis artifacts in chat by path — images, CSVs, PDFs, notebooks, logs. " +
|
|
33
|
+
description: "Display EXISTING analysis artifacts in chat by path — images, CSVs, PDFs, notebooks, logs, and a `.pdb` or `.cif` structure that a step wrote. " +
|
|
34
34
|
"The card references the file directly and its content is fetched when the user views it: you never read, " +
|
|
35
35
|
"inline, or paste the bytes. Pick this tool by what you are referencing, not by how the output looks: " +
|
|
36
36
|
"NOT for content you synthesized (a chart, a table, a snippet you invented — use `show_user`), NOT for a stored plan (`show_plan`). " +
|
|
@@ -26,12 +26,13 @@ export function createShowPlanTool(pool) {
|
|
|
26
26
|
return defineTool({
|
|
27
27
|
id: "show_plan",
|
|
28
28
|
description: "Display a STORED plan in chat by its planId. The plan is loaded from storage and embedded in the card, " +
|
|
29
|
-
"so the UI renders it directly. Use it to
|
|
30
|
-
"
|
|
29
|
+
"so the UI renders it directly. Use it when the user asks to see an earlier plan again: `generate_plan` shows the card " +
|
|
30
|
+
"of the plan that it makes. " +
|
|
31
31
|
"Pick this tool by what you are referencing, not by how the output looks: " +
|
|
32
32
|
"NOT for a plan you are drafting in conversation (that is `generate_plan`), NOT for other synthesized content " +
|
|
33
33
|
"(`show_user`), NOT for analysis files (`show_file`). Never re-type a plan's steps into a `show_user` markdown " +
|
|
34
|
-
"card — that renders a second, divergent copy of the same plan."
|
|
34
|
+
"card — that renders a second, divergent copy of the same plan. " +
|
|
35
|
+
"`shown: false` with `plan_not_found` means that the planId is no longer valid: generate a new plan, because a retry of the same planId fails the same way.",
|
|
35
36
|
inputSchema: ShowPlanInputSchema,
|
|
36
37
|
describeCall: ({ planId }) => planId,
|
|
37
38
|
execute: async ({ planId, title }, ctx) => {
|
|
@@ -27,7 +27,8 @@ export function createWriteFileTool(deps) {
|
|
|
27
27
|
"missing parent directories. On success it returns `status: ok`, the absolute " +
|
|
28
28
|
"'/<analysisId>/...' path, and `bytesWritten`. A path outside the working directory " +
|
|
29
29
|
"returns an `out_of_prefix` data variant (no I/O), one escaping the analysis tree returns " +
|
|
30
|
-
"`out_of_scope`, and a symbolic link on the path returns `symlink_denied`."
|
|
30
|
+
"`out_of_scope`, and a symbolic link on the path returns `symlink_denied`. A path under the staged " +
|
|
31
|
+
"inputs returns `read_only` with the reason.",
|
|
31
32
|
inputSchema: WriteFileInputSchema,
|
|
32
33
|
// The path only. `content` is a whole file and must never ride a display
|
|
33
34
|
// channel, which the emit-site length cap enforces regardless.
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
* pure means parent recovery replays the same dispatch sequence given the
|
|
18
18
|
* same persisted child outcomes.
|
|
19
19
|
*/
|
|
20
|
-
import type { MachineBudget, ResourceSpec } from "../config/resource-limits.js";
|
|
21
20
|
/**
|
|
22
21
|
* The minimum shape the scheduler needs from a plan step. Larger workflow-state
|
|
23
22
|
* schemas are a superset of this, keeping the scheduler decoupled.
|
|
@@ -26,22 +25,6 @@ export type PlanStep = {
|
|
|
26
25
|
readonly id: string;
|
|
27
26
|
readonly depends_on: readonly string[];
|
|
28
27
|
};
|
|
29
|
-
/** Budget-admission inputs — the snapshotted machine budget from the workflow
|
|
30
|
-
* input plus each step's plan-declared resources. */
|
|
31
|
-
export type BudgetAdmission = {
|
|
32
|
-
readonly budget: MachineBudget;
|
|
33
|
-
readonly resourcesByStepId: Readonly<Record<string, ResourceSpec>>;
|
|
34
|
-
};
|
|
35
|
-
/** Partition of the dependency-satisfied steps under budget admission. */
|
|
36
|
-
export type ScheduledSteps = {
|
|
37
|
-
/** Steps to start now — they fit the remaining budget capacity. */
|
|
38
|
-
readonly admit: readonly string[];
|
|
39
|
-
/** Dependency-satisfied steps held until in-flight steps free capacity. */
|
|
40
|
-
readonly heldForCapacity: readonly string[];
|
|
41
|
-
/** Steps whose declared resources exceed the budget outright — they could
|
|
42
|
-
* never be admitted even against an empty budget. */
|
|
43
|
-
readonly neverFits: readonly string[];
|
|
44
|
-
};
|
|
45
28
|
/** Thrown when the plan DAG contains a dependency cycle. */
|
|
46
29
|
export declare class CycleError extends Error {
|
|
47
30
|
readonly involvedSteps: readonly string[];
|
|
@@ -71,17 +54,8 @@ export declare function validatePlanDag<S extends PlanStep>(steps: readonly S[])
|
|
|
71
54
|
* completion (or on parent recovery) and dispatches each returned id via
|
|
72
55
|
* `DBOS.startWorkflow`. Order in the returned array follows the plan's
|
|
73
56
|
* declared order for deterministic dispatch.
|
|
74
|
-
*
|
|
75
|
-
* With `admission`, the dependency-satisfied steps are additionally gated on
|
|
76
|
-
* the machine budget and partitioned instead of returned flat: a step is
|
|
77
|
-
* admitted only while the declared resources of the in-flight steps
|
|
78
|
-
* (`startedSet`) plus every step admitted earlier in the round leave room for
|
|
79
|
-
* it. Admission is greedy in plan order with skip-over — a candidate that does
|
|
80
|
-
* not fit never blocks a later, smaller one. Determinism holds because every
|
|
81
|
-
* input derives from the workflow input and checkpointed completion state.
|
|
82
57
|
*/
|
|
83
58
|
export declare function scheduleReady(steps: readonly PlanStep[], completedSet: ReadonlySet<string>, startedSet?: ReadonlySet<string>): string[];
|
|
84
|
-
export declare function scheduleReady(steps: readonly PlanStep[], completedSet: ReadonlySet<string>, startedSet: ReadonlySet<string>, admission: BudgetAdmission): ScheduledSteps;
|
|
85
59
|
/**
|
|
86
60
|
* Compute a depth per step. A step with no dependencies is level 0; a step
|
|
87
61
|
* with deps is `max(level(dep)) + 1`. Used by the UI for column layout in
|
|
@@ -113,7 +113,17 @@ function detectCycle(steps) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
// ── Scheduling ───────────────────────────────────────────────────────
|
|
117
|
+
/**
|
|
118
|
+
* Return every step that is now eligible to start: all of its `depends_on`
|
|
119
|
+
* are in `completedSet`, and the step itself is not yet in `startedSet`.
|
|
120
|
+
*
|
|
121
|
+
* Pure function — the parent workflow recomputes this after every child
|
|
122
|
+
* completion (or on parent recovery) and dispatches each returned id via
|
|
123
|
+
* `DBOS.startWorkflow`. Order in the returned array follows the plan's
|
|
124
|
+
* declared order for deterministic dispatch.
|
|
125
|
+
*/
|
|
126
|
+
export function scheduleReady(steps, completedSet, startedSet = new Set()) {
|
|
117
127
|
const ready = [];
|
|
118
128
|
for (const step of steps) {
|
|
119
129
|
if (startedSet.has(step.id) || completedSet.has(step.id))
|
|
@@ -122,37 +132,7 @@ export function scheduleReady(steps, completedSet, startedSet = new Set(), admis
|
|
|
122
132
|
ready.push(step.id);
|
|
123
133
|
}
|
|
124
134
|
}
|
|
125
|
-
|
|
126
|
-
return ready;
|
|
127
|
-
const { budget, resourcesByStepId } = admission;
|
|
128
|
-
// A step with no declared resources weighs nothing — it is never throttled,
|
|
129
|
-
// matching the unbudgeted legacy behavior for that step.
|
|
130
|
-
const weightOf = (id) => resourcesByStepId[id] ?? { cpu: 0, memoryGb: 0 };
|
|
131
|
-
let usedCpu = 0;
|
|
132
|
-
let usedMemoryGb = 0;
|
|
133
|
-
for (const id of startedSet) {
|
|
134
|
-
const w = weightOf(id);
|
|
135
|
-
usedCpu += w.cpu;
|
|
136
|
-
usedMemoryGb += w.memoryGb;
|
|
137
|
-
}
|
|
138
|
-
const admit = [];
|
|
139
|
-
const heldForCapacity = [];
|
|
140
|
-
const neverFits = [];
|
|
141
|
-
for (const id of ready) {
|
|
142
|
-
const w = weightOf(id);
|
|
143
|
-
if (w.cpu > budget.cpu || w.memoryGb > budget.memoryGb) {
|
|
144
|
-
neverFits.push(id);
|
|
145
|
-
}
|
|
146
|
-
else if (usedCpu + w.cpu <= budget.cpu && usedMemoryGb + w.memoryGb <= budget.memoryGb) {
|
|
147
|
-
admit.push(id);
|
|
148
|
-
usedCpu += w.cpu;
|
|
149
|
-
usedMemoryGb += w.memoryGb;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
heldForCapacity.push(id);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return { admit, heldForCapacity, neverFits };
|
|
135
|
+
return ready;
|
|
156
136
|
}
|
|
157
137
|
// ── Topological levels (UI layout) ───────────────────────────────────
|
|
158
138
|
/**
|
|
@@ -37,9 +37,6 @@
|
|
|
37
37
|
* part, and are never dispatched. In-flight siblings and independent
|
|
38
38
|
* ready steps continue; the loop drains and the run finalises (typically
|
|
39
39
|
* `partial`).
|
|
40
|
-
* - the halt cascade (cancel in-flight via
|
|
41
|
-
* `Promise.allSettled(handles.map(h => DBOS.cancelWorkflow(h.workflowID)))`,
|
|
42
|
-
* stop scheduling) survives only for the `neverFits` plan-validation guard.
|
|
43
40
|
* 4. Pause (work-pause spec)
|
|
44
41
|
* - a step pauses in its own workflow, and the parent waits for its result as
|
|
45
42
|
* for a step that runs. The other steps go on, and the run row stays
|
|
@@ -65,8 +62,9 @@
|
|
|
65
62
|
* - WRITES NOTHING to the conversation thread (results are pull-only
|
|
66
63
|
* via `inspectRun`)
|
|
67
64
|
*/
|
|
65
|
+
import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
|
|
66
|
+
import type { ScriptRuntime } from "../tools/script/run-script.js";
|
|
68
67
|
import type { Querier } from "../state/db.js";
|
|
69
|
-
import type { MachineBudget, ResourceSpec } from "../config/resource-limits.js";
|
|
70
68
|
import type { RunSession } from "../auth/types.js";
|
|
71
69
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
72
70
|
import type { Logger } from "../lib/logger.js";
|
|
@@ -113,24 +111,10 @@ export type ExecuteAnalysisInput = {
|
|
|
113
111
|
* plan row (a mid-run plan edit cannot change a running run's instructions).
|
|
114
112
|
*/
|
|
115
113
|
readonly planStepById: Readonly<Record<string, AnalysisStep>>;
|
|
114
|
+
/** The request that the plan answers, which the seed of each step renders. Absent when the plan stores none. */
|
|
115
|
+
readonly request?: string;
|
|
116
116
|
/** Per-step agent id (sandbox catalog entry). */
|
|
117
117
|
readonly agentByStepId: Readonly<Record<string, string>>;
|
|
118
|
-
/** Per-step planner-estimated sandbox resource request. */
|
|
119
|
-
readonly resourcesByStepId: Readonly<Record<string, ResourceSpec>>;
|
|
120
|
-
/**
|
|
121
|
-
* Per-step execution timeout in seconds, from the plan's `step.timeout`.
|
|
122
|
-
* Only present for steps that declared one; the child falls back to
|
|
123
|
-
* `DEFAULT_STEP_TIMEOUT_SECONDS` when a step is absent here.
|
|
124
|
-
*/
|
|
125
|
-
readonly timeoutByStepId?: Readonly<Record<string, number>>;
|
|
126
|
-
/**
|
|
127
|
-
* Machine resource budget snapshotted by `executePlan` at the async edge.
|
|
128
|
-
* The scheduler admits dependency-satisfied steps only while the declared
|
|
129
|
-
* resources of concurrently running steps fit within it. Absent (no policy
|
|
130
|
-
* configured, or a workflow persisted before this field existed), every
|
|
131
|
-
* dependency-satisfied step starts immediately — the legacy fan-out.
|
|
132
|
-
*/
|
|
133
|
-
readonly budget?: MachineBudget;
|
|
134
118
|
/**
|
|
135
119
|
* Replay-stable run-level synthesis decision. Absent preserves the legacy
|
|
136
120
|
* behavior for workflows persisted before this field existed.
|
|
@@ -159,12 +143,10 @@ export type ExecuteAnalysisResult = {
|
|
|
159
143
|
readonly canceledSteps: readonly string[];
|
|
160
144
|
};
|
|
161
145
|
/**
|
|
162
|
-
* A step's in-run display state. Wider than the ledger's `StepExecutionStatus`: `
|
|
163
|
-
*
|
|
164
|
-
* database row, and `skipped` is asserted mid-run for a doomed dependent whose ledger row stays
|
|
165
|
-
* `pending` until the terminal sweep.
|
|
146
|
+
* A step's in-run display state. Wider than the ledger's `StepExecutionStatus`: `skipped` is asserted
|
|
147
|
+
* mid-run for a doomed dependent whose ledger row stays `pending` until the terminal sweep.
|
|
166
148
|
*/
|
|
167
|
-
export type StepRuntimeStatus = "pending" | "
|
|
149
|
+
export type StepRuntimeStatus = "pending" | "running" | "completed" | "failed" | "skipped";
|
|
168
150
|
/**
|
|
169
151
|
* One step's state inside a {@link RunObservation}. `id` is the plan slug the ledger and the
|
|
170
152
|
* dependency edges use; `name` is the human phrase the plan author wrote, which is the only field
|
|
@@ -221,21 +203,21 @@ export type ExecuteAnalysisDeps = {
|
|
|
221
203
|
readonly sandboxStepCallable: SandboxStepCallable;
|
|
222
204
|
/** Workspace-root resolution seam — run dir creation + synthesis disk reads. */
|
|
223
205
|
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
206
|
+
/** The read path of the workspace. The synthesizer reads the files of the run through it. */
|
|
207
|
+
readonly workspaceFs: WorkspaceFilesystem;
|
|
224
208
|
/** Model id for the synthesizer agent loop. */
|
|
225
209
|
readonly synthesisModel: string;
|
|
226
|
-
/** The
|
|
227
|
-
readonly
|
|
210
|
+
/** The lookup tools of the synthesis (`createSynthesisLookupTools`). */
|
|
211
|
+
readonly synthesisLookups: readonly Tool[];
|
|
212
|
+
/** Where the VM of `run_script` comes from, for the lookups of the synthesizer. */
|
|
213
|
+
readonly scriptRuntime?: ScriptRuntime;
|
|
228
214
|
/** Run-level billing-bracket seam (managed: external bracket; OSS: no-op). */
|
|
229
215
|
readonly runCharge: RunCharge;
|
|
230
216
|
/** The delays of the timer of an outage. Absent: the default of the pause. */
|
|
231
217
|
readonly pauseRetryDelaysMs?: readonly number[];
|
|
232
218
|
/** Run-authorization seam — the terminal path revokes through `revoke`. */
|
|
233
219
|
readonly runAuthorizer: RunAuthorizer;
|
|
234
|
-
/**
|
|
235
|
-
* LLM usage-accounting seam for the in-body synthesizer loop (and the
|
|
236
|
-
* literature reviewer it delegates to); omitted falls back to the no-op
|
|
237
|
-
* recorder.
|
|
238
|
-
*/
|
|
220
|
+
/** LLM usage-accounting seam for the in-body synthesizer loop; omitted falls back to the no-op recorder. */
|
|
239
221
|
readonly usageRecorder?: UsageRecorder;
|
|
240
222
|
/** The store of the loops of the run synthesis and of their read tools. */
|
|
241
223
|
readonly toolOutputStore?: ToolOutputStore;
|
|
@@ -297,7 +279,7 @@ export type ExecuteAnalysisDeps = {
|
|
|
297
279
|
* level computation and the seed (`prompt`, composed by `composeStepSeed` at
|
|
298
280
|
* dispatch); the rest comes from `ExecuteAnalysisInput`. The body fills in
|
|
299
281
|
* `runSession` via `forStep` after this returns. Exported for the
|
|
300
|
-
* projection
|
|
282
|
+
* projection tests.
|
|
301
283
|
*/
|
|
302
284
|
export declare function buildChildInput(args: {
|
|
303
285
|
input: ExecuteAnalysisInput;
|
|
@@ -388,8 +370,8 @@ export declare function synthesisRowUpdate(outcome: {
|
|
|
388
370
|
* quick-display findings for the run-completed card; empty when synthesis is
|
|
389
371
|
* skipped or produced no synthesizable content. A genuine failure re-throws,
|
|
390
372
|
* failing the run (D10). It runs in the body of the run workflow: each read and
|
|
391
|
-
* write is a step, each model call is a step, and each
|
|
392
|
-
*
|
|
373
|
+
* write is a step, each model call is a step, and each lookup call is a step.
|
|
374
|
+
* Exported so integration tests can drive the wired synthesis
|
|
393
375
|
* without a full parent body.
|
|
394
376
|
*/
|
|
395
377
|
export declare function synthesizeFindings(args: {
|
|
@@ -37,9 +37,6 @@
|
|
|
37
37
|
* part, and are never dispatched. In-flight siblings and independent
|
|
38
38
|
* ready steps continue; the loop drains and the run finalises (typically
|
|
39
39
|
* `partial`).
|
|
40
|
-
* - the halt cascade (cancel in-flight via
|
|
41
|
-
* `Promise.allSettled(handles.map(h => DBOS.cancelWorkflow(h.workflowID)))`,
|
|
42
|
-
* stop scheduling) survives only for the `neverFits` plan-validation guard.
|
|
43
40
|
* 4. Pause (work-pause spec)
|
|
44
41
|
* - a step pauses in its own workflow, and the parent waits for its result as
|
|
45
42
|
* for a step that runs. The other steps go on, and the run row stays
|
|
@@ -81,7 +78,6 @@ import { recordRunCompleted, recordStepCompleted, stepOutcomeOf } from "../lib/m
|
|
|
81
78
|
import { describeDbError, withTransaction } from "../lib/db-result.js";
|
|
82
79
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
83
80
|
import { countArtifactsByRuns, insertStepExecution, loadDataProfileStatus, queryActiveRun, queryStepArtifactPaths, queryStepsByRun, seedStepExecutions, setRunSynthesisOutcome, sweepPendingStepExecutions, updateRunStatus, updateStepExecution, } from "../state/index.js";
|
|
84
|
-
import { clearPausesOfWork } from "../state/paused-work.js";
|
|
85
81
|
import { addChatUsage, hasReportedUsage } from "../loop/metrics.js";
|
|
86
82
|
import { flattenDataPart } from "../contracts/chat-frame.js";
|
|
87
83
|
import { SYNTHESIS_AGENT_ID, loadStepSummariesFromDisk } from "../execution/run-synthesis.js";
|
|
@@ -90,7 +86,6 @@ import { SYNTHESIS_STEP_ID, runDir, runStepDir, stepSubdir, stepWritePrefix, toS
|
|
|
90
86
|
import { isChatDataPart, synthesisEmission } from "../sandbox/sandbox-step-translate.js";
|
|
91
87
|
import { synthesizeRun } from "../app/synthesize-run.js";
|
|
92
88
|
import { computeTopologicalLevels, scheduleReady, validatePlanDag } from "./execute-analysis-scheduler.js";
|
|
93
|
-
import { recordCancelledChild } from "./metrics.js";
|
|
94
89
|
import { pauseAtGate, workPause } from "./pause.js";
|
|
95
90
|
import { presentGet } from "../lib/present.js";
|
|
96
91
|
// ── In-body orchestration helpers ────────────────────────────────────
|
|
@@ -183,14 +178,10 @@ function observeRunGuarded(deps, observation) {
|
|
|
183
178
|
* level computation and the seed (`prompt`, composed by `composeStepSeed` at
|
|
184
179
|
* dispatch); the rest comes from `ExecuteAnalysisInput`. The body fills in
|
|
185
180
|
* `runSession` via `forStep` after this returns. Exported for the
|
|
186
|
-
* projection
|
|
181
|
+
* projection tests.
|
|
187
182
|
*/
|
|
188
183
|
export function buildChildInput(args) {
|
|
189
184
|
const { input, stepId, level, runId, prompt, templateBinding } = args;
|
|
190
|
-
const resources = input.resourcesByStepId[stepId];
|
|
191
|
-
if (!resources) {
|
|
192
|
-
throw new Error(`executeAnalysis: step "${stepId}" missing from resourcesByStepId — every step must declare resources`);
|
|
193
|
-
}
|
|
194
185
|
const planStep = input.planStepById[stepId];
|
|
195
186
|
if (!planStep) {
|
|
196
187
|
throw new Error(`executeAnalysis: step "${stepId}" missing from planStepById — every step must carry its plan data`);
|
|
@@ -213,8 +204,6 @@ export function buildChildInput(args) {
|
|
|
213
204
|
templateBinding,
|
|
214
205
|
level,
|
|
215
206
|
prompt,
|
|
216
|
-
resources,
|
|
217
|
-
timeoutSeconds: input.timeoutByStepId?.[stepId],
|
|
218
207
|
};
|
|
219
208
|
}
|
|
220
209
|
// ── Seed composition (at dispatch, not at plan submission) ────────────
|
|
@@ -303,6 +292,7 @@ export async function gatherStepPackage(args) {
|
|
|
303
292
|
profile: profile?.result ?? null,
|
|
304
293
|
upstream,
|
|
305
294
|
memory: { goal: memory.goal, constraints: memory.constraints },
|
|
295
|
+
request: input.request,
|
|
306
296
|
...contract.briefing,
|
|
307
297
|
},
|
|
308
298
|
templateBinding: contract.binding,
|
|
@@ -504,8 +494,8 @@ export function synthesisRowUpdate(outcome, durationMs) {
|
|
|
504
494
|
* quick-display findings for the run-completed card; empty when synthesis is
|
|
505
495
|
* skipped or produced no synthesizable content. A genuine failure re-throws,
|
|
506
496
|
* failing the run (D10). It runs in the body of the run workflow: each read and
|
|
507
|
-
* write is a step, each model call is a step, and each
|
|
508
|
-
*
|
|
497
|
+
* write is a step, each model call is a step, and each lookup call is a step.
|
|
498
|
+
* Exported so integration tests can drive the wired synthesis
|
|
509
499
|
* without a full parent body.
|
|
510
500
|
*/
|
|
511
501
|
export function synthesizeFindings(args) {
|
|
@@ -536,8 +526,10 @@ export function synthesizeFindings(args) {
|
|
|
536
526
|
provider: deps.provider,
|
|
537
527
|
embedding: deps.embedding,
|
|
538
528
|
resolveWorkspaceRoot: deps.resolveWorkspaceRoot,
|
|
529
|
+
workspaceFs: deps.workspaceFs,
|
|
539
530
|
synthesisModel: deps.synthesisModel,
|
|
540
|
-
|
|
531
|
+
synthesisLookups: deps.synthesisLookups,
|
|
532
|
+
scriptRuntime: deps.scriptRuntime,
|
|
541
533
|
usageRecorder: deps.usageRecorder,
|
|
542
534
|
toolOutputStore: deps.toolOutputStore,
|
|
543
535
|
}, {
|
|
@@ -845,12 +837,6 @@ async function runSchedulerLoop(args) {
|
|
|
845
837
|
// exactly without a second channel. A child that settled by throwing carries
|
|
846
838
|
// no result and therefore contributes nothing — same rule as its duration.
|
|
847
839
|
const runUsage = {};
|
|
848
|
-
// Child workflow ids the parent itself cancelled (the neverFits halt cascade). Their `getResult`
|
|
849
|
-
// rejects with a DBOS cancellation error and lands in the settlement error branch — but the step was
|
|
850
|
-
// CANCELED by us, not failed on its own merits. Keyed here (deterministic: `cancelInFlight` runs the
|
|
851
|
-
// same way on a replay) so that branch records it as canceled everywhere, matching how a child that
|
|
852
|
-
// returns a graceful `{status:"canceled"}` is already handled.
|
|
853
|
-
const canceledByParent = new Set();
|
|
854
840
|
const stepRuntime = new Map();
|
|
855
841
|
for (const step of input.steps) {
|
|
856
842
|
stepRuntime.set(step.id, { status: "pending" });
|
|
@@ -872,7 +858,7 @@ async function runSchedulerLoop(args) {
|
|
|
872
858
|
* can never become dependency-satisfied and will never be dispatched.
|
|
873
859
|
* Stream-only — ledger rows stay `pending` until the terminal sweep flips
|
|
874
860
|
* them to `skipped`. Dependents of an unfinished step are necessarily
|
|
875
|
-
* `pending` (never
|
|
861
|
+
* `pending` (never running); an already-`skipped` one is not
|
|
876
862
|
* re-walked.
|
|
877
863
|
*/
|
|
878
864
|
const markDependentsSkipped = (failedStepId) => {
|
|
@@ -887,9 +873,6 @@ async function runSchedulerLoop(args) {
|
|
|
887
873
|
}
|
|
888
874
|
}
|
|
889
875
|
};
|
|
890
|
-
// Halts scheduling entirely — reserved for the `neverFits` guard. An ordinary
|
|
891
|
-
// step failure does NOT set it: it dooms only that step's transitive dependents.
|
|
892
|
-
let halted = false;
|
|
893
876
|
let failureReason = null;
|
|
894
877
|
const stepIndexById = new Map(input.steps.map((s, i) => [s.id, i]));
|
|
895
878
|
const emitDagSnapshot = async () => {
|
|
@@ -932,59 +915,14 @@ async function runSchedulerLoop(args) {
|
|
|
932
915
|
observeRunGuarded(deps, buildRunObservation({ input, runId, status: "running", stepStates: stepRuntime }));
|
|
933
916
|
};
|
|
934
917
|
const dispatchReady = async () => {
|
|
935
|
-
if (halted)
|
|
936
|
-
return;
|
|
937
918
|
const inFlightStepIds = new Set([...inFlight.values()].map((h) => h.stepId));
|
|
938
919
|
// A settled-but-not-completed step (failed / blocked / canceled) is not
|
|
939
920
|
// in `completed` and not in flight, so `scheduleReady` would offer it
|
|
940
|
-
// again — filter it out of the candidate list.
|
|
941
|
-
// declared resources from weighing against the budget: settled steps
|
|
942
|
-
// hold no capacity.
|
|
921
|
+
// again — filter it out of the candidate list.
|
|
943
922
|
const candidates = input.steps.filter((s) => !failed.has(s.id) && !canceled.has(s.id));
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
if (input.budget) {
|
|
947
|
-
const scheduled = scheduleReady(candidates, completed, inFlightStepIds, {
|
|
948
|
-
budget: input.budget,
|
|
949
|
-
resourcesByStepId: input.resourcesByStepId,
|
|
950
|
-
});
|
|
951
|
-
if (scheduled.neverFits.length > 0) {
|
|
952
|
-
// An over-budget step can never be admitted — plan-time validation
|
|
953
|
-
// makes this unreachable for new plans; stored plans fail loudly
|
|
954
|
-
// instead of waiting forever. The stored plan is invalid against
|
|
955
|
-
// this machine, so the run halts outright — unlike an ordinary
|
|
956
|
-
// step failure, which dooms only its dependents.
|
|
957
|
-
for (const stepId of scheduled.neverFits) {
|
|
958
|
-
const r = input.resourcesByStepId[stepId];
|
|
959
|
-
const declared = r ? `${r.cpu} CPU / ${r.memoryGb} GB` : "undeclared";
|
|
960
|
-
failed.add(stepId);
|
|
961
|
-
stepRuntime.set(stepId, {
|
|
962
|
-
status: "failed",
|
|
963
|
-
error: `step resources (${declared}) exceed the machine budget (${input.budget.cpu} CPU / ${input.budget.memoryGb} GB)`,
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
halted = true;
|
|
967
|
-
failureReason = `step "${String(scheduled.neverFits[0])}" exceeds the machine resource budget`;
|
|
968
|
-
await cancelInFlight(inFlight, "fail_fast", canceledByParent);
|
|
969
|
-
await emitDagSnapshot();
|
|
970
|
-
return;
|
|
971
|
-
}
|
|
972
|
-
for (const stepId of scheduled.heldForCapacity) {
|
|
973
|
-
if (presentGet(stepRuntime, stepId, "the runtime of the step").status !== "queued") {
|
|
974
|
-
stepRuntime.set(stepId, { status: "queued" });
|
|
975
|
-
queuedChanged = true;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
admit = scheduled.admit;
|
|
979
|
-
}
|
|
980
|
-
else {
|
|
981
|
-
admit = scheduleReady(candidates, completed, inFlightStepIds);
|
|
982
|
-
}
|
|
983
|
-
if (admit.length === 0) {
|
|
984
|
-
if (queuedChanged)
|
|
985
|
-
await emitDagSnapshot();
|
|
923
|
+
const admit = scheduleReady(candidates, completed, inFlightStepIds);
|
|
924
|
+
if (admit.length === 0)
|
|
986
925
|
return;
|
|
987
|
-
}
|
|
988
926
|
for (const stepId of admit) {
|
|
989
927
|
const idx = presentGet(stepIndexById, stepId, "the index of the step");
|
|
990
928
|
const childWorkflowId = `${workflowId}-${idx}`;
|
|
@@ -1072,8 +1010,7 @@ async function runSchedulerLoop(args) {
|
|
|
1072
1010
|
// failed or blocked — a blocker (see the harness-sandbox-agents spec)
|
|
1073
1011
|
// is treated exactly like a failure: the step declared it cannot
|
|
1074
1012
|
// deliver, so only its transitive dependents can never run. In-flight
|
|
1075
|
-
// siblings and independent ready steps continue
|
|
1076
|
-
// below also lets a budget-held step claim the freed capacity.
|
|
1013
|
+
// siblings and independent ready steps continue.
|
|
1077
1014
|
failed.add(settled.stepId);
|
|
1078
1015
|
failureReason ??= r.error ?? (r.status === "blocked" ? "step_blocked" : "step_failed");
|
|
1079
1016
|
stepRuntime.set(settled.stepId, {
|
|
@@ -1088,26 +1025,8 @@ async function runSchedulerLoop(args) {
|
|
|
1088
1025
|
await dispatchReady();
|
|
1089
1026
|
}
|
|
1090
1027
|
}
|
|
1091
|
-
else if (canceledByParent.has(settled.childId)) {
|
|
1092
|
-
// The child threw because the PARENT cancelled it (the neverFits halt cascade), not
|
|
1093
|
-
// because it failed on its own — its `getResult` rejects with a DBOS cancellation error.
|
|
1094
|
-
// Record it as canceled, mirroring the graceful `{status:"canceled"}` branch above so the
|
|
1095
|
-
// `canceled` set, the terminal result, and the `step_completed` provenance all agree. No
|
|
1096
|
-
// halt / cancel cascade here: the run is already halted (that is why this child was cancelled).
|
|
1097
|
-
canceled.add(settled.stepId);
|
|
1098
|
-
stepRuntime.set(settled.stepId, {
|
|
1099
|
-
// `stepRuntime` (the DAG snapshot) has no canceled tier; the graceful-cancel branch above
|
|
1100
|
-
// likewise renders canceled steps as "failed", so this stays consistent with it.
|
|
1101
|
-
status: "failed",
|
|
1102
|
-
error: errorMessageOf(settled.err) ?? textOf(settled.err),
|
|
1103
|
-
});
|
|
1104
|
-
stepStatus = "canceled";
|
|
1105
|
-
stepDurationMs = undefined;
|
|
1106
|
-
await emitDagSnapshot();
|
|
1107
|
-
}
|
|
1108
1028
|
else {
|
|
1109
|
-
// The child threw
|
|
1110
|
-
// treated exactly like a step failure:
|
|
1029
|
+
// The child threw. A throw is treated exactly like a step failure:
|
|
1111
1030
|
// dependents doomed, siblings continue.
|
|
1112
1031
|
failed.add(settled.stepId);
|
|
1113
1032
|
failureReason ??= errorMessageOf(settled.err) ?? textOf(settled.err);
|
|
@@ -1148,17 +1067,6 @@ async function runSchedulerLoop(args) {
|
|
|
1148
1067
|
usage: hasReportedUsage(runUsage) ? { ...runUsage } : undefined,
|
|
1149
1068
|
};
|
|
1150
1069
|
}
|
|
1151
|
-
async function cancelInFlight(inFlight, cause, canceledByParent) {
|
|
1152
|
-
const ids = [...inFlight.keys()];
|
|
1153
|
-
// Record every id BEFORE the cancel so the settlement loop classifies the resulting
|
|
1154
|
-
// DBOS-cancellation rejection as canceled rather than failed (see `canceledByParent`).
|
|
1155
|
-
for (const childId of ids)
|
|
1156
|
-
canceledByParent.add(childId);
|
|
1157
|
-
await Promise.allSettled(ids.map((childId) => DBOS.cancelWorkflow(childId)));
|
|
1158
|
-
for (let i = 0; i < ids.length; i++) {
|
|
1159
|
-
recordCancelledChild({ cause });
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
1070
|
// ── (6) collectAndComplete ────────────────────────────────────────────
|
|
1163
1071
|
/** The waits between the attempts are 1, 2, 4, and 8 seconds. */
|
|
1164
1072
|
const FINAL_STATUS_WRITE_RETRY = { retriesAllowed: true, maxAttempts: 5, intervalSeconds: 1, backoffRate: 2 };
|
|
@@ -1294,8 +1202,6 @@ async function collectAndComplete(args) {
|
|
|
1294
1202
|
}
|
|
1295
1203
|
// Sweep never-started steps to `skipped`. Log-don't-rollback, like every other finalisation step here.
|
|
1296
1204
|
await ledgerStep(logger, "sweep-pending-steps", () => sweepPendingStepExecutions(deps.pool, runId));
|
|
1297
|
-
// A step that the neverFits halt canceled in its pause leaves its row, thus the end of the run clears each row of the run.
|
|
1298
|
-
await ledgerStep(logger, "clear-run-pauses", () => clearPausesOfWork(deps.pool, runId));
|
|
1299
1205
|
// The run end was emitted above. These branches only fan out the UI stream part, whose
|
|
1300
1206
|
// completed/failed shapes genuinely differ — the provenance record does not.
|
|
1301
1207
|
if (status === "completed" || status === "partial") {
|