Competitive Intelligence completed a full CrewAI Enterprise deep-dive:
- Crew Studio confirmed as a real node-and-edge drag-and-drop canvas (not
just forms), ships in both SaaS and AMP Factory self-hosted — but paradigm
is workflow design, not persistent-identity governance. Counter-positioning
for #582 must be explicit: governance canvas, not just visual canvas.
- AMP Factory self-host is stronger than previously assessed: on-prem or
private VPC, Kubernetes, full Studio included, FedRAMP High certified.
- A2A support is first-class at v0.8/v0.9 (both client and server modes) —
Molecule AI orgs can recruit CrewAI agents as workers via standard A2A today.
Integration opportunity, not just threat.
- Differentiator gaps: CrewAI has 20+ native connectors, agent training,
checkpoint/fork, FedRAMP High; Molecule AI has persistent identity, org
hierarchy, governance canvas (#582 pending).
threat_level remains high. FedRAMP gap flagged for enterprise sales tracking.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Second eco-watch scan of the day (Go trending + HN :38 run).
**GitHub MCP Server** (github/github-mcp-server, 28.9k⭐, v1.0.0 Apr 16):
GitHub's official MCP Server — 60+ tools (repos, issues, PRs, Actions,
code security). Same "adopt as workspace plugin source" pattern as
Chrome DevTools MCP. Dynamic toolset discovery (beta) is a reference
design for our plugins available endpoint. Added LOW threat.
**Skillshare** (runkids/skillshare, 1.5k⭐, v0.19.2 Apr 14):
Go binary syncing SKILL.md + agent configs across 50+ AI tools via
symlinks. Direct overlap with our plugins/ distribution model and
SKILL.md format. Notable: ships a prompt-injection/exfiltration scanner
on install — we have no equivalent gate in our plugin install path.
Added LOW threat; scanner pattern is an actionable gap.
Both added to YAML snapshot (LOW tier) and Entries narrative.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Daily ecosystem survey — two new projects not previously tracked:
**Cognee** (topoteretes/cognee, 15.8k⭐, v1.0.1.dev1 Apr 15):
Hybrid graph+vector knowledge engine for agent memory. Ships a claude-code
plugin for session memory and native Hermes Agent integration. The
four-operation API (remember/recall/forget/improve) and cross-agent
tenant-isolated knowledge graph are directly relevant to closing our
agent_memories gap. Added as LOW threat; watch for a first-class MCP
server release.
**Archestra** (archestra-ai/archestra, 3.6k⭐, platform-v1.2.15 Apr 16):
Enterprise MCP registry + dual-LLM security gateway. Kubernetes-native,
AGPL-3.0. Governs which teams can access which MCP servers, plus a
security sub-agent that intercepts tool responses to block prompt
injection. Complementary to (not competitive with) Molecule AI today;
dual-LLM gateway pattern worth borrowing for A2A proxy hardening.
Added as LOW threat.
Both added to YAML snapshot (LOW tier) and Entries narrative.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On EC2 tenant instances, Caddy serves Canvas (:3000) and API (:8080) under
the same domain. Canvas makes same-origin requests without X-Molecule-Org-Id
or Fly-Replay-Src headers, causing TenantGuard to 404 every API route.
- Add isSameOriginCanvas() as tertiary check in TenantGuard — when
CANVAS_PROXY_URL is set and Referer/Origin matches Host, pass through.
- Enhance isSameOriginCanvas() to also check Origin header (WebSocket
upgrade requests send Origin but may not send Referer).
- Add 3 new tests: Referer bypass, Origin bypass (WS), inactive without env.
Fixes all 404s on /workspaces, /templates, /org/templates, /approvals/pending,
/canvas/viewport, and /ws WebSocket on tenant EC2 instances.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds response_format support to HermesA2AExecutor so callers can request
structured JSON output via the OpenAI-native response_format parameter.
Changes:
- _validate_response_format(): validates type (json_schema/json_object/text)
and required sub-fields; returns None if valid, error message if invalid
- HermesA2AExecutor.__init__: new response_format kwarg, stored as _response_format
- execute(): validates before API call — invalid schema enqueues error and
returns early without hitting Hermes API; valid and non-None adds
response_format= to create_kwargs; None omits the field entirely
Tests (12 new):
- _validate_response_format: all valid types, invalid type, missing fields
- constructor stores response_format correctly
- valid response_format forwarded to API call
- response_format omitted when None (no key in call kwargs)
- invalid schema → error message enqueued, API not called
Closes#498
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of injecting tool definitions as text into the system prompt,
HermesA2AExecutor now accepts a tools: list[dict] | None constructor
parameter containing OpenAI-format tool definitions and forwards them
via the native tools= parameter on chat.completions.create().
Empty list / None rule: when tools is falsy, the tools key is omitted
from the API call entirely — never sent as tools=[] — so providers
that reject an empty tools array don't return a 400.
Tool-call response handling: when the model returns finish_reason
"tool_calls" with no text content, the executor serialises the call
list as a JSON string and enqueues it as the A2A reply. This keeps
the executor thin (single API call per turn, no ReAct loop) while
surfacing function-call intent in a structured, parseable format.
Changes:
- HermesA2AExecutor.__init__: new tools kwarg; stored as self._tools
(copy; mutating the input list has no effect)
- execute(): builds create_kwargs dict and conditionally adds tools=
only when self._tools is non-empty; handles tool_calls response
- Module docstring: new "Native tools (#497)" section with schema
reference and edge-case explanation
Tests (12 new, 47 total in hermes test file, 1002 total suite):
- tools stored correctly in constructor (copy, None, [], non-empty)
- non-empty tools forwarded as tools= in API call
- multiple tools all forwarded
- empty list ([] and None and default) → tools key absent from call
- model tool_call response → JSON-serialised list as A2A reply
- multiple tool_calls → all in JSON reply
- text content present → text wins over tool_calls
Closes#497
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: the github-app-auth plugin injects GH_TOKEN + GITHUB_TOKEN
into each workspace container's env at provision time (EnvMutator). Those
are GitHub App installation tokens with a fixed ~60 min TTL. The plugin
has an in-process cache that proactively refreshes 5 min before expiry —
but the workspace env is set once at container start and never updated.
Any workspace alive >60 min ends up with an expired token.
Fix (Option B — on-demand endpoint):
pkg/provisionhook:
- Add TokenProvider interface: Token(ctx) (token, expiresAt, error)
Lives in pkg/ (public) so the github-app-auth plugin can implement it.
- Add Registry.FirstTokenProvider() — discovers the first mutator that
also satisfies TokenProvider via interface assertion. Safe under
concurrent reads (existing RWMutex).
platform/internal/handlers/github_token.go:
- New GitHubTokenHandler serving GET /admin/github-installation-token
- Delegates to the registered TokenProvider (plugin cache — always fresh)
- 404 if no GitHub App configured, 500 + [github] prefix log on error
- Never logs the token itself
platform/internal/handlers/workspace.go:
- Add TokenRegistry() getter so the router can wire the handler without
coupling to WorkspaceHandler internals
platform/internal/router/router.go:
- Register GET /admin/github-installation-token under AdminAuth
workspace-template/:
- scripts/molecule-git-token-helper.sh — git credential helper; calls
the platform endpoint on every push/fetch; falls through to next
helper (operator PAT) if platform unreachable
- entrypoint.sh — configure the credential helper at startup
Why Option B over Option A (background goroutine):
- The plugin already has its own cache refresh; nothing to refresh here.
- Pushing env updates into running containers requires docker exec, which
the architecture explicitly rejects (issue #547 "Alternatives").
- Pull-based is stateless, trivially testable, zero extra goroutines.
Closes#547
Co-authored-by: Molecule AI DevOps Engineer <devops-engineer@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
When a workspace delegated a task to itself, it would acquire
_run_lock twice on the same goroutine mutex, blocking permanently.
Add an early-return guard in `DelegationHandler.Delegate` that
returns HTTP 400 {"error": "self-delegation not permitted"} as soon
as sourceID == body.TargetID, before any DB or A2A work is done.
Adds TestDelegate_SelfDelegation_Rejected to delegation_test.go.
Closes#548
Co-authored-by: Molecule AI Backend Engineer <backend-engineer@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
`CreateWorkspacePayload` was missing a `Secrets` field, so any
`secrets: { KEY: value }` included in a POST /workspaces body was
silently dropped by ShouldBindJSON.
Changes:
- Add `Secrets map[string]string` field to `CreateWorkspacePayload`
- Wrap workspace INSERT in a DB transaction; iterate over secrets,
encrypt each value via `crypto.Encrypt`, and upsert into
`workspace_secrets` within the same tx — rollback both on any failure
- Add `mock.ExpectBegin()`/`mock.ExpectCommit()`/`mock.ExpectRollback()`
to all existing Create tests that were missing transaction expectations
- Add 3 new tests: WithSecrets_Persists, SecretPersistFails_RollsBack,
EmptySecrets_OK
Closes#545
Co-authored-by: Molecule AI Backend Engineer <backend-engineer@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Deep-dive #571 (Competitive Intelligence, 2026-04-17) confirmed Paperclip
has no A2A protocol, no visual canvas, and no org-chart UI on roadmap.
Blocker dependencies are a single-process task-graph DAG, not inter-agent
coordination. Execution policies are budget ceilings only. The sole
capability gap vs Molecule AI is per-workspace budget limits (tracked #541).
Brand/framing threat ("zero-human companies") but not a technical substitute.
- docs/ecosystem-watch.md: threat_level high → medium, notable_changes
updated with deep-dive conclusion
- docs/marketing/competitors.md: move Paperclip row from HIGH to MEDIUM
table; update Watchlist escalation levels; add recently-changed entry
Closes#571
Co-authored-by: Molecule AI Research Lead <research-lead@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
CVE-2024-47081: upgrade requests 2.32.3 → 2.33.1 (netrc credential leak).
Slack mrkdwn injection: post_digest() embedded raw tweet text into a
mrkdwn link label (<url|snippet>) without escaping, allowing a malicious
tweet containing <!channel> or a phishing <url|label> to inject verbatim.
Fix: add _escape_mrkdwn() helper (& → &, < → <, > → >) and
apply to the snippet in post_digest(). post_mentions() was already safe
via _format_tweet_block(). New test: test_post_digest_mrkdwn_escaping_in_snippet.
65 tests, 100% coverage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- #554 CRITICAL: Add hydrationError state to Zustand store; catch handler now
calls setHydrationError instead of silent console.error; page renders a
full-screen zinc-950 error banner with a Retry button that reloads the page
- #556 MEDIUM: Add roving tabIndex + ArrowDown/Up/Left/Right keyboard handler
to the tier radio group in CreateWorkspaceDialog (WCAG 2.1 compliant)
- #557 MEDIUM: Add "Zoom to Team" menu item to ContextMenu (visible only when
node has children); dispatches molecule:zoom-to-team for keyboard accessibility
- Bonus: add missing 'use client' directive to RevealToggle.tsx
Co-authored-by: Molecule AI Frontend Engineer <frontend-engineer@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds brand-monitor/ — a cron-based X API v2 poller that posts new Molecule AI
brand mentions to Slack #brand-monitoring. Surge mode enables 15-min polling
for launch days / crisis windows; state persisted in .surge_state.json so
restarts within an active window continue in surge mode.
Closes#549
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
molecule_runtime's _deep_merge_hooks() uses unconditional list.extend()
when merging plugin settings-fragment.json files. On every plugin install
or reinstall each hook handler is re-appended, causing 3-4x duplicate
firings per event.
scripts/dedup_settings_hooks.py — idempotent live fix (reads via
/proc/*/root, no docker CLI required). Safe to re-run.
scripts/verify_settings_hooks.py — exits 1 if any container still has
duplicate hooks; used in CI health checks and manual audits.
Upstream fix needed in molecule_runtime._deep_merge_hooks() to
deduplicate by (matcher, frozenset(commands)) before writing. Track
separately.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements WorkspaceAdapter for Google's Agent Development Kit (google-adk
v1.x, Apache-2.0). Ships four files under workspace-template/adapters/google-adk/:
- adapter.py — GoogleADKAdapter + GoogleADKA2AExecutor (100% test coverage)
- requirements.txt — pinned google-adk==1.30.0 + google-genai>=1.16.0
- README.md — overview, install, usage, config, architecture diagram
- test_adapter.py — 46 unit tests, all passing, no live API calls
Supports AI Studio (GOOGLE_API_KEY) and Vertex AI (GOOGLE_GENAI_USE_VERTEXAI=1).
Model prefix stripping: "google:gemini-2.0-flash" → "gemini-2.0-flash".
Error sanitization mirrors the hermes_executor convention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The CP provisioner calls POST /cp/workspaces/provision which now
creates EC2 instances (not Fly Machines). The tenant platform
auto-activates this when MOLECULE_ORG_ID is set.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three new entries from today's eco-watch scan:
- paperclipai/paperclip (~54.8k ⭐): hierarchical CEO/manager/worker multi-agent
orchestration with budget constraints and audit trails. Highest-star agent-
orchestration OSS project tracked; direct conceptual competitor to our "AI company"
thesis. Signals: watch for persistent memory and visual org chart additions.
- google/adk-python (~19k ⭐, v1.29.0): Google's official multi-agent SDK. Pairs with
Gemini CLI (already tracked) to form Google's full agent stack. Evaluation teams will
weigh ADK + Gemini CLI vs Molecule AI. Spawns issue #542 (google-adk adapter).
- ChromeDevTools/chrome-devtools-mcp (~35.5k ⭐): official ChromeDevTools MCP server,
23 tools, already the de facto standard for browser tool use across 29 MCP clients.
Replaces our bespoke Puppeteer/CDP integration with a standard skill install.
Spawns issue #540 (browser-automation plugin migration).
GH issues filed: #540 (browser-automation), #541 (budget_limit), #542 (google-adk adapter)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hermes 4 is a hybrid-reasoning model trained on <think> tags; without asking
for thinking we pay flagship $/tok but get non-reasoning quality. This adds a
dedicated HermesA2AExecutor that dispatches to any OpenAI-compat endpoint
(OpenRouter, Nous Portal) and enables native reasoning for Hermes 4 models.
Key decisions:
- ProviderConfig + _reasoning_supported() detect Hermes 4 by model slug
substring ("hermes-4", "hermes4") — case-insensitive, no config needed
- extra_body={"reasoning": {"enabled": True}} sent only to Hermes 4 entries;
Hermes 3 path unchanged (no extra_body, no regressions)
- choices[0].message.reasoning + reasoning_details extracted and written to
an OTEL span (hermes.reasoning) — deliberately NOT echoed in the A2A reply
so the reasoning trace never contaminates the agent's next-turn context
- API key / base URL default to OPENAI_API_KEY / OPENAI_BASE_URL env vars
with openrouter.ai/api/v1 as the fallback endpoint
- _client injection parameter for unit tests (no live API calls needed)
- Error sanitization: only exception class name surfaces to user (mirrors
sanitize_agent_error() convention from cli_executor.py)
Test coverage: 35 tests, 100% coverage on all new code paths including:
- _reasoning_supported() — Hermes 4/3/unknown/empty/uppercase
- ProviderConfig — field assignment and capability flags
- extra_body presence for Hermes 4, absence for Hermes 3
- reasoning not in A2A reply; _log_reasoning called when trace present
- reasoning_details forwarded; span attributes set correctly
- Telemetry failure swallowed (never blocks response)
- API error → sanitized class-name-only reply
- cancel() → TaskStatusUpdateEvent(state=canceled)
Full suite: 990 passed, 0 failed (no regressions).
Resolves#496
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes 'unsafe-inline' and 'unsafe-eval' from script-src in the
production Content-Security-Policy, replacing them with a per-request
nonce + 'strict-dynamic'. This closes the XSS gap reported in #450
where the CSP header gave false assurance.
Key decisions:
- 'strict-dynamic' propagates nonce trust to Next.js dynamic chunk
imports — no need to enumerate every chunk URL
- style-src retains 'unsafe-inline': React Flow writes inline style=""
attributes for node positioning which cannot be nonce'd, and CSS
injection is accepted as significantly lower risk than script injection
- Dev mode keeps the permissive policy so HMR/fast-refresh keep working
- buildCsp() is exported for unit testing (21 tests added)
Additional hardening in production CSP:
object-src 'none', base-uri 'self', frame-ancestors 'none',
upgrade-insecure-requests, connect-src limited to wss: (not ws:)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>