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
When the tunnel was started before parseReadiness flipped to URL-required
(or by an external invocation), the manager's status URL stays null and
buildRemoteLoginUrlForTokenType used to fall back to a constructed
http://<configured-hostname>:<port> URL — which the user saw encoded
into the QR as e.g. "fusionstudio:4040".
Add a tailscale status --json fallback that recovers the public tailnet
DNS name and reconstructs the funnel URL (https://<machine>.<tailnet>.ts.net/).
If both sources fail, surface REMOTE_URL_NOT_READY rather than emitting a
non-public URL into the auth/QR payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Override the stored tailscale targetPort in the start handler using the
actual port the start request landed on (req.socket.localPort), instead
of trusting a value the browser supplied. window.location.port can be
wrong behind a reverse proxy, ssh forward, or split deployment, and
silently funneling the wrong process is worse than the prior
"port-not-configured" error.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Replace placeholder /remote/qr SVG (URL drawn as text) with real QR
rendered via the qrcode package; add format=terminal returning ASCII
QR for the TUI.
* Resolve the public tailscale funnel URL from captured CLI output
instead of constructing http://<hostname>:<port> from a configured
hostname label — that label was never used by `tailscale funnel` and
produced a non-public URL in the auth/QR link.
* Drop hostname requirement from engine + UI; only target port matters.
* Tighten tailscale parseReadiness to require a URL on the matched line
so the tunnel manager doesn't lock in `running` before the URL line.
* TUI: poll remote status, show ● tunnel indicator + URL in MainHeader,
bind Ctrl+Q to a global QR overlay (terminal ASCII), and switch the
in-Settings K shortcut to render the same ASCII QR.
* Auto-poll remote status in the dashboard while in `starting`/`stopping`
so the UI flips to running without reopening the modal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a worktree was rebased onto newer main, baseCommitSha remained an
ancestor of HEAD but the range baseCommitSha..HEAD then swept in upstream
main commits as if they were task changes (FN-2840 showed 33 files for a
4-file task). The previous display-recovery only fired when baseCommitSha
was no longer an ancestor, missing this case.
When enableDisplayRecovery is on and baseBranch is missing, also compute
merge-base(HEAD, main) and prefer it when it's a descendant of
baseCommitSha (tighter fork point). When merge-base is not a descendant
(FN-2855: baseCommitSha is on a deleted feature branch), keep
baseCommitSha so we don't widen to unrelated upstream files.
Also stabilizes the card-vs-Changes-tab divergence: resolution no longer
depends on whether a rebase happened between polls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes for the in-review/in-progress "files changed" count on task
cards and the Changes tab:
- Drop untracked files from /tasks/:id/diff and /tasks/:id/file-diffs.
At review time these are almost always build artifacts/cache/logs not
in .gitignore, not real task changes, and they inflated the count.
- Add display-only `enableDisplayRecovery` option to resolveDiffBase.
When a worktree was rebased onto origin/main after baseCommitSha was
recorded and baseBranch was not set, the prior code fell through to
HEAD~1 — undercounting to just the last commit's files (e.g. FN-2957
showed 2 in review, 6 after merge). Recovery now tries
merge-base(HEAD, main) / origin/main before HEAD~1.
Routes opt into recovery; the merger's mirrored copy
(resolveTaskDiffBaseRef) is intentionally untouched so merge-time
scope warnings still evaluate the strict task base.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
session.prompt() resolves to void; the reply lives in session.state.messages.
The dream route was awaiting its return value, so parsing crashed with
"Cannot read properties of undefined (reading 'match')" once the dream
completed. Extract the assistant text post-prompt and fail soft on
non-string input in extractDreamProcessorResult.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extend batch task update APIs and route handlers to accept nodeId overrides alongside model settings
- Update ListView bulk edit UX to load nodes and apply node overrides through batch updates
- Add node routing visibility improvements in task detail/settings UI and align related labels/styles
- Expand dashboard/API test coverage for node override batch updates and bulk edit behavior
- Remove the separate "Activate Provider" action from SettingsModal and rely on save-only provider configuration
- Ensure saving remote settings enables the selected active provider and keeps provider flags consistent
- Seed remoteAccess defaults in remote settings routes when project settings are missing instead of returning conflicts
- Expand dashboard tests to cover first-use/default remote settings behavior and updated provider lifecycle expectations
When a dependency task merges and its branch is deleted, self-healing nulls
the dependent task's baseBranch. Both resolveDiffBase (dashboard) and
resolveTaskDiffBaseRef (merger) defaulted to "main" in that case, widening
the diff range to merge-base(HEAD, main) and surfacing unrelated history —
e.g. FN-2855 reported 108 changed files instead of 16. Skip the merge-base
step when baseBranch is unset and a baseCommitSha is recorded; fall back to
"main" only for legacy tasks lacking both hints.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add POST /api/memory/dream route that runs project and agent dream processing with AI prompt execution
- Expose triggerMemoryDreams() in dashboard legacy API client for direct dream invocation
- Switch SettingsModal and useMemoryData Dream Now actions from automation lookup to the new endpoint
- Update dashboard route, hook, and settings modal tests to cover success and error handling
- Add a patch changeset for @runfusion/fusion documenting the new endpoint and client helper
- Extend /models API responses to include optional defaultProvider/defaultModelId from global settings, including empty and error paths
- Update QuickChatFAB to prefer the configured default model on load and fall back to first-model selection only when no agents exist
- Move quick chat model tag/title-wrap inline styles into QuickChatFAB.css using dashboard design tokens
- Add QuickChatFAB tests covering default-model auto-selection with and without agents and legacy behavior when no default is configured
- Add a core node-override-guard module, export it from @fusion/core, and enforce conflicts in store updates
- Add API route and CLI extension safeguards so nodeId override updates are blocked when ownership would conflict
- Wire node override routing and validation through dashboard quick-create, list, modal, settings, and task form/detail surfaces
- Add focused unit and integration tests for core guard logic, workflow routes, and dashboard node override UX
The Paperclip runtime card's Local CLI tab previously only derived an apiUrl
from the local config and then made HTTP calls itself, so the Test action and
the company/agent pickers ignored the user's onboarded CLI auth context.
Add CLI-backed variants for every Paperclip call that has a `paperclipai`
counterpart, and route through them when transport=cli — both from the
settings card and from the runtime adapter's prompt path.
- New plugin functions spawning `paperclipai … --json`:
* probePaperclipViaCli, listCompaniesViaCli, listCompanyAgentsViaCli
(settings card test + pickers)
* createIssueViaCli, getIssueViaCli, agentsMeViaCli (runtime hot path)
- New dashboard routes: /providers/paperclip/cli-status, /cli-companies,
/cli-agents (read-only façades over the plugin's CLI helpers)
- PaperclipRuntimeCard: branches on transport=cli to use the cli-* fetchers
- PaperclipRuntimeAdapter: stores transport on the session and routes
createIssue/getIssue + identity derivation through CLI variants in CLI mode;
raises a clear error when agentId is unset in CLI mode (paperclipai has no
/agents/me equivalent)
- getIssueComments / wakeAgent / getRunEvents stay on HTTP (no matching
paperclipai subcommands) and continue to use the apiKey discovered from
the local paperclipai config, so CLI mode still works end-to-end
- Tests: 9 new paperclip-client tests covering each CLI variant + 5 new
adapter tests for the Local-CLI transport branch (64/64 plugin tests pass)
- Update routes.test for the existing BUNDLED_PLUGIN_RUNTIMES fallback so
the bundled hermes/openclaw/paperclip entries are expected alongside
installed plugins
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Hermes / OpenClaw plugin index.ts now re-export `probeHermesBinary` /
`probeOpenClawBinary` and their status types so the dashboard's
`runtime-provider-probes.ts` façade can import them via the public
package entry instead of deep paths.
- Dashboard `package.json` adds `@fusion-plugin-examples/hermes-runtime`,
`…/openclaw-runtime`, `…/paperclip-runtime` as workspace deps so
pnpm symlinks them into `packages/dashboard/node_modules/`. Without
these, the new probe imports failed with "Cannot find module" during
`pnpm typecheck`.
This clears 6 of the 9 outstanding typecheck errors. The remaining 3 are
in the in-flight Hermes plugin rewrite (runtime-adapter still imports
from a deleted `./pi-module.js`; the new `index.ts` calls a factory
with the wrong arg type) and should be resolved by the same change set
that landed the rewrite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update-check frequency (end-to-end)
- Add `updateCheckFrequency: "manual" | "on-startup" | "daily" | "weekly"`
to GlobalSettings (default: "daily").
- Backend `performUpdateCheck` honors the frequency: TTL = day/week,
`manual` returns cache or empty without hitting npm, `on-startup`
refreshes once per process lifetime then serves cache. `/refresh`
route forces network regardless.
- Settings → Updates surfaces a working `<select>` for the cadence,
disabled when auto-checks are off entirely.
- Tests: 4 new cases covering ttlForFrequency mapping, weekly window,
manual semantics, on-startup once-per-process behavior.
TUI update notice
- Read cached update result synchronously on TUI startup. When an
update is available, render a yellow notice line on the splash and
a colored ● next to the version in the status bar.
Settings modal header polish
- Add "Star on GitHub" pill (icon + Star + cached star count from the
GitHub API, 1h localStorage TTL) and "Help" button (opens project
Discussions). Both link to the Runfusion/Fusion repo.
- Star button auto-hides after the user clicks it (intent = star),
tracked in localStorage `fusion:github-star-clicked`.
- Settings → General gets a "Show Star on GitHub button" checkbox so
users can hide it preemptively. New global setting
`showGitHubStarButton: boolean` (default true) gates rendering.
More resizable modals
- Task Detail modal: 85vh default, resize: both, persisted via
useModalResizePersist (key `fusion:task-detail-modal-size`).
- Quick Chat FAB: full 8-direction resize (4 corners + 4 edges) via
pointer-event handlers; persisted to
`fusion:quick-chat-size-<projectId>`. Each handle has the right
cursor + role="separator" for accessibility.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the worktree-recycle pool reassigned a path to a new task, the old
task's diff endpoints kept reading the new task's branch state — surfacing
unrelated commits as the original task's "files changed" list.
- Clear task.worktree/branch in the merger after the worktree is released
to the pool or removed, so the path no longer points anywhere.
- Validate the worktree's current branch matches task.branch in the three
worktree-backed diff endpoints; on mismatch return empty rather than
diffing against a foreign branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add global updateCheckEnabled setting to core schema/types and wire dashboard command to cache update checks in the CLI
- Implement dashboard server update-check cache module plus REST routes for status and refresh behavior
- Add dashboard client hook, legacy API helpers, and UpdateAvailableBanner UI to show cached CLI update notices
- Cover update-check server routes, hook behavior, banner rendering, and route registration with focused tests
- Document update-check configuration and API behavior in architecture and settings reference docs
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.
- 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
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>
- 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
- 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 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
- Add remote auth token primitives plus login-url generation for short-lived phone auth handoff
- Expose public handoff API endpoint and wire remote auth handling into dashboard server routes
- Tighten remote access settings update typing to satisfy typecheck and preserve API isolation behavior
- Add comprehensive dashboard tests for remote-auth helpers, route behavior, and server integration
- Document the remote login-url and phone auth handoff contract in architecture docs