feat(FN-3914): add durable heartbeat model fallback

Adds durable heartbeat model fallback logic to the engine's heartbeat subsystem (`agent-heartbeat.ts`, `agent-session-helpers.ts`, `pi.ts`), allowing heartbeats to gracefully use alternative AI models when the primary model is unavailable, with documentation updates to `agents.md` and `settings-refe

Fusion-Task-Id: FN-3914
This commit is contained in:
Fusion
2026-05-10 02:48:30 -07:00
committed by gsxdsm
parent adbc5f1c24
commit a1f4b53e21
9 changed files with 262 additions and 36 deletions

View File

@@ -11,7 +11,12 @@ import type { AgentRuntimeOptions } from "./agent-runtime.js";
import type { SkillSelectionContext } from "./skill-resolver.js";
import type { PluginRunner } from "./plugin-runner.js";
import type { AgentSession } from "@mariozechner/pi-coding-agent";
import { resolveTaskExecutionModel, resolveTaskPlanningModel, type Settings } from "@fusion/core";
import {
resolveExecutionSettingsModel,
resolveTaskExecutionModel,
resolveTaskPlanningModel,
type Settings,
} from "@fusion/core";
import { resolveRuntime, buildRuntimeResolutionContext, type SessionPurpose } from "./runtime-resolution.js";
import { createLogger } from "./logger.js";
import { promptWithFallback, describeModel } from "./pi.js";
@@ -163,6 +168,33 @@ export function resolvePlanningSessionModel(
};
}
export function resolveHeartbeatSessionModels(
settings: Partial<Settings> | undefined,
assignedAgentRuntimeConfig?: Record<string, unknown>,
): {
defaultProvider: string | undefined;
defaultModelId: string | undefined;
fallbackProvider: string | undefined;
fallbackModelId: string | undefined;
} {
const assignedRuntimeModel = extractRuntimeModel(assignedAgentRuntimeConfig);
const executionSettingsModel = resolveExecutionSettingsModel(settings);
const defaultProvider = assignedRuntimeModel.provider ?? executionSettingsModel.provider;
const defaultModelId = assignedRuntimeModel.modelId ?? executionSettingsModel.modelId;
const executionPairAvailable = Boolean(executionSettingsModel.provider && executionSettingsModel.modelId);
const defaultMatchesExecution =
defaultProvider === executionSettingsModel.provider && defaultModelId === executionSettingsModel.modelId;
return {
defaultProvider,
defaultModelId,
fallbackProvider: executionPairAvailable && !defaultMatchesExecution ? executionSettingsModel.provider : undefined,
fallbackModelId: executionPairAvailable && !defaultMatchesExecution ? executionSettingsModel.modelId : undefined,
};
}
export function resolveMergerSessionModel(
settings: Partial<Settings> | undefined,
assignedAgentRuntimeConfig?: Record<string, unknown>,