grov 0.5.11 → 0.6.13
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/cli/agents/registry.d.ts +17 -0
- package/dist/cli/agents/registry.js +132 -0
- package/dist/cli/commands/agents.d.ts +1 -0
- package/dist/cli/commands/agents.js +48 -0
- package/dist/cli/commands/disable.d.ts +1 -0
- package/dist/cli/commands/disable.js +179 -0
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +157 -0
- package/dist/{commands → cli/commands}/drift-test.js +39 -26
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +90 -0
- package/dist/{commands → cli/commands}/login.js +19 -18
- package/dist/{commands → cli/commands}/logout.js +1 -1
- package/dist/{commands → cli/commands}/proxy-status.js +1 -1
- package/dist/cli/commands/setup.d.ts +6 -0
- package/dist/cli/commands/setup.js +309 -0
- package/dist/{commands → cli/commands}/status.js +1 -1
- package/dist/{commands → cli/commands}/sync.d.ts +1 -0
- package/dist/{commands → cli/commands}/sync.js +59 -4
- package/dist/{commands → cli/commands}/uninstall.js +2 -2
- package/dist/cli/index.js +270 -0
- package/dist/{lib → core/cloud}/cloud-sync.d.ts +3 -3
- package/dist/{lib → core/cloud}/cloud-sync.js +10 -10
- package/dist/{lib → core/extraction}/correction-builder-proxy.d.ts +1 -1
- package/dist/{lib → core/extraction}/correction-builder-proxy.js +0 -4
- package/dist/{lib → core/extraction}/drift-checker-proxy.d.ts +13 -9
- package/dist/core/extraction/drift-checker-proxy.js +510 -0
- package/dist/{lib → core/extraction}/llm-extractor.d.ts +8 -38
- package/dist/{lib → core/extraction}/llm-extractor.js +132 -220
- package/dist/{lib → core}/store/sessions.js +3 -19
- package/dist/core/store/store.d.ts +1 -0
- package/dist/{lib → core/store}/store.js +1 -1
- package/dist/{lib → core}/store/types.d.ts +0 -4
- package/dist/integrations/mcp/cache.d.ts +27 -0
- package/dist/integrations/mcp/cache.js +106 -0
- package/dist/integrations/mcp/capture/antigravity-parser.d.ts +26 -0
- package/dist/integrations/mcp/capture/antigravity-parser.js +272 -0
- package/dist/integrations/mcp/capture/antigravity-scanner.d.ts +24 -0
- package/dist/integrations/mcp/capture/antigravity-scanner.js +153 -0
- package/dist/integrations/mcp/capture/antigravity-sync-tracker.d.ts +29 -0
- package/dist/integrations/mcp/capture/antigravity-sync-tracker.js +115 -0
- package/dist/integrations/mcp/capture/cli-extractor.d.ts +18 -0
- package/dist/integrations/mcp/capture/cli-extractor.js +258 -0
- package/dist/integrations/mcp/capture/cli-synced.d.ts +4 -0
- package/dist/integrations/mcp/capture/cli-synced.js +62 -0
- package/dist/integrations/mcp/capture/cli-transform.d.ts +30 -0
- package/dist/integrations/mcp/capture/cli-transform.js +62 -0
- package/dist/integrations/mcp/capture/cli-watcher.d.ts +31 -0
- package/dist/integrations/mcp/capture/cli-watcher.js +106 -0
- package/dist/integrations/mcp/capture/hook-handler.d.ts +2 -0
- package/dist/integrations/mcp/capture/hook-handler.js +157 -0
- package/dist/integrations/mcp/capture/sqlite-reader.d.ts +35 -0
- package/dist/integrations/mcp/capture/sqlite-reader.js +388 -0
- package/dist/integrations/mcp/capture/sync-tracker.d.ts +16 -0
- package/dist/integrations/mcp/capture/sync-tracker.js +102 -0
- package/dist/integrations/mcp/clients/cursor/rules-installer.d.ts +19 -0
- package/dist/integrations/mcp/clients/cursor/rules-installer.js +123 -0
- package/dist/integrations/mcp/index.d.ts +1 -0
- package/dist/integrations/mcp/index.js +94 -0
- package/dist/integrations/mcp/logger.d.ts +8 -0
- package/dist/integrations/mcp/logger.js +50 -0
- package/dist/integrations/mcp/server.d.ts +5 -0
- package/dist/integrations/mcp/server.js +58 -0
- package/dist/integrations/mcp/tools/expand.d.ts +1 -0
- package/dist/integrations/mcp/tools/expand.js +53 -0
- package/dist/integrations/mcp/tools/preview.d.ts +1 -0
- package/dist/integrations/mcp/tools/preview.js +64 -0
- package/dist/integrations/proxy/agents/base.d.ts +43 -0
- package/dist/integrations/proxy/agents/base.js +13 -0
- package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.d.ts +4 -8
- package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.js +4 -33
- package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.d.ts +1 -1
- package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.js +22 -6
- package/dist/integrations/proxy/agents/claude/index.d.ts +43 -0
- package/dist/integrations/proxy/agents/claude/index.js +386 -0
- package/dist/{proxy/action-parser.d.ts → integrations/proxy/agents/claude/parser.d.ts} +1 -1
- package/dist/integrations/proxy/agents/codex/extractors.d.ts +6 -0
- package/dist/integrations/proxy/agents/codex/extractors.js +49 -0
- package/dist/integrations/proxy/agents/codex/forwarder.d.ts +9 -0
- package/dist/integrations/proxy/agents/codex/forwarder.js +125 -0
- package/dist/integrations/proxy/agents/codex/index.d.ts +44 -0
- package/dist/integrations/proxy/agents/codex/index.js +371 -0
- package/dist/integrations/proxy/agents/codex/parser.d.ts +11 -0
- package/dist/integrations/proxy/agents/codex/parser.js +104 -0
- package/dist/integrations/proxy/agents/codex/patch.d.ts +12 -0
- package/dist/integrations/proxy/agents/codex/patch.js +40 -0
- package/dist/integrations/proxy/agents/codex/settings.d.ts +18 -0
- package/dist/integrations/proxy/agents/codex/settings.js +73 -0
- package/dist/integrations/proxy/agents/codex/types.d.ts +59 -0
- package/dist/integrations/proxy/agents/codex/types.js +2 -0
- package/dist/integrations/proxy/agents/index.d.ts +11 -0
- package/dist/integrations/proxy/agents/index.js +25 -0
- package/dist/integrations/proxy/agents/types.d.ts +77 -0
- package/dist/integrations/proxy/agents/types.js +2 -0
- package/dist/{proxy → integrations/proxy/cache}/extended-cache.js +2 -6
- package/dist/{proxy → integrations/proxy}/config.js +1 -1
- package/dist/{proxy → integrations/proxy}/handlers/preprocess.d.ts +3 -3
- package/dist/integrations/proxy/handlers/preprocess.js +194 -0
- package/dist/integrations/proxy/index.js +20 -0
- package/dist/integrations/proxy/injection/memory-injection.d.ts +56 -0
- package/dist/integrations/proxy/injection/memory-injection.js +252 -0
- package/dist/integrations/proxy/orchestrator.d.ts +30 -0
- package/dist/integrations/proxy/orchestrator.js +954 -0
- package/dist/integrations/proxy/request-processor.d.ts +14 -0
- package/dist/integrations/proxy/request-processor.js +68 -0
- package/dist/{proxy → integrations/proxy}/response-processor.d.ts +4 -3
- package/dist/{proxy → integrations/proxy}/response-processor.js +51 -43
- package/dist/{proxy → integrations/proxy}/server.d.ts +0 -1
- package/dist/integrations/proxy/server.js +146 -0
- package/dist/{proxy → integrations/proxy}/types.d.ts +4 -0
- package/dist/{proxy → integrations/proxy}/utils/logging.d.ts +1 -0
- package/dist/{proxy → integrations/proxy}/utils/logging.js +5 -0
- package/package.json +31 -10
- package/postinstall.js +62 -6
- package/dist/cli.js +0 -149
- package/dist/commands/capture.d.ts +0 -6
- package/dist/commands/capture.js +0 -324
- package/dist/commands/disable.d.ts +0 -1
- package/dist/commands/disable.js +0 -14
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.js +0 -89
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +0 -52
- package/dist/commands/inject.d.ts +0 -5
- package/dist/commands/inject.js +0 -88
- package/dist/commands/prompt-inject.d.ts +0 -4
- package/dist/commands/prompt-inject.js +0 -451
- package/dist/commands/unregister.d.ts +0 -1
- package/dist/commands/unregister.js +0 -28
- package/dist/lib/anchor-extractor.d.ts +0 -30
- package/dist/lib/anchor-extractor.js +0 -296
- package/dist/lib/correction-builder.d.ts +0 -10
- package/dist/lib/correction-builder.js +0 -226
- package/dist/lib/drift-checker-proxy.js +0 -373
- package/dist/lib/drift-checker.d.ts +0 -66
- package/dist/lib/drift-checker.js +0 -341
- package/dist/lib/hooks.d.ts +0 -38
- package/dist/lib/hooks.js +0 -291
- package/dist/lib/jsonl-parser.d.ts +0 -87
- package/dist/lib/jsonl-parser.js +0 -281
- package/dist/lib/session-parser.d.ts +0 -44
- package/dist/lib/session-parser.js +0 -256
- package/dist/lib/store.d.ts +0 -1
- package/dist/proxy/cache.d.ts +0 -32
- package/dist/proxy/cache.js +0 -47
- package/dist/proxy/handlers/preprocess.js +0 -186
- package/dist/proxy/index.js +0 -30
- package/dist/proxy/injection/delta-tracking.d.ts +0 -11
- package/dist/proxy/injection/delta-tracking.js +0 -94
- package/dist/proxy/injection/injectors.d.ts +0 -7
- package/dist/proxy/injection/injectors.js +0 -139
- package/dist/proxy/request-processor.d.ts +0 -27
- package/dist/proxy/request-processor.js +0 -233
- package/dist/proxy/server.js +0 -1289
- /package/dist/{commands → cli/commands}/drift-test.d.ts +0 -0
- /package/dist/{commands → cli/commands}/login.d.ts +0 -0
- /package/dist/{commands → cli/commands}/logout.d.ts +0 -0
- /package/dist/{commands → cli/commands}/proxy-status.d.ts +0 -0
- /package/dist/{commands → cli/commands}/status.d.ts +0 -0
- /package/dist/{commands → cli/commands}/uninstall.d.ts +0 -0
- /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
- /package/dist/{lib → core/cloud}/api-client.d.ts +0 -0
- /package/dist/{lib → core/cloud}/api-client.js +0 -0
- /package/dist/{lib → core/cloud}/credentials.d.ts +0 -0
- /package/dist/{lib → core/cloud}/credentials.js +0 -0
- /package/dist/{lib → core}/store/convenience.d.ts +0 -0
- /package/dist/{lib → core}/store/convenience.js +0 -0
- /package/dist/{lib → core}/store/database.d.ts +0 -0
- /package/dist/{lib → core}/store/database.js +0 -0
- /package/dist/{lib → core}/store/drift.d.ts +0 -0
- /package/dist/{lib → core}/store/drift.js +0 -0
- /package/dist/{lib → core}/store/index.d.ts +0 -0
- /package/dist/{lib → core}/store/index.js +0 -0
- /package/dist/{lib → core}/store/sessions.d.ts +0 -0
- /package/dist/{lib → core}/store/steps.d.ts +0 -0
- /package/dist/{lib → core}/store/steps.js +0 -0
- /package/dist/{lib → core}/store/tasks.d.ts +0 -0
- /package/dist/{lib → core}/store/tasks.js +0 -0
- /package/dist/{lib → core}/store/types.js +0 -0
- /package/dist/{proxy/action-parser.js → integrations/proxy/agents/claude/parser.js} +0 -0
- /package/dist/{lib → integrations/proxy/agents/claude}/settings.d.ts +0 -0
- /package/dist/{lib → integrations/proxy/agents/claude}/settings.js +0 -0
- /package/dist/{proxy → integrations/proxy/cache}/extended-cache.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/config.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/index.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/types.js +0 -0
- /package/dist/{lib → utils}/debug.d.ts +0 -0
- /package/dist/{lib → utils}/debug.js +0 -0
- /package/dist/{lib → utils}/utils.d.ts +0 -0
- /package/dist/{lib → utils}/utils.js +0 -0
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
// Drift checker for proxy - scores Claude's actions vs original goal
|
|
2
|
-
// Reference: plan_proxy_local.md Section 4.2, 4.3
|
|
3
|
-
import Anthropic from '@anthropic-ai/sdk';
|
|
4
|
-
let anthropicClient = null;
|
|
5
|
-
function getAnthropicClient() {
|
|
6
|
-
if (!anthropicClient) {
|
|
7
|
-
const apiKey = process.env.ANTHROPIC_API_KEY || process.env.GROV_API_KEY;
|
|
8
|
-
if (!apiKey) {
|
|
9
|
-
throw new Error('ANTHROPIC_API_KEY or GROV_API_KEY required for drift checking');
|
|
10
|
-
}
|
|
11
|
-
anthropicClient = new Anthropic({ apiKey });
|
|
12
|
-
}
|
|
13
|
-
return anthropicClient;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Check if drift checking is available
|
|
17
|
-
*/
|
|
18
|
-
export function isDriftCheckAvailable() {
|
|
19
|
-
return !!(process.env.ANTHROPIC_API_KEY || process.env.GROV_API_KEY);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Main drift check - uses LLM if available, fallback to basic
|
|
23
|
-
*/
|
|
24
|
-
export async function checkDrift(input) {
|
|
25
|
-
if (isDriftCheckAvailable()) {
|
|
26
|
-
try {
|
|
27
|
-
return await checkDriftWithLLM(input);
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// Fallback to basic if LLM fails
|
|
31
|
-
return checkDriftBasic(input);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return checkDriftBasic(input);
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* LLM-based drift check using Haiku
|
|
38
|
-
* Reference: plan_proxy_local.md Section 3.1
|
|
39
|
-
*/
|
|
40
|
-
async function checkDriftWithLLM(input) {
|
|
41
|
-
const client = getAnthropicClient();
|
|
42
|
-
const actionsText = input.recentSteps
|
|
43
|
-
.slice(-10)
|
|
44
|
-
.map(step => {
|
|
45
|
-
if (step.action_type === 'bash' && step.command) {
|
|
46
|
-
return `- ${step.action_type}: ${step.command.substring(0, 100)}`;
|
|
47
|
-
}
|
|
48
|
-
if (step.files.length > 0) {
|
|
49
|
-
return `- ${step.action_type}: ${step.files.join(', ')}`;
|
|
50
|
-
}
|
|
51
|
-
return `- ${step.action_type}`;
|
|
52
|
-
})
|
|
53
|
-
.join('\n');
|
|
54
|
-
// If we have a latest user message, that's the CURRENT instruction
|
|
55
|
-
const currentInstruction = input.latestUserMessage?.substring(0, 500) || '';
|
|
56
|
-
const hasCurrentInstruction = currentInstruction.length > 20;
|
|
57
|
-
const prompt = `You are a drift detection system. Check if Claude is following the user's CURRENT instructions.
|
|
58
|
-
|
|
59
|
-
${hasCurrentInstruction ? `CURRENT USER INSTRUCTION (PRIMARY - check against this!):
|
|
60
|
-
"${currentInstruction}"
|
|
61
|
-
|
|
62
|
-
ORIGINAL SESSION GOAL (secondary context):
|
|
63
|
-
${input.sessionState.original_goal || 'Not specified'}` : `ORIGINAL GOAL:
|
|
64
|
-
${input.sessionState.original_goal || 'Not specified'}`}
|
|
65
|
-
|
|
66
|
-
EXPECTED SCOPE: ${input.sessionState.expected_scope.length > 0 ? input.sessionState.expected_scope.join(', ') : 'Not specified'}
|
|
67
|
-
|
|
68
|
-
CONSTRAINTS FROM USER: ${input.sessionState.constraints.length > 0 ? input.sessionState.constraints.join(', ') : 'None'}
|
|
69
|
-
|
|
70
|
-
CLAUDE'S RECENT ACTIONS:
|
|
71
|
-
${actionsText || 'No actions yet'}
|
|
72
|
-
|
|
73
|
-
═══════════════════════════════════════════════════════════════
|
|
74
|
-
CRITICAL: Compare Claude's actions against ${hasCurrentInstruction ? 'CURRENT USER INSTRUCTION' : 'ORIGINAL GOAL'}
|
|
75
|
-
═══════════════════════════════════════════════════════════════
|
|
76
|
-
|
|
77
|
-
DRIFT = Claude doing something the user did NOT ask for, or IGNORING what user said.
|
|
78
|
-
NOT DRIFT = Claude following user's instructions, even if different from original goal.
|
|
79
|
-
|
|
80
|
-
Example:
|
|
81
|
-
- Original goal: "analyze the code"
|
|
82
|
-
- Current instruction: "now create the files"
|
|
83
|
-
- Claude creates files → NOT DRIFT (following current instruction)
|
|
84
|
-
|
|
85
|
-
CHECK FOR REAL DRIFT:
|
|
86
|
-
1. Claude modifying files user said NOT to modify
|
|
87
|
-
2. Claude ignoring explicit constraints ("don't run commands" but runs commands)
|
|
88
|
-
3. Claude doing unrelated work (user asks about auth, Claude fixes CSS)
|
|
89
|
-
4. Repetitive loops (editing same file 5+ times without progress)
|
|
90
|
-
|
|
91
|
-
Rate 1-10:
|
|
92
|
-
- 10: Perfect - following current instruction exactly
|
|
93
|
-
- 8-9: Good - on track with minor deviations
|
|
94
|
-
- 6-7: Moderate drift - needs nudge
|
|
95
|
-
- 4-5: Significant drift - ignoring parts of instruction
|
|
96
|
-
- 2-3: Major drift - doing opposite of what user asked
|
|
97
|
-
- 1: Critical - completely off track
|
|
98
|
-
|
|
99
|
-
RESPONSE RULES:
|
|
100
|
-
- English only
|
|
101
|
-
- No emojis
|
|
102
|
-
- Return JSON: {"score": N, "diagnostic": "brief reason", "suggestedAction": "what to do"}`;
|
|
103
|
-
const response = await client.messages.create({
|
|
104
|
-
model: 'claude-haiku-4-5-20251001',
|
|
105
|
-
max_tokens: 300,
|
|
106
|
-
messages: [{ role: 'user', content: prompt }],
|
|
107
|
-
});
|
|
108
|
-
const content = response.content?.[0];
|
|
109
|
-
if (!content || content.type !== 'text') {
|
|
110
|
-
return createDefaultResult(8, 'Could not parse LLM response');
|
|
111
|
-
}
|
|
112
|
-
return parseLLMResponse(content.text);
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Basic drift check without LLM (fallback)
|
|
116
|
-
*/
|
|
117
|
-
export function checkDriftBasic(input) {
|
|
118
|
-
let score = 8;
|
|
119
|
-
const diagnostics = [];
|
|
120
|
-
const { sessionState, recentSteps } = input;
|
|
121
|
-
const expectedScope = sessionState.expected_scope;
|
|
122
|
-
// Check if actions touch files outside scope
|
|
123
|
-
for (const step of recentSteps) {
|
|
124
|
-
if (step.action_type === 'read')
|
|
125
|
-
continue; // Read is OK
|
|
126
|
-
for (const file of step.files) {
|
|
127
|
-
if (expectedScope.length > 0) {
|
|
128
|
-
const inScope = expectedScope.some(scope => file.includes(scope));
|
|
129
|
-
if (!inScope) {
|
|
130
|
-
score -= 2;
|
|
131
|
-
diagnostics.push(`File outside scope: ${file}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// Check repetition (same file edited 3+ times recently)
|
|
137
|
-
const fileCounts = new Map();
|
|
138
|
-
for (const step of recentSteps) {
|
|
139
|
-
if (step.action_type === 'edit' || step.action_type === 'write') {
|
|
140
|
-
for (const file of step.files) {
|
|
141
|
-
fileCounts.set(file, (fileCounts.get(file) || 0) + 1);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
for (const [file, count] of fileCounts) {
|
|
146
|
-
if (count >= 3) {
|
|
147
|
-
score -= 1;
|
|
148
|
-
diagnostics.push(`Repeated edits to ${file} (${count}x)`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
score = Math.max(1, Math.min(10, score));
|
|
152
|
-
return {
|
|
153
|
-
score,
|
|
154
|
-
driftType: scoreToDriftType(score),
|
|
155
|
-
diagnostic: diagnostics.length > 0 ? diagnostics.join('; ') : 'On track',
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Parse LLM response JSON
|
|
160
|
-
*/
|
|
161
|
-
function parseLLMResponse(text) {
|
|
162
|
-
try {
|
|
163
|
-
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
164
|
-
if (!jsonMatch) {
|
|
165
|
-
return createDefaultResult(8, 'No JSON in response');
|
|
166
|
-
}
|
|
167
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
168
|
-
const score = typeof parsed.score === 'number'
|
|
169
|
-
? Math.min(10, Math.max(1, parsed.score))
|
|
170
|
-
: 8;
|
|
171
|
-
return {
|
|
172
|
-
score,
|
|
173
|
-
driftType: scoreToDriftType(score),
|
|
174
|
-
diagnostic: typeof parsed.diagnostic === 'string' ? parsed.diagnostic : 'Unknown',
|
|
175
|
-
suggestedAction: typeof parsed.suggestedAction === 'string' ? parsed.suggestedAction : undefined,
|
|
176
|
-
recoverySteps: Array.isArray(parsed.recoverySteps)
|
|
177
|
-
? parsed.recoverySteps.filter((s) => typeof s === 'string')
|
|
178
|
-
: undefined,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
return createDefaultResult(8, 'Failed to parse response');
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Convert score to drift type
|
|
187
|
-
*/
|
|
188
|
-
function scoreToDriftType(score) {
|
|
189
|
-
if (score >= 8)
|
|
190
|
-
return 'none';
|
|
191
|
-
if (score >= 5)
|
|
192
|
-
return 'minor';
|
|
193
|
-
if (score >= 3)
|
|
194
|
-
return 'major';
|
|
195
|
-
return 'critical';
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Convert score to correction level
|
|
199
|
-
* Reference: plan_proxy_local.md Section 4.3
|
|
200
|
-
*/
|
|
201
|
-
export function scoreToCorrectionLevel(score) {
|
|
202
|
-
if (score >= 8)
|
|
203
|
-
return null;
|
|
204
|
-
if (score === 7)
|
|
205
|
-
return 'nudge';
|
|
206
|
-
if (score >= 5)
|
|
207
|
-
return 'correct';
|
|
208
|
-
if (score >= 3)
|
|
209
|
-
return 'intervene';
|
|
210
|
-
return 'halt';
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Check if score requires skipping steps table
|
|
214
|
-
* Reference: plan_proxy_local.md Section 4.2
|
|
215
|
-
*/
|
|
216
|
-
export function shouldSkipSteps(score) {
|
|
217
|
-
return score < 5;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Create default result
|
|
221
|
-
*/
|
|
222
|
-
function createDefaultResult(score, diagnostic) {
|
|
223
|
-
return {
|
|
224
|
-
score,
|
|
225
|
-
driftType: scoreToDriftType(score),
|
|
226
|
-
diagnostic,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
// ============================================
|
|
230
|
-
// RECOVERY ALIGNMENT CHECK
|
|
231
|
-
// Reference: plan_proxy_local.md Section 4.4
|
|
232
|
-
// ============================================
|
|
233
|
-
/**
|
|
234
|
-
* Check if Claude's action aligns with the recovery plan
|
|
235
|
-
* Returns true if aligned, false if still drifting
|
|
236
|
-
*/
|
|
237
|
-
export function checkRecoveryAlignment(proposedAction, recoveryPlan, sessionState) {
|
|
238
|
-
if (!recoveryPlan || recoveryPlan.steps.length === 0) {
|
|
239
|
-
// No recovery plan - check if action is within scope
|
|
240
|
-
if (sessionState.expected_scope.length === 0) {
|
|
241
|
-
return { aligned: true, reason: 'No recovery plan or scope defined' };
|
|
242
|
-
}
|
|
243
|
-
// Check if files are in expected scope
|
|
244
|
-
const inScope = proposedAction.files.every(file => sessionState.expected_scope.some(scope => file.includes(scope)));
|
|
245
|
-
return {
|
|
246
|
-
aligned: inScope,
|
|
247
|
-
reason: inScope ? 'Files within expected scope' : 'Files outside expected scope',
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
const firstStep = recoveryPlan.steps[0].toLowerCase();
|
|
251
|
-
const actionDesc = `${proposedAction.actionType} ${proposedAction.files.join(' ')}`.toLowerCase();
|
|
252
|
-
// Check for keyword matches
|
|
253
|
-
const keywords = firstStep.split(/\s+/).filter(w => w.length > 3);
|
|
254
|
-
const matches = keywords.filter(kw => actionDesc.includes(kw) || proposedAction.files.some(f => f.toLowerCase().includes(kw)));
|
|
255
|
-
if (matches.length >= 2 || (matches.length >= 1 && proposedAction.files.length > 0)) {
|
|
256
|
-
return { aligned: true, reason: `Action matches recovery step: ${firstStep}` };
|
|
257
|
-
}
|
|
258
|
-
return { aligned: false, reason: `Expected: ${firstStep}, Got: ${actionDesc}` };
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Generate forced recovery prompt using Haiku
|
|
262
|
-
* Called when escalation_count >= 3 (forced mode)
|
|
263
|
-
* This STOPS Claude and injects a specific recovery message
|
|
264
|
-
*/
|
|
265
|
-
export async function generateForcedRecovery(sessionState, recentActions, lastDriftResult) {
|
|
266
|
-
const client = getAnthropicClient();
|
|
267
|
-
const actionsText = recentActions
|
|
268
|
-
.slice(-5)
|
|
269
|
-
.map(a => `- ${a.actionType}: ${a.files.join(', ')}`)
|
|
270
|
-
.join('\n');
|
|
271
|
-
const prompt = `You are helping recover a coding assistant that has COMPLETELY DRIFTED from its goal.
|
|
272
|
-
|
|
273
|
-
ORIGINAL GOAL: ${sessionState.original_goal || 'Not specified'}
|
|
274
|
-
|
|
275
|
-
EXPECTED SCOPE: ${sessionState.expected_scope.join(', ') || 'Not specified'}
|
|
276
|
-
|
|
277
|
-
CONSTRAINTS: ${sessionState.constraints.join(', ') || 'None'}
|
|
278
|
-
|
|
279
|
-
RECENT ACTIONS (all off-track):
|
|
280
|
-
${actionsText || 'None recorded'}
|
|
281
|
-
|
|
282
|
-
DRIFT DIAGNOSTIC: ${lastDriftResult.diagnostic}
|
|
283
|
-
|
|
284
|
-
ESCALATION COUNT: ${sessionState.escalation_count} (MAX REACHED)
|
|
285
|
-
|
|
286
|
-
Generate a STRICT recovery message that will:
|
|
287
|
-
1. STOP the assistant immediately
|
|
288
|
-
2. FORCE it to acknowledge the drift
|
|
289
|
-
3. Give ONE SPECIFIC, SIMPLE action to get back on track
|
|
290
|
-
|
|
291
|
-
RESPONSE RULES:
|
|
292
|
-
- English only
|
|
293
|
-
- No emojis
|
|
294
|
-
- Return JSON:
|
|
295
|
-
{
|
|
296
|
-
"recoveryPrompt": "The full message to inject (be firm but constructive, ~200 words)",
|
|
297
|
-
"mandatoryAction": "ONE specific action (e.g., 'Read src/auth/login.ts to refocus on authentication')"
|
|
298
|
-
}`;
|
|
299
|
-
const response = await client.messages.create({
|
|
300
|
-
model: 'claude-haiku-4-5-20251001',
|
|
301
|
-
max_tokens: 600,
|
|
302
|
-
messages: [{ role: 'user', content: prompt }],
|
|
303
|
-
});
|
|
304
|
-
const content = response.content?.[0];
|
|
305
|
-
if (!content || content.type !== 'text') {
|
|
306
|
-
return createFallbackForcedRecovery(sessionState);
|
|
307
|
-
}
|
|
308
|
-
try {
|
|
309
|
-
const jsonMatch = content.text.match(/\{[\s\S]*\}/);
|
|
310
|
-
if (!jsonMatch) {
|
|
311
|
-
return createFallbackForcedRecovery(sessionState);
|
|
312
|
-
}
|
|
313
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
314
|
-
const recoveryPrompt = typeof parsed.recoveryPrompt === 'string'
|
|
315
|
-
? parsed.recoveryPrompt
|
|
316
|
-
: `STOP. Return to: ${sessionState.original_goal}`;
|
|
317
|
-
const mandatoryAction = typeof parsed.mandatoryAction === 'string'
|
|
318
|
-
? parsed.mandatoryAction
|
|
319
|
-
: `Focus on ${sessionState.original_goal}`;
|
|
320
|
-
return {
|
|
321
|
-
recoveryPrompt,
|
|
322
|
-
mandatoryAction,
|
|
323
|
-
injectionText: formatForcedRecoveryInjection(recoveryPrompt, mandatoryAction, sessionState),
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
catch {
|
|
327
|
-
return createFallbackForcedRecovery(sessionState);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Format forced recovery for system prompt injection
|
|
332
|
-
*/
|
|
333
|
-
function formatForcedRecoveryInjection(recoveryPrompt, mandatoryAction, sessionState) {
|
|
334
|
-
return `
|
|
335
|
-
|
|
336
|
-
<grov_forced_recovery>
|
|
337
|
-
════════════════════════════════════════════════════════════
|
|
338
|
-
*** CRITICAL: FORCED RECOVERY MODE ACTIVATED ***
|
|
339
|
-
════════════════════════════════════════════════════════════
|
|
340
|
-
|
|
341
|
-
${recoveryPrompt}
|
|
342
|
-
|
|
343
|
-
────────────────────────────────────────────────────────────
|
|
344
|
-
MANDATORY FIRST ACTION (you MUST do this before ANYTHING else):
|
|
345
|
-
${mandatoryAction}
|
|
346
|
-
────────────────────────────────────────────────────────────
|
|
347
|
-
|
|
348
|
-
Original goal: ${sessionState.original_goal || 'See above'}
|
|
349
|
-
Escalation level: ${sessionState.escalation_count}/3 (MAXIMUM)
|
|
350
|
-
|
|
351
|
-
YOUR NEXT MESSAGE MUST:
|
|
352
|
-
1. Acknowledge: "I understand I have drifted from the goal"
|
|
353
|
-
2. State: "I will now ${mandatoryAction}"
|
|
354
|
-
3. Execute ONLY that action
|
|
355
|
-
|
|
356
|
-
ANY OTHER RESPONSE WILL BE REJECTED.
|
|
357
|
-
════════════════════════════════════════════════════════════
|
|
358
|
-
</grov_forced_recovery>
|
|
359
|
-
|
|
360
|
-
`;
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Fallback forced recovery without LLM
|
|
364
|
-
*/
|
|
365
|
-
function createFallbackForcedRecovery(sessionState) {
|
|
366
|
-
const goal = sessionState.original_goal || 'the original task';
|
|
367
|
-
const mandatoryAction = `Stop current work and return to: ${goal}`;
|
|
368
|
-
return {
|
|
369
|
-
recoveryPrompt: `You have completely drifted from your goal. Stop what you're doing immediately and refocus on: ${goal}`,
|
|
370
|
-
mandatoryAction,
|
|
371
|
-
injectionText: formatForcedRecoveryInjection(`You have completely drifted from your goal. Stop what you're doing immediately and refocus on: ${goal}`, mandatoryAction, sessionState),
|
|
372
|
-
};
|
|
373
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { SessionState, RecoveryPlan, StepRecord } from './store.js';
|
|
2
|
-
import type { ClaudeAction } from './session-parser.js';
|
|
3
|
-
export declare const DRIFT_CONFIG: {
|
|
4
|
-
SCORE_NO_INJECTION: number;
|
|
5
|
-
SCORE_NUDGE: number;
|
|
6
|
-
SCORE_CORRECT: number;
|
|
7
|
-
SCORE_INTERVENE: number;
|
|
8
|
-
SCORE_HALT: number;
|
|
9
|
-
MAX_WARNINGS_BEFORE_FLAG: number;
|
|
10
|
-
AVG_SCORE_THRESHOLD: number;
|
|
11
|
-
MAX_ESCALATION: number;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Input for drift check
|
|
15
|
-
*
|
|
16
|
-
* CRITICAL: We check Claude's ACTIONS, not user prompts.
|
|
17
|
-
* The user can ask whatever they want - we monitor what CLAUDE DOES.
|
|
18
|
-
*/
|
|
19
|
-
export interface DriftCheckInput {
|
|
20
|
-
originalGoal: string;
|
|
21
|
-
expectedScope: string[];
|
|
22
|
-
constraints: string[];
|
|
23
|
-
keywords: string[];
|
|
24
|
-
driftHistory: Array<{
|
|
25
|
-
score: number;
|
|
26
|
-
level: string;
|
|
27
|
-
}>;
|
|
28
|
-
escalationCount: number;
|
|
29
|
-
claudeActions: ClaudeAction[];
|
|
30
|
-
retrievedSteps: StepRecord[];
|
|
31
|
-
lastNSteps: StepRecord[];
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Result from drift check
|
|
35
|
-
*/
|
|
36
|
-
export interface DriftCheckResult {
|
|
37
|
-
score: number;
|
|
38
|
-
type: 'aligned' | 'minor' | 'moderate' | 'severe' | 'critical';
|
|
39
|
-
diagnostic: string;
|
|
40
|
-
recoveryPlan?: RecoveryPlan;
|
|
41
|
-
boundaries: string[];
|
|
42
|
-
verification: string;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Build input for drift check from Claude's ACTIONS and session state.
|
|
46
|
-
*
|
|
47
|
-
* CRITICAL: We check ACTIONS, not user prompts.
|
|
48
|
-
*/
|
|
49
|
-
export declare function buildDriftCheckInput(claudeActions: ClaudeAction[], sessionId: string, sessionState: SessionState): DriftCheckInput;
|
|
50
|
-
/**
|
|
51
|
-
* Check drift using LLM or fallback
|
|
52
|
-
*/
|
|
53
|
-
export declare function checkDrift(input: DriftCheckInput): Promise<DriftCheckResult>;
|
|
54
|
-
/**
|
|
55
|
-
* Basic drift detection without LLM.
|
|
56
|
-
*
|
|
57
|
-
* CRITICAL: Checks Claude's ACTIONS, not user prompts.
|
|
58
|
-
* - Read actions are ALWAYS OK (exploration is not drift)
|
|
59
|
-
* - Edit/Write actions outside scope = drift
|
|
60
|
-
* - Repetition patterns = drift
|
|
61
|
-
*/
|
|
62
|
-
export declare function checkDriftBasic(input: DriftCheckInput): DriftCheckResult;
|
|
63
|
-
/**
|
|
64
|
-
* Infer action type from prompt
|
|
65
|
-
*/
|
|
66
|
-
export declare function inferAction(prompt: string): string;
|