@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,994 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { artifactDefaultPath } from "../../core/artifact-paths.js";
|
|
4
|
+
import { canonicalizeExistingPath } from "../../core/path-canonical.js";
|
|
5
|
+
import { ToolNames } from "../../core/tool-names.js";
|
|
6
|
+
import { clioConfigDir } from "../../core/xdg.js";
|
|
7
|
+
import { type ActionClass, type Classification, type ClassifierCall, classify } from "./action-classifier.js";
|
|
8
|
+
import type { DamageControlMatch, DamageControlRule } from "./damage-control.js";
|
|
9
|
+
import { DEFAULT_DAMAGE_CONTROL_PATH_POLICY, mergePathPolicyInputs } from "./default-path-policy.js";
|
|
10
|
+
import {
|
|
11
|
+
type CompiledPathPolicy,
|
|
12
|
+
compilePathPolicy,
|
|
13
|
+
evaluatePathPolicy,
|
|
14
|
+
type PathPolicyDecision,
|
|
15
|
+
type PathPolicyOperation,
|
|
16
|
+
} from "./path-policy.js";
|
|
17
|
+
import {
|
|
18
|
+
type LoadedProjectSafetyPolicy,
|
|
19
|
+
loadProjectSafetyPolicy,
|
|
20
|
+
type ProjectCommandPolicy,
|
|
21
|
+
} from "./project-policy.js";
|
|
22
|
+
import {
|
|
23
|
+
extractCommandDeleteTargets,
|
|
24
|
+
extractCommandWriteTargets,
|
|
25
|
+
inlineShellScript,
|
|
26
|
+
tokenizeShellLike,
|
|
27
|
+
} from "./protected-artifacts.js";
|
|
28
|
+
import { formatRejection, type RejectionMessage } from "./rejection-feedback.js";
|
|
29
|
+
import { getCachedDefaultRulePacks, type PackId, type RulePacks } from "./rule-pack-loader.js";
|
|
30
|
+
|
|
31
|
+
export type SafetyPolicySource =
|
|
32
|
+
| "damage-control:base"
|
|
33
|
+
| "project-policy"
|
|
34
|
+
| "project-policy-invalid"
|
|
35
|
+
| "builtin-command-allowlist"
|
|
36
|
+
| "builtin-classifier"
|
|
37
|
+
| "none";
|
|
38
|
+
|
|
39
|
+
export interface SafetyPolicyDecision {
|
|
40
|
+
/**
|
|
41
|
+
* Net verdict (sd-01 §2.2): `block` is final at every autonomy level,
|
|
42
|
+
* `ask` is a net rail demanding operator confirmation at every level, and
|
|
43
|
+
* `allow` means the net passed; the autonomy mapping decides what happens
|
|
44
|
+
* next at the admission seam (tools/registry.ts, acp/tool-mediator.ts).
|
|
45
|
+
*/
|
|
46
|
+
kind: "allow" | "ask" | "block";
|
|
47
|
+
classification: Classification;
|
|
48
|
+
tool: string;
|
|
49
|
+
actionClass: ActionClass;
|
|
50
|
+
reasons: ReadonlyArray<string>;
|
|
51
|
+
ruleId?: string;
|
|
52
|
+
reasonCode: string;
|
|
53
|
+
command?: string;
|
|
54
|
+
cwd: string;
|
|
55
|
+
posture?: string;
|
|
56
|
+
policySource: SafetyPolicySource;
|
|
57
|
+
policyHash?: string;
|
|
58
|
+
projectPolicyPath?: string;
|
|
59
|
+
match?: DamageControlMatch;
|
|
60
|
+
rejection?: RejectionMessage;
|
|
61
|
+
/**
|
|
62
|
+
* Execute-class passes only: whether the command is in the no-prompt set
|
|
63
|
+
* (built-in allowlist, project policy command, typed execution tool). The
|
|
64
|
+
* autonomy mapping asks for unrecognized execution below full-auto.
|
|
65
|
+
*/
|
|
66
|
+
execRecognition?: "recognized" | "unrecognized";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface SafetyPolicyMetadata {
|
|
70
|
+
version: 1;
|
|
71
|
+
rulePackHash: string;
|
|
72
|
+
rulePackVersion: number;
|
|
73
|
+
activeRuleIds: ReadonlyArray<string>;
|
|
74
|
+
projectPolicyPath: string | null;
|
|
75
|
+
projectPolicyHash: string | null;
|
|
76
|
+
projectPolicyValid: boolean;
|
|
77
|
+
projectPolicyErrors: ReadonlyArray<string>;
|
|
78
|
+
cwd: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface SafetyPolicyEngine {
|
|
82
|
+
evaluate(call: ClassifierCall, posture?: string): SafetyPolicyDecision;
|
|
83
|
+
metadata(posture?: string): SafetyPolicyMetadata;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface SafetyPolicyEngineOptions {
|
|
87
|
+
cwd?: string;
|
|
88
|
+
rulePacks?: RulePacks;
|
|
89
|
+
projectPolicy?: LoadedProjectSafetyPolicy;
|
|
90
|
+
/**
|
|
91
|
+
* Absolute directories a write-class tool call is confined to for this run.
|
|
92
|
+
* When present and non-empty, a write/edit target outside every root is a
|
|
93
|
+
* final BLOCK (reason code "write-root"). Empty or absent disables the check.
|
|
94
|
+
* Enforced at the worker safety seam so both the native worker registry and
|
|
95
|
+
* the Claude SDK hook path inherit it.
|
|
96
|
+
*/
|
|
97
|
+
writeRoots?: ReadonlyArray<string>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface SourcedRule {
|
|
101
|
+
rule: DamageControlRule;
|
|
102
|
+
source: SafetyPolicySource;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const BUILTIN_ALLOWLIST: ReadonlyArray<{ id: string; re: RegExp }> = [
|
|
106
|
+
{ id: "builtin:pwd", re: /^pwd$/ },
|
|
107
|
+
{ id: "builtin:ls", re: /^ls(?:\s+(-[A-Za-z0-9]+|\.[/\w.-]*|[/\w.-]+))*$/ },
|
|
108
|
+
{ id: "builtin:git-status", re: /^git\s+status(?:\s+--short|\s+--branch|\s+-sb)*$/ },
|
|
109
|
+
{ id: "builtin:git-diff", re: /^git\s+diff(?:\s+--cached|\s+--stat|\s+--name-only|\s+--\s+[\w./-]+)*$/ },
|
|
110
|
+
{ id: "builtin:git-log", re: /^git\s+log\s+--oneline(?:\s+-n\s+[1-9]\d{0,2})?(?:\s+--\s+[\w./-]+)?$/ },
|
|
111
|
+
{ id: "builtin:npm-test", re: /^npm\s+(?:test|run\s+test)(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
|
|
112
|
+
{ id: "builtin:npm-lint", re: /^npm\s+run\s+lint(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
|
|
113
|
+
{ id: "builtin:npm-build", re: /^npm\s+run\s+build(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
|
|
114
|
+
{ id: "builtin:npm-typecheck", re: /^npm\s+run\s+typecheck(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
|
|
115
|
+
{ id: "builtin:npm-ci-script", re: /^npm\s+run\s+ci(?:\s+--\s+[\w=./:-]+(?:\s+[\w=./:-]+)*)?$/ },
|
|
116
|
+
{ id: "builtin:pytest", re: /^pytest(?:\s+[\w=./:-]+)*$/ },
|
|
117
|
+
{ id: "builtin:python-pytest", re: /^python(?:3(?:\.\d+)?)?\s+-m\s+pytest(?:\s+[\w=./:-]+)*$/ },
|
|
118
|
+
{ id: "builtin:cargo-test", re: /^cargo\s+test(?:\s+[\w=./:-]+)*$/ },
|
|
119
|
+
{ id: "builtin:go-test", re: /^go\s+test(?:\s+[\w=./:-]+)*$/ },
|
|
120
|
+
{ id: "builtin:make-test", re: /^make\s+test(?:\s+[\w=./:-]+)*$/ },
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* What the recognized spelling looks like, carried on every unrecognized-bash
|
|
125
|
+
* decision. It rides the decision's reasons so the approval overlay, the audit
|
|
126
|
+
* record, and any surface that renders them all say the same thing: an agent
|
|
127
|
+
* that hits this rail should learn the form that passes instead of retrying the
|
|
128
|
+
* same shape. A headless run still answers the resulting ask with its own
|
|
129
|
+
* denial sentence, which does not carry these reasons.
|
|
130
|
+
*/
|
|
131
|
+
const BASH_RECOGNIZED_FORM_HINT =
|
|
132
|
+
"Recognized forms run without asking: one command per bash call, or a && chain whose every step is recognized, with cwd passed as the cwd argument instead of a leading cd.";
|
|
133
|
+
|
|
134
|
+
const EXECUTION_TOOLS = new Set<string>([ToolNames.Bash, ToolNames.Verify]);
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Tools write-root confinement refuses by name, whatever their arguments: they
|
|
138
|
+
* run a project script, a shell, or an unconfined child, so no lexical check
|
|
139
|
+
* can bound where they write. Exported because a run that declares write roots
|
|
140
|
+
* must not be *offered* them: a tool that is guaranteed to be refused is a
|
|
141
|
+
* budget the model spends learning it cannot use it, and the refusal reads to
|
|
142
|
+
* the model as a mistake it should retry. The class-based check below still
|
|
143
|
+
* catches anything that classifies as execute or dispatch at call time.
|
|
144
|
+
*/
|
|
145
|
+
export const WRITE_ROOT_REFUSED_TOOLS: ReadonlySet<string> = new Set<string>([
|
|
146
|
+
ToolNames.Bash,
|
|
147
|
+
ToolNames.Verify,
|
|
148
|
+
ToolNames.Dispatch,
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
// Write-class tools (action class "write"). bash is execute class and runs its
|
|
152
|
+
// own loop, so it is not covered by the lexical write-root containment below.
|
|
153
|
+
const WRITE_ROOT_TOOLS = new Set<string>([ToolNames.Write, ToolNames.Edit, ToolNames.Artifact]);
|
|
154
|
+
|
|
155
|
+
function writeRootTargetPath(call: ClassifierCall): string | null {
|
|
156
|
+
if (!WRITE_ROOT_TOOLS.has(call.tool)) return null;
|
|
157
|
+
const target = pathArg(call.args);
|
|
158
|
+
if (target !== null) return target;
|
|
159
|
+
if (call.tool === ToolNames.Artifact) return artifactDefaultPath(call.args?.kind);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Under active write-root confinement, a tool that can write outside the roots
|
|
165
|
+
* without a path argument the lexical check can inspect. Execute-class tools run
|
|
166
|
+
* project scripts or a shell; dispatch spawns a worker not bound to these roots.
|
|
167
|
+
*/
|
|
168
|
+
function isWriteConfinementEscape(call: ClassifierCall, actionClass: string): boolean {
|
|
169
|
+
if (WRITE_ROOT_REFUSED_TOOLS.has(call.tool)) return true;
|
|
170
|
+
return actionClass === "execute" || actionClass === "dispatch";
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Lexical write-root containment. The target is resolved against the worker cwd
|
|
175
|
+
* and must equal a root or sit beneath it (`root` + path separator). The check
|
|
176
|
+
* is lexical and does not chase symlinks, so a symlink inside a root that points
|
|
177
|
+
* outside is not detected here. Returns the block reason, or null when allowed.
|
|
178
|
+
*/
|
|
179
|
+
function evaluateWriteRoots(roots: ReadonlyArray<string>, writeRootCwd: string, call: ClassifierCall): string | null {
|
|
180
|
+
if (roots.length === 0) return null;
|
|
181
|
+
const target = writeRootTargetPath(call);
|
|
182
|
+
if (target === null) return null;
|
|
183
|
+
const resolved = path.resolve(writeRootCwd, target);
|
|
184
|
+
for (const root of roots) {
|
|
185
|
+
if (resolved === root || resolved.startsWith(`${root}${path.sep}`)) return null;
|
|
186
|
+
}
|
|
187
|
+
return `write target '${target}' resolves to '${resolved}', which is outside the permitted write roots for this run: ${roots.join(", ")}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function createSafetyPolicyEngine(options: SafetyPolicyEngineOptions = {}): SafetyPolicyEngine {
|
|
191
|
+
const cwd = canonicalizeExistingPath(path.resolve(options.cwd ?? process.cwd()));
|
|
192
|
+
// Write-root containment resolves lexically, so it keeps its own un-canonicalized
|
|
193
|
+
// cwd and roots to compare like against like (the design mandates no symlink chasing).
|
|
194
|
+
const writeRootCwd = path.resolve(options.cwd ?? process.cwd());
|
|
195
|
+
const writeRoots = (options.writeRoots ?? []).map((root) => path.resolve(root));
|
|
196
|
+
const packs = options.rulePacks ?? getCachedDefaultRulePacks();
|
|
197
|
+
const projectPolicy = options.projectPolicy ?? loadProjectSafetyPolicy(cwd);
|
|
198
|
+
const projectPolicyRoot =
|
|
199
|
+
projectPolicy.path === null ? cwd : path.dirname(path.dirname(canonicalizeExistingPath(projectPolicy.path)));
|
|
200
|
+
const pathPolicyInput = projectPolicy.disableDefaultPathPolicy
|
|
201
|
+
? projectPolicy.pathPolicy
|
|
202
|
+
: mergePathPolicyInputs(DEFAULT_DAMAGE_CONTROL_PATH_POLICY, projectPolicy.pathPolicy);
|
|
203
|
+
// Clio's own secret store, by absolute path. The static default list carries
|
|
204
|
+
// the `credentials.yaml` literal; the expansion has to happen here because
|
|
205
|
+
// the list cannot call config helpers at module scope.
|
|
206
|
+
const expandedDefaults = projectPolicy.disableDefaultPathPolicy
|
|
207
|
+
? pathPolicyInput
|
|
208
|
+
: mergePathPolicyInputs(pathPolicyInput, { zeroAccessPaths: clioCredentialStorePaths() });
|
|
209
|
+
const pathPolicy = compilePathPolicy(expandedDefaults, projectPolicyRoot);
|
|
210
|
+
// Bash-read scanning tests argument tokens against zero-access entries only:
|
|
211
|
+
// read-only paths stay readable from bash by design, secrets do not.
|
|
212
|
+
const zeroAccessPolicy: CompiledPathPolicy = {
|
|
213
|
+
root: pathPolicy.root,
|
|
214
|
+
entries: pathPolicy.entries.filter((entry) => entry.kind === "zeroAccessPaths"),
|
|
215
|
+
diagnostics: [],
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Base pack rules apply at every posture, so they are sourced once at
|
|
219
|
+
// construction. Building the array inside evaluate() re-allocated it on
|
|
220
|
+
// every admission for no behavioral gain.
|
|
221
|
+
const sourcedRules: SourcedRule[] = packs.base.rules.map((rule) => ({ rule, source: "damage-control:base" }));
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
evaluate(call, posture) {
|
|
225
|
+
const rawClassification = classify(call);
|
|
226
|
+
const command = commandArg(call.args);
|
|
227
|
+
const callCwd = cwdArg(call.args, cwd);
|
|
228
|
+
const scan = damageControlScan(call);
|
|
229
|
+
const hit = scan ? matchSourcedRule(scan, sourcedRules) : null;
|
|
230
|
+
const classification = effectiveClassification(rawClassification, hit?.match);
|
|
231
|
+
|
|
232
|
+
const base = baseDecision(call, classification, callCwd, posture, command);
|
|
233
|
+
|
|
234
|
+
// Worker write-root containment (Slice C). A write-class tool whose
|
|
235
|
+
// target escapes every permitted root is a final block, ranked ahead of
|
|
236
|
+
// the git/system-modify/path-policy rails so an out-of-root write reports
|
|
237
|
+
// "write-root" even when the classifier escalated it to system_modify
|
|
238
|
+
// (e.g. a write outside cwd). No-op unless the run carries writeRoots.
|
|
239
|
+
const writeRootReason = evaluateWriteRoots(writeRoots, writeRootCwd, call);
|
|
240
|
+
if (writeRootReason !== null) {
|
|
241
|
+
return blockDecision(base, {
|
|
242
|
+
ruleId: "write-root",
|
|
243
|
+
reasonCode: "write-root",
|
|
244
|
+
reasons: [writeRootReason],
|
|
245
|
+
policySource: "builtin-classifier",
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Write-confinement is only honest if the run cannot escape the roots by
|
|
250
|
+
// running an arbitrary command or spawning an unconfined child. Under
|
|
251
|
+
// active writeRoots, execute-class tools (bash, verify, which run project
|
|
252
|
+
// scripts) and dispatch (which spawns a worker not bound to these roots)
|
|
253
|
+
// are blocked outright: they can mutate the filesystem outside the roots.
|
|
254
|
+
if (writeRoots.length > 0 && isWriteConfinementEscape(call, classification.actionClass)) {
|
|
255
|
+
return blockDecision(base, {
|
|
256
|
+
ruleId: "write-root",
|
|
257
|
+
reasonCode: "write-root",
|
|
258
|
+
reasons: [
|
|
259
|
+
`tool '${call.tool}' (${classification.actionClass}) can mutate the filesystem outside the permitted write roots and is blocked under write-root confinement`,
|
|
260
|
+
],
|
|
261
|
+
policySource: "builtin-classifier",
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// An explicit `ask: true` damage-control rule is an authored
|
|
266
|
+
// confirm rail and takes precedence over classifier escalation for
|
|
267
|
+
// the same command (sd-01 M3). Without this, the unconditional
|
|
268
|
+
// git_destructive block made every authored git ask rule dead
|
|
269
|
+
// config. Commands matched only by classifier patterns, and rules
|
|
270
|
+
// with `block: true`, stay hard blocks. The ask rail itself runs
|
|
271
|
+
// only after the fail-closed and path-policy blocks below: hard
|
|
272
|
+
// blocks always win, and the ask rail decides among survivors.
|
|
273
|
+
const askRule = hit?.match.ask === true && hit.match.block !== true;
|
|
274
|
+
if (
|
|
275
|
+
!askRule &&
|
|
276
|
+
(classification.actionClass === "git_destructive" ||
|
|
277
|
+
hit?.match.actionClass === "git_destructive" ||
|
|
278
|
+
hit?.match.block === true)
|
|
279
|
+
) {
|
|
280
|
+
const blockInput: Omit<
|
|
281
|
+
SafetyPolicyDecision,
|
|
282
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
283
|
+
> = {
|
|
284
|
+
reasonCode: hit ? `damage-control:${hit.match.ruleId}` : "classification:git_destructive",
|
|
285
|
+
reasons: [...classification.reasons, ...(hit ? [hit.match.reason] : [])],
|
|
286
|
+
policySource: hit?.source ?? "damage-control:base",
|
|
287
|
+
};
|
|
288
|
+
if (hit?.match.ruleId !== undefined) blockInput.ruleId = hit.match.ruleId;
|
|
289
|
+
if (hit?.match !== undefined) blockInput.match = hit.match;
|
|
290
|
+
return blockDecision(base, blockInput);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!projectPolicy.valid && EXECUTION_TOOLS.has(call.tool)) {
|
|
294
|
+
const blockInput: Omit<
|
|
295
|
+
SafetyPolicyDecision,
|
|
296
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
297
|
+
> = {
|
|
298
|
+
ruleId: "project-policy-invalid",
|
|
299
|
+
reasonCode: "project-policy-invalid",
|
|
300
|
+
reasons: [`project safety policy is invalid and execution fails closed: ${projectPolicy.errors.join("; ")}`],
|
|
301
|
+
policySource: "project-policy-invalid",
|
|
302
|
+
};
|
|
303
|
+
if (projectPolicy.hash !== null) blockInput.policyHash = projectPolicy.hash;
|
|
304
|
+
if (projectPolicy.path !== null) blockInput.projectPolicyPath = projectPolicy.path;
|
|
305
|
+
return blockDecision(base, blockInput);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// The path policy runs regardless of project policy validity. When
|
|
309
|
+
// `.clio-coder/safety.yaml` is invalid, the loader has already dropped every
|
|
310
|
+
// project-authored path entry and forced `disableDefaultPathPolicy`
|
|
311
|
+
// off, so the compiled policy here carries exactly the built-in
|
|
312
|
+
// defaults. Evaluating it unconditionally keeps default credential
|
|
313
|
+
// protection (`.env`, `~/.ssh/`, `credentials.yaml`, ...) active on a
|
|
314
|
+
// broken config, which is the fail-closed intent; project-authored
|
|
315
|
+
// additions and exemptions stay gated on validity inside the loader.
|
|
316
|
+
const pathBlock = evaluateProjectPathPolicy(pathPolicy, call, callCwd);
|
|
317
|
+
if (pathBlock !== null) {
|
|
318
|
+
const blockInput: Omit<
|
|
319
|
+
SafetyPolicyDecision,
|
|
320
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
321
|
+
> = {
|
|
322
|
+
ruleId: pathBlock.reasonCode,
|
|
323
|
+
reasonCode: pathBlock.reasonCode,
|
|
324
|
+
reasons: [pathBlock.reason],
|
|
325
|
+
policySource: "project-policy",
|
|
326
|
+
};
|
|
327
|
+
if (projectPolicy.hash !== null) blockInput.policyHash = projectPolicy.hash;
|
|
328
|
+
if (projectPolicy.path !== null) blockInput.projectPolicyPath = projectPolicy.path;
|
|
329
|
+
return blockDecision(base, blockInput);
|
|
330
|
+
}
|
|
331
|
+
// Bash reads of zero-access paths. pathPolicyTargets extracts only
|
|
332
|
+
// write/delete targets from bash, so `cat .env` used to run and its
|
|
333
|
+
// output persisted into the transcript and evidence previews. Any
|
|
334
|
+
// path-like argument token matching a zero-access entry blocks the
|
|
335
|
+
// command; the one carve-out is the exit-code-only presence check
|
|
336
|
+
// (`grep -q`/`grep -sq` with a ^NAME= pattern), which is the safe
|
|
337
|
+
// protocol the credentials skill teaches.
|
|
338
|
+
if (call.tool === ToolNames.Bash && command !== null) {
|
|
339
|
+
const secretRead = evaluateBashZeroAccessRead(zeroAccessPolicy, command, callCwd);
|
|
340
|
+
if (secretRead !== null) {
|
|
341
|
+
const blockInput: Omit<
|
|
342
|
+
SafetyPolicyDecision,
|
|
343
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
344
|
+
> = {
|
|
345
|
+
ruleId: "secret_path_bash",
|
|
346
|
+
reasonCode: "secret_path_bash",
|
|
347
|
+
reasons: [
|
|
348
|
+
`bash read of zero-access path blocked: '${secretRead.token}' matches ${secretRead.entrySource}. Check presence with exit codes only (grep -sq "^NAME=" <file>); have the user supply values through their own terminal (read -s), never through chat or command output.`,
|
|
349
|
+
],
|
|
350
|
+
policySource: "project-policy",
|
|
351
|
+
};
|
|
352
|
+
if (projectPolicy.hash !== null) blockInput.policyHash = projectPolicy.hash;
|
|
353
|
+
if (projectPolicy.path !== null) blockInput.projectPolicyPath = projectPolicy.path;
|
|
354
|
+
return blockDecision(base, blockInput);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// The authored ask rail (sd-01 M3) decides only among calls that
|
|
359
|
+
// survived every hard block above. A confirmed posture admits the
|
|
360
|
+
// matched command; an unconfirmed one parks it for confirmation.
|
|
361
|
+
if (askRule && hit !== null && posture !== "confirmed") {
|
|
362
|
+
return askDecision(base, {
|
|
363
|
+
ruleId: hit.match.ruleId,
|
|
364
|
+
reasonCode: `damage-control:${hit.match.ruleId}`,
|
|
365
|
+
reasons: [...classification.reasons, hit.match.reason, "damage-control rule requires confirmation"],
|
|
366
|
+
policySource: hit.source,
|
|
367
|
+
match: hit.match,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
if (askRule && hit !== null && posture === "confirmed") {
|
|
371
|
+
return allowDecision(base, {
|
|
372
|
+
ruleId: hit.match.ruleId,
|
|
373
|
+
reasonCode: `damage-control:${hit.match.ruleId}`,
|
|
374
|
+
reasons: [...classification.reasons, hit.match.reason, "damage-control rule confirmed by operator"],
|
|
375
|
+
policySource: hit.source,
|
|
376
|
+
match: hit.match,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (classification.actionClass === "system_modify") {
|
|
381
|
+
const input: Omit<
|
|
382
|
+
SafetyPolicyDecision,
|
|
383
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
384
|
+
> = {
|
|
385
|
+
ruleId: "system-modify-confirm",
|
|
386
|
+
reasonCode: "system-modify-confirm",
|
|
387
|
+
reasons: [...classification.reasons, "system-level changes require one-shot confirmation at every autonomy level"],
|
|
388
|
+
policySource: "builtin-classifier",
|
|
389
|
+
};
|
|
390
|
+
return posture === "confirmed" ? allowDecision(base, input) : askDecision(base, input);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (call.tool === ToolNames.Bash && classification.actionClass === "execute") {
|
|
394
|
+
const bash = evaluateBashPolicy(command ?? "", callCwd, cwd, posture, projectPolicy);
|
|
395
|
+
if (bash.kind === "block") return blockDecision(base, bash);
|
|
396
|
+
if (bash.kind === "ask") return askDecision(base, bash);
|
|
397
|
+
return allowDecision(base, bash);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const allowInput: Omit<
|
|
401
|
+
SafetyPolicyDecision,
|
|
402
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
403
|
+
> = {
|
|
404
|
+
reasonCode: "allowed",
|
|
405
|
+
reasons: classification.reasons,
|
|
406
|
+
policySource: hit?.source ?? "none",
|
|
407
|
+
};
|
|
408
|
+
if (hit?.match.ruleId !== undefined) allowInput.ruleId = hit.match.ruleId;
|
|
409
|
+
if (hit?.match !== undefined) allowInput.match = hit.match;
|
|
410
|
+
// The typed execution tool (verify) is bounded by its own check
|
|
411
|
+
// allowlist, so it sits in the no-prompt set.
|
|
412
|
+
if (classification.actionClass === "execute") allowInput.execRecognition = "recognized";
|
|
413
|
+
return allowDecision(base, allowInput);
|
|
414
|
+
},
|
|
415
|
+
metadata() {
|
|
416
|
+
return {
|
|
417
|
+
version: 1,
|
|
418
|
+
rulePackHash: rulePackHash(packs),
|
|
419
|
+
rulePackVersion: packs.base.version,
|
|
420
|
+
activeRuleIds: sourcedRules.map((entry) => entry.rule.id),
|
|
421
|
+
projectPolicyPath: projectPolicy.path,
|
|
422
|
+
projectPolicyHash: projectPolicy.hash,
|
|
423
|
+
projectPolicyValid: projectPolicy.valid,
|
|
424
|
+
projectPolicyErrors: [...projectPolicy.errors, ...pathPolicy.diagnostics],
|
|
425
|
+
cwd,
|
|
426
|
+
};
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** Absolute path of Clio's provider secret store, when resolvable. */
|
|
432
|
+
function clioCredentialStorePaths(): string[] {
|
|
433
|
+
try {
|
|
434
|
+
return [path.join(clioConfigDir(), "credentials.yaml")];
|
|
435
|
+
} catch {
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* The exact safe presence form: `grep -q` or `grep -sq` (either flag order)
|
|
442
|
+
* with a `^NAME=`-shaped pattern and a single file argument. Exit code only;
|
|
443
|
+
* the value never enters context. Anything broader stays blocked.
|
|
444
|
+
*/
|
|
445
|
+
const SAFE_PRESENCE_RE = /^\s*grep\s+(?:-(?:q|sq|qs)\s+)('[^']*'|"[^"]*"|\S+)\s+\S+\s*$/;
|
|
446
|
+
|
|
447
|
+
function isSafePresenceCheck(command: string): boolean {
|
|
448
|
+
const match = SAFE_PRESENCE_RE.exec(command);
|
|
449
|
+
if (!match || match[1] === undefined) return false;
|
|
450
|
+
const pattern = stripQuotes(match[1]);
|
|
451
|
+
return /^\^[A-Za-z_][A-Za-z0-9_]*=/.test(pattern);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function stripQuotes(token: string): string {
|
|
455
|
+
if (token.length >= 2) {
|
|
456
|
+
const first = token[0];
|
|
457
|
+
const last = token[token.length - 1];
|
|
458
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) return token.slice(1, -1);
|
|
459
|
+
}
|
|
460
|
+
return token;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Split a bash command into candidate path tokens. Quotes bind: a quoted
|
|
465
|
+
* string is one token, so `git commit -m "handle .env parsing"` yields the
|
|
466
|
+
* whole message (not a path) while `cat ".env"` still yields `.env`. Shell
|
|
467
|
+
* metacharacters outside quotes act as separators. Tokens are matched as
|
|
468
|
+
* paths against the compiled zero-access entries (which handle tilde
|
|
469
|
+
* expansion and globs), never against a list of reader binaries: a
|
|
470
|
+
* zero-access path appearing anywhere in the command is the signal.
|
|
471
|
+
*/
|
|
472
|
+
function bashPathTokenCandidates(command: string): string[] {
|
|
473
|
+
const candidates: string[] = [];
|
|
474
|
+
for (const token of tokenizeShellLike(command)) {
|
|
475
|
+
if (token.length === 0) continue;
|
|
476
|
+
if (token === ";" || token === "&&" || token === "||" || token === "|" || token === ">" || token === ">>") continue;
|
|
477
|
+
if (token === "<" || token === "<<") continue;
|
|
478
|
+
// A token containing whitespace came from a quoted string of prose, not
|
|
479
|
+
// a path argument; and flags are not paths unless they embed one
|
|
480
|
+
// (--file=~/.aws/credentials).
|
|
481
|
+
if (/\s/.test(token)) continue;
|
|
482
|
+
if (token.startsWith("-") && !token.includes("/") && !token.includes("=")) continue;
|
|
483
|
+
candidates.push(token);
|
|
484
|
+
const eq = token.indexOf("=");
|
|
485
|
+
if (eq > 0 && eq < token.length - 1) candidates.push(token.slice(eq + 1));
|
|
486
|
+
}
|
|
487
|
+
return candidates;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function evaluateBashZeroAccessRead(
|
|
491
|
+
zeroAccessPolicy: CompiledPathPolicy,
|
|
492
|
+
command: string,
|
|
493
|
+
callCwd: string,
|
|
494
|
+
): { token: string; entrySource: string } | null {
|
|
495
|
+
if (zeroAccessPolicy.entries.length === 0) return null;
|
|
496
|
+
if (isSafePresenceCheck(command)) return null;
|
|
497
|
+
for (const token of bashPathTokenCandidates(command)) {
|
|
498
|
+
const decision = evaluatePathPolicy(zeroAccessPolicy, "read", token, callCwd);
|
|
499
|
+
if (decision.kind === "block") {
|
|
500
|
+
return { token, entrySource: `zeroAccessPaths entry ${decision.matchedPath}` };
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function evaluateProjectPathPolicy(
|
|
507
|
+
policy: CompiledPathPolicy,
|
|
508
|
+
call: ClassifierCall,
|
|
509
|
+
callCwd: string,
|
|
510
|
+
): Extract<PathPolicyDecision, { kind: "block" }> | null {
|
|
511
|
+
if (policy.entries.length === 0) return null;
|
|
512
|
+
for (const target of pathPolicyTargets(call)) {
|
|
513
|
+
const decision = evaluatePathPolicy(policy, target.operation, target.path, callCwd);
|
|
514
|
+
if (decision.kind === "block") return decision;
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function pathPolicyTargets(call: ClassifierCall): Array<{ operation: PathPolicyOperation; path: string }> {
|
|
520
|
+
const args = call.args;
|
|
521
|
+
switch (call.tool) {
|
|
522
|
+
case ToolNames.Read:
|
|
523
|
+
case ToolNames.Ls:
|
|
524
|
+
case ToolNames.Grep:
|
|
525
|
+
case ToolNames.Find: {
|
|
526
|
+
const target = pathArg(args) ?? ".";
|
|
527
|
+
return [{ operation: "read", path: target }];
|
|
528
|
+
}
|
|
529
|
+
case ToolNames.Write:
|
|
530
|
+
case ToolNames.Edit: {
|
|
531
|
+
const target = pathArg(args);
|
|
532
|
+
return target === null ? [] : [{ operation: "write", path: target }];
|
|
533
|
+
}
|
|
534
|
+
case ToolNames.Artifact:
|
|
535
|
+
return [{ operation: "write", path: pathArg(args) ?? artifactDefaultPath(args?.kind) }];
|
|
536
|
+
case ToolNames.CredentialPresent:
|
|
537
|
+
// Sanctioned typed presence check: it may inspect secret-shaped paths
|
|
538
|
+
// internally, but its tool contract can return only boolean metadata.
|
|
539
|
+
return [];
|
|
540
|
+
case ToolNames.Bash: {
|
|
541
|
+
const command = commandArg(args);
|
|
542
|
+
if (command === null) return [];
|
|
543
|
+
return [
|
|
544
|
+
...extractCommandWriteTargets(command).map((target) => ({ operation: "write" as const, path: target })),
|
|
545
|
+
...extractCommandDeleteTargets(command).map((target) => ({ operation: "delete" as const, path: target })),
|
|
546
|
+
];
|
|
547
|
+
}
|
|
548
|
+
default:
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Bash net evaluation (sd-01 §2.2). Net blocks: empty commands and cwd
|
|
555
|
+
* escapes. Net confirms: project policy `requireConfirmation` and command
|
|
556
|
+
* substitution (the content-hiding channel). Everything else passes with an
|
|
557
|
+
* `execRecognition` tag: project policy commands and the built-in no-prompt
|
|
558
|
+
* allowlist are recognized; arbitrary bash, including commands with
|
|
559
|
+
* sequencing operators, is unrecognized and the autonomy mapping at the
|
|
560
|
+
* admission seam decides whether it runs, asks, or is denied.
|
|
561
|
+
*/
|
|
562
|
+
function evaluateBashPolicy(
|
|
563
|
+
command: string,
|
|
564
|
+
callCwd: string,
|
|
565
|
+
workspaceRoot: string,
|
|
566
|
+
posture: string | undefined,
|
|
567
|
+
policy: LoadedProjectSafetyPolicy,
|
|
568
|
+
): Omit<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"> {
|
|
569
|
+
if (command.trim().length === 0) {
|
|
570
|
+
return {
|
|
571
|
+
kind: "block",
|
|
572
|
+
ruleId: "bash-empty-command",
|
|
573
|
+
reasonCode: "bash-empty-command",
|
|
574
|
+
reasons: ["bash command must not be empty"],
|
|
575
|
+
policySource: "builtin-command-allowlist",
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
const projectMatch = matchingProjectCommand(policy, command, callCwd);
|
|
579
|
+
if (projectMatch) {
|
|
580
|
+
const base: Omit<
|
|
581
|
+
SafetyPolicyDecision,
|
|
582
|
+
"kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"
|
|
583
|
+
> = {
|
|
584
|
+
ruleId: projectMatch.id,
|
|
585
|
+
reasonCode: `project-policy:${projectMatch.id}`,
|
|
586
|
+
reasons: [`allowed by project safety policy command '${projectMatch.id}'`],
|
|
587
|
+
policySource: "project-policy" as const,
|
|
588
|
+
execRecognition: "recognized",
|
|
589
|
+
};
|
|
590
|
+
if (policy.hash !== null) base.policyHash = policy.hash;
|
|
591
|
+
if (policy.path !== null) base.projectPolicyPath = policy.path;
|
|
592
|
+
if (projectMatch.requireConfirmation && posture !== "confirmed") {
|
|
593
|
+
return {
|
|
594
|
+
...base,
|
|
595
|
+
kind: "ask",
|
|
596
|
+
reasons: [...base.reasons, "project policy requires confirmation"],
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
return { ...base, kind: "allow" };
|
|
600
|
+
}
|
|
601
|
+
if (!isUnderOrSame(callCwd, workspaceRoot)) {
|
|
602
|
+
return {
|
|
603
|
+
kind: "block",
|
|
604
|
+
ruleId: "bash-cwd-escape",
|
|
605
|
+
reasonCode: "bash-cwd-escape",
|
|
606
|
+
reasons: [
|
|
607
|
+
`bash cwd '${callCwd}' escapes workspace root '${workspaceRoot}'; use a typed tool or a project policy entry with explicit cwd`,
|
|
608
|
+
],
|
|
609
|
+
policySource: "builtin-command-allowlist",
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
// Command substitution is the content-hiding channel: the net cannot scan
|
|
613
|
+
// what `$(...)` or backticks produce at runtime, so it stays an ask rail at
|
|
614
|
+
// every level, including full-auto (sd-01 M5). A confirmed posture (one-shot
|
|
615
|
+
// grant) admits it like any other confirm rail.
|
|
616
|
+
// A command that is nothing but `sh -c '<script>'` is recognized by its
|
|
617
|
+
// script. Recognition can only narrow this way: the script is what runs, and
|
|
618
|
+
// judging the wrapper instead is what let the wrapper be a bypass.
|
|
619
|
+
const recognitionCommand = inlineShellScript(command) ?? command;
|
|
620
|
+
if (hasCommandSubstitution(recognitionCommand) && posture !== "confirmed") {
|
|
621
|
+
return {
|
|
622
|
+
kind: "ask",
|
|
623
|
+
ruleId: "bash-command-substitution",
|
|
624
|
+
reasonCode: "bash-command-substitution",
|
|
625
|
+
reasons: ["command substitution hides the executed content from the safety net and requires one-shot confirmation"],
|
|
626
|
+
policySource: "builtin-command-allowlist",
|
|
627
|
+
execRecognition: "unrecognized",
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
const chain = recognizeCommandChain(recognitionCommand, callCwd, workspaceRoot, policy);
|
|
631
|
+
if (chain !== null) {
|
|
632
|
+
const chainReasons = [`every step of the && chain is recognized: ${chain.ruleIds.join(", ")}`];
|
|
633
|
+
if (chain.requiresConfirmation && posture !== "confirmed") {
|
|
634
|
+
return {
|
|
635
|
+
kind: "ask",
|
|
636
|
+
ruleId: "bash-recognized-chain",
|
|
637
|
+
reasonCode: "bash-recognized-chain",
|
|
638
|
+
reasons: [...chainReasons, "project policy requires confirmation for one step"],
|
|
639
|
+
policySource: "builtin-command-allowlist",
|
|
640
|
+
execRecognition: "recognized",
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
kind: "allow",
|
|
645
|
+
ruleId: "bash-recognized-chain",
|
|
646
|
+
reasonCode: "bash-recognized-chain",
|
|
647
|
+
reasons: chainReasons,
|
|
648
|
+
policySource: "builtin-command-allowlist",
|
|
649
|
+
execRecognition: "recognized",
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
// Remaining sequencing operators (pipes, ;, redirects, and && chains with an
|
|
653
|
+
// unrecognized member) defeat per-command recognition, so the command is
|
|
654
|
+
// unrecognized by definition: the autonomy mapping asks at suggest/auto-edit,
|
|
655
|
+
// runs at full-auto, denies at read-only. The rule pack has already scanned
|
|
656
|
+
// the full string, so a destructive verb behind an operator was caught before
|
|
657
|
+
// this point.
|
|
658
|
+
if (hasSequencingOperators(command)) {
|
|
659
|
+
return {
|
|
660
|
+
kind: "allow",
|
|
661
|
+
ruleId: "bash-shell-operators",
|
|
662
|
+
reasonCode: "bash-shell-operators",
|
|
663
|
+
reasons: [
|
|
664
|
+
"shell operators defeat per-command recognition; the autonomy level decides admission",
|
|
665
|
+
BASH_RECOGNIZED_FORM_HINT,
|
|
666
|
+
],
|
|
667
|
+
policySource: "builtin-command-allowlist",
|
|
668
|
+
execRecognition: "unrecognized",
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
for (const entry of BUILTIN_ALLOWLIST) {
|
|
672
|
+
if (entry.re.test(recognitionCommand)) {
|
|
673
|
+
return {
|
|
674
|
+
kind: "allow",
|
|
675
|
+
ruleId: entry.id,
|
|
676
|
+
reasonCode: entry.id,
|
|
677
|
+
reasons: [`matched built-in no-prompt command allowlist '${entry.id}'`],
|
|
678
|
+
policySource: "builtin-command-allowlist",
|
|
679
|
+
execRecognition: "recognized",
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
kind: "allow",
|
|
685
|
+
ruleId: "bash-unrecognized",
|
|
686
|
+
reasonCode: "bash-unrecognized",
|
|
687
|
+
reasons: [
|
|
688
|
+
"bash command is outside the no-prompt set; the autonomy level decides admission",
|
|
689
|
+
BASH_RECOGNIZED_FORM_HINT,
|
|
690
|
+
],
|
|
691
|
+
policySource: "builtin-command-allowlist",
|
|
692
|
+
execRecognition: "unrecognized",
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function baseDecision(
|
|
697
|
+
call: ClassifierCall,
|
|
698
|
+
classification: Classification,
|
|
699
|
+
cwd: string,
|
|
700
|
+
posture: string | undefined,
|
|
701
|
+
command: string | null,
|
|
702
|
+
): Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"> {
|
|
703
|
+
const out: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command"> = {
|
|
704
|
+
classification,
|
|
705
|
+
tool: call.tool,
|
|
706
|
+
actionClass: classification.actionClass,
|
|
707
|
+
cwd,
|
|
708
|
+
};
|
|
709
|
+
if (posture !== undefined) out.posture = posture;
|
|
710
|
+
if (command !== null) out.command = command;
|
|
711
|
+
return out;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function allowDecision(
|
|
715
|
+
base: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
|
|
716
|
+
input: Omit<SafetyPolicyDecision, "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
|
|
717
|
+
): SafetyPolicyDecision {
|
|
718
|
+
return { ...base, ...input, kind: "allow" };
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function askDecision(
|
|
722
|
+
base: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
|
|
723
|
+
input: Omit<SafetyPolicyDecision, "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
|
|
724
|
+
): SafetyPolicyDecision {
|
|
725
|
+
const rejectionInput: Parameters<typeof formatRejection>[0] = {
|
|
726
|
+
tool: base.tool,
|
|
727
|
+
actionClass: base.classification.actionClass,
|
|
728
|
+
reasons: input.reasons,
|
|
729
|
+
};
|
|
730
|
+
if (base.posture !== undefined) rejectionInput.posture = base.posture;
|
|
731
|
+
if (input.ruleId !== undefined) rejectionInput.ruleId = input.ruleId;
|
|
732
|
+
const rejection = formatRejection(rejectionInput);
|
|
733
|
+
return { ...base, ...input, kind: "ask", rejection };
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function blockDecision(
|
|
737
|
+
base: Pick<SafetyPolicyDecision, "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
|
|
738
|
+
input: Omit<SafetyPolicyDecision, "kind" | "classification" | "tool" | "actionClass" | "cwd" | "posture" | "command">,
|
|
739
|
+
): SafetyPolicyDecision {
|
|
740
|
+
const rejectionInput: Parameters<typeof formatRejection>[0] = {
|
|
741
|
+
tool: base.tool,
|
|
742
|
+
actionClass: base.classification.actionClass,
|
|
743
|
+
reasons: input.reasons,
|
|
744
|
+
};
|
|
745
|
+
if (base.posture !== undefined) rejectionInput.posture = base.posture;
|
|
746
|
+
if (input.ruleId !== undefined) rejectionInput.ruleId = input.ruleId;
|
|
747
|
+
const rejection = formatRejection(rejectionInput);
|
|
748
|
+
return { ...base, ...input, kind: "block", rejection };
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function matchSourcedRule(commandString: string, rules: ReadonlyArray<SourcedRule>) {
|
|
752
|
+
for (const entry of rules) {
|
|
753
|
+
if (entry.rule.pattern.test(commandString)) {
|
|
754
|
+
const match: DamageControlMatch = {
|
|
755
|
+
ruleId: entry.rule.id,
|
|
756
|
+
reason: `matched ${entry.rule.id}: ${entry.rule.description}`,
|
|
757
|
+
actionClass: entry.rule.class,
|
|
758
|
+
block: entry.rule.block,
|
|
759
|
+
};
|
|
760
|
+
if (entry.rule.ask !== undefined) match.ask = entry.rule.ask;
|
|
761
|
+
return { match, source: entry.source };
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Excludes "unknown" because damage control overrides must classify actions into concrete, actionable categories.
|
|
768
|
+
const ACTION_CLASSES = new Set<ActionClass>([
|
|
769
|
+
"read",
|
|
770
|
+
"write",
|
|
771
|
+
"execute",
|
|
772
|
+
"system_modify",
|
|
773
|
+
"git_destructive",
|
|
774
|
+
"dispatch",
|
|
775
|
+
]);
|
|
776
|
+
|
|
777
|
+
function effectiveClassification(
|
|
778
|
+
classification: Classification,
|
|
779
|
+
match: DamageControlMatch | undefined,
|
|
780
|
+
): Classification {
|
|
781
|
+
if (!match || !ACTION_CLASSES.has(match.actionClass as ActionClass)) return classification;
|
|
782
|
+
const actionClass = match.actionClass as ActionClass;
|
|
783
|
+
if (actionClass === classification.actionClass) return classification;
|
|
784
|
+
return {
|
|
785
|
+
actionClass,
|
|
786
|
+
reasons: [...classification.reasons, `damage-control:${match.ruleId}`],
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function matchingProjectCommand(
|
|
791
|
+
policy: LoadedProjectSafetyPolicy,
|
|
792
|
+
command: string,
|
|
793
|
+
cwd: string,
|
|
794
|
+
): ProjectCommandPolicy | null {
|
|
795
|
+
if (!policy.valid || policy.path === null) return null;
|
|
796
|
+
const policyRoot = path.dirname(path.dirname(policy.path));
|
|
797
|
+
for (const entry of policy.commands) {
|
|
798
|
+
if (entry.command !== command) continue;
|
|
799
|
+
if (entry.shellOperators === "deny" && hasShellOperators(command)) continue;
|
|
800
|
+
const allowedCwd = entry.cwd !== undefined ? path.resolve(policyRoot, entry.cwd) : policyRoot;
|
|
801
|
+
if (!isUnderOrSame(cwd, allowedCwd)) continue;
|
|
802
|
+
return entry;
|
|
803
|
+
}
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/** Operators that end chain recognition; only `&&` keeps a chain readable. */
|
|
808
|
+
const CHAIN_BREAKING_TOKENS: ReadonlySet<string> = new Set([";", "|", "||", ">", ">>", "<", "<<"]);
|
|
809
|
+
|
|
810
|
+
/** Chain length ceiling. Real compound calls are two or three steps. */
|
|
811
|
+
const CHAIN_MAX_SEGMENTS = 6;
|
|
812
|
+
|
|
813
|
+
interface ChainRecognition {
|
|
814
|
+
ruleIds: ReadonlyArray<string>;
|
|
815
|
+
requiresConfirmation: boolean;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Recognition for `cd <workspace dir> && <recognized command>` and the longer
|
|
820
|
+
* `&&` chains built from the same parts. Every member is checked on its own and
|
|
821
|
+
* the chain takes its most restrictive member's verdict, so the chain can never
|
|
822
|
+
* admit something its members would not.
|
|
823
|
+
*
|
|
824
|
+
* This exists because the compound form is what a model reaches for first and
|
|
825
|
+
* the flat rule cost more than it bought: in a recorded live drive 34 of 75
|
|
826
|
+
* calls were blocked, nearly all of them `cd x && y`, since an unrecognized
|
|
827
|
+
* execute asks below full-auto and a headless run answers every ask with a
|
|
828
|
+
* denial (REPORT-dispatch-drive-1.md S2). Meanwhile `sh -c '<anything>'` sailed
|
|
829
|
+
* past the same rail, so the rail was mostly taxing the honest spelling. The
|
|
830
|
+
* `sh -c` half is closed at both ends now: this function recognizes such a
|
|
831
|
+
* command by its inner script, and the shared write-target scanner reads inside
|
|
832
|
+
* the script too.
|
|
833
|
+
*/
|
|
834
|
+
function recognizeCommandChain(
|
|
835
|
+
command: string,
|
|
836
|
+
callCwd: string,
|
|
837
|
+
workspaceRoot: string,
|
|
838
|
+
policy: LoadedProjectSafetyPolicy,
|
|
839
|
+
): ChainRecognition | null {
|
|
840
|
+
const segments: string[][] = [];
|
|
841
|
+
let current: string[] = [];
|
|
842
|
+
for (const token of tokenizeShellLike(command)) {
|
|
843
|
+
if (token === "&&") {
|
|
844
|
+
segments.push(current);
|
|
845
|
+
current = [];
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (CHAIN_BREAKING_TOKENS.has(token)) return null;
|
|
849
|
+
current.push(token);
|
|
850
|
+
}
|
|
851
|
+
segments.push(current);
|
|
852
|
+
if (segments.length < 2 || segments.length > CHAIN_MAX_SEGMENTS) return null;
|
|
853
|
+
const ruleIds: string[] = [];
|
|
854
|
+
let requiresConfirmation = false;
|
|
855
|
+
for (const segment of segments) {
|
|
856
|
+
if (segment.length === 0) return null;
|
|
857
|
+
if (segment[0] === "cd") {
|
|
858
|
+
// A `cd` that stays inside the workspace re-bases nothing the net cares
|
|
859
|
+
// about; one that leaves it is the laundering pattern the action
|
|
860
|
+
// classifier already escalates, so it is not recognizable here.
|
|
861
|
+
if (segment.length !== 2) return null;
|
|
862
|
+
const target = segment[1] ?? "";
|
|
863
|
+
if (target.startsWith("~") || !isUnderOrSame(path.resolve(callCwd, target), workspaceRoot)) return null;
|
|
864
|
+
ruleIds.push("builtin:cd-workspace");
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
// Re-rendered from tokens, so quoting is gone: a member that needed its
|
|
868
|
+
// quotes fails the allowlist regex and the whole chain stays unrecognized.
|
|
869
|
+
const rendered = segment.join(" ");
|
|
870
|
+
const projectMatch = matchingProjectCommand(policy, rendered, callCwd);
|
|
871
|
+
if (projectMatch) {
|
|
872
|
+
ruleIds.push(projectMatch.id);
|
|
873
|
+
if (projectMatch.requireConfirmation) requiresConfirmation = true;
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
const builtin = BUILTIN_ALLOWLIST.find((entry) => entry.re.test(rendered));
|
|
877
|
+
if (builtin === undefined) return null;
|
|
878
|
+
ruleIds.push(builtin.id);
|
|
879
|
+
}
|
|
880
|
+
return { ruleIds, requiresConfirmation };
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Sequencing and redirection operators. These defeat per-command allowlist
|
|
885
|
+
* matching but hide nothing from the rule pack, which scans the full string.
|
|
886
|
+
*/
|
|
887
|
+
function hasSequencingOperators(command: string): boolean {
|
|
888
|
+
return /(\|\||&&|;|\||>>?|<|\n|\r)/.test(command);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Content-hiding constructs: `$(...)` and backticks execute text the net
|
|
893
|
+
* cannot see until runtime. Kept separate from sequencing (sd-01 M5) so they
|
|
894
|
+
* can stay ask-gated at full-auto.
|
|
895
|
+
*/
|
|
896
|
+
function hasCommandSubstitution(command: string): boolean {
|
|
897
|
+
return /(`|\$\()/.test(command);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/** Any shell operator at all; project policy entries with `shellOperators: deny` reject both kinds. */
|
|
901
|
+
function hasShellOperators(command: string): boolean {
|
|
902
|
+
return hasSequencingOperators(command) || hasCommandSubstitution(command);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function isUnderOrSame(child: string, parent: string): boolean {
|
|
906
|
+
const rel = path.relative(
|
|
907
|
+
canonicalizeExistingPath(path.resolve(parent)),
|
|
908
|
+
canonicalizeExistingPath(path.resolve(child)),
|
|
909
|
+
);
|
|
910
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function commandArg(args: Record<string, unknown> | undefined): string | null {
|
|
914
|
+
return typeof args?.command === "string" ? args.command : null;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function pathArg(args: Record<string, unknown> | undefined): string | null {
|
|
918
|
+
if (!args) return null;
|
|
919
|
+
const candidate = args.path ?? args.file_path ?? args.filePath;
|
|
920
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : null;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function cwdArg(args: Record<string, unknown> | undefined, fallback: string): string {
|
|
924
|
+
const resolved = typeof args?.cwd === "string" && args.cwd.length > 0 ? path.resolve(fallback, args.cwd) : fallback;
|
|
925
|
+
return canonicalizeExistingPath(resolved);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Tools whose arguments are a file's contents rather than a command line.
|
|
930
|
+
* Their damage-control scan is the destination path alone.
|
|
931
|
+
*/
|
|
932
|
+
const CONTENT_BEARING_TOOLS: ReadonlySet<string> = new Set([ToolNames.Write, ToolNames.Edit, ToolNames.Artifact]);
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* The text damage-control rules are matched against.
|
|
936
|
+
*
|
|
937
|
+
* Every rule in the pack is a command pattern: shell (`rm -rf /`, `chmod -R
|
|
938
|
+
* 777`), cloud CLI (`aws s3 rm --recursive`), or SQL (`DROP TABLE`). Matching
|
|
939
|
+
* them against a file's contents asks whether the file *mentions* a dangerous
|
|
940
|
+
* command, which is a different question from whether the call *runs* one, and
|
|
941
|
+
* the two are indistinguishable once the text is in the haystack.
|
|
942
|
+
*
|
|
943
|
+
* That cost a real feature. `clio-coder context wiki` could not write its own
|
|
944
|
+
* `domains/safety.md`: the page documents what the classifier blocks, so it
|
|
945
|
+
* quotes `rm -rf /`, and the write was refused as `system_modify` with reason
|
|
946
|
+
* `damage-control:rm-rf-root`. The same defect blocks writing a SQL migration
|
|
947
|
+
* containing `DROP TABLE` or a test fixture for the classifier itself.
|
|
948
|
+
*
|
|
949
|
+
* Writing a file is not executing it. A script written with a destructive body
|
|
950
|
+
* still has to be run, and that run is an execute-class call scanned here in
|
|
951
|
+
* full. Only the destination path is scanned for a mutation tool, which keeps
|
|
952
|
+
* any path-shaped rule working; where the file may land is the write tool's own
|
|
953
|
+
* gate in `writePathClass`.
|
|
954
|
+
*/
|
|
955
|
+
function damageControlScan(call: ClassifierCall): string {
|
|
956
|
+
if (!CONTENT_BEARING_TOOLS.has(call.tool)) return serializeArgs(call.args);
|
|
957
|
+
const pathArg = call.args?.path;
|
|
958
|
+
return typeof pathArg === "string" ? pathArg : "";
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
function serializeArgs(args?: Record<string, unknown>): string {
|
|
962
|
+
if (!args) return "";
|
|
963
|
+
const parts: string[] = [];
|
|
964
|
+
for (const v of Object.values(args)) {
|
|
965
|
+
if (v == null) continue;
|
|
966
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") parts.push(String(v));
|
|
967
|
+
else {
|
|
968
|
+
try {
|
|
969
|
+
parts.push(JSON.stringify(v));
|
|
970
|
+
} catch {
|
|
971
|
+
// ignore values that cannot be serialized
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
return parts.join(" ");
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function rulePackHash(packs: RulePacks): string {
|
|
979
|
+
const payload: Record<PackId, Array<Record<string, unknown>>> = {
|
|
980
|
+
base: packPayload(packs.base.rules),
|
|
981
|
+
};
|
|
982
|
+
return createHash("sha256").update(JSON.stringify(payload), "utf8").digest("hex");
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function packPayload(rules: ReadonlyArray<DamageControlRule>): Array<Record<string, unknown>> {
|
|
986
|
+
return rules.map((rule) => ({
|
|
987
|
+
id: rule.id,
|
|
988
|
+
description: rule.description,
|
|
989
|
+
pattern: rule.pattern.source,
|
|
990
|
+
class: rule.class,
|
|
991
|
+
block: rule.block,
|
|
992
|
+
...(rule.ask !== undefined ? { ask: rule.ask } : {}),
|
|
993
|
+
}));
|
|
994
|
+
}
|