@getpaseo/server 0.1.101 → 0.1.102
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/.env.example +8 -0
- package/dist/scripts/supervisor.js +26 -8
- package/dist/server/server/agent/activity-curator.d.ts +17 -0
- package/dist/server/server/agent/activity-curator.js +101 -24
- package/dist/server/server/agent/agent-manager.js +5 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +7 -2
- package/dist/server/server/agent/provider-registry.js +12 -11
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +8 -1
- package/dist/server/server/agent/provider-snapshot-manager.js +78 -33
- package/dist/server/server/agent/providers/acp-agent.d.ts +13 -0
- package/dist/server/server/agent/providers/acp-agent.js +37 -1
- package/dist/server/server/agent/providers/claude/agent.js +51 -14
- package/dist/server/server/agent/providers/claude/query.d.ts +3 -0
- package/dist/server/server/agent/providers/claude/query.js +4 -2
- package/dist/server/server/agent/providers/generic-acp-agent.d.ts +2 -1
- package/dist/server/server/agent/providers/generic-acp-agent.js +2 -1
- package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
- package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +8 -0
- package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/paths.js +7 -0
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +34 -5
- package/dist/server/server/agent/providers/opencode-agent.d.ts +4 -0
- package/dist/server/server/agent/providers/opencode-agent.js +14 -2
- package/dist/server/server/agent/providers/pi/agent.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/agent.js +9 -3
- package/dist/server/server/agent/providers/provider-image-output.js +11 -6
- package/dist/server/server/agent/tools/paseo-tools.d.ts +1 -1
- package/dist/server/server/agent/tools/paseo-tools.js +0 -2
- package/dist/server/server/bootstrap.d.ts +7 -1
- package/dist/server/server/bootstrap.js +18 -0
- package/dist/server/server/config.d.ts +2 -0
- package/dist/server/server/config.js +57 -1
- package/dist/server/server/daemon-worker.js +19 -7
- package/dist/server/server/lifecycle-reasons.d.ts +4 -0
- package/dist/server/server/lifecycle-reasons.js +6 -0
- package/dist/server/server/persisted-config.d.ts +13 -2
- package/dist/server/server/persisted-config.js +30 -11
- package/dist/server/server/process-diagnostics.d.ts +17 -0
- package/dist/server/server/process-diagnostics.js +22 -0
- package/dist/server/server/relay-transport.js +1 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +3 -1
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
- package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
- package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
- package/dist/server/server/session/daemon/daemon-session.d.ts +12 -0
- package/dist/server/server/session/daemon/daemon-session.js +12 -0
- package/dist/server/server/session/daemon/diagnostics.js +10 -0
- package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
- package/dist/server/server/session/daemon/install-origin.js +64 -0
- package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
- package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
- package/dist/server/server/session/provider/provider-catalog-session.js +8 -4
- package/dist/server/server/session.d.ts +5 -3
- package/dist/server/server/session.js +74 -32
- package/dist/server/server/speech/providers/openai/config.d.ts +0 -2
- package/dist/server/server/speech/providers/openai/config.js +71 -34
- package/dist/server/server/speech/providers/openai/runtime.js +4 -4
- package/dist/server/server/web-ui.d.ts +10 -0
- package/dist/server/server/web-ui.js +205 -0
- package/dist/server/server/websocket/runtime-metrics.d.ts +3 -0
- package/dist/server/server/websocket-server.d.ts +3 -0
- package/dist/server/server/websocket-server.js +190 -32
- package/dist/server/services/quota-fetcher/manifest.js +5 -0
- package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
- package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.js +2 -2
- package/dist/server/utils/checkout-git.js +156 -3
- package/dist/server/utils/directory-suggestions.js +1 -4
- package/dist/server/utils/path.d.ts +2 -0
- package/dist/server/utils/path.js +13 -0
- package/dist/server/utils/worktree.d.ts +1 -0
- package/dist/server/utils/worktree.js +92 -11
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-5fa9347afb28c7d61bbf73684f3bdafa.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-5fa9347afb28c7d61bbf73684f3bdafa.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-5fa9347afb28c7d61bbf73684f3bdafa.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-35159ed50cf218b741c5dab5a6f4a889.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-35159ed50cf218b741c5dab5a6f4a889.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-35159ed50cf218b741c5dab5a6f4a889.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-cf63ba3f4de4164d2d9c3738e1b791b2.js +16157 -0
- package/dist/server/web-ui/_expo/static/js/web/index-cf63ba3f4de4164d2d9c3738e1b791b2.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-cf63ba3f4de4164d2d9c3738e1b791b2.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-6a6cfdd9f06d4fd2b05adc1a8771c58c.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-6a6cfdd9f06d4fd2b05adc1a8771c58c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-6a6cfdd9f06d4fd2b05adc1a8771c58c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-b31888f9a7de4f0c52d6a58fcddbcb20.js +3 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-b31888f9a7de4f0c52d6a58fcddbcb20.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-b31888f9a7de4f0c52d6a58fcddbcb20.js.gz +0 -0
- package/dist/server/web-ui/apple-touch-icon.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/file-explorer.3e15e8f72c825c85ce336bcb0cdef776.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/notification-icon.3bf81d33ddbf380606bdd248ba83e158.png +0 -0
- package/dist/server/web-ui/favicon.ico +0 -0
- package/dist/server/web-ui/index.html +90 -0
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/dist/server/web-ui/manifest.json +27 -0
- package/dist/server/web-ui/manifest.json.br +0 -0
- package/dist/server/web-ui/manifest.json.gz +0 -0
- package/dist/server/web-ui/metadata.json +1 -0
- package/dist/server/web-ui/metadata.json.br +1 -0
- package/dist/server/web-ui/metadata.json.gz +0 -0
- package/dist/server/web-ui/pwa-icon-192.png +0 -0
- package/dist/server/web-ui/pwa-icon-512.png +0 -0
- package/dist/server/web-ui/robots.txt +2 -0
- package/dist/src/server/persisted-config.js +30 -11
- package/package.json +7 -7
|
@@ -16,6 +16,7 @@ export declare function createLoggedNdJsonStream(output: NodeWritableStream, inp
|
|
|
16
16
|
logger: Logger;
|
|
17
17
|
provider: string;
|
|
18
18
|
}): ACPStream;
|
|
19
|
+
export type ACPExtensionCommandsParser = (method: string, params: Record<string, unknown>) => AgentSlashCommand[] | null;
|
|
19
20
|
interface ACPAgentClientOptions {
|
|
20
21
|
provider: string;
|
|
21
22
|
logger: Logger;
|
|
@@ -32,6 +33,7 @@ interface ACPAgentClientOptions {
|
|
|
32
33
|
beforeModeWriter?: (context: ACPProviderModeWriterContext) => Promise<ACPBeforeModeWriteResult>;
|
|
33
34
|
thinkingOptionWriter?: (connection: ClientSideConnection, sessionId: string, thinkingOptionId: string) => Promise<void>;
|
|
34
35
|
capabilities?: AgentCapabilityFlags;
|
|
36
|
+
extensionCommandsParser?: ACPExtensionCommandsParser;
|
|
35
37
|
waitForInitialCommands?: boolean;
|
|
36
38
|
initialCommandsWaitTimeoutMs?: number;
|
|
37
39
|
terminateProcess?: ProcessTerminator;
|
|
@@ -52,6 +54,7 @@ interface ACPAgentSessionOptions {
|
|
|
52
54
|
beforeModeWriter?: (context: ACPProviderModeWriterContext) => Promise<ACPBeforeModeWriteResult>;
|
|
53
55
|
thinkingOptionWriter?: (connection: ClientSideConnection, sessionId: string, thinkingOptionId: string) => Promise<void>;
|
|
54
56
|
capabilities: AgentCapabilityFlags;
|
|
57
|
+
extensionCommandsParser?: ACPExtensionCommandsParser;
|
|
55
58
|
handle?: AgentPersistenceHandle;
|
|
56
59
|
agentId?: string;
|
|
57
60
|
launchEnv?: Record<string, string>;
|
|
@@ -177,6 +180,7 @@ export declare class ACPAgentClient implements AgentClient {
|
|
|
177
180
|
private readonly thinkingOptionWriter?;
|
|
178
181
|
private readonly waitForInitialCommands;
|
|
179
182
|
private readonly initialCommandsWaitTimeoutMs;
|
|
183
|
+
private readonly extensionCommandsParser?;
|
|
180
184
|
protected readonly terminateProcess: ProcessTerminator;
|
|
181
185
|
constructor(options: ACPAgentClientOptions);
|
|
182
186
|
createSession(config: AgentSessionConfig, launchContext?: AgentLaunchContext): Promise<AgentSession>;
|
|
@@ -251,6 +255,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
|
|
|
251
255
|
private commandsReadySettled;
|
|
252
256
|
private waitForInitialCommands;
|
|
253
257
|
private initialCommandsWaitTimeoutMs;
|
|
258
|
+
private readonly extensionCommandsParser?;
|
|
254
259
|
private currentTurnUsage;
|
|
255
260
|
private activeForegroundTurnId;
|
|
256
261
|
private closed;
|
|
@@ -261,6 +266,13 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
|
|
|
261
266
|
constructor(config: AgentSessionConfig, options: ACPAgentSessionOptions);
|
|
262
267
|
get id(): string | null;
|
|
263
268
|
initializeNewSession(): Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* IMPORTANT: Some ACP providers (e.g., Devin CLI) require all three params
|
|
271
|
+
* (sessionId, cwd, mcpServers) to be present in session/load or
|
|
272
|
+
* unstable_resumeSession — even when mcpServers is an empty array — and
|
|
273
|
+
* return "Invalid params" if any are omitted. Never drop cwd or mcpServers
|
|
274
|
+
* from these calls regardless of capabilities.
|
|
275
|
+
*/
|
|
264
276
|
initializeResumedSession(): Promise<void>;
|
|
265
277
|
run(prompt: AgentPromptInput, options?: AgentRunOptions): Promise<AgentRunResult>;
|
|
266
278
|
startTurn(prompt: AgentPromptInput, options?: AgentRunOptions): Promise<{
|
|
@@ -292,6 +304,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
|
|
|
292
304
|
requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse>;
|
|
293
305
|
sessionUpdate(params: SessionNotification): Promise<void>;
|
|
294
306
|
extNotification(method: string, params: Record<string, unknown>): Promise<void>;
|
|
307
|
+
private applyResolvedCommands;
|
|
295
308
|
readTextFile(params: ReadTextFileRequest): Promise<{
|
|
296
309
|
content: string;
|
|
297
310
|
}>;
|
|
@@ -318,6 +318,7 @@ export class ACPAgentClient {
|
|
|
318
318
|
this.thinkingOptionWriter = options.thinkingOptionWriter;
|
|
319
319
|
this.waitForInitialCommands = options.waitForInitialCommands ?? false;
|
|
320
320
|
this.initialCommandsWaitTimeoutMs = options.initialCommandsWaitTimeoutMs ?? 1500;
|
|
321
|
+
this.extensionCommandsParser = options.extensionCommandsParser;
|
|
321
322
|
}
|
|
322
323
|
async createSession(config, launchContext) {
|
|
323
324
|
this.assertProvider(config);
|
|
@@ -339,6 +340,7 @@ export class ACPAgentClient {
|
|
|
339
340
|
capabilities: this.capabilities,
|
|
340
341
|
agentId: launchContext?.agentId,
|
|
341
342
|
launchEnv: launchContext?.env,
|
|
343
|
+
extensionCommandsParser: this.extensionCommandsParser,
|
|
342
344
|
waitForInitialCommands: this.waitForInitialCommands,
|
|
343
345
|
initialCommandsWaitTimeoutMs: this.initialCommandsWaitTimeoutMs,
|
|
344
346
|
});
|
|
@@ -379,6 +381,7 @@ export class ACPAgentClient {
|
|
|
379
381
|
handle,
|
|
380
382
|
agentId: launchContext?.agentId,
|
|
381
383
|
launchEnv: launchContext?.env,
|
|
384
|
+
extensionCommandsParser: this.extensionCommandsParser,
|
|
382
385
|
waitForInitialCommands: this.waitForInitialCommands,
|
|
383
386
|
initialCommandsWaitTimeoutMs: this.initialCommandsWaitTimeoutMs,
|
|
384
387
|
});
|
|
@@ -386,7 +389,7 @@ export class ACPAgentClient {
|
|
|
386
389
|
return session;
|
|
387
390
|
}
|
|
388
391
|
async fetchCatalog(options) {
|
|
389
|
-
const
|
|
392
|
+
const cwd = options.scope === "global" ? homedir() : options.cwd;
|
|
390
393
|
const timeoutMs = options.timeoutMs ?? ACP_CATALOG_TIMEOUT_MS;
|
|
391
394
|
let probe = null;
|
|
392
395
|
try {
|
|
@@ -784,6 +787,7 @@ export class ACPAgentSession {
|
|
|
784
787
|
this.currentTitle = config.title ?? null;
|
|
785
788
|
this.waitForInitialCommands = options.waitForInitialCommands ?? false;
|
|
786
789
|
this.initialCommandsWaitTimeoutMs = options.initialCommandsWaitTimeoutMs ?? 1500;
|
|
790
|
+
this.extensionCommandsParser = options.extensionCommandsParser;
|
|
787
791
|
}
|
|
788
792
|
get id() {
|
|
789
793
|
return this.sessionId;
|
|
@@ -802,6 +806,13 @@ export class ACPAgentSession {
|
|
|
802
806
|
this.applySessionState(response);
|
|
803
807
|
await this.applyConfiguredOverrides();
|
|
804
808
|
}
|
|
809
|
+
/**
|
|
810
|
+
* IMPORTANT: Some ACP providers (e.g., Devin CLI) require all three params
|
|
811
|
+
* (sessionId, cwd, mcpServers) to be present in session/load or
|
|
812
|
+
* unstable_resumeSession — even when mcpServers is an empty array — and
|
|
813
|
+
* return "Invalid params" if any are omitted. Never drop cwd or mcpServers
|
|
814
|
+
* from these calls regardless of capabilities.
|
|
815
|
+
*/
|
|
805
816
|
async initializeResumedSession() {
|
|
806
817
|
const handle = this.initialHandle;
|
|
807
818
|
if (!handle) {
|
|
@@ -1408,6 +1419,31 @@ export class ACPAgentSession {
|
|
|
1408
1419
|
method,
|
|
1409
1420
|
rawEvent: params,
|
|
1410
1421
|
}, "provider.acp.extension_notification");
|
|
1422
|
+
const parsedCommands = this.extensionCommandsParser?.(method, params);
|
|
1423
|
+
if (parsedCommands) {
|
|
1424
|
+
this.applyResolvedCommands(parsedCommands, {
|
|
1425
|
+
sessionId: typeof params.sessionId === "string" ? params.sessionId : undefined,
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
// Cache an asynchronously-delivered slash-command batch and unblock any
|
|
1430
|
+
// listCommands() call that is waiting on the initial batch. Used when a
|
|
1431
|
+
// provider supplies an extensionCommandsParser whose result arrives after
|
|
1432
|
+
// session/new (e.g. via a vendor extension notification). The ready gate is
|
|
1433
|
+
// always settled — even for an empty batch — so a provider that legitimately
|
|
1434
|
+
// reports no commands does not leave listCommands() blocked for the full
|
|
1435
|
+
// initial-commands timeout. An optional sessionId scopes the batch to this
|
|
1436
|
+
// session; notifications addressed to a different session are ignored.
|
|
1437
|
+
applyResolvedCommands(commands, options) {
|
|
1438
|
+
if (options?.sessionId !== undefined &&
|
|
1439
|
+
this.sessionId !== null &&
|
|
1440
|
+
options.sessionId !== this.sessionId) {
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
if (commands.length > 0) {
|
|
1444
|
+
this.cachedCommands = commands;
|
|
1445
|
+
}
|
|
1446
|
+
this.settleCommandsReady();
|
|
1411
1447
|
}
|
|
1412
1448
|
async readTextFile(params) {
|
|
1413
1449
|
const raw = await fs.readFile(params.path, "utf8");
|
|
@@ -22,6 +22,7 @@ import { getAgentStreamEventTurnId, } from "../../agent-sdk-types.js";
|
|
|
22
22
|
import { importSessionFromPersistence } from "../../provider-session-import.js";
|
|
23
23
|
import { checkProviderLaunchAvailable, createProviderEnv, createProviderEnvSpec, resolveProviderLaunch, } from "../../provider-launch-config.js";
|
|
24
24
|
import { withTimeout } from "../../../../utils/promise-timeout.js";
|
|
25
|
+
import { terminateWithTreeKill } from "../../../../utils/tree-kill.js";
|
|
25
26
|
import { execCommand } from "../../../../utils/spawn.js";
|
|
26
27
|
import { composeSystemPromptParts } from "../../system-prompt.js";
|
|
27
28
|
const fsPromises = promises;
|
|
@@ -1289,6 +1290,13 @@ function readLegacyResultUsageTokens(usage) {
|
|
|
1289
1290
|
function isClaudeSubagentToolName(name) {
|
|
1290
1291
|
return name === "Task" || name === "Agent";
|
|
1291
1292
|
}
|
|
1293
|
+
function readClaudeParentToolUseId(message) {
|
|
1294
|
+
if (!("parent_tool_use_id" in message)) {
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
const parentToolUseId = message.parent_tool_use_id;
|
|
1298
|
+
return typeof parentToolUseId === "string" && parentToolUseId.length > 0 ? parentToolUseId : null;
|
|
1299
|
+
}
|
|
1292
1300
|
class ClaudeContextUsageState {
|
|
1293
1301
|
constructor(initialContextWindowMaxTokens) {
|
|
1294
1302
|
this.completedResultTurns = 0;
|
|
@@ -1414,6 +1422,7 @@ class ClaudeAgentSession {
|
|
|
1414
1422
|
this.provider = "claude";
|
|
1415
1423
|
this.capabilities = CLAUDE_CAPABILITIES;
|
|
1416
1424
|
this.query = null;
|
|
1425
|
+
this.childProcess = null;
|
|
1417
1426
|
this.input = null;
|
|
1418
1427
|
this.planResumeMode = null;
|
|
1419
1428
|
this.availableModes = DEFAULT_MODES;
|
|
@@ -1887,6 +1896,19 @@ class ClaudeAgentSession {
|
|
|
1887
1896
|
await this.awaitWithTimeout(this.query?.return?.(), "close query return");
|
|
1888
1897
|
this.query = null;
|
|
1889
1898
|
this.input = null;
|
|
1899
|
+
// Terminate the entire process tree (claude + MCP children) to prevent
|
|
1900
|
+
// orphan accumulation. The SDK's internal cleanup may only kill the
|
|
1901
|
+
// direct child process.
|
|
1902
|
+
if (this.childProcess) {
|
|
1903
|
+
const result = await terminateWithTreeKill(this.childProcess, {
|
|
1904
|
+
gracefulTimeoutMs: 2000,
|
|
1905
|
+
forceTimeoutMs: 2000,
|
|
1906
|
+
});
|
|
1907
|
+
if (result === "kill-timeout") {
|
|
1908
|
+
this.logger.warn({ pid: this.childProcess.pid, agentId: this.agentId }, "Claude process tree did not report exit after SIGKILL");
|
|
1909
|
+
}
|
|
1910
|
+
this.childProcess = null;
|
|
1911
|
+
}
|
|
1890
1912
|
if (this.persistSession === false && this.claudeSessionId) {
|
|
1891
1913
|
// Claude Code currently ignores --no-session-persistence outside --print mode
|
|
1892
1914
|
// (see `claude --help`), so the SDK's persistSession=false is silently dropped
|
|
@@ -2225,6 +2247,18 @@ class ClaudeAgentSession {
|
|
|
2225
2247
|
catch {
|
|
2226
2248
|
/* ignore */
|
|
2227
2249
|
}
|
|
2250
|
+
// Tree-kill the old process tree now that the SDK has cleaned up.
|
|
2251
|
+
// If we skip this, MCP children of the previous claude process can
|
|
2252
|
+
// survive as orphans when the session spawns a replacement query.
|
|
2253
|
+
if (this.childProcess) {
|
|
2254
|
+
await terminateWithTreeKill(this.childProcess, {
|
|
2255
|
+
gracefulTimeoutMs: 2000,
|
|
2256
|
+
forceTimeoutMs: 2000,
|
|
2257
|
+
}).catch(() => {
|
|
2258
|
+
/* process may already be dead */
|
|
2259
|
+
});
|
|
2260
|
+
this.childProcess = null;
|
|
2261
|
+
}
|
|
2228
2262
|
}
|
|
2229
2263
|
// Preserve claudeSessionId across query recreation so buildOptions() passes
|
|
2230
2264
|
// resume: sessionId and the new query continues the existing conversation.
|
|
@@ -2237,6 +2271,9 @@ class ClaudeAgentSession {
|
|
|
2237
2271
|
runtimeSettings: this.runtimeSettings,
|
|
2238
2272
|
launchEnv: this.launchEnv,
|
|
2239
2273
|
queryFactory: this.queryFactory,
|
|
2274
|
+
onChildProcess: (child) => {
|
|
2275
|
+
this.childProcess = child;
|
|
2276
|
+
},
|
|
2240
2277
|
});
|
|
2241
2278
|
const fastMode = this.resolveFastModeSetting();
|
|
2242
2279
|
if (fastMode !== null) {
|
|
@@ -2809,17 +2846,19 @@ class ClaudeAgentSession {
|
|
|
2809
2846
|
suppressAssistantText: true,
|
|
2810
2847
|
suppressReasoning: true,
|
|
2811
2848
|
});
|
|
2812
|
-
const assistantTimelineEvents =
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2849
|
+
const assistantTimelineEvents = readClaudeParentToolUseId(message)
|
|
2850
|
+
? []
|
|
2851
|
+
: this.timelineAssembler
|
|
2852
|
+
.consume({
|
|
2853
|
+
message,
|
|
2854
|
+
runId: turnId,
|
|
2855
|
+
messageIdHint,
|
|
2856
|
+
})
|
|
2857
|
+
.map((item) => ({
|
|
2858
|
+
type: "timeline",
|
|
2859
|
+
item,
|
|
2860
|
+
provider: "claude",
|
|
2861
|
+
}));
|
|
2823
2862
|
return [...messageEvents, ...assistantTimelineEvents];
|
|
2824
2863
|
}
|
|
2825
2864
|
async handleMissingResumedConversation(message, activeQuery) {
|
|
@@ -2867,9 +2906,7 @@ class ClaudeAgentSession {
|
|
|
2867
2906
|
}
|
|
2868
2907
|
}
|
|
2869
2908
|
translateMessageToEvents(message, options) {
|
|
2870
|
-
const parentToolUseId =
|
|
2871
|
-
? message.parent_tool_use_id
|
|
2872
|
-
: null;
|
|
2909
|
+
const parentToolUseId = readClaudeParentToolUseId(message);
|
|
2873
2910
|
if (parentToolUseId) {
|
|
2874
2911
|
return this.sidechainTracker.handleMessage(message, parentToolUseId);
|
|
2875
2912
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
1
2
|
import { query, type Options, type Query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
3
|
import { type ProviderRuntimeSettings } from "../../provider-launch-config.js";
|
|
3
4
|
export type ClaudeOptions = Options;
|
|
@@ -9,6 +10,8 @@ export interface ClaudeQueryContext {
|
|
|
9
10
|
runtimeSettings?: ProviderRuntimeSettings;
|
|
10
11
|
launchEnv?: Record<string, string>;
|
|
11
12
|
queryFactory?: ClaudeQueryFactory;
|
|
13
|
+
/** Called with the spawned child process so the caller can tree-kill it on close. */
|
|
14
|
+
onChildProcess?: (child: ChildProcess) => void;
|
|
12
15
|
}
|
|
13
16
|
export declare function claudeQuery(input: ClaudeQueryInput, context?: ClaudeQueryContext): Query;
|
|
14
17
|
//# sourceMappingURL=query.d.ts.map
|
|
@@ -24,7 +24,8 @@ function resolveClaudeSpawnCommand(spawnOptions, runtimeSettings) {
|
|
|
24
24
|
args: [...commandConfig.argv.slice(1), ...spawnOptions.args],
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
function applyRuntimeSettingsToClaudeOptions(options,
|
|
27
|
+
function applyRuntimeSettingsToClaudeOptions(options, context) {
|
|
28
|
+
const { runtimeSettings, launchEnv, onChildProcess } = context;
|
|
28
29
|
return {
|
|
29
30
|
...options,
|
|
30
31
|
spawnClaudeCodeProcess: (spawnOptions) => {
|
|
@@ -62,6 +63,7 @@ function applyRuntimeSettingsToClaudeOptions(options, runtimeSettings, launchEnv
|
|
|
62
63
|
// The command is always a resolved binary path, so shell routing is unnecessary.
|
|
63
64
|
shell: false,
|
|
64
65
|
});
|
|
66
|
+
onChildProcess?.(child);
|
|
65
67
|
if (typeof options.stderr === "function") {
|
|
66
68
|
child.stderr?.on("data", (chunk) => {
|
|
67
69
|
options.stderr?.(chunk.toString());
|
|
@@ -78,7 +80,7 @@ export function claudeQuery(input, context = {}) {
|
|
|
78
80
|
const launchQuery = context.queryFactory ?? query;
|
|
79
81
|
return launchQuery({
|
|
80
82
|
...input,
|
|
81
|
-
options: applyRuntimeSettingsToClaudeOptions(input.options, context
|
|
83
|
+
options: applyRuntimeSettingsToClaudeOptions(input.options, context),
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
//# sourceMappingURL=query.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Logger } from "pino";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { ACPAgentClient } from "./acp-agent.js";
|
|
3
|
+
import { ACPAgentClient, type ACPExtensionCommandsParser } from "./acp-agent.js";
|
|
4
4
|
export declare const GenericACPProviderParamsSchema: z.ZodObject<{
|
|
5
5
|
supportsMcpServers: z.ZodOptional<z.ZodBoolean>;
|
|
6
6
|
}, z.core.$loose>;
|
|
@@ -14,6 +14,7 @@ interface GenericACPAgentClientOptions {
|
|
|
14
14
|
waitForInitialCommands?: boolean;
|
|
15
15
|
initialCommandsWaitTimeoutMs?: number;
|
|
16
16
|
diagnosticPhaseTimeoutMs?: number;
|
|
17
|
+
extensionCommandsParser?: ACPExtensionCommandsParser;
|
|
17
18
|
}
|
|
18
19
|
export declare class GenericACPAgentClient extends ACPAgentClient {
|
|
19
20
|
private readonly command;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { checkProviderLaunchAvailable, resolveProviderLaunch } from "../provider-launch-config.js";
|
|
3
|
-
import { ACPAgentClient, DEFAULT_ACP_CAPABILITIES } from "./acp-agent.js";
|
|
3
|
+
import { ACPAgentClient, DEFAULT_ACP_CAPABILITIES, } from "./acp-agent.js";
|
|
4
4
|
import { buildBinaryDiagnosticRows, formatProviderDiagnostic, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
|
|
5
5
|
export const GenericACPProviderParamsSchema = z
|
|
6
6
|
.object({
|
|
@@ -19,6 +19,7 @@ export class GenericACPAgentClient extends ACPAgentClient {
|
|
|
19
19
|
capabilities: buildGenericACPCapabilities(options),
|
|
20
20
|
waitForInitialCommands: options.waitForInitialCommands,
|
|
21
21
|
initialCommandsWaitTimeoutMs: options.initialCommandsWaitTimeoutMs,
|
|
22
|
+
extensionCommandsParser: options.extensionCommandsParser,
|
|
22
23
|
});
|
|
23
24
|
this.command = options.command;
|
|
24
25
|
this.providerId = options.providerId;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
import type { ACPExtensionCommandsParser } from "./acp-agent.js";
|
|
3
|
+
import { GenericACPAgentClient } from "./generic-acp-agent.js";
|
|
4
|
+
interface KiroACPAgentClientOptions {
|
|
5
|
+
logger: Logger;
|
|
6
|
+
command: [string, ...string[]];
|
|
7
|
+
env?: Record<string, string>;
|
|
8
|
+
providerId?: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
providerParams?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export declare const parseKiroExtensionCommands: ACPExtensionCommandsParser;
|
|
13
|
+
export declare class KiroACPAgentClient extends GenericACPAgentClient {
|
|
14
|
+
constructor(options: KiroACPAgentClientOptions);
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=kiro-acp-agent.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { GenericACPAgentClient } from "./generic-acp-agent.js";
|
|
2
|
+
// Kiro CLI publishes its slash commands and skills asynchronously through the
|
|
3
|
+
// `_kiro.dev/commands/available` extension notification shortly after
|
|
4
|
+
// `session/new` resolves. Wait for that first batch so listCommands() doesn't
|
|
5
|
+
// resolve to an empty list before Kiro has reported its commands.
|
|
6
|
+
const KIRO_INITIAL_COMMANDS_WAIT_TIMEOUT_MS = 10000;
|
|
7
|
+
// ACP extension method (per the `_`-prefixed vendor namespace convention) that
|
|
8
|
+
// Kiro CLI uses to publish its slash commands and skills after session/new.
|
|
9
|
+
const KIRO_COMMANDS_AVAILABLE_METHOD = "_kiro.dev/commands/available";
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
// Maps a `_kiro.dev/commands/available` payload onto Paseo slash commands.
|
|
14
|
+
// Kiro reports built-in slash commands under `commands` (names arrive with a
|
|
15
|
+
// leading "/", e.g. "/agent") and skills/prompts under `prompts` (names without
|
|
16
|
+
// a slash, tagged with a `skill:` serverName). Paseo stores command names
|
|
17
|
+
// without the leading slash — the composer prepends it on insertion.
|
|
18
|
+
function mapKiroAvailableCommands(params) {
|
|
19
|
+
const result = [];
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
const pushEntry = (entry) => {
|
|
22
|
+
if (!isRecord(entry)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const rawName = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
26
|
+
const name = rawName.replace(/^\/+/, "");
|
|
27
|
+
if (!name || seen.has(name)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
seen.add(name);
|
|
31
|
+
const description = typeof entry.description === "string" ? entry.description : "";
|
|
32
|
+
const meta = isRecord(entry.meta) ? entry.meta : null;
|
|
33
|
+
const argumentHint = meta && typeof meta.hint === "string" ? meta.hint : "";
|
|
34
|
+
const serverName = typeof entry.serverName === "string" ? entry.serverName : "";
|
|
35
|
+
const kind = serverName.startsWith("skill:") ? "skill" : "command";
|
|
36
|
+
result.push({ name, description, argumentHint, kind });
|
|
37
|
+
};
|
|
38
|
+
if (Array.isArray(params.commands)) {
|
|
39
|
+
for (const entry of params.commands) {
|
|
40
|
+
pushEntry(entry);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(params.prompts)) {
|
|
44
|
+
for (const entry of params.prompts) {
|
|
45
|
+
pushEntry(entry);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
// Provider-specific parser injected into the generic ACP session via the
|
|
51
|
+
// `extensionCommandsParser` option (mirrors how Cursor/Copilot inject their
|
|
52
|
+
// behavior through constructor options). Kiro advertises its slash commands and
|
|
53
|
+
// skills through the `_kiro.dev/commands/available` extension notification
|
|
54
|
+
// instead of the standard `available_commands_update` session update; this
|
|
55
|
+
// recognizes that one method and returns the parsed commands (possibly empty),
|
|
56
|
+
// or null for any other notification so the base session ignores it.
|
|
57
|
+
export const parseKiroExtensionCommands = (method, params) => {
|
|
58
|
+
if (method !== KIRO_COMMANDS_AVAILABLE_METHOD) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return mapKiroAvailableCommands(params);
|
|
62
|
+
};
|
|
63
|
+
export class KiroACPAgentClient extends GenericACPAgentClient {
|
|
64
|
+
constructor(options) {
|
|
65
|
+
super({
|
|
66
|
+
logger: options.logger,
|
|
67
|
+
command: options.command,
|
|
68
|
+
env: options.env,
|
|
69
|
+
providerId: options.providerId,
|
|
70
|
+
label: options.label,
|
|
71
|
+
providerParams: options.providerParams,
|
|
72
|
+
waitForInitialCommands: true,
|
|
73
|
+
initialCommandsWaitTimeoutMs: KIRO_INITIAL_COMMANDS_WAIT_TIMEOUT_MS,
|
|
74
|
+
extensionCommandsParser: parseKiroExtensionCommands,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=kiro-acp-agent.js.map
|
|
@@ -439,12 +439,14 @@ export class MockLoadTestAgentSession {
|
|
|
439
439
|
}
|
|
440
440
|
const profile = resolveModelProfile(this.modelId);
|
|
441
441
|
const turnId = randomUUID();
|
|
442
|
+
const assistantMessageId = randomUUID();
|
|
442
443
|
let resolve;
|
|
443
444
|
const completed = new Promise((promiseResolve) => {
|
|
444
445
|
resolve = promiseResolve;
|
|
445
446
|
});
|
|
446
447
|
const turn = {
|
|
447
448
|
turnId,
|
|
449
|
+
assistantMessageId,
|
|
448
450
|
prompt,
|
|
449
451
|
startedAt: Date.now(),
|
|
450
452
|
cycle: 0,
|
|
@@ -655,6 +657,7 @@ export class MockLoadTestAgentSession {
|
|
|
655
657
|
this.emitTimeline(turn.turnId, {
|
|
656
658
|
type: "assistant_message",
|
|
657
659
|
text: finalText,
|
|
660
|
+
messageId: turn.assistantMessageId,
|
|
658
661
|
});
|
|
659
662
|
this.activeTurn = null;
|
|
660
663
|
this.emit({
|
|
@@ -669,6 +672,7 @@ export class MockLoadTestAgentSession {
|
|
|
669
672
|
{
|
|
670
673
|
type: "assistant_message",
|
|
671
674
|
text: finalText,
|
|
675
|
+
messageId: turn.assistantMessageId,
|
|
672
676
|
},
|
|
673
677
|
],
|
|
674
678
|
canceled: false,
|
|
@@ -768,6 +772,7 @@ export class MockLoadTestAgentSession {
|
|
|
768
772
|
? {
|
|
769
773
|
type: "assistant_message",
|
|
770
774
|
text: `stress-update-${index}`,
|
|
775
|
+
messageId: turn.assistantMessageId,
|
|
771
776
|
}
|
|
772
777
|
: {
|
|
773
778
|
type: "todo",
|
|
@@ -834,6 +839,7 @@ export class MockLoadTestAgentSession {
|
|
|
834
839
|
this.emitTimeline(turn.turnId, {
|
|
835
840
|
type: "assistant_message",
|
|
836
841
|
text: `data:image/png;base64,${payload}`,
|
|
842
|
+
messageId: turn.assistantMessageId,
|
|
837
843
|
});
|
|
838
844
|
}
|
|
839
845
|
this.activeTurn = null;
|
|
@@ -892,6 +898,7 @@ export class MockLoadTestAgentSession {
|
|
|
892
898
|
this.emitTimeline(turn.turnId, {
|
|
893
899
|
type: "assistant_message",
|
|
894
900
|
text: event.text,
|
|
901
|
+
messageId: turn.assistantMessageId,
|
|
895
902
|
});
|
|
896
903
|
return;
|
|
897
904
|
}
|
|
@@ -933,6 +940,7 @@ export class MockLoadTestAgentSession {
|
|
|
933
940
|
this.emitTimeline(turn.turnId, {
|
|
934
941
|
type: "assistant_message",
|
|
935
942
|
text: "\n\n_(end of synthetic stream)_\n",
|
|
943
|
+
messageId: turn.assistantMessageId,
|
|
936
944
|
});
|
|
937
945
|
this.finishTurnWithText(turn, "Synthetic load test complete");
|
|
938
946
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { resolvePaseoHome } from "../../../paseo-home.js";
|
|
3
|
+
const OPENCODE_HOME_DIRNAME = "opencode-home";
|
|
4
|
+
export function resolveOpenCodeHomeDir(env = process.env) {
|
|
5
|
+
return path.join(resolvePaseoHome(env), OPENCODE_HOME_DIRNAME);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -46,6 +46,7 @@ export interface OpenCodeServerManagerOptions {
|
|
|
46
46
|
terminateProcess?: ProcessTerminator;
|
|
47
47
|
portAllocator?: OpenCodePortAllocator;
|
|
48
48
|
resolveCommandPrefix?: OpenCodeCommandPrefixResolver;
|
|
49
|
+
resolveHomeDir?: () => string;
|
|
49
50
|
spawnServerProcess?: OpenCodeServerProcessSpawner;
|
|
50
51
|
}
|
|
51
52
|
export declare class OpenCodeServerManager implements OpenCodeServerManagerLike {
|
|
@@ -62,6 +63,7 @@ export declare class OpenCodeServerManager implements OpenCodeServerManagerLike
|
|
|
62
63
|
private readonly terminateProcess;
|
|
63
64
|
private readonly portAllocator;
|
|
64
65
|
private readonly resolveCommandPrefix;
|
|
66
|
+
private readonly resolveHomeDir;
|
|
65
67
|
private readonly spawnServerProcess;
|
|
66
68
|
constructor(options: OpenCodeServerManagerOptions);
|
|
67
69
|
static getInstance(logger: Logger, runtimeSettings?: ProviderRuntimeSettings, options?: Omit<OpenCodeServerManagerOptions, "logger" | "runtimeSettings">): OpenCodeServerManager;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
1
3
|
import net from "node:net";
|
|
2
|
-
import
|
|
4
|
+
import path from "node:path";
|
|
3
5
|
import { findExecutable } from "../../../../executable-resolution/executable-resolution.js";
|
|
4
6
|
import { spawnProcess } from "../../../../utils/spawn.js";
|
|
5
7
|
import { terminateWithTreeKill } from "../../../../utils/tree-kill.js";
|
|
6
8
|
import { createProviderEnvSpec, resolveProviderCommandPrefix, } from "../../provider-launch-config.js";
|
|
9
|
+
import { resolveOpenCodeHomeDir } from "./paths.js";
|
|
7
10
|
const OPENCODE_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
8
11
|
const OPENCODE_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS = 1000;
|
|
9
12
|
export class OpenCodeServerManager {
|
|
@@ -21,6 +24,7 @@ export class OpenCodeServerManager {
|
|
|
21
24
|
this.resolveCommandPrefix =
|
|
22
25
|
options.resolveCommandPrefix ??
|
|
23
26
|
(() => resolveProviderCommandPrefix(this.runtimeSettings?.command, resolveOpenCodeBinary));
|
|
27
|
+
this.resolveHomeDir = options.resolveHomeDir ?? resolveOpenCodeHomeDir;
|
|
24
28
|
this.spawnServerProcess = options.spawnServerProcess ?? spawnProcess;
|
|
25
29
|
}
|
|
26
30
|
static getInstance(logger, runtimeSettings, options = {}) {
|
|
@@ -164,7 +168,11 @@ export class OpenCodeServerManager {
|
|
|
164
168
|
const url = `http://127.0.0.1:${port}`;
|
|
165
169
|
const launchPrefix = await this.resolveCommandPrefix();
|
|
166
170
|
const serverArgs = [...launchPrefix.args, "serve", "--port", String(port)];
|
|
167
|
-
|
|
171
|
+
// Use a neutral OpenCode home as the server cwd. Launching from the user's
|
|
172
|
+
// home directory causes OpenCode to treat it as the default workspace and
|
|
173
|
+
// index the entire home tree.
|
|
174
|
+
const serverCwd = this.resolveHomeDir();
|
|
175
|
+
mkdirSync(serverCwd, { recursive: true });
|
|
168
176
|
const serverProcess = this.spawnServerProcess(launchPrefix.command, serverArgs, {
|
|
169
177
|
cwd: serverCwd,
|
|
170
178
|
detached: process.platform !== "win32",
|
|
@@ -371,10 +379,31 @@ OpenCodeServerManager.instance = null;
|
|
|
371
379
|
OpenCodeServerManager.exitHandlerRegistered = false;
|
|
372
380
|
async function resolveOpenCodeBinary() {
|
|
373
381
|
const found = await findExecutable("opencode");
|
|
374
|
-
if (found) {
|
|
375
|
-
|
|
382
|
+
if (!found) {
|
|
383
|
+
throw new Error("OpenCode binary not found. Install OpenCode (https://github.com/opencode-ai/opencode) and ensure it is available in your shell PATH.");
|
|
384
|
+
}
|
|
385
|
+
if (process.platform === "win32" && path.extname(found).toLowerCase() === ".cmd") {
|
|
386
|
+
// Global npm: <prefix>/opencode.cmd → <prefix>/node_modules/opencode-ai/bin/opencode.exe
|
|
387
|
+
const globalCandidate = path.join(path.dirname(found), "node_modules", "opencode-ai", "bin", "opencode.exe");
|
|
388
|
+
if (await pathExists(globalCandidate))
|
|
389
|
+
return globalCandidate;
|
|
390
|
+
// Local/pnpm: <project>/node_modules/.bin/opencode.cmd → <project>/node_modules/opencode-ai/bin/opencode.exe
|
|
391
|
+
const localCandidate = path.join(path.dirname(found), "..", "opencode-ai", "bin", "opencode.exe");
|
|
392
|
+
if (await pathExists(localCandidate))
|
|
393
|
+
return localCandidate;
|
|
394
|
+
console.warn("[opencode-server] Found opencode.cmd but could not resolve the real opencode.exe. " +
|
|
395
|
+
"The process may not be properly terminated on exit. Path: %s", found);
|
|
396
|
+
}
|
|
397
|
+
return found;
|
|
398
|
+
}
|
|
399
|
+
async function pathExists(filePath) {
|
|
400
|
+
try {
|
|
401
|
+
await stat(filePath);
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
return false;
|
|
376
406
|
}
|
|
377
|
-
throw new Error("OpenCode binary not found. Install OpenCode (https://github.com/opencode-ai/opencode) and ensure it is available in your shell PATH.");
|
|
378
407
|
}
|
|
379
408
|
function findAvailablePort() {
|
|
380
409
|
return new Promise((resolve, reject) => {
|
|
@@ -4,6 +4,7 @@ import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOpt
|
|
|
4
4
|
import { isDefaultAgentCreateConfigUnattended } from "../create-agent-mode.js";
|
|
5
5
|
import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
|
|
6
6
|
import { type OpenCodeServerManagerLike } from "./opencode/server-manager.js";
|
|
7
|
+
import { resolveOpenCodeHomeDir } from "./opencode/paths.js";
|
|
7
8
|
import type { ManagedProcessRegistry } from "../../managed-processes/managed-processes.js";
|
|
8
9
|
declare function resolveOpenCodeCreateConfig(input: ResolveAgentCreateConfigInput): ResolveAgentCreateConfigResult;
|
|
9
10
|
declare function isOpenCodeCreateConfigUnattended(input: Parameters<typeof isDefaultAgentCreateConfigUnattended>[0]): boolean;
|
|
@@ -100,11 +101,13 @@ export declare const __openCodeInternals: {
|
|
|
100
101
|
resolveOpenCodeSelectedModelContextWindow: typeof resolveOpenCodeSelectedModelContextWindow;
|
|
101
102
|
isSelectableOpenCodeAgent: typeof isSelectableOpenCodeAgent;
|
|
102
103
|
mapOpenCodeAgentToMode: typeof mapOpenCodeAgentToMode;
|
|
104
|
+
resolveOpenCodeHomeDir: typeof resolveOpenCodeHomeDir;
|
|
103
105
|
readonly OpenCodeAgentSession: typeof OpenCodeAgentSession;
|
|
104
106
|
};
|
|
105
107
|
interface OpenCodeAgentClientDeps {
|
|
106
108
|
serverManager?: OpenCodeServerManagerLike;
|
|
107
109
|
createClient?: OpenCodeClientFactory;
|
|
110
|
+
resolveHomeDir?: () => string;
|
|
108
111
|
managedProcesses?: ManagedProcessRegistry;
|
|
109
112
|
}
|
|
110
113
|
type OpenCodeClientFactory = (options: {
|
|
@@ -118,6 +121,7 @@ export declare class OpenCodeAgentClient implements AgentClient {
|
|
|
118
121
|
readonly isCreateConfigUnattended: typeof isOpenCodeCreateConfigUnattended;
|
|
119
122
|
private readonly serverManager;
|
|
120
123
|
private readonly createOpenCodeClient;
|
|
124
|
+
private readonly resolveHomeDir;
|
|
121
125
|
private readonly logger;
|
|
122
126
|
private readonly runtimeSettings?;
|
|
123
127
|
private readonly modelContextWindows;
|