Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a2de6677a | |||
| 43b9d16503 | |||
| bc1102366d | |||
| 15a952c213 |
+84
-77
@@ -26,6 +26,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 2026-05-11
|
||||
|
||||
### ✨ New features
|
||||
@@ -60,6 +61,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 2026-05-10
|
||||
|
||||
### ✨ New features
|
||||
@@ -96,82 +98,6 @@ Entries are published daily at 23:50 UTC.
|
||||
- **molecule-ai-plugin-molecule-careful-bash**: token exfiltration pattern block (OFFSEC-002) now documented in `known-issues.md`. (`molecule-ai-plugin-molecule-careful-bash` [#3](https://git.moleculesai.app/molecule-ai/molecule-ai-plugin-molecule-careful-bash/pulls/3))
|
||||
- **molecule-ci**: 7 reusable workflows ported to `.gitea/workflows/`, and Docker build smoke tests now gracefully skip when the daemon is unavailable. (`molecule-ci` [#6](https://git.moleculesai.app/molecule-ai/molecule-ci/pulls/6), [#7](https://git.moleculesai.app/molecule-ai/molecule-ci/pulls/7))
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-23
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- **SaaS Federation v2 tutorial**: a clean, self-contained walkthrough for platform operators who want to run multi-tenant workspaces from a single control plane. Covers org onboarding via `POST /cp/orgs`, workspace provisioning per tenant, fleet inspection, quota controls, and suspension/teardown. (`molecule-core` [#1700](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1700))
|
||||
- **External workspace quickstart**: a 5-minute guide to running any HTTP-speaking agent (Python, Node, Go, Rust) on your own machine and having it appear on the canvas alongside platform-provisioned agents. Covers tunnel setup, `POST /workspaces` registration, and a working echo agent. (`molecule-core` [#1760](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1760))
|
||||
|
||||
### 🔧 Fixes
|
||||
|
||||
- **SSRF guard in SaaS mode**: previously the SSRF protection was blocking all RFC-1918 private IP ranges (`10/8`, `172.16/12`, `192.168/16`) even in SaaS mode — this was a regression from the earlier SaaS-mode work. The fix wires up the `saasMode` flag correctly so private IPs are allowed in SaaS deployments (for internal service calls), while metadata ranges (`169.254/16`), CGNAT, loopback, and link-local remain blocked in every mode. IPv6 ULA (`fd00::/8`) handling is also now correct. (`molecule-core` [#1692](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1692))
|
||||
- **PUT `/workspaces/:id/files/*path` on SaaS (EC2) workspaces**: fixed a 500 error (`docker not available`) that occurred when saving files from Canvas on SaaS workspaces. The handler now detects non-Docker workspaces via `workspaces.instance_id` and routes writes via EC2 Instance Connect (SSH-backed write with an ephemeral key pair) instead of trying to `docker cp`. (`molecule-core` [#1702](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1702))
|
||||
|
||||
### 📚 Docs
|
||||
|
||||
- **molecli shell completion**: tab completion for `molecule` CLI in bash, zsh, fish, and PowerShell — covers all subcommands and flags. (`docs` [#79](https://git.moleculesai.app/molecule-ai/docs/pulls/79))
|
||||
- **MCP server structured logging**: `LOG_LEVEL` env var, pino JSON output with AsyncLocalStorage context on every tool call. (`docs` [#78](https://git.moleculesai.app/molecule-ai/docs/pulls/78))
|
||||
|
||||
### 🧹 Internal
|
||||
|
||||
- SaaS Federation v2 tutorial published — clean rewrite of #1613, now with correct HTTP status codes, fleet metrics endpoint, and security model table (`molecule-core` [#1700](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1700)); Files API SSH-backed write path for SaaS EC2 workspaces — fixes 500 on PUT `/workspaces/:id/files/*path` for SaaS users (`molecule-core` [#1702](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1702)); Canvas create-workspace dialog now requires hermes runtime model (`molecule-core` [#1714](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1714)).
|
||||
- EC2 Instance Connect SSH tutorial published (`molecule-core` [#1617](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1617)); AI agent org-scoped key credential model blog published (`molecule-core` [#1614](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1614)); Phase 30 Day 2 social package ready (`molecule-core` [#1662](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1662)).
|
||||
|
||||
### 🌅 Late-day updates (17:30–23:50 UTC)
|
||||
|
||||
#### 🔒 Security
|
||||
|
||||
- **Cross-tenant memory poisoning fix** (`molecule-core` [#1791](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1791)): fixes a bug where `commit_memory` with `scope=TEAM` could write to a sibling workspace's memory store under high concurrency. `commit_memory` now validates `target_workspace_id` against the caller's known peer set before any write.
|
||||
- **CWE-78 shell injection hardening** (`molecule-core` [#1885](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1885)): `shellQuote` now uses `strconv.Quote` for all shell-delimited paths in the EC2 Instance Connect and bastion SSH paths. Defense-in-depth layer hardened; primary protection remains path-validation logic upstream.
|
||||
|
||||
#### ✨ New features
|
||||
|
||||
- **A2A priority queue — Phase 1** (`molecule-core` [#1892](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1892)): task dispatch now supports a `priority` field (`low` / `normal` / `high` / `urgent`). High/urgent tasks bypass the normal FIFO queue and are dispatched immediately. Phase 2 (priority inversion deadlock prevention) on the roadmap.
|
||||
|
||||
#### 🔧 Fixes
|
||||
|
||||
- **A2A queue nil-safe drain** (`molecule-core` [#1893](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1893), [#1896](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1896)): `DequeueTask` no longer panics when the in-memory queue map is uninitialized — graceful empty-result returned instead.
|
||||
- **Workspaces stuck in `provisioning` after失败** (`molecule-core` [#1794](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1794)): provisioner now transitions workspaces to `failed` state with a descriptive error message instead of leaving them orphaned in `provisioning`.
|
||||
- **Dedup settings hooks double-fire** (`molecule-core` [#1797](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1797)): the `dedup_settings_hooks` registry now correctly unsubscribes after one fire — eliminates the 3–4× duplicate hook execution observed in CI.
|
||||
- **Semantic memory search returning stale results** (`molecule-core` [#1778](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1778)): pgvector index now refreshes synchronously on `commit_memory` write instead of on a 5-minute background cycle.
|
||||
- **pgvector migration race in E2E CI** (`molecule-core` [#1777](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1777)): `CREATE EXTENSION` wrapped in `IF NOT EXISTS` inside a `DO` block — eliminates E2E CI flakiness on fresh DB spin-up.
|
||||
- **EC2 Instance Connect endpoint not found in us-west-2** (`molecule-core` [#1779](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1779)): Instance Connect endpoint SDK call now falls back gracefully to direct SSM session when the EIC endpoint is unavailable in a region.
|
||||
- **Canvas topology overlay edge labels clipped** (`molecule-core` [#1802](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1802)): SVG edge labels now respect viewport bounds; labels that would render off-screen are repositioned.
|
||||
- **Audit trail panel not loading for large workspaces** (`molecule-core` [#1854](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1854)): audit log fetch now uses cursor-based pagination (100 events per page) instead of returning all events at once.
|
||||
- **Hermes `response_format` not forwarded to MiniMax** (`molecule-core` [#1861](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1861)): `response_format=json_schema` now propagates through the model config passthrough for hermes/MiniMax-M2.7-highspeed workspaces.
|
||||
- **Memory Inspector panel memory leak** (`molecule-core` [#1871](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1871)): `useMemoryStore` hook now correctly cancels the SSE subscription on panel unmount.
|
||||
- **Token revocation cache stale-read window** (`molecule-core` [#1888](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1888)): revoked-token invalidation now propagates within 5 s (down from 60 s) — closes the window where a revoked token could still authenticate.
|
||||
- **TenantGuard same-origin bypass (regression)** (`molecule-core` [#1898](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1898)): fixes a regression introduced in the Phase 33 cloudflare-removal change that re-opened the TenantGuard same-origin bypass for EC2 tenant Canvas deployments.
|
||||
|
||||
#### 📚 Docs
|
||||
|
||||
- **Chrome DevTools MCP tutorial** (`docs` [#1798](https://git.moleculesai.app/molecule-ai/docs/pulls/1798)): hands-on guide for debugging Molecule AI agents in-browser using Chrome's built-in MCP inspector.
|
||||
- **Phase 34 launch page** (`docs` [#1799](https://git.moleculesai.app/molecule-ai/docs/pulls/1799)): public-facing launch collateral for GA scheduled 2026-04-30.
|
||||
- **Tool Trace demo environment** (`docs` [#1844](https://git.moleculesai.app/molecule-ai/docs/pulls/1844)): interactive demo showing the tool trace inspector in action, with sample run data.
|
||||
- **Enterprise battlecard** (`docs` [#1864](https://git.moleculesai.app/molecule-ai/docs/pulls/1864)): competitive positioning doc for sales and enterprise evaluation teams.
|
||||
|
||||
#### 🧹 Internal
|
||||
|
||||
- `a2a-sdk` hot-pinned to `0.3.x` across all workspace template repos (`molecule-core` [#1890](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1890)); SDK upgrade path documented in `KI-009` (`internal` [#1631](https://git.moleculesai.app/molecule-ai/internal/issues/1631)).
|
||||
- Phase 34 CI matrix expanded to cover Node 22 and Go 1.24 (`molecule-ci`).
|
||||
|
||||
#### 🔧 Runtime fixes
|
||||
|
||||
- **Heartbeat 401 retry** (`molecule-ai-workspace-runtime` [#40](https://git.moleculesai.app/molecule-ai/molecule-ai-workspace-runtime/pulls/40)): heartbeat worker now retries with fresh token on 401 before declaring the workspace unreachable — eliminates false `disconnected` status during token rotation.
|
||||
- **LLM token auto-detect** (`molecule-ai-workspace-runtime` [#38](https://git.moleculesai.app/molecule-ai/molecule-ai-workspace-runtime/pulls/38)): hermes runtime now auto-detects `max_tokens` from model context window and request timeout when not explicitly configured.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2026-05-10
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- **A2A priority queue — Phase 1**: task dispatch now supports a `priority` field (`low` / `normal` / `high` / `urgent`). High/urgent tasks bypass the normal FIFO queue and are dispatched immediately. (`molecule-core` [#225](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/225))
|
||||
@@ -214,7 +140,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
- **SOP tier-check AND-composition of required team approvals per tier**: tier-check now enforces AND-composition of required team approvals per tier (`tier:high`). (`molecule-core` [#225](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/225))
|
||||
- **Canvas structural tests for TIER_CONFIG and COMM_TYPE_LABELS**: structural tests added for canvas TIER_CONFIG and COMM_TYPE_LABELS constants. (`molecule-core` [#245](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/245))
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-09
|
||||
|
||||
@@ -243,6 +169,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 2026-05-08
|
||||
|
||||
### 🔧 Fixes
|
||||
@@ -253,6 +180,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 2026-05-07
|
||||
|
||||
### 📚 Docs
|
||||
@@ -271,6 +199,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 2026-05-06
|
||||
|
||||
### 🧹 Internal
|
||||
@@ -280,6 +209,7 @@ Entries are published daily at 23:50 UTC.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 2026-04-22
|
||||
|
||||
### ✨ New features
|
||||
@@ -345,6 +275,83 @@ See the [migration blog post](/blog/cloudflare-tunnel-migration).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-23
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- **SaaS Federation v2 tutorial**: a clean, self-contained walkthrough for platform operators who want to run multi-tenant workspaces from a single control plane. Covers org onboarding via `POST /cp/orgs`, workspace provisioning per tenant, fleet inspection, quota controls, and suspension/teardown. (`molecule-core` [#1700](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1700))
|
||||
- **External workspace quickstart**: a 5-minute guide to running any HTTP-speaking agent (Python, Node, Go, Rust) on your own machine and having it appear on the canvas alongside platform-provisioned agents. Covers tunnel setup, `POST /workspaces` registration, and a working echo agent. (`molecule-core` [#1760](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1760))
|
||||
|
||||
### 🔧 Fixes
|
||||
|
||||
- **SSRF guard in SaaS mode**: previously the SSRF protection was blocking all RFC-1918 private IP ranges (`10/8`, `172.16/12`, `192.168/16`) even in SaaS mode — this was a regression from the earlier SaaS-mode work. The fix wires up the `saasMode` flag correctly so private IPs are allowed in SaaS deployments (for internal service calls), while metadata ranges (`169.254/16`), CGNAT, loopback, and link-local remain blocked in every mode. IPv6 ULA (`fd00::/8`) handling is also now correct. (`molecule-core` [#1692](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1692))
|
||||
- **PUT `/workspaces/:id/files/*path` on SaaS (EC2) workspaces**: fixed a 500 error (`docker not available`) that occurred when saving files from Canvas on SaaS workspaces. The handler now detects non-Docker workspaces via `workspaces.instance_id` and routes writes via EC2 Instance Connect (SSH-backed write with an ephemeral key pair) instead of trying to `docker cp`. (`molecule-core` [#1702](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1702))
|
||||
|
||||
### 📚 Docs
|
||||
|
||||
- **molecli shell completion**: tab completion for `molecule` CLI in bash, zsh, fish, and PowerShell — covers all subcommands and flags. (`docs` [#79](https://git.moleculesai.app/molecule-ai/docs/pulls/79))
|
||||
- **MCP server structured logging**: `LOG_LEVEL` env var, pino JSON output with AsyncLocalStorage context on every tool call. (`docs` [#78](https://git.moleculesai.app/molecule-ai/docs/pulls/78))
|
||||
|
||||
### 🧹 Internal
|
||||
|
||||
- SaaS Federation v2 tutorial published — clean rewrite of #1613, now with correct HTTP status codes, fleet metrics endpoint, and security model table (`molecule-core` [#1700](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1700)); Files API SSH-backed write path for SaaS EC2 workspaces — fixes 500 on PUT `/workspaces/:id/files/*path` for SaaS users (`molecule-core` [#1702](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1702)); Canvas create-workspace dialog now requires hermes runtime model (`molecule-core` [#1714](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1714)).
|
||||
- EC2 Instance Connect SSH tutorial published (`molecule-core` [#1617](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1617)); AI agent org-scoped key credential model blog published (`molecule-core` [#1614](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1614)); Phase 30 Day 2 social package ready (`molecule-core` [#1662](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1662)).
|
||||
|
||||
### 🌅 Late-day updates (17:30–23:50 UTC)
|
||||
|
||||
#### 🔒 Security
|
||||
|
||||
- **Cross-tenant memory poisoning fix** (`molecule-core` [#1791](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1791)): fixes a bug where `commit_memory` with `scope=TEAM` could write to a sibling workspace's memory store under high concurrency. `commit_memory` now validates `target_workspace_id` against the caller's known peer set before any write.
|
||||
- **CWE-78 shell injection hardening** (`molecule-core` [#1885](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1885)): `shellQuote` now uses `strconv.Quote` for all shell-delimited paths in the EC2 Instance Connect and bastion SSH paths. Defense-in-depth layer hardened; primary protection remains path-validation logic upstream.
|
||||
|
||||
#### ✨ New features
|
||||
|
||||
- **A2A priority queue — Phase 1** (`molecule-core` [#1892](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1892)): task dispatch now supports a `priority` field (`low` / `normal` / `high` / `urgent`). High/urgent tasks bypass the normal FIFO queue and are dispatched immediately. Phase 2 (priority inversion deadlock prevention) on the roadmap.
|
||||
|
||||
#### 🔧 Fixes
|
||||
|
||||
- **A2A queue nil-safe drain** (`molecule-core` [#1893](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1893), [#1896](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1896)): `DequeueTask` no longer panics when the in-memory queue map is uninitialized — graceful empty-result returned instead.
|
||||
- **Workspaces stuck in `provisioning` after failure** (`molecule-core` [#1794](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1794)): provisioner now transitions workspaces to `failed` state with a descriptive error message instead of leaving them orphaned in `provisioning`.
|
||||
- **Dedup settings hooks double-fire** (`molecule-core` [#1797](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1797)): the `dedup_settings_hooks` registry now correctly unsubscribes after one fire — eliminates the 3–4× duplicate hook execution observed in CI.
|
||||
- **Semantic memory search returning stale results** (`molecule-core` [#1778](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1778)): pgvector index now refreshes synchronously on `commit_memory` write instead of on a 5-minute background cycle.
|
||||
- **pgvector migration race in E2E CI** (`molecule-core` [#1777](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1777)): `CREATE EXTENSION` wrapped in `IF NOT EXISTS` inside a `DO` block — eliminates E2E CI flakiness on fresh DB spin-up.
|
||||
- **EC2 Instance Connect endpoint not found in us-west-2** (`molecule-core` [#1779](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1779)): Instance Connect endpoint SDK call now falls back gracefully to direct SSM session when the EIC endpoint is unavailable in a region.
|
||||
- **Canvas topology overlay edge labels clipped** (`molecule-core` [#1802](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1802)): SVG edge labels now respect viewport bounds; labels that would render off-screen are repositioned.
|
||||
- **Audit trail panel not loading for large workspaces** (`molecule-core` [#1854](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1854)): audit log fetch now uses cursor-based pagination (100 events per page) instead of returning all events at once.
|
||||
- **Hermes `response_format` not forwarded to MiniMax** (`molecule-core` [#1861](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1861)): `response_format=json_schema` now propagates through the model config passthrough for hermes/MiniMax-M2.7-highspeed workspaces.
|
||||
- **Memory Inspector panel memory leak** (`molecule-core` [#1871](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1871)): `useMemoryStore` hook now correctly cancels the SSE subscription on panel unmount.
|
||||
- **Token revocation cache stale-read window** (`molecule-core` [#1888](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1888)): revoked-token invalidation now propagates within 5 s (down from 60 s) — closes the window where a revoked token could still authenticate.
|
||||
- **TenantGuard same-origin bypass (regression)** (`molecule-core` [#1898](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1898)): fixes a regression introduced in the Phase 33 cloudflare-removal change that re-opened the TenantGuard same-origin bypass for EC2 tenant Canvas deployments.
|
||||
|
||||
#### 📚 Docs
|
||||
|
||||
- **Chrome DevTools MCP tutorial** (`docs` [#1798](https://git.moleculesai.app/molecule-ai/docs/pulls/1798)): hands-on guide for debugging Molecule AI agents in-browser using Chrome's built-in MCP inspector.
|
||||
- **Phase 34 launch page** (`docs` [#1799](https://git.moleculesai.app/molecule-ai/docs/pulls/1799)): public-facing launch collateral for GA scheduled 2026-04-30.
|
||||
- **Tool Trace demo environment** (`docs` [#1844](https://git.moleculesai.app/molecule-ai/docs/pulls/1844)): interactive demo showing the tool trace inspector in action, with sample run data.
|
||||
- **Enterprise battlecard** (`docs` [#1864](https://git.moleculesai.app/molecule-ai/docs/pulls/1864)): competitive positioning doc for sales and enterprise evaluation teams.
|
||||
|
||||
#### 🧹 Internal
|
||||
|
||||
- `a2a-sdk` hot-pinned to `0.3.x` across all workspace template repos (`molecule-core` [#1890](https://git.moleculesai.app/molecule-ai/molecule-core/pulls/1890)); SDK upgrade path documented in `KI-009` (`internal` [#1631](https://git.moleculesai.app/molecule-ai/internal/issues/1631)).
|
||||
- Phase 34 CI matrix expanded to cover Node 22 and Go 1.24 (`molecule-ci`).
|
||||
|
||||
#### 🔧 Runtime fixes
|
||||
|
||||
- **Heartbeat 401 retry** (`molecule-ai-workspace-runtime` [#40](https://git.moleculesai.app/molecule-ai/molecule-ai-workspace-runtime/pulls/40)): heartbeat worker now retries with fresh token on 401 before declaring the workspace unreachable — eliminates false `disconnected` status during token rotation.
|
||||
- **LLM token auto-detect** (`molecule-ai-workspace-runtime` [#38](https://git.moleculesai.app/molecule-ai/molecule-ai-workspace-runtime/pulls/38)): hermes runtime now auto-detects `max_tokens` from model context window and request timeout when not explicitly configured.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2026-04-17
|
||||
|
||||
A high-velocity day: 80+ PRs merged across platform, canvas, runtimes, security, and channels.
|
||||
|
||||
@@ -63,6 +63,15 @@ claude mcp add molecule -s user -- env \
|
||||
Reconnect with `/mcp` (or restart the Claude Code session) and the tools
|
||||
appear in the next turn.
|
||||
|
||||
<Callout type="warn">
|
||||
Claude Code 2.1.x+ requires the tagged flag form
|
||||
`--dangerously-load-development-channels server:molecule`. The bare flag
|
||||
(`--dangerously-load-development-channels` with no value) causes every A2A
|
||||
turn to wedge with a `Control request timeout: initialize` error. See
|
||||
[Dev-channels flag: tagged-form requirement](/docs/runtime-mcp/dev-channels-flag)
|
||||
for the full failure-mode breakdown and SDK integration notes.
|
||||
</Callout>
|
||||
|
||||
### Hermes Agent
|
||||
|
||||
```bash
|
||||
@@ -382,6 +391,7 @@ needed when you can't run an MCP stdio server inside your agent (rare).
|
||||
|
||||
## See also
|
||||
|
||||
- [Dev-channels flag: tagged-form requirement](/docs/runtime-mcp/dev-channels-flag) — why `--dangerously-load-development-channels server:molecule` (not the bare flag) is required for inline channel push in Claude Code 2.1.x+
|
||||
- [External Agents](/docs/external-agents) — manual A2A path for non-MCP runtimes
|
||||
- [Tokens](/docs/tokens) — token management and rotation
|
||||
- [Concepts — Workspaces](/docs/concepts#workspaces)
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
title: "Dev-channels flag — tagged-form requirement"
|
||||
description: "Why Claude Code 2.1.x+ requires `--dangerously-load-development-channels server:molecule` (not the bare flag) to enable inline channel push from the molecule-mcp wheel."
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
The `molecule-mcp` wheel emits a JSON-RPC `notifications/claude/channel`
|
||||
notification on every inbound A2A message so Claude Code can render it
|
||||
as an inline `<channel>` synthetic user turn — zero polling, zero
|
||||
per-turn stall. During the channels research preview, Claude Code only
|
||||
processes that notification when the host is launched with the
|
||||
`--dangerously-load-development-channels` flag *and the flag carries a
|
||||
matching tagged allowlist entry*.
|
||||
|
||||
This page covers the form that flag must take, what breaks when it's
|
||||
wrong, and when an operator has to think about it.
|
||||
|
||||
<Callout type="warn">
|
||||
The bare flag (no value) is rejected by the post-2.1 CLI parser, and
|
||||
the failure mode propagates upstream as a `Control request timeout:
|
||||
initialize` from any SDK that spawns the CLI — every A2A turn wedges
|
||||
100% of the time. See [Failure mode](#failure-mode) below.
|
||||
</Callout>
|
||||
|
||||
## The flag
|
||||
|
||||
```
|
||||
--dangerously-load-development-channels <entries...>
|
||||
```
|
||||
|
||||
Available in Claude Code **2.1.x and later**. It opts the CLI into
|
||||
processing experimental `notifications/<channel>` JSON-RPC methods
|
||||
emitted by registered MCP servers and plugin channels. Without it, the
|
||||
CLI silently drops those notifications during the allowlist check, even
|
||||
though the wheel ships the wire shape correctly.
|
||||
|
||||
## Required form: tagged allowlist entries
|
||||
|
||||
Each entry must carry one of two prefixes:
|
||||
|
||||
| Form | Use for |
|
||||
|---|---|
|
||||
| `server:<MCP-server-name>` | Manually configured MCP servers — the name matches what you registered with `claude mcp add <name> ...` or the key under `mcpServers` in `~/.claude.json`. |
|
||||
| `plugin:<plugin-name>@<owner>/<repo>` | Plugin channels installed from a Claude Code plugin marketplace. |
|
||||
|
||||
Multiple entries are space-separated:
|
||||
|
||||
```bash
|
||||
claude --dangerously-load-development-channels server:molecule server:telegram
|
||||
```
|
||||
|
||||
Untagged values (`molecule` instead of `server:molecule`) are rejected
|
||||
with `--dangerously-load-development-channels entries must be tagged`.
|
||||
|
||||
## Failure mode
|
||||
|
||||
A bare flag (`--dangerously-load-development-channels` with no value)
|
||||
walks through three layers of damage before surfacing:
|
||||
|
||||
1. **CLI**: rejects the invocation with
|
||||
`error: option '--dangerously-load-development-channels <servers...>' argument missing`.
|
||||
2. **SDK**: `claude-agent-sdk` (used by `claude_sdk_executor.py` in the
|
||||
Claude Code workspace template) renders the kwarg as a bare switch when
|
||||
the value is `None`. The CLI then never responds to the SDK's first
|
||||
`initialize` control message.
|
||||
3. **Workspace agent**: the SDK times out with
|
||||
`Control request timeout: initialize`. Every A2A turn wedges — 100%
|
||||
reproducible. Caught live on workspace `dd40faf8` on 2026-05-01.
|
||||
|
||||
Two small fixes prevent this: pass a tagged value (don't let `None`
|
||||
render as a bare switch), and verify the CLI accepts your specific
|
||||
entries before going broad.
|
||||
|
||||
## For Molecule operators
|
||||
|
||||
Pass `server:molecule` to enable the inbox bridge → MCP
|
||||
`notifications/claude/channel` push for the `molecule-mcp` wheel.
|
||||
|
||||
```bash
|
||||
claude --dangerously-load-development-channels server:molecule
|
||||
```
|
||||
|
||||
The `molecule` here matches the name you registered the wheel under in
|
||||
[Step 2 of the runtime-mcp guide](/docs/runtime-mcp#claude-code) (the
|
||||
key under `mcpServers`, or the first positional arg to `claude mcp add`).
|
||||
If you registered the wheel as `mol` or `molecule-prod`, use that name
|
||||
in the tag.
|
||||
|
||||
When push is live, the session header prints:
|
||||
|
||||
```
|
||||
Listening for channel messages from: server:molecule
|
||||
```
|
||||
|
||||
…and inbound canvas/peer-agent messages render inline as
|
||||
`<channel source="molecule" ...>` synthetic user turns instead of
|
||||
arriving via `inbox_peek`.
|
||||
|
||||
### Embedding in an SDK-driven agent
|
||||
|
||||
If you spawn `claude` through `claude-agent-sdk` (e.g. the Claude Code
|
||||
workspace template's `claude_sdk_executor.py`), forward the tagged value
|
||||
through `extra_args`:
|
||||
|
||||
```python
|
||||
from claude_agent_sdk import ClaudeAgentOptions
|
||||
|
||||
ClaudeAgentOptions(
|
||||
model=self.model,
|
||||
permission_mode="bypassPermissions",
|
||||
cwd=self._resolve_cwd(),
|
||||
mcp_servers=mcp_servers,
|
||||
system_prompt=self._build_system_prompt(),
|
||||
resume=self._session_id,
|
||||
extra_args={"dangerously-load-development-channels": "server:molecule"},
|
||||
)
|
||||
```
|
||||
|
||||
The SDK forwards `extra_args` keys as `--<key> <value>` to the spawned
|
||||
CLI. Passing `None` as the value renders as a bare switch and trips the
|
||||
[Failure mode](#failure-mode) chain above.
|
||||
|
||||
## Verification
|
||||
|
||||
Verified live on 2026-05-02: with the tagged value in `extra_args`,
|
||||
the in-workspace agent received `<channel source="molecule" kind="..."
|
||||
peer_id="..." activity_id="..." ts="...">` tags inline as synthetic
|
||||
user turns. No `wait_for_message` poll was needed for delivery. A2A
|
||||
returned coherent replies on every turn.
|
||||
|
||||
## When this matters
|
||||
|
||||
Only when both of the following apply:
|
||||
|
||||
- You're running Claude Code (any version 2.1.x or later) as the
|
||||
workspace runtime, AND
|
||||
- The in-workspace `molecule-mcp` server is configured (it is, by
|
||||
default, in the `claude-code` workspace template).
|
||||
|
||||
**Hosted Molecule SaaS handles this automatically** — the executor
|
||||
passes `extra_args={"dangerously-load-development-channels": "server:molecule"}`
|
||||
when spawning the CLI. Operators on hosted SaaS do not need to do
|
||||
anything.
|
||||
|
||||
**Self-hosted operators using the Claude Code workspace template** also
|
||||
get this for free since the template's executor sets `extra_args`. The
|
||||
flag only needs operator attention when:
|
||||
|
||||
- Forking the Claude Code workspace template and stripping `extra_args`
|
||||
inadvertently.
|
||||
- Running `claude` directly outside the template (e.g. interactive
|
||||
sessions on a developer laptop) and wanting inline `<channel>` push.
|
||||
- Adding a second tagged source (e.g. `server:telegram` alongside
|
||||
`server:molecule`) — append, don't replace.
|
||||
|
||||
Operators on Cursor, Cline, OpenCode, codex, hermes-agent, or any
|
||||
non-Claude-Code MCP host are unaffected: those clients ignore the
|
||||
notification and the wheel's poll path delivers via
|
||||
`wait_for_message` as the universal fallback.
|
||||
|
||||
## Forward note
|
||||
|
||||
This requirement is a **research-preview gate**. Once Claude Code
|
||||
graduates `notifications/<channel>` from research preview to a default
|
||||
allowlist, the `--dangerously-load-development-channels` flag will no
|
||||
longer be required for the `molecule` server. Drop the `extra_args`
|
||||
entry in `claude_sdk_executor.py` (and any operator launch wrappers)
|
||||
when that happens — the wheel emits the wire shape correctly today
|
||||
and will continue to do so post-graduation.
|
||||
|
||||
## See also
|
||||
|
||||
- [Bring Your Own Runtime (MCP) — Inbound delivery](/docs/runtime-mcp#inbound-delivery-universal-poll-optional-push)
|
||||
- [Bring Your Own Runtime (MCP) — Step 2: Claude Code](/docs/runtime-mcp#claude-code)
|
||||
- [Troubleshooting — Control request timeout: initialize](/docs/runtime-mcp#control-request-timeout-initialize-from-the-workspace-agent)
|
||||
@@ -1,201 +0,0 @@
|
||||
---
|
||||
title: Self-Hosted Workspace Deployment with Docker
|
||||
---
|
||||
|
||||
# Self-Hosted Workspace Deployment with Docker
|
||||
|
||||
This guide covers running a Molecule AI workspace agent as a Docker container on a self-hosted server or VM. It covers the Docker image, required environment variables, the built-in healthcheck, graceful shutdown, and Kubernetes deployment considerations.
|
||||
|
||||
> **Prerequisites:** A running Molecule AI control plane (self-hosted or SaaS), an `ADMIN_TOKEN` or org-scoped API key with admin scope, and Docker 20.10+ on the host.
|
||||
|
||||
## How the workspace container works
|
||||
|
||||
The Molecule AI workspace Dockerfile includes:
|
||||
|
||||
- A `HEALTHCHECK` directive that probes the agent card endpoint every 30 seconds
|
||||
- A uvicorn server on port 8000 (configurable via `PORT`)
|
||||
- Support for `stop_event` graceful shutdown via SIGTERM
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Docker host (your VM / bare metal) │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ workspace container │ │
|
||||
│ │ │ │
|
||||
│ │ uvicorn (port 8000) │ │
|
||||
│ │ └─ /agent/card ← HEALTHCHECK │ │
|
||||
│ │ │ │
|
||||
│ │ run_heartbeat_loop(stop_event) │ │
|
||||
│ └──────────────┬──────────────────────┘ │
|
||||
│ │ │
|
||||
│ host.docker.internal:8080 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ Molecule AI control plane │ │
|
||||
│ │ (platform on port 8080) │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Step 1: Create an external workspace
|
||||
|
||||
First register the workspace as an external (self-managed) agent on the platform.
|
||||
|
||||
```bash
|
||||
ADMIN_TOKEN="your-admin-token"
|
||||
PLATFORM_URL="https://platform.moleculesai.app" # or http://localhost:8080 for local dev
|
||||
WORKSPACE=$(curl -s -X POST "${PLATFORM_URL}/workspaces" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "self-hosted-agent", "runtime": "external"}')
|
||||
|
||||
WORKSPACE_ID=$(echo "$WORKSPACE" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
|
||||
echo "Workspace ID: $WORKSPACE_ID"
|
||||
```
|
||||
|
||||
Save the returned `WORKSPACE_ID` and bearer token from the next step.
|
||||
|
||||
## Step 2: Pull the workspace image
|
||||
|
||||
The workspace image is published to the Molecule AI ECR registry. Contact your platform administrator for the registry prefix and credentials, then log in:
|
||||
|
||||
```bash
|
||||
aws ecr get-login-password --region us-east-1 | \
|
||||
docker login --username AWS --password-stdin "${REGISTRY_PREFIX}.dkr.ecr.us-east-1.amazonaws.com"
|
||||
|
||||
docker pull "${REGISTRY_PREFIX}.dkr.ecr.us-east-1.amazonaws.com/molecule-workspace:latest"
|
||||
```
|
||||
|
||||
## Step 3: Configure environment variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `MOLECULE_API_URL` | `http://localhost:8080` | Platform API URL. From Docker on Linux/macOS, use `http://host.docker.internal:8080` to reach the host machine. |
|
||||
| `MOLECULE_API_KEY` | — | Bearer token obtained during agent registration |
|
||||
| `WORKSPACE_ID` | — | Workspace ID from Step 1 |
|
||||
| `PORT` | `8000` | Agent server port (matches HEALTHCHECK) |
|
||||
| `AGENT_CARD_URL` | `http://localhost:${PORT}/agent/card` | Advertised agent card URL (must be reachable from the platform) |
|
||||
|
||||
## Step 4: Run the container
|
||||
|
||||
### Docker (standalone)
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name molecule-workspace \
|
||||
-p 8000:8000 \
|
||||
-e MOLECULE_API_URL="http://host.docker.internal:8080" \
|
||||
-e MOLECULE_API_KEY="your-agent-bearer-token" \
|
||||
-e WORKSPACE_ID="your-workspace-id" \
|
||||
-e PORT=8000 \
|
||||
"${REGISTRY_PREFIX}.dkr.ecr.us-east-1.amazonaws.com/molecule-workspace:latest"
|
||||
```
|
||||
|
||||
> **Note for Linux hosts:** Docker does not include `host.docker.internal` by default. On Linux, either add `--add-host=host.docker.internal:host-gateway` to the `docker run` command, or use the host machine's IP address directly (e.g. `http://192.168.1.100:8080`).
|
||||
|
||||
### Verify the healthcheck
|
||||
|
||||
```bash
|
||||
# Wait for the container to become healthy (up to ~2 minutes)
|
||||
docker inspect --format='{{.State.Health.Status}}' molecule-workspace
|
||||
|
||||
# Expected output: healthy
|
||||
# Once healthy, the agent card is reachable:
|
||||
curl -s http://localhost:8000/agent/card | python3 -m json.tool
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
molecule-workspace:
|
||||
image: "${REGISTRY_PREFIX}.dkr.ecr.us-east-1.amazonaws.com/molecule-workspace:latest"
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
MOLECULE_API_URL: "http://host.docker.internal:8080"
|
||||
MOLECULE_API_KEY: "your-agent-bearer-token"
|
||||
WORKSPACE_ID: "your-workspace-id"
|
||||
PORT: "8000"
|
||||
# Linux hosts: add host.docker.internal resolution
|
||||
# extra_hosts:
|
||||
# - "host.docker.internal:host-gateway"
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/agent/card"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
```
|
||||
|
||||
## Step 5: Graceful shutdown
|
||||
|
||||
The workspace agent supports graceful shutdown via a `stop_event: threading.Event`. When the container receives SIGTERM (e.g. from `docker stop`), the heartbeat loop exits cleanly with return value `"stopped"` instead of hanging.
|
||||
|
||||
To enable SIGTERM handling in your agent code:
|
||||
|
||||
```python
|
||||
import signal, threading
|
||||
from molecule_agent import RemoteAgentClient
|
||||
|
||||
client = RemoteAgentClient(
|
||||
molecule_api_url=os.environ["MOLECULE_API_URL"],
|
||||
api_key=os.environ["MOLECULE_API_KEY"],
|
||||
workspace_id=os.environ["WORKSPACE_ID"],
|
||||
)
|
||||
|
||||
stop_event = threading.Event()
|
||||
|
||||
def sigterm_handler(signum, frame):
|
||||
print("Received SIGTERM, initiating graceful shutdown...")
|
||||
stop_event.set()
|
||||
|
||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
|
||||
# run_heartbeat_loop exits with return value "stopped" when stop_event is set
|
||||
result = client.run_heartbeat_loop(stop_event=stop_event)
|
||||
print(f"Heartbeat loop stopped: {result}")
|
||||
```
|
||||
|
||||
Without explicit SIGTERM handling, the container will be killed after the Docker default 10-second timeout. The healthcheck ensures orchestrators can detect an unhealthy container before the SIGTERM timeout.
|
||||
|
||||
## Kubernetes deployment
|
||||
|
||||
For Kubernetes deployments, use the native liveness/readiness probe configuration instead of the Docker HEALTHCHECK:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /agent/card
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /agent/card
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
terminationGracePeriodSeconds: 120
|
||||
```
|
||||
|
||||
> **Note:** `terminationGracePeriodSeconds` must exceed the liveness probe failure window (3 × 30s = 90s) so that Kubernetes sends SIGTERM and allows graceful shutdown before the pod is killed. The 120s value here gives a 30s buffer beyond the 90s threshold.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
|---|---|---|
|
||||
| Container shows `unhealthy` after startup | Platform unreachable from container | Verify `MOLECULE_API_URL` uses `host.docker.internal` (Docker) or the correct host IP |
|
||||
| `curl: (7) Failed to connect` on healthcheck | Container not fully started | Wait up to 30s; increase `start_period` |
|
||||
| Agent not appearing on canvas | Wrong `WORKSPACE_ID` or expired token | Re-run registration; check platform logs |
|
||||
| `host.docker.internal` not resolved | Linux host without the Docker flag | Use `--add-host=host.docker.internal:host-gateway` or the host's LAN IP |
|
||||
Reference in New Issue
Block a user