Commit Graph

263 Commits

Author SHA1 Message Date
gsxdsm
f8173ac7f8 feat(cli): auto-load .env and .env.local from cwd
On every CLI invocation, parse .env (and .env.local if present) from
the working directory into process.env before dispatching to command
handlers. Existing shell-exported variables always win — the loader
never clobbers an explicitly-set value. .env.local overrides .env.

Motivation: FUSION_DAEMON_TOKEN (and soon other config knobs) is more
ergonomic as a gitignored local file than as a shell export each
session. Without this, `fn dashboard` falls back to auto-generating a
new token on every restart, which means the banner URL changes every
time and stale localStorage tokens silently return 401 on every API
call.

SSE and WebSocket clients already carry the token via appendTokenQuery
(fn_token= query-string fallback, since EventSource and WebSocket
constructors cannot set Authorization headers) — verified: every
`new EventSource` and `new WebSocket` call site is wrapped.

Hand-rolled minimal parser (no new dependency) to keep the bundled
single-binary CLI lean. Supports KEY=value, quoted values, comments,
blank lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:48:47 -07:00
gsxdsm
7e3c68249e feat(dashboard): bearer-token auth with browser persistence + MIT license
Pre-release polish. Two related changes bundled because they both land the
project on public-release footing:

Dashboard auth
- fn dashboard now gates the HTTP API + terminal/badge WebSockets behind a
  bearer token by default. Token resolution order: --token flag,
  FUSION_DASHBOARD_TOKEN env, FUSION_DAEMON_TOKEN env (back-compat), or an
  auto-generated fn_<32 hex>. --no-auth disables. The startup banner prints
  a click-to-open URL with ?token=<token> embedded.
- Auth middleware now also accepts fn_token=<token> as a query-string
  fallback so EventSource and WebSocket clients (which can't set custom
  headers) still authenticate.
- setupTerminalWebSocket / setupBadgeWebSocket now refuse unauthenticated
  upgrades with a proper 401 + socket close.
- Frontend: new auth.ts module captures ?token= off the URL into
  localStorage (key fn.authToken), strips it from the visible URL via
  replaceState, and installs a window.fetch wrapper that injects
  Authorization: Bearer <token> on every same-origin /api/* request.
  EventSource/WebSocket URL builders (api.ts, sse-bus.ts, useTerminal,
  useBadgeWebSocket) route through appendTokenQuery().

MIT license
- LICENSE file at repo root.
- license: "MIT" on root package.json and every packages/*/package.json,
  plus description/bugs metadata on the CLI package.

Docs
- docs/cli-reference.md documents --token / --no-auth / FUSION_DASHBOARD_TOKEN
  and the click-to-open auth flow.
- docs/getting-started.md, docs/docker.md, README.md point at the new flow
  and the CLI reference section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:12:00 -07:00
Fusion
c21e6fef15 perf(executor): recover approved steps on engine restart
When the engine restarts mid-step, an in-progress step may have already
passed plan + code review but not yet been flipped to done by the agent's
next task_update call. Previously, the next executor pass re-entered the
step and replayed both reviews — measured at 5-20 min of pure waste per
restart (observed in FN-2215 Step 1 and FN-2207 Step 6).

recoverApprovedStepsOnResume scans the task log for any in-progress step
whose most recent "code review Step N: APPROVE" entry is newer than its
most recent "Step N → pending" transition, and marks those steps done
before execute() runs. Safely skips steps that were reset after approval
(e.g. by a workflow revision) or only received REVISE verdicts.

Called from both the engine-restart path (resumeOrphaned) and the
unpause path, matching the two places the task log shows as vulnerable
to this race.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:12:00 -07:00
Fusion
e1af5bbfe4 feat(FN-2202): reposition and restyle task card agent badge
- Move the assigned agent badge out of the card header into a dedicated metadata row below task details
- Add a new .card-agent-row container to control spacing and alignment for the badge block
- Update .card-agent-badge styling to use token-aligned pill radius and color-mix backgrounds while removing monospace/fixed-width conventions
- Expand TaskCard agent badge tests to verify new DOM placement and enforced badge style rules
2026-04-21 00:32:12 -07:00
Fusion
0b9bf62a2a refactor(FN-2162): rename kb-agent identifiers to fn-agent
- Rename core loader, dashboard server chat/planning routes, and frontend agent IDs/storage keys from kb-agent to fn-agent naming
- Update dashboard hooks and components (agent list, chat view, quick chat) to use the new fn agent key prefixes consistently
- Refresh engine, dashboard, core, and CLI tests/mocks to remove remaining kb-agent route and temp prefix references
- Update storage/gap-analysis docs to reflect fn agent key names and add a @gsxdsm/fusion patch changeset for the rename
2026-04-19 20:48:14 -07:00
Fusion
a93f0f2ef9 feat(FN-2160): add push-after-merge remote sync workflow
- Add project settings for pushAfterMerge and pushRemote with defaults and typed merge result fields for push status/errors
- Implement post-merge remote sync in the merger with pull --rebase, auto/AI conflict resolution, and one non-fast-forward retry before push
- Expose push-after-merge controls in Settings modal with conditional Push Remote input and coverage for desktop/mobile save flows
- Document the new settings in the settings reference and stabilize CLI cross-build help test timeout
2026-04-19 20:48:13 -07:00
gsxdsm
3472c122d4 feat(FN-2123): merge fusion/fn-2123 2026-04-19 10:13:34 -07:00
Fusion
9a532316cf test(FN-2122): isolate HOME for core and CLI vitest runs
- Add test setup files in core and CLI that override HOME to a per-worker temp directory
- Wire the new isolation setup into core and CLI vitest setupFiles before existing test bootstrap
- Add a core canary test to verify HOME, homedir(), and defaultGlobalDir() resolve under isolated temp paths
- Document how global HOME isolation complements per-fixture isolation in test-project utilities
2026-04-19 10:13:34 -07:00
Fusion
2f611ce489 fix(FN-2107): normalize task ID handling for task creation tools
- Return the created task ID directly from runTaskPlan and propagate it through fn_task_plan
- Remove hardcoded FN-### log parsing so CLI extension supports structured IDs like PROJ-042
- Add heartbeat fallback parsing from task_create text output when details.taskId is absent
- Expand engine and CLI tests to cover structured task IDs and updated task-plan return behavior
2026-04-19 10:13:34 -07:00
Fusion
af104006ba feat(FN-2102): merge fusion/fn-2102 2026-04-19 10:13:33 -07:00
gsxdsm
bc841e9bf6 test: enforce test-directory isolation across all packages
Introduce a shared test-utils module and global vitest setup that
guarantee tests never write to the real .fusion directory or leak temp
directories under /tmp.

Infrastructure:
- packages/core/src/__test-utils__/workspace.ts — tempWorkspace(),
  useIsolatedCwd(), trackForCleanup(), assertOutsideRealFusion() with
  auto-cleanup in afterEach.
- packages/core/src/__test-utils__/vitest-setup.ts — per-worker guard:
  chdirs each worker into an isolated tmp dir, wraps process.chdir to
  refuse the real .fusion, scopes tmp dirs under fusion-test-workers/
  (skips cwd change in thread-pool workers where chdir isn't supported).
- packages/core/src/__test-utils__/vitest-teardown.ts — globalSetup
  hook that wipes the shared parent even when workers are SIGKILLed.
- scripts/check-test-isolation.mjs + `test:isolated` / `test:check-
  isolation` scripts for CI.
- @fusion/test-utils alias + setupFiles + globalSetup wired into core,
  cli, engine, dashboard, tui vitest configs; matching tsconfig paths.

Test refactors (no behavior change):
- cli provider-settings, auth-paths, provider-auth — switch leaking
  mkdtempSync calls to tempWorkspace().
- core migration, first-run, store-backward-compat — replace manual
  process.chdir save/restore with useIsolatedCwd().
- tui fusion-context — replace 9 hardcoded tmp paths (collision-prone
  under parallelism) with tempWorkspace().
- dashboard useTheme, FileBrowser, TaskCard — resolve source-file reads
  against a PACKAGE_ROOT computed from import.meta.url instead of cwd,
  so tests don't depend on the process working directory.

Verified: full suite (~15,500 tests across 8 packages + plugins) passes
and the orphan-detector reports zero leaked temp directories after a
complete run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:41:47 -07:00
Fusion
96aeb67a4f feat(FN-2014): merge fusion/fn-2014 2026-04-17 14:31:43 -07:00
gsxdsm
633ed3e591 feat(FN-2001): merge fusion/fn-2001 2026-04-17 14:09:45 -07:00
gsxdsm
ef696f4564 fix(FN-1572): stabilize fusion agent execution 2026-04-17 12:13:39 -07:00
gsxdsm
383033fcdc fix(FN-000): align qmd memory lifecycle 2026-04-17 09:41:05 -07:00
gsxdsm
75dc5a8c77 fix(FN-000): simplify qmd memory settings 2026-04-17 08:16:36 -07:00
gsxdsm
d51992850f fix(FN-1952): merge pi package settings 2026-04-17 00:20:19 -07:00
gsxdsm
32a92e961e fix(FN-1952): restore pi auth and extension loading 2026-04-16 21:42:16 -07:00
gsxdsm
22e411a82b fix(FN-1952): read legacy oauth auth 2026-04-16 21:16:48 -07:00
gsxdsm
a313037cca fix(FN-1952): recover failed review tasks 2026-04-16 20:52:50 -07:00
gsxdsm
95ced20e40 fix(FN-1952): read legacy pi auth keys 2026-04-16 20:23:48 -07:00
gsxdsm
5adb013c4e fix(FN-1952): use fusion storage for pi config 2026-04-16 20:12:09 -07:00
Fusion
74b4d20c45 feat(FN-1887): merge fusion/fn-1887 2026-04-16 10:58:19 -07:00
gsxdsm
3a0350da60 feat(FN-1908): merge fusion/fn-1908 2026-04-16 08:38:41 -07:00
Fusion
6e784c3700 feat(FN-1813): merge fusion/fn-1813 2026-04-16 05:37:24 -07:00
gsxdsm
ed778db7c2 feat(FN-1743): merge fusion/fn-1743 2026-04-16 01:08:05 -07:00
Fusion
a9d6c4159d feat(FN-1899): implement login timeout, cancellation, and 409 conflict handling
- Add login outcome types (timeout, success, failed) and state tracking via stepData
- Implement login timeout after MAX_POLL_CYCLES (150 polls × 2s = 5 minutes) with warning toast
- Add 409 Conflict detection for concurrent login attempts with warning toast
- Add cancellation capability for in-progress logins with cleanup and state reset
- Update ModelOnboardingModal tests to cover timeout and concurrent login scenarios
2026-04-16 01:05:18 -07:00
gsxdsm
dfb0a836be feat(FN-1770): merge fusion/fn-1770 2026-04-16 00:23:05 -07:00
Fusion
e874a3ca06 feat(FN-1644): rename default global data directory from ~/.pi/fusion to ~/.fusion
- Change default global directory from ~/.pi/fusion to ~/.fusion
- Add migration logic to copy existing data from old directory to new location
- Update all core packages (store, settings, central-core, central-db) to use new default path
- Update all documentation references from ~/.pi/fusion to ~/.fusion
- Add test for ~/.pi/fusion migration path with updated mock paths
- Include changeset for @gsxdsm/fusion minor version bump
2026-04-15 13:33:27 -07:00
Fusion
cd8dfa452f refactor(FN-1633): migrate MessageStore from filesystem to SQLite backend
- Replace filesystem-based message storage with SQLite backend
- Add MessageStore class using better-sqlite3 with WAL mode
- Update message.ts CLI command to use new MessageStore API
- Update dashboard routes and engine runtime for SQLite integration
- Update all related tests for new storage implementation
2026-04-15 07:02:38 -07:00
Fusion
a0b3840dde feat(FN-1832): add daemon mode and auth middleware integration tests
- Add --daemon flag to fn serve command for headless node mode
- Add daemon-aware messaging to fn node connect showing authentication status
- Add auth middleware integration tests covering subpath /api/health
- Fix test isolation issues in node.test.ts with proper mock cleanup
- Fix SkillsView onClose prop type issue
- Add CLI integration tests for daemon mode validation
2026-04-15 06:14:53 -07:00
Fusion
b316b2e552 feat(FN-1832): add daemon mode and auth-aware node connect
- Add --daemon flag to fn serve command for headless node operation
- Add daemon-aware messaging to fn node connect (shows auth status)
- Add CLI integration tests for daemon mode (serve.test.ts)
- Add auth middleware integration tests (auth-middleware-integration.test.ts)
- Fix auth middleware test for /api/health subpath
- Update bin.test.ts with daemon mode test coverage
2026-04-15 05:27:07 -07:00
gsxdsm
bae5d1d812 feat(FN-1831): merge fusion/fn-1831 2026-04-15 04:01:02 -07:00
Fusion
afd8608683 feat(FN-1805): integrate PeerExchangeService into dashboard and serve runtimes
- Wire PeerExchangeService into dashboard runtime with proper lifecycle management
- Wire PeerExchangeService into serve runtime for headless node mode
- Add mDNS discovery startup/shutdown coordination during runtime lifecycle
- Add comprehensive dashboard tests for peer exchange and discovery lifecycle
- Add serve tests covering node lifecycle and peer discovery operations
2026-04-15 01:23:57 -07:00
gsxdsm
614f501b31 feat(FN-1810): merge fusion/fn-1810 2026-04-14 12:45:15 -07:00
gsxdsm
a10151d234 feat(FN-1798): merge fusion/fn-1798 2026-04-14 12:45:09 -07:00
gsxdsm
f9f7aff3ec fix(engine): add cross-process merge guard to prevent concurrent merges
Multiple engine processes (dashboard + serve) share the same SQLite database
but each has its own in-memory merge queue. Without a cross-process check,
two processes can start merging different tasks simultaneously.

Added store.getActiveMergingTask() as a DB-level check before any merge
starts. The drainMergeQueue defers with pollIntervalMs delay, and both
aiMergeTask and processPullRequestMergeTask have safety-net checks.
Also moved stale merge status cleanup to run regardless of autoMerge setting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:22:10 -07:00
gsxdsm
748db6c605 feat(FN-1506): add skills registry and configuration API
- Add skills discovery API (GET /api/skills/discovered) to list available skills with enabled state
- Add skills execution toggle API (PATCH /api/skills/execution) for enabling/disabling skills with project-scoped persistence
- Add skills catalog API (GET /api/skills/catalog) with resilient fallback to fetch skills.sh catalog
- Skills are stored in project settings (.fusion/settings.json) with support for both top-level and package-scoped skills
- Add SkillsAdapter runtime class for skills discovery, catalog fetching, and execution toggle
- Add comprehensive tests for all skills API endpoints
- Update dashboard, serve, and provider-settings commands with skills adapter integration
- Skip flaky streamChatResponse test (matches main branch behavior)
2026-04-13 18:30:52 -07:00
gsxdsm
26732e7e43 feat(FN-1719): rebuild eslint baseline and add memory lessons
- Rebuild eslint config with context-aware flat config for better TypeScript/JSX handling
- Add memory lessons for lint/type/test baseline restoration
- Skip pre-existing flaky stream test (flushes a final complete event)
- Fix api.ts require import path
- Add ProjectEngineManager.startReconciliation mock to tests for main compatibility
2026-04-13 18:21:31 -07:00
gsxdsm
564659fcbe fix: prevent agents from killing production dashboard on port 4040
An AI review agent (FN-1506) killed the running dashboard by finding
the process on port 4040 via lsof and running kill -9, causing exit
code 137 (SIGKILL) with no logs. This adds multi-layer guardrails:

- AGENTS.md: project-level rule reserving port 4040
- Executor/reviewer system prompts: explicit prohibition on killing
  port 4040 processes, with instruction to use --port 0 instead
- Core agent-prompts.ts: same guardrails in all prompt variants
- Reviewer told to issue REVISE if executor violates the rule
- SIGHUP handlers in dashboard.ts and serve.ts for resilience
- Background engine reconciliation in dashboard/serve startup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:00:49 -07:00
gsxdsm
eb669a21b4 feat(FN-1692): merge fusion/fn-1692 2026-04-13 16:05:05 -07:00
gsxdsm
1383b8d7d6 refactor: replace primary/secondary engine pattern with uniform ProjectEngineManager
Remove the anti-pattern where the cwd project was treated as "primary" with a
special engine, and other projects got "secondary" engines through a separate
code path. Every project now gets an identical ProjectEngine created through
ProjectEngineManager.

Key changes:
- Add ProjectEngineManager class to @fusion/engine for uniform engine lifecycle
- Replace manual engine maps in dashboard.ts and serve.ts with engineManager
- Add engineManager to ServerOptions for per-project engine resolution
- Add getProjectContext() helper in routes.ts (replaces 199 getScopedStore calls)
- Merge and automation routes now resolve engine subsystems per-request
- SSE endpoint uses engine's store when available (same EventEmitter)
- Fix tsx not found in dev-with-memory.mjs startup script
- Add invalidateAllGlobalSettingsCaches for cross-project settings sync

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 13:32:38 -07:00
gsxdsm
2618eefc75 feat(FN-1451): complete Step 3 — align CLI hierarchy imports 2026-04-13 12:55:30 -07:00
gsxdsm
2486f2fb07 fix(lint): resolve pre-existing lint errors and merge conflicts
- Remove unused imports/vars from routes.ts (VALID_TRANSITIONS, AUTOMATION_PRESETS, ChatStore, FileListResponse, etc.)
- Prefix unused destructured error vars with _ convention
- Fix prefer-const for summary variable
- Add _ ignore pattern to eslint.config.mjs
- Include test files in tsconfig.app.json to fix @testing-library/jest-dom types
- Resolve GitManagerModal.test.tsx merge conflict (take fn-1626 expectLatestCallStartsWith style)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 10:31:15 -07:00
gsxdsm
36d5373a6e fix(settings): fix Zai API key clear/save and update GitManagerModal tests
- hasApiKey now only returns true for stored api_key credentials; previously
  it fell back to hasAuth() which includes env vars, causing Clear to appear
  to do nothing and Save to never appear
- Save button now shows when user types into the key input even if already
  authenticated, allowing key updates without clearing first
- Remove unused importFile state variable (TS 6133 lint error)
- Update GitManagerModal tests to pass undefined as projectId argument to
  all API mocks, matching the component's project-aware API signatures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 09:48:59 -07:00
gsxdsm
cd851c61a6 fix(dashboard): use ProjectEngine for secondary projects
Secondary projects were started via ProjectManager (bare InProcessRuntime)
which lacks auto-merge queue, startup sweep, periodic retry, PR monitor,
and settings listeners. Tasks reaching in-review in secondary projects
would never be auto-merged. Now all projects use ProjectEngine for the
full subsystem set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 07:58:20 -07:00
gsxdsm
a2d54c4a42 Fix Zai and Minimax auth for usage 2026-04-13 07:38:38 -07:00
gsxdsm
bbe2542362 Read provider settings from fusion fallback 2026-04-13 07:05:01 -07:00
gsxdsm
897398a3fc fix(dashboard): start engine for every registered project on first access
Tasks in projects other than the primary (cwd) project were never triaged
because only one ProjectEngine was started. When a project is accessed via
?projectId= API/SSE, getOrCreateProjectStore created a TaskStore but left
the Scheduler, TriageProcessor, and TaskExecutor unstarted.

Fix: introduce setOnProjectFirstCreated callback in project-store-resolver
so the dashboard server is notified when any new project is first accessed.
dashboard.ts creates a ProjectManager that lazily starts an InProcessRuntime
(Scheduler + TriageProcessor + TaskExecutor) for each project the first time
it is accessed — works for any number of registered projects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 21:58:19 -07:00
gsxdsm
e5b1bb1abe fix(cli): remove obsolete engine-internal tests from dashboard __tests__
Tests for MissionAutopilot wiring, semaphore boundaries, CronRunner,
syncInsightExtraction, and internal subsystem constructors are now
handled by ProjectEngine internally. Replace with a single test
verifying `engine` is passed to createServer in non-dev mode.

All 636 CLI tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 21:16:13 -07:00