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,40 @@
1
+ // Parse Codex apply_patch format to extract file operations
2
+ const ADD_FILE_PREFIX = '*** Add File: ';
3
+ const UPDATE_FILE_PREFIX = '*** Update File: ';
4
+ const DELETE_FILE_PREFIX = '*** Delete File: ';
5
+ const MOVE_TO_PREFIX = '*** Move to: ';
6
+ export function parsePatchContent(patchText) {
7
+ const files = [];
8
+ const operations = [];
9
+ let hasAdd = false;
10
+ let hasDelete = false;
11
+ let currentOp = null;
12
+ for (const line of patchText.split('\n')) {
13
+ if (line.startsWith(ADD_FILE_PREFIX)) {
14
+ const file = line.slice(ADD_FILE_PREFIX.length).trim();
15
+ files.push(file);
16
+ hasAdd = true;
17
+ currentOp = { type: 'add', file };
18
+ operations.push(currentOp);
19
+ }
20
+ else if (line.startsWith(UPDATE_FILE_PREFIX)) {
21
+ const file = line.slice(UPDATE_FILE_PREFIX.length).trim();
22
+ files.push(file);
23
+ currentOp = { type: 'update', file };
24
+ operations.push(currentOp);
25
+ }
26
+ else if (line.startsWith(DELETE_FILE_PREFIX)) {
27
+ const file = line.slice(DELETE_FILE_PREFIX.length).trim();
28
+ files.push(file);
29
+ hasDelete = true;
30
+ currentOp = { type: 'delete', file };
31
+ operations.push(currentOp);
32
+ }
33
+ else if (line.startsWith(MOVE_TO_PREFIX) && currentOp) {
34
+ const moveTo = line.slice(MOVE_TO_PREFIX.length).trim();
35
+ currentOp.moveTo = moveTo;
36
+ files.push(moveTo);
37
+ }
38
+ }
39
+ return { files: [...new Set(files)], operations, hasAdd, hasDelete };
40
+ }
@@ -0,0 +1,18 @@
1
+ interface ModelProvider {
2
+ name: string;
3
+ base_url: string;
4
+ env_key: string;
5
+ wire_api: 'responses' | 'chat';
6
+ query_params?: Record<string, string>;
7
+ http_headers?: Record<string, string>;
8
+ }
9
+ interface CodexConfig {
10
+ model_providers?: Record<string, ModelProvider>;
11
+ [key: string]: unknown;
12
+ }
13
+ export declare function getSettingsPath(): string;
14
+ export declare function readCodexConfig(): CodexConfig;
15
+ export declare function setProxyEnv(enable: boolean): {
16
+ action: 'added' | 'removed' | 'unchanged';
17
+ };
18
+ export {};
@@ -0,0 +1,73 @@
1
+ // Codex CLI settings management (~/.codex/config.toml)
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+ import { parse, stringify } from 'smol-toml';
6
+ const CODEX_DIR = join(homedir(), '.codex');
7
+ const CONFIG_PATH = join(CODEX_DIR, 'config.toml');
8
+ const PROXY_URL = 'http://127.0.0.1:8080/v1';
9
+ export function getSettingsPath() {
10
+ return CONFIG_PATH;
11
+ }
12
+ export function readCodexConfig() {
13
+ if (!existsSync(CONFIG_PATH)) {
14
+ return {};
15
+ }
16
+ try {
17
+ const content = readFileSync(CONFIG_PATH, 'utf-8');
18
+ return parse(content);
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ function writeCodexConfig(config) {
25
+ if (!existsSync(CODEX_DIR)) {
26
+ mkdirSync(CODEX_DIR, { recursive: true, mode: 0o700 });
27
+ }
28
+ writeFileSync(CONFIG_PATH, stringify(config), { mode: 0o600 });
29
+ }
30
+ export function setProxyEnv(enable) {
31
+ const config = readCodexConfig();
32
+ if (enable) {
33
+ if (!config.model_providers) {
34
+ config.model_providers = {};
35
+ }
36
+ const alreadyConfigured = config.model_providers.grov?.base_url === PROXY_URL &&
37
+ config.model_provider === 'grov';
38
+ if (alreadyConfigured) {
39
+ return { action: 'unchanged' };
40
+ }
41
+ // Store original provider to restore on disable
42
+ if (config.model_provider && config.model_provider !== 'grov') {
43
+ config._grov_original_provider = config.model_provider;
44
+ }
45
+ config.model_providers.grov = {
46
+ name: 'Grov Memory Proxy',
47
+ base_url: PROXY_URL,
48
+ env_key: 'OPENAI_API_KEY',
49
+ wire_api: 'responses',
50
+ };
51
+ // Tell Codex to use the grov provider
52
+ config.model_provider = 'grov';
53
+ writeCodexConfig(config);
54
+ return { action: 'added' };
55
+ }
56
+ if (!config.model_providers?.grov) {
57
+ return { action: 'unchanged' };
58
+ }
59
+ delete config.model_providers.grov;
60
+ // Restore original provider or remove the setting
61
+ if (config._grov_original_provider) {
62
+ config.model_provider = config._grov_original_provider;
63
+ delete config._grov_original_provider;
64
+ }
65
+ else {
66
+ delete config.model_provider;
67
+ }
68
+ if (Object.keys(config.model_providers).length === 0) {
69
+ delete config.model_providers;
70
+ }
71
+ writeCodexConfig(config);
72
+ return { action: 'removed' };
73
+ }
@@ -0,0 +1,59 @@
1
+ export interface CodexRequestBody {
2
+ model: string;
3
+ instructions?: string;
4
+ input: CodexInputItem[];
5
+ previous_response_id?: string;
6
+ stream?: boolean;
7
+ tools?: CodexTool[];
8
+ parallel_tool_calls?: boolean;
9
+ }
10
+ export type CodexInputItem = {
11
+ role: 'user' | 'assistant';
12
+ content: string | Array<{
13
+ type: string;
14
+ text?: string;
15
+ [key: string]: unknown;
16
+ }>;
17
+ } | {
18
+ type: 'function_call_output';
19
+ call_id: string;
20
+ output: string;
21
+ };
22
+ export interface CodexTool {
23
+ type: 'function';
24
+ function: {
25
+ name: string;
26
+ description?: string;
27
+ parameters?: Record<string, unknown>;
28
+ };
29
+ }
30
+ export interface CodexResponse {
31
+ id: string;
32
+ status: 'completed' | 'failed' | 'in_progress';
33
+ output: CodexOutputItem[];
34
+ usage?: {
35
+ input_tokens: number;
36
+ output_tokens: number;
37
+ total_tokens?: number;
38
+ };
39
+ }
40
+ export type CodexOutputItem = {
41
+ type: 'message';
42
+ role: 'assistant';
43
+ content: CodexMessageContent[];
44
+ } | CodexFunctionCall;
45
+ export interface CodexMessageContent {
46
+ type: 'output_text';
47
+ text: string;
48
+ }
49
+ export interface CodexFunctionCall {
50
+ type: 'function_call';
51
+ call_id: string;
52
+ name: string;
53
+ arguments: string;
54
+ }
55
+ export interface ShellArguments {
56
+ command: string[];
57
+ workdir: string;
58
+ timeout?: number;
59
+ }
@@ -0,0 +1,2 @@
1
+ // Codex API request and response type definitions
2
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { AgentAdapter, AgentName } from './types.js';
3
+ export declare function getAllAgents(): AgentAdapter[];
4
+ export declare function getAgentForRequest(request: FastifyRequest): AgentAdapter | undefined;
5
+ export declare function getAgentByName(name: AgentName): AgentAdapter | undefined;
6
+ export declare function getAgentByEndpoint(endpoint: string): AgentAdapter | undefined;
7
+ export declare function getSupportedEndpoints(): string[];
8
+ export { ClaudeAdapter } from './claude/index.js';
9
+ export { CodexAdapter } from './codex/index.js';
10
+ export { BaseAdapter } from './base.js';
11
+ export type { AgentAdapter, AgentName, AgentSettings, NormalizedAction, ForwardResult, TokenUsage, ToolUseBlock, } from './types.js';
@@ -0,0 +1,25 @@
1
+ // Agent registry - detects and provides agent adapters
2
+ import { ClaudeAdapter } from './claude/index.js';
3
+ import { CodexAdapter } from './codex/index.js';
4
+ const adapters = [
5
+ new ClaudeAdapter(),
6
+ new CodexAdapter(),
7
+ ];
8
+ export function getAllAgents() {
9
+ return adapters;
10
+ }
11
+ export function getAgentForRequest(request) {
12
+ return adapters.find(adapter => adapter.canHandle(request));
13
+ }
14
+ export function getAgentByName(name) {
15
+ return adapters.find(adapter => adapter.name === name);
16
+ }
17
+ export function getAgentByEndpoint(endpoint) {
18
+ return adapters.find(adapter => adapter.endpoint === endpoint);
19
+ }
20
+ export function getSupportedEndpoints() {
21
+ return adapters.map(adapter => adapter.endpoint);
22
+ }
23
+ export { ClaudeAdapter } from './claude/index.js';
24
+ export { CodexAdapter } from './codex/index.js';
25
+ export { BaseAdapter } from './base.js';
@@ -0,0 +1,77 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { StepActionType } from '../../../core/store/store.js';
3
+ import type { ConversationMessage } from '../../../core/extraction/llm-extractor.js';
4
+ export type AgentName = 'claude' | 'codex' | 'gemini';
5
+ export interface NormalizedAction {
6
+ toolName: string;
7
+ actionType: StepActionType;
8
+ sourceAgent: AgentName;
9
+ files: string[];
10
+ folders: string[];
11
+ command?: string;
12
+ rawInput: unknown;
13
+ }
14
+ export interface ForwardResult {
15
+ statusCode: number;
16
+ headers: Record<string, string>;
17
+ body: unknown;
18
+ rawBody: string;
19
+ wasSSE?: boolean;
20
+ }
21
+ export interface TokenUsage {
22
+ inputTokens: number;
23
+ outputTokens: number;
24
+ totalTokens: number;
25
+ cacheCreation: number;
26
+ cacheRead: number;
27
+ }
28
+ export interface ToolUseBlock {
29
+ id: string;
30
+ name: string;
31
+ input: unknown;
32
+ }
33
+ export interface AgentSettings {
34
+ getConfigPath(): string;
35
+ setProxyEnabled(enabled: boolean): {
36
+ action: 'added' | 'removed' | 'unchanged';
37
+ };
38
+ }
39
+ export interface AgentAdapter {
40
+ readonly name: AgentName;
41
+ readonly endpoint: string;
42
+ canHandle(request: FastifyRequest): boolean;
43
+ forward(body: unknown, headers: Record<string, string>, rawBody?: Buffer): Promise<ForwardResult>;
44
+ extractProjectPath(body: unknown): string | null;
45
+ extractSessionId(response: unknown): string | null;
46
+ extractTextContent(response: unknown): string;
47
+ extractGoal(messages: unknown[]): string;
48
+ extractHistory(messages: unknown[]): ConversationMessage[];
49
+ extractUsage(response: unknown): TokenUsage;
50
+ isValidResponse(body: unknown): boolean;
51
+ isSubagentModel(model: string): boolean;
52
+ isEndTurn(response: unknown): boolean;
53
+ isToolUse(response: unknown): boolean;
54
+ parseActions(response: unknown): NormalizedAction[];
55
+ getToolUseBlocks(response: unknown): ToolUseBlock[];
56
+ findInternalToolUse(response: unknown, toolName: string): ToolUseBlock | null;
57
+ injectMemory(body: unknown, memory: string): unknown;
58
+ injectDelta(body: unknown, delta: string): unknown;
59
+ injectTool(body: unknown, toolDef: unknown): unknown;
60
+ buildGrovExpandTool(): unknown;
61
+ getMessages(body: unknown): unknown[];
62
+ setMessages(body: unknown, messages: unknown[]): unknown;
63
+ getLastUserContent(body: unknown): string;
64
+ injectIntoRawSystemPrompt(rawBody: string, injection: string): {
65
+ modified: string;
66
+ success: boolean;
67
+ };
68
+ injectIntoRawUserMessage(rawBody: string, injection: string): string;
69
+ injectToolIntoRawBody(rawBody: string, toolDef: unknown): {
70
+ modified: string;
71
+ success: boolean;
72
+ };
73
+ filterResponseHeaders(headers: Record<string, string | string[]>): Record<string, string>;
74
+ buildContinueBody(body: unknown, assistantContent: unknown, toolResult: string, toolId: string): unknown;
75
+ getResponseContentType(wasSSE: boolean): string;
76
+ getSettings(): AgentSettings;
77
+ }
@@ -0,0 +1,2 @@
1
+ // Agent adapter types for multi-agent proxy support
2
+ export {};
@@ -1,6 +1,6 @@
1
1
  // Extended Cache - Keep Anthropic cache alive during idle
2
2
  // Sends minimal keep-alive requests to prevent cache TTL expiration
3
- import { forwardToAnthropic } from './forwarder.js';
3
+ import { forwardToAnthropic, isForwardError } from '../agents/claude/forwarder.js';
4
4
  export const extendedCache = new Map();
5
5
  // Timing constants
6
6
  const EXTENDED_CACHE_IDLE_THRESHOLD = 4 * 60 * 1000; // 4 minutes (under 5-min TTL)
@@ -94,10 +94,6 @@ async function sendExtendedCacheKeepAlive(projectPath, entry) {
94
94
  if (result.statusCode !== 200) {
95
95
  throw new Error(`Keep-alive failed: ${result.statusCode}`);
96
96
  }
97
- // Log cache metrics (minimal)
98
- const usage = result.body.usage;
99
- const cacheRead = usage?.cache_read_input_tokens || 0;
100
- console.log(`[CACHE] keep-alive ${projectName}: read=${cacheRead}`);
101
97
  }
102
98
  export async function checkExtendedCache() {
103
99
  const now = Date.now();
@@ -133,7 +129,7 @@ export async function checkExtendedCache() {
133
129
  })
134
130
  .catch((err) => {
135
131
  extendedCache.delete(projectPath);
136
- const errMsg = err instanceof Error ? err.message : 'unknown';
132
+ const errMsg = isForwardError(err) ? err.message : (err instanceof Error ? err.message : 'unknown');
137
133
  console.error(`[CACHE] keep-alive ${projectName} failed: ${errMsg}`);
138
134
  });
139
135
  keepAlivePromises.push(promise);
@@ -9,7 +9,7 @@ export const config = {
9
9
  REQUEST_TIMEOUT: parseInt(process.env.REQUEST_TIMEOUT || '300000', 10), // 5 minutes
10
10
  BODY_LIMIT: parseInt(process.env.BODY_LIMIT || '10485760', 10), // 10MB
11
11
  // Drift settings
12
- DRIFT_CHECK_INTERVAL: parseInt(process.env.DRIFT_CHECK_INTERVAL || '3', 10),
12
+ DRIFT_CHECK_INTERVAL: parseInt(process.env.DRIFT_CHECK_INTERVAL || '5', 10),
13
13
  TOKEN_WARNING_THRESHOLD: parseInt(process.env.TOKEN_WARNING_THRESHOLD || '160000', 10), // 80%
14
14
  TOKEN_CLEAR_THRESHOLD: parseInt(process.env.TOKEN_CLEAR_THRESHOLD || '180000', 10), // 90%
15
15
  // Security (Phase 2 - disabled for local)
@@ -1,4 +1,4 @@
1
- import type { MessagesRequestBody } from '../types.js';
1
+ import type { AgentAdapter } from '../agents/types.js';
2
2
  export declare function getPendingPlanClear(): {
3
3
  projectPath: string;
4
4
  summary: string;
@@ -8,7 +8,7 @@ export declare function setPendingPlanClear(value: {
8
8
  summary: string;
9
9
  }): void;
10
10
  export declare function clearPendingPlan(): void;
11
- export declare function preProcessRequest(body: MessagesRequestBody, sessionInfo: {
11
+ export declare function preProcessRequest(adapter: AgentAdapter, body: unknown, sessionInfo: {
12
12
  sessionId: string;
13
13
  promptCount: number;
14
14
  projectPath: string;
@@ -17,4 +17,4 @@ export declare function preProcessRequest(body: MessagesRequestBody, sessionInfo
17
17
  }, detectRequestType: (messages: Array<{
18
18
  role: string;
19
19
  content: unknown;
20
- }>, projectPath: string) => 'first' | 'continuation' | 'retry'): Promise<MessagesRequestBody>;
20
+ }>, projectPath: string) => 'first' | 'continuation' | 'retry'): Promise<unknown>;
@@ -0,0 +1,194 @@
1
+ // Pre-process requests before forwarding to upstream API (agent-agnostic)
2
+ import { config } from '../config.js';
3
+ import { extractFilesFromMessages } from '../request-processor.js';
4
+ import { fetchTeamMemories } from '../../../core/cloud/api-client.js';
5
+ import { getSessionState, updateSessionState, markCleared, } from '../../../core/store/store.js';
6
+ import { isSyncEnabled, getSyncTeamId } from '../../../core/cloud/cloud-sync.js';
7
+ import { clearSessionState, cacheMemories, buildMemoryPreview, buildToolDescription, buildDriftRecoveryInjection, reconstructMessages, addInjectionRecord, commitPendingRecords, setCachedPreview, getCachedPreview, } from '../injection/memory-injection.js';
8
+ let pendingPlanClear = null;
9
+ export function getPendingPlanClear() {
10
+ return pendingPlanClear;
11
+ }
12
+ export function setPendingPlanClear(value) {
13
+ pendingPlanClear = value;
14
+ }
15
+ export function clearPendingPlan() {
16
+ pendingPlanClear = null;
17
+ }
18
+ export async function preProcessRequest(adapter, body, sessionInfo, logger, detectRequestType) {
19
+ let modified = { ...body };
20
+ // === WARMUP CHECK (FIRST - before any injection) ===
21
+ // Skip warmup requests entirely - no tool, no system prompt, nothing
22
+ const rawEarlyPrompt = adapter.getLastUserContent(modified);
23
+ const earlyUserPrompt = rawEarlyPrompt
24
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
25
+ .replace(/\\n["']?\s*<\/system-reminder>/g, '')
26
+ .replace(/<\/system-reminder>/g, '')
27
+ .trim();
28
+ if (earlyUserPrompt.startsWith('Warmup')) {
29
+ return modified;
30
+ }
31
+ // Save clean user prompt BEFORE any reconstruction/injection (agent-agnostic)
32
+ modified.__grovRawUserPrompt = earlyUserPrompt.substring(0, 500);
33
+ // === TOOL INJECTION (only for real user requests) ===
34
+ const toolDesc = buildToolDescription();
35
+ modified.__grovInjection = toolDesc;
36
+ modified.__grovInjectionCached = false;
37
+ // Add grov_expand tool to tools array (agent-specific format)
38
+ const toolDef = adapter.buildGrovExpandTool();
39
+ modified = adapter.injectTool(modified, toolDef);
40
+ const messages = adapter.getMessages(modified);
41
+ const requestType = detectRequestType(messages, sessionInfo.projectPath);
42
+ const sessionState = getSessionState(sessionInfo.sessionId);
43
+ // === COMMIT PENDING RECORDS FROM PREVIOUS TURN ===
44
+ // When a new turn starts, commit any pending records so reconstruction stays consistent
45
+ if (requestType === 'first') {
46
+ commitPendingRecords(sessionInfo.projectPath);
47
+ }
48
+ // === PLANNING CLEAR ===
49
+ if (pendingPlanClear && pendingPlanClear.projectPath === sessionInfo.projectPath) {
50
+ modified = adapter.setMessages(modified, []);
51
+ modified = adapter.injectMemory(modified, pendingPlanClear.summary);
52
+ pendingPlanClear = null;
53
+ clearSessionState(sessionInfo.projectPath);
54
+ return modified;
55
+ }
56
+ // === CLEAR MODE (token threshold) ===
57
+ if (sessionState) {
58
+ const currentTokenCount = sessionState.token_count || 0;
59
+ if (currentTokenCount > config.TOKEN_CLEAR_THRESHOLD && sessionState.pending_clear_summary) {
60
+ logger.info({
61
+ msg: 'CLEAR MODE ACTIVATED',
62
+ tokenCount: currentTokenCount,
63
+ threshold: config.TOKEN_CLEAR_THRESHOLD,
64
+ });
65
+ modified = adapter.setMessages(modified, []);
66
+ modified = adapter.injectMemory(modified, sessionState.pending_clear_summary);
67
+ markCleared(sessionInfo.sessionId);
68
+ updateSessionState(sessionInfo.sessionId, { pending_clear_summary: undefined });
69
+ clearSessionState(sessionInfo.projectPath);
70
+ return modified;
71
+ }
72
+ }
73
+ // Capture original position BEFORE reconstruction (for injection tracking)
74
+ const originalMessages = adapter.getMessages(modified);
75
+ let originalLastUserPos = originalMessages.length - 1;
76
+ for (let i = originalMessages.length - 1; i >= 0; i--) {
77
+ if (originalMessages[i]?.role === 'user') {
78
+ originalLastUserPos = i;
79
+ break;
80
+ }
81
+ }
82
+ // === RECONSTRUCT HISTORICAL INJECTIONS (for cache consistency) ===
83
+ const currentMessages = adapter.getMessages(modified);
84
+ const { messages: reconstructedMsgs, reconstructedCount } = reconstructMessages(currentMessages, sessionInfo.projectPath);
85
+ if (reconstructedCount > 0) {
86
+ modified = adapter.setMessages(modified, reconstructedMsgs);
87
+ modified.__grovReconstructedCount = reconstructedCount;
88
+ }
89
+ // Pass original position to orchestrator for tool_cycle tracking
90
+ modified.__grovOriginalLastUserPos = originalLastUserPos;
91
+ // === MEMORY PREVIEW INJECTION (new system) ===
92
+ if (requestType === 'first') {
93
+ const teamId = getSyncTeamId();
94
+ const rawUserPrompt = adapter.getLastUserContent(modified);
95
+ // Clean user prompt - strip Claude Code injections that pollute semantic search
96
+ const userPrompt = rawUserPrompt
97
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
98
+ .replace(/\\n["']?\s*<\/system-reminder>/g, '')
99
+ .replace(/<\/system-reminder>/g, '')
100
+ .replace(/^This session is being continued from a previous conversation[\s\S]*?Summary:/gi, '')
101
+ .replace(/^[\s\n"'\\]+/, '')
102
+ .trim();
103
+ const messagesForFiles = adapter.getMessages(modified);
104
+ const mentionedFiles = extractFilesFromMessages(messagesForFiles);
105
+ const syncEnabled = isSyncEnabled();
106
+ if (syncEnabled && teamId && userPrompt) {
107
+ try {
108
+ const memories = await fetchTeamMemories(teamId, sessionInfo.projectPath, {
109
+ status: 'complete',
110
+ limit: 3,
111
+ context: userPrompt,
112
+ current_files: mentionedFiles.length > 0 ? mentionedFiles : undefined,
113
+ });
114
+ if (memories.length > 0) {
115
+ const memoryIds = memories.map(m => m.id.substring(0, 8)).join(', ');
116
+ console.log(`[MEMORY] ${memories.length} memories found: [${memoryIds}]`);
117
+ // Cache for expand tool
118
+ cacheMemories(sessionInfo.projectPath, memories);
119
+ // Build preview for user message
120
+ const preview = buildMemoryPreview(memories);
121
+ // Build drift/recovery if pending
122
+ const driftRecovery = buildDriftRecoveryInjection(sessionState?.pending_correction, sessionState?.pending_forced_recovery);
123
+ // Combine preview + drift/recovery
124
+ let userMsgInjection = preview || '';
125
+ if (driftRecovery) {
126
+ userMsgInjection = userMsgInjection ? `${userMsgInjection}\n${driftRecovery}` : driftRecovery;
127
+ }
128
+ if (userMsgInjection) {
129
+ modified.__grovUserMsgInjection = userMsgInjection;
130
+ setCachedPreview(sessionInfo.projectPath, userMsgInjection, messages.length);
131
+ // Track for reconstruction on next request (use ORIGINAL position)
132
+ addInjectionRecord(sessionInfo.projectPath, {
133
+ position: originalLastUserPos,
134
+ type: 'preview',
135
+ preview: userMsgInjection,
136
+ });
137
+ }
138
+ logger.info({
139
+ msg: 'Memory preview injected',
140
+ memoriesCount: memories.length,
141
+ previewSize: preview?.length || 0,
142
+ hasDriftRecovery: !!driftRecovery,
143
+ });
144
+ }
145
+ else {
146
+ // No memories found - inject explicit "no entries" so Claude doesn't use old previews
147
+ let noMemoriesMsg = '[PROJECT KNOWLEDGE BASE: No relevant entries for this query]';
148
+ const driftRecovery = buildDriftRecoveryInjection(sessionState?.pending_correction, sessionState?.pending_forced_recovery);
149
+ if (driftRecovery) {
150
+ noMemoriesMsg = `${noMemoriesMsg}\n${driftRecovery}`;
151
+ }
152
+ modified.__grovUserMsgInjection = noMemoriesMsg;
153
+ setCachedPreview(sessionInfo.projectPath, noMemoriesMsg, messages.length);
154
+ // Track for reconstruction
155
+ addInjectionRecord(sessionInfo.projectPath, {
156
+ position: originalLastUserPos,
157
+ type: 'preview',
158
+ preview: noMemoriesMsg,
159
+ });
160
+ }
161
+ // Clear pending corrections after injection
162
+ if (sessionState?.pending_correction || sessionState?.pending_forced_recovery) {
163
+ updateSessionState(sessionInfo.sessionId, {
164
+ pending_correction: undefined,
165
+ pending_forced_recovery: undefined,
166
+ });
167
+ }
168
+ }
169
+ catch (err) {
170
+ console.error(`[MEMORY] fetchTeamMemories error: ${err}`);
171
+ }
172
+ }
173
+ else {
174
+ // Sync not enabled - only inject drift/recovery if pending
175
+ const driftRecovery = buildDriftRecoveryInjection(sessionState?.pending_correction, sessionState?.pending_forced_recovery);
176
+ if (driftRecovery) {
177
+ modified.__grovUserMsgInjection = driftRecovery;
178
+ if (sessionState?.pending_correction || sessionState?.pending_forced_recovery) {
179
+ updateSessionState(sessionInfo.sessionId, {
180
+ pending_correction: undefined,
181
+ pending_forced_recovery: undefined,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ }
187
+ else if (requestType === 'retry') {
188
+ const cached = getCachedPreview(sessionInfo.projectPath, messages.length);
189
+ if (cached) {
190
+ modified.__grovUserMsgInjection = cached;
191
+ }
192
+ }
193
+ return modified;
194
+ }
@@ -0,0 +1,20 @@
1
+ // Grov Proxy CLI entry point
2
+ // Load .env file for API keys
3
+ import { config } from 'dotenv';
4
+ import { join } from 'path';
5
+ import { homedir } from 'os';
6
+ import { existsSync } from 'fs';
7
+ // Load from current directory .env first
8
+ config();
9
+ // Also load ~/.grov/.env as fallback
10
+ const grovEnvPath = join(homedir(), '.grov', '.env');
11
+ if (existsSync(grovEnvPath)) {
12
+ config({ path: grovEnvPath });
13
+ }
14
+ import { startServer } from './server.js';
15
+ // Note: Claude Code auth comes from request headers (no env var needed)
16
+ // Env loading above is for Codex future use and CLI tools
17
+ startServer().catch((err) => {
18
+ console.error('Proxy failed:', err);
19
+ process.exit(1);
20
+ });
@@ -0,0 +1,56 @@
1
+ import type { Memory } from '@grov/shared';
2
+ export interface InjectionRecord {
3
+ position: number;
4
+ type: 'preview' | 'tool_cycle';
5
+ preview?: string;
6
+ toolUse?: {
7
+ id: string;
8
+ name: string;
9
+ input: unknown;
10
+ };
11
+ toolResult?: string;
12
+ }
13
+ export interface SessionInjectionState {
14
+ memoriesById: Map<string, Memory>;
15
+ injectionHistory: InjectionRecord[];
16
+ pendingRecords: InjectionRecord[];
17
+ lastOriginalMsgCount: number;
18
+ cachedPreview?: {
19
+ preview: string;
20
+ msgCount: number;
21
+ };
22
+ }
23
+ export declare function getOrCreateState(sessionId: string): SessionInjectionState;
24
+ export declare function clearSessionState(sessionId: string): void;
25
+ export declare function cacheMemories(sessionId: string, memories: Memory[]): void;
26
+ export declare function getCachedMemoryById(sessionId: string, memoryId: string): Memory | null;
27
+ export declare function setCachedPreview(sessionId: string, preview: string, msgCount: number): void;
28
+ export declare function getCachedPreview(sessionId: string, currentMsgCount: number): string | null;
29
+ export declare function addInjectionRecord(sessionId: string, record: InjectionRecord): void;
30
+ export declare function commitPendingRecords(sessionId: string): number;
31
+ export declare function hasToolCycleAtPosition(sessionId: string, position: number): boolean;
32
+ export declare function formatAge(updatedAt: string | undefined): string;
33
+ export declare function buildMemoryPreview(memories: Memory[]): string | null;
34
+ export declare function buildExpandedMemory(memory: Memory): string;
35
+ export declare function buildToolDescription(): string;
36
+ export declare function buildDriftRecoveryInjection(pendingCorrection?: string, pendingForcedRecovery?: string): string | null;
37
+ type MessageContent = string | Array<{
38
+ type: string;
39
+ text?: string;
40
+ [key: string]: unknown;
41
+ }>;
42
+ export declare function appendTextToMessage(message: {
43
+ role: string;
44
+ content: MessageContent;
45
+ }, text: string): void;
46
+ export declare function reconstructMessages(messages: Array<{
47
+ role: string;
48
+ content: unknown;
49
+ }>, projectPath: string): {
50
+ messages: Array<{
51
+ role: string;
52
+ content: unknown;
53
+ }>;
54
+ reconstructedCount: number;
55
+ };
56
+ export {};