feat(memory): persist auto-memory scratchpad for skill extraction (#25873)

This commit is contained in:
Sandy Tao
2026-04-24 17:21:12 -07:00
committed by GitHub
parent a5b030b424
commit 42587de733
17 changed files with 2418 additions and 171 deletions
@@ -779,6 +779,8 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
return {
result: finalResult || 'Task completed.',
terminate_reason: terminateReason,
turn_count: turnCounter,
duration_ms: Date.now() - startTime,
};
}
@@ -786,6 +788,8 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
result:
finalResult || 'Agent execution was terminated before completion.',
terminate_reason: terminateReason,
turn_count: turnCounter,
duration_ms: Date.now() - startTime,
};
} catch (error) {
// Check if the error is an AbortError caused by our internal timeout.
@@ -826,6 +830,8 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
return {
result: finalResult,
terminate_reason: terminateReason,
turn_count: turnCounter,
duration_ms: Date.now() - startTime,
};
}
}
@@ -840,6 +846,8 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
return {
result: finalResult,
terminate_reason: terminateReason,
turn_count: turnCounter,
duration_ms: Date.now() - startTime,
};
}
@@ -74,12 +74,14 @@ describe('SkillExtractionAgent', () => {
expect(query).toContain(existingSkillsSummary);
expect(query).toContain(sessionIndex);
expect(query).toContain('optional workflow hint');
expect(query).toContain(
'The summary is a user-intent summary, not a workflow summary.',
'workflow hints alone is never enough evidence for a reusable skill.',
);
expect(query).toContain(
'The session summaries describe user intent, not workflow details.',
'Session summaries describe user intent; optional workflow hints describe likely procedural traces.',
);
expect(query).toContain('Use workflow hints for routing');
expect(query).toContain(
'Only write a skill if the evidence shows a durable, recurring workflow',
);
@@ -303,10 +303,11 @@ export const SkillExtractionAgent = (
'# Session Index',
'',
'Below is an index of past conversation sessions. Each line shows:',
'[NEW] or [old] status, a 1-line summary, message count, and the file path.',
'[NEW] or [old] status, a 1-line user-intent summary, optional workflow hint, message count, and the file path.',
'',
'The summary is a user-intent summary, not a workflow summary.',
'Matching summary text alone is never enough evidence for a reusable skill.',
'Some lines may include "| workflow: ..."; this is a compact workflow hint from session metadata.',
'Use workflow hints to prioritize which sessions to read and to group likely recurring workflows.',
'Matching summary text or workflow hints alone is never enough evidence for a reusable skill.',
'',
'[NEW] = not yet processed for skill extraction (focus on these)',
'[old] = previously processed (read only if a [NEW] session hints at a repeated pattern)',
@@ -326,7 +327,7 @@ export const SkillExtractionAgent = (
return {
systemPrompt: buildSystemPrompt(skillsDir),
query: `${initialContext}\n\nAnalyze the session index above. The session summaries describe user intent, not workflow details. Read sessions that suggest repeated workflows using read_file. Only write a skill if the evidence shows a durable, recurring workflow or a stable recurring repo procedure. If recurrence or future reuse is unclear, create no skill and explain why.`,
query: `${initialContext}\n\nAnalyze the session index above. Session summaries describe user intent; optional workflow hints describe likely procedural traces. Use workflow hints for routing, then read sessions that suggest repeated workflows using read_file to verify recurrence from transcript evidence. Only write a skill if the evidence shows a durable, recurring workflow or a stable recurring repo procedure. If recurrence or future reuse is unclear, create no skill and explain why.`,
};
},
runConfig: {
+2
View File
@@ -36,6 +36,8 @@ export enum AgentTerminateMode {
export interface OutputObject {
result: string;
terminate_reason: AgentTerminateMode;
turn_count?: number;
duration_ms?: number;
}
/**