@getpaseo/server 0.1.3 → 0.1.6

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 (148) hide show
  1. package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts +8 -0
  2. package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts.map +1 -0
  3. package/dist/server/client/daemon-client-relay-e2ee-transport.js +161 -0
  4. package/dist/server/client/daemon-client-relay-e2ee-transport.js.map +1 -0
  5. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts +43 -0
  6. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts.map +1 -0
  7. package/dist/server/client/daemon-client-terminal-stream-manager.js +134 -0
  8. package/dist/server/client/daemon-client-terminal-stream-manager.js.map +1 -0
  9. package/dist/server/client/daemon-client-transport-types.d.ts +34 -0
  10. package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -0
  11. package/dist/server/client/daemon-client-transport-types.js +2 -0
  12. package/dist/server/client/daemon-client-transport-types.js.map +1 -0
  13. package/dist/server/client/daemon-client-transport-utils.d.ts +9 -0
  14. package/dist/server/client/daemon-client-transport-utils.d.ts.map +1 -0
  15. package/dist/server/client/daemon-client-transport-utils.js +121 -0
  16. package/dist/server/client/daemon-client-transport-utils.js.map +1 -0
  17. package/dist/server/client/daemon-client-transport.d.ts +5 -0
  18. package/dist/server/client/daemon-client-transport.d.ts.map +1 -0
  19. package/dist/server/client/daemon-client-transport.js +4 -0
  20. package/dist/server/client/daemon-client-transport.js.map +1 -0
  21. package/dist/server/client/daemon-client-websocket-transport.d.ts +7 -0
  22. package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -0
  23. package/dist/server/client/daemon-client-websocket-transport.js +64 -0
  24. package/dist/server/client/daemon-client-websocket-transport.js.map +1 -0
  25. package/dist/server/client/daemon-client.d.ts +63 -46
  26. package/dist/server/client/daemon-client.d.ts.map +1 -1
  27. package/dist/server/client/daemon-client.js +497 -796
  28. package/dist/server/client/daemon-client.js.map +1 -1
  29. package/dist/server/server/agent/agent-management-mcp.d.ts +2 -0
  30. package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -1
  31. package/dist/server/server/agent/agent-management-mcp.js +29 -4
  32. package/dist/server/server/agent/agent-management-mcp.js.map +1 -1
  33. package/dist/server/server/agent/agent-manager.d.ts +48 -0
  34. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  35. package/dist/server/server/agent/agent-manager.js +224 -14
  36. package/dist/server/server/agent/agent-manager.js.map +1 -1
  37. package/dist/server/server/agent/agent-sdk-types.d.ts +14 -0
  38. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  39. package/dist/server/server/agent/agent-storage.d.ts +4 -4
  40. package/dist/server/server/agent/mcp-server.d.ts +2 -0
  41. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  42. package/dist/server/server/agent/mcp-server.js +30 -5
  43. package/dist/server/server/agent/mcp-server.js.map +1 -1
  44. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
  45. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +120 -6
  46. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  47. package/dist/server/server/agent/providers/claude-agent.d.ts +7 -0
  48. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  49. package/dist/server/server/agent/providers/claude-agent.js +83 -9
  50. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  51. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  52. package/dist/server/server/agent/providers/codex-app-server-agent.js +25 -0
  53. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  54. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +42 -0
  55. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  56. package/dist/server/server/agent/stt-manager.d.ts +3 -2
  57. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  58. package/dist/server/server/agent/stt-manager.js +5 -3
  59. package/dist/server/server/agent/stt-manager.js.map +1 -1
  60. package/dist/server/server/agent/timeline-append.d.ts +10 -0
  61. package/dist/server/server/agent/timeline-append.d.ts.map +1 -0
  62. package/dist/server/server/agent/timeline-append.js +27 -0
  63. package/dist/server/server/agent/timeline-append.js.map +1 -0
  64. package/dist/server/server/agent/timeline-projection.d.ts +19 -0
  65. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -0
  66. package/dist/server/server/agent/timeline-projection.js +142 -0
  67. package/dist/server/server/agent/timeline-projection.js.map +1 -0
  68. package/dist/server/server/agent/tts-manager.d.ts +3 -2
  69. package/dist/server/server/agent/tts-manager.d.ts.map +1 -1
  70. package/dist/server/server/agent/tts-manager.js +5 -3
  71. package/dist/server/server/agent/tts-manager.js.map +1 -1
  72. package/dist/server/server/agent-attention-policy.d.ts +20 -0
  73. package/dist/server/server/agent-attention-policy.d.ts.map +1 -0
  74. package/dist/server/server/agent-attention-policy.js +40 -0
  75. package/dist/server/server/agent-attention-policy.js.map +1 -0
  76. package/dist/server/server/bootstrap.d.ts.map +1 -1
  77. package/dist/server/server/bootstrap.js +16 -18
  78. package/dist/server/server/bootstrap.js.map +1 -1
  79. package/dist/server/server/dictation/dictation-stream-manager.d.ts +10 -2
  80. package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
  81. package/dist/server/server/dictation/dictation-stream-manager.js +81 -14
  82. package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
  83. package/dist/server/server/exports.d.ts +1 -1
  84. package/dist/server/server/exports.d.ts.map +1 -1
  85. package/dist/server/server/persisted-config.d.ts +12 -12
  86. package/dist/server/server/relay-transport.d.ts +3 -2
  87. package/dist/server/server/relay-transport.d.ts.map +1 -1
  88. package/dist/server/server/relay-transport.js +21 -5
  89. package/dist/server/server/relay-transport.js.map +1 -1
  90. package/dist/server/server/session.d.ts +51 -14
  91. package/dist/server/server/session.d.ts.map +1 -1
  92. package/dist/server/server/session.js +872 -250
  93. package/dist/server/server/session.js.map +1 -1
  94. package/dist/server/server/speech/provider-resolver.d.ts +3 -0
  95. package/dist/server/server/speech/provider-resolver.d.ts.map +1 -0
  96. package/dist/server/server/speech/provider-resolver.js +7 -0
  97. package/dist/server/server/speech/provider-resolver.js.map +1 -0
  98. package/dist/server/server/speech/providers/local/runtime.d.ts +1 -0
  99. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
  100. package/dist/server/server/speech/providers/local/runtime.js +4 -3
  101. package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
  102. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  103. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +3 -66
  104. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  105. package/dist/server/server/speech/speech-runtime.d.ts +26 -3
  106. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  107. package/dist/server/server/speech/speech-runtime.js +466 -112
  108. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  109. package/dist/server/server/websocket-server.d.ts +23 -7
  110. package/dist/server/server/websocket-server.d.ts.map +1 -1
  111. package/dist/server/server/websocket-server.js +288 -64
  112. package/dist/server/server/websocket-server.js.map +1 -1
  113. package/dist/server/server/worktree-bootstrap.d.ts +29 -0
  114. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -0
  115. package/dist/server/server/worktree-bootstrap.js +407 -0
  116. package/dist/server/server/worktree-bootstrap.js.map +1 -0
  117. package/dist/server/shared/binary-mux.d.ts +31 -0
  118. package/dist/server/shared/binary-mux.d.ts.map +1 -0
  119. package/dist/server/shared/binary-mux.js +114 -0
  120. package/dist/server/shared/binary-mux.js.map +1 -0
  121. package/dist/server/shared/messages.d.ts +6437 -5839
  122. package/dist/server/shared/messages.d.ts.map +1 -1
  123. package/dist/server/shared/messages.js +238 -50
  124. package/dist/server/shared/messages.js.map +1 -1
  125. package/dist/server/shared/terminal-key-input.d.ts +9 -0
  126. package/dist/server/shared/terminal-key-input.d.ts.map +1 -0
  127. package/dist/server/shared/terminal-key-input.js +132 -0
  128. package/dist/server/shared/terminal-key-input.js.map +1 -0
  129. package/dist/server/shared/tool-call-display.d.ts.map +1 -1
  130. package/dist/server/shared/tool-call-display.js +4 -0
  131. package/dist/server/shared/tool-call-display.js.map +1 -1
  132. package/dist/server/terminal/terminal-manager.d.ts +11 -0
  133. package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
  134. package/dist/server/terminal/terminal-manager.js +75 -24
  135. package/dist/server/terminal/terminal-manager.js.map +1 -1
  136. package/dist/server/terminal/terminal.d.ts +18 -0
  137. package/dist/server/terminal/terminal.d.ts.map +1 -1
  138. package/dist/server/terminal/terminal.js +142 -5
  139. package/dist/server/terminal/terminal.js.map +1 -1
  140. package/dist/server/utils/checkout-git.d.ts +4 -0
  141. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  142. package/dist/server/utils/checkout-git.js +92 -0
  143. package/dist/server/utils/checkout-git.js.map +1 -1
  144. package/dist/server/utils/worktree.d.ts +32 -0
  145. package/dist/server/utils/worktree.d.ts.map +1 -1
  146. package/dist/server/utils/worktree.js +160 -10
  147. package/dist/server/utils/worktree.js.map +1 -1
  148. package/package.json +2 -2
@@ -1,7 +1,10 @@
1
1
  import { AgentCreateFailedStatusPayloadSchema, AgentCreatedStatusPayloadSchema, AgentRefreshedStatusPayloadSchema, AgentResumedStatusPayloadSchema, RestartRequestedStatusPayloadSchema, SessionInboundMessageSchema, WSOutboundMessageSchema, } from "../shared/messages.js";
2
2
  import { getAgentProviderDefinition } from "../server/agent/provider-manifest.js";
3
- import { createClientChannel, } from "@getpaseo/relay/e2ee";
4
3
  import { isRelayClientWebSocketUrl } from "../shared/daemon-endpoints.js";
4
+ import { asUint8Array, decodeBinaryMuxFrame, encodeBinaryMuxFrame, BinaryMuxChannel, TerminalBinaryFlags, TerminalBinaryMessageType, } from "../shared/binary-mux.js";
5
+ import { encodeTerminalKeyInput, } from "../shared/terminal-key-input.js";
6
+ import { TerminalStreamManager, } from "./daemon-client-terminal-stream-manager.js";
7
+ import { createRelayE2eeTransportFactory, createWebSocketTransportFactory, decodeMessageData, defaultWebSocketFactory, describeTransportClose, describeTransportError, encodeUtf8String, safeRandomId, } from "./daemon-client-transport.js";
5
8
  const consoleLogger = {
6
9
  debug: (obj, msg) => console.debug(msg, obj),
7
10
  info: (obj, msg) => console.info(msg, obj),
@@ -21,6 +24,12 @@ const DEFAULT_RECONNECT_BASE_DELAY_MS = 1500;
21
24
  const DEFAULT_RECONNECT_MAX_DELAY_MS = 30000;
22
25
  /** Default timeout for waiting for connection before sending queued messages */
23
26
  const DEFAULT_SEND_QUEUE_TIMEOUT_MS = 10000;
27
+ const DEFAULT_DICTATION_FINISH_ACCEPT_TIMEOUT_MS = 15000;
28
+ const DEFAULT_DICTATION_FINISH_FALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
29
+ const DEFAULT_DICTATION_FINISH_TIMEOUT_GRACE_MS = 5000;
30
+ function isWaiterTimeoutError(error) {
31
+ return (error instanceof Error && error.message.startsWith("Timeout waiting for message"));
32
+ }
24
33
  export class DaemonClient {
25
34
  constructor(config) {
26
35
  this.config = config;
@@ -43,9 +52,21 @@ export class DaemonClient {
43
52
  this.connectionState = { status: "idle" };
44
53
  this.agentUpdateSubscriptions = new Map();
45
54
  this.checkoutDiffSubscriptions = new Map();
55
+ this.terminalDirectorySubscriptions = new Set();
46
56
  this.pendingSendQueue = [];
47
57
  this.relayClientId = null;
48
58
  this.logger = config.logger ?? consoleLogger;
59
+ this.terminalStreams = new TerminalStreamManager({
60
+ sendAck: (ack) => {
61
+ this.sendBinaryFrame({
62
+ channel: BinaryMuxChannel.Terminal,
63
+ messageType: TerminalBinaryMessageType.Ack,
64
+ streamId: ack.streamId,
65
+ offset: ack.offset,
66
+ payload: new Uint8Array(0),
67
+ });
68
+ },
69
+ });
49
70
  // Relay requires a clientId so the daemon can create an independent
50
71
  // socket + E2EE channel per connected client. Generate one per DaemonClient
51
72
  // instance (stable across reconnects in this tab/app session).
@@ -132,6 +153,7 @@ export class DaemonClient {
132
153
  this.updateConnectionState({ status: "connected" });
133
154
  this.resubscribeAgentUpdates();
134
155
  this.resubscribeCheckoutDiffSubscriptions();
156
+ this.resubscribeTerminalDirectorySubscriptions();
135
157
  this.flushPendingSendQueue();
136
158
  this.resolveConnect();
137
159
  }),
@@ -235,6 +257,7 @@ export class DaemonClient {
235
257
  }
236
258
  this.disposeTransport(1000, "Client closed");
237
259
  this.clearWaiters(new Error("Daemon client closed"));
260
+ this.terminalStreams.clearAll();
238
261
  this.updateConnectionState({
239
262
  status: "disconnected",
240
263
  reason: "client_closed",
@@ -329,6 +352,23 @@ export class DaemonClient {
329
352
  throw error instanceof Error ? error : new Error(String(error));
330
353
  }
331
354
  }
355
+ sendBinaryFrame(frame) {
356
+ if (!this.transport || this.connectionState.status !== "connected") {
357
+ if (this.config.suppressSendErrors) {
358
+ return;
359
+ }
360
+ throw new Error(`Transport not connected (status: ${this.connectionState.status})`);
361
+ }
362
+ try {
363
+ this.transport.send(encodeBinaryMuxFrame(frame));
364
+ }
365
+ catch (error) {
366
+ if (this.config.suppressSendErrors) {
367
+ return;
368
+ }
369
+ throw error instanceof Error ? error : new Error(String(error));
370
+ }
371
+ }
332
372
  /**
333
373
  * Send a session message for RPC methods that create waiters.
334
374
  * If the connection is still being established ("connecting"), the message
@@ -428,6 +468,43 @@ export class DaemonClient {
428
468
  }
429
469
  return result.value;
430
470
  }
471
+ async sendCorrelatedRequest(params) {
472
+ return this.sendRequest({
473
+ requestId: params.requestId,
474
+ message: params.message,
475
+ timeout: params.timeout,
476
+ options: params.options,
477
+ select: (msg) => {
478
+ const correlated = msg;
479
+ if (correlated.type !== params.responseType) {
480
+ return null;
481
+ }
482
+ const payload = correlated.payload;
483
+ if (payload.requestId !== params.requestId) {
484
+ return null;
485
+ }
486
+ if (!params.selectPayload) {
487
+ return payload;
488
+ }
489
+ return params.selectPayload(payload);
490
+ },
491
+ });
492
+ }
493
+ sendCorrelatedSessionRequest(params) {
494
+ const resolvedRequestId = this.createRequestId(params.requestId);
495
+ const message = SessionInboundMessageSchema.parse({
496
+ ...params.message,
497
+ requestId: resolvedRequestId,
498
+ });
499
+ return this.sendCorrelatedRequest({
500
+ requestId: resolvedRequestId,
501
+ message,
502
+ responseType: params.responseType,
503
+ timeout: params.timeout,
504
+ options: { skipQueue: true },
505
+ ...(params.selectPayload ? { selectPayload: params.selectPayload } : {}),
506
+ });
507
+ }
431
508
  sendSessionMessageStrict(message) {
432
509
  if (!this.transport || this.connectionState.status !== "connected") {
433
510
  throw new Error("Transport not connected");
@@ -496,6 +573,8 @@ export class DaemonClient {
496
573
  type: "fetch_agents_request",
497
574
  requestId: resolvedRequestId,
498
575
  ...(options?.filter ? { filter: options.filter } : {}),
576
+ ...(options?.sort ? { sort: options.sort } : {}),
577
+ ...(options?.page ? { page: options.page } : {}),
499
578
  });
500
579
  return this.sendRequest({
501
580
  requestId: resolvedRequestId,
@@ -509,29 +588,6 @@ export class DaemonClient {
509
588
  if (msg.payload.requestId !== resolvedRequestId) {
510
589
  return null;
511
590
  }
512
- return msg.payload.agents;
513
- },
514
- });
515
- }
516
- async fetchAgentsGroupedByProject(options) {
517
- const resolvedRequestId = this.createRequestId(options?.requestId);
518
- const message = SessionInboundMessageSchema.parse({
519
- type: "fetch_agents_grouped_by_project_request",
520
- requestId: resolvedRequestId,
521
- ...(options?.filter ? { filter: options.filter } : {}),
522
- });
523
- return this.sendRequest({
524
- requestId: resolvedRequestId,
525
- message,
526
- timeout: 15000,
527
- options: { skipQueue: true },
528
- select: (msg) => {
529
- if (msg.type !== "fetch_agents_grouped_by_project_response") {
530
- return null;
531
- }
532
- if (msg.payload.requestId !== resolvedRequestId) {
533
- return null;
534
- }
535
591
  return msg.payload;
536
592
  },
537
593
  });
@@ -610,6 +666,17 @@ export class DaemonClient {
610
666
  this.sendSessionMessage(message);
611
667
  }
612
668
  }
669
+ resubscribeTerminalDirectorySubscriptions() {
670
+ if (this.terminalDirectorySubscriptions.size === 0) {
671
+ return;
672
+ }
673
+ for (const cwd of this.terminalDirectorySubscriptions) {
674
+ this.sendSessionMessage({
675
+ type: "subscribe_terminals_request",
676
+ cwd,
677
+ });
678
+ }
679
+ }
613
680
  // ============================================================================
614
681
  // Agent Lifecycle
615
682
  // ============================================================================
@@ -783,20 +850,24 @@ export class DaemonClient {
783
850
  },
784
851
  });
785
852
  }
786
- async initializeAgent(agentId, requestId) {
787
- const resolvedRequestId = this.createRequestId(requestId);
853
+ async fetchAgentTimeline(agentId, options = {}) {
854
+ const resolvedRequestId = this.createRequestId(options.requestId);
788
855
  const message = SessionInboundMessageSchema.parse({
789
- type: "initialize_agent_request",
856
+ type: "fetch_agent_timeline_request",
790
857
  agentId,
791
858
  requestId: resolvedRequestId,
859
+ ...(options.direction ? { direction: options.direction } : {}),
860
+ ...(options.cursor ? { cursor: options.cursor } : {}),
861
+ ...(typeof options.limit === "number" ? { limit: options.limit } : {}),
862
+ ...(options.projection ? { projection: options.projection } : {}),
792
863
  });
793
864
  const payload = await this.sendRequest({
794
865
  requestId: resolvedRequestId,
795
866
  message,
796
- timeout: 10000,
867
+ timeout: 15000,
797
868
  options: { skipQueue: true },
798
869
  select: (msg) => {
799
- if (msg.type !== "initialize_agent_request") {
870
+ if (msg.type !== "fetch_agent_timeline_response") {
800
871
  return null;
801
872
  }
802
873
  if (msg.payload.requestId !== resolvedRequestId) {
@@ -808,11 +879,7 @@ export class DaemonClient {
808
879
  if (payload.error) {
809
880
  throw new Error(payload.error);
810
881
  }
811
- const agent = await this.fetchAgent(agentId);
812
- if (!agent) {
813
- throw new Error(`Agent not found after initialize: ${agentId}`);
814
- }
815
- return agent;
882
+ return payload;
816
883
  }
817
884
  // ============================================================================
818
885
  // Agent Interaction
@@ -972,7 +1039,7 @@ export class DaemonClient {
972
1039
  ...(agentId ? { agentId } : {}),
973
1040
  requestId,
974
1041
  });
975
- return this.sendRequest({
1042
+ const response = await this.sendRequest({
976
1043
  requestId,
977
1044
  message,
978
1045
  timeout: 10000,
@@ -986,11 +1053,18 @@ export class DaemonClient {
986
1053
  return msg.payload;
987
1054
  },
988
1055
  });
1056
+ if (!response.accepted) {
1057
+ const codeSuffix = typeof response.reasonCode === "string" && response.reasonCode.trim().length > 0
1058
+ ? ` (${response.reasonCode})`
1059
+ : "";
1060
+ throw new Error((response.error ?? "Failed to set voice mode") + codeSuffix);
1061
+ }
1062
+ return response;
989
1063
  }
990
1064
  async sendVoiceAudioChunk(audio, format, isLast) {
991
1065
  this.sendSessionMessage({ type: "voice_audio_chunk", audio, format, isLast });
992
1066
  }
993
- startDictationStream(dictationId, format) {
1067
+ async startDictationStream(dictationId, format) {
994
1068
  const ack = this.waitForWithCancel((msg) => {
995
1069
  if (msg.type !== "dictation_stream_ack") {
996
1070
  return null;
@@ -1016,23 +1090,22 @@ export class DaemonClient {
1016
1090
  const errorPromise = streamError.promise.then((payload) => {
1017
1091
  throw new Error(payload.error);
1018
1092
  });
1093
+ const cleanupError = new Error("Cancelled dictation start waiter");
1019
1094
  try {
1020
1095
  this.sendSessionMessageStrict({ type: "dictation_stream_start", dictationId, format });
1096
+ await Promise.race([ackPromise, errorPromise]);
1021
1097
  }
1022
- catch (error) {
1023
- const err = error instanceof Error ? error : new Error(String(error));
1024
- ack.cancel(err);
1025
- streamError.cancel(err);
1098
+ finally {
1099
+ ack.cancel(cleanupError);
1100
+ streamError.cancel(cleanupError);
1026
1101
  void ackPromise.catch(() => undefined);
1027
1102
  void errorPromise.catch(() => undefined);
1028
- throw err;
1029
1103
  }
1030
- return Promise.race([ackPromise, errorPromise]);
1031
1104
  }
1032
1105
  sendDictationStreamChunk(dictationId, seq, audio, format) {
1033
1106
  this.sendSessionMessageStrict({ type: "dictation_stream_chunk", dictationId, seq, audio, format });
1034
1107
  }
1035
- finishDictationStream(dictationId, finalSeq) {
1108
+ async finishDictationStream(dictationId, finalSeq) {
1036
1109
  const final = this.waitForWithCancel((msg) => {
1037
1110
  if (msg.type !== "dictation_stream_final") {
1038
1111
  return null;
@@ -1041,7 +1114,7 @@ export class DaemonClient {
1041
1114
  return null;
1042
1115
  }
1043
1116
  return msg.payload;
1044
- }, 30000, { skipQueue: true });
1117
+ }, 0, { skipQueue: true });
1045
1118
  const streamError = this.waitForWithCancel((msg) => {
1046
1119
  if (msg.type !== "dictation_stream_error") {
1047
1120
  return null;
@@ -1050,23 +1123,96 @@ export class DaemonClient {
1050
1123
  return null;
1051
1124
  }
1052
1125
  return msg.payload;
1053
- }, 30000, { skipQueue: true });
1126
+ }, 0, { skipQueue: true });
1127
+ const finishAccepted = this.waitForWithCancel((msg) => {
1128
+ if (msg.type !== "dictation_stream_finish_accepted") {
1129
+ return null;
1130
+ }
1131
+ if (msg.payload.dictationId !== dictationId) {
1132
+ return null;
1133
+ }
1134
+ return msg.payload;
1135
+ }, DEFAULT_DICTATION_FINISH_ACCEPT_TIMEOUT_MS, { skipQueue: true });
1054
1136
  const finalPromise = final.promise;
1055
1137
  const errorPromise = streamError.promise.then((payload) => {
1056
1138
  throw new Error(payload.error);
1057
1139
  });
1140
+ const finishAcceptedPromise = finishAccepted.promise;
1141
+ const finalOutcomePromise = finalPromise.then((payload) => ({
1142
+ kind: "final",
1143
+ payload,
1144
+ }));
1145
+ const errorOutcomePromise = errorPromise.then(() => ({
1146
+ kind: "error",
1147
+ error: new Error("Unexpected dictation stream error state"),
1148
+ }), (error) => ({
1149
+ kind: "error",
1150
+ error: error instanceof Error ? error : new Error(String(error)),
1151
+ }));
1152
+ const finishAcceptedOutcomePromise = finishAcceptedPromise.then((payload) => ({ kind: "accepted", payload }), (error) => {
1153
+ if (isWaiterTimeoutError(error)) {
1154
+ return { kind: "accepted_timeout" };
1155
+ }
1156
+ return {
1157
+ kind: "accepted_error",
1158
+ error: error instanceof Error ? error : new Error(String(error)),
1159
+ };
1160
+ });
1161
+ const waitForFinalResult = async (timeoutMs) => {
1162
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1163
+ const outcome = await Promise.race([finalOutcomePromise, errorOutcomePromise]);
1164
+ if (outcome.kind === "error") {
1165
+ throw outcome.error;
1166
+ }
1167
+ return outcome.payload;
1168
+ }
1169
+ let timeoutHandle = null;
1170
+ const timeoutPromise = new Promise((resolve) => {
1171
+ timeoutHandle = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs);
1172
+ });
1173
+ const outcome = await Promise.race([
1174
+ finalOutcomePromise,
1175
+ errorOutcomePromise,
1176
+ timeoutPromise,
1177
+ ]);
1178
+ if (timeoutHandle) {
1179
+ clearTimeout(timeoutHandle);
1180
+ }
1181
+ if (outcome.kind === "timeout") {
1182
+ throw new Error(`Timeout waiting for dictation finalization (${timeoutMs}ms)`);
1183
+ }
1184
+ if (outcome.kind === "error") {
1185
+ throw outcome.error;
1186
+ }
1187
+ return outcome.payload;
1188
+ };
1189
+ const cleanupError = new Error("Cancelled dictation finish waiter");
1058
1190
  try {
1059
1191
  this.sendSessionMessageStrict({ type: "dictation_stream_finish", dictationId, finalSeq });
1192
+ const firstOutcome = await Promise.race([
1193
+ finalOutcomePromise,
1194
+ errorOutcomePromise,
1195
+ finishAcceptedOutcomePromise,
1196
+ ]);
1197
+ if (firstOutcome.kind === "final") {
1198
+ return firstOutcome.payload;
1199
+ }
1200
+ if (firstOutcome.kind === "error") {
1201
+ throw firstOutcome.error;
1202
+ }
1203
+ if (firstOutcome.kind === "accepted") {
1204
+ return await waitForFinalResult(firstOutcome.payload.timeoutMs + DEFAULT_DICTATION_FINISH_TIMEOUT_GRACE_MS);
1205
+ }
1206
+ return await waitForFinalResult(DEFAULT_DICTATION_FINISH_FALLBACK_TIMEOUT_MS);
1060
1207
  }
1061
- catch (error) {
1062
- const err = error instanceof Error ? error : new Error(String(error));
1063
- final.cancel(err);
1064
- streamError.cancel(err);
1208
+ finally {
1209
+ final.cancel(cleanupError);
1210
+ streamError.cancel(cleanupError);
1211
+ finishAccepted.cancel(cleanupError);
1065
1212
  void finalPromise.catch(() => undefined);
1066
1213
  void errorPromise.catch(() => undefined);
1067
- throw err;
1214
+ void finishAcceptedPromise.catch(() => undefined);
1068
1215
  }
1069
- return Promise.race([finalPromise, errorPromise]);
1070
1216
  }
1071
1217
  cancelDictationStream(dictationId) {
1072
1218
  this.sendSessionMessageStrict({ type: "dictation_stream_cancel", dictationId });
@@ -1171,22 +1317,17 @@ export class DaemonClient {
1171
1317
  requestId: resolvedRequestId,
1172
1318
  });
1173
1319
  try {
1174
- return await this.sendRequest({
1320
+ return await this.sendCorrelatedRequest({
1175
1321
  requestId: resolvedRequestId,
1176
1322
  message,
1323
+ responseType: "subscribe_checkout_diff_response",
1177
1324
  timeout: 60000,
1178
1325
  options: { skipQueue: true },
1179
- select: (msg) => {
1180
- if (msg.type !== "subscribe_checkout_diff_response") {
1181
- return null;
1182
- }
1183
- if (msg.payload.requestId !== resolvedRequestId) {
1184
- return null;
1185
- }
1186
- if (msg.payload.subscriptionId !== subscriptionId) {
1326
+ selectPayload: (payload) => {
1327
+ if (payload.subscriptionId !== subscriptionId) {
1187
1328
  return null;
1188
1329
  }
1189
- return msg.payload;
1330
+ return payload;
1190
1331
  },
1191
1332
  });
1192
1333
  }
@@ -1208,441 +1349,238 @@ export class DaemonClient {
1208
1349
  });
1209
1350
  }
1210
1351
  async checkoutCommit(cwd, input, requestId) {
1211
- const resolvedRequestId = this.createRequestId(requestId);
1212
- const message = SessionInboundMessageSchema.parse({
1213
- type: "checkout_commit_request",
1214
- cwd,
1215
- message: input.message,
1216
- addAll: input.addAll,
1217
- requestId: resolvedRequestId,
1218
- });
1219
- return this.sendRequest({
1220
- requestId: resolvedRequestId,
1221
- message,
1222
- timeout: 60000,
1223
- options: { skipQueue: true },
1224
- select: (msg) => {
1225
- if (msg.type !== "checkout_commit_response") {
1226
- return null;
1227
- }
1228
- if (msg.payload.requestId !== resolvedRequestId) {
1229
- return null;
1230
- }
1231
- return msg.payload;
1352
+ return this.sendCorrelatedSessionRequest({
1353
+ requestId,
1354
+ message: {
1355
+ type: "checkout_commit_request",
1356
+ cwd,
1357
+ message: input.message,
1358
+ addAll: input.addAll,
1232
1359
  },
1360
+ responseType: "checkout_commit_response",
1361
+ timeout: 60000,
1233
1362
  });
1234
1363
  }
1235
1364
  async checkoutMerge(cwd, input, requestId) {
1236
- const resolvedRequestId = this.createRequestId(requestId);
1237
- const message = SessionInboundMessageSchema.parse({
1238
- type: "checkout_merge_request",
1239
- cwd,
1240
- baseRef: input.baseRef,
1241
- strategy: input.strategy,
1242
- requireCleanTarget: input.requireCleanTarget,
1243
- requestId: resolvedRequestId,
1244
- });
1245
- return this.sendRequest({
1246
- requestId: resolvedRequestId,
1247
- message,
1248
- timeout: 60000,
1249
- options: { skipQueue: true },
1250
- select: (msg) => {
1251
- if (msg.type !== "checkout_merge_response") {
1252
- return null;
1253
- }
1254
- if (msg.payload.requestId !== resolvedRequestId) {
1255
- return null;
1256
- }
1257
- return msg.payload;
1365
+ return this.sendCorrelatedSessionRequest({
1366
+ requestId,
1367
+ message: {
1368
+ type: "checkout_merge_request",
1369
+ cwd,
1370
+ baseRef: input.baseRef,
1371
+ strategy: input.strategy,
1372
+ requireCleanTarget: input.requireCleanTarget,
1258
1373
  },
1374
+ responseType: "checkout_merge_response",
1375
+ timeout: 60000,
1259
1376
  });
1260
1377
  }
1261
1378
  async checkoutMergeFromBase(cwd, input, requestId) {
1262
- const resolvedRequestId = this.createRequestId(requestId);
1263
- const message = SessionInboundMessageSchema.parse({
1264
- type: "checkout_merge_from_base_request",
1265
- cwd,
1266
- baseRef: input.baseRef,
1267
- requireCleanTarget: input.requireCleanTarget,
1268
- requestId: resolvedRequestId,
1269
- });
1270
- return this.sendRequest({
1271
- requestId: resolvedRequestId,
1272
- message,
1273
- timeout: 60000,
1274
- options: { skipQueue: true },
1275
- select: (msg) => {
1276
- if (msg.type !== "checkout_merge_from_base_response") {
1277
- return null;
1278
- }
1279
- if (msg.payload.requestId !== resolvedRequestId) {
1280
- return null;
1281
- }
1282
- return msg.payload;
1379
+ return this.sendCorrelatedSessionRequest({
1380
+ requestId,
1381
+ message: {
1382
+ type: "checkout_merge_from_base_request",
1383
+ cwd,
1384
+ baseRef: input.baseRef,
1385
+ requireCleanTarget: input.requireCleanTarget,
1283
1386
  },
1387
+ responseType: "checkout_merge_from_base_response",
1388
+ timeout: 60000,
1284
1389
  });
1285
1390
  }
1286
1391
  async checkoutPush(cwd, requestId) {
1287
- const resolvedRequestId = this.createRequestId(requestId);
1288
- const message = SessionInboundMessageSchema.parse({
1289
- type: "checkout_push_request",
1290
- cwd,
1291
- requestId: resolvedRequestId,
1292
- });
1293
- return this.sendRequest({
1294
- requestId: resolvedRequestId,
1295
- message,
1296
- timeout: 60000,
1297
- options: { skipQueue: true },
1298
- select: (msg) => {
1299
- if (msg.type !== "checkout_push_response") {
1300
- return null;
1301
- }
1302
- if (msg.payload.requestId !== resolvedRequestId) {
1303
- return null;
1304
- }
1305
- return msg.payload;
1392
+ return this.sendCorrelatedSessionRequest({
1393
+ requestId,
1394
+ message: {
1395
+ type: "checkout_push_request",
1396
+ cwd,
1306
1397
  },
1398
+ responseType: "checkout_push_response",
1399
+ timeout: 60000,
1307
1400
  });
1308
1401
  }
1309
1402
  async checkoutPrCreate(cwd, input, requestId) {
1310
- const resolvedRequestId = this.createRequestId(requestId);
1311
- const message = SessionInboundMessageSchema.parse({
1312
- type: "checkout_pr_create_request",
1313
- cwd,
1314
- title: input.title,
1315
- body: input.body,
1316
- baseRef: input.baseRef,
1317
- requestId: resolvedRequestId,
1318
- });
1319
- return this.sendRequest({
1320
- requestId: resolvedRequestId,
1321
- message,
1322
- timeout: 60000,
1323
- options: { skipQueue: true },
1324
- select: (msg) => {
1325
- if (msg.type !== "checkout_pr_create_response") {
1326
- return null;
1327
- }
1328
- if (msg.payload.requestId !== resolvedRequestId) {
1329
- return null;
1330
- }
1331
- return msg.payload;
1403
+ return this.sendCorrelatedSessionRequest({
1404
+ requestId,
1405
+ message: {
1406
+ type: "checkout_pr_create_request",
1407
+ cwd,
1408
+ title: input.title,
1409
+ body: input.body,
1410
+ baseRef: input.baseRef,
1332
1411
  },
1412
+ responseType: "checkout_pr_create_response",
1413
+ timeout: 60000,
1333
1414
  });
1334
1415
  }
1335
1416
  async checkoutPrStatus(cwd, requestId) {
1336
- const resolvedRequestId = this.createRequestId(requestId);
1337
- const message = SessionInboundMessageSchema.parse({
1338
- type: "checkout_pr_status_request",
1339
- cwd,
1340
- requestId: resolvedRequestId,
1341
- });
1342
- return this.sendRequest({
1343
- requestId: resolvedRequestId,
1344
- message,
1345
- timeout: 60000,
1346
- options: { skipQueue: true },
1347
- select: (msg) => {
1348
- if (msg.type !== "checkout_pr_status_response") {
1349
- return null;
1350
- }
1351
- if (msg.payload.requestId !== resolvedRequestId) {
1352
- return null;
1353
- }
1354
- return msg.payload;
1417
+ return this.sendCorrelatedSessionRequest({
1418
+ requestId,
1419
+ message: {
1420
+ type: "checkout_pr_status_request",
1421
+ cwd,
1355
1422
  },
1423
+ responseType: "checkout_pr_status_response",
1424
+ timeout: 60000,
1356
1425
  });
1357
1426
  }
1358
1427
  async getPaseoWorktreeList(input, requestId) {
1359
- const resolvedRequestId = this.createRequestId(requestId);
1360
- const message = SessionInboundMessageSchema.parse({
1361
- type: "paseo_worktree_list_request",
1362
- cwd: input.cwd,
1363
- repoRoot: input.repoRoot,
1364
- requestId: resolvedRequestId,
1365
- });
1366
- return this.sendRequest({
1367
- requestId: resolvedRequestId,
1368
- message,
1369
- timeout: 60000,
1370
- options: { skipQueue: true },
1371
- select: (msg) => {
1372
- if (msg.type !== "paseo_worktree_list_response") {
1373
- return null;
1374
- }
1375
- if (msg.payload.requestId !== resolvedRequestId) {
1376
- return null;
1377
- }
1378
- return msg.payload;
1428
+ return this.sendCorrelatedSessionRequest({
1429
+ requestId,
1430
+ message: {
1431
+ type: "paseo_worktree_list_request",
1432
+ cwd: input.cwd,
1433
+ repoRoot: input.repoRoot,
1379
1434
  },
1435
+ responseType: "paseo_worktree_list_response",
1436
+ timeout: 60000,
1380
1437
  });
1381
1438
  }
1382
1439
  async archivePaseoWorktree(input, requestId) {
1383
- const resolvedRequestId = this.createRequestId(requestId);
1384
- const message = SessionInboundMessageSchema.parse({
1385
- type: "paseo_worktree_archive_request",
1386
- worktreePath: input.worktreePath,
1387
- repoRoot: input.repoRoot,
1388
- branchName: input.branchName,
1389
- requestId: resolvedRequestId,
1390
- });
1391
- return this.sendRequest({
1392
- requestId: resolvedRequestId,
1393
- message,
1394
- timeout: 20000,
1395
- options: { skipQueue: true },
1396
- select: (msg) => {
1397
- if (msg.type !== "paseo_worktree_archive_response") {
1398
- return null;
1399
- }
1400
- if (msg.payload.requestId !== resolvedRequestId) {
1401
- return null;
1402
- }
1403
- return msg.payload;
1440
+ return this.sendCorrelatedSessionRequest({
1441
+ requestId,
1442
+ message: {
1443
+ type: "paseo_worktree_archive_request",
1444
+ worktreePath: input.worktreePath,
1445
+ repoRoot: input.repoRoot,
1446
+ branchName: input.branchName,
1404
1447
  },
1448
+ responseType: "paseo_worktree_archive_response",
1449
+ timeout: 20000,
1405
1450
  });
1406
1451
  }
1407
1452
  async validateBranch(options, requestId) {
1408
- const resolvedRequestId = this.createRequestId(requestId);
1409
- const message = SessionInboundMessageSchema.parse({
1410
- type: "validate_branch_request",
1411
- cwd: options.cwd,
1412
- branchName: options.branchName,
1413
- requestId: resolvedRequestId,
1414
- });
1415
- return this.sendRequest({
1416
- requestId: resolvedRequestId,
1417
- message,
1453
+ return this.sendCorrelatedSessionRequest({
1454
+ requestId,
1455
+ message: {
1456
+ type: "validate_branch_request",
1457
+ cwd: options.cwd,
1458
+ branchName: options.branchName,
1459
+ },
1460
+ responseType: "validate_branch_response",
1418
1461
  timeout: 10000,
1419
- options: { skipQueue: true },
1420
- select: (msg) => {
1421
- if (msg.type !== "validate_branch_response") {
1422
- return null;
1423
- }
1424
- if (msg.payload.requestId !== resolvedRequestId) {
1425
- return null;
1426
- }
1427
- return msg.payload;
1462
+ });
1463
+ }
1464
+ async getBranchSuggestions(options, requestId) {
1465
+ return this.sendCorrelatedSessionRequest({
1466
+ requestId,
1467
+ message: {
1468
+ type: "branch_suggestions_request",
1469
+ cwd: options.cwd,
1470
+ query: options.query,
1471
+ limit: options.limit,
1428
1472
  },
1473
+ responseType: "branch_suggestions_response",
1474
+ timeout: 10000,
1429
1475
  });
1430
1476
  }
1431
1477
  // ============================================================================
1432
1478
  // File Explorer
1433
1479
  // ============================================================================
1434
1480
  async exploreFileSystem(agentId, path, mode = "list", requestId) {
1435
- const resolvedRequestId = this.createRequestId(requestId);
1436
- const message = SessionInboundMessageSchema.parse({
1437
- type: "file_explorer_request",
1438
- agentId,
1439
- path,
1440
- mode,
1441
- requestId: resolvedRequestId,
1442
- });
1443
- return this.sendRequest({
1444
- requestId: resolvedRequestId,
1445
- message,
1446
- timeout: 10000,
1447
- options: { skipQueue: true },
1448
- select: (msg) => {
1449
- if (msg.type !== "file_explorer_response") {
1450
- return null;
1451
- }
1452
- if (msg.payload.requestId !== resolvedRequestId) {
1453
- return null;
1454
- }
1455
- return msg.payload;
1481
+ return this.sendCorrelatedSessionRequest({
1482
+ requestId,
1483
+ message: {
1484
+ type: "file_explorer_request",
1485
+ agentId,
1486
+ path,
1487
+ mode,
1456
1488
  },
1489
+ responseType: "file_explorer_response",
1490
+ timeout: 10000,
1457
1491
  });
1458
1492
  }
1459
1493
  async requestDownloadToken(agentId, path, requestId) {
1460
- const resolvedRequestId = this.createRequestId(requestId);
1461
- const message = SessionInboundMessageSchema.parse({
1462
- type: "file_download_token_request",
1463
- agentId,
1464
- path,
1465
- requestId: resolvedRequestId,
1466
- });
1467
- return this.sendRequest({
1468
- requestId: resolvedRequestId,
1469
- message,
1470
- timeout: 10000,
1471
- options: { skipQueue: true },
1472
- select: (msg) => {
1473
- if (msg.type !== "file_download_token_response") {
1474
- return null;
1475
- }
1476
- if (msg.payload.requestId !== resolvedRequestId) {
1477
- return null;
1478
- }
1479
- return msg.payload;
1494
+ return this.sendCorrelatedSessionRequest({
1495
+ requestId,
1496
+ message: {
1497
+ type: "file_download_token_request",
1498
+ agentId,
1499
+ path,
1480
1500
  },
1501
+ responseType: "file_download_token_response",
1502
+ timeout: 10000,
1481
1503
  });
1482
1504
  }
1483
1505
  async requestProjectIcon(cwd, requestId) {
1484
- const resolvedRequestId = this.createRequestId(requestId);
1485
- const message = SessionInboundMessageSchema.parse({
1486
- type: "project_icon_request",
1487
- cwd,
1488
- requestId: resolvedRequestId,
1489
- });
1490
- return this.sendRequest({
1491
- requestId: resolvedRequestId,
1492
- message,
1493
- timeout: 10000,
1494
- options: { skipQueue: true },
1495
- select: (msg) => {
1496
- if (msg.type !== "project_icon_response") {
1497
- return null;
1498
- }
1499
- if (msg.payload.requestId !== resolvedRequestId) {
1500
- return null;
1501
- }
1502
- return msg.payload;
1506
+ return this.sendCorrelatedSessionRequest({
1507
+ requestId,
1508
+ message: {
1509
+ type: "project_icon_request",
1510
+ cwd,
1503
1511
  },
1512
+ responseType: "project_icon_response",
1513
+ timeout: 10000,
1504
1514
  });
1505
1515
  }
1506
1516
  // ============================================================================
1507
1517
  // Provider Models / Commands
1508
1518
  // ============================================================================
1509
1519
  async listProviderModels(provider, options) {
1510
- const resolvedRequestId = this.createRequestId(options?.requestId);
1511
- const message = SessionInboundMessageSchema.parse({
1512
- type: "list_provider_models_request",
1513
- provider,
1514
- cwd: options?.cwd,
1515
- requestId: resolvedRequestId,
1516
- });
1517
- return this.sendRequest({
1518
- requestId: resolvedRequestId,
1519
- message,
1520
- timeout: 30000,
1521
- options: { skipQueue: true },
1522
- select: (msg) => {
1523
- if (msg.type !== "list_provider_models_response") {
1524
- return null;
1525
- }
1526
- if (msg.payload.requestId !== resolvedRequestId) {
1527
- return null;
1528
- }
1529
- return msg.payload;
1520
+ return this.sendCorrelatedSessionRequest({
1521
+ requestId: options?.requestId,
1522
+ message: {
1523
+ type: "list_provider_models_request",
1524
+ provider,
1525
+ cwd: options?.cwd,
1530
1526
  },
1527
+ responseType: "list_provider_models_response",
1528
+ timeout: 30000,
1531
1529
  });
1532
1530
  }
1533
1531
  async listAvailableProviders(options) {
1534
- const resolvedRequestId = this.createRequestId(options?.requestId);
1535
- const message = SessionInboundMessageSchema.parse({
1536
- type: "list_available_providers_request",
1537
- requestId: resolvedRequestId,
1538
- });
1539
- return this.sendRequest({
1540
- requestId: resolvedRequestId,
1541
- message,
1542
- timeout: 30000,
1543
- options: { skipQueue: true },
1544
- select: (msg) => {
1545
- if (msg.type !== "list_available_providers_response") {
1546
- return null;
1547
- }
1548
- if (msg.payload.requestId !== resolvedRequestId) {
1549
- return null;
1550
- }
1551
- return msg.payload;
1532
+ return this.sendCorrelatedSessionRequest({
1533
+ requestId: options?.requestId,
1534
+ message: {
1535
+ type: "list_available_providers_request",
1552
1536
  },
1537
+ responseType: "list_available_providers_response",
1538
+ timeout: 30000,
1553
1539
  });
1554
1540
  }
1555
1541
  async listSpeechModels(requestId) {
1556
- const resolvedRequestId = this.createRequestId(requestId);
1557
- const message = SessionInboundMessageSchema.parse({
1558
- type: "speech_models_list_request",
1559
- requestId: resolvedRequestId,
1560
- });
1561
- return this.sendRequest({
1562
- requestId: resolvedRequestId,
1563
- message,
1564
- timeout: 30000,
1565
- options: { skipQueue: true },
1566
- select: (msg) => {
1567
- if (msg.type !== "speech_models_list_response") {
1568
- return null;
1569
- }
1570
- if (msg.payload.requestId !== resolvedRequestId) {
1571
- return null;
1572
- }
1573
- return msg.payload;
1542
+ return this.sendCorrelatedSessionRequest({
1543
+ requestId,
1544
+ message: {
1545
+ type: "speech_models_list_request",
1574
1546
  },
1547
+ responseType: "speech_models_list_response",
1548
+ timeout: 30000,
1575
1549
  });
1576
1550
  }
1577
1551
  async downloadSpeechModels(options) {
1578
- const resolvedRequestId = this.createRequestId(options?.requestId);
1579
- const message = SessionInboundMessageSchema.parse({
1580
- type: "speech_models_download_request",
1581
- modelIds: options?.modelIds,
1582
- requestId: resolvedRequestId,
1583
- });
1584
- return this.sendRequest({
1585
- requestId: resolvedRequestId,
1586
- message,
1587
- timeout: 30 * 60 * 1000,
1588
- options: { skipQueue: true },
1589
- select: (msg) => {
1590
- if (msg.type !== "speech_models_download_response") {
1591
- return null;
1592
- }
1593
- if (msg.payload.requestId !== resolvedRequestId) {
1594
- return null;
1595
- }
1596
- return msg.payload;
1552
+ return this.sendCorrelatedSessionRequest({
1553
+ requestId: options?.requestId,
1554
+ message: {
1555
+ type: "speech_models_download_request",
1556
+ modelIds: options?.modelIds,
1597
1557
  },
1558
+ responseType: "speech_models_download_response",
1559
+ timeout: 30 * 60 * 1000,
1598
1560
  });
1599
1561
  }
1600
1562
  async listCommands(agentId, requestId) {
1601
- const resolvedRequestId = this.createRequestId(requestId);
1602
- const message = SessionInboundMessageSchema.parse({
1603
- type: "list_commands_request",
1604
- agentId,
1605
- requestId: resolvedRequestId,
1606
- });
1607
- return this.sendRequest({
1608
- requestId: resolvedRequestId,
1609
- message,
1610
- timeout: 30000,
1611
- options: { skipQueue: true },
1612
- select: (msg) => {
1613
- if (msg.type !== "list_commands_response") {
1614
- return null;
1615
- }
1616
- if (msg.payload.requestId !== resolvedRequestId) {
1617
- return null;
1618
- }
1619
- return msg.payload;
1563
+ return this.sendCorrelatedSessionRequest({
1564
+ requestId,
1565
+ message: {
1566
+ type: "list_commands_request",
1567
+ agentId,
1620
1568
  },
1569
+ responseType: "list_commands_response",
1570
+ timeout: 30000,
1621
1571
  });
1622
1572
  }
1623
1573
  async executeCommand(agentId, commandName, args, requestId) {
1624
- const resolvedRequestId = this.createRequestId(requestId);
1625
- const message = SessionInboundMessageSchema.parse({
1626
- type: "execute_command_request",
1627
- agentId,
1628
- commandName,
1629
- args,
1630
- requestId: resolvedRequestId,
1631
- });
1632
- return this.sendRequest({
1633
- requestId: resolvedRequestId,
1634
- message,
1635
- timeout: 30000,
1636
- options: { skipQueue: true },
1637
- select: (msg) => {
1638
- if (msg.type !== "execute_command_response") {
1639
- return null;
1640
- }
1641
- if (msg.payload.requestId !== resolvedRequestId) {
1642
- return null;
1643
- }
1644
- return msg.payload;
1574
+ return this.sendCorrelatedSessionRequest({
1575
+ requestId,
1576
+ message: {
1577
+ type: "execute_command_request",
1578
+ agentId,
1579
+ commandName,
1580
+ args,
1645
1581
  },
1582
+ responseType: "execute_command_response",
1583
+ timeout: 30000,
1646
1584
  });
1647
1585
  }
1648
1586
  // ============================================================================
@@ -1704,20 +1642,12 @@ export class DaemonClient {
1704
1642
  agentId,
1705
1643
  timeoutMs: timeout,
1706
1644
  });
1707
- const payload = await this.sendRequest({
1645
+ const payload = await this.sendCorrelatedRequest({
1708
1646
  requestId,
1709
1647
  message,
1648
+ responseType: "wait_for_finish_response",
1710
1649
  timeout: timeout + 5000,
1711
1650
  options: { skipQueue: true },
1712
- select: (msg) => {
1713
- if (msg.type !== "wait_for_finish_response") {
1714
- return null;
1715
- }
1716
- if (msg.payload.requestId !== requestId) {
1717
- return null;
1718
- }
1719
- return msg.payload;
1720
- },
1721
1651
  });
1722
1652
  return {
1723
1653
  status: payload.status,
@@ -1729,6 +1659,26 @@ export class DaemonClient {
1729
1659
  // ============================================================================
1730
1660
  // Terminals
1731
1661
  // ============================================================================
1662
+ subscribeTerminals(input) {
1663
+ this.terminalDirectorySubscriptions.add(input.cwd);
1664
+ if (!this.transport || this.connectionState.status !== "connected") {
1665
+ return;
1666
+ }
1667
+ this.sendSessionMessage({
1668
+ type: "subscribe_terminals_request",
1669
+ cwd: input.cwd,
1670
+ });
1671
+ }
1672
+ unsubscribeTerminals(input) {
1673
+ this.terminalDirectorySubscriptions.delete(input.cwd);
1674
+ if (!this.transport || this.connectionState.status !== "connected") {
1675
+ return;
1676
+ }
1677
+ this.sendSessionMessage({
1678
+ type: "unsubscribe_terminals_request",
1679
+ cwd: input.cwd,
1680
+ });
1681
+ }
1732
1682
  async listTerminals(cwd, requestId) {
1733
1683
  const resolvedRequestId = this.createRequestId(requestId);
1734
1684
  const message = SessionInboundMessageSchema.parse({
@@ -1736,20 +1686,12 @@ export class DaemonClient {
1736
1686
  cwd,
1737
1687
  requestId: resolvedRequestId,
1738
1688
  });
1739
- return this.sendRequest({
1689
+ return this.sendCorrelatedRequest({
1740
1690
  requestId: resolvedRequestId,
1741
1691
  message,
1692
+ responseType: "list_terminals_response",
1742
1693
  timeout: 10000,
1743
1694
  options: { skipQueue: true },
1744
- select: (msg) => {
1745
- if (msg.type !== "list_terminals_response") {
1746
- return null;
1747
- }
1748
- if (msg.payload.requestId !== resolvedRequestId) {
1749
- return null;
1750
- }
1751
- return msg.payload;
1752
- },
1753
1695
  });
1754
1696
  }
1755
1697
  async createTerminal(cwd, name, requestId) {
@@ -1760,20 +1702,12 @@ export class DaemonClient {
1760
1702
  name,
1761
1703
  requestId: resolvedRequestId,
1762
1704
  });
1763
- return this.sendRequest({
1705
+ return this.sendCorrelatedRequest({
1764
1706
  requestId: resolvedRequestId,
1765
1707
  message,
1708
+ responseType: "create_terminal_response",
1766
1709
  timeout: 10000,
1767
1710
  options: { skipQueue: true },
1768
- select: (msg) => {
1769
- if (msg.type !== "create_terminal_response") {
1770
- return null;
1771
- }
1772
- if (msg.payload.requestId !== resolvedRequestId) {
1773
- return null;
1774
- }
1775
- return msg.payload;
1776
- },
1777
1711
  });
1778
1712
  }
1779
1713
  async subscribeTerminal(terminalId, requestId) {
@@ -1783,20 +1717,12 @@ export class DaemonClient {
1783
1717
  terminalId,
1784
1718
  requestId: resolvedRequestId,
1785
1719
  });
1786
- return this.sendRequest({
1720
+ return this.sendCorrelatedRequest({
1787
1721
  requestId: resolvedRequestId,
1788
1722
  message,
1723
+ responseType: "subscribe_terminal_response",
1789
1724
  timeout: 10000,
1790
1725
  options: { skipQueue: true },
1791
- select: (msg) => {
1792
- if (msg.type !== "subscribe_terminal_response") {
1793
- return null;
1794
- }
1795
- if (msg.payload.requestId !== resolvedRequestId) {
1796
- return null;
1797
- }
1798
- return msg.payload;
1799
- },
1800
1726
  });
1801
1727
  }
1802
1728
  unsubscribeTerminal(terminalId) {
@@ -1819,20 +1745,94 @@ export class DaemonClient {
1819
1745
  terminalId,
1820
1746
  requestId: resolvedRequestId,
1821
1747
  });
1822
- return this.sendRequest({
1748
+ return this.sendCorrelatedRequest({
1823
1749
  requestId: resolvedRequestId,
1824
1750
  message,
1751
+ responseType: "kill_terminal_response",
1825
1752
  timeout: 10000,
1826
1753
  options: { skipQueue: true },
1827
- select: (msg) => {
1828
- if (msg.type !== "kill_terminal_response") {
1829
- return null;
1830
- }
1831
- if (msg.payload.requestId !== resolvedRequestId) {
1832
- return null;
1754
+ });
1755
+ }
1756
+ async attachTerminalStream(terminalId, options, requestId) {
1757
+ const resolvedRequestId = this.createRequestId(requestId);
1758
+ const message = SessionInboundMessageSchema.parse({
1759
+ type: "attach_terminal_stream_request",
1760
+ terminalId,
1761
+ requestId: resolvedRequestId,
1762
+ ...(options?.resumeOffset !== undefined ? { resumeOffset: options.resumeOffset } : {}),
1763
+ ...(options?.rows !== undefined ? { rows: options.rows } : {}),
1764
+ ...(options?.cols !== undefined ? { cols: options.cols } : {}),
1765
+ });
1766
+ return this.sendCorrelatedRequest({
1767
+ requestId: resolvedRequestId,
1768
+ message,
1769
+ responseType: "attach_terminal_stream_response",
1770
+ timeout: 10000,
1771
+ options: { skipQueue: true },
1772
+ });
1773
+ }
1774
+ async detachTerminalStream(streamId, requestId) {
1775
+ const resolvedRequestId = this.createRequestId(requestId);
1776
+ const message = SessionInboundMessageSchema.parse({
1777
+ type: "detach_terminal_stream_request",
1778
+ streamId,
1779
+ requestId: resolvedRequestId,
1780
+ });
1781
+ const payload = await this.sendCorrelatedRequest({
1782
+ requestId: resolvedRequestId,
1783
+ message,
1784
+ responseType: "detach_terminal_stream_response",
1785
+ timeout: 10000,
1786
+ options: { skipQueue: true },
1787
+ });
1788
+ this.terminalStreams.clearStream({ streamId });
1789
+ return payload;
1790
+ }
1791
+ onTerminalStreamData(streamId, handler) {
1792
+ return this.terminalStreams.subscribe({ streamId, handler });
1793
+ }
1794
+ async waitForTerminalStreamData(streamId, predicate, timeout = 5000) {
1795
+ return new Promise((resolve, reject) => {
1796
+ const timeoutHandle = setTimeout(() => {
1797
+ unsubscribe();
1798
+ reject(new Error(`Timeout waiting for terminal stream data (${timeout}ms)`));
1799
+ }, timeout);
1800
+ const unsubscribe = this.onTerminalStreamData(streamId, (chunk) => {
1801
+ if (!predicate(chunk)) {
1802
+ return;
1833
1803
  }
1834
- return msg.payload;
1835
- },
1804
+ clearTimeout(timeoutHandle);
1805
+ unsubscribe();
1806
+ resolve(chunk);
1807
+ });
1808
+ });
1809
+ }
1810
+ sendTerminalStreamInput(streamId, data) {
1811
+ const payload = typeof data === "string" ? encodeUtf8String(data) : data;
1812
+ this.sendBinaryFrame({
1813
+ channel: BinaryMuxChannel.Terminal,
1814
+ messageType: TerminalBinaryMessageType.InputUtf8,
1815
+ streamId,
1816
+ offset: 0,
1817
+ payload,
1818
+ });
1819
+ }
1820
+ sendTerminalStreamKey(streamId, input) {
1821
+ const encoded = encodeTerminalKeyInput(input);
1822
+ if (!encoded) {
1823
+ return;
1824
+ }
1825
+ this.sendTerminalStreamInput(streamId, encoded);
1826
+ }
1827
+ sendTerminalStreamAck(streamId, offset) {
1828
+ const normalizedOffset = Math.max(0, Math.floor(offset));
1829
+ this.terminalStreams.noteAck({ streamId, offset: normalizedOffset });
1830
+ this.sendBinaryFrame({
1831
+ channel: BinaryMuxChannel.Terminal,
1832
+ messageType: TerminalBinaryMessageType.Ack,
1833
+ streamId,
1834
+ offset: normalizedOffset,
1835
+ payload: new Uint8Array(0),
1836
1836
  });
1837
1837
  }
1838
1838
  async waitForTerminalOutput(terminalId, timeout = 5000) {
@@ -1883,6 +1883,14 @@ export class DaemonClient {
1883
1883
  const rawData = data && typeof data === "object" && "data" in data
1884
1884
  ? data.data
1885
1885
  : data;
1886
+ const rawBytes = asUint8Array(rawData);
1887
+ if (rawBytes) {
1888
+ const frame = decodeBinaryMuxFrame(rawBytes);
1889
+ if (frame) {
1890
+ this.handleBinaryFrame(frame);
1891
+ return;
1892
+ }
1893
+ }
1886
1894
  const payload = decodeMessageData(rawData);
1887
1895
  if (!payload) {
1888
1896
  return;
@@ -1905,6 +1913,20 @@ export class DaemonClient {
1905
1913
  }
1906
1914
  this.handleSessionMessage(parsed.data.message);
1907
1915
  }
1916
+ handleBinaryFrame(frame) {
1917
+ if (frame.channel === BinaryMuxChannel.Terminal &&
1918
+ frame.messageType === TerminalBinaryMessageType.OutputUtf8) {
1919
+ const chunk = {
1920
+ streamId: frame.streamId,
1921
+ offset: frame.offset,
1922
+ endOffset: frame.offset + (frame.payload?.byteLength ?? 0),
1923
+ replay: Boolean((frame.flags ?? 0) & TerminalBinaryFlags.Replay),
1924
+ data: frame.payload ?? new Uint8Array(0),
1925
+ };
1926
+ this.terminalStreams.receiveChunk({ chunk });
1927
+ return;
1928
+ }
1929
+ }
1908
1930
  updateConnectionState(next) {
1909
1931
  this.connectionState = next;
1910
1932
  for (const listener of this.connectionListeners) {
@@ -1937,6 +1959,7 @@ export class DaemonClient {
1937
1959
  // and responses from the previous connection will never arrive.
1938
1960
  this.clearWaiters(new Error(reason ?? "Connection lost"));
1939
1961
  this.rejectPendingSendQueue(new Error(reason ?? "Connection lost"));
1962
+ this.terminalStreams.clearAll();
1940
1963
  this.updateConnectionState({
1941
1964
  status: "disconnected",
1942
1965
  ...(reason ? { reason } : {}),
@@ -1950,6 +1973,9 @@ export class DaemonClient {
1950
1973
  }, delay);
1951
1974
  }
1952
1975
  handleSessionMessage(msg) {
1976
+ if (msg.type === "terminal_stream_exit") {
1977
+ this.terminalStreams.clearStream({ streamId: msg.payload.streamId });
1978
+ }
1953
1979
  if (this.rawMessageListeners.size > 0) {
1954
1980
  for (const handler of this.rawMessageListeners) {
1955
1981
  try {
@@ -2016,6 +2042,8 @@ export class DaemonClient {
2016
2042
  agentId: msg.payload.agentId,
2017
2043
  event: msg.payload.event,
2018
2044
  timestamp: msg.payload.timestamp,
2045
+ ...(typeof msg.payload.seq === "number" ? { seq: msg.payload.seq } : {}),
2046
+ ...(typeof msg.payload.epoch === "string" ? { epoch: msg.payload.epoch } : {}),
2019
2047
  };
2020
2048
  case "status":
2021
2049
  return { type: "status", payload: msg.payload };
@@ -2101,333 +2129,6 @@ export class DaemonClient {
2101
2129
  return { promise, cancel };
2102
2130
  }
2103
2131
  }
2104
- // ============================================================================
2105
- // Helpers
2106
- // ============================================================================
2107
- function defaultWebSocketFactory(url, _options) {
2108
- const globalWs = globalThis.WebSocket;
2109
- if (!globalWs) {
2110
- throw new Error("WebSocket is not available in this runtime");
2111
- }
2112
- return new globalWs(url);
2113
- }
2114
- function createWebSocketTransportFactory(factory) {
2115
- return ({ url, headers }) => {
2116
- const ws = factory(url, { headers });
2117
- return {
2118
- send: (data) => {
2119
- if (typeof ws.readyState === "number" && ws.readyState !== 1) {
2120
- throw new Error(`WebSocket not open (readyState=${ws.readyState})`);
2121
- }
2122
- ws.send(data);
2123
- },
2124
- close: (code, reason) => ws.close(code, reason),
2125
- onOpen: (handler) => bindWsHandler(ws, "open", handler),
2126
- onClose: (handler) => bindWsHandler(ws, "close", handler),
2127
- onError: (handler) => bindWsHandler(ws, "error", handler),
2128
- onMessage: (handler) => bindWsHandler(ws, "message", handler),
2129
- };
2130
- };
2131
- }
2132
- function createRelayE2eeTransportFactory(args) {
2133
- return ({ url, headers }) => {
2134
- const base = args.baseFactory({ url, headers });
2135
- return createEncryptedTransport(base, args.daemonPublicKeyB64, args.logger);
2136
- };
2137
- }
2138
- function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
2139
- let channel = null;
2140
- let opened = false;
2141
- let closed = false;
2142
- const openHandlers = new Set();
2143
- const closeHandlers = new Set();
2144
- const errorHandlers = new Set();
2145
- const messageHandlers = new Set();
2146
- const emitOpen = () => {
2147
- if (opened || closed)
2148
- return;
2149
- opened = true;
2150
- for (const handler of openHandlers) {
2151
- try {
2152
- handler();
2153
- }
2154
- catch {
2155
- // no-op
2156
- }
2157
- }
2158
- };
2159
- const emitClose = (event) => {
2160
- if (closed)
2161
- return;
2162
- closed = true;
2163
- for (const handler of closeHandlers) {
2164
- try {
2165
- handler(event);
2166
- }
2167
- catch {
2168
- // no-op
2169
- }
2170
- }
2171
- };
2172
- const emitError = (event) => {
2173
- if (closed)
2174
- return;
2175
- for (const handler of errorHandlers) {
2176
- try {
2177
- handler(event);
2178
- }
2179
- catch {
2180
- // no-op
2181
- }
2182
- }
2183
- };
2184
- const emitMessage = (data) => {
2185
- if (closed)
2186
- return;
2187
- for (const handler of messageHandlers) {
2188
- try {
2189
- handler(data);
2190
- }
2191
- catch {
2192
- // no-op
2193
- }
2194
- }
2195
- };
2196
- const relayTransport = {
2197
- send: (data) => {
2198
- if (typeof data === "string") {
2199
- base.send(data);
2200
- return;
2201
- }
2202
- if (data instanceof ArrayBuffer) {
2203
- if (typeof TextDecoder !== "undefined") {
2204
- base.send(new TextDecoder().decode(data));
2205
- return;
2206
- }
2207
- if (typeof Buffer !== "undefined") {
2208
- base.send(Buffer.from(data).toString("utf8"));
2209
- return;
2210
- }
2211
- base.send(String(data));
2212
- return;
2213
- }
2214
- base.send(String(data));
2215
- },
2216
- close: (code, reason) => base.close(code, reason),
2217
- onmessage: null,
2218
- onclose: null,
2219
- onerror: null,
2220
- };
2221
- const startHandshake = async () => {
2222
- try {
2223
- channel = await createClientChannel(relayTransport, daemonPublicKeyB64, {
2224
- onopen: emitOpen,
2225
- onmessage: (data) => emitMessage(data),
2226
- onclose: (code, reason) => emitClose({ code, reason }),
2227
- onerror: (error) => emitError(error),
2228
- });
2229
- }
2230
- catch (error) {
2231
- logger.warn({ err: error }, "relay_e2ee_handshake_failed");
2232
- emitError(error);
2233
- base.close(1011, "E2EE handshake failed");
2234
- }
2235
- };
2236
- base.onOpen(() => {
2237
- void startHandshake();
2238
- });
2239
- base.onMessage((event) => {
2240
- relayTransport.onmessage?.(extractRelayMessageData(event));
2241
- });
2242
- base.onClose((event) => {
2243
- const record = event;
2244
- relayTransport.onclose?.(record?.code ?? 0, record?.reason ?? "");
2245
- emitClose(event);
2246
- });
2247
- base.onError((event) => {
2248
- relayTransport.onerror?.(event instanceof Error ? event : new Error(String(event)));
2249
- emitError(event);
2250
- });
2251
- return {
2252
- send: (data) => {
2253
- if (!channel) {
2254
- throw new Error("Encrypted channel not ready");
2255
- }
2256
- void channel.send(data).catch((error) => {
2257
- emitError(error);
2258
- });
2259
- },
2260
- close: (code, reason) => {
2261
- if (channel) {
2262
- channel.close(code, reason);
2263
- }
2264
- else {
2265
- base.close(code, reason);
2266
- }
2267
- emitClose({ code, reason });
2268
- },
2269
- onMessage: (handler) => {
2270
- messageHandlers.add(handler);
2271
- return () => messageHandlers.delete(handler);
2272
- },
2273
- onOpen: (handler) => {
2274
- openHandlers.add(handler);
2275
- if (opened) {
2276
- try {
2277
- handler();
2278
- }
2279
- catch {
2280
- // no-op
2281
- }
2282
- }
2283
- return () => openHandlers.delete(handler);
2284
- },
2285
- onClose: (handler) => {
2286
- closeHandlers.add(handler);
2287
- if (closed) {
2288
- try {
2289
- handler();
2290
- }
2291
- catch {
2292
- // no-op
2293
- }
2294
- }
2295
- return () => closeHandlers.delete(handler);
2296
- },
2297
- onError: (handler) => {
2298
- errorHandlers.add(handler);
2299
- return () => errorHandlers.delete(handler);
2300
- },
2301
- };
2302
- }
2303
- function extractRelayMessageData(event) {
2304
- const raw = event && typeof event === "object" && "data" in event
2305
- ? event.data
2306
- : event;
2307
- if (typeof raw === "string")
2308
- return raw;
2309
- if (raw instanceof ArrayBuffer)
2310
- return raw;
2311
- if (ArrayBuffer.isView(raw)) {
2312
- const view = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
2313
- const out = new Uint8Array(view.byteLength);
2314
- out.set(view);
2315
- return out.buffer;
2316
- }
2317
- return String(raw ?? "");
2318
- }
2319
- function bindWsHandler(ws, event, handler) {
2320
- if (typeof ws.addEventListener === "function") {
2321
- ws.addEventListener(event, handler);
2322
- return () => {
2323
- if (typeof ws.removeEventListener === "function") {
2324
- ws.removeEventListener(event, handler);
2325
- }
2326
- };
2327
- }
2328
- if (typeof ws.on === "function") {
2329
- ws.on(event, handler);
2330
- return () => {
2331
- if (typeof ws.off === "function") {
2332
- ws.off(event, handler);
2333
- return;
2334
- }
2335
- if (typeof ws.removeListener === "function") {
2336
- ws.removeListener(event, handler);
2337
- }
2338
- };
2339
- }
2340
- const prop = `on${event}`;
2341
- const previous = ws[prop];
2342
- ws[prop] = handler;
2343
- return () => {
2344
- if (ws[prop] === handler) {
2345
- ws[prop] = previous ?? null;
2346
- }
2347
- };
2348
- }
2349
- function describeTransportClose(event) {
2350
- if (!event) {
2351
- return "Transport closed";
2352
- }
2353
- if (event instanceof Error) {
2354
- return event.message;
2355
- }
2356
- if (typeof event === "string") {
2357
- return event;
2358
- }
2359
- if (typeof event === "object") {
2360
- const record = event;
2361
- if (typeof record.reason === "string" && record.reason.trim().length > 0) {
2362
- return record.reason.trim();
2363
- }
2364
- if (typeof record.message === "string" && record.message.trim().length > 0) {
2365
- return record.message.trim();
2366
- }
2367
- if (typeof record.code === "number") {
2368
- return `Transport closed (code ${record.code})`;
2369
- }
2370
- }
2371
- return "Transport closed";
2372
- }
2373
- function describeTransportError(event) {
2374
- if (!event) {
2375
- return "Transport error";
2376
- }
2377
- if (event instanceof Error) {
2378
- return event.message;
2379
- }
2380
- if (typeof event === "string") {
2381
- return event;
2382
- }
2383
- if (typeof event === "object") {
2384
- const record = event;
2385
- if (typeof record.message === "string" && record.message.trim().length > 0) {
2386
- return record.message.trim();
2387
- }
2388
- }
2389
- return "Transport error";
2390
- }
2391
- function safeRandomId() {
2392
- try {
2393
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
2394
- return crypto.randomUUID();
2395
- }
2396
- }
2397
- catch {
2398
- // ignore
2399
- }
2400
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
2401
- }
2402
- function decodeMessageData(data) {
2403
- if (data === null || data === undefined) {
2404
- return null;
2405
- }
2406
- if (typeof data === "string") {
2407
- return data;
2408
- }
2409
- if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
2410
- if (typeof Buffer !== "undefined") {
2411
- return Buffer.from(data).toString("utf8");
2412
- }
2413
- if (typeof TextDecoder !== "undefined") {
2414
- return new TextDecoder().decode(data);
2415
- }
2416
- }
2417
- if (ArrayBuffer.isView(data)) {
2418
- const view = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
2419
- if (typeof Buffer !== "undefined") {
2420
- return Buffer.from(view).toString("utf8");
2421
- }
2422
- if (typeof TextDecoder !== "undefined") {
2423
- return new TextDecoder().decode(view);
2424
- }
2425
- }
2426
- if (typeof data.toString === "function") {
2427
- return data.toString();
2428
- }
2429
- return null;
2430
- }
2431
2132
  function resolveAgentConfig(options) {
2432
2133
  const { config, provider, cwd, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
2433
2134
  const baseConfig = {