@guilz-dev/belay 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -20
- package/dist/adapters/claude/runtime-entry.js +54 -25
- package/dist/adapters/shared/gate-runtime.d.ts +7 -0
- package/dist/adapters/shared/gate-runtime.js +443 -225
- package/dist/bundle/claude-runtime.mjs +13027 -7271
- package/dist/bundle/codex-runtime.mjs +12700 -6977
- package/dist/bundle/cursor-runtime.mjs +12695 -6977
- package/dist/cli.js +37 -1
- package/dist/commands/approve.js +34 -10
- package/dist/commands/doctor.js +58 -0
- package/dist/commands/dogfood.js +1 -1
- package/dist/commands/explain.js +2 -2
- package/dist/commands/harvest.d.ts +0 -1
- package/dist/commands/harvest.js +1 -4
- package/dist/commands/metrics.js +6 -0
- package/dist/commands/quality.js +1 -3
- package/dist/commands/recovery-checkpoints.d.ts +164 -0
- package/dist/commands/recovery-checkpoints.js +347 -0
- package/dist/commands/revoke.js +19 -7
- package/dist/commands/status.js +7 -0
- package/dist/config-io.js +49 -18
- package/dist/conformance/guarantee-table.js +137 -9
- package/dist/conformance/types.d.ts +2 -0
- package/dist/core/approval-replay.d.ts +1 -1
- package/dist/core/approval-replay.js +4 -1
- package/dist/core/approval-service.d.ts +6 -1
- package/dist/core/approval-service.js +86 -27
- package/dist/core/approval.d.ts +2 -0
- package/dist/core/approval.js +6 -3
- package/dist/core/audit-metrics.d.ts +1 -0
- package/dist/core/audit-metrics.js +12 -4
- package/dist/core/audit-types.d.ts +16 -0
- package/dist/core/capability/approval-state-mutation.d.ts +19 -0
- package/dist/core/capability/approval-state-mutation.js +76 -3
- package/dist/core/capability/approval-v3.d.ts +18 -1
- package/dist/core/capability/approval-v3.js +63 -23
- package/dist/core/capability/attestation.d.ts +3 -0
- package/dist/core/capability/attestation.js +7 -0
- package/dist/core/capability/boundary-driver-container.d.ts +12 -0
- package/dist/core/capability/boundary-driver-container.js +101 -34
- package/dist/core/capability/boundary-driver.js +1 -0
- package/dist/core/capability/boundary-grant-materialize.d.ts +1 -0
- package/dist/core/capability/boundary-grant-materialize.js +15 -0
- package/dist/core/capability/boundary-run.d.ts +25 -0
- package/dist/core/capability/boundary-run.js +37 -9
- package/dist/core/capability/boundary-session.d.ts +4 -0
- package/dist/core/capability/boundary-session.js +9 -0
- package/dist/core/capability/boundary-workspace-mount.d.ts +5 -0
- package/dist/core/capability/boundary-workspace-mount.js +68 -0
- package/dist/core/capability/gate-policy-shadow.js +23 -1
- package/dist/core/capability/grant-consumption.d.ts +4 -0
- package/dist/core/capability/grant-consumption.js +11 -0
- package/dist/core/capability/grant-lease.d.ts +36 -1
- package/dist/core/capability/grant-lease.js +213 -16
- package/dist/core/capability/grant-match.js +3 -0
- package/dist/core/capability/index.d.ts +2 -2
- package/dist/core/capability/index.js +1 -1
- package/dist/core/capability/policy-engine.d.ts +38 -1
- package/dist/core/capability/policy-engine.js +388 -15
- package/dist/core/capability/request.d.ts +12 -0
- package/dist/core/capability/resolver.d.ts +0 -1
- package/dist/core/capability/resolver.js +0 -1
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.js +62 -2
- package/dist/core/effect-ir/audit.d.ts +9 -0
- package/dist/core/effect-ir/audit.js +87 -0
- package/dist/core/effect-ir/build.d.ts +31 -0
- package/dist/core/effect-ir/build.js +281 -0
- package/dist/core/effect-ir/index.d.ts +13 -0
- package/dist/core/effect-ir/index.js +9 -0
- package/dist/core/effect-ir/normalize.d.ts +4 -0
- package/dist/core/effect-ir/normalize.js +110 -0
- package/dist/core/effect-ir/package-exec-eval.d.ts +8 -0
- package/dist/core/effect-ir/package-exec-eval.js +152 -0
- package/dist/core/effect-ir/package-exec.d.ts +29 -0
- package/dist/core/effect-ir/package-exec.js +292 -0
- package/dist/core/effect-ir/policy.d.ts +14 -0
- package/dist/core/effect-ir/policy.js +164 -0
- package/dist/core/effect-ir/shell-build.d.ts +43 -0
- package/dist/core/effect-ir/shell-build.js +77 -0
- package/dist/core/effect-ir/shell-lower.d.ts +14 -0
- package/dist/core/effect-ir/shell-lower.js +1509 -0
- package/dist/core/effect-ir/types.d.ts +62 -0
- package/dist/core/effect-ir/types.js +1 -0
- package/dist/core/egress/allowlist.d.ts +1 -0
- package/dist/core/egress/allowlist.js +8 -0
- package/dist/core/egress-approval.js +45 -51
- package/dist/core/gate-contract.d.ts +2 -0
- package/dist/core/gate-contract.js +2 -0
- package/dist/core/gate-engine.js +82 -25
- package/dist/core/git-resource-identity.d.ts +26 -0
- package/dist/core/git-resource-identity.js +284 -0
- package/dist/core/harvest.d.ts +3 -7
- package/dist/core/harvest.js +4 -27
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.js +3 -1
- package/dist/core/judge-cloud-consent.js +29 -15
- package/dist/core/network-endpoint.d.ts +10 -0
- package/dist/core/network-endpoint.js +51 -0
- package/dist/core/path-utils.js +42 -23
- package/dist/core/process-runner.d.ts +10 -0
- package/dist/core/process-runner.js +124 -0
- package/dist/core/recover-advice.js +2 -1
- package/dist/core/recovery/artifact-store.d.ts +29 -0
- package/dist/core/recovery/artifact-store.js +336 -0
- package/dist/core/recovery/checkpoint.d.ts +25 -0
- package/dist/core/recovery/checkpoint.js +260 -0
- package/dist/core/recovery/index.d.ts +3 -1
- package/dist/core/recovery/index.js +2 -0
- package/dist/core/recovery/operator-guidance.d.ts +7 -0
- package/dist/core/recovery/operator-guidance.js +39 -0
- package/dist/core/recovery/reconcile.d.ts +3 -0
- package/dist/core/recovery/reconcile.js +48 -0
- package/dist/core/recovery/resource-identity.d.ts +4 -0
- package/dist/core/recovery/resource-identity.js +34 -0
- package/dist/core/recovery/restore.d.ts +13 -0
- package/dist/core/recovery/restore.js +114 -0
- package/dist/core/recovery/snapshot-node.d.ts +27 -0
- package/dist/core/recovery/snapshot-node.js +261 -0
- package/dist/core/recovery/types.d.ts +85 -1
- package/dist/core/shell-substitution.d.ts +1 -0
- package/dist/core/shell-substitution.js +61 -0
- package/dist/core/standing-allow.d.ts +1 -2
- package/dist/core/standing-allow.js +3 -20
- package/dist/core/transactional/apply-observed-changes.d.ts +13 -0
- package/dist/core/transactional/apply-observed-changes.js +275 -0
- package/dist/core/transactional/backend-selector.d.ts +8 -0
- package/dist/core/transactional/backend-selector.js +118 -0
- package/dist/core/transactional/backend.d.ts +55 -0
- package/dist/core/transactional/backend.js +1 -0
- package/dist/core/transactional/file-checkpoint-backend.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-backend.js +278 -0
- package/dist/core/transactional/file-checkpoint-git.d.ts +11 -0
- package/dist/core/transactional/file-checkpoint-git.js +217 -0
- package/dist/core/transactional/file-checkpoint-staging.d.ts +13 -0
- package/dist/core/transactional/file-checkpoint-staging.js +54 -0
- package/dist/core/transactional/file-clone.d.ts +15 -0
- package/dist/core/transactional/file-clone.js +139 -0
- package/dist/core/transactional/file-tree-path.d.ts +7 -0
- package/dist/core/transactional/file-tree-path.js +50 -0
- package/dist/core/transactional/file-tree.d.ts +41 -0
- package/dist/core/transactional/file-tree.js +186 -0
- package/dist/core/transactional/git-worktree-backend.d.ts +2 -0
- package/dist/core/transactional/git-worktree-backend.js +50 -0
- package/dist/core/transactional/git-worktree.d.ts +9 -11
- package/dist/core/transactional/git-worktree.js +24 -119
- package/dist/core/transactional/index.d.ts +11 -0
- package/dist/core/transactional/index.js +10 -0
- package/dist/core/transactional/reasons.js +1 -0
- package/dist/core/transactional/runner.js +157 -25
- package/dist/core/transactional/snapshot-node.d.ts +26 -0
- package/dist/core/transactional/snapshot-node.js +87 -0
- package/dist/core/transactional/types.d.ts +17 -1
- package/dist/core/types.d.ts +13 -1
- package/dist/core/verdict/adapter.js +23 -15
- package/dist/core/verdict/containment.js +4 -0
- package/dist/core/verdict/egress-classify.d.ts +12 -0
- package/dist/core/verdict/egress-classify.js +746 -0
- package/dist/core/verdict/git-classifier.d.ts +12 -0
- package/dist/core/verdict/git-classifier.js +320 -0
- package/dist/core/verdict/launcher-resolve.js +79 -37
- package/dist/core/verdict/parser.d.ts +2 -0
- package/dist/core/verdict/parser.js +95 -1
- package/dist/core/verdict/types.d.ts +6 -2
- package/dist/core/verdict/verdict.js +103 -935
- package/dist/corpus/must-allow-commands.d.ts +2 -1
- package/dist/corpus/must-allow-commands.js +2 -1
- package/dist/corpus/runtime-match.d.ts +2 -1
- package/dist/corpus/runtime-match.js +2 -1
- package/dist/corpus/types.d.ts +2 -2
- package/dist/templates.js +7 -2
- package/dist/types.d.ts +11 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/skills/belay/SKILL.md +8 -3
- package/skills/belay/belay-approve.md +10 -7
- package/dist/core/verdict/overrides.d.ts +0 -7
- package/dist/core/verdict/overrides.js +0 -37
- package/dist/core/verdict/shell-policy.d.ts +0 -25
- package/dist/core/verdict/shell-policy.js +0 -40
- package/dist/corpus/standing-allow-catalog.generated.d.ts +0 -13
- package/dist/corpus/standing-allow-catalog.generated.js +0 -99
package/README.md
CHANGED
|
@@ -86,7 +86,8 @@ npx @guilz-dev/belay status
|
|
|
86
86
|
|
|
87
87
|
Fresh installs default to **fail-closed** shell policy: unknown or unparseable
|
|
88
88
|
shell commands are denied until approved. Use `belay explain` to inspect a
|
|
89
|
-
verdict
|
|
89
|
+
verdict. Correct inaccurate EffectPlan semantics or resource scope; otherwise
|
|
90
|
+
approve the exact request for one-shot, resource-scoped authorization.
|
|
90
91
|
|
|
91
92
|
## How it works
|
|
92
93
|
|
|
@@ -109,6 +110,16 @@ When an action is denied, approve the **next matching action once** by sending:
|
|
|
109
110
|
/belay-approve <approval-id>
|
|
110
111
|
```
|
|
111
112
|
|
|
113
|
+
With the default `approval.flow: one_step`, an editor approval immediately replays the exact denied
|
|
114
|
+
shell action; no follow-up prompt is required. Tool and subagent approvals still require retrying
|
|
115
|
+
the original action unchanged. The one-shot grant is claimed before shell replay, so a failed or
|
|
116
|
+
timed-out replay requires a fresh approval. Replay runs through the configured `BoundaryDriver`.
|
|
117
|
+
After replay succeeds, the approval prompt continues the current host turn so the agent can report
|
|
118
|
+
the result and resume its workflow without another user message.
|
|
119
|
+
You may put a follow-up instruction on the lines after `/belay-approve <id>`; Belay replays the
|
|
120
|
+
approved shell action first, then lets the remaining prompt continue. CLI replay remains explicit:
|
|
121
|
+
`belay approve <approval-id> --replay`.
|
|
122
|
+
|
|
112
123
|
Approvals are one-shot and expire after 15 minutes by default. Every decision is
|
|
113
124
|
written to `.cursor/belay/audit.ndjson`, `.claude/belay/audit.ndjson`, or
|
|
114
125
|
`.codex/belay/audit.ndjson` (depending on the adapter).
|
|
@@ -128,12 +139,12 @@ Belay is a layered hook gate, not a static denylist. Higher layers are opt-in.
|
|
|
128
139
|
| **L3** Prediction | Policy rules + command heuristics | default |
|
|
129
140
|
| **L4** Approval | Human one-shot / scoped approvals | default |
|
|
130
141
|
|
|
131
|
-
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
- Normalized shell authorization uses only canonical `EffectPlan` requirements.
|
|
143
|
+
Command lists, legacy overrides, corpus labels, and shell standing allows are inert.
|
|
144
|
+
- Payload-free reads are `allow`; reversible repository-local writes (including implicit
|
|
145
|
+
download output) are `allow_flagged`. Outside-repository writes, external mutation,
|
|
146
|
+
explicit payload/file/secret sends, high-stakes effects, and partial/indeterminate plans
|
|
147
|
+
require approval. See [ADR-004](./docs/adr/ADR-004-effectplan-shell-authority.md).
|
|
137
148
|
- Adversarial resistance requires the full L1 stack:
|
|
138
149
|
`belay init --preset l1-full-recommended`, verified with `belay sandbox status`.
|
|
139
150
|
|
|
@@ -178,18 +189,18 @@ npx @guilz-dev/belay dogfood # mode: audit, unknownLocalEffect: deny
|
|
|
178
189
|
# ...run normal agent work...
|
|
179
190
|
npx @guilz-dev/belay metrics # review what would have been blocked
|
|
180
191
|
npx @guilz-dev/belay status # check readiness
|
|
181
|
-
#
|
|
192
|
+
# inspect EffectPlan semantics and resource scope with `belay explain`, then:
|
|
182
193
|
npx @guilz-dev/belay dogfood --enforce
|
|
183
194
|
```
|
|
184
195
|
|
|
185
196
|
## Configuration
|
|
186
197
|
|
|
187
|
-
`belay.config.json` uses `version:
|
|
198
|
+
`belay.config.json` uses `version: 4`. v1/v2/v3 configs migrate automatically on
|
|
188
199
|
load.
|
|
189
200
|
|
|
190
201
|
```json
|
|
191
202
|
{
|
|
192
|
-
"version":
|
|
203
|
+
"version": 4,
|
|
193
204
|
"mode": "enforce",
|
|
194
205
|
"gates": {
|
|
195
206
|
"shell": true,
|
|
@@ -204,10 +215,6 @@ load.
|
|
|
204
215
|
"policy": {
|
|
205
216
|
"unknownLocalEffect": "allow_flagged"
|
|
206
217
|
},
|
|
207
|
-
"overrides": {
|
|
208
|
-
"allow": ["pnpm release:staging"],
|
|
209
|
-
"external": ["./scripts/release.sh"]
|
|
210
|
-
},
|
|
211
218
|
"redaction": {
|
|
212
219
|
"maskApprovalIds": true,
|
|
213
220
|
"maskBearerTokens": true,
|
|
@@ -228,17 +235,25 @@ load.
|
|
|
228
235
|
|
|
229
236
|
Notable settings:
|
|
230
237
|
|
|
231
|
-
- **`policy.unknownLocalEffect: "allow_flagged"`** (fresh default) —
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
- **`policy.unknownLocalEffect: "allow_flagged"`** (fresh default) — compatibility fallback
|
|
239
|
+
for legacy, non-EffectPlan classification paths. It cannot loosen a normalized shell
|
|
240
|
+
EffectPlan: partial/indeterminate plans and outside-repository writes still ask. Use
|
|
241
|
+
`"deny"` (via `belay dogfood`) for a stricter fallback.
|
|
234
242
|
- **`classifier.strictChains: true`** (default) — scans every `&&`, `|`, and `;`
|
|
235
|
-
segment and keeps the strictest
|
|
236
|
-
|
|
243
|
+
segment into the EffectPlan and keeps the strictest policy projection. Legacy
|
|
244
|
+
`overrides.allow` / `overrides.external` lists are accepted only for config
|
|
245
|
+
compatibility; shell authorization ignores them and `belay doctor` warns when
|
|
246
|
+
either list is non-empty.
|
|
237
247
|
- **`controlPlane.enabled: true`** — stores approval state under
|
|
238
248
|
`~/.config/belay/` (or `XDG_CONFIG_HOME/belay`), shared across repos for the
|
|
239
249
|
current OS user. `upgrade` migrates repo-local approvals in; disabling merges
|
|
240
250
|
them back. File-mutation tools and shell redirects cannot write control-plane
|
|
241
251
|
paths while it is enabled.
|
|
252
|
+
- **`policy.transactional.checkpoint.enabled: true`** — after enabling the
|
|
253
|
+
transactional git-worktree runner, persist repo-local pre-images and expose them through
|
|
254
|
+
`belay recover list`. Restore is conflict-checked and always requires a signed
|
|
255
|
+
out-of-band, exact one-shot approval. Network, remote Git, databases, processes, and
|
|
256
|
+
repo-external effects are outside this guarantee.
|
|
242
257
|
- **Cloud judge** — configure with `belay config` (interactive) or `belay config set judge.providerId <id>`.
|
|
243
258
|
Providers: `ollama`, `codex`, `claude`, `cursor`. **Provider** is the vendor/service
|
|
244
259
|
(`judge.providerId`); **driver** is the API compatibility layer (`judge.provider`:
|
|
@@ -281,7 +296,11 @@ belay doctor [--fix] # check (and repair) floor health
|
|
|
281
296
|
belay status # show install scope / skill-only state
|
|
282
297
|
belay metrics # would-block / verdict summary
|
|
283
298
|
belay report # audit log report
|
|
284
|
-
belay recover [--command "rm important.ts"]
|
|
299
|
+
belay recover [advice] [--command "rm important.ts"] # advisory candidates only
|
|
300
|
+
belay recover status # checkpoint backend and state counts
|
|
301
|
+
belay recover list # proven repo-local recovery points
|
|
302
|
+
belay recover show <checkpoint-id>
|
|
303
|
+
belay recover apply <checkpoint-id> # signed OOB exact one-shot approval required
|
|
285
304
|
belay explain -- <shell-command> # inspect a verdict
|
|
286
305
|
belay explain --kind subagent -- "deploy to production"
|
|
287
306
|
belay explain --kind tool --tool Write -- .env
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
|
+
import { effectPlanAuditFields } from '../../core/effect-ir/audit.js';
|
|
3
|
+
import { buildCapabilityEffectPlan } from '../../core/effect-ir/build.js';
|
|
4
|
+
import { hashValue, toolFingerprint } from '../../core/fingerprint.js';
|
|
2
5
|
import { unnormalizedGateVerdict } from '../../core/gate-contract.js';
|
|
3
6
|
import { claudeLayout } from '../layouts/claude.js';
|
|
4
7
|
import { appendObservedAudit, createDefaultGateRuntimeDeps, evaluateGatedAction, gateVerdictToClaudePreToolUseResponse, gateVerdictToClaudeUserPromptResponse, processApprovalPrompt, resolveGateConfig, } from '../shared/gate-runtime.js';
|
|
@@ -22,6 +25,23 @@ async function readStdinJson() {
|
|
|
22
25
|
function jsonResponse(value) {
|
|
23
26
|
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
24
27
|
}
|
|
28
|
+
function claudeFallbackToolVerdict(params) {
|
|
29
|
+
return {
|
|
30
|
+
...unnormalizedGateVerdict({
|
|
31
|
+
reason: params.reason,
|
|
32
|
+
mode: params.mode,
|
|
33
|
+
user_message: params.userMessage,
|
|
34
|
+
agent_message: params.agentMessage,
|
|
35
|
+
}),
|
|
36
|
+
effectPlan: buildCapabilityEffectPlan({
|
|
37
|
+
actionKind: 'tool',
|
|
38
|
+
summary: params.toolName,
|
|
39
|
+
inputFingerprint: hashValue(`claude-fallback:${params.reason}:${toolFingerprint(params.toolName, params.payload, params.repoRoot)}`),
|
|
40
|
+
requests: [],
|
|
41
|
+
effectFree: false,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
25
45
|
async function loadRuntimeContext(cwd) {
|
|
26
46
|
const repoRoot = findRepoRoot(cwd, claudeLayout);
|
|
27
47
|
const configPath = claudeLayout.configPath(repoRoot);
|
|
@@ -120,13 +140,10 @@ export async function runBeforeSubmitPromptHook() {
|
|
|
120
140
|
jsonResponse(gateVerdictToClaudeUserPromptResponse(result));
|
|
121
141
|
}
|
|
122
142
|
catch {
|
|
123
|
-
jsonResponse({
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
user_message: 'belay failed while processing approval state. Run belay doctor, then retry.',
|
|
128
|
-
},
|
|
129
|
-
});
|
|
143
|
+
jsonResponse(gateVerdictToClaudeUserPromptResponse({
|
|
144
|
+
continue: false,
|
|
145
|
+
user_message: 'belay failed while processing approval state. Run belay doctor, then retry.',
|
|
146
|
+
}));
|
|
130
147
|
}
|
|
131
148
|
}
|
|
132
149
|
export async function runShellGateHook() {
|
|
@@ -167,6 +184,15 @@ export async function runToolGateHook(_eventName) {
|
|
|
167
184
|
const toolName = String(payload.tool_name ?? '');
|
|
168
185
|
const mappedKind = mapClaudeToolName(toolName);
|
|
169
186
|
if (mappedKind === 'mcp') {
|
|
187
|
+
const verdict = claudeFallbackToolVerdict({
|
|
188
|
+
toolName,
|
|
189
|
+
payload,
|
|
190
|
+
repoRoot: ctx.repoRoot,
|
|
191
|
+
reason: 'unsupported_mcp_tool',
|
|
192
|
+
mode: ctx.config.mode,
|
|
193
|
+
userMessage: 'belay blocked this MCP tool because Claude MCP payloads are not normalized safely yet.',
|
|
194
|
+
agentMessage: 'Belay denied this MCP tool because its payload shape is unsupported.',
|
|
195
|
+
});
|
|
170
196
|
await deps.appendAudit(ctx, {
|
|
171
197
|
event: 'preToolUse',
|
|
172
198
|
kind: 'tool',
|
|
@@ -176,17 +202,21 @@ export async function runToolGateHook(_eventName) {
|
|
|
176
202
|
wouldBlock: true,
|
|
177
203
|
permission: 'deny',
|
|
178
204
|
summary: toolName,
|
|
179
|
-
|
|
180
|
-
const verdict = unnormalizedGateVerdict({
|
|
181
|
-
reason: 'unsupported_mcp_tool',
|
|
182
|
-
mode: ctx.config.mode,
|
|
183
|
-
user_message: 'belay blocked this MCP tool because Claude MCP payloads are not normalized safely yet.',
|
|
184
|
-
agent_message: 'Belay denied this MCP tool because its payload shape is unsupported.',
|
|
205
|
+
...effectPlanAuditFields(verdict.effectPlan),
|
|
185
206
|
});
|
|
186
207
|
jsonResponse(gateVerdictToClaudePreToolUseResponse(verdict));
|
|
187
208
|
return;
|
|
188
209
|
}
|
|
189
210
|
if (!mappedKind) {
|
|
211
|
+
const verdict = claudeFallbackToolVerdict({
|
|
212
|
+
toolName,
|
|
213
|
+
payload,
|
|
214
|
+
repoRoot: ctx.repoRoot,
|
|
215
|
+
reason: 'unmapped_tool',
|
|
216
|
+
mode: ctx.config.mode,
|
|
217
|
+
userMessage: 'belay does not recognize this tool action. Run belay doctor, then retry.',
|
|
218
|
+
agentMessage: 'Belay denied this action because the tool could not be normalized.',
|
|
219
|
+
});
|
|
190
220
|
await deps.appendAudit(ctx, {
|
|
191
221
|
event: 'preToolUse',
|
|
192
222
|
kind: 'tool',
|
|
@@ -196,18 +226,22 @@ export async function runToolGateHook(_eventName) {
|
|
|
196
226
|
wouldBlock: true,
|
|
197
227
|
permission: 'deny',
|
|
198
228
|
summary: toolName,
|
|
199
|
-
|
|
200
|
-
const verdict = unnormalizedGateVerdict({
|
|
201
|
-
reason: 'unmapped_tool',
|
|
202
|
-
mode: ctx.config.mode,
|
|
203
|
-
user_message: 'belay does not recognize this tool action. Run belay doctor, then retry.',
|
|
204
|
-
agent_message: 'Belay denied this action because the tool could not be normalized.',
|
|
229
|
+
...effectPlanAuditFields(verdict.effectPlan),
|
|
205
230
|
});
|
|
206
231
|
jsonResponse(gateVerdictToClaudePreToolUseResponse(verdict));
|
|
207
232
|
return;
|
|
208
233
|
}
|
|
209
234
|
const normalizedPayload = normalizeClaudeToolPayload(toolName, payload);
|
|
210
235
|
if (!normalizedPayload) {
|
|
236
|
+
const verdict = claudeFallbackToolVerdict({
|
|
237
|
+
toolName,
|
|
238
|
+
payload,
|
|
239
|
+
repoRoot: ctx.repoRoot,
|
|
240
|
+
reason: 'normalization_failed',
|
|
241
|
+
mode: ctx.config.mode,
|
|
242
|
+
userMessage: 'belay could not normalize this Claude tool payload. Run belay doctor, then retry.',
|
|
243
|
+
agentMessage: 'Belay denied this action because the Claude tool payload could not be normalized.',
|
|
244
|
+
});
|
|
211
245
|
await deps.appendAudit(ctx, {
|
|
212
246
|
event: 'preToolUse',
|
|
213
247
|
kind: 'tool',
|
|
@@ -217,12 +251,7 @@ export async function runToolGateHook(_eventName) {
|
|
|
217
251
|
wouldBlock: true,
|
|
218
252
|
permission: 'deny',
|
|
219
253
|
summary: toolName,
|
|
220
|
-
|
|
221
|
-
const verdict = unnormalizedGateVerdict({
|
|
222
|
-
reason: 'normalization_failed',
|
|
223
|
-
mode: ctx.config.mode,
|
|
224
|
-
user_message: 'belay could not normalize this Claude tool payload. Run belay doctor, then retry.',
|
|
225
|
-
agent_message: 'Belay denied this action because the Claude tool payload could not be normalized.',
|
|
254
|
+
...effectPlanAuditFields(verdict.effectPlan),
|
|
226
255
|
});
|
|
227
256
|
jsonResponse(gateVerdictToClaudePreToolUseResponse(verdict));
|
|
228
257
|
return;
|
|
@@ -23,6 +23,13 @@ export interface GateRuntimeDeps {
|
|
|
23
23
|
state: ApprovalStateFile;
|
|
24
24
|
}>;
|
|
25
25
|
writeApprovals: (filePath: string, state: ApprovalStateFile) => Promise<void>;
|
|
26
|
+
replayApprovedShell: (ctx: GateRuntimeContext, command: string, cwd: string, timeoutMs: number) => Promise<{
|
|
27
|
+
exitCode: number | null;
|
|
28
|
+
timedOut: boolean;
|
|
29
|
+
signal: string | null;
|
|
30
|
+
stdout?: string;
|
|
31
|
+
stderr?: string;
|
|
32
|
+
}>;
|
|
26
33
|
}
|
|
27
34
|
export declare function createDefaultGateRuntimeDeps(): GateRuntimeDeps;
|
|
28
35
|
export declare function resolveGateConfig(ctx: Pick<GateRuntimeContext, 'layout' | 'repoRoot' | 'configPath'>, deps: GateRuntimeDeps): Promise<BelayConfigV3>;
|