@remnic/core 9.3.725 → 9.3.727
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-boundary.d.ts +5 -5
- package/dist/access-boundary.js +28 -27
- package/dist/access-cli.js +65 -63
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +20 -5
- package/dist/access-http.js +34 -33
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +31 -30
- package/dist/access-operations-batch.js +29 -28
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +30 -29
- package/dist/{access-service-B2PlPtxh.d.ts → access-service-Bgpjxo4f.d.ts} +36 -4
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +27 -26
- package/dist/access-surface-catalog.d.ts +5 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +3 -3
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +9 -8
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-CzUb-GyD.d.ts → catalog-bMczlO2G.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +10 -9
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-KGZF2VO3.js → chunk-27QQC7ZX.js} +80 -19
- package/dist/{chunk-KGZF2VO3.js.map → chunk-27QQC7ZX.js.map} +1 -1
- package/dist/{chunk-HCVWF657.js → chunk-2KAYTPPT.js} +12 -12
- package/dist/{chunk-4SF232E6.js → chunk-36SEDFAU.js} +19 -7
- package/dist/chunk-36SEDFAU.js.map +1 -0
- package/dist/{chunk-ZK2L72VN.js → chunk-37436GML.js} +2 -2
- package/dist/{chunk-SC2YAWRF.js → chunk-3TSTGI6F.js} +2 -2
- package/dist/{chunk-FACTVMXQ.js → chunk-4H4F3BFL.js} +2 -2
- package/dist/{chunk-WAFDHKES.js → chunk-4NOI577P.js} +2 -2
- package/dist/{chunk-YQMPUYHE.js → chunk-4NR2PDZ5.js} +2 -2
- package/dist/{chunk-TAPSNIMA.js → chunk-5CEJH5ZN.js} +4 -4
- package/dist/{chunk-FN6QP7JQ.js → chunk-6PY5YJAV.js} +100 -14
- package/dist/{chunk-FN6QP7JQ.js.map → chunk-6PY5YJAV.js.map} +1 -1
- package/dist/{chunk-PKMQK2WY.js → chunk-773DPI6T.js} +4 -4
- package/dist/{chunk-4R3CGCI5.js → chunk-7RLCCNHU.js} +15 -12
- package/dist/chunk-7RLCCNHU.js.map +1 -0
- package/dist/{chunk-XGTBNVIU.js → chunk-AAD2ZS6P.js} +11 -1
- package/dist/chunk-AAD2ZS6P.js.map +1 -0
- package/dist/{chunk-Z3FGC3QE.js → chunk-AQLOP5F4.js} +139 -30
- package/dist/chunk-AQLOP5F4.js.map +1 -0
- package/dist/{chunk-FXCIOLRB.js → chunk-ASKXIAHD.js} +2 -2
- package/dist/{chunk-5OPYYPJN.js → chunk-CBWI22JN.js} +2 -2
- package/dist/{chunk-ZOHMPFOB.js → chunk-CKFLLAIS.js} +3 -3
- package/dist/{chunk-GZQXOQQ2.js → chunk-CSRXQ6NB.js} +5 -5
- package/dist/{chunk-QR67FU5G.js → chunk-CTOQEZSN.js} +2 -2
- package/dist/{chunk-ZUDM75KG.js → chunk-DCWIQFNA.js} +4 -4
- package/dist/{chunk-6CWGS2LY.js → chunk-DKJCRGWM.js} +3 -3
- package/dist/{chunk-JJJCVCCQ.js → chunk-DMQWV6SQ.js} +2 -2
- package/dist/{chunk-F3XZN24Z.js → chunk-DRUDC2UX.js} +2 -2
- package/dist/{chunk-4WZ3J7M6.js → chunk-EY6MDMHK.js} +2 -2
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-C4X4QGIO.js → chunk-G6NKALRN.js} +5 -2
- package/dist/chunk-G6NKALRN.js.map +1 -0
- package/dist/{chunk-6SQQOJ4U.js → chunk-GKRLPHPU.js} +2 -2
- package/dist/{chunk-D7EBTQ24.js → chunk-HOHVIVDR.js} +2 -2
- package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-UBHVAGO7.js → chunk-JYLFZWUX.js} +2 -2
- package/dist/{chunk-LD53WPMU.js → chunk-LQ6JI4VH.js} +4 -4
- package/dist/chunk-LTHVM4XN.js +109 -0
- package/dist/chunk-LTHVM4XN.js.map +1 -0
- package/dist/{chunk-WM7DNKBD.js → chunk-M4V2JVSZ.js} +2 -2
- package/dist/{chunk-I2HL5M6T.js → chunk-MJIHJ7OG.js} +52 -52
- package/dist/{chunk-J334N6QV.js → chunk-MLKWJOAJ.js} +2 -2
- package/dist/{chunk-C6BWLYW5.js → chunk-N3VNUM4Z.js} +171 -79
- package/dist/chunk-N3VNUM4Z.js.map +1 -0
- package/dist/{chunk-DF3I6JGP.js → chunk-NSA5ZRUL.js} +15 -15
- package/dist/{chunk-VI5SGGXB.js → chunk-OEEW4KJA.js} +4 -4
- package/dist/{chunk-7AG54AF3.js → chunk-PUR2CKX2.js} +1 -1
- package/dist/chunk-PUR2CKX2.js.map +1 -0
- package/dist/{chunk-NT36GU6X.js → chunk-R7IYU5DZ.js} +2 -2
- package/dist/{chunk-TT5U3UDO.js → chunk-SNKPUJHR.js} +4 -4
- package/dist/{chunk-T55EGZSJ.js → chunk-UVPL565C.js} +2 -2
- package/dist/chunk-W5H6ZRPT.js +14 -0
- package/dist/chunk-W5H6ZRPT.js.map +1 -0
- package/dist/{chunk-YQGHW5ML.js → chunk-WRFKZEO6.js} +4 -4
- package/dist/{chunk-KLFYKC3K.js → chunk-XPHVAOBT.js} +195 -74
- package/dist/chunk-XPHVAOBT.js.map +1 -0
- package/dist/{chunk-H7HUQIY7.js → chunk-Y4YD5TYW.js} +3 -3
- package/dist/{chunk-MDBZTH7N.js → chunk-YKBHOBJK.js} +44 -10
- package/dist/chunk-YKBHOBJK.js.map +1 -0
- package/dist/{chunk-YUYPWJD3.js → chunk-YOLZS7NY.js} +2 -2
- package/dist/{chunk-6OIDDKAA.js → chunk-YTU7WKYN.js} +35 -9
- package/dist/chunk-YTU7WKYN.js.map +1 -0
- package/dist/{cli-IOlDHSBT.d.ts → cli-BLME420h.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +60 -59
- package/dist/compat/codex-cli-fallback.d.ts +1 -0
- package/dist/compat/codex-cli-fallback.js +22 -0
- package/dist/compat/codex-cli-fallback.js.map +1 -0
- package/dist/compat/codex-thread-key.d.ts +1 -0
- package/dist/compat/codex-thread-key.js +18 -0
- package/dist/compat/codex-thread-key.js.map +1 -0
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +9 -8
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +3 -3
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +9 -8
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +9 -8
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/content-hash.d.ts +14 -0
- package/dist/content-hash.js +10 -0
- package/dist/content-hash.js.map +1 -0
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/dashboard-runtime.js +2 -2
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +9 -8
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +2 -2
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-redaction-rules.d.ts +33 -0
- package/dist/extraction-redaction-rules.js +14 -0
- package/dist/extraction-redaction-rules.js.map +1 -0
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +3 -3
- package/dist/faithfulness-config.d.ts +1 -0
- package/dist/faithfulness-config.js +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{forget-OEE5OELK.js → forget-WYLR4ZFV.js} +2 -2
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +107 -105
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +4 -4
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +9 -4
- package/dist/local-model-endpoint.js +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +9 -8
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +9 -8
- package/dist/maintenance/rebuild-memory-projection.js +10 -9
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +19 -18
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +9 -8
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +28 -27
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +11 -10
- package/dist/{orchestrator-C_OMSIMZ.d.ts → orchestrator-DqPAz8ot.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +51 -49
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/provenance.js +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -4
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +14 -14
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-B8LiYtvx.d.ts → semantic-consolidation-Cl15ksZy.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +10 -9
- package/dist/semantic-rule-promotion.js +9 -8
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +9 -8
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +3 -3
- package/dist/storage.js +9 -8
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-DmTaqob2.d.ts → types-ChQb9D6s.d.ts} +8 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +9 -8
- package/package.json +26 -2
- package/src/access-http.ts +68 -1
- package/src/admin/admin-surfaces.test.ts +289 -0
- package/src/admin/admin-surfaces.ts +138 -7
- package/src/bitemporal-dedup-backfill.test.ts +294 -0
- package/src/chat/chat-engine.test.ts +66 -1
- package/src/chat/chat-http.test.ts +119 -1
- package/src/chat/chat-http.ts +252 -97
- package/src/chat/chat-session.ts +143 -7
- package/src/compat/codex-cli-fallback.ts +23 -0
- package/src/compat/codex-subpath-aliases.test.ts +93 -0
- package/src/compat/codex-thread-key.ts +23 -0
- package/src/config.test.ts +50 -0
- package/src/config.ts +21 -4
- package/src/content-hash.ts +24 -0
- package/src/correction/correction-access-wiring.ts +104 -19
- package/src/correction/correction-contract.ts +68 -10
- package/src/correction/correction-executor.test.ts +112 -2
- package/src/correction/correction-executor.ts +65 -5
- package/src/correction/correction-planner.test.ts +47 -0
- package/src/correction/correction-planner.ts +40 -1
- package/src/correction/correction-storage-integration.test.ts +183 -0
- package/src/extraction-faithfulness.test.ts +240 -0
- package/src/extraction-faithfulness.ts +58 -6
- package/src/extraction-redaction-rules.test.ts +213 -0
- package/src/extraction-redaction-rules.ts +208 -0
- package/src/faithfulness-config.ts +16 -1
- package/src/local-model-endpoint.ts +24 -3
- package/src/orchestrator.ts +252 -9
- package/src/provenance-extraction.test.ts +266 -0
- package/src/provenance.ts +66 -16
- package/src/storage.ts +6 -10
- package/src/types.ts +8 -0
- package/dist/chunk-4R3CGCI5.js.map +0 -1
- package/dist/chunk-4SF232E6.js.map +0 -1
- package/dist/chunk-6OIDDKAA.js.map +0 -1
- package/dist/chunk-7AG54AF3.js.map +0 -1
- package/dist/chunk-C4X4QGIO.js.map +0 -1
- package/dist/chunk-C6BWLYW5.js.map +0 -1
- package/dist/chunk-KLFYKC3K.js.map +0 -1
- package/dist/chunk-MDBZTH7N.js.map +0 -1
- package/dist/chunk-XGTBNVIU.js.map +0 -1
- package/dist/chunk-Z3FGC3QE.js.map +0 -1
- /package/dist/{chunk-HCVWF657.js.map → chunk-2KAYTPPT.js.map} +0 -0
- /package/dist/{chunk-ZK2L72VN.js.map → chunk-37436GML.js.map} +0 -0
- /package/dist/{chunk-SC2YAWRF.js.map → chunk-3TSTGI6F.js.map} +0 -0
- /package/dist/{chunk-FACTVMXQ.js.map → chunk-4H4F3BFL.js.map} +0 -0
- /package/dist/{chunk-WAFDHKES.js.map → chunk-4NOI577P.js.map} +0 -0
- /package/dist/{chunk-YQMPUYHE.js.map → chunk-4NR2PDZ5.js.map} +0 -0
- /package/dist/{chunk-TAPSNIMA.js.map → chunk-5CEJH5ZN.js.map} +0 -0
- /package/dist/{chunk-PKMQK2WY.js.map → chunk-773DPI6T.js.map} +0 -0
- /package/dist/{chunk-FXCIOLRB.js.map → chunk-ASKXIAHD.js.map} +0 -0
- /package/dist/{chunk-5OPYYPJN.js.map → chunk-CBWI22JN.js.map} +0 -0
- /package/dist/{chunk-ZOHMPFOB.js.map → chunk-CKFLLAIS.js.map} +0 -0
- /package/dist/{chunk-GZQXOQQ2.js.map → chunk-CSRXQ6NB.js.map} +0 -0
- /package/dist/{chunk-QR67FU5G.js.map → chunk-CTOQEZSN.js.map} +0 -0
- /package/dist/{chunk-ZUDM75KG.js.map → chunk-DCWIQFNA.js.map} +0 -0
- /package/dist/{chunk-6CWGS2LY.js.map → chunk-DKJCRGWM.js.map} +0 -0
- /package/dist/{chunk-JJJCVCCQ.js.map → chunk-DMQWV6SQ.js.map} +0 -0
- /package/dist/{chunk-F3XZN24Z.js.map → chunk-DRUDC2UX.js.map} +0 -0
- /package/dist/{chunk-4WZ3J7M6.js.map → chunk-EY6MDMHK.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-6SQQOJ4U.js.map → chunk-GKRLPHPU.js.map} +0 -0
- /package/dist/{chunk-D7EBTQ24.js.map → chunk-HOHVIVDR.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-UBHVAGO7.js.map → chunk-JYLFZWUX.js.map} +0 -0
- /package/dist/{chunk-LD53WPMU.js.map → chunk-LQ6JI4VH.js.map} +0 -0
- /package/dist/{chunk-WM7DNKBD.js.map → chunk-M4V2JVSZ.js.map} +0 -0
- /package/dist/{chunk-I2HL5M6T.js.map → chunk-MJIHJ7OG.js.map} +0 -0
- /package/dist/{chunk-J334N6QV.js.map → chunk-MLKWJOAJ.js.map} +0 -0
- /package/dist/{chunk-DF3I6JGP.js.map → chunk-NSA5ZRUL.js.map} +0 -0
- /package/dist/{chunk-VI5SGGXB.js.map → chunk-OEEW4KJA.js.map} +0 -0
- /package/dist/{chunk-NT36GU6X.js.map → chunk-R7IYU5DZ.js.map} +0 -0
- /package/dist/{chunk-TT5U3UDO.js.map → chunk-SNKPUJHR.js.map} +0 -0
- /package/dist/{chunk-T55EGZSJ.js.map → chunk-UVPL565C.js.map} +0 -0
- /package/dist/{chunk-YQGHW5ML.js.map → chunk-WRFKZEO6.js.map} +0 -0
- /package/dist/{chunk-H7HUQIY7.js.map → chunk-Y4YD5TYW.js.map} +0 -0
- /package/dist/{chunk-YUYPWJD3.js.map → chunk-YOLZS7NY.js.map} +0 -0
- /package/dist/{forget-OEE5OELK.js.map → forget-WYLR4ZFV.js.map} +0 -0
package/src/orchestrator.ts
CHANGED
|
@@ -44,6 +44,11 @@ import {
|
|
|
44
44
|
createFaithfulnessCounters,
|
|
45
45
|
runFaithfulnessGateBatch,
|
|
46
46
|
} from "./extraction-faithfulness.js";
|
|
47
|
+
import {
|
|
48
|
+
contentMatchesRedactionRules,
|
|
49
|
+
loadRedactionRules,
|
|
50
|
+
type CompiledRedactionRule,
|
|
51
|
+
} from "./extraction-redaction-rules.js";
|
|
47
52
|
import type { FaithfulnessGateCounters } from "./extraction-faithfulness.js";
|
|
48
53
|
import {
|
|
49
54
|
EXTRACTION_JUDGE_VERDICT_CATEGORY,
|
|
@@ -2766,16 +2771,28 @@ export class Orchestrator {
|
|
|
2766
2771
|
dedupContent: string,
|
|
2767
2772
|
bounds: {
|
|
2768
2773
|
invalidAt?: string;
|
|
2774
|
+
// #1707 thread 2 — per-fact start bound (valid_at). Carried so a
|
|
2775
|
+
// re-extracted duplicate whose event time yields only a start bound
|
|
2776
|
+
// ("since 2024", "yesterday", an absolute date) gets the corrected
|
|
2777
|
+
// per-fact anchoring onto the existing copy.
|
|
2778
|
+
validFrom?: string;
|
|
2769
2779
|
observedAt?: string;
|
|
2770
2780
|
eventTimeSource?: "extracted" | "assumed";
|
|
2771
2781
|
},
|
|
2772
2782
|
entityRef?: string,
|
|
2773
2783
|
): Promise<void> {
|
|
2774
|
-
// I/O gate:
|
|
2775
|
-
//
|
|
2776
|
-
//
|
|
2777
|
-
//
|
|
2778
|
-
|
|
2784
|
+
// I/O gate: scan when there is a recall-relevant bound to backfill —
|
|
2785
|
+
// either an end bound (invalidAt, which expires the fact) or a corrected
|
|
2786
|
+
// EXTRACTED start bound. A start bound only changes recall when it is
|
|
2787
|
+
// extracted (the as-of filter excludes facts whose valid_at is after the
|
|
2788
|
+
// as-of instant); an "assumed" validFrom is just the ingestion anchor
|
|
2789
|
+
// (resolveFactEventTime sets one for every fact), so scanning on it would
|
|
2790
|
+
// run a full readAllMemories on every bi-temporal dedup hit for no benefit
|
|
2791
|
+
// (review cursor PRRT_OvHk / codex PRRT_OvHxVH). observedAt and
|
|
2792
|
+
// eventTimeSource alone never change recall.
|
|
2793
|
+
const hasExtractedStart =
|
|
2794
|
+
bounds.validFrom !== undefined && bounds.eventTimeSource === "extracted";
|
|
2795
|
+
if (!bounds.invalidAt && !hasExtractedStart) return;
|
|
2779
2796
|
try {
|
|
2780
2797
|
const incomingHash = ContentHashIndex.computeHash(dedupContent);
|
|
2781
2798
|
const normalizedIncoming = ContentHashIndex.normalizeContent(dedupContent);
|
|
@@ -2834,6 +2851,37 @@ export class Orchestrator {
|
|
|
2834
2851
|
) {
|
|
2835
2852
|
patch.eventTimeSource = bounds.eventTimeSource;
|
|
2836
2853
|
}
|
|
2854
|
+
// #1707 thread 2 — per-fact-anchored start bound. A re-extracted
|
|
2855
|
+
// duplicate whose event time resolves a real start bound must carry
|
|
2856
|
+
// that anchor onto the existing copy so as-of recall uses the corrected
|
|
2857
|
+
// valid_at instead of a stale batch-anchored value. Only an EXTRACTED
|
|
2858
|
+
// bound corrects; an "assumed" bound is just the ingestion anchor.
|
|
2859
|
+
//
|
|
2860
|
+
// No-clobber via equality, not provenance inference: exact-content dedup
|
|
2861
|
+
// re-extracts the SAME event-time expression, which #1670 per-fact
|
|
2862
|
+
// anchoring resolves deterministically to the same validFrom — so for
|
|
2863
|
+
// stable content the incoming validFrom EQUALS the copy's valid_at and
|
|
2864
|
+
// we skip the redundant write (the only no-clobber that holds without a
|
|
2865
|
+
// fragile provenance heuristic — review codex PRRT_Ov7LKC). When they
|
|
2866
|
+
// differ (a prior batch/assumed anchor, end-only assumed start, or a
|
|
2867
|
+
// non-deterministic re-resolution), the extracted validFrom is the
|
|
2868
|
+
// authoritative correction and overwrites. The eventTimeSource upgrade
|
|
2869
|
+
// below records that the start is now extracted-anchored.
|
|
2870
|
+
if (
|
|
2871
|
+
bounds.validFrom &&
|
|
2872
|
+
bounds.eventTimeSource === "extracted" &&
|
|
2873
|
+
fm.valid_at !== bounds.validFrom
|
|
2874
|
+
) {
|
|
2875
|
+
patch.valid_at = bounds.validFrom;
|
|
2876
|
+
// Mark the copy extracted-anchored in the SAME patch so its provenance
|
|
2877
|
+
// reflects the correction (review cursor PRRT_OvHM / codex PRRT_OvHxVD):
|
|
2878
|
+
// without this, a copy upgraded from "assumed" would keep "assumed"
|
|
2879
|
+
// provenance while carrying an extracted start. (The earlier
|
|
2880
|
+
// eventTimeSource block only fills an EMPTY source.)
|
|
2881
|
+
if (fm.eventTimeSource !== "extracted") {
|
|
2882
|
+
patch.eventTimeSource = "extracted";
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2837
2885
|
if (Object.keys(patch).length === 0) return;
|
|
2838
2886
|
const ok = await targetStorage.writeMemoryFrontmatter(existing, patch);
|
|
2839
2887
|
if (ok) {
|
|
@@ -14119,6 +14167,23 @@ export class Orchestrator {
|
|
|
14119
14167
|
// (declared here, inside persistExtraction) so a transient hiccup in one
|
|
14120
14168
|
// batch does not permanently disable dedup in future batches.
|
|
14121
14169
|
let batchBackendUnavailable = false;
|
|
14170
|
+
// #1669: per-namespace redaction-rule cache for this persist pass. A
|
|
14171
|
+
// `never_store` / redaction_rule correction persists patterns under each
|
|
14172
|
+
// namespace's state/corrections/redaction-rules/ dir; we consult them
|
|
14173
|
+
// before a fact reaches the storage write chokepoint so matching content
|
|
14174
|
+
// is withheld entirely rather than landing as pending_review. Cached per
|
|
14175
|
+
// dir so a multi-fact batch over one namespace reads the dir once.
|
|
14176
|
+
let redactionGatedCount = 0;
|
|
14177
|
+
const redactionRulesByDir = new Map<string, CompiledRedactionRule[]>();
|
|
14178
|
+
const redactionRulesFor = async (...dirs: string[]): Promise<CompiledRedactionRule[]> => {
|
|
14179
|
+
const out: CompiledRedactionRule[] = [];
|
|
14180
|
+
for (const d of dirs) {
|
|
14181
|
+
let r = redactionRulesByDir.get(d);
|
|
14182
|
+
if (!r) { r = await loadRedactionRules(d); redactionRulesByDir.set(d, r); }
|
|
14183
|
+
out.push(...r);
|
|
14184
|
+
}
|
|
14185
|
+
return out;
|
|
14186
|
+
};
|
|
14122
14187
|
const behaviorSignalsByStorage = new Map<
|
|
14123
14188
|
string,
|
|
14124
14189
|
{ storage: StorageManager; events: BehaviorSignalEvent[] }
|
|
@@ -14280,12 +14345,14 @@ export class Orchestrator {
|
|
|
14280
14345
|
// lacks (re-extraction with a now-resolved invalidAt). Best-effort,
|
|
14281
14346
|
// fail-open; the helper gates on invalidAt to avoid I/O when no
|
|
14282
14347
|
// end bound is present.
|
|
14283
|
-
if (options.invalidAt) {
|
|
14348
|
+
if (options.invalidAt || options.validAt) {
|
|
14284
14349
|
await this.backfillTemporalBoundsOnDedupHit(
|
|
14285
14350
|
targetStorage,
|
|
14286
14351
|
dedupContent,
|
|
14287
14352
|
{
|
|
14288
14353
|
invalidAt: options.invalidAt,
|
|
14354
|
+
// #1707 thread 2 — carry the corrected start bound.
|
|
14355
|
+
validFrom: options.validAt,
|
|
14289
14356
|
...(options.observedAt ? { observedAt: options.observedAt } : {}),
|
|
14290
14357
|
...(options.eventTimeSource ? { eventTimeSource: options.eventTimeSource } : {}),
|
|
14291
14358
|
},
|
|
@@ -14459,12 +14526,14 @@ export class Orchestrator {
|
|
|
14459
14526
|
// before the supersession short-circuit. Covers all return paths below
|
|
14460
14527
|
// (supersession-hit, catch-skip, and the no-supersession short-circuit)
|
|
14461
14528
|
// in one shot. Best-effort / fail-open; the helper gates on invalidAt.
|
|
14462
|
-
if (options.invalidAt) {
|
|
14529
|
+
if (options.invalidAt || options.validAt) {
|
|
14463
14530
|
await this.backfillTemporalBoundsOnDedupHit(
|
|
14464
14531
|
sharedStorage,
|
|
14465
14532
|
dedupContent,
|
|
14466
14533
|
{
|
|
14467
14534
|
invalidAt: options.invalidAt,
|
|
14535
|
+
// #1707 thread 2 — carry the corrected start bound.
|
|
14536
|
+
validFrom: options.validAt,
|
|
14468
14537
|
...(options.observedAt ? { observedAt: options.observedAt } : {}),
|
|
14469
14538
|
...(options.eventTimeSource ? { eventTimeSource: options.eventTimeSource } : {}),
|
|
14470
14539
|
},
|
|
@@ -14695,6 +14764,116 @@ export class Orchestrator {
|
|
|
14695
14764
|
);
|
|
14696
14765
|
}
|
|
14697
14766
|
};
|
|
14767
|
+
// #1707 thread 1 — backfill temporal bounds onto promotion copies when the
|
|
14768
|
+
// SOURCE-namespace dedup short-circuit fires. That branch patches the
|
|
14769
|
+
// source copy then `continue`s before the promotion dedup paths run, so
|
|
14770
|
+
// promoted shared/profile copies written before the source fact carried
|
|
14771
|
+
// resolved bounds stay stale (cross-namespace recall surfaces an expired
|
|
14772
|
+
// fact). This mirrors the promotion-target resolution used by the two
|
|
14773
|
+
// promote closures above and calls the same fail-open helper against each
|
|
14774
|
+
// target storage. Backfill-only: never writes a new promoted copy.
|
|
14775
|
+
const backfillTemporalBoundsOnPromotionCopies = async (args: {
|
|
14776
|
+
sourceStorage: StorageManager;
|
|
14777
|
+
content: string;
|
|
14778
|
+
category: string;
|
|
14779
|
+
confidence: number;
|
|
14780
|
+
entityRef?: string;
|
|
14781
|
+
structuredAttributes?: Record<string, string>;
|
|
14782
|
+
bounds: {
|
|
14783
|
+
invalidAt?: string;
|
|
14784
|
+
validFrom?: string;
|
|
14785
|
+
observedAt?: string;
|
|
14786
|
+
eventTimeSource?: "extracted" | "assumed";
|
|
14787
|
+
};
|
|
14788
|
+
}): Promise<void> => {
|
|
14789
|
+
// Mirror the helper's I/O gate: only resolve promotion targets when
|
|
14790
|
+
// there is an end bound OR an EXTRACTED start bound. An "assumed"
|
|
14791
|
+
// validFrom is just the ingestion anchor (no recall effect), so
|
|
14792
|
+
// skipping it avoids resolving target storages on every bi-temporal
|
|
14793
|
+
// duplicate (review cursor PRRT_OvHk).
|
|
14794
|
+
const hasExtractedStart =
|
|
14795
|
+
args.bounds.validFrom !== undefined &&
|
|
14796
|
+
args.bounds.eventTimeSource === "extracted";
|
|
14797
|
+
if (!args.bounds.invalidAt && !hasExtractedStart) return;
|
|
14798
|
+
// Build the same dedupContent the promotion functions hash on so the
|
|
14799
|
+
// content-hash lookup in the helper matches what was stored.
|
|
14800
|
+
const rawContent =
|
|
14801
|
+
citationEnabled && hasCitationForTemplate(args.content, citationTemplate)
|
|
14802
|
+
? stripCitationForTemplate(args.content, citationTemplate)
|
|
14803
|
+
: args.content;
|
|
14804
|
+
const sanitizedBase = sanitizeMemoryContent(rawContent);
|
|
14805
|
+
const dedupContent =
|
|
14806
|
+
args.category === "fact" &&
|
|
14807
|
+
args.structuredAttributes &&
|
|
14808
|
+
Object.keys(args.structuredAttributes).length > 0
|
|
14809
|
+
? `${sanitizedBase.text}\n[Attributes: ${normalizeAttributePairs(args.structuredAttributes)}]`
|
|
14810
|
+
: sanitizedBase.text;
|
|
14811
|
+
// Profile targets. NOTE: we do NOT gate on profileAutoPromotionAllows —
|
|
14812
|
+
// a promoted profile copy may exist from an EARLIER extraction with
|
|
14813
|
+
// higher confidence or older auto-promote settings, so the current
|
|
14814
|
+
// duplicate's confidence must not skip backfilling an already-existing
|
|
14815
|
+
// promoted copy (review codex PRRT_Ov7LKF). The helper no-ops when no
|
|
14816
|
+
// matching copy is found, so resolving all configured auto-promote
|
|
14817
|
+
// targets is safe.
|
|
14818
|
+
if (scopeProfileWritePlan) {
|
|
14819
|
+
const autoTargets = new Set(
|
|
14820
|
+
scopeProfileWritePlan.profile.autoPromote.targets,
|
|
14821
|
+
);
|
|
14822
|
+
const profileTargets = scopeProfileWritePlan.promotionTargets.filter(
|
|
14823
|
+
(target) =>
|
|
14824
|
+
target.target !== "serverShared" &&
|
|
14825
|
+
autoTargets.has(target.target) &&
|
|
14826
|
+
target.authorized &&
|
|
14827
|
+
target.namespace,
|
|
14828
|
+
);
|
|
14829
|
+
for (const target of profileTargets) {
|
|
14830
|
+
if (!target.namespace) continue;
|
|
14831
|
+
try {
|
|
14832
|
+
const targetStorage = await this.storageRouter.storageFor(
|
|
14833
|
+
target.namespace,
|
|
14834
|
+
);
|
|
14835
|
+
if (targetStorage.dir === args.sourceStorage.dir) continue;
|
|
14836
|
+
await this.backfillTemporalBoundsOnDedupHit(
|
|
14837
|
+
targetStorage,
|
|
14838
|
+
dedupContent,
|
|
14839
|
+
args.bounds,
|
|
14840
|
+
args.entityRef,
|
|
14841
|
+
);
|
|
14842
|
+
} catch (err) {
|
|
14843
|
+
log.warn(
|
|
14844
|
+
`bitemporal-backfill: profile-target backfill failed open for ${target.target}: ${err}`,
|
|
14845
|
+
);
|
|
14846
|
+
}
|
|
14847
|
+
}
|
|
14848
|
+
}
|
|
14849
|
+
// Shared target. A shared copy may exist from an earlier extraction
|
|
14850
|
+
// regardless of the current confidence, so do not gate on
|
|
14851
|
+
// shouldPromoteToShared (review codex PRRT_Ov7LKF) — BUT still respect
|
|
14852
|
+
// shared-write AUTHORIZATION: a scoped profile that does not authorize
|
|
14853
|
+
// serverShared writes must not have its shared namespace backfilled
|
|
14854
|
+
// (review codex PRRT_Ov7dHR). profileAllowsSharedWrites is true for the
|
|
14855
|
+
// legacy no-scope case and encodes the readable/writable/authorized
|
|
14856
|
+
// checks under a scope profile.
|
|
14857
|
+
if (profileAllowsSharedWrites) {
|
|
14858
|
+
try {
|
|
14859
|
+
const sharedStorage = await this.storageRouter.storageFor(
|
|
14860
|
+
this.config.sharedNamespace,
|
|
14861
|
+
);
|
|
14862
|
+
if (sharedStorage.dir !== args.sourceStorage.dir) {
|
|
14863
|
+
await this.backfillTemporalBoundsOnDedupHit(
|
|
14864
|
+
sharedStorage,
|
|
14865
|
+
dedupContent,
|
|
14866
|
+
args.bounds,
|
|
14867
|
+
args.entityRef,
|
|
14868
|
+
);
|
|
14869
|
+
}
|
|
14870
|
+
} catch (err) {
|
|
14871
|
+
log.warn(
|
|
14872
|
+
`bitemporal-backfill: shared-target backfill failed open: ${err}`,
|
|
14873
|
+
);
|
|
14874
|
+
}
|
|
14875
|
+
}
|
|
14876
|
+
};
|
|
14698
14877
|
|
|
14699
14878
|
// Defensive: validate result and facts array
|
|
14700
14879
|
if (!result || !Array.isArray(result.facts)) {
|
|
@@ -14846,6 +15025,10 @@ export class Orchestrator {
|
|
|
14846
15025
|
// into the caller's buffer-retention decision.
|
|
14847
15026
|
this.lastPersistExtractionDeferredCount = 0;
|
|
14848
15027
|
if (this.config.extractionJudgeEnabled) {
|
|
15028
|
+
// #1669 P1: pre-filter redacted facts from judge candidates so never-store
|
|
15029
|
+
// content is not persisted as judge training data.
|
|
15030
|
+
let preJudgeRedactionRules: CompiledRedactionRule[] = [];
|
|
15031
|
+
try { preJudgeRedactionRules = await redactionRulesFor(storage.dir); } catch { /* fail open */ }
|
|
14849
15032
|
try {
|
|
14850
15033
|
const judgeCandidates: JudgeCandidate[] = [];
|
|
14851
15034
|
const candidateToFactIndex: number[] = [];
|
|
@@ -14886,6 +15069,11 @@ export class Orchestrator {
|
|
|
14886
15069
|
) {
|
|
14887
15070
|
continue;
|
|
14888
15071
|
}
|
|
15072
|
+
if (preJudgeRedactionRules.length > 0) {
|
|
15073
|
+
const rc = f.content + (f.structuredAttributes ? " " + JSON.stringify(f.structuredAttributes) : "")
|
|
15074
|
+
+ (f.procedureSteps ? " " + f.procedureSteps.map((s) => `${s.intent} ${s.expectedOutcome ?? ""} ${s.toolCall ? `${s.toolCall.kind} ${s.toolCall.signature}` : ""}`.trim()).join(" ") : "");
|
|
15075
|
+
if (contentMatchesRedactionRules(rc, preJudgeRedactionRules)) continue;
|
|
15076
|
+
}
|
|
14889
15077
|
judgeCandidates.push({
|
|
14890
15078
|
text: f.content,
|
|
14891
15079
|
category: judgeCategory,
|
|
@@ -15064,6 +15252,7 @@ export class Orchestrator {
|
|
|
15064
15252
|
// affect both the dedup fingerprint and importance (issue #519 procedure routing).
|
|
15065
15253
|
let writeCategory = fact.category;
|
|
15066
15254
|
let targetStorage = storage;
|
|
15255
|
+
const sourceStorageDir = storage.dir; // #1669 thread #2: pre-routing source ns for redaction gate
|
|
15067
15256
|
// Track the KNOWN target namespace NAME alongside targetStorage (round 6,
|
|
15068
15257
|
// codex P2 — NCQI0). Re-deriving it from `targetStorage.dir` mangles a raw
|
|
15069
15258
|
// namespace literally named like a canonical token (e.g. `ns-616c706861`
|
|
@@ -15138,6 +15327,23 @@ export class Orchestrator {
|
|
|
15138
15327
|
);
|
|
15139
15328
|
}
|
|
15140
15329
|
}
|
|
15330
|
+
// #1669 redaction-rule gate: consult BOTH source and target namespace
|
|
15331
|
+
// rules before any write. A never-store pattern registered under the
|
|
15332
|
+
// source namespace must survive scope-routing to a different target
|
|
15333
|
+
// (review thread #2). Fails open on read error.
|
|
15334
|
+
try {
|
|
15335
|
+
const redactionRules = await redactionRulesFor(sourceStorageDir, targetStorage.dir);
|
|
15336
|
+
const redactionCandidate = fact.content
|
|
15337
|
+
+ (fact.structuredAttributes ? " " + JSON.stringify(fact.structuredAttributes) : "")
|
|
15338
|
+
+ (fact.procedureSteps ? " " + fact.procedureSteps.map((s) => `${s.intent} ${s.expectedOutcome ?? ""} ${s.toolCall ? `${s.toolCall.kind} ${s.toolCall.signature}` : ""}`.trim()).join(" ") : "");
|
|
15339
|
+
if (redactionRules.length > 0 && contentMatchesRedactionRules(redactionCandidate, redactionRules)) {
|
|
15340
|
+
redactionGatedCount++;
|
|
15341
|
+
log.debug(`extraction: redaction-rule withheld fact #${redactionGatedCount} in ${targetStorage.dir}`);
|
|
15342
|
+
continue;
|
|
15343
|
+
}
|
|
15344
|
+
} catch (redactionErr) {
|
|
15345
|
+
log.warn(`extraction: redaction-rule gate failed open: ${redactionErr}`);
|
|
15346
|
+
}
|
|
15141
15347
|
|
|
15142
15348
|
// Procedures: fingerprint the full serialized body (title + steps), not
|
|
15143
15349
|
// the title alone, so distinct step lists are not collapsed (issue #519).
|
|
@@ -15177,7 +15383,20 @@ export class Orchestrator {
|
|
|
15177
15383
|
// Skip when the fact would be rejected/deferred/pending by downstream
|
|
15178
15384
|
// gates — a non-durable candidate must not expire an active fact
|
|
15179
15385
|
// (chatgpt-codex P1: faithfulness, requireSpans, extraction judge).
|
|
15180
|
-
|
|
15386
|
+
// #1671 + #1707: backfill bi-temporal bounds onto the existing
|
|
15387
|
+
// source-namespace copy if it lacks bounds the incoming fact now
|
|
15388
|
+
// carries (re-extraction with a resolved bound). #1707 thread 3:
|
|
15389
|
+
// gate on writeCategory === "fact" — the helper only matches facts,
|
|
15390
|
+
// so a non-fact duplicate must not reach the fact-only scan.
|
|
15391
|
+
// #1707 thread 2: also fire when only a corrected start bound
|
|
15392
|
+
// (validFrom) is present, not just an end bound (validUntil). The
|
|
15393
|
+
// downstream-gate skip still applies — a non-durable candidate must
|
|
15394
|
+
// not expire an active fact (chatgpt-codex P1).
|
|
15395
|
+
if (
|
|
15396
|
+
biTemporal &&
|
|
15397
|
+
writeCategory === "fact" &&
|
|
15398
|
+
(biTemporal.validUntil || biTemporal.validFrom)
|
|
15399
|
+
) {
|
|
15181
15400
|
const fr = faithfulnessResultsByFactIndex?.get(factLoopIndex);
|
|
15182
15401
|
const faithfulnessWouldPending =
|
|
15183
15402
|
faithfulnessMode === "enforce" &&
|
|
@@ -15205,11 +15424,33 @@ export class Orchestrator {
|
|
|
15205
15424
|
contentHashDedupKey,
|
|
15206
15425
|
{
|
|
15207
15426
|
invalidAt: biTemporal.validUntil,
|
|
15427
|
+
// #1707 thread 2 — carry the corrected start bound too.
|
|
15428
|
+
validFrom: biTemporal.validFrom,
|
|
15208
15429
|
observedAt: biTemporal.observedAt,
|
|
15209
15430
|
eventTimeSource: biTemporal.eventTimeSource,
|
|
15210
15431
|
},
|
|
15211
15432
|
fact.entityRef,
|
|
15212
15433
|
);
|
|
15434
|
+
// #1707 thread 1 — the source branch short-circuits (`continue`
|
|
15435
|
+
// below) before the promotion dedup paths run, so promoted
|
|
15436
|
+
// shared/profile copies written before the source fact carried
|
|
15437
|
+
// resolved bounds stay stale and cross-namespace recall surfaces
|
|
15438
|
+
// an expired fact. Backfill the promotion targets too (fail-open,
|
|
15439
|
+
// backfill-only — never writes a new promoted copy).
|
|
15440
|
+
await backfillTemporalBoundsOnPromotionCopies({
|
|
15441
|
+
sourceStorage: targetStorage,
|
|
15442
|
+
content: fact.content,
|
|
15443
|
+
category: writeCategory,
|
|
15444
|
+
confidence: fact.confidence,
|
|
15445
|
+
entityRef: fact.entityRef,
|
|
15446
|
+
structuredAttributes: fact.structuredAttributes,
|
|
15447
|
+
bounds: {
|
|
15448
|
+
invalidAt: biTemporal.validUntil,
|
|
15449
|
+
validFrom: biTemporal.validFrom,
|
|
15450
|
+
observedAt: biTemporal.observedAt,
|
|
15451
|
+
eventTimeSource: biTemporal.eventTimeSource,
|
|
15452
|
+
},
|
|
15453
|
+
});
|
|
15213
15454
|
}
|
|
15214
15455
|
}
|
|
15215
15456
|
log.debug(
|
|
@@ -16263,8 +16504,10 @@ export class Orchestrator {
|
|
|
16263
16504
|
importanceGatedCount > 0 ? ` (${importanceGatedCount} gated)` : "";
|
|
16264
16505
|
const judgeSuffix =
|
|
16265
16506
|
judgeGatedCount > 0 ? ` (${judgeGatedCount} judge-rejected)` : "";
|
|
16507
|
+
const redactionSuffix =
|
|
16508
|
+
redactionGatedCount > 0 ? ` (${redactionGatedCount} redacted)` : "";
|
|
16266
16509
|
log.info(
|
|
16267
|
-
`persisted: ${facts.length - dedupedCount - importanceGatedCount - judgeGatedCount} facts${dedupSuffix}${gatedSuffix}${judgeSuffix}, ${entities.length} entities, ${questions.length} questions, ${profileUpdates.length} profile updates`,
|
|
16510
|
+
`persisted: ${facts.length - dedupedCount - importanceGatedCount - judgeGatedCount - redactionGatedCount} facts${dedupSuffix}${gatedSuffix}${judgeSuffix}${redactionSuffix}, ${entities.length} entities, ${questions.length} questions, ${profileUpdates.length} profile updates`,
|
|
16268
16511
|
);
|
|
16269
16512
|
|
|
16270
16513
|
// Update temporal + tag indexes (v8.1) — fire-and-forget, fail-open
|
|
@@ -836,3 +836,269 @@ test("toStrictIsoTimestamp path: valid MM/DD/YYYY still normalizes (thread dH47
|
|
|
836
836
|
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/,
|
|
837
837
|
);
|
|
838
838
|
});
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
// ---------------------------------------------------------------------------
|
|
842
|
+
// Regression: issue #1657 — partial year-led timestamps must not be admitted
|
|
843
|
+
// as fabricated complete dates. Date.parse fills in missing calendar
|
|
844
|
+
// components ("2026-05" -> May 1) and the ymd guard misparses the date/time
|
|
845
|
+
// boundary ("2026-05T10:00" reads the hour as the day). The normalization
|
|
846
|
+
// path must reject any year-led shape with fewer than three complete calendar
|
|
847
|
+
// components rather than persisting a fabricated observedAt.
|
|
848
|
+
// ---------------------------------------------------------------------------
|
|
849
|
+
|
|
850
|
+
test("toStrictIsoTimestamp path: partial YYYY-MM timestamp is rejected, not fabricated (issue #1657)", () => {
|
|
851
|
+
// "2026-05" is Date.parse-able (-> 2026-05-01) and passes the separator
|
|
852
|
+
// guard, so pre-fix the write path persisted a fabricated observedAt of
|
|
853
|
+
// 2026-05-01T00:00:00.000Z. Reject it so the source is dropped.
|
|
854
|
+
const turns = [
|
|
855
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
856
|
+
timestamp: "2026-05" as unknown as string,
|
|
857
|
+
}),
|
|
858
|
+
];
|
|
859
|
+
const result = buildFactProvenance(
|
|
860
|
+
"migrated the production database to pgBouncer",
|
|
861
|
+
turns,
|
|
862
|
+
DEFAULT_CONFIG,
|
|
863
|
+
);
|
|
864
|
+
// No verified source: the only matching turn has a partial timestamp.
|
|
865
|
+
assert.equal(result.provenance, "unverified");
|
|
866
|
+
assert.ok(result.sources);
|
|
867
|
+
assert.notEqual(
|
|
868
|
+
result.sources![0]!.observedAt,
|
|
869
|
+
"2026-05-01T00:00:00.000Z",
|
|
870
|
+
"partial YYYY-MM timestamp must not fabricate May 1",
|
|
871
|
+
);
|
|
872
|
+
assert.equal(
|
|
873
|
+
result.sources![0]!.observedAt,
|
|
874
|
+
new Date(0).toISOString(),
|
|
875
|
+
"rejected partial timestamp falls back to epoch",
|
|
876
|
+
);
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
test("toStrictIsoTimestamp path: partial YYYY-MM T HH:MM is rejected, not misparsed (issue #1657)", () => {
|
|
880
|
+
// "2026-05T10:00" is Date.parse-able; the ymd regex grabs the hour "10" as
|
|
881
|
+
// the day across the date/time boundary, so pre-fix the write path persisted
|
|
882
|
+
// a fabricated observedAt (on May 10, shifted by timezone). Reject it.
|
|
883
|
+
const turns = [
|
|
884
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
885
|
+
timestamp: "2026-05T10:00" as unknown as string,
|
|
886
|
+
}),
|
|
887
|
+
];
|
|
888
|
+
const result = buildFactProvenance(
|
|
889
|
+
"migrated the production database to pgBouncer",
|
|
890
|
+
turns,
|
|
891
|
+
DEFAULT_CONFIG,
|
|
892
|
+
);
|
|
893
|
+
assert.equal(result.provenance, "unverified");
|
|
894
|
+
assert.ok(result.sources);
|
|
895
|
+
assert.equal(
|
|
896
|
+
result.sources![0]!.observedAt,
|
|
897
|
+
new Date(0).toISOString(),
|
|
898
|
+
"partial YYYY-MM T HH:MM timestamp must fall back to epoch, not a fabricated date",
|
|
899
|
+
);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
test("toStrictIsoTimestamp path: complete YYYY-MM-DD (no time) still normalizes (issue #1657 non-regression)", () => {
|
|
903
|
+
// A complete calendar date with no time component must still round-trip
|
|
904
|
+
// through Date.parse to strict ISO. This is the boundary the partial-date
|
|
905
|
+
// guard must NOT over-reject.
|
|
906
|
+
const turns = [
|
|
907
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
908
|
+
timestamp: "2026-05-03" as unknown as string,
|
|
909
|
+
}),
|
|
910
|
+
];
|
|
911
|
+
const result = buildFactProvenance(
|
|
912
|
+
"migrated the production database to pgBouncer",
|
|
913
|
+
turns,
|
|
914
|
+
DEFAULT_CONFIG,
|
|
915
|
+
);
|
|
916
|
+
assert.equal(result.provenance, "verified");
|
|
917
|
+
assert.ok(result.sources);
|
|
918
|
+
assert.equal(
|
|
919
|
+
result.sources![0]!.observedAt,
|
|
920
|
+
"2026-05-03T00:00:00.000Z",
|
|
921
|
+
"complete date-only timestamp normalizes to UTC midnight",
|
|
922
|
+
);
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
test("toStrictIsoTimestamp path: space-separated partial YYYY-MM HH:MM is rejected (issue #1657, codex r2)", () => {
|
|
926
|
+
// "2026-05 10:00" is Date.parse-able; the date separator must be a real
|
|
927
|
+
// date separator (- or /), not the space that precedes the time component.
|
|
928
|
+
// Pre-fix the guard treated the space as a date separator and captured the
|
|
929
|
+
// hour "10" as the day, persisting a fabricated 2026-05-(10) source.
|
|
930
|
+
const turns = [
|
|
931
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
932
|
+
timestamp: "2026-05 10:00" as unknown as string,
|
|
933
|
+
}),
|
|
934
|
+
];
|
|
935
|
+
const result = buildFactProvenance(
|
|
936
|
+
"migrated the production database to pgBouncer",
|
|
937
|
+
turns,
|
|
938
|
+
DEFAULT_CONFIG,
|
|
939
|
+
);
|
|
940
|
+
assert.equal(result.provenance, "unverified");
|
|
941
|
+
assert.ok(result.sources);
|
|
942
|
+
assert.equal(
|
|
943
|
+
result.sources![0]!.observedAt,
|
|
944
|
+
new Date(0).toISOString(),
|
|
945
|
+
"space-separated partial timestamp must fall back to epoch, not a fabricated date",
|
|
946
|
+
);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
test("toStrictIsoTimestamp path: leading-whitespace partial timestamp is rejected (issue #1657, codex r3)", () => {
|
|
950
|
+
// Date.parse trims leading whitespace, so " 2026-05" parses as May 1 —
|
|
951
|
+
// but the ^\d{4} guard anchored at the start would not recognize the
|
|
952
|
+
// string as year-led, bypassing the partial-date rejection. Trim first.
|
|
953
|
+
const turns = [
|
|
954
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
955
|
+
timestamp: " 2026-05" as unknown as string,
|
|
956
|
+
}),
|
|
957
|
+
];
|
|
958
|
+
const result = buildFactProvenance(
|
|
959
|
+
"migrated the production database to pgBouncer",
|
|
960
|
+
turns,
|
|
961
|
+
DEFAULT_CONFIG,
|
|
962
|
+
);
|
|
963
|
+
assert.equal(result.provenance, "unverified");
|
|
964
|
+
assert.ok(result.sources);
|
|
965
|
+
assert.equal(
|
|
966
|
+
result.sources![0]!.observedAt,
|
|
967
|
+
new Date(0).toISOString(),
|
|
968
|
+
"leading-whitespace partial timestamp must fall back to epoch, not a fabricated date",
|
|
969
|
+
);
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
test("toStrictIsoTimestamp path: complete year-first textual-month date is preserved (issue #1657, codex r4)", () => {
|
|
973
|
+
// A complete year-first TEXTUAL-month date ("2026-Jan-15") is normalized by
|
|
974
|
+
// Date.parse and must stay verified — the unified guard must not reject a
|
|
975
|
+
// complete textual month. Date.parse parses this non-ISO form in the LOCAL
|
|
976
|
+
// timezone, so assert the source is preserved (verified, not the epoch
|
|
977
|
+
// fallback) and normalized to strict ISO — without assuming the UTC day,
|
|
978
|
+
// which shifts across timezones (codex r7).
|
|
979
|
+
const turns = [
|
|
980
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
981
|
+
timestamp: "2026-Jan-15" as unknown as string,
|
|
982
|
+
}),
|
|
983
|
+
];
|
|
984
|
+
const result = buildFactProvenance(
|
|
985
|
+
"migrated the production database to pgBouncer",
|
|
986
|
+
turns,
|
|
987
|
+
DEFAULT_CONFIG,
|
|
988
|
+
);
|
|
989
|
+
assert.equal(result.provenance, "verified", "complete textual-month date must stay verified");
|
|
990
|
+
assert.ok(result.sources);
|
|
991
|
+
const observedAt = result.sources![0]!.observedAt;
|
|
992
|
+
assert.match(
|
|
993
|
+
observedAt,
|
|
994
|
+
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/,
|
|
995
|
+
"textual-month observedAt must normalize to strict ISO",
|
|
996
|
+
);
|
|
997
|
+
assert.notEqual(
|
|
998
|
+
observedAt,
|
|
999
|
+
new Date(0).toISOString(),
|
|
1000
|
+
"complete textual-month date must not fall back to the epoch (it was accepted, not rejected)",
|
|
1001
|
+
);
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test("toStrictIsoTimestamp path: partial textual year-month is rejected (issue #1657, codex r5)", () => {
|
|
1005
|
+
// "2026-Jan" is Date.parse-able (-> Jan 1) — a partial year-led TEXTUAL
|
|
1006
|
+
// month that must be rejected just like the numeric "2026-05", not
|
|
1007
|
+
// fabricated. The unified year-led guard covers textual months too.
|
|
1008
|
+
const turns = [
|
|
1009
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
1010
|
+
timestamp: "2026-Jan" as unknown as string,
|
|
1011
|
+
}),
|
|
1012
|
+
];
|
|
1013
|
+
const result = buildFactProvenance(
|
|
1014
|
+
"migrated the production database to pgBouncer",
|
|
1015
|
+
turns,
|
|
1016
|
+
DEFAULT_CONFIG,
|
|
1017
|
+
);
|
|
1018
|
+
assert.equal(result.provenance, "unverified");
|
|
1019
|
+
assert.equal(
|
|
1020
|
+
result.sources![0]!.observedAt,
|
|
1021
|
+
new Date(0).toISOString(),
|
|
1022
|
+
"partial textual year-month must fall back to epoch, not a fabricated Jan 1",
|
|
1023
|
+
);
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
test("toStrictIsoTimestamp path: partial textual year-month with time is rejected (issue #1657, codex r5)", () => {
|
|
1027
|
+
// "2026-Jan 10:00" — the hour must not be captured as the day across the
|
|
1028
|
+
// date/time boundary (textual analogue of "2026-05 10:00").
|
|
1029
|
+
const turns = [
|
|
1030
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
1031
|
+
timestamp: "2026-Jan 10:00" as unknown as string,
|
|
1032
|
+
}),
|
|
1033
|
+
];
|
|
1034
|
+
const result = buildFactProvenance(
|
|
1035
|
+
"migrated the production database to pgBouncer",
|
|
1036
|
+
turns,
|
|
1037
|
+
DEFAULT_CONFIG,
|
|
1038
|
+
);
|
|
1039
|
+
assert.equal(result.provenance, "unverified");
|
|
1040
|
+
assert.equal(
|
|
1041
|
+
result.sources![0]!.observedAt,
|
|
1042
|
+
new Date(0).toISOString(),
|
|
1043
|
+
"partial textual year-month with time must fall back to epoch",
|
|
1044
|
+
);
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
test("toStrictIsoTimestamp path: textual-month calendar overflow is rejected (issue #1657, codex r5)", () => {
|
|
1048
|
+
// "2026-Feb-30" is a COMPLETE textual date but Feb 30 overflows — Date.parse
|
|
1049
|
+
// shifts it to March 2. The unified guard validates the textual month's
|
|
1050
|
+
// calendar day too, so this is rejected rather than fabricated (textual
|
|
1051
|
+
// analogue of the numeric dANc overflow).
|
|
1052
|
+
const turns = [
|
|
1053
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
1054
|
+
timestamp: "2026-Feb-30" as unknown as string,
|
|
1055
|
+
}),
|
|
1056
|
+
];
|
|
1057
|
+
const result = buildFactProvenance(
|
|
1058
|
+
"migrated the production database to pgBouncer",
|
|
1059
|
+
turns,
|
|
1060
|
+
DEFAULT_CONFIG,
|
|
1061
|
+
);
|
|
1062
|
+
assert.equal(result.provenance, "unverified");
|
|
1063
|
+
assert.notEqual(
|
|
1064
|
+
result.sources![0]!.observedAt,
|
|
1065
|
+
"2026-03-02T00:00:00.000Z",
|
|
1066
|
+
"textual-month overflow must not be silently shifted to March 2",
|
|
1067
|
+
);
|
|
1068
|
+
assert.equal(
|
|
1069
|
+
result.sources![0]!.observedAt,
|
|
1070
|
+
new Date(0).toISOString(),
|
|
1071
|
+
"textual-month overflow falls back to epoch",
|
|
1072
|
+
);
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
test("toStrictIsoTimestamp path: full month name is preserved (issue #1657, codex r6)", () => {
|
|
1076
|
+
// Date.parse accepts full month names ("2026-January-15"), so the textual
|
|
1077
|
+
// month map must include both abbreviations and full names — otherwise a
|
|
1078
|
+
// complete, valid full-name date is dropped to an unverified epoch source.
|
|
1079
|
+
// Date.parse parses this non-ISO form in the LOCAL timezone, so assert the
|
|
1080
|
+
// source is preserved (verified, not epoch) without a UTC-day assumption.
|
|
1081
|
+
const turns = [
|
|
1082
|
+
makeTurn("We migrated the production database to pgBouncer.", {
|
|
1083
|
+
timestamp: "2026-January-15" as unknown as string,
|
|
1084
|
+
}),
|
|
1085
|
+
];
|
|
1086
|
+
const result = buildFactProvenance(
|
|
1087
|
+
"migrated the production database to pgBouncer",
|
|
1088
|
+
turns,
|
|
1089
|
+
DEFAULT_CONFIG,
|
|
1090
|
+
);
|
|
1091
|
+
assert.equal(result.provenance, "verified", "full-name textual month must stay verified");
|
|
1092
|
+
assert.ok(result.sources);
|
|
1093
|
+
const observedAt = result.sources![0]!.observedAt;
|
|
1094
|
+
assert.match(
|
|
1095
|
+
observedAt,
|
|
1096
|
+
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/,
|
|
1097
|
+
"full-name textual month observedAt must normalize to strict ISO",
|
|
1098
|
+
);
|
|
1099
|
+
assert.notEqual(
|
|
1100
|
+
observedAt,
|
|
1101
|
+
new Date(0).toISOString(),
|
|
1102
|
+
"full-name textual month must not fall back to the epoch (it was accepted, not rejected)",
|
|
1103
|
+
);
|
|
1104
|
+
});
|