phase-loop-runtime 0.2.0__py3-none-any.whl
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.
- phase_loop_runtime/__init__.py +20 -0
- phase_loop_runtime/_contract_docs/phase-loop/extraction-readiness.md +90 -0
- phase_loop_runtime/_contract_docs/phase-loop/granular-execution-policy.md +137 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-capability-matrix.md +510 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-skill-matrix.md +125 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-substrate-manifest.md +232 -0
- phase_loop_runtime/_contract_docs/phase-loop/pi-loop-control.md +247 -0
- phase_loop_runtime/_contract_docs/phase-loop/protocol.md +2759 -0
- phase_loop_runtime/_contract_docs/phase-loop/runtime-boundary.md +289 -0
- phase_loop_runtime/_contract_docs/phase-loop/spec-discovery-roots.md +47 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-governed-pipeline.md +42 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-portal-projection.md +34 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-regenesis.md +29 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-report.md +32 -0
- phase_loop_runtime/_contract_docs/runtime/verification-evidence-contract.md +52 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/commands.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/mixed.json +5 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/timeout-note.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/redacted-summary.json +6 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/sample.json +7 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/edge.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-a.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-b.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-c.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/claimed-results.json +5 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/metrics.json +9 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/findings.md +10 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/scores.json +4 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/summary.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/bundle-check.json +8 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/operator-note.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/events-sample.jsonl +3 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/receipt.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/evidence.json +13 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/summary.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/matrix.json +9 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/notes.md +4 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/console-summary.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/screenshot-standin.ppm +5 -0
- phase_loop_runtime/adoption_bundle.py +273 -0
- phase_loop_runtime/advisor_board/CONTRACTS.md +257 -0
- phase_loop_runtime/advisor_board/__init__.py +288 -0
- phase_loop_runtime/advisor_board/backing.py +137 -0
- phase_loop_runtime/advisor_board/backing_omnigent.py +498 -0
- phase_loop_runtime/advisor_board/config.py +225 -0
- phase_loop_runtime/advisor_board/events.py +115 -0
- phase_loop_runtime/advisor_board/fixtures/advisor-boards.example.toml +59 -0
- phase_loop_runtime/advisor_board/fixtures.py +91 -0
- phase_loop_runtime/advisor_board/harness_mapping.py +125 -0
- phase_loop_runtime/advisor_board/matrix.py +146 -0
- phase_loop_runtime/advisor_board/observability.py +481 -0
- phase_loop_runtime/advisor_board/presets.py +188 -0
- phase_loop_runtime/advisor_board/registries.py +309 -0
- phase_loop_runtime/advisor_board/resolver.py +253 -0
- phase_loop_runtime/advisor_board/schema.py +269 -0
- phase_loop_runtime/advisor_board/standin.py +166 -0
- phase_loop_runtime/advisor_board/validation.py +159 -0
- phase_loop_runtime/agent_runtime_provider.py +676 -0
- phase_loop_runtime/baml_modular.py +656 -0
- phase_loop_runtime/baml_src/dotfiles_adoption_manifest.baml +37 -0
- phase_loop_runtime/baml_src/dotfiles_c4_document.baml +14 -0
- phase_loop_runtime/baml_src/dotfiles_plan_manifest.baml +50 -0
- phase_loop_runtime/baml_src/dotfiles_runtime_projection.baml +16 -0
- phase_loop_runtime/baml_src/dotfiles_task_catalog.baml +20 -0
- phase_loop_runtime/baml_src/emit_phase_closeout.baml +86 -0
- phase_loop_runtime/baml_src/evaluate_suspected_fake_evidence.baml +36 -0
- phase_loop_runtime/baml_src/verification_evidence.baml +40 -0
- phase_loop_runtime/broker.py +181 -0
- phase_loop_runtime/build_bundle.py +294 -0
- phase_loop_runtime/capability_registry.py +835 -0
- phase_loop_runtime/classifier.py +87 -0
- phase_loop_runtime/claude_agent_view.py +625 -0
- phase_loop_runtime/claude_channel_sidecar.py +1086 -0
- phase_loop_runtime/cli.py +2599 -0
- phase_loop_runtime/closeout.py +865 -0
- phase_loop_runtime/closeout_classifier.py +120 -0
- phase_loop_runtime/closeout_evidence_audit.py +112 -0
- phase_loop_runtime/closeout_validation.py +144 -0
- phase_loop_runtime/closeout_validators.py +203 -0
- phase_loop_runtime/consiliency_gates.py +374 -0
- phase_loop_runtime/consiliency_ingest.py +399 -0
- phase_loop_runtime/consiliency_layout.py +176 -0
- phase_loop_runtime/consiliency_scaffold.py +305 -0
- phase_loop_runtime/cross_repo_channel.py +331 -0
- phase_loop_runtime/discovery.py +2368 -0
- phase_loop_runtime/dispatch_lock.py +167 -0
- phase_loop_runtime/doc_delta_validator.py +98 -0
- phase_loop_runtime/docs_audit.py +285 -0
- phase_loop_runtime/docs_freshness.py +468 -0
- phase_loop_runtime/docs_surfaces.py +150 -0
- phase_loop_runtime/dotfiles_profile_plugin.py +96 -0
- phase_loop_runtime/events.py +154 -0
- phase_loop_runtime/events_migration.py +96 -0
- phase_loop_runtime/evidence_audit.py +1082 -0
- phase_loop_runtime/evidence_audit_config.py +172 -0
- phase_loop_runtime/fleet_map.py +426 -0
- phase_loop_runtime/fleet_metrics.py +298 -0
- phase_loop_runtime/fleet_metrics_export.py +196 -0
- phase_loop_runtime/git_discipline.py +479 -0
- phase_loop_runtime/git_ops.py +122 -0
- phase_loop_runtime/git_topology.py +252 -0
- phase_loop_runtime/governed_bundle.py +137 -0
- phase_loop_runtime/governed_premerge.py +219 -0
- phase_loop_runtime/governed_review.py +264 -0
- phase_loop_runtime/handoff.py +932 -0
- phase_loop_runtime/handoff_path.py +14 -0
- phase_loop_runtime/injection.py +740 -0
- phase_loop_runtime/install_status.py +88 -0
- phase_loop_runtime/lane_scheduler.py +317 -0
- phase_loop_runtime/launcher.py +2760 -0
- phase_loop_runtime/lease_store.py +553 -0
- phase_loop_runtime/maintenance.py +401 -0
- phase_loop_runtime/migrate_handoffs.py +201 -0
- phase_loop_runtime/models.py +2144 -0
- phase_loop_runtime/observability.py +1290 -0
- phase_loop_runtime/panel_invoker.py +1638 -0
- phase_loop_runtime/phase_loop_drift_audit.py +388 -0
- phase_loop_runtime/phase_worktree_executor.py +433 -0
- phase_loop_runtime/pipeline_adapter/__init__.py +12 -0
- phase_loop_runtime/pipeline_adapter/branch_ops.py +274 -0
- phase_loop_runtime/pipeline_adapter/flag.py +55 -0
- phase_loop_runtime/pipeline_adapter/markers.py +13 -0
- phase_loop_runtime/pipeline_adapter/merge_policy.py +72 -0
- phase_loop_runtime/pipeline_adapter/ratification.py +100 -0
- phase_loop_runtime/pipeline_adapter/sibling_matcher.py +123 -0
- phase_loop_runtime/plan_ir.py +523 -0
- phase_loop_runtime/plan_manifest.py +415 -0
- phase_loop_runtime/planner_validation.py +176 -0
- phase_loop_runtime/profiles.py +462 -0
- phase_loop_runtime/prompts.py +365 -0
- phase_loop_runtime/provenance.py +165 -0
- phase_loop_runtime/publishing.py +339 -0
- phase_loop_runtime/py.typed +1 -0
- phase_loop_runtime/reconcile.py +1550 -0
- phase_loop_runtime/redaction.py +184 -0
- phase_loop_runtime/reflection_sync.py +293 -0
- phase_loop_runtime/release_guard.py +186 -0
- phase_loop_runtime/render.py +432 -0
- phase_loop_runtime/repo_validation.py +427 -0
- phase_loop_runtime/review_summary.py +150 -0
- phase_loop_runtime/roadmap_lint.py +425 -0
- phase_loop_runtime/route_log.py +59 -0
- phase_loop_runtime/route_policy/__init__.py +11 -0
- phase_loop_runtime/route_policy/fixtures/route_selection.golden.json +263 -0
- phase_loop_runtime/runner.py +8400 -0
- phase_loop_runtime/runtime_paths.py +225 -0
- phase_loop_runtime/runtime_projection.py +113 -0
- phase_loop_runtime/runtime_resources.py +36 -0
- phase_loop_runtime/schema_export.py +292 -0
- phase_loop_runtime/schemas/phase_loop_closeout.schema.json +366 -0
- phase_loop_runtime/skill_install.py +176 -0
- phase_loop_runtime/skill_inventory.py +662 -0
- phase_loop_runtime/skill_paths.py +42 -0
- phase_loop_runtime/skill_sources_plugin.py +37 -0
- phase_loop_runtime/skills_bundle/claude-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/claude-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/claude-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-execute-detailed/SKILL.md +152 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/SKILL.md +745 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/claude-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-phase-loop/SKILL.md +115 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/SKILL.md +439 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/SKILL.md +334 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/SKILL.md +776 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/claude-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/SKILL.md +243 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/SKILL.md +249 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/SKILL.md +74 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/codex-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/codex-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/codex-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-execute-detailed/SKILL.md +154 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/SKILL.md +231 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/codex-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-phase-loop/SKILL.md +248 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/SKILL.md +150 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/SKILL.md +307 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/codex-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/SKILL.md +94 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/SKILL.md +114 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/SKILL.md +102 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/gemini-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-execute-detailed/SKILL.md +144 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/SKILL.md +198 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/gemini-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-phase-loop/SKILL.md +111 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/SKILL.md +147 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/SKILL.md +217 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/gemini-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/SKILL.md +87 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/SKILL.md +109 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/SKILL.md +98 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/opencode-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-execute-detailed/SKILL.md +144 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/SKILL.md +196 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/opencode-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-phase-loop/SKILL.md +113 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/SKILL.md +147 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/SKILL.md +211 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/opencode-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/SKILL.md +87 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/SKILL.md +109 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/SKILL.md +98 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/state.py +156 -0
- phase_loop_runtime/state_degradation.py +136 -0
- phase_loop_runtime/state_ops.py +676 -0
- phase_loop_runtime/train_ledger.py +215 -0
- phase_loop_runtime/train_roadmap.py +339 -0
- phase_loop_runtime/train_runner.py +1197 -0
- phase_loop_runtime/verification_evidence.py +650 -0
- phase_loop_runtime/verification_evidence_validator.py +75 -0
- phase_loop_runtime/visual_evidence_validator.py +36 -0
- phase_loop_runtime/worker_pool.py +123 -0
- phase_loop_runtime/worktree_index.py +270 -0
- phase_loop_runtime-0.2.0.data/data/share/phase-loop-runtime/protocol/protocol.md +2475 -0
- phase_loop_runtime-0.2.0.dist-info/METADATA +106 -0
- phase_loop_runtime-0.2.0.dist-info/RECORD +370 -0
- phase_loop_runtime-0.2.0.dist-info/WHEEL +5 -0
- phase_loop_runtime-0.2.0.dist-info/entry_points.txt +9 -0
- phase_loop_runtime-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""Omnigent provider backing (ABDOMNI, Phase 5).
|
|
2
|
+
|
|
3
|
+
The ``omnigent`` transport for breadth harnesses (opencode / pi, and cursor / amp
|
|
4
|
+
when the live catalog reports them). It routes a board seat through
|
|
5
|
+
omniagent-plus -> Omnigent **v0.4.0** over the frozen HTTP surface, opt-in and
|
|
6
|
+
fail-closed. It is the sibling of ABDHOME's ``homebrew`` backing (the built-3 +
|
|
7
|
+
native host leg): homebrew is hand-written and stays byte-for-byte the legacy
|
|
8
|
+
panel; omnigent is *not* hand-written per harness — it reuses the Omnigent
|
|
9
|
+
launcher, which is the whole point of the maintenance-offload (ABDHOME/ABDOMNI
|
|
10
|
+
non-goal: never hand-write a breadth adapter).
|
|
11
|
+
|
|
12
|
+
**We do not fork the transport.** This module mirrors the method set of
|
|
13
|
+
omniagent-plus ``core-contracts/src/provider.ts`` and issues requests ONLY against
|
|
14
|
+
the frozen v0.4.0 endpoint contract
|
|
15
|
+
(``fixtures/omnigent/discovery/http-surface.json``, vendored into the test tree at
|
|
16
|
+
``tests/fixtures/omnigent_contract/``). ``SESSION_ENDPOINTS`` / ``HARNESS_ENDPOINT``
|
|
17
|
+
below are this module's declared endpoint set;
|
|
18
|
+
``tests/test_advisor_board_backing_omnigent.py`` asserts every one appears in that
|
|
19
|
+
frozen contract and that the freeze target is ``0.4.0`` — so "faithful to v0.4.0"
|
|
20
|
+
is a checked invariant, not a claim.
|
|
21
|
+
|
|
22
|
+
**Auth = the same frozen no-silent-key contract as homebrew.** ``run_seat`` resolves
|
|
23
|
+
the seat's env through ``backing.resolve_seat_env`` (subscription scrubs EVERY
|
|
24
|
+
vendor key; an api-key seat, only behind the board opt-in, keeps ONLY the seat
|
|
25
|
+
vendor's key), then transmits vendor-key material to the gateway *derived from that
|
|
26
|
+
resolved env*. A subscription seat therefore puts zero vendor-key material on the
|
|
27
|
+
wire; an api-key opt-in seat transmits exactly the seat vendor's key. The gateway
|
|
28
|
+
**reports which auth lane it resolved** (``session.metadata.auth_lane``); ``run_seat``
|
|
29
|
+
reads it back, so no-silent-key is *observable* for omnigent seats — testable on the
|
|
30
|
+
recorded request, not merely asserted.
|
|
31
|
+
|
|
32
|
+
**Two distinct fail-closed skips** (they satisfy two different exit criteria):
|
|
33
|
+
|
|
34
|
+
* gateway unreachable -> ``select_backing`` degrades the seat skip-with-warning
|
|
35
|
+
(``gateway_available=False``); ``OmnigentBacking.gateway_available()`` is the real
|
|
36
|
+
probe the seam feeds it.
|
|
37
|
+
* harness absent from the live ``GET /v1/harnesses`` catalog -> a SEPARATE per-seat
|
|
38
|
+
gate (``catalog_harnesses``). This is the dynamic cursor/amp gate: they route only
|
|
39
|
+
when the live catalog exposes them, with no code gate.
|
|
40
|
+
"""
|
|
41
|
+
from __future__ import annotations
|
|
42
|
+
|
|
43
|
+
import json
|
|
44
|
+
import urllib.error
|
|
45
|
+
import urllib.parse
|
|
46
|
+
import urllib.request
|
|
47
|
+
import uuid
|
|
48
|
+
from collections.abc import Mapping
|
|
49
|
+
from dataclasses import dataclass, field
|
|
50
|
+
from typing import Any, Callable
|
|
51
|
+
|
|
52
|
+
from .backing import VENDOR_API_KEY_VARS, resolve_seat_env
|
|
53
|
+
from .schema import Seat, seat_vendor_family
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# --- frozen v0.4.0 endpoint contract (checked against http-surface.json) ------
|
|
57
|
+
|
|
58
|
+
OMNIGENT_FREEZE_TARGET = "0.4.0"
|
|
59
|
+
|
|
60
|
+
# (method, path-template) set this client issues. Every entry MUST appear in the
|
|
61
|
+
# frozen ``http-surface.json`` (asserted by the conformance test). We use only the
|
|
62
|
+
# subset a single-turn advisory board leg needs.
|
|
63
|
+
HARNESS_ENDPOINT: tuple[str, str] = ("GET", "/v1/harnesses")
|
|
64
|
+
SESSION_ENDPOINTS: tuple[tuple[str, str], ...] = (
|
|
65
|
+
("POST", "/v1/sessions"),
|
|
66
|
+
("GET", "/v1/sessions/{session_id}"),
|
|
67
|
+
("GET", "/v1/sessions/{session_id}/items"),
|
|
68
|
+
("POST", "/v1/sessions/{session_id}/events"),
|
|
69
|
+
("DELETE", "/v1/sessions/{session_id}"),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# The per-seat-vendor key header prefix. A subscription seat sends NONE of these;
|
|
73
|
+
# an api-key opt-in seat sends exactly the seat vendor's key var(s). The gateway
|
|
74
|
+
# derives ``auth_lane`` from their presence (never from a client-asserted claim).
|
|
75
|
+
VENDOR_KEY_HEADER_PREFIX = "x-omnigent-key-"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# --- errors -----------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class OmnigentHttpError(RuntimeError):
|
|
82
|
+
"""A non-2xx response from the Omnigent gateway. Mirrors
|
|
83
|
+
``http-client.ts``'s ``OmnigentHttpError`` (method/path/status/body)."""
|
|
84
|
+
|
|
85
|
+
def __init__(self, *, method: str, path: str, status_code: int, body: Any) -> None:
|
|
86
|
+
super().__init__(f"{method} {path} failed with {status_code}")
|
|
87
|
+
self.method = method
|
|
88
|
+
self.path = path
|
|
89
|
+
self.status_code = status_code
|
|
90
|
+
self.body = body
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class OmnigentGatewayUnavailable(RuntimeError):
|
|
94
|
+
"""The gateway could not be reached at all (connection refused / DNS / reset).
|
|
95
|
+
|
|
96
|
+
Distinct from ``OmnigentHttpError`` (a reached-but-refusing gateway): this is
|
|
97
|
+
the gateway-DOWN condition that degrades an omnigent seat skip-with-warning.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# --- HTTP client (faithful, synchronous port of http-client.ts) --------------
|
|
102
|
+
|
|
103
|
+
# Injection seam for tests: a callable mirroring ``urlopen`` semantics enough for
|
|
104
|
+
# the client. Default is the stdlib opener.
|
|
105
|
+
Opener = Callable[[urllib.request.Request, float | None], Any]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class OmnigentHttpClient:
|
|
110
|
+
"""Synchronous Omnigent v0.4.0 HTTP client (stdlib only, no new deps).
|
|
111
|
+
|
|
112
|
+
Mirrors the ``core-contracts`` request/response shapes used by a single-turn
|
|
113
|
+
board leg. ``base_url`` is the gateway; ``headers`` carries the gateway-bearer
|
|
114
|
+
(the transport auth, NOT a vendor key). ``opener`` is injectable so tests can
|
|
115
|
+
exercise the mapping without a socket, but the shipped tests run a real
|
|
116
|
+
in-process fake server so the wire path is genuinely exercised.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
base_url: str
|
|
120
|
+
headers: Mapping[str, str] = field(default_factory=dict)
|
|
121
|
+
timeout: float | None = 30.0
|
|
122
|
+
opener: Opener | None = None
|
|
123
|
+
|
|
124
|
+
def _url(self, path: str) -> str:
|
|
125
|
+
return self.base_url.rstrip("/") + path
|
|
126
|
+
|
|
127
|
+
def _request(
|
|
128
|
+
self,
|
|
129
|
+
method: str,
|
|
130
|
+
path: str,
|
|
131
|
+
*,
|
|
132
|
+
body: Any = None,
|
|
133
|
+
extra_headers: Mapping[str, str] | None = None,
|
|
134
|
+
) -> Any:
|
|
135
|
+
url = self._url(path)
|
|
136
|
+
data = None
|
|
137
|
+
headers = {**self.headers, **(extra_headers or {})}
|
|
138
|
+
if body is not None:
|
|
139
|
+
data = json.dumps(body).encode("utf-8")
|
|
140
|
+
headers["content-type"] = "application/json"
|
|
141
|
+
request = urllib.request.Request(url, data=data, method=method, headers=headers)
|
|
142
|
+
opener = self.opener or (lambda req, timeout: urllib.request.urlopen(req, timeout=timeout))
|
|
143
|
+
try:
|
|
144
|
+
response = opener(request, self.timeout)
|
|
145
|
+
except urllib.error.HTTPError as exc: # reached the gateway; it refused
|
|
146
|
+
raw = exc.read()
|
|
147
|
+
try:
|
|
148
|
+
parsed = json.loads(raw.decode("utf-8")) if raw else None
|
|
149
|
+
except (ValueError, UnicodeDecodeError):
|
|
150
|
+
parsed = raw.decode("utf-8", "replace") if raw else None
|
|
151
|
+
raise OmnigentHttpError(
|
|
152
|
+
method=method, path=path, status_code=exc.code, body=parsed
|
|
153
|
+
) from exc
|
|
154
|
+
except (urllib.error.URLError, ConnectionError, OSError) as exc: # gateway down
|
|
155
|
+
raise OmnigentGatewayUnavailable(f"{method} {path}: {exc}") from exc
|
|
156
|
+
with response:
|
|
157
|
+
status = getattr(response, "status", 200)
|
|
158
|
+
payload = response.read()
|
|
159
|
+
if status == 204 or not payload:
|
|
160
|
+
return None
|
|
161
|
+
return json.loads(payload.decode("utf-8"))
|
|
162
|
+
|
|
163
|
+
# -- endpoint methods (each anchored to an SESSION_ENDPOINTS/HARNESS_ENDPOINT) --
|
|
164
|
+
|
|
165
|
+
def list_harnesses(self) -> Mapping[str, Any]:
|
|
166
|
+
return self._request("GET", "/v1/harnesses") or {}
|
|
167
|
+
|
|
168
|
+
def create_session(
|
|
169
|
+
self,
|
|
170
|
+
*,
|
|
171
|
+
target_harness: str,
|
|
172
|
+
idempotency_key: str,
|
|
173
|
+
title: str,
|
|
174
|
+
initial_message: str | None = None,
|
|
175
|
+
vendor_key_headers: Mapping[str, str] | None = None,
|
|
176
|
+
) -> Mapping[str, Any]:
|
|
177
|
+
body = {
|
|
178
|
+
"idempotencyKey": idempotency_key,
|
|
179
|
+
"targetHarness": target_harness,
|
|
180
|
+
"targetProvider": "omnigent",
|
|
181
|
+
"title": title,
|
|
182
|
+
}
|
|
183
|
+
if initial_message is not None:
|
|
184
|
+
body["initialMessage"] = initial_message
|
|
185
|
+
return self._request("POST", "/v1/sessions", body=body, extra_headers=vendor_key_headers)
|
|
186
|
+
|
|
187
|
+
def send_turn(self, session_id: str, message: str) -> Mapping[str, Any]:
|
|
188
|
+
path = f"/v1/sessions/{_quote(session_id)}/events"
|
|
189
|
+
return self._request("POST", path, body={"type": "message", "data": {"message": message}})
|
|
190
|
+
|
|
191
|
+
def interrupt(self, session_id: str, reason: str = "user_request") -> Mapping[str, Any]:
|
|
192
|
+
path = f"/v1/sessions/{_quote(session_id)}/events"
|
|
193
|
+
return self._request("POST", path, body={"type": "interrupt", "data": {"reason": reason}})
|
|
194
|
+
|
|
195
|
+
def get_session(self, session_id: str) -> Mapping[str, Any]:
|
|
196
|
+
return self._request("GET", f"/v1/sessions/{_quote(session_id)}")
|
|
197
|
+
|
|
198
|
+
def get_history(self, session_id: str) -> list[Any]:
|
|
199
|
+
return self._request("GET", f"/v1/sessions/{_quote(session_id)}/items") or []
|
|
200
|
+
|
|
201
|
+
def delete_session(self, session_id: str) -> None:
|
|
202
|
+
self._request("DELETE", f"/v1/sessions/{_quote(session_id)}")
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _quote(value: str) -> str:
|
|
206
|
+
return urllib.parse.quote(str(value), safe="")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# --- outcome + status mapping ------------------------------------------------
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass(frozen=True)
|
|
213
|
+
class SeatRunOutcome:
|
|
214
|
+
"""Result of running one seat through Omnigent, in ``PanelLegResult`` terms.
|
|
215
|
+
|
|
216
|
+
``auth_lane`` is the lane the GATEWAY reported it resolved (read back from the
|
|
217
|
+
session snapshot), so no-silent-key is testable for omnigent seats.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
status: str # a panel LEG_STATUS: OK | EMPTY | ERROR | DEGRADED | UNAVAILABLE
|
|
221
|
+
text: str = ""
|
|
222
|
+
detail: str = ""
|
|
223
|
+
auth_lane: str | None = None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# response-stream event families (types.ts ``omnigentStreamEventTypes``).
|
|
227
|
+
_TEXT_DELTA = "response.output_text.delta"
|
|
228
|
+
_COMPLETED = {"response.completed", "turn.completed"}
|
|
229
|
+
_FAILED = {"response.failed", "turn.failed"}
|
|
230
|
+
_CANCELLED = {"response.cancelled", "turn.cancelled"}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _extract_text_and_status(events: "tuple[Any, ...]") -> tuple[str, str]:
|
|
234
|
+
"""Fold a seam ``SessionHistory.events`` stream into ``(text, leg_status)``.
|
|
235
|
+
|
|
236
|
+
Operates on the ``AgentRuntimeProvider`` history (``RuntimeEvent`` with ``.type``
|
|
237
|
+
+ ``.payload``), so the mapping runs on what the SEAM returns — not raw HTTP.
|
|
238
|
+
Text is the concatenation of ``response.output_text.delta`` deltas plus any
|
|
239
|
+
``response.completed`` ``outputText`` (mirrors event-mapper.ts's text handling).
|
|
240
|
+
Status keys on the terminal marker: completed -> OK/EMPTY, failed -> ERROR,
|
|
241
|
+
cancelled -> DEGRADED, none -> ERROR (no terminal event, treated as a broken turn).
|
|
242
|
+
"""
|
|
243
|
+
parts: list[str] = []
|
|
244
|
+
completed = failed = cancelled = False
|
|
245
|
+
for event in events:
|
|
246
|
+
etype = event.type
|
|
247
|
+
payload = event.payload
|
|
248
|
+
if etype == _TEXT_DELTA and payload.get("delta"):
|
|
249
|
+
parts.append(str(payload["delta"]))
|
|
250
|
+
elif etype == "response.completed" and payload.get("outputText"):
|
|
251
|
+
parts.append(str(payload["outputText"]))
|
|
252
|
+
if etype in _COMPLETED:
|
|
253
|
+
completed = True
|
|
254
|
+
elif etype in _FAILED:
|
|
255
|
+
failed = True
|
|
256
|
+
elif etype in _CANCELLED:
|
|
257
|
+
cancelled = True
|
|
258
|
+
text = _dedupe_join(parts)
|
|
259
|
+
if failed:
|
|
260
|
+
return text, "ERROR"
|
|
261
|
+
if cancelled:
|
|
262
|
+
return text, "DEGRADED"
|
|
263
|
+
if completed:
|
|
264
|
+
return text, ("OK" if text.strip() else "EMPTY")
|
|
265
|
+
return text, "ERROR"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _dedupe_join(parts: list[str]) -> str:
|
|
269
|
+
"""Join text fragments, collapsing an exact delta/outputText duplicate (the
|
|
270
|
+
fake emits the same ``Echo: …`` as both a delta and the completed outputText)."""
|
|
271
|
+
out: list[str] = []
|
|
272
|
+
for part in parts:
|
|
273
|
+
if not out or out[-1] != part:
|
|
274
|
+
out.append(part)
|
|
275
|
+
return "\n".join(out)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def classify_http_failure(status_code: int, body_text: str) -> tuple[str, str]:
|
|
279
|
+
"""Map a gateway HTTP rejection to ``(leg_status, category)``.
|
|
280
|
+
|
|
281
|
+
Mirrors omniagent-plus ``failure-mapper.ts``: 429 -> rate_limit, 403 with
|
|
282
|
+
auth/billing in the body -> auth/billing, other 403 -> policy_denied, else
|
|
283
|
+
backend_unavailable. Every reached-but-refused rejection degrades the SEAT
|
|
284
|
+
(DEGRADED) — it never blocks the board and never silently downgrades auth.
|
|
285
|
+
"""
|
|
286
|
+
text = (body_text or "").lower()
|
|
287
|
+
if status_code == 429:
|
|
288
|
+
return "DEGRADED", "rate_limit"
|
|
289
|
+
if status_code == 403 and ("billing" in text or "quota" in text):
|
|
290
|
+
return "DEGRADED", "billing"
|
|
291
|
+
if status_code == 403 and "auth" in text:
|
|
292
|
+
return "DEGRADED", "auth"
|
|
293
|
+
if status_code == 403:
|
|
294
|
+
return "DEGRADED", "policy_denied"
|
|
295
|
+
return "DEGRADED", "backend_unavailable"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# --- the backing ------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@dataclass
|
|
302
|
+
class OmnigentBacking:
|
|
303
|
+
"""The ``omnigent`` backing the board seam (``invoke_board``) routes through.
|
|
304
|
+
|
|
305
|
+
Construct with a ready :class:`OmnigentHttpClient`, or via :meth:`from_env` /
|
|
306
|
+
:meth:`from_config` so a production caller (ABDVERIFY) can wire it without
|
|
307
|
+
reshaping this API. ``title`` is the session title prefix for board legs.
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
client: OmnigentHttpClient
|
|
311
|
+
title_prefix: str = "advisor-board"
|
|
312
|
+
|
|
313
|
+
# -- availability probes ---------------------------------------------------
|
|
314
|
+
|
|
315
|
+
def catalog_harnesses(self) -> frozenset[str]:
|
|
316
|
+
"""The set of harness names the LIVE ``GET /v1/harnesses`` catalog reports.
|
|
317
|
+
|
|
318
|
+
The v0.4.0 catalog is ``{category: [ {name, ...}, ... ]}`` (types.ts
|
|
319
|
+
``OmnigentHarnessCatalogResponse``); we flatten the ``name`` of every entry.
|
|
320
|
+
This is the dynamic gate: cursor/amp route only when they appear here.
|
|
321
|
+
"""
|
|
322
|
+
catalog = self.client.list_harnesses()
|
|
323
|
+
names: set[str] = set()
|
|
324
|
+
for entries in catalog.values():
|
|
325
|
+
if not isinstance(entries, (list, tuple)):
|
|
326
|
+
continue
|
|
327
|
+
for entry in entries:
|
|
328
|
+
if isinstance(entry, Mapping) and entry.get("name"):
|
|
329
|
+
names.add(str(entry["name"]).lower())
|
|
330
|
+
return frozenset(names)
|
|
331
|
+
|
|
332
|
+
def gateway_available(self) -> bool:
|
|
333
|
+
"""Real gateway-reachability probe the seam feeds ``select_backing``.
|
|
334
|
+
|
|
335
|
+
A reachable gateway (even one returning a catalog) is available; a
|
|
336
|
+
connection failure (gateway down) is not. A gateway that is up but returns
|
|
337
|
+
an HTTP error is still 'reachable' — availability is about the transport,
|
|
338
|
+
not the catalog contents (harness presence is the SEPARATE catalog gate)."""
|
|
339
|
+
try:
|
|
340
|
+
self.client.list_harnesses()
|
|
341
|
+
return True
|
|
342
|
+
except OmnigentGatewayUnavailable:
|
|
343
|
+
return False
|
|
344
|
+
except OmnigentHttpError:
|
|
345
|
+
return True
|
|
346
|
+
|
|
347
|
+
# -- run one seat ----------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
def run_seat(
|
|
350
|
+
self,
|
|
351
|
+
seat: Seat,
|
|
352
|
+
artifact: str,
|
|
353
|
+
*,
|
|
354
|
+
base_env: Mapping[str, str],
|
|
355
|
+
allow_api_key_fallback: bool = False,
|
|
356
|
+
) -> SeatRunOutcome:
|
|
357
|
+
"""Route one seat through Omnigent: create session -> send one turn ->
|
|
358
|
+
read history -> map to a leg result, enforcing no-silent-key on the wire.
|
|
359
|
+
|
|
360
|
+
Raises :class:`OmnigentGatewayUnavailable` if the gateway drops mid-run
|
|
361
|
+
(the caller degrades it skip-with-warning) and ``ValueError`` for a
|
|
362
|
+
never-silent-key violation (an api-key seat without the board opt-in — the
|
|
363
|
+
caller maps it to DEGRADED, exactly like the homebrew path).
|
|
364
|
+
|
|
365
|
+
Routes through the SHARED provider seam: it drives an
|
|
366
|
+
:class:`~phase_loop_runtime.agent_runtime_provider.OmnigentAgentRuntimeProvider`
|
|
367
|
+
(an ``AgentRuntimeProvider``) via ``create_session`` -> ``send_turn`` ->
|
|
368
|
+
``get_session_info`` -> ``read_history`` -> ``close_session`` — exactly as
|
|
369
|
+
the homebrew path drives ``HomebrewAgentRuntimeProvider``.
|
|
370
|
+
"""
|
|
371
|
+
# Import here to avoid a module-load cycle (agent_runtime_provider TYPE-imports
|
|
372
|
+
# this module's client for annotations only).
|
|
373
|
+
from ..agent_runtime_provider import (
|
|
374
|
+
OMNIGENT_VENDOR_KEY_HEADERS_META,
|
|
375
|
+
RUNTIME_OMNIGENT,
|
|
376
|
+
CreateSessionRequest,
|
|
377
|
+
OmnigentAgentRuntimeProvider,
|
|
378
|
+
SendTurnRequest,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# 1) Frozen no-silent-key contract: the SAME env-scrub that governs the
|
|
382
|
+
# homebrew subprocess env governs what rides to the gateway. A
|
|
383
|
+
# subscription seat ends with zero vendor keys; an api-key opt-in seat
|
|
384
|
+
# keeps ONLY the seat vendor's key. (ValueError propagates: never-silent.)
|
|
385
|
+
seat_env = resolve_seat_env(seat, base_env, allow_api_key_fallback=allow_api_key_fallback)
|
|
386
|
+
vendor = seat_vendor_family(seat)
|
|
387
|
+
vendor_key_headers = {
|
|
388
|
+
f"{VENDOR_KEY_HEADER_PREFIX}{var}": seat_env[var]
|
|
389
|
+
for var in VENDOR_API_KEY_VARS.get(vendor, ())
|
|
390
|
+
if var in seat_env
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
provider = OmnigentAgentRuntimeProvider(self.client)
|
|
394
|
+
title = f"{self.title_prefix}:{seat.seat_key}"
|
|
395
|
+
metadata: dict[str, Any] = (
|
|
396
|
+
{OMNIGENT_VENDOR_KEY_HEADERS_META: vendor_key_headers} if vendor_key_headers else {}
|
|
397
|
+
)
|
|
398
|
+
try:
|
|
399
|
+
info = provider.create_session(
|
|
400
|
+
CreateSessionRequest(
|
|
401
|
+
target_harness=seat.harness or "",
|
|
402
|
+
idempotency_key=uuid.uuid4().hex,
|
|
403
|
+
title=title,
|
|
404
|
+
runtime=RUNTIME_OMNIGENT,
|
|
405
|
+
metadata=metadata,
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
provider.send_turn(
|
|
409
|
+
SendTurnRequest(
|
|
410
|
+
session_id=info.id, idempotency_key=uuid.uuid4().hex, message=artifact
|
|
411
|
+
)
|
|
412
|
+
)
|
|
413
|
+
# Re-read the snapshot through the seam: the gateway reports the resolved
|
|
414
|
+
# auth lane in session metadata (so no-silent-key is observable).
|
|
415
|
+
info = provider.get_session_info(info.id)
|
|
416
|
+
history = provider.read_history(info.id)
|
|
417
|
+
except OmnigentHttpError as exc:
|
|
418
|
+
status, category = classify_http_failure(exc.status_code, _body_text(exc.body))
|
|
419
|
+
return SeatRunOutcome(status=status, detail=f"omnigent {category}: HTTP {exc.status_code}")
|
|
420
|
+
|
|
421
|
+
auth_lane = _reported_auth_lane(info.metadata)
|
|
422
|
+
text, status = _extract_text_and_status(history.events)
|
|
423
|
+
detail = f"omnigent v{OMNIGENT_FREEZE_TARGET} lane={auth_lane}" if auth_lane else ""
|
|
424
|
+
# Best-effort close through the seam; never let cleanup change the verdict.
|
|
425
|
+
try:
|
|
426
|
+
provider.close_session(info.id)
|
|
427
|
+
except (OmnigentHttpError, OmnigentGatewayUnavailable):
|
|
428
|
+
pass
|
|
429
|
+
return SeatRunOutcome(status=status, text=text, detail=detail, auth_lane=auth_lane)
|
|
430
|
+
|
|
431
|
+
# -- factories -------------------------------------------------------------
|
|
432
|
+
|
|
433
|
+
@classmethod
|
|
434
|
+
def from_env(
|
|
435
|
+
cls,
|
|
436
|
+
env: Mapping[str, str],
|
|
437
|
+
*,
|
|
438
|
+
opener: Opener | None = None,
|
|
439
|
+
title_prefix: str = "advisor-board",
|
|
440
|
+
) -> "OmnigentBacking | None":
|
|
441
|
+
"""Build from ``OMNIGENT_GATEWAY_URL`` (+ optional ``OMNIGENT_GATEWAY_TOKEN``
|
|
442
|
+
gateway-bearer). Returns ``None`` when no gateway URL is configured, so the
|
|
443
|
+
seam degrades omnigent seats skip-with-warning rather than erroring."""
|
|
444
|
+
base_url = env.get("OMNIGENT_GATEWAY_URL")
|
|
445
|
+
if not base_url:
|
|
446
|
+
return None
|
|
447
|
+
headers: dict[str, str] = {}
|
|
448
|
+
token = env.get("OMNIGENT_GATEWAY_TOKEN")
|
|
449
|
+
if token:
|
|
450
|
+
headers["authorization"] = f"Bearer {token}"
|
|
451
|
+
client = OmnigentHttpClient(base_url=base_url, headers=headers, opener=opener)
|
|
452
|
+
return cls(client=client, title_prefix=title_prefix)
|
|
453
|
+
|
|
454
|
+
@classmethod
|
|
455
|
+
def from_config(
|
|
456
|
+
cls,
|
|
457
|
+
*,
|
|
458
|
+
base_url: str,
|
|
459
|
+
gateway_token: str | None = None,
|
|
460
|
+
opener: Opener | None = None,
|
|
461
|
+
title_prefix: str = "advisor-board",
|
|
462
|
+
) -> "OmnigentBacking":
|
|
463
|
+
headers = {"authorization": f"Bearer {gateway_token}"} if gateway_token else {}
|
|
464
|
+
client = OmnigentHttpClient(base_url=base_url, headers=headers, opener=opener)
|
|
465
|
+
return cls(client=client, title_prefix=title_prefix)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _body_text(body: Any) -> str:
|
|
469
|
+
if isinstance(body, str):
|
|
470
|
+
return body
|
|
471
|
+
if body is None:
|
|
472
|
+
return ""
|
|
473
|
+
try:
|
|
474
|
+
return json.dumps(body)
|
|
475
|
+
except (TypeError, ValueError):
|
|
476
|
+
return str(body)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _reported_auth_lane(metadata: Mapping[str, Any] | None) -> str | None:
|
|
480
|
+
"""The auth lane the gateway reports it resolved (``metadata.auth_lane``)."""
|
|
481
|
+
if isinstance(metadata, Mapping):
|
|
482
|
+
lane = metadata.get("auth_lane")
|
|
483
|
+
return str(lane) if lane is not None else None
|
|
484
|
+
return None
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
__all__ = [
|
|
488
|
+
"OMNIGENT_FREEZE_TARGET",
|
|
489
|
+
"HARNESS_ENDPOINT",
|
|
490
|
+
"SESSION_ENDPOINTS",
|
|
491
|
+
"VENDOR_KEY_HEADER_PREFIX",
|
|
492
|
+
"OmnigentHttpError",
|
|
493
|
+
"OmnigentGatewayUnavailable",
|
|
494
|
+
"OmnigentHttpClient",
|
|
495
|
+
"SeatRunOutcome",
|
|
496
|
+
"OmnigentBacking",
|
|
497
|
+
"classify_http_failure",
|
|
498
|
+
]
|