Commit Graph

11055 Commits

Author SHA1 Message Date
gsxdsm
23dd0fcdf4 test: fix stale refinement-title assertions after FN-7165
Two store-comments refinement tests located the auto-created refinement task by
`title.includes("Refinement")`, but FN-7165 (34efa8b89) intentionally changed
refinement titles to `${sourceId}: ${feedback}` for traceability. The refinement
task is still created correctly (the length assertion passes); only the
identifying substring was stale, so the tests failed on `expect(refinement)
.toBeDefined()`. Match the refinement by its feedback-derived title instead.
No product behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:37:37 -07:00
gsxdsm
6e74eedf2d fix: make explicit prompt write atomic with the task-row commit (greptile P1)
updateTask now performs the explicit updates.prompt write (and its File Scope
validation) BEFORE committing the task row / task.json, instead of after. A
failed prompt write (EACCES/EISDIR/disk-full) or invalid File Scope now aborts
the whole update atomically rather than leaving the field changes committed with
a stale PROMPT.md and returning an error — the write counterpart to the
read-resilience guards in getTask/updateStep. Adds an atomicity regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:36:43 -07:00
gsxdsm
4fd6317aa4 fix: accurate error when updateStep can't read prompt-derived steps (greptile P1)
When a task's steps live only in an unreadable PROMPT.md, updateStep still can't
resolve step 0 — but it now throws an error naming PROMPT.md as the cause instead
of a misleading "task has 0 steps". The operation genuinely cannot succeed (no
step data), so this is accurate reporting, not a silent success. The reachable
reset path (which derives its loop from getTask's steps) remains unaffected.
Adds a test asserting the PROMPT.md-attributed error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:36:43 -07:00
gsxdsm
54960672d7 fix: address PR review feedback (surface coverage, list/search guard, Intel Homebrew, serve exit tests)
- store.ts: guard parseStepsFromPrompt in listTasks and searchTasks too, so one
  unreadable PROMPT.md can't reject the Promise.all and 500 the whole board
  list/search (CodeRabbit). Matches the getTask fallback.
- update-check.ts: isHomebrewInstall now resolves symlinks and matches the real
  Cellar/opt install roots, fixing Intel-macOS Homebrew detection that only
  checked /usr/local/Homebrew/ (brew's repo dir) and would have shown npm/sudo
  guidance instead of `brew upgrade` (CodeRabbit).
- task-detail-prompt-resilience.test.ts: extend to assert the invariant across
  all surfaces — listTasks(slim)/searchTasks, reopen-to-todo moveTask
  (resetPromptCheckboxes), and deleteTask — not just getTask/updateTask/archive
  (CodeRabbit; Surface Enumeration rule).
- serve.test.ts: add SIGINT/SIGTERM exit-code assertions (130/143) so the serve
  path's POSIX exit contract can't regress independently of daemon (CodeRabbit).
- update-check.test.ts: add Intel-Homebrew remediation test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:36:43 -07:00
gsxdsm
93b080102e fix: make per-task API resilient to unreadable PROMPT.md; evict archived badge snapshots
#5 root cause (reproduced): getTask — the shared load for the entire per-task
API — plus the mutation helpers updateTaskUnlocked, updateStep,
readPromptForArchive, and resetPromptCheckboxes all read PROMPT.md unguarded.
An unreadable PROMPT.md (root-owned from a prior `sudo` run -> EACCES, PROMPT.md
being a directory -> EISDIR, transient FS error) threw and 500'd every per-task
operation (GET/DELETE/PATCH/retry/reset/archive) for every task, while the
PROMPT.md-free board list and create kept working. These reads are now
best-effort: degrade (empty prompt / unsynced steps / skipped cosmetic sync)
and log, so a PROMPT.md hiccup can never brick task management. Added a symptom-
verification test that forces EISDIR and asserts getTask/updateTask/archiveTask
still succeed.

#10c: the dashboard badge-snapshot cache only evicted on hard-delete, so
archived tasks were re-cached via task:updated and retained for the daemon's
lifetime — a slow memory leak. New isBadgeEligibleTask predicate gates the
create/update listeners so archived tasks are evicted (matching the startup
prime's includeArchived:false). Added a unit test for the invariant.

Updates the #5 changeset to cover the real fix; adds a badge-eviction changeset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:36:43 -07:00
gsxdsm
23e36b8935 fix: surface API 500 causes, guide update EACCES, non-zero daemon signal exit
Addresses three user-reported bugs:

- API 500 diagnosability: rethrowAsApiError now preserves the original error
  as Error `cause` and the /api boundary logs stack + cause for 5xx, so the
  opaque "task write API returns 500 for every task" failures are traceable
  (client body stays generic in production).
- In-app "Update now": detect EACCES/EPERM install failures and return
  actionable remediation (sudo fn update / reinstall without sudo / brew
  upgrade) instead of raw npm stderr; do not retry --force for this class.
- Daemon restart: `fn daemon` and `fn serve` exit 128+signal (SIGTERM=143,
  SIGINT=130) on signal-initiated shutdown so Restart=on-failure restarts a
  memory-pressure kill. Interactive `fn dashboard` TUI intentionally unchanged.

Adds regression tests (update-check EACCES/EPERM, daemon exit codes) and three
@runfusion/fusion patch changesets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:36:43 -07:00
gsxdsm
7846c9613e FN-7798: filter unconfigured/no-entitlement providers from usage view
Only show usage meters for AI providers the user has actually configured, instead of surfacing entries for providers with no meterable data.

- fetchGitHubCopilotUsage now demotes GitHub's 404 "No Copilot subscription found" response (both the Fusion-credential HTTP path and the gh-CLI fallback path) to a `no-auth` status instead of `error`, so it is treated as no meterable entitlement.
- fetchAllProviderUsage's existing `status !== "no-auth"` filter now also excludes these no-entitlement Copilot results, so they no longer appear in the usage list.
- Configured-but-failing providers (expired auth returning 401/403, transient HTTP 5xx, or other errors) keep `status: "error"` and remain visible with their diagnostic message.
- Added regression tests covering: Fusion-credential 404 omitted, Fusion-credential 500 surfaced as error, gh-CLI 404 omitted, gh-CLI 401 surfaced as "GitHub auth expired" error.
- Added a changeset documenting the usage-view behavior change as a patch/fix.

Files changed:
 .changeset/fn-7798-usage-configured-providers.md |  7 +++
 packages/dashboard/src/__tests__/usage.test.ts   | 71 ++++++++++++++++++++++--
 packages/dashboard/src/usage.ts                  | 19 ++++++-
 3 files changed, 90 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7798

Fusion-Task-Lineage: 18835b48-68f3-48aa-a03c-cc85772778a9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 21:19:23 -07:00
gsxdsm
725ce45c5d FN-7799: fix false-negative Git repository detection blocking task execution
Replace the boolean isGitRepository() check with a tri-state Git detection so environmental git failures (dubious ownership, missing git binary, timeouts) are no longer misreported as "not a Git repository", which previously blocked all task execution in valid repos and survived engine restarts.

- Add detectGitRepository() in worktree-pool.ts returning repo / not-repo / error (with reason: dubious-ownership, git-missing, timeout, unknown), classified from git's stderr; bound the git rev-parse call with a 10s timeout and maxBuffer; keep isGitRepository() as a backward-compatible wrapper
- Route the executor dispatch preflight guard through detectGitRepository(): only emit the original "not a Git repository / run git init" fatal on a positive not-repo verdict; on error, throw a distinct accurate error naming the real git failure, including the safe.directory remedy for dubious ownership
- Route the in-process runtime startup warning through the same tri-state detection so it only warns "not a Git repository" on a positive not-repo verdict
- Add a regression test locking extractWorktreeConflictInfo() to NOT misclassify a dubious-ownership git worktree add failure as not-git-repo
- Add targeted tests across worktree-pool, executor-worktree, and in-process-runtime test suites covering repo/not-repo/dubious-ownership/git-missing/timeout classifications on Windows OneDrive-style and POSIX paths
- Add changeset and a docs/solutions/logic-errors write-up of the false-negative root cause and fix

Files changed:
 .changeset/fn-7799-git-detection-false-negative.md |  7 +++
 .../logic-errors/git-detection-false-not-repo.md   | 54 ++++++++++++++++
 .../engine/src/__tests__/executor-worktree.test.ts | 61 +++++++++++++++++++
 .../engine/src/__tests__/worktree-pool.test.ts     | 71 +++++++++++++++++++---
 packages/engine/src/executor.ts                    | 38 +++++++++---
 .../runtimes/__tests__/in-process-runtime.test.ts  | 53 ++++++++++++++--
 packages/engine/src/runtimes/in-process-runtime.ts | 16 ++++-
 packages/engine/src/worktree-pool.ts               | 66 ++++++++++++++++++--
 8 files changed, 334 insertions(+), 32 deletions(-)

Fusion-Task-Id: FN-7799
Fusion-Task-Lineage: 25a84283-bf47-472b-8a98-a10bf7e494de
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 21:19:22 -07:00
gsxdsm
915c1e0152 FN-7801: infer xAI icon for Grok model IDs in ProviderIcon
Fix ProviderIcon so Grok model-shaped provider strings (e.g. grok-4.5, grok-cli/grok-4-fast) render the xAI brand icon instead of falling back to the generic Cpu icon.

- ProviderIcon now falls back to inferProviderIconKey(provider) when an exact providerConfig lookup misses, before defaulting to the Cpu icon
- Added a changeset (patch) documenting the fix for dashboard provider icon surfaces
- Added regression tests covering exact xAI/Grok keys, inferred Grok model-id strings, non-Grok inferred providers (e.g. gpt-5.5), and confirming unknown/empty providers still fall back to Cpu without the xAI icon

Files changed:
 .changeset/fn-7801-grok-xai-logo.md                |  7 ++++++
 packages/dashboard/app/components/ProviderIcon.tsx | 11 ++++++++-
 .../app/components/__tests__/ProviderIcon.test.tsx | 27 ++++++++++++++++++++++
 3 files changed, 44 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7801

Fusion-Task-Lineage: e81f15f6-d295-4f6d-b21f-7cf58854457f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 21:19:22 -07:00
gsxdsm
ea1e4fe062 fix: load live workspace plugin over stale staged bundle in dev
Closes the durability gap behind "grok chat returns empty replies". In a source
checkout the dashboard resolved the staged CLI tsup bundle
(packages/cli/dist/plugins/<id>/bundled.js), which resolvePluginEntryPath prefers
verbatim with no freshness check. The FN-7779 dev prebuild rebuilds each plugin's
own plugins/<id>/dist but never the staged tsup bundle, so a source-only plugin
fix ran stale until a manual `pnpm build`.

getCandidatePluginDirs now probes the live workspace source dir (<repo>/plugins/<id>)
before the staged bundle, so dev loads the freshness-checked live plugin (dist-vs-src),
self-healing even when the prebuild is skipped. The global-staged dir stays first,
so published installs (no workspace dir) are unaffected — asserted by the retained
global-install regression test plus a new source-checkout preference test.

Verified on a live dashboard: grok chat streams text and the loader now writes its
reload copies under plugins/fusion-plugin-grok-runtime/dist, not the staged bundle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 21:19:22 -07:00
fusion-merge-train
87b787319f fix(cli): harden dormant PR merge path against stale-base dead-ends
Two failure modes wedged the PR-based merge flow (dormant under the
current direct-merge config, but the rollback target):

1. Empty-diff branches ("No commits between ...") were marked `failed`,
   pinning the serial merge slot + file leases on a task that is a
   legitimate no-op. Now finalized as a terminal DONE via
   `finalizeNoOpMergeTask`, mirroring the engine's canonical
   `noOpResult` decision (merger-ai.ts).

2. A stale-base PR that GitHub reports CONFLICTING never becomes
   mergeable on its own (nothing in the PR path rebases the head), so
   `awaiting-pr-checks` waited unbounded — no escape, because the PR
   path never incremented `mergeRetries`. Now each conflicting poll
   counts against `mergeRetries`, so the existing
   `getInReviewStallReason` "merge-retries-exhausted" escape disposes
   the task after `maxAutoMergeRetries` cycles. Pending/behind PRs
   still wait (checks legitimately run; "behind" is fixed by rebase).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:04:22 -07:00
Phil Larson
3ef90bc6fe fix(core): repair chat session thinkingLevel migration 2026-07-10 21:03:29 -07:00
gsxdsm
93a93450c2 fix(FUX-015): address slash-command review feedback
Greptile/CodeRabbit review of the chat slash-command framework:

- Composer-wipe race: ChatView and TaskPlannerChatTab cleared the composer
  inside the command's success callback, silently wiping any text the user
  typed while the command was in flight. Clear on submit (before the network
  round-trip) instead — consistent with normal chat send, which also clears
  immediately and does not restore on failure.
- Attachments were silently dropped when dispatching a slash command in
  ChatView (clearing the composer revokes staged attachment URLs). Block
  dispatch with a warning toast when attachments are staged.
- CHAT_COMMANDS is now a readonly array; helper signatures accept
  readonly ChatCommand[].
- The planner command menu (commands-only) used skill-menu aria-label/empty
  copy; use command-specific copy instead.

Add regression tests: in-flight text survives command success, composer
clears on submit even on failure, attachment dispatch is blocked, and the
planner command menu uses command-specific accessible copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 18:14:09 -07:00
ddonaldson130
7c91824875 feat(dashboard): add slash-command framework with /steer (FUX-015)
Adds a chat command registry and the /steer command for task-bound
chats. Commands are triggered by '/' and dispatch through a common
match/dispatch path alongside existing skills autocomplete.
2026-07-10 18:14:09 -07:00
gsxdsm
7c26c7e63a fix(dashboard): let Memory tabs use the full view width
Operator follow-up on the centered 960px column: the Memory panes should
stretch to the full width of the view; the editor benefits from the space.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:13:48 -07:00
gsxdsm
c73094ec7f fix(dashboard): fix Memory view dead space, unlabeled toolbar, and toggle-row alignment
- Working/Insights/Engines tabs now render a centered 960px content
  column instead of leaving the right half of wide viewports empty.
- The file editor's collapsed chevron-only toolbar (an unlabeled mystery
  control) now always shows its labeled actions in MemoryView.
- 'Process dreams' / 'Auto-summarize memory' rows share one aligned
  layout, sentence case, and a tooltip explaining dream processing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:13:48 -07:00
gsxdsm
ed163cfc81 fix(FUX-042): use in-scope store resolver for reliability endpoints
The reliability GET/reset handlers referenced getScopedStore, which is
defined inside setupBadgeWebSocket and is not visible in the createServer
scope where these handlers live — so the scoping change did not typecheck.
Switch to the in-scope resolveProjectScopedStore helper (used by the other
realtime endpoints), which also routes through engineManager for correct
per-project resolution.

Guard store resolution with try/catch returning a targeted 500, mirroring
the project SSE handler, instead of falling through to the generic error
handler. Add project-scoping regression tests: GET reads the project store,
GET without projectId falls back to root, and reset writes the project store.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 18:13:27 -07:00
ddonaldson130
47f889a0da fix(dashboard): scope reliability endpoints by projectId (FUX-042)
Reliability GET and reset endpoints were always using the server's
root store, ignoring projectId. Mirror the Command Center pattern by
using getProjectIdFromRequest and getScopedStore so multi-project
servers report per-project reliability stats.

Refs FUX-042
2026-07-10 18:13:27 -07:00
gsxdsm
7ed71ccd37 fix(dashboard): keep selection-comment composer fixed-positioned and inside the viewport
The panel carries the shared .card class, and '.card { position: relative }'
loads after the popover stylesheet, so equal specificity let bundle order
strip the panel's position: fixed — it then flowed inside the documents
viewer and rendered clipped at the bottom-right of the viewport, far from
the selection. A .selection-comment-panel.card rule restores fixed
positioning immune to order; the panel's left is now a width-aware clamp
(no half-offscreen composer near viewport edges), top is clamped near the
bottom, and the textarea focuses with preventScroll so opening the
composer no longer scrolls the selected content out of view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:09:58 -07:00
gsxdsm
f628095205 fix(dashboard): make selection Add-comment trigger clickable and label read-only previews
The global .btn:active press-feedback transform (scale 0.97) overrode
the selection-comment trigger's positioning translate, teleporting the
fixed-position button mid-press so the click never landed and the
composer silently never opened. The :active state now restates the
translate alongside the scale on desktop and mobile offsets. Document
previews also gain a subtle Read-only badge explaining select-to-comment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:09:58 -07:00
gsxdsm
db247557b5 fix(dashboard): restyle quick-add action row as a quiet toolbar
Operator design follow-up: even grouped, nine outlined chips read noisy.
Option controls and the secondary attach/Fast pair are now borderless
ghost buttons in muted text that gain a subtle fill and border on
hover/focus/open, leaving Save as the row's single emphasized control.
Scoped to the quick-entry action row; shared .btn/.dep-trigger rules
untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:05:50 -07:00
gsxdsm
ec1a2eafcf fix(dashboard): organize board quick-add composer and expose task actions menu
- Quick-add composer: single divider, option chips grouped left
  (workflow, priority, steps, deps, models, node, agent, GitHub) and
  primary actions right-aligned (attach, fast, Save last).
- Task cards get a visible kebab button (hover on desktop, always on
  mobile) opening the same context menu as right-click, which was
  previously undiscoverable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:05:50 -07:00
gsxdsm
6adc0095dc fix(dashboard): out-specify global uppercase transform on isolation-mode cards
Live verification showed .setup-wizard-isolation-option (0,1,0) losing
the cascade to the global .form-group label rule (0,1,1), so the cards
still rendered ALL-CAPS. Add a higher-specificity neutralization rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:20:17 -07:00
gsxdsm
cf03d10c1e fix(dashboard): shorten changeset summary to fit 120-char limit
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:20:17 -07:00
gsxdsm
2cfeb74407 fix(dashboard): polish first-run setup wizard (AI step, GitHub step, project step)
- AI Setup: connected providers now render first; 'Skip for now' hides
  once a provider is connected; footer buttons share one size.
- GitHub step: state-driven (no install/pitch content when connected),
  one type scale, real <code> literals, per-OS gh instructions behind a
  disclosure, single skip affordance; gh/OAuth status revalidates on
  window focus and OAuth relogin so later steps stop showing stale
  'not connected'.
- Project step: onboarding-driven opens keep 'Step 3 of 5' context;
  runtime-node picker no longer lists 'Local node' and 'local (local)'
  duplicates and hides itself when only the local node exists;
  isolation-mode cards no longer render a stretched native radio or
  uppercased descriptions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:20:17 -07:00
gsxdsm
e4d404ea74 fix(dashboard): keep GitLab disclosure inside settings gutter and stop footer Save clipping
The GitLab Configuration disclosure was the only .settings-content
child without the form-group horizontal gutter, so its bordered box and
right-flushed checkbox leaked to the pane edge. The footer action rail
was a no-wrap flex with zero slack, clipping Save whenever extra footer
content appeared; it now wraps on desktop (mobile scroll rail intact).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:17:29 -07:00
gsxdsm
5dc3837254 fix(dashboard): dedupe host kind in header connection pill and unify its font size
The connection status pill rendered the host kind twice ('Desktop
Desktop local mode') with mismatched font sizes. The kind is now folded
once into the summary ('Desktop · Local mode') across all pill states.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:13:39 -07:00
gsxdsm
a9d5c0f763 fix: repair Grok CLI dashboard chat (routing + stale bundle)
Grok CLI chat failed two different ways depending on the surface:

1. Default (no-project) chat errored with "requires the bundled Grok CLI
   runtime". The default ChatManager was handed a bare PluginLoader, but Grok
   routing (deriveGrokRuntimeHintForNoVisibleKey -> resolveRuntime) needs a
   PluginRunner's getRuntimeById/createRuntimeContext; the unguarded call threw
   "getRuntimeById is not a function". New resolveChatManagerPluginRunner()
   prefers the engine's PluginRunner (same runner the project-scoped path uses),
   falling back to the loader only in UI-only mode.

2. Project-scoped chat returned empty replies. The CLI-bundled Grok plugin
   (packages/cli/dist/plugins/.../bundled.js, gitignored) was stale vs the
   FN-7796 single-JSON adapter source; the running server loads that bundle,
   not the plugin's own dist. `pnpm build` regenerates it. Noted in the
   changeset that the freshness guard only warns and the dev prebuild does not
   rebuild the CLI tsup bundle.

Verified end-to-end on a live dashboard: both default and project-scoped
grok-cli/grok-4.5 chats now stream thinking + text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 15:36:25 -07:00
gsxdsm
8f7089ecc9 fix(FN-7779): rebuild changed plugins on pnpm dev + warn on stale plugin dist
The Grok stale-dist bug was possible because the dev/build path never
refreshed plugin dist:

- The `client` prebuild (default `pnpm dev dashboard`) rebuilt only
  @fusion/core + @fusion/engine + @fusion/dashboard, never plugins.
- The FN-6638 stale-dist startup warning only scanned packages/, never
  plugins/, so a source-ahead plugin dist ran phantom-old with no warning.

Changes:
- build-workspace.mjs: add `--plugins-only` to plan/build just the plugins
  that changed, reusing the existing content-hash skip cache (cheap no-op when
  unchanged).
- scripts/dev-prebuild-client.mjs: new orchestrator — fast core/engine/
  dashboard build, then incremental changed-plugin rebuild. The `client`
  prebuild now runs this single cross-platform command.
- dist-freshness.mjs: scan plugin roots (plugins/, plugins/examples/) so a
  stale plugin dist is warned like a stale package dist; the warning names the
  plugin dir.

Verified: --plugins-only plans only plugins, skips unchanged on the second
run, and re-plans exactly the one plugin whose source changed. All script and
CLI lib tests pass.

Fusion-Task-Id: FN-7779
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 13:26:23 -07:00
gsxdsm
ee796ee991 fix(FN-7779): rebuild Grok plugin dist so headless prompts use valid CLI flags
The running dashboard loads the compiled plugin dist, which was stale and
invoked `grok --prompt <text> --format json --directory <cwd>` — flags grok
0.2.93 rejects ("unexpected argument '--prompt'"), yielding a non-zero exit,
no JSON, and an empty "No message" bubble. The source already switched to the
valid `grok -p <text> --output-format json [-m <model>] [--cwd <dir>]`
contract (FN-7790/FN-7796); this rebuilds dist to match.

Also reconcile the FN-7779 test suite: a genuinely empty response is a parsed
`{text:"",stopReason:"EndTurn"}` object, not zero stdout bytes, so the
"stays silent" test now models that shape instead of contradicting the
FN-7796 zero-stdout wrong-binary diagnostic. All 64 plugin tests pass.

Fusion-Task-Id: FN-7779
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 13:20:24 -07:00
gsxdsm
c258fc1590 FN-7796: switch Grok CLI headless prompts to the reliable single-object JSON output
Narrative: the streaming-json headless contract intermittently emitted only thought events then stopReason:Cancelled with zero text, leaving Chat replies silently empty; the adapter now spawns grok with --output-format json, buffers stdout, and parses the single JSON response on process close, with streaming-json parsing kept only as a diagnostic fallback.

- Change grok CLI invocation from --output-format streaming-json to --output-format json (cli-stream.ts)
- Add GrokCliJsonResponse type ({text, stopReason, sessionId, requestId, thought}) and parseJsonOutput() to stream-parser.ts, keeping legacy NDJSON line parsing for fallback/diagnostics
- Rework runtime-adapter.ts to buffer full stdout, parse it via parsePromptOutput (JSON object first, NDJSON fallback), and surface a formatTerminalNoTextDiagnostic when a non-EndTurn stopReason yields no assistant text
- Rename first-line/inactivity timeout bookkeeping from line-based to output/chunk-based (FIRST_OUTPUT_TIMEOUT_MS, firstOutputReceived, firstStdoutChunk) since stdout is no longer consumed via readline
- Update cli-stream/runtime-adapter/stream-parser tests to cover the JSON response path and the Cancelled/no-text diagnostic
- Update docs/grok-cli-contract.md and plugin README to document the json output-format contract and diagnostics
- Add changeset fn-7796-grok-cli-reliable-headless.md (patch, fix)

Files changed:
 .changeset/fn-7796-grok-cli-reliable-headless.md   |   7 +
 docs/grok-cli-contract.md                          | 108 ++++++++-----
 plugins/fusion-plugin-grok-runtime/README.md       |  16 +-
 .../src/__tests__/cli-stream.test.ts               |   4 +-
 .../src/__tests__/runtime-adapter.test.ts          |  73 ++++++++-
 .../src/__tests__/stream-parser.test.ts            |  80 +++++----
 .../fusion-plugin-grok-runtime/src/cli-stream.ts   |  14 +-
 .../src/runtime-adapter.ts                         | 180 ++++++++++++---------
 .../src/stream-parser.ts                           |  65 ++++++--
 plugins/fusion-plugin-grok-runtime/src/types.ts    |  13 +-
 10 files changed, 373 insertions(+), 187 deletions(-)

Fusion-Task-Id: FN-7796

Fusion-Task-Lineage: c920fcf0-98f8-42ec-867a-7f76c0aca1b7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 12:57:41 -07:00
gsxdsm
3d5cc0ada4 FN-7795: add thinking-level selectors to fallback model pickers
Adds a per-lane thinking-level selector to every fallback model picker across global, project, and workflow Settings surfaces, wiring them into the existing null-as-delete save paths.

- Add inline thinking-level dropdowns to the global Fallback Model, workflow-declared planning/validator fallback lanes, and the project-scoped Title Summarizer fallback picker via CustomModelDropdown's `showThinkingLevel`.
- Route `planningFallbackThinkingLevel`/`validatorFallbackThinkingLevel` through workflow settings PATCH and `titleSummarizerFallbackThinkingLevel` through project settings save-split, both with null-as-delete semantics on reset.
- Move Title Summarizer fallback out of the workflow-declared model pairs into a dedicated project-scoped lane in ProjectModelsSection so its thinking companion isn't tied to workflow settings.
- Update WorkflowSettingsPanel to surface the new fallback thinking companion keys.
- Extend/adjust tests (settings-save-split, settings-sections, SettingsModal.models-auth, WorkflowSettingsPanel, core settings-migration) to cover the new selectors and save routing.
- Document the fallback thinking-level runtime behavior in docs/settings-reference.md and docs/dashboard-guide.md.
- Add a minor changeset describing the new fallback thinking-level selectors.

Files changed:
 .changeset/fn-7795-fallback-thinking-selectors.md  |   7 +
 docs/dashboard-guide.md                            |   4 +-
 docs/settings-reference.md                         |  10 +-
 packages/core/src/__tests__/settings-migration.test.ts  |  14 +-
 packages/dashboard/app/__tests__/settings-save-split.test.ts      |  35 +++++
 packages/dashboard/app/__tests__/settings-sections.test.tsx       | 163 ++++++++++++++++++++-
 packages/dashboard/app/components/WorkflowSettingsPanel.tsx       |   6 +-
 packages/dashboard/app/components/__tests__/SettingsModal.models-auth.test.tsx   |  11 +-
 packages/dashboard/app/components/__tests__/WorkflowSettingsPanel.test.tsx       |  38 +++++
 packages/dashboard/app/components/settings/save-split.ts          |   9 +-
 packages/dashboard/app/components/settings/sections/GlobalModelsSection.tsx      |   8 +-
 packages/dashboard/app/components/settings/sections/ProjectModelsSection.tsx     |  58 ++++++--
 12 files changed, 328 insertions(+), 35 deletions(-)

Fusion-Task-Id: FN-7795

Fusion-Task-Lineage: ec990d47-defe-4717-993a-56988afe8d7d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 12:02:46 -07:00
gsxdsm
fc4acd4d66 FN-7794: apply fallback model's own thinking level when swapping in at runtime
Adds fallbackThinkingLevel plumbing so, when Fusion swaps from a primary model to a configured fallback model (executor, validator/reviewer, merger, planning, title-summarizer, heartbeat, and workflow-step lanes), the fallback's own configured thinking level is applied instead of silently reusing the primary lane's level.

- Add fallbackThinkingLevel option to AgentRuntimeOptions (agent-runtime.ts), AgentOptions (pi.ts), and ReviewOptions (reviewer.ts)
- Add per-lane resolvers: resolveExecutorFallbackThinkingLevel, resolvePlanningFallbackThinkingLevel, resolveValidatorFallbackThinkingLevel, resolveTitleSummarizerFallbackThinkingLevel, resolveMergerFallbackThinkingLevel (agent-session-helpers.ts), each following fallback-provider precedence and falling back to the primary lane/default thinking level when unset
- Export new resolvers from packages/engine/src/index.ts
- Apply the resolved fallback thinking level in createFnAgent's applyThinkingLevelIfSupported once a session has swapped to the fallback model (pi.ts)
- Wire fallbackThinkingLevel through executor session creation (workflow-step, task validator, child-agent, and main executor session paths), merger session creation, and heartbeat session creation
- Promote the fallback thinking level alongside the fallback model/provider when the no-visible-key Grok CLI fallback is promoted to primary, so the cleared fallback pair doesn't leave the session on the superseded primary's thinking level
- Route workflow-step fallback thinking level by which fallback candidate (validatorFallback vs globalFallback) actually matched
- Document fallbackThinkingLevel runtime-swap behavior in docs/settings-reference.md
- Add minor changeset for @runfusion/fusion
- Add regression tests covering fallback thinking-level resolution and application (agent-session-helpers.test.ts, pi.test.ts) and a shared test helper (executor-test-helpers.ts)

Files changed:
 .changeset/fn-7794-fallback-thinking-level.md      |   7 ++
 docs/settings-reference.md                         |   3 +
 .../src/__tests__/agent-session-helpers.test.ts    |  38 ++++++
 .../engine/src/__tests__/executor-test-helpers.ts  |  23 ++++
 packages/engine/src/__tests__/pi.test.ts           | 136 +++++++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             |   3 +-
 packages/engine/src/agent-runtime.ts               |   5 +
 packages/engine/src/agent-session-helpers.ts       |  54 ++++++++
 packages/engine/src/executor.ts                    |  31 ++++-
 packages/engine/src/index.ts                       |   5 +
 packages/engine/src/merger.ts                      |   7 +-
 packages/engine/src/pi.ts                          |  16 ++-
 packages/engine/src/reviewer.ts                    |   6 +
 13 files changed, 327 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7794

Fusion-Task-Lineage: c94d621a-ccbd-42b2-9fe6-cb619418ad90

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 11:53:07 -07:00
gsxdsm
de67b57517 FN-7791: bridge image task attachments into the artifact registry
Image attachments added via TaskStore.addAttachment now surface as first-class image artifacts, reusing the existing artifact listing/SSE/media pipeline instead of duplicating bytes.

- addAttachment() registers a URI-only "image" artifact (metadata.source: "attachment") pointing at the already-written attachments/<file> path whenever an image mimeType is attached; registration is best-effort and swallows the archived/soft-deleted-task rejection so addAttachment keeps its always-succeeds contract for valid images.
- deleteAttachment() now removes any bridged artifact rows for a filename before deleting the attachment file, so /api/artifacts/:id/media can never point at a deleted attachment.
- register-task-workflow-routes.ts's resolveArtifactMediaPath now accepts task-scoped attachments/<file> URIs (in addition to artifacts/<file>) so the media route can stream bridged image-attachment artifacts; task-less artifacts remain restricted to .fusion/artifacts/.
- docs/storage.md documents the attachment→artifact bridge behavior and the media route's accepted URI prefixes.
- Added a changeset (@runfusion/fusion: minor) describing the user-facing Artifacts view change.
- Extended store-attachments and artifacts-route-integration tests to cover the new bridging and deletion behavior.

Files changed:
 .changeset/fn-7791-image-attachments-artifacts.md  |  7 +++
 docs/storage.md                                    |  3 +-
 packages/core/src/__tests__/store-attachments.test.ts   | 59 +++++++++++++++++++++-
 packages/core/src/store.ts                         | 58 ++++++++++++++++++++-
 packages/dashboard/src/routes/__tests__/artifacts-route-integration.test.ts  | 40 +++++++++++++++
 packages/dashboard/src/routes/register-task-workflow-routes.ts    |  9 +++-
 6 files changed, 172 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7791

Fusion-Task-Lineage: 4df47880-6161-4a8b-933a-2f6fc2fed953

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 11:30:21 -07:00
gsxdsm
84fb5138fc FN-7793: add fallback-lane thinking-level settings foundation
Adds the schema/storage foundation for per-fallback-lane thinking-level settings, without any runtime or UI consumption yet.

- Add global `fallbackThinkingLevel` default settings key.
- Add workflow-declared `planningFallbackThinkingLevel` and `validatorFallbackThinkingLevel` companion settings (enum-validated against THINKING_LEVELS) alongside their existing fallback provider/model pairs.
- Add project-scoped `titleSummarizerFallbackThinkingLevel` default settings key.
- Extend `GlobalSettings`/`ProjectSettings` TypeScript interfaces with the new optional `ThinkingLevel` fields and FNXC documentation comments.
- Update settings-schema `MovedProjectSettingsKey` type to include the new workflow-moved thinking-level keys.
- Update `docs/settings-reference.md` and `docs/storage.md` to document the new keys and correct workflow-vs-project scope for the pre-existing planning/validator fallback provider/model keys.
- Extend `effective-settings-overlay` and `settings-parity` unit tests to cover the new keys' scope and inheritance behavior.
- Add changeset `.changeset/fallback-thinking-levels.md` (minor).

Files changed:
 .changeset/fallback-thinking-levels.md             |  7 +++++++
 docs/settings-reference.md                         |  4 ++++
 docs/storage.md                                    | 12 +++++++----
 .../__tests__/effective-settings-overlay.test.ts   |  6 ++++++
 .../core/src/__tests__/settings-parity.test.ts     | 10 ++++++++++
 packages/core/src/builtin-workflow-settings.ts     | 23 +++++++++++++++++++++-
 packages/core/src/settings-schema.ts               | 10 +++++++++-
 packages/core/src/types.ts                         | 12 +++++++++++
 8 files changed, 78 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7793
Fusion-Task-Lineage: 0e4bac3e-01d2-4d17-9f0a-af87e8a66eee
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 11:26:16 -07:00
gsxdsm
db9b9d22c4 FN-7790: align grok CLI runtime adapter with real xAI grok CLI contract
Align the Grok CLI runtime plugin adapter with the real xAI grok CLI stream contract so tool responses are no longer silently dropped, and update tests/docs to match.

- Rework stream-parser.ts to parse the actual grok CLI event/message shape instead of the previously assumed schema
- Trim runtime-adapter.ts and types.ts down to the fields the real CLI contract emits, removing speculative/unsupported fields
- Update cli-stream.ts to match the corrected event handling
- Rewrite runtime-adapter, stream-parser, and cli-stream test suites to exercise the real CLI contract end-to-end
- Update docs/grok-cli-contract.md and plugin README to document the verified contract
- Add changeset for the grok-runtime plugin fix

Files changed:
 .changeset/fn-7790-grok-cli-real-contract.md       |   7 +
 docs/grok-cli-contract.md                          | 432 +++++++--------------
 plugins/fusion-plugin-grok-runtime/README.md       | 149 ++-----
 .../src/__tests__/cli-stream.test.ts               |  22 +-
 .../src/__tests__/runtime-adapter.test.ts          | 250 ++++--------
 .../src/__tests__/stream-parser.test.ts            | 108 ++----
 .../fusion-plugin-grok-runtime/src/cli-stream.ts   |  22 +-
 .../src/runtime-adapter.ts                         | 109 ++----
 .../src/stream-parser.ts                           |  27 +-
 plugins/fusion-plugin-grok-runtime/src/types.ts    | 104 +----
 10 files changed, 338 insertions(+), 892 deletions(-)

Fusion-Task-Id: FN-7790

Fusion-Task-Lineage: 377e86c4-c005-46a5-9eb4-69e858c38b79

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 10:57:04 -07:00
gsxdsm
d80cdd2b3b FN-7787: honor assigned agent's runtimeConfig model in task execution sessions
Task execution sessions previously ignored the assigned permanent agent's
runtimeConfig model whenever the executor was handed an agents-less
worktree AgentStore, silently drifting to the pi runtime's built-in
default model instead of the configured one.

- Add TaskExecutor.getAuthoritativeAssignedAgent(): falls back to the
  authoritative project `.fusion` AgentStore when the live executor's
  worktree AgentStore has no record of the assigned agent, so
  runtimeConfig resolution matches chat-session behavior.
- Replace direct `this.options.agentStore.getAgent(...)` lookups across
  step-session, workflow-graph, and legacy execution paths with the new
  authoritative lookup helper.
- Warn and audit (`noModelResolved` / `runtimeBuiltInFallbackModel`) when
  a non-mock, non-test-mode session resolves no provider/model pair and
  falls back to the runtime's built-in default, so the drift is visible
  instead of silent.
- Add regression tests covering assigned-agent runtime-config resolution
  and the new runtime-resolved audit fields.
- Add changeset (patch) and update docs/settings-reference.md and
  AGENTS.md.

Files changed:
 .changeset/fuzzy-fable-fallback.md                 |  7 +++
 AGENTS.md                                          |  1 +
 docs/settings-reference.md                         |  2 +-
 .../executor-assigned-agent-runtime-config.test.ts | 68 ++++++++++++++++++++++
 .../run-audit-session-runtime-resolved.test.ts     | 44 ++++++++++++++
 packages/engine/src/agent-session-helpers.ts       | 31 +++++++---
 packages/engine/src/executor.ts                    | 43 +++++++++-----
 7 files changed, 174 insertions(+), 22 deletions(-)

Fusion-Task-Id: FN-7787

Fusion-Task-Lineage: 40fccad5-2e67-4ee2-8199-4548ce9025c6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 10:13:38 -07:00
gsxdsm
30bd7790e7 FN-7788: diagnose zero-NDJSON Grok CLI headless exits as a real failure
Fixes the residual "Grok CLI still returns no message immediately" case where a headless run exits code 0 but never emits any parsed NDJSON event, previously treated as a silent success.

- Detect a code-0 close with zero parsed NDJSON events and surface a diagnostic explaining the likely cause (wrong/unsupported grok binary falling into interactive mode and hitting EOF on stdin).
- Track and emit assistant text/diagnostics via a new appendMessage/emitDiagnosticText path so onText and session.state.errorMessage stay in sync, including on spawn failure and inactivity/first-line timeouts.
- Add first-line/inactivity timeout diagnostics with concrete elapsed-time messaging instead of silent kills.
- Add regression coverage in runtime-adapter.test.ts and grok-runtime-routing.test.ts for the zero-NDJSON exit path.
- Document the contract update in docs/grok-cli-contract.md.
- Add a patch changeset for @runfusion/fusion.

Files changed:
$(cat /tmp/diffstat_fn7788.txt)

Fusion-Task-Id: FN-7788

Fusion-Task-Lineage: dbb238a9-9601-47fc-8a88-40817d749337

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 10:01:49 -07:00
gsxdsm
dd82a601be FN-7789: fix thinking-level select styling in portaled model dropdowns
Rescopes the Thinking Level <select> dark-theme CSS to the createPortal dropdown container so it renders styled everywhere CustomModelDropdown is used, not just under the trigger wrapper.

- Change .thinking-level-select CSS selectors from .model-combobox to .model-combobox-dropdown scope (base, hover, focus, disabled, option, optgroup rules)
- Update test regexes/assertions to match the new .model-combobox-dropdown scope and assert the old .model-combobox scope no longer appears
- Add assertion that the thinking select renders inside .model-combobox-dropdown
- Add changeset (patch) documenting the fix

Files changed:
 .changeset/fn-7789-thinking-level-select-portal-scope.md  |  7 +++++++
 packages/dashboard/app/components/CustomModelDropdown.css | 15 +++++++++------
 .../app/components/__tests__/CustomModelDropdown.test.tsx |  6 ++++--
 3 files changed, 20 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-7789

Fusion-Task-Lineage: 5c3addff-a672-42b1-94a9-11c0f235bb57

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 09:54:39 -07:00
gsxdsm
cc8b1b69e0 FN-7786: price analytics from token-usage model snapshot to fix empty estimated cost
Estimated cost on the dashboard could render empty because team/workflow analytics priced rows using the legacy task.modelProvider/modelId columns, which can be NULL even when a durable tokenUsageModelProvider/tokenUsageModelId snapshot exists on the row.

- Select tokenUsageModelProvider/tokenUsageModelId alongside the legacy model columns in the team-analytics and workflow-analytics task token queries
- Price each row using the token-usage model snapshot first, falling back to the legacy model columns, so cost survives model-resolution drift and empty legacy columns
- Add regression coverage in team-analytics, workflow-analytics, and model-pricing tests, plus a dashboard TaskDetailModal summary-tab test asserting estimated cost renders
- Add a patch changeset documenting the fix for @runfusion/fusion

Files changed:
 .changeset/fn-7786-estimated-cost.md               |  7 +++++
 packages/core/src/__tests__/model-pricing.test.ts  | 10 +++++++
 packages/core/src/__tests__/team-analytics.test.ts | 34 ++++++++++++++++++++--
 .../core/src/__tests__/workflow-analytics.test.ts  | 33 +++++++++++++++++++--
 packages/core/src/team-analytics.ts                | 15 ++++++++--
 packages/core/src/workflow-analytics.ts            | 15 ++++++++--
 .../__tests__/TaskDetailModal.summary-tab.test.tsx | 34 ++++++++++++++++++++++
 7 files changed, 140 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-7786

Fusion-Task-Lineage: dab32809-4bb8-42c0-aed0-a0c1131ec641

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:33:42 -07:00
gsxdsm
4d4e9ad5cb FN-7785: reduce margin and padding across mobile Settings pages
Tightens spacing on the Settings modal's mobile (≤768px) layout so pages feel less loose on narrow viewports, and updates mobile settings tests to match.

- Reduce .settings-content scroll container padding and cut section-heading/margin/padding tokens from --space-lg/--space-md down to --space-sm across settings-note, remote-status-bar, remote-share-block, remote-cli-detection, remote-external-tunnel-panel, notification-provider-card, settings-scope-banner, memory-file-summary, settings-description, auth-* rows/cards/hints, and settings-preset-* controls
- Add tighter padding for settings-empty-state and theme-selector, and restructure settings-model-lane-actions spacing (margin-block + padding)
- Update settings-mobile.test.tsx assertions to reflect the new compact spacing values
- Add changeset (patch) documenting the mobile Settings spacing fix

Files changed:
 .changeset/fn-7785-mobile-settings-spacing.md      |  7 +++
 .../dashboard/app/components/SettingsModal.css     | 73 +++++++++++++---------
 .../components/__tests__/settings-mobile.test.tsx  | 43 +++++++++----
 3 files changed, 82 insertions(+), 41 deletions(-)

Fusion-Task-Id: FN-7785

Fusion-Task-Lineage: ca9332ea-782d-41de-ba51-010caea9489e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:26:41 -07:00
gsxdsm
59a798bc0d FN-7784: keep mobile chat header controls in sync with visible pane
Fix Chat header showing thread controls (session switcher, back button, room header) while the conversation list is still displayed on mobile after re-entering Chat, since restored session/room state no longer implied the detail pane was visible.

- Derive mobileThreadPaneOpen from actual sidebar visibility (!sidebarVisible) combined with active thread/room state instead of relying solely on restored session/room presence.
- Gate showMobileSessionSwitcher, showMobileDirectThreadHeaderControls, and new showMobileRoomThreadHeaderControls off the corrected mobileThreadPaneOpen signal.
- Wrap the room thread header in showMobileRoomThreadHeaderControls so it only renders when the detail pane is actually shown.
- Add regression coverage in ChatView.mobile.test.tsx and ChatView.swipe-back.test.tsx for the list-shown-after-remount case.
- Add changeset documenting the fix.

Files changed:
 .changeset/fn-7784-chat-header-list-consistency.md |   7 +
 packages/dashboard/app/components/ChatView.tsx     |  18 ++-
 .../components/__tests__/ChatView.mobile.test.tsx  | 141 +++++++++++++++++++--
 .../__tests__/ChatView.swipe-back.test.tsx         |  74 +++++++++--
 4 files changed, 214 insertions(+), 26 deletions(-)

Fusion-Task-Id: FN-7784

Fusion-Task-Lineage: e2571efa-dff9-43ef-af09-f7d2ce32788b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:21:52 -07:00
gsxdsm
a32307f8f1 FN-7778: resolve plugin-contributed skills per requesting project instead of daemon root
Fixes plugin skills silently disappearing when the fn daemon is started outside the project that enabled the contributing plugin, by making skill resolution project-aware instead of scoped to the daemon's root PluginLoader.

- getPluginSkills now resolves per requesting rootDir against project_plugin_states rather than the daemon-root PluginLoader scope
- Plugins skipped as disabled are now logged at load time for visibility
- Wired the new project-aware resolution through dashboard.ts, serve.ts, and daemon.ts CLI commands
- Added regression coverage in plugin-loader.test.ts and skills-adapter.test.ts
- Documented the project-scoped behavior in docs/PLUGIN_AUTHORING.md and docs/agents.md
- Added a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7778-plugin-skills-project-scope.md  |  7 +++
 docs/PLUGIN_AUTHORING.md                           |  2 +
 docs/agents.md                                     |  2 +-
 packages/cli/src/commands/daemon.ts                | 68 +++++++++++++++++++--
 packages/cli/src/commands/dashboard.ts             | 71 ++++++++++++++++++++--
 packages/cli/src/commands/serve.ts                 | 68 +++++++++++++++++++--
 packages/core/src/__tests__/plugin-loader.test.ts  | 69 +++++++++++++++++++++
 packages/core/src/plugin-loader.ts                 | 29 ++++++---
 .../dashboard/src/__tests__/skills-adapter.test.ts | 29 +++++++++
 packages/dashboard/src/skills-adapter.ts           | 19 ++++--
 10 files changed, 337 insertions(+), 27 deletions(-)

Fusion-Task-Id: FN-7778
Fusion-Task-Lineage: 5d9a8ff2-ed0e-4859-bf9c-a16f715b081d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:18:47 -07:00
gsxdsm
2e97395cf3 FN-7782: surface Grok CLI failures instead of silent empty replies
Root-causes the Grok CLI empty-response bug: readline close no longer finalizes the session before the subprocess close event can attach exit code/stderr diagnostics, so failures were silently swallowed as empty assistant messages.

- Wait for subprocess close/error (not readline close) to finalize the Grok CLI session, so non-zero exits can attach stderr before callers inspect the result
- Add GrokSession.state.errorMessage to carry concrete diagnostics (spawn failure, process error, non-zero exit + stderr, or NDJSON error event) through the resolve-never-reject runtime contract
- Track whether any text was received so error diagnostics are only recorded when the run actually produced nothing
- Add a changeset documenting the fix for @runfusion/fusion
- Extend runtime-adapter tests to cover spawn failure, process error, non-zero exit with/without stderr, and NDJSON error-event diagnostics

Files changed:
 .changeset/fn-7782-grok-cli-no-response.md         |  7 ++
 .../src/__tests__/runtime-adapter.test.ts          | 98 ++++++++++++++++++++--
 .../src/runtime-adapter.ts                         | 75 +++++++++++++----
 plugins/fusion-plugin-grok-runtime/src/types.ts    |  1 +
 4 files changed, 161 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-7782

Fusion-Task-Lineage: c2907a70-0556-488f-bda3-132657b64071

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:11:43 -07:00
gsxdsm
03073afa85 FN-7783: add Fable weekly usage window to Usage dropdown
Adds parsing and display support for a Claude "Weekly (Fable)" usage window alongside existing Sonnet/Opus per-model windows.
- fetchClaudeUsage parses seven_day_fable (with tolerant fallback keys: seven_day_claude_fable, fable, seven_day_fable_5) and pushes a Fable usage window
- fetchClaudeUsageViaCli recognizes a "Current week (Fable" CLI section and renders it as "Weekly (Fable)"
- Adds regression tests covering both API and CLI parsing paths
- Adds changeset documenting the new minor feature

Files changed:
 .changeset/fn-7783-fable-usage.md              |   7 ++
 packages/dashboard/src/__tests__/usage.test.ts | 117 ++++++++++++++++++++++++-
 packages/dashboard/src/usage.ts                |  15 +++-
 3 files changed, 134 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7783

Fusion-Task-Lineage: 0ed79ed5-15f3-4b6a-a443-d99653e72448

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:09:12 -07:00
gsxdsm
f5fd8b84f8 FN-7780: move agent-created badge to bottom row on task cards
Moves the agent-created badge out of the task card header's meta-badges row into its own bottom row so it no longer causes header wrapping.

- Moved .card-agent-created-badge out of .card-meta-badges into a new .card-agent-badge-row in TaskCard.tsx
- Updated hasCardMetaBadges guard logic and TaskCard.css to support the new bottom row layout
- Added TaskCard.badge-wrap.test.tsx regression coverage and extended TaskCard.test.tsx
- Updated docs/dashboard-guide.md
- Added changeset fn-7780-agent-badge-bottom-row.md (patch, fix category)

Files changed:
 .changeset/fn-7780-agent-badge-bottom-row.md       |  7 +++
 docs/dashboard-guide.md                            |  3 +-
 packages/dashboard/app/components/TaskCard.css     | 12 ++++
 packages/dashboard/app/components/TaskCard.tsx     | 29 ++++++----
 .../__tests__/TaskCard.badge-wrap.test.tsx         | 66 ++++++++++++++++++++++
 .../app/components/__tests__/TaskCard.test.tsx     | 34 ++++++++---
 6 files changed, 131 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-7780

Fusion-Task-Lineage: 6a6cd9f6-a9ce-488f-969d-520459c8631b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:06:08 -07:00
gsxdsm
5729fe292c FN-7781: add optional workflow step toggles to task edit form
Enables editing a task's optional workflow steps directly from TaskForm edit mode, sourcing the step catalog from the resolved task workflow instead of re-seeding from defaultOn.

- TaskForm loads optional-step catalog from the task's resolved workflow when editing and exposes toggles for enabling/disabling optional steps
- TaskDetailModal passes through the additional workflow context needed for edit-mode step toggling
- Added changeset for @runfusion/fusion (minor)
- Updated docs/dashboard-guide.md to describe the new edit-mode workflow step behavior
- Added test coverage in TaskForm.test.tsx and TaskDetailModal.models-progress-workflow.test.tsx

Files changed:
 .changeset/fn-7781-edit-workflow-steps.md          |  7 ++
 docs/dashboard-guide.md                            |  5 +-
 .../dashboard/app/components/TaskDetailModal.tsx   |  6 ++
 packages/dashboard/app/components/TaskForm.tsx     | 76 +++++++++++++-----
 ...skDetailModal.models-progress-workflow.test.tsx | 90 ++++++++++++++++++++++
 .../app/components/__tests__/TaskForm.test.tsx     | 51 ++++++++++++
 6 files changed, 214 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-7781

Fusion-Task-Lineage: e44d9383-cfe7-4da1-9f20-8191211651cf

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:03:35 -07:00
gsxdsm
035caca6c8 FN-7775: Add thinking level selection to new chat model picker
Adds a per-session thinking-level selector to the new chat model dropdown, persisting the choice through the chat store and engine session options.

- Adds thinkingLevel column to chat_sessions with a db migration
- Extends chat-store, chat-types, and chat.ts to read/write thinkingLevel
- ChatView model selector now exposes a thinking-level control alongside model choice
- useChat and register-chat-routes plumb thinkingLevel through session creation/API
- engine/src/index.ts passes thinkingLevel as defaultThinkingLevel session option
- Adds a minor changeset and updates settings-reference/dashboard-guide docs
- Adds/updates unit tests across core and dashboard packages

Files changed:
 .changeset/fn-7775-chat-thinking-level.md          |  7 ++
 docs/dashboard-guide.md                            |  1 +
 docs/settings-reference.md                         |  2 +-
 packages/core/src/__tests__/chat-store.test.ts     | 15 +++-
 packages/core/src/__tests__/db-migrate.test.ts     | 33 ++++++++
 packages/core/src/chat-store.ts                    | 12 ++-
 packages/core/src/chat-types.ts                    |  6 ++
 packages/core/src/db.ts                            | 14 +++-
 packages/dashboard/app/api/legacy.ts               |  2 +-
 packages/dashboard/app/components/ChatView.tsx     | 16 +++-
 .../__tests__/ChatView.core-interactions.test.tsx  | 75 +++++++++++++++---
 .../dashboard/app/hooks/__tests__/useChat.test.ts  | 17 +++-
 packages/dashboard/app/hooks/useChat.ts            |  6 +-
 .../dashboard/src/__tests__/chat-manager.test.ts   | 90 ++++++++++++++++++++++
 packages/dashboard/src/chat.ts                     | 23 ++++++
 .../dashboard/src/routes/register-chat-routes.ts   | 25 +++++-
 packages/engine/src/index.ts                       |  1 +
 17 files changed, 318 insertions(+), 27 deletions(-)

Fusion-Task-Id: FN-7775

Fusion-Task-Lineage: e16c7d3b-361e-4908-87ad-10be17a47c47

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 08:00:34 -07:00
gsxdsm
57c3d7ceb8 FN-7776: evaluate promptContribution.condition against effective plugin settings
Plugin prompt contributions declared a `condition` field in the SDK, but the host never evaluated it, so gated prompt content always rendered unconditionally.

- Add packages/core/src/plugin-prompt-condition.ts implementing a minimal, non-eval `settings["key"] === "value"` / `!==` condition grammar
- Wire condition evaluation into plugin-runner.ts / agent-instructions.ts / executor.ts / reviewer.ts / triage.ts / agent-heartbeat.ts so prompt contributions are filtered by effective plugin settings at each call site
- Extend plugin-types.ts and core index.ts/index.gate.ts to expose the new evaluator and condition typing
- Document the condition grammar in docs/PLUGIN_AUTHORING.md
- Add regression tests covering the evaluator and its wiring through plugin-runner and agent-instructions
- Add changeset (@runfusion/fusion minor, feature) describing the new gating behavior

Files changed:
 .changeset/fn-7776-prompt-condition.md             |  7 ++
 docs/PLUGIN_AUTHORING.md                           | 13 ++-
 .../src/__tests__/plugin-prompt-condition.test.ts  | 90 ++++++++++++++++++++
 packages/core/src/index.gate.ts                    |  6 ++
 packages/core/src/index.ts                         |  6 ++
 packages/core/src/plugin-prompt-condition.ts       | 51 +++++++++++
 packages/core/src/plugin-types.ts                  | 10 ++-
 .../src/__tests__/agent-instructions.test.ts       | 33 ++++++--
 .../engine/src/__tests__/plugin-runner.test.ts     | 98 +++++++++++++++++++++-
 packages/engine/src/agent-heartbeat.ts             |  2 +-
 packages/engine/src/agent-instructions.ts          |  6 +-
 packages/engine/src/executor.ts                    | 28 +++++--
 packages/engine/src/plugin-runner.ts               | 64 ++++++++++++--
 packages/engine/src/reviewer.ts                    |  2 +-
 packages/engine/src/triage.ts                      |  2 +-
 packages/plugin-sdk/src/index.ts                   |  4 +
 16 files changed, 385 insertions(+), 37 deletions(-)

Fusion-Task-Id: FN-7776

Fusion-Task-Lineage: ba8dcd52-260a-4166-a712-f3dd39b81b15

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 07:46:52 -07:00
gsxdsm
f9641ec76e FN-7774: style the Thinking Level select to match the dark model dropdown
Adds dark-theme CSS styling for the inline Thinking Level <select> so it no longer renders as an OS-default white control across model-picker surfaces, including the quick-add QuickEntryBox and InlineCreateCard popups.

- Add .thinking-level-select rule set in CustomModelDropdown.css mirroring the canonical dark select tokens (background, border, color, focus ring, disabled state, option/optgroup styling)
- Add a custom dropdown chevron indicator via .model-combobox-thinking::after and make the container position: relative
- Add a regression test asserting the select and option rules use the dark theme CSS tokens
- Add a patch changeset for @runfusion/fusion documenting the fix

Files changed:
 .changeset/fn-7774-thinking-level-select-styling.md           |  7 +++
 packages/dashboard/app/components/CustomModelDropdown.css     | 59 ++++++++++++++++++++++
 packages/dashboard/app/components/__tests__/CustomModelDropdown.test.tsx | 16 ++++++
 3 files changed, 82 insertions(+)

Fusion-Task-Id: FN-7774

Fusion-Task-Lineage: 9ef9bf07-dd18-499c-b04d-1b137d200689

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 07:43:08 -07:00