@inflexa-ai/harness 0.46.2 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/agents/conversation-agent.d.ts +7 -6
- package/dist/agents/conversation-agent.js +26 -4
- package/dist/agents/report-session-agent.d.ts +14 -14
- package/dist/agents/report-session-agent.js +40 -26
- package/dist/agents/sandbox/shared.d.ts +18 -13
- package/dist/agents/sandbox/shared.js +42 -27
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/sandbox-catalog.d.ts +2 -0
- package/dist/agents/sandbox-catalog.js +30 -1
- package/dist/app/synthesize-run.d.ts +10 -6
- package/dist/app/synthesize-run.js +4 -2
- package/dist/contracts/analogy-report.d.ts +46 -2
- package/dist/contracts/analogy-report.js +31 -25
- package/dist/contracts/chat-parts.d.ts +3 -14
- package/dist/contracts/report-reference.d.ts +2 -1
- package/dist/contracts/report-reference.js +2 -1
- package/dist/contracts/schemas/chat-parts.d.ts +0 -27
- package/dist/contracts/schemas/chat-parts.js +1 -3
- package/dist/execution/run-canceler.d.ts +2 -0
- package/dist/execution/run-canceler.js +9 -0
- package/dist/execution/run-synthesis.d.ts +36 -8
- package/dist/execution/run-synthesis.js +114 -32
- package/dist/index.d.ts +5 -6
- package/dist/index.js +5 -7
- package/dist/input-scan/enrich.d.ts +5 -10
- package/dist/input-scan/enrich.js +12 -8
- package/dist/lib/hooks.d.ts +1 -1
- package/dist/lib/markdown-pdf.d.ts +27 -0
- package/dist/lib/markdown-pdf.js +236 -0
- package/dist/lib/metrics.d.ts +31 -0
- package/dist/lib/metrics.js +64 -0
- package/dist/lib/pdf-text.d.ts +23 -0
- package/dist/lib/pdf-text.js +34 -0
- package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +3 -5
- package/dist/lib/regulatory-corpus/fetch-ich.js +3 -5
- package/dist/loop/chat-stream.js +2 -1
- package/dist/loop/run-loop.d.ts +5 -0
- package/dist/loop/run-loop.js +30 -2
- package/dist/loop/tool-name-repair.d.ts +11 -0
- package/dist/loop/tool-name-repair.js +23 -0
- package/dist/loop/types.d.ts +19 -0
- package/dist/memory/card-builders.d.ts +1 -1
- package/dist/prompts/SOUL.d.ts +6 -6
- package/dist/prompts/SOUL.js +22 -121
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +35 -101
- package/dist/prompts/briefing.d.ts +20 -11
- package/dist/prompts/briefing.js +36 -19
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +103 -383
- package/dist/prompts/planner.d.ts +1 -5
- package/dist/prompts/planner.js +45 -128
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +29 -58
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +3 -3
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +12 -47
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +4 -5
- package/dist/prompts/sandbox/data-profiler.js +11 -34
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +3 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +23 -73
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +3 -1
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +3 -4
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +5 -4
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +3 -1
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +3 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +3 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +3 -1
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +3 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +4 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +3 -1
- package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
- package/dist/prompts/sandbox/scientific-executor.js +0 -12
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +3 -1
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +4 -4
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +10 -29
- package/dist/prompts/sandbox-standards.d.ts +15 -18
- package/dist/prompts/sandbox-standards.js +96 -305
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +24 -90
- package/dist/provenance/collector.d.ts +2 -5
- package/dist/provenance/collector.js +2 -5
- package/dist/provenance/seam.d.ts +36 -1
- package/dist/provenance/workspace-reads.d.ts +36 -0
- package/dist/provenance/workspace-reads.js +33 -0
- package/dist/report-model/production-resolver.d.ts +22 -52
- package/dist/report-model/production-resolver.js +43 -112
- package/dist/report-render/chart-export.d.ts +9 -0
- package/dist/report-render/chart-export.js +1 -1
- package/dist/report-render/views/chart-view.d.ts +1 -0
- package/dist/report-render/views/chart-view.js +1 -0
- package/dist/report-render/views/lineage.d.ts +1 -0
- package/dist/report-render/views/page-view.d.ts +1 -0
- package/dist/report-render/views/page-view.js +1 -0
- package/dist/report-render/views/prose.d.ts +1 -0
- package/dist/report-render/views/prose.js +1 -0
- package/dist/report-render/views/references-view.d.ts +1 -0
- package/dist/report-render/views/values.d.ts +1 -0
- package/dist/report-render/views/values.js +1 -0
- package/dist/runtime/assemble.d.ts +32 -12
- package/dist/runtime/assemble.js +49 -38
- package/dist/sandbox/client.d.ts +5 -3
- package/dist/sandbox/create-sandbox.d.ts +7 -12
- package/dist/sandbox/create-sandbox.js +15 -37
- package/dist/sandbox/docker-client.d.ts +1 -3
- package/dist/sandbox/docker-client.js +3 -5
- package/dist/sandbox/exec-outcome.d.ts +11 -11
- package/dist/sandbox/exec-outcome.js +22 -22
- package/dist/sandbox/exec.d.ts +5 -0
- package/dist/sandbox/exec.js +9 -1
- package/dist/sandbox/identity.d.ts +4 -16
- package/dist/sandbox/identity.js +5 -21
- package/dist/sandbox/k8s-client.d.ts +0 -1
- package/dist/sandbox/k8s-client.js +8 -13
- package/dist/sandbox/lease-names.d.ts +6 -0
- package/dist/sandbox/lease-names.js +12 -0
- package/dist/sandbox/leases.d.ts +105 -0
- package/dist/sandbox/leases.js +519 -0
- package/dist/sandbox/liveness.d.ts +5 -2
- package/dist/sandbox/mount-plan.d.ts +10 -1
- package/dist/sandbox/mount-plan.js +7 -1
- package/dist/sandbox/reconciler.d.ts +58 -0
- package/dist/sandbox/reconciler.js +153 -0
- package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
- package/dist/sandbox/types.d.ts +25 -18
- package/dist/sandbox/types.js +10 -4
- package/dist/schemas/plan-schemas.d.ts +0 -21
- package/dist/schemas/plan-schemas.js +2 -3
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/schemas/run-synthesis.js +3 -3
- package/dist/schemas/validate-plan.d.ts +1 -14
- package/dist/schemas/validate-plan.js +5 -24
- package/dist/schemas/workflow-state.d.ts +3 -32
- package/dist/schemas/workflow-state.js +4 -13
- package/dist/state/index.d.ts +2 -3
- package/dist/state/index.js +1 -2
- package/dist/state/migrations/20261030120000_lease_the_sandboxes.d.ts +7 -0
- package/dist/state/migrations/20261030120000_lease_the_sandboxes.js +34 -0
- package/dist/state/migrations/20261030120100_drop_the_report_derivations.d.ts +5 -0
- package/dist/state/migrations/20261030120100_drop_the_report_derivations.js +9 -0
- package/dist/state/migrations/20261030120200_keep_the_report_derivations.d.ts +6 -0
- package/dist/state/migrations/20261030120200_keep_the_report_derivations.js +10 -0
- package/dist/state/migrations/index.js +6 -0
- package/dist/state/purge-analysis.d.ts +4 -2
- package/dist/state/purge-analysis.js +22 -11
- package/dist/state/sandbox-leases.d.ts +81 -0
- package/dist/state/sandbox-leases.js +247 -0
- package/dist/state/schema.d.ts +1 -13
- package/dist/state/schema.js +1 -6
- package/dist/state/step-executions.d.ts +13 -2
- package/dist/state/step-executions.js +23 -4
- package/dist/tasks/data-profile.d.ts +40 -2
- package/dist/tasks/data-profile.js +89 -87
- package/dist/tools/ad-hoc-router.d.ts +1 -6
- package/dist/tools/ad-hoc-router.js +5 -48
- package/dist/tools/bio/comptox.js +1 -1
- package/dist/tools/bio/drug-gene-interactions.js +1 -1
- package/dist/tools/bio/gene-disease-evidence.js +1 -1
- package/dist/tools/bio/keys.d.ts +1 -1
- package/dist/tools/bio/pubmed.js +31 -0
- package/dist/tools/bio/search-gene.js +17 -0
- package/dist/tools/bio/search-geo-datasets.js +13 -0
- package/dist/tools/bio/search-regulatory-approvals.js +1 -1
- package/dist/tools/define-tool.d.ts +25 -2
- package/dist/tools/define-tool.js +11 -1
- package/dist/tools/display/show-user.js +1 -1
- package/dist/tools/execute-analysis.d.ts +1 -3
- package/dist/tools/execute-analysis.js +5 -33
- package/dist/tools/report-authoring/authoring-tools.d.ts +2 -2
- package/dist/tools/report-authoring/authoring-tools.js +31 -6
- package/dist/tools/report-session/index.d.ts +0 -1
- package/dist/tools/report-session/index.js +0 -1
- package/dist/tools/report-session/script-tools.d.ts +33 -0
- package/dist/tools/report-session/script-tools.js +186 -0
- package/dist/tools/research/generate-analogy-report.d.ts +16 -9
- package/dist/tools/research/generate-analogy-report.js +111 -49
- package/dist/tools/research/generate-plan.d.ts +102 -7
- package/dist/tools/research/generate-plan.js +74 -43
- package/dist/tools/research/index.d.ts +4 -3
- package/dist/tools/research/index.js +4 -3
- package/dist/tools/research/inspect-run.d.ts +0 -1
- package/dist/tools/research/inspect-run.js +5 -10
- package/dist/tools/research/search-arxiv.js +15 -0
- package/dist/tools/research/search-github-repos.js +13 -0
- package/dist/tools/research/search-semantic-scholar.js +16 -0
- package/dist/tools/research/synthesis-lookups.d.ts +21 -0
- package/dist/tools/research/synthesis-lookups.js +29 -0
- package/dist/tools/research/web-fetch.d.ts +1 -1
- package/dist/tools/research/web-fetch.js +14 -12
- package/dist/tools/sandbox/lease-tools.d.ts +32 -0
- package/dist/tools/sandbox/lease-tools.js +60 -0
- package/dist/tools/script/archive.d.ts +34 -0
- package/dist/tools/script/archive.js +182 -0
- package/dist/tools/script/codemode-worker.d.ts +5 -0
- package/dist/tools/script/codemode-worker.js +5 -0
- package/dist/tools/script/file-tools.d.ts +106 -0
- package/dist/tools/script/file-tools.js +491 -0
- package/dist/tools/script/metrics.d.ts +30 -0
- package/dist/tools/script/metrics.js +53 -0
- package/dist/tools/script/quickjs.wasm +0 -0
- package/dist/tools/script/run-script.d.ts +76 -0
- package/dist/tools/script/run-script.js +413 -0
- package/dist/tools/script/scratch.d.ts +84 -0
- package/dist/tools/script/scratch.js +145 -0
- package/dist/tools/start-report-session.js +1 -1
- package/dist/tools/workspace/edit-file.js +2 -1
- package/dist/tools/workspace/execute-command.d.ts +9 -13
- package/dist/tools/workspace/execute-command.js +23 -19
- package/dist/tools/workspace/file-stat.js +1 -1
- package/dist/tools/workspace/grep.d.ts +5 -2
- package/dist/tools/workspace/grep.js +19 -10
- package/dist/tools/workspace/mutator.d.ts +51 -4
- package/dist/tools/workspace/mutator.js +54 -13
- package/dist/tools/workspace/read-file.d.ts +5 -2
- package/dist/tools/workspace/read-file.js +15 -11
- package/dist/tools/workspace/read-surface.d.ts +17 -0
- package/dist/tools/workspace/read-surface.js +17 -0
- package/dist/tools/workspace/result-bounds.d.ts +1 -0
- package/dist/tools/workspace/run-exec.d.ts +12 -13
- package/dist/tools/workspace/run-exec.js +13 -13
- package/dist/tools/workspace/scan-inputs.d.ts +2 -5
- package/dist/tools/workspace/scan-inputs.js +2 -3
- package/dist/tools/workspace/show-file.js +1 -1
- package/dist/tools/workspace/show-plan.js +4 -3
- package/dist/tools/workspace/write-file.js +2 -1
- package/dist/workflows/execute-analysis-scheduler.d.ts +0 -26
- package/dist/workflows/execute-analysis-scheduler.js +12 -32
- package/dist/workflows/execute-analysis.d.ts +17 -35
- package/dist/workflows/execute-analysis.js +13 -107
- package/dist/workflows/pause.js +29 -22
- package/dist/workflows/sandbox-step.d.ts +16 -31
- package/dist/workflows/sandbox-step.js +36 -71
- package/dist/workflows/test/sql-writes.d.ts +0 -1
- package/dist/workflows/test/sql-writes.js +0 -2
- package/dist/workspace/filesystem.d.ts +2 -2
- package/dist/workspace/filesystem.js +10 -5
- package/dist/workspace/paths.d.ts +19 -1
- package/dist/workspace/paths.js +22 -1
- package/dist/workspace/session-files.d.ts +35 -0
- package/dist/workspace/session-files.js +16 -0
- package/package.json +7 -3
- package/dist/prompts/literature-reviewer.d.ts +0 -1
- package/dist/prompts/literature-reviewer.js +0 -114
- package/dist/sandbox/reaper.d.ts +0 -81
- package/dist/sandbox/reaper.js +0 -161
- package/dist/sandbox/step-sandbox.d.ts +0 -47
- package/dist/sandbox/step-sandbox.js +0 -53
- package/dist/state/active-sandboxes.d.ts +0 -33
- package/dist/state/active-sandboxes.js +0 -65
- package/dist/tasks/extract-values-script.d.ts +0 -64
- package/dist/tasks/extract-values-script.js +0 -180
- package/dist/tasks/extract-values.d.ts +0 -77
- package/dist/tasks/extract-values.js +0 -146
- package/dist/tools/report-session/derive-table.d.ts +0 -164
- package/dist/tools/report-session/derive-table.js +0 -419
- package/dist/tools/research/literature-reviewer.d.ts +0 -42
- package/dist/tools/research/literature-reviewer.js +0 -107
- package/dist/workflows/metrics.d.ts +0 -23
- package/dist/workflows/metrics.js +0 -38
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `cortex_step_executions` operations — step lifecycle and retry telemetry.
|
|
3
|
-
* The `sandbox_ref` column is read/written by the sibling `active-sandboxes`
|
|
4
|
-
* module.
|
|
5
3
|
*/
|
|
6
4
|
import type { ResultAsync } from "neverthrow";
|
|
7
5
|
import type { QueryConfig } from "pg";
|
|
@@ -96,3 +94,16 @@ export declare function sweepPendingStepExecutions(pool: Querier, runId: string)
|
|
|
96
94
|
*/
|
|
97
95
|
export declare function cancelRunningStepExecutions(pool: Querier, runId: string, reason: string): ResultAsync<void, DbError>;
|
|
98
96
|
export declare function queryStepsByRun(pool: StepRowQuerier, runId: string): ResultAsync<StepExecutionRow[], DbError>;
|
|
97
|
+
/** The step counts of one run, from {@link countStepsByRuns}. */
|
|
98
|
+
export type RunStepCounts = {
|
|
99
|
+
runId: string;
|
|
100
|
+
doneSteps: number;
|
|
101
|
+
runningSteps: number;
|
|
102
|
+
totalSteps: number;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The step counts of each run of a list, in one statement. `totalSteps` counts each row, the `synthesis` row
|
|
106
|
+
* included, and `doneSteps` counts the `completed` rows: the rule of the analysis summary reads. A step that
|
|
107
|
+
* waits counts in `totalSteps` only. A run with no row has no entry, and a caller reads it as 0.
|
|
108
|
+
*/
|
|
109
|
+
export declare function countStepsByRuns(pool: Querier, analysisId: string, runIds: readonly string[]): ResultAsync<RunStepCounts[], DbError>;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `cortex_step_executions` operations — step lifecycle and retry telemetry.
|
|
3
|
-
* The `sandbox_ref` column is read/written by the sibling `active-sandboxes`
|
|
4
|
-
* module.
|
|
5
3
|
*/
|
|
6
4
|
import { tryMutation, tryQuery } from "../lib/db-result.js";
|
|
7
5
|
import { StepExecutionRowSchema } from "./schema.js";
|
|
@@ -165,7 +163,7 @@ export function queryStepsByRun(pool, runId) {
|
|
|
165
163
|
text: `SELECT run_id, step_id, analysis_id, wave, agent_id,
|
|
166
164
|
status, started_at, completed_at, duration_ms, error,
|
|
167
165
|
attempts, last_error_class, finish_reason, hit_max_steps,
|
|
168
|
-
blocked_reason,
|
|
166
|
+
blocked_reason, child_workflow_id
|
|
169
167
|
FROM cortex_step_executions
|
|
170
168
|
WHERE run_id = $1
|
|
171
169
|
ORDER BY wave, started_at NULLS LAST, step_id`,
|
|
@@ -174,6 +172,28 @@ export function queryStepsByRun(pool, runId) {
|
|
|
174
172
|
return result.rows.map(mapStepExecutionRow);
|
|
175
173
|
});
|
|
176
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* The step counts of each run of a list, in one statement. `totalSteps` counts each row, the `synthesis` row
|
|
177
|
+
* included, and `doneSteps` counts the `completed` rows: the rule of the analysis summary reads. A step that
|
|
178
|
+
* waits counts in `totalSteps` only. A run with no row has no entry, and a caller reads it as 0.
|
|
179
|
+
*/
|
|
180
|
+
export function countStepsByRuns(pool, analysisId, runIds) {
|
|
181
|
+
return tryQuery("stepExecutions.countStepsByRuns", async () => {
|
|
182
|
+
if (runIds.length === 0)
|
|
183
|
+
return [];
|
|
184
|
+
const result = await pool.query({
|
|
185
|
+
text: `SELECT run_id,
|
|
186
|
+
COUNT(*) FILTER (WHERE status = 'completed')::int AS done,
|
|
187
|
+
COUNT(*) FILTER (WHERE status = 'running')::int AS running,
|
|
188
|
+
COUNT(*)::int AS total
|
|
189
|
+
FROM cortex_step_executions
|
|
190
|
+
WHERE analysis_id = $1 AND run_id = ANY($2::text[])
|
|
191
|
+
GROUP BY run_id`,
|
|
192
|
+
values: [analysisId, [...runIds]],
|
|
193
|
+
});
|
|
194
|
+
return result.rows.map((r) => ({ runId: r.run_id, doneSteps: r.done, runningSteps: r.running, totalSteps: r.total }));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
177
197
|
function mapStepExecutionRow(row) {
|
|
178
198
|
return StepExecutionRowSchema.parse({
|
|
179
199
|
runId: row.run_id,
|
|
@@ -192,7 +212,6 @@ function mapStepExecutionRow(row) {
|
|
|
192
212
|
finishReason: row.finish_reason ?? null,
|
|
193
213
|
hitMaxSteps: row.hit_max_steps === 1 || row.hit_max_steps === true,
|
|
194
214
|
blockedReason: row.blocked_reason ?? null,
|
|
195
|
-
sandboxRef: row.sandbox_ref ?? null,
|
|
196
215
|
childWorkflowId: row.child_workflow_id ?? null,
|
|
197
216
|
});
|
|
198
217
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* triggers dedup via the ledger CAS (only one `tryStart/tryRerun/tryRetry`
|
|
18
18
|
* UPDATE wins); each winning attempt starts a workflow under a per-attempt id.
|
|
19
19
|
*/
|
|
20
|
+
import type { ScriptRuntime } from "../tools/script/run-script.js";
|
|
20
21
|
import { ResultAsync } from "neverthrow";
|
|
21
22
|
import { type AuthContext, type RunSession } from "../auth/types.js";
|
|
22
23
|
import type { EnvironmentStorePaths } from "../config/environment-stores.js";
|
|
@@ -32,13 +33,46 @@ import type { Querier } from "../state/db.js";
|
|
|
32
33
|
import { type GateRefusal, type NoticeFailure } from "../lib/hooks.js";
|
|
33
34
|
import type { Logger } from "../lib/logger.js";
|
|
34
35
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
36
|
+
import { type Tool } from "../tools/define-tool.js";
|
|
35
37
|
import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
|
|
36
|
-
import type {
|
|
38
|
+
import type { ResourceSpec } from "../config/resource-limits.js";
|
|
39
|
+
import type { SandboxLeases } from "../sandbox/leases.js";
|
|
37
40
|
import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
|
|
38
41
|
import { type ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
42
|
+
import { z } from "zod";
|
|
39
43
|
import { type ProfileSubmission } from "../schemas/data-profile-schemas.js";
|
|
44
|
+
import { type SetMenu } from "../input-scan/menu.js";
|
|
45
|
+
import type { DetectedSets } from "../input-scan/set-types.js";
|
|
46
|
+
import { type AbsorbOutcome } from "./data-profile-absorb.js";
|
|
40
47
|
import { type ProfileResolution } from "./data-profile-resolve.js";
|
|
41
48
|
import { type DataProfileResult } from "../state/index.js";
|
|
49
|
+
/** What one call of `submit_profile` tells the agent. */
|
|
50
|
+
declare const SubmitOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
51
|
+
status: z.ZodLiteral<"needs_repair">;
|
|
52
|
+
detail: z.ZodString;
|
|
53
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
54
|
+
status: z.ZodLiteral<"accepted">;
|
|
55
|
+
groups: z.ZodNumber;
|
|
56
|
+
keptFiles: z.ZodNumber;
|
|
57
|
+
unclassifiedFiles: z.ZodNumber;
|
|
58
|
+
unresolvedOperations: z.ZodNumber;
|
|
59
|
+
}, z.core.$strip>], "status">;
|
|
60
|
+
type SubmitOutcome = z.infer<typeof SubmitOutcomeSchema>;
|
|
61
|
+
/**
|
|
62
|
+
* The first message of the profiler. No scheduler composes a briefing for it, thus this message is its briefing: the
|
|
63
|
+
* system prompt names no paths, because it is static for each agent type. The workspace frame and the resource budget
|
|
64
|
+
* use the same words as the step briefing.
|
|
65
|
+
*/
|
|
66
|
+
export declare function composeProfilerSeed(args: {
|
|
67
|
+
readonly analysisId: string;
|
|
68
|
+
/** The in-sandbox working directory of the profiler. */
|
|
69
|
+
readonly workingDir: string;
|
|
70
|
+
readonly resources: ResourceSpec;
|
|
71
|
+
readonly menu: SetMenu;
|
|
72
|
+
readonly absorb: AbsorbOutcome;
|
|
73
|
+
}): string;
|
|
74
|
+
/** The terminal tool of the profiler: it resolves the operations of a submission against the scan and the menu. */
|
|
75
|
+
export declare function createSubmitProfileTool(detected: DetectedSets, menu: SetMenu): Tool<ProfileSubmission, SubmitOutcome>;
|
|
42
76
|
/** The settle of one profile claim: the ledger row left `running`, and the terminal write stamped it. */
|
|
43
77
|
export type DataProfileSettlement = {
|
|
44
78
|
readonly analysisId: string;
|
|
@@ -54,7 +88,8 @@ export type DataProfileDeps = EnvironmentStorePaths & {
|
|
|
54
88
|
readonly logger?: Logger;
|
|
55
89
|
readonly provider: ChatProvider;
|
|
56
90
|
readonly pool: Querier;
|
|
57
|
-
|
|
91
|
+
/** The sandbox module. The profile acquires its lease from it, and its agent runs commands on that lease. */
|
|
92
|
+
readonly leases: SandboxLeases;
|
|
58
93
|
readonly workspaceFs: WorkspaceFilesystem;
|
|
59
94
|
/** Workspace-root resolution seam (see workspace/paths.ts). */
|
|
60
95
|
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
@@ -90,6 +125,8 @@ export type DataProfileDeps = EnvironmentStorePaths & {
|
|
|
90
125
|
readonly notifyDataProfileSettled?: NotifyDataProfileSettled;
|
|
91
126
|
/** The delays of the timer of an outage. Absent: the default of the pause. */
|
|
92
127
|
readonly pauseRetryDelaysMs?: readonly number[];
|
|
128
|
+
/** Where the VM of `run_script` comes from, for the lookups of the profiler. */
|
|
129
|
+
readonly scriptRuntime?: ScriptRuntime;
|
|
93
130
|
};
|
|
94
131
|
/**
|
|
95
132
|
* Workflow input. JSON-serialisable — DBOS persists it as the workflow's
|
|
@@ -270,3 +307,4 @@ export type DataProfileStopDeps = {
|
|
|
270
307
|
* teardown of the sandbox stay in the body, which runs them on its way out. The `running` guard of the row write keeps a profile that settled first.
|
|
271
308
|
*/
|
|
272
309
|
export declare function stopDataProfile(deps: DataProfileStopDeps, analysisId: string, workflowId: string): Promise<void>;
|
|
310
|
+
export {};
|
|
@@ -27,21 +27,19 @@ import { forStep, forSubAgent } from "../auth/types.js";
|
|
|
27
27
|
import { presentFirst } from "../lib/present.js";
|
|
28
28
|
import { collapseWhitespace } from "../lib/text.js";
|
|
29
29
|
import { createDataProfilerAgent } from "../agents/sandbox/data-profiler.js";
|
|
30
|
-
import { runWithSalvage } from "../loop/run-loop.js";
|
|
30
|
+
import { runWithSalvage, withRunScript } from "../loop/run-loop.js";
|
|
31
31
|
import { recordedInputOf } from "../loop/tool-adapter.js";
|
|
32
32
|
import { createNoopLogger } from "../lib/console-logger.js";
|
|
33
33
|
import { deliverNotice, passGate } from "../lib/hooks.js";
|
|
34
34
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
35
35
|
import { defineTool } from "../tools/define-tool.js";
|
|
36
36
|
import { createDetailResolver } from "../tools/detail-resolver.js";
|
|
37
|
-
import { renderWorkspace } from "../prompts/briefing.js";
|
|
38
|
-
import { keepSuspendingRefusal, SandboxFailure } from "../sandbox/sandbox-error.js";
|
|
37
|
+
import { renderResources, renderWorkspace } from "../prompts/briefing.js";
|
|
39
38
|
import { workPause } from "../workflows/pause.js";
|
|
40
39
|
import { clearPausesOfWork } from "../state/paused-work.js";
|
|
41
|
-
import { toSandboxPath } from "../workspace/paths.js";
|
|
40
|
+
import { STAGED_INPUT_ROOT, toSandboxPath } from "../workspace/paths.js";
|
|
42
41
|
import { estimateDataProfileResources } from "../sandbox/estimate-data-profile-resources.js";
|
|
43
42
|
import { generateExecutionId } from "../sandbox/execution-id.js";
|
|
44
|
-
import { mintSandboxIdentity } from "../sandbox/identity.js";
|
|
45
43
|
import { createVectorStore } from "../state/vector-store.js";
|
|
46
44
|
import { z } from "zod";
|
|
47
45
|
import { ProfileSubmissionSchema } from "../schemas/data-profile-schemas.js";
|
|
@@ -63,12 +61,6 @@ import { ensureSearchIndex, searchIndexName } from "../workspace/search-config.j
|
|
|
63
61
|
import { DATA_PROFILE_RUN_LITERAL } from "../contracts/data-profile.js";
|
|
64
62
|
const DATA_PROFILE_STEP_LITERAL = "profile";
|
|
65
63
|
const DATA_PROFILE_AGENT_ID = "data-profiler";
|
|
66
|
-
/**
|
|
67
|
-
* Sandbox-server exec budget for the profile run. Twenty minutes: the deterministic
|
|
68
|
-
* scan bounds the discovery half, and what remains is the agent's own reading of a
|
|
69
|
-
* bounded number of example files.
|
|
70
|
-
*/
|
|
71
|
-
const DEFAULT_DEADLINE_MS = 1_200_000;
|
|
72
64
|
/** Index entries embedded and upserted per round trip. */
|
|
73
65
|
const INDEX_BATCH_SIZE = 256;
|
|
74
66
|
const SUBMIT_PROFILE_TOOL_ID = "submit_profile";
|
|
@@ -83,6 +75,56 @@ const SubmitOutcomeSchema = z.discriminatedUnion("status", [
|
|
|
83
75
|
unresolvedOperations: z.number(),
|
|
84
76
|
}),
|
|
85
77
|
]);
|
|
78
|
+
/**
|
|
79
|
+
* The first message of the profiler. No scheduler composes a briefing for it, thus this message is its briefing: the
|
|
80
|
+
* system prompt names no paths, because it is static for each agent type. The workspace frame and the resource budget
|
|
81
|
+
* use the same words as the step briefing.
|
|
82
|
+
*/
|
|
83
|
+
export function composeProfilerSeed(args) {
|
|
84
|
+
return [
|
|
85
|
+
`Profile the input data for this analysis: say what the dataset IS, so planning can proceed.`,
|
|
86
|
+
``,
|
|
87
|
+
renderWorkspace({ analysisId: args.analysisId, workingDir: args.workingDir }),
|
|
88
|
+
``,
|
|
89
|
+
renderResources(args.resources),
|
|
90
|
+
``,
|
|
91
|
+
renderSetMenu(args.menu, STAGED_INPUT_ROOT),
|
|
92
|
+
``,
|
|
93
|
+
...(args.absorb.kind === "partial"
|
|
94
|
+
? [renderAbsorbDelta(args.absorb), ``]
|
|
95
|
+
: [
|
|
96
|
+
`Author the dataset's groups as operations on this menu: \`use\` a set, \`split\` one by a slot or by a`,
|
|
97
|
+
`value mapping, \`merge\` several, or \`group\` explicit paths the scan left over. Membership and counts`,
|
|
98
|
+
`are computed from your operations — you do not state them. Inspect ONE example file per group where a`,
|
|
99
|
+
`description needs content the scan did not capture, then deliver the groups with \`submit_profile\`.`,
|
|
100
|
+
]),
|
|
101
|
+
].join("\n");
|
|
102
|
+
}
|
|
103
|
+
/** The terminal tool of the profiler: it resolves the operations of a submission against the scan and the menu. */
|
|
104
|
+
export function createSubmitProfileTool(detected, menu) {
|
|
105
|
+
return defineTool({
|
|
106
|
+
id: SUBMIT_PROFILE_TOOL_ID,
|
|
107
|
+
description: "Submit the profiling results. Call this tool after completing all profiling work — it resolves your " +
|
|
108
|
+
"operations against the scan, computes every group's membership and counts, and checks the profile. " +
|
|
109
|
+
"A submission whose operations do not resolve, or that leaves kept files unclaimed, comes back with " +
|
|
110
|
+
"the errors; the repair is a FULL resubmit of the whole operation list. The last accepted submission is the profile.",
|
|
111
|
+
inputSchema: ProfileSubmissionSchema,
|
|
112
|
+
describeCall: "none",
|
|
113
|
+
execute: (input) => {
|
|
114
|
+
const resolution = resolveProfileSubmission(input, detected, menu, { finalRound: false });
|
|
115
|
+
if (resolution.errors.length > 0 || resolution.unclaimed.length > 0) {
|
|
116
|
+
return Promise.resolve(ok({ status: "needs_repair", detail: formatResolutionErrors(resolution) }));
|
|
117
|
+
}
|
|
118
|
+
return Promise.resolve(ok({
|
|
119
|
+
status: "accepted",
|
|
120
|
+
groups: resolution.groups.length,
|
|
121
|
+
keptFiles: resolution.partition.keptFiles,
|
|
122
|
+
unclassifiedFiles: resolution.partition.unclassifiedFiles,
|
|
123
|
+
unresolvedOperations: resolution.errors.length,
|
|
124
|
+
}));
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
86
128
|
/** The submissions of a run, in call order, with the outcome that the tool gave each one. */
|
|
87
129
|
function profileSubmissions(steps) {
|
|
88
130
|
const submissions = [];
|
|
@@ -110,8 +152,6 @@ function selectedSubmission(steps) {
|
|
|
110
152
|
const chosen = submissions.filter((entry) => entry.accepted).at(-1) ?? submissions.at(-1);
|
|
111
153
|
return chosen === undefined ? undefined : { ...chosen, count: submissions.length };
|
|
112
154
|
}
|
|
113
|
-
/** Where the embedder stages this analysis's inputs (see the data-profile-init spec). */
|
|
114
|
-
const STAGED_INPUT_ROOT = "data/inputs";
|
|
115
155
|
/** Build the canonical artifact path for a staged input file. */
|
|
116
156
|
function inputArtifactPath(f) {
|
|
117
157
|
return `data/${f.relativePath}`;
|
|
@@ -164,16 +204,14 @@ export function buildDataProfileResult(submission, resolution, stagedInputs, pro
|
|
|
164
204
|
* rests on, so failing the profile over it would trade the whole capability for a nicety.
|
|
165
205
|
*/
|
|
166
206
|
async function readSetHeaders(args) {
|
|
167
|
-
const { session, deps, analysisId, detected,
|
|
207
|
+
const { session, deps, analysisId, detected, lease, logger } = args;
|
|
168
208
|
try {
|
|
169
209
|
return await readHeaders({
|
|
170
210
|
targets: readoutTargets(detected),
|
|
171
211
|
session,
|
|
172
212
|
fs: deps.workspaceFs,
|
|
173
|
-
|
|
174
|
-
sandbox,
|
|
213
|
+
lease,
|
|
175
214
|
mountRoot: `/${analysisId}`,
|
|
176
|
-
deadlineMs,
|
|
177
215
|
emit: async () => { },
|
|
178
216
|
});
|
|
179
217
|
}
|
|
@@ -298,7 +336,7 @@ export function profilerSandboxAgentDeps(deps, step) {
|
|
|
298
336
|
return {
|
|
299
337
|
provider: deps.provider,
|
|
300
338
|
pool: deps.pool,
|
|
301
|
-
|
|
339
|
+
toolchainSource: deps.leases.toolchainSource,
|
|
302
340
|
workspaceFs: deps.workspaceFs,
|
|
303
341
|
embedding: deps.embedding,
|
|
304
342
|
model: deps.model,
|
|
@@ -309,6 +347,7 @@ export function profilerSandboxAgentDeps(deps, step) {
|
|
|
309
347
|
extendAnalysisFarm: deps.extendAnalysisFarm,
|
|
310
348
|
toolOutputStore: deps.toolOutputStore,
|
|
311
349
|
bioKeys: deps.bioKeys,
|
|
350
|
+
scriptRuntime: deps.scriptRuntime,
|
|
312
351
|
step,
|
|
313
352
|
};
|
|
314
353
|
}
|
|
@@ -458,95 +497,57 @@ export async function runDataProfileBody(input, deps) {
|
|
|
458
497
|
// from the agent loop would leave the longest wait of all unreported — the same defect the
|
|
459
498
|
// run panel's activity readout was built to remove.
|
|
460
499
|
await activity.sandboxInit();
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
500
|
+
// The lease of the profile. A refused spawn with the suspend flag pauses the lease workflow under the
|
|
501
|
+
// profile, and the acquire waits through the pause.
|
|
502
|
+
const lease = deps.leases.bind({
|
|
503
|
+
leaseKey: workflowId,
|
|
504
|
+
session: forStep(runSession, DATA_PROFILE_STEP_LITERAL),
|
|
505
|
+
work: { kind: "profile", id: workflowId },
|
|
506
|
+
emit: (part) => DBOS.writeStream("events", part),
|
|
507
|
+
});
|
|
508
|
+
const acquired = await lease.acquire(estimateDataProfileResources(stagedInputs));
|
|
509
|
+
if (acquired.kind === "refused")
|
|
510
|
+
throw new Error(acquired.reason);
|
|
511
|
+
if (acquired.kind === "failed")
|
|
512
|
+
throw new Error(`the profiler sandbox did not start: ${acquired.detail}`);
|
|
513
|
+
const resources = acquired.size;
|
|
472
514
|
try {
|
|
473
|
-
// Checkpointed clock, not `Date.now()`: one deadline bounds every exec of the
|
|
474
|
-
// profile, and a wall-clock deadline would grow on each recovery (see
|
|
475
|
-
// sandbox-step.ts, which captures its step deadline the same way).
|
|
476
|
-
const deadlineAbs = (await DBOS.now()) + DEFAULT_DEADLINE_MS;
|
|
477
515
|
const headers = await readSetHeaders({
|
|
478
516
|
session: childSession,
|
|
479
517
|
deps,
|
|
480
518
|
analysisId,
|
|
481
519
|
detected,
|
|
482
|
-
|
|
483
|
-
deadlineMs: deadlineAbs,
|
|
520
|
+
lease,
|
|
484
521
|
logger,
|
|
485
522
|
});
|
|
486
523
|
const briefingMenu = { ...menu, headers };
|
|
487
524
|
// The profiler runs outside `executeAnalysis`, so no scheduler composes a
|
|
488
525
|
// briefing for it — this prompt IS its briefing, and the sandbox system
|
|
489
526
|
// prompt names no paths (it is static per agent type, for the prompt cache).
|
|
490
|
-
// Its workspace frame therefore
|
|
491
|
-
// step briefing uses.
|
|
492
|
-
const prompt =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
``,
|
|
500
|
-
`CRITICAL: File and directory paths may contain spaces. You MUST always double-quote paths in shell commands (e.g. head "/${analysisId}/data/inputs/My Folder/file.csv"). Unquoted paths with spaces will silently break commands.`,
|
|
501
|
-
``,
|
|
502
|
-
renderSetMenu(briefingMenu, STAGED_INPUT_ROOT),
|
|
503
|
-
``,
|
|
504
|
-
...(absorb.kind === "partial"
|
|
505
|
-
? [renderAbsorbDelta(absorb), ``]
|
|
506
|
-
: [
|
|
507
|
-
`Author the dataset's groups as operations on this menu: \`use\` a set, \`split\` one by a slot or by a`,
|
|
508
|
-
`value mapping, \`merge\` several, or \`group\` explicit paths the scan left over. Membership and counts`,
|
|
509
|
-
`are computed from your operations — you do not state them. Inspect ONE example file per group where a`,
|
|
510
|
-
`description needs content the scan did not capture, then call \`submit_profile\` once.`,
|
|
511
|
-
]),
|
|
512
|
-
].join("\n");
|
|
527
|
+
// Its workspace frame and its resource budget therefore have to be stated here,
|
|
528
|
+
// in the same words the step briefing uses.
|
|
529
|
+
const prompt = composeProfilerSeed({
|
|
530
|
+
analysisId,
|
|
531
|
+
workingDir: toSandboxPath(workspaceRoot, analysisId, profileWritePrefix),
|
|
532
|
+
resources,
|
|
533
|
+
menu: briefingMenu,
|
|
534
|
+
absorb,
|
|
535
|
+
});
|
|
513
536
|
const sandboxAgentDeps = profilerSandboxAgentDeps(deps, {
|
|
514
|
-
|
|
537
|
+
lease,
|
|
515
538
|
workspaceRoot,
|
|
516
539
|
analysisId,
|
|
517
540
|
// The profiler writes Python scripts and intermediate artifacts under
|
|
518
541
|
// the synthetic step path; the post-agent `rm -rf runs/data-profile/`
|
|
519
542
|
// cleanup wipes them.
|
|
520
543
|
allowedWritePrefix: profileWritePrefix,
|
|
521
|
-
deadlineMs: () => deadlineAbs,
|
|
522
|
-
});
|
|
523
|
-
const submitProfileTool = defineTool({
|
|
524
|
-
id: SUBMIT_PROFILE_TOOL_ID,
|
|
525
|
-
description: "Submit the profiling results. Call this tool after completing all profiling work — it resolves your " +
|
|
526
|
-
"operations against the scan, computes every group's membership and counts, and checks the profile. " +
|
|
527
|
-
"A submission whose operations do not resolve, or that leaves kept files unclaimed, comes back with " +
|
|
528
|
-
"the errors; the repair is a FULL resubmit of the whole operation list. The last accepted submission is the profile.",
|
|
529
|
-
inputSchema: ProfileSubmissionSchema,
|
|
530
|
-
describeCall: "none",
|
|
531
|
-
execute: (input) => {
|
|
532
|
-
const resolution = resolveProfileSubmission(input, detected, briefingMenu, { finalRound: false });
|
|
533
|
-
if (resolution.errors.length > 0 || resolution.unclaimed.length > 0) {
|
|
534
|
-
return Promise.resolve(ok({ status: "needs_repair", detail: formatResolutionErrors(resolution) }));
|
|
535
|
-
}
|
|
536
|
-
return Promise.resolve(ok({
|
|
537
|
-
status: "accepted",
|
|
538
|
-
groups: resolution.groups.length,
|
|
539
|
-
keptFiles: resolution.partition.keptFiles,
|
|
540
|
-
unclassifiedFiles: resolution.partition.unclassifiedFiles,
|
|
541
|
-
unresolvedOperations: resolution.errors.length,
|
|
542
|
-
}));
|
|
543
|
-
},
|
|
544
544
|
});
|
|
545
|
+
const submitProfileTool = createSubmitProfileTool(detected, briefingMenu);
|
|
545
546
|
const baseAgent = createDataProfilerAgent(sandboxAgentDeps);
|
|
546
547
|
const agentDef = { ...baseAgent, tools: [...baseAgent.tools, submitProfileTool] };
|
|
547
548
|
// The profiler's own roster, `submit_profile` included, so every call it
|
|
548
549
|
// makes is phrased by the tool that was called.
|
|
549
|
-
const resolveDetail = createDetailResolver(agentDef.tools, logger);
|
|
550
|
+
const resolveDetail = createDetailResolver([...withRunScript(agentDef).tools, ...(agentDef.scriptTools ?? [])], logger);
|
|
550
551
|
const signal = new AbortController().signal;
|
|
551
552
|
// Covers the gap between a ready sandbox and the agent's first tool call, which would
|
|
552
553
|
// otherwise still read as `Starting sandbox` long after the container was up. The run
|
|
@@ -609,15 +610,16 @@ export async function runDataProfileBody(input, deps) {
|
|
|
609
610
|
// write above, the revoke — is already behind us, so any failure on the way here reaches
|
|
610
611
|
// the catch having emitted no terminal at all and reports `failed` alone. Emitted any
|
|
611
612
|
// earlier, a failing write would produce BOTH terminals and leave the fold's winner
|
|
612
|
-
// decided by arrival order. The
|
|
613
|
+
// decided by arrival order. The release below emits nothing for the same reason.
|
|
613
614
|
await activity.complete();
|
|
614
615
|
}
|
|
615
616
|
finally {
|
|
616
617
|
try {
|
|
617
|
-
await
|
|
618
|
+
await lease.release();
|
|
618
619
|
}
|
|
619
|
-
catch (
|
|
620
|
-
|
|
620
|
+
catch (releaseErr) {
|
|
621
|
+
// The owner test of the lease workflow releases a lease that this release missed.
|
|
622
|
+
logger.warn("release failed (non-fatal)", { executionId, ...logger.errorFields(releaseErr) });
|
|
621
623
|
}
|
|
622
624
|
}
|
|
623
625
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Querier } from "../state/db.js";
|
|
2
2
|
import { type AgentSession } from "../auth/types.js";
|
|
3
3
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
4
|
-
import type { ResourcePolicy, ResourceSpec } from "../config/resource-limits.js";
|
|
5
4
|
import type { Logger } from "../lib/logger.js";
|
|
6
5
|
import type { AgentRunUsage } from "../loop/metrics.js";
|
|
7
6
|
import { type ChatProvider } from "../providers/types.js";
|
|
@@ -22,7 +21,6 @@ export type DroppedPackage = {
|
|
|
22
21
|
};
|
|
23
22
|
export type AdHocRoute = {
|
|
24
23
|
readonly agentId: string;
|
|
25
|
-
readonly resources: ResourceSpec;
|
|
26
24
|
readonly rationale: string;
|
|
27
25
|
/** The capability phrase of the chosen agent, when the router copied one that the agent advertises. */
|
|
28
26
|
readonly capability?: string;
|
|
@@ -30,13 +28,12 @@ export type AdHocRoute = {
|
|
|
30
28
|
readonly packages: readonly string[];
|
|
31
29
|
/** Every entry that the validation removed, with the reason for the caveat. */
|
|
32
30
|
readonly droppedPackages: readonly DroppedPackage[];
|
|
33
|
-
readonly fallbackClass?: "timeout" | "provider_error" | "malformed" | "no_match" | "invalid_agent"
|
|
31
|
+
readonly fallbackClass?: "timeout" | "provider_error" | "malformed" | "no_match" | "invalid_agent";
|
|
34
32
|
};
|
|
35
33
|
export type AdHocRouterDeps = {
|
|
36
34
|
readonly provider: ChatProvider;
|
|
37
35
|
readonly model: string;
|
|
38
36
|
readonly pool: Querier;
|
|
39
|
-
readonly resourcePolicy?: ResourcePolicy;
|
|
40
37
|
readonly logger?: Logger;
|
|
41
38
|
/**
|
|
42
39
|
* Test seam that sets an explicit deadline. An explicit value overrides the
|
|
@@ -55,8 +52,6 @@ export type AdHocRouterDeps = {
|
|
|
55
52
|
/** Falls back to the no-op recorder when omitted. */
|
|
56
53
|
readonly usageRecorder?: UsageRecorder;
|
|
57
54
|
};
|
|
58
|
-
export declare function defaultAdHocResources(policy?: ResourcePolicy): ResourceSpec;
|
|
59
|
-
export declare function validAdHocResources(value: unknown, policy?: ResourcePolicy): ResourceSpec | null;
|
|
60
55
|
export declare function routeAdHocRequest(deps: AdHocRouterDeps, input: {
|
|
61
56
|
analysisId: string;
|
|
62
57
|
request: string;
|
|
@@ -10,7 +10,7 @@ import { createNoopLogger } from "../lib/console-logger.js";
|
|
|
10
10
|
import { toThrowable, unwrapOrThrow } from "../lib/result.js";
|
|
11
11
|
import { parseUnknownRecord } from "../lib/unknown-containers.js";
|
|
12
12
|
import { runLoop } from "../loop/run-loop.js";
|
|
13
|
-
import { packagesSection
|
|
13
|
+
import { packagesSection } from "../prompts/planner.js";
|
|
14
14
|
import { effectiveDeadlineMs } from "../providers/types.js";
|
|
15
15
|
import { formatQuery, parseQuery } from "../sandbox/package-identity.js";
|
|
16
16
|
import { loadDataProfileStatus } from "../state/index.js";
|
|
@@ -19,50 +19,15 @@ export const AD_HOC_ROUTER_AGENT_ID = "adhoc-router";
|
|
|
19
19
|
export const AD_HOC_ROUTER_TIMEOUT_MS = 10_000;
|
|
20
20
|
export const AD_HOC_FALLBACK_AGENT_ID = "scientific-executor";
|
|
21
21
|
const SUBMIT_ROUTE_TOOL_ID = "submit_route";
|
|
22
|
-
const resourcesSchema = z.object({
|
|
23
|
-
cpu: z.number().positive(),
|
|
24
|
-
memoryGb: z.number().positive(),
|
|
25
|
-
gpu: z.object({ count: z.number().int().positive() }).optional(),
|
|
26
|
-
});
|
|
27
22
|
const routeSchema = z.object({
|
|
28
23
|
agentId: z.string().optional().describe("The id of one specialist from the eligible catalog, copied exactly."),
|
|
29
24
|
capability: z.string().optional().describe("The one capability phrase of that specialist which this request uses, copied exactly from its capabilities."),
|
|
30
|
-
resources: resourcesSchema
|
|
31
|
-
.optional()
|
|
32
|
-
.describe("The sandbox resources of the step, inside the stated ranges. Omit to take the defaults; a value outside the ranges is replaced by the defaults."),
|
|
33
25
|
rationale: z.string().optional().describe("One or two sentences on why this specialist fits the request. The step agent reads it."),
|
|
34
26
|
packages: z
|
|
35
27
|
.array(z.string())
|
|
36
28
|
.optional()
|
|
37
29
|
.describe("The packages that the scripts of the step import, each as `[python:|r:]<name>[==<version>]`. An empty array is valid."),
|
|
38
30
|
});
|
|
39
|
-
export function defaultAdHocResources(policy) {
|
|
40
|
-
return policy ? { cpu: Math.min(4, policy.perStep.maxCpu), memoryGb: Math.min(8, policy.perStep.maxMemoryGb) } : { cpu: 4, memoryGb: 8 };
|
|
41
|
-
}
|
|
42
|
-
export function validAdHocResources(value, policy) {
|
|
43
|
-
const parsed = resourcesSchema.safeParse(value);
|
|
44
|
-
if (!parsed.success)
|
|
45
|
-
return null;
|
|
46
|
-
const r = parsed.data;
|
|
47
|
-
const maxCpu = policy?.perStep.maxCpu ?? 4;
|
|
48
|
-
const maxMemoryGb = policy?.perStep.maxMemoryGb ?? 8;
|
|
49
|
-
const maxGpuCount = policy?.perStep.maxGpuCount ?? 0;
|
|
50
|
-
if (r.cpu > maxCpu || r.memoryGb > maxMemoryGb || (r.gpu?.count ?? 0) > maxGpuCount)
|
|
51
|
-
return null;
|
|
52
|
-
return r;
|
|
53
|
-
}
|
|
54
|
-
function resourceBounds(policy) {
|
|
55
|
-
const defaults = defaultAdHocResources(policy);
|
|
56
|
-
if (!policy) {
|
|
57
|
-
return `CPU range 1..${defaults.cpu}; default ${defaults.cpu}. Memory range 1..${defaults.memoryGb} GB; default ${defaults.memoryGb}. GPU range 0..0.`;
|
|
58
|
-
}
|
|
59
|
-
const { perStep } = policy;
|
|
60
|
-
return [
|
|
61
|
-
`CPU range ${Math.min(1, perStep.maxCpu)}..${perStep.maxCpu}; default ${defaults.cpu}.`,
|
|
62
|
-
`Memory range ${Math.min(1, perStep.maxMemoryGb)}..${perStep.maxMemoryGb} GB; default ${defaults.memoryGb}.`,
|
|
63
|
-
`GPU range 0..${perStep.maxGpuCount}; omit gpu unless required.`,
|
|
64
|
-
].join(" ");
|
|
65
|
-
}
|
|
66
31
|
async function profileOrientation(pool, analysisId, logger) {
|
|
67
32
|
try {
|
|
68
33
|
const status = unwrapOrThrow(await loadDataProfileStatus(pool, analysisId));
|
|
@@ -85,8 +50,7 @@ function suggestedSpelling(rows) {
|
|
|
85
50
|
}
|
|
86
51
|
/**
|
|
87
52
|
* Validate the package entries of a route on their own axis: a bad entry costs
|
|
88
|
-
* that entry and nothing else, because the specialist
|
|
89
|
-
* sound without it.
|
|
53
|
+
* that entry and nothing else, because the specialist is sound without it.
|
|
90
54
|
*
|
|
91
55
|
* A guess of a model reaches here, and no person reviewed it. Thus a name that
|
|
92
56
|
* the pool does not hold leaves the step with a caveat, where the same name in
|
|
@@ -159,7 +123,7 @@ async function routePackages(raw, resolve, logger) {
|
|
|
159
123
|
function routeTool() {
|
|
160
124
|
return defineTool({
|
|
161
125
|
id: SUBMIT_ROUTE_TOOL_ID,
|
|
162
|
-
description: "Submit the single specialist
|
|
126
|
+
description: "Submit the single specialist and the packages of the step. This is the only valid response.",
|
|
163
127
|
inputSchema: routeSchema,
|
|
164
128
|
describeCall: "none",
|
|
165
129
|
execute: () => Promise.resolve(ok({ recorded: true })),
|
|
@@ -180,10 +144,8 @@ export async function routeAdHocRequest(deps, input) {
|
|
|
180
144
|
const router = {
|
|
181
145
|
id: AD_HOC_ROUTER_AGENT_ID,
|
|
182
146
|
systemPrompt: [
|
|
183
|
-
"Select exactly one specialist for a targeted one-step analysis,
|
|
147
|
+
"Select exactly one specialist for a targeted one-step analysis, and name the packages that the step imports.",
|
|
184
148
|
"Choose only from the supplied catalog. Do not create a plan, reject the request, or select scientific-executor.",
|
|
185
|
-
resourceEstimationSection(deps.resourcePolicy),
|
|
186
|
-
resourceBounds(deps.resourcePolicy),
|
|
187
149
|
packagesSection(),
|
|
188
150
|
"You hold no package census. For a name that both ecosystems hold, the language of the specialist you select and the wording of the request decide the track.",
|
|
189
151
|
"Respond only by calling submit_route.",
|
|
@@ -243,10 +205,6 @@ export async function routeAdHocRequest(deps, input) {
|
|
|
243
205
|
failure = "no_match";
|
|
244
206
|
if (!failure && !validAgent)
|
|
245
207
|
failure = "invalid_agent";
|
|
246
|
-
const validResources = validAdHocResources(candidate.resources, deps.resourcePolicy);
|
|
247
|
-
const resources = validResources ?? defaultAdHocResources(deps.resourcePolicy);
|
|
248
|
-
if (!validResources && candidate.resources !== undefined && !failure)
|
|
249
|
-
failure = "invalid_resources";
|
|
250
208
|
const rationale = typeof candidate.rationale === "string" && candidate.rationale.trim()
|
|
251
209
|
? candidate.rationale.trim()
|
|
252
210
|
: failure
|
|
@@ -262,10 +220,9 @@ export async function routeAdHocRequest(deps, input) {
|
|
|
262
220
|
// most requests use a capability that declares nothing.
|
|
263
221
|
const requestedCapability = typeof candidate.capability === "string" ? candidate.capability.trim() : "";
|
|
264
222
|
const capability = (SANDBOX_AGENT_META[agentId]?.capabilities ?? []).includes(requestedCapability) ? requestedCapability : undefined;
|
|
265
|
-
logger.info("ad hoc route selected", { agentId,
|
|
223
|
+
logger.info("ad hoc route selected", { agentId, rationale, failure, packages, droppedPackages, capability });
|
|
266
224
|
return {
|
|
267
225
|
agentId,
|
|
268
|
-
resources,
|
|
269
226
|
rationale,
|
|
270
227
|
packages,
|
|
271
228
|
droppedPackages,
|
|
@@ -107,7 +107,7 @@ export function createComptoxTool(deps) {
|
|
|
107
107
|
"products). See `dataset` for what each returns; all four resolve `query` to a DTXSID first.\n" +
|
|
108
108
|
"ACCEPTED IDENTIFIERS, for every dataset: a DTXSID ('DTXSID7020182') used directly, or a CASRN ('80-05-7'), a chemical name " +
|
|
109
109
|
"('bisphenol A') or an InChIKey, each resolved by EXACT match.\n" +
|
|
110
|
-
"Requires EPA_CCTE_API_KEY — a missing key fails terminally: do NOT retry
|
|
110
|
+
"Requires EPA_CCTE_API_KEY — a missing key fails terminally: do NOT retry; state that the key is not configured, and proceed without EPA data.\n" +
|
|
111
111
|
"found: false means the query did not resolve to a chemical; a present-but-empty section is likewise valid no-data. Do not retry either.\n" +
|
|
112
112
|
"This is environmental/industrial-chemical data: for drug-like compounds prefer PubChem or ChEMBL — dataset 'chemical' returns " +
|
|
113
113
|
"`detail.pubchemCid` to bridge back.",
|
|
@@ -115,7 +115,7 @@ export function createDrugGeneInteractionsTool(deps) {
|
|
|
115
115
|
"Verbose fields (`includeAttributes`, `includeDrugRecord`) are off by default — both cost a lot of context and are rarely what the question needs.\n" +
|
|
116
116
|
"`perSource` gives the outcome of each corpus, and a conclusion that an interaction is unknown rests on it: 'no_data' means that corpus " +
|
|
117
117
|
"has nothing; 'unavailable' means it could " +
|
|
118
|
-
"not be reached (a missing DRUGBANK_API_KEY lands here —
|
|
118
|
+
"not be reached (a missing DRUGBANK_API_KEY lands here — state that the key is missing, and proceed with DGIdb). A `found: false` entry means the identifier " +
|
|
119
119
|
"is not in DGIdb at all, usually a non-HUGO symbol or a brand name. Neither is worth retrying unchanged.",
|
|
120
120
|
inputSchema,
|
|
121
121
|
describeCall: "none",
|
|
@@ -167,7 +167,7 @@ export function createGeneDiseaseEvidenceTool(deps) {
|
|
|
167
167
|
"no_data there ('low density lipoprotein cholesterol measurement' is the label) while the other corpora still answer it.\n" +
|
|
168
168
|
"`perSource` gives the outcome of each corpus, and a conclusion of absence rests on it. 'no_data' means that corpus genuinely has nothing; " +
|
|
169
169
|
"'unavailable' means it " +
|
|
170
|
-
"could not be reached (a missing DISGENET_API_KEY lands here —
|
|
170
|
+
"could not be reached (a missing DISGENET_API_KEY lands here — state that the key is missing, and proceed with the others); 'not_applicable' means that " +
|
|
171
171
|
"corpus has no lookup for this queryType. Only 'no_data' is evidence of absence, and none is worth retrying unchanged.",
|
|
172
172
|
inputSchema,
|
|
173
173
|
describeCall: "none",
|
package/dist/tools/bio/keys.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Tool } from "../define-tool.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* API keys for the external bio/chem data sources. Threaded from the
|
|
5
5
|
* composition root to every place that assembles bio tools (conversation
|
|
6
|
-
* agent, sandbox agents,
|
|
6
|
+
* agent, sandbox agents, run synthesizer, analogy reporter). Each key
|
|
7
7
|
* may be empty when its source is unconfigured — the tool surfaces the
|
|
8
8
|
* resulting auth failure as a normal tool error.
|
|
9
9
|
*/
|