grov 0.5.11 → 0.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/agents/registry.d.ts +17 -0
- package/dist/cli/agents/registry.js +132 -0
- package/dist/cli/commands/agents.d.ts +1 -0
- package/dist/cli/commands/agents.js +48 -0
- package/dist/cli/commands/disable.d.ts +1 -0
- package/dist/cli/commands/disable.js +179 -0
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +157 -0
- package/dist/{commands → cli/commands}/drift-test.js +39 -26
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +90 -0
- package/dist/{commands → cli/commands}/login.js +19 -18
- package/dist/{commands → cli/commands}/logout.js +1 -1
- package/dist/{commands → cli/commands}/proxy-status.js +1 -1
- package/dist/cli/commands/setup.d.ts +6 -0
- package/dist/cli/commands/setup.js +309 -0
- package/dist/{commands → cli/commands}/status.js +1 -1
- package/dist/{commands → cli/commands}/sync.d.ts +1 -0
- package/dist/{commands → cli/commands}/sync.js +59 -4
- package/dist/{commands → cli/commands}/uninstall.js +2 -2
- package/dist/cli/index.js +270 -0
- package/dist/{lib → core/cloud}/cloud-sync.d.ts +3 -3
- package/dist/{lib → core/cloud}/cloud-sync.js +10 -10
- package/dist/{lib → core/extraction}/correction-builder-proxy.d.ts +1 -1
- package/dist/{lib → core/extraction}/correction-builder-proxy.js +0 -4
- package/dist/{lib → core/extraction}/drift-checker-proxy.d.ts +13 -9
- package/dist/core/extraction/drift-checker-proxy.js +510 -0
- package/dist/{lib → core/extraction}/llm-extractor.d.ts +8 -38
- package/dist/{lib → core/extraction}/llm-extractor.js +132 -220
- package/dist/{lib → core}/store/sessions.js +3 -19
- package/dist/core/store/store.d.ts +1 -0
- package/dist/{lib → core/store}/store.js +1 -1
- package/dist/{lib → core}/store/types.d.ts +0 -4
- package/dist/integrations/mcp/cache.d.ts +27 -0
- package/dist/integrations/mcp/cache.js +106 -0
- package/dist/integrations/mcp/capture/antigravity-parser.d.ts +26 -0
- package/dist/integrations/mcp/capture/antigravity-parser.js +272 -0
- package/dist/integrations/mcp/capture/antigravity-scanner.d.ts +24 -0
- package/dist/integrations/mcp/capture/antigravity-scanner.js +153 -0
- package/dist/integrations/mcp/capture/antigravity-sync-tracker.d.ts +29 -0
- package/dist/integrations/mcp/capture/antigravity-sync-tracker.js +115 -0
- package/dist/integrations/mcp/capture/cli-extractor.d.ts +18 -0
- package/dist/integrations/mcp/capture/cli-extractor.js +258 -0
- package/dist/integrations/mcp/capture/cli-synced.d.ts +4 -0
- package/dist/integrations/mcp/capture/cli-synced.js +62 -0
- package/dist/integrations/mcp/capture/cli-transform.d.ts +30 -0
- package/dist/integrations/mcp/capture/cli-transform.js +62 -0
- package/dist/integrations/mcp/capture/cli-watcher.d.ts +31 -0
- package/dist/integrations/mcp/capture/cli-watcher.js +106 -0
- package/dist/integrations/mcp/capture/hook-handler.d.ts +2 -0
- package/dist/integrations/mcp/capture/hook-handler.js +157 -0
- package/dist/integrations/mcp/capture/sqlite-reader.d.ts +35 -0
- package/dist/integrations/mcp/capture/sqlite-reader.js +388 -0
- package/dist/integrations/mcp/capture/sync-tracker.d.ts +16 -0
- package/dist/integrations/mcp/capture/sync-tracker.js +102 -0
- package/dist/integrations/mcp/clients/cursor/rules-installer.d.ts +19 -0
- package/dist/integrations/mcp/clients/cursor/rules-installer.js +123 -0
- package/dist/integrations/mcp/index.d.ts +1 -0
- package/dist/integrations/mcp/index.js +94 -0
- package/dist/integrations/mcp/logger.d.ts +8 -0
- package/dist/integrations/mcp/logger.js +50 -0
- package/dist/integrations/mcp/server.d.ts +5 -0
- package/dist/integrations/mcp/server.js +58 -0
- package/dist/integrations/mcp/tools/expand.d.ts +1 -0
- package/dist/integrations/mcp/tools/expand.js +53 -0
- package/dist/integrations/mcp/tools/preview.d.ts +1 -0
- package/dist/integrations/mcp/tools/preview.js +64 -0
- package/dist/integrations/proxy/agents/base.d.ts +43 -0
- package/dist/integrations/proxy/agents/base.js +13 -0
- package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.d.ts +4 -8
- package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.js +4 -33
- package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.d.ts +1 -1
- package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.js +22 -6
- package/dist/integrations/proxy/agents/claude/index.d.ts +43 -0
- package/dist/integrations/proxy/agents/claude/index.js +386 -0
- package/dist/{proxy/action-parser.d.ts → integrations/proxy/agents/claude/parser.d.ts} +1 -1
- package/dist/integrations/proxy/agents/codex/extractors.d.ts +6 -0
- package/dist/integrations/proxy/agents/codex/extractors.js +49 -0
- package/dist/integrations/proxy/agents/codex/forwarder.d.ts +9 -0
- package/dist/integrations/proxy/agents/codex/forwarder.js +125 -0
- package/dist/integrations/proxy/agents/codex/index.d.ts +44 -0
- package/dist/integrations/proxy/agents/codex/index.js +371 -0
- package/dist/integrations/proxy/agents/codex/parser.d.ts +11 -0
- package/dist/integrations/proxy/agents/codex/parser.js +104 -0
- package/dist/integrations/proxy/agents/codex/patch.d.ts +12 -0
- package/dist/integrations/proxy/agents/codex/patch.js +40 -0
- package/dist/integrations/proxy/agents/codex/settings.d.ts +18 -0
- package/dist/integrations/proxy/agents/codex/settings.js +73 -0
- package/dist/integrations/proxy/agents/codex/types.d.ts +59 -0
- package/dist/integrations/proxy/agents/codex/types.js +2 -0
- package/dist/integrations/proxy/agents/index.d.ts +11 -0
- package/dist/integrations/proxy/agents/index.js +25 -0
- package/dist/integrations/proxy/agents/types.d.ts +77 -0
- package/dist/integrations/proxy/agents/types.js +2 -0
- package/dist/{proxy → integrations/proxy/cache}/extended-cache.js +2 -6
- package/dist/{proxy → integrations/proxy}/config.js +1 -1
- package/dist/{proxy → integrations/proxy}/handlers/preprocess.d.ts +3 -3
- package/dist/integrations/proxy/handlers/preprocess.js +194 -0
- package/dist/integrations/proxy/index.js +20 -0
- package/dist/integrations/proxy/injection/memory-injection.d.ts +56 -0
- package/dist/integrations/proxy/injection/memory-injection.js +252 -0
- package/dist/integrations/proxy/orchestrator.d.ts +30 -0
- package/dist/integrations/proxy/orchestrator.js +954 -0
- package/dist/integrations/proxy/request-processor.d.ts +14 -0
- package/dist/integrations/proxy/request-processor.js +68 -0
- package/dist/{proxy → integrations/proxy}/response-processor.d.ts +4 -3
- package/dist/{proxy → integrations/proxy}/response-processor.js +51 -43
- package/dist/{proxy → integrations/proxy}/server.d.ts +0 -1
- package/dist/integrations/proxy/server.js +146 -0
- package/dist/{proxy → integrations/proxy}/types.d.ts +4 -0
- package/dist/{proxy → integrations/proxy}/utils/logging.d.ts +1 -0
- package/dist/{proxy → integrations/proxy}/utils/logging.js +5 -0
- package/package.json +31 -10
- package/postinstall.js +62 -6
- package/dist/cli.js +0 -149
- package/dist/commands/capture.d.ts +0 -6
- package/dist/commands/capture.js +0 -324
- package/dist/commands/disable.d.ts +0 -1
- package/dist/commands/disable.js +0 -14
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.js +0 -89
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +0 -52
- package/dist/commands/inject.d.ts +0 -5
- package/dist/commands/inject.js +0 -88
- package/dist/commands/prompt-inject.d.ts +0 -4
- package/dist/commands/prompt-inject.js +0 -451
- package/dist/commands/unregister.d.ts +0 -1
- package/dist/commands/unregister.js +0 -28
- package/dist/lib/anchor-extractor.d.ts +0 -30
- package/dist/lib/anchor-extractor.js +0 -296
- package/dist/lib/correction-builder.d.ts +0 -10
- package/dist/lib/correction-builder.js +0 -226
- package/dist/lib/drift-checker-proxy.js +0 -373
- package/dist/lib/drift-checker.d.ts +0 -66
- package/dist/lib/drift-checker.js +0 -341
- package/dist/lib/hooks.d.ts +0 -38
- package/dist/lib/hooks.js +0 -291
- package/dist/lib/jsonl-parser.d.ts +0 -87
- package/dist/lib/jsonl-parser.js +0 -281
- package/dist/lib/session-parser.d.ts +0 -44
- package/dist/lib/session-parser.js +0 -256
- package/dist/lib/store.d.ts +0 -1
- package/dist/proxy/cache.d.ts +0 -32
- package/dist/proxy/cache.js +0 -47
- package/dist/proxy/handlers/preprocess.js +0 -186
- package/dist/proxy/index.js +0 -30
- package/dist/proxy/injection/delta-tracking.d.ts +0 -11
- package/dist/proxy/injection/delta-tracking.js +0 -94
- package/dist/proxy/injection/injectors.d.ts +0 -7
- package/dist/proxy/injection/injectors.js +0 -139
- package/dist/proxy/request-processor.d.ts +0 -27
- package/dist/proxy/request-processor.js +0 -233
- package/dist/proxy/server.js +0 -1289
- /package/dist/{commands → cli/commands}/drift-test.d.ts +0 -0
- /package/dist/{commands → cli/commands}/login.d.ts +0 -0
- /package/dist/{commands → cli/commands}/logout.d.ts +0 -0
- /package/dist/{commands → cli/commands}/proxy-status.d.ts +0 -0
- /package/dist/{commands → cli/commands}/status.d.ts +0 -0
- /package/dist/{commands → cli/commands}/uninstall.d.ts +0 -0
- /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
- /package/dist/{lib → core/cloud}/api-client.d.ts +0 -0
- /package/dist/{lib → core/cloud}/api-client.js +0 -0
- /package/dist/{lib → core/cloud}/credentials.d.ts +0 -0
- /package/dist/{lib → core/cloud}/credentials.js +0 -0
- /package/dist/{lib → core}/store/convenience.d.ts +0 -0
- /package/dist/{lib → core}/store/convenience.js +0 -0
- /package/dist/{lib → core}/store/database.d.ts +0 -0
- /package/dist/{lib → core}/store/database.js +0 -0
- /package/dist/{lib → core}/store/drift.d.ts +0 -0
- /package/dist/{lib → core}/store/drift.js +0 -0
- /package/dist/{lib → core}/store/index.d.ts +0 -0
- /package/dist/{lib → core}/store/index.js +0 -0
- /package/dist/{lib → core}/store/sessions.d.ts +0 -0
- /package/dist/{lib → core}/store/steps.d.ts +0 -0
- /package/dist/{lib → core}/store/steps.js +0 -0
- /package/dist/{lib → core}/store/tasks.d.ts +0 -0
- /package/dist/{lib → core}/store/tasks.js +0 -0
- /package/dist/{lib → core}/store/types.js +0 -0
- /package/dist/{proxy/action-parser.js → integrations/proxy/agents/claude/parser.js} +0 -0
- /package/dist/{lib → integrations/proxy/agents/claude}/settings.d.ts +0 -0
- /package/dist/{lib → integrations/proxy/agents/claude}/settings.js +0 -0
- /package/dist/{proxy → integrations/proxy/cache}/extended-cache.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/config.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/index.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/types.js +0 -0
- /package/dist/{lib → utils}/debug.d.ts +0 -0
- /package/dist/{lib → utils}/debug.js +0 -0
- /package/dist/{lib → utils}/utils.d.ts +0 -0
- /package/dist/{lib → utils}/utils.js +0 -0
|
@@ -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;
|