okstra 0.163.2 → 0.165.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/docs/architecture.md +24 -15
- package/docs/cli.md +15 -8
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-inspect.md +2 -2
- package/docs/for-ai/skills/okstra-user-response.md +2 -2
- package/docs/project-structure-overview.md +22 -14
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +9 -7
- package/runtime/agents/workers/claude-worker.md +1 -0
- package/runtime/agents/workers/codex-worker.md +9 -7
- package/runtime/agents/workers/grok-worker.md +6 -4
- package/runtime/agents/workers/kimi-worker.md +6 -4
- package/runtime/bin/lib/okstra/cli.sh +5 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/usage.sh +5 -5
- package/runtime/bin/okstra-antigravity-exec.sh +1 -340
- package/runtime/bin/okstra-claude-exec.sh +1 -178
- package/runtime/bin/okstra-codex-exec.sh +1 -467
- package/runtime/bin/okstra-provider-exec.py +165 -190
- package/runtime/bin/okstra-trace-cleanup.sh +14 -7
- package/runtime/bin/okstra-wrapper-status.py +26 -19
- package/runtime/bin/okstra.sh +87 -91
- package/runtime/prompts/lead/adapters/cmux.md +2 -2
- package/runtime/prompts/lead/convergence.md +36 -8
- package/runtime/prompts/lead/okstra-lead-contract.md +24 -1
- package/runtime/prompts/lead/plan-body-verification.md +9 -1
- package/runtime/prompts/lead/report-writer.md +1 -0
- package/runtime/prompts/lead/team-contract.md +3 -3
- package/runtime/prompts/profiles/_common-contract.md +9 -1
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/_implementation-diff-review.md +3 -1
- package/runtime/prompts/profiles/_implementation-self-check.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -1
- package/runtime/prompts/profiles/implementation-planning.md +6 -4
- package/runtime/python/okstra_ctl/adapters/accounting/__init__.py +11 -0
- package/runtime/python/okstra_ctl/adapters/accounting/claude_jsonl.py +17 -0
- package/runtime/python/okstra_ctl/adapters/accounting/cli_artifact.py +17 -0
- package/runtime/python/okstra_ctl/adapters/accounting/unavailable.py +19 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +92 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +54 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +68 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +60 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/antigravity.md → python/okstra_ctl/adapters/hosts/antigravity/relay.md} +52 -0
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +292 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +120 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/claude-code.md → python/okstra_ctl/adapters/hosts/claude-code/relay.md} +112 -1
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +60 -0
- package/runtime/python/okstra_ctl/adapters/hosts/codex/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/codex.md → python/okstra_ctl/adapters/hosts/codex/relay.md} +52 -0
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +72 -0
- package/runtime/python/okstra_ctl/adapters/hosts/external/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/external.md → python/okstra_ctl/adapters/hosts/external/relay.md} +53 -1
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +63 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +90 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +63 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +90 -0
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +183 -0
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +110 -0
- package/runtime/python/okstra_ctl/adapters/providers/claude/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +84 -0
- package/runtime/python/okstra_ctl/adapters/providers/codex/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +76 -0
- package/runtime/python/okstra_ctl/adapters/providers/grok/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +80 -0
- package/runtime/python/okstra_ctl/adapters/providers/kimi/manifest.json +1 -0
- package/runtime/python/okstra_ctl/application/__init__.py +1 -0
- package/runtime/python/okstra_ctl/application/advance_wizard.py +25 -0
- package/runtime/python/okstra_ctl/application/collect_usage.py +15 -0
- package/runtime/python/okstra_ctl/application/dispatch_assignments.py +15 -0
- package/runtime/python/okstra_ctl/application/resolve_assignment.py +93 -0
- package/runtime/python/okstra_ctl/application/resume_run.py +21 -0
- package/runtime/python/okstra_ctl/application/start_run.py +21 -0
- package/runtime/python/okstra_ctl/codex_dispatch.py +49 -826
- package/runtime/python/okstra_ctl/dispatch_core.py +244 -29
- package/runtime/python/okstra_ctl/dispatch_state.py +27 -0
- package/runtime/python/okstra_ctl/domain/__init__.py +34 -0
- package/runtime/python/okstra_ctl/domain/host.py +100 -0
- package/runtime/python/okstra_ctl/domain/provider.py +70 -0
- package/runtime/python/okstra_ctl/domain/wizard/__init__.py +19 -0
- package/runtime/python/okstra_ctl/domain/wizard/interaction.py +140 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +102 -0
- package/runtime/python/okstra_ctl/domain/worker_role.py +34 -0
- package/runtime/python/okstra_ctl/domain/worker_stream.py +261 -0
- package/runtime/python/okstra_ctl/entrypoints/__init__.py +1 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +334 -0
- package/runtime/python/okstra_ctl/incremental_scope.py +16 -4
- package/runtime/python/okstra_ctl/models.py +54 -269
- package/runtime/python/okstra_ctl/ports/__init__.py +15 -0
- package/runtime/python/okstra_ctl/ports/host.py +32 -0
- package/runtime/python/okstra_ctl/ports/interaction.py +15 -0
- package/runtime/python/okstra_ctl/ports/lead_session.py +25 -0
- package/runtime/python/okstra_ctl/ports/usage_accounting.py +23 -0
- package/runtime/python/okstra_ctl/ports/worker_dispatch.py +32 -0
- package/runtime/python/okstra_ctl/registry/__init__.py +13 -0
- package/runtime/python/okstra_ctl/registry/factory_loader.py +32 -0
- package/runtime/python/okstra_ctl/registry/host_discovery.py +124 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +365 -0
- package/runtime/python/okstra_ctl/registry/provider_registry.py +149 -0
- package/runtime/python/okstra_ctl/render.py +145 -47
- package/runtime/python/okstra_ctl/report_html/common.py +71 -25
- package/runtime/python/okstra_ctl/report_html/models.py +5 -0
- package/runtime/python/okstra_ctl/report_html/render.py +1 -1
- package/runtime/python/okstra_ctl/report_html/run_usage.py +19 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +14 -0
- package/runtime/python/okstra_ctl/report_views.py +44 -16
- package/runtime/python/okstra_ctl/run.py +80 -58
- package/runtime/python/okstra_ctl/session.py +1 -1
- package/runtime/python/okstra_ctl/stage_citations.py +52 -15
- package/runtime/python/okstra_ctl/team.py +44 -32
- package/runtime/python/okstra_ctl/user_response.py +45 -29
- package/runtime/python/okstra_ctl/wizard.py +175 -73
- package/runtime/python/okstra_ctl/worker_audit_ledger.py +29 -4
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +10 -3
- package/runtime/python/okstra_ctl/worker_request.py +140 -0
- package/runtime/python/okstra_ctl/worker_runner.py +622 -0
- package/runtime/python/okstra_token_usage/collect.py +42 -7
- package/runtime/python/okstra_token_usage/report.py +42 -0
- package/runtime/python/okstra_token_usage/task_totals.py +88 -0
- package/runtime/schemas/final-report-v1.0.schema.json +4040 -1066
- package/runtime/schemas/final-report-v2.0.schema.json +5673 -1412
- package/runtime/skills/okstra-inspect/SKILL.md +1 -2
- package/runtime/skills/okstra-inspect/facets/logs.md +5 -5
- package/runtime/skills/okstra-inspect/facets/run-audit.md +3 -3
- package/runtime/skills/okstra-run/SKILL.md +74 -29
- package/runtime/skills/okstra-user-response/SKILL.md +15 -5
- package/runtime/templates/implementation-worker-preamble.md +1 -1
- package/runtime/templates/report-writer-prompt-preamble.md +1 -0
- package/runtime/templates/reports/final-report.template.md +3 -3
- package/runtime/templates/reports/html/assets/base.css +8 -4
- package/runtime/templates/reports/html/base.template.html +12 -6
- package/runtime/templates/reports/html/i18n/en.json +32 -7
- package/runtime/templates/reports/html/i18n/ko.json +32 -7
- package/runtime/templates/reports/html/macros/forms.html +9 -3
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +14 -19
- package/runtime/templates/reports/report.js +59 -26
- package/runtime/templates/reports/user-response.template.md +12 -8
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-implementation-plan-stages.py +17 -22
- package/runtime/validators/validate-report-views.py +0 -39
- package/runtime/validators/validate-run.py +96 -14
- package/runtime/validators/validate_session_conformance.py +69 -3
- package/src/cli-registry.mjs +0 -7
- package/src/commands/execute/render-bundle.mjs +4 -4
- package/src/commands/execute/run.mjs +8 -25
- package/src/commands/execute/wizard.mjs +33 -13
- package/src/commands/lifecycle/doctor.mjs +10 -10
- package/src/commands/lifecycle/install.mjs +53 -30
- package/src/commands/lifecycle/preflight.mjs +14 -4
- package/src/lib/host-registry-client.mjs +176 -0
- package/src/lib/runtime-manifest.mjs +6 -8
- package/runtime/bin/okstra-wrapper-agy-stream.py +0 -61
- package/runtime/python/okstra_ctl/error_issue.py +0 -640
- package/runtime/python/okstra_ctl/issue_signals.py +0 -186
- package/runtime/python/okstra_ctl/lead_runtime.py +0 -115
- package/runtime/python/okstra_ctl/runner_resolution.py +0 -103
- package/runtime/skills/okstra-inspect/facets/error-issue.md +0 -77
- package/src/commands/inspect/error-issue.mjs +0 -27
- package/src/lib/runtime-readiness.mjs +0 -90
- package/src/lib/runtime-resolver.mjs +0 -123
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Assemble the request every provider strategy then takes on trust.
|
|
2
|
+
|
|
3
|
+
A strategy translates a ``WorkerExecRequest`` into its CLI's syntax and nothing
|
|
4
|
+
more: it does not resolve paths, does not decide what may be written, and does
|
|
5
|
+
not know which role it is running. Whoever builds the request answers all
|
|
6
|
+
three, so the answers live here — importable — rather than inside a dispatch
|
|
7
|
+
entrypoint where the next caller would have to copy them.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import subprocess
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from .domain.worker_exec import ExecutionPolicy, WorkerExecRequest
|
|
16
|
+
from .domain.worker_role import role_spec
|
|
17
|
+
|
|
18
|
+
# Carried over from the codex wrapper, where it was one provider's rule: cargo's
|
|
19
|
+
# package-cache flock and its registry live outside every workspace, so a
|
|
20
|
+
# verifier bounded to the tree alone fails each `cargo build/test/clippy` on the
|
|
21
|
+
# global lock. The rule follows the role rather than the provider now — every
|
|
22
|
+
# provider's verifier receives these — so the name no longer says codex. Set it
|
|
23
|
+
# to a colon-separated list of absolute paths to override, or to empty to
|
|
24
|
+
# disable.
|
|
25
|
+
VERIFIER_EXTRA_DIRS_ENV = "OKSTRA_VERIFIER_EXTRA_DIRS"
|
|
26
|
+
|
|
27
|
+
_VERIFIER_ROLE = "verifier"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def build_request(
|
|
31
|
+
*,
|
|
32
|
+
prompt_text: str,
|
|
33
|
+
model: str,
|
|
34
|
+
project_root: Path,
|
|
35
|
+
worktree_path: Path | None,
|
|
36
|
+
role: str,
|
|
37
|
+
idle_timeout_seconds: int,
|
|
38
|
+
) -> WorkerExecRequest:
|
|
39
|
+
"""One dispatch, with every value the strategies will not re-derive.
|
|
40
|
+
|
|
41
|
+
Paths are resolved here because nothing downstream resolves them: the
|
|
42
|
+
strategies hand what they are given straight to a CLI, so an unresolved
|
|
43
|
+
`..` or symlink would survive into the sandbox boundary itself.
|
|
44
|
+
"""
|
|
45
|
+
root = project_root.resolve()
|
|
46
|
+
worktree = worktree_path.resolve() if worktree_path is not None else None
|
|
47
|
+
return WorkerExecRequest(
|
|
48
|
+
prompt_text=prompt_text,
|
|
49
|
+
model=model,
|
|
50
|
+
project_root=root,
|
|
51
|
+
worktree_path=worktree,
|
|
52
|
+
# Nobody is at the keyboard to answer an approval prompt, so the gate is
|
|
53
|
+
# open and the boundary comes from the write scope instead.
|
|
54
|
+
policy=ExecutionPolicy(
|
|
55
|
+
auto_approve=True, write_scope=write_scope(root, worktree, role)
|
|
56
|
+
),
|
|
57
|
+
idle_timeout_seconds=idle_timeout_seconds,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def write_scope(
|
|
62
|
+
project_root: Path, worktree: Path | None, role: str
|
|
63
|
+
) -> tuple[Path, ...]:
|
|
64
|
+
"""What this worker may write, in the order the CLIs are told it.
|
|
65
|
+
|
|
66
|
+
The order is contract, not taste: strategies translate this tuple into
|
|
67
|
+
repeated `--add-dir` positionally, so it mirrors what the shell wrappers
|
|
68
|
+
claimed. The project root leads (the antigravity wrapper's first
|
|
69
|
+
`--add-dir`; codex names it with `-C` and its strategy skips the repeat),
|
|
70
|
+
then the stage tree, then the git directory that tree commits through.
|
|
71
|
+
|
|
72
|
+
The worktree belongs here as well as in the request: passing it as the
|
|
73
|
+
working directory alone produces an argv with no `--add-dir`, and the worker
|
|
74
|
+
then fails by not writing files rather than by erroring.
|
|
75
|
+
"""
|
|
76
|
+
scope = [project_root]
|
|
77
|
+
if worktree is not None:
|
|
78
|
+
scope.append(worktree)
|
|
79
|
+
common_git_dir = git_common_dir(worktree)
|
|
80
|
+
if common_git_dir is not None:
|
|
81
|
+
scope.append(common_git_dir)
|
|
82
|
+
scope.extend(verifier_extra_dirs(role))
|
|
83
|
+
return tuple(scope)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def git_common_dir(worktree: Path) -> Path | None:
|
|
87
|
+
"""The main repository's `.git`, which a linked worktree commits through.
|
|
88
|
+
|
|
89
|
+
A linked worktree keeps its index and refs under the main repo's `.git` and
|
|
90
|
+
shares its object database, so a worker bounded to the tree alone cannot
|
|
91
|
+
`git commit` from it. A directory that is no worktree at all simply has
|
|
92
|
+
nothing extra to grant.
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
probe = subprocess.run(
|
|
96
|
+
["git", "-C", str(worktree), "rev-parse", "--git-common-dir"],
|
|
97
|
+
capture_output=True,
|
|
98
|
+
text=True,
|
|
99
|
+
check=False,
|
|
100
|
+
)
|
|
101
|
+
except OSError:
|
|
102
|
+
return None
|
|
103
|
+
if probe.returncode != 0 or not probe.stdout.strip():
|
|
104
|
+
return None
|
|
105
|
+
candidate = Path(probe.stdout.strip())
|
|
106
|
+
if not candidate.is_absolute():
|
|
107
|
+
candidate = worktree / candidate
|
|
108
|
+
return candidate.resolve() if candidate.is_dir() else None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def verifier_extra_dirs(role: str) -> tuple[Path, ...]:
|
|
112
|
+
"""Toolchain directories granted to the verifier role only.
|
|
113
|
+
|
|
114
|
+
Role decides this, not the provider, so it is assembled here rather than in
|
|
115
|
+
a strategy — and not in `WorkerRoleSpec`, which holds values that hold for
|
|
116
|
+
every machine while these paths exist only where the toolchain is.
|
|
117
|
+
"""
|
|
118
|
+
if role != _VERIFIER_ROLE:
|
|
119
|
+
return ()
|
|
120
|
+
home = Path.home()
|
|
121
|
+
raw = os.environ.get(VERIFIER_EXTRA_DIRS_ENV, f"{home}/.cargo:{home}/.rustup")
|
|
122
|
+
candidates = (Path(entry) for entry in raw.split(":") if entry)
|
|
123
|
+
return tuple(path.resolve() for path in candidates if path.is_dir())
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def idle_timeout(raw: str, role: str) -> int:
|
|
127
|
+
"""The explicit budget, or the one the role's spec owns.
|
|
128
|
+
|
|
129
|
+
The default is not written here. The same 1500/600 pair used to live in
|
|
130
|
+
every wrapper and in the python entrypoint, and each copy could drift from
|
|
131
|
+
the policy. Raises ``ValueError`` on a malformed budget; the exit code that
|
|
132
|
+
failure earns belongs to the entrypoint, not to this module.
|
|
133
|
+
"""
|
|
134
|
+
if not raw:
|
|
135
|
+
return role_spec(role).idle_timeout_seconds
|
|
136
|
+
if not raw.isdigit():
|
|
137
|
+
raise ValueError(
|
|
138
|
+
f"idle-timeout-seconds must be a non-negative integer: {raw!r}"
|
|
139
|
+
)
|
|
140
|
+
return int(raw)
|