@getpaseo/server 0.1.2 → 0.1.4

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 (175) 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 +130 -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 +54 -36
  26. package/dist/server/client/daemon-client.d.ts.map +1 -1
  27. package/dist/server/client/daemon-client.js +497 -800
  28. package/dist/server/client/daemon-client.js.map +1 -1
  29. package/dist/server/server/agent/agent-manager.d.ts +52 -0
  30. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  31. package/dist/server/server/agent/agent-manager.js +238 -10
  32. package/dist/server/server/agent/agent-manager.js.map +1 -1
  33. package/dist/server/server/agent/agent-storage.d.ts +4 -4
  34. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
  35. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +62 -12
  36. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
  37. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +4 -4
  38. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
  39. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +213 -50
  40. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  41. package/dist/server/server/agent/providers/claude-agent.d.ts +7 -0
  42. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  43. package/dist/server/server/agent/providers/claude-agent.js +100 -19
  44. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  45. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +0 -1
  46. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts.map +1 -1
  47. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +67 -30
  48. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -1
  49. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +1 -1
  50. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  51. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +293 -266
  52. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  53. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +1 -1
  54. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  55. package/dist/server/server/agent/providers/codex-app-server-agent.js +49 -14
  56. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  57. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -1
  58. package/dist/server/server/agent/providers/codex-rollout-timeline.js +5 -4
  59. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -1
  60. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -1
  61. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +8 -2
  62. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  63. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +1 -1
  64. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -1
  65. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +121 -45
  66. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -1
  67. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  68. package/dist/server/server/agent/providers/opencode-agent.js +87 -35
  69. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  70. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +2 -2
  71. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  72. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +23 -6
  73. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  74. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +0 -1
  75. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -1
  76. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +0 -10
  77. package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -1
  78. package/dist/server/server/agent/stt-manager.d.ts +3 -2
  79. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  80. package/dist/server/server/agent/stt-manager.js +5 -3
  81. package/dist/server/server/agent/stt-manager.js.map +1 -1
  82. package/dist/server/server/agent/timeline-projection.d.ts +19 -0
  83. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -0
  84. package/dist/server/server/agent/timeline-projection.js +142 -0
  85. package/dist/server/server/agent/timeline-projection.js.map +1 -0
  86. package/dist/server/server/agent/tts-manager.d.ts +3 -2
  87. package/dist/server/server/agent/tts-manager.d.ts.map +1 -1
  88. package/dist/server/server/agent/tts-manager.js +5 -17
  89. package/dist/server/server/agent/tts-manager.js.map +1 -1
  90. package/dist/server/server/agent-attention-policy.d.ts +20 -0
  91. package/dist/server/server/agent-attention-policy.d.ts.map +1 -0
  92. package/dist/server/server/agent-attention-policy.js +40 -0
  93. package/dist/server/server/agent-attention-policy.js.map +1 -0
  94. package/dist/server/server/bootstrap.d.ts.map +1 -1
  95. package/dist/server/server/bootstrap.js +13 -20
  96. package/dist/server/server/bootstrap.js.map +1 -1
  97. package/dist/server/server/dictation/dictation-stream-manager.d.ts +10 -2
  98. package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
  99. package/dist/server/server/dictation/dictation-stream-manager.js +83 -27
  100. package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
  101. package/dist/server/server/exports.d.ts +2 -1
  102. package/dist/server/server/exports.d.ts.map +1 -1
  103. package/dist/server/server/exports.js +1 -0
  104. package/dist/server/server/exports.js.map +1 -1
  105. package/dist/server/server/persisted-config.d.ts +36 -22
  106. package/dist/server/server/persisted-config.d.ts.map +1 -1
  107. package/dist/server/server/persisted-config.js +2 -0
  108. package/dist/server/server/persisted-config.js.map +1 -1
  109. package/dist/server/server/relay-transport.d.ts +3 -2
  110. package/dist/server/server/relay-transport.d.ts.map +1 -1
  111. package/dist/server/server/relay-transport.js +99 -16
  112. package/dist/server/server/relay-transport.js.map +1 -1
  113. package/dist/server/server/session.d.ts +34 -16
  114. package/dist/server/server/session.d.ts.map +1 -1
  115. package/dist/server/server/session.js +619 -328
  116. package/dist/server/server/session.js.map +1 -1
  117. package/dist/server/server/speech/provider-resolver.d.ts +3 -0
  118. package/dist/server/server/speech/provider-resolver.d.ts.map +1 -0
  119. package/dist/server/server/speech/provider-resolver.js +7 -0
  120. package/dist/server/server/speech/provider-resolver.js.map +1 -0
  121. package/dist/server/server/speech/providers/local/config.d.ts.map +1 -1
  122. package/dist/server/server/speech/providers/local/config.js +11 -8
  123. package/dist/server/server/speech/providers/local/config.js.map +1 -1
  124. package/dist/server/server/speech/providers/local/runtime.d.ts +1 -0
  125. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
  126. package/dist/server/server/speech/providers/local/runtime.js +13 -9
  127. package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
  128. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  129. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +73 -54
  130. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  131. package/dist/server/server/speech/providers/openai/config.d.ts.map +1 -1
  132. package/dist/server/server/speech/providers/openai/config.js +10 -5
  133. package/dist/server/server/speech/providers/openai/config.js.map +1 -1
  134. package/dist/server/server/speech/providers/openai/runtime.d.ts.map +1 -1
  135. package/dist/server/server/speech/providers/openai/runtime.js +17 -6
  136. package/dist/server/server/speech/providers/openai/runtime.js.map +1 -1
  137. package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -1
  138. package/dist/server/server/speech/speech-config-resolver.js +25 -4
  139. package/dist/server/server/speech/speech-config-resolver.js.map +1 -1
  140. package/dist/server/server/speech/speech-runtime.d.ts +26 -3
  141. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  142. package/dist/server/server/speech/speech-runtime.js +468 -85
  143. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  144. package/dist/server/server/speech/speech-types.d.ts +3 -0
  145. package/dist/server/server/speech/speech-types.d.ts.map +1 -1
  146. package/dist/server/server/speech/speech-types.js +1 -0
  147. package/dist/server/server/speech/speech-types.js.map +1 -1
  148. package/dist/server/server/websocket-server.d.ts +23 -7
  149. package/dist/server/server/websocket-server.d.ts.map +1 -1
  150. package/dist/server/server/websocket-server.js +288 -102
  151. package/dist/server/server/websocket-server.js.map +1 -1
  152. package/dist/server/shared/binary-mux.d.ts +31 -0
  153. package/dist/server/shared/binary-mux.d.ts.map +1 -0
  154. package/dist/server/shared/binary-mux.js +101 -0
  155. package/dist/server/shared/binary-mux.js.map +1 -0
  156. package/dist/server/shared/messages.d.ts +5206 -4814
  157. package/dist/server/shared/messages.d.ts.map +1 -1
  158. package/dist/server/shared/messages.js +225 -58
  159. package/dist/server/shared/messages.js.map +1 -1
  160. package/dist/server/shared/terminal-key-input.d.ts +9 -0
  161. package/dist/server/shared/terminal-key-input.d.ts.map +1 -0
  162. package/dist/server/shared/terminal-key-input.js +132 -0
  163. package/dist/server/shared/terminal-key-input.js.map +1 -0
  164. package/dist/server/terminal/terminal.d.ts +17 -0
  165. package/dist/server/terminal/terminal.d.ts.map +1 -1
  166. package/dist/server/terminal/terminal.js +89 -0
  167. package/dist/server/terminal/terminal.js.map +1 -1
  168. package/dist/server/utils/checkout-git.d.ts +9 -1
  169. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  170. package/dist/server/utils/checkout-git.js +314 -75
  171. package/dist/server/utils/checkout-git.js.map +1 -1
  172. package/dist/server/utils/worktree.d.ts.map +1 -1
  173. package/dist/server/utils/worktree.js +33 -4
  174. package/dist/server/utils/worktree.js.map +1 -1
  175. 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;
@@ -46,6 +55,17 @@ export class DaemonClient {
46
55
  this.pendingSendQueue = [];
47
56
  this.relayClientId = null;
48
57
  this.logger = config.logger ?? consoleLogger;
58
+ this.terminalStreams = new TerminalStreamManager({
59
+ sendAck: (ack) => {
60
+ this.sendBinaryFrame({
61
+ channel: BinaryMuxChannel.Terminal,
62
+ messageType: TerminalBinaryMessageType.Ack,
63
+ streamId: ack.streamId,
64
+ offset: ack.offset,
65
+ payload: new Uint8Array(0),
66
+ });
67
+ },
68
+ });
49
69
  // Relay requires a clientId so the daemon can create an independent
50
70
  // socket + E2EE channel per connected client. Generate one per DaemonClient
51
71
  // instance (stable across reconnects in this tab/app session).
@@ -235,6 +255,7 @@ export class DaemonClient {
235
255
  }
236
256
  this.disposeTransport(1000, "Client closed");
237
257
  this.clearWaiters(new Error("Daemon client closed"));
258
+ this.terminalStreams.clearAll();
238
259
  this.updateConnectionState({
239
260
  status: "disconnected",
240
261
  reason: "client_closed",
@@ -329,6 +350,23 @@ export class DaemonClient {
329
350
  throw error instanceof Error ? error : new Error(String(error));
330
351
  }
331
352
  }
353
+ sendBinaryFrame(frame) {
354
+ if (!this.transport || this.connectionState.status !== "connected") {
355
+ if (this.config.suppressSendErrors) {
356
+ return;
357
+ }
358
+ throw new Error(`Transport not connected (status: ${this.connectionState.status})`);
359
+ }
360
+ try {
361
+ this.transport.send(encodeBinaryMuxFrame(frame));
362
+ }
363
+ catch (error) {
364
+ if (this.config.suppressSendErrors) {
365
+ return;
366
+ }
367
+ throw error instanceof Error ? error : new Error(String(error));
368
+ }
369
+ }
332
370
  /**
333
371
  * Send a session message for RPC methods that create waiters.
334
372
  * If the connection is still being established ("connecting"), the message
@@ -428,6 +466,43 @@ export class DaemonClient {
428
466
  }
429
467
  return result.value;
430
468
  }
469
+ async sendCorrelatedRequest(params) {
470
+ return this.sendRequest({
471
+ requestId: params.requestId,
472
+ message: params.message,
473
+ timeout: params.timeout,
474
+ options: params.options,
475
+ select: (msg) => {
476
+ const correlated = msg;
477
+ if (correlated.type !== params.responseType) {
478
+ return null;
479
+ }
480
+ const payload = correlated.payload;
481
+ if (payload.requestId !== params.requestId) {
482
+ return null;
483
+ }
484
+ if (!params.selectPayload) {
485
+ return payload;
486
+ }
487
+ return params.selectPayload(payload);
488
+ },
489
+ });
490
+ }
491
+ sendCorrelatedSessionRequest(params) {
492
+ const resolvedRequestId = this.createRequestId(params.requestId);
493
+ const message = SessionInboundMessageSchema.parse({
494
+ ...params.message,
495
+ requestId: resolvedRequestId,
496
+ });
497
+ return this.sendCorrelatedRequest({
498
+ requestId: resolvedRequestId,
499
+ message,
500
+ responseType: params.responseType,
501
+ timeout: params.timeout,
502
+ options: { skipQueue: true },
503
+ ...(params.selectPayload ? { selectPayload: params.selectPayload } : {}),
504
+ });
505
+ }
431
506
  sendSessionMessageStrict(message) {
432
507
  if (!this.transport || this.connectionState.status !== "connected") {
433
508
  throw new Error("Transport not connected");
@@ -621,6 +696,7 @@ export class DaemonClient {
621
696
  requestId,
622
697
  config,
623
698
  ...(options.initialPrompt ? { initialPrompt: options.initialPrompt } : {}),
699
+ ...(options.outputSchema ? { outputSchema: options.outputSchema } : {}),
624
700
  ...(options.images && options.images.length > 0
625
701
  ? { images: options.images }
626
702
  : {}),
@@ -702,6 +778,36 @@ export class DaemonClient {
702
778
  });
703
779
  return { archivedAt: result.archivedAt };
704
780
  }
781
+ async updateAgent(agentId, updates) {
782
+ const requestId = this.createRequestId();
783
+ const message = SessionInboundMessageSchema.parse({
784
+ type: "update_agent_request",
785
+ agentId,
786
+ ...(updates.name !== undefined ? { name: updates.name } : {}),
787
+ ...(updates.labels && Object.keys(updates.labels).length > 0
788
+ ? { labels: updates.labels }
789
+ : {}),
790
+ requestId,
791
+ });
792
+ const payload = await this.sendRequest({
793
+ requestId,
794
+ message,
795
+ timeout: 10000,
796
+ options: { skipQueue: true },
797
+ select: (msg) => {
798
+ if (msg.type !== "update_agent_response") {
799
+ return null;
800
+ }
801
+ if (msg.payload.requestId !== requestId) {
802
+ return null;
803
+ }
804
+ return msg.payload;
805
+ },
806
+ });
807
+ if (!payload.accepted) {
808
+ throw new Error(payload.error ?? "updateAgent rejected");
809
+ }
810
+ }
705
811
  async resumeAgent(handle, overrides) {
706
812
  const requestId = this.createRequestId();
707
813
  const message = SessionInboundMessageSchema.parse({
@@ -752,20 +858,24 @@ export class DaemonClient {
752
858
  },
753
859
  });
754
860
  }
755
- async initializeAgent(agentId, requestId) {
756
- const resolvedRequestId = this.createRequestId(requestId);
861
+ async fetchAgentTimeline(agentId, options = {}) {
862
+ const resolvedRequestId = this.createRequestId(options.requestId);
757
863
  const message = SessionInboundMessageSchema.parse({
758
- type: "initialize_agent_request",
864
+ type: "fetch_agent_timeline_request",
759
865
  agentId,
760
866
  requestId: resolvedRequestId,
867
+ ...(options.direction ? { direction: options.direction } : {}),
868
+ ...(options.cursor ? { cursor: options.cursor } : {}),
869
+ ...(typeof options.limit === "number" ? { limit: options.limit } : {}),
870
+ ...(options.projection ? { projection: options.projection } : {}),
761
871
  });
762
872
  const payload = await this.sendRequest({
763
873
  requestId: resolvedRequestId,
764
874
  message,
765
- timeout: 10000,
875
+ timeout: 15000,
766
876
  options: { skipQueue: true },
767
877
  select: (msg) => {
768
- if (msg.type !== "initialize_agent_request") {
878
+ if (msg.type !== "fetch_agent_timeline_response") {
769
879
  return null;
770
880
  }
771
881
  if (msg.payload.requestId !== resolvedRequestId) {
@@ -777,11 +887,7 @@ export class DaemonClient {
777
887
  if (payload.error) {
778
888
  throw new Error(payload.error);
779
889
  }
780
- const agent = await this.fetchAgent(agentId);
781
- if (!agent) {
782
- throw new Error(`Agent not found after initialize: ${agentId}`);
783
- }
784
- return agent;
890
+ return payload;
785
891
  }
786
892
  // ============================================================================
787
893
  // Agent Interaction
@@ -941,7 +1047,7 @@ export class DaemonClient {
941
1047
  ...(agentId ? { agentId } : {}),
942
1048
  requestId,
943
1049
  });
944
- return this.sendRequest({
1050
+ const response = await this.sendRequest({
945
1051
  requestId,
946
1052
  message,
947
1053
  timeout: 10000,
@@ -955,11 +1061,18 @@ export class DaemonClient {
955
1061
  return msg.payload;
956
1062
  },
957
1063
  });
1064
+ if (!response.accepted) {
1065
+ const codeSuffix = typeof response.reasonCode === "string" && response.reasonCode.trim().length > 0
1066
+ ? ` (${response.reasonCode})`
1067
+ : "";
1068
+ throw new Error((response.error ?? "Failed to set voice mode") + codeSuffix);
1069
+ }
1070
+ return response;
958
1071
  }
959
1072
  async sendVoiceAudioChunk(audio, format, isLast) {
960
1073
  this.sendSessionMessage({ type: "voice_audio_chunk", audio, format, isLast });
961
1074
  }
962
- startDictationStream(dictationId, format) {
1075
+ async startDictationStream(dictationId, format) {
963
1076
  const ack = this.waitForWithCancel((msg) => {
964
1077
  if (msg.type !== "dictation_stream_ack") {
965
1078
  return null;
@@ -985,23 +1098,22 @@ export class DaemonClient {
985
1098
  const errorPromise = streamError.promise.then((payload) => {
986
1099
  throw new Error(payload.error);
987
1100
  });
1101
+ const cleanupError = new Error("Cancelled dictation start waiter");
988
1102
  try {
989
1103
  this.sendSessionMessageStrict({ type: "dictation_stream_start", dictationId, format });
1104
+ await Promise.race([ackPromise, errorPromise]);
990
1105
  }
991
- catch (error) {
992
- const err = error instanceof Error ? error : new Error(String(error));
993
- ack.cancel(err);
994
- streamError.cancel(err);
1106
+ finally {
1107
+ ack.cancel(cleanupError);
1108
+ streamError.cancel(cleanupError);
995
1109
  void ackPromise.catch(() => undefined);
996
1110
  void errorPromise.catch(() => undefined);
997
- throw err;
998
1111
  }
999
- return Promise.race([ackPromise, errorPromise]);
1000
1112
  }
1001
1113
  sendDictationStreamChunk(dictationId, seq, audio, format) {
1002
1114
  this.sendSessionMessageStrict({ type: "dictation_stream_chunk", dictationId, seq, audio, format });
1003
1115
  }
1004
- finishDictationStream(dictationId, finalSeq) {
1116
+ async finishDictationStream(dictationId, finalSeq) {
1005
1117
  const final = this.waitForWithCancel((msg) => {
1006
1118
  if (msg.type !== "dictation_stream_final") {
1007
1119
  return null;
@@ -1010,7 +1122,7 @@ export class DaemonClient {
1010
1122
  return null;
1011
1123
  }
1012
1124
  return msg.payload;
1013
- }, 30000, { skipQueue: true });
1125
+ }, 0, { skipQueue: true });
1014
1126
  const streamError = this.waitForWithCancel((msg) => {
1015
1127
  if (msg.type !== "dictation_stream_error") {
1016
1128
  return null;
@@ -1019,23 +1131,96 @@ export class DaemonClient {
1019
1131
  return null;
1020
1132
  }
1021
1133
  return msg.payload;
1022
- }, 30000, { skipQueue: true });
1134
+ }, 0, { skipQueue: true });
1135
+ const finishAccepted = this.waitForWithCancel((msg) => {
1136
+ if (msg.type !== "dictation_stream_finish_accepted") {
1137
+ return null;
1138
+ }
1139
+ if (msg.payload.dictationId !== dictationId) {
1140
+ return null;
1141
+ }
1142
+ return msg.payload;
1143
+ }, DEFAULT_DICTATION_FINISH_ACCEPT_TIMEOUT_MS, { skipQueue: true });
1023
1144
  const finalPromise = final.promise;
1024
1145
  const errorPromise = streamError.promise.then((payload) => {
1025
1146
  throw new Error(payload.error);
1026
1147
  });
1148
+ const finishAcceptedPromise = finishAccepted.promise;
1149
+ const finalOutcomePromise = finalPromise.then((payload) => ({
1150
+ kind: "final",
1151
+ payload,
1152
+ }));
1153
+ const errorOutcomePromise = errorPromise.then(() => ({
1154
+ kind: "error",
1155
+ error: new Error("Unexpected dictation stream error state"),
1156
+ }), (error) => ({
1157
+ kind: "error",
1158
+ error: error instanceof Error ? error : new Error(String(error)),
1159
+ }));
1160
+ const finishAcceptedOutcomePromise = finishAcceptedPromise.then((payload) => ({ kind: "accepted", payload }), (error) => {
1161
+ if (isWaiterTimeoutError(error)) {
1162
+ return { kind: "accepted_timeout" };
1163
+ }
1164
+ return {
1165
+ kind: "accepted_error",
1166
+ error: error instanceof Error ? error : new Error(String(error)),
1167
+ };
1168
+ });
1169
+ const waitForFinalResult = async (timeoutMs) => {
1170
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1171
+ const outcome = await Promise.race([finalOutcomePromise, errorOutcomePromise]);
1172
+ if (outcome.kind === "error") {
1173
+ throw outcome.error;
1174
+ }
1175
+ return outcome.payload;
1176
+ }
1177
+ let timeoutHandle = null;
1178
+ const timeoutPromise = new Promise((resolve) => {
1179
+ timeoutHandle = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs);
1180
+ });
1181
+ const outcome = await Promise.race([
1182
+ finalOutcomePromise,
1183
+ errorOutcomePromise,
1184
+ timeoutPromise,
1185
+ ]);
1186
+ if (timeoutHandle) {
1187
+ clearTimeout(timeoutHandle);
1188
+ }
1189
+ if (outcome.kind === "timeout") {
1190
+ throw new Error(`Timeout waiting for dictation finalization (${timeoutMs}ms)`);
1191
+ }
1192
+ if (outcome.kind === "error") {
1193
+ throw outcome.error;
1194
+ }
1195
+ return outcome.payload;
1196
+ };
1197
+ const cleanupError = new Error("Cancelled dictation finish waiter");
1027
1198
  try {
1028
1199
  this.sendSessionMessageStrict({ type: "dictation_stream_finish", dictationId, finalSeq });
1200
+ const firstOutcome = await Promise.race([
1201
+ finalOutcomePromise,
1202
+ errorOutcomePromise,
1203
+ finishAcceptedOutcomePromise,
1204
+ ]);
1205
+ if (firstOutcome.kind === "final") {
1206
+ return firstOutcome.payload;
1207
+ }
1208
+ if (firstOutcome.kind === "error") {
1209
+ throw firstOutcome.error;
1210
+ }
1211
+ if (firstOutcome.kind === "accepted") {
1212
+ return await waitForFinalResult(firstOutcome.payload.timeoutMs + DEFAULT_DICTATION_FINISH_TIMEOUT_GRACE_MS);
1213
+ }
1214
+ return await waitForFinalResult(DEFAULT_DICTATION_FINISH_FALLBACK_TIMEOUT_MS);
1029
1215
  }
1030
- catch (error) {
1031
- const err = error instanceof Error ? error : new Error(String(error));
1032
- final.cancel(err);
1033
- streamError.cancel(err);
1216
+ finally {
1217
+ final.cancel(cleanupError);
1218
+ streamError.cancel(cleanupError);
1219
+ finishAccepted.cancel(cleanupError);
1034
1220
  void finalPromise.catch(() => undefined);
1035
1221
  void errorPromise.catch(() => undefined);
1036
- throw err;
1222
+ void finishAcceptedPromise.catch(() => undefined);
1037
1223
  }
1038
- return Promise.race([finalPromise, errorPromise]);
1039
1224
  }
1040
1225
  cancelDictationStream(dictationId) {
1041
1226
  this.sendSessionMessageStrict({ type: "dictation_stream_cancel", dictationId });
@@ -1140,22 +1325,17 @@ export class DaemonClient {
1140
1325
  requestId: resolvedRequestId,
1141
1326
  });
1142
1327
  try {
1143
- return await this.sendRequest({
1328
+ return await this.sendCorrelatedRequest({
1144
1329
  requestId: resolvedRequestId,
1145
1330
  message,
1331
+ responseType: "subscribe_checkout_diff_response",
1146
1332
  timeout: 60000,
1147
1333
  options: { skipQueue: true },
1148
- select: (msg) => {
1149
- if (msg.type !== "subscribe_checkout_diff_response") {
1334
+ selectPayload: (payload) => {
1335
+ if (payload.subscriptionId !== subscriptionId) {
1150
1336
  return null;
1151
1337
  }
1152
- if (msg.payload.requestId !== resolvedRequestId) {
1153
- return null;
1154
- }
1155
- if (msg.payload.subscriptionId !== subscriptionId) {
1156
- return null;
1157
- }
1158
- return msg.payload;
1338
+ return payload;
1159
1339
  },
1160
1340
  });
1161
1341
  }
@@ -1177,465 +1357,238 @@ export class DaemonClient {
1177
1357
  });
1178
1358
  }
1179
1359
  async checkoutCommit(cwd, input, requestId) {
1180
- const resolvedRequestId = this.createRequestId(requestId);
1181
- const message = SessionInboundMessageSchema.parse({
1182
- type: "checkout_commit_request",
1183
- cwd,
1184
- message: input.message,
1185
- addAll: input.addAll,
1186
- requestId: resolvedRequestId,
1187
- });
1188
- return this.sendRequest({
1189
- requestId: resolvedRequestId,
1190
- message,
1191
- timeout: 60000,
1192
- options: { skipQueue: true },
1193
- select: (msg) => {
1194
- if (msg.type !== "checkout_commit_response") {
1195
- return null;
1196
- }
1197
- if (msg.payload.requestId !== resolvedRequestId) {
1198
- return null;
1199
- }
1200
- return msg.payload;
1360
+ return this.sendCorrelatedSessionRequest({
1361
+ requestId,
1362
+ message: {
1363
+ type: "checkout_commit_request",
1364
+ cwd,
1365
+ message: input.message,
1366
+ addAll: input.addAll,
1201
1367
  },
1368
+ responseType: "checkout_commit_response",
1369
+ timeout: 60000,
1202
1370
  });
1203
1371
  }
1204
1372
  async checkoutMerge(cwd, input, requestId) {
1205
- const resolvedRequestId = this.createRequestId(requestId);
1206
- const message = SessionInboundMessageSchema.parse({
1207
- type: "checkout_merge_request",
1208
- cwd,
1209
- baseRef: input.baseRef,
1210
- strategy: input.strategy,
1211
- requireCleanTarget: input.requireCleanTarget,
1212
- requestId: resolvedRequestId,
1213
- });
1214
- return this.sendRequest({
1215
- requestId: resolvedRequestId,
1216
- message,
1217
- timeout: 60000,
1218
- options: { skipQueue: true },
1219
- select: (msg) => {
1220
- if (msg.type !== "checkout_merge_response") {
1221
- return null;
1222
- }
1223
- if (msg.payload.requestId !== resolvedRequestId) {
1224
- return null;
1225
- }
1226
- return msg.payload;
1373
+ return this.sendCorrelatedSessionRequest({
1374
+ requestId,
1375
+ message: {
1376
+ type: "checkout_merge_request",
1377
+ cwd,
1378
+ baseRef: input.baseRef,
1379
+ strategy: input.strategy,
1380
+ requireCleanTarget: input.requireCleanTarget,
1227
1381
  },
1382
+ responseType: "checkout_merge_response",
1383
+ timeout: 60000,
1228
1384
  });
1229
1385
  }
1230
1386
  async checkoutMergeFromBase(cwd, input, requestId) {
1231
- const resolvedRequestId = this.createRequestId(requestId);
1232
- const message = SessionInboundMessageSchema.parse({
1233
- type: "checkout_merge_from_base_request",
1234
- cwd,
1235
- baseRef: input.baseRef,
1236
- requireCleanTarget: input.requireCleanTarget,
1237
- requestId: resolvedRequestId,
1238
- });
1239
- return this.sendRequest({
1240
- requestId: resolvedRequestId,
1241
- message,
1242
- timeout: 60000,
1243
- options: { skipQueue: true },
1244
- select: (msg) => {
1245
- if (msg.type !== "checkout_merge_from_base_response") {
1246
- return null;
1247
- }
1248
- if (msg.payload.requestId !== resolvedRequestId) {
1249
- return null;
1250
- }
1251
- return msg.payload;
1387
+ return this.sendCorrelatedSessionRequest({
1388
+ requestId,
1389
+ message: {
1390
+ type: "checkout_merge_from_base_request",
1391
+ cwd,
1392
+ baseRef: input.baseRef,
1393
+ requireCleanTarget: input.requireCleanTarget,
1252
1394
  },
1395
+ responseType: "checkout_merge_from_base_response",
1396
+ timeout: 60000,
1253
1397
  });
1254
1398
  }
1255
1399
  async checkoutPush(cwd, requestId) {
1256
- const resolvedRequestId = this.createRequestId(requestId);
1257
- const message = SessionInboundMessageSchema.parse({
1258
- type: "checkout_push_request",
1259
- cwd,
1260
- requestId: resolvedRequestId,
1261
- });
1262
- return this.sendRequest({
1263
- requestId: resolvedRequestId,
1264
- message,
1265
- timeout: 60000,
1266
- options: { skipQueue: true },
1267
- select: (msg) => {
1268
- if (msg.type !== "checkout_push_response") {
1269
- return null;
1270
- }
1271
- if (msg.payload.requestId !== resolvedRequestId) {
1272
- return null;
1273
- }
1274
- return msg.payload;
1400
+ return this.sendCorrelatedSessionRequest({
1401
+ requestId,
1402
+ message: {
1403
+ type: "checkout_push_request",
1404
+ cwd,
1275
1405
  },
1406
+ responseType: "checkout_push_response",
1407
+ timeout: 60000,
1276
1408
  });
1277
1409
  }
1278
1410
  async checkoutPrCreate(cwd, input, requestId) {
1279
- const resolvedRequestId = this.createRequestId(requestId);
1280
- const message = SessionInboundMessageSchema.parse({
1281
- type: "checkout_pr_create_request",
1282
- cwd,
1283
- title: input.title,
1284
- body: input.body,
1285
- baseRef: input.baseRef,
1286
- requestId: resolvedRequestId,
1287
- });
1288
- return this.sendRequest({
1289
- requestId: resolvedRequestId,
1290
- message,
1291
- timeout: 60000,
1292
- options: { skipQueue: true },
1293
- select: (msg) => {
1294
- if (msg.type !== "checkout_pr_create_response") {
1295
- return null;
1296
- }
1297
- if (msg.payload.requestId !== resolvedRequestId) {
1298
- return null;
1299
- }
1300
- return msg.payload;
1411
+ return this.sendCorrelatedSessionRequest({
1412
+ requestId,
1413
+ message: {
1414
+ type: "checkout_pr_create_request",
1415
+ cwd,
1416
+ title: input.title,
1417
+ body: input.body,
1418
+ baseRef: input.baseRef,
1301
1419
  },
1420
+ responseType: "checkout_pr_create_response",
1421
+ timeout: 60000,
1302
1422
  });
1303
1423
  }
1304
1424
  async checkoutPrStatus(cwd, requestId) {
1305
- const resolvedRequestId = this.createRequestId(requestId);
1306
- const message = SessionInboundMessageSchema.parse({
1307
- type: "checkout_pr_status_request",
1308
- cwd,
1309
- requestId: resolvedRequestId,
1310
- });
1311
- return this.sendRequest({
1312
- requestId: resolvedRequestId,
1313
- message,
1314
- timeout: 60000,
1315
- options: { skipQueue: true },
1316
- select: (msg) => {
1317
- if (msg.type !== "checkout_pr_status_response") {
1318
- return null;
1319
- }
1320
- if (msg.payload.requestId !== resolvedRequestId) {
1321
- return null;
1322
- }
1323
- return msg.payload;
1425
+ return this.sendCorrelatedSessionRequest({
1426
+ requestId,
1427
+ message: {
1428
+ type: "checkout_pr_status_request",
1429
+ cwd,
1324
1430
  },
1431
+ responseType: "checkout_pr_status_response",
1432
+ timeout: 60000,
1325
1433
  });
1326
1434
  }
1327
1435
  async getPaseoWorktreeList(input, requestId) {
1328
- const resolvedRequestId = this.createRequestId(requestId);
1329
- const message = SessionInboundMessageSchema.parse({
1330
- type: "paseo_worktree_list_request",
1331
- cwd: input.cwd,
1332
- repoRoot: input.repoRoot,
1333
- requestId: resolvedRequestId,
1334
- });
1335
- return this.sendRequest({
1336
- requestId: resolvedRequestId,
1337
- message,
1338
- timeout: 60000,
1339
- options: { skipQueue: true },
1340
- select: (msg) => {
1341
- if (msg.type !== "paseo_worktree_list_response") {
1342
- return null;
1343
- }
1344
- if (msg.payload.requestId !== resolvedRequestId) {
1345
- return null;
1346
- }
1347
- return msg.payload;
1436
+ return this.sendCorrelatedSessionRequest({
1437
+ requestId,
1438
+ message: {
1439
+ type: "paseo_worktree_list_request",
1440
+ cwd: input.cwd,
1441
+ repoRoot: input.repoRoot,
1348
1442
  },
1443
+ responseType: "paseo_worktree_list_response",
1444
+ timeout: 60000,
1349
1445
  });
1350
1446
  }
1351
1447
  async archivePaseoWorktree(input, requestId) {
1352
- const resolvedRequestId = this.createRequestId(requestId);
1353
- const message = SessionInboundMessageSchema.parse({
1354
- type: "paseo_worktree_archive_request",
1355
- worktreePath: input.worktreePath,
1356
- repoRoot: input.repoRoot,
1357
- branchName: input.branchName,
1358
- requestId: resolvedRequestId,
1359
- });
1360
- return this.sendRequest({
1361
- requestId: resolvedRequestId,
1362
- message,
1448
+ return this.sendCorrelatedSessionRequest({
1449
+ requestId,
1450
+ message: {
1451
+ type: "paseo_worktree_archive_request",
1452
+ worktreePath: input.worktreePath,
1453
+ repoRoot: input.repoRoot,
1454
+ branchName: input.branchName,
1455
+ },
1456
+ responseType: "paseo_worktree_archive_response",
1363
1457
  timeout: 20000,
1364
- options: { skipQueue: true },
1365
- select: (msg) => {
1366
- if (msg.type !== "paseo_worktree_archive_response") {
1367
- return null;
1368
- }
1369
- if (msg.payload.requestId !== resolvedRequestId) {
1370
- return null;
1371
- }
1372
- return msg.payload;
1458
+ });
1459
+ }
1460
+ async validateBranch(options, requestId) {
1461
+ return this.sendCorrelatedSessionRequest({
1462
+ requestId,
1463
+ message: {
1464
+ type: "validate_branch_request",
1465
+ cwd: options.cwd,
1466
+ branchName: options.branchName,
1373
1467
  },
1468
+ responseType: "validate_branch_response",
1469
+ timeout: 10000,
1374
1470
  });
1375
1471
  }
1376
- async getGitDiff(agentId, requestId) {
1377
- const resolvedRequestId = this.createRequestId(requestId);
1378
- const message = SessionInboundMessageSchema.parse({
1379
- type: "git_diff_request",
1380
- agentId,
1381
- requestId: resolvedRequestId,
1382
- });
1383
- return this.sendRequest({
1384
- requestId: resolvedRequestId,
1385
- message,
1386
- timeout: 10000,
1387
- options: { skipQueue: true },
1388
- select: (msg) => {
1389
- if (msg.type !== "git_diff_response") {
1390
- return null;
1391
- }
1392
- if (msg.payload.requestId !== resolvedRequestId) {
1393
- return null;
1394
- }
1395
- return msg.payload;
1472
+ async getBranchSuggestions(options, requestId) {
1473
+ return this.sendCorrelatedSessionRequest({
1474
+ requestId,
1475
+ message: {
1476
+ type: "branch_suggestions_request",
1477
+ cwd: options.cwd,
1478
+ query: options.query,
1479
+ limit: options.limit,
1396
1480
  },
1397
- });
1398
- }
1399
- async getHighlightedDiff(agentId, requestId) {
1400
- const resolvedRequestId = this.createRequestId(requestId);
1401
- const message = SessionInboundMessageSchema.parse({
1402
- type: "highlighted_diff_request",
1403
- agentId,
1404
- requestId: resolvedRequestId,
1405
- });
1406
- return this.sendRequest({
1407
- requestId: resolvedRequestId,
1408
- message,
1481
+ responseType: "branch_suggestions_response",
1409
1482
  timeout: 10000,
1410
- options: { skipQueue: true },
1411
- select: (msg) => {
1412
- if (msg.type !== "highlighted_diff_response") {
1413
- return null;
1414
- }
1415
- if (msg.payload.requestId !== resolvedRequestId) {
1416
- return null;
1417
- }
1418
- return msg.payload;
1419
- },
1420
- });
1421
- }
1422
- async validateBranch(options, requestId) {
1423
- const resolvedRequestId = this.createRequestId(requestId);
1424
- const message = SessionInboundMessageSchema.parse({
1425
- type: "validate_branch_request",
1426
- cwd: options.cwd,
1427
- branchName: options.branchName,
1428
- requestId: resolvedRequestId,
1429
- });
1430
- return this.sendRequest({
1431
- requestId: resolvedRequestId,
1432
- message,
1433
- timeout: 10000,
1434
- options: { skipQueue: true },
1435
- select: (msg) => {
1436
- if (msg.type !== "validate_branch_response") {
1437
- return null;
1438
- }
1439
- if (msg.payload.requestId !== resolvedRequestId) {
1440
- return null;
1441
- }
1442
- return msg.payload;
1443
- },
1444
1483
  });
1445
1484
  }
1446
1485
  // ============================================================================
1447
1486
  // File Explorer
1448
1487
  // ============================================================================
1449
1488
  async exploreFileSystem(agentId, path, mode = "list", requestId) {
1450
- const resolvedRequestId = this.createRequestId(requestId);
1451
- const message = SessionInboundMessageSchema.parse({
1452
- type: "file_explorer_request",
1453
- agentId,
1454
- path,
1455
- mode,
1456
- requestId: resolvedRequestId,
1457
- });
1458
- return this.sendRequest({
1459
- requestId: resolvedRequestId,
1460
- message,
1461
- timeout: 10000,
1462
- options: { skipQueue: true },
1463
- select: (msg) => {
1464
- if (msg.type !== "file_explorer_response") {
1465
- return null;
1466
- }
1467
- if (msg.payload.requestId !== resolvedRequestId) {
1468
- return null;
1469
- }
1470
- return msg.payload;
1489
+ return this.sendCorrelatedSessionRequest({
1490
+ requestId,
1491
+ message: {
1492
+ type: "file_explorer_request",
1493
+ agentId,
1494
+ path,
1495
+ mode,
1471
1496
  },
1497
+ responseType: "file_explorer_response",
1498
+ timeout: 10000,
1472
1499
  });
1473
1500
  }
1474
1501
  async requestDownloadToken(agentId, path, requestId) {
1475
- const resolvedRequestId = this.createRequestId(requestId);
1476
- const message = SessionInboundMessageSchema.parse({
1477
- type: "file_download_token_request",
1478
- agentId,
1479
- path,
1480
- requestId: resolvedRequestId,
1481
- });
1482
- return this.sendRequest({
1483
- requestId: resolvedRequestId,
1484
- message,
1485
- timeout: 10000,
1486
- options: { skipQueue: true },
1487
- select: (msg) => {
1488
- if (msg.type !== "file_download_token_response") {
1489
- return null;
1490
- }
1491
- if (msg.payload.requestId !== resolvedRequestId) {
1492
- return null;
1493
- }
1494
- return msg.payload;
1502
+ return this.sendCorrelatedSessionRequest({
1503
+ requestId,
1504
+ message: {
1505
+ type: "file_download_token_request",
1506
+ agentId,
1507
+ path,
1495
1508
  },
1509
+ responseType: "file_download_token_response",
1510
+ timeout: 10000,
1496
1511
  });
1497
1512
  }
1498
1513
  async requestProjectIcon(cwd, requestId) {
1499
- const resolvedRequestId = this.createRequestId(requestId);
1500
- const message = SessionInboundMessageSchema.parse({
1501
- type: "project_icon_request",
1502
- cwd,
1503
- requestId: resolvedRequestId,
1504
- });
1505
- return this.sendRequest({
1506
- requestId: resolvedRequestId,
1507
- message,
1508
- timeout: 10000,
1509
- options: { skipQueue: true },
1510
- select: (msg) => {
1511
- if (msg.type !== "project_icon_response") {
1512
- return null;
1513
- }
1514
- if (msg.payload.requestId !== resolvedRequestId) {
1515
- return null;
1516
- }
1517
- return msg.payload;
1514
+ return this.sendCorrelatedSessionRequest({
1515
+ requestId,
1516
+ message: {
1517
+ type: "project_icon_request",
1518
+ cwd,
1518
1519
  },
1520
+ responseType: "project_icon_response",
1521
+ timeout: 10000,
1519
1522
  });
1520
1523
  }
1521
1524
  // ============================================================================
1522
1525
  // Provider Models / Commands
1523
1526
  // ============================================================================
1524
1527
  async listProviderModels(provider, options) {
1525
- const resolvedRequestId = this.createRequestId(options?.requestId);
1526
- const message = SessionInboundMessageSchema.parse({
1527
- type: "list_provider_models_request",
1528
- provider,
1529
- cwd: options?.cwd,
1530
- requestId: resolvedRequestId,
1531
- });
1532
- return this.sendRequest({
1533
- requestId: resolvedRequestId,
1534
- message,
1528
+ return this.sendCorrelatedSessionRequest({
1529
+ requestId: options?.requestId,
1530
+ message: {
1531
+ type: "list_provider_models_request",
1532
+ provider,
1533
+ cwd: options?.cwd,
1534
+ },
1535
+ responseType: "list_provider_models_response",
1535
1536
  timeout: 30000,
1536
- options: { skipQueue: true },
1537
- select: (msg) => {
1538
- if (msg.type !== "list_provider_models_response") {
1539
- return null;
1540
- }
1541
- if (msg.payload.requestId !== resolvedRequestId) {
1542
- return null;
1543
- }
1544
- return msg.payload;
1537
+ });
1538
+ }
1539
+ async listAvailableProviders(options) {
1540
+ return this.sendCorrelatedSessionRequest({
1541
+ requestId: options?.requestId,
1542
+ message: {
1543
+ type: "list_available_providers_request",
1545
1544
  },
1545
+ responseType: "list_available_providers_response",
1546
+ timeout: 30000,
1546
1547
  });
1547
1548
  }
1548
1549
  async listSpeechModels(requestId) {
1549
- const resolvedRequestId = this.createRequestId(requestId);
1550
- const message = SessionInboundMessageSchema.parse({
1551
- type: "speech_models_list_request",
1552
- requestId: resolvedRequestId,
1553
- });
1554
- return this.sendRequest({
1555
- requestId: resolvedRequestId,
1556
- message,
1557
- timeout: 30000,
1558
- options: { skipQueue: true },
1559
- select: (msg) => {
1560
- if (msg.type !== "speech_models_list_response") {
1561
- return null;
1562
- }
1563
- if (msg.payload.requestId !== resolvedRequestId) {
1564
- return null;
1565
- }
1566
- return msg.payload;
1550
+ return this.sendCorrelatedSessionRequest({
1551
+ requestId,
1552
+ message: {
1553
+ type: "speech_models_list_request",
1567
1554
  },
1555
+ responseType: "speech_models_list_response",
1556
+ timeout: 30000,
1568
1557
  });
1569
1558
  }
1570
1559
  async downloadSpeechModels(options) {
1571
- const resolvedRequestId = this.createRequestId(options?.requestId);
1572
- const message = SessionInboundMessageSchema.parse({
1573
- type: "speech_models_download_request",
1574
- modelIds: options?.modelIds,
1575
- requestId: resolvedRequestId,
1576
- });
1577
- return this.sendRequest({
1578
- requestId: resolvedRequestId,
1579
- message,
1580
- timeout: 30 * 60 * 1000,
1581
- options: { skipQueue: true },
1582
- select: (msg) => {
1583
- if (msg.type !== "speech_models_download_response") {
1584
- return null;
1585
- }
1586
- if (msg.payload.requestId !== resolvedRequestId) {
1587
- return null;
1588
- }
1589
- return msg.payload;
1560
+ return this.sendCorrelatedSessionRequest({
1561
+ requestId: options?.requestId,
1562
+ message: {
1563
+ type: "speech_models_download_request",
1564
+ modelIds: options?.modelIds,
1590
1565
  },
1566
+ responseType: "speech_models_download_response",
1567
+ timeout: 30 * 60 * 1000,
1591
1568
  });
1592
1569
  }
1593
1570
  async listCommands(agentId, requestId) {
1594
- const resolvedRequestId = this.createRequestId(requestId);
1595
- const message = SessionInboundMessageSchema.parse({
1596
- type: "list_commands_request",
1597
- agentId,
1598
- requestId: resolvedRequestId,
1599
- });
1600
- return this.sendRequest({
1601
- requestId: resolvedRequestId,
1602
- message,
1603
- timeout: 30000,
1604
- options: { skipQueue: true },
1605
- select: (msg) => {
1606
- if (msg.type !== "list_commands_response") {
1607
- return null;
1608
- }
1609
- if (msg.payload.requestId !== resolvedRequestId) {
1610
- return null;
1611
- }
1612
- return msg.payload;
1571
+ return this.sendCorrelatedSessionRequest({
1572
+ requestId,
1573
+ message: {
1574
+ type: "list_commands_request",
1575
+ agentId,
1613
1576
  },
1577
+ responseType: "list_commands_response",
1578
+ timeout: 30000,
1614
1579
  });
1615
1580
  }
1616
1581
  async executeCommand(agentId, commandName, args, requestId) {
1617
- const resolvedRequestId = this.createRequestId(requestId);
1618
- const message = SessionInboundMessageSchema.parse({
1619
- type: "execute_command_request",
1620
- agentId,
1621
- commandName,
1622
- args,
1623
- requestId: resolvedRequestId,
1624
- });
1625
- return this.sendRequest({
1626
- requestId: resolvedRequestId,
1627
- message,
1628
- timeout: 30000,
1629
- options: { skipQueue: true },
1630
- select: (msg) => {
1631
- if (msg.type !== "execute_command_response") {
1632
- return null;
1633
- }
1634
- if (msg.payload.requestId !== resolvedRequestId) {
1635
- return null;
1636
- }
1637
- return msg.payload;
1582
+ return this.sendCorrelatedSessionRequest({
1583
+ requestId,
1584
+ message: {
1585
+ type: "execute_command_request",
1586
+ agentId,
1587
+ commandName,
1588
+ args,
1638
1589
  },
1590
+ responseType: "execute_command_response",
1591
+ timeout: 30000,
1639
1592
  });
1640
1593
  }
1641
1594
  // ============================================================================
@@ -1697,25 +1650,18 @@ export class DaemonClient {
1697
1650
  agentId,
1698
1651
  timeoutMs: timeout,
1699
1652
  });
1700
- const payload = await this.sendRequest({
1653
+ const payload = await this.sendCorrelatedRequest({
1701
1654
  requestId,
1702
1655
  message,
1656
+ responseType: "wait_for_finish_response",
1703
1657
  timeout: timeout + 5000,
1704
1658
  options: { skipQueue: true },
1705
- select: (msg) => {
1706
- if (msg.type !== "wait_for_finish_response") {
1707
- return null;
1708
- }
1709
- if (msg.payload.requestId !== requestId) {
1710
- return null;
1711
- }
1712
- return msg.payload;
1713
- },
1714
1659
  });
1715
1660
  return {
1716
1661
  status: payload.status,
1717
1662
  final: payload.final,
1718
1663
  error: payload.error,
1664
+ lastMessage: payload.lastMessage,
1719
1665
  };
1720
1666
  }
1721
1667
  // ============================================================================
@@ -1728,20 +1674,12 @@ export class DaemonClient {
1728
1674
  cwd,
1729
1675
  requestId: resolvedRequestId,
1730
1676
  });
1731
- return this.sendRequest({
1677
+ return this.sendCorrelatedRequest({
1732
1678
  requestId: resolvedRequestId,
1733
1679
  message,
1680
+ responseType: "list_terminals_response",
1734
1681
  timeout: 10000,
1735
1682
  options: { skipQueue: true },
1736
- select: (msg) => {
1737
- if (msg.type !== "list_terminals_response") {
1738
- return null;
1739
- }
1740
- if (msg.payload.requestId !== resolvedRequestId) {
1741
- return null;
1742
- }
1743
- return msg.payload;
1744
- },
1745
1683
  });
1746
1684
  }
1747
1685
  async createTerminal(cwd, name, requestId) {
@@ -1752,20 +1690,12 @@ export class DaemonClient {
1752
1690
  name,
1753
1691
  requestId: resolvedRequestId,
1754
1692
  });
1755
- return this.sendRequest({
1693
+ return this.sendCorrelatedRequest({
1756
1694
  requestId: resolvedRequestId,
1757
1695
  message,
1696
+ responseType: "create_terminal_response",
1758
1697
  timeout: 10000,
1759
1698
  options: { skipQueue: true },
1760
- select: (msg) => {
1761
- if (msg.type !== "create_terminal_response") {
1762
- return null;
1763
- }
1764
- if (msg.payload.requestId !== resolvedRequestId) {
1765
- return null;
1766
- }
1767
- return msg.payload;
1768
- },
1769
1699
  });
1770
1700
  }
1771
1701
  async subscribeTerminal(terminalId, requestId) {
@@ -1775,20 +1705,12 @@ export class DaemonClient {
1775
1705
  terminalId,
1776
1706
  requestId: resolvedRequestId,
1777
1707
  });
1778
- return this.sendRequest({
1708
+ return this.sendCorrelatedRequest({
1779
1709
  requestId: resolvedRequestId,
1780
1710
  message,
1711
+ responseType: "subscribe_terminal_response",
1781
1712
  timeout: 10000,
1782
1713
  options: { skipQueue: true },
1783
- select: (msg) => {
1784
- if (msg.type !== "subscribe_terminal_response") {
1785
- return null;
1786
- }
1787
- if (msg.payload.requestId !== resolvedRequestId) {
1788
- return null;
1789
- }
1790
- return msg.payload;
1791
- },
1792
1714
  });
1793
1715
  }
1794
1716
  unsubscribeTerminal(terminalId) {
@@ -1811,20 +1733,94 @@ export class DaemonClient {
1811
1733
  terminalId,
1812
1734
  requestId: resolvedRequestId,
1813
1735
  });
1814
- return this.sendRequest({
1736
+ return this.sendCorrelatedRequest({
1815
1737
  requestId: resolvedRequestId,
1816
1738
  message,
1739
+ responseType: "kill_terminal_response",
1817
1740
  timeout: 10000,
1818
1741
  options: { skipQueue: true },
1819
- select: (msg) => {
1820
- if (msg.type !== "kill_terminal_response") {
1821
- return null;
1822
- }
1823
- if (msg.payload.requestId !== resolvedRequestId) {
1824
- return null;
1742
+ });
1743
+ }
1744
+ async attachTerminalStream(terminalId, options, requestId) {
1745
+ const resolvedRequestId = this.createRequestId(requestId);
1746
+ const message = SessionInboundMessageSchema.parse({
1747
+ type: "attach_terminal_stream_request",
1748
+ terminalId,
1749
+ requestId: resolvedRequestId,
1750
+ ...(options?.resumeOffset !== undefined ? { resumeOffset: options.resumeOffset } : {}),
1751
+ ...(options?.rows !== undefined ? { rows: options.rows } : {}),
1752
+ ...(options?.cols !== undefined ? { cols: options.cols } : {}),
1753
+ });
1754
+ return this.sendCorrelatedRequest({
1755
+ requestId: resolvedRequestId,
1756
+ message,
1757
+ responseType: "attach_terminal_stream_response",
1758
+ timeout: 10000,
1759
+ options: { skipQueue: true },
1760
+ });
1761
+ }
1762
+ async detachTerminalStream(streamId, requestId) {
1763
+ const resolvedRequestId = this.createRequestId(requestId);
1764
+ const message = SessionInboundMessageSchema.parse({
1765
+ type: "detach_terminal_stream_request",
1766
+ streamId,
1767
+ requestId: resolvedRequestId,
1768
+ });
1769
+ const payload = await this.sendCorrelatedRequest({
1770
+ requestId: resolvedRequestId,
1771
+ message,
1772
+ responseType: "detach_terminal_stream_response",
1773
+ timeout: 10000,
1774
+ options: { skipQueue: true },
1775
+ });
1776
+ this.terminalStreams.clearStream({ streamId });
1777
+ return payload;
1778
+ }
1779
+ onTerminalStreamData(streamId, handler) {
1780
+ return this.terminalStreams.subscribe({ streamId, handler });
1781
+ }
1782
+ async waitForTerminalStreamData(streamId, predicate, timeout = 5000) {
1783
+ return new Promise((resolve, reject) => {
1784
+ const timeoutHandle = setTimeout(() => {
1785
+ unsubscribe();
1786
+ reject(new Error(`Timeout waiting for terminal stream data (${timeout}ms)`));
1787
+ }, timeout);
1788
+ const unsubscribe = this.onTerminalStreamData(streamId, (chunk) => {
1789
+ if (!predicate(chunk)) {
1790
+ return;
1825
1791
  }
1826
- return msg.payload;
1827
- },
1792
+ clearTimeout(timeoutHandle);
1793
+ unsubscribe();
1794
+ resolve(chunk);
1795
+ });
1796
+ });
1797
+ }
1798
+ sendTerminalStreamInput(streamId, data) {
1799
+ const payload = typeof data === "string" ? encodeUtf8String(data) : data;
1800
+ this.sendBinaryFrame({
1801
+ channel: BinaryMuxChannel.Terminal,
1802
+ messageType: TerminalBinaryMessageType.InputUtf8,
1803
+ streamId,
1804
+ offset: 0,
1805
+ payload,
1806
+ });
1807
+ }
1808
+ sendTerminalStreamKey(streamId, input) {
1809
+ const encoded = encodeTerminalKeyInput(input);
1810
+ if (!encoded) {
1811
+ return;
1812
+ }
1813
+ this.sendTerminalStreamInput(streamId, encoded);
1814
+ }
1815
+ sendTerminalStreamAck(streamId, offset) {
1816
+ const normalizedOffset = Math.max(0, Math.floor(offset));
1817
+ this.terminalStreams.noteAck({ streamId, offset: normalizedOffset });
1818
+ this.sendBinaryFrame({
1819
+ channel: BinaryMuxChannel.Terminal,
1820
+ messageType: TerminalBinaryMessageType.Ack,
1821
+ streamId,
1822
+ offset: normalizedOffset,
1823
+ payload: new Uint8Array(0),
1828
1824
  });
1829
1825
  }
1830
1826
  async waitForTerminalOutput(terminalId, timeout = 5000) {
@@ -1875,6 +1871,14 @@ export class DaemonClient {
1875
1871
  const rawData = data && typeof data === "object" && "data" in data
1876
1872
  ? data.data
1877
1873
  : data;
1874
+ const rawBytes = asUint8Array(rawData);
1875
+ if (rawBytes) {
1876
+ const frame = decodeBinaryMuxFrame(rawBytes);
1877
+ if (frame) {
1878
+ this.handleBinaryFrame(frame);
1879
+ return;
1880
+ }
1881
+ }
1878
1882
  const payload = decodeMessageData(rawData);
1879
1883
  if (!payload) {
1880
1884
  return;
@@ -1897,6 +1901,20 @@ export class DaemonClient {
1897
1901
  }
1898
1902
  this.handleSessionMessage(parsed.data.message);
1899
1903
  }
1904
+ handleBinaryFrame(frame) {
1905
+ if (frame.channel === BinaryMuxChannel.Terminal &&
1906
+ frame.messageType === TerminalBinaryMessageType.OutputUtf8) {
1907
+ const chunk = {
1908
+ streamId: frame.streamId,
1909
+ offset: frame.offset,
1910
+ endOffset: frame.offset + (frame.payload?.byteLength ?? 0),
1911
+ replay: Boolean((frame.flags ?? 0) & TerminalBinaryFlags.Replay),
1912
+ data: frame.payload ?? new Uint8Array(0),
1913
+ };
1914
+ this.terminalStreams.receiveChunk({ chunk });
1915
+ return;
1916
+ }
1917
+ }
1900
1918
  updateConnectionState(next) {
1901
1919
  this.connectionState = next;
1902
1920
  for (const listener of this.connectionListeners) {
@@ -1929,6 +1947,7 @@ export class DaemonClient {
1929
1947
  // and responses from the previous connection will never arrive.
1930
1948
  this.clearWaiters(new Error(reason ?? "Connection lost"));
1931
1949
  this.rejectPendingSendQueue(new Error(reason ?? "Connection lost"));
1950
+ this.terminalStreams.clearAll();
1932
1951
  this.updateConnectionState({
1933
1952
  status: "disconnected",
1934
1953
  ...(reason ? { reason } : {}),
@@ -1942,6 +1961,9 @@ export class DaemonClient {
1942
1961
  }, delay);
1943
1962
  }
1944
1963
  handleSessionMessage(msg) {
1964
+ if (msg.type === "terminal_stream_exit") {
1965
+ this.terminalStreams.clearStream({ streamId: msg.payload.streamId });
1966
+ }
1945
1967
  if (this.rawMessageListeners.size > 0) {
1946
1968
  for (const handler of this.rawMessageListeners) {
1947
1969
  try {
@@ -2008,6 +2030,8 @@ export class DaemonClient {
2008
2030
  agentId: msg.payload.agentId,
2009
2031
  event: msg.payload.event,
2010
2032
  timestamp: msg.payload.timestamp,
2033
+ ...(typeof msg.payload.seq === "number" ? { seq: msg.payload.seq } : {}),
2034
+ ...(typeof msg.payload.epoch === "string" ? { epoch: msg.payload.epoch } : {}),
2011
2035
  };
2012
2036
  case "status":
2013
2037
  return { type: "status", payload: msg.payload };
@@ -2093,333 +2117,6 @@ export class DaemonClient {
2093
2117
  return { promise, cancel };
2094
2118
  }
2095
2119
  }
2096
- // ============================================================================
2097
- // Helpers
2098
- // ============================================================================
2099
- function defaultWebSocketFactory(url, _options) {
2100
- const globalWs = globalThis.WebSocket;
2101
- if (!globalWs) {
2102
- throw new Error("WebSocket is not available in this runtime");
2103
- }
2104
- return new globalWs(url);
2105
- }
2106
- function createWebSocketTransportFactory(factory) {
2107
- return ({ url, headers }) => {
2108
- const ws = factory(url, { headers });
2109
- return {
2110
- send: (data) => {
2111
- if (typeof ws.readyState === "number" && ws.readyState !== 1) {
2112
- throw new Error(`WebSocket not open (readyState=${ws.readyState})`);
2113
- }
2114
- ws.send(data);
2115
- },
2116
- close: (code, reason) => ws.close(code, reason),
2117
- onOpen: (handler) => bindWsHandler(ws, "open", handler),
2118
- onClose: (handler) => bindWsHandler(ws, "close", handler),
2119
- onError: (handler) => bindWsHandler(ws, "error", handler),
2120
- onMessage: (handler) => bindWsHandler(ws, "message", handler),
2121
- };
2122
- };
2123
- }
2124
- function createRelayE2eeTransportFactory(args) {
2125
- return ({ url, headers }) => {
2126
- const base = args.baseFactory({ url, headers });
2127
- return createEncryptedTransport(base, args.daemonPublicKeyB64, args.logger);
2128
- };
2129
- }
2130
- function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
2131
- let channel = null;
2132
- let opened = false;
2133
- let closed = false;
2134
- const openHandlers = new Set();
2135
- const closeHandlers = new Set();
2136
- const errorHandlers = new Set();
2137
- const messageHandlers = new Set();
2138
- const emitOpen = () => {
2139
- if (opened || closed)
2140
- return;
2141
- opened = true;
2142
- for (const handler of openHandlers) {
2143
- try {
2144
- handler();
2145
- }
2146
- catch {
2147
- // no-op
2148
- }
2149
- }
2150
- };
2151
- const emitClose = (event) => {
2152
- if (closed)
2153
- return;
2154
- closed = true;
2155
- for (const handler of closeHandlers) {
2156
- try {
2157
- handler(event);
2158
- }
2159
- catch {
2160
- // no-op
2161
- }
2162
- }
2163
- };
2164
- const emitError = (event) => {
2165
- if (closed)
2166
- return;
2167
- for (const handler of errorHandlers) {
2168
- try {
2169
- handler(event);
2170
- }
2171
- catch {
2172
- // no-op
2173
- }
2174
- }
2175
- };
2176
- const emitMessage = (data) => {
2177
- if (closed)
2178
- return;
2179
- for (const handler of messageHandlers) {
2180
- try {
2181
- handler(data);
2182
- }
2183
- catch {
2184
- // no-op
2185
- }
2186
- }
2187
- };
2188
- const relayTransport = {
2189
- send: (data) => {
2190
- if (typeof data === "string") {
2191
- base.send(data);
2192
- return;
2193
- }
2194
- if (data instanceof ArrayBuffer) {
2195
- if (typeof TextDecoder !== "undefined") {
2196
- base.send(new TextDecoder().decode(data));
2197
- return;
2198
- }
2199
- if (typeof Buffer !== "undefined") {
2200
- base.send(Buffer.from(data).toString("utf8"));
2201
- return;
2202
- }
2203
- base.send(String(data));
2204
- return;
2205
- }
2206
- base.send(String(data));
2207
- },
2208
- close: (code, reason) => base.close(code, reason),
2209
- onmessage: null,
2210
- onclose: null,
2211
- onerror: null,
2212
- };
2213
- const startHandshake = async () => {
2214
- try {
2215
- channel = await createClientChannel(relayTransport, daemonPublicKeyB64, {
2216
- onopen: emitOpen,
2217
- onmessage: (data) => emitMessage(data),
2218
- onclose: (code, reason) => emitClose({ code, reason }),
2219
- onerror: (error) => emitError(error),
2220
- });
2221
- }
2222
- catch (error) {
2223
- logger.warn({ err: error }, "relay_e2ee_handshake_failed");
2224
- emitError(error);
2225
- base.close(1011, "E2EE handshake failed");
2226
- }
2227
- };
2228
- base.onOpen(() => {
2229
- void startHandshake();
2230
- });
2231
- base.onMessage((event) => {
2232
- relayTransport.onmessage?.(extractRelayMessageData(event));
2233
- });
2234
- base.onClose((event) => {
2235
- const record = event;
2236
- relayTransport.onclose?.(record?.code ?? 0, record?.reason ?? "");
2237
- emitClose(event);
2238
- });
2239
- base.onError((event) => {
2240
- relayTransport.onerror?.(event instanceof Error ? event : new Error(String(event)));
2241
- emitError(event);
2242
- });
2243
- return {
2244
- send: (data) => {
2245
- if (!channel) {
2246
- throw new Error("Encrypted channel not ready");
2247
- }
2248
- void channel.send(data).catch((error) => {
2249
- emitError(error);
2250
- });
2251
- },
2252
- close: (code, reason) => {
2253
- if (channel) {
2254
- channel.close(code, reason);
2255
- }
2256
- else {
2257
- base.close(code, reason);
2258
- }
2259
- emitClose({ code, reason });
2260
- },
2261
- onMessage: (handler) => {
2262
- messageHandlers.add(handler);
2263
- return () => messageHandlers.delete(handler);
2264
- },
2265
- onOpen: (handler) => {
2266
- openHandlers.add(handler);
2267
- if (opened) {
2268
- try {
2269
- handler();
2270
- }
2271
- catch {
2272
- // no-op
2273
- }
2274
- }
2275
- return () => openHandlers.delete(handler);
2276
- },
2277
- onClose: (handler) => {
2278
- closeHandlers.add(handler);
2279
- if (closed) {
2280
- try {
2281
- handler();
2282
- }
2283
- catch {
2284
- // no-op
2285
- }
2286
- }
2287
- return () => closeHandlers.delete(handler);
2288
- },
2289
- onError: (handler) => {
2290
- errorHandlers.add(handler);
2291
- return () => errorHandlers.delete(handler);
2292
- },
2293
- };
2294
- }
2295
- function extractRelayMessageData(event) {
2296
- const raw = event && typeof event === "object" && "data" in event
2297
- ? event.data
2298
- : event;
2299
- if (typeof raw === "string")
2300
- return raw;
2301
- if (raw instanceof ArrayBuffer)
2302
- return raw;
2303
- if (ArrayBuffer.isView(raw)) {
2304
- const view = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
2305
- const out = new Uint8Array(view.byteLength);
2306
- out.set(view);
2307
- return out.buffer;
2308
- }
2309
- return String(raw ?? "");
2310
- }
2311
- function bindWsHandler(ws, event, handler) {
2312
- if (typeof ws.addEventListener === "function") {
2313
- ws.addEventListener(event, handler);
2314
- return () => {
2315
- if (typeof ws.removeEventListener === "function") {
2316
- ws.removeEventListener(event, handler);
2317
- }
2318
- };
2319
- }
2320
- if (typeof ws.on === "function") {
2321
- ws.on(event, handler);
2322
- return () => {
2323
- if (typeof ws.off === "function") {
2324
- ws.off(event, handler);
2325
- return;
2326
- }
2327
- if (typeof ws.removeListener === "function") {
2328
- ws.removeListener(event, handler);
2329
- }
2330
- };
2331
- }
2332
- const prop = `on${event}`;
2333
- const previous = ws[prop];
2334
- ws[prop] = handler;
2335
- return () => {
2336
- if (ws[prop] === handler) {
2337
- ws[prop] = previous ?? null;
2338
- }
2339
- };
2340
- }
2341
- function describeTransportClose(event) {
2342
- if (!event) {
2343
- return "Transport closed";
2344
- }
2345
- if (event instanceof Error) {
2346
- return event.message;
2347
- }
2348
- if (typeof event === "string") {
2349
- return event;
2350
- }
2351
- if (typeof event === "object") {
2352
- const record = event;
2353
- if (typeof record.reason === "string" && record.reason.trim().length > 0) {
2354
- return record.reason.trim();
2355
- }
2356
- if (typeof record.message === "string" && record.message.trim().length > 0) {
2357
- return record.message.trim();
2358
- }
2359
- if (typeof record.code === "number") {
2360
- return `Transport closed (code ${record.code})`;
2361
- }
2362
- }
2363
- return "Transport closed";
2364
- }
2365
- function describeTransportError(event) {
2366
- if (!event) {
2367
- return "Transport error";
2368
- }
2369
- if (event instanceof Error) {
2370
- return event.message;
2371
- }
2372
- if (typeof event === "string") {
2373
- return event;
2374
- }
2375
- if (typeof event === "object") {
2376
- const record = event;
2377
- if (typeof record.message === "string" && record.message.trim().length > 0) {
2378
- return record.message.trim();
2379
- }
2380
- }
2381
- return "Transport error";
2382
- }
2383
- function safeRandomId() {
2384
- try {
2385
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
2386
- return crypto.randomUUID();
2387
- }
2388
- }
2389
- catch {
2390
- // ignore
2391
- }
2392
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
2393
- }
2394
- function decodeMessageData(data) {
2395
- if (data === null || data === undefined) {
2396
- return null;
2397
- }
2398
- if (typeof data === "string") {
2399
- return data;
2400
- }
2401
- if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
2402
- if (typeof Buffer !== "undefined") {
2403
- return Buffer.from(data).toString("utf8");
2404
- }
2405
- if (typeof TextDecoder !== "undefined") {
2406
- return new TextDecoder().decode(data);
2407
- }
2408
- }
2409
- if (ArrayBuffer.isView(data)) {
2410
- const view = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
2411
- if (typeof Buffer !== "undefined") {
2412
- return Buffer.from(view).toString("utf8");
2413
- }
2414
- if (typeof TextDecoder !== "undefined") {
2415
- return new TextDecoder().decode(view);
2416
- }
2417
- }
2418
- if (typeof data.toString === "function") {
2419
- return data.toString();
2420
- }
2421
- return null;
2422
- }
2423
2120
  function resolveAgentConfig(options) {
2424
2121
  const { config, provider, cwd, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
2425
2122
  const baseConfig = {