@remnic/core 9.3.639 → 9.3.641
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/access-cli.js +26 -26
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +16 -16
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +15 -15
- package/dist/{access-service-jyYute38.d.ts → access-service-DFXIlGvZ.d.ts} +2 -2
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +14 -14
- package/dist/action-confidence.d.ts +2 -2
- package/dist/active-memory-bridge.d.ts +2 -2
- package/dist/active-recall.d.ts +2 -2
- package/dist/behavior-learner.d.ts +2 -2
- package/dist/behavior-signals.d.ts +2 -2
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +2 -2
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/bulk-import/index.js +2 -2
- package/dist/calibration.d.ts +2 -2
- package/dist/causal-behavior.d.ts +2 -2
- package/dist/causal-consolidation.d.ts +2 -2
- package/dist/causal-consolidation.js +4 -4
- package/dist/{chunk-2BHNQRE5.js → chunk-3UR4SBZF.js} +314 -77
- package/dist/chunk-3UR4SBZF.js.map +1 -0
- package/dist/{chunk-RJGPH2Q3.js → chunk-4HYLRROV.js} +2 -2
- package/dist/{chunk-3AZDLWDW.js → chunk-4HYWWO3W.js} +3 -3
- package/dist/{chunk-ISMZIMD3.js → chunk-4MNDVDBS.js} +2 -2
- package/dist/{chunk-4VTVCWVB.js → chunk-5IDVOWBE.js} +4 -4
- package/dist/{chunk-BB6FNMMB.js → chunk-6DDBW2V4.js} +5 -5
- package/dist/{chunk-EDQVAMQI.js → chunk-6GIKAUTN.js} +2 -2
- package/dist/{chunk-4HFJQCJZ.js → chunk-7HYPN2GC.js} +2 -2
- package/dist/{chunk-Q6YIJGXJ.js → chunk-7OGJQP7T.js} +2 -2
- package/dist/{chunk-RW5DGAGO.js → chunk-7V36WCUW.js} +2 -2
- package/dist/{chunk-LUDUFZTV.js → chunk-ABP6QMVM.js} +2 -2
- package/dist/chunk-ABP6QMVM.js.map +1 -0
- package/dist/{chunk-FGRV3X3Y.js → chunk-ACNU2UBF.js} +18 -18
- package/dist/{chunk-52XI77OQ.js → chunk-AK4DSORQ.js} +9 -9
- package/dist/{chunk-CMZLMIZI.js → chunk-BEMWL2FZ.js} +2 -2
- package/dist/{chunk-3F7RMHC4.js → chunk-C43KEWEV.js} +1 -1
- package/dist/{chunk-3F7RMHC4.js.map → chunk-C43KEWEV.js.map} +1 -1
- package/dist/{chunk-H2NCNXMS.js → chunk-CKITWVWJ.js} +2 -2
- package/dist/{chunk-AFH2JXSF.js → chunk-DM4US77W.js} +3 -3
- package/dist/{chunk-ORFGK3XI.js → chunk-E6ZDCOHM.js} +2 -2
- package/dist/{chunk-SBHQBC37.js → chunk-ECMMM2VE.js} +2 -2
- package/dist/{chunk-TKWGAOLV.js → chunk-EEC4PCG5.js} +2 -2
- package/dist/{chunk-BTY5XQG3.js → chunk-EGBH5IPJ.js} +4 -4
- package/dist/{chunk-YR6GIWWY.js → chunk-GQL52GQ5.js} +2 -2
- package/dist/{chunk-SHHT4JTC.js → chunk-GYITUZFT.js} +2 -2
- package/dist/{chunk-77NAFXUD.js → chunk-H3HDXD3U.js} +2 -2
- package/dist/{chunk-LASA46TQ.js → chunk-HRXCPKN4.js} +2 -2
- package/dist/{chunk-EKIQBGAY.js → chunk-IENGGY2C.js} +2 -2
- package/dist/{chunk-RAGXUVQW.js → chunk-KVUG7FF7.js} +4 -4
- package/dist/{chunk-DEDTFRH2.js → chunk-MUKXANAM.js} +14 -3
- package/dist/chunk-MUKXANAM.js.map +1 -0
- package/dist/{chunk-UU5LHD6J.js → chunk-NLPH23IQ.js} +2 -2
- package/dist/{chunk-A3UPMOJZ.js → chunk-NV2MGIW7.js} +2 -2
- package/dist/chunk-O75CRYGF.js +15 -0
- package/dist/chunk-O75CRYGF.js.map +1 -0
- package/dist/{chunk-PU44GBL4.js → chunk-OUELRE5E.js} +1 -1
- package/dist/chunk-OUELRE5E.js.map +1 -0
- package/dist/{chunk-J2KFC47S.js → chunk-RFSCIBCB.js} +2 -2
- package/dist/{chunk-IXSTQGYV.js → chunk-RREI7CNM.js} +2 -2
- package/dist/{chunk-C6XO6ONP.js → chunk-SZL2GGFY.js} +2 -2
- package/dist/{chunk-7XRSLMUD.js → chunk-THWT7RJU.js} +2 -2
- package/dist/{chunk-H4RWKZE2.js → chunk-TKNTAUA3.js} +2 -2
- package/dist/{chunk-7OH7KPF5.js → chunk-TTOYT5RB.js} +2 -2
- package/dist/{chunk-UI2UP7XX.js → chunk-U3GQ33JC.js} +36 -7
- package/dist/chunk-U3GQ33JC.js.map +1 -0
- package/dist/{chunk-C3EJ3UIS.js → chunk-WLGE6KEO.js} +3 -3
- package/dist/{chunk-7R37SOT4.js → chunk-Y2XKJJEI.js} +2 -2
- package/dist/{cli-A4AIhv9e.d.ts → cli-DrL2Nv4j.d.ts} +5 -5
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +30 -30
- package/dist/compat/checks.js +2 -2
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.d.ts +2 -2
- package/dist/compression-optimizer.d.ts +2 -2
- package/dist/config.d.ts +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +2 -2
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +2 -2
- package/dist/connectors/index.d.ts +2 -2
- package/dist/connectors/index.js +5 -5
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/conversation-index/backend.d.ts +2 -2
- package/dist/conversation-index/backend.js +3 -3
- package/dist/conversation-index/chunker.d.ts +2 -2
- package/dist/conversation-index/faiss-adapter.d.ts +2 -2
- package/dist/conversation-index/faiss-adapter.js +2 -2
- package/dist/conversation-index/indexer.d.ts +2 -2
- package/dist/conversation-index/search.d.ts +2 -2
- package/dist/day-summary.d.ts +2 -2
- package/dist/delinearize.d.ts +2 -2
- package/dist/direct-answer-wiring.d.ts +2 -2
- package/dist/direct-answer.d.ts +2 -2
- package/dist/embedding-fallback.d.ts +2 -2
- package/dist/enrichment/index.d.ts +2 -2
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +2 -2
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/extraction-judge-telemetry.d.ts +2 -2
- package/dist/extraction-judge-training.d.ts +2 -2
- package/dist/extraction-judge.d.ts +2 -2
- package/dist/extraction.d.ts +2 -2
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.d.ts +2 -2
- package/dist/identity-continuity.d.ts +2 -2
- package/dist/importance.d.ts +2 -2
- package/dist/importers/index.d.ts +2 -2
- package/dist/importers/index.js +2 -2
- package/dist/index.d.ts +10 -10
- package/dist/index.js +41 -41
- package/dist/intent.d.ts +2 -2
- package/dist/lcm/engine.d.ts +2 -2
- package/dist/lcm/index.d.ts +2 -2
- package/dist/lcm/tools.d.ts +2 -2
- package/dist/lifecycle.d.ts +2 -2
- package/dist/live-connectors-runner.d.ts +2 -2
- package/dist/local-llm.d.ts +2 -2
- package/dist/local-llm.js +2 -2
- package/dist/maintenance/memory-governance.d.ts +2 -2
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +2 -2
- package/dist/memory-cache.d.ts +2 -2
- package/dist/memory-lifecycle-ledger-utils.d.ts +2 -2
- package/dist/memory-projection-store.d.ts +2 -2
- package/dist/memory-provenance.d.ts +2 -2
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/migrate/from-engram.js +2 -2
- package/dist/models-json.d.ts +2 -2
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +10 -10
- package/dist/namespaces/principal.d.ts +2 -2
- package/dist/namespaces/search.d.ts +2 -2
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +2 -2
- package/dist/network/tailscale.js +2 -2
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +12 -12
- package/dist/{orchestrator-BEDTiuEO.d.ts → orchestrator-DEQW9j0Z.d.ts} +22 -5
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +25 -23
- package/dist/patterns-cli.d.ts +2 -2
- package/dist/{pipeline-D18UAKlN.d.ts → pipeline-BSTuml0d.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +2 -2
- package/dist/qmd.d.ts +5 -2
- package/dist/qmd.js +2 -2
- package/dist/recall-disclosure-escalation.d.ts +2 -2
- package/dist/recall-explain-renderer.d.ts +2 -2
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +2 -2
- package/dist/recall-state.d.ts +2 -2
- package/dist/recall-tag-filter.d.ts +2 -2
- package/dist/recall-xray-cli.d.ts +2 -2
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +2 -2
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +2 -2
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +2 -2
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +2 -2
- package/dist/routing/engine.d.ts +2 -2
- package/dist/routing/store.d.ts +2 -2
- package/dist/runtime/child-process.js +1 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/search/embed-helper.d.ts +2 -2
- package/dist/search/factory.d.ts +2 -2
- package/dist/search/factory.js +5 -5
- package/dist/search/index.d.ts +2 -2
- package/dist/search/index.js +5 -5
- package/dist/search/lancedb-backend.d.ts +2 -2
- package/dist/search/meilisearch-backend.d.ts +2 -2
- package/dist/search/noop-backend.d.ts +2 -2
- package/dist/search/orama-backend.d.ts +2 -2
- package/dist/search/port.d.ts +2 -2
- package/dist/search/remote-backend.d.ts +2 -2
- package/dist/{semantic-consolidation-Byut1ufK.d.ts → semantic-consolidation-DKdYzQOg.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -3
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +2 -2
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-bands.d.ts +2 -2
- package/dist/session-observer-state.d.ts +2 -2
- package/dist/shared-context/manager.d.ts +2 -2
- package/dist/signal.d.ts +2 -2
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +2 -2
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +2 -2
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-validity.d.ts +2 -2
- package/dist/threading.d.ts +2 -2
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +2 -2
- package/dist/topics.d.ts +2 -2
- package/dist/transcript.d.ts +2 -2
- package/dist/transfer/types.d.ts +12 -12
- package/dist/{types-BliCnURB.d.ts → types-ByK7T3L6.d.ts} +2 -0
- package/dist/{types-BzA1FKbg.d.ts → types-D8yUmSik.d.ts} +2 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -2
- package/dist/verified-recall.js +3 -3
- package/package.json +3 -1
- package/src/bulk-import/types.ts +2 -0
- package/src/extraction-timeout.test.ts +6 -1
- package/src/extraction.ts +34 -5
- package/src/importers/base.ts +1 -1
- package/src/orchestrator-flush.test.ts +640 -42
- package/src/orchestrator.ts +389 -70
- package/src/qmd.test.ts +35 -0
- package/src/qmd.ts +16 -1
- package/src/runtime/child-process.test.ts +15 -0
- package/src/runtime/child-process.ts +9 -22
- package/src/types.ts +1 -0
- package/dist/chunk-2BHNQRE5.js.map +0 -1
- package/dist/chunk-DEDTFRH2.js.map +0 -1
- package/dist/chunk-LUDUFZTV.js.map +0 -1
- package/dist/chunk-NNVTUXEB.js +0 -23
- package/dist/chunk-NNVTUXEB.js.map +0 -1
- package/dist/chunk-PU44GBL4.js.map +0 -1
- package/dist/chunk-UI2UP7XX.js.map +0 -1
- /package/dist/{chunk-RJGPH2Q3.js.map → chunk-4HYLRROV.js.map} +0 -0
- /package/dist/{chunk-3AZDLWDW.js.map → chunk-4HYWWO3W.js.map} +0 -0
- /package/dist/{chunk-ISMZIMD3.js.map → chunk-4MNDVDBS.js.map} +0 -0
- /package/dist/{chunk-4VTVCWVB.js.map → chunk-5IDVOWBE.js.map} +0 -0
- /package/dist/{chunk-BB6FNMMB.js.map → chunk-6DDBW2V4.js.map} +0 -0
- /package/dist/{chunk-EDQVAMQI.js.map → chunk-6GIKAUTN.js.map} +0 -0
- /package/dist/{chunk-4HFJQCJZ.js.map → chunk-7HYPN2GC.js.map} +0 -0
- /package/dist/{chunk-Q6YIJGXJ.js.map → chunk-7OGJQP7T.js.map} +0 -0
- /package/dist/{chunk-RW5DGAGO.js.map → chunk-7V36WCUW.js.map} +0 -0
- /package/dist/{chunk-FGRV3X3Y.js.map → chunk-ACNU2UBF.js.map} +0 -0
- /package/dist/{chunk-52XI77OQ.js.map → chunk-AK4DSORQ.js.map} +0 -0
- /package/dist/{chunk-CMZLMIZI.js.map → chunk-BEMWL2FZ.js.map} +0 -0
- /package/dist/{chunk-H2NCNXMS.js.map → chunk-CKITWVWJ.js.map} +0 -0
- /package/dist/{chunk-AFH2JXSF.js.map → chunk-DM4US77W.js.map} +0 -0
- /package/dist/{chunk-ORFGK3XI.js.map → chunk-E6ZDCOHM.js.map} +0 -0
- /package/dist/{chunk-SBHQBC37.js.map → chunk-ECMMM2VE.js.map} +0 -0
- /package/dist/{chunk-TKWGAOLV.js.map → chunk-EEC4PCG5.js.map} +0 -0
- /package/dist/{chunk-BTY5XQG3.js.map → chunk-EGBH5IPJ.js.map} +0 -0
- /package/dist/{chunk-YR6GIWWY.js.map → chunk-GQL52GQ5.js.map} +0 -0
- /package/dist/{chunk-SHHT4JTC.js.map → chunk-GYITUZFT.js.map} +0 -0
- /package/dist/{chunk-77NAFXUD.js.map → chunk-H3HDXD3U.js.map} +0 -0
- /package/dist/{chunk-LASA46TQ.js.map → chunk-HRXCPKN4.js.map} +0 -0
- /package/dist/{chunk-EKIQBGAY.js.map → chunk-IENGGY2C.js.map} +0 -0
- /package/dist/{chunk-RAGXUVQW.js.map → chunk-KVUG7FF7.js.map} +0 -0
- /package/dist/{chunk-UU5LHD6J.js.map → chunk-NLPH23IQ.js.map} +0 -0
- /package/dist/{chunk-A3UPMOJZ.js.map → chunk-NV2MGIW7.js.map} +0 -0
- /package/dist/{chunk-J2KFC47S.js.map → chunk-RFSCIBCB.js.map} +0 -0
- /package/dist/{chunk-IXSTQGYV.js.map → chunk-RREI7CNM.js.map} +0 -0
- /package/dist/{chunk-C6XO6ONP.js.map → chunk-SZL2GGFY.js.map} +0 -0
- /package/dist/{chunk-7XRSLMUD.js.map → chunk-THWT7RJU.js.map} +0 -0
- /package/dist/{chunk-H4RWKZE2.js.map → chunk-TKNTAUA3.js.map} +0 -0
- /package/dist/{chunk-7OH7KPF5.js.map → chunk-TTOYT5RB.js.map} +0 -0
- /package/dist/{chunk-C3EJ3UIS.js.map → chunk-WLGE6KEO.js.map} +0 -0
- /package/dist/{chunk-7R37SOT4.js.map → chunk-Y2XKJJEI.js.map} +0 -0
package/src/orchestrator.ts
CHANGED
|
@@ -376,6 +376,42 @@ import type {
|
|
|
376
376
|
EntityTimelineEntry,
|
|
377
377
|
} from "./types.js";
|
|
378
378
|
|
|
379
|
+
export interface BulkImportBatchIngestResult {
|
|
380
|
+
attemptedTurnCount: number;
|
|
381
|
+
extractionCount: number;
|
|
382
|
+
persistedCount: number;
|
|
383
|
+
durableOutputCount: number;
|
|
384
|
+
skippedCount: number;
|
|
385
|
+
failedCount: number;
|
|
386
|
+
postPersistMetadataFailureCount: number;
|
|
387
|
+
processedTurnCount: number;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export class BulkImportBatchPartialFailureError extends Error {
|
|
391
|
+
readonly partialResult: BulkImportBatchIngestResult;
|
|
392
|
+
|
|
393
|
+
readonly originalError: unknown;
|
|
394
|
+
|
|
395
|
+
constructor(
|
|
396
|
+
message: string,
|
|
397
|
+
partialResult: BulkImportBatchIngestResult,
|
|
398
|
+
originalError: unknown,
|
|
399
|
+
) {
|
|
400
|
+
super(message);
|
|
401
|
+
this.name = "BulkImportBatchPartialFailureError";
|
|
402
|
+
this.partialResult = partialResult;
|
|
403
|
+
this.originalError = originalError;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
interface ExtractionRunResult {
|
|
408
|
+
status: "completed" | "skipped";
|
|
409
|
+
reason?: string;
|
|
410
|
+
persistedCount: number;
|
|
411
|
+
durableOutputCount: number;
|
|
412
|
+
postPersistMetadataFailed?: boolean;
|
|
413
|
+
}
|
|
414
|
+
|
|
379
415
|
export function dedupeEntitySynthesisEvidenceEntries(
|
|
380
416
|
entries: EntityTimelineEntry[],
|
|
381
417
|
): EntityTimelineEntry[] {
|
|
@@ -1032,7 +1068,10 @@ function sortSourceValidAtSlicesChronologically(
|
|
|
1032
1068
|
.map((slice) => slice.turns);
|
|
1033
1069
|
}
|
|
1034
1070
|
|
|
1035
|
-
function splitTurnsBySourceValidAt(
|
|
1071
|
+
function splitTurnsBySourceValidAt(
|
|
1072
|
+
turns: readonly BufferTurn[],
|
|
1073
|
+
options: { includeContext?: boolean } = {},
|
|
1074
|
+
): BufferTurn[][] {
|
|
1036
1075
|
if (turns.length === 0) return [];
|
|
1037
1076
|
if (!turns.some((turn) => sourceValidAtMs(turn) !== null)) {
|
|
1038
1077
|
return [[...turns]];
|
|
@@ -1051,13 +1090,12 @@ function splitTurnsBySourceValidAt(turns: readonly BufferTurn[]): BufferTurn[][]
|
|
|
1051
1090
|
end += 1;
|
|
1052
1091
|
}
|
|
1053
1092
|
|
|
1093
|
+
const contextTurns =
|
|
1094
|
+
options.includeContext === false
|
|
1095
|
+
? []
|
|
1096
|
+
: sourceValidAtContextTurns(turns, start, end, targetValidAtMs);
|
|
1054
1097
|
slices.push([
|
|
1055
|
-
...
|
|
1056
|
-
turns,
|
|
1057
|
-
start,
|
|
1058
|
-
end,
|
|
1059
|
-
targetValidAtMs,
|
|
1060
|
-
),
|
|
1098
|
+
...contextTurns,
|
|
1061
1099
|
...turns.slice(start, end).map(asExtractionTargetTurn),
|
|
1062
1100
|
]);
|
|
1063
1101
|
start = end;
|
|
@@ -11415,9 +11453,22 @@ export class Orchestrator {
|
|
|
11415
11453
|
turns: ImportTurn[],
|
|
11416
11454
|
options: {
|
|
11417
11455
|
deadlineMs?: number;
|
|
11456
|
+
failOnExtractionFailure?: boolean;
|
|
11457
|
+
includeSourceValidAtContext?: boolean;
|
|
11418
11458
|
} = {},
|
|
11419
|
-
): Promise<
|
|
11420
|
-
if (!Array.isArray(turns) || turns.length === 0)
|
|
11459
|
+
): Promise<BulkImportBatchIngestResult> {
|
|
11460
|
+
if (!Array.isArray(turns) || turns.length === 0) {
|
|
11461
|
+
return {
|
|
11462
|
+
attemptedTurnCount: 0,
|
|
11463
|
+
extractionCount: 0,
|
|
11464
|
+
persistedCount: 0,
|
|
11465
|
+
durableOutputCount: 0,
|
|
11466
|
+
skippedCount: 0,
|
|
11467
|
+
failedCount: 0,
|
|
11468
|
+
postPersistMetadataFailureCount: 0,
|
|
11469
|
+
processedTurnCount: 0,
|
|
11470
|
+
};
|
|
11471
|
+
}
|
|
11421
11472
|
|
|
11422
11473
|
// Per-batch unique sessionKey keeps threading honest without matching
|
|
11423
11474
|
// typical prefix/map routing rules. Combined with writeNamespaceOverride
|
|
@@ -11426,9 +11477,33 @@ export class Orchestrator {
|
|
|
11426
11477
|
// security-context insecure-randomness use even though this value never
|
|
11427
11478
|
// leaves the process; the bytes just need to be collision-resistant
|
|
11428
11479
|
// across concurrent bulk-import batches.
|
|
11429
|
-
const
|
|
11430
|
-
|
|
11431
|
-
|
|
11480
|
+
const shouldUseStableBatchKey = turns.some(
|
|
11481
|
+
(turn) =>
|
|
11482
|
+
turn.persistProcessedFingerprint === true ||
|
|
11483
|
+
(typeof turn.turnFingerprint === "string" &&
|
|
11484
|
+
turn.turnFingerprint.length > 0),
|
|
11485
|
+
);
|
|
11486
|
+
const stableBatchFingerprint = shouldUseStableBatchKey
|
|
11487
|
+
? createHash("sha256")
|
|
11488
|
+
.update(
|
|
11489
|
+
turns
|
|
11490
|
+
.map((turn) =>
|
|
11491
|
+
[
|
|
11492
|
+
turn.role,
|
|
11493
|
+
typeof turn.turnFingerprint === "string" &&
|
|
11494
|
+
turn.turnFingerprint.length > 0
|
|
11495
|
+
? turn.turnFingerprint
|
|
11496
|
+
: turn.content.replace(/\s+/g, " ").trim(),
|
|
11497
|
+
].join(":"),
|
|
11498
|
+
)
|
|
11499
|
+
.join("\n"),
|
|
11500
|
+
)
|
|
11501
|
+
.digest("hex")
|
|
11502
|
+
.slice(0, 32)
|
|
11503
|
+
: undefined;
|
|
11504
|
+
const sessionKey = stableBatchFingerprint
|
|
11505
|
+
? `bulk-import:batch:${stableBatchFingerprint}`
|
|
11506
|
+
: `bulk-import:batch:${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
|
|
11432
11507
|
|
|
11433
11508
|
const sessionTurns: BufferTurn[] = [];
|
|
11434
11509
|
for (const turn of turns) {
|
|
@@ -11443,9 +11518,22 @@ export class Orchestrator {
|
|
|
11443
11518
|
rawContent: turn.rawContent,
|
|
11444
11519
|
sourceFormat: turn.sourceFormat,
|
|
11445
11520
|
importProvenance: turn.importProvenance,
|
|
11521
|
+
turnFingerprint: turn.turnFingerprint,
|
|
11522
|
+
persistProcessedFingerprint: turn.persistProcessedFingerprint === true,
|
|
11446
11523
|
});
|
|
11447
11524
|
}
|
|
11448
|
-
if (sessionTurns.length === 0)
|
|
11525
|
+
if (sessionTurns.length === 0) {
|
|
11526
|
+
return {
|
|
11527
|
+
attemptedTurnCount: 0,
|
|
11528
|
+
extractionCount: 0,
|
|
11529
|
+
persistedCount: 0,
|
|
11530
|
+
durableOutputCount: 0,
|
|
11531
|
+
skippedCount: 0,
|
|
11532
|
+
failedCount: 0,
|
|
11533
|
+
postPersistMetadataFailureCount: 0,
|
|
11534
|
+
processedTurnCount: 0,
|
|
11535
|
+
};
|
|
11536
|
+
}
|
|
11449
11537
|
|
|
11450
11538
|
if (this.lcmEngine?.enabled) {
|
|
11451
11539
|
await this.lcmEngine.observeMessages(
|
|
@@ -11460,29 +11548,79 @@ export class Orchestrator {
|
|
|
11460
11548
|
);
|
|
11461
11549
|
}
|
|
11462
11550
|
|
|
11463
|
-
const
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11551
|
+
const sessionSlices = splitTurnsBySourceValidAt(sessionTurns, {
|
|
11552
|
+
includeContext: options.includeSourceValidAtContext !== false,
|
|
11553
|
+
});
|
|
11554
|
+
const results: ExtractionRunResult[] = [];
|
|
11555
|
+
let processedTurnCount = 0;
|
|
11556
|
+
let firstRejected: unknown;
|
|
11557
|
+
for (const sessionSlice of sessionSlices) {
|
|
11558
|
+
try {
|
|
11559
|
+
const result = await new Promise<ExtractionRunResult>(
|
|
11560
|
+
(resolve, reject) => {
|
|
11561
|
+
void this.queueBufferedExtraction(sessionSlice, "trigger_mode", {
|
|
11562
|
+
skipDedupeCheck: true,
|
|
11563
|
+
clearBufferAfterExtraction: false,
|
|
11564
|
+
skipCharThreshold: true,
|
|
11565
|
+
skipUserTurnThreshold: true,
|
|
11566
|
+
bufferKey: sessionKey,
|
|
11567
|
+
extractionDeadlineMs: options.deadlineMs,
|
|
11568
|
+
failOnExtractionFailure: options.failOnExtractionFailure === true,
|
|
11569
|
+
writeNamespaceOverride: this.bulkImportWriteNamespace(),
|
|
11570
|
+
onTaskSettled: (err, result) =>
|
|
11571
|
+
err
|
|
11572
|
+
? reject(err)
|
|
11573
|
+
: resolve(
|
|
11574
|
+
result ?? {
|
|
11575
|
+
status: "skipped",
|
|
11576
|
+
reason: "missing_extraction_result",
|
|
11577
|
+
persistedCount: 0,
|
|
11578
|
+
durableOutputCount: 0,
|
|
11579
|
+
},
|
|
11580
|
+
),
|
|
11581
|
+
}).catch(reject);
|
|
11582
|
+
},
|
|
11583
|
+
);
|
|
11584
|
+
results.push(result);
|
|
11585
|
+
processedTurnCount += sessionSlice.filter(
|
|
11586
|
+
(turn) => turn.extractionContextOnly !== true,
|
|
11587
|
+
).length;
|
|
11588
|
+
} catch (err) {
|
|
11589
|
+
firstRejected = err;
|
|
11590
|
+
break;
|
|
11591
|
+
}
|
|
11592
|
+
}
|
|
11593
|
+
const rejectedCount = firstRejected ? 1 : 0;
|
|
11594
|
+
const ingestResult: BulkImportBatchIngestResult = {
|
|
11595
|
+
attemptedTurnCount: sessionTurns.length,
|
|
11596
|
+
extractionCount: results.length,
|
|
11597
|
+
persistedCount: results.reduce(
|
|
11598
|
+
(sum, result) => sum + result.persistedCount,
|
|
11599
|
+
0,
|
|
11600
|
+
),
|
|
11601
|
+
durableOutputCount: results.reduce(
|
|
11602
|
+
(sum, result) => sum + result.durableOutputCount,
|
|
11603
|
+
0,
|
|
11604
|
+
),
|
|
11605
|
+
skippedCount: results.filter((result) => result.status === "skipped").length,
|
|
11606
|
+
failedCount: rejectedCount,
|
|
11607
|
+
postPersistMetadataFailureCount: results.filter(
|
|
11608
|
+
(result) => result.postPersistMetadataFailed === true,
|
|
11609
|
+
).length,
|
|
11610
|
+
processedTurnCount:
|
|
11611
|
+
rejectedCount === 0 ? sessionTurns.length : processedTurnCount,
|
|
11612
|
+
};
|
|
11483
11613
|
if (firstRejected) {
|
|
11484
|
-
|
|
11614
|
+
if (processedTurnCount > 0) {
|
|
11615
|
+
throw new BulkImportBatchPartialFailureError(
|
|
11616
|
+
"bulk import failed after partial processing",
|
|
11617
|
+
ingestResult,
|
|
11618
|
+
firstRejected,
|
|
11619
|
+
);
|
|
11620
|
+
}
|
|
11621
|
+
throw firstRejected;
|
|
11485
11622
|
}
|
|
11623
|
+
return ingestResult;
|
|
11486
11624
|
}
|
|
11487
11625
|
|
|
11488
11626
|
async observeSessionHeartbeat(
|
|
@@ -11564,7 +11702,11 @@ export class Orchestrator {
|
|
|
11564
11702
|
skipCharThreshold?: boolean;
|
|
11565
11703
|
skipUserTurnThreshold?: boolean;
|
|
11566
11704
|
extractionDeadlineMs?: number;
|
|
11567
|
-
|
|
11705
|
+
failOnExtractionFailure?: boolean;
|
|
11706
|
+
onTaskSettled?: (
|
|
11707
|
+
error?: unknown,
|
|
11708
|
+
result?: ExtractionRunResult,
|
|
11709
|
+
) => void;
|
|
11568
11710
|
bufferKey?: string;
|
|
11569
11711
|
abortSignal?: AbortSignal;
|
|
11570
11712
|
/**
|
|
@@ -11583,26 +11725,77 @@ export class Orchestrator {
|
|
|
11583
11725
|
!this.shouldQueueExtraction(turnsToExtract, { bufferKey })
|
|
11584
11726
|
) {
|
|
11585
11727
|
log.debug(`extraction dedupe skip: preserving buffer (${reason})`);
|
|
11586
|
-
options.onTaskSettled?.(
|
|
11728
|
+
options.onTaskSettled?.(undefined, {
|
|
11729
|
+
status: "skipped",
|
|
11730
|
+
reason: "dedupe",
|
|
11731
|
+
persistedCount: 0,
|
|
11732
|
+
durableOutputCount: 0,
|
|
11733
|
+
});
|
|
11587
11734
|
return;
|
|
11588
11735
|
}
|
|
11589
11736
|
|
|
11737
|
+
const extractionDeadlineMs =
|
|
11738
|
+
typeof options.extractionDeadlineMs === "number" &&
|
|
11739
|
+
Number.isFinite(options.extractionDeadlineMs)
|
|
11740
|
+
? options.extractionDeadlineMs
|
|
11741
|
+
: undefined;
|
|
11742
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
11743
|
+
let settled = false;
|
|
11744
|
+
const clearQueueWaitTimer = (): void => {
|
|
11745
|
+
if (timeout) {
|
|
11746
|
+
clearTimeout(timeout);
|
|
11747
|
+
timeout = undefined;
|
|
11748
|
+
}
|
|
11749
|
+
};
|
|
11750
|
+
const settleTask = (
|
|
11751
|
+
error?: unknown,
|
|
11752
|
+
result?: ExtractionRunResult,
|
|
11753
|
+
): boolean => {
|
|
11754
|
+
if (settled) return false;
|
|
11755
|
+
settled = true;
|
|
11756
|
+
clearQueueWaitTimer();
|
|
11757
|
+
options.onTaskSettled?.(error, result);
|
|
11758
|
+
return true;
|
|
11759
|
+
};
|
|
11760
|
+
|
|
11761
|
+
if (typeof extractionDeadlineMs === "number") {
|
|
11762
|
+
const remainingMs = extractionDeadlineMs - Date.now();
|
|
11763
|
+
if (remainingMs <= 0) {
|
|
11764
|
+
settleTask(new Error("replay extraction deadline exceeded (queue_wait)"));
|
|
11765
|
+
return;
|
|
11766
|
+
}
|
|
11767
|
+
timeout = setTimeout(() => {
|
|
11768
|
+
settleTask(new Error("replay extraction deadline exceeded (queue_wait)"));
|
|
11769
|
+
}, remainingMs);
|
|
11770
|
+
}
|
|
11771
|
+
|
|
11590
11772
|
this.extractionQueue.push(async () => {
|
|
11773
|
+
if (settled) return;
|
|
11774
|
+
if (
|
|
11775
|
+
typeof extractionDeadlineMs === "number" &&
|
|
11776
|
+
extractionDeadlineMs <= Date.now()
|
|
11777
|
+
) {
|
|
11778
|
+
settleTask(new Error("replay extraction deadline exceeded (queue_wait)"));
|
|
11779
|
+
return;
|
|
11780
|
+
}
|
|
11781
|
+
clearQueueWaitTimer();
|
|
11591
11782
|
try {
|
|
11592
|
-
await this.runExtraction(turnsToExtract, {
|
|
11783
|
+
const result = await this.runExtraction(turnsToExtract, {
|
|
11593
11784
|
clearBufferAfterExtraction:
|
|
11594
11785
|
options.clearBufferAfterExtraction ?? true,
|
|
11595
11786
|
skipCharThreshold: options.skipCharThreshold ?? false,
|
|
11596
11787
|
skipUserTurnThreshold: options.skipUserTurnThreshold ?? false,
|
|
11597
|
-
deadlineMs:
|
|
11788
|
+
deadlineMs: extractionDeadlineMs,
|
|
11598
11789
|
bufferKey,
|
|
11599
11790
|
abortSignal: options.abortSignal,
|
|
11791
|
+
failOnExtractionFailure: options.failOnExtractionFailure === true,
|
|
11600
11792
|
writeNamespaceOverride: options.writeNamespaceOverride,
|
|
11601
11793
|
});
|
|
11602
|
-
|
|
11794
|
+
settleTask(undefined, result);
|
|
11603
11795
|
} catch (err) {
|
|
11604
|
-
|
|
11605
|
-
|
|
11796
|
+
if (settleTask(err)) {
|
|
11797
|
+
throw err;
|
|
11798
|
+
}
|
|
11606
11799
|
}
|
|
11607
11800
|
});
|
|
11608
11801
|
|
|
@@ -11744,6 +11937,7 @@ export class Orchestrator {
|
|
|
11744
11937
|
deadlineMs?: number;
|
|
11745
11938
|
bufferKey?: string;
|
|
11746
11939
|
abortSignal?: AbortSignal;
|
|
11940
|
+
failOnExtractionFailure?: boolean;
|
|
11747
11941
|
/**
|
|
11748
11942
|
* Explicit namespace override for the write path (#460). When set,
|
|
11749
11943
|
* extraction writes go to this namespace instead of the one derived
|
|
@@ -11753,7 +11947,7 @@ export class Orchestrator {
|
|
|
11753
11947
|
*/
|
|
11754
11948
|
writeNamespaceOverride?: string;
|
|
11755
11949
|
} = {},
|
|
11756
|
-
): Promise<
|
|
11950
|
+
): Promise<ExtractionRunResult> {
|
|
11757
11951
|
log.debug(`running extraction on ${turns.length} turns`);
|
|
11758
11952
|
const clearBufferAfterExtraction =
|
|
11759
11953
|
options.clearBufferAfterExtraction ?? true;
|
|
@@ -11787,7 +11981,12 @@ export class Orchestrator {
|
|
|
11787
11981
|
if (sessionKey.includes(":cron:")) {
|
|
11788
11982
|
log.debug(`skipping extraction for cron session: ${sessionKey}`);
|
|
11789
11983
|
await clearBuffer();
|
|
11790
|
-
return
|
|
11984
|
+
return {
|
|
11985
|
+
status: "skipped",
|
|
11986
|
+
reason: "cron_session",
|
|
11987
|
+
persistedCount: 0,
|
|
11988
|
+
durableOutputCount: 0,
|
|
11989
|
+
};
|
|
11791
11990
|
}
|
|
11792
11991
|
|
|
11793
11992
|
const normalizedTurns = turns
|
|
@@ -11807,7 +12006,12 @@ export class Orchestrator {
|
|
|
11807
12006
|
if (targetTurns.length === 0) {
|
|
11808
12007
|
log.debug("skipping extraction: no non-context turns after normalization");
|
|
11809
12008
|
await clearBuffer();
|
|
11810
|
-
return
|
|
12009
|
+
return {
|
|
12010
|
+
status: "skipped",
|
|
12011
|
+
reason: "empty_normalized_turns",
|
|
12012
|
+
persistedCount: 0,
|
|
12013
|
+
durableOutputCount: 0,
|
|
12014
|
+
};
|
|
11811
12015
|
}
|
|
11812
12016
|
const sourceValidAt = latestSourceValidAtFromTurns(targetTurns);
|
|
11813
12017
|
throwIfDeadlineExceeded("before_extract");
|
|
@@ -11827,7 +12031,12 @@ export class Orchestrator {
|
|
|
11827
12031
|
`skipping extraction: below threshold (totalChars=${totalChars}, userTurns=${userTurns.length})`,
|
|
11828
12032
|
);
|
|
11829
12033
|
await clearBuffer();
|
|
11830
|
-
return
|
|
12034
|
+
return {
|
|
12035
|
+
status: "skipped",
|
|
12036
|
+
reason: "below_threshold",
|
|
12037
|
+
persistedCount: 0,
|
|
12038
|
+
durableOutputCount: 0,
|
|
12039
|
+
};
|
|
11831
12040
|
}
|
|
11832
12041
|
|
|
11833
12042
|
const principal = resolvePrincipal(sessionKey, this.config);
|
|
@@ -11866,7 +12075,12 @@ export class Orchestrator {
|
|
|
11866
12075
|
`runExtraction: skipping already-processed extraction fingerprint for ${bufferKey}`,
|
|
11867
12076
|
);
|
|
11868
12077
|
await clearBuffer();
|
|
11869
|
-
return
|
|
12078
|
+
return {
|
|
12079
|
+
status: "skipped",
|
|
12080
|
+
reason: "processed_fingerprint",
|
|
12081
|
+
persistedCount: 0,
|
|
12082
|
+
durableOutputCount: 0,
|
|
12083
|
+
};
|
|
11870
12084
|
}
|
|
11871
12085
|
|
|
11872
12086
|
// Pass existing entity names so the LLM can reuse them instead of inventing variants
|
|
@@ -11882,19 +12096,59 @@ export class Orchestrator {
|
|
|
11882
12096
|
throwIfDeadlineExceeded("before_persist");
|
|
11883
12097
|
throwIfAborted("before_persist");
|
|
11884
12098
|
|
|
11885
|
-
// Defensive: validate extraction result before processing
|
|
12099
|
+
// Defensive: validate extraction result before processing. Explicit
|
|
12100
|
+
// fail-closed callers, such as flush-plan import, must not observe
|
|
12101
|
+
// malformed extractor output as a successful skip.
|
|
11886
12102
|
if (!result) {
|
|
11887
12103
|
log.warn("runExtraction: extraction returned null/undefined");
|
|
12104
|
+
if (options.failOnExtractionFailure) {
|
|
12105
|
+
throw new Error("extraction failed: invalid_extraction_result");
|
|
12106
|
+
}
|
|
11888
12107
|
await clearBuffer();
|
|
11889
|
-
return
|
|
12108
|
+
return {
|
|
12109
|
+
status: "skipped",
|
|
12110
|
+
reason: "invalid_extraction_result",
|
|
12111
|
+
persistedCount: 0,
|
|
12112
|
+
durableOutputCount: 0,
|
|
12113
|
+
};
|
|
11890
12114
|
}
|
|
11891
|
-
|
|
12115
|
+
const invalidExtractionResultFields = [
|
|
12116
|
+
["facts", result.facts],
|
|
12117
|
+
["entities", result.entities],
|
|
12118
|
+
["questions", result.questions],
|
|
12119
|
+
["profileUpdates", result.profileUpdates],
|
|
12120
|
+
]
|
|
12121
|
+
.filter(([, value]) => !Array.isArray(value))
|
|
12122
|
+
.map(([field]) => field);
|
|
12123
|
+
if (invalidExtractionResultFields.length > 0) {
|
|
11892
12124
|
log.warn(
|
|
11893
|
-
"runExtraction: extraction returned invalid
|
|
11894
|
-
{
|
|
12125
|
+
"runExtraction: extraction returned invalid collection fields",
|
|
12126
|
+
{
|
|
12127
|
+
invalidFields: invalidExtractionResultFields,
|
|
12128
|
+
resultKeys:
|
|
12129
|
+
typeof result === "object" && result !== null
|
|
12130
|
+
? Object.keys(result)
|
|
12131
|
+
: [],
|
|
12132
|
+
},
|
|
11895
12133
|
);
|
|
12134
|
+
if (options.failOnExtractionFailure) {
|
|
12135
|
+
throw new Error("extraction failed: invalid_extraction_result");
|
|
12136
|
+
}
|
|
11896
12137
|
await clearBuffer();
|
|
11897
|
-
return
|
|
12138
|
+
return {
|
|
12139
|
+
status: "skipped",
|
|
12140
|
+
reason: "invalid_extraction_result",
|
|
12141
|
+
persistedCount: 0,
|
|
12142
|
+
durableOutputCount: 0,
|
|
12143
|
+
};
|
|
12144
|
+
}
|
|
12145
|
+
const extractionFailure =
|
|
12146
|
+
typeof result.extractionFailure === "string" &&
|
|
12147
|
+
result.extractionFailure.trim().length > 0
|
|
12148
|
+
? result.extractionFailure
|
|
12149
|
+
: undefined;
|
|
12150
|
+
if (options.failOnExtractionFailure && extractionFailure) {
|
|
12151
|
+
throw new Error(`extraction failed: ${extractionFailure}`);
|
|
11898
12152
|
}
|
|
11899
12153
|
if (
|
|
11900
12154
|
result.facts.length === 0 &&
|
|
@@ -11905,8 +12159,34 @@ export class Orchestrator {
|
|
|
11905
12159
|
log.debug(
|
|
11906
12160
|
"runExtraction: extraction produced no durable outputs; skipping persistence",
|
|
11907
12161
|
);
|
|
12162
|
+
if (extractionFailure) {
|
|
12163
|
+
log.warn(
|
|
12164
|
+
"runExtraction: extraction reported failure with no durable outputs; not marking fingerprint processed",
|
|
12165
|
+
{ extractionFailure },
|
|
12166
|
+
);
|
|
12167
|
+
}
|
|
12168
|
+
if (
|
|
12169
|
+
extractionFingerprint &&
|
|
12170
|
+
shouldPersistProcessedFingerprint &&
|
|
12171
|
+
!extractionFailure
|
|
12172
|
+
) {
|
|
12173
|
+
meta ??= await storage.loadMeta();
|
|
12174
|
+
await this.recordProcessedExtractionFingerprint(
|
|
12175
|
+
storage,
|
|
12176
|
+
extractionFingerprint,
|
|
12177
|
+
meta,
|
|
12178
|
+
);
|
|
12179
|
+
meta.extractionCount += 1;
|
|
12180
|
+
meta.lastExtractionAt = new Date().toISOString();
|
|
12181
|
+
await storage.saveMeta(meta);
|
|
12182
|
+
}
|
|
11908
12183
|
await clearBuffer();
|
|
11909
|
-
return
|
|
12184
|
+
return {
|
|
12185
|
+
status: "skipped",
|
|
12186
|
+
reason: "empty_extraction_result",
|
|
12187
|
+
persistedCount: 0,
|
|
12188
|
+
durableOutputCount: 0,
|
|
12189
|
+
};
|
|
11910
12190
|
}
|
|
11911
12191
|
|
|
11912
12192
|
let threadIdForExtraction: string | null = null;
|
|
@@ -11929,6 +12209,7 @@ export class Orchestrator {
|
|
|
11929
12209
|
threadIdForExtraction,
|
|
11930
12210
|
{ sessionKey, principal, validAt: sourceValidAt },
|
|
11931
12211
|
);
|
|
12212
|
+
let postPersistMetadataFailed = false;
|
|
11932
12213
|
meta ??= await storage.loadMeta();
|
|
11933
12214
|
if (extractionFingerprint && shouldPersistProcessedFingerprint) {
|
|
11934
12215
|
try {
|
|
@@ -11942,6 +12223,7 @@ export class Orchestrator {
|
|
|
11942
12223
|
"runExtraction: failed to persist processed extraction fingerprint; continuing with buffer clear",
|
|
11943
12224
|
error,
|
|
11944
12225
|
);
|
|
12226
|
+
postPersistMetadataFailed = true;
|
|
11945
12227
|
}
|
|
11946
12228
|
}
|
|
11947
12229
|
// Persist extraction counters and processed fingerprints before running
|
|
@@ -11957,13 +12239,22 @@ export class Orchestrator {
|
|
|
11957
12239
|
meta.totalEntities += Array.isArray(result?.entities)
|
|
11958
12240
|
? result.entities.length
|
|
11959
12241
|
: 0;
|
|
11960
|
-
let postPersistMetaError: unknown;
|
|
11961
12242
|
try {
|
|
11962
12243
|
await storage.saveMeta(meta);
|
|
11963
12244
|
} catch (error) {
|
|
11964
|
-
|
|
12245
|
+
log.warn(
|
|
12246
|
+
"runExtraction: failed to save extraction metadata after durable persistence; continuing with buffer clear",
|
|
12247
|
+
error,
|
|
12248
|
+
);
|
|
12249
|
+
postPersistMetadataFailed = true;
|
|
11965
12250
|
}
|
|
11966
12251
|
|
|
12252
|
+
const durableOutputCount =
|
|
12253
|
+
result.facts.length +
|
|
12254
|
+
result.entities.length +
|
|
12255
|
+
result.questions.length +
|
|
12256
|
+
result.profileUpdates.length;
|
|
12257
|
+
|
|
11967
12258
|
// Buffer retention for defer verdicts (issue #562, PR 2). When the judge
|
|
11968
12259
|
// deferred at least one candidate, retain the tail of the current turn
|
|
11969
12260
|
// window so the next extraction pass has the surrounding context that
|
|
@@ -12050,27 +12341,55 @@ export class Orchestrator {
|
|
|
12050
12341
|
// Thread title update for the already-established thread context.
|
|
12051
12342
|
if (this.config.threadingEnabled && threadIdForExtraction) {
|
|
12052
12343
|
const conversationContent = turns.map((t) => t.content).join(" ");
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12344
|
+
try {
|
|
12345
|
+
await this.threading.updateThreadTitle(
|
|
12346
|
+
threadIdForExtraction,
|
|
12347
|
+
conversationContent,
|
|
12348
|
+
);
|
|
12349
|
+
} catch (err) {
|
|
12350
|
+
log.warn(
|
|
12351
|
+
"[threading] updateThreadTitle failed after persistence (non-fatal)",
|
|
12352
|
+
err,
|
|
12353
|
+
);
|
|
12354
|
+
}
|
|
12057
12355
|
}
|
|
12058
12356
|
|
|
12059
12357
|
// Check if consolidation is needed (debounced + non-zero gated).
|
|
12060
|
-
const nonZeroExtraction =
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12066
|
-
|
|
12358
|
+
const nonZeroExtraction = durableOutputCount > 0;
|
|
12359
|
+
try {
|
|
12360
|
+
if (nonZeroExtraction) this.nonZeroExtractionsSinceConsolidation += 1;
|
|
12361
|
+
this.maybeScheduleConsolidation(nonZeroExtraction);
|
|
12362
|
+
} catch (err) {
|
|
12363
|
+
log.warn(
|
|
12364
|
+
"runExtraction: consolidation scheduling failed after persistence (non-fatal)",
|
|
12365
|
+
err,
|
|
12366
|
+
);
|
|
12367
|
+
}
|
|
12067
12368
|
|
|
12068
|
-
|
|
12069
|
-
|
|
12369
|
+
try {
|
|
12370
|
+
this.requestQmdMaintenance();
|
|
12371
|
+
} catch (err) {
|
|
12372
|
+
log.warn(
|
|
12373
|
+
"runExtraction: QMD maintenance scheduling failed after persistence (non-fatal)",
|
|
12374
|
+
err,
|
|
12375
|
+
);
|
|
12376
|
+
}
|
|
12070
12377
|
|
|
12071
|
-
|
|
12072
|
-
|
|
12378
|
+
try {
|
|
12379
|
+
await this.runTierMigrationCycle(storage, "extraction");
|
|
12380
|
+
} catch (err) {
|
|
12381
|
+
log.warn(
|
|
12382
|
+
"runExtraction: tier migration failed after persistence (non-fatal)",
|
|
12383
|
+
err,
|
|
12384
|
+
);
|
|
12073
12385
|
}
|
|
12386
|
+
|
|
12387
|
+
return {
|
|
12388
|
+
status: "completed",
|
|
12389
|
+
persistedCount: persistedIds.length,
|
|
12390
|
+
durableOutputCount,
|
|
12391
|
+
postPersistMetadataFailed,
|
|
12392
|
+
};
|
|
12074
12393
|
}
|
|
12075
12394
|
|
|
12076
12395
|
private async recordProcessedExtractionFingerprint(
|
package/src/qmd.test.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import test from "node:test";
|
|
3
5
|
|
|
4
6
|
import { parseConfig } from "./config.js";
|
|
@@ -148,6 +150,39 @@ test("post-install auto-upgrade probes PATH qmd before stale fallback paths", ()
|
|
|
148
150
|
]);
|
|
149
151
|
});
|
|
150
152
|
|
|
153
|
+
test("QmdClient preserves configured qmdPath diagnostics when all probes fail", async () => {
|
|
154
|
+
const { QmdClient } = await import("./qmd.js");
|
|
155
|
+
const originalPath = process.env.PATH;
|
|
156
|
+
const originalWindowsPath = process.env.Path;
|
|
157
|
+
const missingQmdPath = path.join(
|
|
158
|
+
os.tmpdir(),
|
|
159
|
+
`remnic-missing-qmd-${process.pid}-${Date.now()}`,
|
|
160
|
+
"qmd.cmd",
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
process.env.PATH = "";
|
|
164
|
+
process.env.Path = "";
|
|
165
|
+
try {
|
|
166
|
+
const client = new QmdClient("test-collection", 10, {
|
|
167
|
+
qmdPath: missingQmdPath,
|
|
168
|
+
qmdFallbackPaths: [],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
assert.equal(await client.probe(), false);
|
|
172
|
+
const status = client.debugStatus();
|
|
173
|
+
|
|
174
|
+
assert.ok(status.includes(`cliPath=${missingQmdPath}`), status);
|
|
175
|
+
assert.ok(status.includes("cliPathSource=configured"), status);
|
|
176
|
+
assert.ok(status.includes("cliProbeError="), status);
|
|
177
|
+
assert.ok(!status.includes("/opt/homebrew/bin/qmd"), status);
|
|
178
|
+
} finally {
|
|
179
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
180
|
+
else process.env.PATH = originalPath;
|
|
181
|
+
if (originalWindowsPath === undefined) delete process.env.Path;
|
|
182
|
+
else process.env.Path = originalWindowsPath;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
151
186
|
test("QmdClient applies chunk strategy to normal and forced embed args", async () => {
|
|
152
187
|
const { QmdClient } = await import("./qmd.js");
|
|
153
188
|
const client = new QmdClient("test", 5, {
|