okstra 0.174.0 → 0.176.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 (419) hide show
  1. package/README.md +18 -9
  2. package/bin/okstra +8 -4
  3. package/dist/cli-registry.d.mts +16 -0
  4. package/dist/cli-registry.mjs +560 -0
  5. package/dist/cli-registry.mjs.map +1 -0
  6. package/dist/commands/execute/agent-prompt.d.mts +1 -0
  7. package/{src → dist}/commands/execute/agent-prompt.mjs +10 -11
  8. package/dist/commands/execute/agent-prompt.mjs.map +1 -0
  9. package/dist/commands/execute/codex-dispatch.d.mts +3 -0
  10. package/dist/commands/execute/codex-dispatch.mjs +6 -0
  11. package/dist/commands/execute/codex-dispatch.mjs.map +1 -0
  12. package/dist/commands/execute/codex-run.d.mts +3 -0
  13. package/{src → dist}/commands/execute/codex-run.mjs +30 -37
  14. package/dist/commands/execute/codex-run.mjs.map +1 -0
  15. package/dist/commands/execute/convergence.d.mts +1 -0
  16. package/{src → dist}/commands/execute/convergence.mjs +15 -16
  17. package/dist/commands/execute/convergence.mjs.map +1 -0
  18. package/dist/commands/execute/error-log.d.mts +1 -0
  19. package/{src → dist}/commands/execute/error-log.mjs +2 -3
  20. package/dist/commands/execute/error-log.mjs.map +1 -0
  21. package/dist/commands/execute/git-reconcile.d.mts +1 -0
  22. package/{src → dist}/commands/execute/git-reconcile.mjs +10 -11
  23. package/dist/commands/execute/git-reconcile.mjs.map +1 -0
  24. package/dist/commands/execute/handoff.d.mts +1 -0
  25. package/{src → dist}/commands/execute/handoff.mjs +10 -11
  26. package/dist/commands/execute/handoff.mjs.map +1 -0
  27. package/dist/commands/execute/incremental-carry.d.mts +1 -0
  28. package/{src → dist}/commands/execute/incremental-carry.mjs +2 -3
  29. package/dist/commands/execute/incremental-carry.mjs.map +1 -0
  30. package/dist/commands/execute/incremental-scope.d.mts +1 -0
  31. package/{src → dist}/commands/execute/incremental-scope.mjs +2 -3
  32. package/dist/commands/execute/incremental-scope.mjs.map +1 -0
  33. package/dist/commands/execute/integrate-stages.d.mts +1 -0
  34. package/{src → dist}/commands/execute/integrate-stages.mjs +10 -11
  35. package/dist/commands/execute/integrate-stages.mjs.map +1 -0
  36. package/dist/commands/execute/pane-title.d.mts +1 -0
  37. package/dist/commands/execute/pane-title.mjs +20 -0
  38. package/dist/commands/execute/pane-title.mjs.map +1 -0
  39. package/dist/commands/execute/plan-items.d.mts +1 -0
  40. package/dist/commands/execute/plan-items.mjs +9 -0
  41. package/dist/commands/execute/plan-items.mjs.map +1 -0
  42. package/dist/commands/execute/plan-validate.d.mts +1 -0
  43. package/dist/commands/execute/plan-validate.mjs +68 -0
  44. package/dist/commands/execute/plan-validate.mjs.map +1 -0
  45. package/dist/commands/execute/plan-verify.d.mts +1 -0
  46. package/{src → dist}/commands/execute/plan-verify.mjs +13 -16
  47. package/dist/commands/execute/plan-verify.mjs.map +1 -0
  48. package/dist/commands/execute/render-bundle.d.mts +3 -0
  49. package/{src → dist}/commands/execute/render-bundle.mjs +65 -61
  50. package/dist/commands/execute/render-bundle.mjs.map +1 -0
  51. package/dist/commands/execute/run.d.mts +24 -0
  52. package/dist/commands/execute/run.mjs +196 -0
  53. package/dist/commands/execute/run.mjs.map +1 -0
  54. package/dist/commands/execute/spawn-followups.d.mts +1 -0
  55. package/{src → dist}/commands/execute/spawn-followups.mjs +6 -7
  56. package/dist/commands/execute/spawn-followups.mjs.map +1 -0
  57. package/dist/commands/execute/team.d.mts +3 -0
  58. package/{src → dist}/commands/execute/team.mjs +29 -36
  59. package/dist/commands/execute/team.mjs.map +1 -0
  60. package/dist/commands/execute/token-usage.d.mts +1 -0
  61. package/{src → dist}/commands/execute/token-usage.mjs +2 -3
  62. package/dist/commands/execute/token-usage.mjs.map +1 -0
  63. package/dist/commands/execute/wizard.d.mts +8 -0
  64. package/dist/commands/execute/wizard.mjs +191 -0
  65. package/dist/commands/execute/wizard.mjs.map +1 -0
  66. package/dist/commands/execute/worker-audit-check.d.mts +1 -0
  67. package/{src → dist}/commands/execute/worker-audit-check.mjs +11 -12
  68. package/dist/commands/execute/worker-audit-check.mjs.map +1 -0
  69. package/dist/commands/execute/worker-dispatch.d.mts +7 -0
  70. package/{src → dist}/commands/execute/worker-dispatch.mjs +34 -46
  71. package/dist/commands/execute/worker-dispatch.mjs.map +1 -0
  72. package/dist/commands/execute/worker-state.d.mts +1 -0
  73. package/{src → dist}/commands/execute/worker-state.mjs +15 -16
  74. package/dist/commands/execute/worker-state.mjs.map +1 -0
  75. package/dist/commands/execute/worktree-lookup.d.mts +1 -0
  76. package/{src → dist}/commands/execute/worktree-lookup.mjs +38 -37
  77. package/dist/commands/execute/worktree-lookup.mjs.map +1 -0
  78. package/dist/commands/execute/worktree-status.d.mts +1 -0
  79. package/{src → dist}/commands/execute/worktree-status.mjs +52 -46
  80. package/dist/commands/execute/worktree-status.mjs.map +1 -0
  81. package/dist/commands/inspect/code-review.d.mts +1 -0
  82. package/{src → dist}/commands/inspect/code-review.mjs +16 -19
  83. package/dist/commands/inspect/code-review.mjs.map +1 -0
  84. package/dist/commands/inspect/container.d.mts +1 -0
  85. package/{src → dist}/commands/inspect/container.mjs +11 -13
  86. package/dist/commands/inspect/container.mjs.map +1 -0
  87. package/dist/commands/inspect/context-cost.d.mts +1 -0
  88. package/{src → dist}/commands/inspect/context-cost.mjs +11 -13
  89. package/dist/commands/inspect/context-cost.mjs.map +1 -0
  90. package/dist/commands/inspect/design-prep.d.mts +1 -0
  91. package/{src → dist}/commands/inspect/design-prep.mjs +10 -11
  92. package/dist/commands/inspect/design-prep.mjs.map +1 -0
  93. package/dist/commands/inspect/error-report.d.mts +1 -0
  94. package/{src → dist}/commands/inspect/error-report.mjs +11 -13
  95. package/dist/commands/inspect/error-report.mjs.map +1 -0
  96. package/dist/commands/inspect/error-zip.d.mts +1 -0
  97. package/{src → dist}/commands/inspect/error-zip.mjs +11 -12
  98. package/dist/commands/inspect/error-zip.mjs.map +1 -0
  99. package/dist/commands/inspect/log-report.d.mts +1 -0
  100. package/{src → dist}/commands/inspect/log-report.mjs +11 -13
  101. package/dist/commands/inspect/log-report.mjs.map +1 -0
  102. package/dist/commands/inspect/profile-show.d.mts +1 -0
  103. package/{src → dist}/commands/inspect/profile-show.mjs +10 -11
  104. package/dist/commands/inspect/profile-show.mjs.map +1 -0
  105. package/dist/commands/inspect/recap.d.mts +1 -0
  106. package/{src → dist}/commands/inspect/recap.mjs +11 -13
  107. package/dist/commands/inspect/recap.mjs.map +1 -0
  108. package/dist/commands/inspect/resolve-task-key.d.mts +1 -0
  109. package/{src → dist}/commands/inspect/resolve-task-key.mjs +11 -13
  110. package/dist/commands/inspect/resolve-task-key.mjs.map +1 -0
  111. package/dist/commands/inspect/rollup.d.mts +1 -0
  112. package/{src → dist}/commands/inspect/rollup.mjs +11 -13
  113. package/dist/commands/inspect/rollup.mjs.map +1 -0
  114. package/dist/commands/inspect/run-audit.d.mts +1 -0
  115. package/{src → dist}/commands/inspect/run-audit.mjs +11 -12
  116. package/dist/commands/inspect/run-audit.mjs.map +1 -0
  117. package/dist/commands/inspect/set-work-status.d.mts +1 -0
  118. package/{src → dist}/commands/inspect/set-work-status.mjs +11 -13
  119. package/dist/commands/inspect/set-work-status.mjs.map +1 -0
  120. package/dist/commands/inspect/stage-map.d.mts +1 -0
  121. package/dist/commands/inspect/stage-map.mjs +110 -0
  122. package/dist/commands/inspect/stage-map.mjs.map +1 -0
  123. package/dist/commands/inspect/task-list.d.mts +1 -0
  124. package/dist/commands/inspect/task-list.mjs +103 -0
  125. package/dist/commands/inspect/task-list.mjs.map +1 -0
  126. package/dist/commands/inspect/task-show.d.mts +1 -0
  127. package/dist/commands/inspect/task-show.mjs +108 -0
  128. package/dist/commands/inspect/task-show.mjs.map +1 -0
  129. package/dist/commands/inspect/time-report.d.mts +1 -0
  130. package/{src → dist}/commands/inspect/time-report.mjs +11 -13
  131. package/dist/commands/inspect/time-report.mjs.map +1 -0
  132. package/dist/commands/inspect/usage-report.d.mts +1 -0
  133. package/{src → dist}/commands/inspect/usage-report.mjs +11 -13
  134. package/dist/commands/inspect/usage-report.mjs.map +1 -0
  135. package/dist/commands/inspect/user-response.d.mts +1 -0
  136. package/{src → dist}/commands/inspect/user-response.mjs +10 -11
  137. package/dist/commands/inspect/user-response.mjs.map +1 -0
  138. package/dist/commands/inspect/worker-liveness.d.mts +1 -0
  139. package/{src → dist}/commands/inspect/worker-liveness.mjs +11 -13
  140. package/dist/commands/inspect/worker-liveness.mjs.map +1 -0
  141. package/dist/commands/lifecycle/check-project.d.mts +21 -0
  142. package/dist/commands/lifecycle/check-project.mjs +175 -0
  143. package/dist/commands/lifecycle/check-project.mjs.map +1 -0
  144. package/dist/commands/lifecycle/config.d.mts +1 -0
  145. package/dist/commands/lifecycle/config.mjs +369 -0
  146. package/dist/commands/lifecycle/config.mjs.map +1 -0
  147. package/dist/commands/lifecycle/doctor.d.mts +10 -0
  148. package/dist/commands/lifecycle/doctor.mjs +317 -0
  149. package/dist/commands/lifecycle/doctor.mjs.map +1 -0
  150. package/dist/commands/lifecycle/install.d.mts +17 -0
  151. package/dist/commands/lifecycle/install.mjs +1077 -0
  152. package/dist/commands/lifecycle/install.mjs.map +1 -0
  153. package/dist/commands/lifecycle/migrate.d.mts +1 -0
  154. package/{src → dist}/commands/lifecycle/migrate.mjs +10 -11
  155. package/dist/commands/lifecycle/migrate.mjs.map +1 -0
  156. package/dist/commands/lifecycle/model.d.mts +1 -0
  157. package/dist/commands/lifecycle/model.mjs +22 -0
  158. package/dist/commands/lifecycle/model.mjs.map +1 -0
  159. package/dist/commands/lifecycle/paths.d.mts +1 -0
  160. package/dist/commands/lifecycle/paths.mjs +102 -0
  161. package/dist/commands/lifecycle/paths.mjs.map +1 -0
  162. package/dist/commands/lifecycle/preflight.d.mts +1 -0
  163. package/dist/commands/lifecycle/preflight.mjs +124 -0
  164. package/dist/commands/lifecycle/preflight.mjs.map +1 -0
  165. package/dist/commands/lifecycle/setup.d.mts +1 -0
  166. package/dist/commands/lifecycle/setup.mjs +297 -0
  167. package/dist/commands/lifecycle/setup.mjs.map +1 -0
  168. package/dist/commands/lifecycle/uninstall.d.mts +4 -0
  169. package/dist/commands/lifecycle/uninstall.mjs +262 -0
  170. package/dist/commands/lifecycle/uninstall.mjs.map +1 -0
  171. package/dist/commands/manager.d.mts +3 -0
  172. package/{src → dist}/commands/manager.mjs +25 -32
  173. package/dist/commands/manager.mjs.map +1 -0
  174. package/dist/commands/memory/memory.d.mts +1 -0
  175. package/dist/commands/memory/memory.mjs +569 -0
  176. package/dist/commands/memory/memory.mjs.map +1 -0
  177. package/dist/commands/pr/pr.d.mts +33 -0
  178. package/dist/commands/pr/pr.mjs +296 -0
  179. package/dist/commands/pr/pr.mjs.map +1 -0
  180. package/dist/commands/report/agent-activity.d.mts +1 -0
  181. package/{src → dist}/commands/report/agent-activity.mjs +11 -12
  182. package/dist/commands/report/agent-activity.mjs.map +1 -0
  183. package/dist/commands/report/finalize.d.mts +4 -0
  184. package/{src → dist}/commands/report/finalize.mjs +25 -32
  185. package/dist/commands/report/finalize.mjs.map +1 -0
  186. package/dist/commands/report/inject-report-index.d.mts +1 -0
  187. package/{src → dist}/commands/report/inject-report-index.mjs +6 -7
  188. package/dist/commands/report/inject-report-index.mjs.map +1 -0
  189. package/dist/commands/report/render-final-report.d.mts +1 -0
  190. package/{src → dist}/commands/report/render-final-report.mjs +6 -7
  191. package/dist/commands/report/render-final-report.mjs.map +1 -0
  192. package/dist/commands/report/render-views.d.mts +1 -0
  193. package/{src → dist}/commands/report/render-views.mjs +6 -7
  194. package/dist/commands/report/render-views.mjs.map +1 -0
  195. package/dist/commands/report/translate.d.mts +1 -0
  196. package/{src → dist}/commands/report/translate.mjs +6 -7
  197. package/dist/commands/report/translate.mjs.map +1 -0
  198. package/dist/lib/helper-scripts.d.mts +3 -0
  199. package/{src → dist}/lib/helper-scripts.mjs +11 -14
  200. package/dist/lib/helper-scripts.mjs.map +1 -0
  201. package/dist/lib/host-registry-client.d.mts +25 -0
  202. package/dist/lib/host-registry-client.mjs +152 -0
  203. package/dist/lib/host-registry-client.mjs.map +1 -0
  204. package/dist/lib/install-assets.d.mts +8 -0
  205. package/{src → dist}/lib/install-assets.mjs +17 -20
  206. package/dist/lib/install-assets.mjs.map +1 -0
  207. package/dist/lib/okstra-dirs.d.mts +4 -0
  208. package/{src → dist}/lib/okstra-dirs.mjs +3 -6
  209. package/dist/lib/okstra-dirs.mjs.map +1 -0
  210. package/dist/lib/paths.d.mts +4 -0
  211. package/dist/lib/paths.mjs +78 -0
  212. package/dist/lib/paths.mjs.map +1 -0
  213. package/dist/lib/proc.d.mts +3 -0
  214. package/dist/lib/proc.mjs +31 -0
  215. package/dist/lib/proc.mjs.map +1 -0
  216. package/dist/lib/python-helper.d.mts +8 -0
  217. package/dist/lib/python-helper.mjs +123 -0
  218. package/dist/lib/python-helper.mjs.map +1 -0
  219. package/dist/lib/runtime-manifest.d.mts +7 -0
  220. package/dist/lib/runtime-manifest.mjs +73 -0
  221. package/dist/lib/runtime-manifest.mjs.map +1 -0
  222. package/dist/lib/skill-catalog.d.mts +4 -0
  223. package/dist/lib/skill-catalog.mjs +50 -0
  224. package/dist/lib/skill-catalog.mjs.map +1 -0
  225. package/dist/lib/types.d.mts +129 -0
  226. package/dist/lib/types.mjs +2 -0
  227. package/dist/lib/types.mjs.map +1 -0
  228. package/dist/lib/version.d.mts +2 -0
  229. package/dist/lib/version.mjs +27 -0
  230. package/dist/lib/version.mjs.map +1 -0
  231. package/docs/architecture/storage-model.md +6 -2
  232. package/docs/architecture.md +29 -20
  233. package/docs/cli.md +40 -16
  234. package/docs/contributor-change-matrix.md +2 -2
  235. package/docs/for-ai/skills/okstra-inspect.md +2 -3
  236. package/docs/for-ai/skills/okstra-rollup.md +1 -0
  237. package/docs/project-structure-overview.md +72 -62
  238. package/docs/task-process/README.md +12 -8
  239. package/docs/task-process/common-flow.md +13 -16
  240. package/docs/task-process/error-analysis.md +9 -10
  241. package/docs/task-process/final-verification.md +7 -7
  242. package/docs/task-process/implementation-planning.md +9 -9
  243. package/docs/task-process/implementation.md +6 -6
  244. package/docs/task-process/release-handoff.md +8 -7
  245. package/docs/task-process/requirements-discovery.md +8 -8
  246. package/package.json +11 -5
  247. package/runtime/BUILD.json +2 -2
  248. package/runtime/agents/workers/claude-worker.md +2 -1
  249. package/runtime/agents/workers/report-writer-worker.md +3 -1
  250. package/runtime/agents/workers/translator-worker.md +3 -1
  251. package/runtime/bin/lib/okstra/cli.sh +12 -0
  252. package/runtime/bin/lib/okstra/globals.sh +3 -0
  253. package/runtime/bin/lib/okstra/interactive.sh +12 -6
  254. package/runtime/bin/lib/okstra/usage.sh +7 -6
  255. package/runtime/bin/okstra-error-log.py +27 -0
  256. package/runtime/bin/okstra-provider-exec.py +217 -5
  257. package/runtime/bin/okstra-spawn-followups.py +4 -2
  258. package/runtime/bin/okstra.sh +10 -0
  259. package/runtime/prompts/launch.template.md +2 -2
  260. package/runtime/prompts/lead/context-loader.md +1 -2
  261. package/runtime/prompts/lead/convergence.md +27 -18
  262. package/runtime/prompts/lead/okstra-lead-contract.md +6 -5
  263. package/runtime/prompts/lead/plan-body-verification.md +10 -3
  264. package/runtime/prompts/lead/report-writer.md +15 -1
  265. package/runtime/prompts/lead/team-contract.md +17 -13
  266. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  267. package/runtime/prompts/profiles/_implementation-executor.md +0 -3
  268. package/runtime/prompts/profiles/_implementation-verifier.md +3 -7
  269. package/runtime/prompts/profiles/change-impact-analysis.md +20 -0
  270. package/runtime/prompts/profiles/error-analysis.md +27 -1
  271. package/runtime/prompts/profiles/feature-analysis.md +20 -0
  272. package/runtime/prompts/profiles/final-verification.md +20 -1
  273. package/runtime/prompts/profiles/implementation-option-selection.md +20 -0
  274. package/runtime/prompts/profiles/implementation-planning.md +26 -0
  275. package/runtime/prompts/profiles/implementation.md +19 -0
  276. package/runtime/prompts/profiles/improvement-discovery.md +21 -1
  277. package/runtime/prompts/profiles/project-analysis.md +20 -0
  278. package/runtime/prompts/profiles/release-handoff.md +4 -0
  279. package/runtime/prompts/profiles/requirements-discovery.md +27 -1
  280. package/runtime/prompts/wizard/prompts.ko.json +35 -0
  281. package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +8 -0
  282. package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +8 -0
  283. package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
  284. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +2 -2
  285. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +25 -5
  286. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  287. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +4 -1
  288. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  289. package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +2 -2
  290. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +79 -11
  291. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +95 -25
  292. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +59 -14
  293. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +55 -9
  294. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +51 -12
  295. package/runtime/python/okstra_ctl/adapters/runtime/__init__.py +1 -0
  296. package/runtime/python/okstra_ctl/adapters/runtime/assembly.py +27 -0
  297. package/runtime/python/okstra_ctl/adapters/runtime/cli_wrapper.py +49 -0
  298. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +74 -0
  299. package/runtime/python/okstra_ctl/agent_activity.py +5 -1
  300. package/runtime/python/okstra_ctl/agent_invocation.py +608 -14
  301. package/runtime/python/okstra_ctl/agent_prompt_cli.py +249 -50
  302. package/runtime/python/okstra_ctl/application/open_worker.py +29 -0
  303. package/runtime/python/okstra_ctl/application/resolve_assignment.py +53 -12
  304. package/runtime/python/okstra_ctl/assignment_environment.py +102 -0
  305. package/runtime/python/okstra_ctl/assignment_resolver.py +790 -0
  306. package/runtime/python/okstra_ctl/attempt_evidence.py +342 -0
  307. package/runtime/python/okstra_ctl/convergence.py +409 -3
  308. package/runtime/python/okstra_ctl/convergence_engine.py +248 -15
  309. package/runtime/python/okstra_ctl/convergence_store.py +170 -1
  310. package/runtime/python/okstra_ctl/dispatch_core.py +1204 -155
  311. package/runtime/python/okstra_ctl/dispatch_state.py +696 -11
  312. package/runtime/python/okstra_ctl/doctor.py +14 -0
  313. package/runtime/python/okstra_ctl/domain/host.py +34 -1
  314. package/runtime/python/okstra_ctl/domain/provider.py +212 -6
  315. package/runtime/python/okstra_ctl/domain/role.py +125 -0
  316. package/runtime/python/okstra_ctl/domain/wizard/interaction.py +18 -0
  317. package/runtime/python/okstra_ctl/domain/worker_exec.py +34 -1
  318. package/runtime/python/okstra_ctl/domain/worker_role.py +13 -3
  319. package/runtime/python/okstra_ctl/domain/worker_runtime.py +44 -0
  320. package/runtime/python/okstra_ctl/domain/worker_stream.py +24 -0
  321. package/runtime/python/okstra_ctl/error_log_write.py +9 -0
  322. package/runtime/python/okstra_ctl/error_report.py +21 -1
  323. package/runtime/python/okstra_ctl/execution_identity.py +529 -0
  324. package/runtime/python/okstra_ctl/execution_manifest.py +1281 -0
  325. package/runtime/python/okstra_ctl/execution_mutation_audit.py +619 -0
  326. package/runtime/python/okstra_ctl/implementation_outcome.py +21 -5
  327. package/runtime/python/okstra_ctl/index.py +6 -2
  328. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +43 -3
  329. package/runtime/python/okstra_ctl/legacy_model_selection.py +578 -0
  330. package/runtime/python/okstra_ctl/manager_sync.py +4 -1
  331. package/runtime/python/okstra_ctl/model_cli.py +278 -0
  332. package/runtime/python/okstra_ctl/model_defaults.py +46 -0
  333. package/runtime/python/okstra_ctl/model_discovery.py +26 -7
  334. package/runtime/python/okstra_ctl/model_pool.py +501 -0
  335. package/runtime/python/okstra_ctl/models.py +47 -27
  336. package/runtime/python/okstra_ctl/mutation_recovery.py +293 -0
  337. package/runtime/python/okstra_ctl/next_phase.py +236 -0
  338. package/runtime/python/okstra_ctl/pane_title.py +151 -0
  339. package/runtime/python/okstra_ctl/ports/host_model.py +48 -17
  340. package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
  341. package/runtime/python/okstra_ctl/ports/worker_runtime.py +26 -0
  342. package/runtime/python/okstra_ctl/recap.py +4 -1
  343. package/runtime/python/okstra_ctl/registry/provider_registry.py +8 -4
  344. package/runtime/python/okstra_ctl/render.py +124 -57
  345. package/runtime/python/okstra_ctl/render_final_report.py +19 -0
  346. package/runtime/python/okstra_ctl/report_contract.py +39 -0
  347. package/runtime/python/okstra_ctl/report_finalize.py +16 -0
  348. package/runtime/python/okstra_ctl/report_html/render.py +1 -0
  349. package/runtime/python/okstra_ctl/role_requirements.py +210 -0
  350. package/runtime/python/okstra_ctl/rollup.py +4 -1
  351. package/runtime/python/okstra_ctl/run.py +1210 -258
  352. package/runtime/python/okstra_ctl/run_index_row.py +7 -1
  353. package/runtime/python/okstra_ctl/stage_fix_carry.py +17 -1
  354. package/runtime/python/okstra_ctl/team.py +14 -18
  355. package/runtime/python/okstra_ctl/time_report.py +38 -4
  356. package/runtime/python/okstra_ctl/usage_identity.py +86 -0
  357. package/runtime/python/okstra_ctl/usage_report.py +15 -3
  358. package/runtime/python/okstra_ctl/wizard.py +1449 -66
  359. package/runtime/python/okstra_ctl/worker_audit_ledger.py +68 -65
  360. package/runtime/python/okstra_ctl/worker_prompt_body.py +18 -2
  361. package/runtime/python/okstra_ctl/worker_prompt_contract.py +40 -9
  362. package/runtime/python/okstra_ctl/worker_prompt_headers.py +16 -1
  363. package/runtime/python/okstra_ctl/worker_request.py +7 -1
  364. package/runtime/python/okstra_ctl/worker_runner.py +130 -5
  365. package/runtime/python/okstra_ctl/workers.py +7 -5
  366. package/runtime/python/okstra_ctl/workflow.py +18 -32
  367. package/runtime/python/okstra_ctl/worktree.py +3 -3
  368. package/runtime/python/okstra_ctl/worktree_registry.py +5 -4
  369. package/runtime/python/okstra_ctl/write_policy.py +571 -0
  370. package/runtime/python/okstra_project/state.py +54 -6
  371. package/runtime/schemas/convergence-groups-v2.0.schema.json +128 -0
  372. package/runtime/schemas/execution-manifest-v2.schema.json +340 -0
  373. package/runtime/schemas/final-report-v2.0.schema.json +68 -5
  374. package/runtime/skills/okstra-inspect/facets/recap.md +2 -0
  375. package/runtime/skills/okstra-inspect/facets/report.md +1 -1
  376. package/runtime/skills/okstra-inspect/facets/status.md +15 -13
  377. package/runtime/skills/okstra-rollup/SKILL.md +1 -0
  378. package/runtime/skills/okstra-run/SKILL.md +14 -14
  379. package/runtime/templates/implementation-worker-preamble.md +3 -18
  380. package/runtime/templates/project-docs/task-index.template.md +0 -1
  381. package/runtime/templates/report-writer-prompt-preamble.md +2 -0
  382. package/runtime/templates/reports/final-report-v2.template.md +7 -1
  383. package/runtime/templates/reports/html/base.template.html +21 -0
  384. package/runtime/templates/reports/html/i18n/en.json +4 -0
  385. package/runtime/templates/reports/html/i18n/ko.json +4 -0
  386. package/runtime/templates/reports/html/macros/forms.html +5 -4
  387. package/runtime/templates/reports/html/tasks/final-verification.template.html +1 -1
  388. package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
  389. package/runtime/templates/worker-prompt-preamble.md +6 -35
  390. package/runtime/validators/lib/fixtures.sh +49 -16
  391. package/runtime/validators/validate-run.py +486 -134
  392. package/runtime/validators/validate_session_conformance.py +25 -7
  393. package/src/cli-registry.mjs +0 -539
  394. package/src/commands/execute/codex-dispatch.mjs +0 -10
  395. package/src/commands/execute/plan-items.mjs +0 -9
  396. package/src/commands/execute/plan-validate.mjs +0 -68
  397. package/src/commands/execute/run.mjs +0 -208
  398. package/src/commands/execute/wizard.mjs +0 -167
  399. package/src/commands/inspect/stage-map.mjs +0 -98
  400. package/src/commands/inspect/task-list.mjs +0 -97
  401. package/src/commands/inspect/task-show.mjs +0 -100
  402. package/src/commands/lifecycle/check-project.mjs +0 -187
  403. package/src/commands/lifecycle/config.mjs +0 -385
  404. package/src/commands/lifecycle/doctor.mjs +0 -308
  405. package/src/commands/lifecycle/install.mjs +0 -1170
  406. package/src/commands/lifecycle/paths.mjs +0 -104
  407. package/src/commands/lifecycle/preflight.mjs +0 -125
  408. package/src/commands/lifecycle/setup.mjs +0 -333
  409. package/src/commands/lifecycle/uninstall.mjs +0 -279
  410. package/src/commands/memory/memory.mjs +0 -524
  411. package/src/commands/pr/pr.mjs +0 -261
  412. package/src/lib/host-registry-client.mjs +0 -176
  413. package/src/lib/paths.mjs +0 -86
  414. package/src/lib/proc.mjs +0 -31
  415. package/src/lib/python-helper.mjs +0 -142
  416. package/src/lib/runtime-manifest.mjs +0 -70
  417. package/src/lib/skill-catalog.mjs +0 -53
  418. package/src/lib/version.mjs +0 -20
  419. /package/{src → dist}/commands/pr/default.md +0 -0
@@ -60,7 +60,7 @@ def create_adapter(
60
60
  detector=no_automatic_claim,
61
61
  provider_registry=provider_registry,
62
62
  host_model_port=host_model_port or NativeExecutionValueHostModelBindingPort(
63
- DESCRIPTOR.id,
64
- DESCRIPTOR.native_provider_id,
63
+ host_runtime=DESCRIPTOR.id,
64
+ native_provider=DESCRIPTOR.native_provider_id,
65
65
  ),
66
66
  )
@@ -15,6 +15,7 @@ from okstra_ctl.adapters.hosts.capability_adapter import (
15
15
  numbered_interaction_port,
16
16
  )
17
17
  from okstra_ctl.domain.host import HostClaim, HostDescriptor, HostResolutionContext
18
+ from okstra_ctl.domain.provider import HostModelBinding
18
19
  from okstra_ctl.ports.host_model import (
19
20
  HostModelBindingError,
20
21
  HostModelBindingRequest,
@@ -42,27 +43,46 @@ DESCRIPTOR = HostDescriptor(
42
43
  class ClaudeFamilyHostModelBindingPort:
43
44
  _FAMILIES = ("fable", "opus", "sonnet", "haiku")
44
45
 
45
- def resolve(self, request: HostModelBindingRequest) -> str | None:
46
+ def resolve(self, request: HostModelBindingRequest) -> HostModelBinding:
46
47
  if request.runner == "cli-wrapper":
47
- return None
48
+ return HostModelBinding(
49
+ runner=request.runner,
50
+ catalog_execution_value=request.model_execution_value,
51
+ resolved_execution_value=request.execution_value,
52
+ host_model_value=None,
53
+ binding_fidelity="exact",
54
+ worker_write_capability=request.worker_write_capability,
55
+ )
48
56
  if (
49
57
  request.runner != "native-session"
50
58
  or request.host_runtime != DESCRIPTOR.id
51
59
  or request.provider != DESCRIPTOR.native_provider_id
52
60
  ):
53
61
  raise self._unsupported(request)
54
- segments = request.model_execution_value.lower().split("-")
62
+ segments = request.execution_value.lower().split("-")
55
63
  matches = [family for family in self._FAMILIES if family in segments]
56
64
  if len(matches) != 1:
57
65
  raise self._unsupported(request)
58
- return matches[0]
66
+ if request.version_kind == "pinned":
67
+ raise HostModelBindingError(
68
+ "exact binding unavailable: "
69
+ f"host={request.host_runtime!r}, model={request.model_ref!r}"
70
+ )
71
+ return HostModelBinding(
72
+ runner=request.runner,
73
+ catalog_execution_value=request.model_execution_value,
74
+ resolved_execution_value=request.execution_value,
75
+ host_model_value=matches[0],
76
+ binding_fidelity="channel",
77
+ worker_write_capability=request.worker_write_capability,
78
+ )
59
79
 
60
80
  @staticmethod
61
81
  def _unsupported(request: HostModelBindingRequest) -> HostModelBindingError:
62
82
  return HostModelBindingError(
63
83
  "unsupported host model: "
64
84
  f"host={request.host_runtime!r}, provider={request.provider!r}, "
65
- f"model={request.model_execution_value!r}"
85
+ f"model={request.execution_value!r}"
66
86
  )
67
87
 
68
88
 
@@ -83,7 +83,7 @@ def create_adapter(
83
83
  provider_registry=provider_registry,
84
84
  readiness_probe=_okstra_home_write_checks,
85
85
  host_model_port=host_model_port or NativeExecutionValueHostModelBindingPort(
86
- DESCRIPTOR.id,
87
- DESCRIPTOR.native_provider_id,
86
+ host_runtime=DESCRIPTOR.id,
87
+ native_provider=DESCRIPTOR.native_provider_id,
88
88
  ),
89
89
  )
@@ -13,6 +13,7 @@ from okstra_ctl.adapters.hosts.capability_adapter import (
13
13
  numbered_interaction_port,
14
14
  )
15
15
  from okstra_ctl.domain.host import HostClaim, HostDescriptor, HostResolutionContext
16
+ from okstra_ctl.ports.host_model import FailClosedHostModelBindingPort
16
17
  from okstra_ctl.registry.provider_registry import ProviderRegistry
17
18
 
18
19
 
@@ -70,5 +71,7 @@ def create_adapter(
70
71
  supported_functions=INTERACTION_FUNCTIONS,
71
72
  detector=_detect_tmux,
72
73
  provider_registry=provider_registry,
73
- host_model_port=host_model_port,
74
+ host_model_port=(
75
+ host_model_port or FailClosedHostModelBindingPort(DESCRIPTOR.id)
76
+ ),
74
77
  )
@@ -63,7 +63,7 @@ def create_adapter(
63
63
  detector=no_automatic_claim,
64
64
  provider_registry=provider_registry,
65
65
  host_model_port=host_model_port or NativeExecutionValueHostModelBindingPort(
66
- DESCRIPTOR.id,
67
- DESCRIPTOR.native_provider_id,
66
+ host_runtime=DESCRIPTOR.id,
67
+ native_provider=DESCRIPTOR.native_provider_id,
68
68
  ),
69
69
  )
@@ -63,7 +63,7 @@ def create_adapter(
63
63
  detector=no_automatic_claim,
64
64
  provider_registry=provider_registry,
65
65
  host_model_port=host_model_port or NativeExecutionValueHostModelBindingPort(
66
- DESCRIPTOR.id,
67
- DESCRIPTOR.native_provider_id,
66
+ host_runtime=DESCRIPTOR.id,
67
+ native_provider=DESCRIPTOR.native_provider_id,
68
68
  ),
69
69
  )
@@ -1,7 +1,14 @@
1
1
  """Bundled Antigravity provider catalog."""
2
2
  from typing import Any, Mapping
3
3
 
4
- from okstra_ctl.domain.provider import LeadLaunchSpec, ModelSpec, ProviderSpec
4
+ from okstra_ctl.domain.provider import (
5
+ LeadLaunchSpec,
6
+ ModelSpec,
7
+ ProviderSpec,
8
+ ServedModelAttestation,
9
+ snapshot_execution_normalizations,
10
+ )
11
+ from okstra_ctl.domain.role import ALL_ROLE_TOKENS
5
12
  from okstra_ctl.domain.worker_exec import (
6
13
  STREAM_JSON,
7
14
  ExecCommand,
@@ -15,24 +22,47 @@ from okstra_ctl.domain.worker_stream import (
15
22
  ToolCall,
16
23
  ToolResult,
17
24
  )
25
+ import okstra_ctl.model_discovery as model_discovery
18
26
 
19
27
 
20
- # `agy` needs a tier suffix at dispatch time; model_discovery owns that host
21
- # specific normalization so catalog display values never reach the CLI.
28
+ # `agy` needs a tier suffix at dispatch time. The provider exposes its
29
+ # normalizer through ProviderSpec so assignment resolution does not branch on
30
+ # provider names.
22
31
  ANTIGRAVITY = {
23
- "gemini-3.1-pro": ModelSpec("Gemini 3.1 Pro", "gemini-3.1-pro", in_picker=True),
24
- "gemini 3.1 pro": ModelSpec("Gemini 3.1 Pro", "gemini-3.1-pro"),
32
+ "gemini-3.1-pro": ModelSpec(
33
+ "gemini-3.1-pro", "Gemini 3.1 Pro", "gemini-3.1-pro",
34
+ aliases=("gemini 3.1 pro",),
35
+ ),
25
36
  "gemini-3.6-flash": ModelSpec(
26
- "Gemini 3.6 Flash", "gemini-3.6-flash", in_picker=True
37
+ "gemini-3.6-flash", "Gemini 3.6 Flash", "gemini-3.6-flash",
38
+ aliases=("gemini 3.6 flash",),
27
39
  ),
28
- "gemini 3.6 flash": ModelSpec("Gemini 3.6 Flash", "gemini-3.6-flash"),
29
40
  "gemini-3.5-flash": ModelSpec(
30
- "Gemini 3.5 Flash", "gemini-3.5-flash", in_picker=True
41
+ "gemini-3.5-flash", "Gemini 3.5 Flash", "gemini-3.5-flash",
42
+ aliases=("gemini 3.5 flash",),
31
43
  ),
32
- "gemini 3.5 flash": ModelSpec("Gemini 3.5 Flash", "gemini-3.5-flash"),
33
44
  }
34
45
 
35
46
 
47
+ def normalise_served_model(raw_model: str | None) -> ServedModelAttestation:
48
+ if not raw_model or not raw_model.strip():
49
+ return ServedModelAttestation.unknown()
50
+ model_id = raw_model.strip().lower().removeprefix("antigravity/")
51
+ return ServedModelAttestation(
52
+ raw_model, f"antigravity/{model_id}", "exact", "provider-output"
53
+ )
54
+
55
+
56
+ def observe_served_model(event: Mapping[str, Any]) -> str | None:
57
+ if event.get("event") != "init":
58
+ return None
59
+ init = event.get("init")
60
+ if not isinstance(init, Mapping):
61
+ return None
62
+ model = init.get("model")
63
+ return model if isinstance(model, str) and model.strip() else None
64
+
65
+
36
66
  # The wall-clock cap on one print run, carried over from the wrapper. It is
37
67
  # deliberately not derived from the idle budget: an analyser legitimately works
38
68
  # for far longer than it goes quiet, so folding the idle budget in here would
@@ -45,6 +75,29 @@ _CALL_STARTED = "ACTIVE"
45
75
  _CALL_FINISHED = "DONE"
46
76
 
47
77
 
78
+ class AntigravityExecutionNormalizer:
79
+ """Capture `agy models` once, then normalize the complete catalog."""
80
+
81
+ def snapshot(self, models, roles):
82
+ available = model_discovery.agy_models()
83
+ return snapshot_execution_normalizations(
84
+ models,
85
+ roles,
86
+ lambda execution, role: model_discovery.normalize_antigravity_execution(
87
+ execution=execution,
88
+ role=role,
89
+ available=available,
90
+ ),
91
+ )
92
+
93
+
94
+ def normalize_execution(execution: str, role: str) -> str:
95
+ """Compatibility entry point for direct adapter callers."""
96
+ return model_discovery.normalize_execution_for_dispatch(
97
+ provider="antigravity", execution=execution, role=role
98
+ )
99
+
100
+
48
101
  def step_update_events(event: Mapping[str, Any]) -> tuple[StreamEvent, ...]:
49
102
  """Normalise this CLI's stream-json, which shares no key with the other four.
50
103
 
@@ -151,6 +204,7 @@ class AntigravityExecution:
151
204
  stdin_text=None,
152
205
  stream_format=STREAM_JSON,
153
206
  normalise=step_update_events,
207
+ observe_served_model=observe_served_model,
154
208
  cwd=request.project_root,
155
209
  )
156
210
 
@@ -172,12 +226,26 @@ def create_provider() -> ProviderSpec:
172
226
  models=ANTIGRAVITY,
173
227
  default_models={
174
228
  role: "gemini-3.1-pro"
175
- for role in ("lead", "analyser", "critic", "executor", "verifier")
229
+ for role in (
230
+ "lead", "analyser", "critic", "designer", "planner", "executor",
231
+ "verifier", "report-writer", "translator",
232
+ )
176
233
  },
177
234
  wrapper="okstra-antigravity-exec.sh",
178
- supported_roles=frozenset({"lead", "analyser", "critic", "executor", "verifier"}),
235
+ supported_roles=ALL_ROLE_TOKENS,
236
+ execution_capabilities=frozenset(
237
+ {
238
+ "lead-session",
239
+ "worker-artifact-io",
240
+ "source-readonly",
241
+ "extended-artifact-authoring",
242
+ "project-mutation",
243
+ }
244
+ ),
179
245
  lead_launch=LeadLaunchSpec(
180
246
  executable="agy", model_flag="--model", prompt_flag="--prompt-interactive"
181
247
  ),
182
248
  exec_strategy=AntigravityExecution(),
249
+ execution_normalizer=AntigravityExecutionNormalizer(),
250
+ served_model_normalizer=normalise_served_model,
183
251
  )
@@ -1,5 +1,12 @@
1
1
  """Bundled Claude provider catalog."""
2
- from okstra_ctl.domain.provider import LeadLaunchSpec, ModelSpec, ProviderSpec
2
+ from typing import Any, Mapping
3
+ from okstra_ctl.domain.provider import (
4
+ LeadLaunchSpec,
5
+ ModelSpec,
6
+ ProviderSpec,
7
+ ServedModelAttestation,
8
+ )
9
+ from okstra_ctl.domain.role import ALL_ROLE_TOKENS
3
10
  from okstra_ctl.domain.worker_exec import (
4
11
  STREAM_JSON,
5
12
  ExecCommand,
@@ -10,32 +17,83 @@ from okstra_ctl.domain.worker_stream import content_block_events
10
17
 
11
18
 
12
19
  CLAUDE = {
13
- "fable": ModelSpec("fable", "fable", in_picker=True),
14
- "fable-5": ModelSpec("fable-5", "claude-fable-5"),
15
- "claude-fable-5": ModelSpec("fable-5", "claude-fable-5"),
16
- "opus": ModelSpec("opus", "opus", in_picker=True),
17
- "opus-5": ModelSpec("opus-5", "claude-opus-5"),
18
- "claude-opus-5": ModelSpec("opus-5", "claude-opus-5"),
19
- "opus-4-8": ModelSpec("opus-4-8", "claude-opus-4-8"),
20
- "claude-opus-4-8": ModelSpec("opus-4-8", "claude-opus-4-8"),
21
- "opus-4-7": ModelSpec("opus-4-7", "claude-opus-4-7"),
22
- "claude-opus-4-7": ModelSpec("opus-4-7", "claude-opus-4-7"),
23
- "opus-4-6": ModelSpec("opus-4-6", "claude-opus-4-6"),
24
- "claude-opus-4-6": ModelSpec("opus-4-6", "claude-opus-4-6"),
25
- "sonnet": ModelSpec("sonnet", "sonnet", in_picker=True),
26
- "sonnet-5": ModelSpec("sonnet-5", "claude-sonnet-5", in_picker=True),
27
- "claude-sonnet-5": ModelSpec("sonnet-5", "claude-sonnet-5"),
28
- "sonnet-4-6": ModelSpec("sonnet-4-6", "claude-sonnet-4-6"),
29
- "claude-sonnet-4-6": ModelSpec("sonnet-4-6", "claude-sonnet-4-6"),
30
- "haiku": ModelSpec("haiku", "haiku", in_picker=True),
31
- "haiku-4-5": ModelSpec("haiku-4-5", "claude-haiku-4-5"),
32
- "claude-haiku-4-5": ModelSpec("haiku-4-5", "claude-haiku-4-5"),
33
- "claude-haiku-4-5-20251001": ModelSpec(
34
- "haiku-4-5", "claude-haiku-4-5-20251001"
20
+ "fable": ModelSpec(
21
+ "fable", "fable", "fable", version_kind="channel", channel_family="fable"
22
+ ),
23
+ "fable-5": ModelSpec(
24
+ "fable-5", "fable-5", "claude-fable-5", aliases=("claude-fable-5",),
25
+ channel_family="fable",
26
+ ),
27
+ "opus": ModelSpec(
28
+ "opus", "opus", "opus", version_kind="channel", channel_family="opus"
29
+ ),
30
+ "opus-5": ModelSpec(
31
+ "opus-5", "opus-5", "claude-opus-5", aliases=("claude-opus-5",),
32
+ channel_family="opus",
33
+ ),
34
+ "opus-4-8": ModelSpec(
35
+ "opus-4-8", "opus-4-8", "claude-opus-4-8", aliases=("claude-opus-4-8",),
36
+ channel_family="opus",
37
+ ),
38
+ "opus-4-7": ModelSpec(
39
+ "opus-4-7", "opus-4-7", "claude-opus-4-7", aliases=("claude-opus-4-7",),
40
+ channel_family="opus",
41
+ ),
42
+ "opus-4-6": ModelSpec(
43
+ "opus-4-6", "opus-4-6", "claude-opus-4-6", aliases=("claude-opus-4-6",),
44
+ channel_family="opus",
45
+ ),
46
+ "sonnet": ModelSpec(
47
+ "sonnet", "sonnet", "sonnet", version_kind="channel", channel_family="sonnet"
48
+ ),
49
+ "sonnet-5": ModelSpec(
50
+ "sonnet-5", "sonnet-5", "claude-sonnet-5", aliases=("claude-sonnet-5",),
51
+ channel_family="sonnet",
52
+ ),
53
+ "sonnet-4-6": ModelSpec(
54
+ "sonnet-4-6", "sonnet-4-6", "claude-sonnet-4-6", aliases=("claude-sonnet-4-6",),
55
+ channel_family="sonnet",
56
+ ),
57
+ "haiku": ModelSpec(
58
+ "haiku", "haiku", "haiku", version_kind="channel", channel_family="haiku"
59
+ ),
60
+ "haiku-4-5": ModelSpec(
61
+ "haiku-4-5", "haiku-4-5", "claude-haiku-4-5", aliases=("claude-haiku-4-5",),
62
+ channel_family="haiku",
63
+ ),
64
+ "haiku-4-5-20251001": ModelSpec(
65
+ "haiku-4-5-20251001", "haiku-4-5", "claude-haiku-4-5-20251001",
66
+ channel_family="haiku",
35
67
  ),
36
68
  }
37
69
 
38
70
 
71
+ def normalise_served_model(raw_model: str | None) -> ServedModelAttestation:
72
+ if not raw_model or not raw_model.strip():
73
+ return ServedModelAttestation.unknown()
74
+ model_id = raw_model.strip().lower().removeprefix("claude-")
75
+ spec = CLAUDE.get(model_id)
76
+ level = (
77
+ "channel"
78
+ if spec is not None and spec.version_kind == "channel"
79
+ else "exact"
80
+ )
81
+ return ServedModelAttestation(
82
+ raw_model, f"claude/{model_id}", level, "provider-output"
83
+ )
84
+
85
+
86
+ def observe_served_model(event: Mapping[str, Any]) -> str | None:
87
+ if event.get("type") == "system" and event.get("subtype") == "init":
88
+ model = event.get("model")
89
+ return model if isinstance(model, str) and model.strip() else None
90
+ message = event.get("message")
91
+ if isinstance(message, Mapping):
92
+ model = message.get("model")
93
+ return model if isinstance(model, str) and model.strip() else None
94
+ return None
95
+
96
+
39
97
  # Opening the approval gate for a non-interactive run. Without it this CLI
40
98
  # auto-denies any tool call outside the seeded allowlist and the worker silently
41
99
  # routes around the denial, which reads as a thinner analysis rather than as a
@@ -73,6 +131,7 @@ class ClaudeExecution:
73
131
  stdin_text=request.prompt_text,
74
132
  stream_format=STREAM_JSON,
75
133
  normalise=content_block_events,
134
+ observe_served_model=observe_served_model,
76
135
  cwd=request.project_root,
77
136
  )
78
137
 
@@ -97,13 +156,23 @@ def create_provider() -> ProviderSpec:
97
156
  "lead": "opus",
98
157
  "analyser": "opus",
99
158
  "critic": "opus",
159
+ "designer": "opus",
160
+ "planner": "opus",
100
161
  "executor": "opus",
101
162
  "verifier": "opus",
102
163
  "report-writer": "sonnet",
164
+ "translator": "sonnet",
103
165
  },
104
166
  wrapper="okstra-claude-exec.sh",
105
- supported_roles=frozenset(
106
- {"lead", "analyser", "critic", "executor", "verifier", "report-writer"}
167
+ supported_roles=ALL_ROLE_TOKENS,
168
+ execution_capabilities=frozenset(
169
+ {
170
+ "lead-session",
171
+ "worker-artifact-io",
172
+ "source-readonly",
173
+ "extended-artifact-authoring",
174
+ "project-mutation",
175
+ }
107
176
  ),
108
177
  lead_launch=LeadLaunchSpec(
109
178
  executable="claude",
@@ -112,4 +181,5 @@ def create_provider() -> ProviderSpec:
112
181
  resume_session_id_flag="--resume",
113
182
  ),
114
183
  exec_strategy=ClaudeExecution(),
184
+ served_model_normalizer=normalise_served_model,
115
185
  )
@@ -1,28 +1,64 @@
1
1
  """Bundled Codex provider catalog."""
2
- from okstra_ctl.domain.provider import LeadLaunchSpec, ModelSpec, ProviderSpec
2
+ from okstra_ctl.domain.provider import (
3
+ LeadLaunchSpec,
4
+ ModelSpec,
5
+ ProviderSpec,
6
+ ServedModelAttestation,
7
+ snapshot_execution_normalizations,
8
+ )
9
+ from okstra_ctl.domain.role import ALL_ROLE_TOKENS
3
10
  from okstra_ctl.domain.worker_exec import (
4
11
  TEXT,
5
12
  ExecCommand,
6
13
  PolicySupport,
7
14
  WorkerExecRequest,
8
15
  )
16
+ import okstra_ctl.model_discovery as model_discovery
9
17
 
10
18
 
11
19
  CODEX = {
12
20
  # ChatGPT-account hosts serve this model without per-token billing.
13
- "gpt-5.6-sol": ModelSpec("gpt-5.6-sol", "gpt-5.6-sol", None, in_picker=True),
14
- "gpt-5.6": ModelSpec("gpt-5.6", "gpt-5.6", (5.00, 0.50, 30.0), in_picker=True),
15
- "gpt-5.5": ModelSpec("gpt-5.5", "gpt-5.5", (5.00, 0.50, 30.0), in_picker=True),
16
- "gpt-5.4": ModelSpec("gpt-5.4", "gpt-5.4", (2.50, 0.25, 15.00), in_picker=True),
17
- "gpt-5.4-mini": ModelSpec(
18
- "gpt-5.4-mini", "gpt-5.4-mini", (0.75, 0.075, 4.50), in_picker=True
19
- ),
20
- "gpt-5.3-codex": ModelSpec("gpt-5.4", "gpt-5.4"),
21
- "gpt-5.2": ModelSpec("gpt-5.2", "gpt-5.2", (1.75, 0.175, 14.0)),
22
- "codex-auto-review": ModelSpec("codex-auto-review", "codex-auto-review"),
21
+ "gpt-5.6-sol": ModelSpec("gpt-5.6-sol", "gpt-5.6-sol", "gpt-5.6-sol"),
22
+ "gpt-5.6": ModelSpec("gpt-5.6", "gpt-5.6", "gpt-5.6", pricing=(5.00, 0.50, 30.0)),
23
+ "gpt-5.5": ModelSpec("gpt-5.5", "gpt-5.5", "gpt-5.5", pricing=(5.00, 0.50, 30.0)),
24
+ "gpt-5.4": ModelSpec("gpt-5.4", "gpt-5.4", "gpt-5.4", aliases=("gpt-5.3-codex",), pricing=(2.50, 0.25, 15.00)),
25
+ "gpt-5.4-mini": ModelSpec("gpt-5.4-mini", "gpt-5.4-mini", "gpt-5.4-mini", pricing=(0.75, 0.075, 4.50)),
26
+ "gpt-5.2": ModelSpec("gpt-5.2", "gpt-5.2", "gpt-5.2", pricing=(1.75, 0.175, 14.0)),
27
+ "codex-auto-review": ModelSpec("codex-auto-review", "codex-auto-review", "codex-auto-review", selectable=False),
23
28
  }
24
29
 
25
30
 
31
+ def normalise_served_model(raw_model: str | None) -> ServedModelAttestation:
32
+ if not raw_model or not raw_model.strip():
33
+ return ServedModelAttestation.unknown()
34
+ model_id = raw_model.strip().lower().removeprefix("codex/")
35
+ return ServedModelAttestation(
36
+ raw_model, f"codex/{model_id}", "exact", "provider-output"
37
+ )
38
+
39
+
40
+ class CodexExecutionNormalizer:
41
+ """Capture Codex model availability once, then normalize the catalog."""
42
+
43
+ def snapshot(self, models, roles):
44
+ available = model_discovery.codex_availability()
45
+ return snapshot_execution_normalizations(
46
+ models,
47
+ roles,
48
+ lambda execution, _role: model_discovery.normalize_codex_execution(
49
+ execution=execution,
50
+ available=available,
51
+ ),
52
+ )
53
+
54
+
55
+ def normalize_execution(execution: str, role: str) -> str:
56
+ """Compatibility entry point for direct adapter callers."""
57
+ return model_discovery.normalize_execution_for_dispatch(
58
+ provider="codex", execution=execution, role=role
59
+ )
60
+
61
+
26
62
  class CodexExecution:
27
63
  """codex CLI invocation.
28
64
 
@@ -64,11 +100,18 @@ def create_provider() -> ProviderSpec:
64
100
  models=CODEX,
65
101
  default_models={
66
102
  role: "gpt-5.6-sol"
67
- for role in ("lead", "analyser", "critic", "executor", "verifier", "report-writer")
103
+ for role in ("lead", "analyser", "critic", "designer", "planner", "executor", "verifier", "report-writer", "translator")
68
104
  },
69
105
  wrapper="okstra-codex-exec.sh",
70
- supported_roles=frozenset(
71
- {"lead", "analyser", "critic", "executor", "verifier", "report-writer"}
106
+ supported_roles=ALL_ROLE_TOKENS,
107
+ execution_capabilities=frozenset(
108
+ {
109
+ "lead-session",
110
+ "worker-artifact-io",
111
+ "source-readonly",
112
+ "extended-artifact-authoring",
113
+ "project-mutation",
114
+ }
72
115
  ),
73
116
  lead_launch=LeadLaunchSpec(
74
117
  executable="codex",
@@ -81,4 +124,6 @@ def create_provider() -> ProviderSpec:
81
124
  ),
82
125
  ),
83
126
  exec_strategy=CodexExecution(),
127
+ execution_normalizer=CodexExecutionNormalizer(),
128
+ served_model_normalizer=normalise_served_model,
84
129
  )
@@ -1,5 +1,12 @@
1
1
  """Bundled Grok provider catalog."""
2
- from okstra_ctl.domain.provider import LeadLaunchSpec, ModelSpec, ProviderSpec
2
+ from typing import Any, Mapping
3
+ from okstra_ctl.domain.provider import (
4
+ LeadLaunchSpec,
5
+ ModelSpec,
6
+ ProviderSpec,
7
+ ServedModelAttestation,
8
+ )
9
+ from okstra_ctl.domain.role import ALL_ROLE_TOKENS
3
10
  from okstra_ctl.domain.worker_exec import (
4
11
  STREAM_JSON,
5
12
  ExecCommand,
@@ -10,12 +17,31 @@ from okstra_ctl.domain.worker_stream import content_block_events
10
17
 
11
18
 
12
19
  GROK = {
13
- "grok-build-0.1": ModelSpec(
14
- "grok-build-0.1", "grok-build-0.1", (1.00, 0.20, 2.00), in_picker=True
15
- ),
16
- "grok-4.5": ModelSpec("grok-4.5", "grok-4.5", (2.00, 0.30, 6.00), in_picker=True),
20
+ "grok-4.6": ModelSpec("grok-4.6", "grok-4.6", "grok-4.6", pricing=(2.00, 0.50, 6.00)),
21
+ "grok-4.5": ModelSpec("grok-4.5", "grok-4.5", "grok-4.5", pricing=(2.00, 0.30, 6.00)),
22
+ "grok-build-0.1": ModelSpec("grok-build-0.1", "grok-build-0.1", "grok-build-0.1", pricing=(1.00, 0.20, 2.00)),
17
23
  }
18
24
 
25
+
26
+ def normalise_served_model(raw_model: str | None) -> ServedModelAttestation:
27
+ if not raw_model or not raw_model.strip():
28
+ return ServedModelAttestation.unknown()
29
+ model_id = raw_model.strip().lower().removeprefix("grok/")
30
+ return ServedModelAttestation(
31
+ raw_model, f"grok/{model_id}", "exact", "provider-output"
32
+ )
33
+
34
+
35
+ def observe_served_model(event: Mapping[str, Any]) -> str | None:
36
+ model = event.get("model")
37
+ if isinstance(model, str) and model.strip():
38
+ return model
39
+ message = event.get("message")
40
+ if isinstance(message, Mapping):
41
+ model = message.get("model")
42
+ return model if isinstance(model, str) and model.strip() else None
43
+ return None
44
+
19
45
  GROK_LEAD_LAUNCH = LeadLaunchSpec(
20
46
  executable="grok",
21
47
  model_flag="--model",
@@ -48,6 +74,7 @@ class GrokExecution:
48
74
  stdin_text=None,
49
75
  stream_format=STREAM_JSON,
50
76
  normalise=content_block_events,
77
+ observe_served_model=observe_served_model,
51
78
  cwd=cwd,
52
79
  )
53
80
 
@@ -65,12 +92,31 @@ def create_provider() -> ProviderSpec:
65
92
  display_label="Grok",
66
93
  models=GROK,
67
94
  default_models={
68
- "lead": "grok-build-0.1",
69
- "analyser": "grok-build-0.1",
70
- "critic": "grok-4.5",
95
+ role: "grok-4.6"
96
+ for role in (
97
+ "lead",
98
+ "analyser",
99
+ "critic",
100
+ "designer",
101
+ "planner",
102
+ "executor",
103
+ "verifier",
104
+ "report-writer",
105
+ "translator",
106
+ )
71
107
  },
72
108
  wrapper="okstra-grok-exec.sh",
73
- supported_roles=frozenset({"lead", "analyser", "critic"}),
109
+ supported_roles=ALL_ROLE_TOKENS,
110
+ execution_capabilities=frozenset(
111
+ {
112
+ "lead-session",
113
+ "worker-artifact-io",
114
+ "source-readonly",
115
+ "extended-artifact-authoring",
116
+ "project-mutation",
117
+ }
118
+ ),
74
119
  lead_launch=GROK_LEAD_LAUNCH,
75
120
  exec_strategy=GrokExecution(),
121
+ served_model_normalizer=normalise_served_model,
76
122
  )