fix(pi-claude-cli): unblock parameterless MCP tool calls in triage
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>
This commit is contained in:
@@ -26,7 +26,7 @@ import {
|
||||
stopAllDevServers,
|
||||
type RuntimeLogger,
|
||||
} from "@fusion/dashboard";
|
||||
import { aiMergeTask, MissionAutopilot, MissionExecutionLoop, HeartbeatMonitor, HeartbeatTriggerScheduler, type WakeContext, ProjectEngineManager, PeerExchangeService } from "@fusion/engine";
|
||||
import { aiMergeTask, MissionAutopilot, MissionExecutionLoop, HeartbeatMonitor, HeartbeatTriggerScheduler, type WakeContext, ProjectEngineManager, PeerExchangeService, setHostExtensionPaths } from "@fusion/engine";
|
||||
import { AuthStorage, DefaultPackageManager, ModelRegistry, SettingsManager, discoverAndLoadExtensions, createExtensionRuntime } from "@mariozechner/pi-coding-agent";
|
||||
import {
|
||||
getMergeStrategy,
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
resolveClaudeCliExtensionPaths,
|
||||
setCachedClaudeCliResolution,
|
||||
} from "./claude-cli-extension.js";
|
||||
import { resolveSelfExtension } from "./self-extension.js";
|
||||
import { DashboardTUI, DashboardLogSink, isTTYAvailable, type SystemInfo, type GitStatus, type GitCommit, type GitCommitDetail, type GitBranch, type GitWorktree, type FileEntry, type FileReadResult, type TaskStep as TUITaskStep, type TaskLogEntry as TUITaskLogEntry, type TaskDetailData, type TaskEvent } from "./dashboard-tui/index.js";
|
||||
|
||||
// Re-export for backward compatibility with tests
|
||||
@@ -1131,9 +1132,24 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
})();
|
||||
|
||||
// Always inject the cli's own extension (`@runfusion/fusion`) so its
|
||||
// `fn_*` tools register globally even when the user hasn't run
|
||||
// `pi install npm:@runfusion/fusion`. Without this, agent chat with
|
||||
// pi-claude-cli has no fn_* tools at all.
|
||||
const selfExtension = resolveSelfExtension();
|
||||
const selfExtensionPaths = selfExtension.status === "ok" ? [selfExtension.path] : [];
|
||||
if (selfExtension.status !== "ok") {
|
||||
logSink.warn(`[extensions] self: ${selfExtension.reason}`, "extensions");
|
||||
}
|
||||
// Propagate self-extension path to engine so createFnAgent sessions
|
||||
// (chat, refine, mission, etc.) also load fn_* tools, not just the
|
||||
// dashboard's extension runtime.
|
||||
setHostExtensionPaths(selfExtensionPaths);
|
||||
|
||||
// Load all enabled extensions: Fusion/Pi filesystem-discovered + package-resolved.
|
||||
const extensionsResult = await discoverAndLoadExtensions(
|
||||
[
|
||||
...selfExtensionPaths,
|
||||
...getEnabledPiExtensionPaths(cwd),
|
||||
...packageExtensionPaths,
|
||||
...claudeCliPaths,
|
||||
|
||||
Reference in New Issue
Block a user