devrites 3.2.28 → 4.0.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/CHANGELOG.md +16 -144
- package/NOTICE.md +6 -5
- package/README.md +187 -95
- package/SECURITY.md +103 -79
- package/bin/devrites.mjs +140 -24
- package/docs/adr/0003-gate-model-hitl-pause.md +1 -1
- package/docs/adr/0013-native-codex-agent-orchestration.md +74 -0
- package/docs/adr/0014-native-host-hook-boundary.md +64 -0
- package/docs/adr/0015-read-only-root-native-orchestration.md +96 -0
- package/docs/adr/0016-codex-writer-fail-closed.md +53 -0
- package/docs/adr/0017-native-codex-writer-agent.md +63 -0
- package/docs/adr/0018-native-sandbox-instruction-writer-boundary.md +66 -0
- package/docs/adr/0019-native-boundary-with-deterministic-gates.md +48 -0
- package/docs/adr/0020-thin-engine-native-orchestration-boundary.md +54 -0
- package/docs/adr/0021-observable-workspace-compatibility.md +49 -0
- package/docs/adr/0022-native-orchestration-thin-engine.md +90 -0
- package/docs/adr/0023-native-workspace-reads-line-output.md +71 -0
- package/docs/adr/0024-native-policy-offline-installer-boundary.md +111 -0
- package/docs/adr/0025-evidence-gated-workspace-upgrades.md +64 -0
- package/docs/adr/0026-content-bound-proof-and-bounded-inputs.md +169 -0
- package/docs/adr/0027-content-bound-build-readiness.md +77 -0
- package/docs/adr/README.md +26 -11
- package/docs/agents/domain.md +2 -1
- package/docs/agents/issue-driven-rites.md +1 -1
- package/docs/architecture.md +191 -103
- package/docs/candidate-integrity.md +138 -0
- package/docs/capability-surface-selection.md +15 -5
- package/docs/cli.md +139 -92
- package/docs/command-map.md +88 -63
- package/docs/engine/commands.md +115 -507
- package/docs/engine/state-schema.md +94 -113
- package/docs/engine/workspace-schema.md +169 -229
- package/docs/flow.md +113 -68
- package/docs/harness-compliance.md +29 -56
- package/docs/markdown-instruction-upgrade-2026-08-02.md +302 -0
- package/docs/orchestration.md +136 -149
- package/docs/porting-to-a-new-harness.md +22 -7
- package/docs/quick-reference.md +21 -8
- package/docs/release.md +47 -4
- package/docs/skills.md +111 -72
- package/docs/templates/CRITIC_REVIEW_PACKET_TEMPLATE.md +2 -0
- package/docs/upstream-workflow-benchmark-2026-08-01.md +655 -0
- package/docs/usage.md +99 -65
- package/engine/Makefile +1 -2
- package/engine/cmd/releasepack/main.go +45 -25
- package/engine/cmd/releasepack/main_test.go +69 -0
- package/engine/commands.go +8 -258
- package/engine/harnessmatrix_test.go +28 -41
- package/engine/internal/devritespaths/paths.go +12 -28
- package/engine/internal/fsutil/copy.go +8 -0
- package/engine/internal/gate/gate.go +104 -105
- package/engine/internal/gate/gate_test.go +94 -84
- package/engine/internal/gate/readiness_binding.go +203 -0
- package/engine/internal/gate/readiness_binding_test.go +271 -0
- package/engine/internal/gitenv/env.go +46 -0
- package/engine/internal/gitenv/env_test.go +48 -0
- package/engine/internal/hostpack/hostpack.go +16 -20
- package/engine/internal/hostpack/hostpack_test.go +6 -6
- package/engine/internal/install/install.go +289 -511
- package/engine/internal/install/install_test.go +218 -306
- package/engine/internal/lib/candidate.go +475 -0
- package/engine/internal/lib/candidate_test.go +503 -0
- package/engine/internal/lib/closeout.go +17 -21
- package/engine/internal/lib/closeout_test.go +23 -0
- package/engine/internal/lib/cursor_compat_test.go +12 -75
- package/engine/internal/lib/evidencefresh.go +58 -47
- package/engine/internal/lib/evidencefresh_test.go +120 -0
- package/engine/internal/lib/resolve.go +13 -35
- package/engine/internal/lib/resolve_remediation_test.go +5 -17
- package/engine/internal/lib/secretscan.go +432 -0
- package/engine/internal/lib/secretscan_test.go +555 -0
- package/engine/internal/lib/util.go +24 -65
- package/engine/internal/lib/util_test.go +44 -0
- package/engine/internal/markdowntext/markdowntext.go +108 -0
- package/engine/internal/markdowntext/markdowntext_test.go +58 -0
- package/engine/internal/markdowntext/testdata/structural.json +27 -0
- package/engine/internal/reason/reason.go +2 -123
- package/engine/internal/reason/reason_test.go +4 -2
- package/engine/internal/rootfacts/facts.go +6 -8
- package/engine/internal/rootfacts/facts_test.go +20 -0
- package/engine/internal/state/cmd/workflowmanifest/main.go +10 -12
- package/engine/internal/state/cursor.go +53 -14
- package/engine/internal/state/cursor_test.go +57 -0
- package/engine/internal/state/feature.go +64 -154
- package/engine/internal/state/schema.go +24 -50
- package/engine/internal/state/state_test.go +151 -140
- package/engine/internal/state/status.go +4 -17
- package/engine/internal/state/workflow_manifest.json +9 -89
- package/engine/main.go +73 -502
- package/engine/root_routing_test.go +183 -212
- package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/feature.md → work/auth-tokens/README.md} +0 -1
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/status.md → work/auth-tokens/state.md} +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/{features/search-ranking/feature.md → work/search-ranking/README.md} +0 -1
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +2 -0
- package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +1 -1
- package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +1 -1
- package/engine/tests/adr_0003_gate_exit_code_test.go +2 -2
- package/engine/tests/adr_0011_define_plan_test.go +2 -2
- package/engine/tests/adr_0027_readiness_binding_test.go +106 -0
- package/engine/tests/cli_test.go +24 -107
- package/engine/tests/gate_test.go +177 -200
- package/engine/tests/meta_test.go +5 -35
- package/engine/tests/parity_closeout_test.go +12 -12
- package/engine/tests/parity_resolve_test.go +6 -16
- package/engine/tests/parity_test.go +8 -302
- package/install.sh +201 -34
- package/pack/.claude/agents/devrites-code-reviewer.md +9 -18
- package/pack/.claude/agents/devrites-devex-reviewer.md +10 -18
- package/pack/.claude/agents/devrites-doubt-reviewer.md +10 -20
- package/pack/.claude/agents/devrites-evidence-scout.md +14 -26
- package/pack/.claude/agents/devrites-frontend-reviewer.md +9 -15
- package/pack/.claude/agents/devrites-performance-reviewer.md +13 -21
- package/pack/.claude/agents/devrites-plan-drafter.md +24 -28
- package/pack/.claude/agents/devrites-plan-reviewer.md +30 -47
- package/pack/.claude/agents/devrites-proof-runner.md +41 -47
- package/pack/.claude/agents/devrites-retrospector.md +34 -79
- package/pack/.claude/agents/devrites-security-auditor.md +9 -17
- package/pack/.claude/agents/devrites-simplifier-reviewer.md +9 -18
- package/pack/.claude/agents/devrites-slice-wright.md +44 -62
- package/pack/.claude/agents/devrites-spec-reviewer.md +38 -50
- package/pack/.claude/agents/devrites-strategy-reviewer.md +10 -19
- package/pack/.claude/agents/devrites-test-analyst.md +22 -21
- package/pack/.claude/agents/devrites-upgrade-planner.md +52 -71
- package/pack/.claude/settings.json +12 -100
- package/pack/.claude/skills/devrites-api-interface/SKILL.md +1 -2
- package/pack/.claude/skills/devrites-audit/SKILL.md +6 -9
- package/pack/.claude/skills/devrites-browser-proof/SKILL.md +0 -1
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -27
- package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
- package/pack/.claude/skills/devrites-doubt/SKILL.md +4 -6
- package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +1 -2
- package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
- package/pack/.claude/skills/devrites-interview/SKILL.md +31 -36
- package/pack/.claude/skills/devrites-lib/SKILL.md +37 -66
- package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
- package/pack/.claude/skills/devrites-lib/reference/intent-map.md +20 -12
- package/pack/.claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
- package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +26 -93
- package/pack/.claude/skills/devrites-lib/reference/standards/README.md +6 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +90 -216
- package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
- package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
- package/pack/.claude/skills/devrites-lib/reference/standards/core.md +59 -54
- package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
- package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +29 -143
- package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
- package/pack/.claude/skills/devrites-lib/reference/standards/security.md +13 -31
- package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
- package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
- package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +22 -11
- package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
- package/pack/.claude/skills/devrites-prose-craft/SKILL.md +3 -4
- package/pack/.claude/skills/devrites-source-driven/SKILL.md +3 -4
- package/pack/.claude/skills/devrites-ux-shape/SKILL.md +3 -3
- package/pack/.claude/skills/rite/SKILL.md +12 -13
- package/pack/.claude/skills/rite/reference/menu.md +11 -11
- package/pack/.claude/skills/rite-adopt/SKILL.md +29 -84
- package/pack/.claude/skills/rite-adopt/reference/adoption.md +16 -50
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +65 -54
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +24 -17
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
- package/pack/.claude/skills/rite-build/SKILL.md +28 -34
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +36 -34
- package/pack/.claude/skills/rite-build/reference/anti-patterns.md +5 -19
- package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +17 -17
- package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +3 -3
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
- package/pack/.claude/skills/rite-build/reference/output.md +14 -24
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +49 -188
- package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +43 -133
- package/pack/.claude/skills/rite-clarify/SKILL.md +46 -40
- package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
- package/pack/.claude/skills/rite-converge/SKILL.md +9 -33
- package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
- package/pack/.claude/skills/rite-customize/SKILL.md +34 -50
- package/pack/.claude/skills/rite-define/SKILL.md +34 -58
- package/pack/.claude/skills/rite-define/reference/plan-template.md +15 -0
- package/pack/.claude/skills/rite-doctor/SKILL.md +52 -64
- package/pack/.claude/skills/rite-dogfood/SKILL.md +1 -2
- package/pack/.claude/skills/rite-explain/SKILL.md +1 -20
- package/pack/.claude/skills/rite-frame/SKILL.md +1 -7
- package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
- package/pack/.claude/skills/rite-handoff/SKILL.md +1 -2
- package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +1 -1
- package/pack/.claude/skills/rite-learn/SKILL.md +44 -104
- package/pack/.claude/skills/rite-plan/SKILL.md +14 -26
- package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -1
- package/pack/.claude/skills/rite-polish/SKILL.md +20 -28
- package/pack/.claude/skills/rite-polish/reference/adr-promotion.md +11 -0
- package/pack/{generated/claude/skills/rite-ship → .claude/skills/rite-polish}/reference/design-memory.md +23 -21
- package/pack/.claude/skills/rite-polish/reference/ledger.md +62 -0
- package/pack/.claude/skills/rite-pov/SKILL.md +28 -32
- package/pack/.claude/skills/rite-pr-feedback/SKILL.md +0 -1
- package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -13
- package/pack/.claude/skills/rite-prototype/SKILL.md +0 -6
- package/pack/.claude/skills/rite-prove/SKILL.md +73 -68
- package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
- package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
- package/pack/.claude/skills/rite-quick/SKILL.md +5 -19
- package/pack/.claude/skills/rite-resolve/SKILL.md +4 -21
- package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
- package/pack/.claude/skills/rite-review/SKILL.md +34 -57
- package/pack/.claude/skills/rite-seal/SKILL.md +54 -87
- package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +2 -2
- package/pack/.claude/skills/rite-seal/reference/final-evidence.md +31 -56
- package/pack/.claude/skills/rite-seal/reference/go-no-go.md +27 -51
- package/pack/.claude/skills/rite-seal/reference/output.md +2 -4
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +42 -140
- package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
- package/pack/.claude/skills/rite-seal/reference/seal-template.md +17 -28
- package/pack/.claude/skills/rite-ship/SKILL.md +65 -93
- package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +4 -1
- package/pack/.claude/skills/rite-ship/reference/close-out.md +4 -12
- package/pack/.claude/skills/rite-ship/reference/git-ship.md +86 -48
- package/pack/.claude/skills/rite-ship/reference/ship-template.md +22 -16
- package/pack/.claude/skills/rite-spec/SKILL.md +62 -95
- package/pack/.claude/skills/rite-spec/reference/investigation.md +1 -4
- package/pack/.claude/skills/rite-spec/reference/question-protocol.md +21 -20
- package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +16 -17
- package/pack/.claude/skills/rite-spec/reference/spec-template.md +47 -49
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +28 -13
- package/pack/.claude/skills/rite-status/SKILL.md +30 -88
- package/pack/.claude/skills/rite-temper/SKILL.md +12 -31
- package/pack/.claude/skills/rite-upgrade/SKILL.md +86 -101
- package/pack/.claude/skills/rite-vet/SKILL.md +88 -117
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +56 -50
- package/pack/.claude/skills/rite-vet/reference/cross-model.md +15 -39
- package/pack/.claude/skills/rite-vet/reference/depth.md +2 -2
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -6
- package/pack/.claude/skills/rite-zoom-out/SKILL.md +0 -6
- package/pack/generated/README.md +2 -2
- package/pack/generated/claude/agents/devrites-code-reviewer.md +9 -18
- package/pack/generated/claude/agents/devrites-devex-reviewer.md +10 -18
- package/pack/generated/claude/agents/devrites-doubt-reviewer.md +10 -20
- package/pack/generated/claude/agents/devrites-evidence-scout.md +14 -26
- package/pack/generated/claude/agents/devrites-frontend-reviewer.md +9 -15
- package/pack/generated/claude/agents/devrites-performance-reviewer.md +13 -21
- package/pack/generated/claude/agents/devrites-plan-drafter.md +24 -28
- package/pack/generated/claude/agents/devrites-plan-reviewer.md +30 -47
- package/pack/generated/claude/agents/devrites-proof-runner.md +41 -47
- package/pack/generated/claude/agents/devrites-retrospector.md +34 -79
- package/pack/generated/claude/agents/devrites-security-auditor.md +9 -17
- package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +9 -18
- package/pack/generated/claude/agents/devrites-slice-wright.md +44 -62
- package/pack/generated/claude/agents/devrites-spec-reviewer.md +38 -50
- package/pack/generated/claude/agents/devrites-strategy-reviewer.md +10 -19
- package/pack/generated/claude/agents/devrites-test-analyst.md +22 -21
- package/pack/generated/claude/agents/devrites-upgrade-planner.md +52 -71
- package/pack/generated/claude/settings.json +12 -100
- package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +1 -2
- package/pack/generated/claude/skills/devrites-audit/SKILL.md +6 -9
- package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +0 -1
- package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +21 -27
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
- package/pack/generated/claude/skills/devrites-doubt/SKILL.md +4 -6
- package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +1 -2
- package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
- package/pack/generated/claude/skills/devrites-interview/SKILL.md +31 -36
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +37 -66
- package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
- package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +20 -12
- package/pack/generated/claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
- package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +26 -93
- package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +6 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +90 -216
- package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
- package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
- package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +59 -54
- package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
- package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +29 -143
- package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
- package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +13 -31
- package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
- package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
- package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +22 -11
- package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
- package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +3 -4
- package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +3 -4
- package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +3 -3
- package/pack/generated/claude/skills/rite/SKILL.md +12 -13
- package/pack/generated/claude/skills/rite/reference/menu.md +11 -11
- package/pack/generated/claude/skills/rite-adopt/SKILL.md +29 -84
- package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +16 -50
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +65 -54
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +24 -17
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
- package/pack/generated/claude/skills/rite-build/SKILL.md +28 -34
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +36 -34
- package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +5 -19
- package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +17 -17
- package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +3 -3
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
- package/pack/generated/claude/skills/rite-build/reference/output.md +14 -24
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +49 -188
- package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +43 -133
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +46 -40
- package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
- package/pack/generated/claude/skills/rite-converge/SKILL.md +9 -33
- package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
- package/pack/generated/claude/skills/rite-customize/SKILL.md +34 -50
- package/pack/generated/claude/skills/rite-define/SKILL.md +34 -58
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +15 -0
- package/pack/generated/claude/skills/rite-doctor/SKILL.md +52 -64
- package/pack/generated/claude/skills/rite-dogfood/SKILL.md +1 -2
- package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -20
- package/pack/generated/claude/skills/rite-frame/SKILL.md +1 -7
- package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +1 -1
- package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -2
- package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +1 -1
- package/pack/generated/claude/skills/rite-learn/SKILL.md +44 -104
- package/pack/generated/claude/skills/rite-plan/SKILL.md +14 -26
- package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -1
- package/pack/generated/claude/skills/rite-polish/SKILL.md +20 -28
- package/pack/generated/claude/skills/rite-polish/reference/adr-promotion.md +11 -0
- package/pack/generated/{codex/skills/rite-ship → claude/skills/rite-polish}/reference/design-memory.md +23 -21
- package/pack/generated/claude/skills/rite-polish/reference/ledger.md +62 -0
- package/pack/generated/claude/skills/rite-pov/SKILL.md +28 -32
- package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +0 -1
- package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -13
- package/pack/generated/claude/skills/rite-prototype/SKILL.md +0 -6
- package/pack/generated/claude/skills/rite-prove/SKILL.md +73 -68
- package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
- package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
- package/pack/generated/claude/skills/rite-quick/SKILL.md +5 -19
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +4 -21
- package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
- package/pack/generated/claude/skills/rite-review/SKILL.md +34 -57
- package/pack/generated/claude/skills/rite-seal/SKILL.md +54 -87
- package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +2 -2
- package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +31 -56
- package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +27 -51
- package/pack/generated/claude/skills/rite-seal/reference/output.md +2 -4
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +42 -140
- package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
- package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +17 -28
- package/pack/generated/claude/skills/rite-ship/SKILL.md +65 -93
- package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +4 -1
- package/pack/generated/claude/skills/rite-ship/reference/close-out.md +4 -12
- package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +86 -48
- package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +22 -16
- package/pack/generated/claude/skills/rite-spec/SKILL.md +62 -95
- package/pack/generated/claude/skills/rite-spec/reference/investigation.md +1 -4
- package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +21 -20
- package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +16 -17
- package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +47 -49
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +28 -13
- package/pack/generated/claude/skills/rite-status/SKILL.md +30 -88
- package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -31
- package/pack/generated/claude/skills/rite-upgrade/SKILL.md +86 -101
- package/pack/generated/claude/skills/rite-vet/SKILL.md +88 -117
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +56 -50
- package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +15 -39
- package/pack/generated/claude/skills/rite-vet/reference/depth.md +2 -2
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +3 -6
- package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +0 -6
- package/pack/generated/codex/AGENTS.md +5 -4
- package/pack/generated/codex/agents/devrites-code-reviewer.toml +8 -27
- package/pack/generated/codex/agents/devrites-devex-reviewer.toml +9 -27
- package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +9 -29
- package/pack/generated/codex/agents/devrites-evidence-scout.toml +14 -36
- package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +8 -24
- package/pack/generated/codex/agents/devrites-performance-reviewer.toml +12 -30
- package/pack/generated/codex/agents/devrites-plan-drafter.toml +24 -38
- package/pack/generated/codex/agents/devrites-plan-reviewer.toml +30 -57
- package/pack/generated/codex/agents/devrites-proof-runner.toml +41 -57
- package/pack/generated/codex/agents/devrites-retrospector.toml +34 -89
- package/pack/generated/codex/agents/devrites-security-auditor.toml +8 -26
- package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +8 -27
- package/pack/generated/codex/agents/devrites-slice-wright.toml +44 -71
- package/pack/generated/codex/agents/devrites-spec-reviewer.toml +38 -60
- package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +9 -28
- package/pack/generated/codex/agents/devrites-test-analyst.toml +21 -30
- package/pack/generated/codex/agents/devrites-upgrade-planner.toml +52 -81
- package/pack/generated/codex/config.toml +7 -0
- package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +1 -21
- package/pack/generated/codex/skills/devrites-audit/SKILL.md +6 -28
- package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +1 -21
- package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +21 -46
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
- package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -25
- package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +1 -21
- package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +2 -1
- package/pack/generated/codex/skills/devrites-interview/SKILL.md +32 -56
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +38 -86
- package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +51 -0
- package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +20 -12
- package/pack/generated/codex/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
- package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +26 -93
- package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +6 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +90 -216
- package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +9 -28
- package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
- package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +59 -54
- package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -5
- package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +29 -143
- package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
- package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +13 -31
- package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
- package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
- package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +22 -11
- package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +15 -24
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
- package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +3 -23
- package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +3 -23
- package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +3 -22
- package/pack/generated/codex/skills/rite/SKILL.md +12 -32
- package/pack/generated/codex/skills/rite/reference/menu.md +11 -11
- package/pack/generated/codex/skills/rite-adopt/SKILL.md +29 -103
- package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +16 -50
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +65 -73
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +24 -17
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
- package/pack/generated/codex/skills/rite-build/SKILL.md +28 -53
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +36 -34
- package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +5 -19
- package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +10 -9
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +17 -17
- package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +3 -3
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +11 -14
- package/pack/generated/codex/skills/rite-build/reference/output.md +14 -24
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +49 -188
- package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +43 -133
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +46 -59
- package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +4 -10
- package/pack/generated/codex/skills/rite-converge/SKILL.md +9 -52
- package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +2 -2
- package/pack/generated/codex/skills/rite-customize/SKILL.md +32 -67
- package/pack/generated/codex/skills/rite-define/SKILL.md +34 -77
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +15 -0
- package/pack/generated/codex/skills/rite-doctor/SKILL.md +52 -83
- package/pack/generated/codex/skills/rite-dogfood/SKILL.md +1 -21
- package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -39
- package/pack/generated/codex/skills/rite-frame/SKILL.md +1 -26
- package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +1 -1
- package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -21
- package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +1 -1
- package/pack/generated/codex/skills/rite-learn/SKILL.md +44 -123
- package/pack/generated/codex/skills/rite-plan/SKILL.md +14 -45
- package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +4 -1
- package/pack/generated/codex/skills/rite-polish/SKILL.md +20 -47
- package/pack/generated/codex/skills/rite-polish/reference/adr-promotion.md +11 -0
- package/pack/{.claude/skills/rite-ship → generated/codex/skills/rite-polish}/reference/design-memory.md +23 -21
- package/pack/generated/codex/skills/rite-polish/reference/ledger.md +62 -0
- package/pack/generated/codex/skills/rite-pov/SKILL.md +28 -51
- package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +0 -20
- package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -32
- package/pack/generated/codex/skills/rite-prototype/SKILL.md +0 -25
- package/pack/generated/codex/skills/rite-prove/SKILL.md +73 -87
- package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +34 -30
- package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +15 -14
- package/pack/generated/codex/skills/rite-quick/SKILL.md +5 -38
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -40
- package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -2
- package/pack/generated/codex/skills/rite-review/SKILL.md +34 -76
- package/pack/generated/codex/skills/rite-seal/SKILL.md +51 -103
- package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +2 -2
- package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +31 -56
- package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +27 -51
- package/pack/generated/codex/skills/rite-seal/reference/output.md +2 -4
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +42 -140
- package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +2 -2
- package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +17 -28
- package/pack/generated/codex/skills/rite-ship/SKILL.md +65 -112
- package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +4 -1
- package/pack/generated/codex/skills/rite-ship/reference/close-out.md +4 -12
- package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +86 -48
- package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +22 -16
- package/pack/generated/codex/skills/rite-spec/SKILL.md +62 -114
- package/pack/generated/codex/skills/rite-spec/reference/investigation.md +1 -4
- package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +21 -20
- package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +16 -17
- package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +47 -49
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +28 -13
- package/pack/generated/codex/skills/rite-status/SKILL.md +30 -107
- package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -50
- package/pack/generated/codex/skills/rite-upgrade/SKILL.md +86 -120
- package/pack/generated/codex/skills/rite-vet/SKILL.md +88 -136
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +56 -50
- package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +15 -39
- package/pack/generated/codex/skills/rite-vet/reference/depth.md +2 -2
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +3 -6
- package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +0 -25
- package/package.json +6 -5
- package/scripts/build-host-artifacts.sh +5 -9
- package/scripts/codex-generate.sh +18 -278
- package/scripts/install-lib.sh +121 -34
- package/uninstall.sh +19 -17
- package/update.sh +26 -18
- package/docs/engine/agent-contract.md +0 -172
- package/docs/extensions.md +0 -178
- package/engine/git_guard.go +0 -187
- package/engine/git_guard_test.go +0 -283
- package/engine/harnessmatrix.go +0 -60
- package/engine/hookpolicy.go +0 -113
- package/engine/hookpolicy_test.go +0 -160
- package/engine/hooks.go +0 -677
- package/engine/hooks_agent_dispatch.go +0 -1726
- package/engine/hooks_events_test.go +0 -431
- package/engine/hooks_workspace.go +0 -1196
- package/engine/internal/doctor/doctor.go +0 -306
- package/engine/internal/doctor/doctor_test.go +0 -252
- package/engine/internal/forge/forge.go +0 -938
- package/engine/internal/forge/forge_test.go +0 -576
- package/engine/internal/forge/git.go +0 -245
- package/engine/internal/forge/liveness_unix.go +0 -48
- package/engine/internal/forge/liveness_windows.go +0 -67
- package/engine/internal/forge/manifest.go +0 -402
- package/engine/internal/harness/compliance.go +0 -96
- package/engine/internal/harness/harness.go +0 -301
- package/engine/internal/harness/harness_test.go +0 -124
- package/engine/internal/iohooks/detach_other.go +0 -11
- package/engine/internal/iohooks/detach_unix.go +0 -11
- package/engine/internal/iohooks/iohooks.go +0 -839
- package/engine/internal/iohooks/iohooks_test.go +0 -561
- package/engine/internal/lib/adoption.go +0 -659
- package/engine/internal/lib/adoption_test.go +0 -121
- package/engine/internal/lib/analyze.go +0 -232
- package/engine/internal/lib/archivesearch.go +0 -125
- package/engine/internal/lib/archivesearch_test.go +0 -138
- package/engine/internal/lib/budget.go +0 -172
- package/engine/internal/lib/buildreadiness.go +0 -111
- package/engine/internal/lib/checkacceptance.go +0 -176
- package/engine/internal/lib/clarifyreturn.go +0 -91
- package/engine/internal/lib/clarifyreturn_test.go +0 -123
- package/engine/internal/lib/command_other.go +0 -7
- package/engine/internal/lib/command_unix.go +0 -24
- package/engine/internal/lib/context.go +0 -280
- package/engine/internal/lib/conventions.go +0 -449
- package/engine/internal/lib/coverage.go +0 -145
- package/engine/internal/lib/dispatchwaive.go +0 -31
- package/engine/internal/lib/dispatchwaive_test.go +0 -23
- package/engine/internal/lib/doubtcoverage.go +0 -78
- package/engine/internal/lib/extensions.go +0 -622
- package/engine/internal/lib/extensions_test.go +0 -305
- package/engine/internal/lib/footprint.go +0 -239
- package/engine/internal/lib/gitauthority.go +0 -601
- package/engine/internal/lib/gitauthority_test.go +0 -346
- package/engine/internal/lib/jsonout.go +0 -129
- package/engine/internal/lib/jsonout_test.go +0 -73
- package/engine/internal/lib/lanes.go +0 -112
- package/engine/internal/lib/learnings.go +0 -397
- package/engine/internal/lib/learnings_inject_test.go +0 -77
- package/engine/internal/lib/learnings_remediation_test.go +0 -24
- package/engine/internal/lib/ledger.go +0 -311
- package/engine/internal/lib/ledger_test.go +0 -186
- package/engine/internal/lib/mutationgate.go +0 -89
- package/engine/internal/lib/observability_test.go +0 -358
- package/engine/internal/lib/overrides.go +0 -164
- package/engine/internal/lib/overrides_test.go +0 -112
- package/engine/internal/lib/preamble.go +0 -159
- package/engine/internal/lib/preamble_questions_test.go +0 -10
- package/engine/internal/lib/progress.go +0 -210
- package/engine/internal/lib/provenance.go +0 -462
- package/engine/internal/lib/provenance_test.go +0 -154
- package/engine/internal/lib/readiness_contract.json +0 -118
- package/engine/internal/lib/readinessartifact.go +0 -533
- package/engine/internal/lib/readinessartifact_test.go +0 -422
- package/engine/internal/lib/reconcile.go +0 -1496
- package/engine/internal/lib/reconcile_test.go +0 -1071
- package/engine/internal/lib/recoveryattempts.go +0 -298
- package/engine/internal/lib/recoveryattempts_test.go +0 -215
- package/engine/internal/lib/reviewers.go +0 -143
- package/engine/internal/lib/reviewers_test.go +0 -45
- package/engine/internal/lib/reviewerstats.go +0 -199
- package/engine/internal/lib/reviewerstats_test.go +0 -104
- package/engine/internal/lib/reviewintegrity.go +0 -143
- package/engine/internal/lib/reviewintegrity_test.go +0 -103
- package/engine/internal/lib/runbook.go +0 -295
- package/engine/internal/lib/runbook_context_test.go +0 -182
- package/engine/internal/lib/session.go +0 -1020
- package/engine/internal/lib/session_test.go +0 -177
- package/engine/internal/lib/specparse.go +0 -125
- package/engine/internal/lib/specskeleton.go +0 -79
- package/engine/internal/lib/specvalidate.go +0 -391
- package/engine/internal/lib/specvalidate_test.go +0 -185
- package/engine/internal/lib/stuck.go +0 -206
- package/engine/internal/lib/templates/conventions_drift.tmpl +0 -6
- package/engine/internal/lib/templates/conventions_ledger_header.md +0 -6
- package/engine/internal/lib/templates/conventions_orient_header.txt +0 -1
- package/engine/internal/lib/testintegrity.go +0 -199
- package/engine/internal/lib/testintegrity_test.go +0 -149
- package/engine/internal/lib/tickafk.go +0 -86
- package/engine/internal/migrate/migrate.go +0 -279
- package/engine/internal/migrate/migrate_test.go +0 -157
- package/engine/internal/orient/firstrun.go +0 -50
- package/engine/internal/orient/firstrun_test.go +0 -72
- package/engine/internal/orient/orient.go +0 -69
- package/engine/internal/orient/orient_test.go +0 -25
- package/engine/internal/profile/profile.go +0 -284
- package/engine/internal/profile/profile_test.go +0 -13
- package/engine/internal/state/clarify_transition.go +0 -165
- package/engine/internal/state/clarify_transition_test.go +0 -130
- package/engine/internal/state/snapshot.go +0 -457
- package/engine/internal/toolpolicy/classifier.go +0 -533
- package/engine/internal/toolpolicy/classifier_test.go +0 -424
- package/engine/internal/toolpolicy/git.go +0 -616
- package/engine/internal/toolpolicy/scanner.go +0 -382
- package/engine/observability_cli_test.go +0 -52
- package/engine/profile_test.go +0 -76
- package/engine/subagent_orient_context.txt +0 -8
- package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +0 -2
- package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +0 -1
- package/engine/testdata/golden/TestParityAnalyze/noactive.golden +0 -1
- package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +0 -15
- package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +0 -16
- package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +0 -1
- package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +0 -15
- package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +0 -17
- package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +0 -16
- package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +0 -16
- package/engine/testdata/golden/TestParityBudget/default-active.golden +0 -4
- package/engine/testdata/golden/TestParityBudget/extra-md.golden +0 -5
- package/engine/testdata/golden/TestParityBudget/no-active.golden +0 -1
- package/engine/testdata/golden/TestParityBudget/no-feature.golden +0 -1
- package/engine/testdata/golden/TestParityBudget/over-advisory.golden +0 -6
- package/engine/testdata/golden/TestParityBudget/over-strict.golden +0 -6
- package/engine/testdata/golden/TestParityBudget/within.golden +0 -6
- package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=clarifyopen.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +0 -2
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +0 -2
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance.golden +0 -1
- package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +0 -7
- package/engine/testdata/golden/TestParityCoverage/arg=full.golden +0 -10
- package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +0 -1
- package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +0 -1
- package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +0 -9
- package/engine/testdata/golden/TestParityCoverage.golden +0 -1
- package/engine/testdata/golden/TestParityCursor/bare.golden +0 -3
- package/engine/testdata/golden/TestParityCursor/full-afk.golden +0 -6
- package/engine/testdata/golden/TestParityCursor/full-hitl.golden +0 -5
- package/engine/testdata/golden/TestParityCursor/no-active.golden +0 -1
- package/engine/testdata/golden/TestParityCursor/red.golden +0 -5
- package/engine/testdata/golden/TestParityCursor/zerogates.golden +0 -5
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +0 -3
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +0 -4
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage.golden +0 -1
- package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +0 -3
- package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +0 -3
- package/engine/testdata/golden/TestParityMutationGate/ghost.golden +0 -1
- package/engine/testdata/golden/TestParityOrientSilentPath.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-clean.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-violation.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/close-window/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/close-window.golden +0 -3
- package/engine/testdata/golden/TestParityReconcile/not-git.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +0 -2
- package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +0 -1
- package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +0 -1
- package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +0 -1
- package/engine/testdata/golden/TestParityResolve/next-qid.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +0 -2
- package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +0 -2
- package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +0 -2
- package/engine/testdata/golden/TestParitySpecValidate.golden +0 -1
- package/engine/testdata/golden/TestParityStatusline/full-afk.golden +0 -2
- package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +0 -2
- package/engine/testdata/golden/TestParityStatusline/no-active.golden +0 -1
- package/engine/testdata/golden/TestParityStatusline/nophase.golden +0 -2
- package/engine/testdata/golden/TestParityStatusline/red.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +0 -1
- package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/few.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/identical.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +0 -1
- package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +0 -1
- package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/varied.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +0 -2
- package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +0 -2
- package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +0 -2
- package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +0 -1
- package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +0 -2
- package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/budget1.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +0 -5
- package/engine/testdata/golden/TestParityTickAfk/budget3.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +0 -3
- package/engine/testdata/golden/TestParityTickAfk/missingField.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/none.golden +0 -2
- package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +0 -1
- package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +0 -2
- package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +0 -1
- package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +0 -1
- package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +0 -2
- package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +0 -1
- package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +0 -2
- package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +0 -2
- package/engine/testdata/golden/TestStuckLogHashParity.golden +0 -1
- package/engine/tests/adr_0006_clock_seam_test.go +0 -36
- package/engine/tests/budget_test.go +0 -88
- package/engine/tests/canonical_ac_ids_test.go +0 -44
- package/engine/tests/concurrency_cli_test.go +0 -131
- package/engine/tests/conventions_test.go +0 -106
- package/engine/tests/doctor_cli_test.go +0 -223
- package/engine/tests/forge_cli_test.go +0 -463
- package/engine/tests/hook_test.go +0 -2778
- package/engine/tests/hooks_io_test.go +0 -309
- package/engine/tests/json_contract_test.go +0 -224
- package/engine/tests/lib_parity_test.go +0 -930
- package/engine/tests/migrate_cli_test.go +0 -195
- package/engine/tests/parity_analyze_test.go +0 -55
- package/engine/tests/parity_buildreadiness_test.go +0 -199
- package/engine/tests/parity_learnings_test.go +0 -89
- package/engine/tests/parity_mutationgate_test.go +0 -36
- package/engine/tests/parity_reconcile_test.go +0 -94
- package/engine/tests/parity_testintegrity_test.go +0 -65
- package/engine/tests/parity_tickafk_test.go +0 -83
- package/engine/validatepack.go +0 -192
- package/engine/validatepack_test.go +0 -79
- package/engine/validatepackskills.go +0 -181
- package/engine/validatepackskills_test.go +0 -171
- package/pack/.claude/agents/devrites-forge-judge.md +0 -118
- package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +0 -44
- package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
- package/pack/.claude/skills/rite-build/reference/evidence-standard.md +0 -45
- package/pack/.claude/skills/rite-build/reference/forge.md +0 -210
- package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
- package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +0 -9
- package/pack/.claude/skills/rite-ship/reference/ledger.md +0 -56
- package/pack/generated/claude/agents/devrites-forge-judge.md +0 -118
- package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +0 -44
- package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
- package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +0 -45
- package/pack/generated/claude/skills/rite-build/reference/forge.md +0 -210
- package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
- package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +0 -9
- package/pack/generated/claude/skills/rite-ship/reference/ledger.md +0 -56
- package/pack/generated/codex/agents/devrites-forge-judge.toml +0 -128
- package/pack/generated/codex/hooks.json +0 -185
- package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +0 -44
- package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +0 -71
- package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +0 -45
- package/pack/generated/codex/skills/rite-build/reference/forge.md +0 -210
- package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +0 -60
- package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +0 -9
- package/pack/generated/codex/skills/rite-ship/reference/ledger.md +0 -56
- package/scripts/build-binaries.sh +0 -75
- package/scripts/build-release-tarball.sh +0 -105
- package/scripts/check-authority-drift.py +0 -125
- package/scripts/check-cross-refs.py +0 -139
- package/scripts/check-generated-skill-budget.mjs +0 -42
- package/scripts/check-instruction-size-baseline.mjs +0 -88
- package/scripts/check-invocation-integrity.py +0 -115
- package/scripts/check-no-global-writes.sh +0 -66
- package/scripts/check-no-personal-paths.py +0 -50
- package/scripts/check-npm-audit.mjs +0 -110
- package/scripts/check-reference-governance.mjs +0 -83
- package/scripts/check-reply-contract.sh +0 -64
- package/scripts/check-rule-uniqueness.sh +0 -45
- package/scripts/devrites-detect.sh +0 -176
- package/scripts/grade-feature.sh +0 -204
- package/scripts/live-hosts/agent-result.schema.json +0 -231
- package/scripts/live-hosts/claude.sh +0 -87
- package/scripts/live-hosts/codex.sh +0 -85
- package/scripts/live-hosts/common.sh +0 -113
- package/scripts/live-hosts/fake-host.py +0 -257
- package/scripts/live-hosts/host-transport.py +0 -286
- package/scripts/npm-audit-exceptions.json +0 -26
- package/scripts/pin.sh +0 -249
- package/scripts/release-check.sh +0 -75
- package/scripts/run-agent-contract-evals.py +0 -1380
- package/scripts/run-behavioral-evals.sh +0 -196
- package/scripts/run-evals.sh +0 -132
- package/scripts/run-live-behavioral-evals.py +0 -1310
- package/scripts/run-outcome-evals.sh +0 -454
- package/scripts/run-routing-evals.py +0 -342
- package/scripts/run-tests.mjs +0 -232
- package/scripts/scan-pack-security.py +0 -209
- package/scripts/scan-supply-chain-iocs.py +0 -127
- package/scripts/skill-pruning-audit.mjs +0 -121
- package/scripts/skills-inventory.mjs +0 -125
- package/scripts/supply-chain-iocs.json +0 -11
- package/scripts/sync-version.sh +0 -54
- package/scripts/validate-agent-composition.py +0 -57
- package/scripts/validate-command-parity.py +0 -70
- package/scripts/validate-frontmatter.py +0 -218
- package/scripts/validate-skill-anatomy.py +0 -139
- package/scripts/validate-workflow-security.py +0 -184
- package/scripts/validate-workspace-schema.py +0 -800
- package/scripts/validate.sh +0 -354
- package/scripts/workflow_schema.py +0 -69
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/decisions.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/plan.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/spec.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/tasks.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/search-ranking/spec.md +0 -0
|
@@ -1,1726 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"bufio"
|
|
5
|
-
"bytes"
|
|
6
|
-
"crypto/sha256"
|
|
7
|
-
"encoding/hex"
|
|
8
|
-
"encoding/json"
|
|
9
|
-
"fmt"
|
|
10
|
-
"io"
|
|
11
|
-
"io/fs"
|
|
12
|
-
"os"
|
|
13
|
-
"path/filepath"
|
|
14
|
-
"regexp"
|
|
15
|
-
"sort"
|
|
16
|
-
"strconv"
|
|
17
|
-
"strings"
|
|
18
|
-
"time"
|
|
19
|
-
|
|
20
|
-
"github.com/devrites/devrites/internal/harness"
|
|
21
|
-
"github.com/devrites/devrites/internal/lib"
|
|
22
|
-
"github.com/devrites/devrites/internal/orient"
|
|
23
|
-
"github.com/devrites/devrites/internal/safepath"
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
const (
|
|
27
|
-
agentDispatchStateVersion = "devrites-agent-dispatch/v1"
|
|
28
|
-
agentDispatchStateDir = "devrites-agent-dispatch-v1"
|
|
29
|
-
agentDispatchMetadataRole = "devrites-skill-contract-error"
|
|
30
|
-
agentDispatchSkillGuard = "devrites-skill-dispatch-guard"
|
|
31
|
-
maxCodexRolloutLine = 8 << 20
|
|
32
|
-
v2HiddenAgentTypeRule = "GPT-5.6 V2 may omit agent_type from the visible tool schema while the runtime accepts it. Schema omission alone is not capability loss, V1 detection, or HITL: send agent_type anyway. Use a generic child only after the runtime explicitly identifies MultiAgent V1. If the named V2 call rejects agent_type, stop before any generic/default spawn; never use a default child."
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
var (
|
|
36
|
-
devritesAgentPathRe = regexp.MustCompile(`\.codex/agents/(devrites-[a-z0-9-]+)\.toml`)
|
|
37
|
-
devritesAgentNameRe = regexp.MustCompile(`^devrites-[a-z0-9-]+$`)
|
|
38
|
-
skillInvocationRe = regexp.MustCompile(`(?i)(?:^|[^a-z0-9_./-])(?:\$|/)([a-z][a-z0-9-]*)\b`)
|
|
39
|
-
reconcileCloseRe = regexp.MustCompile(`(?m)(?:^|[;&|]\s*)(?:rtk\s+)?(?:[A-Za-z0-9_./-]+/)?devrites-engine\s+reconcile\s+close\b`)
|
|
40
|
-
dispatchWaiveRe = regexp.MustCompile(`^\s*(?:rtk\s+)?(?:[A-Za-z0-9_./-]+/)?devrites-engine\s+dispatch-waive\s+([a-z-]+)\s*$`)
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
type agentDispatchHookInput struct {
|
|
44
|
-
HookEventName string `json:"hook_event_name"`
|
|
45
|
-
SessionID string `json:"session_id"`
|
|
46
|
-
TurnID string `json:"turn_id"`
|
|
47
|
-
ToolName string `json:"tool_name"`
|
|
48
|
-
ToolUseID string `json:"tool_use_id"`
|
|
49
|
-
AgentType string `json:"agent_type"`
|
|
50
|
-
AgentID string `json:"agent_id"`
|
|
51
|
-
Prompt string `json:"prompt"`
|
|
52
|
-
StopHookActive bool `json:"stop_hook_active"`
|
|
53
|
-
LastAssistantMessage string `json:"last_assistant_message"`
|
|
54
|
-
ToolResponse string `json:"-"`
|
|
55
|
-
ToolInputRaw json.RawMessage `json:"-"`
|
|
56
|
-
ToolInput struct {
|
|
57
|
-
Command string `json:"command"`
|
|
58
|
-
Cmd string `json:"cmd"`
|
|
59
|
-
AgentType string `json:"agent_type"`
|
|
60
|
-
ForkTurns string `json:"fork_turns"`
|
|
61
|
-
Message string `json:"message"`
|
|
62
|
-
TaskName string `json:"task_name"`
|
|
63
|
-
ReceiverThreadIDs []string `json:"receiver_thread_ids"`
|
|
64
|
-
IDs []string `json:"ids"`
|
|
65
|
-
AgentIDs []string `json:"agent_ids"`
|
|
66
|
-
Target string `json:"target"`
|
|
67
|
-
TimeoutMS int `json:"timeout_ms"`
|
|
68
|
-
} `json:"tool_input"`
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
type agentDispatchEvent struct {
|
|
72
|
-
Version string `json:"version"`
|
|
73
|
-
Event string `json:"event"`
|
|
74
|
-
TurnID string `json:"turn_id"`
|
|
75
|
-
ToolUseID string `json:"tool_use_id,omitempty"`
|
|
76
|
-
AgentID string `json:"agent_id,omitempty"`
|
|
77
|
-
AgentType string `json:"agent_type,omitempty"`
|
|
78
|
-
Role string `json:"role,omitempty"`
|
|
79
|
-
WindowID string `json:"window_id,omitempty"`
|
|
80
|
-
ResultSHA256 string `json:"result_sha256,omitempty"`
|
|
81
|
-
Reason string `json:"reason,omitempty"`
|
|
82
|
-
AtUnixNano int64 `json:"at_unix_nano"`
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type agentDispatchAttempt struct {
|
|
86
|
-
Role string
|
|
87
|
-
AgentType string
|
|
88
|
-
ToolUseID string
|
|
89
|
-
AgentID string
|
|
90
|
-
WindowID string
|
|
91
|
-
Started bool
|
|
92
|
-
Stopped bool
|
|
93
|
-
Waited bool
|
|
94
|
-
ResultSHA256 string
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
type codexRolloutRecord struct {
|
|
98
|
-
Timestamp string `json:"timestamp"`
|
|
99
|
-
Type string `json:"type"`
|
|
100
|
-
Payload struct {
|
|
101
|
-
Type string `json:"type"`
|
|
102
|
-
SessionID string `json:"session_id"`
|
|
103
|
-
ID string `json:"id"`
|
|
104
|
-
ParentThreadID string `json:"parent_thread_id"`
|
|
105
|
-
CWD string `json:"cwd"`
|
|
106
|
-
AgentPath string `json:"agent_path"`
|
|
107
|
-
AgentRole string `json:"agent_role"`
|
|
108
|
-
Name string `json:"name"`
|
|
109
|
-
Arguments string `json:"arguments"`
|
|
110
|
-
Author string `json:"author"`
|
|
111
|
-
Recipient string `json:"recipient"`
|
|
112
|
-
Role string `json:"role"`
|
|
113
|
-
LastAgentResult string `json:"last_agent_message"`
|
|
114
|
-
Content []struct {
|
|
115
|
-
Type string `json:"type"`
|
|
116
|
-
Text string `json:"text"`
|
|
117
|
-
} `json:"content"`
|
|
118
|
-
Metadata struct {
|
|
119
|
-
TurnID string `json:"turn_id"`
|
|
120
|
-
} `json:"internal_chat_message_metadata_passthrough"`
|
|
121
|
-
} `json:"payload"`
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
type codexV2Spawn struct {
|
|
125
|
-
Role string
|
|
126
|
-
AgentPath string
|
|
127
|
-
SpawnLine int
|
|
128
|
-
ResultLine int
|
|
129
|
-
SpawnedAt time.Time
|
|
130
|
-
Result string
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
func parseAgentDispatchHookInput(r io.Reader) (agentDispatchHookInput, error) {
|
|
134
|
-
var in agentDispatchHookInput
|
|
135
|
-
data, err := io.ReadAll(r)
|
|
136
|
-
if err != nil {
|
|
137
|
-
return in, err
|
|
138
|
-
}
|
|
139
|
-
if err := json.Unmarshal(data, &in); err != nil {
|
|
140
|
-
return in, err
|
|
141
|
-
}
|
|
142
|
-
var raw struct {
|
|
143
|
-
ToolInput json.RawMessage `json:"tool_input"`
|
|
144
|
-
ToolResponse json.RawMessage `json:"tool_response"`
|
|
145
|
-
}
|
|
146
|
-
if err := json.Unmarshal(data, &raw); err != nil {
|
|
147
|
-
return in, err
|
|
148
|
-
}
|
|
149
|
-
in.ToolInputRaw = raw.ToolInput
|
|
150
|
-
if len(raw.ToolResponse) > 0 {
|
|
151
|
-
if raw.ToolResponse[0] == '"' {
|
|
152
|
-
_ = json.Unmarshal(raw.ToolResponse, &in.ToolResponse)
|
|
153
|
-
} else {
|
|
154
|
-
in.ToolResponse = string(raw.ToolResponse)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
in.HookEventName = strings.TrimSpace(in.HookEventName)
|
|
158
|
-
in.SessionID = strings.TrimSpace(in.SessionID)
|
|
159
|
-
in.TurnID = strings.TrimSpace(in.TurnID)
|
|
160
|
-
in.ToolName = strings.TrimSpace(in.ToolName)
|
|
161
|
-
in.ToolUseID = strings.TrimSpace(in.ToolUseID)
|
|
162
|
-
in.AgentType = strings.TrimSpace(in.AgentType)
|
|
163
|
-
in.AgentID = strings.TrimSpace(in.AgentID)
|
|
164
|
-
in.ToolInput.AgentType = strings.TrimSpace(in.ToolInput.AgentType)
|
|
165
|
-
in.ToolInput.ForkTurns = strings.TrimSpace(in.ToolInput.ForkTurns)
|
|
166
|
-
in.ToolInput.TaskName = strings.TrimSpace(in.ToolInput.TaskName)
|
|
167
|
-
return in, nil
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
func agentDispatchRoot() (string, bool) {
|
|
171
|
-
root, err := orient.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
|
|
172
|
-
if err != nil {
|
|
173
|
-
return "", false
|
|
174
|
-
}
|
|
175
|
-
return root, true
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
func agentDispatchLogPath(root, sessionID string) (string, error) {
|
|
179
|
-
if strings.TrimSpace(sessionID) == "" {
|
|
180
|
-
return "", fmt.Errorf("missing session_id")
|
|
181
|
-
}
|
|
182
|
-
rootSum := sha256.Sum256([]byte(filepath.Clean(root)))
|
|
183
|
-
sessionSum := sha256.Sum256([]byte(sessionID))
|
|
184
|
-
dir := filepath.Join(os.TempDir(), agentDispatchStateDir, hex.EncodeToString(rootSum[:]))
|
|
185
|
-
if err := os.MkdirAll(dir, 0o700); err != nil {
|
|
186
|
-
return "", err
|
|
187
|
-
}
|
|
188
|
-
if err := os.Chmod(dir, 0o700); err != nil {
|
|
189
|
-
return "", err
|
|
190
|
-
}
|
|
191
|
-
return filepath.Join(dir, hex.EncodeToString(sessionSum[:])+".jsonl"), nil
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
func appendAgentDispatchEvent(root, sessionID string, event agentDispatchEvent) error {
|
|
195
|
-
path, err := agentDispatchLogPath(root, sessionID)
|
|
196
|
-
if err != nil {
|
|
197
|
-
return err
|
|
198
|
-
}
|
|
199
|
-
event.Version = agentDispatchStateVersion
|
|
200
|
-
event.AtUnixNano = time.Now().UnixNano()
|
|
201
|
-
data, err := json.Marshal(event)
|
|
202
|
-
if err != nil {
|
|
203
|
-
return err
|
|
204
|
-
}
|
|
205
|
-
data = append(data, '\n')
|
|
206
|
-
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
|
|
207
|
-
if err != nil {
|
|
208
|
-
return err
|
|
209
|
-
}
|
|
210
|
-
defer f.Close()
|
|
211
|
-
if err := f.Chmod(0o600); err != nil {
|
|
212
|
-
return err
|
|
213
|
-
}
|
|
214
|
-
_, err = f.Write(data)
|
|
215
|
-
return err
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
func readAgentDispatchEvents(root, sessionID string) ([]agentDispatchEvent, error) {
|
|
219
|
-
path, err := agentDispatchLogPath(root, sessionID)
|
|
220
|
-
if err != nil {
|
|
221
|
-
return nil, err
|
|
222
|
-
}
|
|
223
|
-
data, err := os.ReadFile(path)
|
|
224
|
-
if os.IsNotExist(err) {
|
|
225
|
-
return nil, nil
|
|
226
|
-
}
|
|
227
|
-
if err != nil {
|
|
228
|
-
return nil, err
|
|
229
|
-
}
|
|
230
|
-
var events []agentDispatchEvent
|
|
231
|
-
for _, line := range bytes.Split(data, []byte{'\n'}) {
|
|
232
|
-
if len(bytes.TrimSpace(line)) == 0 {
|
|
233
|
-
continue
|
|
234
|
-
}
|
|
235
|
-
var event agentDispatchEvent
|
|
236
|
-
if err := json.Unmarshal(line, &event); err != nil {
|
|
237
|
-
return nil, fmt.Errorf("invalid agent dispatch state: %w", err)
|
|
238
|
-
}
|
|
239
|
-
if event.Version != agentDispatchStateVersion {
|
|
240
|
-
return nil, fmt.Errorf("unsupported agent dispatch state %q", event.Version)
|
|
241
|
-
}
|
|
242
|
-
events = append(events, event)
|
|
243
|
-
}
|
|
244
|
-
return events, nil
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
func explicitRolesInText(text string) []string {
|
|
248
|
-
matches := devritesAgentPathRe.FindAllStringSubmatch(text, -1)
|
|
249
|
-
seen := map[string]struct{}{}
|
|
250
|
-
var roles []string
|
|
251
|
-
for _, match := range matches {
|
|
252
|
-
role := match[1]
|
|
253
|
-
if _, ok := seen[role]; ok {
|
|
254
|
-
continue
|
|
255
|
-
}
|
|
256
|
-
seen[role] = struct{}{}
|
|
257
|
-
roles = append(roles, role)
|
|
258
|
-
}
|
|
259
|
-
sort.Strings(roles)
|
|
260
|
-
return roles
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
func requiredAgentRolesFromSkill(path string) ([]string, error) {
|
|
264
|
-
info, err := os.Stat(path)
|
|
265
|
-
if err != nil {
|
|
266
|
-
return nil, err
|
|
267
|
-
}
|
|
268
|
-
if !info.Mode().IsRegular() {
|
|
269
|
-
return nil, fmt.Errorf("skill contract is not a regular file")
|
|
270
|
-
}
|
|
271
|
-
if info.Size() > 256<<10 {
|
|
272
|
-
return nil, fmt.Errorf("skill contract exceeds 256 KiB")
|
|
273
|
-
}
|
|
274
|
-
data, err := os.ReadFile(path)
|
|
275
|
-
if err != nil {
|
|
276
|
-
return nil, err
|
|
277
|
-
}
|
|
278
|
-
lines := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
|
|
279
|
-
if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" {
|
|
280
|
-
return nil, fmt.Errorf("skill contract is missing frontmatter")
|
|
281
|
-
}
|
|
282
|
-
value := ""
|
|
283
|
-
found := false
|
|
284
|
-
closed := false
|
|
285
|
-
for _, line := range lines[1:] {
|
|
286
|
-
if strings.TrimSpace(line) == "---" {
|
|
287
|
-
closed = true
|
|
288
|
-
break
|
|
289
|
-
}
|
|
290
|
-
if !strings.HasPrefix(line, "required-agent-roles:") {
|
|
291
|
-
continue
|
|
292
|
-
}
|
|
293
|
-
if found {
|
|
294
|
-
return nil, fmt.Errorf("required-agent-roles is duplicated")
|
|
295
|
-
}
|
|
296
|
-
found = true
|
|
297
|
-
value = strings.TrimSpace(strings.TrimPrefix(line, "required-agent-roles:"))
|
|
298
|
-
}
|
|
299
|
-
if !closed {
|
|
300
|
-
return nil, fmt.Errorf("skill contract has unterminated frontmatter")
|
|
301
|
-
}
|
|
302
|
-
if !found {
|
|
303
|
-
return nil, fmt.Errorf("skill contract is missing required-agent-roles")
|
|
304
|
-
}
|
|
305
|
-
value = strings.Trim(value, `"'`)
|
|
306
|
-
if value == "none" {
|
|
307
|
-
return nil, nil
|
|
308
|
-
}
|
|
309
|
-
if value == "" {
|
|
310
|
-
return nil, fmt.Errorf("required-agent-roles is empty")
|
|
311
|
-
}
|
|
312
|
-
seen := map[string]struct{}{}
|
|
313
|
-
var roles []string
|
|
314
|
-
for _, item := range strings.Split(value, ",") {
|
|
315
|
-
role := strings.TrimSpace(item)
|
|
316
|
-
if !devritesAgentNameRe.MatchString(role) {
|
|
317
|
-
return nil, fmt.Errorf("invalid required agent role %q", role)
|
|
318
|
-
}
|
|
319
|
-
if _, ok := seen[role]; ok {
|
|
320
|
-
return nil, fmt.Errorf("duplicate required agent role %q", role)
|
|
321
|
-
}
|
|
322
|
-
seen[role] = struct{}{}
|
|
323
|
-
roles = append(roles, role)
|
|
324
|
-
}
|
|
325
|
-
sort.Strings(roles)
|
|
326
|
-
return roles, nil
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
func requiredRolesInPrompt(root, text string) ([]string, bool, error) {
|
|
330
|
-
seen := map[string]struct{}{}
|
|
331
|
-
skillInvoked := false
|
|
332
|
-
skillsDir := filepath.Join(filepath.Dir(root), ".agents", "skills")
|
|
333
|
-
for _, match := range skillInvocationRe.FindAllStringSubmatch(text, -1) {
|
|
334
|
-
skill := strings.ToLower(match[1])
|
|
335
|
-
path := filepath.Join(skillsDir, skill, "SKILL.md")
|
|
336
|
-
if !safepath.WithinResolved(path, skillsDir) {
|
|
337
|
-
return nil, false, fmt.Errorf("unsafe skill path for %s", skill)
|
|
338
|
-
}
|
|
339
|
-
roles, err := requiredAgentRolesFromSkill(path)
|
|
340
|
-
if os.IsNotExist(err) {
|
|
341
|
-
continue
|
|
342
|
-
}
|
|
343
|
-
if err != nil {
|
|
344
|
-
return nil, false, fmt.Errorf("%s: %w", skill, err)
|
|
345
|
-
}
|
|
346
|
-
skillInvoked = true
|
|
347
|
-
for _, role := range roles {
|
|
348
|
-
seen[role] = struct{}{}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
roles := make([]string, 0, len(seen))
|
|
352
|
-
for role := range seen {
|
|
353
|
-
roles = append(roles, role)
|
|
354
|
-
}
|
|
355
|
-
sort.Strings(roles)
|
|
356
|
-
return roles, skillInvoked, nil
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
func expectedAgentType(role string) string {
|
|
360
|
-
if role == "devrites-slice-wright" {
|
|
361
|
-
return "worker"
|
|
362
|
-
}
|
|
363
|
-
return "explorer"
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
func agentRoleDeveloperInstructions(root, role string) (string, error) {
|
|
367
|
-
agentsDir := filepath.Join(filepath.Dir(root), ".codex", "agents")
|
|
368
|
-
path := filepath.Join(agentsDir, role+".toml")
|
|
369
|
-
if !safepath.WithinResolved(path, agentsDir) {
|
|
370
|
-
return "", fmt.Errorf("unsafe role path")
|
|
371
|
-
}
|
|
372
|
-
info, err := os.Stat(path)
|
|
373
|
-
if err != nil || !info.Mode().IsRegular() {
|
|
374
|
-
return "", fmt.Errorf("role contract is not a regular file")
|
|
375
|
-
}
|
|
376
|
-
if info.Size() > 256<<10 {
|
|
377
|
-
return "", fmt.Errorf("role contract exceeds 256 KiB")
|
|
378
|
-
}
|
|
379
|
-
data, err := os.ReadFile(path)
|
|
380
|
-
if err != nil {
|
|
381
|
-
return "", err
|
|
382
|
-
}
|
|
383
|
-
lines := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
|
|
384
|
-
wantName := `name = "` + role + `"`
|
|
385
|
-
nameMatches := false
|
|
386
|
-
start := -1
|
|
387
|
-
for i, line := range lines {
|
|
388
|
-
switch strings.TrimSpace(line) {
|
|
389
|
-
case wantName:
|
|
390
|
-
nameMatches = true
|
|
391
|
-
case "developer_instructions = '''":
|
|
392
|
-
start = i + 1
|
|
393
|
-
}
|
|
394
|
-
if start >= 0 {
|
|
395
|
-
break
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
if !nameMatches || start < 0 {
|
|
399
|
-
return "", fmt.Errorf("role contract name or developer_instructions is invalid")
|
|
400
|
-
}
|
|
401
|
-
for i := start; i < len(lines); i++ {
|
|
402
|
-
if strings.TrimSpace(lines[i]) != "'''" {
|
|
403
|
-
continue
|
|
404
|
-
}
|
|
405
|
-
instructions := strings.TrimSpace(strings.Join(lines[start:i], "\n"))
|
|
406
|
-
if instructions == "" {
|
|
407
|
-
return "", fmt.Errorf("developer_instructions is empty")
|
|
408
|
-
}
|
|
409
|
-
return instructions, nil
|
|
410
|
-
}
|
|
411
|
-
return "", fmt.Errorf("developer_instructions is unterminated")
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
func currentReconcileWindowID() string {
|
|
415
|
-
_, _, dir, ok := resolveWorkspace()
|
|
416
|
-
if !ok {
|
|
417
|
-
return ""
|
|
418
|
-
}
|
|
419
|
-
// The wright-start canonical fingerprint is captured by the spawn hook
|
|
420
|
-
// before the child starts. It narrows the delta but does not change source
|
|
421
|
-
// authorization.
|
|
422
|
-
names := []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"}
|
|
423
|
-
h := sha256.New()
|
|
424
|
-
for _, name := range names {
|
|
425
|
-
data, err := os.ReadFile(filepath.Join(dir, name))
|
|
426
|
-
if err != nil {
|
|
427
|
-
return ""
|
|
428
|
-
}
|
|
429
|
-
_, _ = io.WriteString(h, name)
|
|
430
|
-
_, _ = h.Write([]byte{0})
|
|
431
|
-
_, _ = h.Write(data)
|
|
432
|
-
_, _ = h.Write([]byte{0})
|
|
433
|
-
}
|
|
434
|
-
return hex.EncodeToString(h.Sum(nil))
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
func dispatchTurnState(events []agentDispatchEvent, turnID string) (map[string]struct{}, []*agentDispatchAttempt) {
|
|
438
|
-
armed := map[string]struct{}{}
|
|
439
|
-
attemptByTool := map[string]*agentDispatchAttempt{}
|
|
440
|
-
attemptByAgent := map[string]*agentDispatchAttempt{}
|
|
441
|
-
var attempts []*agentDispatchAttempt
|
|
442
|
-
for _, event := range events {
|
|
443
|
-
if event.TurnID != turnID {
|
|
444
|
-
continue
|
|
445
|
-
}
|
|
446
|
-
switch event.Event {
|
|
447
|
-
case "armed":
|
|
448
|
-
armed[event.Role] = struct{}{}
|
|
449
|
-
case "waived":
|
|
450
|
-
delete(armed, event.Role)
|
|
451
|
-
case "pending":
|
|
452
|
-
armed[event.Role] = struct{}{}
|
|
453
|
-
attempt := &agentDispatchAttempt{
|
|
454
|
-
Role: event.Role,
|
|
455
|
-
AgentType: event.AgentType,
|
|
456
|
-
ToolUseID: event.ToolUseID,
|
|
457
|
-
WindowID: event.WindowID,
|
|
458
|
-
}
|
|
459
|
-
attemptByTool[event.ToolUseID] = attempt
|
|
460
|
-
attempts = append(attempts, attempt)
|
|
461
|
-
case "started":
|
|
462
|
-
attempt := attemptByTool[event.ToolUseID]
|
|
463
|
-
if attempt == nil {
|
|
464
|
-
attempt = &agentDispatchAttempt{
|
|
465
|
-
Role: event.Role,
|
|
466
|
-
AgentType: event.AgentType,
|
|
467
|
-
ToolUseID: event.ToolUseID,
|
|
468
|
-
WindowID: event.WindowID,
|
|
469
|
-
}
|
|
470
|
-
attempts = append(attempts, attempt)
|
|
471
|
-
}
|
|
472
|
-
attempt.Started = true
|
|
473
|
-
attempt.AgentID = event.AgentID
|
|
474
|
-
attemptByAgent[event.AgentID] = attempt
|
|
475
|
-
case "waited":
|
|
476
|
-
if attempt := attemptByAgent[event.AgentID]; attempt != nil {
|
|
477
|
-
attempt.Waited = true
|
|
478
|
-
}
|
|
479
|
-
case "stopped":
|
|
480
|
-
if attempt := attemptByAgent[event.AgentID]; attempt != nil {
|
|
481
|
-
attempt.Stopped = true
|
|
482
|
-
attempt.ResultSHA256 = event.ResultSHA256
|
|
483
|
-
}
|
|
484
|
-
case "verified":
|
|
485
|
-
attempts = append(attempts, &agentDispatchAttempt{
|
|
486
|
-
Role: event.Role,
|
|
487
|
-
AgentType: event.AgentType,
|
|
488
|
-
AgentID: event.AgentID,
|
|
489
|
-
WindowID: event.WindowID,
|
|
490
|
-
Started: true,
|
|
491
|
-
Stopped: true,
|
|
492
|
-
Waited: true,
|
|
493
|
-
ResultSHA256: event.ResultSHA256,
|
|
494
|
-
})
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
return armed, attempts
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
func durableCodexV2DispatchAttempts(
|
|
501
|
-
root, sessionID, turnID string,
|
|
502
|
-
armed map[string]struct{},
|
|
503
|
-
) ([]*agentDispatchAttempt, error) {
|
|
504
|
-
parentPath, sessionsDir, err := findCodexParentRollout(sessionID)
|
|
505
|
-
if err != nil || parentPath == "" {
|
|
506
|
-
return nil, err
|
|
507
|
-
}
|
|
508
|
-
projectRoot := filepath.Dir(root)
|
|
509
|
-
spawns, waitLines, err := readCodexV2ParentRollout(parentPath, projectRoot, sessionID, turnID, armed)
|
|
510
|
-
if err != nil {
|
|
511
|
-
return nil, err
|
|
512
|
-
}
|
|
513
|
-
var attempts []*agentDispatchAttempt
|
|
514
|
-
for _, spawn := range spawns {
|
|
515
|
-
role := spawn.Role
|
|
516
|
-
var childPath string
|
|
517
|
-
var childMeta codexRolloutRecord
|
|
518
|
-
if strings.TrimSpace(spawn.Result) != "" {
|
|
519
|
-
childPath, childMeta, err = findCodexV2ChildRollout(
|
|
520
|
-
sessionsDir, parentPath, projectRoot, sessionID, spawn.AgentPath, "",
|
|
521
|
-
)
|
|
522
|
-
if err != nil {
|
|
523
|
-
return nil, err
|
|
524
|
-
}
|
|
525
|
-
if childPath != "" {
|
|
526
|
-
role = childMeta.Payload.AgentRole
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
if !devritesAgentNameRe.MatchString(role) || role == agentDispatchSkillGuard {
|
|
530
|
-
if strings.TrimSpace(spawn.Result) != "" {
|
|
531
|
-
return nil, fmt.Errorf("codex V2 completed a default or non-DevRites child during a DevRites skill turn; this turn is tainted and cannot be repaired by another dispatch. Do not use the child result or advance workflow state. End this turn, then retry the skill in a fresh turn; %s", conditionalDispatchInstruction())
|
|
532
|
-
}
|
|
533
|
-
continue
|
|
534
|
-
}
|
|
535
|
-
armed[role] = struct{}{}
|
|
536
|
-
attempt := &agentDispatchAttempt{
|
|
537
|
-
Role: role,
|
|
538
|
-
AgentType: role,
|
|
539
|
-
Started: true,
|
|
540
|
-
}
|
|
541
|
-
attempts = append(attempts, attempt)
|
|
542
|
-
if strings.TrimSpace(spawn.Result) == "" || !lineBetween(waitLines, spawn.SpawnLine, spawn.ResultLine) {
|
|
543
|
-
continue
|
|
544
|
-
}
|
|
545
|
-
if childPath == "" || childMeta.Payload.AgentRole != role {
|
|
546
|
-
continue
|
|
547
|
-
}
|
|
548
|
-
instructions, err := agentRoleDeveloperInstructions(root, role)
|
|
549
|
-
if err != nil {
|
|
550
|
-
return nil, fmt.Errorf("named role %s is not loadable: %w", role, err)
|
|
551
|
-
}
|
|
552
|
-
childResult, err := codexChildResult(childPath, instructions)
|
|
553
|
-
if err != nil {
|
|
554
|
-
return nil, err
|
|
555
|
-
}
|
|
556
|
-
if childMeta.Payload.ID == "" || strings.TrimSpace(childResult) == "" {
|
|
557
|
-
continue
|
|
558
|
-
}
|
|
559
|
-
attempt.AgentID = childMeta.Payload.ID
|
|
560
|
-
attempt.Stopped = true
|
|
561
|
-
attempt.Waited = true
|
|
562
|
-
if role == "devrites-slice-wright" {
|
|
563
|
-
attempt.WindowID = currentReconcileWindowID()
|
|
564
|
-
if attempt.WindowID == "" || !reconcileWindowPredates(spawn.SpawnedAt) {
|
|
565
|
-
continue
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
sum := sha256.Sum256([]byte(childResult))
|
|
569
|
-
attempt.ResultSHA256 = hex.EncodeToString(sum[:])
|
|
570
|
-
}
|
|
571
|
-
return attempts, nil
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
func findCodexParentRollout(sessionID string) (string, string, error) {
|
|
575
|
-
codeHome := strings.TrimSpace(os.Getenv("CODEX_HOME"))
|
|
576
|
-
if codeHome == "" {
|
|
577
|
-
home, err := os.UserHomeDir()
|
|
578
|
-
if err != nil {
|
|
579
|
-
return "", "", nil
|
|
580
|
-
}
|
|
581
|
-
codeHome = filepath.Join(home, ".codex")
|
|
582
|
-
}
|
|
583
|
-
sessionsDir := filepath.Join(codeHome, "sessions")
|
|
584
|
-
wantSuffix := "-" + sessionID + ".jsonl"
|
|
585
|
-
var parentPath string
|
|
586
|
-
err := filepath.WalkDir(sessionsDir, func(path string, entry fs.DirEntry, walkErr error) error {
|
|
587
|
-
if walkErr != nil {
|
|
588
|
-
if os.IsNotExist(walkErr) {
|
|
589
|
-
return nil
|
|
590
|
-
}
|
|
591
|
-
return walkErr
|
|
592
|
-
}
|
|
593
|
-
if entry.IsDir() || !strings.HasSuffix(entry.Name(), wantSuffix) {
|
|
594
|
-
return nil
|
|
595
|
-
}
|
|
596
|
-
parentPath = path
|
|
597
|
-
return fs.SkipAll
|
|
598
|
-
})
|
|
599
|
-
if os.IsNotExist(err) {
|
|
600
|
-
return "", sessionsDir, nil
|
|
601
|
-
}
|
|
602
|
-
return parentPath, sessionsDir, err
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
func readCodexV2ParentRollout(
|
|
606
|
-
path, projectRoot, sessionID, turnID string,
|
|
607
|
-
armed map[string]struct{},
|
|
608
|
-
) (map[string]*codexV2Spawn, []int, error) {
|
|
609
|
-
file, err := os.Open(path)
|
|
610
|
-
if err != nil {
|
|
611
|
-
return nil, nil, err
|
|
612
|
-
}
|
|
613
|
-
defer file.Close()
|
|
614
|
-
|
|
615
|
-
spawns := map[string]*codexV2Spawn{}
|
|
616
|
-
var waitLines []int
|
|
617
|
-
metaSeen := false
|
|
618
|
-
_, guarded := armed[agentDispatchSkillGuard]
|
|
619
|
-
scanner := bufio.NewScanner(file)
|
|
620
|
-
scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
|
|
621
|
-
for lineNumber := 1; scanner.Scan(); lineNumber++ {
|
|
622
|
-
line := scanner.Bytes()
|
|
623
|
-
if !bytes.Contains(line, []byte(`"session_meta"`)) &&
|
|
624
|
-
!bytes.Contains(line, []byte(`"spawn_agent"`)) &&
|
|
625
|
-
!bytes.Contains(line, []byte(`"wait_agent"`)) &&
|
|
626
|
-
!bytes.Contains(line, []byte(`"agent_message"`)) {
|
|
627
|
-
continue
|
|
628
|
-
}
|
|
629
|
-
var record codexRolloutRecord
|
|
630
|
-
if err := json.Unmarshal(line, &record); err != nil {
|
|
631
|
-
return nil, nil, fmt.Errorf("invalid Codex parent rollout: %w", err)
|
|
632
|
-
}
|
|
633
|
-
if record.Type == "session_meta" {
|
|
634
|
-
if record.Payload.ID != sessionID || !sameResolvedPath(record.Payload.CWD, projectRoot) {
|
|
635
|
-
return nil, nil, fmt.Errorf("codex parent rollout does not match the current session root")
|
|
636
|
-
}
|
|
637
|
-
metaSeen = true
|
|
638
|
-
continue
|
|
639
|
-
}
|
|
640
|
-
if record.Payload.Metadata.TurnID != turnID {
|
|
641
|
-
continue
|
|
642
|
-
}
|
|
643
|
-
switch {
|
|
644
|
-
case record.Payload.Type == "function_call" && record.Payload.Name == "spawn_agent":
|
|
645
|
-
var args struct {
|
|
646
|
-
AgentType string `json:"agent_type"`
|
|
647
|
-
TaskName string `json:"task_name"`
|
|
648
|
-
ForkTurns string `json:"fork_turns"`
|
|
649
|
-
}
|
|
650
|
-
if json.Unmarshal([]byte(record.Payload.Arguments), &args) != nil {
|
|
651
|
-
continue
|
|
652
|
-
}
|
|
653
|
-
_, required := armed[args.AgentType]
|
|
654
|
-
if (!required && !guarded) || args.TaskName == "" ||
|
|
655
|
-
strings.Contains(args.TaskName, "/") || args.ForkTurns != "none" {
|
|
656
|
-
continue
|
|
657
|
-
}
|
|
658
|
-
spawnedAt, err := time.Parse(time.RFC3339Nano, record.Timestamp)
|
|
659
|
-
if err != nil {
|
|
660
|
-
return nil, nil, fmt.Errorf("codex spawn timestamp is invalid: %w", err)
|
|
661
|
-
}
|
|
662
|
-
spawns["/root/"+args.TaskName] = &codexV2Spawn{
|
|
663
|
-
Role: args.AgentType,
|
|
664
|
-
AgentPath: "/root/" + args.TaskName,
|
|
665
|
-
SpawnLine: lineNumber,
|
|
666
|
-
SpawnedAt: spawnedAt,
|
|
667
|
-
}
|
|
668
|
-
case record.Payload.Type == "function_call" && record.Payload.Name == "wait_agent":
|
|
669
|
-
waitLines = append(waitLines, lineNumber)
|
|
670
|
-
case record.Payload.Type == "agent_message" &&
|
|
671
|
-
record.Payload.Recipient == "/root":
|
|
672
|
-
spawn := spawns[record.Payload.Author]
|
|
673
|
-
if spawn == nil {
|
|
674
|
-
continue
|
|
675
|
-
}
|
|
676
|
-
spawn.Result = rolloutInputText(record.Payload.Content)
|
|
677
|
-
spawn.ResultLine = lineNumber
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
if err := scanner.Err(); err != nil {
|
|
681
|
-
return nil, nil, fmt.Errorf("read Codex parent rollout: %w", err)
|
|
682
|
-
}
|
|
683
|
-
if !metaSeen {
|
|
684
|
-
return nil, nil, fmt.Errorf("codex parent rollout is missing session metadata")
|
|
685
|
-
}
|
|
686
|
-
return spawns, waitLines, nil
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
func findCodexV2ChildRollout(
|
|
690
|
-
sessionsDir, parentPath, projectRoot, sessionID, agentPath, agentID string,
|
|
691
|
-
) (string, codexRolloutRecord, error) {
|
|
692
|
-
var childPath string
|
|
693
|
-
var childMeta codexRolloutRecord
|
|
694
|
-
selector := agentPath
|
|
695
|
-
if selector == "" {
|
|
696
|
-
selector = agentID
|
|
697
|
-
}
|
|
698
|
-
err := filepath.WalkDir(sessionsDir, func(path string, entry fs.DirEntry, walkErr error) error {
|
|
699
|
-
if walkErr != nil {
|
|
700
|
-
if os.IsNotExist(walkErr) {
|
|
701
|
-
return nil
|
|
702
|
-
}
|
|
703
|
-
return walkErr
|
|
704
|
-
}
|
|
705
|
-
if entry.IsDir() || path == parentPath || filepath.Ext(path) != ".jsonl" {
|
|
706
|
-
return nil
|
|
707
|
-
}
|
|
708
|
-
record, ok, err := firstCodexRolloutRecord(path)
|
|
709
|
-
if err != nil || !ok {
|
|
710
|
-
return err
|
|
711
|
-
}
|
|
712
|
-
if record.Type != "session_meta" ||
|
|
713
|
-
record.Payload.ParentThreadID != sessionID ||
|
|
714
|
-
(agentPath != "" && record.Payload.AgentPath != agentPath) ||
|
|
715
|
-
(agentID != "" && record.Payload.ID != agentID) ||
|
|
716
|
-
!sameResolvedPath(record.Payload.CWD, projectRoot) {
|
|
717
|
-
return nil
|
|
718
|
-
}
|
|
719
|
-
if childPath != "" {
|
|
720
|
-
return fmt.Errorf("multiple Codex child rollouts match %s", selector)
|
|
721
|
-
}
|
|
722
|
-
childPath = path
|
|
723
|
-
childMeta = record
|
|
724
|
-
return nil
|
|
725
|
-
})
|
|
726
|
-
return childPath, childMeta, err
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
func firstCodexRolloutRecord(path string) (codexRolloutRecord, bool, error) {
|
|
730
|
-
var record codexRolloutRecord
|
|
731
|
-
file, err := os.Open(path)
|
|
732
|
-
if err != nil {
|
|
733
|
-
return record, false, err
|
|
734
|
-
}
|
|
735
|
-
defer file.Close()
|
|
736
|
-
scanner := bufio.NewScanner(file)
|
|
737
|
-
scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
|
|
738
|
-
if !scanner.Scan() {
|
|
739
|
-
return record, false, scanner.Err()
|
|
740
|
-
}
|
|
741
|
-
if err := json.Unmarshal(scanner.Bytes(), &record); err != nil {
|
|
742
|
-
return record, false, nil
|
|
743
|
-
}
|
|
744
|
-
return record, true, nil
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
func codexChildResult(path, instructions string) (string, error) {
|
|
748
|
-
file, err := os.Open(path)
|
|
749
|
-
if err != nil {
|
|
750
|
-
return "", err
|
|
751
|
-
}
|
|
752
|
-
defer file.Close()
|
|
753
|
-
scanner := bufio.NewScanner(file)
|
|
754
|
-
scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
|
|
755
|
-
instructionsLoaded := false
|
|
756
|
-
result := ""
|
|
757
|
-
for scanner.Scan() {
|
|
758
|
-
line := scanner.Bytes()
|
|
759
|
-
if !bytes.Contains(line, []byte(`"task_complete"`)) &&
|
|
760
|
-
!bytes.Contains(line, []byte(`"developer"`)) {
|
|
761
|
-
continue
|
|
762
|
-
}
|
|
763
|
-
var record codexRolloutRecord
|
|
764
|
-
if json.Unmarshal(line, &record) != nil {
|
|
765
|
-
continue
|
|
766
|
-
}
|
|
767
|
-
if record.Payload.Type == "message" && record.Payload.Role == "developer" &&
|
|
768
|
-
strings.Contains(rolloutInputText(record.Payload.Content), instructions) {
|
|
769
|
-
instructionsLoaded = true
|
|
770
|
-
}
|
|
771
|
-
if record.Payload.Type == "task_complete" &&
|
|
772
|
-
strings.TrimSpace(record.Payload.LastAgentResult) != "" {
|
|
773
|
-
result = record.Payload.LastAgentResult
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
if err := scanner.Err(); err != nil {
|
|
777
|
-
return "", err
|
|
778
|
-
}
|
|
779
|
-
if !instructionsLoaded {
|
|
780
|
-
return "", nil
|
|
781
|
-
}
|
|
782
|
-
return result, nil
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
func rolloutInputText(content []struct {
|
|
786
|
-
Type string `json:"type"`
|
|
787
|
-
Text string `json:"text"`
|
|
788
|
-
}) string {
|
|
789
|
-
var text []string
|
|
790
|
-
for _, item := range content {
|
|
791
|
-
if item.Type == "input_text" && strings.TrimSpace(item.Text) != "" {
|
|
792
|
-
text = append(text, item.Text)
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
return strings.Join(text, "\n")
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
func lineBetween(lines []int, after, before int) bool {
|
|
799
|
-
for _, line := range lines {
|
|
800
|
-
if line > after && line < before {
|
|
801
|
-
return true
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
return false
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
func sameResolvedPath(left, right string) bool {
|
|
808
|
-
resolve := func(path string) string {
|
|
809
|
-
absolute, err := filepath.Abs(path)
|
|
810
|
-
if err == nil {
|
|
811
|
-
path = absolute
|
|
812
|
-
}
|
|
813
|
-
if resolved, err := filepath.EvalSymlinks(path); err == nil {
|
|
814
|
-
path = resolved
|
|
815
|
-
}
|
|
816
|
-
return filepath.Clean(path)
|
|
817
|
-
}
|
|
818
|
-
return resolve(left) == resolve(right)
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
func reconcileWindowPredates(at time.Time) bool {
|
|
822
|
-
_, _, dir, ok := resolveWorkspace()
|
|
823
|
-
if !ok {
|
|
824
|
-
return false
|
|
825
|
-
}
|
|
826
|
-
for _, name := range []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"} {
|
|
827
|
-
info, err := os.Stat(filepath.Join(dir, name))
|
|
828
|
-
if err != nil || info.ModTime().After(at) {
|
|
829
|
-
return false
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
return true
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
func dispatchAttemptComplete(attempt *agentDispatchAttempt) bool {
|
|
836
|
-
return attempt.Started && attempt.Stopped && attempt.Waited && attempt.ResultSHA256 != ""
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
func roleSatisfied(role, windowID string, attempts []*agentDispatchAttempt) bool {
|
|
840
|
-
for _, attempt := range attempts {
|
|
841
|
-
if attempt.Role != role || !dispatchAttemptComplete(attempt) {
|
|
842
|
-
continue
|
|
843
|
-
}
|
|
844
|
-
if windowID == "" || attempt.WindowID == windowID {
|
|
845
|
-
return true
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
return false
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
func dispatchInstruction(role string) string {
|
|
852
|
-
return fmt.Sprintf(
|
|
853
|
-
"On MultiAgent V2 call spawn_agent with agent_type=%s, a unique task_name, and fork_turns=\"none\" so Codex loads .codex/agents/%s.toml natively. %s On V1 use agent_type=%s with fork_turns=\"none\" and name that role TOML in the message. Wait for the returned child and use its non-empty result. Do not call wait before spawn_agent and do not synthesize the agent result.",
|
|
854
|
-
role, role, v2HiddenAgentTypeRule, expectedAgentType(role),
|
|
855
|
-
)
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
func conditionalDispatchInstruction() string {
|
|
859
|
-
return "CONDITIONAL DISPATCH RULE — If this skill reaches a child-agent step, call spawn_agent with the exact named agent_type=devrites-<role> specified by the skill, a unique task_name, and fork_turns=\"none\". " + v2HiddenAgentTypeRule + " Wait for the returned child and use its non-empty result."
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
func preDispatchStopInstruction() string {
|
|
863
|
-
return "PRE-DISPATCH STOP RULE — If a deterministic prerequisite forces STOP before any spawn attempt, do not spawn only to satisfy this receipt. Run exactly one successful standalone devrites-engine dispatch-waive <reason> before replying. Use only the matching observed predicate: no-active-workspace, wrong-phase, readiness-failed, no-eligible-work, or human-gate-before-dispatch. The successful PostToolUse receipt is required; never waive after a spawn attempt."
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
func incompleteDispatchReason(armed map[string]struct{}, attempts []*agentDispatchAttempt) string {
|
|
867
|
-
roles := make([]string, 0, len(armed))
|
|
868
|
-
for role := range armed {
|
|
869
|
-
if role == agentDispatchSkillGuard {
|
|
870
|
-
continue
|
|
871
|
-
}
|
|
872
|
-
if !roleSatisfied(role, "", attempts) {
|
|
873
|
-
roles = append(roles, role)
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
if len(roles) == 0 {
|
|
877
|
-
return ""
|
|
878
|
-
}
|
|
879
|
-
sort.Strings(roles)
|
|
880
|
-
role := roles[0]
|
|
881
|
-
if role == agentDispatchMetadataRole {
|
|
882
|
-
return "DevRites could not determine this skill's required agents because its installed required-agent-roles contract is invalid. Reinstall or repair the DevRites skill pack before continuing."
|
|
883
|
-
}
|
|
884
|
-
reason := fmt.Sprintf("DevRites dispatch for %s is not complete. %s", role, dispatchInstruction(role))
|
|
885
|
-
if len(attempts) == 0 {
|
|
886
|
-
reason += " " + preDispatchStopInstruction()
|
|
887
|
-
}
|
|
888
|
-
return reason
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
func preToolDeny(h harness.Harness, reason string, stdout, stderr io.Writer) int {
|
|
892
|
-
out, err := h.PreToolDeny(reason)
|
|
893
|
-
if err != nil {
|
|
894
|
-
debugf(stderr, "agent-dispatch: %v", err)
|
|
895
|
-
return exitOK
|
|
896
|
-
}
|
|
897
|
-
fmt.Fprintln(stdout, out)
|
|
898
|
-
return exitOK
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
func preToolRewrite(h harness.Harness, toolInput json.RawMessage, message string, stdout, stderr io.Writer) int {
|
|
902
|
-
var updated map[string]any
|
|
903
|
-
if err := json.Unmarshal(toolInput, &updated); err != nil {
|
|
904
|
-
return preToolDeny(h, "DevRites could not bind developer_instructions to the spawn input.", stdout, stderr)
|
|
905
|
-
}
|
|
906
|
-
updated["message"] = message
|
|
907
|
-
out, err := json.Marshal(map[string]any{
|
|
908
|
-
"hookSpecificOutput": map[string]any{
|
|
909
|
-
"hookEventName": "PreToolUse",
|
|
910
|
-
"permissionDecision": "allow",
|
|
911
|
-
"updatedInput": updated,
|
|
912
|
-
},
|
|
913
|
-
})
|
|
914
|
-
if err != nil {
|
|
915
|
-
return preToolDeny(h, "DevRites could not encode the bound spawn input.", stdout, stderr)
|
|
916
|
-
}
|
|
917
|
-
fmt.Fprintln(stdout, string(out))
|
|
918
|
-
return exitOK
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
func stopDispatchBlock(h harness.Harness, reason string, stdout, stderr io.Writer) int {
|
|
922
|
-
out, err := h.StopBlock(reason)
|
|
923
|
-
if err != nil {
|
|
924
|
-
debugf(stderr, "agent-dispatch: %v", err)
|
|
925
|
-
return exitOK
|
|
926
|
-
}
|
|
927
|
-
fmt.Fprintln(stdout, out)
|
|
928
|
-
return exitOK
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
func stopDispatchFailure(reason string, stdout, stderr io.Writer) int {
|
|
932
|
-
out, err := json.Marshal(map[string]any{
|
|
933
|
-
"continue": false,
|
|
934
|
-
"stopReason": reason,
|
|
935
|
-
"systemMessage": reason,
|
|
936
|
-
})
|
|
937
|
-
if err != nil {
|
|
938
|
-
debugf(stderr, "agent-dispatch: %v", err)
|
|
939
|
-
return exitOK
|
|
940
|
-
}
|
|
941
|
-
fmt.Fprintln(stdout, string(out))
|
|
942
|
-
return exitOK
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
func refreshPendingWrightBoundary(
|
|
946
|
-
root string,
|
|
947
|
-
in agentDispatchHookInput,
|
|
948
|
-
armed map[string]struct{},
|
|
949
|
-
attempts []*agentDispatchAttempt,
|
|
950
|
-
) error {
|
|
951
|
-
if in.AgentID != "" || in.AgentType != "" || currentReconcileWindowID() == "" {
|
|
952
|
-
return nil
|
|
953
|
-
}
|
|
954
|
-
for _, attempt := range attempts {
|
|
955
|
-
if attempt.Role == "devrites-slice-wright" {
|
|
956
|
-
return nil
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
boundaryArmed := make(map[string]struct{}, len(armed)+1)
|
|
960
|
-
for role := range armed {
|
|
961
|
-
boundaryArmed[role] = struct{}{}
|
|
962
|
-
}
|
|
963
|
-
boundaryArmed["devrites-slice-wright"] = struct{}{}
|
|
964
|
-
durable, err := durableCodexV2DispatchAttempts(root, in.SessionID, in.TurnID, boundaryArmed)
|
|
965
|
-
if err != nil {
|
|
966
|
-
return err
|
|
967
|
-
}
|
|
968
|
-
for _, attempt := range durable {
|
|
969
|
-
if attempt.Role == "devrites-slice-wright" {
|
|
970
|
-
return nil
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
workspaceRoot, slug, _, ok := resolveWorkspace()
|
|
974
|
-
if !ok || !sameResolvedPath(workspaceRoot, root) {
|
|
975
|
-
return fmt.Errorf("active reconcile window is unavailable")
|
|
976
|
-
}
|
|
977
|
-
return lib.CaptureReconcileWrightBoundary(root, slug)
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
func hookAgentDispatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
981
|
-
if h != harness.Codex {
|
|
982
|
-
_, _ = io.Copy(io.Discard, stdin)
|
|
983
|
-
return exitOK
|
|
984
|
-
}
|
|
985
|
-
in, err := parseAgentDispatchHookInput(stdin)
|
|
986
|
-
if err != nil {
|
|
987
|
-
return exitOK
|
|
988
|
-
}
|
|
989
|
-
root, ok := agentDispatchRoot()
|
|
990
|
-
if !ok || in.SessionID == "" || in.TurnID == "" {
|
|
991
|
-
return exitOK
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
switch in.HookEventName {
|
|
995
|
-
case "UserPromptSubmit":
|
|
996
|
-
roles, skillInvoked, rolesErr := requiredRolesInPrompt(root, in.Prompt)
|
|
997
|
-
if rolesErr != nil {
|
|
998
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
999
|
-
Event: "armed",
|
|
1000
|
-
TurnID: in.TurnID,
|
|
1001
|
-
Role: agentDispatchMetadataRole,
|
|
1002
|
-
}); err != nil {
|
|
1003
|
-
return stopDispatchBlock(h, "DevRites could not record the invalid agent-role contract: "+err.Error(), stdout, stderr)
|
|
1004
|
-
}
|
|
1005
|
-
return stopDispatchBlock(h, "DevRites could not read the required agent roles: "+rolesErr.Error(), stdout, stderr)
|
|
1006
|
-
}
|
|
1007
|
-
if skillInvoked {
|
|
1008
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1009
|
-
Event: "armed",
|
|
1010
|
-
TurnID: in.TurnID,
|
|
1011
|
-
Role: agentDispatchSkillGuard,
|
|
1012
|
-
}); err != nil {
|
|
1013
|
-
return stopDispatchBlock(h, "DevRites could not arm the skill dispatch guard: "+err.Error(), stdout, stderr)
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
for _, role := range roles {
|
|
1017
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1018
|
-
Event: "armed",
|
|
1019
|
-
TurnID: in.TurnID,
|
|
1020
|
-
Role: role,
|
|
1021
|
-
}); err != nil {
|
|
1022
|
-
return stopDispatchBlock(h, "DevRites could not arm the required agent dispatch: "+err.Error(), stdout, stderr)
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
events, stateErr := readAgentDispatchEvents(root, in.SessionID)
|
|
1026
|
-
if stateErr == nil {
|
|
1027
|
-
armed, attempts := dispatchTurnState(events, in.TurnID)
|
|
1028
|
-
if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
|
|
1029
|
-
debugf(stderr, "agent-dispatch pending wright boundary: %v", err)
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
if len(roles) > 0 {
|
|
1033
|
-
instructions := make([]string, 0, len(roles))
|
|
1034
|
-
for _, role := range roles {
|
|
1035
|
-
instructions = append(instructions, dispatchInstruction(role))
|
|
1036
|
-
}
|
|
1037
|
-
fmt.Fprintf(
|
|
1038
|
-
stdout,
|
|
1039
|
-
"MANDATORY DISPATCH THIS TURN — At the skill's dispatch step, execute every required child before finishing or claiming completion; a success phrase is not evidence.\n%s\n%s",
|
|
1040
|
-
strings.Join(instructions, "\n"),
|
|
1041
|
-
preDispatchStopInstruction(),
|
|
1042
|
-
)
|
|
1043
|
-
}
|
|
1044
|
-
if skillInvoked {
|
|
1045
|
-
if len(roles) > 0 {
|
|
1046
|
-
fmt.Fprintln(stdout)
|
|
1047
|
-
}
|
|
1048
|
-
fmt.Fprint(stdout, conditionalDispatchInstruction())
|
|
1049
|
-
}
|
|
1050
|
-
return exitOK
|
|
1051
|
-
|
|
1052
|
-
case "PreToolUse":
|
|
1053
|
-
return hookAgentDispatchPreTool(h, root, in, stdout, stderr)
|
|
1054
|
-
|
|
1055
|
-
case "PostToolUse":
|
|
1056
|
-
events, err := readAgentDispatchEvents(root, in.SessionID)
|
|
1057
|
-
if err != nil {
|
|
1058
|
-
debugf(stderr, "agent-dispatch post-tool state: %v", err)
|
|
1059
|
-
return exitOK
|
|
1060
|
-
}
|
|
1061
|
-
armed, attempts := dispatchTurnState(events, in.TurnID)
|
|
1062
|
-
if reason, ok := dispatchWaiverReason(agentDispatchCommand(in)); ok &&
|
|
1063
|
-
strings.Contains(in.ToolResponse, "dispatch-waive: accepted "+reason) {
|
|
1064
|
-
if err := recordDispatchWaiver(root, in, reason, armed, attempts); err != nil {
|
|
1065
|
-
debugf(stderr, "agent-dispatch post-tool waiver: %v", err)
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
if isWaitTool(in.ToolName) {
|
|
1069
|
-
if waitToolSucceeded(in.ToolResponse) {
|
|
1070
|
-
if err := recordCompletedWaits(root, in, attempts); err != nil {
|
|
1071
|
-
debugf(stderr, "agent-dispatch post-tool wait: %v", err)
|
|
1072
|
-
}
|
|
1073
|
-
} else if err := recordCancelledWaits(root, in, attempts); err != nil {
|
|
1074
|
-
debugf(stderr, "agent-dispatch post-tool wait cancellation: %v", err)
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
|
|
1078
|
-
debugf(stderr, "agent-dispatch post-tool wright boundary: %v", err)
|
|
1079
|
-
}
|
|
1080
|
-
return exitOK
|
|
1081
|
-
|
|
1082
|
-
case "SubagentStop":
|
|
1083
|
-
if in.AgentID == "" {
|
|
1084
|
-
return exitOK
|
|
1085
|
-
}
|
|
1086
|
-
attempt, found, lookupErr := boundDispatchAttempt(root, in.SessionID, in.AgentID)
|
|
1087
|
-
if lookupErr != nil {
|
|
1088
|
-
return stopDispatchBlock(h, "DevRites could not identify the stopped subagent: "+lookupErr.Error(), stdout, stderr)
|
|
1089
|
-
}
|
|
1090
|
-
if !found {
|
|
1091
|
-
return exitOK
|
|
1092
|
-
}
|
|
1093
|
-
resultSum := ""
|
|
1094
|
-
if strings.TrimSpace(in.LastAssistantMessage) != "" {
|
|
1095
|
-
sum := sha256.Sum256([]byte(in.LastAssistantMessage))
|
|
1096
|
-
resultSum = hex.EncodeToString(sum[:])
|
|
1097
|
-
}
|
|
1098
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1099
|
-
Event: "stopped",
|
|
1100
|
-
TurnID: attempt.TurnID,
|
|
1101
|
-
AgentID: in.AgentID,
|
|
1102
|
-
AgentType: attempt.AgentType,
|
|
1103
|
-
ResultSHA256: resultSum,
|
|
1104
|
-
}); err != nil {
|
|
1105
|
-
return stopDispatchBlock(h, "DevRites could not record the subagent result: "+err.Error(), stdout, stderr)
|
|
1106
|
-
}
|
|
1107
|
-
if active, activeErr := activeWaitIntent(root, in.SessionID, attempt.TurnID, in.AgentID, time.Now()); activeErr != nil {
|
|
1108
|
-
return stopDispatchBlock(h, "DevRites could not verify the awaited subagent result: "+activeErr.Error(), stdout, stderr)
|
|
1109
|
-
} else if active {
|
|
1110
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1111
|
-
Event: "waited",
|
|
1112
|
-
TurnID: attempt.TurnID,
|
|
1113
|
-
AgentID: in.AgentID,
|
|
1114
|
-
}); err != nil {
|
|
1115
|
-
return stopDispatchBlock(h, "DevRites could not record the awaited subagent result: "+err.Error(), stdout, stderr)
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
return exitOK
|
|
1119
|
-
|
|
1120
|
-
case "Stop":
|
|
1121
|
-
events, err := readAgentDispatchEvents(root, in.SessionID)
|
|
1122
|
-
if err != nil {
|
|
1123
|
-
reason := "DevRites could not verify the required agent dispatch: " + err.Error()
|
|
1124
|
-
if in.StopHookActive {
|
|
1125
|
-
return stopDispatchFailure(reason, stdout, stderr)
|
|
1126
|
-
}
|
|
1127
|
-
return stopDispatchBlock(h, reason, stdout, stderr)
|
|
1128
|
-
}
|
|
1129
|
-
armed, attempts := dispatchTurnState(events, in.TurnID)
|
|
1130
|
-
_, guarded := armed[agentDispatchSkillGuard]
|
|
1131
|
-
if reason := incompleteDispatchReason(armed, attempts); reason != "" || guarded && len(attempts) == 0 {
|
|
1132
|
-
durable, durableErr := durableCodexV2DispatchAttempts(
|
|
1133
|
-
root, in.SessionID, in.TurnID, armed,
|
|
1134
|
-
)
|
|
1135
|
-
if durableErr != nil {
|
|
1136
|
-
reason := "DevRites could not verify the Codex V2 agent dispatch: " + durableErr.Error()
|
|
1137
|
-
if in.StopHookActive {
|
|
1138
|
-
return stopDispatchFailure(reason, stdout, stderr)
|
|
1139
|
-
}
|
|
1140
|
-
return stopDispatchBlock(h, reason, stdout, stderr)
|
|
1141
|
-
}
|
|
1142
|
-
attempts = append(attempts, durable...)
|
|
1143
|
-
}
|
|
1144
|
-
if reason := incompleteDispatchReason(armed, attempts); reason != "" {
|
|
1145
|
-
if in.StopHookActive {
|
|
1146
|
-
return stopDispatchFailure(reason, stdout, stderr)
|
|
1147
|
-
}
|
|
1148
|
-
return stopDispatchBlock(h, reason, stdout, stderr)
|
|
1149
|
-
}
|
|
1150
|
-
return exitOK
|
|
1151
|
-
}
|
|
1152
|
-
return exitOK
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
func hookAgentDispatchPreTool(h harness.Harness, root string, in agentDispatchHookInput, stdout, stderr io.Writer) int {
|
|
1156
|
-
events, err := readAgentDispatchEvents(root, in.SessionID)
|
|
1157
|
-
if err != nil {
|
|
1158
|
-
return preToolDeny(h, "DevRites could not verify agent dispatch state: "+err.Error(), stdout, stderr)
|
|
1159
|
-
}
|
|
1160
|
-
armed, attempts := dispatchTurnState(events, in.TurnID)
|
|
1161
|
-
|
|
1162
|
-
command := agentDispatchCommand(in)
|
|
1163
|
-
if isShellTool(in.ToolName) && strings.Contains(command, "dispatch-waive") {
|
|
1164
|
-
reason, ok := dispatchWaiverReason(command)
|
|
1165
|
-
if !ok || !lib.ValidDispatchWaiver(reason) {
|
|
1166
|
-
return preToolDeny(h, "DevRites dispatch waiver must be one exact standalone engine command with an approved reason.", stdout, stderr)
|
|
1167
|
-
}
|
|
1168
|
-
if len(attempts) != 0 {
|
|
1169
|
-
return preToolDeny(h, "DevRites dispatch waiver is only valid before any specialist spawn attempt.", stdout, stderr)
|
|
1170
|
-
}
|
|
1171
|
-
required := 0
|
|
1172
|
-
for role := range armed {
|
|
1173
|
-
if strings.HasPrefix(role, "devrites-") && role != agentDispatchSkillGuard && role != agentDispatchMetadataRole {
|
|
1174
|
-
required++
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
if required == 0 {
|
|
1178
|
-
return preToolDeny(h, "DevRites dispatch waiver requires an armed mandatory role in this turn.", stdout, stderr)
|
|
1179
|
-
}
|
|
1180
|
-
return exitOK
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
if isAgentDispatchTool(in.ToolName) {
|
|
1184
|
-
agentType := in.ToolInput.AgentType
|
|
1185
|
-
if agentType == "" {
|
|
1186
|
-
roles := explicitRolesInText(in.ToolInput.Message + "\n" + in.ToolInput.TaskName)
|
|
1187
|
-
if len(roles) == 1 {
|
|
1188
|
-
return preToolDeny(h, "DevRites conditional dispatch requires the exact named specialist. "+dispatchInstruction(roles[0]), stdout, stderr)
|
|
1189
|
-
}
|
|
1190
|
-
if reason := incompleteDispatchReason(armed, attempts); reason != "" {
|
|
1191
|
-
return preToolDeny(h, reason, stdout, stderr)
|
|
1192
|
-
}
|
|
1193
|
-
if _, guarded := armed[agentDispatchSkillGuard]; guarded {
|
|
1194
|
-
return preToolDeny(h, conditionalDispatchInstruction(), stdout, stderr)
|
|
1195
|
-
}
|
|
1196
|
-
return exitOK
|
|
1197
|
-
}
|
|
1198
|
-
role := ""
|
|
1199
|
-
if strings.HasPrefix(agentType, "devrites-") {
|
|
1200
|
-
role = agentType
|
|
1201
|
-
} else {
|
|
1202
|
-
roles := explicitRolesInText(in.ToolInput.Message)
|
|
1203
|
-
if len(roles) == 1 {
|
|
1204
|
-
role = roles[0]
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
if role == "" {
|
|
1208
|
-
if agentType == "explorer" || agentType == "worker" {
|
|
1209
|
-
if reason := incompleteDispatchReason(armed, attempts); reason != "" {
|
|
1210
|
-
return preToolDeny(h, reason, stdout, stderr)
|
|
1211
|
-
}
|
|
1212
|
-
if _, guarded := armed[agentDispatchSkillGuard]; !guarded {
|
|
1213
|
-
return exitOK // Unrelated generic subagent in a DevRites repository.
|
|
1214
|
-
}
|
|
1215
|
-
return preToolDeny(h, "DevRites generic dispatch must name exactly one .codex/agents/devrites-<role>.toml contract.", stdout, stderr)
|
|
1216
|
-
}
|
|
1217
|
-
if reason := incompleteDispatchReason(armed, attempts); reason != "" {
|
|
1218
|
-
return preToolDeny(h, reason, stdout, stderr)
|
|
1219
|
-
}
|
|
1220
|
-
if _, guarded := armed[agentDispatchSkillGuard]; guarded {
|
|
1221
|
-
return preToolDeny(h, conditionalDispatchInstruction(), stdout, stderr)
|
|
1222
|
-
}
|
|
1223
|
-
return exitOK
|
|
1224
|
-
}
|
|
1225
|
-
instructions, contractErr := agentRoleDeveloperInstructions(root, role)
|
|
1226
|
-
if contractErr != nil {
|
|
1227
|
-
return preToolDeny(h, "DevRites role contract .codex/agents/"+role+".toml cannot be loaded: "+contractErr.Error()+".", stdout, stderr)
|
|
1228
|
-
}
|
|
1229
|
-
if in.ToolInput.ForkTurns != "none" {
|
|
1230
|
-
return preToolDeny(h, "DevRites agents require fork_turns=\"none\" so the child starts in the role contract rather than inheriting the root type.", stdout, stderr)
|
|
1231
|
-
}
|
|
1232
|
-
if agentType == "worker" && role != "devrites-slice-wright" {
|
|
1233
|
-
return preToolDeny(h, "Generic worker is reserved for devrites-slice-wright; use generic explorer for read-only DevRites roles.", stdout, stderr)
|
|
1234
|
-
}
|
|
1235
|
-
if agentType == "explorer" && role == "devrites-slice-wright" {
|
|
1236
|
-
return preToolDeny(h, "devrites-slice-wright requires the generic worker identity and exact wright allowlist.", stdout, stderr)
|
|
1237
|
-
}
|
|
1238
|
-
if agentType != "" && agentType != role && agentType != expectedAgentType(role) {
|
|
1239
|
-
return preToolDeny(h, fmt.Sprintf("DevRites role %s requires agent_type=%s or its exposed named role. %s", role, expectedAgentType(role), dispatchInstruction(role)), stdout, stderr)
|
|
1240
|
-
}
|
|
1241
|
-
if in.ToolUseID == "" {
|
|
1242
|
-
return preToolDeny(h, "DevRites cannot bind a spawn without tool_use_id.", stdout, stderr)
|
|
1243
|
-
}
|
|
1244
|
-
windowID := currentReconcileWindowID()
|
|
1245
|
-
if role == "devrites-slice-wright" && windowID == "" {
|
|
1246
|
-
return preToolDeny(h, "devrites-slice-wright requires an active reconcile snapshot before spawn_agent.", stdout, stderr)
|
|
1247
|
-
}
|
|
1248
|
-
if role == "devrites-slice-wright" {
|
|
1249
|
-
workspaceRoot, slug, _, ok := resolveWorkspace()
|
|
1250
|
-
if !ok || !sameResolvedPath(workspaceRoot, root) {
|
|
1251
|
-
return preToolDeny(h, "DevRites could not resolve the active wright reconcile window before spawn_agent.", stdout, stderr)
|
|
1252
|
-
}
|
|
1253
|
-
if err := lib.CaptureReconcileWrightBoundary(root, slug); err != nil {
|
|
1254
|
-
return preToolDeny(h, "DevRites could not capture the wright canonical boundary before spawn_agent: "+err.Error(), stdout, stderr)
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1258
|
-
Event: "pending",
|
|
1259
|
-
TurnID: in.TurnID,
|
|
1260
|
-
ToolUseID: in.ToolUseID,
|
|
1261
|
-
AgentType: agentType,
|
|
1262
|
-
Role: role,
|
|
1263
|
-
WindowID: windowID,
|
|
1264
|
-
}); err != nil {
|
|
1265
|
-
return preToolDeny(h, "DevRites could not record the spawn attempt: "+err.Error(), stdout, stderr)
|
|
1266
|
-
}
|
|
1267
|
-
if agentType != role {
|
|
1268
|
-
message := fmt.Sprintf(
|
|
1269
|
-
"DevRites bound developer_instructions for `%s`:\n\n%s\n\n## Unchanged parent packet\n%s",
|
|
1270
|
-
role, instructions, in.ToolInput.Message,
|
|
1271
|
-
)
|
|
1272
|
-
return preToolRewrite(h, in.ToolInputRaw, message, stdout, stderr)
|
|
1273
|
-
}
|
|
1274
|
-
return exitOK
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
switch toolBaseName(in.ToolName) {
|
|
1278
|
-
case "wait", "wait_agent":
|
|
1279
|
-
if len(armed) == 0 {
|
|
1280
|
-
return exitOK
|
|
1281
|
-
}
|
|
1282
|
-
var started []*agentDispatchAttempt
|
|
1283
|
-
for _, attempt := range attempts {
|
|
1284
|
-
if attempt.Started {
|
|
1285
|
-
started = append(started, attempt)
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
if len(started) == 0 {
|
|
1289
|
-
reason := incompleteDispatchReason(armed, attempts)
|
|
1290
|
-
if reason == "" {
|
|
1291
|
-
reason = conditionalDispatchInstruction()
|
|
1292
|
-
}
|
|
1293
|
-
return preToolDeny(h, reason, stdout, stderr)
|
|
1294
|
-
}
|
|
1295
|
-
targets := waitTargets(in)
|
|
1296
|
-
if len(targets) > 0 {
|
|
1297
|
-
known := map[string]*agentDispatchAttempt{}
|
|
1298
|
-
for _, attempt := range started {
|
|
1299
|
-
known[attempt.AgentID] = attempt
|
|
1300
|
-
}
|
|
1301
|
-
for _, agentID := range targets {
|
|
1302
|
-
if known[agentID] == nil {
|
|
1303
|
-
return preToolDeny(h, "DevRites wait target is not a confirmed child from this turn.", stdout, stderr)
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
if err := recordWaitIntents(root, in, started); err != nil {
|
|
1308
|
-
return preToolDeny(h, "DevRites could not record the wait intent: "+err.Error(), stdout, stderr)
|
|
1309
|
-
}
|
|
1310
|
-
return exitOK
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
if isShellTool(in.ToolName) && reconcileCloseRe.MatchString(command) {
|
|
1314
|
-
windowID := currentReconcileWindowID()
|
|
1315
|
-
if windowID == "" {
|
|
1316
|
-
return exitOK
|
|
1317
|
-
}
|
|
1318
|
-
if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
|
|
1319
|
-
durable, durableErr := durableCodexV2DispatchAttempts(
|
|
1320
|
-
root, in.SessionID, in.TurnID, armed,
|
|
1321
|
-
)
|
|
1322
|
-
if durableErr != nil {
|
|
1323
|
-
return preToolDeny(h, "DevRites could not verify the Codex V2 wright result: "+durableErr.Error(), stdout, stderr)
|
|
1324
|
-
}
|
|
1325
|
-
attempts = append(attempts, durable...)
|
|
1326
|
-
if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
|
|
1327
|
-
return preToolDeny(h, "DevRites reconcile close requires a confirmed, awaited devrites-slice-wright result bound to the active reconcile snapshot.", stdout, stderr)
|
|
1328
|
-
}
|
|
1329
|
-
for _, attempt := range durable {
|
|
1330
|
-
if attempt.Role != "devrites-slice-wright" || attempt.WindowID != windowID ||
|
|
1331
|
-
!dispatchAttemptComplete(attempt) {
|
|
1332
|
-
continue
|
|
1333
|
-
}
|
|
1334
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1335
|
-
Event: "verified",
|
|
1336
|
-
TurnID: in.TurnID,
|
|
1337
|
-
AgentID: attempt.AgentID,
|
|
1338
|
-
AgentType: attempt.AgentType,
|
|
1339
|
-
Role: attempt.Role,
|
|
1340
|
-
WindowID: attempt.WindowID,
|
|
1341
|
-
ResultSHA256: attempt.ResultSHA256,
|
|
1342
|
-
}); err != nil {
|
|
1343
|
-
return preToolDeny(h, "DevRites could not persist the verified Codex V2 wright result: "+err.Error(), stdout, stderr)
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
return exitOK
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
func isWaitTool(toolName string) bool {
|
|
1352
|
-
switch toolBaseName(toolName) {
|
|
1353
|
-
case "wait", "wait_agent":
|
|
1354
|
-
return true
|
|
1355
|
-
default:
|
|
1356
|
-
return false
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
func agentDispatchCommand(in agentDispatchHookInput) string {
|
|
1361
|
-
if in.ToolInput.Command != "" {
|
|
1362
|
-
return in.ToolInput.Command
|
|
1363
|
-
}
|
|
1364
|
-
return in.ToolInput.Cmd
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
func dispatchWaiverReason(command string) (string, bool) {
|
|
1368
|
-
match := dispatchWaiveRe.FindStringSubmatch(command)
|
|
1369
|
-
if len(match) != 2 {
|
|
1370
|
-
return "", false
|
|
1371
|
-
}
|
|
1372
|
-
return match[1], true
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
func recordDispatchWaiver(
|
|
1376
|
-
root string,
|
|
1377
|
-
in agentDispatchHookInput,
|
|
1378
|
-
reason string,
|
|
1379
|
-
armed map[string]struct{},
|
|
1380
|
-
attempts []*agentDispatchAttempt,
|
|
1381
|
-
) error {
|
|
1382
|
-
if len(attempts) != 0 || !lib.ValidDispatchWaiver(reason) {
|
|
1383
|
-
return fmt.Errorf("waiver is not valid after a spawn attempt")
|
|
1384
|
-
}
|
|
1385
|
-
recorded := 0
|
|
1386
|
-
for role := range armed {
|
|
1387
|
-
if !strings.HasPrefix(role, "devrites-") || role == agentDispatchSkillGuard || role == agentDispatchMetadataRole {
|
|
1388
|
-
continue
|
|
1389
|
-
}
|
|
1390
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1391
|
-
Event: "waived",
|
|
1392
|
-
TurnID: in.TurnID,
|
|
1393
|
-
Role: role,
|
|
1394
|
-
Reason: reason,
|
|
1395
|
-
}); err != nil {
|
|
1396
|
-
return err
|
|
1397
|
-
}
|
|
1398
|
-
recorded++
|
|
1399
|
-
}
|
|
1400
|
-
if recorded == 0 {
|
|
1401
|
-
return fmt.Errorf("no armed mandatory role to waive")
|
|
1402
|
-
}
|
|
1403
|
-
return nil
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
func waitTargets(in agentDispatchHookInput) []string {
|
|
1407
|
-
if len(in.ToolInput.ReceiverThreadIDs) > 0 {
|
|
1408
|
-
return in.ToolInput.ReceiverThreadIDs
|
|
1409
|
-
}
|
|
1410
|
-
if len(in.ToolInput.IDs) > 0 {
|
|
1411
|
-
return in.ToolInput.IDs
|
|
1412
|
-
}
|
|
1413
|
-
return in.ToolInput.AgentIDs
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
func waitToolSucceeded(response string) bool {
|
|
1417
|
-
response = strings.ToLower(strings.TrimSpace(response))
|
|
1418
|
-
if response == "" {
|
|
1419
|
-
return false
|
|
1420
|
-
}
|
|
1421
|
-
for _, failure := range []string{
|
|
1422
|
-
`"timed_out":true`,
|
|
1423
|
-
`"timed_out": true`,
|
|
1424
|
-
"timed out",
|
|
1425
|
-
"timeout summary",
|
|
1426
|
-
"no activity",
|
|
1427
|
-
} {
|
|
1428
|
-
if strings.Contains(response, failure) {
|
|
1429
|
-
return false
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
return true
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
func waitTargetIDs(in agentDispatchHookInput, attempts []*agentDispatchAttempt) []string {
|
|
1436
|
-
targets := waitTargets(in)
|
|
1437
|
-
if len(targets) > 0 {
|
|
1438
|
-
return targets
|
|
1439
|
-
}
|
|
1440
|
-
targets = make([]string, 0, len(attempts))
|
|
1441
|
-
for _, attempt := range attempts {
|
|
1442
|
-
if attempt.Started && attempt.AgentID != "" {
|
|
1443
|
-
targets = append(targets, attempt.AgentID)
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
return targets
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
func agentWaitTimeout(in agentDispatchHookInput) time.Duration {
|
|
1450
|
-
const defaultTimeout = 30 * time.Second
|
|
1451
|
-
const maxTimeout = time.Hour
|
|
1452
|
-
if in.ToolInput.TimeoutMS <= 0 {
|
|
1453
|
-
return defaultTimeout
|
|
1454
|
-
}
|
|
1455
|
-
timeout := time.Duration(in.ToolInput.TimeoutMS) * time.Millisecond
|
|
1456
|
-
if timeout > maxTimeout {
|
|
1457
|
-
return maxTimeout
|
|
1458
|
-
}
|
|
1459
|
-
return timeout
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
func recordWaitIntents(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
|
|
1463
|
-
timeoutMS := strconv.FormatInt(agentWaitTimeout(in).Milliseconds(), 10)
|
|
1464
|
-
for _, agentID := range waitTargetIDs(in, attempts) {
|
|
1465
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1466
|
-
Event: "waiting",
|
|
1467
|
-
TurnID: in.TurnID,
|
|
1468
|
-
AgentID: agentID,
|
|
1469
|
-
Reason: timeoutMS,
|
|
1470
|
-
}); err != nil {
|
|
1471
|
-
return err
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
return nil
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
func recordCancelledWaits(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
|
|
1478
|
-
for _, agentID := range waitTargetIDs(in, attempts) {
|
|
1479
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1480
|
-
Event: "wait-cancelled",
|
|
1481
|
-
TurnID: in.TurnID,
|
|
1482
|
-
AgentID: agentID,
|
|
1483
|
-
}); err != nil {
|
|
1484
|
-
return err
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
return nil
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
func activeWaitIntent(root, sessionID, turnID, agentID string, now time.Time) (bool, error) {
|
|
1491
|
-
events, err := readAgentDispatchEvents(root, sessionID)
|
|
1492
|
-
if err != nil {
|
|
1493
|
-
return false, err
|
|
1494
|
-
}
|
|
1495
|
-
for i := len(events) - 1; i >= 0; i-- {
|
|
1496
|
-
event := events[i]
|
|
1497
|
-
if event.TurnID != turnID || event.AgentID != agentID {
|
|
1498
|
-
continue
|
|
1499
|
-
}
|
|
1500
|
-
switch event.Event {
|
|
1501
|
-
case "waited", "wait-cancelled":
|
|
1502
|
-
return false, nil
|
|
1503
|
-
case "waiting":
|
|
1504
|
-
timeoutMS, err := strconv.ParseInt(event.Reason, 10, 64)
|
|
1505
|
-
if err != nil || timeoutMS <= 0 {
|
|
1506
|
-
return false, nil
|
|
1507
|
-
}
|
|
1508
|
-
deadline := time.Unix(0, event.AtUnixNano).Add(time.Duration(timeoutMS) * time.Millisecond)
|
|
1509
|
-
return !now.After(deadline), nil
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
return false, nil
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
func recordCompletedWaits(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
|
|
1516
|
-
targetSet := map[string]struct{}{}
|
|
1517
|
-
for _, agentID := range waitTargets(in) {
|
|
1518
|
-
targetSet[agentID] = struct{}{}
|
|
1519
|
-
}
|
|
1520
|
-
for _, attempt := range attempts {
|
|
1521
|
-
if !attempt.Started || attempt.Waited {
|
|
1522
|
-
continue
|
|
1523
|
-
}
|
|
1524
|
-
if len(targetSet) > 0 {
|
|
1525
|
-
if _, ok := targetSet[attempt.AgentID]; !ok {
|
|
1526
|
-
continue
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1530
|
-
Event: "waited",
|
|
1531
|
-
TurnID: in.TurnID,
|
|
1532
|
-
AgentID: attempt.AgentID,
|
|
1533
|
-
}); err != nil {
|
|
1534
|
-
return err
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
return nil
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
func bindAgentDispatchStart(root string, in agentDispatchHookInput) (string, bool, error) {
|
|
1541
|
-
events, err := readAgentDispatchEvents(root, in.SessionID)
|
|
1542
|
-
if err != nil {
|
|
1543
|
-
return "", false, err
|
|
1544
|
-
}
|
|
1545
|
-
started := map[string]struct{}{}
|
|
1546
|
-
for _, event := range events {
|
|
1547
|
-
if event.Event == "started" {
|
|
1548
|
-
if event.AgentID == in.AgentID {
|
|
1549
|
-
if event.AgentType != in.AgentType || event.Role == "" {
|
|
1550
|
-
return "", false, fmt.Errorf("subagent %s is already bound to a different dispatch", in.AgentID)
|
|
1551
|
-
}
|
|
1552
|
-
return event.Role, true, nil
|
|
1553
|
-
}
|
|
1554
|
-
started[event.ToolUseID] = struct{}{}
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
var candidates []agentDispatchEvent
|
|
1558
|
-
for i := len(events) - 1; i >= 0; i-- {
|
|
1559
|
-
event := events[i]
|
|
1560
|
-
if event.Event != "pending" || event.AgentType != in.AgentType {
|
|
1561
|
-
continue
|
|
1562
|
-
}
|
|
1563
|
-
if _, ok := started[event.ToolUseID]; ok {
|
|
1564
|
-
continue
|
|
1565
|
-
}
|
|
1566
|
-
if strings.HasPrefix(in.AgentType, "devrites-") && event.Role != in.AgentType {
|
|
1567
|
-
continue
|
|
1568
|
-
}
|
|
1569
|
-
candidates = append(candidates, event)
|
|
1570
|
-
}
|
|
1571
|
-
if len(candidates) == 0 {
|
|
1572
|
-
return bindDurableCodexV2Start(root, in, events)
|
|
1573
|
-
}
|
|
1574
|
-
if len(candidates) != 1 {
|
|
1575
|
-
return "", false, fmt.Errorf("ambiguous subagent start: %d pending %s dispatches lack a parent tool-use binding", len(candidates), in.AgentType)
|
|
1576
|
-
}
|
|
1577
|
-
event := candidates[0]
|
|
1578
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1579
|
-
Event: "started",
|
|
1580
|
-
TurnID: event.TurnID,
|
|
1581
|
-
ToolUseID: event.ToolUseID,
|
|
1582
|
-
AgentID: in.AgentID,
|
|
1583
|
-
AgentType: in.AgentType,
|
|
1584
|
-
Role: event.Role,
|
|
1585
|
-
WindowID: event.WindowID,
|
|
1586
|
-
}); err != nil {
|
|
1587
|
-
return "", false, err
|
|
1588
|
-
}
|
|
1589
|
-
return event.Role, true, nil
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
func bindDurableCodexV2Start(
|
|
1593
|
-
root string, in agentDispatchHookInput, events []agentDispatchEvent,
|
|
1594
|
-
) (string, bool, error) {
|
|
1595
|
-
if !devritesAgentNameRe.MatchString(in.AgentType) || in.AgentType == agentDispatchSkillGuard {
|
|
1596
|
-
return "", false, nil
|
|
1597
|
-
}
|
|
1598
|
-
parentPath, sessionsDir, err := findCodexParentRollout(in.SessionID)
|
|
1599
|
-
if err != nil || parentPath == "" {
|
|
1600
|
-
return "", false, err
|
|
1601
|
-
}
|
|
1602
|
-
projectRoot := filepath.Dir(root)
|
|
1603
|
-
childPath, childMeta, err := findCodexV2ChildRollout(
|
|
1604
|
-
sessionsDir, parentPath, projectRoot, in.SessionID, "", in.AgentID,
|
|
1605
|
-
)
|
|
1606
|
-
if err != nil || childPath == "" || childMeta.Payload.AgentRole != in.AgentType {
|
|
1607
|
-
return "", false, err
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
turns := map[string]struct{}{}
|
|
1611
|
-
for _, event := range events {
|
|
1612
|
-
if event.TurnID != "" {
|
|
1613
|
-
turns[event.TurnID] = struct{}{}
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
type binding struct {
|
|
1617
|
-
turnID, windowID string
|
|
1618
|
-
}
|
|
1619
|
-
var matches []binding
|
|
1620
|
-
for turnID := range turns {
|
|
1621
|
-
armed, _ := dispatchTurnState(events, turnID)
|
|
1622
|
-
_, required := armed[in.AgentType]
|
|
1623
|
-
_, guarded := armed[agentDispatchSkillGuard]
|
|
1624
|
-
if !required && !guarded {
|
|
1625
|
-
continue
|
|
1626
|
-
}
|
|
1627
|
-
spawns, _, err := readCodexV2ParentRollout(
|
|
1628
|
-
parentPath, projectRoot, in.SessionID, turnID, armed,
|
|
1629
|
-
)
|
|
1630
|
-
if err != nil {
|
|
1631
|
-
return "", false, err
|
|
1632
|
-
}
|
|
1633
|
-
spawn := spawns[childMeta.Payload.AgentPath]
|
|
1634
|
-
if spawn == nil || spawn.Role != in.AgentType {
|
|
1635
|
-
continue
|
|
1636
|
-
}
|
|
1637
|
-
windowID := ""
|
|
1638
|
-
if in.AgentType == "devrites-slice-wright" {
|
|
1639
|
-
windowID = currentReconcileWindowID()
|
|
1640
|
-
if windowID == "" || !reconcileWindowPredates(spawn.SpawnedAt) {
|
|
1641
|
-
continue
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
matches = append(matches, binding{turnID: turnID, windowID: windowID})
|
|
1645
|
-
}
|
|
1646
|
-
if len(matches) == 0 {
|
|
1647
|
-
return "", false, nil
|
|
1648
|
-
}
|
|
1649
|
-
if len(matches) != 1 {
|
|
1650
|
-
return "", false, fmt.Errorf(
|
|
1651
|
-
"ambiguous durable subagent start: %d parent spawns match %s",
|
|
1652
|
-
len(matches), in.AgentID,
|
|
1653
|
-
)
|
|
1654
|
-
}
|
|
1655
|
-
match := matches[0]
|
|
1656
|
-
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1657
|
-
Event: "started",
|
|
1658
|
-
TurnID: match.turnID,
|
|
1659
|
-
ToolUseID: "codex-v2:" + in.AgentID,
|
|
1660
|
-
AgentID: in.AgentID,
|
|
1661
|
-
AgentType: in.AgentType,
|
|
1662
|
-
Role: in.AgentType,
|
|
1663
|
-
WindowID: match.windowID,
|
|
1664
|
-
}); err != nil {
|
|
1665
|
-
return "", false, err
|
|
1666
|
-
}
|
|
1667
|
-
return in.AgentType, true, nil
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
type boundAgentDispatchAttempt struct {
|
|
1671
|
-
TurnID string
|
|
1672
|
-
AgentType string
|
|
1673
|
-
Role string
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
func boundDispatchAttempt(root, sessionID, agentID string) (boundAgentDispatchAttempt, bool, error) {
|
|
1677
|
-
events, err := readAgentDispatchEvents(root, sessionID)
|
|
1678
|
-
if err != nil {
|
|
1679
|
-
return boundAgentDispatchAttempt{}, false, err
|
|
1680
|
-
}
|
|
1681
|
-
for i := len(events) - 1; i >= 0; i-- {
|
|
1682
|
-
event := events[i]
|
|
1683
|
-
if event.Event == "started" && event.AgentID == agentID {
|
|
1684
|
-
return boundAgentDispatchAttempt{
|
|
1685
|
-
TurnID: event.TurnID,
|
|
1686
|
-
AgentType: event.AgentType,
|
|
1687
|
-
Role: event.Role,
|
|
1688
|
-
}, true, nil
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
return boundAgentDispatchAttempt{}, false, nil
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
type devritesAgentGuardBinding struct {
|
|
1695
|
-
Kind devritesAgentKind
|
|
1696
|
-
Role string
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
func devritesAgentBindingForGuard(h harness.Harness, in harness.GuardInput) devritesAgentGuardBinding {
|
|
1700
|
-
kind := devritesAgent(in.AgentType)
|
|
1701
|
-
binding := devritesAgentGuardBinding{Kind: kind, Role: declaredDevritesRole(in.AgentType)}
|
|
1702
|
-
generic := in.AgentType == "" || in.AgentType == "explorer" || in.AgentType == "worker"
|
|
1703
|
-
if h != harness.Codex || !generic ||
|
|
1704
|
-
os.Getenv("DEVRITES_CODEX_GENERIC_AGENT_COMPAT") != "1" ||
|
|
1705
|
-
in.SessionID == "" || in.AgentID == "" {
|
|
1706
|
-
return binding
|
|
1707
|
-
}
|
|
1708
|
-
root, ok := agentDispatchRoot()
|
|
1709
|
-
if !ok {
|
|
1710
|
-
return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
|
|
1711
|
-
}
|
|
1712
|
-
attempt, found, err := boundDispatchAttempt(root, in.SessionID, in.AgentID)
|
|
1713
|
-
if err != nil {
|
|
1714
|
-
return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
|
|
1715
|
-
}
|
|
1716
|
-
if !found {
|
|
1717
|
-
return binding
|
|
1718
|
-
}
|
|
1719
|
-
if attempt.Role == "devrites-slice-wright" {
|
|
1720
|
-
return devritesAgentGuardBinding{Kind: devritesAgentGenericWright, Role: attempt.Role}
|
|
1721
|
-
}
|
|
1722
|
-
if strings.HasPrefix(attempt.Role, "devrites-") {
|
|
1723
|
-
return devritesAgentGuardBinding{Kind: devritesAgentReadonly, Role: attempt.Role}
|
|
1724
|
-
}
|
|
1725
|
-
return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
|
|
1726
|
-
}
|