agent-relay 2.1.3 → 2.1.5
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/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-arm64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/dist/index.cjs +420 -184
- package/package.json +19 -19
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +4 -4
- package/packages/bridge/dist/cli-resolution.d.ts +32 -0
- package/packages/bridge/dist/cli-resolution.d.ts.map +1 -0
- package/packages/bridge/dist/cli-resolution.js +88 -0
- package/packages/bridge/dist/cli-resolution.js.map +1 -0
- package/packages/bridge/dist/index.d.ts +1 -0
- package/packages/bridge/dist/index.d.ts.map +1 -1
- package/packages/bridge/dist/index.js +2 -0
- package/packages/bridge/dist/index.js.map +1 -1
- package/packages/bridge/dist/spawner.d.ts.map +1 -1
- package/packages/bridge/dist/spawner.js +43 -17
- package/packages/bridge/dist/spawner.js.map +1 -1
- package/packages/bridge/package.json +8 -8
- package/packages/bridge/src/cli-resolution.test.ts +225 -0
- package/packages/bridge/src/cli-resolution.ts +100 -0
- package/packages/bridge/src/index.ts +9 -0
- package/packages/bridge/src/spawner.ts +44 -18
- package/packages/cli-tester/package.json +1 -1
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +2 -2
- package/packages/daemon/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +3 -3
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/packages/wrapper/dist/opencode-wrapper.d.ts +6 -2
- package/packages/wrapper/dist/opencode-wrapper.d.ts.map +1 -1
- package/packages/wrapper/dist/opencode-wrapper.js +34 -10
- package/packages/wrapper/dist/opencode-wrapper.js.map +1 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +22 -2
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.js +174 -4
- package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -1
- package/packages/wrapper/dist/shared.d.ts.map +1 -1
- package/packages/wrapper/dist/shared.js +5 -0
- package/packages/wrapper/dist/shared.js.map +1 -1
- package/packages/wrapper/package.json +6 -6
- package/packages/wrapper/src/opencode-wrapper.ts +37 -9
- package/packages/wrapper/src/relay-pty-orchestrator.ts +197 -4
- package/packages/wrapper/src/shared.ts +5 -0
- package/relay-snippets/agent-relay-snippet.md +17 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/bridge",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Multi-project bridge client utilities for Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/protocol": "2.1.
|
|
26
|
-
"@agent-relay/config": "2.1.
|
|
27
|
-
"@agent-relay/utils": "2.1.
|
|
28
|
-
"@agent-relay/policy": "2.1.
|
|
29
|
-
"@agent-relay/user-directory": "2.1.
|
|
30
|
-
"@agent-relay/wrapper": "2.1.
|
|
31
|
-
"@agent-relay/mcp": "2.1.
|
|
25
|
+
"@agent-relay/protocol": "2.1.5",
|
|
26
|
+
"@agent-relay/config": "2.1.5",
|
|
27
|
+
"@agent-relay/utils": "2.1.5",
|
|
28
|
+
"@agent-relay/policy": "2.1.5",
|
|
29
|
+
"@agent-relay/user-directory": "2.1.5",
|
|
30
|
+
"@agent-relay/wrapper": "2.1.5",
|
|
31
|
+
"@agent-relay/mcp": "2.1.5"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22.19.3",
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CLI Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Tests the detection and resolution of CLI commands for different providers,
|
|
5
|
+
* particularly the Cursor CLI which has two names: 'agent' (newer) and 'cursor-agent' (older).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import {
|
|
11
|
+
commandExists,
|
|
12
|
+
detectCursorCli,
|
|
13
|
+
resolveCli,
|
|
14
|
+
resetCursorCliCache,
|
|
15
|
+
CLI_COMMAND_MAP,
|
|
16
|
+
} from './cli-resolution.js';
|
|
17
|
+
|
|
18
|
+
// Mock child_process module
|
|
19
|
+
vi.mock('node:child_process', () => ({
|
|
20
|
+
execSync: vi.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const mockedExecSync = vi.mocked(execSync);
|
|
24
|
+
|
|
25
|
+
describe('CLI Resolution', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks();
|
|
28
|
+
resetCursorCliCache();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
vi.restoreAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('commandExists', () => {
|
|
36
|
+
it('returns true when command exists', () => {
|
|
37
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
38
|
+
expect(commandExists('agent')).toBe(true);
|
|
39
|
+
expect(mockedExecSync).toHaveBeenCalledWith('which agent', { stdio: 'ignore' });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('returns false when command does not exist', () => {
|
|
43
|
+
mockedExecSync.mockImplementation(() => {
|
|
44
|
+
throw new Error('Command not found');
|
|
45
|
+
});
|
|
46
|
+
expect(commandExists('nonexistent-cmd')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('uses "where" on Windows', () => {
|
|
50
|
+
const originalPlatform = process.platform;
|
|
51
|
+
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
52
|
+
|
|
53
|
+
mockedExecSync.mockReturnValue(Buffer.from('C:\\Path\\agent.exe'));
|
|
54
|
+
commandExists('agent');
|
|
55
|
+
expect(mockedExecSync).toHaveBeenCalledWith('where agent', { stdio: 'ignore' });
|
|
56
|
+
|
|
57
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('detectCursorCli', () => {
|
|
62
|
+
it('returns "agent" when agent command exists', () => {
|
|
63
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
64
|
+
expect(detectCursorCli()).toBe('agent');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns "cursor-agent" when only cursor-agent exists', () => {
|
|
68
|
+
mockedExecSync.mockImplementation((cmd: string) => {
|
|
69
|
+
if (cmd === 'which agent') {
|
|
70
|
+
throw new Error('not found');
|
|
71
|
+
}
|
|
72
|
+
return Buffer.from('/usr/bin/cursor-agent');
|
|
73
|
+
});
|
|
74
|
+
expect(detectCursorCli()).toBe('cursor-agent');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns null when neither command exists', () => {
|
|
78
|
+
mockedExecSync.mockImplementation(() => {
|
|
79
|
+
throw new Error('not found');
|
|
80
|
+
});
|
|
81
|
+
expect(detectCursorCli()).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('caches the detected CLI', () => {
|
|
85
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
86
|
+
|
|
87
|
+
// First call detects
|
|
88
|
+
expect(detectCursorCli()).toBe('agent');
|
|
89
|
+
expect(mockedExecSync).toHaveBeenCalledTimes(1);
|
|
90
|
+
|
|
91
|
+
// Second call uses cache
|
|
92
|
+
expect(detectCursorCli()).toBe('agent');
|
|
93
|
+
expect(mockedExecSync).toHaveBeenCalledTimes(1); // Still 1
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('cache can be reset', () => {
|
|
97
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
98
|
+
|
|
99
|
+
detectCursorCli();
|
|
100
|
+
expect(mockedExecSync).toHaveBeenCalledTimes(1);
|
|
101
|
+
|
|
102
|
+
resetCursorCliCache();
|
|
103
|
+
|
|
104
|
+
detectCursorCli();
|
|
105
|
+
expect(mockedExecSync).toHaveBeenCalledTimes(2);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('resolveCli', () => {
|
|
110
|
+
it('resolves "cursor" to detected CLI (agent)', () => {
|
|
111
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
112
|
+
expect(resolveCli('cursor')).toBe('agent');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('resolves "cursor" to detected CLI (cursor-agent)', () => {
|
|
116
|
+
mockedExecSync.mockImplementation((cmd: string) => {
|
|
117
|
+
if (cmd === 'which agent') {
|
|
118
|
+
throw new Error('not found');
|
|
119
|
+
}
|
|
120
|
+
return Buffer.from('/usr/bin/cursor-agent');
|
|
121
|
+
});
|
|
122
|
+
expect(resolveCli('cursor')).toBe('cursor-agent');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('resolves "cursor-agent" input to detected CLI', () => {
|
|
126
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
127
|
+
expect(resolveCli('cursor-agent')).toBe('agent');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('falls back to "agent" when cursor CLI not detected', () => {
|
|
131
|
+
mockedExecSync.mockImplementation(() => {
|
|
132
|
+
throw new Error('not found');
|
|
133
|
+
});
|
|
134
|
+
expect(resolveCli('cursor')).toBe('agent');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('is case insensitive for cursor', () => {
|
|
138
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
139
|
+
expect(resolveCli('CURSOR')).toBe('agent');
|
|
140
|
+
expect(resolveCli('Cursor')).toBe('agent');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('resolves "google" to "gemini"', () => {
|
|
144
|
+
expect(resolveCli('google')).toBe('gemini');
|
|
145
|
+
expect(resolveCli('Google')).toBe('gemini');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('returns other commands unchanged', () => {
|
|
149
|
+
expect(resolveCli('claude')).toBe('claude');
|
|
150
|
+
expect(resolveCli('codex')).toBe('codex');
|
|
151
|
+
expect(resolveCli('gemini')).toBe('gemini');
|
|
152
|
+
expect(resolveCli('opencode')).toBe('opencode');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('preserves case for unknown commands', () => {
|
|
156
|
+
expect(resolveCli('MyCustomCli')).toBe('MyCustomCli');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('CLI_COMMAND_MAP', () => {
|
|
161
|
+
it('maps cursor to agent', () => {
|
|
162
|
+
expect(CLI_COMMAND_MAP['cursor']).toBe('agent');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('maps cursor-agent to agent', () => {
|
|
166
|
+
expect(CLI_COMMAND_MAP['cursor-agent']).toBe('agent');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('maps google to gemini', () => {
|
|
170
|
+
expect(CLI_COMMAND_MAP['google']).toBe('gemini');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('CLI Resolution Integration', () => {
|
|
176
|
+
describe('Cursor CLI scenarios', () => {
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
vi.clearAllMocks();
|
|
179
|
+
resetCursorCliCache();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('handles user with newer Cursor (agent available)', () => {
|
|
183
|
+
// User has Cursor v0.50+ with "agent" CLI
|
|
184
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
185
|
+
|
|
186
|
+
const resolved = resolveCli('cursor');
|
|
187
|
+
expect(resolved).toBe('agent');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('handles user with older Cursor (cursor-agent available)', () => {
|
|
191
|
+
// User has older Cursor with "cursor-agent" CLI
|
|
192
|
+
mockedExecSync.mockImplementation((cmd: string) => {
|
|
193
|
+
if (cmd === 'which agent') {
|
|
194
|
+
throw new Error('not found');
|
|
195
|
+
}
|
|
196
|
+
return Buffer.from('/usr/bin/cursor-agent');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const resolved = resolveCli('cursor');
|
|
200
|
+
expect(resolved).toBe('cursor-agent');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('handles team spawn request with cursor CLI', () => {
|
|
204
|
+
// Lead agent spawns worker with "cursor" - should resolve to available CLI
|
|
205
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
206
|
+
|
|
207
|
+
// Simulate spawn request parsing
|
|
208
|
+
const cli = 'cursor';
|
|
209
|
+
const cliParts = cli.split(' ');
|
|
210
|
+
const rawCommand = cliParts[0];
|
|
211
|
+
const resolved = resolveCli(rawCommand);
|
|
212
|
+
|
|
213
|
+
expect(resolved).toBe('agent');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('handles explicit cursor-agent in spawn request', () => {
|
|
217
|
+
// User explicitly requests cursor-agent - should still check availability
|
|
218
|
+
mockedExecSync.mockReturnValue(Buffer.from('/usr/bin/agent'));
|
|
219
|
+
|
|
220
|
+
const resolved = resolveCli('cursor-agent');
|
|
221
|
+
// Even when asking for cursor-agent, if agent is available, use agent
|
|
222
|
+
expect(resolved).toBe('agent');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles mapping and detection of CLI commands for different providers.
|
|
5
|
+
* Cursor has two CLI names: 'agent' (newer) and 'cursor-agent' (older).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { createLogger } from '@agent-relay/utils/logger';
|
|
10
|
+
|
|
11
|
+
const log = createLogger('cli-resolution');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if a command exists in PATH
|
|
15
|
+
*/
|
|
16
|
+
export function commandExists(cmd: string): boolean {
|
|
17
|
+
try {
|
|
18
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
19
|
+
execSync(`${whichCmd} ${cmd}`, { stdio: 'ignore' });
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Cache for detected Cursor CLI command
|
|
27
|
+
let detectedCursorCli: string | null = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Reset the Cursor CLI detection cache.
|
|
31
|
+
* Useful for testing.
|
|
32
|
+
*/
|
|
33
|
+
export function resetCursorCliCache(): void {
|
|
34
|
+
detectedCursorCli = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect which Cursor CLI command is available.
|
|
39
|
+
* Newer versions use 'agent', older versions use 'cursor-agent'.
|
|
40
|
+
* Returns null if neither is found.
|
|
41
|
+
*/
|
|
42
|
+
export function detectCursorCli(): string | null {
|
|
43
|
+
if (detectedCursorCli !== null) {
|
|
44
|
+
return detectedCursorCli;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Try newer 'agent' command first
|
|
48
|
+
if (commandExists('agent')) {
|
|
49
|
+
detectedCursorCli = 'agent';
|
|
50
|
+
log.debug('Detected Cursor CLI: agent (newer version)');
|
|
51
|
+
return 'agent';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fall back to older 'cursor-agent' command
|
|
55
|
+
if (commandExists('cursor-agent')) {
|
|
56
|
+
detectedCursorCli = 'cursor-agent';
|
|
57
|
+
log.debug('Detected Cursor CLI: cursor-agent (older version)');
|
|
58
|
+
return 'cursor-agent';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
log.debug('Cursor CLI not found (neither agent nor cursor-agent)');
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve CLI command for a provider.
|
|
67
|
+
* For cursor, detects whether 'agent' or 'cursor-agent' is available.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveCli(rawCommand: string): string {
|
|
70
|
+
const cmdLower = rawCommand.toLowerCase();
|
|
71
|
+
|
|
72
|
+
// Handle cursor specially - detect which CLI is installed
|
|
73
|
+
if (cmdLower === 'cursor' || cmdLower === 'cursor-agent') {
|
|
74
|
+
const cursorCli = detectCursorCli();
|
|
75
|
+
if (cursorCli) {
|
|
76
|
+
return cursorCli;
|
|
77
|
+
}
|
|
78
|
+
// Fall back to 'agent' if detection fails (let it fail at spawn time)
|
|
79
|
+
return 'agent';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle other mappings
|
|
83
|
+
if (cmdLower === 'google') {
|
|
84
|
+
return 'gemini';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Return as-is for other commands
|
|
88
|
+
return rawCommand;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* CLI command mapping for providers (kept for reference, resolveCli handles logic)
|
|
93
|
+
* Maps provider names to actual CLI command names
|
|
94
|
+
*/
|
|
95
|
+
export const CLI_COMMAND_MAP: Record<string, string> = {
|
|
96
|
+
cursor: 'agent', // Cursor CLI installs as 'agent' (newer versions)
|
|
97
|
+
'cursor-agent': 'agent', // Cursor CLI older name, also maps to 'agent'
|
|
98
|
+
google: 'gemini', // Google provider uses 'gemini' CLI
|
|
99
|
+
// Other providers use their name as the command (claude, codex, etc.)
|
|
100
|
+
};
|
|
@@ -13,6 +13,15 @@ export {
|
|
|
13
13
|
type ShadowCliSelection,
|
|
14
14
|
} from './shadow-cli.js';
|
|
15
15
|
|
|
16
|
+
// CLI resolution utilities
|
|
17
|
+
export {
|
|
18
|
+
commandExists,
|
|
19
|
+
detectCursorCli,
|
|
20
|
+
resolveCli,
|
|
21
|
+
resetCursorCliCache,
|
|
22
|
+
CLI_COMMAND_MAP,
|
|
23
|
+
} from './cli-resolution.js';
|
|
24
|
+
|
|
16
25
|
// Agent spawner
|
|
17
26
|
export {
|
|
18
27
|
AgentSpawner,
|
|
@@ -9,6 +9,7 @@ import { execFile } from 'node:child_process';
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
import { sleep } from './utils.js';
|
|
12
|
+
import { resolveCli } from './cli-resolution.js';
|
|
12
13
|
import { getProjectPaths, getAgentOutboxTemplate } from '@agent-relay/config';
|
|
13
14
|
import { resolveCommand } from '@agent-relay/utils/command-resolver';
|
|
14
15
|
import { createTraceableError } from '@agent-relay/utils/error-tracking';
|
|
@@ -40,16 +41,6 @@ import type {
|
|
|
40
41
|
// Logger instance for spawner (uses daemon log system instead of console)
|
|
41
42
|
const log = createLogger('spawner');
|
|
42
43
|
|
|
43
|
-
/**
|
|
44
|
-
* CLI command mapping for providers
|
|
45
|
-
* Maps provider names to actual CLI command names
|
|
46
|
-
*/
|
|
47
|
-
const CLI_COMMAND_MAP: Record<string, string> = {
|
|
48
|
-
cursor: 'agent', // Cursor CLI installs as 'agent'
|
|
49
|
-
google: 'gemini', // Google provider uses 'gemini' CLI
|
|
50
|
-
// Other providers use their name as the command (claude, codex, etc.)
|
|
51
|
-
};
|
|
52
|
-
|
|
53
44
|
function extractGhTokenFromHosts(content: string): string | null {
|
|
54
45
|
const lines = content.split(/\r?\n/);
|
|
55
46
|
let inGithubSection = false;
|
|
@@ -796,10 +787,10 @@ export class AgentSpawner {
|
|
|
796
787
|
}
|
|
797
788
|
|
|
798
789
|
try {
|
|
799
|
-
// Parse CLI command and
|
|
790
|
+
// Parse CLI command and resolve actual command (e.g., cursor -> agent or cursor-agent)
|
|
800
791
|
const cliParts = cli.split(' ');
|
|
801
792
|
const rawCommandName = cliParts[0];
|
|
802
|
-
const commandName =
|
|
793
|
+
const commandName = resolveCli(rawCommandName);
|
|
803
794
|
const args = cliParts.slice(1);
|
|
804
795
|
|
|
805
796
|
if (commandName !== rawCommandName && debug) {
|
|
@@ -821,7 +812,7 @@ export class AgentSpawner {
|
|
|
821
812
|
// Add auto-accept flags for non-interactive agents (normal spawns, not setup terminals)
|
|
822
813
|
// When interactive=true (setup flows), we SKIP these flags so users can respond to prompts
|
|
823
814
|
const isClaudeCli = commandName.startsWith('claude');
|
|
824
|
-
const isCursorCli = commandName === 'agent' || rawCommandName === 'cursor';
|
|
815
|
+
const isCursorCli = commandName === 'agent' || rawCommandName === 'cursor' || rawCommandName === 'cursor-agent';
|
|
825
816
|
|
|
826
817
|
if (!interactive) {
|
|
827
818
|
// Add --dangerously-skip-permissions for Claude agents
|
|
@@ -1204,8 +1195,9 @@ export class AgentSpawner {
|
|
|
1204
1195
|
// Send task to the newly spawned agent if provided
|
|
1205
1196
|
if (task && task.trim()) {
|
|
1206
1197
|
const ready = await openCodeWrapper.waitUntilReadyForMessages(20000, 100);
|
|
1198
|
+
let taskSent = false;
|
|
1207
1199
|
if (ready) {
|
|
1208
|
-
|
|
1200
|
+
taskSent = await openCodeWrapper.injectTask(task, spawnerName || 'spawner');
|
|
1209
1201
|
if (!taskSent) {
|
|
1210
1202
|
log.warn(`Failed to inject task for ${name} via OpenCodeWrapper`);
|
|
1211
1203
|
} else if (debug) {
|
|
@@ -1214,6 +1206,28 @@ export class AgentSpawner {
|
|
|
1214
1206
|
} else {
|
|
1215
1207
|
log.warn(`OpenCodeWrapper ${name} not ready for task injection`);
|
|
1216
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
// If task injection failed, kill the agent and return error
|
|
1211
|
+
// An agent without its task is useless and will just sit idle
|
|
1212
|
+
if (!taskSent) {
|
|
1213
|
+
const tracedError = createTraceableError('Task injection failed', {
|
|
1214
|
+
agentName: name,
|
|
1215
|
+
cli,
|
|
1216
|
+
taskLength: task.length,
|
|
1217
|
+
ready,
|
|
1218
|
+
});
|
|
1219
|
+
log.error(`CRITICAL: ${tracedError.logMessage}`);
|
|
1220
|
+
await openCodeWrapper.stop();
|
|
1221
|
+
if (this.onClearSpawning) {
|
|
1222
|
+
this.onClearSpawning(name);
|
|
1223
|
+
}
|
|
1224
|
+
return {
|
|
1225
|
+
success: false,
|
|
1226
|
+
name,
|
|
1227
|
+
error: tracedError.userMessage,
|
|
1228
|
+
errorId: tracedError.errorId,
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1217
1231
|
}
|
|
1218
1232
|
|
|
1219
1233
|
// Track the worker (cast to AgentWrapper for type compatibility)
|
|
@@ -1380,14 +1394,15 @@ export class AgentSpawner {
|
|
|
1380
1394
|
await (pty as RelayPtyOrchestrator).waitUntilCliReady(15000, 100);
|
|
1381
1395
|
}
|
|
1382
1396
|
|
|
1383
|
-
// Inject task via socket (
|
|
1397
|
+
// Inject task via socket (relay-pty confirms write)
|
|
1384
1398
|
const success = await pty.injectTask(task, spawnerName || 'spawner');
|
|
1385
1399
|
if (success) {
|
|
1386
1400
|
taskSent = true;
|
|
1387
1401
|
if (debug) log.debug(`Task injected to ${name} (attempt ${attempt})`);
|
|
1388
1402
|
break;
|
|
1389
1403
|
} else {
|
|
1390
|
-
|
|
1404
|
+
// Delivery failed - safe to retry
|
|
1405
|
+
throw new Error('Task injection returned false - delivery failed');
|
|
1391
1406
|
}
|
|
1392
1407
|
} catch (err: any) {
|
|
1393
1408
|
// Log retry attempts at DEBUG level to avoid terminal noise
|
|
@@ -1407,8 +1422,19 @@ export class AgentSpawner {
|
|
|
1407
1422
|
taskLength: task.length,
|
|
1408
1423
|
});
|
|
1409
1424
|
log.error(`CRITICAL: ${tracedError.logMessage}`);
|
|
1410
|
-
//
|
|
1411
|
-
//
|
|
1425
|
+
// Kill the agent since it's useless without its task - it will just sit idle
|
|
1426
|
+
// Return an error so the caller knows the spawn effectively failed
|
|
1427
|
+
await pty.stop();
|
|
1428
|
+
this.activeWorkers.delete(name);
|
|
1429
|
+
if (this.onClearSpawning) {
|
|
1430
|
+
this.onClearSpawning(name);
|
|
1431
|
+
}
|
|
1432
|
+
return {
|
|
1433
|
+
success: false,
|
|
1434
|
+
name,
|
|
1435
|
+
error: tracedError.userMessage,
|
|
1436
|
+
errorId: tracedError.errorId,
|
|
1437
|
+
};
|
|
1412
1438
|
}
|
|
1413
1439
|
}
|
|
1414
1440
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/config",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Shared configuration schemas and loaders for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"test:watch": "vitest"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@agent-relay/protocol": "2.1.
|
|
86
|
+
"@agent-relay/protocol": "2.1.5",
|
|
87
87
|
"zod": "^3.23.8",
|
|
88
88
|
"zod-to-json-schema": "^3.23.1"
|
|
89
89
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/continuity",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Session continuity manager for Relay (ledgers, handoffs, resume)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/memory": "2.1.
|
|
25
|
+
"@agent-relay/memory": "2.1.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/daemon",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Relay daemon server - agent coordination and message routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/protocol": "2.1.
|
|
26
|
-
"@agent-relay/config": "2.1.
|
|
27
|
-
"@agent-relay/storage": "2.1.
|
|
28
|
-
"@agent-relay/bridge": "2.1.
|
|
29
|
-
"@agent-relay/utils": "2.1.
|
|
30
|
-
"@agent-relay/policy": "2.1.
|
|
31
|
-
"@agent-relay/memory": "2.1.
|
|
32
|
-
"@agent-relay/resiliency": "2.1.
|
|
33
|
-
"@agent-relay/user-directory": "2.1.
|
|
34
|
-
"@agent-relay/wrapper": "2.1.
|
|
35
|
-
"@agent-relay/telemetry": "2.1.
|
|
25
|
+
"@agent-relay/protocol": "2.1.5",
|
|
26
|
+
"@agent-relay/config": "2.1.5",
|
|
27
|
+
"@agent-relay/storage": "2.1.5",
|
|
28
|
+
"@agent-relay/bridge": "2.1.5",
|
|
29
|
+
"@agent-relay/utils": "2.1.5",
|
|
30
|
+
"@agent-relay/policy": "2.1.5",
|
|
31
|
+
"@agent-relay/memory": "2.1.5",
|
|
32
|
+
"@agent-relay/resiliency": "2.1.5",
|
|
33
|
+
"@agent-relay/user-directory": "2.1.5",
|
|
34
|
+
"@agent-relay/wrapper": "2.1.5",
|
|
35
|
+
"@agent-relay/telemetry": "2.1.5",
|
|
36
36
|
"ws": "^8.18.3",
|
|
37
37
|
"pg": "^8.16.3",
|
|
38
38
|
"uuid": "^10.0.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/hooks",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Hook emitter, registry, and trajectory hooks for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"test:watch": "vitest"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@agent-relay/protocol": "2.1.
|
|
41
|
-
"@agent-relay/config": "2.1.
|
|
42
|
-
"@agent-relay/trajectory": "2.1.
|
|
40
|
+
"@agent-relay/protocol": "2.1.5",
|
|
41
|
+
"@agent-relay/config": "2.1.5",
|
|
42
|
+
"@agent-relay/trajectory": "2.1.5"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/mcp",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "MCP server for Agent Relay - native messaging tools for AI agents in Claude, Cursor, and VS Code",
|
|
5
5
|
"author": "Agent Workforce Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"prepublishOnly": "npm run clean && npm run build && npm test"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@agent-relay/config": "2.1.
|
|
51
|
-
"@agent-relay/protocol": "2.1.
|
|
50
|
+
"@agent-relay/config": "2.1.5",
|
|
51
|
+
"@agent-relay/protocol": "2.1.5",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
53
53
|
"smol-toml": "^1.6.0",
|
|
54
54
|
"zod": "^3.23.8"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/memory",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Semantic memory storage and retrieval system for agent-relay with multiple backend support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/hooks": "2.1.
|
|
25
|
+
"@agent-relay/hooks": "2.1.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/policy",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Agent policy management with multi-level fallback (repo, local PRPM, cloud workspace)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/config": "2.1.
|
|
25
|
+
"@agent-relay/config": "2.1.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|