- 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
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>
Parallel subagent pass: four typescript-pro agents on non-overlapping scopes.
Patterns applied:
- catch (err: any) { ... err.message ... } → catch (err) { ... getErrorMessage(err) ... }
using the new @fusion/core helper. Bare catch {} where the error was unused.
- SQLite row types: defined typed XxxRow interfaces per table and cast
.all()/.get() results via `as unknown as XxxRow[]` (the double cast is
required because better-sqlite3 returns Record<string, SQLOutputValue>).
- rowToX(row: any) converters: typed argument with the matching row interface.
- Dynamic settings key writes: (settings as Record<string, unknown>)[key].
- React event handlers and setState callbacks: inferred types or concrete
React.{Mouse,Change,Form}Event<...> where needed.
- pi-claude-cli: local PiMessage / PiContext duck types to avoid re-typing
pi-ai concrete shapes; typed Claude stream event message fields.
72 files changed, ~400 anys eliminated. Typecheck passes across the workspace.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the stray useClaudeCli settings checkbox + onboarding question
with a proper provider-card UX. The card lives next to OAuth + API-key
cards in onboarding and settings, with Enable/Disable + Test actions.
Backend:
- Vendors rchern/pi-claude-cli@0.3.1 as packages/pi-claude-cli
(MIT, attribution in UPSTREAM.md). Lets us bump peer-dep on
pi-coding-agent in lockstep with Fusion (upstream pinned ^0.52.0
vs ours ^0.62.0) and fix bugs without waiting on upstream.
- Adds @fusion/pi-claude-cli as a workspace dep of @runfusion/fusion
so users don't have to `npm install -g pi-claude-cli` manually.
- serve/daemon/dashboard conditionally load the extension via
discoverAndLoadExtensions() when GlobalSettings.useClaudeCli is on;
no side-effects on user ~/.fusion/agent/settings.json.
- New GET /api/providers/claude-cli/status: claude --version probe
+ toggle state + cached extension resolution.
- New POST /api/auth/claude-cli: flips useClaudeCli, refuses if the
claude binary is missing, fires the existing skill-backfill hook.
- /api/auth/status now injects a synthetic {id:"claude-cli", type:"cli"}
provider entry so onboarding + settings see a consistent list.
Frontend:
- New ClaudeCliProviderCard component shared between ModelOnboardingModal
and SettingsModal's Authentication section.
- New AuthProvider.type = "cli" variant.
- Removed the old "Route AI calls through the Claude CLI" checkbox from
Global Models settings and the opt-in step from the onboarding wizard.
- ProviderIcon gets a composite Anthropic-mark-plus-terminal glyph for
the claude-cli provider id.
Tests:
- 8 unit tests for extension resolution (@fusion/pi-claude-cli is
workspace-linked so these run in-tree).
- 2 unit tests for the binary probe.
- Existing /auth/status tests filter out the new synthetic entry so
they keep asserting structural OAuth/API-key behavior in isolation.
- The vendored package's own 296 tests still pass unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>