@iowarp/clio-coder 0.3.9 → 0.4.0

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 (416) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/NOTICE +33 -0
  3. package/README.md +7 -2
  4. package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
  8. package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
  11. package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
  12. package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
  13. package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
  14. package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
  15. package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
  16. package/dist/chunk-42FMPA75.js +101 -0
  17. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  18. package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
  19. package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
  20. package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
  21. package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
  22. package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
  23. package/dist/chunk-B7OBL7PK.js +1405 -0
  24. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  25. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  26. package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
  27. package/dist/chunk-BKFM6EJV.js +462 -0
  28. package/dist/chunk-BMS5RKQY.js +27 -0
  29. package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
  30. package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
  31. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  32. package/dist/chunk-C2LTL2W6.js +2447 -0
  33. package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
  34. package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
  35. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  36. package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
  37. package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
  38. package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
  39. package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
  40. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  41. package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
  42. package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
  43. package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
  44. package/dist/chunk-HHV2GANA.js +88 -0
  45. package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
  46. package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
  47. package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
  48. package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
  49. package/dist/chunk-HZHHCK24.js +1631 -0
  50. package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
  51. package/dist/chunk-IBEBSCYA.js +564 -0
  52. package/dist/chunk-IQ7KR472.js +362 -0
  53. package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
  54. package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
  55. package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
  56. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  57. package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
  58. package/dist/chunk-KMVISBZR.js +132 -0
  59. package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
  60. package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
  61. package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
  62. package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
  63. package/dist/chunk-NEKRRTYW.js +56 -0
  64. package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
  65. package/dist/chunk-NHLBIGRH.js +1506 -0
  66. package/dist/chunk-NQQH3YT7.js +302 -0
  67. package/dist/chunk-NYS75XW5.js +15 -0
  68. package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
  69. package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
  70. package/dist/chunk-OQBA45DZ.js +97 -0
  71. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  72. package/dist/chunk-PMZCIOCJ.js +25 -0
  73. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  74. package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
  75. package/dist/chunk-QDC3K2U3.js +262 -0
  76. package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
  77. package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
  78. package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
  79. package/dist/chunk-QSNYB6ZV.js +195 -0
  80. package/dist/chunk-R346GLFC.js +31 -0
  81. package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
  82. package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
  83. package/dist/chunk-SUCTJL45.js +45 -0
  84. package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
  85. package/dist/chunk-T56WDKA5.js +183 -0
  86. package/dist/chunk-TVHHYFHE.js +255 -0
  87. package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
  88. package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
  89. package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
  90. package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
  91. package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
  92. package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
  93. package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
  94. package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
  95. package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
  96. package/dist/chunk-VO67MWHC.js +75 -0
  97. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  98. package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
  99. package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
  100. package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
  101. package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
  102. package/dist/chunk-X75E3D2N.js +686 -0
  103. package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
  104. package/dist/chunk-YJX4SHTD.js +40 -0
  105. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  106. package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
  107. package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
  108. package/dist/cli/index.js +47 -36
  109. package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
  110. package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
  111. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  112. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  113. package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
  114. package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
  115. package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
  116. package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
  117. package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
  118. package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
  119. package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
  120. package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
  121. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  122. package/dist/doctor-QOA5FNY5.js +313 -0
  123. package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
  124. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  125. package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
  126. package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
  127. package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
  128. package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
  129. package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
  130. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  131. package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
  132. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  133. package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
  134. package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
  135. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  136. package/dist/fleet-view-ZCON35AG.js +102 -0
  137. package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
  138. package/dist/install-XGLBQY5E.js +13 -0
  139. package/dist/interop-OZBKXAYL.js +114 -0
  140. package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
  141. package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
  142. package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
  143. package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
  144. package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
  145. package/dist/panes-HMABYVO4.js +58 -0
  146. package/dist/panes-KY6W3V2E.js +103 -0
  147. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  148. package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
  149. package/dist/resources-BB3MVJMD.js +111 -0
  150. package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
  151. package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
  152. package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
  153. package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
  154. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  155. package/dist/slash-commands-ZSGASKJC.js +77 -0
  156. package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
  157. package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
  158. package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
  159. package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
  160. package/dist/tools-DAF3DI3C.js +27 -0
  161. package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
  162. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  163. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  164. package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
  165. package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
  166. package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
  167. package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
  168. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  169. package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
  170. package/dist/with-panes-MKB46MPQ.js +782 -0
  171. package/dist/worker/entry.js +101 -66
  172. package/docs/README.md +3 -2
  173. package/docs/acp.md +24 -3
  174. package/docs/alcf-provider.md +1 -1
  175. package/docs/architecture.md +2 -2
  176. package/docs/artifact-versions.md +4 -1
  177. package/docs/built-in-agents.md +1 -1
  178. package/docs/capacity-and-scheduling.md +43 -5
  179. package/docs/commands-and-modes.md +31 -2
  180. package/docs/configuration-and-targets.md +40 -10
  181. package/docs/context-engine.md +1 -1
  182. package/docs/development-pipeline.md +19 -0
  183. package/docs/dispatch-typed-intent.md +385 -0
  184. package/docs/documentation-coverage.md +2 -2
  185. package/docs/documentation-guide.md +1 -1
  186. package/docs/environment-variables.md +1 -0
  187. package/docs/eval-runner.md +1 -1
  188. package/docs/evals-internal.md +1 -1
  189. package/docs/evidence-and-memory.md +2 -2
  190. package/docs/evolution.md +1 -1
  191. package/docs/exit-codes-and-output.md +1 -1
  192. package/docs/extensions-and-sharing.md +25 -1
  193. package/docs/fleet-dispatch.md +22 -1
  194. package/docs/installation-and-lifecycle.md +15 -2
  195. package/docs/middleware-and-components.md +1 -1
  196. package/docs/model-catalog.md +10 -1
  197. package/docs/observability.md +3 -3
  198. package/docs/proactive-memory.md +1 -1
  199. package/docs/prompt-envelope-and-tools.md +1 -1
  200. package/docs/provider-adapter-cookbook.md +1 -1
  201. package/docs/safety-model.md +2 -2
  202. package/docs/scientific-validation.md +3 -3
  203. package/docs/session-lifecycle.md +1 -1
  204. package/docs/skills-marketplace.md +1 -1
  205. package/docs/tool-usage.md +18 -8
  206. package/docs/trace-store.md +1 -1
  207. package/docs/troubleshooting.md +1 -1
  208. package/docs/tui-design.md +1 -1
  209. package/docs/worker-dispatch-mechanics.md +1 -1
  210. package/package.json +4 -2
  211. package/src/cli/acp.ts +6 -2
  212. package/src/cli/argv.ts +25 -0
  213. package/src/cli/configure.ts +23 -21
  214. package/src/cli/doctor-panes.ts +124 -0
  215. package/src/cli/doctor-toolchain.ts +57 -0
  216. package/src/cli/doctor.ts +20 -1
  217. package/src/cli/eval-inventory.ts +436 -0
  218. package/src/cli/eval.ts +13 -0
  219. package/src/cli/evidence-detail.ts +88 -0
  220. package/src/cli/evidence-inventory.ts +183 -0
  221. package/src/cli/evidence.ts +30 -5
  222. package/src/cli/fleet-decisions.ts +69 -0
  223. package/src/cli/fleet-inspect.ts +334 -0
  224. package/src/cli/fleet-verify.ts +133 -0
  225. package/src/cli/fleet-view.ts +810 -0
  226. package/src/cli/fleet.ts +148 -37
  227. package/src/cli/index.ts +14 -2
  228. package/src/cli/interop-inspect.ts +128 -0
  229. package/src/cli/interop.ts +34 -0
  230. package/src/cli/panes.ts +35 -0
  231. package/src/cli/reset.ts +5 -2
  232. package/src/cli/run.ts +58 -0
  233. package/src/cli/skills-inventory.ts +185 -0
  234. package/src/cli/skills.ts +16 -13
  235. package/src/cli/tools.ts +321 -0
  236. package/src/cli/trace-inspect.ts +252 -0
  237. package/src/cli/trace.ts +23 -2
  238. package/src/cli/verifiers-inspect.ts +347 -0
  239. package/src/cli/verifiers.ts +9 -0
  240. package/src/core/bus-events.ts +4 -0
  241. package/src/core/config.ts +49 -0
  242. package/src/core/defaults.ts +93 -7
  243. package/src/core/response-schema.ts +44 -4
  244. package/src/core/theme-token-hex.ts +43 -0
  245. package/src/core/tool-names.ts +2 -1
  246. package/src/core/xdg.ts +1 -1
  247. package/src/domains/agents/fleets/build-review.md +0 -3
  248. package/src/domains/agents/fleets/build-test.md +0 -3
  249. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  250. package/src/domains/agents/result-contract.ts +164 -35
  251. package/src/domains/config/classify.ts +3 -0
  252. package/src/domains/dispatch/admission-error.ts +9 -0
  253. package/src/domains/dispatch/admission.ts +22 -21
  254. package/src/domains/dispatch/capacity-lease.ts +21 -1
  255. package/src/domains/dispatch/council-topology.ts +398 -0
  256. package/src/domains/dispatch/extension.ts +222 -50
  257. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  258. package/src/domains/dispatch/fleet-plan.ts +7 -2
  259. package/src/domains/dispatch/fleet-run.ts +64 -1
  260. package/src/domains/dispatch/gate-decisions.ts +11 -1
  261. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  262. package/src/domains/dispatch/gate-topology.ts +289 -0
  263. package/src/domains/dispatch/index.ts +19 -0
  264. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  265. package/src/domains/dispatch/intent.ts +85 -1
  266. package/src/domains/dispatch/reservation-store.ts +24 -4
  267. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  268. package/src/domains/dispatch/run-event-journal.ts +598 -0
  269. package/src/domains/dispatch/state.ts +45 -1
  270. package/src/domains/dispatch/types.ts +13 -0
  271. package/src/domains/dispatch/validation.ts +33 -8
  272. package/src/domains/eval/compare/compare.ts +3 -16
  273. package/src/domains/eval/inventory.ts +113 -0
  274. package/src/domains/eval/schema/serving.ts +31 -0
  275. package/src/domains/evidence/store.ts +6 -0
  276. package/src/domains/memory/index.ts +0 -1
  277. package/src/domains/memory/task-memory-policy.ts +6 -10
  278. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  279. package/src/domains/middleware/memory-intervention.ts +33 -21
  280. package/src/domains/mux/contract.ts +434 -0
  281. package/src/domains/mux/detect.ts +158 -0
  282. package/src/domains/mux/extension.ts +47 -0
  283. package/src/domains/mux/index.ts +96 -0
  284. package/src/domains/mux/manifest.ts +6 -0
  285. package/src/domains/mux/operations.ts +164 -0
  286. package/src/domains/mux/pane-registry.ts +90 -0
  287. package/src/domains/mux/protocol.ts +49 -0
  288. package/src/domains/mux/socket-client.ts +816 -0
  289. package/src/domains/mux/types.ts +222 -0
  290. package/src/domains/mux/viewer-command.ts +59 -0
  291. package/src/domains/mux/yazi/assets/init.lua +2 -0
  292. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  293. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  294. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  295. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  296. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  297. package/src/domains/mux/yazi/event-stream.ts +180 -0
  298. package/src/domains/mux/yazi/profile.ts +299 -0
  299. package/src/domains/mux/yazi/session.ts +228 -0
  300. package/src/domains/mux/yazi/theme.ts +30 -0
  301. package/src/domains/observability/cost.ts +21 -0
  302. package/src/domains/observability/index.ts +2 -0
  303. package/src/domains/observability/trace-store.ts +42 -0
  304. package/src/domains/providers/endpoint-capacity.ts +154 -22
  305. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  306. package/src/domains/providers/extension.ts +20 -3
  307. package/src/domains/providers/index.ts +22 -0
  308. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  309. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  310. package/src/domains/providers/runtimes/builtins.ts +2 -0
  311. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  312. package/src/domains/providers/support.ts +1 -0
  313. package/src/domains/providers/target-model-cache.ts +124 -0
  314. package/src/domains/resources/index.ts +3 -0
  315. package/src/domains/resources/skills/loader.ts +33 -0
  316. package/src/domains/safety/action-classifier.ts +6 -0
  317. package/src/domains/toolchain/archive.ts +175 -0
  318. package/src/domains/toolchain/contract.ts +28 -0
  319. package/src/domains/toolchain/extension.ts +47 -0
  320. package/src/domains/toolchain/index.ts +39 -0
  321. package/src/domains/toolchain/install.ts +327 -0
  322. package/src/domains/toolchain/manifest.ts +8 -0
  323. package/src/domains/toolchain/paths.ts +34 -0
  324. package/src/domains/toolchain/registry.ts +265 -0
  325. package/src/domains/toolchain/remove.ts +218 -0
  326. package/src/domains/toolchain/resolve.ts +182 -0
  327. package/src/domains/toolchain/types.ts +113 -0
  328. package/src/domains/toolchain/version.ts +88 -0
  329. package/src/engine/acp/server.ts +413 -70
  330. package/src/engine/acp/types.ts +19 -1
  331. package/src/engine/claude/sdk-module.ts +98 -0
  332. package/src/engine/claude/sdk-runtime.ts +19 -11
  333. package/src/engine/tui-primitives.ts +21 -0
  334. package/src/engine/tui.ts +1 -0
  335. package/src/engine/worker-runtime.ts +2 -13
  336. package/src/entry/boot-options.ts +2 -0
  337. package/src/entry/orchestrator.ts +103 -5
  338. package/src/entry/panes-activation.ts +31 -0
  339. package/src/entry/with-panes.ts +20 -0
  340. package/src/interactive/context-overlay.ts +1 -0
  341. package/src/interactive/cost-overlay.ts +2 -0
  342. package/src/interactive/council-grid.ts +1 -3
  343. package/src/interactive/council.ts +11 -0
  344. package/src/interactive/dispatch-board.ts +26 -16
  345. package/src/interactive/interactive-application.ts +153 -6
  346. package/src/interactive/interactive-input-runtime.ts +7 -0
  347. package/src/interactive/interactive-slash-runtime.ts +36 -6
  348. package/src/interactive/memory-overlay.ts +1 -0
  349. package/src/interactive/modal-marker.ts +170 -0
  350. package/src/interactive/mux-bridge.ts +214 -0
  351. package/src/interactive/overlay-frame.ts +58 -2
  352. package/src/interactive/overlay-general-openers.ts +1 -0
  353. package/src/interactive/overlay-key-routing.ts +14 -3
  354. package/src/interactive/overlay-lifecycle.ts +19 -3
  355. package/src/interactive/overlay-model-selectors.ts +40 -3
  356. package/src/interactive/overlay-permission-lifecycle.ts +90 -22
  357. package/src/interactive/overlay-transitions.ts +18 -4
  358. package/src/interactive/overlays/agents.ts +1 -0
  359. package/src/interactive/overlays/ask-user.ts +136 -30
  360. package/src/interactive/overlays/auth-dialog.ts +1 -0
  361. package/src/interactive/overlays/context-reset.ts +1 -0
  362. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  363. package/src/interactive/overlays/decisions.ts +11 -11
  364. package/src/interactive/overlays/extensions.ts +1 -0
  365. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  366. package/src/interactive/overlays/handoff-review.ts +1 -0
  367. package/src/interactive/overlays/help-reference.ts +2 -0
  368. package/src/interactive/overlays/interop.ts +1 -0
  369. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  370. package/src/interactive/overlays/library-tabs.ts +28 -0
  371. package/src/interactive/overlays/list-overlay.ts +10 -1
  372. package/src/interactive/overlays/message-picker.ts +1 -0
  373. package/src/interactive/overlays/model-scope.ts +86 -0
  374. package/src/interactive/overlays/model-selector.ts +1 -0
  375. package/src/interactive/overlays/prompts.ts +1 -0
  376. package/src/interactive/overlays/session-selector.ts +1 -0
  377. package/src/interactive/overlays/settings-sections.ts +30 -0
  378. package/src/interactive/overlays/settings.ts +579 -44
  379. package/src/interactive/overlays/side-question.ts +1 -0
  380. package/src/interactive/overlays/skills-hub.ts +3 -11
  381. package/src/interactive/overlays/tree-selector.ts +1 -0
  382. package/src/interactive/pane-policy.ts +46 -0
  383. package/src/interactive/panes-runtime.ts +292 -0
  384. package/src/interactive/renderers/compaction-summary.ts +29 -0
  385. package/src/interactive/renderers/worker-entry.ts +122 -14
  386. package/src/interactive/slash-commands.ts +251 -15
  387. package/src/interactive/tasks-overlay.ts +1 -0
  388. package/src/interactive/theme/tokens.ts +3 -14
  389. package/src/interactive/turn-context.ts +47 -2
  390. package/src/interactive/view/artifacts.ts +109 -1
  391. package/src/interactive/view/view-overlay.ts +1 -0
  392. package/src/interactive/watch-pane.ts +152 -0
  393. package/src/interactive/worker-progress.ts +7 -1
  394. package/src/interactive/worker-receipts.ts +19 -1
  395. package/src/interactive/worker-stream.ts +5 -0
  396. package/src/interactive/yazi-bridge.ts +444 -0
  397. package/src/tools/bootstrap.ts +26 -2
  398. package/src/tools/builtin-tool-catalog.ts +15 -0
  399. package/src/tools/compete-worktrees.ts +83 -2
  400. package/src/tools/core-bootstrap.ts +2 -1
  401. package/src/tools/dispatch-admission.ts +14 -3
  402. package/src/tools/dispatch-arguments.ts +20 -20
  403. package/src/tools/dispatch-run-events.ts +134 -19
  404. package/src/tools/dispatch-runner.ts +29 -7
  405. package/src/tools/dispatch-types.ts +15 -3
  406. package/src/tools/dispatch.ts +1 -1
  407. package/src/tools/executables.ts +17 -14
  408. package/src/tools/observation.ts +54 -4
  409. package/src/tools/panes-surface.ts +38 -0
  410. package/src/tools/panes.ts +112 -0
  411. package/src/tools/policy.ts +10 -1
  412. package/src/tools/presentation.ts +1 -0
  413. package/dist/chunk-D73KXYPF.js +0 -37
  414. package/dist/chunk-MLOK6ZOS.js +0 -2888
  415. package/dist/chunk-YKOFT37S.js +0 -279
  416. package/dist/doctor-TWBWFK5V.js +0 -165
@@ -105,6 +105,15 @@ export interface ResultContractFilesystem {
105
105
  * calling an unreadable path fabricated.
106
106
  */
107
107
  pathExists?(path: string): boolean;
108
+ /**
109
+ * Whether this location is a directory. Optional for the same reason as
110
+ * `pathExists`: a contract that only needs a file's bytes never asks. A
111
+ * Scout citation does, because a survey task cites directories, and a
112
+ * directory has no bytes to read and no line to ground. Absent means the
113
+ * distinction is unobservable here, so an unreadable location stays
114
+ * unreadable rather than being assumed to be a directory.
115
+ */
116
+ isDirectory?(path: string): boolean;
108
117
  }
109
118
 
110
119
  /**
@@ -792,6 +801,41 @@ function parseChecks(value: unknown): VerifierCheck[] | null {
792
801
 
793
802
  type ScoutCitationEvidence = ScoutCitation & { contentDigest: string };
794
803
 
804
+ /**
805
+ * Where a citation lands relative to the workspace root, after the written form
806
+ * is normalized away.
807
+ *
808
+ * A model writes the same location six ways: `src`, `./src`, `src/`, `./src/`,
809
+ * and for the root itself `.` or `./`. `path.resolve` collapses every one of
810
+ * them, so the containment question is answered on the resolved pair and never
811
+ * on the string the model typed.
812
+ *
813
+ * The root is the case that broke. `path.relative(cwd, cwd)` is the empty
814
+ * string, and the check read empty as "not inside", which is exactly backwards:
815
+ * the workspace root is the one path guaranteed to be contained. A
816
+ * directory-survey Scout cites `.` because that is what it surveyed, and four
817
+ * runs died `result_contract_exhausted` on evidence that never left the
818
+ * workspace. Containment is unchanged for a real escape, which still resolves
819
+ * to a `..` segment or an absolute path outside the root.
820
+ */
821
+ function citationWithinWorkspace(cwd: string, citedPath: string): boolean {
822
+ const relative = path.relative(cwd, citedPath);
823
+ if (relative === "") return true;
824
+ return !(relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative));
825
+ }
826
+
827
+ /**
828
+ * A directory citation carries no bytes and no line, so its evidence digest is
829
+ * bound to the location instead. Prefixed so it can never collide with the
830
+ * content digest of a file that happens to hold the same text.
831
+ */
832
+ function directoryCitationDigest(cwd: string, citedPath: string): string {
833
+ const relative = path.relative(cwd, citedPath);
834
+ return createHash("sha256")
835
+ .update(`directory:${relative === "" ? "." : relative}`, "utf8")
836
+ .digest("hex");
837
+ }
838
+
795
839
  /** One citation against the workspace and, where known, against this run's own reads. */
796
840
  function validateScoutCitation(
797
841
  input: ResultContractValidationInput,
@@ -799,10 +843,17 @@ function validateScoutCitation(
799
843
  ): { ok: true; evidence: ScoutCitationEvidence } | { ok: false; reason: string } {
800
844
  const cwd = path.resolve(input.cwd);
801
845
  const citedPath = path.resolve(cwd, citation.path);
802
- const relative = path.relative(cwd, citedPath);
803
- if (relative === "" || relative.startsWith(`..${path.sep}`) || relative === ".." || path.isAbsolute(relative)) {
846
+ if (!citationWithinWorkspace(cwd, citedPath)) {
804
847
  return { ok: false, reason: `Scout citation path escapes the workspace: ${citation.path}` };
805
848
  }
849
+ // A directory is a legal reconnaissance location. Existence is the whole
850
+ // check available: there is no content to digest, no line count to bound the
851
+ // cited line against, and no read span to ground it in, because a survey
852
+ // lists a directory rather than reading it. Requiring a live read here would
853
+ // reject the correct answer to every directory-survey task.
854
+ if (input.filesystem.isDirectory?.(citedPath) === true) {
855
+ return { ok: true, evidence: { ...citation, contentDigest: directoryCitationDigest(cwd, citedPath) } };
856
+ }
806
857
  const content = input.filesystem.readFile(citedPath);
807
858
  if (content === null) return { ok: false, reason: `Scout citation cannot be read: ${citation.path}` };
808
859
  const lineCount = content.split(/\r\n|\r|\n/u).length;
@@ -888,14 +939,57 @@ function groundDegradedScout(
888
939
 
889
940
  function validateVerifier(contract: ResultContract, output: string | null): ResultContractValidation {
890
941
  const parsed = parseJson(output);
891
- if (!parsed.ok) return failure(contract, "fail", parsed.reason);
942
+ if (!parsed.ok) return failure(contract, "fail", `Verifier result payload failed: ${parsed.reason}`);
892
943
  const value = parsed.value;
893
- if (!hasOnlyKeys(value, ["verdict", "checks"]) || (value.verdict !== "pass" && value.verdict !== "fail")) {
894
- return failure(contract, "fail", "Verifier result must carry pass or fail verdict and checks");
944
+ const unknown = Object.keys(value).filter((key) => key !== "verdict" && key !== "checks");
945
+ if (unknown.length > 0) {
946
+ return failure(
947
+ contract,
948
+ "fail",
949
+ `Verifier result field${unknown.length === 1 ? "" : "s"} ${unknown.join(", ")} ${unknown.length === 1 ? "is" : "are"} not legal; legal top-level fields are verdict and checks`,
950
+ );
895
951
  }
896
- const checks = parseChecks(value.checks);
897
- if (checks === null || (value.verdict === "pass") !== checks.every((check) => check.passed)) {
898
- return failure(contract, "fail", "Verifier verdict must agree with every typed check");
952
+ if (value.verdict !== "pass" && value.verdict !== "fail") {
953
+ return failure(contract, "fail", 'Verifier field "verdict" must be one of: pass | fail');
954
+ }
955
+ if (!Array.isArray(value.checks) || value.checks.length === 0) {
956
+ return failure(
957
+ contract,
958
+ "fail",
959
+ 'Verifier field "checks" must be a non-empty array of {name, passed, evidence}; each checks[].passed value must be one of: true | false',
960
+ );
961
+ }
962
+ const checks: VerifierCheck[] = [];
963
+ for (const [index, raw] of value.checks.entries()) {
964
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
965
+ return failure(contract, "fail", `Verifier field "checks[${index}]" must be an object`);
966
+ }
967
+ const check = raw as Record<string, unknown>;
968
+ const checkUnknown = Object.keys(check).filter((key) => key !== "name" && key !== "passed" && key !== "evidence");
969
+ if (checkUnknown.length > 0) {
970
+ return failure(
971
+ contract,
972
+ "fail",
973
+ `Verifier field "checks[${index}].${checkUnknown[0]}" is not legal; legal check fields are name, passed, and evidence`,
974
+ );
975
+ }
976
+ if (!string(check.name)) {
977
+ return failure(contract, "fail", `Verifier field "checks[${index}].name" must be a non-empty string`);
978
+ }
979
+ if (typeof check.passed !== "boolean") {
980
+ return failure(contract, "fail", `Verifier field "checks[${index}].passed" must be one of: true | false`);
981
+ }
982
+ if (!string(check.evidence)) {
983
+ return failure(contract, "fail", `Verifier field "checks[${index}].evidence" must be a non-empty string`);
984
+ }
985
+ checks.push({ name: check.name, passed: check.passed, evidence: check.evidence });
986
+ }
987
+ if ((value.verdict === "pass") !== checks.every((check) => check.passed)) {
988
+ return failure(
989
+ contract,
990
+ "fail",
991
+ 'Verifier field "verdict" must agree with every checks[].passed value; verdict legal values are pass | fail and checks[].passed legal values are true | false',
992
+ );
899
993
  }
900
994
  const verifier: VerifierResult = { verdict: value.verdict, checks };
901
995
  return success(contract, value.verdict, verifier, { verifier });
@@ -1086,23 +1180,39 @@ export function resultContractAuthorship(contract: ResultContract, output: strin
1086
1180
 
1087
1181
  function validateDebugger(contract: ResultContract, output: string | null): ResultContractValidation {
1088
1182
  const parsed = parseJson(output);
1089
- if (!parsed.ok) return failure(contract, "unmeasured", parsed.reason);
1183
+ if (!parsed.ok) return failure(contract, "unmeasured", `Debugger result payload failed: ${parsed.reason}`);
1090
1184
  const value = parsed.value;
1185
+ const unknown = Object.keys(value).filter(
1186
+ (key) => key !== "diagnosis" && key !== "reproduction" && key !== "evidence",
1187
+ );
1188
+ if (unknown.length > 0) {
1189
+ return failure(
1190
+ contract,
1191
+ "unmeasured",
1192
+ `Debugger result field${unknown.length === 1 ? "" : "s"} ${unknown.join(", ")} ${unknown.length === 1 ? "is" : "are"} not legal; legal top-level fields are diagnosis, reproduction, and evidence`,
1193
+ );
1194
+ }
1195
+ if (!string(value.diagnosis)) {
1196
+ return failure(contract, "unmeasured", 'Debugger field "diagnosis" must be a non-empty string');
1197
+ }
1091
1198
  if (
1092
- !hasOnlyKeys(value, ["diagnosis", "reproduction", "evidence"]) ||
1093
- !string(value.diagnosis) ||
1094
- (value.reproduction !== "reproduced" &&
1095
- value.reproduction !== "not-reproduced" &&
1096
- value.reproduction !== "unknown") ||
1097
- !Array.isArray(value.evidence) ||
1098
- value.evidence.some((entry) => !string(entry))
1199
+ value.reproduction !== "reproduced" &&
1200
+ value.reproduction !== "not-reproduced" &&
1201
+ value.reproduction !== "unknown"
1099
1202
  ) {
1100
1203
  return failure(
1101
1204
  contract,
1102
1205
  "unmeasured",
1103
- "Debugger result must carry diagnosis, reproduction status, and evidence without a verdict",
1206
+ 'Debugger field "reproduction" must be one of: reproduced | not-reproduced | unknown',
1104
1207
  );
1105
1208
  }
1209
+ if (!Array.isArray(value.evidence)) {
1210
+ return failure(contract, "unmeasured", 'Debugger field "evidence" must be an array of strings');
1211
+ }
1212
+ const malformedEvidence = value.evidence.findIndex((entry) => !string(entry));
1213
+ if (malformedEvidence >= 0) {
1214
+ return failure(contract, "unmeasured", `Debugger field "evidence[${malformedEvidence}]" must be a non-empty string`);
1215
+ }
1106
1216
  return success(contract, "unmeasured", value);
1107
1217
  }
1108
1218
 
@@ -1112,29 +1222,47 @@ function validateResearch(
1112
1222
  networkAllowed: boolean,
1113
1223
  ): ResultContractValidation {
1114
1224
  const parsed = parseJson(output);
1115
- if (!parsed.ok) return failure(contract, "unmeasured", parsed.reason);
1225
+ if (!parsed.ok) return failure(contract, "unmeasured", `Research result payload failed: ${parsed.reason}`);
1116
1226
  const value = parsed.value;
1117
- if (
1118
- !hasOnlyKeys(value, ["source", "findings"]) ||
1119
- (value.source !== "local" && value.source !== "external") ||
1120
- !Array.isArray(value.findings)
1121
- ) {
1227
+ const unknown = Object.keys(value).filter((key) => key !== "source" && key !== "findings");
1228
+ if (unknown.length > 0) {
1122
1229
  return failure(
1123
1230
  contract,
1124
1231
  "unmeasured",
1125
- "Research result must distinguish local or external sources and carry findings",
1232
+ `Research result field${unknown.length === 1 ? "" : "s"} ${unknown.join(", ")} ${unknown.length === 1 ? "is" : "are"} not legal; legal top-level fields are source and findings`,
1126
1233
  );
1127
1234
  }
1235
+ if (value.source !== "local" && value.source !== "external") {
1236
+ return failure(contract, "unmeasured", 'Research field "source" must be one of: local | external');
1237
+ }
1238
+ if (!Array.isArray(value.findings)) {
1239
+ return failure(contract, "unmeasured", 'Research field "findings" must be an array of {claim, evidence} objects');
1240
+ }
1128
1241
  if (value.source === "external" && !networkAllowed) {
1129
- return failure(contract, "unmeasured", "external research requires an allowed network posture");
1242
+ return failure(
1243
+ contract,
1244
+ "unmeasured",
1245
+ 'Research field "source" is external but the run has no allowed network posture; legal values are local | external, and use local for workspace-only evidence',
1246
+ );
1130
1247
  }
1131
- for (const finding of value.findings) {
1248
+ for (const [index, finding] of value.findings.entries()) {
1132
1249
  if (finding === null || typeof finding !== "object" || Array.isArray(finding)) {
1133
- return failure(contract, "unmeasured", "Research findings must be objects");
1250
+ return failure(contract, "unmeasured", `Research field "findings[${index}]" must be an object`);
1134
1251
  }
1135
1252
  const record = finding as Record<string, unknown>;
1136
- if (!hasOnlyKeys(record, ["claim", "evidence"]) || !string(record.claim) || !string(record.evidence)) {
1137
- return failure(contract, "unmeasured", "Research findings must carry claim and evidence");
1253
+ const findingUnknown = Object.keys(record).filter((key) => key !== "claim" && key !== "evidence");
1254
+ if (findingUnknown.length > 0) {
1255
+ return failure(
1256
+ contract,
1257
+ "unmeasured",
1258
+ `Research field "findings[${index}].${findingUnknown[0]}" is not legal; legal finding fields are claim and evidence`,
1259
+ );
1260
+ }
1261
+ if (!string(record.claim)) {
1262
+ return failure(contract, "unmeasured", `Research field "findings[${index}].claim" must be a non-empty string`);
1263
+ }
1264
+ if (!string(record.evidence)) {
1265
+ return failure(contract, "unmeasured", `Research field "findings[${index}].evidence" must be a non-empty string`);
1138
1266
  }
1139
1267
  }
1140
1268
  return success(contract, "unmeasured", value);
@@ -1415,10 +1543,11 @@ export const RESULT_CONTRACT_ANCHOR_LIMIT = 12;
1415
1543
 
1416
1544
  /**
1417
1545
  * The exact terminal shape each contract accepts, quoted to the model verbatim.
1418
- * This is the one place a contract's wire example is written; agent recipes and
1419
- * repair rounds both cite it, so a prompt cannot drift from its validator.
1546
+ * This is the one place a contract's wire example is written; agent recipes,
1547
+ * repair rounds, and a fleet node's own answer directive all cite it, so a
1548
+ * prompt cannot drift from its validator.
1420
1549
  */
1421
- function resultContractShape(contract: ResultContract): string {
1550
+ export function resultContractShape(contract: ResultContract): string {
1422
1551
  switch (contract.kind) {
1423
1552
  case "architect-plan":
1424
1553
  return `a plan artifact written to ${contract.path}, then optionally {"commitMessage":"...","summary":"..."} describing it`;
@@ -1428,15 +1557,15 @@ function resultContractShape(contract: ResultContract): string {
1428
1557
  // run already failed twice, so it is told the cheaper exit as well.
1429
1558
  return '{"findings":[{"claim":"what you observed","path":"src/file.ts","line":1}],"needsSplit":false,"proposedSubtasks":[]} or {"findings":[],"needsSplit":true,"proposedSubtasks":[{"id":"inspect","task":"Inspect the boundary","dependencies":[],"expectedResultContract":"scout-report","requestedAuthority":"read-only"}]} or, if you cannot produce a citation you are sure of, at least {"findings":[{"claim":"what you observed"}]}, which is accepted as an ungrounded lead rather than losing the run';
1430
1559
  case "verifier-report":
1431
- return '{"verdict":"pass","checks":[{"name":"npm run typecheck","passed":true,"evidence":"exit 0"}]}';
1560
+ return 'one of {"verdict":"pass","checks":[{"name":"npm run typecheck","passed":true,"evidence":"exit 0"}]} or {"verdict":"fail","checks":[{"name":"npm run typecheck","passed":false,"evidence":"exit 1"}]}; verdict legal values are pass | fail and checks[].passed legal values are true | false';
1432
1561
  case "council-report":
1433
1562
  return '{"members":[{"label":"alpha","runId":"run-1","round":1,"answer":"..."}],"synthesis":{"kind":"none"}}';
1434
1563
  case "council-ballot":
1435
1564
  return COUNCIL_BALLOT_SHAPE;
1436
1565
  case "debugger-report":
1437
- return '{"diagnosis":"...","reproduction":"reproduced","evidence":["..."]}';
1566
+ return 'one of {"diagnosis":"...","reproduction":"reproduced","evidence":["..."]}, {"diagnosis":"...","reproduction":"not-reproduced","evidence":["..."]}, or {"diagnosis":"...","reproduction":"unknown","evidence":["..."]}; reproduction legal values are reproduced | not-reproduced | unknown';
1438
1567
  case "research-report":
1439
- return '{"source":"local","findings":[{"claim":"...","evidence":"..."}]}';
1568
+ return 'one of {"source":"local","findings":[{"claim":"...","evidence":"..."}]} or {"source":"external","findings":[{"claim":"...","evidence":"..."}]}; source legal values are local | external';
1440
1569
  case "mutation-report":
1441
1570
  return '{"mutatedPaths":["src/file.ts"],"validations":[{"name":"npm test","passed":true,"evidence":"exit 0"}],"commitMessage":"optional: the commit message for this change","summary":"optional: one line"}';
1442
1571
  case "provenance-report":
@@ -28,6 +28,9 @@ const HOT_RELOAD_FIELDS = new Set<string>([
28
28
  "modelSelector",
29
29
  "terminal.smoothStreaming",
30
30
  "attribution.gitCommits",
31
+ // The bridge reads these on every explicit open; no mux re-detection is
32
+ // needed because the host capability rung remains `panes.enabled`.
33
+ "panes.yazi",
31
34
  // The watchdog registration reads its settings live on every trigger, so
32
35
  // enabling it, retargeting it, or changing its cadence takes effect on the
33
36
  // next turn boundary without a restart or a session-routing patch.
@@ -0,0 +1,9 @@
1
+ /** A queued assignment was intentionally canceled before it acquired capacity. */
2
+ export class AdmissionCanceledError extends Error {
3
+ readonly code = "admission_canceled" as const;
4
+
5
+ constructor(message = "dispatch: admission canceled before a capacity slot opened") {
6
+ super(message);
7
+ this.name = "AdmissionCanceledError";
8
+ }
9
+ }
@@ -2,6 +2,7 @@ import { randomBytes } from "node:crypto";
2
2
  import { endpointLabel } from "../providers/endpoint-capacity.js";
3
3
  import type { ActionClass } from "../safety/action-classifier.js";
4
4
  import type { ScopeSpec } from "../safety/scope.js";
5
+ import { AdmissionCanceledError } from "./admission-error.js";
5
6
  import { createAdmissionQueue } from "./admission-queue.js";
6
7
  import {
7
8
  acquireCapacityLease,
@@ -131,7 +132,12 @@ function describeAdmissionFailure(input: {
131
132
  holders === undefined
132
133
  ? "the supplied capacity snapshot does not attribute the occupied slots"
133
134
  : describeEndpointCapacityHolders(holders);
134
- return `dispatch: admission denied: endpoint '${endpointLabel(input.endpointKey)}' capacity reached (${endpointActive}/${endpointLimit} slots): ${occupancy}; collect in-flight runs or point workers at a second server`;
135
+ const queued = input.queueDepth > 0 ? `, ${input.queueDepth} more queued` : "";
136
+ return [
137
+ `dispatch: admission timed out after ${Math.round(input.waitedMs)}ms waiting for an endpoint slot`,
138
+ ` on '${endpointLabel(input.endpointKey)}' (${endpointActive}/${endpointLimit} slots${queued}): ${occupancy}.`,
139
+ " Wait for the running request to settle or dispatch this work again.",
140
+ ].join("");
135
141
  }
136
142
  }
137
143
  const nodeLimit = input.limits.nodes[input.nodeId];
@@ -202,14 +208,9 @@ export function createCapacityAdmissionController(options: {
202
208
  admittedLease = request.value();
203
209
  return true;
204
210
  } catch (error) {
205
- // Endpoint saturation is a named refusal because waiting would keep a
206
- // model request queued behind the same inference scheduler. Existing
207
- // node and global admission retain their bounded wait behavior.
208
- if (error instanceof Error && /endpoint '.*' capacity reached/u.test(error.message)) {
209
- queue.fail(request.requestId, error);
210
- return false;
211
- }
212
- // Node and global capacity can wait. Anything else fails the request.
211
+ // Saturation at any capacity dimension is transient and keeps its
212
+ // assignment in the one bounded admission queue. Unavailable capacity,
213
+ // draining, corrupt state, and every other error still fail immediately.
213
214
  if (error instanceof Error && /capacity reached/.test(error.message)) return false;
214
215
  queue.fail(request.requestId, error instanceof Error ? error : new Error(String(error)));
215
216
  return false;
@@ -278,18 +279,18 @@ export function createCapacityAdmissionController(options: {
278
279
  pumpNow();
279
280
  const outcome = await outcomePromise;
280
281
  if (outcome.state !== "admitted") {
281
- throw new Error(
282
- describeAdmissionFailure({
283
- state: outcome.state,
284
- nodeId: input.nodeId,
285
- ...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
286
- waitedMs: now() - queuedAt,
287
- overdueAtQueueMs: queuedAt - input.deadlineAt,
288
- queueDepth: queue.size(),
289
- limits: options.limits(),
290
- usage: options.usage?.() ?? capacityLeaseUsage({ nowMs: now() }),
291
- }),
292
- );
282
+ const detail = describeAdmissionFailure({
283
+ state: outcome.state,
284
+ nodeId: input.nodeId,
285
+ ...(input.endpointKey !== undefined ? { endpointKey: input.endpointKey } : {}),
286
+ waitedMs: now() - queuedAt,
287
+ overdueAtQueueMs: queuedAt - input.deadlineAt,
288
+ queueDepth: queue.size(),
289
+ limits: options.limits(),
290
+ usage: options.usage?.() ?? capacityLeaseUsage({ nowMs: now() }),
291
+ });
292
+ if (outcome.state === "canceled") throw new AdmissionCanceledError(detail);
293
+ throw new Error(detail);
293
294
  }
294
295
  const lease = acquired.get(input.assignmentId);
295
296
  if (!lease) throw new Error("dispatch: admitted request has no capacity lease");
@@ -263,6 +263,26 @@ export function describeEndpointCapacityHolders(holders: EndpointCapacityHolders
263
263
  return `${named} ${total === 1 ? "holds" : "hold"} ${total === 1 ? "the slot" : "the slots"}`;
264
264
  }
265
265
 
266
+ /**
267
+ * The ways past a saturated inference endpoint, in the order an operator can
268
+ * act on them.
269
+ *
270
+ * `maxConcurrentRequests` is named because the conservative default of one slot
271
+ * is a guess about an unprobed server, and on a server started with
272
+ * `--parallel 4` it is simply wrong. Without that sentence the denial reads as
273
+ * a hardware limit and the operator has no reason to look for a setting.
274
+ *
275
+ * `rosterFloor` is the smallest same-wave width the caller's topology can have.
276
+ * A council runs its whole roster in one wave, so "dispatch fewer at once" is
277
+ * not a move it has; saying so keeps the remedy honest.
278
+ */
279
+ export function endpointCapacityRemedy(input?: { rosterFloor?: number }): string {
280
+ const ways =
281
+ "set this target's maxConcurrentRequests to the slot count the server was started with, collect in-flight runs, or point workers at a second server";
282
+ if (input?.rosterFloor === undefined) return `reduce the same-wave worker count, ${ways}`;
283
+ return `a council runs its whole roster in one wave and cannot go below ${input.rosterFloor} members, so ${ways}`;
284
+ }
285
+
266
286
  function assertCapacity(
267
287
  file: CapacityStateFile,
268
288
  nodeId: string,
@@ -299,7 +319,7 @@ function assertCapacity(
299
319
  const used = holders.leases + holders.reservations + holders.foregroundStreams;
300
320
  if (used >= endpointLimit)
301
321
  throw new Error(
302
- `dispatch: admission denied: endpoint '${endpointLabel(endpointKey)}' capacity reached (${used}/${endpointLimit} slots): ${describeEndpointCapacityHolders(holders)}; collect in-flight runs or point workers at a second server`,
322
+ `dispatch: admission denied: endpoint '${endpointLabel(endpointKey)}' capacity reached (${used}/${endpointLimit} slots): ${describeEndpointCapacityHolders(holders)}; ${endpointCapacityRemedy()}`,
303
323
  );
304
324
  }
305
325
  }