Yapitalism

io.github.AytuncYildizli由 GitHub 账号 AytuncYildizli 发布v0.6.0更新于 Oct 24, 2026

Drive terminal coding agents by voice, with receipts that never claim more than they proved.

已验证STDIO仅桌面效率与工作流开发者工具

概览

AI 生成的概览

本地 MCP 服务器,让语音客户端驱动终端里的编码代理(codex、claude、kimi、opencode),并返回能证明什么的回执。

可以做什么
  • 列出、读取并命名终端窗格,包括已配置对等机器上的窗格
  • 向已确认的代理窗格发送文本,并得到 GREEN、YELLOW 或 RED 的投递判定
  • 等待窗格直到代理回合结束,或一次调用完成发送加等待,报告 ended、waiting_input、agent_error、exited 或 running
  • 创建、恢复和清空代理窗格,远程创建由单独开关控制
  • 通过命令行运行 doctor/setup 检查并查看回执账本
适用场景
适合你不在屏幕前、想用语音指示终端里的编码代理,并且需要语音回复区分“已证实投递”与“未证实完成”的场景。若你就在桌前看着终端,则没有必要。
运行要求
在 macOS 或 Linux 上作为本地进程运行,需要 Python 3.11+ 和 tmux(可用 pipx、uv 或 pip 安装)。HTTP 传输只绑定回环地址,首次启动会生成 bearer token,可用 YAPITALISM_MCP_TOKEN 覆盖;stdio 模式无需 token。可选的 Superset 后端需要由 setup 命令写入的 0600 清单文件。默认端口 8792,可用 YAPITALISM_MCP_PORT 更改。
安装前请注意
该服务器能读取你能看到的所有终端,并向代理窗格输入内容,因此应以你的用户身份运行,切勿用 root。HTTP 写入默认关闭,需用授权命令开启;只读标志可在所有传输上关闭写入。对等机器上的远程窗格创建是单独的选项。bearer token 存放在仓库外的清单文件中,应保持 0600 权限。GREEN 只证明投递与确认,不证明工作正确。目前为公开 pre-alpha。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Yapitalism,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

Yapitalism

Drive terminal coding agents by voice, and never let the answer claim more than it proved.

You are away from your desk. You speak; an agent in a terminal does the work; you get a spoken reply. That reply is the only thing you have — you cannot see the screen. So the worst failure is not a crash, it is the voice saying "done" while your text sits unread in a prompt box.

Yapitalism is a local MCP server that lets a voice client reach your coding agents, plus a receipt layer that decides what the voice is allowed to say. If you are at your desk, use your screen — this exists for the hours you are not.

Public pre-alpha. Local-only. No production deployment, no external messaging, and no automation of closed-source clients.

How it fits together

MCP client (Codex · Hermes · Claude Code · Claude Desktop · Cursor)    │  MCP over loopback — no public endpoint, no OAuth, no relay    ▼yapitalism MCP server          panes_list · pane_read · pane_send    │                          pane_await · pane_task    │                          panes_name · panes_unname    │                          panes_create · panes_resume · pane_clear    ├── superset backend       local host-service over tRPC (127.0.0.1:48900)    └── tmux backend           capture-pane / send-keys

The agents it addresses: codex, claude, kimi, opencode — recognised by measurement (a process tree on tmux, the host's own agent registry on Superset), never by a caller's claim. A pane running anything else is refused, because typing into a shell and pressing Enter is running a command.

The voice client never reaches your machine directly: it drives a local agent session, and that session talks to this server over 127.0.0.1. Nothing is exposed to the network.

The part that matters: receipts

pane_send returns a verdict, not a shrug.

meaning
GREENthe agent echoed a one-time marker. It demonstrably received and processed the text — which is not a claim the work it then did is correct; no receipt can promise that, and this one does not try.
YELLOWthe write landed; processing was not proven. Never round this up.
REDthe backend refused the write. Nothing reached the terminal.

YELLOW is the whole point. Text left unsubmitted in an agent's input box looks identical to work in progress from outside — same spinner, same scrolling output, same HTTP 200. A voice that rounds that up to "done" costs you hours before you notice.

Explicitly not acceptance: an HTTP 2xx, a PTY write returning, terminal output changing, a revision advancing, or the prompt echoing your own words back.

Waiting is not the same as failing

A fixed deadline reports on the clock, not on the agent. An agent that thinks for a minute and then answers correctly was verified all along, and calling that YELLOW teaches an operator to ignore YELLOW. So the wait is an idle timeout: it restarts whenever the pane changes, bounded by a hard ceiling.

Pane movement decides only whether to keep waiting. It is never evidence of acceptance — that stays the canary alone. A YELLOW therefore says which kind it is:

  • canary_timeout_pane_moving — the pane's text was still changing. Named after what was measured: a spinner, a clock, a log tail or a second agent sharing the pane all produce this without the intended agent doing anything. It is a hint that looking again may be worth it, never a claim that the agent is working.
  • canary_timeout_pane_still — nothing moved at all.

What remains irreducible: if an agent silently ignores the text and prints nothing, no mechanism here can distinguish that from an agent that never received it. Verification needs the agent to emit something.

The second receipt: how the turn ended

Delivery is table stakes; the expensive failure is an agent that has been sitting on a yes/no question for forty minutes while you thought it was working. pane_await watches a pane until the agent's turn ends and says how; pane_task is send-then-await in one call — instruct, walk away, come back to one of:

turnmeaning
endedprompt idle, screen stopped changing. The turn is over — spoken with "not that the work is correct" attached, never as "done".
waiting_inputa blocking dialog owns the screen (trust / login / confirmation). You are the blocker, and the payload's tail carries the question so it can be quoted.
agent_errora known failure line owns the screen — Please run /login, a 401, a rate limit — named, not timed out.
exitedthe pane no longer runs an agent.
runningstill changing when time ran out. Unproven; offers to look.

An ended verdict re-verifies the agent process is still alive before it is claimed: the frozen prompt of a dead pane must not be celebrated as a finished turn.

Who may write, by transport

The bearer token answers who is calling; it never answered what they may do. Since 0.5.0: stdio clients write, always — the OS made that trust decision when it let the client spawn the process. Writes over HTTP are off by default and every refusal names the fix: yapitalism authority allow-http-writes, once, on that machine (yapitalism setup asks the same question while printing the HTTP registration lines). Reading, watching and doctor work on every transport regardless, and yapitalism-mcp --read-only turns writes off everywhere — install the watcher first, allow writes when it has earned them. Every write result carries its origin.

On tmux, the send path also pins the identity of the admitted agent (pid + start time) and re-verifies it immediately before Enter. An agent that exits into a shell between typing and submit gets the text staged but never submitted — typing is recoverable, Enter into a shell is command execution.

Where the checks happen

Most of the time this does not change what you do, and it is not read out loud. It is in the payload and in yapitalism setup, for when something goes wrong and you want to know what was actually checked before the write.

meaning
hostthe host refuses the write itself — the check and the write are one operation
clientthis process checks, then writes; real against what it covers, not atomic
nonenothing checks
json
{ "status": "GREEN",  "client_guarantees": ["idempotent_dispatch", "empty_prompt_check"],  "missing_guarantees": ["optimistic_revision"] }

yapitalism setup prints the table for your machine. Today both backends land in client for most of it: the checks happen here, a moment before the write, rather than inside the host atomically with it. tmux cannot do the revision check at all.

No shipped Superset enforces these itself, and this tool used to claim it did. It concluded "guarded" from the fact that a procedure named terminal.send was routed. The terminal.send Superset actually ships takes {terminalId, workspaceId, text, submit} and guards nothing. The levels are now read from what the host's own validator requires — the difference between a name and a contract.

Both backends reach GREEN, and GREEN means the same thing either way: the agent emitted the one-time marker, so it received the text and processed it. The table is for diagnosing a machine, not something to reason about on every send.

Reviewing this repo

Whether you are a person or someone's agent asked "should I install this," the claims worth checking and where each one lives — verify, do not take our word:

  • Authority and its boundaries, including the shared-machine exposure and the fail-closed bearer gate: SECURITY.md.
  • What a caller can never do (closed launcher tables, non-agent refusal, occupied-prompt refusal, per-pane serialisation): src/yapitalism/mcp/tmux.py and src/yapitalism/mcp/backends/, each guard with a test named after the failure it prevents.
  • What GREEN claims and does not claim: the receipts section above, and tests/test_canary.py for why an echo cannot satisfy it.
  • This project's own past overclaims, retracted by name: CHANGELOG.md, 0.2.0 through 0.2.4.

Machines are panes too

A peer is another machine's yapitalism server, mounted under its own namespace: the codex pane tmux:%0 on the machine studio is studio:tmux:%0 here, in the same panes_list, addressable by the same pane_send. Receipts pass through verbatim — the peer proved them next to its own terminals, and this side only gives the ids their machine name back.

bash
# on the peer (reachable over your tailnet):YAPITALISM_MCP_HOST=100.x.y.z yapitalism-mcp   # tailnet bind requires the bearer gate
# on this machine:yapitalism peers add studio http://100.x.y.z:8792/mcp --token-file <peer's token>

peers add verifies the peer answers as a yapitalism server before writing anything. A peer on a public address is refused unless allow_public is said explicitly; the tailnet is the transport, not the trust story — the peer's bearer token still decides who may call. Configured peers show up in yapitalism doctor with live pane counts.

Agents can be started and resumed on a peer too: panes_create and panes_resume take a machine argument naming one. The peer runs its own full gate chain — and starting an agent remotely is more authority than typing into one, so it has its own switch, off by default: yapitalism authority allow-remote-create, once, on the peer. To keep a peer's server alive across reboots, yapitalism service --host <its tailnet IP> prints a filled launchd plist or systemd unit; installing it stays your decision.

Say names, not ids

Voice cannot say mbp3:tmux:%2. Bind a word to a pane once —

panes_name("billing", "tmux:%4")

— and every tool accepts billing from then on. The binding records what the pane runs at name-time and is re-verified against a live listing on every use: tmux reuses pane ids, so a name whose pane vanished or changed runtime is refused with the reason, never silently retargeted. No fuzzy matching — "probably the right codex" is not a target. Names appear on their panes in panes_list.

Driving it well

The bridge agent behaves best with standing instructions — speak receipts, never resend a YELLOW, name panes like a person. The exact prompt we run is in docs/operator-prompt.md; paste it into your voice client's custom instructions.

Recipes

The six tools compose into watchers worth having: a permission watch (nothing waits on a dialog silently — its first real catch was an agent silently holding a wallet-transaction approval), a provider-outage watch, an end-of-day digest. Working prompts in docs/recipes.md.

Install

Needs Python 3.11+ and tmux, on macOS or Linux — the full send-and-prove loop is exercised on a bare python:3.11-slim container per release, not just assumed portable. Everything runs on your machine; nothing is exposed to the network.

bash
# 1. installpipx install yapitalism        # or: uv tool install yapitalism / pip install yapitalism
# 2. start the server (loopback only — it refuses to bind anything else)yapitalism-mcp
# 3. point your voice client's agent at it, in another shellcodex mcp add yapitalism --url http://127.0.0.1:8792/mcp
# 4. ask the tool what your machine can actually doyapitalism setup

setup interviews the machine rather than printing "installed successfully": which backends are usable, which agent CLIs are on PATH, whether a Superset host is live and which build it is, and the guarantee table above filled in for you. It writes nothing except, if you say yes, the Superset manifest — and it names what is still missing with the exact command for each.

Then talk to the voice app: "list my panes", then "send this to the Codex pane". Any MCP client works the same way — Hermes, Claude Code, Claude Desktop and Cursor registrations are printed by yapitalism setup for exactly the machine it is run on.

YAPITALISM_MCP_PORT moves the port if 8792 is taken. YAPITALISM_TMUX_SOCKET targets a non-default tmux server. The HTTP transport requires a bearer token by default — loopback is not a user boundary, so the server mints one on first start and yapitalism setup prints the registration lines that carry it. YAPITALISM_MCP_TOKEN overrides it; YAPITALISM_MCP_INSECURE=1 opts out. stdio needs no token: the OS already decided who may talk to a spawned process.

Clients that launch the server themselves

Codex takes a URL. Claude Desktop, Cursor and most other MCP clients instead spawn the process and speak over stdin/stdout, so point them at --stdio and do not run a separate server:

json
{  "mcpServers": {    "yapitalism": {      "command": "yapitalism-mcp",      "args": ["--stdio"]    }  }}

Use the absolute path from command -v yapitalism-mcp if the client does not inherit your PATH — GUI apps on macOS usually do not. YAPITALISM_MCP_TRANSPORT=stdio does the same as the flag, for clients that only let you set the environment.

In stdio mode nothing but protocol may reach stdout, so the server suppresses its own startup banner. If you wrap it in a shell script, keep that script silent too.

Keeping it running

The voice route dies when the server does, so run it under your init system rather than a terminal. Both units run as your user, never root: the server can read every terminal you can see.

  • macOS — .agents/launchd/. The plist is a template; its README has a sed line that fills in the real binary path, because launchd searches neither PATH nor ~.
  • Linux — .agents/systemd/, a --user unit. systemctl --user enable --now yapitalism-mcp.

Kill any shell instance first either way, or the two race for port 8792 and which one wins is down to timing.

Superset terminals (optional)

The tmux backend needs nothing. Superset needs a 0600 manifest holding the host endpoint and token — and you do not write it by hand, because Superset already writes what it needs:

bash
yapitalism superset setup            # shows what it found and would doyapitalism superset setup --confirm  # writes it

That reads the app's own ~/.superset/host/<organizationId>/manifest.json, proves the token against the live host, picks a default terminal and writes ~/.cache/superset-watch-voice/yapitalism-manifest.json (or $YAPITALISM_SUPERSET_MANIFEST). It refuses rather than guesses: a source file looser than 0600, a manifest naming a dead process, or two live organizations with no way to choose all stop it. The token is never printed.

Without a manifest, panes_list still returns your tmux panes and reports Superset in errors — a backend that could not be reached is never silently reported as "no terminals".

Teaching the voice how to speak the receipts

.agents/skills/superset-operator/ holds the policy that stops a model rounding YELLOW up to "done", plus a drift check against the copy your agent actually loads.

The receipt core, on its own

The five-leg model is usable without the MCP server:

  1. capture — the intent was captured
  2. dispatch — a concrete target received a write attempt
  3. accept — the target proved acceptance by canary or explicit acknowledgement
  4. work — material agent progress was observed
  5. deliver — a final update reached the user

Evidence carries provenance — api, terminal_diff, ui_observation, user_report, inferred — and inferred may never mark a leg succeeded.

The incident this project came from: the audio session stayed alive, spoken progress stopped, two 180-second watchers saw the terminal frozen at revision 920118, and the canary never arrived — while the voice kept implying work. The product is not "fix the voice client". It is never fake GREEN.

CLI surface: doctor, receipt show, ledger verify|manifest|migrate, and superset status|send. A confirmed superset send requires reusing the exact client-token a dry run emitted, snapshots immediately before dispatch, rejects a changed revision, and never retries an ambiguous POST.

Known limits

Stated plainly, because a receipt system that overclaims is worse than none:

  • Tail truncation is undetectable. Deleting the last ledger rows leaves a prefix that still verifies. Catching it needs an anchor outside the file — see docs/adr/0004.
  • The ledger attests to itself. Hash verification proves rows were not edited or reordered; it does not prove who wrote them, and a wholly fabricated ledger verifies fine.
  • No backend can offer tmux an optimistic-revision guard. It needs an expectation from the caller — "write only if the pane still looks as it did when I read it" — and pane_send takes no expected revision. Reading the pane twice and refusing if it moved would be a different guarantee wearing that name, so it is reported none rather than approximated.
  • The empty-prompt check outside a guarded host is a heuristic. It reads the rendered screen, so a false "empty" would append to somebody's half-typed text and submit the merge. It therefore refuses on anything short of a confident empty, including screens it cannot read, and pane_clear is the way through. Validated against real panes, not a large sample.
  • The Superset it is measured against is the shipped, stock build — the one whose terminal.send guards nothing. Proven sends (canary observed, GREEN) have been completed against that live host repeatedly since 0.2.4, driven both directly and through the full MCP surface. The guarded-host branch is the one exercised only against recorded shapes, because no shipped build carries those guards to test against.
  • A guarded Superset host over-refuses Codex placeholder text, counting the agent's own suggestion line as staged input, so sends to such panes are refused and clearing cannot help. The receipt says exactly that instead of advising a clear. The fix belongs in the host.
  • A tmux pane's runtime can go stale between the check and the write. Superset's comes from the host's own registry, which is why its gate can run before the write rather than after it.
  • A canary proves less than "it worked". It proves the text reached something that then echoed a one-time marker — delivery and acknowledgement. It does not prove the agent did the work, did only the work, or that the pane was the one you meant. GREEN is a delivery receipt, not a work receipt.
  • Concurrent sends to one pane are serialized, not queued. The second waits for the first; it is not held, ordered, or retried. A voice client that fires faster than a terminal accepts will block its own turn rather than interleave.
  • Connector-in-voice behaviour in closed clients is undocumented and can change without notice.

Repository map

text
src/yapitalism/mcp/       MCP server, backend registry, receipts, tmux driversrc/yapitalism/adapters/  Superset host-service clientsrc/yapitalism/           receipt core: model, canary, claims, ledger, CLItests/                    deterministic unit, replay, and real-tmux testsfixtures/                 scrubbed incident replays.agents/skills/           voice policy + drift check.agents/launchd/          run the server as a login agentdocs/adr/                 load-bearing decisionsdocs/architecture.md      component boundaries and evidence model

Explicit non-goals

  • reverse-engineering or patching closed voice-client internals;
  • inventing background-turn or push APIs that do not exist;
  • multi-tenant SaaS;
  • automatic email, DM, or messaging delivery;
  • treating terminal revision movement as command acceptance.

Credits

The receipt-integrity core was written by @liri-ha, whose commits are carried here unrewritten. See CONTRIBUTORS.md.

Security

Raw transcripts, terminal text, and credentials do not belong in this repository. Ledgers store bounded metadata, hashes, and classifications. A manifest holding a bearer token lives outside the repo at mode 0600 and is read only when its path is passed explicitly; it is never printed. See SECURITY.md.

来源:README.md,提交 4fd0738

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.6.0最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分