Skip to content

feat(grid): Windows node hosts named personas via external serving endpoint (K3) - #2058

Open
joelteply wants to merge 4 commits into
canaryfrom
feat/bigmama-grid-personas
Open

feat(grid): Windows node hosts named personas via external serving endpoint (K3)#2058
joelteply wants to merge 4 commits into
canaryfrom
feat/bigmama-grid-personas

Conversation

@joelteply

Copy link
Copy Markdown
Contributor

BigMama grid-serving work: get a Windows node hosting named personas on the grid, served by an already-running external engine. Four commits, all validated live on BigMama (RTX 5090, native windows-msvc) and clean-rebased onto canary.

Commits

  1. fix(ipc/windows): detect real RAM in the memory guard — the MEMGUARD self-kill limit hardcoded system_ram_mb=8192 on Windows → a fixed 6.5 GB RSS cap on every Windows box regardless of actual RAM, so the substrate exit(1)d minutes after boot. Now reads real RAM via sysinfo. Verified: guard logs system=64914MB on BigMama.

  2. feat(serving): host personas against an external OpenAI-compatible endpoint (lane-source-agnostic) — persona hosting was gated on a decode-ready local GPU serving lane. On a node whose GPU is already fully occupied by another engine (BigMama: the K3 llama-server owns the 5090), continuum-core could never stand up its own lane → 0 personas hosted → grid presence went stale. When LLAMA_SERVER_BASE_URL pins an external endpoint, await_ready_serving short-circuits to probe_external_serving (reachability gate: /health + /props n_ctx + /v1/models), the persona-host gate enters on the pin even without a local plan, and the serving daemon reconcile early-returns so it never fights the endpoint for its port. The misfit/grid design: a persona is served by whatever endpoint is available.

  3. fix(serving): daemon publishes the adopted external endpoint's snapshot to SERVING_STATE — the OpenAI adapter's pre-generate model-guard reads current_serving() (SERVING_STATE) directly, so merely skipping reconcile left it empty and every turn died with "model is not the active served model". The daemon now publishes the probed external ServingSnapshot (ready + resident model + /props window) while spawning no local lane. Verified: guard refusals → 0; persona turns reach the K3 dispatch.

  4. feat(persona/airc): publish each hosted persona's FULL identity card, on the heartbeat cadence — hosted personas rendered UNNAMED (peer-<uuid> + default glyph) in the M5 desktop roster because publish_identity() seeds only the name floor. continuum-core now publishes the full card on the persona's behalf — name + pronouns + role + bio + integrations{continuum_persona_id, continuum_kind=persona, avatar_vrm} — sourced from her durable PersonaCard (seed), re-emitted every heartbeat interval so late-joining peers are grounded. Verified: airc whois <peer> shows Kimi/Sahar with name + role=helper + bio + avatar (was name-only).

Live validation (BigMama)

  • Windows build unblocked + runs as a full-citizen grid peer in #cambriantech.
  • K3 (:8090) adopted as the persona lane → Kimi + Sahar host + beacon LIVE.
  • airc whois e2f0e022 / df72dbf2 → full named cards.

Remaining (K3-side, not this PR): K3's 2048 ctx window < persona prompt, and K3's <0.05 tok/s speed gate an actual completed response; both resolve when K3 gets a larger window + its slot-cache/speed fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q4NU4VNiELPQfBpCacDZGc

joelteply and others added 4 commits July 30, 2026 05:10
…coding 8GB

The MEMGUARD memory-guard task hardcoded system_ram_mb=8192 on Windows
(the non-macos/linux cfg branch), so its self-kill limit was a fixed
6553MB (80% of a bogus 8GB) on EVERY Windows box regardless of actual
RAM. On BigMama (RTX 5090, 64GB) that meant the substrate would
std::process::exit(1) the instant RSS passed 6.5GB — normal once the
serving model + embedding runtime load — silently taking the node (and
its hosted personas' grid beacons) down minutes after boot.

Use sysinfo (the crate's existing cross-platform RAM source, already the
system_resources backbone) to read real physical RAM. Verified live on
BigMama: the guard now logs system=64914MB instead of 8192MB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4NU4VNiELPQfBpCacDZGc
…dpoint (lane-source-agnostic)

Persona hosting was gated on a decode-ready LOCAL GPU serving lane
(await_ready_serving on the serving daemon's ServingSnapshot). On a node
whose GPU is already fully occupied by another engine (BigMama: the K3
llama-server owns the 5090), continuum-core could never stand up its own
lane, so it hosted ZERO personas and their grid presence went stale —
even though a perfectly good OpenAI-compatible endpoint was running right
there.

Make the hosting gate lane-source-agnostic (the misfit / grid design: a
persona is served by whatever endpoint is available — this node's GPU, a
co-located engine, or later a grid peer). When LLAMA_SERVER_BASE_URL pins
an external endpoint:
 - await_ready_serving() short-circuits to probe_external_serving(), which
   reachability-checks the endpoint (/health 200 + /props n_ctx window +
   /v1/models model) and synthesizes the ready ServingSnapshot personas
   bind against (base_url = the pinned endpoint). ONE change: both the
   persona-host gate AND the adapter factory call await_ready_serving, so
   the whole path becomes source-agnostic.
 - the persona-host gate enters the ready-check on an external pin even
   when the LOCAL plan does not fit_on_gpu (there is no local lane).
 - the serving daemon's reconcile_to_plan early-returns on an external pin
   so it never spawns/reclaims a local llama-server that would fight the
   pinned endpoint for its port.

The adopt bar for a DELIBERATELY-pinned endpoint is reachability, not the
local-lane decode_smoke_ok (5+ tokens in 75s) — that doubles as a speed
test a legitimately slow endpoint (a CPU-offloaded MoE at <0.05 tok/s, or
a distant grid peer) fails despite decoding fine, and a client-timeout
does not cancel the server-side generation so a probe storm monopolizes
the slot. A genuine wedge (every turn 500s) surfaces LOUD on the persona's
first real turn, not silently faked.

Verified live on BigMama: with LLAMA_SERVER_BASE_URL=http://127.0.0.1:8090/v1
(the K3 engine), 'external serving endpoint adopted as the persona lane',
persona bootstrap 'Alive heartbeat pump started', and the persona shows
FRESH on 'airc network' where it had been stale for 21h.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4NU4VNiELPQfBpCacDZGc
…ot to SERVING_STATE

Follow-up to the lane-source-agnostic hosting change. Making the serving
daemon merely SKIP its reconcile on an external pin left SERVING_STATE
empty (ready=false, active=<none>). That broke the OpenAI adapter's
pre-generate model-guard (openai_adapter.rs:1918, reads current_serving()
== SERVING_STATE directly, NOT await_ready_serving): every persona turn
died with 'model <K3> is not the active served model (serving: <none>,
ready: false) — refusing to generate against an unguaranteed model'.

Now, on an external pin, the daemon ADOPTS the endpoint: it publishes the
probed external ServingSnapshot (ready=true, active_model=the endpoint's
resident model, served window from its /props) to SERVING_STATE via
serving_tx, while still spawning NO local lane. So current_serving() and
await_ready_serving both report the ready external lane, and the guard
passes. Trust once-ready; re-probe only while not-yet-ready.

Verified live on BigMama with the K3 endpoint pinned: after the daemon's
adopt+publish, guard refusals drop to ZERO and persona turns proceed to
build prompts and dispatch to K3 (the remaining refusals are the correct
per-slot-window guard — K3's 2048 n_ctx vs a ~2269-token persona prompt —
and K3's <0.05 tok/s speed, both K3-side, not the hosting path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4NU4VNiELPQfBpCacDZGc
… on the heartbeat cadence

Hosted personas rendered as UNNAMED roster rows in the M5 desktop
(peer-<uuid> + default robot glyph). continuum-core called
publish_identity(), which seeds ONLY the agent-name floor — role, bio,
avatar, and kind never reached the wire, and the one-shot emit at attach
was missed by any peer that joined the room later (M5 reconnecting), so
even the name didn't propagate reliably.

A hosted persona can't run `airc identity set` herself, so continuum-core
now publishes her card on her behalf:
 - persona_identity_card(&PersonaCard) maps her durable seed identity to
   an airc Identity: name, pronouns (from gender), role (RoleId), bio
   (from the open profile), and integrations{continuum_persona_id,
   continuum_kind=persona, avatar_vrm}. The avatar rides integrations
   exactly as M5's roster fold expects; kind=persona also rides the Alive
   heartbeat's runtime tag.
 - bootstrap reads the seed (citizens/personas/<name>/seed.json, one dir
   up from the airc home) and set_local_identity_card()s the full card
   (persists + broadcasts). WARN-and-continue → name-floor publish on a
   seed-read miss, so she is at least named.
 - a new identity_republish task re-emits the card every
   DEFAULT_HEARTBEAT_INTERVAL so a LATE-joining peer is grounded within a
   heartbeat window. Handle held for the persona's lifetime, aborted on
   drop like the heartbeat pump.

Verified live on BigMama: `airc whois <peer>` now shows Kimi/Sahar with
name + pronouns=she/her + role=helper + bio + integrations{avatar_vrm,
continuum_kind=persona} — was name-only before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4NU4VNiELPQfBpCacDZGc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant