Commit Graph

3996 Commits

Author SHA1 Message Date
molecule-ai[bot]
15a850ea4e
Merge pull request #695 from Molecule-AI/chore/eco-watch-2026-04-17-c
chore(eco-watch): add Anthropic Agent Skills + Microsoft APM — 2026-04-17
2026-04-17 11:21:21 +00:00
molecule-ai[bot]
572b314c4e fix(security): AdminAuth scope, token revocation, metrics auth (#682 #683 #684)
Three Offensive Security findings addressed:

#684 — AdminAuth accepts any workspace bearer token (FALSE POSITIVE).
ValidateAnyToken intentionally accepts any valid workspace token — the
platform's trust model uses workspace credentials as admin credentials.
No code change; documented as by-design in the PR body.

#682 — Deleted-workspace bearer tokens still authenticate (defense-in-depth).
The Delete handler already revokes all tokens (revoked_at = now()), so this
was a false positive. As defense-in-depth we add a JOIN against workspaces in
ValidateAnyToken so that even if revoked_at is not set (transient DB error
between status update and token revocation), the token still fails validation
once workspace.status = 'removed'.
Files: platform/internal/wsauth/tokens.go, tokens_test.go,
       platform/internal/middleware/wsauth_middleware_test.go

#683 — /metrics unauthenticated (REAL).
GET /metrics was on the open router with no auth. The Prometheus endpoint
exposes the full HTTP route-pattern map, request counts by route+status, and
Go runtime memory stats — ops intel that should not reach unauthenticated
callers. Scraper must now present a valid workspace bearer token.
File: platform/internal/router/router.go

All 16 packages pass: go test ./...

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 11:14:15 +00:00
molecule-ai[bot]
bf4f7e755e
fix(security): AdminAuth scope, token revocation, metrics auth (#682 #683 #684)
Three Offensive Security findings addressed:

#684 — AdminAuth accepts any workspace bearer token (FALSE POSITIVE).
ValidateAnyToken intentionally accepts any valid workspace token — the
platform's trust model uses workspace credentials as admin credentials.
No code change; documented as by-design in the PR body.

#682 — Deleted-workspace bearer tokens still authenticate (defense-in-depth).
The Delete handler already revokes all tokens (revoked_at = now()), so this
was a false positive. As defense-in-depth we add a JOIN against workspaces in
ValidateAnyToken so that even if revoked_at is not set (transient DB error
between status update and token revocation), the token still fails validation
once workspace.status = 'removed'.
Files: platform/internal/wsauth/tokens.go, tokens_test.go,
       platform/internal/middleware/wsauth_middleware_test.go

#683 — /metrics unauthenticated (REAL).
GET /metrics was on the open router with no auth. The Prometheus endpoint
exposes the full HTTP route-pattern map, request counts by route+status, and
Go runtime memory stats — ops intel that should not reach unauthenticated
callers. Scraper must now present a valid workspace bearer token.
File: platform/internal/router/router.go

All 16 packages pass: go test ./...

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 11:14:15 +00:00
Molecule AI Research Lead
786c7566cd chore(eco-watch): add Anthropic Agent Skills + Microsoft APM — 2026-04-17
Two new ecosystem entries from daily trending survey:

- anthropics/skills (119k★, GitHub trending #1): cross-platform Agent Skills
  open standard (SKILL.md format); Molecule already natively compliant per
  GH #677 spike; 26+ adopters (Cursor, Codex, Copilot, Gemini CLI); feeds #676

- microsoft/apm (1.8k★, v0.8.11): Agent Package Manager for apm.yml manifests
  managing plugins/skills/MCP servers; overlaps with Molecule plugin system;
  content-security (apm audit) worth borrowing for #675; tracked in GH #694

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 11:12:46 +00:00
Molecule AI Research Lead
3a7da49088 chore(eco-watch): add Anthropic Agent Skills + Microsoft APM — 2026-04-17
Two new ecosystem entries from daily trending survey:

- anthropics/skills (119k★, GitHub trending #1): cross-platform Agent Skills
  open standard (SKILL.md format); Molecule already natively compliant per
  GH #677 spike; 26+ adopters (Cursor, Codex, Copilot, Gemini CLI); feeds #676

- microsoft/apm (1.8k★, v0.8.11): Agent Package Manager for apm.yml manifests
  managing plugins/skills/MCP servers; overlaps with Molecule plugin system;
  content-security (apm audit) worth borrowing for #675; tracked in GH #694

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 11:12:46 +00:00
molecule-ai[bot]
1bbda53142 Merge pull request #692 from Molecule-AI/fix/issue-680-681-workspace-auth
fix(security): auth+ownership on PATCH /workspaces/:id (#680 #681)
2026-04-17 11:03:25 +00:00
molecule-ai[bot]
92a28341fb
Merge pull request #692 from Molecule-AI/fix/issue-680-681-workspace-auth
fix(security): auth+ownership on PATCH /workspaces/:id (#680 #681)
2026-04-17 11:03:25 +00:00
molecule-ai[bot]
c825e44b50 Merge pull request #659 from Molecule-AI/infra/rebuild-runtime-images-script
infra: add rebuild-runtime-images.sh — patches all 6 adapter images with git credential helper (#658)
2026-04-17 10:59:33 +00:00
molecule-ai[bot]
1f6163b5d2
Merge pull request #659 from Molecule-AI/infra/rebuild-runtime-images-script
infra: add rebuild-runtime-images.sh — patches all 6 adapter images with git credential helper (#658)
2026-04-17 10:59:33 +00:00
molecule-ai[bot]
627946528d fix(security): add auth+ownership to PATCH /workspaces/:id (#680 #681)
ISSUE #680 — IDOR on PATCH /workspaces/🆔
- Route was on the open router with no auth middleware. Any unauthenticated
  caller could rename, change role, or update any workspace field of any
  workspace ID without credentials (zero auth + no ownership check).
- Fix: register under wsAuth (WorkspaceAuth middleware) which (a) requires a
  valid bearer token and (b) validates the token belongs to the target
  workspace, providing auth + ownership in a single check.
- Remove the now-redundant in-handler field-level auth block — the middleware
  is a strictly stronger gate. Dead code gone.
- Remove unused `middleware` import from workspace.go.
- Update tests: two tests that asserted the old in-handler 401 are replaced
  by TestWorkspaceUpdate_SensitiveField_AuthEnforcedByMiddleware (documents
  that auth is now at the router layer); cosmetic-field test renamed.

ISSUE #681 — test-token endpoint auth:
- Confirmed: GET /admin/workspaces/:id/test-token already has
  middleware.AdminAuth(db.DB). No change needed — finding was from older state.

Build: `go build ./...` clean. All 15 test packages pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:55:23 +00:00
molecule-ai[bot]
a3e278feb3
fix(security): add auth+ownership to PATCH /workspaces/:id (#680 #681)
ISSUE #680 — IDOR on PATCH /workspaces/🆔
- Route was on the open router with no auth middleware. Any unauthenticated
  caller could rename, change role, or update any workspace field of any
  workspace ID without credentials (zero auth + no ownership check).
- Fix: register under wsAuth (WorkspaceAuth middleware) which (a) requires a
  valid bearer token and (b) validates the token belongs to the target
  workspace, providing auth + ownership in a single check.
- Remove the now-redundant in-handler field-level auth block — the middleware
  is a strictly stronger gate. Dead code gone.
- Remove unused `middleware` import from workspace.go.
- Update tests: two tests that asserted the old in-handler 401 are replaced
  by TestWorkspaceUpdate_SensitiveField_AuthEnforcedByMiddleware (documents
  that auth is now at the router layer); cosmetic-field test renamed.

ISSUE #681 — test-token endpoint auth:
- Confirmed: GET /admin/workspaces/:id/test-token already has
  middleware.AdminAuth(db.DB). No change needed — finding was from older state.

Build: `go build ./...` clean. All 15 test packages pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:55:23 +00:00
Hongming Wang
b4590023cd Merge pull request #671 from Molecule-AI/feat/issue-618-admin-schedules-health
feat(platform): GET /admin/schedules/health — cross-workspace cron firing status (#618)
2026-04-17 03:47:44 -07:00
Hongming Wang
fdd03f8f5f
Merge pull request #671 from Molecule-AI/feat/issue-618-admin-schedules-health
feat(platform): GET /admin/schedules/health — cross-workspace cron firing status (#618)
2026-04-17 03:47:44 -07:00
molecule-ai[bot]
c07793eedf fix(security): cap discord error response body read at 4096 bytes
Unbounded io.ReadAll on the Discord webhook error response body was a LOW
OOM risk: a malicious gateway or misconfigured proxy could return a multi-MB
body and exhaust agent memory. Cap with io.LimitReader(resp.Body, 4096) —
error messages are always short; any extra content is irrelevant noise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:46:09 +00:00
molecule-ai[bot]
fde90efde5
fix(security): cap discord error response body read at 4096 bytes
Unbounded io.ReadAll on the Discord webhook error response body was a LOW
OOM risk: a malicious gateway or misconfigured proxy could return a multi-MB
body and exhaust agent memory. Cap with io.LimitReader(resp.Body, 4096) —
error messages are always short; any extra content is irrelevant noise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:46:09 +00:00
molecule-ai[bot]
4a27866c45 fix(router): restore artifacts routes, remove stray audit route from #618 scope
FIX 1: Cloudflare Artifacts routes (wsAuth POST/GET /artifacts, /fork, /token)
were accidentally dropped when #618 modified router.go. Restored along with the
handler and client packages that were already on main (#595/#641) but missing
from this branch.

FIX 2: Stray `audh := handlers.NewAuditHandler()` / `wsAuth.GET("/audit", ...)` block
was added out-of-scope during #618 work. Removed — #594 (audit-ledger) is a
separate merged PR and its routes live on main independently.

Build: `go build ./...` clean. All 17 test packages pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:44:34 +00:00
molecule-ai[bot]
a3e06f888d
fix(router): restore artifacts routes, remove stray audit route from #618 scope
FIX 1: Cloudflare Artifacts routes (wsAuth POST/GET /artifacts, /fork, /token)
were accidentally dropped when #618 modified router.go. Restored along with the
handler and client packages that were already on main (#595/#641) but missing
from this branch.

FIX 2: Stray `audh := handlers.NewAuditHandler()` / `wsAuth.GET("/audit", ...)` block
was added out-of-scope during #618 work. Removed — #594 (audit-ledger) is a
separate merged PR and its routes live on main independently.

Build: `go build ./...` clean. All 17 test packages pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:44:34 +00:00
molecule-ai[bot]
470704416e fix(security): Ed25519 signature verification for Discord webhooks + strip token from error chain
HIGH (#659-1): POST /webhooks/discord had no signature verification, allowing
any attacker to POST forged Discord slash-command payloads. Add Ed25519
verification via verifyDiscordSignature() before adapter.ParseWebhook() is
called. The function reads r.Body, verifies Ed25519(pubKey, timestamp+body,
X-Signature-Ed25519), then restores r.Body with io.NopCloser so ParseWebhook
can still read the payload. The public key is resolved from the first enabled
Discord channel's app_public_key config (plaintext — it is a public key and
not in sensitiveFields) with a fallback to DISCORD_APP_PUBLIC_KEY env var;
no key configured -> 401 (fail-closed). discordPublicKey() is the DB helper.

MEDIUM (#659-2): discord.go SendMessage() wrapped http.Client.Do errors with
%w, propagating the *url.Error which includes the full webhook URL
(https://discord.com/api/webhooks/{id}/{token}) into logs and error responses.
Replace with a static "discord: HTTP request failed" string.

Tests added (11 new):
- TestVerifyDiscordSignature_Valid / _WrongKey / _TamperedBody /
  _MissingTimestamp / _MissingSignature / _InvalidHexSignature /
  _InvalidHexPubKey / _WrongLengthPubKey (real Ed25519 key pairs)
- TestChannelHandler_Webhook_Discord_NoKey_Returns401
- TestChannelHandler_Webhook_Discord_InvalidSig_Returns401
- TestChannelHandler_Webhook_Discord_ValidSig_PingAccepted
- TestDiscordAdapter_SendMessage_ErrorDoesNotLeakToken

go test ./... green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:36:51 +00:00
molecule-ai[bot]
15d4b25c78
fix(security): Ed25519 signature verification for Discord webhooks + strip token from error chain
HIGH (#659-1): POST /webhooks/discord had no signature verification, allowing
any attacker to POST forged Discord slash-command payloads. Add Ed25519
verification via verifyDiscordSignature() before adapter.ParseWebhook() is
called. The function reads r.Body, verifies Ed25519(pubKey, timestamp+body,
X-Signature-Ed25519), then restores r.Body with io.NopCloser so ParseWebhook
can still read the payload. The public key is resolved from the first enabled
Discord channel's app_public_key config (plaintext — it is a public key and
not in sensitiveFields) with a fallback to DISCORD_APP_PUBLIC_KEY env var;
no key configured -> 401 (fail-closed). discordPublicKey() is the DB helper.

MEDIUM (#659-2): discord.go SendMessage() wrapped http.Client.Do errors with
%w, propagating the *url.Error which includes the full webhook URL
(https://discord.com/api/webhooks/{id}/{token}) into logs and error responses.
Replace with a static "discord: HTTP request failed" string.

Tests added (11 new):
- TestVerifyDiscordSignature_Valid / _WrongKey / _TamperedBody /
  _MissingTimestamp / _MissingSignature / _InvalidHexSignature /
  _InvalidHexPubKey / _WrongLengthPubKey (real Ed25519 key pairs)
- TestChannelHandler_Webhook_Discord_NoKey_Returns401
- TestChannelHandler_Webhook_Discord_InvalidSig_Returns401
- TestChannelHandler_Webhook_Discord_ValidSig_PingAccepted
- TestDiscordAdapter_SendMessage_ErrorDoesNotLeakToken

go test ./... green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:36:51 +00:00
molecule-ai[bot]
6e4979954b feat(platform): add GET /admin/schedules/health for cross-workspace schedule monitoring (#618)
Operators and audit agents can now detect silent cron failures across all
workspaces with a single AdminAuth-gated request — no per-workspace bearer
tokens required. This closes the proactive detection gap that left issue #85
(cron died silently 10+ hours) undetectable until users noticed missing work.

Changes:
- platform/internal/handlers/admin_schedules_health.go: new AdminSchedulesHealthHandler
  - GET /admin/schedules/health joins workspace_schedules + workspaces (excluding
    removed workspaces), computes status (ok|stale|never_run) and
    stale_threshold_seconds (2 × cron interval via scheduler.ComputeNextRun)
  - computeStaleThreshold() and classifyScheduleStatus() extracted as
    package-level helpers for direct unit testing
- platform/internal/handlers/admin_schedules_health_test.go: 16 tests
  - Unit tests for computeStaleThreshold (5min/hourly/daily crons, invalid expr,
    invalid timezone) and classifyScheduleStatus (never_run/stale/ok/zero-threshold)
  - Integration tests via sqlmock: empty result, never_run classification,
    stale detection, ok status, DB error → 500, multi-workspace response,
    required JSON fields coverage
- platform/internal/router/router.go: register GET /admin/schedules/health
  behind middleware.AdminAuth(db.DB), mirroring the /admin/liveness gate

Closes #618

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:28:55 +00:00
molecule-ai[bot]
ca8edaf6a4
feat(platform): add GET /admin/schedules/health for cross-workspace schedule monitoring (#618)
Operators and audit agents can now detect silent cron failures across all
workspaces with a single AdminAuth-gated request — no per-workspace bearer
tokens required. This closes the proactive detection gap that left issue #85
(cron died silently 10+ hours) undetectable until users noticed missing work.

Changes:
- platform/internal/handlers/admin_schedules_health.go: new AdminSchedulesHealthHandler
  - GET /admin/schedules/health joins workspace_schedules + workspaces (excluding
    removed workspaces), computes status (ok|stale|never_run) and
    stale_threshold_seconds (2 × cron interval via scheduler.ComputeNextRun)
  - computeStaleThreshold() and classifyScheduleStatus() extracted as
    package-level helpers for direct unit testing
- platform/internal/handlers/admin_schedules_health_test.go: 16 tests
  - Unit tests for computeStaleThreshold (5min/hourly/daily crons, invalid expr,
    invalid timezone) and classifyScheduleStatus (never_run/stale/ok/zero-threshold)
  - Integration tests via sqlmock: empty result, never_run classification,
    stale detection, ok status, DB error → 500, multi-workspace response,
    required JSON fields coverage
- platform/internal/router/router.go: register GET /admin/schedules/health
  behind middleware.AdminAuth(db.DB), mirroring the /admin/liveness gate

Closes #618

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:28:55 +00:00
06938e8335 fix(security): allowlist-validate runtime arg in rebuild-runtime-images.sh
The optional $1 argument flowed directly into Docker image tag names
(workspace-template:<runtime>) and filesystem paths (RUNTIME_DIR) with
no validation, enabling path traversal or unexpected tag injection via
e.g. `bash rebuild-runtime-images.sh '../evil'`.

Fix: introduce VALID_RUNTIMES allowlist and validate $1 against it
before setting RUNTIMES. Any unlisted value now exits with a clear
error message. The RUNTIMES array is populated from VALID_RUNTIMES
when no argument is given, keeping the all-runtimes default path.

shellcheck clean; $1 only appears inside the validated block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:27:11 +00:00
bbfe2e92d4 fix(security): allowlist-validate runtime arg in rebuild-runtime-images.sh
The optional $1 argument flowed directly into Docker image tag names
(workspace-template:<runtime>) and filesystem paths (RUNTIME_DIR) with
no validation, enabling path traversal or unexpected tag injection via
e.g. `bash rebuild-runtime-images.sh '../evil'`.

Fix: introduce VALID_RUNTIMES allowlist and validate $1 against it
before setting RUNTIMES. Any unlisted value now exits with a clear
error message. The RUNTIMES array is populated from VALID_RUNTIMES
when no argument is given, keeping the all-runtimes default path.

shellcheck clean; $1 only appears inside the validated block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:27:11 +00:00
cd6c82030d fix(infra): rename TMPDIR→RUNTIME_DIR, fix PIPESTATUS docker exit check
Bug 1: TMPDIR is a POSIX-reserved variable used by mktemp, Docker
BuildKit, and git subprocesses as their system temp directory.
Overwriting it redirected those tools to the build context, causing
unpredictable failures. Renamed all 6 occurrences to RUNTIME_DIR.

Bug 2: `docker build ... | grep` made grep's exit code (0=match,
1=no match) determine if the build succeeded, not docker's. Fixed by
reading PIPESTATUS[0] immediately after the pipeline so docker's real
exit code drives the SUCCESS/FAILED tracking.

Also fixed two pre-existing shellcheck warnings:
- SC2034: removed unused REPO_ROOT variable
- SC2064: trap now uses single quotes so TMPBASE expands at signal time

shellcheck clean with no warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:25:43 +00:00
7066fce6f4 fix(infra): rename TMPDIR→RUNTIME_DIR, fix PIPESTATUS docker exit check
Bug 1: TMPDIR is a POSIX-reserved variable used by mktemp, Docker
BuildKit, and git subprocesses as their system temp directory.
Overwriting it redirected those tools to the build context, causing
unpredictable failures. Renamed all 6 occurrences to RUNTIME_DIR.

Bug 2: `docker build ... | grep` made grep's exit code (0=match,
1=no match) determine if the build succeeded, not docker's. Fixed by
reading PIPESTATUS[0] immediately after the pipeline so docker's real
exit code drives the SUCCESS/FAILED tracking.

Also fixed two pre-existing shellcheck warnings:
- SC2034: removed unused REPO_ROOT variable
- SC2064: trap now uses single quotes so TMPBASE expands at signal time

shellcheck clean with no warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:25:43 +00:00
molecule-ai[bot]
b7f79a4be1 Merge pull request #669 from Molecule-AI/feat/issue-652-effort-taskbudget-v2
feat(issue-652): wire effort + task_budget to Anthropic output_config
2026-04-17 10:11:09 +00:00
molecule-ai[bot]
fb0d615de0
Merge pull request #669 from Molecule-AI/feat/issue-652-effort-taskbudget-v2
feat(issue-652): wire effort + task_budget to Anthropic output_config
2026-04-17 10:11:09 +00:00
molecule-ai[bot]
588848f701 fix(migrations): TEXT→UUID in 028_workspace_artifacts — unblocks all E2E CI
fix(migrations): TEXT→UUID in 028_workspace_artifacts — unblocks all E2E CI
2026-04-17 10:08:51 +00:00
molecule-ai[bot]
2c47e990c8
fix(migrations): TEXT→UUID in 028_workspace_artifacts — unblocks all E2E CI
fix(migrations): TEXT→UUID in 028_workspace_artifacts — unblocks all E2E CI
2026-04-17 10:08:51 +00:00
Molecule AI QA Engineer
dc2c5817bc test: add _load_config_dict coverage for issue #652
Cover the four paths that were exercised only via mock in the
_build_options tests: valid YAML, missing file, malformed YAML,
and empty file (safe_load → None → {} via `or {}`).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:08:45 +00:00
Molecule AI QA Engineer
5c95c6dc42 test: add _load_config_dict coverage for issue #652
Cover the four paths that were exercised only via mock in the
_build_options tests: valid YAML, missing file, malformed YAML,
and empty file (safe_load → None → {} via `or {}`).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:08:45 +00:00
rabbitblood
8eaffc49aa fix(migrations): TEXT→UUID in 028_workspace_artifacts — unblocks all E2E CI
Migration 028 declared workspace_id as TEXT with a FK to workspaces(id)
which is UUID. Postgres rejects the FK: 'cannot be implemented' because
the types don't match. Same class of bug as #646 (which fixed 025).

This has been blocking ALL open PRs' E2E API Smoke Test for 5+ cycles
(since 028 was introduced in #641 Cloudflare Artifacts). Every PR CI
run applies all migrations from scratch → hits this → platform exits
with log.Fatalf → /health never responds → 30s timeout → FAIL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:48:08 -07:00
rabbitblood
a94613a6fe fix(migrations): TEXT→UUID in 028_workspace_artifacts — unblocks all E2E CI
Migration 028 declared workspace_id as TEXT with a FK to workspaces(id)
which is UUID. Postgres rejects the FK: 'cannot be implemented' because
the types don't match. Same class of bug as #646 (which fixed 025).

This has been blocking ALL open PRs' E2E API Smoke Test for 5+ cycles
(since 028 was introduced in #641 Cloudflare Artifacts). Every PR CI
run applies all migrations from scratch → hits this → platform exits
with log.Fatalf → /health never responds → 30s timeout → FAIL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 02:48:08 -07:00
Molecule AI Backend Engineer
e11e077027 feat(issue-652): wire effort and task_budget to claude sdk output_config
Adds _load_config_dict() helper to ClaudeSDKExecutor and wires the new
effort and task_budget config fields into _build_options() before the
Anthropic API call:

- effort (str): low|medium|high|xhigh|max — populates output_config.effort
- task_budget (int): advisory total-token budget; must be >= 20000 when set;
  automatically adds task-budgets-2026-03-13 beta header

Also adds WorkspaceConfig.effort and WorkspaceConfig.task_budget fields in
config.py and 5 acceptance tests covering all code paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:33:07 +00:00
Molecule AI Backend Engineer
cf5428664b feat(issue-652): wire effort and task_budget to claude sdk output_config
Adds _load_config_dict() helper to ClaudeSDKExecutor and wires the new
effort and task_budget config fields into _build_options() before the
Anthropic API call:

- effort (str): low|medium|high|xhigh|max — populates output_config.effort
- task_budget (int): advisory total-token budget; must be >= 20000 when set;
  automatically adds task-budgets-2026-03-13 beta header

Also adds WorkspaceConfig.effort and WorkspaceConfig.task_budget fields in
config.py and 5 acceptance tests covering all code paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:33:07 +00:00
Molecule AI Backend Engineer
0e9270feb7 chore: renumber audit-events migration 028 → 029
PR #641 (workspace_artifacts) already claimed 028 on main.
Rename both .up.sql and .down.sql to 029_audit_events.* to avoid
the collision when this branch merges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:31:14 +00:00
Molecule AI Backend Engineer
a67e9ca492 chore: renumber audit-events migration 028 → 029
PR #641 (workspace_artifacts) already claimed 028 on main.
Rename both .up.sql and .down.sql to 029_audit_events.* to avoid
the collision when this branch merges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:31:14 +00:00
molecule-ai[bot]
a2a26c6cce Merge pull request #656 from Molecule-AI/feat/issue-625-discord-adapter-clean
feat(channels): add Discord adapter (#625)
2026-04-17 07:30:39 +00:00
molecule-ai[bot]
1ffa33cf61
Merge pull request #656 from Molecule-AI/feat/issue-625-discord-adapter-clean
feat(channels): add Discord adapter (#625)
2026-04-17 07:30:39 +00:00
molecule-ai[bot]
84c3177b06 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]
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]
d814dd8b7f 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
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
Molecule AI Backend Engineer
3895e02e01 fix(security): address Security Auditor findings on audit-ledger (#651)
- Replace == HMAC comparisons with hmac.compare_digest (Python) and
  hmac.Equal (Go) in ledger.py, verify.py, and audit.go to prevent
  timing oracle attacks (Fixes 1-6)
- Increase PBKDF2 iterations from 100K to 210K in both ledger.py and
  audit.go — must match for cross-language verification (Fix 7)
- Return chain_valid: null when offset > 0 (paginated views cannot
  verify a truncated chain; null means "not computed") (Fix 8)
- Remove module-level AUDIT_LEDGER_SALT attribute from ledger.py; read
  the secret exclusively from os.environ inside _get_hmac_key() so the
  salt is not exposed in the module namespace (Fix 9)
- Update tests: use monkeypatch.setenv/delenv instead of setattr on the
  removed AUDIT_LEDGER_SALT attribute; update testAuditKey helper to
  use 210K iterations; add TestAuditQuery_PaginatedOffsetReturnsNullChainValid
- Fix migration 028: workspace_id column type TEXT → UUID to match
  workspaces.id UUID primary key

All tests pass: 1043 pytest + 0 Go test failures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:30:10 +00:00
Molecule AI Backend Engineer
7584267a80 fix(security): address Security Auditor findings on audit-ledger (#651)
- Replace == HMAC comparisons with hmac.compare_digest (Python) and
  hmac.Equal (Go) in ledger.py, verify.py, and audit.go to prevent
  timing oracle attacks (Fixes 1-6)
- Increase PBKDF2 iterations from 100K to 210K in both ledger.py and
  audit.go — must match for cross-language verification (Fix 7)
- Return chain_valid: null when offset > 0 (paginated views cannot
  verify a truncated chain; null means "not computed") (Fix 8)
- Remove module-level AUDIT_LEDGER_SALT attribute from ledger.py; read
  the secret exclusively from os.environ inside _get_hmac_key() so the
  salt is not exposed in the module namespace (Fix 9)
- Update tests: use monkeypatch.setenv/delenv instead of setattr on the
  removed AUDIT_LEDGER_SALT attribute; update testAuditKey helper to
  use 210K iterations; add TestAuditQuery_PaginatedOffsetReturnsNullChainValid
- Fix migration 028: workspace_id column type TEXT → UUID to match
  workspaces.id UUID primary key

All tests pass: 1043 pytest + 0 Go test failures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:30:10 +00:00
dcbc5f3e54 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
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]
7b2f5a7a8e 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[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
54737d58a2 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