Add ensureAgentInConfig, setDefaultAgentInConfig, and resolveActiveAgentId to manage per-workspace agent entries in openclaw.json so the gateway routes to the correct workspace.
BREAKING CHANGE: Convert repository to IronClaw-only package with strict
external dependency on globally installed `openclaw` runtime.
### Changes
- Remove entire OpenClaw core source from repository (src/agents/*, src/acp/*,
src/commands/*, and related modules)
- Implement CLI delegation: non-bootstrap commands now delegate to global
`openclaw` binary via external contract
- Remove local OpenClaw path resolution from web app; always spawn global
`openclaw` binary instead of local scripts
- Rename package.json scripts: `pnpm openclaw` → `pnpm ironclaw`,
`openclaw:rpc` → `ironclaw:rpc`
- Update bootstrap flow to verify and install global OpenClaw when missing
- Migrate web workspace/profile logic to align with OpenClaw state paths
- Add migration contract tests for stream-json, session subscribe, and profile
resolution behaviors
- Update build/release pipeline for IronClaw-only artifacts
- Update documentation for new peer + global installation model
### Architecture
IronClaw is now strictly a frontend/UI/bootstrap layer:
- `npx ironclaw` bootstraps OpenClaw (if missing), runs guided onboarding
- IronClaw UI serves on localhost:3100
- OpenClaw Gateway runs on standard port 18789
- Communication via stable CLI contracts and Gateway WebSocket protocol only
### Migration
Users must have `openclaw` installed globally:
npm install -g openclaw
Existing IronClaw profiles and sessions remain compatible through gateway
protocol stability.
Refs: bootstrap_dev_testing, ironclaw_frontend_split, strict-external-openclaw
YAML-parsed date_between/between filters store range values as a
two-element array under `value`, but the filter system expected
separate value/valueTo fields. Add normalizeFilterGroup at the YAML
read boundary and defensive array handling in the evaluator and SQL
builder so both formats work correctly.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Skip updateIndex for session IDs containing :subagent: so they don't
appear as top-level "New Chat" entries in the sidebar
- Filter subagent IDs in the sidebar component as a safety net
- Clean up existing leaked entries from index.json
Co-authored-by: Cursor <cursoragent@cursor.com>
Subagents now use the same ActiveRun infrastructure as parent sessions:
- startSubscribeRun() creates a subscribe-only ActiveRun when sessions_spawn
tool results are detected, using the same event buffering, persistence,
and SSE reconnection as parent runs
- Stream/stop/chat routes no longer branch on subagent vs parent; both
use getActiveRun/subscribeToRun with the session key as map key
- hasRunningSubagentsForParent moved into active-runs.ts to check the
unified activeRuns map (+ disk registry fallback)
- Deferred finalization on lifecycle/end with 5s safety timeout
- ev.data.text fallback for assistant events without delta field
- 24h cleanup grace for subscribe-only runs (vs 30s for parent)
Reverts the broken childSessionKey registration from 32cfcf14f.
Co-authored-by: Cursor <cursoragent@cursor.com>
The spawn tool result uses `childSessionKey` (not `sessionKey`), and
task/label come from the tool input args, not the result details.
Without this fix registerSubagent was never called because the
extracted sessionKey was always undefined.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Handle ev.data.text fallback when delta is absent in assistant events
(both active-runs and subagent-runs)
- Defer subagent finalizeRun until subscribe process closes so buffered
events in readline are still delivered to SSE subscribers
- Register subagents from sessions_spawn tool results in active-runs so
hasRunningSubagentsForParent works without opening SubagentPanel first
- Add disk registry fallback in hasRunningSubagentsForParent for cases
where in-memory parentIndex has no entries
- Fix pre-commit hook: tolerate oxfmt exit 2 when all files are ignored
Co-authored-by: Cursor <cursoragent@cursor.com>
Bug 1: Subagent events from gateway broadcasts were processed as parent
events because the sessionKey filter was accidentally removed during the
subagent decoupling refactor. Re-add the filter in wireChildProcess.
Bug 2: Creating workspaces at custom paths failed because:
- mkdir API rejected absolute paths outside workspace root
- Directory picker started at workspace root, not home
- Error responses from mkdir were silently swallowed
Add absolute path support to mkdir, handle errors in picker UI,
start picker at home dir, and normalize init route paths.
Restore backend/logic features that were incorrectly auto-merged from the
design branch:
- Spreadsheet viewer (xlsx, csv, ods, etc.) and xlsx dependency
- HTML iframe viewer with source toggle
- Directory picker modal for workspace creation
- Workspace registry for custom-path workspaces
- Session auto-discovery for orphaned sessions
- Workspace init seeding (CRM objects, DuckDB, templates, bootstrap files)
- Symlink resolution and showHidden in tree/browse routes
- Upload to workspace assets/ instead of hidden ~/.ironclaw/uploads/
- Webpack dev watcher config (next.config.ts)
- router.push for back-button navigation history
Subagent event JSONL files and rehydration metadata were stored in the
shared ~/.openclaw/web-chat/ directory regardless of the active workspace
profile, while parent chat sessions were correctly profile-scoped.
Move subagent event persistence into the profile-scoped web-chat dir
(web-chat-<profile>/subagent-events/) and add a profile-local
subagent-index.json for fast rehydration after page refresh. The shared
gateway registry (~/.openclaw/subagents/runs.json) remains the fallback
source. Existing events in the legacy shared path are still readable as
a migration fallback.
Bump version and publish ironclaw@2026.2.10-1.14. Includes chain-of-thought,
chat-message, active-runs, and agent-runner improvements.
Co-authored-by: Cursor <cursoragent@cursor.com>