feat: Recompile system prompt after memory subagents finish (#1310)
This commit is contained in:
108
src/cli/App.tsx
108
src/cli/App.tsx
@@ -232,6 +232,7 @@ import {
|
||||
type ReflectionSettings,
|
||||
reflectionSettingsToLegacyMode,
|
||||
} from "./helpers/memoryReminder";
|
||||
import { handleMemorySubagentCompletion } from "./helpers/memorySubagentCompletion";
|
||||
import {
|
||||
type QueuedMessage,
|
||||
setMessageQueueAdder,
|
||||
@@ -1746,6 +1747,10 @@ export default function App({
|
||||
const initProgressByAgentRef = useRef(
|
||||
new Map<string, { shallowCompleted: boolean; deepFired: boolean }>(),
|
||||
);
|
||||
const systemPromptRecompileByAgentRef = useRef(
|
||||
new Map<string, Promise<void>>(),
|
||||
);
|
||||
const queuedSystemPromptRecompileByAgentRef = useRef(new Set<string>());
|
||||
const updateInitProgress = (
|
||||
forAgentId: string,
|
||||
update: Partial<{ shallowCompleted: boolean; deepFired: boolean }>,
|
||||
@@ -9305,13 +9310,24 @@ export default function App({
|
||||
prompt: initPrompt,
|
||||
description: "Initializing memory",
|
||||
silentCompletion: true,
|
||||
onComplete: ({ success, error }) => {
|
||||
if (success) {
|
||||
updateInitProgress(agentId, { deepFired: true });
|
||||
}
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Memory initialization failed: ${error || "Unknown error"}`;
|
||||
onComplete: async ({ success, error }) => {
|
||||
const msg = await handleMemorySubagentCompletion(
|
||||
{
|
||||
agentId,
|
||||
subagentType: "init",
|
||||
initDepth: "deep",
|
||||
success,
|
||||
error,
|
||||
},
|
||||
{
|
||||
recompileByAgent: systemPromptRecompileByAgentRef.current,
|
||||
recompileQueuedByAgent:
|
||||
queuedSystemPromptRecompileByAgentRef.current,
|
||||
updateInitProgress,
|
||||
logRecompileFailure: (message) =>
|
||||
debugWarn("memory", message),
|
||||
},
|
||||
);
|
||||
appendTaskNotificationEvents([msg]);
|
||||
},
|
||||
});
|
||||
@@ -9479,15 +9495,29 @@ export default function App({
|
||||
// attempt (e.g. another /init subagent in flight) preserves the entry for retry.
|
||||
if (autoInitPendingAgentIdsRef.current.has(agentId) && !isSystemOnly) {
|
||||
try {
|
||||
const fired = await fireAutoInit(agentId, ({ success, error }) => {
|
||||
if (success) {
|
||||
updateInitProgress(agentId, { shallowCompleted: true });
|
||||
}
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Memory initialization failed: ${error || "Unknown error"}`;
|
||||
appendTaskNotificationEvents([msg]);
|
||||
});
|
||||
const fired = await fireAutoInit(
|
||||
agentId,
|
||||
async ({ success, error }) => {
|
||||
const msg = await handleMemorySubagentCompletion(
|
||||
{
|
||||
agentId,
|
||||
subagentType: "init",
|
||||
initDepth: "shallow",
|
||||
success,
|
||||
error,
|
||||
},
|
||||
{
|
||||
recompileByAgent: systemPromptRecompileByAgentRef.current,
|
||||
recompileQueuedByAgent:
|
||||
queuedSystemPromptRecompileByAgentRef.current,
|
||||
updateInitProgress,
|
||||
logRecompileFailure: (message) =>
|
||||
debugWarn("memory", message),
|
||||
},
|
||||
);
|
||||
appendTaskNotificationEvents([msg]);
|
||||
},
|
||||
);
|
||||
if (fired) {
|
||||
autoInitPendingAgentIdsRef.current.delete(agentId);
|
||||
sharedReminderStateRef.current.pendingAutoInitReminder = true;
|
||||
@@ -9596,10 +9626,23 @@ ${SYSTEM_REMINDER_CLOSE}
|
||||
prompt: AUTO_REFLECTION_PROMPT,
|
||||
description: AUTO_REFLECTION_DESCRIPTION,
|
||||
silentCompletion: true,
|
||||
onComplete: ({ success, error }) => {
|
||||
const msg = success
|
||||
? "Reflected on /palace, the halls remember more now."
|
||||
: `Tried to reflect, but got lost in the palace: ${error}`;
|
||||
onComplete: async ({ success, error }) => {
|
||||
const msg = await handleMemorySubagentCompletion(
|
||||
{
|
||||
agentId,
|
||||
subagentType: "reflection",
|
||||
success,
|
||||
error,
|
||||
},
|
||||
{
|
||||
recompileByAgent: systemPromptRecompileByAgentRef.current,
|
||||
recompileQueuedByAgent:
|
||||
queuedSystemPromptRecompileByAgentRef.current,
|
||||
updateInitProgress,
|
||||
logRecompileFailure: (message) =>
|
||||
debugWarn("memory", message),
|
||||
},
|
||||
);
|
||||
appendTaskNotificationEvents([msg]);
|
||||
},
|
||||
});
|
||||
@@ -9638,13 +9681,24 @@ ${SYSTEM_REMINDER_CLOSE}
|
||||
prompt: initPrompt,
|
||||
description: "Deep memory initialization",
|
||||
silentCompletion: true,
|
||||
onComplete: ({ success, error }) => {
|
||||
if (success) {
|
||||
updateInitProgress(agentId, { deepFired: true });
|
||||
}
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Deep memory initialization failed: ${error || "Unknown error"}`;
|
||||
onComplete: async ({ success, error }) => {
|
||||
const msg = await handleMemorySubagentCompletion(
|
||||
{
|
||||
agentId,
|
||||
subagentType: "init",
|
||||
initDepth: "deep",
|
||||
success,
|
||||
error,
|
||||
},
|
||||
{
|
||||
recompileByAgent: systemPromptRecompileByAgentRef.current,
|
||||
recompileQueuedByAgent:
|
||||
queuedSystemPromptRecompileByAgentRef.current,
|
||||
updateInitProgress,
|
||||
logRecompileFailure: (message) =>
|
||||
debugWarn("memory", message),
|
||||
},
|
||||
);
|
||||
appendTaskNotificationEvents([msg]);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -139,7 +139,10 @@ Instructions:
|
||||
*/
|
||||
export async function fireAutoInit(
|
||||
agentId: string,
|
||||
onComplete: (result: { success: boolean; error?: string }) => void,
|
||||
onComplete: (result: {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}) => void | Promise<void>,
|
||||
): Promise<boolean> {
|
||||
if (hasActiveInitSubagent()) return false;
|
||||
if (!settingsManager.isMemfsEnabled(agentId)) return false;
|
||||
|
||||
123
src/cli/helpers/memorySubagentCompletion.ts
Normal file
123
src/cli/helpers/memorySubagentCompletion.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
type RecompileAgentSystemPromptOptions,
|
||||
recompileAgentSystemPrompt,
|
||||
} from "../../agent/modify";
|
||||
|
||||
export type MemorySubagentType = "init" | "reflection";
|
||||
export type MemoryInitDepth = "shallow" | "deep";
|
||||
|
||||
export interface MemoryInitProgressUpdate {
|
||||
shallowCompleted: boolean;
|
||||
deepFired: boolean;
|
||||
}
|
||||
|
||||
type RecompileAgentSystemPromptFn = (
|
||||
agentId: string,
|
||||
options?: RecompileAgentSystemPromptOptions,
|
||||
) => Promise<string>;
|
||||
|
||||
export type MemorySubagentCompletionArgs =
|
||||
| {
|
||||
agentId: string;
|
||||
subagentType: "init";
|
||||
initDepth: MemoryInitDepth;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
| {
|
||||
agentId: string;
|
||||
subagentType: "reflection";
|
||||
initDepth?: never;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export interface MemorySubagentCompletionDeps {
|
||||
recompileByAgent: Map<string, Promise<void>>;
|
||||
recompileQueuedByAgent: Set<string>;
|
||||
updateInitProgress: (
|
||||
agentId: string,
|
||||
update: Partial<MemoryInitProgressUpdate>,
|
||||
) => void;
|
||||
logRecompileFailure?: (message: string) => void;
|
||||
recompileAgentSystemPromptImpl?: RecompileAgentSystemPromptFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize a memory-writing subagent by updating init progress, recompiling the
|
||||
* parent agent's system prompt, and returning the user-facing completion text.
|
||||
*/
|
||||
export async function handleMemorySubagentCompletion(
|
||||
args: MemorySubagentCompletionArgs,
|
||||
deps: MemorySubagentCompletionDeps,
|
||||
): Promise<string> {
|
||||
const { agentId, subagentType, initDepth, success, error } = args;
|
||||
const recompileAgentSystemPromptFn =
|
||||
deps.recompileAgentSystemPromptImpl ?? recompileAgentSystemPrompt;
|
||||
let recompileError: string | null = null;
|
||||
|
||||
if (success) {
|
||||
if (subagentType === "init") {
|
||||
deps.updateInitProgress(
|
||||
agentId,
|
||||
initDepth === "shallow"
|
||||
? { shallowCompleted: true }
|
||||
: { deepFired: true },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
let inFlight = deps.recompileByAgent.get(agentId);
|
||||
|
||||
if (!inFlight) {
|
||||
inFlight = (async () => {
|
||||
do {
|
||||
deps.recompileQueuedByAgent.delete(agentId);
|
||||
await recompileAgentSystemPromptFn(agentId, {
|
||||
updateTimestamp: true,
|
||||
});
|
||||
} while (deps.recompileQueuedByAgent.has(agentId));
|
||||
})().finally(() => {
|
||||
// Cleanup runs only after the shared promise settles, so every
|
||||
// concurrent caller awaits the same full recompile lifecycle.
|
||||
deps.recompileQueuedByAgent.delete(agentId);
|
||||
deps.recompileByAgent.delete(agentId);
|
||||
});
|
||||
deps.recompileByAgent.set(agentId, inFlight);
|
||||
} else {
|
||||
deps.recompileQueuedByAgent.add(agentId);
|
||||
}
|
||||
|
||||
await inFlight;
|
||||
} catch (recompileFailure) {
|
||||
recompileError =
|
||||
recompileFailure instanceof Error
|
||||
? recompileFailure.message
|
||||
: String(recompileFailure);
|
||||
deps.logRecompileFailure?.(
|
||||
`Failed to recompile system prompt after ${subagentType} subagent for ${agentId}: ${recompileError}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
const normalizedError = error || "Unknown error";
|
||||
if (subagentType === "reflection") {
|
||||
return `Tried to reflect, but got lost in the palace: ${normalizedError}`;
|
||||
}
|
||||
return initDepth === "deep"
|
||||
? `Deep memory initialization failed: ${normalizedError}`
|
||||
: `Memory initialization failed: ${normalizedError}`;
|
||||
}
|
||||
|
||||
const baseMessage =
|
||||
subagentType === "reflection"
|
||||
? "Reflected on /palace, the halls remember more now."
|
||||
: "Built a memory palace of you. Visit it with /palace.";
|
||||
|
||||
if (!recompileError) {
|
||||
return baseMessage;
|
||||
}
|
||||
|
||||
return `${baseMessage} System prompt recompilation failed: ${recompileError}`;
|
||||
}
|
||||
Reference in New Issue
Block a user