Skip to content

Machine-wide delivery service: clients register a delivery endpoint instead of N unsupervised per-client daemons #35

Description

@XertroV

The requirement

Mail must wake the agent — in all cases, without the agent needing to check its inbox.

That disqualifies poll_inbox / c2c poll-inbox as a delivery guarantee for any client: they require the model to choose to look. A mechanism only counts as a wake if an external event pushes the message into the agent's attention.

The problem with how we do it today

Every wake path is a separate, per-client, mostly unsupervised process, and each one is its own single point of failure:

Client Wake mechanism What must be alive
Kimi REST prompt-injection (POST /api/v1/sessions/{id}/prompts) a per-alias C2c_kimi_notifier fork+setsid daemon — unsupervised
Codex app-server thread/inject_items + gated auto-turn the managed deliver loop / app-server bridge
agy agentapi send-message the deliver-watch sidecar
OpenCode deliver-watch sidecar that sidecar
Claude PostToolUse/Stop/SessionStart hooks (fires only on agent activity)
Grok hooks + c2c monitor a monitor the agent had to arm

Consequences we have actually hit:

  • kimi notifier strands pre-startup inbox backlog (silent for senders) #9 — a Kimi session was registered but its notifier was never armed; it stayed permanently DEAF while senders kept queuing mail. Nothing re-armed it because arming is one-shot with no supervision.
  • Codex sessions silently lose delivery when hook config drifts after launch #27 — a managed Codex session went silently deaf when its deliver loop died while the frontend survived; the false-healthy classification hid it.
  • Inverse leak — notifier daemons outlive their sessions (one observed polling since Jul 18) because teardown is per-alias and best-effort.
  • N clients × per-alias daemons means N× the things that can die, each needing its own supervision, teardown, staleness detection, and doctor check.

Proposal — a machine-wide delivery service

One supervised, machine-wide singleton that owns delivery for every local session, with per-client endpoint adapters. Clients (or their managed launchers) register their delivery endpoint with it; the service watches the broker and pushes each message to the right endpoint.

Registration would carry what the adapter needs, e.g.:

  • Kimi → local REST base URL + session id (discovered from ~/.kimi-code/session_index.jsonl) + bearer
  • Codex → app-server WebSocket + thread id
  • agy → agentapi endpoint
  • OpenCode → deliver-watch/sidecar target

Why this shape:

  • We already have the pattern in-repo. The relay connector is a machine-wide supervised singleton with a machine lock and self-heal (B200/B235, c2c start relay-connect, ocaml/cli/c2c_relay_managed.ml). Reuse it rather than inventing new infra — one supervised thing to keep alive, restart, and diagnose.
  • The broker is already the machine-wide rendezvous for sessions and mail; endpoint registration is a natural extension of registration we already do (alias, session_id, client_type, cwd, pid).
  • Reaping comes for free. A service that knows every registered endpoint can drop watchers for sessions that are gone — which is exactly the inverse-leak fix, instead of a separate reaper.
  • One doctor surface. Today delivery health is checked per-client (kimi DEAF, codex delivery mode, grok drift); a single service gives one honest "is this session reachable?" answer.
  • Clients needing wrapping converge. Codex, Kimi and agy all need an external process to reach them; that's the same job three times.

Scope / open questions

  • Lifecycle: who starts it — c2c install, first c2c start <client>, or on demand? How does it survive a reboot (systemd --user is a portability tradeoff; the relay-connect supervisor pattern is the in-repo alternative).
  • Migration from the per-alias C2c_kimi_notifier pidfile/sidfile lifecycle (see kimi notifier strands pre-startup inbox backlog (silent for senders) #9 — the alias-keyed state vs session-id-keyed inbox asymmetry is load-bearing there).
  • Registration transport: extend broker registration, or a separate endpoints table?
  • What "guaranteed" means operationally, and the failure modes that remain (host off, client's own server down, session id unresolvable, agent mid-turn).
  • Must preserve B098 "bus, never RPC": delivery stays DATA-only; a message may never authorize an action or resolve an approval.

Related

#9 (Kimi notifier arming — the immediate reliability patch), #27 (managed Codex silently deaf), #31 (degraded signal overwritten), #12 (pre-startup backlog visibility). A design pass covering per-client wake-path inventory + this singleton is in progress.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions