@getpaseo/server 0.1.3 → 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 (120) 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 +44 -32
  26. package/dist/server/client/daemon-client.d.ts.map +1 -1
  27. package/dist/server/client/daemon-client.js +463 -774
  28. package/dist/server/client/daemon-client.js.map +1 -1
  29. package/dist/server/server/agent/agent-manager.d.ts +45 -0
  30. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  31. package/dist/server/server/agent/agent-manager.js +197 -9
  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-mapper.d.ts.map +1 -1
  35. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +120 -6
  36. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  37. package/dist/server/server/agent/providers/claude-agent.d.ts +7 -0
  38. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  39. package/dist/server/server/agent/providers/claude-agent.js +83 -9
  40. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  41. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  42. package/dist/server/server/agent/providers/codex-app-server-agent.js +25 -0
  43. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  44. package/dist/server/server/agent/stt-manager.d.ts +3 -2
  45. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  46. package/dist/server/server/agent/stt-manager.js +5 -3
  47. package/dist/server/server/agent/stt-manager.js.map +1 -1
  48. package/dist/server/server/agent/timeline-projection.d.ts +19 -0
  49. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -0
  50. package/dist/server/server/agent/timeline-projection.js +142 -0
  51. package/dist/server/server/agent/timeline-projection.js.map +1 -0
  52. package/dist/server/server/agent/tts-manager.d.ts +3 -2
  53. package/dist/server/server/agent/tts-manager.d.ts.map +1 -1
  54. package/dist/server/server/agent/tts-manager.js +5 -3
  55. package/dist/server/server/agent/tts-manager.js.map +1 -1
  56. package/dist/server/server/agent-attention-policy.d.ts +20 -0
  57. package/dist/server/server/agent-attention-policy.d.ts.map +1 -0
  58. package/dist/server/server/agent-attention-policy.js +40 -0
  59. package/dist/server/server/agent-attention-policy.js.map +1 -0
  60. package/dist/server/server/bootstrap.d.ts.map +1 -1
  61. package/dist/server/server/bootstrap.js +13 -18
  62. package/dist/server/server/bootstrap.js.map +1 -1
  63. package/dist/server/server/dictation/dictation-stream-manager.d.ts +10 -2
  64. package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
  65. package/dist/server/server/dictation/dictation-stream-manager.js +81 -14
  66. package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
  67. package/dist/server/server/exports.d.ts +1 -1
  68. package/dist/server/server/exports.d.ts.map +1 -1
  69. package/dist/server/server/persisted-config.d.ts +4 -4
  70. package/dist/server/server/relay-transport.d.ts +3 -2
  71. package/dist/server/server/relay-transport.d.ts.map +1 -1
  72. package/dist/server/server/relay-transport.js +21 -5
  73. package/dist/server/server/relay-transport.js.map +1 -1
  74. package/dist/server/server/session.d.ts +32 -8
  75. package/dist/server/server/session.d.ts.map +1 -1
  76. package/dist/server/server/session.js +499 -79
  77. package/dist/server/server/session.js.map +1 -1
  78. package/dist/server/server/speech/provider-resolver.d.ts +3 -0
  79. package/dist/server/server/speech/provider-resolver.d.ts.map +1 -0
  80. package/dist/server/server/speech/provider-resolver.js +7 -0
  81. package/dist/server/server/speech/provider-resolver.js.map +1 -0
  82. package/dist/server/server/speech/providers/local/runtime.d.ts +1 -0
  83. package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
  84. package/dist/server/server/speech/providers/local/runtime.js +4 -3
  85. package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
  86. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  87. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +3 -66
  88. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  89. package/dist/server/server/speech/speech-runtime.d.ts +26 -3
  90. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  91. package/dist/server/server/speech/speech-runtime.js +466 -112
  92. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  93. package/dist/server/server/websocket-server.d.ts +23 -7
  94. package/dist/server/server/websocket-server.d.ts.map +1 -1
  95. package/dist/server/server/websocket-server.js +288 -64
  96. package/dist/server/server/websocket-server.js.map +1 -1
  97. package/dist/server/shared/binary-mux.d.ts +31 -0
  98. package/dist/server/shared/binary-mux.d.ts.map +1 -0
  99. package/dist/server/shared/binary-mux.js +101 -0
  100. package/dist/server/shared/binary-mux.js.map +1 -0
  101. package/dist/server/shared/messages.d.ts +4655 -3615
  102. package/dist/server/shared/messages.d.ts.map +1 -1
  103. package/dist/server/shared/messages.js +184 -26
  104. package/dist/server/shared/messages.js.map +1 -1
  105. package/dist/server/shared/terminal-key-input.d.ts +9 -0
  106. package/dist/server/shared/terminal-key-input.d.ts.map +1 -0
  107. package/dist/server/shared/terminal-key-input.js +132 -0
  108. package/dist/server/shared/terminal-key-input.js.map +1 -0
  109. package/dist/server/terminal/terminal.d.ts +17 -0
  110. package/dist/server/terminal/terminal.d.ts.map +1 -1
  111. package/dist/server/terminal/terminal.js +89 -0
  112. package/dist/server/terminal/terminal.js.map +1 -1
  113. package/dist/server/utils/checkout-git.d.ts +4 -0
  114. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  115. package/dist/server/utils/checkout-git.js +92 -0
  116. package/dist/server/utils/checkout-git.js.map +1 -1
  117. package/dist/server/utils/worktree.d.ts.map +1 -1
  118. package/dist/server/utils/worktree.js +33 -4
  119. package/dist/server/utils/worktree.js.map +1 -1
  120. 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");
@@ -783,20 +858,24 @@ export class DaemonClient {
783
858
  },
784
859
  });
785
860
  }
786
- async initializeAgent(agentId, requestId) {
787
- const resolvedRequestId = this.createRequestId(requestId);
861
+ async fetchAgentTimeline(agentId, options = {}) {
862
+ const resolvedRequestId = this.createRequestId(options.requestId);
788
863
  const message = SessionInboundMessageSchema.parse({
789
- type: "initialize_agent_request",
864
+ type: "fetch_agent_timeline_request",
790
865
  agentId,
791
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 } : {}),
792
871
  });
793
872
  const payload = await this.sendRequest({
794
873
  requestId: resolvedRequestId,
795
874
  message,
796
- timeout: 10000,
875
+ timeout: 15000,
797
876
  options: { skipQueue: true },
798
877
  select: (msg) => {
799
- if (msg.type !== "initialize_agent_request") {
878
+ if (msg.type !== "fetch_agent_timeline_response") {
800
879
  return null;
801
880
  }
802
881
  if (msg.payload.requestId !== resolvedRequestId) {
@@ -808,11 +887,7 @@ export class DaemonClient {
808
887
  if (payload.error) {
809
888
  throw new Error(payload.error);
810
889
  }
811
- const agent = await this.fetchAgent(agentId);
812
- if (!agent) {
813
- throw new Error(`Agent not found after initialize: ${agentId}`);
814
- }
815
- return agent;
890
+ return payload;
816
891
  }
817
892
  // ============================================================================
818
893
  // Agent Interaction
@@ -972,7 +1047,7 @@ export class DaemonClient {
972
1047
  ...(agentId ? { agentId } : {}),
973
1048
  requestId,
974
1049
  });
975
- return this.sendRequest({
1050
+ const response = await this.sendRequest({
976
1051
  requestId,
977
1052
  message,
978
1053
  timeout: 10000,
@@ -986,11 +1061,18 @@ export class DaemonClient {
986
1061
  return msg.payload;
987
1062
  },
988
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;
989
1071
  }
990
1072
  async sendVoiceAudioChunk(audio, format, isLast) {
991
1073
  this.sendSessionMessage({ type: "voice_audio_chunk", audio, format, isLast });
992
1074
  }
993
- startDictationStream(dictationId, format) {
1075
+ async startDictationStream(dictationId, format) {
994
1076
  const ack = this.waitForWithCancel((msg) => {
995
1077
  if (msg.type !== "dictation_stream_ack") {
996
1078
  return null;
@@ -1016,23 +1098,22 @@ export class DaemonClient {
1016
1098
  const errorPromise = streamError.promise.then((payload) => {
1017
1099
  throw new Error(payload.error);
1018
1100
  });
1101
+ const cleanupError = new Error("Cancelled dictation start waiter");
1019
1102
  try {
1020
1103
  this.sendSessionMessageStrict({ type: "dictation_stream_start", dictationId, format });
1104
+ await Promise.race([ackPromise, errorPromise]);
1021
1105
  }
1022
- catch (error) {
1023
- const err = error instanceof Error ? error : new Error(String(error));
1024
- ack.cancel(err);
1025
- streamError.cancel(err);
1106
+ finally {
1107
+ ack.cancel(cleanupError);
1108
+ streamError.cancel(cleanupError);
1026
1109
  void ackPromise.catch(() => undefined);
1027
1110
  void errorPromise.catch(() => undefined);
1028
- throw err;
1029
1111
  }
1030
- return Promise.race([ackPromise, errorPromise]);
1031
1112
  }
1032
1113
  sendDictationStreamChunk(dictationId, seq, audio, format) {
1033
1114
  this.sendSessionMessageStrict({ type: "dictation_stream_chunk", dictationId, seq, audio, format });
1034
1115
  }
1035
- finishDictationStream(dictationId, finalSeq) {
1116
+ async finishDictationStream(dictationId, finalSeq) {
1036
1117
  const final = this.waitForWithCancel((msg) => {
1037
1118
  if (msg.type !== "dictation_stream_final") {
1038
1119
  return null;
@@ -1041,7 +1122,7 @@ export class DaemonClient {
1041
1122
  return null;
1042
1123
  }
1043
1124
  return msg.payload;
1044
- }, 30000, { skipQueue: true });
1125
+ }, 0, { skipQueue: true });
1045
1126
  const streamError = this.waitForWithCancel((msg) => {
1046
1127
  if (msg.type !== "dictation_stream_error") {
1047
1128
  return null;
@@ -1050,23 +1131,96 @@ export class DaemonClient {
1050
1131
  return null;
1051
1132
  }
1052
1133
  return msg.payload;
1053
- }, 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 });
1054
1144
  const finalPromise = final.promise;
1055
1145
  const errorPromise = streamError.promise.then((payload) => {
1056
1146
  throw new Error(payload.error);
1057
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");
1058
1198
  try {
1059
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);
1060
1215
  }
1061
- catch (error) {
1062
- const err = error instanceof Error ? error : new Error(String(error));
1063
- final.cancel(err);
1064
- streamError.cancel(err);
1216
+ finally {
1217
+ final.cancel(cleanupError);
1218
+ streamError.cancel(cleanupError);
1219
+ finishAccepted.cancel(cleanupError);
1065
1220
  void finalPromise.catch(() => undefined);
1066
1221
  void errorPromise.catch(() => undefined);
1067
- throw err;
1222
+ void finishAcceptedPromise.catch(() => undefined);
1068
1223
  }
1069
- return Promise.race([finalPromise, errorPromise]);
1070
1224
  }
1071
1225
  cancelDictationStream(dictationId) {
1072
1226
  this.sendSessionMessageStrict({ type: "dictation_stream_cancel", dictationId });
@@ -1171,22 +1325,17 @@ export class DaemonClient {
1171
1325
  requestId: resolvedRequestId,
1172
1326
  });
1173
1327
  try {
1174
- return await this.sendRequest({
1328
+ return await this.sendCorrelatedRequest({
1175
1329
  requestId: resolvedRequestId,
1176
1330
  message,
1331
+ responseType: "subscribe_checkout_diff_response",
1177
1332
  timeout: 60000,
1178
1333
  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) {
1334
+ selectPayload: (payload) => {
1335
+ if (payload.subscriptionId !== subscriptionId) {
1187
1336
  return null;
1188
1337
  }
1189
- return msg.payload;
1338
+ return payload;
1190
1339
  },
1191
1340
  });
1192
1341
  }
@@ -1208,441 +1357,238 @@ export class DaemonClient {
1208
1357
  });
1209
1358
  }
1210
1359
  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;
1360
+ return this.sendCorrelatedSessionRequest({
1361
+ requestId,
1362
+ message: {
1363
+ type: "checkout_commit_request",
1364
+ cwd,
1365
+ message: input.message,
1366
+ addAll: input.addAll,
1232
1367
  },
1368
+ responseType: "checkout_commit_response",
1369
+ timeout: 60000,
1233
1370
  });
1234
1371
  }
1235
1372
  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;
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,
1258
1381
  },
1382
+ responseType: "checkout_merge_response",
1383
+ timeout: 60000,
1259
1384
  });
1260
1385
  }
1261
1386
  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;
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,
1283
1394
  },
1395
+ responseType: "checkout_merge_from_base_response",
1396
+ timeout: 60000,
1284
1397
  });
1285
1398
  }
1286
1399
  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;
1400
+ return this.sendCorrelatedSessionRequest({
1401
+ requestId,
1402
+ message: {
1403
+ type: "checkout_push_request",
1404
+ cwd,
1306
1405
  },
1406
+ responseType: "checkout_push_response",
1407
+ timeout: 60000,
1307
1408
  });
1308
1409
  }
1309
1410
  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;
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,
1332
1419
  },
1420
+ responseType: "checkout_pr_create_response",
1421
+ timeout: 60000,
1333
1422
  });
1334
1423
  }
1335
1424
  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;
1425
+ return this.sendCorrelatedSessionRequest({
1426
+ requestId,
1427
+ message: {
1428
+ type: "checkout_pr_status_request",
1429
+ cwd,
1355
1430
  },
1431
+ responseType: "checkout_pr_status_response",
1432
+ timeout: 60000,
1356
1433
  });
1357
1434
  }
1358
1435
  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;
1436
+ return this.sendCorrelatedSessionRequest({
1437
+ requestId,
1438
+ message: {
1439
+ type: "paseo_worktree_list_request",
1440
+ cwd: input.cwd,
1441
+ repoRoot: input.repoRoot,
1379
1442
  },
1443
+ responseType: "paseo_worktree_list_response",
1444
+ timeout: 60000,
1380
1445
  });
1381
1446
  }
1382
1447
  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;
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,
1404
1455
  },
1456
+ responseType: "paseo_worktree_archive_response",
1457
+ timeout: 20000,
1405
1458
  });
1406
1459
  }
1407
1460
  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,
1461
+ return this.sendCorrelatedSessionRequest({
1462
+ requestId,
1463
+ message: {
1464
+ type: "validate_branch_request",
1465
+ cwd: options.cwd,
1466
+ branchName: options.branchName,
1467
+ },
1468
+ responseType: "validate_branch_response",
1418
1469
  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;
1470
+ });
1471
+ }
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,
1428
1480
  },
1481
+ responseType: "branch_suggestions_response",
1482
+ timeout: 10000,
1429
1483
  });
1430
1484
  }
1431
1485
  // ============================================================================
1432
1486
  // File Explorer
1433
1487
  // ============================================================================
1434
1488
  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;
1456
- },
1489
+ return this.sendCorrelatedSessionRequest({
1490
+ requestId,
1491
+ message: {
1492
+ type: "file_explorer_request",
1493
+ agentId,
1494
+ path,
1495
+ mode,
1496
+ },
1497
+ responseType: "file_explorer_response",
1498
+ timeout: 10000,
1457
1499
  });
1458
1500
  }
1459
1501
  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;
1502
+ return this.sendCorrelatedSessionRequest({
1503
+ requestId,
1504
+ message: {
1505
+ type: "file_download_token_request",
1506
+ agentId,
1507
+ path,
1480
1508
  },
1509
+ responseType: "file_download_token_response",
1510
+ timeout: 10000,
1481
1511
  });
1482
1512
  }
1483
1513
  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;
1514
+ return this.sendCorrelatedSessionRequest({
1515
+ requestId,
1516
+ message: {
1517
+ type: "project_icon_request",
1518
+ cwd,
1503
1519
  },
1520
+ responseType: "project_icon_response",
1521
+ timeout: 10000,
1504
1522
  });
1505
1523
  }
1506
1524
  // ============================================================================
1507
1525
  // Provider Models / Commands
1508
1526
  // ============================================================================
1509
1527
  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;
1528
+ return this.sendCorrelatedSessionRequest({
1529
+ requestId: options?.requestId,
1530
+ message: {
1531
+ type: "list_provider_models_request",
1532
+ provider,
1533
+ cwd: options?.cwd,
1530
1534
  },
1535
+ responseType: "list_provider_models_response",
1536
+ timeout: 30000,
1531
1537
  });
1532
1538
  }
1533
1539
  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;
1540
+ return this.sendCorrelatedSessionRequest({
1541
+ requestId: options?.requestId,
1542
+ message: {
1543
+ type: "list_available_providers_request",
1552
1544
  },
1545
+ responseType: "list_available_providers_response",
1546
+ timeout: 30000,
1553
1547
  });
1554
1548
  }
1555
1549
  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;
1550
+ return this.sendCorrelatedSessionRequest({
1551
+ requestId,
1552
+ message: {
1553
+ type: "speech_models_list_request",
1574
1554
  },
1555
+ responseType: "speech_models_list_response",
1556
+ timeout: 30000,
1575
1557
  });
1576
1558
  }
1577
1559
  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;
1560
+ return this.sendCorrelatedSessionRequest({
1561
+ requestId: options?.requestId,
1562
+ message: {
1563
+ type: "speech_models_download_request",
1564
+ modelIds: options?.modelIds,
1597
1565
  },
1566
+ responseType: "speech_models_download_response",
1567
+ timeout: 30 * 60 * 1000,
1598
1568
  });
1599
1569
  }
1600
1570
  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;
1571
+ return this.sendCorrelatedSessionRequest({
1572
+ requestId,
1573
+ message: {
1574
+ type: "list_commands_request",
1575
+ agentId,
1620
1576
  },
1577
+ responseType: "list_commands_response",
1578
+ timeout: 30000,
1621
1579
  });
1622
1580
  }
1623
1581
  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;
1582
+ return this.sendCorrelatedSessionRequest({
1583
+ requestId,
1584
+ message: {
1585
+ type: "execute_command_request",
1586
+ agentId,
1587
+ commandName,
1588
+ args,
1645
1589
  },
1590
+ responseType: "execute_command_response",
1591
+ timeout: 30000,
1646
1592
  });
1647
1593
  }
1648
1594
  // ============================================================================
@@ -1704,20 +1650,12 @@ export class DaemonClient {
1704
1650
  agentId,
1705
1651
  timeoutMs: timeout,
1706
1652
  });
1707
- const payload = await this.sendRequest({
1653
+ const payload = await this.sendCorrelatedRequest({
1708
1654
  requestId,
1709
1655
  message,
1656
+ responseType: "wait_for_finish_response",
1710
1657
  timeout: timeout + 5000,
1711
1658
  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
1659
  });
1722
1660
  return {
1723
1661
  status: payload.status,
@@ -1736,20 +1674,12 @@ export class DaemonClient {
1736
1674
  cwd,
1737
1675
  requestId: resolvedRequestId,
1738
1676
  });
1739
- return this.sendRequest({
1677
+ return this.sendCorrelatedRequest({
1740
1678
  requestId: resolvedRequestId,
1741
1679
  message,
1680
+ responseType: "list_terminals_response",
1742
1681
  timeout: 10000,
1743
1682
  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
1683
  });
1754
1684
  }
1755
1685
  async createTerminal(cwd, name, requestId) {
@@ -1760,20 +1690,12 @@ export class DaemonClient {
1760
1690
  name,
1761
1691
  requestId: resolvedRequestId,
1762
1692
  });
1763
- return this.sendRequest({
1693
+ return this.sendCorrelatedRequest({
1764
1694
  requestId: resolvedRequestId,
1765
1695
  message,
1696
+ responseType: "create_terminal_response",
1766
1697
  timeout: 10000,
1767
1698
  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
1699
  });
1778
1700
  }
1779
1701
  async subscribeTerminal(terminalId, requestId) {
@@ -1783,20 +1705,12 @@ export class DaemonClient {
1783
1705
  terminalId,
1784
1706
  requestId: resolvedRequestId,
1785
1707
  });
1786
- return this.sendRequest({
1708
+ return this.sendCorrelatedRequest({
1787
1709
  requestId: resolvedRequestId,
1788
1710
  message,
1711
+ responseType: "subscribe_terminal_response",
1789
1712
  timeout: 10000,
1790
1713
  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
1714
  });
1801
1715
  }
1802
1716
  unsubscribeTerminal(terminalId) {
@@ -1819,20 +1733,94 @@ export class DaemonClient {
1819
1733
  terminalId,
1820
1734
  requestId: resolvedRequestId,
1821
1735
  });
1822
- return this.sendRequest({
1736
+ return this.sendCorrelatedRequest({
1823
1737
  requestId: resolvedRequestId,
1824
1738
  message,
1739
+ responseType: "kill_terminal_response",
1825
1740
  timeout: 10000,
1826
1741
  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;
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;
1833
1791
  }
1834
- return msg.payload;
1835
- },
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),
1836
1824
  });
1837
1825
  }
1838
1826
  async waitForTerminalOutput(terminalId, timeout = 5000) {
@@ -1883,6 +1871,14 @@ export class DaemonClient {
1883
1871
  const rawData = data && typeof data === "object" && "data" in data
1884
1872
  ? data.data
1885
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
+ }
1886
1882
  const payload = decodeMessageData(rawData);
1887
1883
  if (!payload) {
1888
1884
  return;
@@ -1905,6 +1901,20 @@ export class DaemonClient {
1905
1901
  }
1906
1902
  this.handleSessionMessage(parsed.data.message);
1907
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
+ }
1908
1918
  updateConnectionState(next) {
1909
1919
  this.connectionState = next;
1910
1920
  for (const listener of this.connectionListeners) {
@@ -1937,6 +1947,7 @@ export class DaemonClient {
1937
1947
  // and responses from the previous connection will never arrive.
1938
1948
  this.clearWaiters(new Error(reason ?? "Connection lost"));
1939
1949
  this.rejectPendingSendQueue(new Error(reason ?? "Connection lost"));
1950
+ this.terminalStreams.clearAll();
1940
1951
  this.updateConnectionState({
1941
1952
  status: "disconnected",
1942
1953
  ...(reason ? { reason } : {}),
@@ -1950,6 +1961,9 @@ export class DaemonClient {
1950
1961
  }, delay);
1951
1962
  }
1952
1963
  handleSessionMessage(msg) {
1964
+ if (msg.type === "terminal_stream_exit") {
1965
+ this.terminalStreams.clearStream({ streamId: msg.payload.streamId });
1966
+ }
1953
1967
  if (this.rawMessageListeners.size > 0) {
1954
1968
  for (const handler of this.rawMessageListeners) {
1955
1969
  try {
@@ -2016,6 +2030,8 @@ export class DaemonClient {
2016
2030
  agentId: msg.payload.agentId,
2017
2031
  event: msg.payload.event,
2018
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 } : {}),
2019
2035
  };
2020
2036
  case "status":
2021
2037
  return { type: "status", payload: msg.payload };
@@ -2101,333 +2117,6 @@ export class DaemonClient {
2101
2117
  return { promise, cancel };
2102
2118
  }
2103
2119
  }
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
2120
  function resolveAgentConfig(options) {
2432
2121
  const { config, provider, cwd, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
2433
2122
  const baseConfig = {