feat(FN-4080): complete Step 2 — route thinking persistence by agent kind
Fusion-Task-Id: FN-4080 Fusion-Task-Lineage: 086863ba-7100-41ab-9cfc-197301fbcab0
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
import { isEphemeralAgent, resolvePersistAgentThinkingLog, type GlobalSettings } from "@fusion/core";
|
||||
import { AgentLogger } from "../agent-logger.js";
|
||||
|
||||
vi.mock("../agent-logger.js", () => ({
|
||||
AgentLogger: vi.fn(),
|
||||
}));
|
||||
|
||||
function createLoggerForAgent(agent: { metadata?: Record<string, unknown> }, settings: Partial<GlobalSettings>) {
|
||||
const ephemeral = isEphemeralAgent(agent);
|
||||
return new AgentLogger({
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral }),
|
||||
});
|
||||
}
|
||||
|
||||
describe("thinking persistence routing", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const permanentAgent = { metadata: { type: "permanent" } };
|
||||
const ephemeralAgent = { metadata: { agentKind: "task-worker" } };
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "both off",
|
||||
settings: { persistAgentThinkingLogPermanent: false, persistAgentThinkingLogEphemeral: false },
|
||||
expectedPermanent: false,
|
||||
expectedEphemeral: false,
|
||||
},
|
||||
{
|
||||
name: "permanent only",
|
||||
settings: { persistAgentThinkingLogPermanent: true, persistAgentThinkingLogEphemeral: false },
|
||||
expectedPermanent: true,
|
||||
expectedEphemeral: false,
|
||||
},
|
||||
{
|
||||
name: "ephemeral only",
|
||||
settings: { persistAgentThinkingLogPermanent: false, persistAgentThinkingLogEphemeral: true },
|
||||
expectedPermanent: false,
|
||||
expectedEphemeral: true,
|
||||
},
|
||||
{
|
||||
name: "legacy fallback",
|
||||
settings: { persistAgentThinkingLog: true },
|
||||
expectedPermanent: true,
|
||||
expectedEphemeral: true,
|
||||
},
|
||||
])("routes %s", ({ settings, expectedPermanent, expectedEphemeral }) => {
|
||||
createLoggerForAgent(permanentAgent, settings);
|
||||
createLoggerForAgent(ephemeralAgent, settings);
|
||||
|
||||
const calls = vi.mocked(AgentLogger).mock.calls;
|
||||
expect(calls[0]?.[0]).toMatchObject({ persistAgentThinkingLog: expectedPermanent });
|
||||
expect(calls[1]?.[0]).toMatchObject({ persistAgentThinkingLog: expectedEphemeral });
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import type { AgentStore, AgentHeartbeatRun, HeartbeatInvocationSource, AgentHeartbeatConfig, AgentBudgetStatus, Message, MessageStore, TaskStore, TaskDetail, AgentRole, Agent, InboxTask, RunMutationContext, Settings, AgentConfigRevision, ReflectionStore, ChatStore, ChatRoom, ChatRoomMessage } from "@fusion/core";
|
||||
import { ApprovalRequestStore, buildExecutionMemoryInstructions, isEphemeralAgent, hasAgentIdentity, resolveEffectiveAgentPermissionPolicy, canAgentTakeImplementationTask, canAgentTakeImplementationTaskForExplicitRouting } from "@fusion/core";
|
||||
import { ApprovalRequestStore, buildExecutionMemoryInstructions, isEphemeralAgent, hasAgentIdentity, resolveEffectiveAgentPermissionPolicy, canAgentTakeImplementationTask, canAgentTakeImplementationTaskForExplicitRouting, resolvePersistAgentThinkingLog } from "@fusion/core";
|
||||
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
||||
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -2029,7 +2029,7 @@ export class HeartbeatMonitor {
|
||||
appendLog: (entry) => this.store.appendRunLog(agentId, run.id, entry),
|
||||
agent: agent.role as AgentRole,
|
||||
persistAgentToolOutput: memorySettings?.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: memorySettings?.persistAgentThinkingLog,
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(memorySettings, { ephemeral: isAgentEphemeral }),
|
||||
});
|
||||
} else if (taskId) {
|
||||
agentLogger = new AgentLogger({
|
||||
@@ -2038,7 +2038,7 @@ export class HeartbeatMonitor {
|
||||
agent: agent.role as AgentRole,
|
||||
appendLog: (entry) => this.store.appendRunLog(agentId, run.id, entry),
|
||||
persistAgentToolOutput: memorySettings?.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: memorySettings?.persistAgentThinkingLog,
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(memorySettings, { ephemeral: isAgentEphemeral }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getTaskMergeBlocker,
|
||||
isEphemeralAgent,
|
||||
resolveAgentPrompt,
|
||||
resolvePersistAgentThinkingLog,
|
||||
resolveEffectiveAgentPermissionPolicy,
|
||||
resolveProjectDefaultModel,
|
||||
type RunCommandResult,
|
||||
@@ -3160,7 +3161,8 @@ export class TaskExecutor {
|
||||
taskId: task.id,
|
||||
agent: "executor",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Executor sessions are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: (taskId, delta) => {
|
||||
lastAssistantText += delta;
|
||||
stuckDetector?.recordActivity(taskId);
|
||||
@@ -5290,7 +5292,8 @@ ${feedback}
|
||||
taskId: task.id,
|
||||
agent: "executor",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Executor sessions are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: this.options.onAgentText,
|
||||
onAgentTool: this.options.onAgentTool,
|
||||
});
|
||||
@@ -6100,7 +6103,8 @@ and show an appropriate message to the user.\`
|
||||
taskId: task.id,
|
||||
agent: "reviewer",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Review-in-executor sessions are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: (taskId, delta) => {
|
||||
this.options.onAgentText?.(taskId, delta);
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
resolveTaskMergeTarget,
|
||||
resolveTitleSummarizerSettingsModel,
|
||||
resolveAgentPrompt,
|
||||
resolvePersistAgentThinkingLog,
|
||||
summarizeCommitBody,
|
||||
summarizeCommitSubject,
|
||||
summarizeMergeCommit,
|
||||
@@ -1006,7 +1007,8 @@ async function attemptInMergeVerificationFix(
|
||||
taskId,
|
||||
agent: "merger",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Merger agents are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: options.onAgentText,
|
||||
onAgentTool: options.onAgentTool,
|
||||
});
|
||||
@@ -2116,7 +2118,8 @@ async function runAiAgentForAutostashConflict(params: {
|
||||
taskId,
|
||||
agent: "merger",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Merger agents are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: options.onAgentText
|
||||
? (_id: string, delta: string) => options.onAgentText!(delta)
|
||||
: undefined,
|
||||
@@ -2486,7 +2489,8 @@ async function runAiAgentForAutostashHardFail(params: {
|
||||
taskId,
|
||||
agent: "merger",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Merger agents are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: options.onAgentText
|
||||
? (_id: string, delta: string) => options.onAgentText!(delta)
|
||||
: undefined,
|
||||
@@ -4931,7 +4935,8 @@ You are assisting with a paused \`git pull --rebase\`.
|
||||
taskId,
|
||||
agent: "merger",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Merger agents are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: options?.onAgentText
|
||||
? (_id, delta) => options.onAgentText?.(delta)
|
||||
: undefined,
|
||||
@@ -7878,7 +7883,8 @@ async function runAiAgentForCommit(params: AiAgentParams): Promise<{ success: bo
|
||||
taskId,
|
||||
agent: "merger",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Merger agents are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: options.onAgentText
|
||||
? (_id, delta) => options.onAgentText!(delta)
|
||||
: undefined,
|
||||
@@ -8492,7 +8498,8 @@ If issues are found that need attention, describe them clearly and include concr
|
||||
taskId,
|
||||
agent: "merger",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Merger agents are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
import type { TaskStore, TaskComment, AgentPromptsConfig, Settings } from "@fusion/core";
|
||||
import { buildReviewerMemoryInstructions, resolveAgentPrompt } from "@fusion/core";
|
||||
import { buildReviewerMemoryInstructions, resolveAgentPrompt, resolvePersistAgentThinkingLog } from "@fusion/core";
|
||||
import { describeModel, promptWithFallback } from "./pi.js";
|
||||
import { isContextLimitError } from "./context-limit-detector.js";
|
||||
import { createResolvedAgentSession, extractRuntimeHint } from "./agent-session-helpers.js";
|
||||
@@ -350,7 +350,8 @@ export async function reviewStep(
|
||||
? (_id, delta) => options.onText!(delta)
|
||||
: undefined,
|
||||
persistAgentToolOutput: liveSettings?.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: liveSettings?.persistAgentThinkingLog,
|
||||
// Reviewer sessions are task-scoped ephemeral workers.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(liveSettings, { ephemeral: true }),
|
||||
})
|
||||
: null;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { AgentSession } from "@mariozechner/pi-coding-agent";
|
||||
import type { AgentStore, MessageStore, PermanentAgentGatingContext, TaskDetail, Settings, TaskStore } from "@fusion/core";
|
||||
import { resolvePersistAgentThinkingLog } from "@fusion/core";
|
||||
|
||||
import {
|
||||
createResolvedAgentSession,
|
||||
@@ -890,7 +891,8 @@ export class StepSessionExecutor {
|
||||
taskId: taskDetail.id,
|
||||
agent: "executor",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Step-session workers are task-scoped ephemeral agents.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
});
|
||||
let session: AgentSession | null = null;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
import {
|
||||
buildTriageMemoryInstructions,
|
||||
resolveAgentPrompt,
|
||||
resolvePersistAgentThinkingLog,
|
||||
sortTasksByPriorityThenAgeAndId,
|
||||
} from "@fusion/core";
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
@@ -928,7 +929,8 @@ export class TriageProcessor {
|
||||
taskId: task.id,
|
||||
agent: "triage",
|
||||
persistAgentToolOutput: settings.persistAgentToolOutput,
|
||||
persistAgentThinkingLog: settings.persistAgentThinkingLog,
|
||||
// Triage runs in a task-scoped ephemeral worker session.
|
||||
persistAgentThinkingLog: resolvePersistAgentThinkingLog(settings, { ephemeral: true }),
|
||||
onAgentText: (id, delta) => {
|
||||
stuckDetector?.recordActivity(task.id);
|
||||
this.options.onAgentText?.(id, delta);
|
||||
|
||||
Reference in New Issue
Block a user