@remnic/core 10.0.37 → 10.0.38

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.
Files changed (245) hide show
  1. package/dist/access-admin-ops-surface.js +24 -24
  2. package/dist/access-authorization-probe.js +25 -25
  3. package/dist/access-boundary.js +24 -24
  4. package/dist/access-cli.js +71 -71
  5. package/dist/access-extraction-force-flush.js +24 -24
  6. package/dist/access-http-briefing.js +25 -25
  7. package/dist/access-http-query.js +26 -26
  8. package/dist/access-http.js +41 -41
  9. package/dist/access-identity-continuity-surface.js +24 -24
  10. package/dist/access-lcm-surface.js +24 -24
  11. package/dist/access-mcp.js +34 -34
  12. package/dist/access-namespace-preflight.js +24 -24
  13. package/dist/access-observe-write-surface.js +24 -24
  14. package/dist/access-operations-batch.js +25 -25
  15. package/dist/access-operations.js +32 -32
  16. package/dist/access-recall-concurrency.js +24 -24
  17. package/dist/access-recall-diagnostics.js +2 -2
  18. package/dist/access-recall-response.js +24 -24
  19. package/dist/access-recall-surface.js +24 -24
  20. package/dist/access-service.js +24 -24
  21. package/dist/access-surface-catalog.js +26 -26
  22. package/dist/active-recall.js +5 -5
  23. package/dist/{auto-sync-R3XGJL5I.js → auto-sync-432USQDH.js} +4 -4
  24. package/dist/briefing.js +5 -5
  25. package/dist/buffer.js +3 -3
  26. package/dist/calibration.js +1 -1
  27. package/dist/causal-consolidation.js +6 -6
  28. package/dist/{chunk-FL6SLVAJ.js → chunk-2AKIVSHP.js} +2 -2
  29. package/dist/{chunk-G6PPA4TH.js → chunk-3AODCBKX.js} +2 -2
  30. package/dist/{chunk-MGXFJOUR.js → chunk-3K5IEJTK.js} +2 -2
  31. package/dist/{chunk-YGYT7S3G.js → chunk-3PLAL2KP.js} +15 -1
  32. package/dist/chunk-3PLAL2KP.js.map +1 -0
  33. package/dist/{chunk-WADPRAZE.js → chunk-3XVBGGF3.js} +2 -2
  34. package/dist/{chunk-BPBUUGAO.js → chunk-4B37KNMP.js} +2 -2
  35. package/dist/{chunk-2RJO7VS4.js → chunk-4IZ6AC2U.js} +2 -2
  36. package/dist/{chunk-RBC4GIZU.js → chunk-53BLBDV6.js} +2 -2
  37. package/dist/{chunk-5UWP5X3T.js → chunk-5ANUG4JZ.js} +2 -2
  38. package/dist/{chunk-Y6Y3TRBN.js → chunk-5DCS4C4S.js} +2 -2
  39. package/dist/{chunk-MH3LC7KB.js → chunk-5K23NQRI.js} +2 -2
  40. package/dist/{chunk-UMFBHYPW.js → chunk-5UPOKIFP.js} +8 -8
  41. package/dist/{chunk-U4JXQCJ3.js → chunk-63YPKHAU.js} +2 -2
  42. package/dist/{chunk-S53WZWGL.js → chunk-6DTIYAAN.js} +2 -2
  43. package/dist/{chunk-RWWYBE7E.js → chunk-6F5BGIAF.js} +2 -2
  44. package/dist/{chunk-Z5AQZRHF.js → chunk-6JEEY42N.js} +2 -2
  45. package/dist/{chunk-HKEK27M4.js → chunk-6SYS5YX3.js} +2 -2
  46. package/dist/{chunk-CCYXCT4B.js → chunk-7HXZOWTR.js} +2 -2
  47. package/dist/{chunk-DGBWPSM6.js → chunk-7VFNGQ4V.js} +2 -2
  48. package/dist/{chunk-ARFT6ZBE.js → chunk-7ZRRJ6RG.js} +2 -2
  49. package/dist/{chunk-GMMZ45C4.js → chunk-AVVCPA7U.js} +6 -8
  50. package/dist/chunk-AVVCPA7U.js.map +1 -0
  51. package/dist/{chunk-LSSCMZQ2.js → chunk-AYBAC2N4.js} +3 -3
  52. package/dist/{chunk-EVVCE7S2.js → chunk-C2KOW2UQ.js} +2 -2
  53. package/dist/{chunk-GCVUBVEN.js → chunk-CVMAOPAH.js} +8 -8
  54. package/dist/{chunk-D2AKUKAQ.js → chunk-DG4H56SK.js} +1 -1
  55. package/dist/{chunk-BQ6DGL6A.js → chunk-DKHX2FHI.js} +2 -2
  56. package/dist/{chunk-HRT2SJPC.js → chunk-E3NBMTTX.js} +2 -2
  57. package/dist/{chunk-V5RJCXLG.js → chunk-EGNONTVT.js} +5 -5
  58. package/dist/{chunk-7PG7QBWD.js → chunk-FGEY2AGV.js} +2 -2
  59. package/dist/{chunk-O6YJI5VX.js → chunk-FKTMWDN2.js} +2 -2
  60. package/dist/{chunk-QIC7YZ3D.js → chunk-FQFMLOAC.js} +12 -12
  61. package/dist/{chunk-ID2PLENW.js → chunk-GGI6FLO6.js} +2 -2
  62. package/dist/{chunk-KC5SVCUJ.js → chunk-I75BWXRZ.js} +2 -2
  63. package/dist/{chunk-ULUX6Q7R.js → chunk-J7G656MY.js} +3 -3
  64. package/dist/{chunk-VKTP3TZB.js → chunk-JF3MEIHO.js} +2 -2
  65. package/dist/{chunk-A23DPFFI.js → chunk-JIA435P3.js} +2 -2
  66. package/dist/{chunk-USOXPN5H.js → chunk-KOAVX2IT.js} +2 -2
  67. package/dist/{chunk-Z5QWUR3Y.js → chunk-L7PGPH4E.js} +2 -2
  68. package/dist/{chunk-5U3NUHMA.js → chunk-LOMSBYNS.js} +33 -33
  69. package/dist/{chunk-FO5AFCRI.js → chunk-MCPSMWUC.js} +4 -4
  70. package/dist/{chunk-64IO6ZEE.js → chunk-MZWHIAOL.js} +2 -2
  71. package/dist/{chunk-LDXS746V.js → chunk-N7DVBHU3.js} +3 -3
  72. package/dist/{chunk-WSLNC5AL.js → chunk-NH6GZEQN.js} +3 -3
  73. package/dist/{chunk-3L5WH6CV.js → chunk-OKKBOTRA.js} +2 -2
  74. package/dist/{chunk-MBMT5FUQ.js → chunk-OLH4XCLU.js} +2 -2
  75. package/dist/{chunk-UXXVOA4D.js → chunk-OLHN4ZNT.js} +2 -2
  76. package/dist/{chunk-LRJKHXU4.js → chunk-POGPI5GR.js} +2 -2
  77. package/dist/{chunk-RVYXSS3Q.js → chunk-QO5KDQGG.js} +3 -3
  78. package/dist/{chunk-7ZBANI3M.js → chunk-QOUKZ2N3.js} +2 -2
  79. package/dist/{chunk-HIMXDRSQ.js → chunk-RGKXF4W3.js} +2 -2
  80. package/dist/{chunk-CNQQNYJ2.js → chunk-RWRHGE75.js} +2 -2
  81. package/dist/{chunk-6R6CU3MR.js → chunk-SWZOAQUM.js} +22 -22
  82. package/dist/{chunk-FBAHBFT5.js → chunk-SZPPCEYT.js} +2 -2
  83. package/dist/{chunk-QEGE5QCL.js → chunk-TAEJ2MRR.js} +19 -19
  84. package/dist/{chunk-BBDASWNT.js → chunk-TJ5YGCPV.js} +81 -76
  85. package/dist/chunk-TJ5YGCPV.js.map +1 -0
  86. package/dist/{chunk-G2SRB62M.js → chunk-TXP7Q7T5.js} +2 -2
  87. package/dist/{chunk-NSQSOLCE.js → chunk-TYLNWE4D.js} +2 -2
  88. package/dist/{chunk-A6PKEU7T.js → chunk-UFM3PJDT.js} +1 -1
  89. package/dist/chunk-UFM3PJDT.js.map +1 -0
  90. package/dist/{chunk-MNDHKZZQ.js → chunk-VC3X7XP3.js} +2 -2
  91. package/dist/{chunk-PTRPOJ7U.js → chunk-VGRM43X6.js} +2 -2
  92. package/dist/{chunk-DK5VMJL3.js → chunk-VSSQIBVU.js} +2 -2
  93. package/dist/{chunk-R6TCM47S.js → chunk-WCBLPKG3.js} +3 -3
  94. package/dist/{chunk-UQWA5QDT.js → chunk-WCIDCJOQ.js} +2 -2
  95. package/dist/{chunk-UFBIOF7C.js → chunk-WEK3OUGO.js} +2 -2
  96. package/dist/{chunk-5EILJ6W2.js → chunk-WXPBDEMW.js} +2 -2
  97. package/dist/{chunk-CU7WDDPV.js → chunk-X247CYL6.js} +3 -3
  98. package/dist/{chunk-JWTUJVIF.js → chunk-XHVO3CGX.js} +2 -2
  99. package/dist/{chunk-3BPMYMPX.js → chunk-XHX67LX6.js} +2 -2
  100. package/dist/{chunk-LAQS5HYU.js → chunk-Y5ZHZKZD.js} +2 -2
  101. package/dist/{chunk-L46Y5FPE.js → chunk-YYGZ4EIY.js} +2 -2
  102. package/dist/{chunk-NQOM6PHO.js → chunk-ZAP7BKTZ.js} +3 -3
  103. package/dist/cli.js +54 -54
  104. package/dist/coding/export-okf-codegraph.js +4 -4
  105. package/dist/compounding/engine.js +6 -6
  106. package/dist/config.d.ts.map +1 -1
  107. package/dist/config.js +5 -5
  108. package/dist/connectors/codex-materialize-runner.js +5 -5
  109. package/dist/connectors/index.js +5 -5
  110. package/dist/convergence-refresh.js +7 -7
  111. package/dist/corpus-watermark.js +7 -7
  112. package/dist/deep-recall-policy-llm.js +2 -2
  113. package/dist/entity-retrieval.js +5 -5
  114. package/dist/external-wiki-access.js +25 -25
  115. package/dist/extraction-faithfulness.js +2 -2
  116. package/dist/extraction-judge.d.ts +42 -2
  117. package/dist/extraction-judge.d.ts.map +1 -1
  118. package/dist/extraction-judge.js +12 -2
  119. package/dist/extraction.js +9 -9
  120. package/dist/fallback-llm.d.ts +1 -0
  121. package/dist/fallback-llm.d.ts.map +1 -1
  122. package/dist/fallback-llm.js +1 -1
  123. package/dist/harmonic-retrieval.js +5 -5
  124. package/dist/index.js +71 -71
  125. package/dist/location/index.js +7 -7
  126. package/dist/maintenance/memory-governance.js +5 -5
  127. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
  128. package/dist/maintenance/rebuild-memory-projection-runner.js +7 -7
  129. package/dist/maintenance/rebuild-memory-projection.js +6 -6
  130. package/dist/memory-browse-operations.js +25 -25
  131. package/dist/memory-browse.js +3 -3
  132. package/dist/namespaces/migrate.js +6 -6
  133. package/dist/namespaces/storage.js +5 -5
  134. package/dist/operator-doctor-corpus.js +8 -8
  135. package/dist/operator-toolkit.js +14 -14
  136. package/dist/orchestration/maintenance.js +15 -15
  137. package/dist/orchestrator.js +71 -71
  138. package/dist/recall-explain-renderer.js +3 -3
  139. package/dist/recall-navigate-access.js +2 -2
  140. package/dist/recall-navigate-disclosure.js +2 -2
  141. package/dist/recall-navigate.js +2 -2
  142. package/dist/recall-navigation-operations.js +25 -25
  143. package/dist/recall-navigation.js +24 -24
  144. package/dist/recall-planner-llm.js +1 -1
  145. package/dist/recall-state-view-admission.js +3 -3
  146. package/dist/recall-why-service.js +3 -3
  147. package/dist/recall-xray-cli.js +4 -4
  148. package/dist/recall-xray-renderer.js +3 -3
  149. package/dist/recall-xray.js +2 -2
  150. package/dist/resume-bundles.js +6 -6
  151. package/dist/semantic-consolidation.js +6 -6
  152. package/dist/semantic-rule-promotion.js +5 -5
  153. package/dist/semantic-rule-verifier.js +5 -5
  154. package/dist/source-agent-qualifier.js +8 -8
  155. package/dist/standup/build.js +5 -5
  156. package/dist/storage.js +4 -4
  157. package/dist/summarizer.js +2 -2
  158. package/dist/support-passport/index.js +27 -27
  159. package/dist/transfer/export-okf.js +5 -5
  160. package/dist/types.d.ts +12 -0
  161. package/dist/types.d.ts.map +1 -1
  162. package/dist/types.js +1 -1
  163. package/dist/verified-recall.js +5 -5
  164. package/dist/who-knows-cli.js +6 -6
  165. package/dist/who-knows.js +5 -5
  166. package/package.json +2 -2
  167. package/src/config.test.ts +16 -0
  168. package/src/config.ts +14 -0
  169. package/src/extraction-judge-chain.test.ts +298 -2
  170. package/src/extraction-judge.ts +147 -82
  171. package/src/fallback-llm.test.ts +94 -0
  172. package/src/fallback-llm.ts +7 -7
  173. package/src/types.ts +12 -0
  174. package/dist/chunk-A6PKEU7T.js.map +0 -1
  175. package/dist/chunk-BBDASWNT.js.map +0 -1
  176. package/dist/chunk-GMMZ45C4.js.map +0 -1
  177. package/dist/chunk-YGYT7S3G.js.map +0 -1
  178. /package/dist/{auto-sync-R3XGJL5I.js.map → auto-sync-432USQDH.js.map} +0 -0
  179. /package/dist/{chunk-FL6SLVAJ.js.map → chunk-2AKIVSHP.js.map} +0 -0
  180. /package/dist/{chunk-G6PPA4TH.js.map → chunk-3AODCBKX.js.map} +0 -0
  181. /package/dist/{chunk-MGXFJOUR.js.map → chunk-3K5IEJTK.js.map} +0 -0
  182. /package/dist/{chunk-WADPRAZE.js.map → chunk-3XVBGGF3.js.map} +0 -0
  183. /package/dist/{chunk-BPBUUGAO.js.map → chunk-4B37KNMP.js.map} +0 -0
  184. /package/dist/{chunk-2RJO7VS4.js.map → chunk-4IZ6AC2U.js.map} +0 -0
  185. /package/dist/{chunk-RBC4GIZU.js.map → chunk-53BLBDV6.js.map} +0 -0
  186. /package/dist/{chunk-5UWP5X3T.js.map → chunk-5ANUG4JZ.js.map} +0 -0
  187. /package/dist/{chunk-Y6Y3TRBN.js.map → chunk-5DCS4C4S.js.map} +0 -0
  188. /package/dist/{chunk-MH3LC7KB.js.map → chunk-5K23NQRI.js.map} +0 -0
  189. /package/dist/{chunk-UMFBHYPW.js.map → chunk-5UPOKIFP.js.map} +0 -0
  190. /package/dist/{chunk-U4JXQCJ3.js.map → chunk-63YPKHAU.js.map} +0 -0
  191. /package/dist/{chunk-S53WZWGL.js.map → chunk-6DTIYAAN.js.map} +0 -0
  192. /package/dist/{chunk-RWWYBE7E.js.map → chunk-6F5BGIAF.js.map} +0 -0
  193. /package/dist/{chunk-Z5AQZRHF.js.map → chunk-6JEEY42N.js.map} +0 -0
  194. /package/dist/{chunk-HKEK27M4.js.map → chunk-6SYS5YX3.js.map} +0 -0
  195. /package/dist/{chunk-CCYXCT4B.js.map → chunk-7HXZOWTR.js.map} +0 -0
  196. /package/dist/{chunk-DGBWPSM6.js.map → chunk-7VFNGQ4V.js.map} +0 -0
  197. /package/dist/{chunk-ARFT6ZBE.js.map → chunk-7ZRRJ6RG.js.map} +0 -0
  198. /package/dist/{chunk-LSSCMZQ2.js.map → chunk-AYBAC2N4.js.map} +0 -0
  199. /package/dist/{chunk-EVVCE7S2.js.map → chunk-C2KOW2UQ.js.map} +0 -0
  200. /package/dist/{chunk-GCVUBVEN.js.map → chunk-CVMAOPAH.js.map} +0 -0
  201. /package/dist/{chunk-D2AKUKAQ.js.map → chunk-DG4H56SK.js.map} +0 -0
  202. /package/dist/{chunk-BQ6DGL6A.js.map → chunk-DKHX2FHI.js.map} +0 -0
  203. /package/dist/{chunk-HRT2SJPC.js.map → chunk-E3NBMTTX.js.map} +0 -0
  204. /package/dist/{chunk-V5RJCXLG.js.map → chunk-EGNONTVT.js.map} +0 -0
  205. /package/dist/{chunk-7PG7QBWD.js.map → chunk-FGEY2AGV.js.map} +0 -0
  206. /package/dist/{chunk-O6YJI5VX.js.map → chunk-FKTMWDN2.js.map} +0 -0
  207. /package/dist/{chunk-QIC7YZ3D.js.map → chunk-FQFMLOAC.js.map} +0 -0
  208. /package/dist/{chunk-ID2PLENW.js.map → chunk-GGI6FLO6.js.map} +0 -0
  209. /package/dist/{chunk-KC5SVCUJ.js.map → chunk-I75BWXRZ.js.map} +0 -0
  210. /package/dist/{chunk-ULUX6Q7R.js.map → chunk-J7G656MY.js.map} +0 -0
  211. /package/dist/{chunk-VKTP3TZB.js.map → chunk-JF3MEIHO.js.map} +0 -0
  212. /package/dist/{chunk-A23DPFFI.js.map → chunk-JIA435P3.js.map} +0 -0
  213. /package/dist/{chunk-USOXPN5H.js.map → chunk-KOAVX2IT.js.map} +0 -0
  214. /package/dist/{chunk-Z5QWUR3Y.js.map → chunk-L7PGPH4E.js.map} +0 -0
  215. /package/dist/{chunk-5U3NUHMA.js.map → chunk-LOMSBYNS.js.map} +0 -0
  216. /package/dist/{chunk-FO5AFCRI.js.map → chunk-MCPSMWUC.js.map} +0 -0
  217. /package/dist/{chunk-64IO6ZEE.js.map → chunk-MZWHIAOL.js.map} +0 -0
  218. /package/dist/{chunk-LDXS746V.js.map → chunk-N7DVBHU3.js.map} +0 -0
  219. /package/dist/{chunk-WSLNC5AL.js.map → chunk-NH6GZEQN.js.map} +0 -0
  220. /package/dist/{chunk-3L5WH6CV.js.map → chunk-OKKBOTRA.js.map} +0 -0
  221. /package/dist/{chunk-MBMT5FUQ.js.map → chunk-OLH4XCLU.js.map} +0 -0
  222. /package/dist/{chunk-UXXVOA4D.js.map → chunk-OLHN4ZNT.js.map} +0 -0
  223. /package/dist/{chunk-LRJKHXU4.js.map → chunk-POGPI5GR.js.map} +0 -0
  224. /package/dist/{chunk-RVYXSS3Q.js.map → chunk-QO5KDQGG.js.map} +0 -0
  225. /package/dist/{chunk-7ZBANI3M.js.map → chunk-QOUKZ2N3.js.map} +0 -0
  226. /package/dist/{chunk-HIMXDRSQ.js.map → chunk-RGKXF4W3.js.map} +0 -0
  227. /package/dist/{chunk-CNQQNYJ2.js.map → chunk-RWRHGE75.js.map} +0 -0
  228. /package/dist/{chunk-6R6CU3MR.js.map → chunk-SWZOAQUM.js.map} +0 -0
  229. /package/dist/{chunk-FBAHBFT5.js.map → chunk-SZPPCEYT.js.map} +0 -0
  230. /package/dist/{chunk-QEGE5QCL.js.map → chunk-TAEJ2MRR.js.map} +0 -0
  231. /package/dist/{chunk-G2SRB62M.js.map → chunk-TXP7Q7T5.js.map} +0 -0
  232. /package/dist/{chunk-NSQSOLCE.js.map → chunk-TYLNWE4D.js.map} +0 -0
  233. /package/dist/{chunk-MNDHKZZQ.js.map → chunk-VC3X7XP3.js.map} +0 -0
  234. /package/dist/{chunk-PTRPOJ7U.js.map → chunk-VGRM43X6.js.map} +0 -0
  235. /package/dist/{chunk-DK5VMJL3.js.map → chunk-VSSQIBVU.js.map} +0 -0
  236. /package/dist/{chunk-R6TCM47S.js.map → chunk-WCBLPKG3.js.map} +0 -0
  237. /package/dist/{chunk-UQWA5QDT.js.map → chunk-WCIDCJOQ.js.map} +0 -0
  238. /package/dist/{chunk-UFBIOF7C.js.map → chunk-WEK3OUGO.js.map} +0 -0
  239. /package/dist/{chunk-5EILJ6W2.js.map → chunk-WXPBDEMW.js.map} +0 -0
  240. /package/dist/{chunk-CU7WDDPV.js.map → chunk-X247CYL6.js.map} +0 -0
  241. /package/dist/{chunk-JWTUJVIF.js.map → chunk-XHVO3CGX.js.map} +0 -0
  242. /package/dist/{chunk-3BPMYMPX.js.map → chunk-XHX67LX6.js.map} +0 -0
  243. /package/dist/{chunk-LAQS5HYU.js.map → chunk-Y5ZHZKZD.js.map} +0 -0
  244. /package/dist/{chunk-L46Y5FPE.js.map → chunk-YYGZ4EIY.js.map} +0 -0
  245. /package/dist/{chunk-NQOM6PHO.js.map → chunk-ZAP7BKTZ.js.map} +0 -0
@@ -10,7 +10,11 @@
10
10
  * - Critical-importance facts are auto-approved.
11
11
  * - Batches respect extractionJudgeBatchSize.
12
12
  * - Content-hash caching avoids redundant LLM calls.
13
- * - Performance budget: <= 1.5s per batch.
13
+ * - LLM calls use extractionJudgeTimeoutMs (default 20s: measured 2.1-2.5 s
14
+ * per compact verdict request with a batch-scaled completion budget on a
15
+ * small fast judge model; 13-22 s per compact call, 41 s verbose, on the
16
+ * big shared core model — raise it there; the old 150s default only
17
+ * papered over 59-305s verbose replies).
14
18
  */
15
19
 
16
20
  import { createHash } from "node:crypto";
@@ -215,48 +219,79 @@ export const JUDGE_SYSTEM_PROMPT = `You are a memory curator evaluating whether
215
219
 
216
220
  A fact is **durable** if it will still be useful 30+ days from now and is relevant across multiple sessions, not just the current task.
217
221
 
218
- Return one of three verdicts per candidate:
222
+ The user message is a JSON array of candidates: [{"index": 0, "text": "...", "category": "...", "confidence": 0.5}, ...].
219
223
 
220
- ACCEPT — the fact is durable, persist it:
221
- - Personal preferences, identities, or relationships
222
- - Decisions with rationale that affect future work
223
- - Corrections to previously held beliefs
224
- - Principles, rules, or constraints the user wants respected
225
- - Stable facts about projects, tools, or workflows
226
- - Commitments, deadlines, or obligations
224
+ Verdicts:
225
+ - "accept" — durable. Personal preferences, identities, relationships; decisions with rationale that affect future work; corrections to previously held beliefs; principles, rules, or constraints the user wants respected; stable facts about projects, tools, or workflows; commitments, deadlines, or obligations.
226
+ - "reject" — not durable. Transient task details ("currently debugging line 42"); ephemeral state ("the build is running now"); routine operations ("ran npm install"); conversational filler; anything stale within hours; step-by-step instructions for a one-time task.
227
+ - "defer" — MIGHT be durable but the candidate alone cannot say: ambiguous referents ("he said they'd follow up"), in-progress statements that may become durable once completed, future commitments with unclear subject or timeline. Defer is NOT a soft reject: use it only when another turn of context would genuinely change the verdict; when torn between accept and reject, lean accept — losing a useful fact is worse than keeping a marginal one.
227
228
 
228
- REJECT — the fact is not durable, drop it:
229
- - Transient task details ("currently debugging line 42")
230
- - Ephemeral state ("the build is running now")
231
- - Routine operations ("ran npm install")
232
- - Conversational filler or acknowledgements
233
- - Information that will be stale within hours
234
- - Step-by-step instructions for a one-time task
229
+ Reply with a JSON object with a single "verdicts" key, exactly one object per input candidate:
230
+ {"verdicts": [{"index": <candidate index>, "kind": "accept" | "reject" | "defer"}]}
235
231
 
236
- DEFER — the fact MIGHT be durable but needs more context to decide. The candidate will be re-evaluated on a later extraction pass with fresh context; if it cannot be resolved within a small number of re-evaluations it will be rejected.
237
- - Ambiguous referents ("he said they'd follow up on it")
238
- - Partial or in-progress statements that might become durable once completed
239
- - Future-tense commitments whose subject or timeline is unclear
240
- - Facts whose durability hinges on context not present in the candidate text
232
+ Rules:
233
+ 1. The top level must be one object {"verdicts": [...]} — JSON mode rejects a bare top-level array.
234
+ 2. Copy each object's "index" from the input candidate. One object per candidate, exactly.
235
+ 3. No other fields — no "reason", no "durable", no commentary.
236
+ 4. Output valid JSON only. No markdown fences.
237
+ 5. ${OUTPUT_LANGUAGE_POLICY}
241
238
 
242
- Do NOT use defer as a soft reject. Reject facts you are confident are transient. Only defer when another turn of context would genuinely change the verdict.
239
+ Example output:
240
+ {"verdicts": [{"index": 0, "kind": "accept"}, {"index": 1, "kind": "reject"}, {"index": 2, "kind": "defer"}]}`;
243
241
 
244
- Return a JSON array of objects with these fields:
245
- - index: number (the candidate index)
246
- - kind: string — one of "accept", "reject", "defer"
247
- - reason: string (brief explanation, under 80 characters)
242
+ // ---------------------------------------------------------------------------
243
+ // Judge request assembly (PR 3264 follow-up)
244
+ // ---------------------------------------------------------------------------
248
245
 
249
- You may also include durable (boolean) for backwards compatibility: true for accept, false for reject or defer. If kind is omitted, durable determines the verdict.
246
+ /** Completion tokens reserved per candidate verdict (measured 13 on a small judge model). */
247
+ const JUDGE_TOKENS_PER_CANDIDATE = 24;
248
+ /** Ceiling on one judge reply: the old flat budget. */
249
+ export const MAX_JUDGE_TOKENS = 2048;
250
+ /**
251
+ * Extra completion tokens for a judge call routed through the Responses API. Reasoning tokens count against
252
+ * `max_output_tokens`, so the compact verdict budget alone can be spent before a reasoning-enabled model answers; the
253
+ * unparsed batch would then fail open as durable. A cap, not a target: a non-reasoning model still writes a short reply.
254
+ */
255
+ export const JUDGE_REASONING_HEADROOM_TOKENS = 1024;
256
+ /** Largest batch whose verdict array fits MAX_JUDGE_TOKENS; a larger configured batch is split. */
257
+ export const MAX_JUDGE_BATCH = Math.floor(MAX_JUDGE_TOKENS / JUDGE_TOKENS_PER_CANDIDATE);
250
258
 
251
- Rules:
252
- 1. Return exactly one verdict per input candidate, matched by index.
253
- 2. When in doubt between accept and reject, lean toward accept — false negatives (losing a useful fact) are worse than false positives (keeping a marginal one).
254
- 3. Use defer only when another turn of context would genuinely change the verdict.
255
- 4. Output valid JSON only. No markdown fences, no commentary.
256
- 5. ${OUTPUT_LANGUAGE_POLICY}
259
+ /**
260
+ * Compact completion budget for one judge call. A verdict is
261
+ * `{"index":12,"kind":"reject"}` (~14 tokens), so the old flat 2048 only gave
262
+ * a slow model room to produce 59-305s replies that hid inside the timeout.
263
+ * Scales with the batch size (about 24 tokens per candidate verdict, measured
264
+ * 13 per verdict on a small judge model) so a large batch is never truncated;
265
+ * capped at 2048, the old flat budget.
266
+ */
267
+ export function judgeMaxTokens(batchSize: number): number {
268
+ return Math.min(MAX_JUDGE_TOKENS, Math.max(64, batchSize * JUDGE_TOKENS_PER_CANDIDATE));
269
+ }
257
270
 
258
- Example output:
259
- [{"index": 0, "kind": "accept", "durable": true, "reason": "Stable personal preference"}, {"index": 1, "kind": "reject", "durable": false, "reason": "Ephemeral build status"}, {"index": 2, "kind": "defer", "durable": false, "reason": "Ambiguous pronoun"}]`;
271
+ /** One indexed candidate entry in a judge request payload. */
272
+ export interface JudgeRequestEntry {
273
+ index: number;
274
+ text: string;
275
+ category: string;
276
+ confidence: number;
277
+ }
278
+
279
+ /** Full judge LLM request: messages plus the completion budget. */
280
+ export interface JudgeRequest {
281
+ messages: Array<{ role: "system" | "user"; content: string }>;
282
+ maxTokens: number;
283
+ }
284
+
285
+ /** Build a full judge request body: messages plus the completion budget. */
286
+ export function buildJudgeRequest(entries: readonly JudgeRequestEntry[]): JudgeRequest {
287
+ return {
288
+ messages: [
289
+ { role: "system", content: JUDGE_SYSTEM_PROMPT },
290
+ { role: "user", content: JSON.stringify(entries) },
291
+ ],
292
+ maxTokens: judgeMaxTokens(entries.length),
293
+ };
294
+ }
260
295
 
261
296
  // ---------------------------------------------------------------------------
262
297
  // Content-hash cache (in-memory, per-process fallback)
@@ -492,40 +527,39 @@ export async function judgeFactDurability(
492
527
  }
493
528
 
494
529
  // Batch the pending candidates up to batchSize
495
- const batchSize = config.extractionJudgeBatchSize;
530
+ const batchSize = Math.min(config.extractionJudgeBatchSize, MAX_JUDGE_BATCH);
531
+ // Defer bookkeeping spans the WHOLE call, not one batch: identical candidates can land in different batches, and each key
532
+ // must advance the defer cap once and decide against the same prior count (the snapshot taken at its first sighting).
533
+ const deferredThisCall = new Set<string>();
534
+ const priorDefersSeen = new Map<string, number>();
496
535
  for (let batchStart = 0; batchStart < pendingIndices.length; batchStart += batchSize) {
497
536
  const batchIndices = pendingIndices.slice(batchStart, batchStart + batchSize);
498
- const batchPayload = batchIndices.map((idx) => ({
499
- index: idx,
500
- text: candidates[idx].text,
501
- category: candidates[idx].category,
502
- confidence: candidates[idx].confidence,
503
- }));
504
-
505
- const userPrompt = JSON.stringify(batchPayload);
537
+ const request = buildJudgeRequest(
538
+ batchIndices.map((idx) => ({
539
+ index: idx,
540
+ text: candidates[idx].text,
541
+ category: candidates[idx].category,
542
+ confidence: candidates[idx].confidence,
543
+ })),
544
+ );
506
545
 
507
546
  try {
508
- const llmResponse = await callJudgeLlm(
509
- userPrompt,
510
- config,
511
- localLlm,
512
- fallbackLlm,
513
- );
547
+ const llmResponse = await callJudgeLlm(request, config, localLlm, fallbackLlm);
514
548
 
515
549
  if (llmResponse) {
516
550
  const parsed = parseJudgeResponse(llmResponse, batchIndices);
517
- // Per-batch defer-increment dedupe (codex P2): a single extraction
551
+ // Per-call defer-increment dedupe (codex P2): a single extraction
518
552
  // pass must not advance the cap more than once for identical
519
- // candidate content, even if the same text appears multiple times
520
- // in the same LLM response. Duplicate hits share the first
521
- // increment's `priorDeferrals` snapshot.
522
- const deferredThisBatch = new Set<string>();
553
+ // candidate content, even if the same text appears several times in
554
+ // one LLM response or in different batches of one call. Duplicate
555
+ // hits share the first sighting's `priorDeferrals` snapshot.
523
556
  for (const [idx, rawVerdict] of parsed.entries()) {
524
557
  const c = candidates[idx];
525
558
  const key = cacheKey(c.text, c.category);
526
559
  let verdict = rawVerdict;
527
560
  let source: JudgeVerdictObservation["source"] = "llm";
528
- const priorDefers = deferCountMap.get(key) ?? 0;
561
+ if (!priorDefersSeen.has(key)) priorDefersSeen.set(key, deferCountMap.get(key) ?? 0);
562
+ const priorDefers = priorDefersSeen.get(key) ?? 0;
529
563
 
530
564
  // Defer cap (issue #562, PR 2). A candidate that has already been
531
565
  // deferred `deferCap` times is forcibly rejected so a pathological
@@ -541,15 +575,15 @@ export async function judgeFactDurability(
541
575
  // Only clear + count the cap conversion once per batch for
542
576
  // this key — duplicates in the same response all resolve to
543
577
  // reject but should not inflate the cap-rejection counter.
544
- if (!deferredThisBatch.has(key)) {
578
+ if (!deferredThisCall.has(key)) {
545
579
  deferCountMap.delete(key);
546
580
  deferredCappedToReject++;
547
- deferredThisBatch.add(key);
581
+ deferredThisCall.add(key);
548
582
  }
549
- } else if (!deferredThisBatch.has(key)) {
583
+ } else if (!deferredThisCall.has(key)) {
550
584
  deferCountMap.set(key, priorDefers + 1);
551
585
  deferred++;
552
- deferredThisBatch.add(key);
586
+ deferredThisCall.add(key);
553
587
  // Bound the per-process defer-counter map.
554
588
  if (deferCountMap.size > DEFER_COUNT_MAX_SIZE) {
555
589
  const drop = Math.floor(deferCountMap.size / 2);
@@ -561,12 +595,14 @@ export async function judgeFactDurability(
561
595
  }
562
596
  }
563
597
  }
564
- // else: duplicate defer for a key already counted this batch —
598
+ // else: duplicate defer for a key already counted this call —
565
599
  // no additional counter increment, verdict still marked defer.
566
600
  } else {
567
601
  // On accept/reject, clear any outstanding defer counter so a
568
602
  // future reappearance of the same text starts fresh.
569
603
  deferCountMap.delete(key);
604
+ priorDefersSeen.set(key, 0);
605
+ deferredThisCall.delete(key);
570
606
  }
571
607
 
572
608
  verdicts.set(idx, verdict);
@@ -607,6 +643,8 @@ export async function judgeFactDurability(
607
643
  // telemetry event reflects the true state rather than always 0.
608
644
  const priorDefers = deferCountMap.get(hash) ?? 0;
609
645
  deferCountMap.delete(hash);
646
+ priorDefersSeen.set(hash, 0);
647
+ deferredThisCall.delete(hash);
610
648
  const v: JudgeVerdict = {
611
649
  durable: true,
612
650
  reason: "Approved by default (judge unavailable or parse error)",
@@ -641,15 +679,12 @@ export async function judgeFactDurability(
641
679
  // ---------------------------------------------------------------------------
642
680
 
643
681
  async function callJudgeLlm(
644
- userPrompt: string,
682
+ request: JudgeRequest,
645
683
  config: PluginConfig,
646
684
  localLlm: LocalLlmClient | null,
647
685
  fallbackLlm: FallbackLlmClient | null,
648
686
  ): Promise<string | null> {
649
- const messages: Array<{ role: "system" | "user"; content: string }> = [
650
- { role: "system", content: JUDGE_SYSTEM_PROMPT },
651
- { role: "user", content: userPrompt },
652
- ];
687
+ const { messages, maxTokens } = request;
653
688
 
654
689
  const modelOverride = config.extractionJudgeModel || undefined;
655
690
 
@@ -669,9 +704,14 @@ async function callJudgeLlm(
669
704
  try {
670
705
  const result = await (localLlm as any).chatCompletion(messages, {
671
706
  temperature: 0.1,
672
- maxTokens: 2048,
707
+ maxTokens,
708
+ // A verdict needs no reasoning: keep a thinking-capable model from spending the compact budget on it.
709
+ disableThinking: true,
673
710
  responseFormat: { type: "json_object" },
674
- timeoutMs: 1500,
711
+ timeoutMs: config.extractionJudgeTimeoutMs,
712
+ // The client applies timeoutMs per attempt and retries an abort once:
713
+ // one deadline signal bounds the whole call to the configured time.
714
+ signal: AbortSignal.timeout(config.extractionJudgeTimeoutMs),
675
715
  operation: "extraction-judge",
676
716
  ...(modelOverride ? { model: modelOverride } : {}),
677
717
  });
@@ -692,8 +732,9 @@ async function callJudgeLlm(
692
732
  messages as Array<{ role: "system" | "user" | "assistant"; content: string }>,
693
733
  {
694
734
  temperature: 0.1,
695
- maxTokens: 2048,
696
- timeoutMs: 1500,
735
+ maxTokens,
736
+ reasoningHeadroomTokens: JUDGE_REASONING_HEADROOM_TOKENS,
737
+ timeoutMs: config.extractionJudgeTimeoutMs,
697
738
  ...(modelOverride ? { model: modelOverride } : {}),
698
739
  ...gatewayChain,
699
740
  },
@@ -711,6 +752,38 @@ async function callJudgeLlm(
711
752
  return null;
712
753
  }
713
754
 
755
+ interface JudgeVerdictItem {
756
+ index?: number;
757
+ kind?: string;
758
+ action?: string;
759
+ durable?: boolean;
760
+ reason?: string;
761
+ }
762
+
763
+ /** What a judge model may return: a bare array, or one object wrapping verdicts. */
764
+ interface JudgeEnvelope {
765
+ verdicts?: JudgeVerdictItem[];
766
+ index?: number;
767
+ }
768
+
769
+ type ParsedJudgeJson = JudgeVerdictItem[] | JudgeEnvelope | null | undefined;
770
+
771
+ /**
772
+ * Normalise the object shapes a judge model returns to a verdict array.
773
+ * Requested shape {"verdicts": [...]} (response_format json_object forces a
774
+ * top-level object); a single verdict object counts as one verdict at its
775
+ * index, never the whole batch; legacy: any array-valued key.
776
+ */
777
+ function unwrapJudgeEnvelope(parsed: ParsedJudgeJson): JudgeVerdictItem[] | null {
778
+ if (!parsed || Array.isArray(parsed)) return null;
779
+ if (Array.isArray(parsed.verdicts)) return parsed.verdicts;
780
+ if (Number.isInteger(parsed.index)) return [parsed];
781
+ for (const v of Object.values(parsed)) {
782
+ if (Array.isArray(v)) return v;
783
+ }
784
+ return null;
785
+ }
786
+
714
787
  // ---------------------------------------------------------------------------
715
788
  // Response parsing
716
789
  // ---------------------------------------------------------------------------
@@ -724,7 +797,7 @@ function parseJudgeResponse(
724
797
 
725
798
  try {
726
799
  // Try direct parse first, then fall back to JSON extraction
727
- let parsed: unknown;
800
+ let parsed: ParsedJudgeJson;
728
801
  try {
729
802
  parsed = JSON.parse(raw);
730
803
  } catch {
@@ -735,20 +808,12 @@ function parseJudgeResponse(
735
808
  }
736
809
 
737
810
  if (!Array.isArray(parsed)) {
738
- // Might be wrapped in an object with a key
739
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
740
- const values = Object.values(parsed as Record<string, unknown>);
741
- for (const v of values) {
742
- if (Array.isArray(v)) {
743
- parsed = v;
744
- break;
745
- }
746
- }
747
- }
748
- if (!Array.isArray(parsed)) {
811
+ const unwrapped = unwrapJudgeEnvelope(parsed);
812
+ if (unwrapped === null) {
749
813
  log.debug("extraction-judge: response is not an array, cannot parse");
750
814
  return result;
751
815
  }
816
+ parsed = unwrapped;
752
817
  }
753
818
 
754
819
  for (const item of parsed) {
@@ -1453,6 +1453,57 @@ test("fallback llm uses the Responses API for openai-responses transports", { co
1453
1453
  }
1454
1454
  });
1455
1455
 
1456
+ test("fallback llm adds reasoningHeadroomTokens to max_output_tokens on the Responses API", { concurrency: false }, async () => {
1457
+ clearModelsJsonCache();
1458
+ clearSecretCache();
1459
+
1460
+ const llm = new FallbackLlmClient({
1461
+ agents: { defaults: { model: { primary: "responses-provider/demo-model" } } },
1462
+ models: {
1463
+ providers: {
1464
+ "responses-provider": {
1465
+ baseUrl: "https://responses.example/v1",
1466
+ api: "openai-responses",
1467
+ apiKey: "responses-key",
1468
+ models: [],
1469
+ },
1470
+ },
1471
+ },
1472
+ });
1473
+
1474
+ const originalFetch = globalThis.fetch;
1475
+ const bodies: Array<{ max_output_tokens?: number }> = [];
1476
+ // SAFETY: the stub returns the Response shape the client reads and nothing else.
1477
+ globalThis.fetch = (async (_url, init) => {
1478
+ bodies.push(JSON.parse(String(init?.body ?? "{}")));
1479
+ return new Response(JSON.stringify({ output_text: "ok" }), {
1480
+ status: 200,
1481
+ headers: { "Content-Type": "application/json" },
1482
+ });
1483
+ }) as typeof fetch;
1484
+
1485
+ try {
1486
+ const messages = [{ role: "user" as const, content: "Say OK" }];
1487
+ // Non-finite headroom is ignored, like a negative one.
1488
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 8, reasoningHeadroomTokens: Number.NaN });
1489
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 64, reasoningHeadroomTokens: 1024 });
1490
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 64 });
1491
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 64, reasoningHeadroomTokens: -5 });
1492
+ await llm.chatCompletion(messages, { temperature: 0, reasoningHeadroomTokens: 1024 });
1493
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 64, reasoningHeadroomTokens: Number.POSITIVE_INFINITY });
1494
+ assert.equal(bodies[5].max_output_tokens, 64, "an infinite headroom adds nothing (it would serialize as null)");
1495
+ assert.equal(bodies[0].max_output_tokens, 8, "a non-finite headroom adds nothing");
1496
+ assert.equal(bodies[1].max_output_tokens, 64 + 1024, "headroom is added on top of the compact budget");
1497
+ assert.equal(bodies[2].max_output_tokens, 64, "no headroom option: exactly maxTokens, as before");
1498
+ assert.equal(bodies[3].max_output_tokens, 64, "a negative headroom adds nothing");
1499
+ assert.equal(bodies[4].max_output_tokens, 4096, "no maxTokens: the default budget is not inflated by headroom");
1500
+ } finally {
1501
+ globalThis.fetch = originalFetch;
1502
+ clearModelsJsonCache();
1503
+ clearSecretCache();
1504
+ }
1505
+ });
1506
+
1456
1507
  test("fallback llm ignores echoed input_text blocks in responses output extraction", { concurrency: false }, async () => {
1457
1508
  clearModelsJsonCache();
1458
1509
  clearSecretCache();
@@ -2122,3 +2173,46 @@ test("timeout records TimeoutError on failureDiag (issue #2891)", { concurrency:
2122
2173
  clearSecretCache();
2123
2174
  }
2124
2175
  });
2176
+
2177
+ test("fallback llm adds reasoningHeadroomTokens to the Chat Completions token limit too", { concurrency: false }, async () => {
2178
+ clearModelsJsonCache();
2179
+ clearSecretCache();
2180
+
2181
+ const llm = new FallbackLlmClient({
2182
+ agents: { defaults: { model: { primary: "chat-provider/demo-model" } } },
2183
+ models: {
2184
+ providers: {
2185
+ "chat-provider": {
2186
+ baseUrl: "https://chat.example/v1",
2187
+ api: "openai-completions",
2188
+ apiKey: "chat-key",
2189
+ models: [],
2190
+ },
2191
+ },
2192
+ },
2193
+ });
2194
+
2195
+ const originalFetch = globalThis.fetch;
2196
+ const bodies: Array<{ max_tokens?: number; max_completion_tokens?: number }> = [];
2197
+ // SAFETY: the stub returns the Chat Completions shape the client reads and nothing else.
2198
+ globalThis.fetch = (async (_url, init) => {
2199
+ bodies.push(JSON.parse(String(init?.body ?? "{}")));
2200
+ return new Response(JSON.stringify({ choices: [{ message: { content: "ok" } }] }), {
2201
+ status: 200,
2202
+ headers: { "Content-Type": "application/json" },
2203
+ });
2204
+ }) as typeof fetch;
2205
+
2206
+ try {
2207
+ const messages = [{ role: "user" as const, content: "Say OK" }];
2208
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 64, reasoningHeadroomTokens: 1024 });
2209
+ await llm.chatCompletion(messages, { temperature: 0, maxTokens: 64 });
2210
+ const limit = (b: (typeof bodies)[number]) => b.max_completion_tokens ?? b.max_tokens;
2211
+ assert.equal(limit(bodies[0]), 64 + 1024, "headroom is added on top of the compact budget");
2212
+ assert.equal(limit(bodies[1]), 64, "no headroom option: exactly maxTokens, as before");
2213
+ } finally {
2214
+ globalThis.fetch = originalFetch;
2215
+ clearModelsJsonCache();
2216
+ clearSecretCache();
2217
+ }
2218
+ });
@@ -46,6 +46,7 @@ export {
46
46
  export interface FallbackLlmOptions {
47
47
  temperature?: number;
48
48
  maxTokens?: number;
49
+ reasoningHeadroomTokens?: number; // Responses API only: extra max_output_tokens for reasoning (they count against it); unset adds none
49
50
  timeoutMs?: number;
50
51
  signal?: AbortSignal;
51
52
  /** Set the Responses API storage policy. Other transports ignore this value. */
@@ -764,17 +765,16 @@ export class FallbackLlmClient {
764
765
  return await this.callOllamaChat(effectiveConfig, model.modelId, messages, options);
765
766
  }
766
767
 
768
+ const wanted = Math.floor(options.reasoningHeadroomTokens ?? 0);
769
+ const headroom = Number.isFinite(wanted) ? Math.max(0, wanted) : 0; // NaN and Infinity add nothing
770
+ const capped = options.maxTokens === undefined ? options : { ...options, maxTokens: options.maxTokens + headroom };
771
+
767
772
  if (
768
773
  model.providerConfig.api === "openai-responses" ||
769
774
  model.providerConfig.api === "openai-codex-responses" ||
770
775
  model.providerConfig.api === "azure-openai-responses"
771
776
  ) {
772
- return await this.callOpenAIResponses(
773
- effectiveConfig,
774
- model.modelId,
775
- messages,
776
- options,
777
- );
777
+ return await this.callOpenAIResponses(effectiveConfig, model.modelId, messages, capped);
778
778
  }
779
779
 
780
780
  // For OpenAI-compatible chat-completions APIs (openai-completions,
@@ -785,7 +785,7 @@ export class FallbackLlmClient {
785
785
  effectiveConfig,
786
786
  model.modelId,
787
787
  messages,
788
- options,
788
+ capped,
789
789
  shouldAssumeOpenAiChatCompletions(effectiveConfig.baseUrl),
790
790
  );
791
791
  }
package/src/types.ts CHANGED
@@ -1299,6 +1299,18 @@ export interface PluginConfig
1299
1299
  extractionJudgeModel: string;
1300
1300
  /** Maximum number of candidate facts per judge LLM batch call. */
1301
1301
  extractionJudgeBatchSize: number;
1302
+ /**
1303
+ * Per-call timeout for the judge LLM (ms). Default 20000: measured 2.1-2.5 s
1304
+ * per compact verdict call (completion budget 24 tokens per candidate, up
1305
+ * to 2048) on a small fast judge model; the big shared core model measured
1306
+ * 13-22 s per compact call, 41 s verbose — raise it there. The earlier 150s
1307
+ * default only papered over 59-305s verbose replies; sub-second budgets
1308
+ * abort every call. On the local LLM path each attempt lasts
1309
+ * min(localLlmTimeoutMs, extractionJudgeTimeoutMs) (localLlmTimeoutMs
1310
+ * defaults to 180000), so a value above localLlmTimeoutMs does not lengthen
1311
+ * an attempt: raise both settings for a slower judge model.
1312
+ */
1313
+ extractionJudgeTimeoutMs: number;
1302
1314
  /** Shadow mode: log judge verdicts but do not filter facts. Default false. */
1303
1315
  extractionJudgeShadow: boolean;
1304
1316
  /**