Commit Graph

10953 Commits

Author SHA1 Message Date
gsxdsm
71e9f484bb FN-7716: stop requiring a Fusion-visible API key for Grok CLI provider
Grok CLI provider readiness now mirrors the Cursor CLI provider: it is derived from the `grok` binary being available rather than requiring a Fusion-visible GROK_API_KEY or ~/.grok/user-settings.json, since the CLI manages its own auth.

- probeGrokBinary now derives `authenticated` from binary availability (readiness) instead of API-key/user-settings presence; key detection surfaces as a non-blocking `apiKeyDetected` hint
- /auth/status treats the grok-cli provider as authenticated when enabled + binary available
- GrokCliProviderCard drops the blocking "Set GROK_API_KEY" state
- Direct xAI streaming path is unchanged and still uses $GROK_API_KEY when present (FN-7711/FN-7714)
- Added changeset for @runfusion/fusion (patch)

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

Fusion-Task-Id: FN-7716

Fusion-Task-Lineage: ac0efc79-2510-465e-9cd2-4938c08989c9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
c8fcbec94f FN-7717: release active-session locks when a task is archived
Archiving a task from triage/planning/todo (not just in-progress) previously left leaked active-session-registry entries, so a successor task could hit ActiveSessionPathHeldByForeignTaskError and get blocked from Plan Review.

- Add an explicit `to === "archived"` branch in the task-move handler that awaits abort of in-flight task work and sweeps any leftover activeSessionRegistry paths for the task, checked before the narrower `from === "in-progress"` branch so direct in-progress→archived transitions are covered too.
- Deliberately exclude `to === "done"` / `to === "in-review"` from this sweep since those columns legitimately hold ai-merge / workspace-repo-land merge leases that must survive the transition.
- Add regression test coverage for archive releasing active sessions across originating columns.
- Add changeset and architecture doc note.

Files changed:
 .../fn-7717-archive-active-session-release.md      |   7 +
 docs/architecture.md                               |   1 +
 ...xecutor-archive-releases-active-session.test.ts | 167 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |  35 +++++
 4 files changed, 210 insertions(+)

Fusion-Task-Id: FN-7717

Fusion-Task-Lineage: 7cff6821-7bb3-4b75-b502-a26467ca7f51

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
b2613b7132 FN-7714: honor ~/.grok/user-settings.json apiKey when GROK_API_KEY is unset
Fall back to the Grok CLI's user-settings file for the API key so pi's $GROK_API_KEY provider reference resolves even when the env var isn't exported.

- Add hydrateGrokApiKeyFromUserSettings() in grok-provider.ts, called from registerBuiltInGrokProvider(), which hydrates process.env.GROK_API_KEY from ~/.grok/user-settings.json { apiKey } only when the env var is unset/empty
- Env var always wins; a missing (ENOENT), malformed, or empty-apiKey settings file is fail-soft (no throw, no env mutation), mirroring the grok-runtime probe's fallback behavior
- Add regression tests covering env-precedence, fallback hydration, and fail-soft error paths (grok-provider-user-settings.test.ts)
- Document the fallback in docs/settings-reference.md
- Add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7714-grok-user-settings-apikey.md    |   7 +
 docs/settings-reference.md                         |   2 +-
 .../__tests__/grok-provider-user-settings.test.ts  | 156 +++++++++++++++++++++
 packages/core/src/grok-provider.ts                 |  47 +++++++
 4 files changed, 211 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7714

Fusion-Task-Lineage: 5450b480-3a32-4331-9494-867b84605464

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
335dfc3bec FN-7715: clarify GrokRuntimeAdapter no-op stub with intent documentation
Documents that GrokRuntimeAdapter.promptWithFallback is an intentional no-op rather than unfinished work, and updates its regression test to assert that contract explicitly.

- Add FNXC:GrokCli comment on promptWithFallback explaining Grok streaming already flows through the pi/xAI OpenAI-compatible path from FN-7711, that the grok CLI has no documented non-interactive prompt/stream subcommand, and that this stub is only reached via an unused runtimeConfig.runtimeHint === "grok" path
- Remove the stale TODO(FN-7705) comment
- Rename/expand the promptWithFallback test to assert the intentional no-op contract (resolves without throwing, returns undefined)

Files changed:
 .../src/__tests__/runtime-adapter.test.ts              | 11 ++++++++++-
 .../fusion-plugin-grok-runtime/src/runtime-adapter.ts  | 18 ++++++++++++++++--
 2 files changed, 26 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7715

Fusion-Task-Lineage: 118639d3-5530-45d5-bc66-de9b1f18fbc4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
7dc271027f FN-7711: add built-in Grok CLI provider to fix pi model registry lookup
Registers a built-in grok-cli provider so Grok CLI model executions no longer hard-fail with "not found in the pi model registry".

- Add packages/core/src/grok-provider.ts: built-in grok-cli provider config (xAI OpenAI-compatible endpoint https://api.x.ai/v1, api openai-completions, apiKey $GROK_API_KEY), mirroring the existing Z.ai provider
- Register the provider in packages/engine/src/pi.ts (registerExtensionProviders) and packages/engine/src/provider-registration.ts (seedDashboardProviders)
- Wire the provider into CLI entrypoints: packages/cli/src/commands/daemon.ts, dashboard.ts, serve.ts
- Export grok-provider from packages/core/src/index.ts and packages/core/src/index.gate.ts
- Add unit tests: packages/core/src/__tests__/grok-provider.test.ts, and extend packages/engine/src/__tests__/pi-create-fn-agent.test.ts and provider-registration.test.ts
- Document the new provider in docs/settings-reference.md
- Add changeset .changeset/fn-7711-grok-cli-model-registry.md (patch, category: fix)

Note: Grok CLI binary remains discovery/probe only; GrokRuntimeAdapter streaming is a stub (tracked follow-up).

Files changed:
 .changeset/fn-7711-grok-cli-model-registry.md      |   7 +
 docs/settings-reference.md                         |   2 +
 packages/cli/src/commands/daemon.ts                |   4 +
 packages/cli/src/commands/dashboard.ts             |   4 +
 packages/cli/src/commands/serve.ts                 |   4 +
 packages/core/src/__tests__/grok-provider.test.ts  | 130 ++++++++++++
 packages/core/src/grok-provider.ts                 | 224 +++++++++++++++++++++
 packages/core/src/index.gate.ts                    |   7 +
 packages/core/src/index.ts                         |   7 +
 .../src/__tests__/pi-create-fn-agent.test.ts       |  78 ++++++-
 .../src/__tests__/provider-registration.test.ts    |   4 +-
 packages/engine/src/pi.ts                          |   4 +
 packages/engine/src/provider-registration.ts       |   4 +
 13 files changed, 476 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7711

Fusion-Task-Lineage: ae90b54f-206e-46fd-8365-b0a4488ceb84

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
6cff782308 FN-7710: refresh model caches so Grok/Cursor CLI models appear without reopening Settings
Adds a shared single-flight cache refresh so newly enabled Grok/Cursor CLI providers show their models in pickers immediately, instead of requiring a Settings reopen.

- useModelsCache now exposes a shared refreshModelsCache() that clears the SWR MODELS cache key and notifies subscribers
- AuthenticationSection calls refreshModelsCache() after toggling cursor-cli/grok-cli/claude-cli/llama-cpp providers
- Server-side cursor/grok model-cache lookups use a short negative-TTL so transient cold-start empty results self-heal instead of sticking
- Adds regression tests covering the cache refresh flow, hook behavior, and cursor/grok cache TTL self-healing
- Adds changeset (patch) documenting the fix

Files changed:
 .../fn-7710-cli-provider-model-cache-refresh.md    |   7 +
 ...thenticationSection.modelsCacheRefresh.test.tsx | 137 ++++++++++++++++++
 .../settings/sections/AuthenticationSection.tsx    |  32 +++--
 .../app/hooks/__tests__/useModelsCache.test.ts     | 159 ++++++++++++++++++++-
 packages/dashboard/app/hooks/useModelsCache.ts     |  72 +++++++++-
 .../src/__tests__/cursor-model-cache.test.ts       |  34 +++++
 .../src/__tests__/grok-model-cache.test.ts         |  33 +++++
 packages/dashboard/src/cursor-model-cache.ts       |  23 ++-
 packages/dashboard/src/grok-model-cache.ts         |  23 ++-
 9 files changed, 500 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-7710

Fusion-Task-Lineage: ebac46ba-5b3e-41f2-acc4-26f9139c0f71

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
2580524421 FN-7712: fix Grok CLI model list parsing for real grok models output
Fixes the Grok CLI model picker showing raw prompt/preamble text instead of real model names by rewriting parseModelLines to match the actual verified `grok models` output shape.

- Rewrote parseModelLines in process-manager.ts to strip the login/"Default model:"/"Available models:" preamble
- Strip `*`/`-` bullet markers and the `(default)` annotation from each model line
- Preserve existing legacy `id - Label`, columnar, and JSON parsing paths
- Added regression tests covering the real grok models output shape
- Added changeset (patch) documenting the fix

Files changed:
 .changeset/fn-7712-grok-model-parse.md             |  7 ++++
 .../src/__tests__/process-manager.test.ts          | 39 ++++++++++++++++++++++
 .../src/process-manager.ts                         | 34 ++++++++++++-------
 3 files changed, 68 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7712

Fusion-Task-Lineage: 93e34513-07b9-41b3-8b8b-ecdb763b4208

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
ddonaldson130
8dce51fdd9 fix(dashboard): project-scope Command Center analytics (FUX-037)
Apply FUX-037 projectId scoping to Command Center and Reliability view.
2026-07-09 08:17:52 -07:00
gsxdsm
f10c39fa0b feat: add fn_task_file_scope_add tool so agents can widen their File Scope
Agents that must edit files beyond a task's declared ## File Scope had no
way to keep the scope in sync, so those edits were stranded at merge (the
squash merge is scoped to ## File Scope, and cross-task overlap blocking +
the merge file-scope invariant both read it).

New executor tool fn_task_file_scope_add validates repo-relative
paths/globs with isValidFileScopeEntry, de-dupes against existing scope,
appends them to the ## File Scope section of PROMPT.md, and persists via
store.updateTask({ prompt }) (same validation + task.json/PROMPT.md sync as
fn_task_prompt_write). Registered in the main coding-agent tool list; the
base executor prompt now instructs the agent to call it when editing beyond
the declared scope. Merge-time peer-claim refusal is unchanged and remains
the cross-task backstop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:40:52 -07:00
gsxdsm
7f2e34f5b3 test(FN-7690): reconcile anthropic-compatible apiType assertions + de-slow retry test
FN-7690 changed resolveApiType() to return the registered pi-ai key
"anthropic-messages" for anthropic-compatible providers (the bare
"anthropic" key is never registered and throws at stream time), but left
behind a stale JSDoc and a stale test expectation:

- custom-provider-registry.ts: update the FN-7689 buildCustomProviderModels
  comment that still described the anthropic/anthropic-messages drift as
  unresolved.
- provider-registration.test.ts: assert config.api === "anthropic-messages"
  (was still asserting the pre-fix "anthropic").

Also de-slow a retry-exhaustion test: the describe uses fake timers with
shouldAdvanceTime, so awaiting a 3-retry backoff (1s+5s+15s) burned ~21s of
real wall time. Drive the backoff with advanceTimersByTimeAsync instead
(Standing Rule: prefer fake timers over real time waits).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:40:52 -07:00
gsxdsm
66069029a5 FN-7709: unref background integrity-check spawn and scheduling timer
Prevents short-lived CLI processes from being held open by background SQLite integrity checks.

- unref the sqlite3 child process (and its stdio) spawned by integrityCheckSqliteFileAsync via the shared unrefQmdChildProcess helper, immediately after spawn
- unref the 60s scheduling timer in scheduleBackgroundIntegrityCheck so a short-lived caller isn't pinned waiting for a background check it never asked to block on
- add regression test coverage (db-integrity-check-unref.test.ts) plus a CLI fixture (db-integrity-check-fixture.mjs) that exercises the fix in a real short-lived process
- add changeset documenting the fix and the audit of other spawn sites across @fusion/core/@fusion/engine/@fusion/dashboard/cli confirming they are safe

Files changed:
 .changeset/fn-7709-db-integrity-check-unref.md     |   7 ++
 .../src/__tests__/db-integrity-check-unref.test.ts | 135 +++++++++++++++++++++
 .../fixtures/db-integrity-check-fixture.mjs        |  28 +++++
 packages/core/src/db.ts                            |  26 ++++
 4 files changed, 196 insertions(+)

Fusion-Task-Id: FN-7709
Fusion-Task-Lineage: 6594aca4-0268-4bba-9a7f-af96d695f1e9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:35 -07:00
gsxdsm
409de31e57 fix: stop false Anthropic OAuth expiry notifications when token is valid
The OAuth expiry monitor and validity logger iterated the un-aliased
getOAuthProviders() id `anthropic` and evaluated get("anthropic"), which
can resolve to a stale legacy/supplemental row (e.g. ~/.pi/agent/auth.json)
even when the fresh, actually-used token lives under `anthropic-subscription`.
That fired a false "Anthropic OAuth expired" notification while the real
subscription token had refreshed successfully.

Both surfaces now resolve the freshest of the two aliased ids via a shared
resolveEffectiveOAuthCredential helper (mirroring the refresh scheduler's
getRefreshCandidateIds alias handling), so a live subscription token
suppresses the false alert. Notification throttle/cadence unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:39:35 -07:00
gsxdsm
dcfbee9ae6 FN-7707: reuse hardened unref'd executor in searchWithQmd
Fixes searchWithQmd's inline promisify(execFile) copy that could hold a caller open by reusing the already-hardened, synchronously-unref'd executor established for the background refresh path.

- searchWithQmd now calls getDefaultExecFileAsync() instead of building its own promisify(execFile) executor inline
- Removes the second un-unref'd execFile executor that could keep a short-lived caller (e.g. one-shot CLI memory search) open up to the awaited timeout
- Adds regression test fixture and test coverage (qmd-search-fixture.mjs, qmd-search-unref.test.ts) asserting the shared executor is used
- Adds changeset (patch) for @runfusion/fusion

Files changed:
 .changeset/fn-7707-qmd-search-unref.md                       |   7 +
 packages/core/src/__tests__/fixtures/qmd-search-fixture.mjs  |  30 ++++
 packages/core/src/__tests__/qmd-search-unref.test.ts         | 166 +++++++++++++++++++++
 packages/core/src/memory-backend.ts                          |  16 +-
 4 files changed, 216 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7707
Fusion-Task-Lineage: 3f9f94a7-5613-4b9a-a3ba-8e9bcdd6b687
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:35 -07:00
gsxdsm
4fb2bf5c55 FN-7706: unref qmd child process so background refresh doesn't block exit
Replaces promisify(execFile) with a hand-rolled spawn()-based qmd executor that unrefs the child process and its stdio, so a fire-and-forget scheduleQmd* memory-index refresh never keeps a short-lived caller process (e.g. CLI) alive; long-lived callers like the dashboard server still see refresh resolve/reject normally.

- memory-backend.ts: replace promisify(execFile) qmd exec path with spawn()-based executor that unrefs child + stdio
- Add qmd-refresh-unref.test.ts covering unref behavior with a qmd-refresh-fixture.mjs test fixture
- Add changeset fn-7706-qmd-unref.md (patch, fix category)

Files changed:
 .changeset/fn-7706-qmd-unref.md                    |   7 ++
 .../src/__tests__/fixtures/qmd-refresh-fixture.mjs |  28 +++++
 .../core/src/__tests__/qmd-refresh-unref.test.ts   | 136 +++++++++++++++++++++
 packages/core/src/memory-backend.ts                | 123 ++++++++++++++++++-
 4 files changed, 291 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7706

Fusion-Task-Lineage: 713c23c2-d7da-42c4-b066-31883ba78321

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
081dae0e0f FN-7705: Add Grok CLI runtime support as a bundled plugin
Adds a new bundled Grok CLI runtime plugin, wiring it end-to-end into settings, auth routes, model discovery, and the dashboard authentication UI.

- New `fusion-plugin-grok-runtime` package with CLI spawn, probe, provider, process-manager, and runtime-adapter modules plus tests
- Bundled-plugin install list (CLI + core) updated to auto-install the grok-cli plugin
- New `useGrokCli`/`grokCliBinaryPath` settings in `settings-schema.ts` and `types.ts`
- Dashboard: `GrokCliProviderCard` component/styles, `ProviderIcon` grok entry, `AuthenticationSection` wiring
- New `grok-model-cache.ts` for caching `grok models` discovery results, registered model/auth routes for `/auth/grok-cli` and `/providers/grok-cli/status`, merged into `/api/models`
- `runtime-provider-probes.ts` extended with Grok CLI probe/model-discovery delegation
- Docs updated (`PLUGIN_AUTHORING.md`, `settings-reference.md`) and changeset added (minor, feature)
- Workspace config (`pnpm-workspace.yaml`, `pnpm-lock.yaml`) updated to register the new plugin package

Files changed:
 .changeset/fn-7705-grok-cli-runtime.md             |   7 +
 docs/PLUGIN_AUTHORING.md                           |   2 +-
 docs/settings-reference.md                         |   4 +
 packages/cli/src/plugins/bundled-plugin-install.ts |   8 +
 .../cli/src/plugins/staged-bundled-plugin-ids.ts   |   1 +
 packages/cli/vitest.config.ts                      |  12 +
 .../core/src/__tests__/grok-cli-settings.test.ts   |  34 +++
 packages/core/src/index.ts                         |   1 +
 .../core/src/plugins/bundled-plugin-install.ts     |  10 +
 packages/core/src/settings-schema.ts               |   6 +
 packages/core/src/types.ts                         |   9 +
 packages/dashboard/app/api/legacy.ts               |  40 ++++
 .../app/components/GrokCliProviderCard.css         |  65 ++++++
 .../app/components/GrokCliProviderCard.tsx         | 204 ++++++++++++++++
 packages/dashboard/app/components/ProviderIcon.tsx |   5 +
 .../__tests__/GrokCliProviderCard.test.tsx         | 105 +++++++++
 .../app/components/__tests__/ProviderIcon.test.tsx |   8 +
 .../settings/sections/AuthenticationSection.tsx    |   8 +-
 packages/dashboard/package.json                    |   1 +
 .../src/__tests__/grok-model-cache.test.ts         | 152 ++++++++++++
 .../register-model-routes-grok-cli.test.ts         | 214 +++++++++++++++++
 .../dashboard/src/__tests__/routes-auth.test.ts    | 258 ++++++++++++++++++++-
 packages/dashboard/src/grok-model-cache.ts         | 166 +++++++++++++
 packages/dashboard/src/routes.ts                   |   1 +
 .../dashboard/src/routes/register-auth-routes.ts   | 134 ++++++++++-
 .../dashboard/src/routes/register-model-routes.ts  |  51 ++++
 packages/dashboard/src/runtime-provider-probes.ts  |  43 ++++
 packages/dashboard/vitest.config.ts                |  12 +
 packages/desktop/scripts/workspace-tools.ts        |   3 +-
 plugins/fusion-plugin-grok-runtime/CHANGELOG.md    |   7 +
 plugins/fusion-plugin-grok-runtime/README.md       |  54 +++++
 plugins/fusion-plugin-grok-runtime/manifest.json   |   6 +
 plugins/fusion-plugin-grok-runtime/package.json    |  40 ++++
 .../src/__tests__/cli-spawn.test.ts                | 103 ++++++++
 .../src/__tests__/index.test.ts                    |  12 +
 .../src/__tests__/probe.test.ts                    | 135 +++++++++++
 .../src/__tests__/process-manager.test.ts          |  96 ++++++++
 .../src/__tests__/provider.test.ts                 |  57 +++++
 .../src/__tests__/runtime-adapter.test.ts          |  21 ++
 .../fusion-plugin-grok-runtime/src/cli-spawn.ts    |  50 ++++
 plugins/fusion-plugin-grok-runtime/src/index.ts    |  74 ++++++
 plugins/fusion-plugin-grok-runtime/src/probe.ts    | 107 +++++++++
 .../src/process-manager.ts                         |  86 +++++++
 plugins/fusion-plugin-grok-runtime/src/provider.ts |  25 ++
 .../src/runtime-adapter.ts                         |  25 ++
 plugins/fusion-plugin-grok-runtime/src/types.ts    |  12 +
 plugins/fusion-plugin-grok-runtime/tsconfig.json   |  10 +
 .../fusion-plugin-grok-runtime/vitest.config.ts    |  22 ++
 pnpm-lock.yaml                                     |  25 ++
 pnpm-workspace.yaml                                |   1 +
 50 files changed, 2525 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7705

Fusion-Task-Lineage: b8194ea8-c773-4199-a52a-b0e4e7347192

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
55dae49b37 FN-7704: fix fn agent stop/start hanging up to 60s due to unclosed store handles
Fix CLI process exit so `fn agent stop`/`fn agent start` no longer hang up to 60s and eventually time out on repeated retries against the same agent.

- Root cause: `resolveProject()` cached an unclosed `TaskStore`, and `createAgentStore()` never closed the `AgentStore` it opened, leaving SQLite handles alive after the command's real work was done.
- Add `resolveProjectPathOnly`/`closeProjectStore` helpers in `project-context.ts` so path-only callers never leak a `TaskStore`.
- Explicitly close `AgentStore` on every exit/return path in `agent.ts`, since `process.exit()` skips pending `finally` blocks.
- Add a bounded fast-fail timeout around the state-store write (default 10s, overridable via `FUSION_AGENT_CMD_TIMEOUT_MS`) so a genuinely stuck operation fails fast with a clear error and non-zero exit instead of hanging.
- Add regression tests covering process-exit/store-closing behavior and update CLI reference docs.
- Add changeset for the patch release.

Files changed:
 .changeset/fn-7704-agent-cmd-hang-fix.md           |   7 +
 docs/cli-reference.md                              |   3 +
 .../commands/__tests__/agent-process-exit.test.ts  | 114 +++++++++++
 packages/cli/src/commands/__tests__/agent.test.ts  | 111 +++++++++-
 packages/cli/src/commands/agent.ts                 | 223 ++++++++++++++++-----
 packages/cli/src/project-context.ts                |  44 ++++
 6 files changed, 444 insertions(+), 58 deletions(-)

Fusion-Task-Id: FN-7704
Fusion-Task-Lineage: 4679d1a0-3ab8-48ce-86b7-5919bba805fb
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
22e7d75a07 FN-7703: fix search icon overlapping text in file browser search input
Fixes the search icon overlapping placeholder/typed text in the Files — Project search input under the compact spacing theme.

- Anchor .file-browser-search-input padding-left to the icon's own --space-sm offset + 16px icon width + a real gap, instead of the unrelated calc(--space-lg + --space-md) formula that collided exactly with the icon's occupied width under compact spacing
- Add FNXC:FileBrowser comment documenting the collision math and why the padding is now theme-invariant
- Add a regression test asserting padding-left exceeds icon offset + width against the compact spacing scale
- Add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7703-search-icon-overlap.md          |  7 +++
 packages/dashboard/app/components/FileBrowser.css  | 11 ++++-
 .../app/components/__tests__/FileBrowser.test.tsx  | 52 ++++++++++++++++++++++
 3 files changed, 69 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7703

Fusion-Task-Lineage: d7de7f82-f0e7-4086-a8ef-2fed2b4704ec

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
3e7e4a865b FN-7700: enrich Cursor model picker with reasoning/contextWindow metadata
Threads optional reasoning and context-window metadata from Cursor CLI model discovery through to the dashboard's Cursor model picker, replacing hardcoded false/0 defaults with pass-through values when the CLI reports them.
- Extend cursorDiscoveryToModels/discoverCursorProviderModels to carry reasoning/contextWindow from structured JSON model entries
- Update runtime-provider-probes.ts to surface the new metadata fields
- Update cursor-agent process-manager and provider to parse and propagate reasoning/contextWindow from CLI output
- Add/extend tests covering the new metadata plumbing in cursor-model-cache, process-manager, and provider
- Add changeset documenting the patch-level dashboard feature

Files changed:
 .changeset/fn-7700-cursor-picker-reasoning-context-window.md      |  7 ++++
 packages/dashboard/src/__tests__/cursor-model-cache.test.ts       | 26 ++++++++++++
 packages/dashboard/src/cursor-model-cache.ts                      | 24 +++++++----
 packages/dashboard/src/runtime-provider-probes.ts                 | 11 ++++-
 plugins/fusion-plugin-cursor-runtime/src/__tests__/process-manager.test.ts | 33 +++++++++++++++
 plugins/fusion-plugin-cursor-runtime/src/__tests__/provider.test.ts       | 23 +++++++++++
 plugins/fusion-plugin-cursor-runtime/src/process-manager.ts               | 48 +++++++++++++++++++---
 plugins/fusion-plugin-cursor-runtime/src/provider.ts                      | 19 ++++++++-
 8 files changed, 176 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-7700

Fusion-Task-Lineage: 6b371a92-204a-4201-8c7d-df65e9210a1b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
639a706f12 FN-7699: apply cursorCliBinaryPath override to model-picker discovery
Thread the machine-local cursorCliBinaryPath operator override into /api/models Cursor CLI discovery so the model picker spawns the same cursor-agent binary already validated by sign-in/status/probe.

- register-model-routes.ts reads globalSettings.cursorCliBinaryPath, trims and normalizes blank to undefined (preserving PATH auto-detection)
- passes the normalized binaryPath through to getCursorPickerModels({ binaryPath }) for model discovery
- adds regression tests covering override-set and override-blank/unset behavior in register-model-routes-cursor-cli.test.ts
- adds changeset fn-7699-cursor-cli-binary-path-model-picker.md (patch, fix) documenting the follow-up to FN-7696

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

Fusion-Task-Id: FN-7699

Fusion-Task-Lineage: 9895af8e-447d-425b-af58-1af4748c013c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
c9a2b201be FN-7698: update cursor-cli-contract.md with verified model/auth commands
Corrects the FN-3396 preflight's assumed Cursor CLI commands with the real, verified contract captured and implemented in FN-7697.

- Documents model discovery as plain-text `cursor-agent models` (no --json flag), including output shape, empty-account state, and the unreliable --list-models alternative
- Documents the parsing strategy: extract id before first ' - ' per line, filtering header/tip/empty-state lines
- Documents authentication as derived from `cursor-agent status --format json` via `isAuthenticated`, distinct from the --version availability probe
- Updates the Windows shell-backed probe list to include the auth-status probe and the corrected model-discovery command
- Marks the FN-3396 contract-freeze section as superseded by the verified contract, retaining accurate parts (binary candidates, expected failure states, dynamic-first principle)
- Adds an update-history note and FNXC:CursorCli comment documenting the correction

Files changed:
 docs/cursor-cli-contract.md | 43 +++++++++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-7698

Fusion-Task-Lineage: ae30b81c-f750-4011-85ae-883b1c5eb48b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
c565ceba8c FN-7694: fix embedded import preview pane clipped on tablet-width viewports
Scope the tablet-band (max-width: 860px) responsive pane rules in GitHubImportModal.css to :not(.github-import-modal--embedded) so the embedded Import Tasks view is governed only by container-query rules, not viewport-width dialog rules.

- Scope .github-import-workspace, .github-import-workspace__resize-handle, .github-import-list-pane, and .github-import-preview-pane tablet-width rules to :not(.github-import-modal--embedded) (previously only the dialog width rule was scoped)
- Fixes the embedded preview pane's max-height: 50% leaking onto the embedded view, clipping a tall selected issue/PR preview on tablet-width viewports (640-860px)
- Add regression tests asserting embedded-view CSS selectors remain scoped away from viewport tablet rules
- Add changeset (patch) documenting the fix

Files changed:
 .changeset/fn-7694-import-preview-tablet.md        |  7 +++++
 .../dashboard/app/components/GitHubImportModal.css | 24 ++++++++++-------
 .../__tests__/GitHubImportModal.test.tsx           | 31 ++++++++++++++++++++++
 3 files changed, 53 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7694

Fusion-Task-Lineage: 6df5b18a-7f53-48e5-ac57-2800ef2c65f9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
053e34b370 FN-7693: add real-store integration test for image artifact register/list/view pipeline
Adds an end-to-end integration test proving the artifact pipeline works against a real TaskStore, not just a mocked one.

- New test file exercises createArtifactRegisterTool/createArtifactListTool/createArtifactViewTool bound to a real TaskStore (inMemoryDb, real filesystem writes) instead of a mocked store
- Pins the register -> list -> view invariant for a real base64 PNG image artifact, verifying disk persistence, SQLite row fields (type, mimeType, sizeBytes, uri, taskId), and the list/view text surfaces
- Pins the invalid-base64-payload rejection path (non-image bytes for an image-typed artifact) to confirm no artifact row is persisted
- Pins the empty-state list text for a task with no registered artifacts

Files changed:
 packages/engine/src/__tests__/agent-artifact-tools-real-store-integration.test.ts | 131 +++++++++++++++++++++
 1 file changed, 131 insertions(+)

Fusion-Task-Id: FN-7693

Fusion-Task-Lineage: fd3493aa-6fb2-4e18-a735-c4a9d87c9c6c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:33 -07:00
gsxdsm
7fe18dfc9b FN-7696: surface Cursor CLI models in the model picker
Adds Cursor CLI model discovery to the dashboard's /api/models endpoint so cursor-agent-backed models appear in the picker when the Cursor CLI provider is enabled.

- Add cursor-model-cache.ts: short-TTL, single-flight cache for cursor-agent model discovery (no per-request CLI spawn)
- register-model-routes.ts additively merges cursor-cli models, deduped by provider/id, without displacing existing entries
- runtime-provider-probes.ts adds cursor-cli to configuredProviders when useCursorCli is on so rows survive the final provider filter
- Add unit tests for the cache and for register-model-routes cursor-cli integration
- Update docs/settings-reference.md
- Add changeset (patch/minor: @runfusion/fusion) documenting the fix

Files changed:
 .changeset/fn-7696-cursor-cli-models-in-picker.md  |   7 +
 docs/settings-reference.md                         |   2 +
 .../src/__tests__/cursor-model-cache.test.ts       | 158 +++++++++++++++++++
 .../register-model-routes-cursor-cli.test.ts       | 131 +++++++++++++--
 packages/dashboard/src/cursor-model-cache.ts       | 175 +++++++++++++++++++++
 .../dashboard/src/routes/register-model-routes.ts  |  45 ++++++
 packages/dashboard/src/runtime-provider-probes.ts  |  26 +++
 7 files changed, 527 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7696

Fusion-Task-Lineage: 2f2baf1e-5e4d-4c47-a112-b282a8ee45b6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:33 -07:00
gsxdsm
bccb552211 FN-7697: fix Cursor CLI auth-status and model-list discovery
Corrects the Cursor plugin's CLI integration to match cursor-agent's real command contract instead of best-effort heuristics.

- Derive authentication from `cursor-agent status --format json` (`isAuthenticated` field), failing closed with an actionable reason on non-zero exit or malformed JSON, instead of treating `--version` success as auth-ready.
- Switch model discovery to `cursor-agent models` plain-text output (`id - Label` lines), filtering header/tip/empty-state lines, since `--json`/`model list` are not supported.
- Update README to document the corrected CLI usage.
- Add regression tests covering probe.ts auth-status parsing and process-manager.ts model discovery.
- Add changeset (patch) documenting the fix.

Files changed:
 .changeset/fn-7697-cursor-cli.md                   |  7 ++
 plugins/fusion-plugin-cursor-runtime/README.md     |  3 +-
 .../src/__tests__/probe.test.ts                    | 94 +++++++++++++++++++---
 .../src/__tests__/process-manager.test.ts          | 89 +++++++++++++-------
 plugins/fusion-plugin-cursor-runtime/src/probe.ts  | 39 +++++++--
 .../src/process-manager.ts                         | 84 ++++++++++++-------
 6 files changed, 239 insertions(+), 77 deletions(-)

Fusion-Task-Id: FN-7697

Fusion-Task-Lineage: 6dd56a7f-da8f-4a73-82ff-5e9baab697c5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:33 -07:00
gsxdsm
d585edbc92 FN-7695: fix padding on Cursor CLI auth card
Fixes cramped padding on the Cursor CLI provider auth card in the dashboard.

- Adjusted CSS spacing/padding rules in CursorCliProviderCard.css
- Updated CursorCliProviderCard.tsx to apply the corrected layout
- Added regression tests covering the card's rendering/padding behavior
- Added a changeset documenting the patch-level fix

Files changed:
 .changeset/fn-7695-cursor-cli-padding.md           |  7 ++
 .../app/components/CursorCliProviderCard.css       | 19 +++++
 .../app/components/CursorCliProviderCard.tsx       | 14 +++-
 .../__tests__/CursorCliProviderCard.test.tsx       | 96 ++++++++++++++++++++++
 4 files changed, 134 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7695

Fusion-Task-Lineage: 565a7e70-347c-4cbf-8ba1-a672b57c0021

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:33 -07:00
gsxdsm
0faf4ede57 test: fix TaskCard/GraphTaskNode useToast + TaskDetailModal CSS selector-list regex (round 3) (#1969)
## Summary

Round 3 of full-suite greening on `main`. The post-#1965 full-suite
still failed on 6 UI/CSS test files in shards 3+4 (pre-existing
test-drift from recent UI commits, surfaced after the chat/i18n fixes
landed). All fixed.

## Fixes (all test-only; no production change)
- **TaskCard badge/footer tests** (`TaskCard.badge-height`,
`TaskCard.badge-wrap`, `TaskCard.footer-wrap`) — `RuntimeFallbackBadge`
now calls the dashboard `useToast()` hook, but these suites render
`<TaskCard>` without a `ToastProvider`. Added the `useToast` mock (same
pattern as the sibling `TaskCard.test.tsx` and `PlanningModeModal`
suites).
- **`TaskDetailModal.github-tracking-header`** — the github/gitlab
tracking header CSS rules were consolidated into a shared selector list
(`.detail-github-tracking-section .detail-source-header,
.detail-gitlab-tracking-section .detail-source-header {…}`), so the
test's `\s*\{` (selector immediately followed by `{`) no longer matched.
Updated the 3 CSS regexes to `[^{]*\{` to tolerate the selector list
while still pinning the layout contract.
- **`GraphTaskNode` tests** (`fusion-plugin-dependency-graph`) — same
`useToast` issue: `GraphTaskNode` renders the REAL `TaskCard` (to verify
prop pass-through, unlike sibling suites that mock it), hitting
`RuntimeFallbackBadge`→`useToast`. Added the
`@fusion/dashboard/app/hooks/useToast` mock to both files.

## Note on shard-2 engine[2/2]
Shard 2 still times out (watchdog 900s) on `@fusion/engine [2/2]`. This
is the engine-reliability real-git tier running single-threaded under
4-shard concurrent load — locally `[2/2]` is ~96s and green. It's
slow-test-debt / CI-load, not a code bug in these commits; I'm
investigating the specific slow/hanging file separately (the silent CI
reporter hides it).

## Verification
- TaskCard badge/footer: 14/14 ✅
- TaskDetailModal.github-tracking-header: 1/1 ✅
- GraphTaskNode + GraphTaskNode.drag: 29/29 ✅

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Stabilized several dashboard and dependency-graph test suites by
mocking toast behavior to prevent provider-related failures.
  * Improved robustness of task card and graph node interaction tests.
* Updated task detail modal CSS/layout assertions to better align with
current responsive styling and selector patterns.
* Reduced test flakiness for step-session retry timing by using
controlled fake-timer advancement.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-08 23:33:39 -07:00
gsxdsm
c74c5f6d67 perf(test): fast-forward fake timers in step-session terminal-activity test (was 22.6s real-time wait)
The 'publishes failed terminal workflow step activity' test awaited executeAll()
directly while the executor retried a failing step 3x with sleep() delays. Under
useFakeTimers({ shouldAdvanceTime: true }) those sleeps consumed REAL wall-clock
time (~22.6s locally, ballooning under CI load and busting the shard-2 watchdog).
Fast-forward the retry sleeps via vi.advanceTimersByTimeAsync like sibling retry
tests; the loop now completes in milliseconds.
2026-07-08 22:37:29 -07:00
gsxdsm
dc54acd746 test: fix TaskCard/GraphTaskNode useToast + TaskDetailModal CSS selector-list regex
- TaskCard badge/footer tests + GraphTaskNode tests: mock useToast (RuntimeFallbackBadge now calls it; tests render TaskCard without ToastProvider)
- TaskDetailModal.github-tracking-header: allow selector-list form in CSS regex (github+gitlab tracking rules consolidated)
2026-07-08 22:30:29 -07:00
gsxdsm
400f04530c chore(release): v0.57.0
Version bump via changesets.
2026-07-08 16:27:10 -07:00
gsxdsm
8892534676 fix(FUX-039): add release changeset for runtime-fallback viewport hardening (#1966)
## Summary

`003948033` ("harden runtime-fallback agent-card viewport gating")
landed on `main` **without a changeset**, but it affects published
`@runfusion/fusion`. This PR adds the missing patch changeset so the fix
shows up in release notes.

## Context

Supersedes the now-closed #1963, whose code was fully redundant with
`main` — all four FUX-039 findings plus every Greptile/CodeRabbit review
comment already shipped via `003948033` and the preceding FUX-039
commits. The only remaining gap was this release-notes entry.

## What the changeset documents

- **summary (user-facing):** Prevent redundant polling and a re-render
loop in agent-card runtime-fallback badges.
- **category:** `fix`
- **dev:** `AgentsView` caches one stable ref callback per viewport key
(avoids an infinite re-render loop when `IntersectionObserver` is
unavailable) and evicts it on unmount; the test-only toast-dedupe reset
is guarded to a no-op in production builds.

## Status

- Diff: a single new file under `.changeset/`. No production code
changes.
- Gate: ✅ green — Lint, Typecheck, Build, and Gate all pass on
`d8ce3f408`.
- An earlier revision also trimmed `fn-7692`'s over-length summary to
unblock the gate, but `main` since fixed that itself in `5815cd170`, so
this branch was rebased to drop the now-redundant commit. The diff is
now purely the FUX-039 changeset.

🤖 Generated with an autonomous coding agent
2026-07-08 16:20:39 -07:00
gsxdsm
d8ce3f4088 fix(FUX-039): add release changeset for runtime-fallback viewport hardening
Main landed the FUX-039 runtime-fallback agent-card hardening (003948033)
without a changeset, but it affects published @runfusion/fusion. This adds
the missing patch changeset so the fix appears in release notes.
2026-07-08 15:44:03 -07:00
gsxdsm
f617dd75c0 fix: restore full-suite green — i18n parity (FN-7658) + chat core mock (FN-7675) + verification-followup-dedup (FN-7658) (#1965)
## Summary

Follow-up to #1947. The full-suite on `main` is still red on 3 surfaces
introduced by post-#1947 commits. This PR fixes the two real test
failures and the i18n parity gap.

## Fixes
- **i18n key parity (FN-7658)** —
`settings.scheduling.autoArchiveDuplicateTasks` + `...Help` were added
to `en` but not the 5 non-en catalogs, breaking the i18n parity gate
(`parity.test.ts`, `i18n-gate-coverage.test.ts`). Added the 2 keys
(empty-string per the untranslated-entry convention) to `zh-CN`,
`zh-TW`, `fr`, `es`, `ko` in `packages/i18n/locales` (the single source
of truth; `dashboard/app/locales` is gitignored and synced in CI).
- **chat.test.ts (FN-7675)** — `chat.ts` now imports
`FUSION_RUNTIME_SELF_AWARENESS` from `@fusion/core` (CHAT_SYSTEM_PROMPT
embeds it); the hand-written core mock didn't stub it, so the module
failed to load. Added a stub (importOriginal intentionally avoided to
preserve the fs-cascade block).
- **verification-followup-dedup.test.ts (FN-7658)** — the "remains
additive with FN-4892 same-agent duplicate intake" test asserts the
ARCHIVE path, but FN-7658 made same-agent auto-archiving opt-in
(`autoArchiveDuplicateTasksEnabled` defaults false → flag-in-place in
triage). The test now opts into the legacy archive behavior it asserts.

## Note on shard-2 engine[2/2] timeout
The full-suite shard 2 times out on `@fusion/engine [2/2]` (watchdog
900s). Locally `[2/2]` runs in ~96s and is green (the lone
`provider-registration.test.ts` failure is local-only `pi-ai@0.79.9`
staleness — the lockfile pins `0.80.3` which exports `/compat`, so CI
resolves it). The `verification-followup-dedup` failure above is the
only real `[2/2]` defect; this PR fixes it. If the CI timeout persists
it's aggregate real-git load, which I'll address separately (not a code
bug).

## Verification
- i18n `parity` + `i18n-gate-coverage`: 7/7 ✅
- `chat.test.ts`: 14/14 ✅
- `verification-followup-dedup`: 5/5 ✅
- engine `--shard=2/2` (excluding the local-staleness file): 363 files /
4483 tests ✅ in ~96s

No production behavior change; no changeset needed (i18n catalog +
test-only).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added scheduling settings for automatic duplicate-task archiving,
including label and help text entries (currently placeholders) across
Spanish, French, Korean, Simplified Chinese, and Traditional Chinese.
* **Bug Fixes**
* Updated “awaiting confirmation” merger messaging to better reflect
when auto-merge proceeds automatically.
* **Tests**
* Updated reliability interaction tests to explicitly opt into legacy
duplicate-task archiving behavior.
* Adjusted chat-related tests by extending the runtime mock to satisfy a
new core import requirement.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-08 15:37:08 -07:00
gsxdsm
5815cd1700 fix: shorten FN-7692 changeset summary under 120-char changeset-format limit (unblocks lint) 2026-07-08 15:26:28 -07:00
gsxdsm
0039480334 fix(FUX-039): harden runtime-fallback agent-card viewport gating
Follow-up hardening on the RuntimeFallbackBadge viewport-gating work:

- AgentsView.tsx: registerAgentCardRef now returns a cached, stable callback per
  key (agentCardRefCallbacksRef) instead of a fresh closure each render. A fresh
  closure reads as unmount+remount to React; in environments without
  IntersectionObserver the mount path calls setVisibleAgentCardKeys -> re-render
  -> another fresh closure -> an infinite re-render loop (including jsdom). The
  cached entry is evicted on true unmount (el === null) so the Map cannot grow
  unbounded across created/deleted agents.
- ActiveAgentsPanel.tsx: document the viewport-gated badge polling with an FNXC
  comment (behavior unchanged).
- useRuntimeFallbackStatus.ts: guard __resetRuntimeFallbackToastDedupeStoreForTests
  to a no-op outside the test build (import.meta.env.MODE !== "test") so the
  test-only dedupe reset can never affect production code paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:24:01 -07:00
gsxdsm
1cb3cde667 fix(demo): trim leading loading-skeleton frames from README GIFs (#1964)
## Summary

Every animated README GIF opened on the app's page-load sequence — dark
skeleton placeholder boxes, then a "Loading files…" spinner (some also
an "AI engine is not running" banner) — before real content appeared,
making the looping GIFs look broken on first paint. This trims those
leading frames so each GIF starts on fully-rendered, populated content.

**17 dashboard-capture GIFs trimmed** (leading loading frames dropped,
per-GIF):

| GIF | dropped | GIF | dropped |
|---|---|---|---|
| command-center | 16 | chat-rooms | 18 |
| command-center-light | 10 | chat-rooms-light | 9 |
| command-center-gray | 9 | chat-rooms-gray | 10 |
| command-center-ember | 11 | chat-rooms-ember | 10 |
| workflows | 10 | agent-mail | 27 |
| workflows-light | 9 | agent-mail-light | 8 |
| workflows-gray | 16 | agent-mail-gray | 4 |
| workflows-ember | 3 | agent-mail-ember | 4 |
| agent-chat | 3 | | |

Loading-intro length varied widely per recording (3 frames to 27), so
each cut point was determined individually via frame-by-frame inspection
and visually verified.

**Left untouched:** `fusion-reel.gif`, `fusion-company-reel.gif`,
`fusion-mesh.gif` — edited reels that open on designed title cards
("From a rough idea.", "Import a company."), not loading skeletons.

## Technical notes

- Re-encoded with `gifsicle --unoptimize … --optimize=3 --lossy=60`.
`--unoptimize` is required — a naive frame cut leaves the new first
frame as a broken transparency-delta (renders as white garbage).
- `--lossy=60` keeps files at or below original size (a plain
re-optimize bloated them ~30–50%); at 2× zoom it's visually
indistinguishable from lossless and text stays crisp.
- Net total: **40.0 MB → 38.1 MB**.
- Verified frame 0 of all 17 outputs shows clean populated content — no
skeletons, no delta corruption.

No README edits needed (filenames unchanged).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 15:18:27 -07:00
gsxdsm
0bfe7e811b test(engine): opt into FN-7658 auto-archive in verification-followup-dedup additive test 2026-07-08 15:17:46 -07:00
gsxdsm
681113b095 fix: restore full-suite green — i18n parity (FN-7658 keys) + chat core mock (FN-7675) 2026-07-08 15:17:45 -07:00
gsxdsm
0755fc5747 fix(FN-7693): mobile terminal blank — exempt xterm measurement from * { max-width: 100% }
Real root cause of the blank mobile terminal (the FN-7692 remeasure guard did not
fix it and is reverted here). styles.css has a mobile-only reset
`@media (max-width: 768px) { * { max-width: 100% } }` to prevent horizontal
overflow. That universal selector also matches xterm's hidden character-measurement
subtree (`.xterm-helpers` / `.xterm-char-measure-element`). That subtree's containing
block (`.xterm-helpers`) is a 0x0 absolutely-positioned box, so `max-width: 100%`
resolves to `max-width: 0` and hard-caps xterm's character-cell measurement at 0.
FitAddon.fit() then proposes 0 columns/rows and `.xterm-screen` (plus the WebGL
canvas) collapses to 0x0 — the prompt streams in and is written into xterm's row DOM
but paints into a zero-size box, so the terminal is blank. Mobile-only, which is why
desktop always rendered fine.

Reproduced live via mobile emulation: `.xterm-char-measure-element` measured 0 while
an identical monospace span in the same container measured ~295px; `max-width: none`
on the measure element restored ~295px, and reopening the terminal with the exemption
active rendered the prompt with `.xterm-screen` sized 369x760. No amount of
remeasure/refit can fix this — the CSS re-caps the measurement to 0 every time — so
the FN-7692 CharSizeService guard is removed.

- Exempt `.xterm-helpers` / `.xterm-char-measure-element` from the mobile max-width
  reset in styles.css (covers both TerminalModal and SessionTerminal)
- Revert the ineffective FN-7692 remeasure guard and its tests
- Update changeset (patch) and the docs/solutions write-up to the real root cause

Note: root cause + fix validated in the automation browser via mobile emulation
(393px, iPhone UA, forced touch), not a physical device.

Fusion-Task-Id: FN-7693

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:09:26 -07:00
gsxdsm
67cc02750c FN-7692: fix misleading merger confirmation copy under active auto-merge
Correct the planner-oversight confirmation messaging so it no longer claims a hard block when the active auto-merge policy will actually advance the merge/pull-request stage unattended.

- decidePlannerRecovery accepts an additive, messaging-only `autoMergeWillProceed` flag and picks accurate reason wording (advisory vs. genuine human-approval block vs. neutral/unknown) for merger/pull-request await_confirmation decisions
- PlannerRecoveryController.tick threads `allowsAutoMergeProcessing(task, settings)` into decidePlannerRecovery as `autoMergeWillProceed`
- project-engine's requestConfirmation steering comment prefix changed from "confirmation required" to neutral "merge checkpoint" so it doesn't contradict the now-accurate reason text
- added regression tests in planner-recovery.test.ts and planner-overseer-intervention-wiring.test.ts
- added changeset and doc note

Files changed:
 .changeset/fn-7692-merger-confirmation-copy.md     |  7 +++
 docs/architecture.md                               | 10 +++-
 packages/core/src/__tests__/planner-recovery.test.ts    | 66 ++++++++++++++++++++++
 packages/core/src/planner-recovery.ts              | 36 +++++++++++-
 packages/engine/src/__tests__/planner-overseer-intervention-wiring.test.ts | 37 ++++++++++++
 packages/engine/src/planner-recovery-controller.ts | 14 ++++-
 packages/engine/src/project-engine.ts               | 11 +++-
 7 files changed, 176 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7692

Fusion-Task-Lineage: 187684b8-1d24-425d-85d4-627587469908

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 15:09:26 -07:00
gsxdsm
0514aabde7 fix(dashboard): wrap ActiveAgentsPanel/AgentsView tests in ToastProvider (#1962)
## Summary
`RuntimeFallbackBadge.tsx` (added by an earlier change) calls
`useToast()` unconditionally. It's rendered inside `LiveAgentCard` (used
by `ActiveAgentsPanel`) and on both the board/list agent cards in
`AgentsView`, but the render helpers in `ActiveAgentsPanel.test.tsx` and
`AgentsView.test.tsx` never wrapped the component under test in a
`ToastProvider`. Every test that mounts a card threw `"useToast must be
used within ToastProvider"`.

## Fix
Adds a `renderPanel()` / `renderView()` helper to each test file —
matching the exact wrapping pattern already used correctly in
`RuntimeFallbackBadge.test.tsx` — and routes every `render(...)` call
site through it. No test assertions, fixtures, or expected behavior were
changed; this is strictly a render-setup fix.

## Before / After
- `ActiveAgentsPanel.test.tsx`: 12/15 → 15/15 passing
- `AgentsView.test.tsx`: ~96-108/132 → 130/132 passing (the 2 remaining
failures are pre-existing, unrelated CSS-content assertion failures —
not `useToast`-related — and are out of scope for this fix)
- `RuntimeFallbackBadge.test.tsx`: unaffected, still 8/8 passing

## Verification
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/ActiveAgentsPanel.test.tsx` — 15/15 pass
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/AgentsView.test.tsx` — 130/132 pass
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/RuntimeFallbackBadge.test.tsx` — 8/8 pass
- `pnpm --filter @fusion/dashboard run typecheck` — clean
- `eslint` on both modified files — clean

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Updated dashboard component tests to render within the toast context,
preventing crashes when nested UI needs toast access.
* Standardized test helpers for the agents views to better match real
app behavior.
* Adjusted one token-usage assertion to open the controls popup before
checking its content.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-08 15:08:24 -07:00
gsxdsm
7cb76669db fix: resolve 3 staff-engineer review findings from #1957 (init_error mislabel, unbounded off-screen polling, duplicate toasts) (#1960)
## Summary

A Staff Engineer pre-landing review (Greptile/CodeRabbit) on #1957
(merged) flagged four structural issues. This PR fixes the three that
were confirmed still present on `main`; the fourth (an unregistered-rule
`eslint-disable-next-line react-hooks/exhaustive-deps` comment) was
already fixed in #1957's second commit before merge and needed no
further change.

1. **`resolvePluginRuntime()` mislabeled "found but failed to init" as
`not_found`.** When a `runtimeHint` plugin registration is found but
`pluginContext`/`createRuntimeContext(...)` comes back falsy, the
resolver returned `reason: "not_found"` — indistinguishable from "never
registered" — defeating the point of a distinct `FallbackReason`. Now
returns `reason: "init_error"`. Updated the existing test that wrongly
asserted `"not_found"` for this path, and added a new test asserting all
three reachable `FallbackReason` values (`not_found`, `init_error`,
`factory_error`) are pairwise distinct.

2. **`ActiveAgentsPanel.tsx`/`AgentsView.tsx` hardcoded
`isInViewport={true}`.** Every agent card (live-agent header, board
card, list card) polled the runtime-fallback endpoint every 30s forever,
even scrolled off-screen — unlike `TaskCard.tsx`'s correct
`IntersectionObserver`-gated pattern. Both files now thread a real
`IntersectionObserver`-backed viewport signal into
`RuntimeFallbackBadge`. Added regression tests proving polling stops
once a badge instance's `isInViewport` transitions to `false` and
resumes once it goes back to `true` (desktop + a mobile-breakpoint
variant), plus verified via `tsc --noEmit` for `@fusion/dashboard`.

3. **Toast dedupe was per-hook-instance, not shared.**
`useRuntimeFallbackStatus`'s `lastToastedEventIdRef` was a local
`useRef`, so the same task rendered simultaneously in two card surfaces
(e.g. `ActiveAgentsPanel` + `AgentsView`) fired two separate toasts for
one fallback event. Dedupe now lives in module-level shared state (a
bounded `Map` keyed by `taskId:eventId`, FIFO-evicted past 500 entries)
so a fallback event toasts exactly once across every
simultaneously-mounted badge instance for the same task. Added a
cross-instance regression test mounting two badges for the same
`taskId`/`eventId` and asserting exactly one toast fires.

## Test evidence

- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/runtime-resolution.test.ts --reporter=dot` — 25/25 pass
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/RuntimeFallbackBadge.test.tsx --reporter=dot` —
11/11 pass
- `pnpm --filter @fusion/dashboard run typecheck` — clean
- `pnpm --filter @fusion/engine run typecheck` — clean

## Scope

Isolated 6-file diff on top of current `main`
(`packages/engine/src/runtime-resolution.ts`,
`packages/engine/src/__tests__/runtime-resolution.test.ts`,
`packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts`,
`packages/dashboard/app/components/ActiveAgentsPanel.tsx`,
`packages/dashboard/app/components/AgentsView.tsx`,
`packages/dashboard/app/components/__tests__/RuntimeFallbackBadge.test.tsx`).
No behavior outside the three findings above was touched.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- The desktop dashboard now supports plugin-backed runtime features,
improving how plugin-enabled workflows are loaded and run.
- Agent cards now pause background fallback polling when they’re
off-screen, helping the dashboard feel smoother and more responsive.

- **Bug Fixes**
- Improved runtime fallback handling so missing runtimes and
initialization failures are reported more accurately.
- Toast notifications are now better deduplicated, reducing repeated
alerts when multiple views show the same fallback state.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-08 15:07:56 -07:00
gsxdsm
0e7b0e75da fix(demo): trim leading loading-skeleton frames from README GIFs
The 17 dashboard-capture GIFs opened on the app page-load sequence
(skeleton placeholder boxes + "Loading files…" spinner) before real
content. Trim those leading frames per-GIF so each starts on fully
populated content. Re-encoded with gifsicle --unoptimize/--lossy=60;
total 40.0MB -> 38.1MB. Edited reels (fusion-reel/company-reel/mesh)
left untouched — they open on designed title cards, not skeletons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:06:36 -07:00
Fusion Agent
c7960d8a59 fix(dashboard): wrap ActiveAgentsPanel/AgentsView tests in ToastProvider
RuntimeFallbackBadge.tsx calls useToast() unconditionally, but the
ActiveAgentsPanel.test.tsx and AgentsView.test.tsx render helpers never
wrapped the component under test in a ToastProvider, causing every mount
that transitively renders RuntimeFallbackBadge to throw
'useToast must be used within ToastProvider'.

Adds renderPanel()/renderView() helpers (matching the existing
RuntimeFallbackBadge.test.tsx pattern) that wrap render(...) calls in
ToastProvider, and routes every render call site in both files through
them. No test assertions or fixtures were changed.

Fixes 12/15 ActiveAgentsPanel.test.tsx failures and ~96-108/132
AgentsView.test.tsx failures.
2026-07-08 16:47:34 -04:00
gsxdsm
b7b1b71cae fix(FN-7692): recover blank mobile terminal when xterm screen collapses to 0x0
The mobile terminal rendered blank even though the WebSocket was Connected and
the shell prompt had already streamed in. Root cause (reproduced live): on the
mobile fullscreen layout xterm's CharSizeService can measure a 0-width character
cell, so FitAddon.fit() proposes 0 columns/rows and .xterm-screen (plus the WebGL
canvas) collapses to 0x0 — prompt bytes arrive and are written into xterm's row
DOM but paint into a zero-size box. Renderer-independent and mobile-layout-
specific; not fixed by resize/font-size re-fits because prior guards only validate
the container width and font load, never the resulting measured screen/cell width.

Add guardAgainstCollapsedTerminalScreen (app/utils/terminalPreferences.ts) and arm
it from both terminal surfaces (TerminalModal + SessionTerminal) right after their
initial fit. While the container has a width but .xterm-screen does not, it forces
a genuine DOM-strategy remeasure (forceTerminalFontRemeasure) + fit, re-driven by a
ResizeObserver until the screen has a real width. It waits (does not give up) while
the container is not yet measurable, is bounded so it never spins, and is disposed
on every re-init/close path. Recurrence of FN-7620/FN-7686.

- Add isTerminalScreenCollapsed + guardAgainstCollapsedTerminalScreen with tests
- Wire + dispose the guard across all xterm (re)init/close paths in both surfaces
- Add changeset (patch) and a docs/solutions write-up

Note: reproduced via mobile emulation (393px, iPhone UA, forced touch), not a
physical device; the guard is the structural fix — confirm on a real device.

Fusion-Task-Id: FN-7692

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:22:10 -07:00
gsxdsm
461a4a2711 FN-7689: add opt-in Anthropic-style prompt caching for custom providers
Custom providers previously never enabled prompt-cache control, so agent turns re-billed the full context every request even on cache-capable backends.

- Add `CustomProvider.anthropicPromptCaching` opt-in flag in @fusion/core types
- Set pi-ai `compat.cacheControlFormat="anthropic"` on opted-in models in both registration paths: custom-provider-registry `toProviderConfig` and pi.ts `createFnAgent`
- Expose the new toggle in the dashboard CustomProvidersSection UI (with supporting CSS) and thread it through the legacy API + custom-provider routes
- Update docs (dashboard-guide, settings-reference) to document the new setting
- Add engine test coverage for the caching flag across provider registration and pi-create-fn-agent paths
- Add changeset for the fix

Files changed:
 .changeset/fn-7689-custom-provider-prompt-caching.md          |   7 +
 docs/dashboard-guide.md                                       |   1 +
 docs/settings-reference.md                                    |   2 +-
 packages/core/src/types.ts                                    |  15 ++
 packages/dashboard/app/api/legacy.ts                          |  12 ++
 packages/dashboard/app/components/CustomProvidersSection.css  |  24 +++
 packages/dashboard/app/components/CustomProvidersSection.tsx  |  58 +++++-
 packages/dashboard/src/routes/register-custom-provider-routes.ts |  16 ++
 packages/engine/src/__tests__/pi-create-fn-agent.test.ts      |  71 +++++++
 packages/engine/src/__tests__/provider-registration.test.ts   | 204 ++++++++++++++++++++-
 packages/engine/src/custom-provider-registry.ts               |  71 +++++--
 packages/engine/src/pi.ts                                     |  27 ++-
 12 files changed, 473 insertions(+), 35 deletions(-)

Fusion-Task-Id: FN-7689

Fusion-Task-Lineage: b4f88f32-50da-4651-a546-432a95a1ab1c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 13:09:10 -07:00
gsxdsm
64d4bb753a FN-7690: fix custom-provider anthropic-compatible apiType resolution
Reconciles a naming drift where resolveApiType() mapped anthropic-compatible custom providers to an unregistered pi-ai api key, causing streaming failures.

- resolveApiType() now maps anthropic-compatible to "anthropic-messages" (was "anthropic"), matching pi.ts's resolveCustomProviderApiType and the built-in Anthropic provider config
- Added FNXC:CustomProviders comment documenting why anthropic-messages is the only key pi-ai's ModelRegistry actually registers
- Added/updated regression tests in custom-provider-registry.test.ts and provider-registration.test.ts
- Added changeset (patch) documenting the fix

Files changed:
 .changeset/fn-7690-apitype-resolver-reconcile.md    |  7 +++++++
 packages/cli/src/commands/__tests__/custom-provider-registry.test.ts | 21 ++++++++++++++++++---
 packages/engine/src/__tests__/provider-registration.test.ts | 20 ++++++++++++++++++++
 packages/engine/src/custom-provider-registry.ts     | 15 ++++++++++++++-
 4 files changed, 59 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7690

Fusion-Task-Lineage: 461c340f-bc29-44a2-b8ec-19f0b03224d7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 13:06:37 -07:00
gsxdsm
6a81871781 chore: refresh weekly test-velocity baseline (2026-W28) — confirm gate regression resolved
Gate wall-time back down to 8.8s (was 36.3s this morning, pre-FN-7667/7669 fix landing). Cycle 2026-W28 re-measured post-fix.
2026-07-08 11:19:47 -07:00
gsxdsm
07507f5264 FN-7688: add slow login-shell profile latency hint and docs
Investigated whether --login in TerminalService first-prompt latency is a meaningful contributor and added a one-time diagnostic hint plus documentation of findings.

- Add SLOW_LOGIN_PROFILE_HINT_MS (2000ms) threshold and one-time, non-blocking console.info hint in createSession()'s PTY onData handler when a login shell is slow to produce first output
- Track spawnStartedAt and loginProfileHintLogged per session, and whether the succeeding spawn attempt used --login, without altering spawn args, timeouts, or the retry-without-login fallback
- Add regression tests covering the slow-login-profile hint behavior in terminal-service.test.ts
- Document the investigation and findings in docs/solutions/developer-experience/login-shell-profile-latency.md and link it from docs/dashboard-guide.md
- Add a patch changeset for @runfusion/fusion describing the new server-log hint

Files changed:
 .changeset/fn-7688-login-shell-profile-latency.md  |   7 ++
 docs/dashboard-guide.md                            |  17 +++
 .../login-shell-profile-latency.md                 |  79 ++++++++++++++
 .../src/__tests__/terminal-service.test.ts         | 121 +++++++++++++++++++++
 packages/dashboard/src/terminal-service.ts         |  57 ++++++++++
 5 files changed, 281 insertions(+)

Fusion-Task-Id: FN-7688

Fusion-Task-Lineage: 08d5dd47-ea9f-4973-9f0e-a8d5fdeae111

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 10:06:42 -07:00
gsxdsm
83e77431e2 FN-7684: move terminal shortcuts/zoom controls into footer on tablet widths
On tablet-width viewports (769-1024px) the terminal header was overcrowded, so shortcut/zoom/preference controls now move into the bottom status-bar footer instead, mirroring the existing FN-7560 mobile layout while true desktop keeps the header controls.

- Add an isTabletTerminal detection flag (769-1024px, non-mobile) alongside the existing mobile flag
- Render the shared terminalActionControls fragment in the .terminal-status-bar footer for tablet widths, keeping desktop pin/pop-out toggles available there too
- True desktop (>1024px) continues to render font size / clear / shortcuts / preferences controls in the header
- Update TerminalModal.css with footer layout styles for the tablet action controls
- Update TerminalModal tests to cover the new tablet breakpoint rendering
- Update docs/dashboard-guide.md to describe the tablet footer control location
- Add a changeset (@runfusion/fusion: patch) documenting the fix

Files changed:
 .changeset/fn-7684-tablet-terminal-footer.md       |   7 ++
 docs/dashboard-guide.md                            |   6 +-
 .../dashboard/app/components/TerminalModal.css     |  34 ++++++
 .../dashboard/app/components/TerminalModal.tsx     | 103 ++++++++++++-----
 .../components/__tests__/TerminalModal.test.tsx    | 126 ++++++++++++++++++---
 5 files changed, 231 insertions(+), 45 deletions(-)

Fusion-Task-Id: FN-7684

Fusion-Task-Lineage: 627c40af-a152-451a-a045-65ad8babea9e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 09:56:32 -07:00
gsxdsm
a832b7979f FN-7686: skip redundant session-list round trip on fresh terminal load
Speed up initial terminal load by short-circuiting the no-op server session list call when there are no persisted local tabs to validate.

- useTerminalSessions: when readTabsFromStorage returns zero tabs, skip the listTerminalSessions HTTP call entirely and mark bootstrap ready immediately, unblocking auto-create/WebSocket connect instead of serializing behind a provably-discarded round trip
- Reload-with-persisted-tabs path is unchanged and still awaits the list call since its result is decision-relevant there
- Add regression tests covering the fresh-load fast path and the persisted-tabs path
- Add changeset (patch) and a docs/solutions write-up of the bootstrap-list-serialized-before-auto-create issue

Files changed:
 .changeset/fn-7686-slow-terminal-initial-load.md   |  7 ++
 ...bootstrap-list-serialized-before-auto-create.md | 87 ++++++++++++++++++++++
 .../hooks/__tests__/useTerminalSessions.test.ts    | 73 ++++++++++++++++++
 .../dashboard/app/hooks/useTerminalSessions.ts     | 23 +++++-
 4 files changed, 189 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7686

Fusion-Task-Lineage: 9c708329-6362-4c2e-967f-aea12849c47c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 09:53:39 -07:00