@humwick/runner 0.0.0-stage → 0.130.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/LICENSE +167 -0
- package/agent-bin/agent-relay-browser +10 -0
- package/agent-bin/humwick +10 -0
- package/hooks/fff-search-redirect/fff-search-redirect.cjs +161 -0
- package/hooks/fff-search-redirect/hooks.json +13 -0
- package/hooks/rg-r-guard/hooks.json +18 -0
- package/hooks/rg-r-guard/rg-r-guard.cjs +144 -0
- package/hooks/web-tools-redirect/hooks.json +13 -0
- package/hooks/web-tools-redirect/web-tools-redirect.cjs +84 -0
- package/node_modules/@humwick/offload/LICENSE +167 -0
- package/node_modules/@humwick/offload/README.md +57 -0
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +18 -0
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/async-runner.js +158 -0
- package/node_modules/@humwick/offload/dist/async-runner.js.map +1 -0
- package/node_modules/@humwick/offload/dist/envelope.d.ts +25 -0
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/envelope.js +80 -0
- package/node_modules/@humwick/offload/dist/envelope.js.map +1 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +10 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +59 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +1 -0
- package/node_modules/@humwick/offload/dist/index.d.ts +5 -0
- package/node_modules/@humwick/offload/dist/index.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/index.js +5 -0
- package/node_modules/@humwick/offload/dist/index.js.map +1 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +62 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +295 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +1 -0
- package/node_modules/@humwick/offload/dist/types.d.ts +111 -0
- package/node_modules/@humwick/offload/dist/types.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/types.js +176 -0
- package/node_modules/@humwick/offload/dist/types.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +23 -0
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-pool.js +435 -0
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +2 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.js +27 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker.d.ts +5 -0
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker.js +1 -0
- package/node_modules/@humwick/offload/dist/worker.js.map +1 -0
- package/node_modules/@humwick/offload/package.json +46 -0
- package/node_modules/@humwick/offload/src/async-runner.ts +170 -0
- package/node_modules/@humwick/offload/src/envelope.ts +92 -0
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +49 -0
- package/node_modules/@humwick/offload/src/index.ts +4 -0
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +334 -0
- package/node_modules/@humwick/offload/src/types.ts +235 -0
- package/node_modules/@humwick/offload/src/worker-pool.ts +463 -0
- package/node_modules/@humwick/offload/src/worker-runtime.ts +32 -0
- package/node_modules/@humwick/offload/src/worker.ts +5 -0
- package/package.json +47 -4
- package/plugins/browser/skills/playwright-cli/SKILL.md +53 -0
- package/plugins/claude/.claude-plugin/plugin.json +8 -0
- package/plugins/claude/claude-statusline-probe.cjs +144 -0
- package/plugins/claude/hooks/gate-run-guard.provisioned.mjs +914 -0
- package/plugins/claude/hooks/gate-run-guard.sh +9 -0
- package/plugins/claude/hooks/gate-run-guard.ts +237 -0
- package/plugins/claude/hooks/hooks.json +158 -0
- package/plugins/claude/hooks/kill-guard.provisioned.mjs +7027 -0
- package/plugins/claude/hooks/kill-guard.sh +341 -0
- package/plugins/claude/hooks/kill-guard.ts +337 -0
- package/plugins/claude/hooks/permission-request.sh +22 -0
- package/plugins/claude/hooks/post-compact.sh +10 -0
- package/plugins/claude/hooks/post-tool-use.sh +8 -0
- package/plugins/claude/hooks/pre-compact.sh +12 -0
- package/plugins/claude/hooks/read-only-bash-guard.sh +587 -0
- package/plugins/claude/hooks/relay-status.sh +373 -0
- package/plugins/claude/hooks/session-end.sh +26 -0
- package/plugins/claude/hooks/session-start.sh +31 -0
- package/plugins/claude/hooks/shell-trust.ts +28 -0
- package/plugins/claude/hooks/stop-failure.sh +47 -0
- package/plugins/claude/hooks/stop.sh +48 -0
- package/plugins/claude/hooks/subagent-guard.cjs +26 -0
- package/plugins/claude/hooks/subagent-skill-policy.cjs +160 -0
- package/plugins/claude/hooks/subagent-start.sh +13 -0
- package/plugins/claude/hooks/subagent-stop.sh +19 -0
- package/plugins/claude/hooks/user-prompt-submit.sh +18 -0
- package/plugins/claude/hooks/wait-loop-guard.ts +120 -0
- package/plugins/claude/monitors/monitors.json +7 -0
- package/plugins/claude/monitors/relay-monitor.provisioned.mjs +786 -0
- package/plugins/claude/monitors/relay-monitor.ts +58 -0
- package/plugins/claude/skills/disconnect/SKILL.md +16 -0
- package/plugins/claude/skills/label/SKILL.md +23 -0
- package/plugins/claude/skills/message/SKILL.md +40 -0
- package/plugins/claude/skills/pair/SKILL.md +30 -0
- package/plugins/claude/skills/react/SKILL.md +18 -0
- package/plugins/claude/skills/read-message/SKILL.md +24 -0
- package/plugins/claude/skills/reply/SKILL.md +44 -0
- package/plugins/claude/skills/send-claimable/SKILL.md +39 -0
- package/plugins/claude/skills/slice-first/SKILL.md +32 -0
- package/plugins/claude/skills/slice-first/references/plan-recipe.md +46 -0
- package/plugins/claude/skills/status/SKILL.md +16 -0
- package/plugins/claude/skills/tags/SKILL.md +25 -0
- package/plugins/claude/skills/task/SKILL.md +28 -0
- package/plugins/claude-output-style/.claude-plugin/plugin.json +5 -0
- package/plugins/claude-output-style/output-styles/agent-relay.md +5 -0
- package/plugins/codex/skills/disconnect/SKILL.md +15 -0
- package/plugins/codex/skills/guide/SKILL.md +15 -0
- package/plugins/codex/skills/label/SKILL.md +22 -0
- package/plugins/codex/skills/message/SKILL.md +40 -0
- package/plugins/codex/skills/pair/SKILL.md +29 -0
- package/plugins/codex/skills/react/SKILL.md +17 -0
- package/plugins/codex/skills/read-message/SKILL.md +23 -0
- package/plugins/codex/skills/reply/SKILL.md +44 -0
- package/plugins/codex/skills/send-claimable/SKILL.md +39 -0
- package/plugins/codex/skills/slice-first/SKILL.md +32 -0
- package/plugins/codex/skills/slice-first/references/plan-recipe.md +46 -0
- package/plugins/codex/skills/status/SKILL.md +15 -0
- package/plugins/codex/skills/tags/SKILL.md +24 -0
- package/plugins/codex/skills/task/SKILL.md +28 -0
- package/src/adapter.ts +1061 -0
- package/src/adapters/__fixtures__/codex-app-server.ts +47 -0
- package/src/adapters/claude-api.ts +243 -0
- package/src/adapters/claude-context-command.ts +16 -0
- package/src/adapters/claude-credentials.ts +22 -0
- package/src/adapters/claude-delivery-lanes.ts +28 -0
- package/src/adapters/claude-delivery.ts +166 -0
- package/src/adapters/claude-effort.ts +94 -0
- package/src/adapters/claude-exit.ts +290 -0
- package/src/adapters/claude-headless-mirror.ts +88 -0
- package/src/adapters/claude-headless-refusal.ts +51 -0
- package/src/adapters/claude-headless.ts +617 -0
- package/src/adapters/claude-monitor-delivery-plan.ts +317 -0
- package/src/adapters/claude-permission.ts +243 -0
- package/src/adapters/claude-prompt-gates.ts +269 -0
- package/src/adapters/claude-prompt-hook.ts +145 -0
- package/src/adapters/claude-session-capture.ts +293 -0
- package/src/adapters/claude-session-probe.ts +414 -0
- package/src/adapters/claude-shutdown.ts +190 -0
- package/src/adapters/claude-status-detectors.ts +426 -0
- package/src/adapters/claude-subagents.ts +93 -0
- package/src/adapters/claude-submit-confirmation.ts +287 -0
- package/src/adapters/claude-tmux.ts +548 -0
- package/src/adapters/claude-token-usage-capture.ts +253 -0
- package/src/adapters/claude-tool-policy.ts +165 -0
- package/src/adapters/claude-transcript-tail.ts +193 -0
- package/src/adapters/claude-transcript.ts +812 -0
- package/src/adapters/claude.ts +1097 -0
- package/src/adapters/codex-agent-message-capture.ts +189 -0
- package/src/adapters/codex-app-server.ts +70 -0
- package/src/adapters/codex-client.ts +362 -0
- package/src/adapters/codex-effective-config.ts +87 -0
- package/src/adapters/codex-launch-config.ts +153 -0
- package/src/adapters/codex-rollout-context.ts +272 -0
- package/src/adapters/codex-skill-discovery.ts +18 -0
- package/src/adapters/codex-token-usage-capture.ts +163 -0
- package/src/adapters/codex-tool-output.ts +53 -0
- package/src/adapters/codex-turn-outcome.ts +27 -0
- package/src/adapters/codex-version.ts +64 -0
- package/src/adapters/codex.ts +1442 -0
- package/src/adapters/incremental-file.ts +110 -0
- package/src/adapters/opencode-api.ts +71 -0
- package/src/adapters/opencode-context.ts +277 -0
- package/src/adapters/opencode-model-registry.ts +126 -0
- package/src/adapters/opencode-provider.ts +169 -0
- package/src/adapters/opencode-server-retry.ts +50 -0
- package/src/adapters/opencode-soft-steer.ts +302 -0
- package/src/adapters/opencode-spawn.ts +145 -0
- package/src/adapters/opencode-subscribe.ts +62 -0
- package/src/adapters/opencode-token-usage-capture.ts +187 -0
- package/src/adapters/opencode-tool-events.ts +302 -0
- package/src/adapters/opencode-tools-config.ts +17 -0
- package/src/adapters/opencode.ts +2264 -0
- package/src/adapters/project-shell.ts +80 -0
- package/src/adapters/provider-launch-config.ts +21 -0
- package/src/adapters/provider-quota-harvest.ts +127 -0
- package/src/adapters/service-access.ts +153 -0
- package/src/adapters/tmux-offload.ts +207 -0
- package/src/adapters/tool-summary.ts +214 -0
- package/src/agent-redact.ts +75 -0
- package/src/agent-runner/commands/dispatch.ts +185 -0
- package/src/agent-runner/commands/lifecycle.ts +84 -0
- package/src/agent-runner/commands/prompt.ts +284 -0
- package/src/agent-runner/credentials.ts +208 -0
- package/src/agent-runner/http-liveness.ts +85 -0
- package/src/agent-runner/lifecycle-milestones.ts +207 -0
- package/src/agent-runner/lifecycle-phase.ts +80 -0
- package/src/agent-runner/registration.ts +163 -0
- package/src/agent-runner/runner-context.ts +158 -0
- package/src/agent-runner/session-mirror.ts +278 -0
- package/src/agent-runner/task-claims.ts +116 -0
- package/src/agent-runner/token-usage-coverage.ts +54 -0
- package/src/agent-runner/turn-report.ts +269 -0
- package/src/api-budget-accounting.ts +91 -0
- package/src/area-memory.ts +255 -0
- package/src/artifact-path-materialize.ts +52 -0
- package/src/async-guard.ts +35 -0
- package/src/attachment-cache-sweep.ts +31 -0
- package/src/attachment-cache.ts +195 -0
- package/src/background-script-reconcile.ts +106 -0
- package/src/bounded-log.ts +59 -0
- package/src/bounded-spawn-sync.ts +18 -0
- package/src/busy-reconciler.ts +282 -0
- package/src/claim-tracker.ts +140 -0
- package/src/claude-settings.ts +167 -0
- package/src/claude-workspace-trust.ts +289 -0
- package/src/config.ts +547 -0
- package/src/context-boundary.ts +101 -0
- package/src/context-command.ts +151 -0
- package/src/context-probe-state.ts +6 -0
- package/src/continuation-archive.ts +179 -0
- package/src/control-server.ts +730 -0
- package/src/delivery-policy.ts +9 -0
- package/src/delivery-result.ts +24 -0
- package/src/effort-command.ts +125 -0
- package/src/empty-turn.ts +816 -0
- package/src/free-port.ts +25 -0
- package/src/fs-offload.ts +26 -0
- package/src/gate-run-config.ts +35 -0
- package/src/gate-run-lease-probe.ts +38 -0
- package/src/host-asset-plan.ts +450 -0
- package/src/host-claude-config-worker.ts +34 -0
- package/src/host-sources.ts +40 -0
- package/src/inbox-carrier.ts +7 -0
- package/src/index.ts +386 -0
- package/src/launch-assembly.ts +955 -0
- package/src/liveness.ts +105 -0
- package/src/logger.ts +108 -0
- package/src/mcp-outbox.ts +204 -0
- package/src/mcp-preflight.ts +86 -0
- package/src/mcp-source-selection.ts +54 -0
- package/src/mcp-tool-deferral.ts +35 -0
- package/src/mcp-vault-env.ts +25 -0
- package/src/message-body-config/config.ts +16 -0
- package/src/message-body-config.ts +1 -0
- package/src/mid-turn-delivery.ts +43 -0
- package/src/native-memory-lever.ts +329 -0
- package/src/native-self-resume.ts +244 -0
- package/src/outbox.ts +453 -0
- package/src/park-post.ts +17 -0
- package/src/pending-permission-store.ts +148 -0
- package/src/process-meta.ts +9 -0
- package/src/profile-home.ts +419 -0
- package/src/profile-projection.ts +411 -0
- package/src/project-hooks.ts +65 -0
- package/src/project-instructions.ts +119 -0
- package/src/provider-double.ts +59 -0
- package/src/provider-hold.ts +228 -0
- package/src/provider-home-marker.ts +3 -0
- package/src/provider-home-tombstone.ts +4 -0
- package/src/provider-input-queue.ts +134 -0
- package/src/provider-integration.ts +93 -0
- package/src/provider-processes.ts +17 -0
- package/src/provider-submit-failure.ts +15 -0
- package/src/providers.ts +35 -0
- package/src/provisioning.ts +703 -0
- package/src/question-decision.ts +18 -0
- package/src/quota.ts +22 -0
- package/src/rate-limit.ts +351 -0
- package/src/refused-prompt-store.ts +36 -0
- package/src/relay-injection-events.ts +161 -0
- package/src/relay-instructions.ts +107 -0
- package/src/relay-mcp-proxy.long-turn-e2e.ts +183 -0
- package/src/relay-mcp-proxy.ts +704 -0
- package/src/relay-mcp.ts +583 -0
- package/src/reply-obligation-cache.ts +211 -0
- package/src/response-capture-report.ts +111 -0
- package/src/response-routing.ts +6 -0
- package/src/runner-core.ts +2955 -0
- package/src/runner-exit.ts +12 -0
- package/src/runner-helpers.ts +582 -0
- package/src/runner-insights.ts +105 -0
- package/src/runner-mcp-proxy.ts +46 -0
- package/src/runner-spawn-config.ts +68 -0
- package/src/runner-stale-scratch-sweep.ts +21 -0
- package/src/runner.ts +9 -0
- package/src/runtime-token-file.ts +45 -0
- package/src/runtime-token-remint.ts +56 -0
- package/src/service-access.ts +88 -0
- package/src/session-destroy.ts +27 -0
- package/src/session-insights.ts +118 -0
- package/src/session-scratch.ts +296 -0
- package/src/spawn-input.ts +147 -0
- package/src/startup-config-fetch.ts +48 -0
- package/src/subreaper.ts +113 -0
- package/src/template-resolver.ts +29 -0
- package/src/test-run-dedup.ts +435 -0
- package/src/transcript-context.ts +99 -0
- package/src/transport-errors.ts +48 -0
- package/src/turn-opener.ts +55 -0
- package/src/turn-reconcile-event.ts +88 -0
- package/src/vault-proxy-hop.ts +99 -0
- package/src/version.ts +44 -0
- package/src/web-tool-call-proxy.ts +128 -0
- package/src/web-tools.ts +59 -0
- package/README.md +0 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
source "${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/hooks/relay-status.sh"
|
|
4
|
+
relay_install_hook_guard permission-request
|
|
5
|
+
|
|
6
|
+
port="${AGENT_RELAY_RUNNER_PORT:-}"
|
|
7
|
+
if [[ -z "$port" ]]; then
|
|
8
|
+
exit 0
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
payload="$(cat)"
|
|
12
|
+
response="$(
|
|
13
|
+
printf '%s' "$payload" | curl -fsS \
|
|
14
|
+
--max-time 900 \
|
|
15
|
+
-H 'Content-Type: application/json' \
|
|
16
|
+
--data-binary @- \
|
|
17
|
+
"http://127.0.0.1:${port}/permissions/request" 2>/dev/null || true
|
|
18
|
+
)"
|
|
19
|
+
|
|
20
|
+
if [[ -n "$response" ]]; then
|
|
21
|
+
printf '%s\n' "$response"
|
|
22
|
+
fi
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
source "${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/hooks/relay-status.sh"
|
|
4
|
+
relay_install_hook_guard post-compact
|
|
5
|
+
|
|
6
|
+
# #3398 — the trigger (manual for our /compact, auto for Claude's own) tells the runner which input settled.
|
|
7
|
+
trigger="$(relay_json_string_field trigger "$(cat || true)")"
|
|
8
|
+
metadata=""
|
|
9
|
+
[ -n "$trigger" ] && metadata="{\"compactTrigger\":\"$(relay_json_escape "$trigger")\"}"
|
|
10
|
+
relay_post_status idle provider-turn "" "" "" "" "" compacted "$metadata"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
source "${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/hooks/relay-status.sh"
|
|
4
|
+
relay_install_hook_guard post-tool-use
|
|
5
|
+
[ -n "${AGENT_RELAY_RUNNER_PORT:-}" ] || exit 0
|
|
6
|
+
# #3646: stdout belongs to this tool result, before Claude can make its next call.
|
|
7
|
+
context="$(relay_post_json "$AGENT_RELAY_RUNNER_PORT" /tool-use 4 || true)"
|
|
8
|
+
relay_emit_additional_context PostToolUse "$context"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
source "${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/hooks/relay-status.sh"
|
|
4
|
+
relay_install_hook_guard pre-compact
|
|
5
|
+
|
|
6
|
+
payload="$(cat || true)"
|
|
7
|
+
transcript_path="$(relay_json_string_field transcript_path "$payload")"
|
|
8
|
+
|
|
9
|
+
relay_post_timeline_status busy provider-turn "" compacting
|
|
10
|
+
# Pre-destroy seam (#183): compaction is about to discard context — capture this segment's
|
|
11
|
+
# Insights (#184) from the full transcript first. The most common boundary in practice.
|
|
12
|
+
relay_post_session_boundary "$transcript_path" compact
|
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# BEST-EFFORT GUARDRAIL / defense-in-depth only. This hook denies common
|
|
3
|
+
# mutating Bash patterns for read-only Claude sessions, but it is not a sandbox.
|
|
4
|
+
# Real boundaries are Relay token scope, unavailable write tools, and future
|
|
5
|
+
# OS-level enforcement.
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
HOOK_DIR="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/hooks"
|
|
8
|
+
source "${HOOK_DIR}/relay-status.sh"
|
|
9
|
+
relay_install_hook_guard read-only-bash-guard
|
|
10
|
+
# Shared fleet-fratricide kill-pattern matcher (#753): trim_shell_text,
|
|
11
|
+
# split_segments, shell_words and fratricide_deny_reason live here.
|
|
12
|
+
source "${HOOK_DIR}/kill-guard.sh"
|
|
13
|
+
|
|
14
|
+
payload="$(cat)"
|
|
15
|
+
|
|
16
|
+
emit_deny() {
|
|
17
|
+
local reason="${1:-Read-only Claude sessions may only run inspection commands.}"
|
|
18
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"%s"}}\n' \
|
|
19
|
+
"$(relay_json_escape "$reason")"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
shopt -s extglob
|
|
23
|
+
|
|
24
|
+
# Extract the Bash command once. jq is strongly preferred; both the fleet-kill
|
|
25
|
+
# guard (all modes) and the read-only allow-list (read-only mode) consume it.
|
|
26
|
+
jq_available=0
|
|
27
|
+
tool_name=""
|
|
28
|
+
command_text=""
|
|
29
|
+
if command -v jq >/dev/null 2>&1; then
|
|
30
|
+
jq_available=1
|
|
31
|
+
tool_name="$(printf '%s' "$payload" | jq -r '.tool_name // empty' 2>/dev/null || true)"
|
|
32
|
+
command_text="$(printf '%s' "$payload" | jq -r '.tool_input.command // empty' 2>/dev/null || true)"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# --- Fleet-fratricide guard: ACTIVE IN ALL APPROVAL MODES (#753) ---
|
|
36
|
+
# A broad pkill/killall/kill can SIGTERM every sibling agent's runner host-wide,
|
|
37
|
+
# so this block runs before (and independent of) the read-only allow-list.
|
|
38
|
+
if (( jq_available )); then
|
|
39
|
+
if [ "${tool_name,,}" = "bash" ] && [ -n "$command_text" ]; then
|
|
40
|
+
if fratricide_reason="$(fratricide_deny_reason "$command_text")"; then
|
|
41
|
+
emit_deny "$fratricide_reason"
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
else
|
|
46
|
+
# No jq: best-effort raw scan so the fleet guard still bites the obvious cases.
|
|
47
|
+
if fratricide_reason="$(fratricide_raw_reason "$payload")"; then
|
|
48
|
+
emit_deny "$fratricide_reason"
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# --- Read-only allow-list: only the read-only approval mode below this point ---
|
|
54
|
+
case "${AGENT_RELAY_APPROVAL:-}" in
|
|
55
|
+
open|guarded)
|
|
56
|
+
exit 0
|
|
57
|
+
;;
|
|
58
|
+
read-only)
|
|
59
|
+
;;
|
|
60
|
+
*)
|
|
61
|
+
# Unknown approval values fail closed so typos such as "readonly" or
|
|
62
|
+
# casing drift do not silently disable the read-only boundary.
|
|
63
|
+
;;
|
|
64
|
+
esac
|
|
65
|
+
|
|
66
|
+
if (( ! jq_available )); then
|
|
67
|
+
emit_deny "Read-only Bash guard could not inspect the command because jq is unavailable."
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ "${tool_name,,}" != "bash" ]; then
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ -z "$command_text" ]; then
|
|
76
|
+
emit_deny "Read-only Bash guard denied an empty Bash command."
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
DENY_REASON=""
|
|
81
|
+
|
|
82
|
+
deny_segment() {
|
|
83
|
+
DENY_REASON="$1"
|
|
84
|
+
return 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
hard_deny_reason() {
|
|
88
|
+
local text="$1"
|
|
89
|
+
case "$text" in
|
|
90
|
+
*'$('*|*'`'*)
|
|
91
|
+
printf '%s' "Read-only Bash guard denied command substitution."
|
|
92
|
+
return 0
|
|
93
|
+
;;
|
|
94
|
+
*'<('*|*'>('*)
|
|
95
|
+
printf '%s' "Read-only Bash guard denied process substitution."
|
|
96
|
+
return 0
|
|
97
|
+
;;
|
|
98
|
+
esac
|
|
99
|
+
|
|
100
|
+
local i len ch next quote="" escaped=0
|
|
101
|
+
len=${#text}
|
|
102
|
+
for ((i = 0; i < len; i++)); do
|
|
103
|
+
ch="${text:i:1}"
|
|
104
|
+
if (( escaped )); then
|
|
105
|
+
escaped=0
|
|
106
|
+
continue
|
|
107
|
+
fi
|
|
108
|
+
if [ -n "$quote" ]; then
|
|
109
|
+
if [ "$quote" = '"' ] && [ "$ch" = "\\" ]; then
|
|
110
|
+
escaped=1
|
|
111
|
+
continue
|
|
112
|
+
fi
|
|
113
|
+
if [ "$ch" = "$quote" ]; then
|
|
114
|
+
quote=""
|
|
115
|
+
fi
|
|
116
|
+
continue
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
case "$ch" in
|
|
120
|
+
"'"|'"')
|
|
121
|
+
quote="$ch"
|
|
122
|
+
;;
|
|
123
|
+
"\\")
|
|
124
|
+
escaped=1
|
|
125
|
+
;;
|
|
126
|
+
">")
|
|
127
|
+
printf '%s' "Read-only Bash guard denied shell output redirection."
|
|
128
|
+
return 0
|
|
129
|
+
;;
|
|
130
|
+
"<")
|
|
131
|
+
next="${text:i+1:1}"
|
|
132
|
+
if [ "$next" = ">" ]; then
|
|
133
|
+
printf '%s' "Read-only Bash guard denied shell output redirection."
|
|
134
|
+
return 0
|
|
135
|
+
fi
|
|
136
|
+
;;
|
|
137
|
+
esac
|
|
138
|
+
done
|
|
139
|
+
|
|
140
|
+
return 1
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
validate_git_branch() {
|
|
144
|
+
local i="$1" arg
|
|
145
|
+
while (( i < ${#WORDS[@]} )); do
|
|
146
|
+
arg="${WORDS[i]}"
|
|
147
|
+
case "$arg" in
|
|
148
|
+
--list|--all|--remotes|--show-current|-a|-r|-v|-vv)
|
|
149
|
+
;;
|
|
150
|
+
*)
|
|
151
|
+
deny_segment "Read-only Bash guard denied a git branch mutation or unsupported branch option."
|
|
152
|
+
return 1
|
|
153
|
+
;;
|
|
154
|
+
esac
|
|
155
|
+
((i += 1))
|
|
156
|
+
done
|
|
157
|
+
return 0
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
validate_git_read_args() {
|
|
161
|
+
local i="$1" arg
|
|
162
|
+
while (( i < ${#WORDS[@]} )); do
|
|
163
|
+
arg="${WORDS[i]}"
|
|
164
|
+
if [ "$arg" = "--" ]; then
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
167
|
+
case "$arg" in
|
|
168
|
+
--output|--output=*)
|
|
169
|
+
deny_segment "Read-only Bash guard denied git output-to-file options."
|
|
170
|
+
return 1
|
|
171
|
+
;;
|
|
172
|
+
esac
|
|
173
|
+
((i += 1))
|
|
174
|
+
done
|
|
175
|
+
return 0
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
validate_git() {
|
|
179
|
+
local i="$1" arg sub
|
|
180
|
+
while (( i < ${#WORDS[@]} )); do
|
|
181
|
+
arg="${WORDS[i]}"
|
|
182
|
+
case "$arg" in
|
|
183
|
+
-C)
|
|
184
|
+
((i += 1))
|
|
185
|
+
if (( i >= ${#WORDS[@]} )); then
|
|
186
|
+
deny_segment "Read-only Bash guard denied git -C without a path."
|
|
187
|
+
return 1
|
|
188
|
+
fi
|
|
189
|
+
((i += 1))
|
|
190
|
+
;;
|
|
191
|
+
--no-pager)
|
|
192
|
+
((i += 1))
|
|
193
|
+
;;
|
|
194
|
+
-c|--config-env|--config-env=*)
|
|
195
|
+
deny_segment "Read-only Bash guard denied git configuration or alias injection."
|
|
196
|
+
return 1
|
|
197
|
+
;;
|
|
198
|
+
--)
|
|
199
|
+
((i += 1))
|
|
200
|
+
break
|
|
201
|
+
;;
|
|
202
|
+
-*)
|
|
203
|
+
deny_segment "Read-only Bash guard denied an unsupported git global option."
|
|
204
|
+
return 1
|
|
205
|
+
;;
|
|
206
|
+
*)
|
|
207
|
+
break
|
|
208
|
+
;;
|
|
209
|
+
esac
|
|
210
|
+
done
|
|
211
|
+
|
|
212
|
+
if (( i >= ${#WORDS[@]} )); then
|
|
213
|
+
deny_segment "Read-only Bash guard denied git without an explicit read-only subcommand."
|
|
214
|
+
return 1
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
sub="${WORDS[i],,}"
|
|
218
|
+
((i += 1))
|
|
219
|
+
case "$sub" in
|
|
220
|
+
diff|log|status|show|blame|grep|rev-parse|describe)
|
|
221
|
+
validate_git_read_args "$i"
|
|
222
|
+
return $?
|
|
223
|
+
;;
|
|
224
|
+
branch)
|
|
225
|
+
validate_git_branch "$i"
|
|
226
|
+
return $?
|
|
227
|
+
;;
|
|
228
|
+
*)
|
|
229
|
+
deny_segment "Read-only Bash guard denied a git subcommand that is not in the read-only allow-list."
|
|
230
|
+
return 1
|
|
231
|
+
;;
|
|
232
|
+
esac
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
find_arg_takes_operand() {
|
|
236
|
+
local arg="$1"
|
|
237
|
+
case "$arg" in
|
|
238
|
+
-name|-iname|-path|-ipath|-wholename|-iwholename|-regex|-iregex|-type|-xtype|-user|-group|-newer|-samefile|-size|-perm|-mtime|-mmin|-atime|-amin|-ctime|-cmin|-maxdepth|-mindepth|-printf|-printf0)
|
|
239
|
+
return 0
|
|
240
|
+
;;
|
|
241
|
+
-newer??)
|
|
242
|
+
return 0
|
|
243
|
+
;;
|
|
244
|
+
*)
|
|
245
|
+
return 1
|
|
246
|
+
;;
|
|
247
|
+
esac
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
validate_find() {
|
|
251
|
+
local i="$1" arg skip_next=0
|
|
252
|
+
while (( i < ${#WORDS[@]} )); do
|
|
253
|
+
arg="${WORDS[i]}"
|
|
254
|
+
if (( skip_next )); then
|
|
255
|
+
skip_next=0
|
|
256
|
+
((i += 1))
|
|
257
|
+
continue
|
|
258
|
+
fi
|
|
259
|
+
case "$arg" in
|
|
260
|
+
-exec|-execdir|-ok|-okdir|-delete|-fls|-fprint|-fprintf|-fprint0)
|
|
261
|
+
deny_segment "Read-only Bash guard denied find mutating action predicates."
|
|
262
|
+
return 1
|
|
263
|
+
;;
|
|
264
|
+
esac
|
|
265
|
+
if find_arg_takes_operand "$arg"; then
|
|
266
|
+
skip_next=1
|
|
267
|
+
fi
|
|
268
|
+
((i += 1))
|
|
269
|
+
done
|
|
270
|
+
return 0
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
validate_fd() {
|
|
274
|
+
local i="$1" arg
|
|
275
|
+
while (( i < ${#WORDS[@]} )); do
|
|
276
|
+
arg="${WORDS[i]}"
|
|
277
|
+
if [ "$arg" = "--" ]; then
|
|
278
|
+
return 0
|
|
279
|
+
fi
|
|
280
|
+
case "$arg" in
|
|
281
|
+
-x|-X|--exec|--exec-batch)
|
|
282
|
+
deny_segment "Read-only Bash guard denied fd exec action options."
|
|
283
|
+
return 1
|
|
284
|
+
;;
|
|
285
|
+
esac
|
|
286
|
+
((i += 1))
|
|
287
|
+
done
|
|
288
|
+
return 0
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
validate_rg() {
|
|
292
|
+
local i="$1" arg
|
|
293
|
+
while (( i < ${#WORDS[@]} )); do
|
|
294
|
+
arg="${WORDS[i]}"
|
|
295
|
+
if [ "$arg" = "--" ]; then
|
|
296
|
+
return 0
|
|
297
|
+
fi
|
|
298
|
+
case "$arg" in
|
|
299
|
+
--pre|--pre=*)
|
|
300
|
+
deny_segment "Read-only Bash guard denied rg preprocessor execution options."
|
|
301
|
+
return 1
|
|
302
|
+
;;
|
|
303
|
+
esac
|
|
304
|
+
((i += 1))
|
|
305
|
+
done
|
|
306
|
+
return 0
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
validate_sort() {
|
|
310
|
+
local i="$1" arg
|
|
311
|
+
while (( i < ${#WORDS[@]} )); do
|
|
312
|
+
arg="${WORDS[i]}"
|
|
313
|
+
if [ "$arg" = "--" ]; then
|
|
314
|
+
return 0
|
|
315
|
+
fi
|
|
316
|
+
case "$arg" in
|
|
317
|
+
-o|-o?*|--output|--output=*)
|
|
318
|
+
deny_segment "Read-only Bash guard denied sort output-to-file options."
|
|
319
|
+
return 1
|
|
320
|
+
;;
|
|
321
|
+
esac
|
|
322
|
+
((i += 1))
|
|
323
|
+
done
|
|
324
|
+
return 0
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
uniq_arg_takes_operand() {
|
|
328
|
+
local arg="$1"
|
|
329
|
+
case "$arg" in
|
|
330
|
+
-f|-s|-w|--skip-fields|--skip-chars|--check-chars)
|
|
331
|
+
return 0
|
|
332
|
+
;;
|
|
333
|
+
*)
|
|
334
|
+
return 1
|
|
335
|
+
;;
|
|
336
|
+
esac
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
validate_uniq() {
|
|
340
|
+
local i="$1" arg operands=0 after_options=0 skip_next=0
|
|
341
|
+
while (( i < ${#WORDS[@]} )); do
|
|
342
|
+
arg="${WORDS[i]}"
|
|
343
|
+
if (( skip_next )); then
|
|
344
|
+
skip_next=0
|
|
345
|
+
((i += 1))
|
|
346
|
+
continue
|
|
347
|
+
fi
|
|
348
|
+
if (( ! after_options )) && [ "$arg" = "--" ]; then
|
|
349
|
+
after_options=1
|
|
350
|
+
((i += 1))
|
|
351
|
+
continue
|
|
352
|
+
fi
|
|
353
|
+
if (( ! after_options )) && [ "$arg" != "-" ] && [[ "$arg" == -* ]]; then
|
|
354
|
+
if uniq_arg_takes_operand "$arg"; then
|
|
355
|
+
skip_next=1
|
|
356
|
+
fi
|
|
357
|
+
((i += 1))
|
|
358
|
+
continue
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
((operands += 1))
|
|
362
|
+
if (( operands > 1 )); then
|
|
363
|
+
deny_segment "Read-only Bash guard denied uniq output-file operands."
|
|
364
|
+
return 1
|
|
365
|
+
fi
|
|
366
|
+
((i += 1))
|
|
367
|
+
done
|
|
368
|
+
return 0
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
validate_yq() {
|
|
372
|
+
local i="$1" arg
|
|
373
|
+
while (( i < ${#WORDS[@]} )); do
|
|
374
|
+
arg="${WORDS[i]}"
|
|
375
|
+
if [ "$arg" = "--" ]; then
|
|
376
|
+
return 0
|
|
377
|
+
fi
|
|
378
|
+
case "$arg" in
|
|
379
|
+
-i|-i?*|--inplace|--inplace=*)
|
|
380
|
+
deny_segment "Read-only Bash guard denied yq in-place edit options."
|
|
381
|
+
return 1
|
|
382
|
+
;;
|
|
383
|
+
esac
|
|
384
|
+
((i += 1))
|
|
385
|
+
done
|
|
386
|
+
return 0
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
validate_bun() {
|
|
390
|
+
local i="$1" sub next
|
|
391
|
+
if (( i >= ${#WORDS[@]} )); then
|
|
392
|
+
deny_segment "Read-only Bash guard denied bun without an explicit test/typecheck command."
|
|
393
|
+
return 1
|
|
394
|
+
fi
|
|
395
|
+
sub="${WORDS[i],,}"
|
|
396
|
+
case "$sub" in
|
|
397
|
+
test)
|
|
398
|
+
return 0
|
|
399
|
+
;;
|
|
400
|
+
run)
|
|
401
|
+
next="${WORDS[i+1]:-}"
|
|
402
|
+
if [ "$next" = "typecheck" ]; then
|
|
403
|
+
return 0
|
|
404
|
+
fi
|
|
405
|
+
deny_segment "Read-only Bash guard only allows bun run typecheck in read-only sessions."
|
|
406
|
+
return 1
|
|
407
|
+
;;
|
|
408
|
+
*)
|
|
409
|
+
deny_segment "Read-only Bash guard denied a bun command outside the test/typecheck allow-list."
|
|
410
|
+
return 1
|
|
411
|
+
;;
|
|
412
|
+
esac
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
validate_agent_relay() {
|
|
416
|
+
local i="$1" sub next
|
|
417
|
+
if (( i >= ${#WORDS[@]} )); then
|
|
418
|
+
deny_segment "Read-only Bash guard denied agent-relay without an allowed read/message subcommand."
|
|
419
|
+
return 1
|
|
420
|
+
fi
|
|
421
|
+
sub="${WORDS[i]}"
|
|
422
|
+
next="${WORDS[i+1]:-}"
|
|
423
|
+
case "$sub" in
|
|
424
|
+
/reply|/react|/message|/status|/read-message|/tags|/whoami|/guide)
|
|
425
|
+
return 0
|
|
426
|
+
;;
|
|
427
|
+
get-message|status|whoami|guide|tags)
|
|
428
|
+
return 0
|
|
429
|
+
;;
|
|
430
|
+
workspace)
|
|
431
|
+
if [ "$next" = "status" ]; then
|
|
432
|
+
return 0
|
|
433
|
+
fi
|
|
434
|
+
deny_segment "Read-only Bash guard denied a mutating agent-relay workspace command."
|
|
435
|
+
return 1
|
|
436
|
+
;;
|
|
437
|
+
*)
|
|
438
|
+
deny_segment "Read-only Bash guard denied an agent-relay command outside the read/message allow-list."
|
|
439
|
+
return 1
|
|
440
|
+
;;
|
|
441
|
+
esac
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
validate_leading_assignment() {
|
|
445
|
+
local assignment="$1" name value
|
|
446
|
+
name="${assignment%%=*}"
|
|
447
|
+
value="${assignment#*=}"
|
|
448
|
+
|
|
449
|
+
case "$name" in
|
|
450
|
+
LANG|LANGUAGE|LC_*)
|
|
451
|
+
if [[ "$value" =~ ^[A-Za-z0-9_.:@+-]+$ ]]; then
|
|
452
|
+
return 0
|
|
453
|
+
fi
|
|
454
|
+
;;
|
|
455
|
+
TZ)
|
|
456
|
+
if [[ "$value" =~ ^[A-Za-z0-9_+:-]+(/[A-Za-z0-9_+:-]+)*$ ]]; then
|
|
457
|
+
return 0
|
|
458
|
+
fi
|
|
459
|
+
;;
|
|
460
|
+
PAGER|GIT_PAGER)
|
|
461
|
+
if [ "$value" = "cat" ]; then
|
|
462
|
+
return 0
|
|
463
|
+
fi
|
|
464
|
+
;;
|
|
465
|
+
esac
|
|
466
|
+
|
|
467
|
+
deny_segment "Read-only Bash guard denied unsafe leading environment assignment."
|
|
468
|
+
return 1
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
validate_segment() {
|
|
472
|
+
local raw="$1" segment i=0 saw_assignment=0 command_name base
|
|
473
|
+
segment="$(trim_shell_text "$raw")"
|
|
474
|
+
if [ -z "$segment" ]; then
|
|
475
|
+
return 0
|
|
476
|
+
fi
|
|
477
|
+
if ! shell_words "$segment"; then
|
|
478
|
+
deny_segment "Read-only Bash guard denied shell syntax it could not parse."
|
|
479
|
+
return 1
|
|
480
|
+
fi
|
|
481
|
+
if (( ${#WORDS[@]} == 0 )); then
|
|
482
|
+
return 0
|
|
483
|
+
fi
|
|
484
|
+
|
|
485
|
+
while (( i < ${#WORDS[@]} )) && [[ "${WORDS[i]}" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; do
|
|
486
|
+
if ! validate_leading_assignment "${WORDS[i]}"; then
|
|
487
|
+
return 1
|
|
488
|
+
fi
|
|
489
|
+
saw_assignment=1
|
|
490
|
+
((i += 1))
|
|
491
|
+
done
|
|
492
|
+
|
|
493
|
+
if (( i >= ${#WORDS[@]} )); then
|
|
494
|
+
deny_segment "Read-only Bash guard denied an assignment-only shell segment."
|
|
495
|
+
return 1
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
command_name="${WORDS[i]}"
|
|
499
|
+
if [[ "$command_name" == \$* ]]; then
|
|
500
|
+
deny_segment "Read-only Bash guard denied an indirect command name."
|
|
501
|
+
return 1
|
|
502
|
+
fi
|
|
503
|
+
|
|
504
|
+
base="${command_name##*/}"
|
|
505
|
+
base="${base,,}"
|
|
506
|
+
if [ -z "$base" ]; then
|
|
507
|
+
deny_segment "Read-only Bash guard denied an empty command name."
|
|
508
|
+
return 1
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
case "$base" in
|
|
512
|
+
bash|sh|zsh|dash|env|python*|node|ruby|perl|php|awk|eval|exec|source|.)
|
|
513
|
+
deny_segment "Read-only Bash guard denied a nested shell, interpreter, or shell re-entry command."
|
|
514
|
+
return 1
|
|
515
|
+
;;
|
|
516
|
+
esac
|
|
517
|
+
|
|
518
|
+
if (( saw_assignment )) && [[ "$command_name" == *'$'* ]]; then
|
|
519
|
+
deny_segment "Read-only Bash guard denied assignment-based command indirection."
|
|
520
|
+
return 1
|
|
521
|
+
fi
|
|
522
|
+
|
|
523
|
+
case "$base" in
|
|
524
|
+
git)
|
|
525
|
+
validate_git "$((i + 1))"
|
|
526
|
+
return $?
|
|
527
|
+
;;
|
|
528
|
+
find)
|
|
529
|
+
validate_find "$((i + 1))"
|
|
530
|
+
return $?
|
|
531
|
+
;;
|
|
532
|
+
fd)
|
|
533
|
+
validate_fd "$((i + 1))"
|
|
534
|
+
return $?
|
|
535
|
+
;;
|
|
536
|
+
rg)
|
|
537
|
+
validate_rg "$((i + 1))"
|
|
538
|
+
return $?
|
|
539
|
+
;;
|
|
540
|
+
sort)
|
|
541
|
+
validate_sort "$((i + 1))"
|
|
542
|
+
return $?
|
|
543
|
+
;;
|
|
544
|
+
uniq)
|
|
545
|
+
validate_uniq "$((i + 1))"
|
|
546
|
+
return $?
|
|
547
|
+
;;
|
|
548
|
+
yq)
|
|
549
|
+
validate_yq "$((i + 1))"
|
|
550
|
+
return $?
|
|
551
|
+
;;
|
|
552
|
+
grep|cat|ls|head|tail|wc|pwd|jq|nl|cut)
|
|
553
|
+
return 0
|
|
554
|
+
;;
|
|
555
|
+
bun)
|
|
556
|
+
validate_bun "$((i + 1))"
|
|
557
|
+
return $?
|
|
558
|
+
;;
|
|
559
|
+
agent-relay)
|
|
560
|
+
validate_agent_relay "$((i + 1))"
|
|
561
|
+
return $?
|
|
562
|
+
;;
|
|
563
|
+
*)
|
|
564
|
+
deny_segment "Read-only Bash guard denied a command outside the read-only allow-list."
|
|
565
|
+
return 1
|
|
566
|
+
;;
|
|
567
|
+
esac
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if reason="$(hard_deny_reason "$command_text")"; then
|
|
571
|
+
emit_deny "$reason"
|
|
572
|
+
exit 0
|
|
573
|
+
fi
|
|
574
|
+
|
|
575
|
+
if ! split_segments "$command_text"; then
|
|
576
|
+
emit_deny "Read-only Bash guard denied shell syntax it could not parse."
|
|
577
|
+
exit 0
|
|
578
|
+
fi
|
|
579
|
+
|
|
580
|
+
for segment in "${SEGMENTS[@]}"; do
|
|
581
|
+
if ! validate_segment "$segment"; then
|
|
582
|
+
emit_deny "${DENY_REASON:-Read-only Claude sessions may only run explicit inspection commands.}"
|
|
583
|
+
exit 0
|
|
584
|
+
fi
|
|
585
|
+
done
|
|
586
|
+
|
|
587
|
+
exit 0
|