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.
Files changed (190) hide show
  1. package/dist/cli/agents/registry.d.ts +17 -0
  2. package/dist/cli/agents/registry.js +132 -0
  3. package/dist/cli/commands/agents.d.ts +1 -0
  4. package/dist/cli/commands/agents.js +48 -0
  5. package/dist/cli/commands/disable.d.ts +1 -0
  6. package/dist/cli/commands/disable.js +179 -0
  7. package/dist/cli/commands/doctor.d.ts +1 -0
  8. package/dist/cli/commands/doctor.js +157 -0
  9. package/dist/{commands → cli/commands}/drift-test.js +39 -26
  10. package/dist/cli/commands/init.d.ts +1 -0
  11. package/dist/cli/commands/init.js +90 -0
  12. package/dist/{commands → cli/commands}/login.js +19 -18
  13. package/dist/{commands → cli/commands}/logout.js +1 -1
  14. package/dist/{commands → cli/commands}/proxy-status.js +1 -1
  15. package/dist/cli/commands/setup.d.ts +6 -0
  16. package/dist/cli/commands/setup.js +309 -0
  17. package/dist/{commands → cli/commands}/status.js +1 -1
  18. package/dist/{commands → cli/commands}/sync.d.ts +1 -0
  19. package/dist/{commands → cli/commands}/sync.js +59 -4
  20. package/dist/{commands → cli/commands}/uninstall.js +2 -2
  21. package/dist/cli/index.js +270 -0
  22. package/dist/{lib → core/cloud}/cloud-sync.d.ts +3 -3
  23. package/dist/{lib → core/cloud}/cloud-sync.js +10 -10
  24. package/dist/{lib → core/extraction}/correction-builder-proxy.d.ts +1 -1
  25. package/dist/{lib → core/extraction}/correction-builder-proxy.js +0 -4
  26. package/dist/{lib → core/extraction}/drift-checker-proxy.d.ts +13 -9
  27. package/dist/core/extraction/drift-checker-proxy.js +510 -0
  28. package/dist/{lib → core/extraction}/llm-extractor.d.ts +8 -38
  29. package/dist/{lib → core/extraction}/llm-extractor.js +132 -220
  30. package/dist/{lib → core}/store/sessions.js +3 -19
  31. package/dist/core/store/store.d.ts +1 -0
  32. package/dist/{lib → core/store}/store.js +1 -1
  33. package/dist/{lib → core}/store/types.d.ts +0 -4
  34. package/dist/integrations/mcp/cache.d.ts +27 -0
  35. package/dist/integrations/mcp/cache.js +106 -0
  36. package/dist/integrations/mcp/capture/antigravity-parser.d.ts +26 -0
  37. package/dist/integrations/mcp/capture/antigravity-parser.js +272 -0
  38. package/dist/integrations/mcp/capture/antigravity-scanner.d.ts +24 -0
  39. package/dist/integrations/mcp/capture/antigravity-scanner.js +153 -0
  40. package/dist/integrations/mcp/capture/antigravity-sync-tracker.d.ts +29 -0
  41. package/dist/integrations/mcp/capture/antigravity-sync-tracker.js +115 -0
  42. package/dist/integrations/mcp/capture/cli-extractor.d.ts +18 -0
  43. package/dist/integrations/mcp/capture/cli-extractor.js +258 -0
  44. package/dist/integrations/mcp/capture/cli-synced.d.ts +4 -0
  45. package/dist/integrations/mcp/capture/cli-synced.js +62 -0
  46. package/dist/integrations/mcp/capture/cli-transform.d.ts +30 -0
  47. package/dist/integrations/mcp/capture/cli-transform.js +62 -0
  48. package/dist/integrations/mcp/capture/cli-watcher.d.ts +31 -0
  49. package/dist/integrations/mcp/capture/cli-watcher.js +106 -0
  50. package/dist/integrations/mcp/capture/hook-handler.d.ts +2 -0
  51. package/dist/integrations/mcp/capture/hook-handler.js +157 -0
  52. package/dist/integrations/mcp/capture/sqlite-reader.d.ts +35 -0
  53. package/dist/integrations/mcp/capture/sqlite-reader.js +388 -0
  54. package/dist/integrations/mcp/capture/sync-tracker.d.ts +16 -0
  55. package/dist/integrations/mcp/capture/sync-tracker.js +102 -0
  56. package/dist/integrations/mcp/clients/cursor/rules-installer.d.ts +19 -0
  57. package/dist/integrations/mcp/clients/cursor/rules-installer.js +123 -0
  58. package/dist/integrations/mcp/index.d.ts +1 -0
  59. package/dist/integrations/mcp/index.js +94 -0
  60. package/dist/integrations/mcp/logger.d.ts +8 -0
  61. package/dist/integrations/mcp/logger.js +50 -0
  62. package/dist/integrations/mcp/server.d.ts +5 -0
  63. package/dist/integrations/mcp/server.js +58 -0
  64. package/dist/integrations/mcp/tools/expand.d.ts +1 -0
  65. package/dist/integrations/mcp/tools/expand.js +53 -0
  66. package/dist/integrations/mcp/tools/preview.d.ts +1 -0
  67. package/dist/integrations/mcp/tools/preview.js +64 -0
  68. package/dist/integrations/proxy/agents/base.d.ts +43 -0
  69. package/dist/integrations/proxy/agents/base.js +13 -0
  70. package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.d.ts +4 -8
  71. package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.js +4 -33
  72. package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.d.ts +1 -1
  73. package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.js +22 -6
  74. package/dist/integrations/proxy/agents/claude/index.d.ts +43 -0
  75. package/dist/integrations/proxy/agents/claude/index.js +386 -0
  76. package/dist/{proxy/action-parser.d.ts → integrations/proxy/agents/claude/parser.d.ts} +1 -1
  77. package/dist/integrations/proxy/agents/codex/extractors.d.ts +6 -0
  78. package/dist/integrations/proxy/agents/codex/extractors.js +49 -0
  79. package/dist/integrations/proxy/agents/codex/forwarder.d.ts +9 -0
  80. package/dist/integrations/proxy/agents/codex/forwarder.js +125 -0
  81. package/dist/integrations/proxy/agents/codex/index.d.ts +44 -0
  82. package/dist/integrations/proxy/agents/codex/index.js +371 -0
  83. package/dist/integrations/proxy/agents/codex/parser.d.ts +11 -0
  84. package/dist/integrations/proxy/agents/codex/parser.js +104 -0
  85. package/dist/integrations/proxy/agents/codex/patch.d.ts +12 -0
  86. package/dist/integrations/proxy/agents/codex/patch.js +40 -0
  87. package/dist/integrations/proxy/agents/codex/settings.d.ts +18 -0
  88. package/dist/integrations/proxy/agents/codex/settings.js +73 -0
  89. package/dist/integrations/proxy/agents/codex/types.d.ts +59 -0
  90. package/dist/integrations/proxy/agents/codex/types.js +2 -0
  91. package/dist/integrations/proxy/agents/index.d.ts +11 -0
  92. package/dist/integrations/proxy/agents/index.js +25 -0
  93. package/dist/integrations/proxy/agents/types.d.ts +77 -0
  94. package/dist/integrations/proxy/agents/types.js +2 -0
  95. package/dist/{proxy → integrations/proxy/cache}/extended-cache.js +2 -6
  96. package/dist/{proxy → integrations/proxy}/config.js +1 -1
  97. package/dist/{proxy → integrations/proxy}/handlers/preprocess.d.ts +3 -3
  98. package/dist/integrations/proxy/handlers/preprocess.js +194 -0
  99. package/dist/integrations/proxy/index.js +20 -0
  100. package/dist/integrations/proxy/injection/memory-injection.d.ts +56 -0
  101. package/dist/integrations/proxy/injection/memory-injection.js +252 -0
  102. package/dist/integrations/proxy/orchestrator.d.ts +30 -0
  103. package/dist/integrations/proxy/orchestrator.js +954 -0
  104. package/dist/integrations/proxy/request-processor.d.ts +14 -0
  105. package/dist/integrations/proxy/request-processor.js +68 -0
  106. package/dist/{proxy → integrations/proxy}/response-processor.d.ts +4 -3
  107. package/dist/{proxy → integrations/proxy}/response-processor.js +51 -43
  108. package/dist/{proxy → integrations/proxy}/server.d.ts +0 -1
  109. package/dist/integrations/proxy/server.js +146 -0
  110. package/dist/{proxy → integrations/proxy}/types.d.ts +4 -0
  111. package/dist/{proxy → integrations/proxy}/utils/logging.d.ts +1 -0
  112. package/dist/{proxy → integrations/proxy}/utils/logging.js +5 -0
  113. package/package.json +31 -10
  114. package/postinstall.js +62 -6
  115. package/dist/cli.js +0 -149
  116. package/dist/commands/capture.d.ts +0 -6
  117. package/dist/commands/capture.js +0 -324
  118. package/dist/commands/disable.d.ts +0 -1
  119. package/dist/commands/disable.js +0 -14
  120. package/dist/commands/doctor.d.ts +0 -1
  121. package/dist/commands/doctor.js +0 -89
  122. package/dist/commands/init.d.ts +0 -1
  123. package/dist/commands/init.js +0 -52
  124. package/dist/commands/inject.d.ts +0 -5
  125. package/dist/commands/inject.js +0 -88
  126. package/dist/commands/prompt-inject.d.ts +0 -4
  127. package/dist/commands/prompt-inject.js +0 -451
  128. package/dist/commands/unregister.d.ts +0 -1
  129. package/dist/commands/unregister.js +0 -28
  130. package/dist/lib/anchor-extractor.d.ts +0 -30
  131. package/dist/lib/anchor-extractor.js +0 -296
  132. package/dist/lib/correction-builder.d.ts +0 -10
  133. package/dist/lib/correction-builder.js +0 -226
  134. package/dist/lib/drift-checker-proxy.js +0 -373
  135. package/dist/lib/drift-checker.d.ts +0 -66
  136. package/dist/lib/drift-checker.js +0 -341
  137. package/dist/lib/hooks.d.ts +0 -38
  138. package/dist/lib/hooks.js +0 -291
  139. package/dist/lib/jsonl-parser.d.ts +0 -87
  140. package/dist/lib/jsonl-parser.js +0 -281
  141. package/dist/lib/session-parser.d.ts +0 -44
  142. package/dist/lib/session-parser.js +0 -256
  143. package/dist/lib/store.d.ts +0 -1
  144. package/dist/proxy/cache.d.ts +0 -32
  145. package/dist/proxy/cache.js +0 -47
  146. package/dist/proxy/handlers/preprocess.js +0 -186
  147. package/dist/proxy/index.js +0 -30
  148. package/dist/proxy/injection/delta-tracking.d.ts +0 -11
  149. package/dist/proxy/injection/delta-tracking.js +0 -94
  150. package/dist/proxy/injection/injectors.d.ts +0 -7
  151. package/dist/proxy/injection/injectors.js +0 -139
  152. package/dist/proxy/request-processor.d.ts +0 -27
  153. package/dist/proxy/request-processor.js +0 -233
  154. package/dist/proxy/server.js +0 -1289
  155. /package/dist/{commands → cli/commands}/drift-test.d.ts +0 -0
  156. /package/dist/{commands → cli/commands}/login.d.ts +0 -0
  157. /package/dist/{commands → cli/commands}/logout.d.ts +0 -0
  158. /package/dist/{commands → cli/commands}/proxy-status.d.ts +0 -0
  159. /package/dist/{commands → cli/commands}/status.d.ts +0 -0
  160. /package/dist/{commands → cli/commands}/uninstall.d.ts +0 -0
  161. /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
  162. /package/dist/{lib → core/cloud}/api-client.d.ts +0 -0
  163. /package/dist/{lib → core/cloud}/api-client.js +0 -0
  164. /package/dist/{lib → core/cloud}/credentials.d.ts +0 -0
  165. /package/dist/{lib → core/cloud}/credentials.js +0 -0
  166. /package/dist/{lib → core}/store/convenience.d.ts +0 -0
  167. /package/dist/{lib → core}/store/convenience.js +0 -0
  168. /package/dist/{lib → core}/store/database.d.ts +0 -0
  169. /package/dist/{lib → core}/store/database.js +0 -0
  170. /package/dist/{lib → core}/store/drift.d.ts +0 -0
  171. /package/dist/{lib → core}/store/drift.js +0 -0
  172. /package/dist/{lib → core}/store/index.d.ts +0 -0
  173. /package/dist/{lib → core}/store/index.js +0 -0
  174. /package/dist/{lib → core}/store/sessions.d.ts +0 -0
  175. /package/dist/{lib → core}/store/steps.d.ts +0 -0
  176. /package/dist/{lib → core}/store/steps.js +0 -0
  177. /package/dist/{lib → core}/store/tasks.d.ts +0 -0
  178. /package/dist/{lib → core}/store/tasks.js +0 -0
  179. /package/dist/{lib → core}/store/types.js +0 -0
  180. /package/dist/{proxy/action-parser.js → integrations/proxy/agents/claude/parser.js} +0 -0
  181. /package/dist/{lib → integrations/proxy/agents/claude}/settings.d.ts +0 -0
  182. /package/dist/{lib → integrations/proxy/agents/claude}/settings.js +0 -0
  183. /package/dist/{proxy → integrations/proxy/cache}/extended-cache.d.ts +0 -0
  184. /package/dist/{proxy → integrations/proxy}/config.d.ts +0 -0
  185. /package/dist/{proxy → integrations/proxy}/index.d.ts +0 -0
  186. /package/dist/{proxy → integrations/proxy}/types.js +0 -0
  187. /package/dist/{lib → utils}/debug.d.ts +0 -0
  188. /package/dist/{lib → utils}/debug.js +0 -0
  189. /package/dist/{lib → utils}/utils.d.ts +0 -0
  190. /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;