@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
@@ -23,21 +23,25 @@ interface DocsDetails {
23
23
  versionSource?: 'declared-range' | 'npm-latest';
24
24
  declaredRange?: string;
25
25
  /**
26
- * The answer restated a declaration for a question that needed usage semantics, so it
27
- * is UNANSWERED (F-2). Set by isTypeOnlyAnswer; read by `cacheable` so a non-answer is
28
- * never memoised and re-served to a later sibling task.
26
+ * The answer restated a declaration for a question that needed usage semantics, so
27
+ * it is UNANSWERED. Set by isTypeOnlyAnswer; read by `docsCacheable` so a non-answer
28
+ * is never memoised and re-served to a later sibling task.
29
29
  */
30
30
  typeOnly?: boolean;
31
- /** The 5B CAP arm refused this call: the attempt's project-lookup budget is spent. */
31
+ /** The project-lookup budget for this attempt is spent, so the call was refused
32
+ * before any work. Only set when PI_TASK_PROJECT_DOCS_BUDGET is configured. */
32
33
  budgetSpent?: boolean;
33
34
  }
34
35
  /**
35
36
  * Pull `@see {@link https://…}` pointers out of retrieved .d.ts/README text.
36
37
  *
37
- * F-2(d): the answer to a type-only lookup usually is not in the package at all it lives
38
- * at the `@see` URL that the very excerpt being returned already carries. In run 15,
39
- * hono.dev appeared in cache values ONLY inside these JSDoc links, and was never fetched.
40
- * Surfacing the link is therefore free: the pointer is already in hand.
38
+ * When the answer to a type-only lookup is not in the package, it is often at the
39
+ * `@see` URL the excerpt being returned already carries. Surfacing that link costs
40
+ * nothing: the pointer is already in hand.
41
+ *
42
+ * Matches `{@link URL}` and a bare `@link URL`, case-insensitively; strips a trailing
43
+ * `.`/`,`/`;` the surrounding prose added; deduplicates. A bare URL with no `@see` is
44
+ * not a pointer and is not returned.
41
45
  */
42
46
  export declare function extractSeeUrls(content: string): string[];
43
47
  /**
@@ -57,24 +61,25 @@ export interface PiWorkerDocsInternals {
57
61
  }
58
62
  export declare function registerPiWorkerDocs(pi: ExtensionAPI, internals?: PiWorkerDocsInternals): void;
59
63
  /**
60
- * The F-2(e) cache rule for the docs channel, as a NAMED export rather than an
61
- * anonymous property of an adapter literal.
64
+ * The cache rule for the docs channel, as a NAMED export rather than an anonymous
65
+ * property of an adapter literal.
62
66
  *
63
- * It was reachable only through `registerTool execute()`, so
64
- * pi-worker-docs-typeonly.test.ts gave up and hand-retyped it under a
65
- * "keep in sync" commentsix tests asserting against a copy that a change to the
66
- * shipped rule would leave green. That is the same drift class the rule itself
67
- * exists to prevent: four regexes matching three phrasings, documented at length in
68
- * abstention.ts, which cost a real bug.
67
+ * As a property of the adapter literal it would be reachable only through
68
+ * `registerTool → execute()`, so a test would have to retype the rule and would then
69
+ * assert against its own copy green even after the shipped rule changed. Exported,
70
+ * the test imports the rule it is checking.
69
71
  */
70
72
  export declare function docsCacheable(d: Pick<DocsDetails, 'typeOnly' | 'excerptVerified'>, text: string): boolean;
71
- /** The docs cache key: a package's answer is per (module, question). A project-source
72
- * `.` lookup is never cached the working tree mutates as tasks implement. */
73
+ /** The docs cache key: a package's answer is per (module, question), with the question
74
+ * lowercased and its whitespace collapsed so phrasing variants share one entry. Returns
75
+ * null for the project-source `.` lookup, which is never cached — the working tree
76
+ * mutates as tasks implement. */
73
77
  export declare function docsCacheKey(params: {
74
78
  module: string;
75
79
  query: string;
76
80
  }): string | null;
77
- /** Package provenance for per-entry resume invalidation. */
81
+ /** Package provenance for per-entry resume invalidation: the package ROOT of the
82
+ * specifier (`hono/client` → `hono`), and undefined for the project-source `.`. */
78
83
  export declare function docsCachePkg(params: {
79
84
  module: string;
80
85
  }): string | undefined;
@@ -2,11 +2,13 @@ import { spawn as defaultSpawn } from 'node:child_process';
2
2
  import { Type } from '@sinclair/typebox';
3
3
  import { Text } from '@earendil-works/pi-tui';
4
4
  import { openCache as defaultOpenCache } from './docs-cache.js';
5
+ import { ensureIndexed as defaultEnsureIndexed } from './docs-index.js';
6
+ import { resolvePackage as defaultResolvePackage } from './docs-resolve.js';
5
7
  import { retrieveChunks as defaultRetrieveChunks } from './docs-retrieve.js';
6
8
  import { docsLookup } from './docs-lookup.js';
7
9
  import { projectCorpus } from './docs-project.js';
8
10
  import { docsRaw, packageCorpus, buildVersionBanner } from './docs-core.js';
9
- import { formatNpmVersionSection } from './npm-version.js';
11
+ import { npmVersionLookup as defaultNpmVersionLookup, formatNpmVersionSection } from './npm-version.js';
10
12
  import { childFailureReason, makeWorkerTool, workerAnswer, workerUnavailable } from './shared.js';
11
13
  import { isTypeOnlyAnswer } from '../task/type-only-answer.js';
12
14
  import { logDocsAnswer } from './typeonly-log.js';
@@ -27,10 +29,13 @@ const Params = Type.Object({
27
29
  /**
28
30
  * Pull `@see {@link https://…}` pointers out of retrieved .d.ts/README text.
29
31
  *
30
- * F-2(d): the answer to a type-only lookup usually is not in the package at all it lives
31
- * at the `@see` URL that the very excerpt being returned already carries. In run 15,
32
- * hono.dev appeared in cache values ONLY inside these JSDoc links, and was never fetched.
33
- * Surfacing the link is therefore free: the pointer is already in hand.
32
+ * When the answer to a type-only lookup is not in the package, it is often at the
33
+ * `@see` URL the excerpt being returned already carries. Surfacing that link costs
34
+ * nothing: the pointer is already in hand.
35
+ *
36
+ * Matches `{@link URL}` and a bare `@link URL`, case-insensitively; strips a trailing
37
+ * `.`/`,`/`;` the surrounding prose added; deduplicates. A bare URL with no `@see` is
38
+ * not a pointer and is not returned.
34
39
  */
35
40
  export function extractSeeUrls(content) {
36
41
  const out = [];
@@ -57,16 +62,15 @@ function pinDetails(pin) {
57
62
  return pin ? { versionSource: pin.source, declaredRange: pin.range } : {};
58
63
  }
59
64
  /**
60
- * The tool result for a focused-extraction child that failed, shared by both docs paths
61
- * the last chunk they still held in common after the extractor seam took the child running.
62
- * The paths differ only in `prefix`: the npm path leads every result, failures included, with
63
- * its version banner and npm-version header; the project path has neither.
65
+ * The tool result for a focused-extraction child that failed, shared by both docs paths.
66
+ * They differ only in `prefix`: the npm path leads every result, failures included, with
67
+ * its version banner and npm-version header; the project path passes ''.
64
68
  *
65
- * It says UNAVAILABLE, so the cache cannot take it. It used to say so by writing a
66
- * non-zero `childExitCode` and letting `docsCacheable` re-derive the verdict a
67
- * derivation that failed on the case it mattered most for: a signal-killed child
68
- * reports exit code 0, so `"Docs lookup aborted."` was cached for the whole run.
69
- * `aborted` was written here and read by nothing, which is what let that hide.
69
+ * It says UNAVAILABLE, so the cache cannot take it. Writing a non-zero `childExitCode`
70
+ * and letting `docsCacheable` re-derive the verdict would fail on the case it matters
71
+ * most for: node reports a signal kill as `close` with `code === null`, and
72
+ * child-process.ts settles that as `code ?? 0` so an aborted child carries exit code
73
+ * ZERO and `"Docs lookup aborted."` would be cached for the whole run.
70
74
  */
71
75
  function docsFailureResult(extraction, baseDetails, prefix) {
72
76
  return workerUnavailable(prefix + extraction.failure, {
@@ -76,13 +80,12 @@ function docsFailureResult(extraction, baseDetails, prefix) {
76
80
  }, childFailureReason({ exitCode: extraction.exitCode, aborted: extraction.aborted }));
77
81
  }
78
82
  export function registerPiWorkerDocs(pi, internals = {}) {
79
- // CAP arm of nexttask 5B — OFF unless PI_TASK_PROJECT_DOCS_BUDGET is set, and
80
- // then per-ATTEMPT by construction: the extension is loaded into a fresh pi
81
- // child on every spawn, so a restarted attempt starts this counter at 0. The
82
- // budget it enforces is the one the worker was told about in its prompt
83
- // (projectDocsBudgetNotice) enforcement without the notice would be a
84
- // silent tool failure, and the notice without enforcement is what run 18
85
- // already shows does not bind.
83
+ // Project-lookup budget — OFF unless PI_TASK_PROJECT_DOCS_BUDGET is set, and then
84
+ // per-ATTEMPT by construction: the extension is loaded into a fresh pi child on
85
+ // every spawn, so a restarted attempt starts this counter at 0. The budget it
86
+ // enforces is the one the worker was told about in its prompt
87
+ // (projectDocsBudgetNotice). Enforcement without the notice would be a silent
88
+ // tool failure; the notice without enforcement binds nothing.
86
89
  let projectLookups = 0;
87
90
  makeWorkerTool(pi, {
88
91
  name: 'pi-worker-docs',
@@ -121,12 +124,9 @@ export function registerPiWorkerDocs(pi, internals = {}) {
121
124
  + '- You need docs for a specific newer version than what is installed — use pi-worker-fetch on the upstream docs site',
122
125
  parameters: Params,
123
126
  async run(params, signal, ctx) {
124
- // Always node:child_process spawn (matching fetch-core and every other
125
- // worker). The former globalThis.Bun branch called Bun.spawn — whose signature
126
- // is Bun.spawn([cmd, ...args], opts), NOT the node (cmd, args, opts) that
127
- // runChild/SpawnFn require — so it threw "cmd must be an array" whenever it ran.
128
- // It was DEAD in production (pi runs under node) and BYPASSED under bun test
129
- // (internals.spawn is always injected), i.e. untested, unreachable, and wrong.
127
+ // Always node:child_process spawn, matching fetch-core and every other
128
+ // worker. `SpawnFn` is the node `(cmd, args, opts)` shape; Bun.spawn takes
129
+ // `([cmd, ...args], opts)` and would throw here.
130
130
  const spawn = internals.spawn ?? defaultSpawn;
131
131
  // Both arms below run the SAME tail — concatenate, extract, verify,
132
132
  // format — through `docsLookup`; only the CORPUS differs. The
@@ -184,12 +184,10 @@ export function registerPiWorkerDocs(pi, internals = {}) {
184
184
  if (r.kind === 'failed')
185
185
  return docsFailureResult(r.extraction, baseDetails, '');
186
186
  const { extraction, excerptVerified: verified, body: text } = r;
187
- // SAME instrumentation channel as the package path below, extended to the
188
- // project-source branch because that branch is the MAJORITY of what
189
- // worker:apis asks 13 of 17 docs calls in run 15's fatal task, 7 of 12 in
190
- // the first live diagnostic rep. With only the package path recorded, "the
191
- // last docs answer before the worker stopped" was unanswerable: the sink's
192
- // last row was routinely not the worker's last answer.
187
+ // SAME instrumentation channel as the package path below. Both branches
188
+ // record, or "the last docs answer before the worker stopped" is
189
+ // unanswerable whenever the last answer came from the branch that does
190
+ // not log.
193
191
  //
194
192
  // `typeOnly` is recorded FALSE with an explicit reason rather than by running
195
193
  // the detector: this path never applies it, and the record must say what the
@@ -239,11 +237,11 @@ export function registerPiWorkerDocs(pi, internals = {}) {
239
237
  hitCache: rawResult.hitCache,
240
238
  cacheError: rawResult.cacheError,
241
239
  autoInstalled: rawResult.autoInstalled,
242
- // BUG FIX. Both sibling arms carry the pin and this one dropped
243
- // it, so a package that WAS auto-installed and then failed to
244
- // re-resolve lost its `versionSource`/`declaredRange` the
245
- // provenance the last defect in this area was about. `docsRaw`
246
- // sets `autoInstallPin` on three of its five error returns.
240
+ // Carry the pin here as the sibling arms do. Without it a package
241
+ // that WAS auto-installed and then failed to re-resolve loses its
242
+ // `versionSource`/`declaredRange`, and the answer cannot say what
243
+ // version it is grounded in. `docsRaw` sets `autoInstallPin` on
244
+ // every error return that follows an auto-install.
247
245
  ...pinDetails(rawResult.autoInstallPin),
248
246
  ...npmDetails
249
247
  };
@@ -265,7 +263,6 @@ export function registerPiWorkerDocs(pi, internals = {}) {
265
263
  ...npmDetails
266
264
  });
267
265
  }
268
- // kind === 'ok'
269
266
  const { pkg, chunks, hitCache, indexingMs, cacheError, autoInstalled } = rawResult;
270
267
  const versionBanner = buildVersionBanner(rawResult.autoInstallPin, pkg.name, pkg.version, ctx.cwd);
271
268
  const baseDetails = {
@@ -283,18 +280,16 @@ export function registerPiWorkerDocs(pi, internals = {}) {
283
280
  return docsFailureResult(r.extraction, baseDetails, versionBanner + npmHeader);
284
281
  }
285
282
  const { extraction, excerptVerified: verified, body, content: concatenated } = r;
286
- // F-2: a TYPE-ONLY answer is the dangerous failure. "unclear from this package"
283
+ // A TYPE-ONLY answer is the dangerous failure. "unclear from this package"
287
284
  // is honest and already escalates; a signature is a well-formed, confident,
288
285
  // on-topic answer that names the very parameter asked about, so the worker
289
- // stops — and worker:context then fills the semantic gap from memory (F-1).
290
- // Measured: 14 of 17 live reps terminated on exactly this shape.
286
+ // stops asking — and the semantic gap then gets filled from memory.
291
287
  //
292
288
  // The retrieved type is KEPT (it is real and useful) and an UNANSWERED banner
293
289
  // is prepended, naming the gap and — when the excerpt carries one — the `@see`
294
- // URL that actually documents the semantics. That pointer is free: F-2(d) found
295
- // hono.dev present in run-15 cache values ONLY inside these JSDoc links, never
296
- // fetched. Prompting the escalation beats performing it here: this tool runs in
297
- // parallel execution mode and cannot cleanly spawn a fetch of its own.
290
+ // URL that actually documents the semantics. Prompting the escalation beats
291
+ // performing it here: this tool runs in parallel execution mode and cannot
292
+ // cleanly spawn a fetch of its own.
298
293
  const typeOnly = isTypeOnlyAnswer(extraction.answer, params.query);
299
294
  let text = versionBanner + npmHeader + body;
300
295
  if (typeOnly.typeOnly) {
@@ -315,15 +310,14 @@ export function registerPiWorkerDocs(pi, internals = {}) {
315
310
  + '\nThe declaration that WAS retrieved (context only, not the answer):\n'
316
311
  + body;
317
312
  }
318
- // STAGE 1 INSTRUMENTATION — off unless PI_TASK_TYPEONLY_LOG names a sink, and
319
- // side-effect only: nothing below reads it, and every failure inside is
320
- // swallowed. It records EVERY answer, flagged or not, because the open question
321
- // is a RATE how often this fires — and a log of firings alone has no
322
- // denominator. See typeonly-log.ts.
313
+ // Off unless PI_TASK_TYPEONLY_LOG names a sink, and side-effect only: nothing
314
+ // below reads it, and every failure inside is swallowed. It records EVERY
315
+ // answer, flagged or not a log of firings alone would have no denominator to
316
+ // read a rate against. See typeonly-log.ts.
323
317
  //
324
- // It sits AFTER `text` is final (it used to sit above `text`'s first assignment)
318
+ // It sits AFTER `text` is final, not above `text`'s first assignment,
325
319
  // so the record carries what the worker was actually handed, banner and cited
326
- // excerpt included, not just the child's prose. Purely a move: logDocsAnswer
320
+ // excerpt included, not just the child's prose. Position only: logDocsAnswer
327
321
  // returns nothing and nothing between the two positions reads it, so the tool's
328
322
  // behaviour and its return value are unchanged.
329
323
  logDocsAnswer({
@@ -362,16 +356,12 @@ export function registerPiWorkerDocs(pi, internals = {}) {
362
356
  // matched against package.json verbatim (npm names are case-sensitive), unlike
363
357
  // the cache key, which normalises for phrasing collisions.
364
358
  cachePkg: docsCachePkg,
365
- // Only a completed lookup (child exited 0) is a real answer; not-installed,
366
- // no-chunks, resolve/cache errors, and aborts omit childExitCode:0 and fall
367
- // through to a live retry next time.
368
- //
369
- // F-2(e): process health is NOT answer quality. A child that ran fine and answered
370
- // "unclear from this package" exits 0, so the NON-ANSWER was memoised and re-served
371
- // as a cache hit to every later sibling task — 52 of run 15's cached entries were
372
- // "unclear" with hitCache true. One dead end, paid for many times, and escalation
373
- // could never re-fire because the miss never recurred. So a non-answer is now never
374
- // stored: the next task that asks pays for a real lookup and can escalate.
359
+ // Process health is NOT answer quality, and this rule only judges quality.
360
+ // not-installed, resolve and cache errors, and aborts return `unavailable`, which
361
+ // makeWorkerTool refuses before reaching here. What is left is real answers, and a
362
+ // child that ran fine and answered "unclear from this package" exits 0 — so a rule
363
+ // keyed on exit code would memoise that non-answer and re-serve it as a hit to
364
+ // every later sibling, with nothing left to re-trigger an escalation.
375
365
  //
376
366
  // `text` is supplied by makeWorkerTool (shared.ts) alongside details, so the
377
367
  // content check needs no new plumbing.
@@ -379,31 +369,32 @@ export function registerPiWorkerDocs(pi, internals = {}) {
379
369
  });
380
370
  }
381
371
  /**
382
- * The F-2(e) cache rule for the docs channel, as a NAMED export rather than an
383
- * anonymous property of an adapter literal.
372
+ * The cache rule for the docs channel, as a NAMED export rather than an anonymous
373
+ * property of an adapter literal.
384
374
  *
385
- * It was reachable only through `registerTool execute()`, so
386
- * pi-worker-docs-typeonly.test.ts gave up and hand-retyped it under a
387
- * "keep in sync" commentsix tests asserting against a copy that a change to the
388
- * shipped rule would leave green. That is the same drift class the rule itself
389
- * exists to prevent: four regexes matching three phrasings, documented at length in
390
- * abstention.ts, which cost a real bug.
375
+ * As a property of the adapter literal it would be reachable only through
376
+ * `registerTool → execute()`, so a test would have to retype the rule and would then
377
+ * assert against its own copy green even after the shipped rule changed. Exported,
378
+ * the test imports the rule it is checking.
391
379
  */
392
380
  export function docsCacheable(d, text) {
393
381
  // Answer QUALITY only. Whether there IS an answer is `WorkerOutcome.kind`, and
394
382
  // `makeWorkerTool` has already refused an `unavailable` before reaching here —
395
- // this used to open with `childExitCode === 0`, which a signal-killed child
396
- // satisfies, so an aborted lookup was memoised for the run.
383
+ // opening this with `childExitCode === 0` memoises an aborted lookup for the
384
+ // whole run, because a signal-killed child satisfies it.
397
385
  return d.typeOnly !== true && d.excerptVerified !== false && !isAbstention(text);
398
386
  }
399
- /** The docs cache key: a package's answer is per (module, question). A project-source
400
- * `.` lookup is never cached the working tree mutates as tasks implement. */
387
+ /** The docs cache key: a package's answer is per (module, question), with the question
388
+ * lowercased and its whitespace collapsed so phrasing variants share one entry. Returns
389
+ * null for the project-source `.` lookup, which is never cached — the working tree
390
+ * mutates as tasks implement. */
401
391
  export function docsCacheKey(params) {
402
392
  return params.module === '.' ?
403
393
  null
404
394
  : `${normalizeQuery(params.module)}::${normalizeQuery(params.query)}`;
405
395
  }
406
- /** Package provenance for per-entry resume invalidation. */
396
+ /** Package provenance for per-entry resume invalidation: the package ROOT of the
397
+ * specifier (`hono/client` → `hono`), and undefined for the project-source `.`. */
407
398
  export function docsCachePkg(params) {
408
399
  return params.module === '.' ? undefined : packageRootOf(params.module);
409
400
  }
@@ -24,9 +24,13 @@ export interface PiWorkerFetchInternals {
24
24
  }
25
25
  export declare function registerPiWorkerFetch(pi: ExtensionAPI, internals?: PiWorkerFetchInternals): void;
26
26
  /**
27
- * The F-2(e) cache rule for the fetch channel, named for the same reason as
28
- * `docsCacheable`: pi-worker-fetch.test.ts carried a hand-retyped copy driving four
29
- * tests, which a change to the shipped rule would leave green.
27
+ * The cache rule for the fetch channel, named for the same reason as `docsCacheable`:
28
+ * left anonymous inside the adapter literal, a test can only hand-retype it, and then
29
+ * asserts against a copy a change to the shipped rule would leave green.
30
+ *
31
+ * A coverage MISS is cacheable. "not covered by this page" is a real, correct answer
32
+ * ABOUT that page, and re-fetching cannot change it — only the abstention sentinel is
33
+ * refused.
30
34
  */
31
35
  export declare function fetchCacheable(_d: Pick<FetchDetails, never>, text: string): boolean;
32
36
  /** The fetch cache key. URL verbatim (path case can matter), question normalised —
@@ -1,6 +1,6 @@
1
1
  import { Type } from '@sinclair/typebox';
2
2
  import { Text } from '@earendil-works/pi-tui';
3
- import { FetchAndCleanError } from './html-clean.js';
3
+ import { fetchAndClean as defaultFetchAndClean, FetchAndCleanError } from './html-clean.js';
4
4
  import { fetchFocused } from './fetch-core.js';
5
5
  import { formatResultText } from '../shared/child-output.js';
6
6
  import { childFailureReason, makeWorkerTool, workerAnswer, workerUnavailable } from './shared.js';
@@ -47,8 +47,8 @@ export function registerPiWorkerFetch(pi, internals = {}) {
47
47
  spawn: internals.spawn
48
48
  });
49
49
  // Child failure is decided and formatted once, inside the focused extractor
50
- // (workers/focused-extractor.ts) — this used to re-map the result back into a
51
- // ChildOutcome just to ask formatChildFailure the same question.
50
+ // (workers/focused-extractor.ts) — re-mapping the result back into a
51
+ // ChildOutcome here would just ask formatChildFailure the same question.
52
52
  if (result.failure !== undefined) {
53
53
  return workerUnavailable(result.failure, { childExitCode: result.childExitCode }, childFailureReason({
54
54
  exitCode: result.childExitCode,
@@ -85,30 +85,38 @@ export function registerPiWorkerFetch(pi, internals = {}) {
85
85
  text += `\n${theme.fg('dim', ` query: ${truncatedQuery}`)}`;
86
86
  return new Text(text, 0, 0);
87
87
  },
88
- // Cache fetch answers per run (the same page re-fetched across sibling tasks
89
- // otherwise). The URL is kept verbatim (path case can matter); the query is
90
- // normalised. Both parts key the entry same page, different question is a
88
+ // Cache fetch answers per run, or the same page is re-fetched by every sibling
89
+ // task. Both parts key the entry: the URL trimmed but otherwise VERBATIM, since
90
+ // path case can matter, and the query lowercased with its whitespace collapsed
91
+ // so phrasing variants share an entry. Same page, different question is a
91
92
  // different answer.
92
93
  cacheKey: fetchCacheKey,
93
- // Only a completed fetch (child exited 0) is a real answer; invalid-URL,
94
- // fetch failures, and aborts omit childExitCode:0 and fall through.
95
- // F-2(e), on the fetch channel. A child that ran fine and answered
96
- // "unclear from this page" exits 0, so caching on process health alone
97
- // memoised the NON-ANSWER and re-served it to every later sibling task
98
- // the same dead-end-paid-many-times shape pi-worker-docs already closed
99
- // for packages, with escalation unable to re-fire because the miss never
100
- // recurred. One predicate now covers every corpus (workers/abstention.ts).
94
+ // Only a completed fetch is a real answer; invalid-URL, fetch failures and
95
+ // aborts come back as `unavailable`, which makeWorkerTool refuses before the
96
+ // cache is consulted at all.
97
+ //
98
+ // Process health is NOT answer quality. A child that ran fine and answered
99
+ // "unclear from this page" exits 0, so caching on health alone memoises the
100
+ // NON-ANSWER and re-serves it to every later sibling task — one dead end paid
101
+ // for many times, with escalation unable to re-fire because the miss never
102
+ // recurs. One predicate covers every corpus (workers/abstention.ts).
101
103
  cacheable: fetchCacheable
102
104
  });
103
105
  }
104
106
  /**
105
- * The F-2(e) cache rule for the fetch channel, named for the same reason as
106
- * `docsCacheable`: pi-worker-fetch.test.ts carried a hand-retyped copy driving four
107
- * tests, which a change to the shipped rule would leave green.
107
+ * The cache rule for the fetch channel, named for the same reason as `docsCacheable`:
108
+ * left anonymous inside the adapter literal, a test can only hand-retype it, and then
109
+ * asserts against a copy a change to the shipped rule would leave green.
110
+ *
111
+ * A coverage MISS is cacheable. "not covered by this page" is a real, correct answer
112
+ * ABOUT that page, and re-fetching cannot change it — only the abstention sentinel is
113
+ * refused.
108
114
  */
109
115
  export function fetchCacheable(_d, text) {
110
- // Answer QUALITY only — see docsCacheable. `childExitCode === 0` used to lead
111
- // this rule and was true of an aborted child, so `"Fetch aborted."` cached.
116
+ // Answer QUALITY only — see docsCacheable. This predicate returns true for
117
+ // `"Fetch aborted."` on its own; what keeps an aborted fetch out of the cache is
118
+ // the `unavailable` outcome upstream. Leading the rule with `childExitCode === 0`
119
+ // would not, because an aborted child settles at exit code 0.
112
120
  return !isAbstention(text);
113
121
  }
114
122
  /** The fetch cache key. URL verbatim (path case can matter), question normalised —
@@ -41,8 +41,9 @@ export function registerPiWorkerSearch(pi, internals = {}) {
41
41
  return workerUnavailable(result.message, { resultCount: 0 }, result.kind);
42
42
  }
43
43
  const { results } = result;
44
- // Zero results IS an answer: the search ran and the web has nothing.
45
- // Only a search that could not run is unavailable.
44
+ // Zero results IS an answer: the search ran and the web has nothing, so it
45
+ // comes back as `workerAnswer` with resultCount 0. Only a search that could
46
+ // not run — no key, or an engine error — is `unavailable`.
46
47
  if (results.length === 0) {
47
48
  return workerAnswer(`No results for: ${params.query}`, { resultCount: 0 });
48
49
  }
@@ -57,13 +58,16 @@ export function registerPiWorkerSearch(pi, internals = {}) {
57
58
  }
58
59
  return new Text(text, 0, 0);
59
60
  },
60
- // Cache search results per run (the same query re-run across sibling tasks hits
61
- // the live web anew otherwise). Count is part of the key a larger request is a
62
- // different result set and so is the provider: two engines' result sets for
63
- // one query are different answers and must not serve for each other.
61
+ // Cache search results per run, or the same query re-run by a sibling task hits
62
+ // the live web again. Three parts key the entry: the PROVIDER, because two
63
+ // engines' result sets for one query are different answers and must not serve
64
+ // for each other; the query, lowercased and whitespace-collapsed by
65
+ // `normalizeQuery` so phrasing variants share an entry; and the COUNT, because a
66
+ // larger request is a different result set.
64
67
  cacheKey: params => `${provider()}::${normalizeQuery(params.query)}::${params.count ?? ''}`,
65
- // Only a non-empty result set is worth caching; no-key, error, and empty results
66
- // (resultCount 0) fall through so a later attempt can succeed.
68
+ // Only a non-empty result set is worth caching. An empty one falls through so a
69
+ // later attempt can succeed; no-key and engine errors never reach this at all,
70
+ // since makeWorkerTool refuses to store an `unavailable` outcome.
67
71
  cacheable: d => d.resultCount > 0
68
72
  });
69
73
  }
@@ -1,10 +1,15 @@
1
1
  /**
2
2
  * pi-worker — minimal subagent tool.
3
3
  *
4
- * Spawns a sandboxed child `pi --print` for each call, returns its stdout.
5
- * Child has read+grep+find+ls only (no bash, write, or edit) — no skills,
6
- * extensions, prompt templates, context files, or session storage. Cannot
7
- * recurse into another worker.
4
+ * Spawns one child pi per call and returns its assistant text. `childBaseArgs`
5
+ * gives the child `--print --no-skills --no-extensions --no-prompt-templates
6
+ * --no-context-files --no-session`, and runWorker adds `--mode json` plus its
7
+ * default `--tools read,grep,find,ls`.
8
+ *
9
+ * That tool string is what makes the child read-only and non-recursive: no bash,
10
+ * write or edit, and no `pi-worker` of its own to dispatch. `--no-extensions`
11
+ * disables DISCOVERY, so the user's whitelisted `-e` extensions are still loaded
12
+ * — the tool whitelist, not the extension flag, is the bound that holds.
8
13
  */
9
14
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
10
15
  export declare function registerPiWorker(pi: ExtensionAPI): void;
@@ -1,16 +1,22 @@
1
1
  /**
2
2
  * pi-worker — minimal subagent tool.
3
3
  *
4
- * Spawns a sandboxed child `pi --print` for each call, returns its stdout.
5
- * Child has read+grep+find+ls only (no bash, write, or edit) — no skills,
6
- * extensions, prompt templates, context files, or session storage. Cannot
7
- * recurse into another worker.
4
+ * Spawns one child pi per call and returns its assistant text. `childBaseArgs`
5
+ * gives the child `--print --no-skills --no-extensions --no-prompt-templates
6
+ * --no-context-files --no-session`, and runWorker adds `--mode json` plus its
7
+ * default `--tools read,grep,find,ls`.
8
+ *
9
+ * That tool string is what makes the child read-only and non-recursive: no bash,
10
+ * write or edit, and no `pi-worker` of its own to dispatch. `--no-extensions`
11
+ * disables DISCOVERY, so the user's whitelisted `-e` extensions are still loaded
12
+ * — the tool whitelist, not the extension flag, is the bound that holds.
8
13
  */
9
14
  import { Text } from '@earendil-works/pi-tui';
10
15
  import { Type } from '@sinclair/typebox';
11
16
  import { getConfig } from '../config/config.js';
12
17
  import { groupThinkingArgs } from '../config/reasoning-args.js';
13
18
  import { runWorker } from './pi-worker-core.js';
19
+ import { getParentContextWindow } from '../task/context-usage.js';
14
20
  import { childFailureReason, formatChildFailure, makeWorkerTool, workerAnswer, workerUnavailable } from './shared.js';
15
21
  const RENDER_PROMPT_MAX = 120;
16
22
  const WorkerParams = Type.Object({
@@ -40,20 +46,27 @@ export function registerPiWorker(pi) {
40
46
  + '- The task needs the web — use `pi-worker-search` / `pi-worker-fetch`',
41
47
  parameters: WorkerParams,
42
48
  async run(params, signal, ctx) {
43
- // Grouped with `research`: this is the same read-only exploration
44
- // loop the four research workers run, just dispatched by a model
45
- // rather than by the pipeline. Left ungrouped it would be the one
46
- // child that never honoured a profile.
49
+ // `adhoc` guards, `research` thinking deliberately not the same word.
50
+ // Guards answer "how may this child die"; the reasoning group answers
51
+ // "how hard may it think", and this is the same read-only exploration
52
+ // loop the research workers run, just dispatched by a model rather than
53
+ // by the pipeline. See the header of worker-profiles.ts.
47
54
  const result = await runWorker({
48
55
  prompt: params.prompt,
49
56
  cwd: ctx.cwd,
50
57
  signal,
51
58
  profile: 'adhoc',
52
- // The user's own `stuck reply retry` is what bounds this worker
53
- // now it kills on SILENCE, never on slowness. It is an INPUT and
59
+ // The `adhoc` profile carries NO wall clock (`timeoutMs: 0`); what
60
+ // bounds this worker is the user's own `stuck reply retry` setting,
61
+ // which kills on SILENCE and never on slowness. It is an INPUT and
54
62
  // not policy for the same reason the gate's two ceilings are: the
55
63
  // number is the user's, the decision to arm it is the profile's.
56
64
  policyInputs: { streamInactivityMs: getConfig().streamInactivityMs },
65
+ // The session's own window, handed down. No child argv anywhere in
66
+ // this codebase passes `-m`, so the parent's model IS the child's
67
+ // model and its window is the honest one. Without this the churn
68
+ // rule cannot fire — see RunWorkerInput.contextWindow.
69
+ contextWindow: getParentContextWindow(ctx) || 'unknown',
57
70
  thinking: groupThinkingArgs('research')
58
71
  });
59
72
  const details = { exitCode: result.exitCode };