Merge remote-tracking branch 'refs/remotes/upstream/main'
# Conflicts: # ROADMAP.md # TODO.md # src/cli.tsx # src/config.ts # src/mcp.ts # src/permission.ts # src/prompt.ts # src/session.ts # src/snapshot.ts # src/subagent.ts # src/tools-extra.ts # src/tools.ts # src/ui/App.tsx # test/mcp.test.ts # test/prune.test.ts # test/session.test.ts # test/tools.test.ts
This commit is contained in:
+195
-15
@@ -275,6 +275,117 @@ agent·model row inside it and a split footer beneath.
|
||||
|
||||
---
|
||||
|
||||
## What the other agents have
|
||||
|
||||
Surveyed opencode, Claude Code, Codex CLI, and phi against this tool's feature set. The point of
|
||||
the table is to record what is *worth copying* and what is worth *declining*, not to chase parity:
|
||||
each of these four spent effort here deliberately, and several of their choices are load-bearing for
|
||||
a reason that applies to us.
|
||||
|
||||
The four are not the same shape. **Claude Code** and **Codex** are first-party CLIs tied to one
|
||||
vendor's models; **opencode** and **phi** are open-source and provider-agnostic, and opencode in
|
||||
particular is the closest thing to a peer here. Where a feature exists, the notes say what it costs
|
||||
— several are cheap to copy, and three are not.
|
||||
|
||||
### Where the four agree
|
||||
|
||||
Six features have converged across all or nearly all of them. Convergence is the strongest signal
|
||||
available that a feature is not a fad — and the gaps in the table are as informative as the checks,
|
||||
because they show which features are genuinely optional and which are table stakes.
|
||||
|
||||
| Feature | opencode | Claude Code | Codex | phi | Here |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Per-pattern permission rules | `permission.bash` globs | `settings.json` allow/deny/ask | `approval_policy` + rules | Gate + `permissions.mode` | **shipped** |
|
||||
| Subagents with fresh context | `mode: subagent` | `agents/*.md` | — | sub-agents | **shipped** |
|
||||
| Markdown-defined agents | `agents/`, `commands/` | `agents/`, `skills/` | `AGENTS.md` | `.phi/` | **shipped** |
|
||||
| Session resume | `--session`, `--continue` | `--resume`, `--continue` | resume + rollout files | `sessions` | **shipped** |
|
||||
| Compaction | auto + `/compact` | `/compact`, `/rewind` summarize | compact prompt file | — | **shipped** |
|
||||
| Undo / rewind | `/undo`, `/redo` (via git) | `/rewind` (snapshots) | — | — | **missing** |
|
||||
|
||||
Two of the six are outright missing from one or more tools, and undoing is missing from two of the
|
||||
four — so it is a real feature, not table stakes, and the two that have it disagree about how. The
|
||||
permission model here is not behind: it already carries the per-pattern rules, the credential deny,
|
||||
and the repeat guard the others arrived at, and `doom_loop` (below) is the one refinement worth
|
||||
taking. **Undo is the single converged feature genuinely absent**, which is why it leads Next.
|
||||
|
||||
### Detail worth having, by tool
|
||||
|
||||
**opencode** — the closest peer, and the source of the permission shape already adapted here.
|
||||
`/undo` and `/redo` revert file changes **through git**, so they require the project to be a git
|
||||
repository; this is a real limitation, not an implementation detail, and it means an undo is only
|
||||
as good as the working tree's state. Agents are `primary` (build, plan) or `subagent` (general,
|
||||
explore, scout), switchable with Tab or `@`-mention, configured in `opencode.json` or Markdown
|
||||
frontmatter. A subagent runs in a **child session** with its own navigation keybinds
|
||||
(`session_child_first`, `session_parent`), and `subagent_depth` (default 1) caps nesting. Notable:
|
||||
`doom_loop` is a first-class permission key that fires when the same tool call repeats three times
|
||||
with identical input — the repeat guard here is an approval rule; theirs is a named primitive with
|
||||
its own recovery prompts. Sessions share over a URL (`/share`), which is a hosted product decision,
|
||||
not a local one.
|
||||
|
||||
**Claude Code** — the most complete implementation of undo, and worth reading before building one.
|
||||
Checkpointing snapshots **before every user prompt**, keeps the **100 most recent** checkpoints,
|
||||
and stores them with the conversation so `/rewind` survives a resume. The rewind menu offers five
|
||||
distinct actions, and the split is the interesting part: *restore code*, *restore conversation*,
|
||||
*restore both*, *summarize from here*, *summarize up to here*. That is undo and compaction sharing
|
||||
one control surface. The honest limits are documented rather than hidden: only `Write`/`Edit`/
|
||||
`NotebookEdit` are tracked, `bash` side effects are not, **subagent edits are not captured** unless
|
||||
the skill ran in the foreground with `context: fork`, and symlinked or hard-linked files are
|
||||
skipped with an explicit warning. Their hook surface is the largest of the four — see *External
|
||||
hooks* under Later, which is where that capability belongs here.
|
||||
|
||||
**Codex** — the sandbox is the differentiator, and it is genuinely hard to copy. Apple Seatbelt on
|
||||
macOS, Landlock + seccomp on Linux, and a restricted-token/AppContainer mechanism on Windows, with
|
||||
`workspace-write` the default and network **off** unless `sandbox_workspace_write.network_access`
|
||||
is set. `approval_policy` is now `on-request | never | { granular = {...} }` — `untrusted` was
|
||||
**retired** and can prevent the client from starting. Also shipped and worth knowing:
|
||||
`approvals_reviewer = "auto_review"` routes an approval prompt through a *reviewer subagent*
|
||||
rather than the user. `AGENTS.md` resolves global → project root → cwd, one file per directory,
|
||||
`AGENTS.override.md` winning, capped by `project_doc_max_bytes` (32 KiB default).
|
||||
|
||||
**phi** — small (Go, ~12 MB), and the two ideas most worth stealing. First, **MCP without context
|
||||
death**: server tool schemas never enter the prompt; the system prompt lists only **server names**,
|
||||
and the model uses three meta-tools — `mcp_list`, `mcp_inspect`, `mcp_call` — with subprocesses
|
||||
starting lazily on first use. This is the concrete design behind *MCP without the schema tax*
|
||||
under Next. Second, the
|
||||
hook contract is the cleanest of the four: `pre_tool` runs **before** the permission gate and can
|
||||
`allow`, `deny`, or **`modify`** the input; `post_tool` can append model-facing `context` or
|
||||
rewrite `output`. Exit code `2` is a hard deny; `fail_closed` decides crash behaviour; in
|
||||
`readonly` mode only `fail_closed` hooks run, so a slow audit hook cannot stall exploration.
|
||||
|
||||
### Corrections to what this file said before
|
||||
|
||||
Two claims previously written here were incomplete, and the research fixes them:
|
||||
|
||||
- **Input-rewriting hooks are not phi's alone.** Codex ships it too: `PreToolUse` returns
|
||||
`permissionDecision: "allow"` with `updatedInput` to rewrite a call, verified in their docs and
|
||||
in `codex-rs/.../mcp.rs` (`with_updated_hook_input`). Two independent implementations make this
|
||||
the standard shape rather than one project's quirk — which raises its priority, and means the
|
||||
compiled plugin interface here is now the odd one out.
|
||||
- **Codex's hook trust is exactly as described, and the mechanism is now known.** Non-managed
|
||||
hooks cannot run until reviewed: Codex persists a `trusted_hash` in `config.toml`, records trust
|
||||
against the hook's **current hash**, and marks new or changed hooks for review in `/hooks`.
|
||||
`--dangerously-bypass-hook-trust` skips it for one invocation. The known hole is instructive: a
|
||||
reviewer on their PR noted that **replacing the script a hook points at does not reset trust**,
|
||||
because only the config is hashed. A trust story that hashes the declaration but not the artefact
|
||||
is a partial one — worth designing past rather than copying.
|
||||
|
||||
### What is worth declining
|
||||
|
||||
Three of their features are deliberate here, and the survey confirms the reasoning:
|
||||
|
||||
- **A client/server split** (opencode's OpenAPI server + TUI-as-client + IDE/web clients) exists to
|
||||
serve *second clients*. No second client is wanted here.
|
||||
- **LSP integration** — the quote already cited in Declined is accurate and now verified in full:
|
||||
opencode's own LSP page says it "is useful in some projects, but it is not always a net positive,"
|
||||
that servers "can get out of sync, use significant memory, vary by version or project, and slow
|
||||
down agent workflows," and that "in many projects it is better to have the agent run lint,
|
||||
typecheck, or other diagnostic CLI tools directly." They ship 30+ built-in servers and still say
|
||||
this. That is the strongest possible endorsement of the position in Declined.
|
||||
- **Session sharing over a URL** (opencode `/share`) is a hosted-service feature and brings a
|
||||
privacy surface this tool has no reason to take on.
|
||||
|
||||
---
|
||||
|
||||
## Next
|
||||
|
||||
### Prompt caching
|
||||
@@ -293,12 +404,62 @@ answer is three meta-tools — `mcp_list`, `mcp_inspect`, `mcp_call` — with th
|
||||
the servers, so a hundred servers cost almost nothing until one is called. Worth keeping direct
|
||||
registration as an option: for a two-tool server the indirection is the more expensive of the two.
|
||||
|
||||
### Undo a turn
|
||||
|
||||
Every comparable CLI has this: opencode `/undo` and `/redo`, Claude Code `/rewind` with
|
||||
checkpoints. There is `/resume` here, which restores a session, and nothing that walks one back.
|
||||
Claude Code's implementation is the one to read first, because it has already worked out the seams:
|
||||
it snapshots before **every user prompt**, keeps the **100 most recent**, stores snapshots with the
|
||||
conversation so a rewind survives a resume, and splits one menu into *restore code*, *restore
|
||||
conversation*, *restore both*, and *summarize from here / up to here* — undo and compaction on one
|
||||
control surface. opencode's version is simpler and takes a different position: it reverts through
|
||||
**git**, so it needs a repository and inherits whatever the working tree already contained.
|
||||
|
||||
Both document the same hard limit, and so must this: a `bash` command's effects cannot be
|
||||
snapshotted. Claude Code tracks only its own file-edit tools, explicitly does not cover `bash`
|
||||
side effects, and does not capture subagent edits unless the fork ran in the foreground. The
|
||||
honest version here covers file-tool edits, says so in the command's own output, and reports what
|
||||
it skipped rather than pretending the tree is clean — the same shape used for an interrupted
|
||||
command, whose effects are already reported as unknown.
|
||||
|
||||
### Lossless-enough compaction
|
||||
|
||||
Compaction keeps the model's memory of a turn now, but it still says nothing about the messages it
|
||||
discarded, so the model can contradict its own earlier decision with confidence. A summary of the
|
||||
discarded span costs one cheap call and removes the whole class of problem.
|
||||
|
||||
### Derived tool metadata
|
||||
|
||||
`TOOL_SETS` and `MUTATING_TOOLS` are hand-maintained lists of tool names. A tool added to one
|
||||
and forgotten in the other is a silently ungated write. Marking each tool where it is defined,
|
||||
and checking the coverage in the suite, removes the failure mode rather than documenting it.
|
||||
|
||||
### Registry trust
|
||||
|
||||
An index is trusted for its contents, not its authorship: `registryUrl` is the whole trust
|
||||
decision, and there are no signatures. Publisher keys and a pinned digest per entry would make
|
||||
"install this skill" a decision about a specific artifact rather than about a URL.
|
||||
|
||||
### Auto-review an approval
|
||||
|
||||
Codex ships `approvals_reviewer = "auto_review"`: an eligible approval prompt is routed through a
|
||||
reviewer **subagent** instead of surfacing to the user, using the same sandbox boundary. `explore`
|
||||
and `review` already exist and are read-only, so the piece to build is a reviewer persona that
|
||||
decides an approval request and a policy that says which prompts are eligible — a batch of three
|
||||
identical `git status` calls should not each interrupt, but a first `rm` should. The failure mode
|
||||
to design against is a reviewer that waves through exactly what the user would have stopped, so it
|
||||
must be opt-in, name itself when it approves, and never override a deny rule.
|
||||
|
||||
### Name the repeat guard
|
||||
|
||||
opencode has `doom_loop` as a first-class permission key: it fires when the same tool call repeats
|
||||
three times with identical input, carries its own recovery prompts, and is configurable per agent.
|
||||
The equivalent here is a rule inside the permission layer — the call repeated identically three
|
||||
times in one turn asks even when allowed — which works but is unnamed, unconfigurable, and
|
||||
invisible in `/tools`. Promoting it to a named primitive makes it inspectable and lets an agent
|
||||
tighten or loosen it, and the recovery prompt is the part genuinely missing: a loop is better
|
||||
interrupted with advice than with silence.
|
||||
|
||||
### `web_fetch`
|
||||
|
||||
Shipped in beta.4 — see above. What remains declined: wrappers around a single bash line with
|
||||
@@ -317,18 +478,34 @@ for now. Loading `.shiro/plugins/*.ts` needs a sandbox story first — a plugin
|
||||
tool calls can also lie about blocking them, and one that can execute can read whatever the
|
||||
agent can read.
|
||||
|
||||
**External hooks.** phi and both first-party CLIs let a script sit in the tool loop: a directory
|
||||
with a manifest and an executable, one JSON object in on stdin, one out. phi's `pre_tool` can
|
||||
rewrite the tool's input as well as allow or deny, which the compiled plugin interface here cannot
|
||||
express. The reason it is here rather than in Next is that it needs a trust story — Codex hashes
|
||||
each hook and refuses to run one until you review it, which is the right shape and more work than
|
||||
the feature.
|
||||
**Prompt caching.** Anthropic and OpenAI both support it. The system prompt is rebuilt every
|
||||
step for task-list freshness, which defeats a naive cache; splitting the stable prefix from
|
||||
the volatile suffix would fix that.
|
||||
|
||||
**External hooks.** Every one of the four surveyed except here lets a script sit in the tool loop:
|
||||
a directory with a manifest and an executable, one JSON object in on stdin, one out. **Both** phi
|
||||
and Codex let a `PreToolUse` hook **rewrite** a tool's input, not merely allow or deny it — phi
|
||||
returns `{"action":"modify","input":{...}}`, Codex returns `permissionDecision: "allow"` with
|
||||
`updatedInput` — and that is the exact capability the compiled plugin interface here cannot
|
||||
express. Two independent implementations make it the expected shape rather than one project's
|
||||
quirk. What blocks it is the trust story, not the mechanism.
|
||||
|
||||
The trust story has a known shape and a known hole. Codex will not run a non-managed hook until it
|
||||
has been reviewed: it persists a `trusted_hash` in `config.toml`, records trust against the hook's
|
||||
current hash, and lists new or changed hooks for review under `/hooks`;
|
||||
`--dangerously-bypass-hook-trust` overrides for one invocation. The hole is that only the
|
||||
**declaration** is hashed — replacing the script the hook points at does not reset trust, as a
|
||||
reviewer on their own PR pointed out. Hashing the declaration *and* the artefact is the minimum
|
||||
worth doing here, and it is why this is work rather than a weekend.
|
||||
|
||||
**OS-level sandboxing.** The strongest thing in this class, and Codex is the one that has it:
|
||||
Seatbelt on macOS, Landlock and seccomp on Linux, a separate mechanism on Windows, with network
|
||||
egress governed by domain rules. Permission rules gate the *call*; a sandbox governs what the
|
||||
process can then reach. Three platform-specific implementations, and OpenAI moved Codex to Rust
|
||||
partly for this. A half-built sandbox is worse than none, because people would trust it.
|
||||
Seatbelt on macOS, Landlock and seccomp on Linux, a restricted-token/AppContainer mechanism on
|
||||
Windows, with network **off** by default under `workspace-write`. Permission rules gate the *call*;
|
||||
a sandbox governs what the process can then reach. Note that even Codex's sandbox is not a complete
|
||||
boundary — their own hook docs say "hooks are guardrails, but they are not a complete enforcement
|
||||
boundary for every shell or tool path." Three platform-specific implementations, and OpenAI moved
|
||||
Codex to Rust partly for this. A half-built sandbox is worse than none, because people would trust
|
||||
it.
|
||||
|
||||
---
|
||||
|
||||
@@ -355,8 +532,11 @@ endpoint, which is what lets IDE extensions and a web client exist. It is the ri
|
||||
for that product. Here it would add a protocol, a port, and an auth story to serve a second client
|
||||
nobody has asked for.
|
||||
|
||||
**LSP integration.** opencode ships it and its own documentation says the honest thing:
|
||||
*"not always a net positive... in many projects it is better to have the agent run lint, typecheck,
|
||||
or other diagnostic CLI tools directly."* Language servers drift out of sync, use real memory, and
|
||||
vary by version. `bash bun run typecheck` puts the same errors in front of the model with none of
|
||||
that, and `AGENTS.md` is where the command belongs.
|
||||
**LSP integration.** opencode ships 30+ built-in language servers and its own documentation still
|
||||
says the honest thing: LSP "is useful in some projects, but it is not always a net positive,"
|
||||
servers "can get out of sync, use significant memory, vary by version or project, and slow down
|
||||
agent workflows," and *"in many projects it is better to have the agent run lint, typecheck, or
|
||||
other diagnostic CLI tools directly."* That is the strongest available endorsement of declining it:
|
||||
the project with the most invested says it is often the wrong trade. `bash bun run typecheck` puts
|
||||
the same errors in front of the model with none of that, and `AGENTS.md` is where the command
|
||||
belongs.
|
||||
|
||||
Reference in New Issue
Block a user