Commit Graph

3634 Commits

Author SHA1 Message Date
gsxdsm
774726ad3a fix(core): bump SCHEMA_VERSION to 49 so the nodeId task-column migration runs
Migration 49 (`ALTER TABLE tasks ADD COLUMN nodeId TEXT`) was added with
SCHEMA_VERSION still pinned to 48. Existing DBs at version 48 hit the
`if (version >= SCHEMA_VERSION) return;` early exit, so the column was never
created — `TaskStore.listTasks` then crashed at startup with
`no such column: nodeId` and the dashboard exited before initialization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:04:51 -07:00
gsxdsm
0a3c28d872 chore(dashboard): UI polish — Hermes logo, settings header heights, simplify agent card actions
- Use bundled Nous Research SVG (inverted) on the Hermes runtime card
  instead of the generic ProviderIcon.
- Lock GitHub-star and Help buttons in the settings header to a uniform
  26px height so the differing icon sizes don't misalign them.
- Drop the inline Delete button from the idle/terminated agent-card
  states — deletion stays available from the agent detail view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:27:41 -07:00
gsxdsm
2229815bad fix: harden cross-platform paths and child-process handling
Replace process.env.HOME fallbacks with os.homedir() in dashboard usage
probes and the hermes plugin profile resolver so unset HOME no longer
yields literal "~" paths. Skip POSIX process-group semantics on Windows
in engine/merger and dashboard-tui's pgrep-based vitest killer. Add
shell: true to npx spawns in CLI skills/extension so .cmd shims resolve
on Windows, and route test:build-exe through cross-env.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:27:27 -07:00
gsxdsm
02d68f945d fix(dashboard): restore NodeStatusIndicator tests and fix routes.ts typecheck
- packages/dashboard/src/routes.ts: hermes/openclaw runtime metadata declares
  `version` as `string | undefined`, but BUNDLED_PLUGIN_RUNTIMES requires
  `string`. Coalesce to "0.0.0" so the bundled fallback list type-checks.
- packages/dashboard/app/components/__tests__/NodeStatusIndicator.test.tsx:
  the file had been reduced to a stub ("placeholder") which the test runner
  parsed as an undefined identifier. Restore the original 155-line suite from
  fc23e522a; the underlying NodeStatusIndicator component is unchanged.

All 9639 dashboard tests pass; pnpm typecheck is clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:26:47 -07:00
gsxdsm
4d48be9a5c fix(dashboard): expose bundled hermes/openclaw/paperclip runtimes in /api/plugins/runtimes
Merge metadata for the three example runtime plugins bundled as workspace
dependencies of @fusion/dashboard with pluginLoader.getPluginRuntimes(), so
the NewAgentDialog "Plugin Runtime" dropdown populates without requiring
an explicit `fn plugin install`. Installed plugins override the bundled
fallback by runtimeId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:24:21 -07:00
gsxdsm
a6697ee7ca feat(paperclip-runtime): route Paperclip calls through paperclipai CLI in Local CLI mode
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>
2026-04-27 23:06:20 -07:00
gsxdsm
08a4ae01f7 feat(FN-2719): merge fusion/fn-2719 2026-04-27 22:53:51 -07:00
Fusion
a3a6794c41 feat(FN-2718): add unavailable node policy setting and validation
- Define and export unavailable node policy types in core settings interfaces
- Add project-level unavailable node policy default and runtime validation helper
- Add core unit coverage for unavailable node policy parsing and acceptance cases
- Guard Paperclip mint requests to include companyId only when available for type-safe payloads
- Document unavailable node policy in the settings reference
2026-04-27 22:40:08 -07:00
gsxdsm
4a3a8dce7c chore(release): v0.7.0
Version bump via changesets.
2026-04-27 22:34:32 -07:00
gsxdsm
b30e017a34 chore: add 0.7.0 changeset for runtime plugins
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:33:56 -07:00
Fusion
d3e1c28cef fix(plugins): re-export probe symbols + declare plugin deps in dashboard
- 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>
2026-04-27 22:17:54 -07:00
gsxdsm
1a1bec1fb4 test(dashboard): align CSS-contract + onboarding tests with current contracts
Two long-standing test failures, root-caused and fixed.

terminal-mobile-keyboard-layout.test.ts (2 tests)
  CSS structure changed: the desktop terminal modal moved its initial
  width/height into companion `:not([style*="width"])` /
  `:not([style*="height"])` rules so persisted resize values can win
  over the default. The tests still searched the base block. Updated
  the helpers to extract from each rule explicitly, asserting:
    width: min(1800px, …)            in the :not([style*="width"]) rule
    max-width: calc(100vw - …)       in the base rule
    85vh                             somewhere in the :not([style*="height"]) rule
    min-height + max-height: calc(100dvh - 40px)  in the base rule
  Same constraints, just reflecting the current selector layout.

ModelOnboardingModal.test.tsx (2 tests)
  Component refactor (commit f3d918997) made the "what GitHub unlocks"
  feature list conditional on `!isGitHubReady`, so the test that mocks
  `authenticated: true` no longer sees "Import issues as tasks". And
  the gh-CLI optional explanation copy was rewritten to "OAuth from
  the dashboard is optional" instead of "OAuth integration in
  Settings → Authentication is optional". Updated:
    - "shows connected state": assert the new connected-state
      sentence ("GitHub is connected — issue imports …") instead of
      the now-hidden feature list line.
    - "explains gh CLI auth": use getAllByText for the two-place
      "GitHub CLI is already authenticated" copy and match the
      current "OAuth from the dashboard is optional" sentence.
  Also adds the missing `afterEach` import to silence its TS error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:16:45 -07:00
gsxdsm
aa86dcc05c fix(dashboard): scale stats values to fit metric box without overflow
Token counts on the task detail Stats panel could overflow the metric
box for large values (e.g. 10-digit comma-separated totals). Switched
each metric to a container-query inline-size context and gave the
value a fluid font-size of `clamp(13px, 13cqw, 28px)` so it grows to
fill the box up to a 28px cap and shrinks down to 13px on narrow grid
columns. Combined with `min-width: 0`, `overflow-wrap: anywhere`, and
the metric's `overflow: hidden`, long values now stay within the box
and short values render as large as they can without dominating.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:06:06 -07:00
gsxdsm
f3d918997c fix(dashboard): wider/resizable onboarding modal + GitHub CLI-aware messaging
- ModelOnboardingModal: native CSS resize with persisted size via
  useModalResizePersist; default ~1100px wide; modal scrolls inside
  the content area instead of clipping.
- Provider cards flex-wrap so the API-key form drops to a full-width
  row instead of being squished into a fixed side column. Connected
  badge no longer stretches the full body width.
- Step changes now reset content scrollTop so each page lands at the
  top.
- GitHub step: prominent GitHub mark via ProviderIcon; when gh CLI is
  authenticated the intro reads as 'you are all set', primary CTA
  becomes 'Continue with gh CLI auth', and a secondary
  'Connect OAuth (optional)' button is offered.
- CustomModelDropdown: highlight init runs once per open session, and
  filter changes reset highlight to top + scrollTop=0, fixing the
  scroll-fight when filtering models.
- TUI dashboard: at >=150 cols, Stats panel sits to the left of Logs;
  bottom row drops to Utilities + Settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:05:45 -07:00
gsxdsm
4b9fd3dd64 fix(dashboard): clear all 661 test-file type errors
Production typecheck (tsconfig.json + tsconfig.app.json) was already
clean, but a third config that includes test files surfaced 661 errors
across 60+ test files — accumulated drift between mock fixtures and
production types. Six parallel typescript-pro agents fixed every one
without touching production code.

Per-scope before/after (errors → 0):
  ChatView                                                     183
  Mailbox + Agent suite (5 files)                              156
  Task / Modal suite (6 files)                                 127
  App + small components (12 files)                             96
  Hooks + api/auth (8 files)                                    48
  Long tail (32 files)                                          51
  -----------------------------------------------------------------
  Total                                                        661

Major fix categories:
- Untyped state objects inferring `never[]` / `null` literals (root
  cause of ~120 errors in ChatView alone — added a single
  `UseChatReturn` annotation)
- Mock objects missing fields that became required: `WorkflowStep.mode`,
  `ChatMessage.thinkingOutput / metadata`, `ChatSession.projectId`,
  `Task.log`, `ProjectHealth` fields, `PtyTerminalSessionInfo.createdAt`,
  `Agent.metadata`, `InboxResponse.total`, etc.
- Mock objects with stale fields that no longer exist:
  `AgentBudgetStatus.budgetPeriod`, `truncated` on log responses,
  `OutboxResponse.unreadCount`, `MergeResult.source/target/details`
- Modal props that became required (e.g. `PlanningModeModal.onTasksCreated`)
- String literals not in narrowed unions (`Column`, `WorkflowStepPhase`,
  `InsightStatus`, `AgentLogType`, etc.)
- `querySelector` returning `Element` cast to `HTMLElement` for
  `@testing-library/react`'s `within()`
- Vitest mock typing: `.mock.calls` access needing `vi.mocked(...)`,
  zero-param tuple handling, generic `vi.fn(() => [])` inferring
  `never[]`

Helpers introduced in test files (no shared infra):
- `makeSettings(overrides)` in ModelSelectorTab.test.tsx
- `makePromptOverrides(overrides)` in AgentPromptsManager.test.tsx
- `FileBrowserTestOverrides` type alias in FileBrowser.test.tsx
- `makeInboxResponse / makeOutboxResponse` in MailboxView.test.tsx

Verification:
- tsc -p tsconfig.json:        exit 0
- tsc -p tsconfig.app.json:    exit 0
- tsc -p tsconfig.test-check.json (new — includes test files): exit 0
- vitest run:                  9639 / 9641 (2 pre-existing failures
                               in terminal-mobile-keyboard-layout.test.ts
                               unrelated to this work; verified via
                               `git stash` + run on clean HEAD)

Adds packages/dashboard/tsconfig.test-check.json to keep this regression
guard available locally — same as tsconfig.app.json minus the test
exclude.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:59:52 -07:00
gsxdsm
8c554211f3 fix(dashboard): card timer matches stats panel + resizable Changes diff modal
Two related dashboard fixes.

1. Card timer mismatch: the board card timer chip showed only workflow
   runtime (e.g. <1m on FN-2716) while the task detail Stats panel
   reported "Total execution time" of 7m+ for the same task. Cause —
   the slim board listing strips `task.log` to keep payloads small, so
   the card's client-side `[timing]` log scan returned 0. Now the slim
   path aggregates `[timing] … in <N>ms` durations server-side into a
   new `task.timedExecutionMs` field before stripping the log; the
   card prefers this aggregate, falling back to the client scan when
   the full log is loaded (TaskDetailModal). Wire payload stays slim.

2. View Changes diff modal: defaulted to `90vw × 80vh` and was not
   user-resizable. Switched to `min(95vw, 2200px) × min(90vh, ...)`
   default with `resize: both`, persisted via useModalResizePersist
   (`fusion:changes-diff-modal-size`). Mobile keeps fullscreen layout.
   Overlay dismiss switched to the shared `useOverlayDismiss` hook so
   resize-drags that release on the overlay don't close the modal.

Updated the diff modal's regression tests to match the new constraint
shape (still asserts max-height clamps to viewport via calc()).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:55:46 -07:00
gsxdsm
4e2caaf605 docs: add Discord link and badge to README
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:45:04 -07:00
gsxdsm
938b0c62a0 fix(dashboard): no resize-drag dismiss across all resizable modals + Claude CLI in Authenticated group
When a user drags the native CSS resize grip from inside a modal and
releases the mouse over the overlay, the synthesised click event
targets the common ancestor (the overlay) — fooling the existing
e.target === e.currentTarget dismiss check. Audited every modal with
`resize: both` and switched them to a shared mousedown→mouseup tracking
pattern (new useOverlayDismiss hook) so dismiss only fires when both
events land on the overlay.

Modals fixed: TaskDetail, Settings, FileBrowser, GitHubImport,
GitManager, ScheduledTasks, Scripts, WorkflowStepManager. (Terminal
and AgentDetail were already fixed in 56e8e7324; PlanningModeModal
already had the right pattern inline.)

Also: in Settings → Authentication, the "Anthropic via Claude CLI"
card now lives inside the Authenticated group when authenticated and
the Available group otherwise, instead of floating at the top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:36:24 -07:00
gsxdsm
56e8e73244 fix(dashboard): resizable terminal + agent detail modals, no resize-drag dismiss
Terminal modal and agent detail view are now resizable via the native
CSS grip with sizes persisted per-modal in localStorage. Mobile keeps
fullscreen layout (resize disabled, !important overrides any persisted
desktop dimensions).

Both modals previously dismissed when a drag-resize started inside the
modal but released over the overlay — the synthesised click event
targets the common ancestor (overlay), tripping the e.target ===
e.currentTarget dismiss check. Switched to mousedown→mouseup tracking
so dismiss only fires when both events land on the overlay.

Terminal additionally observes its own pixel box via ResizeObserver
and refits xterm on every grip drag — `resize: both` doesn't emit
window resize.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:30:05 -07:00
gsxdsm
cdddc602f9 feat(dashboard): add fireworksai and qwen-ai provider id aliases
Two more brand-id spellings observed in the wild that previously fell
through to the generic Cpu glyph:
  fireworksai → FireworksIcon (joins fireworks / fireworks-ai)
  qwen-ai     → QwenIcon       (joins qwen / qwen-coder / alibaba / tongyi)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:25:10 -07:00
gsxdsm
bb340206bf feat(dashboard): add Cerebras / Groq / Vercel icons + cover all pi-ai catalog providers
Audited the pi-ai built-in model catalog provider IDs against the
ProviderIcon registry and filled every remaining gap.

New icons:
  --provider-cerebras    (#f1592a)  → CerebrasIcon (concentric arcs)
  --provider-groq        (#f55036)  → GroqIcon     (lightning bolt)
  --provider-vercel      (text)     → VercelIcon   (triangle wordmark)

New aliases routed to existing icons:
  minimax-cn               → MiniMaxIcon
  azure-openai-responses   → AzureIcon
  google-gemini-cli        → GeminiIcon
  google-generative-ai     → GeminiIcon
  opencode-go              → OpencodeIcon
  vercel-ai-gateway        → VercelIcon

After this commit every provider id emitted by `@mariozechner/pi-ai`'s
register-builtins (anthropic, openai, openai-codex, google,
google-vertex, google-gemini-cli, google-antigravity,
amazon-bedrock, mistral, openrouter, fireworks, cerebras, groq,
huggingface, kimi-coding, minimax, minimax-cn, zai, xai, opencode,
opencode-go, github-copilot, azure-openai-responses,
vercel-ai-gateway) plus the user-curated set (qwen, lm-studio,
hugging-face, alibaba/tongyi, hf, lmstudio…) renders a brand icon
instead of falling back to the generic Cpu glyph.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:22:42 -07:00
gsxdsm
13b502497c feat(dashboard): add Qwen, LM Studio, Hugging Face, Mistral, Azure, Fireworks logos + Gemini aliases
Adds six new provider icons rendered as inline SVG, each backed by a new
brand token in styles.css:
  --provider-qwen          (Qwen / Tongyi)
  --provider-lmstudio      (LM Studio)
  --provider-huggingface   (Hugging Face)
  --provider-mistral       (Mistral AI)
  --provider-azure         (Microsoft Azure / Azure OpenAI)
  --provider-fireworks     (Fireworks AI)

Aliases registered:
  qwen / qwen-coder / alibaba / tongyi
  lmstudio / lm-studio
  huggingface / hugging-face / hf
  mistral / mistral-ai
  azure / azure-openai
  fireworks / fireworks-ai

Also extends the Gemini icon to cover every Google-product alias the
catalog throws at it: google-vertex, vertex, google-cloud-code,
cloud-code, antigravity (in addition to the existing google,
gemini, google-antigravity).

Tests cover each new mapping (and confirm the Gemini alias group all
resolves to the gemini icon with --provider-gemini color).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:07:32 -07:00
gsxdsm
e8fb9672b5 fix(dashboard): onboarding step buttons + tighter auth provider spacing
- ModelOnboardingModal: completed/skipped step indicators render as
  <button> so they're clickable for review. Without explicit reset,
  browsers gave them their default white-ish background. Added
  background: transparent + border/padding/font-family resets to
  .model-onboarding-step-indicator so it inherits cleanly from the
  surrounding card on every theme.
- SettingsModal authentication: tightened spacing around providers —
  group margin 16→10, group label margin 8→4, provider card margin
  8→4, header padding 12 16 → 8 12. Less wasted space between rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:59:37 -07:00
gsxdsm
6094c999ed fix(tui): tighten StatusModeSingle spacer threshold to cols<71
Previously the spacer kicked in at cols<68 (top of Logs border was
covered at 68-70) then was widened to cols<72 (which added a gap at 71
where Yoga's flex-column placed the panel correctly without help).
cols<71 is the precise boundary: 68-70 need the 1-row compensation
spacer, 71+ does not.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:20:18 -07:00
gsxdsm
3b3d456ca8 fix(dashboard): mobile rendering for resizable modals
The Settings, Workflow Steps, Automations (and friends) modals all carried
desktop `min-width: 480-520px` constraints from the resize work. On phones
that pushed them off-screen, and the overlay's 10vh top padding plus a
fractional `height: 80vh` left awkward gaps top and bottom.

Apply the same `@media (max-width: 768px)` full-screen-sheet treatment
that File Browser already had to every resize-aware modal: drop the
overlay padding, set width/height to `100vw`/`100dvh`, zero out the
min-* / max-* constraints, and disable `resize` (touchscreens can't grab
the corner grip anyway).

Also tightened the existing File Browser and GitHub Import overrides to
match (`min-width: 0`, `min-height: 0`, `resize: none`).

For TaskDetailModal the mobile rules go inside the existing first
`@media (max-width: 768px)` block to keep the FN-1331 detail-body
padding regression test happy (it captures from the first 768px media
query greedily).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:10:24 -07:00
gsxdsm
1fe59be960 feat(dashboard,cli): update-check frequency, GitHub star button, more resizable modals
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>
2026-04-27 17:55:15 -07:00
gsxdsm
dba2299137 fix(core): polling loop falsely emits task:deleted for archived tasks
TaskStore.checkForChanges detects deletions by comparing the in-memory
taskCache against the tasks table. But archiveTask also DELETEs the row
from `tasks` (after copying to archive_db), so any TaskStore instance
polling the same DB sees the archived task vanish and emits
`task:deleted`. The activity-log listener records that as a deletion,
producing entries like "Task FN-NNNN deleted" for tasks that are alive
and well in the archive.

Reproduced live: 2048 task:deleted entries in a single ~1ms burst, all
of them present in archive.db. Two TaskStores (CLI/engine and dashboard
server) on the same DB → CLI archives, dashboard polls and false-flags.

Fix: in checkForChanges, batch-query the archive for all missing IDs.
For ids that exist in archived_tasks, emit `task:moved` (to:archived) —
matching what archiveTask emits in-process — so the activity log
records the correct event. For ids not in archive, emit task:deleted as
before (real deletion).

Adds ArchiveDatabase.filterArchived(ids) helper that returns the subset
in archived_tasks via a single SELECT IN query (chunked at 500 to stay
under SQLite's parameter limit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:46:05 -07:00
gsxdsm
b3b0ce7cb0 feat(dashboard): UI polish pass — resizable modals, themed scrollbars, settings overhaul
Modal resize + size persistence
- Extract `useModalResizePersist` hook (ResizeObserver + localStorage),
  apply to Files, Git Manager, GitHub Import, Workflow Steps, Automations
  (ScheduledTasks), and Settings modals. Each gets `resize: both`, sane
  min/max constraints, and a unique storage key.
- Bump default heights so the modals feel less cramped (Git: 92vh,
  Workflow/Automation: 80vh, Settings: 80vh / 1100px).

Scrollbar theme
- Add a global `*::-webkit-scrollbar*` + `scrollbar-color` rule in
  styles.css so chat, document, system stats, file browser, usage
  indicator, etc. inherit the theme. Existing per-component overrides
  (.board, .column-body, .settings-sidebar, planning modal) still win.

Document view
- Collapse "Show hidden" toggle and search input onto the same row as
  the Project Files / Task Documents segmented control. Stack again
  below 768px.

Mailbox / Todos
- Match Todos header treatment to the Mailbox header (typography,
  padding, border).
- Add top spacing above Mailbox Inbox/Outbox/Agents tab bar so the
  vertical gaps balance.

Settings
- Wider, resizable, persisted Settings modal.
- Project Models description and Authentication panel get proper
  horizontal padding.
- Reorder project sidebar so "General" is first.
- Plugins page: clean margins, integrate refresh button, exclude
  bundled runtimes from the "Installed Plugins" list (they were
  appearing twice — once erroring, once in their own section).
- New "Updates" panel with auto-check toggle + "Check now" button
  (frequency control noted as needing a backend schema field).

Background sessions
- Fix stale "AI N" / planning-icon badge: `handleDeleted` in
  `useBackgroundSessions` now writes a tombstone, advances the
  timestamp guard, and broadcasts completion so the cross-tab
  sync store stops resurrecting the deleted session on the next
  merge tick. Added regression test.
- Re-fetch list on SSE reconnect so terminal events fired during a
  network blip don't get permanently lost.

System stats
- Refresh button uses correct single class (was getting both `btn`
  and `btn-icon`, which conflicted on padding/border).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:26:07 -07:00
gsxdsm
53e763e35f fix(engine): respect global pause in reviewer + stuck detector
Reviewer subprocesses were spawned via fn_review_spec / fn_review_step
even with globalPause on, because reviewer.ts had no pause awareness.
Stuck detector also kept running, treating pause-disposed sessions as
inactivity and re-queuing tasks. Pause-transition listeners only called
session.dispose(), which doesn't always interrupt an in-flight LLM
stream — letting reviewer spawns leak through after pause flipped.

- reviewer.ts: re-read settings, return UNAVAILABLE without spawning
  when globalPause/enginePaused is on.
- stuck-task-detector.ts: skip checkStuckTasks() while paused.
- triage.ts / executor.ts: call session.abort() before dispose() in the
  pause-transition listener to interrupt in-flight work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:26:01 -07:00
gsxdsm
e598d60f01 fix(tui): redesign Main layout, robust header, panel reshuffle
Header was disappearing in tmux at narrow widths under Logs/Utilities
panels because Yoga's flex-column placed the panel body at y=0 instead
of y=1, overdrawing the header. Synthetic tests at every width show the
layout as correct, but real-tmux log-update tracking drifts over many
state updates and the header gets scrolled off.

- Paint header as an ANSI overlay after every Ink frame write (DECSC/
  DECRC cursor save/restore) — guaranteed at terminal row 1 regardless
  of any layout drift. Skipped during splash so the loading screen is
  uncluttered.
- StatusModeGrid: full-width System on top (4-row pinned, chips wrap if
  needed) + Logs filling the middle + Stats/Utilities/Settings as
  equal-width bottom row. Stats now shows just Process/System rows.
- StatusModeSingle: 1-row spacer only at cols<68 (Yoga edge case at
  very narrow widths shifts content up); 68+ has no spacer per UX.
- Panel/LogsPanel/UtilitiesPanel inner content boxes pinned with
  flexShrink=1 + overflow=hidden so panel intrinsic height can't push
  the frame past terminal rows.
- Each Logs entry wrapped in height={1} Box (when wrap is off) — Yoga
  was sometimes measuring nested Text as taller than 1 row at narrow
  widths.
- Tab "Explorer" → "Files" with shortcut "e" → "f" (preserves
  filter-cycle on Logs panel; switches to Files view elsewhere).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:00:43 -07:00
gsxdsm
b055c81ec0 feat(dashboard): redesign Planning modal as two-pane session manager
- Sidebar lists all planning sessions for the project (any status) with
  inline status badges, relative timestamps, "+ New session", and an
  inline delete confirm. Mobile collapses to a single pane with a back
  chevron in the header.
- Closing the modal (X / Escape / overlay) no longer cancels the server
  session — it stays in the list, resumable. Only an explicit Delete
  cancels + removes. Removes the now-redundant minimize button.
- Add re-sync on reopen so a session whose terminal SSE event was missed
  doesn't stay stuck on a stale loading view; previously only a hard
  reload recovered.
- Persist user-chosen modal width/height across opens via localStorage,
  driven by a ResizeObserver on the modal element.
- Theme the modal scrollbars to match the rest of the app.
- Banner dismiss only hides the banner now — it must not delete the
  underlying session, since sessions are first-class in the new sidebar.
- Refresh background sessions list on SSE reconnect so the footer "AI N"
  pill never gets stuck on tombstoned sessions after a network blip.
- Drop selection + broadcast completion on Create Task / Create Tasks
  so the footer count drops in lockstep instead of waiting on SSE.
- Auto-scroll the AI thinking output as new tokens stream in (only when
  already pinned to the tail).
- Guard overlay-click dismissal with a mousedown-on-overlay check so
  releasing a resize drag outside the modal doesn't close it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:58:35 -07:00
Fusion
7986b03207 fix(dashboard): card timer shows total execution = timed events + workflow runtime
The task card timer chip previously fell back through several metrics
(timed duration → workflow runtime → wallclock), so cards showed only a
subset of execution time. For FN-2714 this rendered <1m on the card while
the stats tab reported >2m of workflow runtime.

The chip now reports the sum of [timing]-tagged log events and workflow
step runtime (matching the new "Total execution time" metric in the stats
panel), with live elapsed for in-progress workflow steps. When neither
metric is recorded, the chip is hidden rather than falling back to
wallclock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:05:50 -07:00
Fusion
777d6a1942 fix(FN-2714): restore task timer chip to footer metadata row
- Move TaskCard time indicator rendering from the header back into the footer metadata row
- Render timer and files-changed metadata together in the shared footer row layout
- Update timer chip CSS to stay right-aligned with margin-left:auto and prevent shrinking
- Update TaskCard and mobile board tests to assert footer placement and alignment behavior
2026-04-27 12:45:53 -07:00
Fusion
ac93023f2d fix(FN-2715): show initial loading state in Agents view
- Render an accessible loading status when the first agents fetch is in flight
- Keep existing agent cards visible during refresh instead of replacing the grid with a loader
- Add AgentsView CSS for the loading container with responsive sizing
- Add regression tests covering initial loading, post-load cleanup, and refresh behavior
2026-04-27 12:04:07 -07:00
Fusion
6b2982cda5 test(FN-2712): add runtime e2e and integration coverage
- Add OpenClaw runtime end-to-end tests covering execution flow and runtime contract behavior
- Add OpenClaw integration tests to validate plugin/runtime wiring in engine scenarios
- Add Paperclip runtime end-to-end and integration suites for equivalent cross-runtime coverage
- Update Hermes, OpenClaw, and Paperclip manifest descriptions for consistent runtime metadata
2026-04-27 11:58:31 -07:00
Fusion
626e768f39 feat(FN-2709): migrate Hermes runtime plugin to pi-ai streaming client
- Replace Hermes pi module integration with pi-ai session streaming and updated runtime adapter contracts
- Remove legacy engine guard scaffolding and add hermes-stream-client coverage for streaming behavior
- Rewrite plugin and engine e2e tests to align with the new runtime flow and regenerate dist artifacts
- Update Hermes runtime README and package metadata to document pi-ai execution expectations
2026-04-27 11:34:49 -07:00
gsxdsm
597daeaf33 fix(dashboard): preserve fullDetail.log so task Activity tab renders
The live-token-usage refactor (199ee9cee) merged the SSE-updated `task`
prop on top of `fullDetail` to keep tokenUsage/status fresh. SSE strips
`log` to [] (stripTaskListHeavyFields) for list payloads, so the spread
clobbered fullDetail.log and the Activity timeline rendered empty.

Carve `log` out of the merge alongside `prompt`. Adds a regression test
that simulates the SSE-stripped task prop with a populated fullDetail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 11:34:49 -07:00
Fusion
db504d2ab6 feat(FN-2713): move timer chip into task card header
- Render the countdown timer in the task card header next to metadata for clearer scanning
- Remove legacy footer timer styling and adjust TaskCard structure accordingly
- Update TaskCard unit tests to assert the new timer placement and header behavior
- Expand mobile board tests to validate timer visibility and placement across card states
2026-04-27 11:34:49 -07:00
Fusion
399c7048cb fix(dashboard): prevent foreign-branch diffs after worktree pool reuse
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>
2026-04-27 11:34:49 -07:00
gsxdsm
9650019877 fix(dashboard): prevent foreign-branch diffs after worktree pool reuse
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>
2026-04-27 11:04:42 -07:00
Fusion
f85d369bfc feat(FN-2706): merge fusion/fn-2706 (auto-resolved)
- feat(FN-2706): complete Step 6 — document Paperclip REST runtime behavior
- test(FN-2706): cover getAgentIdentity success and request payload assertions
- fix(FN-2706): align promptWithFallback signature with runtime contract
- fix(FN-2706): add session dispose compatibility for engine callers
- fix(FN-2706): refine Paperclip API client error and config handling
- test(FN-2706): complete Step 4 — cover paperclip api client and adapter flow
- feat(FN-2706): complete Step 3 — wire plugin settings and remove engine guard
- feat(FN-2706): complete Step 2 — rewrite paperclip runtime adapter
- fix(FN-2706): restore compatibility exports during runtime migration
- feat(FN-2706): complete Step 1 — add Paperclip REST client
2026-04-27 10:52:46 -07:00
Fusion
6d1089bc25 docs(FN-2708): document OpenClaw gateway configuration settings
- Add an OpenClaw Gateway Configuration section to docs/settings-reference.md
- Document gatewayUrl, gatewayToken, and agentId settings with defaults and purpose
- Map each setting to OPENCLAW_* environment variable fallbacks and explain resolution priority
- Clarify these are plugin-level settings (not agent runtimeConfig fields) and add token security guidance
2026-04-27 10:03:26 -07:00
Fusion
bd59b1ff99 fix(FN-2704): gate auth onboarding behind setup wizard
- Prevent useAuthOnboarding from opening model onboarding while the setup wizard is active
- Add app-level wiring so onboarding flow respects setup wizard visibility state
- Add regression tests covering setup wizard gating and non-stacking onboarding behavior
2026-04-27 09:56:05 -07:00
Fusion
637f4350c9 feat(FN-2703): support custom MCP tools in plan generation
- Remove ToolSearch prerequisite so custom MCP tools can be used during triage and plan generation flows
- Align built-in tool sets between provider wiring and prompt builder handling, including custom ls behavior
- Expand pi-claude-cli tests for event bridge, MCP config, prompt builder, and tool mapping regressions
- Add FN-2703 changeset and delivery documentation for the published @runfusion/fusion package
2026-04-27 09:51:49 -07:00
Fusion
aaa937f029 feat(FN-2701): route OpenClaw runtime sessions through gateway client
- Add a dedicated gateway client seam and wire runtime adapter sessions through the new request path
- Remove legacy engine-guard/pi seam exports and align runtime types with gateway-facing behavior
- Fix gateway request/stream handling by preventing duplicate user turns, stabilizing tool-call callbacks, and adding a no-op session dispose hook
- Expand plugin test coverage for gateway client, adapter, and index behavior and document runtime gateway behavior in the README
2026-04-27 09:46:25 -07:00
Fusion
9015041283 feat(FN-2705): reduce planning question scroll top padding
- Update PlanningModeModal styles to decrease top padding in the question scroll area
- Improve modal content density so question content starts closer to the top
2026-04-27 09:42:27 -07:00
Fusion
07c635e1b7 test(FN-2702): add Hermes runtime e2e coverage
- Add end-to-end Hermes runtime test covering PluginStore registration through PluginLoader and PluginRunner resolution
- Verify createResolvedAgentSession uses the Hermes runtime and delegates createFnAgent, promptWithFallback, and describeModel calls
- Add regression test ensuring AgentRuntime-shaped Hermes adapters are reused without compatibility wrapping
- Cover fallback behavior to default pi runtime when Hermes plugin is not installed
2026-04-27 09:37:37 -07:00
Fusion
0f04b2a79e feat(FN-2700): add runtime selection to custom agent tab
- Add runtime selection controls to the NewAgentDialog custom tab flow
- Update dialog behavior to persist and apply the selected runtime when creating custom agents
- Add NewAgentDialog styling updates for the new runtime UI states
- Expand NewAgentDialog tests to cover runtime selection behavior on the custom tab
2026-04-27 09:21:03 -07:00
Fusion
347cae8e6c fix(FN-2698): load plugins during CLI runtime startup
- Load configured plugins during dashboard startup before launching the UI flow
- Load plugins during serve and daemon startup so runtime hooks are available immediately
- Add targeted CLI command tests for dashboard, serve, and daemon auto-load behavior at startup
- Add a changeset documenting the plugin runtime startup fix for @runfusion/fusion
2026-04-27 09:10:14 -07:00
Fusion
aa325e5491 feat(FN-2697): add todo task creation with agent picker actions
- Add TodoView controls to create todo tasks directly and optionally assign an agent
- Style the TodoView agent picker dropdown for desktop and mobile interactions
- Add TodoView tests covering todo task creation and agent assignment behavior
- Simplify dashboard TUI status grid sizing to use flexible panel layout
2026-04-27 09:01:32 -07:00