@inflexa-ai/harness 0.45.0 → 0.46.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 +6 -3
- package/dist/agents/conversation-agent.d.ts +19 -3
- package/dist/agents/conversation-agent.js +49 -30
- package/dist/agents/report-session-agent.d.ts +15 -15
- package/dist/agents/report-session-agent.js +23 -15
- package/dist/agents/sandbox/index.d.ts +1 -1
- package/dist/agents/sandbox/index.js +1 -1
- package/dist/agents/sandbox/shared.d.ts +35 -16
- package/dist/agents/sandbox/shared.js +63 -24
- package/dist/agents/sandbox/translational-safety-agent.js +26 -0
- package/dist/agents/sandbox/types.d.ts +26 -3
- package/dist/agents/sandbox/validate-skills.d.ts +2 -2
- package/dist/agents/sandbox/validate-skills.js +3 -1
- package/dist/agents/sandbox-catalog.d.ts +16 -2
- package/dist/agents/sandbox-catalog.js +22 -3
- package/dist/agents/system-prompt.d.ts +2 -2
- package/dist/app/approve-plan.d.ts +5 -2
- package/dist/app/approve-plan.js +1 -1
- package/dist/app/chat-turn-workflow.d.ts +114 -0
- package/dist/app/chat-turn-workflow.js +307 -0
- package/dist/app/chat-turn.d.ts +85 -57
- package/dist/app/chat-turn.js +125 -99
- package/dist/app/data-profile-orientation.js +10 -8
- package/dist/app/data-profile-view.d.ts +4 -4
- package/dist/app/data-profile-view.js +9 -8
- package/dist/app/in-process-turn.d.ts +42 -0
- package/dist/app/in-process-turn.js +139 -0
- package/dist/app/message-assembly.d.ts +10 -4
- package/dist/app/message-assembly.js +13 -8
- package/dist/app/report-session-runtime.d.ts +4 -4
- package/dist/app/report-session-runtime.js +36 -21
- package/dist/app/spawn-report-session.d.ts +33 -28
- package/dist/app/spawn-report-session.js +25 -40
- package/dist/app/synthesize-run.d.ts +22 -16
- package/dist/app/synthesize-run.js +41 -35
- package/dist/app/work.d.ts +96 -0
- package/dist/app/work.js +147 -0
- package/dist/auth/local-auth-context.d.ts +1 -3
- package/dist/auth/local-auth-context.js +1 -3
- package/dist/auth/local-run-authorizer.d.ts +1 -1
- package/dist/auth/local-run-authorizer.js +5 -3
- package/dist/auth/types.d.ts +26 -26
- package/dist/billing/cached-resolver.d.ts +4 -4
- package/dist/billing/run-charge.d.ts +5 -8
- package/dist/billing/run-charge.js +2 -2
- package/dist/billing/usage-recorder.d.ts +9 -9
- package/dist/citations/aggregate.js +1 -1
- package/dist/citations/clients/arxiv.d.ts +2 -2
- package/dist/citations/clients/arxiv.js +12 -4
- package/dist/citations/clients/common.js +4 -2
- package/dist/citations/clients/crossref.d.ts +2 -2
- package/dist/citations/clients/crossref.js +37 -37
- package/dist/citations/clients/doi-registry.js +15 -16
- package/dist/citations/clients/pubmed.d.ts +2 -2
- package/dist/citations/clients/pubmed.js +20 -13
- package/dist/citations/clients/semantic-scholar.d.ts +2 -2
- package/dist/citations/clients/semantic-scholar.js +9 -9
- package/dist/citations/compare.js +2 -2
- package/dist/citations/match.d.ts +2 -2
- package/dist/citations/match.js +12 -7
- package/dist/citations/normalize.js +35 -26
- package/dist/citations/plan.js +6 -2
- package/dist/citations/resolve.d.ts +6 -6
- package/dist/citations/resolve.js +34 -25
- package/dist/citations/types.d.ts +77 -77
- package/dist/config/environment-stores.d.ts +10 -8
- package/dist/config/resource-limits.js +1 -3
- package/dist/contracts/analogy-report.d.ts +7 -7
- package/dist/contracts/analogy-report.js +2 -2
- package/dist/contracts/chat-events.d.ts +46 -22
- package/dist/contracts/chat-events.js +1 -1
- package/dist/contracts/chat-frame.d.ts +15 -5
- package/dist/contracts/chat-frame.js +67 -40
- package/dist/contracts/chat-parts.d.ts +130 -67
- package/dist/contracts/chat-parts.js +1 -0
- package/dist/contracts/content-url.js +16 -2
- package/dist/contracts/data-profile.d.ts +38 -38
- package/dist/contracts/index.d.ts +3 -3
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/message.d.ts +6 -6
- package/dist/contracts/organ-system.d.ts +11 -11
- package/dist/contracts/part-registry.d.ts +5 -4
- package/dist/contracts/part-registry.js +5 -0
- package/dist/contracts/profile-vocabulary.d.ts +11 -10
- package/dist/contracts/profile-vocabulary.js +10 -3
- package/dist/contracts/report-blocks.d.ts +329 -329
- package/dist/contracts/report-reference.d.ts +105 -81
- package/dist/contracts/report-reference.js +30 -13
- package/dist/contracts/schemas/chat-events.d.ts +45 -6
- package/dist/contracts/schemas/chat-events.js +16 -0
- package/dist/contracts/schemas/chat-parts.d.ts +214 -102
- package/dist/contracts/schemas/chat-parts.js +50 -7
- package/dist/contracts/schemas/index.d.ts +1 -1
- package/dist/contracts/schemas/index.js +1 -1
- package/dist/contracts/severity.d.ts +1 -1
- package/dist/contracts/structure-source.d.ts +3 -3
- package/dist/contracts/structure-source.js +6 -2
- package/dist/contracts/subagent-activity.d.ts +2 -2
- package/dist/contracts/subagent-activity.js +5 -3
- package/dist/contracts/usage.d.ts +2 -2
- package/dist/data/safety-panel-schema.d.ts +25 -25
- package/dist/data/safety-panel-schema.js +13 -4
- package/dist/execution/artifact-metadata.d.ts +23 -15
- package/dist/execution/artifact-metadata.js +33 -10
- package/dist/execution/artifact-record.d.ts +2 -2
- package/dist/execution/artifact-registration.d.ts +5 -5
- package/dist/execution/artifact-registry.d.ts +8 -8
- package/dist/execution/dbos-run-launcher.d.ts +24 -4
- package/dist/execution/dbos-run-launcher.js +58 -4
- package/dist/execution/dbos-workflow-purger.d.ts +7 -6
- package/dist/execution/dbos-workflow-purger.js +29 -21
- package/dist/execution/input-signature.d.ts +2 -2
- package/dist/execution/post-step-pipeline.d.ts +9 -9
- package/dist/execution/post-step-pipeline.js +1 -1
- package/dist/execution/post-step.js +2 -2
- package/dist/execution/reconcile-manifest.d.ts +4 -4
- package/dist/execution/reconcile-manifest.js +3 -2
- package/dist/execution/run-authorizer.d.ts +46 -37
- package/dist/execution/run-authorizer.js +8 -8
- package/dist/execution/run-canceler.d.ts +39 -17
- package/dist/execution/run-canceler.js +137 -28
- package/dist/execution/run-end-record.d.ts +29 -0
- package/dist/execution/run-end-record.js +27 -0
- package/dist/execution/run-event-parts.d.ts +0 -8
- package/dist/execution/run-event-parts.js +29 -24
- package/dist/execution/run-event-stream.d.ts +8 -8
- package/dist/execution/run-event-stream.js +3 -4
- package/dist/execution/run-launcher.d.ts +4 -4
- package/dist/execution/run-synthesis.d.ts +49 -48
- package/dist/execution/run-synthesis.js +96 -255
- package/dist/execution/staged-input.d.ts +2 -2
- package/dist/execution/step-summary.d.ts +6 -6
- package/dist/execution/step-summary.js +4 -8
- package/dist/execution/topo-sort.js +4 -3
- package/dist/execution/workflow-purger.d.ts +2 -2
- package/dist/index.d.ts +25 -24
- package/dist/index.js +36 -37
- package/dist/input-sanitization.js +9 -2
- package/dist/input-scan/clustering.d.ts +6 -6
- package/dist/input-scan/clustering.js +11 -5
- package/dist/input-scan/companions.js +3 -2
- package/dist/input-scan/detect-sets.d.ts +2 -2
- package/dist/input-scan/detect-sets.js +25 -21
- package/dist/input-scan/enrich.d.ts +6 -6
- package/dist/input-scan/enrich.js +29 -12
- package/dist/input-scan/formats.d.ts +2 -2
- package/dist/input-scan/formats.js +8 -2
- package/dist/input-scan/markers.d.ts +2 -2
- package/dist/input-scan/markers.js +14 -5
- package/dist/input-scan/menu.d.ts +10 -10
- package/dist/input-scan/menu.js +0 -0
- package/dist/input-scan/quarantine.d.ts +2 -2
- package/dist/input-scan/quarantine.js +23 -18
- package/dist/input-scan/readout-budget.d.ts +2 -2
- package/dist/input-scan/readout-budget.js +1 -1
- package/dist/input-scan/readout.d.ts +2 -2
- package/dist/input-scan/readout.js +47 -34
- package/dist/input-scan/scan.d.ts +2 -2
- package/dist/input-scan/scan.js +7 -5
- package/dist/input-scan/set-types.d.ts +28 -28
- package/dist/input-scan/shapes.d.ts +4 -4
- package/dist/input-scan/shapes.js +0 -0
- package/dist/input-scan/templates.d.ts +10 -10
- package/dist/input-scan/templates.js +30 -22
- package/dist/input-scan/tokens.d.ts +6 -4
- package/dist/input-scan/tokens.js +100 -38
- package/dist/input-scan/types.d.ts +22 -22
- package/dist/lib/async-utils.d.ts +3 -7
- package/dist/lib/async-utils.js +5 -9
- package/dist/lib/cache.js +1 -1
- package/dist/lib/chrome.d.ts +46 -6
- package/dist/lib/console-logger.js +2 -2
- package/dist/lib/db-result.d.ts +22 -6
- package/dist/lib/db-result.js +9 -13
- package/dist/lib/eyes.d.ts +4 -4
- package/dist/lib/eyes.js +1 -1
- package/dist/lib/fs-helpers.js +18 -6
- package/dist/lib/fs-result.d.ts +1 -0
- package/dist/lib/fs-result.js +5 -6
- package/dist/lib/hooks.d.ts +23 -7
- package/dist/lib/hooks.js +17 -3
- package/dist/lib/host-concurrency.js +3 -2
- package/dist/lib/logger.d.ts +2 -2
- package/dist/lib/logger.js +2 -1
- package/dist/lib/metrics.d.ts +1 -1
- package/dist/lib/null-proto.d.ts +6 -0
- package/dist/lib/null-proto.js +17 -0
- package/dist/lib/otel-spans.d.ts +1 -1
- package/dist/lib/otel-spans.js +9 -5
- package/dist/lib/otel.d.ts +4 -2
- package/dist/lib/otel.js +10 -2
- package/dist/lib/page-capture.d.ts +8 -8
- package/dist/lib/page-capture.js +9 -4
- package/dist/lib/parse-json.d.ts +5 -0
- package/dist/lib/parse-json.js +12 -0
- package/dist/lib/present.d.ts +12 -0
- package/dist/lib/present.js +39 -0
- package/dist/lib/rate-limit.d.ts +4 -4
- package/dist/lib/rate-limit.js +12 -13
- package/dist/lib/regulatory-corpus/fetch-fda-guidance.d.ts +4 -4
- package/dist/lib/regulatory-corpus/fetch-fda-guidance.js +4 -10
- package/dist/lib/regulatory-corpus/fetch-ich.d.ts +4 -4
- package/dist/lib/regulatory-corpus/fetch-ich.js +18 -14
- package/dist/lib/regulatory-corpus/normalize-whitespace.d.ts +6 -0
- package/dist/lib/regulatory-corpus/normalize-whitespace.js +41 -0
- package/dist/lib/regulatory-corpus.d.ts +10 -10
- package/dist/lib/regulatory-corpus.js +3 -1
- package/dist/lib/result.d.ts +10 -11
- package/dist/lib/result.js +15 -13
- package/dist/lib/storage.d.ts +2 -2
- package/dist/lib/test/async-iterable.d.ts +9 -0
- package/dist/lib/test/async-iterable.js +28 -0
- package/dist/lib/test/request-url.d.ts +5 -0
- package/dist/lib/test/request-url.js +7 -0
- package/dist/lib/test/result-assert.d.ts +12 -0
- package/dist/lib/test/result-assert.js +17 -0
- package/dist/lib/test/text-shapes.d.ts +24 -0
- package/dist/lib/test/text-shapes.js +63 -0
- package/dist/lib/text.d.ts +22 -0
- package/dist/lib/text.js +68 -0
- package/dist/lib/thrown.d.ts +18 -0
- package/dist/lib/thrown.js +30 -0
- package/dist/lib/unknown-array.d.ts +6 -0
- package/dist/lib/unknown-array.js +8 -0
- package/dist/lib/unknown-containers.d.ts +14 -0
- package/dist/lib/unknown-containers.js +17 -0
- package/dist/lib/unknown-text.d.ts +8 -0
- package/dist/lib/unknown-text.js +28 -0
- package/dist/lib/zod-issue-shape.d.ts +1 -1
- package/dist/lib/zod-issue-shape.js +31 -31
- package/dist/lib/zod-issues.d.ts +3 -3
- package/dist/lib/zod-issues.js +30 -17
- package/dist/literature/sources/arxiv.d.ts +6 -6
- package/dist/literature/sources/arxiv.js +92 -35
- package/dist/literature/sources/http.d.ts +2 -2
- package/dist/literature/sources/http.js +16 -15
- package/dist/literature/sources/pubmed.d.ts +16 -16
- package/dist/literature/sources/pubmed.js +96 -61
- package/dist/literature/sources/semantic-scholar.d.ts +8 -8
- package/dist/literature/sources/semantic-scholar.js +22 -13
- package/dist/loop/accounting.d.ts +63 -0
- package/dist/loop/accounting.js +141 -0
- package/dist/loop/call-policy.d.ts +73 -0
- package/dist/loop/call-policy.js +109 -0
- package/dist/loop/chat-stream.d.ts +62 -0
- package/dist/loop/chat-stream.js +237 -0
- package/dist/loop/compaction.d.ts +14 -9
- package/dist/loop/compaction.js +17 -0
- package/dist/loop/metrics.d.ts +12 -15
- package/dist/loop/metrics.js +14 -26
- package/dist/loop/run-loop.d.ts +233 -0
- package/dist/loop/run-loop.js +770 -0
- package/dist/loop/sub-agent.d.ts +61 -0
- package/dist/loop/sub-agent.js +161 -0
- package/dist/loop/tool-adapter.d.ts +98 -0
- package/dist/loop/tool-adapter.js +364 -0
- package/dist/loop/tool-detail.d.ts +1 -1
- package/dist/loop/tool-detail.js +6 -5
- package/dist/loop/tool-outcome.d.ts +1 -1
- package/dist/loop/tool-output.d.ts +4 -4
- package/dist/loop/tool-output.js +2 -2
- package/dist/loop/turn-credentials.d.ts +34 -0
- package/dist/loop/turn-credentials.js +73 -0
- package/dist/loop/types.d.ts +34 -37
- package/dist/loop/types.js +2 -7
- package/dist/memory/ai-sdk-message-storage.d.ts +5 -5
- package/dist/memory/ai-sdk-message-storage.js +13 -11
- package/dist/memory/card-builders.d.ts +14 -14
- package/dist/memory/card-builders.js +19 -15
- package/dist/memory/conversation-display-recorder.d.ts +22 -6
- package/dist/memory/conversation-display-recorder.js +64 -20
- package/dist/memory/conversation-display-replay.js +3 -2
- package/dist/memory/conversation-display-storage.d.ts +15 -7
- package/dist/memory/conversation-display-storage.js +141 -36
- package/dist/memory/conversation-view.d.ts +4 -4
- package/dist/memory/conversation-view.js +5 -4
- package/dist/memory/count-tokens.js +10 -8
- package/dist/memory/derive-thread-title.d.ts +0 -13
- package/dist/memory/derive-thread-title.js +2 -1
- package/dist/memory/message-backfill.d.ts +12 -5
- package/dist/memory/message-backfill.js +5 -3
- package/dist/memory/thread-history.d.ts +66 -24
- package/dist/memory/thread-history.js +212 -79
- package/dist/memory/thread-store.d.ts +26 -17
- package/dist/memory/thread-store.js +15 -9
- package/dist/memory/tool-call-integrity.d.ts +2 -2
- package/dist/memory/tool-call-integrity.js +4 -3
- package/dist/memory/working-memory.d.ts +16 -12
- package/dist/memory/working-memory.js +72 -61
- package/dist/prompts/briefing.d.ts +63 -9
- package/dist/prompts/briefing.js +130 -3
- package/dist/prompts/planner.js +13 -2
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +6 -14
- package/dist/provenance/collector.d.ts +6 -6
- package/dist/provenance/collector.js +15 -8
- package/dist/provenance/exec-frame.d.ts +2 -2
- package/dist/provenance/exec-frame.js +6 -1
- package/dist/provenance/seam.d.ts +24 -19
- package/dist/provenance/seam.js +7 -5
- package/dist/provenance/types.d.ts +6 -6
- package/dist/providers/ai-sdk.d.ts +113 -62
- package/dist/providers/ai-sdk.js +303 -458
- package/dist/providers/embedding.d.ts +2 -2
- package/dist/providers/embedding.js +1 -1
- package/dist/providers/errors.d.ts +22 -2
- package/dist/providers/errors.js +98 -50
- package/dist/providers/prompt-cache.d.ts +8 -8
- package/dist/providers/prompt-cache.js +13 -11
- package/dist/providers/reasoning.d.ts +2 -2
- package/dist/providers/reasoning.js +2 -2
- package/dist/providers/types.d.ts +59 -92
- package/dist/reference-data/catalog.d.ts +2 -15
- package/dist/reference-data/catalog.js +166 -10
- package/dist/reference-data/receipt.d.ts +2 -2
- package/dist/report-model/assert-rules.js +1 -1
- package/dist/report-model/authoring-grammar.d.ts +209 -231
- package/dist/report-model/block-walk.d.ts +10 -10
- package/dist/report-model/block-walk.js +2 -0
- package/dist/report-model/draft-finish.d.ts +8 -8
- package/dist/report-model/draft-finish.js +1 -1
- package/dist/report-model/draft-hash.js +8 -7
- package/dist/report-model/draft-operations.d.ts +22 -10
- package/dist/report-model/draft-operations.js +48 -30
- package/dist/report-model/draft-read.d.ts +19 -7
- package/dist/report-model/draft-read.js +16 -6
- package/dist/report-model/draft.d.ts +844 -2
- package/dist/report-model/draft.js +3 -1
- package/dist/report-model/fixture-resolver.js +39 -16
- package/dist/report-model/pin-snapshot.d.ts +2 -2
- package/dist/report-model/pin-snapshot.js +15 -15
- package/dist/report-model/production-resolver.d.ts +18 -18
- package/dist/report-model/production-resolver.js +50 -42
- package/dist/report-model/reference-resolver.d.ts +26 -10
- package/dist/report-model/reference-resolver.js +68 -5
- package/dist/report-model/reference-stamp.js +30 -38
- package/dist/report-model/structural-validation.js +12 -4
- package/dist/report-model/validate.d.ts +7 -7
- package/dist/report-model/validate.js +6 -6
- package/dist/report-render/assets.d.ts +2 -2
- package/dist/report-render/assets.js +27 -3
- package/dist/report-render/chart-export.d.ts +10 -2
- package/dist/report-render/chart-export.js +37 -11
- package/dist/report-render/chart-presets.d.ts +8 -8
- package/dist/report-render/chart-presets.js +3 -3
- package/dist/report-render/chart-renderers.d.ts +23 -16
- package/dist/report-render/chart-renderers.js +74 -53
- package/dist/report-render/chart-toolbox.d.ts +2 -2
- package/dist/report-render/chart-toolbox.js +7 -6
- package/dist/report-render/chart.d.ts +15 -16
- package/dist/report-render/chart.js +369 -252
- package/dist/report-render/design.d.ts +36 -297
- package/dist/report-render/design.js +0 -12
- package/dist/report-render/figures/common.d.ts +12 -12
- package/dist/report-render/figures/common.js +42 -31
- package/dist/report-render/figures/dendrogram.d.ts +3 -3
- package/dist/report-render/figures/dendrogram.js +10 -10
- package/dist/report-render/figures/dense.js +15 -8
- package/dist/report-render/figures/dotplot.js +46 -33
- package/dist/report-render/figures/embedding.js +52 -43
- package/dist/report-render/figures/equal-units.d.ts +10 -10
- package/dist/report-render/figures/equal-units.js +27 -27
- package/dist/report-render/figures/forest.js +9 -7
- package/dist/report-render/figures/gsea.js +21 -11
- package/dist/report-render/figures/heatmap.js +13 -9
- package/dist/report-render/figures/index.d.ts +10 -10
- package/dist/report-render/figures/index.js +4 -2
- package/dist/report-render/figures/km.js +10 -9
- package/dist/report-render/figures/label-room.d.ts +12 -12
- package/dist/report-render/figures/label-room.js +7 -2
- package/dist/report-render/figures/locuszoom.d.ts +2 -2
- package/dist/report-render/figures/locuszoom.js +59 -31
- package/dist/report-render/figures/lollipop.js +3 -2
- package/dist/report-render/figures/ma.js +8 -4
- package/dist/report-render/figures/manhattan.d.ts +1 -2
- package/dist/report-render/figures/manhattan.js +52 -34
- package/dist/report-render/figures/oncoprint.js +4 -5
- package/dist/report-render/figures/pca.js +25 -11
- package/dist/report-render/figures/qq.js +20 -9
- package/dist/report-render/figures/roc.js +1 -1
- package/dist/report-render/figures/sankey.d.ts +2 -2
- package/dist/report-render/figures/sankey.js +11 -10
- package/dist/report-render/figures/upset.d.ts +6 -6
- package/dist/report-render/figures/upset.js +7 -4
- package/dist/report-render/figures/volcano.js +12 -10
- package/dist/report-render/hybrid-svg.d.ts +4 -4
- package/dist/report-render/hybrid-svg.js +7 -5
- package/dist/report-render/number-format.d.ts +4 -4
- package/dist/report-render/number-format.js +62 -27
- package/dist/report-render/page.js +2 -1
- package/dist/report-render/provenance-data.d.ts +2 -2
- package/dist/report-render/references.d.ts +4 -4
- package/dist/report-render/render.d.ts +2 -2
- package/dist/report-render/render.js +10 -11
- package/dist/report-render/script-json.js +1 -1
- package/dist/report-render/table-data.d.ts +6 -6
- package/dist/report-render/test/options.d.ts +49 -0
- package/dist/report-render/test/options.js +143 -0
- package/dist/report-render/test/page-function.d.ts +19 -0
- package/dist/report-render/test/page-function.js +33 -0
- package/dist/report-render/types.d.ts +10 -10
- package/dist/report-render/value-bridge.d.ts +4 -4
- package/dist/report-render/value-bridge.js +27 -14
- package/dist/report-render/views/chart-view.d.ts +2 -2
- package/dist/report-render/views/chart-view.js +2 -2
- package/dist/report-render/views/lineage.d.ts +6 -6
- package/dist/report-render/views/lineage.js +2 -0
- package/dist/report-render/views/page-view.d.ts +2 -2
- package/dist/report-render/views/page-view.js +1 -0
- package/dist/report-render/views/prose.js +25 -8
- package/dist/report-render/views/references-view.js +12 -1
- package/dist/report-render/views/values.js +1 -1
- package/dist/runtime/assemble.d.ts +53 -26
- package/dist/runtime/assemble.js +103 -56
- package/dist/runtime/boot.d.ts +8 -7
- package/dist/runtime/boot.js +1 -1
- package/dist/runtime/connection-budget.d.ts +6 -6
- package/dist/runtime/connection-budget.js +1 -1
- package/dist/runtime/dbos.d.ts +29 -19
- package/dist/runtime/dbos.js +35 -20
- package/dist/runtime/result-serialization.js +18 -5
- package/dist/runtime/shutdown.d.ts +2 -2
- package/dist/sandbox/client.d.ts +7 -3
- package/dist/sandbox/cpu-files.d.ts +2 -2
- package/dist/sandbox/cpu-files.js +21 -4
- package/dist/sandbox/create-sandbox.d.ts +7 -8
- package/dist/sandbox/create-sandbox.js +6 -12
- package/dist/sandbox/docker-client.d.ts +48 -3
- package/dist/sandbox/docker-client.js +10 -6
- package/dist/sandbox/estimate-data-profile-resources.js +2 -7
- package/dist/sandbox/exec-outcome.d.ts +2 -2
- package/dist/sandbox/exec-outcome.js +1 -1
- package/dist/sandbox/exec.d.ts +10 -6
- package/dist/sandbox/exec.js +27 -12
- package/dist/sandbox/execution-id.js +14 -6
- package/dist/sandbox/farm.d.ts +2 -2
- package/dist/sandbox/farm.js +22 -27
- package/dist/sandbox/identity.d.ts +7 -0
- package/dist/sandbox/identity.js +14 -3
- package/dist/sandbox/image-packages.d.ts +26 -4
- package/dist/sandbox/image-packages.js +29 -27
- package/dist/sandbox/k8s-client.d.ts +35 -8
- package/dist/sandbox/k8s-client.js +6 -5
- package/dist/sandbox/labels.js +1 -0
- package/dist/sandbox/liveness.js +3 -1
- package/dist/sandbox/mount-plan.d.ts +10 -10
- package/dist/sandbox/mount-plan.js +13 -14
- package/dist/sandbox/notification-sweep.d.ts +4 -4
- package/dist/sandbox/notification-sweep.js +4 -7
- package/dist/sandbox/package-identity.d.ts +6 -13
- package/dist/sandbox/package-identity.js +46 -28
- package/dist/sandbox/reaper.d.ts +10 -10
- package/dist/sandbox/reaper.js +4 -6
- package/dist/sandbox/sandbox-error.d.ts +2 -9
- package/dist/sandbox/sandbox-error.js +21 -8
- package/dist/sandbox/sandbox-step-translate.d.ts +8 -5
- package/dist/sandbox/sandbox-step-translate.js +22 -8
- package/dist/sandbox/step-sandbox.d.ts +47 -0
- package/dist/sandbox/step-sandbox.js +53 -0
- package/dist/sandbox/types.d.ts +18 -19
- package/dist/sandbox/types.js +1 -1
- package/dist/schemas/artifact-manifest.d.ts +4 -4
- package/dist/schemas/data-profile-schemas.d.ts +351 -306
- package/dist/schemas/file-metadata.js +2 -4
- package/dist/schemas/plan-schemas.d.ts +79 -25
- package/dist/schemas/plan-schemas.js +1 -1
- package/dist/schemas/run-synthesis.d.ts +95 -26
- package/dist/schemas/run-synthesis.js +3 -1
- package/dist/schemas/validate-plan.d.ts +4 -4
- package/dist/schemas/validate-plan.js +17 -5
- package/dist/schemas/workflow-state.d.ts +127 -14
- package/dist/schemas/workflow-state.js +56 -2
- package/dist/state/active-turns.d.ts +36 -0
- package/dist/state/active-turns.js +67 -0
- package/dist/state/analyses-metrics.d.ts +8 -8
- package/dist/state/analyses-metrics.js +4 -4
- package/dist/state/analyses.d.ts +0 -14
- package/dist/state/analyses.js +0 -32
- package/dist/state/artifacts.d.ts +47 -18
- package/dist/state/artifacts.js +48 -7
- package/dist/state/data-profile.d.ts +2 -2
- package/dist/state/db.d.ts +12 -2
- package/dist/state/db.js +5 -0
- package/dist/state/edit-intents.d.ts +4 -0
- package/dist/state/edit-intents.js +71 -0
- package/dist/state/index.d.ts +5 -5
- package/dist/state/index.js +3 -3
- package/dist/state/init.d.ts +2 -1
- package/dist/state/init.js +5 -2
- package/dist/state/migrations/20260927120000_baseline.js +3 -1
- package/dist/state/migrations/20261026120000_add_the_edit_intents.d.ts +7 -0
- package/dist/state/migrations/20261026120000_add_the_edit_intents.js +21 -0
- package/dist/state/migrations/20261027120000_record_the_applied_memory_edits.d.ts +7 -0
- package/dist/state/migrations/20261027120000_record_the_applied_memory_edits.js +16 -0
- package/dist/state/migrations/20261027120100_store_the_authoring_results.d.ts +8 -0
- package/dist/state/migrations/20261027120100_store_the_authoring_results.js +20 -0
- package/dist/state/migrations/20261027120200_key_the_groups_of_a_turn.d.ts +7 -0
- package/dist/state/migrations/20261027120200_key_the_groups_of_a_turn.js +12 -0
- package/dist/state/migrations/20261027120300_mark_the_active_turn_of_a_thread.d.ts +7 -0
- package/dist/state/migrations/20261027120300_mark_the_active_turn_of_a_thread.js +11 -0
- package/dist/state/migrations/20261028120000_record_the_paused_work.d.ts +7 -0
- package/dist/state/migrations/20261028120000_record_the_paused_work.js +25 -0
- package/dist/state/migrations/20261028120100_end_the_suspended_runs.d.ts +8 -0
- package/dist/state/migrations/20261028120100_end_the_suspended_runs.js +13 -0
- package/dist/state/migrations/20261029120000_reference_the_run_mandate.d.ts +7 -0
- package/dist/state/migrations/20261029120000_reference_the_run_mandate.js +23 -0
- package/dist/state/migrations/20261029120100_record_the_run_session_of_a_pause.d.ts +7 -0
- package/dist/state/migrations/20261029120100_record_the_run_session_of_a_pause.js +11 -0
- package/dist/state/migrations/index.js +18 -0
- package/dist/state/paused-work.d.ts +52 -0
- package/dist/state/paused-work.js +105 -0
- package/dist/state/plans.d.ts +27 -8
- package/dist/state/plans.js +14 -11
- package/dist/state/purge-analysis.d.ts +6 -6
- package/dist/state/purge-analysis.js +10 -2
- package/dist/state/report-session-state.d.ts +43 -18
- package/dist/state/report-session-state.js +43 -4
- package/dist/state/report-versions.d.ts +16 -11
- package/dist/state/report-versions.js +5 -3
- package/dist/state/runs.d.ts +70 -25
- package/dist/state/runs.js +127 -64
- package/dist/state/schema.d.ts +18 -21
- package/dist/state/schema.js +4 -5
- package/dist/state/snapshot-parse.d.ts +2 -2
- package/dist/state/snapshot-parse.js +14 -8
- package/dist/state/step-executions.d.ts +29 -8
- package/dist/state/step-executions.js +28 -4
- package/dist/state/tool-outputs.d.ts +2 -2
- package/dist/state/tool-outputs.js +2 -1
- package/dist/state/vector-store.d.ts +16 -16
- package/dist/state/vector-store.js +11 -7
- package/dist/tasks/data-profile-absorb.d.ts +3 -3
- package/dist/tasks/data-profile-absorb.js +21 -16
- package/dist/tasks/data-profile-activity.d.ts +5 -5
- package/dist/tasks/data-profile-activity.js +5 -5
- package/dist/tasks/data-profile-index.d.ts +4 -4
- package/dist/tasks/data-profile-index.js +32 -17
- package/dist/tasks/data-profile-resolve.d.ts +8 -8
- package/dist/tasks/data-profile-resolve.js +88 -62
- package/dist/tasks/data-profile.d.ts +40 -31
- package/dist/tasks/data-profile.js +138 -95
- package/dist/tasks/extract-values.d.ts +26 -79
- package/dist/tasks/extract-values.js +48 -108
- package/dist/tasks/refresh-regulatory-corpus.d.ts +4 -20
- package/dist/tasks/refresh-regulatory-corpus.js +12 -8
- package/dist/tasks/regulatory-corpus-boot-refresh.d.ts +2 -2
- package/dist/tasks/regulatory-corpus-boot-refresh.js +12 -10
- package/dist/testing.d.ts +60 -0
- package/dist/testing.js +96 -0
- package/dist/tools/ad-hoc-router.d.ts +11 -9
- package/dist/tools/ad-hoc-router.js +80 -51
- package/dist/tools/analysis-invocation.d.ts +7 -0
- package/dist/tools/analysis-invocation.js +29 -5
- package/dist/tools/approval/contract.d.ts +8 -52
- package/dist/tools/approval/contract.js +7 -36
- package/dist/tools/approval/gateway.d.ts +27 -28
- package/dist/tools/approval/gateway.js +33 -130
- package/dist/tools/approval/queries.d.ts +6 -11
- package/dist/tools/approval/queries.js +43 -29
- package/dist/tools/approval/uuidv7.js +2 -2
- package/dist/tools/bio/chembl.d.ts +3 -3
- package/dist/tools/bio/chembl.js +10 -0
- package/dist/tools/bio/comptox.d.ts +25 -8
- package/dist/tools/bio/comptox.js +4 -2
- package/dist/tools/bio/drug-gene-interactions.d.ts +27 -17
- package/dist/tools/bio/drug-gene-interactions.js +11 -8
- package/dist/tools/bio/gene-disease-evidence.d.ts +61 -15
- package/dist/tools/bio/gene-disease-evidence.js +10 -6
- package/dist/tools/bio/gene-preclinical-profile.d.ts +11 -11
- package/dist/tools/bio/keys.d.ts +2 -2
- package/dist/tools/bio/keys.js +3 -3
- package/dist/tools/bio/lookup-annotation.d.ts +5 -5
- package/dist/tools/bio/lookup-annotation.js +3 -0
- package/dist/tools/bio/opentargets.d.ts +1 -1
- package/dist/tools/bio/opentargets.js +6 -0
- package/dist/tools/bio/pubchem.d.ts +2 -2
- package/dist/tools/bio/pubchem.js +6 -0
- package/dist/tools/bio/pubmed.d.ts +26 -16
- package/dist/tools/bio/pubmed.js +9 -3
- package/dist/tools/bio/search-clinical-trials.d.ts +4 -4
- package/dist/tools/bio/search-clinical-trials.js +5 -1
- package/dist/tools/bio/search-faers.d.ts +2 -2
- package/dist/tools/bio/search-faers.js +1 -1
- package/dist/tools/bio/search-gene.d.ts +2 -2
- package/dist/tools/bio/search-gene.js +1 -0
- package/dist/tools/bio/search-geo-datasets.d.ts +4 -4
- package/dist/tools/bio/search-geo-datasets.js +5 -5
- package/dist/tools/bio/search-interactions.d.ts +1 -1
- package/dist/tools/bio/search-patents.d.ts +28 -22
- package/dist/tools/bio/search-patents.js +20 -17
- package/dist/tools/bio/search-protein.d.ts +2 -2
- package/dist/tools/bio/search-protein.js +1 -1
- package/dist/tools/bio/search-regulatory-approvals.d.ts +64 -18
- package/dist/tools/bio/search-regulatory-approvals.js +21 -16
- package/dist/tools/bio/target-safety.d.ts +8 -8
- package/dist/tools/bio/target-safety.js +11 -6
- package/dist/tools/capability-data.d.ts +40 -0
- package/dist/tools/capability-data.js +74 -0
- package/dist/tools/define-tool.d.ts +55 -46
- package/dist/tools/define-tool.js +39 -27
- package/dist/tools/display/normalize-echart-spec.d.ts +2 -2
- package/dist/tools/display/normalize-echart-spec.js +51 -27
- package/dist/tools/display/show-user.d.ts +2 -2
- package/dist/tools/display/show-user.js +1 -1
- package/dist/tools/execute-analysis.d.ts +54 -17
- package/dist/tools/execute-analysis.js +193 -62
- package/dist/tools/knowledge/check.d.ts +182 -0
- package/dist/tools/knowledge/check.js +94 -0
- package/dist/tools/knowledge/client.d.ts +351 -0
- package/dist/tools/knowledge/client.js +319 -0
- package/dist/tools/knowledge/environment.d.ts +78 -0
- package/dist/tools/knowledge/environment.js +116 -0
- package/dist/tools/knowledge/index.d.ts +49 -0
- package/dist/tools/knowledge/index.js +70 -0
- package/dist/tools/knowledge/local-slots.d.ts +62 -0
- package/dist/tools/knowledge/local-slots.js +184 -0
- package/dist/tools/knowledge/pin.d.ts +14 -0
- package/dist/tools/knowledge/pin.js +17 -0
- package/dist/tools/knowledge/recommend.d.ts +67 -0
- package/dist/tools/knowledge/recommend.js +106 -0
- package/dist/tools/knowledge/situation.d.ts +143 -0
- package/dist/tools/knowledge/situation.js +157 -0
- package/dist/tools/knowledge/skeleton.d.ts +69 -0
- package/dist/tools/knowledge/skeleton.js +201 -0
- package/dist/tools/knowledge/template.d.ts +139 -0
- package/dist/tools/knowledge/template.js +273 -0
- package/dist/tools/lib/alphafold-client.js +2 -1
- package/dist/tools/lib/amass-client.d.ts +28 -28
- package/dist/tools/lib/amass-client.js +20 -10
- package/dist/tools/lib/api-utils.d.ts +13 -3
- package/dist/tools/lib/api-utils.js +17 -3
- package/dist/tools/lib/bgee-client.d.ts +6 -6
- package/dist/tools/lib/bgee-client.js +16 -10
- package/dist/tools/lib/cbioportal-client.d.ts +4 -4
- package/dist/tools/lib/cbioportal-client.js +9 -8
- package/dist/tools/lib/chembl-client.d.ts +14 -14
- package/dist/tools/lib/chembl-client.js +11 -4
- package/dist/tools/lib/clinical-benchmarks-client.d.ts +4 -4
- package/dist/tools/lib/clinical-benchmarks-client.js +4 -7
- package/dist/tools/lib/clinical-trials-client.d.ts +18 -18
- package/dist/tools/lib/clinical-trials-client.js +10 -10
- package/dist/tools/lib/clinvar-client.d.ts +2 -2
- package/dist/tools/lib/clinvar-client.js +6 -6
- package/dist/tools/lib/ctx-ops.d.ts +23 -23
- package/dist/tools/lib/ctx-ops.js +13 -10
- package/dist/tools/lib/dgidb-client.d.ts +6 -6
- package/dist/tools/lib/dgidb-client.js +10 -12
- package/dist/tools/lib/disgenet-client.d.ts +4 -4
- package/dist/tools/lib/ema-client.d.ts +2 -2
- package/dist/tools/lib/ensembl-client.d.ts +4 -4
- package/dist/tools/lib/ensembl-client.js +5 -4
- package/dist/tools/lib/gwas-catalog-client.d.ts +4 -4
- package/dist/tools/lib/gwas-catalog-client.js +17 -17
- package/dist/tools/lib/html-text.d.ts +6 -0
- package/dist/tools/lib/html-text.js +9 -0
- package/dist/tools/lib/identifier-resolver.d.ts +4 -4
- package/dist/tools/lib/identifier-resolver.js +7 -3
- package/dist/tools/lib/impc-client.d.ts +8 -8
- package/dist/tools/lib/impc-client.js +46 -35
- package/dist/tools/lib/iuphar-client.d.ts +2 -2
- package/dist/tools/lib/iuphar-client.js +7 -16
- package/dist/tools/lib/monarch-client.d.ts +4 -4
- package/dist/tools/lib/openfda-client.d.ts +22 -22
- package/dist/tools/lib/openfda-client.js +23 -22
- package/dist/tools/lib/opentargets-client.d.ts +12 -12
- package/dist/tools/lib/pathway-client.d.ts +4 -4
- package/dist/tools/lib/pathway-client.js +15 -19
- package/dist/tools/lib/pharmgkb-client.d.ts +2 -2
- package/dist/tools/lib/pharmgkb-client.js +1 -1
- package/dist/tools/lib/protein-family-modality.d.ts +2 -2
- package/dist/tools/lib/protein-family-modality.js +0 -2
- package/dist/tools/lib/protein-family-normalize.js +7 -0
- package/dist/tools/lib/pubchem-client.d.ts +2 -2
- package/dist/tools/lib/pubchem-client.js +1 -0
- package/dist/tools/lib/pubchem-ops.d.ts +4 -4
- package/dist/tools/lib/pubchem-ops.js +8 -0
- package/dist/tools/lib/pubmed-client.d.ts +9 -9
- package/dist/tools/lib/pubmed-client.js +1 -1
- package/dist/tools/lib/string-client.d.ts +6 -6
- package/dist/tools/lib/uniprot-client.d.ts +8 -8
- package/dist/tools/lib/uniprot-client.js +5 -1
- package/dist/tools/memory/update-working-memory.d.ts +16 -13
- package/dist/tools/memory/update-working-memory.js +21 -3
- package/dist/tools/read-tool-output.js +8 -2
- package/dist/tools/registry.d.ts +2 -2
- package/dist/tools/report-authoring/authoring-tools.d.ts +54 -23
- package/dist/tools/report-authoring/authoring-tools.js +128 -62
- package/dist/tools/report-session/derive-table.d.ts +19 -59
- package/dist/tools/report-session/derive-table.js +120 -122
- package/dist/tools/report-session/examine-page.d.ts +6 -6
- package/dist/tools/report-session/examine-page.js +14 -12
- package/dist/tools/report-session/list-artifacts.d.ts +6 -6
- package/dist/tools/report-session/list-artifacts.js +5 -5
- package/dist/tools/report-session/page-faults.d.ts +6 -6
- package/dist/tools/report-session/page-faults.js +3 -1
- package/dist/tools/report-session/preview-report.d.ts +4 -3
- package/dist/tools/report-session/preview-report.js +26 -7
- package/dist/tools/report-session/record-version.d.ts +9 -4
- package/dist/tools/report-session/record-version.js +16 -9
- package/dist/tools/report-session/session-page-publisher.d.ts +6 -6
- package/dist/tools/research/generate-analogy-report.d.ts +14 -7
- package/dist/tools/research/generate-analogy-report.js +80 -76
- package/dist/tools/research/generate-plan.d.ts +39 -16
- package/dist/tools/research/generate-plan.js +565 -338
- package/dist/tools/research/guarded-address.d.ts +20 -0
- package/dist/tools/research/guarded-address.js +149 -0
- package/dist/tools/research/inspect-data-profile.d.ts +27 -20
- package/dist/tools/research/inspect-data-profile.js +26 -25
- package/dist/tools/research/inspect-run.d.ts +54 -27
- package/dist/tools/research/inspect-run.js +50 -40
- package/dist/tools/research/literature-reviewer.d.ts +8 -7
- package/dist/tools/research/literature-reviewer.js +29 -28
- package/dist/tools/research/search-arxiv.d.ts +1 -1
- package/dist/tools/research/search-arxiv.js +1 -1
- package/dist/tools/research/search-github-repos.d.ts +10 -7
- package/dist/tools/research/search-github-repos.js +25 -22
- package/dist/tools/research/search-semantic-scholar.js +1 -1
- package/dist/tools/research/searxng-engine.d.ts +40 -0
- package/dist/tools/research/searxng-engine.js +122 -0
- package/dist/tools/research/web-fetch.d.ts +127 -0
- package/dist/tools/research/web-fetch.js +618 -0
- package/dist/tools/research/web-search.d.ts +84 -0
- package/dist/tools/research/web-search.js +117 -0
- package/dist/tools/sandbox/link-packages.d.ts +11 -7
- package/dist/tools/sandbox/link-packages.js +11 -8
- package/dist/tools/sandbox/list-available-packages.d.ts +24 -12
- package/dist/tools/sandbox/list-available-packages.js +43 -29
- package/dist/tools/sandbox/list-available-refs.d.ts +38 -14
- package/dist/tools/sandbox/list-available-refs.js +119 -37
- package/dist/tools/sandbox/report-blocker.d.ts +30 -37
- package/dist/tools/sandbox/report-blocker.js +22 -68
- package/dist/tools/sandbox/skills.d.ts +16 -12
- package/dist/tools/sandbox/skills.js +16 -12
- package/dist/tools/sandbox/submit-file-metadata.d.ts +12 -9
- package/dist/tools/sandbox/submit-file-metadata.js +18 -21
- package/dist/tools/start-report-session.d.ts +13 -5
- package/dist/tools/start-report-session.js +35 -8
- package/dist/tools/terminal-text.d.ts +14 -0
- package/dist/tools/terminal-text.js +51 -0
- package/dist/tools/workspace/decision-record.d.ts +81 -0
- package/dist/tools/workspace/decision-record.js +127 -0
- package/dist/tools/workspace/edit-file.d.ts +59 -18
- package/dist/tools/workspace/edit-file.js +98 -15
- package/dist/tools/workspace/execute-command.d.ts +31 -12
- package/dist/tools/workspace/execute-command.js +37 -8
- package/dist/tools/workspace/file-stat.d.ts +6 -3
- package/dist/tools/workspace/grep.d.ts +10 -7
- package/dist/tools/workspace/grep.js +9 -3
- package/dist/tools/workspace/index.d.ts +1 -0
- package/dist/tools/workspace/index.js +1 -0
- package/dist/tools/workspace/list-files.d.ts +6 -3
- package/dist/tools/workspace/mutator.d.ts +28 -15
- package/dist/tools/workspace/mutator.js +107 -53
- package/dist/tools/workspace/read-file.d.ts +8 -5
- package/dist/tools/workspace/result-bounds.d.ts +5 -5
- package/dist/tools/workspace/result-bounds.js +4 -2
- package/dist/tools/workspace/run-exec.d.ts +2 -2
- package/dist/tools/workspace/run-exec.js +3 -3
- package/dist/tools/workspace/scan-inputs.d.ts +8 -5
- package/dist/tools/workspace/scan-inputs.js +17 -5
- package/dist/tools/workspace/show-file.d.ts +1 -1
- package/dist/tools/workspace/show-file.js +2 -1
- package/dist/tools/workspace/show-plan.d.ts +8 -5
- package/dist/tools/workspace/show-plan.js +9 -8
- package/dist/tools/workspace/workspace-search.d.ts +41 -13
- package/dist/tools/workspace/workspace-search.js +27 -14
- package/dist/tools/workspace/write-file.d.ts +9 -7
- package/dist/workflows/execute-analysis-scheduler.d.ts +9 -9
- package/dist/workflows/execute-analysis-scheduler.js +6 -3
- package/dist/workflows/execute-analysis.d.ts +100 -76
- package/dist/workflows/execute-analysis.js +380 -247
- package/dist/workflows/pause.d.ts +71 -0
- package/dist/workflows/pause.js +173 -0
- package/dist/workflows/sandbox-step.d.ts +50 -61
- package/dist/workflows/sandbox-step.js +99 -106
- package/dist/workflows/test/sql-writes.d.ts +21 -0
- package/dist/workflows/test/sql-writes.js +36 -0
- package/dist/workspace/filesystem.d.ts +6 -6
- package/dist/workspace/filesystem.js +10 -8
- package/dist/workspace/paths.d.ts +1 -1
- package/dist/workspace/paths.js +15 -4
- package/dist/workspace/search-config.d.ts +2 -2
- package/dist/workspace/search-config.js +1 -1
- package/package.json +25 -22
- package/dist/loop/continue-agent.d.ts +0 -40
- package/dist/loop/continue-agent.js +0 -38
- package/dist/loop/genai-spans.d.ts +0 -47
- package/dist/loop/genai-spans.js +0 -134
- package/dist/loop/run-agent.d.ts +0 -236
- package/dist/loop/run-agent.js +0 -1110
- package/dist/loop/run-step.d.ts +0 -29
- package/dist/loop/run-step.js +0 -34
- package/dist/loop/run-to-terminal.d.ts +0 -66
- package/dist/loop/run-to-terminal.js +0 -90
- package/dist/loop/tool-mask.d.ts +0 -23
- package/dist/loop/tool-mask.js +0 -46
- package/dist/providers/streaming-chat.d.ts +0 -32
- package/dist/providers/streaming-chat.js +0 -91
- package/dist/tasks/derive-table-exec.d.ts +0 -54
- package/dist/tasks/derive-table-exec.js +0 -86
- package/dist/workflows/suspension.d.ts +0 -32
- package/dist/workflows/suspension.js +0 -38
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* generatePlan — a loop-driving tool: a focused
|
|
3
|
-
*
|
|
2
|
+
* generatePlan — a loop-driving tool: a focused agent loop that builds an
|
|
3
|
+
* analysis plan (a DAG of steps).
|
|
4
4
|
*
|
|
5
5
|
* The dataset's facts are NOT an input. They are read here, server-side, from
|
|
6
6
|
* the analysis's persisted data profile — the one durable record of what the
|
|
@@ -10,34 +10,40 @@
|
|
|
10
10
|
* caller-typed dataset summary would be a model re-transcribing a record the
|
|
11
11
|
* database already holds, and it would lose fidelity at every hop.
|
|
12
12
|
*
|
|
13
|
-
* The tool drives an internal "planner" agent that
|
|
14
|
-
*
|
|
13
|
+
* The tool drives an internal "planner" agent that gives its outcome through
|
|
14
|
+
* three terminal tools. None of them ends the run, and none writes:
|
|
15
15
|
*
|
|
16
|
-
* - submit_plan(plan) →
|
|
17
|
-
*
|
|
18
|
-
* - request_clarification(…) →
|
|
19
|
-
* - report_blocker(reason) →
|
|
16
|
+
* - submit_plan(plan) → checks a candidate; gives back the accepted
|
|
17
|
+
* plan or structured issues
|
|
18
|
+
* - request_clarification(…) → the planner needs more context
|
|
19
|
+
* - report_blocker(reason) → no viable plan
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* terminal-tool surface above.
|
|
21
|
+
* After the run, the tool selects the outcome from the recorded calls — the
|
|
22
|
+
* last accepted plan, else the last clarification, else the first blocker —
|
|
23
|
+
* and persists an accepted plan under an id that a rerun of the call gives again.
|
|
25
24
|
*/
|
|
25
|
+
import { errorMessageOf } from "../../lib/thrown.js";
|
|
26
|
+
import { textOf } from "../../lib/unknown-text.js";
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
26
28
|
import { ok } from "neverthrow";
|
|
27
29
|
import { z } from "zod";
|
|
28
30
|
import { formatAgentCatalog } from "../../agents/sandbox-catalog.js";
|
|
29
31
|
import { composeSystemPrompt } from "../../agents/system-prompt.js";
|
|
30
|
-
import {
|
|
31
|
-
import { passthroughStep } from "../../loop/run-step.js";
|
|
32
|
+
import { DEFAULT_SALVAGE_REQUESTS, runWithSalvage } from "../../loop/run-loop.js";
|
|
32
33
|
import { forSubAgent, scopeResource } from "../../auth/types.js";
|
|
34
|
+
import { PLAN_ID_RE } from "../../contracts/schemas/chat-parts.js";
|
|
33
35
|
import { effectiveDeadlineMs } from "../../providers/types.js";
|
|
36
|
+
import { isUnknownArray } from "../../lib/unknown-array.js";
|
|
37
|
+
import { parseUnknownRecord } from "../../lib/unknown-containers.js";
|
|
34
38
|
import { defineTool } from "../define-tool.js";
|
|
35
39
|
import { createReadToolOutputTool } from "../read-tool-output.js";
|
|
36
40
|
import { createListAvailablePackagesTool } from "../sandbox/list-available-packages.js";
|
|
37
41
|
import { createListAvailableRefsTool } from "../sandbox/list-available-refs.js";
|
|
38
|
-
import { createReportBlockerToolFor } from "../sandbox/report-blocker.js";
|
|
42
|
+
import { blockerOf, createReportBlockerToolFor } from "../sandbox/report-blocker.js";
|
|
43
|
+
import { terminalTextSchema } from "../terminal-text.js";
|
|
39
44
|
import { searchGeoDatasetsTool } from "../bio/search-geo-datasets.js";
|
|
40
45
|
import { createNcbiTools } from "../bio/keys.js";
|
|
46
|
+
import { createKnowledgeTools } from "../knowledge/index.js";
|
|
41
47
|
import { queryDocsTool, resolveLibraryIdTool } from "./context7-docs.js";
|
|
42
48
|
import { searchArxivTool } from "./search-arxiv.js";
|
|
43
49
|
import { createSearchGithubReposTool } from "./search-github-repos.js";
|
|
@@ -50,8 +56,8 @@ import { validatePlan } from "../../schemas/validate-plan.js";
|
|
|
50
56
|
import { AnalysisPlanSchema } from "../../schemas/workflow-state.js";
|
|
51
57
|
import { createNoopLogger } from "../../lib/console-logger.js";
|
|
52
58
|
import { unwrapOrThrow } from "../../lib/result.js";
|
|
53
|
-
import { hintForZodIssue } from "../../lib/zod-issues.js";
|
|
54
|
-
import {
|
|
59
|
+
import { decodeObjectString, hintForZodIssue } from "../../lib/zod-issues.js";
|
|
60
|
+
import { loadDataProfileStatus, loadPlan, upsertPlan } from "../../state/index.js";
|
|
55
61
|
// ── Tool-level config ──────────────────────────────────────────────
|
|
56
62
|
/** Sub-agent identity for the planner — provenance only. */
|
|
57
63
|
const PLANNER_AGENT_ID = "planner";
|
|
@@ -62,17 +68,25 @@ const PLANNER_AGENT_ID = "planner";
|
|
|
62
68
|
* The number is deliberately far above what a plan costs. A well-grounded plan
|
|
63
69
|
* still costs two or three calls, because the prompt gates the research phase on
|
|
64
70
|
* what the seed does not already answer. What the ceiling must never do is stop
|
|
65
|
-
* a planner that is searching correctly. A capped run
|
|
66
|
-
*
|
|
67
|
-
*
|
|
71
|
+
* a planner that is searching correctly. A capped run gets the salvage request
|
|
72
|
+
* and submits the plan that it had, not the plan that it was building. Thus the
|
|
73
|
+
* wall-clock guard, not this number, is what bounds the worst case.
|
|
68
74
|
*/
|
|
69
75
|
const PLANNER_MAX_ITERATIONS = 300;
|
|
70
76
|
/** Wall-clock guard for a single plan-generation invocation. */
|
|
71
77
|
const PLAN_TIMEOUT_MS = 1_800_000;
|
|
78
|
+
/**
|
|
79
|
+
* The refusals of the call guard that end the search phase of one plan. A
|
|
80
|
+
* planner that the guard refused this many times is not searching, it is
|
|
81
|
+
* looping, and the salvage request lets it submit the plan it has. Six
|
|
82
|
+
* is above any count a frontier planner reached in the Phase 0 campaign
|
|
83
|
+
* (zero) and far below the hundred-plus refused calls of a looping run.
|
|
84
|
+
*/
|
|
85
|
+
const PLANNER_REFUSAL_LIMIT = 6;
|
|
72
86
|
// ── Diagnostic bounds ───────────────────────────────────────────────
|
|
73
87
|
//
|
|
74
|
-
// This tool
|
|
75
|
-
//
|
|
88
|
+
// This tool writes no ledger row before its persist and owns no durable stream,
|
|
89
|
+
// so its log records are the ONLY account of an invocation that
|
|
76
90
|
// survives the turn. That makes them worth spending detail on — and makes every
|
|
77
91
|
// one of them a place an unbounded model-authored string could reach a log file.
|
|
78
92
|
// Each bound below is what keeps a record readable and a sink affordable when the
|
|
@@ -114,7 +128,12 @@ function bounded(value, max) {
|
|
|
114
128
|
const MODEL_AUTHORED_KEY = "modelAuthored";
|
|
115
129
|
/** Nest model-authored prose under {@link MODEL_AUTHORED_KEY}, or contribute nothing when there is none. */
|
|
116
130
|
function modelAuthored(fields) {
|
|
117
|
-
const present = Object.entries(fields).
|
|
131
|
+
const present = Object.entries(fields).reduce((kept, entry) => {
|
|
132
|
+
const value = entry[1];
|
|
133
|
+
if (value !== undefined && value.length > 0)
|
|
134
|
+
kept.push([entry[0], value]);
|
|
135
|
+
return kept;
|
|
136
|
+
}, []);
|
|
118
137
|
return present.length === 0 ? {} : { [MODEL_AUTHORED_KEY]: Object.fromEntries(present) };
|
|
119
138
|
}
|
|
120
139
|
function toRejectionRecord(attempt, issues) {
|
|
@@ -130,8 +149,24 @@ function toRejectionRecord(attempt, issues) {
|
|
|
130
149
|
messages: kept.map((i) => bounded(i.message, MAX_LOGGED_ISSUE_CHARS)),
|
|
131
150
|
};
|
|
132
151
|
}
|
|
133
|
-
function
|
|
134
|
-
|
|
152
|
+
function traceOf(steps) {
|
|
153
|
+
let submitAttempts = 0;
|
|
154
|
+
let rejectedAttempts = 0;
|
|
155
|
+
const rejections = [];
|
|
156
|
+
for (const step of steps) {
|
|
157
|
+
for (const call of step.calls) {
|
|
158
|
+
if (call.toolName !== SUBMIT_PLAN_TOOL_ID || call.output === undefined)
|
|
159
|
+
continue;
|
|
160
|
+
submitAttempts += 1;
|
|
161
|
+
const issues = submitIssuesOf(call.output);
|
|
162
|
+
if (issues === undefined)
|
|
163
|
+
continue;
|
|
164
|
+
rejectedAttempts += 1;
|
|
165
|
+
if (rejections.length < MAX_LOGGED_REJECTIONS)
|
|
166
|
+
rejections.push(toRejectionRecord(submitAttempts, issues));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { submitAttempts, rejectedAttempts, rejections };
|
|
135
170
|
}
|
|
136
171
|
// ── Prompt / catalog ────────────────────────────────────────────────
|
|
137
172
|
/** The planner system prompt is deterministic per policy — build it once per
|
|
@@ -139,6 +174,53 @@ function createPlannerTrace() {
|
|
|
139
174
|
function plannerInstructions(resourcePolicy) {
|
|
140
175
|
return plannerPrompt(formatAgentCatalog(), resourcePolicy);
|
|
141
176
|
}
|
|
177
|
+
const ValidationIssueSchema = z.object({ path: z.string(), code: z.enum(["schema", "semantic"]), message: z.string(), hint: z.string().optional() });
|
|
178
|
+
const SubmitPlanOutputSchema = z.discriminatedUnion("accepted", [
|
|
179
|
+
z.object({ accepted: z.literal(false), issues: z.array(ValidationIssueSchema) }),
|
|
180
|
+
z.object({ accepted: z.literal(true), plan: PlannerPlanSchema }),
|
|
181
|
+
]);
|
|
182
|
+
const SUBMIT_PLAN_TOOL_ID = "submit_plan";
|
|
183
|
+
const REQUEST_CLARIFICATION_TOOL_ID = "request_clarification";
|
|
184
|
+
const ClarificationSchema = z.object({ kind: z.literal("clarification"), question: z.string(), questionContext: z.string().optional() });
|
|
185
|
+
/** The issues of a refused submission, or `undefined` for an accepted one and for an output that is no submission. */
|
|
186
|
+
function submitIssuesOf(output) {
|
|
187
|
+
if (output.outcome === "error")
|
|
188
|
+
return [{ path: "plan", code: "schema", message: output.text }];
|
|
189
|
+
if (output.outcome !== "ok")
|
|
190
|
+
return undefined;
|
|
191
|
+
const parsed = SubmitPlanOutputSchema.safeParse(output.value);
|
|
192
|
+
return parsed.success && !parsed.data.accepted ? parsed.data.issues : undefined;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The outcome of a run, selected from its recorded calls: the last accepted
|
|
196
|
+
* plan, else the last clarification, else the first blocker.
|
|
197
|
+
*/
|
|
198
|
+
function selectedOutcomeOf(steps) {
|
|
199
|
+
let plan;
|
|
200
|
+
let clarification;
|
|
201
|
+
for (const step of steps) {
|
|
202
|
+
for (const call of step.calls) {
|
|
203
|
+
if (call.output?.outcome !== "ok")
|
|
204
|
+
continue;
|
|
205
|
+
if (call.toolName === SUBMIT_PLAN_TOOL_ID) {
|
|
206
|
+
const parsed = SubmitPlanOutputSchema.safeParse(call.output.value);
|
|
207
|
+
if (parsed.success && parsed.data.accepted)
|
|
208
|
+
plan = parsed.data.plan;
|
|
209
|
+
}
|
|
210
|
+
else if (call.toolName === REQUEST_CLARIFICATION_TOOL_ID) {
|
|
211
|
+
const parsed = ClarificationSchema.safeParse(call.output.value);
|
|
212
|
+
if (parsed.success)
|
|
213
|
+
clarification = { kind: "clarification", question: parsed.data.question, questionContext: parsed.data.questionContext };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (plan !== undefined)
|
|
218
|
+
return { kind: "plan", plan };
|
|
219
|
+
if (clarification !== undefined)
|
|
220
|
+
return clarification;
|
|
221
|
+
const blocker = blockerOf(steps);
|
|
222
|
+
return blocker === null ? null : { kind: "blocker", reason: blocker.reason };
|
|
223
|
+
}
|
|
142
224
|
// ── Prior plan serialization (iteration context) ───────────────────
|
|
143
225
|
/**
|
|
144
226
|
* Format a loaded prior plan as a markdown block the planner can read —
|
|
@@ -255,6 +337,10 @@ function renderDataContext(grounding, analysisId) {
|
|
|
255
337
|
}
|
|
256
338
|
}
|
|
257
339
|
// ── Persistence ─────────────────────────────────────────────────────
|
|
340
|
+
/** The id of the plan of one call. A rerun of the call gives the same id, thus the upsert keeps one plan. */
|
|
341
|
+
function planIdFor(ctx) {
|
|
342
|
+
return `pln-${createHash("sha256").update(`${ctx.analysisId}:${ctx.invocationId}`).digest("hex").slice(0, 8)}`;
|
|
343
|
+
}
|
|
258
344
|
/**
|
|
259
345
|
* Persist a validated plan. Returns the planId, or an outcome-ready error
|
|
260
346
|
* with a sanitized message — DB-shape errors never leak to the planner.
|
|
@@ -266,7 +352,9 @@ function renderDataContext(grounding, analysisId) {
|
|
|
266
352
|
*/
|
|
267
353
|
async function persistPlan(plan, ctx, pool, logger) {
|
|
268
354
|
try {
|
|
269
|
-
const planId =
|
|
355
|
+
const planId = planIdFor(ctx);
|
|
356
|
+
unwrapOrThrow(await upsertPlan(pool, {
|
|
357
|
+
planId,
|
|
270
358
|
analysisId: ctx.analysisId,
|
|
271
359
|
plan: hydratePlanSteps(plan),
|
|
272
360
|
parentPlanId: ctx.parentPlanId,
|
|
@@ -274,8 +362,9 @@ async function persistPlan(plan, ctx, pool, logger) {
|
|
|
274
362
|
return { ok: true, planId };
|
|
275
363
|
}
|
|
276
364
|
catch (err) {
|
|
277
|
-
const raw = err
|
|
278
|
-
const
|
|
365
|
+
const raw = errorMessageOf(err) ?? textOf(err);
|
|
366
|
+
const lower = raw.toLowerCase();
|
|
367
|
+
const isParentValidation = lower.startsWith("parent plan ") && (lower.endsWith("not found") || lower.endsWith("belongs to a different analysis"));
|
|
279
368
|
logger.error("plan persistence failed", {
|
|
280
369
|
parentPlanId: ctx.parentPlanId,
|
|
281
370
|
stepCount: plan.steps.length,
|
|
@@ -297,14 +386,153 @@ function zodIssuesToValidationIssues(error, input, rootPath = "plan") {
|
|
|
297
386
|
hint: hintForZodIssue(i, input),
|
|
298
387
|
}));
|
|
299
388
|
}
|
|
389
|
+
function rememberAnswer(memory, answer) {
|
|
390
|
+
memory.steps.clear();
|
|
391
|
+
memory.claims.clear();
|
|
392
|
+
memory.digest = answer.snapshot.digest;
|
|
393
|
+
for (const step of answer.plan_skeleton) {
|
|
394
|
+
memory.steps.set(step.id, step);
|
|
395
|
+
for (const claim of step.grounding.claims)
|
|
396
|
+
memory.claims.add(claim);
|
|
397
|
+
for (const alternative of step.alternatives)
|
|
398
|
+
for (const rule of alternative.rules)
|
|
399
|
+
memory.claims.add(rule);
|
|
400
|
+
if (step.disputed !== undefined)
|
|
401
|
+
memory.claims.add(step.disputed.rule);
|
|
402
|
+
}
|
|
403
|
+
for (const claim of answer.claims)
|
|
404
|
+
memory.claims.add(claim.id);
|
|
405
|
+
for (const flag of answer.flags)
|
|
406
|
+
memory.claims.add(flag.rule);
|
|
407
|
+
}
|
|
408
|
+
/** The rule of a claim id: `R-0001` of `R-0001@e7d0`. */
|
|
409
|
+
function ruleOf(claim) {
|
|
410
|
+
return claim.split("@")[0] ?? claim;
|
|
411
|
+
}
|
|
412
|
+
/** One claim id per rule of the answer. A rule with two ids in one answer cannot be repaired, and it is absent. */
|
|
413
|
+
function claimsByRule(claims) {
|
|
414
|
+
const byRule = new Map();
|
|
415
|
+
const ambiguous = new Set();
|
|
416
|
+
for (const claim of claims) {
|
|
417
|
+
const rule = ruleOf(claim);
|
|
418
|
+
if (byRule.has(rule))
|
|
419
|
+
ambiguous.add(rule);
|
|
420
|
+
else
|
|
421
|
+
byRule.set(rule, claim);
|
|
422
|
+
}
|
|
423
|
+
for (const rule of ambiguous)
|
|
424
|
+
byRule.delete(rule);
|
|
425
|
+
return byRule;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Restore the grounding fields the host owns on each candidate step, from the
|
|
429
|
+
* last answer. A model copies the skeleton into the plan, and it drops the
|
|
430
|
+
* template field at times, or it miscopies the 64-character digest or the
|
|
431
|
+
* hash of a claim id, while it keeps the claims and the settings.
|
|
432
|
+
*
|
|
433
|
+
* On a step that copies a skeleton step, by step id, the host stamps the
|
|
434
|
+
* snapshot digest of the answer and restores a dropped template: the digest
|
|
435
|
+
* is what pins the step to the snapshot, and the template is what the host
|
|
436
|
+
* binds the contract to. A step whose snapshot is `none` states that it does
|
|
437
|
+
* not use the answer, and it stays as it is. A step whose status is
|
|
438
|
+
* `ungrounded` states a departure from the answer: the digest is stamped,
|
|
439
|
+
* and no template is restored, because the template realizes the method the
|
|
440
|
+
* step left.
|
|
441
|
+
*
|
|
442
|
+
* On every step, a claim id the answer did not return is repaired when the
|
|
443
|
+
* answer holds one claim of the same rule: one snapshot holds one version of
|
|
444
|
+
* a rule, thus the rule names the claim. A claim of a rule the answer does
|
|
445
|
+
* not hold is an issue that rejects the plan, so the model cites a claim of
|
|
446
|
+
* the answer or removes it.
|
|
447
|
+
*
|
|
448
|
+
* A step the skeleton does not hold, for example a robustness step the model
|
|
449
|
+
* adds under its own id, gets the digest stamped too when one of its claims
|
|
450
|
+
* is a claim of the answer: the claim vouches that the step consulted the
|
|
451
|
+
* answer, and the digest it typed is as prone to a miscopy as any other. A
|
|
452
|
+
* step with no claim of the answer stays as the model wrote it, because
|
|
453
|
+
* nothing of the answer vouches for it. Returns the stamped steps, the
|
|
454
|
+
* restored steps, the repaired claims, and the issues beside the new
|
|
455
|
+
* candidate. A candidate that is not a plan-shaped object, or a plan with no
|
|
456
|
+
* answer, comes back as it is.
|
|
457
|
+
*/
|
|
458
|
+
function restoreSkeletonGrounding(candidate, memory) {
|
|
459
|
+
const decoded = decodeObjectString(candidate);
|
|
460
|
+
const decodedRecord = parseUnknownRecord(decoded);
|
|
461
|
+
const decodedSteps = decodedRecord?.steps;
|
|
462
|
+
if (memory.steps.size === 0 || !isUnknownArray(decodedSteps)) {
|
|
463
|
+
return { candidate: decoded, stamped: [], restored: [], repaired: [], unknown: [] };
|
|
464
|
+
}
|
|
465
|
+
const byRule = claimsByRule(memory.claims);
|
|
466
|
+
const stamped = [];
|
|
467
|
+
const restored = [];
|
|
468
|
+
const repaired = [];
|
|
469
|
+
const unknown = [];
|
|
470
|
+
const steps = decodedSteps.map((step, index) => {
|
|
471
|
+
const fields = parseUnknownRecord(step);
|
|
472
|
+
if (fields === undefined)
|
|
473
|
+
return step;
|
|
474
|
+
const { id, grounding } = fields;
|
|
475
|
+
const current = parseUnknownRecord(grounding);
|
|
476
|
+
if (current === undefined)
|
|
477
|
+
return step;
|
|
478
|
+
const label = typeof id === "string" ? id : String(index);
|
|
479
|
+
let next = current;
|
|
480
|
+
const source = typeof id === "string" ? memory.steps.get(id) : undefined;
|
|
481
|
+
if (source !== undefined && current.snapshot !== "none") {
|
|
482
|
+
if (next.snapshot !== source.grounding.snapshot) {
|
|
483
|
+
stamped.push(label);
|
|
484
|
+
next = { ...next, snapshot: source.grounding.snapshot };
|
|
485
|
+
}
|
|
486
|
+
if (next.template === undefined && source.grounding.template !== undefined && current.status !== "ungrounded") {
|
|
487
|
+
restored.push(label);
|
|
488
|
+
next = { ...next, template: source.grounding.template };
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (isUnknownArray(current.claims)) {
|
|
492
|
+
let changed;
|
|
493
|
+
const claims = current.claims.map((claim, position) => {
|
|
494
|
+
if (typeof claim !== "string" || memory.claims.has(claim))
|
|
495
|
+
return claim;
|
|
496
|
+
const match = byRule.get(ruleOf(claim));
|
|
497
|
+
if (match !== undefined) {
|
|
498
|
+
repaired.push({ step: label, from: claim, to: match });
|
|
499
|
+
changed = true;
|
|
500
|
+
return match;
|
|
501
|
+
}
|
|
502
|
+
unknown.push({
|
|
503
|
+
path: `plan.steps.${index}.grounding.claims.${position}`,
|
|
504
|
+
code: "semantic",
|
|
505
|
+
message: `Claim ${claim} is not a claim of the knowledge_recommend answer of this run. Cite a claim id the answer returned, or remove it.`,
|
|
506
|
+
});
|
|
507
|
+
return claim;
|
|
508
|
+
});
|
|
509
|
+
if (changed)
|
|
510
|
+
next = { ...next, claims };
|
|
511
|
+
}
|
|
512
|
+
// The repaired claims are the ones compared, thus a step whose only claim was miscopied still gets the digest.
|
|
513
|
+
const vouched = Array.isArray(next.claims) && next.claims.some((claim) => typeof claim === "string" && memory.claims.has(claim));
|
|
514
|
+
if (source === undefined && vouched && memory.digest !== undefined && current.snapshot !== "none" && next.snapshot !== memory.digest) {
|
|
515
|
+
stamped.push(label);
|
|
516
|
+
next = { ...next, snapshot: memory.digest };
|
|
517
|
+
}
|
|
518
|
+
return next === current ? step : { ...fields, grounding: next };
|
|
519
|
+
});
|
|
520
|
+
return { candidate: { ...decodedRecord, steps }, stamped, restored, repaired, unknown };
|
|
521
|
+
}
|
|
300
522
|
/**
|
|
301
523
|
* Full validation: Zod schema + semantic checks. The plan is valid only if
|
|
302
524
|
* BOTH pass.
|
|
303
525
|
*/
|
|
304
526
|
function fullyValidate(candidate, resourcePolicy) {
|
|
305
|
-
|
|
527
|
+
// The permissive arg schema of the planner tools accepts a string, thus the
|
|
528
|
+
// loop-boundary repair never sees a plan that a model sent as a JSON-encoded
|
|
529
|
+
// string. A small model does this on a large nested schema. Decode it here,
|
|
530
|
+
// the same as run-synthesis does, so the schema issues describe the plan
|
|
531
|
+
// inside the string and not the string.
|
|
532
|
+
const decoded = decodeObjectString(candidate);
|
|
533
|
+
const parsed = PlannerPlanSchema.safeParse(decoded);
|
|
306
534
|
if (!parsed.success) {
|
|
307
|
-
return { valid: false, issues: zodIssuesToValidationIssues(parsed.error,
|
|
535
|
+
return { valid: false, issues: zodIssuesToValidationIssues(parsed.error, decoded) };
|
|
308
536
|
}
|
|
309
537
|
// Semantic checks operate on the AnalysisPlan shape — PlannerPlan omits
|
|
310
538
|
// maxSteps, so inject the execution-time default to satisfy the validator.
|
|
@@ -332,19 +560,18 @@ function fullyValidate(candidate, resourcePolicy) {
|
|
|
332
560
|
return { valid: true, plan: parsed.data };
|
|
333
561
|
}
|
|
334
562
|
/**
|
|
335
|
-
* Build the planner's inner tools for one invocation.
|
|
336
|
-
*
|
|
337
|
-
* loop finishes.
|
|
563
|
+
* Build the planner's inner tools for one invocation. `memory` holds the last
|
|
564
|
+
* `knowledge_recommend` answer of the run, which the loop folds after each round.
|
|
338
565
|
*/
|
|
339
|
-
function buildInnerTools(
|
|
566
|
+
function buildInnerTools(resourcePolicy, logger, memory) {
|
|
340
567
|
const submitPlanTool = defineTool({
|
|
341
|
-
id:
|
|
342
|
-
description: "Submit the
|
|
343
|
-
"
|
|
344
|
-
"the planning session. On " +
|
|
568
|
+
id: SUBMIT_PLAN_TOOL_ID,
|
|
569
|
+
description: "Submit the plan. Checks the plan; on success returns {accepted: true, plan} " +
|
|
570
|
+
"with the plan as the harness saves it — stop after an accepted plan. On " +
|
|
345
571
|
"rejection returns {accepted: false, issues} — fix and call again, " +
|
|
346
|
-
"or switch to report_blocker if the plan cannot be made valid.
|
|
347
|
-
"
|
|
572
|
+
"or switch to report_blocker if the plan cannot be made valid. The last " +
|
|
573
|
+
"accepted plan is the plan of this session. This arg schema is the " +
|
|
574
|
+
"authoritative plan contract.",
|
|
348
575
|
// Permissive on purpose: every candidate reaches `execute`, where the
|
|
349
576
|
// model gets structured schema and semantic issues in one response.
|
|
350
577
|
// The field-by-field contract remains visible in this tool's description
|
|
@@ -356,123 +583,51 @@ function buildInnerTools(holder, trace, persistCtx, pool, resourcePolicy, logger
|
|
|
356
583
|
plan: z.union([PlannerPlanSchema, z.unknown()]).describe("Candidate plan; malformed values are reported in issues."),
|
|
357
584
|
}),
|
|
358
585
|
describeCall: "none",
|
|
359
|
-
execute:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
const attempt = ++trace.submitAttempts;
|
|
378
|
-
const result = fullyValidate(input.plan, resourcePolicy);
|
|
379
|
-
if (!result.valid) {
|
|
380
|
-
trace.rejectedAttempts++;
|
|
381
|
-
const rejection = toRejectionRecord(attempt, result.issues);
|
|
382
|
-
if (trace.rejections.length < MAX_LOGGED_REJECTIONS)
|
|
383
|
-
trace.rejections.push(rejection);
|
|
586
|
+
execute: (input) => {
|
|
587
|
+
// The host owns the grounding fields: it fills them from the last answer of the run.
|
|
588
|
+
const { candidate, stamped, restored, repaired, unknown } = restoreSkeletonGrounding(input.plan, memory);
|
|
589
|
+
if (stamped.length > 0)
|
|
590
|
+
logger.info("submit_plan stamped the snapshot digest of the answer on steps", { steps: stamped });
|
|
591
|
+
if (restored.length > 0)
|
|
592
|
+
logger.info("submit_plan restored the template of skeleton steps", { steps: restored });
|
|
593
|
+
if (repaired.length > 0)
|
|
594
|
+
logger.info("submit_plan repaired the claim ids of plan steps", { claims: repaired });
|
|
595
|
+
const result = fullyValidate(candidate, resourcePolicy);
|
|
596
|
+
// An unknown claim rejects a plan that the schema accepts, beside the
|
|
597
|
+
// schema issues when the schema rejects it, thus one attempt shows both.
|
|
598
|
+
if (!result.valid || unknown.length > 0) {
|
|
599
|
+
const issues = [...(result.valid ? [] : result.issues), ...unknown];
|
|
384
600
|
// `warn`, not `debug`: a rejection is non-terminal by design, so the model
|
|
385
601
|
// reads the issues and the invocation carries on — nothing else reports that
|
|
386
|
-
// an attempt was spent.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return ok({ accepted: false, issues: result.issues });
|
|
602
|
+
// an attempt was spent.
|
|
603
|
+
logger.warn("submit_plan rejected a plan", { issueCount: issues.length, paths: issues.slice(0, MAX_LOGGED_ISSUES).map((i) => i.path) });
|
|
604
|
+
return Promise.resolve(ok({ accepted: false, issues }));
|
|
390
605
|
}
|
|
391
|
-
const persisted = await persistPlan(result.plan, persistCtx, pool, logger);
|
|
392
|
-
if (!persisted.ok) {
|
|
393
|
-
holder.outcome = { kind: "persist_error", message: persisted.message };
|
|
394
|
-
return ok({
|
|
395
|
-
accepted: false,
|
|
396
|
-
issues: [
|
|
397
|
-
{
|
|
398
|
-
path: "plan",
|
|
399
|
-
code: "semantic",
|
|
400
|
-
message: persisted.message,
|
|
401
|
-
hint: "Persistence failed — do not retry submit_plan.",
|
|
402
|
-
},
|
|
403
|
-
],
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
holder.outcome = {
|
|
407
|
-
kind: "plan_submitted",
|
|
408
|
-
planId: persisted.planId,
|
|
409
|
-
plan: result.plan,
|
|
410
|
-
};
|
|
411
|
-
// The attempt count is the cost of this plan, and it is only knowable here.
|
|
412
|
-
// A plan accepted on attempt 5 is a success that nearly was not one.
|
|
413
606
|
logger.info("submit_plan accepted a plan", {
|
|
414
|
-
planId: persisted.planId,
|
|
415
|
-
attempt,
|
|
416
607
|
stepCount: result.plan.steps.length,
|
|
417
608
|
agents: [...new Set(result.plan.steps.map((s) => s.agent))],
|
|
418
609
|
});
|
|
419
|
-
return ok({ accepted: true,
|
|
610
|
+
return Promise.resolve(ok({ accepted: true, plan: result.plan }));
|
|
420
611
|
},
|
|
421
612
|
});
|
|
422
613
|
const requestClarificationTool = defineTool({
|
|
423
|
-
id:
|
|
614
|
+
id: REQUEST_CLARIFICATION_TOOL_ID,
|
|
424
615
|
description: "Terminal. Use when a specific fact you need is missing from the " +
|
|
425
616
|
"input and cannot be inferred. The question reaches the user through " +
|
|
426
|
-
"the conversation agent
|
|
427
|
-
"plan saved.",
|
|
617
|
+
"the conversation agent, and no plan is saved. Stop after this call.",
|
|
428
618
|
inputSchema: z.object({
|
|
429
|
-
question:
|
|
619
|
+
question: terminalTextSchema("One short question that names each missing fact, and the step that needs it."),
|
|
430
620
|
questionContext: z.string().optional().describe("Optional context: why the plan needs the fact, and what the inputs already say about it."),
|
|
431
621
|
}),
|
|
432
622
|
describeCall: "none",
|
|
433
|
-
execute:
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
});
|
|
440
|
-
return ok({ recorded: true });
|
|
441
|
-
}
|
|
442
|
-
holder.outcome = {
|
|
443
|
-
kind: "clarification",
|
|
444
|
-
question: input.question,
|
|
445
|
-
questionContext: input.questionContext,
|
|
446
|
-
};
|
|
447
|
-
// The question is the answer to "why did planning stop?" — and it reaches the
|
|
448
|
-
// user through the conversation agent, which may paraphrase it into something
|
|
449
|
-
// that no longer identifies the missing fact.
|
|
450
|
-
logger.info("planner requested clarification", {
|
|
451
|
-
submitAttempts: trace.submitAttempts,
|
|
452
|
-
...modelAuthored({ clarificationQuestion: bounded(input.question, MAX_LOGGED_PROSE_CHARS) }),
|
|
453
|
-
});
|
|
454
|
-
return ok({ recorded: true });
|
|
455
|
-
},
|
|
623
|
+
execute: (input) => Promise.resolve(ok({
|
|
624
|
+
kind: "clarification",
|
|
625
|
+
question: input.question,
|
|
626
|
+
questionContext: input.questionContext,
|
|
627
|
+
message: "Clarification recorded. Stop now.",
|
|
628
|
+
})),
|
|
456
629
|
});
|
|
457
630
|
const reportBlockerTool = createReportBlockerToolFor({
|
|
458
|
-
record: (outcome) => {
|
|
459
|
-
if (holder.outcome !== null) {
|
|
460
|
-
trace.duplicateTerminalCalls++;
|
|
461
|
-
logger.warn("report_blocker called after a terminal outcome was recorded", {
|
|
462
|
-
recordedOutcome: holder.outcome.kind,
|
|
463
|
-
duplicateTerminalCalls: trace.duplicateTerminalCalls,
|
|
464
|
-
});
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
holder.outcome = outcome;
|
|
468
|
-
// A blocker after several rejected submits is a planner giving up on a plan it
|
|
469
|
-
// could not make valid — a validation problem wearing a blocker's clothes. The
|
|
470
|
-
// attempt count is what tells those apart, and only this record carries both.
|
|
471
|
-
logger.warn("planner reported a blocker", {
|
|
472
|
-
submitAttempts: trace.submitAttempts,
|
|
473
|
-
...modelAuthored({ blockerReason: bounded(outcome.reason, MAX_LOGGED_PROSE_CHARS) }),
|
|
474
|
-
});
|
|
475
|
-
},
|
|
476
631
|
blockedWhen: "Ends plan generation with no plan saved. Use it when no valid plan can " +
|
|
477
632
|
"be produced for this data and research question (out of scope, data " +
|
|
478
633
|
"incompatible with every available agent, etc.) — not for a plan you " +
|
|
@@ -495,17 +650,18 @@ function inventoryContent(label, result, logger) {
|
|
|
495
650
|
return `## ${label}\n\nInventory lookup failed: ${result.error.error}`;
|
|
496
651
|
}
|
|
497
652
|
const value = result.value;
|
|
498
|
-
|
|
499
|
-
|
|
653
|
+
const content = parseUnknownRecord(value)?.["content"];
|
|
654
|
+
if (typeof content === "string") {
|
|
655
|
+
return `## ${label}\n\n${content}`;
|
|
500
656
|
}
|
|
501
657
|
return `## ${label}\n\n${JSON.stringify(value)}`;
|
|
502
658
|
}
|
|
503
659
|
// ── Transcript reading (diagnostics only) ───────────────────────────
|
|
504
660
|
/**
|
|
505
|
-
* The planner's own account of the run, read off the message array
|
|
661
|
+
* The planner's own account of the run, read off the message array that the run
|
|
506
662
|
* returns. Nothing else can produce it: the transcript is ephemeral — a sub-agent
|
|
507
|
-
* loop
|
|
508
|
-
*
|
|
663
|
+
* loop persists no messages anywhere — so it is read here or it is lost with the
|
|
664
|
+
* turn.
|
|
509
665
|
*
|
|
510
666
|
* `toolCalls` in order is what shows the SHAPE of a failure at a glance:
|
|
511
667
|
* `[submit_plan × 13]` is a planner thrashing on validation, `[]` is a planner
|
|
@@ -540,32 +696,34 @@ function readTranscript(messages) {
|
|
|
540
696
|
* The loop half of the finish record: how the planner's run ended, and what it
|
|
541
697
|
* did to get there.
|
|
542
698
|
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
* (`length`) — which is why three identical-looking retries taught nobody anything.
|
|
699
|
+
* Without it, `no_outcome` is a single word covering three unrelated failures — a
|
|
700
|
+
* budget spent on rejected submits (`max_iterations`), a planner that answered in
|
|
701
|
+
* prose (`stop`), and a reply cut off at the output-token limit (`length`) — which
|
|
702
|
+
* is why three identical-looking retries taught nobody anything.
|
|
548
703
|
*/
|
|
549
704
|
function loopFields(run, kind) {
|
|
550
705
|
const transcript = readTranscript(run.messages);
|
|
551
|
-
|
|
706
|
+
const fields = {
|
|
552
707
|
loop: {
|
|
553
708
|
finishReason: run.finish.reason,
|
|
554
709
|
cappedOut: run.finish.cappedOut,
|
|
555
|
-
truncationRecoveries: run.finish.truncationRecoveries,
|
|
556
710
|
salvaged: run.salvage !== null,
|
|
557
|
-
|
|
711
|
+
firstFinishReason: run.salvage === null ? undefined : run.salvage.firstFinish.reason,
|
|
712
|
+
salvageFinishReason: run.salvage === null ? undefined : run.salvage.finish.reason,
|
|
558
713
|
toolCalls: transcript.toolCalls,
|
|
559
|
-
|
|
714
|
+
toolCallsTruncated: transcript.truncatedToolCalls ? true : undefined,
|
|
560
715
|
},
|
|
561
|
-
...(PROSE_EXPLAINS_OUTCOME.has(kind) ? modelAuthored({ plannerFinalProse: transcript.finalProse }) : {}),
|
|
562
716
|
};
|
|
717
|
+
if (PROSE_EXPLAINS_OUTCOME.has(kind))
|
|
718
|
+
Object.assign(fields, modelAuthored({ plannerFinalProse: transcript.finalProse }));
|
|
719
|
+
return fields;
|
|
563
720
|
}
|
|
564
721
|
/**
|
|
565
722
|
* The endings whose only account is what the planner said last.
|
|
566
723
|
*
|
|
567
|
-
* Everywhere else something better already explains the outcome:
|
|
568
|
-
*
|
|
724
|
+
* Everywhere else something better already explains the outcome: the selected
|
|
725
|
+
* result carries its own argument, the blocker record gives its reason, or a
|
|
726
|
+
* thrown cause carries the fault. Logging the
|
|
569
727
|
* prose there would put model output describing the user's data into a record
|
|
570
728
|
* that had no use for it — including on every successful plan, where the closing
|
|
571
729
|
* text is the planner narrating work that demonstrably went fine.
|
|
@@ -581,8 +739,7 @@ const PROSE_EXPLAINS_OUTCOME = new Set(["no_outcome", "loop_error", "timeout"]);
|
|
|
581
739
|
* caller actually returned.
|
|
582
740
|
*/
|
|
583
741
|
function shapeOutcome(args) {
|
|
584
|
-
const {
|
|
585
|
-
const outcome = holder.outcome;
|
|
742
|
+
const { outcome, runError, timedOut, outerAborted } = args;
|
|
586
743
|
if (outcome?.kind === "plan_submitted") {
|
|
587
744
|
return { output: { event: "plan_complete", planId: outcome.planId, plan: outcome.plan }, kind: "plan_submitted" };
|
|
588
745
|
}
|
|
@@ -591,7 +748,7 @@ function shapeOutcome(args) {
|
|
|
591
748
|
output: {
|
|
592
749
|
event: "clarification_needed",
|
|
593
750
|
question: outcome.question,
|
|
594
|
-
|
|
751
|
+
questionContext: outcome.questionContext ? outcome.questionContext : undefined,
|
|
595
752
|
},
|
|
596
753
|
kind: "clarification",
|
|
597
754
|
};
|
|
@@ -616,7 +773,7 @@ function shapeOutcome(args) {
|
|
|
616
773
|
return { output: { event: "error", error: "Plan generation was cancelled." }, kind: "cancelled" };
|
|
617
774
|
}
|
|
618
775
|
if (runError) {
|
|
619
|
-
const msg = runError
|
|
776
|
+
const msg = errorMessageOf(runError) ?? textOf(runError);
|
|
620
777
|
return { output: { event: "error", error: `Plan generation failed: ${msg}` }, kind: "loop_error" };
|
|
621
778
|
}
|
|
622
779
|
return {
|
|
@@ -657,7 +814,7 @@ const OUTCOME_LEVEL = {
|
|
|
657
814
|
* A tool here never writes and never computes. Thus the worst outcome of a
|
|
658
815
|
* needless call is latency, and the prompt is what bounds that.
|
|
659
816
|
*/
|
|
660
|
-
function buildPlannerSearchTools(deps) {
|
|
817
|
+
export function buildPlannerSearchTools(deps, hooks = {}) {
|
|
661
818
|
const bioKeys = deps.bioKeys;
|
|
662
819
|
const ncbi = createNcbiTools(bioKeys);
|
|
663
820
|
return [
|
|
@@ -669,7 +826,7 @@ function buildPlannerSearchTools(deps) {
|
|
|
669
826
|
// cross-field index that ranks a method paper best.
|
|
670
827
|
ncbi.pubmed,
|
|
671
828
|
searchArxivTool,
|
|
672
|
-
createSearchSemanticScholarTool({
|
|
829
|
+
createSearchSemanticScholarTool({ apiKey: bioKeys.semanticScholar }),
|
|
673
830
|
// The same approach as running code — a pipeline that someone published.
|
|
674
831
|
createSearchGithubReposTool({ githubToken: bioKeys.github }),
|
|
675
832
|
// The API of a staged package, so a step names a function that exists.
|
|
@@ -683,83 +840,78 @@ function buildPlannerSearchTools(deps) {
|
|
|
683
840
|
// The planner is a conversation surface, thus the pool-scope reader binds
|
|
684
841
|
// when the embedder gives one, and the answer names what the store HOLDS.
|
|
685
842
|
createListAvailablePackagesTool({
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
843
|
+
farmLockFile: deps.farmLockFile,
|
|
844
|
+
imagePackagesFile: deps.imagePackagesFile,
|
|
845
|
+
readPoolInventory: deps.readPoolInventory,
|
|
846
|
+
}),
|
|
847
|
+
// The knowledge plane: one cited procedure per situation, and one check
|
|
848
|
+
// of the draft. Both attach only when the embedder binds a client.
|
|
849
|
+
...createKnowledgeTools({
|
|
850
|
+
client: deps.knowledge,
|
|
851
|
+
farmLockFile: deps.farmLockFile,
|
|
852
|
+
imagePackagesFile: deps.imagePackagesFile,
|
|
853
|
+
refStorePath: deps.refStorePath,
|
|
854
|
+
onRecommend: hooks.onRecommend,
|
|
689
855
|
}),
|
|
690
856
|
];
|
|
691
857
|
}
|
|
858
|
+
const generatePlanInputSchema = z.object({
|
|
859
|
+
researchQuestion: z.string().describe("What the user wants to analyze — their goal and specific questions."),
|
|
860
|
+
analystNotes: z
|
|
861
|
+
.string()
|
|
862
|
+
.optional()
|
|
863
|
+
.describe("Facts about the data that ONLY the user could have told you and the data profile cannot know — " +
|
|
864
|
+
'e.g. "samples 3 and 7 were re-sequenced", "treat batch B as the reference", ' +
|
|
865
|
+
'"the tumor/normal labels in column 4 are swapped". ' +
|
|
866
|
+
"This is NOT a place to restate the data profile: organism, omics type, experimental design, " +
|
|
867
|
+
"condition names, file names and dimensions are already loaded server-side and given to the " +
|
|
868
|
+
"planner — repeating them here only risks contradicting the record. Omit entirely unless the " +
|
|
869
|
+
"user has told you something the profile does not already hold."),
|
|
870
|
+
priorRuns: z
|
|
871
|
+
.string()
|
|
872
|
+
.optional()
|
|
873
|
+
.describe("Summary of prior run results if any exist: which steps ran, " + "what succeeded/failed, key findings. Omit if no prior runs."),
|
|
874
|
+
userConstraints: z
|
|
875
|
+
.string()
|
|
876
|
+
.optional()
|
|
877
|
+
.describe("User-specified constraints: preferred methods, steps to " + "include/exclude, resource limits, modifications to a prior plan."),
|
|
878
|
+
parentPlanId: z
|
|
879
|
+
.string()
|
|
880
|
+
.regex(PLAN_ID_RE, "parentPlanId must be a pln-<8hex> value")
|
|
881
|
+
.optional()
|
|
882
|
+
.describe("The planId of the prior plan being iterated on. Set only when " + "the user is asking for modifications to an existing plan."),
|
|
883
|
+
});
|
|
692
884
|
/** Build the `generate_plan` tool bound to its provider and pool. */
|
|
693
|
-
export function createGeneratePlanTool(deps) {
|
|
885
|
+
export function createGeneratePlanTool(deps, registerSubAgent) {
|
|
694
886
|
const baseLogger = (deps.logger ?? createNoopLogger()).named("generate-plan");
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
.
|
|
713
|
-
.
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
.describe("Summary of prior run results if any exist: which steps ran, " + "what succeeded/failed, key findings. Omit if no prior runs."),
|
|
724
|
-
userConstraints: z
|
|
725
|
-
.string()
|
|
726
|
-
.optional()
|
|
727
|
-
.describe("User-specified constraints: preferred methods, steps to " + "include/exclude, resource limits, modifications to a prior plan."),
|
|
728
|
-
parentPlanId: z
|
|
729
|
-
.string()
|
|
730
|
-
.regex(/^pln-[a-f0-9]{8}$/, "parentPlanId must be a pln-<8hex> value")
|
|
731
|
-
.optional()
|
|
732
|
-
.describe("The planId of the prior plan being iterated on. Set only when " + "the user is asking for modifications to an existing plan."),
|
|
733
|
-
}),
|
|
734
|
-
describeCall: ({ researchQuestion }) => researchQuestion,
|
|
735
|
-
execute: async (input, ctx) => {
|
|
736
|
-
const analysisId = scopeResource(ctx.session.scope).resourceId;
|
|
737
|
-
// Bound per invocation: `analysisId` comes from the call-time session, not from
|
|
738
|
-
// the construction-time deps the factory closes over.
|
|
739
|
-
const logger = baseLogger.with({ analysisId });
|
|
740
|
-
const startedAt = Date.now();
|
|
741
|
-
const trace = createPlannerTrace();
|
|
742
|
-
// Every exit routes through here, so "recorded exactly once per invocation" holds
|
|
743
|
-
// by construction rather than by remembering. `elapsedMs` is what separates a
|
|
744
|
-
// planner that gave up early from one still working when the guard cut it — the
|
|
745
|
-
// fixed budget above makes the number readable without any other context.
|
|
746
|
-
//
|
|
747
|
-
// `extra` carries the loop account on the paths that reached the loop. The two
|
|
748
|
-
// parent-plan early returns never do, and a record claiming `loop.finishReason`
|
|
749
|
-
// for a run that never started would be worse than one that omits the field.
|
|
750
|
-
const finish = (shaped, extra = {}) => {
|
|
751
|
-
logger[OUTCOME_LEVEL[shaped.kind]]("plan generation finished", {
|
|
752
|
-
outcome: shaped.kind,
|
|
753
|
-
elapsedMs: Date.now() - startedAt,
|
|
754
|
-
submitAttempts: trace.submitAttempts,
|
|
755
|
-
rejectedAttempts: trace.rejectedAttempts,
|
|
756
|
-
...(trace.rejections.length > 0 ? { rejections: trace.rejections } : {}),
|
|
757
|
-
...(trace.rejectedAttempts > trace.rejections.length ? { rejectionsTruncated: true } : {}),
|
|
758
|
-
...(trace.duplicateTerminalCalls > 0 ? { duplicateTerminalCalls: trace.duplicateTerminalCalls } : {}),
|
|
759
|
-
...extra,
|
|
760
|
-
});
|
|
761
|
-
return ok(shaped.output);
|
|
762
|
-
};
|
|
887
|
+
const plan = registerSubAgent(PLANNER_AGENT_ID, async ({ payload: input, ctx, loop, durably }) => {
|
|
888
|
+
const analysisId = scopeResource(ctx.session.scope).resourceId;
|
|
889
|
+
// Bound per invocation: `analysisId` comes from the call-time session, not from
|
|
890
|
+
// the construction-time deps the factory closes over.
|
|
891
|
+
const logger = baseLogger.with({ analysisId });
|
|
892
|
+
const startedAt = Date.now();
|
|
893
|
+
let trace = { submitAttempts: 0, rejectedAttempts: 0, rejections: [] };
|
|
894
|
+
// Every exit routes through here, so "recorded exactly once per invocation" holds
|
|
895
|
+
// by construction rather than by remembering. `elapsedMs` is what separates a
|
|
896
|
+
// planner that gave up early from one still working when the guard cut it — the
|
|
897
|
+
// fixed budget above makes the number readable without any other context.
|
|
898
|
+
//
|
|
899
|
+
// `extra` carries the loop account on the paths that reached the loop. The two
|
|
900
|
+
// parent-plan early returns never do, and a record claiming `loop.finishReason`
|
|
901
|
+
// for a run that never started would be worse than one that omits the field.
|
|
902
|
+
const finish = (shaped, extra = {}) => {
|
|
903
|
+
logger[OUTCOME_LEVEL[shaped.kind]]("plan generation finished", {
|
|
904
|
+
outcome: shaped.kind,
|
|
905
|
+
elapsedMs: Date.now() - startedAt,
|
|
906
|
+
submitAttempts: trace.submitAttempts,
|
|
907
|
+
rejectedAttempts: trace.rejectedAttempts,
|
|
908
|
+
rejections: trace.rejections,
|
|
909
|
+
rejectionsTruncated: trace.rejectedAttempts > trace.rejections.length ? true : undefined,
|
|
910
|
+
...extra,
|
|
911
|
+
});
|
|
912
|
+
return shaped.output;
|
|
913
|
+
};
|
|
914
|
+
const seed = await durably("planner.seed", async () => {
|
|
763
915
|
// If iterating, load the parent plan so the planner sees what it is
|
|
764
916
|
// revising. Fails fast with the sanitized message submit_plan would
|
|
765
917
|
// have surfaced — saves a wasted planner run.
|
|
@@ -770,10 +922,12 @@ export function createGeneratePlanTool(deps) {
|
|
|
770
922
|
analysisId,
|
|
771
923
|
}));
|
|
772
924
|
if (!priorPlan) {
|
|
773
|
-
return
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
925
|
+
return {
|
|
926
|
+
early: {
|
|
927
|
+
output: { event: "error", error: "parentPlanId is not a valid plan for this analysis" },
|
|
928
|
+
kind: "invalid_parent_plan",
|
|
929
|
+
},
|
|
930
|
+
};
|
|
777
931
|
}
|
|
778
932
|
priorPlanBlock = formatPriorPlan(input.parentPlanId, priorPlan);
|
|
779
933
|
// `formatPriorPlan` returns null on a stored plan that no longer parses as an
|
|
@@ -785,10 +939,12 @@ export function createGeneratePlanTool(deps) {
|
|
|
785
939
|
}
|
|
786
940
|
catch (err) {
|
|
787
941
|
logger.error("parent plan could not be loaded", { parentPlanId: input.parentPlanId, ...logger.errorFields(err) });
|
|
788
|
-
return
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
942
|
+
return {
|
|
943
|
+
early: {
|
|
944
|
+
output: { event: "error", error: "Plan iteration failed — parent plan could not be loaded." },
|
|
945
|
+
kind: "parent_plan_load_failed",
|
|
946
|
+
},
|
|
947
|
+
};
|
|
792
948
|
}
|
|
793
949
|
}
|
|
794
950
|
// The dataset's facts come from the ledger, not from a string a model
|
|
@@ -803,11 +959,10 @@ export function createGeneratePlanTool(deps) {
|
|
|
803
959
|
// Degrading to "no profile" is correct behaviour and indistinguishable, from
|
|
804
960
|
// the outcome, from an analysis that genuinely has none — so the read failure
|
|
805
961
|
// is reported here or it is never known to have happened.
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
});
|
|
962
|
+
const fields = { dbErrorType: error.type, dbOp: error.op };
|
|
963
|
+
if ("cause" in error)
|
|
964
|
+
Object.assign(fields, logger.errorFields(error.cause));
|
|
965
|
+
logger.warn("data profile read failed — planning without dataset facts", fields);
|
|
811
966
|
return null;
|
|
812
967
|
});
|
|
813
968
|
const grounding = classifyGrounding(profileStatus);
|
|
@@ -817,9 +972,9 @@ export function createGeneratePlanTool(deps) {
|
|
|
817
972
|
// the loop and put their rendered content in the planner's seed.
|
|
818
973
|
const listAvailableRefs = createListAvailableRefsTool(deps.refStorePath === undefined ? {} : { refStorePath: deps.refStorePath });
|
|
819
974
|
const listAvailablePackages = createListAvailablePackagesTool({
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
975
|
+
farmLockFile: deps.farmLockFile,
|
|
976
|
+
imagePackagesFile: deps.imagePackagesFile,
|
|
977
|
+
readPoolInventory: deps.readPoolInventory,
|
|
823
978
|
});
|
|
824
979
|
// A path-separator query matches every reference path and selects the
|
|
825
980
|
// tool's recursive leaf scan, so the seed contains usable file paths,
|
|
@@ -839,102 +994,174 @@ export function createGeneratePlanTool(deps) {
|
|
|
839
994
|
...(input.priorRuns ? ["", "## Prior Run Results", input.priorRuns] : []),
|
|
840
995
|
...(input.userConstraints ? ["", "## User Constraints", input.userConstraints] : []),
|
|
841
996
|
].join("\n");
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
// first-order suspects when a planner runs long and returns nothing: the size
|
|
845
|
-
// of the seed it was handed, and which of its blocks made it that size. The
|
|
846
|
-
// reference inventory in particular is a recursive leaf scan of whatever this
|
|
847
|
-
// install has staged — a quantity the harness does not control and no other
|
|
848
|
-
// record reports. The census is sizes only: the seed carries the user's
|
|
849
|
-
// research question and dataset facts, which belong in the model's context and
|
|
850
|
-
// not in a log file.
|
|
851
|
-
logger.info("plan generation started", {
|
|
852
|
-
model: deps.conversation.model,
|
|
853
|
-
maxIterations: PLANNER_MAX_ITERATIONS,
|
|
854
|
-
salvageIterations: DEFAULT_SALVAGE_ITERATIONS,
|
|
855
|
-
timeoutMs: planTimeoutMs,
|
|
997
|
+
return {
|
|
998
|
+
prompt,
|
|
856
999
|
grounding: grounding.kind,
|
|
857
|
-
|
|
858
|
-
seedChars: {
|
|
859
|
-
total: prompt.length,
|
|
1000
|
+
chars: {
|
|
860
1001
|
priorPlan: priorPlanBlock?.length ?? 0,
|
|
861
1002
|
dataContext: dataContextBlock.length,
|
|
862
1003
|
referenceData: refsBlock.length,
|
|
863
1004
|
packages: packagesBlock.length,
|
|
864
|
-
researchQuestion: input.researchQuestion.length,
|
|
865
|
-
analystNotes: input.analystNotes?.length ?? 0,
|
|
866
|
-
priorRuns: input.priorRuns?.length ?? 0,
|
|
867
|
-
userConstraints: input.userConstraints?.length ?? 0,
|
|
868
1005
|
},
|
|
869
|
-
});
|
|
870
|
-
const holder = { outcome: null };
|
|
871
|
-
const persistCtx = {
|
|
872
|
-
analysisId,
|
|
873
|
-
parentPlanId: input.parentPlanId ?? null,
|
|
874
1006
|
};
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1007
|
+
});
|
|
1008
|
+
if ("early" in seed)
|
|
1009
|
+
return finish(seed.early);
|
|
1010
|
+
const { prompt } = seed;
|
|
1011
|
+
const planTimeoutMs = effectiveDeadlineMs(deps.conversation.provider, PLAN_TIMEOUT_MS);
|
|
1012
|
+
// Opens the invocation. Two things are only knowable here and both are
|
|
1013
|
+
// first-order suspects when a planner runs long and returns nothing: the size
|
|
1014
|
+
// of the seed it was handed, and which of its blocks made it that size. The
|
|
1015
|
+
// reference inventory in particular is a recursive leaf scan of whatever this
|
|
1016
|
+
// install has staged — a quantity the harness does not control and no other
|
|
1017
|
+
// record reports. The census is sizes only: the seed carries the user's
|
|
1018
|
+
// research question and dataset facts, which belong in the model's context and
|
|
1019
|
+
// not in a log file.
|
|
1020
|
+
logger.info("plan generation started", {
|
|
1021
|
+
model: deps.conversation.model,
|
|
1022
|
+
maxIterations: PLANNER_MAX_ITERATIONS,
|
|
1023
|
+
salvageRequests: DEFAULT_SALVAGE_REQUESTS,
|
|
1024
|
+
timeoutMs: planTimeoutMs,
|
|
1025
|
+
grounding: seed.grounding,
|
|
1026
|
+
parentPlanId: input.parentPlanId,
|
|
1027
|
+
seedChars: {
|
|
1028
|
+
total: prompt.length,
|
|
1029
|
+
...seed.chars,
|
|
1030
|
+
researchQuestion: input.researchQuestion.length,
|
|
1031
|
+
analystNotes: input.analystNotes?.length ?? 0,
|
|
1032
|
+
priorRuns: input.priorRuns?.length ?? 0,
|
|
1033
|
+
userConstraints: input.userConstraints?.length ?? 0,
|
|
1034
|
+
},
|
|
1035
|
+
});
|
|
1036
|
+
const persistCtx = {
|
|
1037
|
+
analysisId,
|
|
1038
|
+
parentPlanId: input.parentPlanId ?? null,
|
|
1039
|
+
invocationId: ctx.invocationId,
|
|
1040
|
+
};
|
|
1041
|
+
// The last recommend answer of this invocation, thus `submit_plan`
|
|
1042
|
+
// stamps the snapshot digest, restores a template the model dropped,
|
|
1043
|
+
// and repairs or refuses a claim id the answer did not return. The loop
|
|
1044
|
+
// folds each answer after its round, on the first run and on a replay.
|
|
1045
|
+
const memory = { steps: new Map(), claims: new Set() };
|
|
1046
|
+
const innerTools = buildInnerTools(deps.resourcePolicy, logger, memory);
|
|
1047
|
+
// Built here rather than at construction: a `describeCall` hook reads no
|
|
1048
|
+
// dep, thus the tool must stay constructible from an empty bag. The tool
|
|
1049
|
+
// definitions are identical across invocations, thus the request prefix
|
|
1050
|
+
// that the cache keys on does not move.
|
|
1051
|
+
const searchTools = buildPlannerSearchTools(deps, {
|
|
1052
|
+
onRecommend: (answer) => rememberAnswer(memory, answer),
|
|
1053
|
+
});
|
|
1054
|
+
const readTools = deps.toolOutputStore ? [createReadToolOutputTool(deps.toolOutputStore)] : [];
|
|
1055
|
+
const planner = {
|
|
1056
|
+
id: PLANNER_AGENT_ID,
|
|
1057
|
+
systemPrompt: composeSystemPrompt(plannerInstructions(deps.resourcePolicy)),
|
|
1058
|
+
model: deps.conversation.model,
|
|
1059
|
+
// The search tools come first, and the terminal tools come last.
|
|
1060
|
+
// The order is the order of the prompt, and it is stable across
|
|
1061
|
+
// every invocation, thus the cached request prefix holds.
|
|
1062
|
+
tools: [...searchTools, ...readTools, ...innerTools.terminal],
|
|
1063
|
+
maxIterations: PLANNER_MAX_ITERATIONS,
|
|
1064
|
+
};
|
|
1065
|
+
// Merge the outer abort signal with a wall-clock timeout — either
|
|
1066
|
+
// cancels the planner promptly.
|
|
1067
|
+
const signal = AbortSignal.any([ctx.signal, AbortSignal.timeout(planTimeoutMs)]);
|
|
1068
|
+
// The guard bounds the repeated calls of one plan: the third call with
|
|
1069
|
+
// an input the plan already sent, or the call past the budget of one
|
|
1070
|
+
// tool, answers a refusal that says to continue. The terminal tools
|
|
1071
|
+
// stay outside it, because a refused submit would strand the plan.
|
|
1072
|
+
let refusals = 0;
|
|
1073
|
+
let runError = null;
|
|
1074
|
+
let run = null;
|
|
1075
|
+
try {
|
|
1076
|
+
run = await runWithSalvage(planner, [{ role: "user", content: prompt }], forSubAgent(ctx.session, PLANNER_AGENT_ID), {
|
|
1077
|
+
...loop,
|
|
1078
|
+
provider: deps.conversation.provider,
|
|
1079
|
+
signal,
|
|
1080
|
+
policy: {
|
|
1081
|
+
guard: {
|
|
1082
|
+
tools: searchTools.map((tool) => tool.id),
|
|
1083
|
+
onRefusal: (refusal) => {
|
|
1084
|
+
refusals += 1;
|
|
1085
|
+
logger.warn("planner call refused by the guard", { ...refusal, refusals });
|
|
1086
|
+
},
|
|
1087
|
+
},
|
|
1088
|
+
},
|
|
1089
|
+
// A planner the guard refused too many times is looping:
|
|
1090
|
+
// the early cap ends the search, and the salvage request
|
|
1091
|
+
// submits the plan it has.
|
|
1092
|
+
capWhen: () => refusals >= PLANNER_REFUSAL_LIMIT,
|
|
1093
|
+
// No forced tool choice: some Claude models reject it with a
|
|
1094
|
+
// 400. The prompt names the terminal tools, and the salvage
|
|
1095
|
+
// request below recovers a run that ends on prose.
|
|
1096
|
+
logger,
|
|
1097
|
+
usageRecorder: deps.usageRecorder,
|
|
1098
|
+
toolOutputStore: deps.toolOutputStore,
|
|
1099
|
+
}, {
|
|
1100
|
+
tools: innerTools.terminal.map((tool) => tool.id),
|
|
1101
|
+
nudge: "You ended without a terminal outcome. Call submit_plan with " +
|
|
1102
|
+
"your final plan now, or request_clarification / report_blocker " +
|
|
1103
|
+
"if you cannot. Do not reply with prose.",
|
|
1104
|
+
hasOutcome: (steps) => selectedOutcomeOf(steps) !== null,
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
catch (err) {
|
|
1108
|
+
runError = err;
|
|
1109
|
+
}
|
|
1110
|
+
let outcome = null;
|
|
1111
|
+
if (run !== null) {
|
|
1112
|
+
trace = traceOf(run.steps);
|
|
1113
|
+
const selected = selectedOutcomeOf(run.steps);
|
|
1114
|
+
if (selected?.kind === "plan") {
|
|
1115
|
+
const persisted = await durably("planner.persist", () => persistPlan(selected.plan, persistCtx, deps.pool, logger));
|
|
1116
|
+
outcome = persisted.ok
|
|
1117
|
+
? { kind: "plan_submitted", planId: persisted.planId, plan: selected.plan }
|
|
1118
|
+
: { kind: "persist_error", message: persisted.message };
|
|
921
1119
|
}
|
|
922
|
-
|
|
923
|
-
|
|
1120
|
+
else {
|
|
1121
|
+
outcome = selected;
|
|
924
1122
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1123
|
+
if (selected?.kind === "blocker") {
|
|
1124
|
+
// A blocker after several rejected submits is a planner giving up on a plan it
|
|
1125
|
+
// could not make valid — a validation problem wearing a blocker's clothes. The
|
|
1126
|
+
// attempt count is what tells those apart, and only this record carries both.
|
|
1127
|
+
logger.warn("planner reported a blocker", {
|
|
1128
|
+
submitAttempts: trace.submitAttempts,
|
|
1129
|
+
...modelAuthored({ blockerReason: bounded(selected.reason, MAX_LOGGED_PROSE_CHARS) }),
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
const shaped = shapeOutcome({
|
|
1134
|
+
outcome,
|
|
1135
|
+
runError,
|
|
1136
|
+
timedOut: signal.aborted && !ctx.signal.aborted,
|
|
1137
|
+
outerAborted: ctx.signal.aborted,
|
|
1138
|
+
});
|
|
1139
|
+
const extra = {};
|
|
1140
|
+
if (run)
|
|
1141
|
+
Object.assign(extra, loopFields(run, shaped.kind));
|
|
1142
|
+
// A throw leaves no result to read the transcript off, so the cause takes its
|
|
1143
|
+
// place — `shapeOutcome` folds it into one prose sentence for the model, and
|
|
1144
|
+
// this is the only place its type and stack survive.
|
|
1145
|
+
if (runError)
|
|
1146
|
+
Object.assign(extra, logger.errorFields(runError));
|
|
1147
|
+
return finish(shaped, extra);
|
|
1148
|
+
});
|
|
1149
|
+
return defineTool({
|
|
1150
|
+
id: "generate_plan",
|
|
1151
|
+
description: "Generate an analysis plan (DAG of steps) for this analysis's data and the user's research question. " +
|
|
1152
|
+
"The dataset's own facts — domain, organism, experimental design, condition names, quality concerns, " +
|
|
1153
|
+
"per-file data types and dimensions — are read server-side from the persisted data profile and handed " +
|
|
1154
|
+
"to the planner directly. Do NOT summarize or re-type them into this call; you cannot restate that " +
|
|
1155
|
+
"record more faithfully than the record itself. Pass only what the profile cannot hold: the research " +
|
|
1156
|
+
"question, facts the user told you (analystNotes), prior run results, and their constraints. " +
|
|
1157
|
+
"The planner is a multi-step sub-agent, thus a call can take many minutes. " +
|
|
1158
|
+
"The result carries an `event`: `plan_complete` with {planId, plan} — the plan is saved and ready for show_plan and " +
|
|
1159
|
+
"execute_analysis plan mode, and this call neither shows nor runs it; `clarification_needed` with {question, questionContext?} " +
|
|
1160
|
+
"when the planner is missing a fact it cannot infer; or `error` with {error} when no plan was saved (the planner reported " +
|
|
1161
|
+
"a blocker, the save failed, the call timed out or was cancelled, or parentPlanId is not a plan of this analysis).",
|
|
1162
|
+
inputSchema: generatePlanInputSchema,
|
|
1163
|
+
describeCall: ({ researchQuestion }) => researchQuestion,
|
|
1164
|
+
startsWorkflow: true,
|
|
1165
|
+
execute: async (input, ctx) => ok(await plan(input, ctx)),
|
|
939
1166
|
});
|
|
940
1167
|
}
|