Commit Graph
487 Commits
Author SHA1 Message Date
ZakiandClaude Opus 4.6 aa289997e3 style: fix import ordering for routines module
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-12 12:50:53 -07:00
ZakiandClaude Opus 4.6 4aad0cfbaa refactor(cli): rename cron subcommand to routines
The system manages all routine types (cron, webhook, event, manual),
not just cron schedules. Rename the CLI subcommand to reflect this:
- `ironclaw cron` -> `ironclaw routines` (with `cron` as hidden alias)
- List shows all routines by default, add --trigger filter
- Remove cron-trigger-only validation
- Simplify require_routine helper (no trigger type check)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-12 12:06:30 -07:00
Zaki 112a4087e7 fix(cli): reject invalid cron timezones 2026-03-12 11:00:16 -07:00
reidliu41andZaki 403f6f504f feat(cli): add cron subcommand for managing scheduled routines
Rebase onto staging branch and address collaborator review:
  - Fix .unwrap_or(None) → proper error propagation in set_enabled()
  - Add --yes/-y flag for non-interactive deletion with confirmation prompt
  - Add --json flag for machine-readable output in list and history
  - Preserve error context chain with {e:#} in run_cron_cli()

  Note: GATEWAY_USER_ID is trusted from the environment; future work may
  add authentication for multi-tenant deployments.
2026-03-12 11:00:16 -07:00
5a62ceaa99 refactor: extract safety module into ironclaw_safety crate (#1024)
* refactor: extract safety module into ironclaw_safety crate

Move prompt injection defense, input validation, secret leak detection,
and safety policy enforcement into a standalone crate under crates/.
The safety module was a leaf dependency with no async, no database, and
no other ironclaw traits — only pure computation with pattern matching.

SafetyConfig (2 fields) moves into the crate; env-var resolution stays
in ironclaw's config module as a free function. src/safety/mod.rs becomes
a thin re-export so all existing `crate::safety::*` imports keep working.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* docs: update CLAUDE.md for ironclaw_safety crate extraction

Add guidance to migrate imports from crate::safety to ironclaw_safety
when touching files. Update project structure to reflect crates/ dir.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* refactor: move safety fuzz targets into ironclaw_safety crate

Split fuzz infrastructure:
- crates/ironclaw_safety/fuzz/ — 5 safety-only targets (sanitizer,
  validator, leak_detector, credential_detect, config_env) depending
  only on ironclaw_safety for faster builds
- fuzz/ — keeps fuzz_tool_params which needs ironclaw::tools

Add seed corpus files (51 total) covering each pattern family:
sanitizer injection patterns, validator edge cases, leak detector
secret formats, credential detect HTTP param shapes.

Add new fuzz_credential_detect target exercising
params_contain_manual_credentials with arbitrary JSON.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address PR review — single-pass XML escaping and versioned path dep

Rewrite escape_xml_attr from chained .replace() to single-pass char
iteration (O(n) instead of O(4n) with intermediate allocations). Add
version = "0.1.0" to ironclaw_safety path dep to satisfy cargo-deny
wildcards = "deny".

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-12 17:54:24 +00:00
ReidandGitHub e2eb340c04 Add Z.AI provider support for GLM-5 (#938) 2026-03-12 03:43:32 -07:00
ReidandGitHub 5d9d17bf71 feat(cli): add ironclaw channels list subcommand (#933) 2026-03-12 03:43:17 -07:00
Zaki ManianandGitHub 269b3f462f test(html_to_markdown): refresh golden files after renderer bump (#1016) 2026-03-11 20:28:38 -07:00
ReidandGitHub 3fbe290901 feat(cli): add ironclaw skills list/search/info subcommands (#918) 2026-03-11 20:20:20 -07:00
f05896fe6a Migrate GitHub webhook normalization into github tool (#758)
* Add event-triggered routines and workflow skill templates

* Add generic host-verified webhook ingress for tools

* Migrate GitHub webhook normalization into github tool

* Bump github tool registry version

* Stabilize trace E2E test rig and approval behavior

* Add reusable gateway workflow harness with mock LLM server (#762)

* Add reusable gateway workflow test harness with mock LLM server

* Fix clippy issues in workflow harness

* Stabilize trace E2E test rig and approval behavior

* Address PR review feedback on gateway workflow harness

- Extract shared TestChannelHandle into test_channel.rs with name override
  support, eliminating ~55 lines of duplication between test_rig.rs and
  gateway_workflow_harness.rs
- Remove redundant RoutineEngine creation that was immediately overwritten
  by Agent::run()
- Replace flaky sleep(500ms) with polling loop for routine run count check
- Use components.context_manager instead of creating a fresh ContextManager
  for job tools, ensuring agent and tools share the same instance

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Fix import ordering in gateway_workflow_harness

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>

* Address PR #758 review feedback

- Fix header_value to use fully case-insensitive lookup (iterate with
  to_ascii_lowercase) instead of checking only exact/lower/upper variants
- Change comment_id from u32 to u64 to handle GitHub's billion-range IDs
- Remove handle_webhook from LLM-facing JSON schema to prevent direct
  invocation bypassing HMAC verification
- Rename enrichment keys from repository/sender to repository_name/
  sender_login to preserve original JSON objects in webhook payloads
- Remove put_string_normalized helper (no longer needed)
- Replace no-op tests (test_validate_event_in_create_pr_review,
  test_validate_merge_method) with test_header_value_case_insensitive
- Add README docs for 6 undocumented actions (list_issue_comments,
  create_issue_comment, list_pull_request_comments,
  reply_pull_request_comment, get_pull_request_reviews,
  get_combined_status)
- Add comment explaining max_tool_calls <= 8 bound in e2e test
- Fix gateway workflow harness: add webhook_capability with secret auth
  to MockGithubWebhookTool, matching staging's hardened webhook security
- Fix merge artifacts: remove duplicate test function, orphaned code
  fragment in e2e_routine_heartbeat

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Fix formatting in gateway workflow harness

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Address Copilot review: filter keys, pr_number fallback, feature gate, version alignment

- Update SKILL.md and workflow-routines.md templates to use `repository_name`
  and `sender_login` (matching enriched payload field names)
- Mark webhook HMAC secret as required in SKILL.md prerequisites
- Fall back to `/issue/number` for `pr_number` on issue_comment PR webhooks
- Gate `gateway_workflow_harness` module behind `#[cfg(feature = "libsql")]`
- Align tool version to 0.2.1 in Cargo.toml and capabilities.json

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-12 01:52:47 +00:00
febed1e12e feat: add cargo-deny for supply chain safety (#834)
* feat: add cargo-deny for supply chain safety

Add dependency auditing via cargo-deny to catch license violations,
security advisories, and untrusted sources. Integrates into CI as a
parallel job alongside clippy, and into the local quality gate script.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: use cargo-deny action in CI, improve quality gate script

- Use EmbarkStudios/cargo-deny-action@v2 instead of cargo install
  for faster CI execution
- Fix quality_gate_strict.sh to check for cargo-deny availability
  instead of suppressing stderr

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: add missing Unlicense and CDLA-Permissive-2.0 to license allowlist

Add Unlicense (used by aho-corasick, memchr, etc.) and
CDLA-Permissive-2.0 (used by webpki-roots) to prevent
cargo deny check from failing on the current dependency tree.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* chore: trigger CI after retargeting PR to staging

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: use valid cargo-deny v0.19 syntax for unmaintained advisories

The `unmaintained` field in [advisories] accepts "all", "workspace",
"transitive", or "none" — not "warn". Use "workspace" to flag
unmaintained direct dependencies without failing on transitive ones.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* chore: re-trigger CI after adding skip-regression-check label

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: migrate deny.toml [licenses] to version 2 format

Remove deprecated `unlicensed` and `default` fields, add `version = 2`.
In v2, all licenses are denied unless explicitly in the allow list,
making these fields redundant.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: ignore pre-existing advisories in deny.toml with justification

Add known RUSTSEC IDs to the ignore list so cargo-deny CI passes.
Each advisory is documented with mitigation context. Dependency
upgrades to resolve these should be tracked separately.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address PR review feedback for cargo-deny integration

- quality_gate_strict.sh: fail hard when cargo-deny is not installed
  instead of silently skipping, and let set -e handle check failures
- deny.toml: remove empty [graph].targets so cargo-deny checks all
  platforms instead of only the runner's default target

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(deny.toml): correct serde_yml advisory comment to reflect direct dependency

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: tighten clippy-windows check in roll-up job

Change from checking only `== "failure"` to checking
`!= "success" && != "skipped"`. This ensures any unexpected
result (e.g., cancelled) also blocks the merge, while still
allowing the expected "skipped" state for non-main PRs.

Addresses zmanian's review feedback on PR #834.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: cd to repo root in strict gate, deny wildcard versions

- quality_gate_strict.sh: add `cd` to repo root so the script works
  when invoked from any working directory.
- deny.toml: change `wildcards = "allow"` to `"deny"` to catch `*`
  version requirements in dependencies.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 18:50:15 -07:00
Zaki ManianandGitHub c37b64124c fix(setup): preserve model selection on provider re-run (#679) (#987) 2026-03-12 01:14:25 +00:00
Zaki ManianandGitHub f31cd13135 fix(mcp): attach session manager for non-OAuth HTTP clients (#793) (#986)
* fix(mcp): attach session manager for non-OAuth HTTP clients (#793)

* style(mcp): format factory regression test (#986)
2026-03-12 01:12:01 +00:00
Zaki ManianandGitHub 195ff44b1a fix(security): migrate webhook auth to HMAC-SHA256 signature header (#970) 2026-03-12 01:10:26 +00:00
a9821ac20f fix(security): make unsafe env::set_var calls safe with explicit invariants (#968)
* fix(security): make unsafe env::set_var calls safe with explicit invariants

`std::env::set_var` is unsafe in Rust 1.82+ because concurrent calls
from multiple threads cause undefined behavior. This commit addresses
the two production-code call sites:

1. `bootstrap.rs:load_ironclaw_env()` -- called before the Tokio
   runtime starts (genuinely single-threaded). Added a `debug_assert!`
   that verifies no Tokio runtime is active, making the safety
   invariant machine-checkable rather than relying on a comment.

2. `llm/session.rs:api_key_login()` -- was calling `set_var` mid-
   execution inside the multi-threaded Tokio runtime (UB risk).
   Replaced with `set_runtime_env()`, a new thread-safe overlay
   backed by `OnceLock<Mutex<HashMap>>`. The overlay integrates with
   the existing `optional_env()` config resolution and a new
   `env_or_override()` reader function.

All call sites that read `NEARAI_API_KEY` via raw `std::env::var()`
(wizard.rs, main.rs, doctor.rs) are updated to use the thread-safe
`env_or_override()` helper instead, so the value set during
interactive login is visible without mutating the process environment.

Test code `set_var`/`remove_var` calls (bootstrap tests, config tests,
shell tests, oauth tests, wizard tests) are left as-is since they run
under `ENV_MUTEX` serialization and are not production paths.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: address review feedback on thread-safe env overlay PR

- Replace debug_assert! with runtime check in bootstrap.rs so release
  builds skip unsafe set_var when a Tokio runtime is active
- Recover from mutex poison in set_runtime_env instead of silently
  dropping writes (poisoned HashMap is still usable)
- Skip empty override values in env_or_override and optional_env for
  consistency with real env var handling
- Fix doc comment on env_or_override (real env checked first, not
  runtime overrides)
- Update api_key_login doc to describe runtime overlay instead of
  env var mutation

[skip-regression-check]

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: use LazyLock::lock() for INJECTED_VARS; use set_runtime_env() in bootstrap fallback

- helpers.rs: fix env_or_override() to call INJECTED_VARS.lock() instead
  of .get() — INJECTED_VARS was changed upstream from OnceLock<HashMap>
  to LazyLock<Mutex<HashMap>>; calling .get() caused a compile error
  (E0599: no method named 'get' for LazyLock)

- bootstrap.rs: when load_ironclaw_env() is called with an active Tokio
  runtime, use set_runtime_env("DATABASE_BACKEND", "libsql") instead of
  silently dropping the write. This ensures DATABASE_BACKEND is always
  set regardless of thread context (addresses ilblackdragon review item 1).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Gabe Hamilton <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-12 00:59:58 +00:00
8bbb43da52 fix(security): require explicit SANDBOX_ALLOW_FULL_ACCESS to enable FullAccess policy (#967)
* fix(security): require explicit SANDBOX_ALLOW_FULL_ACCESS to enable FullAccess policy

FullAccess policy bypasses Docker entirely and runs commands via sh -c
directly on the host. Previously, setting SANDBOX_POLICY=full_access
alone was sufficient to enable this, which could be triggered
accidentally or via prompt injection if tool approval is bypassed.

This adds a double opt-in guard:

- New SANDBOX_ALLOW_FULL_ACCESS=true env var must ALSO be set for
  FullAccess to take effect. Without it, the policy is downgraded to
  WorkspaceWrite with a tracing::error! log.

- At execution time, every FullAccess command emits a tracing::warn!
  with the command and working directory for audit visibility.

- The FullAccess variant now documents its blast radius (host shell,
  unrestricted filesystem/network/environment).

- SandboxConfig and SandboxModeConfig gain an allow_full_access field,
  wired through from_env() and the builder.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(sandbox): address review feedback on FullAccess double opt-in

- Add doc comment on builder .policy() warning that FullAccess requires
  .allow_full_access(true) or execution will return SandboxError::Config
- Sanitize audit log: log only binary name instead of full command to
  prevent secret leakage; add [FullAccess] prefix for grep-ability
- Add test_builder_full_access_without_allow_returns_error test covering
  the builder path without explicit allow_full_access(true)
- Fix doc comment mismatch: config.rs and SandboxPolicy::FullAccess docs
  said "will downgrade to WorkspaceWrite" but runtime returns
  SandboxError::Config -- aligned docs with actual behavior

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: merge duplicate mod tests; add allow_full_access to struct initializers

After upstream merge, src/config/sandbox.rs had two issues:
- Duplicate mod tests block (upstream's original tests at line 271 + our
  new FullAccess guard tests at line 478) caused E0428 compile error
- Upstream test struct literals for SandboxModeConfig were missing the
  new allow_full_access field (E0063)

Fixes: merge the two mod tests into one; add allow_full_access: false to
the sandbox_mode_config_custom_values and sandbox_mode_to_sandbox_config
test struct initializers.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Gabe Hamilton <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-12 00:57:05 +00:00
f48fe95ac4 fix(security): add Content-Security-Policy header to web gateway (#966)
* fix(security): add Content-Security-Policy header to web gateway

The web gateway set X-Frame-Options and X-Content-Type-Options but had
no Content-Security-Policy header. Without CSP, there is no browser-
enforced mitigation against XSS attacks. This adds a tailored CSP that
matches the resources the frontend actually loads.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(security): address CSP review feedback

- Remove cdnjs.cloudflare.com from script-src (not used in codebase)
- Add explicit object-src 'none' per security best practice
- Add regression test asserting CSP header presence and directives

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Gabe Hamilton <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-12 00:55:38 +00:00
Jonas WiklundGitHubJonas Wiklund <Jonas Wiklund>
acea1143cf Fix systemctl unit (#472)
Co-authored-by: Jonas Wiklund <Jonas Wiklund>
2026-03-11 17:04:11 -07:00
CPU-216andGitHub c372c99729 fix(test): stabilize openai compat oversized-body regression (#839)
* fix(test): stabilize openai compat oversized-body regression

* docs(web): fix stale body limit in CLAUDE.md (1 MB → 10 MB)

CLAUDE.md:200 still documented the pre-#725 body limit of 1 MB, but
server.rs:354 was changed to 10 MB in #725 (image upload support).
Update the documentation to match the actual production value.
2026-03-11 17:03:12 -07:00
81f7b64994 fix(ci): disambiguate WASM bundle filenames to prevent tool/channel collision (#964)
* fix(ci): disambiguate WASM bundle filenames to prevent tool/channel collision

When a tool and channel share the same name (e.g. slack, telegram), the
CI build produced identical bundle filenames, causing the second to
overwrite the first. Both manifests then pointed to the wrong binary.

Prefix bundle filenames with the extension kind (tool-slack-... vs
channel-slack-...) and parse the prefix when patching manifests, so each
manifest receives the correct artifact URL and SHA256.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* test(registry): add installer tests for tool/channel name disambiguation

Regression tests for the CI artifact collision fix (PR #964). Verifies:
- extract_tar_gz rejects archives with wrong wasm name (the collision bug)
- Tool bundle extracts slack-tool.wasm correctly
- Channel bundle extracts slack.wasm correctly
- Tool and channel manifests install to separate directories

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(ci): add kind validation and filter non-WASM checksum entries

- Validate .kind is "tool" or "channel" before using in build-wasm-extensions (hard error)
- Filter checksums.txt to *-wasm32-wasip2.tar.gz entries before parsing, avoiding noisy warnings from binary artifact entries in build-local-artifacts
- Add kind validation with warning+skip in both checksum-parsing loops

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* style: fix rustfmt formatting in installer tests

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 17:02:11 -07:00
ReidandGitHub 977b7fde99 feat(setup): display ASCII art banner during onboarding (#851)
[skip-regression-check]
2026-03-11 16:54:42 -07:00
ArtemandGitHub f3e8e7c599 docs: add Russian localization (README.ru.md) (#850) 2026-03-11 16:54:21 -07:00
Protocol ZeroandGitHub d47282f444 fix(setup): validate channel credentials during setup (#684)
* fix(setup): validate channel credentials during setup

Validate channel setup credentials against the declared validation endpoint so users get immediate feedback before startup failures. Substitute stored secrets into the validation URL, block private or local targets, and warn on failed checks without interrupting setup.

Made-with: Cursor

* fix(setup): harden channel credential validation

Pin setup-time validation requests to vetted DNS results, disable redirects, and avoid leaking substituted secrets in error output. URL-encode placeholder substitutions and add regressions for DNS failure, trailing-dot localhost, and IPv4-mapped IPv6 SSRF bypasses.

Made-with: Cursor

* refactor(setup): cache validation placeholder regex

Reuse a static placeholder regex in channel credential validation so the SSRF hardening path avoids recompiling the same pattern on every call.
2026-03-11 16:53:52 -07:00
adios2d6andGitHub 5879d06447 fix: drain tunnel pipes to prevent zombie process (#735)
* fix(tunnel): drain ngrok stdout/stderr to prevent zombie process

* fix: limit stderr lines read on startup failure to prevent OOM

* fix: drain pipes in cloudflare and custom tunnel to prevent zombie process

* style: fix formatting in custom tunnel

* test: add regression test for stdout drain preventing zombie process

* style: apply rustfmt
2026-03-11 16:53:38 -07:00
ReidandGitHub a1b3911b27 fix(mcp): header safety validation and Authorization conflict bug from #704 (#752)
* fix(mcp): header safety validation and Authorization conflict bug from #704

* fix(mcp): enforce RFC 9110 header validation on all config load paths

  Replace hand-written CRLF checks with reqwest::header::HeaderName::from_bytes()
  and HeaderValue::from_str(), catching spaces, colons, null bytes, and all
  non-token characters that the previous validation missed.

  Add validation to load_mcp_servers_from() and load_mcp_servers_from_db() so
  corrupted configs from disk or DB are rejected at load time instead of silently
  flowing through to McpClient. Improve app.rs error handling to distinguish
  "no config" from "corrupted config" (including malformed JSON).

  Also fix build_request_headers() to check self.custom_headers directly instead
  of indirectly via server_config, and clarify the wire test comment about
  HeaderMap::insert replacement semantics.

* fix ci issue
2026-03-11 16:53:02 -07:00
pikaxingeandGitHub 2094d6e30d fix(agent): block thread_id-based context pollution across users (#760)
* fix(agent): prevent forged thread UUID context/write contamination

* fix(agent): close thread_id race and reject forged UUID hydration

* fix(ci): satisfy clippy and fmt checks after rebase
2026-03-11 16:52:31 -07:00
ReidandGitHub c8cac0925d fix(mcp): stdio/unix transports skip initialize handshake (#890) (#935)
fixes #890

  - Always call initialize() before list_tools()/call_tool(), removing
    the session_manager.is_some() guard that caused stdio/unix clients
    to skip the MCP protocol handshake entirely
  - Add local AtomicBool flag for idempotent initialization when no
    session manager is present
  - Fire-and-forget JSON-RPC notifications (id=None) in stdio/unix
    transports instead of registering a pending response that would
    block for 30s waiting on a reply that never comes
  - Fix mcp test panic on stdio/unix servers by using
    create_client_from_config() instead of new_with_config() which
    asserts HTTP-only transport
2026-03-11 16:46:40 -07:00
ReidandGitHub 6321bb4688 fix(setup): drain residual events and filter key kind in onboard prompts (#937) (#949)
On Windows, single keypresses during `ironclaw onboard` are registered
  twice, causing channel/tool selection to skip or toggle incorrectly.
  Two root causes:

  1. select_many() had no residual event drain, so Enter from a prior
     prompt was immediately consumed on entry, skipping the selection.

  2. Neither select_many() nor read_secret_line() filtered on
     KeyEventKind::Press, so Windows Key Release/Repeat events caused
     every keypress to fire twice (Space toggles cancel out, Enter
     triggers double-advance, arrows jump two positions).

  Extract a shared drain_pending_events() helper (replacing the inline
  drain in read_secret_line from #849), add it to select_many() entry,
  and filter both event loops to only handle KeyEventKind::Press.

  Fixes #937
[skip-regression-check]
2026-03-11 16:46:03 -07:00
94b448ffab fix(security): load WASM tool description and schema from capabilities.json (#520)
The extract_tool_description and extract_tool_schema stubs in runtime.rs
returned permissive fallbacks ("WASM sandboxed tool" and
additionalProperties:true) for every WASM tool, defeating parameter
validation and preventing the LLM from using tools correctly.

Add optional `description` and `parameters` fields to CapabilitiesFile so
tool authors can declare proper metadata in their sidecar JSON. The
WasmToolLoader now extracts these fields and passes them through to the
tool registry as overrides. Tools without a capabilities.json or without
these fields get a tracing::warn and fall back to the old stubs.

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-11 16:29:59 -07:00
bb06565770 fix(security): resolve DNS once and reuse for SSRF validation to prevent rebinding (#518)
* fix(security): resolve DNS once and reuse for SSRF validation to prevent rebinding

The previous SSRF protection resolved DNS in validate_url() to check IPs
against a blocklist, but then reqwest independently re-resolved DNS when
making the actual HTTP connection.  Between validation and connection, a
DNS rebinding attack could flip the record from a public IP (passes
validation) to a private IP like 169.254.169.254 (AWS metadata endpoint).

Fix: split URL validation into two phases:
- validate_url(): synchronous URL structure checks (scheme, localhost,
  IP literals) -- no DNS resolution
- validate_and_resolve_url(): async DNS resolution via
  tokio::net::lookup_host, validates all resolved IPs, returns
  SocketAddrs
- build_pinned_client(): constructs a per-request reqwest Client with
  resolve() pinning so reqwest connects to the pre-validated IPs without
  a second DNS lookup

Applied to both HttpTool and WebFetchTool.  WebFetchTool builds a fresh
pinned client per redirect hop, ensuring DNS rebinding cannot occur at
any point in a redirect chain.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* style: run cargo fmt

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-11 16:18:43 -07:00
19d9562b4f feat(extensions): unify auth and configure into single entrypoint (#677)
* feat(extensions): unify auth and configure into single entrypoint

Refactors the extension lifecycle to eliminate the divergence between
chat and gateway paths that caused Telegram setup via chat to fail
(missing webhook secret auto-generation, no token validation).

Key changes:
- Rename save_setup_secrets() → configure(): single entrypoint for
  providing secrets to any extension (WasmChannel, WasmTool, MCP).
  Validates, stores, auto-generates, and activates.
- Add configure_token(): convenience wrapper for single-token callers
  (chat auth card, WebSocket, agent auth mode).
- Refactor auth() to pure status check: remove token parameter,
  delete token-storing branches from auth_mcp/auth_wasm_tool,
  rename auth_wasm_channel → auth_wasm_channel_status.
- Add ConfigureResult/MissingSecret types for structured responses.
- Replace hardcoded Telegram token validation with generic
  validation_endpoint from capabilities.json.
- Update all callers (9 files) to use the new interface.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: use ValidationFailed error variant instead of string matching

Replace brittle msg.contains("Invalid token") checks with a proper
ExtensionError::ValidationFailed variant. configure() now returns
this variant for token validation failures, and callers match on it
directly instead of parsing error message strings.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: address review — SSRF protection, error typing, missing-secret selection, WS auth

1. SSRF: call validate_fetch_url() before validation_endpoint HTTP request
2. Transport errors map to ExtensionError::Other (not ValidationFailed)
3. configure_token() picks first *missing* secret, not first non-optional
4. WebSocket error path re-emits AuthRequired on ValidationFailed

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* test: add regression tests for extension lifecycle refactoring

- test_configure_token_picks_first_missing_secret: verifies multi-secret
  channels can be configured one secret at a time (commit ce106f4)
- test_auth_is_read_only_for_wasm_channel: verifies auth() has no side
  effects and doesn't store secrets (commit 47f8eb6)
- test_validation_failed_is_distinct_error_variant: verifies the typed
  error variant can be pattern-matched (commit a318161)

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address review comments — activation dispatch, dead code, caps consolidation

- Fix configure() fallthrough bug: dispatch activation by ExtensionKind
  instead of unconditionally calling activate_wasm_channel() for all
  non-WasmTool types (MCP servers and channel relays now use their
  correct activation methods)
- Remove dead MissingSecret struct and missing_secrets field (never
  populated, flagged by reviewer)
- Consolidate capabilities file parsing in configure(): parse once
  and reuse for allowed names, validation_endpoint, and auto-generation
- Fix auth() doc comment: note MCP OAuth side effects
- Fix stale save_setup_secrets reference in server.rs comment
- Add regression test for activation dispatch bug

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-11 16:01:41 -07:00
28a22f2a59 fix(security): replace regex HTML sanitizer with DOMPurify to prevent XSS (#510)
* fix(security): replace regex HTML sanitizer with DOMPurify to prevent XSS

The previous sanitizeRenderedHtml() used regex patterns to strip dangerous
HTML tags and event handler attributes before assigning to innerHTML. Regex-
based HTML sanitization is notoriously bypassable via:

- SVG/MathML elements not in the blocklist (<svg onload=...>)
- Newline-split event handlers (<img src=x on\nload=alert(1)>)
- Mutation XSS (browser parsing quirks that reconstruct dangerous DOM)
- Encoded attribute values and alternative quote styles
- Nested/recursive tag patterns that defeat linear regex

This is exploitable through prompt injection: if an LLM tool output contains
crafted HTML, it flows through marked.parse() -> sanitizeRenderedHtml() ->
innerHTML, allowing script execution in the user's browser session.

Replace the regex sanitizer with DOMPurify 3.2.3, the industry-standard
DOM-based HTML sanitizer. DOMPurify parses HTML into a real DOM tree and
walks it node-by-node, which eliminates all known bypass vectors. It is
used by Mozilla, Google, and most major web applications.

CDN: cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.3/purify.min.js
SRI: sha384-osZDKVu4ipZP703HmPOhWdyBajcFyjX2Psjk//TG1Rc0AdwEtuToaylrmcK3LdAl

Audited all 60+ innerHTML assignments in app.js:
- 5 use renderMarkdown() -> now protected by DOMPurify
- Remainder use escapeHtml(), static literals, or empty strings

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(security): guard sanitizeRenderedHtml against DOMPurify CDN unavailability [skip-regression-check]

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-11 15:55:35 -07:00
d313f44a19 fix(ci): improve Claude Code review reliability (#955)
The Claude review step was failing ~40% of the time because:
- --allowedTools didn't include Read, Glob, Grep, Agent, causing 8-9
  permission denials per run and preventing Claude from reading files
  or spawning the subagents the prompt required
- Step 4 spawned N additional scoring agents per issue found, exhausting
  the 50-turn budget before the PR comment could be posted
- Subagents could independently post PR comments, causing fragmented output

Fix: add missing tools to --allowedTools, merge per-issue scoring into
the review agents themselves, and add guardrails ensuring exactly one
consolidated comment is always posted.

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 14:05:33 -07:00
f08220db82 fix(ci): run gated test jobs during staging CI (#956)
The telegram-tests, windows-build, wasm-wit-compat, and docker-build
jobs were skipped during staging CI because their `if` conditions only
matched `push` and `pull_request` events. When staging-ci.yml calls
test.yml via workflow_call, github.event_name is `schedule` (inherited
from the caller), which matched neither condition.

Invert the conditions to blocklist the one case we want to skip (PRs
targeting staging) instead of allowlisting specific events. This handles
schedule, workflow_dispatch, and any future trigger types.

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 14:04:32 -07:00
34550add3e fix(ci): prevent staging-ci tag failure and chained PR auto-close (#900)
- Use fetch-depth: 0 in update-tag to ensure current_head SHA is available
  even when staging receives new commits during the CI run
- Only merge promotion PRs targeting main; leave chained PRs open to
  prevent delete_branch_on_merge from auto-closing downstream PRs

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 12:04:54 -07:00
fe82469904 fix(ci): WASM WIT compat sqlite3 duplicate symbol conflict (#953)
* fix(ci): use explicit features in WASM WIT compat test to avoid sqlite3 symbol conflicts

The `import` feature (added in #903) brings in `rusqlite[bundled]` which
conflicts with `libsql-ffi` — both bundle SQLite C code, causing duplicate
symbol linker errors. Use explicit features matching the test matrix instead
of `--all-features`.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: replace rusqlite with libsql in import module to fix sqlite3 symbol conflict

The `import` feature used `rusqlite[bundled]` which bundled its own SQLite
C code, conflicting with `libsql-ffi` (also bundles SQLite). This caused
duplicate `sqlite3_*` symbol linker errors when both features were enabled
via `--all-features`.

Replace `rusqlite` with `libsql` (already a dependency) in the import
reader. The `import` feature now implies `libsql`. This eliminates the
duplicate symbol conflict and allows `--all-features` to compile cleanly.

Also restores `--all-features` in the WASM WIT compat CI test (now safe)
and converts all import test helpers from rusqlite to libsql.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* style: apply cargo fmt formatting fixes

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 11:48:24 -07:00
6b841bb817 feat(i18n): Add internationalization support with Chinese and English translations (#929)
* feat(i18n): Add internationalization support with Chinese and English translations
* fix(i18n): fix duplicate keys, broken placeholders, and dead overrides

---------

Co-authored-by: zwb1982 <[email protected]>
2026-03-11 22:34:13 +08:00
8f513428f1 fix: resolve deferred review items from PRs #883, #848, #788 (#915)
Address three deferred implementation items flagged during code review:

1. SIGHUP lock held across .await (#883): Split restart_with_addr into
   merged_router_clone() + install_listener() so the async TcpListener
   bind happens outside the mutex, eliminating lock contention risk.

2. Recursion depth limit for check_strings (#848): Cap JSON traversal
   at 32 levels to prevent stack overflow on pathological tool params.

3. Named error type for add_tokens (#788): Replace Result<(), String>
   with TokenBudgetExceeded { used, limit } for type-safe budget errors.

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 07:12:45 +00:00
369741fc60 Add generic host-verified /webhook/tools/{tool} ingress (#757)
* Add generic host-verified webhook ingress for tools

* Stabilize trace E2E test rig and approval behavior

* Fix webhook security issues from review feedback

- Reject tools without webhook_capability() (was unauthenticated RCE)
- Remove secret-in-query-string fallback (leak via logs/referrers)
- Require approval for event_emit tool (escalation via routine triggers)
- Simplify header_value() (HeaderMap already case-insensitive)
- Redact internal errors from webhook HTTP responses
- Remove unused hmac_timestamp_tolerance_secs field
- Add regression test for tool without webhook capability

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Harden webhook ingress: require auth mechanism, body limit layer, health check

- Reject webhook capabilities that declare no auth mechanism (empty
  WebhookCapability would previously allow unauthenticated access)
- Add DefaultBodyLimit layer to reject oversized payloads before buffering
- Health check (GET) now verifies tool has webhook_capability(), not just
  existence
- Add regression tests for all three fixes

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Fix auto_approve_tools inconsistency between dispatcher and thread_ops

dispatcher.rs skips all approval checks (including Always) when
auto_approve_tools is true, but thread_ops.rs still required approval
for Always tools. This caused deferred tool calls to unexpectedly halt
in test rigs and auto-approve configurations.

Match dispatcher behavior: short-circuit all approval when
auto_approve_tools is enabled.

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 03:36:25 +00:00
55b5a462a2 fix(web): improve UX readability and accessibility in chat UI (#910)
* fix(web): improve UX readability and accessibility in chat UI

Soften user bubbles, increase assistant message readability, widen message
gaps, improve disabled button visibility, add keyboard focus-visible rings,
fix attach button specificity, expand tree-row click targets, and increase
log entry hover contrast.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(web): address PR review — hover guard, accent-soft var, tree-row a11y

- Guard .chat-input button:hover with :not(:disabled) to prevent
  visual feedback on disabled send button
- Add --accent-soft CSS variable, use in .message.user instead of
  hardcoded rgba
- Make tree-rows keyboard-focusable (tabIndex=0, role=treeitem,
  aria-expanded, Enter/Space keydown handlers)

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-11 02:31:35 +00:00
26068db24b feat: Import OpenClaw memory, history and settings (#903)
* feat: Import OpenClaw memory, history and settings

* review fixes

* fix: address remaining code quality issues

1. Remove dead import_conversation() function - replaced by import_conversation_atomic()
2. Improve non-UTF-8 filename handling in list_agent_dbs() - log warning instead of silent 'unknown'
3. Remove emojis from CLI output per project style guide

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

---------

Co-authored-by: Claude Haiku 4.5 <[email protected]>
2026-03-10 18:37:10 -07:00
b0214fef41 feat: add channel-relay integration for Slack (#790)
* fix(ci): secrets can't be used in step if conditions [skip-regression-check] (#787)

GitHub Actions step-level `if:` doesn't have access to `secrets` context.
Replace `if: secrets.X != ''` with `continue-on-error: true` and let
the Set token step handle the fallback.

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* feat: add channel-relay integration for Slack via external relay service

- Add RelayChannel and RelayClient for connecting to channel-relay SSE streams
- Add RelayConfig with env-based configuration (CHANNEL_RELAY_URL, CHANNEL_RELAY_API_KEY)
- Add channel-relay extension lifecycle: install, OAuth auth, activate with hot-add
- Add proxy message sending through channel-relay for Slack chat.postMessage
- Add extension registry entry for Slack relay with OAuth auth hint
- Add relay integration test with mock SSE server
- Wire relay channel into app startup with reconnect on stored credentials
- Add AuthRequired extension error variant for cleaner auth flow detection

[skip-regression-check]

* chore: apply cargo fmt

* fix: remove remaining Telegram test references in relay channel

* fix: address PR #790 review feedback — parser handle leak, CSRF, circuit breaker

- Fix parser handle leak on reconnect by sharing Arc<RwLock> instead of
  creating a local copy in start() (shutdown now aborts the correct task)
- Add CSRF state nonce to OAuth flow: generate in auth_channel_relay,
  validate in slack_relay_oauth_callback_handler, one-time use
- Remove dead proxy_slack method, update integration test to use
  proxy_provider
- Add reconnect circuit breaker (max_consecutive_failures, default 50)
- Fix stale docs (Telegram refs), extract event_types constants

---------

Co-authored-by: Henry Park <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-10 16:34:54 -07:00
Henry ParkandGitHub 3a841b30d8 Merge pull request #898 from nearai/merge/main-into-staging
merge: resolve main -> staging conflicts
2026-03-10 15:34:08 -07:00
Henry ParkandClaude Sonnet 4.6 54a70639e6 merge: resolve main -> staging conflicts (sha256: null)
Keep staging versions for all registry JSON files (sha256: null) and
LLM module helpers. CHANGELOG.md and Cargo updates from main applied.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-10 14:09:25 -07:00
873322f2fb fix: staging CI review issues (batch 1) (#883)
* fix: address staging-ci-review issues (batch 1)

- #811: Fix unreachable error handling in worker — restructure .await?
  to explicit match on nested Result so token budget errors are properly
  logged and marked as failed
- #813: Combine metadata + token budget into single update_context()
  call to prevent concurrent worker observing partial state
- #814: Persist max_tokens and total_tokens_used to both PostgreSQL and
  libSQL backends — add V12 migration, update save_job/get_job
- #815: Cap user-supplied max_tokens at configured max_tokens_per_job
  to prevent budget bypass via metadata injection
- #869: Release locks before async I/O in webhook handler (http.rs) and
  SIGHUP handler (main.rs) to prevent blocking concurrent requests

Fixes: #811, #813, #814, #815, #869

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address PR #883 review feedback

- Fix min(user_val, 0) bug: guard for unlimited config (max_tokens_per_job == 0)
- Remove duplicate columns from libSQL base SCHEMA (v12 migration is sole source)
- Use get_i64() helper for consistency in libsql/jobs.rs
- Add regression tests for scheduler token budget capping

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-10 14:01:07 -07:00
1f5b582c5f fix: agent logging (#888)
* fix: optimize agent logging to reduce DataDog bill

* fix: log permanent repair failures as ERROR not WARN

RepairResult::Failed is permanent failure requiring attention (ERROR level)
not a temporary/retryable condition (WARN level).

[skip-regression-check]

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* security: remove user message content from trace logs

Never log user message content at any log level (includes TRACE).
Log only safe metadata: content length, message ID, image count.

This prevents accidental exposure of sensitive user data in logs
even at the most verbose logging level.

[skip-regression-check]

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* security: move LLM response body logging to TRACE level

Response bodies can contain user-generated content, tool outputs, and
leaked secrets. Moving to TRACE (not enabled in production) prevents
exposure in DEBUG logs. Status log remains at DEBUG.

[skip-regression-check]

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* refactor: simplify URL sanitization using url::Url API

Use set_query, set_fragment, set_username, set_password methods
instead of manual string reconstruction. Cleaner, handles edge cases,
eliminates port branching complexity.

[skip-regression-check]

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* test: add comprehensive unit tests for sanitize_url_for_logging

Add 9 test cases covering:
- URL with query parameters
- URL with credentials (user:pass@host)
- URL with fragment
- URL with port
- URL with all components combined
- Malformed URL fallback behavior
- Short strings (pass-through)
- Non-URL-like strings
- Path preservation

Tests verify that sanitization correctly removes sensitive components
while preserving safe components like host, port, and path.

Co-Authored-By: Claude Haiku 4.5 <[email protected]>

* fix: libsql per-migration logs should be DEBUG, not TRACE

Individual migration logs are now visible with standard debug logging
(RUST_LOG=ironclaw=debug), improving debuggability when troubleshooting
migration issues. Summary log remains at INFO level.

Fixes behavioral change that made it harder to identify which specific
migration ran or failed without enabling full TRACE logging.

[skip-regression-check]

---------

Co-authored-by: Claude Haiku 4.5 <[email protected]>
2026-03-10 13:55:06 -07:00
5635384e51 fix(registry): version-pinned WASM artifact URLs + ChecksumMismatch source fallback (#832)
* fix(registry): version-pinned WASM artifact URLs + ChecksumMismatch fallback (#439)

Root cause: all artifact URLs used releases/latest/download/, which is a
moving target. Every release rebuilds all WASM extensions non-deterministically,
so sha256 baked into an older binary diverges from the content at 'latest'.
ChecksumMismatch was also a hard block with no source-build fallback.

Three-layer fix:

1. src/registry/installer.rs — allow source-build fallback for ChecksumMismatch
   on releases/latest URLs (moving-target artifact rotation, not tampering).
   Version-pinned URLs (releases/download/vX.Y.Z/) remain a hard block.
   Adds regression test (test_source_fallback_on_latest_url_mismatch) and
   updates test_should_attempt_source_fallback_policy to cover both URL types.

2. .github/workflows/release.yml — three CI changes:
   - build-wasm-extensions: version-detect, skip-if-unchanged, versioned filenames
     (name-{version}-wasm32-wasip2.tar.gz). Skip rebuild when manifest already has
     a non-null sha256 and the URL embeds the current version — stable checksums
     until source actually changes.
   - build-local-artifacts: patch manifests with version-pinned URL + sha256 (for
     binary embedding via build.rs).
   - update-registry-checksums: same URL patching for the main-branch PR.
   All three sed patterns use '.*' (greedy) to correctly handle pre-release
   version strings like 0.1.0-alpha.1.

3. registry/{tools,channels}/*.json — null out all 14 stale sha256 values.
   Null sha256 -> MissingChecksum -> source-build fallback (works on all binaries).
   Next release CI will populate version-pinned URLs + stable checksums.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* style: cargo fmt

* fix(ci): use JSON filename stem for WASM bundle names to fix manifest lookup

Manifests like registry/tools/slack.json have name='slack-tool', causing
the patching step to look for registry/tools/slack-tool.json (missing).

Introduce file_stem (JSON filename without .json) for the bundle filename
and checksums.txt entry, while keeping ext_name (manifest .name) for archive
contents — the installer extracts files by manifest.name so those must still
match. The patching step strips -{version}-wasm32-wasip2.tar.gz from the
filename stem and looks up registry/tools/slack.json correctly.

* fix(registry): tighten fallback URL check + deduplicate tests

Address PR review feedback:

1. Make should_attempt_source_fallback check repo-specific
   (github.com/nearai/ironclaw/releases/latest/) instead of a
   generic substring (/releases/latest/download/).

2. Remove duplicate ChecksumMismatch cases from
   test_should_attempt_source_fallback_policy — that coverage
   lives in the dedicated regression test
   test_source_fallback_on_latest_url_mismatch.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-03-10 13:51:17 -07:00
76375f2eaa refactor: centralize test credential constants into testing::credentials (#829)
* refactor: centralize test credential constants into testing::credentials

Scattered test credential strings (API keys, OAuth tokens, crypto keys,
Telegram tokens, session tokens) across ~25 files made security auditing
harder and created unnecessary duplication. Centralize all test-only fake
credentials into a new `src/testing/credentials.rs` module with named
constants and a shared `test_secrets_store()` helper.

- Convert `src/testing.rs` to directory module (`src/testing/mod.rs`)
- Add `src/testing/credentials.rs` with ~30 named constants
- Replace hardcoded literals in 24 source files
- Deduplicate `test_store()` helper (was copy-pasted in 3 files)
- Leave leak_detector/shell/signature tests as-is (inline values
  aid readability for pattern detection tests)

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* refactor: replace real Telegram bot token with obviously fake test stub

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Update src/testing/credentials.rs

Co-authored-by: Copilot <[email protected]>

* Update src/testing/credentials.rs

Co-authored-by: Copilot <[email protected]>

* refactor: address PR review feedback on test credentials

- Fix TEST_CRYPTO_KEY doc comment ("32-byte hex" → "32-character key string")
- Rename confusing "real"/"fake" Anthropic constant names and values
- Change TEST_STRIPE_KEY from "sk-live" to "sk_test_fake123" to avoid scanners
- Use test_secrets_store() helper in orchestrator and http tool tests
- Clarify config_round_trip.rs doc comment about integration test visibility

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Copilot <[email protected]>
2026-03-10 13:25:32 -07:00
Henry ParkandGitHub 1e7950eb1a Merge pull request #820 from nearai/staging-promote/a868b142-22886164216
chore: promote staging to main (2026-03-10 03:47 UTC)
2026-03-10 13:22:22 -07:00
24d4fbb8a7 Revert "Feat/docker shell edition" + fix fmt/clippy (#886)
* Revert "Feat/docker shell edition (#804)"

This reverts commit c566faf28f.

* style: fix formatting issues from revert

Run cargo fmt to fix formatting across 7 files after the revert of
the docker shell edition feature.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-10 11:57:50 -07:00