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.
- package/README.md +18 -9
- package/bin/okstra +8 -4
- package/dist/cli-registry.d.mts +16 -0
- package/dist/cli-registry.mjs +560 -0
- package/dist/cli-registry.mjs.map +1 -0
- package/dist/commands/execute/agent-prompt.d.mts +1 -0
- package/{src → dist}/commands/execute/agent-prompt.mjs +10 -11
- package/dist/commands/execute/agent-prompt.mjs.map +1 -0
- package/dist/commands/execute/codex-dispatch.d.mts +3 -0
- package/dist/commands/execute/codex-dispatch.mjs +6 -0
- package/dist/commands/execute/codex-dispatch.mjs.map +1 -0
- package/dist/commands/execute/codex-run.d.mts +3 -0
- package/{src → dist}/commands/execute/codex-run.mjs +30 -37
- package/dist/commands/execute/codex-run.mjs.map +1 -0
- package/dist/commands/execute/convergence.d.mts +1 -0
- package/{src → dist}/commands/execute/convergence.mjs +15 -16
- package/dist/commands/execute/convergence.mjs.map +1 -0
- package/dist/commands/execute/error-log.d.mts +1 -0
- package/{src → dist}/commands/execute/error-log.mjs +2 -3
- package/dist/commands/execute/error-log.mjs.map +1 -0
- package/dist/commands/execute/git-reconcile.d.mts +1 -0
- package/{src → dist}/commands/execute/git-reconcile.mjs +10 -11
- package/dist/commands/execute/git-reconcile.mjs.map +1 -0
- package/dist/commands/execute/handoff.d.mts +1 -0
- package/{src → dist}/commands/execute/handoff.mjs +10 -11
- package/dist/commands/execute/handoff.mjs.map +1 -0
- package/dist/commands/execute/incremental-carry.d.mts +1 -0
- package/{src → dist}/commands/execute/incremental-carry.mjs +2 -3
- package/dist/commands/execute/incremental-carry.mjs.map +1 -0
- package/dist/commands/execute/incremental-scope.d.mts +1 -0
- package/{src → dist}/commands/execute/incremental-scope.mjs +2 -3
- package/dist/commands/execute/incremental-scope.mjs.map +1 -0
- package/dist/commands/execute/integrate-stages.d.mts +1 -0
- package/{src → dist}/commands/execute/integrate-stages.mjs +10 -11
- package/dist/commands/execute/integrate-stages.mjs.map +1 -0
- package/dist/commands/execute/pane-title.d.mts +1 -0
- package/dist/commands/execute/pane-title.mjs +20 -0
- package/dist/commands/execute/pane-title.mjs.map +1 -0
- package/dist/commands/execute/plan-items.d.mts +1 -0
- package/dist/commands/execute/plan-items.mjs +9 -0
- package/dist/commands/execute/plan-items.mjs.map +1 -0
- package/dist/commands/execute/plan-validate.d.mts +1 -0
- package/dist/commands/execute/plan-validate.mjs +68 -0
- package/dist/commands/execute/plan-validate.mjs.map +1 -0
- package/dist/commands/execute/plan-verify.d.mts +1 -0
- package/{src → dist}/commands/execute/plan-verify.mjs +13 -16
- package/dist/commands/execute/plan-verify.mjs.map +1 -0
- package/dist/commands/execute/render-bundle.d.mts +3 -0
- package/{src → dist}/commands/execute/render-bundle.mjs +65 -61
- package/dist/commands/execute/render-bundle.mjs.map +1 -0
- package/dist/commands/execute/run.d.mts +24 -0
- package/dist/commands/execute/run.mjs +196 -0
- package/dist/commands/execute/run.mjs.map +1 -0
- package/dist/commands/execute/spawn-followups.d.mts +1 -0
- package/{src → dist}/commands/execute/spawn-followups.mjs +6 -7
- package/dist/commands/execute/spawn-followups.mjs.map +1 -0
- package/dist/commands/execute/team.d.mts +3 -0
- package/dist/commands/execute/team.mjs +56 -0
- package/dist/commands/execute/team.mjs.map +1 -0
- package/dist/commands/execute/token-usage.d.mts +1 -0
- package/{src → dist}/commands/execute/token-usage.mjs +2 -3
- package/dist/commands/execute/token-usage.mjs.map +1 -0
- package/dist/commands/execute/wizard.d.mts +8 -0
- package/dist/commands/execute/wizard.mjs +191 -0
- package/dist/commands/execute/wizard.mjs.map +1 -0
- package/dist/commands/execute/worker-audit-check.d.mts +1 -0
- package/{src → dist}/commands/execute/worker-audit-check.mjs +11 -12
- package/dist/commands/execute/worker-audit-check.mjs.map +1 -0
- package/dist/commands/execute/worker-dispatch.d.mts +7 -0
- package/{src → dist}/commands/execute/worker-dispatch.mjs +34 -46
- package/dist/commands/execute/worker-dispatch.mjs.map +1 -0
- package/dist/commands/execute/worker-state.d.mts +1 -0
- package/{src → dist}/commands/execute/worker-state.mjs +15 -16
- package/dist/commands/execute/worker-state.mjs.map +1 -0
- package/dist/commands/execute/worktree-lookup.d.mts +1 -0
- package/{src → dist}/commands/execute/worktree-lookup.mjs +38 -37
- package/dist/commands/execute/worktree-lookup.mjs.map +1 -0
- package/dist/commands/execute/worktree-status.d.mts +1 -0
- package/dist/commands/execute/worktree-status.mjs +115 -0
- package/dist/commands/execute/worktree-status.mjs.map +1 -0
- package/dist/commands/inspect/code-review.d.mts +1 -0
- package/{src → dist}/commands/inspect/code-review.mjs +16 -19
- package/dist/commands/inspect/code-review.mjs.map +1 -0
- package/dist/commands/inspect/container.d.mts +1 -0
- package/{src → dist}/commands/inspect/container.mjs +11 -13
- package/dist/commands/inspect/container.mjs.map +1 -0
- package/dist/commands/inspect/context-cost.d.mts +1 -0
- package/{src → dist}/commands/inspect/context-cost.mjs +11 -13
- package/dist/commands/inspect/context-cost.mjs.map +1 -0
- package/dist/commands/inspect/design-prep.d.mts +1 -0
- package/{src → dist}/commands/inspect/design-prep.mjs +10 -11
- package/dist/commands/inspect/design-prep.mjs.map +1 -0
- package/dist/commands/inspect/error-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/error-report.mjs +11 -13
- package/dist/commands/inspect/error-report.mjs.map +1 -0
- package/dist/commands/inspect/error-zip.d.mts +1 -0
- package/{src → dist}/commands/inspect/error-zip.mjs +11 -12
- package/dist/commands/inspect/error-zip.mjs.map +1 -0
- package/dist/commands/inspect/log-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/log-report.mjs +11 -13
- package/dist/commands/inspect/log-report.mjs.map +1 -0
- package/dist/commands/inspect/profile-show.d.mts +1 -0
- package/{src → dist}/commands/inspect/profile-show.mjs +10 -11
- package/dist/commands/inspect/profile-show.mjs.map +1 -0
- package/dist/commands/inspect/recap.d.mts +1 -0
- package/{src → dist}/commands/inspect/recap.mjs +11 -13
- package/dist/commands/inspect/recap.mjs.map +1 -0
- package/dist/commands/inspect/resolve-task-key.d.mts +1 -0
- package/{src → dist}/commands/inspect/resolve-task-key.mjs +11 -13
- package/dist/commands/inspect/resolve-task-key.mjs.map +1 -0
- package/dist/commands/inspect/rollup.d.mts +1 -0
- package/{src → dist}/commands/inspect/rollup.mjs +11 -13
- package/dist/commands/inspect/rollup.mjs.map +1 -0
- package/dist/commands/inspect/run-audit.d.mts +1 -0
- package/{src → dist}/commands/inspect/run-audit.mjs +11 -12
- package/dist/commands/inspect/run-audit.mjs.map +1 -0
- package/dist/commands/inspect/set-work-status.d.mts +1 -0
- package/{src → dist}/commands/inspect/set-work-status.mjs +11 -13
- package/dist/commands/inspect/set-work-status.mjs.map +1 -0
- package/dist/commands/inspect/stage-map.d.mts +1 -0
- package/dist/commands/inspect/stage-map.mjs +110 -0
- package/dist/commands/inspect/stage-map.mjs.map +1 -0
- package/dist/commands/inspect/task-list.d.mts +1 -0
- package/dist/commands/inspect/task-list.mjs +103 -0
- package/dist/commands/inspect/task-list.mjs.map +1 -0
- package/dist/commands/inspect/task-show.d.mts +1 -0
- package/dist/commands/inspect/task-show.mjs +108 -0
- package/dist/commands/inspect/task-show.mjs.map +1 -0
- package/dist/commands/inspect/time-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/time-report.mjs +11 -13
- package/dist/commands/inspect/time-report.mjs.map +1 -0
- package/dist/commands/inspect/usage-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/usage-report.mjs +11 -13
- package/dist/commands/inspect/usage-report.mjs.map +1 -0
- package/dist/commands/inspect/user-response.d.mts +1 -0
- package/{src → dist}/commands/inspect/user-response.mjs +10 -11
- package/dist/commands/inspect/user-response.mjs.map +1 -0
- package/dist/commands/inspect/worker-liveness.d.mts +1 -0
- package/{src → dist}/commands/inspect/worker-liveness.mjs +11 -13
- package/dist/commands/inspect/worker-liveness.mjs.map +1 -0
- package/dist/commands/lifecycle/check-project.d.mts +21 -0
- package/dist/commands/lifecycle/check-project.mjs +175 -0
- package/dist/commands/lifecycle/check-project.mjs.map +1 -0
- package/dist/commands/lifecycle/config.d.mts +1 -0
- package/dist/commands/lifecycle/config.mjs +369 -0
- package/dist/commands/lifecycle/config.mjs.map +1 -0
- package/dist/commands/lifecycle/doctor.d.mts +10 -0
- package/dist/commands/lifecycle/doctor.mjs +317 -0
- package/dist/commands/lifecycle/doctor.mjs.map +1 -0
- package/dist/commands/lifecycle/install.d.mts +17 -0
- package/dist/commands/lifecycle/install.mjs +1077 -0
- package/dist/commands/lifecycle/install.mjs.map +1 -0
- package/dist/commands/lifecycle/migrate.d.mts +1 -0
- package/{src → dist}/commands/lifecycle/migrate.mjs +10 -11
- package/dist/commands/lifecycle/migrate.mjs.map +1 -0
- package/dist/commands/lifecycle/model.d.mts +1 -0
- package/dist/commands/lifecycle/model.mjs +22 -0
- package/dist/commands/lifecycle/model.mjs.map +1 -0
- package/dist/commands/lifecycle/paths.d.mts +1 -0
- package/dist/commands/lifecycle/paths.mjs +102 -0
- package/dist/commands/lifecycle/paths.mjs.map +1 -0
- package/dist/commands/lifecycle/preflight.d.mts +1 -0
- package/dist/commands/lifecycle/preflight.mjs +124 -0
- package/dist/commands/lifecycle/preflight.mjs.map +1 -0
- package/dist/commands/lifecycle/setup.d.mts +1 -0
- package/dist/commands/lifecycle/setup.mjs +297 -0
- package/dist/commands/lifecycle/setup.mjs.map +1 -0
- package/dist/commands/lifecycle/uninstall.d.mts +4 -0
- package/dist/commands/lifecycle/uninstall.mjs +262 -0
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -0
- package/dist/commands/manager.d.mts +3 -0
- package/{src → dist}/commands/manager.mjs +25 -32
- package/dist/commands/manager.mjs.map +1 -0
- package/dist/commands/memory/memory.d.mts +1 -0
- package/dist/commands/memory/memory.mjs +569 -0
- package/dist/commands/memory/memory.mjs.map +1 -0
- package/dist/commands/pr/pr.d.mts +33 -0
- package/dist/commands/pr/pr.mjs +296 -0
- package/dist/commands/pr/pr.mjs.map +1 -0
- package/dist/commands/report/agent-activity.d.mts +1 -0
- package/{src → dist}/commands/report/agent-activity.mjs +11 -12
- package/dist/commands/report/agent-activity.mjs.map +1 -0
- package/dist/commands/report/finalize.d.mts +4 -0
- package/dist/commands/report/finalize.mjs +64 -0
- package/dist/commands/report/finalize.mjs.map +1 -0
- package/dist/commands/report/inject-report-index.d.mts +1 -0
- package/{src → dist}/commands/report/inject-report-index.mjs +6 -7
- package/dist/commands/report/inject-report-index.mjs.map +1 -0
- package/dist/commands/report/render-final-report.d.mts +1 -0
- package/{src → dist}/commands/report/render-final-report.mjs +6 -7
- package/dist/commands/report/render-final-report.mjs.map +1 -0
- package/dist/commands/report/render-views.d.mts +1 -0
- package/{src → dist}/commands/report/render-views.mjs +6 -7
- package/dist/commands/report/render-views.mjs.map +1 -0
- package/dist/commands/report/translate.d.mts +1 -0
- package/{src → dist}/commands/report/translate.mjs +6 -7
- package/dist/commands/report/translate.mjs.map +1 -0
- package/dist/lib/helper-scripts.d.mts +3 -0
- package/{src → dist}/lib/helper-scripts.mjs +11 -14
- package/dist/lib/helper-scripts.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +25 -0
- package/dist/lib/host-registry-client.mjs +152 -0
- package/dist/lib/host-registry-client.mjs.map +1 -0
- package/dist/lib/install-assets.d.mts +8 -0
- package/{src → dist}/lib/install-assets.mjs +17 -20
- package/dist/lib/install-assets.mjs.map +1 -0
- package/dist/lib/okstra-dirs.d.mts +4 -0
- package/{src → dist}/lib/okstra-dirs.mjs +3 -6
- package/dist/lib/okstra-dirs.mjs.map +1 -0
- package/dist/lib/paths.d.mts +4 -0
- package/dist/lib/paths.mjs +78 -0
- package/dist/lib/paths.mjs.map +1 -0
- package/dist/lib/proc.d.mts +3 -0
- package/dist/lib/proc.mjs +31 -0
- package/dist/lib/proc.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +8 -0
- package/dist/lib/python-helper.mjs +123 -0
- package/dist/lib/python-helper.mjs.map +1 -0
- package/dist/lib/runtime-manifest.d.mts +7 -0
- package/dist/lib/runtime-manifest.mjs +73 -0
- package/dist/lib/runtime-manifest.mjs.map +1 -0
- package/dist/lib/skill-catalog.d.mts +4 -0
- package/dist/lib/skill-catalog.mjs +50 -0
- package/dist/lib/skill-catalog.mjs.map +1 -0
- package/dist/lib/types.d.mts +129 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/lib/version.d.mts +2 -0
- package/dist/lib/version.mjs +27 -0
- package/dist/lib/version.mjs.map +1 -0
- package/docs/architecture/storage-model.md +17 -3
- package/docs/architecture.md +12 -22
- package/docs/cli.md +29 -8
- package/docs/container.md +1 -1
- package/docs/contributor-change-matrix.md +3 -3
- package/docs/project-structure-overview.md +29 -20
- package/docs/task-process/README.md +3 -1
- package/docs/task-process/implementation-planning.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/package.json +11 -5
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +3 -2
- package/runtime/agents/workers/report-writer-worker.md +3 -1
- package/runtime/agents/workers/translator-worker.md +3 -1
- package/runtime/bin/lib/okstra/cli.sh +12 -0
- package/runtime/bin/lib/okstra/globals.sh +4 -1
- package/runtime/bin/lib/okstra/usage.sh +4 -4
- package/runtime/bin/okstra-error-log.py +27 -0
- package/runtime/bin/okstra-provider-exec.py +245 -16
- package/runtime/bin/okstra-trace-cleanup.sh +58 -129
- package/runtime/bin/okstra.sh +10 -0
- package/runtime/prompts/lead/adapters/cmux.md +2 -0
- package/runtime/prompts/lead/convergence.md +27 -18
- package/runtime/prompts/lead/okstra-lead-contract.md +4 -2
- package/runtime/prompts/lead/plan-body-verification.md +13 -6
- package/runtime/prompts/lead/report-writer.md +6 -6
- package/runtime/prompts/lead/team-contract.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/change-impact-analysis.md +16 -0
- package/runtime/prompts/profiles/error-analysis.md +21 -1
- package/runtime/prompts/profiles/feature-analysis.md +16 -0
- package/runtime/prompts/profiles/final-verification.md +16 -0
- package/runtime/prompts/profiles/implementation-option-selection.md +16 -0
- package/runtime/prompts/profiles/implementation-planning.md +32 -9
- package/runtime/prompts/profiles/implementation.md +17 -1
- package/runtime/prompts/profiles/improvement-discovery.md +16 -0
- package/runtime/prompts/profiles/project-analysis.md +16 -0
- package/runtime/prompts/profiles/release-handoff.md +5 -1
- package/runtime/prompts/profiles/requirements-discovery.md +20 -0
- package/runtime/prompts/wizard/prompts.ko.json +18 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
- package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +8 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +8 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +25 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +4 -1
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +79 -11
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +100 -25
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +59 -14
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +55 -9
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +51 -12
- package/runtime/python/okstra_ctl/agent_activity.py +5 -1
- package/runtime/python/okstra_ctl/agent_invocation.py +608 -14
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +249 -50
- package/runtime/python/okstra_ctl/application/resolve_assignment.py +53 -12
- package/runtime/python/okstra_ctl/assignment_environment.py +102 -0
- package/runtime/python/okstra_ctl/assignment_resolver.py +790 -0
- package/runtime/python/okstra_ctl/attempt_evidence.py +342 -0
- package/runtime/python/okstra_ctl/conformance.py +68 -0
- package/runtime/python/okstra_ctl/convergence.py +409 -3
- package/runtime/python/okstra_ctl/convergence_engine.py +248 -15
- package/runtime/python/okstra_ctl/convergence_store.py +170 -1
- package/runtime/python/okstra_ctl/dispatch_core.py +1181 -102
- package/runtime/python/okstra_ctl/dispatch_state.py +835 -22
- package/runtime/python/okstra_ctl/doctor.py +16 -2
- package/runtime/python/okstra_ctl/domain/host.py +32 -1
- package/runtime/python/okstra_ctl/domain/provider.py +212 -6
- package/runtime/python/okstra_ctl/domain/role.py +125 -0
- package/runtime/python/okstra_ctl/domain/wizard/interaction.py +13 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +39 -1
- package/runtime/python/okstra_ctl/domain/worker_role.py +13 -3
- package/runtime/python/okstra_ctl/domain/worker_stream.py +24 -0
- package/runtime/python/okstra_ctl/error_log_write.py +9 -0
- package/runtime/python/okstra_ctl/error_report.py +21 -1
- package/runtime/python/okstra_ctl/execution_identity.py +529 -0
- package/runtime/python/okstra_ctl/execution_manifest.py +1281 -0
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +619 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
- package/runtime/python/okstra_ctl/index.py +6 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +43 -3
- package/runtime/python/okstra_ctl/legacy_model_selection.py +490 -0
- package/runtime/python/okstra_ctl/model_cli.py +278 -0
- package/runtime/python/okstra_ctl/model_defaults.py +46 -0
- package/runtime/python/okstra_ctl/model_discovery.py +26 -7
- package/runtime/python/okstra_ctl/model_pool.py +501 -0
- package/runtime/python/okstra_ctl/models.py +47 -27
- package/runtime/python/okstra_ctl/mutation_recovery.py +293 -0
- package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
- package/runtime/python/okstra_ctl/pane_title.py +151 -0
- package/runtime/python/okstra_ctl/ports/host_model.py +48 -17
- package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
- package/runtime/python/okstra_ctl/registry/provider_registry.py +8 -4
- package/runtime/python/okstra_ctl/render.py +78 -10
- package/runtime/python/okstra_ctl/render_final_report.py +34 -19
- package/runtime/python/okstra_ctl/report_contract.py +39 -1
- package/runtime/python/okstra_ctl/report_finalize.py +84 -9
- package/runtime/python/okstra_ctl/report_html/render.py +1 -0
- package/runtime/python/okstra_ctl/role_requirements.py +217 -0
- package/runtime/python/okstra_ctl/run.py +1250 -260
- package/runtime/python/okstra_ctl/run_index_row.py +7 -1
- package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
- package/runtime/python/okstra_ctl/scope_provenance.py +1 -1
- package/runtime/python/okstra_ctl/session.py +69 -12
- package/runtime/python/okstra_ctl/team.py +51 -25
- package/runtime/python/okstra_ctl/time_report.py +38 -4
- package/runtime/python/okstra_ctl/tmux.py +19 -149
- package/runtime/python/okstra_ctl/usage_identity.py +86 -0
- package/runtime/python/okstra_ctl/usage_report.py +15 -3
- package/runtime/python/okstra_ctl/wizard.py +1253 -62
- package/runtime/python/okstra_ctl/worker_audit_ledger.py +68 -65
- package/runtime/python/okstra_ctl/worker_request.py +9 -1
- package/runtime/python/okstra_ctl/worker_runner.py +130 -5
- package/runtime/python/okstra_ctl/workers.py +7 -5
- package/runtime/python/okstra_ctl/worktree.py +69 -3
- package/runtime/python/okstra_ctl/write_policy.py +571 -0
- package/runtime/python/okstra_token_usage/cli.py +1 -1
- package/runtime/python/okstra_token_usage/collect.py +66 -6
- package/runtime/schemas/convergence-groups-v2.0.schema.json +128 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +340 -0
- package/runtime/schemas/final-report-v2.0.schema.json +25 -0
- package/runtime/skills/okstra-run/SKILL.md +9 -9
- package/runtime/skills/okstra-setup/references/project-config.md +11 -0
- package/runtime/templates/report-writer-prompt-preamble.md +2 -0
- package/runtime/templates/reports/final-report-v2.template.md +7 -1
- package/runtime/templates/reports/html/base.template.html +21 -0
- package/runtime/templates/reports/html/i18n/en.json +4 -0
- package/runtime/templates/reports/html/i18n/ko.json +4 -0
- package/runtime/templates/reports/settings.template.json +0 -24
- package/runtime/templates/worker-prompt-preamble.md +6 -2
- package/runtime/validators/lib/fixtures.sh +98 -33
- package/runtime/validators/validate-implementation-plan-stages.py +63 -3
- package/runtime/validators/validate-run.py +443 -508
- package/runtime/validators/validate_session_conformance.py +26 -8
- package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
- package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
- package/runtime/templates/reports/final-report.template.md +0 -1258
- package/src/cli-registry.mjs +0 -532
- package/src/commands/execute/codex-dispatch.mjs +0 -10
- package/src/commands/execute/plan-items.mjs +0 -9
- package/src/commands/execute/plan-validate.mjs +0 -68
- package/src/commands/execute/run.mjs +0 -208
- package/src/commands/execute/team.mjs +0 -63
- package/src/commands/execute/wizard.mjs +0 -167
- package/src/commands/inspect/stage-map.mjs +0 -98
- package/src/commands/inspect/task-list.mjs +0 -97
- package/src/commands/inspect/task-show.mjs +0 -100
- package/src/commands/lifecycle/check-project.mjs +0 -187
- package/src/commands/lifecycle/config.mjs +0 -385
- package/src/commands/lifecycle/doctor.mjs +0 -308
- package/src/commands/lifecycle/install.mjs +0 -1172
- package/src/commands/lifecycle/paths.mjs +0 -104
- package/src/commands/lifecycle/preflight.mjs +0 -125
- package/src/commands/lifecycle/setup.mjs +0 -333
- package/src/commands/lifecycle/uninstall.mjs +0 -279
- package/src/commands/memory/memory.mjs +0 -524
- package/src/commands/pr/pr.mjs +0 -261
- package/src/commands/report/finalize.mjs +0 -64
- package/src/lib/host-registry-client.mjs +0 -176
- package/src/lib/paths.mjs +0 -86
- package/src/lib/proc.mjs +0 -31
- package/src/lib/python-helper.mjs +0 -142
- package/src/lib/runtime-manifest.mjs +0 -70
- package/src/lib/skill-catalog.mjs +0 -53
- package/src/lib/version.mjs +0 -20
- /package/{src → dist}/commands/pr/default.md +0 -0
|
@@ -36,6 +36,20 @@ class DoctorCheck:
|
|
|
36
36
|
return asdict(self)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def model_pool_diagnostics(
|
|
40
|
+
*,
|
|
41
|
+
host_runtime: str = "claude-code",
|
|
42
|
+
cwd: str | Path | None = None,
|
|
43
|
+
) -> dict[str, object]:
|
|
44
|
+
"""Diagnose the model pool without making an inference call."""
|
|
45
|
+
from .model_cli import model_diagnostics
|
|
46
|
+
|
|
47
|
+
return model_diagnostics(
|
|
48
|
+
host_runtime=host_runtime,
|
|
49
|
+
cwd=None if cwd is None else Path(cwd),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
39
53
|
def phase_diagnostics(
|
|
40
54
|
phase: str,
|
|
41
55
|
*,
|
|
@@ -236,11 +250,11 @@ def _final_verification_checks(workspace: Path) -> list[DoctorCheck]:
|
|
|
236
250
|
_file_check("validation command", workspace / "validators" / "validate-run.py"),
|
|
237
251
|
_file_check(
|
|
238
252
|
"final report template",
|
|
239
|
-
workspace / "templates" / "reports" / "final-report.template.md",
|
|
253
|
+
workspace / "templates" / "reports" / "final-report-v2.template.md",
|
|
240
254
|
),
|
|
241
255
|
_file_check(
|
|
242
256
|
"final report schema",
|
|
243
|
-
workspace / "schemas" / "final-report-
|
|
257
|
+
workspace / "schemas" / "final-report-v2.0.schema.json",
|
|
244
258
|
),
|
|
245
259
|
]
|
|
246
260
|
|
|
@@ -1,11 +1,39 @@
|
|
|
1
1
|
"""Host selection and session value objects."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from dataclasses import dataclass
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Literal, Mapping
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class CurrentSessionModelAttestation:
|
|
11
|
+
"""Host-observed identity of the already running lead session."""
|
|
12
|
+
|
|
13
|
+
provider_id: str
|
|
14
|
+
observed_model: str | None
|
|
15
|
+
normalized_model_ref: str | None
|
|
16
|
+
level: Literal["exact", "channel", "unknown"]
|
|
17
|
+
source: Literal[
|
|
18
|
+
"host-session-metadata",
|
|
19
|
+
"native-api",
|
|
20
|
+
"cli-handoff",
|
|
21
|
+
"unavailable",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def unknown(
|
|
26
|
+
cls,
|
|
27
|
+
provider_id: str,
|
|
28
|
+
source: Literal["unavailable"] = "unavailable",
|
|
29
|
+
) -> CurrentSessionModelAttestation:
|
|
30
|
+
return cls(provider_id, None, None, "unknown", source)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _unavailable_current_model() -> CurrentSessionModelAttestation:
|
|
34
|
+
return CurrentSessionModelAttestation.unknown("")
|
|
35
|
+
|
|
36
|
+
|
|
9
37
|
@dataclass(frozen=True)
|
|
10
38
|
class HostDescriptor:
|
|
11
39
|
id: str
|
|
@@ -29,6 +57,9 @@ class HostSessionContext:
|
|
|
29
57
|
entry_mode: Literal["spawn-process", "current-session"]
|
|
30
58
|
available_functions: frozenset[str]
|
|
31
59
|
interaction_surface: str
|
|
60
|
+
current_model: CurrentSessionModelAttestation = field(
|
|
61
|
+
default_factory=_unavailable_current_model
|
|
62
|
+
)
|
|
32
63
|
|
|
33
64
|
|
|
34
65
|
@dataclass(frozen=True)
|
|
@@ -1,19 +1,168 @@
|
|
|
1
1
|
"""Provider and model value objects shared by the registry and adapters."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING, Literal, Mapping, Optional, Protocol
|
|
7
|
+
|
|
8
|
+
from .role import RoleCatalogError, normalize_role, ROLE_CATALOG
|
|
9
|
+
from .worker_exec import WorkerWriteCapability
|
|
6
10
|
|
|
7
11
|
if TYPE_CHECKING:
|
|
8
12
|
from okstra_ctl.domain.worker_exec import ExecutionStrategy
|
|
9
13
|
|
|
10
14
|
|
|
15
|
+
_MISSING = object()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class ServedModelAttestation:
|
|
20
|
+
"""Provider-observed model identity after one execution attempt."""
|
|
21
|
+
|
|
22
|
+
observed_model: str | None
|
|
23
|
+
normalized_model_ref: str | None
|
|
24
|
+
level: Literal["exact", "channel", "unknown"]
|
|
25
|
+
source: Literal[
|
|
26
|
+
"provider-output",
|
|
27
|
+
"native-api",
|
|
28
|
+
"cli-handoff",
|
|
29
|
+
"unavailable",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def unknown(cls) -> ServedModelAttestation:
|
|
34
|
+
return cls(None, None, "unknown", "unavailable")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
ServedModelNormalizer = Callable[[str | None], ServedModelAttestation]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def unavailable_served_model(_raw_model: str | None) -> ServedModelAttestation:
|
|
41
|
+
return ServedModelAttestation.unknown()
|
|
42
|
+
|
|
43
|
+
|
|
11
44
|
@dataclass(frozen=True)
|
|
45
|
+
class HostModelBinding:
|
|
46
|
+
"""Final provider value and host-native value for one selected model."""
|
|
47
|
+
|
|
48
|
+
runner: str
|
|
49
|
+
catalog_execution_value: str
|
|
50
|
+
resolved_execution_value: str
|
|
51
|
+
host_model_value: str | None
|
|
52
|
+
binding_fidelity: Literal["exact", "channel", "unsupported"]
|
|
53
|
+
worker_write_capability: WorkerWriteCapability | None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True, init=False)
|
|
12
57
|
class ModelSpec:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
58
|
+
"""One canonical model plus provider-owned aliases and execution value."""
|
|
59
|
+
|
|
60
|
+
model_id: str
|
|
61
|
+
display_name: str
|
|
62
|
+
execution_value: str
|
|
63
|
+
aliases: tuple[str, ...]
|
|
64
|
+
version_kind: Literal["pinned", "channel"]
|
|
65
|
+
channel_family: str | None
|
|
66
|
+
supported_roles: frozenset[str] | None
|
|
67
|
+
selectable: bool
|
|
68
|
+
pricing: Optional[tuple[float, float, float]]
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
model_id: str,
|
|
73
|
+
display_name: str,
|
|
74
|
+
execution_value: str | tuple[float, float, float] | None | object = _MISSING,
|
|
75
|
+
aliases: tuple[str, ...] = (),
|
|
76
|
+
version_kind: Literal["pinned", "channel"] = "pinned",
|
|
77
|
+
channel_family: str | None = None,
|
|
78
|
+
supported_roles: frozenset[str] | None = None,
|
|
79
|
+
selectable: bool = True,
|
|
80
|
+
pricing: Optional[tuple[float, float, float]] = None,
|
|
81
|
+
*,
|
|
82
|
+
in_picker: bool | None = None,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Accept the former display/execution form while adapters migrate."""
|
|
85
|
+
is_legacy = execution_value is _MISSING or not isinstance(execution_value, str)
|
|
86
|
+
if is_legacy:
|
|
87
|
+
legacy_pricing = execution_value if isinstance(execution_value, tuple) else None
|
|
88
|
+
canonical_id = display_name
|
|
89
|
+
resolved_display = model_id
|
|
90
|
+
resolved_execution = display_name
|
|
91
|
+
resolved_pricing = legacy_pricing
|
|
92
|
+
else:
|
|
93
|
+
canonical_id = model_id
|
|
94
|
+
resolved_display = display_name
|
|
95
|
+
resolved_execution = execution_value
|
|
96
|
+
resolved_pricing = pricing
|
|
97
|
+
object.__setattr__(self, "model_id", canonical_id)
|
|
98
|
+
object.__setattr__(self, "display_name", resolved_display)
|
|
99
|
+
object.__setattr__(self, "execution_value", resolved_execution)
|
|
100
|
+
object.__setattr__(self, "aliases", aliases)
|
|
101
|
+
object.__setattr__(self, "version_kind", version_kind)
|
|
102
|
+
object.__setattr__(self, "channel_family", channel_family)
|
|
103
|
+
object.__setattr__(self, "supported_roles", supported_roles)
|
|
104
|
+
object.__setattr__(self, "selectable", selectable if in_picker is None else in_picker)
|
|
105
|
+
object.__setattr__(self, "pricing", resolved_pricing)
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def display(self) -> str:
|
|
109
|
+
"""Legacy display-name view."""
|
|
110
|
+
return self.display_name
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def execution(self) -> str:
|
|
114
|
+
"""Legacy execution-value view."""
|
|
115
|
+
return self.execution_value
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def in_picker(self) -> bool:
|
|
119
|
+
"""Legacy picker view."""
|
|
120
|
+
return self.selectable
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass(frozen=True)
|
|
124
|
+
class ExecutionNormalization:
|
|
125
|
+
"""One precomputed CLI execution value or its discovery-time error."""
|
|
126
|
+
|
|
127
|
+
resolved_execution_value: str | None
|
|
128
|
+
error: str = ""
|
|
129
|
+
|
|
130
|
+
def __post_init__(self) -> None:
|
|
131
|
+
if bool(self.resolved_execution_value) == bool(self.error):
|
|
132
|
+
raise ValueError(
|
|
133
|
+
"execution normalization requires exactly one value or error"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ExecutionNormalizer(Protocol):
|
|
138
|
+
"""Provider-owned batch snapshot boundary for ambient CLI discovery."""
|
|
139
|
+
|
|
140
|
+
def snapshot(
|
|
141
|
+
self,
|
|
142
|
+
models: tuple[ModelSpec, ...],
|
|
143
|
+
roles: tuple[str, ...],
|
|
144
|
+
) -> Mapping[tuple[str, str], ExecutionNormalization]: ...
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def snapshot_execution_normalizations(
|
|
148
|
+
models: tuple[ModelSpec, ...],
|
|
149
|
+
roles: tuple[str, ...],
|
|
150
|
+
normalize: Callable[[str, str], str],
|
|
151
|
+
) -> Mapping[tuple[str, str], ExecutionNormalization]:
|
|
152
|
+
"""Evaluate a pure provider normalizer for every model/role pair."""
|
|
153
|
+
outcomes: dict[tuple[str, str], ExecutionNormalization] = {}
|
|
154
|
+
for model in models:
|
|
155
|
+
for role in roles:
|
|
156
|
+
try:
|
|
157
|
+
resolved = normalize(model.execution_value, role)
|
|
158
|
+
except Exception as exc: # provider errors become immutable facts
|
|
159
|
+
outcomes[(model.model_id, role)] = ExecutionNormalization(
|
|
160
|
+
None,
|
|
161
|
+
str(exc),
|
|
162
|
+
)
|
|
163
|
+
else:
|
|
164
|
+
outcomes[(model.model_id, role)] = ExecutionNormalization(resolved)
|
|
165
|
+
return outcomes
|
|
17
166
|
|
|
18
167
|
|
|
19
168
|
@dataclass(frozen=True)
|
|
@@ -37,8 +186,37 @@ class ProviderSpec:
|
|
|
37
186
|
default_models: Mapping[str, str]
|
|
38
187
|
wrapper: str
|
|
39
188
|
supported_roles: frozenset[str]
|
|
189
|
+
execution_capabilities: frozenset[str] = frozenset()
|
|
40
190
|
lead_launch: Optional[LeadLaunchSpec] = None
|
|
41
191
|
exec_strategy: Optional["ExecutionStrategy"] = None
|
|
192
|
+
execution_normalizer: Optional[ExecutionNormalizer] = None
|
|
193
|
+
served_model_normalizer: ServedModelNormalizer = unavailable_served_model
|
|
194
|
+
_legacy_role_restriction: frozenset[str] = field(
|
|
195
|
+
init=False,
|
|
196
|
+
repr=False,
|
|
197
|
+
compare=False,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def __post_init__(self) -> None:
|
|
201
|
+
legacy_roles = _normalized_legacy_roles(self.supported_roles)
|
|
202
|
+
if self.execution_capabilities:
|
|
203
|
+
capabilities = self.execution_capabilities
|
|
204
|
+
restriction = frozenset()
|
|
205
|
+
else:
|
|
206
|
+
capabilities = _legacy_execution_capabilities(legacy_roles)
|
|
207
|
+
restriction = legacy_roles
|
|
208
|
+
object.__setattr__(self, "execution_capabilities", capabilities)
|
|
209
|
+
object.__setattr__(self, "_legacy_role_restriction", restriction)
|
|
210
|
+
|
|
211
|
+
def supports_role(self, role: str) -> bool:
|
|
212
|
+
try:
|
|
213
|
+
canonical_role = normalize_role(role)
|
|
214
|
+
except RoleCatalogError:
|
|
215
|
+
return False
|
|
216
|
+
if self._legacy_role_restriction:
|
|
217
|
+
return canonical_role in self._legacy_role_restriction
|
|
218
|
+
required = ROLE_CATALOG.required_capabilities(canonical_role)
|
|
219
|
+
return required.issubset(self.execution_capabilities)
|
|
42
220
|
|
|
43
221
|
|
|
44
222
|
@dataclass(frozen=True)
|
|
@@ -60,6 +238,8 @@ class LeadProviderAssignment:
|
|
|
60
238
|
class ModelAssignment:
|
|
61
239
|
display: str
|
|
62
240
|
execution: str
|
|
241
|
+
model_ref: str = ""
|
|
242
|
+
version_kind: Literal["pinned", "channel"] = "pinned"
|
|
63
243
|
|
|
64
244
|
|
|
65
245
|
class UnknownModelError(ValueError):
|
|
@@ -68,3 +248,29 @@ class UnknownModelError(ValueError):
|
|
|
68
248
|
|
|
69
249
|
class UnknownProviderError(ValueError):
|
|
70
250
|
"""Raised when a requested provider is absent from the registry."""
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _normalized_legacy_roles(roles: frozenset[str]) -> frozenset[str]:
|
|
254
|
+
normalized: set[str] = set()
|
|
255
|
+
for role in roles:
|
|
256
|
+
try:
|
|
257
|
+
normalized.add(normalize_role(role))
|
|
258
|
+
except RoleCatalogError:
|
|
259
|
+
continue
|
|
260
|
+
return frozenset(normalized)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _legacy_execution_capabilities(roles: frozenset[str]) -> frozenset[str]:
|
|
264
|
+
capabilities: set[str] = set()
|
|
265
|
+
for role in roles:
|
|
266
|
+
if role == "leader":
|
|
267
|
+
capabilities.add("lead-session")
|
|
268
|
+
continue
|
|
269
|
+
capabilities.add("worker-artifact-io")
|
|
270
|
+
if role == "analyser":
|
|
271
|
+
capabilities.add("source-readonly")
|
|
272
|
+
if role == "implementer":
|
|
273
|
+
capabilities.add("project-mutation")
|
|
274
|
+
if role in {"report-writer", "translator"}:
|
|
275
|
+
capabilities.add("extended-artifact-authoring")
|
|
276
|
+
return frozenset(capabilities)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Canonical role identities and the duties that execute them."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Mapping, Sequence
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RoleCatalogError(ValueError):
|
|
9
|
+
"""Raised when a role or duty is absent from the canonical catalog."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class RoleDefinition:
|
|
14
|
+
id: str
|
|
15
|
+
legacy_aliases: tuple[str, ...]
|
|
16
|
+
required_capabilities: frozenset[str]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_READONLY_CAPABILITIES = frozenset({"worker-artifact-io", "source-readonly"})
|
|
20
|
+
|
|
21
|
+
ROLE_DEFINITIONS = (
|
|
22
|
+
RoleDefinition("leader", ("lead",), frozenset({"lead-session"})),
|
|
23
|
+
RoleDefinition("analyser", (), _READONLY_CAPABILITIES),
|
|
24
|
+
RoleDefinition("critic", (), _READONLY_CAPABILITIES),
|
|
25
|
+
RoleDefinition("designer", (), _READONLY_CAPABILITIES),
|
|
26
|
+
RoleDefinition("planner", (), _READONLY_CAPABILITIES),
|
|
27
|
+
RoleDefinition(
|
|
28
|
+
"implementer",
|
|
29
|
+
("executor",),
|
|
30
|
+
frozenset({"worker-artifact-io", "project-mutation"}),
|
|
31
|
+
),
|
|
32
|
+
RoleDefinition("verifier", (), _READONLY_CAPABILITIES),
|
|
33
|
+
RoleDefinition(
|
|
34
|
+
"report-writer",
|
|
35
|
+
(),
|
|
36
|
+
_READONLY_CAPABILITIES | frozenset({"extended-artifact-authoring"}),
|
|
37
|
+
),
|
|
38
|
+
RoleDefinition(
|
|
39
|
+
"translator",
|
|
40
|
+
(),
|
|
41
|
+
_READONLY_CAPABILITIES | frozenset({"extended-artifact-authoring"}),
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
DUTY_ROLE_IDS = {
|
|
46
|
+
"lead": "leader",
|
|
47
|
+
"analysis-worker": "analyser",
|
|
48
|
+
"discovery-worker": "analyser",
|
|
49
|
+
"diagnosis-worker": "analyser",
|
|
50
|
+
"scope-critic": "critic",
|
|
51
|
+
"acceptance-critic": "critic",
|
|
52
|
+
"direction-selection-worker": "designer",
|
|
53
|
+
"planning-worker": "planner",
|
|
54
|
+
"implementation-executor": "implementer",
|
|
55
|
+
"implementation-verifier": "verifier",
|
|
56
|
+
"acceptance-verifier": "verifier",
|
|
57
|
+
"reverification-worker": "verifier",
|
|
58
|
+
"code-reviewer": "verifier",
|
|
59
|
+
"schedule-verifier": "verifier",
|
|
60
|
+
"report-writer": "report-writer",
|
|
61
|
+
"translator": "translator",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RoleCatalog:
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
definitions: Sequence[RoleDefinition],
|
|
69
|
+
duty_role_ids: Mapping[str, str],
|
|
70
|
+
) -> None:
|
|
71
|
+
definition_list = tuple(definitions)
|
|
72
|
+
self._definition_count = len(definition_list)
|
|
73
|
+
self._definitions = {definition.id: definition for definition in definition_list}
|
|
74
|
+
self._aliases = {
|
|
75
|
+
alias: definition.id
|
|
76
|
+
for definition in definition_list
|
|
77
|
+
for alias in definition.legacy_aliases
|
|
78
|
+
}
|
|
79
|
+
self._duty_role_ids = dict(duty_role_ids)
|
|
80
|
+
self._validate()
|
|
81
|
+
|
|
82
|
+
def normalize(self, raw: str) -> str:
|
|
83
|
+
role = raw.strip()
|
|
84
|
+
canonical = self._aliases.get(role, role)
|
|
85
|
+
if canonical not in self._definitions:
|
|
86
|
+
raise RoleCatalogError(f"unknown role: {raw}")
|
|
87
|
+
return canonical
|
|
88
|
+
|
|
89
|
+
def required_capabilities(self, role: str) -> frozenset[str]:
|
|
90
|
+
return self._definitions[self.normalize(role)].required_capabilities
|
|
91
|
+
|
|
92
|
+
def role_for_duty(self, duty_id: str) -> str:
|
|
93
|
+
try:
|
|
94
|
+
return self._duty_role_ids[duty_id]
|
|
95
|
+
except KeyError as exc:
|
|
96
|
+
raise RoleCatalogError(f"unknown duty: {duty_id}") from exc
|
|
97
|
+
|
|
98
|
+
def _validate(self) -> None:
|
|
99
|
+
if len(self._definitions) != self._definition_count:
|
|
100
|
+
raise RoleCatalogError("duplicate canonical role")
|
|
101
|
+
if len(self._aliases) != sum(
|
|
102
|
+
len(definition.legacy_aliases) for definition in self._definitions.values()
|
|
103
|
+
):
|
|
104
|
+
raise RoleCatalogError("duplicate legacy role alias")
|
|
105
|
+
for role in self._duty_role_ids.values():
|
|
106
|
+
self.normalize(role)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
ROLE_CATALOG = RoleCatalog(ROLE_DEFINITIONS, DUTY_ROLE_IDS)
|
|
110
|
+
|
|
111
|
+
# Ids plus legacy aliases. Bundled adapters declare this set so every
|
|
112
|
+
# catalog model can be assigned to every role.
|
|
113
|
+
ALL_ROLE_TOKENS = frozenset(
|
|
114
|
+
token
|
|
115
|
+
for definition in ROLE_DEFINITIONS
|
|
116
|
+
for token in (definition.id, *definition.legacy_aliases)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def normalize_role(raw: str) -> str:
|
|
121
|
+
return ROLE_CATALOG.normalize(raw)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def role_for_duty(duty_id: str) -> str:
|
|
125
|
+
return ROLE_CATALOG.role_for_duty(duty_id)
|
|
@@ -24,6 +24,11 @@ class WizardPrompt:
|
|
|
24
24
|
multi: bool = False
|
|
25
25
|
questions: tuple["WizardPrompt", ...] = ()
|
|
26
26
|
|
|
27
|
+
@property
|
|
28
|
+
def id(self) -> str:
|
|
29
|
+
"""Stable public identifier shared by static and role-instance prompts."""
|
|
30
|
+
return self.step
|
|
31
|
+
|
|
27
32
|
|
|
28
33
|
@dataclass(frozen=True)
|
|
29
34
|
class AnswerProtocol:
|
|
@@ -66,6 +71,14 @@ def _normalize_numbered_item(prompt: WizardPrompt, answer: str) -> str:
|
|
|
66
71
|
number = int(candidate)
|
|
67
72
|
if 1 <= number <= len(prompt.options):
|
|
68
73
|
return prompt.options[number - 1].value
|
|
74
|
+
if prompt.step.startswith("role-count:"):
|
|
75
|
+
role = prompt.step.split(":", 1)[1]
|
|
76
|
+
minimum = prompt.options[0].value if prompt.options else "?"
|
|
77
|
+
maximum = prompt.options[-1].value if prompt.options else "?"
|
|
78
|
+
raise WizardAnswerError(
|
|
79
|
+
f"role {role!r} count must be in {minimum}..{maximum}: "
|
|
80
|
+
f"{candidate}"
|
|
81
|
+
)
|
|
69
82
|
out_of_list = _exact_value_match(prompt, candidate)
|
|
70
83
|
if out_of_list is not None:
|
|
71
84
|
return out_of_list
|
|
@@ -9,15 +9,26 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
from dataclasses import dataclass, field
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from
|
|
12
|
+
from collections.abc import Callable, Mapping
|
|
13
|
+
from typing import Any, Literal, Protocol, runtime_checkable
|
|
13
14
|
|
|
14
15
|
from .worker_stream import Normalise, no_events
|
|
16
|
+
from ..write_policy import WriteEnforcement, WritePolicy
|
|
15
17
|
|
|
16
18
|
# What ``ExecCommand.stream_format`` may hold. The value decides whether the
|
|
17
19
|
# runner pipes the CLI's output through the stream formatter or forwards it
|
|
18
20
|
# unchanged, so it is part of the contract rather than a display hint.
|
|
19
21
|
STREAM_JSON = "stream-json"
|
|
20
22
|
TEXT = "text"
|
|
23
|
+
SERVED_MODEL_MISMATCH_EXIT_CODE = 78
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def no_model_observation(_event: Mapping[str, Any]) -> str | None:
|
|
27
|
+
"""Truthful default for provider output that exposes no model identity."""
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
ObserveServedModel = Callable[[Mapping[str, Any]], str | None]
|
|
21
32
|
|
|
22
33
|
|
|
23
34
|
@dataclass(frozen=True)
|
|
@@ -33,6 +44,8 @@ class ExecutionPolicy:
|
|
|
33
44
|
|
|
34
45
|
auto_approve: bool
|
|
35
46
|
write_scope: tuple[Path, ...]
|
|
47
|
+
write_policy: WritePolicy | None = None
|
|
48
|
+
write_enforcement: WriteEnforcement | None = None
|
|
36
49
|
|
|
37
50
|
|
|
38
51
|
@dataclass(frozen=True)
|
|
@@ -53,6 +66,11 @@ class WorkerExecRequest:
|
|
|
53
66
|
worktree_path: Path | None
|
|
54
67
|
policy: ExecutionPolicy
|
|
55
68
|
idle_timeout_seconds: int
|
|
69
|
+
# The id the dispatcher chose, empty when it chose none. Only a CLI that
|
|
70
|
+
# accepts a session id reads it. Handing it over means the CLI does not
|
|
71
|
+
# issue its own, so the recorded id and the session's own jsonl are
|
|
72
|
+
# guaranteed to name the same session.
|
|
73
|
+
session_id: str = ""
|
|
56
74
|
|
|
57
75
|
|
|
58
76
|
@dataclass(frozen=True)
|
|
@@ -79,6 +97,20 @@ class ExecCommand:
|
|
|
79
97
|
stream_format: str
|
|
80
98
|
cwd: Path
|
|
81
99
|
normalise: Normalise = field(default=no_events)
|
|
100
|
+
observe_served_model: ObserveServedModel = field(
|
|
101
|
+
default=no_model_observation
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class WorkerWriteCapability:
|
|
107
|
+
"""Maximum write-boundary precision exposed by a worker runner."""
|
|
108
|
+
|
|
109
|
+
max_boundary_precision: Literal[
|
|
110
|
+
"exact-path",
|
|
111
|
+
"directory-boundary",
|
|
112
|
+
"none",
|
|
113
|
+
]
|
|
82
114
|
|
|
83
115
|
|
|
84
116
|
@dataclass(frozen=True)
|
|
@@ -94,6 +126,12 @@ class PolicySupport:
|
|
|
94
126
|
can_bound_write_scope: bool
|
|
95
127
|
note: str = ""
|
|
96
128
|
|
|
129
|
+
def worker_write_capability(self) -> WorkerWriteCapability:
|
|
130
|
+
precision = (
|
|
131
|
+
"directory-boundary" if self.can_bound_write_scope else "none"
|
|
132
|
+
)
|
|
133
|
+
return WorkerWriteCapability(precision)
|
|
134
|
+
|
|
97
135
|
|
|
98
136
|
@runtime_checkable
|
|
99
137
|
class ExecutionStrategy(Protocol):
|
|
@@ -9,6 +9,8 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
|
|
12
|
+
from .role import RoleCatalogError, normalize_role
|
|
13
|
+
|
|
12
14
|
# Implementation executor / verifier dispatches run whole build + test suites
|
|
13
15
|
# that legitimately emit nothing for minutes; the 600s cap reaped them mid-suite
|
|
14
16
|
# (observed: a silent jest + tsc build TERM'd at ~929s). This stays below the
|
|
@@ -16,7 +18,7 @@ from dataclasses import dataclass
|
|
|
16
18
|
_BUILD_RUNNING_IDLE_SECONDS = 1500
|
|
17
19
|
_DEFAULT_IDLE_SECONDS = 600
|
|
18
20
|
|
|
19
|
-
_BUILD_RUNNING_ROLES = frozenset({"
|
|
21
|
+
_BUILD_RUNNING_ROLES = frozenset({"implementer", "verifier"})
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
@dataclass(frozen=True)
|
|
@@ -26,9 +28,17 @@ class WorkerRoleSpec:
|
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
def role_spec(role: str) -> WorkerRoleSpec:
|
|
31
|
+
canonical_role = _canonical_role_or_raw(role)
|
|
29
32
|
seconds = (
|
|
30
33
|
_BUILD_RUNNING_IDLE_SECONDS
|
|
31
|
-
if
|
|
34
|
+
if canonical_role in _BUILD_RUNNING_ROLES
|
|
32
35
|
else _DEFAULT_IDLE_SECONDS
|
|
33
36
|
)
|
|
34
|
-
return WorkerRoleSpec(role=
|
|
37
|
+
return WorkerRoleSpec(role=canonical_role, idle_timeout_seconds=seconds)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _canonical_role_or_raw(role: str) -> str:
|
|
41
|
+
try:
|
|
42
|
+
return normalize_role(role)
|
|
43
|
+
except RoleCatalogError:
|
|
44
|
+
return role
|
|
@@ -88,6 +88,30 @@ StreamEvent = Text | ToolCall | ToolResult | Denial | Result
|
|
|
88
88
|
Normalise = Callable[[Mapping[str, Any]], tuple[StreamEvent, ...]]
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
def host_event_row(
|
|
92
|
+
*,
|
|
93
|
+
event_id: str,
|
|
94
|
+
sequence: int,
|
|
95
|
+
event_type: str,
|
|
96
|
+
invocation_ref: str,
|
|
97
|
+
attempt: int,
|
|
98
|
+
payload: Mapping[str, Any],
|
|
99
|
+
timestamp: str,
|
|
100
|
+
) -> dict[str, Any]:
|
|
101
|
+
"""Build one orchestrator-owned host-event row without I/O."""
|
|
102
|
+
row = {
|
|
103
|
+
"schemaVersion": "1.0",
|
|
104
|
+
"eventId": event_id,
|
|
105
|
+
"sequence": sequence,
|
|
106
|
+
"timestamp": timestamp,
|
|
107
|
+
"eventType": event_type,
|
|
108
|
+
"invocationRef": invocation_ref,
|
|
109
|
+
"attempt": attempt,
|
|
110
|
+
}
|
|
111
|
+
row.update(dict(payload))
|
|
112
|
+
return row
|
|
113
|
+
|
|
114
|
+
|
|
91
115
|
def no_events(event: Mapping[str, Any]) -> tuple[StreamEvent, ...]:
|
|
92
116
|
"""The normaliser for a CLI that emits no events at all.
|
|
93
117
|
|
|
@@ -202,6 +202,7 @@ def append_observed(
|
|
|
202
202
|
stderr_excerpt,
|
|
203
203
|
context,
|
|
204
204
|
now=None,
|
|
205
|
+
identity=None,
|
|
205
206
|
):
|
|
206
207
|
"""Append a lead-observed error event to errors.jsonl."""
|
|
207
208
|
if error_type not in ALLOWED_ERROR_TYPES:
|
|
@@ -232,6 +233,9 @@ def append_observed(
|
|
|
232
233
|
"stderrExcerpt": truncate_stderr(stderr_excerpt),
|
|
233
234
|
"context": context,
|
|
234
235
|
}
|
|
236
|
+
from okstra_ctl.execution_identity import stored_identity
|
|
237
|
+
|
|
238
|
+
rec.update(stored_identity(identity))
|
|
235
239
|
append_jsonl_line(out_path, rec)
|
|
236
240
|
return rec
|
|
237
241
|
|
|
@@ -245,6 +249,7 @@ def dump_from_worker_sidecar(
|
|
|
245
249
|
agent_role,
|
|
246
250
|
model,
|
|
247
251
|
now=None,
|
|
252
|
+
identity=None,
|
|
248
253
|
):
|
|
249
254
|
"""Read worker sidecar errors[] and append each to errors.jsonl with
|
|
250
255
|
Lead-side metadata filled in. Returns number of records appended.
|
|
@@ -303,6 +308,10 @@ def dump_from_worker_sidecar(
|
|
|
303
308
|
"stderrExcerpt": truncate_stderr(e.get("stderrExcerpt")),
|
|
304
309
|
"context": entry_context,
|
|
305
310
|
}
|
|
311
|
+
from okstra_ctl.execution_identity import stored_identity
|
|
312
|
+
|
|
313
|
+
rec.update(stored_identity(identity))
|
|
314
|
+
rec.update(stored_identity(e))
|
|
306
315
|
append_jsonl_line(out_path, rec)
|
|
307
316
|
count += 1
|
|
308
317
|
return count
|