refactor(FN-2161): standardize on createFnAgent naming

- Rename engine export and call sites to use createFnAgent consistently across runtime flows
- Update core lazy engine loader and dashboard agent-generation/planning/chat paths to reference createFnAgent
- Refresh affected unit and integration tests, including renaming pi-create-kb-agent.test.ts to pi-create-fn-agent.test.ts
- Update AGENTS.md documentation references to match the new createFnAgent name
This commit is contained in:
Fusion
2026-04-19 17:50:41 -07:00
committed by gsxdsm
parent 0df3234349
commit 6714f7654b
58 changed files with 425 additions and 425 deletions

View File

@@ -30,16 +30,16 @@ vi.mock("./logger.js", () => {
// Mock pi.ts for executeHeartbeat tests
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
promptWithFallback: vi.fn(async (session: any, prompt: string) => {
await session.prompt(prompt);
}),
}));
// Import the mocked functions for test control
import { createKbAgent } from "./pi.js";
import { createFnAgent } from "./pi.js";
import { heartbeatLog } from "./logger.js";
const mockedCreateKbAgent = vi.mocked(createKbAgent);
const mockedCreateKbAgent = vi.mocked(createFnAgent);
// Mock store factory
function createMockStore(overrides: Partial<AgentStore> = {}): AgentStore {
@@ -1198,7 +1198,7 @@ describe("HeartbeatMonitor", () => {
let mockTaskStore: TaskStore;
let mockAgent: Agent;
// Helper: create a mock session returned by createKbAgent
// Helper: create a mock session returned by createFnAgent
function createMockAgentSession() {
return {
prompt: vi.fn().mockResolvedValue(undefined),
@@ -2722,7 +2722,7 @@ describe("HeartbeatMonitor", () => {
expect(taskLogTool.name).toBe("task_log");
});
it("passes model config from agent runtimeConfig to createKbAgent", async () => {
it("passes model config from agent runtimeConfig to createFnAgent", async () => {
const store = createStoreWithAgentForExec({
runtimeConfig: { modelProvider: "openai", modelId: "gpt-4o" },
});
@@ -2898,7 +2898,7 @@ describe("HeartbeatMonitor", () => {
});
describe("error handling", () => {
it("completes run as failed when createKbAgent throws", async () => {
it("completes run as failed when createFnAgent throws", async () => {
const store = createStoreWithAgentForExec();
mockedCreateKbAgent.mockRejectedValue(new Error("Model unavailable"));
@@ -4561,10 +4561,10 @@ describe("HeartbeatTriggerScheduler", () => {
describe("executeHeartbeat — skill selection resolver contract (FN-1510/FN-1511)", () => {
// We need to test the skill selection contract without affecting other tests.
// Since buildSessionSkillContextSync is called via dynamic import inside executeHeartbeat,
// we need to test the integration at a higher level - verifying that createKbAgent
// we need to test the integration at a higher level - verifying that createFnAgent
// receives the skillSelection option when agent has skills.
// Helper: create a mock session returned by createKbAgent
// Helper: create a mock session returned by createFnAgent
function createMockAgentSession() {
return {
prompt: vi.fn().mockResolvedValue(undefined),
@@ -4690,11 +4690,11 @@ describe("executeHeartbeat — skill selection resolver contract (FN-1510/FN-151
vi.restoreAllMocks();
});
// These tests verify the skill selection contract at the createKbAgent level.
// These tests verify the skill selection contract at the createFnAgent level.
// Since we can't easily mock dynamic imports, we verify that when an agent has
// skills in metadata, the createKbAgent is called and the result includes skill info.
// skills in metadata, the createFnAgent is called and the result includes skill info.
it("createKbAgent is called with agent session for heartbeat with skills", async () => {
it("createFnAgent is called with agent session for heartbeat with skills", async () => {
mockedCreateKbAgent.mockResolvedValue({
session: createMockAgentSession(),
} as any);
@@ -4711,7 +4711,7 @@ describe("executeHeartbeat — skill selection resolver contract (FN-1510/FN-151
expect(result.status).toBe("completed");
});
it("createKbAgent is called with correct cwd for skill resolution", async () => {
it("createFnAgent is called with correct cwd for skill resolution", async () => {
mockedCreateKbAgent.mockResolvedValue({
session: createMockAgentSession(),
} as any);
@@ -4751,7 +4751,7 @@ describe("executeHeartbeat — skill selection resolver contract (FN-1510/FN-151
});
describe("executeHeartbeat — skill selection non-fatal (FN-1510/FN-1511)", () => {
// Helper: create a mock session returned by createKbAgent
// Helper: create a mock session returned by createFnAgent
function createMockAgentSession() {
return {
prompt: vi.fn().mockResolvedValue(undefined),

View File

@@ -1058,8 +1058,8 @@ export class HeartbeatMonitor {
},
};
// Lazy-load createKbAgent and promptWithFallback
const { createKbAgent, promptWithFallback } = await import("./pi.js");
// Lazy-load createFnAgent and promptWithFallback
const { createFnAgent, promptWithFallback } = await import("./pi.js");
const { buildSessionSkillContextSync } = await import("./session-skill-context.js");
// Build tools with task creation tracking and run context for mutation correlation
@@ -1135,7 +1135,7 @@ export class HeartbeatMonitor {
}
// Create agent session
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: rootDir,
systemPrompt,
tools: "readonly",

View File

@@ -59,12 +59,12 @@ export interface AgentLoggerOptions {
* detailed argument summaries via {@link summarizeToolArgs}.
*
* Produces `onText` and `onToolStart` callbacks compatible with
* `createKbAgent`'s `AgentOptions` interface.
* `createFnAgent`'s `AgentOptions` interface.
*
* @example
* ```ts
* const logger = new AgentLogger({ store, taskId, onAgentText, onAgentTool });
* const { session } = await createKbAgent({
* const { session } = await createFnAgent({
* cwd: worktreePath,
* onText: logger.onText,
* onToolStart: logger.onToolStart,

View File

@@ -12,15 +12,15 @@ import type {
} from "@fusion/core";
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
promptWithFallback: vi.fn(),
}));
import { createKbAgent, promptWithFallback } from "./pi.js";
import { createFnAgent, promptWithFallback } from "./pi.js";
import { AgentReflectionService } from "./agent-reflection.js";
import { createReflectOnPerformanceTool, reflectOnPerformanceParams } from "./agent-tools.js";
const mockedCreateKbAgent = vi.mocked(createKbAgent);
const mockedCreateKbAgent = vi.mocked(createFnAgent);
const mockedPromptWithFallback = vi.mocked(promptWithFallback);
function makeAgent(overrides: Partial<Agent> = {}): Agent {

View File

@@ -13,7 +13,7 @@ import type {
TaskStore,
} from "@fusion/core";
import { createLogger } from "./logger.js";
import { createKbAgent, promptWithFallback } from "./pi.js";
import { createFnAgent, promptWithFallback } from "./pi.js";
const reflectionLog = createLogger("reflection");
@@ -97,7 +97,7 @@ export class AgentReflectionService {
}
let responseText = "";
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: this.rootDir,
systemPrompt: REFLECTION_SYSTEM_PROMPT,
tools: "readonly",

View File

@@ -11,7 +11,7 @@ const cronLoggerSpies = vi.hoisted(() => ({
}));
const piModuleMocks = vi.hoisted(() => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
promptWithFallback: vi.fn(),
}));
@@ -24,7 +24,7 @@ vi.mock("./logger.js", () => ({
}));
vi.mock("./pi.js", () => ({
createKbAgent: piModuleMocks.createKbAgent,
createFnAgent: piModuleMocks.createFnAgent,
promptWithFallback: piModuleMocks.promptWithFallback,
}));
@@ -99,7 +99,7 @@ describe("CronRunner", () => {
beforeEach(() => {
vi.clearAllMocks();
piModuleMocks.promptWithFallback.mockResolvedValue(undefined);
piModuleMocks.createKbAgent.mockResolvedValue({
piModuleMocks.createFnAgent.mockResolvedValue({
session: {
dispose: vi.fn(),
},
@@ -145,7 +145,7 @@ describe("CronRunner", () => {
describe("createAiPromptExecutor", () => {
it("returns response text even when session disposal throws", async () => {
piModuleMocks.createKbAgent.mockImplementation(async (options: { onText?: (delta: string) => void }) => {
piModuleMocks.createFnAgent.mockImplementation(async (options: { onText?: (delta: string) => void }) => {
options.onText?.("hello ");
options.onText?.("world");
return {

View File

@@ -618,7 +618,7 @@ const AI_AUTOMATION_SYSTEM_PROMPT = [
].join("\n");
/**
* Create an AiPromptExecutor that uses createKbAgent for real AI execution.
* Create an AiPromptExecutor that uses createFnAgent for real AI execution.
*
* Each call creates a fresh agent session, runs the prompt, collects the
* text response, and disposes the session.
@@ -629,13 +629,13 @@ const AI_AUTOMATION_SYSTEM_PROMPT = [
export async function createAiPromptExecutor(cwd: string): Promise<AiPromptExecutor> {
// We import lazily to keep the factory self-contained and to avoid
// pulling pi.ts into the module graph when AI execution isn't used.
const { createKbAgent, promptWithFallback } = await import("./pi.js");
const { createFnAgent, promptWithFallback } = await import("./pi.js");
const disposeLog = createLogger("cron-runner");
return async (prompt: string, modelProvider?: string, modelId?: string): Promise<string> => {
let responseText = "";
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd,
systemPrompt: AI_AUTOMATION_SYSTEM_PROMPT,
tools: "readonly",

View File

@@ -4,7 +4,7 @@ import { detectReviewHandoffIntent } from "./executor.js";
// Mock external dependencies
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
describeModel: vi.fn().mockReturnValue("mock-provider/mock-model"),
compactSessionContext: vi.fn(async (session, instructions) => {
// Delegate to session.compact if available (supports loop recovery tests)
@@ -165,7 +165,7 @@ vi.mock("@mariozechner/pi-coding-agent", () => {
});
import { TaskExecutor, buildExecutionPrompt } from "./executor.js";
import { createKbAgent } from "./pi.js";
import { createFnAgent } from "./pi.js";
import { reviewStep as mockedReviewStepFn } from "./reviewer.js";
import { execSync } from "node:child_process";
import { findWorktreeUser, aiMergeTask } from "./merger.js";
@@ -177,7 +177,7 @@ import { StepSessionExecutor } from "./step-session-executor.js";
import { executorLog } from "./logger.js";
import { withRateLimitRetry } from "./rate-limit-retry.js";
const mockedCreateHaiAgent = vi.mocked(createKbAgent);
const mockedCreateHaiAgent = vi.mocked(createFnAgent);
const mockedSessionManager = vi.mocked(SessionManager);
const mockedGenerateWorktreeName = vi.mocked(generateWorktreeName);
const mockedFindWorktreeUser = vi.mocked(findWorktreeUser);
@@ -3151,7 +3151,7 @@ describe("TaskExecutor pause behavior", () => {
// Should use SessionManager.open for the initial resumed execution
expect(mockedSessionManager.open).toHaveBeenCalledWith(sessionFilePath);
// The first createKbAgent call should use the opened session manager
// The first createFnAgent call should use the opened session manager
const firstCall = mockedCreateHaiAgent.mock.calls[0][0] as any;
expect(firstCall.sessionManager).toBeDefined();
@@ -4032,7 +4032,7 @@ describe("TaskExecutor enginePaused soft pause (no agent termination)", () => {
const mockedReviewStep = vi.mocked(mockedReviewStepFn);
/**
* Helper: executes a task and captures the custom tools passed to createKbAgent.
* Helper: executes a task and captures the custom tools passed to createFnAgent.
* Returns a map of tool name → tool execute function for direct testing.
*/
async function captureTools(): Promise<Record<string, (id: string, params: any) => Promise<any>>> {
@@ -4262,7 +4262,7 @@ describe("Code review verdict enforcement - task_update blocking", () => {
});
it("EXECUTOR_SYSTEM_PROMPT contains code review enforcement language", async () => {
// Capture the system prompt passed to createKbAgent
// Capture the system prompt passed to createFnAgent
let capturedSystemPrompt = "";
mockedCreateHaiAgent.mockImplementation(async (opts: any) => {
capturedSystemPrompt = opts.systemPrompt || "";
@@ -4340,7 +4340,7 @@ describe("RETHINK verdict handling", () => {
}
/**
* Helper: run executor and capture custom tools from createKbAgent mock.
* Helper: run executor and capture custom tools from createFnAgent mock.
* Returns the tools map keyed by tool name.
*/
async function captureRethinkTools(store: any, options?: any) {
@@ -6985,7 +6985,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// createKbAgent called twice: main agent + workflow step agent
// createFnAgent called twice: main agent + workflow step agent
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(2);
// Second call should be the workflow step with readonly tools
@@ -7207,7 +7207,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// Should only call createKbAgent once (main execution), skip workflow step
// Should only call createFnAgent once (main execution), skip workflow step
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(1);
// Should log that it was skipped
@@ -7340,7 +7340,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// createKbAgent called twice: main agent + workflow step agent
// createFnAgent called twice: main agent + workflow step agent
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(2);
// Second call should use the workflow step's model override
@@ -7510,7 +7510,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// Should only call createKbAgent once (main execution — no agent for script mode)
// Should only call createFnAgent once (main execution — no agent for script mode)
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(1);
// Should log script execution
@@ -7868,7 +7868,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// Should only call createKbAgent once (main execution)
// Should only call createFnAgent once (main execution)
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(1);
// Should log that it was skipped
@@ -7957,7 +7957,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// createKbAgent called twice: main agent + workflow step agent (prompt mode)
// createFnAgent called twice: main agent + workflow step agent (prompt mode)
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(2);
// Second call should use prompt mode (readonly tools, agent-based)
@@ -8064,7 +8064,7 @@ describe("Workflow Steps Execution", () => {
updatedAt: new Date().toISOString(),
});
// createKbAgent called twice: main agent + 1 pre-merge step (post-merge skipped)
// createFnAgent called twice: main agent + 1 pre-merge step (post-merge skipped)
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(2);
// Verify the workflow step results only contain pre-merge
@@ -9302,7 +9302,7 @@ describe("Agent Spawning", () => {
);
});
it("creates child agent session via createKbAgent", async () => {
it("creates child agent session via createFnAgent", async () => {
const agentStore = createMockAgentStore();
const { tools } = await captureToolsWithAgentStore(agentStore);
@@ -9312,7 +9312,7 @@ describe("Agent Spawning", () => {
task: "Do some work",
});
// createKbAgent is called at least twice: once for parent, once for child
// createFnAgent is called at least twice: once for parent, once for child
expect(mockedCreateHaiAgent.mock.calls.length).toBeGreaterThanOrEqual(2);
// Find the child session call
@@ -10039,7 +10039,7 @@ describe("TaskExecutor agent execution flow (FN-978)", () => {
let capturedOnText: ((delta: string) => void) | undefined;
mockedCreateHaiAgent.mockImplementation(async (opts: any) => {
// Capture the onText callback that's passed to createKbAgent
// Capture the onText callback that's passed to createFnAgent
capturedOnText = opts.onText;
return {
session: {
@@ -10169,7 +10169,7 @@ describe("TaskExecutor agent execution flow (FN-978)", () => {
await new Promise((resolve) => setTimeout(resolve, 200));
// The executing guard prevents duplicate execution from the event handler.
// Note: createKbAgent may be called a second time if the agent finishes
// Note: createFnAgent may be called a second time if the agent finishes
// without calling task_done (retry path), but the initial trigger should
// only cause one execution, not two.
// Verify that store.on was called with task:moved (listener registered)
@@ -10286,7 +10286,7 @@ describe("StepSessionExecutor integration", () => {
expect(mockedStepSessionExecutor).toHaveBeenCalled();
// executeAll should have been called
expect(mockExecuteAll).toHaveBeenCalledOnce();
// createKbAgent should NOT have been called for step-session path
// createFnAgent should NOT have been called for step-session path
expect(mockedCreateHaiAgent).not.toHaveBeenCalled();
});
@@ -10945,7 +10945,7 @@ describe("TaskExecutor skillSelection regression (FN-1511)", () => {
});
/**
* Helper: execute a task and capture createKbAgent call arguments.
* Helper: execute a task and capture createFnAgent call arguments.
*/
async function captureCreateKbAgentArgs(options?: {
assignedAgentId?: string;
@@ -11019,7 +11019,7 @@ describe("TaskExecutor skillSelection regression (FN-1511)", () => {
}
describe("single-session mode (runStepsInNewSessions: false)", () => {
it("passes skillSelection to createKbAgent when assigned agent has skills", async () => {
it("passes skillSelection to createFnAgent when assigned agent has skills", async () => {
const args = await captureCreateKbAgentArgs({
assignedAgentId: "agent-001",
assignedAgentSkills: ["triage", "executor"],
@@ -11085,7 +11085,7 @@ describe("TaskExecutor skillSelection regression (FN-1511)", () => {
// Note: These tests verify that skillSelection flows from executor to
// StepSessionExecutor. The full integration is complex due to mock setup,
// so we verify the contract indirectly through the step-session-executor tests.
// The executor tests focus on verifying skillSelection is present in createKbAgent calls.
// The executor tests focus on verifying skillSelection is present in createFnAgent calls.
// See StepSessionExecutor skillSelection tests in step-session-executor.test.ts.
// Skipped: Integration tests for step-session skill selection are covered
@@ -11114,7 +11114,7 @@ describe("TaskExecutor messaging tools", () => {
});
/**
* Helper: execute a task and capture the customTools array passed to createKbAgent.
* Helper: execute a task and capture the customTools array passed to createFnAgent.
*/
async function captureCustomTools(options?: {
messageStore?: unknown;

View File

@@ -10,7 +10,7 @@ import { buildExecutionMemoryInstructions, getTaskMergeBlocker, resolveAgentProm
import { findWorktreeUser } from "./merger.js";
import { generateWorktreeName, slugify } from "./worktree-names.js";
import { Type, type Static } from "@mariozechner/pi-ai";
import { createKbAgent, describeModel, promptWithFallback, compactSessionContext } from "./pi.js";
import { createFnAgent, describeModel, promptWithFallback, compactSessionContext } from "./pi.js";
import { buildSessionSkillContext } from "./session-skill-context.js";
import { reviewStep, type ReviewVerdict } from "./reviewer.js";
import { ModelRegistry, SessionManager, type ToolDefinition, type AgentSession } from "@mariozechner/pi-coding-agent";
@@ -1562,7 +1562,7 @@ export class TaskExecutor {
const codeReviewVerdicts = new Map<number, ReviewVerdict>();
let wasPaused = false;
// Mutable ref — populated after createKbAgent, tools access lazily via closure
// Mutable ref — populated after createFnAgent, tools access lazily via closure
const sessionRef: { current: AgentSession | null } = { current: null };
const stepCheckpoints = new Map<number, string>();
@@ -1665,7 +1665,7 @@ export class TaskExecutor {
// sessionFile must be let because it's destructured alongside session which is reassigned
// eslint-disable-next-line prefer-const
let { session, sessionFile } = await createKbAgent({
let { session, sessionFile } = await createFnAgent({
cwd: worktreePath,
systemPrompt: executorSystemPrompt,
tools: "coding",
@@ -1895,7 +1895,7 @@ export class TaskExecutor {
this.activeSessions.delete(task.id);
session.dispose();
const { session: retrySession, sessionFile: retrySessionFile } = await createKbAgent({
const { session: retrySession, sessionFile: retrySessionFile } = await createFnAgent({
cwd: worktreePath,
systemPrompt: executorSystemPrompt,
tools: "coding",
@@ -3413,7 +3413,7 @@ and show an appropriate message to the user.\`
projectRootDir: this.rootDir,
});
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: worktreePath,
systemPrompt: stepSystemPrompt,
tools: toolMode,
@@ -4311,7 +4311,7 @@ and show an appropriate message to the user.\`
});
// Create child agent session
const { session: childSession } = await createKbAgent({
const { session: childSession } = await createFnAgent({
cwd: childWorktreePath,
systemPrompt: childSystemPrompt,
tools: "coding",

View File

@@ -17,7 +17,7 @@ export { MissionAutopilot, type MissionAutopilotOptions } from "./mission-autopi
export { MissionExecutionLoop, type MissionExecutionLoopOptions, type ValidationResult, loopLog } from "./mission-execution-loop.js";
export { aiMergeTask, type MergerOptions } from "./merger.js";
export { reviewStep, type ReviewType, type ReviewVerdict, type ReviewResult, type ReviewOptions } from "./reviewer.js";
export { createKbAgent, promptWithFallback, type AgentOptions, type AgentResult } from "./pi.js";
export { createFnAgent, promptWithFallback, type AgentOptions, type AgentResult } from "./pi.js";
export {
resolveSessionSkills,
createSkillsOverrideFromSelection,

View File

@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
// Mock external dependencies
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
promptWithFallback: vi.fn(async (session, prompt, options) => {
if (options === undefined) {
await session.prompt(prompt);
@@ -113,11 +113,11 @@ import {
type ConflictCategory,
} from "./merger.js";
import { mergerLog } from "./logger.js";
import { createKbAgent } from "./pi.js";
import { createFnAgent } from "./pi.js";
import { execSync } from "node:child_process";
import { type TaskStore, type Task, type MergeResult, DEFAULT_SETTINGS } from "@fusion/core";
const mockedCreateHaiAgent = vi.mocked(createKbAgent);
const mockedCreateHaiAgent = vi.mocked(createFnAgent);
const mockedExecSync = vi.mocked(execSync);
const { existsSync: mockedExistsSyncRaw, readFileSync: mockedReadFileSyncRaw } = await import("node:fs");
const mockedExistsSync = vi.mocked(mockedExistsSyncRaw);
@@ -925,7 +925,7 @@ describe("aiMergeTask — model settings threading", () => {
} as any);
});
it("passes defaultProvider and defaultModelId from settings to createKbAgent", async () => {
it("passes defaultProvider and defaultModelId from settings to createFnAgent", async () => {
const store = createMockStore(
{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050" },
[{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050", column: "in-review" } as Task],
@@ -3771,7 +3771,7 @@ describe("aiMergeTask — post-merge workflow steps", () => {
await aiMergeTask(store, "/tmp/root", "FN-050");
// getWorkflowStep may be called but pre-merge steps should not trigger agent creation
// beyond the merge agent itself. We verify createKbAgent was called only once (merge agent)
// beyond the merge agent itself. We verify createFnAgent was called only once (merge agent)
// since pre-merge steps are skipped in the merger
const mergeAgentCalls = mockedCreateHaiAgent.mock.calls.filter(
(c: any) => c[0]?.systemPrompt?.includes("You are a merge agent")
@@ -4169,7 +4169,7 @@ describe("aiMergeTask — fresh session and compaction recovery", () => {
});
}
it("creates a fresh session for merge agent via createKbAgent", async () => {
it("creates a fresh session for merge agent via createFnAgent", async () => {
setupFreshSessionExecSync();
const sessionInstances: any[] = [];
@@ -5026,7 +5026,7 @@ describe("aiMergeTask — skill selection resolver contract (FN-1510/FN-1511)",
vi.clearAllMocks();
});
it("passes skillSelection to createKbAgent when agentStore is provided", async () => {
it("passes skillSelection to createFnAgent when agentStore is provided", async () => {
const { buildSessionSkillContext } = await import("./session-skill-context.js");
vi.mocked(buildSessionSkillContext).mockResolvedValue({
skillSelectionContext: {
@@ -5064,7 +5064,7 @@ describe("aiMergeTask — skill selection resolver contract (FN-1510/FN-1511)",
});
expect(mockedCreateHaiAgent).toHaveBeenCalled();
// Find the first createKbAgent call (main merger agent)
// Find the first createFnAgent call (main merger agent)
const firstCall = mockedCreateHaiAgent.mock.calls[0];
const opts = firstCall[0];
expect(opts.skillSelection).toBeDefined();

View File

@@ -7,7 +7,7 @@ import { existsSync } from "node:fs";
import { join } from "node:path";
import { getTaskMergeBlocker, type TaskStore, type MergeResult, type MergeDetails, type WorkflowStep, type WorkflowStepResult, type Settings, type AgentPromptsConfig } from "@fusion/core";
import { resolveAgentPrompt } from "@fusion/core";
import { createKbAgent, describeModel, promptWithFallback, compactSessionContext } from "./pi.js";
import { createFnAgent, describeModel, promptWithFallback, compactSessionContext } from "./pi.js";
import { buildSessionSkillContext } from "./session-skill-context.js";
import type { WorktreePool } from "./worktree-pool.js";
import { AgentLogger } from "./agent-logger.js";
@@ -662,7 +662,7 @@ async function attemptInMergeVerificationFix(
}
// Create the fix agent session
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: rootDir, // Runs on the main branch in the project root
systemPrompt: `You are a verification fix agent running during a merge on the main branch.
@@ -1395,7 +1395,7 @@ You are assisting with a paused \`git pull --rebase\`.
: undefined,
});
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: rootDir,
systemPrompt,
tools: "coding",
@@ -2563,7 +2563,7 @@ interface AiAgentParams {
/**
* Run the AI agent to resolve conflicts and/or write commit message.
*
* Each invocation creates a **fresh session** via `createKbAgent` to ensure
* Each invocation creates a **fresh session** via `createFnAgent` to ensure
* no stale conversation state from previous merge attempts or unrelated sessions
* pollutes the merge context. The session is disposed in the `finally` block
* regardless of success or failure.
@@ -2673,7 +2673,7 @@ async function runAiAgentForCommit(params: AiAgentParams): Promise<{ success: bo
}
}
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: rootDir,
systemPrompt: mergerSystemPrompt,
tools: "coding",
@@ -3123,7 +3123,7 @@ If issues are found that need attention, describe them clearly.`;
}
}
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: rootDir,
systemPrompt: postMergeSystemPrompt,
tools: toolMode,

View File

@@ -2,7 +2,7 @@
* MissionExecutionLoop unit tests.
*
* Tests the validation cycle orchestration class with mocked TaskStore, MissionStore,
* and AI agent (createKbAgent/promptWithFallback).
* and AI agent (createFnAgent/promptWithFallback).
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
@@ -31,9 +31,9 @@ const mockSessionHolder: {
// Mock the pi module before MissionExecutionLoop is imported
vi.mock("./pi.js", () => {
const createKbAgent = vi.fn(() => Promise.resolve({ session: mockSessionHolder.session }));
const createFnAgent = vi.fn(() => Promise.resolve({ session: mockSessionHolder.session }));
const promptWithFallback = vi.fn().mockResolvedValue(undefined);
return { createKbAgent, promptWithFallback };
return { createFnAgent, promptWithFallback };
});
vi.mock("./logger.js", () => ({

View File

@@ -19,7 +19,7 @@ import type {
MissionFeature,
MissionValidatorRun,
} from "@fusion/core";
import { createKbAgent, promptWithFallback, type AgentResult } from "./pi.js";
import { createFnAgent, promptWithFallback, type AgentResult } from "./pi.js";
import { createLogger } from "./logger.js";
/** Logger for the mission execution loop subsystem. */
@@ -325,7 +325,7 @@ export class MissionExecutionLoop extends EventEmitter {
try {
// Create validation agent session
session = await createKbAgent({
session = await createFnAgent({
cwd: this.rootDir,
systemPrompt: this.buildValidationSystemPrompt(feature, assertions, taskContext),
tools: "readonly",
@@ -383,7 +383,7 @@ export class MissionExecutionLoop extends EventEmitter {
* Run the actual validation session with the AI agent.
*/
private async runValidationSession(
agentSession: Awaited<ReturnType<typeof createKbAgent>>["session"],
agentSession: Awaited<ReturnType<typeof createFnAgent>>["session"],
prompt: string,
): Promise<void> {
// Use promptWithFallback for resilience - if the primary model fails,
@@ -401,7 +401,7 @@ export class MissionExecutionLoop extends EventEmitter {
* We extract the text from the AI's messages and parse the JSON response.
*/
private async parseValidationResult(
agentSession: Awaited<ReturnType<typeof createKbAgent>>["session"],
agentSession: Awaited<ReturnType<typeof createFnAgent>>["session"],
assertions: MissionContractAssertion[],
): Promise<ValidationResult> {
try {
@@ -467,7 +467,7 @@ export class MissionExecutionLoop extends EventEmitter {
* Looks for the last assistant message with text content.
*/
private extractResponseTextFromSession(
agentSession: Awaited<ReturnType<typeof createKbAgent>>["session"],
agentSession: Awaited<ReturnType<typeof createFnAgent>>["session"],
): string | undefined {
try {
// Access the session state to get messages

View File

@@ -130,7 +130,7 @@ describe("worktree path boundary helpers", () => {
const tools = [mockReadTool as any];
// Simulate wrapping (normally done inside createKbAgent)
// Simulate wrapping (normally done inside createFnAgent)
const { wrapToolsWithBoundary } = await import("./pi.js");
const wrapped = wrapToolsWithBoundary(
tools,
@@ -365,7 +365,7 @@ describe("worktree path boundary helpers", () => {
});
});
describe("createKbAgent", () => {
describe("createFnAgent", () => {
beforeEach(() => {
vi.clearAllMocks();
execSyncMock.mockReturnValue("");
@@ -395,9 +395,9 @@ describe("createKbAgent", () => {
return "worktree /project\nHEAD abc123\nbranch refs/heads/main\n";
});
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await expect(createKbAgent({
await expect(createFnAgent({
cwd: "/project/.worktrees/fn-001",
systemPrompt: "test",
tools: "coding",
@@ -422,9 +422,9 @@ describe("createKbAgent", () => {
"worktree /project/.worktrees/fn-001\nHEAD def456\nbranch refs/heads/fusion/fn-001\n";
});
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await createKbAgent({
await createFnAgent({
cwd: "/project/.worktrees/fn-001",
systemPrompt: "test",
tools: "coding",
@@ -452,9 +452,9 @@ describe("createKbAgent", () => {
errors: [],
});
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await createKbAgent({
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -474,9 +474,9 @@ describe("createKbAgent", () => {
});
it("avoids lock-based SettingsManager.create when loading extension providers", async () => {
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await createKbAgent({
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -495,9 +495,9 @@ describe("createKbAgent", () => {
provider === "zai" && modelId === "glm-5.1" ? undefined : { provider, id: modelId }
));
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await expect(createKbAgent({
await expect(createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -513,9 +513,9 @@ describe("createKbAgent", () => {
provider === "openai-codex" && modelId === "missing-model" ? undefined : { provider, id: modelId }
));
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await expect(createKbAgent({
await expect(createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "coding",
@@ -529,9 +529,9 @@ describe("createKbAgent", () => {
});
it("creates a session when configured models resolve successfully", async () => {
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await createKbAgent({
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -570,9 +570,9 @@ describe("createKbAgent", () => {
},
});
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -620,9 +620,9 @@ describe("createKbAgent", () => {
},
});
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -643,9 +643,9 @@ describe("createKbAgent", () => {
});
it("enables auto-compaction to prevent context-window overflow", async () => {
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await createKbAgent({
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "coding",
@@ -660,9 +660,9 @@ describe("createKbAgent", () => {
});
it("passes compaction enabled alongside retry settings", async () => {
const { createKbAgent } = await import("./pi.js");
const { createFnAgent } = await import("./pi.js");
await createKbAgent({
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
@@ -734,7 +734,7 @@ describe("createKbAgent", () => {
},
}));
const { createKbAgent: freshCreateKbAgent } = await import("./pi.js");
const { createFnAgent: freshCreateKbAgent } = await import("./pi.js");
await freshCreateKbAgent({
cwd: "/tmp",
@@ -809,7 +809,7 @@ describe("createKbAgent", () => {
},
}));
const { createKbAgent: freshCreateKbAgent } = await import("./pi.js");
const { createFnAgent: freshCreateKbAgent } = await import("./pi.js");
await freshCreateKbAgent({
cwd: "/tmp",
@@ -881,7 +881,7 @@ describe("createKbAgent", () => {
},
}));
const { createKbAgent: freshCreateKbAgent } = await import("./pi.js");
const { createFnAgent: freshCreateKbAgent } = await import("./pi.js");
await freshCreateKbAgent({
cwd: "/tmp",

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { describeModel, compactSessionContext, COMPACTION_FALLBACK_INSTRUCTIONS, createKbAgent, promptWithFallback, type AgentOptions } from "./pi.js";
import { describeModel, compactSessionContext, COMPACTION_FALLBACK_INSTRUCTIONS, createFnAgent, promptWithFallback, type AgentOptions } from "./pi.js";
import { createAgentSession, type AgentSession } from "@mariozechner/pi-coding-agent";
import { piLog } from "./logger.js";
@@ -263,7 +263,7 @@ describe("promptWithFallback context recovery", () => {
});
});
describe("createKbAgent skills parameter", () => {
describe("createFnAgent skills parameter", () => {
let piLogSpy: ReturnType<typeof vi.spyOn>;
let piWarnSpy: ReturnType<typeof vi.spyOn>;
let piErrorSpy: ReturnType<typeof vi.spyOn>;
@@ -306,7 +306,7 @@ describe("createKbAgent skills parameter", () => {
skills: ["review", "fusion"],
};
await createKbAgent(options);
await createFnAgent(options);
// Verify resolveSessionSkills was called with auto-derived context
expect(mockResolveSessionSkills).toHaveBeenCalledTimes(1);
@@ -328,7 +328,7 @@ describe("createKbAgent skills parameter", () => {
},
};
await createKbAgent(options);
await createFnAgent(options);
// Verify resolveSessionSkills was called with explicit skillSelection (not auto-derived)
expect(mockResolveSessionSkills).toHaveBeenCalledTimes(1);
@@ -350,7 +350,7 @@ describe("createKbAgent skills parameter", () => {
skills: [],
};
await createKbAgent(options);
await createFnAgent(options);
// Verify no skill resolution occurred
expect(mockResolveSessionSkills).not.toHaveBeenCalled();
@@ -364,7 +364,7 @@ describe("createKbAgent skills parameter", () => {
skills: ["review", "fusion"],
};
await createKbAgent(options);
await createFnAgent(options);
// Verify the log message includes the skill names
expect(piLogSpy).toHaveBeenCalledWith(
@@ -389,7 +389,7 @@ describe("createKbAgent skills parameter", () => {
skills: ["nonexistent-skill"],
};
await createKbAgent(options);
await createFnAgent(options);
// The diagnostics should be logged
expect(mockResolveSessionSkills).toHaveBeenCalled();
@@ -601,7 +601,7 @@ describe("session failure diagnostics", () => {
.mockResolvedValueOnce({ session: primarySession } as any)
.mockResolvedValueOnce({ session: fallbackSession } as any);
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd: "/test/project",
systemPrompt: "Test fallback swap",
defaultProvider: "test",
@@ -639,7 +639,7 @@ describe("piLog structured diagnostics", () => {
});
it("logs session creation with model info", async () => {
await createKbAgent({
await createFnAgent({
cwd: "/test/project",
systemPrompt: "Test",
defaultProvider: "test",
@@ -668,7 +668,7 @@ describe("piLog structured diagnostics", () => {
},
} as any);
await createKbAgent({
await createFnAgent({
cwd: "/test/project",
systemPrompt: "Test",
defaultProvider: "test",
@@ -688,7 +688,7 @@ describe("piLog structured diagnostics", () => {
createAgentSessionMock.mockReset();
createAgentSessionMock.mockRejectedValueOnce(new Error("fatal model failure"));
await expect(createKbAgent({
await expect(createFnAgent({
cwd: "/test/project",
systemPrompt: "Test",
defaultProvider: "test",

View File

@@ -671,8 +671,8 @@ export function wrapToolsWithBoundary(
* Create a pi agent session configured for fn.
* Reuses the user's existing pi auth and model configuration.
*/
export async function createKbAgent(options: AgentOptions): Promise<AgentResult> {
piLog.log(`createKbAgent called (cwd=${options.cwd}, tools=${options.tools}, provider=${options.defaultProvider}, model=${options.defaultModelId})`);
export async function createFnAgent(options: AgentOptions): Promise<AgentResult> {
piLog.log(`createFnAgent called (cwd=${options.cwd}, tools=${options.tools}, provider=${options.defaultProvider}, model=${options.defaultModelId})`);
const authStorage = createFusionAuthStorage();
const modelRegistry = new ModelRegistry(authStorage, getModelRegistryModelsPath());
await registerExtensionProviders(options.cwd, modelRegistry);

View File

@@ -17,7 +17,7 @@ import { AgentSemaphore } from "./concurrency.js";
// ── Module-level mocks (matching existing test patterns) ──────────────────
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
describeModel: vi.fn().mockReturnValue("mock-provider/mock-model"),
promptWithFallback: vi.fn(async (session, prompt, options) => {
if (options === undefined) {
@@ -96,12 +96,12 @@ import { TriageProcessor } from "./triage.js";
import { Scheduler } from "./scheduler.js";
import { aiMergeTask } from "./merger.js";
import { WorktreePool, scanIdleWorktrees, cleanupOrphanedWorktrees } from "./worktree-pool.js";
import { createKbAgent } from "./pi.js";
import { createFnAgent } from "./pi.js";
import { execSync } from "node:child_process";
import { existsSync, readdirSync } from "node:fs";
import type { Task, TaskDetail, TaskStep, Column, Settings, StepStatus } from "@fusion/core";
const mockedCreateHaiAgent = vi.mocked(createKbAgent);
const mockedCreateHaiAgent = vi.mocked(createFnAgent);
const mockedExecSync = vi.mocked(execSync);
const mockedExistsSync = vi.mocked(existsSync);
const mockedReaddirSync = vi.mocked(readdirSync);

View File

@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
describeModel: vi.fn().mockReturnValue("mock-provider/mock-model"),
promptWithFallback: vi.fn(async (session, prompt, options) => {
if (options === undefined) {
@@ -13,9 +13,9 @@ vi.mock("./pi.js", () => ({
}));
import { reviewStep, REVIEWER_SYSTEM_PROMPT } from "./reviewer.js";
import { createKbAgent } from "./pi.js";
import { createFnAgent } from "./pi.js";
const mockedCreateHaiAgent = vi.mocked(createKbAgent);
const mockedCreateHaiAgent = vi.mocked(createFnAgent);
function createMockSession(reviewText: string) {
return {
@@ -38,7 +38,7 @@ describe("reviewStep — model settings threading", () => {
vi.clearAllMocks();
});
it("passes defaultProvider and defaultModelId to createKbAgent when provided", async () => {
it("passes defaultProvider and defaultModelId to createFnAgent when provided", async () => {
mockedCreateHaiAgent.mockResolvedValue(
createMockSession("### Verdict: APPROVE\n### Summary\nLooks good."),
);
@@ -130,7 +130,7 @@ describe("reviewStep — spec review type", () => {
expect(result.verdict).toBe("RETHINK");
});
it("calls createKbAgent with readonly tools and correct system prompt", async () => {
it("calls createFnAgent with readonly tools and correct system prompt", async () => {
mockedCreateHaiAgent.mockResolvedValue(
createMockSession("### Verdict: APPROVE\n### Summary\nGood spec."),
);
@@ -504,7 +504,7 @@ describe("reviewStep — user comments in spec review", () => {
sessionManager: { getLeafId: vi.fn() },
},
} as any);
vi.mocked(createKbAgent).mockImplementation(mockedCreateHaiAgent);
vi.mocked(createFnAgent).mockImplementation(mockedCreateHaiAgent);
});
it("includes user comments in spec review request", async () => {
@@ -647,7 +647,7 @@ describe("reviewStep — skill selection resolver contract (FN-1510/FN-1511)", (
vi.clearAllMocks();
});
it("passes skillSelection to createKbAgent when agentStore and rootDir are provided", async () => {
it("passes skillSelection to createFnAgent when agentStore and rootDir are provided", async () => {
const { buildSessionSkillContext } = await import("./session-skill-context.js");
vi.mocked(buildSessionSkillContext).mockResolvedValue({
skillSelectionContext: {
@@ -828,7 +828,7 @@ describe("reviewStep — skill selection resolver contract (FN-1510/FN-1511)", (
},
);
// Verify the resolved names are passed to createKbAgent
// Verify the resolved names are passed to createFnAgent
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(1);
const opts = mockedCreateHaiAgent.mock.calls[0][0];
expect(opts.skillSelection?.requestedSkillNames).toEqual(resolvedNames);

View File

@@ -10,7 +10,7 @@
import type { TaskStore, TaskComment, AgentPromptsConfig, Settings } from "@fusion/core";
import { buildReviewerMemoryInstructions, resolveAgentPrompt } from "@fusion/core";
import { createKbAgent, describeModel, promptWithFallback } from "./pi.js";
import { createFnAgent, describeModel, promptWithFallback } from "./pi.js";
import { buildSessionSkillContext } from "./session-skill-context.js";
import { AgentLogger } from "./agent-logger.js";
import { reviewerLog } from "./logger.js";
@@ -359,7 +359,7 @@ export async function reviewStep(
} : undefined),
]
: undefined;
const { session } = await createKbAgent({
const { session } = await createFnAgent({
cwd,
systemPrompt: reviewerSystemPrompt,
tools: "readonly",

View File

@@ -56,10 +56,10 @@ export interface SessionSkillContextInput {
/**
* Result of building session skill context.
* Contains the SkillSelectionContext for createKbAgent and any diagnostics.
* Contains the SkillSelectionContext for createFnAgent and any diagnostics.
*/
export interface SessionSkillContextResult {
/** Context to pass to createKbAgent's skillSelection option */
/** Context to pass to createFnAgent's skillSelection option */
skillSelectionContext: SkillSelectionContext | undefined;
/** Normalized skill names that were resolved (for logging/debugging) */
resolvedSkillNames: string[];
@@ -159,7 +159,7 @@ export const SKILL_DIAGNOSTIC_MESSAGES = {
// ── Main Builder ────────────────────────────────────────────────────────────
/**
* Build session skill context for createKbAgent.
* Build session skill context for createFnAgent.
*
* Applies precedence rules:
* 1. Use assigned agent skills if available

View File

@@ -6,7 +6,7 @@
* 2. Optional caller-requested skill names (for per-task overrides)
*
* The resolver reads project settings files directly (read-only) and produces
* a filter set used by createKbAgent's DefaultResourceLoader.skillsOverride.
* a filter set used by createFnAgent's DefaultResourceLoader.skillsOverride.
*/
import { existsSync, readFileSync } from "node:fs";

View File

@@ -538,7 +538,7 @@ Some freeform text without checkboxes.`;
// Mock pi.js for StepSessionExecutor tests
vi.mock("./pi.js", () => ({
createKbAgent: vi.fn(),
createFnAgent: vi.fn(),
promptWithFallback: vi.fn(async (session: any, prompt: string) => {
await session.prompt(prompt);
}),
@@ -639,13 +639,13 @@ vi.mock("node:fs", () => ({
existsSync: vi.fn().mockReturnValue(true),
}));
import { createKbAgent } from "./pi.js";
import { createFnAgent } from "./pi.js";
import { generateWorktreeName } from "./worktree-names.js";
import { execSync } from "node:child_process";
import { AgentSemaphore } from "./concurrency.js";
import { createLogger } from "./logger.js";
const mockedCreateKbAgent = vi.mocked(createKbAgent);
const mockedCreateKbAgent = vi.mocked(createFnAgent);
const mockedExecSync = vi.mocked(execSync);
const mockedGenerateWorktreeName = vi.mocked(generateWorktreeName);
const mockedCreateLogger = vi.mocked(createLogger);
@@ -2023,7 +2023,7 @@ describe("StepSessionExecutor", () => {
// ── Skill Selection Regression Tests (FN-1514) ──────────────────────────
//
// Note: These tests verify that skillSelection is passed through the
// StepSessionExecutor to createKbAgent calls. The actual skill resolution
// StepSessionExecutor to createFnAgent calls. The actual skill resolution
// logic is tested in session-skill-context.test.ts.
// The full integration with executeAll is tested indirectly through
// the executor tests which create StepSessionExecutor with skillSelection.
@@ -2074,7 +2074,7 @@ describe("StepSessionExecutor skillSelection regression (FN-1511)", () => {
describe("StepSessionExecutor tool availability", () => {
/**
* These tests verify tool configuration by capturing the customTools
* passed to createKbAgent during executeStep execution. Each test
* passed to createFnAgent during executeStep execution. Each test
* uses fake timers and advances time to resolve any pending sleep()s.
*/
async function captureCustomTools(options?: {

View File

@@ -19,7 +19,7 @@ import { join } from "node:path";
import type { AgentSession } from "@mariozechner/pi-coding-agent";
import type { AgentStore, MessageStore, TaskDetail, Settings, TaskStore } from "@fusion/core";
import { createKbAgent, promptWithFallback, describeModel } from "./pi.js";
import { createFnAgent, promptWithFallback, describeModel } from "./pi.js";
import type { SkillSelectionContext } from "./skill-resolver.js";
import { generateWorktreeName } from "./worktree-names.js";
import { AgentSemaphore } from "./concurrency.js";
@@ -848,7 +848,7 @@ export class StepSessionExecutor {
? settings.executionGlobalModelId
: settings.defaultModelId));
const createResult = await createKbAgent({
const createResult = await createFnAgent({
cwd: worktreePath,
systemPrompt: `You are an AI agent executing step ${stepIndex} of task ${taskDetail.id}. Follow instructions precisely.`,
defaultProvider: executorProvider,

View File

@@ -23,7 +23,7 @@ vi.mock("./reviewer.js", () => ({
}));
vi.mock("./pi.js", () => ({
createKbAgent: mockCreateKbAgent,
createFnAgent: mockCreateKbAgent,
describeModel: vi.fn().mockReturnValue("mock-model"),
promptWithFallback: vi.fn().mockReturnValue("mock-prompt"),
}));
@@ -1277,7 +1277,7 @@ describe("taskCreate tool model inheritance", () => {
it("closes parent after proactive split even when breakIntoSubtasks is undefined", async () => {
// Test that the post-session closure path doesn't gate on breakIntoSubtasks.
// Strategy: capture the customTools from createKbAgent, then have
// Strategy: capture the customTools from createFnAgent, then have
// promptWithFallback invoke the task_create tool to simulate the agent
// proactively splitting an oversized task.
const task: Task = {
@@ -1329,7 +1329,7 @@ describe("taskCreate tool model inheritance", () => {
.mockResolvedValueOnce(childTask2),
});
// Capture customTools from createKbAgent call
// Capture customTools from createFnAgent call
let capturedCustomTools: any[] = [];
const mockDispose = vi.fn();
mockCreateKbAgent.mockImplementation(async (opts: any) => {
@@ -1457,7 +1457,7 @@ describe("taskCreate tool model inheritance", () => {
pollIntervalMs: 100_000,
});
// Mock createKbAgent to throw a transient error
// Mock createFnAgent to throw a transient error
mockCreateKbAgent.mockRejectedValue(new Error("upstream connect error"));
await processor.specifyTask(task);
@@ -1633,7 +1633,7 @@ describe("taskCreate tool model inheritance", () => {
getTask: vi.fn().mockResolvedValue({ ...task, attachments: [] }),
});
// Set up createKbAgent to return a session that immediately throws
// Set up createFnAgent to return a session that immediately throws
// after the model log line, so we can verify the appendAgentLog call.
// The session will be created, model logged, then promptWithFallback
// throws — but the model log has already been written.
@@ -2447,10 +2447,10 @@ describe("tool callback behavior (FN-1500)", () => {
const processor = new TriageProcessor(store, "/tmp/root", { stuckTaskDetector: mockDetector });
// Access the agentLogger via internal agentWork closure
// by running specifyTask and intercepting the createKbAgent call
// by running specifyTask and intercepting the createFnAgent call
let capturedOnAgentTool: ((id: string, name: string) => void) | undefined;
mockCreateKbAgent.mockImplementation(async (opts: any) => {
// Capture the onToolStart callback that was passed to createKbAgent
// Capture the onToolStart callback that was passed to createFnAgent
// This is the onAgentTool from agentLogger
if (opts.onToolStart) {
capturedOnAgentTool = opts.onToolStart;
@@ -2586,7 +2586,7 @@ describe("TriageProcessor skillSelection regression (FN-1511)", () => {
});
/**
* Helper: execute triage on a task and capture createKbAgent call arguments.
* Helper: execute triage on a task and capture createFnAgent call arguments.
*/
async function captureCreateKbAgentArgs(options?: {
assignedAgentId?: string;
@@ -2661,7 +2661,7 @@ describe("TriageProcessor skillSelection regression (FN-1511)", () => {
}
describe("skillSelection context propagation", () => {
it("passes skillSelection to createKbAgent with correct projectRootDir", async () => {
it("passes skillSelection to createFnAgent with correct projectRootDir", async () => {
const args = await captureCreateKbAgentArgs({
assignedAgentId: "agent-001",
assignedAgentSkills: ["triage"],

View File

@@ -13,7 +13,7 @@ import type {
ToolDefinition,
AgentSession,
} from "@mariozechner/pi-coding-agent";
import { createKbAgent, describeModel, promptWithFallback } from "./pi.js";
import { createFnAgent, describeModel, promptWithFallback } from "./pi.js";
import { reviewStep, type ReviewVerdict } from "./reviewer.js";
import { buildSessionSkillContext } from "./session-skill-context.js";
import { PRIORITY_SPECIFY, type AgentSemaphore } from "./concurrency.js";
@@ -635,7 +635,7 @@ export class TriageProcessor {
},
});
// Mutable ref — populated after createKbAgent, tools access lazily via closure
// Mutable ref — populated after createFnAgent, tools access lazily via closure
const sessionRef: { current: AgentSession | null } = { current: null };
// Checkpoint for RETHINK rewind — captured lazily on first review_spec call
const checkpointRef: { current: string | null } = { current: null };
@@ -704,7 +704,7 @@ export class TriageProcessor {
projectRootDir: this.rootDir,
});
let { session } = await createKbAgent({
let { session } = await createFnAgent({
cwd: this.rootDir,
systemPrompt: triageSystemPrompt,
tools: "coding",
@@ -868,7 +868,7 @@ export class TriageProcessor {
specReviewVerdictRef.current = null;
approvedCommentFingerprintRef.current = "";
const fallbackResult = await createKbAgent({
const fallbackResult = await createFnAgent({
cwd: this.rootDir,
systemPrompt: triageSystemPrompt,
tools: "coding",