@getpaseo/server 0.1.99 → 0.1.101
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/server/executable-resolution/windows.js +3 -0
- package/dist/server/server/agent/agent-manager.d.ts +10 -0
- package/dist/server/server/agent/agent-manager.js +65 -27
- package/dist/server/server/agent/agent-sdk-types.d.ts +8 -0
- package/dist/server/server/agent/mcp-server.d.ts +2 -45
- package/dist/server/server/agent/mcp-server.js +45 -1985
- package/dist/server/server/agent/prompt-attachments.js +6 -2
- package/dist/server/server/agent/provider-registry.js +1 -0
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +4 -0
- package/dist/server/server/agent/provider-snapshot-manager.js +58 -13
- package/dist/server/server/agent/providers/acp-agent.d.ts +39 -2
- package/dist/server/server/agent/providers/acp-agent.js +281 -20
- package/dist/server/server/agent/providers/claude/agent.js +96 -62
- package/dist/server/server/agent/providers/codex-app-server-agent.js +6 -57
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +2 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +10 -0
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts +1 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js +1 -1
- package/dist/server/server/agent/providers/generic-acp-agent.d.ts +3 -0
- package/dist/server/server/agent/providers/generic-acp-agent.js +41 -23
- package/dist/server/server/agent/providers/mock-load-test-agent.js +4 -2
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +14 -11
- package/dist/server/server/agent/providers/opencode/server-manager.js +149 -91
- package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +6 -5
- package/dist/server/server/agent/providers/opencode/test-server-manager.js +13 -3
- package/dist/server/server/agent/providers/opencode/test-utils/{test-opencode-runtime.d.ts → test-opencode-harness.d.ts} +11 -11
- package/dist/server/server/agent/providers/opencode/test-utils/{test-opencode-runtime.js → test-opencode-harness.js} +23 -10
- package/dist/server/server/agent/providers/opencode-agent.d.ts +9 -3
- package/dist/server/server/agent/providers/opencode-agent.js +26 -38
- package/dist/server/server/agent/providers/pi/agent.d.ts +4 -2
- package/dist/server/server/agent/providers/pi/agent.js +8 -3
- package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/cli-runtime.js +6 -3
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +2 -1
- package/dist/server/server/agent/providers/provider-image-output.d.ts +5 -0
- package/dist/server/server/agent/providers/provider-image-output.js +55 -0
- package/dist/server/server/agent/tools/paseo-tools.d.ts +48 -0
- package/dist/server/server/agent/tools/paseo-tools.js +2121 -0
- package/dist/server/server/agent/tools/types.d.ts +36 -0
- package/dist/server/server/agent/tools/types.js +2 -0
- package/dist/server/server/bootstrap.js +71 -62
- package/dist/server/server/persisted-config.d.ts +5 -0
- package/dist/server/server/persisted-config.js +10 -2
- package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +59 -0
- package/dist/server/server/session/agent-updates/agent-updates-service.js +220 -0
- package/dist/server/server/session/checkout/checkout-session.d.ts +13 -15
- package/dist/server/server/session/checkout/checkout-session.js +18 -16
- package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
- package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
- package/dist/server/server/session/daemon/daemon-session.d.ts +14 -0
- package/dist/server/server/session/daemon/daemon-session.js +38 -0
- package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
- package/dist/server/server/session/daemon/diagnostics.js +421 -0
- package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
- package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
- package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
- package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
- package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
- package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
- package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
- package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
- package/dist/server/server/session.d.ts +7 -51
- package/dist/server/server/session.js +113 -938
- package/dist/server/server/speech/providers/openai/config.d.ts +1 -2
- package/dist/server/server/speech/providers/openai/config.js +13 -9
- package/dist/server/server/speech/providers/openai/runtime.js +2 -16
- package/dist/server/server/speech/providers/openai/stt.d.ts +1 -0
- package/dist/server/server/speech/providers/openai/stt.js +4 -2
- package/dist/server/server/speech/providers/openai/tts.d.ts +1 -0
- package/dist/server/server/speech/providers/openai/tts.js +1 -0
- package/dist/server/server/websocket/runtime-metrics.d.ts +20 -0
- package/dist/server/server/websocket-server.d.ts +1 -2
- package/dist/server/server/websocket-server.js +26 -21
- package/dist/server/server/worktree-bootstrap.d.ts +1 -1
- package/dist/server/server/worktree-branch-name-generator.js +3 -1
- package/dist/server/utils/checkout-git.js +51 -26
- package/dist/src/executable-resolution/windows.js +3 -0
- package/dist/src/server/persisted-config.js +10 -2
- package/package.json +5 -5
- package/dist/server/server/agent/providers/opencode/runtime.d.ts +0 -28
- package/dist/server/server/agent/providers/opencode/runtime.js +0 -5
- package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts +0 -42
- package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +0 -168
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { Readable, Writable } from "node:stream";
|
|
5
6
|
import { terminateWithTreeKill } from "../../../utils/tree-kill.js";
|
|
@@ -10,6 +11,8 @@ import { checkProviderLaunchAvailable, createProviderEnvSpec, resolveProviderLau
|
|
|
10
11
|
import { renderPromptAttachmentAsText } from "../prompt-attachments.js";
|
|
11
12
|
import { appendOrReplaceGrowingAssistantMessage, runProviderTurn } from "./provider-runner.js";
|
|
12
13
|
import { platformShell, spawnProcess } from "../../../utils/spawn.js";
|
|
14
|
+
import { toDiagnosticErrorMessage, truncateForDiagnostic, } from "./diagnostic-utils.js";
|
|
15
|
+
import { withTimeout } from "../../../utils/promise-timeout.js";
|
|
13
16
|
function assertChildWithPipes(child) {
|
|
14
17
|
if (!child.stdin || !child.stdout || !child.stderr) {
|
|
15
18
|
throw new Error("Child process did not expose stdio pipes");
|
|
@@ -70,6 +73,19 @@ function resolveTerminalCommand(command, args) {
|
|
|
70
73
|
const shell = platformShell();
|
|
71
74
|
return { command: shell.command, args: [...shell.flag, command] };
|
|
72
75
|
}
|
|
76
|
+
function formatDurationMs(startedAt) {
|
|
77
|
+
return `${Math.max(0, Date.now() - startedAt)}ms`;
|
|
78
|
+
}
|
|
79
|
+
function pushACPStderrRow(rows, stderrChunks) {
|
|
80
|
+
const stderr = stderrChunks.join("").trim();
|
|
81
|
+
if (!stderr) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
rows.push({
|
|
85
|
+
label: "ACP stderr",
|
|
86
|
+
value: truncateForDiagnostic(stderr),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
73
89
|
export const DEFAULT_ACP_CAPABILITIES = {
|
|
74
90
|
supportsStreaming: true,
|
|
75
91
|
supportsSessionPersistence: true,
|
|
@@ -96,6 +112,8 @@ const ACP_CLIENT_CAPABILITIES = {
|
|
|
96
112
|
// sign-in URL in the browser) when probing an ACP agent for models/modes.
|
|
97
113
|
// NO_BROWSER is honored by Gemini CLI; other ACP agents ignore it.
|
|
98
114
|
const PROBE_ENV = { NO_BROWSER: "true" };
|
|
115
|
+
const ACP_CATALOG_TIMEOUT_MS = 60000;
|
|
116
|
+
const ACP_DIAGNOSTIC_PHASE_TIMEOUT_MS = 20000;
|
|
99
117
|
function summarizeMalformedACPStdoutError(error) {
|
|
100
118
|
return {
|
|
101
119
|
type: error instanceof Error ? error.name : typeof error,
|
|
@@ -257,6 +275,26 @@ export function deriveModelDefinitionsFromACP(provider, models, configOptions) {
|
|
|
257
275
|
metadata: option.metadata,
|
|
258
276
|
}));
|
|
259
277
|
}
|
|
278
|
+
export function deriveFeaturesFromACP(configOptions, featureOptions) {
|
|
279
|
+
return featureOptions.flatMap((featureOption) => {
|
|
280
|
+
const option = findSelectConfigFeatureOption(configOptions, featureOption);
|
|
281
|
+
if (!option) {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
return [
|
|
285
|
+
{
|
|
286
|
+
type: "select",
|
|
287
|
+
id: featureOption.id,
|
|
288
|
+
label: featureOption.label,
|
|
289
|
+
description: featureOption.description,
|
|
290
|
+
tooltip: featureOption.tooltip,
|
|
291
|
+
icon: featureOption.icon,
|
|
292
|
+
value: option.currentValue ?? null,
|
|
293
|
+
options: deriveConfigFeatureSelectOptions(option, featureOption),
|
|
294
|
+
},
|
|
295
|
+
];
|
|
296
|
+
});
|
|
297
|
+
}
|
|
260
298
|
export class ACPAgentClient {
|
|
261
299
|
constructor(options) {
|
|
262
300
|
this.provider = options.provider;
|
|
@@ -272,6 +310,7 @@ export class ACPAgentClient {
|
|
|
272
310
|
this.modelTransformer = options.modelTransformer;
|
|
273
311
|
this.sessionResponseTransformer = options.sessionResponseTransformer;
|
|
274
312
|
this.configOptionsTransformer = options.configOptionsTransformer;
|
|
313
|
+
this.configFeatureOptions = options.configFeatureOptions ?? [];
|
|
275
314
|
this.modeIdTransformer = options.modeIdTransformer;
|
|
276
315
|
this.toolSnapshotTransformer = options.toolSnapshotTransformer;
|
|
277
316
|
this.providerModeWriter = options.providerModeWriter;
|
|
@@ -291,6 +330,7 @@ export class ACPAgentClient {
|
|
|
291
330
|
modelTransformer: this.modelTransformer,
|
|
292
331
|
sessionResponseTransformer: this.sessionResponseTransformer,
|
|
293
332
|
configOptionsTransformer: this.configOptionsTransformer,
|
|
333
|
+
configFeatureOptions: this.configFeatureOptions,
|
|
294
334
|
modeIdTransformer: this.modeIdTransformer,
|
|
295
335
|
toolSnapshotTransformer: this.toolSnapshotTransformer,
|
|
296
336
|
providerModeWriter: this.providerModeWriter,
|
|
@@ -329,6 +369,7 @@ export class ACPAgentClient {
|
|
|
329
369
|
modelTransformer: this.modelTransformer,
|
|
330
370
|
sessionResponseTransformer: this.sessionResponseTransformer,
|
|
331
371
|
configOptionsTransformer: this.configOptionsTransformer,
|
|
372
|
+
configFeatureOptions: this.configFeatureOptions,
|
|
332
373
|
modeIdTransformer: this.modeIdTransformer,
|
|
333
374
|
toolSnapshotTransformer: this.toolSnapshotTransformer,
|
|
334
375
|
providerModeWriter: this.providerModeWriter,
|
|
@@ -346,19 +387,50 @@ export class ACPAgentClient {
|
|
|
346
387
|
}
|
|
347
388
|
async fetchCatalog(options) {
|
|
348
389
|
const { cwd } = options;
|
|
390
|
+
const timeoutMs = options.timeoutMs ?? ACP_CATALOG_TIMEOUT_MS;
|
|
391
|
+
let probe = null;
|
|
392
|
+
try {
|
|
393
|
+
const catalogProbe = (async () => {
|
|
394
|
+
const initializedProbe = await this.spawnProcess(PROBE_ENV, {
|
|
395
|
+
initializeTimeoutMs: timeoutMs,
|
|
396
|
+
onSpawned: (spawned) => {
|
|
397
|
+
probe = spawned;
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
probe = initializedProbe;
|
|
401
|
+
const response = await this.runACPRequest(() => initializedProbe.connection.newSession({
|
|
402
|
+
cwd,
|
|
403
|
+
mcpServers: [],
|
|
404
|
+
}));
|
|
405
|
+
const transformed = this.transformSessionResponse(response);
|
|
406
|
+
const models = deriveModelDefinitionsFromACP(this.provider, transformed.models, transformed.configOptions);
|
|
407
|
+
const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
|
|
408
|
+
return {
|
|
409
|
+
models: this.modelTransformer ? this.modelTransformer(models) : models,
|
|
410
|
+
modes: modeInfo.modes,
|
|
411
|
+
};
|
|
412
|
+
})();
|
|
413
|
+
return await withTimeout(catalogProbe, timeoutMs, `ACP catalog probe timed out after ${timeoutMs}ms`);
|
|
414
|
+
}
|
|
415
|
+
finally {
|
|
416
|
+
if (probe) {
|
|
417
|
+
await this.closeProbe(probe);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
async listFeatures(config) {
|
|
422
|
+
if (this.configFeatureOptions.length === 0) {
|
|
423
|
+
return [];
|
|
424
|
+
}
|
|
425
|
+
this.assertProvider(config);
|
|
349
426
|
const probe = await this.spawnProcess(PROBE_ENV);
|
|
350
427
|
try {
|
|
351
428
|
const response = await this.runACPRequest(() => probe.connection.newSession({
|
|
352
|
-
cwd,
|
|
429
|
+
cwd: config.cwd,
|
|
353
430
|
mcpServers: [],
|
|
354
431
|
}));
|
|
355
432
|
const transformed = this.transformSessionResponse(response);
|
|
356
|
-
|
|
357
|
-
const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
|
|
358
|
-
return {
|
|
359
|
-
models: this.modelTransformer ? this.modelTransformer(models) : models,
|
|
360
|
-
modes: modeInfo.modes,
|
|
361
|
-
};
|
|
433
|
+
return deriveFeaturesFromACP(transformed.configOptions, this.configFeatureOptions);
|
|
362
434
|
}
|
|
363
435
|
finally {
|
|
364
436
|
await this.closeProbe(probe);
|
|
@@ -420,6 +492,28 @@ export class ACPAgentClient {
|
|
|
420
492
|
}
|
|
421
493
|
}
|
|
422
494
|
async spawnProcess(launchEnv, options) {
|
|
495
|
+
const transport = await this.spawnTransport(launchEnv);
|
|
496
|
+
const probe = {
|
|
497
|
+
child: transport.child,
|
|
498
|
+
connection: transport.connection,
|
|
499
|
+
stderrChunks: transport.stderrChunks,
|
|
500
|
+
};
|
|
501
|
+
options?.onSpawned?.(probe);
|
|
502
|
+
try {
|
|
503
|
+
const initialize = await this.initializeTransport(transport, options?.initializeTimeoutMs);
|
|
504
|
+
const initializedProbe = {
|
|
505
|
+
...probe,
|
|
506
|
+
initialize,
|
|
507
|
+
};
|
|
508
|
+
probe.initialize = initialize;
|
|
509
|
+
return initializedProbe;
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
await terminateChildProcess(transport.child, 2000, this.terminateProcess);
|
|
513
|
+
throw error;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async spawnTransport(launchEnv) {
|
|
423
517
|
const { command, args } = await this.resolveLaunchCommand();
|
|
424
518
|
const child = spawnProcess(command, args, {
|
|
425
519
|
cwd: process.cwd(),
|
|
@@ -440,38 +534,46 @@ export class ACPAgentClient {
|
|
|
440
534
|
reject(new Error(stderr ? `${String(error)}\n${stderr}` : String(error)));
|
|
441
535
|
});
|
|
442
536
|
});
|
|
537
|
+
const spawnReadyPromise = new Promise((resolve) => {
|
|
538
|
+
child.once("spawn", () => {
|
|
539
|
+
resolve();
|
|
540
|
+
});
|
|
541
|
+
});
|
|
443
542
|
const stream = createLoggedNdJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout), { logger: this.logger, provider: this.provider });
|
|
444
543
|
const connection = new ClientSideConnection(() => this.buildProbeClient(), stream);
|
|
544
|
+
return {
|
|
545
|
+
child,
|
|
546
|
+
connection,
|
|
547
|
+
stderrChunks,
|
|
548
|
+
spawnReady: spawnReadyPromise,
|
|
549
|
+
spawnError: spawnErrorPromise,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
async initializeTransport(transport, initializeTimeoutMs) {
|
|
445
553
|
let timeout = null;
|
|
446
|
-
const initializeTimeoutPromise =
|
|
554
|
+
const initializeTimeoutPromise = initializeTimeoutMs
|
|
447
555
|
? new Promise((_, reject) => {
|
|
448
556
|
timeout = setTimeout(() => {
|
|
449
|
-
reject(new Error(`ACP initialize timed out after ${
|
|
450
|
-
},
|
|
557
|
+
reject(new Error(`ACP initialize timed out after ${initializeTimeoutMs}ms`));
|
|
558
|
+
}, initializeTimeoutMs);
|
|
451
559
|
})
|
|
452
560
|
: null;
|
|
453
|
-
let initialize;
|
|
454
561
|
try {
|
|
455
|
-
|
|
456
|
-
connection.initialize({
|
|
562
|
+
return await this.runACPRequest(() => Promise.race([
|
|
563
|
+
transport.connection.initialize({
|
|
457
564
|
protocolVersion: PROTOCOL_VERSION,
|
|
458
565
|
clientCapabilities: ACP_CLIENT_CAPABILITIES,
|
|
459
566
|
clientInfo: { name: "Paseo", version: "dev" },
|
|
460
567
|
}),
|
|
461
|
-
|
|
568
|
+
transport.spawnError,
|
|
462
569
|
...(initializeTimeoutPromise ? [initializeTimeoutPromise] : []),
|
|
463
570
|
]));
|
|
464
571
|
}
|
|
465
|
-
catch (error) {
|
|
466
|
-
await terminateChildProcess(child, 2000, this.terminateProcess);
|
|
467
|
-
throw error;
|
|
468
|
-
}
|
|
469
572
|
finally {
|
|
470
573
|
if (timeout) {
|
|
471
574
|
clearTimeout(timeout);
|
|
472
575
|
}
|
|
473
576
|
}
|
|
474
|
-
return { child, connection, initialize };
|
|
475
577
|
}
|
|
476
578
|
buildProbeClient() {
|
|
477
579
|
return {
|
|
@@ -495,7 +597,7 @@ export class ACPAgentClient {
|
|
|
495
597
|
}
|
|
496
598
|
async closeProbe(probe) {
|
|
497
599
|
try {
|
|
498
|
-
if (probe.initialize
|
|
600
|
+
if (probe.initialize?.agentCapabilities?.sessionCapabilities?.close) {
|
|
499
601
|
// No active session to close here; ignore capability.
|
|
500
602
|
}
|
|
501
603
|
}
|
|
@@ -511,6 +613,89 @@ export class ACPAgentClient {
|
|
|
511
613
|
throw toACPRequestError(error);
|
|
512
614
|
}
|
|
513
615
|
}
|
|
616
|
+
async buildACPProbeDiagnosticRows(options = {}) {
|
|
617
|
+
const rows = [];
|
|
618
|
+
const phaseTimeoutMs = options.phaseTimeoutMs ?? ACP_DIAGNOSTIC_PHASE_TIMEOUT_MS;
|
|
619
|
+
const cwd = options.cwd ?? homedir();
|
|
620
|
+
let transport = null;
|
|
621
|
+
try {
|
|
622
|
+
const spawnStartedAt = Date.now();
|
|
623
|
+
try {
|
|
624
|
+
transport = await this.spawnTransport(PROBE_ENV);
|
|
625
|
+
await withTimeout(Promise.race([transport.spawnReady, transport.spawnError]), phaseTimeoutMs, `ACP spawn timed out after ${phaseTimeoutMs}ms`);
|
|
626
|
+
rows.push({
|
|
627
|
+
label: "ACP spawn",
|
|
628
|
+
value: `ok (${formatDurationMs(spawnStartedAt)})`,
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
rows.push({
|
|
633
|
+
label: "ACP spawn",
|
|
634
|
+
value: `error: ${toDiagnosticErrorMessage(error)}`,
|
|
635
|
+
});
|
|
636
|
+
return rows;
|
|
637
|
+
}
|
|
638
|
+
const activeTransport = transport;
|
|
639
|
+
const initializeStartedAt = Date.now();
|
|
640
|
+
try {
|
|
641
|
+
await this.initializeTransport(activeTransport, phaseTimeoutMs);
|
|
642
|
+
rows.push({
|
|
643
|
+
label: "ACP initialize",
|
|
644
|
+
value: `ok (${formatDurationMs(initializeStartedAt)})`,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
rows.push({
|
|
649
|
+
label: "ACP initialize",
|
|
650
|
+
value: `error: ${toDiagnosticErrorMessage(error)}`,
|
|
651
|
+
});
|
|
652
|
+
pushACPStderrRow(rows, activeTransport.stderrChunks);
|
|
653
|
+
return rows;
|
|
654
|
+
}
|
|
655
|
+
const sessionStartedAt = Date.now();
|
|
656
|
+
try {
|
|
657
|
+
const response = await withTimeout(this.runACPRequest(() => activeTransport.connection.newSession({
|
|
658
|
+
cwd,
|
|
659
|
+
mcpServers: [],
|
|
660
|
+
})), phaseTimeoutMs, `ACP session/new timed out after ${phaseTimeoutMs}ms`);
|
|
661
|
+
const transformed = this.transformSessionResponse(response);
|
|
662
|
+
const models = deriveModelDefinitionsFromACP(this.provider, transformed.models, transformed.configOptions);
|
|
663
|
+
const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
|
|
664
|
+
rows.push({
|
|
665
|
+
label: "ACP session/new",
|
|
666
|
+
value: `ok (${formatDurationMs(sessionStartedAt)}; models=${models.length}; modes=${modeInfo.modes.length})`,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
catch (error) {
|
|
670
|
+
rows.push({
|
|
671
|
+
label: "ACP session/new",
|
|
672
|
+
value: `error: ${toDiagnosticErrorMessage(error)}`,
|
|
673
|
+
});
|
|
674
|
+
pushACPStderrRow(rows, activeTransport.stderrChunks);
|
|
675
|
+
return rows;
|
|
676
|
+
}
|
|
677
|
+
pushACPStderrRow(rows, activeTransport.stderrChunks);
|
|
678
|
+
return rows;
|
|
679
|
+
}
|
|
680
|
+
finally {
|
|
681
|
+
if (transport) {
|
|
682
|
+
const cleanupStartedAt = Date.now();
|
|
683
|
+
try {
|
|
684
|
+
await terminateChildProcess(transport.child, 2000, this.terminateProcess);
|
|
685
|
+
rows.push({
|
|
686
|
+
label: "ACP cleanup",
|
|
687
|
+
value: `ok (${formatDurationMs(cleanupStartedAt)})`,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
catch (error) {
|
|
691
|
+
rows.push({
|
|
692
|
+
label: "ACP cleanup",
|
|
693
|
+
value: `error: ${toDiagnosticErrorMessage(error)}`,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
514
699
|
async resolveLaunchCommand() {
|
|
515
700
|
const prefix = await resolveProviderLaunch({
|
|
516
701
|
commandConfig: this.runtimeSettings?.command,
|
|
@@ -582,6 +767,7 @@ export class ACPAgentSession {
|
|
|
582
767
|
this.modelTransformer = options.modelTransformer;
|
|
583
768
|
this.sessionResponseTransformer = options.sessionResponseTransformer;
|
|
584
769
|
this.configOptionsTransformer = options.configOptionsTransformer;
|
|
770
|
+
this.configFeatureOptions = options.configFeatureOptions ?? [];
|
|
585
771
|
this.modeIdTransformer = options.modeIdTransformer;
|
|
586
772
|
this.toolSnapshotTransformer = options.toolSnapshotTransformer;
|
|
587
773
|
this.providerModeWriter = options.providerModeWriter;
|
|
@@ -739,6 +925,9 @@ export class ACPAgentSession {
|
|
|
739
925
|
async getCurrentMode() {
|
|
740
926
|
return this.currentMode;
|
|
741
927
|
}
|
|
928
|
+
get features() {
|
|
929
|
+
return deriveFeaturesFromACP(this.configOptions, this.configFeatureOptions);
|
|
930
|
+
}
|
|
742
931
|
ensureCommandsReadyDeferred() {
|
|
743
932
|
if (this.commandsReadyDeferred || this.commandsReadySettled || this.cachedCommands.length > 0) {
|
|
744
933
|
return;
|
|
@@ -1015,6 +1204,37 @@ export class ACPAgentSession {
|
|
|
1015
1204
|
thinkingOptionId: this.thinkingOptionId,
|
|
1016
1205
|
});
|
|
1017
1206
|
}
|
|
1207
|
+
async setFeature(featureId, value) {
|
|
1208
|
+
if (!this.connection || !this.sessionId) {
|
|
1209
|
+
throw new Error("ACP session not initialized");
|
|
1210
|
+
}
|
|
1211
|
+
const featureOption = this.configFeatureOptions.find((option) => option.id === featureId);
|
|
1212
|
+
if (!featureOption) {
|
|
1213
|
+
throw new Error(`Unknown ${this.provider} feature: ${featureId}`);
|
|
1214
|
+
}
|
|
1215
|
+
const option = findSelectConfigFeatureOption(this.configOptions, featureOption);
|
|
1216
|
+
if (!option) {
|
|
1217
|
+
throw new Error(`${this.provider} does not expose ACP feature '${featureId}'`);
|
|
1218
|
+
}
|
|
1219
|
+
const requestedValue = normalizeConfigFeatureValue(value);
|
|
1220
|
+
const choice = findSelectConfigChoice({ option, value: requestedValue });
|
|
1221
|
+
if (!choice) {
|
|
1222
|
+
throw new Error(`${this.provider} feature '${featureId}' does not include option '${requestedValue}'`);
|
|
1223
|
+
}
|
|
1224
|
+
const response = await this.connection.setSessionConfigOption({
|
|
1225
|
+
sessionId: this.sessionId,
|
|
1226
|
+
configId: option.id,
|
|
1227
|
+
value: requestedValue,
|
|
1228
|
+
});
|
|
1229
|
+
const currentValue = this.applyConfigOptionResponse({
|
|
1230
|
+
response,
|
|
1231
|
+
configId: option.id,
|
|
1232
|
+
category: featureOption.category,
|
|
1233
|
+
requestedValue,
|
|
1234
|
+
label: featureOption.label,
|
|
1235
|
+
});
|
|
1236
|
+
this.config.featureValues = { ...this.config.featureValues, [featureId]: currentValue };
|
|
1237
|
+
}
|
|
1018
1238
|
applyConfigOptionResponse({ response, configId, category, requestedValue, label, }) {
|
|
1019
1239
|
this.configOptions = this.transformConfigOptions(response.configOptions);
|
|
1020
1240
|
const responseOption = findSelectConfigOption({
|
|
@@ -1386,6 +1606,13 @@ export class ACPAgentSession {
|
|
|
1386
1606
|
if (this.config.thinkingOptionId && this.config.thinkingOptionId !== this.thinkingOptionId) {
|
|
1387
1607
|
await this.setThinkingOption(this.config.thinkingOptionId);
|
|
1388
1608
|
}
|
|
1609
|
+
const configuredFeatureValues = this.config.featureValues ?? {};
|
|
1610
|
+
for (const featureOption of this.configFeatureOptions) {
|
|
1611
|
+
if (!Object.prototype.hasOwnProperty.call(configuredFeatureValues, featureOption.id)) {
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
await this.setFeature(featureOption.id, configuredFeatureValues[featureOption.id]);
|
|
1615
|
+
}
|
|
1389
1616
|
}
|
|
1390
1617
|
warnInvalidSelection(value, message) {
|
|
1391
1618
|
this.logger.warn({ value }, message);
|
|
@@ -1671,6 +1898,12 @@ function findSelectConfigOption({ configOptions, category, id, }) {
|
|
|
1671
1898
|
const option = configOptions?.find((entry) => entry.type === "select" && entry.category === category && (!id || entry.id === id));
|
|
1672
1899
|
return option ?? null;
|
|
1673
1900
|
}
|
|
1901
|
+
function findSelectConfigFeatureOption(configOptions, featureOption) {
|
|
1902
|
+
const option = configOptions?.find((entry) => entry.type === "select" &&
|
|
1903
|
+
entry.id === featureOption.configId &&
|
|
1904
|
+
entry.category === featureOption.category);
|
|
1905
|
+
return option ?? null;
|
|
1906
|
+
}
|
|
1674
1907
|
function findSelectConfigChoice({ option, value, }) {
|
|
1675
1908
|
if (!option) {
|
|
1676
1909
|
return null;
|
|
@@ -1690,6 +1923,34 @@ function flattenSelectOptions(options) {
|
|
|
1690
1923
|
}
|
|
1691
1924
|
return flattened;
|
|
1692
1925
|
}
|
|
1926
|
+
function deriveConfigFeatureSelectOptions(option, featureOption) {
|
|
1927
|
+
return flattenSelectOptions(option.options).map((choice) => ({
|
|
1928
|
+
id: choice.value,
|
|
1929
|
+
label: normalizeConfigFeatureOptionLabel(choice, featureOption),
|
|
1930
|
+
description: choice.description ?? undefined,
|
|
1931
|
+
isDefault: choice.value === option.currentValue,
|
|
1932
|
+
metadata: choice.group ? { group: choice.group } : undefined,
|
|
1933
|
+
}));
|
|
1934
|
+
}
|
|
1935
|
+
function normalizeConfigFeatureOptionLabel(choice, featureOption) {
|
|
1936
|
+
const name = choice.name.trim();
|
|
1937
|
+
if (name) {
|
|
1938
|
+
return name;
|
|
1939
|
+
}
|
|
1940
|
+
if (choice.value === "" && featureOption.emptyOptionLabel) {
|
|
1941
|
+
return featureOption.emptyOptionLabel;
|
|
1942
|
+
}
|
|
1943
|
+
return choice.value;
|
|
1944
|
+
}
|
|
1945
|
+
function normalizeConfigFeatureValue(value) {
|
|
1946
|
+
if (typeof value === "string") {
|
|
1947
|
+
return value;
|
|
1948
|
+
}
|
|
1949
|
+
if (value === null) {
|
|
1950
|
+
return "";
|
|
1951
|
+
}
|
|
1952
|
+
throw new Error(`ACP feature value must be a string`);
|
|
1953
|
+
}
|
|
1693
1954
|
function deriveSelectorOptions(configOptions, category) {
|
|
1694
1955
|
const option = findSelectConfigOption({ configOptions, category });
|
|
1695
1956
|
if (!option) {
|