@inflexa-ai/harness 0.46.0 → 0.47.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 +1 -1
- package/dist/agents/conversation-agent.d.ts +3 -8
- package/dist/agents/conversation-agent.js +24 -4
- package/dist/agents/report-session-agent.d.ts +11 -14
- package/dist/agents/report-session-agent.js +30 -17
- 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/chat-turn-workflow.js +5 -6
- 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 +2 -5
- package/dist/index.js +3 -6
- package/dist/input-scan/enrich.d.ts +5 -10
- package/dist/input-scan/enrich.js +12 -8
- package/dist/lib/hooks.d.ts +9 -0
- package/dist/lib/hooks.js +6 -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/run-loop.d.ts +5 -0
- package/dist/loop/run-loop.js +30 -2
- package/dist/loop/sub-agent.js +5 -6
- package/dist/loop/tool-name-repair.d.ts +11 -0
- package/dist/loop/tool-name-repair.js +23 -0
- package/dist/loop/turn-credentials.d.ts +5 -0
- package/dist/loop/turn-credentials.js +17 -0
- package/dist/loop/types.d.ts +14 -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 +26 -57
- 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/providers/request-headers.js +12 -3
- 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 +20 -9
- package/dist/runtime/assemble.js +47 -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 +0 -1
- package/dist/state/index.js +0 -1
- 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 +0 -2
- package/dist/state/step-executions.js +1 -4
- package/dist/tasks/data-profile.d.ts +40 -2
- package/dist/tasks/data-profile.js +89 -87
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +6 -1
- 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-session/index.d.ts +0 -1
- package/dist/tools/report-session/index.js +0 -1
- package/dist/tools/report-session/script-tools.d.ts +30 -0
- package/dist/tools/report-session/script-tools.js +177 -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/codemode-worker.d.ts +5 -0
- package/dist/tools/script/codemode-worker.js +5 -0
- package/dist/tools/script/file-tools.d.ts +100 -0
- package/dist/tools/script/file-tools.js +377 -0
- package/dist/tools/script/metrics.d.ts +21 -0
- package/dist/tools/script/metrics.js +38 -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 +326 -0
- package/dist/tools/script/scratch.d.ts +81 -0
- package/dist/tools/script/scratch.js +142 -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 +48 -4
- package/dist/tools/workspace/mutator.js +43 -12
- 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/package.json +6 -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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sandbox reconciler (sandbox-leases spec): the backstop of the lease workflows.
|
|
3
|
+
*
|
|
4
|
+
* A lease workflow removes its own machine at each normal end. The reconciler covers the ends that a lease workflow
|
|
5
|
+
* cannot see: an operator cancel of the lease workflow, a purge, and a consumer that ends while its lease workflow
|
|
6
|
+
* waits in a pause. Each 5 minutes it does three passes in one DBOS step:
|
|
7
|
+
*
|
|
8
|
+
* 1. Each managed machine whose lease workflow is terminal goes away. A machine whose owner does not resolve goes
|
|
9
|
+
* away only when a liveness probe finds it dead.
|
|
10
|
+
* 2. Each lease row whose lease workflow is terminal or absent becomes `released`, thus its capacity frees.
|
|
11
|
+
* 3. Each lease workflow whose owner is terminal gets a cancel, and the next sweep removes its machine.
|
|
12
|
+
*
|
|
13
|
+
* The pure decision (`classifyManaged`) and the sweep (`reconcileOnce`) take their effects as parameters, thus a
|
|
14
|
+
* test drives them without a DBOS runtime.
|
|
15
|
+
*/
|
|
16
|
+
import type { Logger } from "../lib/logger.js";
|
|
17
|
+
import type { Querier } from "../state/db.js";
|
|
18
|
+
import { type ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
19
|
+
import type { SandboxClient } from "./client.js";
|
|
20
|
+
import type { ManagedSandbox } from "./types.js";
|
|
21
|
+
export type ReconcileDecision = "leave" | "remove" | "remove-if-dead";
|
|
22
|
+
/**
|
|
23
|
+
* Decide the fate of a managed machine. A resolved owner status is authoritative in both directions: DBOS records
|
|
24
|
+
* the status of a lease workflow at its enqueue, before its machine exists. A missing status is authoritative for
|
|
25
|
+
* neither, thus past the grace the decision defers to a liveness probe.
|
|
26
|
+
*/
|
|
27
|
+
export declare function classifyManaged(sb: ManagedSandbox, status: {
|
|
28
|
+
status: string;
|
|
29
|
+
} | null, nowMs: number, graceMs: number): ReconcileDecision;
|
|
30
|
+
export type ReconcilerDeps = {
|
|
31
|
+
readonly pool: Querier;
|
|
32
|
+
readonly sandboxClient: Pick<SandboxClient, "listManagedSandboxes" | "teardownById" | "isAliveById" | "teardown">;
|
|
33
|
+
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
34
|
+
readonly getStatus: (workflowId: string) => Promise<{
|
|
35
|
+
status: string;
|
|
36
|
+
} | null>;
|
|
37
|
+
/** Cancel a lease workflow whose owner ended. */
|
|
38
|
+
readonly cancel: (workflowId: string) => Promise<void>;
|
|
39
|
+
/** Creation-time grace before an unattributable machine is even probed. */
|
|
40
|
+
readonly graceMs?: number;
|
|
41
|
+
/** Wall-clock ms; injected for tests. */
|
|
42
|
+
readonly nowMs?: () => number;
|
|
43
|
+
readonly logger?: Logger;
|
|
44
|
+
};
|
|
45
|
+
export type ReconcileSummary = {
|
|
46
|
+
readonly managedCount: number;
|
|
47
|
+
readonly removedCount: number;
|
|
48
|
+
readonly leftCount: number;
|
|
49
|
+
/** Machines past the grace whose owner did not resolve, but which are alive. A non-zero value is a reported leak. */
|
|
50
|
+
readonly liveUnattributed: number;
|
|
51
|
+
readonly leasesReleased: number;
|
|
52
|
+
readonly leasesCanceled: number;
|
|
53
|
+
};
|
|
54
|
+
/** One sweep. A failure on one machine or one lease is logged and skipped, and the next sweep tries again. */
|
|
55
|
+
export declare function reconcileOnce(deps: ReconcilerDeps): Promise<ReconcileSummary>;
|
|
56
|
+
export type RegisterReconcilerDeps = Omit<ReconcilerDeps, "getStatus" | "nowMs">;
|
|
57
|
+
/** Register the scheduled reconciler: one unsharded workflow, whose whole sweep is one step. */
|
|
58
|
+
export declare function registerSandboxReconciler(deps: RegisterReconcilerDeps): void;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sandbox reconciler (sandbox-leases spec): the backstop of the lease workflows.
|
|
3
|
+
*
|
|
4
|
+
* A lease workflow removes its own machine at each normal end. The reconciler covers the ends that a lease workflow
|
|
5
|
+
* cannot see: an operator cancel of the lease workflow, a purge, and a consumer that ends while its lease workflow
|
|
6
|
+
* waits in a pause. Each 5 minutes it does three passes in one DBOS step:
|
|
7
|
+
*
|
|
8
|
+
* 1. Each managed machine whose lease workflow is terminal goes away. A machine whose owner does not resolve goes
|
|
9
|
+
* away only when a liveness probe finds it dead.
|
|
10
|
+
* 2. Each lease row whose lease workflow is terminal or absent becomes `released`, thus its capacity frees.
|
|
11
|
+
* 3. Each lease workflow whose owner is terminal gets a cancel, and the next sweep removes its machine.
|
|
12
|
+
*
|
|
13
|
+
* The pure decision (`classifyManaged`) and the sweep (`reconcileOnce`) take their effects as parameters, thus a
|
|
14
|
+
* test drives them without a DBOS runtime.
|
|
15
|
+
*/
|
|
16
|
+
import { rm } from "node:fs/promises";
|
|
17
|
+
import { DBOS } from "@dbos-inc/dbos-sdk";
|
|
18
|
+
import { createNoopLogger } from "../lib/console-logger.js";
|
|
19
|
+
import { recordMachineReplaced } from "../lib/metrics.js";
|
|
20
|
+
import { untracedWorkflow } from "../lib/otel-spans.js";
|
|
21
|
+
import { unwrapOrThrow } from "../lib/result.js";
|
|
22
|
+
import { scheduleWorkflow } from "../runtime/dbos.js";
|
|
23
|
+
import { openLeases, releaseLease } from "../state/sandbox-leases.js";
|
|
24
|
+
import { tailWritePrefix } from "../workspace/paths.js";
|
|
25
|
+
import { leaseRecordTail } from "./lease-names.js";
|
|
26
|
+
const RECONCILER_CRON = "0 */5 * * * *";
|
|
27
|
+
const RECONCILER_WORKFLOW = "sandbox-reconciler";
|
|
28
|
+
const DEFAULT_GRACE_MS = 10 * 60_000;
|
|
29
|
+
/** The statuses of a workflow that still runs. A machine or a lease of such a workflow stays. */
|
|
30
|
+
const ACTIVE_WORKFLOW_STATUSES = new Set(["PENDING", "ENQUEUED", "RUNNING"]);
|
|
31
|
+
/**
|
|
32
|
+
* Decide the fate of a managed machine. A resolved owner status is authoritative in both directions: DBOS records
|
|
33
|
+
* the status of a lease workflow at its enqueue, before its machine exists. A missing status is authoritative for
|
|
34
|
+
* neither, thus past the grace the decision defers to a liveness probe.
|
|
35
|
+
*/
|
|
36
|
+
export function classifyManaged(sb, status, nowMs, graceMs) {
|
|
37
|
+
if (status)
|
|
38
|
+
return ACTIVE_WORKFLOW_STATUSES.has(status.status) ? "leave" : "remove";
|
|
39
|
+
const ageMs = sb.createdAtMs == null ? Number.POSITIVE_INFINITY : nowMs - sb.createdAtMs;
|
|
40
|
+
return ageMs >= graceMs ? "remove-if-dead" : "leave";
|
|
41
|
+
}
|
|
42
|
+
async function sweepMachines(deps, logger, nowMs, graceMs) {
|
|
43
|
+
const managed = await deps.sandboxClient.listManagedSandboxes();
|
|
44
|
+
let removedCount = 0;
|
|
45
|
+
let leftCount = 0;
|
|
46
|
+
let liveUnattributed = 0;
|
|
47
|
+
for (const sb of managed) {
|
|
48
|
+
const status = sb.ownerWorkflowId ? await deps.getStatus(sb.ownerWorkflowId) : null;
|
|
49
|
+
const decision = classifyManaged(sb, status, nowMs, graceMs);
|
|
50
|
+
if (decision === "leave") {
|
|
51
|
+
leftCount++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (decision === "remove-if-dead") {
|
|
55
|
+
if (sb.ownerWorkflowId)
|
|
56
|
+
logger.warn("owner workflow id resolved to no workflow", { sandboxId: sb.sandboxId, ownerWorkflowId: sb.ownerWorkflowId });
|
|
57
|
+
let liveness;
|
|
58
|
+
try {
|
|
59
|
+
liveness = await deps.sandboxClient.isAliveById(sb.sandboxId);
|
|
60
|
+
}
|
|
61
|
+
catch (failure) {
|
|
62
|
+
// A probe that cannot answer is not a dead machine. The next sweep probes again.
|
|
63
|
+
logger.warn("liveness probe threw — leaving this machine", { sandboxId: sb.sandboxId, ...logger.errorFields(failure) });
|
|
64
|
+
leftCount++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (liveness.alive) {
|
|
68
|
+
logger.warn("machine has no resolvable owner but is still alive — left standing", {
|
|
69
|
+
sandboxId: sb.sandboxId,
|
|
70
|
+
ownerWorkflowId: sb.ownerWorkflowId,
|
|
71
|
+
});
|
|
72
|
+
leftCount++;
|
|
73
|
+
liveUnattributed++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await deps.sandboxClient.teardownById(sb.sandboxId);
|
|
79
|
+
recordMachineReplaced({ cause: "release" });
|
|
80
|
+
removedCount++;
|
|
81
|
+
}
|
|
82
|
+
catch (failure) {
|
|
83
|
+
logger.warn("removal failed — skipping this round", { sandboxId: sb.sandboxId, ...logger.errorFields(failure) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { managedCount: managed.length, removedCount, leftCount, liveUnattributed };
|
|
87
|
+
}
|
|
88
|
+
async function sweepLeases(deps, logger) {
|
|
89
|
+
let leasesReleased = 0;
|
|
90
|
+
let leasesCanceled = 0;
|
|
91
|
+
for (const lease of unwrapOrThrow(await openLeases(deps.pool))) {
|
|
92
|
+
const status = await deps.getStatus(lease.leaseWorkflowId);
|
|
93
|
+
const leaseActive = status !== null && ACTIVE_WORKFLOW_STATUSES.has(status.status);
|
|
94
|
+
if (!leaseActive) {
|
|
95
|
+
// A lease row with no workflow: its acquire stopped before the enqueue. The owner reruns its acquire when it
|
|
96
|
+
// still runs, thus the row stays until the owner ends.
|
|
97
|
+
if (status === null) {
|
|
98
|
+
const owner = await deps.getStatus(lease.ownerWorkflowId);
|
|
99
|
+
if (owner !== null && ACTIVE_WORKFLOW_STATUSES.has(owner.status))
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const released = unwrapOrThrow(await releaseLease(deps.pool, lease.leaseWorkflowId, "lease-workflow-ended"));
|
|
104
|
+
if (released?.machine !== undefined)
|
|
105
|
+
await deps.sandboxClient.teardown(released.machine);
|
|
106
|
+
const dir = tailWritePrefix({ workspaceRoot: await deps.resolveWorkspaceRoot(lease.analysisId), tail: leaseRecordTail(lease.leaseKey) });
|
|
107
|
+
await rm(dir, { recursive: true, force: true });
|
|
108
|
+
leasesReleased++;
|
|
109
|
+
}
|
|
110
|
+
catch (failure) {
|
|
111
|
+
logger.warn("the release of an ended lease failed — skipping this round", {
|
|
112
|
+
leaseWorkflowId: lease.leaseWorkflowId,
|
|
113
|
+
...logger.errorFields(failure),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const owner = await deps.getStatus(lease.ownerWorkflowId);
|
|
119
|
+
if (owner !== null && ACTIVE_WORKFLOW_STATUSES.has(owner.status))
|
|
120
|
+
continue;
|
|
121
|
+
try {
|
|
122
|
+
await deps.cancel(lease.leaseWorkflowId);
|
|
123
|
+
leasesCanceled++;
|
|
124
|
+
}
|
|
125
|
+
catch (failure) {
|
|
126
|
+
logger.warn("the cancel of a lease whose owner ended failed", { leaseWorkflowId: lease.leaseWorkflowId, ...logger.errorFields(failure) });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { leasesReleased, leasesCanceled };
|
|
130
|
+
}
|
|
131
|
+
/** One sweep. A failure on one machine or one lease is logged and skipped, and the next sweep tries again. */
|
|
132
|
+
export async function reconcileOnce(deps) {
|
|
133
|
+
const logger = (deps.logger ?? createNoopLogger()).named("sandbox-reconciler");
|
|
134
|
+
const nowMs = (deps.nowMs ?? (() => Date.now()))();
|
|
135
|
+
const graceMs = deps.graceMs ?? DEFAULT_GRACE_MS;
|
|
136
|
+
const machines = await sweepMachines(deps, logger, nowMs, graceMs);
|
|
137
|
+
const leases = await sweepLeases(deps, logger);
|
|
138
|
+
const summary = { ...machines, ...leases };
|
|
139
|
+
logger.info("sweep completed", { ...summary });
|
|
140
|
+
return summary;
|
|
141
|
+
}
|
|
142
|
+
/** Register the scheduled reconciler: one unsharded workflow, whose whole sweep is one step. */
|
|
143
|
+
export function registerSandboxReconciler(deps) {
|
|
144
|
+
const reconciler = DBOS.registerWorkflow(async () => {
|
|
145
|
+
// One step for the whole sweep: the machine list is live cluster state, and it sets how many status reads
|
|
146
|
+
// run. In the body each status read takes a function id, thus a different list on a replay would diverge.
|
|
147
|
+
await DBOS.runStep(() => reconcileOnce({ ...deps, getStatus: async (workflowId) => await DBOS.getWorkflowStatus(workflowId) }), {
|
|
148
|
+
name: "sandbox-reconciler-sweep",
|
|
149
|
+
});
|
|
150
|
+
}, { name: RECONCILER_WORKFLOW });
|
|
151
|
+
untracedWorkflow(RECONCILER_WORKFLOW);
|
|
152
|
+
scheduleWorkflow({ scheduleName: RECONCILER_WORKFLOW, workflowFn: reconciler, schedule: RECONCILER_CRON });
|
|
153
|
+
}
|
|
@@ -41,7 +41,7 @@ export declare function toolActivityKey(toolUseId: string): string;
|
|
|
41
41
|
* phase, and the `data-activity-entry` that keeps the report in the run stream under the synthesis step. `seq` is the
|
|
42
42
|
* position of the report in the synthesis, so each entry has its own id. The `extra` fields stay on the progress part.
|
|
43
43
|
*/
|
|
44
|
-
export declare function synthesisEmission(runId: string, stepId: string, seq: number, phase: SynthesisPhase, activity: string, at: string, extra: Pick<SynthesisProgressPart, "
|
|
44
|
+
export declare function synthesisEmission(runId: string, stepId: string, seq: number, phase: SynthesisPhase, activity: string, at: string, extra: Pick<SynthesisProgressPart, "reason" | "error">): {
|
|
45
45
|
readonly progress: SynthesisProgressPart;
|
|
46
46
|
readonly entry: ActivityEntryPart;
|
|
47
47
|
};
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sandbox-client types — the wire/persistence shapes that cross the
|
|
3
|
-
* submit/poll protocol (see the harness-sandbox-exec spec) and the
|
|
3
|
+
* submit/poll protocol (see the harness-sandbox-exec spec) and the lease table.
|
|
4
4
|
*
|
|
5
|
-
* `SandboxRef` is the handle the harness uses to talk to a live sandbox.
|
|
6
|
-
*
|
|
5
|
+
* `SandboxRef` is the handle the harness uses to talk to a live sandbox. A lease
|
|
6
|
+
* row persists the same shape (`PersistedSandboxRef`, from
|
|
7
7
|
* `harness/state/schema.ts`).
|
|
8
8
|
*/
|
|
9
9
|
import type { ResultAsync } from "neverthrow";
|
|
@@ -212,6 +212,10 @@ export declare const ExecResultSchema: z.ZodObject<{
|
|
|
212
212
|
stderrTotalBytes: z.ZodOptional<z.ZodNumber>;
|
|
213
213
|
syntheticFailure: z.ZodOptional<z.ZodObject<{
|
|
214
214
|
reason: z.ZodString;
|
|
215
|
+
memoryLimitGb: z.ZodOptional<z.ZodNumber>;
|
|
216
|
+
}, z.core.$strip>>;
|
|
217
|
+
lost: z.ZodOptional<z.ZodObject<{
|
|
218
|
+
reason: z.ZodString;
|
|
215
219
|
}, z.core.$strip>>;
|
|
216
220
|
provenance: z.ZodOptional<z.ZodObject<{
|
|
217
221
|
disabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -276,6 +280,10 @@ export declare const PollResponseSchema: z.ZodObject<{
|
|
|
276
280
|
stderrTotalBytes: z.ZodOptional<z.ZodNumber>;
|
|
277
281
|
syntheticFailure: z.ZodOptional<z.ZodObject<{
|
|
278
282
|
reason: z.ZodString;
|
|
283
|
+
memoryLimitGb: z.ZodOptional<z.ZodNumber>;
|
|
284
|
+
}, z.core.$strip>>;
|
|
285
|
+
lost: z.ZodOptional<z.ZodObject<{
|
|
286
|
+
reason: z.ZodString;
|
|
279
287
|
}, z.core.$strip>>;
|
|
280
288
|
provenance: z.ZodOptional<z.ZodObject<{
|
|
281
289
|
disabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -327,10 +335,12 @@ export type SubmitExecBody = {
|
|
|
327
335
|
};
|
|
328
336
|
/** What a spawn needs beside its session. */
|
|
329
337
|
export type SandboxSpec = {
|
|
330
|
-
/**
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
|
|
338
|
+
/** The id of the lease workflow that owns the machine. Recorded verbatim on the
|
|
339
|
+
* machine under `cortex/owner-workflow-id`, thus the reconciler maps a machine
|
|
340
|
+
* back to its lease workflow. */
|
|
341
|
+
ownerWorkflowId: string;
|
|
342
|
+
/** The workspace-relative directory of the exec records of the lease. */
|
|
343
|
+
recordTail: string;
|
|
334
344
|
/** Backend-specific extras carried through to the per-backend impl. */
|
|
335
345
|
image?: string;
|
|
336
346
|
extraEnv?: Record<string, string>;
|
|
@@ -356,23 +366,20 @@ export type SandboxLabels = Readonly<Record<string, string>>;
|
|
|
356
366
|
*/
|
|
357
367
|
export type ResolveSandboxLabels = (session: RunSession) => ResultAsync<SandboxLabels, GateFailure>;
|
|
358
368
|
/**
|
|
359
|
-
* The
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* the same name instead of leaking a second one.
|
|
369
|
+
* The name of a sandbox machine. It comes from the lease workflow and the machine
|
|
370
|
+
* generation, thus a rerun of the spawn step adopts the machine of the earlier
|
|
371
|
+
* attempt instead of a second one.
|
|
363
372
|
*/
|
|
364
373
|
export type SandboxIdentity = {
|
|
365
|
-
/** `sbx-{run8}-{
|
|
366
|
-
* checkpoint, not the name, is what makes create idempotent. */
|
|
374
|
+
/** `sbx-{run8}-{hash12}`. */
|
|
367
375
|
sandboxId: string;
|
|
368
376
|
};
|
|
369
377
|
/**
|
|
370
378
|
* A sandbox machine the backend is running that Cortex *manages*
|
|
371
|
-
* (`app.kubernetes.io/managed-by=cortex`), enumerated by the
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* creation-time grace and only when observably dead.
|
|
379
|
+
* (`app.kubernetes.io/managed-by=cortex`), enumerated by the reconciler.
|
|
380
|
+
* `ownerWorkflowId` is the id of the lease workflow as given, so it is usable as
|
|
381
|
+
* a DBOS lookup key; null means the machine records no owner, and such a machine
|
|
382
|
+
* is removed only past a creation-time grace and only when observably dead.
|
|
376
383
|
*/
|
|
377
384
|
export type ManagedSandbox = {
|
|
378
385
|
sandboxId: string;
|
package/dist/sandbox/types.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sandbox-client types — the wire/persistence shapes that cross the
|
|
3
|
-
* submit/poll protocol (see the harness-sandbox-exec spec) and the
|
|
3
|
+
* submit/poll protocol (see the harness-sandbox-exec spec) and the lease table.
|
|
4
4
|
*
|
|
5
|
-
* `SandboxRef` is the handle the harness uses to talk to a live sandbox.
|
|
6
|
-
*
|
|
5
|
+
* `SandboxRef` is the handle the harness uses to talk to a live sandbox. A lease
|
|
6
|
+
* row persists the same shape (`PersistedSandboxRef`, from
|
|
7
7
|
* `harness/state/schema.ts`).
|
|
8
8
|
*/
|
|
9
9
|
import { z } from "zod";
|
|
@@ -78,12 +78,18 @@ export const ExecResultSchema = z.object({
|
|
|
78
78
|
stderrTruncated: z.boolean().optional(),
|
|
79
79
|
stdoutTotalBytes: z.number().int().nonnegative().optional(),
|
|
80
80
|
stderrTotalBytes: z.number().int().nonnegative().optional(),
|
|
81
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Set when the machine died under the exec, or when a new machine of the lease found the exec lost. The sandbox
|
|
83
|
+
* module adds the memory limit of the grant, thus the agent can tell its own memory use from an outside cause.
|
|
84
|
+
*/
|
|
82
85
|
syntheticFailure: z
|
|
83
86
|
.object({
|
|
84
87
|
reason: z.string(),
|
|
88
|
+
memoryLimitGb: z.number().optional(),
|
|
85
89
|
})
|
|
86
90
|
.optional(),
|
|
91
|
+
/** Set by sandbox-server on the record of an exec that a restart of the machine cut. */
|
|
92
|
+
lost: z.object({ reason: z.string() }).optional(),
|
|
87
93
|
/** Runtime file-I/O frame from sandbox-server. Absent from a synthetic failure. */
|
|
88
94
|
provenance: ProvenanceFrameSchema.optional(),
|
|
89
95
|
/**
|
|
@@ -18,13 +18,6 @@ export declare const PlanStepSchema: z.ZodObject<{
|
|
|
18
18
|
caveats: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
19
19
|
packages: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
20
20
|
depends_on: z.ZodArray<z.ZodString>;
|
|
21
|
-
resources: z.ZodNonOptional<z.ZodOptional<z.ZodObject<{
|
|
22
|
-
cpu: z.ZodNumber;
|
|
23
|
-
memoryGb: z.ZodNumber;
|
|
24
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
25
|
-
count: z.ZodNumber;
|
|
26
|
-
}, z.core.$strip>>;
|
|
27
|
-
}, z.core.$strip>>>;
|
|
28
21
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
29
22
|
status: z.ZodEnum<{
|
|
30
23
|
flagged: "flagged";
|
|
@@ -69,13 +62,6 @@ export declare const PlannerPlanSchema: z.ZodObject<{
|
|
|
69
62
|
caveats: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
63
|
packages: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
71
64
|
depends_on: z.ZodArray<z.ZodString>;
|
|
72
|
-
resources: z.ZodNonOptional<z.ZodOptional<z.ZodObject<{
|
|
73
|
-
cpu: z.ZodNumber;
|
|
74
|
-
memoryGb: z.ZodNumber;
|
|
75
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
76
|
-
count: z.ZodNumber;
|
|
77
|
-
}, z.core.$strip>>;
|
|
78
|
-
}, z.core.$strip>>>;
|
|
79
65
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
80
66
|
status: z.ZodEnum<{
|
|
81
67
|
flagged: "flagged";
|
|
@@ -129,13 +115,6 @@ export declare const PlanningAgentOutputSchema: z.ZodObject<{
|
|
|
129
115
|
caveats: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
130
116
|
packages: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
131
117
|
depends_on: z.ZodArray<z.ZodString>;
|
|
132
|
-
resources: z.ZodNonOptional<z.ZodOptional<z.ZodObject<{
|
|
133
|
-
cpu: z.ZodNumber;
|
|
134
|
-
memoryGb: z.ZodNumber;
|
|
135
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
136
|
-
count: z.ZodNumber;
|
|
137
|
-
}, z.core.$strip>>;
|
|
138
|
-
}, z.core.$strip>>>;
|
|
139
118
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
140
119
|
status: z.ZodEnum<{
|
|
141
120
|
flagged: "flagged";
|
|
@@ -11,19 +11,18 @@ import { DEFAULT_SANDBOX_MAX_STEPS } from "../config/resource-limits.js";
|
|
|
11
11
|
* and the hardcoded sandbox turn budget. */
|
|
12
12
|
export const PlanStepSchema = AnalysisStepSchema.omit({
|
|
13
13
|
status: true,
|
|
14
|
-
timeout: true,
|
|
15
14
|
summary: true,
|
|
16
15
|
artifactIds: true,
|
|
17
16
|
error: true,
|
|
18
17
|
maxSteps: true,
|
|
19
18
|
})
|
|
20
|
-
.required({ agent: true,
|
|
19
|
+
.required({ agent: true, packages: true, capability: true })
|
|
21
20
|
.extend({
|
|
22
21
|
agent: z.enum(plannableAgentIds).describe("Sandbox agent ID, routed by the step's primary data object. Must be one of the available agents."),
|
|
23
22
|
});
|
|
24
23
|
/** Plan schema for planner output — uses PlanStepSchema for steps and requires
|
|
25
24
|
* a concise title (optional on the persistence schema for historical plans). */
|
|
26
|
-
export const PlannerPlanSchema = AnalysisPlanSchema.extend({
|
|
25
|
+
export const PlannerPlanSchema = AnalysisPlanSchema.omit({ request: true }).extend({
|
|
27
26
|
title: z
|
|
28
27
|
.string()
|
|
29
28
|
.min(1)
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Run synthesis schema — cross-step findings grounded in literature.
|
|
3
3
|
*
|
|
4
4
|
* Produced by the synthesis step after all sandbox steps complete.
|
|
5
|
-
* Combines computational findings from step summaries with literature
|
|
6
|
-
* evidence
|
|
5
|
+
* Combines computational findings from step summaries with the literature
|
|
6
|
+
* evidence of the lookups of the synthesizer.
|
|
7
7
|
*
|
|
8
8
|
* This schema is the synthesizer's output contract: it is emitted as the
|
|
9
9
|
* `submit_synthesis` tool's arg schema, so every field's meaning must live in
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Run synthesis schema — cross-step findings grounded in literature.
|
|
3
3
|
*
|
|
4
4
|
* Produced by the synthesis step after all sandbox steps complete.
|
|
5
|
-
* Combines computational findings from step summaries with literature
|
|
6
|
-
* evidence
|
|
5
|
+
* Combines computational findings from step summaries with the literature
|
|
6
|
+
* evidence of the lookups of the synthesizer.
|
|
7
7
|
*
|
|
8
8
|
* This schema is the synthesizer's output contract: it is emitted as the
|
|
9
9
|
* `submit_synthesis` tool's arg schema, so every field's meaning must live in
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
export const LiteratureReferenceSchema = z.object({
|
|
14
|
-
pmid: z.string().describe("Numeric PubMed ID
|
|
14
|
+
pmid: z.string().describe("Numeric PubMed ID, from a lookup result of this run."),
|
|
15
15
|
citation: z.string().describe('Short citation for the paper, e.g. "Smith 2020".'),
|
|
16
16
|
relevance: z.string().describe("Why this paper matters to the finding — what it establishes, challenges, or contextualizes."),
|
|
17
17
|
concordance: z
|
|
@@ -9,25 +9,12 @@
|
|
|
9
9
|
* 1. Topological sort succeeds (no cycles, no missing deps)
|
|
10
10
|
* 2. Output prefix uniqueness
|
|
11
11
|
* 3. All agent assignments exist in the agent catalog
|
|
12
|
-
* 4. All steps have resources defined
|
|
13
12
|
*/
|
|
14
|
-
import type { ResourceLimits } from "../config/resource-limits.js";
|
|
15
13
|
import type { AnalysisPlan } from "./workflow-state.js";
|
|
16
14
|
export type ValidationResult = {
|
|
17
15
|
valid: boolean;
|
|
18
16
|
errors: string[];
|
|
19
17
|
};
|
|
20
|
-
export type ValidatePlanOptions = {
|
|
21
|
-
/**
|
|
22
|
-
* Host per-step resource ceilings. When present, a step whose declared
|
|
23
|
-
* `resources` exceed them is an error — the planner gets actionable
|
|
24
|
-
* feedback at plan time instead of a silent clamp at sandbox creation.
|
|
25
|
-
* The plan-generation path passes this; `execute_analysis` plan mode deliberately does
|
|
26
|
-
* not, so stored plans that predate the policy keep running (the
|
|
27
|
-
* sandbox-creation clamp remains their backstop).
|
|
28
|
-
*/
|
|
29
|
-
readonly perStepCeiling?: ResourceLimits;
|
|
30
|
-
};
|
|
31
18
|
/** Derive a filesystem-safe output prefix from a step ID. */
|
|
32
19
|
export declare function deriveOutputPrefix(stepId: string): string;
|
|
33
20
|
/**
|
|
@@ -36,4 +23,4 @@ export declare function deriveOutputPrefix(stepId: string): string;
|
|
|
36
23
|
* Returns `valid: true` if the plan passes all checks, or `valid: false`
|
|
37
24
|
* with a list of human-readable error strings.
|
|
38
25
|
*/
|
|
39
|
-
export declare function validatePlan(plan: AnalysisPlan
|
|
26
|
+
export declare function validatePlan(plan: AnalysisPlan): ValidationResult;
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
* 1. Topological sort succeeds (no cycles, no missing deps)
|
|
10
10
|
* 2. Output prefix uniqueness
|
|
11
11
|
* 3. All agent assignments exist in the agent catalog
|
|
12
|
-
* 4. All steps have resources defined
|
|
13
12
|
*/
|
|
14
13
|
import { errorMessageOf } from "../lib/thrown.js";
|
|
15
14
|
import { textOf } from "../lib/unknown-text.js";
|
|
@@ -75,7 +74,7 @@ export function deriveOutputPrefix(stepId) {
|
|
|
75
74
|
* Returns `valid: true` if the plan passes all checks, or `valid: false`
|
|
76
75
|
* with a list of human-readable error strings.
|
|
77
76
|
*/
|
|
78
|
-
export function validatePlan(plan
|
|
77
|
+
export function validatePlan(plan) {
|
|
79
78
|
const errors = [];
|
|
80
79
|
if (plan.steps.length === 0) {
|
|
81
80
|
return { valid: true, errors: [] };
|
|
@@ -120,25 +119,7 @@ export function validatePlan(plan, options) {
|
|
|
120
119
|
errors.push(`Step "${step.id}" assigns unknown agent "${step.agent}" — not found in agent catalog`);
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
|
-
// 4.
|
|
124
|
-
const ceiling = options?.perStepCeiling;
|
|
125
|
-
for (const step of plan.steps) {
|
|
126
|
-
if (!step.resources) {
|
|
127
|
-
errors.push(`Step "${step.id}" has no resources defined — cpu and memoryGb are required`);
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if (!ceiling)
|
|
131
|
-
continue;
|
|
132
|
-
if (step.resources.cpu > ceiling.maxCpu) {
|
|
133
|
-
errors.push(`Step "${step.id}" requests cpu: ${step.resources.cpu} but this host allows at most ` +
|
|
134
|
-
`${ceiling.maxCpu} per step — reduce cpu or restructure the step`);
|
|
135
|
-
}
|
|
136
|
-
if (step.resources.memoryGb > ceiling.maxMemoryGb) {
|
|
137
|
-
errors.push(`Step "${step.id}" requests memoryGb: ${step.resources.memoryGb} but this host allows at most ` +
|
|
138
|
-
`${ceiling.maxMemoryGb} per step — reduce memoryGb or restructure the step`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// 5. Reserved step-id names (collide with artifact subdirectories or the
|
|
122
|
+
// 4. Reserved step-id names (collide with artifact subdirectories or the
|
|
142
123
|
// run-phase synthesis ledger row)
|
|
143
124
|
for (const step of plan.steps) {
|
|
144
125
|
if (RESERVED_STEP_IDS.has(step.id.toLowerCase())) {
|
|
@@ -147,7 +128,7 @@ export function validatePlan(plan, options) {
|
|
|
147
128
|
`step-directory convention; "${SYNTHESIS_STEP_ID}" is the run-level synthesis phase)`);
|
|
148
129
|
}
|
|
149
130
|
}
|
|
150
|
-
//
|
|
131
|
+
// 5. Each package entry is a query of the one grammar. The validation IS
|
|
151
132
|
// the parse, thus no second reader of the grammar can disagree with the
|
|
152
133
|
// link pass. An absent array passes, because stored plans from before
|
|
153
134
|
// the field carry none.
|
|
@@ -158,7 +139,7 @@ export function validatePlan(plan, options) {
|
|
|
158
139
|
errors.push(describeParseError(step.id, entry, parsed.error));
|
|
159
140
|
}
|
|
160
141
|
}
|
|
161
|
-
//
|
|
142
|
+
// 6. Step-id path safety. The id becomes the `runs/{runId}/{stepId}` directory
|
|
162
143
|
// segment and a `/{analysisId}/…` container mount path; an unsafe segment
|
|
163
144
|
// (a slash, or `.`/`..`) could traverse or widen the mount. The sandbox
|
|
164
145
|
// mount boundary also rejects these (assertSafeId), but catching it here
|
|
@@ -170,7 +151,7 @@ export function validatePlan(plan, options) {
|
|
|
170
151
|
`and cannot be '.' or '..' (the id becomes a workspace directory and container mount segment)`);
|
|
171
152
|
}
|
|
172
153
|
}
|
|
173
|
-
//
|
|
154
|
+
// 7. Step-id label safety. The id also becomes the value of the sandbox
|
|
174
155
|
// label `cortex/step-id`, and the harness stamps a label value with no
|
|
175
156
|
// change (sandbox-labels spec). A safe id can be too long for a label
|
|
176
157
|
// value, or it can start or end with a separator. Refused here, the
|
|
@@ -71,13 +71,6 @@ export declare const AnalysisStepSchema: z.ZodObject<{
|
|
|
71
71
|
running: "running";
|
|
72
72
|
skipped: "skipped";
|
|
73
73
|
}>>;
|
|
74
|
-
resources: z.ZodOptional<z.ZodObject<{
|
|
75
|
-
cpu: z.ZodNumber;
|
|
76
|
-
memoryGb: z.ZodNumber;
|
|
77
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
78
|
-
count: z.ZodNumber;
|
|
79
|
-
}, z.core.$strip>>;
|
|
80
|
-
}, z.core.$strip>>;
|
|
81
74
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
82
75
|
status: z.ZodEnum<{
|
|
83
76
|
flagged: "flagged";
|
|
@@ -97,7 +90,6 @@ export declare const AnalysisStepSchema: z.ZodObject<{
|
|
|
97
90
|
}, z.core.$strip>>;
|
|
98
91
|
agent: z.ZodOptional<z.ZodString>;
|
|
99
92
|
capability: z.ZodOptional<z.ZodString>;
|
|
100
|
-
timeout: z.ZodOptional<z.ZodNumber>;
|
|
101
93
|
maxSteps: z.ZodNumber;
|
|
102
94
|
description: z.ZodOptional<z.ZodString>;
|
|
103
95
|
summary: z.ZodOptional<z.ZodString>;
|
|
@@ -126,13 +118,6 @@ export declare const AnalysisPlanSchema: z.ZodObject<{
|
|
|
126
118
|
running: "running";
|
|
127
119
|
skipped: "skipped";
|
|
128
120
|
}>>;
|
|
129
|
-
resources: z.ZodOptional<z.ZodObject<{
|
|
130
|
-
cpu: z.ZodNumber;
|
|
131
|
-
memoryGb: z.ZodNumber;
|
|
132
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
133
|
-
count: z.ZodNumber;
|
|
134
|
-
}, z.core.$strip>>;
|
|
135
|
-
}, z.core.$strip>>;
|
|
136
121
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
137
122
|
status: z.ZodEnum<{
|
|
138
123
|
flagged: "flagged";
|
|
@@ -152,7 +137,6 @@ export declare const AnalysisPlanSchema: z.ZodObject<{
|
|
|
152
137
|
}, z.core.$strip>>;
|
|
153
138
|
agent: z.ZodOptional<z.ZodString>;
|
|
154
139
|
capability: z.ZodOptional<z.ZodString>;
|
|
155
|
-
timeout: z.ZodOptional<z.ZodNumber>;
|
|
156
140
|
maxSteps: z.ZodNumber;
|
|
157
141
|
description: z.ZodOptional<z.ZodString>;
|
|
158
142
|
summary: z.ZodOptional<z.ZodString>;
|
|
@@ -162,6 +146,7 @@ export declare const AnalysisPlanSchema: z.ZodObject<{
|
|
|
162
146
|
created_at: z.ZodString;
|
|
163
147
|
omicsType: z.ZodOptional<z.ZodString>;
|
|
164
148
|
omicsSubtype: z.ZodOptional<z.ZodString>;
|
|
149
|
+
request: z.ZodOptional<z.ZodString>;
|
|
165
150
|
}, z.core.$strip>;
|
|
166
151
|
/**
|
|
167
152
|
* Declarative suspension format — tells the frontend what to render
|
|
@@ -216,13 +201,6 @@ export declare const ContentBlockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
216
201
|
running: "running";
|
|
217
202
|
skipped: "skipped";
|
|
218
203
|
}>>;
|
|
219
|
-
resources: z.ZodOptional<z.ZodObject<{
|
|
220
|
-
cpu: z.ZodNumber;
|
|
221
|
-
memoryGb: z.ZodNumber;
|
|
222
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
223
|
-
count: z.ZodNumber;
|
|
224
|
-
}, z.core.$strip>>;
|
|
225
|
-
}, z.core.$strip>>;
|
|
226
204
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
227
205
|
status: z.ZodEnum<{
|
|
228
206
|
flagged: "flagged";
|
|
@@ -242,7 +220,6 @@ export declare const ContentBlockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
242
220
|
}, z.core.$strip>>;
|
|
243
221
|
agent: z.ZodOptional<z.ZodString>;
|
|
244
222
|
capability: z.ZodOptional<z.ZodString>;
|
|
245
|
-
timeout: z.ZodOptional<z.ZodNumber>;
|
|
246
223
|
maxSteps: z.ZodNumber;
|
|
247
224
|
description: z.ZodOptional<z.ZodString>;
|
|
248
225
|
summary: z.ZodOptional<z.ZodString>;
|
|
@@ -252,6 +229,7 @@ export declare const ContentBlockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
252
229
|
created_at: z.ZodString;
|
|
253
230
|
omicsType: z.ZodOptional<z.ZodString>;
|
|
254
231
|
omicsSubtype: z.ZodOptional<z.ZodString>;
|
|
232
|
+
request: z.ZodOptional<z.ZodString>;
|
|
255
233
|
}, z.core.$strip>;
|
|
256
234
|
}, z.core.$strip>, z.ZodObject<{
|
|
257
235
|
type: z.ZodLiteral<"json">;
|
|
@@ -354,13 +332,6 @@ export declare const SuspensionPayloadSchema: z.ZodObject<{
|
|
|
354
332
|
running: "running";
|
|
355
333
|
skipped: "skipped";
|
|
356
334
|
}>>;
|
|
357
|
-
resources: z.ZodOptional<z.ZodObject<{
|
|
358
|
-
cpu: z.ZodNumber;
|
|
359
|
-
memoryGb: z.ZodNumber;
|
|
360
|
-
gpu: z.ZodOptional<z.ZodObject<{
|
|
361
|
-
count: z.ZodNumber;
|
|
362
|
-
}, z.core.$strip>>;
|
|
363
|
-
}, z.core.$strip>>;
|
|
364
335
|
grounding: z.ZodOptional<z.ZodObject<{
|
|
365
336
|
status: z.ZodEnum<{
|
|
366
337
|
flagged: "flagged";
|
|
@@ -380,7 +351,6 @@ export declare const SuspensionPayloadSchema: z.ZodObject<{
|
|
|
380
351
|
}, z.core.$strip>>;
|
|
381
352
|
agent: z.ZodOptional<z.ZodString>;
|
|
382
353
|
capability: z.ZodOptional<z.ZodString>;
|
|
383
|
-
timeout: z.ZodOptional<z.ZodNumber>;
|
|
384
354
|
maxSteps: z.ZodNumber;
|
|
385
355
|
description: z.ZodOptional<z.ZodString>;
|
|
386
356
|
summary: z.ZodOptional<z.ZodString>;
|
|
@@ -390,6 +360,7 @@ export declare const SuspensionPayloadSchema: z.ZodObject<{
|
|
|
390
360
|
created_at: z.ZodString;
|
|
391
361
|
omicsType: z.ZodOptional<z.ZodString>;
|
|
392
362
|
omicsSubtype: z.ZodOptional<z.ZodString>;
|
|
363
|
+
request: z.ZodOptional<z.ZodString>;
|
|
393
364
|
}, z.core.$strip>;
|
|
394
365
|
}, z.core.$strip>, z.ZodObject<{
|
|
395
366
|
type: z.ZodLiteral<"json">;
|