@mjasnikovs/pi-task 0.38.28 → 0.38.30

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 (370) hide show
  1. package/dist/config/config.d.ts +70 -70
  2. package/dist/config/config.js +26 -35
  3. package/dist/config/extension-list.d.ts +6 -5
  4. package/dist/config/extension-list.js +3 -2
  5. package/dist/config/reasoning-args.d.ts +9 -7
  6. package/dist/config/reasoning-args.js +12 -10
  7. package/dist/config/reasoning.d.ts +44 -105
  8. package/dist/config/reasoning.js +27 -704
  9. package/dist/config/register.d.ts +34 -48
  10. package/dist/config/register.js +41 -51
  11. package/dist/config/tool-list.d.ts +16 -16
  12. package/dist/config/tool-list.js +1 -1
  13. package/dist/remote/bridge.d.ts +19 -10
  14. package/dist/remote/bridge.js +3 -2
  15. package/dist/remote/broadcast.js +3 -1
  16. package/dist/remote/events.js +12 -11
  17. package/dist/remote/history.d.ts +1 -1
  18. package/dist/remote/protocol.d.ts +6 -3
  19. package/dist/remote/protocol.js +2 -1
  20. package/dist/remote/push.d.ts +16 -16
  21. package/dist/remote/push.js +27 -27
  22. package/dist/remote/register.d.ts +3 -3
  23. package/dist/remote/register.js +17 -19
  24. package/dist/remote/server.d.ts +9 -8
  25. package/dist/remote/server.js +15 -14
  26. package/dist/remote/session-state.d.ts +5 -4
  27. package/dist/remote/session-state.js +8 -5
  28. package/dist/remote/sw.d.ts +7 -6
  29. package/dist/remote/sw.js +7 -6
  30. package/dist/remote/tailscale.d.ts +4 -2
  31. package/dist/remote/tailscale.js +4 -2
  32. package/dist/remote/ui-highlight.js +6 -5
  33. package/dist/remote/ui-render.js +4 -4
  34. package/dist/remote/ui-script.js +24 -24
  35. package/dist/remote/ui-styles.d.ts +1 -1
  36. package/dist/remote/ui-styles.js +10 -13
  37. package/dist/remote/ui-tools.js +9 -6
  38. package/dist/shared/child-extensions.d.ts +29 -17
  39. package/dist/shared/child-extensions.js +29 -17
  40. package/dist/shared/child-output.d.ts +30 -24
  41. package/dist/shared/child-output.js +25 -17
  42. package/dist/shared/child-process.d.ts +47 -40
  43. package/dist/shared/child-process.js +50 -59
  44. package/dist/shared/command-watchdog.d.ts +22 -16
  45. package/dist/shared/command-watchdog.js +28 -21
  46. package/dist/shared/fs-text.d.ts +16 -10
  47. package/dist/shared/fs-text.js +16 -10
  48. package/dist/shared/git-runner.d.ts +25 -25
  49. package/dist/shared/git-runner.js +25 -25
  50. package/dist/shared/leaked-tool-call.d.ts +17 -11
  51. package/dist/shared/leaked-tool-call.js +23 -15
  52. package/dist/shared/model-endpoint.d.ts +29 -16
  53. package/dist/shared/model-endpoint.js +33 -21
  54. package/dist/shared/pi-invocation.d.ts +7 -4
  55. package/dist/shared/pi-invocation.js +12 -7
  56. package/dist/shared/pkg-version.d.ts +13 -5
  57. package/dist/shared/pkg-version.js +13 -5
  58. package/dist/shared/reasoning-capability.d.ts +35 -24
  59. package/dist/shared/reasoning-capability.js +35 -24
  60. package/dist/shared/stream-watchdog.d.ts +60 -44
  61. package/dist/shared/stream-watchdog.js +62 -45
  62. package/dist/task/accept-debt.d.ts +41 -43
  63. package/dist/task/accept-debt.js +73 -65
  64. package/dist/task/api-synthesis.d.ts +24 -21
  65. package/dist/task/api-synthesis.js +32 -26
  66. package/dist/task/apis-contract.d.ts +32 -64
  67. package/dist/task/apis-contract.js +32 -64
  68. package/dist/task/artifact-closure.d.ts +27 -13
  69. package/dist/task/artifact-closure.js +95 -67
  70. package/dist/task/auto-commit.d.ts +46 -35
  71. package/dist/task/auto-commit.js +51 -38
  72. package/dist/task/auto-io.d.ts +45 -25
  73. package/dist/task/auto-io.js +57 -29
  74. package/dist/task/auto-orchestrator.d.ts +26 -24
  75. package/dist/task/auto-orchestrator.js +178 -162
  76. package/dist/task/auto-prompts.d.ts +36 -24
  77. package/dist/task/auto-prompts.js +40 -26
  78. package/dist/task/autofix-ledger.d.ts +27 -25
  79. package/dist/task/autofix-ledger.js +29 -26
  80. package/dist/task/batch-test-task.d.ts +20 -12
  81. package/dist/task/batch-test-task.js +67 -60
  82. package/dist/task/boot-probe.d.ts +60 -44
  83. package/dist/task/boot-probe.js +91 -72
  84. package/dist/task/cancel-input.d.ts +30 -16
  85. package/dist/task/cancel-input.js +20 -11
  86. package/dist/task/cancel-points.d.ts +27 -20
  87. package/dist/task/cancel-points.js +30 -22
  88. package/dist/task/child-runner.d.ts +46 -51
  89. package/dist/task/child-runner.js +48 -49
  90. package/dist/task/child-status.d.ts +23 -16
  91. package/dist/task/child-status.js +23 -16
  92. package/dist/task/clamp-output.js +12 -5
  93. package/dist/task/command-run.d.ts +31 -28
  94. package/dist/task/command-run.js +44 -35
  95. package/dist/task/command-shrink.d.ts +25 -18
  96. package/dist/task/command-shrink.js +37 -31
  97. package/dist/task/command-watchdog.d.ts +9 -6
  98. package/dist/task/command-watchdog.js +21 -15
  99. package/dist/task/context-attribution.d.ts +34 -26
  100. package/dist/task/context-attribution.js +34 -26
  101. package/dist/task/context-silence.d.ts +39 -29
  102. package/dist/task/context-silence.js +35 -25
  103. package/dist/task/context-usage.d.ts +25 -7
  104. package/dist/task/context-usage.js +21 -6
  105. package/dist/task/contracts.d.ts +8 -4
  106. package/dist/task/contracts.js +25 -17
  107. package/dist/task/coverage-loop.d.ts +22 -18
  108. package/dist/task/coverage-loop.js +35 -30
  109. package/dist/task/critique-probes.d.ts +13 -14
  110. package/dist/task/critique-probes.js +50 -39
  111. package/dist/task/debug-log.d.ts +13 -5
  112. package/dist/task/debug-log.js +32 -20
  113. package/dist/task/decompose-fidelity.d.ts +11 -9
  114. package/dist/task/decompose-fidelity.js +38 -33
  115. package/dist/task/decompose-granularity.d.ts +41 -38
  116. package/dist/task/decompose-granularity.js +41 -38
  117. package/dist/task/deep-render-check.d.ts +22 -14
  118. package/dist/task/deep-render-check.js +40 -31
  119. package/dist/task/dropped-input.d.ts +12 -7
  120. package/dist/task/dropped-input.js +5 -2
  121. package/dist/task/enforce-attribution.d.ts +38 -47
  122. package/dist/task/enforce-attribution.js +46 -52
  123. package/dist/task/enforce-guidelines.d.ts +31 -20
  124. package/dist/task/enforce-guidelines.js +32 -21
  125. package/dist/task/enrichment.d.ts +7 -2
  126. package/dist/task/enrichment.js +26 -14
  127. package/dist/task/env-notes.d.ts +16 -7
  128. package/dist/task/env-notes.js +48 -31
  129. package/dist/task/env-template-closure.d.ts +4 -4
  130. package/dist/task/env-template-closure.js +42 -34
  131. package/dist/task/external-context.d.ts +28 -21
  132. package/dist/task/external-context.js +17 -12
  133. package/dist/task/failure-classifier.d.ts +4 -5
  134. package/dist/task/failure-classifier.js +6 -7
  135. package/dist/task/file-inventory.d.ts +15 -11
  136. package/dist/task/file-inventory.js +25 -22
  137. package/dist/task/final-gate-fix.d.ts +74 -86
  138. package/dist/task/final-gate-fix.js +97 -116
  139. package/dist/task/final-gate-progress.d.ts +29 -46
  140. package/dist/task/final-gate-progress.js +40 -51
  141. package/dist/task/final-gate.d.ts +64 -97
  142. package/dist/task/final-gate.js +192 -199
  143. package/dist/task/fix-child.d.ts +21 -27
  144. package/dist/task/fix-child.js +21 -27
  145. package/dist/task/foreign-path.d.ts +6 -5
  146. package/dist/task/foreign-path.js +0 -0
  147. package/dist/task/frozen-conflict.d.ts +9 -10
  148. package/dist/task/frozen-conflict.js +61 -64
  149. package/dist/task/frozen-path-guard.d.ts +35 -14
  150. package/dist/task/frozen-path-guard.js +56 -39
  151. package/dist/task/gate-child.d.ts +27 -28
  152. package/dist/task/gate-child.js +37 -35
  153. package/dist/task/gate-deps.d.ts +34 -27
  154. package/dist/task/gate-deps.js +169 -159
  155. package/dist/task/gate-tally.d.ts +77 -80
  156. package/dist/task/gate-tally.js +65 -68
  157. package/dist/task/git-state-guard.d.ts +15 -11
  158. package/dist/task/git-state-guard.js +76 -66
  159. package/dist/task/impl-widget.d.ts +25 -16
  160. package/dist/task/impl-widget.js +27 -17
  161. package/dist/task/implementation-thinking.d.ts +33 -31
  162. package/dist/task/implementation-thinking.js +5 -6
  163. package/dist/task/implementation-turn.d.ts +34 -31
  164. package/dist/task/implementation-turn.js +29 -27
  165. package/dist/task/inline-markdown.d.ts +20 -7
  166. package/dist/task/inline-markdown.js +15 -6
  167. package/dist/task/launch-config-gap.js +25 -39
  168. package/dist/task/launch-contract.d.ts +18 -21
  169. package/dist/task/launch-contract.js +28 -30
  170. package/dist/task/launch-manifest.d.ts +6 -2
  171. package/dist/task/launch-manifest.js +35 -34
  172. package/dist/task/ledger.js +16 -14
  173. package/dist/task/lint-fix.d.ts +6 -8
  174. package/dist/task/lint-fix.js +67 -69
  175. package/dist/task/loop-detector.d.ts +9 -8
  176. package/dist/task/loop-detector.js +16 -12
  177. package/dist/task/mid-run-input.d.ts +17 -15
  178. package/dist/task/mid-run-input.js +17 -15
  179. package/dist/task/orchestrator.d.ts +24 -28
  180. package/dist/task/orchestrator.js +62 -64
  181. package/dist/task/orientation.d.ts +18 -23
  182. package/dist/task/orientation.js +24 -31
  183. package/dist/task/owned-freeze-conflict.d.ts +21 -20
  184. package/dist/task/owned-freeze-conflict.js +52 -85
  185. package/dist/task/owned-freeze-reassign.d.ts +40 -60
  186. package/dist/task/owned-freeze-reassign.js +41 -61
  187. package/dist/task/parsers.d.ts +4 -2
  188. package/dist/task/parsers.js +4 -4
  189. package/dist/task/phases.d.ts +41 -48
  190. package/dist/task/phases.js +180 -248
  191. package/dist/task/plan-io.d.ts +6 -7
  192. package/dist/task/plan-io.js +6 -7
  193. package/dist/task/plan-orchestrator.d.ts +10 -8
  194. package/dist/task/plan-orchestrator.js +14 -10
  195. package/dist/task/plan-prompts.d.ts +6 -5
  196. package/dist/task/plan-prompts.js +6 -5
  197. package/dist/task/plan-readonly.d.ts +4 -5
  198. package/dist/task/plan-readonly.js +4 -5
  199. package/dist/task/plan-rounds.d.ts +17 -29
  200. package/dist/task/plan-rounds.js +21 -34
  201. package/dist/task/plan-session.d.ts +58 -72
  202. package/dist/task/plan-session.js +61 -83
  203. package/dist/task/probe-gaming.d.ts +28 -27
  204. package/dist/task/probe-gaming.js +0 -0
  205. package/dist/task/prohibition-probe.d.ts +14 -16
  206. package/dist/task/prompts.d.ts +3 -4
  207. package/dist/task/prompts.js +17 -26
  208. package/dist/task/qa-transcript.d.ts +15 -22
  209. package/dist/task/qa-transcript.js +15 -21
  210. package/dist/task/question-box.d.ts +17 -13
  211. package/dist/task/question-box.js +19 -15
  212. package/dist/task/question-dedup.d.ts +6 -7
  213. package/dist/task/question-dedup.js +13 -14
  214. package/dist/task/question-dialog.d.ts +22 -32
  215. package/dist/task/question-dialog.js +22 -32
  216. package/dist/task/question-source.d.ts +18 -44
  217. package/dist/task/question-source.js +22 -51
  218. package/dist/task/refuted-constraint.d.ts +11 -31
  219. package/dist/task/refuted-constraint.js +27 -51
  220. package/dist/task/regenerable-artifacts.d.ts +12 -31
  221. package/dist/task/regenerable-artifacts.js +12 -31
  222. package/dist/task/render-check.d.ts +11 -22
  223. package/dist/task/render-check.js +33 -46
  224. package/dist/task/repo-health-check.d.ts +10 -14
  225. package/dist/task/repo-health-check.js +17 -23
  226. package/dist/task/requirements.d.ts +38 -71
  227. package/dist/task/requirements.js +78 -126
  228. package/dist/task/research-fanout-budget.d.ts +51 -88
  229. package/dist/task/research-fanout-budget.js +51 -88
  230. package/dist/task/research-worker.d.ts +33 -36
  231. package/dist/task/research-worker.js +39 -61
  232. package/dist/task/resume-gap.d.ts +14 -15
  233. package/dist/task/root-cause-repair.d.ts +9 -9
  234. package/dist/task/root-cause-repair.js +28 -40
  235. package/dist/task/run-bracket.d.ts +10 -13
  236. package/dist/task/run-end.d.ts +12 -22
  237. package/dist/task/run-end.js +8 -16
  238. package/dist/task/run-final-gate.d.ts +19 -21
  239. package/dist/task/run-final-gate.js +62 -80
  240. package/dist/task/runner-globs.d.ts +12 -13
  241. package/dist/task/runner-globs.js +12 -13
  242. package/dist/task/runner-resolve.d.ts +9 -9
  243. package/dist/task/runner-resolve.js +22 -23
  244. package/dist/task/script-escape.d.ts +10 -12
  245. package/dist/task/script-escape.js +13 -14
  246. package/dist/task/serve-entry.d.ts +1 -1
  247. package/dist/task/serve-entry.js +22 -25
  248. package/dist/task/service-blocks.js +4 -2
  249. package/dist/task/shipped-source.d.ts +11 -29
  250. package/dist/task/shipped-source.js +11 -29
  251. package/dist/task/skip-escape.js +10 -14
  252. package/dist/task/spec-urls.d.ts +26 -65
  253. package/dist/task/spec-urls.js +26 -65
  254. package/dist/task/spec-validation.d.ts +17 -20
  255. package/dist/task/spec-validation.js +17 -20
  256. package/dist/task/stall-detector.d.ts +23 -30
  257. package/dist/task/stall-detector.js +23 -30
  258. package/dist/task/stream-watchdog.d.ts +14 -12
  259. package/dist/task/stream-watchdog.js +14 -12
  260. package/dist/task/substitution-probe.d.ts +17 -20
  261. package/dist/task/substitution-probe.js +17 -20
  262. package/dist/task/task-gates.d.ts +36 -41
  263. package/dist/task/task-gates.js +95 -106
  264. package/dist/task/task-io.d.ts +4 -4
  265. package/dist/task/task-io.js +4 -4
  266. package/dist/task/task-parsers.js +4 -3
  267. package/dist/task/task-provenance.d.ts +2 -2
  268. package/dist/task/task-provenance.js +11 -13
  269. package/dist/task/task-types.d.ts +4 -3
  270. package/dist/task/terminal-outcome.d.ts +14 -16
  271. package/dist/task/terminal-outcome.js +12 -14
  272. package/dist/task/test-assembly.d.ts +13 -20
  273. package/dist/task/test-assembly.js +13 -20
  274. package/dist/task/timings.d.ts +5 -3
  275. package/dist/task/timings.js +5 -3
  276. package/dist/task/title-label.d.ts +9 -4
  277. package/dist/task/title-label.js +9 -4
  278. package/dist/task/type-only-answer.d.ts +44 -52
  279. package/dist/task/type-only-answer.js +44 -52
  280. package/dist/task/unfailable-command.d.ts +18 -24
  281. package/dist/task/unfailable-command.js +21 -27
  282. package/dist/task/unknown-routing.d.ts +10 -4
  283. package/dist/task/unknown-routing.js +10 -4
  284. package/dist/task/user-directives.d.ts +5 -8
  285. package/dist/task/user-directives.js +5 -8
  286. package/dist/task/verify-quality.d.ts +18 -22
  287. package/dist/task/verify-quality.js +45 -46
  288. package/dist/task/verify-reconcile.d.ts +15 -10
  289. package/dist/task/verify-reconcile.js +45 -43
  290. package/dist/task/verify-resolution.d.ts +24 -20
  291. package/dist/task/verify-resolution.js +51 -50
  292. package/dist/task/verify-work.d.ts +59 -66
  293. package/dist/task/verify-work.js +101 -138
  294. package/dist/task/widget.d.ts +15 -14
  295. package/dist/task/widget.js +22 -17
  296. package/dist/task/wiring-claims.d.ts +25 -32
  297. package/dist/task/wiring-claims.js +30 -35
  298. package/dist/task/write-guard.d.ts +39 -39
  299. package/dist/task/write-guard.js +48 -51
  300. package/dist/task/yolo.d.ts +34 -30
  301. package/dist/task/yolo.js +42 -37
  302. package/dist/workers/abstention.d.ts +21 -41
  303. package/dist/workers/abstention.js +27 -48
  304. package/dist/workers/brave-search.d.ts +4 -3
  305. package/dist/workers/brave-search.js +5 -2
  306. package/dist/workers/brave-warning.d.ts +7 -4
  307. package/dist/workers/brave-warning.js +19 -7
  308. package/dist/workers/ddg-search.d.ts +6 -6
  309. package/dist/workers/ddg-search.js +18 -12
  310. package/dist/workers/docs-cache.js +5 -2
  311. package/dist/workers/docs-chunk.d.ts +30 -37
  312. package/dist/workers/docs-chunk.js +37 -41
  313. package/dist/workers/docs-core.d.ts +28 -44
  314. package/dist/workers/docs-core.js +25 -44
  315. package/dist/workers/docs-index.js +4 -3
  316. package/dist/workers/docs-lookup.d.ts +15 -22
  317. package/dist/workers/docs-lookup.js +12 -21
  318. package/dist/workers/docs-project.d.ts +15 -9
  319. package/dist/workers/docs-project.js +17 -10
  320. package/dist/workers/docs-resolve.d.ts +19 -20
  321. package/dist/workers/docs-resolve.js +35 -32
  322. package/dist/workers/docs-retrieve.d.ts +5 -6
  323. package/dist/workers/docs-retrieve.js +18 -15
  324. package/dist/workers/exa-search.d.ts +9 -6
  325. package/dist/workers/exa-search.js +23 -12
  326. package/dist/workers/fetch-core.d.ts +13 -16
  327. package/dist/workers/fetch-core.js +23 -23
  328. package/dist/workers/focused-extractor.d.ts +12 -12
  329. package/dist/workers/focused-extractor.js +16 -19
  330. package/dist/workers/html-clean.js +24 -14
  331. package/dist/workers/http-request.d.ts +28 -20
  332. package/dist/workers/http-request.js +22 -17
  333. package/dist/workers/npm-version.d.ts +28 -11
  334. package/dist/workers/npm-version.js +24 -15
  335. package/dist/workers/phantom-imports.d.ts +15 -12
  336. package/dist/workers/phantom-imports.js +30 -24
  337. package/dist/workers/pi-worker-core.d.ts +86 -54
  338. package/dist/workers/pi-worker-core.js +112 -112
  339. package/dist/workers/pi-worker-docs.d.ts +24 -19
  340. package/dist/workers/pi-worker-docs.js +67 -76
  341. package/dist/workers/pi-worker-fetch.d.ts +7 -3
  342. package/dist/workers/pi-worker-fetch.js +27 -19
  343. package/dist/workers/pi-worker-search.js +12 -8
  344. package/dist/workers/pi-worker.d.ts +9 -4
  345. package/dist/workers/pi-worker.js +23 -10
  346. package/dist/workers/reasoning-warning.d.ts +18 -17
  347. package/dist/workers/reasoning-warning.js +22 -20
  348. package/dist/workers/research-cache.js +50 -78
  349. package/dist/workers/search-core.js +7 -5
  350. package/dist/workers/search-types.d.ts +10 -9
  351. package/dist/workers/search-types.js +9 -8
  352. package/dist/workers/session-hint.d.ts +13 -14
  353. package/dist/workers/session-hint.js +8 -9
  354. package/dist/workers/shared.d.ts +21 -25
  355. package/dist/workers/shared.js +0 -0
  356. package/dist/workers/single-read-extension.d.ts +14 -7
  357. package/dist/workers/single-read-extension.js +14 -7
  358. package/dist/workers/single-read-guard.d.ts +25 -28
  359. package/dist/workers/single-read-guard.js +32 -32
  360. package/dist/workers/typeonly-log.d.ts +12 -9
  361. package/dist/workers/typeonly-log.js +29 -33
  362. package/dist/workers/worker-channels.d.ts +15 -23
  363. package/dist/workers/worker-channels.js +15 -23
  364. package/dist/workers/worker-failure.d.ts +38 -46
  365. package/dist/workers/worker-failure.js +31 -39
  366. package/dist/workers/worker-kill.d.ts +25 -26
  367. package/dist/workers/worker-kill.js +16 -19
  368. package/dist/workers/worker-profiles.d.ts +43 -53
  369. package/dist/workers/worker-profiles.js +30 -38
  370. package/package.json +10 -8
@@ -35,16 +35,17 @@ export declare function hasTypeFiles(root: string): boolean;
35
35
  * lines, and the pointer lines a redirect stub is made of (`/// <reference .. />`
36
36
  * and `export * from "X"`).
37
37
  *
38
- * This is the discriminator `detectTypesRedirect` needs: a redirect stub is a
39
- * file with essentially nothing in it but the pointer, while an API surface that
40
- * merely *declares an ambient dependency* on another types package (the
41
- * `sharp` -> `/// <reference types="node" />` shape) carries its own
42
- * declarations. Counting `.d.ts` FILES cannot tell those apart — sharp ships one
43
- * 1971-line file and `@types/bun` ships one 1-line file, and both count as 1.
38
+ * This is the discriminator `detectTypesRedirect` needs. A redirect stub has
39
+ * nothing in it but the pointer and counts 0. A real API surface that merely
40
+ * declares an AMBIENT DEPENDENCY on another types package
41
+ * `/// <reference types="node" />` above its own declarations — counts those
42
+ * declarations and is not a stub. A `.d.ts` FILE count cannot tell the two apart:
43
+ * both ship exactly one file.
44
44
  *
45
- * Deliberately lexical, not a TypeScript parse: this runs in the shipped worker,
46
- * which has no compiler dependency. Over-counting is the safe direction (a
47
- * declaration found not a stub keep the package's own types).
45
+ * Deliberately lexical, not a TypeScript parse: `typescript` is a devDependency
46
+ * here, so the shipped worker has no compiler to call. Over-counting is the safe
47
+ * direction — a declaration found means not a stub, so the package keeps its own
48
+ * types.
48
49
  */
49
50
  export declare function countEntryDeclarations(content: string): number;
50
51
  /** When a package is a pure pointer to another types package — a single-file
@@ -57,18 +58,16 @@ export declare function detectTypesRedirect(pkg: ResolvedPackage): string | null
57
58
  /**
58
59
  * Follow the `@types/<name>` + triple-slash `<reference types>` redirect chain from
59
60
  * a package that ships no usable types of its own to the one that actually holds
60
- * the declarations — `bun` → `@types/bun` → `bun-types`. Bounded to three hops;
61
- * returns the package it started from when no better source is found.
61
+ * the declarations — `bun` → `@types/bun` → `bun-types`. Bounded to three hops:
62
+ * a chain of pure stubs stops after exactly three. The `visited` set cuts a cycle,
63
+ * and the package it started from comes back when no better source is found.
62
64
  *
63
- * This is what the four predicates above EXIST for. They were exported and heavily
64
- * tested (35 references between them) while the loop that calls them lived in two
65
- * byte-identical copies — `docs-core.ts` and `phantom-imports.ts` — and NEITHER was
66
- * covered: both of their tests pin only the zero-hop case, so the multi-hop
67
- * behaviour cited by name in five doc comments was asserted nowhere.
65
+ * This is what the four predicates above exist for, and the walk is written once
66
+ * for its two callers.
68
67
  *
69
- * `resolveHop` is the one thing the two call sites genuinely disagree about: the
70
- * docs pipeline resolves the next hop through an auto-installing async lookup, the
71
- * phantom-import checker through a bare sync resolve that must never install. It
72
- * returns null to stop the walk.
68
+ * `resolveHop` is the one thing those callers genuinely disagree about: docs-core
69
+ * resolves the next hop through an auto-installing async lookup, while
70
+ * phantom-imports wraps a bare sync `resolvePackage` that must never install.
71
+ * Returning null stops the walk.
73
72
  */
74
73
  export declare function resolveTypeSource(start: ResolvedPackage, seed: string, resolveHop: (name: string) => Promise<ResolvedPackage | null>): Promise<ResolvedPackage>;
@@ -22,10 +22,11 @@ function isValidModuleName(name) {
22
22
  }
23
23
  // Runtimes whose builtin `<runtime>:<sub>` imports are typed by the runtime's own
24
24
  // types package, not by a literal package named "<runtime>:<sub>". `node:fs` and
25
- // `bun:sqlite` are real imports, but their declarations live in @types/node and
26
- // bun-types and a phantom like `bun:sql` (no such submodule) is only disprovable
27
- // by resolving the runtime and finding the symbol absent. Either way the docs
28
- // lookup target is the runtime, never the colon-name.
25
+ // `bun:sqlite` are real imports whose declarations live in @types/node and
26
+ // bun-types. `bun:sql` is a phantom importing it fails with "Cannot find
27
+ // package 'sql'" — and the only way to disprove it is to resolve the runtime and
28
+ // find the symbol absent. Either way the docs lookup target is the runtime, never
29
+ // the colon-name.
29
30
  const RUNTIME_NAMESPACES = new Set(['bun', 'node', 'deno']);
30
31
  /**
31
32
  * Split a runtime builtin specifier (`bun:sql`, `node:fs/promises`) into its
@@ -120,7 +121,9 @@ export function resolvePackage(moduleName, cwd) {
120
121
  throw new ResolveError('invalid_name', `Invalid module name: "${moduleName}"`);
121
122
  }
122
123
  const parent = parentPackageName(moduleName);
123
- // First try: use createRequire (works when package.json is exported)
124
+ // First try: `createRequire`. It only works when the package EXPORTS
125
+ // `./package.json`; a package that does not gives ERR_PACKAGE_PATH_NOT_EXPORTED
126
+ // and falls through. Any other resolve error is rethrown.
124
127
  const requireFromCwd = createRequire(path.join(cwd, '__pi-worker-docs-sentinel__'));
125
128
  let pkgJsonPath = null;
126
129
  try {
@@ -130,9 +133,10 @@ export function resolvePackage(moduleName, cwd) {
130
133
  const code = err.code;
131
134
  if (code !== 'MODULE_NOT_FOUND' && code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED')
132
135
  throw err;
133
- // Fall through to direct filesystem fallback below
134
136
  }
135
- // Second try: walk node_modules directly (handles packages that don't export package.json)
137
+ // Second try: walk node_modules upward from cwd. This is what handles a package
138
+ // that does not export its package.json, and it is the only path that reports
139
+ // `not_installed`.
136
140
  if (!pkgJsonPath) {
137
141
  const direct = findPackageJsonInNodeModules(parent, cwd);
138
142
  if (!direct) {
@@ -230,16 +234,17 @@ const DECLARATION_RE = /^\s*(?:export\s+(?:default\s+)?)?(?:declare\s+)?(?:abstr
230
234
  * lines, and the pointer lines a redirect stub is made of (`/// <reference .. />`
231
235
  * and `export * from "X"`).
232
236
  *
233
- * This is the discriminator `detectTypesRedirect` needs: a redirect stub is a
234
- * file with essentially nothing in it but the pointer, while an API surface that
235
- * merely *declares an ambient dependency* on another types package (the
236
- * `sharp` -> `/// <reference types="node" />` shape) carries its own
237
- * declarations. Counting `.d.ts` FILES cannot tell those apart — sharp ships one
238
- * 1971-line file and `@types/bun` ships one 1-line file, and both count as 1.
237
+ * This is the discriminator `detectTypesRedirect` needs. A redirect stub has
238
+ * nothing in it but the pointer and counts 0. A real API surface that merely
239
+ * declares an AMBIENT DEPENDENCY on another types package
240
+ * `/// <reference types="node" />` above its own declarations — counts those
241
+ * declarations and is not a stub. A `.d.ts` FILE count cannot tell the two apart:
242
+ * both ship exactly one file.
239
243
  *
240
- * Deliberately lexical, not a TypeScript parse: this runs in the shipped worker,
241
- * which has no compiler dependency. Over-counting is the safe direction (a
242
- * declaration found not a stub keep the package's own types).
244
+ * Deliberately lexical, not a TypeScript parse: `typescript` is a devDependency
245
+ * here, so the shipped worker has no compiler to call. Over-counting is the safe
246
+ * direction — a declaration found means not a stub, so the package keeps its own
247
+ * types.
243
248
  */
244
249
  export function countEntryDeclarations(content) {
245
250
  const stripped = content.replace(BLOCK_COMMENT_RE, '');
@@ -295,11 +300,11 @@ export function detectTypesRedirect(pkg) {
295
300
  if (!target)
296
301
  return null;
297
302
  // A pointer line is not a redirect when the file it sits in also declares an
298
- // API. `/// <reference types="node" />` in a package like sharp is an AMBIENT
299
- // DEPENDENCY declaration — "my types need node's" not "my types ARE node's";
300
- // following it answered every sharp question out of @types/node (tty.d.ts,
301
- // zlib.d.ts) while sharp's own 1971-line surface sat one file away. The .d.ts
302
- // FILE count cannot see this: sharp ships one file and so does @types/bun.
303
+ // API. `/// <reference types="node" />` above a package's own declarations
304
+ // means "my types NEED node's", not "my types ARE node's". Following it would
305
+ // answer every question about that package out of @types/node while its own
306
+ // surface sat one file away and the .d.ts FILE count cannot see the
307
+ // difference, because a stub and a single-file API surface both count 1.
303
308
  if (countEntryDeclarations(content) > 0)
304
309
  return null;
305
310
  return target;
@@ -307,19 +312,17 @@ export function detectTypesRedirect(pkg) {
307
312
  /**
308
313
  * Follow the `@types/<name>` + triple-slash `<reference types>` redirect chain from
309
314
  * a package that ships no usable types of its own to the one that actually holds
310
- * the declarations — `bun` → `@types/bun` → `bun-types`. Bounded to three hops;
311
- * returns the package it started from when no better source is found.
315
+ * the declarations — `bun` → `@types/bun` → `bun-types`. Bounded to three hops:
316
+ * a chain of pure stubs stops after exactly three. The `visited` set cuts a cycle,
317
+ * and the package it started from comes back when no better source is found.
312
318
  *
313
- * This is what the four predicates above EXIST for. They were exported and heavily
314
- * tested (35 references between them) while the loop that calls them lived in two
315
- * byte-identical copies — `docs-core.ts` and `phantom-imports.ts` — and NEITHER was
316
- * covered: both of their tests pin only the zero-hop case, so the multi-hop
317
- * behaviour cited by name in five doc comments was asserted nowhere.
319
+ * This is what the four predicates above exist for, and the walk is written once
320
+ * for its two callers.
318
321
  *
319
- * `resolveHop` is the one thing the two call sites genuinely disagree about: the
320
- * docs pipeline resolves the next hop through an auto-installing async lookup, the
321
- * phantom-import checker through a bare sync resolve that must never install. It
322
- * returns null to stop the walk.
322
+ * `resolveHop` is the one thing those callers genuinely disagree about: docs-core
323
+ * resolves the next hop through an auto-installing async lookup, while
324
+ * phantom-imports wraps a bare sync `resolvePackage` that must never install.
325
+ * Returning null stops the walk.
323
326
  */
324
327
  export async function resolveTypeSource(start, seed, resolveHop) {
325
328
  const visited = new Set([start.name, seed]);
@@ -13,13 +13,12 @@ export interface RetrieveOptions {
13
13
  contentBudget?: number;
14
14
  }
15
15
  /**
16
- * How many chunks a retrieval returns, per corpus.
16
+ * How many chunks a retrieval returns, per corpus. An npm package gets 8 and
17
+ * project source gets 50.
17
18
  *
18
- * The two values differ and always have; this is the first place they sit side by
19
- * side, and no comment in the history explains WHY an npm package gets 8 and
20
- * project source gets 50. Recorded as-is rather than harmonised: changing either is
21
- * a retrieval-policy change with its own A/B, not a tidy-up. Before this they were
22
- * three declarations across three files, so the divergence was invisible.
19
+ * Nothing in this repo records WHY they differ. They are stated here, together,
20
+ * so the divergence is at least visible and changing either is a
21
+ * retrieval-policy change, not a tidy-up.
23
22
  */
24
23
  export declare const PACKAGE_RETRIEVE_LIMIT = 8;
25
24
  export declare const PROJECT_RETRIEVE_LIMIT = 50;
@@ -1,18 +1,18 @@
1
1
  /**
2
- * How many chunks a retrieval returns, per corpus.
2
+ * How many chunks a retrieval returns, per corpus. An npm package gets 8 and
3
+ * project source gets 50.
3
4
  *
4
- * The two values differ and always have; this is the first place they sit side by
5
- * side, and no comment in the history explains WHY an npm package gets 8 and
6
- * project source gets 50. Recorded as-is rather than harmonised: changing either is
7
- * a retrieval-policy change with its own A/B, not a tidy-up. Before this they were
8
- * three declarations across three files, so the divergence was invisible.
5
+ * Nothing in this repo records WHY they differ. They are stated here, together,
6
+ * so the divergence is at least visible and changing either is a
7
+ * retrieval-policy change, not a tidy-up.
9
8
  */
10
9
  export const PACKAGE_RETRIEVE_LIMIT = 8;
11
10
  export const PROJECT_RETRIEVE_LIMIT = 50;
12
11
  /** Character budget for the assembled chunk text. The same for both corpora. */
13
12
  export const RETRIEVE_CONTENT_BUDGET = 24_000;
14
- // Both callers always pass `limit`/`contentBudget` explicitly, so these defaults are
15
- // only a backstop for a third caller that does not.
13
+ // Both callers pass `limit` and `contentBudget` explicitly docs-core with the
14
+ // package pair, docs-project with the project pair so these defaults are only a
15
+ // backstop for a third caller that does not.
16
16
  const DEFAULT_LIMIT = PROJECT_RETRIEVE_LIMIT;
17
17
  const DEFAULT_BUDGET = RETRIEVE_CONTENT_BUDGET;
18
18
  const MIN_TOKEN_LEN = 2;
@@ -25,13 +25,11 @@ const FALLBACK_README_CHARS = 4_000;
25
25
  * multi-part identifiers into one string that occurs nowhere in the corpus:
26
26
  * "src/server/routes/auth.ts" -> "srcserverroutesauthts"
27
27
  * "Bun.password.hash" -> "Bunpasswordhash"
28
- * Because buildFtsQuery ORs the tokens, such a token contributes no MATCH at all, so the
29
- * single most informative term in the query — the path, or the dotted API symbol — was
30
- * dropped and ranking fell to the surrounding prose. Measured on the 141 real project
31
- * queries of mx5 run 13, that cost 18% of them any chunk from the file they named
32
- * (82% -> 99% retrieved once split on punctuation); on the 44 gradable npm queries,
33
- * discriminative-symbol recall 89.5% -> 96.4%.
34
- * See scripts/live-project-docs-retrieval-ab.ts and scripts/live-npm-tokenizer-regression.ts.
28
+ * Because buildFtsQuery ORs the tokens, a welded token contributes no MATCH at all.
29
+ * The single most informative term in the query — the path, or the dotted API
30
+ * symbol — is then dropped, and when it was the only term the retrieval falls all
31
+ * the way through to `fallbackChunks`: the alphabetically-first `.d.ts` and the
32
+ * first README, exactly what an empty query returns.
35
33
  */
36
34
  function tokenize(query) {
37
35
  return query.split(/[^a-zA-Z0-9_]+/).filter(t => t.length >= MIN_TOKEN_LEN);
@@ -65,6 +63,11 @@ function fallbackChunks(cache, name, version) {
65
63
  }
66
64
  return out;
67
65
  }
66
+ /**
67
+ * Trim the chunk list to the character budget, in retrieval order. The FIRST
68
+ * chunk is always kept, whatever its size — a top-ranked chunk larger than the
69
+ * whole budget would otherwise return nothing at all.
70
+ */
68
71
  function enforceBudget(chunks, budget) {
69
72
  if (!chunks.length)
70
73
  return chunks;
@@ -1,11 +1,14 @@
1
1
  /**
2
- * Web search via Exa's public MCP endpoint no API key required.
2
+ * Web search via Exa's public MCP endpoint. No API key: search-types.ts gives
3
+ * `exa` an empty env-var list, and the endpoint answers an unauthenticated POST.
3
4
  *
4
- * Exa hosts https://mcp.exa.ai/mcp as an intentionally keyless entry point: a
5
- * single JSON-RPC `tools/call` of `web_search_exa` returns search results with
6
- * content snippets. The response is SSE-framed (`data:` lines) or plain JSON,
7
- * and the result payload is one text blob of `Title:`/`URL:`/`Text:` blocks
8
- * separated by `---`, which we parse back into structured results.
5
+ * One JSON-RPC `tools/call` of `web_search_exa` against https://mcp.exa.ai/mcp
6
+ * returns the results. The response comes back `text/event-stream` an
7
+ * `event: message` line and one `data:` frame and `parseRpcBody` also accepts a
8
+ * plain JSON body. Inside the RPC result is a single text blob of blocks
9
+ * separated by `---`, each carrying `Title:`, `URL:`, and then either a `Text:`
10
+ * label or a `Highlights:` line. `parseResultBlocks` turns that back into
11
+ * structured results.
9
12
  */
10
13
  import { type FetchLike } from './http-request.js';
11
14
  import type { SearchResult } from './search-types.js';
@@ -1,11 +1,14 @@
1
1
  /**
2
- * Web search via Exa's public MCP endpoint no API key required.
2
+ * Web search via Exa's public MCP endpoint. No API key: search-types.ts gives
3
+ * `exa` an empty env-var list, and the endpoint answers an unauthenticated POST.
3
4
  *
4
- * Exa hosts https://mcp.exa.ai/mcp as an intentionally keyless entry point: a
5
- * single JSON-RPC `tools/call` of `web_search_exa` returns search results with
6
- * content snippets. The response is SSE-framed (`data:` lines) or plain JSON,
7
- * and the result payload is one text blob of `Title:`/`URL:`/`Text:` blocks
8
- * separated by `---`, which we parse back into structured results.
5
+ * One JSON-RPC `tools/call` of `web_search_exa` against https://mcp.exa.ai/mcp
6
+ * returns the results. The response comes back `text/event-stream` an
7
+ * `event: message` line and one `data:` frame and `parseRpcBody` also accepts a
8
+ * plain JSON body. Inside the RPC result is a single text blob of blocks
9
+ * separated by `---`, each carrying `Title:`, `URL:`, and then either a `Text:`
10
+ * label or a `Highlights:` line. `parseResultBlocks` turns that back into
11
+ * structured results.
9
12
  */
10
13
  import { httpRequest, HttpRequestError } from './http-request.js';
11
14
  const EXA_MCP_ENDPOINT = 'https://mcp.exa.ai/mcp';
@@ -31,7 +34,10 @@ export async function exaSearch(query, opts = {}) {
31
34
  ...(opts.fetchImpl === undefined ? {} : { fetchImpl: opts.fetchImpl })
32
35
  }, count, query);
33
36
  }
34
- /** The Exa-specific half: its argv, its status policy, its error type. */
37
+ /** The Exa-specific half: the JSON-RPC body it posts, its status policy, and its
38
+ * error type. Every failure leaves here as an ExaSearchError — `http` for a
39
+ * non-2xx, `protocol` for an RPC error / an `isError` result / no text content /
40
+ * an unparseable body, and `network` or `aborted` for a transport fault. */
35
41
  async function request(bounds, count, query) {
36
42
  try {
37
43
  return await httpRequest(EXA_MCP_ENDPOINT, {
@@ -83,9 +89,10 @@ async function request(bounds, count, query) {
83
89
  }
84
90
  }
85
91
  /**
86
- * The endpoint answers either as a text/event-stream (`data: {json}` lines) or
87
- * as a plain JSON body; accept both and take the first frame carrying a
88
- * JSON-RPC result or error.
92
+ * The endpoint answers as a text/event-stream (`data: {json}` lines) in practice,
93
+ * but a plain JSON body parses too. Take the FIRST `data:` frame that carries a
94
+ * JSON-RPC `result` or `error` — frames without either (a bare
95
+ * `{"jsonrpc":"2.0"}`) are skipped, not treated as the answer.
89
96
  */
90
97
  function parseRpcBody(body) {
91
98
  for (const line of body.split('\n')) {
@@ -116,8 +123,12 @@ function parseRpcBody(body) {
116
123
  /**
117
124
  * Split the tool's text payload into `Title:`/`URL:` blocks. The per-result
118
125
  * content lives after a `Text:` label (full text) or a `Highlights:` line
119
- * (snippet mode); either becomes the description, whitespace-collapsed and
120
- * capped so a result line stays a snippet, not a page dump.
126
+ * (snippet mode); either becomes the description, whitespace-collapsed, its
127
+ * trailing `---` removed, and capped at MAX_DESCRIPTION_CHARS so a result line
128
+ * stays a snippet, not a page dump.
129
+ *
130
+ * A block with no `URL:` is dropped entirely. A block with an empty `Title:`
131
+ * keeps the URL as its title, so a result is never nameless.
121
132
  */
122
133
  function parseResultBlocks(text) {
123
134
  const blocks = text.split(/(?=^Title: )/m).filter(b => b.trim().length > 0);
@@ -17,10 +17,9 @@ export interface FetchRawResult {
17
17
  }
18
18
  export declare function fetchRaw(input: FetchRawInput): Promise<FetchRawResult>;
19
19
  /**
20
- * How the page content is turned into the child's prompt. Injectable so an A/B harness can
21
- * run the shipped strategy against a frozen legacy one in the SAME process, and assert per
22
- * rep that the two arms differ by exactly the lever (see scripts/live-fetch-abstention-ab.ts).
23
- * Production always uses {@link shippedStrategy}.
20
+ * How the page content is turned into the child's prompt. A seam, so a harness can run a
21
+ * variant selection or prompt against the shipped one in the SAME process. Nothing in this
22
+ * repo overrides it: {@link shippedStrategy} is the only implementation.
24
23
  */
25
24
  export interface PromptStrategy {
26
25
  selectContent(markdown: string, requestedUrl: string): SelectedContent;
@@ -47,25 +46,23 @@ export interface FetchFocusedResult {
47
46
  excerpt?: string;
48
47
  excerptVerified?: boolean;
49
48
  /**
50
- * The page did not cover the asked-about version/topic — a DISTINCT outcome from
49
+ * The page did not cover the asked-about version or topic — a DISTINCT outcome from
51
50
  * `unclear from this page`, so the caller can pick a different URL instead of treating
52
- * "page has no answer" and "answer is ambiguous" as the same thing (PROMPT-3 item 3).
51
+ * "page has no answer" and "answer is ambiguous" as the same thing.
53
52
  */
54
53
  coverageMiss: boolean;
55
54
  /**
56
- * What to do INSTEAD, present only on a coverage miss. `coverageMiss` was computed and
57
- * stored and read nowhere, so the distinct channel it exists to provide never reached the
58
- * worker: all it ever saw was the bare sentence "not covered by this page", which reads
59
- * as "ask again, differently". It did — 9 of the 84 corpus fetches re-read a URL that had
60
- * already returned a non-answer, one release-notes page three times with near-identical
61
- * questions.
55
+ * What to do INSTEAD, present only on a coverage miss. Without it the worker sees only
56
+ * the bare sentence "not covered by this page", which reads as "ask again, differently"
57
+ * and re-reading the same URL with a reworded question returns the same answer.
62
58
  */
63
59
  nextStep?: string;
64
60
  /** The #fragment slug that was anchored, when the URL carried one and it was located. */
65
61
  anchoredSection?: string;
66
62
  /** Retained evidence for a false `excerptVerified`, so it is diagnosable without re-fetch. */
67
63
  excerptCheck?: ExcerptVerification;
68
- /** The prompt actually handed to the child the A/B asserts surgery against this per rep. */
64
+ /** The prompt actually handed to the child, returned so a caller can see exactly what
65
+ * the selection and the strategy produced. */
69
66
  assembledPrompt: string;
70
67
  /**
71
68
  * Set exactly when the child failed (aborted, or non-zero exit): the standard
@@ -96,10 +93,10 @@ export interface SelectedContent {
96
93
  /**
97
94
  * Slice a markdown page down to the section its heading-anchor #fragment names — the deep
98
95
  * link the caller actually asked for — instead of head-truncating the whole page and losing
99
- * a section that sits past the head window (PROMPT-3 item 2). Falls back to {@link truncate}
96
+ * a section that sits past the head window. Falls back to {@link truncate}
100
97
  * when the URL has no fragment, or the fragment names no heading in THIS content (e.g. the
101
- * page changed since the link was captured), so a fragment we cannot honour never degrades a
102
- * page we could otherwise read.
98
+ * page changed since the link was captured), so a fragment that cannot be honoured never
99
+ * degrades a page that could otherwise be read.
103
100
  */
104
101
  export declare function selectContent(markdown: string, requestedUrl: string): SelectedContent;
105
102
  /** The shipped strategy: fragment-aware selection + the recalibrated prompt. */
@@ -1,6 +1,7 @@
1
1
  import { fetchAndClean as defaultFetchAndClean } from './html-clean.js';
2
2
  import { runFocusedExtraction } from './focused-extractor.js';
3
3
  import { abstentionSentence } from './abstention.js';
4
+ import {} from '../shared/child-output.js';
4
5
  import { groupThinkingArgs } from '../config/reasoning-args.js';
5
6
  const CONTENT_BUDGET = 30_000;
6
7
  const HEAD_CHARS = 25_000;
@@ -14,19 +15,15 @@ export const NOT_COVERED_ANSWER = 'not covered by this page';
14
15
  * answer — anchored, not a substring search. A substring search misreads the opposite case:
15
16
  * rule 5 tells the child to answer partially and say what is missing, and it says it in the
16
17
  * prompt's own words ("… `obs_add_raw_audio_callback` and `obs_remove_raw_audio_callback`
17
- * are not covered by this page"). That is a sourced answer, and the loose match filed it as
18
- * a coverage miss. Observed twice in 5 reps of scripts/fetch-url-normalise-ab.ts once the
19
- * rewrite started delivering pages that could half-answer; never in the 84 recorded corpus
20
- * fetches, where 11 of 11 sentinel answers are the bare sentinel — so tightening it changes
21
- * no recorded verdict.
18
+ * are not covered by this page"). That is a sourced answer, and a loose match would file it
19
+ * as a coverage miss.
22
20
  */
23
21
  const NOT_COVERED_RE = /^not covered by this page[.\s]*$/i;
24
22
  /**
25
23
  * `github.com/{owner}/{repo}/blob/{ref}/{path}` renders the file through a client-side
26
- * viewer, so the HTML we clean carries GitHub chrome ("Sign in", "Appearance settings")
27
- * and none of the file. Every one of the 8 blob URLs in the three-project research-cache
28
- * corpus came back a non-answer for that reason. `raw.githubusercontent.com` serves the
29
- * same bytes as text/plain.
24
+ * viewer. The HTML carries GitHub chrome ("Sign in", "Appearance settings") and NOT the
25
+ * file: fetching one blob URL returns 282 KB of text/html that does not contain the file's
26
+ * bytes anywhere, while `raw.githubusercontent.com` returns the file itself as text/plain.
30
27
  *
31
28
  * Only the URL handed to the fetcher is rewritten — the caller's URL still keys the cache
32
29
  * and still supplies the #fragment — so a worker that retries the raw URL by hand hits the
@@ -141,10 +138,10 @@ function fragmentOf(url) {
141
138
  /**
142
139
  * Slice a markdown page down to the section its heading-anchor #fragment names — the deep
143
140
  * link the caller actually asked for — instead of head-truncating the whole page and losing
144
- * a section that sits past the head window (PROMPT-3 item 2). Falls back to {@link truncate}
141
+ * a section that sits past the head window. Falls back to {@link truncate}
145
142
  * when the URL has no fragment, or the fragment names no heading in THIS content (e.g. the
146
- * page changed since the link was captured), so a fragment we cannot honour never degrades a
147
- * page we could otherwise read.
143
+ * page changed since the link was captured), so a fragment that cannot be honoured never
144
+ * degrades a page that could otherwise be read.
148
145
  */
149
146
  export function selectContent(markdown, requestedUrl) {
150
147
  const frag = fragmentOf(requestedUrl);
@@ -159,9 +156,10 @@ function escapeRegExp(s) {
159
156
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
160
157
  }
161
158
  /** Find the heading whose anchor is `#<frag>` and return that heading's section: from the
162
- * heading line up to the next heading of the same or higher level. Anchors are matched on
163
- * the heading line in both the Docusaurus (`### Title[](#slug "…")`) and GitHub
164
- * (`## [Title](#slug)`) shapes; `#foo` must not match `#foobar`. */
159
+ * heading line up to the next heading of the same or HIGHER level, so a `##` section keeps
160
+ * its `###` subsections. Anchors are matched on the heading line in both the Docusaurus
161
+ * (`### Title[](#slug "…")`) and GitHub (`## [Title](#slug)`) shapes, and the trailing
162
+ * `(?![a-z0-9-])` is what keeps `#foo` from matching `#foobar`. */
165
163
  function sliceSection(markdown, frag) {
166
164
  const lines = markdown.split('\n');
167
165
  const anchor = new RegExp('#' + escapeRegExp(frag) + '(?![a-z0-9-])', 'i');
@@ -194,14 +192,16 @@ function truncate(md) {
194
192
  return md.slice(0, HEAD_CHARS) + TRUNCATION_MARKER + md.slice(md.length - TAIL_CHARS);
195
193
  }
196
194
  /**
197
- * The shipped extraction prompt. Rule 5 was recalibrated for PROMPT 3: the pre-change rule
198
- * sent the child to `unclear from this page` on ANY ambiguity, which threw away answers that
199
- * were on the page (measured: 5 of fetch's 10 failures were deterministic false-"unclear").
200
- * It now (5) answers from the content whenever the content supports one, even partially;
201
- * (6) reports a page-coverage MISS distinctly, so it is not conflated with ambiguity; and
202
- * (7) reserves `unclear` for genuinely-ambiguous content never for absence, and never as
203
- * licence to invent. The abstention was relaxed WITHOUT relaxing rules 1-2 (verbatim
204
- * excerpt) the fabrication guard the A/B scores is untouched.
195
+ * The shipped extraction prompt. Its three abstention rules are deliberately separate, and a
196
+ * child that collapses them loses information the caller needs:
197
+ * 5. answer from the content whenever it supports one, even partially "unclear" is the
198
+ * wrong response merely because coverage is incomplete;
199
+ * 6. a page about a DIFFERENT version or topic is a coverage MISS, `NOT_COVERED_ANSWER`,
200
+ * which the caller turns into "try a different URL";
201
+ * 7. `UNCLEAR_ANSWER` is reserved for content that IS present but ambiguous never for
202
+ * absence, and never as licence to invent.
203
+ * Rules 1 and 2, the verbatim-excerpt requirement the fabrication check rests on, are
204
+ * independent of all three.
205
205
  */
206
206
  function buildPrompt(args) {
207
207
  const sectionNote = args.section ?
@@ -1,9 +1,9 @@
1
1
  import { type SpawnFn } from '../shared/child-process.js';
2
2
  import { type ExcerptVerification } from '../shared/child-output.js';
3
3
  /**
4
- * The argv every focused extraction child runs with: the shared child base (whitelisted
5
- * extensions + `--print --no-session …`) plus `--no-tools`. This was three byte-identical
6
- * one-liners in fetch-core, docs-core and pi-worker-docs.
4
+ * The argv every focused extraction child runs with: the shared child base — any whitelisted
5
+ * extensions, then `--print --no-skills --no-extensions --no-prompt-templates
6
+ * --no-context-files --no-session` — then the caller's thinking fragment, then `--no-tools`.
7
7
  *
8
8
  * `--no-tools` is the contract, not a default: the child is given all the content it may use
9
9
  * inside its prompt, so a tool call could only reach for something unsourced.
@@ -13,11 +13,11 @@ export interface FocusedRequest {
13
13
  /** The fully assembled prompt, including the content block. Delivered on stdin. */
14
14
  prompt: string;
15
15
  /**
16
- * The text the cited excerpt is checked against — a NAMED knob, because the four call
17
- * sites deliberately disagree and the disagreement used to be invisible.
16
+ * The text the cited excerpt is checked against — a NAMED knob, because the two call
17
+ * sites deliberately disagree, and unnamed the disagreement is invisible.
18
18
  *
19
- * `pi-worker-docs` and `docsFocused` pass exactly the concatenated chunks that went into
20
- * the prompt. `fetchFocused` passes the FULL cleaned page while prompting with only the
19
+ * `docsLookup` passes exactly the concatenated chunks that went into the prompt.
20
+ * `fetchFocused` passes the FULL cleaned page while prompting with only the
21
21
  * anchored `#fragment` section: the slice is a substring of the page, so a genuine excerpt
22
22
  * still verifies, and an excerpt pulled from the model's memory still fails — fragment
23
23
  * anchoring therefore cannot change the hallucination detector's discrimination.
@@ -37,11 +37,10 @@ export interface FocusedRequest {
37
37
  * An already-resolved `['--thinking', level]` fragment, or `[]`/omitted to
38
38
  * inherit the session default.
39
39
  *
40
- * Supplied by the CALLER even though all three call sites are the same
40
+ * Supplied by the CALLER even though both call sites resolve the same
41
41
  * `extraction` group. Resolving it inside this module would make
42
42
  * `focusedChildArgs` read ambient config, and a function that reads config
43
- * internally cannot be tested without the developer's own machine state
44
- * which is exactly how a `getConfig()` assertion got into a unit test.
43
+ * internally cannot be tested without the developer's own machine state.
45
44
  */
46
45
  thinking?: readonly string[];
47
46
  }
@@ -50,7 +49,8 @@ interface FocusedChildEvidence {
50
49
  exitCode: number;
51
50
  aborted: boolean;
52
51
  stderr: string;
53
- /** The child's raw stdout, retained for A/B harnesses and failure diagnosis. */
52
+ /** The child's raw stdout, retained so a failure is diagnosable from the result alone —
53
+ * it is the only place a crashed child's output survives. */
54
54
  stdout: string;
55
55
  }
56
56
  /** The child aborted or exited non-zero. There is deliberately no `answer` here. */
@@ -78,7 +78,7 @@ export type FocusedResult = FocusedFailure | FocusedAnswer;
78
78
  * `<answer>`/`<excerpt>` and verify the excerpt against `verifyAgainst`.
79
79
  *
80
80
  * Never retries — a re-ask of a deterministic extraction over unchanged content is a second
81
- * bill for the same answer, and all four call sites already had it that way.
81
+ * bill for the same answer. Exactly one child is spawned per call.
82
82
  */
83
83
  export declare function runFocusedExtraction(req: FocusedRequest): Promise<FocusedResult>;
84
84
  export {};