@wjarka/cezarion 0.16.0 → 0.17.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 (907) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +594 -207
  3. package/dist/agent-config/account-identity.d.ts +3 -19
  4. package/dist/agent-config/account-identity.js +8 -5
  5. package/dist/agent-config/account-identity.js.map +1 -1
  6. package/dist/agent-config/catalog.d.ts +3 -14
  7. package/dist/agent-config/catalog.js.map +1 -1
  8. package/dist/agent-config/files.d.ts +2 -6
  9. package/dist/agent-config/files.js +2 -6
  10. package/dist/agent-config/files.js.map +1 -1
  11. package/dist/agent-config/model-settings/types.d.ts +1 -1
  12. package/dist/agent-config/seed.d.ts +11 -3
  13. package/dist/agent-config/seed.js +52 -48
  14. package/dist/agent-config/seed.js.map +1 -1
  15. package/dist/agent-config/service.js +3 -2
  16. package/dist/agent-config/service.js.map +1 -1
  17. package/dist/agent-config/validate.d.ts +1 -4
  18. package/dist/agent-config/validate.js.map +1 -1
  19. package/dist/application-update/helper.d.ts +2 -2
  20. package/dist/application-update/helper.js +7 -12
  21. package/dist/application-update/helper.js.map +1 -1
  22. package/dist/application-update/launcher.d.ts +4 -0
  23. package/dist/application-update/launcher.js +18 -12
  24. package/dist/application-update/launcher.js.map +1 -1
  25. package/dist/application-update/service.d.ts +3 -3
  26. package/dist/application-update/service.js +1 -1
  27. package/dist/application-update/service.js.map +1 -1
  28. package/dist/archive-on-merge-setting.d.ts +15 -0
  29. package/dist/archive-on-merge-setting.js +14 -0
  30. package/dist/archive-on-merge-setting.js.map +1 -0
  31. package/dist/artifacts/cli.d.ts +8 -0
  32. package/dist/artifacts/cli.js +21 -2
  33. package/dist/artifacts/cli.js.map +1 -1
  34. package/dist/artifacts/resolve.js +2 -1
  35. package/dist/artifacts/resolve.js.map +1 -1
  36. package/dist/artifacts/store.js.map +1 -1
  37. package/dist/automations/automation-cli.d.ts +59 -0
  38. package/dist/automations/automation-cli.js +509 -0
  39. package/dist/automations/automation-cli.js.map +1 -0
  40. package/dist/automations/builtin-skill.d.ts +7 -0
  41. package/dist/automations/builtin-skill.js +14 -0
  42. package/dist/automations/builtin-skill.js.map +1 -0
  43. package/dist/automations/github-baseline.d.ts +3 -0
  44. package/dist/automations/github-baseline.js +17 -0
  45. package/dist/automations/github-baseline.js.map +1 -0
  46. package/dist/automations/github-poller.d.ts +23 -4
  47. package/dist/automations/github-poller.js +157 -88
  48. package/dist/automations/github-poller.js.map +1 -1
  49. package/dist/automations/prompts.d.ts +12 -0
  50. package/dist/automations/prompts.js +160 -0
  51. package/dist/automations/prompts.js.map +1 -0
  52. package/dist/automations/schedule-runner.d.ts +43 -24
  53. package/dist/automations/schedule-runner.js +207 -20
  54. package/dist/automations/schedule-runner.js.map +1 -1
  55. package/dist/automations/scheduler.d.ts +10 -2
  56. package/dist/automations/scheduler.js +200 -54
  57. package/dist/automations/scheduler.js.map +1 -1
  58. package/dist/automations/service.d.ts +777 -0
  59. package/dist/automations/service.js +444 -0
  60. package/dist/automations/service.js.map +1 -0
  61. package/dist/automations/store.d.ts +17 -7
  62. package/dist/automations/store.js +88 -17
  63. package/dist/automations/store.js.map +1 -1
  64. package/dist/automations/task-template.d.ts +7 -2
  65. package/dist/automations/task-template.js +93 -44
  66. package/dist/automations/task-template.js.map +1 -1
  67. package/dist/automations/tool-adapter.d.ts +6 -0
  68. package/dist/automations/tool-adapter.js +108 -0
  69. package/dist/automations/tool-adapter.js.map +1 -0
  70. package/dist/automations/types.d.ts +1 -405
  71. package/dist/automations/types.js +1 -218
  72. package/dist/automations/types.js.map +1 -1
  73. package/dist/autosave-cleanup.d.ts +26 -0
  74. package/dist/autosave-cleanup.js +87 -0
  75. package/dist/autosave-cleanup.js.map +1 -0
  76. package/dist/autosave-git.d.ts +3 -0
  77. package/dist/autosave-git.js +67 -35
  78. package/dist/autosave-git.js.map +1 -1
  79. package/dist/autosave-group.d.ts +8 -0
  80. package/dist/autosave-group.js +61 -0
  81. package/dist/autosave-group.js.map +1 -0
  82. package/dist/autosave-holders.d.ts +31 -4
  83. package/dist/autosave-holders.js +92 -7
  84. package/dist/autosave-holders.js.map +1 -1
  85. package/dist/autosave-windows.d.ts +14 -0
  86. package/dist/autosave-windows.js +36 -2
  87. package/dist/autosave-windows.js.map +1 -1
  88. package/dist/ci-wait/client.d.ts +20 -3
  89. package/dist/ci-wait/client.js +40 -44
  90. package/dist/ci-wait/client.js.map +1 -1
  91. package/dist/ci-wait/controller.d.ts +622 -36
  92. package/dist/ci-wait/controller.js +152 -20
  93. package/dist/ci-wait/controller.js.map +1 -1
  94. package/dist/ci-wait/errors.d.ts +19 -0
  95. package/dist/ci-wait/errors.js +180 -2
  96. package/dist/ci-wait/errors.js.map +1 -1
  97. package/dist/ci-wait/github.d.ts +25 -1
  98. package/dist/ci-wait/github.js +76 -9
  99. package/dist/ci-wait/github.js.map +1 -1
  100. package/dist/ci-wait/mcp.d.ts +0 -1
  101. package/dist/ci-wait/mcp.js +0 -2
  102. package/dist/ci-wait/mcp.js.map +1 -1
  103. package/dist/ci-wait/supervisor.d.ts +7 -0
  104. package/dist/ci-wait/supervisor.js +113 -17
  105. package/dist/ci-wait/supervisor.js.map +1 -1
  106. package/dist/ci-wait/tools.d.ts +536 -16
  107. package/dist/ci-wait/tools.js +165 -20
  108. package/dist/ci-wait/tools.js.map +1 -1
  109. package/dist/config.d.ts +3 -17
  110. package/dist/config.js +4 -60
  111. package/dist/config.js.map +1 -1
  112. package/dist/contract/active-provider.d.ts +12 -0
  113. package/dist/contract/agent-profiles.d.ts +2 -10
  114. package/dist/contract/ask-schema.d.ts +22 -4
  115. package/dist/contract/ask.d.ts +6 -4
  116. package/dist/contract/attachments.d.ts +35 -0
  117. package/dist/contract/attention.d.ts +46 -37
  118. package/dist/contract/automation-requests.d.ts +308 -0
  119. package/dist/contract/automation-schedule.d.ts +40 -11
  120. package/dist/contract/automation-storage.d.ts +512 -0
  121. package/dist/contract/automations.d.ts +421 -40
  122. package/dist/contract/ci-wait.d.ts +41 -4
  123. package/dist/contract/config-requests.d.ts +36 -0
  124. package/dist/contract/conversations.d.ts +24 -10
  125. package/dist/contract/defined.d.ts +8 -0
  126. package/dist/contract/delegation.d.ts +312 -8
  127. package/dist/contract/events.d.ts +24 -3
  128. package/dist/contract/github-requests.d.ts +26 -0
  129. package/dist/contract/github.d.ts +60 -27
  130. package/dist/contract/handoff.d.ts +43 -0
  131. package/dist/contract/health.d.ts +2 -2
  132. package/dist/contract/image-path.d.ts +6 -0
  133. package/dist/contract/index.d.ts +31 -0
  134. package/dist/contract/index.js +3756 -1525
  135. package/dist/contract/live-coordination.d.ts +158 -0
  136. package/dist/contract/live-limits.d.ts +8 -0
  137. package/dist/contract/live.d.ts +123 -0
  138. package/dist/contract/pending-ask.d.ts +58 -0
  139. package/dist/contract/permissions.d.ts +54 -0
  140. package/dist/contract/preview.d.ts +33 -0
  141. package/dist/contract/project-limits.d.ts +33 -0
  142. package/dist/contract/projects.d.ts +486 -20
  143. package/dist/contract/provider-requests.d.ts +77 -0
  144. package/dist/contract/query-requests.d.ts +52 -0
  145. package/dist/contract/repo-requests.d.ts +12 -0
  146. package/dist/contract/run-labels.d.ts +8 -0
  147. package/dist/contract/run-limits.d.ts +8 -0
  148. package/dist/contract/run-predicates.d.ts +32 -0
  149. package/dist/contract/run-requests.d.ts +33 -0
  150. package/dist/contract/run-search.d.ts +193 -0
  151. package/dist/contract/runs.d.ts +2232 -1894
  152. package/dist/contract/scheduled-tasks.d.ts +187 -0
  153. package/dist/contract/skills.d.ts +22 -772
  154. package/dist/contract/team-skill-ref.d.ts +10 -0
  155. package/dist/{core → contract}/tool-display.d.ts +4 -10
  156. package/dist/contract/ui-state-requests.d.ts +60 -0
  157. package/dist/contract/version.d.ts +3 -0
  158. package/dist/contract/voice.d.ts +213 -0
  159. package/dist/contract/waits.d.ts +1204 -0
  160. package/dist/contract/workflow-file.d.ts +37 -0
  161. package/dist/contract/workflow-requests.d.ts +7 -0
  162. package/dist/contract/workflows.d.ts +1 -2
  163. package/dist/contract/workspace.d.ts +34 -82
  164. package/dist/contract/zoned-time.d.ts +2 -11
  165. package/dist/core/agent-env.d.ts +4 -16
  166. package/dist/core/agent-env.js +14 -16
  167. package/dist/core/agent-env.js.map +1 -1
  168. package/dist/core/agent-profiles.d.ts +28 -40
  169. package/dist/core/agent-profiles.js +56 -51
  170. package/dist/core/agent-profiles.js.map +1 -1
  171. package/dist/core/agent-runner.d.ts +88 -44
  172. package/dist/core/agent-runner.js +17 -18
  173. package/dist/core/agent-runner.js.map +1 -1
  174. package/dist/core/ask.d.ts +3 -0
  175. package/dist/core/ask.js +12 -0
  176. package/dist/core/ask.js.map +1 -1
  177. package/dist/core/backend-detect.js +16 -2
  178. package/dist/core/backend-detect.js.map +1 -1
  179. package/dist/core/claude-cli-runner.d.ts +23 -4
  180. package/dist/core/claude-cli-runner.js +280 -76
  181. package/dist/core/claude-cli-runner.js.map +1 -1
  182. package/dist/core/claude-model-catalog.d.ts +2 -10
  183. package/dist/core/claude-model-catalog.js +2 -10
  184. package/dist/core/claude-model-catalog.js.map +1 -1
  185. package/dist/core/claude-ui-mapper.d.ts +2 -2
  186. package/dist/core/claude-ui-mapper.js +94 -2
  187. package/dist/core/claude-ui-mapper.js.map +1 -1
  188. package/dist/core/codex-app-server-runner.d.ts +3 -4
  189. package/dist/core/codex-app-server-runner.js +146 -51
  190. package/dist/core/codex-app-server-runner.js.map +1 -1
  191. package/dist/core/codex-app-server-transport.d.ts +4 -3
  192. package/dist/core/codex-app-server-transport.js +11 -7
  193. package/dist/core/codex-app-server-transport.js.map +1 -1
  194. package/dist/core/codex-approvals.d.ts +12 -0
  195. package/dist/core/codex-approvals.js +161 -0
  196. package/dist/core/codex-approvals.js.map +1 -0
  197. package/dist/core/codex-permissions.d.ts +1 -1
  198. package/dist/core/codex-ui-mapper.js +45 -1
  199. package/dist/core/codex-ui-mapper.js.map +1 -1
  200. package/dist/core/cursor-acp-runner.d.ts +1 -1
  201. package/dist/core/cursor-acp-runner.js +39 -15
  202. package/dist/core/cursor-acp-runner.js.map +1 -1
  203. package/dist/core/cursor-cli-flags.d.ts +19 -0
  204. package/dist/core/cursor-cli-flags.js +47 -0
  205. package/dist/core/cursor-cli-flags.js.map +1 -0
  206. package/dist/core/cursor-model-catalog.d.ts +22 -0
  207. package/dist/core/cursor-model-catalog.js +53 -24
  208. package/dist/core/cursor-model-catalog.js.map +1 -1
  209. package/dist/core/cursor-print-capabilities.d.ts +16 -0
  210. package/dist/core/cursor-print-capabilities.js +141 -0
  211. package/dist/core/cursor-print-capabilities.js.map +1 -0
  212. package/dist/core/cursor-print-process.d.ts +31 -0
  213. package/dist/core/cursor-print-process.js +117 -0
  214. package/dist/core/cursor-print-process.js.map +1 -0
  215. package/dist/core/cursor-print-runner.d.ts +31 -0
  216. package/dist/core/cursor-print-runner.js +555 -0
  217. package/dist/core/cursor-print-runner.js.map +1 -0
  218. package/dist/core/cursor-print-tool-catalog.d.ts +10 -0
  219. package/dist/core/cursor-print-tool-catalog.js +91 -0
  220. package/dist/core/cursor-print-tool-catalog.js.map +1 -0
  221. package/dist/core/cursor-print-ui-mapper.d.ts +47 -0
  222. package/dist/core/cursor-print-ui-mapper.js +553 -0
  223. package/dist/core/cursor-print-ui-mapper.js.map +1 -0
  224. package/dist/core/cursor-runner.d.ts +24 -0
  225. package/dist/core/cursor-runner.js +149 -0
  226. package/dist/core/cursor-runner.js.map +1 -0
  227. package/dist/core/cursor-ui-mapper.js +5 -2
  228. package/dist/core/cursor-ui-mapper.js.map +1 -1
  229. package/dist/core/defined.d.ts +1 -0
  230. package/dist/core/defined.js +2 -0
  231. package/dist/core/defined.js.map +1 -0
  232. package/dist/core/hook-items.d.ts +14 -0
  233. package/dist/core/hook-items.js +13 -0
  234. package/dist/core/hook-items.js.map +1 -0
  235. package/dist/core/host-model-catalog.d.ts +2 -9
  236. package/dist/core/host-model-catalog.js +2 -9
  237. package/dist/core/host-model-catalog.js.map +1 -1
  238. package/dist/core/managed-session.js +15 -3
  239. package/dist/core/managed-session.js.map +1 -1
  240. package/dist/core/model-identity.d.ts +4 -21
  241. package/dist/core/model-identity.js.map +1 -1
  242. package/dist/core/model-presets.d.ts +2 -13
  243. package/dist/core/model-presets.js +2 -13
  244. package/dist/core/model-presets.js.map +1 -1
  245. package/dist/core/omp-model-catalog.js.map +1 -1
  246. package/dist/core/omp-runner.d.ts +6 -7
  247. package/dist/core/omp-runner.js +92 -33
  248. package/dist/core/omp-runner.js.map +1 -1
  249. package/dist/core/omp-ui-mapper.js +1 -1
  250. package/dist/core/omp-ui-mapper.js.map +1 -1
  251. package/dist/core/opencode-model-catalog.js.map +1 -1
  252. package/dist/core/opencode-permissions.d.ts +52 -0
  253. package/dist/core/opencode-permissions.js +101 -0
  254. package/dist/core/opencode-permissions.js.map +1 -0
  255. package/dist/core/opencode-server-runner.d.ts +10 -9
  256. package/dist/core/opencode-server-runner.js +286 -28
  257. package/dist/core/opencode-server-runner.js.map +1 -1
  258. package/dist/core/opencode-session-error.d.ts +15 -7
  259. package/dist/core/opencode-session-error.js +25 -9
  260. package/dist/core/opencode-session-error.js.map +1 -1
  261. package/dist/core/opencode-ui-mapper.js +2 -2
  262. package/dist/core/opencode-ui-mapper.js.map +1 -1
  263. package/dist/core/pi-model-catalog.js.map +1 -1
  264. package/dist/core/pi-runner.d.ts +12 -4
  265. package/dist/core/pi-runner.js +171 -63
  266. package/dist/core/pi-runner.js.map +1 -1
  267. package/dist/core/pi-ui-mapper.js +1 -1
  268. package/dist/core/pi-ui-mapper.js.map +1 -1
  269. package/dist/core/process-usage.d.ts +5 -0
  270. package/dist/core/process-usage.js +24 -4
  271. package/dist/core/process-usage.js.map +1 -1
  272. package/dist/core/provider-auth.d.ts +16 -107
  273. package/dist/core/provider-auth.js +16 -132
  274. package/dist/core/provider-auth.js.map +1 -1
  275. package/dist/core/runner-factory.d.ts +4 -1
  276. package/dist/core/runner-factory.js +3 -3
  277. package/dist/core/runner-factory.js.map +1 -1
  278. package/dist/core/runner-runtime.d.ts +22 -0
  279. package/dist/core/runner-runtime.js +66 -5
  280. package/dist/core/runner-runtime.js.map +1 -1
  281. package/dist/core/runner-tool-grants.d.ts +21 -0
  282. package/dist/core/runner-tool-grants.js +64 -0
  283. package/dist/core/runner-tool-grants.js.map +1 -0
  284. package/dist/core/secret-redaction.d.ts +3 -20
  285. package/dist/core/secret-redaction.js +3 -20
  286. package/dist/core/secret-redaction.js.map +1 -1
  287. package/dist/core/session-process.d.ts +22 -2
  288. package/dist/core/session-process.js +170 -12
  289. package/dist/core/session-process.js.map +1 -1
  290. package/dist/core/shell-env.d.ts +2 -15
  291. package/dist/core/shell-env.js +2 -15
  292. package/dist/core/shell-env.js.map +1 -1
  293. package/dist/core/ui-events.d.ts +60 -47
  294. package/dist/core/ui-events.js +0 -23
  295. package/dist/core/ui-events.js.map +1 -1
  296. package/dist/core/usage-limit.js.map +1 -1
  297. package/dist/data-gitignore.js +3 -1
  298. package/dist/data-gitignore.js.map +1 -1
  299. package/dist/delegation/capacity.d.ts +4 -8
  300. package/dist/delegation/capacity.js +4 -8
  301. package/dist/delegation/capacity.js.map +1 -1
  302. package/dist/delegation/cli.js +123 -21
  303. package/dist/delegation/cli.js.map +1 -1
  304. package/dist/delegation/context.d.ts +19 -3
  305. package/dist/delegation/context.js +217 -22
  306. package/dist/delegation/context.js.map +1 -1
  307. package/dist/delegation/conversations.d.ts +4 -3
  308. package/dist/delegation/conversations.js +7 -5
  309. package/dist/delegation/conversations.js.map +1 -1
  310. package/dist/delegation/destroy-observation.d.ts +1 -1
  311. package/dist/delegation/destroy-observation.js.map +1 -1
  312. package/dist/delegation/effort.js.map +1 -1
  313. package/dist/delegation/execution-identity.d.ts +1 -1
  314. package/dist/delegation/execution-identity.js +41 -7
  315. package/dist/delegation/execution-identity.js.map +1 -1
  316. package/dist/delegation/idle-test-app.d.ts +4 -0
  317. package/dist/delegation/idle-test-app.js +174 -0
  318. package/dist/delegation/idle-test-app.js.map +1 -0
  319. package/dist/delegation/input.js.map +1 -1
  320. package/dist/delegation/park-notice.d.ts +42 -0
  321. package/dist/delegation/park-notice.js +71 -0
  322. package/dist/delegation/park-notice.js.map +1 -0
  323. package/dist/delegation/policy.d.ts +2 -0
  324. package/dist/delegation/policy.js +9 -1
  325. package/dist/delegation/policy.js.map +1 -1
  326. package/dist/delegation/process-liveness.d.ts +10 -15
  327. package/dist/delegation/process-liveness.js +11 -35
  328. package/dist/delegation/process-liveness.js.map +1 -1
  329. package/dist/delegation/provision.js +21 -19
  330. package/dist/delegation/provision.js.map +1 -1
  331. package/dist/delegation/questions.d.ts +3 -1
  332. package/dist/delegation/questions.js +10 -5
  333. package/dist/delegation/questions.js.map +1 -1
  334. package/dist/delegation/readiness.d.ts +2 -2
  335. package/dist/delegation/results.d.ts +4 -1
  336. package/dist/delegation/results.js +49 -18
  337. package/dist/delegation/results.js.map +1 -1
  338. package/dist/delegation/retry-backoff.d.ts +8 -0
  339. package/dist/delegation/retry-backoff.js +8 -0
  340. package/dist/delegation/retry-backoff.js.map +1 -1
  341. package/dist/delegation/routes.d.ts +114 -30
  342. package/dist/delegation/routes.js +4 -1
  343. package/dist/delegation/routes.js.map +1 -1
  344. package/dist/delegation/scratch-cleanup.d.ts +4 -5
  345. package/dist/delegation/scratch-cleanup.js +10 -8
  346. package/dist/delegation/scratch-cleanup.js.map +1 -1
  347. package/dist/delegation/service.d.ts +12 -4
  348. package/dist/delegation/service.js +76 -28
  349. package/dist/delegation/service.js.map +1 -1
  350. package/dist/delegation/transport.d.ts +154 -70
  351. package/dist/delegation/wait.d.ts +11 -0
  352. package/dist/delegation/wait.js +24 -0
  353. package/dist/delegation/wait.js.map +1 -1
  354. package/dist/delegation/windows-test-app.d.ts +5 -0
  355. package/dist/delegation/windows-test-app.js +63 -0
  356. package/dist/delegation/windows-test-app.js.map +1 -0
  357. package/dist/delegation/windows-workspace-identity.d.ts +3 -0
  358. package/dist/delegation/windows-workspace-identity.js +22 -0
  359. package/dist/delegation/windows-workspace-identity.js.map +1 -0
  360. package/dist/delegation/workspace.d.ts +5 -10
  361. package/dist/delegation/workspace.js +136 -75
  362. package/dist/delegation/workspace.js.map +1 -1
  363. package/dist/detached-process.d.ts +103 -0
  364. package/dist/detached-process.js +301 -0
  365. package/dist/detached-process.js.map +1 -0
  366. package/dist/discovery/catalog.d.ts +2 -1
  367. package/dist/discovery/catalog.js.map +1 -1
  368. package/dist/discovery/cli.js +3 -1
  369. package/dist/discovery/cli.js.map +1 -1
  370. package/dist/git-diff-base.d.ts +6 -61
  371. package/dist/git-diff-base.js +6 -70
  372. package/dist/git-diff-base.js.map +1 -1
  373. package/dist/git-index-copy.d.ts +4 -0
  374. package/dist/git-index-copy.js +10 -0
  375. package/dist/git-index-copy.js.map +1 -0
  376. package/dist/git-worktree-lock.d.ts +2 -4
  377. package/dist/git-worktree-lock.js +2 -4
  378. package/dist/git-worktree-lock.js.map +1 -1
  379. package/dist/git-worktree-release.d.ts +2 -2
  380. package/dist/git-worktree-release.js.map +1 -1
  381. package/dist/git-worktree.d.ts +47 -76
  382. package/dist/git-worktree.js +240 -94
  383. package/dist/git-worktree.js.map +1 -1
  384. package/dist/handoff.d.ts +51 -41
  385. package/dist/handoff.js +130 -78
  386. package/dist/handoff.js.map +1 -1
  387. package/dist/index.js +60 -39
  388. package/dist/index.js.map +1 -1
  389. package/dist/merge-observations.d.ts +13 -0
  390. package/dist/merge-observations.js +55 -0
  391. package/dist/merge-observations.js.map +1 -0
  392. package/dist/open-url.js +4 -14
  393. package/dist/open-url.js.map +1 -1
  394. package/dist/paths.d.ts +10 -13
  395. package/dist/paths.js +36 -17
  396. package/dist/paths.js.map +1 -1
  397. package/dist/planner.d.ts +2 -1
  398. package/dist/planner.js +9 -6
  399. package/dist/planner.js.map +1 -1
  400. package/dist/preview/agent-address.d.ts +34 -0
  401. package/dist/preview/agent-address.js +51 -0
  402. package/dist/preview/agent-address.js.map +1 -0
  403. package/dist/preview/agent-marker.d.ts +17 -0
  404. package/dist/preview/agent-marker.js +71 -0
  405. package/dist/preview/agent-marker.js.map +1 -0
  406. package/dist/preview/cdp-proxy.d.ts +54 -0
  407. package/dist/preview/cdp-proxy.js +151 -0
  408. package/dist/preview/cdp-proxy.js.map +1 -0
  409. package/dist/preview/cdp.d.ts +39 -3
  410. package/dist/preview/cdp.js +155 -57
  411. package/dist/preview/cdp.js.map +1 -1
  412. package/dist/preview/chromium.d.ts +18 -6
  413. package/dist/preview/chromium.js +112 -25
  414. package/dist/preview/chromium.js.map +1 -1
  415. package/dist/preview/dev-server.d.ts +1 -1
  416. package/dist/preview/dev-server.js +20 -44
  417. package/dist/preview/dev-server.js.map +1 -1
  418. package/dist/preview/host.d.ts +70 -3
  419. package/dist/preview/host.js +314 -28
  420. package/dist/preview/host.js.map +1 -1
  421. package/dist/preview/registration.d.ts +10 -5
  422. package/dist/preview/registration.js +2 -0
  423. package/dist/preview/registration.js.map +1 -1
  424. package/dist/preview/session.d.ts +2 -0
  425. package/dist/preview/session.js +10 -0
  426. package/dist/preview/session.js.map +1 -1
  427. package/dist/process-wait/exit-file.d.ts +29 -0
  428. package/dist/process-wait/exit-file.js +67 -0
  429. package/dist/process-wait/exit-file.js.map +1 -0
  430. package/dist/process-wait/supervisor.d.ts +30 -0
  431. package/dist/process-wait/supervisor.js +71 -0
  432. package/dist/process-wait/supervisor.js.map +1 -0
  433. package/dist/release/snapshot.d.ts +6 -6
  434. package/dist/runs/agent-tmpdir.d.ts +51 -36
  435. package/dist/runs/agent-tmpdir.js +389 -132
  436. package/dist/runs/agent-tmpdir.js.map +1 -1
  437. package/dist/runs/auto-name.d.ts +6 -6
  438. package/dist/runs/auto-name.js +1 -1
  439. package/dist/runs/auto-name.js.map +1 -1
  440. package/dist/runs/client-request.d.ts +3 -3
  441. package/dist/runs/client-request.js +3 -3
  442. package/dist/runs/client-request.js.map +1 -1
  443. package/dist/runs/delegation-state.d.ts +11 -2
  444. package/dist/runs/delegation-state.js.map +1 -1
  445. package/dist/runs/event-history-agent-context.d.ts +14 -0
  446. package/dist/runs/event-history-agent-context.js +105 -0
  447. package/dist/runs/event-history-agent-context.js.map +1 -0
  448. package/dist/runs/event-history-context.d.ts +15 -0
  449. package/dist/runs/event-history-context.js +109 -0
  450. package/dist/runs/event-history-context.js.map +1 -0
  451. package/dist/runs/event-history.d.ts +4 -1
  452. package/dist/runs/event-history.js +29 -130
  453. package/dist/runs/event-history.js.map +1 -1
  454. package/dist/runs/history-stream.d.ts +2 -0
  455. package/dist/runs/history-stream.js +62 -0
  456. package/dist/runs/history-stream.js.map +1 -0
  457. package/dist/runs/legacy-index.d.ts +6 -19
  458. package/dist/runs/legacy-index.js +6 -19
  459. package/dist/runs/legacy-index.js.map +1 -1
  460. package/dist/runs/live-item-snapshots.d.ts +13 -0
  461. package/dist/runs/live-item-snapshots.js +79 -0
  462. package/dist/runs/live-item-snapshots.js.map +1 -0
  463. package/dist/runs/loaded-run-healing.d.ts +3 -0
  464. package/dist/runs/loaded-run-healing.js +17 -0
  465. package/dist/runs/loaded-run-healing.js.map +1 -0
  466. package/dist/runs/outcome-commit.d.ts +98 -0
  467. package/dist/runs/outcome-commit.js +132 -0
  468. package/dist/runs/outcome-commit.js.map +1 -0
  469. package/dist/runs/pull-requests.d.ts +14 -0
  470. package/dist/runs/pull-requests.js +96 -0
  471. package/dist/runs/pull-requests.js.map +1 -0
  472. package/dist/runs/raw-record.d.ts +3 -15
  473. package/dist/runs/raw-record.js +3 -15
  474. package/dist/runs/raw-record.js.map +1 -1
  475. package/dist/runs/retention.d.ts +2 -2
  476. package/dist/runs/retention.js +7 -0
  477. package/dist/runs/retention.js.map +1 -1
  478. package/dist/runs/review-gate.d.ts +1 -1
  479. package/dist/runs/review-gate.js.map +1 -1
  480. package/dist/runs/run-claims.d.ts +2 -9
  481. package/dist/runs/run-claims.js.map +1 -1
  482. package/dist/runs/run-database.d.ts +29 -34
  483. package/dist/runs/run-database.js +124 -46
  484. package/dist/runs/run-database.js.map +1 -1
  485. package/dist/runs/run-index.d.ts +6 -25
  486. package/dist/runs/run-index.js +25 -38
  487. package/dist/runs/run-index.js.map +1 -1
  488. package/dist/runs/run-row.d.ts +5 -34
  489. package/dist/runs/run-row.js +13 -38
  490. package/dist/runs/run-row.js.map +1 -1
  491. package/dist/runs/store.d.ts +1597 -744
  492. package/dist/runs/store.js +1231 -739
  493. package/dist/runs/store.js.map +1 -1
  494. package/dist/runs/task-markers.d.ts +1 -0
  495. package/dist/runs/task-markers.js +5 -1
  496. package/dist/runs/task-markers.js.map +1 -1
  497. package/dist/runs/task-refs.d.ts +3 -3
  498. package/dist/runs/transcript-facts-fold.d.ts +64 -0
  499. package/dist/runs/transcript-facts-fold.js +109 -0
  500. package/dist/runs/transcript-facts-fold.js.map +1 -0
  501. package/dist/runs/transcript-facts-load.d.ts +11 -0
  502. package/dist/runs/transcript-facts-load.js +66 -0
  503. package/dist/runs/transcript-facts-load.js.map +1 -0
  504. package/dist/runs/transcript-facts-queue.d.ts +30 -0
  505. package/dist/runs/transcript-facts-queue.js +227 -0
  506. package/dist/runs/transcript-facts-queue.js.map +1 -0
  507. package/dist/runs/transcript-facts-worker.d.ts +15 -0
  508. package/dist/runs/transcript-facts-worker.js +26 -0
  509. package/dist/runs/transcript-facts-worker.js.map +1 -0
  510. package/dist/runs/transcript-facts.d.ts +13 -51
  511. package/dist/runs/transcript-facts.js +86 -202
  512. package/dist/runs/transcript-facts.js.map +1 -1
  513. package/dist/runs/ui-event-sink.d.ts +5 -30
  514. package/dist/runs/ui-event-sink.js +8 -30
  515. package/dist/runs/ui-event-sink.js.map +1 -1
  516. package/dist/runs/webhook.d.ts +104 -38
  517. package/dist/runs/webhook.js +180 -77
  518. package/dist/runs/webhook.js.map +1 -1
  519. package/dist/server/app-type.d.ts +2 -17
  520. package/dist/server/archive-on-merge.d.ts +35 -0
  521. package/dist/server/archive-on-merge.js +202 -0
  522. package/dist/server/archive-on-merge.js.map +1 -0
  523. package/dist/server/capabilities.d.ts +3 -15
  524. package/dist/server/capabilities.js +6 -19
  525. package/dist/server/capabilities.js.map +1 -1
  526. package/dist/server/checkout-progress.d.ts +10 -0
  527. package/dist/server/checkout-progress.js +27 -0
  528. package/dist/server/checkout-progress.js.map +1 -0
  529. package/dist/server/checkout.d.ts +10 -19
  530. package/dist/server/checkout.js +15 -45
  531. package/dist/server/checkout.js.map +1 -1
  532. package/dist/server/cockpit-ownership.d.ts +2 -0
  533. package/dist/server/cockpit-ownership.js +9 -0
  534. package/dist/server/cockpit-ownership.js.map +1 -1
  535. package/dist/server/cold-repo-handles.d.ts +2 -7
  536. package/dist/server/cold-repo-handles.js +2 -7
  537. package/dist/server/cold-repo-handles.js.map +1 -1
  538. package/dist/server/completion-actions.d.ts +30 -0
  539. package/dist/server/completion-actions.js +204 -0
  540. package/dist/server/completion-actions.js.map +1 -0
  541. package/dist/server/create-project.d.ts +71 -0
  542. package/dist/server/create-project.js +448 -0
  543. package/dist/server/create-project.js.map +1 -0
  544. package/dist/server/forge/github-filters.d.ts +7 -1
  545. package/dist/server/forge/github-filters.js +66 -51
  546. package/dist/server/forge/github-filters.js.map +1 -1
  547. package/dist/server/forge/github-merge-requirements.d.ts +18 -0
  548. package/dist/server/forge/github-merge-requirements.js +137 -0
  549. package/dist/server/forge/github-merge-requirements.js.map +1 -0
  550. package/dist/server/forge/github.d.ts +98 -111
  551. package/dist/server/forge/github.js +625 -306
  552. package/dist/server/forge/github.js.map +1 -1
  553. package/dist/server/forge/types.d.ts +25 -22
  554. package/dist/server/fs-browse.d.ts +3 -28
  555. package/dist/server/fs-browse.js.map +1 -1
  556. package/dist/server/git-changes.d.ts +5 -15
  557. package/dist/server/git-changes.js +3 -50
  558. package/dist/server/git-changes.js.map +1 -1
  559. package/dist/server/git.d.ts +3 -1
  560. package/dist/server/git.js +12 -0
  561. package/dist/server/git.js.map +1 -1
  562. package/dist/server/github.d.ts +1 -1
  563. package/dist/server/github.js +1 -1
  564. package/dist/server/github.js.map +1 -1
  565. package/dist/server/launch-key.d.ts +2 -4
  566. package/dist/server/launch-key.js +2 -4
  567. package/dist/server/launch-key.js.map +1 -1
  568. package/dist/server/live-routes.d.ts +68 -0
  569. package/dist/server/live-routes.js +147 -0
  570. package/dist/server/live-routes.js.map +1 -0
  571. package/dist/server/open-in-app.d.ts +4 -13
  572. package/dist/server/open-in-app.js +5 -41
  573. package/dist/server/open-in-app.js.map +1 -1
  574. package/dist/server/open-in-targets.d.ts +42 -0
  575. package/dist/server/open-in-targets.js +110 -0
  576. package/dist/server/open-in-targets.js.map +1 -0
  577. package/dist/server/open-in-terminal.d.ts +3 -0
  578. package/dist/server/open-in-terminal.js +6 -26
  579. package/dist/server/open-in-terminal.js.map +1 -1
  580. package/dist/server/preview-cdp-socket.d.ts +21 -0
  581. package/dist/server/preview-cdp-socket.js +112 -0
  582. package/dist/server/preview-cdp-socket.js.map +1 -0
  583. package/dist/server/preview-socket.d.ts +11 -11
  584. package/dist/server/preview-socket.js +22 -22
  585. package/dist/server/preview-socket.js.map +1 -1
  586. package/dist/server/preview-sockets.d.ts +14 -0
  587. package/dist/server/preview-sockets.js +18 -0
  588. package/dist/server/preview-sockets.js.map +1 -0
  589. package/dist/server/project-context.d.ts +54 -18
  590. package/dist/server/project-context.js +178 -5
  591. package/dist/server/project-context.js.map +1 -1
  592. package/dist/server/provider-action-gate.d.ts +0 -2
  593. package/dist/server/provider-action-gate.js +0 -21
  594. package/dist/server/provider-action-gate.js.map +1 -1
  595. package/dist/server/provider-auth-runtime.d.ts +3 -18
  596. package/dist/server/provider-auth-runtime.js +3 -18
  597. package/dist/server/provider-auth-runtime.js.map +1 -1
  598. package/dist/server/repo-branches.d.ts +3 -9
  599. package/dist/server/repo-branches.js +6 -22
  600. package/dist/server/repo-branches.js.map +1 -1
  601. package/dist/server/run-event-feed.d.ts +20 -0
  602. package/dist/server/run-event-feed.js +190 -0
  603. package/dist/server/run-event-feed.js.map +1 -0
  604. package/dist/server/server-deps.d.ts +37 -0
  605. package/dist/server/server-deps.js +114 -0
  606. package/dist/server/server-deps.js.map +1 -0
  607. package/dist/server/server.d.ts +14082 -11998
  608. package/dist/server/server.js +785 -1647
  609. package/dist/server/server.js.map +1 -1
  610. package/dist/server/start-run-options.d.ts +82 -0
  611. package/dist/server/start-run-options.js +111 -0
  612. package/dist/server/start-run-options.js.map +1 -0
  613. package/dist/server/validators.d.ts +3 -29
  614. package/dist/server/validators.js +1 -2
  615. package/dist/server/validators.js.map +1 -1
  616. package/dist/server/ws.d.ts +3 -23
  617. package/dist/server/ws.js +3 -23
  618. package/dist/server/ws.js.map +1 -1
  619. package/dist/server-install/engine.d.ts +6 -9
  620. package/dist/server-install/engine.js +2 -9
  621. package/dist/server-install/engine.js.map +1 -1
  622. package/dist/server-install/platforms/ubuntu-vps.d.ts +1 -1
  623. package/dist/server-install/state.d.ts +2 -4
  624. package/dist/server-install/state.js +2 -4
  625. package/dist/server-install/state.js.map +1 -1
  626. package/dist/server-install/steps.d.ts +2 -5
  627. package/dist/server-install/steps.js +3 -6
  628. package/dist/server-install/steps.js.map +1 -1
  629. package/dist/server-install/types.d.ts +3 -6
  630. package/dist/server-install/types.js +2 -5
  631. package/dist/server-install/types.js.map +1 -1
  632. package/dist/server-install/ui.d.ts +2 -8
  633. package/dist/server-install/ui.js +3 -3
  634. package/dist/server-install/ui.js.map +1 -1
  635. package/dist/session-context.d.ts +64 -0
  636. package/dist/session-context.js +264 -0
  637. package/dist/session-context.js.map +1 -0
  638. package/dist/skills-provision.d.ts +62 -0
  639. package/dist/skills-provision.js +325 -0
  640. package/dist/skills-provision.js.map +1 -0
  641. package/dist/skills-remote.d.ts +29 -27
  642. package/dist/skills-remote.js +236 -100
  643. package/dist/skills-remote.js.map +1 -1
  644. package/dist/skills.d.ts +25 -36
  645. package/dist/skills.js +43 -54
  646. package/dist/skills.js.map +1 -1
  647. package/dist/subcommands.d.ts +11 -0
  648. package/dist/subcommands.js +24 -0
  649. package/dist/subcommands.js.map +1 -0
  650. package/dist/task-cli/cli.js +78 -25
  651. package/dist/task-cli/cli.js.map +1 -1
  652. package/dist/task-cli/discovery.d.ts +4 -2
  653. package/dist/task-cli/discovery.js +14 -13
  654. package/dist/task-cli/discovery.js.map +1 -1
  655. package/dist/task-cli/hint.d.ts +20 -0
  656. package/dist/task-cli/hint.js +14 -0
  657. package/dist/task-cli/hint.js.map +1 -0
  658. package/dist/task-cli/http.d.ts +1 -0
  659. package/dist/task-cli/http.js +3 -1
  660. package/dist/task-cli/http.js.map +1 -1
  661. package/dist/task-cli/projections.d.ts +14 -11
  662. package/dist/task-cli/projections.js +15 -11
  663. package/dist/task-cli/projections.js.map +1 -1
  664. package/dist/task-cli/watch.d.ts +18 -10
  665. package/dist/task-cli/watch.js +128 -20
  666. package/dist/task-cli/watch.js.map +1 -1
  667. package/dist/ui-state.d.ts +17 -3
  668. package/dist/ui-state.js +56 -9
  669. package/dist/ui-state.js.map +1 -1
  670. package/dist/update-check.d.ts +0 -10
  671. package/dist/update-check.js +1 -16
  672. package/dist/update-check.js.map +1 -1
  673. package/dist/voice/decide.d.ts +32 -0
  674. package/dist/voice/decide.js +297 -0
  675. package/dist/voice/decide.js.map +1 -0
  676. package/dist/voice/decision-log.d.ts +52 -0
  677. package/dist/voice/decision-log.js +107 -0
  678. package/dist/voice/decision-log.js.map +1 -0
  679. package/dist/voice/engine.d.ts +119 -0
  680. package/dist/voice/engine.js +60 -0
  681. package/dist/voice/engine.js.map +1 -0
  682. package/dist/voice/fake-engine.d.ts +10 -0
  683. package/dist/voice/fake-engine.js +192 -0
  684. package/dist/voice/fake-engine.js.map +1 -0
  685. package/dist/voice/jev-engine.d.ts +17 -0
  686. package/dist/voice/jev-engine.js +110 -0
  687. package/dist/voice/jev-engine.js.map +1 -0
  688. package/dist/voice/pending-ask.d.ts +17 -0
  689. package/dist/voice/pending-ask.js +50 -0
  690. package/dist/voice/pending-ask.js.map +1 -0
  691. package/dist/voice/routes.d.ts +115 -0
  692. package/dist/voice/routes.js +91 -0
  693. package/dist/voice/routes.js.map +1 -0
  694. package/dist/voice/sentence.d.ts +21 -0
  695. package/dist/voice/sentence.js +61 -0
  696. package/dist/voice/sentence.js.map +1 -0
  697. package/dist/voice/thresholds.d.ts +28 -0
  698. package/dist/voice/thresholds.js +29 -0
  699. package/dist/voice/thresholds.js.map +1 -0
  700. package/dist/webhooks/cli.d.ts +18 -0
  701. package/dist/webhooks/cli.js +145 -0
  702. package/dist/webhooks/cli.js.map +1 -0
  703. package/dist/webhooks/prompt.d.ts +5 -0
  704. package/dist/webhooks/prompt.js +26 -0
  705. package/dist/webhooks/prompt.js.map +1 -0
  706. package/dist/workflows/active-run.d.ts +167 -0
  707. package/dist/workflows/active-run.js +21 -0
  708. package/dist/workflows/active-run.js.map +1 -0
  709. package/dist/workflows/continuation-admission.d.ts +913 -0
  710. package/dist/workflows/continuation-admission.js +265 -0
  711. package/dist/workflows/continuation-admission.js.map +1 -0
  712. package/dist/workflows/execution-account.d.ts +13 -0
  713. package/dist/workflows/execution-account.js +18 -0
  714. package/dist/workflows/execution-account.js.map +1 -0
  715. package/dist/workflows/input-acknowledgement.d.ts +152 -0
  716. package/dist/workflows/input-acknowledgement.js +38 -0
  717. package/dist/workflows/input-acknowledgement.js.map +1 -0
  718. package/dist/workflows/line-order.d.ts +5 -0
  719. package/dist/workflows/line-order.js +25 -0
  720. package/dist/workflows/line-order.js.map +1 -0
  721. package/dist/workflows/outcome-batches.d.ts +20 -0
  722. package/dist/workflows/outcome-batches.js +25 -0
  723. package/dist/workflows/outcome-batches.js.map +1 -0
  724. package/dist/workflows/question-routing.d.ts +8 -0
  725. package/dist/workflows/question-routing.js +18 -0
  726. package/dist/workflows/question-routing.js.map +1 -0
  727. package/dist/workflows/run-scheduling.d.ts +35 -0
  728. package/dist/workflows/run-scheduling.js +61 -0
  729. package/dist/workflows/run-scheduling.js.map +1 -0
  730. package/dist/workflows/run-states.d.ts +190 -0
  731. package/dist/workflows/run-states.js +295 -0
  732. package/dist/workflows/run-states.js.map +1 -0
  733. package/dist/workflows/run.d.ts +1296 -278
  734. package/dist/workflows/run.js +5730 -3265
  735. package/dist/workflows/run.js.map +1 -1
  736. package/dist/workflows/turn-end.d.ts +116 -0
  737. package/dist/workflows/turn-end.js +68 -0
  738. package/dist/workflows/turn-end.js.map +1 -0
  739. package/dist/workflows/types.d.ts +35 -130
  740. package/dist/workflows/types.js +47 -100
  741. package/dist/workflows/types.js.map +1 -1
  742. package/dist/workflows/waits.d.ts +74 -0
  743. package/dist/workflows/waits.js +197 -0
  744. package/dist/workflows/waits.js.map +1 -0
  745. package/dist/workspace/agent-accounts.d.ts +5 -38
  746. package/dist/workspace/agent-accounts.js +14 -40
  747. package/dist/workspace/agent-accounts.js.map +1 -1
  748. package/dist/workspace/agent-profiles.d.ts +40 -16
  749. package/dist/workspace/agent-profiles.js +77 -17
  750. package/dist/workspace/agent-profiles.js.map +1 -1
  751. package/dist/workspace/config.d.ts +48 -49
  752. package/dist/workspace/config.js +68 -72
  753. package/dist/workspace/config.js.map +1 -1
  754. package/dist/workspace/migrations.d.ts +3 -13
  755. package/dist/workspace/migrations.js.map +1 -1
  756. package/dist/workspace/projects-cli.d.ts +1 -1
  757. package/dist/workspace/projects-cli.js.map +1 -1
  758. package/dist/workspace/projects.d.ts +15 -18
  759. package/dist/workspace/projects.js +20 -16
  760. package/dist/workspace/projects.js.map +1 -1
  761. package/dist/workspace/semaphore.d.ts +11 -45
  762. package/dist/workspace/semaphore.js +6 -18
  763. package/dist/workspace/semaphore.js.map +1 -1
  764. package/dist/workspace/ui-state.d.ts +6 -4
  765. package/dist/workspace/ui-state.js +6 -4
  766. package/dist/workspace/ui-state.js.map +1 -1
  767. package/dist/workspace/webhooks.d.ts +40 -0
  768. package/dist/workspace/webhooks.js +146 -0
  769. package/dist/workspace/webhooks.js.map +1 -0
  770. package/dist/worktree-manifest.d.ts +165 -0
  771. package/dist/worktree-manifest.js +530 -0
  772. package/dist/worktree-manifest.js.map +1 -0
  773. package/dist/worktree-setup.js.map +1 -1
  774. package/package.json +6 -5
  775. package/scripts/benchmark-run-store-fixture.ts +7 -3
  776. package/scripts/check-pack.mjs +3 -1
  777. package/scripts/inline-contract.mjs +80 -27
  778. package/scripts/install-as-command.mjs +3 -1
  779. package/scripts/measure-transcript-facts-health.mjs +87 -0
  780. package/scripts/measure-transcript-facts-lag.mjs +68 -0
  781. package/scripts/mock-ci-tool.mjs +49 -6
  782. package/scripts/mock-claude-model-refusal.mjs +42 -0
  783. package/scripts/mock-claude.mjs +833 -591
  784. package/scripts/mock-codex-app-server.mjs +785 -418
  785. package/scripts/mock-cursor-acp.mjs +296 -125
  786. package/scripts/mock-cursor-print.mjs +256 -0
  787. package/scripts/mock-deferred-turn-start.mjs +16 -0
  788. package/scripts/mock-delivery-control.d.mts +23 -0
  789. package/scripts/mock-delivery-control.mjs +80 -0
  790. package/scripts/mock-env-record.mjs +11 -0
  791. package/scripts/mock-failure-control.d.mts +13 -0
  792. package/scripts/mock-failure-control.mjs +68 -0
  793. package/scripts/mock-multi-pr.mjs +7 -0
  794. package/scripts/mock-nudge.mjs +45 -0
  795. package/scripts/mock-omp-rpc.mjs +429 -255
  796. package/scripts/mock-opencode-serve.mjs +972 -557
  797. package/scripts/mock-pi-rpc.mjs +558 -331
  798. package/scripts/mock-runner-crash.mjs +18 -2
  799. package/scripts/mock-session-context.d.mts +5 -0
  800. package/scripts/mock-session-context.mjs +56 -0
  801. package/scripts/mock-silent-tail.mjs +10 -3
  802. package/scripts/pi-default-tools.mjs +22 -0
  803. package/scripts/pi-project-skills.mjs +59 -0
  804. package/scripts/probe-cursor-print.mjs +627 -0
  805. package/scripts/transcript-facts-fixture.mjs +53 -0
  806. package/scripts/voice-replay.ts +50 -0
  807. package/scripts/windows-workspace-identity.cs +208 -0
  808. package/scripts/windows-workspace-identity.ps1 +10 -0
  809. package/web/dist/assets/alert-dialog-DfctKUMw.js +1 -0
  810. package/web/dist/assets/button-CeupvoKG.js +41 -0
  811. package/web/dist/assets/centered-state-hDsyXrnH.js +1 -0
  812. package/web/dist/assets/cez-idle-D3aj_9lp.js +1 -0
  813. package/web/dist/assets/diff-BnIXZK3i2.js +3 -0
  814. package/web/dist/assets/diff-controls-GewpW9aF.js +1 -0
  815. package/web/dist/assets/diff-view-Byo7gpvW.js +11 -0
  816. package/web/dist/assets/dropdown-menu-COj_J5wd.js +6 -0
  817. package/web/dist/assets/eye-B8okNWXU.js +1 -0
  818. package/web/dist/assets/git-toolbar-DY0z0n4S.js +1 -0
  819. package/web/dist/assets/github-DwU6ALRK.js +1 -0
  820. package/web/dist/assets/{image-preview-FOEfqpZP.js → image-preview-C_t9q45f.js} +1 -1
  821. package/web/dist/assets/index-BOfE8pyD.js +34 -0
  822. package/web/dist/assets/index-D9HpsmdW.css +1 -0
  823. package/web/dist/assets/live-protocol-Uce2NwEX.js +64 -0
  824. package/web/dist/assets/live-worker-BizfZSoh.ts +33 -0
  825. package/web/dist/assets/markdown-BBDDg-4t.js +1 -0
  826. package/web/dist/assets/nav-row-styles-AoIhhIwf.js +1 -0
  827. package/web/dist/assets/popover-D56fhiXj.js +1 -0
  828. package/web/dist/assets/preview-pane-connected-BmVqfSU5.js +3 -0
  829. package/web/dist/assets/reference-chip-gQNk-4jc.js +1 -0
  830. package/web/dist/assets/repo-git-B5ECDUuv.js +1 -0
  831. package/web/dist/assets/{repo-git-CoKp-g9K.css → repo-git-DVXNOJDT.css} +1 -1
  832. package/web/dist/assets/run-header-C0Y1NvEM.js +2 -0
  833. package/web/dist/assets/run-header-CC7VUTg9.css +1 -0
  834. package/web/dist/assets/skills-DUeTkZ9z.js +1 -0
  835. package/web/dist/assets/status-dot-1PR0i69P.js +1 -0
  836. package/web/dist/assets/tab-link-DmWk4l5O.js +1 -0
  837. package/web/dist/assets/task-changes-DKZXuW89.js +1 -0
  838. package/web/dist/assets/task-commits-BZ6KxWdF.js +1 -0
  839. package/web/dist/assets/task-files-DlF7Apcn.js +2 -0
  840. package/web/dist/assets/task-github-item-CVPo3geZ.js +1 -0
  841. package/web/dist/assets/task-thread-DtQ6uGfQ.css +1 -0
  842. package/web/dist/assets/task-thread-mm4-ECyo.js +9 -0
  843. package/web/dist/assets/thread-state-CIw2BlhT.js +4 -0
  844. package/web/dist/assets/use-hand-to-agent-state-BCDjzHnG.js +1 -0
  845. package/web/dist/assets/use-hand-to-agent-state-C7esPJ-N.css +1 -0
  846. package/web/dist/assets/workflows-D8vYD-ql.js +7 -0
  847. package/web/dist/assets/zoomable-image-CxQSsfcn.js +1 -0
  848. package/web/dist/index.html +13 -17
  849. package/web/dist/live-worker.js +4 -0
  850. package/dist/core/tool-display.js +0 -179
  851. package/dist/core/tool-display.js.map +0 -1
  852. package/dist/runs/human-ask-summary.d.ts +0 -9
  853. package/dist/runs/human-ask-summary.js +0 -39
  854. package/dist/runs/human-ask-summary.js.map +0 -1
  855. package/dist/runs/run-search.d.ts +0 -29
  856. package/dist/runs/run-search.js +0 -63
  857. package/dist/runs/run-search.js.map +0 -1
  858. package/dist/skills-banner.d.ts +0 -22
  859. package/dist/skills-banner.js +0 -41
  860. package/dist/skills-banner.js.map +0 -1
  861. package/dist/skills-update.d.ts +0 -88
  862. package/dist/skills-update.js +0 -392
  863. package/dist/skills-update.js.map +0 -1
  864. package/dist/todos.d.ts +0 -48
  865. package/dist/todos.js +0 -201
  866. package/dist/todos.js.map +0 -1
  867. package/web/dist/assets/agent-accounts-CFOBMVHc.js +0 -1
  868. package/web/dist/assets/alert-dialog-BrpNUwMt.js +0 -1
  869. package/web/dist/assets/button-C_rd7CPp.js +0 -43
  870. package/web/dist/assets/compare-variants-D6WXhvXA.js +0 -1
  871. package/web/dist/assets/diff-DrEARXTV.js +0 -3
  872. package/web/dist/assets/diff-controls-B1-j7nFg.js +0 -1
  873. package/web/dist/assets/diff-view-DvGLjdTk.js +0 -6
  874. package/web/dist/assets/dropdown-menu-BegOZFrj.js +0 -1
  875. package/web/dist/assets/format-DpNe3S0A.js +0 -1
  876. package/web/dist/assets/git-toolbar-Cw4ljmhB.js +0 -1
  877. package/web/dist/assets/github-CfV5sqL0.js +0 -1
  878. package/web/dist/assets/index-DgWWlOLy.js +0 -13
  879. package/web/dist/assets/index-fBFnuvxu.css +0 -1
  880. package/web/dist/assets/nav-row-styles-vp2JbhuJ.js +0 -1
  881. package/web/dist/assets/popover-BohP6WTx.js +0 -1
  882. package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +0 -3
  883. package/web/dist/assets/preview-state-vuMviq6q.js +0 -1
  884. package/web/dist/assets/prompt-templates-Cubdut_C.js +0 -15
  885. package/web/dist/assets/repo-git-Cf4t5PD0.js +0 -1
  886. package/web/dist/assets/run-diff-BpkUW37Z.js +0 -3
  887. package/web/dist/assets/run-header-68WHf9wV.js +0 -1
  888. package/web/dist/assets/run-header-8GObNx6F.css +0 -1
  889. package/web/dist/assets/search-x-I9Q8pgUa.js +0 -1
  890. package/web/dist/assets/skills-DFBiAuJG.js +0 -1
  891. package/web/dist/assets/skills-DiVIgKeU.js +0 -1
  892. package/web/dist/assets/square-terminal-B6RYYieR.js +0 -1
  893. package/web/dist/assets/status-dot-CqTJHSP9.js +0 -1
  894. package/web/dist/assets/tab-link-B07Hu4QX.js +0 -1
  895. package/web/dist/assets/task-changes--lQF6EYf.js +0 -1
  896. package/web/dist/assets/task-commits-Cx0qy4DN.js +0 -1
  897. package/web/dist/assets/task-files-D50s-gKW.js +0 -2
  898. package/web/dist/assets/task-github-item-DaqmSwYT.js +0 -1
  899. package/web/dist/assets/task-thread-BPQ64lui.js +0 -7
  900. package/web/dist/assets/task-thread-zNE2Q9E-.css +0 -1
  901. package/web/dist/assets/textarea-D20O4Pw0.js +0 -1
  902. package/web/dist/assets/thread-state-DWcFMrvb.js +0 -4
  903. package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +0 -1
  904. package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +0 -1
  905. package/web/dist/assets/utils-JsL3ZsJf.js +0 -64
  906. package/web/dist/assets/workflows-jwG7uxqi.js +0 -11
  907. package/web/dist/assets/zoomable-image-BJJ64KGc.js +0 -1
@@ -1,10 +1,22 @@
1
1
  #!/usr/bin/env node
2
+ // Load only when configured: ordinary mock turns retain their event-loop ordering.
3
+ const failureControl = process.env.CEZ_MOCK_FAILURE_CONTROL === undefined ? undefined
4
+ : (await import('./mock-failure-control.mjs')).readFailureControl();
5
+ const delivery = process.env.CEZ_MOCK_DELIVERY_CONTROL === undefined ? undefined
6
+ : (await import('./mock-delivery-control.mjs')).readDeliveryControl(['reject', 'hold-ack', 'hold-response']);
2
7
  // Keep this fixture standalone: lifecycle tests copy the runner into a temp directory.
3
8
  import { spawn as watchdogSpawn } from 'node:child_process';
4
- import { writeFileSync as watchdogWritePid } from 'node:fs';
9
+ import { closeSync as watchdogCloseInput, renameSync as watchdogRename, writeFileSync as watchdogWrite } from 'node:fs';
10
+ // Publish a pid atomically (temporary sibling, then rename): a test reading it sees no
11
+ // file or a whole pid, never the empty file whose Number() is 0 (hearsay-tools/cezarion#953).
12
+ function watchdogWritePid(file, pid) { const tmp = `${file}.${process.pid}.tmp`; watchdogWrite(tmp, pid); watchdogRename(tmp, file); }
5
13
  function watchdogStall(prompt) {
6
14
  if (!prompt.includes('mock:no-progress')) return false;
7
- watchdogWritePid('watchdog.pid', String(process.pid));
15
+ if (prompt.includes('input-closed')) {
16
+ watchdogCloseInput(0);
17
+ watchdogWritePid('input-closed.pid', String(process.pid));
18
+ setTimeout(() => process.exit(0), 300);
19
+ }
8
20
  if (prompt.includes('ignore-term')) {
9
21
  process.removeAllListeners('SIGTERM');
10
22
  process.on('SIGTERM', () => {});
@@ -17,10 +29,12 @@ function watchdogStall(prompt) {
17
29
  if (prompt.includes('leftover')) {
18
30
  // hearsay-tools/cezarion#890: one child stays in the session's process group and ignores
19
31
  // SIGTERM; one leaves the group with setsid. Both exit on their own as a cleanup backstop.
20
- const leftover = (file) => `process.on('SIGTERM',()=>{}); require('fs').writeFileSync(${JSON.stringify(file)}, String(process.pid)); setTimeout(()=>{},20000)`;
32
+ const leftover = (file) => `process.on('SIGTERM',()=>{}); const fs = require('fs'); fs.writeFileSync(${JSON.stringify(`${file}.tmp`)}, String(process.pid)); fs.renameSync(${JSON.stringify(`${file}.tmp`)}, ${JSON.stringify(file)}); setTimeout(()=>{},20000)`;
21
33
  watchdogSpawn(process.execPath, ['-e', leftover('leftover-group.pid')], { stdio: 'ignore' });
22
34
  watchdogSpawn(process.execPath, ['-e', leftover('leftover-session.pid')], { stdio: 'ignore', detached: true }).unref();
23
35
  }
36
+ // Publish readiness after mode setup: another process may signal us as soon as it sees it.
37
+ watchdogWritePid('watchdog.pid', String(process.pid));
24
38
  return true;
25
39
  }
26
40
 
@@ -35,8 +49,11 @@ function watchdogStall(prompt) {
35
49
  // and handles SIGTERM itself, exiting 143 rather than dying from the signal.
36
50
  import { createInterface } from 'node:readline';
37
51
  import { appendFileSync } from 'node:fs';
52
+ // Dynamic, like every sibling import here, so a test's copy of this mock can repoint it.
53
+ const { answerNudge, silentNudge } = await import('./mock-nudge.mjs');
38
54
 
39
55
  import * as parityGateFs from 'node:fs';
56
+ import { withoutSessionContextParts } from './mock-session-context.mjs';
40
57
 
41
58
  // #401: let the test observe the actual monitoring park before releasing late wire frames.
42
59
  async function afterParityPark(prompt) {
@@ -51,6 +68,7 @@ const write = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
51
68
  // a userMessage item (echoing clientUserMessageId as clientId) and an echo reply.
52
69
  let autonomousTurn = 0;
53
70
  let activeTurnId = null;
71
+ let heldTurn = false;
54
72
  let pendingSteers = [];
55
73
  let steerEchoSerial = 0;
56
74
  const emit = (obj) => {
@@ -66,7 +84,7 @@ const emit = (obj) => {
66
84
  write({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { type: 'agentMessage', id: `item_steer_echo_${steerEchoSerial}`, text: entry.text } } });
67
85
  }
68
86
  }
69
- if (ending) { activeTurnId = null; pendingSteers = []; }
87
+ if (ending) { activeTurnId = null; pendingSteers = []; heldTurn = false; }
70
88
  write(obj);
71
89
  // #505 review: CEZ_MOCK_CODEX_LATE_START_ACK=1 answers turn/start after the turn completed.
72
90
  if (ending && lateStartAck !== undefined) { const id = lateStartAck; lateStartAck = undefined; write({ id, result: { turn: { id: 'turn_mock_1' } } }); }
@@ -74,15 +92,40 @@ const emit = (obj) => {
74
92
  let lateStartAck;
75
93
  let startSerial = 0;
76
94
  let resumeAfterAsk = false;
95
+ let pendingApprovalTurn;
77
96
  const rl = createInterface({ input: process.stdin });
78
97
  let echoSerial = 0;
79
98
  let ciWire;
99
+ // hearsay-tools/cezarion#1294: Codex 0.160.1 asks before an MCP tool call with an
100
+ // `mcpServer/elicitation/request` (codex-rs/core/src/mcp_tool_call.rs). The client
101
+ // answers `{ action, content, _meta }`; an error reply rejects the call.
102
+ const mcpToolApprovalRequest = (serverName, tool, overrides = {}) => ({
103
+ threadId: 'th_mock_1', turnId: 'turn_mock_1', serverName, mode: 'form',
104
+ _meta: { codex_approval_kind: 'mcp_tool_call', persist: ['session', 'always'], tool_description: `Mock ${tool} tool.`,
105
+ tool_params: { query: 'cezar' }, tool_params_display: [{ name: 'query', value: 'cezar', display_name: 'query' }] },
106
+ message: `Allow the ${serverName} MCP server to run tool "${tool}"?`,
107
+ requestedSchema: { type: 'object', properties: {} }, ...overrides,
108
+ });
109
+ const cezarServer = () => Object.entries(ciWire?.config ?? {}).find(([key]) => key.startsWith('mcp_servers.cezar_ci_'));
110
+ const clientReplies = new Map();
111
+ const requestClient = (id, method, params) => new Promise((resolve) => { clientReplies.set(id, resolve); emit({ id, method, params }); });
112
+ let mcpGateSerial = 0;
113
+ // Codex gates every MCP tool call by the server's (or tool's) approval mode and asks
114
+ // unless it is `approve`, whatever `approval_policy` says.
115
+ async function mcpToolGate(tool) {
116
+ const [key, server] = cezarServer() ?? [];
117
+ if (!server || server.default_tools_approval_mode === 'approve') return true;
118
+ const reply = await requestClient(`mcp-gate-${++mcpGateSerial}`, 'mcpServer/elicitation/request',
119
+ mcpToolApprovalRequest(key.slice('mcp_servers.'.length), tool));
120
+ return reply.result?.action === 'accept';
121
+ }
80
122
  // #505 steering scenarios: the one turn that accepts `turn/steer`, while it is open.
81
123
  let steerTurn = null;
82
124
  let steerSerial = 0;
83
125
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
84
- async function waitForMockRelease(fallbackMs) {
85
- const file = process.env.CEZ_MOCK_RELEASE_FILE;
126
+ async function waitForMockRelease(fallbackMs, suffix = '') {
127
+ const release = process.env.CEZ_MOCK_RELEASE_FILE;
128
+ const file = release && `${release}${suffix}`;
86
129
  if (!file) { if (fallbackMs) await sleep(fallbackMs); return; }
87
130
  const deadline = Date.now() + 15_000;
88
131
  while (!parityGateFs.existsSync(file)) {
@@ -110,7 +153,65 @@ if (ignoreEof) {
110
153
  setInterval(() => {}, 60_000);
111
154
  }
112
155
 
113
- rl.on('line', async (line) => {
156
+ // Matcher order is scenario priority; terminal replies return without an extra await.
157
+ const messageReplies = [
158
+ [msg => msg.id === 'ask-bad-1' && msg.error, replyToMalformedAsk],
159
+ [msg => msg.id === 'ask-1' && msg.result, replyToAsk],
160
+ [msg => msg.method === 'turn/steer', handleSteer],
161
+ [msg => msg.method === 'initialize', replyToInitialize],
162
+ [msg => msg.method === 'configRequirements/read', replyWithRequirements],
163
+ [msg => msg.method === 'thread/start' || msg.method === 'thread/resume', handleThread],
164
+ [msg => msg.method === 'turn/start', handleTurnStart],
165
+ ];
166
+ const openingReplies = [
167
+ [turnText => turnText.includes('mock:autonomous') || turnText.startsWith('Continue working autonomously until the task is fully complete.'), replyAutonomously],
168
+ [turnText => turnText.includes('mock:silent-tail') || turnText.includes('mock:tool-tail') || silentNudge(turnText), replyWithSilentTail],
169
+ ];
170
+ const afterSilentReplies = [
171
+ [turnText => turnText.includes('mock:hook-after-park'), replyWithParkedHook],
172
+ [turnText => turnText.includes('mock:subagent-after-park'), replyWithParkedSubagent],
173
+ [turnText => turnText.includes('mock:multi-pr-refs'), replyWithMultiplePrs],
174
+ [turnText => turnText.includes('mock:turn-messages:'), replyWithTurnMessages],
175
+ [turnText => turnText.includes('mock:ask-snapshot'), replyWithAskSnapshot],
176
+ [turnText => turnText.includes('mock:ask-prose'), replyWithAskProse],
177
+ [turnText => turnText.includes('mock:cezar-tool'), replyWithCezarTool],
178
+ [turnText => turnText.includes('mock:ci-wait'), replyWithCiWait],
179
+ [turnText => ['mock:steer-tool', 'mock:steer-late', 'mock:steer-race', 'mock:steer-strand'].find((marker) => turnText.includes(marker)), replyWithSteering],
180
+ [turnText => turnText.includes('mock:agent-echo'), replyWithEcho],
181
+ [turnText => turnText.includes('mock:split-text'), replyWithSplitText],
182
+ [turnText => turnText.includes('mock:ask-bad'), replyWithMalformedAsk],
183
+ [turnText => turnText.includes('mock:hold-done'), replyWithHeldDone],
184
+ [turnText => turnText.includes('mock:hold-ask'), replyWithHeldAsk],
185
+ [turnText => turnText.includes('mock:done'), replyWithDone],
186
+ [turnText => watchdogStall(turnText), acknowledgeWatchdog],
187
+ [turnText => turnText.includes('mock:busy-progress'), replyWithBusyProgress],
188
+ [turnText => turnText.includes('mock:hold-gated'), replyWithGatedHold],
189
+ [turnText => turnText.includes('mock:hold'), replyWithHold],
190
+ [turnText => turnText.includes('mock:stream-retry'), replyWithStreamRetry],
191
+ ];
192
+ // These scenarios follow native failure selection, as in the original line handler.
193
+ const lateReplies = [
194
+ [turnText => turnText.includes('mock:turn-failed'), replyWithFailedTurn],
195
+ [turnText => turnText.includes('mock:subagent-activity'), replyWithSubagentActivity],
196
+ [turnText => turnText.includes('mock:child-turn'), replyWithChildTurn],
197
+ [turnText => /mock:mcp-approval(?:-(own|form|null-turn))?/.exec(turnText), replyWithMcpApproval],
198
+ [turnText => turnText.includes('mock:approval'), replyWithApproval],
199
+ [turnText => process.env.MOCK_CODEX_ASK === '1' || turnText.includes('mock:native-codex-ask'), replyWithAsk],
200
+ ];
201
+
202
+ function dispatchAfterSilent(turnText, msg) {
203
+ return dispatchScenario(afterSilentReplies, turnText, msg, handleNativeFailure);
204
+ }
205
+
206
+ function dispatchScenario(replies, turnText, msg, fallback) {
207
+ for (const [matches, reply] of replies) {
208
+ const match = matches(turnText);
209
+ if (match) return reply(turnText, msg, match);
210
+ }
211
+ return fallback(turnText, msg);
212
+ }
213
+
214
+ async function handleLine(line) {
114
215
  let msg;
115
216
  try {
116
217
  msg = JSON.parse(line);
@@ -118,420 +219,686 @@ rl.on('line', async (line) => {
118
219
  return;
119
220
  }
120
221
  if (process.env.CEZ_MOCK_ARGS_FILE) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, `${JSON.stringify(msg)}\n`);
121
- if (msg.id === 'ask-bad-1' && msg.error) {
122
- // The runner rejected the malformed payload with -32602, as it must. A real
123
- // app-server carries on from there, so the turn still completes — a mock
124
- // that went silent here would make harness parity R4 pass by hanging
125
- // instead of by ending the turn.
126
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
127
- } else if (msg.id === 'ask-1' && msg.result) {
128
- const answer = msg.result.answers?.library?.answers;
129
- const freeText = msg.result.answers?.first?.answers;
130
- if (resumeAfterAsk && Array.isArray(answer) && answer[0] === 'Vitest') {
131
- resumeAfterAsk = false;
132
- const text = 'Implemented the choice.\nCEZ:DONE';
133
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_answer', text: '' } } });
134
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_answer', delta: text } });
135
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_answer', text } } });
136
- }
137
- emit((Array.isArray(answer) && answer[0] === 'Vitest') || (Array.isArray(freeText) && freeText[0] === 'Use sensible defaults')
138
- ? { method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } }
139
- : { method: 'turn/failed', params: { turn: { id: 'turn_mock_1', status: 'failed' }, error: { message: 'bad answer' } } });
140
- } else if (msg.method === 'turn/steer') {
141
- // #505 ambiguity: the transport dies with the steer unanswered.
142
- if (process.env.CEZ_MOCK_CODEX_EXIT_ON_STEER === '1') process.exit(1);
143
- const turn = steerTurn;
144
- if (!turn && activeTurnId && msg.params?.expectedTurnId === activeTurnId) {
145
- pendingSteers.push({ clientId: msg.params?.clientUserMessageId ?? null, text: msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '' });
146
- emit({ id: msg.id, result: {} });
147
- return;
148
- }
149
- if (!turn || msg.params?.expectedTurnId !== turn.id) {
150
- emit({ id: msg.id, error: { code: -32600, message: 'no active turn matching expectedTurnId' } });
151
- return;
152
- }
153
- const text = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
154
- if (turn.strand) {
155
- // The turn completes first, then the server acknowledges the steer: nothing reads it.
156
- completeSteerTurn(turn.id);
157
- emit({ id: msg.id, result: {} });
158
- return;
159
- }
160
- if (turn.race) {
161
- // The turn ends before the server processes the steer: a definitive mismatch.
162
- completeSteerTurn(turn.id);
163
- emit({ id: msg.id, error: { code: -32600, message: 'no active turn matching expectedTurnId' } });
164
- return;
165
- }
166
- turn.steered.push({ clientId: msg.params?.clientUserMessageId ?? null, text });
222
+ if (Array.isArray(msg.params?.input)) msg.params.input = withoutSessionContextParts(msg.params.input);
223
+ if (clientReplies.has(msg.id) && (msg.result || msg.error)) return resolveClientRequest(msg);
224
+ if ((msg.id === 'approval-1' || msg.id === 'approval-unsupported-1' || msg.id === 'mcp-approval-1') && pendingApprovalTurn && (msg.result || msg.error)) return replyToApproval(msg);
225
+ const route = messageReplies.find(([matches]) => matches(msg));
226
+ if (route) return route[1](msg);
227
+ }
228
+
229
+ rl.on('line', handleLine);
230
+
231
+ function replyToMalformedAsk(msg) {
232
+ // The runner rejected the malformed payload with -32602, as it must. A real
233
+ // app-server carries on from there, so the turn still completes — a mock
234
+ // that went silent here would make harness parity R4 pass by hanging
235
+ // instead of by ending the turn.
236
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
237
+ }
238
+
239
+ function replyToAsk(msg) {
240
+ const answer = msg.result.answers?.library?.answers;
241
+ const freeText = msg.result.answers?.first?.answers;
242
+ if (resumeAfterAsk && Array.isArray(answer) && answer[0] === 'Vitest') {
243
+ resumeAfterAsk = false;
244
+ const text = 'Implemented the choice.\nCEZ:DONE';
245
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_answer', text: '' } } });
246
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_answer', delta: text } });
247
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_answer', text } } });
248
+ }
249
+ completeAskedTurn(answer, freeText);
250
+ }
251
+
252
+ function completeAskedTurn(answer, freeText) {
253
+ emit((Array.isArray(answer) && answer[0] === 'Vitest') || (Array.isArray(freeText) && freeText[0] === 'Use sensible defaults')
254
+ ? { method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } }
255
+ : { method: 'turn/failed', params: { turn: { id: 'turn_mock_1', status: 'failed' }, error: { message: 'bad answer' } } });
256
+ }
257
+
258
+ function handleSteer(msg) {
259
+ // #505 ambiguity: the transport dies with the steer unanswered.
260
+ if (process.env.CEZ_MOCK_CODEX_EXIT_ON_STEER === '1') process.exit(1);
261
+ const turn = steerTurn;
262
+ // A held `mock:ack-only` turn runs the steered input as its own (hearsay-tools/cezarion#1301).
263
+ if (isHeldSteer(msg)) {
264
+ heldTurn = false;
167
265
  emit({ id: msg.id, result: {} });
168
- } else if (msg.method === 'initialize') {
169
- emit({ id: msg.id, result: { userAgent: 'mock-codex/0.0.0' } });
170
- } else if (msg.method === 'configRequirements/read') {
171
- if (process.env.MOCK_CODEX_REQUIREMENTS_ERROR) {
172
- emit({ id: msg.id, error: { code: -32601, message: 'Method not found' } });
173
- } else {
174
- emit({ id: msg.id, result: process.env.MOCK_CODEX_REQUIREMENTS_MALFORMED
175
- ? { requirements: 'invalid' } : { requirements: managedRequirements } });
176
- }
177
- } else if (msg.method === 'thread/start' || msg.method === 'thread/resume') {
178
- if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('codex', msg.params); }
179
- ciWire = msg.params;
180
- const expectedSandbox = process.env.CEZ_CODEX_NETWORK === '0' ? 'workspace-write' : 'danger-full-access';
181
- rejectedPermissionOverride = managedRequirements !== null && msg.params?.sandbox !== undefined;
182
- if (rejectedPermissionOverride) permissionWarning();
183
- const unknownRequirements = process.env.MOCK_CODEX_REQUIREMENTS_ERROR || process.env.MOCK_CODEX_REQUIREMENTS_MALFORMED;
184
- if ((!managedRequirements && !unknownRequirements && msg.params?.sandbox !== expectedSandbox) || msg.params?.approvalPolicy !== undefined) {
185
- emit({ id: msg.id, error: { code: -32602, message: `expected ${expectedSandbox} managed permissions` } });
186
- return;
187
- }
188
- if (process.argv.includes('sandbox_workspace_write.network_access=true')) {
189
- emit({ id: msg.id, error: { code: -32602, message: 'workspace-write override is obsolete in full-access mode' } });
190
- return;
191
- }
192
- // Codex retains an allowed persisted profile unless the client explicitly
193
- // selects another (thread_processor::load_and_apply_persisted_resume_metadata).
194
- if (msg.method === 'thread/resume' && process.env.MOCK_CODEX_PERSISTED_PROFILE === ':read-only' && msg.params?.permissions && msg.params.permissions !== ':read-only') {
195
- emit({ id: msg.id, error: { code: -32602, message: 'client widened a persisted read-only profile' } });
196
- return;
197
- }
198
- const sandbox = managedRequirements
199
- ? { type: 'readOnly', networkAccess: process.env.MOCK_CODEX_MANAGED_NETWORK === '1' }
200
- : msg.params?.sandbox === 'danger-full-access' ? { type: 'dangerFullAccess' }
201
- : { type: 'workspaceWrite', networkAccess: false, writableRoots: [], excludeTmpdirEnvVar: false, excludeSlashTmp: false };
202
- if (msg.method === 'thread/start') {
203
- emit({ method: 'thread/started', params: { thread: { id: 'th_mock_1' } } });
204
- emit({ id: msg.id, result: { thread: { id: 'th_mock_1' }, sandbox } });
205
- } else if (process.env.MOCK_CODEX_REJECT_RESUME === '1') {
206
- emit({ id: msg.id, error: { code: -32603, message: `no rollout found for thread id ${msg.params?.threadId ?? ''}` } });
207
- rl.close();
208
- } else {
209
- emit({ id: msg.id, result: { thread: { id: msg.params?.threadId }, sandbox } });
210
- }
211
- } else if (msg.method === 'turn/start') {
212
- const openingCrashText = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
213
- if (openingCrashText.includes('mock:crash-stderr-pre-ack')) {
214
- const { crashWithStderr } = await import('./mock-runner-crash.mjs');
215
- crashWithStderr(openingCrashText, '{"method":"turn/started","params":');
216
- return;
217
- }
218
- if (rejectedPermissionOverride) permissionWarning();
219
- activeTurnId = 'turn_mock_1';
220
- // owned-input-delivery.testkit.ts patches the exact `emit(...)` line below; keep it verbatim.
221
- if (process.env.CEZ_MOCK_CODEX_LATE_START_ACK === '1' && startSerial++ > 0) lateStartAck = msg.id; else {
222
- emit({ id: msg.id, result: { turn: { id: 'turn_mock_1' } } });
223
- }
224
- const turnText = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
225
- let silent;
226
- if (turnText.includes('mock:silent-tail') || turnText.includes('Your last turn ended without a message to the user.')) {
227
- silent = await import('./mock-silent-tail.mjs');
228
- silent.noteSilentTailPrompt(turnText);
229
- if (silent.isAckOnlyNudge(turnText)) return;
230
- if (silent.isLateNudge(turnText) || silent.isLateTurnStartNudge(turnText)) await silent.sleep(silent.LATE_REPLY_MS);
231
- }
232
- if (!turnText.includes('mock:no-progress-ack-only')) {
233
- emit({ method: 'turn/started', params: { turn: { id: 'turn_mock_1', status: 'inProgress', items: [] } } });
234
- }
235
- if (silent?.isLateTurnStartNudge(turnText)) await silent.sleep(silent.LATE_TURN_START_HOLD_MS);
236
- // The real app-server records the turn's own input as a userMessage item,
237
- // echoing clientUserMessageId as clientId (probe 0.155.1, #505).
238
- const opening = { type: 'userMessage', id: `item_user_open_${++steerEchoSerial}`, clientId: msg.params?.clientUserMessageId ?? null, content: [{ type: 'text', text: turnText }] };
239
- if (process.env.CEZ_MOCK_CODEX_NO_USER_ITEM !== '1') {
240
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
241
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
242
- }
243
- if (turnText.includes('mock:crash-stderr')) {
244
- const { crashWithStderr } = await import('./mock-runner-crash.mjs');
245
- await new Promise(resolve => setTimeout(resolve, 30));
246
- if (crashWithStderr(turnText, '{"method":"item/commandExecution/outputDelta","params":')) return;
247
- }
248
- if (turnText.includes('mock:autonomous') || turnText.startsWith('Continue working autonomously until the task is fully complete.')) {
249
- const { autonomousReply } = await import('./mock-autonomous.mjs');
250
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `autonomous-${++autonomousTurn}`, text: autonomousReply(turnText) } } });
251
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
252
- return;
253
- }
254
- if (turnText.includes('mock:silent-tail') || turnText.includes('mock:tool-tail') || turnText.includes('Your last turn ended without a message to the user.')) {
255
- const silent = await import('./mock-silent-tail.mjs');
256
- silent.noteSilentTailPrompt(turnText);
257
- if (silent.isFinalMessageNudge(turnText)) {
258
- const kind = silent.finalMessageNudgeKind();
259
- if (kind === 'silent') {
260
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'reasoning', id: 'silent-nudge-rsn', summary: silent.SILENT_TAIL_REASONING, content: silent.SILENT_TAIL_REASONING } } });
261
- } else {
262
- if (kind === 'slow-done') {
263
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-nudge-prefix', text: silent.SLOW_DONE_PREFIX } } });
264
- await silent.sleep(silent.SLOW_DONE_TAIL_MS);
265
- }
266
- const text = kind === 'standing' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE;
267
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-nudge-done', text } } });
268
- }
269
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
270
- return;
271
- }
272
- if (silent.isSilentTailScenario(turnText)) {
273
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-open', text: silent.SILENT_TAIL_OPENING } } });
274
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'silent-gh', command: ['bash', '-lc', 'gh issue create'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
275
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'reasoning', id: 'silent-rsn', summary: silent.SILENT_TAIL_REASONING, content: silent.SILENT_TAIL_REASONING } } });
276
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
277
- return;
278
- }
279
- if (silent.isToolTailScenario(turnText)) {
280
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'tool-tail-open', text: silent.TOOL_TAIL_OPENING } } });
281
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'tool-tail-git', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
282
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
283
- return;
284
- }
285
- }
286
- // Native thread attribution from collab-agent-tool-call.ndjson (#121/#401).
287
- if (turnText.includes('mock:subagent-after-park')) {
288
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'park-spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_park_child'] } } });
289
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', item: { type: 'agentMessage', id: 'park-parent', text: 'Watching the child.\nCEZ:MONITORING' } } });
290
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
291
- await afterParityPark(turnText);
292
- emit({ method: 'item/started', params: { threadId: 'th_park_child', item: { type: 'commandExecution', id: 'late-read', command: 'cat README.md', status: 'inProgress' } } });
293
- emit({ method: 'item/completed', params: { threadId: 'th_park_child', item: { type: 'commandExecution', id: 'late-read', command: 'cat README.md', status: 'completed', exitCode: 0 } } });
294
- emit({ method: 'item/completed', params: { threadId: 'th_park_child', item: { type: 'agentMessage', id: 'late-child', text: 'Post-park child update processed.' } } });
295
- return;
296
- }
297
- // #401: agentMessage completion without deltas (same item envelope as baseline).
298
- if (turnText.includes('mock:turn-messages:')) {
299
- const { turnMessages } = await import('./mock-turn-messages.mjs');
300
- for (const text of turnMessages(turnText)) emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `item_multi_${++echoSerial}`, text } } });
301
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
302
- return;
303
- }
304
- if (turnText.includes('mock:ask-snapshot')) {
305
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-snapshot', text: turnText.includes('mock:ask-snapshot-bad') ? 'CEZ:ASK {not valid json' : 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}' } } });
306
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
307
- return;
308
- }
309
- if (turnText.includes('mock:ask-prose')) {
310
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-prose', text: 'Use `CEZ:ASK {"questions":[]}` in your reply.\n> CEZ:ASK {not valid json' } } });
311
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
312
- return;
313
- }
314
- if (turnText.includes('mock:ci-wait')) {
315
- const { ciPrompt } = await import('./mock-ci-tool.mjs');
316
- const text = await ciPrompt('codex', ciWire, turnText);
317
- emit({ method: 'item/completed', params: { item: { type: 'agentMessage', id: 'ci-result', text } } });
318
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
319
- return;
320
- }
321
- const steerScenario = ['mock:steer-tool', 'mock:steer-late', 'mock:steer-race', 'mock:steer-strand'].find((marker) => turnText.includes(marker));
322
- if (steerScenario) {
323
- const turnId = 'turn_mock_1';
324
- steerTurn = { id: turnId, steered: [], race: steerScenario === 'mock:steer-race', strand: steerScenario === 'mock:steer-strand' };
325
- const agent = (text) => emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { type: 'agentMessage', id: `item_steer_${++steerSerial}`, text } } });
326
- if (steerScenario === 'mock:steer-late') {
327
- agent(`late window: final message already sent${turnText.includes('mock:done-late') ? '\n\nCEZ:DONE' : ''}`);
328
- await sleep(300);
329
- completeSteerTurn(turnId); // steers accepted in this window are never read
330
- return;
331
- }
332
- const exec = { type: 'commandExecution', id: 'exec_steer', command: 'wait', status: 'inProgress' };
333
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item: exec } });
334
- await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
335
- if (!steerTurn) return; // a race already ended it
336
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { ...exec, status: 'completed' } } });
337
- const steered = steerTurn.steered;
338
- for (const entry of steered) {
339
- const item = { type: 'userMessage', id: `item_user_${++steerSerial}`, clientId: entry.clientId, content: [{ type: 'text', text: entry.text }] };
340
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item } });
341
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item } });
342
- }
343
- agent(['steer tool done', ...steered.map((entry) => `saw: ${entry.text}`)].join('\n'));
344
- completeSteerTurn(turnId);
345
- return;
346
- }
347
- if (turnText.includes('mock:agent-echo')) {
348
- // Same documented agentMessage/turn completion frames as the baseline below.
349
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `item_echo_${++echoSerial}`, text: turnText } } });
350
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
351
- return;
352
- }
353
- if (turnText.includes('mock:split-text')) {
354
- // Deltas that split the marker itself, then the authoritative snapshot on
355
- // `item/completed` — codex's real streaming shape. A runner emitting one
356
- // v1 `text` per delta tears `CEZ:MONITORING` into `CEZ:` + `MONITORING`,
357
- // which is #2 on codex's wire (harness parity S8).
358
- const full = 'parity split text\n\nCEZ:MONITORING';
359
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_sp1', text: '' } } });
360
- for (const delta of ['parity split text\n\n', 'CEZ:', 'MONITORING']) {
361
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_sp1', delta } });
362
- }
363
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_sp1', text: full } } });
364
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
365
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
366
- return;
367
- }
368
- if (turnText.includes('mock:ask-bad')) {
369
- // `questions` is not an array of question objects, so `codexAskQuestions`
370
- // must reject it: no card renders, and the turn still ends once the
371
- // rejection lands above (harness parity R4).
372
- emit({ id: 'ask-bad-1', method: 'item/tool/requestUserInput', params: {
373
- threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ask_bad', autoResolutionMs: null,
374
- questions: 'not-an-array',
375
- } });
376
- return;
377
- }
378
- if (turnText.includes('mock:hold-done')) {
379
- await waitForMockRelease(400);
380
- const full = 'parity hold-done: content after the pause\n\nCEZ:DONE';
381
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hd1', text: '' } } });
382
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_hd1', delta: full } });
383
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hd1', text: full } } });
384
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
385
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
386
- return;
387
- }
388
- if (turnText.includes('mock:hold-ask')) {
389
- await waitForMockRelease(400);
390
- const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}';
391
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_ha1', text: '' } } });
392
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ha1', delta: ask } });
393
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_ha1', text: ask } } });
394
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
395
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
396
- return;
397
- }
398
- if (turnText.includes('mock:done')) {
399
- // A turn that DECLARES the task complete, so the run reaches cezar's
400
- // review gate instead of parking for the user. A markerless turn-end
401
- // correctly parks as `waiting`, which is why harness parity R1 needs
402
- // this scenario rather than the baseline one.
403
- const full = 'parity done: the task is complete\n\nCEZ:DONE';
404
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_d1', text: '' } } });
405
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_d1', delta: full } });
406
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_d1', text: full } } });
407
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
408
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
409
- return;
410
- }
411
- if (watchdogStall(turnText)) return;
412
- if (turnText.includes('mock:busy-progress')) {
413
- for (let i = 0; i < 24; i++) {
414
- emit({ method: 'item/commandExecution/outputDelta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'busy', delta: 'working\n' } });
415
- await new Promise(r => setTimeout(r, 100));
416
- }
417
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
418
- return;
419
- }
420
- if (turnText.includes('mock:hold-gated')) {
421
- await waitForMockRelease(250);
422
- const gated = 'parity hold-gated: content after the pause';
423
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hg1', text: '' } } });
424
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_hg1', delta: gated } });
425
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hg1', text: gated } } });
426
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
427
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
428
- return;
429
- }
430
- if (turnText.includes('mock:hold')) {
431
- // The `turn/start` response and `turn/started` above are the ack. Holding
432
- // the content AND `turn/completed` behind it is what makes harness parity
433
- // S2 meaningful: a runner deriving turn-end from the ack reports it before
434
- // this content ever arrives (the #4 failure mode on codex's wire).
435
- setTimeout(() => {
436
- const held = 'parity hold: content after the pause';
437
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_h1', text: '' } } });
438
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_h1', delta: held } });
439
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_h1', text: held } } });
440
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
441
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
442
- }, 250);
443
- return;
444
- }
445
- if (turnText.includes('mock:stream-retry')) {
446
- // Error payloads from the real 0.155.1 reconnect probe; see stream-retry.md.
447
- const frames = [{"method": "error", "params": {"error": {"message": "Reconnecting... 2/5", "codexErrorInfo": {"responseStreamDisconnected": {"httpStatusCode": null}}, "additionalDetails": "stream disconnected before completion: invalid peer certificate: UnknownIssuer"}, "willRetry": true}}, {"method": "error", "params": {"error": {"message": "Reconnecting... waiting for network", "additionalDetails": "Connection failed: error sending request"}, "willRetry": true}}];
448
- const send = frame => emit({ ...frame, params: { ...frame.params, threadId: 'th_mock_1', turnId: 'turn_mock_1' } });
449
- send(frames[0]);
450
- if (turnText.includes('recover')) {
451
- await sleep(20);
452
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'recovered_message', text: 'Recovered after reconnect.' } } });
453
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
454
- } else {
455
- send(frames[1]);
456
- const retries = setInterval(() => send(frames[1]), 10_000);
457
- rl.once('close', () => clearInterval(retries));
458
- }
459
- return;
460
- }
461
- if (turnText.includes('mock:provider-error') || turnText.includes('mock:empty-success')) {
462
- // Derived from the 0.147 rollout and upstream envelope; see the source
463
- // core/__fixtures__/codex/provider-error.md and its independent golden NDJSON.
464
- const completion = {"method": "turn/completed", "params": {"threadId": "th_mock_1", "turn": {"id": "turn_mock_1", "items": [], "itemsView": "notLoaded", "status": "failed", "error": {"message": "{\"type\":\"error\",\"status\":400,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"The 'gpt-6-astra' model requires a newer version of Codex. Please upgrade to the latest app or CLI and try again.\"}}", "codexErrorInfo": "other", "additionalDetails": null}, "startedAt": 1788632328, "completedAt": 1788632331, "durationMs": 3502}}};
465
- if (turnText.includes('mock:empty-success')) {
466
- completion.params.turn.status = 'completed';
467
- completion.params.turn.error = null;
468
- }
469
- emit(completion);
470
- return;
471
- }
472
- if (turnText.includes('mock:turn-failed')) {
473
- emit({ method: 'turn/failed', params: {
474
- turn: { id: 'turn_mock_1', status: 'failed' },
475
- error: { message: turnText.includes('mock:legacy-interrupt') ? 'Turn interrupted by user' : 'model unavailable' },
476
- } });
477
- return;
478
- }
479
- if (turnText.includes('mock:subagent-activity')) {
480
- emit({ method: 'item/started', params: { item: { type: 'subAgentActivity', id: 'activity_1', kind: 'started', agentThreadId: 'th_child', agentPath: '/root/scope_review' } } });
481
- emit({ method: 'item/completed', params: { item: { type: 'subAgentActivity', id: 'activity_1', kind: 'started', agentThreadId: 'th_child', agentPath: '/root/scope_review' } } });
482
- emit({ method: 'item/started', params: { item: { type: 'collabAgentToolCall', id: 'wait_1', tool: 'wait', status: 'inProgress', receiverThreadIds: [] } } });
483
- emit({ method: 'item/completed', params: { item: { type: 'collabAgentToolCall', id: 'wait_1', tool: 'wait', status: 'completed', receiverThreadIds: [] } } });
484
- emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
485
- return;
486
- }
487
- if (turnText.includes('mock:child-turn')) {
488
- // A spawned sub-agent runs in its OWN child thread that emits a full turn
489
- // lifecycle over the shared connection. Its turn/completed must not end the
490
- // parent turn (#600): the parent is still working after the child finishes.
491
- // Collaboration attribution from collab-agent-tool-call.ndjson; late text reproduces #149.
492
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'item_spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_child'] } } });
493
- emit({ method: 'turn/started', params: { threadId: 'th_child', turn: { id: 'turn_child', status: 'inProgress', items: [] } } });
494
- emit({ method: 'item/started', params: { threadId: 'th_child', turnId: 'turn_child', item: { type: 'commandExecution', id: 'item_child', command: ['rg', 'requestUserInput'], cwd: '/repo', status: 'inProgress' } } });
495
- emit({ method: 'turn/completed', params: { threadId: 'th_child', turn: { id: 'turn_child', status: 'completed' } } });
496
- // Parent keeps streaming after the child's turn ended.
497
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_p1', text: '' } } });
498
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_p1', delta: 'Still working after the sub-agent.\nCEZ:MONITORING' } });
499
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_p1', text: 'Still working after the sub-agent.\nCEZ:MONITORING' } } });
500
- emit({ method: 'item/completed', params: { threadId: 'th_child', turnId: 'turn_child', item: { type: 'agentMessage', id: 'item_child_text', text: 'Child review finished.' } } });
501
- // No completion for this second child item: parent turn-end must not flush it into v1.
502
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_child', turnId: 'turn_child', itemId: 'item_child_partial', delta: 'Child review still streaming.' } });
503
- emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
504
- return;
505
- }
506
- if (process.env.MOCK_CODEX_ASK === '1' || turnText.includes('mock:native-codex-ask')) {
507
- resumeAfterAsk = turnText.includes('mock:resume-done');
508
- const questions = turnText.includes('multi free text')
509
- ? [{ id: 'first', header: 'First', question: 'First choice?', isOther: true, isSecret: false,
510
- options: [{ label: 'A', description: 'Option A.' }, { label: 'B', description: 'Option B.' }] },
511
- { id: 'second', header: 'Second', question: 'Second choice?', isOther: true, isSecret: false,
512
- options: [{ label: 'C', description: 'Option C.' }, { label: 'D', description: 'Option D.' }] }]
513
- : [{ id: 'library', header: 'Library', question: 'Which test library?', isOther: true,
514
- isSecret: false, options: [{ label: 'Vitest', description: 'Use the existing test runner.' },
515
- { label: 'Node test', description: 'Use node:test.' }] }];
516
- emit({ id: 'ask-1', method: 'item/tool/requestUserInput', params: {
517
- threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ask_1', autoResolutionMs: null,
518
- questions,
519
- } });
520
- return;
521
- }
522
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m1', text: '' } } });
523
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_m1', delta: 'Checking the working tree.' } });
524
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m1', text: 'Checking the working tree.' } } });
525
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'item_c1', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'inProgress' } } });
526
- emit({ method: 'item/commandExecution/outputDelta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_c1', delta: ' M src/example.ts\n' } });
527
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'item_c1', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
528
- emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m2', text: '' } } });
529
- emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_m2', delta: 'Done with the first pass.' } });
530
- emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m2', text: 'Done with the first pass.' } } });
531
- emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 1500, inputTokens: 1200, outputTokens: 300 }, last: { totalTokens: 1500, inputTokens: 1200, outputTokens: 300 } } } });
266
+ rl.emit('line', JSON.stringify({ ...msg, method: 'turn/start', mockHeldSteer: true }));
267
+ return;
268
+ }
269
+ if (canQueueSteer(msg, turn)) {
270
+ queueSteer(msg);
271
+ emit({ id: msg.id, result: {} });
272
+ return;
273
+ }
274
+ if (!turn || msg.params?.expectedTurnId !== turn.id) {
275
+ emit({ id: msg.id, error: { code: -32600, message: 'no active turn matching expectedTurnId' } });
276
+ return;
277
+ }
278
+ const text = steerText(msg);
279
+ if (turn.strand) {
280
+ // The turn completes first, then the server acknowledges the steer: nothing reads it.
281
+ completeSteerTurn(turn.id);
282
+ emit({ id: msg.id, result: {} });
283
+ return;
284
+ }
285
+ if (turn.race) {
286
+ // The turn ends before the server processes the steer: a definitive mismatch.
287
+ completeSteerTurn(turn.id);
288
+ emit({ id: msg.id, error: { code: -32600, message: 'no active turn matching expectedTurnId' } });
289
+ return;
290
+ }
291
+ turn.steered.push({ clientId: msg.params?.clientUserMessageId ?? null, text });
292
+ emit({ id: msg.id, result: {} });
293
+ }
294
+
295
+ function isHeldSteer(msg) {
296
+ return heldTurn && activeTurnId && msg.params?.expectedTurnId === activeTurnId;
297
+ }
298
+
299
+ function canQueueSteer(msg, turn) {
300
+ return !turn && activeTurnId && msg.params?.expectedTurnId === activeTurnId;
301
+ }
302
+
303
+ function queueSteer(msg) {
304
+ pendingSteers.push({ clientId: msg.params?.clientUserMessageId ?? null, text: queuedSteerText(msg) });
305
+ }
306
+
307
+ function queuedSteerText(msg) {
308
+ return msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
309
+ }
310
+
311
+ function steerText(msg) {
312
+ return msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
313
+ }
314
+
315
+ function replyToInitialize(msg) {
316
+ emit({ id: msg.id, result: { userAgent: 'mock-codex/0.0.0' } });
317
+ }
318
+
319
+ function replyWithRequirements(msg) {
320
+ if (process.env.MOCK_CODEX_REQUIREMENTS_ERROR) {
321
+ emit({ id: msg.id, error: { code: -32601, message: 'Method not found' } });
322
+ } else {
323
+ emit({ id: msg.id, result: process.env.MOCK_CODEX_REQUIREMENTS_MALFORMED
324
+ ? { requirements: 'invalid' } : { requirements: managedRequirements } });
325
+ }
326
+ }
327
+
328
+ async function handleThread(msg) {
329
+ if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('codex', msg.params); }
330
+ ciWire = msg.params;
331
+ const expectedSandbox = process.env.CEZ_CODEX_NETWORK === '0' ? 'workspace-write' : 'danger-full-access';
332
+ rejectedPermissionOverride = managedRequirements !== null && msg.params?.sandbox !== undefined;
333
+ if (rejectedPermissionOverride) permissionWarning();
334
+ if (rejectThreadPermissions(msg, expectedSandbox)) return;
335
+ if (process.argv.includes('sandbox_workspace_write.network_access=true')) {
336
+ emit({ id: msg.id, error: { code: -32602, message: 'workspace-write override is obsolete in full-access mode' } });
337
+ return;
338
+ }
339
+ // Codex retains an allowed persisted profile unless the client explicitly
340
+ // selects another (thread_processor::load_and_apply_persisted_resume_metadata).
341
+ if (msg.method === 'thread/resume' && process.env.MOCK_CODEX_PERSISTED_PROFILE === ':read-only' && msg.params?.permissions && msg.params.permissions !== ':read-only') {
342
+ emit({ id: msg.id, error: { code: -32602, message: 'client widened a persisted read-only profile' } });
343
+ return;
344
+ }
345
+ const sandbox = threadSandbox(msg);
346
+ if (msg.method === 'thread/start') {
347
+ emit({ method: 'thread/started', params: { thread: { id: 'th_mock_1' } } });
348
+ emit({ id: msg.id, result: { thread: { id: 'th_mock_1' }, sandbox } });
349
+ } else if (process.env.MOCK_CODEX_REJECT_RESUME === '1') {
350
+ emit({ id: msg.id, error: { code: -32603, message: `no rollout found for thread id ${msg.params?.threadId ?? ''}` } });
351
+ rl.close();
352
+ } else {
353
+ emit({ id: msg.id, result: { thread: { id: msg.params?.threadId }, sandbox } });
354
+ }
355
+ }
356
+
357
+ function rejectThreadPermissions(msg, expectedSandbox) {
358
+ const unknownRequirements = process.env.MOCK_CODEX_REQUIREMENTS_ERROR || process.env.MOCK_CODEX_REQUIREMENTS_MALFORMED;
359
+ if ((!managedRequirements && !unknownRequirements && msg.params?.sandbox !== expectedSandbox) || msg.params?.approvalPolicy !== undefined) {
360
+ emit({ id: msg.id, error: { code: -32602, message: `expected ${expectedSandbox} managed permissions` } });
361
+ return true;
362
+ }
363
+ return false;
364
+ }
365
+
366
+ function threadSandbox(msg) {
367
+ return managedRequirements
368
+ ? { type: 'readOnly', networkAccess: process.env.MOCK_CODEX_MANAGED_NETWORK === '1' }
369
+ : msg.params?.sandbox === 'danger-full-access' ? { type: 'dangerFullAccess' }
370
+ : { type: 'workspaceWrite', networkAccess: false, writableRoots: [], excludeTmpdirEnvVar: false, excludeSlashTmp: false };
371
+ }
372
+
373
+ async function replyAutonomously(turnText, msg) {
374
+ const { autonomousReply } = await import('./mock-autonomous.mjs');
375
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `autonomous-${++autonomousTurn}`, text: autonomousReply(turnText) } } });
376
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
377
+ return;
378
+ }
379
+
380
+ async function replyWithSilentTail(turnText, msg) {
381
+ const silent = await import('./mock-silent-tail.mjs');
382
+ silent.noteSilentTailPrompt(turnText);
383
+ if (silent.isFinalMessageNudge(turnText)) {
384
+ return replyWithFinalMessageNudge(silent);
385
+ }
386
+ if (silent.isSilentTailScenario(turnText)) {
387
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-open', text: silent.SILENT_TAIL_OPENING } } });
388
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'silent-gh', command: ['bash', '-lc', 'gh issue create'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
389
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'reasoning', id: 'silent-rsn', summary: silent.SILENT_TAIL_REASONING, content: silent.SILENT_TAIL_REASONING } } });
390
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
391
+ return;
392
+ }
393
+ if (silent.isToolTailScenario(turnText)) {
394
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'tool-tail-open', text: silent.TOOL_TAIL_OPENING } } });
395
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'tool-tail-git', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
396
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
397
+ return;
398
+ }
399
+ return dispatchScenario(afterSilentReplies, turnText, msg, handleNativeFailure);
400
+ }
401
+
402
+ async function replyWithFinalMessageNudge(silent) {
403
+ const kind = silent.finalMessageNudgeKind();
404
+ if (kind === 'silent') {
405
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'reasoning', id: 'silent-nudge-rsn', summary: silent.SILENT_TAIL_REASONING, content: silent.SILENT_TAIL_REASONING } } });
406
+ } else {
407
+ if (kind === 'slow-done') {
408
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-nudge-prefix', text: silent.SLOW_DONE_PREFIX } } });
409
+ await silent.sleep(silent.SLOW_DONE_TAIL_MS);
410
+ }
411
+ const text = kind === 'standing' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE;
412
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-nudge-done', text } } });
413
+ }
414
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
415
+ return;
416
+ }
417
+
418
+ // #961: `hook/completed` (0.160.1 schema) — one mid-turn, one after the run parked.
419
+ async function replyWithParkedHook(turnText, msg) {
420
+ const hook = (id, eventName, text) => ({ method: 'hook/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', run: {
421
+ id, eventName, handlerType: 'command', executionMode: 'sync', scope: 'turn', sourcePath: '/mock/hooks.json',
422
+ displayOrder: 0, startedAt: 1, status: 'completed', entries: [{ kind: 'context', text }],
423
+ } } });
424
+ emit(hook('hook-mid', 'postToolUse', 'Mid-turn hook context.'));
425
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', item: { type: 'agentMessage', id: 'hook-parent', text: 'Watching.\nCEZ:MONITORING' } } });
426
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
427
+ await afterParityPark(turnText);
428
+ emit(hook('hook-late', 'stop', 'Late hook processed.'));
429
+ return;
430
+ }
431
+
432
+ // Native thread attribution from collab-agent-tool-call.ndjson (#121/#401).
433
+ async function replyWithParkedSubagent(turnText, msg) {
434
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'park-spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_park_child'] } } });
435
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', item: { type: 'agentMessage', id: 'park-parent', text: 'Watching the child.\nCEZ:MONITORING' } } });
436
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
437
+ await afterParityPark(turnText);
438
+ emit({ method: 'item/started', params: { threadId: 'th_park_child', item: { type: 'commandExecution', id: 'late-read', command: 'cat README.md', status: 'inProgress' } } });
439
+ emit({ method: 'item/completed', params: { threadId: 'th_park_child', item: { type: 'commandExecution', id: 'late-read', command: 'cat README.md', status: 'completed', exitCode: 0 } } });
440
+ emit({ method: 'item/completed', params: { threadId: 'th_park_child', item: { type: 'agentMessage', id: 'late-child', text: 'Post-park child update processed.' } } });
441
+ return;
442
+ }
443
+
444
+ // #401: agentMessage completion without deltas (same item envelope as baseline).
445
+ async function replyWithMultiplePrs(turnText, msg) {
446
+ const { multiPrText, unrelatedPr } = await import('./mock-multi-pr.mjs');
447
+ const text = multiPrText(turnText);
448
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', item: { type: 'reasoning', id: 'multi-thinking', summary: [unrelatedPr] } } });
449
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', item: { type: 'commandExecution', id: 'multi-tool', command: 'cat other.txt', aggregatedOutput: unrelatedPr, status: 'completed', exitCode: 0 } } });
450
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'multi-spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_foreign_child'] } } });
451
+ emit({ method: 'item/completed', params: { threadId: 'th_foreign_child', item: { type: 'agentMessage', id: 'multi-child', text: unrelatedPr } } });
452
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'multi-message', text: '' } } });
453
+ for (const delta of [text.slice(0, 5), text.slice(5)]) emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'multi-message', delta } });
454
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'multi-message', text } } });
455
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
456
+ return;
457
+ }
458
+
459
+ async function replyWithTurnMessages(turnText, msg) {
460
+ const { turnMessages } = await import('./mock-turn-messages.mjs');
461
+ // `mock:channel-missing` / `mock:channel-refresh` → the second message's completion frame
462
+ // diverges between channels: no snapshot text (delta only), or a refresh of the first
463
+ // message's id with both texts (monitoring-channel-order.test.ts).
464
+ const missing = turnText.includes('mock:channel-missing'), refresh = turnText.includes('mock:channel-refresh');
465
+ if (missing || refresh) {
466
+ emitChannelMessages(turnMessages(turnText), missing, refresh);
467
+ } else {
468
+ for (const text of turnMessages(turnText)) emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `item_multi_${++echoSerial}`, text } } });
469
+ }
470
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
471
+ return;
472
+ }
473
+
474
+ function emitChannelMessages(messages, missing, refresh) {
475
+ let firstId;
476
+ for (let n = 0; n < messages.length; n++) {
477
+ const text = messages[n], id = n === 1 && refresh ? firstId : `channel_${++echoSerial}`;
478
+ if (n === 0) firstId = id;
479
+ emitChannelMessage(messages, n, missing, refresh, id, text);
480
+ }
481
+ }
482
+
483
+ function emitChannelMessage(messages, n, missing, refresh, id, text) {
484
+ if (n === 1 && missing) {
485
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id } } });
486
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: id, delta: text } });
487
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id } } });
488
+ } else emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id, text: n === 1 && refresh ? `${messages[0]}\n${text}` : text } } });
489
+ }
490
+
491
+ function replyWithAskSnapshot(turnText, msg) {
492
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-snapshot', text: turnText.includes('mock:ask-snapshot-bad') ? 'CEZ:ASK {not valid json' : 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}' } } });
493
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
494
+ return;
495
+ }
496
+
497
+ function replyWithAskProse(turnText, msg) {
498
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-prose', text: 'Use `CEZ:ASK {"questions":[]}` in your reply.\n> CEZ:ASK {not valid json' } } });
499
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
500
+ return;
501
+ }
502
+
503
+ async function replyWithCezarTool(turnText, msg) {
504
+ const { toolPrompt } = await import('./mock-ci-tool.mjs');
505
+ const tool = /mock:cezar-tool\s+([a-z_]+)/.exec(turnText)?.[1] ?? 'cezar_wait';
506
+ const text = await mcpToolGate(tool) ? await toolPrompt('codex', ciWire, turnText) : 'user rejected MCP tool call';
507
+ emit({ method: 'item/completed', params: { item: { type: 'agentMessage', id: 'tool-result', text } } });
508
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
509
+ return;
510
+ }
511
+
512
+ async function replyWithCiWait(turnText, msg) {
513
+ const { ciPrompt } = await import('./mock-ci-tool.mjs');
514
+ const text = await mcpToolGate('cezar_wait') ? await ciPrompt('codex', ciWire, turnText) : 'user rejected MCP tool call';
515
+ emit({ method: 'item/completed', params: { item: { type: 'agentMessage', id: 'ci-result', text } } });
516
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
517
+ return;
518
+ }
519
+
520
+ async function replyWithSteering(turnText, msg, steerScenario) {
521
+ const turnId = 'turn_mock_1';
522
+ steerTurn = { id: turnId, steered: [], race: steerScenario === 'mock:steer-race', strand: steerScenario === 'mock:steer-strand' };
523
+ const agent = (text) => emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { type: 'agentMessage', id: `item_steer_${++steerSerial}`, text } } });
524
+ if (steerScenario === 'mock:steer-late') {
525
+ agent(`late window: final message already sent${turnText.includes('mock:done-late') ? '\n\nCEZ:DONE' : ''}`);
526
+ await sleep(300);
527
+ completeSteerTurn(turnId); // steers accepted in this window are never read
528
+ return;
529
+ }
530
+ const exec = { type: 'commandExecution', id: 'exec_steer', command: 'wait', status: 'inProgress' };
531
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item: exec } });
532
+ await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
533
+ if (!steerTurn) return; // a race already ended it
534
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { ...exec, status: 'completed' } } });
535
+ const steered = steerTurn.steered;
536
+ for (const entry of steered) {
537
+ const item = { type: 'userMessage', id: `item_user_${++steerSerial}`, clientId: entry.clientId, content: [{ type: 'text', text: entry.text }] };
538
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item } });
539
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item } });
540
+ }
541
+ agent(['steer tool done', ...steered.map((entry) => `saw: ${entry.text}`)].join('\n'));
542
+ completeSteerTurn(turnId);
543
+ return;
544
+ }
545
+
546
+ function replyWithEcho(turnText, msg) {
547
+ // Same documented agentMessage/turn completion frames as the baseline below.
548
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `item_echo_${++echoSerial}`, text: turnText } } });
549
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
550
+ return;
551
+ }
552
+
553
+ function replyWithSplitText(turnText, msg) {
554
+ // Deltas that split the marker itself, then the authoritative snapshot on
555
+ // `item/completed` — codex's real streaming shape. A runner emitting one
556
+ // v1 `text` per delta tears `CEZ:MONITORING` into `CEZ:` + `MONITORING`,
557
+ // which is #2 on codex's wire (harness parity S8).
558
+ const full = 'parity split text\n\nCEZ:MONITORING';
559
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_sp1', text: '' } } });
560
+ for (const delta of ['parity split text\n\n', 'CEZ:', 'MONITORING']) {
561
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_sp1', delta } });
562
+ }
563
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_sp1', text: full } } });
564
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
565
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
566
+ return;
567
+ }
568
+
569
+ function replyWithMalformedAsk(turnText, msg) {
570
+ // `questions` is not an array of question objects, so `codexAskQuestions`
571
+ // must reject it: no card renders, and the turn still ends once the
572
+ // rejection lands above (harness parity R4).
573
+ emit({ id: 'ask-bad-1', method: 'item/tool/requestUserInput', params: {
574
+ threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ask_bad', autoResolutionMs: null,
575
+ questions: 'not-an-array',
576
+ } });
577
+ return;
578
+ }
579
+
580
+ async function replyWithHeldDone(turnText, msg) {
581
+ await waitForMockRelease(400);
582
+ const full = 'parity hold-done: content after the pause\n\nCEZ:DONE';
583
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hd1', text: '' } } });
584
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_hd1', delta: full } });
585
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hd1', text: full } } });
586
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
587
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
588
+ return;
589
+ }
590
+
591
+ async function replyWithHeldAsk(turnText, msg) {
592
+ await waitForMockRelease(400);
593
+ const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}';
594
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_ha1', text: '' } } });
595
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ha1', delta: ask } });
596
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_ha1', text: ask } } });
597
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
598
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
599
+ return;
600
+ }
601
+
602
+ function replyWithDone(turnText, msg) {
603
+ // A turn that DECLARES the task complete, so the run reaches cezar's
604
+ // review gate instead of parking for the user. A markerless turn-end
605
+ // correctly parks as `waiting`, which is why harness parity R1 needs
606
+ // this scenario rather than the baseline one.
607
+ const full = 'parity done: the task is complete\n\nCEZ:DONE';
608
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_d1', text: '' } } });
609
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_d1', delta: full } });
610
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_d1', text: full } } });
611
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
612
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
613
+ return;
614
+ }
615
+
616
+ function acknowledgeWatchdog() {}
617
+
618
+ async function replyWithBusyProgress(turnText, msg) {
619
+ for (let i = 0; i < 24; i++) {
620
+ emit({ method: 'item/commandExecution/outputDelta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'busy', delta: 'working\n' } });
621
+ await new Promise(r => setTimeout(r, 100));
622
+ }
623
+ const busy = 'busy complete\n\nCEZ:DONE';
624
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_busy', text: '' } } });
625
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_busy', delta: busy } });
626
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_busy', text: busy } } });
627
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
628
+ return;
629
+ }
630
+
631
+ async function replyWithGatedHold(turnText, msg) {
632
+ await waitForMockRelease(250);
633
+ const gated = 'parity hold-gated: content after the pause';
634
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hg1', text: '' } } });
635
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_hg1', delta: gated } });
636
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hg1', text: gated } } });
637
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
638
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
639
+ return;
640
+ }
641
+
642
+ function replyWithHold(turnText, msg) {
643
+ // The `turn/start` response and `turn/started` above are the ack. Holding
644
+ // the content AND `turn/completed` behind it is what makes harness parity
645
+ // S2 meaningful: a runner deriving turn-end from the ack reports it before
646
+ // this content ever arrives (the #4 failure mode on codex's wire).
647
+ setTimeout(() => {
648
+ const held = 'parity hold: content after the pause';
649
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_h1', text: '' } } });
650
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_h1', delta: held } });
651
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_h1', text: held } } });
652
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
532
653
  emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
654
+ }, 250);
655
+ return;
656
+ }
657
+
658
+ async function replyWithStreamRetry(turnText, msg) {
659
+ // Error payloads from the real 0.155.1 reconnect probe; see stream-retry.md.
660
+ const frames = [{"method": "error", "params": {"error": {"message": "Reconnecting... 2/5", "codexErrorInfo": {"responseStreamDisconnected": {"httpStatusCode": null}}, "additionalDetails": "stream disconnected before completion: invalid peer certificate: UnknownIssuer"}, "willRetry": true}}, {"method": "error", "params": {"error": {"message": "Reconnecting... waiting for network", "additionalDetails": "Connection failed: error sending request"}, "willRetry": true}}];
661
+ const send = frame => emit({ ...frame, params: { ...frame.params, threadId: 'th_mock_1', turnId: 'turn_mock_1' } });
662
+ send(frames[0]);
663
+ if (turnText.includes('recover')) {
664
+ await sleep(20);
665
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'recovered_message', text: 'Recovered after reconnect.' } } });
666
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
667
+ } else {
668
+ send(frames[1]);
669
+ const retries = setInterval(() => send(frames[1]), 10_000);
670
+ rl.once('close', () => clearInterval(retries));
533
671
  }
534
- });
672
+ return;
673
+ }
674
+
675
+ function replyWithFailedTurn(turnText, msg) {
676
+ emit({ method: 'turn/failed', params: {
677
+ turn: { id: 'turn_mock_1', status: 'failed' },
678
+ error: { message: turnText.includes('mock:legacy-interrupt') ? 'Turn interrupted by user' : 'model unavailable' },
679
+ } });
680
+ return;
681
+ }
682
+
683
+ function replyWithSubagentActivity(turnText, msg) {
684
+ emit({ method: 'item/started', params: { item: { type: 'subAgentActivity', id: 'activity_1', kind: 'started', agentThreadId: 'th_child', agentPath: '/root/scope_review' } } });
685
+ emit({ method: 'item/completed', params: { item: { type: 'subAgentActivity', id: 'activity_1', kind: 'started', agentThreadId: 'th_child', agentPath: '/root/scope_review' } } });
686
+ emit({ method: 'item/started', params: { item: { type: 'collabAgentToolCall', id: 'wait_1', tool: 'wait', status: 'inProgress', receiverThreadIds: [] } } });
687
+ emit({ method: 'item/completed', params: { item: { type: 'collabAgentToolCall', id: 'wait_1', tool: 'wait', status: 'completed', receiverThreadIds: [] } } });
688
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
689
+ return;
690
+ }
691
+
692
+ function replyWithChildTurn(turnText, msg) {
693
+ // A spawned sub-agent runs in its OWN child thread that emits a full turn
694
+ // lifecycle over the shared connection. Its turn/completed must not end the
695
+ // parent turn (#600): the parent is still working after the child finishes.
696
+ // Collaboration attribution from collab-agent-tool-call.ndjson; late text reproduces #149.
697
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'item_spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_child'] } } });
698
+ emit({ method: 'turn/started', params: { threadId: 'th_child', turn: { id: 'turn_child', status: 'inProgress', items: [] } } });
699
+ emit({ method: 'item/started', params: { threadId: 'th_child', turnId: 'turn_child', item: { type: 'commandExecution', id: 'item_child', command: ['rg', 'requestUserInput'], cwd: '/repo', status: 'inProgress' } } });
700
+ emit({ method: 'turn/completed', params: { threadId: 'th_child', turn: { id: 'turn_child', status: 'completed' } } });
701
+ // Parent keeps streaming after the child's turn ended.
702
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_p1', text: '' } } });
703
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_p1', delta: 'Still working after the sub-agent.\nCEZ:MONITORING' } });
704
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_p1', text: 'Still working after the sub-agent.\nCEZ:MONITORING' } } });
705
+ emit({ method: 'item/completed', params: { threadId: 'th_child', turnId: 'turn_child', item: { type: 'agentMessage', id: 'item_child_text', text: 'Child review finished.' } } });
706
+ // No completion for this second child item: parent turn-end must not flush it into v1.
707
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_child', turnId: 'turn_child', itemId: 'item_child_partial', delta: 'Child review still streaming.' } });
708
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
709
+ return;
710
+ }
711
+
712
+ // hearsay-tools/cezarion#1294: another server's tool approval by default; `-own` names
713
+ // cezar's own server, `-form` is a real form (no approval), `-null-turn` has no turn id.
714
+ function replyWithMcpApproval(turnText, msg, mcpApproval) {
715
+ pendingApprovalTurn = 'turn_mock_1';
716
+ const variant = mcpApproval[1];
717
+ const server = approvalServer(variant);
718
+ emit({ id: 'mcp-approval-1', method: 'mcpServer/elicitation/request', params: mcpToolApprovalRequest(server, 'search',
719
+ variant === 'form' ? { _meta: null, message: 'Which region?', requestedSchema: { type: 'object', properties: { region: { type: 'string' } } } }
720
+ : variant === 'null-turn' ? { turnId: null } : {}) });
721
+ // No turn/completed until the matching native JSON-RPC response arrives.
722
+ return;
723
+ }
724
+
725
+ function approvalServer(variant) {
726
+ return variant === 'own' ? (cezarServer()?.[0].slice('mcp_servers.'.length) ?? 'cezar_ci_mock') : 'docs';
727
+ }
728
+
729
+ async function replyWithApproval(turnText, msg) {
730
+ pendingApprovalTurn = 'turn_mock_1';
731
+ emitApprovalRequest(turnText);
732
+ // P3/P4: Codex settles the request itself, or ends the turn around it, once the test releases it.
733
+ const resolved = turnText.includes('mock:approval-resolved');
734
+ if (resolved || turnText.includes('mock:approval-turn-end')) {
735
+ await waitForMockRelease();
736
+ emitSettledApproval(resolved);
737
+ }
738
+ // Otherwise no turn/completed until the matching native JSON-RPC response arrives.
739
+ return;
740
+ }
741
+
742
+ function emitApprovalRequest(turnText) {
743
+ const unsupported = turnText.includes('mock:approval-unsupported');
744
+ // hearsay-tools/cezarion#1266 P6: no itemId, so cezar answers it with a JSON-RPC error.
745
+ const malformed = turnText.includes('mock:approval-malformed');
746
+ emit({ id: unsupported ? 'approval-unsupported-1' : 'approval-1',
747
+ method: unsupported ? 'item/unsupported/requestApproval' : 'item/commandExecution/requestApproval',
748
+ params: { threadId: 'th_mock_1', turnId: pendingApprovalTurn, ...(malformed ? {} : { itemId: 'item_approval_1' }),
749
+ startedAtMs: 1, command: 'git status --short', cwd: '/repo', reason: 'Native approval requested',
750
+ availableDecisions: ['accept', 'acceptForSession', 'decline', 'cancel'] } });
751
+ }
752
+
753
+ function emitSettledApproval(resolved) {
754
+ const turnId = pendingApprovalTurn; pendingApprovalTurn = undefined;
755
+ if (resolved) emit({ method: 'serverRequest/resolved', params: { threadId: 'th_mock_1', requestId: 'approval-1' } });
756
+ const text = resolved ? 'Native approval settled without a cockpit answer.\nCEZ:DONE'
757
+ : 'Turn ended with the approval still pending.\nCEZ:DONE';
758
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { type: 'agentMessage', id: 'item_approval_settled', text } } });
759
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: turnId, status: 'completed' } } });
760
+ }
761
+
762
+ function replyWithAsk(turnText, msg) {
763
+ resumeAfterAsk = turnText.includes('mock:resume-done');
764
+ const questions = turnText.includes('multi free text')
765
+ ? [{ id: 'first', header: 'First', question: 'First choice?', isOther: true, isSecret: false,
766
+ options: [{ label: 'A', description: 'Option A.' }, { label: 'B', description: 'Option B.' }] },
767
+ { id: 'second', header: 'Second', question: 'Second choice?', isOther: true, isSecret: false,
768
+ options: [{ label: 'C', description: 'Option C.' }, { label: 'D', description: 'Option D.' }] }]
769
+ : [{ id: 'library', header: 'Library', question: 'Which test library?', isOther: true,
770
+ isSecret: false, options: [{ label: 'Vitest', description: 'Use the existing test runner.' },
771
+ { label: 'Node test', description: 'Use node:test.' }] }];
772
+ emit({ id: 'ask-1', method: 'item/tool/requestUserInput', params: {
773
+ threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ask_1', autoResolutionMs: null,
774
+ questions,
775
+ } });
776
+ return;
777
+ }
778
+
779
+ function replyWithOrdinaryTurn() {
780
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m1', text: '' } } });
781
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_m1', delta: 'Checking the working tree.' } });
782
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m1', text: 'Checking the working tree.' } } });
783
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'item_c1', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'inProgress' } } });
784
+ emit({ method: 'item/commandExecution/outputDelta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_c1', delta: ' M src/example.ts\n' } });
785
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'item_c1', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
786
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m2', text: '' } } });
787
+ emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_m2', delta: 'Done with the first pass.' } });
788
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m2', text: 'Done with the first pass.' } } });
789
+ emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 1500, inputTokens: 1200, outputTokens: 300 }, last: { totalTokens: 1500, inputTokens: 1200, outputTokens: 300 } } } });
790
+ emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
791
+ }
792
+
793
+ async function handleNativeFailure(turnText, msg) {
794
+ const selectedFailure = failureControl?.failureFor(turnText);
795
+ const nativeFailure = selectedFailure instanceof Promise ? await selectedFailure : selectedFailure;
796
+ if (failureControl ? nativeFailure === 'provider' : turnText.includes('mock:provider-error') || turnText.includes('mock:empty-success')) {
797
+ // Derived from the 0.147 rollout and upstream envelope; see the source
798
+ // core/__fixtures__/codex/provider-error.md and its independent golden NDJSON.
799
+ const completion = {"method": "turn/completed", "params": {"threadId": "th_mock_1", "turn": {"id": "turn_mock_1", "items": [], "itemsView": "notLoaded", "status": "failed", "error": {"message": "{\"type\":\"error\",\"status\":400,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"The 'gpt-6-astra' model requires a newer version of Codex. Please upgrade to the latest app or CLI and try again.\"}}", "codexErrorInfo": "other", "additionalDetails": null}, "startedAt": 1788632328, "completedAt": 1788632331, "durationMs": 3502}}};
800
+ if (turnText.includes('mock:empty-success')) {
801
+ completion.params.turn.status = 'completed';
802
+ completion.params.turn.error = null;
803
+ }
804
+ emit(completion);
805
+ return;
806
+ }
807
+ return dispatchScenario(lateReplies, turnText, msg, replyWithOrdinaryTurn);
808
+ }
809
+
810
+ async function handleTurnStart(msg) {
811
+ const openingCrashText = openingPromptText(msg);
812
+ if (rejectsOpeningPrompt(openingCrashText)) { emit({ id: msg.id, error: { code: -32603, message: 'owned turn rejected' } }); return; }
813
+ if (openingCrashText.includes('mock:crash-stderr-pre-ack')) {
814
+ const { crashWithStderr } = await import('./mock-runner-crash.mjs');
815
+ crashWithStderr(openingCrashText, '{"method":"turn/started","params":');
816
+ return;
817
+ }
818
+ if (rejectedPermissionOverride) permissionWarning();
819
+ activeTurnId = 'turn_mock_1';
820
+ acknowledgeTurnStart(msg, openingCrashText);
821
+ const turnText = nudgedTurnText(msg);
822
+ // `mock:ack-only` → turn/start is acknowledged and the turn does not start until a steer arrives
823
+ // (hearsay-tools/cezarion#1301).
824
+ if (turnText.includes('mock:ack-only')) { heldTurn = true; return; }
825
+ let silent;
826
+ if (needsSilentTailPreparation(turnText)) {
827
+ silent = await import('./mock-silent-tail.mjs');
828
+ silent.noteSilentTailPrompt(turnText);
829
+ if (silent.isAckOnlyNudge(turnText)) return;
830
+ if (silent.isLateTurnStartNudge(turnText)) await waitForMockRelease(silent.LATE_REPLY_MS);
831
+ else if (silent.isLateNudge(turnText)) await waitForMockRelease(silent.LATE_REPLY_MS);
832
+ }
833
+ // hearsay-tools/cezarion#1112: the acknowledged turn/start reports turn/started late.
834
+ if (turnText.includes('mock:deferred-turn-start')) await (await import('./mock-deferred-turn-start.mjs')).deferredTurnStart();
835
+ if (!turnText.includes('mock:no-progress-ack-only')) {
836
+ emit({ method: 'turn/started', params: { turn: { id: 'turn_mock_1', status: 'inProgress', items: [] } } });
837
+ }
838
+ if (delivery) await delivery.holdResponse(turnText);
839
+ if (needsLateContentHold(silent, turnText)) await waitForMockRelease(silent.LATE_TURN_START_HOLD_MS, '.content');
840
+ emitOpeningInput(msg, turnText);
841
+ if (turnText.includes('mock:crash-stderr')) {
842
+ const { crashWithStderr } = await import('./mock-runner-crash.mjs');
843
+ await new Promise(resolve => setTimeout(resolve, 30));
844
+ if (crashWithStderr(turnText, '{"method":"item/commandExecution/outputDelta","params":')) return;
845
+ }
846
+ return dispatchScenario(openingReplies, turnText, msg, dispatchAfterSilent);
847
+ }
848
+
849
+ function rejectsOpeningPrompt(openingCrashText) {
850
+ return delivery?.rejects(openingCrashText);
851
+ }
852
+
853
+ function needsSilentTailPreparation(turnText) {
854
+ return turnText.includes('mock:silent-tail') || silentNudge(turnText);
855
+ }
856
+
857
+ function needsLateContentHold(silent, turnText) {
858
+ return silent?.isLateTurnStartNudge(turnText);
859
+ }
860
+
861
+ function openingPromptText(msg) {
862
+ return msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
863
+ }
864
+
865
+ function nudgedTurnText(msg) {
866
+ return answerNudge(msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '');
867
+ }
868
+
869
+ function acknowledgeTurnStart(msg, openingCrashText) {
870
+ if (msg.mockHeldSteer) { /* the steer was acknowledged */ } else if (process.env.CEZ_MOCK_CODEX_LATE_START_ACK === '1' && startSerial++ > 0) lateStartAck = msg.id; else {
871
+ const ack = () => emit({ id: msg.id, result: { turn: { id: 'turn_mock_1' } } });
872
+ if (delivery) delivery.ack(openingCrashText, ack); else ack();
873
+ }
874
+ }
875
+
876
+ function emitOpeningInput(msg, turnText) {
877
+ // The real app-server records the turn's own input as a userMessage item,
878
+ // echoing clientUserMessageId as clientId (probe 0.155.1, #505).
879
+ const opening = { type: 'userMessage', id: `item_user_open_${++steerEchoSerial}`, clientId: msg.params?.clientUserMessageId ?? null, content: [{ type: 'text', text: turnText }] };
880
+ if (process.env.CEZ_MOCK_CODEX_NO_USER_ITEM !== '1') {
881
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
882
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
883
+ }
884
+ }
885
+
886
+ function resolveClientRequest(msg) {
887
+ const resolve = clientReplies.get(msg.id); clientReplies.delete(msg.id);
888
+ emit({ method: 'serverRequest/resolved', params: { threadId: 'th_mock_1', requestId: msg.id } });
889
+ resolve(msg);
890
+ return;
891
+ }
892
+
893
+ function replyToApproval(msg) {
894
+ const turnId = pendingApprovalTurn; pendingApprovalTurn = undefined;
895
+ const text = `${msg.error ? `Approval error: ${msg.error.code}` : `Approval response: ${JSON.stringify(msg.result)}`}\nCEZ:DONE`;
896
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId,
897
+ item: { type: 'agentMessage', id: 'item_approval_reply', text } } });
898
+ emit({ method: 'serverRequest/resolved', params: { threadId: 'th_mock_1', requestId: msg.id } });
899
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: turnId, status: msg.result?.decision === 'cancel' ? 'interrupted' : 'completed' } } });
900
+ return;
901
+ }
535
902
 
536
903
  rl.on('close', () => {
537
904
  if (!ignoreEof) process.exit(0);