grov 0.5.2 → 0.5.3
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.
- package/README.md +19 -1
- package/dist/cli.js +8 -0
- package/dist/lib/api-client.d.ts +18 -1
- package/dist/lib/api-client.js +57 -0
- package/dist/lib/llm-extractor.d.ts +14 -38
- package/dist/lib/llm-extractor.js +380 -406
- package/dist/lib/store/convenience.d.ts +40 -0
- package/dist/lib/store/convenience.js +104 -0
- package/dist/lib/store/database.d.ts +22 -0
- package/dist/lib/store/database.js +375 -0
- package/dist/lib/store/drift.d.ts +9 -0
- package/dist/lib/store/drift.js +89 -0
- package/dist/lib/store/index.d.ts +7 -0
- package/dist/lib/store/index.js +13 -0
- package/dist/lib/store/sessions.d.ts +32 -0
- package/dist/lib/store/sessions.js +240 -0
- package/dist/lib/store/steps.d.ts +40 -0
- package/dist/lib/store/steps.js +161 -0
- package/dist/lib/store/tasks.d.ts +33 -0
- package/dist/lib/store/tasks.js +133 -0
- package/dist/lib/store/types.d.ts +167 -0
- package/dist/lib/store/types.js +2 -0
- package/dist/lib/store.d.ts +1 -436
- package/dist/lib/store.js +2 -1478
- package/dist/proxy/cache.d.ts +36 -0
- package/dist/proxy/cache.js +51 -0
- package/dist/proxy/config.d.ts +1 -0
- package/dist/proxy/config.js +2 -0
- package/dist/proxy/extended-cache.d.ts +10 -0
- package/dist/proxy/extended-cache.js +155 -0
- package/dist/proxy/handlers/preprocess.d.ts +20 -0
- package/dist/proxy/handlers/preprocess.js +169 -0
- package/dist/proxy/injection/delta-tracking.d.ts +11 -0
- package/dist/proxy/injection/delta-tracking.js +93 -0
- package/dist/proxy/injection/injectors.d.ts +7 -0
- package/dist/proxy/injection/injectors.js +139 -0
- package/dist/proxy/request-processor.d.ts +18 -4
- package/dist/proxy/request-processor.js +151 -30
- package/dist/proxy/response-processor.js +93 -45
- package/dist/proxy/server.d.ts +0 -1
- package/dist/proxy/server.js +342 -566
- package/dist/proxy/types.d.ts +13 -0
- package/dist/proxy/types.js +2 -0
- package/dist/proxy/utils/extractors.d.ts +18 -0
- package/dist/proxy/utils/extractors.js +109 -0
- package/dist/proxy/utils/logging.d.ts +18 -0
- package/dist/proxy/utils/logging.js +42 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ Every time you start a new Claude Code session:
|
|
|
36
36
|
|
|
37
37
|
Grov captures what Claude learns and injects it back on the next session.
|
|
38
38
|
|
|
39
|
-

|
|
40
40
|
|
|
41
41
|
### What Gets Captured
|
|
42
42
|
|
|
@@ -129,6 +129,24 @@ Grov monitors what Claude **does** (not what you ask) and corrects if it drifts
|
|
|
129
129
|
grov drift-test "refactor the auth system" --goal "fix login bug"
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
+
### Extended Cache (Experimental)
|
|
133
|
+
|
|
134
|
+
Anthropic's prompt cache expires after 5 minutes of inactivity. If you pause to think between prompts, the cache expires and must be recreated (costs more, takes longer).
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
grov proxy --extended-cache
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**What this does:** Sends minimal keep-alive requests (~$0.002 each) during idle periods to preserve your cache.
|
|
141
|
+
|
|
142
|
+
**Important:** By using `--extended-cache`, you consent to Grov making API requests on your behalf to keep the cache active. These requests:
|
|
143
|
+
- Use your Anthropic API key
|
|
144
|
+
- Are sent automatically during idle periods (every ~4 minutes)
|
|
145
|
+
- Cost approximately $0.002 per keep-alive
|
|
146
|
+
- Are discarded (not added to your conversation)
|
|
147
|
+
|
|
148
|
+
This feature is **disabled by default** and requires explicit opt-in.
|
|
149
|
+
|
|
132
150
|
### Environment Variables
|
|
133
151
|
|
|
134
152
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -78,7 +78,15 @@ program
|
|
|
78
78
|
.command('proxy')
|
|
79
79
|
.description('Start the Grov proxy server (intercepts Claude API calls)')
|
|
80
80
|
.option('-d, --debug', 'Enable debug logging to grov-proxy.log')
|
|
81
|
+
.option('--extended-cache', 'Keep Anthropic cache alive during idle (sends requests on your behalf)')
|
|
81
82
|
.action(async (options) => {
|
|
83
|
+
if (options.extendedCache) {
|
|
84
|
+
process.env.GROV_EXTENDED_CACHE = 'true';
|
|
85
|
+
console.log('\n⚠️ Extended Cache Enabled');
|
|
86
|
+
console.log(' By using --extended-cache, you consent to Grov making');
|
|
87
|
+
console.log(' minimal keep-alive requests on your behalf to preserve');
|
|
88
|
+
console.log(' Anthropic\'s prompt cache during idle periods.\n');
|
|
89
|
+
}
|
|
82
90
|
const { startServer } = await import('./proxy/server.js');
|
|
83
91
|
await startServer({ debug: options.debug ?? false });
|
|
84
92
|
});
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Team, MemorySyncRequest, MemorySyncResponse, DeviceFlowStartResponse, DeviceFlowPollResponse } from '@grov/shared';
|
|
1
|
+
import type { Team, Memory, MemorySyncRequest, MemorySyncResponse, DeviceFlowStartResponse, DeviceFlowPollResponse } from '@grov/shared';
|
|
2
2
|
export interface ApiResponse<T> {
|
|
3
3
|
data?: T;
|
|
4
4
|
error?: string;
|
|
@@ -30,6 +30,23 @@ export declare function fetchTeam(teamId: string): Promise<Team>;
|
|
|
30
30
|
* Sync memories to team
|
|
31
31
|
*/
|
|
32
32
|
export declare function syncMemories(teamId: string, request: MemorySyncRequest): Promise<MemorySyncResponse>;
|
|
33
|
+
/**
|
|
34
|
+
* Fetch team memories from cloud (Supabase via API)
|
|
35
|
+
* Cloud equivalent of getTasksForProject() from store.ts
|
|
36
|
+
* Supports hybrid search when context is provided
|
|
37
|
+
*
|
|
38
|
+
* @param teamId - Team UUID
|
|
39
|
+
* @param projectPath - Project path to filter by (exact match)
|
|
40
|
+
* @param options - Optional filters (files, status, limit, context, current_files)
|
|
41
|
+
* @returns Array of memories (empty array on error - fail silent)
|
|
42
|
+
*/
|
|
43
|
+
export declare function fetchTeamMemories(teamId: string, projectPath: string, options?: {
|
|
44
|
+
files?: string[];
|
|
45
|
+
status?: string;
|
|
46
|
+
limit?: number;
|
|
47
|
+
context?: string;
|
|
48
|
+
current_files?: string[];
|
|
49
|
+
}): Promise<Memory[]>;
|
|
33
50
|
/**
|
|
34
51
|
* Sleep helper for polling
|
|
35
52
|
*/
|
package/dist/lib/api-client.js
CHANGED
|
@@ -102,6 +102,63 @@ export async function syncMemories(teamId, request) {
|
|
|
102
102
|
}
|
|
103
103
|
return response.data;
|
|
104
104
|
}
|
|
105
|
+
// Security limits for API params
|
|
106
|
+
const MAX_CONTEXT_LENGTH = 2000; // Max chars for semantic search context
|
|
107
|
+
const MAX_FILES_COUNT = 20; // Max files for boost/filter
|
|
108
|
+
/**
|
|
109
|
+
* Fetch team memories from cloud (Supabase via API)
|
|
110
|
+
* Cloud equivalent of getTasksForProject() from store.ts
|
|
111
|
+
* Supports hybrid search when context is provided
|
|
112
|
+
*
|
|
113
|
+
* @param teamId - Team UUID
|
|
114
|
+
* @param projectPath - Project path to filter by (exact match)
|
|
115
|
+
* @param options - Optional filters (files, status, limit, context, current_files)
|
|
116
|
+
* @returns Array of memories (empty array on error - fail silent)
|
|
117
|
+
*/
|
|
118
|
+
export async function fetchTeamMemories(teamId, projectPath, options) {
|
|
119
|
+
// Build query params
|
|
120
|
+
const params = new URLSearchParams();
|
|
121
|
+
params.set('project_path', projectPath);
|
|
122
|
+
if (options?.status) {
|
|
123
|
+
params.set('status', options.status);
|
|
124
|
+
}
|
|
125
|
+
if (options?.limit) {
|
|
126
|
+
params.set('limit', options.limit.toString());
|
|
127
|
+
}
|
|
128
|
+
if (options?.files && options.files.length > 0) {
|
|
129
|
+
// API expects multiple 'files' params for array
|
|
130
|
+
options.files.slice(0, MAX_FILES_COUNT).forEach(f => params.append('files', f));
|
|
131
|
+
}
|
|
132
|
+
// Hybrid search params (with security limits)
|
|
133
|
+
if (options?.context) {
|
|
134
|
+
params.set('context', options.context.substring(0, MAX_CONTEXT_LENGTH));
|
|
135
|
+
}
|
|
136
|
+
if (options?.current_files && options.current_files.length > 0) {
|
|
137
|
+
// Comma-separated for current_files (boost)
|
|
138
|
+
const files = options.current_files.slice(0, MAX_FILES_COUNT);
|
|
139
|
+
params.set('current_files', files.join(','));
|
|
140
|
+
}
|
|
141
|
+
const url = `/teams/${teamId}/memories?${params.toString()}`;
|
|
142
|
+
console.log(`[API] fetchTeamMemories: GET ${url}`);
|
|
143
|
+
try {
|
|
144
|
+
const response = await apiRequest('GET', url);
|
|
145
|
+
if (response.error) {
|
|
146
|
+
console.warn(`[API] fetchTeamMemories failed: ${response.error} (status: ${response.status})`);
|
|
147
|
+
return []; // Fail silent - don't block Claude Code
|
|
148
|
+
}
|
|
149
|
+
if (!response.data || !response.data.memories) {
|
|
150
|
+
console.log('[API] fetchTeamMemories: No memories returned');
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
console.log(`[API] fetchTeamMemories: Got ${response.data.memories.length} memories`);
|
|
154
|
+
return response.data.memories;
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
158
|
+
console.error(`[API] fetchTeamMemories exception: ${errorMsg}`);
|
|
159
|
+
return []; // Fail silent - don't block Claude Code
|
|
160
|
+
}
|
|
161
|
+
}
|
|
105
162
|
// ============= Utility Functions =============
|
|
106
163
|
/**
|
|
107
164
|
* Sleep helper for polling
|
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { TaskStatus, SessionState, StepRecord } from './store.js';
|
|
3
|
-
export interface ExtractedReasoning {
|
|
4
|
-
task: string;
|
|
5
|
-
goal: string;
|
|
6
|
-
reasoning_trace: string[];
|
|
7
|
-
files_touched: string[];
|
|
8
|
-
decisions: Array<{
|
|
9
|
-
choice: string;
|
|
10
|
-
reason: string;
|
|
11
|
-
}>;
|
|
12
|
-
constraints: string[];
|
|
13
|
-
status: TaskStatus;
|
|
14
|
-
tags: string[];
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Check if LLM extraction is available (OpenAI API key set)
|
|
18
|
-
*/
|
|
19
|
-
export declare function isLLMAvailable(): boolean;
|
|
1
|
+
import type { SessionState, StepRecord } from './store.js';
|
|
20
2
|
export interface ExtractedIntent {
|
|
21
3
|
goal: string;
|
|
22
4
|
expected_scope: string[];
|
|
@@ -34,22 +16,6 @@ export declare function extractIntent(firstPrompt: string): Promise<ExtractedInt
|
|
|
34
16
|
* Check if intent extraction is available
|
|
35
17
|
*/
|
|
36
18
|
export declare function isIntentExtractionAvailable(): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Check if Anthropic API is available (for drift detection)
|
|
39
|
-
*/
|
|
40
|
-
export declare function isAnthropicAvailable(): boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Get the drift model to use (from env or default)
|
|
43
|
-
*/
|
|
44
|
-
export declare function getDriftModel(): string;
|
|
45
|
-
/**
|
|
46
|
-
* Extract structured reasoning from a parsed session using GPT-3.5-turbo
|
|
47
|
-
*/
|
|
48
|
-
export declare function extractReasoning(session: ParsedSession): Promise<ExtractedReasoning>;
|
|
49
|
-
/**
|
|
50
|
-
* Classify just the task status (lighter weight than full extraction)
|
|
51
|
-
*/
|
|
52
|
-
export declare function classifyTaskStatus(session: ParsedSession): Promise<TaskStatus>;
|
|
53
19
|
/**
|
|
54
20
|
* Check if session summary generation is available
|
|
55
21
|
*/
|
|
@@ -63,14 +29,21 @@ export declare function generateSessionSummary(sessionState: SessionState, steps
|
|
|
63
29
|
* Task analysis result from Haiku
|
|
64
30
|
*/
|
|
65
31
|
export interface TaskAnalysis {
|
|
32
|
+
task_type: 'information' | 'planning' | 'implementation';
|
|
66
33
|
action: 'continue' | 'new_task' | 'subtask' | 'parallel_task' | 'task_complete' | 'subtask_complete';
|
|
67
|
-
topic_match?: 'YES' | 'NO';
|
|
68
34
|
task_id: string;
|
|
69
35
|
current_goal: string;
|
|
70
36
|
parent_task_id?: string;
|
|
71
37
|
reasoning: string;
|
|
72
38
|
step_reasoning?: string;
|
|
73
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Conversation message for task analysis
|
|
42
|
+
*/
|
|
43
|
+
export interface ConversationMessage {
|
|
44
|
+
role: 'user' | 'assistant';
|
|
45
|
+
content: string;
|
|
46
|
+
}
|
|
74
47
|
/**
|
|
75
48
|
* Check if task analysis is available
|
|
76
49
|
*/
|
|
@@ -80,7 +53,7 @@ export declare function isTaskAnalysisAvailable(): boolean;
|
|
|
80
53
|
* Called after each main model response to orchestrate sessions
|
|
81
54
|
* Also compresses reasoning for steps if assistantResponse > 1000 chars
|
|
82
55
|
*/
|
|
83
|
-
export declare function analyzeTaskContext(currentSession: SessionState | null, latestUserMessage: string, recentSteps: StepRecord[], assistantResponse: string): Promise<TaskAnalysis>;
|
|
56
|
+
export declare function analyzeTaskContext(currentSession: SessionState | null, latestUserMessage: string, recentSteps: StepRecord[], assistantResponse: string, conversationHistory?: ConversationMessage[]): Promise<TaskAnalysis>;
|
|
84
57
|
export interface ExtractedReasoningAndDecisions {
|
|
85
58
|
reasoning_trace: string[];
|
|
86
59
|
decisions: Array<{
|
|
@@ -95,5 +68,8 @@ export declare function isReasoningExtractionAvailable(): boolean;
|
|
|
95
68
|
/**
|
|
96
69
|
* Extract reasoning trace and decisions from steps
|
|
97
70
|
* Called at task_complete to populate team memory with rich context
|
|
71
|
+
*
|
|
72
|
+
* @param formattedSteps - Pre-formatted XML string with grouped steps and actions
|
|
73
|
+
* @param originalGoal - The original task goal
|
|
98
74
|
*/
|
|
99
|
-
export declare function extractReasoningAndDecisions(
|
|
75
|
+
export declare function extractReasoningAndDecisions(formattedSteps: string, originalGoal: string): Promise<ExtractedReasoningAndDecisions>;
|