Commit Graph

3972 Commits

Author SHA1 Message Date
Hongming Wang
a306a97dd3 ci(synth-e2e): move cron off :00 to dodge GH scheduler drops
GitHub Actions scheduler de-prioritises :00 cron firings under load.
Empirical 2026-05-03: the canary's cron was '0,20,40 * * * *' but
actual firings landed at :08, :03, :01, :03 — :20 and :40 silently
dropped. Detection latency degraded from claimed 20 min to actual
~60 min worst case.

Move to '10,30,50 * * * *':
- :10/:30/:50 sit 10 min off the top-of-hour load peak
- Still 5 min from :15 sweep-cf-orphans and :45 sweep-cf-tunnels
  (the original constraint that kept us off :15/:45)
- Same 20-min cadence; only the phase changes

No code change beyond the cron expression + comment refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:28:45 -07:00
Hongming Wang
065e39dda2
Merge pull request #2657 from Molecule-AI/auto-sync/main-54d32d1e
chore: sync main → staging (auto, ff to 54d32d1e)
2026-05-04 00:23:24 +00:00
molecule-ai[bot]
54d32d1ee2
Merge pull request #2656 from Molecule-AI/staging
staging → main: auto-promote 4cd01a2
2026-05-04 00:19:09 +00:00
Hongming Wang
4cd01a2df1
Merge pull request #2654 from Molecule-AI/auto-sync/main-8760ee16
chore: sync main → staging (auto, ff to 8760ee16)
2026-05-04 00:03:47 +00:00
Hongming Wang
ccb7ca5d8a
Merge pull request #2655 from Molecule-AI/a11y/canvas-console-modal-buttons
canvas/ConsoleModal: fix no-op hovers + Copy feedback + focus rings
2026-05-04 00:01:13 +00:00
Hongming Wang
10f2b9f01c canvas/ConsoleModal: fix no-op hovers + add Copy success feedback
Four UIUX fixes for the EC2 console modal:

1. Copy and Close buttons had hover:bg-surface-card on TOP of the
   same base bg-surface-card — silent no-op hover. Lifted to
   surface-elevated + line-soft border, matching ConfirmDialog's
   Cancel pattern. The button visibly responds now.

2. Copy button silently succeeded — no toast, no animation, no UI
   feedback. Operators clicking it had no idea whether anything
   landed in the clipboard. Now fires showToast on resolve/reject
   so the action is observable.

3. × close button was ~10x16px (well under WCAG 2.5.5's 24x24).
   Bumped to w-6 h-6 with focus-visible ring + hover bg.

4. Added focus-visible:ring-accent/60 + ring-offset-surface to
   all three buttons so keyboard users see focus. Matches the
   semantic ring pattern used across the canvas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:58:31 -07:00
molecule-ai[bot]
8760ee1628
Merge pull request #2653 from Molecule-AI/staging
staging → main: auto-promote e9fdd99
2026-05-03 16:51:55 -07:00
Hongming Wang
28f5108a7c
Merge pull request #2652 from Molecule-AI/a11y/canvas-batch-action-esc
canvas/BatchActionBar: wire Esc to clear selection
2026-05-03 23:33:54 +00:00
Hongming Wang
e9fdd992a9
Merge pull request #2651 from Molecule-AI/auto-sync/main-aedbbc4a
chore: sync main → staging (auto, ff to aedbbc4a)
2026-05-03 23:33:41 +00:00
Hongming Wang
f6fa3669dc
Merge pull request #2650 from Molecule-AI/fix/sweep-stale-e2e-port-verify-pattern
ci: port DELETE-verify pattern to remaining staging e2e workflows
2026-05-03 23:32:42 +00:00
Hongming Wang
b1a1c8e4a9 canvas/BatchActionBar: wire Esc to clear selection (matches button title)
Two small fixes for the batch-action toolbar:

1. The deselect button's title says "Clear selection (Escape)" — but
   pressing Escape did NOTHING. The title has been lying since the bar
   shipped. Now wired: window keydown handler calls clearSelection
   when Esc fires. Skipped while the confirm dialog is open
   (`pending !== null`) so the dialog's own Esc-cancels takes
   precedence, and skipped during a busy in-flight action so the
   user can't strand a partial-failure mid-flight.

2. focus-visible:ring-zinc-500/70 → focus-visible:ring-accent/50
   on the deselect button. The hardcoded zinc broke the semantic-
   token pattern used by the other action buttons.

Tests: two new vitest cases — Esc clears with selection, Esc no-op
when empty (the bar isn't mounted at count===0 so the listener never
registers). Full suite: 1222/1222.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:31:23 -07:00
molecule-ai[bot]
aedbbc4a10
Merge pull request #2649 from Molecule-AI/staging
staging → main: auto-promote 98da627
2026-05-03 23:27:44 +00:00
Hongming Wang
8b9e7e6d59 ci: port DELETE-verify pattern to remaining staging e2e workflows
Follow-up to #2648 — same `>/dev/null || true` swallow-on-error
pattern existed in:

  e2e-staging-canvas.yml   (single-slug)
  e2e-staging-saas.yml     (loop)
  e2e-staging-sanity.yml   (loop)
  e2e-staging-external.yml (loop, was `>/dev/null 2>&1` variant)

All four now capture the HTTP code, log a "[teardown] deleted $slug
(HTTP $code)" line on success, and emit a workflow warning naming
the slug + body excerpt on non-2xx. Loop bodies also tally + summarise
total leaks at the end.

Exit semantics unchanged: a single cleanup miss still doesn't fail-flag
the test (sweep-stale-e2e-orgs is the safety net within ~45 min). The
behavior change is purely surfacing — failures that were silent are
now visible on the workflow run page.

Pairs with #2648's tightened sweeper. Together: per-run cleanup
failures are visible AND the safety net catches them quickly.

Closes the per-workflow port noted as out-of-scope in #2648.
See molecule-controlplane#420.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:24:43 -07:00
Hongming Wang
3c127ae3b9
Merge pull request #2647 from Molecule-AI/a11y/canvas-legend-focus-touch
canvas/Legend: focus rings + 24x24 close-button touch target
2026-05-03 23:13:33 +00:00
Hongming Wang
98da627170
Merge pull request #2648 from Molecule-AI/fix/sweep-stale-e2e-tighter-threshold
ci: tighten e2e cleanup race window 120m → ~45m worst case
2026-05-03 23:12:22 +00:00
Hongming Wang
3cd8c53de0 ci: tighten e2e cleanup race window 120m -> ~45m worst case
Two changes that close one of the leak classes from the
molecule-controlplane#420 vCPU audit:

1. sweep-stale-e2e-orgs.yml: cron */15 (was hourly), MAX_AGE_MINUTES
   30 (was 120). E2E runs are 8-25 min wall clock; 30 min is safely
   above the longest run while shrinking the worst-case leak window
   from ~2h to ~45 min (15-min sweep cadence + 30-min threshold).

2. canary-staging.yml teardown: the per-slug DELETE used `>/dev/null
   || true`, which swallowed every failure. A 5xx or timeout from CP
   looked identical to "successfully deleted" and the canary tenant
   kept eating ~2 vCPU until the sweeper caught it. Now we capture
   the response code and surface non-2xx as a workflow warning that
   names the leaked slug.

The exit semantics stay unchanged — a single-canary cleanup miss
shouldn't fail-flag the canary itself when the actual smoke check
passed. The sweeper is the safety net for whatever slips past.

Caught during the molecule-controlplane#420 audit on 2026-05-03 —
3 e2e canary tenant orphans were running for 24-95 min, all under
the previous 120-min sweep threshold so they went unnoticed until
manual cleanup. Same `|| true` pattern exists in
e2e-staging-{canvas,external,saas,sanity}.yml; out of scope for
this PR (mechanical port; tracking separately) but the sweeper
tightening covers all of them by reducing the safety-net latency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:08:40 -07:00
Hongming Wang
69fcfe9b3a
Merge pull request #2646 from Molecule-AI/auto-sync/main-1141a429
chore: sync main → staging (auto, ff to 1141a429)
2026-05-03 23:05:05 +00:00
Hongming Wang
24d64677ab canvas/Legend: focus rings + 24x24 close-button touch target
Two small a11y fixes for the floating legend.

1. Both buttons (open pill + close ×) had no focus-visible ring —
   keyboard users couldn't tell where focus landed. Added the
   accent-ring pattern used across the rest of the canvas.

2. Close button was a ~10x16px hit area — well below WCAG 2.5.5's
   24x24 minimum. Bumped to w-6 h-6 with negative margin so the
   visible × stays in the same spot but the hit area + focus ring
   are larger. Hover bg added to make the hit area visible on hover.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:04:04 -07:00
molecule-ai[bot]
1141a42910
Merge pull request #2645 from Molecule-AI/staging
staging → main: auto-promote f689c81
2026-05-03 22:54:00 +00:00
Hongming Wang
84448d452b
Merge pull request #2644 from Molecule-AI/a11y/canvas-cookie-consent-not-modal
canvas/CookieConsent: stop claiming aria-modal without a focus trap
2026-05-03 22:39:44 +00:00
Hongming Wang
f689c81a70
Merge pull request #2642 from Molecule-AI/auto-sync/main-bae27270
chore: sync main → staging (auto, ff to bae27270)
2026-05-03 22:38:47 +00:00
Hongming Wang
2268027581
Merge pull request #2643 from Molecule-AI/feat/synth-e2e-claude-code-minimax
feat(synth-e2e): switch canary to claude-code + MiniMax-M2.7-highspeed
2026-05-03 22:38:35 +00:00
Hongming Wang
652124284b canvas/CookieConsent: stop pretending to be a modal + fix link/button focus
Three fixes for the cookie banner:

1. role="dialog" aria-modal="true" → <section role="region">. The
   banner has no focus trap, doesn't block the page, and the user
   can keep using the canvas while it's up — none of which are modal
   semantics. Claiming aria-modal="true" without a trap actively
   harms screen-reader users: they're told the rest of the page is
   inert, jump into the banner, and then can't escape. Region
   semantics let AT navigate around it normally. (Forcing a modal
   cookie banner would also be a dark pattern under GDPR.)

2. Privacy-policy link: hover:text-accent → hover:text-accent-strong.
   The original was a no-op (same color). Also added focus-visible
   ring + underline-offset so the link is readable AND keyboard-
   distinguishable in both themes.

3. Both buttons: focus-visible:ring-2 + ring-offset-surface so
   keyboard users see where focus lands. Mouse clicks unchanged
   thanks to focus-visible.

Tests: swapped getByRole("dialog") → getByRole("region") in 8
existing tests, then tightened the role-assertion test into a
regression guard that explicitly asserts NO aria-modal and NO
dialog role exist. Full suite: 1220/1220.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 15:37:06 -07:00
Hongming Wang
79a0203798 feat(synth-e2e): switch canary to claude-code + MiniMax-M2.7-highspeed
Cuts the per-run LLM cost ~10x (MiniMax M2.7 vs gpt-4.1-mini) and
removes the recurring OpenAI-quota-exhaustion failure mode that took
the canary down on 2026-05-03 (#265 — staging quota burnt for ~16h).

Path:
  E2E_RUNTIME=claude-code (default)
  → workspace-configs-templates/claude-code-default/config.yaml's
    `minimax` provider (lines 64-69)
  → ANTHROPIC_BASE_URL auto-set to api.minimax.io/anthropic
  → reads MINIMAX_API_KEY (per-vendor env, no collision with
    GLM/Z.ai etc.)

Workflow changes (continuous-synth-e2e.yml):
- Default runtime: langgraph → claude-code
- New env: E2E_MODEL_SLUG (defaults to MiniMax-M2.7-highspeed,
  overridable via workflow_dispatch)
- New secret wire: E2E_MINIMAX_API_KEY ←
  secrets.MOLECULE_STAGING_MINIMAX_API_KEY
- Per-runtime missing-secret guard: claude-code requires MINIMAX,
  langgraph/hermes require OPENAI. Cron firing hard-fails on missing
  key for the active runtime; dispatch soft-skips so operators can
  ad-hoc test without setting up the secret first
- Operators can still pick langgraph/hermes via workflow_dispatch;
  the OpenAI fallback path stays wired

Script changes (tests/e2e/test_staging_full_saas.sh):
- SECRETS_JSON branches on which key is set:
    E2E_MINIMAX_API_KEY → {MINIMAX_API_KEY: <key>}  (claude-code path)
    E2E_OPENAI_API_KEY  → {OPENAI_API_KEY, HERMES_*, MODEL_PROVIDER}  (legacy)
  MiniMax wins when both are present — claude-code default canary
  must not accidentally consume the OpenAI key

Tests (new tests/e2e/test_secrets_dispatch.sh):
- 10 cases pinning the precedence + payload shape per branch
- Discipline check verified: 5 of 10 FAIL on a swapped if/elif
  (precedence inversion), all 10 PASS on the fix
- Anchors on the section-comment header so a structural refactor
  fails loudly rather than silently sourcing nothing

The model_slug dispatcher (lib/model_slug.sh) needs no change:
E2E_MODEL_SLUG override path is already wired (line 41), and
claude-code template's `minimax-` prefix matcher catches
"MiniMax-M2.7-highspeed" via lowercase-on-lookup.

Operator action required to land green:
- Set MOLECULE_STAGING_MINIMAX_API_KEY in repo secrets
  (Settings → Secrets and Variables → Actions). Use
  `gh secret set MOLECULE_STAGING_MINIMAX_API_KEY -R Molecule-AI/molecule-core`
  to avoid leaking the value into shell history.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 15:35:14 -07:00
molecule-ai[bot]
bae2727074
Merge pull request #2641 from Molecule-AI/staging
staging → main: auto-promote 2e96860
2026-05-03 15:33:45 -07:00
Hongming Wang
2c4d92d9bc
Merge pull request #2640 from Molecule-AI/fix/canary-classify-provider-quota
test(e2e): canary classifies provider-quota 429 as operator-action, not platform regression
2026-05-03 22:21:01 +00:00
Hongming Wang
4c49ff75f6 test(e2e): canary classifies provider-quota 429 as operator-action, not platform regression
The staging canary's A2A step has a ladder of specific regression
classifiers (hermes-agent down, model_not_found, Invalid API key,
etc.) followed by a generic "error|exception" catch-all. Provider-
side OpenAI 429 quota errors fell through to the catch-all, so the
canary issue body and CI log just said "A2A returned an error-shaped
response" — which is technically true but obscures the actual
operator action.

This adds a 7th classifier above the catch-all for "exceeded your
current quota" / "insufficient_quota" — both terms appear in
OpenAI's quota-exhaustion 429 response. When matched, the failure
message names the operator action directly (top up MOLECULE_STAGING_OPENAI_KEY
or rotate the secret) and links to #2578.

Why this is correct, not "lowering the bar":
- Steps 0–7 of the canary cover full platform health (CP up, tenant
  provisioned, DNS+TLS reachable, workspace booted, A2A delivered).
- Reaching step 8 with a provider-side 429 means the platform IS
  healthy — the failure is downstream of all platform invariants.
- The canary still exits 1 (CI stays red, threshold-3 alarm still
  fires); only the failure message changes.
- All 6 existing specific classifiers run BEFORE this one, so any
  real platform regression is still caught with its specific message.

Verification:
- Regex tested against the actual 429 string from canary run 25291517608:
    "API call failed after 3 retries: HTTP 429: You exceeded your current quota..."
  → matches 
- Negative tests: "PONG", "hermes-agent unreachable" → no match 
- bash -n syntax check passes
- shellcheck -S error clean

Tracking: #2593 (canary), #2578 (root cause)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 15:18:42 -07:00
Hongming Wang
2e9686036d
Merge pull request #2639 from Molecule-AI/a11y/canvas-search-dialog-first-match
canvas/SearchDialog: auto-highlight first match + semantic placeholder
2026-05-03 22:11:31 +00:00
Hongming Wang
2bc304bfd3
Merge pull request #2638 from Molecule-AI/auto-sync/main-d2c0041b
chore: sync main → staging (auto, ff to d2c0041b)
2026-05-03 22:10:16 +00:00
Hongming Wang
7ca764f917 canvas/SearchDialog: auto-highlight first match + semantic placeholder
Two small UIUX fixes for Cmd+K search.

1. Auto-highlight the first match while the user types. Before, Enter
   on a non-empty query was a no-op — focusedIndex stayed at -1 until
   the user pressed ↓. Standard search-palette behavior is to highlight
   the top result so Enter just works. Empty query keeps -1 (opening
   the dialog shows ALL workspaces; arbitrarily pinning one looks
   wrong).

2. placeholder-zinc-400 → placeholder-ink-soft. The hardcoded zinc
   broke the semantic-token pattern other inputs use; placeholder now
   flips with theme correctly. (Also reordered focus:outline-none
   ahead of the focus-visible variants — cosmetic, more idiomatic.)

Tests: replaced the "resets to -1" test with two new ones — auto-
highlight on a matching query (Enter selects without ArrowDown), and
no-results query stays a no-op. Full suite 1220/1220.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 15:09:01 -07:00
molecule-ai[bot]
d2c0041b2b
Merge pull request #2637 from Molecule-AI/staging
staging → main: auto-promote 149d0bf
2026-05-03 15:03:39 -07:00
Hongming Wang
149d0bf3d7
Merge pull request #2635 from Molecule-AI/auto-sync/main-e4db4cfb
chore: sync main → staging (auto, ff to e4db4cfb)
2026-05-03 14:48:58 -07:00
Hongming Wang
c6eec15292
Merge pull request #2636 from Molecule-AI/a11y/canvas-context-menu-clamp
canvas/ContextMenu: clamp to viewport + semantic focus ring
2026-05-03 21:43:00 +00:00
Hongming Wang
68f8fa2621 canvas/ContextMenu: clamp position to viewport + semantic focus ring
Two small fixes for the workspace right-click menu:

1. Off-screen clamp. Right-clicking near the right or bottom edge of
   the canvas put part of the menu past the viewport — items hidden
   under the scrollbar / off the screen. The menu now measures itself
   on the same rAF that auto-focuses the first item, and shifts back
   inside with an 8px margin (matching the floating-tooltip top-edge
   clamp in Tooltip.tsx). Falls back to the raw cursor coords for the
   first paint frame so there's no flash.

2. focus:ring-zinc-600 → focus-visible:ring-accent/50. The hardcoded
   zinc tone broke the semantic-token pattern every other surface
   uses; flipping to focus-visible also stops the ring from showing
   when items are clicked with the mouse (only keyboard nav now
   triggers the ring, matching Toolbar/SidePanel behavior).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 14:40:18 -07:00
Hongming Wang
e4db4cfb11
Merge pull request #2625 from Molecule-AI/staging
staging → main: auto-promote 9fd52e9
2026-05-03 14:38:42 -07:00
Hongming Wang
65b42c33b9
Merge pull request #2634 from Molecule-AI/chore/canvas-e2e-error-detail
canvas/e2e: surface admin-orgs row + workspace body on failure
2026-05-03 21:05:25 +00:00
Hongming Wang
9d45211fd3 canvas/e2e: surface admin-orgs row + workspace body on failure
Two diagnostic upgrades to the Playwright staging-setup harness, both
zero-behavior-change:

1. provision-failed throw now includes the full admin-orgs row (boot
   stage, last error, terraform/SSM state, etc) instead of just the
   slug. Every "provision failed: <slug>" in CI history was followed
   by a manual repro to find out WHY — that round-trip is gone.

2. workspace-failed throw dumps the full /workspaces/{id} body when
   last_sample_error is empty. Boot crashes, image-pull errors,
   missing PYTHONPATH, and OpenAI-quota-at-startup all surface as a
   bare "Workspace failed:" today (see #2632). Now they carry the
   boot_stage / image / last_error fields the API row exposes.

No fix for the underlying flakes — those are tracked in #2632 (CP race)
and #2578 (OpenAI quota). This just stops them looking identical in the
CI log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 14:01:50 -07:00
Hongming Wang
14494fe67c
Merge pull request #2633 from Molecule-AI/a11y/canvas-toaster-esc-focus
canvas/Toaster: Esc dismiss + focus ring + bigger touch target
2026-05-03 20:58:02 +00:00
Hongming Wang
3b244ca6c6 canvas/Toaster: add Esc dismiss + focus-visible ring + larger touch target
Three small a11y fixes for the global toast surface:

1. Esc dismisses the newest toast. Errors never auto-expire, so without
   a keyboard shortcut a keyboard-only user has to tab through the entire
   app to reach the × button on a stuck error.

2. Dismiss button gets focus-visible ring + theme-aware tint. The previous
   `opacity-70 hover:opacity-100` gave no visible focus indicator (WCAG
   2.4.7). Info toasts use the semantic surface that flips with theme,
   so the dismiss tint splits per type — accent ring on info, white ring
   on the always-dark success/error toasts.

3. Touch target bumps from p-1 (~24x24) to w-7 h-7 (28x28) toward WCAG
   2.5.5 AAA's 44x44 ideal.

Tests: 5 new vitest cases covering Esc on info/error, no-op on empty
queue, accessible label, and per-toast click dismissal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:55:24 -07:00
Hongming Wang
18e88e7039
Merge pull request #2630 from Molecule-AI/a11y/canvas-tooltip-esc-dismiss
a11y(canvas): Tooltip Esc-to-dismiss (WCAG 1.4.13)
2026-05-03 20:36:44 +00:00
Hongming Wang
f7d663d19a
Merge pull request #2629 from Molecule-AI/feat/external-connect-hermes-tab
feat(canvas): add Hermes/Codex/OpenClaw tabs to ExternalConnectModal + default to Universal MCP
2026-05-03 20:29:41 +00:00
Hongming Wang
c8e422f6c6
Merge pull request #2627 from Molecule-AI/fix/canvas-chat-agent-prose-brightness
fix(canvas): brighten agent chat prose body in dark mode
2026-05-03 20:29:33 +00:00
Hongming Wang
1d303ee75e a11y(canvas): Tooltip Esc-to-dismiss (WCAG 1.4.13)
WCAG 1.4.13 (Content on Hover or Focus) requires that tooltip content
be DISMISSIBLE without moving pointer hover or keyboard focus. Tooltip
had no escape hatch — once a keyboard user tabbed onto a control with
a tooltip, the tooltip stayed visible until they tabbed away (which
moves focus and may not be possible if the tooltip is itself blocking
content the user needs to see, e.g. for screen-magnifier users).

Add a window-level Escape listener that's active only while a tooltip
is shown. Pressing Esc clears the tooltip without moving focus or
breaking the hover state, satisfying the dismissible criterion.

Used `capture: true` so we beat any modal/dialog Esc handler that
might also be listening — the tooltip belongs to the focused control,
not the modal it sits inside.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:23:08 -07:00
Hongming Wang
1ec7e4af6d
Merge branch 'staging' into feat/external-connect-hermes-tab 2026-05-03 13:16:32 -07:00
Hongming Wang
ae4739f35b
Merge branch 'staging' into fix/canvas-chat-agent-prose-brightness 2026-05-03 13:16:25 -07:00
Hongming Wang
6f203c5646
Merge pull request #2628 from Molecule-AI/test/synth-e2e-eic-terminal-probe
test(e2e): add canvas-terminal diagnose probe to synth-E2E (#269 partial)
2026-05-03 20:14:50 +00:00
Hongming Wang
ff0d4dae77 fix(external-connect): address self-review criticals — config corruption + durability
Self-review of the modal-tab additions caught footguns in the new
hermes/codex/openclaw snippets. Ship the fixes before merge.

Critical 1 — Hermes `cat >> ~/.hermes/config.yaml` corrupts existing
configs. Most existing hermes installs have a top-level gateway:
block; appending creates a duplicate, which YAML rejects. Replaced
the auto-append with explicit instructions: 'under your existing
gateway: block, add a plugin_platforms entry'.

Critical 2 — Codex `cat >> ~/.codex/config.toml` corrupts on
re-run. TOML rejects duplicate [mcp_servers.molecule] tables; a
second run breaks codex parse. Replaced auto-append with commented
config block + explicit 'open ~/.codex/config.toml in your editor
and paste'. Canvas-side token stamping still hits the literal in
the comment so the operator's clipboard has the real token already
substituted.

Required 3 — OpenClaw `onboard --non-interactive` missing
provider/model defaults. Added explicit --provider + --model
placeholders in a commented form so operators see what's needed
without a stub default applying silently.

Required 4 — OpenClaw gateway started with bare '&' dies on
terminal close. Switched to nohup + log file + disown, with a note
that systemd is the right answer for production.

Optional 5 + 6 (env_vars cleanup, tests) deferred — env_vars stripped
to keep the in-tree-vs-external surface narrow; tests for the new
response fields can land separately when external_connection.go is
next touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:12:54 -07:00
Hongming Wang
01bbf4c87b
Merge pull request #2626 from Molecule-AI/ui/canvas-approvalbanner-polish
fix(canvas): ApprovalBanner Approve/Deny button polish
2026-05-03 20:12:13 +00:00
Hongming Wang
e89dd892ac
Merge pull request #2624 from Molecule-AI/fix/canvas-agentcomms-light-mode-prose
fix(canvas): AgentCommsPanel light-mode markdown contrast
2026-05-03 20:07:43 +00:00
Hongming Wang
eba0c5e3f1 feat(canvas): add Hermes/Codex/OpenClaw tabs to ExternalConnectModal + default to Universal MCP
The External Connect modal had tabs for Python SDK / curl / Claude Code
channel / Universal MCP. Operators using hermes / codex / openclaw as
their external runtime had no copy-paste; they pieced together
WORKSPACE_ID + PLATFORM_URL + auth_token into config files by reading
docs.

Adds three runtime-specific snippets stamped server-side:

- **Hermes** — installs molecule-ai-workspace-runtime + the
  hermes-channel-molecule plugin, exports the 4 env vars, and writes
  the gateway.plugin_platforms.molecule block into ~/.hermes/config.yaml.
  Same long-poll-based push semantics the Claude Code channel tab
  delivers (push parity with the in-tree template-hermes adapter).

- **Codex** — wires the molecule_runtime A2A MCP server into
  ~/.codex/config.toml ([mcp_servers.molecule] block with env_vars
  passthrough + literal env values). Outbound tools only — codex's
  MCP client doesn't route arbitrary notifications/* (verified by
  reading codex-rs/codex-mcp/src/connection_manager.rs); push parity
  on external codex would need a separate bridge daemon, tracked
  as future work. Snippet calls this out so operators know to pair
  with Python SDK if they need inbound delivery.

- **OpenClaw** — installs openclaw + onboards, wires the molecule
  MCP server via openclaw mcp set, starts the gateway on loopback.
  Same outbound-tools-only caveat as codex; the in-tree template-
  openclaw adapter implements the full sessions.steer push path,
  but an external setup would need the same bridge daemon to translate
  platform inbox events into sessions.steer calls. Future work.

Default open tab changed from "Claude Code" to "Universal MCP".
Universal MCP is runtime-agnostic and works as a starting point for
any operator regardless of their downstream agent runtime; runtime-
specific tabs are still one click away. Pre-2026-05-03 the modal
defaulted to Claude Code, so operators using non-Claude runtimes
opened to a tab they had to skip past.

Tab order also reorganized:
  Universal MCP → Python SDK → Claude Code → Hermes → Codex → OpenClaw → curl → Fields

Each runtime-specific tab is gated on the platform supplying the
snippet (older platform builds without the field don't show empty
tabs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:07:19 -07:00