Adds a persisted, optional per-step reasoning-effort (thinkingLevel) override for AI-capable schedule and routine automation steps, surfaced in the editors and validated at the route layer.
- Add optional AutomationStep.thinkingLevel field (packages/core/src/automation.ts), riding the existing JSON steps blob so no DB migration is needed; runtime application of the level is deferred to a follow-up.
- Validate thinkingLevel in dashboard route step validation against the shared THINKING_LEVELS set, rejecting unknown values (packages/dashboard/src/routes.ts).
- Add Thinking Level controls to RoutineEditor, ScheduleForm, and ScheduleStepsEditor so users can set/inherit the override per step.
- Extend core and dashboard test suites (automation-store, routine-store, RoutineEditor, ScheduleForm, ScheduleStepsEditor, routes-automation) to cover persistence, validation, and UI behavior.
- Update dashboard-guide.md docs and add a minor changeset for the new feature.
Files changed:
.changeset/fn-7900-automation-thinking-level.md | 7 +
docs/dashboard-guide.md | 3 +-
.../core/src/__tests__/automation-store.test.ts | 45 ++++++
packages/core/src/__tests__/routine-store.test.ts | 46 +++++++
packages/core/src/automation.ts | 9 ++
.../dashboard/app/components/RoutineEditor.tsx | 21 ++-
packages/dashboard/app/components/ScheduleForm.tsx | 28 +++-
.../app/components/ScheduleStepsEditor.tsx | 21 ++-
.../components/__tests__/RoutineEditor.test.tsx | 99 +++++++++++++-
.../app/components/__tests__/ScheduleForm.test.tsx | 137 +++++++++++++++++--
.../__tests__/ScheduleStepsEditor.test.tsx | 83 +++++++++--
.../src/__tests__/routes-automation.test.ts | 152 +++++++++++++++++++++
packages/dashboard/src/routes.ts | 10 ++
13 files changed, 622 insertions(+), 39 deletions(-)
Fusion-Task-Id: FN-7900
Fusion-Task-Lineage: 812a9a8c-ad0f-462f-b1c6-9900f70e4261
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a persisted Thinking Level (reasoning-effort) selector to manual insight generation, threading the selection through the dashboard API, insight run metadata, and retries.
- Add inline Thinking Level selector to the InsightsView model-config popover, persisted to localStorage (fusion-insight-thinking)
- Thread thinkingLevel through triggerInsightRun (legacy API client) and useInsights.runInsights
- Validate and store thinkingLevel in insight run inputMetadata.metadata on the POST /insights/run route; resolve it via resolvePlanningThinkingLevel for the actual generation call
- Recover and reapply the original run's thinkingLevel on retry (retryInsightRunLifecycle) so retries reuse the same reasoning-effort setting
- Export resolvePlanningThinkingLevel from @fusion/engine
- Document the new Thinking Level selector in docs/dashboard-guide.md
- Add a minor changeset for @runfusion/fusion
Files changed:
.changeset/fn-7901-insight-thinking-level.md | 7 ++
docs/dashboard-guide.md | 1 +
.../app/__tests__/insight-model-selector.test.tsx | 41 ++++++++++-
packages/dashboard/app/api/legacy.ts | 2 +
packages/dashboard/app/components/InsightsView.tsx | 24 +++++-
.../app/hooks/__tests__/useInsights.test.ts | 36 ++++++++-
packages/dashboard/app/hooks/useInsights.ts | 6 +-
.../src/__tests__/insights-routes.test.ts | 86 ++++++++++++++++++++++
packages/dashboard/src/insights-routes.ts | 36 ++++++++-
packages/engine/src/index.ts | 1 +
10 files changed, 227 insertions(+), 13 deletions(-)
Fusion-Task-Id: FN-7901
Fusion-Task-Lineage: a6249526-e97d-403e-b853-e497d16f425b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Bring thinking-level (reasoning effort) editing to every remaining model selector surface that previously lacked it, so operators can set it consistently from Agent Detail, Agent Onboarding, and the List view's bulk task editor, in addition to the batch-update-models API and route that back them.
- Agent Detail config tab: persist/edit a built-in agent's runtimeConfig.thinkingLevel inline via the shared model dropdown, with dirty-state and reset tracking.
- Agent Onboarding modal: replace the read-only thinking-level input with an editable control wired into the same model dropdown used for creation.
- List view bulk edit toolbar: add a "no change" / "use default" / explicit-level thinking selector alongside executor/reviewer model and node overrides, wired through to the bulk apply action.
- Dashboard API client (`batchUpdateTaskModels`) and `/api/tasks/batch-update-models` route: accept and validate an optional `thinkingLevel` field (against `THINKING_LEVELS`), applying it per task alongside existing model/node updates.
- Update dashboard-guide.md docs and add regression tests across AgentDetailView, AgentOnboardingModal, ListView, and the batch-update-models route.
- Add a minor changeset documenting the feature for release notes.
Files changed:
.changeset/thinking-level-selector-parity.md | 7 ++
docs/dashboard-guide.md | 5 +-
packages/dashboard/app/api/legacy.ts | 3 +
.../dashboard/app/components/AgentDetailView.tsx | 20 +++++-
.../app/components/AgentOnboardingModal.tsx | 11 +++-
packages/dashboard/app/components/ListView.tsx | 54 +++++++++++++---
.../__tests__/AgentDetailView.settings.test.tsx | 45 +++++++++++++
.../__tests__/AgentDetailView.test-helpers.ts | 19 +++++-
.../__tests__/AgentOnboardingModal.test.tsx | 41 +++++++++++-
.../app/components/__tests__/ListView.test.tsx | 43 ++++++++++++-
.../src/__tests__/routes-tasks-ops.test.ts | 75 ++++++++++++++++++++++
.../src/routes/register-task-workflow-routes.ts | 22 +++++--
12 files changed, 323 insertions(+), 22 deletions(-)
Fusion-Task-Id: FN-7899
Fusion-Task-Lineage: fd584ce4-42b3-4c20-8de5-4d3c8963f593
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a best-effort, idempotent dashboard inbox notice announcing the upcoming embedded-Postgres storage migration, delivered once per project on the first engine start under the Fusion 0.59.x release line.
- New `deliverPostgresMigrationNoticeIfNeeded` in `@fusion/engine` (`postgres-migration-notice.ts`) builds and sends a `system` -> `user` inbox message via `MessageStore`, gated to version `0.59.x` by `isPostgresMigrationNoticeVersion`
- Idempotency via existing inbox message `metadata.kind = "postgres-migration-notice"` marker (no new settings key or table), so restarts never duplicate the notice
- Delivery is fully best-effort: any `MessageStore` failure is caught, logged as a warning, and never blocks or fails `ProjectEngine.start()`
- `ProjectEngine.start()` invokes the notice after runtime start, using an injected `cliPackageVersion` threaded from the CLI layer through `EngineManagerOptions` / `ProjectEngineOptions` so the engine never imports CLI/dashboard code directly
- `daemon.ts`, `dashboard.ts`, and `serve.ts` resolve the published `@runfusion/fusion` version via `getCliPackageVersion` / `isUnresolvedCliPackageVersion` and pass it into `ProjectEngineManager`
- Exported new symbols (`POSTGRES_MIGRATION_HELP_URL`, `POSTGRES_MIGRATION_NOTICE_KIND`, `deliverPostgresMigrationNoticeIfNeeded`, `isPostgresMigrationNoticeVersion`, related types) from `@fusion/engine`, and `isUnresolvedCliPackageVersion` from `@fusion/dashboard`
- New unit tests covering version matching and single-delivery/idempotency behavior
- Docs updated (`docs/agents.md`, `docs/dashboard-guide.md`) to describe the one-time notice and its dedup key
- Changeset added for `@runfusion/fusion` (minor, feature)
Files changed:
.changeset/fn-7879-postgres-migration-inbox-notice.md | 7 ++
docs/agents.md | 1 +
docs/dashboard-guide.md | 1 +
packages/cli/src/commands/daemon.ts | 6 +-
packages/cli/src/commands/dashboard.ts | 5 +
packages/cli/src/commands/serve.ts | 6 +-
packages/dashboard/src/index.ts | 2 +-
packages/engine/src/__tests__/postgres-migration-notice.test.ts | 140 +++++++++++++++++++++
packages/engine/src/index.ts | 9 ++
packages/engine/src/postgres-migration-notice.ts | 107 ++++++++++++++++
packages/engine/src/project-engine-manager.ts | 6 +
packages/engine/src/project-engine.ts | 12 ++
12 files changed, 299 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7879
Fusion-Task-Lineage: 201877e5-6bdc-4168-a8ac-ae0e50ec8308
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Durable agents were parking as error-unrecoverable on any non-transient-pattern failure, even generic/unknown blips that manual Retry immediately fixed; this changes the default to recoverable and reserves immediate unrecoverable parking for operator-actionable errors.
- isHeartbeatErrorRecoverable now returns true unless the error is operator-actionable (auth/model/billing/scope) or a stale worktree/module-resolution error, instead of requiring a transient-pattern match via classifyError
- Add OAuth scope-requirement and insufficient-scope patterns to the operator-actionable error detector so those still park immediately
- Update heartbeat-error-recovery, heartbeat-executor, self-healing, and transient-error-detector tests to cover the new default-recoverable behavior
- Update AGENTS.md and docs/architecture.md durable-agent error recovery notes to describe the new recoverable-by-default policy
- Add changeset documenting the fix
Files changed:
.changeset/fn-7878-recoverable-default.md | 7 ++
AGENTS.md | 2 +-
docs/architecture.md | 4 +-
.../src/__tests__/heartbeat-error-recovery.test.ts | 90 +++++++++++++++++++---
.../src/__tests__/heartbeat-executor.test.ts | 17 ++--
packages/engine/src/__tests__/self-healing.test.ts | 45 ++++++-----
.../src/__tests__/transient-error-detector.test.ts | 7 +-
packages/engine/src/agent-heartbeat.ts | 8 +-
packages/engine/src/transient-error-detector.ts | 2 +
9 files changed, 137 insertions(+), 45 deletions(-)
Fusion-Task-Id: FN-7878
Fusion-Task-Lineage: 6f929af9-ceef-404f-95c9-98f26478f020
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Extends the embedded Task Detail SessionTerminal to surface the shared, user-defined terminal shortcuts (from FN-7872's kb-terminal-preferences localStorage) as tappable buttons in its mobile accessory key bar.
- Read customShortcuts via the shared readTerminalPreferences() store on mount and refresh live on the storage event
- Render each custom shortcut as a mobile-only accessory-bar button that injects decodeTerminalShortcutSequence(value) through the focus-preserving keepFocus + sendInput path, clearing sticky Ctrl like the built-in ^C key
- Suppress the buttons for read-only/replay/idle/ended sessions via the existing canAcceptInput gate; desktop embedded terminals get no key bar
- Add .cli-terminal-key--custom styling for the new buttons
- Update docs/dashboard-guide.md to describe the mobile custom-shortcut key bar behavior
- Add a minor changeset for @runfusion/fusion documenting the feature
- Add SessionTerminal.mobile.test.tsx coverage for rendering, injection, live updates, and read-only suppression
Files changed:
.changeset/fn-7876-session-terminal-custom-shortcuts.md | 7 ++
docs/dashboard-guide.md | 2 +-
packages/dashboard/app/components/SessionTerminal.css | 5 ++
packages/dashboard/app/components/SessionTerminal.tsx | 31 +++++++
packages/dashboard/app/components/__tests__/SessionTerminal.mobile.test.tsx | 94 ++++++++++++++++++++++
5 files changed, 138 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7876
Fusion-Task-Lineage: 61d62ad2-7357-4eb4-b542-6e17deea1e5e
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a per-project Hide done / Show done toggle to the Todo list items header so operators can declutter long selected lists while completion counts still reflect all items.
- Add hideDone state persisted per project via localStorage (kb-dashboard-todo-hide-done key registered in projectStorage)
- Filter rendered todo items to hide completed ones when the toggle is active, while keeping list stats/progress counts based on all items
- Adjust up/down item reordering to operate correctly against the visible (filtered) list while still reordering the underlying full item list
- Add an empty-state message when all items are hidden by the toggle, with Eye/EyeOff icon + i18n strings (todo.hideDone, todo.showDone, todo.allDoneHidden)
- Add regression tests covering the toggle, persistence, filtering, and empty state
- Document the Hide done / Show done control in the dashboard guide
Files changed:
docs/dashboard-guide.md | 3 +
packages/dashboard/app/components/TodoView.css | 45 ++++++++++
packages/dashboard/app/components/TodoView.tsx | 66 +++++++++++++--
.../app/components/__tests__/TodoView.test.tsx | 99 ++++++++++++++++++++++
packages/dashboard/app/utils/projectStorage.ts | 1 +
packages/i18n/locales/en/app.json | 3 +
6 files changed, 210 insertions(+), 7 deletions(-)
Fusion-Task-Id: FN-7869
Fusion-Task-Lineage: 9c334aba-e802-43b8-963c-0f2daf727583
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a shared, persisted auto-save preference for workspace text-file editing, defaulted to on, surfaced as a toolbar toggle in both the Files modal and right-dock Files view.
- Add useAutoSavePreference hook: persists the fn-file-editor-auto-save localStorage preference, broadcasts same-window changes via a custom event (storage events only reach other documents), and defaults to true.
- Extend useWorkspaceFileEditor with an autoSave flag that debounces (800ms) and triggers save() for a loaded, editable file with real pending changes, keyed by workspace+file+content to avoid re-firing on failed writes.
- Add an Auto-save toggle button to FileEditor's toolbar (autoSaveEnabled/onToggleAutoSave/canToggleAutoSave props), hidden for read-only/preview/binary files.
- Wire the shared preference into FileBrowserModal and DockFilesView, disabling auto-save for binary files in the modal.
- Add fileEditor.autoSave / fileEditor.toggleAutoSave i18n strings and document the new default behavior in docs/dashboard-guide.md.
- Add/extend tests covering the new hook, debounced auto-save behavior, and toolbar toggle wiring across FileEditor, FileBrowserModal, and DockFilesView.
Files changed:
docs/dashboard-guide.md | 3 +
.../dashboard/app/components/DockFilesView.tsx | 6 +-
.../dashboard/app/components/FileBrowserModal.tsx | 20 ++--
packages/dashboard/app/components/FileEditor.tsx | 17 +++-
.../components/__tests__/DockFilesView.test.tsx | 37 ++++++-
.../components/__tests__/FileBrowserModal.test.tsx | 86 +++++++++++++---
.../app/components/__tests__/FileEditor.test.tsx | 56 +++++++++++
.../hooks/__tests__/useAutoSavePreference.test.ts | 66 +++++++++++++
.../hooks/__tests__/useWorkspaceFileEditor.test.ts | 108 +++++++++++++++++++++
.../dashboard/app/hooks/useAutoSavePreference.ts | 79 +++++++++++++++
.../dashboard/app/hooks/useWorkspaceFileEditor.ts | 47 ++++++++-
packages/i18n/locales/en/app.json | 2 +
12 files changed, 500 insertions(+), 27 deletions(-)
Fusion-Task-Id: FN-7866
Fusion-Task-Lineage: 0604de18-666d-4872-abce-2a3886c9ea55
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Plugin-contributed skills previously registered only a name for sessions and the dashboard, so their SKILL.md bodies were never actually loaded — fix threads real body paths through to both session creation and the Skills UI.
- Resolve each enabled plugin skill's body path via @fusion/core's resolvePluginSkillBodyPath and thread its body dir (plus parent dir) into every session-creating lane (executor primary/retry/verification-fix/step/child-agent, triage, reviewer, merger, agent-heartbeat, cron-runner) as additionalSkillPaths, unioned with existing CE skill dirs.
- Add collectPluginSkillNames/mergePluginSkills additionalSkillPaths plumbing in session-skill-context.ts so plugin skill discovery paths flow the same way as native/role-fallback skills.
- Update dashboard skills-adapter.ts to read plugin skill SKILL.md and reference files from disk (via the traversal-guarded reader) instead of returning a runtime-placeholder/"not found" response for plugin-sourced skills.
- Document the plugin skill body delivery mechanism in docs/PLUGIN_AUTHORING.md.
- Add regression coverage: plugin-skill-body-delivery.test.ts, expanded session-skill-context.test.ts and skills-adapter.test.ts.
- Add changeset fn-7857-plugin-skill-body-delivery.md (minor, fix).
Files changed:
.changeset/fn-7857-plugin-skill-body-delivery.md | 7 ++
docs/PLUGIN_AUTHORING.md | 3 +
.../dashboard/src/__tests__/skills-adapter.test.ts | 92 ++++++++++++++++------
packages/dashboard/src/skills-adapter.ts | 33 ++------
.../__tests__/plugin-skill-body-delivery.test.ts | 75 ++++++++++++++++++
.../src/__tests__/session-skill-context.test.ts | 84 +++++++++++++++++++-
packages/engine/src/agent-heartbeat.ts | 3 +-
packages/engine/src/cron-runner.ts | 2 +
packages/engine/src/executor.ts | 25 ++++--
packages/engine/src/merger.ts | 10 ++-
packages/engine/src/reviewer.ts | 2 +
packages/engine/src/session-skill-context.ts | 43 ++++++++--
packages/engine/src/step-session-executor.ts | 5 +-
packages/engine/src/triage.ts | 3 +-
14 files changed, 318 insertions(+), 69 deletions(-)
Fusion-Task-Id: FN-7857
Fusion-Task-Lineage: 9ba4c305-8b38-4ae8-85b3-4c87205ef767
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes the artifact viewer FloatingWindow (image/video/PDF/document) so it opens as a full-screen sheet on mobile instead of a small draggable/resizable desktop-style window.
- Add a mobile-breakpoint override for .artifacts-gallery-window that clamps the FloatingWindow to inset:0/100vw/100dvh with no border/radius/shadow
- Hide the FloatingWindow resize handle and disable header drag cursor/touch-action on mobile so the sheet can't be dragged or resized like the desktop window
- Add a CSS-contract regression test (ArtifactsGallery.css.test.ts) asserting the mobile sheet rules exist while desktop keeps the header drag affordance
- Update dashboard-guide.md docs to describe desktop draggable/resizable behavior vs. mobile full-screen sheet behavior
- Add a patch changeset for @runfusion/fusion documenting the fix
Files changed:
.changeset/fn-7865-artifact-viewer-mobile-sheet.md | 7 +++
docs/dashboard-guide.md | 5 +-
.../dashboard/app/components/ArtifactsGallery.css | 28 +++++++++-
.../__tests__/ArtifactsGallery.css.test.ts | 63 ++++++++++++++++++++++
4 files changed, 100 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7865
Fusion-Task-Lineage: 0b5dfaee-3f7b-4a2e-968c-324182ae7953
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Artifact-registration mailbox notifications now render a shared inline preview and open-artifact link instead of plain text metadata.
- Add MailboxArtifactAttachment component rendering an inline image/document preview plus an "open artifact" link from message.metadata (artifactId/artifactType/mimeType) via artifactMediaUrl
- Wire MailboxModal and MailboxView to render the new attachment for artifact-registered messages, with supporting CSS
- Emit metadata.mimeType from notifyArtifactRegistered in agent-tools.ts so mailbox surfaces can pick the right preview affordance without an extra artifact fetch
- Add/extend tests for the new component and for MailboxView/agent-artifact-tools coverage
- Update dashboard guide docs and add a changeset for the feature
Files changed:
.changeset/fn-7864-artifact-mail-link.md | 7 ++
docs/dashboard-guide.md | 2 +-
.../app/components/MailboxArtifactAttachment.tsx | 103 +++++++++++++++++++++
packages/dashboard/app/components/MailboxModal.css | 74 +++++++++++++++
packages/dashboard/app/components/MailboxModal.tsx | 15 +++
packages/dashboard/app/components/MailboxView.tsx | 15 +++
.../__tests__/MailboxArtifactAttachment.test.tsx | 65 +++++++++++++
.../app/components/__tests__/MailboxView.test.tsx | 93 +++++++++++++++++++
.../src/__tests__/agent-artifact-tools.test.ts | 32 ++++++-
packages/engine/src/agent-tools.ts | 5 +
10 files changed, 409 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7864
Fusion-Task-Lineage: a6502e18-5f7f-4c67-80fb-a709e4a52c50
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Bounds the execute->pause-abort->todo dispatch loop so a task can no longer requeue forever with no visible signal or terminal state.
- Track a progress-anchored `executeRequeueLoopCount`/`executeRequeueLoopSignature` pair on the task row (current step + step statuses) so slow no-progress requeue cycles are counted independently of the scheduler's wall-clock `dispatchStormCount` guard.
- Warn visibly in the task log at `EXECUTE_REQUEUE_LOOP_VISIBLE_THRESHOLD` (3) and terminalize non-paused, non-terminal tasks at `MAX_EXECUTE_REQUEUE_LOOP_CYCLES` (6) with `status:"failed"` and an `EXECUTION_DISPATCH_LOOP_EXHAUSTED:` error, preserving worktree/branch/step progress.
- Emit a new `task:execution-dispatch-loop-terminalized` run-audit mutation type with ids/counts/outcomes-only metadata.
- Reset the loop counters on real progress, manual retry, forward moves (in-review/done/archived), and unpause, in both the executor and scheduler.
- Add DB migration 142 (`executeRequeueLoopCount`, `executeRequeueLoopSignature` columns) plus store read/write/reset plumbing.
- Add reliability-interactions coverage for the new loop guard and extend store-persistence tests for the new columns.
- Document the new behavior in AGENTS.md and docs/architecture.md.
Files changed:
AGENTS.md | 1 +
docs/architecture.md | 2 +
packages/core/src/__tests__/store-persistence.test.ts | 45 +++++
packages/core/src/db.ts | 17 +-
packages/core/src/manual-retry-reset.ts | 1 +
packages/core/src/store.ts | 22 ++-
packages/core/src/types.ts | 11 ++
.../execute-requeue-loop-guard.test.ts | 188 +++++++++++++++
packages/engine/src/executor.ts | 67 +++++++-
packages/engine/src/run-audit.ts | 2 +
packages/engine/src/scheduler.ts | 8 +-
11 files changed, 355 insertions(+), 9 deletions(-)
Fusion-Task-Id: FN-7863
Fusion-Task-Lineage: db40507f-5851-435e-8854-c1ed695b4154
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Debug org agents error state recovery regression: durable heartbeat-managed
agents with a non-recoverable error (permanent/credential/model-access/
config, not stale-worktree/module-resolution) were previously left
indefinitely in bare `state:"error"` with no operator-visible reason,
and CLI agent inspection tools did not surface error/pause diagnostics.
- Timer path (`HeartbeatMonitor`) and run-entry recovery now classify
non-recoverable durable heartbeat errors and park the agent `paused`
with `pauseReason:"error-unrecoverable"` instead of restart-looping or
sitting in `error` forever.
- `SelfHealingManager` mirrors the same non-recoverable classification in
its recovery sweep, parking with the same reason/metadata and skipping
the exhausted/next-retry gates for that terminal bucket.
- New `agent:error-parked-unrecoverable` run-audit event type emitted by
both the heartbeat and self-healing paths (ids/counts/outcomes-only
metadata).
- `fn_agent_show` now prints `Last Error`, `Pause Reason`, and a compact
`Error Recovery` counter line; `fn_list_agents` prints the same
diagnostics only for agents currently in `error`/`paused`.
- Updated `AGENTS.md`, `docs/agents.md`, and `docs/architecture.md` to
document the new terminal-park behavior and CLI diagnostics surface.
- Added a changeset (`@runfusion/fusion` patch) describing the
operator-facing fix.
Files changed:
.changeset/fn-7859-org-agent-error-diagnostics.md | 7 ++
AGENTS.md | 2 +-
docs/agents.md | 3 +-
docs/architecture.md | 4 +-
packages/cli/src/__tests__/extension.test.ts | 68 ++++++++++++++++
packages/cli/src/extension.ts | 64 +++++++++++++++
.../src/__tests__/heartbeat-error-recovery.test.ts | 47 ++++++++++-
packages/engine/src/__tests__/self-healing.test.ts | 94 ++++++++++++++++++----
packages/engine/src/agent-heartbeat.ts | 71 +++++++++++++++-
packages/engine/src/run-audit.ts | 1 +
packages/engine/src/self-healing.ts | 46 +++++++++--
11 files changed, 375 insertions(+), 32 deletions(-)
Fusion-Task-Id: FN-7859
Fusion-Task-Lineage: 09b2035d-e8a0-438f-b1ab-1b0048b35c76
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fix useChat so already-rendered user/assistant messages no longer flicker away while an agent turn is actively streaming.
- useChat.ts: during an active streaming turn for the current session, treat stale/empty/cross-session loadMessages responses as append-only against the visible thread instead of replacing it, merging any genuinely new same-session messages in and skipping the session-cache write when the active thread is being preserved.
- ChatView.streaming-thread.test.tsx: add coverage asserting the rendered thread stays visible across mid-turn session-update/tool-call/stale-reload churn.
- useChat.test.ts: add hook-level regression tests for the append-only/merge/cache-skip behavior during active streaming.
- docs/architecture.md, docs/dashboard-guide.md: document the append-only mid-turn thread-stability behavior.
- Add changeset (patch) for @runfusion/fusion describing the user-facing fix.
Files changed:
.../fn-7853-chat-mid-turn-message-stability.md | 7 +
docs/architecture.md | 1 +
docs/dashboard-guide.md | 1 +
.../__tests__/ChatView.streaming-thread.test.tsx | 130 +++++++++++++
.../dashboard/app/hooks/__tests__/useChat.test.ts | 208 +++++++++++++++++++++
packages/dashboard/app/hooks/useChat.ts | 35 +++-
6 files changed, 380 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7853
Fusion-Task-Lineage: d9909469-082c-4eeb-81fb-b36d1a9e4705
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Session skill merging (collectPluginSkillNames) previously ignored per-project
Skills view enable/disable toggles and only consulted each plugin's static
default, so a user disabling a plugin skill in the Skills view would still see
it merged into live agent sessions. Extracted the effective-enablement
resolver shared by dashboard discovery and engine session assembly into
@fusion/core so both surfaces stay in sync.
- Added packages/core/src/skill-settings.ts with computeSkillId/parseSkillId/
normalizeStoredSkillPath/getSkillSettingState/resolvePluginSkillEnabled,
exported from @fusion/core's index.
- packages/dashboard/src/skills-adapter.ts now re-exports and delegates to the
shared @fusion/core resolver instead of duplicating its own
getSkillSettingState/computeSkillId/parseSkillId implementations.
- packages/engine/src/session-skill-context.ts: collectPluginSkillNames now
accepts a projectRootDir, reads project settings via skill-resolver's newly
exported readProjectSettings/resolveProjectRoot, and calls
resolvePluginSkillEnabled instead of only checking the plugin's static
skill.enabled flag; mergePluginSkills passes projectRootDir through.
- packages/engine/src/skill-resolver.ts: exported readProjectSettings and
ProjectSkillSettings for reuse by session-skill-context.
- Updated docs/plugin-management.md to document that per-project Skills view
toggles now apply to runtime agent sessions, not just discovery.
- Added unit tests for the new core resolver and updated dashboard/engine
tests to cover per-project toggle overrides in session merging.
- Added a patch changeset for @runfusion/fusion.
Files changed:
.changeset/fn-7858-plugin-skill-session-toggle.md | 7 ++
docs/plugin-management.md | 4 +-
packages/core/src/__tests__/skill-settings.test.ts | 62 +++++++++
packages/core/src/index.ts | 8 ++
packages/core/src/skill-settings.ts | 102 +++++++++++++++
.../dashboard/src/__tests__/skills-adapter.test.ts | 60 ++++++++-
packages/dashboard/src/skills-adapter.ts | 107 +++-------------
.../src/__tests__/session-skill-context.test.ts | 140 ++++++++++++++++++++-
packages/engine/src/session-skill-context.ts | 23 +++-
packages/engine/src/skill-resolver.ts | 4 +-
10 files changed, 409 insertions(+), 108 deletions(-)
Fusion-Task-Id: FN-7858
Fusion-Task-Lineage: 90e44d24-e385-4a74-b8e4-3c864ec39a95
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Path-registered plugin reload/restart now refreshes persisted manifest metadata instead of leaving stale version/settingsSchema in the store.
- PluginLoader.loadPlugin/reloadPlugin call a new refreshPersistedManifestMetadata helper after each fresh module import, generalizing the previously bundled-only refresh to path-registered plugins
- Refresh is metadata-only (version, settingsSchema) via a stable-JSON comparison, preserving per-project enablement and saved setting values, and is a no-op when nothing changed
- PluginStore.PluginUpdateInput/updatePlugin gain a settingsSchema field (undefined = unchanged, null = explicitly clear) so updatePlugin can persist manifest schema changes independently of setting values
- Docs: add a "Updating path-registered plugins" section to docs/PLUGIN_AUTHORING.md describing the new reload/refresh loop
- Tests: add coverage in plugin-loader.test.ts and plugin-store.test.ts for manifest metadata refresh on load/reload and settingsSchema persistence
- Add a patch changeset for @runfusion/fusion
Files changed:
.changeset/fn-7855-plugin-manifest-refresh.md | 7 +
docs/PLUGIN_AUTHORING.md | 10 ++
packages/core/src/__tests__/plugin-loader.test.ts | 177 ++++++++++++++++++++++
packages/core/src/__tests__/plugin-store.test.ts | 42 +++++
packages/core/src/plugin-loader.ts | 53 +++++++
packages/core/src/plugin-store.ts | 10 ++
6 files changed, 299 insertions(+)
Fusion-Task-Id: FN-7855
Fusion-Task-Lineage: f4d94023-5a27-4059-a7a5-61f524c171b8
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Issue #2015: product-code executor tasks were repeatedly routed to a
liaison-only agent because every routing path gated only on the coarse
role field, and several binding primitives had no guard at all.
- Add runtimeConfig.assignmentPolicy ("auto" | "explicit-only" | "none");
"none" can never be bound to implementation tasks by ANY path — no
override bypasses it (the liaison guarantee)
- Route every binding surface through one shared evaluator
(evaluateImplementationTaskBind): claimTaskForAgent, the previously
unguarded checkoutTask/assignTask primitives, selectNextTaskForAgent
(including the in-progress re-selection loop), scheduler auto-assign
pool, heartbeat inbox/auto-claim, fn_delegate_task, CLI agent-id
validation, and dashboard assign/checkout/inbox routes
- Lock project isolation with a regression test: a foreign-project
agent id is rejected by every binding primitive
- Expose Assignment Policy in Agent Detail settings; document in
docs/agents.md; add changeset
Fusion-Task-Id: FN-7851
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace one-shot grok -p JSON with native grok agent stdio (ACP) for realtime
streaming, tool visibility, and multi-turn sessions. Vendor the ACP client
into fusion-plugin-grok-runtime, forward Fusion fn_* tools and operator MCP,
stage Fusion skills via --plugin-dir, authenticate per xAI headless docs, and
align project chat manager store resolution so Grok chat sessions can send.
Extend the dashboard's Task Documents tab to union each task's registered documents with its task-scoped artifacts, with an inline right-pane viewer for the added artifact types.
- DocumentsView Task Documents tab now merges task documents and task-scoped artifacts per task group, sorted/grouped consistently
- Adds an inline right-pane artifact viewer (image/video/audio/pdf/inline-doc/other) reusing getArtifactCategory + artifactMediaUrl/fetchArtifact
- Selection state is a discriminated document|artifact union kept separate from Project Files selection; the standalone Artifacts gallery tab is unchanged
- Updates dashboard-guide.md to describe the merged Task Documents behavior (grouping, search, and preview now cover both documents and artifacts)
- Adds a minor changeset for @runfusion/fusion documenting the artifact-in-Task-Documents feature
- Expands DocumentsView test coverage for the new union/selection/preview behavior
Files changed:
.changeset/fn-7845-task-documents-artifacts.md | 7 +
docs/dashboard-guide.md | 6 +-
.../dashboard/app/components/DocumentsView.css | 142 +++++++-
.../dashboard/app/components/DocumentsView.tsx | 389 ++++++++++++++++++---
.../components/__tests__/DocumentsView.test.tsx | 290 ++++++++++++++-
5 files changed, 780 insertions(+), 54 deletions(-)
Fusion-Task-Id: FN-7845
Fusion-Task-Lineage: 32bbe6dc-4c02-449c-a13f-38abb8fd727d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Unifies the two independent durable-agent error-recovery paths (heartbeat timer and self-healing sweep) so they share one retry budget, eligibility check, and audit surface instead of racing separate counters.
- Share the heartbeatErrorRecovery attempt budget between HeartbeatMonitor's timer-entry recovery and SelfHealingManager.recoverOrphanedAgents(), with self-healing's legacy durableErrorRecovery metadata folded into the same counter via readHeartbeatErrorRetryCount().
- Add isHeartbeatErrorRecoverable() as the single transient/non-operator-actionable eligibility check, used by both the heartbeat timer and self-healing paths (self-healing additionally allows stale-worktree module-resolution errors).
- resetHeartbeatErrorRecoveryMetadata() now strips the legacy durableErrorRecovery field so recovered agents don't retain stale sweep bookkeeping.
- Self-healing emits the shared agent:auto-recover-error-state / agent:error-retry-exhausted run-audit events with source:"self-healing", and parks the agent paused with pauseReason:"error-retry-exhausted" on budget exhaustion, matching the heartbeat-timer behavior.
- Update AGENTS.md, docs/architecture.md, and docs/agents.md to describe the consolidated recovery budget and audit surface.
- Add a patch changeset documenting the fix for @runfusion/fusion.
Files changed:
.changeset/fn-7844-error-recovery-coordination.md | 7 ++
AGENTS.md | 2 +-
docs/agents.md | 14 ++--
docs/architecture.md | 2 +-
packages/engine/src/__tests__/heartbeat-error-recovery.test.ts | 13 +++-
packages/engine/src/__tests__/self-healing.test.ts | 58 ++++++++++++++-
packages/engine/src/agent-heartbeat.ts | 35 ++++++---
packages/engine/src/self-healing.ts | 85 ++++++++++++++++++----
8 files changed, 180 insertions(+), 36 deletions(-)
Fusion-Task-Id: FN-7844
Fusion-Task-Lineage: b70dcba5-56b6-412c-8be2-ef827bee9964
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Heartbeat-managed durable agents that land in state:"error" now self-recover on the next heartbeat instead of staying stuck until an operator intervenes.
- HeartbeatTriggerScheduler keeps timers armed for durable heartbeat-managed agents in error state when the last error is transient and not operator-actionable (credential/quota/model-access/permanent-config failures stay parked).
- executeHeartbeat clears recoverable errors at run entry (error → active, clears lastError), bounded by MAX_HEARTBEAT_ERROR_RECOVERY_ATTEMPTS (settings-overridable); a successful run resets the counter.
- On budget exhaustion, the agent is parked paused with pauseReason:"error-retry-exhausted".
- Emits new run-audit events agent:auto-recover-error-state and agent:error-retry-exhausted (added to DatabaseMutationType).
- Adds heartbeat-error-recovery.test.ts and extends heartbeat-scheduler.test.ts to cover the recovery/exhaustion paths.
- Adds changeset and documents the new behavior in AGENTS.md and docs/architecture.md.
Files changed:
.changeset/fn-7835-agent-error-auto-recovery.md | 7 +
AGENTS.md | 1 +
docs/architecture.md | 2 +
.../src/__tests__/heartbeat-error-recovery.test.ts | 323 +++++++++++++++++++++
.../src/__tests__/heartbeat-scheduler.test.ts | 89 +++++-
packages/engine/src/agent-heartbeat.ts | 209 ++++++++++++-
packages/engine/src/run-audit.ts | 2 +
7 files changed, 618 insertions(+), 15 deletions(-)
Fusion-Task-Id: FN-7835
Fusion-Task-Lineage: 1bbb28a3-8eb9-40e3-8177-6658ec5dae40
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a shared urgency color source to priorityIndicator and wires it into every priority-glyph surface so low/normal/high/urgent read consistently by color, not just icon shape.
- priorityIndicator.tsx: add colorVar (low=info/blue, normal=muted, high=warning/amber, urgent=error/red) and new getPriorityColorVar() export as the single source of truth
- QuickEntryBox: tint the icon-only priority trigger and each option row in the priority picker with the matching urgency color
- TaskForm: tint the New Task inline priority glyph using the same color source
- TaskCard: render a colored priority glyph alongside the existing text label in the card-priority-badge (kept the non-normal visibility gate and badge geometry via a small CSS gap addition)
- Updated/added tests for QuickEntryBox, TaskCard (badge, badge-height, badge-wrap), and priorityIndicator to assert the new colors; added a patch changeset and a dashboard-guide.md doc update
Files changed:
.changeset/fn-7842-priority-color-coding.md | 7 +++++
docs/dashboard-guide.md | 7 +++--
.../dashboard/app/components/QuickEntryBox.tsx | 7 +++--
packages/dashboard/app/components/TaskCard.css | 2 ++
packages/dashboard/app/components/TaskCard.tsx | 6 +++-
packages/dashboard/app/components/TaskForm.tsx | 7 +++--
.../components/__tests__/QuickEntryBox.test.tsx | 17 +++++++++--
.../__tests__/TaskCard.badge-height.test.tsx | 5 ++++
.../__tests__/TaskCard.badge-wrap.test.tsx | 5 ++++
.../app/components/__tests__/TaskCard.test.tsx | 33 +++++++++++++++++++++-
.../app/utils/__tests__/priorityIndicator.test.tsx | 13 +++++----
packages/dashboard/app/utils/priorityIndicator.tsx | 16 ++++++++---
12 files changed, 103 insertions(+), 22 deletions(-)
Fusion-Task-Id: FN-7842
Fusion-Task-Lineage: 0b2d0d15-6e61-45fc-9378-bc09002bef55
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Raises Grok and Droid CLI cold-start timeout defaults from 60s to 120s and lets operators override them via environment variables.
- Grok runtime adapter: new GROK_CLI_FIRST_OUTPUT_TIMEOUT_MS env override for the first-output cold-start guard; default raised 60000ms → 120000ms; invalid/non-positive values fall back to the default
- Droid provider: new PI_DROID_CLI_FIRST_LINE_TIMEOUT_MS env override for the first-line cold-start guard; default raised 60000ms → 120000ms; invalid/non-positive values fall back to the default
- 30-minute inactivity safety net left unchanged on both adapters
- Added/updated unit tests covering the new env-driven timeout resolution and fallback behavior for both plugins
- Documented the new settings in docs/settings-reference.md and both plugin READMEs
- Added a minor changeset for @runfusion/fusion
Files changed:
.changeset/fn-7838-cli-timeout-configurable.md | 7 ++
docs/settings-reference.md | 13 ++-
plugins/fusion-plugin-droid-runtime/README.md | 8 ++
.../src/__tests__/provider.test.ts | 97 +++++++++++++++++++++-
.../fusion-plugin-droid-runtime/src/provider.ts | 26 ++++--
plugins/fusion-plugin-grok-runtime/README.md | 8 ++
.../src/__tests__/runtime-adapter.test.ts | 52 +++++++++++-
.../src/runtime-adapter.ts | 23 ++++-
8 files changed, 222 insertions(+), 12 deletions(-)
Fusion-Task-Id: FN-7838
Fusion-Task-Lineage: 7aad4470-b28f-42bd-be01-8363a1dd05e5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Gives the Artifacts view's Task Documents sidebar a distinct card-per-task look with more breathing room between task groups, replacing the flat bordered-row list.
- Restyle .documents-task-sidebar-group into a bordered, rounded card with shadow and inter-group gap instead of a bottom-border-only divider
- Strengthen the group header (bolder task id/title, tinted background) so it reads as a container rather than a peer row
- Add hover/selected background treatment and left-indent to .documents-task-document-item entries, scoped under .documents-task-documents-sidebar so Project Files and Artifacts stay unaffected
- Add a regression test asserting group headers remain non-selectable containers distinct from selectable document rows
- Update dashboard-guide.md wording to describe the new distinct task-card grouping
- Add changeset (patch) documenting the sidebar restyle
Files changed:
.changeset/fn-7834-task-documents-grouping.md | 7 +++
docs/dashboard-guide.md | 2 +-
.../dashboard/app/components/DocumentsView.css | 38 +++++++++++++---
.../components/__tests__/DocumentsView.test.tsx | 50 ++++++++++++++++++++++
4 files changed, 90 insertions(+), 7 deletions(-)
Fusion-Task-Id: FN-7834
Fusion-Task-Lineage: 9bac7666-19ba-410b-81dc-d9201a61d0b5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Reworks the Task Detail terminal experience: the embedded Terminal tab now sits between Comments and Cost, its workspace picker defaults to the task's worktree, and the mobile terminal panel is shorter.
- Move the Terminal tab in Task Detail's tab strip to sit right after Comments and before Cost (previously Cost was earlier and Terminal was near the Session tab)
- TerminalModal now defaults its workspace picker to the useWorkspaces entry whose worktree matches the passed defaultCwd, but only until the operator manually changes the selection; the footer/global terminal still defaults to Project Root since it doesn't pass defaultCwd
- Reduce `.detail-section--worktree-terminal`'s mobile min-height from min(65dvh, 14 * --space-2xl) to min(50dvh, 11 * --space-2xl) so tab context and controls stay reachable above the fold
- Update docs/dashboard-guide.md to describe the new Comments → Terminal → Cost tab order and the worktree-matching picker default
- Add regression tests covering the new tab order and the default terminal workspace selection behavior
- Add a patch changeset describing the user-facing change
Files changed:
.changeset/FN-7832-task-terminal-picker-and-tab-order.md | 7 +++
docs/dashboard-guide.md | 9 +--
packages/dashboard/app/components/TaskDetailModal.css | 5 +-
packages/dashboard/app/components/TaskDetailModal.tsx | 30 +++++-----
packages/dashboard/app/components/TerminalModal.tsx | 19 +++++++
packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx | 15 ++---
packages/dashboard/app/components/__tests__/TaskDetailModal.worktree-terminal.test.tsx | 12 ++++
packages/dashboard/app/components/__tests__/TerminalModal.test.tsx | 64 ++++++++++++++++++++++
8 files changed, 134 insertions(+), 27 deletions(-)
Fusion-Task-Id: FN-7832
Fusion-Task-Lineage: 4ee67a65-8564-49c9-b93c-8c3eab05c073
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Relocates the shared terminal action-control cluster into the bottom status-bar footer at every breakpoint and replaces the fixed tablet-viewport tab collapse with a container-width overflow check.
- Move font-size, Clear, Shortcuts, Preferences, connection status, pin, and pop-out controls out of the desktop header into the `.terminal-status-bar` footer at all widths (desktop/tablet/mobile/floating/docked/pinned/embedded), replacing the prior viewport-tier-based split.
- Replace `isTerminalTabletViewport()` with `evaluateTabsOverflow()`, a container-size (ResizeObserver-driven) check with hysteresis that swaps the `.terminal-tabs` strip for the existing mobile-style `.terminal-mobile-tabs` dropdown whenever the tab strip doesn't fit, independent of viewport breakpoint.
- Update TerminalModal.css to match the new footer-first layout and drop now-unused tablet-tier header rules.
- Update dashboard-guide.md terminal usage steps to describe the new always-footer control location and the width-based (not tablet-only) tab dropdown fallback.
- Expand TerminalModal.test.tsx coverage for the overflow-driven tab collapse/expand behavior and footer control placement.
- Add changeset fn-7829-terminal-shortcuts-footer.md (patch, feature) documenting the footer/tab-dropdown change; resolve an add/add conflict on the pre-existing artifacts-doc-editing-and-comment-fix.md changeset by keeping fusion/fn-7829's summary wording.
Files changed:
.../artifacts-doc-editing-and-comment-fix.md | 2 +-
.changeset/fn-7829-terminal-shortcuts-footer.md | 7 +
docs/dashboard-guide.md | 9 +-
.../dashboard/app/components/TerminalModal.css | 121 +++-----
.../dashboard/app/components/TerminalModal.tsx | 336 +++++++++++----------
.../components/__tests__/TerminalModal.test.tsx | 185 ++++++++----
6 files changed, 351 insertions(+), 309 deletions(-)
Fusion-Task-Id: FN-7829
Fusion-Task-Lineage: 443c6e69-8946-4307-8a1a-f4f90681a054
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
- scope session collections to the active project\n- clean up runtime stage registration in discovery tests\n- model brainstorms and plans as repeatable artifact collections\n- compact the singleton Strategy presentation
Terminal WebSocket sessions now retry with capped backoff through cold-start failures instead of giving up and requiring a manual Reconnect click.
- useTerminal tracks whether a socket has ever successfully opened via hasEverConnectedRef
- a never-connected initial connect ignores MAX_RECONNECT_ATTEMPTS and keeps retrying at capped backoff, staying in the reconnecting affordance until it opens
- mid-session drops (sockets that opened at least once) keep the existing bounded give-up behavior, and permanent 4000/4004 closes remain terminal
- context-change invalidation now uses a ref flag (contextChangedSinceLastEffectRef) consumed inside the effect instead of a transient boolean dependency, avoiding cleanup re-runs that tore down the replacement socket during context-switch/reconnect races
- manual reconnect() and context/session changes reset hasEverConnectedRef so cold-start behavior reapplies per session
- added a patch changeset and expanded useTerminal test coverage for first-launch reconnect vs. mid-session disconnect behavior
- documented the first-launch reconnect behavior in docs/dashboard-guide.md
Files changed:
.changeset/FN-7824-terminal-first-launch-autoreconnect.md | 7 +
docs/dashboard-guide.md | 3 +
packages/dashboard/app/hooks/__tests__/useTerminal.test.ts | 208 ++++++++++++++++++++-
packages/dashboard/app/hooks/useTerminal.ts | 34 +++-
4 files changed, 234 insertions(+), 18 deletions(-)
Fusion-Task-Id: FN-7824
Fusion-Task-Lineage: 7ed696d0-449e-4dc0-9be0-48b429b8c844
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Removes the hard divider between the Settings navigation rail and content, keeps section rows single-line with ellipsis overflow, and adds a draggable/keyboard-resizable handle that persists the rail's width in localStorage across the standalone modal and embedded Settings page.
- Add a resize handle (.settings-nav-resize-handle) between .settings-navigation and .settings-content, draggable via pointer events and resizable with ArrowLeft/ArrowRight when focused
- Persist chosen width to localStorage (fusion:settings-nav-width), clamped between 200px and 420px, defaulting to 248px; restore on mount
- Make .settings-navigation the sole owner of rail width via a --settings-nav-width CSS custom property instead of a fixed width, and drop the border-right divider
- Keep nav section labels on one line with white-space: nowrap + text-overflow: ellipsis in both the modal (SettingsModal.css) and embedded (styles.css) nav item styles
- Hide the resize handle on mobile; mobile keeps the stacked section picker unaffected
- Update docs/dashboard-guide.md to describe the new divider-less rail and resize behavior
- Add SettingsModal.navResize.test.tsx covering drag-resize, keyboard-resize, and width persistence
- Add changeset .changeset/fn-7825-settings-nav-resizable.md (minor)
Files changed:
.changeset/fn-7825-settings-nav-resizable.md | 7 +
docs/dashboard-guide.md | 3 +
packages/dashboard/app/components/SettingsModal.css | 56 ++++-
packages/dashboard/app/components/SettingsModal.tsx | 124 +++++++++-
packages/dashboard/app/components/__tests__/SettingsModal.navResize.test.tsx | 268 +++++++++++++++++++++
packages/dashboard/app/styles.css | 27 ++-
6 files changed, 467 insertions(+), 18 deletions(-)
Fusion-Task-Id: FN-7825
Fusion-Task-Lineage: 34b940b4-2698-46a4-b47c-cda0b0cac564
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Removes the single-worktree gate on the Task Detail Terminal tab so it renders for every task, defaulting its first shell to the task worktree when present and otherwise falling back to the project root.
- TaskDetailModal.tsx: showWorktreeTerminalTab is now always true (drops the isWorkspaceTask/single-worktree gate and its stale fallback effect); taskWorktreeCwd still feeds defaultCwd when a worktree is recorded, and the tab renders without requiring taskWorktreeCwd
- Adds a changeset documenting the behavior change (minor, feature) for @runfusion/fusion
- docs/dashboard-guide.md: updates the Terminal tab description to state it is always available, with worktree-or-project-root cwd fallback, including for multi-repo workspace tasks
- Expands TaskDetailModal.worktree-terminal.test.tsx coverage for the no-worktree and workspace-task cases now that the tab is always shown
Files changed:
.changeset/FN-7826-worktree-terminal-always-available.md | 7 +++
docs/dashboard-guide.md | 4 +-
packages/dashboard/app/components/TaskDetailModal.tsx | 12 ++---
.../TaskDetailModal.worktree-terminal.test.tsx | 57 +++++++++++++++++++---
4 files changed, 63 insertions(+), 17 deletions(-)
Fusion-Task-Id: FN-7826
Fusion-Task-Lineage: f87504fe-9330-4392-bdbe-33bba006d96c
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Aligns OAuthExpiryMonitor's ntfy push notifications with the /api/auth/status refresh-then-recheck logic that drives the in-app OAuthReloginBanner, so providers that silently auto-refresh (e.g. GitHub Copilot's ephemeral token) no longer trigger false "OAuth token expired" pushes with no matching banner.
- OAuthExpiryMonitor.check() now performs a best-effort authStorage.getApiKey() refresh and reloads/re-resolves the credential before dispatching oauth-token-expired, instead of relying solely on the stored expiry timestamp
- resolveEffectiveOAuthCredential() now also guards against non-finite expires values in addition to non-numeric ones
- Updated docs/dashboard-guide.md and docs/settings-reference.md to describe the refresh-then-recheck behavior generically (not just Claude/Anthropic) and documented the FN-7821 fix in FNXC provenance comments
- Added regression tests covering the refresh-then-recheck flow in oauth-expiry-monitor.test.ts
- Added a patch changeset describing the fix for release notes
Files changed:
.changeset/fn-7821-oauth-expiry-notification-banner-consistency.md | 7 +
docs/dashboard-guide.md | 6 +-
docs/settings-reference.md | 6 +-
packages/engine/src/notification/__tests__/oauth-expiry-monitor.test.ts | 146 ++++++++++++++++++++-
packages/engine/src/notification/oauth-expiry-monitor.ts | 48 ++++++-
5 files changed, 199 insertions(+), 14 deletions(-)
Fusion-Task-Id: FN-7821
Fusion-Task-Lineage: 5954592c-adda-4fd4-b205-265860eddf3d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Consolidates Cursor Admin API key resolution onto one documented env var so the usage/admin credential path is reachable and unambiguous, replacing the prior dual CURSOR_ADMIN_API_KEY/CURSOR_API_KEY and multi-provider-id lookup.
- Replace CURSOR_ADMIN_API_KEY (preferred) + CURSOR_API_KEY alias with a single CURSOR_API_KEY env var, mirroring the GROK_API_KEY precedent
- Simplify readCursorApiKey to check CURSOR_API_KEY then fall back to the single "cursor" authStorage entry via readConfiguredApiKey (drop the cursor/cursor-cli/cursor-agent provider-id loop)
- Export readCursorApiKey and fetchCursorUsage for direct test coverage
- Update the no-auth error message and settings-reference.md docs to reference only CURSOR_API_KEY, clarifying cursor-cli OAuth/session auth vs the separate Admin API usage-metering credential
- Add changeset (@runfusion/fusion: minor) documenting the credential-path change
- Add/adjust usage.test.ts coverage for readCursorApiKey precedence (env over authStorage) and the updated credential-absent error message
Files changed:
.changeset/fn-7817-cursor-api-key.md | 7 ++++
docs/settings-reference.md | 8 ++--
packages/dashboard/src/__tests__/usage.test.ts | 53 +++++++++++++++++++++++++-
packages/dashboard/src/usage.ts | 52 ++++++++-----------------
4 files changed, 77 insertions(+), 43 deletions(-)
Fusion-Task-Id: FN-7817
Fusion-Task-Lineage: 86ac3d47-8e80-4159-abee-6c41aae56407
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a Cursor provider fetcher to the dashboard usage aggregator so operators with a Cursor Admin API key see spend-based usage alongside the other providers.
- usage.ts: add fetchCursorUsage() using the Cursor Admin API POST https://api.cursor.com/teams/spend with Basic auth (API key as username), resolving the key from CURSOR_ADMIN_API_KEY (preferred) or CURSOR_API_KEY, falling back to fusion-auth/pi-configured api keys; maps teamMemberSpend overallSpendCents/spendCents and hardLimitOverrideDollars/monthlyLimitDollars into a "Monthly spend" usage window with a reset derived from subscriptionCycleStart
- usage.ts: wire fetchCursorUsage into fetchAllProviderUsage's parallel provider fetch list (with withTimeout + no-auth demotion) and update the provider-list comment
- UsageIndicator.tsx: map the "Cursor" provider name to the existing cursor-cli icon token/SVG
- usage.test.ts: add CURSOR_ADMIN_API_KEY/CURSOR_API_KEY env stubbing and a full fetchCursorUsage regression suite (ok/zero-utilization/no-auth/error/expired-key/parse-failure cases)
- UsageIndicator.test.tsx: cover the Cursor icon mapping
- docs/settings-reference.md: document that the Usage dropdown Cursor card requires a Cursor Admin API key (session-only cursor-agent login is insufficient)
- add a minor changeset for @runfusion/fusion documenting the new Cursor usage card
Files changed:
.changeset/fn-7816-cursor-usage.md | 7 +
docs/settings-reference.md | 4 +
packages/dashboard/app/components/UsageIndicator.tsx | 7 +
packages/dashboard/app/components/__tests__/UsageIndicator.test.tsx | 26 +++
packages/dashboard/src/__tests__/usage.test.ts | 157 ++++++++++++++
packages/dashboard/src/usage.ts | 240 ++++++++++++++++++++-
6 files changed, 440 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7816
Fusion-Task-Lineage: 4cec63d8-4ddc-40f3-8d16-5e4078da5eba
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Lifecycle warnings now recompute client-side from the live graph for
editable workflows, so the banner reflects edits immediately instead of
waiting for a save round-trip. The two deterministically fixable codes gain
one-click fixes in the banner (all view modes):
- missing-merge-region inserts a Merge boundary in front of end;
- missing-completion-summary inserts the canonical completion-summary node
(config from @fusion/core's completionSummaryNode) upstream of the merge
region when one exists, else in front of end.
"Fix all" on the collapsed summary line applies both in order, producing
start → summary → merge → end on a fresh workflow in one click. The other
three codes are structural judgment calls and stay manual.
analyzeWorkflowLifecycle + completionSummaryNode are pure and now re-export
through core's browser-safe types.ts alias entry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- parseInsightsContent stripped bullet prefixes before filtering for them, so every
insights category rendered as one blob and counts were wrong (5 shown vs 84 real)
- drop dead GET /memory and GET /memory/stats mount fetches from useMemoryData and
stop refetching the file list on every file selection
- Memory view: full-width layout, accent tabs, 2-column Engines card grid, remove
duplicated capability badges, correct spacing-token-as-font-size rules
- Todos: single-row items with quiet inline action cluster (stacked on narrow/mobile)
- Insights: flat card list (no card-in-card), 28px/16px actions muted until hover
- Agent Memory tab: shared FileEditor (CodeMirror) for memory files, per-section save
actions, distinct inline-toggle aria-labels, fix {{date}} i18n interpolation
- PR screenshots under docs/assets/memory-ui-review-2026-07/
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>