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