@factory/droid-sdk 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.mjs CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  AddUserMessageRequestSchema,
9
9
  AddUserMessageResponseSchema,
10
10
  AddUserMessageResultSchema,
11
+ AnswerSessionElicitationResponseSchema,
11
12
  AppendMessagesResponseSchema,
12
13
  ApplyPatchToolConfirmationDetailsSchema,
13
14
  AskUserCollectedAnswerSchema,
@@ -62,6 +63,7 @@ import {
62
63
  ComputerListResponseSchema,
63
64
  ComputerMetricSchema,
64
65
  ComputerMetricsResponseSchema,
66
+ ComputerReadSchema,
65
67
  ComputerSchema,
66
68
  ConcurrentStreamError,
67
69
  ConnectionClosedError,
@@ -74,8 +76,10 @@ import {
74
76
  ContextStatsSchema,
75
77
  CreateMessageNotificationSchema,
76
78
  CreateToolConfirmationDetailsSchema,
79
+ DEFAULT_CONNECTION_CONFIG,
77
80
  DEFAULT_LOCAL_DAEMON_WS_URL,
78
81
  DEFAULT_REQUEST_TIMEOUT,
82
+ DROID_RUNNING_STATES,
79
83
  DaemonAuthenticationMode,
80
84
  DaemonClient,
81
85
  DaemonDroidEvent,
@@ -89,6 +93,7 @@ import {
89
93
  DocumentSourceSchema,
90
94
  DroidClientError,
91
95
  DroidClientMethod,
96
+ DroidConnectionEndedError,
92
97
  DroidErrorType,
93
98
  DroidEvent,
94
99
  DroidHookEventSchema,
@@ -102,6 +107,9 @@ import {
102
107
  DroidWorkingStateChangedNotificationSchema,
103
108
  EditToolConfirmationDetailsSchema,
104
109
  EmptyResultSchema,
110
+ EnvironmentComputerTimeoutError,
111
+ EnvironmentListResponseSchema,
112
+ EnvironmentSchema,
105
113
  ErrorDetailSchema,
106
114
  ErrorNotificationSchema,
107
115
  ExecToolInfoSchema,
@@ -115,7 +123,6 @@ import {
115
123
  FACTORY_CLIENT_HEADER,
116
124
  FACTORY_CLIENT_VERSION,
117
125
  FACTORY_PROTOCOL_VERSION,
118
- FACTORY_PROTOCOL_VERSION2,
119
126
  FactoryDaemonTransport,
120
127
  FactoryDroidMessageRole,
121
128
  FactoryDroidMessageSchema,
@@ -131,6 +138,7 @@ import {
131
138
  GetContextStatsRequestParamsSchema,
132
139
  GetContextStatsRequestSchema,
133
140
  GetContextStatsResponseSchema,
141
+ GetHandoffSummaryResponseSchema,
134
142
  GetRewindInfoRequestParamsSchema,
135
143
  GetRewindInfoRequestSchema,
136
144
  GetRewindInfoResponseSchema,
@@ -162,7 +170,6 @@ import {
162
170
  IssueSeverity,
163
171
  JSONRPC_VERSION,
164
172
  JSONRPC_VERSION2,
165
- JSONRPC_VERSION3,
166
173
  JsonArraySchema,
167
174
  JsonObjectSchema,
168
175
  JsonRpcBaseResponseSchema,
@@ -178,6 +185,7 @@ import {
178
185
  JsonRpcResponseSchema,
179
186
  JsonRpcResponseSuccessSchema,
180
187
  JsonValueSchema,
188
+ KillBackgroundProcessResponseSchema,
181
189
  KillWorkerSessionRequestParamsSchema,
182
190
  KillWorkerSessionRequestSchema,
183
191
  KillWorkerSessionResponseSchema,
@@ -202,6 +210,7 @@ import {
202
210
  ListModelsOptionsSchema,
203
211
  ListModelsResponseSchema,
204
212
  ListModelsResultSchema,
213
+ ListSessionElicitationsResponseSchema,
205
214
  ListSkillsRequestParamsSchema,
206
215
  ListSkillsRequestSchema,
207
216
  ListSkillsResponseSchema,
@@ -216,6 +225,7 @@ import {
216
225
  LoadSessionRequestSchema,
217
226
  LoadSessionResponseSchema,
218
227
  LoadSessionResultSchema,
228
+ MACHINE_TYPE_CONFIG,
219
229
  MACHINE_TYPE_WEBSOCKET_OVERRIDES,
220
230
  MANAGED_COMPUTER_MAX_RETRIES,
221
231
  MCP_AUTH_TIMEOUT,
@@ -248,6 +258,7 @@ import {
248
258
  MissionFeatureSchema,
249
259
  MissionFeaturesChangedNotificationSchema,
250
260
  MissionHeartbeatNotificationSchema,
261
+ MissionMachineTargetKind,
251
262
  MissionPausedEntrySchema,
252
263
  MissionProgressEntryNotificationSchema,
253
264
  MissionResumedEntrySchema,
@@ -295,6 +306,7 @@ import {
295
306
  RenameSessionRequestSchema,
296
307
  RenameSessionResponseSchema,
297
308
  RenameSessionResultSchema,
309
+ RequestCancelledError,
298
310
  RequestPermissionRequestParamsSchema,
299
311
  RequestPermissionRequestSchema,
300
312
  RequestPermissionResponseSchema,
@@ -303,12 +315,14 @@ import {
303
315
  RewindEvictedFileSchema,
304
316
  RewindFileCreationSchema,
305
317
  RewindFileSnapshotSchema,
318
+ SDK_CLIENT_METADATA,
306
319
  SDK_IDENTITY,
307
320
  SDK_REQUEST_ATTRIBUTION,
308
321
  SDK_TAG,
309
322
  SDK_VERSION,
310
323
  SESSION_INIT_TIMEOUT,
311
324
  SLACK_DELEGATION_MAX_RETRIES,
325
+ SandboxEnvironmentSchema,
312
326
  ServerRequestHandlerType,
313
327
  SessionArtifactAction,
314
328
  SessionArtifactKind,
@@ -316,6 +330,7 @@ import {
316
330
  SessionArtifactSchema,
317
331
  SessionArtifactSource,
318
332
  SessionArtifactsSchema,
333
+ SessionElicitationSchema,
319
334
  SessionError,
320
335
  SessionMetadataSchema,
321
336
  SessionNotFoundError,
@@ -366,6 +381,7 @@ import {
366
381
  SubmitMcpAuthCodeResultSchema,
367
382
  SubmitMcpAuthErrorResponseSchema,
368
383
  SuccessResultSchema,
384
+ SummarizeForMembersResponseSchema,
369
385
  TestCaseSchema,
370
386
  TestFileSchema,
371
387
  TestsSchema,
@@ -417,6 +433,7 @@ import {
417
433
  WorkerSelectedFeatureEntrySchema,
418
434
  WorkerStartedEntrySchema,
419
435
  WorkerStateInfoSchema,
436
+ answerRemoteSessionElicitation,
420
437
  appendContextSession,
421
438
  buildUserMessageContentBlocks,
422
439
  configureDaemonCoreDeps,
@@ -424,10 +441,13 @@ import {
424
441
  connectToDaemon,
425
442
  connectWithRetry,
426
443
  convertNotificationToStreamMessage,
444
+ createApiRequest,
427
445
  createComputer,
428
446
  createContextDocument,
429
447
  createContextFile,
430
448
  createContextSpace,
449
+ createFactoryApi,
450
+ createOrRecoverComputer,
431
451
  createResponseSchema,
432
452
  createWebSocketDaemonClient,
433
453
  defaultDaemonCoreDeps,
@@ -435,6 +455,7 @@ import {
435
455
  deleteContextDocument,
436
456
  deleteContextFile,
437
457
  emitDroidLog,
458
+ encodeLegacyStartupLoadOptions,
438
459
  escapeJsonLineSeparators,
439
460
  extractInnerNotification,
440
461
  filterMessagesForUI,
@@ -449,6 +470,8 @@ import {
449
470
  getPermissionToolInputForDisplay,
450
471
  getQueuedUserMessageDisplayGroup,
451
472
  getQueuedUserMessageReviewPriority,
473
+ getSandboxEnvironmentLaunchRepositories,
474
+ getSandboxEnvironmentLaunchSource,
452
475
  getSubagentCallingMetadata,
453
476
  getToolResultBlocks,
454
477
  getToolResultToolUseId,
@@ -457,38 +480,51 @@ import {
457
480
  injectDroidTraceContext,
458
481
  inspectJsonRpcEnvelope,
459
482
  isDaemonQueuedMessageKind,
483
+ isEnvironmentComputerReady,
484
+ isOfferedPermissionOutcome,
460
485
  isPendingToolResultMarker,
461
486
  isPersistedHookMessage,
462
487
  isProtocolVersionAtLeast,
463
488
  isReviewableQueuedMessageKind,
489
+ isSandboxEnvironmentLaunchable,
464
490
  listComputers,
465
491
  listContextDocuments,
466
492
  listContextSessions,
467
493
  listContextSpaces,
494
+ listEnvironments,
468
495
  listMachineTemplates,
496
+ listRemoteSessionElicitations,
469
497
  listRemoteSessions,
470
498
  machineTypeToMachineConnectionType,
471
499
  mergeSessionSettings,
500
+ parseResponse,
472
501
  readContextFilePart,
473
502
  readContextSessionRecords,
474
503
  recordComputerConnectSli,
475
504
  recordDroidMetric,
476
505
  refreshComputer,
477
506
  registerContextSession,
507
+ resolveAskUserHandler,
508
+ resolvePermissionHandler,
478
509
  restartComputer,
479
510
  retryInstallDeps,
480
511
  retryWithLogger,
512
+ runWithAbort,
513
+ sessionTagsUpdateForPeer,
481
514
  streamFromClient,
482
515
  sumTokenUsage,
483
516
  summarizeSessionToolUsage,
484
517
  toError,
518
+ trustedIdentityFromComputerResponse,
485
519
  unwrapResponseResult,
486
520
  updateComputer,
487
521
  updateContextDocument,
488
522
  uploadContextFilePart,
523
+ waitForEnvironmentComputer,
489
524
  wireAbortSignal,
490
525
  withSdkTag
491
- } from "./chunk-TWAVPIPS.mjs";
526
+ } from "./chunk-NWFX4MPJ.mjs";
527
+ import "./chunk-7ZD7MMNA.mjs";
492
528
 
493
529
  // src/exec-core/client.ts
494
530
  import EventEmitter from "events";
@@ -497,7 +533,7 @@ import { z } from "zod";
497
533
 
498
534
  // src/exec-core/constants.ts
499
535
  var DEFAULT_REQUEST_TIMEOUT2 = 3e4;
500
- var SESSION_INIT_TIMEOUT2 = 6e4;
536
+ var SESSION_INIT_TIMEOUT2 = 12e4;
501
537
  var COMPACTION_TIMEOUT2 = 24e4;
502
538
  var MCP_AUTH_TIMEOUT2 = 3e5;
503
539
  var WINDOWS_JOB_OBJECT_ERROR_MARKER = "AssignProcessToJobObject";
@@ -742,16 +778,21 @@ function resolveExecCoreDeps(observability, fallback = defaultExecCoreDeps) {
742
778
  }
743
779
 
744
780
  // src/exec-core/client.ts
745
- function getResponseId(response) {
746
- const parsed = JsonRpcBaseResponseSchema.safeParse(response);
747
- return parsed.success ? parsed.data.id : void 0;
781
+ function isDroidConnectionEnded(error) {
782
+ const failure = error instanceof ProtocolError ? error.cause : error;
783
+ return failure instanceof DroidConnectionEndedError || failure instanceof ProcessExitError;
748
784
  }
749
785
  var JsonRpcEnvelopePeekSchema = JsonRpcEnvelopeSchema.partial().extend({
750
786
  type: z.nativeEnum(JsonRpcMessageType).optional(),
751
787
  method: z.string().optional(),
752
788
  id: z.string().nullable().optional()
753
789
  }).passthrough();
790
+ function getResponseId(response2) {
791
+ const parsed = JsonRpcEnvelopePeekSchema.safeParse(response2);
792
+ return parsed.success ? parsed.data.id : void 0;
793
+ }
754
794
  var MCP_OAUTH_OPT_OUT_PROTOCOL_VERSION = "1.78.0";
795
+ var PERMISSION_WITHDRAWN = /* @__PURE__ */ Symbol("permission withdrawn");
755
796
  function inspectJsonRpcEnvelope2(message) {
756
797
  const result = JsonRpcEnvelopePeekSchema.safeParse(message);
757
798
  if (!result.success) {
@@ -778,9 +819,18 @@ var DroidClient = class _DroidClient extends EventEmitter {
778
819
  pickOptionsHandlers = /* @__PURE__ */ new Map();
779
820
  pendingPermissionRequests = /* @__PURE__ */ new Map();
780
821
  pendingAskUserRequests = /* @__PURE__ */ new Map();
822
+ /**
823
+ * Queued (non-blocking) AskUser requests that arrived before an AskUser
824
+ * handler was set, as when a reloaded session asks its pending queued sets
825
+ * again during the load. Answering one without a handler would record it
826
+ * as dismissed, and the agent never waits on it, so it waits for the
827
+ * handler instead.
828
+ */
829
+ heldQueuedAskUserRequests = [];
781
830
  pendingPickOptionsRequests = /* @__PURE__ */ new Map();
782
831
  currentSessionId = null;
783
832
  peerProtocolVersion;
833
+ peerEnforcesQueueOwnership = false;
784
834
  /**
785
835
  * Sticky error from a transport failure. Once set, all subsequent
786
836
  * `sendRequest()` calls throw this error immediately, preventing
@@ -788,6 +838,12 @@ var DroidClient = class _DroidClient extends EventEmitter {
788
838
  */
789
839
  transportError = null;
790
840
  completedTurnObserved = false;
841
+ /**
842
+ * Set once the droid sends `droid.disconnect`. It reads no further
843
+ * requests from then on, so it no longer counts as connected even while its
844
+ * transport stays open.
845
+ */
846
+ disconnected = false;
791
847
  logger;
792
848
  tracing;
793
849
  onRequestSettled;
@@ -815,20 +871,10 @@ var DroidClient = class _DroidClient extends EventEmitter {
815
871
  });
816
872
  this.transport.onError((error) => {
817
873
  const err = error instanceof Error ? error : new Error(String(error));
818
- const pendingSessionIds = /* @__PURE__ */ new Set();
819
- let hasPendingIdlessSessionStart = false;
820
- for (const pendingRequest of this.pendingRequests.values()) {
821
- if (pendingRequest.method === "droid.initialize_session" /* INITIALIZE_SESSION */ && pendingRequest.sessionId === void 0) {
822
- hasPendingIdlessSessionStart = true;
823
- } else if (pendingRequest.sessionId !== void 0) {
824
- pendingSessionIds.add(pendingRequest.sessionId);
825
- }
826
- }
827
- const pendingSessionId = pendingSessionIds.size === 1 ? pendingSessionIds.values().next().value : void 0;
828
- const sessionId = hasPendingIdlessSessionStart || pendingSessionIds.size > 1 ? void 0 : pendingSessionId ?? this.currentSessionId ?? void 0;
829
- const expectedCleanExit = err instanceof ProcessExitError && err.exitCode === 0 && this.completedTurnObserved;
830
- if (expectedCleanExit) {
831
- this.logger.info("[DroidClient] Process exited after completed turn", {
874
+ const sessionId = this.addressedSessionId;
875
+ const expectedExit = err instanceof ProcessExitError && (err.isRequestedStop || err.exitCode === 0 && this.completedTurnObserved);
876
+ if (expectedExit) {
877
+ this.logger.info("[DroidClient] Process exited as expected", {
832
878
  sessionId,
833
879
  exitCode: err.exitCode,
834
880
  signal: err.signal
@@ -854,8 +900,48 @@ var DroidClient = class _DroidClient extends EventEmitter {
854
900
  get sessionId() {
855
901
  return this.currentSessionId;
856
902
  }
903
+ /**
904
+ * The session that an inbound prompt or transport fault belongs to. A
905
+ * session start or load in flight owns it, because the CLI replays a loaded
906
+ * session's pending prompts before it answers the load; otherwise the
907
+ * current session does. Undefined when that is ambiguous: a start with no
908
+ * session id yet, or several starts and loads in flight.
909
+ *
910
+ * @internal
911
+ */
912
+ get addressedSessionId() {
913
+ const pendingSessionIds = /* @__PURE__ */ new Set();
914
+ for (const pendingRequest of this.pendingRequests.values()) {
915
+ if (pendingRequest.method === "droid.initialize_session" /* INITIALIZE_SESSION */ && pendingRequest.sessionId === void 0) {
916
+ return void 0;
917
+ }
918
+ if (pendingRequest.sessionId !== void 0) {
919
+ pendingSessionIds.add(pendingRequest.sessionId);
920
+ }
921
+ }
922
+ if (pendingSessionIds.size > 1) {
923
+ return void 0;
924
+ }
925
+ const [pendingSessionId] = pendingSessionIds;
926
+ return pendingSessionId ?? this.currentSessionId ?? void 0;
927
+ }
857
928
  get isConnected() {
858
- return this.transport.isConnected;
929
+ return !this.disconnected && this.transport.isConnected;
930
+ }
931
+ /**
932
+ * Whether the worker runs in a child process. A daemon's shutdown cuts such
933
+ * a worker off from its owner before closing it, and from then on it reads
934
+ * no request; a worker hosted in this process still answers.
935
+ */
936
+ get runsInChildProcess() {
937
+ return (this.transport.getManagedProcess?.() ?? null) !== null;
938
+ }
939
+ /**
940
+ * Whether the runner refuses a queue change from someone other than the
941
+ * prompt's author. Older runners ignore the author on such a change.
942
+ */
943
+ get enforcesQueueOwnership() {
944
+ return this.peerEnforcesQueueOwnership;
859
945
  }
860
946
  requireMcpOAuthOptOutSupport() {
861
947
  if (isProtocolVersionAtLeast(
@@ -882,37 +968,38 @@ var DroidClient = class _DroidClient extends EventEmitter {
882
968
  }
883
969
  async initializeSession(params) {
884
970
  const request = {
885
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
971
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
886
972
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
887
973
  type: "request",
888
974
  jsonrpc: JSONRPC_VERSION2,
889
975
  id: uuidv4(),
890
976
  method: "droid.initialize_session" /* INITIALIZE_SESSION */,
891
- params
977
+ params: encodeLegacyStartupLoadOptions(params)
892
978
  };
893
979
  const rawResponse = await this.sendRequest(
894
980
  request,
895
981
  this.sessionInitTimeout,
896
982
  params.sessionId
897
983
  );
898
- const response = InitializeSessionResponseSchema.parse(rawResponse);
899
- this.peerProtocolVersion = response.factoryProtocolVersion;
900
- if (response.error) {
984
+ const response2 = InitializeSessionResponseSchema.parse(rawResponse);
985
+ this.peerProtocolVersion = response2.factoryProtocolVersion;
986
+ if (response2.error) {
901
987
  throw new ProtocolError("Initialize session request failed", {
902
988
  requestId: request.id,
903
- code: response.error.code,
904
- message: response.error.message,
905
- data: response.error.data
989
+ code: response2.error.code,
990
+ message: response2.error.message,
991
+ data: response2.error.data
906
992
  });
907
993
  }
908
994
  this.validateMcpServersProtocolSupport(params.mcpServers);
909
- const result = InitializeSessionResultSchema.parse(response.result);
995
+ const result = InitializeSessionResultSchema.parse(response2.result);
996
+ this.peerEnforcesQueueOwnership = result.enforcesQueueOwnership === true;
910
997
  this.currentSessionId = result.sessionId;
911
- return response;
998
+ return response2;
912
999
  }
913
1000
  async loadSession(params) {
914
1001
  const request = {
915
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1002
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
916
1003
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
917
1004
  type: "request",
918
1005
  jsonrpc: JSONRPC_VERSION2,
@@ -925,28 +1012,29 @@ var DroidClient = class _DroidClient extends EventEmitter {
925
1012
  this.sessionInitTimeout,
926
1013
  params.sessionId
927
1014
  );
928
- const response = LoadSessionResponseSchema.parse(rawResponse);
929
- this.peerProtocolVersion = response.factoryProtocolVersion;
930
- if (response.error) {
931
- const isSessionNotFound = response.error.code === -32004 /* ENTITY_NOT_FOUND */;
1015
+ const response2 = LoadSessionResponseSchema.parse(rawResponse);
1016
+ this.peerProtocolVersion = response2.factoryProtocolVersion;
1017
+ if (response2.error) {
1018
+ const isSessionNotFound = response2.error.code === -32004 /* ENTITY_NOT_FOUND */;
932
1019
  if (isSessionNotFound) {
933
1020
  throw new SessionNotFoundError(params.sessionId, {
934
1021
  requestId: request.id,
935
- code: response.error.code,
936
- message: response.error.message,
937
- data: response.error.data
1022
+ code: response2.error.code,
1023
+ message: response2.error.message,
1024
+ data: response2.error.data
938
1025
  });
939
1026
  }
940
1027
  throw new ProtocolError("Load session request failed", {
941
1028
  requestId: request.id,
942
- code: response.error.code,
943
- message: response.error.message,
944
- data: response.error.data
1029
+ code: response2.error.code,
1030
+ message: response2.error.message,
1031
+ data: response2.error.data
945
1032
  });
946
1033
  }
947
1034
  this.validateMcpServersProtocolSupport(params.mcpServers);
1035
+ this.peerEnforcesQueueOwnership = response2.result?.enforcesQueueOwnership === true;
948
1036
  this.currentSessionId = params.sessionId;
949
- return response;
1037
+ return response2;
950
1038
  }
951
1039
  async addUserMessage(params, requestId) {
952
1040
  if (!this.currentSessionId) {
@@ -956,7 +1044,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
956
1044
  }
957
1045
  this.completedTurnObserved = false;
958
1046
  const request = {
959
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1047
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
960
1048
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
961
1049
  type: "request",
962
1050
  jsonrpc: JSONRPC_VERSION2,
@@ -968,16 +1056,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
968
1056
  }
969
1057
  };
970
1058
  const rawResponse = await this.sendRequest(request);
971
- const response = AddUserMessageResponseSchema.parse(rawResponse);
972
- if (response.error) {
1059
+ const response2 = AddUserMessageResponseSchema.parse(rawResponse);
1060
+ if (response2.error) {
973
1061
  throw new ProtocolError("Add user message request failed", {
974
1062
  requestId: request.id,
975
- code: response.error.code,
976
- message: response.error.message,
977
- data: response.error.data
1063
+ code: response2.error.code,
1064
+ message: response2.error.message,
1065
+ data: response2.error.data
978
1066
  });
979
1067
  }
980
- return response;
1068
+ return response2;
981
1069
  }
982
1070
  /**
983
1071
  * Persists user-only messages without starting an agent turn.
@@ -992,7 +1080,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
992
1080
  );
993
1081
  }
994
1082
  const request = {
995
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1083
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
996
1084
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
997
1085
  type: "request",
998
1086
  jsonrpc: JSONRPC_VERSION2,
@@ -1001,16 +1089,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1001
1089
  params
1002
1090
  };
1003
1091
  const rawResponse = await this.sendRequest(request);
1004
- const response = AppendMessagesResponseSchema.parse(rawResponse);
1005
- if (response.error) {
1092
+ const response2 = AppendMessagesResponseSchema.parse(rawResponse);
1093
+ if (response2.error) {
1006
1094
  throw new ProtocolError("Append messages request failed", {
1007
1095
  requestId: request.id,
1008
- code: response.error.code,
1009
- message: response.error.message,
1010
- data: response.error.data
1096
+ code: response2.error.code,
1097
+ message: response2.error.message,
1098
+ data: response2.error.data
1011
1099
  });
1012
1100
  }
1013
- return response;
1101
+ return response2;
1014
1102
  }
1015
1103
  async resolveQueuedUserMessage(params) {
1016
1104
  if (!this.currentSessionId) {
@@ -1019,7 +1107,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1019
1107
  );
1020
1108
  }
1021
1109
  const request = {
1022
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1110
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1023
1111
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1024
1112
  type: "request",
1025
1113
  jsonrpc: JSONRPC_VERSION2,
@@ -1028,16 +1116,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1028
1116
  params
1029
1117
  };
1030
1118
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1031
- const response = ResolveQueuedUserMessageResponseSchema.parse(rawResponse);
1032
- if (response.error) {
1119
+ const response2 = ResolveQueuedUserMessageResponseSchema.parse(rawResponse);
1120
+ if (response2.error) {
1033
1121
  throw new ProtocolError("Resolve queued user message request failed", {
1034
1122
  requestId: request.id,
1035
- code: response.error.code,
1036
- message: response.error.message,
1037
- data: response.error.data
1123
+ code: response2.error.code,
1124
+ message: response2.error.message,
1125
+ data: response2.error.data
1038
1126
  });
1039
1127
  }
1040
- return response;
1128
+ return response2;
1041
1129
  }
1042
1130
  async interruptSession(params = {}) {
1043
1131
  if (!this.currentSessionId) {
@@ -1046,7 +1134,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1046
1134
  );
1047
1135
  }
1048
1136
  const request = {
1049
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1137
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1050
1138
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1051
1139
  type: "request",
1052
1140
  jsonrpc: JSONRPC_VERSION2,
@@ -1055,16 +1143,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1055
1143
  params
1056
1144
  };
1057
1145
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1058
- const response = InterruptSessionResponseSchema.parse(rawResponse);
1059
- if (response.error) {
1146
+ const response2 = InterruptSessionResponseSchema.parse(rawResponse);
1147
+ if (response2.error) {
1060
1148
  throw new ProtocolError("Interrupt session request failed", {
1061
1149
  requestId: request.id,
1062
- code: response.error.code,
1063
- message: response.error.message,
1064
- data: response.error.data
1150
+ code: response2.error.code,
1151
+ message: response2.error.message,
1152
+ data: response2.error.data
1065
1153
  });
1066
1154
  }
1067
- return response;
1155
+ return response2;
1068
1156
  }
1069
1157
  async closeSession(params = {}) {
1070
1158
  if (!this.currentSessionId) {
@@ -1073,7 +1161,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1073
1161
  );
1074
1162
  }
1075
1163
  const request = {
1076
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1164
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1077
1165
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1078
1166
  type: "request",
1079
1167
  jsonrpc: JSONRPC_VERSION2,
@@ -1082,16 +1170,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1082
1170
  params
1083
1171
  };
1084
1172
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1085
- const response = CloseSessionResponseSchema.parse(rawResponse);
1086
- if (response.error) {
1173
+ const response2 = CloseSessionResponseSchema.parse(rawResponse);
1174
+ if (response2.error) {
1087
1175
  throw new ProtocolError("Close session request failed", {
1088
1176
  requestId: request.id,
1089
- code: response.error.code,
1090
- message: response.error.message,
1091
- data: response.error.data
1177
+ code: response2.error.code,
1178
+ message: response2.error.message,
1179
+ data: response2.error.data
1092
1180
  });
1093
1181
  }
1094
- return response;
1182
+ return response2;
1095
1183
  }
1096
1184
  async killWorkerSession(params) {
1097
1185
  if (!this.currentSessionId) {
@@ -1100,7 +1188,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1100
1188
  );
1101
1189
  }
1102
1190
  const request = {
1103
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1191
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1104
1192
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1105
1193
  type: "request",
1106
1194
  jsonrpc: JSONRPC_VERSION2,
@@ -1109,16 +1197,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1109
1197
  params
1110
1198
  };
1111
1199
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1112
- const response = KillWorkerSessionResponseSchema.parse(rawResponse);
1113
- if (response.error) {
1200
+ const response2 = KillWorkerSessionResponseSchema.parse(rawResponse);
1201
+ if (response2.error) {
1114
1202
  throw new ProtocolError("Kill worker session request failed", {
1115
1203
  requestId: request.id,
1116
- code: response.error.code,
1117
- message: response.error.message,
1118
- data: response.error.data
1204
+ code: response2.error.code,
1205
+ message: response2.error.message,
1206
+ data: response2.error.data
1119
1207
  });
1120
1208
  }
1121
- return response;
1209
+ return response2;
1122
1210
  }
1123
1211
  async updateSessionSettings(params, requestId) {
1124
1212
  if (!this.currentSessionId) {
@@ -1127,13 +1215,13 @@ var DroidClient = class _DroidClient extends EventEmitter {
1127
1215
  );
1128
1216
  }
1129
1217
  const request = {
1130
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1218
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1131
1219
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1132
1220
  type: "request",
1133
1221
  jsonrpc: JSONRPC_VERSION2,
1134
1222
  id: requestId ?? uuidv4(),
1135
1223
  method: "droid.update_session_settings" /* UPDATE_SESSION_SETTINGS */,
1136
- params
1224
+ params: sessionTagsUpdateForPeer(params, this.peerProtocolVersion)
1137
1225
  };
1138
1226
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1139
1227
  const responseParsed = UpdateSessionSettingsResponseSchema.safeParse(rawResponse);
@@ -1148,16 +1236,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1148
1236
  }
1149
1237
  );
1150
1238
  }
1151
- const response = responseParsed.data;
1152
- if (response.error) {
1239
+ const response2 = responseParsed.data;
1240
+ if (response2.error) {
1153
1241
  throw new ProtocolError("Update session settings request failed", {
1154
1242
  requestId: request.id,
1155
- code: response.error.code,
1156
- message: response.error.message,
1157
- data: response.error.data
1243
+ code: response2.error.code,
1244
+ message: response2.error.message,
1245
+ data: response2.error.data
1158
1246
  });
1159
1247
  }
1160
- return response;
1248
+ return response2;
1161
1249
  }
1162
1250
  async changeWorkingDirectory(params) {
1163
1251
  if (!this.currentSessionId) {
@@ -1166,7 +1254,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1166
1254
  );
1167
1255
  }
1168
1256
  const request = {
1169
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1257
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1170
1258
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1171
1259
  type: "request",
1172
1260
  jsonrpc: JSONRPC_VERSION2,
@@ -1175,16 +1263,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1175
1263
  params
1176
1264
  };
1177
1265
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1178
- const response = ChangeWorkingDirectoryResponseSchema.parse(rawResponse);
1179
- if (response.error) {
1266
+ const response2 = ChangeWorkingDirectoryResponseSchema.parse(rawResponse);
1267
+ if (response2.error) {
1180
1268
  throw new ProtocolError("Change working directory request failed", {
1181
1269
  requestId: request.id,
1182
- code: response.error.code,
1183
- message: response.error.message,
1184
- data: response.error.data
1270
+ code: response2.error.code,
1271
+ message: response2.error.message,
1272
+ data: response2.error.data
1185
1273
  });
1186
1274
  }
1187
- return response;
1275
+ return response2;
1188
1276
  }
1189
1277
  async toggleMcpServer(params) {
1190
1278
  if (!this.currentSessionId) {
@@ -1193,7 +1281,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1193
1281
  );
1194
1282
  }
1195
1283
  const request = {
1196
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1284
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1197
1285
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1198
1286
  type: "request",
1199
1287
  jsonrpc: JSONRPC_VERSION2,
@@ -1206,8 +1294,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1206
1294
  }
1207
1295
  };
1208
1296
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1209
- const response = ToggleMcpServerResponseSchema.parse(rawResponse);
1210
- return response;
1297
+ const response2 = ToggleMcpServerResponseSchema.parse(rawResponse);
1298
+ return response2;
1211
1299
  }
1212
1300
  async authenticateMcpServer(params) {
1213
1301
  if (!this.currentSessionId) {
@@ -1216,7 +1304,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1216
1304
  );
1217
1305
  }
1218
1306
  const request = {
1219
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1307
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1220
1308
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1221
1309
  type: "request",
1222
1310
  jsonrpc: JSONRPC_VERSION2,
@@ -1227,8 +1315,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1227
1315
  }
1228
1316
  };
1229
1317
  const rawResponse = await this.sendRequest(request, MCP_AUTH_TIMEOUT2);
1230
- const response = AuthenticateMcpServerResponseSchema.parse(rawResponse);
1231
- return response;
1318
+ const response2 = AuthenticateMcpServerResponseSchema.parse(rawResponse);
1319
+ return response2;
1232
1320
  }
1233
1321
  async cancelMcpAuth(params) {
1234
1322
  if (!this.currentSessionId) {
@@ -1237,7 +1325,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1237
1325
  );
1238
1326
  }
1239
1327
  const request = {
1240
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1328
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1241
1329
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1242
1330
  type: "request",
1243
1331
  jsonrpc: JSONRPC_VERSION2,
@@ -1248,8 +1336,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1248
1336
  }
1249
1337
  };
1250
1338
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1251
- const response = CancelMcpAuthResponseSchema.parse(rawResponse);
1252
- return response;
1339
+ const response2 = CancelMcpAuthResponseSchema.parse(rawResponse);
1340
+ return response2;
1253
1341
  }
1254
1342
  async clearMcpAuth(params) {
1255
1343
  if (!this.currentSessionId) {
@@ -1258,7 +1346,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1258
1346
  );
1259
1347
  }
1260
1348
  const request = {
1261
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1349
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1262
1350
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1263
1351
  type: "request",
1264
1352
  jsonrpc: JSONRPC_VERSION2,
@@ -1269,8 +1357,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1269
1357
  }
1270
1358
  };
1271
1359
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1272
- const response = ClearMcpAuthResponseSchema.parse(rawResponse);
1273
- return response;
1360
+ const response2 = ClearMcpAuthResponseSchema.parse(rawResponse);
1361
+ return response2;
1274
1362
  }
1275
1363
  async addMcpServer(params) {
1276
1364
  if (!this.currentSessionId) {
@@ -1282,7 +1370,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1282
1370
  this.requireMcpOAuthOptOutSupport();
1283
1371
  }
1284
1372
  const request = {
1285
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1373
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1286
1374
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1287
1375
  type: "request",
1288
1376
  jsonrpc: JSONRPC_VERSION2,
@@ -1291,8 +1379,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1291
1379
  params
1292
1380
  };
1293
1381
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1294
- const response = AddMcpServerResponseSchema.parse(rawResponse);
1295
- return response;
1382
+ const response2 = AddMcpServerResponseSchema.parse(rawResponse);
1383
+ return response2;
1296
1384
  }
1297
1385
  async removeMcpServer(params) {
1298
1386
  if (!this.currentSessionId) {
@@ -1301,7 +1389,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1301
1389
  );
1302
1390
  }
1303
1391
  const request = {
1304
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1392
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1305
1393
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1306
1394
  type: "request",
1307
1395
  jsonrpc: JSONRPC_VERSION2,
@@ -1310,12 +1398,12 @@ var DroidClient = class _DroidClient extends EventEmitter {
1310
1398
  params
1311
1399
  };
1312
1400
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1313
- const response = RemoveMcpServerResponseSchema.parse(rawResponse);
1314
- return response;
1401
+ const response2 = RemoveMcpServerResponseSchema.parse(rawResponse);
1402
+ return response2;
1315
1403
  }
1316
1404
  async listModels(params = {}) {
1317
1405
  const request = {
1318
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1406
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1319
1407
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1320
1408
  type: "request",
1321
1409
  jsonrpc: JSONRPC_VERSION2,
@@ -1333,7 +1421,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1333
1421
  );
1334
1422
  }
1335
1423
  const request = {
1336
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1424
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1337
1425
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1338
1426
  type: "request",
1339
1427
  jsonrpc: JSONRPC_VERSION2,
@@ -1342,8 +1430,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1342
1430
  params: {}
1343
1431
  };
1344
1432
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1345
- const response = ListMcpRegistryResponseSchema.parse(rawResponse);
1346
- return response;
1433
+ const response2 = ListMcpRegistryResponseSchema.parse(rawResponse);
1434
+ return response2;
1347
1435
  }
1348
1436
  async refreshConnectorToolCatalog(params = {}) {
1349
1437
  if (!this.currentSessionId) {
@@ -1352,7 +1440,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1352
1440
  );
1353
1441
  }
1354
1442
  const request = {
1355
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1443
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1356
1444
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1357
1445
  type: "request",
1358
1446
  jsonrpc: JSONRPC_VERSION2,
@@ -1360,8 +1448,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1360
1448
  method: "droid.refresh_connector_tool_catalog" /* REFRESH_CONNECTOR_TOOL_CATALOG */,
1361
1449
  params
1362
1450
  };
1363
- const response = await this.sendRequest(request, this.requestTimeout);
1364
- return RefreshConnectorToolCatalogResponseSchema.parse(response);
1451
+ const response2 = await this.sendRequest(request, this.requestTimeout);
1452
+ return RefreshConnectorToolCatalogResponseSchema.parse(response2);
1365
1453
  }
1366
1454
  async listMcpTools() {
1367
1455
  if (!this.currentSessionId) {
@@ -1370,7 +1458,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1370
1458
  );
1371
1459
  }
1372
1460
  const request = {
1373
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1461
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1374
1462
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1375
1463
  type: "request",
1376
1464
  jsonrpc: JSONRPC_VERSION2,
@@ -1379,13 +1467,13 @@ var DroidClient = class _DroidClient extends EventEmitter {
1379
1467
  params: {}
1380
1468
  };
1381
1469
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1382
- const response = ListMcpToolsResponseSchema.parse(rawResponse);
1383
- return response;
1470
+ const response2 = ListMcpToolsResponseSchema.parse(rawResponse);
1471
+ return response2;
1384
1472
  }
1385
1473
  async listTools(params = {}) {
1386
1474
  const schemasRequested = params.includeSchemas === true;
1387
1475
  const request = {
1388
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1476
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1389
1477
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1390
1478
  type: "request",
1391
1479
  jsonrpc: JSONRPC_VERSION2,
@@ -1394,17 +1482,17 @@ var DroidClient = class _DroidClient extends EventEmitter {
1394
1482
  params
1395
1483
  };
1396
1484
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1397
- const response = ListToolsResponseSchema.parse(rawResponse);
1398
- if (!schemasRequested || !("result" in response)) {
1399
- return response;
1485
+ const response2 = ListToolsResponseSchema.parse(rawResponse);
1486
+ if (!schemasRequested || !("result" in response2)) {
1487
+ return response2;
1400
1488
  }
1401
1489
  const strictResult = ListToolsResultWithSchemasSchema.safeParse(
1402
- response.result
1490
+ response2.result
1403
1491
  );
1404
1492
  if (strictResult.success) {
1405
- return response;
1493
+ return response2;
1406
1494
  }
1407
- const result = ListToolsResultSchema.parse(response.result);
1495
+ const result = ListToolsResultSchema.parse(response2.result);
1408
1496
  const missingSchemaIds = [];
1409
1497
  for (const tool2 of result.tools) {
1410
1498
  if (tool2.schemas === void 0) {
@@ -1425,7 +1513,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1425
1513
  );
1426
1514
  }
1427
1515
  const request = {
1428
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1516
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1429
1517
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1430
1518
  type: "request",
1431
1519
  jsonrpc: JSONRPC_VERSION2,
@@ -1434,8 +1522,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1434
1522
  params: {}
1435
1523
  };
1436
1524
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1437
- const response = ListMcpServersResponseSchema.parse(rawResponse);
1438
- return response;
1525
+ const response2 = ListMcpServersResponseSchema.parse(rawResponse);
1526
+ return response2;
1439
1527
  }
1440
1528
  async toggleMcpTool(serverName, toolName, enabled) {
1441
1529
  if (!this.currentSessionId) {
@@ -1444,7 +1532,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1444
1532
  );
1445
1533
  }
1446
1534
  const request = {
1447
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1535
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1448
1536
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1449
1537
  type: "request",
1450
1538
  jsonrpc: JSONRPC_VERSION2,
@@ -1453,8 +1541,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1453
1541
  params: { serverName, toolName, enabled }
1454
1542
  };
1455
1543
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1456
- const response = ToggleMcpToolResponseSchema.parse(rawResponse);
1457
- return response;
1544
+ const response2 = ToggleMcpToolResponseSchema.parse(rawResponse);
1545
+ return response2;
1458
1546
  }
1459
1547
  async submitMcpAuthCode(params) {
1460
1548
  if (!this.currentSessionId) {
@@ -1463,7 +1551,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1463
1551
  );
1464
1552
  }
1465
1553
  const request = {
1466
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1554
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1467
1555
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1468
1556
  type: "request",
1469
1557
  jsonrpc: JSONRPC_VERSION2,
@@ -1472,8 +1560,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1472
1560
  params
1473
1561
  };
1474
1562
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1475
- const response = SubmitMcpAuthCodeResponseSchema.parse(rawResponse);
1476
- return response;
1563
+ const response2 = SubmitMcpAuthCodeResponseSchema.parse(rawResponse);
1564
+ return response2;
1477
1565
  }
1478
1566
  async submitMcpAuthError(params) {
1479
1567
  if (!this.currentSessionId) {
@@ -1482,7 +1570,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1482
1570
  );
1483
1571
  }
1484
1572
  const request = {
1485
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1573
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1486
1574
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1487
1575
  type: "request",
1488
1576
  jsonrpc: JSONRPC_VERSION2,
@@ -1491,8 +1579,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1491
1579
  params
1492
1580
  };
1493
1581
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1494
- const response = SubmitMcpAuthErrorResponseSchema.parse(rawResponse);
1495
- return response;
1582
+ const response2 = SubmitMcpAuthErrorResponseSchema.parse(rawResponse);
1583
+ return response2;
1496
1584
  }
1497
1585
  async listSkills(params = {}) {
1498
1586
  if (!this.currentSessionId) {
@@ -1501,7 +1589,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1501
1589
  );
1502
1590
  }
1503
1591
  const request = {
1504
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1592
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1505
1593
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1506
1594
  type: "request",
1507
1595
  jsonrpc: JSONRPC_VERSION2,
@@ -1510,8 +1598,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1510
1598
  params
1511
1599
  };
1512
1600
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1513
- const response = ListSkillsResponseSchema.parse(rawResponse);
1514
- return response;
1601
+ const response2 = ListSkillsResponseSchema.parse(rawResponse);
1602
+ return response2;
1515
1603
  }
1516
1604
  async setSkillDisabled(skillName, disabled, settingsLevel) {
1517
1605
  if (!this.currentSessionId) {
@@ -1520,7 +1608,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1520
1608
  );
1521
1609
  }
1522
1610
  const request = {
1523
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1611
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1524
1612
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1525
1613
  type: "request",
1526
1614
  jsonrpc: JSONRPC_VERSION2,
@@ -1538,7 +1626,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1538
1626
  );
1539
1627
  }
1540
1628
  const request = {
1541
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1629
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1542
1630
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1543
1631
  type: "request",
1544
1632
  jsonrpc: JSONRPC_VERSION2,
@@ -1547,8 +1635,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1547
1635
  params: {}
1548
1636
  };
1549
1637
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1550
- const response = ListCommandsResponseSchema.parse(rawResponse);
1551
- return response;
1638
+ const response2 = ListCommandsResponseSchema.parse(rawResponse);
1639
+ return response2;
1552
1640
  }
1553
1641
  async getContextStats() {
1554
1642
  if (!this.currentSessionId) {
@@ -1557,7 +1645,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1557
1645
  );
1558
1646
  }
1559
1647
  const request = {
1560
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1648
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1561
1649
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1562
1650
  type: "request",
1563
1651
  jsonrpc: JSONRPC_VERSION2,
@@ -1566,8 +1654,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
1566
1654
  params: {}
1567
1655
  };
1568
1656
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1569
- const response = GetContextStatsResponseSchema.parse(rawResponse);
1570
- return response;
1657
+ const response2 = GetContextStatsResponseSchema.parse(rawResponse);
1658
+ return response2;
1571
1659
  }
1572
1660
  async getContextBreakdown() {
1573
1661
  if (!this.currentSessionId) {
@@ -1576,7 +1664,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1576
1664
  );
1577
1665
  }
1578
1666
  const request = {
1579
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1667
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1580
1668
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1581
1669
  type: "request",
1582
1670
  jsonrpc: JSONRPC_VERSION2,
@@ -1585,27 +1673,45 @@ var DroidClient = class _DroidClient extends EventEmitter {
1585
1673
  params: {}
1586
1674
  };
1587
1675
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1588
- const response = GetContextBreakdownResponseSchema.parse(rawResponse);
1589
- return response;
1676
+ const response2 = GetContextBreakdownResponseSchema.parse(rawResponse);
1677
+ return response2;
1678
+ }
1679
+ async killBackgroundProcess(processId) {
1680
+ if (!this.currentSessionId) {
1681
+ throw new SessionError(
1682
+ "No active session. Call initializeSession or loadSession first."
1683
+ );
1684
+ }
1685
+ const request = {
1686
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1687
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1688
+ type: "request",
1689
+ jsonrpc: JSONRPC_VERSION2,
1690
+ id: uuidv4(),
1691
+ method: "droid.kill_background_process" /* KILL_BACKGROUND_PROCESS */,
1692
+ params: { processId }
1693
+ };
1694
+ const rawResponse = await this.sendRequest(request, this.requestTimeout);
1695
+ return KillBackgroundProcessResponseSchema.parse(rawResponse);
1590
1696
  }
1591
- async submitBugReport(userComment, clientLogs, source) {
1697
+ async submitBugReport(userComment, clientLogs, source, tuiLogs) {
1592
1698
  if (!this.currentSessionId) {
1593
1699
  throw new SessionError(
1594
1700
  "No active session. Call initializeSession or loadSession first."
1595
1701
  );
1596
1702
  }
1597
1703
  const request = {
1598
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1704
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1599
1705
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1600
1706
  type: "request",
1601
1707
  jsonrpc: JSONRPC_VERSION2,
1602
1708
  id: uuidv4(),
1603
1709
  method: "droid.submit_bug_report" /* SUBMIT_BUG_REPORT */,
1604
- params: { userComment, clientLogs, source }
1710
+ params: { userComment, clientLogs, source, tuiLogs }
1605
1711
  };
1606
1712
  const rawResponse = await this.sendRequest(request, this.requestTimeout);
1607
- const response = SubmitBugReportResponseSchema.parse(rawResponse);
1608
- return response;
1713
+ const response2 = SubmitBugReportResponseSchema.parse(rawResponse);
1714
+ return response2;
1609
1715
  }
1610
1716
  async getRewindInfo(params, timeout = this.requestTimeout) {
1611
1717
  if (!this.currentSessionId) {
@@ -1614,7 +1720,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1614
1720
  );
1615
1721
  }
1616
1722
  const request = {
1617
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1723
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1618
1724
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1619
1725
  type: "request",
1620
1726
  jsonrpc: JSONRPC_VERSION2,
@@ -1635,7 +1741,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1635
1741
  );
1636
1742
  }
1637
1743
  const request = {
1638
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1744
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1639
1745
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1640
1746
  type: "request",
1641
1747
  jsonrpc: JSONRPC_VERSION2,
@@ -1662,7 +1768,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1662
1768
  );
1663
1769
  }
1664
1770
  const request = {
1665
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1771
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1666
1772
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1667
1773
  type: "request",
1668
1774
  jsonrpc: JSONRPC_VERSION2,
@@ -1676,6 +1782,44 @@ var DroidClient = class _DroidClient extends EventEmitter {
1676
1782
  const rawResponse = await this.sendRequest(request, COMPACTION_TIMEOUT2);
1677
1783
  return CompactSessionResponseSchema.parse(rawResponse);
1678
1784
  }
1785
+ /** Summarizes the session the way `/handoff` does, without writing anything. */
1786
+ async getHandoffSummary() {
1787
+ if (!this.currentSessionId) {
1788
+ throw new SessionError(
1789
+ "No active session. Call initializeSession or loadSession first."
1790
+ );
1791
+ }
1792
+ const request = {
1793
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1794
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1795
+ type: "request",
1796
+ jsonrpc: JSONRPC_VERSION2,
1797
+ id: uuidv4(),
1798
+ method: "droid.get_handoff_summary" /* GET_HANDOFF_SUMMARY */,
1799
+ params: {}
1800
+ };
1801
+ const rawResponse = await this.sendRequest(request, COMPACTION_TIMEOUT2);
1802
+ return GetHandoffSummaryResponseSchema.parse(rawResponse);
1803
+ }
1804
+ /** Writes the short summary people joining a shared copy read first. */
1805
+ async summarizeForMembers(params) {
1806
+ if (!this.currentSessionId) {
1807
+ throw new SessionError(
1808
+ "No active session. Call initializeSession or loadSession first."
1809
+ );
1810
+ }
1811
+ const request = {
1812
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1813
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1814
+ type: "request",
1815
+ jsonrpc: JSONRPC_VERSION2,
1816
+ id: uuidv4(),
1817
+ method: "droid.summarize_for_members" /* SUMMARIZE_FOR_MEMBERS */,
1818
+ params: { handoffSummary: params.handoffSummary }
1819
+ };
1820
+ const rawResponse = await this.sendRequest(request, COMPACTION_TIMEOUT2);
1821
+ return SummarizeForMembersResponseSchema.parse(rawResponse);
1822
+ }
1679
1823
  async forkSession(params) {
1680
1824
  if (!this.currentSessionId) {
1681
1825
  throw new SessionError(
@@ -1683,7 +1827,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1683
1827
  );
1684
1828
  }
1685
1829
  const request = {
1686
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1830
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1687
1831
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1688
1832
  type: "request",
1689
1833
  jsonrpc: JSONRPC_VERSION2,
@@ -1701,7 +1845,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1701
1845
  );
1702
1846
  }
1703
1847
  const request = {
1704
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1848
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1705
1849
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1706
1850
  type: "request",
1707
1851
  jsonrpc: JSONRPC_VERSION2,
@@ -1715,15 +1859,15 @@ var DroidClient = class _DroidClient extends EventEmitter {
1715
1859
  return RenameSessionResponseSchema.parse(rawResponse);
1716
1860
  }
1717
1861
  async warmupCache() {
1718
- const response = {
1719
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
1862
+ const response2 = {
1863
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
1720
1864
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
1721
1865
  type: "response",
1722
1866
  jsonrpc: JSONRPC_VERSION2,
1723
1867
  id: uuidv4(),
1724
1868
  result: {}
1725
1869
  };
1726
- return WarmupCacheResponseSchema.parse(response);
1870
+ return WarmupCacheResponseSchema.parse(response2);
1727
1871
  }
1728
1872
  onNotification(callback, filter) {
1729
1873
  const handler = (notification) => {
@@ -1754,14 +1898,27 @@ var DroidClient = class _DroidClient extends EventEmitter {
1754
1898
  setPermissionHandler(handler) {
1755
1899
  this.permissionHandlers.set(
1756
1900
  "default",
1757
- async (event) => await handler(event)
1901
+ async (event, setResponseMetadata) => await handler(event, setResponseMetadata)
1758
1902
  );
1759
1903
  }
1760
1904
  clearPermissionHandler() {
1761
1905
  this.permissionHandlers.delete("default");
1762
1906
  }
1763
1907
  setAskUserHandler(handler) {
1764
- this.askUserHandlers.set("default", async (event) => await handler(event));
1908
+ this.askUserHandlers.set(
1909
+ "default",
1910
+ async (event, setResponseMetadata) => await handler(event, setResponseMetadata)
1911
+ );
1912
+ const held = this.heldQueuedAskUserRequests;
1913
+ this.heldQueuedAskUserRequests = [];
1914
+ for (const event of held) {
1915
+ void this.answerAskUserRequest(event).catch((error) => {
1916
+ this.reportHandlerFailure(
1917
+ error,
1918
+ "Failed to answer a held queued ask-user request"
1919
+ );
1920
+ });
1921
+ }
1765
1922
  }
1766
1923
  clearAskUserHandler() {
1767
1924
  this.askUserHandlers.delete("default");
@@ -1769,14 +1926,14 @@ var DroidClient = class _DroidClient extends EventEmitter {
1769
1926
  setPickOptionsHandler(handler) {
1770
1927
  this.pickOptionsHandlers.set(
1771
1928
  "default",
1772
- async (event) => await handler(event)
1929
+ async (event, setResponseMetadata) => await handler(event, setResponseMetadata)
1773
1930
  );
1774
1931
  }
1775
1932
  clearPickOptionsHandler() {
1776
1933
  this.pickOptionsHandlers.delete("default");
1777
1934
  }
1778
1935
  async close(options) {
1779
- const closeError = new ConnectionError(
1936
+ const closeError = new DroidConnectionEndedError(
1780
1937
  "Droid client closed: pending requests cancelled"
1781
1938
  );
1782
1939
  if (options?.closeTransportFirst) {
@@ -1786,6 +1943,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1786
1943
  this.rejectAllPending(closeError);
1787
1944
  this.permissionHandlers.clear();
1788
1945
  this.askUserHandlers.clear();
1946
+ this.heldQueuedAskUserRequests = [];
1789
1947
  this.removeAllListeners();
1790
1948
  }
1791
1949
  return;
@@ -1793,6 +1951,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
1793
1951
  this.rejectAllPending(closeError);
1794
1952
  this.permissionHandlers.clear();
1795
1953
  this.askUserHandlers.clear();
1954
+ this.heldQueuedAskUserRequests = [];
1796
1955
  this.pickOptionsHandlers.clear();
1797
1956
  this.removeAllListeners();
1798
1957
  await this.transport.close();
@@ -1829,16 +1988,31 @@ var DroidClient = class _DroidClient extends EventEmitter {
1829
1988
  } catch {
1830
1989
  }
1831
1990
  }
1991
+ /** `_meta` for an answer this client gives itself: its caller and the current trace context. */
1992
+ responseMetadata() {
1993
+ const meta = {};
1994
+ const requestAttribution = this.getRequestAttribution?.();
1995
+ if (requestAttribution) meta.requestAttribution = requestAttribution;
1996
+ this.tracing.injectContext(meta, this.tracing.getCurrentContext());
1997
+ return meta;
1998
+ }
1999
+ async sendResponse(response2, hostMeta) {
2000
+ const meta = hostMeta ?? this.responseMetadata();
2001
+ await this.transport.send(
2002
+ Object.keys(meta).length > 0 ? { ...response2, _meta: meta } : response2
2003
+ );
2004
+ }
1832
2005
  async sendRequest(request, timeout, sessionId) {
1833
2006
  if (this.transportError) {
1834
2007
  throw this.transportError;
1835
2008
  }
1836
- const requestAttribution = this.getRequestAttribution?.();
1837
- const _meta = {
1838
- ...requestAttribution && { requestAttribution }
1839
- };
2009
+ const requestAttribution = request._meta?.requestAttribution ?? this.getRequestAttribution?.();
2010
+ const _meta = { ...request._meta };
2011
+ delete _meta.requestAttribution;
2012
+ if (requestAttribution) _meta.requestAttribution = requestAttribution;
1840
2013
  this.tracing.injectContext(_meta, this.tracing.getCurrentContext());
1841
- const requestWithMeta = _meta.traceparent || _meta.requestAttribution ? { ...request, _meta } : request;
2014
+ const { _meta: _originalMeta, ...requestWithoutMeta } = request;
2015
+ const requestWithMeta = Object.keys(_meta).length > 0 ? { ...request, _meta } : requestWithoutMeta;
1842
2016
  const responsePromise = new Promise(
1843
2017
  (resolve2, reject) => {
1844
2018
  const timeoutId = timeout ? setTimeout(() => {
@@ -1862,12 +2036,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
1862
2036
  );
1863
2037
  void responsePromise.catch(() => {
1864
2038
  });
2039
+ void this.writeRequest(requestWithMeta);
2040
+ return responsePromise;
2041
+ }
2042
+ async writeRequest(request) {
1865
2043
  try {
1866
2044
  this.logger.info("[DroidClient] [daemon -> droid] sending request", {
1867
2045
  requestId: request.id,
1868
2046
  method: request.method
1869
2047
  });
1870
- await this.transport.send(requestWithMeta);
2048
+ await this.transport.send(request);
1871
2049
  } catch (error) {
1872
2050
  const pending = this.pendingRequests.get(request.id);
1873
2051
  if (pending) {
@@ -1883,7 +2061,6 @@ var DroidClient = class _DroidClient extends EventEmitter {
1883
2061
  );
1884
2062
  }
1885
2063
  }
1886
- return responsePromise;
1887
2064
  }
1888
2065
  async handleMessage(message) {
1889
2066
  try {
@@ -1896,124 +2073,133 @@ var DroidClient = class _DroidClient extends EventEmitter {
1896
2073
  this.logger.warn("[DroidClient] Failed to handle message");
1897
2074
  }
1898
2075
  }
1899
- handleResponse(response) {
2076
+ handleResponse(response2) {
1900
2077
  try {
1901
- const responseId = getResponseId(response);
2078
+ const responseId = getResponseId(response2);
1902
2079
  if (responseId !== null && responseId !== void 0) {
1903
2080
  const pending = this.pendingRequests.get(responseId);
1904
2081
  if (pending) {
1905
2082
  let validatedResponse;
1906
2083
  switch (pending.method) {
1907
2084
  case "droid.initialize_session" /* INITIALIZE_SESSION */:
1908
- validatedResponse = InitializeSessionResponseSchema.parse(response);
2085
+ validatedResponse = InitializeSessionResponseSchema.parse(response2);
1909
2086
  break;
1910
2087
  case "droid.load_session" /* LOAD_SESSION */:
1911
- validatedResponse = LoadSessionResponseSchema.parse(response);
2088
+ validatedResponse = LoadSessionResponseSchema.parse(response2);
1912
2089
  break;
1913
2090
  case "droid.add_user_message" /* ADD_USER_MESSAGE */:
1914
- validatedResponse = AddUserMessageResponseSchema.parse(response);
2091
+ validatedResponse = AddUserMessageResponseSchema.parse(response2);
1915
2092
  break;
1916
2093
  case "droid.append_messages" /* APPEND_MESSAGES */:
1917
- validatedResponse = AppendMessagesResponseSchema.parse(response);
2094
+ validatedResponse = AppendMessagesResponseSchema.parse(response2);
1918
2095
  break;
1919
2096
  case "droid.resolve_queued_user_message" /* RESOLVE_QUEUED_USER_MESSAGE */:
1920
- validatedResponse = ResolveQueuedUserMessageResponseSchema.parse(response);
2097
+ validatedResponse = ResolveQueuedUserMessageResponseSchema.parse(response2);
1921
2098
  break;
1922
2099
  case "droid.interrupt_session" /* INTERRUPT_SESSION */:
1923
- validatedResponse = InterruptSessionResponseSchema.parse(response);
2100
+ validatedResponse = InterruptSessionResponseSchema.parse(response2);
1924
2101
  break;
1925
2102
  case "droid.close_session" /* CLOSE_SESSION */:
1926
- validatedResponse = CloseSessionResponseSchema.parse(response);
2103
+ validatedResponse = CloseSessionResponseSchema.parse(response2);
1927
2104
  break;
1928
2105
  case "droid.update_session_settings" /* UPDATE_SESSION_SETTINGS */:
1929
- validatedResponse = UpdateSessionSettingsResponseSchema.parse(response);
2106
+ validatedResponse = UpdateSessionSettingsResponseSchema.parse(response2);
1930
2107
  break;
1931
2108
  case "droid.change_working_directory" /* CHANGE_WORKING_DIRECTORY */:
1932
- validatedResponse = ChangeWorkingDirectoryResponseSchema.parse(response);
2109
+ validatedResponse = ChangeWorkingDirectoryResponseSchema.parse(response2);
1933
2110
  break;
1934
2111
  case "droid.toggle_mcp_server" /* TOGGLE_MCP_SERVER */:
1935
- validatedResponse = ToggleMcpServerResponseSchema.parse(response);
2112
+ validatedResponse = ToggleMcpServerResponseSchema.parse(response2);
1936
2113
  break;
1937
2114
  case "droid.authenticate_mcp_server" /* AUTHENTICATE_MCP_SERVER */:
1938
- validatedResponse = AuthenticateMcpServerResponseSchema.parse(response);
2115
+ validatedResponse = AuthenticateMcpServerResponseSchema.parse(response2);
1939
2116
  break;
1940
2117
  case "droid.cancel_mcp_auth" /* CANCEL_MCP_AUTH */:
1941
- validatedResponse = CancelMcpAuthResponseSchema.parse(response);
2118
+ validatedResponse = CancelMcpAuthResponseSchema.parse(response2);
1942
2119
  break;
1943
2120
  case "droid.clear_mcp_auth" /* CLEAR_MCP_AUTH */:
1944
- validatedResponse = ClearMcpAuthResponseSchema.parse(response);
2121
+ validatedResponse = ClearMcpAuthResponseSchema.parse(response2);
1945
2122
  break;
1946
2123
  case "droid.add_mcp_server" /* ADD_MCP_SERVER */:
1947
- validatedResponse = AddMcpServerResponseSchema.parse(response);
2124
+ validatedResponse = AddMcpServerResponseSchema.parse(response2);
1948
2125
  break;
1949
2126
  case "droid.remove_mcp_server" /* REMOVE_MCP_SERVER */:
1950
- validatedResponse = RemoveMcpServerResponseSchema.parse(response);
2127
+ validatedResponse = RemoveMcpServerResponseSchema.parse(response2);
1951
2128
  break;
1952
2129
  case "droid.list_models" /* LIST_MODELS */:
1953
- validatedResponse = ListModelsResponseSchema.parse(response);
2130
+ validatedResponse = ListModelsResponseSchema.parse(response2);
1954
2131
  break;
1955
2132
  case "droid.list_mcp_registry" /* LIST_MCP_REGISTRY */:
1956
- validatedResponse = ListMcpRegistryResponseSchema.parse(response);
2133
+ validatedResponse = ListMcpRegistryResponseSchema.parse(response2);
1957
2134
  break;
1958
2135
  case "droid.list_mcp_tools" /* LIST_MCP_TOOLS */:
1959
- validatedResponse = ListMcpToolsResponseSchema.parse(response);
2136
+ validatedResponse = ListMcpToolsResponseSchema.parse(response2);
1960
2137
  break;
1961
2138
  case "droid.list_tools" /* LIST_TOOLS */:
1962
- validatedResponse = ListToolsResponseSchema.parse(response);
2139
+ validatedResponse = ListToolsResponseSchema.parse(response2);
1963
2140
  break;
1964
2141
  case "droid.refresh_connector_tool_catalog" /* REFRESH_CONNECTOR_TOOL_CATALOG */:
1965
- validatedResponse = RefreshConnectorToolCatalogResponseSchema.parse(response);
2142
+ validatedResponse = RefreshConnectorToolCatalogResponseSchema.parse(response2);
1966
2143
  break;
1967
2144
  case "droid.toggle_mcp_tool" /* TOGGLE_MCP_TOOL */:
1968
- validatedResponse = ToggleMcpToolResponseSchema.parse(response);
2145
+ validatedResponse = ToggleMcpToolResponseSchema.parse(response2);
1969
2146
  break;
1970
2147
  case "droid.submit_mcp_auth_code" /* SUBMIT_MCP_AUTH_CODE */:
1971
- validatedResponse = SubmitMcpAuthCodeResponseSchema.parse(response);
2148
+ validatedResponse = SubmitMcpAuthCodeResponseSchema.parse(response2);
1972
2149
  break;
1973
2150
  case "droid.submit_mcp_auth_error" /* SUBMIT_MCP_AUTH_ERROR */:
1974
- validatedResponse = SubmitMcpAuthErrorResponseSchema.parse(response);
2151
+ validatedResponse = SubmitMcpAuthErrorResponseSchema.parse(response2);
1975
2152
  break;
1976
2153
  case "droid.kill_worker_session" /* KILL_WORKER_SESSION */:
1977
- validatedResponse = KillWorkerSessionResponseSchema.parse(response);
2154
+ validatedResponse = KillWorkerSessionResponseSchema.parse(response2);
1978
2155
  break;
1979
2156
  case "droid.list_skills" /* LIST_SKILLS */:
1980
- validatedResponse = ListSkillsResponseSchema.parse(response);
2157
+ validatedResponse = ListSkillsResponseSchema.parse(response2);
1981
2158
  break;
1982
2159
  case "droid.set_skill_disabled" /* SET_SKILL_DISABLED */:
1983
- validatedResponse = SetSkillDisabledResponseSchema.parse(response);
2160
+ validatedResponse = SetSkillDisabledResponseSchema.parse(response2);
1984
2161
  break;
1985
2162
  case "droid.list_commands" /* LIST_COMMANDS */:
1986
- validatedResponse = ListCommandsResponseSchema.parse(response);
2163
+ validatedResponse = ListCommandsResponseSchema.parse(response2);
1987
2164
  break;
1988
2165
  case "droid.submit_bug_report" /* SUBMIT_BUG_REPORT */:
1989
- validatedResponse = SubmitBugReportResponseSchema.parse(response);
2166
+ validatedResponse = SubmitBugReportResponseSchema.parse(response2);
1990
2167
  break;
1991
2168
  case "droid.list_mcp_servers" /* LIST_MCP_SERVERS */:
1992
- validatedResponse = ListMcpServersResponseSchema.parse(response);
2169
+ validatedResponse = ListMcpServersResponseSchema.parse(response2);
1993
2170
  break;
1994
2171
  case "droid.get_rewind_info" /* GET_REWIND_INFO */:
1995
- validatedResponse = GetRewindInfoResponseSchema.parse(response);
2172
+ validatedResponse = GetRewindInfoResponseSchema.parse(response2);
1996
2173
  break;
1997
2174
  case "droid.execute_rewind" /* EXECUTE_REWIND */:
1998
- validatedResponse = ExecuteRewindResponseSchema.parse(response);
2175
+ validatedResponse = ExecuteRewindResponseSchema.parse(response2);
1999
2176
  break;
2000
2177
  case "droid.compact_session" /* COMPACT_SESSION */:
2001
- validatedResponse = CompactSessionResponseSchema.parse(response);
2178
+ validatedResponse = CompactSessionResponseSchema.parse(response2);
2179
+ break;
2180
+ case "droid.get_handoff_summary" /* GET_HANDOFF_SUMMARY */:
2181
+ validatedResponse = GetHandoffSummaryResponseSchema.parse(response2);
2182
+ break;
2183
+ case "droid.summarize_for_members" /* SUMMARIZE_FOR_MEMBERS */:
2184
+ validatedResponse = SummarizeForMembersResponseSchema.parse(response2);
2002
2185
  break;
2003
2186
  case "droid.fork_session" /* FORK_SESSION */:
2004
- validatedResponse = ForkSessionResponseSchema.parse(response);
2187
+ validatedResponse = ForkSessionResponseSchema.parse(response2);
2005
2188
  break;
2006
2189
  case "droid.rename_session" /* RENAME_SESSION */:
2007
- validatedResponse = RenameSessionResponseSchema.parse(response);
2190
+ validatedResponse = RenameSessionResponseSchema.parse(response2);
2008
2191
  break;
2009
2192
  case "droid.get_context_stats" /* GET_CONTEXT_STATS */:
2010
- validatedResponse = GetContextStatsResponseSchema.parse(response);
2193
+ validatedResponse = GetContextStatsResponseSchema.parse(response2);
2011
2194
  break;
2012
2195
  case "droid.get_context_breakdown" /* GET_CONTEXT_BREAKDOWN */:
2013
- validatedResponse = GetContextBreakdownResponseSchema.parse(response);
2196
+ validatedResponse = GetContextBreakdownResponseSchema.parse(response2);
2014
2197
  break;
2015
2198
  case "droid.warmup_cache" /* WARMUP_CACHE */:
2016
- validatedResponse = WarmupCacheResponseSchema.parse(response);
2199
+ validatedResponse = WarmupCacheResponseSchema.parse(response2);
2200
+ break;
2201
+ case "droid.kill_background_process" /* KILL_BACKGROUND_PROCESS */:
2202
+ validatedResponse = KillBackgroundProcessResponseSchema.parse(response2);
2017
2203
  break;
2018
2204
  default: {
2019
2205
  const exhaustiveCheck = pending.method;
@@ -2031,7 +2217,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2031
2217
  this.settleRequest(responseId);
2032
2218
  pending.resolve(validatedResponse);
2033
2219
  } else {
2034
- const validatedResponse = JsonRpcBaseResponseSchema.parse(response);
2220
+ const validatedResponse = JsonRpcBaseResponseSchema.parse(response2);
2035
2221
  this.logger.info(
2036
2222
  "[DroidClient] Received response for request not in pending map",
2037
2223
  {
@@ -2041,7 +2227,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2041
2227
  }
2042
2228
  }
2043
2229
  } catch (error) {
2044
- const responseId = getResponseId(response);
2230
+ const responseId = getResponseId(response2);
2045
2231
  const pending = responseId !== null && responseId !== void 0 ? this.pendingRequests.get(responseId) : void 0;
2046
2232
  const zodIssues = error instanceof z.ZodError ? error.issues.map((issue) => ({
2047
2233
  path: issue.path,
@@ -2077,7 +2263,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2077
2263
  const syntheticFailure = {
2078
2264
  jsonrpc: JSONRPC_VERSION2,
2079
2265
  type: "response",
2080
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2266
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2081
2267
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2082
2268
  id: responseId,
2083
2269
  error: {
@@ -2096,8 +2282,10 @@ var DroidClient = class _DroidClient extends EventEmitter {
2096
2282
  const notification = validatedEvent.params.notification;
2097
2283
  if (notification.type === "agent_turn_completed" /* AGENT_TURN_COMPLETED */) {
2098
2284
  this.completedTurnObserved = true;
2099
- } else if (notification.type === "droid_working_state_changed" /* DROID_WORKING_STATE_CHANGED */ && notification.newState !== "idle" /* Idle */) {
2285
+ } else if (notification.type === "droid_working_state_changed" /* DROID_WORKING_STATE_CHANGED */ && DROID_RUNNING_STATES.has(notification.newState)) {
2100
2286
  this.completedTurnObserved = false;
2287
+ } else if (notification.type === "permission_resolved" /* PERMISSION_RESOLVED */) {
2288
+ this.pendingPermissionRequests.get(notification.requestId)?.withdraw();
2101
2289
  }
2102
2290
  this.emit("session_notification" /* SESSION_NOTIFICATION */, validatedEvent);
2103
2291
  } else if (validatedEvent.method === "droid.request_permission" /* REQUEST_PERMISSION */) {
@@ -2106,6 +2294,8 @@ var DroidClient = class _DroidClient extends EventEmitter {
2106
2294
  await this.handleAskUserRequest(validatedEvent);
2107
2295
  } else if (validatedEvent.method === "droid.pick_options" /* PICK_OPTIONS */) {
2108
2296
  await this.handlePickOptionsRequest(validatedEvent);
2297
+ } else if (validatedEvent.method === "droid.disconnect" /* DISCONNECT */) {
2298
+ this.disconnected = true;
2109
2299
  }
2110
2300
  } catch (error) {
2111
2301
  this.logDroppedEventParseError(error, event);
@@ -2130,7 +2320,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2130
2320
  const errorResponse = {
2131
2321
  jsonrpc: JSONRPC_VERSION2,
2132
2322
  type: "response",
2133
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2323
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2134
2324
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2135
2325
  id,
2136
2326
  error: {
@@ -2140,7 +2330,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2140
2330
  }
2141
2331
  };
2142
2332
  try {
2143
- await this.transport.send(errorResponse);
2333
+ await this.sendResponse(errorResponse, void 0);
2144
2334
  } catch (sendError) {
2145
2335
  this.logger.exception(
2146
2336
  sendError,
@@ -2185,6 +2375,13 @@ var DroidClient = class _DroidClient extends EventEmitter {
2185
2375
  });
2186
2376
  }
2187
2377
  }
2378
+ reportHandlerFailure(error, message) {
2379
+ if (error instanceof RequestCancelledError || isDroidConnectionEnded(error)) {
2380
+ this.logger.info(message, { cause: error });
2381
+ } else {
2382
+ this.logger.exception(error, message);
2383
+ }
2384
+ }
2188
2385
  async handlePermissionRequest(event) {
2189
2386
  if (this.pendingPermissionRequests.has(event.id)) {
2190
2387
  this.logger.info(
@@ -2199,12 +2396,14 @@ var DroidClient = class _DroidClient extends EventEmitter {
2199
2396
  requestId: event.id,
2200
2397
  count: event.params.toolUses.length
2201
2398
  });
2399
+ let hostMeta;
2400
+ const setResponseMetadata = (meta) => {
2401
+ hostMeta = meta;
2402
+ };
2202
2403
  this.emit("request_permission" /* REQUEST_PERMISSION */, event);
2203
- let selectedOption = "cancel" /* Cancel */;
2204
- let comment;
2205
- let editedSpecContent;
2206
- let clientStartsHandoffSession;
2207
- let specHandoffId;
2404
+ let result = {
2405
+ selectedOption: "cancel" /* Cancel */
2406
+ };
2208
2407
  const handler = this.permissionHandlers.get("default");
2209
2408
  if (handler) {
2210
2409
  try {
@@ -2214,15 +2413,25 @@ var DroidClient = class _DroidClient extends EventEmitter {
2214
2413
  requestId: event.id
2215
2414
  }
2216
2415
  );
2217
- const handlerPromise = new Promise(
2218
- (resolve2, reject) => {
2219
- this.pendingPermissionRequests.set(event.id, { reject });
2220
- handler(event).then(resolve2).catch(reject).finally(() => {
2416
+ const handlerPromise = new Promise((resolve2, reject) => {
2417
+ this.pendingPermissionRequests.set(event.id, {
2418
+ reject,
2419
+ withdraw: () => {
2221
2420
  this.pendingPermissionRequests.delete(event.id);
2222
- });
2223
- }
2224
- );
2421
+ resolve2(PERMISSION_WITHDRAWN);
2422
+ }
2423
+ });
2424
+ handler(event, setResponseMetadata).then(resolve2).catch(reject).finally(() => {
2425
+ this.pendingPermissionRequests.delete(event.id);
2426
+ });
2427
+ });
2225
2428
  const handlerResult = await handlerPromise;
2429
+ if (handlerResult === PERMISSION_WITHDRAWN) {
2430
+ this.logger.info("[DroidClient] Permission request withdrawn", {
2431
+ requestId: event.id
2432
+ });
2433
+ return;
2434
+ }
2226
2435
  if (typeof handlerResult === "string" && handlerResult === "proceed_edit" /* ProceedEdit */) {
2227
2436
  throw new DroidClientError(
2228
2437
  "Permission handler returned proceed_edit without editedSpecContent"
@@ -2232,11 +2441,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2232
2441
  typeof handlerResult === "string" ? { selectedOption: handlerResult } : handlerResult
2233
2442
  );
2234
2443
  if (parsedHandlerResult.success) {
2235
- selectedOption = parsedHandlerResult.data.selectedOption;
2236
- comment = parsedHandlerResult.data.comment;
2237
- editedSpecContent = parsedHandlerResult.data.editedSpecContent;
2238
- clientStartsHandoffSession = parsedHandlerResult.data.clientStartsHandoffSession;
2239
- specHandoffId = parsedHandlerResult.data.specHandoffId;
2444
+ result = parsedHandlerResult.data;
2240
2445
  } else {
2241
2446
  throw new DroidClientError(
2242
2447
  "Permission handler returned invalid result",
@@ -2245,19 +2450,20 @@ var DroidClient = class _DroidClient extends EventEmitter {
2245
2450
  }
2246
2451
  );
2247
2452
  }
2248
- if (!event.params.options.some(
2249
- (option) => option.value === selectedOption
2453
+ if (!isOfferedPermissionOutcome(
2454
+ event.params.options,
2455
+ result.selectedOption
2250
2456
  )) {
2251
2457
  throw new DroidClientError(
2252
- `Permission handler selected unavailable option: ${selectedOption}`
2458
+ `Permission handler selected unavailable option: ${result.selectedOption}`
2253
2459
  );
2254
2460
  }
2255
2461
  this.logger.info("[DroidClient] Permission handler resolved", {
2256
2462
  requestId: event.id,
2257
- result: selectedOption
2463
+ result: result.selectedOption
2258
2464
  });
2259
2465
  } catch (error) {
2260
- this.logger.exception(
2466
+ this.reportHandlerFailure(
2261
2467
  error,
2262
2468
  "Failed to handle permission request (processing)"
2263
2469
  );
@@ -2272,7 +2478,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2272
2478
  const cancelResponse = {
2273
2479
  type: "response",
2274
2480
  jsonrpc: JSONRPC_VERSION2,
2275
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2481
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2276
2482
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2277
2483
  id: event.id,
2278
2484
  result: {
@@ -2280,9 +2486,9 @@ var DroidClient = class _DroidClient extends EventEmitter {
2280
2486
  }
2281
2487
  };
2282
2488
  try {
2283
- await this.transport.send(cancelResponse);
2489
+ await this.sendResponse(cancelResponse, hostMeta);
2284
2490
  } catch (sendError) {
2285
- this.logger.exception(
2491
+ this.reportHandlerFailure(
2286
2492
  sendError,
2287
2493
  "Failed to handle permission request (send cancellation)"
2288
2494
  );
@@ -2297,27 +2503,19 @@ var DroidClient = class _DroidClient extends EventEmitter {
2297
2503
  return;
2298
2504
  }
2299
2505
  }
2300
- const response = {
2506
+ const response2 = {
2301
2507
  type: "response",
2302
2508
  jsonrpc: JSONRPC_VERSION2,
2303
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2509
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2304
2510
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2305
2511
  id: event.id,
2306
- result: {
2307
- selectedOption,
2308
- ...comment !== void 0 && { comment },
2309
- ...editedSpecContent !== void 0 && { editedSpecContent },
2310
- ...clientStartsHandoffSession !== void 0 && {
2311
- clientStartsHandoffSession
2312
- },
2313
- ...specHandoffId !== void 0 && { specHandoffId }
2314
- }
2512
+ result
2315
2513
  };
2316
2514
  this.logger.info("[DroidClient] Sending permission response back to CLI", {
2317
2515
  requestId: event.id,
2318
- result: String(selectedOption)
2516
+ result: String(result.selectedOption)
2319
2517
  });
2320
- await this.transport.send(response);
2518
+ await this.sendResponse(response2, hostMeta);
2321
2519
  }
2322
2520
  async handleAskUserRequest(event) {
2323
2521
  this.logger.info("[DroidClient] Handling ask-user request", {
@@ -2326,6 +2524,21 @@ var DroidClient = class _DroidClient extends EventEmitter {
2326
2524
  count: event.params.questions.length
2327
2525
  });
2328
2526
  this.emit("ask_user" /* ASK_USER */, event);
2527
+ if (event.params.blocking === false && !this.askUserHandlers.has("default")) {
2528
+ this.heldQueuedAskUserRequests.push(event);
2529
+ this.logger.info(
2530
+ "[DroidClient] Holding queued ask-user request until a handler is set",
2531
+ { requestId: event.id }
2532
+ );
2533
+ return;
2534
+ }
2535
+ await this.answerAskUserRequest(event);
2536
+ }
2537
+ async answerAskUserRequest(event) {
2538
+ let hostMeta;
2539
+ const setResponseMetadata = (meta) => {
2540
+ hostMeta = meta;
2541
+ };
2329
2542
  let result = { cancelled: true, answers: [] };
2330
2543
  const handler = this.askUserHandlers.get("default");
2331
2544
  if (handler) {
@@ -2338,7 +2551,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2338
2551
  );
2339
2552
  const handlerPromise = new Promise((resolve2, reject) => {
2340
2553
  this.pendingAskUserRequests.set(event.id, { reject });
2341
- handler(event).then(resolve2).catch(reject).finally(() => {
2554
+ handler(event, setResponseMetadata).then(resolve2).catch(reject).finally(() => {
2342
2555
  this.pendingAskUserRequests.delete(event.id);
2343
2556
  });
2344
2557
  });
@@ -2360,7 +2573,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2360
2573
  count: result.answers.length
2361
2574
  });
2362
2575
  } catch (error) {
2363
- this.logger.exception(error, "Failed to handle ask-user request");
2576
+ this.reportHandlerFailure(error, "Failed to handle ask-user request");
2364
2577
  const err = error instanceof Error ? error : new Error(String(error));
2365
2578
  this.emit(
2366
2579
  "session_notification" /* SESSION_NOTIFICATION */,
@@ -2369,9 +2582,19 @@ var DroidClient = class _DroidClient extends EventEmitter {
2369
2582
  )
2370
2583
  );
2371
2584
  this.pendingAskUserRequests.delete(event.id);
2372
- const cancelResponse = {
2585
+ const cancelResponse = event.params.blocking === false ? {
2373
2586
  jsonrpc: JSONRPC_VERSION2,
2374
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2587
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2588
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2589
+ type: "response",
2590
+ id: event.id,
2591
+ error: {
2592
+ code: -32603 /* INTERNAL_ERROR */,
2593
+ message: "The queued AskUser request could not be answered"
2594
+ }
2595
+ } : {
2596
+ jsonrpc: JSONRPC_VERSION2,
2597
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2375
2598
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2376
2599
  type: "response",
2377
2600
  id: event.id,
@@ -2381,9 +2604,9 @@ var DroidClient = class _DroidClient extends EventEmitter {
2381
2604
  }
2382
2605
  };
2383
2606
  try {
2384
- await this.transport.send(cancelResponse);
2607
+ await this.sendResponse(cancelResponse, hostMeta);
2385
2608
  } catch (sendError) {
2386
- this.logger.exception(
2609
+ this.reportHandlerFailure(
2387
2610
  sendError,
2388
2611
  "Failed to send ask-user cancellation"
2389
2612
  );
@@ -2391,10 +2614,10 @@ var DroidClient = class _DroidClient extends EventEmitter {
2391
2614
  return;
2392
2615
  }
2393
2616
  }
2394
- const response = {
2617
+ const response2 = {
2395
2618
  type: "response",
2396
2619
  jsonrpc: JSONRPC_VERSION2,
2397
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2620
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2398
2621
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2399
2622
  id: event.id,
2400
2623
  result
@@ -2403,7 +2626,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2403
2626
  requestId: event.id,
2404
2627
  cancelled: result.cancelled ?? false
2405
2628
  });
2406
- await this.transport.send(response);
2629
+ await this.sendResponse(response2, hostMeta);
2407
2630
  }
2408
2631
  /**
2409
2632
  * Same shape as the ask-user round trip: the handler (a UI) resolves with
@@ -2418,6 +2641,10 @@ var DroidClient = class _DroidClient extends EventEmitter {
2418
2641
  toolCallId: event.params.toolCallId,
2419
2642
  count: event.params.rows.length
2420
2643
  });
2644
+ let hostMeta;
2645
+ const setResponseMetadata = (meta) => {
2646
+ hostMeta = meta;
2647
+ };
2421
2648
  this.emit("pick_options" /* PICK_OPTIONS */, event);
2422
2649
  let result = { cancelled: true, selectedIds: [] };
2423
2650
  const handler = this.pickOptionsHandlers.get("default");
@@ -2426,7 +2653,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2426
2653
  const handlerPromise = new Promise(
2427
2654
  (resolve2, reject) => {
2428
2655
  this.pendingPickOptionsRequests.set(event.id, { reject });
2429
- handler(event).then(resolve2).catch(reject).finally(() => {
2656
+ handler(event, setResponseMetadata).then(resolve2).catch(reject).finally(() => {
2430
2657
  this.pendingPickOptionsRequests.delete(event.id);
2431
2658
  });
2432
2659
  }
@@ -2450,23 +2677,26 @@ var DroidClient = class _DroidClient extends EventEmitter {
2450
2677
  });
2451
2678
  const failure = {
2452
2679
  jsonrpc: JSONRPC_VERSION2,
2453
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2680
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2454
2681
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2455
2682
  type: "response",
2456
2683
  id: event.id,
2457
2684
  error: error.rpcError
2458
2685
  };
2459
2686
  try {
2460
- await this.transport.send(failure);
2687
+ await this.sendResponse(failure, hostMeta);
2461
2688
  } catch (sendError) {
2462
- this.logger.exception(
2689
+ this.reportHandlerFailure(
2463
2690
  sendError,
2464
2691
  "Failed to send pick-options failure"
2465
2692
  );
2466
2693
  }
2467
2694
  return;
2468
2695
  }
2469
- this.logger.exception(error, "Failed to handle pick-options request");
2696
+ this.reportHandlerFailure(
2697
+ error,
2698
+ "Failed to handle pick-options request"
2699
+ );
2470
2700
  const err = error instanceof Error ? error : new Error(String(error));
2471
2701
  this.emit(
2472
2702
  "session_notification" /* SESSION_NOTIFICATION */,
@@ -2476,16 +2706,16 @@ var DroidClient = class _DroidClient extends EventEmitter {
2476
2706
  );
2477
2707
  const cancelResponse = {
2478
2708
  jsonrpc: JSONRPC_VERSION2,
2479
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2709
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2480
2710
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2481
2711
  type: "response",
2482
2712
  id: event.id,
2483
2713
  result: { cancelled: true, selectedIds: [] }
2484
2714
  };
2485
2715
  try {
2486
- await this.transport.send(cancelResponse);
2716
+ await this.sendResponse(cancelResponse, hostMeta);
2487
2717
  } catch (sendError) {
2488
- this.logger.exception(
2718
+ this.reportHandlerFailure(
2489
2719
  sendError,
2490
2720
  "Failed to send pick-options cancellation"
2491
2721
  );
@@ -2493,19 +2723,20 @@ var DroidClient = class _DroidClient extends EventEmitter {
2493
2723
  return;
2494
2724
  }
2495
2725
  }
2496
- const response = {
2726
+ const response2 = {
2497
2727
  type: "response",
2498
2728
  jsonrpc: JSONRPC_VERSION2,
2499
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2729
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2500
2730
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2501
2731
  id: event.id,
2502
2732
  result
2503
2733
  };
2504
- await this.transport.send(response);
2734
+ await this.sendResponse(response2, hostMeta);
2505
2735
  }
2506
2736
  static getErrorType(error) {
2507
2737
  switch (error.constructor) {
2508
2738
  case ConnectionError:
2739
+ case DroidConnectionEndedError:
2509
2740
  return "ConnectionError" /* CONNECTION_ERROR */;
2510
2741
  case ProtocolError:
2511
2742
  return "ProtocolError" /* PROTOCOL_ERROR */;
@@ -2555,7 +2786,7 @@ var DroidClient = class _DroidClient extends EventEmitter {
2555
2786
  return {
2556
2787
  jsonrpc: JSONRPC_VERSION2,
2557
2788
  type: "notification",
2558
- factoryApiVersion: LEGACY_FACTORY_API_VERSION,
2789
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
2559
2790
  factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
2560
2791
  method: "droid.session_notification" /* SESSION_NOTIFICATION */,
2561
2792
  params: { notification }
@@ -2815,6 +3046,13 @@ function describeSpawnError(error, context) {
2815
3046
  }
2816
3047
  return `Failed to start Droid: ${error.code}`;
2817
3048
  }
3049
+ function createSpawnError(error, context) {
3050
+ return new DroidConnectionEndedError(describeSpawnError(error, context), {
3051
+ error,
3052
+ cwd: context?.cwd,
3053
+ execPath: context?.execPath
3054
+ });
3055
+ }
2818
3056
  function describeExitError(code, signal) {
2819
3057
  if (signal) {
2820
3058
  return `Droid process was killed (${signal})`;
@@ -2860,13 +3098,29 @@ var ManagedProcessImpl = class {
2860
3098
  logger;
2861
3099
  get childProcess() {
2862
3100
  if (!this._childProcess) {
2863
- throw new ConnectionError("Process not connected");
3101
+ throw new DroidConnectionEndedError("Process not connected");
2864
3102
  }
2865
3103
  return this._childProcess;
2866
3104
  }
3105
+ /** Wait for the runtime's spawn result without replacing the transport's error handler. */
3106
+ waitForSpawn() {
3107
+ const child = this.childProcess;
3108
+ return new Promise((resolve2, reject) => {
3109
+ const onSpawn = () => {
3110
+ child.off("error", onError);
3111
+ resolve2();
3112
+ };
3113
+ const onError = () => {
3114
+ child.off("spawn", onSpawn);
3115
+ reject(this.processError);
3116
+ };
3117
+ child.once("spawn", onSpawn);
3118
+ child.once("error", onError);
3119
+ });
3120
+ }
2867
3121
  setupHandlers() {
2868
3122
  if (!this.childProcess.stdout) {
2869
- throw new ConnectionError("Failed to get process stdout");
3123
+ throw new DroidConnectionEndedError("Failed to get process stdout");
2870
3124
  }
2871
3125
  this.childProcess.stdout.setEncoding("utf8");
2872
3126
  this.childProcess.stdout.on("data", this.stdoutDataHandler);
@@ -2874,23 +3128,19 @@ var ManagedProcessImpl = class {
2874
3128
  if (this.processError || this.isClosing || this.exitFinalized) {
2875
3129
  return;
2876
3130
  }
2877
- this.processError = new ConnectionError(
2878
- describeSpawnError(error, this.spawnContext),
2879
- {
2880
- error,
2881
- cwd: this.spawnContext?.cwd,
2882
- execPath: this.spawnContext?.execPath
2883
- }
2884
- );
3131
+ this.processError = createSpawnError(error, this.spawnContext);
2885
3132
  this.errorHandler?.(this.processError);
2886
3133
  });
2887
3134
  this.childProcess.stdin?.on("error", (error) => {
2888
3135
  if (this.processError || this.isClosing || this.exitFinalized) {
2889
3136
  return;
2890
3137
  }
2891
- this.processError = new ConnectionError("Process stdin failed", {
2892
- error
2893
- });
3138
+ this.processError = new DroidConnectionEndedError(
3139
+ "Process stdin failed",
3140
+ {
3141
+ error
3142
+ }
3143
+ );
2894
3144
  this.errorHandler?.(this.processError);
2895
3145
  });
2896
3146
  this.childProcess.on("exit", (code, signal) => {
@@ -2965,24 +3215,28 @@ var ManagedProcessImpl = class {
2965
3215
  throw this.processError;
2966
3216
  }
2967
3217
  if (!this._childProcess || !this._childProcess.stdin) {
2968
- throw new ConnectionError("Process not connected");
3218
+ throw new DroidConnectionEndedError("Process not connected");
2969
3219
  }
2970
3220
  const previousSends = this.queuedSendPromise;
2971
3221
  this.queuedSendPromise = (async () => {
2972
3222
  await previousSends.catch(() => {
2973
3223
  });
2974
3224
  if (!this._childProcess || !this._childProcess.stdin || this._childProcess.killed || this.isClosing) {
2975
- throw new ConnectionError("Process disconnected before write");
3225
+ throw new DroidConnectionEndedError(
3226
+ "Process disconnected before write"
3227
+ );
2976
3228
  }
2977
3229
  await new Promise((resolve2, reject) => {
2978
3230
  try {
2979
3231
  if (!this._childProcess || !this._childProcess.stdin) {
2980
- reject(new ConnectionError("Process stdin unavailable"));
3232
+ reject(new DroidConnectionEndedError("Process stdin unavailable"));
2981
3233
  return;
2982
3234
  }
2983
3235
  const stdin = this._childProcess.stdin;
2984
3236
  if ("writable" in stdin && !stdin.writable) {
2985
- reject(new ConnectionError("Process stdin is not writable"));
3237
+ reject(
3238
+ new DroidConnectionEndedError("Process stdin is not writable")
3239
+ );
2986
3240
  return;
2987
3241
  }
2988
3242
  const childPid = this._childProcess?.pid;
@@ -2998,15 +3252,21 @@ var ManagedProcessImpl = class {
2998
3252
  const errorMessage = error.message || String(error);
2999
3253
  if (errorMessage.includes("EPIPE") || errorMessage.includes("ECONNRESET")) {
3000
3254
  reject(
3001
- new ConnectionError("Process stdin closed during write", {
3002
- error
3003
- })
3255
+ new DroidConnectionEndedError(
3256
+ "Process stdin closed during write",
3257
+ {
3258
+ error
3259
+ }
3260
+ )
3004
3261
  );
3005
3262
  } else {
3006
3263
  reject(
3007
- new ConnectionError("Failed to write to process stdin", {
3008
- error
3009
- })
3264
+ new DroidConnectionEndedError(
3265
+ "Failed to write to process stdin",
3266
+ {
3267
+ error
3268
+ }
3269
+ )
3010
3270
  );
3011
3271
  }
3012
3272
  } else {
@@ -3028,7 +3288,7 @@ var ManagedProcessImpl = class {
3028
3288
  }
3029
3289
  );
3030
3290
  reject(
3031
- new ConnectionError("Error writing to process stdin", {
3291
+ new DroidConnectionEndedError("Error writing to process stdin", {
3032
3292
  error: error instanceof Error ? error : new Error(String(error))
3033
3293
  })
3034
3294
  );
@@ -3185,10 +3445,8 @@ async function spawnWithWindowsJobObjectRetry(spawnFactory, options = {}) {
3185
3445
  const sleep = options.sleep ?? defaultSleep;
3186
3446
  const logPrefix = options.logPrefix ?? "[spawnWithWindowsJobObjectRetry]";
3187
3447
  const logger = options.logger ?? noopLogger;
3188
- let lastChild = null;
3189
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
3448
+ for (let attempt = 0; ; attempt++) {
3190
3449
  const child = spawnFactory();
3191
- lastChild = child;
3192
3450
  const observation = await observeEarlySpawnFailure(
3193
3451
  child,
3194
3452
  gracePeriodMs,
@@ -3198,8 +3456,28 @@ async function spawnWithWindowsJobObjectRetry(spawnFactory, options = {}) {
3198
3456
  return child;
3199
3457
  }
3200
3458
  const isRetryableJobObjectFailure = observation.stderr.includes(WINDOWS_JOB_OBJECT_ERROR_MARKER) || observation.exited && observation.code !== 0 && !observation.error;
3201
- if (attempt === maxRetries || !isRetryableJobObjectFailure) {
3202
- return child;
3459
+ try {
3460
+ await options.disposeEarlyExited?.(child);
3461
+ } catch (disposeErr) {
3462
+ logWarn(logger, "spawnWithWindowsJobObjectRetry: dispose error", {
3463
+ cause: disposeErr instanceof Error ? disposeErr : new Error(String(disposeErr)),
3464
+ name: logPrefix
3465
+ });
3466
+ }
3467
+ if (attempt >= maxRetries || !isRetryableJobObjectFailure) {
3468
+ if (observation.error) {
3469
+ throw createSpawnError(observation.error, options);
3470
+ }
3471
+ throw new DroidConnectionEndedError(
3472
+ "Droid process exited during startup",
3473
+ {
3474
+ cwd: options.cwd,
3475
+ execPath: options.execPath,
3476
+ exitCode: observation.code,
3477
+ signal: observation.signal,
3478
+ stderrTail: observation.stderr.slice(-256)
3479
+ }
3480
+ );
3203
3481
  }
3204
3482
  logger.warn("spawnWithWindowsJobObjectRetry: retrying on early failure", {
3205
3483
  name: logPrefix,
@@ -3209,21 +3487,8 @@ async function spawnWithWindowsJobObjectRetry(spawnFactory, options = {}) {
3209
3487
  signal: observation.signal,
3210
3488
  stderrTail: observation.stderr.slice(-256)
3211
3489
  });
3212
- try {
3213
- await options.disposeEarlyExited?.(child);
3214
- } catch (disposeErr) {
3215
- logWarn(logger, "spawnWithWindowsJobObjectRetry: dispose error", {
3216
- cause: disposeErr instanceof Error ? disposeErr : new Error(String(disposeErr)),
3217
- name: logPrefix
3218
- });
3219
- }
3220
3490
  await sleep(backoff[attempt] ?? backoff[backoff.length - 1] ?? 400);
3221
3491
  }
3222
- if (lastChild) return lastChild;
3223
- throw new DroidClientError(
3224
- "spawnWithWindowsJobObjectRetry: exhausted retries without child",
3225
- {}
3226
- );
3227
3492
  }
3228
3493
 
3229
3494
  // src/exec-core/process-manager.ts
@@ -3311,6 +3576,8 @@ var DroidProcessManager = class {
3311
3576
  () => this.invokeNodeSpawn(planned),
3312
3577
  {
3313
3578
  ...retryOptions,
3579
+ cwd: planned.expandedCwd,
3580
+ execPath: planned.execPath,
3314
3581
  logPrefix: "[DroidProcessManager]",
3315
3582
  logger: this.logger,
3316
3583
  disposeEarlyExited: (child) => {
@@ -3341,7 +3608,7 @@ var DroidProcessManager = class {
3341
3608
  );
3342
3609
  if (!childProcess.stdout || !childProcess.stdin) {
3343
3610
  childProcess.kill();
3344
- throw new ConnectionError("Failed to get process stdio");
3611
+ throw new DroidConnectionEndedError("Failed to get process stdio");
3345
3612
  }
3346
3613
  return new ManagedProcessImpl(
3347
3614
  childProcess,
@@ -3468,7 +3735,7 @@ var DroidProcessManager = class {
3468
3735
  const childProcess = this.invokeNodeSpawn(planned);
3469
3736
  if (!childProcess.stdout || !childProcess.stdin) {
3470
3737
  childProcess.kill();
3471
- throw new ConnectionError("Failed to get process stdio");
3738
+ throw new DroidConnectionEndedError("Failed to get process stdio");
3472
3739
  }
3473
3740
  return {
3474
3741
  logPrefix: planned.logPrefix,
@@ -3509,7 +3776,7 @@ var ProcessTransport = class {
3509
3776
  }
3510
3777
  async connect() {
3511
3778
  if (this.managedProcess && this.managedProcess.isConnected) {
3512
- throw new ConnectionError("Transport already connected");
3779
+ throw new DroidClientError("Transport already connected");
3513
3780
  }
3514
3781
  const config = {
3515
3782
  execPath: this.options.droidExecPath,
@@ -3531,8 +3798,22 @@ var ProcessTransport = class {
3531
3798
  let spawnOutcome = "error";
3532
3799
  let managedProcess;
3533
3800
  try {
3534
- managedProcess = process.platform === "win32" ? await this.processManager.spawnWithWindowsRetry(config) : this.processManager.spawn(config);
3801
+ if (process.platform === "win32") {
3802
+ managedProcess = await this.processManager.spawnWithWindowsRetry(config);
3803
+ } else {
3804
+ const spawned = this.processManager.spawn(config);
3805
+ managedProcess = spawned;
3806
+ if (spawned.childProcess.pid === void 0) {
3807
+ await spawned.waitForSpawn();
3808
+ }
3809
+ }
3535
3810
  spawnOutcome = "success";
3811
+ } catch (error) {
3812
+ await managedProcess?.close();
3813
+ if (error instanceof DroidConnectionEndedError) {
3814
+ this.errorHandler?.(error);
3815
+ }
3816
+ throw error;
3536
3817
  } finally {
3537
3818
  this.metrics.recordDuration(
3538
3819
  CLI_JSONRPC_CHILD_SPAWN_LATENCY_METRIC,
@@ -3556,7 +3837,7 @@ var ProcessTransport = class {
3556
3837
  }
3557
3838
  async send(message) {
3558
3839
  if (!this.managedProcess) {
3559
- throw new ConnectionError("Transport not connected");
3840
+ throw new DroidConnectionEndedError("Transport not connected");
3560
3841
  }
3561
3842
  return await this.managedProcess.send(JSON.stringify(message));
3562
3843
  }
@@ -3586,6 +3867,180 @@ var ProcessTransport = class {
3586
3867
  }
3587
3868
  };
3588
3869
 
3870
+ // src/environment-session.ts
3871
+ import { v4 as uuidv43 } from "uuid";
3872
+
3873
+ // src/environment-launch/index.ts
3874
+ import { v4 as uuidv42 } from "uuid";
3875
+
3876
+ // src/environment-launch/errors.ts
3877
+ var EnvironmentLaunchError = class extends Error {
3878
+ computerId;
3879
+ computerName;
3880
+ cleanupError;
3881
+ constructor(options) {
3882
+ super(
3883
+ options.cause instanceof Error ? options.cause.message : "Environment session startup failed",
3884
+ { cause: options.cause }
3885
+ );
3886
+ this.name = "EnvironmentLaunchError";
3887
+ this.computerId = options.computerId;
3888
+ this.computerName = options.computerName;
3889
+ this.cleanupError = options.cleanupError;
3890
+ }
3891
+ };
3892
+
3893
+ // src/environment-launch/index.ts
3894
+ async function launchEnvironmentSession(options) {
3895
+ const timeoutMs = options.timeoutMs ?? 18e4;
3896
+ const pollIntervalMs = options.pollIntervalMs ?? 1e3;
3897
+ const controller = new AbortController();
3898
+ const cancel = () => controller.abort(options.signal?.reason);
3899
+ if (options.signal?.aborted) cancel();
3900
+ else options.signal?.addEventListener("abort", cancel, { once: true });
3901
+ const timer = setTimeout(
3902
+ () => controller.abort(new EnvironmentComputerTimeoutError()),
3903
+ timeoutMs
3904
+ );
3905
+ const signal = controller.signal;
3906
+ let createdComputerId;
3907
+ let computerName;
3908
+ function launchError(cause, cleanupError) {
3909
+ return new EnvironmentLaunchError({
3910
+ cause,
3911
+ computerId: createdComputerId,
3912
+ computerName,
3913
+ cleanupError
3914
+ });
3915
+ }
3916
+ try {
3917
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || !Number.isFinite(pollIntervalMs) || pollIntervalMs <= 0)
3918
+ throw new Error("Invalid environment launch configuration");
3919
+ const selection = await runWithAbort(
3920
+ signal,
3921
+ async () => await resolveEnvironment({ ...options, signal })
3922
+ );
3923
+ computerName = `environment-${uuidv42()}`;
3924
+ signal.throwIfAborted();
3925
+ const createPromise = createOrRecoverComputer({
3926
+ name: computerName,
3927
+ environmentId: selection.environment.id,
3928
+ keepWhenIdle: options.keepWhenIdle,
3929
+ request: options.request,
3930
+ serviceAccountId: options.serviceAccountId,
3931
+ signal: AbortSignal.timeout(timeoutMs)
3932
+ });
3933
+ const allocated = await runWithAbort(signal, async () => await createPromise).catch(
3934
+ (cause) => {
3935
+ if (signal.aborted && createdComputerId === void 0)
3936
+ void createPromise.then(
3937
+ (computer2) => createFactoryApi(options.request).deleteComputer({
3938
+ computerId: computer2.id,
3939
+ signal: AbortSignal.timeout(Math.min(timeoutMs, 5e3))
3940
+ })
3941
+ ).catch(() => {
3942
+ });
3943
+ throw cause;
3944
+ }
3945
+ );
3946
+ createdComputerId = allocated.id;
3947
+ await runWithAbort(signal, async () => await options.onComputer?.(allocated));
3948
+ const computer = await waitForEnvironmentComputer({
3949
+ computer: allocated,
3950
+ timeoutMs,
3951
+ pollIntervalMs,
3952
+ now: options.now,
3953
+ sleep: options.sleep,
3954
+ signal,
3955
+ isReady: isEnvironmentComputerReady,
3956
+ onComputer: options.onComputer,
3957
+ request: options.request,
3958
+ serviceAccountId: options.serviceAccountId
3959
+ });
3960
+ const connection = await runWithAbort(
3961
+ signal,
3962
+ async () => await options.connect({ computer, signal })
3963
+ );
3964
+ const session = await runWithAbort(
3965
+ signal,
3966
+ async () => await options.startSession({ connection, computer, selection, signal })
3967
+ );
3968
+ return { session, computer, selection };
3969
+ } catch (cause) {
3970
+ if (createdComputerId !== void 0) {
3971
+ const cleanup = new AbortController();
3972
+ const cleanupTimer = setTimeout(
3973
+ () => cleanup.abort(new EnvironmentComputerTimeoutError()),
3974
+ Math.min(timeoutMs, 5e3)
3975
+ );
3976
+ try {
3977
+ const computerId = createdComputerId;
3978
+ await runWithAbort(
3979
+ cleanup.signal,
3980
+ async () => await createFactoryApi(options.request).deleteComputer({
3981
+ computerId,
3982
+ signal: cleanup.signal
3983
+ })
3984
+ );
3985
+ } catch (failure) {
3986
+ throw launchError(cause, failure);
3987
+ } finally {
3988
+ clearTimeout(cleanupTimer);
3989
+ }
3990
+ }
3991
+ throw launchError(cause);
3992
+ } finally {
3993
+ clearTimeout(timer);
3994
+ options.signal?.removeEventListener("abort", cancel);
3995
+ }
3996
+ }
3997
+ async function resolveEnvironment(options) {
3998
+ const http2 = createFactoryApi(options.request);
3999
+ const selector = options.environment;
4000
+ if ("id" in selector ? !selector.id.trim() : !selector.name.trim())
4001
+ throw new Error("Invalid environment selector");
4002
+ const context = {
4003
+ serviceAccountId: options.serviceAccountId,
4004
+ signal: options.signal
4005
+ };
4006
+ let environment;
4007
+ if ("id" in selector && context.serviceAccountId === void 0) {
4008
+ environment = SandboxEnvironmentSchema.parse(
4009
+ await http2.getEnvironment({
4010
+ ...context,
4011
+ environmentId: selector.id
4012
+ })
4013
+ );
4014
+ if (environment.id !== selector.id)
4015
+ throw new Error("Environment lookup returned a different ID");
4016
+ } else {
4017
+ const matches = [];
4018
+ options.signal?.throwIfAborted();
4019
+ const page = await http2.listEnvironments(context);
4020
+ if (!page || typeof page !== "object" || !("environments" in page) || !Array.isArray(page.environments))
4021
+ throw new Error("Invalid environment list response");
4022
+ for (const item of page.environments) {
4023
+ const candidate = SandboxEnvironmentSchema.parse(item);
4024
+ if ("id" in selector ? candidate.id === selector.id : candidate.name === selector.name)
4025
+ matches.push(candidate);
4026
+ }
4027
+ if (matches.length !== 1)
4028
+ throw new Error(
4029
+ "id" in selector ? "Environment is not accessible" : "Environment name must match exactly one accessible environment"
4030
+ );
4031
+ environment = matches[0];
4032
+ }
4033
+ if (!environment) throw new Error("Environment is not accessible");
4034
+ if (!isSandboxEnvironmentLaunchable(environment)) {
4035
+ throw new Error("Environment is not ready to launch");
4036
+ }
4037
+ return {
4038
+ environment,
4039
+ source: getSandboxEnvironmentLaunchSource(environment),
4040
+ repositories: getSandboxEnvironmentLaunchRepositories(environment)
4041
+ };
4042
+ }
4043
+
3589
4044
  // src/helpers.ts
3590
4045
  function resolveApiKey(apiKey) {
3591
4046
  const resolved = apiKey ?? process.env.FACTORY_API_KEY;
@@ -3608,7 +4063,10 @@ async function createTransport(options) {
3608
4063
  ...options.env,
3609
4064
  FACTORY_API_KEY: resolveApiKey(options.apiKey),
3610
4065
  FACTORY_UPSTREAM_CLIENT_TYPE: "sdk" /* SDK */,
3611
- FACTORY_UPSTREAM_SDK: SDK_IDENTITY
4066
+ FACTORY_UPSTREAM_SDK: SDK_IDENTITY,
4067
+ // This helper owns the original SDK process, unlike the generic
4068
+ // ProcessTransport also used by daemon workers.
4069
+ FACTORY_EXEC_ORIGINAL_INVOCATION: "1"
3612
4070
  },
3613
4071
  observability: options.observability
3614
4072
  });
@@ -3616,13 +4074,26 @@ async function createTransport(options) {
3616
4074
  return processTransport;
3617
4075
  }
3618
4076
  function setupClientHandlers(client, options) {
4077
+ const sessionId = () => {
4078
+ const addressed = client.addressedSessionId;
4079
+ if (!addressed) {
4080
+ throw new DroidClientError(
4081
+ "Received a prompt that no single session owns"
4082
+ );
4083
+ }
4084
+ return addressed;
4085
+ };
3619
4086
  if (options.permissionHandler) {
3620
4087
  const handler = options.permissionHandler;
3621
- client.setPermissionHandler((event) => handler(event.params));
4088
+ client.setPermissionHandler(
4089
+ (event) => handler({ ...event.params, sessionId: sessionId() })
4090
+ );
3622
4091
  }
3623
4092
  if (options.askUserHandler) {
3624
4093
  const handler = options.askUserHandler;
3625
- client.setAskUserHandler((event) => handler(event.params));
4094
+ client.setAskUserHandler(
4095
+ (event) => handler({ ...event.params, sessionId: sessionId() })
4096
+ );
3626
4097
  }
3627
4098
  }
3628
4099
  async function createConfiguredClient(options) {
@@ -3922,6 +4393,313 @@ async function startSdkMcpServers(servers) {
3922
4393
  };
3923
4394
  }
3924
4395
 
4396
+ // src/session-operations.ts
4397
+ function response(result) {
4398
+ return {
4399
+ type: "response",
4400
+ jsonrpc: JSONRPC_VERSION2,
4401
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
4402
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
4403
+ id: "sdk-adapter",
4404
+ result
4405
+ };
4406
+ }
4407
+ function createDaemonSessionOperations(controller, initialSessionId, handlers) {
4408
+ let sessionId = initialSessionId;
4409
+ const ownedSessionIds = /* @__PURE__ */ new Set([initialSessionId]);
4410
+ const reportError = (error) => controller.emit("error", toError(error));
4411
+ const permission = async (request) => {
4412
+ if (request.sessionId !== sessionId && !request.associatedSessionIds.includes(sessionId))
4413
+ return;
4414
+ const selected = await resolvePermissionHandler(
4415
+ handlers.permissionHandler,
4416
+ request,
4417
+ reportError
4418
+ ) ?? {
4419
+ selectedOption: "cancel" /* Cancel */
4420
+ };
4421
+ try {
4422
+ await controller.respondToPermission({
4423
+ permissionId: request.requestId,
4424
+ sessionId,
4425
+ ...selected
4426
+ });
4427
+ } catch (error) {
4428
+ reportError(error);
4429
+ }
4430
+ };
4431
+ const askUser = async (request) => {
4432
+ if (request.sessionId !== sessionId) return;
4433
+ const result = await resolveAskUserHandler(
4434
+ handlers.askUserHandler,
4435
+ request,
4436
+ reportError
4437
+ );
4438
+ if (!result && request.blocking === false) return;
4439
+ try {
4440
+ await controller.respondToAskUser({
4441
+ requestId: request.requestId,
4442
+ sessionId,
4443
+ result: result ?? { cancelled: true, answers: [] }
4444
+ });
4445
+ } catch (error) {
4446
+ reportError(error);
4447
+ }
4448
+ };
4449
+ controller.on("permissionRequested" /* PermissionRequested */, permission);
4450
+ controller.on("askUserRequested" /* AskUserRequested */, askUser);
4451
+ const operations = {
4452
+ lifecycle: {
4453
+ shutdown: async () => {
4454
+ try {
4455
+ const results = await Promise.allSettled(
4456
+ [...ownedSessionIds].map((id) => controller.closeSession(id))
4457
+ );
4458
+ const failure = results.find(
4459
+ (result) => result.status === "rejected"
4460
+ );
4461
+ if (failure?.status === "rejected") throw toError(failure.reason);
4462
+ } finally {
4463
+ await operations.close();
4464
+ }
4465
+ },
4466
+ abort: (close) => {
4467
+ void close().catch(() => {
4468
+ });
4469
+ }
4470
+ },
4471
+ onNotification(callback, filter) {
4472
+ const listener = (event) => {
4473
+ if (event.sessionId !== sessionId || filter?.type !== void 0 && filter.type !== event.notification.type)
4474
+ return;
4475
+ try {
4476
+ callback({
4477
+ type: "notification",
4478
+ jsonrpc: JSONRPC_VERSION2,
4479
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION2,
4480
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
4481
+ method: "droid.session_notification" /* SESSION_NOTIFICATION */,
4482
+ params: { notification: event.notification }
4483
+ });
4484
+ } catch {
4485
+ }
4486
+ };
4487
+ controller.on("sessionNotification" /* SessionNotification */, listener);
4488
+ return () => controller.off("sessionNotification" /* SessionNotification */, listener);
4489
+ },
4490
+ onError(callback) {
4491
+ const disconnected = (code, reason) => callback(new ConnectionClosedError(code, reason));
4492
+ const exited = (event) => {
4493
+ if (event.sessionId === sessionId && event.notification.type === "session_process_exited" /* SESSION_PROCESS_EXITED */)
4494
+ callback(
4495
+ new DroidConnectionEndedError("Remote session process exited.")
4496
+ );
4497
+ };
4498
+ controller.on("error", callback);
4499
+ controller.on("disconnected" /* Disconnected */, disconnected);
4500
+ controller.on("sessionNotification" /* SessionNotification */, exited);
4501
+ return () => {
4502
+ controller.off("error", callback);
4503
+ controller.off("disconnected" /* Disconnected */, disconnected);
4504
+ controller.off("sessionNotification" /* SessionNotification */, exited);
4505
+ };
4506
+ },
4507
+ addUserMessage: async (params) => response(await controller.addUserMessage(sessionId, params)),
4508
+ interruptSession: async () => response(await controller.interruptSession(sessionId)),
4509
+ closeSession: async () => {
4510
+ await controller.closeSession(sessionId);
4511
+ return response({});
4512
+ },
4513
+ close: async () => {
4514
+ controller.off("permissionRequested" /* PermissionRequested */, permission);
4515
+ controller.off("askUserRequested" /* AskUserRequested */, askUser);
4516
+ controller.destroy();
4517
+ controller.getSessionStateManager().clear();
4518
+ },
4519
+ loadSession: async (params) => {
4520
+ ownedSessionIds.add(params.sessionId);
4521
+ const result = await controller.loadSession({
4522
+ ...params,
4523
+ sessionOriginHint: params.sessionOriginHint,
4524
+ sessionSource: params.sessionSource
4525
+ });
4526
+ sessionId = params.sessionId;
4527
+ return response(result);
4528
+ },
4529
+ updateSessionSettings: async (params) => response(await controller.updateSessionSettings(sessionId, params)),
4530
+ addMcpServer: async (params) => response(await controller.addMcpServer({ ...params, sessionId })),
4531
+ removeMcpServer: async (params) => response(await controller.removeMcpServer({ ...params, sessionId })),
4532
+ toggleMcpServer: async (params) => response(await controller.toggleMcpServer({ ...params, sessionId })),
4533
+ listMcpServers: async () => response(await controller.listMcpServers(sessionId)),
4534
+ listMcpTools: async () => response(await controller.listMcpTools(sessionId)),
4535
+ refreshConnectorToolCatalog: async () => response(await controller.refreshConnectorToolCatalog()),
4536
+ authenticateMcpServer: async (params) => response(
4537
+ await controller.authenticateMcpServer({ ...params, sessionId })
4538
+ ),
4539
+ listSkills: async (params) => response(await controller.listSkills(sessionId, params)),
4540
+ setSkillDisabled: async (skillName, disabled, settingsLevel) => response(
4541
+ await controller.setSkillDisabled({
4542
+ sessionId,
4543
+ skillName,
4544
+ disabled,
4545
+ settingsLevel
4546
+ })
4547
+ ),
4548
+ getRewindInfo: async (params) => response(await controller.getRewindInfo(sessionId, params.messageId)),
4549
+ executeRewind: async (params) => response(await controller.executeRewind(sessionId, params)),
4550
+ compactSession: async (params) => response(
4551
+ await controller.compactSession(
4552
+ sessionId,
4553
+ params.customInstructions,
4554
+ params.mode
4555
+ )
4556
+ ),
4557
+ forkSession: async (params) => response(await controller.forkSession(sessionId, params)),
4558
+ renameSession: async (params) => response(await controller.renameSession(sessionId, params.title)),
4559
+ listTools: async () => {
4560
+ throw new DroidClientError(
4561
+ "listTools is not supported by remote sessions."
4562
+ );
4563
+ },
4564
+ getContextStats: async () => {
4565
+ throw new DroidClientError(
4566
+ "getContextStats is not supported by remote sessions."
4567
+ );
4568
+ }
4569
+ };
4570
+ return operations;
4571
+ }
4572
+
4573
+ // src/environment-session.ts
4574
+ async function createEnvironmentSession(options, createHandle) {
4575
+ if (!options.environment)
4576
+ throw new DroidClientError("An environment is required.");
4577
+ for (const key of [
4578
+ "execPath",
4579
+ "execArgs",
4580
+ "env",
4581
+ "transport",
4582
+ "machineId"
4583
+ ]) {
4584
+ if (options[key] !== void 0)
4585
+ throw new DroidClientError(
4586
+ `Option "${key}" is local-only and cannot be used with an environment.`
4587
+ );
4588
+ }
4589
+ const mcpServers = options.mcpServers?.map((server) => {
4590
+ if (server instanceof SdkMcpServer)
4591
+ throw new DroidClientError(
4592
+ "In-process MCP servers cannot be used with an environment."
4593
+ );
4594
+ return server;
4595
+ });
4596
+ const apiKey = resolveApiKey(options.apiKey);
4597
+ const auth = { apiKey, baseUrl: options.baseUrl };
4598
+ const request = createApiRequest(auth);
4599
+ const api = createFactoryApi(request);
4600
+ const sessionId = uuidv43();
4601
+ let startup;
4602
+ try {
4603
+ const launched = await launchEnvironmentSession({
4604
+ environment: options.environment,
4605
+ signal: options.abortSignal,
4606
+ request,
4607
+ connect: async ({ computer, signal }) => {
4608
+ if (!computer.relayClientUrl)
4609
+ throw new DroidClientError("Computer has no relay URL.");
4610
+ const connectionConfig = {
4611
+ ...DEFAULT_CONNECTION_CONFIG,
4612
+ ...MACHINE_TYPE_CONFIG["computer" /* Computer */]
4613
+ };
4614
+ const connected = new DaemonSessionController({
4615
+ sessionStateManager: new MultiSessionStateManager(),
4616
+ config: {
4617
+ machineId: computer.id,
4618
+ machineType: "computer" /* Computer */,
4619
+ providerType: computer.providerType,
4620
+ isManaged: true,
4621
+ ensureRunning: async () => ({ wasRestarted: false }),
4622
+ trustedIdentity: trustedIdentityFromComputerResponse(
4623
+ computer,
4624
+ async () => {
4625
+ const fresh = parseResponse(
4626
+ await api.getComputer({
4627
+ computerId: computer.id,
4628
+ signal: AbortSignal.timeout(5e3)
4629
+ }),
4630
+ ComputerReadSchema
4631
+ );
4632
+ if (fresh.id !== computer.id)
4633
+ throw new DroidClientError(
4634
+ "Identity refresh returned a different Computer."
4635
+ );
4636
+ return fresh;
4637
+ }
4638
+ ),
4639
+ url: computer.relayClientUrl,
4640
+ clientType: "sdk" /* SDK */,
4641
+ sdk: SDK_CLIENT_METADATA,
4642
+ getCredential: async () => ({ apiKey }),
4643
+ connectionTimeoutMs: connectionConfig.connectionTimeoutMs,
4644
+ requestTimeout: connectionConfig.requestTimeoutMs,
4645
+ maxPollAttempts: connectionConfig.maxPollAttempts,
4646
+ maxReconnectAttempts: connectionConfig.maxReconnectAttempts,
4647
+ reconnectInterval: connectionConfig.reconnectInitialDelayMs,
4648
+ maxReconnectDelay: connectionConfig.reconnectMaxDelayMs,
4649
+ reconnectBackoffFactor: connectionConfig.reconnectBackoffFactor,
4650
+ supportsTerminalRestoreOnLoad: false
4651
+ }
4652
+ });
4653
+ const operations = createDaemonSessionOperations(
4654
+ connected,
4655
+ sessionId,
4656
+ options
4657
+ );
4658
+ const cancel = () => connected.destroy();
4659
+ signal.addEventListener("abort", cancel, { once: true });
4660
+ startup = {
4661
+ operations,
4662
+ releaseAbort: () => signal.removeEventListener("abort", cancel)
4663
+ };
4664
+ await connected.attemptInitialConnection();
4665
+ signal.throwIfAborted();
4666
+ return { controller: connected, operations };
4667
+ },
4668
+ startSession: async ({ connection, computer, signal }) => {
4669
+ const { controller, operations } = connection;
4670
+ const initParams = buildInitParams({
4671
+ ...options,
4672
+ machineId: computer.id,
4673
+ cwd: options.cwd ?? computer.clonedRepoDirectories?.[0] ?? "~",
4674
+ mcpServers
4675
+ });
4676
+ controller.getSessionStateManager().markSessionLoading(sessionId, computer.id);
4677
+ const result = await controller.initializeSession({
4678
+ ...initParams,
4679
+ sessionId,
4680
+ token: apiKey,
4681
+ sessionSource: initParams.sessionSource,
4682
+ sessionOriginHint: initParams.sessionOriginHint
4683
+ });
4684
+ signal.throwIfAborted();
4685
+ return createHandle({
4686
+ result,
4687
+ operations,
4688
+ computerId: computer.id,
4689
+ cwd: initParams.cwd,
4690
+ mcpServers
4691
+ });
4692
+ }
4693
+ });
4694
+ return launched.session;
4695
+ } catch (error) {
4696
+ await startup?.operations.close();
4697
+ throw error;
4698
+ } finally {
4699
+ startup?.releaseAbort();
4700
+ }
4701
+ }
4702
+
3925
4703
  // src/session.ts
3926
4704
  var DroidSession = class _DroidSession {
3927
4705
  /** @internal */
@@ -3932,11 +4710,22 @@ var DroidSession = class _DroidSession {
3932
4710
  this._settings = metadata.settings;
3933
4711
  this._cwd = metadata.cwd;
3934
4712
  this._cleanupCallbacks = cleanupCallbacks;
4713
+ this._lifecycle = _runtime.lifecycle ?? {
4714
+ shutdown: async () => {
4715
+ await client.closeSession({ reason: "other" }).catch(() => {
4716
+ });
4717
+ await client.close();
4718
+ },
4719
+ abort: (close) => {
4720
+ void close();
4721
+ }
4722
+ };
3935
4723
  this._bindMetadataNotifications();
3936
4724
  this._bindAbortSignal();
3937
4725
  }
3938
4726
  _runtime;
3939
4727
  _client;
4728
+ _lifecycle;
3940
4729
  _sessionId;
3941
4730
  _settings;
3942
4731
  _cwd;
@@ -3950,6 +4739,9 @@ var DroidSession = class _DroidSession {
3950
4739
  get id() {
3951
4740
  return this._sessionId;
3952
4741
  }
4742
+ get computerId() {
4743
+ return this._runtime.computerId;
4744
+ }
3953
4745
  get settings() {
3954
4746
  return this._settings;
3955
4747
  }
@@ -3994,9 +4786,7 @@ var DroidSession = class _DroidSession {
3994
4786
  bridge.release();
3995
4787
  }
3996
4788
  try {
3997
- await this._client.closeSession({ reason: "other" }).catch(() => {
3998
- });
3999
- await this._client.close();
4789
+ await this._lifecycle.shutdown();
4000
4790
  } finally {
4001
4791
  const cleanups = this._cleanupCallbacks.splice(0);
4002
4792
  await Promise.all(cleanups.map((cleanup) => cleanup()));
@@ -4259,7 +5049,7 @@ var DroidSession = class _DroidSession {
4259
5049
  _bindAbortSignal() {
4260
5050
  this._cleanupAbortSignal = wireAbortSignal(
4261
5051
  this._runtime.abortSignal,
4262
- () => void this.close()
5052
+ () => this._lifecycle.abort(() => this.close())
4263
5053
  );
4264
5054
  }
4265
5055
  _bindMetadataNotifications() {
@@ -4304,6 +5094,22 @@ function createReplacementLoadParams(options, mcpServers) {
4304
5094
  };
4305
5095
  }
4306
5096
  async function createSession(options = {}) {
5097
+ if (options.environment) {
5098
+ return await createEnvironmentSession(
5099
+ options,
5100
+ ({ result, operations, computerId, cwd, mcpServers }) => new DroidSession(
5101
+ operations,
5102
+ result.sessionId,
5103
+ { settings: result.settings, cwd },
5104
+ {
5105
+ computerId,
5106
+ abortSignal: options.abortSignal,
5107
+ loadParams: createReplacementLoadParams(options, mcpServers),
5108
+ lifecycle: operations.lifecycle
5109
+ }
5110
+ )
5111
+ );
5112
+ }
4307
5113
  const { client } = await createConfiguredClient(options);
4308
5114
  let cleanupInitAbortSignal = options.abortSignal?.aborted ? () => {
4309
5115
  } : wireAbortSignal(options.abortSignal, () => {
@@ -4393,12 +5199,14 @@ async function run(prompt, options = {}) {
4393
5199
  includePartialMessages: false
4394
5200
  })) {
4395
5201
  if (msg.type === DroidMessageType.Result) {
4396
- return msg;
5202
+ const computerId = session.computerId;
5203
+ return computerId === void 0 ? msg : { ...msg, computerId };
4397
5204
  }
4398
5205
  }
4399
5206
  throw new Error("Stream completed without a result message");
4400
5207
  } finally {
4401
- await session.close();
5208
+ await session.close().catch(() => {
5209
+ });
4402
5210
  }
4403
5211
  }
4404
5212
 
@@ -4418,9 +5226,14 @@ var CR = 13;
4418
5226
  var SPACE = 32;
4419
5227
  var TAB = 9;
4420
5228
  var PARSE_OVERSAMPLE = 20;
4421
- function readFileBuffer(filePath) {
5229
+ function readFileOrNull(filePath, read) {
4422
5230
  try {
4423
- return fs3.readFileSync(filePath);
5231
+ const fd = fs3.openSync(filePath, "r");
5232
+ try {
5233
+ return read(fd);
5234
+ } finally {
5235
+ fs3.closeSync(fd);
5236
+ }
4424
5237
  } catch {
4425
5238
  return null;
4426
5239
  }
@@ -4432,37 +5245,83 @@ function parseJsonBuffer(buf) {
4432
5245
  return null;
4433
5246
  }
4434
5247
  }
4435
- function scanBufferForLines(buf) {
5248
+ var SCAN_CHUNK_BYTES = 64 * 1024;
5249
+ var MAX_SCANNED_FILE_BYTES = 2 ** 31 - 1;
5250
+ function scratchChunk(scratch, size) {
5251
+ const want = Math.min(SCAN_CHUNK_BYTES, size === 0 ? 8192 : size);
5252
+ const current = scratch.chunk;
5253
+ if (current && current.length >= want) return current;
5254
+ const grown = Buffer.allocUnsafe(
5255
+ Math.min(SCAN_CHUNK_BYTES, Math.max(want, 2 * (current?.length ?? 0)))
5256
+ );
5257
+ scratch.chunk = grown;
5258
+ return grown;
5259
+ }
5260
+ function copyFirstLine(parts, chunk, end) {
5261
+ return Buffer.concat([...parts, chunk.subarray(0, end)]);
5262
+ }
5263
+ function scanSessionFile(fd, scratch) {
5264
+ const stats = fs3.fstatSync(fd);
5265
+ const size = stats.isFile() ? stats.size : 0;
5266
+ if (size > MAX_SCANNED_FILE_BYTES) return null;
5267
+ const chunk = scratchChunk(scratch, size);
4436
5268
  let nonEmptyLineCount = 0;
4437
- let firstLineEnd = -1;
4438
- let currentLineHasContent = false;
4439
- for (let i = 0; i < buf.length; i++) {
4440
- const ch = buf[i];
4441
- if (ch === LF) {
4442
- if (currentLineHasContent) nonEmptyLineCount++;
4443
- if (firstLineEnd === -1) firstLineEnd = i;
4444
- currentLineHasContent = false;
4445
- } else if (ch !== CR && ch !== SPACE && ch !== TAB) {
4446
- currentLineHasContent = true;
4447
- }
4448
- }
4449
- if (currentLineHasContent) nonEmptyLineCount++;
4450
- const end = firstLineEnd === -1 ? buf.length : firstLineEnd > 0 && buf[firstLineEnd - 1] === CR ? firstLineEnd - 1 : firstLineEnd;
4451
- return {
4452
- firstLine: buf.subarray(0, end),
4453
- nonEmptyLineCount
4454
- };
5269
+ let lineHasContent = false;
5270
+ const firstLineParts = [];
5271
+ let firstLine;
5272
+ let pos = 0;
5273
+ for (; ; ) {
5274
+ const length = size === 0 ? chunk.length : Math.min(chunk.length, size - pos);
5275
+ if (length === 0) break;
5276
+ const bytesRead = fs3.readSync(fd, chunk, 0, length, null);
5277
+ if (bytesRead === 0) break;
5278
+ pos += bytesRead;
5279
+ let i = 0;
5280
+ while (i < bytesRead) {
5281
+ let lf;
5282
+ if (lineHasContent) {
5283
+ lf = chunk.indexOf(LF, i);
5284
+ if (lf === -1 || lf >= bytesRead) break;
5285
+ nonEmptyLineCount++;
5286
+ lineHasContent = false;
5287
+ } else {
5288
+ const ch = chunk[i];
5289
+ if (ch === CR || ch === SPACE || ch === TAB) {
5290
+ i++;
5291
+ continue;
5292
+ }
5293
+ if (ch !== LF) {
5294
+ lineHasContent = true;
5295
+ continue;
5296
+ }
5297
+ lf = i;
5298
+ }
5299
+ firstLine ??= copyFirstLine(firstLineParts, chunk, lf);
5300
+ i = lf + 1;
5301
+ }
5302
+ if (firstLine === void 0) {
5303
+ firstLineParts.push(Buffer.from(chunk.subarray(0, bytesRead)));
5304
+ }
5305
+ }
5306
+ if (lineHasContent) nonEmptyLineCount++;
5307
+ const [onlyPart] = firstLineParts;
5308
+ firstLine ??= onlyPart !== void 0 && firstLineParts.length === 1 ? onlyPart : Buffer.concat(firstLineParts);
5309
+ return { firstLine, nonEmptyLineCount };
4455
5310
  }
4456
5311
  function loadFavorites(sessionsDir) {
4457
- const buf = readFileBuffer(path2.join(sessionsDir, FAVORITES_FILE));
5312
+ const buf = readFileOrNull(
5313
+ path2.join(sessionsDir, FAVORITES_FILE),
5314
+ (fd) => fs3.readFileSync(fd)
5315
+ );
4458
5316
  if (!buf) return /* @__PURE__ */ new Set();
4459
5317
  const parsed = parseJsonBuffer(buf);
4460
5318
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
4461
5319
  return new Set(parsed.filter((id) => typeof id === "string"));
4462
5320
  }
4463
5321
  function loadSettingsFile(directory, sessionId) {
4464
- const buf = readFileBuffer(
4465
- path2.join(directory, `${sessionId}${SETTINGS_SUFFIX}`)
5322
+ const buf = readFileOrNull(
5323
+ path2.join(directory, `${sessionId}${SETTINGS_SUFFIX}`),
5324
+ (fd) => fs3.readFileSync(fd)
4466
5325
  );
4467
5326
  if (!buf) return null;
4468
5327
  const parsed = parseJsonBuffer(buf);
@@ -4484,9 +5343,11 @@ function getMissionMetadataFromTags(settings) {
4484
5343
  };
4485
5344
  }
4486
5345
  function parseSessionFile(candidate, ctx) {
4487
- const buf = readFileBuffer(candidate.sessionPath);
4488
- if (!buf) return null;
4489
- const scanned = scanBufferForLines(buf);
5346
+ const scanned = readFileOrNull(
5347
+ candidate.sessionPath,
5348
+ (fd) => scanSessionFile(fd, ctx.scratch)
5349
+ );
5350
+ if (!scanned) return null;
4490
5351
  const firstParsed = parseJsonBuffer(scanned.firstLine);
4491
5352
  if (firstParsed === null) return null;
4492
5353
  const firstResult = SessionStartEventSchema.safeParse(firstParsed);
@@ -4603,8 +5464,9 @@ async function listSessions(options = {}) {
4603
5464
  projectCandidates.sort(byMtimeDesc);
4604
5465
  legacyCandidates.sort(byMtimeDesc);
4605
5466
  const parseLimit = options.limit !== void 0 && options.limit >= 0 ? options.limit + PARSE_OVERSAMPLE : void 0;
4606
- const ctx = { favorites };
4607
- const legacyCtx = requiredCwd !== void 0 ? { favorites, requiredCwd } : ctx;
5467
+ const scratch = { chunk: void 0 };
5468
+ const ctx = { favorites, scratch };
5469
+ const legacyCtx = requiredCwd !== void 0 ? { favorites, scratch, requiredCwd } : ctx;
4608
5470
  const projectSessions = parseUpTo(projectCandidates, ctx, parseLimit);
4609
5471
  const legacySessions = parseUpTo(legacyCandidates, legacyCtx, parseLimit);
4610
5472
  const byId = /* @__PURE__ */ new Map();
@@ -4628,10 +5490,10 @@ async function listModels(options = {}) {
4628
5490
  const { includeDisabled, ...transportOptions } = options;
4629
5491
  const { client } = await createConfiguredClient(transportOptions);
4630
5492
  try {
4631
- const response = await client.listModels({
5493
+ const response2 = await client.listModels({
4632
5494
  ...includeDisabled !== void 0 && { includeDisabled }
4633
5495
  });
4634
- return unwrapResponseResult(response).models;
5496
+ return unwrapResponseResult(response2).models;
4635
5497
  } finally {
4636
5498
  await closeQuietly(client);
4637
5499
  }
@@ -4646,6 +5508,7 @@ export {
4646
5508
  AddUserMessageRequestSchema,
4647
5509
  AddUserMessageResponseSchema,
4648
5510
  AddUserMessageResultSchema,
5511
+ AnswerSessionElicitationResponseSchema,
4649
5512
  ApplyPatchToolConfirmationDetailsSchema,
4650
5513
  AskUserCollectedAnswerSchema,
4651
5514
  AskUserConfirmationDetailsSchema,
@@ -4731,6 +5594,7 @@ export {
4731
5594
  DroidClientError,
4732
5595
  DroidClientEvent,
4733
5596
  DroidClientMethod,
5597
+ DroidConnectionEndedError,
4734
5598
  DroidErrorType,
4735
5599
  DroidEvent,
4736
5600
  DroidHookEventSchema,
@@ -4747,6 +5611,8 @@ export {
4747
5611
  DroidWorkingStateChangedNotificationSchema,
4748
5612
  EditToolConfirmationDetailsSchema,
4749
5613
  EmptyResultSchema,
5614
+ EnvironmentListResponseSchema,
5615
+ EnvironmentSchema,
4750
5616
  ErrorDetailSchema,
4751
5617
  ErrorNotificationSchema,
4752
5618
  ExecToolInfoSchema,
@@ -4759,7 +5625,7 @@ export {
4759
5625
  ExitSpecModeConfirmationDetailsSchema,
4760
5626
  FACTORY_CLIENT_HEADER,
4761
5627
  FACTORY_CLIENT_VERSION,
4762
- FACTORY_PROTOCOL_VERSION2 as FACTORY_PROTOCOL_VERSION,
5628
+ FACTORY_PROTOCOL_VERSION,
4763
5629
  FactoryDaemonTransport,
4764
5630
  FactoryDroidMessageRole,
4765
5631
  FactoryDroidMessageSchema,
@@ -4802,7 +5668,7 @@ export {
4802
5668
  InvalidSessionCwdError,
4803
5669
  IpcDaemonClientTransport,
4804
5670
  IssueSeverity,
4805
- JSONRPC_VERSION3 as JSONRPC_VERSION,
5671
+ JSONRPC_VERSION,
4806
5672
  JsonArraySchema,
4807
5673
  JsonObjectSchema,
4808
5674
  JsonRpcEnvelopeSchema,
@@ -4821,7 +5687,7 @@ export {
4821
5687
  KillWorkerSessionRequestSchema,
4822
5688
  KillWorkerSessionResponseSchema,
4823
5689
  KillWorkerSessionResultSchema,
4824
- LEGACY_FACTORY_API_VERSION2 as LEGACY_FACTORY_API_VERSION,
5690
+ LEGACY_FACTORY_API_VERSION,
4825
5691
  LOCAL_MACHINE_ID,
4826
5692
  ListMcpRegistryRequestParamsSchema,
4827
5693
  ListMcpRegistryRequestSchema,
@@ -4837,6 +5703,7 @@ export {
4837
5703
  ListMcpToolsResultSchema,
4838
5704
  ListModelsOptionsSchema,
4839
5705
  ListModelsResultSchema,
5706
+ ListSessionElicitationsResponseSchema,
4840
5707
  ListSkillsRequestParamsSchema,
4841
5708
  ListSkillsRequestSchema,
4842
5709
  ListSkillsResponseSchema,
@@ -4930,6 +5797,7 @@ export {
4930
5797
  RenameSessionRequestSchema,
4931
5798
  RenameSessionResponseSchema,
4932
5799
  RenameSessionResultSchema,
5800
+ RequestCancelledError,
4933
5801
  RequestPermissionRequestParamsSchema,
4934
5802
  RequestPermissionRequestSchema,
4935
5803
  RequestPermissionResponseSchema,
@@ -4949,6 +5817,7 @@ export {
4949
5817
  SessionArtifactSchema,
4950
5818
  SessionArtifactSource,
4951
5819
  SessionArtifactsSchema,
5820
+ SessionElicitationSchema,
4952
5821
  SessionError,
4953
5822
  SessionMetadataSchema,
4954
5823
  SessionNotFoundError,
@@ -5049,7 +5918,9 @@ export {
5049
5918
  WorkerSelectedFeatureEntrySchema,
5050
5919
  WorkerStartedEntrySchema,
5051
5920
  WorkerStateInfoSchema,
5921
+ MissionMachineTargetKind as WorkstreamMachineTargetKind,
5052
5922
  __resetTransportLoggerForTests,
5923
+ answerRemoteSessionElicitation,
5053
5924
  appendContextSession,
5054
5925
  buildUserMessageContentBlocks,
5055
5926
  configureDaemonCoreDeps,
@@ -5088,6 +5959,7 @@ export {
5088
5959
  hasSubagentSessionTag,
5089
5960
  inspectJsonRpcEnvelope,
5090
5961
  isDaemonQueuedMessageKind,
5962
+ isDroidConnectionEnded,
5091
5963
  isPendingToolResultMarker,
5092
5964
  isPersistedHookMessage,
5093
5965
  isReviewableQueuedMessageKind,
@@ -5096,8 +5968,10 @@ export {
5096
5968
  listContextDocuments,
5097
5969
  listContextSessions,
5098
5970
  listContextSpaces,
5971
+ listEnvironments,
5099
5972
  listMachineTemplates,
5100
5973
  listModels,
5974
+ listRemoteSessionElicitations,
5101
5975
  listRemoteSessions,
5102
5976
  listSessions,
5103
5977
  logTransportMessage,