@mjasnikovs/pi-task 0.38.29 → 0.38.31

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 (373) 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/index.js +2 -0
  14. package/dist/remote/bridge.d.ts +19 -10
  15. package/dist/remote/bridge.js +3 -2
  16. package/dist/remote/broadcast.js +3 -1
  17. package/dist/remote/events.js +12 -11
  18. package/dist/remote/history.d.ts +1 -1
  19. package/dist/remote/protocol.d.ts +6 -3
  20. package/dist/remote/protocol.js +2 -1
  21. package/dist/remote/push.d.ts +16 -16
  22. package/dist/remote/push.js +27 -27
  23. package/dist/remote/register.d.ts +3 -3
  24. package/dist/remote/register.js +17 -19
  25. package/dist/remote/server.d.ts +9 -8
  26. package/dist/remote/server.js +15 -14
  27. package/dist/remote/session-state.d.ts +5 -4
  28. package/dist/remote/session-state.js +8 -5
  29. package/dist/remote/sw.d.ts +7 -6
  30. package/dist/remote/sw.js +7 -6
  31. package/dist/remote/tailscale.d.ts +4 -2
  32. package/dist/remote/tailscale.js +4 -2
  33. package/dist/remote/ui-highlight.js +6 -5
  34. package/dist/remote/ui-render.js +4 -4
  35. package/dist/remote/ui-script.js +24 -24
  36. package/dist/remote/ui-styles.d.ts +1 -1
  37. package/dist/remote/ui-styles.js +10 -13
  38. package/dist/remote/ui-tools.js +9 -6
  39. package/dist/shared/child-extensions.d.ts +29 -17
  40. package/dist/shared/child-extensions.js +29 -17
  41. package/dist/shared/child-output.d.ts +30 -24
  42. package/dist/shared/child-output.js +25 -17
  43. package/dist/shared/child-process.d.ts +47 -40
  44. package/dist/shared/child-process.js +50 -59
  45. package/dist/shared/command-watchdog.d.ts +85 -16
  46. package/dist/shared/command-watchdog.js +115 -21
  47. package/dist/shared/fs-text.d.ts +16 -10
  48. package/dist/shared/fs-text.js +16 -10
  49. package/dist/shared/git-runner.d.ts +25 -25
  50. package/dist/shared/git-runner.js +25 -25
  51. package/dist/shared/leaked-tool-call.d.ts +17 -11
  52. package/dist/shared/leaked-tool-call.js +23 -15
  53. package/dist/shared/model-endpoint.d.ts +29 -16
  54. package/dist/shared/model-endpoint.js +33 -21
  55. package/dist/shared/pi-invocation.d.ts +7 -4
  56. package/dist/shared/pi-invocation.js +12 -7
  57. package/dist/shared/pkg-version.d.ts +13 -5
  58. package/dist/shared/pkg-version.js +13 -5
  59. package/dist/shared/reasoning-capability.d.ts +35 -24
  60. package/dist/shared/reasoning-capability.js +35 -24
  61. package/dist/shared/stream-watchdog.d.ts +60 -44
  62. package/dist/shared/stream-watchdog.js +62 -45
  63. package/dist/task/accept-debt.d.ts +41 -43
  64. package/dist/task/accept-debt.js +73 -65
  65. package/dist/task/api-synthesis.d.ts +24 -21
  66. package/dist/task/api-synthesis.js +32 -26
  67. package/dist/task/apis-contract.d.ts +32 -64
  68. package/dist/task/apis-contract.js +32 -64
  69. package/dist/task/artifact-closure.d.ts +27 -13
  70. package/dist/task/artifact-closure.js +95 -67
  71. package/dist/task/auto-commit.d.ts +46 -35
  72. package/dist/task/auto-commit.js +51 -38
  73. package/dist/task/auto-io.d.ts +45 -25
  74. package/dist/task/auto-io.js +57 -29
  75. package/dist/task/auto-orchestrator.d.ts +26 -24
  76. package/dist/task/auto-orchestrator.js +192 -165
  77. package/dist/task/auto-prompts.d.ts +36 -24
  78. package/dist/task/auto-prompts.js +40 -26
  79. package/dist/task/autofix-ledger.d.ts +27 -25
  80. package/dist/task/autofix-ledger.js +29 -26
  81. package/dist/task/batch-test-task.d.ts +20 -12
  82. package/dist/task/batch-test-task.js +67 -60
  83. package/dist/task/boot-probe.d.ts +60 -44
  84. package/dist/task/boot-probe.js +91 -72
  85. package/dist/task/cancel-input.d.ts +30 -16
  86. package/dist/task/cancel-input.js +20 -11
  87. package/dist/task/cancel-points.d.ts +27 -20
  88. package/dist/task/cancel-points.js +30 -22
  89. package/dist/task/child-runner.d.ts +124 -55
  90. package/dist/task/child-runner.js +298 -90
  91. package/dist/task/child-status.d.ts +23 -16
  92. package/dist/task/child-status.js +23 -16
  93. package/dist/task/clamp-output.js +12 -5
  94. package/dist/task/command-run.d.ts +31 -28
  95. package/dist/task/command-run.js +44 -35
  96. package/dist/task/command-shrink.d.ts +25 -18
  97. package/dist/task/command-shrink.js +37 -31
  98. package/dist/task/command-watchdog.d.ts +9 -6
  99. package/dist/task/command-watchdog.js +21 -15
  100. package/dist/task/context-attribution.d.ts +34 -26
  101. package/dist/task/context-attribution.js +34 -26
  102. package/dist/task/context-silence.d.ts +39 -29
  103. package/dist/task/context-silence.js +35 -25
  104. package/dist/task/context-usage.d.ts +16 -9
  105. package/dist/task/context-usage.js +16 -9
  106. package/dist/task/contracts.d.ts +8 -4
  107. package/dist/task/contracts.js +25 -17
  108. package/dist/task/coverage-loop.d.ts +22 -18
  109. package/dist/task/coverage-loop.js +35 -30
  110. package/dist/task/critique-probes.d.ts +13 -14
  111. package/dist/task/critique-probes.js +50 -39
  112. package/dist/task/debug-log.d.ts +13 -5
  113. package/dist/task/debug-log.js +32 -20
  114. package/dist/task/decompose-fidelity.d.ts +11 -9
  115. package/dist/task/decompose-fidelity.js +38 -33
  116. package/dist/task/decompose-granularity.d.ts +41 -38
  117. package/dist/task/decompose-granularity.js +41 -38
  118. package/dist/task/deep-render-check.d.ts +22 -14
  119. package/dist/task/deep-render-check.js +40 -31
  120. package/dist/task/dropped-input.d.ts +12 -7
  121. package/dist/task/dropped-input.js +5 -2
  122. package/dist/task/enforce-attribution.d.ts +38 -47
  123. package/dist/task/enforce-attribution.js +46 -52
  124. package/dist/task/enforce-guidelines.d.ts +31 -20
  125. package/dist/task/enforce-guidelines.js +32 -21
  126. package/dist/task/enrichment.d.ts +7 -2
  127. package/dist/task/enrichment.js +26 -14
  128. package/dist/task/env-notes.d.ts +16 -7
  129. package/dist/task/env-notes.js +48 -31
  130. package/dist/task/env-template-closure.d.ts +4 -4
  131. package/dist/task/env-template-closure.js +42 -34
  132. package/dist/task/external-context.d.ts +28 -21
  133. package/dist/task/external-context.js +17 -12
  134. package/dist/task/failure-classifier.d.ts +4 -5
  135. package/dist/task/failure-classifier.js +30 -8
  136. package/dist/task/file-inventory.d.ts +15 -11
  137. package/dist/task/file-inventory.js +25 -22
  138. package/dist/task/final-gate-fix.d.ts +74 -86
  139. package/dist/task/final-gate-fix.js +97 -116
  140. package/dist/task/final-gate-progress.d.ts +29 -46
  141. package/dist/task/final-gate-progress.js +40 -51
  142. package/dist/task/final-gate.d.ts +64 -97
  143. package/dist/task/final-gate.js +192 -199
  144. package/dist/task/fix-child.d.ts +21 -27
  145. package/dist/task/fix-child.js +21 -27
  146. package/dist/task/foreign-path.d.ts +6 -5
  147. package/dist/task/foreign-path.js +0 -0
  148. package/dist/task/frozen-conflict.d.ts +9 -10
  149. package/dist/task/frozen-conflict.js +61 -64
  150. package/dist/task/frozen-path-guard.d.ts +35 -14
  151. package/dist/task/frozen-path-guard.js +56 -39
  152. package/dist/task/gate-child.d.ts +27 -28
  153. package/dist/task/gate-child.js +36 -35
  154. package/dist/task/gate-deps.d.ts +34 -27
  155. package/dist/task/gate-deps.js +169 -159
  156. package/dist/task/gate-tally.d.ts +77 -80
  157. package/dist/task/gate-tally.js +65 -68
  158. package/dist/task/git-state-guard.d.ts +15 -11
  159. package/dist/task/git-state-guard.js +76 -66
  160. package/dist/task/impl-widget.d.ts +25 -16
  161. package/dist/task/impl-widget.js +27 -17
  162. package/dist/task/implementation-guards.d.ts +26 -0
  163. package/dist/task/implementation-guards.js +177 -0
  164. package/dist/task/implementation-thinking.d.ts +33 -31
  165. package/dist/task/implementation-thinking.js +5 -6
  166. package/dist/task/implementation-turn.d.ts +39 -31
  167. package/dist/task/implementation-turn.js +41 -28
  168. package/dist/task/inline-markdown.d.ts +20 -7
  169. package/dist/task/inline-markdown.js +15 -6
  170. package/dist/task/launch-config-gap.js +25 -39
  171. package/dist/task/launch-contract.d.ts +18 -21
  172. package/dist/task/launch-contract.js +28 -30
  173. package/dist/task/launch-manifest.d.ts +6 -2
  174. package/dist/task/launch-manifest.js +35 -34
  175. package/dist/task/ledger.js +16 -14
  176. package/dist/task/lint-fix.d.ts +6 -8
  177. package/dist/task/lint-fix.js +67 -69
  178. package/dist/task/loop-detector.d.ts +27 -8
  179. package/dist/task/loop-detector.js +38 -14
  180. package/dist/task/mid-run-input.d.ts +17 -15
  181. package/dist/task/mid-run-input.js +17 -15
  182. package/dist/task/orchestrator.d.ts +24 -28
  183. package/dist/task/orchestrator.js +89 -66
  184. package/dist/task/orientation.d.ts +18 -23
  185. package/dist/task/orientation.js +24 -31
  186. package/dist/task/owned-freeze-conflict.d.ts +21 -20
  187. package/dist/task/owned-freeze-conflict.js +52 -85
  188. package/dist/task/owned-freeze-reassign.d.ts +40 -60
  189. package/dist/task/owned-freeze-reassign.js +41 -61
  190. package/dist/task/parsers.d.ts +4 -2
  191. package/dist/task/parsers.js +4 -4
  192. package/dist/task/phases.d.ts +41 -48
  193. package/dist/task/phases.js +196 -252
  194. package/dist/task/plan-io.d.ts +6 -7
  195. package/dist/task/plan-io.js +6 -7
  196. package/dist/task/plan-orchestrator.d.ts +10 -8
  197. package/dist/task/plan-orchestrator.js +14 -10
  198. package/dist/task/plan-prompts.d.ts +6 -5
  199. package/dist/task/plan-prompts.js +6 -5
  200. package/dist/task/plan-readonly.d.ts +4 -5
  201. package/dist/task/plan-readonly.js +4 -5
  202. package/dist/task/plan-rounds.d.ts +17 -29
  203. package/dist/task/plan-rounds.js +21 -34
  204. package/dist/task/plan-session.d.ts +58 -72
  205. package/dist/task/plan-session.js +61 -83
  206. package/dist/task/probe-gaming.d.ts +28 -27
  207. package/dist/task/probe-gaming.js +0 -0
  208. package/dist/task/prohibition-probe.d.ts +14 -16
  209. package/dist/task/prompts.d.ts +3 -4
  210. package/dist/task/prompts.js +17 -26
  211. package/dist/task/qa-transcript.d.ts +15 -22
  212. package/dist/task/qa-transcript.js +15 -21
  213. package/dist/task/question-box.d.ts +17 -13
  214. package/dist/task/question-box.js +19 -15
  215. package/dist/task/question-dedup.d.ts +6 -7
  216. package/dist/task/question-dedup.js +13 -14
  217. package/dist/task/question-dialog.d.ts +22 -32
  218. package/dist/task/question-dialog.js +22 -32
  219. package/dist/task/question-source.d.ts +18 -44
  220. package/dist/task/question-source.js +22 -51
  221. package/dist/task/refuted-constraint.d.ts +11 -31
  222. package/dist/task/refuted-constraint.js +27 -51
  223. package/dist/task/regenerable-artifacts.d.ts +12 -31
  224. package/dist/task/regenerable-artifacts.js +12 -31
  225. package/dist/task/render-check.d.ts +11 -22
  226. package/dist/task/render-check.js +33 -46
  227. package/dist/task/repo-health-check.d.ts +10 -14
  228. package/dist/task/repo-health-check.js +17 -23
  229. package/dist/task/requirements.d.ts +38 -71
  230. package/dist/task/requirements.js +78 -126
  231. package/dist/task/research-fanout-budget.d.ts +51 -88
  232. package/dist/task/research-fanout-budget.js +51 -88
  233. package/dist/task/research-worker.d.ts +29 -39
  234. package/dist/task/research-worker.js +37 -61
  235. package/dist/task/resume-gap.d.ts +14 -15
  236. package/dist/task/root-cause-repair.d.ts +9 -9
  237. package/dist/task/root-cause-repair.js +28 -40
  238. package/dist/task/run-bracket.d.ts +10 -13
  239. package/dist/task/run-end.d.ts +12 -22
  240. package/dist/task/run-end.js +8 -16
  241. package/dist/task/run-final-gate.d.ts +19 -21
  242. package/dist/task/run-final-gate.js +62 -80
  243. package/dist/task/runner-globs.d.ts +12 -13
  244. package/dist/task/runner-globs.js +12 -13
  245. package/dist/task/runner-resolve.d.ts +9 -9
  246. package/dist/task/runner-resolve.js +22 -23
  247. package/dist/task/script-escape.d.ts +10 -12
  248. package/dist/task/script-escape.js +13 -14
  249. package/dist/task/serve-entry.d.ts +1 -1
  250. package/dist/task/serve-entry.js +22 -25
  251. package/dist/task/service-blocks.js +4 -2
  252. package/dist/task/shipped-source.d.ts +11 -29
  253. package/dist/task/shipped-source.js +11 -29
  254. package/dist/task/skip-escape.js +10 -14
  255. package/dist/task/spec-urls.d.ts +26 -65
  256. package/dist/task/spec-urls.js +26 -65
  257. package/dist/task/spec-validation.d.ts +17 -20
  258. package/dist/task/spec-validation.js +17 -20
  259. package/dist/task/stall-detector.d.ts +23 -30
  260. package/dist/task/stall-detector.js +23 -30
  261. package/dist/task/stream-watchdog.d.ts +14 -12
  262. package/dist/task/stream-watchdog.js +14 -12
  263. package/dist/task/substitution-probe.d.ts +17 -20
  264. package/dist/task/substitution-probe.js +17 -20
  265. package/dist/task/task-gates.d.ts +36 -41
  266. package/dist/task/task-gates.js +95 -106
  267. package/dist/task/task-io.d.ts +4 -4
  268. package/dist/task/task-io.js +4 -4
  269. package/dist/task/task-parsers.js +4 -3
  270. package/dist/task/task-provenance.d.ts +2 -2
  271. package/dist/task/task-provenance.js +11 -13
  272. package/dist/task/task-types.d.ts +4 -3
  273. package/dist/task/terminal-outcome.d.ts +14 -16
  274. package/dist/task/terminal-outcome.js +12 -14
  275. package/dist/task/test-assembly.d.ts +13 -20
  276. package/dist/task/test-assembly.js +13 -20
  277. package/dist/task/timings.d.ts +5 -3
  278. package/dist/task/timings.js +5 -3
  279. package/dist/task/title-label.d.ts +9 -4
  280. package/dist/task/title-label.js +9 -4
  281. package/dist/task/type-only-answer.d.ts +44 -52
  282. package/dist/task/type-only-answer.js +44 -52
  283. package/dist/task/unfailable-command.d.ts +18 -24
  284. package/dist/task/unfailable-command.js +21 -27
  285. package/dist/task/unknown-routing.d.ts +10 -4
  286. package/dist/task/unknown-routing.js +10 -4
  287. package/dist/task/user-directives.d.ts +5 -8
  288. package/dist/task/user-directives.js +5 -8
  289. package/dist/task/verify-quality.d.ts +18 -22
  290. package/dist/task/verify-quality.js +45 -46
  291. package/dist/task/verify-reconcile.d.ts +15 -10
  292. package/dist/task/verify-reconcile.js +45 -43
  293. package/dist/task/verify-resolution.d.ts +24 -20
  294. package/dist/task/verify-resolution.js +51 -50
  295. package/dist/task/verify-work.d.ts +59 -66
  296. package/dist/task/verify-work.js +101 -138
  297. package/dist/task/widget.d.ts +15 -14
  298. package/dist/task/widget.js +22 -17
  299. package/dist/task/wiring-claims.d.ts +25 -32
  300. package/dist/task/wiring-claims.js +30 -35
  301. package/dist/task/write-guard.d.ts +39 -39
  302. package/dist/task/write-guard.js +48 -51
  303. package/dist/task/yolo.d.ts +34 -30
  304. package/dist/task/yolo.js +42 -37
  305. package/dist/workers/abstention.d.ts +21 -41
  306. package/dist/workers/abstention.js +27 -48
  307. package/dist/workers/brave-search.d.ts +4 -3
  308. package/dist/workers/brave-search.js +5 -2
  309. package/dist/workers/brave-warning.d.ts +7 -4
  310. package/dist/workers/brave-warning.js +19 -7
  311. package/dist/workers/ddg-search.d.ts +6 -6
  312. package/dist/workers/ddg-search.js +18 -12
  313. package/dist/workers/docs-cache.js +5 -2
  314. package/dist/workers/docs-chunk.d.ts +30 -37
  315. package/dist/workers/docs-chunk.js +37 -41
  316. package/dist/workers/docs-core.d.ts +28 -44
  317. package/dist/workers/docs-core.js +25 -44
  318. package/dist/workers/docs-index.js +4 -3
  319. package/dist/workers/docs-lookup.d.ts +15 -22
  320. package/dist/workers/docs-lookup.js +12 -21
  321. package/dist/workers/docs-project.d.ts +15 -9
  322. package/dist/workers/docs-project.js +17 -10
  323. package/dist/workers/docs-resolve.d.ts +19 -20
  324. package/dist/workers/docs-resolve.js +35 -32
  325. package/dist/workers/docs-retrieve.d.ts +5 -6
  326. package/dist/workers/docs-retrieve.js +18 -15
  327. package/dist/workers/exa-search.d.ts +9 -6
  328. package/dist/workers/exa-search.js +23 -12
  329. package/dist/workers/fetch-core.d.ts +13 -16
  330. package/dist/workers/fetch-core.js +23 -23
  331. package/dist/workers/focused-extractor.d.ts +13 -12
  332. package/dist/workers/focused-extractor.js +27 -19
  333. package/dist/workers/html-clean.js +24 -14
  334. package/dist/workers/http-request.d.ts +28 -20
  335. package/dist/workers/http-request.js +22 -17
  336. package/dist/workers/npm-version.d.ts +28 -11
  337. package/dist/workers/npm-version.js +24 -15
  338. package/dist/workers/phantom-imports.d.ts +15 -12
  339. package/dist/workers/phantom-imports.js +30 -24
  340. package/dist/workers/pi-worker-core.d.ts +65 -96
  341. package/dist/workers/pi-worker-core.js +93 -181
  342. package/dist/workers/pi-worker-docs.d.ts +24 -19
  343. package/dist/workers/pi-worker-docs.js +67 -76
  344. package/dist/workers/pi-worker-fetch.d.ts +7 -3
  345. package/dist/workers/pi-worker-fetch.js +27 -19
  346. package/dist/workers/pi-worker-search.js +12 -8
  347. package/dist/workers/pi-worker.d.ts +9 -4
  348. package/dist/workers/pi-worker.js +21 -14
  349. package/dist/workers/reasoning-warning.d.ts +18 -17
  350. package/dist/workers/reasoning-warning.js +22 -20
  351. package/dist/workers/research-cache.js +50 -78
  352. package/dist/workers/search-core.js +7 -5
  353. package/dist/workers/search-types.d.ts +10 -9
  354. package/dist/workers/search-types.js +9 -8
  355. package/dist/workers/session-hint.d.ts +13 -14
  356. package/dist/workers/session-hint.js +8 -9
  357. package/dist/workers/shared.d.ts +21 -25
  358. package/dist/workers/shared.js +0 -0
  359. package/dist/workers/single-read-extension.d.ts +14 -7
  360. package/dist/workers/single-read-extension.js +14 -7
  361. package/dist/workers/single-read-guard.d.ts +27 -30
  362. package/dist/workers/single-read-guard.js +36 -36
  363. package/dist/workers/typeonly-log.d.ts +12 -9
  364. package/dist/workers/typeonly-log.js +29 -33
  365. package/dist/workers/worker-channels.d.ts +15 -23
  366. package/dist/workers/worker-channels.js +15 -23
  367. package/dist/workers/worker-failure.d.ts +38 -46
  368. package/dist/workers/worker-failure.js +31 -39
  369. package/dist/workers/worker-kill.d.ts +25 -26
  370. package/dist/workers/worker-kill.js +16 -19
  371. package/dist/workers/worker-profiles.d.ts +54 -56
  372. package/dist/workers/worker-profiles.js +63 -39
  373. package/package.json +10 -8
@@ -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,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 { Text } from '@earendil-works/pi-tui';
10
15
  import { Type } from '@sinclair/typebox';
@@ -41,24 +46,26 @@ export function registerPiWorker(pi) {
41
46
  + '- The task needs the web — use `pi-worker-search` / `pi-worker-fetch`',
42
47
  parameters: WorkerParams,
43
48
  async run(params, signal, ctx) {
44
- // Grouped with `research`: this is the same read-only exploration
45
- // loop the four research workers run, just dispatched by a model
46
- // rather than by the pipeline. Left ungrouped it would be the one
47
- // 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.
48
54
  const result = await runWorker({
49
55
  prompt: params.prompt,
50
56
  cwd: ctx.cwd,
51
57
  signal,
52
58
  profile: 'adhoc',
53
- // The user's own `stuck reply retry` is what bounds this worker
54
- // 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
55
62
  // not policy for the same reason the gate's two ceilings are: the
56
63
  // number is the user's, the decision to arm it is the profile's.
57
64
  policyInputs: { streamInactivityMs: getConfig().streamInactivityMs },
58
- // The session's own window, handed down. The child is spawned
59
- // without `-m`, so the parent's model IS the child's model and
60
- // its window is the honest one. Without this the churn rule
61
- // cannot fire — see RunWorkerInput.contextWindow.
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.
62
69
  contextWindow: getParentContextWindow(ctx) || 'unknown',
63
70
  thinking: groupThinkingArgs('research')
64
71
  });
@@ -2,22 +2,22 @@
2
2
  * One-line startup hint shown when /task-config asks for a thinking level the
3
3
  * connected model will not honour.
4
4
  *
5
- * WHY IT HAS TO EXIST. pi never says it ignored or downgraded a level. Measured
6
- * live with a proxy on the request body: a model with `reasoning: false` given
7
- * `--thinking medium` sends no reasoning field at all, and a model whose
8
- * `thinkingLevelMap` nulls `off` given `--thinking off` is clamped UP to
9
- * `medium` thinking stays on. Both are silent. A reasoning profile the user
10
- * set and the model erased is worse than no profile feature, because it looks
11
- * like it worked.
5
+ * WHY IT HAS TO EXIST. pi silently rewrites the level and says nothing. Its own
6
+ * `clampThinkingLevel` (pi-ai models.js) shows both halves: a model with
7
+ * `reasoning: false` supports only `["off"]`, so every requested level collapses
8
+ * there; and a level the model's `thinkingLevelMap` nulls is filtered out, after
9
+ * which the clamp scans UPWARD before downward so asking for `off` on a model
10
+ * that nulls `off` gets the next level that IS available, and thinking stays on.
11
+ * A reasoning profile the user set and the model erased is worse than no profile
12
+ * feature, because it looks like it worked.
12
13
  *
13
14
  * ANTI-NAG. This warns once per session and clears on the first keystroke, and
14
15
  * that is the whole mechanism — deliberately no "already warned about model X"
15
16
  * file. Such a record goes stale the moment models.json is edited, and would
16
17
  * suppress the warning at exactly the moment a `/model` switch made it true.
17
- * brave-warning.ts nags every session for a standing misconfiguration and that
18
- * is correct; this is the same class. The real anti-nag is `inherit`: with the
19
- * shipped all-`inherit` table, `reasoningMismatches` returns empty for every
20
- * model and nothing renders at all.
18
+ * brave-warning.ts nags every session for a standing misconfiguration and that is
19
+ * correct; this is the same class. Setting a group back to `inherit` is what
20
+ * silences it: an all-`inherit` table yields no mismatches for any model.
21
21
  */
22
22
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
23
23
  import { type GroupSetting, type ReasoningGroup } from '../config/reasoning.js';
@@ -28,18 +28,19 @@ import { type ChatTemplateCaps } from '../shared/model-endpoint.js';
28
28
  *
29
29
  * Names the MODEL it checked, because children carry no `-m` and resolve pi's
30
30
  * default model, which need not be the host session's — a warning that does not
31
- * say what it looked at cannot be acted on. Names at most two groups; the count
32
- * carries the rest, since a line long enough to list seven is a line nobody
33
- * reads.
31
+ * say what it looked at cannot be acted on. Names at most two groups and appends
32
+ * `(+N more)` only when there are more than two, since a line long enough to list
33
+ * every group is a line nobody reads. Null when nothing mismatched.
34
34
  */
35
35
  export declare function formatReasoningWarning(modelName: string, mismatches: readonly ReasoningMismatch[]): string | null;
36
36
  /**
37
37
  * The extra cause line, when the SERVER disagrees with models.json.
38
38
  *
39
39
  * This is the `/login llama.cpp` case and the only thing the host-side clamp
40
- * cannot see: pi's built-in llama.cpp provider hardcodes `reasoning: false`, so
41
- * a perfectly capable server is described to pi as having no reasoning at all.
42
- * Returns null whenever the two agree, or when there was nothing to compare.
40
+ * cannot see: pi ships llama.cpp as a built-in extension whose provider entry
41
+ * hardcodes `reasoning: false`, so a perfectly capable server is described to pi
42
+ * as having no reasoning at all. Returns null whenever the two agree, and null
43
+ * when the probe answered nothing, so an unreachable server adds no cause line.
43
44
  */
44
45
  export declare function formatCapabilityConflict(serverSupportsEffort: boolean | null, modelDeclaresReasoning: boolean): string | null;
45
46
  export declare function registerReasoningWarning(pi: ExtensionAPI,