@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
@@ -0,0 +1,990 @@
1
+ import { parseEffort } from '../contract/index.js';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, resolve as resolvePath } from 'node:path';
4
+ import { parseAskMarker } from './ask.js';
5
+ import { isSignalTerminationExit } from './agent-runner.js';
6
+ import { signalSession, spawnSessionLeader } from './session-process.js';
7
+ import { buildChildEnv } from './agent-env.js';
8
+ import { cezarToolNames } from '../ci-wait/tools.js';
9
+ import { readNdjson } from './ndjson.js';
10
+ import { createOmpUiState, mapOmpRpcMessage, ompFlushProviderError, ompMessageUsage, ompProviderErrorMessage, ompTurnStarted, } from './omp-ui-mapper.js';
11
+ import { summarizeRunnerStderr } from './runner-stderr.js';
12
+ import { V1TextCoalescer } from './v1-text-coalescer.js';
13
+ import { InputSubmissions } from './input-submissions.js';
14
+ import { boundOutputDrainAfterExit, AUTO_END_DELAY_MS, DEFAULT_RUN_TIMEOUT_MS, KILL_GRACE_MS } from './runner-runtime.js';
15
+ /**
16
+ * OMP's built-in tool names at the pinned version (`src/tools/builtin-names.ts`, oh-my-pi
17
+ * v18.4.11; the real binary lists the same 30 in its `--tools` rejection). `--tools` is an
18
+ * allowlist validated against the registry, and an unknown name exits 2 before any frame, so
19
+ * only these, cezar's mapped names and `mcp__*` ever reach it.
20
+ */
21
+ export const OMP_BUILTIN_TOOL_NAMES = [
22
+ 'read', 'bash', 'edit', 'ast_grep', 'ast_edit', 'ask', 'debug', 'ida', 'eval', 'github',
23
+ 'glob', 'grep', 'find', 'lsp', 'checkpoint', 'rewind', 'context_notes', 'new_context',
24
+ 'security_scan', 'task', 'wait', 'todo', 'web_search', 'write', 'memory_edit', 'retain',
25
+ 'recall', 'reflect', 'learn', 'manage_skill',
26
+ ];
27
+ /**
28
+ * The tools OMP v18.4.11 enables with no `--tools` and no user config (recorded from the real
29
+ * binary's `get_state.dumpTools`). Only read under governed delegation, which must name a list
30
+ * to leave `task`, `wait` and `eval` out of it.
31
+ */
32
+ const OMP_DEFAULT_TOOL_NAMES = [
33
+ 'read', 'bash', 'edit', 'eval', 'glob', 'grep', 'task', 'wait', 'todo', 'web_search', 'write',
34
+ ];
35
+ /** cezar tool names with an OMP equivalent; OMP built-ins and `mcp__*` pass through as-is. */
36
+ const OMP_TOOL_MAP = {
37
+ Read: 'read',
38
+ Edit: 'edit',
39
+ Write: 'write',
40
+ Bash: 'bash',
41
+ Grep: 'grep',
42
+ Glob: 'glob',
43
+ Subagent: 'task',
44
+ Task: 'task',
45
+ TodoWrite: 'todo',
46
+ WebSearch: 'web_search',
47
+ // No `WebFetch`: OMP v18.4.11 has no `fetch` built-in, and its `read` also reads local files,
48
+ // so mapping a web-only grant onto it would widen the grant. It is dropped (fail closed).
49
+ };
50
+ /**
51
+ * D1: tools that let the agent spawn agents outside cezar's governance. `wait` only joins `task`
52
+ * work; `eval` has `agent()`/`workpool()` helpers and no v18.4.11 setting turns them off (Ruling 6).
53
+ */
54
+ const OMP_DELEGATION_TOOLS = new Set(['task', 'wait', 'eval']);
55
+ /**
56
+ * What the omp CLI receives from each `AgentRunSpec` field (#284, spec § Spawn and spec
57
+ * support). Held against the mock's recorded argv and RPC by the harness parity matrix.
58
+ */
59
+ export const OMP_SPEC_SUPPORT = {
60
+ cezarTools: { honored: true, via: 'explicit CI --extension; cezar_wait_for_ci admitted when --tools is set' },
61
+ systemPrompt: { honored: true, via: '--append-system-prompt' },
62
+ userPrompt: { honored: true, via: 'RPC prompt.message' },
63
+ images: { honored: true, via: "RPC prompt.images ({type:'image', data, mimeType})" },
64
+ cwd: { honored: true, via: 'spawn cwd' },
65
+ allowedTools: { honored: true, via: '--tools, mapped onto OMP names; unmapped names dropped (fail closed)' },
66
+ restrictNativeDelegation: {
67
+ honored: true,
68
+ via: '--config overlay denying task, and task/wait left out of --tools (D1); eval is left out too, since OMP v18.4.11 has no setting that disables its agent()/workpool() helpers; with allowedTools undefined D1 passes an explicit --tools list (OMP\'s default set minus those), failing closed rather than widening to the defaults',
69
+ },
70
+ bashAllowlist: { honored: true, via: "no prefix equivalent: bash dropped from --tools when an allowlist is set (Pi's rule); with allowedTools unset, OMP's default set is named first so bash can be left out" },
71
+ additionalDirectories: { honored: true, via: '--add-dir per directory; an omp build that rejects --add-dir is respawned once without it, with a v1 note (Ruling 22)' },
72
+ env: { honored: true, via: 'merged over the child env through buildChildEnv' },
73
+ model: { honored: true, via: '--model provider/model' },
74
+ effort: { honored: true, via: '--thinking, canonical level' },
75
+ timeoutMs: { honored: true, via: 'wall-clock kill switch' },
76
+ sessionId: { honored: true, via: '--resume <id> when resume is set; a fresh session mints its own id, reported from get_state' },
77
+ resume: { honored: true, via: '--resume in place of a fresh session' },
78
+ resumeFallbackSystemPrompt: { honored: false, reason: 'Continue already resends spec.systemPrompt on every process; a separate fallback prompt is unused' },
79
+ };
80
+ /**
81
+ * Maps cezar's tool names onto OMP's `--tools` allowlist (spec § Tools). Fails closed: a
82
+ * non-empty request that maps to nothing disables every built-in rather than widening to OMP's
83
+ * defaults, and every narrowing (`bashAllowlist`, D1) only ever removes names.
84
+ */
85
+ export function ompTools(allowedTools, opts) {
86
+ // OMP's default set includes task, wait, eval and unrestricted bash: D1 and a bash allowlist
87
+ // each have to name a list to leave something out.
88
+ const narrows = opts.restrictNativeDelegation || (opts.bashAllowlist?.length ?? 0) > 0;
89
+ const requested = allowedTools ?? (narrows ? [...OMP_DEFAULT_TOOL_NAMES] : undefined);
90
+ if (requested === undefined)
91
+ return { flag: null, tools: [], dropped: [] };
92
+ const tools = new Set();
93
+ const dropped = new Set();
94
+ for (const name of requested) {
95
+ // `hasOwn`: a plain-object lookup would map `constructor` onto Object's own function.
96
+ const mapped = Object.hasOwn(OMP_TOOL_MAP, name)
97
+ ? OMP_TOOL_MAP[name]
98
+ : name.startsWith(MCP_PREFIX) ? ompMcpToolName(name)
99
+ : OMP_BUILTIN_TOOL_NAMES.includes(name) ? name : undefined;
100
+ if (mapped === undefined)
101
+ dropped.add(name);
102
+ else
103
+ tools.add(mapped);
104
+ }
105
+ // OMP can allow or deny the whole bash tool but has no command-prefix equivalent.
106
+ if (opts.bashAllowlist && opts.bashAllowlist.length > 0)
107
+ tools.delete('bash');
108
+ if (opts.restrictNativeDelegation)
109
+ for (const name of OMP_DELEGATION_TOOLS)
110
+ tools.delete(name);
111
+ if (tools.size === 0)
112
+ return { flag: 'no-tools', tools: [], dropped: [...dropped] };
113
+ // Narrow before the CI tool joins: a refusal never leaves a CI-only list where --no-tools belongs.
114
+ for (const name of opts.exclude ?? [])
115
+ tools.delete(name);
116
+ if (tools.size === 0)
117
+ return { flag: 'no-tools', tools: [], dropped: [...dropped] };
118
+ // Exactly the names omp-ci-wait.mjs registers (Ruling 11): OMP exits 2 on an unknown --tools name.
119
+ if (opts.cezarTools)
120
+ for (const name of cezarToolNames(opts.env ?? {}))
121
+ tools.add(name);
122
+ return { flag: 'tools', tools: [...tools], dropped: [...dropped] };
123
+ }
124
+ const MCP_PREFIX = 'mcp__';
125
+ /**
126
+ * OMP's name for an MCP tool granted in Claude's `mcp__<server>__<tool>` spelling (Ruling 20).
127
+ * v18.4.11 registers `mcp__<server>_<tool>` (`qjn`): each part lowercased, runs of anything but
128
+ * `[a-z0-9_]` and repeated underscores folded to one `_`, edges trimmed, and a tool name that
129
+ * repeats its server's prefix stripped of it. `--tools` validation matches exact names, so the
130
+ * Claude spelling would exit 2. A name without a second `__` is already OMP's spelling (OMP
131
+ * folds every `__` in it) and passes verbatim. OMP shortens names past 64 characters with a
132
+ * Bun hash cezar cannot reproduce; such a name stays unknown and the startup refusal drops it.
133
+ */
134
+ function ompMcpToolName(name) {
135
+ const rest = name.slice(MCP_PREFIX.length);
136
+ const split = rest.indexOf('__');
137
+ if (split <= 0 || split + 2 >= rest.length)
138
+ return name;
139
+ const server = ompMcpNamePart(rest.slice(0, split), 'server');
140
+ const tool = ompMcpNamePart(rest.slice(split + 2), 'tool');
141
+ return `${MCP_PREFIX}${server}_${tool.startsWith(`${server}_`) ? tool.slice(server.length + 1) : tool}`;
142
+ }
143
+ /** v18.4.11 `x7s` with digits kept, the variant OMP registers under. */
144
+ function ompMcpNamePart(value, fallback) {
145
+ const part = value.toLowerCase().replace(/[^a-z0-9_]+/g, '_').replace(/_+/g, '_').replace(/^_+|_+$/g, '');
146
+ return part.length > 0 ? part : fallback;
147
+ }
148
+ /** `omp --mode rpc` argv, in the order of the spec's spawn block. */
149
+ export function buildOmpArgs(spec, excludeTools,
150
+ /** Ruling 22: an omp build that rejects `--add-dir` runs without the additional directories. */
151
+ opts = {}) {
152
+ const args = ['--mode', 'rpc'];
153
+ if (spec.cezarTools)
154
+ args.push('--extension', ompScriptPath('omp-ci-wait.mjs'));
155
+ // OMP has no --session-id: a fresh session mints its id, which get_state reports.
156
+ if (spec.resume && spec.sessionId)
157
+ args.push('--resume', spec.sessionId);
158
+ if (spec.systemPrompt)
159
+ args.push('--append-system-prompt', spec.systemPrompt);
160
+ if (spec.model)
161
+ args.push('--model', spec.model);
162
+ const effort = parseEffort(spec.effort);
163
+ if (effort)
164
+ args.push('--thinking', effort);
165
+ if (!opts.omitAddDir)
166
+ for (const dir of spec.additionalDirectories ?? [])
167
+ args.push('--add-dir', dir);
168
+ if (spec.restrictNativeDelegation)
169
+ args.push('--config', ompScriptPath('omp-restrict-delegation.yml'));
170
+ const selection = ompTools(spec.allowedTools, {
171
+ bashAllowlist: spec.bashAllowlist,
172
+ restrictNativeDelegation: spec.restrictNativeDelegation,
173
+ cezarTools: spec.cezarTools !== undefined,
174
+ env: spec.env,
175
+ exclude: excludeTools,
176
+ });
177
+ if (selection.flag === 'tools')
178
+ args.push('--tools', selection.tools.join(','));
179
+ else if (selection.flag === 'no-tools')
180
+ args.push('--no-tools');
181
+ return args;
182
+ }
183
+ function ompScriptPath(name) {
184
+ return fileURLToPath(new URL(`../../scripts/${name}`, import.meta.url));
185
+ }
186
+ /**
187
+ * Persistent subprocess adapter for OMP's RPC mode (#595), one `omp --mode rpc` child per
188
+ * session. Structure follows `pi-runner.ts` step for step (Pi stays untouched); what OMP changes
189
+ * is the turn boundary — `session_settled`, or the turn-opening prompt OMP completed without the
190
+ * agent or rejected before admission (`ompTurnBoundary`, gated by prompt id in the mapper), never
191
+ * `agent_end` — plus the startup commands and the error paths. v1 `turn-end` follows the mapper
192
+ * closing its turn, so the two streams cannot disagree on a boundary.
193
+ *
194
+ * Contract: oh-my-pi v18.4.11 `docs/rpc.md`, `packages/coding-agent/src/modes/rpc/rpc-mode.ts`.
195
+ * Design record and the implementation-time rulings: `.ai/specs/2026-10-02-omp-runner.md`.
196
+ */
197
+ export class OmpRunner {
198
+ backend = 'omp';
199
+ specSupport = OMP_SPEC_SUPPORT;
200
+ systemPromptOnResume = 'resent';
201
+ inputDelivery = {
202
+ mode: 'steer', consumption: 'observable',
203
+ via: 'prompt with streamingBehavior steer; set_steering_mode all at session start; user message_start with the submitted text',
204
+ };
205
+ bin;
206
+ timeoutMs;
207
+ killGraceMs;
208
+ lastSession = null;
209
+ constructor(opts = {}) {
210
+ this.bin = opts.bin ?? process.env.CEZ_OMP_BIN ?? (process.env.CEZ_DRY_RUN === '1' ? mockOmpPath() : 'omp');
211
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_RUN_TIMEOUT_MS;
212
+ this.killGraceMs = opts.killGraceMs ?? KILL_GRACE_MS;
213
+ }
214
+ run(spec, onEvent) {
215
+ return this.startSession(spec, onEvent, { autoEndAfterFirstTurn: true }).result;
216
+ }
217
+ async interrupt() {
218
+ this.lastSession?.interrupt();
219
+ }
220
+ startSession(spec, onEvent, opts = {}) {
221
+ const killGraceMs = this.killGraceMs;
222
+ let spawnError = null;
223
+ const stderr = [];
224
+ const spawnOmp = (args) => {
225
+ const spawned = spawnSessionLeader(this.bin, args, {
226
+ cwd: spec.cwd,
227
+ env: buildChildEnv({ backend: this.backend, extraEnv: spec.env }),
228
+ });
229
+ boundOutputDrainAfterExit(spawned);
230
+ spawned.on('error', (error) => {
231
+ spawnError = wrapSpawnError(error, this.bin);
232
+ });
233
+ spawned.stderr.setEncoding('utf8');
234
+ spawned.stderr.on('data', (chunk) => stderr.push(chunk));
235
+ return spawned;
236
+ };
237
+ // Reassigned at most once per refusal class: Ruling 13/20 narrow `--tools`, Ruling 22 drops
238
+ // `--add-dir` for an omp build that does not accept it.
239
+ const firstArgs = buildOmpArgs(spec);
240
+ let spawnArgs = firstArgs;
241
+ let child = spawnOmp(firstArgs);
242
+ let toolsRetried = false;
243
+ let flagsRetried = false;
244
+ let refusedTools = [];
245
+ let omitAddDir = false;
246
+ let sawFrame = false;
247
+ let closedByCaller = false;
248
+ // A refused spawn must leave nothing in v2, so its events wait for the first stdout frame.
249
+ // Held only when something can be dropped and retried: a `--tools` list or `--add-dir`.
250
+ let uiHold = firstArgs.includes('--tools') || firstArgs.includes('--add-dir') ? [] : null;
251
+ const emitUiEvent = (event) => {
252
+ if (uiHold)
253
+ uiHold.push(event);
254
+ else
255
+ opts.onUiEvent?.(event);
256
+ };
257
+ const releaseUiHold = () => {
258
+ const held = uiHold;
259
+ uiHold = null;
260
+ for (const event of held ?? [])
261
+ opts.onUiEvent?.(event);
262
+ };
263
+ let open = true;
264
+ let timedOut = false;
265
+ let terminatedByCezar = false;
266
+ let autoEndTimer;
267
+ let killTimer;
268
+ let interruptKillTimer;
269
+ let ompUi = createOmpUiState();
270
+ const textChunks = [];
271
+ /** One v1 `text` per complete block, never per token (pi-runner.ts, #902 / #2). */
272
+ const textCoalescer = new V1TextCoalescer((text) => {
273
+ textChunks.push(text);
274
+ onEvent?.({ type: 'text', text });
275
+ });
276
+ /** `contentIndex` restarts at 0 on every assistant message (as Pi's); see pi-runner.ts. */
277
+ let textBlockSeq = 0;
278
+ const openTextBlockKeys = new Map();
279
+ const textBlockKey = (contentIndex) => {
280
+ if (typeof contentIndex !== 'number')
281
+ return undefined;
282
+ let key = openTextBlockKeys.get(contentIndex);
283
+ if (!key) {
284
+ key = `omp-text-${++textBlockSeq}`;
285
+ openTextBlockKeys.set(contentIndex, key);
286
+ }
287
+ return key;
288
+ };
289
+ const flushText = () => {
290
+ textCoalescer.flush();
291
+ openTextBlockKeys.clear();
292
+ };
293
+ const toolCalls = [];
294
+ let sessionId = spec.sessionId;
295
+ let startupStateChecked = false;
296
+ let tokensUsed = 0;
297
+ let latchedProviderError;
298
+ const emitLatchedProviderError = () => {
299
+ if (!latchedProviderError)
300
+ return;
301
+ onEvent?.({ type: 'error', message: latchedProviderError });
302
+ latchedProviderError = undefined;
303
+ };
304
+ const emitUi = (value) => {
305
+ const mapped = mapOmpRpcMessage(value, ompUi);
306
+ ompUi = mapped.state;
307
+ for (const event of mapped.events)
308
+ emitUiEvent(event);
309
+ };
310
+ /** Releases the mapper's provider-error latch when the stream ends without a settle. */
311
+ const emitLatchedUiProviderError = () => {
312
+ const mapped = ompFlushProviderError(ompUi);
313
+ ompUi = mapped.state;
314
+ for (const event of mapped.events)
315
+ emitUiEvent(event);
316
+ };
317
+ const writeNow = (command) => {
318
+ if (!child.stdin.writable)
319
+ return false;
320
+ try {
321
+ child.stdin.write(`${JSON.stringify(command)}\n`);
322
+ return true;
323
+ }
324
+ catch {
325
+ return false;
326
+ }
327
+ };
328
+ // Until the current child speaks (OMP's `ready` frame) nothing is written to it: a spawn the
329
+ // Ruling 13 refusal rejects exits without reading stdin, and what it was handed would be lost with
330
+ // it. Accepted commands wait here, in order, and the respawned child gets the same ones.
331
+ const outbox = [];
332
+ const flushOutbox = () => {
333
+ for (const command of outbox.splice(0))
334
+ writeNow(command);
335
+ };
336
+ const write = (command) => {
337
+ if (!open)
338
+ return false;
339
+ if (!sawFrame) {
340
+ outbox.push(command);
341
+ return true;
342
+ }
343
+ return writeNow(command);
344
+ };
345
+ let agentInputReady = false;
346
+ let promptSerial = 0;
347
+ let humanSerial = 0;
348
+ // Every prompt carries an id: a local completion or failure ends the turn only when it
349
+ // answers the prompt that opened it, never a steer (the mapper gates on this id).
350
+ const humanAcks = new Set();
351
+ let agentAck;
352
+ const rejectAgentAck = () => {
353
+ const pending = agentAck;
354
+ agentAck = undefined;
355
+ pending?.reject(new Error('omp closed before prompt acknowledgement'));
356
+ };
357
+ const scheduleAutoEnd = () => {
358
+ if (!opts.autoEndAfterFirstTurn || !open || autoEndTimer || agentAck || humanAcks.size)
359
+ return;
360
+ autoEndTimer = setTimeout(() => {
361
+ autoEndTimer = undefined;
362
+ if (opts.shouldAutoEnd?.() !== false)
363
+ end();
364
+ }, AUTO_END_DELAY_MS);
365
+ autoEndTimer.unref?.();
366
+ };
367
+ // Same readiness rules as pi-runner.ts (#505): a refusal caused only by an outstanding
368
+ // acknowledgement gets one readiness hint when it lands, even mid-turn.
369
+ let refusedForAck = false;
370
+ const readyAfterAck = () => {
371
+ if (open && agentInputReady && !ompUi.turnId && !agentAck && !humanAcks.size) {
372
+ refusedForAck = false;
373
+ opts.onAgentInputReady?.();
374
+ scheduleAutoEnd();
375
+ }
376
+ else if (refusedForAck && open && !agentAck && !humanAcks.size && !pendingMarkerAsk) {
377
+ refusedForAck = false;
378
+ opts.onAgentInputReady?.();
379
+ }
380
+ else if (open && !ompUi.turnId && !agentAck && !humanAcks.size && !pendingMarkerAsk) {
381
+ // #486: a human ack drained with no turn open — re-settle the idle boundary.
382
+ refusedForAck = false;
383
+ opts.onAgentInputReady?.();
384
+ }
385
+ };
386
+ let pendingMarkerAsk = false;
387
+ let turnTextStart = 0;
388
+ // Accepted agent prompts not yet seen as a user message_start (#505).
389
+ const submissions = new InputSubmissions();
390
+ // Submissions already queued when a turn began: that turn reads them. OMP reports an
391
+ // admitted steer at the next terminal agent_end (rpc-prompt-results.ts), never drops it.
392
+ let carried = new Set();
393
+ const acked = new Set();
394
+ let turnFailed = false;
395
+ const sendMessage = (content, requestId, inputIds = []) => {
396
+ if (!open)
397
+ return false;
398
+ // A steer joins the running turn; only an idle prompt opens one (#505).
399
+ const opensTurn = !ompUi.turnId;
400
+ agentInputReady = false;
401
+ if (opensTurn) {
402
+ pendingMarkerAsk = false;
403
+ turnTextStart = textChunks.length;
404
+ }
405
+ const { message, images } = toOmpPrompt(content);
406
+ const id = requestId ?? `cezar-prompt-${++humanSerial}`;
407
+ if (autoEndTimer) {
408
+ clearTimeout(autoEndTimer);
409
+ autoEndTimer = undefined;
410
+ }
411
+ if (!write({
412
+ type: 'prompt',
413
+ id,
414
+ message,
415
+ ...(images.length > 0 ? { images } : {}),
416
+ ...(ompUi.turnId ? { streamingBehavior: 'steer' } : {}),
417
+ })) {
418
+ return false;
419
+ }
420
+ if (!requestId)
421
+ humanAcks.add(id);
422
+ else
423
+ submissions.accept(requestId, inputIds, message);
424
+ if (!ompUi.turnId) {
425
+ const mapped = ompTurnStarted(ompUi, id);
426
+ ompUi = mapped.state;
427
+ for (const event of mapped.events)
428
+ emitUiEvent(event);
429
+ }
430
+ return true;
431
+ };
432
+ const end = () => {
433
+ if (!open)
434
+ return;
435
+ open = false;
436
+ closedByCaller = true;
437
+ rejectAgentAck();
438
+ flushOutbox();
439
+ child.stdin.end();
440
+ killTimer = setTimeout(() => {
441
+ if (child.exitCode !== null || child.signalCode !== null)
442
+ return;
443
+ terminatedByCezar = true;
444
+ signalSession(child, 'SIGTERM');
445
+ // An OMP that ignores SIGTERM would otherwise hold `result` open forever: the deadline's
446
+ // interrupt() is a no-op once `open` is false.
447
+ killTimer = setTimeout(() => {
448
+ if (child.exitCode === null && child.signalCode === null)
449
+ signalSession(child, 'SIGKILL');
450
+ }, killGraceMs);
451
+ killTimer.unref?.();
452
+ }, killGraceMs);
453
+ killTimer.unref?.();
454
+ };
455
+ const interrupt = () => {
456
+ if (!open)
457
+ return;
458
+ if (sawFrame)
459
+ writeNow({ type: 'abort' });
460
+ open = false;
461
+ closedByCaller = true;
462
+ rejectAgentAck();
463
+ terminatedByCezar = true;
464
+ signalSession(child, 'SIGTERM');
465
+ interruptKillTimer = setTimeout(() => {
466
+ if (child.exitCode === null && child.signalCode === null)
467
+ signalSession(child, 'SIGKILL');
468
+ }, killGraceMs);
469
+ interruptKillTimer.unref?.();
470
+ };
471
+ const dropped = ompTools(spec.allowedTools, {
472
+ bashAllowlist: spec.bashAllowlist,
473
+ restrictNativeDelegation: spec.restrictNativeDelegation,
474
+ }).dropped;
475
+ if (dropped.length > 0) {
476
+ onEvent?.({ type: 'note', message: `omp: dropped tools with no OMP equivalent: ${dropped.join(', ')}` });
477
+ }
478
+ const startup = () => {
479
+ // Spec § Session lifecycle: OMP queues these until it is ready.
480
+ write({ id: 'cezar-state', type: 'get_state' });
481
+ // #551 parity: OMP defaults steeringMode to one-at-a-time, and sendAgentMessage requires
482
+ // every accepted steer at the next model call.
483
+ write({ id: 'cezar-steering', type: 'set_steering_mode', mode: 'all' });
484
+ write({ id: 'cezar-subagents', type: 'set_subagent_subscription', level: 'events' });
485
+ // Drops the per-delta `partial` snapshot; the coalescer and the mapper read deltas only.
486
+ write({ id: 'cezar-event-filter', type: 'set_event_filter', events: null, messageUpdates: 'delta' });
487
+ sendMessage([...(spec.images ?? []), { type: 'text', text: spec.userPrompt }]);
488
+ };
489
+ startup();
490
+ const limitMs = spec.timeoutMs ?? this.timeoutMs;
491
+ // One wall-clock limit for the whole run: a Ruling 13 respawn re-arms with what is left.
492
+ const deadlineAt = Date.now() + limitMs;
493
+ const armDeadline = () => {
494
+ const timer = limitMs > 0
495
+ ? setTimeout(() => {
496
+ timedOut = true;
497
+ interrupt();
498
+ }, Math.max(0, deadlineAt - Date.now()))
499
+ : undefined;
500
+ timer?.unref?.();
501
+ return timer;
502
+ };
503
+ let deadline = armDeadline();
504
+ // A refusal is only retried before any frame, once per class, and never after the caller or a
505
+ // deadline ended the session; a spawn with nothing to drop has `uiHold` null.
506
+ const retryPossible = () => !sawFrame && !(toolsRetried && flagsRetried) && !timedOut && !terminatedByCezar && !closedByCaller && uiHold !== null;
507
+ const result = (async function runOmp() {
508
+ try {
509
+ for await (const line of readNdjson(child.stdout)) {
510
+ if (!sawFrame) {
511
+ sawFrame = true;
512
+ releaseUiHold();
513
+ flushOutbox();
514
+ }
515
+ let value;
516
+ try {
517
+ value = JSON.parse(line);
518
+ }
519
+ catch {
520
+ onEvent?.({ type: 'note', message: `omp: skipped unparseable RPC line: ${truncate(line)}` });
521
+ continue;
522
+ }
523
+ opts.onActivity?.();
524
+ // Flush before message/tool/turn boundaries so v2 events never overtake a pending
525
+ // block; never on prompt acks, which can land between text_deltas (pi-runner.ts).
526
+ if (isRecord(value) &&
527
+ (value.type === 'tool_execution_start' ||
528
+ value.type === 'message_end' ||
529
+ value.type === 'session_settled' ||
530
+ value.type === 'prompt_result' ||
531
+ value.type === 'turn_end' ||
532
+ value.type === 'agent_end')) {
533
+ flushText();
534
+ }
535
+ // A boundary only ends a turn that is open: OMP never settles an idle session, and a
536
+ // second boundary for the same prompt must not end the next one.
537
+ const turnOpen = ompUi.turnId !== null;
538
+ emitUi(value);
539
+ const usageReport = ompMessageUsage(value);
540
+ if (usageReport) {
541
+ const { input, output, cacheRead = 0, cacheWrite = 0 } = usageReport.usage;
542
+ // v1 keeps its historical per-message weighted token convention; v2 exposes
543
+ // the raw components of these same parent and child reports.
544
+ tokensUsed += Math.round(input + output + cacheRead * 0.1 + cacheWrite * 1.25);
545
+ onEvent?.({ type: 'token-usage', tokensUsed });
546
+ if (usageReport.costUsd !== undefined)
547
+ onEvent?.({ type: 'cost', usd: usageReport.costUsd });
548
+ }
549
+ // The mapper closed its turn on this frame: v1 ends the turn with it.
550
+ const endsTurn = turnOpen && ompUi.turnId === null;
551
+ if (!isRecord(value))
552
+ continue;
553
+ if (value.type === 'response' && value.command === 'get_state' && value.success === true && isRecord(value.data)) {
554
+ const discovered = string(value.data.sessionId);
555
+ if (discovered && discovered !== sessionId) {
556
+ sessionId = discovered;
557
+ onEvent?.({ type: 'session', sessionId: discovered });
558
+ }
559
+ // OMP v18.6.1 still has no fresh persistent-session flag. Its autoResume
560
+ // setting can restore history without --resume (hearsay-tools/cezarion#832).
561
+ // Only the startup query is evidence: later reads include our own messages.
562
+ if (value.id === 'cezar-state' && !startupStateChecked) {
563
+ startupStateChecked = true;
564
+ const count = value.data.messageCount;
565
+ if (!spec.resume && typeof count === 'number' && count > 0) {
566
+ const message = `omp: unexpectedly restored a session with ${count} messages for a fresh step. Previous conversation context is active; check OMP's autoResume setting.`;
567
+ onEvent?.({ type: 'note', message });
568
+ emitUiEvent({ type: 'session.error', message, fatal: false });
569
+ }
570
+ }
571
+ }
572
+ else if (value.type === 'response' && string(value.command)?.startsWith('set_')) {
573
+ if (value.success !== true) {
574
+ onEvent?.({ type: 'note', message: `omp: ${string(value.command)} failed: ${rpcError(value)}` });
575
+ }
576
+ }
577
+ else if (value.type === 'response' && value.command === 'prompt') {
578
+ if (value.success === false) {
579
+ // The turn-opening prompt rejected before admission gets no `prompt_result`, so it
580
+ // is the turn's error here (Ruling 10). A failure after admission is reported by the
581
+ // `prompt_result` that follows it (rpc-prompt-results.ts `fail`), and a steer's
582
+ // failure leaves the turn running: both are only a note.
583
+ const message = `omp: prompt failed: ${rpcError(value)}`;
584
+ if (endsTurn) {
585
+ onEvent?.({ type: 'error', message });
586
+ turnFailed = true;
587
+ }
588
+ else {
589
+ onEvent?.({ type: 'note', message });
590
+ }
591
+ }
592
+ const pending = agentAck;
593
+ const id = string(value.id);
594
+ if (pending && id === pending.id) {
595
+ agentAck = undefined;
596
+ if (value.success === true) {
597
+ acked.add(pending.id);
598
+ pending.resolve();
599
+ }
600
+ else
601
+ pending.reject(new Error(rpcError(value)));
602
+ }
603
+ else if (id) {
604
+ humanAcks.delete(id);
605
+ }
606
+ readyAfterAck();
607
+ }
608
+ else if (value.type === 'response' && value.success === false) {
609
+ onEvent?.({ type: 'error', message: rpcError(value) });
610
+ }
611
+ else if (value.type === 'prompt_result' && value.agentInvoked === false && value.status === 'error') {
612
+ // The prompt failed before the agent ran: no settle follows, so the turn ends here
613
+ // when it opened the turn. A steer failing mid-turn leaves it running.
614
+ const error = isRecord(value.error) ? value.error : {};
615
+ const message = ompProviderErrorMessage({ provider: error.provider, model: error.model, errorMessage: error.message });
616
+ if (endsTurn) {
617
+ onEvent?.({ type: 'error', message });
618
+ turnFailed = true;
619
+ }
620
+ else {
621
+ // v2 reports it as a session error whether or not a turn is open.
622
+ onEvent?.({ type: 'note', message });
623
+ }
624
+ }
625
+ else if (value.type === 'message_update' && isRecord(value.assistantMessageEvent)) {
626
+ const update = value.assistantMessageEvent;
627
+ const contentKey = textBlockKey(update.contentIndex);
628
+ if (update.type === 'text_delta' && typeof update.delta === 'string') {
629
+ textCoalescer.append(contentKey, update.delta);
630
+ }
631
+ else if (update.type === 'text_end') {
632
+ const snapshot = typeof update.content === 'string' ? update.content : undefined;
633
+ textCoalescer.complete(contentKey, snapshot);
634
+ if (typeof update.contentIndex === 'number')
635
+ openTextBlockKeys.delete(update.contentIndex);
636
+ }
637
+ }
638
+ else if (value.type === 'message_end' && isRecord(value.message) && value.message.role === 'assistant') {
639
+ flushText();
640
+ // Latched until the turn ends, cleared by a later success: OMP retries past
641
+ // provider flakes and a recovered retry must stay silent (#256, #316).
642
+ if (string(value.message.stopReason) === 'error') {
643
+ turnFailed = true;
644
+ latchedProviderError = ompProviderErrorMessage(value.message);
645
+ }
646
+ else {
647
+ latchedProviderError = undefined;
648
+ }
649
+ }
650
+ else if (value.type === 'agent_start') {
651
+ // Only a prompt OMP acknowledged before this agent_start can be in its turn (#505).
652
+ carried = new Set(submissions.pendingIds().filter(id => acked.has(id)));
653
+ turnFailed = false;
654
+ }
655
+ else if (value.type === 'message_start' && isRecord(value.message) && value.message.role === 'user') {
656
+ // The model received this prompt now (#505).
657
+ const ids = submissions.consumeOldestByText(ompMessageText(value.message));
658
+ if (ids.length)
659
+ opts.onAgentInputConsumed?.(ids);
660
+ }
661
+ else if (value.type === 'tool_execution_start') {
662
+ flushText();
663
+ const id = string(value.toolCallId);
664
+ const name = string(value.toolName);
665
+ if (id && name) {
666
+ toolCalls.push({ id, name, input: value.args });
667
+ onEvent?.({ type: 'tool-call', id, tool: name, input: value.args });
668
+ }
669
+ }
670
+ else if (value.type === 'tool_execution_end') {
671
+ const id = string(value.toolCallId);
672
+ if (id) {
673
+ onEvent?.({
674
+ type: 'tool-result',
675
+ toolCallId: id,
676
+ result: contentText(isRecord(value.result) ? value.result.content : undefined) ?? '',
677
+ isError: value.isError === true,
678
+ });
679
+ emitImages(isRecord(value.result) ? value.result.content : undefined, onEvent);
680
+ }
681
+ }
682
+ else if (value.type === 'extension_error') {
683
+ onEvent?.({ type: 'note', message: string(value.error) ?? string(value.message) ?? 'omp extension error' });
684
+ }
685
+ else if (value.type === 'notice') {
686
+ const message = string(value.message);
687
+ if (message)
688
+ onEvent?.({ type: 'note', message: `omp: ${message}` });
689
+ }
690
+ if (endsTurn) {
691
+ flushText();
692
+ emitLatchedProviderError();
693
+ pendingMarkerAsk = parseAskMarker(textChunks.slice(turnTextStart).join('\n')) !== null;
694
+ agentInputReady = true;
695
+ // Input queued before this turn began was processed by it, even without a user
696
+ // message_start (#505) — unless the turn failed and may never have reached the model.
697
+ const read = turnFailed ? [] : [...carried].flatMap(id => submissions.consume(id));
698
+ turnFailed = false;
699
+ carried = new Set();
700
+ if (read.length)
701
+ opts.onAgentInputConsumed?.(read);
702
+ onEvent?.({ type: 'turn-end' });
703
+ scheduleAutoEnd();
704
+ }
705
+ }
706
+ }
707
+ catch (error) {
708
+ if (child.exitCode === null && child.signalCode === null) {
709
+ // A live child whose stream failed is not a refusal: no respawn follows, so nothing
710
+ // may still be accepted into the outbox.
711
+ open = false;
712
+ // The run is reported failed, so the CLI must not keep running tools in the worktree:
713
+ // SIGTERM now, SIGKILL after the grace period (a timer `finally` does not clear).
714
+ terminatedByCezar = true;
715
+ signalSession(child, 'SIGTERM');
716
+ const reap = setTimeout(() => {
717
+ if (child.exitCode === null && child.signalCode === null)
718
+ signalSession(child, 'SIGKILL');
719
+ }, killGraceMs);
720
+ reap.unref?.();
721
+ throw error;
722
+ }
723
+ }
724
+ finally {
725
+ if (deadline)
726
+ clearTimeout(deadline);
727
+ if (autoEndTimer)
728
+ clearTimeout(autoEndTimer);
729
+ if (killTimer)
730
+ clearTimeout(killTimer);
731
+ // A refused spawn may still be respawned: stay open so input accepted meanwhile is kept
732
+ // for the new child instead of being refused in the gap.
733
+ if (!retryPossible())
734
+ open = false;
735
+ rejectAgentAck();
736
+ }
737
+ flushText();
738
+ emitLatchedProviderError();
739
+ emitLatchedUiProviderError();
740
+ const exitCode = await waitForExit(child);
741
+ if (interruptKillTimer)
742
+ clearTimeout(interruptKillTimer);
743
+ // Ruling 13: exit 2 before any frame, naming tools the user's OMP settings disabled. Respawn
744
+ // once without them: the list only narrows, so this never widens what the step granted.
745
+ // Ruling 20 widens the same respawn to MCP names OMP did not register (a Claude spelling
746
+ // it cannot match, or a server slower than OMP's discovery window).
747
+ const canRetry = exitCode === 2 && !spawnError && retryPossible();
748
+ // Ruling 22: OMP rejects unknown flags while parsing, before it validates --tools, so this
749
+ // refusal comes first and a tool refusal may still follow on the respawn.
750
+ const droppedDirs = canRetry && !flagsRetried ? refusedOmpAddDir(stderr.join(''), spawnArgs) : null;
751
+ const refusal = canRetry && !droppedDirs && !toolsRetried
752
+ ? refusedOmpTools(stderr.join(''), ompToolList(spawnArgs))
753
+ : null;
754
+ if (droppedDirs || refusal) {
755
+ stderr.length = 0;
756
+ if (droppedDirs) {
757
+ flagsRetried = true;
758
+ omitAddDir = true;
759
+ onEvent?.({
760
+ type: 'note',
761
+ message: `omp: this omp CLI does not accept --add-dir (update OMP); ran without the additional directories: ${droppedDirs.join(', ')}`,
762
+ });
763
+ }
764
+ if (refusal) {
765
+ toolsRetried = true;
766
+ refusedTools = [...refusal.disabled, ...refusal.mcp];
767
+ const dropped = [
768
+ ...(refusal.disabled.length > 0 ? [`tools disabled by your OMP settings were dropped: ${refusal.disabled.join(', ')}`] : []),
769
+ ...(refusal.mcp.length > 0 ? [`MCP tools OMP has not registered were dropped: ${refusal.mcp.join(', ')}`] : []),
770
+ ];
771
+ onEvent?.({ type: 'note', message: `omp: ${dropped.join('; ')}` });
772
+ }
773
+ // Session state (mapper, held v2 events, ack sets) follows what was accepted, not the
774
+ // child, and the outbox still holds every command accepted so far: the new child is
775
+ // sent exactly those, once, when it speaks.
776
+ spawnArgs = buildOmpArgs(spec, refusedTools, { omitAddDir });
777
+ child = spawnOmp(spawnArgs);
778
+ if (child.pid !== undefined)
779
+ opts.onPidChange?.(child.pid);
780
+ deadline = armDeadline();
781
+ return runOmp();
782
+ }
783
+ open = false;
784
+ releaseUiHold();
785
+ if (spawnError)
786
+ throw spawnError;
787
+ if (timedOut) {
788
+ const message = `omp CLI timed out after ${Math.round((limitMs / 60_000) * 10) / 10}m and was killed`;
789
+ onEvent?.({ type: 'error', message });
790
+ onEvent?.({ type: 'done' });
791
+ return { text: textChunks.join('\n').trim(), toolCalls, tokensUsed, sessionId };
792
+ }
793
+ // A signal we sent is teardown, not a second agent failure (#73).
794
+ if (terminatedByCezar && (isSignalTerminationExit(exitCode) || (exitCode === null && child.signalCode !== null))) {
795
+ onEvent?.({
796
+ type: 'note',
797
+ message: `omp CLI did not exit on its own after close; terminated by cezar (${exitCode === null ? child.signalCode : `code ${exitCode}`})`,
798
+ });
799
+ }
800
+ else if (exitCode === null && child.signalCode !== null && !terminatedByCezar) {
801
+ // Killed from outside (OOM killer, a stray kill): a truncated run, never a success.
802
+ const message = `omp CLI was killed by signal ${child.signalCode}`;
803
+ onEvent?.({ type: 'error', message });
804
+ throw new Error(message);
805
+ }
806
+ else if (exitCode !== 0 && exitCode !== null) {
807
+ const diagnostic = stderr.join('');
808
+ if (diagnostic.trim())
809
+ onEvent?.({ type: 'note', message: `omp CLI stderr:\n${diagnostic}` });
810
+ const detail = ompStderrDetail(diagnostic);
811
+ const message = `omp CLI exited with code ${exitCode}${detail ? ` — ${detail}` : ''}`;
812
+ onEvent?.({ type: 'error', message });
813
+ throw new Error(message);
814
+ }
815
+ if (ompUi.turnId)
816
+ onEvent?.({ type: 'note', message: 'omp RPC session ended before session_settled' });
817
+ if (tokensUsed === 0)
818
+ onEvent?.({ type: 'note', message: 'token usage not reported by omp CLI' });
819
+ emitUiEvent({ type: 'session.ended', reason: ompUi.stopReason });
820
+ onEvent?.({ type: 'done' });
821
+ return { text: textChunks.join('\n').trim(), toolCalls, tokensUsed, sessionId };
822
+ })();
823
+ const session = {
824
+ result,
825
+ sendMessage,
826
+ sendAgentMessage: (content, inputIds = []) => {
827
+ // #505: a running turn is steered; only a pending CEZ:ASK or an unacknowledged
828
+ // prompt refuses.
829
+ if (open && !pendingMarkerAsk && (agentAck || humanAcks.size)) {
830
+ refusedForAck = true;
831
+ return false;
832
+ }
833
+ if (!open || pendingMarkerAsk)
834
+ return false;
835
+ const id = `cezar-agent-${++promptSerial}`;
836
+ let resolve, reject;
837
+ const acknowledged = new Promise((yes, no) => { resolve = yes; reject = no; });
838
+ agentAck = { id, resolve, reject };
839
+ if (!sendMessage(content, id, inputIds)) {
840
+ agentAck = undefined;
841
+ return false;
842
+ }
843
+ return acknowledged;
844
+ },
845
+ discardQueuedMessages: () => undefined,
846
+ holdsHumanInput: () => humanAcks.size > 0,
847
+ heldHumanInputCount: () => humanAcks.size,
848
+ end,
849
+ interrupt,
850
+ // The live child: a Ruling 13 respawn replaces it (`onPidChange` tells the caller who read it).
851
+ get pid() {
852
+ return child.pid;
853
+ },
854
+ get open() {
855
+ return open;
856
+ },
857
+ };
858
+ this.lastSession = session;
859
+ return session;
860
+ }
861
+ }
862
+ /**
863
+ * The stderr line an exit error carries. OMP's "no usable model" exit prints its reason first
864
+ * and setup hints after it, which the generic last-lines summary would keep instead.
865
+ */
866
+ function ompStderrDetail(stderr) {
867
+ const first = stderr.split(/\r?\n/).find(line => line.trim())?.trim();
868
+ if (first?.startsWith('No models available'))
869
+ return first;
870
+ return summarizeRunnerStderr(stderr);
871
+ }
872
+ /** The `--tools` list in an omp argv; empty when none was passed. */
873
+ function ompToolList(args) {
874
+ const at = args.indexOf('--tools');
875
+ return at >= 0 ? (args[at + 1] ?? '').split(',').filter(Boolean) : [];
876
+ }
877
+ /**
878
+ * What OMP's startup refusal (v18.4.11 `emt()`, exit 2) lets a respawn drop, limited to names this
879
+ * spawn passed so a retry only narrows. One error carries up to two lists:
880
+ * `Built-in tool(s) unavailable in this session: a, b.` (tools the user's settings disable, Ruling 13)
881
+ * and `Unknown tool(s) in --tools: a, b.` (Ruling 20: droppable only when every name is an
882
+ * `mcp__` name this spawn passed; any other unknown name stays fatal, so nothing is retried).
883
+ * `null` when there is nothing to drop.
884
+ */
885
+ function refusedOmpTools(stderr, passed) {
886
+ const listed = (pattern) => {
887
+ const match = pattern.exec(stderr);
888
+ return match ? (match[1] ?? '').replace(/\.\s*$/, '').split(/[\s,]+/).filter(Boolean) : undefined;
889
+ };
890
+ const unknown = listed(/Unknown tools? in --tools:[ \t]*([^\r\n]*)/) ?? [];
891
+ if (unknown.some(name => !name.startsWith(MCP_PREFIX) || !passed.includes(name)))
892
+ return null;
893
+ const disabled = (listed(/Built-in tools? unavailable in this session:[ \t]*([^\r\n]*)/) ?? [])
894
+ .filter(name => passed.includes(name));
895
+ return disabled.length + unknown.length > 0 ? { disabled, mcp: unknown } : null;
896
+ }
897
+ /**
898
+ * Ruling 22: the directories to drop when this omp build refused `--add-dir` (`Error: unknown
899
+ * flag(s): …`, exit 2, before any session work). Only `--add-dir` is droppable: every other
900
+ * unknown flag carries something the step needs (model, prompt, resume, tools), so the refusal
901
+ * stays fatal and nothing is retried. `null` when there is nothing to drop.
902
+ */
903
+ function refusedOmpAddDir(stderr, args) {
904
+ const match = /unknown flags?:[ \t]*([^\r\n]*)/i.exec(stderr);
905
+ if (!match)
906
+ return null;
907
+ const unknown = (match[1] ?? '').replace(/\.\s*$/, '').split(/[\s,]+/).filter(Boolean);
908
+ if (unknown.length === 0 || unknown.some(flag => flag !== '--add-dir'))
909
+ return null;
910
+ const dirs = args.flatMap((arg, i) => (arg === '--add-dir' ? [args[i + 1] ?? ''] : [])).filter(Boolean);
911
+ return dirs.length > 0 ? dirs : null;
912
+ }
913
+ /** Same as pi-runner.ts `toPiPrompt`. */
914
+ function toOmpPrompt(content) {
915
+ const text = [];
916
+ const images = [];
917
+ for (const block of content) {
918
+ if (block.type === 'text')
919
+ text.push(block.text);
920
+ else
921
+ images.push({ type: 'image', data: block.source.data, mimeType: block.source.media_type });
922
+ }
923
+ return { message: text.join('\n'), images };
924
+ }
925
+ /** Same as pi-runner.ts `emitImages`. */
926
+ function emitImages(value, onEvent) {
927
+ if (!Array.isArray(value))
928
+ return;
929
+ for (const part of value) {
930
+ if (isRecord(part) && part.type === 'image') {
931
+ const data = string(part.data);
932
+ const mediaType = string(part.mimeType);
933
+ if (data && mediaType)
934
+ onEvent?.({ type: 'image', data, mediaType });
935
+ }
936
+ }
937
+ }
938
+ /** Same as pi-runner.ts `piMessageText`. */
939
+ function ompMessageText(message) {
940
+ if (typeof message.content === 'string')
941
+ return message.content;
942
+ return Array.isArray(message.content)
943
+ ? message.content.flatMap(part => isRecord(part) && part.type === 'text' && typeof part.text === 'string' ? [part.text] : []).join('\n')
944
+ : '';
945
+ }
946
+ /** Same as pi-runner.ts `contentText`. */
947
+ function contentText(value) {
948
+ if (typeof value === 'string')
949
+ return value;
950
+ if (!Array.isArray(value))
951
+ return undefined;
952
+ const text = value
953
+ .map((part) => (isRecord(part) && part.type === 'text' ? string(part.text) : undefined))
954
+ .filter((part) => part !== undefined);
955
+ return text.length > 0 ? text.join('\n') : undefined;
956
+ }
957
+ /** OMP's failure response carries `error` as a string (rpc-mode.ts, v18.4.11). */
958
+ function rpcError(value) {
959
+ if (typeof value.error === 'string')
960
+ return value.error;
961
+ const error = isRecord(value.error) ? value.error : undefined;
962
+ return string(error?.message) ?? string(value.message) ?? `omp RPC command ${string(value.command) ?? 'unknown'} failed`;
963
+ }
964
+ function waitForExit(child) {
965
+ if (child.exitCode !== null || child.signalCode !== null)
966
+ return Promise.resolve(child.exitCode);
967
+ return new Promise((resolve) => child.once('close', resolve));
968
+ }
969
+ function wrapSpawnError(error, bin) {
970
+ if (error.code === 'ENOENT') {
971
+ return new Error(`\`${bin}\` not found on PATH: install OMP (https://omp.sh) and run \`omp login\``);
972
+ }
973
+ return error;
974
+ }
975
+ /** Path to the bundled mock (`scripts/mock-omp-rpc.mjs`), for CEZ_DRY_RUN=1; as pi-runner.ts `mockPiPath`. */
976
+ function mockOmpPath() {
977
+ const here = dirname(fileURLToPath(import.meta.url));
978
+ // here = <pkg>/dist/core (built) or <pkg>/src/core (tsx dev).
979
+ return resolvePath(here, '..', '..', 'scripts', 'mock-omp-rpc.mjs');
980
+ }
981
+ function truncate(value, max = 200) {
982
+ return value.length > max ? `${value.slice(0, max)}…` : value;
983
+ }
984
+ function isRecord(value) {
985
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
986
+ }
987
+ function string(value) {
988
+ return typeof value === 'string' ? value : undefined;
989
+ }
990
+ //# sourceMappingURL=omp-runner.js.map