okstra 0.147.0 → 0.148.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 +21 -7
- package/docs/architecture/storage-model.md +34 -61
- package/docs/architecture.md +51 -49
- package/docs/cli.md +38 -21
- package/docs/for-ai/skills/okstra-run.md +13 -34
- package/docs/performance-improvement-plan-v2.md +2 -2
- package/docs/pr-template-usage.md +1 -1
- package/docs/project-structure-overview.md +10 -8
- package/docs/task-process/README.md +4 -4
- package/docs/task-process/common-flow.md +12 -12
- package/docs/task-process/final-verification.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +2 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +2 -2
- package/runtime/agents/workers/grok-worker.md +256 -0
- package/runtime/agents/workers/kimi-worker.md +256 -0
- package/runtime/agents/workers/report-writer-worker.md +2 -2
- package/runtime/bin/lib/okstra/cli.sh +13 -1
- package/runtime/bin/lib/okstra/globals.sh +3 -0
- package/runtime/bin/lib/okstra/usage.sh +17 -12
- package/runtime/bin/okstra-grok-exec.sh +5 -0
- package/runtime/bin/okstra-kimi-exec.sh +5 -0
- package/runtime/bin/okstra-provider-exec.py +235 -0
- package/runtime/bin/okstra.sh +3 -0
- package/runtime/prompts/lead/adapters/antigravity.md +48 -0
- package/runtime/prompts/lead/adapters/claude-code.md +13 -11
- package/runtime/prompts/lead/adapters/codex.md +7 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +12 -12
- package/runtime/prompts/profiles/_implementation-self-check.md +4 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.md +2 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -0
- package/runtime/prompts/profiles/feature-analysis.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +3 -1
- package/runtime/prompts/profiles/forbidden-actions.json +4 -4
- package/runtime/prompts/profiles/implementation-planning.md +3 -1
- package/runtime/prompts/profiles/implementation.md +2 -2
- package/runtime/prompts/profiles/improvement-discovery.md +3 -1
- package/runtime/prompts/profiles/project-analysis.md +2 -0
- package/runtime/prompts/profiles/release-handoff.md +7 -7
- package/runtime/prompts/profiles/requirements-discovery.md +2 -0
- package/runtime/prompts/wizard/prompts.ko.json +9 -1
- package/runtime/python/okstra_ctl/codex_dispatch.py +68 -87
- package/runtime/python/okstra_ctl/dispatch_core.py +4 -22
- package/runtime/python/okstra_ctl/lead_events.py +1 -1
- package/runtime/python/okstra_ctl/lead_runtime.py +13 -2
- package/runtime/python/okstra_ctl/models.py +156 -8
- package/runtime/python/okstra_ctl/path_hints.py +9 -25
- package/runtime/python/okstra_ctl/paths.py +1 -1
- package/runtime/python/okstra_ctl/render.py +172 -74
- package/runtime/python/okstra_ctl/report_html/common.py +38 -2
- package/runtime/python/okstra_ctl/report_html/filters.py +104 -0
- package/runtime/python/okstra_ctl/report_html/render.py +7 -0
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +2 -1
- package/runtime/python/okstra_ctl/report_html/visualizations.py +32 -6
- package/runtime/python/okstra_ctl/run.py +264 -45
- package/runtime/python/okstra_ctl/runner_resolution.py +103 -0
- package/runtime/python/okstra_ctl/team.py +2 -7
- package/runtime/python/okstra_ctl/wizard.py +194 -21
- package/runtime/python/okstra_ctl/worker_artifacts.py +46 -0
- package/runtime/python/okstra_ctl/workers.py +3 -1
- package/runtime/python/okstra_ctl/workflow.py +4 -2
- package/runtime/python/okstra_token_usage/__init__.py +1 -0
- package/runtime/python/okstra_token_usage/collect.py +32 -23
- package/runtime/python/okstra_token_usage/pricing.py +35 -3
- package/runtime/schemas/final-report-v2.0.schema.json +2 -2
- package/runtime/skills/okstra-run/SKILL.md +31 -42
- package/runtime/templates/prd/pr-body.template.md +1 -1
- package/runtime/templates/reports/html/assets/base.css +6 -3
- package/runtime/templates/reports/html/base.template.html +22 -8
- package/runtime/templates/reports/html/macros/forms.html +2 -2
- package/runtime/templates/reports/html/macros/layout.html +5 -5
- package/runtime/templates/reports/html/macros/visualizations.html +11 -1
- package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +11 -11
- package/runtime/templates/reports/html/tasks/error-analysis.template.html +12 -12
- package/runtime/templates/reports/html/tasks/feature-analysis.template.html +9 -9
- package/runtime/templates/reports/html/tasks/final-verification.template.html +7 -7
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +12 -12
- package/runtime/templates/reports/html/tasks/implementation.template.html +7 -7
- package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +6 -6
- package/runtime/templates/reports/html/tasks/project-analysis.template.html +11 -11
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +5 -5
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +8 -8
- package/runtime/templates/reports/report.js +21 -4
- package/runtime/templates/reports/settings.template.json +4 -0
- package/runtime/templates/reports/task-brief.template.md +7 -7
- package/runtime/validators/validate-run.py +11 -6
- package/runtime/validators/validate_session_conformance.py +2 -1
- package/src/cli-registry.mjs +4 -4
- package/src/commands/execute/codex-dispatch.mjs +7 -10
- package/src/commands/execute/render-bundle.mjs +3 -3
- package/src/commands/execute/run.mjs +17 -52
- package/src/commands/execute/wizard.mjs +4 -1
- package/src/commands/lifecycle/doctor.mjs +6 -3
- package/src/commands/lifecycle/install.mjs +31 -8
- package/src/lib/runtime-manifest.mjs +1 -1
- package/src/lib/runtime-resolver.mjs +2 -2
- package/src/lib/worker-agent-render.mjs +50 -0
|
@@ -56,10 +56,27 @@ from .material import (
|
|
|
56
56
|
from .final_report_schema import load_schema_version
|
|
57
57
|
from .final_report_paths import final_report_data_path as _final_report_data_path
|
|
58
58
|
from .lead_events import LeadEvent, append_lead_event
|
|
59
|
-
from .lead_runtime import
|
|
59
|
+
from .lead_runtime import (
|
|
60
|
+
ALLOWED_LEAD_RUNTIMES,
|
|
61
|
+
ARTIFACT_ONLY_LEAD_RUNTIMES,
|
|
62
|
+
lead_runtime_info,
|
|
63
|
+
)
|
|
60
64
|
from .model_discovery import normalize_execution_for_dispatch
|
|
61
|
-
from .models import
|
|
65
|
+
from .models import (
|
|
66
|
+
ModelAssignment,
|
|
67
|
+
default_model,
|
|
68
|
+
provider_default_model,
|
|
69
|
+
provider_ids,
|
|
70
|
+
provider_spec,
|
|
71
|
+
provider_supports_role,
|
|
72
|
+
resolve_model_metadata,
|
|
73
|
+
)
|
|
62
74
|
from .schema_excerpt import build_schema_excerpt
|
|
75
|
+
from .runner_resolution import (
|
|
76
|
+
RunnerResolutionError,
|
|
77
|
+
native_provider_for_host,
|
|
78
|
+
resolve_runner,
|
|
79
|
+
)
|
|
63
80
|
from .report_contract import CURRENT_REPORT_SCHEMA_VERSION
|
|
64
81
|
from .path_resolve import relative_to_project_root, resolve_user_file
|
|
65
82
|
from .render import (
|
|
@@ -329,10 +346,13 @@ class PrepareInputs:
|
|
|
329
346
|
evidence_inputs_raw: str = ""
|
|
330
347
|
directive: str = ""
|
|
331
348
|
workers_override: str = ""
|
|
349
|
+
lead_provider: str = ""
|
|
332
350
|
lead_model: str = ""
|
|
333
351
|
claude_model: str = ""
|
|
334
352
|
codex_model: str = ""
|
|
335
353
|
antigravity_model: str = ""
|
|
354
|
+
worker_models_raw: str = ""
|
|
355
|
+
report_writer_provider: str = ""
|
|
336
356
|
report_writer_model: str = ""
|
|
337
357
|
lead_runtime: str = "claude-code"
|
|
338
358
|
lead_runtime_request: str = ""
|
|
@@ -743,7 +763,7 @@ def _brief_sha256(path: Path) -> str:
|
|
|
743
763
|
|
|
744
764
|
|
|
745
765
|
def _record_artifact_runtime_render_only_event(inp: PrepareInputs, ctx: dict) -> None:
|
|
746
|
-
if inp.lead_runtime not in
|
|
766
|
+
if inp.lead_runtime not in ARTIFACT_ONLY_LEAD_RUNTIMES or not inp.render_only:
|
|
747
767
|
return
|
|
748
768
|
append_lead_event(
|
|
749
769
|
Path(ctx["LEAD_EVENTS_PATH"]),
|
|
@@ -781,10 +801,16 @@ def _canonical_argv(inp: PrepareInputs, ctx: dict) -> list[str]:
|
|
|
781
801
|
("--implementation-option", inp.implementation_option),
|
|
782
802
|
("--clarification-response", inp.clarification_response_path),
|
|
783
803
|
("--workers", workers),
|
|
804
|
+
("--lead-provider", inp.lead_provider or ctx.get("LEAD_PROVIDER", "")),
|
|
784
805
|
("--lead-model", inp.lead_model or ctx.get("LEAD_MODEL", "")),
|
|
785
806
|
("--claude-model", inp.claude_model or ctx.get("CLAUDE_WORKER_MODEL", "")),
|
|
786
807
|
("--codex-model", inp.codex_model or ctx.get("CODEX_WORKER_MODEL", "")),
|
|
787
808
|
("--antigravity-model", inp.antigravity_model or ctx.get("ANTIGRAVITY_WORKER_MODEL", "")),
|
|
809
|
+
("--worker-model", inp.worker_models_raw),
|
|
810
|
+
(
|
|
811
|
+
"--report-writer-provider",
|
|
812
|
+
inp.report_writer_provider or ctx.get("REPORT_WRITER_PROVIDER", ""),
|
|
813
|
+
),
|
|
788
814
|
("--report-writer-model", inp.report_writer_model or ctx.get("REPORT_WRITER_MODEL", "")),
|
|
789
815
|
("--lead-runtime", inp.lead_runtime if inp.lead_runtime != "claude-code" else ""),
|
|
790
816
|
("--executor", inp.executor or ctx.get("EXECUTOR_PROVIDER", "")),
|
|
@@ -1321,6 +1347,28 @@ def _resolve_pr_template(inp: PrepareInputs) -> tuple[str, str]:
|
|
|
1321
1347
|
return str(resolved_tpl.path), resolved_tpl.source
|
|
1322
1348
|
|
|
1323
1349
|
|
|
1350
|
+
@dataclass
|
|
1351
|
+
class RoleAssignment:
|
|
1352
|
+
role: str
|
|
1353
|
+
provider: str
|
|
1354
|
+
model_display: str
|
|
1355
|
+
model_execution_value: str
|
|
1356
|
+
runner: str
|
|
1357
|
+
worker_id: str = ""
|
|
1358
|
+
|
|
1359
|
+
def to_payload(self) -> dict[str, str]:
|
|
1360
|
+
payload = {
|
|
1361
|
+
"role": self.role,
|
|
1362
|
+
"provider": self.provider,
|
|
1363
|
+
"model": self.model_display,
|
|
1364
|
+
"modelExecutionValue": self.model_execution_value,
|
|
1365
|
+
"runner": self.runner,
|
|
1366
|
+
}
|
|
1367
|
+
if self.worker_id:
|
|
1368
|
+
payload["workerId"] = self.worker_id
|
|
1369
|
+
return payload
|
|
1370
|
+
|
|
1371
|
+
|
|
1324
1372
|
@dataclass
|
|
1325
1373
|
class _ModelBindings:
|
|
1326
1374
|
"""이 run 의 lead/worker 모델 + critic + executor 바인딩.
|
|
@@ -1336,6 +1384,7 @@ class _ModelBindings:
|
|
|
1336
1384
|
ge: ModelAssignment
|
|
1337
1385
|
rw: ModelAssignment
|
|
1338
1386
|
critic_choice: str
|
|
1387
|
+
critic_model_execution: str
|
|
1339
1388
|
executor_provider: str
|
|
1340
1389
|
executor_display_name: str
|
|
1341
1390
|
executor_worker_agent: str
|
|
@@ -1343,45 +1392,125 @@ class _ModelBindings:
|
|
|
1343
1392
|
codex_worker_execution: str
|
|
1344
1393
|
antigravity_worker_execution: str
|
|
1345
1394
|
executor_execution: str
|
|
1395
|
+
lead_assignment: RoleAssignment
|
|
1396
|
+
worker_assignments: tuple[RoleAssignment, ...]
|
|
1346
1397
|
|
|
1347
1398
|
|
|
1348
1399
|
def recommended_role_models() -> dict[str, str]:
|
|
1349
1400
|
"""역할 → 추천 모델 display 값 (env override 반영). prepare 의 모델 해소와
|
|
1350
1401
|
wizard 의 안내 표기가 공유하는 단일 기준점."""
|
|
1351
1402
|
lead_default = _default("OKSTRA_DEFAULT_LEAD_MODEL", default_model("lead"))
|
|
1352
|
-
|
|
1403
|
+
recommendations = {
|
|
1353
1404
|
"lead": lead_default,
|
|
1354
1405
|
"claude": _default("OKSTRA_DEFAULT_CLAUDE_MODEL", default_model("claude")),
|
|
1355
1406
|
"codex": _default("OKSTRA_DEFAULT_CODEX_MODEL", default_model("codex")),
|
|
1356
1407
|
"antigravity": _default("OKSTRA_DEFAULT_ANTIGRAVITY_MODEL", default_model("antigravity")),
|
|
1357
1408
|
"report-writer": _default("OKSTRA_DEFAULT_REPORT_WRITER_MODEL", default_model("report-writer")),
|
|
1358
1409
|
}
|
|
1410
|
+
for provider in provider_ids("analyser"):
|
|
1411
|
+
recommendations.setdefault(
|
|
1412
|
+
provider, provider_default_model(provider, "analyser")
|
|
1413
|
+
)
|
|
1414
|
+
return recommendations
|
|
1415
|
+
|
|
1416
|
+
|
|
1417
|
+
def _parse_worker_model_overrides(raw_value: str) -> dict[str, str]:
|
|
1418
|
+
overrides: dict[str, str] = {}
|
|
1419
|
+
for item in (raw_value or "").split(","):
|
|
1420
|
+
item = item.strip()
|
|
1421
|
+
if not item:
|
|
1422
|
+
continue
|
|
1423
|
+
provider, separator, model = item.partition("=")
|
|
1424
|
+
provider = provider.strip().lower()
|
|
1425
|
+
model = model.strip()
|
|
1426
|
+
if not separator or not provider or not model:
|
|
1427
|
+
raise PrepareError(
|
|
1428
|
+
"--worker-model must use provider=model entries separated by commas"
|
|
1429
|
+
)
|
|
1430
|
+
if provider in overrides:
|
|
1431
|
+
raise PrepareError(f"duplicate --worker-model provider: {provider}")
|
|
1432
|
+
provider_spec(provider)
|
|
1433
|
+
overrides[provider] = model
|
|
1434
|
+
return overrides
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
def _model_for_role(
|
|
1438
|
+
provider: str, raw_value: str, role: str, default_override: str = "",
|
|
1439
|
+
) -> ModelAssignment:
|
|
1440
|
+
default_value = default_override or provider_default_model(provider, role)
|
|
1441
|
+
if not default_value:
|
|
1442
|
+
raise PrepareError(f"provider {provider!r} does not define a default for role {role!r}")
|
|
1443
|
+
return resolve_model_metadata(
|
|
1444
|
+
provider=provider,
|
|
1445
|
+
raw_value=raw_value,
|
|
1446
|
+
default_display=default_value,
|
|
1447
|
+
default_execution=default_value,
|
|
1448
|
+
)
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
def _assignment_runner(lead_runtime: str, provider: str, role: str) -> str:
|
|
1452
|
+
try:
|
|
1453
|
+
return resolve_runner(lead_runtime, provider, role).runner
|
|
1454
|
+
except RunnerResolutionError as exc:
|
|
1455
|
+
raise PrepareError(str(exc)) from exc
|
|
1359
1456
|
|
|
1360
1457
|
|
|
1361
1458
|
def _resolve_worker_models(inp: PrepareInputs) -> dict:
|
|
1362
|
-
"""
|
|
1459
|
+
"""Resolve provider models while preserving legacy fixed-provider projections."""
|
|
1363
1460
|
rec = recommended_role_models()
|
|
1461
|
+
overrides = _parse_worker_model_overrides(inp.worker_models_raw)
|
|
1462
|
+
legacy = {
|
|
1463
|
+
"claude": inp.claude_model,
|
|
1464
|
+
"codex": inp.codex_model,
|
|
1465
|
+
"antigravity": inp.antigravity_model,
|
|
1466
|
+
}
|
|
1467
|
+
provider_models = {}
|
|
1468
|
+
for provider in provider_ids("analyser"):
|
|
1469
|
+
default_override = rec.get(provider, "")
|
|
1470
|
+
provider_models[provider] = _model_for_role(
|
|
1471
|
+
provider, overrides.get(provider, legacy.get(provider, "")),
|
|
1472
|
+
"analyser", default_override,
|
|
1473
|
+
)
|
|
1474
|
+
native_lead_provider = native_provider_for_host(inp.lead_runtime)
|
|
1475
|
+
requested_lead_provider = inp.lead_provider.strip().lower()
|
|
1476
|
+
if (
|
|
1477
|
+
native_lead_provider
|
|
1478
|
+
and requested_lead_provider
|
|
1479
|
+
and requested_lead_provider != native_lead_provider
|
|
1480
|
+
):
|
|
1481
|
+
host_label = lead_runtime_info(inp.lead_runtime).agent_label
|
|
1482
|
+
raise PrepareError(
|
|
1483
|
+
f"{host_label.removesuffix(' CLI')} host requires provider "
|
|
1484
|
+
f"{native_lead_provider!r} for the in-session lead; provider "
|
|
1485
|
+
f"{requested_lead_provider!r} must be selected as a worker instead"
|
|
1486
|
+
)
|
|
1487
|
+
lead_provider = native_lead_provider or requested_lead_provider or "claude"
|
|
1488
|
+
report_writer_provider = inp.report_writer_provider.strip().lower() or "claude"
|
|
1489
|
+
if not provider_supports_role(lead_provider, "lead"):
|
|
1490
|
+
raise PrepareError(f"provider {lead_provider!r} does not support the lead role")
|
|
1491
|
+
if not provider_supports_role(report_writer_provider, "report-writer"):
|
|
1492
|
+
raise PrepareError(
|
|
1493
|
+
f"provider {report_writer_provider!r} does not support the report-writer role"
|
|
1494
|
+
)
|
|
1364
1495
|
return {
|
|
1365
|
-
"
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
provider="claude", raw_value=inp.claude_model,
|
|
1371
|
-
default_display=rec["claude"], default_execution=rec["claude"],
|
|
1372
|
-
),
|
|
1373
|
-
"co": resolve_model_metadata(
|
|
1374
|
-
provider="codex", raw_value=inp.codex_model,
|
|
1375
|
-
default_display=rec["codex"], default_execution=rec["codex"],
|
|
1496
|
+
"lead_provider": lead_provider,
|
|
1497
|
+
"report_writer_provider": report_writer_provider,
|
|
1498
|
+
"lead": _model_for_role(
|
|
1499
|
+
lead_provider, inp.lead_model, "lead",
|
|
1500
|
+
rec["lead"] if lead_provider == "claude" else "",
|
|
1376
1501
|
),
|
|
1377
|
-
"
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
default_display=rec["report-writer"], default_execution=rec["report-writer"],
|
|
1502
|
+
"cw": provider_models["claude"],
|
|
1503
|
+
"co": provider_models["codex"],
|
|
1504
|
+
"ge": provider_models["antigravity"],
|
|
1505
|
+
"rw": _model_for_role(
|
|
1506
|
+
report_writer_provider, inp.report_writer_model, "report-writer",
|
|
1507
|
+
rec["report-writer"] if report_writer_provider == "claude" else "",
|
|
1384
1508
|
),
|
|
1509
|
+
"providers": provider_models,
|
|
1510
|
+
"worker_model_inputs": {
|
|
1511
|
+
provider: overrides.get(provider, legacy.get(provider, ""))
|
|
1512
|
+
for provider in provider_ids("analyser")
|
|
1513
|
+
},
|
|
1385
1514
|
}
|
|
1386
1515
|
|
|
1387
1516
|
|
|
@@ -1389,15 +1518,29 @@ def _resolve_model_bindings(inp: PrepareInputs, workers: list[str]) -> _ModelBin
|
|
|
1389
1518
|
"""worker 모델 + critic 선택 + executor 바인딩을 한 묶음으로 해소·검증한다."""
|
|
1390
1519
|
m = _resolve_worker_models(inp)
|
|
1391
1520
|
critic_choice = (inp.critic or "").strip().lower()
|
|
1392
|
-
|
|
1521
|
+
allowed_critics = ["", "off", *provider_ids("critic")]
|
|
1522
|
+
if critic_choice not in allowed_critics:
|
|
1393
1523
|
raise PrepareError(
|
|
1394
|
-
f"--critic must be one of:
|
|
1524
|
+
f"--critic must be one of: {', '.join(allowed_critics[1:])} "
|
|
1525
|
+
f"(got: {critic_choice!r})"
|
|
1526
|
+
)
|
|
1527
|
+
critic_model_execution = ""
|
|
1528
|
+
if critic_choice in provider_ids("critic"):
|
|
1529
|
+
critic_meta = _model_for_role(
|
|
1530
|
+
critic_choice,
|
|
1531
|
+
m["worker_model_inputs"].get(critic_choice, ""),
|
|
1532
|
+
"critic",
|
|
1533
|
+
)
|
|
1534
|
+
critic_model_execution = _normalized_execution(
|
|
1535
|
+
critic_meta, provider=critic_choice, role="critic",
|
|
1395
1536
|
)
|
|
1396
1537
|
executor_default = _default("OKSTRA_DEFAULT_EXECUTOR", "claude")
|
|
1397
1538
|
executor_provider = (inp.executor or executor_default).strip().lower()
|
|
1398
|
-
|
|
1539
|
+
allowed_executors = provider_ids("executor")
|
|
1540
|
+
if executor_provider not in allowed_executors:
|
|
1399
1541
|
raise PrepareError(
|
|
1400
|
-
f"--executor must be one of:
|
|
1542
|
+
f"--executor must be one of: {', '.join(allowed_executors)} "
|
|
1543
|
+
f"(got: {executor_provider!r})"
|
|
1401
1544
|
)
|
|
1402
1545
|
if inp.task_type == "implementation" and executor_provider not in workers:
|
|
1403
1546
|
raise PrepareError(
|
|
@@ -1405,15 +1548,21 @@ def _resolve_model_bindings(inp: PrepareInputs, workers: list[str]) -> _ModelBin
|
|
|
1405
1548
|
f"--workers, but resolved roster is {workers!r}. "
|
|
1406
1549
|
f"Add it explicitly, e.g. --workers {','.join(sorted(set(workers + [executor_provider])))}."
|
|
1407
1550
|
)
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1551
|
+
model_meta = m["providers"][executor_provider]
|
|
1552
|
+
display_name = f"{provider_spec(executor_provider).display_label} executor"
|
|
1553
|
+
worker_agent = f"{executor_provider}-worker"
|
|
1554
|
+
lead_provider = m["lead_provider"]
|
|
1555
|
+
lead_execution = m["lead"].execution
|
|
1556
|
+
lead_runner = _assignment_runner(inp.lead_runtime, lead_provider, "lead")
|
|
1557
|
+
if lead_runner == "cli-wrapper":
|
|
1558
|
+
lead_execution = _normalized_execution(
|
|
1559
|
+
m["lead"], provider=lead_provider, role="lead",
|
|
1560
|
+
)
|
|
1561
|
+
worker_assignments = _build_worker_assignments(inp, workers, m)
|
|
1414
1562
|
return _ModelBindings(
|
|
1415
1563
|
lead=m["lead"], cw=m["cw"], co=m["co"], ge=m["ge"], rw=m["rw"],
|
|
1416
1564
|
critic_choice=critic_choice,
|
|
1565
|
+
critic_model_execution=critic_model_execution,
|
|
1417
1566
|
executor_provider=executor_provider,
|
|
1418
1567
|
executor_display_name=display_name,
|
|
1419
1568
|
executor_worker_agent=worker_agent,
|
|
@@ -1424,9 +1573,42 @@ def _resolve_model_bindings(inp: PrepareInputs, workers: list[str]) -> _ModelBin
|
|
|
1424
1573
|
m["ge"], "antigravity", "worker", workers),
|
|
1425
1574
|
executor_execution=_normalized_execution(
|
|
1426
1575
|
model_meta, provider=executor_provider, role="executor"),
|
|
1576
|
+
lead_assignment=RoleAssignment(
|
|
1577
|
+
role="lead",
|
|
1578
|
+
provider=lead_provider,
|
|
1579
|
+
model_display=m["lead"].display,
|
|
1580
|
+
model_execution_value=lead_execution,
|
|
1581
|
+
runner=lead_runner,
|
|
1582
|
+
),
|
|
1583
|
+
worker_assignments=worker_assignments,
|
|
1427
1584
|
)
|
|
1428
1585
|
|
|
1429
1586
|
|
|
1587
|
+
def _build_worker_assignments(
|
|
1588
|
+
inp: PrepareInputs, workers: list[str], models: dict,
|
|
1589
|
+
) -> tuple[RoleAssignment, ...]:
|
|
1590
|
+
assignments: list[RoleAssignment] = []
|
|
1591
|
+
for worker_id in workers:
|
|
1592
|
+
is_report_writer = worker_id == "report-writer"
|
|
1593
|
+
provider = (
|
|
1594
|
+
models["report_writer_provider"] if is_report_writer else worker_id
|
|
1595
|
+
)
|
|
1596
|
+
role = "report-writer" if is_report_writer else "analyser"
|
|
1597
|
+
meta = models["rw"] if is_report_writer else models["providers"][provider]
|
|
1598
|
+
execution = _worker_execution_value(
|
|
1599
|
+
meta, provider, "worker", [provider],
|
|
1600
|
+
)
|
|
1601
|
+
assignments.append(RoleAssignment(
|
|
1602
|
+
role=role,
|
|
1603
|
+
provider=provider,
|
|
1604
|
+
model_display=meta.display,
|
|
1605
|
+
model_execution_value=execution,
|
|
1606
|
+
runner=_assignment_runner(inp.lead_runtime, provider, role),
|
|
1607
|
+
worker_id=worker_id,
|
|
1608
|
+
))
|
|
1609
|
+
return tuple(assignments)
|
|
1610
|
+
|
|
1611
|
+
|
|
1430
1612
|
def _normalized_execution(meta: ModelAssignment, provider: str, role: str) -> str:
|
|
1431
1613
|
"""Route a role's catalog execution value through the CLI-identity normalizer
|
|
1432
1614
|
so the manifest carries the dispatch-correct spelling. SSOT for the mapping
|
|
@@ -1760,7 +1942,7 @@ def _render_lead_prompt_and_snapshot(
|
|
|
1760
1942
|
"""compute/default 토큰을 ctx 에 주입한 뒤 final-report 템플릿 사본과 lead 실행
|
|
1761
1943
|
프롬프트를 렌더하고, 프롬프트 스냅샷을 기록한 후 prompt_text 를 돌려준다."""
|
|
1762
1944
|
# inject populates ctx with compute + default tokens consumed by the lead
|
|
1763
|
-
# prompt render below (
|
|
1945
|
+
# prompt render below (lead-execution-prompt.md). The final-report
|
|
1764
1946
|
# template render is effectively a copy (Jinja2 `{{ var }}` syntax does
|
|
1765
1947
|
# not match `_TOKEN_RE`); routed through render_template_with_ctx for SOT
|
|
1766
1948
|
# consistency.
|
|
@@ -1793,10 +1975,11 @@ def _render_lead_prompt_and_snapshot(
|
|
|
1793
1975
|
)
|
|
1794
1976
|
except Exception: # noqa: BLE001 — advisory artifact; never fail prep over it
|
|
1795
1977
|
pass
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
)
|
|
1799
|
-
prompt_text =
|
|
1978
|
+
lead_prompt_path = instruction_set / "lead-execution-prompt.md"
|
|
1979
|
+
legacy_claude_prompt_path = instruction_set / "claude-execution-prompt.md"
|
|
1980
|
+
render_template_with_ctx(str(prompt_template), str(lead_prompt_path), ctx)
|
|
1981
|
+
prompt_text = lead_prompt_path.read_text(encoding="utf-8")
|
|
1982
|
+
legacy_claude_prompt_path.write_text(prompt_text, encoding="utf-8")
|
|
1800
1983
|
Path(ctx["RUN_PROMPT_SNAPSHOT_FILE"]).parent.mkdir(parents=True, exist_ok=True)
|
|
1801
1984
|
Path(ctx["RUN_PROMPT_SNAPSHOT_FILE"]).write_text(prompt_text, encoding="utf-8")
|
|
1802
1985
|
return prompt_text
|
|
@@ -1825,10 +2008,22 @@ def _persist_run_inputs(
|
|
|
1825
2008
|
"taskBriefAbsolutePath": str(inp.brief_path),
|
|
1826
2009
|
"directive": inp.directive,
|
|
1827
2010
|
"workers": selected_reviewers,
|
|
2011
|
+
"leadProvider": models.lead_assignment.provider,
|
|
1828
2012
|
"leadModel": models.lead.display,
|
|
1829
2013
|
"claudeModel": models.cw.display,
|
|
1830
2014
|
"codexModel": models.co.display,
|
|
1831
2015
|
"antigravityModel": models.ge.display,
|
|
2016
|
+
"workerAssignments": [
|
|
2017
|
+
assignment.to_payload() for assignment in models.worker_assignments
|
|
2018
|
+
],
|
|
2019
|
+
"reportWriterProvider": next(
|
|
2020
|
+
(
|
|
2021
|
+
assignment.provider
|
|
2022
|
+
for assignment in models.worker_assignments
|
|
2023
|
+
if assignment.worker_id == "report-writer"
|
|
2024
|
+
),
|
|
2025
|
+
inp.report_writer_provider or "claude",
|
|
2026
|
+
),
|
|
1832
2027
|
"reportWriterModel": models.rw.display,
|
|
1833
2028
|
"executor": models.executor_provider,
|
|
1834
2029
|
"relatedTasks": inp.related_tasks_raw,
|
|
@@ -1925,10 +2120,10 @@ def _finalize_status_and_render_manifests(
|
|
|
1925
2120
|
"""최종 task/run status 를 확정하고 workflow state 를 재계산한 뒤 team-state,
|
|
1926
2121
|
task-manifest, task-index, run-manifest, timeline, discovery 산출물을 렌더한다."""
|
|
1927
2122
|
if inp.render_only:
|
|
1928
|
-
ctx["CURRENT_TASK_STATUS"] = "
|
|
2123
|
+
ctx["CURRENT_TASK_STATUS"] = "ready-for-lead"
|
|
1929
2124
|
ctx["CURRENT_RUN_STATUS"] = "prepared"
|
|
1930
2125
|
else:
|
|
1931
|
-
ctx["CURRENT_TASK_STATUS"] = "
|
|
2126
|
+
ctx["CURRENT_TASK_STATUS"] = "lead-session-started"
|
|
1932
2127
|
ctx["CURRENT_RUN_STATUS"] = "in-progress"
|
|
1933
2128
|
ctx["LATEST_REPORT_PATH"] = ctx["FINAL_REPORT_PATH"]
|
|
1934
2129
|
ctx["LATEST_REPORT_RELATIVE_PATH"] = ctx["FINAL_REPORT_RELATIVE_PATH"]
|
|
@@ -2060,7 +2255,7 @@ def _initial_workflow_ctx(
|
|
|
2060
2255
|
constants; `_finalize_status_and_render_manifests` recomputes the block once
|
|
2061
2256
|
the real status is settled.
|
|
2062
2257
|
"""
|
|
2063
|
-
task_status, run_status = "ready-for-
|
|
2258
|
+
task_status, run_status = "ready-for-lead", "not-run"
|
|
2064
2259
|
return {
|
|
2065
2260
|
"CURRENT_TASK_STATUS": task_status,
|
|
2066
2261
|
"CURRENT_RUN_STATUS": run_status,
|
|
@@ -2091,7 +2286,15 @@ def _model_ctx(models: "_ModelBindings") -> dict[str, str]:
|
|
|
2091
2286
|
"""Render tokens for every model binding this run resolved."""
|
|
2092
2287
|
return {
|
|
2093
2288
|
"LEAD_MODEL": models.lead.display,
|
|
2094
|
-
"LEAD_MODEL_EXECUTION_VALUE": models.
|
|
2289
|
+
"LEAD_MODEL_EXECUTION_VALUE": models.lead_assignment.model_execution_value,
|
|
2290
|
+
"LEAD_PROVIDER": models.lead_assignment.provider,
|
|
2291
|
+
"LEAD_ASSIGNMENT_JSON": json.dumps(
|
|
2292
|
+
models.lead_assignment.to_payload(), ensure_ascii=False,
|
|
2293
|
+
),
|
|
2294
|
+
"WORKER_ASSIGNMENTS_JSON": json.dumps(
|
|
2295
|
+
[assignment.to_payload() for assignment in models.worker_assignments],
|
|
2296
|
+
ensure_ascii=False,
|
|
2297
|
+
),
|
|
2095
2298
|
"CLAUDE_WORKER_MODEL": models.cw.display,
|
|
2096
2299
|
"CLAUDE_WORKER_MODEL_EXECUTION_VALUE": models.cw.execution,
|
|
2097
2300
|
"CODEX_WORKER_MODEL": models.co.display,
|
|
@@ -2100,12 +2303,20 @@ def _model_ctx(models: "_ModelBindings") -> dict[str, str]:
|
|
|
2100
2303
|
"ANTIGRAVITY_WORKER_MODEL_EXECUTION_VALUE": models.antigravity_worker_execution,
|
|
2101
2304
|
"REPORT_WRITER_MODEL": models.rw.display,
|
|
2102
2305
|
"REPORT_WRITER_MODEL_EXECUTION_VALUE": models.rw.execution,
|
|
2306
|
+
"REPORT_WRITER_PROVIDER": next(
|
|
2307
|
+
(
|
|
2308
|
+
assignment.provider for assignment in models.worker_assignments
|
|
2309
|
+
if assignment.worker_id == "report-writer"
|
|
2310
|
+
),
|
|
2311
|
+
"",
|
|
2312
|
+
),
|
|
2103
2313
|
"EXECUTOR_PROVIDER": models.executor_provider,
|
|
2104
2314
|
"EXECUTOR_DISPLAY_NAME": models.executor_display_name,
|
|
2105
2315
|
"EXECUTOR_WORKER_AGENT": models.executor_worker_agent,
|
|
2106
2316
|
"EXECUTOR_MODEL_DISPLAY": models.executor_model_meta.display,
|
|
2107
2317
|
"EXECUTOR_MODEL_EXECUTION_VALUE": models.executor_execution,
|
|
2108
2318
|
"CRITIC_CHOICE": models.critic_choice,
|
|
2319
|
+
"CRITIC_MODEL_EXECUTION_VALUE": models.critic_model_execution,
|
|
2109
2320
|
}
|
|
2110
2321
|
|
|
2111
2322
|
|
|
@@ -2351,6 +2562,7 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
2351
2562
|
ctx.update({
|
|
2352
2563
|
"ANALYSIS_PROFILE": inp.task_type,
|
|
2353
2564
|
"RECOMMENDED_ANALYSERS": selected_reviewers,
|
|
2565
|
+
"HOST_RUNTIME": lead_runtime,
|
|
2354
2566
|
"LEAD_RUNTIME": lead_runtime,
|
|
2355
2567
|
"LEAD_RUNTIME_REQUEST": lead_runtime_request,
|
|
2356
2568
|
"RUNTIME_RESOLUTION_JSON": runtime_resolution_json or "{}",
|
|
@@ -2459,10 +2671,13 @@ def main(argv: list[str]) -> int:
|
|
|
2459
2671
|
),
|
|
2460
2672
|
)
|
|
2461
2673
|
p.add_argument("--workers", default="", dest="workers_override")
|
|
2674
|
+
p.add_argument("--lead-provider", default="")
|
|
2462
2675
|
p.add_argument("--lead-model", default="")
|
|
2463
2676
|
p.add_argument("--claude-model", default="")
|
|
2464
2677
|
p.add_argument("--codex-model", default="")
|
|
2465
2678
|
p.add_argument("--antigravity-model", default="")
|
|
2679
|
+
p.add_argument("--worker-model", default="", dest="worker_models_raw")
|
|
2680
|
+
p.add_argument("--report-writer-provider", default="")
|
|
2466
2681
|
p.add_argument("--report-writer-model", default="")
|
|
2467
2682
|
p.add_argument(
|
|
2468
2683
|
"--lead-runtime",
|
|
@@ -2471,8 +2686,9 @@ def main(argv: list[str]) -> int:
|
|
|
2471
2686
|
dest="lead_runtime",
|
|
2472
2687
|
help=(
|
|
2473
2688
|
"Lead runtime adapter. Default: claude-code. `codex` renders "
|
|
2474
|
-
"Codex CLI artifact bundles; `
|
|
2475
|
-
"bundles
|
|
2689
|
+
"Codex CLI artifact bundles; `antigravity` renders Antigravity CLI "
|
|
2690
|
+
"artifact bundles; `external` renders neutral bootstrap bundles for "
|
|
2691
|
+
"manifest inspection until a dispatch backend is enabled."
|
|
2476
2692
|
),
|
|
2477
2693
|
)
|
|
2478
2694
|
p.add_argument(
|
|
@@ -2628,10 +2844,13 @@ def main(argv: list[str]) -> int:
|
|
|
2628
2844
|
evidence_inputs_raw=args.evidence_inputs_raw,
|
|
2629
2845
|
directive=args.directive,
|
|
2630
2846
|
workers_override=args.workers_override,
|
|
2847
|
+
lead_provider=args.lead_provider,
|
|
2631
2848
|
lead_model=args.lead_model,
|
|
2632
2849
|
claude_model=args.claude_model,
|
|
2633
2850
|
codex_model=args.codex_model,
|
|
2634
2851
|
antigravity_model=args.antigravity_model,
|
|
2852
|
+
worker_models_raw=args.worker_models_raw,
|
|
2853
|
+
report_writer_provider=args.report_writer_provider,
|
|
2635
2854
|
report_writer_model=args.report_writer_model,
|
|
2636
2855
|
lead_runtime=args.lead_runtime,
|
|
2637
2856
|
lead_runtime_request=args.lead_runtime_request,
|
|
@@ -2691,7 +2910,7 @@ def main(argv: list[str]) -> int:
|
|
|
2691
2910
|
"claudeSessionId": ctx["CLAUDE_SESSION_ID"],
|
|
2692
2911
|
"leadModelExecutionValue": ctx["LEAD_MODEL_EXECUTION_VALUE"],
|
|
2693
2912
|
"projectRoot": ctx["PROJECT_ROOT"],
|
|
2694
|
-
"promptFile": str(Path(ctx["INSTRUCTION_SET_PATH"]) / "
|
|
2913
|
+
"promptFile": str(Path(ctx["INSTRUCTION_SET_PATH"]) / "lead-execution-prompt.md"),
|
|
2695
2914
|
}
|
|
2696
2915
|
print(f"__OKSTRA_LAUNCH__ {json.dumps(machine)}")
|
|
2697
2916
|
return 0
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Resolve native versus CLI execution from host and provider capabilities."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from .models import (
|
|
7
|
+
UnknownProviderError,
|
|
8
|
+
provider_spec,
|
|
9
|
+
provider_supports_role,
|
|
10
|
+
provider_wrappers,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
NATIVE_SESSION = "native-session"
|
|
14
|
+
CLI_WRAPPER = "cli-wrapper"
|
|
15
|
+
RUNNER_VALUES = frozenset({NATIVE_SESSION, CLI_WRAPPER})
|
|
16
|
+
|
|
17
|
+
_HOST_NATIVE_PROVIDERS = {
|
|
18
|
+
"claude-code": frozenset({"claude"}),
|
|
19
|
+
"codex": frozenset({"codex"}),
|
|
20
|
+
"antigravity": frozenset({"antigravity"}),
|
|
21
|
+
"external": frozenset(),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def native_provider_for_host(host_runtime: str) -> str:
|
|
26
|
+
"""Return the single provider owned by an in-session host, if any."""
|
|
27
|
+
providers = _HOST_NATIVE_PROVIDERS.get(host_runtime)
|
|
28
|
+
if providers is None:
|
|
29
|
+
allowed = ", ".join(_HOST_NATIVE_PROVIDERS)
|
|
30
|
+
raise RunnerResolutionError(
|
|
31
|
+
f"unknown host runtime {host_runtime!r}. Allowed values: {allowed}"
|
|
32
|
+
)
|
|
33
|
+
return next(iter(providers), "")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RunnerResolutionError(ValueError):
|
|
37
|
+
"""Raised when an assignment cannot run on the selected host."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class RunnerResolution:
|
|
42
|
+
runner: str
|
|
43
|
+
wrapper: str
|
|
44
|
+
reason: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def resolve_runner(
|
|
48
|
+
host_runtime: str,
|
|
49
|
+
provider: str,
|
|
50
|
+
role: str,
|
|
51
|
+
requested_runner: str = "",
|
|
52
|
+
) -> RunnerResolution:
|
|
53
|
+
"""Resolve an assignment without inferring the host from installed CLIs."""
|
|
54
|
+
native_providers = _HOST_NATIVE_PROVIDERS.get(host_runtime)
|
|
55
|
+
if native_providers is None:
|
|
56
|
+
allowed = ", ".join(_HOST_NATIVE_PROVIDERS)
|
|
57
|
+
raise RunnerResolutionError(
|
|
58
|
+
f"unknown host runtime {host_runtime!r}. Allowed values: {allowed}"
|
|
59
|
+
)
|
|
60
|
+
try:
|
|
61
|
+
spec = provider_spec(provider)
|
|
62
|
+
except UnknownProviderError as exc:
|
|
63
|
+
raise RunnerResolutionError(str(exc)) from exc
|
|
64
|
+
if not provider_supports_role(provider, role):
|
|
65
|
+
raise RunnerResolutionError(
|
|
66
|
+
f"provider {provider!r} does not support role {role!r}"
|
|
67
|
+
)
|
|
68
|
+
requested = (requested_runner or "").strip()
|
|
69
|
+
if requested and requested not in RUNNER_VALUES:
|
|
70
|
+
allowed = ", ".join(sorted(RUNNER_VALUES))
|
|
71
|
+
raise RunnerResolutionError(
|
|
72
|
+
f"unknown runner {requested!r}. Allowed values: {allowed}"
|
|
73
|
+
)
|
|
74
|
+
if requested == NATIVE_SESSION and provider not in native_providers:
|
|
75
|
+
raise RunnerResolutionError(
|
|
76
|
+
f"host runtime {host_runtime!r} does not provide native {provider!r} execution"
|
|
77
|
+
)
|
|
78
|
+
if requested == NATIVE_SESSION or (not requested and provider in native_providers):
|
|
79
|
+
return RunnerResolution(
|
|
80
|
+
runner=NATIVE_SESSION,
|
|
81
|
+
wrapper="",
|
|
82
|
+
reason=f"{host_runtime} provides native {provider} execution",
|
|
83
|
+
)
|
|
84
|
+
if not spec.wrapper:
|
|
85
|
+
raise RunnerResolutionError(
|
|
86
|
+
f"provider {provider!r} has no CLI wrapper for role {role!r}"
|
|
87
|
+
)
|
|
88
|
+
return RunnerResolution(
|
|
89
|
+
runner=CLI_WRAPPER,
|
|
90
|
+
wrapper=spec.wrapper,
|
|
91
|
+
reason=f"{host_runtime} uses the registered {provider} CLI wrapper",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def worker_wrapper_registry(report_writer_provider: str = "claude") -> dict[str, str]:
|
|
96
|
+
"""Worker-id wrapper map for dispatch front ends."""
|
|
97
|
+
wrappers = provider_wrappers("analyser")
|
|
98
|
+
report_writer = provider_spec(report_writer_provider)
|
|
99
|
+
if not provider_supports_role(report_writer_provider, "report-writer"):
|
|
100
|
+
raise RunnerResolutionError(
|
|
101
|
+
f"provider {report_writer_provider!r} does not support report-writer"
|
|
102
|
+
)
|
|
103
|
+
return {**wrappers, "report-writer": report_writer.wrapper}
|
|
@@ -16,13 +16,9 @@ from .dispatch_core import (
|
|
|
16
16
|
build_dispatch_plan,
|
|
17
17
|
dispatch_plan,
|
|
18
18
|
)
|
|
19
|
+
from .runner_resolution import worker_wrapper_registry
|
|
19
20
|
|
|
20
|
-
_SUPPORTED_WRAPPERS =
|
|
21
|
-
"claude": "okstra-claude-exec.sh",
|
|
22
|
-
"codex": "okstra-codex-exec.sh",
|
|
23
|
-
"antigravity": "okstra-antigravity-exec.sh",
|
|
24
|
-
"report-writer": "okstra-claude-exec.sh",
|
|
25
|
-
}
|
|
21
|
+
_SUPPORTED_WRAPPERS = worker_wrapper_registry()
|
|
26
22
|
_TERMINAL_STATUSES = {"completed", "timeout", "error", "not-run"}
|
|
27
23
|
|
|
28
24
|
|
|
@@ -98,7 +94,6 @@ def _dispatch(args) -> int:
|
|
|
98
94
|
okstra_bin=Path(args.okstra_bin),
|
|
99
95
|
requested_workers=requested,
|
|
100
96
|
idle_timeout_seconds=args.idle_timeout_seconds,
|
|
101
|
-
enable_report_writer="report-writer" in requested,
|
|
102
97
|
required_lead_runtime="external",
|
|
103
98
|
default_backend=BACKEND_TMUX_PANE,
|
|
104
99
|
supported_worker_wrappers=_SUPPORTED_WRAPPERS,
|