@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,1157 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import type { TSchema } from "typebox";
|
|
3
|
+
import { isWorkerToolCallCapExceededReason } from "../core/guardrails.js";
|
|
4
|
+
import { HEADLESS_PERMISSION_DENIED_MARKER } from "../core/headless-permission.js";
|
|
5
|
+
import {
|
|
6
|
+
evaluateSkillToolSurface,
|
|
7
|
+
type PendingSkillToolPolicy,
|
|
8
|
+
type SkillToolSurfaceViolation,
|
|
9
|
+
} from "../core/skill-activation.js";
|
|
10
|
+
import { type ToolName, ToolNames } from "../core/tool-names.js";
|
|
11
|
+
import type { MiddlewareContract } from "../domains/middleware/contract.js";
|
|
12
|
+
import type { MiddlewareEffect, MiddlewareHookInput, MiddlewareMetadataValue } from "../domains/middleware/types.js";
|
|
13
|
+
import type { ActionClass, ClassifierCall } from "../domains/safety/action-classifier.js";
|
|
14
|
+
import { approvalAxisId } from "../domains/safety/approval-axis.js";
|
|
15
|
+
import {
|
|
16
|
+
type AutonomyExposure,
|
|
17
|
+
type AutonomyLevel,
|
|
18
|
+
autonomyAskRejection,
|
|
19
|
+
autonomyDenyRejection,
|
|
20
|
+
DEFAULT_AUTONOMY_EXPOSURE,
|
|
21
|
+
DEFAULT_AUTONOMY_LEVEL,
|
|
22
|
+
mapAutonomy,
|
|
23
|
+
} from "../domains/safety/autonomy.js";
|
|
24
|
+
import type { SafetyContract, SafetyDecision } from "../domains/safety/contract.js";
|
|
25
|
+
import { hashToolCall } from "../domains/safety/loop-detector.js";
|
|
26
|
+
import { detectValidationCommand } from "../domains/safety/protected-artifacts.js";
|
|
27
|
+
import { askUserExposure } from "./ask-user.js";
|
|
28
|
+
import { type DispatchPlanView, describeDispatchPlan } from "./dispatch-plan.js";
|
|
29
|
+
import { shapeToolResult } from "./result-shaping.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Tool registry. Admission point for every tool call. Delegates classification
|
|
33
|
+
* and policy decisions to the safety domain, parks one-shot confirmation asks,
|
|
34
|
+
* and runs admitted tool bodies. Never throws on safety rejections; the caller
|
|
35
|
+
* surfaces the rejection message back to the model.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Per-tool execution mode override forwarded to pi-agent-core's
|
|
40
|
+
* `AgentTool.executionMode`. Parallel tools may run concurrently with other
|
|
41
|
+
* parallel tool calls; sequential tools run one at a time and prevent any
|
|
42
|
+
* other tool in the batch from running in parallel with them. Leaving this
|
|
43
|
+
* undefined defers to the agent loop's global `toolExecution` setting.
|
|
44
|
+
*/
|
|
45
|
+
export type ToolExecutionMode = "sequential" | "parallel";
|
|
46
|
+
export type ToolSourceScope = "core" | "domain";
|
|
47
|
+
export type ToolRetrySafety = "idempotent" | "retry_safe" | "not_retry_safe" | "unknown";
|
|
48
|
+
export type ToolCostLatencyClass = "local_fast" | "local_medium" | "local_slow" | "network" | "agent";
|
|
49
|
+
|
|
50
|
+
export interface ToolSourceInfo {
|
|
51
|
+
path: string;
|
|
52
|
+
scope: ToolSourceScope;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ToolResultSizePolicy {
|
|
56
|
+
kind: "exact" | "bounded" | "summary" | "truncate";
|
|
57
|
+
maxBytes?: number;
|
|
58
|
+
followUpHint?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ToolMetadata {
|
|
62
|
+
/** Short statement of the tool's purpose for audit/UI surfaces. */
|
|
63
|
+
objective: string;
|
|
64
|
+
/** Stable UI label shown in compact renderers. */
|
|
65
|
+
uiLabel: string;
|
|
66
|
+
/** Whether automatic recovery may safely retry an unfinished call. */
|
|
67
|
+
retrySafety: ToolRetrySafety;
|
|
68
|
+
/** Expected result-size behavior at the registry boundary. */
|
|
69
|
+
resultSizePolicy: ToolResultSizePolicy;
|
|
70
|
+
/** Coarse cost/latency bucket for dashboard diagnostics. */
|
|
71
|
+
costLatency: ToolCostLatencyClass;
|
|
72
|
+
/**
|
|
73
|
+
* One sentence of usage guidance rendered into the session Tool Contract
|
|
74
|
+
* when this tool is on the frozen surface. Hints render sorted by tool
|
|
75
|
+
* name, so the compiled prompt stays byte-stable per surface. Most tools
|
|
76
|
+
* need none; the schema description covers them.
|
|
77
|
+
*/
|
|
78
|
+
promptHint?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ToolSpec {
|
|
82
|
+
name: ToolName;
|
|
83
|
+
description: string;
|
|
84
|
+
sourceInfo?: ToolSourceInfo;
|
|
85
|
+
metadata?: ToolMetadata;
|
|
86
|
+
/**
|
|
87
|
+
* TypeBox schema advertised to the model so it knows which named
|
|
88
|
+
* parameters the tool accepts. Must be a Type.Object(...). Runtime
|
|
89
|
+
* validation still happens inside `run()`, so the schema is advisory
|
|
90
|
+
* to the model, not an enforcement boundary.
|
|
91
|
+
*/
|
|
92
|
+
parameters: TSchema;
|
|
93
|
+
/** Base action class for this tool when arguments are trivial. */
|
|
94
|
+
baseActionClass: ActionClass;
|
|
95
|
+
/**
|
|
96
|
+
* Per-tool execution mode. Read-only tools set `"parallel"` so the model
|
|
97
|
+
* can batch scans; mutating or filesystem-racing tools set `"sequential"`
|
|
98
|
+
* so two `bash` or `edit` calls in the same batch never run concurrently.
|
|
99
|
+
*/
|
|
100
|
+
executionMode?: ToolExecutionMode;
|
|
101
|
+
/**
|
|
102
|
+
* Optional argument normalizer applied before the tool body (and its own
|
|
103
|
+
* internal validation). Mirrors pi's `prepareArguments`: lets a tool accept
|
|
104
|
+
* the common weak-model argument shapes (legacy top-level fields, a
|
|
105
|
+
* JSON-string array) without hand-parsing inside every `run`. Must be pure
|
|
106
|
+
* and idempotent; a throwing normalizer is ignored and the raw args pass
|
|
107
|
+
* through. Tools that also want direct `run` calls normalized should invoke
|
|
108
|
+
* the same function at the top of `run`.
|
|
109
|
+
*/
|
|
110
|
+
prepareArguments?(args: Record<string, unknown>): Record<string, unknown>;
|
|
111
|
+
/**
|
|
112
|
+
* Synchronous admission planner. Unlike `prepareArguments`, this runs before
|
|
113
|
+
* safety/autonomy mapping so approval-sensitive tools can attach the exact
|
|
114
|
+
* immutable artifact—and provisional resources—that admission and execution
|
|
115
|
+
* share. Resource owners must pair it with `disposeAdmissionArguments`.
|
|
116
|
+
*/
|
|
117
|
+
prepareAdmissionArguments?(args: Record<string, unknown>): Record<string, unknown>;
|
|
118
|
+
/** Release provisional resources when prepared admission is denied or execution returns. */
|
|
119
|
+
disposeAdmissionArguments?(args: Record<string, unknown>): void;
|
|
120
|
+
/** Trusted admission artifact renderer used by policy after preparation. */
|
|
121
|
+
describeDispatchPlan?(args: Record<string, unknown>): DispatchPlanView;
|
|
122
|
+
/** Execute the tool. Only called after admission. */
|
|
123
|
+
run(args: Record<string, unknown>, options?: ToolInvokeOptions): Promise<ToolResult>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type ToolResultDetails = Record<string, unknown>;
|
|
127
|
+
|
|
128
|
+
export type ToolResult =
|
|
129
|
+
| {
|
|
130
|
+
kind: "ok";
|
|
131
|
+
output: string;
|
|
132
|
+
details?: ToolResultDetails;
|
|
133
|
+
/**
|
|
134
|
+
* Early-termination hint propagated to pi-agent-core's
|
|
135
|
+
* `AgentToolResult.terminate`. When every finalized tool result in
|
|
136
|
+
* the current batch sets this to true, the agent loop stops without
|
|
137
|
+
* a follow-up LLM call. Used by terminal artifact writers where
|
|
138
|
+
* writing the artifact is the whole turn.
|
|
139
|
+
*/
|
|
140
|
+
terminate?: boolean;
|
|
141
|
+
}
|
|
142
|
+
| { kind: "error"; message: string; details?: ToolResultDetails };
|
|
143
|
+
|
|
144
|
+
export interface RegistryDeps {
|
|
145
|
+
safety: SafetyContract;
|
|
146
|
+
/**
|
|
147
|
+
* Hook layer. The loop guard, registered on `before_tool` by both
|
|
148
|
+
* composition roots (entry/orchestrator.ts and worker-runtime.ts via
|
|
149
|
+
* engine/loop-guard.ts), observes every call attempt through this contract;
|
|
150
|
+
* the registry feeds it `metadata.callFingerprint` and runs `before_tool`
|
|
151
|
+
* for safety-blocked attempts too, so repetition of rejected calls stays
|
|
152
|
+
* observable.
|
|
153
|
+
*/
|
|
154
|
+
middleware?: MiddlewareContract;
|
|
155
|
+
/**
|
|
156
|
+
* Provider-routing effects emitted by tool hooks apply to the next model
|
|
157
|
+
* round. The registry owns those hook calls, so it forwards the complete
|
|
158
|
+
* effect batch to the composition root after evaluation. Consumers may only
|
|
159
|
+
* narrow routing (`require_tool` / `lock_tools`); admission still happens
|
|
160
|
+
* through this registry on the resulting call.
|
|
161
|
+
*/
|
|
162
|
+
onMiddlewareEffects?: (effects: ReadonlyArray<MiddlewareEffect>, input: MiddlewareHookInput) => void;
|
|
163
|
+
/**
|
|
164
|
+
* Live autonomy level (sd-01 §2.2). Read per admission so hot-reloaded
|
|
165
|
+
* settings apply to the next call. The orchestrator wires this to current
|
|
166
|
+
* settings; workers wire it to the level carried on their WorkerSpec.
|
|
167
|
+
* Absent means the default level (M7: auto-edit).
|
|
168
|
+
*/
|
|
169
|
+
autonomy?: () => AutonomyLevel;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface ToolInvokeOptions {
|
|
173
|
+
signal?: AbortSignal;
|
|
174
|
+
runId?: string;
|
|
175
|
+
sessionId?: string;
|
|
176
|
+
turnId?: string;
|
|
177
|
+
toolCallId?: string;
|
|
178
|
+
correlationId?: string;
|
|
179
|
+
pendingSkillPolicy?: PendingSkillToolPolicy;
|
|
180
|
+
askUserPolicy?: AskUserToolPolicy;
|
|
181
|
+
/** Registry-authenticated one-shot operator approval for this execution. */
|
|
182
|
+
approval?: { requestId: string; requestedBy: string; actionClass: ActionClass };
|
|
183
|
+
/**
|
|
184
|
+
* How long this call sat parked awaiting an operator decision, reported when
|
|
185
|
+
* the park resolves. A caller timing the invocation is measuring the tool,
|
|
186
|
+
* and the park is the operator, so the two have to be separable: without
|
|
187
|
+
* this a `npm test` approved after a minute of reading was recorded as a
|
|
188
|
+
* one-minute test run in the transcript, the receipt, and toolStats.
|
|
189
|
+
*/
|
|
190
|
+
onParked?: (parkedMs: number) => void;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type AskUserInterviewStatus = "idle" | "active" | "complete" | "cancelled";
|
|
194
|
+
|
|
195
|
+
export interface AskUserTranscriptQuestion {
|
|
196
|
+
question: string;
|
|
197
|
+
header?: string;
|
|
198
|
+
options?: Array<{ label: string; description?: string }>;
|
|
199
|
+
multi_select?: boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface AskUserTranscriptAnswer {
|
|
203
|
+
question: string;
|
|
204
|
+
answer: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface AskUserTranscriptDecision {
|
|
208
|
+
key: string;
|
|
209
|
+
value: string;
|
|
210
|
+
label?: string;
|
|
211
|
+
source_question?: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface AskUserTranscriptRound {
|
|
215
|
+
round: number;
|
|
216
|
+
requestedAt: string;
|
|
217
|
+
answeredAt?: string;
|
|
218
|
+
questions: AskUserTranscriptQuestion[];
|
|
219
|
+
answers: AskUserTranscriptAnswer[];
|
|
220
|
+
cancelled?: boolean;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface AskUserToolPolicy {
|
|
224
|
+
id: string;
|
|
225
|
+
status: AskUserInterviewStatus;
|
|
226
|
+
startedAt: string;
|
|
227
|
+
updatedAt: string;
|
|
228
|
+
endedAt?: string;
|
|
229
|
+
sessionId?: string;
|
|
230
|
+
turnId?: string;
|
|
231
|
+
transcriptPath?: string;
|
|
232
|
+
summary?: string;
|
|
233
|
+
rounds: AskUserTranscriptRound[];
|
|
234
|
+
decisions: AskUserTranscriptDecision[];
|
|
235
|
+
inFlight: boolean;
|
|
236
|
+
cancelled: boolean;
|
|
237
|
+
answerCount: number;
|
|
238
|
+
callCount: number;
|
|
239
|
+
maxCalls: number;
|
|
240
|
+
askedQuestionKeys: Set<string>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* One-shot elevation grant. The interactive layer issues this when the user
|
|
245
|
+
* confirms a single parked tool call without changing persistent posture.
|
|
246
|
+
* The registry consumes the grant on the next `resumeParkedCalls` pass so
|
|
247
|
+
* exactly one parked call receives elevated admission; subsequent calls go
|
|
248
|
+
* back through the normal safety gate.
|
|
249
|
+
*/
|
|
250
|
+
export interface OneShotGrant {
|
|
251
|
+
/** Parked action class approved for this single admission pass. */
|
|
252
|
+
actionClass: ActionClass;
|
|
253
|
+
/** Parked approval request approved for this single admission pass. */
|
|
254
|
+
requestId?: string;
|
|
255
|
+
/** Free-form origin tag carried into audit (`tool`, `keybind:single`, ...). */
|
|
256
|
+
requestedBy: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface PermissionRequiredMeta {
|
|
260
|
+
requestId: string;
|
|
261
|
+
axis: string;
|
|
262
|
+
/**
|
|
263
|
+
* Provider tool-call id carried on the parked call's invoke options, when
|
|
264
|
+
* one exists. The interactive layer uses it to correlate the park to its
|
|
265
|
+
* transcript tool segment so a parked call renders as awaiting approval
|
|
266
|
+
* instead of running. Purely descriptive: park/resume semantics never
|
|
267
|
+
* read it.
|
|
268
|
+
*/
|
|
269
|
+
toolCallId?: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface ToolRegistry {
|
|
273
|
+
register(spec: ToolSpec): void;
|
|
274
|
+
/** Tools visible in the single operating posture. Models only see these. */
|
|
275
|
+
listVisible(): ReadonlyArray<ToolSpec>;
|
|
276
|
+
/** Tools registered overall. For /audit, /doctor. */
|
|
277
|
+
listAll(): ReadonlyArray<ToolSpec>;
|
|
278
|
+
/** Lookup by tool id. */
|
|
279
|
+
get(name: ToolName): ToolSpec | undefined;
|
|
280
|
+
/** Tool names registered in the single operating posture. */
|
|
281
|
+
listRegistered(): ReadonlyArray<ToolName>;
|
|
282
|
+
/**
|
|
283
|
+
* Admission point. Classifies, evaluates safety, and either runs or
|
|
284
|
+
* returns a rejection. Never throws on safety rejections. When the
|
|
285
|
+
* a safety ask or confirmable action is encountered, the returned promise
|
|
286
|
+
* stays pending until a resume or cancel method resolves it.
|
|
287
|
+
*/
|
|
288
|
+
invoke(call: ClassifierCall, options?: ToolInvokeOptions): Promise<RegistryVerdict>;
|
|
289
|
+
/**
|
|
290
|
+
* True while at least one call awaits operator confirmation. The interactive
|
|
291
|
+
* layer reads this from `closeOverlay()` to re-open the confirmation overlay
|
|
292
|
+
* whenever an unrelated overlay closes with a parked call still pending.
|
|
293
|
+
*/
|
|
294
|
+
hasParkedCalls(): boolean;
|
|
295
|
+
/** Number of calls currently waiting for operator confirmation. */
|
|
296
|
+
parkedCount(): number;
|
|
297
|
+
/**
|
|
298
|
+
* Re-fire the permission-required signal for the oldest parked call without
|
|
299
|
+
* changing queue order or resolving anything.
|
|
300
|
+
*/
|
|
301
|
+
renotifyHead(): void;
|
|
302
|
+
/**
|
|
303
|
+
* Re-run admission for every parked call. When `grant` is provided the
|
|
304
|
+
* grant covers one parked action class. Calls admitted on retry
|
|
305
|
+
* execute and their original promise resolves with the result. Calls still
|
|
306
|
+
* waiting for confirmation stay parked.
|
|
307
|
+
*/
|
|
308
|
+
resumeParkedCalls(grant?: OneShotGrant): Promise<void>;
|
|
309
|
+
/**
|
|
310
|
+
* Resolve one parked call with a `blocked` verdict carrying `reason`.
|
|
311
|
+
* Returns true when the request was found.
|
|
312
|
+
*/
|
|
313
|
+
cancelParkedCall(requestId: string, reason: string): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Resolve every parked call with a `blocked` verdict carrying `reason`.
|
|
316
|
+
* Used when the confirmation overlay is cancelled so the agent loop sees a
|
|
317
|
+
* clean rejection instead of an indefinitely pending tool call.
|
|
318
|
+
*/
|
|
319
|
+
cancelParkedCalls(reason: string): void;
|
|
320
|
+
/**
|
|
321
|
+
* Subscribe to the signal fired when a call is parked awaiting permission
|
|
322
|
+
* confirmation. Returns an unsubscribe handle.
|
|
323
|
+
*/
|
|
324
|
+
onPermissionRequired(
|
|
325
|
+
listener: (call: ClassifierCall, decision: SafetyDecision, meta: PermissionRequiredMeta) => void,
|
|
326
|
+
): () => void;
|
|
327
|
+
/**
|
|
328
|
+
* Subscribe to the signal fired when the autonomy mapping auto-denies a
|
|
329
|
+
* call (deny dispositions, today only at read-only). The verdict already
|
|
330
|
+
* carries the rejection; this exists for operator-facing notices.
|
|
331
|
+
*/
|
|
332
|
+
onAutonomyDenied(listener: (call: ClassifierCall, decision: SafetyDecision, level: AutonomyLevel) => void): () => void;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export type RegistryVerdict =
|
|
336
|
+
| { kind: "ok"; result: ToolResult; decision: SafetyDecision }
|
|
337
|
+
| { kind: "blocked"; reason: string; decision: SafetyDecision }
|
|
338
|
+
| { kind: "not_visible"; reason: string };
|
|
339
|
+
|
|
340
|
+
interface ParkedCall {
|
|
341
|
+
call: ClassifierCall;
|
|
342
|
+
decision: SafetyDecision;
|
|
343
|
+
meta: PermissionRequiredMeta;
|
|
344
|
+
resolve: (verdict: RegistryVerdict) => void;
|
|
345
|
+
/**
|
|
346
|
+
* Seals the park at the instant the operator's decision lands, before the
|
|
347
|
+
* admitted body runs. Idempotent, so the resolve paths that never reach a
|
|
348
|
+
* body still report exactly one park.
|
|
349
|
+
*/
|
|
350
|
+
closePark: () => void;
|
|
351
|
+
options?: ToolInvokeOptions;
|
|
352
|
+
abortCleanup?: () => void;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function createRegistry(deps: RegistryDeps): ToolRegistry {
|
|
356
|
+
const tools = new Map<ToolName, ToolSpec>();
|
|
357
|
+
const parked: ParkedCall[] = [];
|
|
358
|
+
const permissionListeners = new Set<
|
|
359
|
+
(call: ClassifierCall, decision: SafetyDecision, meta: PermissionRequiredMeta) => void
|
|
360
|
+
>();
|
|
361
|
+
const autonomyDeniedListeners = new Set<
|
|
362
|
+
(call: ClassifierCall, decision: SafetyDecision, level: AutonomyLevel) => void
|
|
363
|
+
>();
|
|
364
|
+
let approvalRequestCounter = 0;
|
|
365
|
+
const approvalRequestToken = randomBytes(4).toString("hex");
|
|
366
|
+
|
|
367
|
+
const runSpec = async (
|
|
368
|
+
spec: ToolSpec,
|
|
369
|
+
call: ClassifierCall,
|
|
370
|
+
decision: SafetyDecision,
|
|
371
|
+
options?: ToolInvokeOptions,
|
|
372
|
+
): Promise<RegistryVerdict> => {
|
|
373
|
+
// The hook layer is the only control stage past safety admission. Guards
|
|
374
|
+
// (loop, protected artifacts, dispatch dedup) are before_tool
|
|
375
|
+
// registrations; the first block_tool effect decides the verdict.
|
|
376
|
+
const beforeEffects = runToolHook("before_tool", spec, call, decision, options);
|
|
377
|
+
const block = firstBlockToolEffect(beforeEffects);
|
|
378
|
+
if (block) {
|
|
379
|
+
const verdict = guardBlockedVerdict(decision, call.tool, block.reason);
|
|
380
|
+
recordRegistryDisposition(call, verdict.decision, "blocked", {
|
|
381
|
+
reasonCode: GUARD_BLOCK_REASON_CODE,
|
|
382
|
+
reasons: [block.reason],
|
|
383
|
+
});
|
|
384
|
+
return verdict;
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
const preparedArgs = prepareToolArgs(spec, call.args ?? {});
|
|
388
|
+
const result = shapeToolResult(spec, await spec.run(preparedArgs, options), options);
|
|
389
|
+
const afterEffects = runToolHook("after_tool", spec, call, decision, options, result);
|
|
390
|
+
const finalResult = shapeToolResult(spec, applyToolResultEffects(result, afterEffects), options);
|
|
391
|
+
return { kind: "ok", result: finalResult, decision };
|
|
392
|
+
} catch (err) {
|
|
393
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
394
|
+
const result = shapeToolResult(spec, { kind: "error", message }, options);
|
|
395
|
+
const afterEffects = runToolHook("after_tool", spec, call, decision, options, result);
|
|
396
|
+
return {
|
|
397
|
+
kind: "ok",
|
|
398
|
+
result: shapeToolResult(spec, applyToolResultEffects(result, afterEffects), options),
|
|
399
|
+
decision,
|
|
400
|
+
};
|
|
401
|
+
} finally {
|
|
402
|
+
disposeAdmissionArgs(spec, call.args ?? {});
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const runToolHook = (
|
|
407
|
+
hook: "before_tool" | "after_tool",
|
|
408
|
+
spec: ToolSpec,
|
|
409
|
+
call: ClassifierCall,
|
|
410
|
+
decision: SafetyDecision,
|
|
411
|
+
options: ToolInvokeOptions | undefined,
|
|
412
|
+
result?: ToolResult,
|
|
413
|
+
): ReadonlyArray<MiddlewareEffect> => {
|
|
414
|
+
if (!deps.middleware) return [];
|
|
415
|
+
const input = buildToolHookInput(hook, spec, call, decision, "operating", options, result);
|
|
416
|
+
const effects = deps.middleware.runHook(input).effects;
|
|
417
|
+
try {
|
|
418
|
+
deps.onMiddlewareEffects?.(effects, input);
|
|
419
|
+
} catch {
|
|
420
|
+
// A provider-routing sink is advisory to this call. It may narrow the
|
|
421
|
+
// next request, but it must never throw through tool admission.
|
|
422
|
+
}
|
|
423
|
+
return effects;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
type AdmitOutcome =
|
|
427
|
+
| { kind: "terminal"; verdict: RegistryVerdict }
|
|
428
|
+
| { kind: "execute"; spec: ToolSpec; decision: SafetyDecision }
|
|
429
|
+
| { kind: "park"; decision: SafetyDecision; axis: string };
|
|
430
|
+
|
|
431
|
+
const admit = (call: ClassifierCall, grant?: OneShotGrant, options?: ToolInvokeOptions): AdmitOutcome => {
|
|
432
|
+
const spec = tools.get(call.tool as ToolName);
|
|
433
|
+
if (!spec) {
|
|
434
|
+
return { kind: "terminal", verdict: { kind: "not_visible", reason: `tool not registered: ${call.tool}` } };
|
|
435
|
+
}
|
|
436
|
+
const level = deps.autonomy?.() ?? DEFAULT_AUTONOMY_LEVEL;
|
|
437
|
+
const decision = applyRegisteredToolClassification(deps.safety.evaluate(call, grant ? "confirmed" : undefined), spec);
|
|
438
|
+
// Stage 1, the safety net (level-independent): engine blocks are final;
|
|
439
|
+
// engine asks are confirm rails that park at every level. read-only is
|
|
440
|
+
// the exception by definition: approvals are never invoked there, so a
|
|
441
|
+
// confirm rail resolves as the same auto-deny as any other mutation.
|
|
442
|
+
if (decision.kind === "block") {
|
|
443
|
+
return { kind: "terminal", verdict: { kind: "blocked", reason: decision.rejection.short, decision } };
|
|
444
|
+
}
|
|
445
|
+
// Stage 1.5, the skill tool surface: a loaded SKILL.md that declares
|
|
446
|
+
// allowed-tools or disallowed-tools narrows the surface until the
|
|
447
|
+
// policy's lifetime ends (turn end for the main agent, run end for
|
|
448
|
+
// workers). Narrowing only blocks; it never grants, and an out-of-
|
|
449
|
+
// surface call blocks terminally instead of parking for confirmation.
|
|
450
|
+
const surfaceViolation = evaluateSkillToolSurface(options?.pendingSkillPolicy, call.tool);
|
|
451
|
+
if (surfaceViolation) {
|
|
452
|
+
const verdict = skillSurfaceBlockedVerdict(decision, call.tool, surfaceViolation);
|
|
453
|
+
recordRegistryDisposition(call, verdict.decision, "blocked", {
|
|
454
|
+
reasonCode: "skill_surface",
|
|
455
|
+
reasons: [verdict.reason],
|
|
456
|
+
});
|
|
457
|
+
return { kind: "terminal", verdict };
|
|
458
|
+
}
|
|
459
|
+
const actionClass = decision.classification.actionClass;
|
|
460
|
+
if (decision.kind === "ask") {
|
|
461
|
+
if (level === "read-only") {
|
|
462
|
+
const verdict = autonomyDenyVerdict(decision, level, call.tool, actionClass);
|
|
463
|
+
recordRegistryDisposition(call, verdict.decision, "denied", { reasonCode: `autonomy:${level}` });
|
|
464
|
+
notifyAutonomyDenied(call, verdict.decision, level);
|
|
465
|
+
return { kind: "terminal", verdict };
|
|
466
|
+
}
|
|
467
|
+
if (grant?.actionClass === actionClass) return { kind: "execute", spec, decision };
|
|
468
|
+
return { kind: "park", decision, axis: approvalAxisId(decision, level) };
|
|
469
|
+
}
|
|
470
|
+
// One-shot grant: resumeParkedCalls re-admits exactly the parked call
|
|
471
|
+
// the operator approved, with a confirmed posture. The engine converts
|
|
472
|
+
// its confirm rail to an allow (including M3 git ask rules), so the
|
|
473
|
+
// grant match executes directly instead of re-entering the mapping.
|
|
474
|
+
if (grant?.actionClass === actionClass) {
|
|
475
|
+
return { kind: "execute", spec, decision };
|
|
476
|
+
}
|
|
477
|
+
if (actionClass === "git_destructive") {
|
|
478
|
+
recordRegistryDisposition(call, decision, "blocked", {
|
|
479
|
+
reasons: [`action ${actionClass} is hard-blocked`],
|
|
480
|
+
reasonCode: "classification:git_destructive",
|
|
481
|
+
});
|
|
482
|
+
return {
|
|
483
|
+
kind: "terminal",
|
|
484
|
+
verdict: {
|
|
485
|
+
kind: "blocked",
|
|
486
|
+
reason: `action ${actionClass} is hard-blocked`,
|
|
487
|
+
decision,
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
// Stage 2, the autonomy mapping (sd-01 §2.3): the net passed; the level
|
|
492
|
+
// decides run / ask / deny per action class. Plan-scale dispatch calls
|
|
493
|
+
// (multi-task, compete, remote node) carry the plan flag so supervised
|
|
494
|
+
// levels route them through one plan approval.
|
|
495
|
+
const dispatchPlan =
|
|
496
|
+
call.tool === ToolNames.Dispatch
|
|
497
|
+
? (spec.describeDispatchPlan?.(call.args ?? {}) ?? describeDispatchPlan(call.args))
|
|
498
|
+
: null;
|
|
499
|
+
const planScale = dispatchPlan?.planScale === true;
|
|
500
|
+
// The exposure tier rides on the args of the gate that raised it, so the
|
|
501
|
+
// workflow author, not the classifier, decides what counts as outward.
|
|
502
|
+
// ask_user is the only tool that declares one today.
|
|
503
|
+
const exposure = call.tool === ToolNames.AskUser ? askUserExposure(call.args) : DEFAULT_AUTONOMY_EXPOSURE;
|
|
504
|
+
const disposition = mapAutonomy(level, actionClass, {
|
|
505
|
+
executeRecognized: decision.policy?.execRecognition !== "unrecognized",
|
|
506
|
+
...(planScale ? { dispatchPlanScale: true } : {}),
|
|
507
|
+
...(exposure === "outward" ? { exposure } : {}),
|
|
508
|
+
});
|
|
509
|
+
if (disposition === "deny") {
|
|
510
|
+
const verdict = autonomyDenyVerdict(decision, level, call.tool, actionClass);
|
|
511
|
+
recordRegistryDisposition(call, verdict.decision, "denied", { reasonCode: `autonomy:${level}` });
|
|
512
|
+
notifyAutonomyDenied(call, verdict.decision, level);
|
|
513
|
+
return { kind: "terminal", verdict };
|
|
514
|
+
}
|
|
515
|
+
if (disposition === "ask") {
|
|
516
|
+
const askDecision =
|
|
517
|
+
planScale && dispatchPlan !== null
|
|
518
|
+
? toDispatchPlanAskDecision(decision, level, dispatchPlan)
|
|
519
|
+
: toAutonomyAskDecision(decision, level, call.tool, actionClass, exposure);
|
|
520
|
+
return { kind: "park", decision: askDecision, axis: approvalAxisId(askDecision, level) };
|
|
521
|
+
}
|
|
522
|
+
recordRegistryDisposition(call, decision, "allowed");
|
|
523
|
+
return { kind: "execute", spec, decision };
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const recordRegistryDisposition = (
|
|
527
|
+
call: ClassifierCall,
|
|
528
|
+
decision: SafetyDecision,
|
|
529
|
+
auditDecision: "allowed" | "blocked" | "permission_requested" | "denied",
|
|
530
|
+
overrides?: { reasons?: ReadonlyArray<string>; reasonCode?: string; requestId?: string },
|
|
531
|
+
): void => {
|
|
532
|
+
// Row sequence for net-pass calls: safety.evaluate writes `classified`;
|
|
533
|
+
// registry admission writes the final autonomy disposition. Confirmed
|
|
534
|
+
// re-admissions keep their existing `allowed` row from safety.evaluate.
|
|
535
|
+
// When the registry, not the policy engine, made the final call, the
|
|
536
|
+
// caller passes a reasonCode override so the row does not repeat the
|
|
537
|
+
// net pass's "allowed" code on a non-allowed decision.
|
|
538
|
+
const reasons = overrides?.reasons;
|
|
539
|
+
deps.safety.audit.recordToolCall?.({
|
|
540
|
+
tool: call.tool,
|
|
541
|
+
classification: decision.classification,
|
|
542
|
+
decision: auditDecision,
|
|
543
|
+
args: call.args,
|
|
544
|
+
...(overrides?.requestId !== undefined ? { requestId: overrides.requestId } : {}),
|
|
545
|
+
...(decision.policy !== undefined ? { policy: decision.policy } : {}),
|
|
546
|
+
...(overrides?.reasonCode !== undefined ? { reasonCode: overrides.reasonCode } : {}),
|
|
547
|
+
...(reasons !== undefined ? { reasons } : decision.kind === "allow" ? {} : { reasons: [decision.rejection.detail] }),
|
|
548
|
+
});
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Loop-observe a safety-blocked attempt. The verdict stands and every
|
|
553
|
+
* effect is discarded; this exists so the before_tool loop guard sees
|
|
554
|
+
* rejected attempts too. Without it, a model repeating an identical
|
|
555
|
+
* blocked call would never trip the detector (the former worker guard sat
|
|
556
|
+
* in front of admission and had this coverage).
|
|
557
|
+
*/
|
|
558
|
+
const observeBlockedAttempt = (
|
|
559
|
+
call: ClassifierCall,
|
|
560
|
+
verdict: RegistryVerdict,
|
|
561
|
+
options?: ToolInvokeOptions,
|
|
562
|
+
): RegistryVerdict | null => {
|
|
563
|
+
if (verdict.kind !== "blocked") return null;
|
|
564
|
+
return guardOverrideForRejectedAttempt(
|
|
565
|
+
call,
|
|
566
|
+
verdict.decision,
|
|
567
|
+
observeRejectedAttempt(call, verdict.decision, options),
|
|
568
|
+
);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Run before_tool hooks for an attempt that will not execute, so repetition
|
|
573
|
+
* detectors see it. Most effects do not change the rejection itself; the
|
|
574
|
+
* worker tool-call cap is the exception, because it is the deterministic
|
|
575
|
+
* run bound for denied-call spirals. The returned value is the first
|
|
576
|
+
* block_tool reason (the loop guard's actionable feedback), which the
|
|
577
|
+
* park-denial path substitutes for its generic reason so a model retrying a
|
|
578
|
+
* denied call learns to stop instead of looping until the run times out.
|
|
579
|
+
*/
|
|
580
|
+
const observeRejectedAttempt = (
|
|
581
|
+
call: ClassifierCall,
|
|
582
|
+
decision: SafetyDecision,
|
|
583
|
+
options?: ToolInvokeOptions,
|
|
584
|
+
): string | null => {
|
|
585
|
+
if (!deps.middleware) return null;
|
|
586
|
+
const spec = tools.get(call.tool as ToolName);
|
|
587
|
+
if (!spec) return null;
|
|
588
|
+
const effects = runToolHook("before_tool", spec, call, decision, options);
|
|
589
|
+
return firstBlockToolEffect(effects)?.reason ?? null;
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const guardOverrideForRejectedAttempt = (
|
|
593
|
+
call: ClassifierCall,
|
|
594
|
+
decision: SafetyDecision,
|
|
595
|
+
reason: string | null,
|
|
596
|
+
): RegistryVerdict | null => {
|
|
597
|
+
if (reason === null || !isWorkerToolCallCapExceededReason(reason)) return null;
|
|
598
|
+
const verdict = guardBlockedVerdict(decision, call.tool, reason);
|
|
599
|
+
recordRegistryDisposition(call, verdict.decision, "blocked", {
|
|
600
|
+
reasonCode: GUARD_BLOCK_REASON_CODE,
|
|
601
|
+
reasons: [reason],
|
|
602
|
+
});
|
|
603
|
+
return verdict;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
const cleanupParkedEntry = (entry: ParkedCall): void => {
|
|
607
|
+
entry.abortCleanup?.();
|
|
608
|
+
delete entry.abortCleanup;
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
const resolveParkedAsBlocked = (entry: ParkedCall, reason: string): void => {
|
|
612
|
+
cleanupParkedEntry(entry);
|
|
613
|
+
disposeAdmissionArgs(tools.get(entry.call.tool as ToolName), entry.call.args ?? {});
|
|
614
|
+
// A denied/cancelled park is still a model attempt: observe it so
|
|
615
|
+
// identical retries trip the loop detector. When the detector fires, its
|
|
616
|
+
// reason replaces the generic denial so the model gets recovery guidance.
|
|
617
|
+
const loopReason = observeRejectedAttempt(entry.call, entry.decision, entry.options);
|
|
618
|
+
entry.resolve(
|
|
619
|
+
guardOverrideForRejectedAttempt(entry.call, entry.decision, loopReason) ??
|
|
620
|
+
parkAnsweredBlockedVerdict(entry.decision, entry.call.tool, loopReason ?? reason),
|
|
621
|
+
);
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const reparkEntry = (entry: ParkedCall, index?: number): void => {
|
|
625
|
+
if (index === undefined || index < 0 || index >= parked.length) {
|
|
626
|
+
parked.push(entry);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
parked.splice(index, 0, entry);
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const nextApprovalRequestId = (): string => `apr-${approvalRequestToken}-${++approvalRequestCounter}`;
|
|
633
|
+
|
|
634
|
+
const notifyPermissionRequired = (
|
|
635
|
+
call: ClassifierCall,
|
|
636
|
+
decision: SafetyDecision,
|
|
637
|
+
meta: PermissionRequiredMeta,
|
|
638
|
+
): void => {
|
|
639
|
+
for (const listener of permissionListeners) {
|
|
640
|
+
try {
|
|
641
|
+
listener(call, decision, meta);
|
|
642
|
+
} catch {
|
|
643
|
+
// Listener errors never abort admission; they are surfaced via
|
|
644
|
+
// whatever observability the caller wires up.
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const notifyAutonomyDenied = (call: ClassifierCall, decision: SafetyDecision, level: AutonomyLevel): void => {
|
|
650
|
+
for (const listener of autonomyDeniedListeners) {
|
|
651
|
+
try {
|
|
652
|
+
listener(call, decision, level);
|
|
653
|
+
} catch {
|
|
654
|
+
// Same contract as permission listeners: never abort admission.
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const cancelParkedCallById = (requestId: string, reason: string): boolean => {
|
|
660
|
+
const index = parked.findIndex((entry) => entry.meta.requestId === requestId);
|
|
661
|
+
if (index === -1) return false;
|
|
662
|
+
const [entry] = parked.splice(index, 1);
|
|
663
|
+
if (!entry) return false;
|
|
664
|
+
resolveParkedAsBlocked(entry, reason);
|
|
665
|
+
const next = parked[0];
|
|
666
|
+
if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
|
|
667
|
+
return true;
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
return {
|
|
671
|
+
register(spec) {
|
|
672
|
+
tools.set(spec.name, spec);
|
|
673
|
+
},
|
|
674
|
+
listAll: () => Array.from(tools.values()),
|
|
675
|
+
get: (name) => tools.get(name),
|
|
676
|
+
listRegistered: () => Array.from(tools.keys()),
|
|
677
|
+
listVisible: () => Array.from(tools.values()),
|
|
678
|
+
async invoke(call, options) {
|
|
679
|
+
const admissionCall = prepareAdmissionCall(tools.get(call.tool as ToolName), call);
|
|
680
|
+
const outcome = admit(admissionCall, undefined, options);
|
|
681
|
+
if (outcome.kind === "terminal") {
|
|
682
|
+
disposeAdmissionArgs(tools.get(admissionCall.tool as ToolName), admissionCall.args ?? {});
|
|
683
|
+
return observeBlockedAttempt(admissionCall, outcome.verdict, options) ?? outcome.verdict;
|
|
684
|
+
}
|
|
685
|
+
if (outcome.kind === "execute") return runSpec(outcome.spec, admissionCall, outcome.decision, options);
|
|
686
|
+
const abortReason = "run aborted before the operator decided";
|
|
687
|
+
if (options?.signal?.aborted) {
|
|
688
|
+
disposeAdmissionArgs(tools.get(admissionCall.tool as ToolName), admissionCall.args ?? {});
|
|
689
|
+
const loopReason = observeRejectedAttempt(admissionCall, outcome.decision, options);
|
|
690
|
+
return { kind: "blocked", reason: loopReason ?? abortReason, decision: outcome.decision };
|
|
691
|
+
}
|
|
692
|
+
return new Promise<RegistryVerdict>((resolve) => {
|
|
693
|
+
const meta: PermissionRequiredMeta = {
|
|
694
|
+
requestId: nextApprovalRequestId(),
|
|
695
|
+
axis: outcome.axis,
|
|
696
|
+
...(options?.toolCallId !== undefined && options.toolCallId.length > 0 ? { toolCallId: options.toolCallId } : {}),
|
|
697
|
+
};
|
|
698
|
+
recordRegistryDisposition(admissionCall, outcome.decision, "permission_requested", { requestId: meta.requestId });
|
|
699
|
+
// The park ends when the operator decides, not when the verdict
|
|
700
|
+
// resolves: an approved call runs its body inside the resume pass,
|
|
701
|
+
// so measuring to the resolve would fold the tool's own execution
|
|
702
|
+
// into the park and the caller would subtract it. A dispatch that
|
|
703
|
+
// fanned out for 224.8s settled as a 15ms line that way (issue #82).
|
|
704
|
+
// closePark seals it at the decision; settle covers every path that
|
|
705
|
+
// ends a park without reaching a body, including one added later,
|
|
706
|
+
// because they all resolve the entry through this function.
|
|
707
|
+
const parkedAtClock = performance.now();
|
|
708
|
+
let parkReported = false;
|
|
709
|
+
const closePark = (): void => {
|
|
710
|
+
if (parkReported) return;
|
|
711
|
+
parkReported = true;
|
|
712
|
+
options?.onParked?.(Math.round(performance.now() - parkedAtClock));
|
|
713
|
+
};
|
|
714
|
+
const settle = (verdict: RegistryVerdict): void => {
|
|
715
|
+
closePark();
|
|
716
|
+
resolve(verdict);
|
|
717
|
+
};
|
|
718
|
+
const parkedCall: ParkedCall = {
|
|
719
|
+
call: admissionCall,
|
|
720
|
+
decision: outcome.decision,
|
|
721
|
+
meta,
|
|
722
|
+
resolve: settle,
|
|
723
|
+
closePark,
|
|
724
|
+
};
|
|
725
|
+
if (options !== undefined) parkedCall.options = options;
|
|
726
|
+
if (options?.signal) {
|
|
727
|
+
const onAbort = (): void => {
|
|
728
|
+
cancelParkedCallById(meta.requestId, abortReason);
|
|
729
|
+
};
|
|
730
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
731
|
+
parkedCall.abortCleanup = () => {
|
|
732
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
parked.push(parkedCall);
|
|
736
|
+
notifyPermissionRequired(admissionCall, outcome.decision, meta);
|
|
737
|
+
});
|
|
738
|
+
},
|
|
739
|
+
hasParkedCalls: () => parked.length > 0,
|
|
740
|
+
parkedCount: () => parked.length,
|
|
741
|
+
renotifyHead() {
|
|
742
|
+
const next = parked[0];
|
|
743
|
+
if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
|
|
744
|
+
},
|
|
745
|
+
async resumeParkedCalls(grant?: OneShotGrant) {
|
|
746
|
+
if (parked.length === 0) return;
|
|
747
|
+
const pending: Array<{ entry: ParkedCall; reparkIndex?: number }> = [];
|
|
748
|
+
if (grant === undefined) {
|
|
749
|
+
pending.push(...parked.splice(0, parked.length).map((entry) => ({ entry })));
|
|
750
|
+
} else if (grant.requestId !== undefined) {
|
|
751
|
+
const index = parked.findIndex((entry) => entry.meta.requestId === grant.requestId);
|
|
752
|
+
if (index === -1) {
|
|
753
|
+
const next = parked[0];
|
|
754
|
+
if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const [entry] = parked.splice(index, 1);
|
|
758
|
+
if (entry) pending.push({ entry, reparkIndex: index });
|
|
759
|
+
} else {
|
|
760
|
+
const [entry] = parked.splice(0, 1);
|
|
761
|
+
if (entry) pending.push({ entry });
|
|
762
|
+
}
|
|
763
|
+
for (const { entry, reparkIndex } of pending) {
|
|
764
|
+
// A one-shot grant covers only the parked call it selected. Calls
|
|
765
|
+
// that parked while the overlay was already open remain queued and
|
|
766
|
+
// need their own confirmation, so a concurrent privileged call
|
|
767
|
+
// cannot ride along on a grant approved for another call.
|
|
768
|
+
if (grant !== undefined && entry.decision.classification.actionClass !== grant.actionClass) {
|
|
769
|
+
reparkEntry(entry, reparkIndex);
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const outcome = admit(entry.call, grant, entry.options);
|
|
773
|
+
if (outcome.kind === "park") {
|
|
774
|
+
reparkEntry(entry, reparkIndex);
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
cleanupParkedEntry(entry);
|
|
778
|
+
if (outcome.kind === "terminal") {
|
|
779
|
+
disposeAdmissionArgs(tools.get(entry.call.tool as ToolName), entry.call.args ?? {});
|
|
780
|
+
entry.resolve(observeBlockedAttempt(entry.call, outcome.verdict, entry.options) ?? outcome.verdict);
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const approvedOptions: ToolInvokeOptions | undefined =
|
|
784
|
+
grant === undefined
|
|
785
|
+
? entry.options
|
|
786
|
+
: {
|
|
787
|
+
...(entry.options ?? {}),
|
|
788
|
+
approval: {
|
|
789
|
+
requestId: entry.meta.requestId,
|
|
790
|
+
requestedBy: grant.requestedBy,
|
|
791
|
+
actionClass: grant.actionClass,
|
|
792
|
+
},
|
|
793
|
+
};
|
|
794
|
+
// Seal the park before the body runs. Everything after this line is
|
|
795
|
+
// the tool working, and the caller charges that to the tool.
|
|
796
|
+
entry.closePark();
|
|
797
|
+
entry.resolve(await runSpec(outcome.spec, entry.call, outcome.decision, approvedOptions));
|
|
798
|
+
}
|
|
799
|
+
const next = parked[0];
|
|
800
|
+
if (next) notifyPermissionRequired(next.call, next.decision, next.meta);
|
|
801
|
+
},
|
|
802
|
+
cancelParkedCall(requestId, reason) {
|
|
803
|
+
return cancelParkedCallById(requestId, reason);
|
|
804
|
+
},
|
|
805
|
+
cancelParkedCalls(reason) {
|
|
806
|
+
if (parked.length === 0) return;
|
|
807
|
+
const pending = parked.splice(0, parked.length);
|
|
808
|
+
for (const entry of pending) {
|
|
809
|
+
resolveParkedAsBlocked(entry, reason);
|
|
810
|
+
}
|
|
811
|
+
},
|
|
812
|
+
onPermissionRequired(listener) {
|
|
813
|
+
permissionListeners.add(listener);
|
|
814
|
+
return () => {
|
|
815
|
+
permissionListeners.delete(listener);
|
|
816
|
+
};
|
|
817
|
+
},
|
|
818
|
+
onAutonomyDenied(listener) {
|
|
819
|
+
autonomyDeniedListeners.add(listener);
|
|
820
|
+
return () => {
|
|
821
|
+
autonomyDeniedListeners.delete(listener);
|
|
822
|
+
};
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Run a tool's optional `prepareArguments` normalizer before its body. A
|
|
829
|
+
* throwing or non-object result is discarded so a buggy normalizer can never
|
|
830
|
+
* abort admission; the raw args pass through unchanged.
|
|
831
|
+
*/
|
|
832
|
+
function prepareToolArgs(spec: ToolSpec, args: Record<string, unknown>): Record<string, unknown> {
|
|
833
|
+
if (!spec.prepareArguments) return args;
|
|
834
|
+
try {
|
|
835
|
+
const prepared = spec.prepareArguments(args);
|
|
836
|
+
return prepared !== null && typeof prepared === "object" && !Array.isArray(prepared) ? prepared : args;
|
|
837
|
+
} catch {
|
|
838
|
+
return args;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function disposeAdmissionArgs(spec: ToolSpec | undefined, args: Record<string, unknown>): void {
|
|
843
|
+
try {
|
|
844
|
+
spec?.disposeAdmissionArguments?.(args);
|
|
845
|
+
} catch {
|
|
846
|
+
// Cleanup is best-effort here; durable reservation expiry is the backstop.
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function prepareAdmissionCall(spec: ToolSpec | undefined, call: ClassifierCall): ClassifierCall {
|
|
851
|
+
if (!spec?.prepareAdmissionArguments) return call;
|
|
852
|
+
try {
|
|
853
|
+
const prepared = spec.prepareAdmissionArguments(call.args ?? {});
|
|
854
|
+
if (prepared === null || typeof prepared !== "object" || Array.isArray(prepared)) return call;
|
|
855
|
+
return { ...call, args: prepared };
|
|
856
|
+
} catch {
|
|
857
|
+
return call;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function applyRegisteredToolClassification(decision: SafetyDecision, spec: ToolSpec): SafetyDecision {
|
|
862
|
+
if (decision.classification.actionClass !== "unknown") return decision;
|
|
863
|
+
const classification = {
|
|
864
|
+
actionClass: spec.baseActionClass,
|
|
865
|
+
reasons: [`registered tool: ${spec.name}`],
|
|
866
|
+
};
|
|
867
|
+
return decision.kind === "allow" ? { kind: "allow", classification } : { ...decision, classification };
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/** Final reason code for a before_tool guard block, matching the audit convention (sd-01 §2.5). */
|
|
871
|
+
const GUARD_BLOCK_REASON_CODE = "guard_block";
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Terminal blocked verdict for a before_tool guard block (loop guard,
|
|
875
|
+
* protected artifacts, dispatch dedup, declarative block rules) on a call
|
|
876
|
+
* whose admission already passed. The decision is re-shaped as a block, and
|
|
877
|
+
* the carried policy's net-pass reasonCode is replaced with the guard axis,
|
|
878
|
+
* so downstream consumers (worker finish events, dispatch receipts, audit)
|
|
879
|
+
* count a blocked safety decision instead of repeating the admission's allow.
|
|
880
|
+
*/
|
|
881
|
+
function guardBlockedVerdict(
|
|
882
|
+
decision: SafetyDecision,
|
|
883
|
+
tool: string,
|
|
884
|
+
reason: string,
|
|
885
|
+
): Extract<RegistryVerdict, { kind: "blocked" }> {
|
|
886
|
+
const blocked: SafetyDecision = {
|
|
887
|
+
kind: "block",
|
|
888
|
+
classification: decision.classification,
|
|
889
|
+
rejection: {
|
|
890
|
+
short: `${tool} blocked: tool guard`,
|
|
891
|
+
detail: reason,
|
|
892
|
+
hints: [],
|
|
893
|
+
},
|
|
894
|
+
...(decision.policy !== undefined ? { policy: { ...decision.policy, reasonCode: GUARD_BLOCK_REASON_CODE } } : {}),
|
|
895
|
+
};
|
|
896
|
+
return { kind: "blocked", reason, decision: blocked };
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Longest guidance sentence carried into a headless denial. The model-facing
|
|
901
|
+
* composer caps a line at 300 characters and interpolates policy text verbatim,
|
|
902
|
+
* so bound it here too and leave room for the elision marker.
|
|
903
|
+
*/
|
|
904
|
+
const HEADLESS_GUIDANCE_MAX_CHARS = 240;
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* The recovery guidance a headless denial should repeat, or null when the
|
|
908
|
+
* parked decision has none.
|
|
909
|
+
*
|
|
910
|
+
* A headless run answers every ask with one sentence: nobody can confirm, rerun
|
|
911
|
+
* interactively. That says nothing about what would have run, so a headless
|
|
912
|
+
* model that hits an unrecognized-bash rail retries the same shape until the run
|
|
913
|
+
* ends. An unrecognized execution decision carries the working form in its
|
|
914
|
+
* reasons, and those reasons reach the audit record and the interactive approval
|
|
915
|
+
* overlay but not the model. The first reason states the axis the denial already
|
|
916
|
+
* implies, so only the reasons after it are candidates, and only the first of
|
|
917
|
+
* those, bounded: this is a nudge toward the recognized spelling, not a dump of
|
|
918
|
+
* the policy's reason list.
|
|
919
|
+
*/
|
|
920
|
+
function headlessDenialGuidance(decision: SafetyDecision, reason: string): string | null {
|
|
921
|
+
// Interactive denials are left exactly as they were: the operator saw the
|
|
922
|
+
// reasons in the approval overlay before answering.
|
|
923
|
+
if (!reason.startsWith(HEADLESS_PERMISSION_DENIED_MARKER)) return null;
|
|
924
|
+
const policy = decision.policy;
|
|
925
|
+
if (policy?.execRecognition !== "unrecognized") return null;
|
|
926
|
+
const guidance = policy.reasons.slice(1).find((entry) => entry.trim().length > 0);
|
|
927
|
+
if (guidance === undefined) return null;
|
|
928
|
+
const trimmed = guidance.trim();
|
|
929
|
+
return trimmed.length > HEADLESS_GUIDANCE_MAX_CHARS ? `${trimmed.slice(0, HEADLESS_GUIDANCE_MAX_CHARS)}…` : trimmed;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Terminal blocked verdict for a parked call that has been answered: denied at
|
|
934
|
+
* the confirmation prompt, cancelled with the turn, or settled by an abort.
|
|
935
|
+
*
|
|
936
|
+
* The ask decision's rejection explains that the call *is* parked and what
|
|
937
|
+
* approving it would do. Once the answer is in, that text is stale, and
|
|
938
|
+
* `formatModelRejection` printed it as a second paragraph restating a denial the
|
|
939
|
+
* reason had already stated, followed by four approval hints for an approval
|
|
940
|
+
* that is not coming. The answer is the whole message; the composer still closes
|
|
941
|
+
* it with the standing pivot instruction.
|
|
942
|
+
*
|
|
943
|
+
* A headless answer is the exception: nobody will ever approve it, so the detail
|
|
944
|
+
* carries one line of recovery guidance after the denial sentence. The sentence
|
|
945
|
+
* stays the detail's prefix, which the skill-eval permission-wall recognizer
|
|
946
|
+
* depends on.
|
|
947
|
+
*/
|
|
948
|
+
function parkAnsweredBlockedVerdict(
|
|
949
|
+
decision: SafetyDecision,
|
|
950
|
+
tool: string,
|
|
951
|
+
reason: string,
|
|
952
|
+
): Extract<RegistryVerdict, { kind: "blocked" }> {
|
|
953
|
+
const guidance = headlessDenialGuidance(decision, reason);
|
|
954
|
+
const blocked: SafetyDecision = {
|
|
955
|
+
kind: "block",
|
|
956
|
+
classification: decision.classification,
|
|
957
|
+
rejection: {
|
|
958
|
+
short: `${tool} blocked: ${decision.classification.actionClass} was not approved`,
|
|
959
|
+
detail: guidance === null ? reason : `${reason}\n${guidance}`,
|
|
960
|
+
hints: [],
|
|
961
|
+
},
|
|
962
|
+
...(decision.policy !== undefined ? { policy: decision.policy } : {}),
|
|
963
|
+
};
|
|
964
|
+
return { kind: "blocked", reason, decision: blocked };
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Terminal blocked verdict for a call outside the merged tool surface the
|
|
969
|
+
* loaded skills declared. The reason carries the remediation, since blocked
|
|
970
|
+
* reasons are what the model reads; context and ask_user stay exempt at
|
|
971
|
+
* the evaluator so the message can honestly point at ask_user.
|
|
972
|
+
*/
|
|
973
|
+
function skillSurfaceBlockedVerdict(
|
|
974
|
+
decision: SafetyDecision,
|
|
975
|
+
tool: string,
|
|
976
|
+
violation: SkillToolSurfaceViolation,
|
|
977
|
+
): Extract<RegistryVerdict, { kind: "blocked" }> {
|
|
978
|
+
const reason =
|
|
979
|
+
violation.disallowedBy.length > 0
|
|
980
|
+
? `${tool} is disallowed by the active skill(s) ${violation.disallowedBy.join(", ")} (disallowed-tools). The narrowing ends when the skill policy's turn or worker run ends. Work within the skill workflow; if it genuinely needs this step, use ask_user when available or state the blocker in your reply.`
|
|
981
|
+
: `${tool} is outside the tool surface declared by the active skill(s) ${violation.skills.join(", ")}. Tools are narrowed to: ${(violation.mergedAllowedTools ?? []).join(", ")} (plus context and ask_user). The narrowing ends when the skill policy's turn or worker run ends. Work within the skill workflow; if it genuinely needs this step, use ask_user when available or state the blocker in your reply.`;
|
|
982
|
+
const blocked: SafetyDecision = {
|
|
983
|
+
kind: "block",
|
|
984
|
+
classification: decision.classification,
|
|
985
|
+
rejection: {
|
|
986
|
+
short: `${tool} blocked: outside active skill tool surface`,
|
|
987
|
+
detail: reason,
|
|
988
|
+
hints: ["Skill narrowing never grants tools; it only blocks calls outside the declared workflow surface."],
|
|
989
|
+
},
|
|
990
|
+
...(decision.policy !== undefined ? { policy: decision.policy } : {}),
|
|
991
|
+
};
|
|
992
|
+
return { kind: "blocked", reason, decision: blocked };
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Terminal blocked verdict for an autonomy `deny` disposition (read-only).
|
|
997
|
+
* The decision is re-shaped as a block so downstream consumers (worker
|
|
998
|
+
* events, dispatch receipts) report it as a denial, not a pending ask.
|
|
999
|
+
*/
|
|
1000
|
+
function autonomyDenyVerdict(
|
|
1001
|
+
decision: SafetyDecision,
|
|
1002
|
+
level: AutonomyLevel,
|
|
1003
|
+
tool: string,
|
|
1004
|
+
actionClass: ActionClass,
|
|
1005
|
+
): Extract<RegistryVerdict, { kind: "blocked" }> {
|
|
1006
|
+
const rejection = autonomyDenyRejection(level, tool, actionClass);
|
|
1007
|
+
const blocked: SafetyDecision = {
|
|
1008
|
+
kind: "block",
|
|
1009
|
+
classification: decision.classification,
|
|
1010
|
+
rejection,
|
|
1011
|
+
...(decision.policy !== undefined ? { policy: decision.policy } : {}),
|
|
1012
|
+
};
|
|
1013
|
+
return { kind: "blocked", reason: rejection.short, decision: blocked };
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Park-shaped decision for an autonomy `ask` disposition. The engine passed
|
|
1018
|
+
* the call, so the rejection names the level as the asking axis; overlays and
|
|
1019
|
+
* non-interactive deniers read it from here. An outward-exposure park names
|
|
1020
|
+
* the tier instead of the action class, because the class is not why it asked.
|
|
1021
|
+
*/
|
|
1022
|
+
function toAutonomyAskDecision(
|
|
1023
|
+
decision: SafetyDecision,
|
|
1024
|
+
level: AutonomyLevel,
|
|
1025
|
+
tool: string,
|
|
1026
|
+
actionClass: ActionClass,
|
|
1027
|
+
exposure: AutonomyExposure = DEFAULT_AUTONOMY_EXPOSURE,
|
|
1028
|
+
): SafetyDecision {
|
|
1029
|
+
return {
|
|
1030
|
+
kind: "ask",
|
|
1031
|
+
classification: decision.classification,
|
|
1032
|
+
rejection: autonomyAskRejection(level, tool, actionClass, exposure),
|
|
1033
|
+
...(decision.policy !== undefined ? { policy: decision.policy } : {}),
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Plan-approval ask for a plan-scale dispatch call. The rejection detail IS
|
|
1039
|
+
* the plan artifact (topology, per-task agent/model/node), so the approval
|
|
1040
|
+
* overlay shows the operator exactly what one approval will launch.
|
|
1041
|
+
*/
|
|
1042
|
+
function toDispatchPlanAskDecision(
|
|
1043
|
+
decision: SafetyDecision,
|
|
1044
|
+
level: AutonomyLevel,
|
|
1045
|
+
plan: DispatchPlanView,
|
|
1046
|
+
): SafetyDecision {
|
|
1047
|
+
return {
|
|
1048
|
+
kind: "ask",
|
|
1049
|
+
classification: decision.classification,
|
|
1050
|
+
rejection: {
|
|
1051
|
+
short: `dispatch plan needs approval (${plan.topology}, ${plan.taskCount} task(s)) at autonomy ${level}`,
|
|
1052
|
+
detail: `Approving this call approves the whole plan:\n${plan.text}`,
|
|
1053
|
+
hints: [
|
|
1054
|
+
"One approval covers every run in the plan, including remote placements.",
|
|
1055
|
+
"Deny to keep the fleet idle and revise the plan first.",
|
|
1056
|
+
],
|
|
1057
|
+
},
|
|
1058
|
+
...(decision.policy !== undefined ? { policy: decision.policy } : {}),
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function buildToolHookInput(
|
|
1063
|
+
hook: "before_tool" | "after_tool",
|
|
1064
|
+
spec: ToolSpec,
|
|
1065
|
+
call: ClassifierCall,
|
|
1066
|
+
decision: SafetyDecision,
|
|
1067
|
+
posture: string,
|
|
1068
|
+
options: ToolInvokeOptions | undefined,
|
|
1069
|
+
result: ToolResult | undefined,
|
|
1070
|
+
): MiddlewareHookInput {
|
|
1071
|
+
const metadata: Record<string, MiddlewareMetadataValue> = {
|
|
1072
|
+
posture,
|
|
1073
|
+
actionClass: decision.classification.actionClass,
|
|
1074
|
+
decisionKind: decision.kind,
|
|
1075
|
+
};
|
|
1076
|
+
// Stable call identity for repetition detectors (engine/loop-guard.ts).
|
|
1077
|
+
// Computed for before_tool only; after_tool consumers identify the call
|
|
1078
|
+
// via toolCallId.
|
|
1079
|
+
if (hook === "before_tool") metadata.callFingerprint = hashToolCall(spec.name, call.args ?? {});
|
|
1080
|
+
const validationCommand = detectedValidationCommand(call);
|
|
1081
|
+
if (validationCommand !== null) {
|
|
1082
|
+
metadata.validationCommand = validationCommand;
|
|
1083
|
+
if (result?.kind === "ok") metadata.validationExitCode = 0;
|
|
1084
|
+
}
|
|
1085
|
+
if (call.tool !== spec.name) metadata.requestedToolName = call.tool;
|
|
1086
|
+
if (result !== undefined) {
|
|
1087
|
+
metadata.resultKind = result.kind;
|
|
1088
|
+
if (result.kind === "error") metadata.errorMessage = result.message;
|
|
1089
|
+
if (result.kind === "ok" && result.terminate === true) metadata.terminate = true;
|
|
1090
|
+
// Result identity for the stagnation detector (engine/loop-guard.ts):
|
|
1091
|
+
// consecutive same-shape calls whose outputs hash identically are not
|
|
1092
|
+
// producing new information, whatever their size arguments say.
|
|
1093
|
+
if (result.kind === "ok" && typeof result.output === "string") {
|
|
1094
|
+
metadata.resultFingerprint = createHash("sha256").update(result.output).digest("hex");
|
|
1095
|
+
metadata.resultBytes = Buffer.byteLength(result.output, "utf8");
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
const input: MiddlewareHookInput = {
|
|
1100
|
+
hook,
|
|
1101
|
+
toolName: spec.name,
|
|
1102
|
+
metadata,
|
|
1103
|
+
};
|
|
1104
|
+
if (call.args !== undefined) input.toolArgs = call.args;
|
|
1105
|
+
if (result?.details !== undefined) input.toolResultDetails = result.details;
|
|
1106
|
+
if (options?.runId !== undefined) input.runId = options.runId;
|
|
1107
|
+
if (options?.sessionId !== undefined) input.sessionId = options.sessionId;
|
|
1108
|
+
if (options?.turnId !== undefined) input.turnId = options.turnId;
|
|
1109
|
+
if (options?.toolCallId !== undefined) input.toolCallId = options.toolCallId;
|
|
1110
|
+
if (options?.correlationId !== undefined) input.correlationId = options.correlationId;
|
|
1111
|
+
return input;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
function commandArg(args: Record<string, unknown> | undefined): string | null {
|
|
1115
|
+
if (!args) return null;
|
|
1116
|
+
return typeof args.command === "string" && args.command.length > 0 ? args.command : null;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function detectedValidationCommand(call: ClassifierCall): string | null {
|
|
1120
|
+
if (call.tool !== ToolNames.Bash) return null;
|
|
1121
|
+
const command = commandArg(call.args);
|
|
1122
|
+
if (command === null) return null;
|
|
1123
|
+
const detected = detectValidationCommand(command);
|
|
1124
|
+
return detected.kind === "validation" ? detected.matched : null;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
function firstBlockToolEffect(
|
|
1128
|
+
effects: ReadonlyArray<MiddlewareEffect>,
|
|
1129
|
+
): Extract<MiddlewareEffect, { kind: "block_tool" }> | null {
|
|
1130
|
+
for (const effect of effects) {
|
|
1131
|
+
if (effect.kind === "block_tool") return effect;
|
|
1132
|
+
}
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function applyToolResultEffects(result: ToolResult, effects: ReadonlyArray<MiddlewareEffect>): ToolResult {
|
|
1137
|
+
const annotations = annotationMessages(effects);
|
|
1138
|
+
if (annotations.length === 0) return result;
|
|
1139
|
+
const suffix = `\n\n${annotations.join("\n")}`;
|
|
1140
|
+
if (result.kind === "ok") {
|
|
1141
|
+
const annotated: ToolResult = { kind: "ok", output: `${result.output}${suffix}` };
|
|
1142
|
+
if (result.details !== undefined) annotated.details = result.details;
|
|
1143
|
+
if (result.terminate === true) annotated.terminate = true;
|
|
1144
|
+
return annotated;
|
|
1145
|
+
}
|
|
1146
|
+
return { kind: "error", message: `${result.message}${suffix}` };
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function annotationMessages(effects: ReadonlyArray<MiddlewareEffect>): string[] {
|
|
1150
|
+
const messages: string[] = [];
|
|
1151
|
+
for (const effect of effects) {
|
|
1152
|
+
if (effect.kind !== "annotate_tool_result") continue;
|
|
1153
|
+
const severity = effect.severity ?? "info";
|
|
1154
|
+
messages.push(`[middleware:${severity}] ${effect.message}`);
|
|
1155
|
+
}
|
|
1156
|
+
return messages;
|
|
1157
|
+
}
|