Adds a plugin AI security scan gate that enforces security scanning before plugin installation, including new `plugin-security-scan.ts` infrastructure, CLI integration (`fn plugin install`), plugin loader hooks, and a changeset. Implements branch filter persistence across sessions in the dashboard (
Fusion-Task-Id: FN-3426
- Add core plugin AI security scan module and schema support for scan toggle/state metadata
- Enforce scan checks during CLI and dashboard plugin install flows, with preserved API error status on scan failures
- Expose plugin scan toggle and rescan actions in dashboard/plugin manager with route and UI coverage
- Update plugin authoring and CLI/dashboard docs, plus add changeset for published CLI package
Fusion-Task-Id: FN-3077
Restructures the dependency graph plugin into a modular architecture with separate edges, filters, layout, types, and interaction hooks, adding comprehensive tests for the new modules. Implements a board branch filter with "no-branch" semantics and responsive layout refinement, while removing legacy
Fusion-Task-Id: FN-3425
Removes the `QuickEntryBox` component and its styles as part of FN-3632 cleanup, with its tests refactored accordingly. Also updates the test-changed script and fixes an isolated test home cleanup ordering issue.
Fusion-Task-Id: FN-3632
- Add mesh task replication plumbing in core store/types with node-aware task creation metadata
- Expose dashboard and mesh API route coverage for replicated task creation and sync behavior
- Restore independent scrolling in Git Manager changes list and prevent Scripts modal scroll regressions
- Expand tests across core, dashboard routes, API, and CLI extension insights for the new behavior
Fusion-Task-Id: FN-3631
Implements distributed mesh task creation by adding replicated create primitives to the core store, wiring new API routes (`register-mesh-routes.ts`) that replicate task creation across clustered nodes while preserving remote-targeting metadata, and updating the dashboard's task creation flow accord
Fusion-Task-Id: FN-3450
Adds documentation for graph navigation canonicalization (FN-3080) covering plugin authoring and dashboard usage, along with a changeset for the `@runfusion/fusion` package release.
Fusion-Task-Id: FN-3080
Previous textarea-only touchmove listener didn't catch swipes that
started elsewhere (header, composer padding) — those still panned the
iOS visualViewport over the locked document, sliding the composer up.
Replaces it with a document-level non-passive touchmove listener that
fires while keyboardOpen on mobile and preventDefaults all gestures
EXCEPT when the target is inside .chat-messages (the one container
where pan-y should still work). Stops both header-swipe and composer-
swipe from panning the page, while leaving messages-list scroll
intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
React's onTouchMove handler is registered as a passive listener by
default, so the previous JSX-handler preventDefault() was silently a
no-op — drags on the composer still scrolled the input box up.
Attaches the listener imperatively via addEventListener with
{ passive: false } so preventDefault actually cancels the drag. Tap
(touchstart + touchend without touchmove between) is still unaffected,
so first-tap focus continues to work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex returns weekly window reset_at in milliseconds in some cases,
which was being multiplied by 1000 and producing nonsensical reset
countdowns and pace calculations. Route through _parseResetTimestamp
so both formats work, and add a regression test.
Also tightens comments in useMobileKeyboard / ChatView around why
visualViewport scroll events skip offsetTop updates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrated the dependency-graph plugin's storage layer to use the project's scoped storage API (`projectStorage`) and added canonical storage-key assertions. The ChatView.tsx utility reference was updated to match the new storage path.
Fusion-Task-Id: FN-3626
Restores plugin management features across the CLI and dashboard, including a new `plugin available/settings` commands and a refactored PluginManager component that groups built-in plugins (agent-browser, fusion) separately from custom ones, with updated documentation on the plugin authoring guide.
Fusion-Task-Id: FN-3575
Refined branch chip visibility logic in TaskCard (suppressing the chip when the current branch is the default) and added test coverage for those suppression cases, with corresponding documentation updates. Also added chat layout regression tests and fixed a composer drag-pan issue on mobile touch de
Fusion-Task-Id: FN-3629
touch-action: manipulation is needed on the textarea so iOS registers
first-tap focus reliably, but it also allows pan-y — which let the
user drag the input box up off-screen with the keyboard up.
Cancelling touchmove blocks the drag without affecting tap (a tap
fires touchstart + touchend with no touchmove in between), so the
composer stays locked AND first-tap focus works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unassigned branch filter options to the dashboard header, allowing users to filter tasks by unassigned status. Updates the Header component with new filter controls and includes corresponding tests for the App and Header components.
Fusion-Task-Id: FN-3424
Two fixes for residual mobile chat issues:
1) Textarea touch-action: none was preventing iOS from registering a
clean tap-to-focus, causing the keyboard to flash up via the
programmatic focus() in onTouchStart and then auto-dismiss because
iOS never saw the gesture complete. Switching to manipulation
allows tap while still blocking pan/zoom — the composer stays
anchored thanks to overscroll-behavior: contain on its container.
2) On switch-away-and-back the visualViewport metrics could get
stuck in a half-state (composer pushed up, or blank pane covering
it). Adding a visibilitychange / pageshow handler on ChatView that
force-blurs and re-focuses the active textarea makes iOS resync
the keyboard / vv metrics cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When ChatView remounted (e.g. tab switch with keyboard still up), the
hook started with keyboardOpen=false and corrected itself only after
the effect ran. That single stale-state render briefly unhid the
executor status bar, which appeared as a blank pane covering half the
input box before the next state update settled it.
useState initializers now call getKeyboardMetrics() lazily on first
render so the very first paint already reflects the live keyboard
state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tab/page switches with the keyboard up sometimes still left the
chat-thread anchored wrong because the timed re-reads (50/200/500ms)
missed the actual settlement frame on slower devices.
updateWithTail now also kicks off a rAF poll that re-reads metrics
each frame until offsetTop is stable for 2 consecutive frames or 1.5s
elapses. Combined with the extended timed tail (now adds 1000/1500ms),
this should catch both fast and slow iOS settlement paths without
indefinite polling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes Step 3 of FN-3618 by surfacing AI provider icons throughout the Settings modal and model dropdowns, updating both the component styles and adding integration and unit tests to cover the new icon display behavior.
Fusion-Task-Id: FN-3618
Switching away from the chat view and back left the hook with stale
metrics — keyboardOpen was false at remount even though the system
keyboard was still visible, so the .chat-thread transform never
re-applied and the message list anchored too high. focusin and
synchronous reads also captured offsetTop mid-transition.
Adds a tail of delayed re-reads (50ms, 200ms, 500ms) on focusin,
visibilitychange, and pageshow so iOS has time to settle before the
final snapshot is taken.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A pan-y gesture on the focused textarea still triggered iOS auto-
scroll-into-view, lifting the composer up off-screen even with body
locked. touch-action: none on .chat-input-textarea blocks gesture-
driven panning entirely (tap-to-focus, typing, caret moves still
work) so the composer stays anchored. Trade-off: loses gesture-
scroll within the textarea once content exceeds max-height — minor
since textareas rarely exceed 6 lines in this composer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iOS fires visualViewport scroll events at 60fps during a pan with the
keyboard up. Routing those through React state and into the .chat-thread
translateY(--vv-offset-top) transform amplified the pan into a visible
judder + ~300px shift + body background exposure.
useMobileKeyboard now uses two listeners: a full update (resize +
focusin/focusout) that re-snapshots all metrics including offsetTop, and
a scroll-only update that updates only height/keyboardOpen. offsetTop
is therefore frozen between keyboard open/close events — the transform
correctly compensates for iOS's initial visualViewport shift on focus
without following pan-time movement.
Restores the translateY anchor (so the thread isn't off-screen on
first focus) while keeping the swipe-jitter fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The transform amplified jitter during iOS textarea swipes: visualViewport
scroll events updated --vv-offset-top rapidly, the transform followed,
and the thread juddered then shifted ~300px exposing the body background.
Removing the transform leaves the thread where iOS places it; the
height-shrink to var(--vv-height) still constrains it to the visible
area. Trade-off: on initial focus the thread anchor relies on iOS's
own viewport adjustment instead of explicit compensation, but that
beats the swipe-time jitter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three scoped overscroll fixes for the ChatView mobile keyboard-up case:
- .chat-messages: overscroll-behavior: contain so edge-swipes on the
messages list don't bounce the parent / document.
- .chat-input-area: overscroll-behavior: contain so swipes on the
composer footer don't escape upward.
- .chat-input-textarea: touch-action: pan-y + overscroll-behavior:
contain so a vertical swipe started on the textarea stays inside
the textarea (multi-line scroll still works) instead of
rubber-banding the page and exposing the body background.
User-reported symptom: keyboard up + swipe up on the textarea edge,
the composer shakes and a gray box (body background) partially
covers it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge delivers five major themes: a large mobile chat UX pass (viewport-aware GitManager CSS, compact tool-call layout, keyboard-safe modals, and iterative fixes for the send-button double-fire behavior), a complete refactor of the TaskDetailModal test suite from a 6745-line monolith into eight
Fusion-Task-Id: FN-3614
After dismissing and re-bringing up the mobile keyboard, iOS could
leave window.scrollY > 0 and visualViewport.offsetTop > 0. With
useMobileScrollLock then pinning body{position:fixed} relative to that
drifted scroll, the message thread anchored above the visible viewport
and a large blank area appeared below it.
handleInputFocus now resets window scroll to (0,0) on mobile in a
zero-delay timeout — late enough that iOS finishes its own
scroll-into-view first, but before useMobileScrollLock observes the
drifted state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two mobile chat regressions addressed together:
1) ChatView send button: preventDefault now fires on pointerdown for
touch pointers, before iOS blurs the textarea. The previous
onMouseDown.preventDefault was too late on iOS (mousedown is
synthesized after touchend, by which point the keyboard has
already started dismissing). Click still runs the action so quick
taps remain reliable.
2) ExecutorStatusBar: hidden on mobile while keyboard is open,
mirroring MobileNavBar. The bar is position:fixed against the
layout viewport, which iOS leaves anchored below the keyboard;
during a swipe/pan it would slide over the messages list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The imperative useLayoutEffect approach made mobile worse — first tap
flickered and didn't bring up the keyboard, while the original
swipe-overlap symptom remained. Restoring the previous React-state
flow until a better fix is identified. Removes the changeset that
shipped with the failed attempt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mobile composer/footer slid over the message list when the user
swiped with the keyboard up. Cause: --vv-height / --vv-offset-top were
routed through React state via useMobileKeyboard, so on iOS — which
fires visualViewport scroll/resize on the same frame as its keyboard
animation — the .chat-thread translation lagged by one paint, visible
as the composer momentarily floating over messages.
Now those two vars are written imperatively in a useLayoutEffect
directly to the .chat-thread DOM node on every visualViewport event,
mirroring the working pattern at QuickChatFAB.tsx:1032-1052 (which
already works correctly on mobile). Only --keyboard-overlap (a
structural open/close signal, not per-frame) still flows through
React state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-add preventDefault on mousedown so the textarea doesn't blur when
the user taps send — keyboard stays up, no viewport reflow jumping
the input to the top of the screen. The action still runs on click
(which fires reliably from the iOS touch sequence even for quick
taps), so this preserves the previous fix's quick-tap reliability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mobile send button used pointerdown + touchstart with preventDefault
and a focus-preservation dance to keep the keyboard up while sending.
That path silently failed on quick taps on iOS — only a long press
registered. Switching to plain onClick (with touch-action: manipulation
to skip the click delay) fires reliably on tap. The soft keyboard may
dismiss on send now, which is a minor regression vs. the previous
intent but vastly preferable to silent failure.
QuickChat is unchanged because it already works on mobile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous fix introduced a regression: handledMobileSendRef /
handledMobileActionRef was set in onPointerDown/onTouchStart but only
cleared in onClick. preventDefault() on pointer/touch events suppresses
the synthesized click on iOS Safari, so the ref stayed true forever
after the first tap — every subsequent tap (quick OR long press) hit
the new dedupe guard and silently bailed.
Now both handlers schedule a 500ms setTimeout to self-clear the ref
alongside their action. That covers the full pointerdown/touchstart/
click burst from one tap while still letting the next user tap go
through.
Applied to: ChatView send button, QuickChat send button, QuickChat
stop button.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The ChatView and QuickChat send/stop buttons each had both an
onPointerDown and an onTouchStart handler invoking the action. On a
quick mobile tap both fire, so handleSend / handleSendMessage /
stopStreaming ran twice in rapid succession. The second invocation
closed the first's SSE stream (streamRef.current.close()), the server
treated that as a cancel via beginGeneration, and the chat ended with
no output — exactly matching the reported "tap silently fails, long
press works" symptom (long press happened to suppress one of the two
events).
Both handlers now early-return when the existing handledMobile*Ref
flag is already set, so only the first event for a given tap fires the
action. The send button additionally gets touch-action: manipulation
(removes the click delay that lets the textarea blur win the race) and
an expanded invisible hit area via ::before so slightly-off taps don't
land on the surrounding textarea and dismiss the keyboard without
sending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add built-in plugin catalog metadata and constants for shipped installable plugins
- Render a Built-in Plugins section with install, setup-required, and manage states
- Fetch and display plugin setup status, including setup install flow and status badges
- Add PluginManager CSS and tests covering built-in plugin install/setup behaviors
Fusion-Task-Id: FN-3574
Merges the evaluator follow-up suggestion system (FN-3392, Steps 1–5), which adds a normalized contract, provenance tracking, and lifecycle documentation for AI-generated follow-up tasks, alongside chat UI improvements including unread indicators in header and mobile nav, corrected message routing,
Fusion-Task-Id: FN-3392
The merge lands three commits for FN-3622's canonical task timing calculations, adding `taskTiming.ts` logic and stats timing semantics to `TaskDetailModal` and `TaskTokenStatsPanel`, backed by regression tests across those panels and the root `test-changed.mjs` script. The remaining commits introdu
Fusion-Task-Id: FN-3622
The merge completes Step 2 of FN-3605 by refactoring the ChatView mobile header to preserve its visual identity while converting the toggle control to an inline layout, accompanied by corresponding tests.
Fusion-Task-Id: FN-3605
Implements a chat unread indicator across the header and mobile nav bar, driven by a new unread-response tracker in the app state, with tests and a docs update for the feature.
Fusion-Task-Id: FN-3604
Hardens async assertions in the task detail modal's pause/unpause action tests, improving test reliability for that interaction flow.
Fusion-Task-Id: FN-3600
Documentation delivery alignment completing Step 5 of FN-3369, updating the CLI reference, research docs, and research hardening preflight guide with consistent documentation delivery guidance across all three files.
Fusion-Task-Id: FN-3599
The merge introduces a major TaskDetailModal test refactor (splitting a 6.7K-line monolith into five focused suites), significant merger improvements including autostash race-rescue, deduplication, and advisory logging for destructive operations, a new TUI narrow-mode log-split feature for the dashb
Fusion-Task-Id: FN-3596
- Document how branch field data flows through task lifecycle
- Clarify architecture-level plumbing responsibilities for branch metadata
- Capture Step 4 documentation updates in docs/architecture.md
Fusion-Task-Id: FN-3421