@namzu/cli 29.0.1 → 29.1.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 (384) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +20 -0
  3. package/dist/browser/commands.d.ts +29 -0
  4. package/dist/browser/commands.d.ts.map +1 -0
  5. package/dist/browser/commands.js +361 -0
  6. package/dist/browser/commands.js.map +1 -0
  7. package/dist/browser/control.d.ts +79 -0
  8. package/dist/browser/control.d.ts.map +1 -0
  9. package/dist/browser/control.js +140 -0
  10. package/dist/browser/control.js.map +1 -0
  11. package/dist/browser/describe.d.ts +12 -0
  12. package/dist/browser/describe.d.ts.map +1 -0
  13. package/dist/browser/describe.js +27 -0
  14. package/dist/browser/describe.js.map +1 -0
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +29 -2
  17. package/dist/cli.js.map +1 -1
  18. package/dist/commands/acp.d.ts.map +1 -1
  19. package/dist/commands/acp.js +1 -0
  20. package/dist/commands/acp.js.map +1 -1
  21. package/dist/commands/browser.d.ts +7 -0
  22. package/dist/commands/browser.d.ts.map +1 -0
  23. package/dist/commands/browser.js +16 -0
  24. package/dist/commands/browser.js.map +1 -0
  25. package/dist/commands/drain.d.ts +2 -1
  26. package/dist/commands/drain.d.ts.map +1 -1
  27. package/dist/commands/drain.js +6 -3
  28. package/dist/commands/drain.js.map +1 -1
  29. package/dist/commands/exec-flags.d.ts +2 -1
  30. package/dist/commands/exec-flags.d.ts.map +1 -1
  31. package/dist/commands/exec-flags.js +6 -3
  32. package/dist/commands/exec-flags.js.map +1 -1
  33. package/dist/commands/exec-json.d.ts.map +1 -1
  34. package/dist/commands/exec-json.js +2 -1
  35. package/dist/commands/exec-json.js.map +1 -1
  36. package/dist/commands/exec.d.ts.map +1 -1
  37. package/dist/commands/exec.js +2 -1
  38. package/dist/commands/exec.js.map +1 -1
  39. package/dist/commands/host-queries.d.ts.map +1 -1
  40. package/dist/commands/host-queries.js +10 -3
  41. package/dist/commands/host-queries.js.map +1 -1
  42. package/dist/commands/schedule.d.ts +9 -0
  43. package/dist/commands/schedule.d.ts.map +1 -0
  44. package/dist/commands/schedule.js +126 -0
  45. package/dist/commands/schedule.js.map +1 -0
  46. package/dist/commands/serve.d.ts +3 -1
  47. package/dist/commands/serve.d.ts.map +1 -1
  48. package/dist/commands/serve.js +5 -3
  49. package/dist/commands/serve.js.map +1 -1
  50. package/dist/commands/skills.d.ts +10 -0
  51. package/dist/commands/skills.d.ts.map +1 -1
  52. package/dist/commands/skills.js +23 -7
  53. package/dist/commands/skills.js.map +1 -1
  54. package/dist/commands/upgrade.d.ts +5 -0
  55. package/dist/commands/upgrade.d.ts.map +1 -1
  56. package/dist/commands/upgrade.js +11 -0
  57. package/dist/commands/upgrade.js.map +1 -1
  58. package/dist/config/load.d.ts +39 -0
  59. package/dist/config/load.d.ts.map +1 -1
  60. package/dist/config/load.js +232 -1
  61. package/dist/config/load.js.map +1 -1
  62. package/dist/config/schema.d.ts +78 -0
  63. package/dist/config/schema.d.ts.map +1 -1
  64. package/dist/config/schema.js.map +1 -1
  65. package/dist/config/user-config.d.ts +23 -0
  66. package/dist/config/user-config.d.ts.map +1 -0
  67. package/dist/config/user-config.js +48 -0
  68. package/dist/config/user-config.js.map +1 -0
  69. package/dist/context/capabilities.d.ts +1 -1
  70. package/dist/context/capabilities.d.ts.map +1 -1
  71. package/dist/context/capabilities.js +1 -0
  72. package/dist/context/capabilities.js.map +1 -1
  73. package/dist/doctor/checks/browser.d.ts +15 -0
  74. package/dist/doctor/checks/browser.d.ts.map +1 -0
  75. package/dist/doctor/checks/browser.js +63 -0
  76. package/dist/doctor/checks/browser.js.map +1 -0
  77. package/dist/doctor/checks/index.d.ts +4 -1
  78. package/dist/doctor/checks/index.d.ts.map +1 -1
  79. package/dist/doctor/checks/index.js +8 -1
  80. package/dist/doctor/checks/index.js.map +1 -1
  81. package/dist/doctor/checks/scheduler.d.ts +10 -0
  82. package/dist/doctor/checks/scheduler.d.ts.map +1 -0
  83. package/dist/doctor/checks/scheduler.js +74 -0
  84. package/dist/doctor/checks/scheduler.js.map +1 -0
  85. package/dist/index.d.ts +1 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -1
  88. package/dist/index.js.map +1 -1
  89. package/dist/integrations/notifications/desktop/freedesktop.d.ts +10 -0
  90. package/dist/integrations/notifications/desktop/freedesktop.d.ts.map +1 -0
  91. package/dist/integrations/notifications/desktop/freedesktop.js +33 -0
  92. package/dist/integrations/notifications/desktop/freedesktop.js.map +1 -0
  93. package/dist/integrations/notifications/desktop/macos.d.ts +6 -0
  94. package/dist/integrations/notifications/desktop/macos.d.ts.map +1 -0
  95. package/dist/integrations/notifications/desktop/macos.js +17 -0
  96. package/dist/integrations/notifications/desktop/macos.js.map +1 -0
  97. package/dist/integrations/notifications/desktop/sanitize.d.ts +16 -0
  98. package/dist/integrations/notifications/desktop/sanitize.d.ts.map +1 -0
  99. package/dist/integrations/notifications/desktop/sanitize.js +49 -0
  100. package/dist/integrations/notifications/desktop/sanitize.js.map +1 -0
  101. package/dist/integrations/notifications/desktop/throttle.d.ts +10 -0
  102. package/dist/integrations/notifications/desktop/throttle.d.ts.map +1 -0
  103. package/dist/integrations/notifications/desktop/throttle.js +39 -0
  104. package/dist/integrations/notifications/desktop/throttle.js.map +1 -0
  105. package/dist/integrations/notifications/desktop/windows-toast.d.ts +23 -0
  106. package/dist/integrations/notifications/desktop/windows-toast.d.ts.map +1 -0
  107. package/dist/integrations/notifications/desktop/windows-toast.js +56 -0
  108. package/dist/integrations/notifications/desktop/windows-toast.js.map +1 -0
  109. package/dist/integrations/notifications/desktop.d.ts +82 -0
  110. package/dist/integrations/notifications/desktop.d.ts.map +1 -0
  111. package/dist/integrations/notifications/desktop.js +198 -0
  112. package/dist/integrations/notifications/desktop.js.map +1 -0
  113. package/dist/integrations/resident/session-step.d.ts.map +1 -1
  114. package/dist/integrations/resident/session-step.js +1 -0
  115. package/dist/integrations/resident/session-step.js.map +1 -1
  116. package/dist/integrations/subagents/runtime.d.ts +6 -0
  117. package/dist/integrations/subagents/runtime.d.ts.map +1 -1
  118. package/dist/integrations/subagents/runtime.js +14 -5
  119. package/dist/integrations/subagents/runtime.js.map +1 -1
  120. package/dist/permissions/browser-sites.d.ts +95 -0
  121. package/dist/permissions/browser-sites.d.ts.map +1 -0
  122. package/dist/permissions/browser-sites.js +270 -0
  123. package/dist/permissions/browser-sites.js.map +1 -0
  124. package/dist/schedule/build.d.ts +79 -0
  125. package/dist/schedule/build.d.ts.map +1 -0
  126. package/dist/schedule/build.js +246 -0
  127. package/dist/schedule/build.js.map +1 -0
  128. package/dist/schedule/changes.d.ts +22 -0
  129. package/dist/schedule/changes.d.ts.map +1 -0
  130. package/dist/schedule/changes.js +55 -0
  131. package/dist/schedule/changes.js.map +1 -0
  132. package/dist/schedule/commands/add.d.ts +24 -0
  133. package/dist/schedule/commands/add.d.ts.map +1 -0
  134. package/dist/schedule/commands/add.js +459 -0
  135. package/dist/schedule/commands/add.js.map +1 -0
  136. package/dist/schedule/commands/args.d.ts +22 -0
  137. package/dist/schedule/commands/args.d.ts.map +1 -0
  138. package/dist/schedule/commands/args.js +80 -0
  139. package/dist/schedule/commands/args.js.map +1 -0
  140. package/dist/schedule/commands/confirm-prompt.d.ts +6 -0
  141. package/dist/schedule/commands/confirm-prompt.d.ts.map +1 -0
  142. package/dist/schedule/commands/confirm-prompt.js +21 -0
  143. package/dist/schedule/commands/confirm-prompt.js.map +1 -0
  144. package/dist/schedule/commands/lifecycle.d.ts +39 -0
  145. package/dist/schedule/commands/lifecycle.d.ts.map +1 -0
  146. package/dist/schedule/commands/lifecycle.js +585 -0
  147. package/dist/schedule/commands/lifecycle.js.map +1 -0
  148. package/dist/schedule/commands/list.d.ts +33 -0
  149. package/dist/schedule/commands/list.d.ts.map +1 -0
  150. package/dist/schedule/commands/list.js +202 -0
  151. package/dist/schedule/commands/list.js.map +1 -0
  152. package/dist/schedule/commands/logs.d.ts +7 -0
  153. package/dist/schedule/commands/logs.d.ts.map +1 -0
  154. package/dist/schedule/commands/logs.js +106 -0
  155. package/dist/schedule/commands/logs.js.map +1 -0
  156. package/dist/schedule/commands/service.d.ts +16 -0
  157. package/dist/schedule/commands/service.d.ts.map +1 -0
  158. package/dist/schedule/commands/service.js +458 -0
  159. package/dist/schedule/commands/service.js.map +1 -0
  160. package/dist/schedule/daemon/daemon.d.ts +156 -0
  161. package/dist/schedule/daemon/daemon.d.ts.map +1 -0
  162. package/dist/schedule/daemon/daemon.js +1248 -0
  163. package/dist/schedule/daemon/daemon.js.map +1 -0
  164. package/dist/schedule/daemon/endpoint.d.ts +38 -0
  165. package/dist/schedule/daemon/endpoint.d.ts.map +1 -0
  166. package/dist/schedule/daemon/endpoint.js +158 -0
  167. package/dist/schedule/daemon/endpoint.js.map +1 -0
  168. package/dist/schedule/daemon/exit.d.ts +8 -0
  169. package/dist/schedule/daemon/exit.d.ts.map +1 -0
  170. package/dist/schedule/daemon/exit.js +8 -0
  171. package/dist/schedule/daemon/exit.js.map +1 -0
  172. package/dist/schedule/daemon/log.d.ts +12 -0
  173. package/dist/schedule/daemon/log.d.ts.map +1 -0
  174. package/dist/schedule/daemon/log.js +53 -0
  175. package/dist/schedule/daemon/log.js.map +1 -0
  176. package/dist/schedule/daemon/notify.d.ts +31 -0
  177. package/dist/schedule/daemon/notify.d.ts.map +1 -0
  178. package/dist/schedule/daemon/notify.js +89 -0
  179. package/dist/schedule/daemon/notify.js.map +1 -0
  180. package/dist/schedule/daemon/retention.d.ts +16 -0
  181. package/dist/schedule/daemon/retention.d.ts.map +1 -0
  182. package/dist/schedule/daemon/retention.js +46 -0
  183. package/dist/schedule/daemon/retention.js.map +1 -0
  184. package/dist/schedule/daemon/sessions.d.ts +23 -0
  185. package/dist/schedule/daemon/sessions.d.ts.map +1 -0
  186. package/dist/schedule/daemon/sessions.js +52 -0
  187. package/dist/schedule/daemon/sessions.js.map +1 -0
  188. package/dist/schedule/env.d.ts +28 -0
  189. package/dist/schedule/env.d.ts.map +1 -0
  190. package/dist/schedule/env.js +99 -0
  191. package/dist/schedule/env.js.map +1 -0
  192. package/dist/schedule/fire/browser-preflight.d.ts +41 -0
  193. package/dist/schedule/fire/browser-preflight.d.ts.map +1 -0
  194. package/dist/schedule/fire/browser-preflight.js +73 -0
  195. package/dist/schedule/fire/browser-preflight.js.map +1 -0
  196. package/dist/schedule/fire/fire.d.ts +78 -0
  197. package/dist/schedule/fire/fire.d.ts.map +1 -0
  198. package/dist/schedule/fire/fire.js +482 -0
  199. package/dist/schedule/fire/fire.js.map +1 -0
  200. package/dist/schedule/fire/result.d.ts +13 -0
  201. package/dist/schedule/fire/result.d.ts.map +1 -0
  202. package/dist/schedule/fire/result.js +18 -0
  203. package/dist/schedule/fire/result.js.map +1 -0
  204. package/dist/schedule/fire/unattended-note.d.ts +22 -0
  205. package/dist/schedule/fire/unattended-note.d.ts.map +1 -0
  206. package/dist/schedule/fire/unattended-note.js +46 -0
  207. package/dist/schedule/fire/unattended-note.js.map +1 -0
  208. package/dist/schedule/floor.d.ts +116 -0
  209. package/dist/schedule/floor.d.ts.map +1 -0
  210. package/dist/schedule/floor.js +1051 -0
  211. package/dist/schedule/floor.js.map +1 -0
  212. package/dist/schedule/folder.d.ts +34 -0
  213. package/dist/schedule/folder.d.ts.map +1 -0
  214. package/dist/schedule/folder.js +84 -0
  215. package/dist/schedule/folder.js.map +1 -0
  216. package/dist/schedule/paths.d.ts +49 -0
  217. package/dist/schedule/paths.d.ts.map +1 -0
  218. package/dist/schedule/paths.js +53 -0
  219. package/dist/schedule/paths.js.map +1 -0
  220. package/dist/schedule/policy.d.ts +141 -0
  221. package/dist/schedule/policy.d.ts.map +1 -0
  222. package/dist/schedule/policy.js +378 -0
  223. package/dist/schedule/policy.js.map +1 -0
  224. package/dist/schedule/resume-command.d.ts +36 -0
  225. package/dist/schedule/resume-command.d.ts.map +1 -0
  226. package/dist/schedule/resume-command.js +72 -0
  227. package/dist/schedule/resume-command.js.map +1 -0
  228. package/dist/schedule/service/detect.d.ts +22 -0
  229. package/dist/schedule/service/detect.d.ts.map +1 -0
  230. package/dist/schedule/service/detect.js +29 -0
  231. package/dist/schedule/service/detect.js.map +1 -0
  232. package/dist/schedule/service/index.d.ts +48 -0
  233. package/dist/schedule/service/index.d.ts.map +1 -0
  234. package/dist/schedule/service/index.js +352 -0
  235. package/dist/schedule/service/index.js.map +1 -0
  236. package/dist/schedule/service/launchd.d.ts +24 -0
  237. package/dist/schedule/service/launchd.d.ts.map +1 -0
  238. package/dist/schedule/service/launchd.js +84 -0
  239. package/dist/schedule/service/launchd.js.map +1 -0
  240. package/dist/schedule/service/manifest.d.ts +44 -0
  241. package/dist/schedule/service/manifest.d.ts.map +1 -0
  242. package/dist/schedule/service/manifest.js +16 -0
  243. package/dist/schedule/service/manifest.js.map +1 -0
  244. package/dist/schedule/service/names.d.ts +9 -0
  245. package/dist/schedule/service/names.d.ts.map +1 -0
  246. package/dist/schedule/service/names.js +22 -0
  247. package/dist/schedule/service/names.js.map +1 -0
  248. package/dist/schedule/service/quote.d.ts +24 -0
  249. package/dist/schedule/service/quote.d.ts.map +1 -0
  250. package/dist/schedule/service/quote.js +64 -0
  251. package/dist/schedule/service/quote.js.map +1 -0
  252. package/dist/schedule/service/runner.d.ts +17 -0
  253. package/dist/schedule/service/runner.d.ts.map +1 -0
  254. package/dist/schedule/service/runner.js +31 -0
  255. package/dist/schedule/service/runner.js.map +1 -0
  256. package/dist/schedule/service/systemd.d.ts +37 -0
  257. package/dist/schedule/service/systemd.d.ts.map +1 -0
  258. package/dist/schedule/service/systemd.js +96 -0
  259. package/dist/schedule/service/systemd.js.map +1 -0
  260. package/dist/schedule/service/windows-task.d.ts +51 -0
  261. package/dist/schedule/service/windows-task.d.ts.map +1 -0
  262. package/dist/schedule/service/windows-task.js +134 -0
  263. package/dist/schedule/service/windows-task.js.map +1 -0
  264. package/dist/schedule/service/wsl.d.ts +57 -0
  265. package/dist/schedule/service/wsl.d.ts.map +1 -0
  266. package/dist/schedule/service/wsl.js +111 -0
  267. package/dist/schedule/service/wsl.js.map +1 -0
  268. package/dist/schedule/store/atomic.d.ts +43 -0
  269. package/dist/schedule/store/atomic.d.ts.map +1 -0
  270. package/dist/schedule/store/atomic.js +135 -0
  271. package/dist/schedule/store/atomic.js.map +1 -0
  272. package/dist/schedule/store/claims.d.ts +26 -0
  273. package/dist/schedule/store/claims.d.ts.map +1 -0
  274. package/dist/schedule/store/claims.js +57 -0
  275. package/dist/schedule/store/claims.js.map +1 -0
  276. package/dist/schedule/store/digest.d.ts +18 -0
  277. package/dist/schedule/store/digest.d.ts.map +1 -0
  278. package/dist/schedule/store/digest.js +139 -0
  279. package/dist/schedule/store/digest.js.map +1 -0
  280. package/dist/schedule/store/history.d.ts +21 -0
  281. package/dist/schedule/store/history.d.ts.map +1 -0
  282. package/dist/schedule/store/history.js +114 -0
  283. package/dist/schedule/store/history.js.map +1 -0
  284. package/dist/schedule/store/jobs.d.ts +61 -0
  285. package/dist/schedule/store/jobs.d.ts.map +1 -0
  286. package/dist/schedule/store/jobs.js +210 -0
  287. package/dist/schedule/store/jobs.js.map +1 -0
  288. package/dist/schedule/store/state.d.ts +19 -0
  289. package/dist/schedule/store/state.d.ts.map +1 -0
  290. package/dist/schedule/store/state.js +38 -0
  291. package/dist/schedule/store/state.js.map +1 -0
  292. package/dist/schedule/types.d.ts +256 -0
  293. package/dist/schedule/types.d.ts.map +1 -0
  294. package/dist/schedule/types.js +9 -0
  295. package/dist/schedule/types.js.map +1 -0
  296. package/dist/skills/catalog.d.ts +96 -0
  297. package/dist/skills/catalog.d.ts.map +1 -0
  298. package/dist/skills/catalog.js +226 -0
  299. package/dist/skills/catalog.js.map +1 -0
  300. package/dist/skills/save.d.ts +148 -0
  301. package/dist/skills/save.d.ts.map +1 -0
  302. package/dist/skills/save.js +344 -0
  303. package/dist/skills/save.js.map +1 -0
  304. package/dist/skills/store.d.ts +103 -16
  305. package/dist/skills/store.d.ts.map +1 -1
  306. package/dist/skills/store.js +227 -30
  307. package/dist/skills/store.js.map +1 -1
  308. package/dist/tui/App.d.ts.map +1 -1
  309. package/dist/tui/App.js +591 -72
  310. package/dist/tui/App.js.map +1 -1
  311. package/dist/tui/LiveActivity.d.ts +8 -1
  312. package/dist/tui/LiveActivity.d.ts.map +1 -1
  313. package/dist/tui/LiveActivity.js +20 -4
  314. package/dist/tui/LiveActivity.js.map +1 -1
  315. package/dist/tui/PermissionOverlay.d.ts +24 -4
  316. package/dist/tui/PermissionOverlay.d.ts.map +1 -1
  317. package/dist/tui/PermissionOverlay.js +113 -15
  318. package/dist/tui/PermissionOverlay.js.map +1 -1
  319. package/dist/tui/SaveSkillOverlay.d.ts +33 -0
  320. package/dist/tui/SaveSkillOverlay.d.ts.map +1 -0
  321. package/dist/tui/SaveSkillOverlay.js +118 -0
  322. package/dist/tui/SaveSkillOverlay.js.map +1 -0
  323. package/dist/tui/agent.d.ts +185 -5
  324. package/dist/tui/agent.d.ts.map +1 -1
  325. package/dist/tui/agent.js +325 -54
  326. package/dist/tui/agent.js.map +1 -1
  327. package/dist/tui/browser-notices.d.ts +33 -0
  328. package/dist/tui/browser-notices.d.ts.map +1 -0
  329. package/dist/tui/browser-notices.js +102 -0
  330. package/dist/tui/browser-notices.js.map +1 -0
  331. package/dist/tui/permission-review.d.ts.map +1 -1
  332. package/dist/tui/permission-review.js +53 -4
  333. package/dist/tui/permission-review.js.map +1 -1
  334. package/dist/tui/schedule/host-commands.d.ts +34 -0
  335. package/dist/tui/schedule/host-commands.d.ts.map +1 -0
  336. package/dist/tui/schedule/host-commands.js +309 -0
  337. package/dist/tui/schedule/host-commands.js.map +1 -0
  338. package/dist/tui/schedule/integration.d.ts +62 -0
  339. package/dist/tui/schedule/integration.d.ts.map +1 -0
  340. package/dist/tui/schedule/integration.js +145 -0
  341. package/dist/tui/schedule/integration.js.map +1 -0
  342. package/dist/tui/schedule/loop-host.d.ts +42 -0
  343. package/dist/tui/schedule/loop-host.d.ts.map +1 -0
  344. package/dist/tui/schedule/loop-host.js +153 -0
  345. package/dist/tui/schedule/loop-host.js.map +1 -0
  346. package/dist/tui/schedule/resume.d.ts +122 -0
  347. package/dist/tui/schedule/resume.d.ts.map +1 -0
  348. package/dist/tui/schedule/resume.js +324 -0
  349. package/dist/tui/schedule/resume.js.map +1 -0
  350. package/dist/tui/schedule/startup.d.ts +11 -0
  351. package/dist/tui/schedule/startup.d.ts.map +1 -0
  352. package/dist/tui/schedule/startup.js +121 -0
  353. package/dist/tui/schedule/startup.js.map +1 -0
  354. package/dist/tui/schedule/tool-host.d.ts +44 -0
  355. package/dist/tui/schedule/tool-host.d.ts.map +1 -0
  356. package/dist/tui/schedule/tool-host.js +294 -0
  357. package/dist/tui/schedule/tool-host.js.map +1 -0
  358. package/dist/tui/skills/learning.d.ts +74 -0
  359. package/dist/tui/skills/learning.d.ts.map +1 -0
  360. package/dist/tui/skills/learning.js +131 -0
  361. package/dist/tui/skills/learning.js.map +1 -0
  362. package/dist/tui/skills/suggest-setting.d.ts +22 -0
  363. package/dist/tui/skills/suggest-setting.d.ts.map +1 -0
  364. package/dist/tui/skills/suggest-setting.js +55 -0
  365. package/dist/tui/skills/suggest-setting.js.map +1 -0
  366. package/dist/tui/skills/suggestion-ledger.d.ts +35 -0
  367. package/dist/tui/skills/suggestion-ledger.d.ts.map +1 -0
  368. package/dist/tui/skills/suggestion-ledger.js +52 -0
  369. package/dist/tui/skills/suggestion-ledger.js.map +1 -0
  370. package/dist/tui/slashCommands.d.ts +20 -0
  371. package/dist/tui/slashCommands.d.ts.map +1 -1
  372. package/dist/tui/slashCommands.js +76 -2
  373. package/dist/tui/slashCommands.js.map +1 -1
  374. package/dist/tui/turn-interruption.d.ts +20 -1
  375. package/dist/tui/turn-interruption.d.ts.map +1 -1
  376. package/dist/tui/turn-interruption.js +43 -0
  377. package/dist/tui/turn-interruption.js.map +1 -1
  378. package/dist/tui/types.d.ts +9 -1
  379. package/dist/tui/types.d.ts.map +1 -1
  380. package/package.json +6 -4
  381. package/skills/README.md +15 -0
  382. package/skills/browser-automation/SKILL.md +84 -0
  383. package/skills/schedule-task/SKILL.md +140 -0
  384. package/skills/skill-creator/SKILL.md +101 -0
package/dist/tui/agent.js CHANGED
@@ -24,10 +24,11 @@ import { countListedResults, webActivityFromInput, webActivityKind, webCallTitle
24
24
  * `emptySession()` whose `send()` yields a single error event so the UI
25
25
  * renders an actionable hint rather than crashing.
26
26
  */
27
- import { BOOT_EVENT_NAMES, BackgroundJobRegistry, DiskSessionCheckpointStore, DiskSessionLog, DiskTaskStore, EVENT_NAME_ATTRIBUTE, GuardedFetchProvider, InMemorySessionLog, MarkdownMemoryStore, PromptContributionRegistry, ProviderRegistry, SESSION_GOAL_TOOL_NAMES, SessionPaths, ToolRegistry, WebFetchTool, abandonTurn, batchNeedsReview, buildAskUserQuestionTool, buildMemoryTools, buildResidentHistoryTools, buildResidentToolEvidenceTools, buildSessionGoalTools, compactNow, compactSession, createComputerUseTool, createFileReadTracker, createMemoryPromoter, createMemoryRecallStep, createResidentStepContext, createResidentStepContributions, createReviewHandler, createToolPresenter, ensureProject, generateSessionId, generateTenantId, generateTopicId, generateTurnId, getBuiltinTools, isReviewExempt, isTurnInProgressError, query, resumeSession, seedObservationLedger, webGuidanceContribution, withProviderFallback, } from '@namzu/sdk';
27
+ import { BOOT_EVENT_NAMES, BackgroundJobRegistry, DiskSessionCheckpointStore, DiskSessionLog, DiskTaskStore, EVENT_NAME_ATTRIBUTE, GuardedFetchProvider, InMemorySessionLog, MarkdownMemoryStore, PromptContributionRegistry, ProviderRegistry, SCHEDULE_TOOL_NAME, SESSION_GOAL_TOOL_NAMES, SessionPaths, SkillTool, ToolRegistry, WebFetchTool, abandonTurn, batchNeedsReview, buildAskUserQuestionTool, buildMemoryTools, buildResidentHistoryTools, buildResidentToolEvidenceTools, buildSessionGoalTools, compactNow, compactSession, createBrowserTools, createComputerUseTool, createFileReadTracker, createMemoryPromoter, createMemoryRecallStep, createResidentStepContext, createResidentStepContributions, createReviewHandler, createToolPresenter, ensureProject, generateSessionId, generateTenantId, generateTopicId, generateTurnId, getBuiltinTools, isReviewExempt, isTurnInProgressError, query, resolveContextWindow, resumeSession, seedObservationLedger, webGuidanceContribution, withProviderFallback, } from '@namzu/sdk';
28
28
  import { SubprocessComputerUseHost } from '@namzu/computer-use';
29
29
  import { realpath, stat } from 'node:fs/promises';
30
30
  import { parse, resolve } from 'node:path';
31
+ import { createBrowserControl, } from '../browser/control.js';
31
32
  import { FileCheckpointStore } from '../checkpoints/store.js';
32
33
  import { CHECKPOINTED_TOOLS, withCheckpoints } from '../checkpoints/wrap.js';
33
34
  import { configuredPassthroughTools, resolveToolResultScreens, unmatchedPassthroughTools, } from '../config/tool-result-screens.js';
@@ -62,13 +63,15 @@ import { discoverAgentDefinitions } from '../integrations/subagents/definitions.
62
63
  import { prepareDelegatedEffort } from '../integrations/subagents/model-effort.js';
63
64
  import { resolveSubagentParent } from '../integrations/subagents/parent.js';
64
65
  import { replaySavedChildrenFor } from '../integrations/subagents/replay.js';
65
- import { createSubagentRuntime } from '../integrations/subagents/runtime.js';
66
+ import { createSubagentRuntime, } from '../integrations/subagents/runtime.js';
66
67
  import { createSavedAgentHistory, createSavedAgentsStep, } from '../integrations/subagents/saved-agents.js';
67
68
  import { cliLogger } from '../logging.js';
68
69
  import { formatMemoryDiagnostics } from '../memory/presentation.js';
69
70
  import { composeMemoryPrompt, readMemory } from '../memory/store.js';
70
71
  import { composeStoredMemoryPrompt, describeCuratedNotesImport, describeMemoryMigration, importCuratedNotes, migrateMemoryOnce, saveTypedNote, } from '../memory/typed.js';
71
72
  import { createLiveModeControl, permissionChangeReason, } from '../permissions/live-mode.js';
73
+ import { createSessionSkillCatalog } from '../skills/catalog.js';
74
+ import { SAVE_SKILL_TOOL_NAME } from '../skills/save.js';
72
75
  import { projectTurnConversation } from './conversation-history.js';
73
76
  import { buildSwitchModelTool } from './model-switch-tool.js';
74
77
  import { resolveModelSwitch } from './model-switch.js';
@@ -287,7 +290,7 @@ async function drainIterator(iterator) {
287
290
  * Read preferences + run discovery once. Returned context drives the
288
291
  * App's lifecycle decision: ready / picker / unhealthy.
289
292
  */
290
- export async function probeAgentSession() {
293
+ export async function probeAgentSession(options = {}) {
291
294
  const read = readPreferences();
292
295
  // Bracketed in the log because this is where a boot has stalled without
293
296
  // a record on either side: it reads credential files, and on WSL it asks
@@ -295,7 +298,7 @@ export async function probeAgentSession() {
295
298
  // and nothing after is this step.
296
299
  const discoveryStartedAt = Date.now();
297
300
  cliLogger().debug('discovering provider credentials');
298
- const detected = await discoverProviders();
301
+ const detected = await discoverProviders(options.env ? { env: options.env } : {});
299
302
  cliLogger().debug('provider credentials discovered', {
300
303
  'namzu.boot.discovery_ms': Date.now() - discoveryStartedAt,
301
304
  'namzu.boot.detected_count': detected.length,
@@ -376,6 +379,9 @@ const EAGER_TOOLS_WHEN_DEFERRED = [
376
379
  'web_fetch',
377
380
  'search_conversation',
378
381
  'read_conversation',
382
+ // The skills manifest tells the model to load a skill through this tool;
383
+ // deferring it would make every skill a two-step search first.
384
+ 'skill',
379
385
  'search_resident_history',
380
386
  'read_resident_history',
381
387
  'search_resident_tools',
@@ -483,13 +489,6 @@ function buildToolRegistry(paths, backgroundJobs, checkpoints, screens) {
483
489
  // after runtime tools are registered. Ordinary CLI task tools are active.
484
490
  return { registry, memoryStore, memoryDirectory: directory };
485
491
  }
486
- /** Refresh plugin skill metadata before each provider operation. */
487
- async function currentPluginSkills(registry) {
488
- const names = registry.list().map((skill) => skill.metadata.name);
489
- for (const name of names)
490
- await registry.load(name, 'metadata');
491
- return registry.list();
492
- }
493
492
  export async function createAgentSession(prefs, detected, options = {}) {
494
493
  const fileObservations = new Map();
495
494
  /**
@@ -906,7 +905,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
906
905
  let lastSendInteractive = false;
907
906
  const boundaryFor = (interactive) => ({
908
907
  ...(sandbox.provider && sandbox.environment
909
- ? { sandbox: { environment: sandbox.environment, enforced: sandbox.enforced } }
908
+ ? {
909
+ sandbox: {
910
+ environment: sandbox.environment,
911
+ enforced: sandbox.enforced,
912
+ },
913
+ }
910
914
  : {}),
911
915
  escape: sandboxEscape === 'refuse'
912
916
  ? 'refused'
@@ -962,7 +966,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
962
966
  */
963
967
  const storedMemoryPrompt = async () => {
964
968
  try {
965
- return { prompt: composeStoredMemoryPrompt(await memoryStore.readIndex()) };
969
+ return {
970
+ prompt: composeStoredMemoryPrompt(await memoryStore.readIndex()),
971
+ };
966
972
  }
967
973
  catch (error) {
968
974
  return {
@@ -1014,6 +1020,26 @@ export async function createAgentSession(prefs, detected, options = {}) {
1014
1020
  }));
1015
1021
  }
1016
1022
  }
1023
+ // The browser: mounted for a surface that asked for it, never launched
1024
+ // here. The host detects the engine now and starts the browser on the
1025
+ // model's first call. Parent session only — sub-agents build their own
1026
+ // registries below and never receive it.
1027
+ const browserPackage = capabilities.find((probe) => probe.specifier === '@namzu/browser');
1028
+ let browserControl;
1029
+ let browserError;
1030
+ if (options.browser && browserPackage?.state === 'present') {
1031
+ try {
1032
+ const { PlaywrightBrowserHost } = await import('@namzu/browser');
1033
+ browserControl = createBrowserControl(PlaywrightBrowserHost, options.browser);
1034
+ registry.register(createBrowserTools(browserControl.host));
1035
+ }
1036
+ catch (error) {
1037
+ browserError = error instanceof Error ? error : new Error(String(error));
1038
+ }
1039
+ }
1040
+ const browserUnavailable = browserError !== undefined
1041
+ ? describeError(browserError)
1042
+ : browserControl?.status().unavailableReason;
1017
1043
  // Registered only on the main session path. Sub-agents call
1018
1044
  // `buildToolRegistry` directly below, so they never receive these tools.
1019
1045
  // Per-send denial further keeps the schemas out of ordinary human turns.
@@ -1069,6 +1095,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
1069
1095
  sandboxReady: sandbox.provider !== undefined,
1070
1096
  computerUseReady: computerUseHost !== undefined,
1071
1097
  ...(computerUseError ? { computerUseError } : {}),
1098
+ browserReady: browserControl !== undefined && browserUnavailable === undefined,
1099
+ ...(browserControl ? { browser: browserControl.status() } : {}),
1100
+ ...(browserUnavailable !== undefined ? { browserUnavailable } : {}),
1072
1101
  });
1073
1102
  // URL fetching is separately opt-in and parent-only: children do not
1074
1103
  // carry its guarded provider. Independent search is shared below because
@@ -1096,6 +1125,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
1096
1125
  // Best-effort — if the runtime can't stand up, the chat still works.
1097
1126
  const delegationScopes = new Map();
1098
1127
  const delegationLimits = new Map();
1128
+ // A resumed turn that runs on another model than the session's (a
1129
+ // scheduled job's): its children inherit that model, not the session's.
1130
+ const delegationModels = new Map();
1099
1131
  const delegatedInputWaiters = new Map();
1100
1132
  if (options.residentHistory) {
1101
1133
  const history = options.residentHistory;
@@ -1193,6 +1225,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1193
1225
  tokenBudget: options.limits?.tokenBudget,
1194
1226
  maxIterations: options.limits?.maxIterations,
1195
1227
  resolveLimits: (turnId) => delegationLimits.get(turnId),
1228
+ resolveTurnModel: (turnId) => delegationModels.get(turnId),
1196
1229
  timeoutMs: options.limits?.timeoutMs,
1197
1230
  definitions: discovered.definitions,
1198
1231
  // Children log under the parent's `<session-id>/subagents/`; an
@@ -1382,6 +1415,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1382
1415
  const remaining = await Promise.allSettled([
1383
1416
  mcp.close(),
1384
1417
  computerUseHost?.dispose(),
1418
+ browserControl?.dispose(),
1385
1419
  jobRegistry?.killOwner(jobOwner),
1386
1420
  checkpoints.close(),
1387
1421
  ]);
@@ -1448,6 +1482,10 @@ export async function createAgentSession(prefs, detected, options = {}) {
1448
1482
  // park recorder is supplied.
1449
1483
  registry.register(buildAskUserQuestionTool({ resumeHandler: parkQuestion }));
1450
1484
  }
1485
+ // The host's own additions, to this registry only: `buildTools` above
1486
+ // builds a child's roster separately, so none of these reach a sub-agent.
1487
+ for (const tool of options.extraTools ?? [])
1488
+ registry.register(tool);
1451
1489
  // Task store → query registers task_create / task_update / task_list and
1452
1490
  // emits task_created/task_updated, so the agent can track a plan. Tasks
1453
1491
  // belong to the session (`<session-id>/tasks/`) and record the turn that
@@ -1514,14 +1552,35 @@ export async function createAgentSession(prefs, detected, options = {}) {
1514
1552
  // refusal closes the MCP processes already opened for this candidate before
1515
1553
  // returning an inert session. Sub-agents were built above from their own
1516
1554
  // registries, so executable plugins remain a top-level-session capability.
1555
+ // File skills (built-in, ~/.agents, ~/.namzu, and the project's) reach the
1556
+ // model through the kernel's manifest and `skill` tool. Registered BEFORE
1557
+ // the plugin runtime: a runtime that finds the tool already there never
1558
+ // owns it, so disabling the last plugin skill cannot take away the tool
1559
+ // the file skills load through.
1560
+ const skillCatalog = await createSessionSkillCatalog({
1561
+ cwd,
1562
+ ...(options.skills ? { config: options.skills } : {}),
1563
+ log: cliLogger(),
1564
+ });
1565
+ // A session that can save a skill loads it next turn through this tool,
1566
+ // even when it started with none.
1567
+ if ((skillCatalog.hasFileSkills || registry.has(SAVE_SKILL_TOOL_NAME)) &&
1568
+ !registry.has(SkillTool.name))
1569
+ registry.register(SkillTool);
1517
1570
  let pluginRuntime;
1518
1571
  try {
1519
1572
  pluginRuntime = await createCliPluginRuntime(options.plugins, registry, cwd, options.hooks);
1520
1573
  }
1521
1574
  catch (error) {
1522
- await Promise.allSettled([mcp.close(), computerUseHost?.dispose()]);
1575
+ await Promise.allSettled([mcp.close(), computerUseHost?.dispose(), browserControl?.dispose()]);
1523
1576
  return emptySession(describeError(error));
1524
1577
  }
1578
+ // Everything is registered by now but the deferred task tools, which no
1579
+ // caller withholds.
1580
+ for (const name of options.withheldTools ?? []) {
1581
+ if (registry.get(name))
1582
+ registry.unregister(name);
1583
+ }
1525
1584
  // The session's own lifecycle, for hooks that set up or tear down
1526
1585
  // something per session rather than per turn. These two calls belong to no
1527
1586
  // turn, so they carry no turn id — nothing is minted to fill the field.
@@ -1530,6 +1589,14 @@ export async function createAgentSession(prefs, detected, options = {}) {
1530
1589
  // is replaced when the conversation is first made durable — and a hook
1531
1590
  // given the provisional id could never match it to a turn.
1532
1591
  const sessionPlugins = pluginRuntime;
1592
+ // What one turn's prompt manifest and `skill` tool see: the file skills
1593
+ // gated against the tools registered now, merged with the plugins' own.
1594
+ const turnSkillsFor = (turnModel) => skillCatalog.forTurn({
1595
+ toolNames: registry.listNames(),
1596
+ contextWindowTokens: resolveContextWindow(options.compaction?.contextWindowTokens, turnModel)
1597
+ .tokens,
1598
+ ...(sessionPlugins ? { pluginSkills: sessionPlugins.skills } : {}),
1599
+ });
1533
1600
  let sessionStarted = false;
1534
1601
  const announceSessionStart = async () => {
1535
1602
  if (!sessionPlugins || sessionStarted)
@@ -1591,6 +1658,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1591
1658
  : undefined,
1592
1659
  mcp.close(),
1593
1660
  computerUseHost?.dispose(),
1661
+ browserControl?.dispose(),
1594
1662
  jobRegistry?.killOwner(jobOwner),
1595
1663
  checkpoints.close(),
1596
1664
  ]);
@@ -1647,13 +1715,35 @@ export async function createAgentSession(prefs, detected, options = {}) {
1647
1715
  reasoningEffortDefault = undefined;
1648
1716
  effortNotice = `Reasoning effort levels could not be established for this session: ${describeError(error)}`;
1649
1717
  }
1718
+ /**
1719
+ * The provider a resumed turn runs on when its caller pinned one (a
1720
+ * scheduled job's model): built from this session's credential for that
1721
+ * provider, with no fallback chain, since the job's own run has none.
1722
+ * `undefined` without a pin.
1723
+ */
1724
+ const pinnedResumeRoute = async (pin, sessionId, signal) => {
1725
+ if (!pin)
1726
+ return undefined;
1727
+ const providerId = pin.provider;
1728
+ const found = findDetected(detected, providerId);
1729
+ if (!found)
1730
+ throw new Error(`the turn runs on ${pin.provider}${pin.model ? `/${pin.model}` : ''} and this session has no credential for ${pin.provider}`);
1731
+ const pinnedModel = pin.model ?? found.entry.defaultModel;
1732
+ await ensureRegistered(providerId);
1733
+ const credential = await currentCredentialFor(providerId, signal);
1734
+ const pinnedProvider = constructProvider(providerId, credential, pinnedModel, { sessionId });
1735
+ const effort = pin.effort;
1736
+ if (effort !== undefined)
1737
+ await prepareDelegatedEffort(pinnedProvider, pinnedModel, signal);
1738
+ return { provider: pinnedProvider, model: pinnedModel, ...(effort ? { effort } : {}) };
1739
+ };
1650
1740
  /**
1651
1741
  * The kernel's resume with this session's half of the turn attached: the
1652
1742
  * provider, the tools, the working directory, the doctrine — the part a
1653
1743
  * checkpoint cannot carry. `resumeDurable` and `resumePaused` differ only
1654
1744
  * in where the log and its lease come from.
1655
1745
  */
1656
- const kernelResume = ({ entry, sessionLog, checkpointStore, lease, signal, checkpointId, listener, permissionMode, currentPermissionMode, }) => operations.promise(signal, async (ownedSignal) => {
1746
+ const kernelResume = ({ entry, sessionLog, checkpointStore, lease, signal, checkpointId, listener, permissionMode, currentPermissionMode, pendingDecision, onPermission, rules, reviewHold, model: pinned, systemNote, }) => operations.promise(signal, async (ownedSignal) => {
1657
1747
  const selectTaskStore = beginTaskStoreReadout();
1658
1748
  // The turn's own limits, as its `turn_started` recorded them.
1659
1749
  const resumedLimits = (await readStoredTurnGuards(sessionLog, entry.turnId)) ?? resolveTurnGuards(options.limits);
@@ -1662,9 +1752,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
1662
1752
  // fallback chain has to be built AFTER that so its members do not
1663
1753
  // hold a client the refresh just replaced.
1664
1754
  await prepareProviderCredential(ownedSignal);
1665
- const pluginSkills = pluginRuntime
1666
- ? await currentPluginSkills(pluginRuntime.skills)
1667
- : undefined;
1755
+ const route = await pinnedResumeRoute(pinned, entry.sessionId, ownedSignal);
1756
+ const turnSkills = await turnSkillsFor(route?.model ?? model);
1668
1757
  const curatedMemory = readMemory(undefined, cwd);
1669
1758
  for (const notice of formatMemoryDiagnostics(curatedMemory))
1670
1759
  cliLogger().warn(notice);
@@ -1687,6 +1776,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
1687
1776
  options.conversationSessions ? CONVERSATION_EVIDENCE_GUIDANCE : undefined,
1688
1777
  environmentPrompt,
1689
1778
  memoryPrompt,
1779
+ systemNote,
1780
+ turnSkills.overflowNote,
1690
1781
  ]
1691
1782
  .filter((s) => Boolean(s))
1692
1783
  .join('\n\n') || undefined;
@@ -1708,7 +1799,13 @@ export async function createAgentSession(prefs, detected, options = {}) {
1708
1799
  ...(recordedModes.has(String(entry.sessionId))
1709
1800
  ? { recorded: recordedModes.get(String(entry.sessionId)) }
1710
1801
  : {}),
1711
- handlerFor: (mode) => makeResumeHandler(approval, undefined, mode, reviewExemptionFor(mode, registry, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }),
1802
+ handlerFor: (mode) => onPermission === undefined && reviewHold !== undefined
1803
+ ? makeHoldingResumeHandler(mode, reviewExemptionFor(mode, registry, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }, reviewHold.reason)
1804
+ : makeResumeHandler(
1805
+ // A caller that brings its own prompt (a scheduled turn answered in
1806
+ // the TUI) gets its own latch: the session's "approve all" is not
1807
+ // an answer about a turn another policy started.
1808
+ onPermission ? { all: false } : approval, onPermission, mode, reviewExemptionFor(mode, registry, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }),
1712
1809
  });
1713
1810
  const resumeHandler = modeControl.handler;
1714
1811
  const reviewAllowedCalls = modeControl.reviewAllowedCalls;
@@ -1718,15 +1815,21 @@ export async function createAgentSession(prefs, detected, options = {}) {
1718
1815
  delegatedReviewAllowedCalls.set(entry.turnId, reviewAllowedCalls);
1719
1816
  delegationScopes.set(entry.turnId, turnScope);
1720
1817
  delegationLimits.set(entry.turnId, resumedLimits);
1818
+ if (route && pinned)
1819
+ delegationModels.set(entry.turnId, {
1820
+ provider: pinned.provider,
1821
+ model: route.model,
1822
+ ...(route.effort ? { effort: route.effort } : {}),
1823
+ });
1721
1824
  const turnTaskStore = selectTaskStore(turnScope);
1722
1825
  try {
1723
1826
  return await resumeSession({
1724
- provider: providerForSession(entry.sessionId),
1725
- fallbackProviders: fallbackPlan.build(currentToken, entry.sessionId),
1827
+ provider: route?.provider ?? providerForSession(entry.sessionId),
1828
+ fallbackProviders: route ? [] : fallbackPlan.build(currentToken, entry.sessionId),
1726
1829
  tools: registry,
1727
1830
  pluginManager: pluginRuntime?.manager,
1728
- skillRegistry: pluginRuntime?.skills,
1729
- skills: pluginSkills,
1831
+ ...(turnSkills.registry ? { skillRegistry: turnSkills.registry } : {}),
1832
+ ...(turnSkills.manifest ? { skills: turnSkills.manifest } : {}),
1730
1833
  taskStore: turnTaskStore,
1731
1834
  ...(webCapability ? { web: webCapability } : {}),
1732
1835
  // The same availability the original turn registered under.
@@ -1739,9 +1842,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
1739
1842
  task_list: 'active',
1740
1843
  },
1741
1844
  ...(subagentRuntime
1742
- ? { taskScheduler: await subagentRuntime.gatewayForTurn(entry.turnId) }
1845
+ ? {
1846
+ taskScheduler: await subagentRuntime.gatewayForTurn(entry.turnId),
1847
+ }
1743
1848
  : {}),
1744
- authorizationGate: gateFor(options.rules),
1849
+ authorizationGate: gateFor(rules ?? options.rules),
1850
+ ...(pendingDecision ? { pendingDecision } : {}),
1745
1851
  compactionConfig: compactionConfigFor(options.compaction),
1746
1852
  retainedToolPreviewChars: options.conversationSessions
1747
1853
  ? (options.compaction?.retainedToolPreviewChars ?? 4_000)
@@ -1770,7 +1876,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
1770
1876
  ? { sandboxTeardownTimeoutMs: options.sandbox.teardownTimeoutMs }
1771
1877
  : {}),
1772
1878
  turnConfig: {
1773
- model,
1879
+ model: route?.model ?? model,
1880
+ ...(route?.effort ? { effort: route.effort } : {}),
1774
1881
  ...(nativeWebSearch ? { webSearch: nativeWebSearch } : {}),
1775
1882
  ...(sandbox.provider ? { sandbox: { workspace: sandboxWorkspace } } : {}),
1776
1883
  ...resumedLimits,
@@ -1833,6 +1940,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1833
1940
  delegatedReviewAllowedCalls.delete(entry.turnId);
1834
1941
  delegationScopes.delete(entry.turnId);
1835
1942
  delegationLimits.delete(entry.turnId);
1943
+ delegationModels.delete(entry.turnId);
1836
1944
  await subagentRuntime?.releaseTurn(entry.turnId);
1837
1945
  }
1838
1946
  }
@@ -1843,12 +1951,13 @@ export async function createAgentSession(prefs, detected, options = {}) {
1843
1951
  * headless caller renders a resumed turn exactly as it rendered the first
1844
1952
  * segment.
1845
1953
  */
1846
- const resumePausedStream = ({ turnId, checkpointId, signal, permissionMode, currentPermissionMode, }) => {
1954
+ const resumePausedStream = ({ turnId, checkpointId, signal, permissionMode, currentPermissionMode, pendingDecision, onPermission, rules, reviewHold, model: pinned, systemNote, }) => {
1847
1955
  const queue = [];
1848
1956
  let wake;
1849
1957
  let settled = false;
1850
1958
  let failure;
1851
1959
  const presenter = createToolPresenter(registry);
1960
+ const readsOnly = declaredReadOnly(registry);
1852
1961
  // The log the turn appends to, and its checkpoints beside it.
1853
1962
  const sessionLog = DiskSessionLog.at(paths, { sessionId: scope.sessionId });
1854
1963
  const outcome = kernelResume({
@@ -1863,6 +1972,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
1863
1972
  ...(checkpointId !== undefined ? { checkpointId: checkpointId } : {}),
1864
1973
  ...(permissionMode ? { permissionMode } : {}),
1865
1974
  ...(currentPermissionMode ? { currentPermissionMode } : {}),
1975
+ ...(pendingDecision ? { pendingDecision } : {}),
1976
+ ...(onPermission ? { onPermission } : {}),
1977
+ ...(rules ? { rules } : {}),
1978
+ ...(reviewHold ? { reviewHold } : {}),
1979
+ ...(pinned ? { model: pinned } : {}),
1980
+ ...(systemNote ? { systemNote } : {}),
1866
1981
  listener: (event) => {
1867
1982
  queue.push(event);
1868
1983
  wake?.();
@@ -1888,7 +2003,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1888
2003
  const next = queue.shift();
1889
2004
  if (!next)
1890
2005
  break;
1891
- const mapped = toAgentEvent(next, presenter);
2006
+ const mapped = toAgentEvent(next, presenter, readsOnly);
1892
2007
  if (mapped)
1893
2008
  yield mapped;
1894
2009
  }
@@ -2005,6 +2120,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
2005
2120
  return mcp.failed;
2006
2121
  },
2007
2122
  mcpStatus: () => mcp.current(),
2123
+ ...(browserControl ? { browser: browserControl } : {}),
2008
2124
  configNotices: [
2009
2125
  ...(capabilityNotice ? [capabilityNotice] : []),
2010
2126
  ...(effortNotice ? [effortNotice] : []),
@@ -2012,15 +2128,24 @@ export async function createAgentSession(prefs, detected, options = {}) {
2012
2128
  ...(computerUseError
2013
2129
  ? [`Computer use is unavailable on this device: ${describeError(computerUseError)}`]
2014
2130
  : []),
2131
+ ...(browserUnavailable !== undefined
2132
+ ? [`The browser is unavailable: ${browserUnavailable}`]
2133
+ : []),
2015
2134
  ...unresolvedNotice.map((line) => `Provider chain: capabilities could not be established for ${line}.`),
2016
2135
  ...fallbackPlan.notices,
2017
2136
  ...memoryMigrationNotices,
2018
2137
  ],
2019
2138
  rememberNote: (text, type) => saveTypedNote(memoryStore, text, type),
2020
- importCuratedNotes: async () => describeCuratedNotesImport(await importCuratedNotes({ store: memoryStore, directory: memoryDirectory, cwd }), memoryDirectory),
2139
+ importCuratedNotes: async () => describeCuratedNotesImport(await importCuratedNotes({
2140
+ store: memoryStore,
2141
+ directory: memoryDirectory,
2142
+ cwd,
2143
+ }), memoryDirectory),
2021
2144
  storedMemoryIndex: async () => ({
2022
2145
  directory: memoryDirectory,
2023
- index: await memoryStore.readIndex({ maxLines: Number.POSITIVE_INFINITY }),
2146
+ index: await memoryStore.readIndex({
2147
+ maxLines: Number.POSITIVE_INFINITY,
2148
+ }),
2024
2149
  derived: await memoryStore.readIndex({
2025
2150
  maxLines: Number.POSITIVE_INFINITY,
2026
2151
  derived: true,
@@ -2048,7 +2173,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
2048
2173
  const turnScope = { ...scope };
2049
2174
  const initialMode = opts?.permissionMode ??
2050
2175
  options.permissionMode ??
2051
- (opts?.onPermission ? 'prompt' : 'auto');
2176
+ (opts?.onPermission || opts?.reviewHold ? 'prompt' : 'auto');
2052
2177
  // The mode is read at every decision, so the operator can change it
2053
2178
  // while this turn runs (see permissions/live-mode.ts).
2054
2179
  const modeControl = createLiveModeControl({
@@ -2057,7 +2182,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
2057
2182
  ...(recordedModes.has(String(turnScope.sessionId))
2058
2183
  ? { recorded: recordedModes.get(String(turnScope.sessionId)) }
2059
2184
  : {}),
2060
- handlerFor: (mode) => makeResumeHandler(approval, opts?.onPermission, mode, reviewExemptionFor(mode, runTools, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }),
2185
+ handlerFor: (mode) => opts?.reviewHold
2186
+ ? makeHoldingResumeHandler(mode, reviewExemptionFor(mode, runTools, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }, opts.reviewHold.reason)
2187
+ : makeResumeHandler(approval, opts?.onPermission, mode, reviewExemptionFor(mode, runTools, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }),
2061
2188
  });
2062
2189
  const resumeHandler = modeControl.handler;
2063
2190
  liveModeControls.add(modeControl);
@@ -2106,9 +2233,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
2106
2233
  // runs — midnight passes, and the agent checks out a branch itself.
2107
2234
  // Its text only changes when a fact changes, so it costs a prompt-cache
2108
2235
  // miss exactly when a hit would have been a stale claim.
2109
- const pluginSkills = pluginRuntime
2110
- ? await currentPluginSkills(pluginRuntime.skills)
2111
- : undefined;
2236
+ const turnSkills = await turnSkillsFor(model);
2112
2237
  // One fork after plugin refresh, held through every iteration of
2113
2238
  // this send. Discovery cannot activate another send's schemas.
2114
2239
  if (options.toolLoading === 'deferred')
@@ -2121,7 +2246,11 @@ export async function createAgentSession(prefs, detected, options = {}) {
2121
2246
  }
2122
2247
  const storedMemory = await storedMemoryPrompt();
2123
2248
  if (storedMemory.notice) {
2124
- yield { kind: 'context', text: storedMemory.notice, shed: false };
2249
+ yield {
2250
+ kind: 'context',
2251
+ text: storedMemory.notice,
2252
+ shed: false,
2253
+ };
2125
2254
  }
2126
2255
  const memoryPrompt = [composeMemoryPrompt(curatedMemory), storedMemory.prompt]
2127
2256
  .filter((part) => Boolean(part))
@@ -2179,7 +2308,10 @@ export async function createAgentSession(prefs, detected, options = {}) {
2179
2308
  authorizeLearningRead: (context) => claimed.has(context.turnId) &&
2180
2309
  delegationScopes.get(context.turnId) === turnScope,
2181
2310
  })
2182
- : { contributions: createResidentStepContributions(contextOptions), tools: [] };
2311
+ : {
2312
+ contributions: createResidentStepContributions(contextOptions),
2313
+ tools: [],
2314
+ };
2183
2315
  if (bundle.tools.length) {
2184
2316
  // Per-send membership: neither another send nor delegated sessions inherit this tool.
2185
2317
  runTools = runTools.fork();
@@ -2201,7 +2333,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
2201
2333
  const systemPrompt = [
2202
2334
  NAMZU_IDENTITY,
2203
2335
  residentContext ? undefined : NAMZU_WORKING_DOCTRINE,
2204
- residentContext ? undefined : NAMZU_DELEGATION_DOCTRINE,
2336
+ // Advice on delegating, for a session that has the Agent tool:
2337
+ // a scheduled run whose job withholds it would only be told how
2338
+ // to use a tool it was not sent.
2339
+ residentContext || options.withheldTools?.includes(AGENT_LAUNCH_TOOL)
2340
+ ? undefined
2341
+ : NAMZU_DELEGATION_DOCTRINE,
2205
2342
  !residentContext && opts?.orchestrate ? NAMZU_ORCHESTRATE_DOCTRINE : undefined,
2206
2343
  options.conversationSessions ? CONVERSATION_EVIDENCE_GUIDANCE : undefined,
2207
2344
  options.toolLoading === 'deferred' ? DEFERRED_TOOL_GUIDANCE : undefined,
@@ -2214,6 +2351,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
2214
2351
  residentContext ? undefined : environmentPrompt,
2215
2352
  residentContext ? undefined : memoryPrompt,
2216
2353
  residentContext ? undefined : opts?.extraSystem,
2354
+ residentContext ? undefined : opts?.systemNote,
2355
+ turnSkills.overflowNote,
2217
2356
  ]
2218
2357
  .filter((s) => Boolean(s))
2219
2358
  .join('\n\n') || undefined;
@@ -2246,7 +2385,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
2246
2385
  // when this session is the one bringing it into existence.
2247
2386
  let ephemeralLog;
2248
2387
  if (options.ephemeral) {
2249
- ephemeralLog = new InMemorySessionLog({ sessionId: turnScope.sessionId });
2388
+ ephemeralLog = new InMemorySessionLog({
2389
+ sessionId: turnScope.sessionId,
2390
+ });
2250
2391
  await ensureSessionStarted(ephemeralLog, {
2251
2392
  ...turnScope,
2252
2393
  cwd,
@@ -2288,8 +2429,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
2288
2429
  model,
2289
2430
  tools: runTools,
2290
2431
  pluginManager: pluginRuntime?.manager,
2291
- skillRegistry: pluginRuntime?.skills,
2292
- skills: pluginSkills,
2432
+ skillRegistry: turnSkills.registry,
2433
+ skills: turnSkills.manifest,
2293
2434
  scope: turnScope,
2294
2435
  turnId,
2295
2436
  paths,
@@ -2841,6 +2982,7 @@ async function* runTurn({ retainedToolPreviewChars, fileReadTracker, provider, c
2841
2982
  // matching in the first place: `toAgentEvent` is pure over a `SessionEvent`
2842
2983
  // and could not ask a tool anything, so the host guessed from the name.
2843
2984
  const presenter = createToolPresenter(tools);
2985
+ const readsOnly = declaredReadOnly(tools);
2844
2986
  try {
2845
2987
  const events = query({
2846
2988
  ...(retainedToolPreviewChars !== undefined ? { retainedToolPreviewChars } : {}),
@@ -2959,7 +3101,7 @@ async function* runTurn({ retainedToolPreviewChars, fileReadTracker, provider, c
2959
3101
  // receipts and reasoning before the next user turn.
2960
3102
  continue;
2961
3103
  }
2962
- const mapped = toAgentEvent(event, presenter);
3104
+ const mapped = toAgentEvent(event, presenter, readsOnly);
2963
3105
  if (!mapped)
2964
3106
  continue;
2965
3107
  yield mapped;
@@ -3016,6 +3158,41 @@ export function makeResumeHandler(approval, onPermission, mode = onPermission ?
3016
3158
  unattendedSandboxEscape: escapePolicy.unattendedSandboxEscape ?? 'refuse',
3017
3159
  });
3018
3160
  }
3161
+ /** Thrown by the holding prompt; never leaves {@link makeHoldingResumeHandler}. */
3162
+ class ReviewHoldSignal extends Error {
3163
+ name = 'ReviewHoldSignal';
3164
+ }
3165
+ /**
3166
+ * The kernel's review policy with a prompt that never answers: every batch
3167
+ * that would be put to a person parks the turn instead (`{ action: 'pause' }`),
3168
+ * which the kernel records durably for a person to answer later.
3169
+ *
3170
+ * The prompt throws and THIS wrapper catches it: a handler that rejects is
3171
+ * turned into `abort` by the kernel (`awaitDecisionOrAbort`), which would end
3172
+ * the turn rather than hold it. The latch is private and never set, so no
3173
+ * earlier answer can approve a later batch.
3174
+ */
3175
+ export function makeHoldingResumeHandler(mode, exempt, escapePolicy, reason) {
3176
+ const inner = createReviewHandler({
3177
+ mode,
3178
+ prompt: async () => {
3179
+ throw new ReviewHoldSignal(reason);
3180
+ },
3181
+ exempt,
3182
+ remembered: { all: false },
3183
+ unattendedSandboxEscape: escapePolicy.unattendedSandboxEscape ?? 'refuse',
3184
+ });
3185
+ return async (request) => {
3186
+ try {
3187
+ return await inner(request);
3188
+ }
3189
+ catch (error) {
3190
+ if (error instanceof ReviewHoldSignal)
3191
+ return { action: 'pause', reason };
3192
+ throw error;
3193
+ }
3194
+ };
3195
+ }
3019
3196
  /**
3020
3197
  * Whether a call runs without asking. The kernel's rule: a trusted read-only
3021
3198
  * declaration or a named bookkeeping write, never a fetch, never a tool the
@@ -3038,10 +3215,53 @@ export const AGENT_LAUNCH_TOOL = 'Agent';
3038
3215
  * An operator who wants every launch asked about writes
3039
3216
  * `permissions: { Agent: "ask" }`: an `ask` rule is an explicit review, which
3040
3217
  * no exemption skips.
3218
+ *
3219
+ * `save_skill` (the TUI's alone) is not asked about here either, outside
3220
+ * `strict` and `plan`: its own screen shows the whole file and where it goes,
3221
+ * and asks in every mode, `auto` included. A second question in front of it
3222
+ * would ask the same thing with less on the screen. `strict` and `plan` still
3223
+ * refuse it, and an `ask` or `deny` rule for it still applies. Neither is
3224
+ * the `schedule` tool's `create`, `resume` or `delete`, for the same reason
3225
+ * (see {@link confirmsItself}).
3041
3226
  */
3042
3227
  export function reviewExemptionFor(mode, registry, launchesReadOnlyAgent) {
3043
3228
  return (name, input) => isPromptExempt(registry, name, input) ||
3044
- (mode !== 'strict' && name === AGENT_LAUNCH_TOOL && launchesReadOnlyAgent(input));
3229
+ (mode !== 'strict' && name === AGENT_LAUNCH_TOOL && launchesReadOnlyAgent(input)) ||
3230
+ (mode !== 'strict' && mode !== 'plan' && confirmsItself(name, input)) ||
3231
+ (mode !== 'strict' &&
3232
+ mode !== 'plan' &&
3233
+ name === SAVE_SKILL_TOOL_NAME &&
3234
+ registry.has(SAVE_SKILL_TOOL_NAME));
3235
+ }
3236
+ /**
3237
+ * The `schedule` tool's `create`, `resume` and `delete`: each puts its own
3238
+ * confirmation in front of the operator, drawn from the host's computation,
3239
+ * and changes nothing unless they choose to. A review before it only asked
3240
+ * "Do you want to run schedule?" over the model's raw arguments, and then the
3241
+ * real question came. `pause` and anything else is reviewed as before; `plan`
3242
+ * and `strict` still refuse, and an `ask` or `deny` rule still decides, since
3243
+ * an explicit review is never exempted.
3244
+ */
3245
+ export function confirmsItself(name, input) {
3246
+ if (name !== SCHEDULE_TOOL_NAME || typeof input !== 'object' || input === null)
3247
+ return false;
3248
+ const action = input.action;
3249
+ return action === 'create' || action === 'resume' || action === 'delete';
3250
+ }
3251
+ /**
3252
+ * A call's own read-only declaration, as the tool states it for this input.
3253
+ * Not a permission answer (that is `isPromptExempt`): it only says whether a
3254
+ * finished turn changed anything, for the TUI's skill suggestion.
3255
+ */
3256
+ function declaredReadOnly(registry) {
3257
+ return (toolName, input) => {
3258
+ try {
3259
+ return registry.get(toolName)?.isReadOnly?.(input) === true;
3260
+ }
3261
+ catch {
3262
+ return false;
3263
+ }
3264
+ };
3045
3265
  }
3046
3266
  /** The exempt roster, sorted, for the surface that has to NAME it. */
3047
3267
  export function promptExemptToolNames(registry) {
@@ -3058,7 +3278,9 @@ export const batchNeedsPrompt = batchNeedsReview;
3058
3278
  * `null` for events the chat surface doesn't render (iteration markers,
3059
3279
  * checkpoints, plan lifecycle, …). Pure — unit-tested.
3060
3280
  */
3061
- export function toAgentEvent(event, presenter) {
3281
+ export function toAgentEvent(event, presenter,
3282
+ /** Whether a call only reads, from the tool's own declaration; see `tool-start.readOnly`. */
3283
+ readsOnly) {
3062
3284
  switch (event.type) {
3063
3285
  case 'hosted_tool': {
3064
3286
  const common = {
@@ -3134,6 +3356,7 @@ export function toAgentEvent(event, presenter) {
3134
3356
  const web = webActivityFromInput(event.toolName, event.input);
3135
3357
  return web ? { web } : {};
3136
3358
  })(),
3359
+ ...(readsOnly?.(event.toolName, event.input) ? { readOnly: true } : {}),
3137
3360
  };
3138
3361
  case 'tool_progress':
3139
3362
  return {
@@ -3151,12 +3374,19 @@ export function toAgentEvent(event, presenter) {
3151
3374
  output: event.result,
3152
3375
  });
3153
3376
  const detail = viewToLines(view);
3154
- // Drop only an exact duplicate. A shortened summary cannot replace
3155
- // the first line's evidence in expanded or raw output.
3156
- const summary = view.kind === 'terminal' && detail && detail.length > 0
3157
- ? truncate(detail[0], 120)
3158
- : firstLine(event.result);
3159
- const withoutRepeatedSummary = view.kind === 'terminal' && detail?.[0] === summary ? detail.slice(1) : detail;
3377
+ // The first line is the summary row. It leaves the body only when
3378
+ // the row says all of it: a shortened summary cannot replace the
3379
+ // first line's evidence in expanded or raw output. A first line
3380
+ // up to a few rows long is shown whole, so a note like "A
3381
+ // navigation to … was blocked" is not printed twice, once cut off
3382
+ // on the summary row and again in full underneath.
3383
+ const first = view.kind === 'terminal' && view.output.trim().length > 0
3384
+ ? resultToLines(view.output)[0]
3385
+ : undefined;
3386
+ const summary = first !== undefined ? resultSummaryLine(first) : firstLine(event.result);
3387
+ const withoutRepeatedSummary = first !== undefined && detail?.[0] === first && summarySaysAll(first, summary)
3388
+ ? detail.slice(1)
3389
+ : detail;
3160
3390
  return {
3161
3391
  kind: 'tool-end',
3162
3392
  output: event.result,
@@ -3167,6 +3397,9 @@ export function toAgentEvent(event, presenter) {
3167
3397
  summary,
3168
3398
  ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),
3169
3399
  ...(view.kind === 'generic' && view.visibility === 'hidden' ? { hidden: true } : {}),
3400
+ ...(event.isError && view.kind === 'generic' && view.outcome === 'cancelled'
3401
+ ? { cancelled: true }
3402
+ : {}),
3170
3403
  ...(view.kind === 'generic' && view.label.length > 0 ? { resultLabel: view.label } : {}),
3171
3404
  ...(withoutRepeatedSummary && withoutRepeatedSummary.length > 0
3172
3405
  ? { detail: withoutRepeatedSummary }
@@ -3176,7 +3409,9 @@ export function toAgentEvent(event, presenter) {
3176
3409
  if (!kind)
3177
3410
  return {};
3178
3411
  const results = kind === 'search' && !event.isError ? countListedResults(event.result) : undefined;
3179
- return { web: { kind, ...(results !== undefined ? { results } : {}) } };
3412
+ return {
3413
+ web: { kind, ...(results !== undefined ? { results } : {}) },
3414
+ };
3180
3415
  })(),
3181
3416
  };
3182
3417
  }
@@ -3265,6 +3500,7 @@ export function toAgentEvent(event, presenter) {
3265
3500
  ...(event.failure ? { failure: event.failure } : {}),
3266
3501
  ...(event.providerError ? { providerError: event.providerError } : {}),
3267
3502
  ...(event.explanation ? { explanation: event.explanation } : {}),
3503
+ ...(event.handoff ? { handoff: event.handoff } : {}),
3268
3504
  };
3269
3505
  case 'turn_completed':
3270
3506
  // Carried through rather than dropped: `turn_failed` fires only from
@@ -3448,8 +3684,10 @@ export function viewToLines(view) {
3448
3684
  if (view.output.trim().length === 0)
3449
3685
  return undefined;
3450
3686
  const lines = resultToLines(view.output);
3451
- // A single short line is already the summary — no need to repeat it.
3452
- return lines.length === 1 && lines[0] === truncate(lines[0] ?? '', 120) ? undefined : lines;
3687
+ // A single line the summary row shows whole is not repeated under it.
3688
+ return lines.length === 1 && summarySaysAll(lines[0] ?? '', resultSummaryLine(lines[0] ?? ''))
3689
+ ? undefined
3690
+ : lines;
3453
3691
  }
3454
3692
  }
3455
3693
  }
@@ -3464,6 +3702,21 @@ export function viewToSummary(view) {
3464
3702
  return truncate(view.command ?? view.output.split('\n')[0] ?? '', 120);
3465
3703
  }
3466
3704
  }
3705
+ /**
3706
+ * The longest first line a result's summary row shows whole, about three
3707
+ * terminal rows. Longer, the row shows its first 120 characters and the body
3708
+ * keeps the line.
3709
+ */
3710
+ export const RESULT_SUMMARY_WHOLE_MAX = 300;
3711
+ /** A result's summary row for its first line: whole when short enough, else shortened. */
3712
+ export function resultSummaryLine(line) {
3713
+ const flat = line.replace(/\s+/g, ' ');
3714
+ return flat.length <= RESULT_SUMMARY_WHOLE_MAX ? flat : truncate(line, 120);
3715
+ }
3716
+ /** The summary row says everything `line` does (whitespace aside). */
3717
+ function summarySaysAll(line, summary) {
3718
+ return line === summary || line.replace(/\s+/g, ' ') === summary;
3719
+ }
3467
3720
  function truncate(value, max) {
3468
3721
  const oneLine = value.replace(/\s+/g, ' ');
3469
3722
  return oneLine.length > max ? `${oneLine.slice(0, max - 1)}…` : oneLine;
@@ -3573,7 +3826,9 @@ function logCapabilities(probes, runtime) {
3573
3826
  ? runtime.sandboxReady
3574
3827
  : p.specifier === '@namzu/computer-use'
3575
3828
  ? runtime.computerUseReady
3576
- : p.state === 'present';
3829
+ : p.specifier === '@namzu/browser'
3830
+ ? runtime.browserReady === true
3831
+ : p.state === 'present';
3577
3832
  return `${p.specifier.split('/').pop()} ${present ? 'yes' : 'no'}`;
3578
3833
  })
3579
3834
  .join(' · ');
@@ -3597,6 +3852,22 @@ function logCapabilities(probes, runtime) {
3597
3852
  ...(probe.state === 'present' ? { 'namzu.capability.version': probe.version } : {}),
3598
3853
  });
3599
3854
  }
3855
+ if (runtime.browser) {
3856
+ // The engine and profile the browser tools will use; nothing is running yet.
3857
+ log.info('Browser tools mounted', {
3858
+ [EVENT_NAME_ATTRIBUTE]: BOOT_EVENT_NAMES.CAPABILITY_DETECTED,
3859
+ 'namzu.capability.name': '@namzu/browser',
3860
+ 'namzu.capability.state': runtime.browserUnavailable === undefined ? 'ready' : 'unavailable',
3861
+ 'namzu.capability.present': runtime.browserReady === true,
3862
+ 'namzu.browser.engine': runtime.browser.engine,
3863
+ 'namzu.browser.profile': runtime.browser.profile,
3864
+ 'namzu.browser.headless': runtime.browser.headless,
3865
+ 'namzu.browser.site_count': Object.keys(runtime.browser.sites).length,
3866
+ ...(runtime.browserUnavailable !== undefined
3867
+ ? { 'namzu.browser.unavailable_reason': runtime.browserUnavailable }
3868
+ : {}),
3869
+ });
3870
+ }
3600
3871
  if (runtime.computerUseError) {
3601
3872
  log.warn('Computer use adapter is unavailable', {
3602
3873
  [EVENT_NAME_ATTRIBUTE]: BOOT_EVENT_NAMES.CAPABILITY_DETECTED,