grov 0.5.9 → 0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,6 +69,7 @@ export function initDatabase() {
69
69
  user TEXT,
70
70
  original_query TEXT NOT NULL,
71
71
  goal TEXT,
72
+ summary TEXT,
72
73
  reasoning_trace JSON DEFAULT '[]',
73
74
  files_touched JSON DEFAULT '[]',
74
75
  decisions JSON DEFAULT '[]',
@@ -110,6 +111,14 @@ export function initDatabase() {
110
111
  db.exec(`ALTER TABLE tasks ADD COLUMN sync_error TEXT`);
111
112
  }
112
113
  catch { /* column exists */ }
114
+ try {
115
+ db.exec(`ALTER TABLE tasks ADD COLUMN summary TEXT`);
116
+ }
117
+ catch { /* column exists */ }
118
+ try {
119
+ db.exec(`ALTER TABLE tasks ADD COLUMN system_name TEXT`);
120
+ }
121
+ catch { /* column exists */ }
113
122
  // Create session_states table (temporary per-session tracking)
114
123
  db.exec(`
115
124
  CREATE TABLE IF NOT EXISTS session_states (
@@ -117,6 +126,7 @@ export function initDatabase() {
117
126
  user_id TEXT,
118
127
  project_path TEXT NOT NULL,
119
128
  original_goal TEXT,
129
+ raw_user_prompt TEXT,
120
130
  expected_scope JSON DEFAULT '[]',
121
131
  constraints JSON DEFAULT '[]',
122
132
  keywords JSON DEFAULT '[]',
@@ -197,6 +207,10 @@ export function initDatabase() {
197
207
  db.exec(`CREATE INDEX IF NOT EXISTS idx_session_completed ON session_states(completed_at)`);
198
208
  }
199
209
  catch { /* index exists */ }
210
+ try {
211
+ db.exec(`ALTER TABLE session_states ADD COLUMN raw_user_prompt TEXT`);
212
+ }
213
+ catch { /* column exists */ }
200
214
  // Create file_reasoning table (file-level reasoning with anchoring)
201
215
  db.exec(`
202
216
  CREATE TABLE IF NOT EXISTS file_reasoning (
@@ -1,6 +1,6 @@
1
1
  export type { TaskStatus, TriggerReason, SessionStatus, SessionMode, TaskType, StepActionType, DriftType, CorrectionLevel, Task, CreateTaskInput, RecoveryPlan, DriftEvent, SessionState, CreateSessionStateInput, StepRecord, CreateStepInput, DriftLogEntry, CreateDriftLogInput, } from './types.js';
2
2
  export { initDatabase, closeDatabase, getDatabasePath } from './database.js';
3
- export { createTask, getTasksForProject, getTaskCount, getUnsyncedTasks, markTaskSynced, setTaskSyncError } from './tasks.js';
3
+ export { createTask, getTasksForProject, getTaskCount, getUnsyncedTasks, markTaskSynced, setTaskSyncError, getSyncedTaskCount, cleanupOldSyncedTasks } from './tasks.js';
4
4
  export { createSessionState, getSessionState, updateSessionState, deleteSessionState, getActiveSessionForUser, getActiveSessionsForStatus, getCompletedSessionForProject } from './sessions.js';
5
5
  export { createStep, getStepsForSession, getRecentSteps, getValidatedSteps, getKeyDecisions, getEditedFiles, deleteStepsForSession, updateRecentStepsReasoning, updateLastChecked } from './steps.js';
6
6
  export { updateSessionDrift, logDriftEvent } from './drift.js';
@@ -2,7 +2,7 @@
2
2
  // Re-export database functions
3
3
  export { initDatabase, closeDatabase, getDatabasePath } from './database.js';
4
4
  // Re-export task functions
5
- export { createTask, getTasksForProject, getTaskCount, getUnsyncedTasks, markTaskSynced, setTaskSyncError } from './tasks.js';
5
+ export { createTask, getTasksForProject, getTaskCount, getUnsyncedTasks, markTaskSynced, setTaskSyncError, getSyncedTaskCount, cleanupOldSyncedTasks } from './tasks.js';
6
6
  // Re-export session functions
7
7
  export { createSessionState, getSessionState, updateSessionState, deleteSessionState, getActiveSessionForUser, getActiveSessionsForStatus, getCompletedSessionForProject } from './sessions.js';
8
8
  // Re-export step functions
@@ -10,6 +10,7 @@ function rowToSessionState(row) {
10
10
  user_id: row.user_id,
11
11
  project_path: row.project_path,
12
12
  original_goal: row.original_goal,
13
+ raw_user_prompt: row.raw_user_prompt,
13
14
  expected_scope: safeJsonParse(row.expected_scope, []),
14
15
  constraints: safeJsonParse(row.constraints, []),
15
16
  keywords: safeJsonParse(row.keywords, []),
@@ -46,6 +47,8 @@ function rowToSessionState(row) {
46
47
  * Uses INSERT OR IGNORE to handle race conditions safely.
47
48
  */
48
49
  export function createSessionState(input) {
50
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
51
+ // console.log(`[DEBUG-DB] createSessionState called with raw_user_prompt="${input.raw_user_prompt?.substring(0, 50)}"`);
49
52
  const database = getDb();
50
53
  const now = new Date().toISOString();
51
54
  const sessionState = {
@@ -54,6 +57,7 @@ export function createSessionState(input) {
54
57
  user_id: input.user_id,
55
58
  project_path: input.project_path,
56
59
  original_goal: input.original_goal,
60
+ raw_user_prompt: input.raw_user_prompt,
57
61
  expected_scope: input.expected_scope || [],
58
62
  constraints: input.constraints || [],
59
63
  keywords: input.keywords || [],
@@ -82,7 +86,7 @@ export function createSessionState(input) {
82
86
  };
83
87
  const stmt = database.prepare(`
84
88
  INSERT OR IGNORE INTO session_states (
85
- session_id, user_id, project_path, original_goal,
89
+ session_id, user_id, project_path, original_goal, raw_user_prompt,
86
90
  expected_scope, constraints, keywords,
87
91
  token_count, escalation_count, session_mode,
88
92
  waiting_for_recovery, last_checked_at, last_clear_at,
@@ -90,9 +94,11 @@ export function createSessionState(input) {
90
94
  parent_session_id, task_type,
91
95
  success_criteria, last_drift_score, pending_recovery_plan, drift_history,
92
96
  completed_at
93
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
97
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
94
98
  `);
95
- stmt.run(sessionState.session_id, sessionState.user_id || null, sessionState.project_path, sessionState.original_goal || null, JSON.stringify(sessionState.expected_scope), JSON.stringify(sessionState.constraints), JSON.stringify(sessionState.keywords), sessionState.token_count, sessionState.escalation_count, sessionState.session_mode, sessionState.waiting_for_recovery ? 1 : 0, sessionState.last_checked_at, sessionState.last_clear_at || null, sessionState.start_time, sessionState.last_update, sessionState.status, sessionState.parent_session_id || null, sessionState.task_type, JSON.stringify(sessionState.success_criteria || []), sessionState.last_drift_score || null, sessionState.pending_recovery_plan ? JSON.stringify(sessionState.pending_recovery_plan) : null, JSON.stringify(sessionState.drift_history || []), sessionState.completed_at || null);
99
+ stmt.run(sessionState.session_id, sessionState.user_id || null, sessionState.project_path, sessionState.original_goal || null, sessionState.raw_user_prompt || null, JSON.stringify(sessionState.expected_scope), JSON.stringify(sessionState.constraints), JSON.stringify(sessionState.keywords), sessionState.token_count, sessionState.escalation_count, sessionState.session_mode, sessionState.waiting_for_recovery ? 1 : 0, sessionState.last_checked_at, sessionState.last_clear_at || null, sessionState.start_time, sessionState.last_update, sessionState.status, sessionState.parent_session_id || null, sessionState.task_type, JSON.stringify(sessionState.success_criteria || []), sessionState.last_drift_score || null, sessionState.pending_recovery_plan ? JSON.stringify(sessionState.pending_recovery_plan) : null, JSON.stringify(sessionState.drift_history || []), sessionState.completed_at || null);
100
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
101
+ // console.log(`[DEBUG-DB] INSERT done. sessionState.raw_user_prompt="${sessionState.raw_user_prompt?.substring(0, 50)}"`);
96
102
  return sessionState;
97
103
  }
98
104
  /**
@@ -31,3 +31,15 @@ export declare function markTaskSynced(id: string): void;
31
31
  * Record a sync error for a task
32
32
  */
33
33
  export declare function setTaskSyncError(id: string, error: string): void;
34
+ /**
35
+ * Get count of synced tasks
36
+ */
37
+ export declare function getSyncedTaskCount(): number;
38
+ /**
39
+ * Delete oldest synced tasks to maintain storage limit
40
+ * Called after successful sync to prevent local DB bloat
41
+ *
42
+ * @param maxSyncedTasks - Maximum number of synced tasks to keep (default 500)
43
+ * @returns Number of tasks deleted
44
+ */
45
+ export declare function cleanupOldSyncedTasks(maxSyncedTasks?: number): number;
@@ -11,6 +11,8 @@ function rowToTask(row) {
11
11
  user: row.user,
12
12
  original_query: row.original_query,
13
13
  goal: row.goal,
14
+ system_name: row.system_name,
15
+ summary: row.summary,
14
16
  reasoning_trace: safeJsonParse(row.reasoning_trace, []),
15
17
  files_touched: safeJsonParse(row.files_touched, []),
16
18
  decisions: safeJsonParse(row.decisions, []),
@@ -37,6 +39,8 @@ export function createTask(input) {
37
39
  user: input.user,
38
40
  original_query: input.original_query,
39
41
  goal: input.goal,
42
+ system_name: input.system_name, // Parent system anchor for semantic search
43
+ summary: input.summary,
40
44
  reasoning_trace: input.reasoning_trace || [],
41
45
  files_touched: input.files_touched || [],
42
46
  decisions: input.decisions || [],
@@ -53,18 +57,18 @@ export function createTask(input) {
53
57
  };
54
58
  const stmt = database.prepare(`
55
59
  INSERT INTO tasks (
56
- id, project_path, user, original_query, goal,
60
+ id, project_path, user, original_query, goal, system_name, summary,
57
61
  reasoning_trace, files_touched, decisions, constraints,
58
62
  status, trigger_reason, linked_commit,
59
63
  parent_task_id, turn_number, tags, created_at, synced_at, sync_error
60
64
  ) VALUES (
61
- ?, ?, ?, ?, ?,
65
+ ?, ?, ?, ?, ?, ?, ?,
62
66
  ?, ?, ?, ?,
63
67
  ?, ?, ?,
64
68
  ?, ?, ?, ?, ?, ?
65
69
  )
66
70
  `);
67
- stmt.run(task.id, task.project_path, task.user || null, task.original_query, task.goal || null, JSON.stringify(task.reasoning_trace), JSON.stringify(task.files_touched), JSON.stringify(task.decisions), JSON.stringify(task.constraints), task.status, task.trigger_reason || null, task.linked_commit || null, task.parent_task_id || null, task.turn_number || null, JSON.stringify(task.tags), task.created_at, task.synced_at, task.sync_error);
71
+ stmt.run(task.id, task.project_path, task.user || null, task.original_query, task.goal || null, task.system_name || null, task.summary || null, JSON.stringify(task.reasoning_trace), JSON.stringify(task.files_touched), JSON.stringify(task.decisions), JSON.stringify(task.constraints), task.status, task.trigger_reason || null, task.linked_commit || null, task.parent_task_id || null, task.turn_number || null, JSON.stringify(task.tags), task.created_at, task.synced_at, task.sync_error);
68
72
  return task;
69
73
  }
70
74
  /**
@@ -131,3 +135,39 @@ export function setTaskSyncError(id, error) {
131
135
  const database = getDb();
132
136
  database.prepare('UPDATE tasks SET sync_error = ? WHERE id = ?').run(error, id);
133
137
  }
138
+ /**
139
+ * Get count of synced tasks
140
+ */
141
+ export function getSyncedTaskCount() {
142
+ const database = getDb();
143
+ const stmt = database.prepare('SELECT COUNT(*) as count FROM tasks WHERE synced_at IS NOT NULL');
144
+ const row = stmt.get();
145
+ return row?.count ?? 0;
146
+ }
147
+ /**
148
+ * Delete oldest synced tasks to maintain storage limit
149
+ * Called after successful sync to prevent local DB bloat
150
+ *
151
+ * @param maxSyncedTasks - Maximum number of synced tasks to keep (default 500)
152
+ * @returns Number of tasks deleted
153
+ */
154
+ export function cleanupOldSyncedTasks(maxSyncedTasks = 500) {
155
+ const database = getDb();
156
+ const currentCount = getSyncedTaskCount();
157
+ if (currentCount <= maxSyncedTasks) {
158
+ return 0;
159
+ }
160
+ const toDelete = currentCount - maxSyncedTasks;
161
+ // Delete oldest synced tasks (by synced_at, not created_at)
162
+ const stmt = database.prepare(`
163
+ DELETE FROM tasks
164
+ WHERE id IN (
165
+ SELECT id FROM tasks
166
+ WHERE synced_at IS NOT NULL
167
+ ORDER BY synced_at ASC
168
+ LIMIT ?
169
+ )
170
+ `);
171
+ const result = stmt.run(toDelete);
172
+ return result.changes;
173
+ }
@@ -1,3 +1,4 @@
1
+ import type { ReasoningTraceEntry } from '@grov/shared';
1
2
  export type TaskStatus = 'complete' | 'question' | 'partial' | 'abandoned';
2
3
  export type TriggerReason = 'complete' | 'threshold' | 'abandoned';
3
4
  export interface Task {
@@ -6,9 +7,13 @@ export interface Task {
6
7
  user?: string;
7
8
  original_query: string;
8
9
  goal?: string;
9
- reasoning_trace: string[];
10
+ system_name?: string;
11
+ summary?: string;
12
+ reasoning_trace: ReasoningTraceEntry[];
10
13
  files_touched: string[];
11
14
  decisions: Array<{
15
+ aspect?: string;
16
+ tags?: string;
12
17
  choice: string;
13
18
  reason: string;
14
19
  }>;
@@ -28,9 +33,13 @@ export interface CreateTaskInput {
28
33
  user?: string;
29
34
  original_query: string;
30
35
  goal?: string;
31
- reasoning_trace?: string[];
36
+ system_name?: string;
37
+ summary?: string;
38
+ reasoning_trace?: ReasoningTraceEntry[];
32
39
  files_touched?: string[];
33
40
  decisions?: Array<{
41
+ aspect?: string;
42
+ tags?: string;
34
43
  choice: string;
35
44
  reason: string;
36
45
  }>;
@@ -62,6 +71,7 @@ interface SessionStateBase {
62
71
  user_id?: string;
63
72
  project_path: string;
64
73
  original_goal?: string;
74
+ raw_user_prompt?: string;
65
75
  expected_scope: string[];
66
76
  constraints: string[];
67
77
  keywords: string[];
@@ -102,6 +112,7 @@ export interface CreateSessionStateInput {
102
112
  user_id?: string;
103
113
  project_path: string;
104
114
  original_goal?: string;
115
+ raw_user_prompt?: string;
105
116
  expected_scope?: string[];
106
117
  constraints?: string[];
107
118
  keywords?: string[];
@@ -1,2 +1 @@
1
- // Store types and interfaces
2
1
  export {};
@@ -52,10 +52,6 @@ export declare function extractTokenUsage(response: AnthropicResponse): {
52
52
  cacheCreation: number;
53
53
  cacheRead: number;
54
54
  };
55
- /**
56
- * Check if response contains any file-modifying actions
57
- */
58
- export declare function hasModifyingActions(actions: ParsedAction[]): boolean;
59
55
  /**
60
56
  * Get all unique files from actions
61
57
  */
@@ -143,35 +143,6 @@ export function extractTokenUsage(response) {
143
143
  cacheRead: response.usage.cache_read_input_tokens || 0,
144
144
  };
145
145
  }
146
- /**
147
- * Check if response contains any file-modifying actions
148
- */
149
- export function hasModifyingActions(actions) {
150
- return actions.some(a => a.actionType === 'edit' ||
151
- a.actionType === 'write' ||
152
- (a.actionType === 'bash' && a.command && isModifyingBashCommand(a.command)));
153
- }
154
- /**
155
- * Check if bash command modifies files
156
- */
157
- function isModifyingBashCommand(command) {
158
- const modifyingPatterns = [
159
- /\brm\b/,
160
- /\bmv\b/,
161
- /\bcp\b/,
162
- /\bmkdir\b/,
163
- /\btouch\b/,
164
- /\bchmod\b/,
165
- /\bchown\b/,
166
- /\bsed\b.*-i/,
167
- /\btee\b/,
168
- />/, // redirect
169
- /\bgit\s+(add|commit|push|checkout|reset)/,
170
- /\bnpm\s+(install|uninstall)/,
171
- /\byarn\s+(add|remove)/,
172
- ];
173
- return modifyingPatterns.some(p => p.test(command));
174
- }
175
146
  /**
176
147
  * Get all unique files from actions
177
148
  */
@@ -30,7 +30,3 @@ export declare function getTeamMemoryCache(projectPath: string): string | null;
30
30
  * Check if cache exists for a specific project
31
31
  */
32
32
  export declare function hasCacheForProject(projectPath: string): boolean;
33
- /**
34
- * Get current cache project path (for logging/debugging)
35
- */
36
- export declare function getCacheProjectPath(): string | null;
@@ -15,7 +15,8 @@ export let globalTeamMemoryCache = null;
15
15
  */
16
16
  export function invalidateTeamMemoryCache() {
17
17
  globalTeamMemoryCache = null;
18
- console.log('[CACHE] Team memory cache invalidated');
18
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
19
+ // console.log('[CACHE] Team memory cache invalidated');
19
20
  }
20
21
  /**
21
22
  * Set the global team memory cache
@@ -24,7 +25,8 @@ export function invalidateTeamMemoryCache() {
24
25
  */
25
26
  export function setTeamMemoryCache(projectPath, content) {
26
27
  globalTeamMemoryCache = { projectPath, content };
27
- console.log(`[CACHE] Team memory cache set for project: ${projectPath} (${content.length} chars)`);
28
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
29
+ // console.log(`[CACHE] Team memory cache set for project: ${projectPath} (${content.length} chars)`);
28
30
  }
29
31
  /**
30
32
  * Get the current cache content if it matches the project path
@@ -43,9 +45,3 @@ export function getTeamMemoryCache(projectPath) {
43
45
  export function hasCacheForProject(projectPath) {
44
46
  return globalTeamMemoryCache?.projectPath === projectPath;
45
47
  }
46
- /**
47
- * Get current cache project path (for logging/debugging)
48
- */
49
- export function getCacheProjectPath() {
50
- return globalTeamMemoryCache?.projectPath || null;
51
- }
@@ -23,7 +23,7 @@ export function evictOldestCacheEntry() {
23
23
  }
24
24
  if (oldestId) {
25
25
  extendedCache.delete(oldestId);
26
- log(`Extended cache: evicted ${oldestId.substring(0, 8)} (capacity limit)`);
26
+ // Evicted silently - capacity limit
27
27
  }
28
28
  }
29
29
  async function sendExtendedCacheKeepAlive(projectPath, entry) {
@@ -77,7 +77,7 @@ async function sendExtendedCacheKeepAlive(projectPath, entry) {
77
77
  const keepAliveMsg = messagesIsEmpty
78
78
  ? '{"role":"user","content":"."}'
79
79
  : ',{"role":"user","content":"."}';
80
- log(`Extended cache: SEND keep-alive project=${projectName} msg_array_size=${messagesEnd - messagesStart}`);
80
+ // Sending keep-alive - result logged after response
81
81
  rawBodyStr = rawBodyStr.slice(0, messagesEnd) + keepAliveMsg + rawBodyStr.slice(messagesEnd);
82
82
  // NOTE: We do NOT modify max_tokens or stream!
83
83
  // Keeping them identical preserves the cache prefix for byte-exact matching.
@@ -94,12 +94,10 @@ async function sendExtendedCacheKeepAlive(projectPath, entry) {
94
94
  if (result.statusCode !== 200) {
95
95
  throw new Error(`Keep-alive failed: ${result.statusCode}`);
96
96
  }
97
- // Log cache metrics
97
+ // Log cache metrics (minimal)
98
98
  const usage = result.body.usage;
99
99
  const cacheRead = usage?.cache_read_input_tokens || 0;
100
- const cacheCreate = usage?.cache_creation_input_tokens || 0;
101
- const inputTokens = usage?.input_tokens || 0;
102
- log(`Extended cache: keep-alive for ${projectName} - cache_read=${cacheRead}, cache_create=${cacheCreate}, input=${inputTokens}`);
100
+ console.log(`[CACHE] keep-alive ${projectName}: read=${cacheRead}`);
103
101
  }
104
102
  export async function checkExtendedCache() {
105
103
  const now = Date.now();
@@ -111,7 +109,6 @@ export async function checkExtendedCache() {
111
109
  // Stale cleanup: user left after 10 minutes
112
110
  if (idleTime > EXTENDED_CACHE_MAX_IDLE) {
113
111
  extendedCache.delete(projectPath);
114
- log(`Extended cache: cleared ${projectName} (stale)`);
115
112
  continue;
116
113
  }
117
114
  // Skip if not idle enough yet
@@ -121,7 +118,6 @@ export async function checkExtendedCache() {
121
118
  // Skip if already sent max keep-alives
122
119
  if (entry.keepAliveCount >= EXTENDED_CACHE_MAX_KEEPALIVES) {
123
120
  extendedCache.delete(projectPath);
124
- log(`Extended cache: cleared ${projectName} (max retries)`);
125
121
  continue;
126
122
  }
127
123
  projectsToKeepAlive.push({ projectPath, entry });
@@ -137,14 +133,8 @@ export async function checkExtendedCache() {
137
133
  })
138
134
  .catch((err) => {
139
135
  extendedCache.delete(projectPath);
140
- // Handle both Error instances and ForwardError objects
141
- const errMsg = err instanceof Error
142
- ? err.message
143
- : (err && typeof err === 'object' && 'message' in err)
144
- ? String(err.message)
145
- : JSON.stringify(err);
146
- const errType = err && typeof err === 'object' && 'type' in err ? ` [${err.type}]` : '';
147
- log(`Extended cache: cleared ${projectName} (error${errType}: ${errMsg})`);
136
+ const errMsg = err instanceof Error ? err.message : 'unknown';
137
+ console.error(`[CACHE] keep-alive ${projectName} failed: ${errMsg}`);
148
138
  });
149
139
  keepAlivePromises.push(promise);
150
140
  }
@@ -23,7 +23,8 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
23
23
  // No need to do semantic search or cache operations for these
24
24
  const earlyUserPrompt = extractLastUserPrompt(modified.messages || []);
25
25
  if (earlyUserPrompt === 'Warmup') {
26
- console.log('[INJECT] Skipping warmup request (no search, no cache)');
26
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
27
+ // console.log('[INJECT] Skipping warmup request (no search, no cache)');
27
28
  return modified;
28
29
  }
29
30
  // Detect request type: first, continuation, or retry
@@ -45,24 +46,26 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
45
46
  // === PLANNING CLEAR: Reset after planning task completes ===
46
47
  // This ensures implementation phase starts fresh with planning context from team memory
47
48
  if (pendingPlanClear && pendingPlanClear.projectPath === sessionInfo.projectPath) {
48
- // 1. Empty messages array (fresh start)
49
- modified.messages = [];
50
- // 2. Inject planning summary into system prompt
51
- appendToSystemPrompt(modified, pendingPlanClear.summary);
52
- // 3. Rebuild team memory NOW (includes the just-saved planning task)
49
+ // 1. Extract context BEFORE emptying messages (for hybrid search)
53
50
  const mentionedFiles = extractFilesFromMessages(modified.messages || []);
54
51
  const userPrompt = extractLastUserPrompt(modified.messages || []);
52
+ // 2. Empty messages array (fresh start)
53
+ modified.messages = [];
54
+ // 3. Inject planning summary into system prompt
55
+ appendToSystemPrompt(modified, pendingPlanClear.summary);
56
+ // 4. Rebuild team memory NOW (includes the just-saved planning task)
55
57
  // Use cloud-first approach if sync is enabled
56
58
  let teamContext = null;
57
59
  const teamId = getSyncTeamId();
58
60
  if (isSyncEnabled() && teamId) {
59
- console.log(`[INJECT] PLANNING_CLEAR: Using cloud team memory (teamId=${teamId.substring(0, 8)}...)`);
61
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
62
+ // console.log(`[INJECT] PLANNING_CLEAR: Using cloud team memory (teamId=${teamId.substring(0, 8)}...)`);
60
63
  teamContext = await buildTeamMemoryContextCloud(teamId, sessionInfo.projectPath, mentionedFiles, userPrompt // For hybrid semantic search
61
64
  );
62
65
  }
63
66
  else {
64
67
  // Sync not enabled - no injection (cloud-first approach)
65
- console.log('[INJECT] Sync not enabled. Enable sync for team memory injection.');
68
+ // console.log('[INJECT] Sync not enabled. Enable sync for team memory injection.');
66
69
  teamContext = null;
67
70
  }
68
71
  if (teamContext) {
@@ -81,8 +84,18 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
81
84
  // If token count exceeds threshold AND we have a pre-computed summary, apply CLEAR
82
85
  if (sessionState) {
83
86
  const currentTokenCount = sessionState.token_count || 0;
87
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
88
+ // console.log('[CLEAR-CHECK] ═══════════════════════════════════════════');
89
+ // console.log('[CLEAR-CHECK] currentTokenCount:', currentTokenCount);
90
+ // console.log('[CLEAR-CHECK] threshold:', config.TOKEN_CLEAR_THRESHOLD);
91
+ // console.log('[CLEAR-CHECK] exceedsThreshold:', currentTokenCount > config.TOKEN_CLEAR_THRESHOLD);
92
+ // console.log('[CLEAR-CHECK] hasPendingSummary:', !!sessionState.pending_clear_summary);
93
+ // console.log('[CLEAR-CHECK] summaryLength:', sessionState.pending_clear_summary?.length || 0);
94
+ // console.log('[CLEAR-CHECK] shouldClear:', currentTokenCount > config.TOKEN_CLEAR_THRESHOLD && !!sessionState.pending_clear_summary);
95
+ // console.log('[CLEAR-CHECK] ═══════════════════════════════════════════');
84
96
  if (currentTokenCount > config.TOKEN_CLEAR_THRESHOLD &&
85
97
  sessionState.pending_clear_summary) {
98
+ // console.log('[CLEAR-CHECK] >>> CLEAR MODE TRIGGERED! <<<');
86
99
  logger.info({
87
100
  msg: 'CLEAR MODE ACTIVATED - resetting conversation',
88
101
  tokenCount: currentTokenCount,
@@ -95,6 +108,7 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
95
108
  appendToSystemPrompt(modified, sessionState.pending_clear_summary);
96
109
  // 3. Mark session as cleared
97
110
  markCleared(sessionInfo.sessionId);
111
+ console.log(`[CLEAR] Context reset (${sessionState.pending_clear_summary?.length || 0} chars summary)`);
98
112
  // 4. Clear pending summary and invalidate GLOBAL team memory cache (new baseline)
99
113
  updateSessionState(sessionInfo.sessionId, { pending_clear_summary: undefined });
100
114
  invalidateTeamMemoryCache(); // Force recalculation on next request (CLEAR mode)
@@ -113,7 +127,7 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
113
127
  // Reuse GLOBAL cached team memory (constant for entire conversation)
114
128
  modified.__grovInjection = globalTeamMemoryCache.content;
115
129
  modified.__grovInjectionCached = true;
116
- console.log(`[CACHE] Using global team memory cache, size=${globalTeamMemoryCache.content.length}`);
130
+ // Using cached team memory
117
131
  }
118
132
  else {
119
133
  // First request OR project changed OR cache was invalidated: compute team memory
@@ -123,16 +137,15 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
123
137
  let teamContext = null;
124
138
  const teamId = getSyncTeamId();
125
139
  if (isSyncEnabled() && teamId) {
126
- console.log(`[INJECT] First/cache miss: Using cloud team memory (teamId=${teamId.substring(0, 8)}...)`);
127
140
  teamContext = await buildTeamMemoryContextCloud(teamId, sessionInfo.projectPath, mentionedFiles, userPrompt // For hybrid semantic search
128
141
  );
129
142
  }
130
143
  else {
131
144
  // Sync not enabled - no injection (cloud-first approach)
132
- console.log('[INJECT] Sync not enabled. Enable sync for team memory injection.');
145
+ // console.log('[INJECT] Sync not enabled. Enable sync for team memory injection.');
133
146
  teamContext = null;
134
147
  }
135
- console.log(`[CACHE] Computing team memory (first/new), files=${mentionedFiles.length}, result=${teamContext ? teamContext.length : 'null'}`);
148
+ // console.log(`[CACHE] Computing team memory (first/new), files=${mentionedFiles.length}, result=${teamContext ? teamContext.length : 'null'}`);
136
149
  if (teamContext) {
137
150
  modified.__grovInjection = teamContext;
138
151
  modified.__grovInjectionCached = false;
@@ -141,7 +154,11 @@ export async function preProcessRequest(body, sessionInfo, logger, detectRequest
141
154
  }
142
155
  else {
143
156
  // No team memory available - clear global cache for this project
157
+ // DEBUG: Commented out for cleaner terminal - uncomment when debugging
158
+ // console.log('[CACHE-INVALIDATE] teamContext is NULL - why?');
159
+ // console.log('[CACHE-INVALIDATE] isSameProject:', isSameProject);
144
160
  if (isSameProject) {
161
+ // console.log('[CACHE-INVALIDATE] >>> INVALIDATING CACHE (teamContext null) <<<');
145
162
  invalidateTeamMemoryCache();
146
163
  }
147
164
  }
@@ -62,6 +62,7 @@ export function buildDynamicInjection(sessionId, sessionState, logger) {
62
62
  parts.push(`[DRIFT: ${sessionState.pending_correction}]`);
63
63
  debugInfo.hasDriftCorrection = true;
64
64
  debugInfo.driftCorrectionLength = sessionState.pending_correction.length;
65
+ console.log(`[DRIFT] Correction injected (${sessionState.pending_correction.length} chars)`);
65
66
  }
66
67
  // 4. Add forced recovery if pending
67
68
  if (sessionState?.pending_forced_recovery) {