@inflexa-ai/harness 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/agents/conversation-agent.d.ts +15 -11
- package/dist/agents/conversation-agent.js +31 -34
- package/dist/agents/report-builder.js +2 -1
- package/dist/agents/sandbox/bulk-transcriptomics-agent.js +1 -1
- package/dist/agents/sandbox/cheminformatics-agent.js +1 -19
- package/dist/agents/sandbox/chromatin-agent.js +1 -10
- package/dist/agents/sandbox/dna-methylation-agent.js +1 -10
- package/dist/agents/sandbox/drug-repurposing-agent.js +4 -14
- package/dist/agents/sandbox/enrichment-agent.js +1 -10
- package/dist/agents/sandbox/ephemeral-executor.js +1 -10
- package/dist/agents/sandbox/genomic-variant-agent.js +1 -3
- package/dist/agents/sandbox/immune-profiling-agent.js +1 -13
- package/dist/agents/sandbox/metabolomics-agent.js +1 -1
- package/dist/agents/sandbox/microbiome-agent.js +1 -1
- package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -12
- package/dist/agents/sandbox/multimodal-sc-agent.js +1 -1
- package/dist/agents/sandbox/network-agent.js +1 -10
- package/dist/agents/sandbox/pkpd-clinical-response-agent.js +2 -8
- package/dist/agents/sandbox/proteomics-agent.js +1 -1
- package/dist/agents/sandbox/scientific-executor.js +1 -10
- package/dist/agents/sandbox/shared.d.ts +20 -6
- package/dist/agents/sandbox/shared.js +50 -39
- package/dist/agents/sandbox/single-cell-agent.js +1 -1
- package/dist/agents/sandbox/spatial-omics-agent.js +1 -1
- package/dist/agents/sandbox/statistical-modeling-agent.js +1 -1
- package/dist/agents/sandbox/translational-safety-agent.js +3 -9
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/system-prompt.d.ts +37 -11
- package/dist/agents/system-prompt.js +35 -12
- package/dist/app/chat-turn.d.ts +3 -0
- package/dist/app/chat-turn.js +3 -1
- package/dist/app/data-profile-orientation.d.ts +34 -0
- package/dist/app/data-profile-orientation.js +117 -0
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +3 -1
- package/dist/contracts/schemas/chat-parts.d.ts +9 -9
- package/dist/execution/artifact-metadata.d.ts +3 -0
- package/dist/execution/artifact-metadata.js +10 -3
- package/dist/execution/artifact-registration.d.ts +2 -1
- package/dist/execution/artifact-registration.js +8 -2
- package/dist/execution/ephemeral-runner.d.ts +16 -0
- package/dist/execution/ephemeral-runner.js +31 -5
- package/dist/execution/post-step-pipeline.d.ts +3 -0
- package/dist/execution/post-step-pipeline.js +24 -4
- package/dist/execution/reconcile-manifest.d.ts +7 -0
- package/dist/execution/reconcile-manifest.js +19 -5
- package/dist/execution/report-runner.d.ts +3 -0
- package/dist/execution/report-runner.js +4 -4
- package/dist/execution/run-synthesis.js +20 -19
- package/dist/execution/step-summary.d.ts +3 -0
- package/dist/execution/step-summary.js +6 -3
- package/dist/index.d.ts +9 -2
- package/dist/index.js +24 -5
- package/dist/lib/chrome.d.ts +4 -1
- package/dist/lib/chrome.js +10 -7
- package/dist/lib/console-logger.d.ts +19 -0
- package/dist/lib/console-logger.js +62 -0
- package/dist/lib/logger.d.ts +88 -0
- package/dist/lib/logger.js +37 -0
- package/dist/lib/otel.d.ts +2 -1
- package/dist/lib/otel.js +8 -3
- package/dist/loop/__fixtures__/scripted-provider.d.ts +2 -2
- package/dist/loop/__fixtures__/scripted-provider.js +2 -1
- package/dist/loop/metrics.d.ts +33 -1
- package/dist/loop/metrics.js +56 -2
- package/dist/loop/run-agent.d.ts +13 -1
- package/dist/loop/run-agent.js +19 -4
- package/dist/memory/card-builders.d.ts +8 -1
- package/dist/memory/card-builders.js +20 -3
- package/dist/memory/reconstruct-cards.js +4 -3
- package/dist/memory/thread-history.d.ts +27 -2
- package/dist/memory/thread-history.js +45 -5
- package/dist/memory/working-memory.d.ts +93 -10
- package/dist/memory/working-memory.js +220 -84
- package/dist/prompts/SOUL.d.ts +22 -9
- package/dist/prompts/SOUL.js +56 -37
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +2 -2
- package/dist/prompts/briefing.d.ts +100 -0
- package/dist/prompts/briefing.js +166 -0
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +94 -440
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +10 -10
- package/dist/prompts/planner.js +28 -59
- package/dist/prompts/report-builder.d.ts +1 -1
- package/dist/prompts/report-builder.js +71 -24
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +2 -7
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +11 -15
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +3 -7
- package/dist/prompts/sandbox/data-profiler.d.ts +1 -1
- package/dist/prompts/sandbox/data-profiler.js +13 -8
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +15 -16
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +2 -9
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +2 -6
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +3 -8
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +1 -7
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +2 -7
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +2 -8
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +2 -7
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +2 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +2 -9
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +2 -9
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -6
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +2 -7
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +13 -14
- package/dist/prompts/sandbox-standards.d.ts +22 -14
- package/dist/prompts/sandbox-standards.js +91 -71
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +33 -131
- package/dist/provenance/collector.js +1 -1
- package/dist/providers/ai-sdk.js +31 -4
- package/dist/providers/prompt-cache.d.ts +74 -0
- package/dist/providers/prompt-cache.js +77 -0
- package/dist/providers/types.d.ts +33 -0
- package/dist/reference-data/catalog.d.ts +131 -0
- package/dist/reference-data/catalog.js +348 -0
- package/dist/reference-data/receipt.d.ts +41 -0
- package/dist/reference-data/receipt.js +40 -0
- package/dist/runtime/boot.d.ts +2 -2
- package/dist/runtime/boot.js +1 -1
- package/dist/runtime/connection-budget.d.ts +2 -2
- package/dist/runtime/connection-budget.js +5 -3
- package/dist/runtime/dbos.d.ts +7 -7
- package/dist/runtime/dbos.js +14 -11
- package/dist/runtime/shutdown.d.ts +2 -2
- package/dist/runtime/shutdown.js +4 -3
- package/dist/sandbox/create-sandbox.d.ts +2 -2
- package/dist/sandbox/docker-client.d.ts +2 -2
- package/dist/sandbox/docker-client.js +4 -1
- package/dist/sandbox/k8s-client.d.ts +3 -0
- package/dist/sandbox/k8s-client.js +5 -4
- package/dist/sandbox/notification-sweep.d.ts +3 -3
- package/dist/sandbox/notification-sweep.js +4 -2
- package/dist/sandbox/reaper.d.ts +3 -3
- package/dist/sandbox/reaper.js +7 -4
- package/dist/sandbox/watchdog.d.ts +3 -3
- package/dist/sandbox/watchdog.js +8 -2
- package/dist/schemas/data-profile-schemas.js +5 -3
- package/dist/schemas/plan-schemas.d.ts +21 -21
- package/dist/schemas/plan-schemas.js +9 -3
- package/dist/schemas/run-synthesis.d.ts +8 -4
- package/dist/schemas/run-synthesis.js +92 -56
- package/dist/schemas/validate-plan.js +1 -1
- package/dist/schemas/workflow-state.d.ts +7 -1
- package/dist/schemas/workflow-state.js +33 -19
- package/dist/state/artifacts.d.ts +13 -0
- package/dist/state/artifacts.js +27 -0
- package/dist/state/data-profile.d.ts +64 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +1 -1
- package/dist/state/init.d.ts +2 -1
- package/dist/state/init.js +5 -6
- package/dist/tasks/data-profile.d.ts +25 -2
- package/dist/tasks/data-profile.js +95 -39
- package/dist/tools/bio/chembl.d.ts +38 -0
- package/dist/tools/bio/chembl.js +138 -0
- package/dist/tools/bio/comptox.d.ts +50 -0
- package/dist/tools/bio/comptox.js +149 -0
- package/dist/tools/bio/get-impc-ko-profile.js +9 -6
- package/dist/tools/bio/index.d.ts +5 -17
- package/dist/tools/bio/index.js +5 -17
- package/dist/tools/bio/keys.d.ts +14 -9
- package/dist/tools/bio/keys.js +16 -16
- package/dist/tools/bio/opentargets.d.ts +38 -0
- package/dist/tools/bio/opentargets.js +89 -0
- package/dist/tools/bio/pubchem.d.ts +29 -0
- package/dist/tools/bio/pubchem.js +92 -0
- package/dist/tools/bio/pubmed.d.ts +51 -0
- package/dist/tools/bio/pubmed.js +100 -0
- package/dist/tools/bio/search-bgee-expression.js +12 -6
- package/dist/tools/bio/search-clinical-trials.js +16 -5
- package/dist/tools/bio/search-dgidb.d.ts +11 -1
- package/dist/tools/bio/search-dgidb.js +92 -78
- package/dist/tools/bio/search-faers.js +8 -4
- package/dist/tools/bio/search-geo-datasets.js +20 -6
- package/dist/tools/bio/search-gwas-catalog.d.ts +1 -1
- package/dist/tools/bio/search-pharmgkb.js +10 -4
- package/dist/tools/display/index.d.ts +1 -0
- package/dist/tools/display/index.js +1 -0
- package/dist/tools/display/normalize-echart-spec.d.ts +61 -0
- package/dist/tools/display/normalize-echart-spec.js +211 -0
- package/dist/tools/display/show-user.d.ts +6 -1
- package/dist/tools/display/show-user.js +25 -12
- package/dist/tools/execute-plan.js +5 -2
- package/dist/tools/iterate-report.d.ts +171 -118
- package/dist/tools/iterate-report.js +336 -96
- package/dist/tools/lib/chembl-client.d.ts +12 -0
- package/dist/tools/lib/chembl-client.js +127 -0
- package/dist/tools/lib/ctx-ops.d.ts +224 -0
- package/dist/tools/lib/ctx-ops.js +450 -0
- package/dist/tools/lib/iuphar-client.js +10 -1
- package/dist/tools/lib/openfda-client.js +11 -3
- package/dist/tools/lib/pathway-client.js +10 -4
- package/dist/tools/lib/pubchem-ops.d.ts +74 -0
- package/dist/tools/lib/pubchem-ops.js +224 -0
- package/dist/tools/memory/update-working-memory.d.ts +45 -7
- package/dist/tools/memory/update-working-memory.js +111 -41
- package/dist/tools/report/index.d.ts +3 -2
- package/dist/tools/report/index.js +3 -2
- package/dist/tools/research/generate-analogy-report.js +25 -12
- package/dist/tools/research/generate-plan.d.ts +12 -5
- package/dist/tools/research/generate-plan.js +168 -37
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.d.ts +85 -0
- package/dist/tools/research/inspect-data-profile.js +161 -0
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +5 -6
- package/dist/tools/sandbox/list-available-packages.d.ts +79 -5
- package/dist/tools/sandbox/list-available-packages.js +152 -10
- package/dist/tools/sandbox/list-available-refs.d.ts +141 -5
- package/dist/tools/sandbox/list-available-refs.js +647 -134
- package/dist/tools/sandbox/report-blocker.d.ts +39 -9
- package/dist/tools/sandbox/report-blocker.js +43 -20
- package/dist/tools/workspace/execute-command.d.ts +3 -0
- package/dist/tools/workspace/execute-command.js +3 -1
- package/dist/tools/workspace/read-file.js +4 -5
- package/dist/tools/workspace/run-exec.d.ts +4 -4
- package/dist/tools/workspace/run-exec.js +4 -4
- package/dist/tools/workspace/show-file.js +10 -6
- package/dist/tools/workspace/show-plan.js +12 -6
- package/dist/tools/workspace/workspace-search.d.ts +1 -1
- package/dist/tools/workspace/workspace-search.js +11 -5
- package/dist/workflows/execute-analysis.d.ts +46 -4
- package/dist/workflows/execute-analysis.js +118 -13
- package/dist/workflows/execute-target-assessment.d.ts +3 -0
- package/dist/workflows/execute-target-assessment.js +19 -15
- package/dist/workflows/sandbox-step.d.ts +9 -0
- package/dist/workflows/sandbox-step.js +39 -17
- package/dist/workflows/target-assessment/lib/citation-validator.js +2 -2
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +2 -1
- package/dist/workflows/target-assessment/lib/llm-step.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/llm-step.js +3 -1
- package/dist/workflows/target-assessment/lib/structured-llm.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/structured-llm.js +1 -0
- package/dist/workflows/target-assessment/phase5-persist.d.ts +3 -0
- package/dist/workflows/target-assessment/phase5-persist.js +8 -3
- package/dist/workflows/target-assessment/progress.d.ts +2 -1
- package/dist/workflows/target-assessment/progress.js +4 -3
- package/dist/workspace/filesystem.js +163 -74
- package/dist/workspace/paths.d.ts +2 -2
- package/dist/workspace/paths.js +2 -2
- package/package.json +95 -96
- package/dist/execution/filesystem-artifact-registry.d.ts +0 -57
- package/dist/execution/filesystem-artifact-registry.js +0 -92
- package/dist/lib/identifiers.d.ts +0 -8
- package/dist/lib/identifiers.js +0 -10
- package/dist/lib/mime-detect.d.ts +0 -17
- package/dist/lib/mime-detect.js +0 -29
- package/dist/lib/streamed-download.d.ts +0 -28
- package/dist/lib/streamed-download.js +0 -111
- package/dist/lib/trace-context.d.ts +0 -11
- package/dist/lib/trace-context.js +0 -49
- package/dist/providers/llm-capabilities.d.ts +0 -33
- package/dist/providers/llm-capabilities.js +0 -58
- package/dist/schemas/render-step-prompt.d.ts +0 -32
- package/dist/schemas/render-step-prompt.js +0 -76
- package/dist/tools/bio/find-approval-precedent.d.ts +0 -32
- package/dist/tools/bio/find-approval-precedent.js +0 -72
- package/dist/tools/bio/get-article-details.d.ts +0 -11
- package/dist/tools/bio/get-article-details.js +0 -20
- package/dist/tools/bio/get-article-full-text.d.ts +0 -19
- package/dist/tools/bio/get-article-full-text.js +0 -32
- package/dist/tools/bio/get-bioactivity.d.ts +0 -11
- package/dist/tools/bio/get-bioactivity.js +0 -23
- package/dist/tools/bio/get-drug-info.d.ts +0 -9
- package/dist/tools/bio/get-drug-info.js +0 -22
- package/dist/tools/bio/get-mechanism.d.ts +0 -8
- package/dist/tools/bio/get-mechanism.js +0 -18
- package/dist/tools/bio/get-pubchem-assays.d.ts +0 -22
- package/dist/tools/bio/get-pubchem-assays.js +0 -107
- package/dist/tools/bio/get-pubchem-cross-refs.d.ts +0 -15
- package/dist/tools/bio/get-pubchem-cross-refs.js +0 -68
- package/dist/tools/bio/get-target-safety.d.ts +0 -18
- package/dist/tools/bio/get-target-safety.js +0 -26
- package/dist/tools/bio/search-compounds.d.ts +0 -22
- package/dist/tools/bio/search-compounds.js +0 -160
- package/dist/tools/bio/search-ctx-chemical.d.ts +0 -50
- package/dist/tools/bio/search-ctx-chemical.js +0 -130
- package/dist/tools/bio/search-ctx-exposure.d.ts +0 -55
- package/dist/tools/bio/search-ctx-exposure.js +0 -176
- package/dist/tools/bio/search-ctx-hazard.d.ts +0 -111
- package/dist/tools/bio/search-ctx-hazard.js +0 -178
- package/dist/tools/bio/search-opentargets.d.ts +0 -11
- package/dist/tools/bio/search-opentargets.js +0 -25
- package/dist/tools/bio/search-pubchem-compound.d.ts +0 -26
- package/dist/tools/bio/search-pubchem-compound.js +0 -110
- package/dist/tools/bio/search-pubmed.d.ts +0 -17
- package/dist/tools/bio/search-pubmed.js +0 -31
- package/dist/tools/bio/search-targets.d.ts +0 -9
- package/dist/tools/bio/search-targets.js +0 -20
- package/dist/tools/bio/search-toxcast.d.ts +0 -36
- package/dist/tools/bio/search-toxcast.js +0 -141
- package/dist/workflows/register-workflows.d.ts +0 -51
- package/dist/workflows/register-workflows.js +0 -48
- package/dist/workflows/resume-execute-analysis.d.ts +0 -70
- package/dist/workflows/resume-execute-analysis.js +0 -80
- package/dist/workflows/target-assessment/correction-loop.d.ts +0 -45
- package/dist/workflows/target-assessment/correction-loop.js +0 -62
- package/dist/workflows/target-assessment/steps/phase0-resolve.d.ts +0 -71
- package/dist/workflows/target-assessment/steps/phase0-resolve.js +0 -21
- package/dist/workflows/target-assessment/steps/phase1-aggregate.d.ts +0 -1018
- package/dist/workflows/target-assessment/steps/phase1-aggregate.js +0 -25
- package/dist/workspace/mount-strategy.d.ts +0 -50
- package/dist/workspace/mount-strategy.js +0 -67
- package/dist/workspace/provenance-collector.d.ts +0 -34
- package/dist/workspace/provenance-collector.js +0 -16
- package/dist/workspace/workspace-paths.d.ts +0 -26
- package/dist/workspace/workspace-paths.js +0 -31
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* the workspace mutate trio (`execute_command`, `write_file`, `edit_file`)
|
|
9
9
|
* bound to the shared `SandboxClient` and the step's `allowedWritePrefix`,
|
|
10
10
|
* the workspace read tools (`read_file`, `grep`) over the shared
|
|
11
|
-
* `WorkspaceFilesystem`, and the
|
|
12
|
-
* tools its `meta.tools` allowlist names.
|
|
11
|
+
* `WorkspaceFilesystem`, `inspect_data_profile` over the shared `Pool`, and the
|
|
12
|
+
* bio/literature/context7/run-inspection tools its `meta.tools` allowlist names.
|
|
13
13
|
*
|
|
14
14
|
* Tool resolution is a single registry lookup — every name in `meta.tools`
|
|
15
15
|
* must map to a concrete `Tool`; unknown names throw at composition time
|
|
@@ -18,13 +18,15 @@
|
|
|
18
18
|
import { sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
|
|
19
19
|
import { composeSystemPrompt } from "../system-prompt.js";
|
|
20
20
|
// Sandbox-environment introspection.
|
|
21
|
-
import {
|
|
21
|
+
import { createListAvailableRefsTool, listAvailablePackagesTool } from "../../tools/sandbox/index.js";
|
|
22
22
|
// Context7 docs (pure leaves).
|
|
23
23
|
import { queryDocsTool, resolveLibraryIdTool } from "../../tools/research/context7-docs.js";
|
|
24
24
|
// Run-inspection (dependency-bearing).
|
|
25
25
|
import { createInspectRunTool } from "../../tools/research/inspect-run.js";
|
|
26
|
+
// Data-profile retrieval (dependency-bearing) — always-on, see `createSandboxAgent`.
|
|
27
|
+
import { createInspectDataProfileTool } from "../../tools/research/inspect-data-profile.js";
|
|
26
28
|
// Bio leaf tools.
|
|
27
|
-
import { checkSafetyPanelTool,
|
|
29
|
+
import { checkSafetyPanelTool, chemblTool, getImpcKoProfileTool, lookupGoTermTool, openTargetsTool, pubchemTool, searchBgeeExpressionTool, searchClinicalTrialsTool, createSearchDgidbTool, searchFaersTool, searchGeneTool, searchGeoDatasetsTool, searchGwasCatalogTool, searchInteractionsTool, searchPathwayTool, searchPharmgkbTool, } from "../../tools/bio/index.js";
|
|
28
30
|
import { createNcbiTools, createChemDbTools } from "../../tools/bio/keys.js";
|
|
29
31
|
// Workspace read surface.
|
|
30
32
|
import { createFileStatTool, createGrepTool, createListFilesTool, createReadFileTool, createWorkspaceSearchTool } from "../../tools/workspace/index.js";
|
|
@@ -44,52 +46,48 @@ import { SANDBOX_AGENT_DEFAULT_MAX_ITERATIONS } from "./types.js";
|
|
|
44
46
|
*/
|
|
45
47
|
export const BASE_SANDBOX_TOOLS = ["listAvailablePackages", "listAvailableRefs", "resolveLibraryId", "queryDocs", "inspectRun"];
|
|
46
48
|
/**
|
|
47
|
-
* Resolve `meta.tools` into the bio/research tools record.
|
|
48
|
-
* read/mutate tools are added by `createSandboxAgent`
|
|
49
|
-
* — they are the always-on substrate, not in the allowlist.
|
|
49
|
+
* Resolve `meta.tools` into the bio/research tools record. The workspace
|
|
50
|
+
* read/mutate tools and `inspect_data_profile` are added by `createSandboxAgent`
|
|
51
|
+
* regardless of meta — they are the always-on substrate, not in the allowlist.
|
|
50
52
|
*/
|
|
51
53
|
function resolveSandboxTools(deps, tools) {
|
|
52
54
|
const ncbi = createNcbiTools(deps.bioKeys);
|
|
53
55
|
const chemDb = createChemDbTools(deps.bioKeys);
|
|
54
56
|
const registry = {
|
|
55
57
|
listAvailablePackages: listAvailablePackagesTool,
|
|
56
|
-
listAvailableRefs:
|
|
58
|
+
listAvailableRefs: createListAvailableRefsTool({
|
|
59
|
+
sandboxClient: deps.sandboxClient,
|
|
60
|
+
sandbox: deps.step.sandbox,
|
|
61
|
+
workflowId: deps.step.workflowId,
|
|
62
|
+
stepId: deps.step.stepId,
|
|
63
|
+
nextFunctionId: deps.step.nextFunctionId,
|
|
64
|
+
deadlineMs: deps.step.deadlineMs,
|
|
65
|
+
markExecActive: createMarkExecActive(deps),
|
|
66
|
+
}),
|
|
57
67
|
resolveLibraryId: resolveLibraryIdTool,
|
|
58
68
|
queryDocs: queryDocsTool,
|
|
59
69
|
inspectRun: createInspectRunTool(deps.pool),
|
|
60
|
-
|
|
61
|
-
getArticleDetails: ncbi.getArticleDetails,
|
|
62
|
-
getArticleFullText: ncbi.getArticleFullText,
|
|
70
|
+
pubmed: ncbi.pubmed,
|
|
63
71
|
searchGene: searchGeneTool,
|
|
64
72
|
searchPathway: searchPathwayTool,
|
|
65
73
|
lookupGoTerm: lookupGoTermTool,
|
|
66
74
|
searchInteractions: searchInteractionsTool,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
getMechanism: getMechanismTool,
|
|
71
|
-
getDrugInfo: getDrugInfoTool,
|
|
72
|
-
searchPubchemCompound: searchPubchemCompoundTool,
|
|
73
|
-
getPubchemCrossRefs: getPubchemCrossRefsTool,
|
|
74
|
-
getPubchemAssays: getPubchemAssaysTool,
|
|
75
|
-
searchOpenTargets: searchOpenTargetsTool,
|
|
76
|
-
getTargetSafety: getTargetSafetyTool,
|
|
75
|
+
chembl: chemblTool,
|
|
76
|
+
pubchem: pubchemTool,
|
|
77
|
+
opentargets: openTargetsTool,
|
|
77
78
|
searchPharmgkb: searchPharmgkbTool,
|
|
78
79
|
searchFaers: searchFaersTool,
|
|
79
80
|
searchClinicalTrials: searchClinicalTrialsTool,
|
|
80
81
|
searchGeoDatasets: searchGeoDatasetsTool,
|
|
81
82
|
searchClinvar: ncbi.searchClinvar,
|
|
82
|
-
searchDgidb:
|
|
83
|
+
searchDgidb: createSearchDgidbTool({ ...(deps.logger ? { logger: deps.logger } : {}) }),
|
|
83
84
|
searchGwasCatalog: searchGwasCatalogTool,
|
|
84
85
|
searchDisgenet: chemDb.searchDisgenet,
|
|
85
86
|
searchDrugbank: chemDb.searchDrugbank,
|
|
86
87
|
searchBgeeExpression: searchBgeeExpressionTool,
|
|
87
88
|
getImpcKoProfile: getImpcKoProfileTool,
|
|
88
89
|
checkSafetyPanel: checkSafetyPanelTool,
|
|
89
|
-
|
|
90
|
-
searchCtxHazard: chemDb.searchCtxHazard,
|
|
91
|
-
searchCtxChemical: chemDb.searchCtxChemical,
|
|
92
|
-
searchCtxExposure: chemDb.searchCtxExposure,
|
|
90
|
+
comptox: chemDb.comptox,
|
|
93
91
|
};
|
|
94
92
|
const seen = new Set();
|
|
95
93
|
const resolved = [];
|
|
@@ -105,6 +103,9 @@ function resolveSandboxTools(deps, tools) {
|
|
|
105
103
|
}
|
|
106
104
|
return resolved;
|
|
107
105
|
}
|
|
106
|
+
function createMarkExecActive(deps) {
|
|
107
|
+
return (execId) => setActiveExecId(deps.pool, deps.step.runId, deps.step.stepId, execId).match(() => { }, () => { });
|
|
108
|
+
}
|
|
108
109
|
/** Build the workspace mutate + read tools every sandbox agent receives. In
|
|
109
110
|
* `readOnly` mode the write_file/edit_file pair is omitted; execute_command
|
|
110
111
|
* and the read tools stay. */
|
|
@@ -113,7 +114,7 @@ function buildWorkspaceTools(deps, readOnly) {
|
|
|
113
114
|
// Registry tagging is a best-effort watchdog backstop (`run-exec.ts` already
|
|
114
115
|
// swallows a throw here); fold a `DbError` into a no-op so a registry write
|
|
115
116
|
// failure neither fails the exec nor surfaces as an unhandled rejection.
|
|
116
|
-
const markExecActive = (
|
|
117
|
+
const markExecActive = createMarkExecActive(deps);
|
|
117
118
|
// The agent's writable working directory: relative paths resolve here, and
|
|
118
119
|
// writes are confined here. `allowedWritePrefix` is its host path; the
|
|
119
120
|
// in-sandbox path (the `execute_command` default cwd, see the harness-workspace-tools spec) is the same
|
|
@@ -161,26 +162,36 @@ function buildWorkspaceTools(deps, readOnly) {
|
|
|
161
162
|
}
|
|
162
163
|
/**
|
|
163
164
|
* Build one sandbox `AgentDefinition`. The system prompt is composed once
|
|
164
|
-
* (SOUL
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
165
|
+
* (SOUL execution core + agent body + sandbox standards) — no processors, and
|
|
166
|
+
* neither human-facing SOUL layer: a sandbox agent is headless, so the identity
|
|
167
|
+
* and conversational layers are dead weight in its context while every hard
|
|
168
|
+
* guardrail it does need rides in the execution core. Tools are resolved
|
|
169
|
+
* against the closed allowlist; the workspace surface is always wired
|
|
170
|
+
* regardless of meta.
|
|
171
|
+
*
|
|
172
|
+
* The composed `systemPrompt` is a **pure function of the agent type** — nothing
|
|
173
|
+
* from `deps.step` reaches it, so two steps of one run, and two runs of one
|
|
174
|
+
* analysis, send a byte-identical ~20k-char prefix that the provider's prompt
|
|
175
|
+
* cache can actually reuse. Keep it that way: a single interpolated id or path
|
|
176
|
+
* makes every step's prefix unique again, and each step pays a full cache write
|
|
177
|
+
* and reads nothing back. Per-step values belong in the step's briefing (its
|
|
178
|
+
* first user message — see `prompts/briefing.ts`), which names the working
|
|
179
|
+
* directory, the analysis root, the dataset, and what each dependency produced.
|
|
168
180
|
*/
|
|
169
181
|
export function createSandboxAgent(deps, meta, body, opts = {}) {
|
|
170
182
|
const appendStandards = opts.appendAnalysisStepStandards ?? true;
|
|
171
183
|
const sandboxLayer = appendStandards ? [sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt] : [sandboxOrientCorePrompt];
|
|
172
184
|
const agentBody = [body, ...sandboxLayer].map((s) => s.trim()).join("\n\n");
|
|
173
|
-
|
|
174
|
-
// path model is accurate per step (see the harness-workspace-tools spec), not boilerplate prose.
|
|
175
|
-
const analysisRoot = `/${deps.step.analysisId}`;
|
|
176
|
-
const workingDir = toSandboxPath(deps.step.workspaceRoot, deps.step.analysisId, deps.step.allowedWritePrefix);
|
|
177
|
-
const resolvedBody = agentBody.split("{{WORKING_DIR}}").join(workingDir).split("{{ANALYSIS_ROOT}}").join(analysisRoot);
|
|
178
|
-
const systemPrompt = composeSystemPrompt(resolvedBody, {
|
|
179
|
-
includeConversationalStyle: false,
|
|
180
|
-
});
|
|
185
|
+
const systemPrompt = composeSystemPrompt(agentBody);
|
|
181
186
|
const skillTools = deps.skillsDir ? Object.values(createSkillTools({ skillsDir: deps.skillsDir, skills: meta.skills })) : [];
|
|
182
187
|
const tools = [
|
|
183
188
|
...buildWorkspaceTools(deps, opts.readOnly ?? false),
|
|
189
|
+
// Always-on, not in the `meta.tools` allowlist: the data profile is the only
|
|
190
|
+
// record of what the analysis's input dataset IS, and no file carries it (the
|
|
191
|
+
// profiler's scratch tree is deleted on completion). An agent that cannot pull
|
|
192
|
+
// it has no fallback but to re-derive organism, dimensions, and format from the
|
|
193
|
+
// raw bytes — so every sandbox agent gets it, whatever its meta declares.
|
|
194
|
+
createInspectDataProfileTool(deps.pool),
|
|
184
195
|
...skillTools,
|
|
185
196
|
...resolveSandboxTools(deps, meta.tools),
|
|
186
197
|
...(deps.blockerHolder ? [createReportBlockerTool(deps.blockerHolder)] : []),
|
|
@@ -18,7 +18,7 @@ export const meta = {
|
|
|
18
18
|
],
|
|
19
19
|
suitableFor: ["scrna-seq", "snrna-seq", "cytof", "mass-cytometry"],
|
|
20
20
|
skills: ["single-cell", "shared/omics-general"],
|
|
21
|
-
tools: [...BASE_SANDBOX_TOOLS, "
|
|
21
|
+
tools: [...BASE_SANDBOX_TOOLS, "pubmed", "searchGeoDatasets"],
|
|
22
22
|
};
|
|
23
23
|
export function createSingleCellAgent(deps) {
|
|
24
24
|
return createSandboxAgent(deps, meta, singleCellAgentPrompt);
|
|
@@ -8,7 +8,7 @@ export const meta = {
|
|
|
8
8
|
capabilities: ["spatial statistics", "spatial domain detection", "deconvolution", "niche analysis", "spatial autocorrelation", "ligand-receptor spatial"],
|
|
9
9
|
suitableFor: ["visium", "merfish", "slide-seq", "codex", "xenium", "mibi", "imc"],
|
|
10
10
|
skills: ["spatial-omics", "shared/omics-general"],
|
|
11
|
-
tools: [...BASE_SANDBOX_TOOLS, "
|
|
11
|
+
tools: [...BASE_SANDBOX_TOOLS, "pubmed", "searchGeoDatasets"],
|
|
12
12
|
};
|
|
13
13
|
export function createSpatialOmicsAgent(deps) {
|
|
14
14
|
return createSandboxAgent(deps, meta, spatialOmicsAgentPrompt);
|
|
@@ -30,7 +30,7 @@ export const meta = {
|
|
|
30
30
|
"patient-stratification",
|
|
31
31
|
],
|
|
32
32
|
skills: ["statistical-modeling", "shared/omics-general"],
|
|
33
|
-
tools: [...BASE_SANDBOX_TOOLS, "
|
|
33
|
+
tools: [...BASE_SANDBOX_TOOLS, "pubmed"],
|
|
34
34
|
};
|
|
35
35
|
export function createStatisticalModelingAgent(deps) {
|
|
36
36
|
return createSandboxAgent(deps, meta, statisticalModelingAgentPrompt);
|
|
@@ -34,22 +34,16 @@ export const meta = {
|
|
|
34
34
|
tools: [
|
|
35
35
|
...BASE_SANDBOX_TOOLS,
|
|
36
36
|
"checkSafetyPanel",
|
|
37
|
-
"
|
|
38
|
-
"getArticleDetails",
|
|
39
|
-
"getArticleFullText",
|
|
37
|
+
"pubmed",
|
|
40
38
|
"searchGene",
|
|
41
39
|
"searchPathway",
|
|
42
40
|
"lookupGoTerm",
|
|
43
41
|
"searchInteractions",
|
|
44
|
-
"
|
|
45
|
-
"getTargetSafety",
|
|
42
|
+
"opentargets",
|
|
46
43
|
"searchFaers",
|
|
47
44
|
"searchPharmgkb",
|
|
48
45
|
"searchDgidb",
|
|
49
|
-
"
|
|
50
|
-
"searchCtxHazard",
|
|
51
|
-
"searchCtxChemical",
|
|
52
|
-
"searchCtxExposure",
|
|
46
|
+
"comptox",
|
|
53
47
|
"searchBgeeExpression",
|
|
54
48
|
"getImpcKoProfile",
|
|
55
49
|
],
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* envelope. `searchGwasCatalog` is fully public.
|
|
13
13
|
*/
|
|
14
14
|
/** Closed allowlist of tools any sandbox agent may declare. */
|
|
15
|
-
export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "
|
|
15
|
+
export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "searchGene" | "searchPathway" | "lookupGoTerm" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "searchPharmgkb" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "searchClinvar" | "searchDgidb" | "searchGwasCatalog" | "searchDisgenet" | "searchDrugbank" | "searchBgeeExpression" | "getImpcKoProfile" | "checkSafetyPanel" | "comptox";
|
|
16
16
|
/** Planner-facing metadata + tool allowlist for one sandbox agent. */
|
|
17
17
|
export interface AgentMeta {
|
|
18
18
|
/** Stable agent id — also the key in the catalog and the `AgentDefinition.id`. */
|
|
@@ -2,24 +2,50 @@
|
|
|
2
2
|
* Harness system-prompt composition.
|
|
3
3
|
*
|
|
4
4
|
* SOUL is static text — there is no processor pipeline. `composeSystemPrompt`
|
|
5
|
-
* concatenates the SOUL
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* concatenates the SOUL layers an agent declares with the agent's own prompt
|
|
6
|
+
* into the single `AgentDefinition.systemPrompt` string the loop sends as the
|
|
7
|
+
* cacheable prefix. It is the only place SOUL is applied, and every agent is
|
|
8
|
+
* built through it — an unguarded agent is not constructible.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The layer policy:
|
|
11
|
+
*
|
|
12
|
+
* - `SOULExecutionCore` is **always** included; there is no opt-out. The
|
|
13
|
+
* scientific stance and the hard guardrails (never fabricate, never oversell,
|
|
14
|
+
* never hide uncertainty, confidentiality, prompt-injection defense) bind a
|
|
15
|
+
* headless agent exactly as they bind the user-facing one. The run
|
|
16
|
+
* synthesizer and the report builder author user-facing scientific claims
|
|
17
|
+
* without a human in their loop — they are the last agents that should be
|
|
18
|
+
* free to fabricate.
|
|
19
|
+
*
|
|
20
|
+
* - `identity` (default `false`) adds `SOULIdentity` — the Inflexa name, the
|
|
21
|
+
* scope, and the human-facing refusal / impersonation / external-action
|
|
22
|
+
* guardrails. Only the conversation agent faces a human, so only it opts in.
|
|
23
|
+
*
|
|
24
|
+
* - `conversational` (default `false`) adds `SOULConversationalPrompt` —
|
|
25
|
+
* personality, response policy, and the 7-step reasoning cadence. Conversation
|
|
26
|
+
* agent only: a tool-only specialist's tight loop competes with conversational
|
|
27
|
+
* discipline.
|
|
28
|
+
*
|
|
29
|
+
* Both flags default off, so the zero-argument call yields a fully guarded
|
|
30
|
+
* headless agent — the safe default is also the shortest one to write.
|
|
13
31
|
*/
|
|
14
32
|
export interface ComposeSystemPromptOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Include the SOUL identity layer (name, scope, refusal of harmful
|
|
35
|
+
* requests, impersonation and external-action guardrails). Default `false`
|
|
36
|
+
* — opt in only for an agent a human talks to.
|
|
37
|
+
*/
|
|
38
|
+
readonly identity?: boolean;
|
|
15
39
|
/**
|
|
16
40
|
* Include the SOUL conversational layer (personality, response policy,
|
|
17
|
-
* 7-step reasoning). Default `
|
|
41
|
+
* 7-step reasoning). Default `false` — opt in only for the conversation
|
|
42
|
+
* agent.
|
|
18
43
|
*/
|
|
19
|
-
readonly
|
|
44
|
+
readonly conversational?: boolean;
|
|
20
45
|
}
|
|
21
46
|
/**
|
|
22
|
-
* Compose an agent's full system prompt: SOUL
|
|
23
|
-
*
|
|
47
|
+
* Compose an agent's full system prompt: the always-on SOUL execution core, the
|
|
48
|
+
* layers the agent opted into, then the agent's own prompt — joined by blank
|
|
49
|
+
* lines.
|
|
24
50
|
*/
|
|
25
51
|
export declare function composeSystemPrompt(agentPrompt: string, options?: ComposeSystemPromptOptions): string;
|
|
@@ -2,22 +2,45 @@
|
|
|
2
2
|
* Harness system-prompt composition.
|
|
3
3
|
*
|
|
4
4
|
* SOUL is static text — there is no processor pipeline. `composeSystemPrompt`
|
|
5
|
-
* concatenates the SOUL
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* concatenates the SOUL layers an agent declares with the agent's own prompt
|
|
6
|
+
* into the single `AgentDefinition.systemPrompt` string the loop sends as the
|
|
7
|
+
* cacheable prefix. It is the only place SOUL is applied, and every agent is
|
|
8
|
+
* built through it — an unguarded agent is not constructible.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The layer policy:
|
|
11
|
+
*
|
|
12
|
+
* - `SOULExecutionCore` is **always** included; there is no opt-out. The
|
|
13
|
+
* scientific stance and the hard guardrails (never fabricate, never oversell,
|
|
14
|
+
* never hide uncertainty, confidentiality, prompt-injection defense) bind a
|
|
15
|
+
* headless agent exactly as they bind the user-facing one. The run
|
|
16
|
+
* synthesizer and the report builder author user-facing scientific claims
|
|
17
|
+
* without a human in their loop — they are the last agents that should be
|
|
18
|
+
* free to fabricate.
|
|
19
|
+
*
|
|
20
|
+
* - `identity` (default `false`) adds `SOULIdentity` — the Inflexa name, the
|
|
21
|
+
* scope, and the human-facing refusal / impersonation / external-action
|
|
22
|
+
* guardrails. Only the conversation agent faces a human, so only it opts in.
|
|
23
|
+
*
|
|
24
|
+
* - `conversational` (default `false`) adds `SOULConversationalPrompt` —
|
|
25
|
+
* personality, response policy, and the 7-step reasoning cadence. Conversation
|
|
26
|
+
* agent only: a tool-only specialist's tight loop competes with conversational
|
|
27
|
+
* discipline.
|
|
28
|
+
*
|
|
29
|
+
* Both flags default off, so the zero-argument call yields a fully guarded
|
|
30
|
+
* headless agent — the safe default is also the shortest one to write.
|
|
13
31
|
*/
|
|
14
|
-
import {
|
|
32
|
+
import { SOULExecutionCore, SOULIdentity, SOULConversationalPrompt } from "../prompts/SOUL.js";
|
|
15
33
|
/**
|
|
16
|
-
* Compose an agent's full system prompt: SOUL
|
|
17
|
-
*
|
|
34
|
+
* Compose an agent's full system prompt: the always-on SOUL execution core, the
|
|
35
|
+
* layers the agent opted into, then the agent's own prompt — joined by blank
|
|
36
|
+
* lines.
|
|
18
37
|
*/
|
|
19
38
|
export function composeSystemPrompt(agentPrompt, options = {}) {
|
|
20
|
-
const
|
|
21
|
-
|
|
39
|
+
const sections = [SOULExecutionCore];
|
|
40
|
+
if (options.identity ?? false)
|
|
41
|
+
sections.push(SOULIdentity);
|
|
42
|
+
if (options.conversational ?? false)
|
|
43
|
+
sections.push(SOULConversationalPrompt);
|
|
44
|
+
sections.push(agentPrompt);
|
|
22
45
|
return sections.map((s) => s.trim()).join("\n\n");
|
|
23
46
|
}
|
package/dist/app/chat-turn.d.ts
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { Pool } from "pg";
|
|
16
16
|
import { type AssembledMessages } from "./message-assembly.js";
|
|
17
|
+
import type { Logger } from "../lib/logger.js";
|
|
17
18
|
export interface PrepareChatTurnDeps {
|
|
19
|
+
/** Operational logging seam; omitted falls back to no-op. */
|
|
20
|
+
readonly logger?: Logger;
|
|
18
21
|
readonly pool: Pool;
|
|
19
22
|
}
|
|
20
23
|
export interface PrepareChatTurnParams {
|
package/dist/app/chat-turn.js
CHANGED
|
@@ -19,6 +19,7 @@ import { createThreadStore } from "../memory/thread-store.js";
|
|
|
19
19
|
import { createWorkingMemory } from "../memory/working-memory.js";
|
|
20
20
|
import { loadAnalysisStatus } from "../state/index.js";
|
|
21
21
|
import { assembleMessages } from "./message-assembly.js";
|
|
22
|
+
import { createNoopLogger } from "../lib/console-logger.js";
|
|
22
23
|
/**
|
|
23
24
|
* Prepare one chat turn: resolve thread ownership, seed the title, load
|
|
24
25
|
* analysis status, and assemble the message array. Input sanitization stays
|
|
@@ -48,7 +49,8 @@ export async function prepareChatTurn(deps, params) {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
catch (err) {
|
|
51
|
-
|
|
52
|
+
const logger = (deps.logger ?? createNoopLogger()).named("harness.chat");
|
|
53
|
+
logger.warn("title-seed failed (non-fatal)", logger.errorFields(err));
|
|
52
54
|
}
|
|
53
55
|
const analysisState = await loadAnalysisStatus(pool, analysisId).unwrapOr(null);
|
|
54
56
|
const history = createThreadHistory(pool);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-profile orientation — the bounded projection of a persisted
|
|
3
|
+
* `DataProfileResult` into the few hundred characters an agent needs to know
|
|
4
|
+
* WHAT DATASET IT IS HOLDING before it does anything else.
|
|
5
|
+
*
|
|
6
|
+
* The persisted profile is deliberately rich (per-file metrics, warnings, tags,
|
|
7
|
+
* the full narrative summary) because the DB row is its only durable home — the
|
|
8
|
+
* profiler's scratch tree is deleted on completion. That richness is right for a
|
|
9
|
+
* pull-on-demand tool (`inspect_data_profile`) and wrong for anything injected
|
|
10
|
+
* unconditionally into a context window, which is what a step seed does. This is
|
|
11
|
+
* the lossy half of that pair: a hard character budget, a fixed field order, and
|
|
12
|
+
* visible truncation counts, so an agent that needs more knows to go pull it.
|
|
13
|
+
*
|
|
14
|
+
* A pure function over the record — no I/O, no LLM, no pipeline. It is the whole
|
|
15
|
+
* mechanism.
|
|
16
|
+
*/
|
|
17
|
+
import type { DataProfileResult } from "../state/data-profile.js";
|
|
18
|
+
/** The character budget the projection guarantees it will not exceed. */
|
|
19
|
+
export declare const DATA_PROFILE_ORIENTATION_MAX_CHARS = 1200;
|
|
20
|
+
/**
|
|
21
|
+
* Project a persisted profile into an orientation blurb of at most `maxChars`
|
|
22
|
+
* characters. The bound is a guarantee, not a target: the assembled text is
|
|
23
|
+
* hard-clamped, so no profile — however verbose — can blow a caller's budget.
|
|
24
|
+
*
|
|
25
|
+
* Field order is fixed and reflects what an agent must know first: what the
|
|
26
|
+
* dataset IS, how it was designed, what is wrong with it, then what files carry
|
|
27
|
+
* it. Both list sections state their true total, so an elided tail is visible
|
|
28
|
+
* rather than silent.
|
|
29
|
+
*
|
|
30
|
+
* A snapshot written before the record was widened carries none of the structured
|
|
31
|
+
* fields. Rather than emit a bare file list, that case falls back to the narrative
|
|
32
|
+
* summary — the one orientation a legacy row does have.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildDataProfileOrientation(result: DataProfileResult, maxChars?: number): string;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-profile orientation — the bounded projection of a persisted
|
|
3
|
+
* `DataProfileResult` into the few hundred characters an agent needs to know
|
|
4
|
+
* WHAT DATASET IT IS HOLDING before it does anything else.
|
|
5
|
+
*
|
|
6
|
+
* The persisted profile is deliberately rich (per-file metrics, warnings, tags,
|
|
7
|
+
* the full narrative summary) because the DB row is its only durable home — the
|
|
8
|
+
* profiler's scratch tree is deleted on completion. That richness is right for a
|
|
9
|
+
* pull-on-demand tool (`inspect_data_profile`) and wrong for anything injected
|
|
10
|
+
* unconditionally into a context window, which is what a step seed does. This is
|
|
11
|
+
* the lossy half of that pair: a hard character budget, a fixed field order, and
|
|
12
|
+
* visible truncation counts, so an agent that needs more knows to go pull it.
|
|
13
|
+
*
|
|
14
|
+
* A pure function over the record — no I/O, no LLM, no pipeline. It is the whole
|
|
15
|
+
* mechanism.
|
|
16
|
+
*/
|
|
17
|
+
/** The character budget the projection guarantees it will not exceed. */
|
|
18
|
+
export const DATA_PROFILE_ORIENTATION_MAX_CHARS = 1200;
|
|
19
|
+
/** Files listed before the tail is elided (the count still reports the total). */
|
|
20
|
+
const MAX_FILES = 8;
|
|
21
|
+
/** Quality concerns listed — the profiler orders them, so these are the top ones. */
|
|
22
|
+
const MAX_CONCERNS = 3;
|
|
23
|
+
const MAX_DESIGN_CHARS = 200;
|
|
24
|
+
const MAX_CONCERN_CHARS = 120;
|
|
25
|
+
const MAX_FILE_DESCRIPTION_CHARS = 100;
|
|
26
|
+
const MAX_SUMMARY_FALLBACK_CHARS = 300;
|
|
27
|
+
/**
|
|
28
|
+
* Clamp to `max` chars, marking any elision with an ellipsis. Whitespace is left
|
|
29
|
+
* alone, so the assembled line structure survives the final bound.
|
|
30
|
+
*/
|
|
31
|
+
function clamp(text, max) {
|
|
32
|
+
if (text.length <= max)
|
|
33
|
+
return text;
|
|
34
|
+
if (max <= 1)
|
|
35
|
+
return "";
|
|
36
|
+
return text.slice(0, max - 1).trimEnd() + "…";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Collapse a free-text field to one line and clamp it. Profiler prose (a summary, a
|
|
40
|
+
* design note, a file description) may contain newlines; folding them out is what
|
|
41
|
+
* keeps one record on one line.
|
|
42
|
+
*/
|
|
43
|
+
function clip(text, max) {
|
|
44
|
+
return clamp(text.replace(/\s+/g, " ").trim(), max);
|
|
45
|
+
}
|
|
46
|
+
/** Non-empty, whitespace-collapsed value, or `undefined` — nulls and blanks read the same. */
|
|
47
|
+
function present(value) {
|
|
48
|
+
const flat = value?.replace(/\s+/g, " ").trim();
|
|
49
|
+
return flat ? flat : undefined;
|
|
50
|
+
}
|
|
51
|
+
/** `Homo sapiens (taxon 9606)`, plus a confidence marker when the profiler was unsure. */
|
|
52
|
+
function formatOrganism(organism) {
|
|
53
|
+
if (!organism)
|
|
54
|
+
return undefined;
|
|
55
|
+
const name = present(organism.scientificName);
|
|
56
|
+
if (!name)
|
|
57
|
+
return undefined;
|
|
58
|
+
const taxon = present(organism.taxonId);
|
|
59
|
+
const qualifier = organism.confidence === "high" ? "" : ` [${organism.confidence} confidence]`;
|
|
60
|
+
return taxon ? `${name} (taxon ${taxon})${qualifier}` : `${name}${qualifier}`;
|
|
61
|
+
}
|
|
62
|
+
/** `(20000 x 24, CSV)` — whichever of dimensions / format the profiler recorded. */
|
|
63
|
+
function formatFileFacts(file) {
|
|
64
|
+
const dims = typeof file.rows === "number" && typeof file.cols === "number" ? `${file.rows} x ${file.cols}` : undefined;
|
|
65
|
+
const facts = [dims, present(file.format)].filter((f) => f !== undefined);
|
|
66
|
+
return facts.length > 0 ? ` (${facts.join(", ")})` : "";
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Project a persisted profile into an orientation blurb of at most `maxChars`
|
|
70
|
+
* characters. The bound is a guarantee, not a target: the assembled text is
|
|
71
|
+
* hard-clamped, so no profile — however verbose — can blow a caller's budget.
|
|
72
|
+
*
|
|
73
|
+
* Field order is fixed and reflects what an agent must know first: what the
|
|
74
|
+
* dataset IS, how it was designed, what is wrong with it, then what files carry
|
|
75
|
+
* it. Both list sections state their true total, so an elided tail is visible
|
|
76
|
+
* rather than silent.
|
|
77
|
+
*
|
|
78
|
+
* A snapshot written before the record was widened carries none of the structured
|
|
79
|
+
* fields. Rather than emit a bare file list, that case falls back to the narrative
|
|
80
|
+
* summary — the one orientation a legacy row does have.
|
|
81
|
+
*/
|
|
82
|
+
export function buildDataProfileOrientation(result, maxChars = DATA_PROFILE_ORIENTATION_MAX_CHARS) {
|
|
83
|
+
const lines = [];
|
|
84
|
+
const identity = [
|
|
85
|
+
[present(result.domain), present(result.subtype)].filter((f) => f !== undefined).join(" / ") || undefined,
|
|
86
|
+
formatOrganism(result.organism),
|
|
87
|
+
present(result.tissue) ? `tissue: ${present(result.tissue)}` : undefined,
|
|
88
|
+
present(result.cellType) ? `cells: ${present(result.cellType)}` : undefined,
|
|
89
|
+
present(result.condition) ? `condition: ${present(result.condition)}` : undefined,
|
|
90
|
+
].filter((f) => f !== undefined);
|
|
91
|
+
if (identity.length > 0) {
|
|
92
|
+
lines.push(`Dataset: ${identity.join(" — ")}`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// A legacy snapshot has no structured identity at all; the summary is the
|
|
96
|
+
// only orientation it carries, so it stands in for the whole header.
|
|
97
|
+
lines.push(`Dataset: ${clip(result.summary, MAX_SUMMARY_FALLBACK_CHARS)}`);
|
|
98
|
+
}
|
|
99
|
+
const design = present(result.experimentalDesign);
|
|
100
|
+
if (design)
|
|
101
|
+
lines.push(`Design: ${clip(design, MAX_DESIGN_CHARS)}`);
|
|
102
|
+
const concerns = (result.qualityAssessment?.concerns ?? []).map((c) => present(c)).filter((c) => c !== undefined);
|
|
103
|
+
if (concerns.length > 0) {
|
|
104
|
+
const shown = concerns.slice(0, MAX_CONCERNS).map((c) => clip(c, MAX_CONCERN_CHARS));
|
|
105
|
+
const more = concerns.length > shown.length ? ` (+${concerns.length - shown.length} more)` : "";
|
|
106
|
+
lines.push(`Concerns: ${shown.join("; ")}${more}`);
|
|
107
|
+
}
|
|
108
|
+
if (result.files.length > 0) {
|
|
109
|
+
const shown = result.files.slice(0, MAX_FILES);
|
|
110
|
+
const header = shown.length < result.files.length ? `Files (${shown.length} of ${result.files.length}):` : `Files (${result.files.length}):`;
|
|
111
|
+
lines.push(header);
|
|
112
|
+
for (const file of shown) {
|
|
113
|
+
lines.push(`- ${file.path} — ${clip(file.description, MAX_FILE_DESCRIPTION_CHARS)}${formatFileFacts(file)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return clamp(lines.join("\n"), maxChars);
|
|
117
|
+
}
|
|
@@ -22,7 +22,10 @@ import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
|
|
|
22
22
|
import type { BioToolKeys } from "../tools/bio/keys.js";
|
|
23
23
|
import type { RunFinding } from "../workflows/execute-analysis.js";
|
|
24
24
|
import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
25
|
+
import type { Logger } from "../lib/logger.js";
|
|
25
26
|
export interface SynthesizeRunDeps {
|
|
27
|
+
/** Operational logging seam; omitted falls back to no-op. */
|
|
28
|
+
readonly logger?: Logger;
|
|
26
29
|
readonly pool: Pool;
|
|
27
30
|
readonly provider: ChatProvider;
|
|
28
31
|
/** Write-side embedder — its `dimensions` sizes the per-analysis index. */
|
|
@@ -20,6 +20,7 @@ import { ensureSearchIndex, searchIndexName } from "../workspace/search-config.j
|
|
|
20
20
|
import { createVectorStore } from "../state/vector-store.js";
|
|
21
21
|
import { loadPlan, queryRun } from "../state/index.js";
|
|
22
22
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
23
|
+
import { createNoopLogger } from "../lib/console-logger.js";
|
|
23
24
|
export async function synthesizeRun(deps, params) {
|
|
24
25
|
const { pool, provider, embedding, resolveWorkspaceRoot, synthesisModel, bioKeys } = deps;
|
|
25
26
|
const { analysisId, runId, completedSteps, session, emit, onProgress } = params;
|
|
@@ -71,7 +72,8 @@ export async function synthesizeRun(deps, params) {
|
|
|
71
72
|
}));
|
|
72
73
|
}
|
|
73
74
|
catch (indexErr) {
|
|
74
|
-
|
|
75
|
+
const logger = (deps.logger ?? createNoopLogger()).named("synthesize-run");
|
|
76
|
+
logger.warn("vector indexing failed", { runId, ...logger.errorFields(indexErr) });
|
|
75
77
|
}
|
|
76
78
|
await onProgress("persisting", "Persisting synthesis");
|
|
77
79
|
await persistSynthesis({ workspaceRoot, runId, synthesis });
|