Claude Code, Prime Agent, DeepSeek Harness, Task Agent and Revolve, read against each other by what each one actually does, not by which boxes it can check.
Claude Code is Anthropic's commercial terminal and IDE coding agent — the harness that carried out the research for this very report. It ships a large, actively growing built-in feature set: subagents, background sessions, coordinated "agent teams", cross-session messaging, remote control from a phone, MCP, hooks, skills, plugins, and an OS-level sandbox alongside its rule-based permission system.
Prime Agent is Prime Intellect's open-source (MIT) coding and research agent, built on top of pi. Its organizing idea is the Recursive Language Model (RLM): the model has exactly one built-in tool, a persistent IPython kernel, and file access, shell commands, subagent calls and skill invocations all happen as Python code running inside that kernel rather than as separate discrete tool calls.
DeepSeek Harness (dsh) is DeepSeek AI's open-source (MIT) agent harness, currently in developer preview. It is built on Cordis, a plugin framework whose premise is that everything — the model adapter, the tool registry, the session log, even the agent loop itself — is a plugin contributing to one shared context, composed at boot from ordered "bundles" into a named "profile" (docs/architecture.md).
Task Agent is not a chat agent at all. Its own README states the difference plainly: "Autonomous multi-agent system for a Rust monorepo. A dispatcher periodically evaluates global state ... and distributes work to parallel workers. Not a chatbot — a proactive scheduler." There is no live conversation the model reacts to; a dispatcher loop decides what to work on and spawns short-lived workers to do it.
Revolve is this repository — an interactive terminal (and, more recently, GUI) coding agent whose core crate is, confusingly but deliberately, also named task-agent: it is in the process of absorbing the sibling project above. Every citation below without an external link points into this working tree.
Claude Code runs Bash through a real shell and governs it with permission rules matched against the resulting command string (Bash(git commit *)-style patterns), layered under an optional OS-level sandbox — bwrap/Landlock on Linux, Seatbelt on macOS, an ACL restricted-token runner on Windows — that confines a spawned process's filesystem effects to one of three modes (read-only/workspace-write/danger-full-access). The documentation is candid that string-pattern rules over compound commands are fragile: it names Bash(curl http://github.com/ *) as an example a model can defeat with option reordering, a protocol swap, a redirect chain, or a shell variable, and recommends a deny rule or a hook rather than trusting the pattern (docs, "Bash" / "Compound commands" / "Wrappers"). Two GitHub issues show the failure mode in the wild: a chained command slipping an allow match past a later rm (#36637), and Bash(cd:*) escaping its intended scope (#28784).
DeepSeek Harness also spawns a real subprocess, but confinement is its own capability seam (ctx.sandbox) that a consumer — the bash tool — must wrap its argv through before spawning at all; the result carries an enforcement: 'full' | 'partial' field as a reported fact rather than a promise, plus per-backend classification that tells a runner refusing to start apart from the sandbox correctly blocking the command (docs/subsystems/sandbox.md, packages/sandbox/sandbox/src/index.ts:158). Silent unconfined passthrough for a confined policy is documented as illegal — the seam either confines and says how completely, or throws SandboxUnavailableError.
Prime Agent has no Bash tool in the ordinary sense: the model's tool is ipython, and a shell command is a %%bash cell run inside the persistent kernel (docs/rlm.md). It is explicitly, repeatedly not a security sandbox — "The IPython kernel runs model-generated Python and project commands with the worker's operating-system permissions... not a security sandbox" (docs/rlm.md, "Trust Model"; the README repeats it in a boxed warning). Of the five, this is the most permissive by design: the tradeoff is a genuinely persistent, fully programmatic execution surface rather than a discrete, checkable tool call.
Task Agent does not build its own file or command layer at all — it reuses Revolve's: "The API client, the file sandbox and the transcript compaction come from revolve" (task-agent/CLAUDE.md, "The revolve dependency"), pinned to an exact revision. Its own run_command whitelist is narrower than a Revolve session's default — exactly cargo test/clippy/build/doc — because a worker's job is verification, not general shell use.
Revolve's Bash tool takes a program and an argument array, never a command string; a shell metacharacter (;&|<>$`"'\n\r) anywhere in either is refused before a process is spawned (core/src/files.rs:586-597, the character set at :643-650). There is no pipe, no &&, no $(), no redirect to parse or defeat, because nothing interprets the arguments as shell syntax. cargo and git subcommands run without a prompt; everything else asks once, and the tool description says so to the model itself (core/src/tools.rs:1030). This is a structurally different guarantee than a pattern-matched command string — it cannot be bypassed the way Claude Code's own allowlist has been, because there is no shell left to re-read the string a second time. The cost is symmetric and belongs in the same sentence: no pipe, no &&, no redirect for the model either, which is exactly the class of composition Prime Agent's persistent kernel and a real shell both give you back.
Claude Code combines saved settings-file rules (allow/ask/deny, evaluated deny-then-ask-then-allow) with permission modes (Manual, acceptEdits, Plan, Auto, dontAsk, bypassPermissions) that change which of those rules fire automatically, plus a PreToolUse hook that can force or skip a prompt at runtime (docs). The keystroke race is a documented, still-open failure mode, reported twice in the same product family: "Any keystroke being pressed at that moment — Enter, Space, 1, or 2 — can approve the action in the modal that just appeared" (#37955, main product; #23643, JetBrains plugin).
DeepSeek Harness takes the opposite tradeoff from a persisted rule file: its approval seam (ctx.approval.request) is channel-neutral and strictly one-shot. An answer is allowed-once | rejected | cancelled | unavailable; a missing or failing answerer fails closed; and the package's own "Known Limitations" section says outright that there is no allow-always, no remembered rule, and no grant store yet — every decision is asked again next time (packages/interaction/user-approval/README.md).
Revolve's prompt refuses every keystroke for the first 750 ms by default, and any key landing in that window — including one that answers nothing — restarts the pause instead of being counted (default in core/src/settings.rs:98; the hold logic in ask_key, tui/src/tui.rs:3854-3888). This is exactly the failure Claude Code's own two open issues describe, closed by holding the prompt shut on a timer rather than by any change to how the answer is read. Separately, a Bash call may carry the rule the model would propose if the command needs one — a breadth (command/confined/program), a scope (project/global) and a reason of at most sixty characters — which grants nothing by itself; the user reads it as one line and answers with one key (core/src/tools.rs:1030-1044). confined — a program allowed with any arguments as long as none leaves the sandbox root — has no counterpart found in the other four: it reuses the file layer's own boundary instead of a glob someone has to keep correct.
Prime Agent: not established at this level of detail. The documentation set read for this report does not include a dedicated permissions page; the only related material is the trust-model warning already quoted under command execution, which is about running untrusted code rather than about how a single approval prompt is read.
Task Agent has no interactive prompt to race in the first place — it is a dispatcher, not a chat surface. Its equivalent of escalation is tier (light → medium → heavy model) rather than permission (task-agent/CLAUDE.md, "Task Lifecycle").
Claude Code subagents are markdown files with YAML frontmatter (name, description, tools, model, permissionMode, mcpServers, hooks, skills, memory, isolation, background), discovered across project/user/managed/plugin/CLI scopes and invoked either by description-matching or explicitly; three built-ins (Explore, Plan, general-purpose) plus two named helpers ship by default. isolation: worktree gives a subagent its own git worktree with several enforced checks against a command escaping it — a working-directory check across the whole repository and, for Bash, a block on redirecting git into the main checkout plus a refusal of any command shape the analysis can't verify stays inside the worktree. A subagent can carry its own memory scope (user/project/local) that survives across conversations (docs, "Create custom subagents").
DeepSeek Harness has the most explicit design of the five for this axis. Subagent "providers" are pluggable and coexist by name in one context (ctx.subagents), spanning in-process, fork, ACP, Codex, and Claude-Code backends behind one interface (docs/subsystems/subagent.md) — the same delegation call can end up talking to a fresh in-process child or to another product's own subagent mechanism, and the calling code never knows which. It distinguishes two kinds of child: a one-shot run (start(), resolves once to a SubagentResult) and a continuable child — a durable Session with at most one live in-process "Activation" at a time, so a background subagent can be followed up much later (followup()), park itself between turns while its own descendants are still running, and be resumed cold from disk with no provider involved at all. interrupt() cancels a live one without disposing it. A child's own report to its parent is deliberately a different message kind from the runtime's own account of how the child ended, "since a transcript that merged them would credit the child with words it never wrote" (same doc).
Prime Agent subagents are RLM calls: await rlm(...) returns a handle immediately after admission and never waits for or returns the child's answer — a child replies only through agent_message.send or by writing files (docs/rlm.md). The parent-scoped child registry survives compaction, kernel restart and parent restoration; a successfully completed daemon-backed child stays addressable while its parent session is open, until explicitly deleted with rlm.delete_subagent.
Task Agent doesn't expose subagents to a model as a tool at all — "workers" are processes the dispatcher spawns, on two independent axes: tier (light/medium/heavy → model) and kind (editing/analysis/script → toolset). A script-kind task runs a revolve-script document whose own each fan-out draws on the same worker budget the dispatcher gives ordinary workers, "so a run adds no second pool" (task-agent/CLAUDE.md).
Revolve runs children as futures inside the parent process — never a second OS process — sharing one Nursery (http client, tool definitions, file layer, hooks, event sender, cancel channel; core/src/child.rs). run_script's each fans one agent out per element, and the interpreter's bound names outlive the call that created them: a list a script bound is still bound five turns later, in the same session (core/src/agent.rs:76, environment persisted across run_script calls at :1735-1741). A child's tool calls ride the same AgentEvent::ToolCall as the parent's, told apart by a depth field and a short generated name rather than a wrapper type, so no frontend needs a second code path to render them.
spawn_agent child does not outlive the call that created it — that is the job of start_agent instead (below), a session-level primitive rather than a child.Claude Code's cross-session messaging (ListAgents/SendMessage) lets one running session address another by name over a per-session unix-domain inbox socket restricted to the OS user; delivery resolves to delivered, held (pending your approval) or refused, governed by inbound controls and by which side of a permission-mode split each session is on. The documentation states the same structural guarantee Revolve makes about its own peer channel, but as an instruction rather than a type: a message "can't approve anything," can't change permission settings or CLAUDE.md, and a /-command inside its text "arrives as plain text... Claude Code never executes it" (docs, "How a session treats an incoming message"). A separate, more structured "agent teams" feature — a coordinator spawning and supervising a roster with its own TaskCreate/TaskGet/TaskList/TaskUpdate plus CronCreate/CronDelete/CronList tools — is named on the same page but was not investigated further here; not established beyond that one paragraph.
Prime Agent has agent_message (list/send with receiver_role/receiver_name, three delivery modes — auto, steer, follow_up — and a broadcast to "all" scoped to the family roster) plus prime-agent schedule (a general one-time or cron prompt targeted at a named agent, persisted per session); a due tick is claimed before delivery so a crash can't replay it, and missed ticks coalesce instead of piling up (docs/long-running-agents.md). No file-based task-queue convention comparable to .tasks/ was found — coordination here is message- and schedule-driven, addressed by session name, not by a shared queue of files on disk.
DeepSeek Harness: no user-facing task-queue product was found in the material read; the subagent seam above is the coordination primitive, alongside a jobs/workflow capability seam for background work (ctx.jobs, job_* tools) named in the architecture table but not investigated further. Not established beyond that one-line mention.
Task Agent is the one system among the five actually built around a task queue rather than a chat loop. The dispatcher reads .tasks/*.md — the same convention Revolve's own reader uses (header lines, then a body, filename is the slug) — decides a tier and a kind per file, and workers write back into it; a recurring task's file is never deleted, only deferred with wait, and the interval is claimed when a worker starts rather than when it finishes, so a crash mid-run can't replay it immediately (task-agent/CLAUDE.md, "Task Lifecycle", "Recurring tasks"). Several projects share one workspace-level dispatcher; there is no lane-prefix convention for splitting one project's own queue between several agents — that is Revolve's addition, not Task Agent's.
Revolve routes several agents through one project's .tasks/ directory by filename prefix alone: a task file's name is checked against --lane <name>, and only a session with that lane sees the file in its own system prompt (core/src/tasks.rs:92-96, the mine() check feeding queue()). The directory itself is shared across every worktree of one repository (Files::share_tasks, described in this repo's own CLAUDE.md), so a lane worker never re-derives what a coordinator already wrote. start_agent is what makes a peer reachable rather than a mere child: it spawns a real, detached process with its own socket and name (agents::spawn_detached) instead of a future inside the parent, so it survives the turn that created it and can be reached later by send_agent_message/list_agents. A peer's message reaches the turn through its own channel and is structurally incapable of being read as a command — messaging::open returns None for the command half of a Peer arrival, full stop (core/src/messaging.rs:272-289) — the same guarantee Claude Code's docs state about its own peer channel, but enforced by the type rather than stated as an instruction to the model. revolve schedule writes a marked pair of lines directly into the user's own crontab per session (core/src/schedule.rs:221-238), because a timer living inside a detached session dies with the machine, and the schedule worth having can start a session that isn't running at all.
.tasks/*.md entry, human-writable, human-readable, and legible to any other tool that walks the directory. Claude Code and Prime Agent both center a message between named sessions, structurally similar down to the identical "can't approve a permission" guarantee, addressed by session name rather than by a shared file convention. DeepSeek Harness centers a capability seam (subagent providers, jobs) rather than either shape. Revolve is the only one of the five where a task file's own name is the routing key between several agents sharing one repository at once — neither Claude Code's nor Prime Agent's addressing scheme has an equivalent to a lane prefix, and neither ships anything resembling a wait 3d header as a documented, hand-editable convention.Claude Code's "auto memory" writes notes by itself into a per-repository directory shared across worktrees, with no documented rollback for those notes specifically (docs); /rewind covers file edits and the conversation, not the memory files (docs). A subagent can separately be given its own memory scope (user/project/local) that persists across conversations and is described as building "institutional knowledge" over time (docs).
Prime Agent's /refine "reviews the current trajectory and can apply small, evidence-backed updates to supplemental harness state. It never rewrites the immutable base system prompt, and recorded snapshots support rollback" (README). This states almost exactly the two guarantees Revolve makes for its own /refine — an immutable base prompt, a recorded and reversible snapshot — in close to the same words. Worth flagging as convergent design rather than either side copying the other: neither project's material read for this report is dated precisely enough to say which shipped the idea first, so that question is not established.
DeepSeek Harness's own AGENTS.md package listing names a self-modification/ package ("the agent inspects/mounts its own plugins"), but no directory by that name exists under packages/ at the pinned commit — most likely renamed or folded elsewhere since that listing was written. What the current mechanism actually is, is not established here rather than guessed at; the architecture doc's capability table names no obvious successor.
Revolve's /refine snapshots memory before the model is even asked, and drops the snapshot again if nothing changed, so "an entry exists exactly when memory changed" (core/src/refine.rs, snapshot-then-ask ordering around :128-143). A rewrite whose target file changed underneath it — another session, another worktree, writing the same memory directory in the meantime — is dropped by name with the newer text kept, rather than silently overwritten: apply() compares the content each file had when the refinement was planned against what it holds now and skips the mismatch (core/src/refine.rs:63-82, the compare at :72). /refine undo restores the newest entry not already marked undone, so a second undo walks one refinement further back rather than undoing itself (core/src/refine.rs:277-285). --bare starts a session from the compiled defaults — no memory, no skills, no prompt templates, no CLAUDE.md (core/src/cli.rs:182) — for the case where the agent's own accumulated instructions are the bug being debugged.
Task Agent has its own memory hierarchy (global/project/task), but it is explicitly the dispatcher's working state — todo, goals, inbox, cancel signals — and its own documentation is careful to say it is not the same thing as "revolve's labeled memory specs" (task-agent/CLAUDE.md, "The revolve dependency"). No self-refinement of its own prompt was found documented.
Prime Agent names three scheduling surfaces and is explicit that they are not the same thing: /heartbeat (user-owned, one visible recurring instruction), rlm_heartbeat (agent-owned, several internal ones it manages itself), and prime-agent schedule (general-purpose, one-time or cron, targeted at any addressable agent) — laid out as a table with an explicit "Owner" column (docs/long-running-agents.md). A /goal tracks token usage, elapsed time, continuation count and an optional explicit budget, and keeps prompting after ordinary turns until goal.complete() — model-callable, but the harness never infers completion on its own. "Autonomous mode" is a distinct policy layered on top — bounded continuations plus gate commands that must pass — and the documentation states the two are "complementary but different."
Revolve draws the same three-way line independently: a standing goal in goal.cfg that only the user's /goal done can close (the model may only propose progress through the goal_progress tool); a heartbeat clock consulted only between turns, so it can never stack a second turn on one already running; and revolve schedule for the case where no process is even running to have a clock. Autonomous-mode quality gates run through Files::run_status, on the stated principle that "a gate that cannot fail is not a gate" (this repo's own CLAUDE.md, autonomous.rs section). The tri-partite split — goal vs. heartbeat vs. cron-level schedule — turns up independently in both codebases; this reads as convergent design given the shared problem shape, not as one borrowing from the other.
Claude Code lists "Goals" and "Run prompts on a schedule" as separate pages under its documentation's Automation section, alongside "Push external events to Claude" (channels). Not established beyond the section names captured while researching cross-session messaging — the pages themselves were not fetched for this report.
DeepSeek Harness's architecture table names ctx.goals as "Manage a same-session objective... continue through agent/*" (docs/architecture.md, "Where new behavior goes"). Not investigated beyond that one row; not established whether it has an equivalent to a heartbeat or an autonomous-mode gate.
Task Agent needs none of this machinery, because the dispatcher's entire purpose already is periodic evaluation: "Recurring tasks... are permanent loops" is its heartbeat, expressed through the exact same .tasks/ wait/recurring header convention Revolve reads and writes by hand.
All five have some notion of work that outlives one interactive window, though at very different depths of documentation here.
| Harness | Mechanism |
|---|---|
| Claude Code | A claude -p session binds an inbox socket the same as an interactive one and appears in the agent listing; a --bare-mode session does not bind one and is unreachable by messaging (docs, "Non-interactive sessions"). Background agents and agent view are named elsewhere in the docs but not investigated in depth here. |
| Prime Agent | "Daemon-backed" resident worker processes own the session, its IPython kernel, schedules and descendants independently of any attached client; prime-agent attach <agent>/list/stop manage them (docs/long-running-agents.md). |
| DeepSeek Harness | An ACP ("Agent Client Protocol") automation-only server package exists (packages/acp/); depth of its detach/reattach story is not established from the material read. |
| Task Agent | No daemon concept needed — the dispatcher's own event loop is already always-on by design, and workers are deliberately transient. |
| Revolve | One process per session; revolve detach/attach/agents/stop manage them over unix sockets under $XDG_RUNTIME_DIR (this repo's own CLAUDE.md, "Architecture"). A single daemon hosting every session was considered and refused, on the grounds that one crash would then take every session down together and the sandbox is per-process by construction. |
AGENTS.md no longer exists under that name at the pinned commit) is unresolved.