05151a25dbcfe7b6d89f10f9912e2fb7682cd57b
2350 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
05151a25db |
feat: faster dashboard and serve startup (#2132)
## Summary Speeds up **time-to-HTTP-ready** for `fn dashboard` and `fn serve` after the PostgreSQL cutover without reintroducing the historical 3s cwd-engine race that degraded webhooks. - **Dashboard store share (serve parity):** inject the factory-booted `TaskStore` as `externalTaskStore` so cwd `ensureEngine` does not open a second pool; share only when store root matches project working directory (multi-project safe). - **Serve multi-project:** stop awaiting `startAll()` before listen; await only the primary engine; background the rest + reconciliation. - **Defer non-route-critical engine work:** ordered OAuth (refresh → monitor), automation schedule syncs, and auto-merge **enqueue** after the engine handle is returnable. - **Critical-path merge status clear:** still clear stale `merging`/`merging-pr` before ready so manual merge is not blocked after crash. - **Serve `--paused`:** apply `enginePaused` before `ensureEngine`/`startAll` (dashboard ordering). - **Stop safety:** generation counter so deferred tails cannot resume after `stop()` clears `shuttingDown`. - **Phase timing:** shared `phaseTime` helper, factory substep logs, serve time-to-listen. Plan: `docs/plans/2026-07-14-001-feat-faster-startup-plan.md` ## Test plan - [x] `packages/engine` — `project-engine-manager.test.ts` (path-matched external store) - [x] `packages/engine` — `project-engine-deferred-startup.test.ts` (status clear, OAuth order, stop generation) - [x] `packages/cli` — `startup-phase.test.ts` - [x] `packages/cli` — `serve.test.ts` (60 tests, including `--paused`) - [ ] Local: warm `fn dashboard` / `fn serve` and compare `startup phase *` / `time-to-listen` logs - [ ] `pnpm smoke:boot` (real serve `/api/health` on ephemeral port) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Performance** * Improved dashboard and serve startup times, including faster time-to-listen and time-to-ready. * Moved non-essential background initialization off the critical startup path. * Parallelized dashboard service initialization where possible. * **Reliability** * Improved multi-project startup handling and project selection. * Prevented cross-project task-store sharing. * Added safer shutdown behavior for partially completed startup. * **Diagnostics** * Added startup phase timing logs to help identify performance bottlenecks. * **Tests** * Expanded coverage for deferred startup, shutdown, project isolation, and startup timing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
599a509d22 |
refactor: package code organization (god-file peels, wave 1) (#2139)
## Summary
First wave of package-internal code organization: split oversized
modules into domain-named files/folders while preserving public import
paths via re-exports, and refresh the line-count ratchet scoreboard.
- **Plan:**
`docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`
(multi-wave program; this PR lands U1–U4 + first U3/U6 slices)
- **Core types:** peel `types.ts` into
`types/{board,merge-queue,execution-and-ui,merge-policy,workflow-steps}.ts`
with browser-safe Vite alias preserved
- **Core TaskStore:** rename `remaining-ops-9` →
`task-commit-associations` (domain-named, not ordinal dump)
- **Engine executor:** peel pure helpers into
`executor/{browser-probe,requeue-loop,pseudo-pause,workflow-step-failures}.ts`
- **Engine heartbeat:** peel system prompts/procedures into
`agent-heartbeat-prompts.ts`
- **Ratchet:** one-time baseline truth-up + ratchet-down for touched
files
### Deferred to follow-up PRs (plan U5, U7–U9 + remaining waves)
- Self-healing folder split
- Further remaining-ops domain peels
- Dashboard `legacy.ts` / routes / UI monofiles
- CLI extension + TUI peels
## Test plan
- [x] `pnpm --filter @fusion/core exec tsc --noEmit`
- [x] `pnpm --filter @fusion/engine exec tsc --noEmit`
- [x] Focused vitest: `detect-pseudo-pause`,
`executor-browser-verification`, `clear-terminal-workflow-step-failures`
- [x] `node scripts/check-file-line-count.mjs` clean against updated
baseline
- [ ] CI merge gate (lint/typecheck/build/gate)
- [ ] Browser smoke: N/A for this PR (no dashboard UI route changes)
## Residual Review Findings
None. Review autofix applied dual-home wiring for
`clearTerminalWorkflowStepFailures` only.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added configurable heartbeat procedures for task and no-task scenarios
(including patrol-aware rendering).
* Improved agent-browser availability verification with clearer
availability/status reporting.
* Added detection for pseudo-pauses and review-handoff requests.
* Expanded core configuration/contract options for
execution/UI/localization, merges, merge queues, and workflow steps.
* **Bug Fixes**
* Improved handling of transient execute-requeue and workflow-step
retry/cleanup behavior, including better Windows path support.
* Preserved existing public interfaces during internal restructuring.
* **Documentation**
* Added a multi-phase roadmap for future package reorganization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
85f8b1f909 |
feat: shared Postgres multi-node — retire mesh data-plane replication (#2130)
## Summary - Treat **shared PostgreSQL** (`DATABASE_URL`) as the multi-node durable data plane; mesh HTTP is membership + optional auth, not task/settings replication. - **Peer exchange**: under Postgres backend mode, write queue is **topology/auth-only**; non-topology pending rows fail rather than replaying multi-leader task/settings payloads. - **Mesh routes**: task-ID reserve/commit/abort always hit local shared allocator rows (ignore remote `coordinatorNodeId`); mesh sync ignores settings and only exchanges `authMaterial`. - **Docs**: rewrite multi-project runbook, shared cluster protocol, and architecture mesh sections for shared-Postgres + claims/leases. ## Context Follows the SQLite→Postgres cutover. Multiple Fusion nodes can share one external Postgres while keeping **per-node execution** (worktrees, processes, claims via `central.task_claims`). Explicit non-goals remain: scheduler failover and live process migration. Plan: `docs/plans/2026-07-15-001-refactor-mesh-shared-postgres-multinode-plan.md` ## Test plan - [x] `pnpm --filter @fusion/engine exec vitest run src/__tests__/peer-exchange-service.test.ts` - [x] `pnpm --filter @fusion/dashboard exec vitest run src/__tests__/mesh-routes.test.ts` - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/shared-mesh-state.test.ts` - [ ] CI gate (lint/typecheck/build/gate) - [ ] Manual (optional): two processes, same `DATABASE_URL`, create task on A visible on B; settings change without mesh settings sync; claim exclusivity ## Operator note Multi-node shared board requires **external** `DATABASE_URL` on every node. Default embedded Postgres is still single-host. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Improved multi-node deployments using shared PostgreSQL as the durable source of execution state. * Task ID reservation/commit/abort now run locally (no remote coordinator forwarding). * Mesh syncing now prioritizes topology visibility and authentication material; settings replication is disabled in shared-Postgres mode. * **Bug Fixes** * Prevented task/settings replication over mesh HTTP in shared-Postgres deployments. * Refined lease ownership, recovery, and reconciliation to converge via shared-database primitives. * **Documentation** * Updated architecture and shared-mesh protocol guidance, including multi-node setup and lease/task-ID allocation behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3e978e1540 |
fix: quiet per-poll scheduler hold-release and routing log spam
Both lines fired on every scheduler poll while nothing changed: a held card re-attempts release each sweep, and every dispatch candidate logged its resolved node. On a busy board that filled the operator log pane with "Hold release for FN-XXXX deferred" and "routed to node=local" within seconds, burying real scheduler events. Add a Logger.debug() level, off by default and opted into per subsystem via FUSION_DEBUG, and demote both lines to it. Routing to a remote node stays at info since it explains where work actually went; only the local default is demoted. Lines reporting a real transition (capacity rejection, racing sweep, release failure) are untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d893a026df |
FN-7971: hide GitLab import tab when GitLab is disabled
Hide the Import Tasks GitLab provider affordance when gitlabEnabled is off, coerce restored GitLab state to GitHub, and document the behavior. - Gate GitLab provider tab visibility on effective gitlabEnabled and wait for settings before replaying persisted GitLab auto-load - Coerce disabled GitLab provider preference to GitHub without firing GitLab fetch/import requests - Cover hide/show/coerce paths in GitHubImportModal tests and update dashboard guide copy - Add patch changeset for the published package Files changed: .changeset/fn-7971-hide-gitlab-when-disabled.md | 7 +++ docs/dashboard-guide.md | 4 +- .../dashboard/app/components/GitHubImportModal.tsx | 27 ++++++++-- .../__tests__/GitHubImportModal.test.tsx | 62 +++++++++++++++++++--- 4 files changed, 88 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-7971 Fusion-Task-Lineage: e645a4a7-85e0-4635-8dad-f5839390e5c5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6e3a338cac |
FN-7968: defer slow cleanup off task deletion critical path
Make soft-delete return after the DB mutation while branch and agent cleanup run in the background. - Schedule cleanupBranchForTask after the soft-delete transaction instead of awaiting it under withTaskLock - Persist cleaned-branch log entries on the deleted row asynchronously; warn on deferred failures - Respond from DELETE /tasks/:id after deleteTask and schedule execution-agent binding release off the HTTP path - Add core and dashboard regression tests for non-blocking delete cleanup - Document the fast-path contract in architecture.md and add a patch changeset Files changed: .changeset/fn-7968-task-delete-latency.md | 7 + docs/architecture.md | 1 + .../task-delete-nonblocking-cleanup.test.ts | 160 +++++++++++++++++++++ packages/core/src/task-store/archive-lifecycle.ts | 57 +++++++- .../routes-task-delete-nonblocking.test.ts | 139 ++++++++++++++++++ .../src/routes/register-task-workflow-routes.ts | 19 ++- 6 files changed, 370 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-7968 Fusion-Task-Lineage: f218a91e-aee3-46c9-a80f-182751b3ccc4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
de25e32eac |
FN-7963: add plannerHeartbeatPatrolEnabled to gate idle heartbeat task creation
Add a workflow setting that disables idle/no-task heartbeat proactive task creation without turning off planner overseer stuck-task recovery. - Declare plannerHeartbeatPatrolEnabled (default true) in BUILTIN_OVERSIGHT_SETTINGS - Resolve the flag via resolveEffectivePlannerHeartbeatPatrolEnabled and wire it into agent-heartbeat/triage prompts - Render patrol-off instruction when disabled; keep FN-7962 outage backoff lines when patrol stays enabled - Cover setting defaults, prompt builders, and heartbeat executor paths with tests - Document the setting in settings-reference and add a changeset Files changed: .changeset/fn-7963-planner-heartbeat-patrol.md | 7 ++ docs/settings-reference.md | 11 +- packages/core/src/__tests__/agent-prompts.test.ts | 29 +++++ .../builtin-workflow-settings-triage.test.ts | 21 ++++ .../plannerHeartbeatPatrolEnabled-default.test.ts | 64 ++++++++++ packages/core/src/agent-prompts.ts | 55 +++++++-- packages/core/src/builtin-workflow-settings.ts | 14 +++ packages/core/src/index.gate.ts | 5 + packages/core/src/index.ts | 5 + packages/core/src/workflow-settings-resolver.ts | 15 ++- .../src/__tests__/heartbeat-executor.test.ts | 59 ++++++++- packages/engine/src/agent-heartbeat.ts | 135 +++++++++++++++++++-- packages/engine/src/triage.ts | 8 +- 13 files changed, 402 insertions(+), 26 deletions(-) Fusion-Task-Id: FN-7963 Fusion-Task-Lineage: c5e7a382-52c1-4cc1-8b21-aba7dc7d2b97 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7a4a9c8229 |
fix(engine): auto-recover false-positive heartbeat-model-unavailable parks
Admit under-budget paused/heartbeat-model-unavailable agents to the shared heartbeatErrorRecovery budget so timer, self-healing, and startup paths retry without a manual Retry. Keep the pause reason when the budget is exhausted so operators still see credential guidance. |
||
|
|
e9f14bf024 |
perf: speed up local pnpm build and cap stacked verifications (#2134)
## Summary - Extend the workspace content-hash skip cache to **all** packages (not just plugins), with `--force` / `--full` flags - Default local CLI packaging to a **fast mode** (bin/extension + migrations only); full desktop/plugin/DTS staging runs on CI or `pnpm build:full` - Enable TypeScript `incremental` builds for warm recompiles - Add `maxConcurrentVerifications` (default **1**) so concurrent tasks cannot stack monorepo typecheck/build and peg CPU Warm `pnpm build` measured ~**126s → ~0.8s** when nothing changed. ## Test plan - [x] `node --test scripts/__tests__/build-workspace.test.mjs` (12 pass) - [x] `pnpm --filter @fusion/engine exec vitest run src/__tests__/verification-concurrency.test.ts` - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/settings-parity.test.ts` - [x] Local: first `pnpm build` rebuilds as needed; second warm `pnpm build` skips all packages (~0.8s) - [x] Fast CLI packaging logs skip of desktop/plugin staging without `FUSION_CLI_FULL_PACKAGE` - [ ] CI: `pnpm build` still full-packages under `CI=true` (plugin staging / release surfaces) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a Scheduling setting to limit concurrent verification tasks from 1–8, with a default of 1. * Verification tasks now support cancellation while waiting or running. * Added options for forced and full workspace builds. * **Performance** * Local builds can skip unchanged packages and use incremental compilation for faster rebuilds. * Local CLI packaging is faster by default, while full packaging remains available when needed. * **Documentation** * Updated the settings reference with the new verification concurrency option. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
bc2d22df6e |
feat(dashboard): offer AI translation in Import Tasks preview (#2128)
## Summary Import Tasks can now offer on-demand AI translation when a selected GitHub or GitLab issue/PR title and body appear to be in a different language than the active dashboard locale. - Detect foreign-language content with a conservative client heuristic (Unicode scripts + Latin stopwords) - Show an opt-in banner: **Translate**, then **Show original / Show translation**, plus **Dismiss** - Call new `POST /api/ai/translate-text` (shared AI-helper rate limit with refine/draft) - Translation is **display-only** in the preview; imported task text stays the original source language ## Why Operators working in a non-English dashboard (or reading non-dashboard-language issues) needed a way to understand import candidates without leaving the preview or changing what gets imported. ## Test plan - [x] Unit tests for language detection (`detectContentLanguage`) - [x] Unit tests for translate request validation, response parsing, and AI agent path - [x] GitHub import modal: French content shows translate controls; English content does not - [x] Dashboard typecheck clean for app + server packages - [ ] Manual: open Import Tasks with dashboard language English, select a French/Korean issue, translate and toggle original - [ ] Manual: confirm Import still creates the task with original title/body - [ ] Manual: dismiss banner for a selection and confirm it stays dismissed for that item ## Notes - Comments are not translated (title + body only) - zh-CN / zh-TW share a CJK family so Chinese content does not prompt translation when the UI is either Chinese locale - Secondary locale catalogs have empty placeholders for the new `git.translate*` keys (runtime falls back to English) |
||
|
|
a242f1b449 |
fix(FN-7952): migrate bundled plugins to PostgreSQL (#2111)
## Summary Bundled plugins now persist shared runtime state in project-scoped PostgreSQL tables instead of maintaining independent SQLite authority. Reports, CLI Printing Press, Compound Engineering, Roadmap, Even Realities, and WhatsApp all follow the same ownership and startup contract as Fusion core. ## Design decisions - Plugin schema hooks run through the host’s PostgreSQL owner and enforce project isolation. - The SDK exposes the host contract needed by bundled plugins without importing engine internals. - Legacy Roadmap ownership fixtures use the supported empty-owner sentinel, preserving current composite primary/foreign keys while exercising backfill behavior. - The lockfile travels with the Even Realities PostgreSQL dependency so packaged installs remain reproducible. ## Validation - All six affected plugin builds pass. - Affected plugin suites pass: 773 tests across Printing Press, Compound Engineering, Even Realities, Reports, Roadmap, and WhatsApp. - `pnpm test:gate` passes all 478 gate tests. - This PR changes 40 files. ## Stack - Depends on #2110 → #2109 → #2108. - The documentation/release PR completes the stack. Related: #2105 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Breaking Changes** * PostgreSQL is now required for runtime storage; SQLite files are used only as one-time migration inputs. * The legacy `FUSION_NO_EMBEDDED_PG` fallback has been removed. * **New Features** * Added project-isolated PostgreSQL storage for plugins, reports, tasks, notifications, and other plugin data. * Added agent tools for reports and CLI service drafts. * Added PostgreSQL schema initialization support for plugin authors. * **Bug Fixes** * Improved migration and recovery of legacy plugin state. * Prevented cross-project data access and strengthened transactional schema updates. * **Documentation** * Updated storage, migration, deployment, plugin authoring, CLI, and dashboard guidance for PostgreSQL. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4f037679ad |
feat: planner overseer session advisor (OMP advisor parity) (#2082)
## Summary Adds a **session advisor** to the planner overseer so Fusion can review live executor transcripts the way [oh-my-pi’s advisor](https://github.com/can1357/oh-my-pi/tree/main/packages/coding-agent/src/advisor) does — without replacing the existing lifecycle supervisor (stage watch, retry, merge confirmation, human-control withhold). ### What ships - **Emission guard** (`OverseerEmissionGuard`) — content-free phrase filter, session dedupe with severity-rank escalation, one accept per advisor update - **Session delta runtime** — queues agent-log deltas, drains through an advisor agent, drops backlog after 3 failures - **Session advisor service** — model gate, level matrix (`observe` / `steer` / `autonomous`), human-control re-check at inject, `[session-advisor]` steering comments - **OVERSEER.md / WATCHDOG.md** discovery for project review priorities - **AgentLogger `onEntriesFlushed`** + poll-backed agent-log cursor for durable deltas - Workflow settings: `plannerOverseerAdvisorProvider` + `plannerOverseerAdvisorModelId` (both required; empty = soft-disabled for cost safety) - Docs + changeset ### What does not ship (deferred) - Multi-advisor YAML roster, mutating advisor tools, reviewer/merger shadowing, true tool-abort interrupt ### Plan `docs/plans/2026-07-13-001-feat-overseer-advisor-parity-plan.md` ## Enablement 1. Set workflow **Session advisor model provider** + **Session advisor model id** 2. Oversight level `observe` (log only), `steer`, or `autonomous` (inject) 3. Optional: add `OVERSEER.md` or `WATCHDOG.md` in the project ## Test plan - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/overseer-emission-guard.test.ts` - [x] `pnpm --filter @fusion/engine exec vitest run` overseer-* unit tests (21 tests) - [x] Related planner-overseer / intervention regression tests - [x] `@fusion/engine` + `@fusion/core` typecheck - [ ] Manual: configure advisor model, run an executor task, confirm `[session-advisor]` inject + timeline metadata when concern is raised ## Residual Review Findings None from autofix pass (log-cursor ordering fix already committed). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an off-by-default “session advisor” that can review live execution activity and provide severity-based guidance. * Added project and per-task controls to enable it, including a default enable switch and Quick Add / Task Detail toggles. * Enhanced advisor prompting by discovering and incorporating `OVERSEER.md`/`WATCHDOG.md` review files. * **Documentation** * Added architecture and settings documentation for the new session-advisor parity behavior. * **Bug Fixes** * Improved fail-soft handling so advisor behavior won’t disrupt execution. * Fixed concurrent PostgreSQL migration startup failures. * **Tests** * Added coverage for advice parsing, emission guarding, runtime behavior, and watchdog discovery. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9bdbdc5f16 |
FN-7955: stage bundled plugin skills
Ensure bundled Compound Engineering skills are present in published CLI packages. - Copy plugin src/skills directories into dist/plugins/<id>/skills during CLI packaging. - Add bundle-output coverage that verifies Compound Engineering SKILL.md files stage and resolve from the plugin root. - Document runtime-read bundled plugin asset staging and add a patch changeset for @runfusion/fusion. Files changed: .changeset/fn-7955-ce-skills-published.md | 7 ++++ docs/PLUGIN_AUTHORING.md | 3 ++ packages/cli/src/__tests__/bundle-output.test.ts | 51 ++++++++++++++++++++++++ packages/cli/tsup.config.ts | 14 +++++++ 4 files changed, 75 insertions(+) Fusion-Task-Id: FN-7955 Fusion-Task-Lineage: 32c4ad31-4f3a-478b-996f-ce6bcafd1e27 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
d0ce7829c0 |
FN-7953: fix mobile OAuth code submit taps
Submit Anthropic OAuth manual codes on the first mobile tap instead of requiring keyboard dismissal first. - Add a reusable touch action gesture hook that handles touch/pointer activation before synthetic clicks. - Wire the OAuth manual code Submit button to invoke submission on the first touch while preventing duplicate click handling. - Cover the mobile double-tap regression and document the UI bug pattern for future fixes. Files changed: .../oauth-manual-code-mobile-double-tap-submit.md | 60 +++++++++++ .../app/components/OAuthManualCodeForm.tsx | 31 +++++- .../__tests__/OAuthManualCodeForm.test.tsx | 110 +++++++++++++++++++++ .../hooks/__tests__/useTouchActionGesture.test.ts | 110 +++++++++++++++++++++ .../dashboard/app/hooks/useTouchActionGesture.ts | 89 +++++++++++++++++ 5 files changed, 399 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7953 Fusion-Task-Lineage: d387cdbd-25a7-4b7d-add6-27a1ded5cbea Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
dff864e098 |
feat: harden permanent-agent heartbeat instructions (#2081)
## Summary Hardens permanent-agent operating law while keeping the heartbeat/executor split: - **Critical Rules** in task-scoped and no-task heartbeat system prompts (survive custom `HEARTBEAT.md`) - Stronger default procedures: disposition checklist, scoped-wake, blocked dedup, progress note style - **Wake Delta multi-assign inventory** (ranked, cap 8, coordination-only framing) + `checkout_conflict` regression test - Standing instructions six-section template for blank custom create / empty detail insert - Onboarding interview guidance to prefer structured `instructionsText` - Playbooks, CONCEPTS, agents.md accuracy; remove stale agent gap-analysis doc Plan: `docs/plans/2026-07-12-001-feat-permanent-agent-heartbeat-instructions-plan.md` ## Test plan - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/assigned-task-ranking.test.ts` - [x] `pnpm --filter @fusion/engine exec vitest run src/__tests__/agent-heartbeat-procedures.test.ts src/__tests__/heartbeat-executor.test.ts -u` - [x] `pnpm --filter @fusion/dashboard exec vitest run app/components/__tests__/standing-instructions-template.test.ts` - [ ] CI gate green on PR ## Residual Review Findings None recorded at open (inline review; no residual sink). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added ranked multi-assignment context to agent heartbeat wake-ups, including task status, ownership, and lease details. * Added standing-instructions templates for creating and editing permanent agents. * Improved onboarding guidance with a consistent six-section instruction structure. * Added clearer heartbeat handling for blocked tasks, no-task runs, and checkout conflicts. * **Documentation** * Added permanent-agent heartbeat playbooks and expanded coordination glossary entries. * Updated documentation indexes and heartbeat behavior guidance. * **Tests** * Added coverage for task ranking, instruction templates, wake-up context, and conflict handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b563b12662 |
feat: add Oh My Pi (omp) ACP runtime plugin (#2083)
## Summary - Add `fusion-plugin-omp-runtime` so Fusion agents can run through operator-installed **Oh My Pi (`omp`)** over the [Agent Client Protocol](https://omp.sh/docs/acp) (`omp acp`). - Wire staged/bundled install, Settings → Authentication card (enable + binary path), model discovery (`omp models` → `omp-cli/*`), and MCP eligibility for runtime id `omp`. - Forward Fusion `systemPrompt` via ACP `session/new` `_meta.systemPromptOverride`. ## How operators use it 1. Install/auth `omp` (credentials under `~/.omp`). 2. Enable **Oh My Pi — via omp ACP** in Settings → Authentication (optional binary path). 3. Set agent **Runtime Source → OMP Runtime** (`runtimeHint: "omp"`), or pick an `omp-cli/*` model when enabled. ## Known v1 gaps - No Grok-style Fusion `fn_*` loopback tool bridge yet (operator MCP is forwarded; in-process custom tools are not). - Model is fixed at spawn (`omp --model … acp`); no mid-session Fusion model switch. ## Test plan - [x] `pnpm --filter @fusion-plugin-examples/omp-runtime test` (unit + live ACP when `omp` is on PATH) - [x] Auth routes: `POST /api/auth/omp-cli`, `GET /api/providers/omp-cli/status` - [x] Engine `runtimeSupportsMcp("omp")` - [ ] Manual: enable card in dashboard, select OMP runtime on an agent, run a short chat turn <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Oh My Pi (OMP) CLI support as an ACP-backed runtime and model provider, including model discovery and probing. * Added dashboard auth/status controls to enable OMP, check readiness, and configure the local binary path (with validation). * Exposed OMP custom `fn_*` tools via an MCP loopback bridge, plus optional filesystem capabilities and stricter tool permission gating. * **Documentation** * Added/expanded OMP runtime contract and integration docs (including the ACP session/handshake flow). * **Tests** * Added Vitest coverage for settings wiring, provider status, model discovery, runtime sessions, permissions, MCP bridging, and live connectivity. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
03966ecb79 |
Fix multi-project branch-group route store scoping (#2085)
## Summary Conflict resolution for closed [#2074](https://github.com/Runfusion/Fusion/pull/2074) (FN-001 multi-project branch-group store scoping), rebased onto current `main`. #2074 closed when its fork head was briefly reset to `main` during a ref update; maintainer write access to the fork head only works while the PR is open, so that PR could not be reopened without new fork commits. This branch carries the same fix: - Request-scoped `TaskStore` for branch-group list/read/assign/promote/abandon - Integrated reconcile/close uses the request store for cwd + persistence - Compatible with async branch-group store APIs and main’s CentralProjectIdentity (`projectId` trim) - Postgres durable FN-7438 tests + padded `projectId` regression ## Verification - `FUSION_DASHBOARD_DEEP=1 pnpm --filter @fusion/dashboard exec vitest run --project dashboard-api src/__tests__/routes-branch-groups.test.ts src/__tests__/integrated-routers-group-pr-token.test.ts src/__tests__/routes-context-project-identity.test.ts --silent=passed-only --reporter=dot` — 3 files, 41 tests passed. --------- Co-authored-by: Tchorizo <295840812+Tchorizo@users.noreply.github.com> Co-authored-by: Fusion <noreply@runfusion.ai> |
||
|
|
c15c78feeb |
feat: migrate storage from SQLite to PostgreSQL (#1793)
# Migrate storage from SQLite to PostgreSQL — full dashboard cutover Migrates Fusion's storage layer to the embedded PostgreSQL `AsyncDataLayer` (the default backend) and **completes the satellite-store + feature cutover** so every dashboard and Command Center surface works in PG mode. ## Status — every surface works in embedded-PG mode Verified live against a running embedded-Postgres dashboard (all **200**, zero 5xx) and gate-tested (**23 files / 99 tests** on embedded PG, plus engine-core 294 and ci-shape 63 in the blocking merge gate; core/engine/cli/dashboard typecheck clean). | Area | Surfaces | State | |---|---|---| | Satellite stores | workflows, todos, insights, research, missions, goals, mailbox | ✅ | | Views | artifacts, documents, evals | ✅ | | Command Center | activity, productivity, team, tokens, tools, **workflows**, **github**, **signals**, **plugin-activations**, **live** (all 10) | ✅ | | Run execution | insight generation, research run execution | ✅ (store-path; AI step needs a provider) | | Live updates | SSE push for mission/research/insight events | ✅ | | Workflow editing | create / update / delete / select (+ id counter) | ✅ | | Engine | mission autopilot, incident-signal ingestion, regression storm-guard, agent wake-on-message | ✅ | | Core | tasks, agents, secrets, automations, memory, chat, usage, PRs, git | ✅ | ## Approach Each satellite store gets an `Async<Store>` wrapper exposing the sync store's method names over the existing `async-*-store.ts` helpers; `get<Store>Store()` returns a `Sync | Async` union; consumers `await` (harmless on sync), and engine/CLI paths that can't convert use `instanceof Sync` graceful fallback. Analytics aggregators branch on `"ping" in dbOrLayer` to run schema-qualified raw SQL over `project.*` (snake_case) in PG. Executors/orchestrators/autopilot are await-converted to drive the union store; the async store wrappers extend `EventEmitter` so SSE live-push fires in both backends. Not-yet-ported capabilities degrade gracefully (never 500) and are individually called out in commits. ## Sync with main The branch is kept continuously merged with `main` (currently through FN-7845, 2026-07-12); the earlier "final rebase deferred" note no longer applies. Use **Create a merge commit** (or squash) to land it — GitHub's rebase-merge cannot replay a merge-maintained branch. ## Residual Review Findings Multi-agent code review of the PostgreSQL satellite-store ports (U1–U5) applied 3 safe fixes (see `fix(review): apply autofix feedback`). The following are **real but gated** — recorded here as follow-up work rather than auto-applied. All are SQLite→PostgreSQL **concurrency/atomicity regressions**: the sync stores were immune only by SQLite's single-writer, single-threaded-handler execution; the async ports open multi-await read-modify-write windows. **Reachability is low today** because the execution engines that generate concurrent same-run mutations (insight run executor, research orchestrator/dispatcher) are `instanceof`-gated to sync mode in PG. No process-crash class survived (all engine fallbacks correctly guard the sync store). - **[P1] Research `appendResearchEvent` dual-write is non-atomic** (`packages/core/src/async-research-store.ts`, corroborated: adversarial + reliability). The `research_run_events` insert (own transaction) and the `run.events` jsonb update are separate writes — a crash between them, or two concurrent appends, splits the table count from the jsonb array. **Fix:** perform the seq-insert and the jsonb update in one `layer.transactionImmediate`. - **[P1] Research run terminal-reversion via stale full-row persist** (`async-research-store.ts` `persistResearchRun`/`updateResearchStatus`). Concurrent `PATCH /runs/:id/status` + `POST /runs/:id/events` can revert a terminal run to `running` by overwriting the whole row, bypassing the transition guard. **Fix:** scoped column `UPDATE`s with a `WHERE status …` guard, or optimistic version column. - **[P2] `updateResearchRun`/`updateInsightRun` read-then-write TOCTOU** — concurrent PATCHes last-writer-wins on the lifecycle merge. **Fix:** `SELECT … FOR UPDATE` / enclosing transaction. - **[P2] `upsertRun`/`createRunOrThrowConflict` check-then-create race** (`async-insight-store.ts`) — two callers can each create an "active" run. **Fix:** partial unique index on `(projectId, trigger) WHERE status IN ('pending','running')`. - **[P3] `createResearchRetryRun` return-value divergence** — sync returns the pre-update `queued` snapshot; async returns the reloaded `retry_waiting` run (persisted state is identical). Pick one side for cross-backend parity. - **[P2/perf] Mission `getMissionWithHierarchy`/`getMissionHealth` N+1 fan-out** — O(milestones×slices) sequential round-trips hold one pool slot per request; can starve the pool for large hierarchies. **Fix:** batched/joined reads. - **Testing gaps:** no PG-mode concurrency tests (interleaved status/event mutations), no sync↔async parity assertion for the lifecycle-error codes, and no mission status/health rollup parity test vs the sync `MissionStore`. ~~Out of scope (deferred): AI run *execution* (insight/research) + mission autopilot + live SSE mission events remain sync-gated/degraded in PG mode.~~ **Since ported** — insight/research run execution, mission autopilot, and SSE live push all run on the async layer now, which also makes the concurrency findings above genuinely reachable; they remain open follow-ups. --- ## Update — 2026-07-12: production-readiness hardening & live acceptance Everything below landed on this branch since the description above was written: **Production blockers from review — fixed** - `recoverStaleTransitionPending` ported to the async layer (backend moves write + clear the crash-safe marker; startup/maintenance sweeps no longer throw). - Lost-update class fixed: `atomicWriteTaskJson`/`WithAudit` write changed columns only (full-row upserts silently resurrected stale fields across concurrent store instances — the "task stuck unplanned forever" bug). - First-boot **auto-migration**: booting the PG backend over a project with a legacy `fusion.db` migrates it automatically (loud failure, SQLite kept as backup), and the dashboard shows a one-time **"your data was migrated" banner** with the backup paths and a Need-help Discord link. - `pg_dump`/`pg_restore` discovered from common install locations for embedded-mode backups. - The PG suite is part of the blocking merge gate (`test:pg-gate`). **Multi-project isolation (PR #2007, merged into this branch)** - `project_id` partition key on tasks / archived tasks / config, `taskProjectScope` threaded through every scan/claim/count, per-project config rows, layer bound to the project at startup. - Review P1 follow-up: the shared cold-storage `archive.archived_tasks` table is also partitioned and all archived-board reads/counts/searches are scoped. - Schema drift self-heal generalized to schema-qualified columns so existing databases upgrade in place. **Other changes** - Node settings sync **removed** in PG mode (409 `settings-sync-disabled-postgres`) — nodes share state by connecting to the same database; auth sync kept (per-machine file). - Perf (review findings): `listTasks` pushes column filter + ORDER BY + LIMIT/OFFSET into SQL; `getConversation` capped to the most recent 200 messages. - Fixed a false "operator action required" pause-abort log fired on every successfully auto-merged task. **Live acceptance — PASSED (2026-07-12)** A sandboxed instance (isolated HOME, embedded PG, real Opus executor) ran a task through the complete cycle: create → triage (AI spec) → execute → in-review → AI squash-merge landed on the project's `main` → done. A write+read sweep of every data surface (settings, comments, documents, attachments + artifact bridge + artifact edit, chat with real generation, goals, missions, agent mail, secrets, workflows, memory, CC analytics) was green on embedded PG. **Known remaining work** - The per-project `config` PK re-key has no upgrade path for pre-isolation embedded-PG databases (needs a real `DROP CONSTRAINT`/re-key migration; fresh databases are fine). - `pg_dump`/`pg_restore` binaries are not yet bundled in release artifacts (PATH/common-location discovery only). - The satellite-store concurrency findings listed above. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Phil Larson <hello@phillarson.xyz> Co-authored-by: fusion-merge <fusion-merge@local> |
||
|
|
6e0fde860c |
FN-7949: fix deleted planning-mode session resurrecting after in-flight generation completes
Adds a bounded-TTL delete tombstone to AiSessionStore so a straggling post-delete generation write can never resurrect a session the user explicitly deleted. - AiSessionStore now records a 10-minute delete tombstone (id -> deletion timestamp) in delete(), deleteByIdAndType(), and bulk cleanup paths (cleanupOld/cleanupStaleSessions/emitDeletedSessions). - upsert() checks the tombstone first and drops (no-ops) any write for a tombstoned id without touching SQLite or emitting ai_session:updated, fixing the root cause once in the shared store rather than per-producer (planning.ts, subtask-breakdown.ts, mission-interview.ts, milestone-slice-interview.ts). - Tombstone entries are pruned lazily on check and piggyback on the existing cleanupStaleSessions() cadence so the in-memory map cannot grow unbounded. - Adds a changeset (patch) documenting the user-facing fix. - Updates docs/architecture.md and docs/storage.md with the new "AI session delete tombstones" behavior. - Adds regression tests covering the tombstone guard in ai-session-store.test.ts and routes-planning.test.ts. Files changed: .changeset/fn-7949-ai-session-delete-tombstone.md | 7 + docs/architecture.md | 2 +- docs/storage.md | 12 +- packages/dashboard/src/__tests__/ai-session-store.test.ts | 145 +++++++++++++++ packages/dashboard/src/__tests__/routes-planning.test.ts | 200 ++++++++++++++++++++- packages/dashboard/src/ai-session-store.ts | 83 +++++++++ 6 files changed, 446 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7949 Fusion-Task-Lineage: 8e509dae-0cc5-46cd-9c4b-9048cfda56d3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4e7e013d6f |
FN-7947: add Plan action to context menu for pre-execution task cards
Adds a Plan action to Board/List task context menus so triage/hold/intake cards can jump straight into Planning Mode without duplicating a task. - Add `onPlan` handler and `isPreExecutionHoldColumn` gate to `TaskContextMenu` so Plan only appears for pre-execution (triage/intake/hold) columns, and only when a host wires the handler - Wire the Plan action through `Board.tsx`, `Column.tsx`, `ListView.tsx`, and `WorktreeGroup.tsx` so both board and list views expose the new menu item - Surface the Plan entry point on `TaskCard.tsx` - Add test coverage in `TaskContextMenu.test.tsx`, `TaskCard.test.tsx`, and `ListView.test.tsx` for the new gating/wiring behavior - Document the new action in `docs/dashboard-guide.md` - Add a minor changeset for `@runfusion/fusion` Files changed: .changeset/fn-7947-plan-context-menu-action.md | 7 ++ docs/dashboard-guide.md | 10 ++- packages/dashboard/app/components/Board.tsx | 10 ++- packages/dashboard/app/components/Column.tsx | 4 + packages/dashboard/app/components/ListView.tsx | 15 +++- packages/dashboard/app/components/TaskCard.tsx | 24 +++++- packages/dashboard/app/components/TaskContextMenu.tsx | 18 ++++ packages/dashboard/app/components/WorktreeGroup.tsx | 9 ++ packages/dashboard/app/components/__tests__/ListView.test.tsx | 21 +++++ packages/dashboard/app/components/__tests__/TaskCard.test.tsx | 96 ++++++++++++++++++++++ packages/dashboard/app/components/__tests__/TaskContextMenu.test.tsx | 32 ++++++++ 11 files changed, 236 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-7947 Fusion-Task-Lineage: 41c759a2-e76b-4771-9421-c9805c4596e5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7cc622bed2 |
FN-7946: auto-retry stuck Planning Mode AI generation up to 3 times
Planning Mode now automatically retries a stuck or terminally-errored AI generation session up to three times before falling back to the permanent Retry/Dismiss error panel, reducing manual retries for transient failures. - Add a bounded (MAX_PLANNING_AUTO_RETRIES = 3) client-side auto-retry that reuses the existing /planning/:id/retry endpoint whenever the SSE stream's onError, a session reload, or the stuck-session poll observes a terminal "error" status. - Track the retry budget in refs (planningAutoRetryAttemptRef, planningAutoRetryInFlightRef) so async SSE/poll/loadSession handlers share a single in-flight guard, with the current attempt mirrored into state (isAutoRetrying/autoRetryAttempt) for the UI. - Reset the retry budget whenever the session makes real progress (reaches a new question or a completed summary), and surface the permanent Retry/Dismiss error view once the budget is exhausted. - Show a "Retrying... (attempt N of 3)" loading message while an automatic retry is in flight, distinct from the manual Retry button state. - Fix a stuck-poll edge case where a terminal error discovered only by the poll (missed SSE event) after the auto-retry budget was exhausted left the modal spinning on "Generating next question..." forever instead of showing the error view. - Document the new auto-retry behavior in docs/dashboard-guide.md and add a minor changeset for @runfusion/fusion. - Extend PlanningModeModal.planning-flow.test.tsx with coverage for the auto-retry budget, single-flight behavior, and the poll-discovered terminal-error fallback. Files changed: .changeset/fn-7946-planning-auto-retry.md | 7 + docs/dashboard-guide.md | 3 + .../dashboard/app/components/PlanningModeModal.tsx | 339 ++++++++++++++------ .../PlanningModeModal.planning-flow.test.tsx | 353 ++++++++++++++++++--- 4 files changed, 567 insertions(+), 135 deletions(-) Fusion-Task-Id: FN-7946 Fusion-Task-Lineage: 42e911dc-9639-46ab-bb4f-bc9060413140 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
f0888d43c3 |
FN-7945: route List-view task opens through the movable popup when Open tasks as popups is on
Extends the existing board/right-dock "Open tasks as popups" routing so ordinary List row/card and keyboard opens use the same shared movable/resizable FloatingWindow instead of the docked split-pane/mobile detail. - Add openMobileTasksInPopup prop to ListView, threaded through App -> MainContent -> ListView (dashboard/types.ts) - handleRowClick routes to onPopOut (popOutTaskDetail) when the setting is on, on both desktop split-pane and mobile/tablet single-pane; docked behavior is preserved when the setting is off - Restore Enter/Space keyboard activation on list rows to invoke the same handleRowClick path, alongside existing context-menu key handling - Update docs/dashboard-guide.md and docs/settings-reference.md to describe List row/card opens as part of the popup routing surface, and refresh the Appearance settings help copy/FNXC comment accordingly - Add changeset (.changeset/fn-7945-list-view-task-popup.md, minor) describing the user-facing behavior - Extend ListView.test.tsx coverage for the new popup routing and restored keyboard activation Files changed: .changeset/fn-7945-list-view-task-popup.md | 7 ++ docs/dashboard-guide.md | 4 +- docs/settings-reference.md | 2 +- packages/dashboard/app/App.tsx | 1 + packages/dashboard/app/components/ListView.tsx | 48 +++++++++---- .../app/components/__tests__/ListView.test.tsx | 80 +++++++++++++++++++++- .../app/components/dashboard/MainContent.tsx | 2 + .../dashboard/app/components/dashboard/types.ts | 1 + .../settings/sections/AppearanceSection.tsx | 4 +- 9 files changed, 127 insertions(+), 22 deletions(-) Fusion-Task-Id: FN-7945 Fusion-Task-Lineage: 784cb4ee-c493-4ace-bf8b-0e3dbaaef9a3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7246df22f6 |
FN-7944: add setting to keep task popups attached to their Board/List view
Adds an opt-in project setting so open task-detail popups stay attached to the Board or List view where they were opened, instead of floating over every main-content view. - New project setting taskPopupsBoardListOnly (default: off) in settings-schema.ts and ProjectSettings type, with default preserved via settings-defaults tests. - usePoppedOutTasks now stores each popup's originating TaskView alongside its task snapshot (PoppedOutTaskEntry), keeping legacy tasks output for existing callers. - App.tsx adds isTaskPopupVisibleForView() gating helper and filters popped-out entries to the current view for rendering/keyboard-close handling, while hidden popups remain mounted in hook state (not cleared) so switching back to the originating view restores them with shared persisted geometry. - Settings -> Appearance gets a new "Keep task popups on their Board/List view" checkbox (AppearanceSection.tsx) with i18n strings and updated settings search text in SettingsModal. - Documentation updated in docs/dashboard-guide.md and docs/settings-reference.md to describe the render-only hide/restore behavior. - New/updated tests: App.taskPopupViewGating.test.tsx, usePoppedOutTasks.test.ts, AppearanceSection.test.tsx, settings-default-descriptions.test.tsx, settings-defaults.test.ts. Files changed: docs/dashboard-guide.md | 5 +- docs/settings-reference.md | 1 + .../core/src/__tests__/settings-defaults.test.ts | 13 +++ packages/core/src/settings-schema.ts | 5 + packages/core/src/types.ts | 7 ++ packages/dashboard/app/App.tsx | 49 +++++++-- .../app/__tests__/App.taskPopupViewGating.test.tsx | 113 +++++++++++++++++++++ .../dashboard/app/components/SettingsModal.tsx | 3 +- .../settings/sections/AppearanceSection.tsx | 8 ++ .../sections/__tests__/AppearanceSection.test.tsx | 21 ++++ .../settings-default-descriptions.test.tsx | 1 + .../app/hooks/__tests__/usePoppedOutTasks.test.ts | 14 +++ packages/dashboard/app/hooks/useAppSettings.ts | 4 + packages/dashboard/app/hooks/usePoppedOutTasks.ts | 27 +++-- packages/i18n/locales/en/app.json | 2 + 15 files changed, 255 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-7944 Fusion-Task-Lineage: 4b8ced0e-1853-429f-8482-163821a35ae6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6b78633b07 |
FN-7943: keep Quick Chat open when portaled model/thinking-level dropdowns are clicked
Quick Chat's outside-pointer dismissal now recognizes body-portaled dropdown menus (model, thinking-level, agent, dependency, node, priority) as part of the panel instead of treating them as outside clicks. - Extend FloatingWindow's outside-pointerdown safe-surface selector to include the portaled dropdown classes used by model combobox, model nested menu, dependency, node picker, agent picker, and priority picker menus - Add regression tests covering pointerdown on each portaled dropdown surface and on a child element inside a portaled dropdown, asserting onClose is not called - Update dashboard-guide docs to describe that these portal dropdowns are treated as part of the Quick Chat panel for outside-click purposes Files changed: docs/dashboard-guide.md | 2 +- .../dashboard/app/components/FloatingWindow.tsx | 19 +++++++- .../components/__tests__/FloatingWindow.test.tsx | 50 ++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7943 Fusion-Task-Lineage: fa91bd43-241c-48b0-8858-16521f383784 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
d4001ab0ee |
feat: make merger AI model configurable under Global and Project Models
Add a dedicated merger model lane (project + global provider/model/thinking) so merge-agent sessions no longer share only the default model, without inheriting executor/planner/reviewer lanes. |
||
|
|
e35620c9aa |
FN-7939: supervise heartbeat timer-audit interval and bound non-advancing zombie re-arms
Fixes agents silently going stale for hours even though the heartbeat repair audit process was running. - HeartbeatTriggerScheduler now runs an independent watchdog (armTimerAuditWatchdog/checkTimerAuditLiveness) that tracks the audit loop's last-run timestamp and re-arms + immediately re-runs the 60s audit interval if it goes stale beyond a bounded multiple of the cadence, so a silently dropped audit driver self-heals instead of leaving active agents unrepaired for hours. - Tracks consecutive non-advancing zombie-timer re-arms per agent (nonAdvancingRearmState) and escalates once the count crosses a threshold, recording consecutiveNonAdvancingRearms/nonAdvancingEscalated in agent.metadata.heartbeatTimerRepair and logging reason=heartbeat-rearm-nonadvancing-escalated instead of silently churning the same zombie-timer-rearmed repair forever. - Clears non-advancing rearm state on unregister, non-eligible agents, paused settings, and stale-run-reap skip paths so tracking never leaks stale per-agent counters. - Watchdog and its interval handle are armed in start() and cleared in stop() alongside the existing audit interval. - Adds a changeset (patch) describing the fix, and updates docs/agents.md and docs/architecture.md to document the FN-7939 audit watchdog and non-advancing escalation behavior. - Adds heartbeat-scheduler.test.ts coverage for watchdog re-arm/liveness and non-advancing escalation. Files changed: .changeset/fn-7939-heartbeat-audit-supervision.md | 7 + docs/agents.md | 8 +- docs/architecture.md | 1 + .../src/__tests__/heartbeat-scheduler.test.ts | 209 +++++++++++++++++++++ packages/engine/src/agent-heartbeat.ts | 128 ++++++++++++- 5 files changed, 341 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-7939 Fusion-Task-Lineage: 9fa90240-4333-4588-b595-aef3811b1524 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
316d4fa034 |
FN-7941: anchor execute-requeue loop guard to monotonic terminal-step progress
Hardens the FN-7863 execute-node self-requeue loop guard so residual execute_loop_stall cases (#2043/#2045/#2046/#2047) can no longer reset the loop counter forever via non-terminal signature drift. - Change buildExecuteRequeueLoopSignature to track terminal step count (done/skipped) plus total step count instead of raw currentStep + every step status, so pending/in-progress oscillation no longer produces a "new" signature each cycle. - Add buildExecuteRequeueLoopHighWaterSignature, which derives current terminal-step progress via the shared signature parser (parseExecuteRequeueLoopProgressSignature) and only resets the streak on monotonic forward progress, keeping a high-water mark across cycles so decreases/oscillation below the high-water still count toward exhaustion. - Update executor.ts's execute self-requeue dispatch path to use the new high-water helper when deciding whether to reset (1) or increment executeRequeueLoopCount, replacing the previous raw signature-equality check. - Extend execute-requeue-loop-guard.test.ts with regression coverage: a drifting-signature case that oscillates step order/status with no terminal progress (still terminalizes at MAX_EXECUTE_REQUEUE_LOOP_CYCLES), a done/in-progress oscillation case bounded after the high-water stops increasing, and an updated "real progress never terminalizes" case driven by genuine monotonic done-step advancement. - Update docs/architecture.md's FN-7863/FN-7926 self-healing notes to describe the new terminal-step high-water signature and cross-reference FN-7941. Files changed: docs/architecture.md | 4 +- .../execute-requeue-loop-guard.test.ts | 83 +++++++++++++++++++++- packages/engine/src/executor.ts | 54 ++++++++++++-- 3 files changed, 130 insertions(+), 11 deletions(-) Fusion-Task-Id: FN-7941 Fusion-Task-Lineage: cbf1e536-d29b-40da-bdd8-8c34d8d6b1ca Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
281bb05161 |
FN-7936: alias @fusion/core to a runtime shim in bundled plugin outputs
Fix bundled example plugins (dependency-graph, grok-runtime, roadmap, acp-runtime, compound-engineering) crashing on enable with "Cannot find package '@fusion/core'" by aliasing the private import to a self-contained runtime shim during CLI bundling. - packages/cli/tsup.config.ts: drop @fusion/core from bundlePluginEntry's external list and alias it to the existing pluginSdkCoreRuntimeShim so bundled.js no longer references the private workspace package at runtime - packages/cli/src/__tests__/bundle-output.test.ts: add a regression test asserting every staged bundled plugin's bundled.js contains no bare @fusion/core import/reference - docs/PLUGIN_AUTHORING.md: document that bundled.js outputs must be self-contained and must not leak private @fusion/* workspace imports - .changeset/fn-7936-bundled-plugin-fusion-core-external.md: add a patch changeset for @runfusion/fusion describing the fix Files changed: .changeset/fn-7936-bundled-plugin-fusion-core-external.md | 7 +++++ docs/PLUGIN_AUTHORING.md | 3 +++ packages/cli/src/__tests__/bundle-output.test.ts | 30 ++++++++++++++++++++++ packages/cli/tsup.config.ts | 9 +++++-- 4 files changed, 47 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7936 Fusion-Task-Lineage: a8a391b2-9441-4a7c-92bc-f1675e1a8a0d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
1f9dcea4b6 |
FN-7935: route mailbox artifact View task to the popped-out task-detail window
Mailbox artifact "View task" now opens the producing task in the same shared, movable/resizable floating task-detail window used elsewhere in the dashboard, instead of the docked task-detail modal. - MainContent's MailboxView onOpenTask handler now calls popOutTaskDetail(task) after fetchTaskDetail resolves, instead of openDetailTask(task), matching DocumentsView's artifact-task open path - add regression test verifying mailbox artifact "View task" clicks resolve the task and route to popOutTaskDetail (not openDetailTask) - update docs/dashboard-guide.md to describe the shared movable/resizable task-detail window behavior - add changeset (patch) documenting the fix for @runfusion/fusion Files changed: .changeset/fn-7935-mailbox-artifact-view-task-popout.md | 7 ++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/components/dashboard/MainContent.tsx | 8 ++- .../MainContent.mailbox-view-task.test.tsx | 83 ++++++++++++++++++++++ 4 files changed, 97 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7935 Fusion-Task-Lineage: 51374962-aa36-4390-a6b5-b519e7fc2bf2 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
29560021d3 |
FN-7934: fix chat brain popup clipping in narrow floating windows
Fixes the in-chat model/thinking (Brain) popup being cut off inside a narrow floating Chat window or compact dock on a wide desktop viewport. - Key the popover's viewport-fitting inset layout on ChatView's .chat-view--narrow class (chat surface width) instead of only the @media (max-width: 768px) browser-viewport query, so narrow floating/docked chat surfaces get the fitted layout too. - Add narrow-surface CSS rules for .chat-thinking-level-root, .chat-thinking-popover, .chat-thinking-agent-list, and .chat-thinking-popover-list to constrain position/width/max-height to the chat surface. - Add a CSS-contract regression test asserting both the desktop popover sizing and the new narrow-surface rules stay in sync. - Update docs/dashboard-guide.md to describe the popup staying fitted to the chat surface for narrow floating Chat windows/compact docks, not just mobile/tablet viewports. - Add a patch changeset for @runfusion/fusion documenting the fix. Files changed: .changeset/fn-7934-chat-narrow-model-popup.md | 7 +++++ docs/dashboard-guide.md | 3 +- packages/dashboard/app/components/ChatView.css | 22 +++++++++++++++ .../__tests__/ChatThinkingLevelControl.test.tsx | 32 ++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7934 Fusion-Task-Lineage: 30c461c5-a153-4005-8a8c-24f02916a934 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9ba8a2e575 |
FN-7932: add per-lane Reviewer and Planning thinking-level overrides
Adds validatorThinkingLevel and planningThinkingLevel task fields so the Reviewer and Planning AI lanes can override reasoning effort independently of the shared task thinkingLevel, with dashboard UI, storage, and runtime fallback wiring. - Add validatorThinkingLevel and planningThinkingLevel to Task/TaskCreateInput types (packages/core/src/types.ts) - Persist the new fields in the SQLite schema and store read/write/replication paths (packages/core/src/db.ts, store.ts, mesh-task-replication.ts) - Wire executor and triage lanes to fall back per-lane thinking level -> task.thinkingLevel -> existing settings/lane fallback (packages/engine/src/executor.ts, triage.ts) - Add per-lane thinking-level selectors to the ModelSelectorTab UI, alongside the existing thinking-level control (packages/dashboard/app/components/ModelSelectorTab.tsx) - Expose the new fields through the legacy task API and task-workflow routes (packages/dashboard/app/api/legacy.ts, packages/dashboard/src/routes/register-task-workflow-routes.ts) - Document the new settings in dashboard-guide.md and settings-reference.md - Add a minor changeset and unit/integration test coverage for store persistence, routes, UI, and agent-session helpers Files changed: .changeset/per-lane-task-thinking.md | 7 ++ docs/dashboard-guide.md | 2 + docs/settings-reference.md | 2 +- .../src/__tests__/store-thinking-levels.test.ts | 43 +++++++ packages/core/src/db.ts | 15 ++- packages/core/src/mesh-task-replication.ts | 4 + packages/core/src/store.ts | 24 +++- packages/core/src/types.ts | 12 ++ packages/dashboard/app/api/legacy.ts | 2 + .../dashboard/app/components/ModelSelectorTab.tsx | 126 ++++++++++++++++++++- .../components/__tests__/ModelSelectorTab.test.tsx | 50 +++++++- .../src/__tests__/routes-tasks-ops.test.ts | 74 ++++++++++++ .../src/routes/register-task-workflow-routes.ts | 19 +++- .../src/__tests__/agent-session-helpers.test.ts | 15 +++ packages/engine/src/executor.ts | 16 ++- packages/engine/src/triage.ts | 8 +- 16 files changed, 395 insertions(+), 24 deletions(-) Fusion-Task-Id: FN-7932 Fusion-Task-Lineage: 4202f774-aab9-41d2-86a0-f5277dd0f848 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
967f3dd900 |
FN-7923: align task-card cost badge bottom-right with other footer chips
Narrative: Reworked TaskCard footer/meta layout so the cost badge (and its sibling footer-right chips) render inline at the bottom-right of the card-meta row when the footer has no leading content, instead of always sitting in a separate footer row beside the time badge. - Extracted the footer-right chip cluster (cost, time, retry, near-duplicate, undo-of, GitHub tracking) into a shared `footerRightCluster` render, computed once instead of duplicated inline. - Added `footerHasLeadingContent`/`footerRightHasContent`/`placeFooterRightInMeta` derivations so the cluster moves into `.card-meta` (bottom-right, inline with other tags) when there's no files-changed button or GitHub-import leading content, and the meta row is visible; otherwise it keeps the existing `.card-footer-row` placement for in-progress/tracked cards. - Updated dashboard-guide.md wording to describe the cost badge as appearing 'with the card's other footer/meta chips' rather than 'beside the execution-time badge'. - Extended TaskCard.test.tsx coverage for the new placement behavior. - Desktop local-runtime.ts: kept the previously-unused `reason` parameter on `requestRestart` explicitly referenced (void reason) for API parity/lint cleanliness, unrelated cosmetic cleanup carried in the same branch. Files changed: docs/dashboard-guide.md | 2 +- packages/dashboard/app/components/TaskCard.tsx | 242 +++++++++++---------- .../app/components/__tests__/TaskCard.test.tsx | 96 +++++++- packages/desktop/src/local-runtime.ts | 4 +- 4 files changed, 222 insertions(+), 122 deletions(-) Fusion-Task-Id: FN-7923 Fusion-Task-Lineage: 7e4c3109-f39e-45c0-af13-358ce54f945c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
30d2e3660d |
FN-7927: fix Refine feedback modal self-dismissing immediately after opening
The task-detail Refine overlay used a raw onClick backdrop handler with a stopPropagation-wrapped inner modal, so the same click/touch sequence that opened Refine could bubble into the backdrop handler and close it right away; route it through the shared useOverlayDismiss contract instead so it behaves like every other dashboard modal. - Compute refineOverlayDismissProps via useOverlayDismiss(handleCloseRefineModal) and spread it onto the refine overlay instead of a plain onClick handler - Drop the redundant stopPropagation-only onClick from the inner .detail-refine-modal div now that the overlay itself no longer misfires on the opening interaction - Update docs/dashboard-guide.md to document that the Refine modal (Board and List entry points) stays open until an explicit close or an enabled backdrop dismissal - Add TaskDetailModal.refine.test.tsx regression coverage for the modal staying open across the opening interaction and honoring the dismiss-preference gate - Add a patch changeset summarizing the fix for @runfusion/fusion release notes Files changed: .changeset/fn-7927-refine-modal.md | 7 + docs/dashboard-guide.md | 8 +- .../dashboard/app/components/TaskDetailModal.tsx | 12 +- .../__tests__/TaskDetailModal.refine.test.tsx | 182 +++++++++++++++++++++ 4 files changed, 201 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-7927 Fusion-Task-Lineage: c7e7cd4a-d103-47e6-93ce-6577147b4795 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6dcecb0c34 |
FN-7926: park completed-but-blocked tasks instead of looping execute-requeue
Stops the execute → pause-abort → re-queue-to-todo infinite loop for tasks whose implementation work is done but a dependency/blockedBy blocker is still live, by diverting them into a dedicated parked state instead of feeding the FN-7863 no-progress backstop or looping forever. - Add TaskExecutor.parkCompletedBlockedTask(): when work is complete but getTaskCompletionBlocker() still reports a blocker, park the task in todo with pausedReason:"completed-work-blocked", status:"queued", preserved worktree/branch/steps, and a cleared execute-requeue signature. - Replace shouldFinalizeCompletedTask's boolean with getCompletedTaskFinalizationDecision() returning "finalize" | "blocked" | "incomplete" so both the paused-after-completion and finalization call sites can react to the new "blocked" outcome without re-entering execution. - Divert completed-but-blocked tasks before the FN-7863 execute-requeue-loop counter increments, so waiting-on-dependency states are no longer misclassified as EXECUTION_DISPATCH_LOOP_EXHAUSTED. - Add SelfHealingManager.reconcileCompletedBlockedTasks(): a bounded sweep (wired into both startup/maintenance and periodic self-healing passes) that clears the park and advances the task to review once getTaskCompletionBlockerForStore() resolves, guarded by auto-merge eligibility, user-pause, and live-execution checks; failed advances re-park rather than strand the row. - Add run-audit mutation types task:completed-blocked-parked and task:completed-blocked-advanced (ids/counts/outcomes-only metadata) plus AGENTS.md/docs/architecture.md entries documenting the new lifecycle. - Extend execute-requeue-loop-guard.test.ts with coverage for the park/advance flow, including the zero-step task edge case. Files changed: AGENTS.md | 1 + docs/architecture.md | 2 + .../execute-requeue-loop-guard.test.ts | 256 ++++++++++++++++++++- packages/engine/src/executor.ts | 85 ++++++- packages/engine/src/run-audit.ts | 4 + packages/engine/src/self-healing.ts | 95 ++++++++ 6 files changed, 432 insertions(+), 11 deletions(-) Fusion-Task-Id: FN-7926 Fusion-Task-Lineage: e47945f4-a816-447e-9ea1-7c13105d0ba9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e84fda936a |
FN-7918: make chat go-to-top contextual and inline edit pencil compact
Reworks chat message footer affordances: the scroll-to-top control now only becomes visible once a message's top is actually clipped above the visible thread viewport, and the edit pencil moves from a standalone action row into the timestamp footer beside user messages. - ChatView measures assistant message tops on scroll/message changes (rAF-scheduled) and tracks which message IDs are currently clipped above the `.chat-messages` container edge - StandardChatMessageItem accepts a new `isTopClipped` prop; the go-to-top button stays DOM-mounted (for tests/a11y) but is visually hidden via CSS until clipped - Merged the assistant thinking/copy/scroll-to-top actions into a single collapsible footer row instead of separate action rows - Moved the user-message edit pencil into an inline `chat-message-time-row` next to the relative timestamp instead of a standalone action row above it - Updated ChatView.css for the new inline layout, collapsed-row state, and hidden/visible scroll-to-top button states - Updated message-edit and scroll-to-top tests to cover the new inline placement and clipped-visibility behavior - Added changeset and docs/dashboard-guide.md note describing the new behavior Files changed: .changeset/fn-7918-chat-inline-icons.md | 7 ++ docs/dashboard-guide.md | 6 +- packages/dashboard/app/components/ChatView.css | 80 +++++++++++++++------- packages/dashboard/app/components/ChatView.tsx | 52 +++++++++++++- .../app/components/StandardChatSurface.tsx | 33 +++++++-- .../__tests__/ChatView.message-edit.test.tsx | 34 ++++++++- .../__tests__/ChatView.scroll-to-top.test.tsx | 75 +++++++++++++++++++- 7 files changed, 253 insertions(+), 34 deletions(-) Fusion-Task-Id: FN-7918 Fusion-Task-Lineage: 76206cd2-94a8-47be-b282-94943e184d01 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
2aefaad319 |
FN-7924: Add View task link to artifact-registration mail notifications
Artifact-registration mail messages now expose the task that produced the artifact so users can jump straight to it from the mailbox. - MailboxArtifactAttachment renders a "View task" button when message.metadata.taskId is present and an onOpenTask handler is supplied, alongside the existing Open artifact affordance - MailboxModal and MailboxView thread taskId metadata and onOpenTask through to MailboxArtifactAttachment for both the message-list and detail-pane renders - MainContent wires MailboxView's onOpenTask to the shared fetchTaskDetail -> openDetailTask path, with a toast on failure, so mailbox reuses the existing task-detail flow - docs/dashboard-guide.md documents the new View task affordance for artifact notifications - adds a minor changeset for @runfusion/fusion describing the new mail notification behavior - extends MailboxArtifactAttachment and MailboxView tests to cover the new taskId/onOpenTask wiring Files changed: .changeset/fn-7924-artifact-mail-view-task-link.md | 7 ++++ docs/dashboard-guide.md | 2 +- .../app/components/MailboxArtifactAttachment.tsx | 20 ++++++++++ packages/dashboard/app/components/MailboxModal.css | 2 +- packages/dashboard/app/components/MailboxModal.tsx | 6 +++ packages/dashboard/app/components/MailboxView.tsx | 6 +++ .../__tests__/MailboxArtifactAttachment.test.tsx | 35 ++++++++++++++++- .../app/components/__tests__/MailboxView.test.tsx | 45 +++++++++++++++++++++- .../app/components/dashboard/MainContent.tsx | 6 +++ 9 files changed, 124 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-7924 Fusion-Task-Lineage: 800102e1-9025-40da-8130-9ab0e8acd747 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
313956df5a |
FN-7916: fix chat model selector dismissing before selection on mobile/tablet
Fix the in-chat Brain popup's model picker on touch viewports: pointerdown outside-close was treating the portaled CustomModelDropdown menu as outside the popup, dismissing it before the tap could register a model selection, and the mobile popover was mis-anchored off-screen. - ChatThinkingLevelControl: treat pointerdown targets inside the portaled `.model-combobox-dropdown--portal` menu as inside the popup so touch taps select the model instead of closing the popup first - ChatView.css: clamp the popover to the viewport width via max-width/max-inline-size, and on mobile anchor it to the chat input area with tokenized left/right gutters instead of a collapsing left:0 width - Add a portal-aware regression test covering pointerdown-inside-portal selection, genuine outside-pointerdown close, and the mobile CSS anchoring contract - Add changeset (patch) and update dashboard guide docs to describe mobile/tablet behavior Files changed: .changeset/fn-7916-chat-mobile-model-selector.md | 7 ++ docs/dashboard-guide.md | 3 +- .../app/components/ChatThinkingLevelControl.tsx | 10 ++- packages/dashboard/app/components/ChatView.css | 17 +++- .../ChatThinkingLevelControl.portal.test.tsx | 97 ++++++++++++++++++++++ 5 files changed, 130 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-7916 Fusion-Task-Lineage: bcc2d9ad-0be6-4e23-b643-eebf93aae3c6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
daf3f15fd1 |
FN-7909: add room-level thinking effort override for Chat Rooms
Adds a per-room thinking-effort (reasoning level) override for Chat Rooms so all room responders can share a consistent override instead of relying only on per-agent/global defaults. - Persist `chat_rooms.thinkingLevel` with a new core DB migration and store read/write support - Extend chat-store and chat-types with thinkingLevel plumbing for room create/update - Wire the dashboard chat room API/routes and legacy handlers to accept and return thinkingLevel - Add a ChatView room settings control (with CSS) and useChatRooms hook support for setting/clearing the override - Resolve room responder defaultThinkingLevel from the room override when present - Update docs (dashboard-guide, settings-reference) and add a minor changeset for the feature Files changed: .changeset/fn-7909-room-thinking-level.md | 7 +++ docs/dashboard-guide.md | 1 + docs/settings-reference.md | 2 +- packages/core/src/__tests__/chat-store.test.ts | 21 ++++++++ packages/core/src/__tests__/db-migrate.test.ts | 57 ++++++++++++++++++++++ packages/core/src/chat-store.ts | 12 ++++- packages/core/src/chat-types.ts | 10 ++++ packages/core/src/db.ts | 28 ++++++++++- packages/dashboard/app/api/__tests__/chat-rooms-api.test.ts | 8 +-- packages/dashboard/app/api/legacy.ts | 4 +- packages/dashboard/app/components/ChatView.css | 16 ++++++ packages/dashboard/app/components/ChatView.tsx | 29 ++++++++++- packages/dashboard/app/components/__tests__/ChatView.rooms.test.tsx | 24 +++++++++ packages/dashboard/app/hooks/__tests__/useChatRooms.test.ts | 22 +++++++++ packages/dashboard/app/hooks/useChatRooms.ts | 16 ++++++ packages/dashboard/src/__tests__/chat-room-routes.test.ts | 26 ++++++++++ packages/dashboard/src/__tests__/chat.rooms.test.ts | 42 ++++++++++++++++ packages/dashboard/src/chat.ts | 8 +++ packages/dashboard/src/routes/register-chat-room-routes.ts | 21 ++++++-- 19 files changed, 338 insertions(+), 16 deletions(-) Fusion-Task-Id: FN-7909 Fusion-Task-Lineage: 2741eca9-5305-4f6c-81bf-ae644a9fe307 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
1ea185daa5 |
FN-7911: add workflow validate dry-run command, tool, and API route
Adds a non-mutating `fn workflow validate` dry-run path across CLI, agent tools, and dashboard API so custom workflow IR can be checked before create/update. - Add `packages/cli/src/commands/workflow.ts` implementing `fn workflow validate <id> | --file <path>` with JSON/text output, wired into `bin.ts`. - Add `fn_workflow_validate` agent tool (`agent-tools.ts`, `index.ts`) reusing the existing parseWorkflowIr/trait/code-node/column-agent validation used by create/update, performing no persistence. - Add `POST /api/workflows/validate` route in `register-workflow-routes.ts` plus dashboard route test coverage. - Extend heartbeat tool-gating/exposure tests and gating classifications to include `fn_workflow_validate` alongside the other workflow tools. - Update CLI/agent extension docs (`docs/cli-reference.md`, `docs/agents.md`, `docs/workflow-steps.md`, fusion skill references) to document the new command/tool. - Add changeset `.changeset/fn-7911-workflow-validate.md` (minor) describing the new capability. Files changed: .changeset/fn-7911-workflow-validate.md | 7 ++ docs/agents.md | 5 +- docs/cli-reference.md | 13 ++ docs/workflow-steps.md | 3 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 10 ++ .../skill/fusion/references/fusion-capabilities.md | 1 + .../src/__tests__/extension-workflow-tools.test.ts | 1 + packages/cli/src/__tests__/extension.test.ts | 1 + .../src/__tests__/workflow-docs-current.test.ts | 1 + packages/cli/src/bin.ts | 22 ++++ packages/cli/src/commands/workflow.ts | 80 ++++++++++++ packages/cli/src/extension.ts | 10 ++ .../dashboard/src/__tests__/chat-manager.test.ts | 1 + .../dashboard/src/__tests__/chat.rooms.test.ts | 1 + .../planning-document-tools-exposure.test.ts | 1 + .../__tests__/workflow-validate-route.test.ts | 101 +++++++++++++++ .../src/routes/register-workflow-routes.ts | 27 +++- .../engine/src/__tests__/agent-action-gate.test.ts | 2 +- .../agent-workflow-tools-exposure.test.ts | 70 ++++++++++- .../src/__tests__/gating-classifications.test.ts | 3 +- .../src/__tests__/heartbeat-executor.test.ts | 37 +++--- .../src/__tests__/heartbeat-session-prompt.test.ts | 5 +- .../src/__tests__/permanent-agent-gating.test.ts | 2 +- packages/engine/src/agent-heartbeat.ts | 5 +- packages/engine/src/agent-tools.ts | 140 ++++++++++++++++++++- packages/engine/src/executor.ts | 6 + packages/engine/src/gating-classifications.ts | 2 + packages/engine/src/index.ts | 4 + 29 files changed, 532 insertions(+), 31 deletions(-) Fusion-Task-Id: FN-7911 Fusion-Task-Lineage: 903d15fe-a7ec-458f-aa34-8f2e895a9603 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
8835c6cb48 |
FN-7908: add in-chat model/agent switcher to brain-icon popup
Extend the chat brain-icon popup and its backing session PATCH route so an active Direct chat's model or agent can be switched mid-conversation instead of only being set at creation time. - Add a Model/Agent section to ChatThinkingLevelControl (the brain-icon popup) for picking a model provider/model or retargeting to a real agent without leaving the chat. - Extend PATCH /api/chat/sessions/:id to accept modelProvider/modelId (as a validated pair via the existing validateModelPair helper) and agentId, forwarding only the keys present in the body so omitted fields leave the session's stored target untouched. - Add chat-store updateSession support for the agentId clause alongside the existing model/thinkingLevel fields, and a useChat.setSessionModel hook for the dashboard to call the new PATCH capability. - Update i18n locale strings (en/es/fr/ko/zh-CN/zh-TW) and dashboard-guide.md docs for the new switcher UI. - Add unit/integration test coverage across chat-store, chat-manager, chat-routes, useChat, ChatThinkingLevelControl, and ChatView for the new model/agent switch behavior. - Add changeset fn-7908-chat-model-agent-switcher.md (minor, @runfusion/fusion). Files changed: .changeset/fn-7908-chat-model-agent-switcher.md | 7 + docs/dashboard-guide.md | 3 +- packages/core/src/__tests__/chat-store.test.ts | 21 ++ packages/core/src/chat-store.ts | 8 + packages/core/src/chat-types.ts | 2 + packages/dashboard/app/api/legacy.ts | 11 +- .../app/components/ChatThinkingLevelControl.tsx | 219 ++++++++++++++++++--- packages/dashboard/app/components/ChatView.css | 135 ++++++++++++- packages/dashboard/app/components/ChatView.tsx | 23 ++- .../__tests__/ChatThinkingLevelControl.test.tsx | 109 +++++++++- .../__tests__/ChatView.thinking-level.test.tsx | 67 ++++++- .../dashboard/app/hooks/__tests__/useChat.test.ts | 166 +++++++++++++++- packages/dashboard/app/hooks/useChat.ts | 56 ++++++ .../dashboard/src/__tests__/chat-manager.test.ts | 38 ++++ .../dashboard/src/__tests__/chat-routes.test.ts | 117 ++++++++++- .../dashboard/src/routes/register-chat-routes.ts | 48 ++++- packages/i18n/locales/en/app.json | 8 +- packages/i18n/locales/es/app.json | 8 +- packages/i18n/locales/fr/app.json | 8 +- packages/i18n/locales/ko/app.json | 8 +- packages/i18n/locales/zh-CN/app.json | 8 +- packages/i18n/locales/zh-TW/app.json | 8 +- 22 files changed, 1007 insertions(+), 71 deletions(-) Fusion-Task-Id: FN-7908 Fusion-Task-Lineage: b1104865-9b0c-4d77-973e-89152fe245e0 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3326984a6d |
FN-7913: add fn plugin publish --dry-run preflight command
Adds a non-mutating `fn plugin publish` CLI command that preflights a plugin before manual pack/publish, giving external plugin authors an offline readiness check. - New `packages/cli/src/commands/plugin-publish.ts` with `runPluginPublish`, `collectPluginPreflight`, and `classifyVersionBump` (strict x.y.z semver bump classification), reusing `loadManifestFromPath` / `resolvePluginEntryFile` from the install path - Wire `fn plugin publish <path> [--dry-run] [--previous-version <semver>]` into `bin.ts` command routing, dynamic import list, and help text - Add test coverage in `plugin-publish.test.ts` and update `bin.test.ts` for the new subcommand - Update `docs/PLUGIN_AUTHORING.md`, `docs/cli-reference.md`, and `docs/plugins/external-authoring.md` to document the new preflight command - Add changeset `.changeset/fn-7913-plugin-publish-dry-run.md` (minor, @runfusion/fusion) Files changed: .changeset/fn-7913-plugin-publish-dry-run.md | 7 + docs/PLUGIN_AUTHORING.md | 9 +- docs/cli-reference.md | 5 +- docs/plugins/external-authoring.md | 14 +- packages/cli/src/__tests__/bin.test.ts | 2 +- packages/cli/src/__tests__/plugin-publish.test.ts | 197 ++++++++++++++++ packages/cli/src/bin.ts | 22 +- packages/cli/src/commands/plugin-publish.ts | 272 ++++++++++++++++++++++ 8 files changed, 521 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-7913 Fusion-Task-Lineage: 27bdf937-3195-4619-9d01-b6af4fbba487 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b85a6b8663 |
FN-7912: add quarantine-ledger deadline visibility check
Add a report-only script that surfaces flaky-test quarantine entries approaching their 14-day deletion clock, so maintainers can make deliberate rescue-or-expire decisions before entries silently expire. - Add scripts/check-quarantine-ledger.mjs: reads scripts/lib/test-quarantine.json, computes days-remaining against the existing 14-day deletion clock (shared DELETION_CLOCK_DAYS from scripts/test-velocity-baseline.mjs), and buckets each entry as expired/near/healthy/unknown - Support --warn-within=<days> (default 5) to tune the near-deadline window, --json for machine-readable output, and --strict as an opt-in local/CI gate (exits 1 on expired/near entries) while default mode stays exit-0 and non-blocking - Wire pnpm check:quarantine-ledger script in package.json - Add scripts/__tests__/check-quarantine-ledger.test.mjs covering deadline bucketing/sorting, empty/missing ledger handling, --strict behavior, and --json output shape - Document the new command and its flags in docs/testing.md under the quarantine ledger/deletion ratchet section Files changed: docs/testing.md | 10 + package.json | 1 + scripts/__tests__/check-quarantine-ledger.test.mjs | 159 ++++++++++++++++ scripts/check-quarantine-ledger.mjs | 202 +++++++++++++++++++++ 4 files changed, 372 insertions(+) Fusion-Task-Id: FN-7912 Fusion-Task-Lineage: c08e2e09-473a-4ad0-8c27-43cbc3355168 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
03bca1787b |
FN-7907: Add project chat default (model/agent) setting and New Chat default behavior
Adds a project-scoped Direct-chat default (model pair or durable agent) plus a New Chat behavior toggle (prompt vs. always-use-default), surfaced in Project Models settings using the standard model dropdown. - Add ProjectSettings fields: chatNewSessionMode, chatDefaultKind, chatDefaultAgentId, chatDefaultModelProvider, chatDefaultModelId, chatDefaultThinkingLevel - Extend settings-schema validation for the new chat default fields - Add a "Chat" subsection to ProjectModelsSection with New Chat behavior selector, model/agent target toggle, and the standard CustomModelDropdown for model selection - Update ChatView.tsx handleNewChat() flow to honor the configured default (prompt vs. immediate session creation) - Add i18n strings across en/es/fr/ko/zh-CN/zh-TW locales and regenerate resources.d.ts - Add changeset (@runfusion/fusion minor) and update docs/settings-reference.md and docs/dashboard-guide.md - Add settings-parity, ChatView new-chat-default, and ProjectModelsSection chatDefault test coverage Files changed: .changeset/fn-7907-chat-default.md | 7 + docs/dashboard-guide.md | 3 +- docs/settings-reference.md | 8 + packages/core/src/__tests__/settings-parity.test.ts | 20 ++ packages/core/src/settings-schema.ts | 6 + packages/core/src/types.ts | 15 + packages/dashboard/app/components/ChatView.tsx | 93 +++++- packages/dashboard/app/components/__tests__/ChatView.new-chat-default.test.tsx | 357 +++++++++++++++++++++ packages/dashboard/app/components/settings/sections/ProjectModelsSection.tsx | 77 +++++ packages/dashboard/app/components/settings/sections/__tests__/ProjectModelsSection.chatDefault.test.tsx | 193 +++++++++++ packages/i18n/locales/en/app.json | 20 +- packages/i18n/locales/es/app.json | 20 +- packages/i18n/locales/fr/app.json | 20 +- packages/i18n/locales/ko/app.json | 20 +- packages/i18n/locales/zh-CN/app.json | 20 +- packages/i18n/locales/zh-TW/app.json | 20 +- packages/i18n/src/resources.d.ts | 18 ++ 17 files changed, 899 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-7907 Fusion-Task-Lineage: 86a81b9c-7e0c-4033-9e2a-6f7380810fe9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
41168e273c |
FN-7905: surface resolved default in chat thinking-level labels
Chat thinking-level Default entries hardcoded "off" instead of showing the project/global resolved default, misleading operators about which level would actually apply. - ChatView now fetches Settings via fetchSettings on mount and derives resolvedDefaultThinkingLevel from settings.defaultThinkingLevel (falling back to "off") - Threads resolvedDefaultThinkingLevel into NewChatDialog and the in-chat ChatThinkingLevelControl (Brain popup) so both surfaces show e.g. "Default (medium)" instead of always "Default (off)" - ChatThinkingLevelControl accepts a new optional defaultThinkingLevel prop (defaults to "off" for backward compatibility) used only for the Default/clear option label - Updates dashboard-guide.md to document the resolved-default label behavior in both the New Chat dialog and the in-chat Brain popup - Adds/updates tests: new ChatThinkingLevelControl coverage for the label prop, new ChatView.thinking-level test coverage for the resolved default, and mocks fetchSettings in the other ChatView test suites that render ChatView - Adds a patch changeset for @runfusion/fusion Files changed: .changeset/fn-7905-chat-thinking-default.md | 7 ++++ docs/dashboard-guide.md | 5 +-- .../app/components/ChatThinkingLevelControl.tsx | 9 +++-- packages/dashboard/app/components/ChatView.tsx | 36 ++++++++++++++++--- .../__tests__/ChatThinkingLevelControl.test.tsx | 16 +++++++++ .../__tests__/ChatView.chat-commands.test.tsx | 1 + .../__tests__/ChatView.content-search.test.tsx | 1 + .../__tests__/ChatView.context-window.test.tsx | 1 + .../__tests__/ChatView.copy-response.test.tsx | 1 + .../__tests__/ChatView.core-contracts.test.tsx | 1 + .../__tests__/ChatView.core-interactions.test.tsx | 1 + .../components/__tests__/ChatView.core.test.tsx | 1 + .../__tests__/ChatView.default-model-icon.test.tsx | 1 + .../__tests__/ChatView.hash-mention.test.tsx | 1 + .../components/__tests__/ChatView.mobile.test.tsx | 1 + .../__tests__/ChatView.sessions-rooms.test.tsx | 1 + .../__tests__/ChatView.streaming-thread.test.tsx | 1 + .../__tests__/ChatView.thinking-level.test.tsx | 42 ++++++++++++++++++++++ 18 files changed, 119 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-7905 Fusion-Task-Lineage: 7290aa8a-f812-43c3-8ba5-fc31a3f4579e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9f8db7d1b5 |
FN-7903: wire thinkingLevel into AI session creation for automation steps
Threads the persisted per-step Thinking Level (from FN-7900) into runtime AI session creation and task spawning, so scheduled, routine, and manual automation runs actually apply the chosen reasoning effort instead of only storing it. - CronRunner passes step.thinkingLevel through AiPromptExecutor to createFnAgent's defaultThinkingLevel for scheduled AI-prompt steps - RoutineRunner forwards step.thinkingLevel to the shared AiPromptExecutor seam for routine AI-prompt steps - Cron/routine create-task steps map step.thinkingLevel onto TaskCreateInput.thinkingLevel so spawned tasks inherit the configured reasoning effort - Dashboard's inline/manual AI-prompt and create-task automation routes apply the same defaultThinkingLevel / TaskCreateInput.thinkingLevel behavior - Updated docs (dashboard-guide.md, settings-reference.md) to describe the now-active runtime behavior - Added a changeset for @runfusion/fusion (minor) and expanded cron-runner/routine-runner/routes-automation test coverage Files changed: .changeset/fn-7903-automation-thinking-level.md | 7 ++ docs/dashboard-guide.md | 5 +- docs/settings-reference.md | 2 +- .../src/__tests__/routes-automation.test.ts | 67 +++++++++++++++++++ packages/dashboard/src/routes.ts | 10 +++ packages/engine/src/__tests__/cron-runner.test.ts | 75 +++++++++++++++++++++- .../engine/src/__tests__/routine-runner.test.ts | 68 +++++++++++++++++++- packages/engine/src/cron-runner.ts | 21 +++++- packages/engine/src/routine-runner.ts | 11 +++- 9 files changed, 255 insertions(+), 11 deletions(-) Fusion-Task-Id: FN-7903 Fusion-Task-Lineage: c7eb4660-975d-4c5d-820e-0f1a3ac8b6a6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
23cb061af2 |
FN-7898: add in-chat thinking-level control next to the attach button
Adds a Brain-icon popover control in the chat composer that lets users change an existing session's reasoning-effort level mid-conversation, extending the existing create-time-only thinking-level picker. - Add ChatThinkingLevelControl component (Brain-icon trigger + popover) wired into ChatView's direct-session composer, gated to non-CLI model-loop sessions only - Extend PATCH /api/chat/sessions/:id to accept an optional thinkingLevel field, validated via existing validateThinkingLevel helper; null/empty string explicitly clears back to the inherited default, omitting the key leaves it untouched - Add useChat().setSessionThinkingLevel hook method to call the new PATCH capability - Add i18n strings (thinkingLevelButton) across all locales and regenerate packages/i18n/src/resources.d.ts - Add changeset for @runfusion/fusion (minor) and update docs/dashboard-guide.md Files changed: .changeset/fn-7898-chat-thinking-level-control.md | 7 + docs/dashboard-guide.md | 2 + packages/dashboard/app/api/legacy.ts | 4 +- .../app/components/ChatThinkingLevelControl.tsx | 133 +++++++++++ packages/dashboard/app/components/ChatView.css | 73 ++++++ packages/dashboard/app/components/ChatView.tsx | 20 ++ .../__tests__/ChatThinkingLevelControl.test.tsx | 103 ++++++++ .../__tests__/ChatView.message-edit.test.tsx | 1 + .../components/__tests__/ChatView.test-harness.tsx | 1 + .../__tests__/ChatView.thinking-level.test.tsx | 262 +++++++++++++++++++++ .../dashboard/app/hooks/__tests__/useChat.test.ts | 108 +++++++++ packages/dashboard/app/hooks/useChat.ts | 59 +++++ .../dashboard/src/__tests__/chat-routes.test.ts | 83 +++++++ .../dashboard/src/routes/register-chat-routes.ts | 35 ++- packages/i18n/locales/en/app.json | 1 + packages/i18n/locales/es/app.json | 1 + packages/i18n/locales/fr/app.json | 1 + packages/i18n/locales/ko/app.json | 1 + packages/i18n/locales/zh-CN/app.json | 1 + packages/i18n/locales/zh-TW/app.json | 1 + packages/i18n/src/resources.d.ts | 96 ++++++-- 21 files changed, 970 insertions(+), 23 deletions(-) Fusion-Task-Id: FN-7898 Fusion-Task-Lineage: a052a6ef-d2d3-45af-9b92-221996780b1b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
635d78248b |
FN-7900: persist thinkingLevel override for schedule and routine AI steps
Adds a persisted, optional per-step reasoning-effort (thinkingLevel) override for AI-capable schedule and routine automation steps, surfaced in the editors and validated at the route layer. - Add optional AutomationStep.thinkingLevel field (packages/core/src/automation.ts), riding the existing JSON steps blob so no DB migration is needed; runtime application of the level is deferred to a follow-up. - Validate thinkingLevel in dashboard route step validation against the shared THINKING_LEVELS set, rejecting unknown values (packages/dashboard/src/routes.ts). - Add Thinking Level controls to RoutineEditor, ScheduleForm, and ScheduleStepsEditor so users can set/inherit the override per step. - Extend core and dashboard test suites (automation-store, routine-store, RoutineEditor, ScheduleForm, ScheduleStepsEditor, routes-automation) to cover persistence, validation, and UI behavior. - Update dashboard-guide.md docs and add a minor changeset for the new feature. Files changed: .changeset/fn-7900-automation-thinking-level.md | 7 + docs/dashboard-guide.md | 3 +- .../core/src/__tests__/automation-store.test.ts | 45 ++++++ packages/core/src/__tests__/routine-store.test.ts | 46 +++++++ packages/core/src/automation.ts | 9 ++ .../dashboard/app/components/RoutineEditor.tsx | 21 ++- packages/dashboard/app/components/ScheduleForm.tsx | 28 +++- .../app/components/ScheduleStepsEditor.tsx | 21 ++- .../components/__tests__/RoutineEditor.test.tsx | 99 +++++++++++++- .../app/components/__tests__/ScheduleForm.test.tsx | 137 +++++++++++++++++-- .../__tests__/ScheduleStepsEditor.test.tsx | 83 +++++++++-- .../src/__tests__/routes-automation.test.ts | 152 +++++++++++++++++++++ packages/dashboard/src/routes.ts | 10 ++ 13 files changed, 622 insertions(+), 39 deletions(-) Fusion-Task-Id: FN-7900 Fusion-Task-Lineage: 812a9a8c-ad0f-462f-b1c6-9900f70e4261 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7a51f95b38 |
FN-7901: persist thinkingLevel for insight model selection
Adds a persisted Thinking Level (reasoning-effort) selector to manual insight generation, threading the selection through the dashboard API, insight run metadata, and retries. - Add inline Thinking Level selector to the InsightsView model-config popover, persisted to localStorage (fusion-insight-thinking) - Thread thinkingLevel through triggerInsightRun (legacy API client) and useInsights.runInsights - Validate and store thinkingLevel in insight run inputMetadata.metadata on the POST /insights/run route; resolve it via resolvePlanningThinkingLevel for the actual generation call - Recover and reapply the original run's thinkingLevel on retry (retryInsightRunLifecycle) so retries reuse the same reasoning-effort setting - Export resolvePlanningThinkingLevel from @fusion/engine - Document the new Thinking Level selector in docs/dashboard-guide.md - Add a minor changeset for @runfusion/fusion Files changed: .changeset/fn-7901-insight-thinking-level.md | 7 ++ docs/dashboard-guide.md | 1 + .../app/__tests__/insight-model-selector.test.tsx | 41 ++++++++++- packages/dashboard/app/api/legacy.ts | 2 + packages/dashboard/app/components/InsightsView.tsx | 24 +++++- .../app/hooks/__tests__/useInsights.test.ts | 36 ++++++++- packages/dashboard/app/hooks/useInsights.ts | 6 +- .../src/__tests__/insights-routes.test.ts | 86 ++++++++++++++++++++++ packages/dashboard/src/insights-routes.ts | 36 ++++++++- packages/engine/src/index.ts | 1 + 10 files changed, 227 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-7901 Fusion-Task-Lineage: a6249526-e97d-403e-b853-e497d16f425b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b98314923c |
FN-7899: add thinking-level editing to Agent Detail, Onboarding, and bulk task model selectors
Bring thinking-level (reasoning effort) editing to every remaining model selector surface that previously lacked it, so operators can set it consistently from Agent Detail, Agent Onboarding, and the List view's bulk task editor, in addition to the batch-update-models API and route that back them. - Agent Detail config tab: persist/edit a built-in agent's runtimeConfig.thinkingLevel inline via the shared model dropdown, with dirty-state and reset tracking. - Agent Onboarding modal: replace the read-only thinking-level input with an editable control wired into the same model dropdown used for creation. - List view bulk edit toolbar: add a "no change" / "use default" / explicit-level thinking selector alongside executor/reviewer model and node overrides, wired through to the bulk apply action. - Dashboard API client (`batchUpdateTaskModels`) and `/api/tasks/batch-update-models` route: accept and validate an optional `thinkingLevel` field (against `THINKING_LEVELS`), applying it per task alongside existing model/node updates. - Update dashboard-guide.md docs and add regression tests across AgentDetailView, AgentOnboardingModal, ListView, and the batch-update-models route. - Add a minor changeset documenting the feature for release notes. Files changed: .changeset/thinking-level-selector-parity.md | 7 ++ docs/dashboard-guide.md | 5 +- packages/dashboard/app/api/legacy.ts | 3 + .../dashboard/app/components/AgentDetailView.tsx | 20 +++++- .../app/components/AgentOnboardingModal.tsx | 11 +++- packages/dashboard/app/components/ListView.tsx | 54 +++++++++++++--- .../__tests__/AgentDetailView.settings.test.tsx | 45 +++++++++++++ .../__tests__/AgentDetailView.test-helpers.ts | 19 +++++- .../__tests__/AgentOnboardingModal.test.tsx | 41 +++++++++++- .../app/components/__tests__/ListView.test.tsx | 43 ++++++++++++- .../src/__tests__/routes-tasks-ops.test.ts | 75 ++++++++++++++++++++++ .../src/routes/register-task-workflow-routes.ts | 22 +++++-- 12 files changed, 323 insertions(+), 22 deletions(-) Fusion-Task-Id: FN-7899 Fusion-Task-Lineage: fd584ce4-42b3-4c20-8de5-4d3c8963f593 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
02fdb4c089 |
FN-7897: reserve footer space for pinned below-mode terminal
Fix the pinned (below-mode) terminal panel rendering underneath the fixed ExecutorStatusBar footer, so its bottom action-control row stays visible on desktop and mobile alike. - Add a footerVisible prop to TerminalModal, wired from App.tsx's executorFooterVisible state - Add .terminal-below-host--with-footer CSS modifier that redeclares --executor-footer-height and reserves padding-bottom (with the Android Chrome ICB offset), matching the .project-content--with-footer/.left-sidebar-nav--with-footer/.right-dock--with-footer precedent - Update dashboard-guide.md terminal walkthrough to describe the new footer-avoidance behavior - Add/extend TerminalModal tests covering the footerVisible prop and CSS modifier - Add a patch changeset for @runfusion/fusion documenting the fix Files changed: .../fn-7897-pinned-terminal-footer-overlap.md | 7 ++ docs/dashboard-guide.md | 2 +- packages/dashboard/app/App.tsx | 1 + .../dashboard/app/components/TerminalModal.css | 12 ++ .../dashboard/app/components/TerminalModal.tsx | 17 ++- .../components/__tests__/TerminalModal.test.tsx | 135 ++++++++++++++++++++- 6 files changed, 170 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-7897 Fusion-Task-Lineage: e22db6a9-35a8-46a1-8c15-186eaf5267fd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |