remote-codex 0.11.45 → 0.11.49
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/README.md +80 -10
- package/apps/relay-server/dist/index.js +1 -1
- package/apps/supervisor-api/dist/index.js +8370 -1501
- package/apps/supervisor-web/dist/assets/c-BIGW1oBm.js +1 -0
- package/apps/supervisor-web/dist/assets/{core-DODVy7wn.js → core-B0prQzhr.js} +1 -1
- package/apps/supervisor-web/dist/assets/cpp-DIPi6g--.js +1 -0
- package/apps/supervisor-web/dist/assets/csharp-DSvCPggb.js +1 -0
- package/apps/supervisor-web/dist/assets/go-C27-OAKa.js +1 -0
- package/apps/supervisor-web/dist/assets/index-DZI1aSXo.js +22 -0
- package/apps/supervisor-web/dist/assets/index-Dy8PgXgw.css +1 -0
- package/apps/supervisor-web/dist/assets/java-CylS5w8V.js +1 -0
- package/apps/supervisor-web/dist/assets/{markdown-vendor-RZk8L7-L.js → markdown-vendor-BG6PurxI.js} +33 -33
- package/apps/supervisor-web/dist/assets/ruby-B6AkvBWc.js +1 -0
- package/apps/supervisor-web/dist/assets/rust-B1yitclQ.js +1 -0
- package/apps/supervisor-web/dist/assets/{shellscript-CEILq0vU.js → shellscript-DfDnw5Jg.js} +1 -1
- package/apps/supervisor-web/dist/assets/thread-ui-gcslNXur.js +3968 -0
- package/apps/supervisor-web/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/apps/supervisor-web/dist/index.html +4 -4
- package/docs/windows-device-manager.zh.md +143 -0
- package/docs/windows-device-setup.zh.md +263 -0
- package/docs/windows-one-click-installer-research.zh.md +481 -0
- package/docs/windows.md +6 -0
- package/package.json +7 -2
- package/packages/acp/src/agent-catalog.test.ts +57 -0
- package/packages/acp/src/agent-catalog.ts +361 -0
- package/packages/acp/src/catalog-runtime.test.ts +99 -0
- package/packages/acp/src/catalog-runtime.ts +507 -0
- package/packages/acp/src/codex-environment.test.ts +58 -0
- package/packages/acp/src/codex-environment.ts +33 -0
- package/packages/acp/src/index.ts +5 -0
- package/packages/acp/src/item-mapper.test.ts +137 -0
- package/packages/acp/src/item-mapper.ts +473 -0
- package/packages/acp/src/runtimeAdapter.test.ts +79 -0
- package/packages/acp/src/runtimeAdapter.ts +1193 -0
- package/packages/acp/src/terminal-service.test.ts +31 -0
- package/packages/acp/src/terminal-service.ts +137 -0
- package/packages/agent-runtime/src/runtime-errors.ts +1 -0
- package/packages/agent-runtime/src/types.ts +8 -0
- package/packages/db/migrations/0030_thread_agent_id.sql +9 -0
- package/packages/db/src/repositories.ts +3 -0
- package/packages/db/src/schema.ts +1 -0
- package/packages/shared/src/agent-providers.ts +10 -1
- package/packages/shared/src/index.ts +24 -0
- package/scripts/windows/build-device-manager.ps1 +64 -0
- package/apps/supervisor-web/dist/assets/index-BO9S3vTX.css +0 -1
- package/apps/supervisor-web/dist/assets/index-GqVDOqbI.js +0 -22
- package/apps/supervisor-web/dist/assets/thread-ui-BWC_ljvN.js +0 -3714
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AcpAgentOptionMetadataDto,
|
|
3
|
+
} from '../../shared/src/index';
|
|
4
|
+
import type { AgentModel } from '../../agent-runtime/src/index';
|
|
5
|
+
import {
|
|
6
|
+
parseCommandLine,
|
|
7
|
+
resolveExecutable,
|
|
8
|
+
runProcess,
|
|
9
|
+
} from '../../process-runtime/src/index';
|
|
10
|
+
|
|
11
|
+
export interface AcpAgentDefinition {
|
|
12
|
+
id: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
description: string;
|
|
15
|
+
transport: AcpAgentOptionMetadataDto['transport'];
|
|
16
|
+
baseCommand: string;
|
|
17
|
+
baseProbeCommand: string;
|
|
18
|
+
serverCommand: string;
|
|
19
|
+
serverProbeCommand: string;
|
|
20
|
+
installCommand: string | null;
|
|
21
|
+
modelListCommand?: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AcpAgentCatalogEntry extends AcpAgentDefinition {
|
|
25
|
+
availability: AcpAgentOptionMetadataDto['availability'];
|
|
26
|
+
baseVersion: string | null;
|
|
27
|
+
serverVersion: string | null;
|
|
28
|
+
busy: boolean;
|
|
29
|
+
statusMessage: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const builtinAcpAgents: AcpAgentDefinition[] = [
|
|
33
|
+
{
|
|
34
|
+
id: 'grok',
|
|
35
|
+
displayName: 'Grok Build',
|
|
36
|
+
description: 'xAI Grok coding agent with native ACP support.',
|
|
37
|
+
transport: 'native',
|
|
38
|
+
baseCommand: 'grok',
|
|
39
|
+
baseProbeCommand: 'grok --version',
|
|
40
|
+
serverCommand: 'grok agent stdio',
|
|
41
|
+
serverProbeCommand: 'grok agent stdio --help',
|
|
42
|
+
installCommand: null,
|
|
43
|
+
modelListCommand: 'grok models',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'cursor',
|
|
47
|
+
displayName: 'Cursor Agent',
|
|
48
|
+
description: 'Cursor CLI coding agent with native ACP support.',
|
|
49
|
+
transport: 'native',
|
|
50
|
+
baseCommand: 'cursor-agent',
|
|
51
|
+
baseProbeCommand: 'cursor-agent --version',
|
|
52
|
+
serverCommand: 'cursor-agent acp',
|
|
53
|
+
serverProbeCommand: 'cursor-agent acp --help',
|
|
54
|
+
installCommand: null,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'codex',
|
|
58
|
+
displayName: 'OpenAI Codex',
|
|
59
|
+
description: 'Local Codex CLI connected through the ACP adapter.',
|
|
60
|
+
transport: 'adapter',
|
|
61
|
+
baseCommand: 'codex',
|
|
62
|
+
baseProbeCommand: 'codex --version',
|
|
63
|
+
serverCommand: 'codex-acp',
|
|
64
|
+
serverProbeCommand: 'codex-acp --version',
|
|
65
|
+
installCommand: 'npm install -g @agentclientprotocol/codex-acp@latest',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'claude',
|
|
69
|
+
displayName: 'Claude Agent',
|
|
70
|
+
description: 'Claude Code connected through the Claude Agent ACP adapter.',
|
|
71
|
+
transport: 'adapter',
|
|
72
|
+
baseCommand: 'claude',
|
|
73
|
+
baseProbeCommand: 'claude --version',
|
|
74
|
+
serverCommand: 'claude-agent-acp',
|
|
75
|
+
serverProbeCommand: 'claude-agent-acp --version',
|
|
76
|
+
installCommand: 'npm install -g @agentclientprotocol/claude-agent-acp@latest',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'gemini',
|
|
80
|
+
displayName: 'Gemini CLI',
|
|
81
|
+
description: 'Google Gemini CLI with native ACP support.',
|
|
82
|
+
transport: 'native',
|
|
83
|
+
baseCommand: 'gemini',
|
|
84
|
+
baseProbeCommand: 'gemini --version',
|
|
85
|
+
serverCommand: 'gemini --acp',
|
|
86
|
+
serverProbeCommand: 'gemini --acp --help',
|
|
87
|
+
installCommand: null,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'copilot',
|
|
91
|
+
displayName: 'GitHub Copilot CLI',
|
|
92
|
+
description: 'GitHub Copilot CLI with native ACP support.',
|
|
93
|
+
transport: 'native',
|
|
94
|
+
baseCommand: 'copilot',
|
|
95
|
+
baseProbeCommand: 'copilot --version',
|
|
96
|
+
serverCommand: 'copilot --acp',
|
|
97
|
+
serverProbeCommand: 'copilot --acp --help',
|
|
98
|
+
installCommand: null,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'opencode',
|
|
102
|
+
displayName: 'OpenCode',
|
|
103
|
+
description: 'OpenCode coding agent with native ACP support.',
|
|
104
|
+
transport: 'native',
|
|
105
|
+
baseCommand: 'opencode',
|
|
106
|
+
baseProbeCommand: 'opencode --version',
|
|
107
|
+
serverCommand: 'opencode acp',
|
|
108
|
+
serverProbeCommand: 'opencode acp --help',
|
|
109
|
+
installCommand: null,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'deepseek',
|
|
113
|
+
displayName: 'DeepSeek Harness',
|
|
114
|
+
description: 'DeepSeek Harness connected through its ACP bridge.',
|
|
115
|
+
transport: 'adapter',
|
|
116
|
+
baseCommand: 'dsh',
|
|
117
|
+
baseProbeCommand: 'dsh --version',
|
|
118
|
+
serverCommand: 'dsh-acp',
|
|
119
|
+
serverProbeCommand: 'dsh-acp --version',
|
|
120
|
+
installCommand: 'npm install -g @openma/deepseek-harness-acp@latest',
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
function firstOutputLine(value: string) {
|
|
125
|
+
return value
|
|
126
|
+
.split(/\r?\n/)
|
|
127
|
+
.map((line) => line.trim())
|
|
128
|
+
.find(Boolean) ?? null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function commandError(result: Awaited<ReturnType<typeof runProcess>>) {
|
|
132
|
+
return firstOutputLine(result.stderr) ?? firstOutputLine(result.stdout);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function probeCommand(commandLine: string, timeoutMs = 4_000) {
|
|
136
|
+
const parsed = parseCommandLine(commandLine);
|
|
137
|
+
const executable = await resolveExecutable(parsed.command);
|
|
138
|
+
if (!executable) {
|
|
139
|
+
return { available: false, version: null, error: null, executable: null };
|
|
140
|
+
}
|
|
141
|
+
const result = await runProcess({
|
|
142
|
+
command: parsed.command,
|
|
143
|
+
args: parsed.args,
|
|
144
|
+
timeoutMs,
|
|
145
|
+
maxOutputBytes: 64 * 1024,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
available: result.code === 0,
|
|
149
|
+
version: result.code === 0
|
|
150
|
+
? firstOutputLine(result.stdout) ?? firstOutputLine(result.stderr)
|
|
151
|
+
: null,
|
|
152
|
+
error: result.code === 0 ? null : commandError(result),
|
|
153
|
+
executable,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function customDefinition(command: string | null | undefined) {
|
|
158
|
+
const normalized = command?.trim();
|
|
159
|
+
if (!normalized || normalized === 'grok agent stdio') {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const parsed = parseCommandLine(normalized);
|
|
163
|
+
return {
|
|
164
|
+
id: 'custom',
|
|
165
|
+
displayName: 'Custom ACP Agent',
|
|
166
|
+
description: 'ACP stdio server configured through ACP_COMMAND.',
|
|
167
|
+
transport: 'custom' as const,
|
|
168
|
+
baseCommand: parsed.command,
|
|
169
|
+
baseProbeCommand: `${parsed.command} --version`,
|
|
170
|
+
serverCommand: normalized,
|
|
171
|
+
serverProbeCommand: `${normalized} --help`,
|
|
172
|
+
installCommand: null,
|
|
173
|
+
} satisfies AcpAgentDefinition;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export class AcpAgentCatalog {
|
|
177
|
+
private readonly definitions: AcpAgentDefinition[];
|
|
178
|
+
private readonly busyAgents = new Set<string>();
|
|
179
|
+
private readonly installErrors = new Map<string, string>();
|
|
180
|
+
private cached: { at: number; entries: AcpAgentCatalogEntry[] } | null = null;
|
|
181
|
+
|
|
182
|
+
constructor(input: {
|
|
183
|
+
customCommand?: string | null;
|
|
184
|
+
definitions?: AcpAgentDefinition[];
|
|
185
|
+
} = {}) {
|
|
186
|
+
const custom = customDefinition(input.customCommand);
|
|
187
|
+
const definitions = input.definitions ?? builtinAcpAgents;
|
|
188
|
+
this.definitions = custom ? [...definitions, custom] : definitions;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
definition(agentId: string) {
|
|
192
|
+
return this.definitions.find((definition) => definition.id === agentId) ?? null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async list(options: { force?: boolean } = {}) {
|
|
196
|
+
if (!options.force && this.cached && Date.now() - this.cached.at < 5_000) {
|
|
197
|
+
return this.cached.entries;
|
|
198
|
+
}
|
|
199
|
+
const entries = await Promise.all(this.definitions.map((definition) =>
|
|
200
|
+
this.inspect(definition),
|
|
201
|
+
));
|
|
202
|
+
this.cached = { at: Date.now(), entries };
|
|
203
|
+
return entries;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async installAdapter(agentId: string) {
|
|
207
|
+
const definition = this.definition(agentId);
|
|
208
|
+
if (!definition) {
|
|
209
|
+
throw new Error(`Unknown ACP agent: ${agentId}`);
|
|
210
|
+
}
|
|
211
|
+
const current = await this.inspect(definition);
|
|
212
|
+
if (current.availability === 'base_missing') {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`${definition.displayName} is not installed. Install the base agent first. Probe: ${definition.baseProbeCommand}`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (!definition.installCommand) {
|
|
218
|
+
throw new Error(`${definition.displayName} does not use an installable ACP adapter.`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.busyAgents.add(agentId);
|
|
222
|
+
this.installErrors.delete(agentId);
|
|
223
|
+
this.cached = null;
|
|
224
|
+
try {
|
|
225
|
+
const parsed = parseCommandLine(definition.installCommand);
|
|
226
|
+
const result = await runProcess({
|
|
227
|
+
command: parsed.command,
|
|
228
|
+
args: parsed.args,
|
|
229
|
+
timeoutMs: 2 * 60_000,
|
|
230
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
231
|
+
});
|
|
232
|
+
if (result.code !== 0) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
commandError(result) ??
|
|
235
|
+
`${definition.installCommand} failed with exit code ${result.code ?? 'unknown'}.`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const refreshed = await this.inspect(definition);
|
|
239
|
+
if (refreshed.availability !== 'ready') {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`ACP adapter installed, but its probe still failed: ${definition.serverProbeCommand}`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
246
|
+
this.installErrors.set(agentId, message);
|
|
247
|
+
throw error;
|
|
248
|
+
} finally {
|
|
249
|
+
this.busyAgents.delete(agentId);
|
|
250
|
+
this.cached = null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async listCommandModels(agentId: string): Promise<AgentModel[]> {
|
|
255
|
+
const definition = this.definition(agentId);
|
|
256
|
+
if (!definition?.modelListCommand) {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
const parsed = parseCommandLine(definition.modelListCommand);
|
|
260
|
+
const result = await runProcess({
|
|
261
|
+
command: parsed.command,
|
|
262
|
+
args: parsed.args,
|
|
263
|
+
timeoutMs: 15_000,
|
|
264
|
+
maxOutputBytes: 512 * 1024,
|
|
265
|
+
});
|
|
266
|
+
if (result.code !== 0) {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
270
|
+
const defaultModel = output.match(/^Default model:\s*(\S+)/m)?.[1] ?? null;
|
|
271
|
+
const models = output.split(/\r?\n/).flatMap((line) => {
|
|
272
|
+
const match = line.match(/^\s*[-*]\s+(\S+?)(?:\s+\(default\))?\s*$/);
|
|
273
|
+
if (!match?.[1]) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
const model = match[1];
|
|
277
|
+
return [{
|
|
278
|
+
id: model,
|
|
279
|
+
model,
|
|
280
|
+
displayName: model,
|
|
281
|
+
description: '',
|
|
282
|
+
isDefault: model === defaultModel || line.trim().startsWith('*'),
|
|
283
|
+
hidden: false,
|
|
284
|
+
supportedReasoningEfforts: [],
|
|
285
|
+
defaultReasoningEffort: null,
|
|
286
|
+
selectionKind: 'model' as const,
|
|
287
|
+
}];
|
|
288
|
+
});
|
|
289
|
+
return models.filter(
|
|
290
|
+
(model, index) => models.findIndex((candidate) => candidate.model === model.model) === index,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private async inspect(definition: AcpAgentDefinition): Promise<AcpAgentCatalogEntry> {
|
|
295
|
+
const base = await probeCommand(definition.baseProbeCommand);
|
|
296
|
+
const busy = this.busyAgents.has(definition.id);
|
|
297
|
+
const installError = this.installErrors.get(definition.id) ?? null;
|
|
298
|
+
if (!base.available) {
|
|
299
|
+
return {
|
|
300
|
+
...definition,
|
|
301
|
+
availability: 'base_missing',
|
|
302
|
+
baseVersion: null,
|
|
303
|
+
serverVersion: null,
|
|
304
|
+
busy,
|
|
305
|
+
statusMessage: installError ??
|
|
306
|
+
`Install the base agent first. Probe: ${definition.baseProbeCommand}`,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const serverExecutable = parseCommandLine(definition.serverCommand).command;
|
|
311
|
+
if (definition.transport === 'adapter' && !(await resolveExecutable(serverExecutable))) {
|
|
312
|
+
return {
|
|
313
|
+
...definition,
|
|
314
|
+
availability: 'adapter_missing',
|
|
315
|
+
baseVersion: base.version,
|
|
316
|
+
serverVersion: null,
|
|
317
|
+
busy,
|
|
318
|
+
statusMessage: installError ??
|
|
319
|
+
`Base agent detected. Install its ACP adapter. Probe: ${definition.serverProbeCommand}`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const server = await probeCommand(definition.serverProbeCommand);
|
|
324
|
+
if (!server.available) {
|
|
325
|
+
return {
|
|
326
|
+
...definition,
|
|
327
|
+
availability: 'server_unavailable',
|
|
328
|
+
baseVersion: base.version,
|
|
329
|
+
serverVersion: null,
|
|
330
|
+
busy,
|
|
331
|
+
statusMessage: installError ?? server.error ??
|
|
332
|
+
`ACP server probe failed: ${definition.serverProbeCommand}`,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
...definition,
|
|
338
|
+
availability: 'ready',
|
|
339
|
+
baseVersion: base.version,
|
|
340
|
+
serverVersion: server.version,
|
|
341
|
+
busy,
|
|
342
|
+
statusMessage: `Ready. ACP command: ${definition.serverCommand}`,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function acpAgentMetadata(entry: AcpAgentCatalogEntry): AcpAgentOptionMetadataDto {
|
|
348
|
+
return {
|
|
349
|
+
transport: entry.transport,
|
|
350
|
+
availability: entry.availability,
|
|
351
|
+
baseCommand: entry.baseCommand,
|
|
352
|
+
baseProbeCommand: entry.baseProbeCommand,
|
|
353
|
+
serverCommand: entry.serverCommand,
|
|
354
|
+
serverProbeCommand: entry.serverProbeCommand,
|
|
355
|
+
baseVersion: entry.baseVersion,
|
|
356
|
+
serverVersion: entry.serverVersion,
|
|
357
|
+
installCommand: entry.installCommand,
|
|
358
|
+
busy: entry.busy,
|
|
359
|
+
statusMessage: entry.statusMessage,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AgentProviderRequest,
|
|
7
|
+
AgentRuntimeEvent,
|
|
8
|
+
} from '../../agent-runtime/src/index';
|
|
9
|
+
import { AcpAgentCatalog } from './agent-catalog';
|
|
10
|
+
import {
|
|
11
|
+
AcpCatalogRuntimeAdapter,
|
|
12
|
+
acpSessionId,
|
|
13
|
+
} from './catalog-runtime';
|
|
14
|
+
|
|
15
|
+
const runtimes: AcpCatalogRuntimeAdapter[] = [];
|
|
16
|
+
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await Promise.all(runtimes.splice(0).map((runtime) => runtime.stop()));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('AcpCatalogRuntimeAdapter', () => {
|
|
22
|
+
it('selects a concrete agent and scopes its provider session id', async () => {
|
|
23
|
+
const fixture = path.resolve(
|
|
24
|
+
'node_modules/@agentclientprotocol/sdk/dist/examples/agent.js',
|
|
25
|
+
);
|
|
26
|
+
const runtime = new AcpCatalogRuntimeAdapter({
|
|
27
|
+
catalog: new AcpAgentCatalog({
|
|
28
|
+
definitions: [{
|
|
29
|
+
id: 'fixture-agent',
|
|
30
|
+
displayName: 'Fixture Agent',
|
|
31
|
+
description: 'ACP fixture',
|
|
32
|
+
transport: 'native',
|
|
33
|
+
baseCommand: process.execPath,
|
|
34
|
+
baseProbeCommand: `"${process.execPath}" --version`,
|
|
35
|
+
serverCommand: `"${process.execPath}" "${fixture}"`,
|
|
36
|
+
serverProbeCommand: `"${process.execPath}" --version`,
|
|
37
|
+
installCommand: null,
|
|
38
|
+
}],
|
|
39
|
+
}),
|
|
40
|
+
startupTimeoutMs: 5_000,
|
|
41
|
+
});
|
|
42
|
+
runtimes.push(runtime);
|
|
43
|
+
runtime.on('provider-request', (request) => {
|
|
44
|
+
const mapping = runtime.mapProviderRequest(
|
|
45
|
+
request as AgentProviderRequest,
|
|
46
|
+
{ approvalMode: 'yolo' },
|
|
47
|
+
);
|
|
48
|
+
if (mapping?.autoApprovedResult) {
|
|
49
|
+
runtime.respondToProviderRequest(mapping.providerRequestId, mapping.autoApprovedResult);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await runtime.start();
|
|
54
|
+
expect(await runtime.listModels()).toMatchObject([{
|
|
55
|
+
id: 'fixture-agent',
|
|
56
|
+
model: 'fixture-agent',
|
|
57
|
+
selectionKind: 'agent',
|
|
58
|
+
acpAgent: { availability: 'ready' },
|
|
59
|
+
}]);
|
|
60
|
+
const session = await runtime.startSession({
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
agentId: 'fixture-agent',
|
|
63
|
+
model: 'default',
|
|
64
|
+
approvalMode: 'yolo',
|
|
65
|
+
sandboxMode: 'workspace-write',
|
|
66
|
+
});
|
|
67
|
+
expect(acpSessionId.decode(session.providerSessionId)).toMatchObject({
|
|
68
|
+
agentId: 'fixture-agent',
|
|
69
|
+
});
|
|
70
|
+
expect(session.agentId).toBe('fixture-agent');
|
|
71
|
+
expect(session.model).toBe('default');
|
|
72
|
+
|
|
73
|
+
const completed = new Promise<Extract<AgentRuntimeEvent, { type: 'turn.completed' }>>(
|
|
74
|
+
(resolve, reject) => {
|
|
75
|
+
const timer = setTimeout(() => reject(new Error('Catalog turn timed out.')), 10_000);
|
|
76
|
+
runtime.on('event', (event: AgentRuntimeEvent) => {
|
|
77
|
+
if (event.type === 'turn.completed') {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
resolve(event);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
await runtime.startTurn({
|
|
85
|
+
providerSessionId: session.providerSessionId,
|
|
86
|
+
prompt: 'Run the fixture turn.',
|
|
87
|
+
model: 'default',
|
|
88
|
+
workspacePath: process.cwd(),
|
|
89
|
+
});
|
|
90
|
+
const event = await completed;
|
|
91
|
+
expect(event.providerSessionId).toBe(session.providerSessionId);
|
|
92
|
+
expect(event.turn.items.map((item) => item.kind)).toEqual(expect.arrayContaining([
|
|
93
|
+
'userMessage',
|
|
94
|
+
'agentMessage',
|
|
95
|
+
'fileRead',
|
|
96
|
+
'fileChange',
|
|
97
|
+
]));
|
|
98
|
+
}, 15_000);
|
|
99
|
+
});
|