@iowarp/clio-coder 0.3.0 → 0.3.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/CHANGELOG.md +33 -0
- package/README.md +50 -53
- package/dist/acp-FPR54DGL.js +138 -0
- package/dist/{agents-Q4MYPMUW.js → agents-OGPIHPJH.js} +33 -25
- package/dist/assets/codewiki.json +1 -0
- package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/LICENSE +21 -0
- package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/cgmanifest.json +16 -0
- package/dist/assets/grammars/notices/tree-sitter-wasms/LICENSE +24 -0
- package/dist/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-c.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-cpp.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-go.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-java.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-python.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/assets/grammars/tree-sitter.wasm +0 -0
- package/dist/{auth-O6HYIJ6J.js → auth-IC3K6NIZ.js} +22 -15
- package/dist/{chunk-KKKPTZLM.js → chunk-2STGEDFV.js} +8 -1
- package/dist/chunk-3I7MS7N2.js +40 -0
- package/dist/chunk-3R73A4XB.js +69 -0
- package/dist/{chunk-26BZQOAD.js → chunk-474KN5II.js} +86 -115
- package/dist/{chunk-NI4DEJMC.js → chunk-4QKXUHSR.js} +26 -3
- package/dist/{chunk-X3BR7HWV.js → chunk-5WIGXA4T.js} +7 -1
- package/dist/{chunk-TFSSEXL6.js → chunk-673JJUWJ.js} +14 -4
- package/dist/{chunk-GNGDQYDU.js → chunk-7SS2CTV2.js} +27922 -1249
- package/dist/{chunk-7RBKDI66.js → chunk-ASND7OZK.js} +104 -12
- package/dist/{chunk-X3NE4WVW.js → chunk-BNAZZHFG.js} +7 -1
- package/dist/{chunk-RQDWMVRB.js → chunk-BSU2YIWB.js} +9 -3
- package/dist/{chunk-CLJ5HLUD.js → chunk-C4PTHK7P.js} +24 -5
- package/dist/{chunk-QV47YRF4.js → chunk-CGKSTWHD.js} +7 -1
- package/dist/{chunk-BYEU76JP.js → chunk-DKGKUHFA.js} +34 -9
- package/dist/chunk-EYOKLTMF.js +170 -0
- package/dist/{chunk-PS4PFJQP.js → chunk-FCIH3BIZ.js} +15258 -14130
- package/dist/{chunk-2J63S4SF.js → chunk-FST4FYJB.js} +82 -14
- package/dist/{chunk-3DANZDGR.js → chunk-G34LV2PF.js} +34 -9
- package/dist/{chunk-BBUH4VAA.js → chunk-GAEBEQVI.js} +34 -21
- package/dist/chunk-GB6QRBXN.js +8543 -0
- package/dist/chunk-GEP36Y4X.js +12796 -0
- package/dist/{chunk-TKHQ4DGZ.js → chunk-H6F6BYOH.js} +496 -121
- package/dist/{chunk-UGYAX5YI.js → chunk-HQQID6OA.js} +10 -5
- package/dist/chunk-IHXBNWMM.js +137 -0
- package/dist/{chunk-ME6DNWIU.js → chunk-IS3ONKU3.js} +7 -1
- package/dist/{chunk-DXQNI4PC.js → chunk-IWHMRKLL.js} +8 -1
- package/dist/chunk-K2ITRMHZ.js +642 -0
- package/dist/{chunk-KBR4MZZR.js → chunk-K5XEMXTI.js} +7 -1
- package/dist/{chunk-AMFR5YA3.js → chunk-KKNLWXI6.js} +24 -10
- package/dist/{chunk-JWHCJDCI.js → chunk-KY56HMHH.js} +145 -61
- package/dist/{chunk-E3NYWENM.js → chunk-LU4TK2PR.js} +52 -7
- package/dist/{chunk-UWIJNAOB.js → chunk-M5T5VO65.js} +109 -42
- package/dist/{chunk-JPOWPFCU.js → chunk-MAR7Y6HW.js} +38 -16
- package/dist/chunk-N6F52NLF.js +6951 -0
- package/dist/{chunk-UHTSULZS.js → chunk-NMOX6HFD.js} +20 -7
- package/dist/{chunk-WD6AJM35.js → chunk-OLBBMFRD.js} +30 -13
- package/dist/{chunk-VOO7NYPP.js → chunk-PFEFKVGL.js} +22 -6
- package/dist/{chunk-6CWDTGUC.js → chunk-PIUMUEMV.js} +7 -1
- package/dist/chunk-PV4JUBVJ.js +462 -0
- package/dist/{chunk-527KG6XR.js → chunk-Q3RUPKEJ.js} +22 -4
- package/dist/{chunk-D5YD55AR.js → chunk-Q5WJOSJ7.js} +11 -1
- package/dist/{chunk-5N2FG33Q.js → chunk-R346GLFC.js} +7 -1
- package/dist/{chunk-4UQA7NCT.js → chunk-R5KLMSBV.js} +7 -2
- package/dist/{chunk-PIDUD6M2.js → chunk-RDLVBZEO.js} +11 -6
- package/dist/{chunk-O4EJEDHO.js → chunk-RPTR2H26.js} +40 -31
- package/dist/{chunk-HBU5MTAM.js → chunk-SVNXH4L2.js} +7 -1
- package/dist/{chunk-XNISANGE.js → chunk-TEKV33Q5.js} +122 -57
- package/dist/chunk-TXOTCRLG.js +15 -0
- package/dist/{chunk-VPAWTYLY.js → chunk-WEPFGWHJ.js} +7 -1
- package/dist/{chunk-HMYNFFY4.js → chunk-XPLRXC72.js} +7 -2
- package/dist/{chunk-UU3R62TT.js → chunk-XYWBQRDM.js} +13 -4
- package/dist/{chunk-7BHLZB3A.js → chunk-ZPY3JZ5E.js} +32 -10
- package/dist/cli/index.js +39 -33
- package/dist/clio-BZVGEUFJ.js +58 -0
- package/dist/{components-JVHMUBEB.js → components-DMAOEKFB.js} +26 -9
- package/dist/{config-ZFCDBMDC.js → config-IRUQ7SE4.js} +42 -31
- package/dist/configure-S7S6F6CL.js +32 -0
- package/dist/{context-CDXTP2MP.js → context-3KWFLHJG.js} +17 -11
- package/dist/{context-E3KIFVXI.js → context-5RADCKTR.js} +33 -28
- package/dist/{context-clear-3F4PLXOS.js → context-clear-7TSNPAAI.js} +36 -28
- package/dist/{context-index-Q7YSYTR3.js → context-index-W4RLWOQH.js} +10 -4
- package/dist/{docs-YIETIWZI.js → docs-5AWSPS37.js} +16 -10
- package/dist/doctor-UC5NAJYQ.js +77 -0
- package/dist/{eval-B3KZZESM.js → eval-U6TJHRLX.js} +77 -24
- package/dist/{evidence-V67CHM35.js → evidence-YEGUW4L3.js} +34 -26
- package/dist/{evolve-YDZSUQYA.js → evolve-TXARCTPG.js} +39 -26
- package/dist/{extensions-SRG7XCAH.js → extensions-OZFJ3A3G.js} +13 -7
- package/dist/{fleet-CA2CRTVG.js → fleet-6G3DHNYE.js} +45 -37
- package/dist/fleet-preflight-DSNT37JK.js +23 -0
- package/dist/{init-2OZDJE2D.js → init-KZ5QTF6M.js} +42 -34
- package/dist/{memory-3PIQQAKX.js → memory-73ESV5YC.js} +35 -27
- package/dist/{models-DY35XI7Y.js → models-A4PVNWJK.js} +34 -23
- package/dist/{paths-5OMXW7Z4.js → paths-C4H6IV77.js} +12 -6
- package/dist/preload-6WVMHX3A.js +13 -0
- package/dist/{reset-PIFYNOS3.js → reset-BGW6OGMV.js} +17 -11
- package/dist/{run-3VSPP24F.js → run-YTPEYQOH.js} +60 -48
- package/dist/{share-D36RQCXM.js → share-YIFFV4NQ.js} +16 -10
- package/dist/{skills-F2MRLELY.js → skills-2V6RA3OQ.js} +18 -11
- package/dist/{skills-eval-E2ZTW4PL.js → skills-eval-S2TVJO4F.js} +45 -36
- package/dist/{targets-DZMEZAH4.js → targets-TYXLPB23.js} +61 -59
- package/dist/{trace-7NYCUI2J.js → trace-GGOJ6Q6Z.js} +9 -3
- package/dist/{uninstall-AD3JWHBB.js → uninstall-LLLT4F4W.js} +14 -8
- package/dist/{upgrade-WYYBKGDY.js → upgrade-33G2LMM5.js} +45 -23
- package/dist/{usage-ULIDAGFF.js → usage-ZAFSXKKG.js} +35 -27
- package/dist/version-KGEG7EPO.js +21 -0
- package/dist/{wiki-generate-PKFIX6OB.js → wiki-generate-NUQCVOQ3.js} +55 -45
- package/dist/worker/entry.js +101 -43
- package/docs/README.md +12 -9
- package/docs/acp.md +28 -5
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +23 -2
- package/docs/artifact-placement.md +101 -0
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +4 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +125 -41
- package/docs/configuration-and-targets.md +88 -18
- package/docs/context-engine.md +6 -2
- package/docs/development-pipeline.md +77 -0
- package/docs/documentation-coverage.md +4 -4
- package/docs/documentation-guide.md +3 -3
- package/docs/environment-variables.md +2 -1
- package/docs/eval-runner.md +3 -3
- package/docs/evals-internal.md +2 -2
- package/docs/evidence-and-memory.md +6 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +18 -11
- package/docs/fleet-demo-runbook.md +22 -23
- package/docs/fleet-dispatch.md +111 -12
- package/docs/glossary.md +107 -15
- package/docs/html/agents_blueprint.html +3 -3
- package/docs/html/alcf_blueprint.html +3 -3
- package/docs/html/architecture_blueprint.html +3 -3
- package/docs/html/commands_blueprint.html +187 -42
- package/docs/html/configuration_blueprint.html +7 -7
- package/docs/html/context_blueprint.html +3 -3
- package/docs/html/documentation_blueprint.html +1 -1
- package/docs/html/environment_blueprint.html +3 -3
- package/docs/html/eval_blueprint.html +4 -4
- package/docs/html/evals_internal_blueprint.html +3 -3
- package/docs/html/evolution_blueprint.html +3 -3
- package/docs/html/extensions_blueprint.html +3 -3
- package/docs/html/fleet_dispatch_blueprint.html +8 -8
- package/docs/html/index.html +5 -5
- package/docs/html/lifecycle_blueprint.html +4 -4
- package/docs/html/memory_blueprint.html +3 -3
- package/docs/html/middleware_blueprint.html +3 -3
- package/docs/html/models_blueprint.html +2 -2
- package/docs/html/observability_blueprint.html +2 -2
- package/docs/html/provider_adapter_blueprint.html +3 -3
- package/docs/html/safety_blueprint.html +3 -3
- package/docs/html/skills_blueprint.html +2 -2
- package/docs/html/soak_blueprint.html +3 -3
- package/docs/html/tool_usage_blueprint.html +4 -4
- package/docs/html/tools_blueprint.html +2 -2
- package/docs/html/trace_blueprint.html +3 -3
- package/docs/html/tui_design_blueprint.html +100 -40
- package/docs/html/validation_blueprint.html +1 -1
- package/docs/html/worker_dispatch_blueprint.html +3 -3
- package/docs/installation-and-lifecycle.md +62 -11
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +2 -2
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +12 -12
- package/docs/prompt-envelope-and-tools.md +2 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +4 -0
- package/docs/safety-model.md +21 -5
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +2 -2
- package/docs/skills-marketplace.md +1 -1
- package/docs/time-conventions.md +101 -0
- package/docs/tool-usage.md +5 -4
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +83 -27
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +16 -16
- package/skills/README.md +5 -6
- package/skills/git/file-ticket/SKILL.md +107 -0
- package/skills/git/file-ticket/evals.md +54 -0
- package/skills/git/fix-issue/SKILL.md +106 -0
- package/skills/git/fix-issue/evals.md +71 -0
- package/skills/git/ship/SKILL.md +92 -0
- package/skills/git/ship/evals.md +72 -0
- package/skills/skill-marketplace.json +12 -20
- package/src/cli/acp.ts +88 -0
- package/src/cli/agents.ts +60 -0
- package/src/cli/args.ts +280 -0
- package/src/cli/argv.ts +170 -0
- package/src/cli/ask.ts +34 -0
- package/src/cli/auth.ts +468 -0
- package/src/cli/bootstrap-generate.ts +498 -0
- package/src/cli/clio.ts +34 -0
- package/src/cli/components.ts +195 -0
- package/src/cli/config-inspect.ts +330 -0
- package/src/cli/config.ts +72 -0
- package/src/cli/configure-interop.ts +67 -0
- package/src/cli/configure-layout.ts +172 -0
- package/src/cli/configure.ts +1487 -0
- package/src/cli/context-clear.ts +76 -0
- package/src/cli/context-index.ts +102 -0
- package/src/cli/context.ts +316 -0
- package/src/cli/default-target.ts +63 -0
- package/src/cli/docs.ts +340 -0
- package/src/cli/doctor.ts +54 -0
- package/src/cli/eval.ts +315 -0
- package/src/cli/evidence.ts +222 -0
- package/src/cli/evolve.ts +219 -0
- package/src/cli/extensions.ts +205 -0
- package/src/cli/fleet.ts +827 -0
- package/src/cli/index.ts +309 -0
- package/src/cli/init.ts +219 -0
- package/src/cli/initial-message.ts +47 -0
- package/src/cli/internal-dispatch.ts +55 -0
- package/src/cli/memory.ts +180 -0
- package/src/cli/models.ts +257 -0
- package/src/cli/modes/json-stream.ts +85 -0
- package/src/cli/modes/jsonl.ts +9 -0
- package/src/cli/modes/print.ts +589 -0
- package/src/cli/oauth-manual-input.ts +72 -0
- package/src/cli/oauth-select.ts +36 -0
- package/src/cli/output-guard.ts +48 -0
- package/src/cli/paths.ts +44 -0
- package/src/cli/provider-target.ts +113 -0
- package/src/cli/removal.ts +72 -0
- package/src/cli/reset.ts +253 -0
- package/src/cli/run.ts +497 -0
- package/src/cli/share.ts +243 -0
- package/src/cli/shared.ts +60 -0
- package/src/cli/skills-eval.ts +1225 -0
- package/src/cli/skills.ts +470 -0
- package/src/cli/steer-channel.ts +86 -0
- package/src/cli/targets.ts +1150 -0
- package/src/cli/text-layout.ts +102 -0
- package/src/cli/trace.ts +274 -0
- package/src/cli/uninstall.ts +405 -0
- package/src/cli/upgrade.ts +270 -0
- package/src/cli/usage.ts +869 -0
- package/src/cli/validate-model.ts +28 -0
- package/src/cli/version.ts +7 -0
- package/src/cli/wiki-generate.ts +509 -0
- package/src/core/artifact-paths.ts +43 -0
- package/src/core/bash-exec.ts +242 -0
- package/src/core/boot-trace.ts +37 -0
- package/src/core/bus-events.ts +728 -0
- package/src/core/bus-trace.ts +53 -0
- package/src/core/c-header-language.ts +265 -0
- package/src/core/clio-repo.ts +78 -0
- package/src/core/config.ts +1593 -0
- package/src/core/context-floor.ts +33 -0
- package/src/core/defaults.ts +625 -0
- package/src/core/domain-loader.ts +170 -0
- package/src/core/event-bus.ts +94 -0
- package/src/core/file-references.ts +282 -0
- package/src/core/guardrails.ts +185 -0
- package/src/core/headless-permission.ts +21 -0
- package/src/core/init.ts +223 -0
- package/src/core/json-payload.ts +58 -0
- package/src/core/package-root.ts +54 -0
- package/src/core/path-canonical.ts +37 -0
- package/src/core/process-identity.ts +41 -0
- package/src/core/recent-models.ts +91 -0
- package/src/core/release.ts +2 -0
- package/src/core/residency-protection.ts +56 -0
- package/src/core/resolve-config-value.ts +88 -0
- package/src/core/response-schema.ts +279 -0
- package/src/core/run-overrides.ts +77 -0
- package/src/core/safe-exec.ts +193 -0
- package/src/core/safe-id.ts +37 -0
- package/src/core/safe-resource-write.ts +162 -0
- package/src/core/session-routing.ts +339 -0
- package/src/core/settings-layers.ts +220 -0
- package/src/core/shared-bus.ts +8 -0
- package/src/core/shell-quote.ts +26 -0
- package/src/core/skill-activation.ts +190 -0
- package/src/core/startup-timer.ts +37 -0
- package/src/core/state-file-lock.ts +207 -0
- package/src/core/termination.ts +202 -0
- package/src/core/timers.ts +38 -0
- package/src/core/tool-names.ts +65 -0
- package/src/core/verification-scripts.ts +13 -0
- package/src/core/workspace-files.ts +492 -0
- package/src/core/xdg.ts +177 -0
- package/src/domains/agents/builtins/architect.md +4 -4
- package/src/domains/agents/builtins/coder.md +3 -2
- package/src/domains/agents/builtins/debugger.md +1 -1
- package/src/domains/agents/builtins/documenter.md +1 -1
- package/src/domains/agents/builtins/git-master.md +3 -3
- package/src/domains/agents/builtins/provenance.md +1 -1
- package/src/domains/agents/builtins/researcher.md +1 -1
- package/src/domains/agents/builtins/scout.md +1 -1
- package/src/domains/agents/builtins/tester.md +1 -1
- package/src/domains/agents/builtins/verifier.md +1 -1
- package/src/domains/agents/builtins/wiki-writer.md +1 -1
- package/src/domains/agents/catalog.ts +172 -0
- package/src/domains/agents/contract.ts +16 -0
- package/src/domains/agents/extension.ts +110 -0
- package/src/domains/agents/fleet-commands.ts +154 -0
- package/src/domains/agents/fleet-contract.ts +797 -0
- package/src/domains/agents/frontmatter.ts +40 -0
- package/src/domains/agents/index.ts +102 -0
- package/src/domains/agents/manifest.ts +6 -0
- package/src/domains/agents/recipe-schema.ts +202 -0
- package/src/domains/agents/recipe.ts +125 -0
- package/src/domains/agents/registry.ts +111 -0
- package/src/domains/agents/result-contract.ts +1265 -0
- package/src/domains/agents/spec.ts +371 -0
- package/src/domains/agents/write-boundary.ts +109 -0
- package/src/domains/components/diff.ts +99 -0
- package/src/domains/components/hash.ts +21 -0
- package/src/domains/components/index.ts +3 -0
- package/src/domains/components/scan.ts +304 -0
- package/src/domains/components/snapshot.ts +108 -0
- package/src/domains/components/types.ts +82 -0
- package/src/domains/config/agent-namespace.ts +13 -0
- package/src/domains/config/classify.ts +94 -0
- package/src/domains/config/contract.ts +22 -0
- package/src/domains/config/extension.ts +136 -0
- package/src/domains/config/index.ts +13 -0
- package/src/domains/config/keybindings.ts +164 -0
- package/src/domains/config/manifest.ts +6 -0
- package/src/domains/config/watcher.ts +41 -0
- package/src/domains/context/adoption.ts +880 -0
- package/src/domains/context/bootstrap-prompt.ts +273 -0
- package/src/domains/context/bootstrap.ts +1409 -0
- package/src/domains/context/clear.ts +86 -0
- package/src/domains/context/clio-md.ts +327 -0
- package/src/domains/context/codewiki/cmake.ts +356 -0
- package/src/domains/context/codewiki/cooperative.ts +46 -0
- package/src/domains/context/codewiki/digest.ts +135 -0
- package/src/domains/context/codewiki/grammar-assets.ts +33 -0
- package/src/domains/context/codewiki/indexer.ts +1614 -0
- package/src/domains/context/codewiki/tree-sitter.ts +771 -0
- package/src/domains/context/contract.ts +62 -0
- package/src/domains/context/excluded-dirs.ts +1 -0
- package/src/domains/context/extension.ts +407 -0
- package/src/domains/context/fingerprint.ts +196 -0
- package/src/domains/context/index.ts +164 -0
- package/src/domains/context/init-options.ts +69 -0
- package/src/domains/context/manifest.ts +6 -0
- package/src/domains/context/operator-profile.ts +157 -0
- package/src/domains/context/project-metadata.ts +510 -0
- package/src/domains/context/project-rules.ts +227 -0
- package/src/domains/context/prompt-context.ts +62 -0
- package/src/domains/context/refresh.ts +170 -0
- package/src/domains/context/sibling-files.ts +10 -0
- package/src/domains/context/state.ts +237 -0
- package/src/domains/context/wiki/assemble.ts +303 -0
- package/src/domains/context/wiki/frontmatter.ts +210 -0
- package/src/domains/context/wiki/generate.ts +590 -0
- package/src/domains/context/wiki/layout.ts +111 -0
- package/src/domains/context/wiki/meta.ts +221 -0
- package/src/domains/context/wiki/plan-store.ts +217 -0
- package/src/domains/context/wiki/plan.ts +250 -0
- package/src/domains/context/wiki/prompts.ts +270 -0
- package/src/domains/context/wiki/staleness.ts +198 -0
- package/src/domains/dispatch/active-route-planner.ts +168 -0
- package/src/domains/dispatch/admission-queue.ts +150 -0
- package/src/domains/dispatch/admission.ts +334 -0
- package/src/domains/dispatch/agent-candidates.ts +307 -0
- package/src/domains/dispatch/agent-ledger-hub.ts +71 -0
- package/src/domains/dispatch/agent-ledger-store.ts +257 -0
- package/src/domains/dispatch/agent-ledger.ts +212 -0
- package/src/domains/dispatch/agent-plan-adapter.ts +85 -0
- package/src/domains/dispatch/assignment-events.ts +132 -0
- package/src/domains/dispatch/assignment-reconcile.ts +76 -0
- package/src/domains/dispatch/assignment-store.ts +162 -0
- package/src/domains/dispatch/assignment.ts +278 -0
- package/src/domains/dispatch/backoff.ts +59 -0
- package/src/domains/dispatch/batch-store.ts +134 -0
- package/src/domains/dispatch/batch-tracker.ts +66 -0
- package/src/domains/dispatch/capability-match.ts +155 -0
- package/src/domains/dispatch/capacity-lease.ts +413 -0
- package/src/domains/dispatch/code-step-store.ts +52 -0
- package/src/domains/dispatch/code-step.ts +368 -0
- package/src/domains/dispatch/contract.ts +278 -0
- package/src/domains/dispatch/dedup.ts +133 -0
- package/src/domains/dispatch/event-pump.ts +251 -0
- package/src/domains/dispatch/execution-handoff.ts +29 -0
- package/src/domains/dispatch/execution-plan.ts +328 -0
- package/src/domains/dispatch/execution-role.ts +277 -0
- package/src/domains/dispatch/execution-scheduler.ts +583 -0
- package/src/domains/dispatch/extension.ts +5869 -0
- package/src/domains/dispatch/failure-classification.ts +141 -0
- package/src/domains/dispatch/fleet-plan.ts +225 -0
- package/src/domains/dispatch/fleet-preflight.ts +394 -0
- package/src/domains/dispatch/gate-decisions.ts +1040 -0
- package/src/domains/dispatch/gate-role-prompts.ts +58 -0
- package/src/domains/dispatch/heartbeat.ts +27 -0
- package/src/domains/dispatch/index.ts +137 -0
- package/src/domains/dispatch/joint-route-adapter.ts +114 -0
- package/src/domains/dispatch/joint-route-resolver.ts +283 -0
- package/src/domains/dispatch/manifest.ts +6 -0
- package/src/domains/dispatch/orphan-recovery.ts +224 -0
- package/src/domains/dispatch/outcome.ts +109 -0
- package/src/domains/dispatch/phase-timing.ts +47 -0
- package/src/domains/dispatch/placement.ts +202 -0
- package/src/domains/dispatch/receipt-findings.ts +255 -0
- package/src/domains/dispatch/receipt-integrity.ts +378 -0
- package/src/domains/dispatch/recovery-candidates.ts +74 -0
- package/src/domains/dispatch/reproducibility.ts +48 -0
- package/src/domains/dispatch/reservation-store.ts +501 -0
- package/src/domains/dispatch/route-approval.ts +177 -0
- package/src/domains/dispatch/route-candidates.ts +57 -0
- package/src/domains/dispatch/route-decision.ts +1079 -0
- package/src/domains/dispatch/route-facts.ts +187 -0
- package/src/domains/dispatch/route-history.ts +238 -0
- package/src/domains/dispatch/route-observer.ts +488 -0
- package/src/domains/dispatch/route-policy.ts +327 -0
- package/src/domains/dispatch/route-quality.ts +256 -0
- package/src/domains/dispatch/route-readiness.ts +120 -0
- package/src/domains/dispatch/routing-intent.ts +287 -0
- package/src/domains/dispatch/run-identity.ts +56 -0
- package/src/domains/dispatch/scout-transition.ts +130 -0
- package/src/domains/dispatch/state.ts +277 -0
- package/src/domains/dispatch/tool-stats.ts +132 -0
- package/src/domains/dispatch/transport.ts +209 -0
- package/src/domains/dispatch/types.ts +785 -0
- package/src/domains/dispatch/validation-grounding.ts +154 -0
- package/src/domains/dispatch/validation.ts +721 -0
- package/src/domains/dispatch/worker-protocol.ts +275 -0
- package/src/domains/dispatch/worker-spawn.ts +566 -0
- package/src/domains/dispatch/write-boundary-enforcer.ts +97 -0
- package/src/domains/dispatch/write-boundary.ts +426 -0
- package/src/domains/eval/artifacts/redact.ts +42 -0
- package/src/domains/eval/artifacts/store.ts +173 -0
- package/src/domains/eval/compare/compare.ts +33 -0
- package/src/domains/eval/compare/gates.ts +66 -0
- package/src/domains/eval/compare/thresholds.ts +92 -0
- package/src/domains/eval/harness-metrics.ts +62 -0
- package/src/domains/eval/index.ts +66 -0
- package/src/domains/eval/metrics/chaos-stream.ts +93 -0
- package/src/domains/eval/metrics/context.ts +34 -0
- package/src/domains/eval/metrics/coverage.ts +21 -0
- package/src/domains/eval/metrics/evidence.ts +182 -0
- package/src/domains/eval/metrics/fleet-loop-stream.ts +196 -0
- package/src/domains/eval/metrics/invariants.ts +736 -0
- package/src/domains/eval/metrics/latency.ts +4 -0
- package/src/domains/eval/metrics/token-stream.ts +139 -0
- package/src/domains/eval/metrics/tokens.ts +51 -0
- package/src/domains/eval/metrics/tool-calls.ts +7 -0
- package/src/domains/eval/proactive-memory.ts +279 -0
- package/src/domains/eval/provenance.ts +35 -0
- package/src/domains/eval/report.ts +234 -0
- package/src/domains/eval/reports/json.ts +5 -0
- package/src/domains/eval/reports/junit.ts +23 -0
- package/src/domains/eval/reports/markdown.ts +20 -0
- package/src/domains/eval/reports/swe-jsonl.ts +16 -0
- package/src/domains/eval/reports/text.ts +25 -0
- package/src/domains/eval/run-compare.ts +274 -0
- package/src/domains/eval/runner.ts +244 -0
- package/src/domains/eval/runners/clio-run.ts +148 -0
- package/src/domains/eval/runners/context-index.ts +56 -0
- package/src/domains/eval/runners/context-init.ts +203 -0
- package/src/domains/eval/runners/external-command.ts +350 -0
- package/src/domains/eval/runtime-paths.ts +171 -0
- package/src/domains/eval/schema/artifact.ts +75 -0
- package/src/domains/eval/schema/suite.ts +235 -0
- package/src/domains/eval/schema/validate.ts +339 -0
- package/src/domains/eval/store.ts +392 -0
- package/src/domains/eval/suites/load.ts +74 -0
- package/src/domains/eval/suites/matrix.ts +19 -0
- package/src/domains/eval/suites/resolve.ts +44 -0
- package/src/domains/eval/suites/run.ts +325 -0
- package/src/domains/eval/task-file.ts +186 -0
- package/src/domains/eval/types.ts +141 -0
- package/src/domains/eval/verifiers/command.ts +27 -0
- package/src/domains/eval/verifiers/file-exists.ts +6 -0
- package/src/domains/eval/verifiers/patch.ts +22 -0
- package/src/domains/eval/workspaces/git.ts +41 -0
- package/src/domains/eval/workspaces/local.ts +17 -0
- package/src/domains/eval/workspaces/temp-copy.ts +29 -0
- package/src/domains/evidence/build.ts +1714 -0
- package/src/domains/evidence/eval.ts +605 -0
- package/src/domains/evidence/failure-attribution.ts +54 -0
- package/src/domains/evidence/finish-contract-map.ts +42 -0
- package/src/domains/evidence/index.ts +55 -0
- package/src/domains/evidence/provenance.ts +200 -0
- package/src/domains/evidence/redact.ts +81 -0
- package/src/domains/evidence/store.ts +291 -0
- package/src/domains/evidence/types.ts +325 -0
- package/src/domains/evolution/SELF_EDIT_GATE.md +91 -0
- package/src/domains/evolution/index.ts +26 -0
- package/src/domains/evolution/manifest.ts +154 -0
- package/src/domains/evolution/validate.ts +304 -0
- package/src/domains/extensions/contract.ts +22 -0
- package/src/domains/extensions/discovery.ts +193 -0
- package/src/domains/extensions/extension.ts +46 -0
- package/src/domains/extensions/index.ts +34 -0
- package/src/domains/extensions/manager.ts +30 -0
- package/src/domains/extensions/manifest.ts +6 -0
- package/src/domains/extensions/resources.ts +38 -0
- package/src/domains/extensions/state.ts +224 -0
- package/src/domains/extensions/types.ts +83 -0
- package/src/domains/interop/consent.ts +185 -0
- package/src/domains/interop/contract.ts +29 -0
- package/src/domains/interop/detect.ts +173 -0
- package/src/domains/interop/extension.ts +59 -0
- package/src/domains/interop/index.ts +34 -0
- package/src/domains/interop/manifest.ts +6 -0
- package/src/domains/interop/registry.ts +139 -0
- package/src/domains/interop/state.ts +92 -0
- package/src/domains/interop/types.ts +109 -0
- package/src/domains/lifecycle/contract.ts +11 -0
- package/src/domains/lifecycle/doctor.ts +536 -0
- package/src/domains/lifecycle/extension.ts +23 -0
- package/src/domains/lifecycle/index.ts +21 -0
- package/src/domains/lifecycle/install-method.ts +51 -0
- package/src/domains/lifecycle/manifest.ts +6 -0
- package/src/domains/lifecycle/migrations/index.ts +94 -0
- package/src/domains/lifecycle/state.ts +93 -0
- package/src/domains/lifecycle/upgrade-notice.ts +35 -0
- package/src/domains/lifecycle/version.ts +35 -0
- package/src/domains/memory/index.ts +128 -0
- package/src/domains/memory/operations.ts +143 -0
- package/src/domains/memory/prompt-section.ts +86 -0
- package/src/domains/memory/proposal.ts +180 -0
- package/src/domains/memory/store.ts +163 -0
- package/src/domains/memory/task-bank.ts +229 -0
- package/src/domains/memory/task-memory-handoff.ts +222 -0
- package/src/domains/memory/task-memory-policy.ts +602 -0
- package/src/domains/memory/task-memory-status.ts +58 -0
- package/src/domains/memory/task-memory-telemetry.ts +346 -0
- package/src/domains/memory/task-memory-trace.ts +89 -0
- package/src/domains/memory/types.ts +74 -0
- package/src/domains/memory/validate.ts +297 -0
- package/src/domains/middleware/budget.ts +216 -0
- package/src/domains/middleware/contract.ts +35 -0
- package/src/domains/middleware/dispatch-nudge.ts +469 -0
- package/src/domains/middleware/extension.ts +129 -0
- package/src/domains/middleware/hook-receipts.ts +70 -0
- package/src/domains/middleware/hooks-io.ts +174 -0
- package/src/domains/middleware/hooks.ts +432 -0
- package/src/domains/middleware/index.ts +154 -0
- package/src/domains/middleware/manifest.ts +6 -0
- package/src/domains/middleware/memory-intervention.ts +701 -0
- package/src/domains/middleware/rules.ts +42 -0
- package/src/domains/middleware/runtime.ts +333 -0
- package/src/domains/middleware/skills-reminder.ts +187 -0
- package/src/domains/middleware/snapshot.ts +71 -0
- package/src/domains/middleware/stalled-turn.ts +71 -0
- package/src/domains/middleware/task-board-reminder.ts +87 -0
- package/src/domains/middleware/task-nudge.ts +77 -0
- package/src/domains/middleware/tool-choice-control.ts +42 -0
- package/src/domains/middleware/types.ts +131 -0
- package/src/domains/middleware/validate.ts +329 -0
- package/src/domains/observability/accountability.ts +67 -0
- package/src/domains/observability/contract.ts +168 -0
- package/src/domains/observability/cost.ts +206 -0
- package/src/domains/observability/evidence-index.ts +123 -0
- package/src/domains/observability/extension.ts +281 -0
- package/src/domains/observability/index.ts +78 -0
- package/src/domains/observability/manifest.ts +10 -0
- package/src/domains/observability/metrics.ts +37 -0
- package/src/domains/observability/projection.ts +477 -0
- package/src/domains/observability/telemetry.ts +89 -0
- package/src/domains/observability/trace-store.ts +1454 -0
- package/src/domains/prompts/compiler.ts +556 -0
- package/src/domains/prompts/contract.ts +30 -0
- package/src/domains/prompts/extension.ts +304 -0
- package/src/domains/prompts/fragment-loader.ts +139 -0
- package/src/domains/prompts/fragments/identity/clio.md +3 -2
- package/src/domains/prompts/fragments/identity/self-awareness.md +63 -0
- package/src/domains/prompts/fragments/operating/contract.md +10 -1
- package/src/domains/prompts/hash.ts +73 -0
- package/src/domains/prompts/index.ts +32 -0
- package/src/domains/prompts/manifest.ts +8 -0
- package/src/domains/prompts/memory-intervention.ts +57 -0
- package/src/domains/prompts/preload.ts +82 -0
- package/src/domains/providers/auth/api-key.ts +47 -0
- package/src/domains/providers/auth/backend-file.ts +69 -0
- package/src/domains/providers/auth/backend-memory.ts +17 -0
- package/src/domains/providers/auth/index.ts +37 -0
- package/src/domains/providers/auth/oauth.ts +36 -0
- package/src/domains/providers/auth/storage.ts +667 -0
- package/src/domains/providers/capabilities.ts +50 -0
- package/src/domains/providers/catalog.ts +162 -0
- package/src/domains/providers/contract.ts +151 -0
- package/src/domains/providers/credentials.ts +20 -0
- package/src/domains/providers/eligibility.ts +24 -0
- package/src/domains/providers/errors.ts +64 -0
- package/src/domains/providers/extension.ts +536 -0
- package/src/domains/providers/index.ts +142 -0
- package/src/domains/providers/knowledge-base-path.ts +102 -0
- package/src/domains/providers/manifest.ts +6 -0
- package/src/domains/providers/model-capabilities.ts +136 -0
- package/src/domains/providers/model-discovery.ts +144 -0
- package/src/domains/providers/model-family.ts +32 -0
- package/src/domains/providers/model-runtime-capabilities.ts +712 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +2 -2
- package/src/domains/providers/plugins.ts +59 -0
- package/src/domains/providers/probe/fingerprint.ts +44 -0
- package/src/domains/providers/probe/http.ts +114 -0
- package/src/domains/providers/probe/reasoning.ts +111 -0
- package/src/domains/providers/registry.ts +166 -0
- package/src/domains/providers/resolver.ts +156 -0
- package/src/domains/providers/runtime-resolution.ts +720 -0
- package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +90 -0
- package/src/domains/providers/runtimes/builtins.ts +78 -0
- package/src/domains/providers/runtimes/claude/claude-code.ts +38 -0
- package/src/domains/providers/runtimes/claude/claude-sdk.ts +38 -0
- package/src/domains/providers/runtimes/claude/common.ts +55 -0
- package/src/domains/providers/runtimes/cloud/alcf.ts +171 -0
- package/src/domains/providers/runtimes/cloud/anthropic-max.ts +71 -0
- package/src/domains/providers/runtimes/cloud/anthropic.ts +43 -0
- package/src/domains/providers/runtimes/cloud/bedrock.ts +35 -0
- package/src/domains/providers/runtimes/cloud/deepseek.ts +50 -0
- package/src/domains/providers/runtimes/cloud/google.ts +41 -0
- package/src/domains/providers/runtimes/cloud/groq.ts +46 -0
- package/src/domains/providers/runtimes/cloud/mistral.ts +46 -0
- package/src/domains/providers/runtimes/cloud/openai-codex.ts +49 -0
- package/src/domains/providers/runtimes/cloud/openai.ts +46 -0
- package/src/domains/providers/runtimes/cloud/openrouter.ts +111 -0
- package/src/domains/providers/runtimes/common/lmstudio-logger.ts +32 -0
- package/src/domains/providers/runtimes/common/local-synth.ts +165 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +474 -0
- package/src/domains/providers/runtimes/local-native/lemonade-anthropic.ts +27 -0
- package/src/domains/providers/runtimes/local-native/lemonade-openai.ts +26 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +88 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +207 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +180 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +126 -0
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +98 -0
- package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +491 -0
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +117 -0
- package/src/domains/providers/runtimes/local-native/sglang.ts +26 -0
- package/src/domains/providers/runtimes/local-native/vllm.ts +27 -0
- package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +104 -0
- package/src/domains/providers/runtimes/protocol/anthropic-messages.ts +28 -0
- package/src/domains/providers/runtimes/protocol/bedrock.ts +26 -0
- package/src/domains/providers/runtimes/protocol/google.ts +27 -0
- package/src/domains/providers/runtimes/protocol/openai-compat.ts +145 -0
- package/src/domains/providers/support.ts +238 -0
- package/src/domains/providers/thinking-control-policy.ts +85 -0
- package/src/domains/providers/types/capability-flags.ts +114 -0
- package/src/domains/providers/types/cost-provenance.ts +7 -0
- package/src/domains/providers/types/inference.ts +41 -0
- package/src/domains/providers/types/knowledge-base.ts +156 -0
- package/src/domains/providers/types/local-model-quirks.ts +218 -0
- package/src/domains/providers/types/runtime-descriptor.ts +170 -0
- package/src/domains/providers/types/target-descriptor.ts +38 -0
- package/src/domains/resources/collision.ts +92 -0
- package/src/domains/resources/common-loader.ts +140 -0
- package/src/domains/resources/contract.ts +25 -0
- package/src/domains/resources/extension.ts +61 -0
- package/src/domains/resources/index.ts +74 -0
- package/src/domains/resources/loader.ts +86 -0
- package/src/domains/resources/manifest.ts +6 -0
- package/src/domains/resources/prompts/loader.ts +232 -0
- package/src/domains/resources/prompts/substitute.ts +65 -0
- package/src/domains/resources/skills/content-hash.ts +105 -0
- package/src/domains/resources/skills/evals.ts +175 -0
- package/src/domains/resources/skills/install.ts +410 -0
- package/src/domains/resources/skills/loader.ts +978 -0
- package/src/domains/resources/skills/marketplace.ts +255 -0
- package/src/domains/resources/skills/provenance-pin.ts +115 -0
- package/src/domains/safety/action-classifier.ts +268 -0
- package/src/domains/safety/approval-axis.ts +23 -0
- package/src/domains/safety/audit.ts +487 -0
- package/src/domains/safety/autonomy.ts +174 -0
- package/src/domains/safety/call-target.ts +59 -0
- package/src/domains/safety/contract.ts +65 -0
- package/src/domains/safety/damage-control.ts +44 -0
- package/src/domains/safety/default-path-policy.ts +117 -0
- package/src/domains/safety/extension.ts +383 -0
- package/src/domains/safety/finish-contract-registration.ts +128 -0
- package/src/domains/safety/finish-contract.ts +554 -0
- package/src/domains/safety/index.ts +44 -0
- package/src/domains/safety/loop-detector.ts +143 -0
- package/src/domains/safety/manifest.ts +6 -0
- package/src/domains/safety/path-policy.ts +164 -0
- package/src/domains/safety/policy-engine.ts +994 -0
- package/src/domains/safety/project-policy.ts +391 -0
- package/src/domains/safety/protected-artifacts-registration.ts +189 -0
- package/src/domains/safety/protected-artifacts.ts +993 -0
- package/src/domains/safety/rejection-feedback.ts +102 -0
- package/src/domains/safety/rigor.ts +72 -0
- package/src/domains/safety/rule-compiler.ts +48 -0
- package/src/domains/safety/rule-pack-loader.ts +92 -0
- package/src/domains/safety/run-effects.ts +197 -0
- package/src/domains/safety/scope.ts +61 -0
- package/src/domains/scheduling/budget.ts +35 -0
- package/src/domains/scheduling/cluster.ts +175 -0
- package/src/domains/scheduling/contract.ts +30 -0
- package/src/domains/scheduling/extension.ts +91 -0
- package/src/domains/scheduling/index.ts +12 -0
- package/src/domains/scheduling/manifest.ts +6 -0
- package/src/domains/session/archive-readers.ts +208 -0
- package/src/domains/session/checkpoint.ts +38 -0
- package/src/domains/session/compaction/auto.ts +89 -0
- package/src/domains/session/compaction/branch-summary.ts +186 -0
- package/src/domains/session/compaction/compact.ts +493 -0
- package/src/domains/session/compaction/cut-point.ts +171 -0
- package/src/domains/session/compaction/defaults.ts +34 -0
- package/src/domains/session/compaction/mask-observations.ts +265 -0
- package/src/domains/session/compaction/session-entries.ts +18 -0
- package/src/domains/session/compaction/tokens.ts +147 -0
- package/src/domains/session/context-accounting.ts +583 -0
- package/src/domains/session/context-ledger.ts +348 -0
- package/src/domains/session/contract.ts +144 -0
- package/src/domains/session/cwd-fallback.ts +65 -0
- package/src/domains/session/entries.ts +509 -0
- package/src/domains/session/extension.ts +322 -0
- package/src/domains/session/history.ts +172 -0
- package/src/domains/session/index.ts +84 -0
- package/src/domains/session/manager.ts +128 -0
- package/src/domains/session/manifest.ts +6 -0
- package/src/domains/session/migrations/index.ts +27 -0
- package/src/domains/session/prompt-manifest.ts +173 -0
- package/src/domains/session/protected-artifact-journal.ts +241 -0
- package/src/domains/session/protected-artifacts.ts +90 -0
- package/src/domains/session/retry.ts +238 -0
- package/src/domains/session/task-board.ts +375 -0
- package/src/domains/session/tree/active-path.ts +98 -0
- package/src/domains/session/tree/fork.ts +160 -0
- package/src/domains/session/tree/manager.ts +128 -0
- package/src/domains/session/tree/navigator.ts +177 -0
- package/src/domains/session/tree/preview.ts +259 -0
- package/src/domains/session/usage.ts +148 -0
- package/src/domains/session/workspace/git-probe.ts +161 -0
- package/src/domains/session/workspace/index.ts +4 -0
- package/src/domains/session/workspace/project-type.ts +217 -0
- package/src/domains/session/workspace/snapshot.ts +74 -0
- package/src/domains/share/archive.ts +684 -0
- package/src/domains/share/contract.ts +9 -0
- package/src/domains/share/extension.ts +26 -0
- package/src/domains/share/index.ts +28 -0
- package/src/domains/share/manifest.ts +6 -0
- package/src/engine/acp/adapter.ts +397 -0
- package/src/engine/acp/errors.ts +32 -0
- package/src/engine/acp/event-mapper.ts +154 -0
- package/src/engine/acp/server.ts +656 -0
- package/src/engine/acp/tool-mediator.ts +686 -0
- package/src/engine/acp/transport.ts +654 -0
- package/src/engine/acp/types.ts +170 -0
- package/src/engine/agent.ts +29 -0
- package/src/engine/ai.ts +202 -0
- package/src/engine/alcf-oauth.ts +198 -0
- package/src/engine/antigravity/subprocess-runtime.ts +205 -0
- package/src/engine/apis/degraded-inference.ts +95 -0
- package/src/engine/apis/index.ts +20 -0
- package/src/engine/apis/llamacpp-residency.ts +252 -0
- package/src/engine/apis/lmstudio-native.ts +1438 -0
- package/src/engine/apis/lmstudio-residency.ts +92 -0
- package/src/engine/apis/ollama-native.ts +507 -0
- package/src/engine/apis/openai-completions.ts +716 -0
- package/src/engine/apis/output-budget.ts +99 -0
- package/src/engine/apis/residency-lock.ts +29 -0
- package/src/engine/apis/residency.ts +640 -0
- package/src/engine/apis/resident-models.ts +42 -0
- package/src/engine/apis/sampling-overrides.ts +38 -0
- package/src/engine/apis/thinking-replay.ts +11 -0
- package/src/engine/claude/sdk-runtime.ts +685 -0
- package/src/engine/claude/session-id.ts +5 -0
- package/src/engine/claude/subprocess-runtime.ts +316 -0
- package/src/engine/claude/tool-safety.ts +400 -0
- package/src/engine/external-subprocess.ts +23 -0
- package/src/engine/harmony-response.ts +165 -0
- package/src/engine/loop-guard.ts +1127 -0
- package/src/engine/oauth.ts +192 -0
- package/src/engine/pi-mono-names.ts +13 -0
- package/src/engine/provider-payload.ts +315 -0
- package/src/engine/select-list.ts +53 -0
- package/src/engine/session.ts +834 -0
- package/src/engine/strip-tokenizer-sentinels.ts +125 -0
- package/src/engine/tui.ts +103 -0
- package/src/engine/types.ts +66 -0
- package/src/engine/worker-events.ts +100 -0
- package/src/engine/worker-runtime.ts +1022 -0
- package/src/engine/worker-tools.ts +203 -0
- package/src/entry/orchestrator.ts +1873 -0
- package/src/interactive/application-controller.ts +346 -0
- package/src/interactive/bus-notices.ts +265 -0
- package/src/interactive/chat-loop-messages.ts +626 -0
- package/src/interactive/chat-loop-policy.ts +18 -0
- package/src/interactive/chat-loop.ts +865 -0
- package/src/interactive/chat-panel.ts +1694 -0
- package/src/interactive/chat-renderer.ts +1038 -0
- package/src/interactive/clio-editor.ts +166 -0
- package/src/interactive/command-fallbacks.ts +23 -0
- package/src/interactive/command-output.ts +85 -0
- package/src/interactive/context-activity.ts +203 -0
- package/src/interactive/context-meter.ts +223 -0
- package/src/interactive/context-overlay.ts +220 -0
- package/src/interactive/cost-overlay.ts +279 -0
- package/src/interactive/dispatch-board.ts +1319 -0
- package/src/interactive/dispatch-steering.ts +86 -0
- package/src/interactive/editor-bash.ts +46 -0
- package/src/interactive/editor-steer.ts +51 -0
- package/src/interactive/editor-submit.ts +303 -0
- package/src/interactive/external-editor.ts +67 -0
- package/src/interactive/follow-up-queue-panel.ts +75 -0
- package/src/interactive/footer/dashboard.ts +534 -0
- package/src/interactive/footer/notifications.ts +275 -0
- package/src/interactive/footer/widgets.ts +1246 -0
- package/src/interactive/footer-panel.ts +187 -0
- package/src/interactive/format-time.ts +85 -0
- package/src/interactive/index.ts +11 -0
- package/src/interactive/interactive-application.ts +755 -0
- package/src/interactive/interactive-event-projection.ts +380 -0
- package/src/interactive/interactive-input-runtime.ts +261 -0
- package/src/interactive/interactive-presentation.ts +452 -0
- package/src/interactive/interactive-shell.ts +134 -0
- package/src/interactive/interactive-slash-runtime.ts +492 -0
- package/src/interactive/interactive-subscriptions.ts +111 -0
- package/src/interactive/interactive-tickers.ts +152 -0
- package/src/interactive/keybinding-manager.ts +496 -0
- package/src/interactive/layout.ts +20 -0
- package/src/interactive/leader-key.ts +143 -0
- package/src/interactive/loop-guard-interrupt.ts +101 -0
- package/src/interactive/memory-overlay.ts +280 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +110 -0
- package/src/interactive/overlay-auth-lifecycle.ts +308 -0
- package/src/interactive/overlay-frame.ts +502 -0
- package/src/interactive/overlay-general-openers.ts +217 -0
- package/src/interactive/overlay-key-routing.ts +213 -0
- package/src/interactive/overlay-lifecycle.ts +413 -0
- package/src/interactive/overlay-model-selectors.ts +127 -0
- package/src/interactive/overlay-permission-lifecycle.ts +344 -0
- package/src/interactive/overlay-resource-openers.ts +112 -0
- package/src/interactive/overlay-session-lifecycle.ts +270 -0
- package/src/interactive/overlay-transitions.ts +57 -0
- package/src/interactive/overlays/agents.ts +69 -0
- package/src/interactive/overlays/ask-user.ts +921 -0
- package/src/interactive/overlays/auth-dialog.ts +221 -0
- package/src/interactive/overlays/context-reset.ts +108 -0
- package/src/interactive/overlays/cwd-fallback.ts +141 -0
- package/src/interactive/overlays/extensions.ts +54 -0
- package/src/interactive/overlays/help-reference.ts +140 -0
- package/src/interactive/overlays/interop.ts +139 -0
- package/src/interactive/overlays/keybinding-detail.ts +90 -0
- package/src/interactive/overlays/list-overlay.ts +606 -0
- package/src/interactive/overlays/message-picker.ts +155 -0
- package/src/interactive/overlays/model-selector.ts +1091 -0
- package/src/interactive/overlays/prompts.ts +72 -0
- package/src/interactive/overlays/session-selector-search.ts +31 -0
- package/src/interactive/overlays/session-selector.ts +266 -0
- package/src/interactive/overlays/settings.ts +3363 -0
- package/src/interactive/overlays/skills-hub.ts +237 -0
- package/src/interactive/overlays/tree-selector.ts +387 -0
- package/src/interactive/permission-overlay.ts +170 -0
- package/src/interactive/render-trace.ts +125 -0
- package/src/interactive/renderers/branch-summary.ts +86 -0
- package/src/interactive/renderers/code-ink.ts +420 -0
- package/src/interactive/renderers/compaction-summary.ts +112 -0
- package/src/interactive/renderers/diff.ts +126 -0
- package/src/interactive/renderers/notice.ts +28 -0
- package/src/interactive/renderers/retry-status.ts +31 -0
- package/src/interactive/renderers/structured.ts +89 -0
- package/src/interactive/renderers/tool-execution.ts +1104 -0
- package/src/interactive/renderers/worker-entry.ts +264 -0
- package/src/interactive/session-last-turn.ts +186 -0
- package/src/interactive/session-transcript.ts +105 -0
- package/src/interactive/session-usage-reseed.ts +74 -0
- package/src/interactive/slash-autocomplete.ts +364 -0
- package/src/interactive/slash-commands.ts +1319 -0
- package/src/interactive/slash-spec.ts +453 -0
- package/src/interactive/status/controller.ts +256 -0
- package/src/interactive/status/index.ts +23 -0
- package/src/interactive/status/state-machine.ts +530 -0
- package/src/interactive/status/summary.ts +170 -0
- package/src/interactive/status/types.ts +121 -0
- package/src/interactive/status/verbs.ts +163 -0
- package/src/interactive/status/watchdog.ts +24 -0
- package/src/interactive/tasks-overlay.ts +186 -0
- package/src/interactive/theme/components.ts +55 -0
- package/src/interactive/theme/glyphs.ts +70 -0
- package/src/interactive/theme/index.ts +15 -0
- package/src/interactive/theme/labels.ts +88 -0
- package/src/interactive/theme/rules.ts +138 -0
- package/src/interactive/theme/segments.ts +88 -0
- package/src/interactive/theme/tokens.ts +121 -0
- package/src/interactive/thinking-level-policy.ts +6 -0
- package/src/interactive/tool-prose-loop.ts +122 -0
- package/src/interactive/tool-prose-registration.ts +48 -0
- package/src/interactive/turn-context.ts +854 -0
- package/src/interactive/turn-middleware.ts +283 -0
- package/src/interactive/turn-persistence.ts +441 -0
- package/src/interactive/turn-queues.ts +169 -0
- package/src/interactive/turn-recovery.ts +306 -0
- package/src/interactive/turn-runtime.ts +802 -0
- package/src/interactive/turn-state.ts +123 -0
- package/src/interactive/view/artifacts.ts +1342 -0
- package/src/interactive/view/view-overlay.ts +732 -0
- package/src/interactive/welcome-dashboard.ts +536 -0
- package/src/interactive/worker-receipts.ts +94 -0
- package/src/interactive/worker-replay.ts +111 -0
- package/src/interactive/worker-share.ts +100 -0
- package/src/interactive/worker-stream.ts +442 -0
- package/src/interactive/workspace-facts.ts +141 -0
- package/src/tools/agent-tools.ts +382 -0
- package/src/tools/artifact.ts +103 -0
- package/src/tools/ask-user.ts +656 -0
- package/src/tools/bash.ts +176 -0
- package/src/tools/bootstrap.ts +474 -0
- package/src/tools/codewiki/code-nav.ts +563 -0
- package/src/tools/codewiki/shared.ts +115 -0
- package/src/tools/compete-worktrees.ts +898 -0
- package/src/tools/context/docs-engine.ts +619 -0
- package/src/tools/context/index.ts +435 -0
- package/src/tools/credential-present.ts +151 -0
- package/src/tools/dispatch-arguments.ts +228 -0
- package/src/tools/dispatch-plan.ts +741 -0
- package/src/tools/dispatch-scout.ts +461 -0
- package/src/tools/dispatch.ts +3338 -0
- package/src/tools/edit-diff.ts +534 -0
- package/src/tools/edit.ts +128 -0
- package/src/tools/executables.ts +22 -0
- package/src/tools/file-mutation-queue.ts +35 -0
- package/src/tools/find.ts +292 -0
- package/src/tools/grep.ts +495 -0
- package/src/tools/ignore-policy.ts +169 -0
- package/src/tools/ledger.ts +206 -0
- package/src/tools/ls.ts +110 -0
- package/src/tools/monitor.ts +797 -0
- package/src/tools/network-policy.ts +29 -0
- package/src/tools/observation.ts +421 -0
- package/src/tools/observers.ts +99 -0
- package/src/tools/path-utils.ts +79 -0
- package/src/tools/policy.ts +154 -0
- package/src/tools/profiles.ts +108 -0
- package/src/tools/read.ts +173 -0
- package/src/tools/registry.ts +1157 -0
- package/src/tools/result-shaping.ts +204 -0
- package/src/tools/safe-exec.ts +128 -0
- package/src/tools/spawn-hygiene.ts +182 -0
- package/src/tools/steer.ts +88 -0
- package/src/tools/string-enum.ts +15 -0
- package/src/tools/tasks.ts +168 -0
- package/src/tools/truncate-utf8.ts +17 -0
- package/src/tools/truncate.ts +202 -0
- package/src/tools/verify/frontend.ts +685 -0
- package/src/tools/verify/index.ts +66 -0
- package/src/tools/verify/scripts.ts +108 -0
- package/src/tools/web-fetch.ts +777 -0
- package/src/tools/worker-evidence.ts +215 -0
- package/src/tools/write.ts +52 -0
- package/src/utils/exif-orientation.ts +192 -0
- package/src/utils/git.ts +24 -0
- package/src/utils/image-resize.ts +148 -0
- package/src/utils/photon.ts +103 -0
- package/src/worker/control-lane.ts +22 -0
- package/src/worker/entry.ts +257 -0
- package/src/worker/event-projection.ts +56 -0
- package/src/worker/heartbeat.ts +24 -0
- package/src/worker/ledger-mirror.ts +95 -0
- package/src/worker/ndjson.ts +40 -0
- package/src/worker/protocol.ts +529 -0
- package/src/worker/resource-facts.ts +79 -0
- package/src/worker/runtime-registry.ts +12 -0
- package/src/worker/spec-contract.ts +654 -0
- package/src/worker/stdin-demux.ts +316 -0
- package/dist/acp-UMLFVA3F.js +0 -92
- package/dist/chunk-262G75JS.js +0 -35
- package/dist/chunk-5LDRNKX2.js +0 -1063
- package/dist/chunk-67MTHP2E.js +0 -135
- package/dist/chunk-GOTUR54M.js +0 -9
- package/dist/chunk-TPOCL34A.js +0 -2876
- package/dist/chunk-XV4ZJ6ZM.js +0 -3177
- package/dist/clio-KIQ5SNDS.js +0 -53
- package/dist/configure-G4E3A2PG.js +0 -27
- package/dist/doctor-M5HJJZOL.js +0 -61
- package/dist/domains/agents/builtins/architect.md +0 -33
- package/dist/domains/agents/builtins/coder.md +0 -31
- package/dist/domains/agents/builtins/context-bootstrap.md +0 -38
- package/dist/domains/agents/builtins/debugger.md +0 -30
- package/dist/domains/agents/builtins/documenter.md +0 -31
- package/dist/domains/agents/builtins/git-master.md +0 -30
- package/dist/domains/agents/builtins/provenance.md +0 -30
- package/dist/domains/agents/builtins/researcher.md +0 -71
- package/dist/domains/agents/builtins/scout.md +0 -42
- package/dist/domains/agents/builtins/tester.md +0 -31
- package/dist/domains/agents/builtins/verifier.md +0 -30
- package/dist/domains/agents/builtins/wiki-writer.md +0 -41
- package/dist/fleet-preflight-CLIAX7YR.js +0 -21
- package/dist/preload-KZVHET2B.js +0 -11
- package/dist/version-ROZ6CZKH.js +0 -16
- package/skills/git/commit-crafting/SKILL.md +0 -79
- package/skills/git/commit-crafting/evals.md +0 -92
- package/skills/git/create-pr/SKILL.md +0 -116
- package/skills/git/create-pr/evals.md +0 -114
- package/skills/git/investigate-issue/SKILL.md +0 -139
- package/skills/git/investigate-issue/evals.md +0 -94
- package/skills/git/review-changes/SKILL.md +0 -103
- package/skills/git/review-changes/evals.md +0 -85
|
@@ -0,0 +1,1040 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only coordinator evidence for review and compete decisions.
|
|
3
|
+
*
|
|
4
|
+
* Worker receipts seal before a reviewer verdict or judge winner can be
|
|
5
|
+
* parsed. Mutating those receipts would invalidate their evidence, so the
|
|
6
|
+
* coordinator writes a separate integrity-covered artifact that links the
|
|
7
|
+
* decider receipt to every subject receipt. One file per decision keeps the
|
|
8
|
+
* crash boundary atomic and makes pass/fail/winner evidence recoverable
|
|
9
|
+
* without an in-memory tool result.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
13
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
14
|
+
import { dirname, join, resolve } from "node:path";
|
|
15
|
+
import { parseJsonObjectPayload } from "../../core/json-payload.js";
|
|
16
|
+
import { clioStateDir } from "../../core/xdg.js";
|
|
17
|
+
import { atomicWrite } from "../../engine/session.js";
|
|
18
|
+
import { parseVerifierResult, type VerifierCheck, type VerifierResult } from "../agents/result-contract.js";
|
|
19
|
+
import type { GateRouteCorrelation } from "./execution-role.js";
|
|
20
|
+
import type { RunGateSubjectRef } from "./types.js";
|
|
21
|
+
|
|
22
|
+
export type GateDecisionOutcome =
|
|
23
|
+
| "pass"
|
|
24
|
+
| "fail"
|
|
25
|
+
| "revise"
|
|
26
|
+
| "exhausted"
|
|
27
|
+
| "winner"
|
|
28
|
+
| "no-winner"
|
|
29
|
+
| "operator-confirmed"
|
|
30
|
+
| "full-auto-applied";
|
|
31
|
+
|
|
32
|
+
export interface GateDecisionRef {
|
|
33
|
+
id: string;
|
|
34
|
+
digest: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sealed correlation between the decider route and the subject route it graded.
|
|
39
|
+
*
|
|
40
|
+
* A verdict from a route that shares the subject's agent or model family is a
|
|
41
|
+
* measurement of the same system twice. Recording the correlation on the
|
|
42
|
+
* decision means a small fleet still produces a usable audit trail: the gate is
|
|
43
|
+
* visible and its independence is a stated fact rather than an assumption.
|
|
44
|
+
*/
|
|
45
|
+
export type GateDecisionCorrelation = Pick<
|
|
46
|
+
GateRouteCorrelation,
|
|
47
|
+
"agent" | "target" | "modelFamily" | "runtime" | "node" | "independent"
|
|
48
|
+
>;
|
|
49
|
+
|
|
50
|
+
export interface GateDecisionArtifact {
|
|
51
|
+
version: 2;
|
|
52
|
+
id: string;
|
|
53
|
+
group: string;
|
|
54
|
+
topology: "review" | "compete";
|
|
55
|
+
cycle: number;
|
|
56
|
+
outcome: GateDecisionOutcome;
|
|
57
|
+
subjects: RunGateSubjectRef[];
|
|
58
|
+
decider?: RunGateSubjectRef;
|
|
59
|
+
/** Required whenever a decider run produced this decision. */
|
|
60
|
+
correlation?: GateDecisionCorrelation;
|
|
61
|
+
winner?: { index: number; subject: RunGateSubjectRef; branch: string };
|
|
62
|
+
/** Prior judge decision explicitly confirmed by a supervised operator. */
|
|
63
|
+
confirmation?: GateDecisionRef;
|
|
64
|
+
detail?: string;
|
|
65
|
+
createdAt: string;
|
|
66
|
+
integrity: { algorithm: "sha256"; digest: string };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Structured gate results
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A gate result is a typed answer to the coordinator's question, not a trailing
|
|
75
|
+
* prose line. Two schemas exist because the two topologies ask different
|
|
76
|
+
* questions:
|
|
77
|
+
*
|
|
78
|
+
* - Review asks "does this work pass?", which is exactly the Slice 2
|
|
79
|
+
* `verifier-report` contract. There is deliberately no `revise` verdict:
|
|
80
|
+
* whether a failure is worth another cycle is the coordinator's bounded
|
|
81
|
+
* continuation policy, not a decision the reviewed model gets to author.
|
|
82
|
+
* - Compete asks "which candidate wins?", which no recipe contract expresses
|
|
83
|
+
* because it is a property of the topology rather than of any one agent.
|
|
84
|
+
* That schema therefore lives here, next to the winner shape the decision
|
|
85
|
+
* artifact already validates, and reuses the same typed check evidence.
|
|
86
|
+
*/
|
|
87
|
+
export type GateCheck = VerifierCheck;
|
|
88
|
+
|
|
89
|
+
export type ReviewGateResult = VerifierResult;
|
|
90
|
+
|
|
91
|
+
export interface CompeteGateResult {
|
|
92
|
+
/** 1-based candidate ordinal, validated against the enumerated subjects. */
|
|
93
|
+
winner: number;
|
|
94
|
+
checks: ReadonlyArray<GateCheck>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type GateResultParse<T> = { ok: true; result: T } | { ok: false; reason: string };
|
|
98
|
+
|
|
99
|
+
/** Parse a reviewer answer under the Slice 2 Verifier contract. */
|
|
100
|
+
function parseReviewGateResult(output: string): GateResultParse<ReviewGateResult> {
|
|
101
|
+
const result = parseVerifierResult(output);
|
|
102
|
+
return result === null
|
|
103
|
+
? { ok: false, reason: "reviewer did not return a valid verifier report" }
|
|
104
|
+
: { ok: true, result };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ReviewGateDecisionInput {
|
|
108
|
+
group: string;
|
|
109
|
+
cycle: number;
|
|
110
|
+
/** True when this cycle is the configured bound, so a failure is terminal. */
|
|
111
|
+
terminalCycle: boolean;
|
|
112
|
+
subjects: RunGateSubjectRef[];
|
|
113
|
+
decider: RunGateSubjectRef;
|
|
114
|
+
correlation: GateDecisionCorrelation;
|
|
115
|
+
output: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ReviewGateDecision {
|
|
119
|
+
draft: GateDecisionDraft;
|
|
120
|
+
/** Terminal verdict, or null when the gate continues or needs the operator. */
|
|
121
|
+
verdict: "pass" | "fail" | null;
|
|
122
|
+
/** Structured findings threaded to the next builder, or null when the gate ended. */
|
|
123
|
+
findings: string | null;
|
|
124
|
+
/** Operator-facing reason when the gate ended without a verdict. */
|
|
125
|
+
needsDecision: string | null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Apply the coordinator's review policy to one reviewer answer.
|
|
130
|
+
*
|
|
131
|
+
* The reviewer answers pass or fail under its Slice 2 contract and nothing
|
|
132
|
+
* else. `revise` is this function's bounded continuation decision: a failure
|
|
133
|
+
* with cycles left earns another builder attempt, the same failure at the bound
|
|
134
|
+
* is simply the terminal fail, and an answer that does not satisfy the contract
|
|
135
|
+
* is a broken gate rather than a free extra cycle. The live loop and the
|
|
136
|
+
* crash-replay path share this so a recovered decision is byte-identical to the
|
|
137
|
+
* one the original process would have written.
|
|
138
|
+
*/
|
|
139
|
+
export function decideReviewGate(input: ReviewGateDecisionInput): ReviewGateDecision {
|
|
140
|
+
const base = {
|
|
141
|
+
group: input.group,
|
|
142
|
+
topology: "review" as const,
|
|
143
|
+
cycle: input.cycle,
|
|
144
|
+
subjects: input.subjects,
|
|
145
|
+
decider: input.decider,
|
|
146
|
+
correlation: input.correlation,
|
|
147
|
+
};
|
|
148
|
+
const parsed = parseReviewGateResult(input.output);
|
|
149
|
+
if (!parsed.ok) {
|
|
150
|
+
return {
|
|
151
|
+
draft: { ...base, outcome: "exhausted", detail: parsed.reason },
|
|
152
|
+
verdict: null,
|
|
153
|
+
findings: null,
|
|
154
|
+
needsDecision: `review gate produced no structured verdict in cycle ${input.cycle}: ${parsed.reason}`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (parsed.result.verdict === "pass" || input.terminalCycle) {
|
|
158
|
+
return {
|
|
159
|
+
draft: { ...base, outcome: parsed.result.verdict },
|
|
160
|
+
verdict: parsed.result.verdict,
|
|
161
|
+
findings: null,
|
|
162
|
+
needsDecision: null,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const failed = parsed.result.checks.filter((check) => !check.passed);
|
|
166
|
+
return {
|
|
167
|
+
draft: { ...base, outcome: "revise", detail: `reviewer reported ${failed.length} failed check(s)` },
|
|
168
|
+
verdict: null,
|
|
169
|
+
findings: JSON.stringify({ verdict: "fail", checks: failed }),
|
|
170
|
+
needsDecision: null,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseGateChecks(value: unknown): GateCheck[] | null {
|
|
175
|
+
if (!Array.isArray(value) || value.length === 0) return null;
|
|
176
|
+
const checks: GateCheck[] = [];
|
|
177
|
+
for (const entry of value) {
|
|
178
|
+
if (!isRecord(entry)) return null;
|
|
179
|
+
const keys = Object.keys(entry);
|
|
180
|
+
if (
|
|
181
|
+
keys.some((key) => key !== "name" && key !== "passed" && key !== "evidence") ||
|
|
182
|
+
typeof entry.name !== "string" ||
|
|
183
|
+
entry.name.trim().length === 0 ||
|
|
184
|
+
typeof entry.passed !== "boolean" ||
|
|
185
|
+
typeof entry.evidence !== "string" ||
|
|
186
|
+
entry.evidence.trim().length === 0
|
|
187
|
+
) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
checks.push({ name: entry.name, passed: entry.passed, evidence: entry.evidence });
|
|
191
|
+
}
|
|
192
|
+
return checks;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Parse a judge answer. The winner must name an enumerated candidate ordinal. */
|
|
196
|
+
export function parseCompeteGateResult(output: string, candidateCount: number): GateResultParse<CompeteGateResult> {
|
|
197
|
+
const parsed = parseJsonObjectPayload(output);
|
|
198
|
+
if (!parsed.ok) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
reason: parsed.reason === "not-object" ? "judge result must be a JSON object" : "judge result must be valid JSON",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const value = parsed.value;
|
|
205
|
+
if (Object.keys(value).some((key) => key !== "winner" && key !== "checks")) {
|
|
206
|
+
return { ok: false, reason: "judge result has unknown fields" };
|
|
207
|
+
}
|
|
208
|
+
const winner = value.winner;
|
|
209
|
+
if (typeof winner !== "number" || !Number.isSafeInteger(winner) || winner < 1 || winner > candidateCount) {
|
|
210
|
+
return { ok: false, reason: `judge winner must be an integer 1..${candidateCount}` };
|
|
211
|
+
}
|
|
212
|
+
const checks = parseGateChecks(value.checks);
|
|
213
|
+
if (checks === null) return { ok: false, reason: "judge result must carry typed checks with evidence" };
|
|
214
|
+
return { ok: true, result: { winner, checks } };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export type GateDecisionDraft = Omit<GateDecisionArtifact, "version" | "id" | "createdAt" | "integrity"> & {
|
|
218
|
+
createdAt?: string;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export type GateDecisionVerification = { ok: true } | { ok: false; reason: string };
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Raw terminal output staged as soon as the reviewer/judge event stream
|
|
225
|
+
* settles. At this point the decider receipt may still be finalizing, so the
|
|
226
|
+
* run id is durable but its receipt digest is intentionally not guessed.
|
|
227
|
+
*/
|
|
228
|
+
export interface PendingGateOutputDraft {
|
|
229
|
+
group: string;
|
|
230
|
+
topology: "review" | "compete";
|
|
231
|
+
cycle: number;
|
|
232
|
+
subjects: RunGateSubjectRef[];
|
|
233
|
+
deciderRunId: string;
|
|
234
|
+
finalOutput: string;
|
|
235
|
+
/** Review cycle bound was reached; revise/unparseable also implies exhaustion. */
|
|
236
|
+
terminalCycle?: boolean;
|
|
237
|
+
/** Repository resource owner for compete restart settlement. */
|
|
238
|
+
resourceRoot?: string;
|
|
239
|
+
createdAt?: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface PendingGateOutputRecord {
|
|
243
|
+
version: 1;
|
|
244
|
+
kind: "output";
|
|
245
|
+
id: string;
|
|
246
|
+
group: string;
|
|
247
|
+
topology: "review" | "compete";
|
|
248
|
+
cycle: number;
|
|
249
|
+
subjects: RunGateSubjectRef[];
|
|
250
|
+
deciderRunId: string;
|
|
251
|
+
finalOutput: string;
|
|
252
|
+
terminalCycle?: boolean;
|
|
253
|
+
resourceRoot?: string;
|
|
254
|
+
createdAt: string;
|
|
255
|
+
integrity: { algorithm: "sha256"; digest: string };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* A fully resolved decision waiting to be materialized. The nested artifact
|
|
260
|
+
* already has its final id and digest, making replay safe when a process dies
|
|
261
|
+
* after the artifact rename but before pending-file removal.
|
|
262
|
+
*/
|
|
263
|
+
export interface PendingGateArtifactRecord {
|
|
264
|
+
version: 1;
|
|
265
|
+
kind: "decision";
|
|
266
|
+
id: string;
|
|
267
|
+
decision: GateDecisionArtifact;
|
|
268
|
+
finalOutput?: string;
|
|
269
|
+
resourceRoot?: string;
|
|
270
|
+
createdAt: string;
|
|
271
|
+
integrity: { algorithm: "sha256"; digest: string };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export type PendingGateDecisionRecord = PendingGateOutputRecord | PendingGateArtifactRecord;
|
|
275
|
+
|
|
276
|
+
export interface PendingGateDecisionHandle {
|
|
277
|
+
record: PendingGateDecisionRecord;
|
|
278
|
+
path: string;
|
|
279
|
+
stateDir: string;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface PendingGateDecisionReadResult {
|
|
283
|
+
records: PendingGateDecisionHandle[];
|
|
284
|
+
errors: Array<{ path: string; message: string }>;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface PendingGateDecisionRecoveryPlan {
|
|
288
|
+
/** Resolved records that passed final-artifact collision checks but are not materialized yet. */
|
|
289
|
+
ready: PendingGateDecisionHandle[];
|
|
290
|
+
/** Output-first records need the caller's protocol/policy parser and receipt digest. */
|
|
291
|
+
unresolved: PendingGateDecisionHandle[];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface StagePendingGateDecisionOptions {
|
|
295
|
+
/** Exact final assistant output, retained until the artifact is durable. */
|
|
296
|
+
finalOutput?: string;
|
|
297
|
+
resourceRoot?: string;
|
|
298
|
+
stateDir?: string;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function decisionsDirectory(stateDir = clioStateDir()): string {
|
|
302
|
+
return join(stateDir, "gate-decisions");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function pendingDecisionsDirectory(stateDir = clioStateDir()): string {
|
|
306
|
+
return join(decisionsDirectory(stateDir), "pending");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
310
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function isSubjectRef(value: unknown): value is RunGateSubjectRef {
|
|
314
|
+
if (!isRecord(value) || typeof value.runId !== "string" || value.runId.trim().length === 0) return false;
|
|
315
|
+
return typeof value.digest === "string" && /^[0-9a-f]{64}$/.test(value.digest);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function isSafeDecisionId(value: unknown): value is string {
|
|
319
|
+
return typeof value === "string" && value.length > 0 && value.length <= 256 && /^[A-Za-z0-9._-]+$/.test(value);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function isCorrelation(value: unknown): value is GateDecisionCorrelation {
|
|
323
|
+
if (!isRecord(value)) return false;
|
|
324
|
+
const fields = ["agent", "target", "modelFamily", "runtime", "node", "independent"] as const;
|
|
325
|
+
if (Object.keys(value).some((key) => !(fields as ReadonlyArray<string>).includes(key))) return false;
|
|
326
|
+
if (!fields.every((field) => typeof value[field] === "boolean")) return false;
|
|
327
|
+
// The independence rule is part of the sealed fact, not the writer's opinion:
|
|
328
|
+
// a shared agent or model family is what makes a verdict self-confirming.
|
|
329
|
+
return value.independent === (!value.agent && !value.modelFamily);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function semanticError(value: unknown): string | null {
|
|
333
|
+
if (!isRecord(value)) return "gate decision is not an object";
|
|
334
|
+
if (value.version !== 2) return "unsupported gate decision version";
|
|
335
|
+
if (!isSafeDecisionId(value.id)) return "gate decision id invalid";
|
|
336
|
+
if (typeof value.group !== "string" || value.group.length === 0) return "gate decision group invalid";
|
|
337
|
+
if (value.topology !== "review" && value.topology !== "compete") return "gate decision topology invalid";
|
|
338
|
+
if (typeof value.cycle !== "number" || !Number.isInteger(value.cycle) || value.cycle < 1) {
|
|
339
|
+
return "gate decision cycle invalid";
|
|
340
|
+
}
|
|
341
|
+
const reviewOutcome =
|
|
342
|
+
value.outcome === "pass" || value.outcome === "fail" || value.outcome === "revise" || value.outcome === "exhausted";
|
|
343
|
+
const competeOutcome =
|
|
344
|
+
value.outcome === "winner" ||
|
|
345
|
+
value.outcome === "no-winner" ||
|
|
346
|
+
value.outcome === "operator-confirmed" ||
|
|
347
|
+
value.outcome === "full-auto-applied";
|
|
348
|
+
if ((value.topology === "review" && !reviewOutcome) || (value.topology === "compete" && !competeOutcome)) {
|
|
349
|
+
return "gate decision outcome is incompatible with topology";
|
|
350
|
+
}
|
|
351
|
+
if (!Array.isArray(value.subjects) || value.subjects.length === 0 || !value.subjects.every(isSubjectRef)) {
|
|
352
|
+
return "gate decision subjects invalid";
|
|
353
|
+
}
|
|
354
|
+
if (value.decider !== undefined && !isSubjectRef(value.decider)) return "gate decision decider invalid";
|
|
355
|
+
if (value.decider === undefined) {
|
|
356
|
+
if (value.correlation !== undefined) return "gate decision correlation requires a decider";
|
|
357
|
+
} else if (!isCorrelation(value.correlation)) {
|
|
358
|
+
return "gate decision correlation invalid";
|
|
359
|
+
}
|
|
360
|
+
if (typeof value.createdAt !== "string" || !Number.isFinite(Date.parse(value.createdAt))) {
|
|
361
|
+
return "gate decision timestamp invalid";
|
|
362
|
+
}
|
|
363
|
+
const needsWinner =
|
|
364
|
+
value.outcome === "winner" || value.outcome === "operator-confirmed" || value.outcome === "full-auto-applied";
|
|
365
|
+
if (needsWinner) {
|
|
366
|
+
if (
|
|
367
|
+
!isRecord(value.winner) ||
|
|
368
|
+
typeof value.winner.index !== "number" ||
|
|
369
|
+
!Number.isInteger(value.winner.index) ||
|
|
370
|
+
value.winner.index < 1 ||
|
|
371
|
+
!isSubjectRef(value.winner.subject) ||
|
|
372
|
+
typeof value.winner.branch !== "string" ||
|
|
373
|
+
value.winner.branch !== `clio/compete/${value.group}/${value.winner.index}`
|
|
374
|
+
) {
|
|
375
|
+
return "gate decision winner invalid";
|
|
376
|
+
}
|
|
377
|
+
const winnerSubject = value.winner.subject as RunGateSubjectRef;
|
|
378
|
+
if (!value.subjects.some((subject) => subject.runId === winnerSubject.runId)) {
|
|
379
|
+
return "gate decision winner is not a subject";
|
|
380
|
+
}
|
|
381
|
+
} else if (value.winner !== undefined) {
|
|
382
|
+
return "gate decision winner is not valid for this outcome";
|
|
383
|
+
}
|
|
384
|
+
const needsConfirmation = value.outcome === "operator-confirmed" || value.outcome === "full-auto-applied";
|
|
385
|
+
if (needsConfirmation) {
|
|
386
|
+
if (
|
|
387
|
+
!isRecord(value.confirmation) ||
|
|
388
|
+
typeof value.confirmation.id !== "string" ||
|
|
389
|
+
value.confirmation.id.length === 0 ||
|
|
390
|
+
typeof value.confirmation.digest !== "string" ||
|
|
391
|
+
!/^[0-9a-f]{64}$/.test(value.confirmation.digest)
|
|
392
|
+
) {
|
|
393
|
+
return "gate decision confirmation invalid";
|
|
394
|
+
}
|
|
395
|
+
} else if (value.confirmation !== undefined) {
|
|
396
|
+
return "gate decision confirmation is not valid for this outcome";
|
|
397
|
+
}
|
|
398
|
+
if (value.detail !== undefined && typeof value.detail !== "string") return "gate decision detail invalid";
|
|
399
|
+
if (
|
|
400
|
+
!isRecord(value.integrity) ||
|
|
401
|
+
value.integrity.algorithm !== "sha256" ||
|
|
402
|
+
typeof value.integrity.digest !== "string"
|
|
403
|
+
) {
|
|
404
|
+
return "gate decision integrity invalid";
|
|
405
|
+
}
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function safeGroup(group: string): string {
|
|
410
|
+
const safe = group.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 128);
|
|
411
|
+
return safe.length > 0 ? safe : "gate";
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function newDecisionId(group: string): string {
|
|
415
|
+
return `${safeGroup(group)}-${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function decisionArtifactPath(id: string, stateDir = clioStateDir()): string {
|
|
419
|
+
return join(decisionsDirectory(stateDir), `${id}.json`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function pendingDecisionPath(id: string, stateDir = clioStateDir()): string {
|
|
423
|
+
return join(pendingDecisionsDirectory(stateDir), `${id}.json`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function decisionPayload(decision: Omit<GateDecisionArtifact, "integrity">): Record<string, unknown> {
|
|
427
|
+
return {
|
|
428
|
+
contract: "clio.gateDecision.integrity",
|
|
429
|
+
version: decision.version,
|
|
430
|
+
id: decision.id,
|
|
431
|
+
group: decision.group,
|
|
432
|
+
topology: decision.topology,
|
|
433
|
+
cycle: decision.cycle,
|
|
434
|
+
outcome: decision.outcome,
|
|
435
|
+
subjects: decision.subjects.map((subject) => ({ runId: subject.runId, digest: subject.digest })),
|
|
436
|
+
...(decision.decider !== undefined
|
|
437
|
+
? { decider: { runId: decision.decider.runId, digest: decision.decider.digest } }
|
|
438
|
+
: {}),
|
|
439
|
+
...(decision.correlation !== undefined ? { correlation: { ...decision.correlation } } : {}),
|
|
440
|
+
...(decision.winner !== undefined
|
|
441
|
+
? {
|
|
442
|
+
winner: {
|
|
443
|
+
index: decision.winner.index,
|
|
444
|
+
subject: {
|
|
445
|
+
runId: decision.winner.subject.runId,
|
|
446
|
+
digest: decision.winner.subject.digest,
|
|
447
|
+
},
|
|
448
|
+
branch: decision.winner.branch,
|
|
449
|
+
},
|
|
450
|
+
}
|
|
451
|
+
: {}),
|
|
452
|
+
...(decision.confirmation !== undefined
|
|
453
|
+
? { confirmation: { id: decision.confirmation.id, digest: decision.confirmation.digest } }
|
|
454
|
+
: {}),
|
|
455
|
+
...(decision.detail !== undefined ? { detail: decision.detail } : {}),
|
|
456
|
+
createdAt: decision.createdAt,
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function decisionDigest(decision: Omit<GateDecisionArtifact, "integrity">): string {
|
|
461
|
+
return createHash("sha256")
|
|
462
|
+
.update(JSON.stringify(decisionPayload(decision)), "utf8")
|
|
463
|
+
.digest("hex");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function artifactWithoutIntegrity(artifact: GateDecisionArtifact): Omit<GateDecisionArtifact, "integrity"> {
|
|
467
|
+
const result: Omit<GateDecisionArtifact, "integrity"> = {
|
|
468
|
+
version: artifact.version,
|
|
469
|
+
id: artifact.id,
|
|
470
|
+
group: artifact.group,
|
|
471
|
+
topology: artifact.topology,
|
|
472
|
+
cycle: artifact.cycle,
|
|
473
|
+
outcome: artifact.outcome,
|
|
474
|
+
subjects: artifact.subjects.map((subject) => ({ ...subject })),
|
|
475
|
+
createdAt: artifact.createdAt,
|
|
476
|
+
};
|
|
477
|
+
if (artifact.decider !== undefined) result.decider = { ...artifact.decider };
|
|
478
|
+
if (artifact.correlation !== undefined) result.correlation = { ...artifact.correlation };
|
|
479
|
+
if (artifact.winner !== undefined) {
|
|
480
|
+
result.winner = {
|
|
481
|
+
index: artifact.winner.index,
|
|
482
|
+
subject: { ...artifact.winner.subject },
|
|
483
|
+
branch: artifact.winner.branch,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
if (artifact.confirmation !== undefined) result.confirmation = { ...artifact.confirmation };
|
|
487
|
+
if (artifact.detail !== undefined) result.detail = artifact.detail;
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export function verifyGateDecisionArtifact(artifact: GateDecisionArtifact): GateDecisionVerification {
|
|
492
|
+
const invalid = semanticError(artifact);
|
|
493
|
+
if (invalid !== null) return { ok: false, reason: invalid };
|
|
494
|
+
if (!/^[0-9a-f]{64}$/.test(artifact.integrity.digest)) return { ok: false, reason: "gate decision integrity invalid" };
|
|
495
|
+
return decisionDigest(artifactWithoutIntegrity(artifact)) === artifact.integrity.digest
|
|
496
|
+
? { ok: true }
|
|
497
|
+
: { ok: false, reason: "gate decision integrity mismatch" };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function buildGateDecisionArtifact(
|
|
501
|
+
draft: GateDecisionDraft,
|
|
502
|
+
id = newDecisionId(draft.group),
|
|
503
|
+
createdAt = draft.createdAt ?? new Date().toISOString(),
|
|
504
|
+
): GateDecisionArtifact {
|
|
505
|
+
if (draft.group.trim().length === 0) throw new Error("gate decision group is required");
|
|
506
|
+
if (!Number.isInteger(draft.cycle) || draft.cycle < 1)
|
|
507
|
+
throw new Error("gate decision cycle must be a positive integer");
|
|
508
|
+
if (draft.subjects.length === 0) throw new Error("gate decision requires at least one subject receipt");
|
|
509
|
+
const withoutIntegrity: Omit<GateDecisionArtifact, "integrity"> = {
|
|
510
|
+
version: 2,
|
|
511
|
+
id,
|
|
512
|
+
group: draft.group,
|
|
513
|
+
topology: draft.topology,
|
|
514
|
+
cycle: draft.cycle,
|
|
515
|
+
outcome: draft.outcome,
|
|
516
|
+
subjects: draft.subjects.map((subject) => ({ ...subject })),
|
|
517
|
+
createdAt,
|
|
518
|
+
};
|
|
519
|
+
if (draft.decider !== undefined) withoutIntegrity.decider = { ...draft.decider };
|
|
520
|
+
if (draft.correlation !== undefined) withoutIntegrity.correlation = { ...draft.correlation };
|
|
521
|
+
if (draft.winner !== undefined) {
|
|
522
|
+
withoutIntegrity.winner = {
|
|
523
|
+
index: draft.winner.index,
|
|
524
|
+
subject: { ...draft.winner.subject },
|
|
525
|
+
branch: draft.winner.branch,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
if (draft.confirmation !== undefined) withoutIntegrity.confirmation = { ...draft.confirmation };
|
|
529
|
+
if (draft.detail !== undefined) withoutIntegrity.detail = draft.detail;
|
|
530
|
+
const artifact: GateDecisionArtifact = {
|
|
531
|
+
...withoutIntegrity,
|
|
532
|
+
integrity: { algorithm: "sha256", digest: decisionDigest(withoutIntegrity) },
|
|
533
|
+
};
|
|
534
|
+
const verification = verifyGateDecisionArtifact(artifact);
|
|
535
|
+
if (!verification.ok) throw new Error(verification.reason);
|
|
536
|
+
return artifact;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function readVerifiedArtifact(path: string): GateDecisionArtifact {
|
|
540
|
+
let value: unknown;
|
|
541
|
+
try {
|
|
542
|
+
value = JSON.parse(readFileSync(path, "utf8")) as unknown;
|
|
543
|
+
} catch (error) {
|
|
544
|
+
throw new Error(
|
|
545
|
+
`gate decision artifact is unreadable at ${path}: ${error instanceof Error ? error.message : String(error)}`,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
const artifact = value as GateDecisionArtifact;
|
|
549
|
+
const verification = verifyGateDecisionArtifact(artifact);
|
|
550
|
+
if (!verification.ok) throw new Error(`gate decision artifact is invalid at ${path}: ${verification.reason}`);
|
|
551
|
+
return artifact;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function persistGateDecisionArtifact(
|
|
555
|
+
artifact: GateDecisionArtifact,
|
|
556
|
+
stateDir: string,
|
|
557
|
+
allowIdenticalExisting: boolean,
|
|
558
|
+
): { artifact: GateDecisionArtifact; path: string } {
|
|
559
|
+
const verification = verifyGateDecisionArtifact(artifact);
|
|
560
|
+
if (!verification.ok) throw new Error(verification.reason);
|
|
561
|
+
const directory = decisionsDirectory(stateDir);
|
|
562
|
+
mkdirSync(directory, { recursive: true });
|
|
563
|
+
const path = decisionArtifactPath(artifact.id, stateDir);
|
|
564
|
+
if (existsSync(path)) {
|
|
565
|
+
const existing = readVerifiedArtifact(path);
|
|
566
|
+
if (
|
|
567
|
+
allowIdenticalExisting &&
|
|
568
|
+
existing.id === artifact.id &&
|
|
569
|
+
existing.integrity.digest === artifact.integrity.digest
|
|
570
|
+
) {
|
|
571
|
+
return { artifact: existing, path };
|
|
572
|
+
}
|
|
573
|
+
throw new Error(`gate decision artifact collision: ${artifact.id}`);
|
|
574
|
+
}
|
|
575
|
+
atomicWrite(path, JSON.stringify(artifact, null, 2));
|
|
576
|
+
return { artifact, path };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
type PendingGateRecordWithoutIntegrity =
|
|
580
|
+
| Omit<PendingGateOutputRecord, "integrity">
|
|
581
|
+
| Omit<PendingGateArtifactRecord, "integrity">;
|
|
582
|
+
|
|
583
|
+
function cloneGateDecisionArtifact(artifact: GateDecisionArtifact): GateDecisionArtifact {
|
|
584
|
+
return {
|
|
585
|
+
...artifactWithoutIntegrity(artifact),
|
|
586
|
+
integrity: { ...artifact.integrity },
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function pendingRecordPayload(record: PendingGateRecordWithoutIntegrity): Record<string, unknown> {
|
|
591
|
+
if (record.kind === "output") {
|
|
592
|
+
return {
|
|
593
|
+
contract: "clio.gateDecision.pending",
|
|
594
|
+
version: record.version,
|
|
595
|
+
kind: record.kind,
|
|
596
|
+
id: record.id,
|
|
597
|
+
group: record.group,
|
|
598
|
+
topology: record.topology,
|
|
599
|
+
cycle: record.cycle,
|
|
600
|
+
subjects: record.subjects.map((subject) => ({ runId: subject.runId, digest: subject.digest })),
|
|
601
|
+
deciderRunId: record.deciderRunId,
|
|
602
|
+
finalOutput: record.finalOutput,
|
|
603
|
+
...(record.terminalCycle !== undefined ? { terminalCycle: record.terminalCycle } : {}),
|
|
604
|
+
...(record.resourceRoot !== undefined ? { resourceRoot: record.resourceRoot } : {}),
|
|
605
|
+
createdAt: record.createdAt,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
const decisionWithoutIntegrity = artifactWithoutIntegrity(record.decision);
|
|
609
|
+
return {
|
|
610
|
+
contract: "clio.gateDecision.pending",
|
|
611
|
+
version: record.version,
|
|
612
|
+
kind: record.kind,
|
|
613
|
+
id: record.id,
|
|
614
|
+
decision: {
|
|
615
|
+
...decisionPayload(decisionWithoutIntegrity),
|
|
616
|
+
integrity: { ...record.decision.integrity },
|
|
617
|
+
},
|
|
618
|
+
...(record.finalOutput !== undefined ? { finalOutput: record.finalOutput } : {}),
|
|
619
|
+
...(record.resourceRoot !== undefined ? { resourceRoot: record.resourceRoot } : {}),
|
|
620
|
+
createdAt: record.createdAt,
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function pendingRecordDigest(record: PendingGateRecordWithoutIntegrity): string {
|
|
625
|
+
return createHash("sha256")
|
|
626
|
+
.update(JSON.stringify(pendingRecordPayload(record)), "utf8")
|
|
627
|
+
.digest("hex");
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function pendingRecordWithoutIntegrity(record: PendingGateDecisionRecord): PendingGateRecordWithoutIntegrity {
|
|
631
|
+
if (record.kind === "output") {
|
|
632
|
+
return {
|
|
633
|
+
version: record.version,
|
|
634
|
+
kind: record.kind,
|
|
635
|
+
id: record.id,
|
|
636
|
+
group: record.group,
|
|
637
|
+
topology: record.topology,
|
|
638
|
+
cycle: record.cycle,
|
|
639
|
+
subjects: record.subjects.map((subject) => ({ ...subject })),
|
|
640
|
+
deciderRunId: record.deciderRunId,
|
|
641
|
+
finalOutput: record.finalOutput,
|
|
642
|
+
...(record.terminalCycle !== undefined ? { terminalCycle: record.terminalCycle } : {}),
|
|
643
|
+
...(record.resourceRoot !== undefined ? { resourceRoot: record.resourceRoot } : {}),
|
|
644
|
+
createdAt: record.createdAt,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
version: record.version,
|
|
649
|
+
kind: record.kind,
|
|
650
|
+
id: record.id,
|
|
651
|
+
decision: cloneGateDecisionArtifact(record.decision),
|
|
652
|
+
...(record.finalOutput !== undefined ? { finalOutput: record.finalOutput } : {}),
|
|
653
|
+
...(record.resourceRoot !== undefined ? { resourceRoot: record.resourceRoot } : {}),
|
|
654
|
+
createdAt: record.createdAt,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function pendingSemanticError(value: unknown): string | null {
|
|
659
|
+
if (!isRecord(value)) return "pending gate decision is not an object";
|
|
660
|
+
if (value.version !== 1) return "unsupported pending gate decision version";
|
|
661
|
+
if (!isSafeDecisionId(value.id)) return "pending gate decision id invalid";
|
|
662
|
+
if (typeof value.createdAt !== "string" || !Number.isFinite(Date.parse(value.createdAt))) {
|
|
663
|
+
return "pending gate decision timestamp invalid";
|
|
664
|
+
}
|
|
665
|
+
if (
|
|
666
|
+
!isRecord(value.integrity) ||
|
|
667
|
+
value.integrity.algorithm !== "sha256" ||
|
|
668
|
+
typeof value.integrity.digest !== "string" ||
|
|
669
|
+
!/^[0-9a-f]{64}$/.test(value.integrity.digest)
|
|
670
|
+
) {
|
|
671
|
+
return "pending gate decision integrity invalid";
|
|
672
|
+
}
|
|
673
|
+
if (value.kind === "output") {
|
|
674
|
+
if (typeof value.group !== "string" || value.group.trim().length === 0) {
|
|
675
|
+
return "pending gate output group invalid";
|
|
676
|
+
}
|
|
677
|
+
if (value.topology !== "review" && value.topology !== "compete") {
|
|
678
|
+
return "pending gate output topology invalid";
|
|
679
|
+
}
|
|
680
|
+
if (typeof value.cycle !== "number" || !Number.isInteger(value.cycle) || value.cycle < 1) {
|
|
681
|
+
return "pending gate output cycle invalid";
|
|
682
|
+
}
|
|
683
|
+
if (!Array.isArray(value.subjects) || value.subjects.length === 0 || !value.subjects.every(isSubjectRef)) {
|
|
684
|
+
return "pending gate output subjects invalid";
|
|
685
|
+
}
|
|
686
|
+
if (typeof value.deciderRunId !== "string" || value.deciderRunId.trim().length === 0) {
|
|
687
|
+
return "pending gate output decider run id invalid";
|
|
688
|
+
}
|
|
689
|
+
if (typeof value.finalOutput !== "string") return "pending gate output text invalid";
|
|
690
|
+
if (value.terminalCycle !== undefined && typeof value.terminalCycle !== "boolean") {
|
|
691
|
+
return "pending gate output terminal-cycle marker invalid";
|
|
692
|
+
}
|
|
693
|
+
if (value.resourceRoot !== undefined && (typeof value.resourceRoot !== "string" || value.resourceRoot.length === 0)) {
|
|
694
|
+
return "pending gate output resource root invalid";
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
if (value.kind !== "decision") return "pending gate decision kind invalid";
|
|
699
|
+
if (!isRecord(value.decision)) return "pending gate decision artifact invalid";
|
|
700
|
+
const verification = verifyGateDecisionArtifact(value.decision as unknown as GateDecisionArtifact);
|
|
701
|
+
if (!verification.ok) return `pending gate decision artifact invalid: ${verification.reason}`;
|
|
702
|
+
if (value.decision.id !== value.id) return "pending gate decision artifact id mismatch";
|
|
703
|
+
if (value.decision.createdAt !== value.createdAt) return "pending gate decision artifact timestamp mismatch";
|
|
704
|
+
if (value.finalOutput !== undefined && typeof value.finalOutput !== "string") {
|
|
705
|
+
return "pending gate decision output invalid";
|
|
706
|
+
}
|
|
707
|
+
if (value.resourceRoot !== undefined && (typeof value.resourceRoot !== "string" || value.resourceRoot.length === 0)) {
|
|
708
|
+
return "pending gate decision resource root invalid";
|
|
709
|
+
}
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/** Verify both the pending envelope and its nested final artifact, when resolved. */
|
|
714
|
+
export function verifyPendingGateDecisionRecord(record: PendingGateDecisionRecord): GateDecisionVerification {
|
|
715
|
+
const invalid = pendingSemanticError(record);
|
|
716
|
+
if (invalid !== null) return { ok: false, reason: invalid };
|
|
717
|
+
return pendingRecordDigest(pendingRecordWithoutIntegrity(record)) === record.integrity.digest
|
|
718
|
+
? { ok: true }
|
|
719
|
+
: { ok: false, reason: "pending gate decision integrity mismatch" };
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function clonePendingGateDecisionRecord(record: PendingGateDecisionRecord): PendingGateDecisionRecord {
|
|
723
|
+
const without = pendingRecordWithoutIntegrity(record);
|
|
724
|
+
return {
|
|
725
|
+
...without,
|
|
726
|
+
integrity: { ...record.integrity },
|
|
727
|
+
} as PendingGateDecisionRecord;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function parsePendingGateDecisionRecord(value: unknown): PendingGateDecisionRecord {
|
|
731
|
+
const invalid = pendingSemanticError(value);
|
|
732
|
+
if (invalid !== null) throw new Error(invalid);
|
|
733
|
+
const record = clonePendingGateDecisionRecord(value as PendingGateDecisionRecord);
|
|
734
|
+
const verification = verifyPendingGateDecisionRecord(record);
|
|
735
|
+
if (!verification.ok) throw new Error(verification.reason);
|
|
736
|
+
return record;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function assertPendingHandlePath(handle: PendingGateDecisionHandle): void {
|
|
740
|
+
const expected = pendingDecisionPath(handle.record.id, handle.stateDir);
|
|
741
|
+
if (resolve(handle.path) !== resolve(expected)) {
|
|
742
|
+
throw new Error(`pending gate decision path does not match id ${handle.record.id}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function pendingHandle(record: PendingGateDecisionRecord, stateDir: string): PendingGateDecisionHandle {
|
|
747
|
+
return {
|
|
748
|
+
record: clonePendingGateDecisionRecord(record),
|
|
749
|
+
path: pendingDecisionPath(record.id, stateDir),
|
|
750
|
+
stateDir,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function persistPendingGateDecision(
|
|
755
|
+
record: PendingGateDecisionRecord,
|
|
756
|
+
stateDir: string,
|
|
757
|
+
allowReplace: boolean,
|
|
758
|
+
): PendingGateDecisionHandle {
|
|
759
|
+
const verification = verifyPendingGateDecisionRecord(record);
|
|
760
|
+
if (!verification.ok) throw new Error(verification.reason);
|
|
761
|
+
const path = pendingDecisionPath(record.id, stateDir);
|
|
762
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
763
|
+
if (!allowReplace && existsSync(path)) throw new Error(`pending gate decision collision: ${record.id}`);
|
|
764
|
+
atomicWrite(path, JSON.stringify(record, null, 2));
|
|
765
|
+
return pendingHandle(record, stateDir);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function loadPendingGateDecision(handle: PendingGateDecisionHandle): PendingGateDecisionHandle | null {
|
|
769
|
+
assertPendingHandlePath(handle);
|
|
770
|
+
if (!existsSync(handle.path)) return null;
|
|
771
|
+
const record = parsePendingGateDecisionRecord(JSON.parse(readFileSync(handle.path, "utf8")) as unknown);
|
|
772
|
+
if (record.id !== handle.record.id) throw new Error("pending gate decision changed identity on disk");
|
|
773
|
+
return pendingHandle(record, handle.stateDir);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function fsyncDirectoryBestEffort(path: string): void {
|
|
777
|
+
let fd: number | null = null;
|
|
778
|
+
try {
|
|
779
|
+
fd = openSync(path, "r");
|
|
780
|
+
fsyncSync(fd);
|
|
781
|
+
} catch {
|
|
782
|
+
// Some filesystems/platforms reject directory fsync. A resurrected
|
|
783
|
+
// pending record is harmless because materialization is idempotent.
|
|
784
|
+
} finally {
|
|
785
|
+
if (fd !== null) closeSync(fd);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function clearPendingGateDecision(handle: PendingGateDecisionHandle): void {
|
|
790
|
+
assertPendingHandlePath(handle);
|
|
791
|
+
rmSync(handle.path, { force: true });
|
|
792
|
+
fsyncDirectoryBestEffort(dirname(handle.path));
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function sameSubjectRefs(left: ReadonlyArray<RunGateSubjectRef>, right: ReadonlyArray<RunGateSubjectRef>): boolean {
|
|
796
|
+
return (
|
|
797
|
+
left.length === right.length &&
|
|
798
|
+
left.every((subject, index) => {
|
|
799
|
+
const other = right[index];
|
|
800
|
+
return other !== undefined && subject.runId === other.runId && subject.digest === other.digest;
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Cross the output-loss boundary before waiting for the decider receipt. The
|
|
807
|
+
* returned handle is later resolved with that receipt's authenticated digest.
|
|
808
|
+
*/
|
|
809
|
+
export function stagePendingGateOutput(
|
|
810
|
+
draft: PendingGateOutputDraft,
|
|
811
|
+
stateDir = clioStateDir(),
|
|
812
|
+
): PendingGateDecisionHandle {
|
|
813
|
+
const id = newDecisionId(draft.group);
|
|
814
|
+
const withoutIntegrity: Omit<PendingGateOutputRecord, "integrity"> = {
|
|
815
|
+
version: 1,
|
|
816
|
+
kind: "output",
|
|
817
|
+
id,
|
|
818
|
+
group: draft.group,
|
|
819
|
+
topology: draft.topology,
|
|
820
|
+
cycle: draft.cycle,
|
|
821
|
+
subjects: draft.subjects.map((subject) => ({ ...subject })),
|
|
822
|
+
deciderRunId: draft.deciderRunId,
|
|
823
|
+
finalOutput: draft.finalOutput,
|
|
824
|
+
...(draft.terminalCycle !== undefined ? { terminalCycle: draft.terminalCycle } : {}),
|
|
825
|
+
...(draft.resourceRoot !== undefined ? { resourceRoot: draft.resourceRoot } : {}),
|
|
826
|
+
createdAt: draft.createdAt ?? new Date().toISOString(),
|
|
827
|
+
};
|
|
828
|
+
const record: PendingGateOutputRecord = {
|
|
829
|
+
...withoutIntegrity,
|
|
830
|
+
integrity: { algorithm: "sha256", digest: pendingRecordDigest(withoutIntegrity) },
|
|
831
|
+
};
|
|
832
|
+
if (existsSync(decisionArtifactPath(id, stateDir))) throw new Error(`gate decision artifact collision: ${id}`);
|
|
833
|
+
return persistPendingGateDecision(record, stateDir, false);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/** Stage a fully parsed decision before attempting its final artifact write. */
|
|
837
|
+
export function stagePendingGateDecision(
|
|
838
|
+
draft: GateDecisionDraft,
|
|
839
|
+
options: StagePendingGateDecisionOptions = {},
|
|
840
|
+
): PendingGateDecisionHandle {
|
|
841
|
+
const stateDir = options.stateDir ?? clioStateDir();
|
|
842
|
+
const id = newDecisionId(draft.group);
|
|
843
|
+
const createdAt = draft.createdAt ?? new Date().toISOString();
|
|
844
|
+
const decision = buildGateDecisionArtifact(draft, id, createdAt);
|
|
845
|
+
const withoutIntegrity: Omit<PendingGateArtifactRecord, "integrity"> = {
|
|
846
|
+
version: 1,
|
|
847
|
+
kind: "decision",
|
|
848
|
+
id,
|
|
849
|
+
decision,
|
|
850
|
+
...(options.finalOutput !== undefined ? { finalOutput: options.finalOutput } : {}),
|
|
851
|
+
...(options.resourceRoot !== undefined ? { resourceRoot: options.resourceRoot } : {}),
|
|
852
|
+
createdAt,
|
|
853
|
+
};
|
|
854
|
+
const record: PendingGateArtifactRecord = {
|
|
855
|
+
...withoutIntegrity,
|
|
856
|
+
integrity: { algorithm: "sha256", digest: pendingRecordDigest(withoutIntegrity) },
|
|
857
|
+
};
|
|
858
|
+
if (existsSync(decisionArtifactPath(id, stateDir))) throw new Error(`gate decision artifact collision: ${id}`);
|
|
859
|
+
return persistPendingGateDecision(record, stateDir, false);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Bind a staged reviewer/judge output to its now-sealed receipt and parsed
|
|
864
|
+
* outcome. The decision-bearing replacement is atomic; either the raw output
|
|
865
|
+
* or the complete artifact survives a crash.
|
|
866
|
+
*/
|
|
867
|
+
export function resolvePendingGateDecision(
|
|
868
|
+
handle: PendingGateDecisionHandle,
|
|
869
|
+
draft: GateDecisionDraft,
|
|
870
|
+
): PendingGateDecisionHandle {
|
|
871
|
+
const loaded = loadPendingGateDecision(handle);
|
|
872
|
+
if (loaded === null) throw new Error(`pending gate decision is missing: ${handle.record.id}`);
|
|
873
|
+
if (loaded.record.kind === "decision") {
|
|
874
|
+
const expected = buildGateDecisionArtifact(draft, loaded.record.id, loaded.record.createdAt);
|
|
875
|
+
if (expected.integrity.digest !== loaded.record.decision.integrity.digest) {
|
|
876
|
+
throw new Error("pending gate decision was already resolved to a different artifact");
|
|
877
|
+
}
|
|
878
|
+
return loaded;
|
|
879
|
+
}
|
|
880
|
+
const pending = loaded.record;
|
|
881
|
+
if (
|
|
882
|
+
draft.group !== pending.group ||
|
|
883
|
+
draft.topology !== pending.topology ||
|
|
884
|
+
draft.cycle !== pending.cycle ||
|
|
885
|
+
!sameSubjectRefs(draft.subjects, pending.subjects)
|
|
886
|
+
) {
|
|
887
|
+
throw new Error("resolved gate decision does not match staged output context");
|
|
888
|
+
}
|
|
889
|
+
if (draft.decider === undefined || draft.decider.runId !== pending.deciderRunId) {
|
|
890
|
+
throw new Error("resolved gate decision does not match staged decider run");
|
|
891
|
+
}
|
|
892
|
+
if (draft.createdAt !== undefined && draft.createdAt !== pending.createdAt) {
|
|
893
|
+
throw new Error("resolved gate decision cannot change the staged timestamp");
|
|
894
|
+
}
|
|
895
|
+
const decision = buildGateDecisionArtifact(draft, pending.id, pending.createdAt);
|
|
896
|
+
const withoutIntegrity: Omit<PendingGateArtifactRecord, "integrity"> = {
|
|
897
|
+
version: 1,
|
|
898
|
+
kind: "decision",
|
|
899
|
+
id: pending.id,
|
|
900
|
+
decision,
|
|
901
|
+
finalOutput: pending.finalOutput,
|
|
902
|
+
...(pending.resourceRoot !== undefined ? { resourceRoot: pending.resourceRoot } : {}),
|
|
903
|
+
createdAt: pending.createdAt,
|
|
904
|
+
};
|
|
905
|
+
const resolvedRecord: PendingGateArtifactRecord = {
|
|
906
|
+
...withoutIntegrity,
|
|
907
|
+
integrity: { algorithm: "sha256", digest: pendingRecordDigest(withoutIntegrity) },
|
|
908
|
+
};
|
|
909
|
+
return persistPendingGateDecision(resolvedRecord, loaded.stateDir, true);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/** Write/reuse the reserved final artifact, then durably remove its WAL record. */
|
|
913
|
+
export function materializePendingGateDecision(handle: PendingGateDecisionHandle): {
|
|
914
|
+
artifact: GateDecisionArtifact;
|
|
915
|
+
path: string;
|
|
916
|
+
} {
|
|
917
|
+
const loaded = loadPendingGateDecision(handle);
|
|
918
|
+
if (loaded === null) {
|
|
919
|
+
if (handle.record.kind !== "decision") {
|
|
920
|
+
throw new Error(`pending gate output is missing before resolution: ${handle.record.id}`);
|
|
921
|
+
}
|
|
922
|
+
const path = decisionArtifactPath(handle.record.decision.id, handle.stateDir);
|
|
923
|
+
if (!existsSync(path)) throw new Error(`pending gate decision and artifact are both missing: ${handle.record.id}`);
|
|
924
|
+
const artifact = readVerifiedArtifact(path);
|
|
925
|
+
if (artifact.integrity.digest !== handle.record.decision.integrity.digest) {
|
|
926
|
+
throw new Error(`gate decision artifact conflicts with completed pending record: ${handle.record.id}`);
|
|
927
|
+
}
|
|
928
|
+
return { artifact, path };
|
|
929
|
+
}
|
|
930
|
+
if (loaded.record.kind !== "decision") {
|
|
931
|
+
throw new Error(`pending gate output requires receipt resolution: ${loaded.record.id}`);
|
|
932
|
+
}
|
|
933
|
+
const written = persistGateDecisionArtifact(loaded.record.decision, loaded.stateDir, true);
|
|
934
|
+
const persisted = readVerifiedArtifact(written.path);
|
|
935
|
+
if (persisted.integrity.digest !== loaded.record.decision.integrity.digest) {
|
|
936
|
+
throw new Error(`materialized gate decision does not match pending record: ${loaded.record.id}`);
|
|
937
|
+
}
|
|
938
|
+
clearPendingGateDecision(loaded);
|
|
939
|
+
return { artifact: persisted, path: written.path };
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/** Resolve an output-first record, materialize its artifact, and clear the WAL. */
|
|
943
|
+
export function finalizePendingGateDecision(
|
|
944
|
+
handle: PendingGateDecisionHandle,
|
|
945
|
+
draft: GateDecisionDraft,
|
|
946
|
+
): { artifact: GateDecisionArtifact; path: string } {
|
|
947
|
+
return materializePendingGateDecision(resolvePendingGateDecision(handle, draft));
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
export function readPendingGateDecisions(stateDir = clioStateDir()): PendingGateDecisionReadResult {
|
|
951
|
+
const directory = pendingDecisionsDirectory(stateDir);
|
|
952
|
+
if (!existsSync(directory)) return { records: [], errors: [] };
|
|
953
|
+
const records: PendingGateDecisionHandle[] = [];
|
|
954
|
+
const errors: PendingGateDecisionReadResult["errors"] = [];
|
|
955
|
+
for (const name of readdirSync(directory)
|
|
956
|
+
.filter((entry) => entry.endsWith(".json"))
|
|
957
|
+
.sort()) {
|
|
958
|
+
const path = join(directory, name);
|
|
959
|
+
try {
|
|
960
|
+
const record = parsePendingGateDecisionRecord(JSON.parse(readFileSync(path, "utf8")) as unknown);
|
|
961
|
+
if (name !== `${record.id}.json`) throw new Error("pending gate decision filename does not match its id");
|
|
962
|
+
records.push({ record, path, stateDir });
|
|
963
|
+
} catch (error) {
|
|
964
|
+
errors.push({ path, message: error instanceof Error ? error.message : String(error) });
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return { records, errors };
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/** Fail closed on any final artifact that conflicts with a resolved WAL record. */
|
|
971
|
+
export function preflightPendingGateDecisionMaterialization(handles: ReadonlyArray<PendingGateDecisionHandle>): void {
|
|
972
|
+
for (const handle of handles) {
|
|
973
|
+
if (handle.record.kind !== "decision") continue;
|
|
974
|
+
const path = decisionArtifactPath(handle.record.decision.id, handle.stateDir);
|
|
975
|
+
if (!existsSync(path)) continue;
|
|
976
|
+
const existing = readVerifiedArtifact(path);
|
|
977
|
+
if (existing.integrity.digest !== handle.record.decision.integrity.digest) {
|
|
978
|
+
throw new Error(`gate decision artifact conflicts with pending record: ${handle.record.id}`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Build a mutation-free restart plan. Raw-output records remain unresolved so
|
|
985
|
+
* dispatch can apply its receipt-backed protocol parser. Corrupt journals and
|
|
986
|
+
* conflicting final artifacts fail closed before external resources settle.
|
|
987
|
+
*/
|
|
988
|
+
export function preparePendingGateDecisionRecovery(stateDir = clioStateDir()): PendingGateDecisionRecoveryPlan {
|
|
989
|
+
const pending = readPendingGateDecisions(stateDir);
|
|
990
|
+
if (pending.errors.length > 0) {
|
|
991
|
+
throw new Error(
|
|
992
|
+
`pending gate decision journal is untrustworthy: ${pending.errors
|
|
993
|
+
.map((entry) => `${entry.path}: ${entry.message}`)
|
|
994
|
+
.join("; ")}`,
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
const ready = pending.records.filter((handle) => handle.record.kind === "decision");
|
|
998
|
+
preflightPendingGateDecisionMaterialization(ready);
|
|
999
|
+
return {
|
|
1000
|
+
ready,
|
|
1001
|
+
unresolved: pending.records.filter((handle) => handle.record.kind === "output"),
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
export function readGateDecisionArtifacts(
|
|
1006
|
+
group?: string,
|
|
1007
|
+
stateDir = clioStateDir(),
|
|
1008
|
+
): Array<{ artifact: GateDecisionArtifact; path: string }> {
|
|
1009
|
+
const directory = decisionsDirectory(stateDir);
|
|
1010
|
+
if (!existsSync(directory)) return [];
|
|
1011
|
+
const out: Array<{ artifact: GateDecisionArtifact; path: string }> = [];
|
|
1012
|
+
for (const name of readdirSync(directory)
|
|
1013
|
+
.filter((entry) => entry.endsWith(".json"))
|
|
1014
|
+
.sort()) {
|
|
1015
|
+
const path = join(directory, name);
|
|
1016
|
+
try {
|
|
1017
|
+
const artifact = JSON.parse(readFileSync(path, "utf8")) as GateDecisionArtifact;
|
|
1018
|
+
if (group !== undefined && artifact.group !== group) continue;
|
|
1019
|
+
if (!verifyGateDecisionArtifact(artifact).ok) continue;
|
|
1020
|
+
out.push({ artifact, path });
|
|
1021
|
+
} catch {
|
|
1022
|
+
// Read APIs ignore malformed artifacts; verification tooling can inspect
|
|
1023
|
+
// the file directly without treating it as trusted evidence.
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return out;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/** Discover trusted coordinator decisions from receipt identities alone. */
|
|
1030
|
+
export function readGateDecisionArtifactsForRunIds(
|
|
1031
|
+
runIds: ReadonlySet<string>,
|
|
1032
|
+
stateDir = clioStateDir(),
|
|
1033
|
+
): Array<{ artifact: GateDecisionArtifact; path: string }> {
|
|
1034
|
+
if (runIds.size === 0) return [];
|
|
1035
|
+
return readGateDecisionArtifacts(undefined, stateDir).filter(({ artifact }) => {
|
|
1036
|
+
if (artifact.subjects.some((subject) => runIds.has(subject.runId))) return true;
|
|
1037
|
+
if (artifact.decider !== undefined && runIds.has(artifact.decider.runId)) return true;
|
|
1038
|
+
return artifact.winner !== undefined && runIds.has(artifact.winner.subject.runId);
|
|
1039
|
+
});
|
|
1040
|
+
}
|