Commit Graph

675 Commits

Author SHA1 Message Date
molecule-ai[bot]
0e2cc048ec
Merge pull request #655 from Molecule-AI/feat/issue-499-hermes-stacked-system-messages
feat(hermes): stacked system message merge + Nous sampling defaults (#499 #500)
2026-04-17 07:30:35 +00:00
molecule-ai[bot]
d21f4ff3fb
Merge pull request #647 from Molecule-AI/chore/eco-watch-2026-04-17-b
chore(eco-watch): 2026-04-17 daily survey (pass 2) — AI Hedge Fund
2026-04-17 07:30:22 +00:00
39aa764ce1 fix(gate-1): merge eco-watch pass-2 + pass-3 entries (AI Hedge Fund + Strix)
Both chore/eco-watch-2026-04-17-b and chore/eco-watch-2026-04-17-c added
entries at the end of ecosystem-watch.md. Kept both entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:29:55 +00:00
molecule-ai[bot]
eef63734ac
Merge pull request #660 from Molecule-AI/chore/eco-watch-2026-04-17-c
chore(eco-watch): add Strix — AI security agent graph (Apr 17 pass 3)
2026-04-17 07:27:54 +00:00
Molecule AI Backend Engineer
e0d674089f feat(platform): merge stacked system messages for Hermes/vLLM (#499)
vLLM (and Nous Hermes portal) only accept a single system message.
When the platform builds a messages array from multiple sources
(base system prompt + workspace config + per-session override), the
consecutive system entries at the front cause vLLM to reject or
silently drop all but the first.

Adds mergeSystemMessages() — a stateless pre-flight transform in the
handlers package that collapses the uninterrupted leading run of
{"role":"system"} entries into one, joining their content with "\n\n".
Non-system messages between system messages are not touched; a single
system message is returned as-is (no allocation).

10 unit tests cover: stacked merge, single-unchanged, no-system passthrough,
three-message collapse, interleaved user (trailing system not merged),
only-system-messages, empty slice, nil slice, non-string content, and
assistant-leading passthrough.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:19:30 +00:00
Molecule AI Research Lead
c3343a0f84 chore(eco-watch): add Strix (usestrix/strix) — AI security agent graph
24.1k-star Apache-2.0 security testing platform using a graph-of-agents
architecture; +202 stars Apr 17 2026. Demand signal for domain-specific
multi-agent orchestration and audit-trail patterns adjacent to GH #594.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:17:11 +00:00
molecule-ai[bot]
3d3f1d5543
feat(canvas): add max effort level to ConfigTab dropdown (#653)
feat(canvas): add max effort level to ConfigTab dropdown (#653)
2026-04-17 07:04:57 +00:00
molecule-ai[bot]
cb8f3989c3
feat(hermes): plumb response_format=json_schema for structured output (#498)
feat(hermes): plumb response_format=json_schema for structured output (#498)
2026-04-17 07:03:45 +00:00
af00a6c128 fix(merge): combine response_format (#498) and tools (#497) in hermes_executor
Both PRs restructured the same chat.completions.create() call to use a
create_kwargs dict. Resolved by keeping both __init__ params and both
conditionals in the create_kwargs block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:03:22 +00:00
Molecule AI Frontend Engineer
a7cd538fc3 feat(canvas): add max effort level to ConfigTab dropdown (#653)
Adds a fifth option to the effort <select> in the Claude Settings section:

  <option value="max">max — absolute ceiling</option>

The dropdown now offers: low / medium / high / xhigh / max.

effort is typed as string? so no interface update required.
Test updated: source-assertion count "four" → "five", new toYaml
serialization test for effort: max.

641/641 tests pass. Build clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:58:29 +00:00
molecule-ai[bot]
c9b8c26d5f
feat(hermes): native tools=[] parameter instead of text-in-prompt workaround (#497)
feat(hermes): native tools=[] parameter instead of text-in-prompt workaround (#497)
2026-04-17 06:56:10 +00:00
molecule-ai[bot]
f127d4c0a6
Merge pull request #639 from Molecule-AI/feat/issue-608-effort-task-budget-ui
Merge gate passed (all 7 gates). Adds effort + task_budget to ConfigTab Claude Settings section. Dark zinc palette, conditionally shown for claude/anthropic runtimes, yaml serialization omits zero/empty values. UNSTABLE = known App token scope gap.
2026-04-17 06:49:28 +00:00
molecule-ai[bot]
6ed46fa3b1
Merge pull request #640 from Molecule-AI/fix/issue-613-git-token-helper-path
Merge gate passed (all 7 gates). Root cause fix for GH_TOKEN expiry: copies molecule-git-token-helper.sh into /app/scripts/ and corrects entrypoint.sh path. UNSTABLE = known App token scope gap.
2026-04-17 06:49:21 +00:00
molecule-ai[bot]
2ab7054a26
Merge pull request #646 from Molecule-AI/fix/migration-025-fk-type
Merge gate passed. +2/-2 FK type fix: workspace_id TEXT→UUID in 025, org_id TEXT→UUID in 026 — matches workspaces.id (UUID PK). Schema migration — CEO explicit authorization in chat (boot-blocker/urgent). UNSTABLE = known App token scope gap.
2026-04-17 06:46:08 +00:00
Molecule AI Research Lead
9a60b43da0 chore(eco-watch): 2026-04-17 daily survey — AI Hedge Fund
New LOW entry: virattt/ai-hedge-fund (55.7k, +763 today) — 19-agent
financial-analysis reference implementation. High-visibility demand signal
for domain-specific multi-agent orchestration in finance. Not a competing
platform but a compelling org-template opportunity (19 specialist agents
coordinated by a PM workspace via A2A).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:43:34 +00:00
molecule-ai[bot]
f6673b21a0
Merge pull request #641 from Molecule-AI/feat/issue-595-cloudflare-artifacts-demo
Merge gate passed (all 7 gates). Cloudflare Artifacts demo integration: 4 routes behind WorkspaceAuth, CF token from env only, import_url HTTPS enforced, CF 5xx errors sanitized, parameterized SQL throughout. Migration 028 uses CREATE TABLE IF NOT EXISTS. Schema migration — CEO explicit authorization in chat (urgent/first-mover). Tip SHA daf52da verified. UNSTABLE = known App token scope gap.
2026-04-17 06:43:21 +00:00
Hongming Wang
f7b04c0543 fix(migrations): TEXT→UUID FK type mismatch blocking all E2E runs
Migrations 025 + 026 declared workspace_id/org_id as TEXT but
workspaces.id is UUID — Postgres rejects the FK constraint, crashing
every E2E run on main since these migrations were merged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 23:40:22 -07:00
Molecule AI Backend Engineer
daf52daa1d fix(platform): address security review findings on CF Artifacts (#641)
Four findings from the security audit on PR #641:

FIX 1 (MEDIUM): import_url scheme validation
- Reject non-HTTPS import URLs with 400 before forwarding to CF API.
  Prevents SSRF via http://, git://, ssh://, file:// etc.

FIX 2 (MEDIUM): CF 5xx error leakage
- Add cfErrMessage() helper: returns "upstream service error" for CF 5xx
  responses and non-CF errors, passes through 4xx messages.
- Applied at all four CF-error response sites (Create, Get, Fork, Token).

FIX 3 (LOW): repo name validation
- Add package-level repoNameRE = ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$
- Validate in Create and Fork handlers when caller supplies an explicit name.
  Auto-generated names ("molecule-ws-<id>") are always safe and skip validation.

FIX 4 (LOW): response body size limit in CF client
- Wrap resp.Body with io.LimitReader(1 MB) before json.NewDecoder in do().
  Prevents memory exhaustion from a runaway/malicious CF response.

Tests: 16 new tests covering all four fixes (cfErrMessage 4xx/5xx/non-API,
import_url non-HTTPS cases, invalid repo names in Create and Fork).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:39:47 +00:00
Molecule AI Backend Engineer
3bcb2b21a5 feat(platform): Cloudflare Artifacts demo integration (#595)
Add a minimal but complete integration with the Cloudflare Artifacts API
(private beta Apr 2026, public beta May 2026) — "Git for agents" versioned
workspace-snapshot storage.

## What's included

**`platform/internal/artifacts/client.go`** — typed Go HTTP client for the
CF Artifacts REST API:
- CreateRepo, GetRepo, ForkRepo, ImportRepo, DeleteRepo
- CreateToken, RevokeToken
- CF v4 response-envelope decoding; *APIError with StatusCode + Message

**`platform/internal/handlers/artifacts.go`** — four workspace-scoped
Gin handlers (all behind WorkspaceAuth middleware):
- POST /workspaces/:id/artifacts — attach or import a CF Artifacts repo
- GET  /workspaces/:id/artifacts — get linked repo info (DB + live CF)
- POST /workspaces/:id/artifacts/fork — fork the workspace's repo
- POST /workspaces/:id/artifacts/token — mint a short-lived git credential

**`platform/migrations/028_workspace_artifacts.up.sql`** — `workspace_artifacts`
table: one-to-one link between a workspace and its CF Artifacts repo.
Credentials are never stored; only the credential-stripped remote URL.

**`platform/internal/router/router.go`** — wire the four routes into the
existing wsAuth group.

## Configuration
Two env vars gate the feature (returns 503 when either is absent):
- CF_ARTIFACTS_API_TOKEN — Cloudflare API token with Artifacts write perms
- CF_ARTIFACTS_NAMESPACE — Cloudflare Artifacts namespace name

## Tests
- 10 client-level tests (httptest.Server + CF v4 envelope mocks)
- 14 handler-level tests (sqlmock DB + mock CF server)
- Helper unit tests for stripCredentials, cfErrToHTTP

All 21 packages pass (go test ./...).

Closes #595

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:28:58 +00:00
molecule-ai[bot]
3c88cc7c1e
Merge pull request #634 from Molecule-AI/fix/issue-615-cap-monthly-spend
Merge gate passed (all 7 gates). Caps monthly_spend on heartbeat upsert: negative→0, >0B→0B, zero=no-update path. Comment-only conflicts resolved (identical logic both sides). Depends on #611's monthly_spend column — merged first. UNSTABLE = known App token scope gap.
2026-04-17 06:27:35 +00:00
77313434b1 fix(gate-1): resolve merge conflicts with main
Both conflicts were comment-only — identical logic on both sides:
- registry.go: kept main's wording ("accidentally clearing") for the
  monthly_spend comment in Heartbeat; logic is unchanged
- workspace.go: kept HEAD's comment (describes PR #634's clamping
  behaviour: [0, maxMonthlySpend]); logic is unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:27:14 +00:00
c509eca31d fix(template): copy molecule-git-token-helper.sh into image and fix path
Two bugs prevented the git credential helper (merged in #567) from ever
running at workspace boot:

1. Dockerfile never COPY'd scripts/molecule-git-token-helper.sh into the
   image — only gh-wrapper.sh was copied from scripts/. Result: the helper
   binary did not exist in any built container image.

2. entrypoint.sh looked for the helper at /workspace-template/scripts/...
   but /workspace-template/ is not a path that exists inside the container
   (WORKDIR is /app, no /workspace-template mount). The `if [ -f ... ]`
   guard silently fell through to the WARNING branch on every boot since
   #567 merged — the helper was never registered.

Fix:
- Add `COPY scripts/molecule-git-token-helper.sh ./scripts/` to Dockerfile
  so the script lands at /app/scripts/ in the image (matching WORKDIR /app)
- Update HELPER_SCRIPT path in entrypoint.sh from
  /workspace-template/scripts/... to /app/scripts/...

After this fix, every workspace container registers the helper at boot via:
  git config --global credential.https://github.com.helper \
    "!/app/scripts/molecule-git-token-helper.sh"

Closes #613.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:27:08 +00:00
molecule-ai[bot]
7538b2a95c
Merge pull request #611 from Molecule-AI/feat/issue-541-budget-limit-backend
Merge gate passed (all 7 gates). Adds budget_limit + monthly_spend columns via 027_workspace_budget (ADD COLUMN IF NOT EXISTS — idempotent). A2A budget enforcement is fail-open on DB errors. WorkspaceAuth on all budget routes. Schema migration — CEO explicit authorization in chat. Merging before #634 which writes to monthly_spend.
2026-04-17 06:25:02 +00:00
Molecule AI Frontend Engineer
848b745b6e feat(canvas): expose effort + task_budget in ConfigTab (#608)
Adds two new Claude API primitives (Opus 4.7+) as configurable workspace
fields in the Config tab form:

  effort: 'low' | 'medium' | 'high' | 'xhigh'
    Maps to output_config.effort in the Anthropic Messages API.
    Controls thinking depth — xhigh enables extended thinking mode.

  task_budget: integer (token count, 0 = unset)
    Maps to output_config.task_budget.total; requires beta header
    task-budgets-2026-03-13. Lets operators cap token spend per task.

Both fields are stored as top-level keys in config.yaml and read by
claude_sdk_executor.py (workspace-template side, tracked in #608).

Canvas changes:
- form-inputs.tsx: effort?: string, task_budget?: number added to
  ConfigData; DEFAULT_CONFIG initialises them to "" / 0
- yaml-utils.ts: toYaml() emits effort + task_budget (omits when
  empty/zero); parseYaml() already handles plain string/integer keys
- ConfigTab.tsx: new collapsible "Claude Settings" section (defaultOpen=false)
  shown when runtime === "claude-code" OR model name contains "claude"
  or "anthropic". Dropdown for effort (4 options + unset), number input
  for task_budget (step 1000, 0 = unset).

Tests (25 cases in ClaudeSettings.test.tsx):
  - toYaml serialises all four effort values + omits empty/undefined
  - toYaml serialises task_budget + omits 0/undefined
  - effort appears before task_budget in YAML output
  - parseYaml round-trips both fields correctly
  - DEFAULT_CONFIG shape assertions
  - Source assertions for section guards + option values
  - React rendering: section visible for claude-code/claude model,
    hidden for non-Claude runtime (crewai + gpt-4o)

640/640 tests pass. Build clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:24:36 +00:00
molecule-ai[bot]
ea43256a87
Merge pull request #636 from Molecule-AI/fix/issue-631-migration-gap
Merge gate passed. Pure file renames (+0/-0): 026→025 (workspace_token_usage), 027→026 (org_plugin_allowlist). Closes migration numbering gap so sequential runners proceed past 024. Schema migration — CEO explicit authorization in chat. NOTE: if production DB recorded old filenames 026/027 as applied, verify runner idempotency before restart to avoid double-application.
2026-04-17 06:23:05 +00:00
Molecule AI Backend Engineer
f1fa92ad84 fix(migrations): renumber budget migration 025→027 to follow gap fix (#631)
Rebase on origin/fix/issue-631-migration-gap which inserts token_usage
(025) and org_plugin_allowlist (026); bump workspace_budget from 025 to
027 so the sequential runner applies all three in the correct order.
Update workspace_budget_test.go and workspace_test.go to match the
transaction-wrapped INSERT (BeginTx/Commit) introduced on main and the
resulting 10-arg WithArgs call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:22:09 +00:00
Molecule AI Frontend Engineer
28dfa185aa fix(canvas): mock WorkspaceUsage in BudgetLimit.DetailsTab test
DetailsTab renders WorkspaceUsage alongside BudgetSection. The test suite
sets api.get to return [] (a valid empty peers list) but WorkspaceUsage
calls api.get for metrics and crashes on undefined input_tokens when the
mock returns an array instead of a WorkspaceMetrics object.

Add a stub vi.mock following the same pattern already used for BudgetSection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:22:07 +00:00
molecule-ai[bot]
40a8e41808
Merge pull request #635 from Molecule-AI/chore/eco-watch-2026-04-17-clean
Merge gate passed. Docs-only — ecosystem-watch.md entries only, no code/schema/auth. UNSTABLE = known App token scope gap.
2026-04-17 06:21:03 +00:00
Molecule AI Backend Engineer
fce0be30fd fix(#611): remove budget_limit from PATCH /workspaces/:id and strip financial fields from GET
Security Auditor findings on PR #611:

Fix 1 (BLOCKING): Remove budget_limit handling from Update() entirely.
PATCH /workspaces/:id uses ValidateAnyToken — any enrolled workspace bearer
could self-clear its own spending ceiling. The dedicated AdminAuth-gated
PATCH /workspaces/:id/budget is the only authorised write path.

Fix 2 (MEDIUM): Strip budget_limit and monthly_spend from Get() response
before c.JSON(). GET /workspaces/:id is on the open router — any caller
with a valid UUID must not read billing data.

Also updates four existing tests in workspace_budget_test.go that encoded
the old (insecure) behaviour, and adds three new regression tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:18:41 +00:00
Molecule AI Backend Engineer
dd0b282c79 fix(issue-541): move PATCH /budget to adminAuth — workspace must not self-clear ceiling
Workspace agents could previously call PATCH /workspaces/:id/budget with their
own bearer token and set budget_limit=null, defeating the entire spend enforcement
feature. GET stays on wsAuth (reading own budget is legitimate); PATCH moves to
inline AdminAuth using the same pattern as /approvals/pending.

No existing tests needed updating — all budget PATCH tests call the handler
directly and are unaffected by router-level middleware changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:18:41 +00:00
Molecule AI Backend Engineer
4e6e3745f2 fix(issue-541): correct stale 429 comment to 402 in checkWorkspaceBudget
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:18:41 +00:00
Molecule AI Backend Engineer
2fb0aacd41 fix(#541): change budget enforcement status from 429 to 402
Budget limit exceeded on A2A proxy now returns HTTP 402 PaymentRequired
instead of 429 TooManyRequests, matching the issue spec and the FE amber
banner check. Updates a2a_proxy.go, workspace_budget_test.go (renamed
ExceededReturns429 → ExceededReturns402, AboveLimitReturns429 →
AboveLimitReturns402), and migration comment. All go test ./... pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:18:41 +00:00
Molecule AI Backend Engineer
22af070ef3 feat(#541): add dedicated GET/PATCH /workspaces/:id/budget endpoints
- New BudgetHandler with GetBudget and PatchBudget methods
- GET returns budget_limit (null or int64 USD cents), monthly_spend,
  and computed budget_remaining (null when no limit, can be negative
  when over-budget so callers can see the magnitude of the overage)
- PATCH accepts {budget_limit: int64|null}; null clears the ceiling;
  validates non-negative values; re-reads DB to echo final state
- Both handlers are wired in router.go under the WorkspaceAuth group
- 14 unit tests covering happy paths, 404, 400 validation, DB errors,
  over-budget state, zero limit, and clear-limit round-trip
- All 20 packages pass go test ./... and go build ./... is clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:18:41 +00:00
Molecule AI Backend Engineer
f8106b35be feat(platform): add per-workspace budget_limit field and A2A enforcement (#541)
- Migration 025: ADD COLUMN budget_limit BIGINT DEFAULT NULL and
  monthly_spend BIGINT NOT NULL DEFAULT 0 to workspaces table
- Models: BudgetLimit *int64 in CreateWorkspacePayload;
  MonthlySpend int64 in HeartbeatPayload
- workspace.go: scanWorkspaceRow, workspaceListQuery, Get, Create, and
  Update all handle budget_limit/monthly_spend; budget_limit is gated
  as a sensitiveUpdateField
- registry.go: heartbeat conditionally writes monthly_spend only when
  payload.MonthlySpend > 0 (avoids overwriting with zero)
- a2a_proxy.go: checkWorkspaceBudget() returns 429 when
  monthly_spend >= budget_limit (NULL = no limit; fail-open on DB error)
- Tests: 8 new workspace_budget_test.go tests + patched existing tests
  for the 20-column scanWorkspaceRow and 10-param CREATE INSERT

Field type: BIGINT (int64), units: USD cents (budget_limit=500 = $5.00/month)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:18:41 +00:00
molecule-ai[bot]
5b42bd76b5
Merge pull request #629 from Molecule-AI/fix/issue-614-security-headers
Merge gate passed (all 7 gates). Adds /orgs to apiPrefixes so PR #610's allowlist routes get nosniff + X-Frame-Options headers. One-line fix + 50 lines of regression tests. UNSTABLE = known App token scope gap.
2026-04-17 06:18:25 +00:00
Hongming Wang
44cef47763
Merge pull request #630 from Molecule-AI/fix/issue-615-cap-token-counts
fix(platform): cap token counts before upsert to prevent NUMERIC overflow (#615)
2026-04-16 23:17:37 -07:00
Molecule AI Backend Engineer
3329370b1c fix(migrations): close 024→026 gap — rename 026→025 token_usage, 027→026 allowlist (#631)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:17:36 +00:00
molecule-ai[bot]
9bac2d20f9
Merge pull request #627 from Molecule-AI/feat/issue-592-wire-metrics-api
Merge gate passed (all 7 gates). Conflicts were mechanical: WorkspaceUsage.tsx full implementation over scaffold (backend #593 is live), RevealToggle.tsx 'use client' deduplicated. UNSTABLE = known GitHub App token scope gap.
2026-04-17 06:17:00 +00:00
040f674a6a fix(gate-1): resolve merge conflicts with main
Three add/add + content conflicts, all mechanical:
- WorkspaceUsage.tsx: HEAD (full live-metrics implementation wired
  to GET /workspaces/:id/metrics) over main's scaffold placeholder;
  #593 backend is now live so the TODO is fulfilled
- WorkspaceUsage.test.tsx: HEAD (full mock-api test suite, 10 tests)
  over main's scaffold tests (tested placeholder — values now stale)
- RevealToggle.tsx: both sides independently added 'use client'; kept
  main's double-quote variant ("use client") for codebase consistency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:16:36 +00:00
Molecule AI Backend Engineer
668c93e513 fix(platform): cap monthly_spend on heartbeat upsert (#615)
A malicious or buggy agent could report MonthlySpend = math.MaxInt64
causing NUMERIC overflow in the DB or incorrect budget-enforcement
comparisons downstream.

Changes:
- Add MonthlySpend int64 field to HeartbeatPayload (json:"monthly_spend")
- Clamp negative values to 0 and values above $10B (1_000_000_000_000
  cents) to the cap before any DB write
- The two-path UPDATE: when MonthlySpend > 0 after clamping, include
  monthly_spend = $7 in the UPDATE; otherwise skip to avoid accidentally
  clearing a previously-reported spend value
- 5 regression tests covering: within-bounds passthrough, negative
  clamp, math.MaxInt64 overflow clamp, exact-cap boundary, and
  zero/omitted no-update path

Note: this branch introduces MonthlySpend to HeartbeatPayload; it will
need trivial conflict resolution when feat/issue-541-budget-limit-backend
merges, as that branch also adds the field (without the cap). Keep this
branch's clamping logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:16:06 +00:00
molecule-ai[bot]
398c1e9f68
Merge pull request #628 from Molecule-AI/fix/issue-623-adminauth-origin-bypass
Merge gate passed (all 7 gates). Security fix: removes canvasOriginAllowed + isSameOriginCanvas Origin bypass from AdminAuth — bearer token is now the only accepted credential on admin routes. 3 regression tests cover forged-localhost, forged-tenant-domain, and bearer+Origin golden path. Auth PR — CEO explicit approval confirmed in chat. UNSTABLE = known GitHub App token scope gap.
2026-04-17 06:13:33 +00:00
molecule-ai[bot]
deecd01a8d
Merge pull request #606 from Molecule-AI/feat/issue-541-budget-limit-frontend
Merge gate passed (all 7 gates). All merge conflicts were mechanically additive (BudgetSection + WorkspaceUsage both kept; hydrating spinner + error banner combined; useId import preserved; WCAG a11y tests kept). UNSTABLE = known GitHub App token scope gap, not a test failure.
2026-04-17 06:10:53 +00:00
Molecule AI Frontend Engineer
bfe4e09b7e fix(canvas): move vi.mock to module top level in ZoomShortcut.test (#632)
The vi.mock("../../../store/canvas") call was nested inside an it()
block. Vitest hoists all vi.mock calls to module scope at runtime
regardless, so the code never matched its actual execution order —
prompting the "not at top level" warning that Vitest will make a hard
error in a future version.

Move the mock to after the imports, remove the now-redundant inline
call from the it() body, and add a comment explaining the hoisting rule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:09:39 +00:00
Molecule AI Frontend Engineer
a60ece77c6 fix(canvas): use explicit empty-string check in BudgetSection to preserve zero-credit budget
parseInt("0", 10) || null evaluates to null, silently converting a
zero-credit budget to unlimited. Switch to raw !== "" ? parseInt() : null
so budget_limit: 0 is sent correctly. Adds regression test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:07:08 +00:00
Molecule AI Frontend Engineer
c064200164 fix(canvas): WCAG SC 1.3.1 — programmatic label/input association in InputField
Adds useId() to the InputField helper in CreateWorkspaceDialog so every
<label> is wired to its <input> via htmlFor/id. Without this, screen readers
announced only the placeholder text, not the field name (WCAG 2.1 SC 1.3.1
Level A violation, build 4JIwTGVMjDGNLO8iMGJeC).

Affected fields: Name (required), Role, Budget limit (USD), Template.
The Hermes provider fields were already correctly wired.

Adds 6 new tests in CreateWorkspaceDialog.a11y.test.tsx verifying htmlFor/id
round-trips for each field and unique-id non-collision (602 total, all pass;
build clean; 'use client' grep empty).

Note: #554 (hydration error UI) and #556 (tier radio arrow-key nav) are
confirmed fixed in commit 76defba — audit cycle 2 was run against the
pre-fix build. #557 (zoom-to-team Z key) is a false positive — the handler
IS implemented; closing via Dev Lead once token is refreshed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:07:08 +00:00
Molecule AI Frontend Engineer
2152323cd1 feat(#541): budget settings UI with usage stats and 402 handling
Adds a dedicated BudgetSection component to the workspace details panel:
- GET /workspaces/:id/budget on mount — populates live stats (used/limit/remaining)
- Stats row + blue-500 progress bar (capped at 100%; hidden when unlimited)
- PATCH /workspaces/:id/budget for saving; input blank → budget_limit: null
- "Budget exceeded — messages blocked" amber/zinc-950 banner on any 402 response
  (GET or PATCH); banner clears on a successful subsequent save
- 'use client'; dark zinc theme throughout (zinc-800/700 inputs, blue-500 accents)

DetailsTab refactored: inline budget_limit fields removed; BudgetSection mounted
as a self-contained section between Workspace and Skills. PATCH /workspaces/:id
body no longer includes budget_limit — that concern is isolated to BudgetSection.

Tests: 21 new cases in BudgetSection.test.tsx (loading, stats, progress bar,
save, 402 GET, 402 PATCH, banner clear, non-402 errors). BudgetLimit.DetailsTab
rewritten to mock BudgetSection and verify the DetailsTab/BudgetSection
integration contract (596 total, all pass; build clean; 'use client' grep empty).

API shape: GET/PATCH /workspaces/:id/budget → {budget_limit: int64|null,
budget_used: int64, budget_remaining: int64|null}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:07:07 +00:00
Molecule AI Frontend Engineer
5d081769e5 feat(canvas): budget_limit input in workspace creation and settings UI (#541)
- Adds optional Budget limit (USD) numeric field to CreateWorkspaceDialog;
  blank = null (unlimited), populated = parsed float sent as budget_limit in
  POST /workspaces body
- Adds budget_limit field to DetailsTab edit form; saves via
  PATCH /workspaces/:id; pre-fills from current WorkspaceNodeData
- Shows 'Budget limit exceeded' warning badge when budgetUsed > budgetLimit
  (forward-compatible — badge hidden when budgetUsed is absent)
- Extends WorkspaceData, WorkspaceNodeData, and buildNodesAndEdges to carry
  budgetLimit / budgetUsed fields ready for backend hydration (issue #541 BE PR)
- Ships 22 new tests across CreateWorkspaceDialog and BudgetLimit.DetailsTab
  suites (575 total, all passing); npm run build clean; 'use client' grep empty

API shape confirmed from workspace.go and CreateWorkspacePayload struct:
  field name: budget_limit | type: number | null | units: USD

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:06:36 +00:00
Molecule AI Backend Engineer
13b8965c99 fix(platform): cap token counts before upsert to prevent NUMERIC overflow (#615)
Adversarial or buggy agents can report INT64_MAX token counts via A2A
responses. Without clamping, upsertTokenUsage would pass these directly to
Postgres NUMERIC(12,6), causing a silent upsert failure that corrupts the
workspace's cost accounting.

Fix: clamp input_tokens/output_tokens to [0, 10_000_000] before any
arithmetic or DB write. 10M tokens/call is well above any real LLM API
response; clamped values still produce valid cost rows.

Adds 4 regression tests:
- TestUpsertTokenUsage_615_CapsInt64Max      — INT64_MAX → maxTokensPerCall
- TestUpsertTokenUsage_615_CapsNegative      — negative → 0 (no DB call)
- TestUpsertTokenUsage_615_NormalValuesUnchanged — passthrough for normal counts
- TestUpsertTokenUsage_615_ExactlyAtCap      — at-cap value accepted unchanged

Closes #615

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:03:40 +00:00
Molecule AI Backend Engineer
67a9ec8fcb fix(platform): pin X-Content-Type-Options nosniff + add /orgs API prefix (#614)
SecurityHeaders() middleware already sets X-Content-Type-Options: nosniff and
X-Frame-Options: DENY globally on every response (issue #151 / PR ~securityheaders).
This commit adds the explicit acceptance test that #614 requires and extends
the apiPrefixes list to cover the new /orgs allowlist routes from PR #610.

Changes:
- securityheaders.go: add "/orgs" to apiPrefixes so allowlist routes get the
  strict CSP (no unsafe-inline) rather than the canvas-tier permissive policy
- securityheaders_test.go: TestSecurityHeaders_614_NosniffOnSSEAndAPIEndpoints
  verifies the header is present on SSE endpoint, /settings/secrets, /events,
  and /orgs paths; TestIsAPIPath gains /orgs cases

Closes #614

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:02:18 +00:00
Molecule AI Backend Engineer
cc45f0c0f6 fix(security): remove canvasOriginAllowed from AdminAuth middleware (#623)
The Origin header is trivially forgeable by any container on the Docker
network. Having canvasOriginAllowed() / isSameOriginCanvas() as auth
bypass paths in AdminAuth let any curl/container without a bearer token
reach /settings/secrets, /bundles/import, /bundles/export, /events, and
all other AdminAuth-gated routes by forging Origin: http://localhost:3000.

Fix: remove both Origin bypass branches from AdminAuth. Bearer token is
now the only accepted credential. Lazy-bootstrap fail-open (zero tokens →
pass-through) is preserved for fresh installs.

CanvasOrBearer retains the Origin bypass because it is scoped exclusively
to cosmetic routes (PUT /canvas/viewport) where a forged request has zero
security impact — worst case is viewport position corruption.

Added 3 regression tests:
- TestAdminAuth_623_ForgedOrigin_Returns401
- TestAdminAuth_623_ForgedCORSOrigin_Returns401
- TestAdminAuth_623_ValidBearer_WithOrigin_Passes

Closes #623, Closes #626

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:00:45 +00:00