Keep agent MCP transport across daemon restarts

This commit is contained in:
Meta Vens 2026-07-19 21:05:05 +01:00
commit bb74fc849f
8 changed files with 119 additions and 5 deletions

View file

@ -214,7 +214,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- The one-minute Persona scheduler pulse is an observation clock, not an inference cadence. With the swarm brake present it returns before registry, Discord, embedding, storage, or dispatch work; current Ygg logs show `selected: []` and `queued: []`. Unprompted speech also has a separate global admission window. Do not cool token use by making observation blind; keep turn authorization at the initiative owner.
- Revision `f97e67b` adds the portable moderation-heartbeat coordinator and enforcement organ behind a separate disabled-by-default 15-minute cadence. The model returns an exact review ledger plus urgent IDs and typed case operations; missing evidence or unaccounted urgent evidence blocks cursor advancement. The attempt is receipted by latest message before inference, so one unchanged archive window cannot burn tokens repeatedly. Case enforcement is separate, scans typed pending debt on every cadence, and resolves a case only after a required Discord ban succeeds. Ygg's first `log_only` window at `0629fc7` failed safely with one 401, zero cases, zero cursor movement, and zero sanctions; both supported auth-header shapes prove the mounted MiMo credential itself is stale. `VOID_MODERATION_DAEMON_ENABLED` is false again pending a refreshed provider credential or deliberate alternate model port.
- The shared Bifrost Discord mouth now lives in `packages/core/src/bifrost-discord-command.ts`. Worker repo-Face speech and resident Void candidate delivery use the same stable-id typed command, provider-store write, optional Bifrost pump, and completed-receipt check. The candidate organ selects at most one targeted queued candidate and writes its speech receipt/`spoken` transition only after transport completes. Ygg mounts the Bifrost provider store and CultLib runtime into the scheduler; `VOID_CANDIDATE_DELIVERY_ENABLED` controls this transport-only organ independently from the disabled moderation/rumination model cadence.
- Discord archive/source retrieval is a resident worker organ on Yggdrasil. `apps/worker/src/mcp-http-server.ts` publishes stateless streamable HTTP at `127.0.0.1:17875/mcp` and receives the worker's existing archive repositories, source ingester, and retrieval service by injection. The canonical workstation SSH tunnel maps the same Starfire loopback port to Ygg; repo and global Codex config use the URL directly. No tool call starts SSH, Docker, Node, or a second retrieval body. `scripts/smoke-voidbot-resident-mcp-http.mjs` proves tool advertisement, indexed repo listing, and source search through that resident endpoint.
- Discord archive/source retrieval is a resident worker organ on Yggdrasil. `apps/worker/src/mcp-http-server.ts` publishes stateless streamable HTTP at `127.0.0.1:17875/mcp` and receives the worker's existing archive repositories, source ingester, and retrieval service by injection. The canonical workstation SSH tunnel maps the same Starfire loopback port to Ygg. Codex attaches to `scripts/serve-voidbot-mcp-bridge.mjs`, a task-lifetime stdio transport bridge with no retrieval authority; each tool request uses HTTP to the daemon, and a deployment/tunnel gap returns one MCP tool error without closing the outer transport. It starts no SSH, Docker, or remote Node process. `scripts/smoke-voidbot-mcp-bridge-recovery.mjs` proves one outer client succeeds before an endpoint outage, receives an error during it, and succeeds again after recovery.
- Resident person-shaped rumination is wired behind `VOID_RUMINATION_DAEMON_ENABLED` with its own bounded cadence, Discord evidence window, dedicated doctrine, rules, and voice inputs. The pressure fingerprint is receipted before the model attempt and unchanged pressure cannot retry on the observation clock. Ygg explicitly keeps this false while the MiMo credential is stale; candidate delivery may remain enabled because it performs no inference and the live queue was empty at rollout.
- Revisions `c0cf875` and `1527b29` extract person-shaped rumination and candidate delivery without scheduling them. Rumination context, operation policy, advocacy accountability, tool-free text actuation, and coordinator are portable; the attempt key hashes meaning-bearing pressure/evidence and ignores elapsed clock, so unchanged state cannot create periodic thought. Candidate delivery is deterministic and parent-owned: one targeted queued candidate, duplicate-receipt refusal, injected Bifrost port, then one `mark_candidate_intervention_spoken` operation with the transport receipt. The next cut is extracting the worker's existing typed Bifrost Discord command path into a shared port rather than duplicating CultMesh mechanics.
- Deterministic state-packet assembly now lives in `persona-state-packet-projector.ts`, which takes typed state, registry/room evidence, pronoun guidance, curiosity facts, and an explicit observation time. The shell no longer owns section ordering or social-context composition. The downstream regex vocabulary cleaner was deleted: it was a symptom compensator rewriting ordinary words such as `witness` and `canary` after projection. A smoke proves literal source language survives the projector unchanged.

View file

@ -111,7 +111,7 @@ VoidBot runs on Yggdrasil. Idunn owns deployment and consumes upstream repositor
5. `scripts/reconcile-source-repos.ts` discovers local Git repos, refreshes push hooks, prunes stale repo shards, and indexes newly discovered repos.
6. `scripts/index-source-repos.ts` and `scripts/git-post-push-index.mjs` drive explicit or detached per-repo source/lore reindex work.
7. `apps/worker/src/mcp-server.ts` boots the MCP lane, while `mcp-server-resources.ts` and `mcp-server-tools.ts` expose retrieval to Codex and other sessions through `search_history`, `get_message_context`, `search_sources`, `get_source_context`, and `list_indexed_repos`.
8. The resident worker starts `mcp-http-server.ts` beside its job loop and injects the same long-lived archive repositories, source ingester, and retrieval service used by the daemon. It publishes stateless streamable HTTP at Ygg loopback `127.0.0.1:17875/mcp`. The canonical SSH tunnel maps that endpoint to Starfire loopback, and Codex connects by URL. HTTP protocol handlers are request-scoped, but no request starts a process, an SSH session, a container exec, or a second retrieval authority. Qdrant stays loopback-private on Yggdrasil; agents receive MCP tools, not a raw vector-database route. The retained stdio entrypoint exists only for bounded child-provider use inside the worker body.
8. The resident worker starts `mcp-http-server.ts` beside its job loop and injects the same long-lived archive repositories, source ingester, and retrieval service used by the daemon. It publishes stateless streamable HTTP at Ygg loopback `127.0.0.1:17875/mcp`, mapped to Starfire by the canonical SSH tunnel. Codex connects to one task-local `serve-voidbot-mcp-bridge.mjs` process over stdio. That bridge owns only outer transport continuity: it creates a lightweight HTTP MCP client per tool request, converts daemon/tunnel unavailability into an ordinary tool error, and remains alive for later calls. It cannot read archives, Qdrant, repositories, or Void state itself and starts no SSH, Docker, or remote process. Qdrant stays loopback-private on Yggdrasil. The separate retained stdio server entrypoint exists only for bounded child-provider capability isolation inside the worker body.
## Flow 4: Ops And Recovery