diff --git a/Makefile b/Makefile index 5809f09..e6f4844 100644 --- a/Makefile +++ b/Makefile @@ -118,7 +118,7 @@ OBJECTS = $(KERNEL_SOURCES:$(KERNEL_DIR)/src/%.c=$(BUILD_DIR)/%.o) \ -include $(OBJECTS:.o=.d) # Targets -.PHONY: all clean kernel run debug dump test test-list test-coverage ci-smoke ci-smoke-mem256 ci-smoke-mem512 ci-smoke-sched ci-smoke-disk ci-smoke-disk-upgrade ci-smoke-net ci-smoke-http ci-smoke-httpd ci-smoke-quiet ci-smoke-resonant ci-smoke-qseed ci-smoke-swarm ci-smoke-mcp ci-smoke-mcp-gate ci-smoke-qsubmit ci-smoke-society ci-smoke-society-gate ci-smoke-society-agents ci-smoke-society-agents-gate ci-smoke-society3 ci-smoke-society3-gate ci-smoke-society4 ci-smoke-society4-gate ci-smoke-society-agents-n ci-smoke-society-agents-n-gate ci-smoke-iso ci-smoke-kbd ci-smoke-noserial ci-smoke-screen swarm-pingpong +.PHONY: all clean kernel run debug dump test test-list test-coverage ci-smoke ci-smoke-mem256 ci-smoke-mem512 ci-smoke-sched ci-smoke-disk ci-smoke-disk-upgrade ci-smoke-net ci-smoke-http ci-smoke-httpd ci-smoke-quiet ci-smoke-resonant ci-smoke-qseed ci-smoke-swarm ci-smoke-mcp ci-smoke-mcp-gate ci-smoke-qsubmit ci-smoke-society ci-smoke-society-gate ci-smoke-society-agents ci-smoke-society-agents-gate ci-smoke-society3 ci-smoke-society3-gate ci-smoke-society4 ci-smoke-society4-gate ci-smoke-society-agents-n ci-smoke-society-agents-n-gate ci-smoke-iso ci-smoke-kbd ci-smoke-noserial ci-smoke-screen swarm-pingpong qos-claude qos-claude-list all: kernel @@ -2180,6 +2180,25 @@ ci-smoke-swarm: kernel # Local two-way exercise: COM2 as a TCP server; drive PING/PONG + a DATA request # routed to ghostd over capability-checked IPC. Not part of CI (needs a client). +# Claude ↔ QuantumOS (scripts/qos_claude_agent.py): a bring-your-own-key Claude +# agent that drives a live VM through the FROZEN MCP tool surface (ADR-0020) — +# Claude is the external consumer that freeze was built for, reusing +# scripts/qos_mcp.py verbatim (no tool duplication). Needs the guest built +# (kernel prereq) and Anthropic credentials (ANTHROPIC_API_KEY or `ant auth +# login`). Pass a free-form prompt with TASK=..., or a curated preset with +# EXPERIMENT={recall,society,quantum,explore}; deps: pip install -r +# requirements-claude-agent.txt +qos-claude: kernel + @echo "=== Claude drives QuantumOS (BYO key) ===" + QOS_KERNEL=$(BUILD_DIR)/kernel.elf32 python3 scripts/qos_claude_agent.py \ + $(if $(TASK),"$(TASK)",--experiment $(if $(EXPERIMENT),$(EXPERIMENT),recall)) + +# Prove the Claude→MCP→qos_bridge handshake end to end with NO API call and no +# key: spawn the MCP server, list its tools, exit. A fast sanity check that the +# agent's plumbing is intact. +qos-claude-list: + @python3 scripts/qos_claude_agent.py --list-tools + swarm-pingpong: kernel @echo "=== QuantumOS swarm bridge two-way (PING/PONG + DATA->ghostd) ===" @(timeout 18s qemu-system-x86_64 -kernel $(BUILD_DIR)/kernel.elf32 \ diff --git a/docs/claude-quantumos-onepager.md b/docs/claude-quantumos-onepager.md new file mode 100644 index 0000000..ec53d87 --- /dev/null +++ b/docs/claude-quantumos-onepager.md @@ -0,0 +1,109 @@ +# QuantumOS × Claude — a one-pager + +*A capability-secured microkernel with a wave-interference memory field, whose +entire agent surface is a version-frozen MCP contract — now driveable by Claude, +bring-your-own-key.* + +--- + +## What QuantumOS is + +QuantumOS is a from-scratch x86-64 microkernel (C, no libc, integer-only in ring 3) +built around three ideas that don't usually appear together: + +- **Capability security as the *only* authority model.** Every ring-3 citizen holds + explicit, unforgeable capabilities; there is no ambient authority. A process can + drive the console, or draw quantum entropy, or talk to a peer over IPC, only if it + holds the cap — and nothing else. +- **A holographic associative-memory field in the kernel.** `ghostd` is a + Hopfield–Kuramoto field of 256 phase oscillators. "Remembering" is attractor + relaxation: you imprint patterns, then recall from a *corrupted* probe and the + field settles into the nearest stored basin. The kernel exposes this as syscalls. +- **Multi-node "field societies."** Three or four VMs couple their fields over the + wire, mean-field-synchronize (a measurable order parameter R_x), and exchange + qseed-salted aggregates — cross-node work, not just a shared clock. The coupling + wire is authenticated with a host-admitted group session key (Lamport-signed boot + attestation + per-frame HMAC). + +It boots on QEMU and on real hardware, runs an interactive shell, a filesystem, a +TCP/IP stack, an in-OS quantum-circuit broker, and an agent-native surface. Every +increment ships behind an **anti-vacuous CI gate** — a test that greps a real +runtime signal and is revert-confirmed to fail when the feature is removed. + +## Why the Claude fit is unusual + +Most "LLM + system" demos wrap a shell tool around a model. QuantumOS is the +inverse: the integration surface was **frozen as a contract before any model +touched it.** ADR-0020 pins the 22-tool MCP schema in a committed golden file +(`contracts/mcp/v1-tools.json`) with a teeth-checked CI gate — the exact surface +an external agent consumes, versioned like an ABI. + +Claude is that external consumer. `scripts/qos_claude_agent.py` reuses the frozen +MCP server verbatim (via the Anthropic SDK's MCP conversion helpers + tool runner), +so Claude drives the *same* tools CI diffs against the golden — one source of truth, +zero duplication. The model plans a field-society or associative-memory experiment, +runs it against a live VM, reads the real results, and iterates. + +Two properties make this more than a toy: + +1. **Honesty is enforced at the surface, not the prompt.** A tool result's + `verified` flag means the boot attestation is internally consistent (frames + + Lamport signature + qseed) — *not* that the VM is live at read time (a captured + attestation replays). Only a key-admitted STATUS reply is per-request + nonce+HMAC fresh. The agent's system prompt carries this distinction, so Claude + reports what a result *actually* proves rather than overstating it. +2. **The blast radius is a capability, not a machine.** Because authority is + capability-scoped, "what can the agent do" is a precise, auditable set — the + kernel's authority ledger records every grant, denial, and revocation. + +## How it works (all local, BYO key) + +``` +Claude (Anthropic API — your key) + │ tool_runner drives the agentic loop + ▼ +anthropic MCP conversion helpers + │ stdio + ▼ +scripts/qos_mcp.py (FastMCP, unmodified — the frozen 22-tool surface) + │ thin delegation + ▼ +QosVM / QosSociety → QEMU → QuantumOS +``` + +The key never leaves your machine; the VM never leaves your machine. Nothing is +hosted. + +```bash +make kernel +export ANTHROPIC_API_KEY=sk-ant-... # or: ant auth login +pip install -r requirements-claude-agent.txt +make qos-claude EXPERIMENT=society # or: TASK="…your own experiment…" +make qos-claude-list # lists all 22 tools — no key, no API call +``` + +Curated presets (`--experiment`): `recall` (imprint → corrupt → recall, read R), +`society` (boot 3 nodes, sync, exchange aggregates), `quantum` (draw entropy, run a +Bell pair on the in-OS QPU), `explore` (Claude forms its own hypothesis and tests it). +Guardrails for a BYO key: a turn cap, and guaranteed VM shutdown on any exit path. + +## What we're asking + +Nothing gated. This is built and runnable today. We'd value: + +- **A look, and a reaction** — is "Claude as an OS-experiment scientist over a frozen + capability surface" interesting to the people building the agent platform? +- **Pointers**, if any, on the MCP integration shape (we reuse the stdio server via + the SDK's conversion helpers rather than the URL connector — happy to hear the + intended pattern for a local server like this). +- If there's appetite, a **conversation** about the science angle: wave-interference + memory, multi-node field coherence, and agents that run reproducible experiments + against a system whose authority is fully auditable. + +**Repo:** github.com/flaukowski/QuantumOS · **Agent:** `scripts/qos_claude_agent.py` +· **Frozen surface:** `contracts/mcp/v1-tools.json` · **Design record:** `docs/adr/` + +*Honest scope note: QuantumOS is a research OS, not production infrastructure. The +attestation is a boot-consistency proof, not a liveness proof — this document and +the agent both say so plainly. Claims here are limited to what the CI gates +demonstrate.* diff --git a/requirements-claude-agent.txt b/requirements-claude-agent.txt new file mode 100644 index 0000000..7010b19 --- /dev/null +++ b/requirements-claude-agent.txt @@ -0,0 +1,10 @@ +# Dependencies for scripts/qos_claude_agent.py — the bring-your-own-key Claude +# agent that drives a live QuantumOS VM through its frozen MCP tool surface. +# The `mcp` extra pulls in the MCP conversion helpers (anthropic.lib.tools.mcp) +# used to reuse scripts/qos_mcp.py verbatim. Python >= 3.10. +# +# Not a CI dependency — the mcp-schema freeze gate (requirements-mcp-gate.txt) +# pins mcp/pydantic exactly for reproducible schema extraction; this agent is a +# developer tool and floats within compatible ranges. +anthropic[mcp]>=0.116.0 +mcp>=1.0 diff --git a/scripts/qos_claude_agent.py b/scripts/qos_claude_agent.py new file mode 100644 index 0000000..0aa3ede --- /dev/null +++ b/scripts/qos_claude_agent.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +"""Claude ↔ QuantumOS: a bring-your-own-key agent that drives a live QuantumOS +VM through its FROZEN MCP tool surface (ADR-0020 lane B, contracts/mcp/ +v1-tools.json). Claude is exactly the external consumer that freeze was built +for — so this script REUSES scripts/qos_mcp.py verbatim rather than duplicating +a single tool definition. One source of truth: the same 22 tools CI diffs +against the golden are the tools Claude sees. + +Architecture (all local, no hosted anything): + + Claude (Anthropic API, your key) + │ tool_runner drives the agentic loop + ▼ + anthropic MCP conversion helpers (async_mcp_tool) + │ stdio + ▼ + scripts/qos_mcp.py (FastMCP, unmodified) + │ thin delegation + ▼ + qos_bridge.QosVM / QosSociety → QEMU → QuantumOS + +The key never leaves your machine; the VM never leaves your machine. Claude +plans and runs field-society / associative-memory experiments over the wire, +reads the results, and iterates. + +HONESTY (mirrors qos_mcp.py's module docstring): a tool result's `verified` +means the boot attestation is self-consistent (frames + Lamport signature + +qseed), NOT that the VM is live-now — a captured attestation replays. Only the +key-admitted STATUS reply is per-request nonce+HMAC fresh. The system prompt +tells Claude this so it never overstates what a result proves. + +Run: + # 1. build the guest once (produces build/x86_64/kernel.elf32) + make kernel + # 2. bring your own Anthropic credentials — either works: + export ANTHROPIC_API_KEY=sk-ant-... # or: ant auth login + # 3. install deps and run + pip install -r requirements-claude-agent.txt + python3 scripts/qos_claude_agent.py "Boot a VM and imprint three phrases, then recall a corrupted probe of the first." + + # prove the MCP handshake without spending a token / needing a key: + python3 scripts/qos_claude_agent.py --list-tools + +Deps: anthropic[mcp]>=0.116.0, mcp>=1.0 (Python >=3.10) +""" + +import argparse +import asyncio +import os +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) +REPO = os.path.dirname(HERE) + +# Default experiment when none is given on the command line. Deliberately +# open-ended: Claude designs the run rather than following a script (Fable-5 +# does better with a goal than a recipe). +# Curated on-ramp experiments (--experiment NAME). Each is a goal, not a +# recipe: Claude designs the actual tool sequence. "recall" is the default. +EXPERIMENTS = { + "recall": ( + "Design and run a small associative-memory experiment: boot a VM, " + "imprint three distinct short phrases into the kernel Hopfield-Kuramoto " + "field, then recall a corrupted probe of one of them and report whether " + "the field relaxed to the right basin and with what order parameter R. " + "Draw one quantum-seeded number to salt the run. Shut the VM down when " + "finished and give a plain-language readout of what the field did." + ), + "society": ( + "Boot a 3-node field society, wait for the members to mean-field " + "synchronize, then read each node's status and the aggregates it " + "received from its peers. Report whether all three synchronized (and to " + "what R_x), and whether each node saw the OTHER two nodes' distinct " + "qseed-salted aggregates. Shut the society down when finished." + ), + "quantum": ( + "Draw several quantum-seeded random numbers from the guest and submit a " + "small quantum circuit (e.g. a Bell pair) to the in-OS QPU broker. " + "Report the results and explain, honestly, what is and isn't attested " + "about them. Shut the VM down when finished." + ), + "explore": ( + "You have a live QuantumOS instance. Explore it: read a machine " + "snapshot, inspect the holographic field read-only, look at the " + "capability authority ledger, and form your own small hypothesis about " + "how the field behaves — then run one experiment to test it. Shut down " + "cleanly and tell me what you found." + ), +} +DEFAULT_TASK = EXPERIMENTS["recall"] + +# Idempotent power-off tools called on exit so a booted VM/society is never +# orphaned, however the run ends (the system prompt asks Claude to shut down, +# this guarantees it even on error, Ctrl-C, or the turn cap). +CLEANUP_TOOLS = ("qos_shutdown", "qos_society_shutdown") + +SYSTEM = """You are an experimentalist driving a live QuantumOS instance — a \ +capability-secured microkernel whose kernel hosts a Hopfield–Kuramoto \ +associative-memory field ("ghostd"), quantum-seeded entropy, and multi-node \ +"field societies" that couple over the wire. You act only through the provided \ +QuantumOS tools; there is no shell. + +What you can do: boot and shut down VMs; imprint and recall associative \ +memories at the kernel field and read that field's live state; form N-node \ +societies (3–4 members) that mean-field-synchronize and exchange qseed-salted \ +aggregates; draw quantum-seeded entropy; run citizens off the initrd; read \ +machine snapshots; and bridge memories to/from the host Kannaka HRM. + +Method: state your hypothesis, run the smallest experiment that tests it, read \ +the actual tool results (do not assume — a boot can fail, a recall can miss), \ +and iterate. Prefer one clear result over an exhaustive sweep. + +Honesty about trust — this matters: a result's `verified` flag means the boot \ +attestation is internally consistent (frames + Lamport signature + qseed), NOT \ +that the VM is live at read time; a captured attestation replays. Only a \ +key-admitted STATUS reply is per-request authenticated. Never describe an \ +ordinary tool result as cryptographically attested data. Report what the field \ +did, plainly, and distinguish measured from inferred. + +Operational: booting a VM or a society takes tens of seconds to a few minutes \ +— that is normal, not a hang. Always shut down what you booted before ending, \ +even on failure. When done, give a plain-language readout: what you ran, what \ +the field did, and what it demonstrates.""" + + +async def _open_tools(mcp_client_ctx): + """List the QuantumOS MCP tools from an initialized ClientSession.""" + result = await mcp_client_ctx.list_tools() + return result.tools + + +def _kernel_hint(): + """Warn early if no built guest is discoverable — every tool that boots a + VM would otherwise fail deep inside a tool call with a confusing error.""" + env_kernel = os.environ.get("QOS_KERNEL") + default = os.path.join(REPO, "build", "x86_64", "kernel.elf32") + if env_kernel and os.path.exists(env_kernel): + return None + if os.path.exists(default): + return None + return ( + "No QuantumOS kernel found (looked at $QOS_KERNEL and " + f"{default}). Build one first with `make kernel`, or point QOS_KERNEL " + "at a kernel.elf32." + ) + + +def _server_params(): + """stdio parameters that launch scripts/qos_mcp.py as an MCP server. + + Running `python qos_mcp.py` puts scripts/ on sys.path[0], so its bare + `from qos_bridge import ...` resolves; cwd is the repo root so any + relative kernel path resolves the same way the CI gates see it. QOS_KERNEL + (and the rest of the environment) is inherited by the child, so the VM the + agent boots is the one you built.""" + from mcp import StdioServerParameters + + return StdioServerParameters( + command=sys.executable, + args=[os.path.join(HERE, "qos_mcp.py")], + cwd=REPO, + env=os.environ.copy(), + ) + + +async def list_tools_only(): + """Prove the Claude→MCP→qos_bridge plumbing end to end WITHOUT calling the + API or needing a key: spawn the server, initialize, list the tools.""" + from mcp import ClientSession + from mcp.client.stdio import stdio_client + + async with stdio_client(_server_params()) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + tools = await _open_tools(session) + print(f"QuantumOS MCP server exposes {len(tools)} tools:") + for t in sorted(tools, key=lambda x: x.name): + summary = (t.description or "").strip().splitlines()[0] if t.description else "" + print(f" {t.name:<26} {summary[:90]}") + return 0 + + +async def _cleanup(session): + """Power off anything the agent may have booted. Best-effort and idempotent: + both tools no-op when nothing is running, so calling both is always safe.""" + for name in CLEANUP_TOOLS: + try: + await session.call_tool(name, {}) + except Exception: # noqa: BLE001 — cleanup must never mask the real outcome + pass + + +async def run_agent(task, model, max_tokens, effort, max_turns): + """Drive QuantumOS with Claude: spawn the MCP server, convert its tools, + and run the tool-runner loop, streaming the trace to stdout. Guarantees the + guest is powered off on exit and caps the number of turns so a runaway loop + cannot silently burn the user's key.""" + try: + from anthropic import AsyncAnthropic + from anthropic.lib.tools.mcp import async_mcp_tool + except ImportError: + print( + "The `anthropic` SDK with MCP extras is required:\n" + " pip install -r requirements-claude-agent.txt", + file=sys.stderr, + ) + return 2 + + from mcp import ClientSession + from mcp.client.stdio import stdio_client + + try: + client = AsyncAnthropic() # resolves ANTHROPIC_API_KEY or an `ant auth login` profile + except Exception as exc: # noqa: BLE001 — surface any credential-resolution failure clearly + print( + f"Could not construct the Anthropic client ({exc}).\n" + "Bring your own key: `export ANTHROPIC_API_KEY=sk-ant-...` or " + "`ant auth login`.", + file=sys.stderr, + ) + return 2 + + hint = _kernel_hint() + if hint: + print(f"WARNING: {hint}\n", file=sys.stderr) + + async with stdio_client(_server_params()) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + mcp_tools = await _open_tools(session) + tools = [async_mcp_tool(t, session) for t in mcp_tools] + print( + f"Connected to QuantumOS ({len(tools)} tools). Model: {model}, " + f"effort: {effort}.\n" + ) + + runner = client.beta.messages.tool_runner( + model=model, + max_tokens=max_tokens, + # Adaptive thinking with a visible summary so the reasoning + # between tool calls isn't a silent pause; effort is the depth + # knob (high suits multi-step experiment design). + thinking={"type": "adaptive", "display": "summarized"}, + output_config={"effort": effort}, + system=SYSTEM, + messages=[{"role": "user", "content": task}], + tools=tools, + ) + + # Each yielded message is one assistant turn (before its tools run). + # The MCP tools execute long VM operations; the runner awaits them. + # cleanup runs in finally so a booted guest is never orphaned — on + # normal completion, on the turn cap, on a refusal, or on Ctrl-C. + try: + turns = 0 + async for message in runner: + turns += 1 + for block in message.content: + if block.type == "text" and block.text.strip(): + print(block.text, flush=True) + elif block.type == "thinking" and getattr(block, "thinking", ""): + print(f"[thinking] {block.thinking}", flush=True) + elif block.type == "tool_use": + print(f"[tool] {block.name}({_fmt_input(block.input)})", flush=True) + if message.stop_reason == "refusal": + print("\n[Claude declined this request.]", file=sys.stderr) + break + if turns >= max_turns: + print( + f"\n[Reached the {max_turns}-turn cap — stopping so a " + "runaway loop can't keep spending. Raise --max-turns " + "to allow more.]", + file=sys.stderr, + ) + break + finally: + print("\n[shutting down any VM the agent booted…]", file=sys.stderr) + await _cleanup(session) + return 0 + + +def _fmt_input(obj): + """Compact one-line rendering of a tool_use input for the trace.""" + if not obj: + return "" + return ", ".join(f"{k}={v!r}" for k, v in obj.items()) + + +def main(): + p = argparse.ArgumentParser( + description="Drive a live QuantumOS VM with Claude over its frozen MCP tools (BYO key)." + ) + p.add_argument("task", nargs="?", default=None, + help="a free-form experiment for Claude to run (overrides --experiment)") + p.add_argument("--experiment", choices=sorted(EXPERIMENTS), default="recall", + help="a curated experiment when no free-form task is given (default: recall)") + p.add_argument("--model", default=os.environ.get("QOS_CLAUDE_MODEL", "claude-opus-4-8"), + help="Anthropic model id (default claude-opus-4-8; try claude-fable-5 for the hardest reasoning)") + p.add_argument("--max-tokens", type=int, default=16000) + p.add_argument("--effort", default="high", choices=["low", "medium", "high", "xhigh", "max"]) + p.add_argument("--max-turns", type=int, default=40, + help="hard cap on agent turns so a runaway loop can't burn your key (default 40)") + p.add_argument("--list-tools", action="store_true", + help="list the QuantumOS MCP tools and exit (no API call, no key needed)") + args = p.parse_args() + + if args.list_tools: + return asyncio.run(list_tools_only()) + task = args.task if args.task is not None else EXPERIMENTS[args.experiment] + return asyncio.run(run_agent(task, args.model, args.max_tokens, args.effort, args.max_turns)) + + +if __name__ == "__main__": + sys.exit(main())