The mobile QuickChat panel was unusable on iOS Safari: the input bar
rendered off-screen, the soft keyboard often did not open on FAB tap,
the panel slid up out of view on the second input focus, and the
header pill overflowed when the model name was long.
- The FAB JSX no longer emits inline right/bottom styles on mobile,
and the mobile media query pins every edge with !important so the
panel cannot be dragged off-screen by the desktop draggable hook.
- Body scroll is locked while the panel is open via overflow:hidden
on <html> and <body>, and the document is reset to scroll 0 on
open. This prevents iOS from leaking residual scroll into the
fixed-positioned panel between opens.
- An always-mounted, offscreen stealth input claims the iOS soft
keyboard inside the FAB click gesture (the real composer input is
initially `disabled` waiting for the chat session, and iOS will
not open the keyboard for a disabled input). Focus is transferred
synchronously to the real input once it becomes enabled.
- The composer input intercepts touchstart on mobile and re-focuses
with `preventScroll: true`, suppressing iOS's default focus-and-
scroll behavior that was shifting visualViewport.offsetTop and
sliding the panel up off-screen on a refocus.
- A useLayoutEffect mirrors visualViewport.height onto the panel as
--vv-height directly via the DOM, bypassing React state to avoid
the per-event reconciliation lag that read as visual jank during
the keyboard slide-in. On blur, the variable is cleared and a
short suppress window (~450ms) prevents iOS's mid-dismiss reports
from clobbering the regrowth, so the panel snaps back to full
height immediately while the keyboard finishes sliding down on
top of it.
- The model-tag pill in the header collapses to the provider icon
when the tag would otherwise exceed ~12 characters on mobile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the mobile terminal header used flex-wrap to stack tabs and
the action cluster on separate rows. The actions now stay pinned to the
right edge of the header row while the tab bar flexes to fill remaining
width and remains horizontally scrollable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 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
- useTasks debounced effect bailed when searchQuery transitioned back to
undefined (App passes `searchQuery || undefined`), so clearing the
input never refetched the unfiltered list. Track previous value via
ref and only skip the initial mount.
- FTS5 search used bare tokens (exact term match), so "frob" did not
match "frobnicator". Append `*` to each token (and to quoted tokens)
for prefix matching. LIKE fallback already did substring matching.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge brings multiple substantial features: new research extension tools wired through the AI engine (with full test coverage and documentation), legacy routines agentId backward compatibility with migration paths, migration of experimental remote settings to the global scope, Nerd Font glyph a
Fusion-Task-Id: FN-2996
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
Merges two features: FN-3039 bundles a 2.5MB Nerd Font glyph asset into the dashboard and updates the terminal to prioritize the bundled font, improving icon rendering across environments; FN-3035 tightens insight tool type definitions and adds corresponding tests. The CLI extension gains new tools
Fusion-Task-Id: FN-3039
This merge adds an AgentLogViewer component, improves the NewAgentDialog and QuickChatFAB with mobile keyboard awareness and custom model dropdowns, and introduces insight tools in the CLI extension with tightened typing and comprehensive test coverage. The theme system receives enhancements in `use
Fusion-Task-Id: FN-3041
This merge adds research settings to the settings modal and dashboard (FN-2839, FN-3029), implements a dashboard font scale setting with persistence and documentation (FN-3027), refactors AgentDetailView with a new AgentLogViewer CSS module (FN-2839), and improves NewAgentDialog with custom model dr
Fusion-Task-Id: FN-3027
This merge adds a new **Research Settings** section to the settings modal and dashboard (ResearchView), including a new schema, types, and resolver exported for dashboard alias builds, along with comprehensive tests and documentation updates. It also makes task review step fields editable in the Tas
Fusion-Task-Id: FN-3031
Adds `focus-visible` styling to action buttons in the AgentLogViewer component, improving keyboard accessibility for interactive button elements.
Fusion-Task-Id: FN-3037
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
Adds a heartbeat markdown viewer modal to the AgentDetailView (FN-3030) with styling, tests, and documentation for the feature, while also rendering multi-sample theme swatches in the ThemeSelector component (FN-3028).
Fusion-Task-Id: FN-3030
ThemeSelector now renders multi-sample theme swatches with richer styling; tests were updated to match and the swatch contract is documented in the dashboard README.
Fusion-Task-Id: FN-3028
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>
Adds Nerd Font as the terminal font stack default with a migration to update existing user settings, updates the terminal and settings modals to reflect the new font choice, and includes corresponding tests plus a patch changeset for `@runfusion/fusion`.
Fusion-Task-Id: FN-3026
Workflow-step REVISE retries, pause→todo handoffs, and the
context-overflow fresh-session requeue were all routing tasks back to
`todo` before returning to `in-progress`. The default reopen-to-todo
path reset every step to pending and rewrote PROMPT.md checkboxes, so
each retry restarted from step 0 even when earlier steps had already
been done — the symptom seen on FN-2978, where every workflow REVISE
or pause cycle wiped the task's progress.
- Add `preserveResumeState` to `TaskStore.moveTask`. When set, skip
`resetAllStepsToPending` + `resetPromptCheckboxes` and keep
`worktree` and `executionStartedAt` so the resumed run reattaches to
the same checkout. `status`, `error`, and `blockedBy` still clear.
- Use it on the workflow-rerun bounce, the three pause-graceful
handoffs, and the context-overflow requeue. The agent-terminated
pause path still discards (it nukes worktree+branch by design).
- Context-overflow requeue clears `sessionFile` synchronously in the
awaited `updateTask` immediately before `moveTask`, so the next
dispatch cannot reopen the saturated session via a stale pointer.
- `fn_task_update` no longer silently regresses `done`/`skipped` steps
to `in-progress`, no longer captures a stale rewind checkpoint when
it does, and tells the agent honestly when a regression is ignored.
- Mobile chat keyboard: ChatView/QuickChatFAB gate layout on the new
`keyboardOpen` flag so focused-input + viewport-shrink iOS cases
still adjust when the computed overlap is zero.
Tests:
- New `preserveResumeState` coverage in store.test.ts; updated
workflow-rerun + pause-graceful assertions in executor.test.ts.
- Restructured the previously-flaky "routes exhausted prompt-mode
workflow hard failures" test to drive the bounce inline; passes in
isolation and in the wider workflow/pause/context sweep (59/59).
- Added regression tests in ChatView.test.tsx and QuickChatFAB.test.tsx
for the iOS last-resort `keyboardOpen=true, keyboardOverlap=0` case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three coordinated fixes for the FN-2978 incident class — auto-requeues
that orphaned committed work and watchdog kills on long verification runs.
**Auto-requeue branch reuse** (executor.ts, worktree-pool.ts)
- executor.ts:1782 now uses `task.branch || fusion/<id>` so persisted
branches are honored on requeue. Previously the hardcoded fallback
always tried to re-create the original branch, hit a conflict with
the prior run's ref, and got suffix -2/-3. Other call sites already
honor task.branch — this aligns the worktree-acquisition path.
- worktree-pool.ts:181 prepareForTask now probes existing branches with
`git rev-parse --verify` and checks them out as-is. Falls through to
suffixed creation only when the branch is genuinely in use by another
live worktree. Previously force-reset with `checkout -B`, destroying
prior commits.
- New private reconcileStepsFromGitHistory walks `git log
baseCommitSha..HEAD` for `feat(FN-X): complete Step N` commits and
marks matching steps[] as done so resumes don't redo committed work.
**Manual reset endpoint + UI** (dashboard)
- POST /api/tasks/:id/reset (requires `confirm: true`) — clears worktree,
branch, all retry counters, resets steps[] to pending, moves to todo.
Distinct from /retry which is the soft-resume path.
- Reset button alongside Retry in TaskDetailModal with confirm dialog,
wired through useTasks → AppModals → API.
**fn_run_verification tool** (run-verification-tool.ts, executor.ts)
- New custom tool wrapping test/lint/build commands with a heartbeat
callback (per-line + 60s synthetic), 200KB head+tail output cap, hard
timeout with SIGTERM→SIGKILL escalation, and auto-bootstrap detection
for missing node_modules. Prevents the inactivity watchdog from
killing sessions during long compiles.
- Cross-platform via `shell: true` (Node picks /bin/sh on POSIX,
cmd.exe on Windows). Prompt section in EXECUTOR_SYSTEM_PROMPT and
EXECUTOR_PROMPT_TEXT instructs agents to prefer package-scoped
verification first and reserve workspace-scoped runs for final
integration.
**Tests** (64 passing)
- detect-pseudo-pause.test.ts (27 tests) — covers all 7 regex patterns,
structural fallback, FN-2978 regression text.
- reconcile-step-regex.test.ts (25 tests) — pins the commit-message
regex against a wide variant set.
- run-verification-command.test.ts (12 tests) — basic execution, output
capture, heartbeat callbacks, timeout, error handling. POSIX-specific
cases (multi-cmd `;`, `>&2`, `\$USER`) gated behind itPosix.
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
Merged v0.9.4 bringing custom AI providers to the dashboard — SettingsModal now surfaces provider configuration with an onboarding disclosure flow, backed by new API routes (`/custom-providers`) and comprehensive test coverage. The engine adds self-healing with a ghost-review fallback recovery path,
Fusion-Task-Id: FN-2965
The merge adds an inline version refresh control to the Settings modal with improved touch targets for better mobile usability, replacing a separate duplicate-updates check button. Changes span the SettingsModal component, its CSS styling, and new test coverage for the control behavior.
Fusion-Task-Id: FN-2974
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
739e899b5: 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>
- Pre-select the global default (or first available) model on first open so
users can chat without manually picking from the dropdown.
- Bump specificity of the new-chat (+) button styles so the CTA bg/text
applies instead of being overridden by the header's .btn-icon rule.
- Focus the message input on panel open via rAF.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TUI splash/footer and the dashboard /api/health + /api/updates/check
each had their own walk-up resolver with different rules, so they could
report different versions and trigger spurious update-available banners.
Dashboard now uses a direct ancestor walk (matching the TUI's old logic)
and only falls back to the sibling-cli probe for dashboard-source dev
mode; logo.ts delegates to it so there is one source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Increase desktop nested model menu width baseline for better model-name readability
- Raise desktop width floor from 240px to 320px while preserving viewport clamping
- Replace hardcoded QuickEntryBox menu spacing/shadow/radius values with design tokens
- Update QuickEntryBox nested menu tests to assert the new desktop width behavior
Fusion-Task-Id: FN-3010