@khoavu882/doflow 2.0.0-beta.5 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -16
- package/bin/doflow.js +22 -12
- package/core/.antigravity-plugin/plugin.json +1 -2
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/settings/settings.json +26 -9
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/hooks.json +0 -10
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- package/core/harnesses/copilot/hooks/doflow-lib.sh +94 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +51 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -11
- package/core/harnesses/kiro/hooks/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +11 -2
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
- package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +16 -2
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +20 -7
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +58 -20
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- package/core/registry/contribution-points.json +35 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1445 -366
- package/core/registry/intake.json +53 -0
- package/core/registry/lifecycle.json +599 -996
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/intake-category/files/category.json +8 -0
- package/core/registry/package-templates/intake-category/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +272 -0
- package/core/registry/storage.json +687 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -6
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +3 -2
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +49 -5
- package/core/shared/scripts/doflow/bin/doflow-run +152 -34
- package/core/shared/skills/do/SKILL.md +14 -12
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +45 -97
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +48 -148
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +23 -20
- package/core/shared/skills/do-diagnose/SKILL.md +18 -6
- package/core/shared/skills/do-document/SKILL.md +12 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +19 -7
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +16 -2
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +10 -11
- package/core/shared/skills/do-flow/references/task_classes.md +5 -5
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +13 -14
- package/core/shared/skills/do-plan/SKILL.md +34 -20
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/shared/workflows/do-flow-chain.md +2 -0
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +143 -66
- package/src/adapters/claude/index.js +46 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +39 -3
- package/src/adapters/codex/index.js +61 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +291 -46
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +22 -3
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +337 -32
- package/src/cli/runtime-commands.js +220 -60
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +4 -16
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +472 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +20 -11
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/pointer.js +67 -12
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +158 -79
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-ledger.js +52 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +33 -3
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +624 -18
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +281 -26
- package/src/runtime/intake/engine.js +677 -0
- package/src/runtime/intake/index.js +136 -0
- package/src/runtime/intake/manifest.js +140 -0
- package/src/runtime/intake/taxonomy.js +206 -0
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +88 -10
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +38 -7
- package/src/runtime/lifecycle/overview.js +26 -2
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +2 -2
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +487 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/attendance.js +137 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +273 -0
- package/src/runtime/roadmap/loop.js +1719 -0
- package/src/runtime/roadmap/merge.js +390 -0
- package/src/runtime/roadmap/rows.js +1122 -0
- package/src/runtime/roadmap/seed.js +1063 -0
- package/src/runtime/roadmap/worktree-cleanup.js +147 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +60 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +1 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +85 -14
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +373 -64
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verb-view.js +267 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -54
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +256 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +610 -67
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -251
- package/src/adapters/gemini/index.js +0 -429
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Worker evidence records (IC-005, IC-006). CLI checks are declared by a reviewable checks file;
|
|
4
|
+
// live-run evidence copies only the bounded, non-content fields from an ended worker record.
|
|
5
|
+
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const { spawnSync } = require('node:child_process');
|
|
9
|
+
const readiness = require('./readiness.js');
|
|
10
|
+
const { PROBE_STDOUT_MAX } = readiness;
|
|
11
|
+
const { WORKER_CAPABILITIES, WORKER_OPTIONAL_CAPABILITIES } = require('../../registry/index.js');
|
|
12
|
+
const { maskBody } = require('../mask.js');
|
|
13
|
+
const { resultOfRun } = require('./supervisor.js');
|
|
14
|
+
|
|
15
|
+
const CHECKS_SCHEMA = 'doflow-worker-checks-v1';
|
|
16
|
+
const EVIDENCE_SCHEMA = 'doflow-worker-evidence-v1';
|
|
17
|
+
const MAX_ARGV = 32;
|
|
18
|
+
const MAX_ARG_CHARS = 200;
|
|
19
|
+
const MAX_MATCHED_LINES = 20;
|
|
20
|
+
const MATCHED_LINE_CHARS = 200;
|
|
21
|
+
|
|
22
|
+
class WorkerEvidenceError extends Error {
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
constructor(code , reason , message ) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'WorkerEvidenceError';
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function usage(reason , message ) {
|
|
34
|
+
throw new WorkerEvidenceError('usage', reason, message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function refuse(reason ) {
|
|
38
|
+
throw new WorkerEvidenceError('refused', reason, reason);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function argvProblem(argv ) {
|
|
42
|
+
if (!Array.isArray(argv) || argv.some((item) => typeof item !== 'string')) return 'argv must be an array of strings';
|
|
43
|
+
if (argv.length > MAX_ARGV) return `argv must have at most ${MAX_ARGV} elements`;
|
|
44
|
+
if (argv.some((item) => item.length > MAX_ARG_CHARS)) return `argv elements must be at most ${MAX_ARG_CHARS} characters`;
|
|
45
|
+
if (argv.some((item) => item.includes('\0'))) return 'argv elements must not contain NUL';
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* FR-006 / NFR-001: a check may only ask for the version, for help, or for the feature list; never a model session.
|
|
51
|
+
* Exact shapes only: `--version`; plain subcommand words then one final `--help`/`-h`; `features list` then
|
|
52
|
+
* `--disable <name>` pairs. Any `--` element or free-text token fails every shape, because `codex exec -- --help`
|
|
53
|
+
* reads `--help` as the prompt.
|
|
54
|
+
*/
|
|
55
|
+
function freeArgv(argv ) {
|
|
56
|
+
if (argv.length === 1 && argv[0] === '--version') return true;
|
|
57
|
+
const last = argv[argv.length - 1];
|
|
58
|
+
if ((last === '--help' || last === '-h') && argv.slice(0, -1).every((word) => /^[a-z][a-z-]*$/.test(word))) return true;
|
|
59
|
+
if (argv[0] !== 'features' || argv[1] !== 'list') return false;
|
|
60
|
+
const rest = argv.slice(2);
|
|
61
|
+
return rest.length % 2 === 0 && rest.every((token, index) => (index % 2 === 0 ? token === '--disable' : /^[a-z][a-z0-9_]*$/.test(token)));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function expression(value , where ) {
|
|
65
|
+
if (typeof value !== 'string' || value === '') usage('checks-invalid', `${where} must be a non-empty regular expression`);
|
|
66
|
+
try { return new RegExp(value); } catch (error ) {
|
|
67
|
+
usage('checks-invalid', `${where} is not a valid regular expression: ${error?.message || String(error)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validateChecks(data , harness , worker ) {
|
|
72
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) usage('checks-invalid', 'checks file must be a JSON object');
|
|
73
|
+
if (data.schema !== CHECKS_SCHEMA) usage('checks-invalid', `checks file schema must be '${CHECKS_SCHEMA}'`);
|
|
74
|
+
if (data.harness !== harness) usage('checks-invalid', `checks file harness '${data.harness}' does not match '${harness}'`);
|
|
75
|
+
if (!worker?.cli || worker.cli.status !== 'supported' || typeof worker.cli.binary !== 'string') {
|
|
76
|
+
usage('checks-invalid', `harness '${harness}' does not declare a supported CLI binary`);
|
|
77
|
+
}
|
|
78
|
+
if (data.binary !== worker.cli.binary) usage('checks-invalid', `checks file binary '${data.binary}' does not match harness '${harness}' binary '${worker.cli.binary}'`);
|
|
79
|
+
if (!data.version || typeof data.version !== 'object' || Array.isArray(data.version)) usage('checks-invalid', 'checks file needs a version check');
|
|
80
|
+
const versionArgvProblem = argvProblem(data.version.argv);
|
|
81
|
+
if (versionArgvProblem) usage('checks-invalid', `version.argv ${versionArgvProblem}`);
|
|
82
|
+
if (!freeArgv(data.version.argv)) usage('checks-invalid', `version.argv is not a free check (only --version, a subcommand path ending in --help/-h, or 'features list' with --disable pairs may run)`);
|
|
83
|
+
expression(data.version.expect, 'version.expect');
|
|
84
|
+
if (!Array.isArray(data.checks) || data.checks.length === 0) usage('checks-invalid', 'checks file needs at least one check');
|
|
85
|
+
|
|
86
|
+
const capabilities = new Set([
|
|
87
|
+
...Object.keys(WORKER_CAPABILITIES),
|
|
88
|
+
...Object.keys(WORKER_OPTIONAL_CAPABILITIES),
|
|
89
|
+
]);
|
|
90
|
+
capabilities.delete('cli');
|
|
91
|
+
for (const [index, check] of data.checks.entries()) {
|
|
92
|
+
const where = `checks[${index}]`;
|
|
93
|
+
if (!check || typeof check !== 'object' || Array.isArray(check)) usage('checks-invalid', `${where} must be an object`);
|
|
94
|
+
if (typeof check.id !== 'string' || check.id === '') usage('checks-invalid', `${where}.id must be a non-empty string`);
|
|
95
|
+
const problem = argvProblem(check.argv);
|
|
96
|
+
if (problem) usage('checks-invalid', `${where}.argv ${problem}`);
|
|
97
|
+
if (!freeArgv(check.argv)) {
|
|
98
|
+
usage('checks-invalid', `check '${check.id}' (${where}) is not a free check: its argv must be exactly --version, subcommand words ending in --help or -h, or 'features list' with --disable <name> pairs`);
|
|
99
|
+
}
|
|
100
|
+
if (!Array.isArray(check.expect) || check.expect.length === 0) usage('checks-invalid', `${where}.expect must be a non-empty array`);
|
|
101
|
+
check.expect.forEach((pattern , expectIndex ) => expression(pattern, `${where}.expect[${expectIndex}]`));
|
|
102
|
+
if (!Array.isArray(check.establishes) || check.establishes.length === 0
|
|
103
|
+
|| check.establishes.some((key ) => typeof key !== 'string' || !capabilities.has(key))) {
|
|
104
|
+
usage('checks-invalid', `${where}.establishes must name worker capabilities other than 'cli'`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return data;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function readChecks(file , fsImpl ) {
|
|
111
|
+
let data ;
|
|
112
|
+
try {
|
|
113
|
+
if (!fsImpl.statSync(file).isFile()) usage('checks-invalid', `checks file '${file}' is not a regular file`);
|
|
114
|
+
data = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
|
|
115
|
+
} catch (error ) {
|
|
116
|
+
if (error instanceof WorkerEvidenceError) throw error;
|
|
117
|
+
usage('checks-invalid', `checks file '${file}' cannot be read: ${error?.code || error?.message || String(error)}`);
|
|
118
|
+
}
|
|
119
|
+
return data;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function lines(stdout ) {
|
|
123
|
+
const value = Buffer.isBuffer(stdout) ? stdout.toString('utf8') : String(stdout ?? '');
|
|
124
|
+
return value.split('\n').map((line ) => line.replace(/\r$/, ''));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function runBounded(binary , argv , { env, cwd, spawnSyncImpl } ) {
|
|
128
|
+
try {
|
|
129
|
+
return spawnSyncImpl(binary, argv, {
|
|
130
|
+
env,
|
|
131
|
+
cwd,
|
|
132
|
+
shell: false,
|
|
133
|
+
timeout: readiness.PROBE_TIMEOUT_MS,
|
|
134
|
+
maxBuffer: PROBE_STDOUT_MAX,
|
|
135
|
+
encoding: 'buffer',
|
|
136
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
137
|
+
windowsHide: true,
|
|
138
|
+
});
|
|
139
|
+
} catch (error ) {
|
|
140
|
+
return { status: null, stdout: Buffer.alloc(0), stderr: Buffer.alloc(0), error };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function matchedLines(outputLines , patterns ) {
|
|
145
|
+
const matches = [];
|
|
146
|
+
for (const line of outputLines) {
|
|
147
|
+
if (!patterns.some((pattern) => pattern.test(line))) continue;
|
|
148
|
+
const safe = maskBody(line).text.slice(0, MATCHED_LINE_CHARS);
|
|
149
|
+
matches.push(safe);
|
|
150
|
+
if (matches.length >= MAX_MATCHED_LINES) break;
|
|
151
|
+
}
|
|
152
|
+
return matches;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function sorted(value ) {
|
|
156
|
+
if (Array.isArray(value)) return value.map(sorted);
|
|
157
|
+
if (value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {
|
|
158
|
+
const out = {};
|
|
159
|
+
for (const key of Object.keys(value).sort()) out[key] = sorted(value[key]);
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function outPathAndCheck(out , cwd , fsImpl ) {
|
|
166
|
+
if (typeof out !== 'string' || out === '') usage('out-required', '--out is required');
|
|
167
|
+
const target = path.resolve(cwd, out);
|
|
168
|
+
try {
|
|
169
|
+
if (fsImpl.existsSync(target)) usage('out-exists', `--out '${target}' already exists`);
|
|
170
|
+
} catch (error ) {
|
|
171
|
+
if (error instanceof WorkerEvidenceError) throw error;
|
|
172
|
+
usage('out-unavailable', `--out '${target}' cannot be checked: ${error?.message || String(error)}`);
|
|
173
|
+
}
|
|
174
|
+
return target;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function writeRecord(record , target , fsImpl ) {
|
|
178
|
+
try {
|
|
179
|
+
fsImpl.mkdirSync(path.dirname(target), { recursive: true });
|
|
180
|
+
fsImpl.writeFileSync(target, `${JSON.stringify(sorted(record), null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
|
|
181
|
+
} catch (error ) {
|
|
182
|
+
if (error?.code === 'EEXIST') usage('out-exists', `--out '${target}' already exists`);
|
|
183
|
+
usage('out-write-failed', `evidence file '${target}' could not be written: ${error?.code || error?.message || String(error)}`);
|
|
184
|
+
}
|
|
185
|
+
return { disposition: 'recorded', path: target, establishes: record.establishes };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function dateValue(now ) {
|
|
189
|
+
const value = typeof now === 'function' ? now() : now;
|
|
190
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
191
|
+
if (!Number.isFinite(date.getTime())) usage('clock-invalid', 'recordedAt clock is invalid');
|
|
192
|
+
return date.toISOString();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Runs a declared free-check file against the named harness binary and writes an IC-005 record. */
|
|
196
|
+
function recordCliChecks({ checksFile, harness, worker, out, cwd = process.cwd(), env = process.env,
|
|
197
|
+
now = () => new Date(), platform = process.platform, fsImpl = fs, spawnSyncImpl = spawnSync } = {}) {
|
|
198
|
+
const target = outPathAndCheck(out, cwd, fsImpl);
|
|
199
|
+
const checks = validateChecks(readChecks(path.resolve(cwd, String(checksFile)), fsImpl), harness, worker);
|
|
200
|
+
const binaryPath = readiness.findOnPath(checks.binary, env, platform);
|
|
201
|
+
if (!binaryPath) refuse('version-unmatched');
|
|
202
|
+
|
|
203
|
+
const versionExpect = expression(checks.version.expect, 'version.expect');
|
|
204
|
+
const versionResult = runBounded(binaryPath, checks.version.argv, { env, cwd, spawnSyncImpl });
|
|
205
|
+
const versionLines = lines(`${versionResult.stdout ?? ''}\n${versionResult.stderr ?? ''}`);
|
|
206
|
+
const versionMatch = versionLines.find((line) => versionExpect.test(line));
|
|
207
|
+
if (versionResult.status !== 0 || !versionMatch) refuse('version-unmatched');
|
|
208
|
+
|
|
209
|
+
const cliVersion = maskBody(versionMatch).text.slice(0, MATCHED_LINE_CHARS);
|
|
210
|
+
const recordedChecks = checks.checks.map((check ) => {
|
|
211
|
+
const patterns = check.expect.map((pattern , index ) => expression(pattern, `check '${check.id}' expect[${index}]`));
|
|
212
|
+
const result = runBounded(binaryPath, check.argv, { env, cwd, spawnSyncImpl });
|
|
213
|
+
const outputLines = lines(`${result.stdout ?? ''}\n${result.stderr ?? ''}`);
|
|
214
|
+
const allExpectationsMatched = patterns.every((pattern ) => outputLines.some((line) => pattern.test(line)));
|
|
215
|
+
return {
|
|
216
|
+
id: check.id,
|
|
217
|
+
argv: [...check.argv],
|
|
218
|
+
exit: Number.isInteger(result.status) ? result.status : null,
|
|
219
|
+
expect: [...check.expect],
|
|
220
|
+
matched: matchedLines(outputLines, patterns),
|
|
221
|
+
result: result.status === 0 && allExpectationsMatched ? 'pass' : 'fail',
|
|
222
|
+
establishes: [...check.establishes],
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
const establishes = [...new Set(recordedChecks
|
|
226
|
+
.filter((check ) => check.result === 'pass')
|
|
227
|
+
.flatMap((check ) => check.establishes))].sort();
|
|
228
|
+
const record = {
|
|
229
|
+
schema: EVIDENCE_SCHEMA,
|
|
230
|
+
harness,
|
|
231
|
+
kind: 'cli-check',
|
|
232
|
+
recordedAt: dateValue(now),
|
|
233
|
+
binary: checks.binary,
|
|
234
|
+
cliVersion,
|
|
235
|
+
establishes,
|
|
236
|
+
checks: recordedChecks,
|
|
237
|
+
run: null,
|
|
238
|
+
};
|
|
239
|
+
return writeRecord(record, target, fsImpl);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Records an ended run without retaining prompt, environment, output or summary contents. */
|
|
243
|
+
function recordLiveRun({ run, harness, worker, out, cwd = process.cwd(), now = () => new Date(), fsImpl = fs } = {}) {
|
|
244
|
+
const target = outPathAndCheck(out, cwd, fsImpl);
|
|
245
|
+
if (!run) refuse('unknown-run');
|
|
246
|
+
if (run.phase !== 'ended') refuse('run-not-ended');
|
|
247
|
+
if (run.harness !== harness) refuse('harness-mismatch');
|
|
248
|
+
const stdoutBytes = Number(run.output?.stdout?.bytes);
|
|
249
|
+
if (!Number.isFinite(stdoutBytes) || stdoutBytes <= 0) refuse('no-output');
|
|
250
|
+
if (!run.report || !['success', 'failure', 'denied'].includes(run.report.status)) refuse('no-terminal-event');
|
|
251
|
+
|
|
252
|
+
const { resultOffered, withheld } = resultOfRun(run);
|
|
253
|
+
const edit = Boolean(run.edit);
|
|
254
|
+
const establishes = resultOffered
|
|
255
|
+
? ['oneShot', 'structuredOutput', 'spawnDenial', edit ? 'isolatedEdit' : 'readOnly']
|
|
256
|
+
: [];
|
|
257
|
+
const record = {
|
|
258
|
+
schema: EVIDENCE_SCHEMA,
|
|
259
|
+
harness,
|
|
260
|
+
kind: 'live-run',
|
|
261
|
+
recordedAt: dateValue(now),
|
|
262
|
+
binary: run.recipe?.binary ?? run.descriptor?.cli?.binary ?? worker?.cli?.binary ?? null,
|
|
263
|
+
cliVersion: typeof run.cliVersion === 'string' ? run.cliVersion : '',
|
|
264
|
+
establishes,
|
|
265
|
+
checks: [],
|
|
266
|
+
run: {
|
|
267
|
+
runId: run.runId,
|
|
268
|
+
edit,
|
|
269
|
+
argvShape: run.recipe?.argvShape ?? [],
|
|
270
|
+
parser: run.recipe?.parser ?? run.report?.parser ?? null,
|
|
271
|
+
report: {
|
|
272
|
+
status: run.report.status,
|
|
273
|
+
reason: run.report.reason ?? null,
|
|
274
|
+
deniedCount: run.report.deniedCount ?? 0,
|
|
275
|
+
usage: run.report.usage ?? null,
|
|
276
|
+
},
|
|
277
|
+
isolation: edit ? run.isolation ?? null : null,
|
|
278
|
+
readOnly: edit ? null : run.readOnly ?? null,
|
|
279
|
+
resultOffered,
|
|
280
|
+
withheld,
|
|
281
|
+
stdoutBytes,
|
|
282
|
+
exit: Number.isInteger(run.process?.code) ? run.process.code : null,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
return writeRecord(record, target, fsImpl);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = { recordCliChecks, recordLiveRun, WorkerEvidenceError, CHECKS_SCHEMA, EVIDENCE_SCHEMA };
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Feature 081 B.3 (IC-003, IC-012; DEC-030, DEC-037, DEC-045): the real launcher.
|
|
4
|
+
//
|
|
5
|
+
// `launchEnv` names the worker in the caller's environment: the caller's variables are inherited
|
|
6
|
+
// unchanged (DEC-045), except for the five identity variables and DOFLOW_WORKER_RUN. The run segment of
|
|
7
|
+
// the claim's run key is the worker's run, or, when that segment is the default `local`, the run
|
|
8
|
+
// id without its `w-` prefix (DEC-037), so the worker always resolves to a launched identity and
|
|
9
|
+
// its 078 unit `L.<run>.<lineage>.<token>` is unique to this run. A child run (082 IC-015) is
|
|
10
|
+
// always run-scoped, so its unit differs from its granter's.
|
|
11
|
+
//
|
|
12
|
+
// `assembleArgv` builds the IC-003 argv: `readOnly`, `spawnDenial`, `structuredOutput` and
|
|
13
|
+
// `oneShot` argv in that order (082 IC-014: an edit run puts `isolatedEdit` first in place of
|
|
14
|
+
// `readOnly`). 120 IC-011 inserts `model` second only for a supported binding. The `{prompt}`,
|
|
15
|
+
// `{lastMessageFile}` and `{model}` elements are replaced whole, never inside a longer string.
|
|
16
|
+
// `argvShape` writes the prompt as `<prompt sha256:<hex> bytes:<n>>`, the output path as
|
|
17
|
+
// `<last-message-file>` and the model as the original `{model}` placeholder.
|
|
18
|
+
//
|
|
19
|
+
// `launchWorker` runs IC-012 steps 1 to 5: the environment, the optional 078 grant, the launch
|
|
20
|
+
// emission in process, the emission text before the brief, then `spawn` with no shell. It returns a
|
|
21
|
+
// promise that resolves once the process has spawned and rejects on any throw or a spawn `error`
|
|
22
|
+
// (for example ENOENT); the supervisor ends such a run `not-started`. No variable's value is stored,
|
|
23
|
+
// and the emission text is never returned for storage.
|
|
24
|
+
|
|
25
|
+
const crypto = require('node:crypto');
|
|
26
|
+
const { spawn } = require('node:child_process');
|
|
27
|
+
const { emitContext, utf8Bytes } = require('../context/arbitrate.js');
|
|
28
|
+
const { grantSummary } = require('../context/summary-store.js');
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/** 120 IC-011 / IC-012: the binding shape consumed by the recipe assembler. */
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/** IC-012: the names a launch may set, in the order the run record lists them. The first five are the
|
|
35
|
+
* identity contract; `DOFLOW_WORKER_RUN` (110) carries the worker run id `w-<12 hex>` and nothing else,
|
|
36
|
+
* so the worker's telemetry can name its own run. It is not part of the stored identity. */
|
|
37
|
+
const WORKER_RUN_VAR = 'DOFLOW_WORKER_RUN';
|
|
38
|
+
const ENV_SET = Object.freeze(['DOFLOW_RUN_ID', 'DOFLOW_LINEAGE_ID', 'DOFLOW_ATTEMPT', 'DOFLOW_SESSION_ROLE', 'DOFLOW_WORKTREE', WORKER_RUN_VAR]);
|
|
39
|
+
const PROMPT_ELEMENT = '{prompt}';
|
|
40
|
+
const LAST_MESSAGE_FILE_ELEMENT = '{lastMessageFile}';
|
|
41
|
+
const MODEL_ELEMENT = '{model}';
|
|
42
|
+
/** IC-003 prompt limits in bytes. argv is one under Linux MAX_ARG_STRLEN, which counts the NUL (DEC-062). */
|
|
43
|
+
const PROMPT_LIMITS = Object.freeze({ argv: 131071, stdin: 1048576 });
|
|
44
|
+
const DEFAULT_RUN = 'local';
|
|
45
|
+
const RUN_ID = /^w-[0-9a-f]{12}$/;
|
|
46
|
+
/** IC-003 / 120 IC-011: order of the run recipe's argv; model is conditional on its binding. */
|
|
47
|
+
const RECIPE_ORDER = Object.freeze(['readOnly', 'model', 'spawnDenial', 'structuredOutput', 'oneShot']);
|
|
48
|
+
/** 082 IC-014 / 120 IC-011: the order of an edit run's recipe argv. */
|
|
49
|
+
const EDIT_RECIPE_ORDER = Object.freeze(['isolatedEdit', 'model', 'spawnDenial', 'structuredOutput', 'oneShot']);
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/** A caller error, not a refusal: exit 2 `usage` with `reason`. */
|
|
59
|
+
class LaunchUsageError extends Error {
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
constructor(reason , message ) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = 'LaunchUsageError';
|
|
65
|
+
this.code = reason;
|
|
66
|
+
this.reason = reason;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The five identity variables and the optional worker run id. The run id replaces (or removes) the
|
|
71
|
+
* caller's value; the caller's environment is never mutated. */
|
|
72
|
+
function withIdentity(env , identity , execWorktree , runId ) {
|
|
73
|
+
if (runId !== undefined && !RUN_ID.test(runId)) throw new LaunchUsageError('run-id-malformed', `launch: run id must match ${RUN_ID}: ${runId}`);
|
|
74
|
+
const out = {
|
|
75
|
+
...env,
|
|
76
|
+
DOFLOW_RUN_ID: identity.run,
|
|
77
|
+
DOFLOW_LINEAGE_ID: identity.lineage,
|
|
78
|
+
DOFLOW_ATTEMPT: identity.attempt,
|
|
79
|
+
DOFLOW_SESSION_ROLE: 'worker',
|
|
80
|
+
DOFLOW_WORKTREE: execWorktree,
|
|
81
|
+
};
|
|
82
|
+
if (runId !== undefined) out[WORKER_RUN_VAR] = runId;
|
|
83
|
+
else delete out[WORKER_RUN_VAR];
|
|
84
|
+
// 120 IC-003: the primary declaration names the person's main session, never a worker.
|
|
85
|
+
delete out.DOFLOW_PRIMARY_HARNESS;
|
|
86
|
+
delete out.DOFLOW_PRIMARY_MODEL;
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** IC-012: the worker environment and identity from the claim's run key `<hash>/<run>/<lineage>`.
|
|
91
|
+
* `runScoped` (082 IC-015, a child run) takes the run id as the run whatever the claim's run is. */
|
|
92
|
+
function launchEnv({ env, runKey, token, execWorktree, runId, runScoped = false } ) {
|
|
93
|
+
const segments = String(runKey).split('/');
|
|
94
|
+
if (segments.length !== 3 || segments.some((s) => s === '')) throw new LaunchUsageError('run-key-malformed', `launchEnv: run key must be <hash>/<run>/<lineage>: ${runKey}`);
|
|
95
|
+
const [, claimRun, lineage] = segments;
|
|
96
|
+
let identity ;
|
|
97
|
+
if (claimRun === DEFAULT_RUN || runScoped) {
|
|
98
|
+
if (!RUN_ID.test(runId)) throw new LaunchUsageError('run-id-malformed', `launchEnv: run id must match ${RUN_ID}: ${runId}`);
|
|
99
|
+
identity = { run: runId.slice(2), lineage, attempt: String(token), runSource: 'run-scoped' };
|
|
100
|
+
} else {
|
|
101
|
+
identity = { run: claimRun, lineage, attempt: String(token), runSource: 'claim' };
|
|
102
|
+
}
|
|
103
|
+
return { env: withIdentity(env, identity, execWorktree, runId), identity };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 120 IC-011: only a supported binding supplies a non-empty model element. */
|
|
107
|
+
function boundModel(binding ) {
|
|
108
|
+
if (binding?.flag !== 'supported') return null;
|
|
109
|
+
if (typeof binding.model !== 'string' || binding.model.length === 0) {
|
|
110
|
+
throw new LaunchUsageError('model-invalid', 'assembleArgv: a supported model binding requires a non-empty model string');
|
|
111
|
+
}
|
|
112
|
+
return binding.model;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** IC-003 / 120 IC-011: replace prompt, output path and bound model only as whole elements. */
|
|
116
|
+
function assemble(recipe , element , edit = false, lastMessageFile , binding ) {
|
|
117
|
+
const model = boundModel(binding);
|
|
118
|
+
const argv = [];
|
|
119
|
+
for (const key of edit ? EDIT_RECIPE_ORDER : RECIPE_ORDER) {
|
|
120
|
+
if (key === 'model' && model === null) continue;
|
|
121
|
+
const entry = recipe?.[key];
|
|
122
|
+
if (!entry || entry.status !== 'supported' || !Array.isArray(entry.argv)) {
|
|
123
|
+
throw new LaunchUsageError('recipe-incomplete', `assembleArgv: worker '${key}' is not supported with an argv`);
|
|
124
|
+
}
|
|
125
|
+
argv.push(...entry.argv);
|
|
126
|
+
}
|
|
127
|
+
return argv.map((value) => {
|
|
128
|
+
if (value === LAST_MESSAGE_FILE_ELEMENT) {
|
|
129
|
+
if (lastMessageFile === undefined) {
|
|
130
|
+
throw new LaunchUsageError('last-message-file-missing', "assembleArgv: worker recipe needs a '{lastMessageFile}' output path");
|
|
131
|
+
}
|
|
132
|
+
return lastMessageFile;
|
|
133
|
+
}
|
|
134
|
+
if (value === MODEL_ELEMENT && model !== null) return model;
|
|
135
|
+
if (value === PROMPT_ELEMENT && recipe.oneShot.promptVia === 'argv') return element;
|
|
136
|
+
return value;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function promptVia(recipe ) {
|
|
141
|
+
return recipe?.oneShot?.promptVia === 'stdin' ? 'stdin' : 'argv';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** IC-003: the worker argv for `prompt`; a prompt over its mode's limit is `prompt-too-large`.
|
|
145
|
+
* `edit` selects the edit recipe order (082 IC-014); 120 IC-011 optionally inserts the bound model. */
|
|
146
|
+
function assembleArgv(recipe , prompt , edit = false, lastMessageFile , binding ) {
|
|
147
|
+
const mode = promptVia(recipe);
|
|
148
|
+
const bytes = Buffer.byteLength(prompt, 'utf8');
|
|
149
|
+
if (bytes > PROMPT_LIMITS[mode]) {
|
|
150
|
+
throw new LaunchUsageError('prompt-too-large', `assembleArgv: the prompt is ${bytes} bytes; ${mode} mode allows ${PROMPT_LIMITS[mode]}`);
|
|
151
|
+
}
|
|
152
|
+
return assemble(recipe, prompt, edit, lastMessageFile, binding);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** IC-003 / 120 IC-011: the stored shape hashes the prompt and preserves the model placeholder. */
|
|
156
|
+
function argvShape(recipe , prompt , edit = false, binding ) {
|
|
157
|
+
const hex = crypto.createHash('sha256').update(prompt, 'utf8').digest('hex');
|
|
158
|
+
const shapeBinding = boundModel(binding) === null
|
|
159
|
+
? binding : { model: MODEL_ELEMENT, flag: 'supported' };
|
|
160
|
+
return assemble(recipe, `<prompt sha256:${hex} bytes:${Buffer.byteLength(prompt, 'utf8')}>`, edit, '<last-message-file>', shapeBinding);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** IC-012 / data-model §1: the launch fields a run record stores. Names only, never a value. */
|
|
164
|
+
function launchRecord(recipe , identity , prompt , edit = false, binding ) {
|
|
165
|
+
return {
|
|
166
|
+
identity: { run: identity.run, lineage: identity.lineage, attempt: identity.attempt, runSource: identity.runSource },
|
|
167
|
+
recipe: {
|
|
168
|
+
binary: recipe.cli.binary,
|
|
169
|
+
argvShape: argvShape(recipe, prompt, edit, binding),
|
|
170
|
+
promptVia: promptVia(recipe),
|
|
171
|
+
parser: recipe.structuredOutput.parser,
|
|
172
|
+
profile: recipe.profile ?? null,
|
|
173
|
+
},
|
|
174
|
+
envSet: [...ENV_SET],
|
|
175
|
+
envInherited: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
/** IC-012 steps 1 to 5. Resolves `{ child, env, launched: { reason, bytes } }` once the process has spawned. */
|
|
198
|
+
function launchWorker({ recipe, prompt, identity, execWorktree, env, taskId, grant, edit = false, lastMessageFile, runId, binding } ) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const workerEnv = withIdentity(env, identity, execWorktree, runId);
|
|
201
|
+
if (grant) grantSummary({ ...grant, toRun: identity.run, toLineage: identity.lineage, env });
|
|
202
|
+
const emitted = emitContext({ event: 'launch', cwd: execWorktree, env: workerEnv, ...(taskId ? { taskId } : {}) });
|
|
203
|
+
const reason = emitted.decision.reason;
|
|
204
|
+
// DEC-052: any returned text is delivered, a refused emission's included (it carries the pack and the pointer).
|
|
205
|
+
const delivered = typeof emitted.text === 'string' ? emitted.text : null;
|
|
206
|
+
const text = delivered !== null ? `${delivered}\n\n${prompt}` : prompt;
|
|
207
|
+
const launched = { reason, bytes: delivered !== null ? utf8Bytes(delivered) : 0 };
|
|
208
|
+
const argv = assembleArgv(recipe, text, edit, lastMessageFile, binding);
|
|
209
|
+
const viaStdin = promptVia(recipe) === 'stdin';
|
|
210
|
+
if (recipe?.structuredOutput?.parser) {
|
|
211
|
+
const { PARSERS } = require('./parsers.js');
|
|
212
|
+
if (!Object.hasOwn(PARSERS, recipe.structuredOutput.parser)) {
|
|
213
|
+
throw new LaunchUsageError('parser-unknown', `launchWorker: parser '${recipe.structuredOutput.parser}' is not in core PARSERS`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let file = recipe.cli?.binary;
|
|
217
|
+
let spawnArgs = argv;
|
|
218
|
+
let spawnOptions = {
|
|
219
|
+
cwd: execWorktree,
|
|
220
|
+
env: workerEnv,
|
|
221
|
+
detached: true,
|
|
222
|
+
shell: false,
|
|
223
|
+
stdio: [viaStdin ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
224
|
+
};
|
|
225
|
+
if (recipe.program && recipe.storeEntry) {
|
|
226
|
+
const { launchSpec } = require('../packages/programs.js');
|
|
227
|
+
const spec = launchSpec({
|
|
228
|
+
program: recipe.program,
|
|
229
|
+
storeEntry: recipe.storeEntry,
|
|
230
|
+
recipeArgv: argv,
|
|
231
|
+
cwd: execWorktree,
|
|
232
|
+
env: workerEnv,
|
|
233
|
+
});
|
|
234
|
+
file = spec.file;
|
|
235
|
+
spawnArgs = spec.args;
|
|
236
|
+
spawnOptions = {
|
|
237
|
+
...spec.options,
|
|
238
|
+
detached: true,
|
|
239
|
+
stdio: [viaStdin ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const child = spawn(file, spawnArgs, spawnOptions);
|
|
243
|
+
// Kept attached: a reject after the resolve is a no-op, and an early error never goes unhandled.
|
|
244
|
+
child.on('error', reject);
|
|
245
|
+
child.once('spawn', () => {
|
|
246
|
+
if (viaStdin) {
|
|
247
|
+
// A worker that exits before reading closes the pipe; its exit is what the supervisor records.
|
|
248
|
+
child.stdin.on('error', () => {});
|
|
249
|
+
child.stdin.end(text);
|
|
250
|
+
}
|
|
251
|
+
resolve({ child, env: workerEnv, launched });
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
module.exports = { ENV_SET, PROMPT_LIMITS, RECIPE_ORDER, EDIT_RECIPE_ORDER, LaunchUsageError, argvShape, assembleArgv, launchEnv, launchRecord, launchWorker };
|