Skip to content

feat(tool): command-gate UX — cwd-scoped exact allowlist, read-only split, smarter archive returns - #152

Open
Emasoft wants to merge 3 commits into
mlhher:mainfrom
Emasoft:feat/gate-ux
Open

Emasoft wants to merge 3 commits into
mlhher:mainfrom
Emasoft:feat/gate-ux

Conversation

@Emasoft

@Emasoft Emasoft commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Fixes the command-gate friction reported in Emasoft#1 — the gate re-prompts OTP for the identical command even seconds later, read-only gh subcommands get gated like rm -rf, archived outputs need a second gated read, and the agent can't see approval progress. This PR carries the fork's work that resolves all four suggestions from that issue (it brings the OTP gate split from #127 and the tool-output archiver it builds on, then the UX overhaul on top):

  1. OTP-approved commands are globally allowlisted by the exact (cwd, argv) pair — cross-instance and persistent. The same command in the same directory never triggers the gate again, in any late instance; a different cwd means a different command and a fresh OTP.
  2. Read/write split by subcommand, with a conservative classifier — gh api (GET), issue view, pr view, repo view, search, runs, git log/show/diff/status/blame/rev-parse, and safe filters auto-approve; mutating subcommands, write verbs, redirects, sudo, and dangerous chains still gate.
  3. Richer archive returns — ≤1024 chars inline; 1024–3072 archived AND full inline; >3072 head (2000) + tail (500) + size + a read_file pointer. Deterministic per (output, path), so the reference bytes are prompt-cache stable.
  4. Gate message counters + cwd scoping display — the message shows the resolved cwd, per-command approval counters, and the auto-allow promise, so the agent can see the friction decay.

Design decision — cwd is part of the key: rm -rf ./bin in /tmp is NOT the same command as rm -rf ./bin in $HOME. Relative paths resolve against the cwd, so an approval keyed only on argv would silently approve a different deletion. ResolveShellExecCwd is the single source of truth for the directory a command runs in, and both the OTP binding and the exact allowlist key go through it.

Note: the fork's middle worktree-default tier in ResolveShellExecCwd (WorktreeDirKey) is intentionally not part of this PR — it rides with the upcoming agent-resume PR.

Testing:

  • gofmt on touched files: clean
  • go build ./...
  • go test -race -count=1 ./... — full suite, all green
  • golangci-lint v2.13.2 (fresh GOBIN) — 0 issues

Refs Emasoft#1

…hher#127)

Split out of PR mlhher#127 per owner review (5716775820): the OTP permission mode (`-force-revaluate-dangerous-commands`, OTP registry, otp_code bash parameter, parse-fallback hard-block exemption) and install-dev.sh live here so PR mlhher#127 can stay focused on streaming/retry. Requires the dedicated testing/benchmarking pass requested in the review before it is ready.
…e form in history

Every tool result above 1024 chars is archived content-addressed under
<session>/tool-outputs/ before entering history; history keeps a
deterministic head + '[full output archived: <path>]' reference. The
substitution happens once at admission (never retroactively), so the
prompt cache anchors on the reference bytes from the first request that
carries them; identical outputs dedupe; expand stays exempt and the
reference form passes compaction through unchanged.
…ad-only split, richer archive returns (issue #1)

- Valid OTP execution now globally allowlists the exact (cwd, argv) pair:
  the same command in the same directory never triggers the gate again,
  in any late instance. Different cwd = different command (fresh OTP).
- Read-only operations (gh api GET / issue view / pr view / repo view /
  search / runs; git log/show/diff/status/blame/rev-parse; safe filters)
  auto-approve via a conservative readonly classifier — mutating
  subcommands, write verbs, redirects, sudo and dangerous chains still
  gate.
- Archiver returns richer inline forms: <=1024 inline; 1024-3072 archived
  AND full inline; >3072 head(2000)+tail(500)+size+read_file pointer.
  Deterministic per (output, path) — cache-stable.
- Gate message shows the resolved cwd, per-command approval counters and
  the auto-allow promise.

Upstream port notes (cherry-picked from the fork's local/full):
- ResolveShellExecCwd ships two tiers here (explicit cwd param > process
  CWD). The fork's middle worktree-default tier (WorktreeDirKey) rides
  with the upcoming agent-resume PR, together with its scoping test.
- The compaction-composition archiver test is not ported: compaction
  (mlhher#139) is not part of upstream yet.
- The per-call bash timeout parameter is fork-side work outside this
  change and is not included.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant