@wjarka/cezarion 0.15.1 → 0.16.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 (462) hide show
  1. package/README.md +167 -46
  2. package/dist/agent-config/catalog.d.ts +6 -2
  3. package/dist/agent-config/catalog.js +78 -3
  4. package/dist/agent-config/catalog.js.map +1 -1
  5. package/dist/agent-config/model-settings/shared.js +6 -3
  6. package/dist/agent-config/model-settings/shared.js.map +1 -1
  7. package/dist/agent-config/models.js +4 -0
  8. package/dist/agent-config/models.js.map +1 -1
  9. package/dist/agent-config/validate.js +6 -0
  10. package/dist/agent-config/validate.js.map +1 -1
  11. package/dist/artifacts/cli.js +10 -2
  12. package/dist/artifacts/cli.js.map +1 -1
  13. package/dist/automations/coordinator.d.ts +7 -0
  14. package/dist/automations/coordinator.js +22 -2
  15. package/dist/automations/coordinator.js.map +1 -1
  16. package/dist/automations/github-poller.d.ts +4 -4
  17. package/dist/automations/github-poller.js.map +1 -1
  18. package/dist/automations/schedule-runner.d.ts +130 -0
  19. package/dist/automations/schedule-runner.js +326 -0
  20. package/dist/automations/schedule-runner.js.map +1 -0
  21. package/dist/automations/scheduler.d.ts +38 -7
  22. package/dist/automations/scheduler.js +90 -22
  23. package/dist/automations/scheduler.js.map +1 -1
  24. package/dist/automations/store.d.ts +80 -3
  25. package/dist/automations/store.js +208 -16
  26. package/dist/automations/store.js.map +1 -1
  27. package/dist/automations/task-template.d.ts +42 -6
  28. package/dist/automations/task-template.js +152 -24
  29. package/dist/automations/task-template.js.map +1 -1
  30. package/dist/automations/types.d.ts +142 -6
  31. package/dist/automations/types.js +64 -6
  32. package/dist/automations/types.js.map +1 -1
  33. package/dist/autosave-holders.js +8 -12
  34. package/dist/autosave-holders.js.map +1 -1
  35. package/dist/ci-wait/client.d.ts +8 -1
  36. package/dist/ci-wait/client.js +43 -1
  37. package/dist/ci-wait/client.js.map +1 -1
  38. package/dist/ci-wait/controller.d.ts +83 -4
  39. package/dist/ci-wait/controller.js +76 -11
  40. package/dist/ci-wait/controller.js.map +1 -1
  41. package/dist/ci-wait/errors.d.ts +7 -1
  42. package/dist/ci-wait/errors.js +36 -0
  43. package/dist/ci-wait/errors.js.map +1 -1
  44. package/dist/ci-wait/github.d.ts +2 -0
  45. package/dist/ci-wait/github.js +3 -1
  46. package/dist/ci-wait/github.js.map +1 -1
  47. package/dist/ci-wait/mcp.d.ts +2 -98
  48. package/dist/ci-wait/mcp.js +11 -25
  49. package/dist/ci-wait/mcp.js.map +1 -1
  50. package/dist/ci-wait/supervisor.d.ts +2 -1
  51. package/dist/ci-wait/supervisor.js +2 -1
  52. package/dist/ci-wait/supervisor.js.map +1 -1
  53. package/dist/ci-wait/tools.d.ts +307 -0
  54. package/dist/ci-wait/tools.js +63 -0
  55. package/dist/ci-wait/tools.js.map +1 -0
  56. package/dist/config.d.ts +3 -0
  57. package/dist/config.js +7 -0
  58. package/dist/config.js.map +1 -1
  59. package/dist/contract/agent-config.d.ts +5 -0
  60. package/dist/contract/agent-profiles.d.ts +15 -0
  61. package/dist/contract/automation-schedule.d.ts +60 -0
  62. package/dist/contract/automations.d.ts +208 -36
  63. package/dist/contract/delegation.d.ts +96 -0
  64. package/dist/contract/discovery.d.ts +7 -0
  65. package/dist/contract/github.d.ts +1 -0
  66. package/dist/contract/health.d.ts +6 -0
  67. package/dist/contract/index.d.ts +3 -0
  68. package/dist/contract/index.js +1445 -745
  69. package/dist/contract/preview.d.ts +508 -0
  70. package/dist/contract/runs.d.ts +716 -15
  71. package/dist/contract/skills.d.ts +39 -0
  72. package/dist/contract/workflows.d.ts +6 -0
  73. package/dist/contract/workspace.d.ts +65 -0
  74. package/dist/contract/zoned-time.d.ts +35 -0
  75. package/dist/core/agent-env.d.ts +11 -0
  76. package/dist/core/agent-env.js +27 -5
  77. package/dist/core/agent-env.js.map +1 -1
  78. package/dist/core/agent-profiles.js +7 -0
  79. package/dist/core/agent-profiles.js.map +1 -1
  80. package/dist/core/agent-runner.d.ts +49 -1
  81. package/dist/core/agent-runner.js +2 -1
  82. package/dist/core/agent-runner.js.map +1 -1
  83. package/dist/core/backend-detect.d.ts +3 -2
  84. package/dist/core/backend-detect.js +27 -1
  85. package/dist/core/backend-detect.js.map +1 -1
  86. package/dist/core/claude-cli-runner.d.ts +1 -0
  87. package/dist/core/claude-cli-runner.js +69 -19
  88. package/dist/core/claude-cli-runner.js.map +1 -1
  89. package/dist/core/codex-app-server-runner.d.ts +1 -0
  90. package/dist/core/codex-app-server-runner.js +200 -25
  91. package/dist/core/codex-app-server-runner.js.map +1 -1
  92. package/dist/core/codex-app-server-transport.d.ts +7 -3
  93. package/dist/core/codex-app-server-transport.js +11 -9
  94. package/dist/core/codex-app-server-transport.js.map +1 -1
  95. package/dist/core/cursor-acp-runner.d.ts +1 -0
  96. package/dist/core/cursor-acp-runner.js +24 -9
  97. package/dist/core/cursor-acp-runner.js.map +1 -1
  98. package/dist/core/host-model-catalog.js +2 -0
  99. package/dist/core/host-model-catalog.js.map +1 -1
  100. package/dist/core/managed-session.js +2 -0
  101. package/dist/core/managed-session.js.map +1 -1
  102. package/dist/core/model-identity.js +2 -0
  103. package/dist/core/model-identity.js.map +1 -1
  104. package/dist/core/model-presets.js +3 -0
  105. package/dist/core/model-presets.js.map +1 -1
  106. package/dist/core/omp-model-catalog.d.ts +43 -0
  107. package/dist/core/omp-model-catalog.js +205 -0
  108. package/dist/core/omp-model-catalog.js.map +1 -0
  109. package/dist/core/omp-runner.d.ts +73 -0
  110. package/dist/core/omp-runner.js +990 -0
  111. package/dist/core/omp-runner.js.map +1 -0
  112. package/dist/core/omp-ui-mapper.d.ts +106 -0
  113. package/dist/core/omp-ui-mapper.js +882 -0
  114. package/dist/core/omp-ui-mapper.js.map +1 -0
  115. package/dist/core/opencode-server-runner.d.ts +18 -4
  116. package/dist/core/opencode-server-runner.js +337 -96
  117. package/dist/core/opencode-server-runner.js.map +1 -1
  118. package/dist/core/opencode-session-error.d.ts +11 -0
  119. package/dist/core/opencode-session-error.js +26 -0
  120. package/dist/core/opencode-session-error.js.map +1 -0
  121. package/dist/core/opencode-ui-mapper.js +4 -36
  122. package/dist/core/opencode-ui-mapper.js.map +1 -1
  123. package/dist/core/pi-runner.d.ts +1 -0
  124. package/dist/core/pi-runner.js +75 -12
  125. package/dist/core/pi-runner.js.map +1 -1
  126. package/dist/core/provider-auth.d.ts +9 -2
  127. package/dist/core/provider-auth.js +43 -6
  128. package/dist/core/provider-auth.js.map +1 -1
  129. package/dist/core/runner-factory.js +3 -0
  130. package/dist/core/runner-factory.js.map +1 -1
  131. package/dist/core/runner-model-catalog.js +1 -0
  132. package/dist/core/runner-model-catalog.js.map +1 -1
  133. package/dist/core/runner-stderr.d.ts +2 -0
  134. package/dist/core/runner-stderr.js +20 -0
  135. package/dist/core/runner-stderr.js.map +1 -0
  136. package/dist/core/session-process.d.ts +22 -0
  137. package/dist/core/session-process.js +202 -0
  138. package/dist/core/session-process.js.map +1 -0
  139. package/dist/core/ui-events.d.ts +1 -1
  140. package/dist/core/usage-limit.js +3 -60
  141. package/dist/core/usage-limit.js.map +1 -1
  142. package/dist/data-gitignore.d.ts +6 -0
  143. package/dist/data-gitignore.js +57 -0
  144. package/dist/data-gitignore.js.map +1 -0
  145. package/dist/delegation/capacity.d.ts +19 -0
  146. package/dist/delegation/capacity.js +34 -0
  147. package/dist/delegation/capacity.js.map +1 -0
  148. package/dist/delegation/cli.js +13 -4
  149. package/dist/delegation/cli.js.map +1 -1
  150. package/dist/delegation/conversations.d.ts +4 -2
  151. package/dist/delegation/conversations.js +8 -12
  152. package/dist/delegation/conversations.js.map +1 -1
  153. package/dist/delegation/credentials.d.ts +2 -0
  154. package/dist/delegation/credentials.js +10 -0
  155. package/dist/delegation/credentials.js.map +1 -1
  156. package/dist/delegation/destroy-observation.d.ts +42 -0
  157. package/dist/delegation/destroy-observation.js +115 -0
  158. package/dist/delegation/destroy-observation.js.map +1 -0
  159. package/dist/delegation/execution-identity.d.ts +6 -0
  160. package/dist/delegation/execution-identity.js +1 -1
  161. package/dist/delegation/execution-identity.js.map +1 -1
  162. package/dist/delegation/policy.d.ts +3 -2
  163. package/dist/delegation/policy.js +7 -5
  164. package/dist/delegation/policy.js.map +1 -1
  165. package/dist/delegation/process-liveness.d.ts +57 -20
  166. package/dist/delegation/process-liveness.js +114 -92
  167. package/dist/delegation/process-liveness.js.map +1 -1
  168. package/dist/delegation/provision.js +6 -6
  169. package/dist/delegation/provision.js.map +1 -1
  170. package/dist/delegation/results.js +4 -2
  171. package/dist/delegation/results.js.map +1 -1
  172. package/dist/delegation/retry-backoff.d.ts +23 -0
  173. package/dist/delegation/retry-backoff.js +18 -0
  174. package/dist/delegation/retry-backoff.js.map +1 -0
  175. package/dist/delegation/routes.d.ts +64 -58
  176. package/dist/delegation/scratch-cleanup.d.ts +29 -0
  177. package/dist/delegation/scratch-cleanup.js +143 -0
  178. package/dist/delegation/scratch-cleanup.js.map +1 -0
  179. package/dist/delegation/service.d.ts +20 -1
  180. package/dist/delegation/service.js +233 -70
  181. package/dist/delegation/service.js.map +1 -1
  182. package/dist/delegation/transport.d.ts +12 -6
  183. package/dist/delegation/workspace.d.ts +23 -4
  184. package/dist/delegation/workspace.js +147 -31
  185. package/dist/delegation/workspace.js.map +1 -1
  186. package/dist/git-worktree-lock-helper.js +8 -3
  187. package/dist/git-worktree-lock-helper.js.map +1 -1
  188. package/dist/git-worktree-lock.d.ts +6 -1
  189. package/dist/git-worktree-lock.js +10 -4
  190. package/dist/git-worktree-lock.js.map +1 -1
  191. package/dist/git-worktree-release.d.ts +20 -0
  192. package/dist/git-worktree-release.js +27 -0
  193. package/dist/git-worktree-release.js.map +1 -0
  194. package/dist/git-worktree.d.ts +8 -2
  195. package/dist/git-worktree.js +19 -2
  196. package/dist/git-worktree.js.map +1 -1
  197. package/dist/handoff.d.ts +2 -2
  198. package/dist/handoff.js +1 -1
  199. package/dist/index.js +109 -76
  200. package/dist/index.js.map +1 -1
  201. package/dist/paths.d.ts +1 -1
  202. package/dist/paths.js +5 -1
  203. package/dist/paths.js.map +1 -1
  204. package/dist/preview/cdp.d.ts +13 -0
  205. package/dist/preview/cdp.js +69 -0
  206. package/dist/preview/cdp.js.map +1 -0
  207. package/dist/preview/chromium.d.ts +75 -0
  208. package/dist/preview/chromium.js +394 -0
  209. package/dist/preview/chromium.js.map +1 -0
  210. package/dist/preview/dev-server.d.ts +68 -0
  211. package/dist/preview/dev-server.js +341 -0
  212. package/dist/preview/dev-server.js.map +1 -0
  213. package/dist/preview/host.d.ts +154 -0
  214. package/dist/preview/host.js +681 -0
  215. package/dist/preview/host.js.map +1 -0
  216. package/dist/preview/registration.d.ts +63 -0
  217. package/dist/preview/registration.js +109 -0
  218. package/dist/preview/registration.js.map +1 -0
  219. package/dist/preview/session.d.ts +73 -0
  220. package/dist/preview/session.js +347 -0
  221. package/dist/preview/session.js.map +1 -0
  222. package/dist/preview/url.d.ts +6 -0
  223. package/dist/preview/url.js +17 -0
  224. package/dist/preview/url.js.map +1 -0
  225. package/dist/runs/agent-tmpdir-removal.d.ts +15 -0
  226. package/dist/runs/agent-tmpdir-removal.js +60 -0
  227. package/dist/runs/agent-tmpdir-removal.js.map +1 -0
  228. package/dist/runs/agent-tmpdir.d.ts +12 -1
  229. package/dist/runs/agent-tmpdir.js +123 -34
  230. package/dist/runs/agent-tmpdir.js.map +1 -1
  231. package/dist/runs/delegation-state.d.ts +5 -1
  232. package/dist/runs/delegation-state.js +9 -0
  233. package/dist/runs/delegation-state.js.map +1 -1
  234. package/dist/runs/event-history.d.ts +4 -4
  235. package/dist/runs/event-history.js +97 -117
  236. package/dist/runs/event-history.js.map +1 -1
  237. package/dist/runs/history-compressor.d.ts +29 -0
  238. package/dist/runs/history-compressor.js +116 -0
  239. package/dist/runs/history-compressor.js.map +1 -0
  240. package/dist/runs/history-file.d.ts +36 -0
  241. package/dist/runs/history-file.js +463 -0
  242. package/dist/runs/history-file.js.map +1 -0
  243. package/dist/runs/human-ask-summary.js +18 -13
  244. package/dist/runs/human-ask-summary.js.map +1 -1
  245. package/dist/runs/legacy-index.d.ts +62 -0
  246. package/dist/runs/legacy-index.js +168 -0
  247. package/dist/runs/legacy-index.js.map +1 -0
  248. package/dist/runs/raw-record.d.ts +52 -0
  249. package/dist/runs/raw-record.js +145 -0
  250. package/dist/runs/raw-record.js.map +1 -0
  251. package/dist/runs/retention.d.ts +13 -6
  252. package/dist/runs/retention.js +42 -13
  253. package/dist/runs/retention.js.map +1 -1
  254. package/dist/runs/run-claims.d.ts +23 -0
  255. package/dist/runs/run-claims.js +24 -0
  256. package/dist/runs/run-claims.js.map +1 -0
  257. package/dist/runs/run-database.d.ts +385 -0
  258. package/dist/runs/run-database.js +930 -0
  259. package/dist/runs/run-database.js.map +1 -0
  260. package/dist/runs/run-index.d.ts +35 -9
  261. package/dist/runs/run-index.js +200 -25
  262. package/dist/runs/run-index.js.map +1 -1
  263. package/dist/runs/run-row.d.ts +44 -0
  264. package/dist/runs/run-row.js +84 -0
  265. package/dist/runs/run-row.js.map +1 -0
  266. package/dist/runs/run-search.d.ts +29 -0
  267. package/dist/runs/run-search.js +63 -0
  268. package/dist/runs/run-search.js.map +1 -0
  269. package/dist/runs/store-open-error.d.ts +24 -0
  270. package/dist/runs/store-open-error.js +61 -0
  271. package/dist/runs/store-open-error.js.map +1 -0
  272. package/dist/runs/store.d.ts +667 -32
  273. package/dist/runs/store.js +2153 -324
  274. package/dist/runs/store.js.map +1 -1
  275. package/dist/runs/transcript-facts.d.ts +77 -0
  276. package/dist/runs/transcript-facts.js +302 -0
  277. package/dist/runs/transcript-facts.js.map +1 -0
  278. package/dist/runs/webhook.js +5 -4
  279. package/dist/runs/webhook.js.map +1 -1
  280. package/dist/runs/worker-execution.d.ts +22 -0
  281. package/dist/runs/worker-execution.js +30 -0
  282. package/dist/runs/worker-execution.js.map +1 -0
  283. package/dist/server/capabilities.d.ts +1 -0
  284. package/dist/server/capabilities.js +2 -0
  285. package/dist/server/capabilities.js.map +1 -1
  286. package/dist/server/cockpit-ownership.d.ts +8 -0
  287. package/dist/server/cockpit-ownership.js +19 -0
  288. package/dist/server/cockpit-ownership.js.map +1 -1
  289. package/dist/server/forge/github.d.ts +1 -4
  290. package/dist/server/forge/github.js +47 -23
  291. package/dist/server/forge/github.js.map +1 -1
  292. package/dist/server/git.js +1 -1
  293. package/dist/server/git.js.map +1 -1
  294. package/dist/server/open-in-app.js +1 -0
  295. package/dist/server/open-in-app.js.map +1 -1
  296. package/dist/server/preview-socket.d.ts +36 -0
  297. package/dist/server/preview-socket.js +126 -0
  298. package/dist/server/preview-socket.js.map +1 -0
  299. package/dist/server/project-context.d.ts +31 -4
  300. package/dist/server/project-context.js +73 -9
  301. package/dist/server/project-context.js.map +1 -1
  302. package/dist/server/provider-action-gate.js +1 -0
  303. package/dist/server/provider-action-gate.js.map +1 -1
  304. package/dist/server/repo-branches.d.ts +4 -3
  305. package/dist/server/repo-branches.js +5 -4
  306. package/dist/server/repo-branches.js.map +1 -1
  307. package/dist/server/server.d.ts +4500 -2727
  308. package/dist/server/server.js +651 -202
  309. package/dist/server/server.js.map +1 -1
  310. package/dist/server/upgrade-router.d.ts +23 -0
  311. package/dist/server/upgrade-router.js +53 -0
  312. package/dist/server/upgrade-router.js.map +1 -0
  313. package/dist/server/validators.d.ts +6 -1
  314. package/dist/server/validators.js +4 -4
  315. package/dist/server/validators.js.map +1 -1
  316. package/dist/server/ws.d.ts +16 -16
  317. package/dist/server/ws.js +56 -54
  318. package/dist/server/ws.js.map +1 -1
  319. package/dist/skills-remote.d.ts +14 -0
  320. package/dist/skills-remote.js +91 -15
  321. package/dist/skills-remote.js.map +1 -1
  322. package/dist/task-cli/cli.js +40 -16
  323. package/dist/task-cli/cli.js.map +1 -1
  324. package/dist/task-cli/discovery.d.ts +4 -3
  325. package/dist/task-cli/discovery.js +4 -3
  326. package/dist/task-cli/discovery.js.map +1 -1
  327. package/dist/task-cli/http.d.ts +2 -0
  328. package/dist/task-cli/http.js +10 -4
  329. package/dist/task-cli/http.js.map +1 -1
  330. package/dist/task-cli/projections.d.ts +5 -2
  331. package/dist/task-cli/projections.js +13 -0
  332. package/dist/task-cli/projections.js.map +1 -1
  333. package/dist/task-cli/watch.d.ts +15 -2
  334. package/dist/task-cli/watch.js +31 -9
  335. package/dist/task-cli/watch.js.map +1 -1
  336. package/dist/ui-state.d.ts +4 -3
  337. package/dist/ui-state.js +8 -3
  338. package/dist/ui-state.js.map +1 -1
  339. package/dist/workflows/grouped-command.d.ts +31 -0
  340. package/dist/workflows/grouped-command.js +143 -0
  341. package/dist/workflows/grouped-command.js.map +1 -0
  342. package/dist/workflows/run.d.ts +223 -42
  343. package/dist/workflows/run.js +1316 -398
  344. package/dist/workflows/run.js.map +1 -1
  345. package/dist/workflows/types.d.ts +12 -0
  346. package/dist/workflows/types.js +20 -0
  347. package/dist/workflows/types.js.map +1 -1
  348. package/dist/workspace/agent-accounts.d.ts +5 -1
  349. package/dist/workspace/agent-accounts.js +1 -0
  350. package/dist/workspace/agent-accounts.js.map +1 -1
  351. package/dist/workspace/agent-profiles.js +1 -1
  352. package/dist/workspace/agent-profiles.js.map +1 -1
  353. package/dist/workspace/config.d.ts +3 -1
  354. package/dist/workspace/config.js +1 -0
  355. package/dist/workspace/config.js.map +1 -1
  356. package/dist/worktree-setup.d.ts +85 -0
  357. package/dist/worktree-setup.js +164 -0
  358. package/dist/worktree-setup.js.map +1 -0
  359. package/package.json +7 -5
  360. package/scripts/benchmark-run-store-fixture.ts +428 -0
  361. package/scripts/benchmark-run-store.ts +743 -0
  362. package/scripts/benchmark-stuck-destroys.ts +143 -0
  363. package/scripts/mock-autonomous.mjs +11 -0
  364. package/scripts/mock-ci-tool.mjs +20 -5
  365. package/scripts/mock-claude.mjs +110 -1
  366. package/scripts/mock-codex-app-server.mjs +117 -2
  367. package/scripts/mock-codex-coalesce-steer.mjs +59 -0
  368. package/scripts/mock-codex-reverse-steer.mjs +130 -0
  369. package/scripts/mock-cursor-acp.mjs +75 -1
  370. package/scripts/mock-omp-rpc.mjs +605 -0
  371. package/scripts/mock-opencode-serve.mjs +312 -22
  372. package/scripts/mock-pi-rpc.mjs +109 -3
  373. package/scripts/mock-runner-crash.mjs +42 -0
  374. package/scripts/mock-silent-tail.mjs +78 -0
  375. package/scripts/mock-turn-messages.mjs +5 -0
  376. package/scripts/omp-ci-wait.mjs +29 -0
  377. package/scripts/omp-restrict-delegation.yml +4 -0
  378. package/scripts/pi-ci-wait.mjs +12 -9
  379. package/scripts/seed-archive-profile.ts +3 -3
  380. package/web/dist/assets/agent-accounts-CFOBMVHc.js +1 -0
  381. package/web/dist/assets/alert-dialog-BrpNUwMt.js +1 -0
  382. package/web/dist/assets/app-window-CSHwDX9N.js +1 -0
  383. package/web/dist/assets/button-C_rd7CPp.js +43 -0
  384. package/web/dist/assets/compare-variants-D6WXhvXA.js +1 -0
  385. package/web/dist/assets/diff-DrEARXTV.js +3 -0
  386. package/web/dist/assets/diff-controls-B1-j7nFg.js +1 -0
  387. package/web/dist/assets/{diff-view-DPn6bQ5W.js → diff-view-DvGLjdTk.js} +3 -3
  388. package/web/dist/assets/dropdown-menu-BegOZFrj.js +1 -0
  389. package/web/dist/assets/format-DpNe3S0A.js +1 -0
  390. package/web/dist/assets/git-toolbar-Cw4ljmhB.js +1 -0
  391. package/web/dist/assets/github-CfV5sqL0.js +1 -0
  392. package/web/dist/assets/{image-preview-DJ8cgb1Z.js → image-preview-FOEfqpZP.js} +1 -1
  393. package/web/dist/assets/index-DgWWlOLy.js +13 -0
  394. package/web/dist/assets/index-fBFnuvxu.css +1 -0
  395. package/web/dist/assets/{nav-row-styles-3j64kH5s.js → nav-row-styles-vp2JbhuJ.js} +1 -1
  396. package/web/dist/assets/play-CmVAj_fu.js +1 -0
  397. package/web/dist/assets/popover-BohP6WTx.js +1 -0
  398. package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +3 -0
  399. package/web/dist/assets/preview-state-vuMviq6q.js +1 -0
  400. package/web/dist/assets/prompt-templates-Cubdut_C.js +15 -0
  401. package/web/dist/assets/repo-git-Cf4t5PD0.js +1 -0
  402. package/web/dist/assets/{run-diff-5-RRdcVz.js → run-diff-BpkUW37Z.js} +3 -3
  403. package/web/dist/assets/run-header-68WHf9wV.js +1 -0
  404. package/web/dist/assets/skills-DiVIgKeU.js +1 -0
  405. package/web/dist/assets/square-terminal-B6RYYieR.js +1 -0
  406. package/web/dist/assets/status-dot-CqTJHSP9.js +1 -0
  407. package/web/dist/assets/{tab-link-K0hjzEXR.js → tab-link-B07Hu4QX.js} +1 -1
  408. package/web/dist/assets/task-changes--lQF6EYf.js +1 -0
  409. package/web/dist/assets/task-commits-Cx0qy4DN.js +1 -0
  410. package/web/dist/assets/task-files-D50s-gKW.js +2 -0
  411. package/web/dist/assets/task-github-item-DaqmSwYT.js +1 -0
  412. package/web/dist/assets/task-thread-BPQ64lui.js +7 -0
  413. package/web/dist/assets/{task-thread-Bw_nQuTx.css → task-thread-zNE2Q9E-.css} +1 -1
  414. package/web/dist/assets/textarea-D20O4Pw0.js +1 -0
  415. package/web/dist/assets/thread-state-DWcFMrvb.js +4 -0
  416. package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +1 -0
  417. package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +1 -0
  418. package/web/dist/assets/{utils-fzNfbPzA.js → utils-JsL3ZsJf.js} +1 -1
  419. package/web/dist/assets/workflows-jwG7uxqi.js +11 -0
  420. package/web/dist/assets/{zoomable-image-CDmZ_Rtl.js → zoomable-image-BJJ64KGc.js} +1 -1
  421. package/web/dist/cezarion-lockup-dark.svg +1 -13
  422. package/web/dist/cezarion-lockup-light.svg +1 -13
  423. package/web/dist/cezarion-mark-dark.svg +1 -12
  424. package/web/dist/cezarion-mark-light.svg +1 -12
  425. package/web/dist/index.html +16 -21
  426. package/web/dist/assets/agent-accounts-CU2ZDv24.js +0 -1
  427. package/web/dist/assets/alert-dialog-C-xYE_dg.js +0 -43
  428. package/web/dist/assets/chevron-right-BENZ-1ZN.js +0 -1
  429. package/web/dist/assets/collapsible-DD9J91Iv.js +0 -1
  430. package/web/dist/assets/compare-variants-5FmqpFSN.js +0 -1
  431. package/web/dist/assets/diff-controls-Bj0YrioE.js +0 -1
  432. package/web/dist/assets/diff-rE8rm75J2.js +0 -3
  433. package/web/dist/assets/diff-stat-BKDCSVfJ.js +0 -1
  434. package/web/dist/assets/dropdown-menu-Ch5BYQh_.js +0 -1
  435. package/web/dist/assets/format-dBiNDNVe.js +0 -1
  436. package/web/dist/assets/git-toolbar-ClVvf5iF.js +0 -1
  437. package/web/dist/assets/github-BkNOyour.css +0 -1
  438. package/web/dist/assets/github-DeZMeMwJ.js +0 -1
  439. package/web/dist/assets/github-item-detail-W5F6GVjF.js +0 -1
  440. package/web/dist/assets/github-sidebar-model-DcMu-IK2.js +0 -1
  441. package/web/dist/assets/highlighter-CdHqIwFr.js +0 -3
  442. package/web/dist/assets/index-CatemBEL.js +0 -8
  443. package/web/dist/assets/index-MvfD8bZJ.css +0 -1
  444. package/web/dist/assets/markdown-zdKeLIYA.js +0 -2
  445. package/web/dist/assets/mobile-run-bar-BCfmFi8V.js +0 -1
  446. package/web/dist/assets/prompt-templates-CBPp39IU.js +0 -15
  447. package/web/dist/assets/reference-chip-D6owncJT.js +0 -1
  448. package/web/dist/assets/repo-git-CtdkZFey.js +0 -1
  449. package/web/dist/assets/run-header-BcjoDi0W.js +0 -1
  450. package/web/dist/assets/sheet-ChnYVrp1.js +0 -1
  451. package/web/dist/assets/skills-C8YKp2vr.js +0 -1
  452. package/web/dist/assets/task-changes-BW8YdWG8.js +0 -1
  453. package/web/dist/assets/task-commits-q2kB2y6L.js +0 -1
  454. package/web/dist/assets/task-files-pPL6_3Cr.js +0 -2
  455. package/web/dist/assets/task-github-item-BXF5R52U.js +0 -1
  456. package/web/dist/assets/task-thread-DtkFL3EC.js +0 -10
  457. package/web/dist/assets/textarea-DxlkE-Yd.js +0 -1
  458. package/web/dist/assets/token-metrics-DIAjrPlM.js +0 -1
  459. package/web/dist/assets/use-desktop-CrDny2uG.js +0 -1
  460. package/web/dist/assets/use-submit-shortcut-KWJsC906.js +0 -1
  461. package/web/dist/assets/workflows-TmcPEj2K.js +0 -11
  462. /package/web/dist/assets/{lib-BQXq3kEf2.js → lib-BQXq3kEf.js} +0 -0
@@ -1,18 +1,36 @@
1
+ import { opencodeSkillWarning } from './opencode-session-error.js';
2
+ import { summarizeRunnerStderr } from './runner-stderr.js';
3
+ import { finished } from 'node:stream/promises';
4
+ import { setTimeout as delay } from 'node:timers/promises';
1
5
  import { fileURLToPath } from 'node:url';
2
6
  import { randomUUID } from 'node:crypto';
3
- import { spawn as nodeSpawn } from 'node:child_process';
4
7
  import { parseEffort } from '../contract/index.js';
5
8
  import { request as httpRequest } from 'node:http';
6
9
  import { InputSubmissions } from './input-submissions.js';
7
- import { prependSystemPrompt, trackChildExit } from './agent-runner.js';
10
+ import { isSignalTerminationExit, prependSystemPrompt, trackChildExit } from './agent-runner.js';
11
+ import { signalSession, spawnSessionLeader } from './session-process.js';
8
12
  import { buildChildEnv } from './agent-env.js';
9
13
  import { ciOpenCodeEnv } from '../ci-wait/injection.js';
10
14
  import { parseAskRequest } from './ask.js';
11
- import { AUTO_END_DELAY_MS, DEFAULT_RUN_TIMEOUT_MS } from './runner-runtime.js';
15
+ import { boundOutputDrainAfterExit, AUTO_END_DELAY_MS, DEFAULT_RUN_TIMEOUT_MS } from './runner-runtime.js';
12
16
  import { formatModelIdentity, parseModelIdentity } from './model-identity.js';
13
17
  import { V1TextCoalescer } from './v1-text-coalescer.js';
14
18
  import { createOpencodeUiState, mapOpencodeEvent, opencodeSessionStarted, opencodeTurnStarted, } from './opencode-ui-mapper.js';
15
19
  const SERVER_START_TIMEOUT_MS = 30_000;
20
+ /**
21
+ * Pause before the one retry of a server that exited before listening (#872).
22
+ * opencode 1.18.33, started within ~2 s of SIGTERM to the previous server on
23
+ * the same DB, printed `ServeError` and exited; the next start succeeded. The
24
+ * failing start can die well inside that window, so the pause alone spans it;
25
+ * it is only ever paid by a start that already failed.
26
+ */
27
+ export const SERVE_START_RETRY_DELAY_MS = 2_000;
28
+ /** The URL wait saw the process exit before it printed a URL. */
29
+ class ServeExitedBeforeListening extends Error {
30
+ constructor() {
31
+ super('opencode serve exited before it started listening');
32
+ }
33
+ }
16
34
  /** Grace between the teardown SIGTERM and the SIGKILL that follows it. */
17
35
  export const KILL_GRACE_MS = 4_000;
18
36
  /**
@@ -20,8 +38,9 @@ export const KILL_GRACE_MS = 4_000;
20
38
  * the opencode TUI talks to) with an SSE event stream. One server per session,
21
39
  * bound to the run's `cwd` (worktree), gives OpenCode the same multi-turn shape
22
40
  * as the Claude runner: each `sendMessage` posts another prompt to the same
23
- * session (history is kept server-side), and `session/abort` cancels. The
24
- * current adapter opens a fresh session with the continuation prompt on Continue.
41
+ * session (history is kept server-side), and `session/abort` cancels.
42
+ * Continue and restart recovery GET /session/{id} and prompt that session; a 404
43
+ * falls back to one fresh session with the full system prompt.
25
44
  *
26
45
  * Auth = the host's opencode config/logins. The agent runs autonomously
27
46
  * (auto-approved permissions); this adapter does not map `spec.allowedTools`.
@@ -32,8 +51,8 @@ export const KILL_GRACE_MS = 4_000;
32
51
  * What `opencode serve` receives from each `AgentRunSpec` field (#284). The
33
52
  * server auto-approves permissions and this adapter maps no per-tool
34
53
  * allowlist, so `allowedTools`/`bashAllowlist` are declared dropped rather than
35
- * mapped (spec 2026-07-17-permission-modes); every start opens a fresh session,
36
- * so `sessionId`/`resume` never reach the wire. Held against the recorded HTTP
54
+ * mapped (spec 2026-07-17-permission-modes). Resume GETs `/session/{id}` and
55
+ * prompts it; a 404 opens one fresh session. Held against the recorded HTTP
37
56
  * requests by the harness parity matrix.
38
57
  */
39
58
  export const OPENCODE_SPEC_SUPPORT = {
@@ -43,32 +62,36 @@ export const OPENCODE_SPEC_SUPPORT = {
43
62
  images: { honored: false, reason: 'the adapter posts text parts only; image blocks are dropped' },
44
63
  cwd: { honored: true, via: 'opencode serve spawn cwd; the session is bound to it' },
45
64
  allowedTools: { honored: false, reason: 'permissions are auto-approved server-side and no per-tool allowlist is mapped' },
46
- restrictNativeDelegation: { honored: true, via: 'POST /session permission rule denying task (D1)' },
65
+ restrictNativeDelegation: { honored: true, via: 'POST /session permission rule denying task (D1); PATCH /session/{id} permission on resume when the deny is absent — the deny persists on the session once added (a later unrestricted Continue cannot remove it; fails closed)' },
47
66
  bashAllowlist: { honored: false, reason: 'no per-tool allowlist is mapped, so no command-prefix restriction either' },
48
67
  additionalDirectories: { honored: false, reason: 'the server works from cwd; no extra-root mapping' },
49
68
  env: { honored: true, via: 'merged over the child env through buildChildEnv' },
50
69
  model: { honored: true, via: 'prompt_async model { providerID, modelID }, split from provider/model' },
51
70
  effort: { honored: true, via: 'prompt_async variant, canonical level' },
52
71
  timeoutMs: { honored: true, via: 'wall-clock kill switch on the child process' },
53
- sessionId: { honored: false, reason: 'every start opens a fresh server session; the id is only reported back on the result' },
54
- resume: { honored: false, reason: 'Continue opens a fresh session carrying the continuation prompt' },
72
+ sessionId: { honored: true, via: 'GET /session/{id} when resume is set; a fresh POST /session mints its own id' },
73
+ resume: { honored: true, via: 'GET /session/{id} in place of POST /session; 404 falls back to a fresh session' },
74
+ resumeFallbackSystemPrompt: { honored: true, via: 'opening prependSystemPrompt of a fresh POST /session when GET /session/{id} returns 404 on resume' },
55
75
  };
56
76
  /** How long an idle session may sit on steered input before it counts as a lost wake. */
57
77
  export const OPENCODE_LOST_WAKE_GRACE_MS = 2_000;
58
78
  export class OpencodeServerRunner {
59
79
  backend = 'opencode';
60
80
  specSupport = OPENCODE_SPEC_SUPPORT;
81
+ systemPromptOnResume = 'in-thread';
61
82
  inputDelivery = {
62
83
  mode: 'steer', consumption: 'observable',
63
84
  via: 'prompt_async while busy; assistant message.updated parentID at consumption',
64
85
  };
65
86
  bin;
66
87
  timeoutMs;
88
+ serverStartTimeoutMs;
67
89
  lastSession = null;
68
90
  constructor(opts = {}) {
69
91
  this.bin = opts.bin ?? process.env.CEZ_OPENCODE_BIN ?? (process.env.CEZ_DRY_RUN === '1'
70
92
  ? fileURLToPath(new URL('../../scripts/mock-opencode-serve.mjs', import.meta.url)) : 'opencode');
71
93
  this.timeoutMs = opts.timeoutMs ?? DEFAULT_RUN_TIMEOUT_MS;
94
+ this.serverStartTimeoutMs = opts.serverStartTimeoutMs ?? SERVER_START_TIMEOUT_MS;
72
95
  }
73
96
  run(spec, onEvent) {
74
97
  return this.startSession(spec, onEvent, { autoEndAfterFirstTurn: true }).result;
@@ -77,7 +100,7 @@ export class OpencodeServerRunner {
77
100
  this.lastSession?.interrupt();
78
101
  }
79
102
  startSession(spec, onEvent, opts = {}) {
80
- const session = new OpencodeSession(this.bin, this.timeoutMs, spec, onEvent, opts);
103
+ const session = new OpencodeSession(this.bin, this.timeoutMs, this.serverStartTimeoutMs, spec, onEvent, opts);
81
104
  this.lastSession = session;
82
105
  return session;
83
106
  }
@@ -85,20 +108,27 @@ export class OpencodeServerRunner {
85
108
  /** One live `opencode serve` process driving a single session. */
86
109
  class OpencodeSession {
87
110
  bin;
111
+ serverStartTimeoutMs;
88
112
  spec;
89
113
  onEvent;
90
114
  opts;
91
115
  result;
92
- child;
93
- /** "Has the server actually terminated?" — never `child.killed`, which only
94
- * reports delivery and would disarm the escalation (#844/#858). */
95
- hasExited;
116
+ /** The live server process. A start retry replaces it (#872), so every
117
+ * liveness, pid, signal and stderr read goes through this field. */
118
+ serve;
119
+ /** The first start, once the retry spawned for it ALSO exited before
120
+ * listening on its own (#872) — the one case that names both attempts. */
121
+ failedStart;
122
+ /** The retry's own spawn threw synchronously (EAGAIN, EMFILE…), so no second
123
+ * process exists and the first start's exit alone would misname the failure. */
124
+ retrySpawnError;
125
+ /** Aborted by teardown; cancels a pending start retry. */
126
+ startRetry = new AbortController();
96
127
  serverOpen = true;
128
+ exitFailure;
97
129
  baseUrl;
98
130
  sessionId;
99
131
  ready;
100
- resolveExit;
101
- exited;
102
132
  sse = new AbortController();
103
133
  toolCalls = [];
104
134
  textChunks = [];
@@ -161,42 +191,20 @@ class OpencodeSession {
161
191
  /** Bumped by `discardQueuedMessages` so a `prompt()` waiter that captured
162
192
  * the previous generation returns instead of posting after idle. */
163
193
  queuedPromptGeneration = 0;
194
+ /** Human `prompt()` waiters in the current generation that have not posted yet (#486). */
195
+ humanPromptWaiters = 0;
164
196
  autoEndTimer;
165
- spawnFailed = null;
166
197
  timedOut = false;
167
198
  openingPromptFailed = false;
168
- /** One teardown per session — see `terminate()`. */
169
- signalled = false;
170
- constructor(bin, timeoutMs, spec, onEvent, opts) {
199
+ constructor(bin, timeoutMs, serverStartTimeoutMs, spec, onEvent, opts) {
171
200
  this.bin = bin;
201
+ this.serverStartTimeoutMs = serverStartTimeoutMs;
172
202
  this.spec = spec;
173
203
  this.onEvent = onEvent;
174
204
  this.opts = opts;
175
- // Random high port; the actual bound URL is read back from stdout.
176
- const port = 40000 + Math.floor(Math.random() * 20000);
177
- try {
178
- this.child = nodeSpawn(bin, ['serve', '--hostname', '127.0.0.1', '--port', String(port)], {
179
- cwd: spec.cwd,
180
- env: buildChildEnv({ backend: 'opencode', extraEnv: ciOpenCodeEnv(spec) }),
181
- });
182
- }
183
- catch (err) {
184
- throw wrapSpawnError(err, bin);
185
- }
186
- this.hasExited = trackChildExit(this.child);
187
- this.child.on('error', (err) => {
188
- this.spawnFailed = wrapSpawnError(err, bin);
189
- });
190
- this.exited = new Promise((resolve) => {
191
- this.resolveExit = resolve;
192
- });
193
- this.child.once('exit', () => this.resolveExit());
194
- this.child.once('close', () => this.resolveExit());
195
- const stderrChunks = [];
196
- this.child.stderr.setEncoding('utf8');
197
- this.child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
205
+ this.serve = this.spawnServe();
198
206
  // The server prints its URL on stdout once listening.
199
- const urlReady = this.waitForServerUrl(port);
207
+ const urlReady = this.startServer();
200
208
  const limitMs = spec.timeoutMs ?? timeoutMs;
201
209
  let deadline;
202
210
  if (limitMs > 0) {
@@ -214,12 +222,12 @@ class OpencodeSession {
214
222
  try {
215
223
  await this.ready;
216
224
  // Live for the whole session; the SSE loop runs until end()/interrupt.
217
- await this.exited;
225
+ await this.serve.exited;
218
226
  }
219
227
  catch (err) {
220
228
  if (!this.timedOut && !this.openingPromptFailed) {
221
229
  const message = err instanceof Error ? err.message : String(err);
222
- this.emit({ type: 'error', message: `opencode: ${message}` });
230
+ this.exitFailure = `opencode: ${message}`;
223
231
  }
224
232
  }
225
233
  finally {
@@ -232,9 +240,49 @@ class OpencodeSession {
232
240
  this.agentRequest?.abort();
233
241
  this.terminate();
234
242
  }
235
- await this.exited;
236
- if (this.spawnFailed)
237
- throw this.spawnFailed;
243
+ const serve = this.serve;
244
+ await serve.exited;
245
+ await serve.outputDrained;
246
+ if (serve.spawnFailed) {
247
+ this.emit({ type: 'error', message: serve.spawnFailed.message });
248
+ throw serve.spawnFailed;
249
+ }
250
+ // SSE closure can precede the process exit event. Choose one authoritative
251
+ // error after settlement, before the synthetic turn-end, retaining the code.
252
+ const code = serve.child.exitCode;
253
+ const crashed = code !== null && code !== 0 && !(serve.signalled && isSignalTerminationExit(code));
254
+ if (!this.timedOut && (crashed || this.exitFailure)) {
255
+ const stderr = serve.stderrChunks.join('');
256
+ const detail = summarizeRunnerStderr(stderr);
257
+ if (this.retrySpawnError) {
258
+ if (stderr.trim())
259
+ this.emit({ type: 'note', message: `opencode serve stderr:\n${stderr}` });
260
+ this.emit({
261
+ type: 'error',
262
+ message: `opencode serve exited before listening (${describeEarlyExit(serve)}) and its retry could not start: ${this.retrySpawnError.message}`,
263
+ });
264
+ }
265
+ else if (this.failedStart) {
266
+ // #872: the retry failed too — one error naming both attempts.
267
+ const attempts = [this.failedStart, serve];
268
+ const notes = attempts.map((attempt, i) => {
269
+ const text = attempt.stderrChunks.join('');
270
+ return text.trim() ? `opencode serve stderr (attempt ${i + 1}):\n${text}` : '';
271
+ }).filter(Boolean);
272
+ if (notes.length)
273
+ this.emit({ type: 'note', message: notes.join('\n') });
274
+ const described = attempts.map((attempt, i) => `attempt ${i + 1}: ${describeEarlyExit(attempt)}`);
275
+ this.emit({ type: 'error', message: `opencode serve exited before listening on both attempts — ${described.join('; ')}` });
276
+ }
277
+ else {
278
+ if (stderr.trim())
279
+ this.emit({ type: 'note', message: `opencode serve stderr:\n${stderr}` });
280
+ const message = crashed
281
+ ? `opencode serve exited with code ${code}${detail ? ` — ${detail}` : ''}`
282
+ : this.exitFailure;
283
+ this.emit({ type: 'error', message });
284
+ }
285
+ }
238
286
  // Timeout/interrupt can cut the SSE feed before its `session.idle` —
239
287
  // close the turn (idempotent) so consumers still see the v1 boundary,
240
288
  // and recover prose buffered mid-part.
@@ -261,7 +309,7 @@ class OpencodeSession {
261
309
  return this.serverOpen;
262
310
  }
263
311
  get pid() {
264
- return this.child.pid;
312
+ return this.serve.child.pid;
265
313
  }
266
314
  sendAgentMessage(content, inputIds = []) {
267
315
  // #505: a busy session is steered with prompt_async; only a pending native question,
@@ -333,6 +381,13 @@ class OpencodeSession {
333
381
  discardQueuedMessages() {
334
382
  this.queuedPromptGeneration += 1;
335
383
  this.queuedQuestionMessages.length = 0;
384
+ this.humanPromptWaiters = 0; // #486: must read false immediately, not when waiters resume
385
+ }
386
+ holdsHumanInput() {
387
+ return this.humanPromptWaiters > 0 || this.queuedQuestionMessages.length > 0;
388
+ }
389
+ heldHumanInputCount() {
390
+ return this.humanPromptWaiters + this.queuedQuestionMessages.length;
336
391
  }
337
392
  deliverPrompt(text) {
338
393
  this.pendingPromptRequests += 1;
@@ -399,35 +454,136 @@ class OpencodeSession {
399
454
  * server it was written for: one that installs its own SIGTERM handler stayed
400
455
  * alive with `killed = true` and `exitCode === null`, outliving the whole
401
456
  * window (#858, the same defect #844 fixed for the other two backends). Every
402
- * caller here is followed by `await this.exited`, so a server that survived
457
+ * caller here is followed by `await serve.exited`, so a server that survived
403
458
  * SIGTERM did not just leak — it hung the session's result forever.
404
459
  *
405
460
  * One teardown per session: all three call sites can run for the same session
406
461
  * (`interrupt()` on the deadline, then the result promise's `finally`), and
407
462
  * once SIGTERM is out with SIGKILL armed there is nothing a second pass adds.
408
463
  * The old `!child.killed` test deduplicated this as a side effect of being
409
- * wrong; `signalled` keeps that property on purpose.
464
+ * wrong; `signalled` keeps that property on purpose. It lives on the process,
465
+ * not the session: only the live one is ever signalled, and a start retry
466
+ * (#872) replaces it only after the first exited unsignalled.
467
+ *
468
+ * Teardown also cancels a pending start retry, so no second server spawns
469
+ * after `end()`, `interrupt()` or the deadline.
410
470
  */
411
471
  terminate() {
412
- if (this.signalled || this.hasExited())
472
+ this.startRetry.abort();
473
+ const serve = this.serve;
474
+ if (serve.signalled || serve.hasExited())
413
475
  return;
414
- this.signalled = true;
415
- this.child.kill('SIGTERM');
476
+ serve.signalled = true;
477
+ signalSession(serve.child, 'SIGTERM');
416
478
  setTimeout(() => {
417
- if (this.hasExited())
479
+ if (serve.hasExited())
418
480
  return;
419
- this.child.kill('SIGKILL');
481
+ signalSession(serve.child, 'SIGKILL');
420
482
  }, KILL_GRACE_MS).unref?.();
421
483
  }
422
484
  // ---- server lifecycle ---------------------------------------------------
423
- waitForServerUrl(fallbackPort) {
485
+ /** Spawn one `opencode serve` on a random high port and wire its lifecycle.
486
+ * A synchronous spawn throw is wrapped; an async spawn error is latched on
487
+ * the process record. */
488
+ spawnServe() {
489
+ // Random high port; the actual bound URL is read back from stdout.
490
+ const port = 40000 + Math.floor(Math.random() * 20000);
491
+ let child;
492
+ try {
493
+ child = spawnSessionLeader(this.bin, ['serve', '--hostname', '127.0.0.1', '--port', String(port)], {
494
+ cwd: this.spec.cwd,
495
+ env: buildChildEnv({ backend: 'opencode', extraEnv: ciOpenCodeEnv(this.spec) }),
496
+ });
497
+ }
498
+ catch (err) {
499
+ throw wrapSpawnError(err, this.bin);
500
+ }
501
+ const hasExited = trackChildExit(child);
502
+ boundOutputDrainAfterExit(child);
503
+ // `exit` settles process liveness, not its pipes. The shared 250ms drain
504
+ // bound prevents an inherited descendant pipe from retaining this session.
505
+ const outputDrained = Promise.all([child.stdout, child.stderr].map(stream => finished(stream, { cleanup: true }).catch(() => undefined)));
506
+ const exited = new Promise((resolve) => {
507
+ child.once('exit', () => resolve());
508
+ child.once('close', () => resolve());
509
+ });
510
+ const serve = {
511
+ child, hasExited, exited, outputDrained, port,
512
+ stderrChunks: [], spawnFailed: null, signalled: false,
513
+ };
514
+ child.on('error', (err) => {
515
+ serve.spawnFailed = wrapSpawnError(err, this.bin);
516
+ });
517
+ child.stderr.setEncoding('utf8');
518
+ child.stderr.on('data', (chunk) => serve.stderrChunks.push(chunk));
519
+ return serve;
520
+ }
521
+ /**
522
+ * Wait for the server URL, retrying once — after `SERVE_START_RETRY_DELAY_MS`,
523
+ * on a fresh port — when the process exited before it printed one (#872).
524
+ * Never retried: a spawn error (the process never ran), the no-URL timeout
525
+ * (it falls back to the requested port), and a session already torn down.
526
+ */
527
+ async startServer() {
528
+ try {
529
+ return await this.waitForServerUrl(this.serve);
530
+ }
531
+ catch (err) {
532
+ const first = this.serve;
533
+ if (!(err instanceof ServeExitedBeforeListening))
534
+ throw err;
535
+ // Settle the process first: an ENOENT `error` and late stderr both land
536
+ // around `exit`, and the retry decision and its note need them.
537
+ await first.exited;
538
+ await first.outputDrained;
539
+ if (first.spawnFailed || first.child.pid === undefined || !this.serverOpen || this.timedOut)
540
+ throw err;
541
+ const detail = summarizeRunnerStderr(first.stderrChunks.join(''));
542
+ this.emit({
543
+ type: 'note',
544
+ message: `opencode serve exited before listening (${describeExit(first)}); retrying once${detail ? ` — ${detail}` : ''}`,
545
+ });
546
+ try {
547
+ await delay(SERVE_START_RETRY_DELAY_MS, undefined, { signal: this.startRetry.signal });
548
+ }
549
+ catch {
550
+ // Torn down during the pause: report the first start's failure as before.
551
+ throw err;
552
+ }
553
+ if (this.startRetry.signal.aborted || !this.serverOpen || this.timedOut)
554
+ throw err;
555
+ let next;
556
+ try {
557
+ next = this.spawnServe();
558
+ }
559
+ catch (spawnErr) {
560
+ this.retrySpawnError = spawnErr instanceof Error ? spawnErr : new Error(String(spawnErr));
561
+ throw spawnErr;
562
+ }
563
+ this.serve = next;
564
+ if (next.child.pid !== undefined)
565
+ this.opts.onPidChange?.(next.child.pid);
566
+ try {
567
+ return await this.waitForServerUrl(next);
568
+ }
569
+ catch (retryErr) {
570
+ // Only an unprompted second early exit names both attempts; a retry
571
+ // torn down by the session reports through the ordinary paths.
572
+ if (retryErr instanceof ServeExitedBeforeListening && !next.signalled)
573
+ this.failedStart = first;
574
+ throw retryErr;
575
+ }
576
+ }
577
+ }
578
+ waitForServerUrl(serve) {
579
+ const { child } = serve;
424
580
  return new Promise((resolve, reject) => {
425
581
  let buffer = '';
426
582
  const timer = setTimeout(() => {
427
583
  cleanup();
428
584
  // Nothing parsed — try the port we asked for.
429
- resolve(`http://127.0.0.1:${fallbackPort}`);
430
- }, SERVER_START_TIMEOUT_MS);
585
+ resolve(`http://127.0.0.1:${serve.port}`);
586
+ }, this.serverStartTimeoutMs);
431
587
  timer.unref?.();
432
588
  const onData = (chunk) => {
433
589
  buffer += chunk;
@@ -439,36 +595,92 @@ class OpencodeSession {
439
595
  };
440
596
  const onExit = () => {
441
597
  cleanup();
442
- reject(new Error('opencode serve exited before it started listening'));
598
+ reject(new ServeExitedBeforeListening());
443
599
  };
444
600
  const cleanup = () => {
445
601
  clearTimeout(timer);
446
- this.child.stdout.off('data', onData);
447
- this.child.off('exit', onExit);
602
+ child.stdout.off('data', onData);
603
+ child.off('exit', onExit);
448
604
  };
449
- this.child.stdout.setEncoding('utf8');
450
- this.child.stdout.on('data', onData);
451
- this.child.once('exit', onExit);
605
+ child.stdout.setEncoding('utf8');
606
+ child.stdout.on('data', onData);
607
+ child.once('exit', onExit);
452
608
  });
453
609
  }
610
+ nativeDelegationPermission() {
611
+ return { permission: [{ permission: 'task', pattern: '*', action: 'deny' }] };
612
+ }
613
+ /** 404 → `'missing'`. Any other non-OK status fails bootstrap (no fallback). */
614
+ async tryGetSession(id) {
615
+ if (!this.baseUrl)
616
+ throw new Error('opencode server not ready');
617
+ const res = await fetch(`${this.baseUrl}/session/${encodeURIComponent(id)}`, { method: 'GET' });
618
+ if (res.status === 404)
619
+ return 'missing';
620
+ if (!res.ok) {
621
+ const detail = await res.text().catch(() => '');
622
+ throw new Error(`GET /session/${id} → ${res.status} ${detail.slice(0, 200)}`);
623
+ }
624
+ const text = await res.text();
625
+ if (!text)
626
+ return { id };
627
+ try {
628
+ const parsed = JSON.parse(text);
629
+ return isRecord(parsed) ? parsed : { id };
630
+ }
631
+ catch {
632
+ return { id };
633
+ }
634
+ }
635
+ async attachSession(id, applyDelegationRule) {
636
+ this.sessionId = id;
637
+ this.emit({ type: 'session', sessionId: id });
638
+ this.emitUi((state) => opencodeSessionStarted(id, state));
639
+ if (applyDelegationRule) {
640
+ await this.http('PATCH', `/session/${encodeURIComponent(id)}`, this.nativeDelegationPermission());
641
+ }
642
+ }
454
643
  async bootstrap() {
455
- const created = await this.http('POST', '/session', {
456
- title: 'cezar task',
457
- // OpenCode 1.18.29 /doc: session.create permission rules. The native task
458
- // tool checks this permission; other agent/project permissions stay intact.
459
- ...(this.spec.restrictNativeDelegation ? { permission: [{ permission: 'task', pattern: '*', action: 'deny' }] } : {}),
460
- });
461
- this.sessionId = stringField(created, 'id');
462
- if (!this.sessionId)
463
- throw new Error('opencode did not return a session id');
464
- this.emit({ type: 'session', sessionId: this.sessionId });
465
- const sessionId = this.sessionId;
466
- this.emitUi((state) => opencodeSessionStarted(sessionId, state));
644
+ const requested = this.spec.resume ? this.spec.sessionId : undefined;
645
+ let resumed = false;
646
+ if (requested) {
647
+ const existing = await this.tryGetSession(requested);
648
+ if (existing === 'missing') {
649
+ this.emit({
650
+ type: 'note',
651
+ message: `OpenCode session ${requested} no longer exists; the continuation runs in a fresh session without the earlier conversation.`,
652
+ });
653
+ }
654
+ else {
655
+ const applyDelegationRule = Boolean(this.spec.restrictNativeDelegation)
656
+ && !hasNativeDelegationDeny(existing.permission);
657
+ await this.attachSession(stringField(existing, 'id') ?? requested, applyDelegationRule);
658
+ resumed = true;
659
+ }
660
+ }
661
+ if (!resumed) {
662
+ const created = await this.http('POST', '/session', {
663
+ title: 'cezar task',
664
+ // OpenCode 1.18.33 /doc: session.create permission rules. The native task
665
+ // tool checks this permission; other agent/project permissions stay intact.
666
+ ...(this.spec.restrictNativeDelegation ? this.nativeDelegationPermission() : {}),
667
+ });
668
+ const sessionId = stringField(created, 'id');
669
+ if (!sessionId)
670
+ throw new Error('opencode did not return a session id');
671
+ await this.attachSession(sessionId, false);
672
+ }
467
673
  // The SSE subscription must be LIVE before the first prompt posts —
468
674
  // events the server emits while the POST is in flight would otherwise be
469
675
  // lost (a race this await closes; the bundled mock made it visible).
470
676
  await this.consumeEvents();
471
- const first = prependSystemPrompt(this.spec.systemPrompt, this.spec.userPrompt);
677
+ // Lost-session fallback uses the full skill-inclusive prompt Continue
678
+ // stashed in resumeFallbackSystemPrompt; a true resume re-prepends only
679
+ // extra+handoff (the skill already lives in the thread).
680
+ const systemPrompt = !resumed && requested
681
+ ? (this.spec.resumeFallbackSystemPrompt ?? this.spec.systemPrompt)
682
+ : this.spec.systemPrompt;
683
+ const first = prependSystemPrompt(systemPrompt, this.spec.userPrompt);
472
684
  await this.prompt(first, 'opening');
473
685
  if (this.refusedBeforeOpening)
474
686
  queueMicrotask(() => {
@@ -485,8 +697,14 @@ class OpencodeSession {
485
697
  // resume in FIFO order; a teardown (`finishTurn` runs on every exit
486
698
  // path) releases them into the `serverOpen` check below.
487
699
  const generation = this.queuedPromptGeneration;
700
+ const humanWaiter = origin === 'human';
701
+ if (humanWaiter)
702
+ this.humanPromptWaiters += 1;
488
703
  while (this.turnActive)
489
704
  await this.turnFinished;
705
+ if (humanWaiter && generation === this.queuedPromptGeneration) {
706
+ this.humanPromptWaiters = Math.max(0, this.humanPromptWaiters - 1);
707
+ }
490
708
  if (generation !== this.queuedPromptGeneration)
491
709
  return;
492
710
  // A queued prompt may have started waiting before the preceding idle armed
@@ -524,21 +742,21 @@ class OpencodeSession {
524
742
  this.interrupt();
525
743
  }
526
744
  }
745
+ else if (origin === 'opening') {
746
+ this.emit({ type: 'note', message: `opencode: prompt failed: ${message}` });
747
+ // A dropped ACK can be the first sign of a process crash. Retain HTTP
748
+ // rejection details, but let settlement choose one error before turn-end.
749
+ this.openingPromptFailed = true;
750
+ this.exitFailure = `opencode: ${message}`;
751
+ this.interrupt();
752
+ }
527
753
  else {
528
754
  this.emit({ type: 'note', message: `opencode: prompt failed: ${message}` });
529
- if (origin === 'opening') {
530
- // Opening failure was always fatal. Report it before the synthetic
531
- // boundary, and do not emit it again from the bootstrap result catch.
532
- this.openingPromptFailed = true;
533
- if (!this.timedOut)
534
- this.emit({ type: 'error', message: `opencode: ${message}` });
535
- this.interrupt();
536
- }
537
755
  }
538
- if (!this.providerErrorPending)
756
+ if (!this.providerErrorPending && origin !== 'opening')
539
757
  this.finishTurn();
540
- // Bootstrap still rejects; its result catch suppresses the already
541
- // reported opening error. Human follow-ups keep their nonfatal note.
758
+ // Bootstrap still rejects; its result path reports the latched opening
759
+ // failure. Human follow-ups keep their nonfatal note and turn boundary.
542
760
  throw err;
543
761
  }
544
762
  }
@@ -701,10 +919,9 @@ class OpencodeSession {
701
919
  // note: the turn's remaining output is lost, so the step must record a
702
920
  // failure (run.ts classifies from v1 `error` only), never pass as an
703
921
  // empty success. Then close the turn (flushing what did arrive) and end
704
- // the session; "Continue" resumes it on a fresh server.
922
+ // the session; "Continue" GETs this session id on the next server.
705
923
  if (this.serverOpen && !this.sse.signal.aborted) {
706
- this.emit({ type: 'error', message: 'opencode: event stream closed unexpectedly' });
707
- this.finishTurn();
924
+ this.exitFailure = 'opencode: event stream closed unexpectedly';
708
925
  this.end();
709
926
  }
710
927
  }
@@ -725,7 +942,7 @@ class OpencodeSession {
725
942
  }
726
943
  this.emitUi((state) => mapOpencodeEvent(evt, state));
727
944
  const sid = stringField(evt.properties ?? {}, 'sessionID');
728
- if (evt.type === 'session.error' && (sid === undefined || sid === this.sessionId) && this.agentRequest) {
945
+ if (evt.type === 'session.error' && !opencodeSkillWarning(evt.properties ?? {}) && (sid === undefined || sid === this.sessionId) && this.agentRequest) {
729
946
  // HTTP and SSE are independent sockets: the error can overtake a positive
730
947
  // prompt ACK. Let that exact request settle before v1 makes RunManager
731
948
  // interrupt it. Keep later SSE idle frames behind the same barrier.
@@ -799,7 +1016,12 @@ class OpencodeSession {
799
1016
  this.finishTurn();
800
1017
  }
801
1018
  else if (type === 'session.error') {
802
- // The wire's only failure signal, now that the prompt POST returns
1019
+ const warning = opencodeSkillWarning(props);
1020
+ if (warning) {
1021
+ this.emit({ type: 'note', message: warning });
1022
+ return;
1023
+ }
1024
+ // The wire's failure signal, now that the prompt POST returns
803
1025
  // before the turn runs: forward it to v1, whose `error` events are what
804
1026
  // run.ts records failed steps from — dropped, the terminal idle would
805
1027
  // file a provider/auth failure as a successful step. The idle that
@@ -1125,6 +1347,15 @@ function clippedString(value, max) {
1125
1347
  function isRecord(value) {
1126
1348
  return typeof value === 'object' && value !== null && !Array.isArray(value);
1127
1349
  }
1350
+ /** OpenCode 1.18.33 PATCHes permission by append (no replace, no dedupe). */
1351
+ function hasNativeDelegationDeny(permission) {
1352
+ if (!Array.isArray(permission))
1353
+ return false;
1354
+ return permission.some((rule) => isRecord(rule)
1355
+ && rule.permission === 'task'
1356
+ && rule.pattern === '*'
1357
+ && rule.action === 'deny');
1358
+ }
1128
1359
  function sleep(ms) {
1129
1360
  return new Promise((resolve) => setTimeout(resolve, ms));
1130
1361
  }
@@ -1183,6 +1414,16 @@ function safeStringify(value) {
1183
1414
  return String(value);
1184
1415
  }
1185
1416
  }
1417
+ /** `code N` or `signal S` for a server process that exited. */
1418
+ function describeExit(serve) {
1419
+ const { exitCode, signalCode } = serve.child;
1420
+ return exitCode !== null ? `code ${exitCode}` : `signal ${signalCode}`;
1421
+ }
1422
+ /** `code N — <stderr summary>` for one start that exited before listening. */
1423
+ function describeEarlyExit(serve) {
1424
+ const detail = summarizeRunnerStderr(serve.stderrChunks.join(''));
1425
+ return `${describeExit(serve)}${detail ? ` — ${detail}` : ''}`;
1426
+ }
1186
1427
  function wrapSpawnError(err, bin) {
1187
1428
  const code = err?.code;
1188
1429
  if (code === 'ENOENT') {