@inflexa-ai/harness 0.16.0 → 0.18.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/dist/agents/conversation-agent.d.ts +3 -0
- package/dist/agents/conversation-agent.js +5 -2
- package/dist/agents/report-builder.d.ts +2 -0
- package/dist/agents/report-builder.js +2 -0
- package/dist/agents/sandbox/shared.d.ts +3 -0
- package/dist/agents/sandbox/shared.js +5 -0
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/app/chat-turn.d.ts +2 -0
- package/dist/app/chat-turn.js +11 -2
- package/dist/app/spawn-report-session.d.ts +85 -0
- package/dist/app/spawn-report-session.js +83 -0
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +2 -1
- package/dist/citations/__fixtures__/resolver.d.ts +3 -0
- package/dist/citations/__fixtures__/resolver.js +9 -0
- package/dist/citations/aggregate.d.ts +3 -0
- package/dist/citations/aggregate.js +44 -0
- package/dist/citations/clients/arxiv.d.ts +6 -0
- package/dist/citations/clients/arxiv.js +34 -0
- package/dist/citations/clients/common.d.ts +11 -0
- package/dist/citations/clients/common.js +43 -0
- package/dist/citations/clients/crossref.d.ts +8 -0
- package/dist/citations/clients/crossref.js +98 -0
- package/dist/citations/clients/doi-registry.d.ts +3 -0
- package/dist/citations/clients/doi-registry.js +95 -0
- package/dist/citations/clients/pubmed.d.ts +6 -0
- package/dist/citations/clients/pubmed.js +83 -0
- package/dist/citations/clients/semantic-scholar.d.ts +6 -0
- package/dist/citations/clients/semantic-scholar.js +60 -0
- package/dist/citations/compare.d.ts +9 -0
- package/dist/citations/compare.js +72 -0
- package/dist/citations/match.d.ts +11 -0
- package/dist/citations/match.js +173 -0
- package/dist/citations/normalize.d.ts +31 -0
- package/dist/citations/normalize.js +187 -0
- package/dist/citations/plan.d.ts +2 -0
- package/dist/citations/plan.js +59 -0
- package/dist/citations/resolve.d.ts +28 -0
- package/dist/citations/resolve.js +238 -0
- package/dist/citations/types.d.ts +583 -0
- package/dist/citations/types.js +129 -0
- package/dist/contracts/chat-events.d.ts +9 -0
- package/dist/contracts/index.d.ts +6 -2
- package/dist/contracts/index.js +3 -1
- package/dist/contracts/organ-system.d.ts +49 -0
- package/dist/contracts/organ-system.js +67 -0
- package/dist/contracts/report-blocks.d.ts +564 -0
- package/dist/contracts/report-blocks.js +117 -0
- package/dist/contracts/report-reference.d.ts +459 -0
- package/dist/contracts/report-reference.js +256 -0
- package/dist/contracts/schemas/chat-events.d.ts +2 -0
- package/dist/contracts/schemas/chat-events.js +2 -0
- package/dist/contracts/schemas/chat-parts.d.ts +8 -8
- package/dist/contracts/severity.d.ts +15 -0
- package/dist/contracts/severity.js +10 -0
- package/dist/contracts/target-dossier.d.ts +8673 -12970
- package/dist/contracts/target-dossier.js +534 -591
- package/dist/data/safety-panel-schema.d.ts +18 -5
- package/dist/data/safety-panel-schema.js +4 -4
- package/dist/execution/artifact-metadata.js +1 -0
- package/dist/execution/report-runner.js +1 -3
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +3 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +22 -0
- package/dist/lib/async-utils.d.ts +16 -4
- package/dist/lib/async-utils.js +20 -15
- package/dist/lib/cache.d.ts +8 -0
- package/dist/lib/cache.js +35 -0
- package/dist/lib/host-concurrency.js +1 -0
- package/dist/lib/rate-limit.d.ts +35 -0
- package/dist/lib/rate-limit.js +121 -0
- package/dist/literature/sources/arxiv.d.ts +22 -0
- package/dist/literature/sources/arxiv.js +103 -0
- package/dist/literature/sources/http.d.ts +28 -0
- package/dist/literature/sources/http.js +97 -0
- package/dist/literature/sources/pubmed.d.ts +68 -0
- package/dist/literature/sources/pubmed.js +266 -0
- package/dist/literature/sources/semantic-scholar.d.ts +24 -0
- package/dist/literature/sources/semantic-scholar.js +71 -0
- package/dist/loop/__fixtures__/scripted-provider.js +23 -2
- package/dist/loop/run-agent.js +58 -10
- package/dist/loop/tool-detail.d.ts +40 -0
- package/dist/loop/tool-detail.js +35 -3
- package/dist/loop/types.d.ts +13 -0
- package/dist/memory/thread-history.d.ts +10 -0
- package/dist/memory/thread-history.js +12 -1
- package/dist/memory/thread-store.d.ts +142 -25
- package/dist/memory/thread-store.js +228 -40
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +2 -0
- package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
- package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
- package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
- package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
- package/dist/prompts/target-assessment/investigation/index.js +3 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
- package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
- package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
- package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
- package/dist/providers/__fixtures__/session.d.ts +5 -4
- package/dist/providers/__fixtures__/session.js +4 -4
- package/dist/report-model/block-walk.d.ts +56 -0
- package/dist/report-model/block-walk.js +89 -0
- package/dist/report-model/draft-finish.d.ts +60 -0
- package/dist/report-model/draft-finish.js +56 -0
- package/dist/report-model/draft-operations.d.ts +125 -0
- package/dist/report-model/draft-operations.js +341 -0
- package/dist/report-model/draft-read.d.ts +59 -0
- package/dist/report-model/draft-read.js +109 -0
- package/dist/report-model/draft.d.ts +51 -0
- package/dist/report-model/draft.js +32 -0
- package/dist/report-model/fixture-resolver.d.ts +8 -0
- package/dist/report-model/fixture-resolver.js +252 -0
- package/dist/report-model/mint-snapshot.d.ts +30 -0
- package/dist/report-model/mint-snapshot.js +40 -0
- package/dist/report-model/reference-resolver.d.ts +113 -0
- package/dist/report-model/reference-resolver.js +60 -0
- package/dist/report-model/structural-validation.d.ts +22 -0
- package/dist/report-model/structural-validation.js +68 -0
- package/dist/report-model/validate.d.ts +40 -0
- package/dist/report-model/validate.js +80 -0
- package/dist/report-render/chart.d.ts +33 -0
- package/dist/report-render/chart.js +538 -0
- package/dist/report-render/page.d.ts +127 -0
- package/dist/report-render/page.js +307 -0
- package/dist/report-render/references.d.ts +32 -0
- package/dist/report-render/references.js +44 -0
- package/dist/report-render/render.d.ts +25 -0
- package/dist/report-render/render.js +125 -0
- package/dist/report-render/script-json.d.ts +13 -0
- package/dist/report-render/script-json.js +15 -0
- package/dist/report-render/types.d.ts +39 -0
- package/dist/report-render/types.js +10 -0
- package/dist/report-render/views/chart-view.d.ts +11 -0
- package/dist/report-render/views/chart-view.js +23 -0
- package/dist/report-render/views/page-view.d.ts +20 -0
- package/dist/report-render/views/page-view.js +49 -0
- package/dist/report-render/views/prose.d.ts +28 -0
- package/dist/report-render/views/prose.js +75 -0
- package/dist/report-render/views/references-view.d.ts +20 -0
- package/dist/report-render/views/references-view.js +64 -0
- package/dist/report-render/views/values.d.ts +42 -0
- package/dist/report-render/views/values.js +43 -0
- package/dist/runtime/assemble.d.ts +50 -4
- package/dist/runtime/assemble.js +47 -3
- package/dist/runtime/boot.d.ts +3 -3
- package/dist/runtime/boot.js +22 -5
- package/dist/runtime/dbos.d.ts +4 -4
- package/dist/state/artifacts.d.ts +16 -0
- package/dist/state/artifacts.js +18 -0
- package/dist/state/init.js +77 -1
- package/dist/state/purge-analysis.js +1 -0
- package/dist/state/report-versions.d.ts +132 -0
- package/dist/state/report-versions.js +194 -0
- package/dist/state/schema.d.ts +2 -2
- package/dist/tasks/data-profile.js +1 -0
- package/dist/tools/bio/chembl.js +1 -0
- package/dist/tools/bio/comptox.js +1 -0
- package/dist/tools/bio/drug-gene-interactions.js +1 -0
- package/dist/tools/bio/gene-disease-evidence.js +1 -0
- package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -1
- package/dist/tools/bio/gene-preclinical-profile.js +1 -0
- package/dist/tools/bio/keys.d.ts +4 -2
- package/dist/tools/bio/keys.js +3 -2
- package/dist/tools/bio/lookup-annotation.js +1 -0
- package/dist/tools/bio/opentargets.d.ts +3 -3
- package/dist/tools/bio/opentargets.js +4 -3
- package/dist/tools/bio/pubchem.js +1 -0
- package/dist/tools/bio/pubmed.js +4 -4
- package/dist/tools/bio/search-clinical-trials.js +1 -0
- package/dist/tools/bio/search-faers.js +1 -0
- package/dist/tools/bio/search-geo-datasets.js +1 -0
- package/dist/tools/bio/search-interactions.js +1 -0
- package/dist/tools/bio/target-safety.d.ts +4 -6
- package/dist/tools/bio/target-safety.js +7 -6
- package/dist/tools/define-tool.d.ts +19 -12
- package/dist/tools/define-tool.js +4 -2
- package/dist/tools/display/show-user.js +1 -0
- package/dist/tools/iterate-report.js +2 -0
- package/dist/tools/lib/chembl-client.d.ts +2 -0
- package/dist/tools/lib/chembl-client.js +1 -0
- package/dist/tools/lib/monarch-client.d.ts +42 -0
- package/dist/tools/lib/monarch-client.js +100 -0
- package/dist/tools/lib/ncbi-utils.d.ts +3 -58
- package/dist/tools/lib/ncbi-utils.js +2 -286
- package/dist/tools/lib/pubmed-client.d.ts +5 -12
- package/dist/tools/lib/pubmed-client.js +20 -79
- package/dist/tools/report/build-report.js +1 -0
- package/dist/tools/report/mint-preview-url.js +1 -0
- package/dist/tools/report/preview-snapshot.js +1 -0
- package/dist/tools/report/submit-report.js +1 -0
- package/dist/tools/report/version-fs.js +4 -0
- package/dist/tools/report-authoring/authoring-tools.d.ts +134 -0
- package/dist/tools/report-authoring/authoring-tools.js +352 -0
- package/dist/tools/research/context7-docs.js +2 -0
- package/dist/tools/research/generate-analogy-report.js +3 -2
- package/dist/tools/research/generate-plan.js +3 -0
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.js +4 -0
- package/dist/tools/research/inspect-run.d.ts +1 -1
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +3 -0
- package/dist/tools/research/resolve-citation.d.ts +3 -0
- package/dist/tools/research/resolve-citation.js +15 -0
- package/dist/tools/research/search-arxiv.d.ts +3 -19
- package/dist/tools/research/search-arxiv.js +10 -105
- package/dist/tools/research/search-github-repos.js +1 -0
- package/dist/tools/research/search-semantic-scholar.d.ts +8 -33
- package/dist/tools/research/search-semantic-scholar.js +30 -88
- package/dist/tools/sandbox/list-available-packages.js +23 -0
- package/dist/tools/sandbox/list-available-refs.js +37 -2
- package/dist/tools/sandbox/report-blocker.js +1 -0
- package/dist/tools/sandbox/skills.js +2 -0
- package/dist/tools/workspace/file-stat.js +1 -0
- package/dist/tools/workspace/show-file.js +2 -0
- package/dist/tools/workspace/show-plan.js +1 -0
- package/dist/workflows/execute-analysis.d.ts +8 -1
- package/dist/workflows/execute-analysis.js +6 -5
- package/dist/workflows/execute-target-assessment.d.ts +13 -2
- package/dist/workflows/execute-target-assessment.js +78 -16
- package/dist/workflows/sandbox-step.d.ts +5 -0
- package/dist/workflows/sandbox-step.js +1 -0
- package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
- package/dist/workflows/target-assessment/assemblers/index.js +1 -1
- package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
- package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
- package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
- package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
- package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
- package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
- package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
- package/dist/workflows/target-assessment/collectors/index.js +48 -5
- package/dist/workflows/target-assessment/coverage.d.ts +43 -27
- package/dist/workflows/target-assessment/coverage.js +33 -29
- package/dist/workflows/target-assessment/fanout/index.js +1 -0
- package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
- package/dist/workflows/target-assessment/investigation/index.js +419 -0
- package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
- package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
- package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
- package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
- package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
- package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
- package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
- package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
- package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
- package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
- package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
- package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
- package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
- package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
- package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
- package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
- package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
- package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
- package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
- package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
- package/dist/workflows/target-assessment/metrics.d.ts +1 -6
- package/dist/workflows/target-assessment/metrics.js +1 -13
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +2149 -809
- package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
- package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
- package/dist/workflows/target-assessment/phase5-persist.js +56 -173
- package/dist/workflows/target-assessment/schemas.d.ts +48 -3
- package/dist/workflows/target-assessment/schemas.js +19 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +27 -63
- package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +28 -155
- package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
- package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
- package/dist/workflows/target-assessment/synthesis/index.js +4 -1
- package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
- package/package.json +4 -1
|
@@ -14,14 +14,18 @@
|
|
|
14
14
|
* `applicationVersion` stamp because all registration happens in this one call,
|
|
15
15
|
* before launch — a blue/green drain treats them as a single cohort.
|
|
16
16
|
*/
|
|
17
|
+
import { type Result } from "neverthrow";
|
|
17
18
|
import { type ConversationAgentDeps } from "../agents/conversation-agent.js";
|
|
18
19
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
19
20
|
import type { ResourcePolicy } from "../config/resource-limits.js";
|
|
20
21
|
import type { AgentDefinition } from "../loop/types.js";
|
|
22
|
+
import type { ThreadType } from "../memory/thread-store.js";
|
|
21
23
|
import { type ExecuteAnalysisDeps, type ExecuteAnalysisInput, type ExecuteAnalysisResult } from "../workflows/execute-analysis.js";
|
|
22
24
|
import { type SandboxStepDeps, type SandboxStepInput, type SandboxStepResult } from "../workflows/sandbox-step.js";
|
|
23
25
|
import { type ExecuteTargetAssessmentDeps, type ExecuteTargetAssessmentInput, type ExecuteTargetAssessmentResult } from "../workflows/execute-target-assessment.js";
|
|
24
26
|
import { type DataProfileDeps, type DataProfileWorkflowInput } from "../tasks/data-profile.js";
|
|
27
|
+
import { type CitationResolverConfig } from "../citations/resolve.js";
|
|
28
|
+
import type { CitationResolver } from "../citations/types.js";
|
|
25
29
|
/** Registered child sandbox-step callable the parent's child dispatch closes over. */
|
|
26
30
|
export type SandboxStepCallable = (input: SandboxStepInput) => Promise<SandboxStepResult>;
|
|
27
31
|
/**
|
|
@@ -38,8 +42,8 @@ export type SandboxStepCallable = (input: SandboxStepInput) => Promise<SandboxSt
|
|
|
38
42
|
* conversation side.
|
|
39
43
|
*/
|
|
40
44
|
export interface CoreWorkflowDeps {
|
|
41
|
-
readonly sandboxStep: Omit<SandboxStepDeps, "usageRecorder">;
|
|
42
|
-
readonly buildExecuteAnalysis: (sandboxStep: SandboxStepCallable) => Omit<ExecuteAnalysisDeps, "usageRecorder">;
|
|
45
|
+
readonly sandboxStep: Omit<SandboxStepDeps, "usageRecorder" | "citationResolver">;
|
|
46
|
+
readonly buildExecuteAnalysis: (sandboxStep: SandboxStepCallable) => Omit<ExecuteAnalysisDeps, "usageRecorder" | "citationResolver">;
|
|
43
47
|
readonly executeTargetAssessment: Omit<ExecuteTargetAssessmentDeps, "usageRecorder">;
|
|
44
48
|
readonly dataProfile: Omit<DataProfileDeps, "usageRecorder">;
|
|
45
49
|
}
|
|
@@ -56,7 +60,7 @@ export interface RegisteredWorkflows {
|
|
|
56
60
|
* cannot wire a stale callable, a policy that diverges from the one the
|
|
57
61
|
* workflows see, or a recorder that only half the agent tree reports to.
|
|
58
62
|
*/
|
|
59
|
-
export type ConversationAssemblyDeps = Omit<ConversationAgentDeps, "executeAnalysisWorkflow" | "resourcePolicy" | "usageRecorder">;
|
|
63
|
+
export type ConversationAssemblyDeps = Omit<ConversationAgentDeps, "executeAnalysisWorkflow" | "resourcePolicy" | "usageRecorder" | "citationResolver">;
|
|
60
64
|
export interface CoreRuntimeDeps {
|
|
61
65
|
readonly conversation: ConversationAssemblyDeps;
|
|
62
66
|
readonly workflows: CoreWorkflowDeps;
|
|
@@ -72,9 +76,51 @@ export interface CoreRuntimeDeps {
|
|
|
72
76
|
* `createNoopUsageRecorder()`.
|
|
73
77
|
*/
|
|
74
78
|
readonly usageRecorder?: UsageRecorder;
|
|
79
|
+
/** Harness-owned citation capability configuration; no ambient lookup occurs. */
|
|
80
|
+
readonly citationResolverConfig?: CitationResolverConfig;
|
|
75
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* The refusal a thread type carries no agent yet. `report` is a valid
|
|
84
|
+
* `ThreadType` with no registered agent until its builder plugs in at assembly,
|
|
85
|
+
* so resolution has to have a typed way to say "not that one" without throwing.
|
|
86
|
+
* The channel is permanent, not interim: `ThreadType` grows over the product's
|
|
87
|
+
* life, and a bare-`AgentDefinition` return would force every future member to
|
|
88
|
+
* register an agent in the same commit that adds the member.
|
|
89
|
+
*/
|
|
90
|
+
export interface UnregisteredThreadType {
|
|
91
|
+
readonly type: "unregistered_thread_type";
|
|
92
|
+
readonly threadType: ThreadType;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* How a thread's type selects the agent that runs its turns. Resolution is a
|
|
96
|
+
* synchronous record lookup, so the channel is plain `Result`, never
|
|
97
|
+
* `ResultAsync`: a registered type resolves to its assembled singleton, an
|
|
98
|
+
* unregistered one refuses on the error channel.
|
|
99
|
+
*/
|
|
100
|
+
export interface ThreadAgentResolver {
|
|
101
|
+
forThread(type: ThreadType): Result<AgentDefinition, UnregisteredThreadType>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Wrap a type→agent registry as the resolution surface. Held apart from
|
|
105
|
+
* `assembleCoreRuntime` so the resolution contract — a registered type resolves
|
|
106
|
+
* to the very object the registry holds, an unregistered one refuses — is
|
|
107
|
+
* exercisable without the DBOS registration `assembleCoreRuntime` performs.
|
|
108
|
+
*
|
|
109
|
+
* The registry holds assembled singletons, so `forThread` returns the same
|
|
110
|
+
* `AgentDefinition` object on every call for a given type: construction-time
|
|
111
|
+
* captures (an embedder's delegating provider handles, closure-wired tools)
|
|
112
|
+
* stay valid across every turn.
|
|
113
|
+
*/
|
|
114
|
+
export declare function createThreadAgentResolver(registry: Partial<Record<ThreadType, AgentDefinition>>): ThreadAgentResolver;
|
|
76
115
|
export interface CoreRuntime {
|
|
77
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Thread→agent resolution — the only way to reach an assembled agent by
|
|
118
|
+
* thread type. Holds the singletons this call built; `conversation` resolves
|
|
119
|
+
* to the assembled conversation agent, every not-yet-registered type refuses
|
|
120
|
+
* on the `Result` channel.
|
|
121
|
+
*/
|
|
122
|
+
readonly agents: ThreadAgentResolver;
|
|
78
123
|
readonly workflows: RegisteredWorkflows;
|
|
124
|
+
readonly citationResolver: CitationResolver;
|
|
79
125
|
}
|
|
80
126
|
export declare function assembleCoreRuntime(deps: CoreRuntimeDeps): CoreRuntime;
|
package/dist/runtime/assemble.js
CHANGED
|
@@ -14,17 +14,50 @@
|
|
|
14
14
|
* `applicationVersion` stamp because all registration happens in this one call,
|
|
15
15
|
* before launch — a blue/green drain treats them as a single cohort.
|
|
16
16
|
*/
|
|
17
|
+
import { err, ok } from "neverthrow";
|
|
17
18
|
import { createConversationAgent } from "../agents/conversation-agent.js";
|
|
18
19
|
import { createNoopUsageRecorder } from "../billing/noop-usage-recorder.js";
|
|
19
20
|
import { registerExecuteAnalysis } from "../workflows/execute-analysis.js";
|
|
20
21
|
import { registerSandboxStep } from "../workflows/sandbox-step.js";
|
|
21
22
|
import { registerExecuteTargetAssessment, } from "../workflows/execute-target-assessment.js";
|
|
22
23
|
import { registerDataProfileWorkflow } from "../tasks/data-profile.js";
|
|
24
|
+
import { createCitationResolver } from "../citations/resolve.js";
|
|
25
|
+
const _assertDomainError = true;
|
|
26
|
+
/**
|
|
27
|
+
* Wrap a type→agent registry as the resolution surface. Held apart from
|
|
28
|
+
* `assembleCoreRuntime` so the resolution contract — a registered type resolves
|
|
29
|
+
* to the very object the registry holds, an unregistered one refuses — is
|
|
30
|
+
* exercisable without the DBOS registration `assembleCoreRuntime` performs.
|
|
31
|
+
*
|
|
32
|
+
* The registry holds assembled singletons, so `forThread` returns the same
|
|
33
|
+
* `AgentDefinition` object on every call for a given type: construction-time
|
|
34
|
+
* captures (an embedder's delegating provider handles, closure-wired tools)
|
|
35
|
+
* stay valid across every turn.
|
|
36
|
+
*/
|
|
37
|
+
export function createThreadAgentResolver(registry) {
|
|
38
|
+
return {
|
|
39
|
+
forThread: (type) => {
|
|
40
|
+
const agent = registry[type];
|
|
41
|
+
if (agent === undefined) {
|
|
42
|
+
const refusal = { type: "unregistered_thread_type", threadType: type };
|
|
43
|
+
return err(refusal);
|
|
44
|
+
}
|
|
45
|
+
return ok(agent);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
23
49
|
export function assembleCoreRuntime(deps) {
|
|
24
50
|
const { conversation, workflows: wf, resourcePolicy } = deps;
|
|
25
51
|
const usageRecorder = deps.usageRecorder ?? createNoopUsageRecorder();
|
|
26
|
-
const
|
|
27
|
-
|
|
52
|
+
const citationResolver = createCitationResolver({
|
|
53
|
+
...(deps.citationResolverConfig ?? {}),
|
|
54
|
+
...(deps.citationResolverConfig?.ncbiApiKey !== undefined || conversation.bioKeys.ncbi === undefined ? {} : { ncbiApiKey: conversation.bioKeys.ncbi }),
|
|
55
|
+
...(deps.citationResolverConfig?.semanticScholarApiKey !== undefined || conversation.bioKeys.semanticScholar === undefined
|
|
56
|
+
? {}
|
|
57
|
+
: { semanticScholarApiKey: conversation.bioKeys.semanticScholar }),
|
|
58
|
+
});
|
|
59
|
+
const sandboxStep = registerSandboxStep({ ...wf.sandboxStep, citationResolver, usageRecorder });
|
|
60
|
+
const executeAnalysis = registerExecuteAnalysis({ ...wf.buildExecuteAnalysis(sandboxStep), citationResolver, usageRecorder });
|
|
28
61
|
const executeTargetAssessment = registerExecuteTargetAssessment({ ...wf.executeTargetAssessment, usageRecorder });
|
|
29
62
|
const dataProfile = registerDataProfileWorkflow({ ...wf.dataProfile, usageRecorder });
|
|
30
63
|
const conversationAgent = createConversationAgent({
|
|
@@ -32,14 +65,25 @@ export function assembleCoreRuntime(deps) {
|
|
|
32
65
|
executeAnalysisWorkflow: executeAnalysis,
|
|
33
66
|
resourcePolicy,
|
|
34
67
|
usageRecorder,
|
|
68
|
+
citationResolver,
|
|
35
69
|
});
|
|
70
|
+
// The single registration point for every typed agent. One entry today; a
|
|
71
|
+
// future thread type's agent plugs in here at assembly with no embedder
|
|
72
|
+
// change. `conversation` is required at the type level because boot resolves
|
|
73
|
+
// it unconditionally (`boot.ts` backfill) — a dropped registration must fail
|
|
74
|
+
// tsc here, not surface only as a boot-time throw. The `Partial` over the
|
|
75
|
+
// rest keeps the compiler honest that `report` has no agent yet.
|
|
76
|
+
const agents = {
|
|
77
|
+
conversation: conversationAgent,
|
|
78
|
+
};
|
|
36
79
|
return {
|
|
37
|
-
|
|
80
|
+
agents: createThreadAgentResolver(agents),
|
|
38
81
|
workflows: {
|
|
39
82
|
executeAnalysis,
|
|
40
83
|
sandboxStep,
|
|
41
84
|
executeTargetAssessment,
|
|
42
85
|
dataProfile,
|
|
43
86
|
},
|
|
87
|
+
citationResolver,
|
|
44
88
|
};
|
|
45
89
|
}
|
package/dist/runtime/boot.d.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* to a no-op so a library host never acquires process-wide telemetry (or a
|
|
12
12
|
* console banner) it did not ask for. An embedder that wants harness OTel
|
|
13
13
|
* passes `initOtel` / `shutdownOtel` here.
|
|
14
|
-
* 2. `validateAgentSkills(skillsDir,
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* 2. `validateAgentSkills(skillsDir, ...)` — pure fs stat, zero external deps.
|
|
15
|
+
* A `meta.skills` typo or a `skillsDir` / image drift dies in milliseconds,
|
|
16
|
+
* before any Postgres or DBOS cost is paid (agent-skill-assignment).
|
|
17
17
|
* 3. `initCortexState(pool)` — app tables must exist before launch; recovery
|
|
18
18
|
* queries them on the first step.
|
|
19
19
|
* 4. `assertConnectionBudget(...)` — needs the live pool; gates launch so a
|
package/dist/runtime/boot.js
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* to a no-op so a library host never acquires process-wide telemetry (or a
|
|
12
12
|
* console banner) it did not ask for. An embedder that wants harness OTel
|
|
13
13
|
* passes `initOtel` / `shutdownOtel` here.
|
|
14
|
-
* 2. `validateAgentSkills(skillsDir,
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* 2. `validateAgentSkills(skillsDir, ...)` — pure fs stat, zero external deps.
|
|
15
|
+
* A `meta.skills` typo or a `skillsDir` / image drift dies in milliseconds,
|
|
16
|
+
* before any Postgres or DBOS cost is paid (agent-skill-assignment).
|
|
17
17
|
* 3. `initCortexState(pool)` — app tables must exist before launch; recovery
|
|
18
18
|
* queries them on the first step.
|
|
19
19
|
* 4. `assertConnectionBudget(...)` — needs the live pool; gates launch so a
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
* releases whatever it acquired). Only `shutdown` swallows per-step failures —
|
|
37
37
|
* once shutdown starts, exiting takes precedence (see `runShutdownSequence`).
|
|
38
38
|
*/
|
|
39
|
+
import { unwrapOrThrow } from "../lib/result.js";
|
|
39
40
|
import { SANDBOX_AGENT_META } from "../agents/sandbox/index.js";
|
|
41
|
+
import { REPORT_BUILDER_AGENT_ID, REPORT_BUILDER_SKILLS } from "../agents/report-builder.js";
|
|
40
42
|
import { validateAgentSkills } from "../agents/sandbox/validate-skills.js";
|
|
41
43
|
import { initCortexState } from "../state/init.js";
|
|
42
44
|
import { backfillConversationDisplayEnvelopes } from "../memory/conversation-display-backfill.js";
|
|
@@ -47,17 +49,32 @@ import { markDraining } from "./lifecycle.js";
|
|
|
47
49
|
import { runShutdownSequence } from "./shutdown.js";
|
|
48
50
|
const noop = () => { };
|
|
49
51
|
const noopAsync = () => Promise.resolve();
|
|
52
|
+
/**
|
|
53
|
+
* Every agent whose declared packs must resolve before the first run. The
|
|
54
|
+
* report-builder is not in the sandbox catalog — it is not plannable — but it
|
|
55
|
+
* declares a pack all the same, and a missing one there surfaces as a failed
|
|
56
|
+
* render partway through a report rather than at boot.
|
|
57
|
+
*/
|
|
58
|
+
const SKILL_DECLARING_AGENTS = {
|
|
59
|
+
...SANDBOX_AGENT_META,
|
|
60
|
+
[REPORT_BUILDER_AGENT_ID]: { id: REPORT_BUILDER_AGENT_ID, skills: REPORT_BUILDER_SKILLS },
|
|
61
|
+
};
|
|
50
62
|
export async function bootHarness(deps) {
|
|
51
63
|
const { core, pool, skillsDir, logger } = deps;
|
|
52
64
|
(deps.initTelemetry ?? noop)();
|
|
53
|
-
validateAgentSkills(skillsDir,
|
|
65
|
+
validateAgentSkills(skillsDir, SKILL_DECLARING_AGENTS);
|
|
54
66
|
await initCortexState(pool);
|
|
55
67
|
await assertConnectionBudget({ pool, logger, config: deps.connectionBudget });
|
|
56
68
|
const runtime = assembleCoreRuntime(core);
|
|
69
|
+
// Assembly registers `conversation` unconditionally, so this resolution can
|
|
70
|
+
// never refuse; `unwrapOrThrow` turns the impossible error branch into a
|
|
71
|
+
// boot-propagating throw rather than threading a Result through the boot
|
|
72
|
+
// sequence, whose steps already fail by propagation.
|
|
73
|
+
const conversationAgent = unwrapOrThrow(runtime.agents.forThread("conversation"));
|
|
57
74
|
await backfillConversationDisplayEnvelopes({
|
|
58
75
|
pool,
|
|
59
76
|
resolveWorkspaceRoot: core.conversation.resolveWorkspaceRoot,
|
|
60
|
-
tools:
|
|
77
|
+
tools: conversationAgent.tools,
|
|
61
78
|
});
|
|
62
79
|
await deps.beforeLaunch?.();
|
|
63
80
|
await launchDbos({ config: deps.dbos, logger });
|
package/dist/runtime/dbos.d.ts
CHANGED
|
@@ -38,7 +38,8 @@ export interface DbosConfig {
|
|
|
38
38
|
*/
|
|
39
39
|
readonly logLevel?: string;
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
/** DBOS lifecycle facts a host's readiness probe reports on. */
|
|
42
|
+
export interface DbosState {
|
|
42
43
|
launched: boolean;
|
|
43
44
|
recoveryStarted: boolean;
|
|
44
45
|
}
|
|
@@ -85,9 +86,8 @@ export declare function sweepEphemeralWorkflows({ pool, logger: injectedLogger,
|
|
|
85
86
|
executorId: string;
|
|
86
87
|
}): Promise<void>;
|
|
87
88
|
/** Snapshot of DBOS lifecycle state — read by the readiness probe. */
|
|
88
|
-
export declare function dbosState():
|
|
89
|
+
export declare function dbosState(): DbosState;
|
|
89
90
|
/** Test hook: force-reset state without calling DBOS. Test-only. */
|
|
90
91
|
export declare function __resetDbosStateForTest(): void;
|
|
91
92
|
/** Test hook: mark launched without calling DBOS. Test-only. */
|
|
92
|
-
export declare function __setDbosStateForTest(next: Partial<
|
|
93
|
-
export {};
|
|
93
|
+
export declare function __setDbosStateForTest(next: Partial<DbosState>): void;
|
|
@@ -57,6 +57,22 @@ export interface StepArtifactRef {
|
|
|
57
57
|
* slice of it — and so the same call replays to the same rows.
|
|
58
58
|
*/
|
|
59
59
|
export declare function queryStepArtifactPaths(pool: Querier, resourceId: string, runId: string, stepId: string, limit: number): Promise<StepArtifactRef[]>;
|
|
60
|
+
/** One registered artifact of an analysis, as a reader of the full artifact set names it. */
|
|
61
|
+
export interface AnalysisArtifactRef {
|
|
62
|
+
/** Workspace-root-relative path (`runs/{runId}/{stepId}/output/de.csv`). */
|
|
63
|
+
path: string;
|
|
64
|
+
hash: string;
|
|
65
|
+
fileType: string | null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Every artifact of one analysis, ordered by path so the same call gives the same rows.
|
|
69
|
+
*
|
|
70
|
+
* The query applies no filter. An input row is a member, the same as a step output. A row that carries
|
|
71
|
+
* `unrecoverable_at` is a member too, because the artifact existed at the moment of this read. If the
|
|
72
|
+
* query drops such a row, a later refusal says that the artifact never existed, and that reason is
|
|
73
|
+
* false.
|
|
74
|
+
*/
|
|
75
|
+
export declare function queryAnalysisArtifacts(pool: Querier, analysisId: string): Promise<AnalysisArtifactRef[]>;
|
|
60
76
|
/** Count step-output artifacts produced by a run — for the run-completed card. */
|
|
61
77
|
export declare function countArtifactsForRun(pool: Querier, analysisId: string, runId: string): Promise<number>;
|
|
62
78
|
export declare function updateArtifactId(pool: Querier, resourceId: string, path: string, artifactId: string, fileType?: string | null): Promise<void>;
|
package/dist/state/artifacts.js
CHANGED
|
@@ -148,6 +148,24 @@ export async function queryStepArtifactPaths(pool, resourceId, runId, stepId, li
|
|
|
148
148
|
});
|
|
149
149
|
return result.rows.map((r) => ({ path: r.path, fileType: r.file_type ?? null }));
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Every artifact of one analysis, ordered by path so the same call gives the same rows.
|
|
153
|
+
*
|
|
154
|
+
* The query applies no filter. An input row is a member, the same as a step output. A row that carries
|
|
155
|
+
* `unrecoverable_at` is a member too, because the artifact existed at the moment of this read. If the
|
|
156
|
+
* query drops such a row, a later refusal says that the artifact never existed, and that reason is
|
|
157
|
+
* false.
|
|
158
|
+
*/
|
|
159
|
+
export async function queryAnalysisArtifacts(pool, analysisId) {
|
|
160
|
+
const result = await pool.query({
|
|
161
|
+
text: `SELECT path, hash, file_type
|
|
162
|
+
FROM cortex_artifacts
|
|
163
|
+
WHERE analysis_id = $1
|
|
164
|
+
ORDER BY path`,
|
|
165
|
+
values: [analysisId],
|
|
166
|
+
});
|
|
167
|
+
return result.rows.map((r) => ({ path: r.path, hash: r.hash, fileType: r.file_type ?? null }));
|
|
168
|
+
}
|
|
151
169
|
/** Count step-output artifacts produced by a run — for the run-completed card. */
|
|
152
170
|
export async function countArtifactsForRun(pool, analysisId, runId) {
|
|
153
171
|
const result = await pool.query({
|
package/dist/state/init.js
CHANGED
|
@@ -130,6 +130,37 @@ CREATE TABLE IF NOT EXISTS cortex_plans (
|
|
|
130
130
|
CREATE INDEX IF NOT EXISTS idx_cortex_plans_analysis
|
|
131
131
|
ON cortex_plans(analysis_id, created_at DESC);
|
|
132
132
|
|
|
133
|
+
-- Report versions — one immutable row for each recorded report version. A row
|
|
134
|
+
-- holds the block document, the pinned snapshot, and the anchor. A thread holds
|
|
135
|
+
-- at most one version. The named unique constraint on thread_id enforces the
|
|
136
|
+
-- rule, because the report policy binds one version to each report session. The
|
|
137
|
+
-- row outlives its thread. Thus the table has no foreign key to
|
|
138
|
+
-- cortex_analysis_threads, and the row carries the anchor columns (thread_id,
|
|
139
|
+
-- parent_thread_id, parent_seq) directly. A purge of the analysis removes the row
|
|
140
|
+
-- through the analysis_id cascade. The table is append-only, and a correction is
|
|
141
|
+
-- a new version, never an update to a recorded row.
|
|
142
|
+
CREATE TABLE IF NOT EXISTS cortex_report_versions (
|
|
143
|
+
version_id TEXT PRIMARY KEY,
|
|
144
|
+
analysis_id TEXT NOT NULL
|
|
145
|
+
REFERENCES cortex_analysis_state(analysis_id)
|
|
146
|
+
ON DELETE CASCADE,
|
|
147
|
+
thread_id TEXT NOT NULL,
|
|
148
|
+
-- Nullable pair: a root conversation anchor can be absent. A version with no
|
|
149
|
+
-- parent thread carries no parent_seq either.
|
|
150
|
+
parent_thread_id TEXT,
|
|
151
|
+
parent_seq BIGINT,
|
|
152
|
+
parent_version_id TEXT
|
|
153
|
+
REFERENCES cortex_report_versions(version_id)
|
|
154
|
+
ON DELETE SET NULL,
|
|
155
|
+
document JSONB NOT NULL,
|
|
156
|
+
snapshot JSONB NOT NULL,
|
|
157
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
158
|
+
CONSTRAINT cortex_report_versions_one_per_thread UNIQUE (thread_id)
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
CREATE INDEX IF NOT EXISTS idx_cortex_report_versions_analysis
|
|
162
|
+
ON cortex_report_versions(analysis_id);
|
|
163
|
+
|
|
133
164
|
-- Target assessments — organization-scoped, snapshot-style target dossiers.
|
|
134
165
|
-- Independent of analyses, projects, runs, and chat threads. The full
|
|
135
166
|
-- dossier is persisted as JSONB on the row, no separate artifacts table.
|
|
@@ -275,12 +306,57 @@ CREATE TABLE IF NOT EXISTS cortex_analysis_threads (
|
|
|
275
306
|
title TEXT,
|
|
276
307
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
277
308
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
278
|
-
deleted_at TIMESTAMPTZ
|
|
309
|
+
deleted_at TIMESTAMPTZ,
|
|
310
|
+
thread_type TEXT NOT NULL DEFAULT 'conversation',
|
|
311
|
+
-- The cascade on this self-reference is a BACKSTOP, never the deletion path.
|
|
312
|
+
-- messages carries no foreign key to this table, so a cascade firing on its
|
|
313
|
+
-- own drops a descendant's metadata row while leaving that descendant's
|
|
314
|
+
-- messages rows behind with nothing naming them, beyond the reach of any
|
|
315
|
+
-- later thread-scoped or analysis-scoped reclamation. purgeThread therefore
|
|
316
|
+
-- walks the subtree and deletes its messages rows explicitly, to the same
|
|
317
|
+
-- depth the cascade reaches.
|
|
318
|
+
parent_thread_id TEXT REFERENCES cortex_analysis_threads(thread_id) ON DELETE CASCADE,
|
|
319
|
+
parent_seq BIGINT
|
|
279
320
|
);
|
|
280
321
|
|
|
322
|
+
-- These three sit here, and not in the additive block further down, because the
|
|
323
|
+
-- index below reads one of them. CREATE TABLE IF NOT EXISTS adds no column to a
|
|
324
|
+
-- table that already exists, so on every database but a brand new one the column
|
|
325
|
+
-- arrives from an ALTER -- and the whole additive block runs after this text. An
|
|
326
|
+
-- index declared here over a column added there fails with 42703 on exactly the
|
|
327
|
+
-- databases a migration exists to serve. Additive with no backfill either way: an
|
|
328
|
+
-- existing row reads back as a root conversation thread with no anchor, which is
|
|
329
|
+
-- what it is.
|
|
330
|
+
ALTER TABLE cortex_analysis_threads
|
|
331
|
+
ADD COLUMN IF NOT EXISTS thread_type TEXT NOT NULL DEFAULT 'conversation';
|
|
332
|
+
|
|
333
|
+
ALTER TABLE cortex_analysis_threads
|
|
334
|
+
ADD COLUMN IF NOT EXISTS parent_thread_id TEXT REFERENCES cortex_analysis_threads(thread_id) ON DELETE CASCADE;
|
|
335
|
+
|
|
336
|
+
ALTER TABLE cortex_analysis_threads
|
|
337
|
+
ADD COLUMN IF NOT EXISTS parent_seq BIGINT;
|
|
338
|
+
|
|
281
339
|
CREATE INDEX IF NOT EXISTS idx_cortex_analysis_threads_analysis
|
|
282
340
|
ON cortex_analysis_threads(analysis_id) WHERE deleted_at IS NULL;
|
|
283
341
|
|
|
342
|
+
-- Not partial, unlike the analysis index above. Two of the three readers of
|
|
343
|
+
-- parent_thread_id carry no deleted_at predicate and cannot: the recursive
|
|
344
|
+
-- subtree walk in thread-store.ts must reach archived descendants, and the
|
|
345
|
+
-- referential trigger behind ON DELETE CASCADE is the database's own query.
|
|
346
|
+
-- Postgres uses a partial index only where it can prove the predicate holds, so
|
|
347
|
+
-- a partial index here leaves both to a sequential scan of the whole table.
|
|
348
|
+
-- Over 150k rows that measured 28 ms per walk and 20 ms per cascade, against
|
|
349
|
+
-- 0.06 ms and 0.10 ms with this one. The third reader, a listing filtered by
|
|
350
|
+
-- parent, keeps an index scan either way.
|
|
351
|
+
--
|
|
352
|
+
-- The old partial index goes by name: CREATE INDEX IF NOT EXISTS keeps whatever
|
|
353
|
+
-- definition already holds the name, so a database that built the partial one
|
|
354
|
+
-- would never see this.
|
|
355
|
+
DROP INDEX IF EXISTS idx_cortex_analysis_threads_parent;
|
|
356
|
+
|
|
357
|
+
CREATE INDEX IF NOT EXISTS idx_cortex_analysis_threads_parent_fk
|
|
358
|
+
ON cortex_analysis_threads(parent_thread_id);
|
|
359
|
+
|
|
284
360
|
-- Working memory — the harness WorkingMemory module's table, one row per
|
|
285
361
|
-- analysis. The data column holds a JSONB object with four sections (goal,
|
|
286
362
|
-- constraints, hypotheses, findings). Section updates are a read-modify-write
|
|
@@ -80,6 +80,7 @@ const ANALYSIS_KEYED_DELETES = [
|
|
|
80
80
|
{ op: "purgeAnalysis.asks", sql: "DELETE FROM cortex_asks WHERE analysis_id = $1" },
|
|
81
81
|
{ op: "purgeAnalysis.askGrants", sql: "DELETE FROM cortex_ask_grants WHERE analysis_id = $1" },
|
|
82
82
|
{ op: "purgeAnalysis.plans", sql: "DELETE FROM cortex_plans WHERE analysis_id = $1" },
|
|
83
|
+
{ op: "purgeAnalysis.reportVersions", sql: "DELETE FROM cortex_report_versions WHERE analysis_id = $1" },
|
|
83
84
|
{ op: "purgeAnalysis.analysisState", sql: "DELETE FROM cortex_analysis_state WHERE analysis_id = $1" },
|
|
84
85
|
];
|
|
85
86
|
/**
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report version store -- the append-only record of report versions.
|
|
3
|
+
*
|
|
4
|
+
* A version is one immutable row. It holds the block document, the pinned
|
|
5
|
+
* snapshot, and the anchor. The store records a version and reads a version. It
|
|
6
|
+
* never updates a recorded row, thus a correction is a new version.
|
|
7
|
+
*
|
|
8
|
+
* A thread holds at most one version, and a named unique constraint on the thread
|
|
9
|
+
* id enforces it. The record reads nothing before the insert. A second record for
|
|
10
|
+
* one thread trips the constraint, and the store maps that trip to the typed
|
|
11
|
+
* refusal thread_already_holds_version.
|
|
12
|
+
*
|
|
13
|
+
* The store keeps the snapshot and the anchor as given, and it never mints a
|
|
14
|
+
* snapshot. A read parses the stored document and the stored snapshot with the
|
|
15
|
+
* current schemas. A row that fails a parse reads as a typed error, and it does
|
|
16
|
+
* not crash. An absent row reads as a normal absence.
|
|
17
|
+
*
|
|
18
|
+
* A version outlives its thread. The anchor columns sit on the row, thus a
|
|
19
|
+
* deleted thread leaves a recorded version in place. Only a purge of the analysis
|
|
20
|
+
* removes the version, through the analysis-id cascade.
|
|
21
|
+
*/
|
|
22
|
+
import { type ResultAsync } from "neverthrow";
|
|
23
|
+
import type { Pool } from "pg";
|
|
24
|
+
import { type ReportDocument } from "../contracts/report-blocks.js";
|
|
25
|
+
import { type DbError } from "../lib/db-result.js";
|
|
26
|
+
import type { Logger } from "../lib/logger.js";
|
|
27
|
+
import type { ReportSnapshot } from "../report-model/reference-resolver.js";
|
|
28
|
+
/** One reduced schema issue -- the dotted path and the message, without the rest. */
|
|
29
|
+
export interface SchemaIssue {
|
|
30
|
+
readonly path: string;
|
|
31
|
+
readonly message: string;
|
|
32
|
+
}
|
|
33
|
+
/** The anchor and the payload of a version to record. */
|
|
34
|
+
export interface RecordVersionInput {
|
|
35
|
+
/** The block document. It rides as `unknown`, and the record parses it. */
|
|
36
|
+
readonly document: unknown;
|
|
37
|
+
/** The pinned snapshot, stored as given. */
|
|
38
|
+
readonly snapshot: ReportSnapshot;
|
|
39
|
+
readonly analysisId: string;
|
|
40
|
+
readonly threadId: string;
|
|
41
|
+
/** The parent conversation thread of the anchor, or `null` when a root has none. */
|
|
42
|
+
readonly parentThreadId: string | null;
|
|
43
|
+
/** The parent transcript position of the anchor, or `null` beside a null parent thread. */
|
|
44
|
+
readonly parentSeq: number | null;
|
|
45
|
+
/** The earlier version that this one reuses. The parent must belong to the same analysis. */
|
|
46
|
+
readonly parentVersionId?: string;
|
|
47
|
+
}
|
|
48
|
+
/** The stable reference of a freshly recorded version. */
|
|
49
|
+
export interface RecordedVersionRef {
|
|
50
|
+
readonly versionId: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A record the store refuses as data, before or in place of a row. Distinct from
|
|
54
|
+
* `DbError`: the request describes a version that the store will not write.
|
|
55
|
+
*
|
|
56
|
+
* The store parses the document and the snapshot before any row. The store
|
|
57
|
+
* refuses a malformed document or a malformed snapshot here as typed data, and
|
|
58
|
+
* no row lands.
|
|
59
|
+
*
|
|
60
|
+
* A second record for one thread is `thread_already_holds_version`. The unique
|
|
61
|
+
* constraint on the thread id trips, and the record maps that one constraint by
|
|
62
|
+
* its name. Every other constraint trip stays a `DbError`.
|
|
63
|
+
*
|
|
64
|
+
* A `parentVersionId` naming no row is not one of these -- the self foreign key
|
|
65
|
+
* refuses an unknown parent id, and `tryMutation` classifies that refusal as the
|
|
66
|
+
* `constraint_violation` variant of `DbError`.
|
|
67
|
+
*/
|
|
68
|
+
export type RecordVersionError = {
|
|
69
|
+
readonly type: "malformed_document";
|
|
70
|
+
readonly op: string;
|
|
71
|
+
readonly issues: readonly SchemaIssue[];
|
|
72
|
+
} | {
|
|
73
|
+
readonly type: "malformed_snapshot";
|
|
74
|
+
readonly op: string;
|
|
75
|
+
readonly issues: readonly SchemaIssue[];
|
|
76
|
+
} | {
|
|
77
|
+
readonly type: "parent_analysis_mismatch";
|
|
78
|
+
readonly op: string;
|
|
79
|
+
readonly analysisId: string;
|
|
80
|
+
readonly parentVersionId: string;
|
|
81
|
+
readonly parentAnalysisId: string;
|
|
82
|
+
} | {
|
|
83
|
+
readonly type: "thread_already_holds_version";
|
|
84
|
+
readonly op: string;
|
|
85
|
+
readonly threadId: string;
|
|
86
|
+
} | DbError;
|
|
87
|
+
/**
|
|
88
|
+
* A stored row that a read cannot parse with the current schemas. It names the
|
|
89
|
+
* version id and the part that failed, and it carries the reduced issues.
|
|
90
|
+
*/
|
|
91
|
+
export type VersionReadError = {
|
|
92
|
+
readonly type: "corrupt_version";
|
|
93
|
+
readonly op: string;
|
|
94
|
+
readonly versionId: string;
|
|
95
|
+
readonly part: "document" | "snapshot";
|
|
96
|
+
readonly issues: readonly SchemaIssue[];
|
|
97
|
+
};
|
|
98
|
+
/** One recorded version, as the store gives it back. */
|
|
99
|
+
export interface RecordedVersion {
|
|
100
|
+
readonly versionId: string;
|
|
101
|
+
readonly analysisId: string;
|
|
102
|
+
readonly threadId: string;
|
|
103
|
+
readonly parentThreadId: string | null;
|
|
104
|
+
readonly parentSeq: number | null;
|
|
105
|
+
/** The earlier version that this one reuses, or `null` when it has none. */
|
|
106
|
+
readonly parentVersionId: string | null;
|
|
107
|
+
readonly document: ReportDocument;
|
|
108
|
+
readonly snapshot: ReportSnapshot;
|
|
109
|
+
readonly createdAt: Date;
|
|
110
|
+
}
|
|
111
|
+
export interface ReportVersionStore {
|
|
112
|
+
/**
|
|
113
|
+
* Record a version. The record parses the document, refuses a malformed value
|
|
114
|
+
* as data with no row, and refuses a parent from a different analysis. It reads
|
|
115
|
+
* nothing before the insert. A second record for one thread refuses with
|
|
116
|
+
* thread_already_holds_version. The snapshot and the anchor store as given.
|
|
117
|
+
*/
|
|
118
|
+
record(input: RecordVersionInput): ResultAsync<RecordedVersionRef, RecordVersionError>;
|
|
119
|
+
/** One version by its id, or `null` when no row holds it. */
|
|
120
|
+
getVersion(versionId: string): ResultAsync<RecordedVersion | null, DbError | VersionReadError>;
|
|
121
|
+
/** The one version of a thread, or `null` for a thread with no version. */
|
|
122
|
+
getThreadVersion(threadId: string): ResultAsync<RecordedVersion | null, DbError | VersionReadError>;
|
|
123
|
+
}
|
|
124
|
+
export interface ReportVersionStoreDeps {
|
|
125
|
+
readonly pool: Pool;
|
|
126
|
+
readonly logger?: Logger;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create a `ReportVersionStore` bound to a Postgres pool. The
|
|
130
|
+
* `cortex_report_versions` table is provisioned by the state-init DDL.
|
|
131
|
+
*/
|
|
132
|
+
export declare function createReportVersionStore({ pool }: ReportVersionStoreDeps): ReportVersionStore;
|