@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21

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 (359) hide show
  1. package/CHANGELOG.md +292 -0
  2. package/README.md +8 -11
  3. package/docs/prefix-cache-architecture.md +14 -0
  4. package/package.json +22 -24
  5. package/packages/ask-user/README.md +55 -107
  6. package/packages/ask-user/ask-ui.ts +306 -732
  7. package/packages/ask-user/clipboard.ts +50 -0
  8. package/packages/ask-user/config.ts +74 -78
  9. package/packages/ask-user/package.json +2 -2
  10. package/packages/ask-user/questions.ts +151 -0
  11. package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
  12. package/packages/ask-user/tools.ts +198 -432
  13. package/packages/autocomplete/package.json +1 -1
  14. package/packages/autocomplete/src/constants.ts +38 -43
  15. package/packages/autocomplete/src/sorting.ts +8 -0
  16. package/packages/background-tasks/package.json +2 -2
  17. package/packages/background-tasks/src/config.ts +0 -73
  18. package/packages/background-tasks/src/index.ts +20 -16
  19. package/packages/background-tasks/src/registry.ts +1 -171
  20. package/packages/background-tasks/src/types.ts +0 -32
  21. package/packages/btw/README.md +13 -56
  22. package/packages/btw/extensions/btw.ts +403 -1796
  23. package/packages/btw/package.json +5 -2
  24. package/packages/btw/skills/btw/SKILL.md +7 -143
  25. package/packages/compactor/README.md +56 -287
  26. package/packages/compactor/package.json +5 -9
  27. package/packages/compactor/skills/compactor/SKILL.md +13 -20
  28. package/packages/compactor/src/card.ts +90 -0
  29. package/packages/compactor/src/commands/index.ts +73 -130
  30. package/packages/compactor/src/compaction/compact-args.ts +5 -3
  31. package/packages/compactor/src/compaction/filter-noise.ts +1 -1
  32. package/packages/compactor/src/compaction/hooks.ts +487 -311
  33. package/packages/compactor/src/compaction/source.ts +178 -0
  34. package/packages/compactor/src/compaction/summarize.ts +586 -52
  35. package/packages/compactor/src/config/manager.ts +130 -127
  36. package/packages/compactor/src/config/schema.ts +32 -48
  37. package/packages/compactor/src/index.ts +40 -431
  38. package/packages/compactor/src/info-screen.ts +44 -190
  39. package/packages/compactor/src/session/recall-blocks.ts +1 -1
  40. package/packages/compactor/src/stats.ts +76 -0
  41. package/packages/compactor/src/tools/context-budget.ts +17 -34
  42. package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
  43. package/packages/compactor/src/tools/register.ts +29 -269
  44. package/packages/compactor/src/types.ts +47 -186
  45. package/packages/core/README.md +25 -0
  46. package/packages/core/child-agent.ts +1 -0
  47. package/packages/core/command-echo.ts +63 -0
  48. package/packages/core/compaction-context.ts +41 -0
  49. package/packages/core/constants.ts +8 -38
  50. package/packages/core/events.ts +0 -53
  51. package/packages/core/fusion-status.ts +4 -0
  52. package/packages/core/global.d.ts +10 -0
  53. package/packages/core/harness-messages.ts +392 -0
  54. package/packages/core/index.ts +14 -0
  55. package/packages/core/kanboard-status.ts +49 -0
  56. package/packages/core/long-horizon-owner-status.ts +46 -0
  57. package/packages/core/model-cache.ts +81 -7
  58. package/packages/core/package.json +6 -3
  59. package/packages/core/src/attach/detect.ts +163 -0
  60. package/packages/core/src/child-agent/child-args.ts +31 -0
  61. package/packages/core/src/child-agent/delivery.ts +41 -0
  62. package/packages/core/src/child-agent/index.ts +5 -0
  63. package/packages/core/src/child-agent/readers.ts +203 -0
  64. package/packages/core/src/child-agent/runtime.ts +584 -0
  65. package/packages/core/src/evidence.ts +78 -0
  66. package/packages/core/src/hints/browser.ts +278 -0
  67. package/packages/core/src/hints/crab-data.ts +19 -0
  68. package/packages/core/src/hints/index.ts +865 -0
  69. package/packages/core/src/hints/lines.ts +607 -0
  70. package/packages/core/src/hints/store.ts +120 -0
  71. package/packages/core/src/jev/chatter.ts +24 -0
  72. package/packages/core/src/jev/settings.ts +177 -14
  73. package/packages/core/src/package-colors.ts +2 -0
  74. package/packages/core/src/settings/catalog.ts +39 -15
  75. package/packages/core/src/settings/engine.ts +48 -2
  76. package/packages/core/src/settings/hub.ts +83 -20
  77. package/packages/core/src/settings/open.ts +49 -0
  78. package/packages/core/src/settings/schema.ts +27 -7
  79. package/packages/core/src/tui/hub-kit.ts +18 -5
  80. package/packages/core/src/tui/kit.ts +198 -0
  81. package/packages/core/src/tui/progress.ts +73 -0
  82. package/packages/core/src/turn/arbiter.ts +290 -0
  83. package/packages/footer/README.md +4 -6
  84. package/packages/footer/package.json +3 -3
  85. package/packages/footer/src/glance-editor.ts +19 -11
  86. package/packages/footer/src/help.ts +1 -1
  87. package/packages/footer/src/index.ts +36 -3
  88. package/packages/footer/src/presets.ts +3 -4
  89. package/packages/footer/src/rendering/icons.ts +0 -20
  90. package/packages/footer/src/segments/compactor.ts +52 -142
  91. package/packages/footer/src/segments/status-ext.ts +0 -1
  92. package/packages/fusion/README.md +14 -4
  93. package/packages/fusion/package.json +4 -3
  94. package/packages/fusion/src/child-args.ts +1 -0
  95. package/packages/fusion/src/index.ts +161 -8
  96. package/packages/fusion/src/pi-settings.ts +57 -0
  97. package/packages/fusion/src/picker.ts +186 -51
  98. package/packages/fusion/src/preset.ts +56 -2
  99. package/packages/fusion/src/prompts.ts +1 -1
  100. package/packages/fusion/src/sidekick-runtime.ts +1 -420
  101. package/packages/fusion/src/tools.ts +107 -128
  102. package/packages/fusion/src/transcript.ts +27 -85
  103. package/packages/info-screen/package.json +2 -2
  104. package/packages/input-shortcuts/README.md +18 -30
  105. package/packages/input-shortcuts/package.json +2 -2
  106. package/packages/input-shortcuts/src/burst.ts +88 -0
  107. package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
  108. package/packages/input-shortcuts/src/cleared-input.ts +97 -0
  109. package/packages/input-shortcuts/src/index.ts +99 -193
  110. package/packages/input-shortcuts/src/last-response.ts +40 -0
  111. package/packages/input-shortcuts/src/registers.ts +8 -23
  112. package/packages/input-shortcuts/src/types.ts +4 -16
  113. package/packages/input-shortcuts/src/undo-redo.ts +59 -47
  114. package/packages/kanboard/README.md +51 -47
  115. package/packages/kanboard/index.ts +293 -55
  116. package/packages/kanboard/package.json +8 -8
  117. package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
  118. package/packages/kanboard/src/badges.ts +93 -0
  119. package/packages/kanboard/src/bin.ts +4 -2
  120. package/packages/kanboard/src/commands.ts +218 -84
  121. package/packages/kanboard/src/debug.ts +20 -0
  122. package/packages/kanboard/src/guard.ts +267 -109
  123. package/packages/kanboard/src/monitor.ts +330 -0
  124. package/packages/kanboard/src/notice-buffer.ts +74 -0
  125. package/packages/kanboard/src/progress.ts +27 -0
  126. package/packages/kanboard/src/reminders.ts +234 -0
  127. package/packages/kanboard/src/settings.ts +69 -15
  128. package/packages/kanboard/src/shapes.ts +5 -0
  129. package/packages/long-horizon/index.ts +190 -13
  130. package/packages/long-horizon/package.json +3 -3
  131. package/packages/long-horizon/src/commands.ts +103 -7
  132. package/packages/long-horizon/src/compaction-brief.ts +35 -0
  133. package/packages/long-horizon/src/engine/continuation.ts +84 -15
  134. package/packages/long-horizon/src/engine/goal-state.ts +64 -13
  135. package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
  136. package/packages/long-horizon/src/engine/ralph.ts +3 -2
  137. package/packages/long-horizon/src/engine/runaway.ts +68 -11
  138. package/packages/long-horizon/src/engine/verifier.ts +19 -0
  139. package/packages/long-horizon/src/gate.ts +37 -12
  140. package/packages/long-horizon/src/judge/resolve.ts +1 -0
  141. package/packages/long-horizon/src/owner.ts +35 -2
  142. package/packages/long-horizon/src/progress.ts +87 -0
  143. package/packages/long-horizon/src/prompts/goal.ts +1 -1
  144. package/packages/long-horizon/src/runtime.ts +172 -18
  145. package/packages/long-horizon/src/settings.ts +54 -39
  146. package/packages/long-horizon/src/tools/goal.ts +6 -0
  147. package/packages/long-horizon/src/tools/graph.ts +1 -1
  148. package/packages/long-horizon/src/tools/swarm.ts +3 -3
  149. package/packages/mcp/package.json +2 -2
  150. package/packages/memory/commands.ts +7 -2
  151. package/packages/memory/index.ts +210 -32
  152. package/packages/memory/package.json +3 -3
  153. package/packages/memory/save-session.ts +264 -0
  154. package/packages/memory/settings.ts +17 -0
  155. package/packages/memory/tools.ts +253 -203
  156. package/packages/notify/package.json +2 -2
  157. package/packages/skill-registry/README.md +53 -16
  158. package/packages/skill-registry/index.ts +230 -0
  159. package/packages/skill-registry/package.json +19 -8
  160. package/packages/skill-registry/src/editor.ts +206 -0
  161. package/packages/skill-registry/src/judge.ts +289 -0
  162. package/packages/skill-registry/src/registry.ts +87 -0
  163. package/packages/skill-registry/src/settings.ts +259 -0
  164. package/packages/skill-registry/src/vault.ts +80 -0
  165. package/packages/subagents/README.md +39 -119
  166. package/packages/subagents/package.json +6 -13
  167. package/packages/subagents/skills/subagents/SKILL.md +18 -42
  168. package/packages/subagents/src/agents.ts +213 -0
  169. package/packages/subagents/src/cards.ts +182 -0
  170. package/packages/subagents/src/index.ts +609 -1084
  171. package/packages/subagents/src/manager.ts +429 -0
  172. package/packages/subagents/src/profiles.ts +130 -0
  173. package/packages/subagents/src/transcript.ts +154 -0
  174. package/packages/subagents/src/ui.ts +389 -0
  175. package/packages/unipi/bundled.js +39756 -45569
  176. package/packages/unipi/index.ts +55 -10
  177. package/packages/updater/package.json +2 -2
  178. package/packages/utility/README.md +76 -69
  179. package/packages/utility/package.json +2 -2
  180. package/packages/utility/skills/image/SKILL.md +37 -0
  181. package/packages/utility/skills/utility/SKILL.md +17 -60
  182. package/packages/utility/src/answer/extract.ts +128 -0
  183. package/packages/utility/src/answer/index.ts +213 -0
  184. package/packages/utility/src/answer/reply.ts +133 -0
  185. package/packages/utility/src/answer/web.ts +167 -0
  186. package/packages/utility/src/attach/index.ts +205 -0
  187. package/packages/utility/src/commands.ts +38 -142
  188. package/packages/utility/src/diagnostics/engine.ts +50 -5
  189. package/packages/utility/src/image/index.ts +33 -0
  190. package/packages/utility/src/image/models.ts +221 -0
  191. package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
  192. package/packages/utility/src/image/settings.ts +178 -0
  193. package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
  194. package/packages/utility/src/image/tools.ts +168 -0
  195. package/packages/utility/src/image/transports.ts +186 -0
  196. package/packages/utility/src/index.ts +68 -389
  197. package/packages/utility/src/lifecycle/cleanup.ts +116 -311
  198. package/packages/utility/src/rename/gate.ts +102 -0
  199. package/packages/utility/src/rename/index.ts +191 -0
  200. package/packages/utility/src/rename/session.ts +170 -0
  201. package/packages/utility/src/render/delegated.ts +285 -0
  202. package/packages/utility/src/render/harness.ts +683 -0
  203. package/packages/utility/src/render/parse.ts +156 -0
  204. package/packages/utility/src/render/pi-settings.ts +38 -0
  205. package/packages/utility/src/render/reply-bg.ts +307 -0
  206. package/packages/utility/src/render/simple.ts +724 -0
  207. package/packages/utility/src/render/spacing.ts +235 -0
  208. package/packages/utility/src/render/styled.ts +251 -0
  209. package/packages/utility/src/render/tools.ts +353 -0
  210. package/packages/utility/src/settings.ts +166 -233
  211. package/packages/utility/src/types.ts +1 -222
  212. package/packages/watchdog/index.ts +17 -6
  213. package/packages/watchdog/package.json +3 -3
  214. package/packages/watchdog/src/config.ts +3 -2
  215. package/packages/web-api/package.json +2 -2
  216. package/packages/workflow/README.md +3 -0
  217. package/packages/workflow/index.ts +0 -2
  218. package/packages/workflow/package.json +2 -2
  219. package/packages/workflow/src/permission/decide.ts +6 -4
  220. package/packages/workflow/src/permission/index.ts +17 -7
  221. package/packages/workflow/src/permission/settings.ts +39 -4
  222. package/packages/workflow/src/plan/index.ts +19 -4
  223. package/packages/background-tasks/extensions/delegate-child.ts +0 -1
  224. package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
  225. package/packages/background-tasks/src/context-token-budget.ts +0 -903
  226. package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
  227. package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
  228. package/packages/background-tasks/src/delegate/budget.ts +0 -415
  229. package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
  230. package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
  231. package/packages/background-tasks/src/delegate/launch.ts +0 -484
  232. package/packages/background-tasks/src/delegate/result-package.ts +0 -459
  233. package/packages/background-tasks/src/delegate/runner.ts +0 -430
  234. package/packages/background-tasks/src/delegate/seed.ts +0 -423
  235. package/packages/background-tasks/src/delegate/types.ts +0 -323
  236. package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
  237. package/packages/background-tasks/src/delegate-extension.ts +0 -654
  238. package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
  239. package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
  240. package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
  241. package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
  242. package/packages/compactor/src/compaction/build-sections.ts +0 -76
  243. package/packages/compactor/src/compaction/extract/goals.ts +0 -36
  244. package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
  245. package/packages/compactor/src/compaction/format.ts +0 -53
  246. package/packages/compactor/src/compaction/merge.ts +0 -143
  247. package/packages/compactor/src/config/presets.ts +0 -160
  248. package/packages/compactor/src/executor/executor.ts +0 -233
  249. package/packages/compactor/src/executor/runtime.ts +0 -115
  250. package/packages/compactor/src/security/evaluator.ts +0 -89
  251. package/packages/compactor/src/security/policy.ts +0 -96
  252. package/packages/compactor/src/security/scanner.ts +0 -65
  253. package/packages/compactor/src/session/auto-inject.ts +0 -60
  254. package/packages/compactor/src/session/db.ts +0 -325
  255. package/packages/compactor/src/session/extract.ts +0 -144
  256. package/packages/compactor/src/session/resume-inject.ts +0 -64
  257. package/packages/compactor/src/session/snapshot.ts +0 -327
  258. package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
  259. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
  260. package/packages/compactor/src/tools/ctx-execute.ts +0 -23
  261. package/packages/compactor/src/tools/ctx-stats.ts +0 -96
  262. package/packages/image/README.md +0 -99
  263. package/packages/image/package.json +0 -61
  264. package/packages/image/skills/image/SKILL.md +0 -76
  265. package/packages/image/src/generate.ts +0 -256
  266. package/packages/image/src/index.ts +0 -135
  267. package/packages/image/src/models.ts +0 -519
  268. package/packages/image/src/openai-images-api.ts +0 -282
  269. package/packages/image/src/register-providers.ts +0 -209
  270. package/packages/image/src/settings.ts +0 -173
  271. package/packages/image/src/tools.ts +0 -326
  272. package/packages/input-shortcuts/src/clipboard.ts +0 -119
  273. package/packages/kanboard/src/runner.ts +0 -817
  274. package/packages/skill-registry/skills/auto/SKILL.md +0 -465
  275. package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
  276. package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
  277. package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
  278. package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
  279. package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
  280. package/packages/skill-registry/skills/debug/SKILL.md +0 -228
  281. package/packages/skill-registry/skills/document/SKILL.md +0 -120
  282. package/packages/skill-registry/skills/fix/SKILL.md +0 -212
  283. package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
  284. package/packages/skill-registry/skills/plan/SKILL.md +0 -231
  285. package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
  286. package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
  287. package/packages/skill-registry/skills/research/SKILL.md +0 -251
  288. package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
  289. package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
  290. package/packages/skill-registry/skills/work/SKILL.md +0 -218
  291. package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
  292. package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
  293. package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
  294. package/packages/subagents/agents/delegate.md +0 -14
  295. package/packages/subagents/agents/oracle.md +0 -78
  296. package/packages/subagents/agents/researcher.md +0 -52
  297. package/packages/subagents/agents/reviewer.md +0 -80
  298. package/packages/subagents/agents/scout.md +0 -50
  299. package/packages/subagents/agents/worker.md +0 -59
  300. package/packages/subagents/prompts/council.md +0 -48
  301. package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
  302. package/packages/subagents/prompts/parallel-cleanup.md +0 -59
  303. package/packages/subagents/prompts/parallel-research.md +0 -50
  304. package/packages/subagents/prompts/parallel-review.md +0 -54
  305. package/packages/subagents/prompts/review-loop.md +0 -43
  306. package/packages/subagents/skills/council-mode/SKILL.md +0 -230
  307. package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
  308. package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
  309. package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
  310. package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
  311. package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
  312. package/packages/subagents/src/acceptance.ts +0 -331
  313. package/packages/subagents/src/agent-manager.ts +0 -405
  314. package/packages/subagents/src/agent-memory.ts +0 -300
  315. package/packages/subagents/src/agent-overrides.ts +0 -183
  316. package/packages/subagents/src/agent-runner.ts +0 -271
  317. package/packages/subagents/src/async-runner.ts +0 -422
  318. package/packages/subagents/src/authority-policy.ts +0 -47
  319. package/packages/subagents/src/budgets.ts +0 -285
  320. package/packages/subagents/src/child-safety.ts +0 -140
  321. package/packages/subagents/src/config.ts +0 -249
  322. package/packages/subagents/src/conversation-viewer.ts +0 -281
  323. package/packages/subagents/src/core-compat.ts +0 -94
  324. package/packages/subagents/src/custom-agents.ts +0 -343
  325. package/packages/subagents/src/file-system-retry.ts +0 -50
  326. package/packages/subagents/src/fleet-data.ts +0 -105
  327. package/packages/subagents/src/fleet-view.ts +0 -322
  328. package/packages/subagents/src/foreground-detach.ts +0 -59
  329. package/packages/subagents/src/fork-context.ts +0 -285
  330. package/packages/subagents/src/global.d.ts +0 -7
  331. package/packages/subagents/src/guide.ts +0 -129
  332. package/packages/subagents/src/mission-state.ts +0 -133
  333. package/packages/subagents/src/mission-store.ts +0 -436
  334. package/packages/subagents/src/output-limits.ts +0 -142
  335. package/packages/subagents/src/parity-types.ts +0 -315
  336. package/packages/subagents/src/pi-args.ts +0 -167
  337. package/packages/subagents/src/result-files.ts +0 -232
  338. package/packages/subagents/src/result-watcher.ts +0 -221
  339. package/packages/subagents/src/retained-children.ts +0 -176
  340. package/packages/subagents/src/run-fanout-budget.ts +0 -356
  341. package/packages/subagents/src/scheduled-runs.ts +0 -349
  342. package/packages/subagents/src/schemas.ts +0 -263
  343. package/packages/subagents/src/slash-commands.ts +0 -80
  344. package/packages/subagents/src/supervisor-channel.ts +0 -249
  345. package/packages/subagents/src/tool-handler.ts +0 -1144
  346. package/packages/subagents/src/types.ts +0 -238
  347. package/packages/subagents/src/widget.ts +0 -457
  348. package/packages/subagents/src/workflow-script.ts +0 -737
  349. package/packages/subagents/src/workflow-worker.ts +0 -384
  350. package/packages/subagents/src/worktree.ts +0 -614
  351. package/packages/utility/src/analytics/collector.ts +0 -293
  352. package/packages/utility/src/info-screen.ts +0 -80
  353. package/packages/utility/src/lifecycle/process.ts +0 -162
  354. package/packages/utility/src/prefix-cache.ts +0 -263
  355. package/packages/utility/src/skill-discovery.ts +0 -542
  356. package/packages/utility/src/tools/env.ts +0 -113
  357. package/packages/utility/src/tui/name-badge-state.ts +0 -315
  358. package/packages/utility/src/tui/name-badge.ts +0 -109
  359. /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
@@ -1,415 +0,0 @@
1
- import {
2
- TOKEN_BUDGET_CALIBRATION_VERSION,
3
- TOKEN_BUDGET_FAMILY_CALIBRATIONS,
4
- TOKEN_BUDGET_RATE_SCALE,
5
- estimateInputTokens,
6
- knownTextSegment,
7
- resolveTokenBudgetFamily,
8
- utf8ByteClassBreakdown,
9
- allowedInputTokens,
10
- isUsableContextWindow,
11
- type EstimateInputTokensResult,
12
- type TokenBudgetByteClassBreakdown,
13
- type TokenBudgetFamily,
14
- type TokenBudgetFamilyCalibration,
15
- type TokenBudgetRateSource,
16
- } from '../context-token-budget.js';
17
- import {
18
- DELEGATE_BUDGET_PLAN_SCHEMA_VERSION,
19
- DelegateError,
20
- type DelegateLimits,
21
- type DelegatePinnedRoute,
22
- } from './types.js';
23
-
24
- /**
25
- * Delegate budgeting.
26
- *
27
- * A delegate child is a multi-turn, tool-using agent, so its budget has two
28
- * distinct phases rather than Fusion's single-shot stage forecast:
29
- *
30
- * 1. Launch admission checks the frozen seed, framing, and child system prompt
31
- * with the same backed family calibration used by Fusion for large prompts.
32
- * 2. A separate provable 1 B/token forecast sizes the transcript-growth runway
33
- * used for explicit tool-result spilling.
34
- * 3. Runtime measurements are advisory. Package-owned growth is controlled
35
- * before transcript entry; Pi and the provider own live context handling.
36
- *
37
- * Nothing here clips, substitutes, or silently reduces content. Tool bytes that
38
- * do not fit the retained-growth runway are preserved as hashed spill artifacts.
39
- */
40
-
41
- /** Output tokens reserved so the child can always finish an answer. */
42
- export const DELEGATE_RESERVED_OUTPUT_TOKENS = 16_384;
43
- /** Provider/tool-schema framing the package does not directly control. */
44
- export const DELEGATE_FRAMING_RESERVE_TOKENS = 8_192;
45
- export const DELEGATE_SAFETY_RESERVE_TOKENS = 4_096;
46
- /** Below this, a route cannot hold a useful seed plus real investigation. */
47
- export const DELEGATE_MIN_USABLE_INPUT_TOKENS = 8_192;
48
- export const DELEGATE_MIN_CONTEXT_WINDOW_TOKENS =
49
- DELEGATE_MIN_USABLE_INPUT_TOKENS +
50
- DELEGATE_RESERVED_OUTPUT_TOKENS +
51
- DELEGATE_FRAMING_RESERVE_TOKENS +
52
- DELEGATE_SAFETY_RESERVE_TOKENS;
53
-
54
- export const DELEGATE_DEFAULT_MAX_TURNS = 24;
55
- export const DELEGATE_DEFAULT_MAX_TOOL_CALLS = 120;
56
- export const DELEGATE_DEFAULT_TIMEOUT_SECONDS = 1200;
57
- export const DELEGATE_MAX_TOOL_RESULT_BYTES = 64 * 1024;
58
- export const DELEGATE_MAX_TOTAL_TOOL_OUTPUT_BYTES = 64 * 1024 * 1024;
59
- export const DELEGATE_MAX_ANSWER_BYTES = 4 * 1024 * 1024;
60
- /** Input runway held back for a final no-tool answer after investigation. */
61
- export const DELEGATE_FINALIZATION_INPUT_RESERVE_TOKENS = 32 * 1024;
62
- /** Remaining retained-growth runway at which the child disables tools. */
63
- export const DELEGATE_FINALIZATION_TRIGGER_TOKENS = 8 * 1024;
64
- /** Answers at or under this serialize inline; larger ones degrade explicitly. */
65
- export const DELEGATE_INLINE_ANSWER_BYTES = 48 * 1024;
66
-
67
- export const DELEGATE_BUDGET_POLICY_ID = 'delegate-budget-policy-v3';
68
-
69
- export interface DelegateBudgetPolicyDescriptor {
70
- id: typeof DELEGATE_BUDGET_POLICY_ID;
71
- calibration_version: string;
72
- calibration_table: Readonly<Record<TokenBudgetFamily, TokenBudgetFamilyCalibration>>;
73
- reserved_output_tokens: number;
74
- framing_reserve_tokens: number;
75
- safety_reserve_tokens: number;
76
- min_usable_input_tokens: number;
77
- inline_answer_bytes: number;
78
- finalization_input_reserve_tokens: number;
79
- finalization_trigger_tokens: number;
80
- launch_estimator_scope: 'calibrated_large_prompt';
81
- retained_growth_estimator_scope: 'provable_1_byte_per_token';
82
- live_provider_context_owner: 'pi_and_provider';
83
- }
84
-
85
- export const DELEGATE_BUDGET_POLICY: DelegateBudgetPolicyDescriptor = {
86
- id: DELEGATE_BUDGET_POLICY_ID,
87
- calibration_version: TOKEN_BUDGET_CALIBRATION_VERSION,
88
- calibration_table: TOKEN_BUDGET_FAMILY_CALIBRATIONS,
89
- reserved_output_tokens: DELEGATE_RESERVED_OUTPUT_TOKENS,
90
- framing_reserve_tokens: DELEGATE_FRAMING_RESERVE_TOKENS,
91
- safety_reserve_tokens: DELEGATE_SAFETY_RESERVE_TOKENS,
92
- min_usable_input_tokens: DELEGATE_MIN_USABLE_INPUT_TOKENS,
93
- inline_answer_bytes: DELEGATE_INLINE_ANSWER_BYTES,
94
- finalization_input_reserve_tokens: DELEGATE_FINALIZATION_INPUT_RESERVE_TOKENS,
95
- finalization_trigger_tokens: DELEGATE_FINALIZATION_TRIGGER_TOKENS,
96
- launch_estimator_scope: 'calibrated_large_prompt',
97
- retained_growth_estimator_scope: 'provable_1_byte_per_token',
98
- live_provider_context_owner: 'pi_and_provider',
99
- };
100
-
101
- export interface DelegateAdmissionPlanV1 {
102
- schema_version: typeof DELEGATE_BUDGET_PLAN_SCHEMA_VERSION;
103
- policy: DelegateBudgetPolicyDescriptor;
104
- route: {
105
- provider: string;
106
- model: string;
107
- qualified_id: string;
108
- context_window_tokens: number;
109
- allowed_input_tokens: number;
110
- family: TokenBudgetFamily;
111
- backed: boolean;
112
- rate_source: TokenBudgetRateSource;
113
- byte_capacity_utf8_bytes: number;
114
- };
115
- child_prompt_utf8_bytes: number;
116
- child_prompt_multibyte_utf8_bytes: number;
117
- system_prompt_utf8_bytes: number;
118
- system_prompt_multibyte_utf8_bytes: number;
119
- launch_utf8_bytes: number;
120
- launch_input_tokens_upper_bound: number;
121
- conservative_launch_input_tokens: number;
122
- conservative_launch_fits: boolean;
123
- signed_headroom_tokens: number;
124
- utilization_basis_points: number;
125
- retained_growth_budget_tokens: number;
126
- finalization_input_reserve_tokens: number;
127
- byte_class_breakdown: TokenBudgetByteClassBreakdown;
128
- dominant_byte_class: EstimateInputTokensResult['rateSource']['dominant_byte_class'];
129
- estimate: EstimateInputTokensResult;
130
- conservative_estimate: EstimateInputTokensResult;
131
- fits: boolean;
132
- limits: DelegateLimits;
133
- }
134
-
135
- function utilizationBasisPoints(tokens: number, allowed: number): number {
136
- if (!Number.isSafeInteger(tokens) || tokens < 0) {
137
- throw new TypeError('tokens must be a non-negative safe integer');
138
- }
139
- if (!Number.isSafeInteger(allowed) || allowed <= 0) {
140
- throw new TypeError('allowed must be a positive safe integer');
141
- }
142
- return Math.floor(((tokens * 10_000) + allowed - 1) / allowed);
143
- }
144
-
145
- function routeFamily(route: DelegatePinnedRoute): ReturnType<typeof resolveTokenBudgetFamily> {
146
- return resolveTokenBudgetFamily({ provider: route.provider, model: route.model });
147
- }
148
-
149
- /**
150
- * Usable input tokens for a pinned route.
151
- *
152
- * A route with an unknown, non-integral, or non-positive context window is a
153
- * loud `route_capacity_unknown` refusal. The delegate never assumes a default
154
- * window, because assuming one is how oversized prompts reach a provider.
155
- */
156
- export function delegateAllowedInputTokens(route: DelegatePinnedRoute): number {
157
- if (!isUsableContextWindow(route.context_window_tokens)) {
158
- throw new DelegateError(
159
- `bg_delegate route ${route.qualified_id} reports no usable context-window capacity`,
160
- {
161
- code: 'route_capacity_unknown',
162
- childCreated: false,
163
- remediation: [
164
- 'Pin an explicit route whose model catalogue entry declares a context window.',
165
- 'No child was created and no capacity was assumed.',
166
- ],
167
- },
168
- );
169
- }
170
- const allowed = allowedInputTokens(route.context_window_tokens, {
171
- reservedOutputTokens: DELEGATE_RESERVED_OUTPUT_TOKENS,
172
- framingReserveTokens: DELEGATE_FRAMING_RESERVE_TOKENS,
173
- safetyReserveTokens: DELEGATE_SAFETY_RESERVE_TOKENS,
174
- });
175
- if (allowed < DELEGATE_MIN_USABLE_INPUT_TOKENS) {
176
- throw new DelegateError(
177
- `bg_delegate route ${route.qualified_id} has a ${String(route.context_window_tokens)}-token context window, but a delegate child requires at least ${String(DELEGATE_MIN_CONTEXT_WINDOW_TOKENS)} tokens: ${String(DELEGATE_RESERVED_OUTPUT_TOKENS)} output + ${String(DELEGATE_FRAMING_RESERVE_TOKENS)} framing + ${String(DELEGATE_SAFETY_RESERVE_TOKENS)} safety + ${String(DELEGATE_MIN_USABLE_INPUT_TOKENS)} usable input`,
178
- {
179
- code: 'route_capacity_unknown',
180
- childCreated: false,
181
- remediation: ['Pin a larger-context route for this delegate.'],
182
- },
183
- );
184
- }
185
- return allowed;
186
- }
187
-
188
- export interface DelegateAdmissionInput {
189
- route: DelegatePinnedRoute;
190
- childPrompt: string;
191
- childSystemPrompt: string;
192
- limits: DelegateLimits;
193
- }
194
-
195
- /** Deterministic launch-admission forecast. Pure; creates nothing. */
196
- export function planDelegateAdmission(input: DelegateAdmissionInput): DelegateAdmissionPlanV1 {
197
- const allowed = delegateAllowedInputTokens(input.route);
198
- const family = routeFamily(input.route);
199
- const childPrompt = utf8ByteClassBreakdown(input.childPrompt);
200
- const system = utf8ByteClassBreakdown(input.childSystemPrompt);
201
- const segments = [knownTextSegment(input.childPrompt), knownTextSegment(input.childSystemPrompt)];
202
- const estimate = estimateInputTokens({
203
- family: family.family,
204
- calibrationBacked: family.backed,
205
- familyResolution: family.resolution,
206
- allowedInputTokens: allowed,
207
- scope: 'delegate_launch',
208
- segments,
209
- });
210
- const conservativeEstimate = estimateInputTokens({
211
- family: family.family,
212
- calibrationBacked: family.backed,
213
- familyResolution: family.resolution,
214
- allowedInputTokens: allowed,
215
- scope: 'conservative',
216
- segments,
217
- });
218
- // `conservativeEstimate.tokens` intentionally uses the shared estimator's
219
- // calibrated multibyte diagnostic rate. The counter-forecast published as
220
- // "provable" must instead use that estimator's explicit 1 B/token ceiling
221
- // for multibyte bytes as well as normal/dense bytes.
222
- const provableConservativeLaunchTokens =
223
- conservativeEstimate.advisory.input_tokens_if_multibyte_used_provable_ceiling;
224
- const byteCapacity = Math.floor(
225
- (allowed * estimate.rateSource.effective_rate_bytes_per_token_x100) / TOKEN_BUDGET_RATE_SCALE,
226
- );
227
- const launchBytes = childPrompt.bytes + system.bytes;
228
- const retainedGrowthBudget = Math.max(
229
- 0,
230
- allowed - estimate.tokens - DELEGATE_FINALIZATION_INPUT_RESERVE_TOKENS,
231
- );
232
- return {
233
- schema_version: DELEGATE_BUDGET_PLAN_SCHEMA_VERSION,
234
- policy: DELEGATE_BUDGET_POLICY,
235
- route: {
236
- provider: input.route.provider,
237
- model: input.route.model,
238
- qualified_id: input.route.qualified_id,
239
- context_window_tokens: input.route.context_window_tokens,
240
- allowed_input_tokens: allowed,
241
- family: family.family,
242
- backed: estimate.rateSource.backed,
243
- rate_source: estimate.rateSource,
244
- byte_capacity_utf8_bytes: byteCapacity,
245
- },
246
- child_prompt_utf8_bytes: childPrompt.bytes,
247
- child_prompt_multibyte_utf8_bytes: childPrompt.multibyteBytes,
248
- system_prompt_utf8_bytes: system.bytes,
249
- system_prompt_multibyte_utf8_bytes: system.multibyteBytes,
250
- launch_utf8_bytes: launchBytes,
251
- launch_input_tokens_upper_bound: estimate.tokens,
252
- conservative_launch_input_tokens: provableConservativeLaunchTokens,
253
- conservative_launch_fits: provableConservativeLaunchTokens <= allowed,
254
- signed_headroom_tokens: allowed - estimate.tokens,
255
- utilization_basis_points: utilizationBasisPoints(estimate.tokens, allowed),
256
- retained_growth_budget_tokens: retainedGrowthBudget,
257
- finalization_input_reserve_tokens: DELEGATE_FINALIZATION_INPUT_RESERVE_TOKENS,
258
- byte_class_breakdown: estimate.byte_class_breakdown,
259
- dominant_byte_class: estimate.rateSource.dominant_byte_class,
260
- estimate,
261
- conservative_estimate: conservativeEstimate,
262
- fits: estimate.tokens <= allowed,
263
- limits: input.limits,
264
- };
265
- }
266
-
267
- function rateWarningText(rateSource: TokenBudgetRateSource, qualifiedId: string): string {
268
- if (rateSource.warning === null) return '';
269
- return ` Estimator warning for ${qualifiedId}: ${rateSource.warning}.`;
270
- }
271
-
272
- function requiredByteReduction(plan: DelegateAdmissionPlanV1): number {
273
- const variableTokens =
274
- plan.route.allowed_input_tokens - plan.route.rate_source.affine_f_tokens;
275
- const maximumBytes = variableTokens <= 0
276
- ? 0
277
- : Math.floor(
278
- (variableTokens * plan.route.rate_source.effective_rate_bytes_per_token_x100) /
279
- TOKEN_BUDGET_RATE_SCALE,
280
- );
281
- return Math.max(0, plan.launch_utf8_bytes - maximumBytes);
282
- }
283
-
284
- /**
285
- * Enforce the admission plan.
286
- *
287
- * Called before the child process, the child session, and the artifact
288
- * directory exist, so a refusal leaves zero children and zero artifacts.
289
- */
290
- export function assertDelegateAdmission(plan: DelegateAdmissionPlanV1): void {
291
- if (plan.fits) return;
292
- const overage = plan.launch_input_tokens_upper_bound - plan.route.allowed_input_tokens;
293
- throw new DelegateError(
294
- `bg_delegate child prompt does not fit the pinned route before launch. Route ${plan.route.qualified_id} allows ${String(plan.route.allowed_input_tokens)} input tokens; the exact child prompt plus child system prompt measure ${String(plan.launch_utf8_bytes)} UTF-8 bytes (<= ${String(plan.launch_input_tokens_upper_bound)} input tokens), over by ${String(overage)} tokens. Estimator family ${plan.route.family}, source ${plan.route.rate_source.source}, backed=${String(plan.route.rate_source.backed)}, dominant_byte_class=${plan.dominant_byte_class}, rate ${String(plan.route.rate_source.effective_rate_bytes_per_token_x100)}/100 B/tok + ${String(plan.route.rate_source.affine_f_tokens)} tokens.${rateWarningText(plan.route.rate_source, plan.route.qualified_id)} Required reduction is at least ${String(requiredByteReduction(plan))} UTF-8 bytes. No child process, child session, or artifact was created. Nothing was clipped, dropped, or substituted.`,
295
- {
296
- code: 'seed_budget_exceeded',
297
- childCreated: false,
298
- budget: {
299
- measurement_kind: 'launch_admission',
300
- measured_utf8_bytes: plan.launch_utf8_bytes,
301
- measured_input_tokens_upper_bound: plan.launch_input_tokens_upper_bound,
302
- allowed_input_tokens: plan.route.allowed_input_tokens,
303
- rate_source: plan.route.rate_source,
304
- backed: plan.route.rate_source.backed,
305
- dominant_byte_class: plan.dominant_byte_class,
306
- byte_class_breakdown: plan.byte_class_breakdown,
307
- },
308
- remediation: [
309
- 'Pin a larger-context route with the route argument.',
310
- 'Delegate earlier in the session, or start a fresh conversation, so less history is projected.',
311
- 'Restate only the required findings as visible conversation text; omitted tool payloads are not what is large here.',
312
- ],
313
- },
314
- );
315
- }
316
-
317
- export interface DelegateRuntimeMeasurement {
318
- /** Complete retained input for the next model call, in UTF-8 bytes. */
319
- retainedInputBytes: number;
320
- retainedInputMultibyteBytes: number;
321
- retainedInputDenseBytes: number;
322
- }
323
-
324
- export interface DelegateGovernorVerdict {
325
- withinBudget: boolean;
326
- measuredTokens: number;
327
- allowedTokens: number;
328
- overageTokens: number;
329
- byteClassBreakdown: TokenBudgetByteClassBreakdown;
330
- dominantByteClass: EstimateInputTokensResult['rateSource']['dominant_byte_class'];
331
- backed: boolean;
332
- rateSource: TokenBudgetRateSource;
333
- }
334
-
335
- /**
336
- * Advisory runtime measurement for one prospective model call.
337
- *
338
- * This deliberately uses the calibrated large-prompt policy and never decides
339
- * whether transport may occur. Fusion's BUG-185 proved that a package-local
340
- * estimator must not reject a live provider payload by subtracting hypothetical
341
- * output. The delegate child uses this result for evidence and graceful
342
- * finalization while proactive spilling controls package-owned growth.
343
- */
344
- export function evaluateDelegateRuntimeBudget(
345
- measurement: DelegateRuntimeMeasurement,
346
- allowedTokens: number,
347
- route: { provider: string; model: string },
348
- ): DelegateGovernorVerdict {
349
- const family = resolveTokenBudgetFamily(route);
350
- const estimate = estimateInputTokens({
351
- family: family.family,
352
- calibrationBacked: family.backed,
353
- familyResolution: family.resolution,
354
- allowedInputTokens: allowedTokens,
355
- scope: 'delegate_launch',
356
- segments: [
357
- {
358
- kind: 'known_text',
359
- bytes: measurement.retainedInputBytes,
360
- multibyteBytes: measurement.retainedInputMultibyteBytes,
361
- denseBytes: measurement.retainedInputDenseBytes,
362
- },
363
- ],
364
- });
365
- return {
366
- withinBudget: estimate.tokens <= allowedTokens,
367
- measuredTokens: estimate.tokens,
368
- allowedTokens,
369
- overageTokens: Math.max(0, estimate.tokens - allowedTokens),
370
- byteClassBreakdown: estimate.byte_class_breakdown,
371
- dominantByteClass: estimate.rateSource.dominant_byte_class,
372
- backed: estimate.rateSource.backed,
373
- rateSource: estimate.rateSource,
374
- };
375
- }
376
-
377
- export interface DelegateLimitOverrides {
378
- maxTurns?: number | undefined;
379
- maxToolCalls?: number | undefined;
380
- timeoutSeconds?: number | undefined;
381
- }
382
-
383
- function positiveInteger(value: number | undefined, fallback: number, label: string): number {
384
- if (value === undefined) return fallback;
385
- if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
386
- throw new DelegateError(`bg_delegate ${label} must be a positive integer`, {
387
- code: 'invalid_arguments',
388
- childCreated: false,
389
- });
390
- }
391
- return value;
392
- }
393
-
394
- export function resolveDelegateLimits(
395
- route: DelegatePinnedRoute,
396
- overrides: DelegateLimitOverrides = {},
397
- ): DelegateLimits {
398
- return {
399
- max_turns: positiveInteger(overrides.maxTurns, DELEGATE_DEFAULT_MAX_TURNS, 'maxTurns'),
400
- max_tool_calls: positiveInteger(
401
- overrides.maxToolCalls,
402
- DELEGATE_DEFAULT_MAX_TOOL_CALLS,
403
- 'maxToolCalls',
404
- ),
405
- timeout_seconds: positiveInteger(
406
- overrides.timeoutSeconds,
407
- DELEGATE_DEFAULT_TIMEOUT_SECONDS,
408
- 'timeoutSeconds',
409
- ),
410
- max_tool_result_bytes: DELEGATE_MAX_TOOL_RESULT_BYTES,
411
- max_total_tool_output_bytes: DELEGATE_MAX_TOTAL_TOOL_OUTPUT_BYTES,
412
- max_answer_bytes: DELEGATE_MAX_ANSWER_BYTES,
413
- allowed_input_tokens: delegateAllowedInputTokens(route),
414
- };
415
- }
@@ -1,18 +0,0 @@
1
- {
2
- "schema_version": "pi-background-tasks.delegate-hook-contract.v1",
3
- "contract_id": "context-measure-abort-v1+tool-result-spill-v1",
4
- "guarantees": {
5
- "context_fires_before_every_model_call": true,
6
- "context_result_messages_reach_provider": true,
7
- "context_abort_blocks_provider_call": true,
8
- "context_abort_skips_stream_invocation": false,
9
- "context_abort_terminates_run": true,
10
- "context_throw_blocks_provider_call": false,
11
- "context_throw_isolated_to_throwing_handler": true,
12
- "tool_result_fires_before_transcript_entry": true,
13
- "tool_result_replacement_reaches_provider": true,
14
- "tool_result_replacement_preserves_identity": true,
15
- "tool_result_chains_in_load_order": true,
16
- "handlers_run_in_extension_load_order": true
17
- }
18
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * Pi extension-hook contract required by the `bg_delegate` child-side guard.
3
- *
4
- * The guard installed inside a delegate child depends on runtime behaviour of
5
- * Pi's `context` and `tool_result` hooks. That behaviour is proven by the
6
- * `tests/scripted-provider/pi-hook-contract.test.ts` characterisation gate,
7
- * which drives a real Pi agent loop and writes the observed guarantees to
8
- * `tests/scripted-provider/pi-hook-contract-evidence.json`.
9
- *
10
- * Nothing here infers behaviour from type declarations. A guarantee is either
11
- * observed by that gate or the delegate launch refuses to spawn a child.
12
- */
13
-
14
- export const DELEGATE_HOOK_CONTRACT_SCHEMA_VERSION =
15
- 'pi-background-tasks.delegate-hook-contract.v1' as const;
16
-
17
- /**
18
- * Identifier for the exact guard mechanism the child installs.
19
- *
20
- * `context-measure-abort-v1`: measure the outgoing message set inside the
21
- * `context` hook and, when it would exceed the pinned route window, call
22
- * `ctx.abort()` so the request is never issued, then report a typed failure over
23
- * the child result channel.
24
- *
25
- * Empirically established across the supported Pi lines by the characterisation
26
- * and exact-version compatibility gates: Pi 0.81.1-0.83.0 invoke the provider
27
- * entry point with an already-aborted `AbortSignal`, while Pi 0.84.0 propagates
28
- * that signal through auth resolution and skips the entry point. No network
29
- * request is issued in either mode, and the run terminates. Throwing from a
30
- * `context` handler is NOT a barrier at all: Pi catches the exception, reports
31
- * it as an extension error, and continues dispatch. The guard therefore uses
32
- * abort, never a throw, and additionally suppresses the oversized content itself
33
- * so a non-conforming provider cannot transmit it.
34
- *
35
- * `tool-result-spill-v1`: replace an oversized `tool_result` payload with an
36
- * explicit hash-accounted receipt before it enters the transcript.
37
- */
38
- export const DELEGATE_HOOK_CONTRACT_ID = 'context-measure-abort-v1+tool-result-spill-v1' as const;
39
-
40
- export const DELEGATE_HOOK_GUARANTEE_NAMES = [
41
- 'context_fires_before_every_model_call',
42
- 'context_result_messages_reach_provider',
43
- 'context_abort_blocks_provider_call',
44
- 'context_abort_skips_stream_invocation',
45
- 'context_abort_terminates_run',
46
- 'context_throw_blocks_provider_call',
47
- 'context_throw_isolated_to_throwing_handler',
48
- 'tool_result_fires_before_transcript_entry',
49
- 'tool_result_replacement_reaches_provider',
50
- 'tool_result_replacement_preserves_identity',
51
- 'tool_result_chains_in_load_order',
52
- 'handlers_run_in_extension_load_order',
53
- ] as const;
54
-
55
- export type DelegateHookGuaranteeName = (typeof DELEGATE_HOOK_GUARANTEE_NAMES)[number];
56
-
57
- export type DelegateHookGuarantees = Readonly<Record<DelegateHookGuaranteeName, boolean>>;
58
-
59
- export interface DelegateHookContractEvidence {
60
- schema_version: typeof DELEGATE_HOOK_CONTRACT_SCHEMA_VERSION;
61
- contract_id: typeof DELEGATE_HOOK_CONTRACT_ID;
62
- guarantees: DelegateHookGuarantees;
63
- }
64
-
65
- /**
66
- * Guarantees the child guard actually depends on.
67
- *
68
- * `context_throw_blocks_provider_call` and
69
- * `context_abort_skips_stream_invocation` are deliberately absent. Throws do
70
- * not block dispatch, and skipping the stream entry point is not shared by every
71
- * supported Pi line. The guard needs neither behavior: it aborts the run AND
72
- * removes the oversized content from the outgoing message set, so the request
73
- * cannot be issued and could not carry the content even if it were.
74
- */
75
- export const DELEGATE_REQUIRED_HOOK_GUARANTEES: readonly DelegateHookGuaranteeName[] = [
76
- 'context_fires_before_every_model_call',
77
- 'context_result_messages_reach_provider',
78
- 'context_abort_blocks_provider_call',
79
- 'context_abort_terminates_run',
80
- 'context_throw_isolated_to_throwing_handler',
81
- 'tool_result_fires_before_transcript_entry',
82
- 'tool_result_replacement_reaches_provider',
83
- 'tool_result_replacement_preserves_identity',
84
- 'tool_result_chains_in_load_order',
85
- 'handlers_run_in_extension_load_order',
86
- ];
87
-
88
- export interface DelegateHookContractVerdict {
89
- supported: boolean;
90
- missing: readonly DelegateHookGuaranteeName[];
91
- }
92
-
93
- export function evaluateDelegateHookContract(
94
- evidence: DelegateHookContractEvidence,
95
- ): DelegateHookContractVerdict {
96
- const missing = DELEGATE_REQUIRED_HOOK_GUARANTEES.filter(
97
- (guarantee) => evidence.guarantees[guarantee] !== true,
98
- );
99
- return { supported: missing.length === 0, missing };
100
- }
101
-
102
- function isRecord(value: unknown): value is Record<PropertyKey, unknown> {
103
- return typeof value === 'object' && value !== null && !Array.isArray(value);
104
- }
105
-
106
- /** Strict parse. A malformed or partial evidence file is a loud failure, never a default-allow. */
107
- export function parseDelegateHookContractEvidence(value: unknown): DelegateHookContractEvidence {
108
- if (!isRecord(value)) throw new Error('delegate hook-contract evidence must be an object');
109
- if (value['schema_version'] !== DELEGATE_HOOK_CONTRACT_SCHEMA_VERSION) {
110
- throw new Error(
111
- `delegate hook-contract evidence schema_version must be ${DELEGATE_HOOK_CONTRACT_SCHEMA_VERSION}`,
112
- );
113
- }
114
- if (value['contract_id'] !== DELEGATE_HOOK_CONTRACT_ID) {
115
- throw new Error(
116
- `delegate hook-contract evidence contract_id must be ${DELEGATE_HOOK_CONTRACT_ID}`,
117
- );
118
- }
119
- const raw = value['guarantees'];
120
- if (!isRecord(raw)) throw new Error('delegate hook-contract evidence guarantees must be an object');
121
- const keys = Object.keys(raw).sort();
122
- const expected = [...DELEGATE_HOOK_GUARANTEE_NAMES].sort();
123
- if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
124
- throw new Error(
125
- `delegate hook-contract evidence guarantees keys mismatch: expected ${expected.join(', ')}`,
126
- );
127
- }
128
- const flag = (name: DelegateHookGuaranteeName): boolean => {
129
- const observed = raw[name];
130
- if (typeof observed !== 'boolean') {
131
- throw new Error(`delegate hook-contract evidence guarantee ${name} must be a boolean`);
132
- }
133
- return observed;
134
- };
135
- const guarantees: DelegateHookGuarantees = {
136
- context_fires_before_every_model_call: flag('context_fires_before_every_model_call'),
137
- context_result_messages_reach_provider: flag('context_result_messages_reach_provider'),
138
- context_abort_blocks_provider_call: flag('context_abort_blocks_provider_call'),
139
- context_abort_skips_stream_invocation: flag('context_abort_skips_stream_invocation'),
140
- context_abort_terminates_run: flag('context_abort_terminates_run'),
141
- context_throw_blocks_provider_call: flag('context_throw_blocks_provider_call'),
142
- context_throw_isolated_to_throwing_handler: flag('context_throw_isolated_to_throwing_handler'),
143
- tool_result_fires_before_transcript_entry: flag('tool_result_fires_before_transcript_entry'),
144
- tool_result_replacement_reaches_provider: flag('tool_result_replacement_reaches_provider'),
145
- tool_result_replacement_preserves_identity: flag('tool_result_replacement_preserves_identity'),
146
- tool_result_chains_in_load_order: flag('tool_result_chains_in_load_order'),
147
- handlers_run_in_extension_load_order: flag('handlers_run_in_extension_load_order'),
148
- };
149
- return {
150
- schema_version: DELEGATE_HOOK_CONTRACT_SCHEMA_VERSION,
151
- contract_id: DELEGATE_HOOK_CONTRACT_ID,
152
- guarantees,
153
- };
154
- }