okstra 0.173.0 → 0.175.1

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 (402) 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/dist/commands/execute/team.mjs +56 -0
  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/dist/commands/execute/worktree-status.mjs +115 -0
  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/dist/commands/report/finalize.mjs +64 -0
  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 +17 -3
  232. package/docs/architecture.md +12 -22
  233. package/docs/cli.md +29 -8
  234. package/docs/container.md +1 -1
  235. package/docs/contributor-change-matrix.md +3 -3
  236. package/docs/project-structure-overview.md +29 -20
  237. package/docs/task-process/README.md +3 -1
  238. package/docs/task-process/implementation-planning.md +2 -2
  239. package/docs/task-process/implementation.md +1 -1
  240. package/package.json +11 -5
  241. package/runtime/BUILD.json +2 -2
  242. package/runtime/agents/workers/claude-worker.md +3 -2
  243. package/runtime/agents/workers/report-writer-worker.md +3 -1
  244. package/runtime/agents/workers/translator-worker.md +3 -1
  245. package/runtime/bin/lib/okstra/cli.sh +12 -0
  246. package/runtime/bin/lib/okstra/globals.sh +4 -1
  247. package/runtime/bin/lib/okstra/usage.sh +4 -4
  248. package/runtime/bin/okstra-error-log.py +27 -0
  249. package/runtime/bin/okstra-provider-exec.py +245 -16
  250. package/runtime/bin/okstra-trace-cleanup.sh +58 -129
  251. package/runtime/bin/okstra.sh +10 -0
  252. package/runtime/prompts/lead/adapters/cmux.md +2 -0
  253. package/runtime/prompts/lead/convergence.md +27 -18
  254. package/runtime/prompts/lead/okstra-lead-contract.md +4 -2
  255. package/runtime/prompts/lead/plan-body-verification.md +13 -6
  256. package/runtime/prompts/lead/report-writer.md +6 -6
  257. package/runtime/prompts/lead/team-contract.md +1 -1
  258. package/runtime/prompts/profiles/_common-contract.md +2 -2
  259. package/runtime/prompts/profiles/_implementation-executor.md +2 -0
  260. package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
  261. package/runtime/prompts/profiles/change-impact-analysis.md +16 -0
  262. package/runtime/prompts/profiles/error-analysis.md +21 -1
  263. package/runtime/prompts/profiles/feature-analysis.md +16 -0
  264. package/runtime/prompts/profiles/final-verification.md +16 -0
  265. package/runtime/prompts/profiles/implementation-option-selection.md +16 -0
  266. package/runtime/prompts/profiles/implementation-planning.md +32 -9
  267. package/runtime/prompts/profiles/implementation.md +17 -1
  268. package/runtime/prompts/profiles/improvement-discovery.md +16 -0
  269. package/runtime/prompts/profiles/project-analysis.md +16 -0
  270. package/runtime/prompts/profiles/release-handoff.md +5 -1
  271. package/runtime/prompts/profiles/requirements-discovery.md +20 -0
  272. package/runtime/prompts/wizard/prompts.ko.json +18 -0
  273. package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
  274. package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +8 -0
  275. package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +8 -0
  276. package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
  277. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +2 -2
  278. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +25 -5
  279. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  280. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +4 -1
  281. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
  282. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  283. package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +2 -2
  284. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +79 -11
  285. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +100 -25
  286. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +59 -14
  287. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +55 -9
  288. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +51 -12
  289. package/runtime/python/okstra_ctl/agent_activity.py +5 -1
  290. package/runtime/python/okstra_ctl/agent_invocation.py +608 -14
  291. package/runtime/python/okstra_ctl/agent_prompt_cli.py +249 -50
  292. package/runtime/python/okstra_ctl/application/resolve_assignment.py +53 -12
  293. package/runtime/python/okstra_ctl/assignment_environment.py +102 -0
  294. package/runtime/python/okstra_ctl/assignment_resolver.py +790 -0
  295. package/runtime/python/okstra_ctl/attempt_evidence.py +342 -0
  296. package/runtime/python/okstra_ctl/conformance.py +68 -0
  297. package/runtime/python/okstra_ctl/convergence.py +409 -3
  298. package/runtime/python/okstra_ctl/convergence_engine.py +248 -15
  299. package/runtime/python/okstra_ctl/convergence_store.py +170 -1
  300. package/runtime/python/okstra_ctl/dispatch_core.py +1181 -102
  301. package/runtime/python/okstra_ctl/dispatch_state.py +835 -22
  302. package/runtime/python/okstra_ctl/doctor.py +16 -2
  303. package/runtime/python/okstra_ctl/domain/host.py +32 -1
  304. package/runtime/python/okstra_ctl/domain/provider.py +212 -6
  305. package/runtime/python/okstra_ctl/domain/role.py +125 -0
  306. package/runtime/python/okstra_ctl/domain/wizard/interaction.py +13 -0
  307. package/runtime/python/okstra_ctl/domain/worker_exec.py +39 -1
  308. package/runtime/python/okstra_ctl/domain/worker_role.py +13 -3
  309. package/runtime/python/okstra_ctl/domain/worker_stream.py +24 -0
  310. package/runtime/python/okstra_ctl/error_log_write.py +9 -0
  311. package/runtime/python/okstra_ctl/error_report.py +21 -1
  312. package/runtime/python/okstra_ctl/execution_identity.py +529 -0
  313. package/runtime/python/okstra_ctl/execution_manifest.py +1281 -0
  314. package/runtime/python/okstra_ctl/execution_mutation_audit.py +619 -0
  315. package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
  316. package/runtime/python/okstra_ctl/index.py +6 -2
  317. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +43 -3
  318. package/runtime/python/okstra_ctl/legacy_model_selection.py +490 -0
  319. package/runtime/python/okstra_ctl/model_cli.py +278 -0
  320. package/runtime/python/okstra_ctl/model_defaults.py +46 -0
  321. package/runtime/python/okstra_ctl/model_discovery.py +26 -7
  322. package/runtime/python/okstra_ctl/model_pool.py +501 -0
  323. package/runtime/python/okstra_ctl/models.py +47 -27
  324. package/runtime/python/okstra_ctl/mutation_recovery.py +293 -0
  325. package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
  326. package/runtime/python/okstra_ctl/pane_title.py +151 -0
  327. package/runtime/python/okstra_ctl/ports/host_model.py +48 -17
  328. package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
  329. package/runtime/python/okstra_ctl/registry/provider_registry.py +8 -4
  330. package/runtime/python/okstra_ctl/render.py +78 -10
  331. package/runtime/python/okstra_ctl/render_final_report.py +34 -19
  332. package/runtime/python/okstra_ctl/report_contract.py +39 -1
  333. package/runtime/python/okstra_ctl/report_finalize.py +84 -9
  334. package/runtime/python/okstra_ctl/report_html/render.py +1 -0
  335. package/runtime/python/okstra_ctl/role_requirements.py +217 -0
  336. package/runtime/python/okstra_ctl/run.py +1250 -260
  337. package/runtime/python/okstra_ctl/run_index_row.py +7 -1
  338. package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
  339. package/runtime/python/okstra_ctl/scope_provenance.py +1 -1
  340. package/runtime/python/okstra_ctl/session.py +69 -12
  341. package/runtime/python/okstra_ctl/team.py +51 -25
  342. package/runtime/python/okstra_ctl/time_report.py +38 -4
  343. package/runtime/python/okstra_ctl/tmux.py +19 -149
  344. package/runtime/python/okstra_ctl/usage_identity.py +86 -0
  345. package/runtime/python/okstra_ctl/usage_report.py +15 -3
  346. package/runtime/python/okstra_ctl/wizard.py +1253 -62
  347. package/runtime/python/okstra_ctl/worker_audit_ledger.py +68 -65
  348. package/runtime/python/okstra_ctl/worker_request.py +9 -1
  349. package/runtime/python/okstra_ctl/worker_runner.py +130 -5
  350. package/runtime/python/okstra_ctl/workers.py +7 -5
  351. package/runtime/python/okstra_ctl/worktree.py +69 -3
  352. package/runtime/python/okstra_ctl/write_policy.py +571 -0
  353. package/runtime/python/okstra_token_usage/cli.py +1 -1
  354. package/runtime/python/okstra_token_usage/collect.py +66 -6
  355. package/runtime/schemas/convergence-groups-v2.0.schema.json +128 -0
  356. package/runtime/schemas/execution-manifest-v2.schema.json +340 -0
  357. package/runtime/schemas/final-report-v2.0.schema.json +25 -0
  358. package/runtime/skills/okstra-run/SKILL.md +9 -9
  359. package/runtime/skills/okstra-setup/references/project-config.md +11 -0
  360. package/runtime/templates/report-writer-prompt-preamble.md +2 -0
  361. package/runtime/templates/reports/final-report-v2.template.md +7 -1
  362. package/runtime/templates/reports/html/base.template.html +21 -0
  363. package/runtime/templates/reports/html/i18n/en.json +4 -0
  364. package/runtime/templates/reports/html/i18n/ko.json +4 -0
  365. package/runtime/templates/reports/settings.template.json +0 -24
  366. package/runtime/templates/worker-prompt-preamble.md +6 -2
  367. package/runtime/validators/lib/fixtures.sh +98 -33
  368. package/runtime/validators/validate-implementation-plan-stages.py +63 -3
  369. package/runtime/validators/validate-run.py +443 -508
  370. package/runtime/validators/validate_session_conformance.py +26 -8
  371. package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
  372. package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
  373. package/runtime/templates/reports/final-report.template.md +0 -1258
  374. package/src/cli-registry.mjs +0 -532
  375. package/src/commands/execute/codex-dispatch.mjs +0 -10
  376. package/src/commands/execute/plan-items.mjs +0 -9
  377. package/src/commands/execute/plan-validate.mjs +0 -68
  378. package/src/commands/execute/run.mjs +0 -208
  379. package/src/commands/execute/team.mjs +0 -63
  380. package/src/commands/execute/wizard.mjs +0 -167
  381. package/src/commands/inspect/stage-map.mjs +0 -98
  382. package/src/commands/inspect/task-list.mjs +0 -97
  383. package/src/commands/inspect/task-show.mjs +0 -100
  384. package/src/commands/lifecycle/check-project.mjs +0 -187
  385. package/src/commands/lifecycle/config.mjs +0 -385
  386. package/src/commands/lifecycle/doctor.mjs +0 -308
  387. package/src/commands/lifecycle/install.mjs +0 -1172
  388. package/src/commands/lifecycle/paths.mjs +0 -104
  389. package/src/commands/lifecycle/preflight.mjs +0 -125
  390. package/src/commands/lifecycle/setup.mjs +0 -333
  391. package/src/commands/lifecycle/uninstall.mjs +0 -279
  392. package/src/commands/memory/memory.mjs +0 -524
  393. package/src/commands/pr/pr.mjs +0 -261
  394. package/src/commands/report/finalize.mjs +0 -64
  395. package/src/lib/host-registry-client.mjs +0 -176
  396. package/src/lib/paths.mjs +0 -86
  397. package/src/lib/proc.mjs +0 -31
  398. package/src/lib/python-helper.mjs +0 -142
  399. package/src/lib/runtime-manifest.mjs +0 -70
  400. package/src/lib/skill-catalog.mjs +0 -53
  401. package/src/lib/version.mjs +0 -20
  402. /package/{src → dist}/commands/pr/default.md +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Scope
4
4
 
5
- This adapter maps the neutral Okstra lead operations to a generic host using Okstra-owned tmux panes. Read it only when the rendered launch prompt selects `leadRuntime=external`.
5
+ This adapter maps the neutral Okstra lead operations to a generic host. Workers run as Okstra-owned CLI wrapper subprocesses; the host is not asked to provide a pane for them. Read it only when the rendered launch prompt selects `leadRuntime=external`.
6
6
 
7
7
  ## Capability declaration
8
8
 
@@ -11,11 +11,11 @@ This adapter maps the neutral Okstra lead operations to a generic host using Oks
11
11
  | `runtime` | `external` |
12
12
  | `leadRoleLabel` | `Okstra lead` |
13
13
  | `userPromptMode` | `host-text` |
14
- | `workerDispatchBackend` | `tmux-pane` |
15
- | `initialPromptDeliveryMode` | `lazy-path-reference` |
14
+ | `workerDispatchBackend` | `cli-wrapper` |
15
+ | `initialPromptDeliveryMode` | `eager-include` |
16
16
  | `sessionAccounting` | `artifact-only` |
17
17
  | `resumeMode` | `artifact-checkpoint` |
18
- | `teardownMode` | `pane-teardown` |
18
+ | `teardownMode` | `process-cleanup` |
19
19
  | `leadEventSource` | `lead-events-jsonl` |
20
20
 
21
21
  ## Wizard interaction relay
@@ -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
@@ -54,6 +112,11 @@ class ClaudeExecution:
54
112
 
55
113
  def build_command(self, request: WorkerExecRequest) -> ExecCommand:
56
114
  argv = ["claude", "-p", "--model", request.model]
115
+ # The dispatcher's id, not the CLI's own: it is what team-state records,
116
+ # so token collection reads this worker's jsonl instead of guessing at
117
+ # `agentName` — which a pane-dispatched CLI never writes.
118
+ if request.session_id:
119
+ argv += ["--session-id", request.session_id]
57
120
  for directory in request.policy.write_scope:
58
121
  if directory != request.project_root:
59
122
  argv += ["--add-dir", str(directory)]
@@ -68,6 +131,7 @@ class ClaudeExecution:
68
131
  stdin_text=request.prompt_text,
69
132
  stream_format=STREAM_JSON,
70
133
  normalise=content_block_events,
134
+ observe_served_model=observe_served_model,
71
135
  cwd=request.project_root,
72
136
  )
73
137
 
@@ -92,13 +156,23 @@ def create_provider() -> ProviderSpec:
92
156
  "lead": "opus",
93
157
  "analyser": "opus",
94
158
  "critic": "opus",
159
+ "designer": "opus",
160
+ "planner": "opus",
95
161
  "executor": "opus",
96
162
  "verifier": "opus",
97
163
  "report-writer": "sonnet",
164
+ "translator": "sonnet",
98
165
  },
99
166
  wrapper="okstra-claude-exec.sh",
100
- supported_roles=frozenset(
101
- {"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
+ }
102
176
  ),
103
177
  lead_launch=LeadLaunchSpec(
104
178
  executable="claude",
@@ -107,4 +181,5 @@ def create_provider() -> ProviderSpec:
107
181
  resume_session_id_flag="--resume",
108
182
  ),
109
183
  exec_strategy=ClaudeExecution(),
184
+ served_model_normalizer=normalise_served_model,
110
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
  )
@@ -1,5 +1,12 @@
1
1
  """Bundled Kimi 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,18 +17,33 @@ from okstra_ctl.domain.worker_stream import content_block_events
10
17
 
11
18
 
12
19
  KIMI = {
13
- "kimi-k2.7-code": ModelSpec(
14
- "Kimi K2.7 Code", "kimi-k2.7-code", (0.95, 0.19, 4.00), in_picker=True
15
- ),
16
- "kimi k2.7 code": ModelSpec("Kimi K2.7 Code", "kimi-k2.7-code"),
17
- "kimi-for-coding": ModelSpec("Kimi K2.7 Code", "kimi-for-coding", in_picker=True),
18
- "kimi-k3": ModelSpec("Kimi K3", "kimi-k3", (3.00, 0.30, 15.00), in_picker=True),
19
- "kimi k3": ModelSpec("Kimi K3", "kimi-k3"),
20
- "k3": ModelSpec("Kimi K3", "k3", in_picker=True),
21
- "k3-256k": ModelSpec("Kimi K3 256K", "k3-256k", in_picker=True),
22
- "kimi k3 256k": ModelSpec("Kimi K3 256K", "k3-256k"),
20
+ "kimi-k2.7-code": ModelSpec("kimi-k2.7-code", "Kimi K2.7 Code", "kimi-k2.7-code", aliases=("kimi k2.7 code",), pricing=(0.95, 0.19, 4.00)),
21
+ "kimi-for-coding": ModelSpec("kimi-for-coding", "Kimi K2.7 Code", "kimi-for-coding"),
22
+ "kimi-k3": ModelSpec("kimi-k3", "Kimi K3", "kimi-k3", aliases=("kimi k3",), pricing=(3.00, 0.30, 15.00)),
23
+ "k3": ModelSpec("k3", "Kimi K3", "k3"),
24
+ "k3-256k": ModelSpec("k3-256k", "Kimi K3 256K", "k3-256k", aliases=("kimi k3 256k",)),
23
25
  }
24
26
 
27
+
28
+ def normalise_served_model(raw_model: str | None) -> ServedModelAttestation:
29
+ if not raw_model or not raw_model.strip():
30
+ return ServedModelAttestation.unknown()
31
+ model_id = raw_model.strip().lower().removeprefix("kimi/")
32
+ return ServedModelAttestation(
33
+ raw_model, f"kimi/{model_id}", "exact", "provider-output"
34
+ )
35
+
36
+
37
+ def observe_served_model(event: Mapping[str, Any]) -> str | None:
38
+ model = event.get("model")
39
+ if isinstance(model, str) and model.strip():
40
+ return model
41
+ message = event.get("message")
42
+ if isinstance(message, Mapping):
43
+ model = message.get("model")
44
+ return model if isinstance(model, str) and model.strip() else None
45
+ return None
46
+
25
47
  KIMI_LEAD_LAUNCH = LeadLaunchSpec(
26
48
  executable="kimi",
27
49
  model_flag="--model",
@@ -52,6 +74,7 @@ class KimiExecution:
52
74
  stdin_text=None,
53
75
  stream_format=STREAM_JSON,
54
76
  normalise=content_block_events,
77
+ observe_served_model=observe_served_model,
55
78
  cwd=cwd,
56
79
  )
57
80
 
@@ -72,9 +95,25 @@ def create_provider() -> ProviderSpec:
72
95
  "lead": "kimi-k2.7-code",
73
96
  "analyser": "kimi-k2.7-code",
74
97
  "critic": "kimi-k3",
98
+ "designer": "kimi-k2.7-code",
99
+ "planner": "kimi-k2.7-code",
100
+ "executor": "kimi-k2.7-code",
101
+ "verifier": "kimi-k2.7-code",
102
+ "report-writer": "kimi-k2.7-code",
103
+ "translator": "kimi-k2.7-code",
75
104
  },
76
105
  wrapper="okstra-kimi-exec.sh",
77
- supported_roles=frozenset({"lead", "analyser", "critic"}),
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
+ }
115
+ ),
78
116
  lead_launch=KIMI_LEAD_LAUNCH,
79
117
  exec_strategy=KimiExecution(),
118
+ served_model_normalizer=normalise_served_model,
80
119
  )
@@ -113,7 +113,11 @@ def _activity_row(event: LeadEvent) -> dict[str, Any]:
113
113
  or _ACTIVITY_ID_RE.fullmatch(activity_id) is None
114
114
  ):
115
115
  raise ActivityProjectionError(f"invalid activityId: {activity_id}")
116
- return {key: details[key] for key in ACTIVITY_FIELDS}
116
+ from okstra_ctl.execution_identity import stored_identity
117
+
118
+ row = {key: details[key] for key in ACTIVITY_FIELDS}
119
+ row.update(stored_identity(details))
120
+ return row
117
121
 
118
122
 
119
123
  def _validate_activity_order(rows: Sequence[Mapping[str, Any]]) -> None: