@remnic/core 9.3.726 → 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 +20 -20
- package/dist/access-cli.js +42 -42
- package/dist/access-http.d.ts +20 -5
- package/dist/access-http.js +25 -25
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +24 -24
- package/dist/access-operations-batch.js +21 -21
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +23 -23
- package/dist/access-schema.js +3 -3
- 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 +19 -19
- 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/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 +7 -7
- 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/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- 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 +8 -8
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-Q6D75LVH.js → chunk-27QQC7ZX.js} +75 -14
- package/dist/{chunk-Q6D75LVH.js.map → chunk-27QQC7ZX.js.map} +1 -1
- package/dist/{chunk-53EEQYE5.js → chunk-2KAYTPPT.js} +7 -7
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-BIA7ELIB.js → chunk-36SEDFAU.js} +3 -3
- package/dist/{chunk-UQSVUEN2.js → chunk-37436GML.js} +2 -2
- package/dist/{chunk-RRHHQYJS.js → chunk-3TSTGI6F.js} +2 -2
- package/dist/{chunk-FO3HO52S.js → chunk-4H4F3BFL.js} +2 -2
- package/dist/{chunk-BEUCACTV.js → chunk-4NOI577P.js} +2 -2
- package/dist/{chunk-BBIE7W52.js → chunk-4NR2PDZ5.js} +2 -2
- package/dist/{chunk-TQQH2FTX.js → chunk-6PY5YJAV.js} +101 -15
- package/dist/{chunk-TQQH2FTX.js.map → chunk-6PY5YJAV.js.map} +1 -1
- package/dist/{chunk-PKMQK2WY.js → chunk-773DPI6T.js} +4 -4
- package/dist/{chunk-MQVKHFIU.js → chunk-7RLCCNHU.js} +8 -8
- package/dist/{chunk-XGTBNVIU.js → chunk-AAD2ZS6P.js} +11 -1
- package/dist/chunk-AAD2ZS6P.js.map +1 -0
- package/dist/{chunk-EZNDNMRH.js → chunk-AQLOP5F4.js} +2 -2
- package/dist/{chunk-UNNDFYUX.js → chunk-ARV3AUOM.js} +5 -5
- package/dist/{chunk-NPL7VDM6.js → chunk-ASKXIAHD.js} +2 -2
- package/dist/{chunk-K4ZNARDO.js → chunk-CBWI22JN.js} +2 -2
- package/dist/{chunk-ZOHMPFOB.js → chunk-CKFLLAIS.js} +3 -3
- package/dist/{chunk-DCKNMLEQ.js → chunk-CSRXQ6NB.js} +2 -2
- package/dist/{chunk-6AFVIOVZ.js → chunk-CTOQEZSN.js} +2 -2
- package/dist/{chunk-7NS7AWMZ.js → chunk-DKJCRGWM.js} +3 -3
- package/dist/{chunk-JPUT5NGH.js → chunk-DMQWV6SQ.js} +2 -2
- package/dist/{chunk-F3XZN24Z.js → chunk-DRUDC2UX.js} +2 -2
- package/dist/{chunk-KJR66XOM.js → chunk-EY6MDMHK.js} +2 -2
- package/dist/{chunk-3TUT4PWB.js → chunk-FVZ2ISIX.js} +2 -2
- 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-5NWYQUQC.js → chunk-HOHVIVDR.js} +2 -2
- package/dist/{chunk-6WAJQKBS.js → chunk-JYLFZWUX.js} +2 -2
- package/dist/{chunk-5W7CTZ2E.js → chunk-KQAFEZQX.js} +4 -4
- package/dist/{chunk-YTZ3VGC7.js → chunk-M4V2JVSZ.js} +2 -2
- package/dist/{chunk-PLG6JHQS.js → chunk-MJIHJ7OG.js} +38 -38
- package/dist/{chunk-TA3Y6GEX.js → chunk-MLKWJOAJ.js} +2 -2
- package/dist/{chunk-2NBP25BG.js → chunk-N3VNUM4Z.js} +171 -79
- package/dist/chunk-N3VNUM4Z.js.map +1 -0
- package/dist/{chunk-UKTDXTWX.js → chunk-NSA5ZRUL.js} +6 -6
- package/dist/{chunk-DQXPJ6HD.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-5OOA7SGX.js → chunk-R7IYU5DZ.js} +2 -2
- package/dist/{chunk-WGTWGDR5.js → chunk-SNKPUJHR.js} +5 -5
- package/dist/{chunk-BB57OZWC.js → chunk-UVPL565C.js} +2 -2
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-OAHOL3ZC.js → chunk-XPHVAOBT.js} +111 -30
- package/dist/chunk-XPHVAOBT.js.map +1 -0
- package/dist/{chunk-V7CMKMHM.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-3MS3QVX6.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 +47 -47
- 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 +7 -7
- 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 +7 -7
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +7 -7
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- 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/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 +7 -7
- 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.d.ts +1 -1
- package/dist/extraction.js +2 -2
- 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/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 +61 -61
- 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 +7 -7
- 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 +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- 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 +12 -12
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +7 -7
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +18 -18
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +9 -9
- package/dist/{orchestrator-C_OMSIMZ.d.ts → orchestrator-DqPAz8ot.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +30 -30
- 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 +3 -3
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +7 -7
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- 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 +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +7 -7
- 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 +1 -1
- package/dist/storage.js +6 -6
- 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/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- 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 +7 -7
- 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/extraction-faithfulness.test.ts +240 -0
- package/src/extraction-faithfulness.ts +58 -6
- package/src/faithfulness-config.ts +16 -1
- package/src/local-model-endpoint.ts +24 -3
- package/src/orchestrator.ts +200 -8
- package/src/provenance-extraction.test.ts +266 -0
- package/src/provenance.ts +66 -16
- package/src/types.ts +8 -0
- package/dist/chunk-2NBP25BG.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-MDBZTH7N.js.map +0 -1
- package/dist/chunk-OAHOL3ZC.js.map +0 -1
- package/dist/chunk-XGTBNVIU.js.map +0 -1
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{chunk-53EEQYE5.js.map → chunk-2KAYTPPT.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-BIA7ELIB.js.map → chunk-36SEDFAU.js.map} +0 -0
- /package/dist/{chunk-UQSVUEN2.js.map → chunk-37436GML.js.map} +0 -0
- /package/dist/{chunk-RRHHQYJS.js.map → chunk-3TSTGI6F.js.map} +0 -0
- /package/dist/{chunk-FO3HO52S.js.map → chunk-4H4F3BFL.js.map} +0 -0
- /package/dist/{chunk-BEUCACTV.js.map → chunk-4NOI577P.js.map} +0 -0
- /package/dist/{chunk-BBIE7W52.js.map → chunk-4NR2PDZ5.js.map} +0 -0
- /package/dist/{chunk-PKMQK2WY.js.map → chunk-773DPI6T.js.map} +0 -0
- /package/dist/{chunk-MQVKHFIU.js.map → chunk-7RLCCNHU.js.map} +0 -0
- /package/dist/{chunk-EZNDNMRH.js.map → chunk-AQLOP5F4.js.map} +0 -0
- /package/dist/{chunk-UNNDFYUX.js.map → chunk-ARV3AUOM.js.map} +0 -0
- /package/dist/{chunk-NPL7VDM6.js.map → chunk-ASKXIAHD.js.map} +0 -0
- /package/dist/{chunk-K4ZNARDO.js.map → chunk-CBWI22JN.js.map} +0 -0
- /package/dist/{chunk-ZOHMPFOB.js.map → chunk-CKFLLAIS.js.map} +0 -0
- /package/dist/{chunk-DCKNMLEQ.js.map → chunk-CSRXQ6NB.js.map} +0 -0
- /package/dist/{chunk-6AFVIOVZ.js.map → chunk-CTOQEZSN.js.map} +0 -0
- /package/dist/{chunk-7NS7AWMZ.js.map → chunk-DKJCRGWM.js.map} +0 -0
- /package/dist/{chunk-JPUT5NGH.js.map → chunk-DMQWV6SQ.js.map} +0 -0
- /package/dist/{chunk-F3XZN24Z.js.map → chunk-DRUDC2UX.js.map} +0 -0
- /package/dist/{chunk-KJR66XOM.js.map → chunk-EY6MDMHK.js.map} +0 -0
- /package/dist/{chunk-3TUT4PWB.js.map → chunk-FVZ2ISIX.js.map} +0 -0
- /package/dist/{chunk-6SQQOJ4U.js.map → chunk-GKRLPHPU.js.map} +0 -0
- /package/dist/{chunk-5NWYQUQC.js.map → chunk-HOHVIVDR.js.map} +0 -0
- /package/dist/{chunk-6WAJQKBS.js.map → chunk-JYLFZWUX.js.map} +0 -0
- /package/dist/{chunk-5W7CTZ2E.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-YTZ3VGC7.js.map → chunk-M4V2JVSZ.js.map} +0 -0
- /package/dist/{chunk-PLG6JHQS.js.map → chunk-MJIHJ7OG.js.map} +0 -0
- /package/dist/{chunk-TA3Y6GEX.js.map → chunk-MLKWJOAJ.js.map} +0 -0
- /package/dist/{chunk-UKTDXTWX.js.map → chunk-NSA5ZRUL.js.map} +0 -0
- /package/dist/{chunk-DQXPJ6HD.js.map → chunk-OEEW4KJA.js.map} +0 -0
- /package/dist/{chunk-5OOA7SGX.js.map → chunk-R7IYU5DZ.js.map} +0 -0
- /package/dist/{chunk-WGTWGDR5.js.map → chunk-SNKPUJHR.js.map} +0 -0
- /package/dist/{chunk-BB57OZWC.js.map → chunk-UVPL565C.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-V7CMKMHM.js.map → chunk-Y4YD5TYW.js.map} +0 -0
- /package/dist/{chunk-3MS3QVX6.js.map → chunk-YOLZS7NY.js.map} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import * as canonicalCliFallback from "../cli-fallback.js";
|
|
7
|
+
import * as canonicalThreadKey from "../thread-key.js";
|
|
8
|
+
import * as aliasCliFallback from "./codex-cli-fallback.js";
|
|
9
|
+
import * as aliasThreadKey from "./codex-thread-key.js";
|
|
10
|
+
|
|
11
|
+
const pkgJsonPath = fileURLToPath(new URL("../../package.json", import.meta.url));
|
|
12
|
+
|
|
13
|
+
test("codex-cli-fallback alias re-exports the same value symbols as cli-fallback", () => {
|
|
14
|
+
// export * forwards live bindings by reference, so the function objects and
|
|
15
|
+
// the test-hooks namespace are identical (===) across both subpaths.
|
|
16
|
+
assert.equal(
|
|
17
|
+
aliasCliFallback.callCodexCliFallback,
|
|
18
|
+
canonicalCliFallback.callCodexCliFallback,
|
|
19
|
+
);
|
|
20
|
+
assert.equal(
|
|
21
|
+
aliasCliFallback.setCodexCliFallbackRunnerForProcess,
|
|
22
|
+
canonicalCliFallback.setCodexCliFallbackRunnerForProcess,
|
|
23
|
+
);
|
|
24
|
+
assert.equal(
|
|
25
|
+
aliasCliFallback.__codexCliFallbackTestHooks,
|
|
26
|
+
canonicalCliFallback.__codexCliFallbackTestHooks,
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("codex-thread-key alias re-exports the same symbol as thread-key", () => {
|
|
31
|
+
assert.equal(
|
|
32
|
+
aliasThreadKey.CODEX_THREAD_KEY_PREFIX,
|
|
33
|
+
canonicalThreadKey.CODEX_THREAD_KEY_PREFIX,
|
|
34
|
+
);
|
|
35
|
+
assert.equal(aliasThreadKey.CODEX_THREAD_KEY_PREFIX, "codex-thread:");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("every named runtime export of cli-fallback is forwarded by the alias", () => {
|
|
39
|
+
for (const key of Object.keys(canonicalCliFallback)) {
|
|
40
|
+
assert.equal(
|
|
41
|
+
key in aliasCliFallback,
|
|
42
|
+
true,
|
|
43
|
+
`alias missing forwarded export: ${key}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("importing the old subpath resolves to the same symbol as the new subpath", async () => {
|
|
49
|
+
// @remnic/core self-references its own exports map. Under --conditions=
|
|
50
|
+
// remnic-source (set by preflight/test NODE_OPTIONS) the subpaths resolve to
|
|
51
|
+
// src/. Both specifiers land on the same module record, so the exported
|
|
52
|
+
// function is the same object reference.
|
|
53
|
+
const [byNew, byOld] = await Promise.all([
|
|
54
|
+
import("@remnic/core/cli-fallback"),
|
|
55
|
+
import("@remnic/core/codex-cli-fallback"),
|
|
56
|
+
]);
|
|
57
|
+
assert.equal(byOld.callCodexCliFallback, byNew.callCodexCliFallback);
|
|
58
|
+
|
|
59
|
+
const [newKey, oldKey] = await Promise.all([
|
|
60
|
+
import("@remnic/core/thread-key"),
|
|
61
|
+
import("@remnic/core/codex-thread-key"),
|
|
62
|
+
]);
|
|
63
|
+
assert.equal(oldKey.CODEX_THREAD_KEY_PREFIX, newKey.CODEX_THREAD_KEY_PREFIX);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("the deprecated subpaths are registered with a removal timeline in the exports map", () => {
|
|
67
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf8")) as {
|
|
68
|
+
exports: Record<string, { deprecated?: string; import?: string }>;
|
|
69
|
+
};
|
|
70
|
+
const aliasEntries = [
|
|
71
|
+
"./codex-cli-fallback",
|
|
72
|
+
"./codex-cli-fallback.js",
|
|
73
|
+
"./codex-thread-key",
|
|
74
|
+
"./codex-thread-key.js",
|
|
75
|
+
];
|
|
76
|
+
for (const subpath of aliasEntries) {
|
|
77
|
+
const entry = pkg.exports[subpath];
|
|
78
|
+
assert.ok(entry, `missing exports entry for ${subpath}`);
|
|
79
|
+
assert.ok(
|
|
80
|
+
entry.deprecated,
|
|
81
|
+
`exports entry for ${subpath} missing a deprecated field`,
|
|
82
|
+
);
|
|
83
|
+
assert.match(
|
|
84
|
+
entry.deprecated,
|
|
85
|
+
/v?10\.0\.0|remov/i,
|
|
86
|
+
`deprecated field for ${subpath} should state a removal timeline`,
|
|
87
|
+
);
|
|
88
|
+
assert.ok(
|
|
89
|
+
entry.import?.includes("/compat/"),
|
|
90
|
+
`exports entry for ${subpath} should target the compat re-export module`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecated alias subpath `@remnic/core/codex-thread-key`.
|
|
3
|
+
*
|
|
4
|
+
* PR #1638 renamed `src/codex-thread-key.ts` -> `src/thread-key.ts` to
|
|
5
|
+
* graduate the host-prefixed name per CLAUDE.md rule 31. This thin re-export
|
|
6
|
+
* keeps the old subpath resolving during the compat window so external
|
|
7
|
+
* consumers importing `@remnic/core/codex-thread-key` do not hit
|
|
8
|
+
* ERR_PACKAGE_PATH_NOT_EXPORTED. It forwards every export to the canonical
|
|
9
|
+
* module -- no logic is duplicated.
|
|
10
|
+
*
|
|
11
|
+
* Removal timeline: v10.0.0 (see issue #1643).
|
|
12
|
+
* Import from `@remnic/core/thread-key` instead.
|
|
13
|
+
*/
|
|
14
|
+
export * from "../thread-key.js";
|
|
15
|
+
|
|
16
|
+
process.emitWarning(
|
|
17
|
+
"@remnic/core/codex-thread-key is a deprecated alias; import from @remnic/core/thread-key instead. Removed in v10.0.0 (issue #1643).",
|
|
18
|
+
{
|
|
19
|
+
type: "DeprecationWarning",
|
|
20
|
+
code: "REMNIC_DEP_CORE_SUBPATH_CODEX_THREAD_KEY",
|
|
21
|
+
detail: "PR #1638 renamed codex-thread-key.ts to thread-key.ts.",
|
|
22
|
+
},
|
|
23
|
+
);
|
|
@@ -26,6 +26,7 @@ import type { FaithfulnessResult } from "./extraction-faithfulness.js";
|
|
|
26
26
|
import type { FallbackLlmClient } from "./fallback-llm.js";
|
|
27
27
|
import type { LocalLlmClient } from "./local-llm.js";
|
|
28
28
|
import type { MemoryFrontmatter } from "./types.js";
|
|
29
|
+
import { callOpenAiCompatibleChat } from "./local-model-endpoint.js";
|
|
29
30
|
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
31
32
|
// Helpers — stub LLM clients
|
|
@@ -1429,3 +1430,242 @@ test("checkFaithfulnessBatch: local-endpoint failure does NOT leak the local mod
|
|
|
1429
1430
|
);
|
|
1430
1431
|
assert.equal(result.results[0]?.ok, true);
|
|
1431
1432
|
});
|
|
1433
|
+
|
|
1434
|
+
// ---------------------------------------------------------------------------
|
|
1435
|
+
// Issue #1700 nits #5 + #6: probe signal forwarding + local parse-fallback
|
|
1436
|
+
// ---------------------------------------------------------------------------
|
|
1437
|
+
|
|
1438
|
+
test("callOpenAiCompatibleChat: a pre-aborted batch signal aborts the probe immediately, not after timeoutMs (#1700 nit #5)", async () => {
|
|
1439
|
+
// The batch signal is ALREADY aborted when the probe starts. With nit #5 the
|
|
1440
|
+
// probe forwards it and aborts instantly; without it the probe would wait for
|
|
1441
|
+
// its own timeoutMs (5000ms here). Assert the probe returns null fast.
|
|
1442
|
+
const endpoint = { baseUrl: "http://localhost:11434/v1", model: "remnic-faithfulness-gate-v1" };
|
|
1443
|
+
const batchController = new AbortController();
|
|
1444
|
+
batchController.abort();
|
|
1445
|
+
const spy = ((_url: string, init: RequestInit) =>
|
|
1446
|
+
new Promise<Response>((_resolve, reject) => {
|
|
1447
|
+
const sig = init.signal;
|
|
1448
|
+
if (!sig) return reject(new Error("no signal"));
|
|
1449
|
+
if (sig.aborted) return reject(new Error("aborted"));
|
|
1450
|
+
sig.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
|
|
1451
|
+
})) as unknown as typeof fetch;
|
|
1452
|
+
const start = Date.now();
|
|
1453
|
+
const result = await callOpenAiCompatibleChat(
|
|
1454
|
+
endpoint,
|
|
1455
|
+
[{ role: "user", content: "hi" }],
|
|
1456
|
+
{ timeoutMs: 5000, signal: batchController.signal },
|
|
1457
|
+
spy,
|
|
1458
|
+
);
|
|
1459
|
+
const elapsed = Date.now() - start;
|
|
1460
|
+
assert.equal(result, null, "a pre-aborted signal must fail the probe closed (null)");
|
|
1461
|
+
assert.ok(
|
|
1462
|
+
elapsed < 1000,
|
|
1463
|
+
`probe must abort immediately on a pre-aborted signal (${elapsed}ms), not wait timeoutMs`,
|
|
1464
|
+
);
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
test("checkFaithfulnessBatch: default surfaces malformed_output for a 200-with-garbage local response (#1700 nit #6)", async () => {
|
|
1468
|
+
// extractionFaithfulnessLocalParseFallback unset (default false): a local
|
|
1469
|
+
// endpoint returning 200 with non-verdict JSON surfaces malformed_output so a
|
|
1470
|
+
// misconfigured endpoint is visible. The configured chain must NOT run.
|
|
1471
|
+
const inputs = [{ factText: "Fact", quote: "Quote" }];
|
|
1472
|
+
const config = parseConfig({
|
|
1473
|
+
extractionFaithfulnessModel: "remnic-faithfulness-gate-v1",
|
|
1474
|
+
extractionFaithfulnessBaseUrl: "http://localhost:11434/v1",
|
|
1475
|
+
extractionFaithfulnessTimeoutMs: 5000,
|
|
1476
|
+
});
|
|
1477
|
+
const fallbackCalls: Array<{ messages: unknown; options: unknown }> = [];
|
|
1478
|
+
const { fetch: garbageFetch } = fakeFetchFor("http://localhost:11434/v1", () => ({
|
|
1479
|
+
model: "remnic-faithfulness-gate-v1",
|
|
1480
|
+
choices: [{ message: { content: "this is not a verdict array" } }],
|
|
1481
|
+
}));
|
|
1482
|
+
const result = await checkFaithfulnessBatch(
|
|
1483
|
+
inputs,
|
|
1484
|
+
config,
|
|
1485
|
+
null,
|
|
1486
|
+
stubFallbackLlm(JSON.stringify([{ index: 0, verdict: "entailed" }]), fallbackCalls),
|
|
1487
|
+
garbageFetch,
|
|
1488
|
+
);
|
|
1489
|
+
assert.equal(fallbackCalls.length, 0, "chain must NOT run when flag is off — garbage surfaces loudly");
|
|
1490
|
+
assert.equal(result.results[0]?.ok, false);
|
|
1491
|
+
if (!result.results[0]?.ok) {
|
|
1492
|
+
assert.equal(result.results[0].error.code, "malformed_output");
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
test("checkFaithfulnessBatch: extractionFaithfulnessLocalParseFallback falls back to the chain on local parse failure (#1700 nit #6)", async () => {
|
|
1497
|
+
// Flag ON: the same 200-with-garbage local response now falls through to the
|
|
1498
|
+
// configured chain instead of surfacing malformed_output (resilient fallback).
|
|
1499
|
+
const inputs = [{ factText: "Fact", quote: "Quote" }];
|
|
1500
|
+
const config = parseConfig({
|
|
1501
|
+
extractionFaithfulnessModel: "remnic-faithfulness-gate-v1",
|
|
1502
|
+
extractionFaithfulnessBaseUrl: "http://localhost:11434/v1",
|
|
1503
|
+
extractionFaithfulnessTimeoutMs: 5000,
|
|
1504
|
+
extractionFaithfulnessLocalParseFallback: true,
|
|
1505
|
+
});
|
|
1506
|
+
const fallbackCalls: Array<{ messages: unknown; options: unknown }> = [];
|
|
1507
|
+
const { fetch: garbageFetch } = fakeFetchFor("http://localhost:11434/v1", () => ({
|
|
1508
|
+
model: "remnic-faithfulness-gate-v1",
|
|
1509
|
+
choices: [{ message: { content: "this is not a verdict array" } }],
|
|
1510
|
+
}));
|
|
1511
|
+
const result = await checkFaithfulnessBatch(
|
|
1512
|
+
inputs,
|
|
1513
|
+
config,
|
|
1514
|
+
null,
|
|
1515
|
+
stubFallbackLlm(JSON.stringify([{ index: 0, verdict: "entailed" }]), fallbackCalls),
|
|
1516
|
+
garbageFetch,
|
|
1517
|
+
);
|
|
1518
|
+
assert.equal(
|
|
1519
|
+
fallbackCalls.length,
|
|
1520
|
+
1,
|
|
1521
|
+
"chain MUST run when flag is on and the local response is unparseable",
|
|
1522
|
+
);
|
|
1523
|
+
assert.equal(result.results[0]?.ok, true);
|
|
1524
|
+
if (result.results[0]?.ok) {
|
|
1525
|
+
assert.equal(result.results[0].verdict, "entailed", "fallback chain verdict is used");
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
test("checkFaithfulnessBatch: extractionFaithfulnessLocalParseFallback does NOT fall back when the local response is valid (#1700 nit #6 regression)", async () => {
|
|
1530
|
+
// Flag ON but the local response IS a valid verdict array → use it, do not
|
|
1531
|
+
// call the chain. Guards against the flag over-triggering on good responses.
|
|
1532
|
+
const inputs = [{ factText: "Fact", quote: "Quote" }];
|
|
1533
|
+
const config = parseConfig({
|
|
1534
|
+
extractionFaithfulnessModel: "remnic-faithfulness-gate-v1",
|
|
1535
|
+
extractionFaithfulnessBaseUrl: "http://localhost:11434/v1",
|
|
1536
|
+
extractionFaithfulnessTimeoutMs: 5000,
|
|
1537
|
+
extractionFaithfulnessLocalParseFallback: true,
|
|
1538
|
+
});
|
|
1539
|
+
const fallbackCalls: Array<{ messages: unknown; options: unknown }> = [];
|
|
1540
|
+
const { fetch: goodFetch } = fakeFetchFor("http://localhost:11434/v1", () => ({
|
|
1541
|
+
model: "remnic-faithfulness-gate-v1",
|
|
1542
|
+
choices: [{ message: { content: JSON.stringify([{ index: 0, verdict: "contradicted" }]) } }],
|
|
1543
|
+
}));
|
|
1544
|
+
const result = await checkFaithfulnessBatch(
|
|
1545
|
+
inputs,
|
|
1546
|
+
config,
|
|
1547
|
+
null,
|
|
1548
|
+
stubFallbackLlm(JSON.stringify([{ index: 0, verdict: "entailed" }]), fallbackCalls),
|
|
1549
|
+
goodFetch,
|
|
1550
|
+
);
|
|
1551
|
+
assert.equal(fallbackCalls.length, 0, "chain must NOT run when the local response is valid, even with the flag on");
|
|
1552
|
+
assert.equal(result.results[0]?.ok, true);
|
|
1553
|
+
if (result.results[0]?.ok) {
|
|
1554
|
+
assert.equal(result.results[0].verdict, "contradicted", "valid local verdict is used");
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
test("checkFaithfulnessBatch: extractionFaithfulnessLocalParseFallback falls back when the local response is a PARTIAL verdict array (codex P2 PRRT_kwDORJXyws6O6zwZ)", async () => {
|
|
1559
|
+
// Flag ON: a local endpoint that returns a valid-but-INCOMPLETE verdict
|
|
1560
|
+
// array (1 of N entries) must fall through to the configured chain, NOT be
|
|
1561
|
+
// accepted as-is. parseFaithfulnessResponse is truthy as soon as ONE entry
|
|
1562
|
+
// is valid, so the previous guard accepted the partial response and the
|
|
1563
|
+
// missing indexes surfaced as malformed_output downstream -- defeating the
|
|
1564
|
+
// resilient fallback. Accept the local response only when it covers every
|
|
1565
|
+
// expected index.
|
|
1566
|
+
const inputs = [
|
|
1567
|
+
{ factText: "Fact A", quote: "Quote A" },
|
|
1568
|
+
{ factText: "Fact B", quote: "Quote B" },
|
|
1569
|
+
];
|
|
1570
|
+
const config = parseConfig({
|
|
1571
|
+
extractionFaithfulnessModel: "remnic-faithfulness-gate-v1",
|
|
1572
|
+
extractionFaithfulnessBaseUrl: "http://localhost:11434/v1",
|
|
1573
|
+
extractionFaithfulnessTimeoutMs: 5000,
|
|
1574
|
+
extractionFaithfulnessLocalParseFallback: true,
|
|
1575
|
+
});
|
|
1576
|
+
const fallbackCalls: Array<{ messages: unknown; options: unknown }> = [];
|
|
1577
|
+
// Local endpoint returns only index 0 -- index 1 is missing (partial).
|
|
1578
|
+
const { fetch: partialFetch } = fakeFetchFor("http://localhost:11434/v1", () => ({
|
|
1579
|
+
model: "remnic-faithfulness-gate-v1",
|
|
1580
|
+
choices: [{ message: { content: JSON.stringify([{ index: 0, verdict: "contradicted" }]) } }],
|
|
1581
|
+
}));
|
|
1582
|
+
const result = await checkFaithfulnessBatch(
|
|
1583
|
+
inputs,
|
|
1584
|
+
config,
|
|
1585
|
+
null,
|
|
1586
|
+
stubFallbackLlm(
|
|
1587
|
+
JSON.stringify([
|
|
1588
|
+
{ index: 0, verdict: "entailed" },
|
|
1589
|
+
{ index: 1, verdict: "unsupported" },
|
|
1590
|
+
]),
|
|
1591
|
+
fallbackCalls,
|
|
1592
|
+
),
|
|
1593
|
+
partialFetch,
|
|
1594
|
+
);
|
|
1595
|
+
assert.equal(
|
|
1596
|
+
fallbackCalls.length,
|
|
1597
|
+
1,
|
|
1598
|
+
"a partial local verdict array must fall through to the configured chain",
|
|
1599
|
+
);
|
|
1600
|
+
// The chain's complete verdict array is used for both indexes.
|
|
1601
|
+
assert.equal(result.results[0]?.ok && result.results[0].verdict, "entailed");
|
|
1602
|
+
assert.equal(result.results[1]?.ok && result.results[1].verdict, "unsupported");
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
test("checkFaithfulnessBatch: extractionFaithfulnessLocalParseFallback falls back when local indexes are fractional/non-contiguous (codex P2 PRRT_kwDORJXyws6O7PfY)", async () => {
|
|
1606
|
+
// Flag ON: a malformed local response with expectedCount distinct but
|
|
1607
|
+
// NON-INTEGER indexes (e.g. 0 and 0.5 for a two-item batch) must NOT be
|
|
1608
|
+
// accepted. parseFaithfulnessResponse used to admit any numeric index, so a
|
|
1609
|
+
// size-only check passed while index 1 stayed missing -> malformed_output.
|
|
1610
|
+
// parseEntries now rejects non-integer indexes, and the gate requires every
|
|
1611
|
+
// integer key 0..N-1 before accepting the local response.
|
|
1612
|
+
const inputs = [
|
|
1613
|
+
{ factText: "Fact A", quote: "Quote A" },
|
|
1614
|
+
{ factText: "Fact B", quote: "Quote B" },
|
|
1615
|
+
];
|
|
1616
|
+
const config = parseConfig({
|
|
1617
|
+
extractionFaithfulnessModel: "remnic-faithfulness-gate-v1",
|
|
1618
|
+
extractionFaithfulnessBaseUrl: "http://localhost:11434/v1",
|
|
1619
|
+
extractionFaithfulnessTimeoutMs: 5000,
|
|
1620
|
+
extractionFaithfulnessLocalParseFallback: true,
|
|
1621
|
+
});
|
|
1622
|
+
const fallbackCalls: Array<{ messages: unknown; options: unknown }> = [];
|
|
1623
|
+
// Local endpoint returns indexes 0 and 0.5 -- two distinct keys, but index 1
|
|
1624
|
+
// is missing (fractional index is invalid).
|
|
1625
|
+
const { fetch: fracFetch } = fakeFetchFor("http://localhost:11434/v1", () => ({
|
|
1626
|
+
model: "remnic-faithfulness-gate-v1",
|
|
1627
|
+
choices: [{ message: { content: JSON.stringify([
|
|
1628
|
+
{ index: 0, verdict: "contradicted" },
|
|
1629
|
+
{ index: 0.5, verdict: "entailed" },
|
|
1630
|
+
]) } }],
|
|
1631
|
+
}));
|
|
1632
|
+
const result = await checkFaithfulnessBatch(
|
|
1633
|
+
inputs,
|
|
1634
|
+
config,
|
|
1635
|
+
null,
|
|
1636
|
+
stubFallbackLlm(
|
|
1637
|
+
JSON.stringify([
|
|
1638
|
+
{ index: 0, verdict: "entailed" },
|
|
1639
|
+
{ index: 1, verdict: "unsupported" },
|
|
1640
|
+
]),
|
|
1641
|
+
fallbackCalls,
|
|
1642
|
+
),
|
|
1643
|
+
fracFetch,
|
|
1644
|
+
);
|
|
1645
|
+
assert.equal(
|
|
1646
|
+
fallbackCalls.length,
|
|
1647
|
+
1,
|
|
1648
|
+
"a fractional/non-contiguous local response must fall through to the chain",
|
|
1649
|
+
);
|
|
1650
|
+
assert.equal(result.results[0]?.ok && result.results[0].verdict, "entailed");
|
|
1651
|
+
assert.equal(result.results[1]?.ok && result.results[1].verdict, "unsupported");
|
|
1652
|
+
});
|
|
1653
|
+
|
|
1654
|
+
test('parseConfig: extractionFaithfulnessLocalParseFallback coerces CLI string "true" (#1700 review — coerceBool parity)', () => {
|
|
1655
|
+
// A CLI override reaches the parser as the string "true"; === true would
|
|
1656
|
+
// silently leave the flag disabled. coerceBool must turn it on (gotcha 36).
|
|
1657
|
+
const onByString = parseConfig({
|
|
1658
|
+
extractionFaithfulnessLocalParseFallback: "true",
|
|
1659
|
+
});
|
|
1660
|
+
assert.equal(onByString.extractionFaithfulnessLocalParseFallback, true);
|
|
1661
|
+
const onByBool = parseConfig({
|
|
1662
|
+
extractionFaithfulnessLocalParseFallback: true,
|
|
1663
|
+
});
|
|
1664
|
+
assert.equal(onByBool.extractionFaithfulnessLocalParseFallback, true);
|
|
1665
|
+
const offDefault = parseConfig({});
|
|
1666
|
+
assert.equal(offDefault.extractionFaithfulnessLocalParseFallback, false);
|
|
1667
|
+
const offByString = parseConfig({
|
|
1668
|
+
extractionFaithfulnessLocalParseFallback: "false",
|
|
1669
|
+
});
|
|
1670
|
+
assert.equal(offByString.extractionFaithfulnessLocalParseFallback, false);
|
|
1671
|
+
});
|
|
@@ -220,6 +220,27 @@ export function parseFaithfulnessResponse(
|
|
|
220
220
|
return null;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
/**
|
|
224
|
+
* True when `map` contains an entry for every integer index in
|
|
225
|
+
* [0, expectedCount). The local parse-fallback gate uses this to accept a
|
|
226
|
+
* local model-lab response only when it is COMPLETE: size alone is
|
|
227
|
+
* insufficient because a malformed response with duplicate/fractional indexes
|
|
228
|
+
* can yield expectedCount distinct map keys without covering index 0..N-1
|
|
229
|
+
* (codex P2 PRRT_kwDORJXyws6O7PfY). parseEntries now rejects non-integer
|
|
230
|
+
* indexes so the two checks agree, but the explicit coverage loop documents
|
|
231
|
+
* the invariant and stays robust to any future parser relaxation.
|
|
232
|
+
*/
|
|
233
|
+
function coversAllIndexes(
|
|
234
|
+
map: Map<number, ParsedFaithfulnessEntry>,
|
|
235
|
+
expectedCount: number,
|
|
236
|
+
): boolean {
|
|
237
|
+
if (map.size < expectedCount) return false;
|
|
238
|
+
for (let i = 0; i < expectedCount; i++) {
|
|
239
|
+
if (!map.has(i)) return false;
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
223
244
|
function parseEntries(
|
|
224
245
|
data: unknown,
|
|
225
246
|
expectedCount: number,
|
|
@@ -243,7 +264,7 @@ function parseEntries(
|
|
|
243
264
|
if (!entry || typeof entry !== "object") continue;
|
|
244
265
|
const obj = entry as Record<string, unknown>;
|
|
245
266
|
const idx = typeof obj.index === "number" ? obj.index : undefined;
|
|
246
|
-
if (idx === undefined || idx < 0 || idx >= expectedCount) continue;
|
|
267
|
+
if (idx === undefined || !Number.isInteger(idx) || idx < 0 || idx >= expectedCount) continue;
|
|
247
268
|
const verdictRaw = typeof obj.verdict === "string" ? obj.verdict : undefined;
|
|
248
269
|
if (!verdictRaw || !VALID_VERDICTS.has(verdictRaw)) continue;
|
|
249
270
|
const rationale =
|
|
@@ -279,6 +300,7 @@ interface LlmCallResult {
|
|
|
279
300
|
async function callFaithfulnessLlm(
|
|
280
301
|
systemPrompt: string,
|
|
281
302
|
userPrompt: string,
|
|
303
|
+
expectedCount: number,
|
|
282
304
|
config: PluginConfig,
|
|
283
305
|
localLlm: LocalLlmClient | null,
|
|
284
306
|
fallbackLlm: FallbackLlmClient | null,
|
|
@@ -311,15 +333,44 @@ async function callFaithfulnessLlm(
|
|
|
311
333
|
const result = await callOpenAiCompatibleChat(
|
|
312
334
|
localEndpoint,
|
|
313
335
|
messages,
|
|
314
|
-
{
|
|
336
|
+
{
|
|
337
|
+
temperature: 0.1,
|
|
338
|
+
maxTokens: 2048,
|
|
339
|
+
responseFormatJson: true,
|
|
340
|
+
timeoutMs: probeBudgetMs,
|
|
341
|
+
// Forward the batch signal so the probe aborts the instant the batch
|
|
342
|
+
// budget elapses, not after probeBudgetMs (issue #1700 nit #5).
|
|
343
|
+
...(signal ? { signal } : {}),
|
|
344
|
+
},
|
|
315
345
|
fetchImpl,
|
|
316
346
|
);
|
|
317
347
|
if (result?.content) {
|
|
318
|
-
|
|
348
|
+
// Issue #1700 nit #6: pre-validate the local endpoint response shape.
|
|
349
|
+
// Default (extractionFaithfulnessLocalParseFallback=false) returns the
|
|
350
|
+
// content as-is -- a 200-with-garbage local response surfaces
|
|
351
|
+
// malformed_output, alerting the operator to a misconfigured endpoint.
|
|
352
|
+
// When the operator opts into resilient fallback, an unparseable OR
|
|
353
|
+
// PARTIAL local response falls through to the configured chain instead
|
|
354
|
+
// of surfacing. parseFaithfulnessResponse is truthy as soon as ONE entry
|
|
355
|
+
// is valid, so accept the local response only when the parsed verdict
|
|
356
|
+
// map covers every expected index -- otherwise the missing indexes would
|
|
357
|
+
// surface as malformed_output downstream, defeating the resilient
|
|
358
|
+
// fallback (codex P2 PRRT_kwDORJXyws6O6zwZ).
|
|
359
|
+
if (!config.extractionFaithfulnessLocalParseFallback) {
|
|
360
|
+
return { content: result.content, modelUsed: result.modelUsed };
|
|
361
|
+
}
|
|
362
|
+
const parsedLocal = parseFaithfulnessResponse(result.content, expectedCount);
|
|
363
|
+
if (parsedLocal && coversAllIndexes(parsedLocal, expectedCount)) {
|
|
364
|
+
return { content: result.content, modelUsed: result.modelUsed };
|
|
365
|
+
}
|
|
366
|
+
log.debug(
|
|
367
|
+
"extraction-faithfulness: local endpoint returned incomplete/unparseable output; falling back to configured chain",
|
|
368
|
+
);
|
|
369
|
+
} else {
|
|
370
|
+
log.debug(
|
|
371
|
+
"extraction-faithfulness: local model-lab endpoint unavailable, trying configured chain",
|
|
372
|
+
);
|
|
319
373
|
}
|
|
320
|
-
log.debug(
|
|
321
|
-
"extraction-faithfulness: local model-lab endpoint unavailable, trying configured chain",
|
|
322
|
-
);
|
|
323
374
|
}
|
|
324
375
|
|
|
325
376
|
// extractionFaithfulnessModel is the LOCAL served model's name (e.g.
|
|
@@ -532,6 +583,7 @@ export async function checkFaithfulnessBatch(
|
|
|
532
583
|
const callPromise = callFaithfulnessLlm(
|
|
533
584
|
FAITHFULNESS_SYSTEM_PROMPT,
|
|
534
585
|
userPrompt,
|
|
586
|
+
checkableInputs.length,
|
|
535
587
|
config,
|
|
536
588
|
localLlm,
|
|
537
589
|
fallbackLlm,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* `config.ts` spread the result in one line.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { coerceNumber } from "./connectors/coerce.js";
|
|
20
|
+
import { coerceBool, coerceNumber } from "./connectors/coerce.js";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Parse a value as a strict integer >= `min`, throwing on anything else.
|
|
@@ -67,6 +67,7 @@ export function parseFaithfulnessGateConfig(cfg: Record<string, unknown>): {
|
|
|
67
67
|
extractionFaithfulnessBaseUrl: string;
|
|
68
68
|
extractionFaithfulnessContextChars: number;
|
|
69
69
|
extractionFaithfulnessTimeoutMs: number;
|
|
70
|
+
extractionFaithfulnessLocalParseFallback: boolean;
|
|
70
71
|
} {
|
|
71
72
|
const gateValue = cfg.extractionFaithfulnessGate;
|
|
72
73
|
let extractionFaithfulnessGate: "off" | "shadow" | "enforce";
|
|
@@ -105,12 +106,26 @@ export function parseFaithfulnessGateConfig(cfg: Record<string, unknown>): {
|
|
|
105
106
|
60_000,
|
|
106
107
|
);
|
|
107
108
|
|
|
109
|
+
// Issue #1700 nit #6: resilient-fallback toggle for the local model-lab
|
|
110
|
+
// endpoint. Default false preserves the loud-signal behavior (a 200-with-
|
|
111
|
+
// garbage local response surfaces malformed_output so a misconfigured
|
|
112
|
+
// endpoint is visible); true falls back to the configured chain on local
|
|
113
|
+
// parse failure. Byte-identical pre-feature when unset (rule 39).
|
|
114
|
+
// coerceBool for CLI-string parity: a CLI override like
|
|
115
|
+
// --config extractionFaithfulnessLocalParseFallback=true reaches here as the
|
|
116
|
+
// string "true"; === true would silently leave the flag disabled. Matches
|
|
117
|
+
// every other CLI-settable boolean in config.ts (gotcha 36, codex P2
|
|
118
|
+
// PRRT_kwDORJXyws6O6gDQ / cursor BUGBOT e273b22c).
|
|
119
|
+
const extractionFaithfulnessLocalParseFallback =
|
|
120
|
+
coerceBool(cfg.extractionFaithfulnessLocalParseFallback) === true;
|
|
121
|
+
|
|
108
122
|
return {
|
|
109
123
|
extractionFaithfulnessGate,
|
|
110
124
|
extractionFaithfulnessModel,
|
|
111
125
|
extractionFaithfulnessBaseUrl,
|
|
112
126
|
extractionFaithfulnessContextChars,
|
|
113
127
|
extractionFaithfulnessTimeoutMs,
|
|
128
|
+
extractionFaithfulnessLocalParseFallback,
|
|
114
129
|
};
|
|
115
130
|
}
|
|
116
131
|
|
|
@@ -42,10 +42,15 @@ export interface EndpointChatMessage {
|
|
|
42
42
|
|
|
43
43
|
/** Options for a single local-endpoint chat call. */
|
|
44
44
|
export interface EndpointChatOptions {
|
|
45
|
-
/** Per-call timeout in ms.
|
|
46
|
-
* (matches the local-LLM precedent
|
|
47
|
-
* timer so one slow server can't starve the batch). */
|
|
45
|
+
/** Per-call timeout in ms. Bounds each attempt so one slow server can't
|
|
46
|
+
* starve the batch (matches the local-LLM precedent). */
|
|
48
47
|
timeoutMs: number;
|
|
48
|
+
/** Optional batch AbortSignal forwarded into the probe (issue #1700 nit #5).
|
|
49
|
+
* When the batch budget elapses (or the caller aborts), the in-flight probe
|
|
50
|
+
* aborts immediately instead of waiting for its own `timeoutMs` — a
|
|
51
|
+
* micro-optimization, not a correctness fix (the outer race already bounds
|
|
52
|
+
* the total). Undefined preserves the prior per-attempt-only behavior. */
|
|
53
|
+
signal?: AbortSignal;
|
|
49
54
|
/** Sampling temperature — classification wants low entropy (0.0–0.2). */
|
|
50
55
|
temperature?: number;
|
|
51
56
|
/** Max output tokens. */
|
|
@@ -119,6 +124,21 @@ export async function callOpenAiCompatibleChat(
|
|
|
119
124
|
const url = joinBaseUrl(endpoint.baseUrl, "/chat/completions");
|
|
120
125
|
const controller = new AbortController();
|
|
121
126
|
const timer = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
127
|
+
// Forward the batch AbortSignal into the probe (issue #1700 nit #5): if the
|
|
128
|
+
// batch budget already elapsed, abort the in-flight probe immediately rather
|
|
129
|
+
// than waiting for the probe's own timeoutMs. AbortSignal.any is ES2024
|
|
130
|
+
// (forbidden here); wire the listener manually + guard the already-aborted
|
|
131
|
+
// case. The fetch still uses the probe's controller, so clearing the timer
|
|
132
|
+
// below is the only cleanup needed.
|
|
133
|
+
const batchSignal = options.signal;
|
|
134
|
+
const onBatchAbort = () => controller.abort();
|
|
135
|
+
if (batchSignal) {
|
|
136
|
+
if (batchSignal.aborted) {
|
|
137
|
+
controller.abort();
|
|
138
|
+
} else {
|
|
139
|
+
batchSignal.addEventListener("abort", onBatchAbort, { once: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
122
142
|
try {
|
|
123
143
|
const body: Record<string, unknown> = {
|
|
124
144
|
model: endpoint.model,
|
|
@@ -150,6 +170,7 @@ export async function callOpenAiCompatibleChat(
|
|
|
150
170
|
return { content, modelUsed: extractModel(parsed) ?? endpoint.model };
|
|
151
171
|
} finally {
|
|
152
172
|
clearTimeout(timer);
|
|
173
|
+
if (batchSignal) batchSignal.removeEventListener("abort", onBatchAbort);
|
|
153
174
|
}
|
|
154
175
|
}
|
|
155
176
|
|