@inflexa-ai/harness 0.1.1 → 0.3.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 +48 -0
- package/dist/agents/conversation-agent.d.ts +15 -11
- package/dist/agents/conversation-agent.js +31 -34
- package/dist/agents/report-builder.js +2 -1
- package/dist/agents/sandbox/bulk-transcriptomics-agent.js +1 -1
- package/dist/agents/sandbox/cheminformatics-agent.js +1 -19
- package/dist/agents/sandbox/chromatin-agent.js +1 -10
- package/dist/agents/sandbox/dna-methylation-agent.js +1 -10
- package/dist/agents/sandbox/drug-repurposing-agent.js +4 -14
- package/dist/agents/sandbox/enrichment-agent.js +1 -10
- package/dist/agents/sandbox/ephemeral-executor.js +1 -10
- package/dist/agents/sandbox/genomic-variant-agent.js +1 -3
- package/dist/agents/sandbox/immune-profiling-agent.js +1 -13
- package/dist/agents/sandbox/metabolomics-agent.js +1 -1
- package/dist/agents/sandbox/microbiome-agent.js +1 -1
- package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -12
- package/dist/agents/sandbox/multimodal-sc-agent.js +1 -1
- package/dist/agents/sandbox/network-agent.js +1 -10
- package/dist/agents/sandbox/pkpd-clinical-response-agent.js +2 -8
- package/dist/agents/sandbox/proteomics-agent.js +1 -1
- package/dist/agents/sandbox/scientific-executor.js +1 -10
- package/dist/agents/sandbox/shared.d.ts +20 -6
- package/dist/agents/sandbox/shared.js +50 -39
- package/dist/agents/sandbox/single-cell-agent.js +1 -1
- package/dist/agents/sandbox/spatial-omics-agent.js +1 -1
- package/dist/agents/sandbox/statistical-modeling-agent.js +1 -1
- package/dist/agents/sandbox/translational-safety-agent.js +3 -9
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/system-prompt.d.ts +37 -11
- package/dist/agents/system-prompt.js +35 -12
- package/dist/app/chat-turn.d.ts +3 -0
- package/dist/app/chat-turn.js +3 -1
- package/dist/app/data-profile-orientation.d.ts +34 -0
- package/dist/app/data-profile-orientation.js +117 -0
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +3 -1
- package/dist/contracts/schemas/chat-parts.d.ts +9 -9
- package/dist/execution/artifact-metadata.d.ts +3 -0
- package/dist/execution/artifact-metadata.js +10 -3
- package/dist/execution/artifact-registration.d.ts +2 -1
- package/dist/execution/artifact-registration.js +8 -2
- package/dist/execution/ephemeral-runner.d.ts +16 -0
- package/dist/execution/ephemeral-runner.js +31 -5
- package/dist/execution/post-step-pipeline.d.ts +3 -0
- package/dist/execution/post-step-pipeline.js +24 -4
- package/dist/execution/reconcile-manifest.d.ts +7 -0
- package/dist/execution/reconcile-manifest.js +19 -5
- package/dist/execution/report-runner.d.ts +3 -0
- package/dist/execution/report-runner.js +4 -4
- package/dist/execution/run-synthesis.js +20 -19
- package/dist/execution/step-summary.d.ts +3 -0
- package/dist/execution/step-summary.js +6 -3
- package/dist/index.d.ts +9 -2
- package/dist/index.js +24 -5
- package/dist/lib/chrome.d.ts +4 -1
- package/dist/lib/chrome.js +10 -7
- package/dist/lib/console-logger.d.ts +19 -0
- package/dist/lib/console-logger.js +62 -0
- package/dist/lib/logger.d.ts +88 -0
- package/dist/lib/logger.js +37 -0
- package/dist/lib/otel.d.ts +2 -1
- package/dist/lib/otel.js +8 -3
- package/dist/loop/__fixtures__/scripted-provider.d.ts +2 -2
- package/dist/loop/__fixtures__/scripted-provider.js +2 -1
- package/dist/loop/metrics.d.ts +33 -1
- package/dist/loop/metrics.js +56 -2
- package/dist/loop/run-agent.d.ts +13 -1
- package/dist/loop/run-agent.js +19 -4
- package/dist/memory/card-builders.d.ts +8 -1
- package/dist/memory/card-builders.js +20 -3
- package/dist/memory/reconstruct-cards.js +4 -3
- package/dist/memory/thread-history.d.ts +27 -2
- package/dist/memory/thread-history.js +45 -5
- package/dist/memory/working-memory.d.ts +93 -10
- package/dist/memory/working-memory.js +220 -84
- package/dist/prompts/SOUL.d.ts +22 -9
- package/dist/prompts/SOUL.js +56 -37
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +2 -2
- package/dist/prompts/briefing.d.ts +100 -0
- package/dist/prompts/briefing.js +166 -0
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +94 -440
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +10 -10
- package/dist/prompts/planner.js +28 -59
- package/dist/prompts/report-builder.d.ts +1 -1
- package/dist/prompts/report-builder.js +71 -24
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +2 -7
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +11 -15
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +3 -7
- package/dist/prompts/sandbox/data-profiler.d.ts +1 -1
- package/dist/prompts/sandbox/data-profiler.js +13 -8
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +15 -16
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +2 -9
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +2 -6
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +3 -8
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +1 -7
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +2 -7
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +2 -8
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +2 -7
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +2 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +2 -9
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +2 -9
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -6
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +2 -7
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +13 -14
- package/dist/prompts/sandbox-standards.d.ts +22 -14
- package/dist/prompts/sandbox-standards.js +91 -71
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +33 -131
- package/dist/provenance/collector.js +1 -1
- package/dist/providers/ai-sdk.js +31 -4
- package/dist/providers/prompt-cache.d.ts +74 -0
- package/dist/providers/prompt-cache.js +77 -0
- package/dist/providers/types.d.ts +33 -0
- package/dist/reference-data/catalog.d.ts +131 -0
- package/dist/reference-data/catalog.js +348 -0
- package/dist/reference-data/receipt.d.ts +41 -0
- package/dist/reference-data/receipt.js +40 -0
- package/dist/runtime/boot.d.ts +2 -2
- package/dist/runtime/boot.js +1 -1
- package/dist/runtime/connection-budget.d.ts +2 -2
- package/dist/runtime/connection-budget.js +5 -3
- package/dist/runtime/dbos.d.ts +7 -7
- package/dist/runtime/dbos.js +14 -11
- package/dist/runtime/shutdown.d.ts +2 -2
- package/dist/runtime/shutdown.js +4 -3
- package/dist/sandbox/create-sandbox.d.ts +2 -2
- package/dist/sandbox/docker-client.d.ts +2 -2
- package/dist/sandbox/docker-client.js +4 -1
- package/dist/sandbox/k8s-client.d.ts +3 -0
- package/dist/sandbox/k8s-client.js +5 -4
- package/dist/sandbox/notification-sweep.d.ts +3 -3
- package/dist/sandbox/notification-sweep.js +4 -2
- package/dist/sandbox/reaper.d.ts +3 -3
- package/dist/sandbox/reaper.js +7 -4
- package/dist/sandbox/watchdog.d.ts +3 -3
- package/dist/sandbox/watchdog.js +8 -2
- package/dist/schemas/data-profile-schemas.js +5 -3
- package/dist/schemas/plan-schemas.d.ts +21 -21
- package/dist/schemas/plan-schemas.js +9 -3
- package/dist/schemas/run-synthesis.d.ts +8 -4
- package/dist/schemas/run-synthesis.js +92 -56
- package/dist/schemas/validate-plan.js +1 -1
- package/dist/schemas/workflow-state.d.ts +7 -1
- package/dist/schemas/workflow-state.js +33 -19
- package/dist/state/artifacts.d.ts +13 -0
- package/dist/state/artifacts.js +27 -0
- package/dist/state/data-profile.d.ts +64 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +1 -1
- package/dist/state/init.d.ts +2 -1
- package/dist/state/init.js +5 -6
- package/dist/tasks/data-profile.d.ts +25 -2
- package/dist/tasks/data-profile.js +95 -39
- package/dist/tools/bio/chembl.d.ts +38 -0
- package/dist/tools/bio/chembl.js +138 -0
- package/dist/tools/bio/comptox.d.ts +50 -0
- package/dist/tools/bio/comptox.js +149 -0
- package/dist/tools/bio/get-impc-ko-profile.js +9 -6
- package/dist/tools/bio/index.d.ts +5 -17
- package/dist/tools/bio/index.js +5 -17
- package/dist/tools/bio/keys.d.ts +14 -9
- package/dist/tools/bio/keys.js +16 -16
- package/dist/tools/bio/opentargets.d.ts +38 -0
- package/dist/tools/bio/opentargets.js +89 -0
- package/dist/tools/bio/pubchem.d.ts +29 -0
- package/dist/tools/bio/pubchem.js +92 -0
- package/dist/tools/bio/pubmed.d.ts +51 -0
- package/dist/tools/bio/pubmed.js +100 -0
- package/dist/tools/bio/search-bgee-expression.js +12 -6
- package/dist/tools/bio/search-clinical-trials.js +16 -5
- package/dist/tools/bio/search-dgidb.d.ts +11 -1
- package/dist/tools/bio/search-dgidb.js +92 -78
- package/dist/tools/bio/search-faers.js +8 -4
- package/dist/tools/bio/search-geo-datasets.js +20 -6
- package/dist/tools/bio/search-gwas-catalog.d.ts +1 -1
- package/dist/tools/bio/search-pharmgkb.js +10 -4
- package/dist/tools/display/index.d.ts +1 -0
- package/dist/tools/display/index.js +1 -0
- package/dist/tools/display/normalize-echart-spec.d.ts +61 -0
- package/dist/tools/display/normalize-echart-spec.js +211 -0
- package/dist/tools/display/show-user.d.ts +6 -1
- package/dist/tools/display/show-user.js +25 -12
- package/dist/tools/execute-plan.js +5 -2
- package/dist/tools/iterate-report.d.ts +171 -118
- package/dist/tools/iterate-report.js +336 -96
- package/dist/tools/lib/chembl-client.d.ts +12 -0
- package/dist/tools/lib/chembl-client.js +127 -0
- package/dist/tools/lib/ctx-ops.d.ts +224 -0
- package/dist/tools/lib/ctx-ops.js +450 -0
- package/dist/tools/lib/iuphar-client.js +10 -1
- package/dist/tools/lib/openfda-client.js +11 -3
- package/dist/tools/lib/pathway-client.js +10 -4
- package/dist/tools/lib/pubchem-ops.d.ts +74 -0
- package/dist/tools/lib/pubchem-ops.js +224 -0
- package/dist/tools/memory/update-working-memory.d.ts +45 -7
- package/dist/tools/memory/update-working-memory.js +111 -41
- package/dist/tools/report/index.d.ts +3 -2
- package/dist/tools/report/index.js +3 -2
- package/dist/tools/research/generate-analogy-report.js +25 -12
- package/dist/tools/research/generate-plan.d.ts +12 -5
- package/dist/tools/research/generate-plan.js +168 -37
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.d.ts +85 -0
- package/dist/tools/research/inspect-data-profile.js +161 -0
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +5 -6
- package/dist/tools/sandbox/list-available-packages.d.ts +79 -5
- package/dist/tools/sandbox/list-available-packages.js +152 -10
- package/dist/tools/sandbox/list-available-refs.d.ts +141 -5
- package/dist/tools/sandbox/list-available-refs.js +647 -134
- package/dist/tools/sandbox/report-blocker.d.ts +39 -9
- package/dist/tools/sandbox/report-blocker.js +43 -20
- package/dist/tools/workspace/execute-command.d.ts +3 -0
- package/dist/tools/workspace/execute-command.js +3 -1
- package/dist/tools/workspace/read-file.js +4 -5
- package/dist/tools/workspace/run-exec.d.ts +4 -4
- package/dist/tools/workspace/run-exec.js +4 -4
- package/dist/tools/workspace/show-file.js +10 -6
- package/dist/tools/workspace/show-plan.js +12 -6
- package/dist/tools/workspace/workspace-search.d.ts +1 -1
- package/dist/tools/workspace/workspace-search.js +11 -5
- package/dist/workflows/execute-analysis.d.ts +46 -4
- package/dist/workflows/execute-analysis.js +118 -13
- package/dist/workflows/execute-target-assessment.d.ts +3 -0
- package/dist/workflows/execute-target-assessment.js +19 -15
- package/dist/workflows/sandbox-step.d.ts +9 -0
- package/dist/workflows/sandbox-step.js +39 -17
- package/dist/workflows/target-assessment/lib/citation-validator.js +2 -2
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +2 -1
- package/dist/workflows/target-assessment/lib/llm-step.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/llm-step.js +3 -1
- package/dist/workflows/target-assessment/lib/structured-llm.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/structured-llm.js +1 -0
- package/dist/workflows/target-assessment/phase5-persist.d.ts +3 -0
- package/dist/workflows/target-assessment/phase5-persist.js +8 -3
- package/dist/workflows/target-assessment/progress.d.ts +2 -1
- package/dist/workflows/target-assessment/progress.js +4 -3
- package/dist/workspace/filesystem.js +163 -74
- package/dist/workspace/paths.d.ts +2 -2
- package/dist/workspace/paths.js +2 -2
- package/package.json +95 -96
- package/dist/execution/filesystem-artifact-registry.d.ts +0 -57
- package/dist/execution/filesystem-artifact-registry.js +0 -92
- package/dist/lib/identifiers.d.ts +0 -8
- package/dist/lib/identifiers.js +0 -10
- package/dist/lib/mime-detect.d.ts +0 -17
- package/dist/lib/mime-detect.js +0 -29
- package/dist/lib/streamed-download.d.ts +0 -28
- package/dist/lib/streamed-download.js +0 -111
- package/dist/lib/trace-context.d.ts +0 -11
- package/dist/lib/trace-context.js +0 -49
- package/dist/providers/llm-capabilities.d.ts +0 -33
- package/dist/providers/llm-capabilities.js +0 -58
- package/dist/schemas/render-step-prompt.d.ts +0 -32
- package/dist/schemas/render-step-prompt.js +0 -76
- package/dist/tools/bio/find-approval-precedent.d.ts +0 -32
- package/dist/tools/bio/find-approval-precedent.js +0 -72
- package/dist/tools/bio/get-article-details.d.ts +0 -11
- package/dist/tools/bio/get-article-details.js +0 -20
- package/dist/tools/bio/get-article-full-text.d.ts +0 -19
- package/dist/tools/bio/get-article-full-text.js +0 -32
- package/dist/tools/bio/get-bioactivity.d.ts +0 -11
- package/dist/tools/bio/get-bioactivity.js +0 -23
- package/dist/tools/bio/get-drug-info.d.ts +0 -9
- package/dist/tools/bio/get-drug-info.js +0 -22
- package/dist/tools/bio/get-mechanism.d.ts +0 -8
- package/dist/tools/bio/get-mechanism.js +0 -18
- package/dist/tools/bio/get-pubchem-assays.d.ts +0 -22
- package/dist/tools/bio/get-pubchem-assays.js +0 -107
- package/dist/tools/bio/get-pubchem-cross-refs.d.ts +0 -15
- package/dist/tools/bio/get-pubchem-cross-refs.js +0 -68
- package/dist/tools/bio/get-target-safety.d.ts +0 -18
- package/dist/tools/bio/get-target-safety.js +0 -26
- package/dist/tools/bio/search-compounds.d.ts +0 -22
- package/dist/tools/bio/search-compounds.js +0 -160
- package/dist/tools/bio/search-ctx-chemical.d.ts +0 -50
- package/dist/tools/bio/search-ctx-chemical.js +0 -130
- package/dist/tools/bio/search-ctx-exposure.d.ts +0 -55
- package/dist/tools/bio/search-ctx-exposure.js +0 -176
- package/dist/tools/bio/search-ctx-hazard.d.ts +0 -111
- package/dist/tools/bio/search-ctx-hazard.js +0 -178
- package/dist/tools/bio/search-opentargets.d.ts +0 -11
- package/dist/tools/bio/search-opentargets.js +0 -25
- package/dist/tools/bio/search-pubchem-compound.d.ts +0 -26
- package/dist/tools/bio/search-pubchem-compound.js +0 -110
- package/dist/tools/bio/search-pubmed.d.ts +0 -17
- package/dist/tools/bio/search-pubmed.js +0 -31
- package/dist/tools/bio/search-targets.d.ts +0 -9
- package/dist/tools/bio/search-targets.js +0 -20
- package/dist/tools/bio/search-toxcast.d.ts +0 -36
- package/dist/tools/bio/search-toxcast.js +0 -141
- package/dist/workflows/register-workflows.d.ts +0 -51
- package/dist/workflows/register-workflows.js +0 -48
- package/dist/workflows/resume-execute-analysis.d.ts +0 -70
- package/dist/workflows/resume-execute-analysis.js +0 -80
- package/dist/workflows/target-assessment/correction-loop.d.ts +0 -45
- package/dist/workflows/target-assessment/correction-loop.js +0 -62
- package/dist/workflows/target-assessment/steps/phase0-resolve.d.ts +0 -71
- package/dist/workflows/target-assessment/steps/phase0-resolve.js +0 -21
- package/dist/workflows/target-assessment/steps/phase1-aggregate.d.ts +0 -1018
- package/dist/workflows/target-assessment/steps/phase1-aggregate.js +0 -25
- package/dist/workspace/mount-strategy.d.ts +0 -50
- package/dist/workspace/mount-strategy.js +0 -67
- package/dist/workspace/provenance-collector.d.ts +0 -34
- package/dist/workspace/provenance-collector.js +0 -16
- package/dist/workspace/workspace-paths.d.ts +0 -26
- package/dist/workspace/workspace-paths.js +0 -31
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Report authoring tools for the conversation agent: `plan_report` + `submit_report`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* existing `previewId`. Optional top-level `sources` adds new assets
|
|
12
|
-
* on top of the existing assets/ dir.
|
|
4
|
+
* WHY TWO TOOLS. The report brief (title/audience/sources plus the section
|
|
5
|
+
* union — narrative/metrics/figure/table/chart, each with its own
|
|
6
|
+
* encoding/transform/asset sub-fields) is a ~12k-char JSON schema. Every
|
|
7
|
+
* registered tool's schema ships on EVERY conversation turn, so parking that
|
|
8
|
+
* brief on an always-on tool would tax every turn for a tool used only when a
|
|
9
|
+
* report is actually built. We split the single former tool in two and deliver
|
|
10
|
+
* the heavy schema just-in-time:
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* - `plan_report` — a tiny, always-on trigger (no-arg). Its `execute` RETURNS
|
|
13
|
+
* the brief schema (`z.toJSONSchema(ReportBriefSchema)`) plus the authoring
|
|
14
|
+
* rules as its tool RESULT, so the full contract enters context only on a
|
|
15
|
+
* report-building turn — and thereafter rides in the cached history prefix,
|
|
16
|
+
* not at full price on every request.
|
|
17
|
+
* - `submit_report` — takes the composed brief and does the real work
|
|
18
|
+
* (pre-flight staging, previewId/versioning, `runReportIteration`, preview
|
|
19
|
+
* publishing). It accepts the brief as `unknown` so the 12k schema stays off
|
|
20
|
+
* the always-on surface, then validates it against `ReportBriefSchema` INSIDE
|
|
21
|
+
* `execute`, returning `{ ok: false, issues }` as DATA the model can fix (not
|
|
22
|
+
* a thrown error). That is the same trade `validate_plan` makes: a runtime
|
|
23
|
+
* contract in exchange for keeping the schema out of every request. The small
|
|
24
|
+
* iterate-mode + common fields (modifications / previewId / baseVersion /
|
|
25
|
+
* sources / format) stay fully typed — they cost little and guide iteration.
|
|
18
26
|
*
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
27
|
+
* On a valid brief, `submit_report`'s behaviour is identical to the former
|
|
28
|
+
* `iterate_report`: two modes (Create v1 via `report`; Iterate v2+ via
|
|
29
|
+
* `modifications` + `previewId`), pre-flight stages every source into the
|
|
30
|
+
* preview's assets/ dir, and it emits a `data-report-preview` chat data part on
|
|
31
|
+
* success or `data-report-preview-failed` on pre-flight/builder failure. The
|
|
32
|
+
* hosted preview surface is reached through an injected `PreviewPublisher` seam
|
|
33
|
+
* (managed default mints a short-lived run-authorization grant; local default
|
|
34
|
+
* returns "unavailable"). The 4 custom report tools the builder drives are
|
|
35
|
+
* constructed inside `runReportIteration` — see design.md Decision #9.
|
|
36
|
+
*
|
|
37
|
+
* Naming note: the report-builder SUB-agent has its OWN same-id `submit_report`
|
|
38
|
+
* terminal tool (`tools/report/submit-report.ts`). The two never share a tool
|
|
39
|
+
* roster — this one is the conversation agent's brief-submission entry point;
|
|
40
|
+
* that one is the builder's finalize gate.
|
|
21
41
|
*/
|
|
22
42
|
import { ok } from "neverthrow";
|
|
23
43
|
import { z } from "zod";
|
|
@@ -29,6 +49,7 @@ import { defineTool } from "./define-tool.js";
|
|
|
29
49
|
import { previewDir } from "../workspace/paths.js";
|
|
30
50
|
import { runReportIteration } from "../execution/report-runner.js";
|
|
31
51
|
import { formatBytes, stageReportAssets } from "./lib/report-preflight.js";
|
|
52
|
+
import { createNoopLogger } from "../lib/console-logger.js";
|
|
32
53
|
const REPORT_TOOL_ACCESS_TTL_SECONDS = 3600;
|
|
33
54
|
const PREVIEW_META_FILE = "preview-meta.json";
|
|
34
55
|
async function readPreviewMeta(metaPath) {
|
|
@@ -68,7 +89,7 @@ const SourceSchema = z.object({
|
|
|
68
89
|
const StatItemSchema = z.object({
|
|
69
90
|
label: z.string(),
|
|
70
91
|
value: z.union([z.string(), z.number()]),
|
|
71
|
-
unit: z.string().optional(),
|
|
92
|
+
unit: z.string().optional().describe("Unit rendered next to the value (e.g. '%', 'genes', 'kb')."),
|
|
72
93
|
});
|
|
73
94
|
const SortSpecSchema = z.object({
|
|
74
95
|
column: z.string(),
|
|
@@ -76,97 +97,180 @@ const SortSpecSchema = z.object({
|
|
|
76
97
|
});
|
|
77
98
|
const ChartTypeSchema = z.enum(["bar", "line", "scatter", "histogram", "box", "heatmap", "pie"]);
|
|
78
99
|
const ChartEncodingSchema = z.object({
|
|
79
|
-
x: z.string().optional(),
|
|
80
|
-
y: z
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
x: z.string().optional().describe("Column on the x / category axis."),
|
|
101
|
+
y: z
|
|
102
|
+
.union([z.string(), z.array(z.string()).min(1)])
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("Column on the y axis; an array plots one series per column."),
|
|
105
|
+
group: z.string().optional().describe("Column to split series (and colour) by."),
|
|
106
|
+
value: z.string().optional().describe("Value column for pie / heatmap-shaped charts."),
|
|
83
107
|
});
|
|
84
108
|
const INLINE_CHART_ROW_CAP = 500;
|
|
85
109
|
const ChartInlineDataSchema = z.object({
|
|
86
|
-
columns: z.array(z.string()).min(1),
|
|
110
|
+
columns: z.array(z.string()).min(1).describe("Column names of the inline rows, in render order."),
|
|
87
111
|
rows: z
|
|
88
112
|
.array(z.record(z.string(), z.union([z.string(), z.number(), z.null()])))
|
|
89
113
|
.min(1)
|
|
90
|
-
.max(INLINE_CHART_ROW_CAP)
|
|
114
|
+
.max(INLINE_CHART_ROW_CAP)
|
|
115
|
+
.describe("The pre-computed rows. Every value must come from a file you actually read — never fabricate, never estimate."),
|
|
91
116
|
source: z
|
|
92
117
|
.string()
|
|
93
118
|
.min(1)
|
|
94
|
-
.describe("
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"this as a footnote under the chart."),
|
|
119
|
+
.describe("Where the values came from — cite the real files (e.g. 'aggregated " +
|
|
120
|
+
"significant-gene counts from runs/r1/.../sig.csv, runs/r2/.../sig.csv'). " +
|
|
121
|
+
"The builder renders this verbatim as a footnote under the chart."),
|
|
98
122
|
});
|
|
123
|
+
/**
|
|
124
|
+
* The editorial-emphasis channel every section carries. A fresh instance per
|
|
125
|
+
* section type — Zod inlines reused schemas rather than sharing a `$defs` ref.
|
|
126
|
+
*/
|
|
127
|
+
const intentField = () => z
|
|
128
|
+
.string()
|
|
129
|
+
.describe("Why this section exists and what to emphasize (e.g. 'hero — headline " +
|
|
130
|
+
"finding', 'downplay if space is tight'). Emphasis, not layout — the " +
|
|
131
|
+
"builder picks the components, sizing and alternation.");
|
|
132
|
+
/** Free-text row transform — the reason to never pre-slice a CSV with `run_ephemeral`. */
|
|
133
|
+
const transformField = () => z
|
|
134
|
+
.string()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("Free-text row transform the builder applies client-side to the loaded " +
|
|
137
|
+
"asset — filter, derive, aggregate, sort (e.g. 'filter padj < 0.05 and " +
|
|
138
|
+
"abs(log2FoldChange) > 1', 'compute -log10(padj) as neg_log_padj', 'group " +
|
|
139
|
+
"by sample and sum count', 'sort by mean_count desc, take top 50'). Use " +
|
|
140
|
+
"this instead of running `run_ephemeral` to pre-slice the file. The text " +
|
|
141
|
+
"is rendered verbatim as a provenance footnote, so the data reads as " +
|
|
142
|
+
"processed, not fabricated.");
|
|
99
143
|
const SectionSchema = z.discriminatedUnion("type", [
|
|
100
|
-
z
|
|
144
|
+
z
|
|
145
|
+
.object({
|
|
101
146
|
type: z.literal("narrative"),
|
|
102
147
|
title: z.string(),
|
|
103
|
-
intent:
|
|
104
|
-
content: z.object({
|
|
105
|
-
|
|
106
|
-
|
|
148
|
+
intent: intentField(),
|
|
149
|
+
content: z.object({
|
|
150
|
+
prose: z.string().min(1).describe("Markdown prose you wrote. The builder never opens your data files to write it."),
|
|
151
|
+
}),
|
|
152
|
+
})
|
|
153
|
+
.describe("Prose you wrote — context, story, interpretation of the results."),
|
|
154
|
+
z
|
|
155
|
+
.object({
|
|
107
156
|
type: z.literal("metrics"),
|
|
108
157
|
title: z.string(),
|
|
109
|
-
intent:
|
|
110
|
-
content: z.object({
|
|
111
|
-
|
|
112
|
-
|
|
158
|
+
intent: intentField(),
|
|
159
|
+
content: z.object({
|
|
160
|
+
stats: z.array(StatItemSchema).min(1).describe("Headline numbers you already derived — the builder does not compute them."),
|
|
161
|
+
}),
|
|
162
|
+
})
|
|
163
|
+
.describe("Labeled numbers you extracted — headline stats, at-a-glance summary."),
|
|
164
|
+
z
|
|
165
|
+
.object({
|
|
113
166
|
type: z.literal("figure"),
|
|
114
167
|
title: z.string(),
|
|
115
|
-
intent:
|
|
168
|
+
intent: intentField(),
|
|
116
169
|
content: z.object({
|
|
117
|
-
imageAsset: z.string().min(1),
|
|
170
|
+
imageAsset: z.string().min(1).describe("Staged asset name, not a path — a `sources[].as`, or the basename of a `sources[].path`."),
|
|
118
171
|
caption: z.string().optional(),
|
|
119
172
|
}),
|
|
120
|
-
})
|
|
121
|
-
|
|
173
|
+
})
|
|
174
|
+
.describe("A static image the analysis already produced (PNG/SVG). Prefer `chart` " +
|
|
175
|
+
"whenever the underlying data file exists — a chart is interactive, themed " +
|
|
176
|
+
"and re-encodable on iteration. Reach for `figure` only when the user asks " +
|
|
177
|
+
"for the existing image, the visual is genuinely image-only (e.g. a heatmap " +
|
|
178
|
+
"baked by an R script with no exportable data), or the image carries " +
|
|
179
|
+
"annotations the data does not."),
|
|
180
|
+
z
|
|
181
|
+
.object({
|
|
122
182
|
type: z.literal("table"),
|
|
123
183
|
title: z.string(),
|
|
124
|
-
intent:
|
|
184
|
+
intent: intentField(),
|
|
125
185
|
content: z.object({
|
|
126
|
-
dataAsset: z.string().min(1),
|
|
127
|
-
columns: z.array(z.string()).optional(),
|
|
128
|
-
topN: z.number().int().positive().optional(),
|
|
129
|
-
sortBy: SortSpecSchema.optional(),
|
|
130
|
-
transform:
|
|
186
|
+
dataAsset: z.string().min(1).describe("Staged asset name, not a path — `.csv`, `.tsv`, or `.json` (array of row objects)."),
|
|
187
|
+
columns: z.array(z.string()).optional().describe("Column subset to render, in order. Omit to render every column."),
|
|
188
|
+
topN: z.number().int().positive().optional().describe("Keep only the first N rows, applied after `sortBy` and `transform`."),
|
|
189
|
+
sortBy: SortSpecSchema.optional().describe("Sort applied before `topN`."),
|
|
190
|
+
transform: transformField(),
|
|
131
191
|
caption: z.string().optional(),
|
|
132
192
|
}),
|
|
133
|
-
})
|
|
134
|
-
|
|
193
|
+
})
|
|
194
|
+
.describe("A tabular asset rendered as a table. Pick a column subset and `topN` that fit the audience."),
|
|
195
|
+
z
|
|
196
|
+
.object({
|
|
135
197
|
type: z.literal("chart"),
|
|
136
198
|
title: z.string(),
|
|
137
|
-
intent:
|
|
199
|
+
intent: intentField(),
|
|
138
200
|
content: z
|
|
139
201
|
.object({
|
|
140
|
-
dataAsset: z
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
202
|
+
dataAsset: z
|
|
203
|
+
.string()
|
|
204
|
+
.min(1)
|
|
205
|
+
.optional()
|
|
206
|
+
.describe("Staged asset name, not a path — `.csv`, `.tsv`, or `.json` (an array " +
|
|
207
|
+
"of row objects, or a pre-built ECharts option object the builder " +
|
|
208
|
+
"hands straight to `setOption`)."),
|
|
209
|
+
data: ChartInlineDataSchema.optional().describe("Escape hatch: inline rows for a cross-file aggregate you computed " +
|
|
210
|
+
"yourself from analysis files you ACTUALLY read (e.g. significant-gene " +
|
|
211
|
+
"counts pulled from several runs). Prefer `dataAsset`. More than one " +
|
|
212
|
+
"inline-data chart per report is a smell — the aggregate belongs in a " +
|
|
213
|
+
"persisted derived CSV."),
|
|
214
|
+
chartType: ChartTypeSchema.describe("Editorial choice — bar vs scatter changes the story. Always required, " + "including for a pre-built ECharts option asset."),
|
|
215
|
+
encoding: ChartEncodingSchema.describe("Which column feeds which channel, resolved AFTER `transform` — it may " +
|
|
216
|
+
"name a column the transform derives. Always required: pass `{}` when " +
|
|
217
|
+
"`dataAsset` is a pre-built ECharts option object."),
|
|
218
|
+
topN: z.number().int().positive().optional().describe("Keep only the first N rows, applied after `sortBy` and `transform`."),
|
|
219
|
+
sortBy: SortSpecSchema.optional().describe("Sort applied before `topN`."),
|
|
220
|
+
transform: transformField(),
|
|
147
221
|
caption: z.string().optional(),
|
|
148
222
|
})
|
|
149
223
|
.refine((v) => (v.dataAsset !== undefined) !== (v.data !== undefined), { message: "Chart content needs exactly one of `dataAsset` or `data`." })
|
|
150
224
|
.refine((v) => !(v.data !== undefined && v.transform !== undefined), {
|
|
151
225
|
message: "`transform` is for dataAsset only — inline `data` is already pre-computed.",
|
|
152
226
|
}),
|
|
153
|
-
})
|
|
154
|
-
|
|
227
|
+
})
|
|
228
|
+
.describe("A tabular asset (or inline rows) rendered as an interactive ECharts plot. " +
|
|
229
|
+
"Default to this over `figure` whenever the data file exists. To show the " +
|
|
230
|
+
"same data as both a chart and a table, write two sections against the same " +
|
|
231
|
+
"`dataAsset` — the file is staged once."),
|
|
232
|
+
z
|
|
233
|
+
.object({
|
|
155
234
|
type: z.literal("methods"),
|
|
156
235
|
title: z.string(),
|
|
157
|
-
intent:
|
|
158
|
-
content: z.object({
|
|
159
|
-
|
|
236
|
+
intent: intentField(),
|
|
237
|
+
content: z.object({
|
|
238
|
+
prose: z.string().min(1).describe("Markdown prose you wrote. The builder never opens your data files to write it."),
|
|
239
|
+
}),
|
|
240
|
+
})
|
|
241
|
+
.describe("Prose you wrote — what was done, how, and with which parameters."),
|
|
160
242
|
]);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
243
|
+
/**
|
|
244
|
+
* The report brief the conversation agent composes: title/audience + sources +
|
|
245
|
+
* the section union. This is the ~12k-char schema that `plan_report` hands back
|
|
246
|
+
* just-in-time and that `submit_report` validates its `report` field against
|
|
247
|
+
* inside `execute`. Exported for `plan_report`, `submit_report`, and the
|
|
248
|
+
* brief-shape tests.
|
|
249
|
+
*/
|
|
250
|
+
export const ReportBriefSchema = z.object({
|
|
251
|
+
title: z.string().describe("Report title. Persisted with the preview and reused as the card title on every later version."),
|
|
252
|
+
audience: z
|
|
253
|
+
.string()
|
|
254
|
+
.describe("Who reads this (e.g. 'wet-lab collaborators', 'a computational-biology PI'). The builder tunes tone, density and jargon to it."),
|
|
255
|
+
styleGuidance: z.string().optional().describe("Optional art direction for the builder (e.g. 'dense and print-ready', 'no hero imagery')."),
|
|
256
|
+
sources: z
|
|
257
|
+
.array(SourceSchema)
|
|
258
|
+
.default([])
|
|
259
|
+
.describe("Every file the report renders — CSV/TSV, PNG/SVG, JSON. A section's " +
|
|
260
|
+
"`imageAsset` / `dataAsset` must name one of these staged assets or the " +
|
|
261
|
+
"call fails. Do NOT list markdown or synthesis.json: those are inputs to " +
|
|
262
|
+
"the prose you write, not to the renderer."),
|
|
263
|
+
sections: z.array(SectionSchema).min(1).describe("The report's sections, in the order they are rendered."),
|
|
167
264
|
});
|
|
168
|
-
/**
|
|
169
|
-
|
|
265
|
+
/**
|
|
266
|
+
* `submit_report`'s always-on input surface. The heavy brief rides in `report`
|
|
267
|
+
* as `unknown` — typed that way ON PURPOSE so the ~12k `ReportBriefSchema` does
|
|
268
|
+
* NOT ship on every conversation turn; `execute` validates it against
|
|
269
|
+
* `ReportBriefSchema` and returns issues as data. The iterate-mode + common
|
|
270
|
+
* fields stay fully typed (they are small and guide iteration). Exported for
|
|
271
|
+
* the envelope-validation tests.
|
|
272
|
+
*/
|
|
273
|
+
export const submitReportInputSchema = z
|
|
170
274
|
.object({
|
|
171
275
|
previewId: z
|
|
172
276
|
.string()
|
|
@@ -174,36 +278,169 @@ export const iterateReportInputSchema = z
|
|
|
174
278
|
.max(64)
|
|
175
279
|
.optional()
|
|
176
280
|
.describe("Preview group ID. Omit for new reports (auto-generated). " + "Required for iterations — must match the existing preview."),
|
|
177
|
-
baseVersion: z
|
|
281
|
+
baseVersion: z
|
|
282
|
+
.number()
|
|
283
|
+
.int()
|
|
284
|
+
.min(1)
|
|
285
|
+
.optional()
|
|
286
|
+
.describe("Version to branch from (1-based) — use when the user prefers an earlier version. Defaults to the latest."),
|
|
178
287
|
format: z.enum(["html", "pdf"]).default("html").describe("Output format. Defaults to 'html' — only set explicitly for PDF."),
|
|
179
|
-
report:
|
|
288
|
+
report: z
|
|
289
|
+
.unknown()
|
|
290
|
+
.optional()
|
|
291
|
+
.describe("CREATION ONLY (v1): the composed report brief. Get its schema + " +
|
|
292
|
+
"authoring rules from `plan_report` first. Validated on submit — an " +
|
|
293
|
+
"invalid brief comes back as `{ ok: false, issues }`, not a thrown error. " +
|
|
294
|
+
"Mutually exclusive with `modifications`."),
|
|
180
295
|
modifications: z
|
|
181
296
|
.string()
|
|
182
297
|
.optional()
|
|
183
|
-
.describe("ITERATION ONLY (v2+). Natural-language
|
|
298
|
+
.describe("ITERATION ONLY (v2+). Natural-language change instructions. The " +
|
|
299
|
+
"report-builder sees ONLY this text plus the previous version's template " +
|
|
300
|
+
"— name what changes and leave everything else alone."),
|
|
184
301
|
sources: z
|
|
185
302
|
.array(SourceSchema)
|
|
186
303
|
.optional()
|
|
187
|
-
.describe("Iteration only — additional assets to stage on top of the existing " + "assets/ dir. For creation, put sources inside `
|
|
304
|
+
.describe("Iteration only — additional assets to stage on top of the existing " + "assets/ dir. For creation, put sources inside the brief's `sources`."),
|
|
188
305
|
})
|
|
189
306
|
.refine((data) => (data.report !== undefined) !== (data.modifications !== undefined), {
|
|
190
307
|
message: "Exactly one of `report` or `modifications` must be provided.",
|
|
191
308
|
})
|
|
192
|
-
.refine((data) => !(data.report && data.sources), {
|
|
193
|
-
message: "Top-level `sources` is for iteration mode. For creation, put sources inside `
|
|
309
|
+
.refine((data) => !(data.report !== undefined && data.sources), {
|
|
310
|
+
message: "Top-level `sources` is for iteration mode. For creation, put sources inside the brief's `sources`.",
|
|
194
311
|
});
|
|
195
|
-
|
|
312
|
+
// ── plan_report: the just-in-time authoring contract ────────────────
|
|
313
|
+
/**
|
|
314
|
+
* The authoring rules `plan_report` hands back alongside the brief schema.
|
|
315
|
+
* These are the cross-field, when-to-use rules the schema's per-field
|
|
316
|
+
* `.describe()` text cannot carry on its own — pulled from the former
|
|
317
|
+
* `iterate_report` description and `prompts/report-builder.ts`. Guidance for
|
|
318
|
+
* the conversation agent composing the brief, NOT for the builder.
|
|
319
|
+
*/
|
|
320
|
+
const REPORT_AUTHORING_RULES = `# Composing a report brief
|
|
321
|
+
|
|
322
|
+
The report-builder agent NEVER sees the analysis tree — it receives only the
|
|
323
|
+
brief you compose, so the brief must be complete. You write the prose, the
|
|
324
|
+
numbers, and the chart encodings; the builder only does layout and visual
|
|
325
|
+
treatment. Pass the composed brief as \`submit_report\`'s \`report\` field.
|
|
326
|
+
|
|
327
|
+
## Create vs iterate
|
|
328
|
+
- CREATE (v1): pass \`report\` (this brief), omit \`previewId\`. Every CSV / image /
|
|
329
|
+
JSON the report renders goes in \`report.sources\` — pre-flight stages each into
|
|
330
|
+
the preview's assets/ dir and parses its columns, first 5 rows, and row count
|
|
331
|
+
into the brief for you.
|
|
332
|
+
- ITERATE (v2+): pass \`modifications\` (natural-language change instructions)
|
|
333
|
+
plus the existing \`previewId\`, and NEVER \`report\` — passing \`report\` builds a
|
|
334
|
+
fresh report and discards all prior work. New data files go in the top-level
|
|
335
|
+
\`sources\`. \`baseVersion\` branches from an earlier version instead of the latest.
|
|
336
|
+
|
|
337
|
+
## Sources
|
|
338
|
+
- Every file the report renders — CSV/TSV, PNG/SVG, JSON — must be listed in
|
|
339
|
+
\`sources\`. A section's \`imageAsset\` / \`dataAsset\` must name one of these staged
|
|
340
|
+
assets (its \`as\`, or the basename of its \`path\`) or the call fails.
|
|
341
|
+
- Markdown is NOT a source. Keep summary.md / synthesis.json out of \`sources\`;
|
|
342
|
+
their content reaches the report as prose you write into \`narrative\` /
|
|
343
|
+
\`methods\` sections.
|
|
344
|
+
|
|
345
|
+
## Do NOT reach for run_ephemeral first
|
|
346
|
+
- Not to peek at a CSV — pre-flight already parsed its columns and head rows.
|
|
347
|
+
- Not to filter, slice, rank, or derive columns from a single CSV about to be
|
|
348
|
+
rendered — \`chart.content.transform\` and \`table.content.transform\` do exactly
|
|
349
|
+
that client-side, and the transform text renders as a provenance footnote.
|
|
350
|
+
- run_ephemeral is only for computation no single section transform covers:
|
|
351
|
+
cross-file aggregation, statistics needing a real numerical library, or a
|
|
352
|
+
derived CSV the report then lists as a fresh source.
|
|
353
|
+
|
|
354
|
+
## Choosing a section type
|
|
355
|
+
- narrative — prose you wrote: context, story, interpretation.
|
|
356
|
+
- methods — prose you wrote: what was done, how, with which parameters.
|
|
357
|
+
- metrics — labeled headline numbers you already derived (the builder does not
|
|
358
|
+
compute them).
|
|
359
|
+
- table — a tabular asset rendered as a table; pick a column subset + topN that
|
|
360
|
+
fit the audience.
|
|
361
|
+
- chart — a tabular asset (or inline rows) rendered as an interactive ECharts
|
|
362
|
+
plot. PREFER chart over figure whenever the data file exists — a chart is
|
|
363
|
+
interactive, themed, and re-encodable on iteration.
|
|
364
|
+
- figure — a static image the analysis already produced (PNG/SVG). Reach for it
|
|
365
|
+
only when the user asks for the existing image, the visual is genuinely
|
|
366
|
+
image-only, or the image carries annotations the data does not.
|
|
367
|
+
|
|
368
|
+
## Inline chart data
|
|
369
|
+
\`chart.content.data\` is an escape hatch for a cross-file aggregate you computed
|
|
370
|
+
yourself from files you ACTUALLY read. Prefer \`dataAsset\`. Never fabricate or
|
|
371
|
+
estimate values — every inline value must come from a file you read, and cite
|
|
372
|
+
the real files in \`data.source\`. More than one inline-data chart per report is a
|
|
373
|
+
smell — persist the aggregate as a derived CSV instead.
|
|
374
|
+
|
|
375
|
+
## Result
|
|
376
|
+
\`submit_report\` returns the preview id + version and emits a preview card.
|
|
377
|
+
Pre-flight and builder failures come back as an \`error\` string on the result,
|
|
378
|
+
not as a thrown error. An invalid brief comes back as \`{ ok: false, issues }\` —
|
|
379
|
+
fix the named fields and resubmit.`;
|
|
380
|
+
/** Precomputed once at module load — the brief schema `plan_report` returns as data. */
|
|
381
|
+
const REPORT_BRIEF_JSON_SCHEMA = z.toJSONSchema(ReportBriefSchema);
|
|
382
|
+
/**
|
|
383
|
+
* `plan_report` — the tiny, always-on trigger (no args). Its result delivers
|
|
384
|
+
* the brief schema + authoring rules just-in-time, so the ~12k contract enters
|
|
385
|
+
* context only on a report-building turn (and thereafter rides in the cached
|
|
386
|
+
* history prefix). Compose the brief from what it returns, then call
|
|
387
|
+
* `submit_report`. Pure logic, no deps — hence a module-scope leaf tool.
|
|
388
|
+
*/
|
|
389
|
+
export const planReportTool = defineTool({
|
|
390
|
+
id: "plan_report",
|
|
391
|
+
description: "Start building or iterating a report. Returns the report-brief schema " +
|
|
392
|
+
"and the authoring rules as its result — the full contract you compose " +
|
|
393
|
+
"against — so it is not carried on every turn. Call this first, compose " +
|
|
394
|
+
"the brief it describes, then call `submit_report` with it. (Iterating an " +
|
|
395
|
+
"existing preview with `modifications` needs no brief — you may go " +
|
|
396
|
+
"straight to `submit_report`.)",
|
|
397
|
+
inputSchema: z.object({}),
|
|
398
|
+
executionMode: "inline",
|
|
399
|
+
execute: async () => ok({
|
|
400
|
+
schema: REPORT_BRIEF_JSON_SCHEMA,
|
|
401
|
+
rules: REPORT_AUTHORING_RULES,
|
|
402
|
+
}),
|
|
403
|
+
});
|
|
404
|
+
/**
|
|
405
|
+
* `submit_report` — takes the composed brief and drives the report iteration.
|
|
406
|
+
* On a valid brief its behaviour is byte-for-byte the former `iterate_report`:
|
|
407
|
+
* pre-flight staging, previewId/versioning, `runReportIteration`, preview
|
|
408
|
+
* publishing. The brief rides as `unknown` on the wire (keeping its ~12k schema
|
|
409
|
+
* off the always-on surface) and is validated against `ReportBriefSchema` here.
|
|
410
|
+
*/
|
|
411
|
+
export function createReportSubmitTool(deps) {
|
|
196
412
|
return defineTool({
|
|
197
|
-
id: "
|
|
198
|
-
description: "
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"report
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
|
|
413
|
+
id: "submit_report",
|
|
414
|
+
description: "Submit a composed report brief to build or iterate an HTML/PDF report " +
|
|
415
|
+
"(the report-builder renders it; it never sees the analysis tree). Call " +
|
|
416
|
+
"`plan_report` FIRST to get the brief schema + authoring rules, compose " +
|
|
417
|
+
"the brief, then call this. CREATE: pass `report` (the composed brief), " +
|
|
418
|
+
"omit `previewId`. ITERATE: pass `modifications` + the existing " +
|
|
419
|
+
"`previewId`, and NEVER `report`. An invalid brief comes back as " +
|
|
420
|
+
"`{ ok: false, issues }` — fix the named fields and resubmit (see " +
|
|
421
|
+
"`plan_report` for the schema); pre-flight and builder failures come back " +
|
|
422
|
+
"as an `error` string. Returns the preview id + version and emits a " +
|
|
423
|
+
"preview card.",
|
|
424
|
+
inputSchema: submitReportInputSchema,
|
|
206
425
|
execute: async (input, ctx) => {
|
|
426
|
+
// The brief rides as `unknown` so its ~12k schema stays off the
|
|
427
|
+
// always-on tool surface. Validate it here and return issues as DATA
|
|
428
|
+
// (the validate_plan trade) — never a thrown error.
|
|
429
|
+
let brief;
|
|
430
|
+
if (input.report !== undefined) {
|
|
431
|
+
const parsed = ReportBriefSchema.safeParse(input.report);
|
|
432
|
+
if (!parsed.success) {
|
|
433
|
+
return ok({
|
|
434
|
+
ok: false,
|
|
435
|
+
issues: parsed.error.issues.map((i) => ({
|
|
436
|
+
path: i.path.join(".") || "(root)",
|
|
437
|
+
message: i.message,
|
|
438
|
+
})),
|
|
439
|
+
hint: "The `report` brief did not match the schema. Call `plan_report` for the full schema, fix the fields named in `issues`, and resubmit.",
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
brief = parsed.data;
|
|
443
|
+
}
|
|
207
444
|
const { resourceId } = scopeResource(ctx.session.scope);
|
|
208
445
|
const analysisRoot = deps.resolveWorkspaceRoot(resourceId);
|
|
209
446
|
// Zod fills the default at parse time but the static type keeps it
|
|
@@ -215,7 +452,7 @@ export function createIterateReportTool(deps) {
|
|
|
215
452
|
const metaPathAbs = join(previewRootAbs, PREVIEW_META_FILE);
|
|
216
453
|
// Iteration mode: recover the title from the creation-time meta file
|
|
217
454
|
// so the data-report-preview part keeps the original title across versions.
|
|
218
|
-
const existingMeta =
|
|
455
|
+
const existingMeta = brief ? null : await readPreviewMeta(metaPathAbs);
|
|
219
456
|
// Build the preview-publishing seam for this iteration. The managed
|
|
220
457
|
// default mints a 1h scoped access grant so preview calls outlive the chat
|
|
221
458
|
// turn; the local default returns an "unavailable" publisher.
|
|
@@ -239,7 +476,7 @@ export function createIterateReportTool(deps) {
|
|
|
239
476
|
});
|
|
240
477
|
}
|
|
241
478
|
// ── Pre-flight: stage declared sources into assets/. ────────
|
|
242
|
-
const sources =
|
|
479
|
+
const sources = brief?.sources ?? input.sources ?? [];
|
|
243
480
|
let staged = [];
|
|
244
481
|
if (sources.length > 0) {
|
|
245
482
|
const result = await stageReportAssets({
|
|
@@ -269,9 +506,9 @@ export function createIterateReportTool(deps) {
|
|
|
269
506
|
}
|
|
270
507
|
// Cross-check creation-mode briefs — every section asset reference
|
|
271
508
|
// must be present in staged[].
|
|
272
|
-
if (
|
|
509
|
+
if (brief) {
|
|
273
510
|
const stagedNames = new Set(staged.map((s) => s.name));
|
|
274
|
-
const missing = collectMissingAssetRefs(
|
|
511
|
+
const missing = collectMissingAssetRefs(brief.sections, stagedNames);
|
|
275
512
|
if (missing.length > 0) {
|
|
276
513
|
const reason = `section asset references not staged: ${missing.join(", ")} (pass them in report.sources)`;
|
|
277
514
|
await ctx.emit({
|
|
@@ -292,7 +529,7 @@ export function createIterateReportTool(deps) {
|
|
|
292
529
|
});
|
|
293
530
|
}
|
|
294
531
|
}
|
|
295
|
-
const prompt =
|
|
532
|
+
const prompt = brief ? buildCreationPrompt(brief, format, staged) : buildModificationPrompt(input.modifications, format, staged);
|
|
296
533
|
const result = await runReportIteration({
|
|
297
534
|
provider: deps.provider,
|
|
298
535
|
pool: deps.pool,
|
|
@@ -331,20 +568,21 @@ export function createIterateReportTool(deps) {
|
|
|
331
568
|
});
|
|
332
569
|
}
|
|
333
570
|
// Persist title on creation so iteration runs can recover it.
|
|
334
|
-
if (
|
|
571
|
+
if (brief) {
|
|
335
572
|
try {
|
|
336
573
|
await mkdir(previewRootAbs, { recursive: true });
|
|
337
574
|
await writePreviewMeta(metaPathAbs, {
|
|
338
|
-
title:
|
|
339
|
-
audience:
|
|
575
|
+
title: brief.title,
|
|
576
|
+
audience: brief.audience,
|
|
340
577
|
format,
|
|
341
578
|
});
|
|
342
579
|
}
|
|
343
580
|
catch (err) {
|
|
344
|
-
|
|
581
|
+
const logger = (deps.logger ?? createNoopLogger()).named("submit-report");
|
|
582
|
+
logger.warn("failed to persist preview meta", logger.errorFields(err));
|
|
345
583
|
}
|
|
346
584
|
}
|
|
347
|
-
const previewTitle =
|
|
585
|
+
const previewTitle = brief?.title ?? existingMeta?.title ?? "Report";
|
|
348
586
|
await ctx.emit({
|
|
349
587
|
type: "data-report-preview",
|
|
350
588
|
data: {
|
|
@@ -468,7 +706,9 @@ export function stagedAssetsBlock(staged, opts = {}) {
|
|
|
468
706
|
return out.join("\n");
|
|
469
707
|
}
|
|
470
708
|
function escapePipe(s) {
|
|
471
|
-
|
|
709
|
+
// Escape backslashes first so a literal `\` in the cell cannot combine with
|
|
710
|
+
// the escape we add and re-expose an unescaped `|` that breaks the table.
|
|
711
|
+
return s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
472
712
|
}
|
|
473
713
|
function formatSectionContent(s) {
|
|
474
714
|
switch (s.type) {
|
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used directly by target-assessment workflow steps and by tool wrappers.
|
|
5
5
|
*/
|
|
6
|
+
export interface ChemblCompound {
|
|
7
|
+
chemblId: string;
|
|
8
|
+
preferredCompoundName: string | null;
|
|
9
|
+
canonicalSmiles: string | null;
|
|
10
|
+
molecularWeight: number | null;
|
|
11
|
+
alogp: number | null;
|
|
12
|
+
molecularFormula: string | null;
|
|
13
|
+
}
|
|
14
|
+
/** How `searchCompounds` interprets its query string. */
|
|
15
|
+
export type CompoundSearchType = "target" | "compound" | "smiles";
|
|
6
16
|
export interface ChemblTarget {
|
|
7
17
|
targetChemblId: string;
|
|
8
18
|
preferredName: string | null;
|
|
@@ -53,6 +63,8 @@ export interface ChemblPolypharmHit {
|
|
|
53
63
|
standardUnits: string | null;
|
|
54
64
|
pchemblValue: number | null;
|
|
55
65
|
}
|
|
66
|
+
/** Search ChEMBL for compounds by target, compound name, or SMILES. */
|
|
67
|
+
export declare function searchCompounds(query: string, searchType: CompoundSearchType, limit?: number): Promise<ChemblCompound[]>;
|
|
56
68
|
export declare function searchTargets(query: string, limit?: number): Promise<ChemblTarget[]>;
|
|
57
69
|
export declare function getBioactivity(chemblId: string, type: "compound" | "target", options?: {
|
|
58
70
|
activityType?: string;
|