@inflexa-ai/harness 0.45.1 → 0.46.2
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 +9 -3
- package/dist/agents/conversation-agent.js +34 -33
- 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 +12 -14
- package/dist/agents/sandbox/shared.js +18 -12
- package/dist/agents/sandbox/types.d.ts +4 -4
- 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 +2 -2
- package/dist/agents/sandbox-catalog.js +3 -3
- package/dist/agents/system-prompt.d.ts +2 -2
- package/dist/app/chat-turn-workflow.d.ts +114 -0
- package/dist/app/chat-turn-workflow.js +306 -0
- package/dist/app/chat-turn.d.ts +85 -57
- package/dist/app/chat-turn.js +123 -100
- 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 +6 -6
- 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 +38 -24
- package/dist/contracts/chat-frame.d.ts +15 -5
- package/dist/contracts/chat-frame.js +64 -41
- 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 +2 -2
- 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 +30 -16
- package/dist/contracts/schemas/chat-events.js +15 -6
- package/dist/contracts/schemas/chat-parts.d.ts +175 -102
- package/dist/contracts/schemas/chat-parts.js +33 -7
- 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 +5 -6
- package/dist/execution/dbos-workflow-purger.js +8 -7
- 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 +7 -7
- package/dist/execution/run-canceler.js +14 -7
- package/dist/execution/run-end-record.d.ts +2 -3
- 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 +17 -20
- package/dist/index.js +25 -36
- 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 +32 -7
- package/dist/lib/hooks.js +22 -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 +9 -9
- package/dist/loop/compaction.js +1 -1
- 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 +160 -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 +39 -0
- package/dist/loop/turn-credentials.js +90 -0
- package/dist/loop/types.d.ts +29 -38
- 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 -4
- package/dist/memory/conversation-display-recorder.js +43 -24
- 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 +36 -21
- package/dist/memory/thread-history.js +78 -54
- 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 +12 -12
- package/dist/prompts/briefing.js +1 -1
- 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 +1 -4
- package/dist/provenance/types.d.ts +6 -6
- package/dist/providers/ai-sdk.d.ts +113 -62
- package/dist/providers/ai-sdk.js +283 -518
- 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/request-headers.js +12 -3
- package/dist/providers/types.d.ts +55 -94
- package/dist/reference-data/catalog.d.ts +2 -15
- package/dist/reference-data/catalog.js +25 -8
- 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 +211 -233
- 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.js +15 -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 +343 -298
- package/dist/schemas/file-metadata.js +2 -4
- package/dist/schemas/plan-schemas.d.ts +31 -31
- 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 +19 -19
- package/dist/schemas/workflow-state.js +2 -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 +32 -19
- package/dist/state/artifacts.js +28 -5
- 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 +4 -4
- package/dist/state/index.js +2 -2
- 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 +30 -19
- package/dist/state/runs.js +43 -51
- 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 +20 -8
- package/dist/state/step-executions.js +6 -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 +62 -0
- package/dist/testing.js +101 -0
- package/dist/tools/ad-hoc-router.d.ts +9 -9
- package/dist/tools/ad-hoc-router.js +65 -52
- 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 +9 -5
- package/dist/tools/capability-data.js +2 -1
- 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 +19 -10
- package/dist/tools/execute-analysis.js +83 -62
- package/dist/tools/knowledge/check.d.ts +166 -44
- package/dist/tools/knowledge/check.js +5 -3
- package/dist/tools/knowledge/client.d.ts +31 -62
- package/dist/tools/knowledge/client.js +44 -27
- package/dist/tools/knowledge/environment.d.ts +6 -6
- package/dist/tools/knowledge/environment.js +18 -7
- package/dist/tools/knowledge/index.d.ts +2 -2
- package/dist/tools/knowledge/index.js +9 -9
- package/dist/tools/knowledge/local-slots.d.ts +4 -4
- package/dist/tools/knowledge/local-slots.js +11 -4
- package/dist/tools/knowledge/pin.d.ts +2 -2
- package/dist/tools/knowledge/recommend.d.ts +20 -45
- package/dist/tools/knowledge/recommend.js +27 -19
- package/dist/tools/knowledge/situation.d.ts +40 -40
- package/dist/tools/knowledge/situation.js +2 -9
- package/dist/tools/knowledge/skeleton.d.ts +4 -4
- package/dist/tools/knowledge/skeleton.js +10 -11
- package/dist/tools/knowledge/template.d.ts +20 -18
- package/dist/tools/knowledge/template.js +38 -32
- 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 +16 -16
- package/dist/tools/research/generate-plan.js +410 -374
- package/dist/tools/research/guarded-address.js +3 -2
- 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 +2 -2
- package/dist/tools/research/searxng-engine.js +6 -4
- package/dist/tools/research/web-fetch.d.ts +24 -21
- package/dist/tools/research/web-fetch.js +65 -74
- package/dist/tools/research/web-search.d.ts +26 -17
- package/dist/tools/research/web-search.js +26 -21
- 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 +17 -8
- package/dist/tools/sandbox/list-available-packages.js +31 -25
- package/dist/tools/sandbox/list-available-refs.d.ts +17 -14
- package/dist/tools/sandbox/list-available-refs.js +61 -36
- package/dist/tools/sandbox/report-blocker.d.ts +30 -37
- package/dist/tools/sandbox/report-blocker.js +23 -73
- 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 +5 -2
- package/dist/tools/terminal-text.js +35 -3
- package/dist/tools/workspace/decision-record.d.ts +4 -4
- package/dist/tools/workspace/decision-record.js +23 -13
- package/dist/tools/workspace/edit-file.d.ts +59 -20
- package/dist/tools/workspace/edit-file.js +81 -16
- package/dist/tools/workspace/execute-command.d.ts +15 -13
- package/dist/tools/workspace/execute-command.js +11 -9
- 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/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 +46 -62
- package/dist/workflows/execute-analysis.js +137 -219
- package/dist/workflows/pause.d.ts +71 -0
- package/dist/workflows/pause.js +173 -0
- package/dist/workflows/sandbox-step.d.ts +38 -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 +21 -22
- package/dist/loop/call-guard.d.ts +0 -50
- package/dist/loop/call-guard.js +0 -90
- 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 -245
- package/dist/loop/run-agent.js +0 -1124
- 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 -93
- 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,33 +10,37 @@
|
|
|
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
|
*/
|
|
26
|
-
import {
|
|
25
|
+
import { errorMessageOf } from "../../lib/thrown.js";
|
|
26
|
+
import { textOf } from "../../lib/unknown-text.js";
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
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";
|
|
39
|
-
import {
|
|
42
|
+
import { blockerOf, createReportBlockerToolFor } from "../sandbox/report-blocker.js";
|
|
43
|
+
import { terminalTextSchema } from "../terminal-text.js";
|
|
40
44
|
import { searchGeoDatasetsTool } from "../bio/search-geo-datasets.js";
|
|
41
45
|
import { createNcbiTools } from "../bio/keys.js";
|
|
42
46
|
import { createKnowledgeTools } from "../knowledge/index.js";
|
|
@@ -46,7 +50,6 @@ import { createSearchGithubReposTool } from "./search-github-repos.js";
|
|
|
46
50
|
import { createSearchSemanticScholarTool } from "./search-semantic-scholar.js";
|
|
47
51
|
import { DATA_PROFILE_ORIENTATION_MAX_CHARS, buildDataProfileOrientation } from "../../app/data-profile-orientation.js";
|
|
48
52
|
import { DEFAULT_SANDBOX_MAX_STEPS } from "../../config/resource-limits.js";
|
|
49
|
-
import { guardRepeatedCalls } from "../../loop/call-guard.js";
|
|
50
53
|
import { plannerPrompt } from "../../prompts/planner.js";
|
|
51
54
|
import { hydratePlanSteps, PlannerPlanSchema } from "../../schemas/plan-schemas.js";
|
|
52
55
|
import { validatePlan } from "../../schemas/validate-plan.js";
|
|
@@ -54,7 +57,7 @@ import { AnalysisPlanSchema } from "../../schemas/workflow-state.js";
|
|
|
54
57
|
import { createNoopLogger } from "../../lib/console-logger.js";
|
|
55
58
|
import { unwrapOrThrow } from "../../lib/result.js";
|
|
56
59
|
import { decodeObjectString, hintForZodIssue } from "../../lib/zod-issues.js";
|
|
57
|
-
import {
|
|
60
|
+
import { loadDataProfileStatus, loadPlan, upsertPlan } from "../../state/index.js";
|
|
58
61
|
// ── Tool-level config ──────────────────────────────────────────────
|
|
59
62
|
/** Sub-agent identity for the planner — provenance only. */
|
|
60
63
|
const PLANNER_AGENT_ID = "planner";
|
|
@@ -65,9 +68,9 @@ const PLANNER_AGENT_ID = "planner";
|
|
|
65
68
|
* The number is deliberately far above what a plan costs. A well-grounded plan
|
|
66
69
|
* still costs two or three calls, because the prompt gates the research phase on
|
|
67
70
|
* what the seed does not already answer. What the ceiling must never do is stop
|
|
68
|
-
* a planner that is searching correctly. A capped run
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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.
|
|
71
74
|
*/
|
|
72
75
|
const PLANNER_MAX_ITERATIONS = 300;
|
|
73
76
|
/** Wall-clock guard for a single plan-generation invocation. */
|
|
@@ -75,15 +78,15 @@ const PLAN_TIMEOUT_MS = 1_800_000;
|
|
|
75
78
|
/**
|
|
76
79
|
* The refusals of the call guard that end the search phase of one plan. A
|
|
77
80
|
* planner that the guard refused this many times is not searching, it is
|
|
78
|
-
* looping, and the
|
|
81
|
+
* looping, and the salvage request lets it submit the plan it has. Six
|
|
79
82
|
* is above any count a frontier planner reached in the Phase 0 campaign
|
|
80
83
|
* (zero) and far below the hundred-plus refused calls of a looping run.
|
|
81
84
|
*/
|
|
82
85
|
const PLANNER_REFUSAL_LIMIT = 6;
|
|
83
86
|
// ── Diagnostic bounds ───────────────────────────────────────────────
|
|
84
87
|
//
|
|
85
|
-
// This tool
|
|
86
|
-
//
|
|
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
|
|
87
90
|
// survives the turn. That makes them worth spending detail on — and makes every
|
|
88
91
|
// one of them a place an unbounded model-authored string could reach a log file.
|
|
89
92
|
// Each bound below is what keeps a record readable and a sink affordable when the
|
|
@@ -125,7 +128,12 @@ function bounded(value, max) {
|
|
|
125
128
|
const MODEL_AUTHORED_KEY = "modelAuthored";
|
|
126
129
|
/** Nest model-authored prose under {@link MODEL_AUTHORED_KEY}, or contribute nothing when there is none. */
|
|
127
130
|
function modelAuthored(fields) {
|
|
128
|
-
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
|
+
}, []);
|
|
129
137
|
return present.length === 0 ? {} : { [MODEL_AUTHORED_KEY]: Object.fromEntries(present) };
|
|
130
138
|
}
|
|
131
139
|
function toRejectionRecord(attempt, issues) {
|
|
@@ -141,8 +149,24 @@ function toRejectionRecord(attempt, issues) {
|
|
|
141
149
|
messages: kept.map((i) => bounded(i.message, MAX_LOGGED_ISSUE_CHARS)),
|
|
142
150
|
};
|
|
143
151
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
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 };
|
|
146
170
|
}
|
|
147
171
|
// ── Prompt / catalog ────────────────────────────────────────────────
|
|
148
172
|
/** The planner system prompt is deterministic per policy — build it once per
|
|
@@ -150,6 +174,53 @@ function createPlannerTrace() {
|
|
|
150
174
|
function plannerInstructions(resourcePolicy) {
|
|
151
175
|
return plannerPrompt(formatAgentCatalog(), resourcePolicy);
|
|
152
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
|
+
}
|
|
153
224
|
// ── Prior plan serialization (iteration context) ───────────────────
|
|
154
225
|
/**
|
|
155
226
|
* Format a loaded prior plan as a markdown block the planner can read —
|
|
@@ -266,6 +337,10 @@ function renderDataContext(grounding, analysisId) {
|
|
|
266
337
|
}
|
|
267
338
|
}
|
|
268
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
|
+
}
|
|
269
344
|
/**
|
|
270
345
|
* Persist a validated plan. Returns the planId, or an outcome-ready error
|
|
271
346
|
* with a sanitized message — DB-shape errors never leak to the planner.
|
|
@@ -277,7 +352,9 @@ function renderDataContext(grounding, analysisId) {
|
|
|
277
352
|
*/
|
|
278
353
|
async function persistPlan(plan, ctx, pool, logger) {
|
|
279
354
|
try {
|
|
280
|
-
const planId =
|
|
355
|
+
const planId = planIdFor(ctx);
|
|
356
|
+
unwrapOrThrow(await upsertPlan(pool, {
|
|
357
|
+
planId,
|
|
281
358
|
analysisId: ctx.analysisId,
|
|
282
359
|
plan: hydratePlanSteps(plan),
|
|
283
360
|
parentPlanId: ctx.parentPlanId,
|
|
@@ -285,8 +362,9 @@ async function persistPlan(plan, ctx, pool, logger) {
|
|
|
285
362
|
return { ok: true, planId };
|
|
286
363
|
}
|
|
287
364
|
catch (err) {
|
|
288
|
-
const raw = err
|
|
289
|
-
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"));
|
|
290
368
|
logger.error("plan persistence failed", {
|
|
291
369
|
parentPlanId: ctx.parentPlanId,
|
|
292
370
|
stepCount: plan.steps.length,
|
|
@@ -379,7 +457,9 @@ function claimsByRule(claims) {
|
|
|
379
457
|
*/
|
|
380
458
|
function restoreSkeletonGrounding(candidate, memory) {
|
|
381
459
|
const decoded = decodeObjectString(candidate);
|
|
382
|
-
|
|
460
|
+
const decodedRecord = parseUnknownRecord(decoded);
|
|
461
|
+
const decodedSteps = decodedRecord?.steps;
|
|
462
|
+
if (memory.steps.size === 0 || !isUnknownArray(decodedSteps)) {
|
|
383
463
|
return { candidate: decoded, stamped: [], restored: [], repaired: [], unknown: [] };
|
|
384
464
|
}
|
|
385
465
|
const byRule = claimsByRule(memory.claims);
|
|
@@ -387,13 +467,14 @@ function restoreSkeletonGrounding(candidate, memory) {
|
|
|
387
467
|
const restored = [];
|
|
388
468
|
const repaired = [];
|
|
389
469
|
const unknown = [];
|
|
390
|
-
const steps =
|
|
391
|
-
|
|
470
|
+
const steps = decodedSteps.map((step, index) => {
|
|
471
|
+
const fields = parseUnknownRecord(step);
|
|
472
|
+
if (fields === undefined)
|
|
392
473
|
return step;
|
|
393
|
-
const { id, grounding } =
|
|
394
|
-
|
|
474
|
+
const { id, grounding } = fields;
|
|
475
|
+
const current = parseUnknownRecord(grounding);
|
|
476
|
+
if (current === undefined)
|
|
395
477
|
return step;
|
|
396
|
-
const current = grounding;
|
|
397
478
|
const label = typeof id === "string" ? id : String(index);
|
|
398
479
|
let next = current;
|
|
399
480
|
const source = typeof id === "string" ? memory.steps.get(id) : undefined;
|
|
@@ -407,8 +488,8 @@ function restoreSkeletonGrounding(candidate, memory) {
|
|
|
407
488
|
next = { ...next, template: source.grounding.template };
|
|
408
489
|
}
|
|
409
490
|
}
|
|
410
|
-
if (
|
|
411
|
-
let changed
|
|
491
|
+
if (isUnknownArray(current.claims)) {
|
|
492
|
+
let changed;
|
|
412
493
|
const claims = current.claims.map((claim, position) => {
|
|
413
494
|
if (typeof claim !== "string" || memory.claims.has(claim))
|
|
414
495
|
return claim;
|
|
@@ -434,9 +515,9 @@ function restoreSkeletonGrounding(candidate, memory) {
|
|
|
434
515
|
stamped.push(label);
|
|
435
516
|
next = { ...next, snapshot: memory.digest };
|
|
436
517
|
}
|
|
437
|
-
return next === current ? step : { ...
|
|
518
|
+
return next === current ? step : { ...fields, grounding: next };
|
|
438
519
|
});
|
|
439
|
-
return { candidate: { ...
|
|
520
|
+
return { candidate: { ...decodedRecord, steps }, stamped, restored, repaired, unknown };
|
|
440
521
|
}
|
|
441
522
|
/**
|
|
442
523
|
* Full validation: Zod schema + semantic checks. The plan is valid only if
|
|
@@ -479,19 +560,18 @@ function fullyValidate(candidate, resourcePolicy) {
|
|
|
479
560
|
return { valid: true, plan: parsed.data };
|
|
480
561
|
}
|
|
481
562
|
/**
|
|
482
|
-
* Build the planner's inner tools for one invocation.
|
|
483
|
-
*
|
|
484
|
-
* 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.
|
|
485
565
|
*/
|
|
486
|
-
function buildInnerTools(
|
|
566
|
+
function buildInnerTools(resourcePolicy, logger, memory) {
|
|
487
567
|
const submitPlanTool = defineTool({
|
|
488
|
-
id:
|
|
489
|
-
description: "Submit the
|
|
490
|
-
"
|
|
491
|
-
"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 " +
|
|
492
571
|
"rejection returns {accepted: false, issues} — fix and call again, " +
|
|
493
|
-
"or switch to report_blocker if the plan cannot be made valid.
|
|
494
|
-
"
|
|
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.",
|
|
495
575
|
// Permissive on purpose: every candidate reaches `execute`, where the
|
|
496
576
|
// model gets structured schema and semantic issues in one response.
|
|
497
577
|
// The field-by-field contract remains visible in this tool's description
|
|
@@ -503,25 +583,8 @@ function buildInnerTools(holder, trace, persistCtx, pool, resourcePolicy, logger
|
|
|
503
583
|
plan: z.union([PlannerPlanSchema, z.unknown()]).describe("Candidate plan; malformed values are reported in issues."),
|
|
504
584
|
}),
|
|
505
585
|
describeCall: "none",
|
|
506
|
-
execute:
|
|
507
|
-
|
|
508
|
-
trace.duplicateTerminalCalls++;
|
|
509
|
-
logger.warn("submit_plan called after a terminal outcome was recorded", {
|
|
510
|
-
recordedOutcome: holder.outcome.kind,
|
|
511
|
-
duplicateTerminalCalls: trace.duplicateTerminalCalls,
|
|
512
|
-
});
|
|
513
|
-
return ok({
|
|
514
|
-
accepted: false,
|
|
515
|
-
issues: [
|
|
516
|
-
{
|
|
517
|
-
path: "plan",
|
|
518
|
-
code: "semantic",
|
|
519
|
-
message: "A terminal outcome has already been recorded; submit_plan " + "can only be called once per invocation.",
|
|
520
|
-
},
|
|
521
|
-
],
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
const attempt = ++trace.submitAttempts;
|
|
586
|
+
execute: (input) => {
|
|
587
|
+
// The host owns the grounding fields: it fills them from the last answer of the run.
|
|
525
588
|
const { candidate, stamped, restored, repaired, unknown } = restoreSkeletonGrounding(input.plan, memory);
|
|
526
589
|
if (stamped.length > 0)
|
|
527
590
|
logger.info("submit_plan stamped the snapshot digest of the answer on steps", { steps: stamped });
|
|
@@ -534,107 +597,37 @@ function buildInnerTools(holder, trace, persistCtx, pool, resourcePolicy, logger
|
|
|
534
597
|
// schema issues when the schema rejects it, thus one attempt shows both.
|
|
535
598
|
if (!result.valid || unknown.length > 0) {
|
|
536
599
|
const issues = [...(result.valid ? [] : result.issues), ...unknown];
|
|
537
|
-
trace.rejectedAttempts++;
|
|
538
|
-
const rejection = toRejectionRecord(attempt, issues);
|
|
539
|
-
if (trace.rejections.length < MAX_LOGGED_REJECTIONS)
|
|
540
|
-
trace.rejections.push(rejection);
|
|
541
600
|
// `warn`, not `debug`: a rejection is non-terminal by design, so the model
|
|
542
601
|
// reads the issues and the invocation carries on — nothing else reports that
|
|
543
|
-
// an attempt was spent.
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
return ok({ accepted: false, issues });
|
|
547
|
-
}
|
|
548
|
-
const persisted = await persistPlan(result.plan, persistCtx, pool, logger);
|
|
549
|
-
if (!persisted.ok) {
|
|
550
|
-
holder.outcome = { kind: "persist_error", message: persisted.message };
|
|
551
|
-
return ok({
|
|
552
|
-
accepted: false,
|
|
553
|
-
issues: [
|
|
554
|
-
{
|
|
555
|
-
path: "plan",
|
|
556
|
-
code: "semantic",
|
|
557
|
-
message: persisted.message,
|
|
558
|
-
hint: "Persistence failed — do not retry submit_plan.",
|
|
559
|
-
},
|
|
560
|
-
],
|
|
561
|
-
});
|
|
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 }));
|
|
562
605
|
}
|
|
563
|
-
holder.outcome = {
|
|
564
|
-
kind: "plan_submitted",
|
|
565
|
-
planId: persisted.planId,
|
|
566
|
-
plan: result.plan,
|
|
567
|
-
};
|
|
568
|
-
// The attempt count is the cost of this plan, and it is only knowable here.
|
|
569
|
-
// A plan accepted on attempt 5 is a success that nearly was not one.
|
|
570
606
|
logger.info("submit_plan accepted a plan", {
|
|
571
|
-
planId: persisted.planId,
|
|
572
|
-
attempt,
|
|
573
607
|
stepCount: result.plan.steps.length,
|
|
574
608
|
agents: [...new Set(result.plan.steps.map((s) => s.agent))],
|
|
575
609
|
});
|
|
576
|
-
return ok({ accepted: true,
|
|
610
|
+
return Promise.resolve(ok({ accepted: true, plan: result.plan }));
|
|
577
611
|
},
|
|
578
612
|
});
|
|
579
613
|
const requestClarificationTool = defineTool({
|
|
580
|
-
id:
|
|
614
|
+
id: REQUEST_CLARIFICATION_TOOL_ID,
|
|
581
615
|
description: "Terminal. Use when a specific fact you need is missing from the " +
|
|
582
616
|
"input and cannot be inferred. The question reaches the user through " +
|
|
583
|
-
"the conversation agent
|
|
584
|
-
"plan saved.",
|
|
617
|
+
"the conversation agent, and no plan is saved. Stop after this call.",
|
|
585
618
|
inputSchema: z.object({
|
|
586
|
-
question:
|
|
619
|
+
question: terminalTextSchema("One short question that names each missing fact, and the step that needs it."),
|
|
587
620
|
questionContext: z.string().optional().describe("Optional context: why the plan needs the fact, and what the inputs already say about it."),
|
|
588
621
|
}),
|
|
589
622
|
describeCall: "none",
|
|
590
|
-
execute:
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (holder.outcome !== null) {
|
|
597
|
-
trace.duplicateTerminalCalls++;
|
|
598
|
-
logger.warn("request_clarification called after a terminal outcome was recorded", {
|
|
599
|
-
recordedOutcome: holder.outcome.kind,
|
|
600
|
-
duplicateTerminalCalls: trace.duplicateTerminalCalls,
|
|
601
|
-
});
|
|
602
|
-
return ok({ recorded: true });
|
|
603
|
-
}
|
|
604
|
-
holder.outcome = {
|
|
605
|
-
kind: "clarification",
|
|
606
|
-
question: input.question,
|
|
607
|
-
questionContext: input.questionContext,
|
|
608
|
-
};
|
|
609
|
-
// The question is the answer to "why did planning stop?" — and it reaches the
|
|
610
|
-
// user through the conversation agent, which may paraphrase it into something
|
|
611
|
-
// that no longer identifies the missing fact.
|
|
612
|
-
logger.info("planner requested clarification", {
|
|
613
|
-
submitAttempts: trace.submitAttempts,
|
|
614
|
-
...modelAuthored({ clarificationQuestion: bounded(input.question, MAX_LOGGED_PROSE_CHARS) }),
|
|
615
|
-
});
|
|
616
|
-
return ok({ recorded: true });
|
|
617
|
-
},
|
|
623
|
+
execute: (input) => Promise.resolve(ok({
|
|
624
|
+
kind: "clarification",
|
|
625
|
+
question: input.question,
|
|
626
|
+
questionContext: input.questionContext,
|
|
627
|
+
message: "Clarification recorded. Stop now.",
|
|
628
|
+
})),
|
|
618
629
|
});
|
|
619
630
|
const reportBlockerTool = createReportBlockerToolFor({
|
|
620
|
-
record: (outcome) => {
|
|
621
|
-
if (holder.outcome !== null) {
|
|
622
|
-
trace.duplicateTerminalCalls++;
|
|
623
|
-
logger.warn("report_blocker called after a terminal outcome was recorded", {
|
|
624
|
-
recordedOutcome: holder.outcome.kind,
|
|
625
|
-
duplicateTerminalCalls: trace.duplicateTerminalCalls,
|
|
626
|
-
});
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
holder.outcome = outcome;
|
|
630
|
-
// A blocker after several rejected submits is a planner giving up on a plan it
|
|
631
|
-
// could not make valid — a validation problem wearing a blocker's clothes. The
|
|
632
|
-
// attempt count is what tells those apart, and only this record carries both.
|
|
633
|
-
logger.warn("planner reported a blocker", {
|
|
634
|
-
submitAttempts: trace.submitAttempts,
|
|
635
|
-
...modelAuthored({ blockerReason: bounded(outcome.reason, MAX_LOGGED_PROSE_CHARS) }),
|
|
636
|
-
});
|
|
637
|
-
},
|
|
638
631
|
blockedWhen: "Ends plan generation with no plan saved. Use it when no valid plan can " +
|
|
639
632
|
"be produced for this data and research question (out of scope, data " +
|
|
640
633
|
"incompatible with every available agent, etc.) — not for a plan you " +
|
|
@@ -657,17 +650,18 @@ function inventoryContent(label, result, logger) {
|
|
|
657
650
|
return `## ${label}\n\nInventory lookup failed: ${result.error.error}`;
|
|
658
651
|
}
|
|
659
652
|
const value = result.value;
|
|
660
|
-
|
|
661
|
-
|
|
653
|
+
const content = parseUnknownRecord(value)?.["content"];
|
|
654
|
+
if (typeof content === "string") {
|
|
655
|
+
return `## ${label}\n\n${content}`;
|
|
662
656
|
}
|
|
663
657
|
return `## ${label}\n\n${JSON.stringify(value)}`;
|
|
664
658
|
}
|
|
665
659
|
// ── Transcript reading (diagnostics only) ───────────────────────────
|
|
666
660
|
/**
|
|
667
|
-
* 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
|
|
668
662
|
* returns. Nothing else can produce it: the transcript is ephemeral — a sub-agent
|
|
669
|
-
* loop
|
|
670
|
-
*
|
|
663
|
+
* loop persists no messages anywhere — so it is read here or it is lost with the
|
|
664
|
+
* turn.
|
|
671
665
|
*
|
|
672
666
|
* `toolCalls` in order is what shows the SHAPE of a failure at a glance:
|
|
673
667
|
* `[submit_plan × 13]` is a planner thrashing on validation, `[]` is a planner
|
|
@@ -702,32 +696,34 @@ function readTranscript(messages) {
|
|
|
702
696
|
* The loop half of the finish record: how the planner's run ended, and what it
|
|
703
697
|
* did to get there.
|
|
704
698
|
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
* (`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.
|
|
710
703
|
*/
|
|
711
704
|
function loopFields(run, kind) {
|
|
712
705
|
const transcript = readTranscript(run.messages);
|
|
713
|
-
|
|
706
|
+
const fields = {
|
|
714
707
|
loop: {
|
|
715
708
|
finishReason: run.finish.reason,
|
|
716
709
|
cappedOut: run.finish.cappedOut,
|
|
717
|
-
truncationRecoveries: run.finish.truncationRecoveries,
|
|
718
710
|
salvaged: run.salvage !== null,
|
|
719
|
-
|
|
711
|
+
firstFinishReason: run.salvage === null ? undefined : run.salvage.firstFinish.reason,
|
|
712
|
+
salvageFinishReason: run.salvage === null ? undefined : run.salvage.finish.reason,
|
|
720
713
|
toolCalls: transcript.toolCalls,
|
|
721
|
-
|
|
714
|
+
toolCallsTruncated: transcript.truncatedToolCalls ? true : undefined,
|
|
722
715
|
},
|
|
723
|
-
...(PROSE_EXPLAINS_OUTCOME.has(kind) ? modelAuthored({ plannerFinalProse: transcript.finalProse }) : {}),
|
|
724
716
|
};
|
|
717
|
+
if (PROSE_EXPLAINS_OUTCOME.has(kind))
|
|
718
|
+
Object.assign(fields, modelAuthored({ plannerFinalProse: transcript.finalProse }));
|
|
719
|
+
return fields;
|
|
725
720
|
}
|
|
726
721
|
/**
|
|
727
722
|
* The endings whose only account is what the planner said last.
|
|
728
723
|
*
|
|
729
|
-
* Everywhere else something better already explains the outcome:
|
|
730
|
-
*
|
|
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
|
|
731
727
|
* prose there would put model output describing the user's data into a record
|
|
732
728
|
* that had no use for it — including on every successful plan, where the closing
|
|
733
729
|
* text is the planner narrating work that demonstrably went fine.
|
|
@@ -743,8 +739,7 @@ const PROSE_EXPLAINS_OUTCOME = new Set(["no_outcome", "loop_error", "timeout"]);
|
|
|
743
739
|
* caller actually returned.
|
|
744
740
|
*/
|
|
745
741
|
function shapeOutcome(args) {
|
|
746
|
-
const {
|
|
747
|
-
const outcome = holder.outcome;
|
|
742
|
+
const { outcome, runError, timedOut, outerAborted } = args;
|
|
748
743
|
if (outcome?.kind === "plan_submitted") {
|
|
749
744
|
return { output: { event: "plan_complete", planId: outcome.planId, plan: outcome.plan }, kind: "plan_submitted" };
|
|
750
745
|
}
|
|
@@ -753,7 +748,7 @@ function shapeOutcome(args) {
|
|
|
753
748
|
output: {
|
|
754
749
|
event: "clarification_needed",
|
|
755
750
|
question: outcome.question,
|
|
756
|
-
|
|
751
|
+
questionContext: outcome.questionContext ? outcome.questionContext : undefined,
|
|
757
752
|
},
|
|
758
753
|
kind: "clarification",
|
|
759
754
|
};
|
|
@@ -778,7 +773,7 @@ function shapeOutcome(args) {
|
|
|
778
773
|
return { output: { event: "error", error: "Plan generation was cancelled." }, kind: "cancelled" };
|
|
779
774
|
}
|
|
780
775
|
if (runError) {
|
|
781
|
-
const msg = runError
|
|
776
|
+
const msg = errorMessageOf(runError) ?? textOf(runError);
|
|
782
777
|
return { output: { event: "error", error: `Plan generation failed: ${msg}` }, kind: "loop_error" };
|
|
783
778
|
}
|
|
784
779
|
return {
|
|
@@ -831,7 +826,7 @@ export function buildPlannerSearchTools(deps, hooks = {}) {
|
|
|
831
826
|
// cross-field index that ranks a method paper best.
|
|
832
827
|
ncbi.pubmed,
|
|
833
828
|
searchArxivTool,
|
|
834
|
-
createSearchSemanticScholarTool({
|
|
829
|
+
createSearchSemanticScholarTool({ apiKey: bioKeys.semanticScholar }),
|
|
835
830
|
// The same approach as running code — a pipeline that someone published.
|
|
836
831
|
createSearchGithubReposTool({ githubToken: bioKeys.github }),
|
|
837
832
|
// The API of a staged package, so a step names a function that exists.
|
|
@@ -845,92 +840,78 @@ export function buildPlannerSearchTools(deps, hooks = {}) {
|
|
|
845
840
|
// The planner is a conversation surface, thus the pool-scope reader binds
|
|
846
841
|
// when the embedder gives one, and the answer names what the store HOLDS.
|
|
847
842
|
createListAvailablePackagesTool({
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
843
|
+
farmLockFile: deps.farmLockFile,
|
|
844
|
+
imagePackagesFile: deps.imagePackagesFile,
|
|
845
|
+
readPoolInventory: deps.readPoolInventory,
|
|
851
846
|
}),
|
|
852
847
|
// The knowledge plane: one cited procedure per situation, and one check
|
|
853
848
|
// of the draft. Both attach only when the embedder binds a client.
|
|
854
849
|
...createKnowledgeTools({
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
850
|
+
client: deps.knowledge,
|
|
851
|
+
farmLockFile: deps.farmLockFile,
|
|
852
|
+
imagePackagesFile: deps.imagePackagesFile,
|
|
853
|
+
refStorePath: deps.refStorePath,
|
|
854
|
+
onRecommend: hooks.onRecommend,
|
|
860
855
|
}),
|
|
861
856
|
];
|
|
862
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
|
+
});
|
|
863
884
|
/** Build the `generate_plan` tool bound to its provider and pool. */
|
|
864
|
-
export function createGeneratePlanTool(deps) {
|
|
885
|
+
export function createGeneratePlanTool(deps, registerSubAgent) {
|
|
865
886
|
const baseLogger = (deps.logger ?? createNoopLogger()).named("generate-plan");
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
.
|
|
884
|
-
.
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
.describe("Summary of prior run results if any exist: which steps ran, " + "what succeeded/failed, key findings. Omit if no prior runs."),
|
|
895
|
-
userConstraints: z
|
|
896
|
-
.string()
|
|
897
|
-
.optional()
|
|
898
|
-
.describe("User-specified constraints: preferred methods, steps to " + "include/exclude, resource limits, modifications to a prior plan."),
|
|
899
|
-
parentPlanId: z
|
|
900
|
-
.string()
|
|
901
|
-
.regex(/^pln-[a-f0-9]{8}$/, "parentPlanId must be a pln-<8hex> value")
|
|
902
|
-
.optional()
|
|
903
|
-
.describe("The planId of the prior plan being iterated on. Set only when " + "the user is asking for modifications to an existing plan."),
|
|
904
|
-
}),
|
|
905
|
-
describeCall: ({ researchQuestion }) => researchQuestion,
|
|
906
|
-
execute: async (input, ctx) => {
|
|
907
|
-
const analysisId = scopeResource(ctx.session.scope).resourceId;
|
|
908
|
-
// Bound per invocation: `analysisId` comes from the call-time session, not from
|
|
909
|
-
// the construction-time deps the factory closes over.
|
|
910
|
-
const logger = baseLogger.with({ analysisId });
|
|
911
|
-
const startedAt = Date.now();
|
|
912
|
-
const trace = createPlannerTrace();
|
|
913
|
-
// Every exit routes through here, so "recorded exactly once per invocation" holds
|
|
914
|
-
// by construction rather than by remembering. `elapsedMs` is what separates a
|
|
915
|
-
// planner that gave up early from one still working when the guard cut it — the
|
|
916
|
-
// fixed budget above makes the number readable without any other context.
|
|
917
|
-
//
|
|
918
|
-
// `extra` carries the loop account on the paths that reached the loop. The two
|
|
919
|
-
// parent-plan early returns never do, and a record claiming `loop.finishReason`
|
|
920
|
-
// for a run that never started would be worse than one that omits the field.
|
|
921
|
-
const finish = (shaped, extra = {}) => {
|
|
922
|
-
logger[OUTCOME_LEVEL[shaped.kind]]("plan generation finished", {
|
|
923
|
-
outcome: shaped.kind,
|
|
924
|
-
elapsedMs: Date.now() - startedAt,
|
|
925
|
-
submitAttempts: trace.submitAttempts,
|
|
926
|
-
rejectedAttempts: trace.rejectedAttempts,
|
|
927
|
-
...(trace.rejections.length > 0 ? { rejections: trace.rejections } : {}),
|
|
928
|
-
...(trace.rejectedAttempts > trace.rejections.length ? { rejectionsTruncated: true } : {}),
|
|
929
|
-
...(trace.duplicateTerminalCalls > 0 ? { duplicateTerminalCalls: trace.duplicateTerminalCalls } : {}),
|
|
930
|
-
...extra,
|
|
931
|
-
});
|
|
932
|
-
return ok(shaped.output);
|
|
933
|
-
};
|
|
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 () => {
|
|
934
915
|
// If iterating, load the parent plan so the planner sees what it is
|
|
935
916
|
// revising. Fails fast with the sanitized message submit_plan would
|
|
936
917
|
// have surfaced — saves a wasted planner run.
|
|
@@ -941,10 +922,12 @@ export function createGeneratePlanTool(deps) {
|
|
|
941
922
|
analysisId,
|
|
942
923
|
}));
|
|
943
924
|
if (!priorPlan) {
|
|
944
|
-
return
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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
|
+
};
|
|
948
931
|
}
|
|
949
932
|
priorPlanBlock = formatPriorPlan(input.parentPlanId, priorPlan);
|
|
950
933
|
// `formatPriorPlan` returns null on a stored plan that no longer parses as an
|
|
@@ -956,10 +939,12 @@ export function createGeneratePlanTool(deps) {
|
|
|
956
939
|
}
|
|
957
940
|
catch (err) {
|
|
958
941
|
logger.error("parent plan could not be loaded", { parentPlanId: input.parentPlanId, ...logger.errorFields(err) });
|
|
959
|
-
return
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
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
|
+
};
|
|
963
948
|
}
|
|
964
949
|
}
|
|
965
950
|
// The dataset's facts come from the ledger, not from a string a model
|
|
@@ -974,11 +959,10 @@ export function createGeneratePlanTool(deps) {
|
|
|
974
959
|
// Degrading to "no profile" is correct behaviour and indistinguishable, from
|
|
975
960
|
// the outcome, from an analysis that genuinely has none — so the read failure
|
|
976
961
|
// is reported here or it is never known to have happened.
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
});
|
|
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);
|
|
982
966
|
return null;
|
|
983
967
|
});
|
|
984
968
|
const grounding = classifyGrounding(profileStatus);
|
|
@@ -988,9 +972,9 @@ export function createGeneratePlanTool(deps) {
|
|
|
988
972
|
// the loop and put their rendered content in the planner's seed.
|
|
989
973
|
const listAvailableRefs = createListAvailableRefsTool(deps.refStorePath === undefined ? {} : { refStorePath: deps.refStorePath });
|
|
990
974
|
const listAvailablePackages = createListAvailablePackagesTool({
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
975
|
+
farmLockFile: deps.farmLockFile,
|
|
976
|
+
imagePackagesFile: deps.imagePackagesFile,
|
|
977
|
+
readPoolInventory: deps.readPoolInventory,
|
|
994
978
|
});
|
|
995
979
|
// A path-separator query matches every reference path and selects the
|
|
996
980
|
// tool's recursive leaf scan, so the seed contains usable file paths,
|
|
@@ -1010,122 +994,174 @@ export function createGeneratePlanTool(deps) {
|
|
|
1010
994
|
...(input.priorRuns ? ["", "## Prior Run Results", input.priorRuns] : []),
|
|
1011
995
|
...(input.userConstraints ? ["", "## User Constraints", input.userConstraints] : []),
|
|
1012
996
|
].join("\n");
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
// first-order suspects when a planner runs long and returns nothing: the size
|
|
1016
|
-
// of the seed it was handed, and which of its blocks made it that size. The
|
|
1017
|
-
// reference inventory in particular is a recursive leaf scan of whatever this
|
|
1018
|
-
// install has staged — a quantity the harness does not control and no other
|
|
1019
|
-
// record reports. The census is sizes only: the seed carries the user's
|
|
1020
|
-
// research question and dataset facts, which belong in the model's context and
|
|
1021
|
-
// not in a log file.
|
|
1022
|
-
logger.info("plan generation started", {
|
|
1023
|
-
model: deps.conversation.model,
|
|
1024
|
-
maxIterations: PLANNER_MAX_ITERATIONS,
|
|
1025
|
-
salvageIterations: DEFAULT_SALVAGE_ITERATIONS,
|
|
1026
|
-
timeoutMs: planTimeoutMs,
|
|
997
|
+
return {
|
|
998
|
+
prompt,
|
|
1027
999
|
grounding: grounding.kind,
|
|
1028
|
-
|
|
1029
|
-
seedChars: {
|
|
1030
|
-
total: prompt.length,
|
|
1000
|
+
chars: {
|
|
1031
1001
|
priorPlan: priorPlanBlock?.length ?? 0,
|
|
1032
1002
|
dataContext: dataContextBlock.length,
|
|
1033
1003
|
referenceData: refsBlock.length,
|
|
1034
1004
|
packages: packagesBlock.length,
|
|
1035
|
-
researchQuestion: input.researchQuestion.length,
|
|
1036
|
-
analystNotes: input.analystNotes?.length ?? 0,
|
|
1037
|
-
priorRuns: input.priorRuns?.length ?? 0,
|
|
1038
|
-
userConstraints: input.userConstraints?.length ?? 0,
|
|
1039
1005
|
},
|
|
1040
|
-
});
|
|
1041
|
-
const holder = { outcome: null };
|
|
1042
|
-
const persistCtx = {
|
|
1043
|
-
analysisId,
|
|
1044
|
-
parentPlanId: input.parentPlanId ?? null,
|
|
1045
1006
|
};
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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
|
+
},
|
|
1066
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,
|
|
1067
1105
|
});
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
const signal = AbortSignal.any([ctx.signal, AbortSignal.timeout(planTimeoutMs)]);
|
|
1082
|
-
let runError = null;
|
|
1083
|
-
let run = null;
|
|
1084
|
-
try {
|
|
1085
|
-
run = await runToTerminal(planner, [{ role: "user", content: prompt }], forSubAgent(ctx.session, PLANNER_AGENT_ID), {
|
|
1086
|
-
provider: deps.conversation.provider,
|
|
1087
|
-
signal,
|
|
1088
|
-
emit: ctx.emit,
|
|
1089
|
-
runStep: passthroughStep,
|
|
1090
|
-
resolved: () => holder.outcome !== null,
|
|
1091
|
-
// A planner the guard refused too many times is looping:
|
|
1092
|
-
// the early cap ends the search, and the salvage turn
|
|
1093
|
-
// submits the plan it has.
|
|
1094
|
-
stopWhen: () => refusals >= PLANNER_REFUSAL_LIMIT,
|
|
1095
|
-
// No forced tool choice: some Claude models reject it with a
|
|
1096
|
-
// 400. The prompt names the terminal tools, and the salvage
|
|
1097
|
-
// turn below recovers a run that ends on prose.
|
|
1098
|
-
logger,
|
|
1099
|
-
usageRecorder: deps.usageRecorder,
|
|
1100
|
-
toolOutputStore: deps.toolOutputStore,
|
|
1101
|
-
// Fold the planner's calls into the turn total the root loop reports.
|
|
1102
|
-
turnUsage: ctx.turnUsage,
|
|
1103
|
-
// Keeps the usage record keys of two parallel dispatches disjoint —
|
|
1104
|
-
// same frame, same call path, same loop-local step names.
|
|
1105
|
-
invocationId: ctx.invocationId,
|
|
1106
|
-
}, {
|
|
1107
|
-
tools: innerTools.terminal,
|
|
1108
|
-
nudge: "You ended without a terminal outcome. Call submit_plan with " +
|
|
1109
|
-
"your final plan now, or request_clarification / report_blocker " +
|
|
1110
|
-
"if you cannot. Do not reply with prose.",
|
|
1111
|
-
});
|
|
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 };
|
|
1112
1119
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1120
|
+
else {
|
|
1121
|
+
outcome = selected;
|
|
1115
1122
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
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)),
|
|
1130
1166
|
});
|
|
1131
1167
|
}
|