External services (Grafana, Sentry, Slack) call plugin webhooks with
their own per-plugin shared secret — they cannot present the
dashboard's daemon token. Daemon auth was 401'ing those callbacks
before they reached the plugin handler, so even with the route
correctly mounted the secret check inside the plugin never fired.
Add a registry of dynamically-exempt paths populated at server
startup when plugin routes are mounted. Plugin management routes
(/api/plugins, /api/plugins/:id/enable, etc.) stay gated; only the
plugin-defined routes (/api/plugins/:pluginId/<route>) are exempted.
Each plugin handler is responsible for its own secret check (the
telemetry-watcher webhook compares Authorization Bearer against
settings.grafanaWebhookSecret in constant time at the handler).
Phase-1 telemetry-watcher's grafana-webhook handler 401'd because the
dashboard never mounted plugin-supplied routes — getPluginRoutes()
exists on PluginLoader but no caller consumed it. The smoke test was
working around this by injecting incident tasks directly through
/api/tasks; we want the real path to work end-to-end.
Two changes:
1. PluginLoader gains createContextFor(pluginId, { taskStore? }).
Lifecycle hooks still see the loader's bound store (the cwd
project), but REST handlers receive a project-scoped store derived
from the request's projectId so a Grafana webhook addressed to
sase opens tasks in sase even though fusion's loader is bound
to its own cwd. Settings still come from the loader's store at
load time, which is the right thing — settings don't follow the
request.
2. routes.ts iterates pluginLoader.getPluginRoutes() once at server
startup and binds /api/plugins/:pluginId/:routePath to a handler
that resolves project context per request, builds the context via
createContextFor, and forwards to the plugin's route. ApiError +
rethrowAsApiError preserve the dashboard's standard error envelope.
Plugins added after server start still need a restart for routes to
bind; reloadPlugin doesn't currently re-mount Express handlers. That
limitation matches the existing constraint and is out of scope here.
ProjectEngineManager runs one engine per registered project, but the
heartbeat handler in register-agent-runtime-routes.ts only used the
single global heartbeatMonitor passed via ServerOptions. That monitor
is bound to the cwd project's engine, so heartbeat triggers for any
secondary project silently no-op'd: the run id was returned but no
execution actually happened.
Add resolveHeartbeatMonitorFor(scopedStore) that walks
engineManager.getAllEngines() and returns the engine whose working
directory matches the request's scoped store, falling back to the
global monitor when its rootDir matches. Use the resolver at every
heartbeat call site (state-pause stop, /agents/:id/heartbeat,
/agents/:id/runs, /agents/:id/runs/stop).
The dashboard modules used a variable-specifier dynamic import
(`const m = "@fusion/engine"; await import(m)`) to defeat bundler static
analysis. tsup honored that and left the dynamic import in dist/bin.js,
so the published `@runfusion/fusion` package failed at runtime with
"createFnAgent2 is not a function" — `@fusion/engine` isn't on npm and
the silent catch set the binding to undefined. Replaces the trick with
static imports across planning, chat, subtask-breakdown, mission-interview,
agent-generation, ai-refine, roadmap-suggestions, milestone-slice-interview,
and routes. Core can't statically import engine (cycle), so it now exposes
setCreateFnAgent and engine wires itself in at module load. Documents the
pattern in AGENTS.md.
FixesRunfusion/Fusion#9.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- fix(FN-2610): add changeset for health version fix
- test(FN-2610): verify health endpoint returns real package version
- fix(FN-2610): read version from package.json in health endpoint
Update test regexps to match .modal.terminal-modal (two-class selector) and assert min-height: 100dvh on mobile. Also clean up a stale comment in TaskDetailModal.
When the soft keyboard opens on mobile, the overlay's align-items:center was vertically centering the shrunken modal, pushing the header/tabs out of view. Add a rule to switch to align-items:flex-start when --keyboard-overlap is detected.
readStoredAuthProvidersFromDisk() previously returned only the first
successfully-parsed auth file, missing providers that existed only in
fallback locations (e.g. github-copilot in ~/.pi/agent/auth.json when
another provider was in ~/.fusion/agent/auth.json). Now iterates all
candidates and merges entries with first-found-wins priority.
- Persist hidden usage-window IDs in project storage so visibility choices survive reloads
- Add hide/show actions in UsageIndicator rows plus a provider-level control to reveal hidden windows
- Style hidden window rows and usage header actions in PlanningModeModal for clear state and aligned controls
- Expand UsageIndicator and projectStorage tests to cover hide/show behavior and persistence callbacks
- Add a GitHub Copilot usage fetcher to usage collection flow
- Include copilot in provider aggregation so totals include Copilot consumption
- Map the copilot provider to the correct icon key in UsageIndicator
- Expand usage tests to cover Copilot provider parsing and aggregation behavior
- Add register-file-workspace-routes.ts with task file, workspace discovery, file operation, and markdown/search endpoints
- Mount the new file workspace registrar from routes.ts using shared route context and injected helpers
- Remove file/workspace route implementations from routes.ts and register-task-workflow-routes.ts to keep domain boundaries clear
- Update routes/README.md with registrar responsibilities and ordering constraints for file wildcard routes
- Render provider icons on TaskCard model metadata with token-based sizing and spacing
- Add provider icon display in TaskDetailModal for executor, validator, and planning model rows
- Update dashboard styling with reusable provider icon classes and layout tweaks in component CSS
- Expand TaskCard tests to cover provider icon rendering and fallback behavior
- Add a dedicated Bedrock SVG provider icon and provider token styling
- Wire Bedrock provider aliases so provider detection resolves to the new icon key
- Update usage indicator mapping so Bedrock usage providers render the correct icon
- Expand ProviderIcon tests to cover Bedrock icon rendering and alias coverage
- Add dedicated xAI and Opencode SVG icon components in ProviderIcon with provider config entries
- Map grok to the xAI icon and add opencode color token for consistent themed rendering
- Extend UsageIndicator provider normalization to resolve xai/grok and opencode icon keys
- Add ProviderIcon tests for xai, grok alias behavior, and opencode rendering/color normalization
Builds now emit version.json + a __BUILD_VERSION__ define. The client
re-checks the remote version on visibilitychange/focus and reloads on
mismatch, so a backgrounded tab doesn't hit a 404'd hashed chunk and
surface "'text/html' is not a valid JavaScript MIME type" when opening
Settings. ErrorBoundary catches stale-chunk errors as a safety net.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Register remoteAccess in KNOWN_EXPERIMENTAL_FEATURES so it appears in Experimental Features toggles
- Compute visible settings sections from experimental flags and hide Remote Access nav/select entries when disabled
- Redirect stale initial or active remote section selection to the first visible section instead of rendering a hidden section
- Add SettingsModal tests covering remote section visibility, experimental toggle presence, and disabled-state fallback behavior
- Remove the standalone remoteEnabled setting from CLI, core settings defaults/types, and dashboard settings APIs/UI
- Treat remote access as enabled when an active provider is selected and that provider is configured as enabled
- Update remote auth and engine lifecycle checks to gate on provider activation instead of a global flag
- Adjust tests and add a changeset documenting the remote access configuration simplification
- Increase dashboard board column fixed width to 300px in styles and related expectations
- Update mobile board snap-centering behavior to match wider column sizing
- Adjust column fixed-width and board mobile tests to reflect the new layout measurements
- Extract auth, model, and usage route registration into dedicated modules
- Preserve existing mount order and orchestration behavior in the top-level routes entrypoint
- Add route contract tests covering auth and usage endpoints to guard wiring regressions
- Update routes README and address lint drift in the orchestrator refactor
- Add Step 1 tests for remote provider selection and lifecycle state handling in SettingsModal
- Add Step 2 tests for remote token flows plus URL and QR rendering/validation scenarios
- Refactor existing SettingsModal test structure to reduce duplication and improve remote settings assertions
- Strengthen regression coverage for remote access UX edge cases in dashboard settings
- Add regression tests across CLI, core, dashboard, and engine for remote access auth, settings parity, and serve/TUI callback wiring
- Expand dashboard route and modal coverage for remote settings/auth flows including node environment behaviors
- Redact provider-switch failure details in tunnel process manager to avoid leaking sensitive provider diagnostics
- Update route registration and engine lifecycle tests to lock in remote-access behavior under real execution paths
- Split monolithic dashboard route registration into dedicated project, node, mesh discovery, and settings sync registrar files
- Add settings sync helper and inbound sync registrar modules to isolate shared logic and simplify route composition
- Expand routes test coverage to enforce registrar ordering and prevent regressions after extraction
- Update routes README and clean up lint/doc issues introduced during the refactor
- Add ProjectEngine restore lifecycle core to perform safe restarts and surface detailed restore state transitions
- Expose restore diagnostics through remote-access status types and settings/memory route context, including legacy API mapping updates
- Add comprehensive regression coverage for restore lifecycle behavior in engine and dashboard headless remote-access tests
- Document the restore lifecycle contract in architecture/settings docs and include a patch changeset for @runfusion/fusion