@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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECharts layout normalization — the renderer-side invariant behind `show_user(kind: "echart")`.
|
|
3
|
+
*
|
|
4
|
+
* The layout discipline (no duplicate title, bottom legend, grid margins that fit the axis labels,
|
|
5
|
+
* every category label shown, a downloadable chart) is applied HERE, deterministically, rather than
|
|
6
|
+
* being a checklist the model must remember on every chart it composes. A rule a model must remember
|
|
7
|
+
* is a rule it can forget.
|
|
8
|
+
*
|
|
9
|
+
* Two invariants govern every rule below:
|
|
10
|
+
*
|
|
11
|
+
* 1. **Defaults, not overrides.** Every property is filled in only when the author left it unset —
|
|
12
|
+
* an explicit, sensible authored `grid`/`legend`/`rotate`/filename is respected. The two forced
|
|
13
|
+
* fields are `title` (deleted: the `show_user` `title` param is the card heading, so an in-spec
|
|
14
|
+
* title is a genuine duplicate render) and `axisLabel.interval` (pinned to `0`: ECharts' default
|
|
15
|
+
* `"auto"` SILENTLY DROPS labels, which is the exact bug this rule exists to prevent).
|
|
16
|
+
* 2. **Never turn a valid chart invalid.** Nothing here is derived from data that may be absent: a
|
|
17
|
+
* property is only injected where it is meaningful (a `grid` only for a cartesian chart), and an
|
|
18
|
+
* unknown category count yields the safe layout rather than a guessed one.
|
|
19
|
+
*
|
|
20
|
+
* Pure: the input spec is never mutated. Modified branches are copy-on-write; untouched branches are
|
|
21
|
+
* shared by reference. `normalize(normalize(x))` deep-equals `normalize(x)`.
|
|
22
|
+
*/
|
|
23
|
+
/** Max length of a derived `saveAsImage` filename. */
|
|
24
|
+
const MAX_FILENAME_LEN = 64;
|
|
25
|
+
/** Fallback download filename when no heading is available to derive one from. */
|
|
26
|
+
const DEFAULT_FILENAME = "chart";
|
|
27
|
+
function isRecord(value) {
|
|
28
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
/** The axis entries of an `xAxis`/`yAxis` option, which ECharts accepts as one object or an array. */
|
|
31
|
+
function axisEntries(axis) {
|
|
32
|
+
if (Array.isArray(axis))
|
|
33
|
+
return axis.filter(isRecord);
|
|
34
|
+
return isRecord(axis) ? [axis] : [];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Kebab-case download filename: `"HMGCR — VST Expression"` → `"hmgcr-vst-expression"`.
|
|
38
|
+
* Diacritics are folded, every other non-alphanumeric run collapses to a single dash.
|
|
39
|
+
*/
|
|
40
|
+
export function kebabFilename(source) {
|
|
41
|
+
const slug = (source ?? "")
|
|
42
|
+
.normalize("NFKD")
|
|
43
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
46
|
+
.slice(0, MAX_FILENAME_LEN)
|
|
47
|
+
.replace(/^-+|-+$/g, "");
|
|
48
|
+
return slug.length > 0 ? slug : DEFAULT_FILENAME;
|
|
49
|
+
}
|
|
50
|
+
/** Declared series count. `0` = none declared, which is also "nothing a legend could label". */
|
|
51
|
+
function seriesCount(spec) {
|
|
52
|
+
const series = spec.series;
|
|
53
|
+
if (Array.isArray(series))
|
|
54
|
+
return series.length;
|
|
55
|
+
return isRecord(series) ? 1 : 0;
|
|
56
|
+
}
|
|
57
|
+
/** True when any series maps its categories to dataset COLUMNS rather than rows. */
|
|
58
|
+
function laysOutByRow(spec) {
|
|
59
|
+
const series = Array.isArray(spec.series) ? spec.series : [spec.series];
|
|
60
|
+
return series.some((s) => isRecord(s) && s.seriesLayoutBy === "row");
|
|
61
|
+
}
|
|
62
|
+
/** Header rows to discount from a `dataset.source` array, per ECharts' `sourceHeader` rules. */
|
|
63
|
+
function headerRowCount(dataset, source) {
|
|
64
|
+
const declared = dataset.sourceHeader;
|
|
65
|
+
if (declared === true)
|
|
66
|
+
return 1;
|
|
67
|
+
if (declared === false)
|
|
68
|
+
return 0;
|
|
69
|
+
if (typeof declared === "number" && Number.isFinite(declared)) {
|
|
70
|
+
return Math.min(Math.max(Math.trunc(declared), 0), source.length);
|
|
71
|
+
}
|
|
72
|
+
// `"auto"` (the default): a leading row of all-strings in front of further rows is dimension names.
|
|
73
|
+
const first = source[0];
|
|
74
|
+
return source.length > 1 && Array.isArray(first) && first.length > 0 && first.every((cell) => typeof cell === "string") ? 1 : 0;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Category count on the x axis, or `null` when the spec cannot tell us.
|
|
78
|
+
*
|
|
79
|
+
* `null` is the honest answer for a CSV-backed chart (`show_user`'s `dataPath`): the rows are loaded
|
|
80
|
+
* by the host at render time and deliberately never pulled through the model's context window, so
|
|
81
|
+
* they are NOT in the spec. Guessing a rotation for an axis of unknown length is worse than not
|
|
82
|
+
* rotating — an unrotated axis is also exactly what the other `null` cases (a value axis: scatter,
|
|
83
|
+
* time series) want. The count is used ONLY to pick a label rotation; it never touches the data, so
|
|
84
|
+
* an off-by-one at a bucket boundary costs at most a rotation, never a rendering.
|
|
85
|
+
*/
|
|
86
|
+
export function categoryCount(spec) {
|
|
87
|
+
for (const axis of axisEntries(spec.xAxis)) {
|
|
88
|
+
// An explicit category list is unambiguous — prefer it over any dataset inference.
|
|
89
|
+
if (Array.isArray(axis.data))
|
|
90
|
+
return axis.data.length;
|
|
91
|
+
}
|
|
92
|
+
// An array of datasets, an object-of-columns `source`, or a row-major layout: do not guess.
|
|
93
|
+
const dataset = spec.dataset;
|
|
94
|
+
if (!isRecord(dataset) || laysOutByRow(spec))
|
|
95
|
+
return null;
|
|
96
|
+
const source = dataset.source;
|
|
97
|
+
if (!Array.isArray(source) || source.length === 0)
|
|
98
|
+
return null;
|
|
99
|
+
return Math.max(0, source.length - headerRowCount(dataset, source));
|
|
100
|
+
}
|
|
101
|
+
/** The label rotation for `count` categories, or `null` for "leave the labels horizontal". */
|
|
102
|
+
function rotationFor(count) {
|
|
103
|
+
if (count === null || count <= 10)
|
|
104
|
+
return null;
|
|
105
|
+
// >20 categories: ECharts' own escape hatch is a `dataZoom` slider, but that rewrites the chart's
|
|
106
|
+
// interaction model and its vertical budget — a 90° turn is the mutation that cannot surprise.
|
|
107
|
+
return count <= 20 ? 45 : 90;
|
|
108
|
+
}
|
|
109
|
+
/** True when the legend (authored or defaulted) sits below the chart and consumes bottom margin. */
|
|
110
|
+
function isBottomLegend(legend) {
|
|
111
|
+
const entries = Array.isArray(legend) ? legend.filter(isRecord) : isRecord(legend) ? [legend] : [];
|
|
112
|
+
return entries.some((entry) => entry.show !== false && entry.bottom !== undefined);
|
|
113
|
+
}
|
|
114
|
+
/** True when a component option carries any explicit placement — adding another would fight it. */
|
|
115
|
+
function hasPlacement(option) {
|
|
116
|
+
return option.left !== undefined || option.right !== undefined || option.top !== undefined || option.bottom !== undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Apply the ECharts layout invariant to an author-composed spec.
|
|
120
|
+
*
|
|
121
|
+
* - `title` is deleted (always) — the `show_user` `title` param is the visible card heading, and
|
|
122
|
+
* `title.subtext` is the documented misuse for stats that belong in the tooltip.
|
|
123
|
+
* - `legend` defaults to `{ bottom: 0 }` for a multi-series chart and `{ show: false }` for a
|
|
124
|
+
* single-series one (nothing to disambiguate); an authored legend is left alone.
|
|
125
|
+
* - `grid` (cartesian charts only) gets `top` 8% — 12% when a `graphic` annotation shares the canvas
|
|
126
|
+
* — `bottom` 20% / 25% (rotated labels) / 30% (rotated labels above a bottom legend), `left` 10%,
|
|
127
|
+
* `right` 5%. Only unset keys are filled.
|
|
128
|
+
* - `axisLabel.interval` is pinned to `0` on every axis; the x axis is rotated 45° (11–20 categories)
|
|
129
|
+
* or 90° (>20) unless the author chose a rotation.
|
|
130
|
+
* - `toolbox.feature.saveAsImage` is injected with `type: "png"` and a kebab-case filename derived
|
|
131
|
+
* from the `show_user` title (falling back to the stripped in-spec title, then `"chart"`).
|
|
132
|
+
*/
|
|
133
|
+
export function normalizeEchartSpec(spec, opts = {}) {
|
|
134
|
+
const { title: specTitle, ...out } = spec;
|
|
135
|
+
// ── Legend ──────────────────────────────────────────────────────────
|
|
136
|
+
const legendAuthored = out.legend !== undefined && out.legend !== null;
|
|
137
|
+
if (!legendAuthored) {
|
|
138
|
+
const count = seriesCount(out);
|
|
139
|
+
if (count >= 2)
|
|
140
|
+
out.legend = { bottom: 0 };
|
|
141
|
+
// A single series has nothing to disambiguate: hide it explicitly rather than omit it, so a
|
|
142
|
+
// themed legend component cannot reintroduce an orphan swatch. An undeclared series count
|
|
143
|
+
// (0) gets no legend option at all — there is nothing to show or hide.
|
|
144
|
+
else if (count === 1)
|
|
145
|
+
out.legend = { show: false };
|
|
146
|
+
}
|
|
147
|
+
const bottomLegend = isBottomLegend(out.legend);
|
|
148
|
+
// ── Axis labels ─────────────────────────────────────────────────────
|
|
149
|
+
const rotate = rotationFor(categoryCount(out));
|
|
150
|
+
let rotated = false;
|
|
151
|
+
// Rotation is an x-axis rule: turning a horizontal bar chart's y-axis category labels on their
|
|
152
|
+
// side would be actively wrong, so only `interval` crosses to the y axis (where it is inert on a
|
|
153
|
+
// value axis and prevents silent label-skipping on a category one).
|
|
154
|
+
const normalizeAxis = (axis, isX) => {
|
|
155
|
+
const axisLabel = isRecord(axis.axisLabel) ? { ...axis.axisLabel } : {};
|
|
156
|
+
axisLabel.interval = 0;
|
|
157
|
+
if (isX && rotate !== null && axisLabel.rotate === undefined)
|
|
158
|
+
axisLabel.rotate = rotate;
|
|
159
|
+
if (isX && typeof axisLabel.rotate === "number" && axisLabel.rotate !== 0)
|
|
160
|
+
rotated = true;
|
|
161
|
+
return { ...axis, axisLabel };
|
|
162
|
+
};
|
|
163
|
+
for (const key of ["xAxis", "yAxis"]) {
|
|
164
|
+
const axis = out[key];
|
|
165
|
+
// A malformed entry is passed through rather than dropped — normalization rewrites layout,
|
|
166
|
+
// never the shape of the spec it was handed.
|
|
167
|
+
if (Array.isArray(axis))
|
|
168
|
+
out[key] = axis.map((entry) => (isRecord(entry) ? normalizeAxis(entry, key === "xAxis") : entry));
|
|
169
|
+
else if (isRecord(axis))
|
|
170
|
+
out[key] = normalizeAxis(axis, key === "xAxis");
|
|
171
|
+
}
|
|
172
|
+
// ── Grid ────────────────────────────────────────────────────────────
|
|
173
|
+
const cartesian = out.xAxis !== undefined || out.yAxis !== undefined;
|
|
174
|
+
const grid = out.grid;
|
|
175
|
+
// An array of grids, or a grid the author sized explicitly (`width`/`height`): a margin added on
|
|
176
|
+
// top of an explicit box could over-constrain it. Non-cartesian charts (pie, sunburst, graph)
|
|
177
|
+
// have no grid to lay out.
|
|
178
|
+
const gridNormalizable = cartesian && !Array.isArray(grid) && (grid === undefined || (isRecord(grid) && grid.width === undefined && grid.height === undefined));
|
|
179
|
+
if (gridNormalizable) {
|
|
180
|
+
const authored = isRecord(grid) ? grid : {};
|
|
181
|
+
out.grid = {
|
|
182
|
+
...authored,
|
|
183
|
+
...(authored.top === undefined ? { top: out.graphic !== undefined ? "12%" : "8%" } : {}),
|
|
184
|
+
...(authored.bottom === undefined ? { bottom: rotated ? (bottomLegend ? "30%" : "25%") : "20%" } : {}),
|
|
185
|
+
...(authored.left === undefined ? { left: "10%" } : {}),
|
|
186
|
+
...(authored.right === undefined ? { right: "5%" } : {}),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// ── Toolbox ─────────────────────────────────────────────────────────
|
|
190
|
+
if (!Array.isArray(out.toolbox)) {
|
|
191
|
+
const authored = isRecord(out.toolbox) ? out.toolbox : {};
|
|
192
|
+
const feature = isRecord(authored.feature) ? authored.feature : {};
|
|
193
|
+
const saveAsImage = isRecord(feature.saveAsImage) ? feature.saveAsImage : {};
|
|
194
|
+
// The in-spec title is stripped, but it is still the author's own description of the chart —
|
|
195
|
+
// a better filename seed than the generic fallback when no card title was given.
|
|
196
|
+
const specTitleText = isRecord(specTitle) && typeof specTitle.text === "string" ? specTitle.text : undefined;
|
|
197
|
+
out.toolbox = {
|
|
198
|
+
...authored,
|
|
199
|
+
...(hasPlacement(authored) ? {} : { right: 0, top: 0 }),
|
|
200
|
+
feature: {
|
|
201
|
+
...feature,
|
|
202
|
+
saveAsImage: {
|
|
203
|
+
...saveAsImage,
|
|
204
|
+
...(saveAsImage.type === undefined ? { type: "png" } : {}),
|
|
205
|
+
...(saveAsImage.name === undefined ? { name: kebabFilename(opts.title ?? specTitleText) } : {}),
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* downstream reconciliation concern of the emit pipeline (change 3), not a
|
|
10
10
|
* per-request `Set` smuggled through an ambient context bag.
|
|
11
11
|
*
|
|
12
|
+
* An `echart` spec's layout is normalized (`normalize-echart-spec.ts`) inside
|
|
13
|
+
* `buildPresentationCardData`, the one construction site both this tool and the
|
|
14
|
+
* reconstruct-on-read path share — the model authors the data, the renderer owns
|
|
15
|
+
* the layout.
|
|
16
|
+
*
|
|
12
17
|
* For stored plans, use `show_plan`. For existing files, use `show_file`.
|
|
13
18
|
*/
|
|
14
19
|
type ShowUserOutput = {
|
|
@@ -18,7 +23,7 @@ type ShowUserOutput = {
|
|
|
18
23
|
id: string;
|
|
19
24
|
};
|
|
20
25
|
export declare const showUserTool: import("../define-tool.js").Tool<{
|
|
21
|
-
kind: "code" | "
|
|
26
|
+
kind: "code" | "markdown" | "echart" | "svg" | "table";
|
|
22
27
|
title?: string | undefined;
|
|
23
28
|
spec?: Record<string, unknown> | undefined;
|
|
24
29
|
dataPath?: string | undefined;
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* downstream reconciliation concern of the emit pipeline (change 3), not a
|
|
10
10
|
* per-request `Set` smuggled through an ambient context bag.
|
|
11
11
|
*
|
|
12
|
+
* An `echart` spec's layout is normalized (`normalize-echart-spec.ts`) inside
|
|
13
|
+
* `buildPresentationCardData`, the one construction site both this tool and the
|
|
14
|
+
* reconstruct-on-read path share — the model authors the data, the renderer owns
|
|
15
|
+
* the layout.
|
|
16
|
+
*
|
|
12
17
|
* For stored plans, use `show_plan`. For existing files, use `show_file`.
|
|
13
18
|
*/
|
|
14
19
|
import { ok } from "neverthrow";
|
|
@@ -22,12 +27,18 @@ import { validatePath } from "../lib/path-validation.js";
|
|
|
22
27
|
// LLM picks the right ones based on `kind`.
|
|
23
28
|
const ShowUserInputSchema = z.object({
|
|
24
29
|
kind: z.enum(["echart", "markdown", "code", "svg", "table"]).describe("The type of agent-synthesized content to display"),
|
|
25
|
-
title: z
|
|
26
|
-
|
|
30
|
+
title: z
|
|
31
|
+
.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Card heading rendered above the content. For an echart it is the chart's only title, and it seeds the PNG download filename."),
|
|
34
|
+
spec: z
|
|
35
|
+
.record(z.string(), z.unknown())
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Full ECharts option JSON (kind=echart). Author the data, series, `encode`, and tooltip — and OMIT `title`: the card heading is the `title` param, so an in-spec title is stripped (put stats in `tooltip.formatter`, never in `title.subtext`). Layout is normalized for you at render time (legend placement, grid margins, `axisLabel.interval`/rotation, and a PNG `saveAsImage` toolbox), so do not hand-tune it."),
|
|
27
38
|
dataPath: z
|
|
28
39
|
.string()
|
|
29
40
|
.optional()
|
|
30
|
-
.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 and omit `dataset.source` from the spec — the
|
|
41
|
+
.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. If the data is not chart-ready (needs aggregation, filtering, reshaping), do that in a sandbox step that writes a chart-ready CSV. Inline `dataset.source` is only for a handful of numbers you just computed in conversation that exist nowhere as an artifact."),
|
|
31
42
|
body: z.string().optional().describe("Markdown content to render (kind=markdown)"),
|
|
32
43
|
code: z.string().optional().describe("Code content (kind=code)"),
|
|
33
44
|
language: z.string().optional().describe("Language identifier, e.g. r, python, bash (kind=code)"),
|
|
@@ -38,15 +49,17 @@ const ShowUserInputSchema = z.object({
|
|
|
38
49
|
});
|
|
39
50
|
export const showUserTool = defineTool({
|
|
40
51
|
id: "show_user",
|
|
41
|
-
description: "Display
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
52
|
+
description: "Display content you are INVENTING — a chart you composed, a code snippet you are proposing, a markdown " +
|
|
53
|
+
"synthesis, an SVG diagram, a table you built. The content is inlined on the wire. " +
|
|
54
|
+
"Pick this tool by what you are referencing, not by how the output looks: " +
|
|
55
|
+
"NOT for an existing analysis file — never read an artifact and paste its bytes here, reference it with `show_file` " +
|
|
56
|
+
"(images, CSVs, PDFs, notebooks). NOT for a stored plan — use `show_plan`. " +
|
|
57
|
+
"NEVER put markdown image syntax pointing at a workspace file in a `markdown` body: `` " +
|
|
58
|
+
"renders as a broken image, because the chat UI cannot resolve workspace-relative paths. To interleave figures " +
|
|
59
|
+
"with prose (a write-up, a narrative summary), emit a SEQUENCE of cards in display order — `show_user(markdown)` " +
|
|
60
|
+
"for a prose section, then `show_file` for the figures that follow it, and so on — instead of one monolithic card. " +
|
|
61
|
+
"One card per call, rendered in call order; an identical repeat call resolves to the same card, so compose the " +
|
|
62
|
+
"finished content before calling rather than re-rendering a draft.",
|
|
50
63
|
inputSchema: ShowUserInputSchema,
|
|
51
64
|
execute: async (input, ctx) => {
|
|
52
65
|
// `dataPath` is a reference the host resolves at render time; validate its
|
|
@@ -32,7 +32,6 @@ import { buildRunCardData } from "../memory/card-builders.js";
|
|
|
32
32
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
33
33
|
import { validatePlan } from "../schemas/validate-plan.js";
|
|
34
34
|
import { AnalysisPlanSchema } from "../schemas/workflow-state.js";
|
|
35
|
-
import { renderStepPrompt } from "../schemas/render-step-prompt.js";
|
|
36
35
|
import { RunDedupCollisionError, insertRun, loadPlan, queryActiveRun, updateRunStatus } from "../state/index.js";
|
|
37
36
|
import { defineTool } from "./define-tool.js";
|
|
38
37
|
const planIdSchema = z.string().regex(/^pln-[a-f0-9]{8}$/, "planId must be a pln-<8hex> value");
|
|
@@ -177,7 +176,11 @@ export function createExecutePlanTool(deps) {
|
|
|
177
176
|
id: s.id,
|
|
178
177
|
depends_on: s.depends_on ?? [],
|
|
179
178
|
})),
|
|
180
|
-
|
|
179
|
+
// The step DATA, not a rendered prompt: each step's seed is composed
|
|
180
|
+
// by the scheduler at dispatch, when its dependencies' results exist.
|
|
181
|
+
// A prompt frozen here — before the first step has run — could never
|
|
182
|
+
// mention them.
|
|
183
|
+
planStepById: Object.fromEntries(plan.steps.map((s) => [s.id, s])),
|
|
181
184
|
agentByStepId: Object.fromEntries(plan.steps.map((s) => [s.id, s.agent ?? "unknown"])),
|
|
182
185
|
resourcesByStepId: Object.fromEntries(plan.steps.map((s) => {
|
|
183
186
|
if (!s.resources) {
|
|
@@ -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 type { Pool } from "pg";
|
|
23
43
|
import { z } from "zod";
|
|
@@ -27,119 +47,145 @@ import { type ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
|
27
47
|
import { type StagedAsset } from "./lib/report-preflight.js";
|
|
28
48
|
import type { PreviewPublisher } from "./report/preview-publisher.js";
|
|
29
49
|
import type { ChromeConfig } from "../lib/chrome.js";
|
|
30
|
-
|
|
31
|
-
|
|
50
|
+
import type { Logger } from "../lib/logger.js";
|
|
51
|
+
/**
|
|
52
|
+
* The report brief the conversation agent composes: title/audience + sources +
|
|
53
|
+
* the section union. This is the ~12k-char schema that `plan_report` hands back
|
|
54
|
+
* just-in-time and that `submit_report` validates its `report` field against
|
|
55
|
+
* inside `execute`. Exported for `plan_report`, `submit_report`, and the
|
|
56
|
+
* brief-shape tests.
|
|
57
|
+
*/
|
|
58
|
+
export declare const ReportBriefSchema: z.ZodObject<{
|
|
59
|
+
title: z.ZodString;
|
|
60
|
+
audience: z.ZodString;
|
|
61
|
+
styleGuidance: z.ZodOptional<z.ZodString>;
|
|
62
|
+
sources: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
63
|
+
path: z.ZodString;
|
|
64
|
+
as: z.ZodOptional<z.ZodString>;
|
|
65
|
+
}, z.core.$strip>>>;
|
|
66
|
+
sections: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
67
|
+
type: z.ZodLiteral<"narrative">;
|
|
68
|
+
title: z.ZodString;
|
|
69
|
+
intent: z.ZodString;
|
|
70
|
+
content: z.ZodObject<{
|
|
71
|
+
prose: z.ZodString;
|
|
72
|
+
}, z.core.$strip>;
|
|
73
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
74
|
+
type: z.ZodLiteral<"metrics">;
|
|
75
|
+
title: z.ZodString;
|
|
76
|
+
intent: z.ZodString;
|
|
77
|
+
content: z.ZodObject<{
|
|
78
|
+
stats: z.ZodArray<z.ZodObject<{
|
|
79
|
+
label: z.ZodString;
|
|
80
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
81
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
82
|
+
}, z.core.$strip>>;
|
|
83
|
+
}, z.core.$strip>;
|
|
84
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
85
|
+
type: z.ZodLiteral<"figure">;
|
|
86
|
+
title: z.ZodString;
|
|
87
|
+
intent: z.ZodString;
|
|
88
|
+
content: z.ZodObject<{
|
|
89
|
+
imageAsset: z.ZodString;
|
|
90
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
93
|
+
type: z.ZodLiteral<"table">;
|
|
94
|
+
title: z.ZodString;
|
|
95
|
+
intent: z.ZodString;
|
|
96
|
+
content: z.ZodObject<{
|
|
97
|
+
dataAsset: z.ZodString;
|
|
98
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
99
|
+
topN: z.ZodOptional<z.ZodNumber>;
|
|
100
|
+
sortBy: z.ZodOptional<z.ZodObject<{
|
|
101
|
+
column: z.ZodString;
|
|
102
|
+
order: z.ZodEnum<{
|
|
103
|
+
desc: "desc";
|
|
104
|
+
asc: "asc";
|
|
105
|
+
}>;
|
|
106
|
+
}, z.core.$strip>>;
|
|
107
|
+
transform: z.ZodOptional<z.ZodString>;
|
|
108
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
109
|
+
}, z.core.$strip>;
|
|
110
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
111
|
+
type: z.ZodLiteral<"chart">;
|
|
112
|
+
title: z.ZodString;
|
|
113
|
+
intent: z.ZodString;
|
|
114
|
+
content: z.ZodObject<{
|
|
115
|
+
dataAsset: z.ZodOptional<z.ZodString>;
|
|
116
|
+
data: z.ZodOptional<z.ZodObject<{
|
|
117
|
+
columns: z.ZodArray<z.ZodString>;
|
|
118
|
+
rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodNull]>>>;
|
|
119
|
+
source: z.ZodString;
|
|
120
|
+
}, z.core.$strip>>;
|
|
121
|
+
chartType: z.ZodEnum<{
|
|
122
|
+
line: "line";
|
|
123
|
+
bar: "bar";
|
|
124
|
+
scatter: "scatter";
|
|
125
|
+
histogram: "histogram";
|
|
126
|
+
box: "box";
|
|
127
|
+
heatmap: "heatmap";
|
|
128
|
+
pie: "pie";
|
|
129
|
+
}>;
|
|
130
|
+
encoding: z.ZodObject<{
|
|
131
|
+
x: z.ZodOptional<z.ZodString>;
|
|
132
|
+
y: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
133
|
+
group: z.ZodOptional<z.ZodString>;
|
|
134
|
+
value: z.ZodOptional<z.ZodString>;
|
|
135
|
+
}, z.core.$strip>;
|
|
136
|
+
topN: z.ZodOptional<z.ZodNumber>;
|
|
137
|
+
sortBy: z.ZodOptional<z.ZodObject<{
|
|
138
|
+
column: z.ZodString;
|
|
139
|
+
order: z.ZodEnum<{
|
|
140
|
+
desc: "desc";
|
|
141
|
+
asc: "asc";
|
|
142
|
+
}>;
|
|
143
|
+
}, z.core.$strip>>;
|
|
144
|
+
transform: z.ZodOptional<z.ZodString>;
|
|
145
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
146
|
+
}, z.core.$strip>;
|
|
147
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
148
|
+
type: z.ZodLiteral<"methods">;
|
|
149
|
+
title: z.ZodString;
|
|
150
|
+
intent: z.ZodString;
|
|
151
|
+
content: z.ZodObject<{
|
|
152
|
+
prose: z.ZodString;
|
|
153
|
+
}, z.core.$strip>;
|
|
154
|
+
}, z.core.$strip>], "type">>;
|
|
155
|
+
}, z.core.$strip>;
|
|
156
|
+
/**
|
|
157
|
+
* `submit_report`'s always-on input surface. The heavy brief rides in `report`
|
|
158
|
+
* as `unknown` — typed that way ON PURPOSE so the ~12k `ReportBriefSchema` does
|
|
159
|
+
* NOT ship on every conversation turn; `execute` validates it against
|
|
160
|
+
* `ReportBriefSchema` and returns issues as data. The iterate-mode + common
|
|
161
|
+
* fields stay fully typed (they are small and guide iteration). Exported for
|
|
162
|
+
* the envelope-validation tests.
|
|
163
|
+
*/
|
|
164
|
+
export declare const submitReportInputSchema: z.ZodObject<{
|
|
32
165
|
previewId: z.ZodOptional<z.ZodString>;
|
|
33
166
|
baseVersion: z.ZodOptional<z.ZodNumber>;
|
|
34
167
|
format: z.ZodDefault<z.ZodEnum<{
|
|
35
168
|
html: "html";
|
|
36
169
|
pdf: "pdf";
|
|
37
170
|
}>>;
|
|
38
|
-
report: z.ZodOptional<z.
|
|
39
|
-
title: z.ZodString;
|
|
40
|
-
audience: z.ZodString;
|
|
41
|
-
styleGuidance: z.ZodOptional<z.ZodString>;
|
|
42
|
-
sources: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
43
|
-
path: z.ZodString;
|
|
44
|
-
as: z.ZodOptional<z.ZodString>;
|
|
45
|
-
}, z.core.$strip>>>;
|
|
46
|
-
sections: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
47
|
-
type: z.ZodLiteral<"narrative">;
|
|
48
|
-
title: z.ZodString;
|
|
49
|
-
intent: z.ZodString;
|
|
50
|
-
content: z.ZodObject<{
|
|
51
|
-
prose: z.ZodString;
|
|
52
|
-
}, z.core.$strip>;
|
|
53
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
54
|
-
type: z.ZodLiteral<"metrics">;
|
|
55
|
-
title: z.ZodString;
|
|
56
|
-
intent: z.ZodString;
|
|
57
|
-
content: z.ZodObject<{
|
|
58
|
-
stats: z.ZodArray<z.ZodObject<{
|
|
59
|
-
label: z.ZodString;
|
|
60
|
-
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
61
|
-
unit: z.ZodOptional<z.ZodString>;
|
|
62
|
-
}, z.core.$strip>>;
|
|
63
|
-
}, z.core.$strip>;
|
|
64
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
65
|
-
type: z.ZodLiteral<"figure">;
|
|
66
|
-
title: z.ZodString;
|
|
67
|
-
intent: z.ZodString;
|
|
68
|
-
content: z.ZodObject<{
|
|
69
|
-
imageAsset: z.ZodString;
|
|
70
|
-
caption: z.ZodOptional<z.ZodString>;
|
|
71
|
-
}, z.core.$strip>;
|
|
72
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
73
|
-
type: z.ZodLiteral<"table">;
|
|
74
|
-
title: z.ZodString;
|
|
75
|
-
intent: z.ZodString;
|
|
76
|
-
content: z.ZodObject<{
|
|
77
|
-
dataAsset: z.ZodString;
|
|
78
|
-
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
79
|
-
topN: z.ZodOptional<z.ZodNumber>;
|
|
80
|
-
sortBy: z.ZodOptional<z.ZodObject<{
|
|
81
|
-
column: z.ZodString;
|
|
82
|
-
order: z.ZodEnum<{
|
|
83
|
-
desc: "desc";
|
|
84
|
-
asc: "asc";
|
|
85
|
-
}>;
|
|
86
|
-
}, z.core.$strip>>;
|
|
87
|
-
transform: z.ZodOptional<z.ZodString>;
|
|
88
|
-
caption: z.ZodOptional<z.ZodString>;
|
|
89
|
-
}, z.core.$strip>;
|
|
90
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
91
|
-
type: z.ZodLiteral<"chart">;
|
|
92
|
-
title: z.ZodString;
|
|
93
|
-
intent: z.ZodString;
|
|
94
|
-
content: z.ZodObject<{
|
|
95
|
-
dataAsset: z.ZodOptional<z.ZodString>;
|
|
96
|
-
data: z.ZodOptional<z.ZodObject<{
|
|
97
|
-
columns: z.ZodArray<z.ZodString>;
|
|
98
|
-
rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodNull]>>>;
|
|
99
|
-
source: z.ZodString;
|
|
100
|
-
}, z.core.$strip>>;
|
|
101
|
-
chartType: z.ZodEnum<{
|
|
102
|
-
line: "line";
|
|
103
|
-
bar: "bar";
|
|
104
|
-
scatter: "scatter";
|
|
105
|
-
histogram: "histogram";
|
|
106
|
-
box: "box";
|
|
107
|
-
heatmap: "heatmap";
|
|
108
|
-
pie: "pie";
|
|
109
|
-
}>;
|
|
110
|
-
encoding: z.ZodObject<{
|
|
111
|
-
x: z.ZodOptional<z.ZodString>;
|
|
112
|
-
y: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
113
|
-
group: z.ZodOptional<z.ZodString>;
|
|
114
|
-
value: z.ZodOptional<z.ZodString>;
|
|
115
|
-
}, z.core.$strip>;
|
|
116
|
-
topN: z.ZodOptional<z.ZodNumber>;
|
|
117
|
-
sortBy: z.ZodOptional<z.ZodObject<{
|
|
118
|
-
column: z.ZodString;
|
|
119
|
-
order: z.ZodEnum<{
|
|
120
|
-
desc: "desc";
|
|
121
|
-
asc: "asc";
|
|
122
|
-
}>;
|
|
123
|
-
}, z.core.$strip>>;
|
|
124
|
-
transform: z.ZodOptional<z.ZodString>;
|
|
125
|
-
caption: z.ZodOptional<z.ZodString>;
|
|
126
|
-
}, z.core.$strip>;
|
|
127
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
128
|
-
type: z.ZodLiteral<"methods">;
|
|
129
|
-
title: z.ZodString;
|
|
130
|
-
intent: z.ZodString;
|
|
131
|
-
content: z.ZodObject<{
|
|
132
|
-
prose: z.ZodString;
|
|
133
|
-
}, z.core.$strip>;
|
|
134
|
-
}, z.core.$strip>], "type">>;
|
|
135
|
-
}, z.core.$strip>>;
|
|
171
|
+
report: z.ZodOptional<z.ZodUnknown>;
|
|
136
172
|
modifications: z.ZodOptional<z.ZodString>;
|
|
137
173
|
sources: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
138
174
|
path: z.ZodString;
|
|
139
175
|
as: z.ZodOptional<z.ZodString>;
|
|
140
176
|
}, z.core.$strip>>>;
|
|
141
177
|
}, z.core.$strip>;
|
|
142
|
-
|
|
178
|
+
/**
|
|
179
|
+
* `plan_report` — the tiny, always-on trigger (no args). Its result delivers
|
|
180
|
+
* the brief schema + authoring rules just-in-time, so the ~12k contract enters
|
|
181
|
+
* context only on a report-building turn (and thereafter rides in the cached
|
|
182
|
+
* history prefix). Compose the brief from what it returns, then call
|
|
183
|
+
* `submit_report`. Pure logic, no deps — hence a module-scope leaf tool.
|
|
184
|
+
*/
|
|
185
|
+
export declare const planReportTool: Tool;
|
|
186
|
+
export interface SubmitReportDeps {
|
|
187
|
+
/** Operational logging seam; omitted falls back to no-op. */
|
|
188
|
+
readonly logger?: Logger;
|
|
143
189
|
readonly provider: ChatProvider;
|
|
144
190
|
readonly pool: Pool;
|
|
145
191
|
/** Embedder-supplied workspace-root resolution seam (see workspace/paths.ts). */
|
|
@@ -165,7 +211,14 @@ export interface IterateReportDeps {
|
|
|
165
211
|
ttlSeconds: number;
|
|
166
212
|
}) => Promise<PreviewPublisher>;
|
|
167
213
|
}
|
|
168
|
-
|
|
214
|
+
/**
|
|
215
|
+
* `submit_report` — takes the composed brief and drives the report iteration.
|
|
216
|
+
* On a valid brief its behaviour is byte-for-byte the former `iterate_report`:
|
|
217
|
+
* pre-flight staging, previewId/versioning, `runReportIteration`, preview
|
|
218
|
+
* publishing. The brief rides as `unknown` on the wire (keeping its ~12k schema
|
|
219
|
+
* off the always-on surface) and is validated against `ReportBriefSchema` here.
|
|
220
|
+
*/
|
|
221
|
+
export declare function createReportSubmitTool(deps: SubmitReportDeps): Tool;
|
|
169
222
|
/** Exported for unit tests — pipe-escape correctness. */
|
|
170
223
|
export declare function stagedAssetsBlock(staged: StagedAsset[], opts?: {
|
|
171
224
|
newOnly?: boolean;
|