@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
|
@@ -41,6 +41,7 @@ import type { RunLauncher } from "../execution/run-launcher.js";
|
|
|
41
41
|
import { type SubmitReportDeps } from "../tools/iterate-report.js";
|
|
42
42
|
import type { Logger } from "../lib/logger.js";
|
|
43
43
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
44
|
+
import type { CitationResolver } from "../citations/types.js";
|
|
44
45
|
/** Canonical agent id — the single source of truth. */
|
|
45
46
|
export declare const CONVERSATION_AGENT_ID: "conversation-agent";
|
|
46
47
|
/**
|
|
@@ -58,6 +59,8 @@ export interface ConversationAgentDeps extends EnvironmentStorePaths {
|
|
|
58
59
|
* Omitted falls back to the no-op recorder.
|
|
59
60
|
*/
|
|
60
61
|
readonly usageRecorder?: UsageRecorder;
|
|
62
|
+
/** Shared host-side bibliographic verification service. */
|
|
63
|
+
readonly citationResolver: CitationResolver;
|
|
61
64
|
/** The LLM seam every loop-driving tool runs its sub-agent on. */
|
|
62
65
|
readonly provider: ChatProvider;
|
|
63
66
|
/** Postgres pool — plan persistence, run inspection, workspace index, working memory. */
|
|
@@ -40,13 +40,14 @@ import { createListAvailablePackagesTool } from "../tools/sandbox/list-available
|
|
|
40
40
|
import { createListAvailableRefsTool } from "../tools/sandbox/list-available-refs.js";
|
|
41
41
|
import { createExecuteAnalysisTool } from "../tools/execute-analysis.js";
|
|
42
42
|
import { planReportTool, createReportSubmitTool } from "../tools/iterate-report.js";
|
|
43
|
+
import { createResolveCitationTool } from "../tools/research/resolve-citation.js";
|
|
43
44
|
/** Canonical agent id — the single source of truth. */
|
|
44
45
|
export const CONVERSATION_AGENT_ID = "conversation-agent";
|
|
45
46
|
/** Runaway guard — heavy tool-driving turns need generous headroom. */
|
|
46
47
|
const CONVERSATION_MAX_ITERATIONS = 50;
|
|
47
48
|
/** Build the conversation `AgentDefinition` with every tool fully dep-bound. */
|
|
48
49
|
export function createConversationAgent(deps) {
|
|
49
|
-
const { provider, pool, embedding, workspaceFs, model, utilityProvider, utilityModel, executeAnalysisWorkflow, resolveWorkspaceRoot, runAuthorizer, runLauncher, createPreviewPublisher, bioKeys, templatesDir, skillsDir, chrome, resourcePolicy, hostTools, refStorePath, packagesFile, usageRecorder, } = deps;
|
|
50
|
+
const { provider, pool, embedding, workspaceFs, model, utilityProvider, utilityModel, executeAnalysisWorkflow, resolveWorkspaceRoot, runAuthorizer, runLauncher, createPreviewPublisher, bioKeys, templatesDir, skillsDir, chrome, resourcePolicy, hostTools, refStorePath, packagesFile, usageRecorder, citationResolver, } = deps;
|
|
50
51
|
const workingMemory = createWorkingMemory(pool);
|
|
51
52
|
const ncbi = createNcbiTools(bioKeys);
|
|
52
53
|
const chemDb = createChemDbTools(bioKeys, { ...(deps.logger ? { logger: deps.logger } : {}) });
|
|
@@ -59,6 +60,8 @@ export function createConversationAgent(deps) {
|
|
|
59
60
|
searchInteractionsTool,
|
|
60
61
|
// Literature (search / details / fulltext behind one action).
|
|
61
62
|
ncbi.pubmed,
|
|
63
|
+
// Verification of one caller-supplied citation (distinct from discovery).
|
|
64
|
+
createResolveCitationTool(citationResolver),
|
|
62
65
|
// ChEMBL (compounds / drug / mechanism / bioactivity / targets behind one action).
|
|
63
66
|
chemblTool,
|
|
64
67
|
// PubChem (compound / crossrefs / assays behind one action).
|
|
@@ -132,7 +135,7 @@ export function createConversationAgent(deps) {
|
|
|
132
135
|
createShowPlanTool(pool),
|
|
133
136
|
showFileTool,
|
|
134
137
|
// Batch literature/biology research (sub-agent as a loop-driving tool).
|
|
135
|
-
createLiteratureReviewerTool({ provider, model, bioKeys, usageRecorder }),
|
|
138
|
+
createLiteratureReviewerTool({ provider, model, bioKeys, citationResolver, usageRecorder }),
|
|
136
139
|
// Cross-domain analogy generation (sub-agent as a loop-driving tool).
|
|
137
140
|
createGenerateAnalogyReportTool({ provider, model, bioKeys, usageRecorder }),
|
|
138
141
|
// Workspace semantic search + raw read/grep over the read seam.
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
import type { AgentDefinition } from "../loop/types.js";
|
|
20
20
|
import type { Tool } from "../tools/define-tool.js";
|
|
21
21
|
export declare const REPORT_BUILDER_AGENT_ID = "report-builder";
|
|
22
|
+
/** Skill pack the report-builder prompt directs the model to read (design-system reference). */
|
|
23
|
+
export declare const REPORT_BUILDER_SKILLS: readonly ["report-html"];
|
|
22
24
|
export interface CreateReportBuilderAgentDeps {
|
|
23
25
|
/** Model id — provenance / metric label; the provider owns the wire model. */
|
|
24
26
|
readonly model: string;
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
import { reportBuilderPrompt } from "../prompts/report-builder.js";
|
|
20
20
|
import { composeSystemPrompt } from "./system-prompt.js";
|
|
21
21
|
export const REPORT_BUILDER_AGENT_ID = "report-builder";
|
|
22
|
+
/** Skill pack the report-builder prompt directs the model to read (design-system reference). */
|
|
23
|
+
export const REPORT_BUILDER_SKILLS = ["report-html"];
|
|
22
24
|
/** Runaway guard — Jinja recovery may take several build/edit cycles. */
|
|
23
25
|
const REPORT_BUILDER_MAX_ITERATIONS = 75;
|
|
24
26
|
export function createReportBuilderAgent(deps) {
|
|
@@ -27,6 +27,7 @@ import { type BlockerHolder } from "../../tools/sandbox/report-blocker.js";
|
|
|
27
27
|
import type { AgentMeta, SandboxToolName } from "./types.js";
|
|
28
28
|
import type { EnvironmentStorePaths } from "../../config/environment-stores.js";
|
|
29
29
|
import type { Logger } from "../../lib/logger.js";
|
|
30
|
+
import type { CitationResolver } from "../../citations/types.js";
|
|
30
31
|
/**
|
|
31
32
|
* Tools every sandbox agent receives — sandbox-environment introspection,
|
|
32
33
|
* library docs, and run inspection. Spread into each agent's
|
|
@@ -74,6 +75,8 @@ export interface SandboxAgentDeps extends EnvironmentStorePaths {
|
|
|
74
75
|
readonly step: SandboxStepCoords;
|
|
75
76
|
/** API keys for the external bio/chem data sources. */
|
|
76
77
|
readonly bioKeys: BioToolKeys;
|
|
78
|
+
/** Shared resolver. Required only when `meta.tools` declares `resolve_citation`. */
|
|
79
|
+
readonly citationResolver?: CitationResolver;
|
|
77
80
|
/**
|
|
78
81
|
* Per-run blocker cell (see the harness-sandbox-agents spec). When present, the agent gets a
|
|
79
82
|
* `report_blocker` tool that records `{ kind: "blocker", reason }` into it;
|
|
@@ -38,6 +38,7 @@ import { createReportBlockerTool } from "../../tools/sandbox/report-blocker.js";
|
|
|
38
38
|
import { toSandboxPath } from "../../workspace/paths.js";
|
|
39
39
|
import { setActiveExecId } from "../../state/index.js";
|
|
40
40
|
import { SANDBOX_AGENT_DEFAULT_MAX_ITERATIONS } from "./types.js";
|
|
41
|
+
import { createResolveCitationTool } from "../../tools/research/resolve-citation.js";
|
|
41
42
|
/**
|
|
42
43
|
* Tools every sandbox agent receives — sandbox-environment introspection,
|
|
43
44
|
* library docs, and run inspection. Spread into each agent's
|
|
@@ -53,6 +54,9 @@ export const BASE_SANDBOX_TOOLS = ["listAvailablePackages", "listAvailableRefs",
|
|
|
53
54
|
function resolveSandboxTools(deps, tools) {
|
|
54
55
|
const ncbi = createNcbiTools(deps.bioKeys);
|
|
55
56
|
const chemDb = createChemDbTools(deps.bioKeys, { ...(deps.logger ? { logger: deps.logger } : {}) });
|
|
57
|
+
if (tools.includes("resolve_citation") && deps.citationResolver === undefined) {
|
|
58
|
+
throw new Error('createSandboxAgent: SandboxToolName "resolve_citation" requires a CitationResolver dependency.');
|
|
59
|
+
}
|
|
56
60
|
const registry = {
|
|
57
61
|
listAvailablePackages: createListAvailablePackagesTool({ ...(deps.packagesFile ? { packagesFile: deps.packagesFile } : {}) }),
|
|
58
62
|
listAvailableRefs: createListAvailableRefsTool({ ...(deps.refStorePath ? { refStorePath: deps.refStorePath } : {}) }),
|
|
@@ -60,6 +64,7 @@ function resolveSandboxTools(deps, tools) {
|
|
|
60
64
|
queryDocs: queryDocsTool,
|
|
61
65
|
inspectRun: createInspectRunTool(deps.pool),
|
|
62
66
|
pubmed: ncbi.pubmed,
|
|
67
|
+
resolve_citation: deps.citationResolver === undefined ? undefined : createResolveCitationTool(deps.citationResolver),
|
|
63
68
|
searchGene: searchGeneTool,
|
|
64
69
|
lookupAnnotation: lookupAnnotationTool,
|
|
65
70
|
searchInteractions: searchInteractionsTool,
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* in the tool's `perSource` report; the rest of the call still answers.
|
|
13
13
|
*/
|
|
14
14
|
/** Closed allowlist of tools any sandbox agent may declare. */
|
|
15
|
-
export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "searchGene" | "lookupAnnotation" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "geneDiseaseEvidence" | "drugGeneInteractions" | "genePreclinicalProfile" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "targetSafety" | "comptox";
|
|
15
|
+
export type SandboxToolName = "listAvailablePackages" | "listAvailableRefs" | "resolveLibraryId" | "queryDocs" | "inspectRun" | "pubmed" | "resolve_citation" | "searchGene" | "lookupAnnotation" | "searchInteractions" | "chembl" | "pubchem" | "opentargets" | "geneDiseaseEvidence" | "drugGeneInteractions" | "genePreclinicalProfile" | "searchFaers" | "searchClinicalTrials" | "searchGeoDatasets" | "targetSafety" | "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`. */
|
package/dist/app/chat-turn.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* assembled `messages` and the standalone `userMessage` the caller persists.
|
|
14
14
|
*/
|
|
15
15
|
import type { Pool } from "pg";
|
|
16
|
+
import { type ThreadType } from "../memory/thread-store.js";
|
|
16
17
|
import { type AssembledMessages } from "./message-assembly.js";
|
|
17
18
|
import type { Logger } from "../lib/logger.js";
|
|
18
19
|
export interface PrepareChatTurnDeps {
|
|
@@ -27,6 +28,7 @@ export interface PrepareChatTurnParams {
|
|
|
27
28
|
}
|
|
28
29
|
export type PrepareChatTurnResult = ({
|
|
29
30
|
readonly kind: "ok";
|
|
31
|
+
readonly threadType: ThreadType;
|
|
30
32
|
} & AssembledMessages) | {
|
|
31
33
|
readonly kind: "not_found";
|
|
32
34
|
};
|
package/dist/app/chat-turn.js
CHANGED
|
@@ -36,14 +36,23 @@ export async function prepareChatTurn(deps, params) {
|
|
|
36
36
|
if (existing && existing.analysisId !== analysisId) {
|
|
37
37
|
return { kind: "not_found" };
|
|
38
38
|
}
|
|
39
|
+
// The type a caller resolves the turn's agent from. An existing row carries
|
|
40
|
+
// its own. An absent one defaults to `conversation` — the store's own
|
|
41
|
+
// default — so a best-effort create that fails non-fatally still leaves a
|
|
42
|
+
// usable type; a successful create overrides it from the returned row.
|
|
43
|
+
let threadType = existing ? existing.threadType : "conversation";
|
|
39
44
|
// Seed the thread title from the first user message. Best-effort.
|
|
40
45
|
try {
|
|
41
46
|
if (!existing) {
|
|
42
|
-
|
|
47
|
+
// `createThread` is idempotent (ON CONFLICT reads the row back), so
|
|
48
|
+
// the returned row is authoritative for the type: a create that
|
|
49
|
+
// races another writer reflects the stored type, not an assumed one.
|
|
50
|
+
const created = unwrapOrThrow(await store.createThread({
|
|
43
51
|
threadId,
|
|
44
52
|
analysisId,
|
|
45
53
|
title: deriveThreadTitle(userInput),
|
|
46
54
|
}));
|
|
55
|
+
threadType = created.threadType;
|
|
47
56
|
}
|
|
48
57
|
else if (!existing.title || existing.title.length === 0) {
|
|
49
58
|
unwrapOrThrow(await store.updateTitle(threadId, deriveThreadTitle(userInput)));
|
|
@@ -65,5 +74,5 @@ export async function prepareChatTurn(deps, params) {
|
|
|
65
74
|
history,
|
|
66
75
|
workingMemory: createWorkingMemory(pool),
|
|
67
76
|
});
|
|
68
|
-
return { kind: "ok", messages, userMessage };
|
|
77
|
+
return { kind: "ok", threadType, messages, userMessage };
|
|
69
78
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report-session spawn — the host-agnostic operation that makes a `report`
|
|
3
|
+
* child thread from a conversation.
|
|
4
|
+
*
|
|
5
|
+
* The operation owns no table. It composes four reads and writes the store and
|
|
6
|
+
* the thread history already give: `getThread` for the parent, `latestSeq` for
|
|
7
|
+
* the anchor, `listThreads` for the count of report children, and
|
|
8
|
+
* `createThread` for the insert. The one storage read this capability adds,
|
|
9
|
+
* `latestSeq`, lives on the thread history, because that module owns the
|
|
10
|
+
* `messages` table.
|
|
11
|
+
*
|
|
12
|
+
* The reads and the insert take no lock and no transaction. A concurrent turn
|
|
13
|
+
* can append between the anchor read and the insert, and a retract can cut the
|
|
14
|
+
* parent's tail. A transaction cannot keep the parent still one turn later, so
|
|
15
|
+
* a lock buys no real guarantee. The anchor records the spawn point, and skew
|
|
16
|
+
* past the parent's current end is a normal state a reader expects.
|
|
17
|
+
*
|
|
18
|
+
* A conversation thread id comes from the host UI. But the spawn is a harness
|
|
19
|
+
* operation with no UI in front of it. Thus it mints a `randomUUID` and returns
|
|
20
|
+
* the full row. A managed deployment gets the same behavior with no host code.
|
|
21
|
+
*/
|
|
22
|
+
import { type ResultAsync } from "neverthrow";
|
|
23
|
+
import type { Pool } from "pg";
|
|
24
|
+
import type { DbError } from "../lib/db-result.js";
|
|
25
|
+
import { type Thread, type ThreadInputError, type ThreadPage, type ThreadType } from "../memory/thread-store.js";
|
|
26
|
+
/**
|
|
27
|
+
* A spawn the operation refuses before it writes a row. Distinct from
|
|
28
|
+
* `DbError`: nothing failed at the driver — the parent names a session the
|
|
29
|
+
* spawn refuses, and each variant carries the identifiers that say which values
|
|
30
|
+
* disagreed.
|
|
31
|
+
*
|
|
32
|
+
* The set is closed. `parent_not_found` covers an absent parent and an archived
|
|
33
|
+
* one alike, because `getThread` filters the tombstone and a spawn into hidden
|
|
34
|
+
* state is not permitted. `parent_not_a_conversation` keeps the tree flat: a
|
|
35
|
+
* report session cannot spawn another. `empty_parent_transcript` refuses a
|
|
36
|
+
* report on a parent that holds no messages, because such a report reports
|
|
37
|
+
* nothing.
|
|
38
|
+
*/
|
|
39
|
+
export type SpawnRefusal = {
|
|
40
|
+
readonly type: "parent_not_found";
|
|
41
|
+
readonly op: string;
|
|
42
|
+
readonly parentThreadId: string;
|
|
43
|
+
} | {
|
|
44
|
+
readonly type: "parent_not_a_conversation";
|
|
45
|
+
readonly op: string;
|
|
46
|
+
readonly parentThreadId: string;
|
|
47
|
+
readonly threadType: ThreadType;
|
|
48
|
+
} | {
|
|
49
|
+
readonly type: "empty_parent_transcript";
|
|
50
|
+
readonly op: string;
|
|
51
|
+
readonly parentThreadId: string;
|
|
52
|
+
};
|
|
53
|
+
/** The paging inputs `listReportSessions` forwards to the thread listing. */
|
|
54
|
+
export interface ReportSessionPaging {
|
|
55
|
+
readonly page?: number;
|
|
56
|
+
readonly perPage?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface ReportSessionSpawnDeps {
|
|
59
|
+
readonly pool: Pool;
|
|
60
|
+
}
|
|
61
|
+
export interface ReportSessionSpawn {
|
|
62
|
+
/**
|
|
63
|
+
* Make a `report` child of the parent conversation and return the full row.
|
|
64
|
+
* The child takes the analysis of the parent, the parent thread id, and the
|
|
65
|
+
* anchor — the parent's latest `messages.seq` at this moment.
|
|
66
|
+
*
|
|
67
|
+
* Refused with a `SpawnRefusal`, no row written: an absent or archived
|
|
68
|
+
* parent, a parent that is not a conversation, and a parent with no
|
|
69
|
+
* messages. A store refusal (`DbError`, `ThreadInputError`) passes through
|
|
70
|
+
* unchanged.
|
|
71
|
+
*/
|
|
72
|
+
spawnReportSession(parentThreadId: string): ResultAsync<Thread, SpawnRefusal | DbError | ThreadInputError>;
|
|
73
|
+
/**
|
|
74
|
+
* The report sessions of one analysis, through the thread listing narrowed
|
|
75
|
+
* to the type `report`. It adds no predicate of its own, so its answer and
|
|
76
|
+
* the thread listing cannot disagree.
|
|
77
|
+
*/
|
|
78
|
+
listReportSessions(analysisId: string, paging?: ReportSessionPaging): ResultAsync<ThreadPage, DbError>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Build the report-session operations bound to a Postgres pool. The factory
|
|
82
|
+
* closure captures `pool` and constructs the store and the thread history from
|
|
83
|
+
* it, the same way the chat-turn preparation does.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createReportSessionSpawn(deps: ReportSessionSpawnDeps): ReportSessionSpawn;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report-session spawn — the host-agnostic operation that makes a `report`
|
|
3
|
+
* child thread from a conversation.
|
|
4
|
+
*
|
|
5
|
+
* The operation owns no table. It composes four reads and writes the store and
|
|
6
|
+
* the thread history already give: `getThread` for the parent, `latestSeq` for
|
|
7
|
+
* the anchor, `listThreads` for the count of report children, and
|
|
8
|
+
* `createThread` for the insert. The one storage read this capability adds,
|
|
9
|
+
* `latestSeq`, lives on the thread history, because that module owns the
|
|
10
|
+
* `messages` table.
|
|
11
|
+
*
|
|
12
|
+
* The reads and the insert take no lock and no transaction. A concurrent turn
|
|
13
|
+
* can append between the anchor read and the insert, and a retract can cut the
|
|
14
|
+
* parent's tail. A transaction cannot keep the parent still one turn later, so
|
|
15
|
+
* a lock buys no real guarantee. The anchor records the spawn point, and skew
|
|
16
|
+
* past the parent's current end is a normal state a reader expects.
|
|
17
|
+
*
|
|
18
|
+
* A conversation thread id comes from the host UI. But the spawn is a harness
|
|
19
|
+
* operation with no UI in front of it. Thus it mints a `randomUUID` and returns
|
|
20
|
+
* the full row. A managed deployment gets the same behavior with no host code.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID } from "node:crypto";
|
|
23
|
+
import { errAsync } from "neverthrow";
|
|
24
|
+
import { createThreadHistory } from "../memory/thread-history.js";
|
|
25
|
+
import { createThreadStore } from "../memory/thread-store.js";
|
|
26
|
+
const _assertDomainError = true;
|
|
27
|
+
const OP = "spawn-report-session";
|
|
28
|
+
/**
|
|
29
|
+
* Compose the child title. N counts the existing report children of the parent
|
|
30
|
+
* plus one. A parent with no title yields `Report N` alone, because the seed of
|
|
31
|
+
* the parent title is best-effort and can be absent.
|
|
32
|
+
*/
|
|
33
|
+
function composeTitle(parentTitle, n) {
|
|
34
|
+
const suffix = `Report ${n}`;
|
|
35
|
+
return parentTitle && parentTitle.length > 0 ? `${parentTitle} — ${suffix}` : suffix;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the report-session operations bound to a Postgres pool. The factory
|
|
39
|
+
* closure captures `pool` and constructs the store and the thread history from
|
|
40
|
+
* it, the same way the chat-turn preparation does.
|
|
41
|
+
*/
|
|
42
|
+
export function createReportSessionSpawn(deps) {
|
|
43
|
+
const { pool } = deps;
|
|
44
|
+
const store = createThreadStore(pool);
|
|
45
|
+
const history = createThreadHistory(pool);
|
|
46
|
+
function spawnReportSession(parentThreadId) {
|
|
47
|
+
return store.getThread(parentThreadId).andThen((parent) => {
|
|
48
|
+
// An absent parent and an archived one arrive the same way — `getThread`
|
|
49
|
+
// filters the tombstone — and both refuse as `parent_not_found`.
|
|
50
|
+
if (parent === null) {
|
|
51
|
+
return errAsync({ type: "parent_not_found", op: OP, parentThreadId });
|
|
52
|
+
}
|
|
53
|
+
// The tree stays flat: only a conversation spawns a report.
|
|
54
|
+
if (parent.threadType !== "conversation") {
|
|
55
|
+
return errAsync({ type: "parent_not_a_conversation", op: OP, parentThreadId, threadType: parent.threadType });
|
|
56
|
+
}
|
|
57
|
+
return history.latestSeq(parentThreadId).andThen((anchor) => {
|
|
58
|
+
// `null` is a parent with no messages. A report on an empty transcript
|
|
59
|
+
// reports nothing, so the refusal is the correct answer.
|
|
60
|
+
if (anchor === null) {
|
|
61
|
+
return errAsync({ type: "empty_parent_transcript", op: OP, parentThreadId });
|
|
62
|
+
}
|
|
63
|
+
// `total`, not the page length: N counts every existing report child,
|
|
64
|
+
// not the count on one page. Two concurrent spawns can compose one N.
|
|
65
|
+
// The result is two titles a user renames, and no identifier collides.
|
|
66
|
+
return store
|
|
67
|
+
.listThreads({ analysisId: parent.analysisId, type: "report", parentThreadId })
|
|
68
|
+
.andThen((children) => store.createThread({
|
|
69
|
+
threadId: randomUUID(),
|
|
70
|
+
analysisId: parent.analysisId,
|
|
71
|
+
title: composeTitle(parent.title, children.total + 1),
|
|
72
|
+
type: "report",
|
|
73
|
+
parentThreadId,
|
|
74
|
+
parentSeq: anchor,
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function listReportSessions(analysisId, paging = {}) {
|
|
80
|
+
return store.listThreads({ analysisId, type: "report", ...paging });
|
|
81
|
+
}
|
|
82
|
+
return { spawnReportSession, listReportSessions };
|
|
83
|
+
}
|
|
@@ -25,6 +25,7 @@ import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
|
25
25
|
import type { SynthesisStatus } from "../state/index.js";
|
|
26
26
|
import type { Logger } from "../lib/logger.js";
|
|
27
27
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
28
|
+
import type { CitationResolver } from "../citations/types.js";
|
|
28
29
|
export interface SynthesizeRunDeps {
|
|
29
30
|
/** Operational logging seam; omitted falls back to no-op. */
|
|
30
31
|
readonly logger?: Logger;
|
|
@@ -38,6 +39,8 @@ export interface SynthesizeRunDeps {
|
|
|
38
39
|
readonly synthesisModel: string;
|
|
39
40
|
/** API keys for the bio/chem tools the embedded literature reviewer uses. */
|
|
40
41
|
readonly bioKeys: BioToolKeys;
|
|
42
|
+
/** Shared resolver used by the embedded literature reviewer. */
|
|
43
|
+
readonly citationResolver: CitationResolver;
|
|
41
44
|
/** LLM usage-accounting seam for the synthesizer loop; omitted falls back to the no-op recorder. */
|
|
42
45
|
readonly usageRecorder?: UsageRecorder;
|
|
43
46
|
}
|
|
@@ -22,7 +22,7 @@ import { loadPlan, queryRun } from "../state/index.js";
|
|
|
22
22
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
23
23
|
import { createNoopLogger } from "../lib/console-logger.js";
|
|
24
24
|
export async function synthesizeRun(deps, params) {
|
|
25
|
-
const { pool, provider, embedding, resolveWorkspaceRoot, synthesisModel, bioKeys, usageRecorder } = deps;
|
|
25
|
+
const { pool, provider, embedding, resolveWorkspaceRoot, synthesisModel, bioKeys, citationResolver, usageRecorder } = deps;
|
|
26
26
|
const { analysisId, runId, completedSteps, session, emit, onProgress } = params;
|
|
27
27
|
await onProgress("starting", "Beginning literature-grounded synthesis");
|
|
28
28
|
try {
|
|
@@ -47,6 +47,7 @@ export async function synthesizeRun(deps, params) {
|
|
|
47
47
|
session: synthesizerSession,
|
|
48
48
|
model: synthesisModel,
|
|
49
49
|
bioKeys,
|
|
50
|
+
citationResolver,
|
|
50
51
|
usageRecorder,
|
|
51
52
|
summaries,
|
|
52
53
|
planNarrative,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Test-only resolver for construction paths that do not execute citation tools. */
|
|
2
|
+
export const unusedCitationResolver = {
|
|
3
|
+
async resolveOne() {
|
|
4
|
+
throw new Error("unusedCitationResolver.resolveOne was unexpectedly called");
|
|
5
|
+
},
|
|
6
|
+
async resolveMany() {
|
|
7
|
+
throw new Error("unusedCitationResolver.resolveMany was unexpectedly called");
|
|
8
|
+
},
|
|
9
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type CitationMatchConfig } from "./match.js";
|
|
2
|
+
import type { CitationInput, CitationResolutionResult, CitationSourceOutcome, NormalizedCitation } from "./types.js";
|
|
3
|
+
export declare function aggregateCitationResolution(input: CitationInput, normalized: NormalizedCitation, sourceOutcomes: readonly CitationSourceOutcome[], matchConfig?: CitationMatchConfig): CitationResolutionResult;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { compareSuppliedMetadata, detectCitationConflicts } from "./compare.js";
|
|
2
|
+
import { clusterCitationRecords, selectCitationCluster, DEFAULT_MATCH_CONFIG } from "./match.js";
|
|
3
|
+
function deriveCoverage(outcomes, comparisonsIncomplete) {
|
|
4
|
+
const applicable = outcomes.filter((outcome) => outcome.status !== "not_applicable");
|
|
5
|
+
if (applicable.length === 0)
|
|
6
|
+
return "none";
|
|
7
|
+
const hasCoverage = applicable.some((outcome) => outcome.status === "ok" || outcome.status === "no_data" || outcome.records.length > 0 || outcome.identifierEvidence?.exists === true);
|
|
8
|
+
if (!hasCoverage)
|
|
9
|
+
return "none";
|
|
10
|
+
if (applicable.some((outcome) => outcome.status === "unavailable") || comparisonsIncomplete)
|
|
11
|
+
return "partial";
|
|
12
|
+
return "complete";
|
|
13
|
+
}
|
|
14
|
+
function deriveVerdict(normalized, selected, comparisons, coverage) {
|
|
15
|
+
if (normalized.unsupportedWorkKind !== undefined)
|
|
16
|
+
return "unverifiable";
|
|
17
|
+
if (selected !== undefined) {
|
|
18
|
+
if (comparisons.some((comparison) => comparison.status === "mismatch"))
|
|
19
|
+
return "metadata_mismatch";
|
|
20
|
+
if (comparisons.some((comparison) => comparison.status === "not_compared"))
|
|
21
|
+
return "inconclusive";
|
|
22
|
+
return "verified";
|
|
23
|
+
}
|
|
24
|
+
return coverage === "complete" ? "not_found" : "inconclusive";
|
|
25
|
+
}
|
|
26
|
+
export function aggregateCitationResolution(input, normalized, sourceOutcomes, matchConfig = DEFAULT_MATCH_CONFIG) {
|
|
27
|
+
const records = sourceOutcomes.flatMap((outcome) => outcome.records);
|
|
28
|
+
const candidates = clusterCitationRecords(input, normalized, records, matchConfig);
|
|
29
|
+
const selected = selectCitationCluster(candidates, matchConfig);
|
|
30
|
+
const comparisons = compareSuppliedMetadata(input, selected);
|
|
31
|
+
const coverage = deriveCoverage(sourceOutcomes, comparisons.some((comparison) => comparison.status === "not_compared"));
|
|
32
|
+
const verdict = deriveVerdict(normalized, selected, comparisons, coverage);
|
|
33
|
+
return {
|
|
34
|
+
input,
|
|
35
|
+
normalized,
|
|
36
|
+
sourceOutcomes: [...sourceOutcomes],
|
|
37
|
+
candidates,
|
|
38
|
+
...(selected === undefined ? {} : { selectedClusterId: selected.id }),
|
|
39
|
+
comparisons,
|
|
40
|
+
conflicts: detectCitationConflicts(candidates),
|
|
41
|
+
verdict,
|
|
42
|
+
coverage,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CitationSourceClient } from "../types.js";
|
|
2
|
+
import type { SourceHttpOptions } from "../../literature/sources/http.js";
|
|
3
|
+
export interface ArxivCitationClientOptions extends SourceHttpOptions {
|
|
4
|
+
readonly maxCandidates?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function createArxivCitationClient(options?: ArxivCitationClientOptions): CitationSourceClient;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { extractArxivId } from "../normalize.js";
|
|
2
|
+
import { createArxivSource } from "../../literature/sources/arxiv.js";
|
|
3
|
+
import { parseYear, sourceOutcome } from "./common.js";
|
|
4
|
+
function arxivRecord(paper) {
|
|
5
|
+
const year = parseYear(paper.published);
|
|
6
|
+
const identifier = extractArxivId(paper.id) ?? paper.id.toLocaleLowerCase("en-US");
|
|
7
|
+
return {
|
|
8
|
+
source: "arxiv",
|
|
9
|
+
sourceRecordId: paper.id,
|
|
10
|
+
identifiers: { arxiv: identifier },
|
|
11
|
+
title: paper.title,
|
|
12
|
+
authors: paper.authors,
|
|
13
|
+
...(year === undefined ? {} : { year }),
|
|
14
|
+
venue: "arXiv",
|
|
15
|
+
url: paper.url,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function createArxivCitationClient(options = {}) {
|
|
19
|
+
const source = createArxivSource(options);
|
|
20
|
+
return {
|
|
21
|
+
source: "arxiv",
|
|
22
|
+
async resolve(request, signal) {
|
|
23
|
+
if (!request.plan.applicable)
|
|
24
|
+
return sourceOutcome("arxiv", request.plan.operation, "not_applicable", 0, [], request.plan.reason);
|
|
25
|
+
const result = request.plan.operation === "arxiv_exact"
|
|
26
|
+
? await source.lookupExact(request.normalized.identifiers.arxiv, options.maxCandidates ?? 5, signal)
|
|
27
|
+
: await source.search({ query: request.input.title ?? request.input.citation, limit: options.maxCandidates ?? 5 }, signal);
|
|
28
|
+
if (result.status !== "ok")
|
|
29
|
+
return sourceOutcome("arxiv", request.plan.operation, result.status, 1, [], result.detail);
|
|
30
|
+
const records = result.value.map(arxivRecord);
|
|
31
|
+
return sourceOutcome("arxiv", request.plan.operation, records.length === 0 ? "no_data" : "ok", 1, records);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CitationRecord, CitationSource, CitationSourceOperation, CitationSourceOutcome, CitationSourcePlanItem } from "../types.js";
|
|
2
|
+
export declare function notApplicableOutcome(plan: CitationSourcePlanItem): CitationSourceOutcome;
|
|
3
|
+
export declare function sourceOutcome(source: CitationSource, operation: CitationSourceOperation, status: CitationSourceOutcome["status"], requestCount: number, records?: CitationRecord[], detail?: string): CitationSourceOutcome;
|
|
4
|
+
/**
|
|
5
|
+
* Percent-encode a DOI for use as a URL path segment. A DOI suffix may legally
|
|
6
|
+
* contain `#`, `?`, and `%`, each of which silently re-parses the URL when
|
|
7
|
+
* interpolated raw; only the prefix/suffix `/` is structural and stays literal.
|
|
8
|
+
*/
|
|
9
|
+
export declare function encodeDoiPath(doi: string): string;
|
|
10
|
+
export declare function parseYear(value: unknown): number | undefined;
|
|
11
|
+
export declare function firstNonEmpty(values: readonly unknown[]): string | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function notApplicableOutcome(plan) {
|
|
2
|
+
return {
|
|
3
|
+
source: plan.source,
|
|
4
|
+
operation: plan.operation,
|
|
5
|
+
status: "not_applicable",
|
|
6
|
+
requestCount: 0,
|
|
7
|
+
records: [],
|
|
8
|
+
detail: plan.reason,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function sourceOutcome(source, operation, status, requestCount, records = [], detail) {
|
|
12
|
+
return {
|
|
13
|
+
source,
|
|
14
|
+
operation,
|
|
15
|
+
status,
|
|
16
|
+
requestCount,
|
|
17
|
+
records,
|
|
18
|
+
...(detail === undefined ? {} : { detail }),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Percent-encode a DOI for use as a URL path segment. A DOI suffix may legally
|
|
23
|
+
* contain `#`, `?`, and `%`, each of which silently re-parses the URL when
|
|
24
|
+
* interpolated raw; only the prefix/suffix `/` is structural and stays literal.
|
|
25
|
+
*/
|
|
26
|
+
export function encodeDoiPath(doi) {
|
|
27
|
+
return doi.split("/").map(encodeURIComponent).join("/");
|
|
28
|
+
}
|
|
29
|
+
export function parseYear(value) {
|
|
30
|
+
if (typeof value === "number" && Number.isInteger(value))
|
|
31
|
+
return value;
|
|
32
|
+
if (typeof value !== "string")
|
|
33
|
+
return undefined;
|
|
34
|
+
const match = value.match(/\b(1[5-9]\d{2}|20\d{2}|21\d{2})\b/);
|
|
35
|
+
return match?.[1] === undefined ? undefined : Number(match[1]);
|
|
36
|
+
}
|
|
37
|
+
export function firstNonEmpty(values) {
|
|
38
|
+
for (const value of values) {
|
|
39
|
+
if (typeof value === "string" && value.trim())
|
|
40
|
+
return value.trim();
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CitationSourceClient } from "../types.js";
|
|
2
|
+
import { type SourceHttpOptions } from "../../literature/sources/http.js";
|
|
3
|
+
export interface CrossrefClientOptions extends SourceHttpOptions {
|
|
4
|
+
readonly mailto?: string;
|
|
5
|
+
readonly userAgent?: string;
|
|
6
|
+
readonly maxCandidates?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function createCrossrefClient(options?: CrossrefClientOptions): CitationSourceClient;
|