grov 0.6.17 → 0.6.18
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/dist/core/cloud/api-client.d.ts +2 -1
- package/dist/core/cloud/api-client.js +16 -0
- package/dist/integrations/proxy/handlers/preprocess.js +17 -0
- package/dist/integrations/proxy/orchestrator.js +22 -0
- package/dist/integrations/proxy/utils/usage-warnings.d.ts +2 -0
- package/dist/integrations/proxy/utils/usage-warnings.js +40 -0
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Team, Memory, MemorySyncRequest, MemorySyncResponse, DeviceFlowStartResponse, DeviceFlowPollResponse, ReasoningTraceEntry } from '@grov/shared';
|
|
1
|
+
import type { Team, Memory, MemorySyncRequest, MemorySyncResponse, DeviceFlowStartResponse, DeviceFlowPollResponse, ReasoningTraceEntry, RecordInjectionRequest, RecordInjectionResponse } from '@grov/shared';
|
|
2
2
|
export interface ApiResponse<T> {
|
|
3
3
|
data?: T;
|
|
4
4
|
error?: string;
|
|
@@ -88,6 +88,7 @@ export interface MatchResponse {
|
|
|
88
88
|
* @returns Match response with memory and score
|
|
89
89
|
*/
|
|
90
90
|
export declare function fetchMatch(teamId: string, data: MatchInput): Promise<MatchResponse>;
|
|
91
|
+
export declare function reportInjection(event: RecordInjectionRequest): Promise<RecordInjectionResponse | null>;
|
|
91
92
|
/**
|
|
92
93
|
* Sleep helper for polling
|
|
93
94
|
*/
|
|
@@ -148,6 +148,12 @@ export async function fetchTeamMemories(teamId, projectPath, options) {
|
|
|
148
148
|
if (!response.data || !response.data.memories) {
|
|
149
149
|
return [];
|
|
150
150
|
}
|
|
151
|
+
// Check if blocked due to quota
|
|
152
|
+
if (response.data.blocked) {
|
|
153
|
+
console.log('\n[grov] \x1b[33m⚠️ Free quota exceeded (110%). Upgrade to continue using memory injection.\x1b[0m');
|
|
154
|
+
console.log('[grov] Manage plan: https://app.grov.dev/settings\n');
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
151
157
|
return response.data.memories;
|
|
152
158
|
}
|
|
153
159
|
catch (err) {
|
|
@@ -186,6 +192,16 @@ export async function fetchMatch(teamId, data) {
|
|
|
186
192
|
return { match: null };
|
|
187
193
|
}
|
|
188
194
|
}
|
|
195
|
+
// ============= Usage Tracking =============
|
|
196
|
+
export async function reportInjection(event) {
|
|
197
|
+
try {
|
|
198
|
+
const response = await apiRequest('POST', '/usage/injection', event);
|
|
199
|
+
return response.data || null;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
189
205
|
// ============= Utility Functions =============
|
|
190
206
|
/**
|
|
191
207
|
* Sleep helper for polling
|
|
@@ -4,7 +4,10 @@ import { extractFilesFromMessages } from '../request-processor.js';
|
|
|
4
4
|
import { fetchTeamMemories } from '../../../core/cloud/api-client.js';
|
|
5
5
|
import { getSessionState, updateSessionState, markCleared, } from '../../../core/store/store.js';
|
|
6
6
|
import { isSyncEnabled, getSyncTeamId } from '../../../core/cloud/cloud-sync.js';
|
|
7
|
+
import { getCurrentUser } from '../../../core/cloud/credentials.js';
|
|
8
|
+
import { reportInjection } from '../../../core/cloud/api-client.js';
|
|
7
9
|
import { clearSessionState, cacheMemories, buildMemoryPreview, buildToolDescription, buildDriftRecoveryInjection, reconstructMessages, addInjectionRecord, commitPendingRecords, setCachedPreview, getCachedPreview, } from '../injection/memory-injection.js';
|
|
10
|
+
import { handleInjectionResponse } from '../utils/usage-warnings.js';
|
|
8
11
|
let pendingPlanClear = null;
|
|
9
12
|
export function getPendingPlanClear() {
|
|
10
13
|
return pendingPlanClear;
|
|
@@ -141,6 +144,20 @@ export async function preProcessRequest(adapter, body, sessionInfo, logger, dete
|
|
|
141
144
|
previewSize: preview?.length || 0,
|
|
142
145
|
hasDriftRecovery: !!driftRecovery,
|
|
143
146
|
});
|
|
147
|
+
const user = getCurrentUser();
|
|
148
|
+
if (user && teamId) {
|
|
149
|
+
reportInjection({
|
|
150
|
+
team_id: teamId,
|
|
151
|
+
user_id: user.id,
|
|
152
|
+
session_id: sessionInfo.sessionId,
|
|
153
|
+
event_id: `${sessionInfo.sessionId}:${Date.now()}:preview`,
|
|
154
|
+
injection_type: 'preview',
|
|
155
|
+
memory_ids: memories.map(m => m.id),
|
|
156
|
+
timestamp: new Date().toISOString(),
|
|
157
|
+
})
|
|
158
|
+
.then(res => handleInjectionResponse(res, teamId))
|
|
159
|
+
.catch(() => { });
|
|
160
|
+
}
|
|
144
161
|
}
|
|
145
162
|
else {
|
|
146
163
|
// No memories found - inject explicit "no entries" so Claude doesn't use old previews
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Handles session management, task orchestration, drift detection, and memory injection
|
|
3
3
|
import { randomUUID } from 'crypto';
|
|
4
4
|
import { config, buildSafeHeaders } from './config.js';
|
|
5
|
+
import { getSyncTeamId } from '../../core/cloud/cloud-sync.js';
|
|
6
|
+
import { getCurrentUser } from '../../core/cloud/credentials.js';
|
|
7
|
+
import { reportInjection } from '../../core/cloud/api-client.js';
|
|
5
8
|
import { extendedCache, evictOldestCacheEntry } from './cache/extended-cache.js';
|
|
6
9
|
import { getNextRequestId, taskLog, proxyLog, logTokenUsage } from './utils/logging.js';
|
|
7
10
|
import { preProcessRequest, setPendingPlanClear } from './handlers/preprocess.js';
|
|
@@ -11,6 +14,7 @@ import { buildCorrection, formatCorrectionForInjection } from '../../core/extrac
|
|
|
11
14
|
import { generateSessionSummary, analyzeTaskContext, } from '../../core/extraction/llm-extractor.js';
|
|
12
15
|
import { saveToTeamMemory } from './response-processor.js';
|
|
13
16
|
import { getCachedMemoryById, buildExpandedMemory, addInjectionRecord, hasToolCycleAtPosition, } from './injection/memory-injection.js';
|
|
17
|
+
import { handleInjectionResponse } from './utils/usage-warnings.js';
|
|
14
18
|
// In-memory state
|
|
15
19
|
const lastDriftResults = new Map();
|
|
16
20
|
const lastMessageCount = new Map();
|
|
@@ -180,6 +184,24 @@ export async function handleAgentRequest(context) {
|
|
|
180
184
|
if (ids.length > 0) {
|
|
181
185
|
const expandedCount = expandedParts.filter(p => !p.includes('not found')).length;
|
|
182
186
|
console.log(`[MEMORY] Expanded ${expandedCount}/${ids.length} memories`);
|
|
187
|
+
const teamId = getSyncTeamId();
|
|
188
|
+
const user = getCurrentUser();
|
|
189
|
+
if (teamId && user && expandedCount > 0) {
|
|
190
|
+
const expandedIds = ids.filter((_, i) => !expandedParts[i].includes('not found'));
|
|
191
|
+
for (const memoryId of expandedIds) {
|
|
192
|
+
reportInjection({
|
|
193
|
+
team_id: teamId,
|
|
194
|
+
user_id: user.id,
|
|
195
|
+
session_id: sessionInfo.sessionId,
|
|
196
|
+
event_id: `${sessionInfo.sessionId}:${Date.now()}:expand:${memoryId}`,
|
|
197
|
+
injection_type: 'expand',
|
|
198
|
+
memory_ids: [memoryId],
|
|
199
|
+
timestamp: new Date().toISOString(),
|
|
200
|
+
})
|
|
201
|
+
.then(res => handleInjectionResponse(res, teamId))
|
|
202
|
+
.catch(() => { });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
183
205
|
}
|
|
184
206
|
grovExpandResult = expandedParts.join('\n\n');
|
|
185
207
|
toolResultBlocks.push({
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Usage warning display for CLI
|
|
2
|
+
// Shows warnings when approaching or exceeding monthly quota
|
|
3
|
+
const SEVERITY = {
|
|
4
|
+
normal: 0,
|
|
5
|
+
warning_80: 1,
|
|
6
|
+
warning_100: 2,
|
|
7
|
+
overage: 3,
|
|
8
|
+
};
|
|
9
|
+
// Cache: teamId -> last warned status (prevents duplicate warnings)
|
|
10
|
+
const warnedStatus = new Map();
|
|
11
|
+
export function handleInjectionResponse(response, teamId) {
|
|
12
|
+
if (!response)
|
|
13
|
+
return;
|
|
14
|
+
// Period reset - clear cache
|
|
15
|
+
if (response.status === 'normal') {
|
|
16
|
+
warnedStatus.delete(teamId);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const lastSeverity = SEVERITY[warnedStatus.get(teamId) ?? 'normal'];
|
|
20
|
+
const currentSeverity = SEVERITY[response.status];
|
|
21
|
+
// Only warn on escalation
|
|
22
|
+
if (currentSeverity <= lastSeverity)
|
|
23
|
+
return;
|
|
24
|
+
warnedStatus.set(teamId, response.status);
|
|
25
|
+
const percent = Math.round((response.current_count / response.quota) * 100);
|
|
26
|
+
const usage = `${response.current_count}/${response.quota}`;
|
|
27
|
+
switch (response.status) {
|
|
28
|
+
case 'warning_80':
|
|
29
|
+
console.log(`[grov] ⚠️ Usage at ${percent}% of monthly quota (${usage} injections)`);
|
|
30
|
+
break;
|
|
31
|
+
case 'warning_100':
|
|
32
|
+
console.log(`[grov] ⚠️ Monthly quota reached (${usage} injections)`);
|
|
33
|
+
console.log(`[grov] Overage charges apply after 110% usage`);
|
|
34
|
+
break;
|
|
35
|
+
case 'overage':
|
|
36
|
+
console.log(`[grov] ⚠️ Overage billing now active (${usage} injections)`);
|
|
37
|
+
console.log(`[grov] Manage usage: https://grov.dev/settings`);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
CHANGED