@inflexa-ai/harness 0.46.2 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agents/conversation-agent.d.ts +3 -8
- package/dist/agents/conversation-agent.js +24 -4
- package/dist/agents/report-session-agent.d.ts +11 -14
- package/dist/agents/report-session-agent.js +30 -17
- package/dist/agents/sandbox/shared.d.ts +18 -13
- package/dist/agents/sandbox/shared.js +42 -27
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/sandbox-catalog.d.ts +2 -0
- package/dist/agents/sandbox-catalog.js +30 -1
- package/dist/app/synthesize-run.d.ts +10 -6
- package/dist/app/synthesize-run.js +4 -2
- package/dist/contracts/analogy-report.d.ts +46 -2
- package/dist/contracts/analogy-report.js +31 -25
- package/dist/contracts/chat-parts.d.ts +3 -14
- package/dist/contracts/report-reference.d.ts +2 -1
- package/dist/contracts/report-reference.js +2 -1
- package/dist/contracts/schemas/chat-parts.d.ts +0 -27
- package/dist/contracts/schemas/chat-parts.js +1 -3
- package/dist/execution/run-canceler.d.ts +2 -0
- package/dist/execution/run-canceler.js +9 -0
- package/dist/execution/run-synthesis.d.ts +36 -8
- package/dist/execution/run-synthesis.js +114 -32
- package/dist/index.d.ts +2 -5
- package/dist/index.js +3 -6
- package/dist/input-scan/enrich.d.ts +5 -10
- package/dist/input-scan/enrich.js +12 -8
- package/dist/lib/markdown-pdf.d.ts +27 -0
- package/dist/lib/markdown-pdf.js +236 -0
- package/dist/lib/metrics.d.ts +31 -0
- package/dist/lib/metrics.js +64 -0
- package/dist/lib/pdf-text.d.ts +23 -0
- package/dist/lib/pdf-text.js +34 -0
- package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +3 -5
- package/dist/lib/regulatory-corpus/fetch-ich.js +3 -5
- package/dist/loop/run-loop.d.ts +5 -0
- package/dist/loop/run-loop.js +30 -2
- package/dist/loop/tool-name-repair.d.ts +11 -0
- package/dist/loop/tool-name-repair.js +23 -0
- package/dist/loop/types.d.ts +14 -0
- package/dist/memory/card-builders.d.ts +1 -1
- package/dist/prompts/SOUL.d.ts +6 -6
- package/dist/prompts/SOUL.js +22 -121
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +35 -101
- package/dist/prompts/briefing.d.ts +20 -11
- package/dist/prompts/briefing.js +36 -19
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +103 -383
- package/dist/prompts/planner.d.ts +1 -5
- package/dist/prompts/planner.js +45 -128
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +26 -57
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +3 -3
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +12 -47
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +4 -5
- package/dist/prompts/sandbox/data-profiler.js +11 -34
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +3 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +23 -73
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +3 -1
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +3 -4
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +5 -4
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +3 -1
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +3 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +3 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +3 -1
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +3 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +4 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +3 -1
- package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
- package/dist/prompts/sandbox/scientific-executor.js +0 -12
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +3 -1
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +4 -4
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +10 -29
- package/dist/prompts/sandbox-standards.d.ts +15 -18
- package/dist/prompts/sandbox-standards.js +96 -305
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +24 -90
- package/dist/provenance/collector.d.ts +2 -5
- package/dist/provenance/collector.js +2 -5
- package/dist/provenance/seam.d.ts +36 -1
- package/dist/provenance/workspace-reads.d.ts +36 -0
- package/dist/provenance/workspace-reads.js +33 -0
- package/dist/report-model/production-resolver.d.ts +22 -52
- package/dist/report-model/production-resolver.js +43 -112
- package/dist/report-render/chart-export.d.ts +9 -0
- package/dist/report-render/chart-export.js +1 -1
- package/dist/report-render/views/chart-view.d.ts +1 -0
- package/dist/report-render/views/chart-view.js +1 -0
- package/dist/report-render/views/lineage.d.ts +1 -0
- package/dist/report-render/views/page-view.d.ts +1 -0
- package/dist/report-render/views/page-view.js +1 -0
- package/dist/report-render/views/prose.d.ts +1 -0
- package/dist/report-render/views/prose.js +1 -0
- package/dist/report-render/views/references-view.d.ts +1 -0
- package/dist/report-render/views/values.d.ts +1 -0
- package/dist/report-render/views/values.js +1 -0
- package/dist/runtime/assemble.d.ts +20 -9
- package/dist/runtime/assemble.js +47 -38
- package/dist/sandbox/client.d.ts +5 -3
- package/dist/sandbox/create-sandbox.d.ts +7 -12
- package/dist/sandbox/create-sandbox.js +15 -37
- package/dist/sandbox/docker-client.d.ts +1 -3
- package/dist/sandbox/docker-client.js +3 -5
- package/dist/sandbox/exec-outcome.d.ts +11 -11
- package/dist/sandbox/exec-outcome.js +22 -22
- package/dist/sandbox/exec.d.ts +5 -0
- package/dist/sandbox/exec.js +9 -1
- package/dist/sandbox/identity.d.ts +4 -16
- package/dist/sandbox/identity.js +5 -21
- package/dist/sandbox/k8s-client.d.ts +0 -1
- package/dist/sandbox/k8s-client.js +8 -13
- package/dist/sandbox/lease-names.d.ts +6 -0
- package/dist/sandbox/lease-names.js +12 -0
- package/dist/sandbox/leases.d.ts +105 -0
- package/dist/sandbox/leases.js +519 -0
- package/dist/sandbox/liveness.d.ts +5 -2
- package/dist/sandbox/mount-plan.d.ts +10 -1
- package/dist/sandbox/mount-plan.js +7 -1
- package/dist/sandbox/reconciler.d.ts +58 -0
- package/dist/sandbox/reconciler.js +153 -0
- package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
- package/dist/sandbox/types.d.ts +25 -18
- package/dist/sandbox/types.js +10 -4
- package/dist/schemas/plan-schemas.d.ts +0 -21
- package/dist/schemas/plan-schemas.js +2 -3
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/schemas/run-synthesis.js +3 -3
- package/dist/schemas/validate-plan.d.ts +1 -14
- package/dist/schemas/validate-plan.js +5 -24
- package/dist/schemas/workflow-state.d.ts +3 -32
- package/dist/schemas/workflow-state.js +4 -13
- package/dist/state/index.d.ts +0 -1
- package/dist/state/index.js +0 -1
- package/dist/state/migrations/20261030120000_lease_the_sandboxes.d.ts +7 -0
- package/dist/state/migrations/20261030120000_lease_the_sandboxes.js +34 -0
- package/dist/state/migrations/20261030120100_drop_the_report_derivations.d.ts +5 -0
- package/dist/state/migrations/20261030120100_drop_the_report_derivations.js +9 -0
- package/dist/state/migrations/20261030120200_keep_the_report_derivations.d.ts +6 -0
- package/dist/state/migrations/20261030120200_keep_the_report_derivations.js +10 -0
- package/dist/state/migrations/index.js +6 -0
- package/dist/state/purge-analysis.d.ts +4 -2
- package/dist/state/purge-analysis.js +22 -11
- package/dist/state/sandbox-leases.d.ts +81 -0
- package/dist/state/sandbox-leases.js +247 -0
- package/dist/state/schema.d.ts +1 -13
- package/dist/state/schema.js +1 -6
- package/dist/state/step-executions.d.ts +0 -2
- package/dist/state/step-executions.js +1 -4
- package/dist/tasks/data-profile.d.ts +40 -2
- package/dist/tasks/data-profile.js +89 -87
- package/dist/tools/ad-hoc-router.d.ts +1 -6
- package/dist/tools/ad-hoc-router.js +5 -48
- package/dist/tools/bio/comptox.js +1 -1
- package/dist/tools/bio/drug-gene-interactions.js +1 -1
- package/dist/tools/bio/gene-disease-evidence.js +1 -1
- package/dist/tools/bio/keys.d.ts +1 -1
- package/dist/tools/bio/pubmed.js +31 -0
- package/dist/tools/bio/search-gene.js +17 -0
- package/dist/tools/bio/search-geo-datasets.js +13 -0
- package/dist/tools/bio/search-regulatory-approvals.js +1 -1
- package/dist/tools/define-tool.d.ts +25 -2
- package/dist/tools/define-tool.js +11 -1
- package/dist/tools/display/show-user.js +1 -1
- package/dist/tools/execute-analysis.d.ts +1 -3
- package/dist/tools/execute-analysis.js +5 -33
- package/dist/tools/report-session/index.d.ts +0 -1
- package/dist/tools/report-session/index.js +0 -1
- package/dist/tools/report-session/script-tools.d.ts +30 -0
- package/dist/tools/report-session/script-tools.js +177 -0
- package/dist/tools/research/generate-analogy-report.d.ts +16 -9
- package/dist/tools/research/generate-analogy-report.js +111 -49
- package/dist/tools/research/generate-plan.d.ts +102 -7
- package/dist/tools/research/generate-plan.js +74 -43
- package/dist/tools/research/index.d.ts +4 -3
- package/dist/tools/research/index.js +4 -3
- package/dist/tools/research/inspect-run.d.ts +0 -1
- package/dist/tools/research/inspect-run.js +5 -10
- package/dist/tools/research/search-arxiv.js +15 -0
- package/dist/tools/research/search-github-repos.js +13 -0
- package/dist/tools/research/search-semantic-scholar.js +16 -0
- package/dist/tools/research/synthesis-lookups.d.ts +21 -0
- package/dist/tools/research/synthesis-lookups.js +29 -0
- package/dist/tools/research/web-fetch.d.ts +1 -1
- package/dist/tools/research/web-fetch.js +14 -12
- package/dist/tools/sandbox/lease-tools.d.ts +32 -0
- package/dist/tools/sandbox/lease-tools.js +60 -0
- package/dist/tools/script/codemode-worker.d.ts +5 -0
- package/dist/tools/script/codemode-worker.js +5 -0
- package/dist/tools/script/file-tools.d.ts +100 -0
- package/dist/tools/script/file-tools.js +377 -0
- package/dist/tools/script/metrics.d.ts +21 -0
- package/dist/tools/script/metrics.js +38 -0
- package/dist/tools/script/quickjs.wasm +0 -0
- package/dist/tools/script/run-script.d.ts +76 -0
- package/dist/tools/script/run-script.js +326 -0
- package/dist/tools/script/scratch.d.ts +81 -0
- package/dist/tools/script/scratch.js +142 -0
- package/dist/tools/start-report-session.js +1 -1
- package/dist/tools/workspace/edit-file.js +2 -1
- package/dist/tools/workspace/execute-command.d.ts +9 -13
- package/dist/tools/workspace/execute-command.js +23 -19
- package/dist/tools/workspace/file-stat.js +1 -1
- package/dist/tools/workspace/grep.d.ts +5 -2
- package/dist/tools/workspace/grep.js +19 -10
- package/dist/tools/workspace/mutator.d.ts +48 -4
- package/dist/tools/workspace/mutator.js +43 -12
- package/dist/tools/workspace/read-file.d.ts +5 -2
- package/dist/tools/workspace/read-file.js +15 -11
- package/dist/tools/workspace/read-surface.d.ts +17 -0
- package/dist/tools/workspace/read-surface.js +17 -0
- package/dist/tools/workspace/result-bounds.d.ts +1 -0
- package/dist/tools/workspace/run-exec.d.ts +12 -13
- package/dist/tools/workspace/run-exec.js +13 -13
- package/dist/tools/workspace/scan-inputs.d.ts +2 -5
- package/dist/tools/workspace/scan-inputs.js +2 -3
- package/dist/tools/workspace/show-file.js +1 -1
- package/dist/tools/workspace/show-plan.js +4 -3
- package/dist/tools/workspace/write-file.js +2 -1
- package/dist/workflows/execute-analysis-scheduler.d.ts +0 -26
- package/dist/workflows/execute-analysis-scheduler.js +12 -32
- package/dist/workflows/execute-analysis.d.ts +17 -35
- package/dist/workflows/execute-analysis.js +13 -107
- package/dist/workflows/pause.js +29 -22
- package/dist/workflows/sandbox-step.d.ts +16 -31
- package/dist/workflows/sandbox-step.js +36 -71
- package/dist/workflows/test/sql-writes.d.ts +0 -1
- package/dist/workflows/test/sql-writes.js +0 -2
- package/dist/workspace/filesystem.d.ts +2 -2
- package/dist/workspace/filesystem.js +10 -5
- package/dist/workspace/paths.d.ts +19 -1
- package/dist/workspace/paths.js +22 -1
- package/package.json +6 -3
- package/dist/prompts/literature-reviewer.d.ts +0 -1
- package/dist/prompts/literature-reviewer.js +0 -114
- package/dist/sandbox/reaper.d.ts +0 -81
- package/dist/sandbox/reaper.js +0 -161
- package/dist/sandbox/step-sandbox.d.ts +0 -47
- package/dist/sandbox/step-sandbox.js +0 -53
- package/dist/state/active-sandboxes.d.ts +0 -33
- package/dist/state/active-sandboxes.js +0 -65
- package/dist/tasks/extract-values-script.d.ts +0 -64
- package/dist/tasks/extract-values-script.js +0 -180
- package/dist/tasks/extract-values.d.ts +0 -77
- package/dist/tasks/extract-values.js +0 -146
- package/dist/tools/report-session/derive-table.d.ts +0 -164
- package/dist/tools/report-session/derive-table.js +0 -419
- package/dist/tools/research/literature-reviewer.d.ts +0 -42
- package/dist/tools/research/literature-reviewer.js +0 -107
- package/dist/workflows/metrics.d.ts +0 -23
- package/dist/workflows/metrics.js +0 -38
|
@@ -29,6 +29,22 @@ const NON_SYMBOL_PATTERNS = [/^ENSG\d{11}$/, /^HGNC:\d+$/, /^CHEMBL\d+$/, /^[A-Z
|
|
|
29
29
|
function isSymbol(identifier) {
|
|
30
30
|
return !NON_SYMBOL_PATTERNS.some((pattern) => pattern.test(identifier));
|
|
31
31
|
}
|
|
32
|
+
const SearchGeneOutputSchema = z.object({
|
|
33
|
+
genes: z.array(z.object({
|
|
34
|
+
symbol: z.string(),
|
|
35
|
+
id: z.string().describe("The Ensembl gene id (ENSG…), the key of the other gene lookups."),
|
|
36
|
+
displayName: z.string(),
|
|
37
|
+
description: z.string(),
|
|
38
|
+
biotype: z.string(),
|
|
39
|
+
start: z.number(),
|
|
40
|
+
end: z.number(),
|
|
41
|
+
strand: z.number(),
|
|
42
|
+
assemblyName: z.string(),
|
|
43
|
+
seqRegionName: z.string(),
|
|
44
|
+
})),
|
|
45
|
+
resolvedFrom: z.array(z.object({ input: z.string(), symbol: z.string() })),
|
|
46
|
+
notFound: z.array(z.string()),
|
|
47
|
+
});
|
|
32
48
|
export const searchGeneTool = defineTool({
|
|
33
49
|
id: "search_gene",
|
|
34
50
|
description: "Resolve gene identifiers against Ensembl — the identifier step other tools depend on. Per gene: Ensembl gene ID (ENSG…), coordinates, strand, " +
|
|
@@ -41,6 +57,7 @@ export const searchGeneTool = defineTool({
|
|
|
41
57
|
"non-human `species` looks the ortholog up by the human approved symbol.\n" +
|
|
42
58
|
"notFound is valid no-data (a deprecated, aliased or mistyped identifier, or one this species has no gene for) — report it and continue, do not " +
|
|
43
59
|
"retry unchanged.",
|
|
60
|
+
outputSchema: SearchGeneOutputSchema,
|
|
44
61
|
inputSchema: z.object({
|
|
45
62
|
identifiers: z
|
|
46
63
|
.array(z.string())
|
|
@@ -70,6 +70,18 @@ export function mapGeoDatasets(ids, summary) {
|
|
|
70
70
|
}
|
|
71
71
|
return datasets;
|
|
72
72
|
}
|
|
73
|
+
const SearchGeoDatasetsOutputSchema = z.object({
|
|
74
|
+
totalFound: z.number(),
|
|
75
|
+
datasets: z.array(z.object({
|
|
76
|
+
accession: z.string(),
|
|
77
|
+
title: z.string(),
|
|
78
|
+
summary: z.string(),
|
|
79
|
+
platform: z.string().nullable(),
|
|
80
|
+
sampleCount: z.number().nullable(),
|
|
81
|
+
organism: z.string().nullable(),
|
|
82
|
+
pubmedIds: z.array(z.union([z.string(), z.number()])),
|
|
83
|
+
})),
|
|
84
|
+
});
|
|
73
85
|
export const searchGeoDatasetsTool = defineTool({
|
|
74
86
|
id: "search_geo_datasets",
|
|
75
87
|
description: "Search the NCBI Gene Expression Omnibus (GEO) — the public functional-genomics repository of the U.S. National Library of Medicine — for " +
|
|
@@ -80,6 +92,7 @@ export const searchGeoDatasetsTool = defineTool({
|
|
|
80
92
|
"HARD CAVEAT: this tool returns metadata only, and sandbox containers have no network — never plan an in-sandbox step that downloads a GEO dataset. " +
|
|
81
93
|
"Bringing the data itself in is a capability of the surrounding environment, not of this tool: consult the tools you actually have rather than assuming it is possible or that it is not. " +
|
|
82
94
|
"An empty datasets array is a valid 'nothing matched' — do not retry the identical query.",
|
|
95
|
+
outputSchema: SearchGeoDatasetsOutputSchema,
|
|
83
96
|
inputSchema: z.object({
|
|
84
97
|
query: z
|
|
85
98
|
.string()
|
|
@@ -105,7 +105,7 @@ export function createSearchRegulatoryApprovalsTool(deps) {
|
|
|
105
105
|
"ANDA), emaProductNumber, ndc (product or package code) or splSetId.\n" +
|
|
106
106
|
"status 'not_found' is a valid no-data answer and status 'ambiguous' lists the candidate amassIds of an identifier that matches more " +
|
|
107
107
|
"than one record — call 'details' again with one of them. An empty search is also valid no-data. Do not retry any of these unchanged.\n" +
|
|
108
|
-
"Requires AMASS_API_KEY — a missing key fails terminally: do NOT retry
|
|
108
|
+
"Requires AMASS_API_KEY — a missing key fails terminally: do NOT retry; state that the key is not configured, and proceed without " +
|
|
109
109
|
"Amass regulatory data. Every call spends the user's Amass credits.",
|
|
110
110
|
inputSchema,
|
|
111
111
|
describeCall: "none",
|
|
@@ -128,6 +128,13 @@ export type ToolContext = {
|
|
|
128
128
|
* no child agent ignores it.
|
|
129
129
|
*/
|
|
130
130
|
readonly turnUsage?: AgentRunUsage;
|
|
131
|
+
/**
|
|
132
|
+
* The script that makes this call, when `run_script` makes it. A tool that records how its output was made
|
|
133
|
+
* keeps the code, for example the derivation record of a report session. Absent for a call of the model.
|
|
134
|
+
*/
|
|
135
|
+
readonly script?: {
|
|
136
|
+
readonly code: string;
|
|
137
|
+
};
|
|
131
138
|
};
|
|
132
139
|
/** What the approval hook of a tool reads of its call. */
|
|
133
140
|
export type ToolApprovalContext = {
|
|
@@ -143,6 +150,10 @@ export type Tool<Input = unknown, Output = unknown> = {
|
|
|
143
150
|
readonly description: string;
|
|
144
151
|
readonly inputSchema: z.ZodType<Input>;
|
|
145
152
|
readonly jsonSchema: Record<string, unknown>;
|
|
153
|
+
/** See {@link ToolDefinition.outputSchema}. Absent when the tool declares none. */
|
|
154
|
+
readonly outputSchema?: z.ZodType;
|
|
155
|
+
/** The JSON Schema of {@link outputSchema}. */
|
|
156
|
+
readonly outputJsonSchema?: Record<string, unknown>;
|
|
146
157
|
/** See {@link ToolDefinition.describeCall}. Absent when the tool declares none. */
|
|
147
158
|
describeCall?(input: Input): string | undefined;
|
|
148
159
|
/** See {@link ToolDefinition.describeResult}. Absent when the tool declares none. */
|
|
@@ -156,12 +167,19 @@ export type Tool<Input = unknown, Output = unknown> = {
|
|
|
156
167
|
approval?(input: Input, ctx: ToolApprovalContext): AskRequest | undefined | Promise<AskRequest | undefined>;
|
|
157
168
|
/** See {@link ToolDefinition.startsWorkflow}. */
|
|
158
169
|
readonly startsWorkflow?: boolean;
|
|
170
|
+
/** See {@link ToolDefinition.ownStep}. */
|
|
171
|
+
readonly ownStep?: boolean;
|
|
159
172
|
execute(input: Input, ctx: ToolContext): Promise<Result<Output, ToolError>>;
|
|
160
173
|
};
|
|
161
|
-
export type ToolDefinition<Schema extends z.ZodType<z.output<Schema>>, Output> = {
|
|
174
|
+
export type ToolDefinition<Schema extends z.ZodType<z.output<Schema>>, Output, OutputSchema extends z.ZodType = z.ZodType> = {
|
|
162
175
|
readonly id: string;
|
|
163
176
|
readonly description: string;
|
|
164
177
|
readonly inputSchema: Schema;
|
|
178
|
+
/**
|
|
179
|
+
* The shape of the ok value. A script reads the value as data, thus `run_script` shows this type in the signature
|
|
180
|
+
* of the tool. The value of `execute` must hold each field of the schema, and it can hold more.
|
|
181
|
+
*/
|
|
182
|
+
readonly outputSchema?: OutputSchema;
|
|
165
183
|
/**
|
|
166
184
|
* One line that names what THIS call does — the call-time counterpart of
|
|
167
185
|
* `description`. `description` self-describes the tool at attach time, so an
|
|
@@ -251,6 +269,11 @@ export type ToolDefinition<Schema extends z.ZodType<z.output<Schema>>, Output> =
|
|
|
251
269
|
* child before its first `await`, thus the parallel calls of one reply keep their order on a replay.
|
|
252
270
|
*/
|
|
253
271
|
readonly startsWorkflow?: boolean;
|
|
272
|
+
/**
|
|
273
|
+
* The call must be one durable step of its own, for example an exec whose id comes from its step. Each call of
|
|
274
|
+
* a script runs inside the one step of the script, thus `withRunScript` refuses such a tool as a script tool.
|
|
275
|
+
*/
|
|
276
|
+
readonly ownStep?: boolean;
|
|
254
277
|
execute(input: z.infer<Schema>, ctx: ToolContext): Promise<Result<Output, ToolError>>;
|
|
255
278
|
};
|
|
256
279
|
/**
|
|
@@ -260,4 +283,4 @@ export type ToolDefinition<Schema extends z.ZodType<z.output<Schema>>, Output> =
|
|
|
260
283
|
* the first LLM call). Union-shaped inputs must be modelled as a flat object
|
|
261
284
|
* with a discriminator field.
|
|
262
285
|
*/
|
|
263
|
-
export declare function defineTool<Schema extends z.ZodType<z.output<Schema>>, Output>(def: ToolDefinition<Schema, Output>): Tool<z.infer<Schema>, Output>;
|
|
286
|
+
export declare function defineTool<Schema extends z.ZodType<z.output<Schema>>, Output extends z.output<OutputSchema>, OutputSchema extends z.ZodType = z.ZodType>(def: ToolDefinition<Schema, Output, OutputSchema>): Tool<z.infer<Schema>, Output>;
|
|
@@ -111,13 +111,15 @@ export function defineTool(def) {
|
|
|
111
111
|
`top-level "type" — model the input as a flat object with a ` +
|
|
112
112
|
`discriminator field instead.`);
|
|
113
113
|
}
|
|
114
|
-
|
|
114
|
+
let packaged = {
|
|
115
115
|
id: def.id,
|
|
116
116
|
description: def.description,
|
|
117
117
|
inputSchema: def.inputSchema,
|
|
118
118
|
jsonSchema,
|
|
119
119
|
execute: def.execute.bind(def),
|
|
120
120
|
};
|
|
121
|
+
if (def.outputSchema !== undefined)
|
|
122
|
+
packaged = { ...packaged, outputSchema: def.outputSchema, outputJsonSchema: outputJsonSchemaOf(def.outputSchema) };
|
|
121
123
|
if (typeof def.foldCallRecord === "function")
|
|
122
124
|
packaged.foldCallRecord = def.foldCallRecord.bind(def);
|
|
123
125
|
if (typeof def.approval === "function")
|
|
@@ -133,7 +135,15 @@ export function defineTool(def) {
|
|
|
133
135
|
// property check on either side.
|
|
134
136
|
if (def.describeResult !== undefined)
|
|
135
137
|
packaged.describeResult = def.describeResult;
|
|
138
|
+
if (def.ownStep === true)
|
|
139
|
+
packaged = { ...packaged, ownStep: true };
|
|
136
140
|
if (def.startsWorkflow === true)
|
|
137
141
|
return { ...packaged, startsWorkflow: true };
|
|
138
142
|
return packaged;
|
|
139
143
|
}
|
|
144
|
+
/** The JSON Schema of an ok value, as a reader of the value sees it. */
|
|
145
|
+
function outputJsonSchemaOf(schema) {
|
|
146
|
+
const json = z.toJSONSchema(schema, { io: "output", unrepresentable: "any" });
|
|
147
|
+
delete json.$schema;
|
|
148
|
+
return json;
|
|
149
|
+
}
|
|
@@ -41,7 +41,7 @@ const ShowUserInputSchema = z.object({
|
|
|
41
41
|
dataPath: z
|
|
42
42
|
.string()
|
|
43
43
|
.optional()
|
|
44
|
-
.describe("echart only: analysis-rooted CSV artifact path (e.g. 'runs/run-abc/step-2/output/de-summary.csv') the host loads as the ECharts `dataset.source` at render time. Author `encode`/dimensions against the CSV's column names (read only its header) and omit `dataset.source` from the spec — the rows are never pulled through the context window. The tool checks the shape of the path only, not that the file exists; a wrong path fails only when the card renders. If the data is not chart-ready (needs
|
|
44
|
+
.describe("echart only: analysis-rooted CSV artifact path (e.g. 'runs/run-abc/step-2/output/de-summary.csv') the host loads as the ECharts `dataset.source` at render time. Author `encode`/dimensions against the CSV's column names (read only its header) and omit `dataset.source` from the spec — the rows are never pulled through the context window. The tool checks the shape of the path only, not that the file exists; a wrong path fails only when the card renders. If the data is not chart-ready (it needs an aggregate, a filter, or a reshape), make a chart-ready CSV with `run_script` (`read_table`, then `write_derived`), and pass its path. Inline `dataset.source` is only for a handful of numbers you just computed in conversation that exist nowhere as an artifact."),
|
|
45
45
|
body: z.string().optional().describe("Markdown content to render (kind=markdown)"),
|
|
46
46
|
code: z.string().optional().describe("Code content (kind=code)"),
|
|
47
47
|
language: z.string().optional().describe("Language identifier, e.g. r, python, bash (kind=code)"),
|
|
@@ -9,7 +9,6 @@ import { z } from "zod";
|
|
|
9
9
|
import type { AgentSession } from "../auth/types.js";
|
|
10
10
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
11
11
|
import type { EnvironmentStorePaths } from "../config/environment-stores.js";
|
|
12
|
-
import type { ResourcePolicy } from "../config/resource-limits.js";
|
|
13
12
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
14
13
|
import type { RunLauncher } from "../execution/run-launcher.js";
|
|
15
14
|
import type { Logger } from "../lib/logger.js";
|
|
@@ -33,7 +32,6 @@ export type ExecuteAnalysisToolDeps = Pick<EnvironmentStorePaths, "farmLockFile"
|
|
|
33
32
|
readonly executeAnalysisWorkflow: ExecuteAnalysisWorkflow;
|
|
34
33
|
readonly runAuthorizer: RunAuthorizer;
|
|
35
34
|
readonly runLauncher: RunLauncher;
|
|
36
|
-
readonly resourcePolicy?: ResourcePolicy;
|
|
37
35
|
readonly utilityProvider: ChatProvider;
|
|
38
36
|
readonly utilityModel: string;
|
|
39
37
|
readonly logger?: Logger;
|
|
@@ -78,7 +76,7 @@ export declare class PlanReferencesMissingError extends Error {
|
|
|
78
76
|
constructor(missing: string[], unavailable?: string | undefined);
|
|
79
77
|
}
|
|
80
78
|
/** The deps that start the run of a stored plan. */
|
|
81
|
-
export type LaunchPlanRunDeps = Pick<ExecuteAnalysisToolDeps, "pool" | "executeAnalysisWorkflow" | "runAuthorizer" | "runLauncher" | "
|
|
79
|
+
export type LaunchPlanRunDeps = Pick<ExecuteAnalysisToolDeps, "pool" | "executeAnalysisWorkflow" | "runAuthorizer" | "runLauncher" | "extendAnalysisFarm" | "refStorePath" | "logger">;
|
|
82
80
|
/**
|
|
83
81
|
* The run of a stored plan. `started` is false when this call started nothing: `runId` then names the
|
|
84
82
|
* active run of the plan, or the newest run of the plan when the view of the caller was stale.
|
|
@@ -51,20 +51,20 @@ const inputSchema = z
|
|
|
51
51
|
});
|
|
52
52
|
export class PlanNotFoundError extends Error {
|
|
53
53
|
constructor(planId) {
|
|
54
|
-
super(`Plan ${planId} not found for this analysis
|
|
54
|
+
super(`Plan ${planId} not found for this analysis. Generate a new plan; a retry of this planId fails the same way.`);
|
|
55
55
|
this.name = "PlanNotFoundError";
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
export class PlanMalformedError extends Error {
|
|
59
59
|
constructor(planId) {
|
|
60
|
-
super(`Plan ${planId} is malformed
|
|
60
|
+
super(`Plan ${planId} is malformed. Generate a new plan; a retry of this planId fails the same way.`);
|
|
61
61
|
this.name = "PlanMalformedError";
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
export class PlanValidationError extends Error {
|
|
65
65
|
errors;
|
|
66
66
|
constructor(planId, errors) {
|
|
67
|
-
super(`Plan ${planId} failed validation: ${errors.length} error(s)
|
|
67
|
+
super(`Plan ${planId} failed validation: ${errors.length} error(s). Generate a new plan; a retry of this planId fails the same way.`);
|
|
68
68
|
this.errors = errors;
|
|
69
69
|
this.name = "PlanValidationError";
|
|
70
70
|
}
|
|
@@ -243,25 +243,6 @@ async function linkPlanPackages(extendAnalysisFarm, analysisId, planId, plan) {
|
|
|
243
243
|
function planSummary(plan) {
|
|
244
244
|
return plan.title?.trim() || plan.analytical_narrative.trim().slice(0, 280);
|
|
245
245
|
}
|
|
246
|
-
/**
|
|
247
|
-
* The resources of each step. `validatePlan` refuses a step without them, thus
|
|
248
|
-
* a missing entry here means that the two readers disagree.
|
|
249
|
-
*/
|
|
250
|
-
function stepResources(planId, plan) {
|
|
251
|
-
const byStepId = {};
|
|
252
|
-
for (const step of plan.steps) {
|
|
253
|
-
if (!step.resources) {
|
|
254
|
-
return err({
|
|
255
|
-
type: "plan_invalid",
|
|
256
|
-
planId,
|
|
257
|
-
malformed: false,
|
|
258
|
-
errors: [`Step "${step.id}" has no resources — validation should have rejected it`],
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
byStepId[step.id] = step.resources;
|
|
262
|
-
}
|
|
263
|
-
return ok(byStepId);
|
|
264
|
-
}
|
|
265
246
|
function workflowInput(planId, plan, args) {
|
|
266
247
|
return {
|
|
267
248
|
analysisId: args.analysisId,
|
|
@@ -270,10 +251,8 @@ function workflowInput(planId, plan, args) {
|
|
|
270
251
|
threadId: args.threadId,
|
|
271
252
|
steps: plan.steps.map((step) => ({ id: step.id, depends_on: step.depends_on })),
|
|
272
253
|
planStepById: Object.fromEntries(plan.steps.map((step) => [step.id, step])),
|
|
254
|
+
request: plan.request,
|
|
273
255
|
agentByStepId: Object.fromEntries(plan.steps.map((step) => [step.id, step.agent ?? "unknown"])),
|
|
274
|
-
resourcesByStepId: args.resourcesByStepId,
|
|
275
|
-
timeoutByStepId: Object.fromEntries(plan.steps.flatMap((step) => (step.timeout === undefined ? [] : [[step.id, step.timeout]]))),
|
|
276
|
-
budget: args.budget,
|
|
277
256
|
runSession: args.runSession,
|
|
278
257
|
synthesisEnabled: args.synthesisEnabled,
|
|
279
258
|
};
|
|
@@ -349,7 +328,6 @@ export function buildAdHocPlan(request, route, createdAt = new Date().toISOStrin
|
|
|
349
328
|
packages: [...route.packages],
|
|
350
329
|
depends_on: [],
|
|
351
330
|
status: "pending",
|
|
352
|
-
resources: route.resources,
|
|
353
331
|
agent: route.agentId,
|
|
354
332
|
capability: route.capability,
|
|
355
333
|
maxSteps: meta?.defaultMaxSteps ?? DEFAULT_SANDBOX_MAX_STEPS,
|
|
@@ -394,7 +372,6 @@ async function persistedAdHocPlan(deps, args) {
|
|
|
394
372
|
provider: deps.utilityProvider,
|
|
395
373
|
model: deps.utilityModel,
|
|
396
374
|
pool: deps.pool,
|
|
397
|
-
resourcePolicy: deps.resourcePolicy,
|
|
398
375
|
logger: deps.logger,
|
|
399
376
|
resolvePackages,
|
|
400
377
|
usageRecorder: deps.usageRecorder,
|
|
@@ -426,9 +403,6 @@ async function launchReservedRun(deps, args) {
|
|
|
426
403
|
const { session, planId, plan } = args;
|
|
427
404
|
const analysisId = session.scope.analysisId;
|
|
428
405
|
const threadId = session.scope.threadId ?? null;
|
|
429
|
-
const resources = stepResources(planId, plan);
|
|
430
|
-
if (resources.isErr())
|
|
431
|
-
return err(resources.error);
|
|
432
406
|
// Reference data first: it is a read, so it costs nothing to learn before the farm link
|
|
433
407
|
// writes. Both passes come before the run reserves anything, thus a miss of either leaves no
|
|
434
408
|
// run row and no revoked authorization behind.
|
|
@@ -466,9 +440,7 @@ async function launchReservedRun(deps, args) {
|
|
|
466
440
|
analysisId,
|
|
467
441
|
threadId,
|
|
468
442
|
runSession,
|
|
469
|
-
budget: deps.resourcePolicy?.budget,
|
|
470
443
|
synthesisEnabled: args.mode === "plan",
|
|
471
|
-
resourcesByStepId: resources.value,
|
|
472
444
|
});
|
|
473
445
|
try {
|
|
474
446
|
await deps.runLauncher.launch(deps.executeAnalysisWorkflow, { workflowId: runId }, wfInput);
|
|
@@ -649,7 +621,7 @@ export function createExecuteAnalysisTool(deps) {
|
|
|
649
621
|
description: "Launch analysis computation asynchronously. Use mode=plan with the planId only after the user approved that stored plan. " +
|
|
650
622
|
"Use mode=adhoc with the user's exact targeted computational request when they explicitly asked to run/compute/test/compare it; " +
|
|
651
623
|
"that explicit request is consent and needs no synthetic-plan approval. If computation is merely your suggestion, ask first. " +
|
|
652
|
-
"
|
|
624
|
+
"Returns runId with status=in_progress; inspect results on a later turn. " +
|
|
653
625
|
"A plan that already has an active run returns that run, so a second run of one plan does not start. " +
|
|
654
626
|
"The launch fails with an error, and no run starts, when the plan is not found or fails validation, when a package of the plan cannot link, or when the run is not authorized.",
|
|
655
627
|
inputSchema,
|
|
@@ -9,5 +9,4 @@ export { createPreviewReportTool, type PreviewReportInput, type PreviewReportRes
|
|
|
9
9
|
export { createExaminePageTool, type CaptureCoverage, type CapturePage, type ExaminedTile, type ExaminePageInput, type ExaminePageResult, type ExaminePageToolDeps, type FailedRequest, type PageCapture, type ResolvePageUrl, } from "./examine-page.js";
|
|
10
10
|
export { describeSessionPageMintFailure, UnavailableSessionPagePublisher, type MakeSessionPagePublisher, type SessionPageGrant, type SessionPageMintFailure, type SessionPagePublisher, } from "./session-page-publisher.js";
|
|
11
11
|
export { createRecordVersionTool, type RecordVersionInput, type RecordVersionResult, type RecordVersionToolDeps } from "./record-version.js";
|
|
12
|
-
export { createDeriveTableTool, type DeriveTableInput, type DeriveTableResult, type DeriveTableToolDeps } from "./derive-table.js";
|
|
13
12
|
export { createListPinnedArtifactsTool, type ListPinnedArtifactsInput, type ListPinnedArtifactsResult, type ListPinnedArtifactsToolDeps, type PinnedArtifact, type PinnedCitation, } from "./list-artifacts.js";
|
|
@@ -9,5 +9,4 @@ export { createPreviewReportTool, } from "./preview-report.js";
|
|
|
9
9
|
export { createExaminePageTool, } from "./examine-page.js";
|
|
10
10
|
export { describeSessionPageMintFailure, UnavailableSessionPagePublisher, } from "./session-page-publisher.js";
|
|
11
11
|
export { createRecordVersionTool } from "./record-version.js";
|
|
12
|
-
export { createDeriveTableTool } from "./derive-table.js";
|
|
13
12
|
export { createListPinnedArtifactsTool, } from "./list-artifacts.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The script tools of a report session: `read_table` over the pinned evidence, and `write_derived`, which pins a
|
|
3
|
+
* derived table to the session.
|
|
4
|
+
*
|
|
5
|
+
* A script reshapes the evidence into the table that a block binds: a join, a pivot, or an aggregate. The script
|
|
6
|
+
* runs in the VM of `run_script`, thus a derivation starts no container. A table over 16 MiB does not derive.
|
|
7
|
+
*
|
|
8
|
+
* `read_table` reads only a member of the served snapshot, and it refuses bytes whose hash is not the pinned hash.
|
|
9
|
+
* `write_derived` keeps the rules of a session derivation: the output lands under the derived directory of the session,
|
|
10
|
+
* each declared input must be a member, a name that the session holds is refused, and the record chains the
|
|
11
|
+
* table to its inputs and to the code of the script. A rerun of a call whose record landed gives that record
|
|
12
|
+
* back, and it emits the derivation again.
|
|
13
|
+
*/
|
|
14
|
+
import type { Logger } from "../../lib/logger.js";
|
|
15
|
+
import { type ProvenanceSeam } from "../../provenance/seam.js";
|
|
16
|
+
import type { ReportSessionStateStore } from "../../state/report-session-state.js";
|
|
17
|
+
import type { WorkspaceFilesystem } from "../../workspace/filesystem.js";
|
|
18
|
+
import { type ResolveWorkspaceRoot } from "../../workspace/paths.js";
|
|
19
|
+
import { type Tool } from "../define-tool.js";
|
|
20
|
+
import { type ReportSessionStateGateway } from "../report-authoring/authoring-tools.js";
|
|
21
|
+
export type ReportScriptToolsDeps = {
|
|
22
|
+
readonly gateway: ReportSessionStateGateway;
|
|
23
|
+
readonly workspaceFs: WorkspaceFilesystem;
|
|
24
|
+
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
25
|
+
readonly derivations: Pick<ReportSessionStateStore, "appendDerivation">;
|
|
26
|
+
readonly provenance?: ProvenanceSeam;
|
|
27
|
+
readonly logger?: Logger;
|
|
28
|
+
};
|
|
29
|
+
/** The script tools of a report session. */
|
|
30
|
+
export declare function createReportScriptTools(deps: ReportScriptToolsDeps): Tool[];
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The script tools of a report session: `read_table` over the pinned evidence, and `write_derived`, which pins a
|
|
3
|
+
* derived table to the session.
|
|
4
|
+
*
|
|
5
|
+
* A script reshapes the evidence into the table that a block binds: a join, a pivot, or an aggregate. The script
|
|
6
|
+
* runs in the VM of `run_script`, thus a derivation starts no container. A table over 16 MiB does not derive.
|
|
7
|
+
*
|
|
8
|
+
* `read_table` reads only a member of the served snapshot, and it refuses bytes whose hash is not the pinned hash.
|
|
9
|
+
* `write_derived` keeps the rules of a session derivation: the output lands under the derived directory of the session,
|
|
10
|
+
* each declared input must be a member, a name that the session holds is refused, and the record chains the
|
|
11
|
+
* table to its inputs and to the code of the script. A rerun of a call whose record landed gives that record
|
|
12
|
+
* back, and it emits the derivation again.
|
|
13
|
+
*/
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { ok, err } from "neverthrow";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { createNoopLogger } from "../../lib/console-logger.js";
|
|
18
|
+
import { computeSha256 } from "../../lib/fs-helpers.js";
|
|
19
|
+
import { bindSessionEmit } from "../../provenance/seam.js";
|
|
20
|
+
import { snapshotEntry } from "../../report-model/reference-resolver.js";
|
|
21
|
+
import { isSafeId, reportSessionDerivedDir } from "../../workspace/paths.js";
|
|
22
|
+
import { defineTool, withToolCallRecord } from "../define-tool.js";
|
|
23
|
+
import { openReportThread } from "../report-authoring/authoring-tools.js";
|
|
24
|
+
import { ReadTableOutputSchema, readTableRows, rowsText } from "../script/file-tools.js";
|
|
25
|
+
import { DERIVED_FILE_CAP_BYTES } from "../script/scratch.js";
|
|
26
|
+
import { confinedWrite } from "../workspace/mutator.js";
|
|
27
|
+
function refused(error) {
|
|
28
|
+
return err({ error, retryable: false });
|
|
29
|
+
}
|
|
30
|
+
const readTableInput = z.object({
|
|
31
|
+
path: z.string().min(1).describe("The path of a pinned artifact or of a derived table of this session, as `list_pinned_artifacts` gives it."),
|
|
32
|
+
columns: z.array(z.string()).min(1).optional().describe("Keep only these columns. Absent: each column."),
|
|
33
|
+
});
|
|
34
|
+
function createReadTableTool(deps) {
|
|
35
|
+
return defineTool({
|
|
36
|
+
id: "read_table",
|
|
37
|
+
description: "Read one table of the pinned evidence as rows: CSV, TSV, JSON (an array of flat objects), or parquet, up to 16 MiB. " +
|
|
38
|
+
"Each row is an object keyed by column, and a cell that holds a plain number reads as a number. " +
|
|
39
|
+
"A larger table does not derive; tell the user which table is absent.",
|
|
40
|
+
inputSchema: readTableInput,
|
|
41
|
+
outputSchema: ReadTableOutputSchema,
|
|
42
|
+
describeCall: ({ path }) => path,
|
|
43
|
+
describeResult: (_input, result) => `${String(result.rowCount)} rows`,
|
|
44
|
+
execute: async ({ path, columns }, ctx) => {
|
|
45
|
+
const opened = await openReportThread(deps.gateway, ctx.session.scope);
|
|
46
|
+
if (opened.isErr())
|
|
47
|
+
return refused(opened.error.detail);
|
|
48
|
+
const entry = snapshotEntry(opened.value.state.snapshot, path);
|
|
49
|
+
if (entry === undefined)
|
|
50
|
+
return refused(`the pinned evidence of this session holds no artifact at ${path}; list_pinned_artifacts gives the paths`);
|
|
51
|
+
const table = await readTableRows({
|
|
52
|
+
workspaceFs: deps.workspaceFs,
|
|
53
|
+
session: ctx.session,
|
|
54
|
+
path,
|
|
55
|
+
columns,
|
|
56
|
+
tooLarge: "a larger table does not derive",
|
|
57
|
+
});
|
|
58
|
+
if (table.isErr())
|
|
59
|
+
return refused(table.error);
|
|
60
|
+
const hash = computeSha256(table.value.content);
|
|
61
|
+
if (hash !== entry.hash)
|
|
62
|
+
return refused(`${path} changed after the session pinned it, thus its bytes are not the evidence of this session`);
|
|
63
|
+
const { rows } = table.value;
|
|
64
|
+
return ok(withToolCallRecord({ path, columns: table.value.columns, rowCount: rows.length, rows }, { reads: [{ path, hash }] }));
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const writeDerivedInput = z.object({
|
|
69
|
+
name: z
|
|
70
|
+
.string()
|
|
71
|
+
.min(1)
|
|
72
|
+
.describe("The file name of the table, with no directory, for example 'yield_by_group.csv'. The extension picks the format: .csv, .tsv, or .json."),
|
|
73
|
+
rows: z
|
|
74
|
+
.array(z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()])))
|
|
75
|
+
.describe("The rows of the table, each an object keyed by column."),
|
|
76
|
+
inputs: z.array(z.string()).min(1).describe("The path of each pinned table that the script read for this table. The record pins the hash of each one."),
|
|
77
|
+
});
|
|
78
|
+
const DerivedOutputSchema = z.object({ path: z.string(), hash: z.string(), columns: z.array(z.string()).readonly() });
|
|
79
|
+
function derivedOutput(record, size, columns) {
|
|
80
|
+
return withToolCallRecord({ path: record.outputPath, hash: record.outputHash, columns }, { writes: [{ path: record.outputPath, hash: record.outputHash, size }] });
|
|
81
|
+
}
|
|
82
|
+
function createWriteDerivedTool(deps) {
|
|
83
|
+
const logger = (deps.logger ?? createNoopLogger()).named("report-write-derived");
|
|
84
|
+
const observe = bindSessionEmit(deps.provenance, logger);
|
|
85
|
+
const observeDerived = (analysisId, threadId, record) => {
|
|
86
|
+
observe({
|
|
87
|
+
type: "run-derivation",
|
|
88
|
+
analysisId,
|
|
89
|
+
threadId,
|
|
90
|
+
outputPath: record.outputPath,
|
|
91
|
+
outputHash: record.outputHash,
|
|
92
|
+
scriptHash: record.scriptHash,
|
|
93
|
+
sources: record.sources.map((source) => ({ path: source.path, hash: source.hash })),
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
return defineTool({
|
|
97
|
+
id: "write_derived",
|
|
98
|
+
description: "Pin one derived table to this session, and give its path for a block to bind. " +
|
|
99
|
+
"Name each pinned table that the script read in `inputs`. A name that this session already holds is refused, thus a second derivation of one table takes a new name.",
|
|
100
|
+
inputSchema: writeDerivedInput,
|
|
101
|
+
outputSchema: DerivedOutputSchema,
|
|
102
|
+
describeCall: ({ name }) => name,
|
|
103
|
+
describeResult: (_input, result) => result.path,
|
|
104
|
+
execute: async ({ name, rows, inputs }, ctx) => {
|
|
105
|
+
if (ctx.script === undefined)
|
|
106
|
+
return refused("write_derived runs inside a script only");
|
|
107
|
+
const opened = await openReportThread(deps.gateway, ctx.session.scope);
|
|
108
|
+
if (opened.isErr())
|
|
109
|
+
return refused(opened.error.detail);
|
|
110
|
+
const { threadId, analysisId, state, derivations } = opened.value;
|
|
111
|
+
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
112
|
+
// A rerun of a call whose record landed gives that record back. The membership holds the output of
|
|
113
|
+
// the record by now, thus the name test below would refuse the call its own table.
|
|
114
|
+
const recorded = derivations.find((record) => record.toolCallId === ctx.invocationId);
|
|
115
|
+
if (recorded !== undefined) {
|
|
116
|
+
observeDerived(analysisId, threadId, recorded);
|
|
117
|
+
return ok(derivedOutput(recorded, Buffer.byteLength(rowsText(name, rows) ?? "", "utf8"), columns));
|
|
118
|
+
}
|
|
119
|
+
if (!isSafeId(name))
|
|
120
|
+
return refused("the name is one file name, with no directory, no separator, and no traversal");
|
|
121
|
+
const text = rowsText(name, rows);
|
|
122
|
+
if (text === undefined)
|
|
123
|
+
return refused(`${name} names no table format: end the name in .csv, .tsv, or .json`);
|
|
124
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
125
|
+
if (bytes > DERIVED_FILE_CAP_BYTES)
|
|
126
|
+
return refused(`the table is ${String(bytes)} bytes, and the cap of a derived table is ${String(DERIVED_FILE_CAP_BYTES)} bytes`);
|
|
127
|
+
const sources = [];
|
|
128
|
+
for (const path of new Set(inputs)) {
|
|
129
|
+
const entry = snapshotEntry(state.snapshot, path);
|
|
130
|
+
if (entry === undefined)
|
|
131
|
+
return refused(`the pinned evidence of this session holds no artifact at ${path}`);
|
|
132
|
+
sources.push({ path, hash: entry.hash });
|
|
133
|
+
}
|
|
134
|
+
const dir = reportSessionDerivedDir(threadId);
|
|
135
|
+
const outputPath = `${dir}/${name}`;
|
|
136
|
+
if (snapshotEntry(state.snapshot, outputPath) !== undefined) {
|
|
137
|
+
return refused(`the evidence of this session already holds ${outputPath}, thus this table takes a new name`);
|
|
138
|
+
}
|
|
139
|
+
const workspaceRoot = await deps.resolveWorkspaceRoot(analysisId);
|
|
140
|
+
const landed = await confinedWrite({
|
|
141
|
+
workspaceRoot,
|
|
142
|
+
analysisId,
|
|
143
|
+
workingDir: join(workspaceRoot, dir),
|
|
144
|
+
path: name,
|
|
145
|
+
content: text,
|
|
146
|
+
invocationId: ctx.invocationId,
|
|
147
|
+
beforeLand: undefined,
|
|
148
|
+
refuseStagedInputs: true,
|
|
149
|
+
});
|
|
150
|
+
if (landed.status !== "ok")
|
|
151
|
+
return refused(`the write of ${outputPath} was refused (${landed.status})`);
|
|
152
|
+
const record = {
|
|
153
|
+
outputPath,
|
|
154
|
+
outputHash: computeSha256(landed.bytes),
|
|
155
|
+
sources,
|
|
156
|
+
scriptHash: computeSha256(Buffer.from(ctx.script.code, "utf8")),
|
|
157
|
+
script: ctx.script.code,
|
|
158
|
+
toolCallId: ctx.invocationId,
|
|
159
|
+
};
|
|
160
|
+
const appended = await deps.derivations.appendDerivation(threadId, record);
|
|
161
|
+
if (appended.isErr()) {
|
|
162
|
+
logger.error("the derivation record did not land", { threadId, ...logger.errorFields(appended.error.cause) });
|
|
163
|
+
return err({ error: "the derivation record did not land, thus the table is not evidence of this session", retryable: true });
|
|
164
|
+
}
|
|
165
|
+
if (appended.value === "duplicate")
|
|
166
|
+
return refused(`another turn recorded ${outputPath} first, thus this table takes a new name`);
|
|
167
|
+
if (appended.value === "absent")
|
|
168
|
+
return refused("no report session state row exists to hold the derivation");
|
|
169
|
+
observeDerived(analysisId, threadId, record);
|
|
170
|
+
return ok(derivedOutput(record, landed.bytes.length, columns));
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** The script tools of a report session. */
|
|
175
|
+
export function createReportScriptTools(deps) {
|
|
176
|
+
return [createReadTableTool(deps), createWriteDerivedTool(deps)];
|
|
177
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* generateAnalogyReport — cross-domain analogy report as a sub-agent tool.
|
|
3
3
|
*
|
|
4
|
-
* An inline harness `AgentDefinition`
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* with no outcome gets one salvage request, then an `extraction-failed`
|
|
10
|
-
* envelope, so the frontend never renders raw prose.
|
|
4
|
+
* An inline harness `AgentDefinition` driven by `runWithSalvage`. The reasoner records its report in parts: the
|
|
5
|
+
* problem with its planned analogies, then each analogy when its search is done. A provider filter can stop a reply in
|
|
6
|
+
* the middle of a call, and a cut call is lost whole, thus a small part loses little. The wrapper assembles the report
|
|
7
|
+
* from the last accepted record of each part, else it gives the first blocker. A run with no outcome gets one salvage
|
|
8
|
+
* request, then an `extraction-failed` envelope, so the frontend never renders raw prose.
|
|
11
9
|
*/
|
|
12
10
|
import { z } from "zod";
|
|
13
11
|
import { type AnalogyReport } from "@inflexa-ai/harness/contracts/analogy-report.js";
|
|
@@ -15,12 +13,17 @@ import type { Logger } from "../../lib/logger.js";
|
|
|
15
13
|
import { type LoopStep } from "../../loop/run-loop.js";
|
|
16
14
|
import type { SubAgentRegistrar } from "../../loop/sub-agent.js";
|
|
17
15
|
import type { ToolOutputStore } from "../../loop/tool-output.js";
|
|
16
|
+
import type { AgentDefinition } from "../../loop/types.js";
|
|
17
|
+
import type { ScriptRuntime } from "../script/run-script.js";
|
|
18
18
|
import type { ChatProvider } from "../../providers/types.js";
|
|
19
19
|
import type { UsageRecorder } from "../../billing/usage-recorder.js";
|
|
20
20
|
import { type Tool } from "../define-tool.js";
|
|
21
21
|
import { type BioToolKeys } from "../bio/keys.js";
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The report that a run recorded, or nothing when it recorded no problem. The analogies keep the planned order, and a
|
|
24
|
+
* recorded analogy that the plan does not name follows them. A planned analogy with no record is not loaded.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assembledReportOf(steps: readonly LoopStep[]): AnalogyReport | undefined;
|
|
24
27
|
export declare const generateAnalogyReportInputSchema: z.ZodObject<{
|
|
25
28
|
problem: z.ZodString;
|
|
26
29
|
context: z.ZodOptional<z.ZodString>;
|
|
@@ -49,6 +52,10 @@ export type GenerateAnalogyReportDeps = {
|
|
|
49
52
|
readonly toolOutputStore?: ToolOutputStore;
|
|
50
53
|
/** Logging seam; omitted falls back to no-op. Logs the salvage warning and an accounting error of the reasoner loop. */
|
|
51
54
|
readonly logger?: Logger;
|
|
55
|
+
/** Where the VM of `run_script` comes from. See {@link ScriptRuntime}. */
|
|
56
|
+
readonly scriptRuntime?: ScriptRuntime;
|
|
52
57
|
};
|
|
53
58
|
/** Build the `generate_analogy_report` delegation tool bound to its provider. */
|
|
59
|
+
/** The analogical reasoner: its searches run in a script, and it records its report and a blocker directly. */
|
|
60
|
+
export declare function analogyAgentOf(deps: Pick<GenerateAnalogyReportDeps, "bioKeys" | "model" | "toolOutputStore" | "scriptRuntime">): AgentDefinition;
|
|
54
61
|
export declare function createGenerateAnalogyReportTool(deps: GenerateAnalogyReportDeps, registerSubAgent: SubAgentRegistrar): Tool;
|