{"slug": "show-hn-loomcycle-a-sidecar-runtime-for-ai-agents-go-binary-apache-2-0", "title": "Show HN: Loomcycle – a sidecar runtime for AI agents (Go binary, Apache-2.0)", "summary": "Loomcycle, an open-source sidecar runtime for AI agents, has reached v1.0 under the Apache-2.0 license. The Go binary provides a hardened agent loop, MCP support on both sides, multi-replica high availability, and runs alongside existing applications without requiring code changes. The release marks a stable, distribution-ready milestone after extensive hardening and testing.", "body_md": "**The agentic runtime, in a sidecar.**\n\n*One Go binary alongside your application. Hardened agent loop, MCP on both sides, multi-replica HA. Apache-2.0.*\n\n🌐 [ loomcycle.dev](https://loomcycle.dev) ·\n📝\n\n[Engineering blog](https://loomcycle.dev/blog/)· 📐\n\n[Architecture](https://github.com/denn-gubsky/loomcycle/blob/main/docs/ARCHITECTURE.md)\n\n🌳\n\nv1.0 is here.loomcycle1.0is released — the feature set is complete and the runtime is hardened and distribution-ready. The core primitives stabilised across the v0.8 → v0.23 line (multi-replica HA; the substrate Defs Agent/Skill/MCPServer/Schedule/Webhook/MemoryBackend/A2A; A2A interoperability; inbound webhooks; pluggable memory + a memory layer; the synthetic`code-js`\n\nprovider; andOSS multi-tenant authorizationin v0.17.0 — per-principal bearer tokens + a role-aware Web UI). The v0.24 → v0.37 line was a hardening + operability run: the interactive terminal, pause/resume/snapshot + cross-instance resume, the context-compaction subsystem, context-transform plugins, external fan-out on every transport, and slow-local-model robustness. 1.0 itself is a pure hardening + distribution milestone — no new primitives — wired to Homebrew, multi-arch Docker, and the Claude Code plugin. 8-hour stability soak: 1.27M circuits, 3.8M agent runs, 100% completion across 468 waves, zero leaks. Apache-2.0. We welcome bug reports, security disclosures, feature contributions, downstream consumers, and forks. See[.]`CONTRIBUTING.md`\n\n**The agentic runtime, in a sidecar.** loomcycle is one Go binary, ~50 MB. It runs *alongside* your application, not inside it. Your app calls loomcycle over HTTP, gRPC, MCP, the TypeScript adapter, or the Python adapter. The agent loop, multi-provider routing, memory and channel primitives, MCP server identity, OpenTelemetry traces, and multi-replica coordination all live in the binary. Your application stays in whatever language you wrote it in.\n\n**The shape that's different.** Today's agentic-systems market gives you three options. One: embed a Python or TypeScript library inside your application process. Two: rent a managed cloud service tied to one vendor's IAM. Three: proxy your model calls through a gateway that doesn't actually run agents.\n\nloomcycle is a fourth option. A lightweight self-hostable runtime that owns the loop *and* speaks every wire format your stack already uses.\n\n| Release | Highlights |\n|---|---|\nv0.4 → v0.26.x foundation |\nEverything the runtime is built on, condensed. Seven inference modes: Anthropic, OpenAI, DeepSeek, Gemini, Ollama (cloud + local), plus the synthetic provider and a mock provider. The hardened `code-js` `model → tool_use → tool_result` loop. 19 built-in tools with Claude Code parity (Read, Write, Edit, Grep, Glob, NotebookEdit), plus HTTP, WebFetch, WebSearch, Bash, Agent, Skill, Memory, Channel, AgentDef, SkillDef, Evaluation, Interruption, Context. The content-addressed, runtime-mutable substrate (Agent / Skill / MCPServer / Schedule / Webhook / MemoryBackend / A2A defs). Vector Memory (sqlite-vec / pgvector) on a pluggable MemoryBackend, with a memory layer above. MCP on both sides. The LLM Gateway + OpenAI-compatible shims. A2A interop. Input webhooks. Ensemble-sync primitives (RFC S). OTEL + per-tenant fairness + Pause / Resume / Snapshot + multi-replica HA. Per-run named credentials + tool-use hooks. OSS multi-tenant authorization (RFC L, v0.17.0) across both the state and definition planes. The embedded React Web UI + the interactive terminal. TS + Python + n8n adapters. Homebrew + Docker distribution. Per-version detail:\n`REVISIONS.md` |\nv0.27.0 |\nInteractive runs survive leaving the terminal. Background-goroutine execution under `context.WithoutCancel` , re-attach via `GET /v1/runs/{run_id}/stream` (replay-from-`?from_seq` + live-tail). `Context op=self` reports the resolved provider + model. |\nv0.28.0 |\nPer-agent LLM (temperature / top_p / top_k / penalties / seed / stop, set via yaml or AgentDef overlay or per-run). And `sampling` : the loop parks at an iteration boundary and `pause` cooperative quiesce`Pause()` waits for in-flight runs, so a mid-run snapshot is reliable. |\nv0.29.0 |\nWeb UI + operability. Agent-editor sampling controls + a collapsible advanced JSON/YAML overlay, terminal message-echo + a context-size gauge, and soft reclaim of a retired agent name (no new runtime primitives). |\nv0.30.0 |\nCross-instance resume of a snapshotted mid-run (RFC X Phase 2). A paused run is re-dispatched by reconstructing its loop from the transcript, fired after a snapshot restore and at boot (crash recovery, cluster-gated). |\nv0.31.0 |\nPark + resume a fan-out parent blocked in `Agent.parallel_spawn` (RFC X Phase 3). Pause-watcher + a no-schema-change spawn ledger, gated behind `LOOMCYCLE_RESUME_FANOUT` . |\nv0.32.0 |\nContext-compaction subsystem. Replace older turns with a summary + keep-last-N verbatim (clean user-turn boundary, non-destructive). Manual / auto / self triggers and a per-agent `compaction` block that flows down the spawn tree. |\nv0.33.0 |\nExternal fan-out and the run-mutation surface on every transport. `POST /v1/runs:batch` , a `spawn_runs` MCP tool (≤32 server-concurrent), a `SpawnRunBatch` RPC. `compact_run` / `CompactRun` . Per-run sampling + compaction on MCP/gRPC. `@loomcycle/client` 0.33.0. |\nv0.34.0 |\nContext-transform plugins (RFC Z Phase 1a, with the `redact` outbound-secret-scrub plugin), an exp7 self-review hardening pass, and a cross-provider thinking-model fallback downgrade (`reasoner` → `chat` ). |\nv0.34.1 |\nHardening + branding (no new features). A central tenant-scoping store accessor closing three live cross-tenant read gaps (security review S2), plus the new loomcycle brand logo and favicon in the Web UI. |\nv0.34.2 |\nWeb UI design system + theming. A tokenized `--lc-*` design system (spacing / type / radius / shadow / fonts + semantic colors), light + dark themes (OS default + a persistent topbar toggle), bundled brand fonts (Outfit / Inter / JetBrains Mono), and the loom-wood accent. Plus a loop fix (interactive runs unbounded by default; no more stop-at-16) and a `#56c596` `-race` test de-flake. |\nv0.34.3 |\nPatch. `Context op=self` reports a fresh context footprint right after a compaction. It had kept showing the stale pre-compaction size (e.g. `164k / 82%` ) for one turn even though the wire request had already shrunk. The loop now refreshes `lastCtxTokens` at every compaction site. |\nv0.34.4 |\nPatch. Interactive-terminal UX + local-model fixes: a collapsible interactive-sessions switcher on the run page (and `interactive` tags on the runs page), a flashing waiting indicator in the terminal, the Ollama context gauge backed by `num_ctx` , generous (300s), relative `ollama-local` timeoutssandbox paths anchored to the root (not the process cwd), and the static agent base re-surfaced in the Library when no dynamic version is active. |\nv0.34.5 |\nPatch. Ollama now reports the model's actual loaded context window (read from `/api/ps` at the stream's done frame) instead of only a pinned `num_ctx` — so the gauge is truthful for local models loaded at the server's `OLLAMA_CONTEXT_LENGTH` ; plus docs (the architecture diagram + `ARCHITECTURE.md` show the context-transform plugin layer; a README v1.0 voice refresh). |\nv0.35.0 |\nModel aliases in tier candidates. A `models:` alias (e.g. `local-gemma` ) now resolves anywhere a tier candidate is accepted — per-agent `models:` , `user_tiers` , and the library `tiers:` — not only in an agent pin, closing the \"pin expands aliases but tier candidates don't\" 503. A candidate can be written as a bare alias string (`- local-gemma` ); config-load validation and the Web UI Library editor know aliases too. |\nv0.36.0 |\nJailed agents can see their sandbox. The `Read` /`Write` /`Edit` /`Bash` path docs now instruct relative paths (they wrongly said \"Absolute file path,\" so agents passed host paths that resolve outside the jail), and reports the sandbox roots (`Context op=self` `read_root` /`write_root` /`bash_cwd` ) + the effective host allowlist. Plus a collapsible left column on the run page so the live terminal can use the full width. |\nv0.37.0 |\nSlow-local-model robustness. Two loop fixes that keep an interactive run on a slow local model alive for hours: a run-lifetime heartbeat (a long prefill / retry no longer gets reaped as a crashed run) and a compaction window cap (auto-compaction always fits the model's window instead of \"succeeding\" yet still overflowing). Plus an aliases-first `loomcycle.example.yaml` , a \"Local models (Ollama)\" config guide + a `loomcycle.local-interactive.example.yaml` . Validated by a 133-min standalone local run. |\nv1.0.0 |\n🌳 1.0 — feature-complete, hardened, distribution-ready. The milestone the roadmap pointed at: no new primitives, the culmination of the v0.8→v0.37 primitive + hardening line. Apache-2.0, wired to Homebrew / multi-arch Docker / the Claude Code plugin. Same codebase as v0.37.0, tagged as the stable 1.0. |\n\nFull per-version log: [ REVISIONS.md](/denn-gubsky/loomcycle/blob/main/REVISIONS.md).\n\nSame Go binary, same config schema. Operator flips a few env vars to pick the posture.\n\n| Posture | Configuration shape | Use case |\n|---|---|---|\nTrue managed sandbox |\n`LOOMCYCLE_BASH_ENABLED=0` , `LOOMCYCLE_READ_ROOT` / `LOOMCYCLE_WRITE_ROOT` unset, `LOOMCYCLE_HTTP_HOST_ALLOWLIST` empty, `LOOMCYCLE_HTTP_CALLER_AUTHORITATIVE=1` . Every tool default-deny; agents can only reach what the caller's per-request `allowed_hosts` says. |\nShared-server deployments processing untrusted prompts. The runtime survives contact with adversarial input. |\nAgentic dev environment |\nBash enabled, filesystem roots set to your workspace, broad `allowed_hosts` , optional local Ollama for offline work. |\nLocal development. Internal trusted operators. Single-user research workstation. |\n\nThe trust boundary is **operator / caller**. The operator config is the floor; callers can narrow per-request but never widen. The bearer token (`LOOMCYCLE_AUTH_TOKEN`\n\n) is the authority. Treat anyone with the token as fully trusted to drive the runtime. For true isolation in the sandbox posture, run loomcycle inside a container or VM. `Bash`\n\nis restricted (cwd, env scrub, output bounds, timeouts) but it is **not** a kernel-level sandbox.\n\nPick the path that fits. All four ship the same single static binary\nplus the v0.11.1 `init`\n\n/ `doctor`\n\nfirst-run flow. `Context.help installation`\n\ncovers each in detail.\n\n```\n# Homebrew (macOS + Linux)\nbrew install denn-gubsky/loomcycle/loomcycle\n\n# Docker (v0.11.2+; pull works on amd64 + arm64 including Apple Silicon)\ndocker pull denngubsky/loomcycle:latest\n\n# go install from source (skips Web UI embedding — for dev only)\ngo install github.com/denn-gubsky/loomcycle/cmd/loomcycle@latest\n\n# Direct tarball (one of darwin-arm64 / darwin-amd64 / linux-arm64 / linux-amd64)\ncurl -L https://github.com/denn-gubsky/loomcycle/releases/latest/download/loomcycle-darwin-arm64.tar.gz | tar xz\nloomcycle init --with-token   # writes config + mints a token to ~/.config/loomcycle/auth.env (0600)\nexport ANTHROPIC_API_KEY=sk-...   # (or OPENAI_API_KEY / DEEPSEEK_API_KEY) — at least one provider key\nloomcycle doctor              # verify env + keys + storage + the just-minted token\nloomcycle                     # starts on 127.0.0.1:8787 (auto-loads auth.env — no shell-rc edit)\n```\n\n`init --with-token`\n\nprints the Web UI URL (`http://127.0.0.1:8787/ui`\n\n). Open it, then paste the token from `~/.config/loomcycle/auth.env`\n\nat the login prompt. (The token is kept in the `0600`\n\nfile and never embedded in a URL. A `?token=`\n\nlink would leak the bearer into browser history and into any fronting proxy's logs.) `loomcycle`\n\nand `loomcycle doctor`\n\nboth auto-load `auth.env`\n\nfrom the config dir; a real `export LOOMCYCLE_AUTH_TOKEN=…`\n\nalways overrides it.\n\nPick the tier that fits. Each is a superset of the one above. **Auth is enforced only once something is configured**, so Tier 1 needs no token at all.\n\nNo token, no flags. Fastest way to kick the tires on `127.0.0.1`\n\n.\n\n```\nloomcycle init               # config only — no secret written\nexport ANTHROPIC_API_KEY=sk-...\nloomcycle                    # open mode: /v1/* + /ui pass through unauthenticated (logs a warning)\nopen http://127.0.0.1:8787/ui\n```\n\nWith no `LOOMCYCLE_AUTH_TOKEN`\n\nand no minted tokens, the runtime runs **open** on localhost. Every request is allowed, whoami returns a synthetic admin. Good for a 10-second smoke test. **Never** expose this off localhost.\n\nOne bearer gates everything. `init --with-token`\n\nis the easy button (above). Equivalent manual setup:\n\n```\nloomcycle init\nexport LOOMCYCLE_AUTH_TOKEN=$(openssl rand -hex 32)   # or: loomcycle init --with-token\nexport ANTHROPIC_API_KEY=sk-...\nloomcycle\nopen \"http://127.0.0.1:8787/ui?token=$LOOMCYCLE_AUTH_TOKEN\"   # sets the cookie once\n```\n\nTreat anyone holding the token as fully trusted to drive the runtime.\n\nMint a distinct bearer per developer / app, each bound to an authoritative `(tenant, subject, scopes)`\n\n. Migrate a Tier-2 deployment in place, no downtime:\n\n```\n# promote your existing shared token into the substrate, then mint scoped tokens\nloomcycle operator-token create --copy-from-env --name ops --tenant ops --scopes substrate:admin\nloomcycle operator-token create --name acme-app --tenant acme --subject alice --scopes runs:create\n```\n\nThe first admin `OperatorTokenDef`\n\ndisables the legacy shared-token fallback. Per-route HTTP and per-RPC gRPC scopes. The Web UI becomes role-aware (super-admin vs tenant). See `Context.help operator-tokens`\n\nand the v0.17.0 notes in [ REVISIONS.md](/denn-gubsky/loomcycle/blob/main/REVISIONS.md).\n\n**Smoke any tier:**\n\n```\ncurl http://127.0.0.1:8787/healthz\n# {\"ok\":true}\n```\n\nReal call (from another terminal):\n\n```\ncurl -N http://127.0.0.1:8787/v1/runs \\\n  -H \"Authorization: Bearer $LOOMCYCLE_AUTH_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"agent\":\"default\",\"segments\":[{\"role\":\"user\",\"content\":[{\"type\":\"trusted-text\",\"text\":\"Hello\"}]}]}'\n```\n\nBuild from a checkout (for development):\n\n```\nmake build-all       # UI + binary in one shot; output → ./bin/loomcycle\n./bin/loomcycle --config loomcycle.example.yaml\n```\n\n**Multi-replica cluster demo (v0.12.x).** For a one-command `docker compose up`\n\ncluster (2 loomcycle replicas, Postgres, nginx LB) with a verify script, see [ examples/cluster/README.md](/denn-gubsky/loomcycle/blob/main/examples/cluster/README.md). Full operator runbook in\n\n[.](/denn-gubsky/loomcycle/blob/main/docs/MULTI-REPLICA.md)\n\n`docs/MULTI-REPLICA.md`\n\n**v1.0.0: feature-complete, hardened, distribution-ready.** 🌳 The 1.0 milestone — the point the roadmap pointed at since the multi-tenant-auth capstone (v0.17.0) and the substrate-completeness line (v0.18–v0.23). 1.0 adds **no new primitives**; it's the stable tag on the runtime that stabilised across v0.8→v0.23 (the providers, built-in tools, substrate Defs, triggers, multi-tenant authz, HA) and was hardened across v0.24→v0.37 (interactive terminal, pause/resume/snapshot + cross-instance resume, the context-compaction subsystem, context-transform plugins, external fan-out on every transport, slow-local-model robustness). Distribution is wired end-to-end: Homebrew + multi-arch Docker, `init`\n\n/ `doctor`\n\nfirst-run, the Claude Code plugin, the TS (`@loomcycle/client`\n\n) + Python adapters, and the embedded Web UI. Apache-2.0. Code-identical to v0.37.0 — 1.0 is the stability stamp, not a feature delta. Beyond 1.0 (unscheduled): a settings UI, an operator cookbook, Helm.\n\n**v0.37.0: slow-local-model robustness + aliases-first docs.** Two loop fixes from a slow-local-model stress test, plus a config/docs pass. (1) **Heartbeat during a model call (#502)** — `OnHeartbeat`\n\nfired only at iteration start, so a single iteration that blocked far longer than the cadence (a large-context prefill on a slow local model, or a same-provider retry) let the heartbeat go stale and the stale-run sweeper reaped the **live** run as `heartbeat_timeout`\n\n. A run-lifetime ticker now pulses every 30s while the run goroutine is alive; the HTTP timeouts remain the authority on a genuinely stuck call. (2) **Compaction caps the kept tail to the window (#503)** — auto-compaction could \"succeed\" yet leave the context still over the window (a tool-heavy agent's `keep_last_n`\n\ntail of huge file reads); the loop now folds the oldest kept turns into the summary until the tail fits ~half the window, so compaction always relieves pressure. (3) **Aliases-first config + local-model guide (#504)** — `loomcycle.example.yaml`\n\nrestructured aliases-first, a new `docs/CONFIGURATION.md`\n\n§6b \"Local models (Ollama)\" (the global `num_ctx`\n\nknob, header/idle timeouts, compaction tuning), a new `loomcycle.local-interactive.example.yaml`\n\n, and a runs-page chip fix. Validated by a **133-minute** standalone interactive `code-reviewer`\n\nrun on a local GPU; the one edge it surfaced (compaction can't reduce a single indivisible tool-chain) is captured for a follow-up. Runtime + docs; no `@loomcycle/client`\n\nbump.\n\n**v0.36.0: jailed agents can see their sandbox + a roomier run terminal.** Two features. (1) Agents jailed to a filesystem sandbox kept addressing files with absolute host paths, which resolve outside the root and fail — because the `Read`\n\n/`Write`\n\n/`Edit`\n\npath params said \"Absolute file path\" (what the model reads every turn) even though the resolver anchors *relative* paths to the root. The descriptions now instruct relative paths, and ** Context op=self** additionally reports the jail (\n\n`sandbox.read_root`\n\n/`write_root`\n\n/`bash_cwd`\n\n+ the relative-path convention) and the granted host allowlist (`network.allowed_hosts`\n\n+ `source`\n\n), so an agent can introspect its sandbox instead of guessing. `docs/TOOLS.md`\n\nis corrected too (it documented the old relative-to-process-cwd behaviour). (2) The run page's left column (interactive-sessions switcher + run form) is now **collapsible** to a thin strip showing the running-interactive count, so the live terminal can use the full width during a run. Runtime + Web UI + docs; op=self fields are additive; no\n\n`@loomcycle/client`\n\nbump (#499, #500).**v0.35.0: model aliases work in tier candidates.** A `models:`\n\nalias (e.g. `local-gemma → {ollama-local, gemma4:max}`\n\n) now resolves anywhere a tier candidate is accepted — per-agent `models:`\n\n, `user_tiers.*.tiers`\n\n, and the library `tiers:`\n\n— not only in an agent pin. Previously a tier candidate naming an alias failed with `503 no provider available for requested tier`\n\nbecause the resolver matched the model string literally. Expansion now happens at the two resolver-entry boundaries through one shared helper (the same rule the pin path uses), covering HTTP, gRPC, and dynamic / Web-UI / MCP-authored agents. A candidate can also be written as a bare alias string (`- local-gemma`\n\n), and both config-load validation and the Web UI Library editor understand aliases. Wire-shape-neutral; no `@loomcycle/client`\n\nbump (#497).\n\n**v0.34.5 patch: Ollama reports the model's real loaded context window.** Runtime + docs. The `ollama`\n\n/ `ollama-local`\n\ncontext gauge previously showed a window only when `LOOMCYCLE_OLLAMA_LOCAL_NUM_CTX`\n\nwas pinned — and that knob is also sent as `options.num_ctx`\n\n, so it both caps and reports the context, overriding the ollama server's own setting. With it unset, loomcycle reported \"unknown\" even when ollama loaded the model at a larger window (a 256K-trained model running at a real 128K). The driver now reads the **actual loaded context_length from ollama's /api/ps** at the stream's done frame (the model is in VRAM by then) and stamps it on the usage event — so the gauge reflects what ollama actually allocated. An explicit\n\n`num_ctx`\n\nstill wins; a not-yet-loaded model reports 0 (\"unknown\"); per-model cached, best-effort (gauge-only, never correctness). The loop prefers this per-call window over the static capability default (#495). Docs: the architecture diagram + `ARCHITECTURE.md`\n\nnow show the context-transform plugin layer (#493), plus a README v1.0 voice refresh (#494). No `@loomcycle/client`\n\nbump.**v0.34.4 patch: interactive-terminal UX, local-model fixes, two sandbox/Library fixes.** Web UI + runtime; no new primitives.\n\n**(1) Interactive-session switcher (#491).** Leaving the `/run`\n\nterminal stranded an interactive run behind the runs-page \"resume in terminal\" link. The run page (Single tab) now has a collapsible **Interactive sessions** list in its left column. Each row is one of the operator's running interactive sessions, re-attaching in the terminal on click (replay + live-tail), the current one marked *open*, collapsed shows \"N interactive sessions\". The runs page (tree + detail) gains an ** interactive** tag. Backed by an additive\n\n`interactive`\n\nfield on the `GET /v1/users/{id}/agents`\n\nrow.**(2) Terminal waiting indicator (#488).** A flashing indicator with an adaptive label (\"running `<tool>`\n\n…\" vs \"waiting for the model…\") while the agent works, so a slow turn doesn't look stalled.\n\n**(3) Local Ollama (#488).** The context gauge renders the operator-pinned `num_ctx`\n\nas the window (`Capabilities().MaxContextTokens`\n\nwas hard-coded `0`\n\n), and `ollama-local`\n\ngets generous **300s / 300s** default timeouts (overridable via `LOOMCYCLE_OLLAMA_LOCAL_HEADER_TIMEOUT_MS`\n\n/ `_IDLE_TIMEOUT_MS`\n\n) so a cold local model isn't cut off before its first token.\n\n**(4) Sandbox paths (#489).** The file tools resolved a relative path against the loomcycle *process* cwd, disagreeing with the Bash tool (cwd = jail). This let a relative tool path land outside the sandbox (cryptic `ENOTDIR`\n\nwhen a like-named file sat at the server's cwd). Relative targets now anchor to the **sandbox root**. Absolute paths and the symlink-escape guard are unchanged.\n\n**(5) Static agent base in the Library (#490).** A static agent buried under retired/inactive dynamic versions (no active pointer) is now surfaced as the **effective** lineage row and is editable/forkable again (\"Edit (forks from yaml)\"). The runtime already resolved it; this was a UI visibility fix.\n\nNo `@loomcycle/client`\n\nbump.\n\n**v0.34.3 patch: Context op=self footprint is fresh after a compaction.** A compaction rewrites the loop's in-memory history to\n\n`[summary, ack] ++ last-N`\n\n. But the **context footprint** the loop tracks (\n\n`lastCtxTokens`\n\n) was only ever updated from a *completed*provider turn's usage. That's the value\n\n`Context op=self`\n\nreports as `used_tokens`\n\n/ `used_pct`\n\n, and the same value the auto-compact threshold reads. So for one turn after a compaction the agent kept reporting its **pre-compaction** size (e.g.\n\n`~164k / 82%`\n\n) even though the actual outbound request had already shrunk (the operator saw `in=1504`\n\non the wire).The loop now refreshes `lastCtxTokens`\n\n(via `estimateMessageTokens`\n\n, the same estimator behind the `context_compaction`\n\nevent's before/after numbers) at **every** compaction site: the parked-run `steer.KindCompact`\n\npath, the running-run `drainSteer`\n\n`KindCompact`\n\npath, and the inline auto/self `maybeAutoCompact`\n\npath. The footprint stamp also moves below the loop's compaction block so a *same-turn* `op=self`\n\nis correct too. Fail-before regression test (`TestRun_Interactive_ContextUsageRefreshedAfterCompaction`\n\n). Runtime-only; no `@loomcycle/client`\n\nbump.\n\n**v0.34.2: Web UI design system + light/dark theming.** No runtime primitives. A Web UI design pass and two small fixes.\n\n**(1) Tokenized design system.** A new `--lc-*`\n\ntoken layer (`web/src/tokens.css`\n\n) mirrors the brand design system: spacing, type-scale, radius, shadow, fonts, semantic colors. The legacy `--bg`\n\n/ `--fg`\n\n/ `--accent`\n\n/ … names become aliases of the themed tokens, so the whole stylesheet themes for free.\n\n**(2) Light + dark themes.** Default follows the OS `prefers-color-scheme`\n\n. A persistent topbar sun/moon toggle overrides it (localStorage), with a pre-paint script so there's no flash-of-wrong-theme. Dark is the current palette verbatim; light ships as a functional basic-neutral palette (the brand-cream refinement is the next step).\n\n**(3) Brand fonts + accent.** Self-hosted, bundled Outfit (display), Inter (body), JetBrains Mono (code). No CDN, embedded, offline-safe. The accent moves from light-blue `#5b9dff`\n\nto the loom-wood brand green ** #56c596** everywhere (CSS + charts). Form controls and the Activity charts theme against the tokens, and the topbar wordmark swaps per theme (near-white on dark, black-ink on light).\n\n**(4) Fixes.** Interactive runs are now **unbounded by default** (an interactive terminal no longer stops after 16 turns with `max_iterations`\n\n; the runaway guard was never meant for an operator-driven, Cancel-bounded session, and an explicit `max_iterations`\n\nis still honored). The `TestSchedulerBearerCompound`\n\n`-race`\n\nload-flake is fixed at the root (the 310-scale load test is capped under `-race`\n\n). The agent editor's **advanced (raw overlay) now round-trips visibly**: it pre-fills from the source on reopen instead of starting empty (the overlay was always persisted; the empty box just made it look unsaved).\n\nWeb-UI / CI only; no `@loomcycle/client`\n\nbump.\n\n**v0.34.1: hardening + branding (no new features).** A security-hardening and cosmetic release on the road to v1.0.\n\n**(1) Central tenant-scoping (security review S2).** The per-handler tenant-isolation convention (`tenantVisible`\n\n/ `sessionOwnershipOK`\n\n) becomes a single choke-point: a per-request ** tenantScopedStore** accessor that folds a cross-tenant row into an opaque\n\n`*store.ErrNotFound`\n\n(no existence oracle). Three **live cross-tenant read gaps** are closed: the run-scoped interrupt list (\n\n`GET /v1/runs/{id}/interrupts`\n\n), the user interrupt inbox (`GET /v1/users/{id}/interrupts`\n\n, via a new `tenantID`\n\narg on `store.InterruptListByUser`\n\n), and the user run-state stream (`GET /v1/users/{id}/agents/stream`\n\n, via a `TenantID`\n\non the run-state event + a filter). Per-user channel routes are gated to the principal's own subject. The whole-tenant model is preserved: same-tenant subjects collaborate, super-admin sees all.**(2) New brand identity in the Web UI.** The topbar shows the new loomcycle **wordmark logo** (top-left; the dark-theme variant has the wordmark recoloured to the theme foreground, loom-mark colours kept) and the new **favicon**.\n\nRuntime change is server-side only; no `@loomcycle/client`\n\nbump.\n\n**v0.34.0: context-transform plugins, exp7 (v0.33.0 re-run) hardening, and a cross-provider thinking-fallback fix.** One new primitive, one hardening line, one fix line.\n\n**(1) Context-transform plugins (RFC Z Phase 1a).** A runtime-wide plugin chain sits between the agent's assembled context and the outbound LLM request, transforming a **copy**. Deterministic, copy-on-write; the caller's history is never mutated. The synthetic `code-js`\n\nprovider is exempt so replay stays byte-stable. Phase 1a ships the ** redact** plugin: outbound secret scrubbing that reuses the F32\n\n`redact.Redactor`\n\n. Tier-A is exact env-value masking; Tier-B is heuristic patterns (`Authorization`\n\n, `sk-`\n\n, `AKIA`\n\n, `xox`\n\n, `ghp_`\n\n, `key=value`\n\n). The model never sees a configured secret, even when it leaks into history. Configured via a top-level `context_plugins:`\n\nblock.**(2) exp7 self-review re-run hardening.** Every finding from a 10-agent fan-out review was independently verified against `main`\n\n(≈9 of ~40 refuted on verification, including a Go-1.24 `crypto/rand`\n\n-never-errors catch). The confirmed set landed as correctness / robustness fixes: admin POST `MaxBytesReader`\n\ncaps, `ExportPretty`\n\nchecksum, an OAuth-refresher `Stop()`\n\n-before-`Start()`\n\ndeadlock, a `HeartbeatRunner`\n\ncancel race, a bounded backplane publish in `Bus.Notify`\n\n, scheduler `on_complete`\n\nhooks on the survival ctx, pause `Glob`\n\n/`Grep`\n\nidempotency, an absolute-in-root `Glob`\n\npattern (R1), evaluation-`dimensions`\n\nparse logging, cmd shutdown-budget + SSE-safe `IdleTimeout`\n\n. Plus a dead-code / cosmetic cleanup sweep.\n\n**(3) R2: cross-provider thinking-model fallback downgrade.** A DeepSeek thinking model (`deepseek-reasoner`\n\n/ `*-pro`\n\n) 400s (\"reasoning_content must be passed back\") when a fallback hands it a history whose assistant turns lack `reasoning_content`\n\n(a foreign provider produced them, or the reasoning strip zeroed them). A new optional `providers.ThinkingDowngrader`\n\nlets the loop downgrade to the non-thinking sibling (`reasoner`\n\n→ `chat`\n\n, `*-pro`\n\n→ `*-flash`\n\n) for that leg, emitting a new `model_downgraded`\n\nevent.\n\n**(4) @loomcycle/client 0.34.0.** Version-aligned lockstep release; no client-surface change. The new event passes through the generic stream; context plugins are server config.\n\n**v0.33.0: external fan-out, the run-mutation surface on every transport, and exp7 hardening.** Three feature lines and a self-review fix line.\n\n**(1) RFC Y external fan-out.** ** POST /v1/runs:batch** and a\n\n**MCP tool (mode**\n\n`spawn_runs`\n\n`\"join\"`\n\n) spawn up to 32 fresh runs **server-side concurrent** in one call. Bounded by the existing per-user admission gate. Returns a combined\n\n**index-aligned** envelope once all settle. A per-child failure rides in-envelope (\n\n`status`\n\n+ `error`\n\n), never failing the batch. The batch caller's authoritative tenant stamps every child. Replaces the \"fire N serializing `spawn_run`\n\ncalls\" / in-loomcycle-dispatcher workaround. (`mode: \"detach\"`\n\nawaits RFC P. `timeout_ms`\n\ncaps the join.)**(2) Compaction + sampling on every transport.** A ** compact_run** MCP tool and a\n\n**gRPC RPC (the**\n\n`CompactRun`\n\n`POST /v1/runs/{id}/compact`\n\nop lifted to a shared `connector.CompactRun`\n\n; HTTP byte-identical). Per-run **and**\n\n`sampling`\n\n**overrides now on**\n\n`compaction`\n\n`spawn_run`\n\n/ `spawn_runs`\n\n(MCP), the gRPC `RunRequest`\n\n/ `ContinueRequest`\n\n(proto3 `optional`\n\n, so `temperature: 0`\n\nstays deterministic), and `@loomcycle/client`\n\n's `runStreaming`\n\n/ `continueSession`\n\n.**(3) @loomcycle/client 0.33.0.** New\n\n`spawnRunBatch()`\n\n+ `compactRun()`\n\n+ per-run sampling / compaction (52 → 54 methods).**(4) exp7 self-review.** Tenant-scope `DynamicAgentDelete`\n\n(cross-tenant delete), infra-secret YAML-expand deny + newline reject, `/v1/runs/{id}/input`\n\nscope-gate, scheduler error logging, an O(1) `ChannelGet`\n\n, an additive snapshot checksum, and three smaller hardening fixes.\n\n**v0.32.0: context compaction subsystem (keep-last-N, auto-compact, per-agent settings + spawn inheritance).** A long session crowds the model's window. Compaction summarizes older turns and continues from the summary. The loop's in-memory history is replaced with `[pinned task? + summary, ack] ++ last-N kept verbatim`\n\n, snapped to a clean user-turn boundary so a `tool_use`\n\n/ `tool_result`\n\npair is never split. A `context_compaction`\n\nmarker records summary + keep-N / keep-first so `replayTranscript`\n\nrebuilds the identical form on resume (full transcript retained, non-destructive).\n\nFour triggers, one shared summarizer (`loop.Summarize`\n\n):\n\n**manual.** The`/run`\n\nCompact button calls`POST /v1/runs/{id}/compact`\n\n.**auto.** When`compaction.enabled`\n\nand the footprint crosses`autocompact_at_pct`\n\n, the loop compacts inline at a boundary. Works for**autonomous** runs too. Off by default, self-debouncing.**self.**`Context op=compact`\n\n.\n\nAll three emit the marker and an OTEL `context.compaction`\n\nspan event.\n\nThe per-agent ** compaction** block mirrors\n\n`sampling`\n\n: yaml, AgentDef overlay, per-run, content-identifying, exposed by `Context op=self`\n\n. Fields: `enabled`\n\n, `target_percentage`\n\n(10-50), `keep_last_n`\n\n, `keep_first`\n\n, `autocompact_at_pct`\n\n(50-95), optional cheaper summary `model`\n\n. The block **flows down the spawn tree**: a child inherits the parent's effective policy, its own def fills the gaps, and the parent overrides per-spawn via the Agent tool. Bundles the\n\n`/run`\n\ncomposer restyle (#458), the ✕ Stop button (#459), and the manual Compact button (#460). Runtime-only; no `@loomcycle/client`\n\nbump.**v0.31.0: park + resume a fan-out parent blocked in parallel_spawn (F42 / RFC X Phase 3).** Closes the documented Phase-2 deferral. A run blocked inside\n\n`Agent.parallel_spawn`\n\n→ `wg.Wait()`\n\nis *inside a tool call*, not at the loop's only park point, so on pause it never parked.\n\n`paused_runs_count`\n\nexcluded it, the pause Manager warned \"fan-out PARENT … did not reach a pause boundary\", and a mid-fan-out snapshot missed the parent. v0.31.0 fixes both sides, **gated behind**(default OFF, so existing behavior is byte-identical until opt-in).\n\n`LOOMCYCLE_RESUME_FANOUT`\n\n**(1) Capture.** A pause-watcher goroutine in `executeParallelSpawn`\n\ncalls the existing `PauseGate.Park`\n\non pause and unparks on resume, **without touching wg.Wait** or result collection. The parent now counts as parked (warning gone, count accurate) and a same-instance pause → resume mid-fan-out just works.\n\n**(2) Durability.** A two-event **spawn ledger** on the parent transcript: `spawn_child_started`\n\nis index → run_id, `spawn_child_result`\n\nis a child that finished pre-snapshot. Rides the existing event emitter, **no schema change**, ignored by `replayTranscript`\n\n.\n\n**(3) Resume.** `resumePausedRun`\n\ndetects the parked fan-out parent. In its background goroutine, before taking a run slot (no semaphore deadlock), it reconciles each child (durable ledger result, else **awaits** the re-dispatched child to terminal and reads its transcript), synthesizes the byte-compatible `{\"results\": [...]}`\n\nenvelope, and seeds it into `PriorMessages`\n\nso the loop continues. Edges: pre-snapshot completion, gone agent (error result), never-dispatched-past-cap (re-issuable error), depth > 1.\n\nDeferred: a mixed tool turn (parked fan-out + sibling in-flight tools) is flagged for manual re-attach. Default-ON awaits exp6.5 re-validation. Runtime-only; no `@loomcycle/client`\n\nbump.\n\n**v0.30.0: cross-instance resume of a snapshotted mid-run (F42 / RFC X Phase 2).** v0.28.0 made `pause`\n\nquiesce in-flight runs (Phase 1) so a mid-run snapshot is reliable. But a snapshotted `pause_state='paused'`\n\nrun restored on another instance was **data only**: nothing relaunched its loop. `POST /v1/_resume`\n\nreturned `409 not_paused`\n\n, and a restart didn't relaunch it.\n\nv0.30.0 **re-dispatches paused runs by reconstructing their loop from the transcript**. `ResumePausedRuns`\n\nre-resolves the agent, replays the transcript into `PriorMessages`\n\n, flips the row to `running`\n\n, re-registers cancel / pause / steer, and re-enters `loop.Run`\n\nunder the **existing run_id** in a background goroutine. It fires after a **snapshot restore** (response reports `paused_runs_resumed`\n\n) and at **boot** for crash recovery (cluster-gated by an advisory lock so one replica resurrects each run).\n\nA new additive `runs.interactive`\n\ncolumn (captured + restored) preserves park-vs-complete semantics. The stale-run sweeper now skips parked (`paused`\n\n/ `pausing`\n\n) runs so they aren't killed before resume.\n\nLimitations: per-run secrets (`user_bearer`\n\n/ credentials) and call-time overrides (allowed_hosts / sampling / metadata) aren't snapshotted; they're re-derived from the agent def. A run idle-awaiting-input when paused is flagged for manual re-attach, not auto-resumed. Runtime-only; no `@loomcycle/client`\n\nbump.\n\n**v0.29.1 patch: adapter lockstep publish.** The additive `max_context_tokens`\n\nusage-event field (v0.29.0) shipped in the runtime but the `@loomcycle/client`\n\nnpm publish skipped. The publish workflow only fires when the adapter's `package.json`\n\nversion equals the release tag, and it was at `0.26.0`\n\n. v0.29.1 realigns the adapter version with the tag so `@loomcycle/client@0.29.1`\n\npublishes with the field. No runtime change (binaries identical to v0.29.0).\n\n**v0.29.0: Web UI + operability (agent-editor controls, terminal UX, soft reclaim).** Three operator-facing improvements (no new runtime primitives).\n\n**(1) Agent editor (#449).** Dedicated **sampling** controls (temperature / top_p / top_k / penalties / seed / stop, string-typed so blank means unset and `0`\n\nmeans explicit). Plus a collapsible **advanced JSON / YAML overlay** for the long tail (channels, interruption, `*_def_scopes`\n\n, …) the substrate already accepts. Empty box never blocks; a malformed non-empty body blocks inline.\n\n**(2) Interactive terminal (#450).** Your own messages now echo into the transcript (`❯ …`\n\nfor initial prompt, steer, continue; they were filtered from the live tail). Plus a **context-size gauge** `ctx 47.2k / 200k (24%)`\n\nbacked by an additive `max_context_tokens`\n\non the usage event. `@loomcycle/client`\n\npublishes the field in **0.29.1** (the v0.29.0 adapter publish skipped on a version mismatch); gRPC parity is a fast-follow.\n\n**(3) Soft reclaim (#452).** Retiring the active agent now clears the active pointer so the **name is reclaimable**. Recreate to grant more tools: a fork can't widen the `allowed_tools`\n\nceiling, a fresh create can. Also fixes a latent bug where a retired-but-active def was still served to runs. The Library badges inactive / retired names. No hard delete; full audit lineage preserved.\n\nPlus a bundled `examples/exp6-self-evolving-agents/`\n\n(#451). `allowed_hosts`\n\nper-run narrowing already lives in the run launcher (caller-authoritative).\n\n**v0.28.0: per-agent LLM sampling, and pause actually quiesces.** Two features.\n\n**(1) Per-agent sampling.** A grouped `sampling:`\n\nblock (`temperature`\n\n, `top_p`\n\n, `top_k`\n\n, `frequency_penalty`\n\n, `presence_penalty`\n\n, `seed`\n\n, `stop`\n\n), settable via static yaml, the AgentDef substrate (create / fork; this is how a self-evolving breeder mints variants), and per-instance on `POST /v1/runs`\n\n. Overlays / overrides merge **per field**: per-run > per-agent > provider default. (`temperature: 0.0`\n\nis deterministic, not unset.) Each driver maps what its provider supports; Anthropic drops temperature when `effort`\n\nengages thinking. Content-identifying. Reported back via `Context op=self`\n\n.\n\n**(2) pause cooperative quiesce (F41 / RFC X).** The run loop now parks at a clean iteration boundary.\n\n`Pause()`\n\nwaits (up to `timeout_ms`\n\n) for in-flight runs to park, so `paused_runs_count`\n\nis accurate. New runs are 503-gated on `/v1/runs`\n\n, gRPC, and webhook / A2A. That makes \"pause + snapshot a mid-run\" reliable. (A fan-out parent mid-`parallel_spawn`\n\nis the documented Phase-2 deferral.) Runtime-only; no `@loomcycle/client`\n\nbump.**v0.27.2 patch: collapsed tool results actually fold.** The collapsed `tool_result`\n\nsummary used `oneLine(text)`\n\n(flattens whitespace, doesn't truncate), so a large result showed in full whether folded or not. It's now truncated to a 100-char summary like `tool_call`\n\n. The full output stays in the expand. Frontend-only.\n\n**v0.27.1 patch: interactive-terminal Web UI polish.** Three follow-ups to the v0.27.0 terminal. The transcript now **auto-scrolls** to follow live output. (Streaming text coalesces into one line, so the old line-count trigger stalled the tail; it now follows the `events`\n\nstream with a stick-to-bottom ref that pauses when you scroll up.) ** tool_call collapses** to a one-line summary + expand-on-click, so a\n\n`Write`\n\n's full file body no longer floods the scrollback, matching `tool_result`\n\n. And the continue / steer box is a **multi-line**: Enter sends, Shift+Enter inserts a newline, auto-growing up to a cap. Frontend-only; no\n\n`<textarea>`\n\n`@loomcycle/client`\n\nbump.**v0.27.0: interactive runs survive leaving the terminal, and you can come back.** v0.26.x's interactive `/run`\n\nrun was bound to its HTTP request. Navigating to the runs menu closed the SSE stream, cancelled the request ctx, and killed the parked run.\n\nv0.27.0 runs an interactive run in a **background goroutine** under `context.WithoutCancel`\n\n. That keeps the request's auth / tenant values; the run isn't cancelled on disconnect and stops only via the cancel registry. It also adds ** GET /v1/runs/{run_id}/stream** to\n\n**re-attach**: replay from\n\n`?from_seq`\n\n+ live-tail, re-emitting stored events as SSE frames. `ScopeRunsRead`\n\n+ tenant-ownership gated. A new run-scoped incremental store read (`GetRunEventsSince`\n\n, sqlite + postgres) backs the tail. A **\"resume in terminal\"** link on a running agent in the runs list is the way back.\n\nTwo more changes ride along. ** Context op=self** now returns the resolved\n\n**and**\n\n`provider`\n\n**(non-secret introspection, stamped per-iteration so it's fallback-truthful). The terminal UI caps its height (inner-scroll instead of growing the page) and**\n\n`model`\n\n**collapses tool results**(one-line summary + expand-on-click; operator / agent messages stay full).\n\nBehaviour note: the steer *echo* frame is no longer on the live wire for interactive runs (steering itself is unchanged). Single-replica re-attach (DB-backed deployments work cluster-wide since events are in the shared store). Cross-replica + multi-viewer deferred. Adds one GET route; no `@loomcycle/client`\n\nbump.\n\n**v0.4 → v0.26.x foundation and substrate build-out.** Summarized in the changelog table above. Full per-version detail in [ REVISIONS.md](/denn-gubsky/loomcycle/blob/main/REVISIONS.md).\n\n**Planned: v1.0 (hardening + distribution).**\n\n**No new features are planned before v1.0.** The feature set is complete. The remaining work is the in-progress test / QA + security-hardening pass across the shipped surfaces. Once that's green, the **v1.0** tag ships. No new primitives, no new wire surface. Every change from here is a fix, a test, or distribution polish. (With the multi-tenant-auth capstone shipped in v0.17.0, v1.0 is a pure hardening + distribution milestone.)\n\n8-hour stability soak completed: 1.27M circuits, 3.8M agent runs, 100% completion across 468 waves, zero leaks.\n\n**Distribution + bootstrapping.** A first-run install story that survives contact with a fresh machine. Hardened**Homebrew** formula and**Docker** images (multi-arch, the`init`\n\n/`doctor`\n\nflow, a sane default config), so`brew install`\n\n/`docker run`\n\ngets an operator to a working sidecar without reading the docs first.**Claude Code plugin hardening.** The`claude-code-plugin-loomcycle`\n\nplugin (slash commands + skills + hooks over`loomcycle mcp`\n\n) gets a hardening pass: error surfaces, version-skew handling, and a clean bootstrap from the published binary.**Security + runtime-QA pass** across the v0.13-v0.17 surfaces (A2A, webhooks, pluggable memory + the memory layer, the code-js provider, the multi-tenant-auth substrate). Then the**v1.0** tag.**Enterprise auth**(SSO / RBAC / SCIM / signed, queryable audit) stays out of scope for OSS. It's a separate edition built on the same`OperatorTokenDef`\n\nsubstrate.**Beyond**(polish): a settings UI, an operator cookbook of postures, broader distribution (Helm).\n\n**Post-v1.0 design RFCs.**\n\nThree named design lines beyond v1.0. Designs are drafted; locks and PRs come after the v1.0 hardening pass clears.\n\n**Context-compress plugin (RFC Z Phase 2).** A second built-in plugin in the same contextplugin chain that ships`redact`\n\nin v0.34.0. Operator-configurable, LLMLingua-style content compression at the per-turn boundary: tokenizer + content compressor compose with`redact`\n\n, transform a copy of the outbound request, leave the canonical history and the persisted transcript untouched. The seam is the same one auto-compaction already uses; the new dependency is a tokenizer.**In preparation.****SQL Memory (RFC AA).** A second facet of the Memory primitive: per-scope SQL databases the runtime owns, isolated from the main loomcycle store, that sandboxed agents query with arbitrary SQL. Two new Memory ops (`sql_query`\n\n,`sql_exec`\n\n), durable per-`(tenant, scope, scope_id)`\n\nplus an ephemeral per-run scratch DB, default-deny`sql_scopes`\n\nACL, statement timeouts, byte quotas, full audit. sqlite tier first (one file per scope, hardened authorizer + statement allowlist); postgres tier (one schema per scope in a separate aux DB) is Phase 2. The \"agents that need structured tables today need`Bash + sqlite3`\n\n\" gap.**Capability-based memory interface + mem0 backend (RFC K).** Generalize the v0.15.0`memory.Backend`\n\ncontract with an optional`MemoryLayer`\n\ncapability so LLM-extract memory products work natively (`add(messages) → recall(query)`\n\n) rather than degraded into a KV store. mem0 lands as the first`MemoryLayer`\n\nbackend (Apache-2.0, 57k★, daily commits). The flat`Backend`\n\nstays canonical; an external memory-layer backend declines`Backend`\n\nand serves`MemoryLayer`\n\nonly. Mem9 demotes to PREVIEW and re-targets at`MemoryLayer`\n\n.\n\nFull per-version release notes: [ REVISIONS.md](/denn-gubsky/loomcycle/blob/main/REVISIONS.md).\nPublic roadmap with v0.8.x → v1.0 design details:\n\n[.](/denn-gubsky/loomcycle/blob/main/docs/PLAN.md)\n\n`docs/PLAN.md`\n\nThree diagrams cover different views of the same runtime:\n\nDiagram source: [ docs/architecture.d2](/denn-gubsky/loomcycle/blob/main/docs/architecture.d2) (regenerate with\n\n`d2 docs/architecture.d2 docs/assets/architecture.png`\n\n).**Connector detail.** The v0.8.15 `Connector`\n\nabstraction layer (the pink block in the middle of the main diagram) is the architectural anchor every wire transport dispatches through. The detail diagram enumerates all 36 methods and shows which transports IMPLEMENT, CONSUME, and MIRROR the interface:\n\nSource: [ docs/architecture-connector.d2](/denn-gubsky/loomcycle/blob/main/docs/architecture-connector.d2).\n\n**Multi-replica cluster mode (v0.12.x).** When `LOOMCYCLE_REPLICA_ID`\n\nis set per process and the Postgres backend is used, loomcycle runs as a cluster behind any HTTP load balancer. The shared Postgres doubles as the LISTEN / NOTIFY backplane for cross-replica cancel, pause / resume, run-state fanout, and quota notifications. SQLite refuses cluster mode at boot.\n\nSource: [ docs/architecture-cluster.d2](/denn-gubsky/loomcycle/blob/main/docs/architecture-cluster.d2). Operator runbook:\n\n[. Demo:](/denn-gubsky/loomcycle/blob/main/docs/MULTI-REPLICA.md)\n\n`docs/MULTI-REPLICA.md`\n\n[.](/denn-gubsky/loomcycle/blob/main/examples/cluster/README.md)\n\n`examples/cluster/README.md`\n\nFull request flow, abstractions, and concurrency model: [ docs/ARCHITECTURE.md](/denn-gubsky/loomcycle/blob/main/docs/ARCHITECTURE.md).\n\n**TypeScript.**`npm install @loomcycle/client`\n\n(see). HTTP + SSE.`adapters/ts/`\n\n**Python.**`pip install loomcycle`\n\n(see). Async over`adapters/python/`\n\n`grpc.aio`\n\n.\n\n**No vendor binary** in the loop. Pure HTTP to provider APIs. No subprocess auth inheritance.**Default-deny everything.** Every built-in tool is disabled until env-configured. Every agent gets zero tools until`allowed_tools`\n\nis set.**Two-layer policy + per-request narrowing.** Operator floor in env; agent narrowing in yaml; caller narrowing per-run. Caller can never widen.**SSRF defence.** Hostname allowlist + RFC1918/loopback/link-local IP block at the dial layer. Defeats DNS rebinding.**Constant-time bearer auth.**`sha256+CTC`\n\non both HTTP and gRPC.Run inside a container or VM if you need real isolation.`Bash`\n\nis restricted, not isolated.\n\nFull security model and the two-layer default-deny walkthrough: [ docs/TOOLS.md](/denn-gubsky/loomcycle/blob/main/docs/TOOLS.md).\n\nRepo-side docs (this directory):\n\n. Request flow, provider abstraction, agent loop, sub-agents, skills, storage, concurrency, cancellation.`docs/ARCHITECTURE.md`\n\n. Two-layer default-deny model, every built-in tool, MCP / LocalAPI integrations, per-request narrowing.`docs/TOOLS.md`\n\n. End-to-end MCP HTTP pipeline: request lifecycle,`docs/MCP_INTEGRATION.md`\n\n`${run.user_bearer}`\n\nsubstitution, model-visibility boundary, recipe for wrapping a REST API as an MCP server consumable by loomcycle.. Register loomcycle as an MCP server in Claude Code / Claude Desktop. Copy-paste config snippets for Docker / Homebrew / direct-binary transports, plus the`docs/MCP_SERVER.md`\n\n`loomcycle mcp install`\n\nhelper.. Driving loomcycle from Claude Code: the recommended`docs/CLAUDE-CODE.md`\n\n`claude-code-plugin-loomcycle`\n\nplugin (slash commands + skills + hooks) vs. the manual`loomcycle mcp install`\n\npath.. Operator config guide: provider / tier / user_tier resolution rules, four cookbook patterns (single / multi-provider × single / multi-user-tier),`docs/CONFIGURATION.md`\n\n`models:`\n\nalias map, and the agent`.md`\n\nfrontmatter field reference.. Postgres backend operator guide: configuration, migrations, sqlite → postgres runbook, concurrency benchmark.`docs/POSTGRES.md`\n\n. gRPC surface: enablement, wire-shape parity with HTTP + SSE, error mapping, Python adapter quick-start.`docs/GRPC.md`\n\n. Public roadmap: shipped v0.4 → v0.8.12; planned v0.8.13 → v1.0.`docs/PLAN.md`\n\n. Per-version release notes (v0.4.0 onward).`REVISIONS.md`\n\n. Contribution policy (closed for external PRs until v1.x).`CONTRIBUTING.md`\n\n. Project guide for agents working in this repo (Claude Code).`CLAUDE.md`\n\nIn-binary docs (bundled `Context.help`\n\ntopics; agents read these directly, operators hit `GET /v1/_help/<topic>`\n\nagainst a running instance):\n\n`installation`\n\n. All four install paths (Homebrew, Docker,`go install`\n\n, direct tarball) with verification + troubleshooting.`getting-started`\n\n. First-run walkthrough:`init`\n\n→ set env vars →`doctor`\n\n→ run.`llm-gateway`\n\n. Direct LLM routing endpoint (v0.11.0; for n8n + LangChain consumers).`openai-compat`\n\n. Drop-in OpenAI SDK shims (v0.11.3 chat + v0.11.4 embeddings) with Python + TypeScript examples.`fairness`\n\n. Per-user concurrency quota policy.`observability`\n\n. OTEL trace export setup.`vector-memory`\n\n,`voyage-embedder`\n\n,`sqlite-vec`\n\n. Vector Memory backends.`dynamic-mcp`\n\n. Register MCP servers at runtime.`bash-security`\n\n. The Bash tool's restricted-not-isolated security posture.\n\nFull list via `GET /v1/_help`\n\nagainst a running instance.\n\nIf loomcycle is useful to you or your team, [GitHub Sponsors](https://github.com/sponsors/denn-gubsky) helps fund continued development. Individual supporters and corporate sponsors both welcome.\n\nThe runtime stays Apache-2.0 either way. Sponsorships fund the v1.x runway: Helm chart, operator cookbook, settings UI, and the sustained engineering that keeps the binary small and the substrate stable.\n\nCurrent sponsors are listed in [ BACKERS.md](/denn-gubsky/loomcycle/blob/main/BACKERS.md) (and at\n\n[loomcycle.dev/sponsors](https://loomcycle.dev/sponsors)with logos).\n\nApache-2.0. See [LICENSE](/denn-gubsky/loomcycle/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-loomcycle-a-sidecar-runtime-for-ai-agents-go-binary-apache-2-0", "canonical_source": "https://github.com/denn-gubsky/loomcycle", "published_at": "2026-06-17 03:26:02+00:00", "updated_at": "2026-06-17 03:53:00.561132+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-tools", "developer-tools"], "entities": ["Loomcycle", "Anthropic", "OpenAI", "DeepSeek", "Gemini", "Ollama", "Homebrew", "Docker"], "alternates": {"html": "https://wpnews.pro/news/show-hn-loomcycle-a-sidecar-runtime-for-ai-agents-go-binary-apache-2-0", "markdown": "https://wpnews.pro/news/show-hn-loomcycle-a-sidecar-runtime-for-ai-agents-go-binary-apache-2-0.md", "text": "https://wpnews.pro/news/show-hn-loomcycle-a-sidecar-runtime-for-ai-agents-go-binary-apache-2-0.txt", "jsonld": "https://wpnews.pro/news/show-hn-loomcycle-a-sidecar-runtime-for-ai-agents-go-binary-apache-2-0.jsonld"}}