okstra 0.167.0 → 0.169.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 +6 -5
- package/docs/architecture/storage-model.md +57 -1
- package/docs/architecture.md +70 -2
- package/docs/cli.md +8 -4
- package/docs/for-ai/skills/okstra-code-review.md +3 -2
- package/docs/for-ai/skills/okstra-schedule-gen.md +3 -1
- package/docs/pr-template-usage.md +10 -6
- package/docs/project-structure-overview.md +14 -11
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -5
- package/runtime/agents/workers/report-writer-worker.md +9 -4
- package/runtime/agents/workers/translator-worker.md +6 -4
- package/runtime/prompts/coding-preflight/architectures/hexagonal.md +27 -1
- package/runtime/prompts/coding-preflight/clean-code.md +13 -0
- package/runtime/prompts/duties/acceptance-critic.md +24 -0
- package/runtime/prompts/duties/acceptance-verifier.md +24 -0
- package/runtime/prompts/duties/analysis-worker.md +24 -0
- package/runtime/prompts/duties/code-reviewer.md +24 -0
- package/runtime/prompts/duties/common.md +35 -0
- package/runtime/prompts/duties/implementation-executor.md +24 -0
- package/runtime/prompts/duties/implementation-verifier.md +24 -0
- package/runtime/prompts/duties/lead.md +24 -0
- package/runtime/prompts/duties/report-writer.md +24 -0
- package/runtime/prompts/duties/reverification-worker.md +24 -0
- package/runtime/prompts/duties/schedule-verifier.md +24 -0
- package/runtime/prompts/duties/scope-critic.md +24 -0
- package/runtime/prompts/duties/translator.md +24 -0
- package/runtime/prompts/lead/convergence.md +51 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +10 -20
- package/runtime/prompts/lead/plan-body-verification.md +16 -1
- package/runtime/prompts/lead/report-writer.md +20 -5
- package/runtime/prompts/lead/team-contract.md +13 -13
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_implementation-diff-review.md +3 -1
- package/runtime/prompts/profiles/_implementation-executor.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -1
- package/runtime/prompts/profiles/implementation.md +4 -2
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +6 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/relay.md +3 -2
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +8 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +33 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +13 -12
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +6 -0
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +3 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +2 -0
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +3 -3
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +6 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +2 -1
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +6 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +2 -1
- package/runtime/python/okstra_ctl/agent_invocation.py +1502 -0
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +788 -0
- package/runtime/python/okstra_ctl/codex_dispatch.py +2 -107
- package/runtime/python/okstra_ctl/context_cost.py +46 -5
- package/runtime/python/okstra_ctl/dispatch_core.py +312 -37
- package/runtime/python/okstra_ctl/dispatch_state.py +461 -36
- package/runtime/python/okstra_ctl/doctor.py +150 -16
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +87 -9
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +214 -23
- package/runtime/python/okstra_ctl/path_hints.py +26 -0
- package/runtime/python/okstra_ctl/paths.py +20 -0
- package/runtime/python/okstra_ctl/ports/__init__.py +8 -0
- package/runtime/python/okstra_ctl/ports/host.py +3 -0
- package/runtime/python/okstra_ctl/ports/host_model.py +60 -0
- package/runtime/python/okstra_ctl/pr_template.py +3 -6
- package/runtime/python/okstra_ctl/registry/host_registry.py +5 -0
- package/runtime/python/okstra_ctl/render.py +217 -12
- package/runtime/python/okstra_ctl/report_finalize.py +44 -0
- package/runtime/python/okstra_ctl/run.py +368 -51
- package/runtime/python/okstra_ctl/session.py +16 -12
- package/runtime/python/okstra_ctl/team.py +11 -11
- package/runtime/python/okstra_ctl/worker_dispatch.py +104 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +5 -38
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +32 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +38 -1
- package/runtime/skills/okstra-code-review/SKILL.md +22 -3
- package/runtime/skills/okstra-run/SKILL.md +16 -1
- package/runtime/skills/okstra-schedule-gen/SKILL.md +15 -1
- package/runtime/templates/implementation-worker-preamble.md +0 -10
- package/runtime/templates/report-writer-prompt-preamble.md +0 -9
- package/runtime/templates/reports/settings.template.json +0 -11
- package/runtime/templates/worker-prompt-preamble.md +0 -10
- package/runtime/validators/lib/fixtures.sh +93 -0
- package/runtime/validators/lib/validate-assets.sh +0 -8
- package/runtime/validators/validate-run.py +182 -0
- package/src/cli-registry.mjs +14 -0
- package/src/commands/execute/agent-prompt.mjs +25 -0
- package/src/commands/execute/codex-dispatch.mjs +6 -63
- package/src/commands/execute/worker-dispatch.mjs +76 -0
- package/src/commands/lifecycle/doctor.mjs +18 -3
- package/src/commands/lifecycle/install.mjs +33 -15
- package/src/commands/lifecycle/uninstall.mjs +4 -3
- package/src/lib/install-assets.mjs +9 -0
- package/runtime/agents/workers/antigravity-worker.md +0 -259
- package/runtime/agents/workers/codex-worker.md +0 -259
- package/runtime/agents/workers/grok-worker.md +0 -259
- package/runtime/agents/workers/kimi-worker.md +0 -259
- package/runtime/prompts/coding-preflight/scripts/preedit-check.sh +0 -79
- package/runtime/templates/operating-standard.md +0 -22
- package/src/lib/worker-agent-render.mjs +0 -50
- /package/runtime/templates/{prd → pr}/pr-body.template.md +0 -0
|
@@ -1,114 +1,9 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
The prepared assignment's ``runner`` field is authoritative. Native Codex
|
|
4
|
-
assignments stay in the host session; this entrypoint executes only the backend
|
|
5
|
-
plan returned by the worker-dispatch application use case.
|
|
6
|
-
"""
|
|
1
|
+
"""Compatibility alias for :mod:`okstra_ctl.worker_dispatch`."""
|
|
7
2
|
from __future__ import annotations
|
|
8
3
|
|
|
9
|
-
import argparse
|
|
10
|
-
import json
|
|
11
4
|
import sys
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any, Mapping, Sequence
|
|
14
|
-
|
|
15
|
-
from . import dispatch_core
|
|
16
|
-
from .adapters.dispatch import with_worker_dispatch
|
|
17
|
-
from .adapters.dispatch.cli_wrapper import CliWrapperDispatchPort
|
|
18
|
-
from .application.dispatch_assignments import dispatch_assignments
|
|
19
|
-
from .dispatch_state import DispatchError
|
|
20
|
-
from .models import provider_wrappers
|
|
21
|
-
from .ports.worker_dispatch import WorkerDispatchRequest
|
|
22
|
-
from .registry.host_registry import default_host_registry
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
SUPPORTED_CLI_WORKERS = provider_wrappers("analyser")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def main(argv: Sequence[str] | None = None) -> int:
|
|
29
|
-
args = _parser().parse_args(argv)
|
|
30
|
-
try:
|
|
31
|
-
if args.jobs_file and args.workers:
|
|
32
|
-
raise DispatchError("--jobs-file and --workers cannot be combined")
|
|
33
|
-
request = _dispatch_request(args)
|
|
34
|
-
port = CliWrapperDispatchPort(
|
|
35
|
-
supported_worker_wrappers=SUPPORTED_CLI_WORKERS,
|
|
36
|
-
unsupported_worker_label="Codex lead",
|
|
37
|
-
required_lead_runtime="codex",
|
|
38
|
-
cli_wrapper_assignments_only=True,
|
|
39
|
-
default_provider_by_worker_id={"report-writer": "codex"},
|
|
40
|
-
)
|
|
41
|
-
host_adapter = with_worker_dispatch(
|
|
42
|
-
default_host_registry().resolve("codex"),
|
|
43
|
-
port,
|
|
44
|
-
)
|
|
45
|
-
plan = dispatch_assignments(request, host_adapter.worker_dispatch())
|
|
46
|
-
backend_plan = plan.backend_plan
|
|
47
|
-
if backend_plan is None:
|
|
48
|
-
raise DispatchError("CLI wrapper dispatch did not produce a backend plan")
|
|
49
|
-
if args.dry_run:
|
|
50
|
-
_print_json(_backend_payload(backend_plan, dry_run=True))
|
|
51
|
-
return 0
|
|
52
|
-
code = dispatch_core.dispatch_plan(backend_plan)
|
|
53
|
-
_print_json({**_backend_payload(backend_plan, dry_run=False), "exitCode": code})
|
|
54
|
-
return code
|
|
55
|
-
except DispatchError as exc:
|
|
56
|
-
print(f"error: {exc}", file=sys.stderr)
|
|
57
|
-
return 2
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _dispatch_request(args: argparse.Namespace) -> WorkerDispatchRequest:
|
|
61
|
-
return WorkerDispatchRequest(
|
|
62
|
-
project_root=Path(args.project_root),
|
|
63
|
-
run_manifest=Path(args.run_manifest),
|
|
64
|
-
workspace_root=Path(args.workspace_root),
|
|
65
|
-
okstra_bin=Path(args.okstra_bin) if args.okstra_bin else None,
|
|
66
|
-
requested_workers=tuple(_parse_workers(args.workers)),
|
|
67
|
-
idle_timeout_seconds=_parse_idle_timeout(args.idle_timeout_seconds),
|
|
68
|
-
dispatch_kind=args.dispatch_kind,
|
|
69
|
-
jobs_file=Path(args.jobs_file) if args.jobs_file else None,
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def _backend_payload(backend_plan, *, dry_run: bool) -> dict[str, Any]:
|
|
74
|
-
payload = backend_plan.to_payload(dry_run=dry_run)
|
|
75
|
-
payload.pop("workerDispatches", None)
|
|
76
|
-
return payload
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def _parser() -> argparse.ArgumentParser:
|
|
80
|
-
parser = argparse.ArgumentParser(
|
|
81
|
-
prog="okstra codex-dispatch",
|
|
82
|
-
description="Dispatch CLI-backed workers for a prepared Codex lead run.",
|
|
83
|
-
)
|
|
84
|
-
parser.add_argument("--project-root", required=True)
|
|
85
|
-
parser.add_argument("--run-manifest", required=True)
|
|
86
|
-
parser.add_argument("--workspace-root", required=True)
|
|
87
|
-
parser.add_argument("--okstra-bin", default="")
|
|
88
|
-
parser.add_argument("--workers", default="")
|
|
89
|
-
parser.add_argument("--dry-run", action="store_true")
|
|
90
|
-
parser.add_argument("--idle-timeout-seconds", default="600")
|
|
91
|
-
parser.add_argument("--dispatch-kind", default="initial")
|
|
92
|
-
parser.add_argument("--jobs-file", default="")
|
|
93
|
-
return parser
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _parse_workers(raw: str) -> list[str]:
|
|
97
|
-
return [item.strip() for item in raw.split(",") if item.strip()]
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def _parse_idle_timeout(raw: str) -> int:
|
|
101
|
-
try:
|
|
102
|
-
value = int(raw)
|
|
103
|
-
except ValueError as exc:
|
|
104
|
-
raise DispatchError("--idle-timeout-seconds must be an integer") from exc
|
|
105
|
-
if value < 0:
|
|
106
|
-
raise DispatchError("--idle-timeout-seconds must be non-negative")
|
|
107
|
-
return value
|
|
108
|
-
|
|
109
5
|
|
|
110
|
-
|
|
111
|
-
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
6
|
+
from .worker_dispatch import main
|
|
112
7
|
|
|
113
8
|
|
|
114
9
|
if __name__ == "__main__":
|
|
@@ -25,8 +25,8 @@ INPUT_FILES = (
|
|
|
25
25
|
"clarification-response.md",
|
|
26
26
|
)
|
|
27
27
|
# Per-run hot-path instruction assets the lead loads OUTSIDE the task bundle:
|
|
28
|
-
# the lifecycle lead contracts (Phase 1 / 2-5 / 5.5 / 6-7) plus the
|
|
29
|
-
# agent specs. These are the prompt-diet (perf plan v2 P2) targets; the
|
|
28
|
+
# the lifecycle lead contracts (Phase 1 / 2-5 / 5.5 / 6-7) plus the host-native
|
|
29
|
+
# functional agent specs. These are the prompt-diet (perf plan v2 P2) targets; the
|
|
30
30
|
# bundle-local metrics below cannot see them, so they get their own metric.
|
|
31
31
|
# The contracts ship as runtime resources under ~/.okstra/prompts/lead/, not
|
|
32
32
|
# as agent skills.
|
|
@@ -38,9 +38,8 @@ HOT_PATH_LEAD_RESOURCES = (
|
|
|
38
38
|
)
|
|
39
39
|
WORKER_AGENT_FILES = (
|
|
40
40
|
"claude-worker.md",
|
|
41
|
-
"codex-worker.md",
|
|
42
|
-
"antigravity-worker.md",
|
|
43
41
|
"report-writer-worker.md",
|
|
42
|
+
"translator-worker.md",
|
|
44
43
|
)
|
|
45
44
|
TIMESTAMPED_ARTIFACT_RE = re.compile(r"\d{4}-\d{2}-\d{2}[_T]\d{2}-\d{2}-\d{2}")
|
|
46
45
|
|
|
@@ -114,6 +113,31 @@ def _latest_matching_file(directory: Path, pattern: str) -> Path | None:
|
|
|
114
113
|
return max(matches, key=lambda path: path.stat().st_mtime)
|
|
115
114
|
|
|
116
115
|
|
|
116
|
+
def _duty_contract_files(
|
|
117
|
+
run_dir: Path | None,
|
|
118
|
+
project_root: Path,
|
|
119
|
+
audience: str,
|
|
120
|
+
) -> list[Path]:
|
|
121
|
+
if run_dir is None:
|
|
122
|
+
return []
|
|
123
|
+
manifest_path = _latest_matching_file(
|
|
124
|
+
run_dir / "manifests",
|
|
125
|
+
"run-manifest-*.json",
|
|
126
|
+
)
|
|
127
|
+
manifest = _load_json(manifest_path) if manifest_path else {}
|
|
128
|
+
contract = manifest.get("agentContract")
|
|
129
|
+
duty_root_value = (
|
|
130
|
+
contract.get("dutyRootPath")
|
|
131
|
+
if isinstance(contract, dict)
|
|
132
|
+
else None
|
|
133
|
+
)
|
|
134
|
+
if not isinstance(duty_root_value, str) or not duty_root_value:
|
|
135
|
+
return []
|
|
136
|
+
duty_root = project_root / duty_root_value
|
|
137
|
+
files = [duty_root / "common.md", duty_root / f"{audience}.md"]
|
|
138
|
+
return [path for path in files if path.is_file()]
|
|
139
|
+
|
|
140
|
+
|
|
117
141
|
def _is_timestamped_legacy_artifact(path: Path) -> bool:
|
|
118
142
|
return bool(TIMESTAMPED_ARTIFACT_RE.search(path.name))
|
|
119
143
|
|
|
@@ -275,12 +299,15 @@ def _lead_phase1_metric(
|
|
|
275
299
|
files.append(team_state)
|
|
276
300
|
mode = "legacy-five-file"
|
|
277
301
|
|
|
302
|
+
duty_files = _duty_contract_files(run_dir, project_root, "lead")
|
|
303
|
+
files.extend(duty_files)
|
|
278
304
|
file_count, byte_count = _count_files(files)
|
|
279
305
|
return {
|
|
280
306
|
"mode": mode,
|
|
281
307
|
"fileCount": file_count,
|
|
282
308
|
"bytes": byte_count,
|
|
283
309
|
"estimatedTokens": _estimate_tokens(files),
|
|
310
|
+
"dutyContractBytes": sum(_file_size(path) for path in duty_files),
|
|
284
311
|
"files": [project_rel(path, project_root) for path in files if path.is_file()],
|
|
285
312
|
}
|
|
286
313
|
|
|
@@ -293,6 +320,12 @@ def _analysis_worker_metric(
|
|
|
293
320
|
) -> dict:
|
|
294
321
|
instruction_set = task_root / "instruction-set"
|
|
295
322
|
full_contract_files = [instruction_set / name for name in INPUT_FILES]
|
|
323
|
+
duty_files = _duty_contract_files(
|
|
324
|
+
run_dir,
|
|
325
|
+
project_root,
|
|
326
|
+
"analysis-worker",
|
|
327
|
+
)
|
|
328
|
+
full_contract_files.extend(duty_files)
|
|
296
329
|
default_preamble = (
|
|
297
330
|
"implementation-worker-preamble.md"
|
|
298
331
|
if task_type == "implementation"
|
|
@@ -309,7 +342,11 @@ def _analysis_worker_metric(
|
|
|
309
342
|
legacy_packet = instruction_set / "task-packet.md"
|
|
310
343
|
primary_packet = packet if packet.is_file() else legacy_packet
|
|
311
344
|
has_packet = primary_packet.is_file()
|
|
312
|
-
packet_files =
|
|
345
|
+
packet_files = (
|
|
346
|
+
[primary_packet, preamble, error_contract, *duty_files]
|
|
347
|
+
if has_packet
|
|
348
|
+
else []
|
|
349
|
+
)
|
|
313
350
|
packet_file_count, packet_byte_count = _count_files(packet_files)
|
|
314
351
|
mode = "analysis-packet-primary" if packet.is_file() else "full-input-contract"
|
|
315
352
|
byte_count = packet_byte_count if packet.is_file() else full_byte_count
|
|
@@ -324,6 +361,7 @@ def _analysis_worker_metric(
|
|
|
324
361
|
"fileCount": file_count,
|
|
325
362
|
"bytesPerWorker": byte_count,
|
|
326
363
|
"estimatedTokensPerWorker": _estimate_tokens(current_files),
|
|
364
|
+
"dutyContractBytes": sum(_file_size(path) for path in duty_files),
|
|
327
365
|
"legacyFullContractBytesPerWorker": full_byte_count,
|
|
328
366
|
"legacyFullContractFileCount": full_file_count,
|
|
329
367
|
"estimatedPacketModeBytesPerWorker": packet_byte_count,
|
|
@@ -382,6 +420,8 @@ def _report_writer_metric(run_dir: Path | None, task_root: Path, project_root: P
|
|
|
382
420
|
"report-writer-prompt-preamble.md",
|
|
383
421
|
)
|
|
384
422
|
files.extend((preamble, error_contract))
|
|
423
|
+
duty_files = _duty_contract_files(run_dir, project_root, "report-writer")
|
|
424
|
+
files.extend(duty_files)
|
|
385
425
|
if run_dir is not None:
|
|
386
426
|
files.extend(_current_seq_worker_results(run_dir))
|
|
387
427
|
convergence = _latest_matching_file(
|
|
@@ -395,6 +435,7 @@ def _report_writer_metric(run_dir: Path | None, task_root: Path, project_root: P
|
|
|
395
435
|
"fileCount": file_count,
|
|
396
436
|
"bytes": byte_count,
|
|
397
437
|
"estimatedTokens": _estimate_tokens(files),
|
|
438
|
+
"dutyContractBytes": sum(_file_size(path) for path in duty_files),
|
|
398
439
|
"promptPreamblePath": str(preamble),
|
|
399
440
|
"workerErrorContractPath": str(error_contract),
|
|
400
441
|
"files": [project_rel(path, project_root) for path in files if path.is_file()],
|