Adds vitest coverage for validateCliPresenceAsync and validateCliAuthAsync,
verifying success paths, spawn-error paths, non-zero exit codes, and that
auth failures emit the expected warning. Pairs with the async/memoized
validation refactor in f7df0d4e3 that unblocks the dashboard event loop
on every chat send.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fn_task_create and fn_task_update accepted any string as `agentId` and
wrote it verbatim onto `task.assignedAgentId`, letting hallucinated IDs
(e.g. `agent-executor-001`) appear as agent badges in the dashboard.
Mirror the validation already used by fn_delegate: look the agent up via
AgentStore and reject unknown or ephemeral/runtime-managed agents. Null
still clears the field on update.
Also clean up two stale failures in bundle-output.test that predated this
change:
- pi-claude-cli no longer imports cross-spawn, so drop the dependency and
its orphan type-decl file.
- Loosen the spawn-import regex to match `spawn` anywhere in the
destructured import (the source has additional named imports).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add missing Claude model entries and extend provider metadata handling for model extras
- Improve subprocess diagnostics in pi-claude-cli process management for clearer failure visibility
- Add targeted tests for provider model extras and process-manager diagnostic behavior
- Update Settings modal copy for project default model guidance and record changes in pi-claude-cli changelog
Fusion-Task-Id: FN-2898
- Strip private @fusion/* workspace devDependencies from the published CLI manifest via prepare-publish-manifest and package metadata updates
- Replace cross-spawn usage and add staged bundle layout assertions to verify resolver output in dist packaging
- Add per-task/project model override resolution across core, dashboard settings/task modals, and route coverage with new regression tests
- Strengthen engine merge/recovery handling for paused/interrupted/squash paths and surface merger timeline activity with additional self-healing and merger tests
- Add changesets for npm bundle dependency fixes, project model override stabilization, and FTS5 corruption recovery
Fusion-Task-Id: FN-2897
- Remove ToolSearch prerequisite so custom MCP tools can be used during triage and plan generation flows
- Align built-in tool sets between provider wiring and prompt builder handling, including custom ls behavior
- Expand pi-claude-cli tests for event bridge, MCP config, prompt builder, and tool mapping regressions
- Add FN-2703 changeset and delivery documentation for the published @runfusion/fusion package
Manual version bump to 0.5.0 (changeset version produced 1.0.0 from a
single minor changeset against 0.4.1; that release was rolled back and
the 1.0.0 npm version deprecated).
Aggregates: status terminology refresh (planning/replan), Reviewer
rename, in-review pause behavior, dashboard-tui resize hardening,
dev-server experimental toggle fix, version reporting fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- test(FN-2618): harden process-manager fs/tmpdir mocks
- fix(cli): mark react-devtools-core external in bun compile
- fix(pty): switch to @homebridge/node-pty-prebuilt-multiarch fork
- Update TaskCard done-duration calculations to use createdAt as the start baseline for completed tasks
- Refactor TaskCard timer tests to assert against TaskStore-backed timestamps and avoid brittle elapsed-time assumptions
- Cover done-state timer behavior with expanded test scenarios for start/end timestamp combinations
- Remove unused provider code in pi-claude-cli to satisfy lint and keep the full test/build gates green
Version bump via changesets (consumed 11 changesets). Forced 0.3.0
instead of changesets' default escalation to 1.0.0 for the pre-1.0
minor bump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Triage with claude-sonnet-4-6 via pi-claude-cli kept looping on
fn_review_spec calls that were rejected by pi's validator with
"root: must be object". Parameterless MCP tools (schema
{type:"object", properties:{}}) emit zero input_json_delta events,
so partialJson stayed "" and the catch fell through to
finalArgs = "" — a string, which TypeBox's Type.Object({}) rightly
refuses. Default empty partialJson to {} so the call lands.
Also:
- Add a 2-step reminder loop in triage before swapping to the
fallback planning model — primary models that wrote PROMPT.md
but forgot fn_review_spec recover from a nudge, no need to pay
the cold-start tax of a new triage on a different model.
- Inject @runfusion/fusion's own pi extension into dashboard/
daemon/serve sessions and propagate the path to createFnAgent
via setHostExtensionPaths so fn_* tools register globally
without requiring `pi install npm:@runfusion/fusion`.
- Drop the "historical" qualifier from replayed tool labels —
Claude was reading "TOOL RESULT (historical Read):" as
"previous session, ignore" and looping on verification.
- Remove subprocess-lifecycle stderr debug logs that landed for
hang diagnosis — root cause is fixed, the noise can go.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Triage runs through pi-claude-cli still hang silently for 20+ minutes after
only 2 thinking-deltas before the engine's StuckTaskDetector kills them. We
can't tell from agentLogEntries whether the subprocess crashed, the MCP
server failed to attach, or Claude is in extended thinking that doesn't
stream.
Adds stderr lines tagged [pi-claude-cli] for:
- spawn (pid, model, mode, prompt sizes, mcp on/off)
- init (session id, model, permissionMode, mcp_servers status list)
- first-stdout-line (latency from spawn)
- tool_use (per top-level tool, with piKnown flag)
- break-early (when we kill at message_stop)
- close (exit code, signal, elapsed, broken-flag)
No behavioural change. Restart the dashboard, trigger triage, and these
lines tell us which phase silence sets in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The engine already runs a StuckTaskDetector with a configurable per-task
heartbeat (default 1 hour) and aborts triage/executor sessions through
AbortSignal when it judges them quiet. pi-claude-cli forwards that signal to
the subprocess. The 300s subprocess-level inactivity timeout was a redundant,
much-tighter shadow that kept killing Sonnet 4.6 mid-thinking on the 40k-char
triage prompt — exactly what the engine-level detector is designed not to do.
Move the local timeout to 30 minutes purely as a last-resort guard for embeds
that don't pass an abort signal. Stuck-detection responsibility now lives in
the caller, where it can be configured per task.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sonnet 4.6 with extended thinking on the triage prompt (~40k chars + AGENTS.md
+ skills) can take >3 minutes between thinking deltas. The 180s inactivity
timeout was killing those subprocesses before they could finish PROMPT.md +
fn_review_spec, leaving triage looping into the fallback model. Bump to 5
minutes.
Test mock timings updated to match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of "fn_review_spec was never called" tracked through the diagnostic
chain: the prompt actually sent to triage agents has zero `fn_*` tokens —
because resolveAgentPrompt("triage", ...) returns the BUILTIN_AGENT_PROMPTS
default-triage template (TRIAGE_PROMPT_TEXT in core/agent-prompts.ts), and that
template was forked from an older version that never had the "MUST call
fn_review_spec()" workflow nor any fn_-prefixed tool names. The fallback
`|| TRIAGE_SYSTEM_PROMPT` in engine/triage.ts never fires because the core
template is non-empty.
So the model writes PROMPT.md, doesn't see any instruction to review it, and
ends. zai/glm-5.1 happened to call fn_review_spec from training-pattern
inertia; Sonnet via pi-claude-cli stopped at write — same prompt, same bug.
Replace TRIAGE_PROMPT_TEXT with the engine's up-to-date TRIAGE_SYSTEM_PROMPT
verbatim (fn_-prefixed tools, fn_review_spec workflow, subtask breakdown,
project-commands handling, frontend UX criteria injection). Also remove the
diagnostic-only console.error lines added during this debugging session — the
core fix is now elsewhere and the noise isn't worth keeping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reports "no custom tool refs to rewrite (tools=18)" — meaning Context.tools
is populated but the prompt body doesn't contain any of the names. Need the
first 200 chars of the prompt and a sample of custom-tool names to figure out
whether the engine's customPrompt is reaching us at all or whether names
differ from what we expect.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds stderr lines so we can verify the rewrite is actually firing on a given
session ("rewrote N custom tool ref(s) [fn_review_spec×3, ...]") vs. silently
no-opping (no tools, no matches). Helps distinguish "fix not deployed yet"
from "fix not effective" without redeploying instrumentation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Triage system prompts read "MUST call fn_review_spec()" but Sonnet 4.6 routed
through pi-claude-cli writes PROMPT.md and silently skips the call — even with
the addendum explaining the deferred-tool protocol. Confirmed by the FN-2564
agent log: model called other MCP tools (fn_memory_search, fn_task_list) fine
but consistently never reached fn_review_spec, leaving triage looping on
"fn_review_spec was never called" and falling back to zai/glm-5.1 every time.
Rewrite bare `fn_*` (and any non-built-in custom tool name) references in the
system prompt to their `mcp__custom-tools__fn_*` form before sending. The
prompt now literally says "call mcp__custom-tools__fn_review_spec()" so the
model has no inference step, and the deferred-tool reminder Claude Code injects
matches verbatim. Word-boundary safe (won't touch fn_review_specifier) and
idempotent (won't double-prefix already-MCP-named occurrences).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude Code 2.x defers MCP tool schemas behind ToolSearch — tools are listed
but calling them by short name (e.g. fn_review_spec) returns "tool not found"
until ToolSearch loads the schema. Triage system prompts that say "call
fn_review_spec()" leave the model guessing whether to use the bare name or
the mcp__custom-tools__ form.
Append a system-prompt section listing every custom tool with its short and
MCP-prefixed names plus the ToolSearch + call protocol. Drives off the
Context.tools we now already thread through, so plain chats with no custom
tools get no addendum.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous fix regenerated the MCP config per call but still pulled the tool
set from pi.getAllTools(), which depends on the runtime binding being scoped to
the right AgentSession. Engine-spawned triage/executor sessions register custom
tools (fn_review_spec, fn_review_step) and pi-ai already passes those to the
provider via Context.tools — that's the authoritative per-session list and
bypasses any binding ambiguity. Falls back to pi.getAllTools() when the caller
doesn't populate Context.tools.
Also logs the tool names on refresh so missing tools are diagnosable from stderr.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MCP config was generated lazily once and locked, so engine session-scoped
tools (fn_review_spec, fn_review_step) never reached the Claude CLI subprocess
and triage/executor sessions failed with "unknown tool" errors. Now the config
is hashed per call and rewritten when the tool set changes.
Also adds scripts/sync-fusion-skill-tools.mjs to regenerate the SKILL.md
tool-categories block from extension.ts at build time, with a --check mode
wired into skill-sync tests so drift fails CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>