* 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]>
* 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]>
* feat(web): show error details and input params for failed tool calls
Failed tool calls in the gateway UI previously showed only a red X icon
with an empty expandable body. This change:
- Adds optional `error` and `parameters` fields to `ToolCompleted` SSE
events so the browser receives failure details in real-time
- Auto-expands failed tool cards to make errors immediately visible
- Adds `StatusUpdate::tool_completed()` constructor that centralizes
the 5 duplicated construction sites and applies `redact_params()` to
prevent sensitive values (e.g. secret_save's "value" param) from
leaking through SSE broadcasts
- Adds `sensitive_params()` trait method to `Tool` for declaring which
parameters must be redacted before logging, hooks, and UI display
- Adds `redact_params()` utility and wires it through hooks, approvals,
ActionRecord storage, and debug logs in dispatcher/worker
- Adds `SecretListTool` and `SecretDeleteTool` for LLM-driven secret
management (values never returned, only names/metadata)
- Fixes auth flow: setup-only extensions show configure modal instead
of OAuth card; auth_completed SSE dismisses both UI paths
- CI: release workflow creates PR instead of pushing directly to main
- Registry: MissingChecksum error enables source fallback for
bootstrapping when checksums haven't been populated yet
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: apply cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: keep original params in PendingApproval for execution, redact only for display
Address two PR review comments:
1. execute_chat_tool_standalone now redacts sensitive params before logging,
matching the pattern already used in worker.rs.
2. PendingApproval previously stored redacted parameters, which meant
approved tool calls received "[REDACTED]" instead of the actual values.
Add a display_parameters field for UI/logs and keep parameters as the
original values used for execution.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: address PR review comments
- worker.rs: redact sensitive params before BeforeToolCall hook, matching
dispatcher.rs — hooks in the autonomous job path now receive redacted
params instead of raw values
- registry.rs: fix docstring for register_secrets_tools (list, delete,
not save/list/delete — no SecretSaveTool is registered)
- app.js: fix double toast/loadExtensions in submitConfigureModal —
for non-OAuth success the auth_completed SSE already handles both,
so skip them in the HTTP response handler to avoid duplicates
[skip-regression-check]
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
* fix: make onboarding installs prefer release artifacts with source fallback
* fix: harden extension fallback errors and surface setup warnings
* fix: validate registry artifacts and harden fallback errors
* fix: address review feedback on installer fallback
- Add upfront validate_manifest_install_inputs() in
install_with_source_fallback so bad manifests fail fast without
relying on inner methods to catch them
- Document ALLOWED_ARTIFACT_HOSTS as GitHub-only by design
- Document intentional url omission from DownloadFailed Display
- Add channel manifest validation tests (wrong prefix rejected,
correct prefix accepted)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: require SHA256 checksum for artifact downloads
Reject artifact installs when the manifest has sha256: null instead of
warning and proceeding. This prevents installing unverified pre-built
binaries during onboarding. The check runs before downloading to avoid
wasting bandwidth.
Since InvalidManifest blocks source fallback, manifests with URLs but
no checksums will hard-fail rather than silently falling back to source
build — forcing the manifest to be fixed.
The release CI already computes SHA256 for each bundle; the manifests
just need to be populated with the actual values.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: enforce SHA256 checksums and auto-patch manifests in CI
- Fix cargo fmt on SHA256 check code
- Reorder release CI: build WASM extensions before binary so manifests
can be patched with computed SHA256 before build.rs embeds them
- Add "Patch manifests with WASM checksums" step in build-local-artifacts
that reads checksums.txt and updates registry JSON files before building
- Add update-registry-checksums job that commits patched manifests back
to main after release, keeping the repo in sync with released artifacts
This closes the integrity gap where all manifests had sha256: null and
artifact downloads were unverified. The binary now embeds correct SHA256
values and the installer hard-rejects null checksums.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Bowen Wang <[email protected]>
* feat: embedded registry catalog and WASM bundle install pipeline
Embed registry manifests at compile time so the extension catalog is
available without network access. Add tar.gz bundle support for WASM
extension downloads (tools and channels), a /api/extensions/registry
endpoint, CI job to build and publish WASM bundles on release, and
ephemeral in-memory secrets fallback so the extension manager works
even without a persistent secrets store.
Key changes:
- build.rs: collect registry/*.json into embedded_catalog.json at compile time
- src/registry/embedded.rs + catalog.rs: load embedded or on-disk catalog
- src/extensions/manager.rs: download_and_install_wasm handles tar.gz bundles,
bare .wasm files, and separate capabilities downloads; wasm channel install
- src/channels/web/server.rs: /api/extensions/registry endpoint + no-cache headers
- src/app.rs: ephemeral InMemorySecretsStore fallback for extension manager
- registry/*.json: populate artifact download URLs for release bundles
- .github/workflows/release.yml: build-wasm-extensions CI job
- Simplified setup wizard and CLI registry commands
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: address PR review — archive hardening, decompression bomb guard, test fix
- Add 100 MB decompressed entry size cap to tar.gz extraction in both
manager.rs and installer.rs to prevent decompression bombs
- Add archive.set_preserve_permissions(false) and set_unpack_xattrs(false)
for defense-in-depth against malicious archives
- Fix test assertion logic in catalog.rs (|| → || with correct negation)
- Replace silent tar fallback in CI with explicit if/else for capabilities
- Add warning when installing without SHA256 verification
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: resolve clippy warning in settings.rs and enforce zero-warnings policy
Use struct initializer with ..Default::default() instead of field
reassignment. Update CLAUDE.md to codify zero clippy warnings policy —
all warnings must be fixed before committing, including pre-existing ones.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: address PR review round 2 — build reliability, caps validation, naming
- build.rs: emit per-file rerun-if-changed for reliable content tracking;
fix bundles fallback to match BundlesFile shape ({"bundles":{}})
- embedded.rs: parse catalog once via OnceLock instead of double-parsing
- manager.rs + installer.rs: add 1 MB size cap on capabilities_url downloads
with proper error surfacing
- secrets/store.rs: rename misleading `pub mod testing` to `pub mod in_memory`
- server.rs: track installed extensions by (name, kind) tuple to avoid
false positives across different extension kinds
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>