Closes the loop on the blur/close summarize work. Previously the sidebar
always rendered `session.preview` for drafts, so the model call fired by
blur/close updated `session.title` to a value the user never saw, and
then `startExistingSession` summarized again on Start Planning even when
blur had already produced a title for the exact same text.
The sidebar now shows the summarized title once available and falls
back to the inputPayload preview only while the title is still the
placeholder. summarizeDraftTitle persists the plan text it summarized
from via a new `markDraftSummarized` store method, and updateDraft
preserves that marker only while the persisted text still matches it
(text edits invalidate it automatically). startExistingSession reads
the marker and skips its own summarize when the persisted plan equals
what was summarized last, eliminating the redundant model call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The planning modal's draft sync only updated SQLite, never the in-memory
session, so Start Planning ran the agent against whatever fragment was
typed before the first 500ms debounce and silently dropped everything
after. Drafts that survived a backend restart couldn't be started at
all (lazy rebuild was missing for the start path), reopened drafts left
the textarea empty, and the model override the user picked at create
time was forgotten on reopen — Start Planning would silently use the
project default instead.
This change rehydrates initialPlan from SQLite in startExistingSession,
flushes the latest text from the request body before reading it back,
lazy-rebuilds the in-memory session from the persisted row when needed,
and round-trips the model override through inputPayload so reopen
restores it into modal state. Sidebar drafts now render a derived
preview (from inputPayload, never persisted as title) so multiple
drafts are distinguishable while editing, and summarizeDraftTitle fires
on textarea blur and modal close — gated on status rather than title
content so blur-then-edit still refreshes rather than locking to the
first snapshot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge lands FN-3015's insights feature: it adds persistence-backed coverage for the insights routes, expands the insight store lifecycle tests, closes route validation regressions, and fixes workspace verification gates. The implementation in `insights-routes.ts` was streamlined, and a new rese
Fusion-Task-Id: FN-3015
The merge brings in a fix (FN-2998) that scopes the bottom safe-area padding in ResearchView to mobile devices only, correcting an over-application of the padding on desktop or larger screens.
Fusion-Task-Id: FN-2998
Merged two changes: a new feature (FN-3229) that syncs planning drafts to the server in real-time while the user types, adding a PlanningModeModal component, a new ai-session-store for managing session state, and a dashboard API route to persist drafts; plus a performance fix replacing a blocking `e
Fusion-Task-Id: FN-3229
The dashboard backend (Express routes, services) lives under src/ and is pure
Node logic, but every test was paying for jsdom env init plus full CSS-import
processing. Split via environmentMatchGlobs so app/** keeps jsdom (React UI)
and src/** runs in node by default. Two src tests opt back into jsdom:
- status-bar.test.ts uses window.matchMedia.
- proxy-routes.test.ts asserts on DOMException constructor.name, which jsdom
reports as "AbortError" but node reports as "DOMException".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split packages/dashboard/app/__tests__/api.test.ts (~6.4k lines) into per-area files (auth, git, missions, projects, settings, tasks), and src/__tests__/routes.test.ts (~20k lines) into per-area files (agents, auth, automation, git, github, planning, settings, system, tasks, tasks-ops). The monolith files were dominating wall-clock for the dashboard suite under file-parallel execution.
Also document in packages/core/vitest.config.ts why the core suite cannot move to "threads": vitest-setup gates per-worker cwd on isMainThread (false in worker_threads, so isolation breaks), and setup-test-isolation writes process.env.HOME unconditionally (threads share env, so concurrent workers race).
Drop an unused execFileSync import from scripts/test-with-lock.mjs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/test-with-lock.mjs: pnpm test:locked acquires an exclusive
~/.fusion/test.lock (O_EXLOCK) before running pnpm test, so several
Claude Code worktrees on the same machine serialize their vitest
fan-out instead of saturating the box. Prints the holding PID and
worktree path while waiting.
- dev-server-manager test children now park on stdin instead of
setInterval so manager.shutdown() can deterministically exit them
via stdin close, preventing orphaned node processes outliving the
test run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a draft lifecycle for planning sessions: typing into the
PlanningModeModal textarea now creates a server-side draft after a
300ms debounce, persisted with status='draft' so the user's in-flight
plan survives modal close/reopen and shows up immediately in the
session list.
- planning.ts: new createDraftSession path; persistSession status
union widened to include 'draft'; Session gains an explicit title
field so subsequent updates don't clobber it.
- register-planning-subtask-routes.ts: wires the createPlanningDraft
POST endpoint that the modal calls on debounce.
- ai-session-store.ts: tracks the draft status across queries so the
session list and locks behave the same as any active session.
- legacy.ts: client wrapper for createPlanningDraft.
- PlanningModeModal styling, tests, and ModalReentry coverage updated
for the new flow.
- docs/architecture.md notes the expanded ai_sessions.status lifecycle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two sync hot paths were stalling the dashboard event loop on a periodic
timer:
1. `pgrep -f vitest` ran via `execSync` in `getVitestProcessIds`
(`/api/system-stats`, `/api/kill-vitest`) and `killVitestProcesses`
(TUI memory-pressure check). On a busy machine pgrep walking the
process table can take 100ms+; execSync blocks the entire Node event
loop for that duration, so every concurrent dashboard request hangs
while pgrep runs. The TUI variant fired on every memory-pressure tick
(~2s when over threshold), the dashboard variant fired on every
system-stats poll (5s while the modal is open). Both now use execFile
with a callback wrapped in a Promise.
2. `discoverDashboardPiExtensions` (called from 3 /api/settings/pi-
extensions routes) did 6+ blocking existsSync/readFileSync calls per
invocation across legacy and fusion settings paths. Converted to
fs.promises.readFile/access and parallelized via Promise.all.
Behavior preserved:
- TUI memory-pressure detection still works (sync os.totalmem path
unchanged); auto-kill still fires on threshold breach.
- The `lastAutoKillAt` 30s re-fire gate is set before the async kill
starts, so concurrent ticks can't trigger duplicate kills.
- system-stats still polls every 5s while the modal is open and still
returns vitestProcessCount.
- All 7 system-stats / kill-vitest tests pass; all 6 settings/pi-
extensions tests pass. Test mocks updated for the (err, stdout, stderr)
callback signature.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge delivers the full draft planning feature (FN-3106) — API routes for planning subtasks, the AiSessionStore backend for draft sessions, a draft planning API client, auto-creation of planning drafts from user input, and corresponding UI polish in PlanningModeModal with accessibility-focused
Fusion-Task-Id: FN-3106
This merge ships several feature and infrastructure improvements across the codebase. Task title validation is strengthened in triage with stricter rejection of malformed titles and preference for prompt-declared titles (FN-3056), while task creation now preserves priority settings (FN-3210). The Mi
Fusion-Task-Id: FN-3056
- Add scheduler logic to create dependency-linked follow-up tasks when actionable PR feedback remains after a PR is merged or closed
- Update engine runtime/project wiring to support manual PR create flows and branch publish behavior for fusion/<task-id>
- Add dashboard route coverage for manual PR creation/linking behavior and corresponding engine/runtime tests
- Document manual PR branch conventions and follow-up behavior in task management and dashboard docs
Fusion-Task-Id: FN-3202
This release (v0.15.0) introduces plugin dashboard views and a new dependency graph plugin, extends the plugin slot system with richer metadata surfaces and UX improvements, adds SQLite WAL tuning with integrity checks, and stabilizes agent run log streaming with enriched session labels. It also fix
Fusion-Task-Id: FN-3066
- Add plugin dashboard view registration and hosting across core, dashboard routes, and plugin SDK exports
- Integrate plugin-provided views into app navigation, mobile/header UI, and view state hooks with coverage
- Add fusion-plugin-dependency-graph example plugin with persisted storage, dashboard view UI, and manifest wiring
- Update plugin authoring and architecture docs for dashboard view extension points
- Add a changeset for @runfusion/fusion covering plugin dashboard view support
Fusion-Task-Id: FN-3079
The dashboard task-edit route destructured every editable body field
except priority, so changing priority via the task-detail modal was
silently dropped before reaching store.updateTask. Wire priority
through with isTaskPriority validation (null resets to default).
Without this fix the priority-aware triage/scheduler/merge ordering
shipped previously had no effect for tasks edited in the dashboard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge lands four features: a `/clear` command for Chat and Quick Chat with session banner fixes (FN-3062), a todo view redesign with restructured rows and improved styling (FN-3063), deterministic peer exchange shutdown with dashboard improvements (FN-3040), and a summary Q&A disclosure feature
Fusion-Task-Id: FN-3078
- Stop overwriting AI session titles with the "Planning session" fallback
during cross-tab broadcasts before initialPlan has hydrated on resume.
- Persist SessionNotificationBanner dismissals to localStorage keyed by
updatedAt so they survive refresh and auto-re-show on the next event.
- Add Settings → Appearance toggle to hide the banner entirely.
- Drop the inner scrollbar on the planning question list; let the outer
pane handle all scrolling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The merge delivers three major features: a droid CLI path reconciliation extension that resolves workspace path mismatches for spawned agents, session-first quick chat with improved heartbeat prompts and a dramatically simplified QuickChatFAB component, and canonical agent asset directory naming wit
Fusion-Task-Id: FN-3033
Merges FN-2982 Step 3: adds model filtering for the droid-cli settings API, fixes planning mode question display and new-session reset in the dashboard, and adds test coverage for droid-cli settings and model route registration.
Fusion-Task-Id: FN-2982
Archive/unarchive routes were returning the row-update boolean rather than
the actual archived flag, so a no-op unarchive replied with `archived: true`.
Re-read the row and report its current state instead. Also adds tests for
archive (terminal-only), unarchive event emission, and listAll filtering.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In a multi-iteration tool loop, buildResumePrompt anchored on the last user
message and walked forward through preceding tool results. The only user
message stays at index 0, so each iteration re-sent the entire transcript over
stdin to the resumed CLI session, duplicating the original query plus a
growing stack of tool results into the on-disk session every turn. The agent
saw its own context bloating and called it "compaction." Anchor on the last
assistant message and slice forward instead — the genuine delta since the
last turn already on disk. Same bug+fix in droid-cli (copy of pi-claude-cli).
Quick chat also created a fresh pi/Claude CLI session per user message and
faked continuity by stuffing the last 50 chat_messages into the prompt as
"## Previous Conversation". Replace that with real session continuity:
chat_sessions gains a cliSessionFile column (migration 56); ChatManager
opens the existing pi SessionManager file when present and creates a fresh
one (persisting its path) on the first turn. The prompt now carries only
the new user content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge lands v0.12.0 with two major features: a droid-cli provider integration adding auth routes, status endpoints, and a settings toggle hook for controlling CLI-based authentication, plus a new experimental agent onboarding modal with a create-agent form. The release also stabilizes engine st
Fusion-Task-Id: FN-2981
Merges FN-3049 to replace all runtime dynamic imports of `@fusion/engine` with static imports that esbuild can inline into the CLI bundle, preventing the known regression where the published `npm i -g @runfusion/fusion` silently failed at the import site. Also ships FN-3024, adding an experimental a
Fusion-Task-Id: FN-3049
This merge delivers v0.12.0, which includes a new experimental agent onboarding modal (`ExperimentalAgentOnboardingModal`) that streamlines the handoff from onboarding to the create-agent form, along with backend lifecycle tests and documentation for the experimental flag. The release bumps all work
Fusion-Task-Id: FN-3024
Added a new `droid-cli-probe` module to the dashboard package with test coverage, implementing a CLI probe capability for the droid system.
Fusion-Task-Id: FN-2979
This merge implements a recoverable pending-login auth flow (FN-3047) across three phases: cancellable in-progress auth routes in the API, an extended auth client contract, and updated ModelOnboardingModal/SettingsModal UI with proper cancel-button visibility during logout. It also adds `dashboard-r
Fusion-Task-Id: FN-3047
This merge introduces canonical mock helper modules across the dashboard and engine packages, and migrates representative test suites to use them for consistency. The changes add four new mock helpers (`mockApi.ts`, `mockLucide.ts`, `mockCore.ts`, `mockCoreEngine.ts`) and harden the API mock proxy's
Fusion-Task-Id: FN-3044
- Add backend onboarding session service and legacy API routes for start, state, and import/export generation
- Introduce experimental AgentOnboardingModal UI with stream-state handling, model selector wiring, and onboarding launch flow in AgentsView
- Add onboarding gating via prompt overrides/store updates and include search behavior fix for partial-match filtering
- Expand dashboard/core tests for onboarding API, modal behavior, agents/settings integration, and prompt override handling
Fusion-Task-Id: FN-3025
Merges FN-3009 (remote settings migrated to global scope with updated schema and docs), FN-3034 (legacy routines compatibility layer: agentId backfill on load and schema validation for old format), and FN-3039 (Nerd Font glyph asset bundled into the terminal with CSS prioritization). Core settings s
Fusion-Task-Id: FN-3034
This merge migrates Fusion's settings architecture to a unified global scope (FN-3009), consolidating settings types, defaults, merge semantics, updater logic, and routing into a centralized system that aligns dashboard remote settings with global configuration. Secondary changes include adding the
Fusion-Task-Id: FN-3009
Exports the research settings resolver from `@fusion/core` types to fix dashboard alias builds, ensuring the resolver is available when imported through the dashboard's module alias configuration.
Fusion-Task-Id: FN-2995
The vitest child-process guard wrapped exec/execFile without preserving the
`[util.promisify.custom]` symbol, so awaited `execAsync` resolved to a raw
stdout string instead of `{stdout, stderr}`. That single regression cascaded
through ~60 "failing" tests across cli, core, engine, and dashboard whose
production code was actually correct. Also relax the AI-CLI blocklist for
cheap introspection (--version/--help/which …), give SIGTERM'd subprocesses a
brief grace period before being flagged as "left running", fix a few real
test-side bugs uncovered along the way (executor mock step transitions, iOS
last-resort keyboard path, mission SSE replay tests racing with the real AI
agent), and convert dashboard route tests' dynamic `await import("../server.js")`
to static imports so first-test timings drop from 2–5s to <200ms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The merge hardens the cloudflared installation backend with fallback logic and architecture detection, updates the SettingsModal UI with clearer manual installation guidance, and adds comprehensive test coverage for the remote access routes including the new memory-based settings registration flow.
Fusion-Task-Id: FN-2977
Merges external Tailscale funnel detection (FN-2976) — adds types, detection logic, status inclusion, kill flow, and a dedicated UI panel for funnel processes started outside Fusion — alongside custom AI providers API routes and a new settings UI section (FN-2965).
Fusion-Task-Id: FN-2976
Hoist agentStore to outer scope so the finally block can close it
before removing the temp dir, preventing leaked SQLite handles when
the test errors mid-try.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each agent now gets its own .fusion/agents/<id>/HEARTBEAT.md procedure
file instead of sharing a single project-wide file. A one-shot
migration in AgentStore.init() re-points existing agents off the legacy
shared path and copies the legacy file's contents into each agent's
new per-agent location so operator edits are preserved.
The HeartbeatTriggerScheduler now phase-aligns the first tick to
lastHeartbeatAt + intervalMs so a process restart resumes each agent's
existing schedule rather than waiting up to a full interval before
firing again. Overdue ticks fire promptly within a small jitter window
to avoid a thundering herd at boot.
Also fixes three pre-existing QuickChatFAB test failures introduced by
ad4db8243: auto-select default model now switches to model mode whether
or not agents are present, the model tag only renders in model mode,
and one test scopes its option lookup to role="option" to disambiguate
the in-header tag from the dropdown entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds server-side validation for the `unavailableNodePolicy` field on the
PUT /api/settings route. Accepts only `block` or `fallback-local`;
rejects unknown strings and non-string values with 400.
- packages/dashboard/src/routes/register-settings-memory-routes.ts:
call `validateUnavailableNodePolicy` and throw `badRequest` on invalid input
- packages/dashboard/src/__tests__/routes.test.ts: cover accept,
invalid-value, and non-string cases
Fusion-Task-Id: FN-2952