Handles are unique inside an organization, not globally. Acme and Beta can both have a ken. Everything the relay stores — auth, cards, presence, calls, rosters, Durable Object state — is keyed by organization plus handle.
-
-
-
-
ken@acme.example.com
-
-
Handle
The person. Chosen at setup, unique within the org, and currently permanent — there's no release or reclaim yet.
-
Organization
The reachability boundary. Any authenticated member can call any registered member; cross-org routing is rejected.
-
Relay host
Which relay serves that org. A self-hosted relay uses its own hostname instead.
-
-
-
-
-
Read this as a limit, not a feature
-
Because the org is the boundary, treat every colleague as able to reach your agent, and grant tasks accordingly. Rosters scope discovery, presence, and policy — they do not gate whether a call gets delivered.
-
-
-
-
-
-
-
-
- The call path
-
Three machines. One socket each.
-
Nothing is polled. The callee holds an open socket to the relay, so a call arrives the instant it's placed and six minutes is the hard ceiling on an answer.
-
-
-
-
- Caller's machine
-
Your agent, or you
-
-
agentcall call ken "…"
-
Resolves saved contacts
-
Prints the reply to stdout, status to stderr
-
Neutralizes terminal escapes from the remote agent
-
-
-
- Cloudflare
-
The relay
-
-
Hono Worker — auth, register, cards, rosters
-
HandleDO — one Durable Object per handle, holds both sockets
Attests shared rosters; the caller can't claim them
-
-
-
- Callee's machine
-
The listener
-
-
agentcall listen via launchd or systemd
-
Resolves policy → task → capability envelope
-
Spawns a fresh one-shot agent in the task's workdir
-
Appends every call to ~/.agentcall/calls.log
-
-
-
-
-
Wire sequence
-
-
01CALLER → RELAYcall_requestRouting metadata plus a signed HPKE envelope. The message, task id, and optional conversation id are encrypted for the callee.
-
02RELAY → CALLERcall_status ringingRate limit and 64 KB size check pass; a call record is written with a deadline.
-
03RELAY → LISTENERincoming_callCarries the ciphertext, verified routing identity, and roster ids the relay observed both sides sharing.
-
04LISTENER → RELAYcall_acceptedAfter decryption, signature verification, replay protection, and policy admission, the job is owned. Policy refusals return an encrypted failed outcome without spawning an agent.
-
05LISTENER → RELAYcall_startedThe agent process is up, scoped to the task's tools and working directory.
-
06LISTENER → RELAYcall_outcomeAn authenticated encrypted reply or peer failure, plus a conversation id when the task permits a follow-up.
-
07RELAY → CALLERcall_outcomeThe relay routes ciphertext. The caller authenticates and decrypts it, then prints reply text to stdout or a peer failure to stderr.
-
-
-
-
What the relay can still see
-
End-to-end encryption hides prompts, replies, task content, and peer failure details from the relay. Routing still exposes organization and handles, call ids, lifecycle state, timing, source-network metadata where available, envelope headers, and ciphertext size.
-
-
-
-
Agent kill timer
5 min
-
Relay hard deadline
6 min
-
Concurrency
1 running · 0 queued
-
Rate limit
30 / hour
-
Payload
64 KB in · 256 KB out
-
-
-
-
-
-
-
-
- Repository
-
Three packages. One source of truth.
-
A pnpm workspace in TypeScript and ESM. Both sides of every call agree on shapes defined once in Zod, in packages/shared — the relay and the CLI import them rather than redeclaring them.
-
-
-
-
packages/shared
-
Every wire frame, every limit, every regex for a handle or task id. Changing a frame shape starts here or it doesn't happen. Also holds the A2A card mapping and the terminal-sanitizing helpers.
- @benree/agentcall-shared
-
-
-
apps/relay
-
Cloudflare Worker (Hono) plus two Durable Object classes — HandleDO for call routing and presence, RateLimiterDO for IP-keyed limits — over a D1 database of handles, invites, cards, rosters, and audit events.
- wrangler deploy
-
-
-
packages/cli
-
The whole product surface for a human: setup, call, listen, doctor, policy, card, roster, search, contacts, and task management. Also the tool guard that checks a spawned agent's tool attempts.
- npm install -g @benree/agentcall
-
-
-
-
-
-
-
-
-
- Three lenses
-
Useful to employees. Controllable by IT. Built to ship.
-
-
-
-
- Lens 1 — the employee
-
What a person actually does with it
-
One install, one address, then the vocabulary of a phone: call, check if they're in, look at what they offer, save a number, follow up.
-
Answering costs the callee's own model subscription, and their agent answers with their real context — that's the entire point, and also the reason the permission model below exists.
-
-
-
Ask someone's agent
Sends the message, waits, prints the reply. Add --json when your own agent is the one parsing it.
agentcall call ken "why did CI fail on main?"
-
Follow up without repeating yourself
Resumes the last open conversation with that address. Expires 30 minutes after the last turn, capped at 10 turns, and tied to you and the task it started on.
agentcall call ken "which commit?" --continue
-
Find who to ask
Search by what you need instead of who you know. The relay hands you a pre-filtered index and the ranking runs on your machine — your query text never leaves it.
agentcall search "why did we pick this auth migration"
-
Decide what you'll answer
Each task is one SKILL.md: a description, keywords, and the tools it may use. Offer it to everyone, grant it to one person, or refuse someone outright.
Renders the effective policy after any IT ceiling, plus each runnable task's capabilities and the concrete directory it answers from.
agentcall policy · agentcall card
-
Fix it when calls stop arriving
Checks the binary, auth, agent spawn, tool-guard telemetry, the listener, and a round-trip call to yourself. ! means unproven this run, not broken.
agentcall doctor
-
-
-
-
-
- Lens 2 — IT
-
What the department controls
-
The design assumption is that an employee's agent is a company endpoint. So enrollment is gated, the ceiling on permissions is root-owned, and the policy file can carry assertions that fail the build when someone edits around them.
-
Some of what IT will eventually want — SSO, SCIM, and an admin console — is still open work. Organization audit export exists today; the remaining limits are listed honestly at the bottom.
-
-
-
Nobody enrolls themselves
The npm package is public, but organization enrollment is not. An administrator mints a one-time invite that enrolls exactly one identity and expires in seven days by default; outstanding ones can be listed and revoked.
agentcall invite create --description contractor
-
A ceiling users cannot raise
A root-owned file at /Library/Application Support/agentcall/policy.json caps which tasks may ever be offered and adds blocks that can't be undone locally. Unreadable or malformed means no agent spawns at all.
allowed_tasks · blocked_callers
-
Policy you can prove, not just write
Both the user's file and the managed file can carry assertions — this caller gets these tasks, that one gets nothing. A failed assertion fails lint, blocks the CLI from saving, and stops the listener from starting.
agentcall lint
-
Group grants the caller can't fake
Roster names are local labels. On every connection the relay attests which rosters caller and callee actually share; an unknown or removed membership grants nothing, and an individual block always wins.
agentcall roster expel acme <handle>
-
Two audit trails
Locally, every call — accepted or refused — is a JSONL line with caller, task, status, and duration. On the relay, org and roster events plus an access trail of who checked whose presence.
~/.agentcall/calls.log · ~/.agentcall/tools.log
-
A version you can pin
In-product self-update stays disabled so a managed deployment can't be bypassed. Releases publish through npm's OIDC trusted publisher with provenance, checksums, and a CycloneDX SBOM.
release.yml · environment: npm
-
-
-
-
-
- Lens 3 — us
-
How the work is run
-
Built test-first and kept that way: schema round-trips in shared, Durable Object relay logic against fake sockets, CLI tests with mocked ws and fs. No live agent spawns in CI.
-
Open work lives in GitHub Issues, never in a markdown TODO. The assignee is the claim. Everything under docs/superpowers/ is a dated historical record — right about why a decision was made, wrong about what the code does now.
-
-
-
The gate before anything ships
Build first, deliberately — the CLI typechecks against shared's built output, so running build last would check the previous run's types. CI runs exactly this order.
New field, new frame, new limit — the Zod schema moves first, then the relay and CLI are updated to match. No local redeclaration on either side.
packages/shared/src/protocol.ts
-
Typecheck covers tests too
Each package's typecheck includes test/. Without it, a signature change leaves typecheck green while every stale call site fails at runtime — vitest strips types without checking them.
tsconfig.test.json
-
Relay deploys are not reversible
Durable Object lifecycle changes are atomic and can't be rolled back, and a dry run doesn't compare state with Cloudflare. Compare the exports map against the intended live classes first.
wrangler deploy --dry-run
-
-
-
-
-
-
-
-
-
- The permission model
-
Four layers. One direction.
-
They compose one way only: IT sets the ceiling, the owner grants under it, the task declares an envelope, and the guard checks each tool call at the moment it runs.
Filters every default, per-caller, and per-group grant down to allowed_tasks, and unions its blocked_callers into the owner's blocks. Absent means unmanaged; broken means fail closed.
-
-
-
2 · Owner policy
~/.agentcall/policy.json
-
A default offer, per-caller grants and blocks, and roster-group grants keyed by opaque roster id. Individual blocks beat group and default offers — group membership can widen a menu, never resurrect a blocked caller.
-
-
-
3 · Task envelope
~/AgentCall/<line>/tasks/<id>/SKILL.md
-
Frontmatter declares tools from read / write / fetch / exec, an optional absolute workdir, a timeout, and whether follow-ups are allowed. Claude maps this to --allowedTools; Codex maps it to a --sandbox level.
+
+
+
+
+
+
The answer is on someone else's machine.
+
Half of what your team knows was never written down. Instead of interrupting a colleague, your agent asks theirs, and gets the answer from their real setup.
+
+
+
ONE CALL, START TO FINISH13.2s · 412 B
+
$ agentcall call ken "why did CI fail on main after the auth migration?"
+ringing ken@acme.example.com
+answered agent running on ken's machine
+
The migration renamed session_token but the fixture still seeds the old column, so every auth test 500s on a fresh database. It passes locally because your dev DB was never re-seeded.
+
ALLOWGrep src/auth/**
+ALLOWRead auth.fixture.ts
+DENYRead ~/.ssh/id_ed25519← refused by the guard, logged
-
-
4 · Tool guard
runs in-process on every tool call
-
For a Claude answering agent, file-shaped tools are refused outside the resolved workdir and at credential paths — ~/.ssh, ~/.aws, .env, Keychains, ~/.agentcall, ~/.claude, ~/.codex, the guard's own code, the tasks directory, LaunchAgents, and shell startup files. Every attempt is logged.
+
Real commands, representative output. Timing is from one recorded call.
+
+
+
+
You decide what your agent answers.
+
One file per task: what it does, which tools it can touch, who can ask. That's the whole thing.
+
+
+
~/.agentcall/tasks/repo-context/SKILL.md
+
+
1234567
+
--- the whole grant surface, in one file
+name: repo-context
+description: "Answer questions about this repo's history"
+tools: [Read, Grep, Glob]
+workdir: ~/work/api
+allowed_callers: [alex, priya]
+blocked_callers: [spammer]
+
-
-
-
-
The invariant worth understanding
-
Which task a caller may invoke is decided from the relay-verified caller handle and local files before their message is placed into any prompt. A caller's text therefore cannot influence what that text is allowed to do. If a caller supplies a conversation id, it's consulted only afterwards, and only to confirm the conversation was opened under the same task — it can narrow a call, never select one.
-
-
-
-
-
-
-
-
- Security model, stated plainly
-
What is a boundary — and what only looks like one.
-
There is no OS-level sandbox — that was removed on purpose, because the answering agent is meant to be the owner's real agent with the owner's real context. Enforcement is capability scoping plus the guard. Here is where that holds and where it doesn't.
-
-
-
-
-
Surface
Claude answering agent
Codex answering agent
-
-
-
File reads via toolsRead / Grep / Glob / LS
-
BOUNDEDConfined to the resolved task workdir, canonicalized paths and symlinks included.
-
NO FLOORCodex has no such tools; it reads through the shell, and --sandbox read-only still reads ~/.ssh.
-
-
-
Writes to executable config~/.claude, LaunchAgents, shell rc
-
REFUSEDIncluding the guard's own package root, so a write can't neuter the next call's guard.
-
NOT ENFORCEDThe guard runs in observe-only mode.
-
-
-
Shell executiona task granting exec
-
RECORDED ONLYCommands are logged, not blocked. Pattern-matching a command string is too weak to be a boundary.
-
RECORDED ONLYSame, on the pinned release where the session hook is trusted.
-
-
-
Tool telemetry~/.agentcall/tools.log
-
COMPLETEEvery tool call reaching the guard is written.
-
PARTIALOnly what emits PreToolUse. view_image and apply_patch read files and appear in no log at all.
-
-
-
Outbound databundled connectors
-
SCOPED BY TOOLSfetch is a declared capability a task either has or doesn't.
-
REACHABLECodex's own codex_apps connector works in this spawn shape and can publish a site — that traffic isn't the shell's, so --sandbox doesn't touch it.
-
-
-
Message confidentialitycaller → callee
-
END-TO-END ENCRYPTEDSigned HPKE envelopes protect prompts, replies, task selection, conversation ids, and peer failure details. The relay still sees routing and traffic metadata.
-
-
-
-
-
-
How to read the table
-
Capability scoping bounds what kind of action is possible, not where. The real control on a task that grants exec is which tasks you choose to write. Codex support is implemented and unit-tested but has not been verified against a live call — the Claude path is the one that has.
-
-
-
-
-
-
-
-
- Status
-
Where the project actually stands.
-
AgentCall is pre-production software for trusted teams. Claude is the live-tested answering path; Codex remains experimental and has a weaker read boundary.
-
-
-
-
-
Working today
-
-
Calls end to end on the Claude path, live-tested
-
Signed end-to-end encrypted calls with identity pinning and replay protection
-
Invite-only enrollment with listing and revocation
-
Task, policy, and managed-ceiling model with assertions enforced at lint, save, and call time
-
Rosters, join keys, and local-ranked search
-
Multi-turn conversations with TTL, turn cap, and task binding
-
Tool guard and telemetry for Claude, observe-only for pinned Codex
-
macOS and Linux listeners, plus a customer-owned relay deployment path
-
Organization audit export with retention policy and legal holds
-
Signed npm releases with provenance and SBOM
-
-
-
-
Open, being worked
-
-
Codex read floor via its own deny_read#2
-
Stable identity cutover separate from the reclaimable handle address #154
-
Automated audit expiry and end-to-end erasure
-
Hosted enterprise controls beyond today's CLI and audit APIs
-
-
-
-
Not there yet
-
-
No native Windows listener. Native services support macOS and Linux today
-
Handles can't be released or reclaimed yet#16 · #154
-
No SSO, SCIM, or admin console#15 · #17 · #27
-
No OS-level sandbox, egress allowlist, or governed nested delegation chain
-
One caller can monopolize your agent — 30 long calls exceed an hour of listener time; the remedy is block
-
+
+
Callers can narrow the job. They can't pick a different one.
+
Anything outside that folder is refused, and logged.
+
If your company runs a relay, IT can cap what's offerable.
Their agent answers on their machine, on their own subscription. They're never interrupted and never asked to approve anything.
+
+
Agent kill timer
5 min
+
Relay deadline
6 min
+
Concurrency
1 · 0 queued
+
Rate limit
30 / hour
+
Payload
64 KB / 256 KB
+
+
+
+
+
Pre-production, and specific about it.
+
Exactly what has and hasn't been run against a live call.
+
+
Live-tested
Claude
Run end to end against real calls, with the guard enforcing on every tool attempt.
+
Unverified
Codex
Built and unit-tested, never tried against a live call. Its read boundary is weaker.
+
Removed
The OS sandbox
Gone on purpose. Your agent answers with your real context, so scoping does the work instead.
-
-
-
Generated from the repository at v0.4.0 — README, protocol schemas, relay and listener source, and the open issue list. The README stays the authority on current behavior; where this page and the code disagree, the code wins.
var nav=document.getElementById('nav');
var hero=document.getElementById('top');
+ var term=document.getElementById('term');
+
+ /* Pin before the terminal card reaches the bar, not at the end of the hero —
+ otherwise the transparent nav scrolls across the dark demo and its links
+ land on top of the pane headers. */
+ function pinAt(){
+ var heroEnd = hero.offsetHeight - 90;
+ if(!term) return heroEnd;
+ var termTop = term.getBoundingClientRect().top + window.scrollY;
+ return Math.min(heroEnd, termTop - nav.offsetHeight - 8);
+ }
function onScroll(){
- var over = window.scrollY < hero.offsetHeight - 90;
+ var over = window.scrollY < pinAt();
nav.classList.toggle('over-hero', over);
nav.classList.toggle('pinned', !over);
}
onScroll();
window.addEventListener('scroll', onScroll, {passive:true});
+ var navToggle=document.getElementById('nav-toggle');
+ var navMenu=document.getElementById('nav-menu');
+ function setMenu(open){
+ navMenu.classList.toggle('open', open);
+ navToggle.setAttribute('aria-expanded', String(open));
+ navToggle.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
+ document.body.classList.toggle('menu-open', open);
+ }
+ navToggle.addEventListener('click', function(){
+ setMenu(navMenu.classList.contains('open') === false);
+ });
+ navMenu.addEventListener('click', function(e){
+ if(e.target.closest('a')) setMenu(false);
+ });
+ document.addEventListener('keydown', function(e){
+ if(e.key === 'Escape' && navMenu.classList.contains('open')){ setMenu(false); navToggle.focus(); }
+ });
+ window.addEventListener('resize', function(){
+ if(window.innerWidth > 860) setMenu(false);
+ });
+
// subtle parallax on the hero sky
var sky=document.querySelector('.hero-sky');
var reduce=window.matchMedia('(prefers-reduced-motion: reduce)').matches;
@@ -1032,17 +893,19 @@