feat(FN-4393): complete Step 3 — branch memory inclusion by mode
Fusion-Task-Id: FN-4393 Fusion-Task-Lineage: 3615215d-9caa-4402-9258-a5a5de137dd6
This commit is contained in:
@@ -422,6 +422,26 @@ describe("resolveAgentInstructions with rating summary", () => {
|
|||||||
expect(result).not.toContain("Should be trimmed");
|
expect(result).not.toContain("Should be trimmed");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders index-mode memory instead of full body", async () => {
|
||||||
|
await mkdir(join(testDir, ".fusion", "agent-memory", "agent-test"), { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
join(testDir, ".fusion", "agent-memory", "agent-test", "MEMORY.md"),
|
||||||
|
"## Delegation\n\nUse concise asks\n",
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await resolveAgentInstructions(makeAgent({ memory: "inline memory" }), testDir, undefined, "index");
|
||||||
|
expect(result).toContain("Memory is provided in index mode");
|
||||||
|
expect(result).toContain("## Agent Memory Index (use fn_memory_search / fn_memory_get to read)");
|
||||||
|
expect(result).not.toContain("inline memory");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("omits memory section in off mode", async () => {
|
||||||
|
const result = await resolveAgentInstructions(makeAgent({ memory: "inline memory" }), testDir, undefined, "off");
|
||||||
|
expect(result).not.toContain("## Agent Memory");
|
||||||
|
expect(result).not.toContain("inline memory");
|
||||||
|
});
|
||||||
|
|
||||||
it("omits category breakdown when category averages are empty", async () => {
|
it("omits category breakdown when category averages are empty", async () => {
|
||||||
const result = await resolveAgentInstructions(
|
const result = await resolveAgentInstructions(
|
||||||
makeAgent({ instructionsText: "Do work" }),
|
makeAgent({ instructionsText: "Do work" }),
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
* - onTerminated: Called when a heartbeat run is terminated
|
* - onTerminated: Called when a heartbeat run is terminated
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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 type { AgentStore, AgentHeartbeatRun, HeartbeatInvocationSource, AgentHeartbeatConfig, AgentBudgetStatus, Message, MessageStore, TaskStore, TaskDetail, AgentRole, Agent, InboxTask, RunMutationContext, Settings, AgentConfigRevision, ReflectionStore, ChatStore, ChatRoom, ChatRoomMessage, AgentMemoryInclusionMode } from "@fusion/core";
|
||||||
import { ApprovalRequestStore, buildExecutionMemoryInstructions, isEphemeralAgent, hasAgentIdentity, resolveEffectiveAgentPermissionPolicy, canAgentTakeImplementationTask, canAgentTakeImplementationTaskForExplicitRouting, resolvePersistAgentThinkingLog } from "@fusion/core";
|
import { ApprovalRequestStore, buildExecutionMemoryInstructions, isEphemeralAgent, hasAgentIdentity, resolveEffectiveAgentPermissionPolicy, canAgentTakeImplementationTask, canAgentTakeImplementationTaskForExplicitRouting, resolvePersistAgentThinkingLog, resolveAgentMemoryInclusionMode } from "@fusion/core";
|
||||||
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
||||||
import { Type, type Static } from "@mariozechner/pi-ai";
|
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
@@ -39,6 +39,16 @@ import type { AgentActionGateContext } from "./agent-action-gate.js";
|
|||||||
import { buildSessionSkillContextSync } from "./session-skill-context.js";
|
import { buildSessionSkillContextSync } from "./session-skill-context.js";
|
||||||
import type { AgentReflectionService } from "./agent-reflection.js";
|
import type { AgentReflectionService } from "./agent-reflection.js";
|
||||||
|
|
||||||
|
function adjustHeartbeatMemoryPrimer(basePrompt: string, mode: AgentMemoryInclusionMode): string {
|
||||||
|
if (mode === "full") return basePrompt;
|
||||||
|
const memoryPrimer = /\nYou may receive an Agent Memory section and a Project Memory section\.[\s\S]*?repository pitfalls\.\n/;
|
||||||
|
if (mode === "off") return basePrompt.replace(memoryPrimer, "\n");
|
||||||
|
return basePrompt.replace(
|
||||||
|
memoryPrimer,
|
||||||
|
"\nWhen an Agent Memory Index is provided instead of full memory, call fn_memory_search first for task-relevant context. Use fn_memory_get to open only relevant snippets.\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface SelfImproveServiceLike {
|
interface SelfImproveServiceLike {
|
||||||
shouldRunSelfImprove(agentId: string): Promise<boolean>;
|
shouldRunSelfImprove(agentId: string): Promise<boolean>;
|
||||||
getSelfImprovePrompt(agentId: string): Promise<string>;
|
getSelfImprovePrompt(agentId: string): Promise<string>;
|
||||||
@@ -1968,13 +1978,19 @@ export class HeartbeatMonitor {
|
|||||||
// Build skill selection context for heartbeat session (uses waking agent's skills, no role fallback)
|
// Build skill selection context for heartbeat session (uses waking agent's skills, no role fallback)
|
||||||
const skillContext = buildSessionSkillContextSync(agent, "heartbeat", rootDir, this.pluginRunner);
|
const skillContext = buildSessionSkillContextSync(agent, "heartbeat", rootDir, this.pluginRunner);
|
||||||
|
|
||||||
const baseHeartbeatSystemPrompt = isNoTaskRun
|
const resolvedMemoryMode = resolveAgentMemoryInclusionMode({
|
||||||
? HEARTBEAT_NO_TASK_SYSTEM_PROMPT
|
agent,
|
||||||
: HEARTBEAT_SYSTEM_PROMPT;
|
projectSettings: memorySettings,
|
||||||
|
});
|
||||||
|
const priorMemoryMode = agent.runtimeConfig?.lastAgentMemoryInclusionMode;
|
||||||
|
const baseHeartbeatSystemPrompt = adjustHeartbeatMemoryPrimer(
|
||||||
|
isNoTaskRun ? HEARTBEAT_NO_TASK_SYSTEM_PROMPT : HEARTBEAT_SYSTEM_PROMPT,
|
||||||
|
resolvedMemoryMode.mode,
|
||||||
|
);
|
||||||
let resolvedInstructionsForIdentity = "";
|
let resolvedInstructionsForIdentity = "";
|
||||||
let workspaceMemoryForIdentity = "";
|
let workspaceMemoryForIdentity = "";
|
||||||
try {
|
try {
|
||||||
resolvedInstructionsForIdentity = await resolveAgentInstructionsWithRatings(agent, rootDir, this.store);
|
resolvedInstructionsForIdentity = await resolveAgentInstructionsWithRatings(agent, rootDir, this.store, resolvedMemoryMode.mode);
|
||||||
} catch (instructionError) {
|
} catch (instructionError) {
|
||||||
const message = instructionError instanceof Error ? instructionError.message : String(instructionError);
|
const message = instructionError instanceof Error ? instructionError.message : String(instructionError);
|
||||||
heartbeatLog.warn(`Failed to resolve agent instructions for heartbeat ${agentId}: ${message}`);
|
heartbeatLog.warn(`Failed to resolve agent instructions for heartbeat ${agentId}: ${message}`);
|
||||||
@@ -1988,9 +2004,11 @@ export class HeartbeatMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let memoryInstructions = "";
|
let memoryInstructions = "";
|
||||||
if (memorySettings?.memoryEnabled !== false) {
|
if (resolvedMemoryMode.mode !== "off" && memorySettings?.memoryEnabled !== false) {
|
||||||
try {
|
try {
|
||||||
memoryInstructions = buildExecutionMemoryInstructions(rootDir, memorySettings);
|
memoryInstructions = resolvedMemoryMode.mode === "index"
|
||||||
|
? "## Project Memory (Index Only)\n\nProject memory is available via fn_memory_search and fn_memory_get. Search first, then fetch only relevant excerpts."
|
||||||
|
: buildExecutionMemoryInstructions(rootDir, memorySettings);
|
||||||
} catch (memoryInstructionErr) {
|
} catch (memoryInstructionErr) {
|
||||||
const message = memoryInstructionErr instanceof Error ? memoryInstructionErr.message : String(memoryInstructionErr);
|
const message = memoryInstructionErr instanceof Error ? memoryInstructionErr.message : String(memoryInstructionErr);
|
||||||
heartbeatLog.warn(`Failed to resolve project memory instructions for heartbeat ${agentId}: ${message}`);
|
heartbeatLog.warn(`Failed to resolve project memory instructions for heartbeat ${agentId}: ${message}`);
|
||||||
@@ -2028,6 +2046,20 @@ export class HeartbeatMonitor {
|
|||||||
|
|
||||||
const systemPromptFinal = collapsePromptLayers(heartbeatLayers);
|
const systemPromptFinal = collapsePromptLayers(heartbeatLayers);
|
||||||
|
|
||||||
|
if (priorMemoryMode !== resolvedMemoryMode.mode) {
|
||||||
|
const from = priorMemoryMode ? priorMemoryMode : "(initial)";
|
||||||
|
try {
|
||||||
|
await this.store.appendRunLog(agentId, run.id, {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
taskId: taskId ?? run.taskId ?? "heartbeat",
|
||||||
|
type: "text",
|
||||||
|
text: `Agent memory inclusion mode: ${from} → ${resolvedMemoryMode.mode} (source: ${resolvedMemoryMode.source})`,
|
||||||
|
});
|
||||||
|
} catch (modeLogError) {
|
||||||
|
heartbeatLog.warn(`Failed to append memory-mode transition run log for ${agentId}: ${modeLogError instanceof Error ? modeLogError.message : String(modeLogError)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fn_heartbeat_done must be the last tool in the array (stable terminal signal)
|
// fn_heartbeat_done must be the last tool in the array (stable terminal signal)
|
||||||
heartbeatTools.push(heartbeatDoneTool);
|
heartbeatTools.push(heartbeatDoneTool);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { isAbsolute, resolve, relative, normalize, sep, dirname } from "node:pat
|
|||||||
import {
|
import {
|
||||||
readProjectMemory,
|
readProjectMemory,
|
||||||
type Agent,
|
type Agent,
|
||||||
|
type AgentMemoryInclusionMode,
|
||||||
type AgentRatingSummary,
|
type AgentRatingSummary,
|
||||||
type AgentStore,
|
type AgentStore,
|
||||||
type PluginPromptSurface,
|
type PluginPromptSurface,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
import type { PluginRunner } from "./plugin-runner.js";
|
import type { PluginRunner } from "./plugin-runner.js";
|
||||||
import { createLogger } from "./logger.js";
|
import { createLogger } from "./logger.js";
|
||||||
import { readAgentMemoryWorkspaceLongTerm } from "./agent-tools.js";
|
import { readAgentMemoryWorkspaceLongTerm } from "./agent-tools.js";
|
||||||
|
import { buildMemoryIndex } from "./agent-memory-index.js";
|
||||||
|
|
||||||
const log = createLogger("agent-instructions");
|
const log = createLogger("agent-instructions");
|
||||||
|
|
||||||
@@ -188,7 +190,31 @@ function memoryWorkspaceDisplayPath(agentId: string): string {
|
|||||||
return `.fusion/agent-memory/${safeAgentId}/MEMORY.md`;
|
return `.fusion/agent-memory/${safeAgentId}/MEMORY.md`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMemorySection(memory: string, workspaceMemory: string, agentId: string): string {
|
async function formatMemorySection(
|
||||||
|
memory: string,
|
||||||
|
workspaceMemory: string,
|
||||||
|
agentId: string,
|
||||||
|
rootDir: string,
|
||||||
|
inclusionMode: AgentMemoryInclusionMode,
|
||||||
|
): Promise<string> {
|
||||||
|
if (inclusionMode === "off") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inclusionMode === "index") {
|
||||||
|
const indexBody = await buildMemoryIndex({ rootDir, agentId });
|
||||||
|
if (!indexBody) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
"## Agent Memory",
|
||||||
|
"",
|
||||||
|
"Memory is provided in index mode. Use fn_memory_search first, then fn_memory_get for relevant files/lines.",
|
||||||
|
"",
|
||||||
|
indexBody,
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
const inlineTrimmed = trimAndClamp(memory, MAX_MEMORY_LENGTH, "memory", agentId);
|
const inlineTrimmed = trimAndClamp(memory, MAX_MEMORY_LENGTH, "memory", agentId);
|
||||||
const workspaceTrimmed = trimAndClamp(workspaceMemory, MAX_MEMORY_LENGTH, "workspace memory", agentId);
|
const workspaceTrimmed = trimAndClamp(workspaceMemory, MAX_MEMORY_LENGTH, "workspace memory", agentId);
|
||||||
if (!inlineTrimmed && !workspaceTrimmed) {
|
if (!inlineTrimmed && !workspaceTrimmed) {
|
||||||
@@ -260,6 +286,7 @@ export async function resolveAgentInstructions(
|
|||||||
agent: Agent | null | undefined,
|
agent: Agent | null | undefined,
|
||||||
rootDir: string,
|
rootDir: string,
|
||||||
ratingSummary?: AgentRatingSummary,
|
ratingSummary?: AgentRatingSummary,
|
||||||
|
inclusionMode: AgentMemoryInclusionMode = "full",
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (!agent) return "";
|
if (!agent) return "";
|
||||||
|
|
||||||
@@ -316,7 +343,13 @@ export async function resolveAgentInstructions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const workspaceMemory = await readAgentMemoryWorkspaceLongTerm(rootDir, agent.id);
|
const workspaceMemory = await readAgentMemoryWorkspaceLongTerm(rootDir, agent.id);
|
||||||
const memorySection = formatMemorySection(agent.memory ?? "", workspaceMemory, agent.id);
|
const memorySection = await formatMemorySection(
|
||||||
|
agent.memory ?? "",
|
||||||
|
workspaceMemory,
|
||||||
|
agent.id,
|
||||||
|
rootDir,
|
||||||
|
inclusionMode,
|
||||||
|
);
|
||||||
if (memorySection) {
|
if (memorySection) {
|
||||||
parts.push(memorySection);
|
parts.push(memorySection);
|
||||||
}
|
}
|
||||||
@@ -336,12 +369,13 @@ export async function resolveAgentInstructionsWithRatings(
|
|||||||
agent: Agent | null | undefined,
|
agent: Agent | null | undefined,
|
||||||
rootDir: string,
|
rootDir: string,
|
||||||
agentStore: AgentStore | undefined,
|
agentStore: AgentStore | undefined,
|
||||||
|
inclusionMode: AgentMemoryInclusionMode = "full",
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseInstructions = await resolveAgentInstructions(agent, rootDir);
|
const baseInstructions = await resolveAgentInstructions(agent, rootDir, undefined, inclusionMode);
|
||||||
|
|
||||||
if (!agentStore || !agent.id) {
|
if (!agentStore || !agent.id) {
|
||||||
return baseInstructions;
|
return baseInstructions;
|
||||||
@@ -349,7 +383,7 @@ export async function resolveAgentInstructionsWithRatings(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const ratingSummary = await agentStore.getRatingSummary(agent.id);
|
const ratingSummary = await agentStore.getRatingSummary(agent.id);
|
||||||
return await resolveAgentInstructions(agent, rootDir, ratingSummary);
|
return await resolveAgentInstructions(agent, rootDir, ratingSummary, inclusionMode);
|
||||||
} catch {
|
} catch {
|
||||||
return baseInstructions;
|
return baseInstructions;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const execAsync = promisify(exec);
|
|||||||
import { delimiter, isAbsolute, join, relative, resolve as resolvePath } from "node:path";
|
import { delimiter, isAbsolute, join, relative, resolve as resolvePath } from "node:path";
|
||||||
import { existsSync, realpathSync } from "node:fs";
|
import { existsSync, realpathSync } from "node:fs";
|
||||||
import { readFile, writeFile } from "node:fs/promises";
|
import { readFile, writeFile } from "node:fs/promises";
|
||||||
import type { TaskStore, Task, TaskDetail, TaskTokenUsage, StepStatus, Settings, WorkflowStep, MissionStore, Slice, AgentState, AgentCapability, RunMutationContext, AgentHeartbeatConfig, Agent } from "@fusion/core";
|
import type { TaskStore, Task, TaskDetail, TaskTokenUsage, StepStatus, Settings, WorkflowStep, MissionStore, Slice, AgentState, AgentCapability, RunMutationContext, AgentHeartbeatConfig, Agent, AgentMemoryInclusionMode } from "@fusion/core";
|
||||||
import {
|
import {
|
||||||
ApprovalRequestStore,
|
ApprovalRequestStore,
|
||||||
buildExecutionMemoryInstructions,
|
buildExecutionMemoryInstructions,
|
||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
resolvePersistAgentThinkingLog,
|
resolvePersistAgentThinkingLog,
|
||||||
resolveEffectiveAgentPermissionPolicy,
|
resolveEffectiveAgentPermissionPolicy,
|
||||||
resolveProjectDefaultModel,
|
resolveProjectDefaultModel,
|
||||||
|
resolveAgentMemoryInclusionMode,
|
||||||
type RunCommandResult,
|
type RunCommandResult,
|
||||||
} from "@fusion/core";
|
} from "@fusion/core";
|
||||||
import { findWorktreeUser } from "./merger.js";
|
import { findWorktreeUser } from "./merger.js";
|
||||||
@@ -2314,7 +2315,7 @@ export class TaskExecutor {
|
|||||||
* in the AgentStore that have instructions configured.
|
* in the AgentStore that have instructions configured.
|
||||||
* Returns an empty string if no instructions are found.
|
* Returns an empty string if no instructions are found.
|
||||||
*/
|
*/
|
||||||
private async resolveInstructionsForRole(role: string): Promise<string> {
|
private async resolveInstructionsForRole(role: string, settings?: Settings): Promise<string> {
|
||||||
if (!this.options.agentStore) return "";
|
if (!this.options.agentStore) return "";
|
||||||
try {
|
try {
|
||||||
const agents = await this.options.agentStore.listAgents({ role: role as AgentCapability });
|
const agents = await this.options.agentStore.listAgents({ role: role as AgentCapability });
|
||||||
@@ -2322,11 +2323,13 @@ export class TaskExecutor {
|
|||||||
if (agent.instructionsText || agent.instructionsPath) {
|
if (agent.instructionsText || agent.instructionsPath) {
|
||||||
try {
|
try {
|
||||||
const ratingSummary = await this.options.agentStore.getRatingSummary(agent.id);
|
const ratingSummary = await this.options.agentStore.getRatingSummary(agent.id);
|
||||||
return await resolveAgentInstructions(agent, this.rootDir, ratingSummary);
|
const mode = resolveAgentMemoryInclusionMode({ agent, projectSettings: settings }).mode;
|
||||||
|
return await resolveAgentInstructions(agent, this.rootDir, ratingSummary, mode);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const msg = err instanceof Error ? err.message : String(err);
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
executorLog.warn(`${agent.id}: failed to load rating summary for instruction resolution, falling back to default instructions: ${msg}`);
|
executorLog.warn(`${agent.id}: failed to load rating summary for instruction resolution, falling back to default instructions: ${msg}`);
|
||||||
return await resolveAgentInstructions(agent, this.rootDir);
|
const mode = resolveAgentMemoryInclusionMode({ agent, projectSettings: settings }).mode;
|
||||||
|
return await resolveAgentInstructions(agent, this.rootDir, undefined, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3241,7 +3244,7 @@ export class TaskExecutor {
|
|||||||
executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
|
executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
|
||||||
|
|
||||||
// Resolve per-agent custom instructions for the executor role
|
// Resolve per-agent custom instructions for the executor role
|
||||||
const executorInstructions = await this.resolveInstructionsForRole("executor");
|
const executorInstructions = await this.resolveInstructionsForRole("executor", settings);
|
||||||
|
|
||||||
// Build structured layers for cross-session prompt caching.
|
// Build structured layers for cross-session prompt caching.
|
||||||
const executorPluginContributions = buildPluginPromptSection(
|
const executorPluginContributions = buildPluginPromptSection(
|
||||||
@@ -6411,7 +6414,7 @@ and show an appropriate message to the user.\`
|
|||||||
attemptLabel: string,
|
attemptLabel: string,
|
||||||
): Promise<WorkflowStepOutcome> => {
|
): Promise<WorkflowStepOutcome> => {
|
||||||
// Workflow step agents inherit executor instructions
|
// Workflow step agents inherit executor instructions
|
||||||
const stepInstructions = await this.resolveInstructionsForRole("executor");
|
const stepInstructions = await this.resolveInstructionsForRole("executor", settings);
|
||||||
const stepSystemPrompt = buildSystemPromptWithInstructions(systemPrompt, stepInstructions);
|
const stepSystemPrompt = buildSystemPromptWithInstructions(systemPrompt, stepInstructions);
|
||||||
|
|
||||||
// Build skill selection context for workflow step session
|
// Build skill selection context for workflow step session
|
||||||
@@ -8156,7 +8159,7 @@ and show an appropriate message to the user.\`
|
|||||||
await this.options.agentStore.updateAgentState(agent.id, "active");
|
await this.options.agentStore.updateAgentState(agent.id, "active");
|
||||||
|
|
||||||
// Child agents inherit executor instructions
|
// Child agents inherit executor instructions
|
||||||
const childInstructions = await this.resolveInstructionsForRole("executor");
|
const childInstructions = await this.resolveInstructionsForRole("executor", settings);
|
||||||
const childBasePrompt = `You are a child agent spawned by a parent task executor.
|
const childBasePrompt = `You are a child agent spawned by a parent task executor.
|
||||||
|
|
||||||
Your role:
|
Your role:
|
||||||
@@ -8373,9 +8376,12 @@ git log --oneline
|
|||||||
// When enabled, agents consult and update project memory for durable project learnings.
|
// When enabled, agents consult and update project memory for durable project learnings.
|
||||||
// Backend-aware: instructions branch based on memoryBackendType (file, readonly, qmd)
|
// Backend-aware: instructions branch based on memoryBackendType (file, readonly, qmd)
|
||||||
const memoryEnabled = settings?.memoryEnabled !== false;
|
const memoryEnabled = settings?.memoryEnabled !== false;
|
||||||
|
const memoryMode: AgentMemoryInclusionMode = settings?.agentMemoryInclusionMode ?? "full";
|
||||||
let memorySection = "";
|
let memorySection = "";
|
||||||
if (memoryEnabled && rootDir) {
|
if (memoryEnabled && rootDir && memoryMode !== "off") {
|
||||||
memorySection = "\n" + buildExecutionMemoryInstructions(rootDir, settings);
|
memorySection = memoryMode === "index"
|
||||||
|
? "\n## Project Memory (Index Only)\n\nUse fn_memory_search first to find relevant memory, then fn_memory_get for specific excerpts.\n"
|
||||||
|
: "\n" + buildExecutionMemoryInstructions(rootDir, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build steering comments section (last 10 comments only to avoid context bloat)
|
// Build steering comments section (last 10 comments only to avoid context bloat)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { TaskStore, TaskComment, AgentPromptsConfig, Settings } from "@fusion/core";
|
import type { TaskStore, TaskComment, AgentPromptsConfig, Settings } from "@fusion/core";
|
||||||
import { buildReviewerMemoryInstructions, resolveAgentPrompt, resolvePersistAgentThinkingLog } from "@fusion/core";
|
import { buildReviewerMemoryInstructions, resolveAgentPrompt, resolvePersistAgentThinkingLog, resolveAgentMemoryInclusionMode } from "@fusion/core";
|
||||||
import { describeModel, promptWithFallback } from "./pi.js";
|
import { describeModel, promptWithFallback } from "./pi.js";
|
||||||
import { isContextLimitError } from "./context-limit-detector.js";
|
import { isContextLimitError } from "./context-limit-detector.js";
|
||||||
import { createResolvedAgentSession, extractRuntimeHint } from "./agent-session-helpers.js";
|
import { createResolvedAgentSession, extractRuntimeHint } from "./agent-session-helpers.js";
|
||||||
@@ -387,7 +387,8 @@ export async function reviewStep(
|
|||||||
const agents = await options.agentStore.listAgents({ role: "reviewer" });
|
const agents = await options.agentStore.listAgents({ role: "reviewer" });
|
||||||
for (const agent of agents) {
|
for (const agent of agents) {
|
||||||
if (agent.instructionsText || agent.instructionsPath) {
|
if (agent.instructionsText || agent.instructionsPath) {
|
||||||
reviewerInstructions = await resolveAgentInstructions(agent, options.rootDir);
|
const memoryMode = resolveAgentMemoryInclusionMode({ agent, projectSettings: options.settings }).mode;
|
||||||
|
reviewerInstructions = await resolveAgentInstructions(agent, options.rootDir, undefined, memoryMode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
resolveAgentPrompt,
|
resolveAgentPrompt,
|
||||||
resolvePersistAgentThinkingLog,
|
resolvePersistAgentThinkingLog,
|
||||||
sortTasksByPriorityThenAgeAndId,
|
sortTasksByPriorityThenAgeAndId,
|
||||||
|
resolveAgentMemoryInclusionMode,
|
||||||
} from "@fusion/core";
|
} from "@fusion/core";
|
||||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||||
import { Type, type Static } from "@mariozechner/pi-ai";
|
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||||
@@ -1008,10 +1009,12 @@ export class TriageProcessor {
|
|||||||
// Resolve per-agent custom instructions for the triage role or assigned agent.
|
// Resolve per-agent custom instructions for the triage role or assigned agent.
|
||||||
let triageInstructions = "";
|
let triageInstructions = "";
|
||||||
if (assignedAgent) {
|
if (assignedAgent) {
|
||||||
|
const memoryMode = resolveAgentMemoryInclusionMode({ agent: assignedAgent, projectSettings: settings }).mode;
|
||||||
triageInstructions = await resolveAgentInstructionsWithRatings(
|
triageInstructions = await resolveAgentInstructionsWithRatings(
|
||||||
assignedAgent,
|
assignedAgent,
|
||||||
this.rootDir,
|
this.rootDir,
|
||||||
this.options.agentStore,
|
this.options.agentStore,
|
||||||
|
memoryMode,
|
||||||
);
|
);
|
||||||
} else if (this.options.agentStore) {
|
} else if (this.options.agentStore) {
|
||||||
try {
|
try {
|
||||||
@@ -1019,7 +1022,8 @@ export class TriageProcessor {
|
|||||||
for (const agent of agents) {
|
for (const agent of agents) {
|
||||||
triageRuntimeHint ??= extractRuntimeHint(agent.runtimeConfig);
|
triageRuntimeHint ??= extractRuntimeHint(agent.runtimeConfig);
|
||||||
if (agent.instructionsText || agent.instructionsPath || agent.soul || agent.memory) {
|
if (agent.instructionsText || agent.instructionsPath || agent.soul || agent.memory) {
|
||||||
triageInstructions = await resolveAgentInstructions(agent, this.rootDir);
|
const memoryMode = resolveAgentMemoryInclusionMode({ agent, projectSettings: settings }).mode;
|
||||||
|
triageInstructions = await resolveAgentInstructions(agent, this.rootDir, undefined, memoryMode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user