@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
@@ -1,24 +1,19 @@
1
1
  import { getPiInvocation } from '../shared/pi-invocation.js';
2
2
  import { runChildDefault } from '../shared/child-process.js';
3
- import { CommandWatchdog, commandTimeoutHint, realTimerDeps } from '../shared/command-watchdog.js';
3
+ import { commandCeilingForAttempt, commandTimeoutHint, commandWatch } from '../shared/command-watchdog.js';
4
+ export { commandCeilingForAttempt } from '../shared/command-watchdog.js';
4
5
  import { isGroundingRetrieval as isGrounding, workerChannel } from './worker-channels.js';
5
6
  import { childBaseArgs } from '../shared/child-extensions.js';
6
- import { LoopDetector } from '../task/loop-detector.js';
7
+ import { LoopDetector, MAX_LOOP_RESTARTS } from '../task/loop-detector.js';
7
8
  import { StallDetector, formatStallHint } from '../task/stall-detector.js';
8
- import { MAX_LOOP_RESTARTS, formatLoopHint, isConnectionError, connectionRetryBackoffMs } from '../task/child-runner.js';
9
+ import { formatLoopHint, isConnectionError, connectionRetryBackoffMs } from '../task/child-runner.js';
9
10
  import { detectLeakedToolCall, leakedToolCallHint, MAX_LEAK_RETRIES } from '../shared/leaked-tool-call.js';
10
11
  import { discoverModelEndpoints, probeModelEndpoints } from '../shared/model-endpoint.js';
11
12
  import { streamStallHint } from '../shared/stream-watchdog.js';
12
13
  import { classifyWorkerFailure } from './worker-failure.js';
13
- import { CARRY_FORWARD_IDS } from './worker-kill.js';
14
+ import { CARRY_FORWARD_IDS, RESTART_ORDER } from './worker-kill.js';
14
15
  import { applyOverride, WORKER_PROFILES } from './worker-profiles.js';
15
- // `--mode json` makes pi emit structured events as they happen instead of
16
- // buffering the assistant text and flushing on exit. That matters for the
17
- // wait/work timing split: in text mode the first stdout chunk only arrives at
18
- // the very end, so onFirstByte fires moments before close and workMs is
19
- // effectively zero. With JSON events the first byte lands as soon as the
20
- // model starts producing — making waitMs the real queue/cold-start cost and
21
- // workMs the real generation+tool-call cost.
16
+ /** The tool whitelist a caller gets when it names none. */
22
17
  const DEFAULT_TOOLS = 'read,grep,find,ls';
23
18
  /**
24
19
  * The one place `'unknown'` becomes the 0 both consumers already treat as
@@ -33,18 +28,19 @@ function contextWindowTokens(cw) {
33
28
  * command could be cited from. `pi-worker-docs` (the primary), `read` and `grep`
34
29
  * (project source), and the web escalations `pi-worker-search`/`pi-worker-fetch`.
35
30
  *
36
- * `ls` and `find` are deliberately EXCLUDED: they return file/directory NAMES,
37
- * and APIS owns symbols by name only, never paths (RESEARCH_APIS_PROMPT). Bare
38
- * enumeration cannot verify a signature, so a worker that fabricates its section
39
- * from memory does not launder itself grounded by calling `ls` once. That
40
- * exclusion is the anti-gaming property of any gate built on this count: "one
41
- * trivial `ls` then fabricate the rest" leaves groundingRetrievalCount at 0.
31
+ * `ls` and `find` are deliberately EXCLUDED: they return file and directory
32
+ * NAMES, and APIS owns symbols by name only, never paths (see
33
+ * RESEARCH_APIS_PROMPT in prompts.ts). Bare enumeration cannot verify a
34
+ * signature, so a worker that fabricates its section from memory cannot launder
35
+ * itself grounded by calling `ls` once "one trivial `ls` then fabricate the
36
+ * rest" still leaves groundingRetrievalCount at 0.
37
+ *
38
+ * The set is DERIVED from WORKER_CHANNELS (worker-channels.ts), not hand-kept.
39
+ * Re-exported here only so worker-channels.test.ts can assert this module hands
40
+ * back the same predicate.
42
41
  */
43
- // The grounding set is derived from WORKER_CHANNELS (worker-channels.ts), not
44
- // hand-kept — this was a second copy of the four tool names. Re-exported because
45
- // several call sites and tests import it from here.
46
42
  export { isGroundingRetrieval } from './worker-channels.js';
47
- // RESEARCH_WORKER_TIMEOUT_MS and STALL_AFTER_MS live on the profile table now
43
+ // RESEARCH_WORKER_TIMEOUT_MS and STALL_AFTER_MS live on the profile table
48
44
  // (worker-profiles.ts): they are the default VALUES of two guard rows, and a
49
45
  // default that lives apart from the table stating it is a second place to look.
50
46
  /**
@@ -59,49 +55,44 @@ const WORKER_TIMEOUT_HINT = '[SYSTEM NOTE: Your previous attempt ran out of time
59
55
  /**
60
56
  * How much of a discarded attempt's answer is carried into the next one.
61
57
  *
62
- * A restart used to hand the re-spawn nothing but a hint — which is why
63
- * WORKER_TIMEOUT_HINT above can tell a worker "do not re-explore ground you have
64
- * already covered" while giving it no record of what that ground was. It could
65
- * not comply. mx5 run 18 shows the cost: on tasks with >=46 project-source
66
- * lookups, 5 of 5 workers burned the FULL restart budget, because every attempt
67
- * re-read the same files against the same clock and died in the same place.
58
+ * A restart that hands the re-spawn nothing but a hint is why WORKER_TIMEOUT_HINT
59
+ * above can tell a worker "do not re-explore ground you have already covered"
60
+ * while giving it no record of what that ground was. It cannot comply, so it
61
+ * re-reads the same files against the same clock and dies in the same place.
68
62
  *
69
- * Carrying the partial answer forward is what makes a restart converge instead
70
- * of repeat. The risk it takes is real and is the thing the A/B measures: a
71
- * half-written or speculative entry, replayed under "already established", is
72
- * exactly how a fabrication gets laundered into a final answer. That is what the
73
- * ungrounded-symbol and anti-synthesis guards are pointed at, so the carry is
74
- * framed as findings to VERIFY-or-DROP rather than as settled fact.
63
+ * Carrying the partial forward is what lets a restart converge instead of repeat.
64
+ * The risk is real: a half-written or speculative entry, replayed under "already
65
+ * established", is how a fabrication gets laundered into a final answer. That is
66
+ * why `formatCarryForward` frames it as findings to VERIFY-or-DROP rather than as
67
+ * settled fact.
75
68
  */
76
69
  const CARRY_FORWARD_LIMIT = 24_000;
77
70
  /**
78
71
  * Restart reasons whose partial output is worth keeping.
79
72
  *
80
- * A clock kill (`worker-timeout`), a hung tool (`command-timeout`), an idle
81
- * stream (`stream-stall`) and a dropped socket (`connection-error`) all discard
82
- * work the model genuinely did. A loop kill and a leaked tool call do not — the
83
- * first is by definition the same call repeated, the second is malformed
84
- * protocol text, and replaying either would feed the failure back to itself.
73
+ * Exactly four, derived from WORKER_KILLS: `command-timeout`, `stream-stall`,
74
+ * `worker-timeout` and `connection-error` all discard work the model genuinely
75
+ * did. A loop kill and a leaked tool call do not — the first is by definition the
76
+ * same call repeated, the second is malformed protocol text, and replaying either
77
+ * would feed the failure back to itself.
85
78
  */
86
79
  const CARRY_FORWARD_REASONS = CARRY_FORWARD_IDS;
87
80
  /**
88
81
  * Does this partial output carry ANSWER CONTENT, or is it the model clearing its
89
82
  * throat?
90
83
  *
91
- * Salvage originally kept the LONGEST partial, which is not the same question. On
92
- * the live carry arm, TASK_0020 and TASK_0021 both timed out on all three
93
- * attempts and salvage shipped this as the section:
84
+ * Keeping the LONGEST partial is not the same question, and it has an obvious
85
+ * failure: a preamble sentence like
94
86
  *
95
87
  * "Now let me get more details on the specific APIs and components I need:"
96
88
  *
97
- * — a preamble sentence, which beats an empty string on length and carries
98
- * nothing. Both trials scored 2 entries and DEGRADED, against 22 and 5 for the
99
- * same fixtures in baseline.
89
+ * beats an empty string on length and carries nothing.
100
90
  *
101
91
  * A research worker's answer is a list of lines that each name something and
102
- * describe it. The test is therefore structural, not lexical: at least two lines
103
- * that look like entries — a name, then a gap, then a description. Prose wraps
104
- * at no particular column and does not repeat that shape.
92
+ * describe it. The test is therefore structural, not lexical: at least TWO lines
93
+ * that look like entries — a name, then a gap, then a description. Prose wraps at
94
+ * no particular column and does not repeat that shape; the sentence above scores
95
+ * zero entry lines.
105
96
  */
106
97
  export function hasAnswerContent(text) {
107
98
  return text.split('\n').filter(isEntryLine).length >= 2;
@@ -109,13 +100,15 @@ export function hasAnswerContent(text) {
109
100
  /**
110
101
  * Is ONE line an entry — a name, a gap, then a description — rather than prose?
111
102
  *
112
- * Split out of `hasAnswerContent` so the same rule can decide what a line IS,
113
- * not just how many of them there are. A FILES section's paths are read back
114
- * with it, and a scorer that used its own idea of an entry counted a preamble
115
- * sentence and a leaked `</tool_call>` as invented paths.
103
+ * Split out of `hasAnswerContent` so the same rule can decide what a line IS, not
104
+ * just how many of them there are a reader of a FILES section needs the same
105
+ * test, and its own idea of an entry would count a preamble sentence or a leaked
106
+ * `</tool_call>` as one.
116
107
  *
117
- * Prose wraps at no particular column, so it carries no two-space gap and no
118
- * spaced dash; when it does, it ends in `.` or `:` and an entry does not.
108
+ * A leading `-`, `*`, `•` or `1.`/`1)` bullet is stripped first. What remains must
109
+ * hold a two-space gap or a spaced dash and must NOT end in `.` or `:`. Prose
110
+ * wraps at no particular column, so it carries neither; when it does carry one, it
111
+ * ends in punctuation and an entry does not.
119
112
  */
120
113
  export function isEntryLine(raw) {
121
114
  const l = raw.replace(/^\s*(?:[-*•]|\d+[.)])\s+/, '').trim();
@@ -183,10 +176,10 @@ absoluteCeilingMs) {
183
176
  return {
184
177
  signal: ctrl.signal,
185
178
  timedOut: () => timedOut,
186
- // SCALE arm of nexttask 5B, inert unless a caller calls it: push the
187
- // deadline out, never past `started + ceilingMs`. A disabled timeout
188
- // (nothing armed) stays disabled — extending "never" is meaningless — and
189
- // an already-fired timer is not resurrected.
179
+ // Push the deadline out, never past `started + ceilingMs`. Inert unless a
180
+ // caller calls it. A disabled timeout (nothing armed) stays disabled
181
+ // extending "never" is meaningless — and an already-fired timer is not
182
+ // resurrected.
190
183
  extend: (byMs, ceilingMs) => {
191
184
  if (!armed || timedOut || ctrl.signal.aborted)
192
185
  return;
@@ -224,33 +217,6 @@ absoluteCeilingMs) {
224
217
  }
225
218
  };
226
219
  }
227
- /**
228
- * The per-command ceiling for attempt N, halving each time a hang recurs.
229
- *
230
- * The first attempt gets the full configured ceiling — a genuinely slow build or
231
- * test suite deserves it. But every hang-caused restart carries
232
- * commandTimeoutHint, which tells the model in as many words to bound its
233
- * command; a SECOND hang means it ignored an explicit instruction, and a third
234
- * means it ignored it twice. Giving a non-complying child the full ceiling again
235
- * would put the worst case at 3 × 15 min = 45 minutes of dead time, resting
236
- * entirely on the model obeying prose. Halving bounds it at ~26 min while
237
- * costing a complying child nothing.
238
- *
239
- * `priorHangs` counts watchdog kills specifically, NOT total restarts — the
240
- * restart budget is shared with loop kills, and a child restarted for LOOPING
241
- * never received the bound-your-command hint, so its first hang still deserves
242
- * the full ceiling. Only a hang after a hang is defiance.
243
- *
244
- * Floored at 30s so repeated halving cannot shrink the ceiling to something no
245
- * real command could finish inside — but never ABOVE the configured ceiling
246
- * itself, or a caller asking for 10s would silently get 30.
247
- */
248
- export function commandCeilingForAttempt(baseMs, priorHangs) {
249
- if (!(baseMs > 0))
250
- return 0;
251
- const floor = Math.min(baseMs, 30_000);
252
- return Math.max(floor, Math.round(baseMs / 2 ** priorHangs));
253
- }
254
220
  /**
255
221
  * The restart ladder, in precedence order. FIRST MATCH WINS.
256
222
  *
@@ -323,8 +289,8 @@ export const RESTART_RULES = [
323
289
  // a loop also tripped — the loop hint above is more specific.
324
290
  reason: 'worker-timeout',
325
291
  detect: s => s.timedOut && !s.loopHit && s.restartBudgetSpent < MAX_LOOP_RESTARTS ?
326
- // The EFFECTIVE cap, which the SCALE arm moves reporting the
327
- // configured one would misname why this attempt died.
292
+ // The EFFECTIVE cap, which `extend`/`progress` can have moved
293
+ // reporting the configured one would misname why this attempt died.
328
294
  { detail: `cap ${s.effectiveCapMs}ms` }
329
295
  : null,
330
296
  hint: () => WORKER_TIMEOUT_HINT,
@@ -332,22 +298,15 @@ export const RESTART_RULES = [
332
298
  },
333
299
  {
334
300
  // A connection-class model error is restartable on the same budget, exactly
335
- // as runPhaseChild already treats it a research worker had no such
336
- // retry, so one dropped fetch failed the whole task at research while the
337
- // identical blip in refine/compose was absorbed.
301
+ // as runPhaseChild already treats it. Without it one dropped socket fails
302
+ // the whole task at research, while the identical blip in refine or compose
303
+ // is absorbed.
338
304
  //
339
- // What this can and cannot buy, measured (flaky proxy in front of the local
340
- // llama-server, dropping every connection for a fixed outage window): pi
341
- // retries a failed turn itself, 4 attempts over ~15s, and a run that
342
- // recovers no longer reports modelError at all (see JsonEventSink). So a
343
- // surfaced connection error means pi's own ~15s budget is already spent, and
344
- // a re-spawn only helps when the outage outlasts it. It does: at a 20s
345
- // outage the baseline never recovered and this policy always did, 0/8 → 8/8
346
- // (Fisher p=0.00016), and the same at 35s. Below ~15s pi absorbs it alone —
347
- // 8/8 both arms, so the retry neither helps nor costs there. Beyond ~46s
348
- // (three spawns' combined budget) both arms fail. The price is paid only on
349
- // a backend that is really gone: time-to-report goes ~15s → ~46s. Re-run:
350
- // scripts/connection-retry-ab.ts.
305
+ // pi retries a failed turn itself before reporting anything, and a run that
306
+ // recovers reports no modelError at all (see JsonEventSink). So a SURFACED
307
+ // connection error means pi's own budget is already spent, and a re-spawn
308
+ // only helps when the outage outlasts it. The price is paid only on a
309
+ // backend that is really gone: time-to-report grows by the extra spawns.
351
310
  //
352
311
  // Connection class ONLY. Auth, bad request and context overflow still fail
353
312
  // fast: re-issuing the same request cannot fix them, so spending the budget
@@ -374,60 +333,15 @@ export const RESTART_RULES = [
374
333
  counters: { leak: true }
375
334
  }
376
335
  ];
377
- /**
378
- * Build the child-side command watchdog for ONE attempt: a per-tool-call timer
379
- * machine (shared with the main session) whose `onFire` aborts `signal`, which
380
- * runChild turns into a process-GROUP kill — reaping the hung command itself,
381
- * not just the pi child holding it.
382
- *
383
- * LIMIT: the group kill only reaches processes still IN the group. A hung
384
- * command that detached a daemon (setsid/nohup dev server) leaves it running —
385
- * the fresh attempt can then hit a port the dead attempt's escapee still holds
386
- * (the run-9 orphan-dev-server → false-EADDRINUSE shape). No cheap fix from
387
- * here; the restart hint's "check current state" line is the mitigation.
388
- *
389
- * Returns null when the watchdog is off, so the caller keeps the plain timeout
390
- * signal and no per-call bookkeeping happens at all.
391
- */
392
- function commandWatch(timeoutMs) {
393
- if (!(timeoutMs > 0))
394
- return null;
395
- const ctrl = new AbortController();
396
- // pi's toolCallId pairs start↔end. When it is absent (a fake stream in a
397
- // test, an older pi), fall back to one shared slot: tool executions in a
398
- // child are sequential, so a single slot is still correctly paired.
399
- const key = (id) => id ?? 'anon';
400
- const details = new Map();
401
- let killed;
402
- const watchdog = new CommandWatchdog({
403
- getTimeoutMs: () => timeoutMs,
404
- ...realTimerDeps,
405
- onFire: (toolCallId, toolName, ms) => {
406
- killed = {
407
- toolName,
408
- timeoutMs: ms,
409
- ...(details.has(toolCallId) ? { detail: details.get(toolCallId) } : {})
410
- };
411
- ctrl.abort();
412
- }
413
- });
414
- return {
415
- onStart: call => {
416
- const id = key(call.toolCallId);
417
- const args = call.args;
418
- if (typeof args?.command === 'string') {
419
- details.set(id, args.command.slice(0, 120));
420
- }
421
- watchdog.onStart(id, call.name);
422
- },
423
- onEnd: id => watchdog.onEnd(key(id)),
424
- killed: () => killed,
425
- signal: ctrl.signal,
426
- clear: () => watchdog.clearAll()
427
- };
428
- }
429
336
  export async function runWorker(input) {
430
337
  const tools = input.tools ?? DEFAULT_TOOLS;
338
+ // `--mode json` makes pi emit structured events as they happen instead of
339
+ // buffering the assistant text and flushing on exit. Its print-mode source
340
+ // shows both halves: under `json` a session subscriber writes every event to
341
+ // stdout as it arrives, while under `text` NOTHING is written until after the
342
+ // prompt resolves, when the last assistant message is printed once. That is
343
+ // what makes the wait/work split real — onFirstByte would otherwise fire
344
+ // moments before close and leave workMs at nearly zero.
431
345
  const baseArgs = [
432
346
  ...childBaseArgs(input.extensions ?? []),
433
347
  ...(input.thinking ?? []),
@@ -475,11 +389,10 @@ export async function runWorker(input) {
475
389
  const salvage = { text: null };
476
390
  for (;;) {
477
391
  const carried = salvage.text === null ? null : formatCarryForward(salvage.text);
478
- // Announce the INJECTION, not just the restart. Without this, "the carry
479
- // reached the re-spawn" can only be inferred from entry countsand
480
- // inferring what a worker did from what it produced is the exact gap 5A
481
- // exists to close. The prompt goes to the child on stdin, so no log
482
- // downstream of here can show it.
392
+ // Announce the INJECTION, not just the restart. The prompt goes to the child
393
+ // on stdin, so no log downstream of here can show it without this hook
394
+ // "the carry reached the re-spawn" could only be inferred from the answer,
395
+ // which is inferring what a worker did from what it produced.
483
396
  if (carried !== null) {
484
397
  input.onCarryForward?.({
485
398
  attempt: restarts.length + 1,
@@ -506,8 +419,8 @@ export async function runWorker(input) {
506
419
  null
507
420
  : new StallDetector(guards.loop.progress.limit, guards.loop.progress.churnFactor);
508
421
  // Arm the churn rule BEFORE the first tool call. pi's stream carries no
509
- // context event (issue #16), so waiting for one leaves the rule
510
- // permanently disarmed. The parent knows the window at spawn time.
422
+ // context event at all, so waiting for one leaves the rule permanently
423
+ // disarmed. The parent knows the window at spawn time.
511
424
  stallDetector?.noteContext(contextWindowTokens(input.contextWindow));
512
425
  // Capture the hit the detector reports (it also returns it to the unified
513
426
  // runner, which kills the child on a hit). Without capturing it here the
@@ -546,8 +459,9 @@ export async function runWorker(input) {
546
459
  // A tool call is the worker working. Inert unless the
547
460
  // caller opted into a progress-based deadline.
548
461
  timeout.progress();
549
- // The generic child runner used to name ONE tool and ONE of
550
- // its parameters here. It asks the tool's own row now.
462
+ // Naming ONE tool and ONE of its parameters here would put
463
+ // that knowledge in the generic runner, so it asks the
464
+ // tool's own row in WORKER_CHANNELS instead.
551
465
  if (clock.fanout
552
466
  && workerChannel(call.name)?.isProjectSourceLookup?.(call.args ?? {}) === true) {
553
467
  timeout.extend(clock.fanout.perLookupMs, clock.fanout.ceilingMs);
@@ -570,11 +484,11 @@ export async function runWorker(input) {
570
484
  timeout.progress();
571
485
  input.onLine?.(line);
572
486
  },
573
- // Always wired now (it used to be conditional on the command
574
- // watchdog): the sink only emits tool_execution_end if a
575
- // handler exists, and a completed tool call is the clearest
576
- // progress signal there is. Without it a worker whose tool
577
- // calls all succeed would still look idle to the deadline.
487
+ // Always wired, never conditional on the command watchdog: the
488
+ // sink only emits a tool-execution-end if a handler exists, and
489
+ // a completed tool call is the clearest progress signal there
490
+ // is. Without it a worker whose tool calls all succeed would
491
+ // still look idle to the deadline.
578
492
  onToolResult: r => {
579
493
  timeout.progress();
580
494
  cmdWatch?.onEnd(r.toolCallId);
@@ -637,7 +551,7 @@ export async function runWorker(input) {
637
551
  // there would just mislabel the real failure.
638
552
  const leaked = result.exitCode === 0 && !result.aborted ? detectLeakedToolCall(text) : null;
639
553
  // THE RESTART LADDER. Precedence is RESTART_RULES' row order; this loop
640
- // owns the ritual every rule used to repeat: budget, hint, counters,
554
+ // owns the ritual every rule would otherwise repeat: budget, hint, counters,
641
555
  // record-and-announce, backoff, re-spawn.
642
556
  const state = {
643
557
  ...(loopHit ? { loopHit } : {}),
@@ -678,24 +592,22 @@ export async function runWorker(input) {
678
592
  }
679
593
  if (restarted)
680
594
  continue;
681
- // SALVAGE. The run used to return the LAST attempt's text unconditionally,
682
- // so a worker whose final attempt was killed early reported nothing at all
683
- // — even when a discarded attempt had produced a usable answer that was
684
- // still in hand at the moment it was thrown away. A restart budget is
685
- // meant to buy more chances at an answer, not to overwrite a good attempt
686
- // with a worse one.
595
+ // SALVAGE. Returning the LAST attempt's text unconditionally makes a
596
+ // worker whose final attempt was killed early report nothing at all — even
597
+ // when a discarded attempt produced a usable answer that was still in hand
598
+ // at the moment it was thrown away. A restart budget is meant to buy more
599
+ // chances at an answer, not to overwrite a good attempt with a worse one.
687
600
  //
688
601
  // Gated on the final attempt having FAILED, not on it being shorter. A
689
602
  // worker that finished cleanly has answered, and a short answer is a
690
603
  // legitimate answer — length would let a long half-finished fragment
691
604
  // override a concise correct one, which is the opposite of the fix.
692
- // ASK THE LADDER — do not restate it. This was an eight-term disjunction,
693
- // a fifth hand-written statement of the taxonomy `worker-failure.ts` exists
694
- // to own, and it had already drifted: `leakedToolCall` and a plain non-zero
695
- // `exitCode` are rows in FAILURE_RULES and were missing here. Both are cases
696
- // where an attempt that produced nothing usable counted as NOT failed, so
697
- // salvage was skipped and a good earlier partial was overwritten — the exact
698
- // outcome the comment above forbids.
605
+ // ASK THE LADDER — do not restate it. Hand-writing this test restates the
606
+ // taxonomy `worker-failure.ts` owns, and a restatement drifts: drop
607
+ // `leakedToolCall` or a plain non-zero `exitCode` from it and an attempt
608
+ // that produced nothing usable counts as NOT failed, so salvage is skipped
609
+ // and a good earlier partial is overwritten the outcome the comment above
610
+ // forbids.
699
611
  //
700
612
  // The two non-kill terms stay explicit because `worker-failure.ts`
701
613
  // deliberately excludes them as CONSUMER policy: an empty answer and a
@@ -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;