ds-harness-remote 0.5.10 → 0.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/acp/adapters/claude-process.js +1 -1
  2. package/dist/acp/adapters/claude-process.js.map +1 -1
  3. package/dist/acp/adapters/cursor-process.d.ts +3 -0
  4. package/dist/acp/adapters/cursor-process.d.ts.map +1 -1
  5. package/dist/acp/adapters/cursor-process.js +55 -1
  6. package/dist/acp/adapters/cursor-process.js.map +1 -1
  7. package/dist/acp/adapters/isolated-process.d.ts +45 -0
  8. package/dist/acp/adapters/isolated-process.d.ts.map +1 -0
  9. package/dist/acp/adapters/isolated-process.js +308 -0
  10. package/dist/acp/adapters/isolated-process.js.map +1 -0
  11. package/dist/acp/adapters/opencode-native-api.d.ts +16 -0
  12. package/dist/acp/adapters/opencode-native-api.d.ts.map +1 -0
  13. package/dist/acp/adapters/opencode-native-api.js +103 -0
  14. package/dist/acp/adapters/opencode-native-api.js.map +1 -0
  15. package/dist/acp/adapters/opencode-process.d.ts +22 -2
  16. package/dist/acp/adapters/opencode-process.d.ts.map +1 -1
  17. package/dist/acp/adapters/opencode-process.js +273 -27
  18. package/dist/acp/adapters/opencode-process.js.map +1 -1
  19. package/dist/acp/adapters/pi-process.d.ts +3 -40
  20. package/dist/acp/adapters/pi-process.d.ts.map +1 -1
  21. package/dist/acp/adapters/pi-process.js +3 -408
  22. package/dist/acp/adapters/pi-process.js.map +1 -1
  23. package/dist/acp/adapters/session-models.d.ts +33 -0
  24. package/dist/acp/adapters/session-models.d.ts.map +1 -0
  25. package/dist/acp/adapters/session-models.js +78 -0
  26. package/dist/acp/adapters/session-models.js.map +1 -0
  27. package/dist/acp/gateway.d.ts +4 -1
  28. package/dist/acp/gateway.d.ts.map +1 -1
  29. package/dist/acp/gateway.js +182 -28
  30. package/dist/acp/gateway.js.map +1 -1
  31. package/dist/acp/history-events.d.ts +8 -0
  32. package/dist/acp/history-events.d.ts.map +1 -0
  33. package/dist/acp/history-events.js +119 -0
  34. package/dist/acp/history-events.js.map +1 -0
  35. package/dist/acp/method-policy.d.ts +26 -16
  36. package/dist/acp/method-policy.d.ts.map +1 -1
  37. package/dist/acp/method-policy.js +6 -5
  38. package/dist/acp/method-policy.js.map +1 -1
  39. package/dist/acp/peer-bridge.d.ts +3 -2
  40. package/dist/acp/peer-bridge.d.ts.map +1 -1
  41. package/dist/acp/peer-bridge.js +6 -1
  42. package/dist/acp/peer-bridge.js.map +1 -1
  43. package/dist/acp/virtual-catalog.d.ts +5 -5
  44. package/dist/acp/virtual-catalog.d.ts.map +1 -1
  45. package/dist/acp/virtual-catalog.js +8 -21
  46. package/dist/acp/virtual-catalog.js.map +1 -1
  47. package/dist/acp/virtual-contract.d.ts +13 -6
  48. package/dist/acp/virtual-contract.d.ts.map +1 -1
  49. package/dist/acp/virtual-contract.js +3 -3
  50. package/dist/acp/virtual-contract.js.map +1 -1
  51. package/dist/acp/virtual-event-carrier.d.ts +1 -0
  52. package/dist/acp/virtual-event-carrier.d.ts.map +1 -1
  53. package/dist/acp/virtual-event-carrier.js +1 -1
  54. package/dist/acp/virtual-event-carrier.js.map +1 -1
  55. package/dist/acp/virtual-harness.d.ts +8 -3
  56. package/dist/acp/virtual-harness.d.ts.map +1 -1
  57. package/dist/acp/virtual-harness.js +128 -60
  58. package/dist/acp/virtual-harness.js.map +1 -1
  59. package/dist/acp/virtual-live.d.ts +3 -2
  60. package/dist/acp/virtual-live.d.ts.map +1 -1
  61. package/dist/acp/virtual-live.js +5 -4
  62. package/dist/acp/virtual-live.js.map +1 -1
  63. package/dist/acp/workspace-tools.js +1 -1
  64. package/dist/acp/workspace-tools.js.map +1 -1
  65. package/dist/client/navigation.js +1 -1
  66. package/dist/client/navigation.js.map +1 -1
  67. package/dist/client/settings.js +1 -1
  68. package/dist/client/settings.js.map +1 -1
  69. package/dist/client/shell.d.ts.map +1 -1
  70. package/dist/client/shell.js +3 -2
  71. package/dist/client/shell.js.map +1 -1
  72. package/dist/client/types.d.ts +3 -2
  73. package/dist/client/types.d.ts.map +1 -1
  74. package/dist/client/workspaces.d.ts.map +1 -1
  75. package/dist/client/workspaces.js +130 -48
  76. package/dist/client/workspaces.js.map +1 -1
  77. package/dist/client-capabilities.d.ts.map +1 -1
  78. package/dist/client-capabilities.js +3 -1
  79. package/dist/client-capabilities.js.map +1 -1
  80. package/dist/client-control.d.ts +3 -3
  81. package/dist/client-control.d.ts.map +1 -1
  82. package/dist/client-control.js +23 -3
  83. package/dist/client-control.js.map +1 -1
  84. package/dist/client-locale.d.ts +8 -4
  85. package/dist/client-locale.d.ts.map +1 -1
  86. package/dist/client-locale.js +16 -8
  87. package/dist/client-locale.js.map +1 -1
  88. package/dist/client-runtime-contract.d.ts +2 -1
  89. package/dist/client-runtime-contract.d.ts.map +1 -1
  90. package/dist/client-runtime-contract.js.map +1 -1
  91. package/dist/client-runtime.d.ts +3 -3
  92. package/dist/client-runtime.d.ts.map +1 -1
  93. package/dist/client-runtime.js.map +1 -1
  94. package/dist/client.github.js +115 -57
  95. package/dist/client.js +115 -57
  96. package/dist/client.js.map +1 -1
  97. package/dist/codex/app-server.d.ts +10 -0
  98. package/dist/codex/app-server.d.ts.map +1 -1
  99. package/dist/codex/app-server.js +24 -10
  100. package/dist/codex/app-server.js.map +1 -1
  101. package/dist/codex/domain.d.ts +3 -0
  102. package/dist/codex/domain.d.ts.map +1 -1
  103. package/dist/codex/domain.js +93 -17
  104. package/dist/codex/domain.js.map +1 -1
  105. package/dist/codex/method-policy.d.ts +2 -2
  106. package/dist/codex/peer-bridge.d.ts.map +1 -1
  107. package/dist/codex/peer-bridge.js +7 -5
  108. package/dist/codex/peer-bridge.js.map +1 -1
  109. package/dist/codex/virtual-harness.d.ts.map +1 -1
  110. package/dist/codex/virtual-harness.js +16 -3
  111. package/dist/codex/virtual-harness.js.map +1 -1
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2669 -1872
  114. package/dist/index.js.map +4 -4
  115. package/dist/remote-typert-gateway.js +42 -1
  116. package/dist/remote-typert-gateway.js.map +1 -1
  117. package/dist/service.d.ts.map +1 -1
  118. package/dist/service.js +11 -1
  119. package/dist/service.js.map +1 -1
  120. package/dist/version.d.ts +1 -1
  121. package/dist/version.js +1 -1
  122. package/dist/workspace-bridge.d.ts.map +1 -1
  123. package/dist/workspace-bridge.js +26 -4
  124. package/dist/workspace-bridge.js.map +1 -1
  125. package/dsh-plugin.json +1 -1
  126. package/package.json +1 -1
@@ -47,6 +47,8 @@ export declare class AcpRemoteGateway {
47
47
  private readonly startingClients;
48
48
  /** Desired configuration is saved immediately; unfinished cleanup must remain retryable. */
49
49
  private readonly pendingReconfiguration;
50
+ private readonly archivingSessions;
51
+ private readonly opencodeOperations;
50
52
  constructor(config: ResolvedAcpConfig, logger: SafeLogger, createAcp?: AcpFactory, restartDelaysMs?: readonly number[], terminalEnabled?: () => boolean, terminalSpawner?: RemoteTerminalSpawner | undefined);
51
53
  /** Only implemented adapters are advertised; registry entries never select an adapter by executable name. */
52
54
  enabledBackends(): string[];
@@ -65,6 +67,8 @@ export declare class AcpRemoteGateway {
65
67
  };
66
68
  createPeer(context: PeerConnectionContext, publish: PublishAcpFrame): AcpPeerBridge | undefined;
67
69
  call(connectionId: string, input: unknown): Promise<unknown>;
70
+ private captureCallScope;
71
+ private callInternal;
68
72
  private runPromptInBackground;
69
73
  respond(connectionId: string, input: unknown): Promise<{
70
74
  resolved: true;
@@ -102,7 +106,6 @@ export declare class AcpRemoteGateway {
102
106
  private requireSessionOwner;
103
107
  private captureCatalogClient;
104
108
  private listCatalogSessions;
105
- private piSessionCwd;
106
109
  private catalogSessionCwd;
107
110
  private requireExistingDirectory;
108
111
  private listDirectory;
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/acp/gateway.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/acp/gateway.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { IsolatedAcpClient } from './adapters/isolated-process.js';
1
2
  import { WorkspaceBridge, WorkspaceBridgeState } from '../workspace-bridge.js';
2
3
  import { TerminalPolicy } from '../terminal-policy.js';
3
4
  import { AcpWorkspaceTools } from './workspace-tools.js';
@@ -7,7 +8,7 @@ import { homedir } from 'node:os';
7
8
  import { basename, isAbsolute, join, relative, resolve } from 'node:path';
8
9
  import { RpcError } from '../safe-error.js';
9
10
  import { PLUGIN_VERSION } from '../version.js';
10
- import { CursorAcpClient, CursorAcpError, } from './adapters/cursor-process.js';
11
+ import { CursorAcpError, } from './adapters/cursor-process.js';
11
12
  import { ClaudeAcpClient } from './adapters/claude-process.js';
12
13
  import { PiAcpClient } from './adapters/pi-process.js';
13
14
  import { AntigravityAcpClient } from './adapters/antigravity-process.js';
@@ -63,7 +64,7 @@ export class AcpRemoteGateway {
63
64
  /** Catch-up buffer for turns that finish while the Client is reconnecting. */
64
65
  recentFrames = new Map();
65
66
  /** Per-prompt live updates; attached to prompt_completed when streaming was lossy. */
66
- promptsInProgress = new Set();
67
+ promptsInProgress = new Map();
67
68
  cancelledSessions = new Set();
68
69
  /** Identity fences asynchronous Pi preparation from a later retry of the same session. */
69
70
  preparingPrompts = new Map();
@@ -72,6 +73,8 @@ export class AcpRemoteGateway {
72
73
  startingClients = new Map();
73
74
  /** Desired configuration is saved immediately; unfinished cleanup must remain retryable. */
74
75
  pendingReconfiguration = new Set();
76
+ archivingSessions = new Set();
77
+ opencodeOperations = new Map();
75
78
  constructor(config, logger, createAcp = (binary, targetLogger, backend) => {
76
79
  if (backend.id === 'antigravity') {
77
80
  return new AntigravityAcpClient(binary, targetLogger, undefined, { args: backend.args, cwd: backend.cwd });
@@ -83,7 +86,7 @@ export class AcpRemoteGateway {
83
86
  if (backend.id === 'opencode') {
84
87
  return new OpencodeAcpClient(binary, targetLogger, undefined, { args: backend.args, cwd: backend.cwd });
85
88
  }
86
- return new CursorAcpClient(binary, targetLogger, undefined, { args: backend.args, cwd: backend.cwd });
89
+ return new IsolatedAcpClient(binary, targetLogger, { args: backend.args, cwd: backend.cwd, backend: 'cursor' });
87
90
  }, restartDelaysMs = DEFAULT_RESTART_DELAYS_MS, terminalEnabled = () => false, terminalSpawner) {
88
91
  this.config = config;
89
92
  this.logger = logger;
@@ -169,12 +172,51 @@ export class AcpRemoteGateway {
169
172
  async call(connectionId, input) {
170
173
  const envelope = parseCallEnvelope(input);
171
174
  const call = parseAcpCall(envelope.method, envelope.params);
175
+ const sessionId = sessionIdFromParams(call.method, call.params);
176
+ const backend = call.params.backend ?? (sessionId ? this.sessionBackends.get(sessionId) ?? this.defaultBackend() : undefined);
177
+ const assertCurrent = this.captureCallScope(connectionId, typeof backend === 'string' ? backend : this.defaultBackend());
178
+ const reserve = backend === 'opencode' && sessionId !== undefined && ['session/load', 'session/prompt', 'dsh/selectModel'].includes(call.method);
179
+ if (reserve) {
180
+ if (this.archivingSessions.has(sessionId))
181
+ throw new RpcError('ACP_SESSION_BUSY', 'The OpenCode session is being archived.');
182
+ this.opencodeOperations.set(sessionId, (this.opencodeOperations.get(sessionId) ?? 0) + 1);
183
+ }
184
+ try {
185
+ const result = await this.callInternal(connectionId, call, assertCurrent);
186
+ assertCurrent();
187
+ return result;
188
+ }
189
+ finally {
190
+ if (reserve) {
191
+ const remaining = (this.opencodeOperations.get(sessionId) ?? 1) - 1;
192
+ if (remaining)
193
+ this.opencodeOperations.set(sessionId, remaining);
194
+ else
195
+ this.opencodeOperations.delete(sessionId);
196
+ }
197
+ }
198
+ }
199
+ captureCallScope(connectionId, backend) {
200
+ const peer = this.peers.get(connectionId);
201
+ const instance = this.backendInstances.get(backend);
202
+ return () => {
203
+ if (this.closed || this.backendInstances.get(backend) !== instance
204
+ || (instance !== undefined && this.requireAcp(backend) !== instance.client)) {
205
+ throw new RpcError('CURSOR_UNAVAILABLE', 'The ACP backend changed while handling the request.');
206
+ }
207
+ if (peer !== undefined && this.peers.get(connectionId) !== peer) {
208
+ throw new RpcError('ACP_CONNECTION_CLOSED', 'The ACP connection is closed.');
209
+ }
210
+ };
211
+ }
212
+ async callInternal(connectionId, call, assertCurrent) {
213
+ assertCurrent();
172
214
  this.logger.info('ACP call received', {
173
215
  method: call.method,
174
216
  sessionId: typeof call.params?.sessionId === 'string' ? shortSessionId(call.params.sessionId) : undefined,
175
217
  });
176
218
  if (typeof call.params.backend === 'string') {
177
- const prefix = typeof call.params.sessionId === 'string' ? /^(acp|cursor|pi|claude):/.exec(call.params.sessionId)?.[1] : undefined;
219
+ const prefix = typeof call.params.sessionId === 'string' ? /^(acp|cursor|pi|claude|opencode):/.exec(call.params.sessionId)?.[1] : undefined;
178
220
  if (prefix && (prefix === 'acp' ? 'antigravity' : prefix) !== call.params.backend) {
179
221
  throw new RpcError('INVALID_MESSAGE', 'The ACP session backend does not match.');
180
222
  }
@@ -192,14 +234,83 @@ export class AcpRemoteGateway {
192
234
  this.requireAcp(requestedBackend);
193
235
  return this.initializeResult(call.params, requestedBackend);
194
236
  }
237
+ if (call.method === 'dsh/sessionArchive') {
238
+ const sessionId = String(call.params.sessionId);
239
+ if (this.archivingSessions.has(sessionId) || this.opencodeOperations.has(sessionId))
240
+ throw new RpcError('ACP_SESSION_BUSY', 'The OpenCode session is busy.');
241
+ this.archivingSessions.add(sessionId);
242
+ try {
243
+ const backend = 'opencode';
244
+ const client = this.requireAcp(backend);
245
+ const instance = this.backendInstances.get(backend);
246
+ const cwd = await this.catalogSessionCwd(backend, sessionId);
247
+ assertCurrent();
248
+ if (this.closed || this.requireAcp(backend) !== client || this.backendInstances.get(backend) !== instance)
249
+ throw new RpcError('OPENCODE_ARCHIVE_UNAVAILABLE', 'The OpenCode backend changed. Refresh the session list.');
250
+ if (this.promptsInProgress.has(sessionId) || [...this.approvals.values()].some(item => item.sessionId === sessionId))
251
+ throw new RpcError('ACP_SESSION_BUSY', 'Stop the OpenCode reply and resolve its approvals before archiving.');
252
+ if (!client.archiveSession)
253
+ throw new RpcError('OPENCODE_ARCHIVE_UNAVAILABLE', 'The Host OpenCode adapter does not support native archiving.');
254
+ await client.archiveSession(sessionId, cwd);
255
+ if (this.closed || this.requireAcp(backend) !== client || this.backendInstances.get(backend) !== instance)
256
+ throw new RpcError('OPENCODE_ARCHIVE_UNAVAILABLE', 'The OpenCode backend changed. Refresh the session list to confirm the archive state.');
257
+ const result = { backend, sessionId, archived: true };
258
+ this.inboundChain = this.inboundChain.then(async () => {
259
+ if (!this.closed && this.backendInstances.get(backend) === instance) {
260
+ await Promise.allSettled([...this.peers.values()].map(peer => peer.publishArchived(result)));
261
+ }
262
+ });
263
+ await this.inboundChain;
264
+ if (this.closed || this.backendInstances.get(backend) !== instance)
265
+ throw new RpcError('OPENCODE_ARCHIVE_UNAVAILABLE', 'The OpenCode backend changed. Refresh the session list to confirm the archive state.');
266
+ return result;
267
+ }
268
+ finally {
269
+ this.archivingSessions.delete(sessionId);
270
+ }
271
+ }
272
+ if (typeof call.params.sessionId === 'string' && this.archivingSessions.has(call.params.sessionId)
273
+ && ['session/load', 'session/prompt', 'dsh/selectModel'].includes(call.method))
274
+ throw new RpcError('ACP_SESSION_BUSY', 'The OpenCode session is being archived.');
275
+ const controlId = sessionIdFromParams(call.method, call.params);
276
+ if (controlId && (call.params.backend ?? this.sessionBackends.get(controlId) ?? this.defaultBackend()) === 'opencode'
277
+ && ['session/load', 'session/prompt', 'dsh/selectModel'].includes(call.method)) {
278
+ const client = this.requireAcp('opencode');
279
+ if (client.isSessionArchived) {
280
+ const cwd = await this.catalogSessionCwd('opencode', controlId);
281
+ const archived = await client.isSessionArchived(controlId, cwd);
282
+ if (this.closed || this.requireAcp('opencode') !== client)
283
+ throw new RpcError('OPENCODE_ARCHIVE_UNAVAILABLE', 'The OpenCode backend changed.');
284
+ if (archived)
285
+ throw new RpcError('ACP_SESSION_ARCHIVED', 'This OpenCode session is archived.');
286
+ }
287
+ }
288
+ assertCurrent();
195
289
  if (call.method === 'dsh/directoryList') {
196
290
  return this.listDirectory(String(call.params.path));
197
291
  }
198
292
  if (call.method === 'dsh/workspaceList') {
199
293
  const requestedBackend = typeof call.params.backend === 'string' ? call.params.backend : this.defaultBackend();
200
- if (requestedBackend === 'pi' || requestedBackend === 'claude') {
294
+ if (requestedBackend === 'pi' || requestedBackend === 'claude' || requestedBackend === 'opencode') {
201
295
  const sessions = await this.listCatalogSessions(requestedBackend);
202
- return [...new Set(sessions.map(item => item.cwd))].map(path => ({ path, title: basename(path) || 'workspace' }));
296
+ const discovered = [...new Set(sessions.map(item => item.cwd))].map(path => ({ path, title: basename(path) || 'workspace' }));
297
+ if (discovered.length > 0 || requestedBackend !== 'opencode')
298
+ return discovered;
299
+ const defaultCandidates = new Set();
300
+ const cwd = process.cwd();
301
+ if (cwd)
302
+ defaultCandidates.add(cwd);
303
+ for (const candidate of ['/var/lib/dsh/workspace/ds-harness-remote', '/srv/dsh-workspace', '/var/lib/dsh/local']) {
304
+ try {
305
+ const s = await stat(candidate);
306
+ if (s.isDirectory())
307
+ defaultCandidates.add(candidate);
308
+ }
309
+ catch {
310
+ // ignore
311
+ }
312
+ }
313
+ return [...defaultCandidates].map(path => ({ path, title: basename(path) || 'workspace' }));
203
314
  }
204
315
  const candidates = new Set();
205
316
  const cwd = process.cwd();
@@ -255,20 +366,24 @@ export class AcpRemoteGateway {
255
366
  // Prepare one idle process in that directory without awaiting startup.
256
367
  if (path.trim() !== '' && call.params.prewarm !== false) {
257
368
  const cwd = await this.requireExistingDirectory(path);
369
+ assertCurrent();
258
370
  this.requireAcp('antigravity').prewarmSession?.(cwd);
259
371
  }
260
372
  const items = await discoverAntigravitySessions(path, limit);
261
373
  if (path) {
262
374
  const cwd = await this.requireExistingDirectory(path);
375
+ assertCurrent();
263
376
  for (const item of items)
264
377
  this.sessionCwds.set(item.conversationId, { backend, cwd });
265
378
  }
266
379
  this.logger.info('ACP session list fetched', { count: items.length });
267
380
  return { items };
268
381
  }
269
- if (backend === 'pi' || backend === 'claude') {
382
+ if (backend === 'pi' || backend === 'claude' || backend === 'opencode') {
270
383
  const cwd = path ? await this.requireExistingDirectory(path) : undefined;
271
384
  const sessions = await this.listCatalogSessions(backend, cwd);
385
+ sessions.sort((a, b) => (Date.parse(b.updatedAt ?? '') || 0) - (Date.parse(a.updatedAt ?? '') || 0)
386
+ || a.sessionId.localeCompare(b.sessionId));
272
387
  return { items: sessions.slice(0, limit).map(item => ({ conversationId: item.sessionId, title: item.title ?? 'Untitled Session', createdAt: Date.parse(item.updatedAt ?? '') || 0, updatedAt: Date.parse(item.updatedAt ?? '') || 0, cwd: item.cwd })) };
273
388
  }
274
389
  return { items: [] };
@@ -293,9 +408,10 @@ export class AcpRemoteGateway {
293
408
  throw new RpcError('INVALID_MESSAGE', 'The ACP session backend does not match.');
294
409
  }
295
410
  this.requireAcp(backend);
296
- if (backend === 'pi' || backend === 'claude') {
411
+ if (backend === 'pi' || backend === 'claude' || backend === 'opencode') {
297
412
  const cwd = await this.catalogSessionCwd(backend, conversationId);
298
- this.requireSessionOwner(connectionId, conversationId);
413
+ // History uses an isolated reader and must neither require nor claim
414
+ // the live session's control ownership.
299
415
  const client = this.requireAcp(backend);
300
416
  if (!client.history)
301
417
  throw new RpcError('METHOD_NOT_ALLOWED', 'The ACP backend does not support history.');
@@ -312,6 +428,7 @@ export class AcpRemoteGateway {
312
428
  }
313
429
  if (call.method === 'session/new') {
314
430
  const cwd = await this.requireExistingDirectory(String(call.params.cwd));
431
+ assertCurrent();
315
432
  const requestedBackend = typeof call.params.backend === 'string'
316
433
  ? call.params.backend
317
434
  : this.defaultBackend();
@@ -321,6 +438,7 @@ export class AcpRemoteGateway {
321
438
  mcpServers: [],
322
439
  ...(typeof call.params.mode === 'string' ? { mode: call.params.mode } : {}),
323
440
  });
441
+ assertCurrent();
324
442
  const sessionId = readSessionId(result);
325
443
  if (sessionId !== undefined) {
326
444
  const bound = this.sessionBackends.get(sessionId);
@@ -360,11 +478,16 @@ export class AcpRemoteGateway {
360
478
  if (boundBackend !== undefined && targetBackend !== boundBackend) {
361
479
  throw new RpcError('INVALID_MESSAGE', 'The ACP session backend does not match.');
362
480
  }
363
- if (targetBackend === 'pi' || targetBackend === 'claude')
481
+ if (targetBackend === 'pi' || targetBackend === 'claude' || targetBackend === 'opencode')
364
482
  await this.catalogSessionCwd(targetBackend, sessionId);
483
+ assertCurrent();
365
484
  this.requireSessionOwner(connectionId, sessionId);
366
485
  const acp = this.requireAcp(targetBackend);
486
+ // Track pending loads too, so reconfiguration revokes their ownership
487
+ // before an upstream result can arrive.
488
+ this.sessionBackends.set(sessionId, targetBackend);
367
489
  const result = await acp.call(call.method, { ...call.params, ...(sessionId && this.sessionCwds.get(sessionId) ? { cwd: this.sessionCwds.get(sessionId).cwd, mcpServers: [] } : {}) });
490
+ assertCurrent();
368
491
  const loadedId = readSessionId(result) ?? sessionId;
369
492
  if (loadedId !== undefined) {
370
493
  if (loadedId !== sessionId || (this.sessionBackends.has(loadedId) && targetBackend !== undefined && this.sessionBackends.get(loadedId) !== targetBackend)) {
@@ -379,12 +502,17 @@ export class AcpRemoteGateway {
379
502
  if (isSessionMutation(call.method) && sessionId !== undefined) {
380
503
  this.requireSessionOwner(connectionId, sessionId);
381
504
  }
382
- if ((call.method === 'dsh/sessionModels' || call.method === 'dsh/selectModel') && call.params.backend === 'pi') {
505
+ if ((call.method === 'dsh/sessionModels' || call.method === 'dsh/selectModel') && (call.params.backend === 'pi' || call.params.backend === 'claude' || call.params.backend === 'opencode')) {
383
506
  this.requireSessionOwner(connectionId, sessionId);
384
- const cwd = await this.piSessionCwd(sessionId);
385
- await this.requireAcp('pi').call('session/load', { sessionId, cwd, mcpServers: [] });
507
+ const backend = call.params.backend;
508
+ const cwd = await this.catalogSessionCwd(backend, sessionId);
509
+ assertCurrent();
510
+ await this.requireAcp(backend).call('session/load', { sessionId, cwd, mcpServers: [] });
511
+ assertCurrent();
386
512
  }
387
513
  if (call.method === 'dsh/selectModel') {
514
+ if (this.promptsInProgress.has(sessionId))
515
+ throw new RpcError('PROMPT_IN_PROGRESS', 'Stop the ACP reply before changing its model.');
388
516
  const session = this.sessionCwds.get(sessionId);
389
517
  if (!session || session.backend !== call.params.backend) {
390
518
  throw new RpcError('ACP_WORKSPACE_UNAVAILABLE', 'The ACP session has no trusted workspace.');
@@ -392,6 +520,7 @@ export class AcpRemoteGateway {
392
520
  // Resume straight into the target model. Loading the old model first
393
521
  // starts a second CLI process that would immediately be discarded.
394
522
  const result = await this.requireAcp(session.backend).call(call.method, { ...call.params, cwd: session.cwd });
523
+ assertCurrent();
395
524
  this.sessionBackends.set(sessionId, session.backend);
396
525
  return sanitizeSessionResult(result);
397
526
  }
@@ -402,29 +531,35 @@ export class AcpRemoteGateway {
402
531
  if (call.method === 'session/prompt' && sessionId !== undefined) {
403
532
  if (this.promptsInProgress.has(sessionId))
404
533
  throw new RpcError('PROMPT_IN_PROGRESS', 'An ACP reply is already in progress.');
405
- this.promptsInProgress.add(sessionId);
534
+ const backend = this.sessionBackends.get(sessionId) ?? this.defaultBackend();
535
+ const task = { backend, client: this.requireAcp(backend) };
536
+ this.promptsInProgress.set(sessionId, task);
406
537
  this.cancelledSessions.delete(sessionId);
407
- if (this.sessionBackends.get(sessionId) === 'pi') {
538
+ const preparationBackend = this.sessionBackends.get(sessionId);
539
+ if (preparationBackend === 'pi' || preparationBackend === 'claude') {
408
540
  const preparation = { cancelled: false };
409
541
  this.preparingPrompts.set(sessionId, preparation);
410
542
  try {
411
- const cwd = await this.piSessionCwd(sessionId);
543
+ const cwd = await this.catalogSessionCwd(preparationBackend, sessionId);
412
544
  if (preparation.cancelled)
413
545
  return { accepted: false, stopReason: 'cancelled' };
414
- await this.requireAcp('pi').call('session/load', { sessionId, cwd, mcpServers: [] });
546
+ assertCurrent();
547
+ await task.client.call('session/load', { sessionId, cwd, mcpServers: [] });
415
548
  if (preparation.cancelled)
416
549
  return { accepted: false, stopReason: 'cancelled' };
550
+ assertCurrent();
417
551
  }
418
552
  catch (error) {
419
553
  if (preparation.cancelled)
420
554
  return { accepted: false, stopReason: 'cancelled' };
421
- this.promptsInProgress.delete(sessionId);
555
+ if (this.promptsInProgress.get(sessionId) === task)
556
+ this.promptsInProgress.delete(sessionId);
422
557
  throw error;
423
558
  }
424
559
  finally {
425
560
  if (this.preparingPrompts.get(sessionId) === preparation) {
426
561
  this.preparingPrompts.delete(sessionId);
427
- if (preparation.cancelled)
562
+ if (preparation.cancelled && this.promptsInProgress.get(sessionId) === task)
428
563
  this.promptsInProgress.delete(sessionId);
429
564
  }
430
565
  }
@@ -436,7 +571,7 @@ export class AcpRemoteGateway {
436
571
  });
437
572
  }
438
573
  this.turnCatchUp.set(sessionId, []);
439
- void this.runPromptInBackground(sessionId, call.params);
574
+ void this.runPromptInBackground(sessionId, call.params, task);
440
575
  return { accepted: true, stopReason: 'in_progress' };
441
576
  }
442
577
  if (call.method === 'session/cancel' && sessionId !== undefined) {
@@ -465,15 +600,17 @@ export class AcpRemoteGateway {
465
600
  const sessionBackend = sessionId !== undefined ? this.sessionBackends.get(sessionId) : undefined;
466
601
  return sanitizeSessionResult(await this.requireAcp(typeof call.params.backend === 'string' ? call.params.backend : sessionBackend).call(call.method, call.params));
467
602
  }
468
- async runPromptInBackground(sessionId, params) {
603
+ async runPromptInBackground(sessionId, params, task) {
604
+ const current = () => !this.closed && this.promptsInProgress.get(sessionId) === task
605
+ && !this.pendingReconfiguration.has(task.backend) && this.backendInstances.get(task.backend)?.client === task.client;
469
606
  try {
470
- const sessionBackend = this.sessionBackends.get(sessionId);
471
- const acp = this.requireAcp(sessionBackend);
472
- const result = await acp.call('session/prompt', params);
607
+ const result = await task.client.call('session/prompt', params);
473
608
  // Cursor emits final session/update lines before the JSON-RPC result.
474
609
  // Those notifications are queued on inboundChain; drain it before we
475
610
  // snapshot catch-up or the Client only sees an empty prompt_completed.
476
611
  await this.inboundChain;
612
+ if (!current())
613
+ return;
477
614
  const stopReason = isRecord(result) && typeof result.stopReason === 'string'
478
615
  ? result.stopReason
479
616
  : 'end_turn';
@@ -496,8 +633,12 @@ export class AcpRemoteGateway {
496
633
  });
497
634
  }
498
635
  catch (error) {
636
+ if (!current())
637
+ return;
499
638
  this.logger?.warn('ACP session/prompt failed', { code: errorCode(error) });
500
639
  await this.inboundChain.catch(() => undefined);
640
+ if (!current())
641
+ return;
501
642
  const catchUp = takeTurnCatchUp(this.turnCatchUp, sessionId);
502
643
  await this.publishToSession(sessionId, {
503
644
  method: 'session/update',
@@ -512,7 +653,8 @@ export class AcpRemoteGateway {
512
653
  }).catch(() => undefined);
513
654
  }
514
655
  finally {
515
- this.promptsInProgress.delete(sessionId);
656
+ if (this.promptsInProgress.get(sessionId) === task)
657
+ this.promptsInProgress.delete(sessionId);
516
658
  }
517
659
  }
518
660
  async respond(connectionId, input) {
@@ -585,7 +727,7 @@ export class AcpRemoteGateway {
585
727
  return;
586
728
  }
587
729
  if (owner !== connectionId) {
588
- throw new RpcError('CURSOR_SESSION_OWNED', 'Another Remote connection owns this Cursor session.');
730
+ throw new RpcError('CURSOR_SESSION_OWNED', 'Another Remote connection owns this session.');
589
731
  }
590
732
  }
591
733
  /** Replay frames buffered while no healthy peer could receive them. */
@@ -636,6 +778,11 @@ export class AcpRemoteGateway {
636
778
  .then(() => undefined, () => new RpcError('TERMINAL_CLEANUP_FAILED', 'Some ACP terminal processes could not be stopped. Save the backend settings again to retry.'));
637
779
  for (const id of sessions) {
638
780
  this.sessionOwners.delete(id);
781
+ this.promptsInProgress.delete(id);
782
+ const preparation = this.preparingPrompts.get(id);
783
+ if (preparation)
784
+ preparation.cancelled = true;
785
+ this.preparingPrompts.delete(id);
639
786
  this.sessionBackends.delete(id);
640
787
  this.recentFrames.delete(id);
641
788
  this.turnCatchUp.delete(id);
@@ -683,6 +830,10 @@ export class AcpRemoteGateway {
683
830
  this.sessionBackends.clear();
684
831
  this.recentFrames.clear();
685
832
  this.turnCatchUp.clear();
833
+ this.promptsInProgress.clear();
834
+ for (const preparation of this.preparingPrompts.values())
835
+ preparation.cancelled = true;
836
+ this.preparingPrompts.clear();
686
837
  this.approvals.clear();
687
838
  const processes = await Promise.allSettled([...this.startingClients.keys()].map(client => client.close()).concat(this.disposeAllInstances()));
688
839
  const terminalError = await terminals;
@@ -998,7 +1149,7 @@ export class AcpRemoteGateway {
998
1149
  return;
999
1150
  }
1000
1151
  if (owner !== connectionId && isSessionMutation(method)) {
1001
- throw new RpcError('CURSOR_SESSION_OWNED', 'Another Remote connection owns this Cursor session.');
1152
+ throw new RpcError('CURSOR_SESSION_OWNED', 'Another Remote connection owns this session.');
1002
1153
  }
1003
1154
  }
1004
1155
  requireSessionOwner(connectionId, sessionId) {
@@ -1041,6 +1192,8 @@ export class AcpRemoteGateway {
1041
1192
  continue;
1042
1193
  this.sessionCwds.set(item.sessionId, { backend, cwd: canonical });
1043
1194
  this.sessionBackends.set(item.sessionId, backend);
1195
+ if (backend === 'opencode' && item.archived === true)
1196
+ continue;
1044
1197
  items.push({ sessionId: item.sessionId, cwd: canonical,
1045
1198
  ...(typeof item.title === 'string' ? { title: item.title } : {}),
1046
1199
  ...(typeof item.updatedAt === 'string' ? { updatedAt: item.updatedAt } : {}) });
@@ -1055,7 +1208,6 @@ export class AcpRemoteGateway {
1055
1208
  }
1056
1209
  return items;
1057
1210
  }
1058
- piSessionCwd(sessionId) { return this.catalogSessionCwd('pi', sessionId); }
1059
1211
  async catalogSessionCwd(backend, sessionId) {
1060
1212
  const { assertCurrent } = this.captureCatalogClient(backend);
1061
1213
  if (!this.sessionCwds.has(sessionId))
@@ -1256,6 +1408,8 @@ export function cursorBinaryCandidates(configured) {
1256
1408
  }
1257
1409
  if (configured === 'opencode') {
1258
1410
  return [
1411
+ // Official installer location (curl -fsSL https://opencode.ai/install | bash).
1412
+ join(userHome, '.opencode', 'bin', 'opencode'),
1259
1413
  join(userHome, '.local', 'bin', 'opencode'),
1260
1414
  'opencode',
1261
1415
  ];