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,25 +1,4 @@
1
- // Pure extraction functions for parsing request/response data
2
- export function detectKeyDecision(action, reasoning) {
3
- // Code modifications are always key decisions
4
- if (action.actionType === 'edit' || action.actionType === 'write') {
5
- return true;
6
- }
7
- // Check for decision-related keywords in reasoning
8
- const decisionKeywords = [
9
- 'decision', 'decided', 'chose', 'chosen', 'selected', 'picked',
10
- 'approach', 'strategy', 'solution', 'implementation',
11
- 'because', 'reason', 'rationale', 'trade-off', 'tradeoff',
12
- 'instead of', 'rather than', 'prefer', 'opted',
13
- 'conclusion', 'determined', 'resolved'
14
- ];
15
- const reasoningLower = reasoning.toLowerCase();
16
- const hasDecisionKeyword = decisionKeywords.some(kw => reasoningLower.includes(kw));
17
- // Substantial reasoning (>200 chars) with decision keyword = key decision
18
- if (hasDecisionKeyword && reasoning.length > 200) {
19
- return true;
20
- }
21
- return false;
22
- }
1
+ // Claude-specific extraction functions
23
2
  export function extractTextContent(response) {
24
3
  return response.content
25
4
  .filter((block) => block.type === 'text')
@@ -27,14 +6,11 @@ export function extractTextContent(response) {
27
6
  .join('\n');
28
7
  }
29
8
  export function extractProjectPath(body) {
30
- // Try to extract from system prompt or messages
31
- // Handle both string and array format for system prompt
32
9
  let systemPrompt = '';
33
10
  if (typeof body.system === 'string') {
34
11
  systemPrompt = body.system;
35
12
  }
36
13
  else if (Array.isArray(body.system)) {
37
- // New API format: system is array of {type: 'text', text: '...'}
38
14
  systemPrompt = body.system
39
15
  .filter((block) => block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string')
40
16
  .map(block => block.text)
@@ -46,29 +22,27 @@ export function extractProjectPath(body) {
46
22
  }
47
23
  return null;
48
24
  }
25
+ export function extractSessionId(response) {
26
+ return response.id || null;
27
+ }
49
28
  export function extractGoalFromMessages(messages) {
50
29
  const userMessages = messages?.filter(m => m.role === 'user') || [];
51
- // Iterate in REVERSE to get the LAST (most recent) user message
52
30
  for (const userMsg of [...userMessages].reverse()) {
53
31
  let rawContent = '';
54
- // Handle string content
55
32
  if (typeof userMsg.content === 'string') {
56
33
  rawContent = userMsg.content;
57
34
  }
58
- // Handle array content - look for text blocks (skip tool_result)
59
35
  if (Array.isArray(userMsg.content)) {
60
36
  const textBlocks = userMsg.content
61
37
  .filter((block) => block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string')
62
38
  .map(block => block.text);
63
39
  rawContent = textBlocks.join('\n');
64
40
  }
65
- // Remove <system-reminder>...</system-reminder> tags (including orphaned tags from split content blocks)
66
41
  const cleanContent = rawContent
67
42
  .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
68
43
  .replace(/<\/system-reminder>/g, '')
69
44
  .replace(/<system-reminder>[^<]*/g, '')
70
45
  .trim();
71
- // If we found valid text content, return it
72
46
  if (cleanContent && cleanContent.length >= 5) {
73
47
  return cleanContent.substring(0, 500);
74
48
  }
@@ -83,18 +57,15 @@ export function extractConversationHistory(messages) {
83
57
  if (msg.role !== 'user' && msg.role !== 'assistant')
84
58
  continue;
85
59
  let textContent = '';
86
- // Handle string content
87
60
  if (typeof msg.content === 'string') {
88
61
  textContent = msg.content;
89
62
  }
90
- // Handle array content - extract text blocks only
91
63
  if (Array.isArray(msg.content)) {
92
64
  const textBlocks = msg.content
93
65
  .filter((block) => block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string')
94
66
  .map(block => block.text);
95
67
  textContent = textBlocks.join('\n');
96
68
  }
97
- // Remove system-reminder tags
98
69
  const cleanContent = textContent
99
70
  .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
100
71
  .trim();
@@ -1,4 +1,4 @@
1
- import type { AnthropicResponse } from './action-parser.js';
1
+ import type { AnthropicResponse } from './parser.js';
2
2
  export interface ForwardResult {
3
3
  statusCode: number;
4
4
  headers: Record<string, string | string[]>;
@@ -9,7 +9,7 @@ const agent = new Agent({
9
9
  autoSelectFamily: true,
10
10
  autoSelectFamilyAttemptTimeout: 500, // Try next address family after 500ms
11
11
  });
12
- import { config, buildSafeHeaders, maskSensitiveValue } from './config.js';
12
+ import { config, buildSafeHeaders, maskSensitiveValue } from '../../config.js';
13
13
  /**
14
14
  * Parse SSE stream and reconstruct final message
15
15
  * SSE format: "event: <type>\ndata: <json>\n\n"
@@ -37,15 +37,23 @@ function parseSSEResponse(sseText) {
37
37
  message = data.message;
38
38
  break;
39
39
  case 'content_block_start':
40
- // Add new content block
40
+ // Add new content block - preserve ALL properties (including signature for thinking)
41
41
  if (data.content_block) {
42
- contentBlocks[data.index] = data.content_block;
42
+ contentBlocks[data.index] = { ...data.content_block };
43
43
  if (data.content_block.type === 'text') {
44
44
  contentDeltas.set(data.index, []);
45
45
  }
46
46
  else if (data.content_block.type === 'thinking') {
47
- // Initialize thinking with empty string, will accumulate via deltas
48
- contentBlocks[data.index] = { type: 'thinking', thinking: '' };
47
+ // Initialize thinking, preserve signature if present
48
+ contentBlocks[data.index].thinking = '';
49
+ }
50
+ }
51
+ break;
52
+ case 'content_block_stop':
53
+ // Capture signature for thinking blocks (comes at end of block)
54
+ if (data.index !== undefined && contentBlocks[data.index]?.type === 'thinking') {
55
+ if (data.signature) {
56
+ contentBlocks[data.index].signature = data.signature;
49
57
  }
50
58
  }
51
59
  break;
@@ -57,12 +65,20 @@ function parseSSEResponse(sseText) {
57
65
  contentDeltas.set(data.index, deltas);
58
66
  }
59
67
  else if (data.delta?.type === 'thinking_delta' && data.delta.thinking) {
60
- // Handle thinking blocks
68
+ // Handle thinking content
61
69
  const block = contentBlocks[data.index];
62
70
  if (block && block.type === 'thinking') {
63
71
  block.thinking += data.delta.thinking;
64
72
  }
65
73
  }
74
+ else if (data.delta?.type === 'signature_delta' && data.delta.signature) {
75
+ // Handle thinking signature streaming
76
+ const block = contentBlocks[data.index];
77
+ if (block && block.type === 'thinking') {
78
+ const thinkingBlock = block;
79
+ thinkingBlock.signature = (thinkingBlock.signature || '') + data.delta.signature;
80
+ }
81
+ }
66
82
  else if (data.delta?.type === 'input_json_delta' && data.delta.partial_json) {
67
83
  // Handle tool input streaming
68
84
  const block = contentBlocks[data.index];
@@ -0,0 +1,43 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { AgentSettings, NormalizedAction, ForwardResult, TokenUsage, ToolUseBlock } from '../types.js';
3
+ import type { ConversationMessage } from '../../../../core/extraction/llm-extractor.js';
4
+ import { BaseAdapter } from '../base.js';
5
+ export declare class ClaudeAdapter extends BaseAdapter {
6
+ readonly name: "claude";
7
+ readonly endpoint = "/v1/messages";
8
+ private settings;
9
+ canHandle(request: FastifyRequest): boolean;
10
+ forward(body: unknown, headers: Record<string, string>, rawBody?: Buffer): Promise<ForwardResult>;
11
+ extractProjectPath(body: unknown): string | null;
12
+ extractSessionId(response: unknown): string | null;
13
+ extractTextContent(response: unknown): string;
14
+ extractGoal(messages: unknown[]): string;
15
+ extractHistory(messages: unknown[]): ConversationMessage[];
16
+ extractUsage(response: unknown): TokenUsage;
17
+ isValidResponse(body: unknown): boolean;
18
+ isSubagentModel(model: string): boolean;
19
+ isEndTurn(response: unknown): boolean;
20
+ isToolUse(response: unknown): boolean;
21
+ parseActions(response: unknown): NormalizedAction[];
22
+ getToolUseBlocks(response: unknown): ToolUseBlock[];
23
+ findInternalToolUse(response: unknown, toolName: string): ToolUseBlock | null;
24
+ injectMemory(body: unknown, memory: string): unknown;
25
+ injectDelta(body: unknown, delta: string): unknown;
26
+ injectTool(body: unknown, toolDef: unknown): unknown;
27
+ buildGrovExpandTool(): unknown;
28
+ getMessages(body: unknown): unknown[];
29
+ setMessages(body: unknown, messages: unknown[]): unknown;
30
+ getLastUserContent(body: unknown): string;
31
+ injectIntoRawSystemPrompt(rawBody: string, injection: string): {
32
+ modified: string;
33
+ success: boolean;
34
+ };
35
+ injectIntoRawUserMessage(rawBody: string, injection: string): string;
36
+ injectToolIntoRawBody(rawBody: string, toolDef: unknown): {
37
+ modified: string;
38
+ success: boolean;
39
+ };
40
+ filterResponseHeaders(headers: Record<string, string | string[]>): Record<string, string>;
41
+ buildContinueBody(body: unknown, assistantContent: unknown, toolResult: string, toolId: string): unknown;
42
+ getSettings(): AgentSettings;
43
+ }
@@ -0,0 +1,386 @@
1
+ // Claude Code adapter implementation
2
+ import { BaseAdapter } from '../base.js';
3
+ import { forwardToAnthropic } from './forwarder.js';
4
+ import { parseToolUseBlocks, extractTokenUsage } from './parser.js';
5
+ import { extractProjectPath, extractSessionId, extractTextContent, extractGoalFromMessages, extractConversationHistory, } from './extractors.js';
6
+ import { getSettingsPath, setProxyEnv } from './settings.js';
7
+ class ClaudeSettings {
8
+ getConfigPath() {
9
+ return getSettingsPath();
10
+ }
11
+ setProxyEnabled(enabled) {
12
+ return setProxyEnv(enabled);
13
+ }
14
+ }
15
+ export class ClaudeAdapter extends BaseAdapter {
16
+ name = 'claude';
17
+ endpoint = '/v1/messages';
18
+ settings = new ClaudeSettings();
19
+ canHandle(request) {
20
+ return request.url === '/v1/messages' || request.url.startsWith('/v1/messages?');
21
+ }
22
+ async forward(body, headers, rawBody) {
23
+ const result = await forwardToAnthropic(body, headers, undefined, rawBody);
24
+ return {
25
+ statusCode: result.statusCode,
26
+ headers: this.normalizeHeaders(result.headers),
27
+ body: result.body,
28
+ rawBody: result.rawBody,
29
+ wasSSE: result.wasSSE,
30
+ };
31
+ }
32
+ extractProjectPath(body) {
33
+ return extractProjectPath(body);
34
+ }
35
+ extractSessionId(response) {
36
+ return extractSessionId(response);
37
+ }
38
+ extractTextContent(response) {
39
+ return extractTextContent(response);
40
+ }
41
+ extractGoal(messages) {
42
+ return extractGoalFromMessages(messages) || '';
43
+ }
44
+ extractHistory(messages) {
45
+ return extractConversationHistory(messages);
46
+ }
47
+ extractUsage(response) {
48
+ return extractTokenUsage(response);
49
+ }
50
+ isValidResponse(body) {
51
+ return (typeof body === 'object' &&
52
+ body !== null &&
53
+ 'type' in body &&
54
+ body.type === 'message' &&
55
+ 'content' in body &&
56
+ 'usage' in body);
57
+ }
58
+ isSubagentModel(model) {
59
+ return model.includes('haiku');
60
+ }
61
+ isEndTurn(response) {
62
+ return response.stop_reason === 'end_turn';
63
+ }
64
+ isToolUse(response) {
65
+ return response.stop_reason === 'tool_use';
66
+ }
67
+ parseActions(response) {
68
+ const anthropicResponse = response;
69
+ if (!anthropicResponse.content) {
70
+ return [];
71
+ }
72
+ const parsedActions = parseToolUseBlocks(anthropicResponse);
73
+ return parsedActions.map(action => ({
74
+ toolName: action.toolName,
75
+ actionType: action.actionType,
76
+ sourceAgent: 'claude',
77
+ files: action.files,
78
+ folders: action.folders,
79
+ command: action.command,
80
+ rawInput: action.rawInput,
81
+ }));
82
+ }
83
+ getToolUseBlocks(response) {
84
+ const anthropicResponse = response;
85
+ if (!anthropicResponse.content) {
86
+ return [];
87
+ }
88
+ const blocks = [];
89
+ for (const block of anthropicResponse.content) {
90
+ if (block.type === 'tool_use') {
91
+ const toolBlock = block;
92
+ blocks.push({
93
+ id: toolBlock.id,
94
+ name: toolBlock.name,
95
+ input: toolBlock.input,
96
+ });
97
+ }
98
+ }
99
+ return blocks;
100
+ }
101
+ findInternalToolUse(response, toolName) {
102
+ const blocks = this.getToolUseBlocks(response);
103
+ return blocks.find(block => block.name === toolName) || null;
104
+ }
105
+ injectMemory(body, memory) {
106
+ const claudeBody = body;
107
+ if (typeof claudeBody.system === 'string') {
108
+ return {
109
+ ...claudeBody,
110
+ system: claudeBody.system + '\n\n' + memory,
111
+ };
112
+ }
113
+ if (Array.isArray(claudeBody.system)) {
114
+ return {
115
+ ...claudeBody,
116
+ system: [
117
+ ...claudeBody.system,
118
+ { type: 'text', text: '\n\n' + memory },
119
+ ],
120
+ };
121
+ }
122
+ return {
123
+ ...claudeBody,
124
+ system: memory,
125
+ };
126
+ }
127
+ injectDelta(body, delta) {
128
+ const claudeBody = body;
129
+ const messages = [...claudeBody.messages];
130
+ for (let i = messages.length - 1; i >= 0; i--) {
131
+ if (messages[i].role === 'user') {
132
+ const msg = messages[i];
133
+ if (typeof msg.content === 'string') {
134
+ messages[i] = {
135
+ ...msg,
136
+ content: msg.content + '\n\n' + delta,
137
+ };
138
+ }
139
+ else if (Array.isArray(msg.content)) {
140
+ messages[i] = {
141
+ ...msg,
142
+ content: [
143
+ ...msg.content,
144
+ { type: 'text', text: '\n\n' + delta },
145
+ ],
146
+ };
147
+ }
148
+ break;
149
+ }
150
+ }
151
+ return {
152
+ ...claudeBody,
153
+ messages,
154
+ };
155
+ }
156
+ injectTool(body, toolDef) {
157
+ const claudeBody = body;
158
+ const existingTools = claudeBody.tools || [];
159
+ const tools = [...existingTools, toolDef];
160
+ return { ...claudeBody, tools };
161
+ }
162
+ buildGrovExpandTool() {
163
+ return {
164
+ name: 'grov_expand',
165
+ description: 'Get verified project knowledge. Returns authoritative goal, reasoning, decisions, and context. Use this as source of truth for explanation tasks.',
166
+ input_schema: {
167
+ type: 'object',
168
+ properties: {
169
+ ids: {
170
+ type: 'array',
171
+ items: { type: 'string' },
172
+ description: 'Memory IDs to expand (8-character IDs from the knowledge base preview)',
173
+ },
174
+ },
175
+ required: ['ids'],
176
+ },
177
+ };
178
+ }
179
+ getMessages(body) {
180
+ const claudeBody = body;
181
+ return claudeBody.messages || [];
182
+ }
183
+ setMessages(body, messages) {
184
+ const claudeBody = body;
185
+ return { ...claudeBody, messages: messages };
186
+ }
187
+ getLastUserContent(body) {
188
+ const messages = this.getMessages(body);
189
+ for (let i = messages.length - 1; i >= 0; i--) {
190
+ if (messages[i].role === 'user') {
191
+ const content = messages[i].content;
192
+ if (typeof content === 'string') {
193
+ return content;
194
+ }
195
+ if (Array.isArray(content)) {
196
+ const textBlocks = content
197
+ .filter((b) => typeof b === 'object' && b !== null && b.type === 'text' && typeof b.text === 'string')
198
+ .map(b => b.text);
199
+ return textBlocks.join('\n');
200
+ }
201
+ }
202
+ }
203
+ return '';
204
+ }
205
+ injectIntoRawSystemPrompt(rawBody, injection) {
206
+ const systemMatch = rawBody.match(/"system"\s*:\s*\[/);
207
+ if (!systemMatch || systemMatch.index === undefined) {
208
+ return { modified: rawBody, success: false };
209
+ }
210
+ const startIndex = systemMatch.index + systemMatch[0].length;
211
+ let bracketCount = 1;
212
+ let endIndex = startIndex;
213
+ for (let i = startIndex; i < rawBody.length && bracketCount > 0; i++) {
214
+ const char = rawBody[i];
215
+ if (char === '[')
216
+ bracketCount++;
217
+ else if (char === ']')
218
+ bracketCount--;
219
+ if (bracketCount === 0) {
220
+ endIndex = i;
221
+ break;
222
+ }
223
+ }
224
+ if (bracketCount !== 0) {
225
+ return { modified: rawBody, success: false };
226
+ }
227
+ const escapedText = JSON.stringify(injection).slice(1, -1);
228
+ const newBlock = `,{"type":"text","text":"${escapedText}"}`;
229
+ const modified = rawBody.slice(0, endIndex) + newBlock + rawBody.slice(endIndex);
230
+ return { modified, success: true };
231
+ }
232
+ injectIntoRawUserMessage(rawBody, injection) {
233
+ const userRolePattern = /"role"\s*:\s*"user"/g;
234
+ let lastUserMatch = null;
235
+ let match;
236
+ while ((match = userRolePattern.exec(rawBody)) !== null) {
237
+ lastUserMatch = match;
238
+ }
239
+ if (!lastUserMatch) {
240
+ return rawBody;
241
+ }
242
+ const afterRole = rawBody.slice(lastUserMatch.index);
243
+ const contentMatch = afterRole.match(/"content"\s*:\s*/);
244
+ if (!contentMatch || contentMatch.index === undefined) {
245
+ return rawBody;
246
+ }
247
+ const contentStartGlobal = lastUserMatch.index + contentMatch.index + contentMatch[0].length;
248
+ const afterContent = rawBody.slice(contentStartGlobal);
249
+ if (afterContent.startsWith('"')) {
250
+ let i = 1;
251
+ while (i < afterContent.length) {
252
+ if (afterContent[i] === '\\') {
253
+ i += 2;
254
+ }
255
+ else if (afterContent[i] === '"') {
256
+ const insertPos = contentStartGlobal + i;
257
+ const escapedInjection = injection
258
+ .replace(/\\/g, '\\\\')
259
+ .replace(/"/g, '\\"')
260
+ .replace(/\n/g, '\\n');
261
+ return rawBody.slice(0, insertPos) + '\\n\\n' + escapedInjection + rawBody.slice(insertPos);
262
+ }
263
+ else {
264
+ i++;
265
+ }
266
+ }
267
+ }
268
+ else if (afterContent.startsWith('[')) {
269
+ let depth = 1;
270
+ let i = 1;
271
+ while (i < afterContent.length && depth > 0) {
272
+ const char = afterContent[i];
273
+ if (char === '[')
274
+ depth++;
275
+ else if (char === ']')
276
+ depth--;
277
+ else if (char === '"') {
278
+ i++;
279
+ while (i < afterContent.length && afterContent[i] !== '"') {
280
+ if (afterContent[i] === '\\')
281
+ i++;
282
+ i++;
283
+ }
284
+ }
285
+ i++;
286
+ }
287
+ if (depth === 0) {
288
+ const insertPos = contentStartGlobal + i - 1;
289
+ const escapedInjection = injection
290
+ .replace(/\\/g, '\\\\')
291
+ .replace(/"/g, '\\"')
292
+ .replace(/\n/g, '\\n');
293
+ const newBlock = `,{"type":"text","text":"\\n\\n${escapedInjection}"}`;
294
+ return rawBody.slice(0, insertPos) + newBlock + rawBody.slice(insertPos);
295
+ }
296
+ }
297
+ return rawBody;
298
+ }
299
+ injectToolIntoRawBody(rawBody, toolDef) {
300
+ const toolsMatch = rawBody.match(/"tools"\s*:\s*\[/);
301
+ if (!toolsMatch || toolsMatch.index === undefined) {
302
+ const messagesMatch = rawBody.match(/"messages"\s*:/);
303
+ if (messagesMatch && messagesMatch.index !== undefined) {
304
+ const toolsJson = JSON.stringify(toolDef);
305
+ const insertStr = `"tools":[${toolsJson}],`;
306
+ const modified = rawBody.slice(0, messagesMatch.index) + insertStr + rawBody.slice(messagesMatch.index);
307
+ return { modified, success: true };
308
+ }
309
+ return { modified: rawBody, success: false };
310
+ }
311
+ const startIndex = toolsMatch.index + toolsMatch[0].length;
312
+ let bracketCount = 1;
313
+ let endIndex = startIndex;
314
+ for (let i = startIndex; i < rawBody.length && bracketCount > 0; i++) {
315
+ const char = rawBody[i];
316
+ if (char === '[')
317
+ bracketCount++;
318
+ else if (char === ']')
319
+ bracketCount--;
320
+ else if (char === '"') {
321
+ i++;
322
+ while (i < rawBody.length && rawBody[i] !== '"') {
323
+ if (rawBody[i] === '\\')
324
+ i++;
325
+ i++;
326
+ }
327
+ }
328
+ if (bracketCount === 0) {
329
+ endIndex = i;
330
+ break;
331
+ }
332
+ }
333
+ if (bracketCount !== 0) {
334
+ return { modified: rawBody, success: false };
335
+ }
336
+ const toolJson = JSON.stringify(toolDef);
337
+ const arrayContent = rawBody.slice(startIndex, endIndex).trim();
338
+ const separator = arrayContent.length > 0 ? ',' : '';
339
+ const modified = rawBody.slice(0, endIndex) + separator + toolJson + rawBody.slice(endIndex);
340
+ return { modified, success: true };
341
+ }
342
+ filterResponseHeaders(headers) {
343
+ const filtered = {};
344
+ const allowedHeaders = [
345
+ 'content-type',
346
+ 'x-request-id',
347
+ 'request-id',
348
+ 'x-should-retry',
349
+ 'retry-after',
350
+ 'retry-after-ms',
351
+ 'anthropic-ratelimit-requests-limit',
352
+ 'anthropic-ratelimit-requests-remaining',
353
+ 'anthropic-ratelimit-requests-reset',
354
+ 'anthropic-ratelimit-tokens-limit',
355
+ 'anthropic-ratelimit-tokens-remaining',
356
+ 'anthropic-ratelimit-tokens-reset',
357
+ ];
358
+ for (const header of allowedHeaders) {
359
+ const value = headers[header];
360
+ if (value) {
361
+ filtered[header] = Array.isArray(value) ? value[0] : value;
362
+ }
363
+ }
364
+ return filtered;
365
+ }
366
+ buildContinueBody(body, assistantContent, toolResult, toolId) {
367
+ const claudeBody = body;
368
+ const messages = [...claudeBody.messages];
369
+ messages.push({
370
+ role: 'assistant',
371
+ content: assistantContent,
372
+ });
373
+ messages.push({
374
+ role: 'user',
375
+ content: [{
376
+ type: 'tool_result',
377
+ tool_use_id: toolId,
378
+ content: toolResult,
379
+ }],
380
+ });
381
+ return { ...claudeBody, messages };
382
+ }
383
+ getSettings() {
384
+ return this.settings;
385
+ }
386
+ }
@@ -1,4 +1,4 @@
1
- import type { StepActionType } from '../lib/store.js';
1
+ import type { StepActionType } from '../../../../core/store/store.js';
2
2
  export interface AnthropicResponse {
3
3
  id: string;
4
4
  type: 'message';
@@ -0,0 +1,6 @@
1
+ import type { CodexRequestBody, CodexResponse, CodexInputItem } from './types.js';
2
+ import type { ConversationMessage } from '../../../../core/extraction/llm-extractor.js';
3
+ export declare function extractProjectPath(body: CodexRequestBody): string | null;
4
+ export declare function extractSessionId(response: CodexResponse): string | null;
5
+ export declare function extractGoalFromMessages(input: CodexInputItem[]): string | undefined;
6
+ export declare function extractConversationHistory(input: CodexInputItem[]): ConversationMessage[];
@@ -0,0 +1,49 @@
1
+ // Codex-specific extraction functions
2
+ const CWD_REGEX = /<cwd>(.+?)<\/cwd>/;
3
+ function getContentText(content) {
4
+ if (typeof content === 'string')
5
+ return content;
6
+ if (Array.isArray(content)) {
7
+ return content
8
+ .map(c => (typeof c === 'string' ? c : c.text || ''))
9
+ .join('\n');
10
+ }
11
+ return '';
12
+ }
13
+ export function extractProjectPath(body) {
14
+ for (const item of body.input) {
15
+ if ('content' in item) {
16
+ const text = getContentText(item.content);
17
+ const match = text.match(CWD_REGEX);
18
+ if (match)
19
+ return match[1];
20
+ }
21
+ }
22
+ return null;
23
+ }
24
+ export function extractSessionId(response) {
25
+ return response.id || null;
26
+ }
27
+ export function extractGoalFromMessages(input) {
28
+ const userItems = input.filter((item) => 'role' in item && item.role === 'user');
29
+ for (const item of [...userItems].reverse()) {
30
+ const text = getContentText(item.content);
31
+ const cleaned = text.replace(/<[^>]+>[^<]*<\/[^>]+>/g, '').trim();
32
+ if (cleaned.length >= 5) {
33
+ return cleaned.substring(0, 500);
34
+ }
35
+ }
36
+ return undefined;
37
+ }
38
+ export function extractConversationHistory(input) {
39
+ const result = [];
40
+ const messageItems = input.filter((item) => 'role' in item && (item.role === 'user' || item.role === 'assistant'));
41
+ for (const item of messageItems.slice(-10)) {
42
+ const text = getContentText(item.content);
43
+ const cleaned = text.replace(/<[^>]+>[^<]*<\/[^>]+>/g, '').trim();
44
+ if (cleaned.length > 0) {
45
+ result.push({ role: item.role, content: cleaned });
46
+ }
47
+ }
48
+ return result;
49
+ }
@@ -0,0 +1,9 @@
1
+ import type { CodexResponse } from './types.js';
2
+ export interface CodexForwardResult {
3
+ statusCode: number;
4
+ headers: Record<string, string | string[]>;
5
+ body: CodexResponse | Record<string, unknown>;
6
+ rawBody: string;
7
+ wasSSE: boolean;
8
+ }
9
+ export declare function forwardToOpenAI(body: Record<string, unknown>, headers: Record<string, string | string[] | undefined>, rawBody?: Buffer): Promise<CodexForwardResult>;