@tangle-network/agent-knowledge 17.1.9 → 18.0.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/CHANGELOG.md +30 -0
- package/README.md +7 -4
- package/dist/benchmarks-B3kU-Yxz.js.map +1 -1
- package/dist/cli.js +9 -5
- package/dist/cli.js.map +1 -1
- package/dist/index-DZeFm-BP.d.ts.map +1 -1
- package/dist/index-eDsIXWyM.d.ts.map +1 -1
- package/dist/index.d.ts +473 -482
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +295 -227
- package/dist/index.js.map +1 -1
- package/dist/{inspect-DrJnvx5Z.js → inspect-BVT9JWDL.js} +7 -5
- package/dist/{inspect-DrJnvx5Z.js.map → inspect-BVT9JWDL.js.map} +1 -1
- package/dist/memory/index.js +1 -1
- package/dist/{memory-Bn1Xs1hY.js → memory-BYWNir2t.js} +9 -6
- package/dist/{memory-Bn1Xs1hY.js.map → memory-BYWNir2t.js.map} +1 -1
- package/dist/sources/index.d.ts +25 -26
- package/dist/sources/index.d.ts.map +1 -1
- package/dist/sources/index.js +2 -2
- package/dist/sources/index.js.map +1 -1
- package/dist/viz/index.d.ts +9 -10
- package/dist/viz/index.d.ts.map +1 -1
- package/package.json +18 -43
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { $ as formatKnowledgeCitationReference, $t as inspectPendingKnowledgeMutation, A as addSourceText, An as writeJsonDurableWithinRoot, At as ResearchSourceVersionSchema, B as KB_STORE_DIR, Bt as claimId, C as gradeClaims, Cn as renameDurable, Ct as KnowledgePageSchema, D as buildKnowledgeIndex, Dn as writeFileDurable, Dt as ResearchClaimEvidenceSchema, E as verifyGradeableEvidence, En as withSafeDirectory, Et as KnowledgeRelationSchema, F as ClaimLedgerMigrationRequiredError, Ft as assertDeepQuestionIntegrity, G as KnowledgeCitationAuditError, Gt as materializeRegisteredClaimEvidence, H as assertClaimLedgerId, Ht as deepQuestionId, I as FileSystemKbStore, It as assertResearchClaimEvidenceIntegrity, J as assertKnowledgeCitationAudit, Jt as normalizeClaimText, K as KnowledgeCitationResolutionError, Kt as mergeClaimLedgers, L as KB_CLAIM_LEDGER_DIR, Lt as assertResearchClaimLedgerIntegrity, M as snapshotSourceTextInput, Mn as textSourceAdapter, Mt as SourceRecordSchema, N as sourceRegistryPath, Nt as KNOWLEDGE_EVENT_TYPES, O as writeKnowledgeIndex, On as writeFileDurableWithinRoot, Ot as ResearchClaimLedgerSchema, P as writeSourceRegistry, Pt as ClaimLedgerGoalConflictError, Q as auditKnowledgeCitations, Qt as acquireDurableFileLock, R as KB_EVENTS_PATH, Rt as assertTrackedClaimIntegrity, S as claimCheckKey, Sn as removeDurable, St as KnowledgePageInvalidationSchema, T as verdictFor, Tn as withSafeDescendant, Tt as KnowledgeRelationNodeSchema, U as buildKnowledgeGraph, Ut as emptyClaimLedger, V as MemoryKbStore, Vt as claimSourceHost, W as knowledgePageRelations, Wt as linkClaimContradictions, X as assertRunScopedCitationsResolved, Xt as formatFrontmatter, Y as assertKnowledgeCitationsResolved, Yt as researchSourceVersionKey, Z as auditCurrentRunCitations, Zt as parseFrontmatter, _ as lintKnowledgeIndex, _n as isMissingFile, _t as KnowledgeBaseCandidateSchema, a as applyKnowledgeWriteBlocksFile, an as applyKnowledgeFileTransaction, at as SCAFFOLD_PAGE_BASENAMES, b as UncheckableClaimError, bn as readRegularFileWithinRoot, bt as KnowledgeGraphNodeSchema, c as KnowledgeDuplicateIntakeError, cn as finishKnowledgeFileTransaction, ct as isScaffoldPath, d as createFileRunLineageAuthority, dn as rollbackKnowledgeFileTransaction, dt as loadKnowledgePages, en as isKnowledgeMutationHeld, et as parseKnowledgeCitationReference, f as createRunScopedStores, fn as DEFAULT_PAGES_DIRECTORY, ft as writeJson, g as validateKnowledgeIndex, gn as isKernelAnchoredPath, gt as DeepQuestionSchema, h as normalizePageText, hn as canonicalRelativeWithinRoot, ht as normalizeLinkTarget, i as applyKnowledgeWriteBlocks, in as withKnowledgeRead, it as resolveRunScopedCitations, j as loadSourceRegistry, jn as mediaTypeFor, jt as SourceAnchorSchema, k as addSourcePath, kn as writeJsonDurable, kt as ResearchClaimRecordSchema, l as assertKnowledgeWriteIntake, ln as knowledgeFileTransactionPlanHash, lt as knowledgePageFromMarkdown, m as detectNearDuplicatePages, mn as canonicalPathsEqual, mt as extractWikilinks, n as inspectKnowledgeIndex, nn as runInKnowledgeMutationScope, nt as resolveKnowledgeCitations, o as isSafeKnowledgePath, on as assertKnowledgeMutationPath, ot as initKnowledgeBase, p as originatedPages, pn as normalizePagesDirectory, pt as WIKILINK_REGEX, q as assertCurrentRunCitationsResolved, qt as mergeTrackedClaims, r as stringMetadata, rn as withKnowledgeMutation, rt as resolveRunScopedCitation, s as parseKnowledgeWriteBlocks, sn as commitKnowledgeFileMutations, st as isKnowledgePagePath, t as explainKnowledgeTarget, tn as recoverPendingKnowledgeMutation, tt as resolveKnowledgeCitation, u as RUN_LINEAGE_BASENAME, un as prepareKnowledgeFileTransaction, ut as layoutFor, v as CHECKABLE_RUNG_THRESHOLD, vn as listRegularFilesWithinRoot, vt as KnowledgeEventSchema, w as gradeFor, wn as syncDirectory, wt as KnowledgeRelationGraphSchema, x as assertGradeableEvidence, xn as relativeWithinRoot, xt as KnowledgeIndexSchema, y as DEADLINE_EXIT_CODE, yn as readRegularFileNoFollow, yt as KnowledgeGraphEdgeSchema, z as KB_INDEX_PATH, zt as claimEvidenceId } from "./inspect-
|
|
1
|
+
import { $ as formatKnowledgeCitationReference, $t as inspectPendingKnowledgeMutation, A as addSourceText, An as writeJsonDurableWithinRoot, At as ResearchSourceVersionSchema, B as KB_STORE_DIR, Bt as claimId, C as gradeClaims, Cn as renameDurable, Ct as KnowledgePageSchema, D as buildKnowledgeIndex, Dn as writeFileDurable, Dt as ResearchClaimEvidenceSchema, E as verifyGradeableEvidence, En as withSafeDirectory, Et as KnowledgeRelationSchema, F as ClaimLedgerMigrationRequiredError, Ft as assertDeepQuestionIntegrity, G as KnowledgeCitationAuditError, Gt as materializeRegisteredClaimEvidence, H as assertClaimLedgerId, Ht as deepQuestionId, I as FileSystemKbStore, It as assertResearchClaimEvidenceIntegrity, J as assertKnowledgeCitationAudit, Jt as normalizeClaimText, K as KnowledgeCitationResolutionError, Kt as mergeClaimLedgers, L as KB_CLAIM_LEDGER_DIR, Lt as assertResearchClaimLedgerIntegrity, M as snapshotSourceTextInput, Mn as textSourceAdapter, Mt as SourceRecordSchema, N as sourceRegistryPath, Nt as KNOWLEDGE_EVENT_TYPES, O as writeKnowledgeIndex, On as writeFileDurableWithinRoot, Ot as ResearchClaimLedgerSchema, P as writeSourceRegistry, Pt as ClaimLedgerGoalConflictError, Q as auditKnowledgeCitations, Qt as acquireDurableFileLock, R as KB_EVENTS_PATH, Rt as assertTrackedClaimIntegrity, S as claimCheckKey, Sn as removeDurable, St as KnowledgePageInvalidationSchema, T as verdictFor, Tn as withSafeDescendant, Tt as KnowledgeRelationNodeSchema, U as buildKnowledgeGraph, Ut as emptyClaimLedger, V as MemoryKbStore, Vt as claimSourceHost, W as knowledgePageRelations, Wt as linkClaimContradictions, X as assertRunScopedCitationsResolved, Xt as formatFrontmatter, Y as assertKnowledgeCitationsResolved, Yt as researchSourceVersionKey, Z as auditCurrentRunCitations, Zt as parseFrontmatter, _ as lintKnowledgeIndex, _n as isMissingFile, _t as KnowledgeBaseCandidateSchema, a as applyKnowledgeWriteBlocksFile, an as applyKnowledgeFileTransaction, at as SCAFFOLD_PAGE_BASENAMES, b as UncheckableClaimError, bn as readRegularFileWithinRoot, bt as KnowledgeGraphNodeSchema, c as KnowledgeDuplicateIntakeError, cn as finishKnowledgeFileTransaction, ct as isScaffoldPath, d as createFileRunLineageAuthority, dn as rollbackKnowledgeFileTransaction, dt as loadKnowledgePages, en as isKnowledgeMutationHeld, et as parseKnowledgeCitationReference, f as createRunScopedStores, fn as DEFAULT_PAGES_DIRECTORY, ft as writeJson, g as validateKnowledgeIndex, gn as isKernelAnchoredPath, gt as DeepQuestionSchema, h as normalizePageText, hn as canonicalRelativeWithinRoot, ht as normalizeLinkTarget, i as applyKnowledgeWriteBlocks, in as withKnowledgeRead, it as resolveRunScopedCitations, j as loadSourceRegistry, jn as mediaTypeFor, jt as SourceAnchorSchema, k as addSourcePath, kn as writeJsonDurable, kt as ResearchClaimRecordSchema, l as assertKnowledgeWriteIntake, ln as knowledgeFileTransactionPlanHash, lt as knowledgePageFromMarkdown, m as detectNearDuplicatePages, mn as canonicalPathsEqual, mt as extractWikilinks, n as inspectKnowledgeIndex, nn as runInKnowledgeMutationScope, nt as resolveKnowledgeCitations, o as isSafeKnowledgePath, on as assertKnowledgeMutationPath, ot as initKnowledgeBase, p as originatedPages, pn as normalizePagesDirectory, pt as WIKILINK_REGEX, q as assertCurrentRunCitationsResolved, qt as mergeTrackedClaims, r as stringMetadata, rn as withKnowledgeMutation, rt as resolveRunScopedCitation, s as parseKnowledgeWriteBlocks, sn as commitKnowledgeFileMutations, st as isKnowledgePagePath, t as explainKnowledgeTarget, tn as recoverPendingKnowledgeMutation, tt as resolveKnowledgeCitation, u as RUN_LINEAGE_BASENAME, un as prepareKnowledgeFileTransaction, ut as layoutFor, v as CHECKABLE_RUNG_THRESHOLD, vn as listRegularFilesWithinRoot, vt as KnowledgeEventSchema, w as gradeFor, wn as syncDirectory, wt as KnowledgeRelationGraphSchema, x as assertGradeableEvidence, xn as relativeWithinRoot, xt as KnowledgeIndexSchema, y as DEADLINE_EXIT_CODE, yn as readRegularFileNoFollow, yt as KnowledgeGraphEdgeSchema, z as KB_INDEX_PATH, zt as claimEvidenceId } from "./inspect-BVT9JWDL.js";
|
|
2
2
|
import { i as textSourceId, n as slugify, r as stableId, t as sha256 } from "./ids-Bevz_pXV.js";
|
|
3
|
-
import { A as runSerializedKnowledgeOptimization, C as deterministicRng, D as toOffPolicyTrajectory, E as retrievalHoldoutConfigHash, M as assertImmutableRef, O as jsonCandidateCodec, S as applySessionStickyRetrievalHoldout, T as resetRetrievalHoldoutRegistry, _ as createAgentMemoryBranch, a as createNeo4jAgentMemoryAdapter, b as renderMemoryContext, c as runAgentMemoryImprovement, d as compareAgentMemoryLearning, f as runAgentMemoryLearningExperiment, g as buildAgentMemorySequencesFromBenchmarkCases, h as buildAgentMemorySequenceScenarios, i as AgentMemoryWriteInputSchema, j as scenarioContentFingerprint, k as jsonObjectCandidateCodec, l as createGraphitiMemoryAdapter, m as agentMemorySequenceJudge, n as AgentMemoryKindSchema, o as createMem0MemoryAdapter, p as runAgentMemoryExperiment, r as AgentMemoryScopeSchema, s as mem0MemoryAdapterIdentity, t as AgentMemoryHitSchema, u as graphitiMemoryAdapterIdentity, v as forkAgentMemoryBranchSnapshot, w as emitRetrievalHoldoutBypass, x as applyRetrievalHoldout, y as defaultGetMemoryContext } from "./memory-
|
|
3
|
+
import { A as runSerializedKnowledgeOptimization, C as deterministicRng, D as toOffPolicyTrajectory, E as retrievalHoldoutConfigHash, M as assertImmutableRef, O as jsonCandidateCodec, S as applySessionStickyRetrievalHoldout, T as resetRetrievalHoldoutRegistry, _ as createAgentMemoryBranch, a as createNeo4jAgentMemoryAdapter, b as renderMemoryContext, c as runAgentMemoryImprovement, d as compareAgentMemoryLearning, f as runAgentMemoryLearningExperiment, g as buildAgentMemorySequencesFromBenchmarkCases, h as buildAgentMemorySequenceScenarios, i as AgentMemoryWriteInputSchema, j as scenarioContentFingerprint, k as jsonObjectCandidateCodec, l as createGraphitiMemoryAdapter, m as agentMemorySequenceJudge, n as AgentMemoryKindSchema, o as createMem0MemoryAdapter, p as runAgentMemoryExperiment, r as AgentMemoryScopeSchema, s as mem0MemoryAdapterIdentity, t as AgentMemoryHitSchema, u as graphitiMemoryAdapterIdentity, v as forkAgentMemoryBranchSnapshot, w as emitRetrievalHoldoutBypass, x as applyRetrievalHoldout, y as defaultGetMemoryContext } from "./memory-BYWNir2t.js";
|
|
4
4
|
import { C as sleepForMemoryRecovery, F as buildIndustryRagBenchmarkSmokeCases, G as buildRetrievalEvalDispatch, H as createNoopMemoryBenchmarkAdapter, I as respondToIndustryMemoryBenchmarkSmokeCase, J as retrievalConfigSurface, K as partitionRetrievalScenarios, L as respondToIndustryRagBenchmarkSmokeCase, M as INDUSTRY_RAG_BENCHMARKS, N as buildFirstPartyMemoryLifecycleBenchmarkCases, P as buildIndustryMemoryBenchmarkSmokeCases, R as isKnowledgeMemoryBenchmarkCase, S as runBoundedMemoryLifecycle, U as memoryHitToSourceRecord, V as createInMemoryBenchmarkAdapter, W as memoryWriteResultToSourceRecord, X as scoreRetrievalArtifact, Y as retrievalRecallJudge, Z as mean, a as buildKnowledgeBenchmarkScenarios, b as memoryRecoveryDelayMs, c as runKnowledgeBenchmarkSuite, d as summarizeKnowledgeBenchmarkCampaign, f as acquireAgentMemoryRunLease, h as DEFAULT_MEMORY_CLEANUP_TIMEOUT_MS, i as runMemoryAdapterBenchmark, j as INDUSTRY_MEMORY_BENCHMARKS, l as scoreKnowledgeBenchmarkArtifact, m as AgentMemoryLifecycleUnsafeError, n as parseKnowledgeBenchmarkJsonl, o as knowledgeBenchmarkJudge, p as AgentMemoryLifecycleTimeoutError, q as retrievalConfigFromSurface, r as parseKnowledgeBenchmarkQrels, s as renderKnowledgeBenchmarkReportMarkdown, t as buildRetrievalBenchmarkCasesFromQrels, u as scoreMemoryBenchmarkArtifact, x as resolveMemoryCleanupTimeoutMs, y as createMemoryExecutionPool } from "./benchmarks-B3kU-Yxz.js";
|
|
5
5
|
import { a as buildKnowledgeLexicalIndex, c as tokenizeText, i as searchKnowledgePages, n as reciprocalRankFusion, o as scoreBm25, r as searchKnowledge, s as tokenizeQuery, t as KNOWLEDGE_SEARCH_RETRIEVER_ID } from "./search-Cbyj2shJ.js";
|
|
6
6
|
import { IRS_DIMENSION_HINTS, MAX_RESPONSE_BYTES, MIN_REQUEST_GAP_MS, POLITE_USER_AGENT, __resetHttpThrottle, createCornellLiiSource, createIrsPublicationsSource, createStateSosSource, extractLinks, firstMatch, htmlToText, innerHtmlById, looksLikeBlockPage, politeFetch } from "./sources/index.js";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
|
+
import { TCloudClient, TCloudError } from "@tangle-network/tcloud";
|
|
8
9
|
import { cp, lstat, mkdir, mkdtemp, readFile, rm, stat } from "node:fs/promises";
|
|
9
10
|
import { dirname, join } from "node:path";
|
|
10
11
|
import { agentImprovementActivationResultSchema, agentImprovementActivationSchema, canonicalCandidateBytes, canonicalCandidateDigest, canonicalCandidateJson, omitTopLevelDigest, sha256Bytes, sha256DigestSchema } from "@tangle-network/agent-interface";
|
|
@@ -39,10 +40,9 @@ import { pathToFileURL } from "node:url";
|
|
|
39
40
|
* worker ADDS; the driver GATES. Together they build a cleaner knowledge base
|
|
40
41
|
* than a single agent at the same compute budget.
|
|
41
42
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* passing `baseUrl`; supply the key via `apiKey` or `TANGLE_API_KEY`.
|
|
43
|
+
* Model and search requests use the published TCloud SDK. Knowledge owns the
|
|
44
|
+
* research policy, not HTTP, bearer headers, retries or provider rate cards.
|
|
45
|
+
* Point it at any router with `baseUrl`; supply `apiKey` or `TANGLE_API_KEY`.
|
|
46
46
|
*/
|
|
47
47
|
/** Default router model. Plain id (no namespace) — see CLAUDE/creds notes. */
|
|
48
48
|
const DEFAULT_MODEL = "glm-5.2";
|
|
@@ -54,35 +54,7 @@ const DEFAULT_BASE_URL = "https://router.tangle.tools/v1";
|
|
|
54
54
|
* reasoning model never silently yields nothing. (Verified creds note.)
|
|
55
55
|
*/
|
|
56
56
|
const MIN_MAX_TOKENS = 1200;
|
|
57
|
-
/**
|
|
58
|
-
const transientStatuses = /* @__PURE__ */ new Set([
|
|
59
|
-
429,
|
|
60
|
-
502,
|
|
61
|
-
503,
|
|
62
|
-
504
|
|
63
|
-
]);
|
|
64
|
-
/**
|
|
65
|
-
* POST with bounded exponential backoff on transient upstream statuses. Returns
|
|
66
|
-
* the first `res.ok` response, or the LAST response (so the caller throws the
|
|
67
|
-
* real status). A non-transient failure returns immediately — only 502/503/504/
|
|
68
|
-
* 429 are retried. Aborts propagate at once.
|
|
69
|
-
*/
|
|
70
|
-
async function fetchWithRetry(url, init, opts) {
|
|
71
|
-
let lastRes;
|
|
72
|
-
for (let attempt = 0; attempt <= opts.maxRetries; attempt += 1) {
|
|
73
|
-
if (opts.signal?.aborted) throw new RouterError(0, "aborted");
|
|
74
|
-
const res = await fetch(url, init);
|
|
75
|
-
if (res.ok || !transientStatuses.has(res.status)) return res;
|
|
76
|
-
lastRes = res;
|
|
77
|
-
if (attempt === opts.maxRetries) break;
|
|
78
|
-
await res.text().catch(() => "");
|
|
79
|
-
const jitter = opts.retryBaseMs * 2 ** attempt * (.75 + Math.random() * .5);
|
|
80
|
-
await new Promise((resolve) => setTimeout(resolve, jitter));
|
|
81
|
-
}
|
|
82
|
-
if (lastRes) return lastRes;
|
|
83
|
-
throw new RouterError(0, "fetchWithRetry produced no response");
|
|
84
|
-
}
|
|
85
|
-
/** A small error so a failed router call fails loud rather than returning junk. */
|
|
57
|
+
/** Compatibility error facade. HTTP execution and classification belong to TCloud. */
|
|
86
58
|
var RouterError = class extends Error {
|
|
87
59
|
status;
|
|
88
60
|
constructor(status, message) {
|
|
@@ -91,26 +63,26 @@ var RouterError = class extends Error {
|
|
|
91
63
|
this.name = "RouterError";
|
|
92
64
|
}
|
|
93
65
|
};
|
|
94
|
-
/**
|
|
95
|
-
* Build a dependency-free Tangle router client over `fetch`. This is the same
|
|
96
|
-
* wire surface the `tcloud` SDK + `tcloud mcp` use (`/v1/search` for web search,
|
|
97
|
-
* `/v1/chat/completions` for chat) so it needs no CLI installed.
|
|
98
|
-
*/
|
|
66
|
+
/** Adapt Knowledge's public contract to the published SDK, without a second transport. */
|
|
99
67
|
function createTangleRouterClient(options = {}) {
|
|
100
|
-
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
101
68
|
const apiKey = options.apiKey ?? process.env.TANGLE_API_KEY;
|
|
102
69
|
if (!apiKey) throw new RouterError(401, "no TANGLE_API_KEY (pass apiKey or set the env var)");
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
70
|
+
const client = new TCloudClient({
|
|
71
|
+
baseURL: (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
72
|
+
apiKey,
|
|
73
|
+
model: options.model ?? DEFAULT_MODEL,
|
|
74
|
+
timeout: 0,
|
|
75
|
+
retry: {
|
|
76
|
+
maxRetries: Math.max(0, options.maxRetries ?? 4),
|
|
77
|
+
initialBackoffMs: Math.max(1, options.retryBaseMs ?? 1500),
|
|
78
|
+
retryableStatuses: [
|
|
79
|
+
429,
|
|
80
|
+
502,
|
|
81
|
+
503,
|
|
82
|
+
504
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
});
|
|
114
86
|
const acc = {
|
|
115
87
|
chatCalls: 0,
|
|
116
88
|
searchCalls: 0,
|
|
@@ -119,70 +91,79 @@ function createTangleRouterClient(options = {}) {
|
|
|
119
91
|
usd: 0,
|
|
120
92
|
wallMs: 0
|
|
121
93
|
};
|
|
94
|
+
const recordCost = (cost) => {
|
|
95
|
+
acc.usd += typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : NaN;
|
|
96
|
+
};
|
|
97
|
+
const translate = (error) => {
|
|
98
|
+
options.signal?.throwIfAborted();
|
|
99
|
+
if (error instanceof TCloudError) throw new RouterError(error.status, error.message);
|
|
100
|
+
throw error;
|
|
101
|
+
};
|
|
122
102
|
return {
|
|
123
103
|
async search(query, opts) {
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
signal: options.signal,
|
|
129
|
-
body: JSON.stringify({
|
|
104
|
+
options.signal?.throwIfAborted();
|
|
105
|
+
const started = Date.now();
|
|
106
|
+
try {
|
|
107
|
+
const response = await client.search({
|
|
130
108
|
query,
|
|
131
109
|
...options.searchProvider ? { provider: options.searchProvider } : {},
|
|
132
|
-
...opts?.maxResults != null ? { maxResults: opts.maxResults } : {}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
110
|
+
...opts?.maxResults != null ? { maxResults: opts.maxResults } : {},
|
|
111
|
+
signal: options.signal
|
|
112
|
+
});
|
|
113
|
+
recordCost(response.usage?.billed_cost);
|
|
114
|
+
return (response.data ?? []).filter((hit) => typeof hit?.url === "string" && hit.url.length > 0).map((hit) => ({
|
|
115
|
+
title: hit.title ?? hit.url,
|
|
116
|
+
url: hit.url,
|
|
117
|
+
snippet: hit.snippet
|
|
118
|
+
}));
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return translate(error);
|
|
121
|
+
} finally {
|
|
122
|
+
acc.searchCalls += 1;
|
|
123
|
+
acc.wallMs += Date.now() - started;
|
|
124
|
+
}
|
|
147
125
|
},
|
|
148
126
|
async chat(messages, maxTokens) {
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
headers,
|
|
154
|
-
signal: options.signal,
|
|
155
|
-
body: JSON.stringify({
|
|
156
|
-
model,
|
|
127
|
+
options.signal?.throwIfAborted();
|
|
128
|
+
const started = Date.now();
|
|
129
|
+
try {
|
|
130
|
+
const response = await client.chat({
|
|
157
131
|
messages,
|
|
158
|
-
|
|
132
|
+
maxTokens: Math.max(MIN_MAX_TOKENS, maxTokens ?? MIN_MAX_TOKENS),
|
|
159
133
|
temperature: .2,
|
|
160
|
-
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
acc.promptTokens += promptTokens;
|
|
173
|
-
acc.completionTokens += completionTokens;
|
|
174
|
-
acc.usd += promptTokens * price.prompt + completionTokens * price.completion;
|
|
175
|
-
acc.wallMs += Date.now() - t0;
|
|
176
|
-
return body.choices?.[0]?.message?.content ?? "";
|
|
134
|
+
signal: options.signal
|
|
135
|
+
});
|
|
136
|
+
acc.chatCalls += 1;
|
|
137
|
+
acc.promptTokens += response.usage?.prompt_tokens ?? 0;
|
|
138
|
+
acc.completionTokens += response.usage?.completion_tokens ?? 0;
|
|
139
|
+
recordCost(response.tangle?.costSource === "receipt" ? response.tangle.costUsd : void 0);
|
|
140
|
+
return response.choices?.[0]?.message?.content ?? "";
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return translate(error);
|
|
143
|
+
} finally {
|
|
144
|
+
acc.wallMs += Date.now() - started;
|
|
145
|
+
}
|
|
177
146
|
},
|
|
178
147
|
usage() {
|
|
179
148
|
return { ...acc };
|
|
180
149
|
}
|
|
181
150
|
};
|
|
182
151
|
}
|
|
183
|
-
/**
|
|
184
|
-
function resolveRouter(opts) {
|
|
185
|
-
|
|
152
|
+
/** Bind run cancellation to the default router without changing an injected client. */
|
|
153
|
+
function resolveRouter(opts, signal) {
|
|
154
|
+
if (opts.router) return opts.router;
|
|
155
|
+
const requestSignal = combineRouterSignals(signal, opts.router_options?.signal);
|
|
156
|
+
return createTangleRouterClient({
|
|
157
|
+
...opts.router_options,
|
|
158
|
+
signal: requestSignal
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function combineRouterSignals(runSignal, configuredSignal) {
|
|
162
|
+
return runSignal && configuredSignal ? AbortSignal.any([runSignal, configuredSignal]) : runSignal ?? configuredSignal;
|
|
163
|
+
}
|
|
164
|
+
function throwIfRouterAborted(runSignal, opts) {
|
|
165
|
+
runSignal?.throwIfAborted();
|
|
166
|
+
if (!opts.router) opts.router_options?.signal?.throwIfAborted();
|
|
186
167
|
}
|
|
187
168
|
/**
|
|
188
169
|
* The real web-research worker. Conforms to the loop's `ResearchWorker`
|
|
@@ -196,33 +177,41 @@ function createWebResearchWorker(options = {}) {
|
|
|
196
177
|
const minTextChars = Math.max(1, options.minTextChars ?? 200);
|
|
197
178
|
const maxTextChars = Math.max(minTextChars, options.maxTextChars ?? 4e3);
|
|
198
179
|
return async (ctx) => {
|
|
199
|
-
const
|
|
180
|
+
const assertActive = () => throwIfRouterAborted(ctx.signal, options);
|
|
181
|
+
assertActive();
|
|
182
|
+
const router = resolveRouter(options, ctx.signal);
|
|
183
|
+
const fetchSignal = options.router ? ctx.signal : combineRouterSignals(ctx.signal, options.router_options?.signal);
|
|
200
184
|
const targetGaps = ctx.gaps.filter((gap) => gap.blocking);
|
|
201
185
|
const gaps = targetGaps.length > 0 ? targetGaps : ctx.gaps;
|
|
202
186
|
if (gaps.length === 0) return {
|
|
203
187
|
sources: [],
|
|
204
188
|
notes: "no open gaps to research"
|
|
205
189
|
};
|
|
206
|
-
const queries = await formSearchQueries(router, ctx, gaps, queriesPerGap);
|
|
190
|
+
const queries = await formSearchQueries(router, ctx, gaps, queriesPerGap, assertActive);
|
|
191
|
+
assertActive();
|
|
207
192
|
const proposals = [];
|
|
208
193
|
const seenUris = /* @__PURE__ */ new Set();
|
|
209
194
|
for (const query of queries) {
|
|
210
195
|
if (proposals.length >= maxSourcesPerRound) break;
|
|
211
|
-
|
|
196
|
+
assertActive();
|
|
212
197
|
let hits;
|
|
213
198
|
try {
|
|
214
199
|
hits = await router.search(query, { maxResults: resultsPerQuery });
|
|
215
200
|
} catch (error) {
|
|
216
|
-
|
|
201
|
+
assertActive();
|
|
202
|
+
if (error?.name === "AbortError") throw error;
|
|
217
203
|
continue;
|
|
218
204
|
}
|
|
205
|
+
assertActive();
|
|
219
206
|
for (const hit of hits) {
|
|
207
|
+
assertActive();
|
|
220
208
|
if (proposals.length >= maxSourcesPerRound) break;
|
|
221
209
|
if (seenUris.has(hit.url)) continue;
|
|
222
210
|
const fetched = await politeFetch(hit.url, {
|
|
223
|
-
signal:
|
|
211
|
+
signal: fetchSignal,
|
|
224
212
|
cacheDir: options.cacheDir
|
|
225
213
|
});
|
|
214
|
+
assertActive();
|
|
226
215
|
if (!fetched.verifiable) continue;
|
|
227
216
|
const text = htmlToText(fetched.body).slice(0, maxTextChars);
|
|
228
217
|
if (text.length < minTextChars) continue;
|
|
@@ -241,6 +230,7 @@ function createWebResearchWorker(options = {}) {
|
|
|
241
230
|
});
|
|
242
231
|
}
|
|
243
232
|
}
|
|
233
|
+
assertActive();
|
|
244
234
|
return {
|
|
245
235
|
sources: proposals,
|
|
246
236
|
buildPages: buildCitingPages(proposals),
|
|
@@ -253,7 +243,7 @@ function createWebResearchWorker(options = {}) {
|
|
|
253
243
|
* to the gap's own readiness query if the model returns nothing parseable, so a
|
|
254
244
|
* model hiccup degrades to a sane search rather than an empty round.
|
|
255
245
|
*/
|
|
256
|
-
async function formSearchQueries(router, ctx, gaps, queriesPerGap) {
|
|
246
|
+
async function formSearchQueries(router, ctx, gaps, queriesPerGap, assertActive) {
|
|
257
247
|
const gapLines = gaps.map((gap, i) => `${i + 1}. ${gap.description} (readiness query: "${gap.query}")`).join("\n");
|
|
258
248
|
const want = gaps.length * queriesPerGap;
|
|
259
249
|
const system = "You are a research librarian. Turn knowledge gaps into precise web search queries that will surface authoritative primary sources (papers, docs, standards, official pages). Return ONLY a JSON array of query strings, no prose.";
|
|
@@ -272,7 +262,10 @@ async function formSearchQueries(router, ctx, gaps, queriesPerGap) {
|
|
|
272
262
|
role: "user",
|
|
273
263
|
content: user
|
|
274
264
|
}], MIN_MAX_TOKENS);
|
|
275
|
-
|
|
265
|
+
assertActive();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
assertActive();
|
|
268
|
+
if (error?.name === "AbortError") throw error;
|
|
276
269
|
raw = "";
|
|
277
270
|
}
|
|
278
271
|
const fromLlm = parseQueryList(raw).slice(0, want);
|
|
@@ -354,7 +347,9 @@ function escapeYaml(value) {
|
|
|
354
347
|
function createVerifyingResearchDriver(options = {}) {
|
|
355
348
|
const acceptOnParseFailure = options.acceptOnParseFailure ?? false;
|
|
356
349
|
return { async verifySource(source, ctx) {
|
|
357
|
-
const
|
|
350
|
+
const assertActive = () => throwIfRouterAborted(ctx.signal, options);
|
|
351
|
+
assertActive();
|
|
352
|
+
const router = resolveRouter(options, ctx.signal);
|
|
358
353
|
const gapLines = ctx.gaps.map((gap) => `- ${gap.description} (query: "${gap.query}")`).join("\n");
|
|
359
354
|
const acceptedTitles = ctx.acceptedThisRound.map((accepted) => `- ${accepted.title ?? accepted.uri}`).join("\n");
|
|
360
355
|
const excerpt = source.text.slice(0, 1500);
|
|
@@ -375,8 +370,10 @@ function createVerifyingResearchDriver(options = {}) {
|
|
|
375
370
|
role: "user",
|
|
376
371
|
content: user
|
|
377
372
|
}], MIN_MAX_TOKENS);
|
|
373
|
+
assertActive();
|
|
378
374
|
} catch (error) {
|
|
379
|
-
|
|
375
|
+
assertActive();
|
|
376
|
+
if (error?.name === "AbortError") throw error;
|
|
380
377
|
return acceptOnParseFailure ? { accept: true } : {
|
|
381
378
|
accept: false,
|
|
382
379
|
reason: `verifier unavailable: ${error.message}`
|
|
@@ -494,7 +491,8 @@ const trackingParams = /* @__PURE__ */ new Set([
|
|
|
494
491
|
* mirror/syndication of an already-accepted source even when the URL differs.
|
|
495
492
|
*/
|
|
496
493
|
function contentKey(text) {
|
|
497
|
-
|
|
494
|
+
const normalized = text.toLowerCase().replace(/[^\p{L}\p{N}\s]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
495
|
+
return sha256(normalized);
|
|
498
496
|
}
|
|
499
497
|
/** Default authoritative host suffixes — papers, official docs, standards. */
|
|
500
498
|
const defaultAuthoritativeHosts = [
|
|
@@ -934,7 +932,8 @@ function knowledgeImprovementRunDir(root, runId) {
|
|
|
934
932
|
return runDir;
|
|
935
933
|
}
|
|
936
934
|
async function withKnowledgeImprovementRun(root, runId, create, use) {
|
|
937
|
-
const
|
|
935
|
+
const runDir = knowledgeImprovementRunDir(root, runId);
|
|
936
|
+
const relativePath = relativeWithinRoot(root, runDir);
|
|
938
937
|
if (!relativePath) throw new Error("knowledge improvement run directory escaped its root");
|
|
939
938
|
return withSafeDirectory(root, relativePath, create, async (openedRunDir) => {
|
|
940
939
|
const result = await use(openedRunDir);
|
|
@@ -1005,8 +1004,9 @@ function parseKnowledgeImprovementEvent(value) {
|
|
|
1005
1004
|
return event;
|
|
1006
1005
|
}
|
|
1007
1006
|
async function acquireRunLease(runDir, options) {
|
|
1007
|
+
const path = join(runDir, "run.lock.durable");
|
|
1008
1008
|
const acquired = await acquireDurableFileLock(runDir, {
|
|
1009
|
-
lockfilePath:
|
|
1009
|
+
lockfilePath: path,
|
|
1010
1010
|
staleMs: options.ttlMs
|
|
1011
1011
|
});
|
|
1012
1012
|
return {
|
|
@@ -2461,100 +2461,114 @@ async function runRagKnowledgeImprovementPhases(options, previous) {
|
|
|
2461
2461
|
let knowledgeUpdate = previous?.knowledgeUpdate;
|
|
2462
2462
|
let answerQuality = previous?.answerQuality;
|
|
2463
2463
|
let promotion = previous?.promotion;
|
|
2464
|
-
if (phaseEnabled(options, "gap-diagnosis"))
|
|
2465
|
-
|
|
2466
|
-
return options.diagnose({
|
|
2467
|
-
goal: options.goal,
|
|
2468
|
-
phases,
|
|
2469
|
-
optimization: selectRagOptimization(optimization),
|
|
2470
|
-
signal: options.signal,
|
|
2471
|
-
retrieval: selectRetrievalOptimization(retrieval)
|
|
2472
|
-
});
|
|
2473
|
-
}, (diagnosed) => `${diagnosed.length} finding(s)`)];
|
|
2474
|
-
else skipPhase(phases, now, "gap-diagnosis", "no diagnosis hook provided");
|
|
2475
|
-
if (phaseEnabled(options, "knowledge-acquisition")) if (options.acquireKnowledge) acquisition = await runPhase(phases, now, "knowledge-acquisition", async () => {
|
|
2476
|
-
assertNotAborted(options.signal);
|
|
2477
|
-
return options.acquireKnowledge({
|
|
2478
|
-
goal: options.goal,
|
|
2479
|
-
phases,
|
|
2480
|
-
optimization: selectRagOptimization(optimization),
|
|
2481
|
-
signal: options.signal,
|
|
2482
|
-
retrieval: selectRetrievalOptimization(retrieval),
|
|
2483
|
-
findings
|
|
2484
|
-
});
|
|
2485
|
-
}, summarizeAcquisitionDecision);
|
|
2486
|
-
else skipPhase(phases, now, "knowledge-acquisition", "no acquisition hook provided");
|
|
2487
|
-
if (phaseEnabled(options, "knowledge-update")) if (options.updateKnowledge) knowledgeUpdate = await runPhase(phases, now, "knowledge-update", async () => {
|
|
2488
|
-
assertNotAborted(options.signal);
|
|
2489
|
-
return options.updateKnowledge({
|
|
2490
|
-
goal: options.goal,
|
|
2491
|
-
phases,
|
|
2492
|
-
optimization: selectRagOptimization(optimization),
|
|
2493
|
-
signal: options.signal,
|
|
2494
|
-
retrieval: selectRetrievalOptimization(retrieval),
|
|
2495
|
-
findings,
|
|
2496
|
-
acquisition
|
|
2497
|
-
});
|
|
2498
|
-
}, (result) => result.summary);
|
|
2499
|
-
else if (options.knowledgeResearch) knowledgeUpdate = await runPhase(phases, now, "knowledge-update", async () => {
|
|
2500
|
-
assertNotAborted(options.signal);
|
|
2501
|
-
return runKnowledgeResearchUpdate(options, acquisition);
|
|
2502
|
-
}, (result) => result.summary);
|
|
2503
|
-
else skipPhase(phases, now, "knowledge-update", "no update hook or research loop provided");
|
|
2504
|
-
if (phaseEnabled(options, "rag-optimization") && (options.optimization || options.enabledPhases?.includes("rag-optimization") || options.requiredPhases?.includes("rag-optimization"))) if (options.optimization) optimization = await runPhase(phases, now, "rag-optimization", async () => {
|
|
2505
|
-
assertNotAborted(options.signal);
|
|
2506
|
-
return runRagOptimization(options.optimization);
|
|
2507
|
-
}, summarizeRagOptimization);
|
|
2508
|
-
else skipPhase(phases, now, "rag-optimization", "no full RAG optimization options provided");
|
|
2509
|
-
if (phaseEnabled(options, "retrieval-tuning")) if (options.retrieval) retrieval = await runPhase(phases, now, "retrieval-tuning", async () => {
|
|
2510
|
-
assertNotAborted(options.signal);
|
|
2511
|
-
return runRetrievalImprovementLoop(options.retrieval);
|
|
2512
|
-
}, summarizeRetrievalResult);
|
|
2513
|
-
else skipPhase(phases, now, "retrieval-tuning", "no retrieval options provided");
|
|
2514
|
-
if (phaseEnabled(options, "answer-quality")) if (options.evaluateAnswers) {
|
|
2515
|
-
answerQuality = await runPhase(phases, now, "answer-quality", async () => {
|
|
2464
|
+
if (phaseEnabled(options, "gap-diagnosis")) {
|
|
2465
|
+
if (options.diagnose) findings = [...await runPhase(phases, now, "gap-diagnosis", async () => {
|
|
2516
2466
|
assertNotAborted(options.signal);
|
|
2517
|
-
|
|
2467
|
+
return options.diagnose({
|
|
2518
2468
|
goal: options.goal,
|
|
2519
2469
|
phases,
|
|
2520
2470
|
optimization: selectRagOptimization(optimization),
|
|
2521
2471
|
signal: options.signal,
|
|
2472
|
+
retrieval: selectRetrievalOptimization(retrieval)
|
|
2473
|
+
});
|
|
2474
|
+
}, (diagnosed) => `${diagnosed.length} finding(s)`)];
|
|
2475
|
+
else skipPhase(phases, now, "gap-diagnosis", "no diagnosis hook provided");
|
|
2476
|
+
}
|
|
2477
|
+
if (phaseEnabled(options, "knowledge-acquisition")) {
|
|
2478
|
+
if (options.acquireKnowledge) acquisition = await runPhase(phases, now, "knowledge-acquisition", async () => {
|
|
2479
|
+
assertNotAborted(options.signal);
|
|
2480
|
+
return options.acquireKnowledge({
|
|
2481
|
+
goal: options.goal,
|
|
2482
|
+
phases,
|
|
2483
|
+
optimization: selectRagOptimization(optimization),
|
|
2484
|
+
signal: options.signal,
|
|
2485
|
+
retrieval: selectRetrievalOptimization(retrieval),
|
|
2486
|
+
findings
|
|
2487
|
+
});
|
|
2488
|
+
}, summarizeAcquisitionDecision);
|
|
2489
|
+
else skipPhase(phases, now, "knowledge-acquisition", "no acquisition hook provided");
|
|
2490
|
+
}
|
|
2491
|
+
if (phaseEnabled(options, "knowledge-update")) {
|
|
2492
|
+
if (options.updateKnowledge) knowledgeUpdate = await runPhase(phases, now, "knowledge-update", async () => {
|
|
2493
|
+
assertNotAborted(options.signal);
|
|
2494
|
+
return options.updateKnowledge({
|
|
2495
|
+
goal: options.goal,
|
|
2496
|
+
phases,
|
|
2497
|
+
optimization: selectRagOptimization(optimization),
|
|
2498
|
+
signal: options.signal,
|
|
2499
|
+
retrieval: selectRetrievalOptimization(retrieval),
|
|
2500
|
+
findings,
|
|
2501
|
+
acquisition
|
|
2502
|
+
});
|
|
2503
|
+
}, (result) => result.summary);
|
|
2504
|
+
else if (options.knowledgeResearch) knowledgeUpdate = await runPhase(phases, now, "knowledge-update", async () => {
|
|
2505
|
+
assertNotAborted(options.signal);
|
|
2506
|
+
return runKnowledgeResearchUpdate(options, acquisition);
|
|
2507
|
+
}, (result) => result.summary);
|
|
2508
|
+
else skipPhase(phases, now, "knowledge-update", "no update hook or research loop provided");
|
|
2509
|
+
}
|
|
2510
|
+
if (phaseEnabled(options, "rag-optimization") && (options.optimization || options.enabledPhases?.includes("rag-optimization") || options.requiredPhases?.includes("rag-optimization"))) {
|
|
2511
|
+
if (options.optimization) optimization = await runPhase(phases, now, "rag-optimization", async () => {
|
|
2512
|
+
assertNotAborted(options.signal);
|
|
2513
|
+
return runRagOptimization(options.optimization);
|
|
2514
|
+
}, summarizeRagOptimization);
|
|
2515
|
+
else skipPhase(phases, now, "rag-optimization", "no full RAG optimization options provided");
|
|
2516
|
+
}
|
|
2517
|
+
if (phaseEnabled(options, "retrieval-tuning")) {
|
|
2518
|
+
if (options.retrieval) retrieval = await runPhase(phases, now, "retrieval-tuning", async () => {
|
|
2519
|
+
assertNotAborted(options.signal);
|
|
2520
|
+
return runRetrievalImprovementLoop(options.retrieval);
|
|
2521
|
+
}, summarizeRetrievalResult);
|
|
2522
|
+
else skipPhase(phases, now, "retrieval-tuning", "no retrieval options provided");
|
|
2523
|
+
}
|
|
2524
|
+
if (phaseEnabled(options, "answer-quality")) {
|
|
2525
|
+
if (options.evaluateAnswers) {
|
|
2526
|
+
answerQuality = await runPhase(phases, now, "answer-quality", async () => {
|
|
2527
|
+
assertNotAborted(options.signal);
|
|
2528
|
+
const result = await options.evaluateAnswers({
|
|
2529
|
+
goal: options.goal,
|
|
2530
|
+
phases,
|
|
2531
|
+
optimization: selectRagOptimization(optimization),
|
|
2532
|
+
signal: options.signal,
|
|
2533
|
+
retrieval: selectRetrievalOptimization(retrieval),
|
|
2534
|
+
findings,
|
|
2535
|
+
acquisition,
|
|
2536
|
+
knowledgeUpdate
|
|
2537
|
+
});
|
|
2538
|
+
assertRagAnswerEvidence(result);
|
|
2539
|
+
return result;
|
|
2540
|
+
}, summarizeAnswerQuality);
|
|
2541
|
+
findings = [...findings, ...answerQuality.findings ?? []];
|
|
2542
|
+
} else skipPhase(phases, now, "answer-quality", "no answer-quality hook provided");
|
|
2543
|
+
}
|
|
2544
|
+
if (phaseEnabled(options, "promotion")) {
|
|
2545
|
+
if (options.decidePromotion) promotion = await runPhase(phases, now, "promotion", async () => {
|
|
2546
|
+
assertNotAborted(options.signal);
|
|
2547
|
+
const evidenceRejection = rejectUnsafePromotionEvidence({
|
|
2548
|
+
optimization: optimization?.comparison,
|
|
2549
|
+
optimizationCostCeiling: options.optimization?.costCeiling,
|
|
2550
|
+
retrieval: retrieval?.comparison,
|
|
2551
|
+
retrievalCostCeiling: options.retrieval?.costCeiling,
|
|
2552
|
+
answerQuality,
|
|
2553
|
+
answerQualityCostCeiling: options.answerQualityCostCeiling
|
|
2554
|
+
});
|
|
2555
|
+
if (evidenceRejection) return evidenceRejection;
|
|
2556
|
+
return options.decidePromotion({
|
|
2557
|
+
goal: options.goal,
|
|
2558
|
+
phases,
|
|
2559
|
+
optimization: selectRagOptimization(optimization),
|
|
2560
|
+
optimizationComparison: optimization?.comparison,
|
|
2561
|
+
signal: options.signal,
|
|
2522
2562
|
retrieval: selectRetrievalOptimization(retrieval),
|
|
2563
|
+
retrievalComparison: retrieval?.comparison,
|
|
2523
2564
|
findings,
|
|
2524
2565
|
acquisition,
|
|
2525
|
-
knowledgeUpdate
|
|
2566
|
+
knowledgeUpdate,
|
|
2567
|
+
answerQuality
|
|
2526
2568
|
});
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
findings = [...findings, ...answerQuality.findings ?? []];
|
|
2531
|
-
} else skipPhase(phases, now, "answer-quality", "no answer-quality hook provided");
|
|
2532
|
-
if (phaseEnabled(options, "promotion")) if (options.decidePromotion) promotion = await runPhase(phases, now, "promotion", async () => {
|
|
2533
|
-
assertNotAborted(options.signal);
|
|
2534
|
-
const evidenceRejection = rejectUnsafePromotionEvidence({
|
|
2535
|
-
optimization: optimization?.comparison,
|
|
2536
|
-
optimizationCostCeiling: options.optimization?.costCeiling,
|
|
2537
|
-
retrieval: retrieval?.comparison,
|
|
2538
|
-
retrievalCostCeiling: options.retrieval?.costCeiling,
|
|
2539
|
-
answerQuality,
|
|
2540
|
-
answerQualityCostCeiling: options.answerQualityCostCeiling
|
|
2541
|
-
});
|
|
2542
|
-
if (evidenceRejection) return evidenceRejection;
|
|
2543
|
-
return options.decidePromotion({
|
|
2544
|
-
goal: options.goal,
|
|
2545
|
-
phases,
|
|
2546
|
-
optimization: selectRagOptimization(optimization),
|
|
2547
|
-
optimizationComparison: optimization?.comparison,
|
|
2548
|
-
signal: options.signal,
|
|
2549
|
-
retrieval: selectRetrievalOptimization(retrieval),
|
|
2550
|
-
retrievalComparison: retrieval?.comparison,
|
|
2551
|
-
findings,
|
|
2552
|
-
acquisition,
|
|
2553
|
-
knowledgeUpdate,
|
|
2554
|
-
answerQuality
|
|
2555
|
-
});
|
|
2556
|
-
}, (result) => `${result.promoted ? "promoted" : "held"}: ${result.reason}`);
|
|
2557
|
-
else skipPhase(phases, now, "promotion", "no promotion decision hook provided");
|
|
2569
|
+
}, (result) => `${result.promoted ? "promoted" : "held"}: ${result.reason}`);
|
|
2570
|
+
else skipPhase(phases, now, "promotion", "no promotion decision hook provided");
|
|
2571
|
+
}
|
|
2558
2572
|
return {
|
|
2559
2573
|
goal: options.goal,
|
|
2560
2574
|
phases,
|
|
@@ -2801,9 +2815,11 @@ async function withMeasuredCandidateSnapshot(liveRoot, runDir, state, candidateR
|
|
|
2801
2815
|
await assertStateIdentity(liveRoot, candidateRef, state);
|
|
2802
2816
|
const candidate = state.candidates.find((entry) => entry.candidateId === candidateRef.candidateId);
|
|
2803
2817
|
if (!candidate) throw new Error(`knowledge candidate '${candidateRef.candidateId}' does not exist`);
|
|
2804
|
-
|
|
2818
|
+
const expectedRef = candidateRefFor(candidateRef.runId, state, candidate);
|
|
2819
|
+
if (canonicalJson(expectedRef) !== canonicalJson(candidateRef)) throw new Error("knowledge candidate approval does not match the measured candidate");
|
|
2805
2820
|
const evidence = await assertCandidateEvidence(runDir, candidateRef, state.implementationRef);
|
|
2806
|
-
|
|
2821
|
+
const relativePath = join("candidates", candidate.candidateId, "snapshots", candidateRef.candidateHash);
|
|
2822
|
+
return withSafeDirectory(runDir, relativePath, false, async (root) => {
|
|
2807
2823
|
if (await hashKnowledgeBase(root, state.stateScope) !== candidateRef.candidateHash) throw new Error("knowledge candidate snapshot changed after approval");
|
|
2808
2824
|
const result = await use({
|
|
2809
2825
|
root,
|
|
@@ -2906,7 +2922,8 @@ async function withBaselineSnapshot(runDir, expectedHash, use, scope) {
|
|
|
2906
2922
|
});
|
|
2907
2923
|
}
|
|
2908
2924
|
async function withFrozenCandidateWorkspace(runDir, candidate, candidateRoot, use, scope) {
|
|
2909
|
-
|
|
2925
|
+
const snapshotsPath = join("candidates", safePathSegmentSchema.parse(candidate.candidateId), "snapshots");
|
|
2926
|
+
return withSafeDirectory(runDir, snapshotsPath, true, async (snapshotsDir) => {
|
|
2910
2927
|
const preparation = await mkdtemp(join(snapshotsDir, "prepare-"));
|
|
2911
2928
|
let activated = false;
|
|
2912
2929
|
try {
|
|
@@ -3325,7 +3342,8 @@ async function knowledgePlanMutations(targetRoot, plan) {
|
|
|
3325
3342
|
}));
|
|
3326
3343
|
}
|
|
3327
3344
|
function assertCandidateTransitionPlan(plan, candidate, target, scope) {
|
|
3328
|
-
const
|
|
3345
|
+
const approvedDirection = target === "candidate" ? plan : reverseKnowledgeFilePlan(plan);
|
|
3346
|
+
const actualPlanHash = knowledgeFileTransactionPlanHash(approvedDirection, normalizeKnowledgeStateScope(scope).pagesDirectory, scope?.researchState);
|
|
3329
3347
|
if (actualPlanHash !== candidate.promotionPlanHash) throw new Error(`knowledge candidate plan changed after approval: expected ${candidate.promotionPlanHash}, got ${actualPlanHash}`);
|
|
3330
3348
|
}
|
|
3331
3349
|
function assertCandidateTransitionTransaction(transaction, candidate, target, scope) {
|
|
@@ -3929,7 +3947,7 @@ async function improveSelectedKnowledgeCandidate(options) {
|
|
|
3929
3947
|
if (sourcePlanHash !== sourceCandidate.promotionPlanHash) throw new Error("source knowledge candidate plan no longer matches its measured identity");
|
|
3930
3948
|
const changedSourcePlan = sourcePlan.filter(planEntryChanged).filter((entry) => notDerivedPath(entry, scope));
|
|
3931
3949
|
const selectedPaths = normalizeSelectedPaths(options.selectedPaths, changedSourcePlan, scope);
|
|
3932
|
-
const
|
|
3950
|
+
const selectionMaterial = immutableJson({
|
|
3933
3951
|
kind: "measured-knowledge-change-selection",
|
|
3934
3952
|
version: 1,
|
|
3935
3953
|
sourceCandidate,
|
|
@@ -3937,7 +3955,8 @@ async function improveSelectedKnowledgeCandidate(options) {
|
|
|
3937
3955
|
selectedPaths,
|
|
3938
3956
|
...options.rationale?.trim() ? { rationale: options.rationale.trim() } : {},
|
|
3939
3957
|
...metadata ? { metadata } : {}
|
|
3940
|
-
})
|
|
3958
|
+
});
|
|
3959
|
+
const selectionDigest = contentHash(selectionMaterial);
|
|
3941
3960
|
const derivedImplementationRef = immutableRefSchema.parse(`sha256:${contentHash({
|
|
3942
3961
|
engine: "agent-knowledge/measured-selection-v1",
|
|
3943
3962
|
implementationRef: requestedImplementationRef,
|
|
@@ -4112,7 +4131,8 @@ async function persistSelectionReceipt(root, receipt) {
|
|
|
4112
4131
|
const runDir = knowledgeImprovementRunDir(root, receipt.runId);
|
|
4113
4132
|
const relativePath = join("candidates", safePathSegmentSchema.parse(receipt.derivedCandidateId), "selection.json").replace(/\\/g, "/");
|
|
4114
4133
|
try {
|
|
4115
|
-
|
|
4134
|
+
const existing = measuredSelectionReceiptSchema.parse(JSON.parse((await readRegularFileWithinRoot(runDir, relativePath)).bytes.toString("utf8")));
|
|
4135
|
+
if (canonicalJson(existing) !== canonicalJson(receipt)) throw new Error("measured knowledge selection receipt conflicts with durable content");
|
|
4116
4136
|
return;
|
|
4117
4137
|
} catch (error) {
|
|
4118
4138
|
if (!isMissingFile(error)) throw error;
|
|
@@ -5527,17 +5547,22 @@ async function materialFactsSurfaced(kb, checklist) {
|
|
|
5527
5547
|
* and `searchKnowledge` — and reinvents none of them.
|
|
5528
5548
|
*/
|
|
5529
5549
|
async function runVerifiedResearchLoop(options) {
|
|
5550
|
+
const assertActive = () => options.signal?.throwIfAborted();
|
|
5551
|
+
assertActive();
|
|
5530
5552
|
const maxRounds = Math.max(1, options.maxRounds ?? 3);
|
|
5531
5553
|
await initKnowledgeBase(options.root);
|
|
5554
|
+
assertActive();
|
|
5532
5555
|
const store = new FileSystemKbStore({ root: options.root });
|
|
5533
5556
|
const steps = [];
|
|
5534
5557
|
let index = await buildKnowledgeIndex(options.root);
|
|
5558
|
+
assertActive();
|
|
5535
5559
|
await confirmRegisteredSources(options.driver, index.sources);
|
|
5560
|
+
assertActive();
|
|
5536
5561
|
let readiness = readinessFor(options, index);
|
|
5537
5562
|
let ready = isReady(readiness?.report) && (options.driver.isComplete?.() ?? true);
|
|
5538
5563
|
let steer;
|
|
5539
5564
|
for (let round = 1; round <= maxRounds && !ready; round++) {
|
|
5540
|
-
|
|
5565
|
+
assertActive();
|
|
5541
5566
|
const gaps = gapsFromReadiness(readiness);
|
|
5542
5567
|
const workerContribution = await options.worker({
|
|
5543
5568
|
root: options.root,
|
|
@@ -5549,6 +5574,7 @@ async function runVerifiedResearchLoop(options) {
|
|
|
5549
5574
|
readiness: requireReadiness(readiness, options),
|
|
5550
5575
|
signal: options.signal
|
|
5551
5576
|
});
|
|
5577
|
+
assertActive();
|
|
5552
5578
|
const accepted = [];
|
|
5553
5579
|
const rejectedWorkerSources = [];
|
|
5554
5580
|
const existingUris = new Set(index.sources.flatMap((source) => typeof source.metadata?.originalUri === "string" ? [source.metadata.originalUri] : []));
|
|
@@ -5570,6 +5596,7 @@ async function runVerifiedResearchLoop(options) {
|
|
|
5570
5596
|
acceptedThisRound: accepted,
|
|
5571
5597
|
signal: options.signal
|
|
5572
5598
|
});
|
|
5599
|
+
assertActive();
|
|
5573
5600
|
if (verdict.accept) accepted.push(source);
|
|
5574
5601
|
else rejectedWorkerSources.push({
|
|
5575
5602
|
source,
|
|
@@ -5577,10 +5604,14 @@ async function runVerifiedResearchLoop(options) {
|
|
|
5577
5604
|
});
|
|
5578
5605
|
}
|
|
5579
5606
|
const acceptedWorkerSources = await registerSources(options, accepted);
|
|
5607
|
+
assertActive();
|
|
5580
5608
|
await confirmRegisteredSources(options.driver, acceptedWorkerSources);
|
|
5609
|
+
assertActive();
|
|
5581
5610
|
const writtenPages = [];
|
|
5582
5611
|
writtenPages.push(...await applyPages(options.root, workerContribution, acceptedWorkerSources));
|
|
5612
|
+
assertActive();
|
|
5583
5613
|
index = await buildKnowledgeIndex(options.root);
|
|
5614
|
+
assertActive();
|
|
5584
5615
|
readiness = readinessFor(options, index);
|
|
5585
5616
|
let driverSources = [];
|
|
5586
5617
|
let driverNotes;
|
|
@@ -5595,11 +5626,16 @@ async function runVerifiedResearchLoop(options) {
|
|
|
5595
5626
|
readiness: requireReadiness(readiness, options),
|
|
5596
5627
|
signal: options.signal
|
|
5597
5628
|
});
|
|
5629
|
+
assertActive();
|
|
5598
5630
|
driverNotes = driverContribution.notes;
|
|
5599
5631
|
driverSources = await registerSources(options, driverContribution.sources ?? []);
|
|
5632
|
+
assertActive();
|
|
5600
5633
|
await confirmRegisteredSources(options.driver, driverSources);
|
|
5634
|
+
assertActive();
|
|
5601
5635
|
writtenPages.push(...await applyPages(options.root, driverContribution, driverSources));
|
|
5636
|
+
assertActive();
|
|
5602
5637
|
index = await buildKnowledgeIndex(options.root);
|
|
5638
|
+
assertActive();
|
|
5603
5639
|
readiness = readinessFor(options, index);
|
|
5604
5640
|
}
|
|
5605
5641
|
const driverComplete = options.driver.isComplete?.() ?? true;
|
|
@@ -5608,6 +5644,7 @@ async function runVerifiedResearchLoop(options) {
|
|
|
5608
5644
|
if (ready || remainingGaps.length === 0 && driverComplete) steer = void 0;
|
|
5609
5645
|
else {
|
|
5610
5646
|
await options.driver.prepareFold?.();
|
|
5647
|
+
assertActive();
|
|
5611
5648
|
steer = foldGaps(options.driver, remainingGaps);
|
|
5612
5649
|
}
|
|
5613
5650
|
const step = {
|
|
@@ -5640,10 +5677,14 @@ async function runVerifiedResearchLoop(options) {
|
|
|
5640
5677
|
}
|
|
5641
5678
|
};
|
|
5642
5679
|
await options.driver.checkpoint?.();
|
|
5680
|
+
assertActive();
|
|
5643
5681
|
await store.putEvent(step.event);
|
|
5682
|
+
assertActive();
|
|
5644
5683
|
steps.push(step);
|
|
5645
5684
|
await options.onRound?.(step);
|
|
5685
|
+
assertActive();
|
|
5646
5686
|
}
|
|
5687
|
+
assertActive();
|
|
5647
5688
|
return {
|
|
5648
5689
|
root: options.root,
|
|
5649
5690
|
goal: options.goal,
|
|
@@ -5685,8 +5726,10 @@ function isDuplicate(source, existingUris, accepted) {
|
|
|
5685
5726
|
async function registerSources(options, sources) {
|
|
5686
5727
|
const records = [];
|
|
5687
5728
|
for (const candidate of sources) {
|
|
5729
|
+
options.signal?.throwIfAborted();
|
|
5688
5730
|
const source = snapshotSourceTextInput(candidate);
|
|
5689
5731
|
records.push(await addSourceText(options.root, source, options.sourceOptions));
|
|
5732
|
+
options.signal?.throwIfAborted();
|
|
5690
5733
|
}
|
|
5691
5734
|
return records;
|
|
5692
5735
|
}
|
|
@@ -5859,7 +5902,7 @@ function thesisSynthesisMessages(input, kbText) {
|
|
|
5859
5902
|
async function writeThesisPage(root, input, thesis) {
|
|
5860
5903
|
return withKnowledgeMutation(root, async () => {
|
|
5861
5904
|
const relativePath = `knowledge/thesis-${slugify(input.ticker)}.md`;
|
|
5862
|
-
|
|
5905
|
+
const body = [
|
|
5863
5906
|
"---",
|
|
5864
5907
|
`title: Investment thesis — ${input.company} (${input.ticker})`,
|
|
5865
5908
|
`ticker: ${input.ticker}`,
|
|
@@ -5870,7 +5913,8 @@ async function writeThesisPage(root, input, thesis) {
|
|
|
5870
5913
|
"",
|
|
5871
5914
|
thesis.trim(),
|
|
5872
5915
|
""
|
|
5873
|
-
].join("\n")
|
|
5916
|
+
].join("\n");
|
|
5917
|
+
await writeFileDurableWithinRoot(root, relativePath, body, { encoding: "utf8" });
|
|
5874
5918
|
return join(root, relativePath);
|
|
5875
5919
|
});
|
|
5876
5920
|
}
|
|
@@ -5942,7 +5986,7 @@ function buildKnowledgeBrief(visiblePages, question, options = {}) {
|
|
|
5942
5986
|
origin: entry.origin
|
|
5943
5987
|
});
|
|
5944
5988
|
const originals = /* @__PURE__ */ new Map();
|
|
5945
|
-
const
|
|
5989
|
+
const pages = visiblePages.map((entry) => {
|
|
5946
5990
|
const page = {
|
|
5947
5991
|
...entry.page,
|
|
5948
5992
|
id: qualified(entry),
|
|
@@ -5959,7 +6003,8 @@ function buildKnowledgeBrief(visiblePages, question, options = {}) {
|
|
|
5959
6003
|
};
|
|
5960
6004
|
originals.set(page, entry);
|
|
5961
6005
|
return page;
|
|
5962
|
-
})
|
|
6006
|
+
});
|
|
6007
|
+
const ranked = searchKnowledgePages(pages, question, {
|
|
5963
6008
|
limit,
|
|
5964
6009
|
excludeInvalidated,
|
|
5965
6010
|
...options.tags === void 0 ? {} : { tags: options.tags },
|
|
@@ -6296,7 +6341,7 @@ function verifyKnowledgeRetrievalReceipt(receipt) {
|
|
|
6296
6341
|
if (receipt.digestAlgorithm !== "rfc8785-sha256") throw new Error(`unsupported knowledge retrieval digestAlgorithm '${receipt.digestAlgorithm}'`);
|
|
6297
6342
|
const visibility = normalizeVisibilityRef(receipt.visibility);
|
|
6298
6343
|
validateReceiptResultShape(receipt.results, visibility.pageCount);
|
|
6299
|
-
|
|
6344
|
+
const material = retrievalMaterial({
|
|
6300
6345
|
createdAt: isoTimestamp(receipt.createdAt, "knowledge retrieval createdAt"),
|
|
6301
6346
|
runId: nonEmpty$1(receipt.runId, "knowledge retrieval runId"),
|
|
6302
6347
|
actorId: optionalText(receipt.actorId, "knowledge retrieval actorId"),
|
|
@@ -6308,7 +6353,8 @@ function verifyKnowledgeRetrievalReceipt(receipt) {
|
|
|
6308
6353
|
results: receipt.results,
|
|
6309
6354
|
evidenceRefs: normalizeEvidenceRefs(receipt.evidenceRefs),
|
|
6310
6355
|
attributes: normalizeAttributes(receipt.attributes)
|
|
6311
|
-
})
|
|
6356
|
+
});
|
|
6357
|
+
if (canonicalCandidateDigest(material) !== receipt.receiptDigest) throw new Error("knowledge retrieval receipt digest mismatch");
|
|
6312
6358
|
return receipt;
|
|
6313
6359
|
}
|
|
6314
6360
|
/**
|
|
@@ -6400,7 +6446,7 @@ function verifyKnowledgeUseReceipt(receipt, retrieval) {
|
|
|
6400
6446
|
if (receipt.retrievalReceiptDigest !== verifiedRetrieval.receiptDigest) throw new Error("knowledge use receipt references a different retrieval receipt");
|
|
6401
6447
|
const selected = verifiedRetrieval.results.find((result) => result.rank === receipt.used.rank);
|
|
6402
6448
|
if (!selected || selected.pageId !== receipt.used.pageId || selected.origin !== receipt.used.origin || selected.path !== receipt.used.path || selected.pageDigest !== receipt.used.pageDigest) throw new Error("knowledge use receipt selected result does not match the retrieval receipt");
|
|
6403
|
-
|
|
6449
|
+
const material = useMaterial({
|
|
6404
6450
|
createdAt: isoTimestamp(receipt.createdAt, "knowledge use createdAt"),
|
|
6405
6451
|
retrieval: verifiedRetrieval,
|
|
6406
6452
|
used: receipt.used,
|
|
@@ -6408,7 +6454,8 @@ function verifyKnowledgeUseReceipt(receipt, retrieval) {
|
|
|
6408
6454
|
consumer: normalizeConsumer(receipt.consumer),
|
|
6409
6455
|
evidenceRefs: normalizeEvidenceRefs(receipt.evidenceRefs),
|
|
6410
6456
|
attributes: normalizeAttributes(receipt.attributes)
|
|
6411
|
-
})
|
|
6457
|
+
});
|
|
6458
|
+
if (canonicalCandidateDigest(material) !== receipt.receiptDigest) throw new Error("knowledge use receipt digest mismatch");
|
|
6412
6459
|
return receipt;
|
|
6413
6460
|
}
|
|
6414
6461
|
/** Record that one retrieval led to no use, with no rank to select. */
|
|
@@ -6435,14 +6482,15 @@ function verifyKnowledgeRetrievalDisposition(disposition, retrieval) {
|
|
|
6435
6482
|
if (disposition.kind !== "knowledge-retrieval-disposition") throw new Error(`knowledge retrieval disposition kind must be 'knowledge-retrieval-disposition'`);
|
|
6436
6483
|
if (disposition.digestAlgorithm !== "rfc8785-sha256") throw new Error(`unsupported knowledge retrieval disposition digestAlgorithm '${disposition.digestAlgorithm}'`);
|
|
6437
6484
|
if (disposition.retrievalReceiptDigest !== verifiedRetrieval.receiptDigest) throw new Error("knowledge retrieval disposition references a different retrieval receipt");
|
|
6438
|
-
|
|
6485
|
+
const material = dispositionMaterial({
|
|
6439
6486
|
createdAt: isoTimestamp(disposition.createdAt, "knowledge retrieval disposition createdAt"),
|
|
6440
6487
|
retrieval: verifiedRetrieval,
|
|
6441
6488
|
relation: validateDispositionRelation(disposition.relation),
|
|
6442
6489
|
consumer: normalizeConsumer(disposition.consumer),
|
|
6443
6490
|
evidenceRefs: normalizeEvidenceRefs(disposition.evidenceRefs),
|
|
6444
6491
|
attributes: normalizeAttributes(disposition.attributes)
|
|
6445
|
-
})
|
|
6492
|
+
});
|
|
6493
|
+
if (canonicalCandidateDigest(material) !== disposition.receiptDigest) throw new Error("knowledge retrieval disposition digest mismatch");
|
|
6446
6494
|
return disposition;
|
|
6447
6495
|
}
|
|
6448
6496
|
function assertReceiptJoinsVisibility(receipt, index) {
|
|
@@ -6856,7 +6904,8 @@ async function promoteRunScopedPages(stores, runId, options) {
|
|
|
6856
6904
|
const existing = await loadKnowledgePages(sharedRoot, { pagesDirectory });
|
|
6857
6905
|
assertNoIdentityConflict(travellers, existing);
|
|
6858
6906
|
const promotedPaths = new Set(travellers.map((traveller) => traveller.entry.page.path));
|
|
6859
|
-
|
|
6907
|
+
const promotedView = originatedPages([...existing.filter((page) => !promotedPaths.has(page.path)), ...travellers.map((traveller) => traveller.entry.page)], "shared");
|
|
6908
|
+
assertKnowledgeCitationsResolved(promotedView, travellers.flatMap((traveller) => (traveller.entry.page.cites ?? []).map((persisted) => parseKnowledgeCitationReference(persisted))));
|
|
6860
6909
|
await commitKnowledgeFileMutations({
|
|
6861
6910
|
root: sharedRoot,
|
|
6862
6911
|
transactionRoot: lock.transactionRoot,
|
|
@@ -7672,8 +7721,18 @@ function buildDriver(options, persistence, restored) {
|
|
|
7672
7721
|
}
|
|
7673
7722
|
if (goal !== nextGoal) throw new Error(`claim ledger '${persistence?.ledgerId ?? "in-memory"}' accumulated evidence for goal '${goal}' and cannot be reused for '${nextGoal}'`);
|
|
7674
7723
|
}
|
|
7675
|
-
function resolveRouter() {
|
|
7676
|
-
|
|
7724
|
+
function resolveRouter(signal) {
|
|
7725
|
+
if (options.router) return options.router;
|
|
7726
|
+
const configuredSignal = options.router_options?.signal;
|
|
7727
|
+
const requestSignal = configuredSignal && signal ? AbortSignal.any([configuredSignal, signal]) : configuredSignal ?? signal;
|
|
7728
|
+
return createTangleRouterClient({
|
|
7729
|
+
...options.router_options,
|
|
7730
|
+
signal: requestSignal
|
|
7731
|
+
});
|
|
7732
|
+
}
|
|
7733
|
+
function throwIfRouterAborted(signal) {
|
|
7734
|
+
signal?.throwIfAborted();
|
|
7735
|
+
if (!options.router) options.router_options?.signal?.throwIfAborted();
|
|
7677
7736
|
}
|
|
7678
7737
|
/** Record a claim from a source, growing its independent-source support. */
|
|
7679
7738
|
function recordClaim(extracted, sourceUri, round) {
|
|
@@ -7835,12 +7894,14 @@ function buildDriver(options, persistence, restored) {
|
|
|
7835
7894
|
* because such a source cannot drive the research and pollutes the KB.
|
|
7836
7895
|
*/
|
|
7837
7896
|
async verifySource(source, ctx) {
|
|
7897
|
+
throwIfRouterAborted(ctx.signal);
|
|
7838
7898
|
const sourceSnapshot = snapshotSourceTextInput(source);
|
|
7839
7899
|
const goalSnapshot = ctx.goal;
|
|
7840
7900
|
const roundSnapshot = ctx.round;
|
|
7841
7901
|
const sourceVersion = sourceVersionOfProposal(sourceSnapshot);
|
|
7842
7902
|
bindGoal(goalSnapshot);
|
|
7843
|
-
const extracted = await extractClaims(sourceSnapshot, goalSnapshot);
|
|
7903
|
+
const extracted = await extractClaims(sourceSnapshot, goalSnapshot, ctx.signal);
|
|
7904
|
+
throwIfRouterAborted(ctx.signal);
|
|
7844
7905
|
if (extracted.length === 0) return {
|
|
7845
7906
|
accept: false,
|
|
7846
7907
|
reason: "no extractable claim: source yields nothing to drive the research deeper"
|
|
@@ -7855,6 +7916,7 @@ function buildDriver(options, persistence, restored) {
|
|
|
7855
7916
|
if (registered && researchSourceVersionKey(registered) === researchSourceVersionKey(sourceVersion)) markAddressed(materializeEvidenceFor(/* @__PURE__ */ new Set([researchSourceVersionKey(sourceVersion)])));
|
|
7856
7917
|
}
|
|
7857
7918
|
await persist();
|
|
7919
|
+
throwIfRouterAborted(ctx.signal);
|
|
7858
7920
|
return { accept: true };
|
|
7859
7921
|
},
|
|
7860
7922
|
/**
|
|
@@ -7898,8 +7960,9 @@ function buildDriver(options, persistence, restored) {
|
|
|
7898
7960
|
commitSources,
|
|
7899
7961
|
toLedger
|
|
7900
7962
|
};
|
|
7901
|
-
async function extractClaims(source, goal) {
|
|
7902
|
-
const fromLlm = await extractClaimsWithLlm(source, goal, claimsForExtraction());
|
|
7963
|
+
async function extractClaims(source, goal, signal) {
|
|
7964
|
+
const fromLlm = await extractClaimsWithLlm(source, goal, claimsForExtraction(), signal);
|
|
7965
|
+
throwIfRouterAborted(signal);
|
|
7903
7966
|
if (fromLlm.length > 0) return fromLlm.slice(0, maxClaimsPerSource);
|
|
7904
7967
|
if (deterministicFallback) return deterministicClaims(source).slice(0, maxClaimsPerSource);
|
|
7905
7968
|
return [];
|
|
@@ -7920,11 +7983,13 @@ function buildDriver(options, persistence, restored) {
|
|
|
7920
7983
|
}
|
|
7921
7984
|
return [...known.values()];
|
|
7922
7985
|
}
|
|
7923
|
-
async function extractClaimsWithLlm(source, goal, ledger) {
|
|
7986
|
+
async function extractClaimsWithLlm(source, goal, ledger, signal) {
|
|
7924
7987
|
let router;
|
|
7925
7988
|
try {
|
|
7926
|
-
router = resolveRouter();
|
|
7927
|
-
} catch {
|
|
7989
|
+
router = resolveRouter(signal);
|
|
7990
|
+
} catch (error) {
|
|
7991
|
+
throwIfRouterAborted(signal);
|
|
7992
|
+
if (error?.name === "AbortError") throw error;
|
|
7928
7993
|
return [];
|
|
7929
7994
|
}
|
|
7930
7995
|
const excerpt = source.text.slice(0, 1800);
|
|
@@ -7946,9 +8011,12 @@ function buildDriver(options, persistence, restored) {
|
|
|
7946
8011
|
role: "user",
|
|
7947
8012
|
content: user
|
|
7948
8013
|
}], 1200);
|
|
7949
|
-
} catch {
|
|
8014
|
+
} catch (error) {
|
|
8015
|
+
throwIfRouterAborted(signal);
|
|
8016
|
+
if (error?.name === "AbortError") throw error;
|
|
7950
8017
|
return [];
|
|
7951
8018
|
}
|
|
8019
|
+
throwIfRouterAborted(signal);
|
|
7952
8020
|
return parseExtractedClaims(raw, ledger);
|
|
7953
8021
|
}
|
|
7954
8022
|
/**
|