@inflexa-ai/harness 0.1.1 → 0.2.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/dist/agents/conversation-agent.d.ts +12 -11
- package/dist/agents/conversation-agent.js +30 -33
- package/dist/agents/report-builder.js +2 -1
- package/dist/agents/sandbox/bulk-transcriptomics-agent.js +1 -1
- package/dist/agents/sandbox/cheminformatics-agent.js +1 -19
- package/dist/agents/sandbox/chromatin-agent.js +1 -10
- package/dist/agents/sandbox/dna-methylation-agent.js +1 -10
- package/dist/agents/sandbox/drug-repurposing-agent.js +4 -14
- package/dist/agents/sandbox/enrichment-agent.js +1 -10
- package/dist/agents/sandbox/ephemeral-executor.js +1 -10
- package/dist/agents/sandbox/genomic-variant-agent.js +1 -3
- package/dist/agents/sandbox/immune-profiling-agent.js +1 -13
- package/dist/agents/sandbox/metabolomics-agent.js +1 -1
- package/dist/agents/sandbox/microbiome-agent.js +1 -1
- package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -12
- package/dist/agents/sandbox/multimodal-sc-agent.js +1 -1
- package/dist/agents/sandbox/network-agent.js +1 -10
- package/dist/agents/sandbox/pkpd-clinical-response-agent.js +2 -8
- package/dist/agents/sandbox/proteomics-agent.js +1 -1
- package/dist/agents/sandbox/scientific-executor.js +1 -10
- package/dist/agents/sandbox/shared.d.ts +17 -6
- package/dist/agents/sandbox/shared.js +49 -38
- package/dist/agents/sandbox/single-cell-agent.js +1 -1
- package/dist/agents/sandbox/spatial-omics-agent.js +1 -1
- package/dist/agents/sandbox/statistical-modeling-agent.js +1 -1
- package/dist/agents/sandbox/translational-safety-agent.js +3 -9
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/system-prompt.d.ts +37 -11
- package/dist/agents/system-prompt.js +35 -12
- package/dist/app/data-profile-orientation.d.ts +34 -0
- package/dist/app/data-profile-orientation.js +117 -0
- package/dist/contracts/schemas/chat-parts.d.ts +9 -9
- package/dist/execution/artifact-metadata.js +2 -1
- package/dist/execution/ephemeral-runner.d.ts +13 -0
- package/dist/execution/ephemeral-runner.js +23 -1
- package/dist/execution/run-synthesis.js +20 -19
- package/dist/execution/step-summary.js +2 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +19 -5
- package/dist/loop/__fixtures__/scripted-provider.d.ts +2 -2
- package/dist/loop/__fixtures__/scripted-provider.js +2 -1
- package/dist/loop/metrics.d.ts +33 -1
- package/dist/loop/metrics.js +56 -2
- package/dist/loop/run-agent.d.ts +13 -1
- package/dist/loop/run-agent.js +19 -4
- package/dist/memory/card-builders.d.ts +8 -1
- package/dist/memory/card-builders.js +20 -3
- package/dist/memory/reconstruct-cards.js +4 -3
- package/dist/memory/thread-history.d.ts +27 -2
- package/dist/memory/thread-history.js +45 -5
- package/dist/memory/working-memory.d.ts +93 -10
- package/dist/memory/working-memory.js +220 -84
- package/dist/prompts/SOUL.d.ts +22 -9
- package/dist/prompts/SOUL.js +56 -37
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +2 -2
- package/dist/prompts/briefing.d.ts +100 -0
- package/dist/prompts/briefing.js +166 -0
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +94 -440
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +10 -10
- package/dist/prompts/planner.js +28 -59
- package/dist/prompts/report-builder.d.ts +1 -1
- package/dist/prompts/report-builder.js +71 -24
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +2 -7
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +11 -15
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +3 -7
- package/dist/prompts/sandbox/data-profiler.d.ts +1 -1
- package/dist/prompts/sandbox/data-profiler.js +13 -8
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +15 -16
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +2 -9
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +2 -6
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +3 -8
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +1 -7
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +2 -7
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +2 -8
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +2 -7
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +2 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +2 -9
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +2 -9
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -6
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +2 -7
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +13 -14
- package/dist/prompts/sandbox-standards.d.ts +22 -14
- package/dist/prompts/sandbox-standards.js +91 -71
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +33 -131
- package/dist/provenance/collector.js +1 -1
- package/dist/providers/ai-sdk.js +31 -4
- package/dist/providers/prompt-cache.d.ts +74 -0
- package/dist/providers/prompt-cache.js +77 -0
- package/dist/providers/types.d.ts +33 -0
- package/dist/reference-data/catalog.d.ts +131 -0
- package/dist/reference-data/catalog.js +348 -0
- package/dist/reference-data/receipt.d.ts +41 -0
- package/dist/reference-data/receipt.js +40 -0
- package/dist/schemas/data-profile-schemas.js +5 -3
- package/dist/schemas/plan-schemas.d.ts +21 -21
- package/dist/schemas/plan-schemas.js +9 -3
- package/dist/schemas/run-synthesis.d.ts +8 -4
- package/dist/schemas/run-synthesis.js +92 -56
- package/dist/schemas/validate-plan.js +1 -1
- package/dist/schemas/workflow-state.d.ts +7 -1
- package/dist/schemas/workflow-state.js +33 -19
- package/dist/state/artifacts.d.ts +13 -0
- package/dist/state/artifacts.js +27 -0
- package/dist/state/data-profile.d.ts +64 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +1 -1
- package/dist/tasks/data-profile.d.ts +20 -2
- package/dist/tasks/data-profile.js +71 -22
- package/dist/tools/bio/chembl.d.ts +38 -0
- package/dist/tools/bio/chembl.js +138 -0
- package/dist/tools/bio/comptox.d.ts +50 -0
- package/dist/tools/bio/comptox.js +149 -0
- package/dist/tools/bio/get-impc-ko-profile.js +9 -6
- package/dist/tools/bio/index.d.ts +5 -17
- package/dist/tools/bio/index.js +5 -17
- package/dist/tools/bio/keys.d.ts +14 -9
- package/dist/tools/bio/keys.js +16 -16
- package/dist/tools/bio/opentargets.d.ts +38 -0
- package/dist/tools/bio/opentargets.js +89 -0
- package/dist/tools/bio/pubchem.d.ts +29 -0
- package/dist/tools/bio/pubchem.js +92 -0
- package/dist/tools/bio/pubmed.d.ts +51 -0
- package/dist/tools/bio/pubmed.js +100 -0
- package/dist/tools/bio/search-bgee-expression.js +12 -6
- package/dist/tools/bio/search-clinical-trials.js +16 -5
- package/dist/tools/bio/search-faers.js +8 -4
- package/dist/tools/bio/search-geo-datasets.js +20 -6
- package/dist/tools/bio/search-gwas-catalog.d.ts +1 -1
- package/dist/tools/bio/search-pharmgkb.js +10 -4
- package/dist/tools/display/index.d.ts +1 -0
- package/dist/tools/display/index.js +1 -0
- package/dist/tools/display/normalize-echart-spec.d.ts +61 -0
- package/dist/tools/display/normalize-echart-spec.js +211 -0
- package/dist/tools/display/show-user.d.ts +6 -1
- package/dist/tools/display/show-user.js +25 -12
- package/dist/tools/execute-plan.js +5 -2
- package/dist/tools/iterate-report.d.ts +168 -118
- package/dist/tools/iterate-report.js +334 -96
- package/dist/tools/lib/chembl-client.d.ts +12 -0
- package/dist/tools/lib/chembl-client.js +127 -0
- package/dist/tools/lib/ctx-ops.d.ts +224 -0
- package/dist/tools/lib/ctx-ops.js +450 -0
- package/dist/tools/lib/iuphar-client.js +10 -1
- package/dist/tools/lib/openfda-client.js +11 -3
- package/dist/tools/lib/pathway-client.js +10 -4
- package/dist/tools/lib/pubchem-ops.d.ts +74 -0
- package/dist/tools/lib/pubchem-ops.js +224 -0
- package/dist/tools/memory/update-working-memory.d.ts +45 -7
- package/dist/tools/memory/update-working-memory.js +111 -41
- package/dist/tools/report/index.d.ts +3 -2
- package/dist/tools/report/index.js +3 -2
- package/dist/tools/research/generate-analogy-report.js +25 -12
- package/dist/tools/research/generate-plan.d.ts +12 -5
- package/dist/tools/research/generate-plan.js +168 -37
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.d.ts +85 -0
- package/dist/tools/research/inspect-data-profile.js +161 -0
- package/dist/tools/research/literature-reviewer.js +3 -4
- package/dist/tools/sandbox/list-available-packages.d.ts +79 -5
- package/dist/tools/sandbox/list-available-packages.js +152 -10
- package/dist/tools/sandbox/list-available-refs.d.ts +141 -5
- package/dist/tools/sandbox/list-available-refs.js +647 -134
- package/dist/tools/sandbox/report-blocker.d.ts +39 -9
- package/dist/tools/sandbox/report-blocker.js +43 -20
- package/dist/tools/workspace/read-file.js +4 -5
- package/dist/tools/workspace/run-exec.d.ts +4 -4
- package/dist/tools/workspace/run-exec.js +4 -4
- package/dist/tools/workspace/show-file.js +10 -6
- package/dist/tools/workspace/show-plan.js +12 -6
- package/dist/tools/workspace/workspace-search.d.ts +1 -1
- package/dist/tools/workspace/workspace-search.js +11 -5
- package/dist/workflows/execute-analysis.d.ts +43 -4
- package/dist/workflows/execute-analysis.js +107 -6
- package/dist/workflows/target-assessment/lib/citation-validator.js +2 -2
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +2 -1
- package/dist/workspace/paths.d.ts +2 -2
- package/dist/workspace/paths.js +2 -2
- package/package.json +33 -33
- package/dist/execution/filesystem-artifact-registry.d.ts +0 -57
- package/dist/execution/filesystem-artifact-registry.js +0 -92
- package/dist/lib/identifiers.d.ts +0 -8
- package/dist/lib/identifiers.js +0 -10
- package/dist/lib/mime-detect.d.ts +0 -17
- package/dist/lib/mime-detect.js +0 -29
- package/dist/lib/streamed-download.d.ts +0 -28
- package/dist/lib/streamed-download.js +0 -111
- package/dist/lib/trace-context.d.ts +0 -11
- package/dist/lib/trace-context.js +0 -49
- package/dist/providers/llm-capabilities.d.ts +0 -33
- package/dist/providers/llm-capabilities.js +0 -58
- package/dist/schemas/render-step-prompt.d.ts +0 -32
- package/dist/schemas/render-step-prompt.js +0 -76
- package/dist/tools/bio/find-approval-precedent.d.ts +0 -32
- package/dist/tools/bio/find-approval-precedent.js +0 -72
- package/dist/tools/bio/get-article-details.d.ts +0 -11
- package/dist/tools/bio/get-article-details.js +0 -20
- package/dist/tools/bio/get-article-full-text.d.ts +0 -19
- package/dist/tools/bio/get-article-full-text.js +0 -32
- package/dist/tools/bio/get-bioactivity.d.ts +0 -11
- package/dist/tools/bio/get-bioactivity.js +0 -23
- package/dist/tools/bio/get-drug-info.d.ts +0 -9
- package/dist/tools/bio/get-drug-info.js +0 -22
- package/dist/tools/bio/get-mechanism.d.ts +0 -8
- package/dist/tools/bio/get-mechanism.js +0 -18
- package/dist/tools/bio/get-pubchem-assays.d.ts +0 -22
- package/dist/tools/bio/get-pubchem-assays.js +0 -107
- package/dist/tools/bio/get-pubchem-cross-refs.d.ts +0 -15
- package/dist/tools/bio/get-pubchem-cross-refs.js +0 -68
- package/dist/tools/bio/get-target-safety.d.ts +0 -18
- package/dist/tools/bio/get-target-safety.js +0 -26
- package/dist/tools/bio/search-compounds.d.ts +0 -22
- package/dist/tools/bio/search-compounds.js +0 -160
- package/dist/tools/bio/search-ctx-chemical.d.ts +0 -50
- package/dist/tools/bio/search-ctx-chemical.js +0 -130
- package/dist/tools/bio/search-ctx-exposure.d.ts +0 -55
- package/dist/tools/bio/search-ctx-exposure.js +0 -176
- package/dist/tools/bio/search-ctx-hazard.d.ts +0 -111
- package/dist/tools/bio/search-ctx-hazard.js +0 -178
- package/dist/tools/bio/search-opentargets.d.ts +0 -11
- package/dist/tools/bio/search-opentargets.js +0 -25
- package/dist/tools/bio/search-pubchem-compound.d.ts +0 -26
- package/dist/tools/bio/search-pubchem-compound.js +0 -110
- package/dist/tools/bio/search-pubmed.d.ts +0 -17
- package/dist/tools/bio/search-pubmed.js +0 -31
- package/dist/tools/bio/search-targets.d.ts +0 -9
- package/dist/tools/bio/search-targets.js +0 -20
- package/dist/tools/bio/search-toxcast.d.ts +0 -36
- package/dist/tools/bio/search-toxcast.js +0 -141
- package/dist/workflows/register-workflows.d.ts +0 -51
- package/dist/workflows/register-workflows.js +0 -48
- package/dist/workflows/resume-execute-analysis.d.ts +0 -70
- package/dist/workflows/resume-execute-analysis.js +0 -80
- package/dist/workflows/target-assessment/correction-loop.d.ts +0 -45
- package/dist/workflows/target-assessment/correction-loop.js +0 -62
- package/dist/workflows/target-assessment/steps/phase0-resolve.d.ts +0 -71
- package/dist/workflows/target-assessment/steps/phase0-resolve.js +0 -21
- package/dist/workflows/target-assessment/steps/phase1-aggregate.d.ts +0 -1018
- package/dist/workflows/target-assessment/steps/phase1-aggregate.js +0 -25
- package/dist/workspace/mount-strategy.d.ts +0 -50
- package/dist/workspace/mount-strategy.js +0 -67
- package/dist/workspace/provenance-collector.d.ts +0 -34
- package/dist/workspace/provenance-collector.js +0 -16
- package/dist/workspace/workspace-paths.d.ts +0 -26
- package/dist/workspace/workspace-paths.js +0 -31
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `report_blocker` —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* `report_blocker` — an agent's honest exit when it cannot fulfil the loop it
|
|
3
|
+
* is driving (see the harness-sandbox-agents spec). There is no `submit`/`done`
|
|
4
|
+
* tool on a sandbox step: a step's deliverable is its persisted files, so a
|
|
5
|
+
* clean end-of-turn after writing them is the implicit success. Calling
|
|
6
|
+
* `report_blocker` is the alternative to improvising an inline result the
|
|
7
|
+
* harness would launder into a green run.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* This module is the canonical home of the tool. Three loops offer it — the
|
|
10
|
+
* sandbox step agent, the planner (`tools/research/generate-plan.ts`) and the
|
|
11
|
+
* run synthesizer (`execution/run-synthesis.ts`) — and all three share one id,
|
|
12
|
+
* one input schema and one terminal contract via `createReportBlockerToolFor`.
|
|
13
|
+
* What differs per loop is only what a blocker *means* there, so each call site
|
|
14
|
+
* injects its own `blockedWhen` prose and its own capture closure. The tool
|
|
15
|
+
* records the reason and tells the agent to stop; the driving body reads its
|
|
16
|
+
* own outcome cell after the loop and decides the terminal status.
|
|
12
17
|
*/
|
|
13
18
|
import { type Tool } from "../define-tool.js";
|
|
14
19
|
export interface BlockerOutcome {
|
|
@@ -20,4 +25,29 @@ export interface BlockerHolder {
|
|
|
20
25
|
outcome: BlockerOutcome | null;
|
|
21
26
|
}
|
|
22
27
|
export declare function createBlockerHolder(): BlockerHolder;
|
|
28
|
+
export interface ReportBlockerDeps {
|
|
29
|
+
/**
|
|
30
|
+
* The calling loop's blocker-capture side effect — writes the outcome into
|
|
31
|
+
* whatever cell that body reads after its loop. Invoked on every call; the
|
|
32
|
+
* closure owns the first-call-wins guard, because only the call site knows
|
|
33
|
+
* what else may already have terminated its loop.
|
|
34
|
+
*/
|
|
35
|
+
readonly record: (outcome: BlockerOutcome) => void;
|
|
36
|
+
/**
|
|
37
|
+
* What a blocker means in THIS loop: which loop the call ends and the
|
|
38
|
+
* conditions that warrant it. Composed into the shared terminal contract,
|
|
39
|
+
* so a description can never promise a loop this instance does not end.
|
|
40
|
+
*/
|
|
41
|
+
readonly blockedWhen: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build the `report_blocker` tool for one loop. The id, the input schema and
|
|
45
|
+
* the terminal contract ("pass a reason, then stop") are fixed here; the call
|
|
46
|
+
* site supplies only the per-loop meaning and the capture closure.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createReportBlockerToolFor(deps: ReportBlockerDeps): Tool;
|
|
49
|
+
/**
|
|
50
|
+
* The sandbox step agent's `report_blocker`, bound to the step body's holder.
|
|
51
|
+
* A blocker here ends the step with no deliverables and a blocked reason.
|
|
52
|
+
*/
|
|
23
53
|
export declare function createReportBlockerTool(holder: BlockerHolder): Tool;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `report_blocker` —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* `report_blocker` — an agent's honest exit when it cannot fulfil the loop it
|
|
3
|
+
* is driving (see the harness-sandbox-agents spec). There is no `submit`/`done`
|
|
4
|
+
* tool on a sandbox step: a step's deliverable is its persisted files, so a
|
|
5
|
+
* clean end-of-turn after writing them is the implicit success. Calling
|
|
6
|
+
* `report_blocker` is the alternative to improvising an inline result the
|
|
7
|
+
* harness would launder into a green run.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* This module is the canonical home of the tool. Three loops offer it — the
|
|
10
|
+
* sandbox step agent, the planner (`tools/research/generate-plan.ts`) and the
|
|
11
|
+
* run synthesizer (`execution/run-synthesis.ts`) — and all three share one id,
|
|
12
|
+
* one input schema and one terminal contract via `createReportBlockerToolFor`.
|
|
13
|
+
* What differs per loop is only what a blocker *means* there, so each call site
|
|
14
|
+
* injects its own `blockedWhen` prose and its own capture closure. The tool
|
|
15
|
+
* records the reason and tells the agent to stop; the driving body reads its
|
|
16
|
+
* own outcome cell after the loop and decides the terminal status.
|
|
12
17
|
*/
|
|
13
18
|
import { ok } from "neverthrow";
|
|
14
19
|
import { z } from "zod";
|
|
@@ -16,20 +21,20 @@ import { defineTool } from "../define-tool.js";
|
|
|
16
21
|
export function createBlockerHolder() {
|
|
17
22
|
return { outcome: null };
|
|
18
23
|
}
|
|
19
|
-
|
|
24
|
+
/** The one input every `report_blocker` takes, wherever it is offered. */
|
|
25
|
+
const ReportBlockerInputSchema = z.object({ reason: z.string().min(1) });
|
|
26
|
+
/**
|
|
27
|
+
* Build the `report_blocker` tool for one loop. The id, the input schema and
|
|
28
|
+
* the terminal contract ("pass a reason, then stop") are fixed here; the call
|
|
29
|
+
* site supplies only the per-loop meaning and the capture closure.
|
|
30
|
+
*/
|
|
31
|
+
export function createReportBlockerToolFor(deps) {
|
|
20
32
|
return defineTool({
|
|
21
33
|
id: "report_blocker",
|
|
22
|
-
description:
|
|
23
|
-
|
|
24
|
-
"the available data/packages, or you cannot produce the persisted " +
|
|
25
|
-
"deliverables (scripts, output files, figures). Pass a clear reason. Do " +
|
|
26
|
-
"NOT improvise an inline result and narrate it; if you cannot persist " +
|
|
27
|
-
"real output files, report a blocker. Stop after calling.",
|
|
28
|
-
inputSchema: z.object({ reason: z.string().min(1) }),
|
|
34
|
+
description: `Terminal. ${deps.blockedWhen} Pass a clear reason. Stop immediately after calling — take no further actions.`,
|
|
35
|
+
inputSchema: ReportBlockerInputSchema,
|
|
29
36
|
execute: async (input) => {
|
|
30
|
-
|
|
31
|
-
holder.outcome = { kind: "blocker", reason: input.reason };
|
|
32
|
-
}
|
|
37
|
+
deps.record({ kind: "blocker", reason: input.reason });
|
|
33
38
|
return ok({
|
|
34
39
|
recorded: true,
|
|
35
40
|
message: "Blocker recorded. You are done — stop now and do not take further actions.",
|
|
@@ -37,3 +42,21 @@ export function createReportBlockerTool(holder) {
|
|
|
37
42
|
},
|
|
38
43
|
});
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The sandbox step agent's `report_blocker`, bound to the step body's holder.
|
|
47
|
+
* A blocker here ends the step with no deliverables and a blocked reason.
|
|
48
|
+
*/
|
|
49
|
+
export function createReportBlockerTool(holder) {
|
|
50
|
+
return createReportBlockerToolFor({
|
|
51
|
+
record: (outcome) => {
|
|
52
|
+
if (holder.outcome === null)
|
|
53
|
+
holder.outcome = outcome;
|
|
54
|
+
},
|
|
55
|
+
blockedWhen: "Ends this analysis step with no deliverables. Call it when you cannot " +
|
|
56
|
+
"fulfil the step — required inputs are missing or unreadable, the " +
|
|
57
|
+
"requested analysis is infeasible with the available data/packages, or " +
|
|
58
|
+
"you cannot produce the persisted deliverables (scripts, output files, " +
|
|
59
|
+
"figures). Do NOT improvise an inline result and narrate it: if you " +
|
|
60
|
+
"cannot persist real output files, report a blocker.",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -19,8 +19,7 @@ const ReadFileInputSchema = z.object({
|
|
|
19
19
|
.min(1)
|
|
20
20
|
.describe("Workspace path. Relative paths resolve against your working directory; " +
|
|
21
21
|
"an absolute '/<analysisId>/...' path is resolved against the analysis " +
|
|
22
|
-
"root (use absolute to read input data
|
|
23
|
-
"steps' outputs)."),
|
|
22
|
+
"root (use absolute to read input data or other steps' outputs)."),
|
|
24
23
|
headLines: z
|
|
25
24
|
.number()
|
|
26
25
|
.int()
|
|
@@ -28,7 +27,7 @@ const ReadFileInputSchema = z.object({
|
|
|
28
27
|
.max(10_000)
|
|
29
28
|
.optional()
|
|
30
29
|
.describe("Read only the first N lines (head). Use for inspecting CSV/TSV " +
|
|
31
|
-
"headers,
|
|
30
|
+
"headers, step summaries, or the top of any large file. Mutually " +
|
|
32
31
|
"exclusive with tailLines."),
|
|
33
32
|
tailLines: z
|
|
34
33
|
.number()
|
|
@@ -42,8 +41,8 @@ export function createReadFileTool(fs, workingDir) {
|
|
|
42
41
|
return defineTool({
|
|
43
42
|
id: "read_file",
|
|
44
43
|
description: "Read a workspace file: input data, prior-run outputs, current-run " +
|
|
45
|
-
"outputs, step summaries, run syntheses
|
|
46
|
-
"
|
|
44
|
+
"outputs, step summaries, or run syntheses. Returns the file content " +
|
|
45
|
+
"as UTF-8 text. Output is capped at " +
|
|
47
46
|
`${DEFAULT_MAX_BYTES} bytes; pass headLines or tailLines to read a ` +
|
|
48
47
|
"specific window of a large file (typical for bio CSV/TSV/log files). " +
|
|
49
48
|
"Oversize reads come back truncated with a marker. Missing paths and " +
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Internal helper used by
|
|
2
|
+
* Internal helper used by dependency-bearing tools to drive one sandbox exec
|
|
3
3
|
* through the `SandboxClient`. Centralises the submit/await pair, exec-id
|
|
4
4
|
* derivation, and intermediate-event forwarding so all three tools share
|
|
5
|
-
* one chokepoint
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* one chokepoint. `write_file` / `edit_file` are layered on the workspace
|
|
6
|
+
* mutator and environment introspection tools such as `list_available_refs`
|
|
7
|
+
* use this same replay-safe submit/await path rather than a parallel protocol.
|
|
8
8
|
*
|
|
9
9
|
* Not a registered tool — `defineTool` is called by the user-facing tools
|
|
10
10
|
* (`execute-command.ts`, `write-file.ts`, `edit-file.ts`) which wrap this.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Internal helper used by
|
|
2
|
+
* Internal helper used by dependency-bearing tools to drive one sandbox exec
|
|
3
3
|
* through the `SandboxClient`. Centralises the submit/await pair, exec-id
|
|
4
4
|
* derivation, and intermediate-event forwarding so all three tools share
|
|
5
|
-
* one chokepoint
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* one chokepoint. `write_file` / `edit_file` are layered on the workspace
|
|
6
|
+
* mutator and environment introspection tools such as `list_available_refs`
|
|
7
|
+
* use this same replay-safe submit/await path rather than a parallel protocol.
|
|
8
8
|
*
|
|
9
9
|
* Not a registered tool — `defineTool` is called by the user-facing tools
|
|
10
10
|
* (`execute-command.ts`, `write-file.ts`, `edit-file.ts`) which wrap this.
|
|
@@ -20,19 +20,23 @@ const ShowFileInputSchema = z.object({
|
|
|
20
20
|
title: z.string().optional().describe("Group heading shown above the card or gallery"),
|
|
21
21
|
files: z
|
|
22
22
|
.array(z.object({
|
|
23
|
-
path: z.string().describe("Analysis-rooted file path (e.g., 'runs/run-abc/step-1/figures/volcano.png')"),
|
|
23
|
+
path: z.string().describe("Analysis-rooted file path — no leading slash, no `..` (e.g., 'runs/run-abc/step-1/figures/volcano.png')"),
|
|
24
24
|
caption: z.string().optional().describe("One-sentence context rendered beside this file"),
|
|
25
25
|
}))
|
|
26
26
|
.min(1)
|
|
27
27
|
.max(MAX_FILES)
|
|
28
|
-
.describe(`1 to ${MAX_FILES}
|
|
28
|
+
.describe(`The related set of files to show together, 1 to ${MAX_FILES} per call; multiple render as ONE gallery card. Pass a related set in a single call — do not make one call per figure.`),
|
|
29
29
|
});
|
|
30
30
|
export const showFileTool = defineTool({
|
|
31
31
|
id: "show_file",
|
|
32
|
-
description: "Display
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
32
|
+
description: "Display EXISTING analysis artifacts in chat by path — images, CSVs, PDFs, notebooks, logs. " +
|
|
33
|
+
"The card references the file directly and its content is fetched when the user views it: you never read, " +
|
|
34
|
+
"inline, or paste the bytes. Pick this tool by what you are referencing, not by how the output looks: " +
|
|
35
|
+
"NOT for content you synthesized (a chart, a table, a snippet you invented — use `show_user`), NOT for a stored plan (`show_plan`). " +
|
|
36
|
+
"Paths are analysis-rooted (no leading slash, no `..`); discover them with `workspace_search` or `list_files`. " +
|
|
37
|
+
"Up to 10 files per call — pass a related set in ONE call and they render as a gallery, rather than one call per figure. " +
|
|
38
|
+
"Cards render in call order, so to interleave figures with prose alternate `show_user(markdown)` and `show_file` calls " +
|
|
39
|
+
"(markdown image syntax pointing at a workspace file does NOT render — this tool is the only way to show one).",
|
|
36
40
|
inputSchema: ShowFileInputSchema,
|
|
37
41
|
execute: async (input, ctx) => {
|
|
38
42
|
const card = buildFileReferenceCardData(input);
|
|
@@ -18,13 +18,19 @@ const PLAN_ID_REGEX = /^pln-[a-f0-9]{8}$/;
|
|
|
18
18
|
export function createShowPlanTool(pool) {
|
|
19
19
|
return defineTool({
|
|
20
20
|
id: "show_plan",
|
|
21
|
-
description: "Display a
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
21
|
+
description: "Display a STORED plan in chat by its planId. The plan is loaded from storage and embedded in the card, " +
|
|
22
|
+
"so the UI renders it directly. Use it to present the plan `generate_plan` just produced, and when the user " +
|
|
23
|
+
"asks to see an earlier plan again. " +
|
|
24
|
+
"Pick this tool by what you are referencing, not by how the output looks: " +
|
|
25
|
+
"NOT for a plan you are drafting in conversation (that is `generate_plan`), NOT for other synthesized content " +
|
|
26
|
+
"(`show_user`), NOT for analysis files (`show_file`). Never re-type a plan's steps into a `show_user` markdown " +
|
|
27
|
+
"card — that renders a second, divergent copy of the same plan.",
|
|
25
28
|
inputSchema: z.object({
|
|
26
|
-
planId: z
|
|
27
|
-
|
|
29
|
+
planId: z
|
|
30
|
+
.string()
|
|
31
|
+
.regex(PLAN_ID_REGEX, "planId must match pln-<8hex>")
|
|
32
|
+
.describe("Plan ID (`pln-<8hex>`) from `generate_plan` (newly created) or `inspect_run` (each run carries the planId it ran)"),
|
|
33
|
+
title: z.string().optional().describe("Optional card heading; defaults to the plan's own title"),
|
|
28
34
|
}),
|
|
29
35
|
execute: async ({ planId, title }, ctx) => {
|
|
30
36
|
const resourceId = scopeResource(ctx.session.scope).resourceId;
|
|
@@ -15,7 +15,7 @@ import type { EmbeddingProvider } from "../../providers/types.js";
|
|
|
15
15
|
export declare function createWorkspaceSearchTool(pool: Pool, embedding: EmbeddingProvider): import("../define-tool.js").Tool<{
|
|
16
16
|
query: string;
|
|
17
17
|
limit: number;
|
|
18
|
-
type?: "output" | "input" | "
|
|
18
|
+
type?: "output" | "input" | "synthesis" | "summary" | undefined;
|
|
19
19
|
}, {
|
|
20
20
|
results: {
|
|
21
21
|
id: string;
|
|
@@ -32,13 +32,19 @@ function indexName(analysisId) {
|
|
|
32
32
|
export function createWorkspaceSearchTool(pool, embedding) {
|
|
33
33
|
return defineTool({
|
|
34
34
|
id: "workspace_search",
|
|
35
|
-
description: "Semantic search over the analysis workspace
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
35
|
+
description: "Semantic search over the analysis workspace. Returns ranked file paths " +
|
|
36
|
+
"with descriptions and metadata; read a file separately to see its " +
|
|
37
|
+
"contents. Indexed entries are exactly four types, and `type` restricts " +
|
|
38
|
+
'results to one of them: "input" (staged input data files — their ' +
|
|
39
|
+
'descriptions come from the data profiler), "output" (files a step ' +
|
|
40
|
+
'produced), "summary" (a step\'s summary), "synthesis" (a run\'s ' +
|
|
41
|
+
"literature-grounded synthesis).",
|
|
39
42
|
inputSchema: z.object({
|
|
40
43
|
query: z.string().min(1).describe("What to search for, in natural language"),
|
|
41
|
-
type: z
|
|
44
|
+
type: z
|
|
45
|
+
.enum(["input", "output", "summary", "synthesis"])
|
|
46
|
+
.optional()
|
|
47
|
+
.describe('Restrict results to one entry type: "input", "output", "summary", or "synthesis"'),
|
|
42
48
|
limit: z.number().int().min(1).max(50).default(8).describe("Maximum number of results to return"),
|
|
43
49
|
}),
|
|
44
50
|
execute: async ({ query, type, limit }, ctx) => {
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
* - dispatch every `scheduleReady(plan, completedSet, startedSet)` via
|
|
21
21
|
* `DBOS.startWorkflow` with deterministic child id
|
|
22
22
|
* `"${parent.workflowID}-${N}"` (N is the step's stable index)
|
|
23
|
+
* - compose each child's seed prompt HERE, at dispatch (`composeStepSeed`,
|
|
24
|
+
* inside a durable step): the moment a step is dispatched is the first
|
|
25
|
+
* moment its dependencies' results exist, so this is the only place the
|
|
26
|
+
* seed can name them
|
|
23
27
|
* - each child input is `forStep(input.runSession, stepId)` for the
|
|
24
28
|
* session field
|
|
25
29
|
* - emit `data-dag-state` (reconciling by id) on every dispatch +
|
|
@@ -53,6 +57,7 @@ import type { Pool } from "pg";
|
|
|
53
57
|
import type { MachineBudget, ResourceSpec } from "../config/resource-limits.js";
|
|
54
58
|
import type { RunSession } from "../auth/types.js";
|
|
55
59
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
60
|
+
import type { AnalysisStep } from "../schemas/workflow-state.js";
|
|
56
61
|
import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
|
|
57
62
|
import type { BioToolKeys } from "../tools/bio/keys.js";
|
|
58
63
|
import type { RunCharge } from "../billing/run-charge.js";
|
|
@@ -76,8 +81,15 @@ export interface ExecuteAnalysisInput {
|
|
|
76
81
|
readonly steps: readonly PlanStep[];
|
|
77
82
|
/** Optional thread id for cortex_runs.thread_id (UI breadcrumb only). */
|
|
78
83
|
readonly threadId?: string | null;
|
|
79
|
-
/**
|
|
80
|
-
|
|
84
|
+
/**
|
|
85
|
+
* The plan's steps, keyed by id — the instruction DATA each step's seed is
|
|
86
|
+
* composed FROM, not a pre-rendered prompt string. The seed itself is
|
|
87
|
+
* composed per step at dispatch (`composeStepSeed`), because that is the
|
|
88
|
+
* first moment a step's dependencies have produced anything to tell it
|
|
89
|
+
* about. Snapshotted here at the async edge so the body never re-reads the
|
|
90
|
+
* plan row (a mid-run plan edit cannot change a running run's instructions).
|
|
91
|
+
*/
|
|
92
|
+
readonly planStepById: Readonly<Record<string, AnalysisStep>>;
|
|
81
93
|
/** Per-step agent id (sandbox catalog entry). */
|
|
82
94
|
readonly agentByStepId: Readonly<Record<string, string>>;
|
|
83
95
|
/** Per-step planner-estimated sandbox resource request. */
|
|
@@ -229,8 +241,9 @@ export interface ExecuteAnalysisDeps {
|
|
|
229
241
|
}
|
|
230
242
|
/**
|
|
231
243
|
* Project the sandbox-step input handed to each child. The parent owns the
|
|
232
|
-
* level computation
|
|
233
|
-
*
|
|
244
|
+
* level computation and the seed (`prompt`, composed by `composeStepSeed` at
|
|
245
|
+
* dispatch); the rest comes from `ExecuteAnalysisInput`. The body fills in
|
|
246
|
+
* `runSession` via `forStep` after this returns. Exported for the
|
|
234
247
|
* projection/timeout-propagation tests.
|
|
235
248
|
*/
|
|
236
249
|
export declare function buildChildInput(args: {
|
|
@@ -239,7 +252,33 @@ export declare function buildChildInput(args: {
|
|
|
239
252
|
level: number;
|
|
240
253
|
runId: string;
|
|
241
254
|
workflowId: string;
|
|
255
|
+
prompt: string;
|
|
242
256
|
}): Omit<SandboxStepInput, "runSession">;
|
|
257
|
+
/**
|
|
258
|
+
* Compose one step's seed — the user-content message its agent opens on.
|
|
259
|
+
*
|
|
260
|
+
* The composition point is the design: the scheduler calls this at the instant
|
|
261
|
+
* it dispatches a step, which is the first instant at which every one of that
|
|
262
|
+
* step's dependencies has *finished* and written its results down. A seed
|
|
263
|
+
* rendered any earlier (at plan submission, say) cannot mention them, and the
|
|
264
|
+
* agent is left to rediscover its own upstream.
|
|
265
|
+
*
|
|
266
|
+
* Everything read here is durable state written by an already-settled child
|
|
267
|
+
* (`output/summary.md`, `cortex_artifacts`) or by the profiler
|
|
268
|
+
* (`cortex_analysis_state`) — never live in-flight state, which would not
|
|
269
|
+
* reproduce.
|
|
270
|
+
*
|
|
271
|
+
* REPLAY: the caller wraps this in a `DBOS.runStep`, so the composed string is
|
|
272
|
+
* checkpointed and a replay returns it verbatim — the DB and disk are not read
|
|
273
|
+
* again and cannot drift the seed under a recovered run. Never call it
|
|
274
|
+
* unwrapped from the workflow body.
|
|
275
|
+
*/
|
|
276
|
+
export declare function composeStepSeed(args: {
|
|
277
|
+
input: ExecuteAnalysisInput;
|
|
278
|
+
stepId: string;
|
|
279
|
+
runId: string;
|
|
280
|
+
deps: ExecuteAnalysisDeps;
|
|
281
|
+
}): Promise<string>;
|
|
243
282
|
/**
|
|
244
283
|
* Run-level literature-grounded synthesis (no sandbox). Returns the
|
|
245
284
|
* quick-display findings for the run-completed card; empty when synthesis is
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
* - dispatch every `scheduleReady(plan, completedSet, startedSet)` via
|
|
21
21
|
* `DBOS.startWorkflow` with deterministic child id
|
|
22
22
|
* `"${parent.workflowID}-${N}"` (N is the step's stable index)
|
|
23
|
+
* - compose each child's seed prompt HERE, at dispatch (`composeStepSeed`,
|
|
24
|
+
* inside a durable step): the moment a step is dispatched is the first
|
|
25
|
+
* moment its dependencies' results exist, so this is the only place the
|
|
26
|
+
* seed can name them
|
|
23
27
|
* - each child input is `forStep(input.runSession, stepId)` for the
|
|
24
28
|
* session field
|
|
25
29
|
* - emit `data-dag-state` (reconciling by id) on every dispatch +
|
|
@@ -54,9 +58,11 @@ import { mkdir } from "node:fs/promises";
|
|
|
54
58
|
import { join } from "node:path";
|
|
55
59
|
import { forStep } from "../auth/types.js";
|
|
56
60
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
57
|
-
import { RunDedupCollisionError, countArtifactsForRun, queryActiveRun, seedStepExecutions, suspendAnalysis as suspendAnalysisQuery, sweepPendingStepExecutions, updateRunStatus, } from "../state/index.js";
|
|
61
|
+
import { RunDedupCollisionError, countArtifactsForRun, loadDataProfileStatus, queryActiveRun, queryStepArtifactPaths, seedStepExecutions, suspendAnalysis as suspendAnalysisQuery, sweepPendingStepExecutions, updateRunStatus, } from "../state/index.js";
|
|
58
62
|
import { isBudgetExceeded } from "../loop/budget-exceeded.js";
|
|
59
|
-
import {
|
|
63
|
+
import { loadStepSummariesFromDisk } from "../execution/run-synthesis.js";
|
|
64
|
+
import { MAX_UPSTREAM_ARTIFACTS, composeStepBriefing } from "../prompts/briefing.js";
|
|
65
|
+
import { runDir, runStepDir, stepSubdir, stepWritePrefix, toSandboxPath } from "../workspace/paths.js";
|
|
60
66
|
import { isChatDataPart } from "../sandbox/sandbox-step-translate.js";
|
|
61
67
|
import { synthesizeRun } from "../app/synthesize-run.js";
|
|
62
68
|
import { computeTopologicalLevels, scheduleReady, validatePlanDag } from "./execute-analysis-scheduler.js";
|
|
@@ -86,12 +92,13 @@ function emitProvenanceGuarded(deps, event) {
|
|
|
86
92
|
}
|
|
87
93
|
/**
|
|
88
94
|
* Project the sandbox-step input handed to each child. The parent owns the
|
|
89
|
-
* level computation
|
|
90
|
-
*
|
|
95
|
+
* level computation and the seed (`prompt`, composed by `composeStepSeed` at
|
|
96
|
+
* dispatch); the rest comes from `ExecuteAnalysisInput`. The body fills in
|
|
97
|
+
* `runSession` via `forStep` after this returns. Exported for the
|
|
91
98
|
* projection/timeout-propagation tests.
|
|
92
99
|
*/
|
|
93
100
|
export function buildChildInput(args) {
|
|
94
|
-
const { input, stepId, level, runId, workflowId } = args;
|
|
101
|
+
const { input, stepId, level, runId, workflowId, prompt } = args;
|
|
95
102
|
const resources = input.resourcesByStepId[stepId];
|
|
96
103
|
if (!resources) {
|
|
97
104
|
throw new Error(`executeAnalysis: step "${stepId}" missing from resourcesByStepId — every step must declare resources`);
|
|
@@ -102,12 +109,100 @@ export function buildChildInput(args) {
|
|
|
102
109
|
stepId,
|
|
103
110
|
agentId: input.agentByStepId[stepId] ?? "scientific-executor",
|
|
104
111
|
level,
|
|
105
|
-
prompt
|
|
112
|
+
prompt,
|
|
106
113
|
parentWorkflowId: workflowId,
|
|
107
114
|
resources,
|
|
108
115
|
timeoutSeconds: input.timeoutByStepId?.[stepId],
|
|
109
116
|
};
|
|
110
117
|
}
|
|
118
|
+
// ── Seed composition (at dispatch, not at plan submission) ────────────
|
|
119
|
+
/**
|
|
120
|
+
* Compose one step's seed — the user-content message its agent opens on.
|
|
121
|
+
*
|
|
122
|
+
* The composition point is the design: the scheduler calls this at the instant
|
|
123
|
+
* it dispatches a step, which is the first instant at which every one of that
|
|
124
|
+
* step's dependencies has *finished* and written its results down. A seed
|
|
125
|
+
* rendered any earlier (at plan submission, say) cannot mention them, and the
|
|
126
|
+
* agent is left to rediscover its own upstream.
|
|
127
|
+
*
|
|
128
|
+
* Everything read here is durable state written by an already-settled child
|
|
129
|
+
* (`output/summary.md`, `cortex_artifacts`) or by the profiler
|
|
130
|
+
* (`cortex_analysis_state`) — never live in-flight state, which would not
|
|
131
|
+
* reproduce.
|
|
132
|
+
*
|
|
133
|
+
* REPLAY: the caller wraps this in a `DBOS.runStep`, so the composed string is
|
|
134
|
+
* checkpointed and a replay returns it verbatim — the DB and disk are not read
|
|
135
|
+
* again and cannot drift the seed under a recovered run. Never call it
|
|
136
|
+
* unwrapped from the workflow body.
|
|
137
|
+
*/
|
|
138
|
+
export async function composeStepSeed(args) {
|
|
139
|
+
const { input, stepId, runId, deps } = args;
|
|
140
|
+
const step = input.planStepById[stepId];
|
|
141
|
+
if (!step) {
|
|
142
|
+
throw new Error(`executeAnalysis: step "${stepId}" missing from planStepById — every step must carry its plan data`);
|
|
143
|
+
}
|
|
144
|
+
const analysisId = input.analysisId;
|
|
145
|
+
const workspaceRoot = deps.resolveWorkspaceRoot(analysisId);
|
|
146
|
+
// The paths the agent actually sees: the tree is bind-mounted at
|
|
147
|
+
// `/{analysisId}`, and the step's writable directory is its cwd. Derived
|
|
148
|
+
// through the same helpers the sandbox mount and the mutate tools use, so
|
|
149
|
+
// the seed cannot disagree with the container about where anything is.
|
|
150
|
+
const workspace = {
|
|
151
|
+
analysisRoot: `/${analysisId}`,
|
|
152
|
+
workingDir: toSandboxPath(workspaceRoot, analysisId, stepWritePrefix({ workspaceRoot, runId, stepId })),
|
|
153
|
+
};
|
|
154
|
+
// A DB failure here throws (house rules): a swallowed error would be frozen
|
|
155
|
+
// into the checkpointed seed and served to every replay of this step.
|
|
156
|
+
const profile = unwrapOrThrow(await loadDataProfileStatus(deps.pool, analysisId));
|
|
157
|
+
const upstream = await collectUpstreamHandoffs({
|
|
158
|
+
analysisId,
|
|
159
|
+
runId,
|
|
160
|
+
workspaceRoot,
|
|
161
|
+
dependsOn: step.depends_on,
|
|
162
|
+
agentByStepId: input.agentByStepId,
|
|
163
|
+
pool: deps.pool,
|
|
164
|
+
});
|
|
165
|
+
return composeStepBriefing({
|
|
166
|
+
step,
|
|
167
|
+
workspace,
|
|
168
|
+
profile: profile?.result ?? null,
|
|
169
|
+
upstream,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Gather what each completed dependency produced. Dependency order is the
|
|
174
|
+
* plan's declared `depends_on` order — deterministic, so the seed is stable.
|
|
175
|
+
*
|
|
176
|
+
* A dependency that wrote no summary is omitted rather than blocking the
|
|
177
|
+
* dispatch (`loadStepSummariesFromDisk` skips a missing file): a step that
|
|
178
|
+
* completed without a summary has nothing to hand off, and waiting for one that
|
|
179
|
+
* will never arrive would deadlock the run.
|
|
180
|
+
*/
|
|
181
|
+
async function collectUpstreamHandoffs(args) {
|
|
182
|
+
const { analysisId, runId, workspaceRoot, dependsOn, agentByStepId, pool } = args;
|
|
183
|
+
if (dependsOn.length === 0)
|
|
184
|
+
return [];
|
|
185
|
+
const summaries = await loadStepSummariesFromDisk({
|
|
186
|
+
workspaceRoot,
|
|
187
|
+
runId,
|
|
188
|
+
completedSteps: dependsOn,
|
|
189
|
+
agentByStepId,
|
|
190
|
+
});
|
|
191
|
+
const handoffs = [];
|
|
192
|
+
for (const summary of summaries) {
|
|
193
|
+
const outputDir = stepSubdir(runStepDir(runId, summary.stepId), "output");
|
|
194
|
+
const artifacts = await queryStepArtifactPaths(pool, analysisId, runId, summary.stepId, MAX_UPSTREAM_ARTIFACTS);
|
|
195
|
+
handoffs.push({
|
|
196
|
+
stepId: summary.stepId,
|
|
197
|
+
agentId: summary.agentId,
|
|
198
|
+
summaryMarkdown: summary.markdown,
|
|
199
|
+
summaryPath: `/${analysisId}/${outputDir}/summary.md`,
|
|
200
|
+
outputDir: `/${analysisId}/${outputDir}`,
|
|
201
|
+
artifacts: artifacts.map((a) => `/${analysisId}/${a.path}`),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return handoffs;
|
|
205
|
+
}
|
|
111
206
|
/**
|
|
112
207
|
* Run-level literature-grounded synthesis (no sandbox). Returns the
|
|
113
208
|
* quick-display findings for the run-completed card; empty when synthesis is
|
|
@@ -416,12 +511,18 @@ async function runSchedulerLoop(args) {
|
|
|
416
511
|
for (const stepId of admit) {
|
|
417
512
|
const idx = stepIndexById.get(stepId);
|
|
418
513
|
const childWorkflowId = `${workflowId}-${idx}`;
|
|
514
|
+
// Compose the seed HERE — the dependencies of `stepId` are all in
|
|
515
|
+
// `completed`, so this is the first (and only) moment their results
|
|
516
|
+
// can be named. Checkpointed so a replay re-dispatches the child
|
|
517
|
+
// with a byte-identical prompt instead of re-reading the DB/disk.
|
|
518
|
+
const prompt = await DBOS.runStep(() => composeStepSeed({ input, stepId, runId, deps }), { name: `compose-step-seed:${stepId}` });
|
|
419
519
|
const baseChildInput = buildChildInput({
|
|
420
520
|
input,
|
|
421
521
|
stepId,
|
|
422
522
|
level: levels.get(stepId) ?? 0,
|
|
423
523
|
runId,
|
|
424
524
|
workflowId,
|
|
525
|
+
prompt,
|
|
425
526
|
});
|
|
426
527
|
const childInput = {
|
|
427
528
|
...baseChildInput,
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
// was always 0. These patterns match the format the agent actually writes.
|
|
6
6
|
// The block content may include array index notation like `[0]`, so we
|
|
7
7
|
// use a pattern that allows `[digits]` inside the outer brackets.
|
|
8
|
-
const DOSSIER_CITE_BLOCK_RE = /\[dossier:\s*((?:[^[\]]
|
|
8
|
+
const DOSSIER_CITE_BLOCK_RE = /\[dossier:\s*((?:[^[\]]|\[\d+\])*)\]/gi;
|
|
9
9
|
// External citation IDs are plain strings — no array-index notation like `[n]` — so the simple [^\]]+ class suffices here instead of the array-index-aware pattern used by DOSSIER_CITE_BLOCK_RE.
|
|
10
10
|
const EXTERNAL_CITE_BLOCK_RE = /\[external citation:\s*([^\]]+)\]/gi;
|
|
11
11
|
// Assumes at most one `external citation:` segment per dossier block (the recommendation prompt produces at most one); a hypothetical second segment in the same block would be parsed imperfectly.
|
|
12
|
-
const EXTERNAL_SEGMENT_RE = /external citation:\s*((?:[^[\]]
|
|
12
|
+
const EXTERNAL_SEGMENT_RE = /external citation:\s*((?:[^[\]]|\[\d+\])*)/i;
|
|
13
13
|
const NCT_RE = /\bNCT\d{8}\b/g;
|
|
14
14
|
const PMID_RE = /\bPMID[:\s]?(\d{6,9})\b/g;
|
|
15
15
|
// Legacy inline format: `dossier.path.foo` written directly in prose, no
|
|
@@ -19,6 +19,7 @@ import { z } from "zod";
|
|
|
19
19
|
import { withHost } from "../../../lib/host-concurrency.js";
|
|
20
20
|
import { tryQuery, tryMutation } from "../../../lib/db-result.js";
|
|
21
21
|
import { offTargetAnnotatorPrompt } from "../../../prompts/target-assessment/off-target-annotator.js";
|
|
22
|
+
import { composeSystemPrompt } from "../../../agents/system-prompt.js";
|
|
22
23
|
import { forSubAgent } from "../../../auth/types.js";
|
|
23
24
|
import { finalText, runAgent } from "../../../loop/run-agent.js";
|
|
24
25
|
import { passthroughStep } from "../../../loop/run-step.js";
|
|
@@ -93,7 +94,7 @@ const ANNOTATOR_AGENT_ID = "off-target-annotator";
|
|
|
93
94
|
function buildAnnotatorAgent(model) {
|
|
94
95
|
return {
|
|
95
96
|
id: ANNOTATOR_AGENT_ID,
|
|
96
|
-
systemPrompt: offTargetAnnotatorPrompt,
|
|
97
|
+
systemPrompt: composeSystemPrompt(offTargetAnnotatorPrompt),
|
|
97
98
|
model,
|
|
98
99
|
tools: [],
|
|
99
100
|
maxIterations: 2,
|
|
@@ -127,8 +127,8 @@ export declare function stepWritePrefix(args: {
|
|
|
127
127
|
* in-sandbox absolute path. The tree is bind-mounted at `/{resourceId}`, so
|
|
128
128
|
* the sandbox path is the resource id plus the root-relative tail — the host
|
|
129
129
|
* location of the root never leaks into the container. Single source of truth
|
|
130
|
-
* for the `execute_command` / `write_file` cwd and the
|
|
131
|
-
*
|
|
130
|
+
* for the `execute_command` / `write_file` cwd and for the working directory
|
|
131
|
+
* a step briefing names (see the harness-workspace-tools spec).
|
|
132
132
|
*/
|
|
133
133
|
export declare function toSandboxPath(workspaceRoot: string, resourceId: string, hostAbsPath: string): string;
|
|
134
134
|
/**
|
package/dist/workspace/paths.js
CHANGED
|
@@ -101,8 +101,8 @@ export function stepWritePrefix(args) {
|
|
|
101
101
|
* in-sandbox absolute path. The tree is bind-mounted at `/{resourceId}`, so
|
|
102
102
|
* the sandbox path is the resource id plus the root-relative tail — the host
|
|
103
103
|
* location of the root never leaks into the container. Single source of truth
|
|
104
|
-
* for the `execute_command` / `write_file` cwd and the
|
|
105
|
-
*
|
|
104
|
+
* for the `execute_command` / `write_file` cwd and for the working directory
|
|
105
|
+
* a step briefing names (see the harness-workspace-tools spec).
|
|
106
106
|
*/
|
|
107
107
|
export function toSandboxPath(workspaceRoot, resourceId, hostAbsPath) {
|
|
108
108
|
const tail = relativePath(workspaceRoot, hostAbsPath).split(sep).join("/");
|