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
@@ -0,0 +1,125 @@
1
+ // Forward requests to OpenAI API
2
+ import { request, Agent } from 'undici';
3
+ import { config } from '../../config.js';
4
+ const OPENAI_BASE_URL = 'https://api.openai.com';
5
+ const OPENAI_FORWARD_HEADERS = [
6
+ 'authorization',
7
+ 'openai-organization',
8
+ 'openai-project',
9
+ 'openai-beta',
10
+ ];
11
+ const agent = new Agent({
12
+ connect: { timeout: 30000 },
13
+ autoSelectFamily: true,
14
+ autoSelectFamilyAttemptTimeout: 500,
15
+ });
16
+ export async function forwardToOpenAI(body, headers, rawBody) {
17
+ const targetUrl = `${OPENAI_BASE_URL}/v1/responses`;
18
+ const safeHeaders = buildOpenAIHeaders(headers);
19
+ const requestBody = rawBody || JSON.stringify(body);
20
+ const response = await request(targetUrl, {
21
+ method: 'POST',
22
+ headers: {
23
+ ...safeHeaders,
24
+ 'content-type': 'application/json',
25
+ },
26
+ body: requestBody,
27
+ bodyTimeout: config.REQUEST_TIMEOUT,
28
+ headersTimeout: config.REQUEST_TIMEOUT,
29
+ dispatcher: agent,
30
+ });
31
+ const chunks = [];
32
+ for await (const chunk of response.body) {
33
+ chunks.push(Buffer.from(chunk));
34
+ }
35
+ const responseRawBody = Buffer.concat(chunks).toString('utf-8');
36
+ const contentType = response.headers['content-type'];
37
+ const isSSE = typeof contentType === 'string' && contentType.includes('text/event-stream');
38
+ let parsedBody;
39
+ if (isSSE) {
40
+ const sseResponse = parseOpenAISSE(responseRawBody);
41
+ parsedBody = sseResponse || { error: 'Failed to parse SSE response' };
42
+ }
43
+ else {
44
+ try {
45
+ parsedBody = JSON.parse(responseRawBody);
46
+ }
47
+ catch {
48
+ parsedBody = { error: 'Invalid JSON response' };
49
+ }
50
+ }
51
+ const responseHeaders = {};
52
+ for (const [key, value] of Object.entries(response.headers)) {
53
+ if (value !== undefined) {
54
+ responseHeaders[key] = value;
55
+ }
56
+ }
57
+ if (isSSE) {
58
+ responseHeaders['content-type'] = 'application/json';
59
+ }
60
+ return {
61
+ statusCode: response.statusCode,
62
+ headers: responseHeaders,
63
+ body: parsedBody,
64
+ rawBody: responseRawBody,
65
+ wasSSE: isSSE,
66
+ };
67
+ }
68
+ function buildOpenAIHeaders(headers) {
69
+ const result = {};
70
+ const lowerHeaders = {};
71
+ for (const [key, value] of Object.entries(headers)) {
72
+ lowerHeaders[key.toLowerCase()] = value;
73
+ }
74
+ for (const header of OPENAI_FORWARD_HEADERS) {
75
+ const value = lowerHeaders[header.toLowerCase()];
76
+ if (value) {
77
+ result[header] = Array.isArray(value) ? value[0] : value;
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ function parseOpenAISSE(sseText) {
83
+ let response = null;
84
+ const output = [];
85
+ for (const line of sseText.split('\n')) {
86
+ if (!line.startsWith('data: '))
87
+ continue;
88
+ const dataStr = line.slice(6);
89
+ if (dataStr === '[DONE]')
90
+ continue;
91
+ try {
92
+ const data = JSON.parse(dataStr);
93
+ if (data.type === 'response.created') {
94
+ response = {
95
+ id: data.response?.id,
96
+ status: 'in_progress',
97
+ output: [],
98
+ };
99
+ }
100
+ if (data.type === 'response.output_item.done' && data.item) {
101
+ output.push(data.item);
102
+ }
103
+ if (data.type === 'response.completed' && data.response) {
104
+ return {
105
+ id: data.response.id,
106
+ status: data.response.status,
107
+ output: data.response.output || output,
108
+ usage: data.response.usage,
109
+ };
110
+ }
111
+ }
112
+ catch {
113
+ continue;
114
+ }
115
+ }
116
+ if (response) {
117
+ return {
118
+ id: response.id || '',
119
+ status: 'completed',
120
+ output,
121
+ usage: response.usage,
122
+ };
123
+ }
124
+ return null;
125
+ }
@@ -0,0 +1,44 @@
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 CodexAdapter extends BaseAdapter {
6
+ readonly name: "codex";
7
+ readonly endpoint = "/v1/responses";
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
+ private parseToolInput;
44
+ }
@@ -0,0 +1,371 @@
1
+ // Codex CLI adapter implementation
2
+ import { BaseAdapter } from '../base.js';
3
+ import { forwardToOpenAI } from './forwarder.js';
4
+ import { parseCodexResponse } from './parser.js';
5
+ import { extractProjectPath, extractSessionId, extractGoalFromMessages, extractConversationHistory, } from './extractors.js';
6
+ import { getSettingsPath, setProxyEnv } from './settings.js';
7
+ class CodexSettings {
8
+ getConfigPath() {
9
+ return getSettingsPath();
10
+ }
11
+ setProxyEnabled(enabled) {
12
+ return setProxyEnv(enabled);
13
+ }
14
+ }
15
+ export class CodexAdapter extends BaseAdapter {
16
+ name = 'codex';
17
+ endpoint = '/v1/responses';
18
+ settings = new CodexSettings();
19
+ canHandle(request) {
20
+ return request.url === '/v1/responses' || request.url.startsWith('/v1/responses?');
21
+ }
22
+ async forward(body, headers, rawBody) {
23
+ const result = await forwardToOpenAI(body, headers, 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
+ const codexResponse = response;
40
+ const textParts = [];
41
+ for (const item of codexResponse.output) {
42
+ if (item.type === 'message' && item.content) {
43
+ for (const content of item.content) {
44
+ if (content.type === 'output_text') {
45
+ textParts.push(content.text);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ return textParts.join('\n');
51
+ }
52
+ extractGoal(messages) {
53
+ return extractGoalFromMessages(messages) || '';
54
+ }
55
+ extractHistory(messages) {
56
+ return extractConversationHistory(messages);
57
+ }
58
+ extractUsage(response) {
59
+ const codexResponse = response;
60
+ const usage = codexResponse.usage || { input_tokens: 0, output_tokens: 0 };
61
+ return {
62
+ inputTokens: usage.input_tokens,
63
+ outputTokens: usage.output_tokens,
64
+ totalTokens: usage.total_tokens || (usage.input_tokens + usage.output_tokens),
65
+ cacheCreation: 0,
66
+ cacheRead: 0,
67
+ };
68
+ }
69
+ isValidResponse(body) {
70
+ return (typeof body === 'object' &&
71
+ body !== null &&
72
+ 'id' in body &&
73
+ 'status' in body &&
74
+ 'output' in body);
75
+ }
76
+ isSubagentModel(model) {
77
+ return model.includes('mini');
78
+ }
79
+ isEndTurn(response) {
80
+ const codexResponse = response;
81
+ if (codexResponse.status !== 'completed')
82
+ return false;
83
+ const hasFunctionCall = codexResponse.output.some(item => item.type === 'function_call');
84
+ return !hasFunctionCall;
85
+ }
86
+ isToolUse(response) {
87
+ const codexResponse = response;
88
+ return codexResponse.output.some(item => item.type === 'function_call');
89
+ }
90
+ parseActions(response) {
91
+ const codexResponse = response;
92
+ if (!codexResponse.output) {
93
+ return [];
94
+ }
95
+ const parsedActions = parseCodexResponse(codexResponse);
96
+ return parsedActions.map(action => ({
97
+ toolName: action.toolName,
98
+ actionType: action.actionType,
99
+ sourceAgent: 'codex',
100
+ files: action.files,
101
+ folders: action.folders,
102
+ command: action.command,
103
+ rawInput: action.rawInput,
104
+ }));
105
+ }
106
+ getToolUseBlocks(response) {
107
+ const codexResponse = response;
108
+ return codexResponse.output
109
+ .filter((item) => item.type === 'function_call')
110
+ .map(item => ({
111
+ id: item.call_id,
112
+ name: item.name,
113
+ input: this.parseToolInput(item.arguments),
114
+ }));
115
+ }
116
+ findInternalToolUse(response, toolName) {
117
+ const blocks = this.getToolUseBlocks(response);
118
+ return blocks.find(block => block.name === toolName) || null;
119
+ }
120
+ injectMemory(body, memory) {
121
+ const codexBody = body;
122
+ return {
123
+ ...codexBody,
124
+ instructions: codexBody.instructions
125
+ ? codexBody.instructions + '\n\n' + memory
126
+ : memory,
127
+ };
128
+ }
129
+ injectDelta(body, delta) {
130
+ const codexBody = body;
131
+ const input = [...codexBody.input];
132
+ for (let i = input.length - 1; i >= 0; i--) {
133
+ const item = input[i];
134
+ if ('role' in item && item.role === 'user') {
135
+ input[i] = {
136
+ ...item,
137
+ content: item.content + '\n\n' + delta,
138
+ };
139
+ break;
140
+ }
141
+ }
142
+ return { ...codexBody, input };
143
+ }
144
+ injectTool(body, toolDef) {
145
+ const codexBody = body;
146
+ const existingTools = codexBody.tools || [];
147
+ const tools = [...existingTools, toolDef];
148
+ return { ...codexBody, tools };
149
+ }
150
+ buildGrovExpandTool() {
151
+ return {
152
+ type: 'function',
153
+ function: {
154
+ name: 'grov_expand',
155
+ description: 'Get verified project knowledge. Returns authoritative goal, reasoning, decisions, and context. Use this as source of truth for explanation tasks.',
156
+ parameters: {
157
+ type: 'object',
158
+ properties: {
159
+ ids: {
160
+ type: 'array',
161
+ items: { type: 'string' },
162
+ description: 'Memory IDs to expand (8-character IDs from the knowledge base preview)',
163
+ },
164
+ },
165
+ required: ['ids'],
166
+ },
167
+ },
168
+ };
169
+ }
170
+ getMessages(body) {
171
+ const codexBody = body;
172
+ return codexBody.input || [];
173
+ }
174
+ setMessages(body, messages) {
175
+ const codexBody = body;
176
+ return { ...codexBody, input: messages };
177
+ }
178
+ getLastUserContent(body) {
179
+ const input = this.getMessages(body);
180
+ for (let i = input.length - 1; i >= 0; i--) {
181
+ const item = input[i];
182
+ if ('role' in item && item.role === 'user') {
183
+ if (typeof item.content === 'string') {
184
+ return item.content;
185
+ }
186
+ if (Array.isArray(item.content)) {
187
+ // Handle specific Codex content array format
188
+ return item.content
189
+ .map(c => {
190
+ if (typeof c === 'string')
191
+ return c;
192
+ if (c && typeof c === 'object' && 'text' in c)
193
+ return c.text;
194
+ return '';
195
+ })
196
+ .join('\n');
197
+ }
198
+ return '';
199
+ }
200
+ }
201
+ return '';
202
+ }
203
+ injectIntoRawSystemPrompt(rawBody, injection) {
204
+ // Codex uses "instructions" as a string field, not an array
205
+ const instructionsMatch = rawBody.match(/"instructions"\s*:\s*"/);
206
+ if (instructionsMatch && instructionsMatch.index !== undefined) {
207
+ // Find the end of the instructions string
208
+ const startQuote = instructionsMatch.index + instructionsMatch[0].length - 1;
209
+ let i = startQuote + 1;
210
+ while (i < rawBody.length) {
211
+ if (rawBody[i] === '\\') {
212
+ i += 2;
213
+ }
214
+ else if (rawBody[i] === '"') {
215
+ const insertPos = i;
216
+ const escapedInjection = injection
217
+ .replace(/\\/g, '\\\\')
218
+ .replace(/"/g, '\\"')
219
+ .replace(/\n/g, '\\n');
220
+ const modified = rawBody.slice(0, insertPos) + '\\n\\n' + escapedInjection + rawBody.slice(insertPos);
221
+ return { modified, success: true };
222
+ }
223
+ else {
224
+ i++;
225
+ }
226
+ }
227
+ }
228
+ // No instructions field found - try to add one
229
+ const inputMatch = rawBody.match(/"input"\s*:/);
230
+ if (inputMatch && inputMatch.index !== undefined) {
231
+ const escapedInjection = injection
232
+ .replace(/\\/g, '\\\\')
233
+ .replace(/"/g, '\\"')
234
+ .replace(/\n/g, '\\n');
235
+ const insertStr = `"instructions":"${escapedInjection}",`;
236
+ const modified = rawBody.slice(0, inputMatch.index) + insertStr + rawBody.slice(inputMatch.index);
237
+ return { modified, success: true };
238
+ }
239
+ return { modified: rawBody, success: false };
240
+ }
241
+ injectIntoRawUserMessage(rawBody, injection) {
242
+ // Find the last user message in the input array
243
+ const userRolePattern = /"role"\s*:\s*"user"/g;
244
+ let lastUserMatch = null;
245
+ let match;
246
+ while ((match = userRolePattern.exec(rawBody)) !== null) {
247
+ lastUserMatch = match;
248
+ }
249
+ if (!lastUserMatch) {
250
+ return rawBody;
251
+ }
252
+ // Find "content" field after role
253
+ const afterRole = rawBody.slice(lastUserMatch.index);
254
+ const contentMatch = afterRole.match(/"content"\s*:\s*"/);
255
+ if (!contentMatch || contentMatch.index === undefined) {
256
+ return rawBody;
257
+ }
258
+ const contentStartGlobal = lastUserMatch.index + contentMatch.index + contentMatch[0].length - 1;
259
+ const afterContent = rawBody.slice(contentStartGlobal);
260
+ // Codex user content is always a string
261
+ let i = 1;
262
+ while (i < afterContent.length) {
263
+ if (afterContent[i] === '\\') {
264
+ i += 2;
265
+ }
266
+ else if (afterContent[i] === '"') {
267
+ const insertPos = contentStartGlobal + i;
268
+ const escapedInjection = injection
269
+ .replace(/\\/g, '\\\\')
270
+ .replace(/"/g, '\\"')
271
+ .replace(/\n/g, '\\n');
272
+ return rawBody.slice(0, insertPos) + '\\n\\n' + escapedInjection + rawBody.slice(insertPos);
273
+ }
274
+ else {
275
+ i++;
276
+ }
277
+ }
278
+ return rawBody;
279
+ }
280
+ injectToolIntoRawBody(rawBody, toolDef) {
281
+ const toolsMatch = rawBody.match(/"tools"\s*:\s*\[/);
282
+ if (!toolsMatch || toolsMatch.index === undefined) {
283
+ // No tools array - add one before input
284
+ const inputMatch = rawBody.match(/"input"\s*:/);
285
+ if (inputMatch && inputMatch.index !== undefined) {
286
+ const toolsJson = JSON.stringify(toolDef);
287
+ const insertStr = `"tools":[${toolsJson}],`;
288
+ const modified = rawBody.slice(0, inputMatch.index) + insertStr + rawBody.slice(inputMatch.index);
289
+ return { modified, success: true };
290
+ }
291
+ return { modified: rawBody, success: false };
292
+ }
293
+ // Find closing bracket
294
+ const startIndex = toolsMatch.index + toolsMatch[0].length;
295
+ let bracketCount = 1;
296
+ let endIndex = startIndex;
297
+ for (let i = startIndex; i < rawBody.length && bracketCount > 0; i++) {
298
+ const char = rawBody[i];
299
+ if (char === '[')
300
+ bracketCount++;
301
+ else if (char === ']')
302
+ bracketCount--;
303
+ else if (char === '"') {
304
+ i++;
305
+ while (i < rawBody.length && rawBody[i] !== '"') {
306
+ if (rawBody[i] === '\\')
307
+ i++;
308
+ i++;
309
+ }
310
+ }
311
+ if (bracketCount === 0) {
312
+ endIndex = i;
313
+ break;
314
+ }
315
+ }
316
+ if (bracketCount !== 0) {
317
+ return { modified: rawBody, success: false };
318
+ }
319
+ const toolJson = JSON.stringify(toolDef);
320
+ const arrayContent = rawBody.slice(startIndex, endIndex).trim();
321
+ const separator = arrayContent.length > 0 ? ',' : '';
322
+ const modified = rawBody.slice(0, endIndex) + separator + toolJson + rawBody.slice(endIndex);
323
+ return { modified, success: true };
324
+ }
325
+ filterResponseHeaders(headers) {
326
+ const filtered = {};
327
+ const allowedHeaders = [
328
+ 'content-type',
329
+ 'x-request-id',
330
+ 'request-id',
331
+ 'x-ratelimit-limit-requests',
332
+ 'x-ratelimit-limit-tokens',
333
+ 'x-ratelimit-remaining-requests',
334
+ 'x-ratelimit-remaining-tokens',
335
+ 'x-ratelimit-reset-requests',
336
+ 'x-ratelimit-reset-tokens',
337
+ ];
338
+ for (const header of allowedHeaders) {
339
+ const value = headers[header];
340
+ if (value) {
341
+ filtered[header] = Array.isArray(value) ? value[0] : value;
342
+ }
343
+ }
344
+ return filtered;
345
+ }
346
+ buildContinueBody(body, assistantContent, toolResult, toolId) {
347
+ const codexBody = body;
348
+ const input = [...codexBody.input];
349
+ input.push({
350
+ role: 'assistant',
351
+ content: JSON.stringify(assistantContent),
352
+ });
353
+ input.push({
354
+ type: 'function_call_output',
355
+ call_id: toolId,
356
+ output: toolResult,
357
+ });
358
+ return { ...codexBody, input };
359
+ }
360
+ getSettings() {
361
+ return this.settings;
362
+ }
363
+ parseToolInput(argumentsJson) {
364
+ try {
365
+ return JSON.parse(argumentsJson);
366
+ }
367
+ catch {
368
+ return {};
369
+ }
370
+ }
371
+ }
@@ -0,0 +1,11 @@
1
+ import type { StepActionType } from '../../../../core/store/store.js';
2
+ import type { CodexResponse } from './types.js';
3
+ export interface ParsedCodexAction {
4
+ toolName: string;
5
+ actionType: StepActionType;
6
+ files: string[];
7
+ folders: string[];
8
+ command?: string;
9
+ rawInput: unknown;
10
+ }
11
+ export declare function parseCodexResponse(response: CodexResponse): ParsedCodexAction[];
@@ -0,0 +1,104 @@
1
+ // Parse shell function calls from Codex responses
2
+ import { parsePatchContent } from './patch.js';
3
+ export function parseCodexResponse(response) {
4
+ const actions = [];
5
+ for (const item of response.output) {
6
+ if (item.type !== 'function_call' || item.name !== 'shell')
7
+ continue;
8
+ const args = parseShellArguments(item.arguments);
9
+ if (!args)
10
+ continue;
11
+ const action = parseShellCommand(args);
12
+ if (action)
13
+ actions.push(action);
14
+ }
15
+ return actions;
16
+ }
17
+ function parseShellArguments(argsJson) {
18
+ try {
19
+ return JSON.parse(argsJson);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ function parseShellCommand(args) {
26
+ const { command, workdir } = args;
27
+ if (command.length === 0)
28
+ return null;
29
+ const [cmd, ...cmdArgs] = command;
30
+ if (cmd === 'cat' || cmd === 'head' || cmd === 'tail') {
31
+ return {
32
+ toolName: `shell:${cmd}`,
33
+ actionType: 'read',
34
+ files: extractFilePaths(cmdArgs),
35
+ folders: [],
36
+ rawInput: args,
37
+ };
38
+ }
39
+ if (cmd === 'apply_patch') {
40
+ const patchContent = cmdArgs[0] || '';
41
+ const patchInfo = parsePatchContent(patchContent);
42
+ return {
43
+ toolName: 'shell:apply_patch',
44
+ actionType: patchInfo.hasAdd ? 'write' : 'edit',
45
+ files: patchInfo.files,
46
+ folders: [],
47
+ rawInput: args,
48
+ };
49
+ }
50
+ if (cmd === 'rg') {
51
+ if (cmdArgs.includes('--files')) {
52
+ return {
53
+ toolName: 'shell:rg',
54
+ actionType: 'glob',
55
+ files: [],
56
+ folders: extractPaths(cmdArgs),
57
+ rawInput: args,
58
+ };
59
+ }
60
+ return {
61
+ toolName: 'shell:rg',
62
+ actionType: 'grep',
63
+ files: [],
64
+ folders: extractPaths(cmdArgs),
65
+ rawInput: args,
66
+ };
67
+ }
68
+ if (cmd === 'ls') {
69
+ return {
70
+ toolName: 'shell:ls',
71
+ actionType: 'glob',
72
+ files: [],
73
+ folders: extractPaths(cmdArgs),
74
+ rawInput: args,
75
+ };
76
+ }
77
+ if (cmd === 'bash') {
78
+ const actualCmd = cmdArgs.includes('-lc')
79
+ ? cmdArgs[cmdArgs.indexOf('-lc') + 1]
80
+ : cmdArgs.join(' ');
81
+ return {
82
+ toolName: 'shell:bash',
83
+ actionType: 'bash',
84
+ files: [],
85
+ folders: [],
86
+ command: actualCmd,
87
+ rawInput: args,
88
+ };
89
+ }
90
+ return {
91
+ toolName: 'shell',
92
+ actionType: 'bash',
93
+ files: [],
94
+ folders: [],
95
+ command: command.join(' '),
96
+ rawInput: args,
97
+ };
98
+ }
99
+ function extractFilePaths(args) {
100
+ return args.filter(arg => !arg.startsWith('-') && (arg.includes('/') || arg.includes('.')));
101
+ }
102
+ function extractPaths(args) {
103
+ return args.filter(arg => !arg.startsWith('-') && arg.length > 0);
104
+ }
@@ -0,0 +1,12 @@
1
+ export interface PatchOperation {
2
+ type: 'add' | 'update' | 'delete';
3
+ file: string;
4
+ moveTo?: string;
5
+ }
6
+ export interface PatchInfo {
7
+ files: string[];
8
+ operations: PatchOperation[];
9
+ hasAdd: boolean;
10
+ hasDelete: boolean;
11
+ }
12
+ export declare function parsePatchContent(patchText: string): PatchInfo;