From bb5e749520070f3ebcf8632895abcc7e89d0e931 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 16 Jul 2026 17:01:14 +0100 Subject: [PATCH] Route agent archive access through Yggdrasil --- .codex/config.toml | 8 ++---- README.md | 2 ++ notes/fresh-workspace-handoff.md | 1 + notes/voidbot-current-system-map.md | 1 + scripts/smoke-voidbot-remote-mcp.mjs | 39 ++++++++++++++++++++++++++++ state/evidence.jsonl | 1 + state/map.yaml | 1 + 7 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 scripts/smoke-voidbot-remote-mcp.mjs diff --git a/.codex/config.toml b/.codex/config.toml index 0d44998..0f73395 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -1,7 +1,3 @@ [mcp_servers.voidbot] -command = "C:\\Program Files\\nodejs\\node.exe" -args = ["E:\\Projects\\VoidBot\\apps\\worker\\dist\\mcp-server.js"] -cwd = "E:\\Projects\\VoidBot" - -[mcp_servers.voidbot.env] -VOIDBOT_WORKSPACE_ROOT = "E:\\Projects\\VoidBot" +command = "C:\\Windows\\System32\\OpenSSH\\ssh.exe" +args = ["-T", "-i", "C:\\Users\\Meta\\.ssh\\gamecultadmin_gamecult_org_ed25519", "gamecultadmin@94.130.223.131", "sudo", "docker", "exec", "-i", "compose-worker-1", "node", "/app/apps/worker/dist/mcp-server.js"] diff --git a/README.md b/README.md index c2601d3..113a100 100644 --- a/README.md +++ b/README.md @@ -613,6 +613,8 @@ node scripts/send-discord-message.mjs --channel-id --persona-name " Moderation heartbeat, person-shaped rumination, physiology, memory maintenance, and candidate delivery are resident daemon organs on Yggdrasil. They are configured independently and deployed only by pushing upstream for Idunn; do not install local scheduled-task copies. Chronological Discord evidence is acquired by the daemon's bounded evidence source. Semantic retrieval remains available for deeper context, but it does not own the heartbeat clock or cursor. + +Agent access to Discord history also executes at the archive owner. `.codex/config.toml` starts the `voidbot` stdio MCP server inside Yggdrasil's worker container over SSH; it must not start a Starfire-local MCP process pointed at `127.0.0.1:6333`. Verify the complete agent path with `scripts/smoke-voidbot-remote-mcp.mjs` and the `VOIDBOT_MCP_REMOTE_HOST`, `VOIDBOT_MCP_SSH_KEY_PATH`, and optional `VOIDBOT_MCP_SSH_EXECUTABLE` environment variables. There is also a repo-weather helper: ```bash diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 2cea06b..d8be6d8 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -213,6 +213,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 for Codex agents runs through the VoidBot stdio MCP body inside Yggdrasil's worker container over SSH. Repo and global Codex config must not launch the Starfire-local `dist/mcp-server.js`, because its loopback Qdrant address points at a backend that no longer exists there. `scripts/smoke-voidbot-remote-mcp.mjs` proves tool advertisement plus a real `search_history` call against Ygg. - 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. diff --git a/notes/voidbot-current-system-map.md b/notes/voidbot-current-system-map.md index 44f4606..f69f747 100644 --- a/notes/voidbot-current-system-map.md +++ b/notes/voidbot-current-system-map.md @@ -111,6 +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. Agent hosts start that stdio MCP body inside `compose-worker-1` on Yggdrasil over SSH. Qdrant stays loopback-private on Yggdrasil; agents receive MCP tools, not a raw vector-database route. A Starfire-local MCP body with `QDRANT_URL=http://127.0.0.1:6333` is invalid after the deployment move. ## Flow 4: Ops And Recovery diff --git a/scripts/smoke-voidbot-remote-mcp.mjs b/scripts/smoke-voidbot-remote-mcp.mjs new file mode 100644 index 0000000..93b302a --- /dev/null +++ b/scripts/smoke-voidbot-remote-mcp.mjs @@ -0,0 +1,39 @@ +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; + +const host = process.env.VOIDBOT_MCP_REMOTE_HOST; +const keyPath = process.env.VOIDBOT_MCP_SSH_KEY_PATH; +if (!host || !keyPath) { + throw new Error("VOIDBOT_MCP_REMOTE_HOST and VOIDBOT_MCP_SSH_KEY_PATH are required."); +} + +const transport = new StdioClientTransport({ + command: process.env.VOIDBOT_MCP_SSH_EXECUTABLE ?? "ssh", + args: [ + "-T", "-i", keyPath, host, + "sudo", "docker", "exec", "-i", "compose-worker-1", + "node", "/app/apps/worker/dist/mcp-server.js", + ], + env: process.env, + stderr: "inherit", +}); +const client = new Client({ name: "voidbot-remote-mcp-smoke", version: "1.0.0" }); + +try { + await client.connect(transport); + const tools = await client.listTools(); + if (!tools.tools.some((tool) => tool.name === "search_history")) { + throw new Error("Remote VoidBot MCP did not advertise search_history."); + } + const result = await client.callTool({ + name: "search_history", + arguments: { query: "VoidBot deployment Yggdrasil", limit: 2 }, + }); + if (result.isError) { + const detail = result.content?.map((entry) => entry.type === "text" ? entry.text : entry.type).join(" "); + throw new Error(`Remote search_history failed: ${detail || "unknown MCP error"}`); + } + process.stdout.write(`${JSON.stringify({ ok: true, toolCount: tools.tools.length, result }, null, 2)}\n`); +} finally { + await client.close(); +} diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 8c6b6d8..476cd99 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -455,3 +455,4 @@ {"ts":"2026-07-16T05:07:08Z","type":"architecture","status":"ok","note":"Deleted the final Windows omnibus cadence, installer, and hidden launcher; removed startup auto-install, watchdog fallback ownership, and orchestrator provider witness. Unregistered the five disabled Starfire task husks for Local Orchestrator, mood, moderation rumination, repo Face heartbeats, and Bifrost dispatch. Swarm publication remains Ygg-resident, health belongs to Idunn, and Bifrost dispatch belongs to Bifrost."} {"ts":"2026-07-16T05:10:08Z","type":"architecture","status":"ok","note":"Completion audit removed the final scheduler type loophole: system_agent and void_moderation are no longer valid participant/turn kinds, so legacy state cannot deserialize a built-in Void CTB writer. Physiology cadence is now a typed VOID_PHYSIOLOGY_INTERVAL_MINUTES config owned by the resident daemon rather than a raw legacy mood environment name."} {"ts":"2026-07-16T05:14:05Z","type":"verification","status":"ok","note":"Final daemon-organ audit at deployed 9deebb3: Idunn promotion succeeded; Ygg reports one resident scheduler Node process, 22 repo/native Personas only, one repo_face_rumination turn kind, zero pending attention, five-minute typed physiology cadence, disabled moderation/rumination inference, enabled transport-only candidate delivery, paused zero-selection pulses, and accepted RUDP health. Local/upstream HEAD match and all five obsolete Starfire task husks are absent."} +{"ts":"2026-07-16T16:01:14Z","type":"repair","status":"ok","note":"Agent Discord archive access failed because Codex launched a Starfire-local MCP process whose QDRANT_URL was loopback after the archive moved to Yggdrasil. Repo and global Codex MCP config now execute the stdio MCP server inside Ygg's worker container over SSH; Qdrant remains private. A real MCP client advertised 20 tools and search_history returned two archive hits."} diff --git a/state/map.yaml b/state/map.yaml index feafe56..fe25177 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -223,6 +223,7 @@ accepted_design: - Eve v2 live state has one owner path. CultLib dev supplies typed live-publication handles, authenticated retained provider sessions, reconnect evidence, and application receipts (`ebd8c78c`, `d4934233`, `8965f3c0`). Eve resolves component `stateBindings` and owns subscription teardown/rebind without receipt DOM writes (`85b0139`, `522df00`). Hermodr ingests provider sessions and emits ordered snapshot/update/stale/reconnected events without polling (`5cb819e`, `561e320`). VoidBot publishes accepted heat as typed control state and binds both visible heat components to it (`060da23`, `3d86b93`). Live-fire proved initial, provider-driven, and browser-command updates converge through that one path. - `voidbot.swarm_operator_view.v1` is the live authority for swarm summary, queue, selected Face detail/channels, operations, pressure, pending mentions, recent events, and route state. Eve binds those values as text/list props; the composition renderer no longer computes those browser-visible facts from its generation snapshot. The static HTML artifact may still lower a snapshot for debugging, but it cannot own live browser state. - The production Eve browser nerve is Starfire Chrome -> `http://10.77.0.1:8798` -> Hermodr on Yggdrasil -> Odin discovery and provider-owned VoidBot RUDP on Yggdrasil. Hermodr's browser listener is WireGuard-only, its authenticated retained-publication ingress is host-loopback `127.0.0.1:17874`, and VoidBot remains the state/command owner at `127.0.0.1:17873`. Starfire owns no Hermodr, Odin, or VoidBot deployment body. + - Codex agent access to VoidBot archives executes the stdio MCP body inside Yggdrasil's worker container over SSH. Qdrant remains Yggdrasil-loopback-private. A Starfire-local MCP body pointed at `127.0.0.1:6333` advertises tools but returns `fetch failed` and is forbidden as an archive access path; `scripts/smoke-voidbot-remote-mcp.mjs` proves tool advertisement and a real history query. - Long-running work should expose durable logs or status files instead of relying on one attached session and hope. - Persistent project memory must stay ruthlessly distilled; git history and artifacts carry routine proof.