## Summary
- normalize callback-only plugin sessions at the shared runtime boundary
- preserve runtime-native subscriptions and isolate subscriber failures
- strengthen ACP multi-delta, unsubscribe, and callback-delivery
coverage
- correct the task environment and unsubscribe contracts
## Why
PR #3501 fixed the generic ACP adapter, but workflow steps still call
`session.subscribe()` unconditionally. Bundled callback-only runtimes
such as Hermes and the vendored Grok/Claude/OMP ACP clients can still
return sessions without that method. Handling the compatibility once in
`createResolvedAgentSession` closes every current runtime surface
without copying the bridge into each adapter.
## Testing
- `packages/engine`: `agent-session-helpers.test.ts` — 61 passed
- `fusion-plugin-acp-runtime`: `runtime-adapter.test.ts` — 14 passed
- `fusion-plugin-acp-runtime`: `process-manager.test.ts` — 15 passed
- engine typecheck passed
- ACP runtime typecheck passed
- changeset format, FNXC date check, ESLint, and `git diff --check`
passed
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved compatibility with callback-based runtime sessions.
- Added reliable subscriptions for text, thinking, and tool activity
updates.
- Preserved native subscription behavior where available.
- Prevented subscriber errors from interrupting event delivery.
- Improved unsubscribe behavior for removed handlers.
- Improved event delivery during deferred runtime fallback.
- Corrected task environment values passed to runtime subprocesses.
- **Tests**
- Expanded coverage for streaming updates, fallback handling, cleanup,
and subscriber isolation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds the **Stash memory backend** (`memory.backendType=stash`) that
connects Fusion's agent memory to the
[Stash](https://github.com/Fergana-Labs/stash) product — *knowledge
bases for the agent era* ([product site:
joinstash.ai](https://joinstash.ai)). Fusion becomes a first-class Stash
client: complete chat sessions and finished tasks are captured into
Stash, memory is recalled during chat, and Stash sessions are kept in
sync with the dashboard (including deletes and archival).
**Product:** <https://github.com/Fergana-Labs/stash> ·
[joinstash.ai](https://joinstash.ai)
## What's included
### 1. Stash memory backend (RUFU-068 / RUFU-121)
- New `StashMemoryBackend` (`memory.backendType=stash`) with `stashUrl`
/ `stashApiKey` settings (global secrets-store `stash-api-key` +
per-project override).
- **Complete-chat-session capture** keyed by ChatSession id.
- Sessions are classified into **per-project folders** (get-or-create,
`external_key fusion-<projectId>`, 1h per-process cache) and
**soft-deleted with their chat** via `DELETE /api/chat/sessions/:id`.
- Per-conversation **memory-focus** read-time scoping (new
`0066_chat_session_memory_focus.sql` migration — sequence renumbered
0059→0060→0061→0065→0066 as origin/main claimed the lower numbers);
event metadata enriched with `project` / `project_name` / `chat_title`.
- Recall queries normalized to single-keyword / explicit-OR ASCII (≤100
chars); shared normalizer export reused by per-turn recall.
### 2. Per-task executor transcript capture (RUFU-122)
Finished or failed tasks upload their executor transcript
(`agent-log.jsonl`) to Stash as a task session.
### 3. Bulk archive Stash sync (RUFU-125)
Archived task-planner chats soft-delete their Stash sessions on bulk
archival (paged). The snapshot of doomed session ids is taken *before*
the local bulk delete, and the Stash sync runs fire-and-forget so a
Stash stall can never delay local archival.
### 4. Per-chat "Preserve to Stash" backfill (RUFU-136)
A per-chat action that backfills a chat's full history into Stash, with
client-side idempotency and a pre-check that skips already-uploaded
content (fail-closed, no duplicate upload on transport failure).
- **Session-folder naming fix:** the first project folder is now named
"Fusion — <project name>" instead of the bare "Fusion" fallback
(the backfill now resolves the central-registry project name,
best-effort, never blocking the upload).
### 5. Opt-in semantic (vector) recall (RUFU-126)
`stashVectorSearch` setting (default `false` — **zero behavior change
until enabled**). For multi-word queries the backend tries Stash's
semantic-search endpoint first, then falls back byte-identically to the
keyword path. Definitive 404/405/501/503 responses are negatively cached
per process. Requires a patched Stash server (new endpoint +
`sentence-transformers` + embedding backfill); unpatched servers are
transparently bypassed after the first 404.
## Safety
- **Opt-in / inert by default:** the default backend remains `qmd`; the
Stash backend is inert until `memoryBackendType=stash` + `stashUrl` are
set.
- All Stash I/O is **best-effort, fail-closed, and non-blocking** — a
Stash outage never blocks chat, task completion, or archival. No
run-audit content is emitted.
## Testing
- Backfill + delete-sync suites (20/20), Stash backend suite (68/68),
executor memory / session capture suites, `memory-focus-recalling`,
description-guard — all green.
- `tsc` clean across core / engine / dashboard.
- Live verification: bulk backfill of 21/24 chats completed; the
"Preserve to Stash" action is idempotent on re-run.
## Changesets
- `@runfusion/fusion` **minor** — Stash memory backend + capture
(RUFU-068/121), per-task transcript (RUFU-122), bulk archive sync
(RUFU-125), per-chat backfill (RUFU-136), opt-in vector search
(RUFU-126)
- `@runfusion/fusion` **patch** — backfill session-folder naming fix
## Rebase Note (2026-08-23)
Rebased onto `origin/main` `3f448f7292` (v0.77.0-beta.7). Conflicts
resolved additively:
- `packages/core/src/postgres/schema-applier.ts` + test — upstream's
0062-0065 migrations (task/subtask splitting removal, AI merge review
reconciliation, task repository scope, FN-149 review convergence)
unioned with this PR's `chat_sessions.memory_focus` migration, which is
**renumbered 0065 → 0066** (upstream's FN-149 shipped 0065 canonically
on origin/main); `SCHEMA_BASELINE_VERSION` advances to `0066`.
- `packages/dashboard/app/components/ChatView.tsx` — upstream's docked
chat sidebar resize handlers unioned with the RUFU-136 "Preserve to
Stash" backfill handler.
- New commit: `settings.memory.*` stash-backend i18n keys added to all 6
secondary locales (RUFU-121/122 parity fix; `pnpm i18n:status` no longer
reports any violation introduced by this PR).
**Deploy note (operator environments that already ran a pre-rebase build
of this PR):** the memory-focus SQL may already be in the schema under
ledger row `0065`. Remap that row to `0066` (`UPDATE
fusion_schema_migrations SET version = '0066' WHERE version = '0065';`)
*before* first boot of a 0066-ceiling binary — otherwise the fresh
upstream `0065_fn_149_review_convergence_stage.sql` would be skipped as
"already applied". Clean databases (no prior memory-focus row) need no
action.
**CI note — Lint (lifecycle-column census) is red on the merge base:**
`pnpm check:lifecycle-columns --strict` fails identically on pure
`origin/main` `3f448f7292` with
`packages/core/src/db/legacy-adoption.ts: 0 -> 3` (3 column guards in
the U9b legacy-adoption table without a baseline entry or
`DELIBERATE-LITERAL` marker). Verified by running the census on a clean
origin/main checkout — inherited from the base, not introduced by this
PR. Fix belongs upstream; tracked separately.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added Stash memory integration with project configuration and optional
semantic search.
* Added per-chat memory focus controls and a `/focus` command.
* Added “Preserve to Stash” for uploading complete chat history.
* Added automatic chat, task transcript, and completion-event capture.
* Added project-specific Stash session folders and archive/delete
synchronization.
* **Bug Fixes**
* Improved Stash folder naming and handling of missing branches during
no-commit tasks.
* **Documentation**
* Added setup, configuration, integration, vector-search, and
performance guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Fusion <noreply@runfusion.ai>
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
## Summary
Fixes the `session.subscribe is not a function` crash that breaks every
engine workflow step (Plan Review, Code Review) executed by an ACP agent
(Hermes ACP, Prime, Grok).
Introduced by the ACP custom-tools bridge (#3476) in the sense that it
made the gap reachable: ACP sessions stream through the bridging client
handler onto `callbacks` and never exposed the pi-style `subscribe()`
that the engine's AgentSession contract promises. Two production call
sites call it unconditionally:
- `packages/engine/src/executor/execute-workflow-step.ts` (workflow
steps — Plan Review / Code Review)
- `packages/engine/src/pi.ts` fallback wiring (`wireFallbackHooks`,
`promptableSession.subscribe`)
(`reviewer.ts` guards with `typeof session.subscribe === "function"`;
the other paths do not.)
Real-world symptom (verified on 0.77.0-beta.6 with a Hermes ACP agent):
every Plan Review attempt fails before producing a verdict:
```
[pre-merge] Workflow step failed: Plan Review
outcome: Plan Review failed before producing a verdict: session.subscribe is not a function
Plan Review provider retry budget exhausted
```
## Fix
Fix at the seam rather than guarding every call site forever:
- `AcpRuntimeAdapter.createSession` wraps the raw callbacks so each
forwarded text/thinking/tool event is **also** replayed to subscribers
as the pi-shaped event consumers parse (`message_update` +
`assistantMessageEvent.{text_delta,thinking_delta}`,
`tool_execution_start/end`)
- exposes `session.subscribe(handler)` returning an unsubscribe
function; dispose clears subscribers
- merges engine `taskEnv` into the subprocess env behind the existing
allow-list trust boundary (KTD6b preserved — only allow-listed keys
forward, task values win)
- original callback delivery unchanged; subscriber exceptions are
isolated so a faulty consumer can't break the streaming bridge
## Testing
- New regression tests in `runtime-adapter.test.ts` against the real
echo-agent fixture (`ACP_FIXTURE_RICH_PROMPT=1`):
- subscribe replays text/thinking/tool events as pi-shaped events
- unsubscribe stops delivery
- dual delivery: original `onText` callback AND subscriber both fire
- Full plugin suite: 255 passed (21 files)
- `tsc --noEmit` clean for the plugin
- changeset for `@runfusion/fusion` (patch, bugfix)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added session event subscriptions with unsubscribe support.
* Streamed text, thinking, and tool updates are replayed in a consistent
event format.
* Added support for task-specific environment values when launching
subprocesses.
* **Bug Fixes**
* Ensured streamed events reach both existing callbacks and subscribers.
* Isolated subscriber errors so they do not interrupt other handlers.
* Session cleanup now stops further subscription notifications.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- resolves renamed review, complete, and archived workflow columns
before admitting late workspace repositories
- avoids workflow resolution when an existing repository or landing
state already decides the result
- syncs the bundled dependency-graph plugin with the current TaskCard
and scoped-storage APIs
## Test plan
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/workspace-add-repo-midflight.test.ts --silent=passed-only
--reporter=dot`
- `pnpm --filter @fusion/engine typecheck`
- `pnpm --filter @fusion-plugin-examples/dependency-graph test`
- `pnpm --filter @fusion-plugin-examples/dependency-graph build`
- `pnpm check:plugin-interop-drift`
- `pnpm check:lifecycle-columns`
- `pnpm check:changesets`
- `pnpm lint`
- `pnpm build`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Late workspace repository acquisition now respects renamed review,
complete, and archived workflow columns.
* Tasks entering a blocked lifecycle state during acquisition are
refused safely.
* Existing safeguards for merge status, landed worktrees, legacy
terminal identifiers, and existing worktrees remain supported.
* Concurrent acquisition attempts now avoid duplicate worktrees and
preserve task updates reliably.
* **Improvements**
* Improved dependency-graph dashboard interoperability with current
task-card and storage APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Three defects found while bringing up a container from a clean checkout:
- The dashboard's vite build (~5.7k modules) exceeded V8's default old-space on a
stock Docker Desktop VM and aborted the image build with "Ineffective
mark-compacts near heap limit" (exit 134). Raise the ceiling for that RUN only.
- The documented `-v fusion-home:/home/node/.fusion` mount seeded a root-owned
named volume over a path absent from the image, so embedded Postgres initdb hit
"Permission denied", the supervisor burned its 4 restarts, and the container went
unhealthy on first run. Pre-create the directory node-owned so a fresh named
volume inherits it; document that bind mounts still need a host-side chown.
- Drop the dependency-graph plugin's tsconfig path mapping for the taskStuck module
deleted in 2eae0b2507 / 29d94e0fa3.
Verified: full `docker build` from a clean export of this tree succeeds unpatched,
and a run against brand-new named volumes with no manual chown reaches health=healthy
with /api/health 200.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dependency-graph plugin imported the deleted taskStuck util through its
dashboard interop shim, breaking the dashboard vite build. Remove the isStuck
gate from graph nodes, the taskStuckTimeoutMs prop threading, the taskStuck
module declaration from dashboard-interop.d.ts, and the stalled-card-as-stuck
test coverage. Covered by the existing remove-stuck-task-tagging changeset.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refresh the Grok runtime README with the current flagship model invocation.
- Update the optional Grok ACP command to use grok-4.6.
- Document why the illustrative model identifier tracks the provider default.
Files changed:
plugins/fusion-plugin-grok-runtime/README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-9015
Fusion-Task-Lineage: 6dd04810-fe06-4462-82b9-5abb38e229c0
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
The released Grok CLI (v1.0.0, latest stable) does not recognize the
--no-auto-update flag and exits immediately with error: unexpected
argument. This causes Fusion to report 'ACP connection closed' when
spawning grok agent stdio.
buildGrokAcpArgs previously defaulted noAutoUpdate to true (via !==
false). Changed to opt-in (=== true) so the flag is only passed when
explicitly enabled. Updated acp-settings.test.ts assertions accordingly.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Grok ACP startup no longer disables automatic updates by default.
- Automatic update prevention is applied only when explicitly enabled in
settings.
- **Tests**
- Updated startup argument validation to reflect the revised default
behavior.
- **Documentation**
- Added release notes documenting the change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
Both plugins declared dashboardViews only in their src/index.ts module.
PluginLoader.getCurrentManifestDashboardViews treats a successfully-read
manifest as authoritative and returns an empty list when the key is absent,
and getPluginDashboardViews only falls back to the module definition when the
manifest read fails. The module-level entries were therefore discarded and
neither view appeared on any nav surface -- header overflow, desktop sidebar,
or mobile More sheet all consume the same array from usePluginDashboardViews.
Mirror the manifest shape used by fusion-plugin-compound-engineering.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## Summary
- remove an unavailable jest-dom type from the Todo plugin production
TypeScript build
- update the dashboard navigation fixture for the plugin-owned Todo
destination and root test id
## Test plan
- `corepack pnpm --filter @fusion-plugin-examples/todos build`
- `corepack pnpm --filter @fusion-plugin-examples/todos test`
- `FUSION_DASHBOARD_DEEP=1 corepack pnpm --filter @fusion/dashboard exec
vitest run app/components/__tests__/navigation-history.test.tsx
--project dashboard-app-quality-components-a --silent=passed-only
--reporter=dot`
- `corepack pnpm check:changesets`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Restored CLI packaging for the bundled Todo Lists plugin.
* Made `AgentStore` available to bundled plugins at runtime.
* **Tests**
* Updated navigation coverage for Todo Lists dashboard views, overflow
placement, and ordering.
* Added coverage for opening and dismissing the Todo view through
browser history navigation.
* Improved validation of runtime exports.
* **Chores**
* Simplified test type configuration for the Todo Lists plugin.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->