Procházet zdrojové kódy

fix: compact resumable session reminders

dhaern před 3 měsíci
rodič
revize
ff1d64d13a

+ 1 - 1
src/agents/oracle.ts

@@ -20,7 +20,7 @@ const ORACLE_PROMPT = `You are Oracle - a strategic technical advisor and code r
 
 
 **Constraints**:
 **Constraints**:
 - READ-ONLY: You advise, you don't implement
 - READ-ONLY: You advise, you don't implement
-- Focus on strategy, not execution
+- Focus on strategy, not execution; stay within the requested scope and directly related context
 - Point to specific files/lines when relevant
 - Point to specific files/lines when relevant
 `;
 `;
 
 

+ 17 - 12
src/hooks/task-session-manager/index.test.ts

@@ -130,7 +130,7 @@ describe('task-session-manager hook', () => {
     expect(next.args.task_id).toBe('child-1');
     expect(next.args.task_id).toBe('child-1');
   });
   });
 
 
-  test('tracks files read by child sessions in resumable message context', async () => {
+  test('keeps file context out of resumable message context', async () => {
     const { hook } = createHook();
     const { hook } = createHook();
 
 
     await hook.event({
     await hook.event({
@@ -190,12 +190,11 @@ describe('task-session-manager hook', () => {
 
 
     const userMessage = messages.messages[0];
     const userMessage = messages.messages[0];
     expect(userMessage.parts[0].text).toContain('exp-1 session files');
     expect(userMessage.parts[0].text).toContain('exp-1 session files');
-    expect(userMessage.parts[0].text).toContain(
-      'Context read by exp-1: src/index.ts (12 lines)',
-    );
+    expect(userMessage.parts[0].text).not.toContain('Context read by exp-1');
+    expect(userMessage.parts[0].text).not.toContain('src/index.ts');
   });
   });
 
 
-  test('accumulates multiple reads and hides tiny read context', async () => {
+  test('tracks read context internally without rendering file paths', async () => {
     const { hook } = createHook();
     const { hook } = createHook();
 
 
     await hook.event({
     await hook.event({
@@ -255,8 +254,10 @@ describe('task-session-manager hook', () => {
     await hook['experimental.chat.messages.transform']({}, messages);
     await hook['experimental.chat.messages.transform']({}, messages);
 
 
     const prompt = messages.messages[0].parts[0].text;
     const prompt = messages.messages[0].parts[0].text;
+    expect(prompt).toContain('exp-1 line counts');
     expect(prompt).not.toContain('small.ts');
     expect(prompt).not.toContain('small.ts');
-    expect(prompt).toContain('src/large.ts (12 lines)');
+    expect(prompt).not.toContain('large.ts');
+    expect(prompt).not.toContain('Context read by');
   });
   });
 
 
   test('counts overlapping repeated reads once per unique line', async () => {
   test('counts overlapping repeated reads once per unique line', async () => {
@@ -298,8 +299,9 @@ describe('task-session-manager hook', () => {
     await hook['experimental.chat.messages.transform']({}, messages);
     await hook['experimental.chat.messages.transform']({}, messages);
 
 
     const prompt = messages.messages[0].parts[0].text;
     const prompt = messages.messages[0].parts[0].text;
-    expect(prompt).toContain('src/repeat.ts (12 lines)');
-    expect(prompt).not.toContain('src/repeat.ts (24 lines)');
+    expect(prompt).toContain('exp-1 repeat reads');
+    expect(prompt).not.toContain('src/repeat.ts');
+    expect(prompt).not.toContain('Context read by');
   });
   });
 
 
   test('uses configured read context thresholds', async () => {
   test('uses configured read context thresholds', async () => {
@@ -348,9 +350,12 @@ describe('task-session-manager hook', () => {
     await hook['experimental.chat.messages.transform']({}, messages);
     await hook['experimental.chat.messages.transform']({}, messages);
 
 
     const prompt = messages.messages[0].parts[0].text;
     const prompt = messages.messages[0].parts[0].text;
+    expect(prompt).toContain('exp-1 configured caps');
     expect(prompt).not.toContain('small.ts');
     expect(prompt).not.toContain('small.ts');
-    expect(prompt).toContain('Context read by exp-1:');
-    expect(prompt).toContain('(+1 more)');
+    expect(prompt).not.toContain('medium.ts');
+    expect(prompt).not.toContain('large.ts');
+    expect(prompt).not.toContain('Context read by exp-1:');
+    expect(prompt).not.toContain('(+1 more)');
   });
   });
 
 
   test('ignores reads from unmanaged child sessions', async () => {
   test('ignores reads from unmanaged child sessions', async () => {
@@ -438,9 +443,9 @@ describe('task-session-manager hook', () => {
     expect(prompt).not.toContain('exp-1 thread 1');
     expect(prompt).not.toContain('exp-1 thread 1');
     expect(prompt).not.toContain('file-1.ts');
     expect(prompt).not.toContain('file-1.ts');
     expect(prompt).toContain('exp-2 thread 2');
     expect(prompt).toContain('exp-2 thread 2');
-    expect(prompt).toContain('file-2.ts (12 lines)');
+    expect(prompt).not.toContain('file-2.ts');
     expect(prompt).toContain('exp-3 thread 3');
     expect(prompt).toContain('exp-3 thread 3');
-    expect(prompt).toContain('file-3.ts (12 lines)');
+    expect(prompt).not.toContain('file-3.ts');
   });
   });
 
 
   test('drops stale remembered sessions and falls back to fresh', async () => {
   test('drops stale remembered sessions and falls back to fresh', async () => {

+ 17 - 14
src/utils/session-manager.test.ts

@@ -46,7 +46,7 @@ describe('SessionManager', () => {
     expect(manager.formatForPrompt('parent-1')).toBeUndefined();
     expect(manager.formatForPrompt('parent-1')).toBeUndefined();
   });
   });
 
 
-  test('includes read context for remembered sessions', () => {
+  test('omits read context from resumable prompt', () => {
     const manager = new SessionManager(2);
     const manager = new SessionManager(2);
 
 
     manager.remember({
     manager.remember({
@@ -66,12 +66,12 @@ describe('SessionManager', () => {
 
 
     const prompt = manager.formatForPrompt('parent-1');
     const prompt = manager.formatForPrompt('parent-1');
     expect(prompt).toContain('exp-1 session manager');
     expect(prompt).toContain('exp-1 session manager');
-    expect(prompt).toContain(
-      'Context read by exp-1: src/multiplexer/session-manager.ts (24 lines), src/index.ts (42 lines)',
-    );
+    expect(prompt).not.toContain('Context read by exp-1');
+    expect(prompt).not.toContain('src/multiplexer/session-manager.ts');
+    expect(prompt).not.toContain('src/index.ts');
   });
   });
 
 
-  test('filters tiny reads and caps read context files', () => {
+  test('does not render tracked read context file paths', () => {
     const manager = new SessionManager(2);
     const manager = new SessionManager(2);
 
 
     manager.remember({
     manager.remember({
@@ -90,12 +90,13 @@ describe('SessionManager', () => {
     );
     );
 
 
     const prompt = manager.formatForPrompt('parent-1') ?? '';
     const prompt = manager.formatForPrompt('parent-1') ?? '';
+    expect(prompt).toContain('exp-1 large context');
     expect(prompt).not.toContain('file-0.ts');
     expect(prompt).not.toContain('file-0.ts');
-    expect(prompt).toContain('file-9.ts (29 lines)');
-    expect(prompt).toContain('(+1 more)');
+    expect(prompt).not.toContain('file-9.ts');
+    expect(prompt).not.toContain('(+1 more)');
   });
   });
 
 
-  test('uses configurable read context thresholds', () => {
+  test('keeps configurable read context thresholds internal', () => {
     const manager = new SessionManager(2, {
     const manager = new SessionManager(2, {
       readContextMinLines: 5,
       readContextMinLines: 5,
       readContextMaxFiles: 1,
       readContextMaxFiles: 1,
@@ -114,13 +115,14 @@ describe('SessionManager', () => {
     ]);
     ]);
 
 
     const prompt = manager.formatForPrompt('parent-1') ?? '';
     const prompt = manager.formatForPrompt('parent-1') ?? '';
+    expect(prompt).toContain('exp-1 custom thresholds');
     expect(prompt).not.toContain('small.ts');
     expect(prompt).not.toContain('small.ts');
-    expect(prompt).toContain('large.ts (12 lines)');
+    expect(prompt).not.toContain('large.ts');
     expect(prompt).not.toContain('medium.ts');
     expect(prompt).not.toContain('medium.ts');
-    expect(prompt).toContain('(+1 more)');
+    expect(prompt).not.toContain('(+1 more)');
   });
   });
 
 
-  test('bounds stored read context files to the render cap plus overflow marker', () => {
+  test('bounds stored read context files without rendering them', () => {
     const manager = new SessionManager(2, {
     const manager = new SessionManager(2, {
       readContextMinLines: 1,
       readContextMinLines: 1,
       readContextMaxFiles: 2,
       readContextMaxFiles: 2,
@@ -143,9 +145,10 @@ describe('SessionManager', () => {
 
 
     expect(remembered.contextFiles).toHaveLength(3);
     expect(remembered.contextFiles).toHaveLength(3);
     const prompt = manager.formatForPrompt('parent-1') ?? '';
     const prompt = manager.formatForPrompt('parent-1') ?? '';
-    expect(prompt).toContain('file-9.ts (10 lines)');
-    expect(prompt).toContain('file-8.ts (10 lines)');
-    expect(prompt).toContain('(+1 more)');
+    expect(prompt).toContain('exp-1 bounded context');
+    expect(prompt).not.toContain('file-9.ts');
+    expect(prompt).not.toContain('file-8.ts');
+    expect(prompt).not.toContain('(+1 more)');
     expect(prompt).not.toContain('file-0.ts');
     expect(prompt).not.toContain('file-0.ts');
   });
   });
 });
 });

+ 2 - 34
src/utils/session-manager.ts

@@ -230,28 +230,11 @@ export class SessionManager {
       )
       )
       .filter(([, entries]) => entries.length > 0)
       .filter(([, entries]) => entries.length > 0)
       .sort((a, b) => b[1][0].lastUsedAt - a[1][0].lastUsedAt)
       .sort((a, b) => b[1][0].lastUsedAt - a[1][0].lastUsedAt)
-      .map(([agentType, entries]) =>
-        [
+      .map(
+        ([agentType, entries]) =>
           `- ${agentType}: ${entries
           `- ${agentType}: ${entries
             .map((entry) => `${entry.alias} ${entry.label}`)
             .map((entry) => `${entry.alias} ${entry.label}`)
             .join('; ')}`,
             .join('; ')}`,
-          ...entries
-            .map(
-              (entry) =>
-                [
-                  entry,
-                  formatContextFiles(entry.contextFiles, {
-                    minLines: this.readContextMinLines,
-                    maxFiles: this.readContextMaxFiles,
-                  }),
-                ] as const,
-            )
-            .filter(([, context]) => context.length > 0)
-            .map(
-              ([entry, context]) =>
-                `  Context read by ${entry.alias}: ${context}`,
-            ),
-        ].join('\n'),
       );
       );
 
 
     if (lines.length === 0) return undefined;
     if (lines.length === 0) return undefined;
@@ -340,18 +323,3 @@ export class SessionManager {
     return this.orderCounter;
     return this.orderCounter;
   }
   }
 }
 }
-
-function formatContextFiles(
-  files: ContextFile[],
-  options: { minLines: number; maxFiles: number },
-): string {
-  const eligible = files
-    .filter((file) => file.lineCount >= options.minLines)
-    .sort((a, b) => b.lastReadAt - a.lastReadAt);
-  const shown = eligible.slice(0, options.maxFiles);
-  const rest = eligible.length - shown.length;
-  const rendered = shown.map(
-    (file) => `${file.path} (${file.lineCount} lines)`,
-  );
-  return `${rendered.join(', ')}${rest > 0 ? ` (+${rest} more)` : ''}`;
-}