Faku
September 1, 2026 · View on GitHub
Faku = fx + Waku.
A Native SDK Zig desktop for local coding agents. First-party Vercel fx. Waku-protocol compatible. See NOTICE and LICENSE.
Faku is the window; waku-daemon can stay the brain. Chrome is a chromeless 48px header (measured sidebar, composer send circle). It is fx-first: Native SDK markup plus a Zig Model / Msg / update loop.
Egoist asked for a Zig GPUI slopfork so Waku compiles faster and uses less disk: https://x.com/localhost_5173/status/2090464458695192842
Product and architecture context lives in CONTEXT.md. Agent wayfinding is in AGENTS.md and docs/agents/. Zig sources are under src/.
Why fx
Waku does not ship Vercel fx (https://fx.sh, https://github.com/vercel-labs/fx). Faku does, as the default first-class provider.
fx is a minimal Zig coding-agent harness (about 7.8 MiB, Apache-2.0,
model-agnostic via AI_GATEWAY_API_KEY / VERCEL_OIDC_TOKEN /
FX_MODEL). Install lands at ~/.local/bin/fx.
Surfaces: fx interactive, fx ask (headless), fx acp (Agent Client
Protocol over stdio), fx resume.
Live path
When the fx CLI is installed, Send on an fx session spawns a one-shot
faku acp-proxy -- {fx_path} acp. Send on a probed ACP stdio provider
(cursor, PATH cursor-agent; OpenCode, PATH opencode; kimi, PATH
kimi; grok, PATH grok) uses the same sidecar: faku acp-proxy -- {binary} acp for bare-acp ids, or faku acp-proxy -- grok agent stdio. Native
fx.spawn writes one stdin buffer and then closes stdin. This is
not a long-lived ACP or WebSocket loop. Composer image attach on
those probed ACP stdio providers ships a first-cut official ACP v1
image content block on session/prompt (base64 + mimeType; png /
jpeg / jpg / gif / webp; ~256KB raw; missing / unknown type /
overflow fail closed to demo). fx still uses fx ask --image and
does not put image blocks on the fx ACP stdin batch.
The local catalog sessions.json is canonical. Daemon and ACP are
best-effort sidecars: the desktop update loop never holds a socket.
WAKU_DAEMON_ADDRESS still selects the daemon sidecar. Missing fx
still uses the demo timer. Available Claude is one-shot
claude -p --output-format stream-json --verbose --include-partial-messages --forward-subagent-text (not ACP; later
Sends pass documented --resume {fx_session_id} when that field is
non-empty; first Send and Fork omit it; not --continue; documented
image path inside that -p prompt when a composer image is attached;
stdout is NDJSON with live text_delta into the transcript; live
Subagent Background from real parent_tool_use_id plus a bounded
512KB last-window from forwarded parent_tool_use_id text
(newlines kept; CSI stripped for display via a 100ms render
cache; Environment Summary stays a one-line preview); live Monitor
Background from real Claude Monitor tool_use, with a bounded
512KB last-window log from matching user tool_result (newlines
kept; CSI stripped for display via the same 100ms render cache;
Environment Summary stays a one-line preview). Available
Codex is
one-shot codex exec {prompt} (not ACP; documented --image {path}
after the prompt when a composer image is attached). Available Amp is
one-shot amp -x {prompt} (not ACP; documented @{path} in the -x
prompt when a composer image is attached). Available Pi is one-shot
pi --mode json {prompt}
(not ACP, not --mode rpc; documented @{path} after json when a
composer image is attached; stdout is JSON events with live
text_delta into the transcript). Unavailable cursor / opencode / kimi / grok / claude / codex /
amp / pi stay demo.
Apply on Settings →
Providers sets session.provider. fx missing copies the verified
install command; fx available copies fx login. Other missing CLIs
get a PATH hint. Not OAuth or auto-install.
Probe (boot init_fx, or first Send): ~/.local/bin/fx --help, then
fx --help on PATH. New sessions default to fx.
Local session store
Sessions persist on disk beside the app. This is not the Waku daemon and does not open a WebSocket. Native has no daemon/WebSocket client; the store is a local JSON document that follows Waku's catalog / hydrate / merge-only save rules.
Path (Native app_dirs data directory, app name faku):
Linux: $XDG_DATA_HOME/faku/sessions.json or ~/.local/share/faku/sessions.json
macOS: ~/Library/Application Support/faku/sessions.json
Missing sessions.json keeps the two first-run demo sessions. Save is
merge-only; RemoveSession is the only delete. New untitled sessions
are not written until they have real content.
Scope
Ready: desktop shell, demo sessions + timer fallback, one-shot fx acp
when the CLI is present (via acp-proxy, which auto-answers
session/request_permission), first-cut live Send for probed ACP
stdio providers (cursor / OpenCode / kimi acp, grok agent stdio),
including first-cut official ACP v1 image content blocks on
session/prompt when a composer image is attached (base64 +
mimeType; ~256KB raw; fail-closed on overflow / bad file; fx still
fx ask --image, no ACP image blocks),
first-cut live Send for Available Claude via official print-mode
stream-json (claude -p --output-format stream-json --verbose --include-partial-messages --forward-subagent-text; later Sends pass
documented --resume {fx_session_id} when that field is non-empty;
first Send and Fork omit it; stdout is parsed as NDJSON — live
text_delta into the assistant turn, not a dump of raw JSON;
non-empty parent_tool_use_id does not append into that turn
(forwarded subagent text fills a bounded 512KB last-window on the
Subagent Background row);
documented image path in the -p prompt when a composer
image is attached), first-cut live Send for Available Codex via official
non-interactive codex exec {prompt} (stdout is the same non-ACP
fx line path; not ACP; documented --image {path} after the prompt
when a composer image is attached), first-cut live Send for Available Amp via
official execute mode (amp -x {prompt}; stdout is the same
non-ACP fx line path; not ACP; documented @{path} in the -x
prompt when a composer image is attached), first-cut live Send for Available
Pi via official JSON event-stream mode (pi --mode json {prompt};
stdout is parsed as JSON lines — live text_delta into the
assistant turn, not a dump of raw JSON or print-mode prose; not ACP;
documented @{path} after json when a composer image is attached),
fx ask --image / fx ask fallback, local session catalog + hydrate,
waku-protocol v4 JSON builders + server-frame parser, one-shot daemon
sidecar, provider id "fx", Environment Summary Background kind
chrome (Process / Monitor / Subagent labels) with Process rows from
window-side stream/settle, live Monitor rows from real Claude
Monitor tool_use plus a first-cut Waku-sized 512KB last-window
log in the right-panel Background surface from matching user
tool_result (runtime-only; newlines kept; CSI stripped for
display via a 100ms render cache; Environment Summary stays a
one-line preview),
live Subagent rows from real Claude
parent_tool_use_id / Agent tool_use signals while streaming
plus a first-cut Waku-sized 512KB last-window log in the
right-panel Background surface from forwarded parent_tool_use_id
text (runtime-only; newlines kept; CSI stripped for display via
the same 100ms render cache; Environment Summary stays a
one-line preview),
first-cut settled Monitor / Subagent persist after the turn
(status from Process settle; Monitor / Subagent last-window kept; Faku-side
Dismiss, not Claude TaskStop; not live after one-shot -p exits), and a
first-cut right-panel Background surface (kind / title / live-or-settled
status / Monitor / Subagent log / Stop when the selected row is a live Process,
live Monitor, or live Subagent; Dismiss when the selected row is a settled
Monitor or Subagent) opened from those rows. Live Monitor
and Subagent Stop is Faku-side dismiss of that row on one-shot
claude -p (not Claude TaskStop mid-turn). Settled Monitor / Subagent
Dismiss is the same Faku-side slot clear after the turn.
Later: Pi ACP / --mode rpc, Claude ACP, --continue,
full onboarding / OAuth / auto-install, a long-lived daemon socket in
the update loop
(not this cut), a long-lived ACP loop once a window-side stdin-write
effect exists, Claude CLI TaskStop,
daemon refreshBackgroundWork, full BackgroundWorkRegistry
event/reconcile parity.
No listSessions / createSession. Catalog is loadTaskState (local
JSON today). New session is a client-built session saved after first
content.
Run
Install the Native CLI globally, then from this directory:
native test --yes
native dev --yes
native check
native build
License
GPL-3.0-only for this Waku-inspired client. Not a verbatim copy of Waku's Rust or TypeScript sources. Native SDK and Vercel fx are Apache-2.0. See LICENSE and NOTICE.