sandbox-agent 0.2.0 → 0.2.2

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/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/client.ts
2
2
  import {
3
3
  AcpHttpClient,
4
+ AcpRpcError,
4
5
  PROTOCOL_VERSION
5
6
  } from "acp-http-client";
6
7
 
@@ -82,7 +83,9 @@ var InMemorySessionPersistDriver = class {
82
83
  function cloneSessionRecord(session) {
83
84
  return {
84
85
  ...session,
85
- sessionInit: session.sessionInit ? JSON.parse(JSON.stringify(session.sessionInit)) : void 0
86
+ sessionInit: session.sessionInit ? JSON.parse(JSON.stringify(session.sessionInit)) : void 0,
87
+ configOptions: session.configOptions ? JSON.parse(JSON.stringify(session.configOptions)) : void 0,
88
+ modes: session.modes ? JSON.parse(JSON.stringify(session.modes)) : session.modes
86
89
  };
87
90
  }
88
91
  function cloneSessionEvent(event) {
@@ -121,9 +124,16 @@ function parseCursor(cursor) {
121
124
  // src/client.ts
122
125
  var API_PREFIX = "/v1";
123
126
  var FS_PATH = `${API_PREFIX}/fs`;
127
+ var DEFAULT_BASE_URL = "http://sandbox-agent";
124
128
  var DEFAULT_REPLAY_MAX_EVENTS = 50;
125
129
  var DEFAULT_REPLAY_MAX_CHARS = 12e3;
126
130
  var EVENT_INDEX_SCAN_EVENTS_LIMIT = 500;
131
+ var SESSION_CANCEL_METHOD = "session/cancel";
132
+ var MANUAL_CANCEL_ERROR = "Manual session/cancel calls are not allowed. Use destroySession(sessionId) instead.";
133
+ var HEALTH_WAIT_MIN_DELAY_MS = 500;
134
+ var HEALTH_WAIT_MAX_DELAY_MS = 15e3;
135
+ var HEALTH_WAIT_LOG_AFTER_MS = 5e3;
136
+ var HEALTH_WAIT_LOG_EVERY_MS = 1e4;
127
137
  var SandboxAgentError = class extends Error {
128
138
  status;
129
139
  problem;
@@ -136,6 +146,52 @@ var SandboxAgentError = class extends Error {
136
146
  this.response = response;
137
147
  }
138
148
  };
149
+ var UnsupportedSessionCategoryError = class extends Error {
150
+ sessionId;
151
+ category;
152
+ availableCategories;
153
+ constructor(sessionId, category, availableCategories) {
154
+ super(
155
+ `Session '${sessionId}' does not support category '${category}'. Available categories: ${availableCategories.join(", ") || "(none)"}`
156
+ );
157
+ this.name = "UnsupportedSessionCategoryError";
158
+ this.sessionId = sessionId;
159
+ this.category = category;
160
+ this.availableCategories = availableCategories;
161
+ }
162
+ };
163
+ var UnsupportedSessionValueError = class extends Error {
164
+ sessionId;
165
+ category;
166
+ configId;
167
+ requestedValue;
168
+ allowedValues;
169
+ constructor(sessionId, category, configId, requestedValue, allowedValues) {
170
+ super(
171
+ `Session '${sessionId}' does not support value '${requestedValue}' for category '${category}' (configId='${configId}'). Allowed values: ${allowedValues.join(", ") || "(none)"}`
172
+ );
173
+ this.name = "UnsupportedSessionValueError";
174
+ this.sessionId = sessionId;
175
+ this.category = category;
176
+ this.configId = configId;
177
+ this.requestedValue = requestedValue;
178
+ this.allowedValues = allowedValues;
179
+ }
180
+ };
181
+ var UnsupportedSessionConfigOptionError = class extends Error {
182
+ sessionId;
183
+ configId;
184
+ availableConfigIds;
185
+ constructor(sessionId, configId, availableConfigIds) {
186
+ super(
187
+ `Session '${sessionId}' does not expose config option '${configId}'. Available configIds: ${availableConfigIds.join(", ") || "(none)"}`
188
+ );
189
+ this.name = "UnsupportedSessionConfigOptionError";
190
+ this.sessionId = sessionId;
191
+ this.configId = configId;
192
+ this.availableConfigIds = availableConfigIds;
193
+ }
194
+ };
139
195
  var Session = class {
140
196
  record;
141
197
  sandbox;
@@ -178,6 +234,32 @@ var Session = class {
178
234
  const response = await this.send("session/prompt", { prompt });
179
235
  return response;
180
236
  }
237
+ async setMode(modeId) {
238
+ const updated = await this.sandbox.setSessionMode(this.id, modeId);
239
+ this.apply(updated.session.toRecord());
240
+ return updated.response;
241
+ }
242
+ async setConfigOption(configId, value) {
243
+ const updated = await this.sandbox.setSessionConfigOption(this.id, configId, value);
244
+ this.apply(updated.session.toRecord());
245
+ return updated.response;
246
+ }
247
+ async setModel(model) {
248
+ const updated = await this.sandbox.setSessionModel(this.id, model);
249
+ this.apply(updated.session.toRecord());
250
+ return updated.response;
251
+ }
252
+ async setThoughtLevel(thoughtLevel) {
253
+ const updated = await this.sandbox.setSessionThoughtLevel(this.id, thoughtLevel);
254
+ this.apply(updated.session.toRecord());
255
+ return updated.response;
256
+ }
257
+ async getConfigOptions() {
258
+ return this.sandbox.getSessionConfigOptions(this.id);
259
+ }
260
+ async getModes() {
261
+ return this.sandbox.getSessionModes(this.id);
262
+ }
181
263
  onEvent(listener) {
182
264
  return this.sandbox.onSessionEvent(this.id, listener);
183
265
  }
@@ -197,6 +279,8 @@ var LiveAcpConnection = class _LiveAcpConnection {
197
279
  pendingNewSessionLocals = [];
198
280
  pendingRequestSessionById = /* @__PURE__ */ new Map();
199
281
  pendingReplayByLocalSessionId = /* @__PURE__ */ new Map();
282
+ lastAdapterExit = null;
283
+ lastAdapterExitAt = 0;
200
284
  onObservedEnvelope;
201
285
  constructor(agent, connectionId, acp, onObservedEnvelope) {
202
286
  this.agent = agent;
@@ -218,6 +302,10 @@ var LiveAcpConnection = class _LiveAcpConnection {
218
302
  },
219
303
  client: {
220
304
  sessionUpdate: async (_notification) => {
305
+ },
306
+ extNotification: async (method, params) => {
307
+ if (!live) return;
308
+ live.handleAdapterNotification(method, params);
221
309
  }
222
310
  },
223
311
  onEnvelope: (envelope, direction) => {
@@ -265,6 +353,7 @@ var LiveAcpConnection = class _LiveAcpConnection {
265
353
  this.pendingReplayByLocalSessionId.set(localSessionId, replayText);
266
354
  }
267
355
  async createRemoteSession(localSessionId, sessionInit) {
356
+ const createStartedAt = Date.now();
268
357
  this.pendingNewSessionLocals.push(localSessionId);
269
358
  try {
270
359
  const response = await this.acp.newSession(sessionInit);
@@ -275,6 +364,11 @@ var LiveAcpConnection = class _LiveAcpConnection {
275
364
  if (index !== -1) {
276
365
  this.pendingNewSessionLocals.splice(index, 1);
277
366
  }
367
+ const adapterExit = this.lastAdapterExit;
368
+ if (adapterExit && this.lastAdapterExitAt >= createStartedAt) {
369
+ const suffix = adapterExit.code == null ? "" : ` (code ${adapterExit.code})`;
370
+ throw new Error(`Agent process exited while creating session${suffix}`);
371
+ }
278
372
  throw error;
279
373
  }
280
374
  }
@@ -316,6 +410,16 @@ var LiveAcpConnection = class _LiveAcpConnection {
316
410
  const localSessionId = this.resolveSessionId(envelope, direction);
317
411
  this.onObservedEnvelope(this, envelope, direction, localSessionId);
318
412
  }
413
+ handleAdapterNotification(method, params) {
414
+ if (method !== "_adapter/agent_exited") {
415
+ return;
416
+ }
417
+ this.lastAdapterExit = {
418
+ success: params.success === true,
419
+ code: typeof params.code === "number" ? params.code : null
420
+ };
421
+ this.lastAdapterExitAt = Date.now();
422
+ }
319
423
  resolveSessionId(envelope, direction) {
320
424
  const id = envelopeId(envelope);
321
425
  const method = envelopeMethod(envelope);
@@ -359,26 +463,39 @@ var SandboxAgent = class _SandboxAgent {
359
463
  token;
360
464
  fetcher;
361
465
  defaultHeaders;
466
+ healthWait;
467
+ healthWaitAbortController = new AbortController();
362
468
  persist;
363
469
  replayMaxEvents;
364
470
  replayMaxChars;
365
471
  spawnHandle;
472
+ healthPromise;
473
+ healthError;
474
+ disposed = false;
366
475
  liveConnections = /* @__PURE__ */ new Map();
476
+ pendingLiveConnections = /* @__PURE__ */ new Map();
367
477
  sessionHandles = /* @__PURE__ */ new Map();
368
478
  eventListeners = /* @__PURE__ */ new Map();
369
479
  nextSessionEventIndexBySession = /* @__PURE__ */ new Map();
370
480
  seedSessionEventIndexBySession = /* @__PURE__ */ new Map();
371
481
  constructor(options) {
372
- this.baseUrl = options.baseUrl.replace(/\/$/, "");
482
+ const baseUrl = options.baseUrl?.trim();
483
+ if (!baseUrl && !options.fetch) {
484
+ throw new Error("baseUrl is required unless fetch is provided.");
485
+ }
486
+ this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
373
487
  this.token = options.token;
374
- this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
488
+ const resolvedFetch = options.fetch ?? globalThis.fetch?.bind(globalThis);
489
+ if (!resolvedFetch) {
490
+ throw new Error("Fetch API is not available; provide a fetch implementation.");
491
+ }
492
+ this.fetcher = resolvedFetch;
375
493
  this.defaultHeaders = options.headers;
494
+ this.healthWait = normalizeHealthWaitOptions(options.waitForHealth, options.signal);
376
495
  this.persist = options.persist ?? new InMemorySessionPersistDriver();
377
496
  this.replayMaxEvents = normalizePositiveInt(options.replayMaxEvents, DEFAULT_REPLAY_MAX_EVENTS);
378
497
  this.replayMaxChars = normalizePositiveInt(options.replayMaxChars, DEFAULT_REPLAY_MAX_CHARS);
379
- if (!this.fetcher) {
380
- throw new Error("Fetch API is not available; provide a fetch implementation.");
381
- }
498
+ this.startHealthWait();
382
499
  }
383
500
  static async connect(options) {
384
501
  return new _SandboxAgent(options);
@@ -389,12 +506,14 @@ var SandboxAgent = class _SandboxAgent {
389
506
  throw new Error("SandboxAgent.start requires spawn to be enabled.");
390
507
  }
391
508
  const { spawnSandboxAgent } = await import("./spawn-BQVVCZX7.js");
392
- const handle = await spawnSandboxAgent(spawnOptions, options.fetch ?? globalThis.fetch);
509
+ const resolvedFetch = options.fetch ?? globalThis.fetch?.bind(globalThis);
510
+ const handle = await spawnSandboxAgent(spawnOptions, resolvedFetch);
393
511
  const client = new _SandboxAgent({
394
512
  baseUrl: handle.baseUrl,
395
513
  token: handle.token,
396
514
  fetch: options.fetch,
397
515
  headers: options.headers,
516
+ waitForHealth: false,
398
517
  persist: options.persist,
399
518
  replayMaxEvents: options.replayMaxEvents,
400
519
  replayMaxChars: options.replayMaxChars
@@ -403,8 +522,18 @@ var SandboxAgent = class _SandboxAgent {
403
522
  return client;
404
523
  }
405
524
  async dispose() {
525
+ this.disposed = true;
526
+ this.healthWaitAbortController.abort(createAbortError("SandboxAgent was disposed."));
406
527
  const connections = [...this.liveConnections.values()];
407
528
  this.liveConnections.clear();
529
+ const pending = [...this.pendingLiveConnections.values()];
530
+ this.pendingLiveConnections.clear();
531
+ const pendingSettled = await Promise.allSettled(pending);
532
+ for (const item of pendingSettled) {
533
+ if (item.status === "fulfilled") {
534
+ connections.push(item.value);
535
+ }
536
+ }
408
537
  await Promise.all(
409
538
  connections.map(async (connection) => {
410
539
  await connection.close();
@@ -446,12 +575,32 @@ var SandboxAgent = class _SandboxAgent {
446
575
  agentSessionId: response.sessionId,
447
576
  lastConnectionId: live.connectionId,
448
577
  createdAt: nowMs(),
449
- sessionInit
578
+ sessionInit,
579
+ configOptions: cloneConfigOptions(response.configOptions),
580
+ modes: cloneModes(response.modes)
450
581
  };
451
582
  await this.persist.updateSession(record);
452
583
  this.nextSessionEventIndexBySession.set(record.id, 1);
453
584
  live.bindSession(record.id, record.agentSessionId);
454
- return this.upsertSessionHandle(record);
585
+ let session = this.upsertSessionHandle(record);
586
+ try {
587
+ if (request.mode) {
588
+ session = (await this.setSessionMode(session.id, request.mode)).session;
589
+ }
590
+ if (request.model) {
591
+ session = (await this.setSessionModel(session.id, request.model)).session;
592
+ }
593
+ if (request.thoughtLevel) {
594
+ session = (await this.setSessionThoughtLevel(session.id, request.thoughtLevel)).session;
595
+ }
596
+ } catch (err) {
597
+ try {
598
+ await this.destroySession(session.id);
599
+ } catch {
600
+ }
601
+ throw err;
602
+ }
603
+ return session;
455
604
  }
456
605
  async resumeSession(id) {
457
606
  const existing = await this.persist.getSession(id);
@@ -469,7 +618,9 @@ var SandboxAgent = class _SandboxAgent {
469
618
  ...existing,
470
619
  agentSessionId: recreated.sessionId,
471
620
  lastConnectionId: live.connectionId,
472
- destroyedAt: void 0
621
+ destroyedAt: void 0,
622
+ configOptions: cloneConfigOptions(recreated.configOptions),
623
+ modes: cloneModes(recreated.modes)
473
624
  };
474
625
  await this.persist.updateSession(updated);
475
626
  live.bindSession(updated.id, updated.agentSessionId);
@@ -479,15 +630,26 @@ var SandboxAgent = class _SandboxAgent {
479
630
  async resumeOrCreateSession(request) {
480
631
  const existing = await this.persist.getSession(request.id);
481
632
  if (existing) {
482
- return this.resumeSession(existing.id);
633
+ let session = await this.resumeSession(existing.id);
634
+ if (request.mode) {
635
+ session = (await this.setSessionMode(session.id, request.mode)).session;
636
+ }
637
+ if (request.model) {
638
+ session = (await this.setSessionModel(session.id, request.model)).session;
639
+ }
640
+ if (request.thoughtLevel) {
641
+ session = (await this.setSessionThoughtLevel(session.id, request.thoughtLevel)).session;
642
+ }
643
+ return session;
483
644
  }
484
645
  return this.createSession(request);
485
646
  }
486
647
  async destroySession(id) {
487
- const existing = await this.persist.getSession(id);
488
- if (!existing) {
489
- throw new Error(`session '${id}' not found`);
648
+ try {
649
+ await this.sendSessionMethodInternal(id, SESSION_CANCEL_METHOD, {}, {}, true);
650
+ } catch {
490
651
  }
652
+ const existing = await this.requireSessionRecord(id);
491
653
  const updated = {
492
654
  ...existing,
493
655
  destroyedAt: nowMs()
@@ -495,7 +657,132 @@ var SandboxAgent = class _SandboxAgent {
495
657
  await this.persist.updateSession(updated);
496
658
  return this.upsertSessionHandle(updated);
497
659
  }
660
+ async setSessionMode(sessionId, modeId) {
661
+ const mode = modeId.trim();
662
+ if (!mode) {
663
+ throw new Error("setSessionMode requires a non-empty modeId");
664
+ }
665
+ const record = await this.requireSessionRecord(sessionId);
666
+ const knownModeIds = extractKnownModeIds(record.modes);
667
+ if (knownModeIds.length > 0 && !knownModeIds.includes(mode)) {
668
+ throw new UnsupportedSessionValueError(sessionId, "mode", "mode", mode, knownModeIds);
669
+ }
670
+ try {
671
+ return await this.sendSessionMethodInternal(
672
+ sessionId,
673
+ "session/set_mode",
674
+ { modeId: mode },
675
+ {},
676
+ false
677
+ );
678
+ } catch (error) {
679
+ if (!(error instanceof AcpRpcError) || error.code !== -32601) {
680
+ throw error;
681
+ }
682
+ return this.setSessionCategoryValue(sessionId, "mode", mode);
683
+ }
684
+ }
685
+ async setSessionConfigOption(sessionId, configId, value) {
686
+ const resolvedConfigId = configId.trim();
687
+ if (!resolvedConfigId) {
688
+ throw new Error("setSessionConfigOption requires a non-empty configId");
689
+ }
690
+ const resolvedValue = value.trim();
691
+ if (!resolvedValue) {
692
+ throw new Error("setSessionConfigOption requires a non-empty value");
693
+ }
694
+ const options = await this.getSessionConfigOptions(sessionId);
695
+ const option = findConfigOptionById(options, resolvedConfigId);
696
+ if (!option) {
697
+ throw new UnsupportedSessionConfigOptionError(
698
+ sessionId,
699
+ resolvedConfigId,
700
+ options.map((item) => item.id)
701
+ );
702
+ }
703
+ const allowedValues = extractConfigValues(option);
704
+ if (allowedValues.length > 0 && !allowedValues.includes(resolvedValue)) {
705
+ throw new UnsupportedSessionValueError(
706
+ sessionId,
707
+ option.category ?? "uncategorized",
708
+ option.id,
709
+ resolvedValue,
710
+ allowedValues
711
+ );
712
+ }
713
+ return await this.sendSessionMethodInternal(
714
+ sessionId,
715
+ "session/set_config_option",
716
+ {
717
+ configId: resolvedConfigId,
718
+ value: resolvedValue
719
+ },
720
+ {},
721
+ false
722
+ );
723
+ }
724
+ async setSessionModel(sessionId, model) {
725
+ return this.setSessionCategoryValue(sessionId, "model", model);
726
+ }
727
+ async setSessionThoughtLevel(sessionId, thoughtLevel) {
728
+ return this.setSessionCategoryValue(sessionId, "thought_level", thoughtLevel);
729
+ }
730
+ async getSessionConfigOptions(sessionId) {
731
+ const record = await this.requireSessionRecord(sessionId);
732
+ const hydrated = await this.hydrateSessionConfigOptions(record.id, record);
733
+ return cloneConfigOptions(hydrated.configOptions) ?? [];
734
+ }
735
+ async getSessionModes(sessionId) {
736
+ const record = await this.requireSessionRecord(sessionId);
737
+ return cloneModes(record.modes);
738
+ }
739
+ async setSessionCategoryValue(sessionId, category, value) {
740
+ const resolvedValue = value.trim();
741
+ if (!resolvedValue) {
742
+ throw new Error(`setSession${toTitleCase(category)} requires a non-empty value`);
743
+ }
744
+ const options = await this.getSessionConfigOptions(sessionId);
745
+ const option = findConfigOptionByCategory(options, category);
746
+ if (!option) {
747
+ const categories = uniqueCategories(options);
748
+ throw new UnsupportedSessionCategoryError(sessionId, category, categories);
749
+ }
750
+ const allowedValues = extractConfigValues(option);
751
+ if (allowedValues.length > 0 && !allowedValues.includes(resolvedValue)) {
752
+ throw new UnsupportedSessionValueError(
753
+ sessionId,
754
+ category,
755
+ option.id,
756
+ resolvedValue,
757
+ allowedValues
758
+ );
759
+ }
760
+ return this.setSessionConfigOption(sessionId, option.id, resolvedValue);
761
+ }
762
+ async hydrateSessionConfigOptions(sessionId, snapshot) {
763
+ if (snapshot.configOptions !== void 0) {
764
+ return snapshot;
765
+ }
766
+ const info = await this.getAgent(snapshot.agent, { config: true });
767
+ const configOptions = normalizeSessionConfigOptions(info.configOptions) ?? [];
768
+ const record = await this.persist.getSession(sessionId);
769
+ if (!record) {
770
+ return { ...snapshot, configOptions };
771
+ }
772
+ const updated = {
773
+ ...record,
774
+ configOptions
775
+ };
776
+ await this.persist.updateSession(updated);
777
+ return updated;
778
+ }
498
779
  async sendSessionMethod(sessionId, method, params, options = {}) {
780
+ return this.sendSessionMethodInternal(sessionId, method, params, options, false);
781
+ }
782
+ async sendSessionMethodInternal(sessionId, method, params, options, allowManagedCancel) {
783
+ if (method === SESSION_CANCEL_METHOD && !allowManagedCancel) {
784
+ throw new Error(MANUAL_CANCEL_ERROR);
785
+ }
499
786
  const record = await this.persist.getSession(sessionId);
500
787
  if (!record) {
501
788
  throw new Error(`session '${sessionId}' not found`);
@@ -503,15 +790,73 @@ var SandboxAgent = class _SandboxAgent {
503
790
  const live = await this.getLiveConnection(record.agent);
504
791
  if (!live.hasBoundSession(record.id, record.agentSessionId)) {
505
792
  const restored = await this.resumeSession(record.id);
506
- return this.sendSessionMethod(restored.id, method, params, options);
793
+ return this.sendSessionMethodInternal(restored.id, method, params, options, allowManagedCancel);
507
794
  }
508
795
  const response = await live.sendSessionMethod(record.id, method, params, options);
796
+ await this.persistSessionStateFromMethod(record.id, method, params, response);
509
797
  const refreshed = await this.requireSessionRecord(record.id);
510
798
  return {
511
799
  session: this.upsertSessionHandle(refreshed),
512
800
  response
513
801
  };
514
802
  }
803
+ async persistSessionStateFromMethod(sessionId, method, params, response) {
804
+ const record = await this.persist.getSession(sessionId);
805
+ if (!record) {
806
+ return;
807
+ }
808
+ if (method === "session/set_config_option") {
809
+ const configId = typeof params.configId === "string" ? params.configId : null;
810
+ const value = typeof params.value === "string" ? params.value : null;
811
+ const updates = {};
812
+ const serverConfigOptions = extractConfigOptionsFromSetResponse(response);
813
+ if (serverConfigOptions) {
814
+ updates.configOptions = cloneConfigOptions(serverConfigOptions);
815
+ } else if (record.configOptions && configId && value) {
816
+ const updated = applyConfigOptionValue(record.configOptions, configId, value);
817
+ if (updated) {
818
+ updates.configOptions = updated;
819
+ }
820
+ }
821
+ if (configId && value) {
822
+ const source = updates.configOptions ?? record.configOptions;
823
+ const option = source ? findConfigOptionById(source, configId) : null;
824
+ if (option?.category === "mode") {
825
+ const nextModes = applyCurrentMode(record.modes, value);
826
+ if (nextModes) {
827
+ updates.modes = nextModes;
828
+ }
829
+ }
830
+ }
831
+ if (Object.keys(updates).length > 0) {
832
+ await this.persist.updateSession({ ...record, ...updates });
833
+ }
834
+ return;
835
+ }
836
+ if (method === "session/set_mode") {
837
+ const modeId = typeof params.modeId === "string" ? params.modeId : null;
838
+ if (!modeId) {
839
+ return;
840
+ }
841
+ const updates = {};
842
+ const nextModes = applyCurrentMode(record.modes, modeId);
843
+ if (nextModes) {
844
+ updates.modes = nextModes;
845
+ }
846
+ if (record.configOptions) {
847
+ const modeOption = findConfigOptionByCategory(record.configOptions, "mode");
848
+ if (modeOption) {
849
+ const updated = applyConfigOptionValue(record.configOptions, modeOption.id, modeId);
850
+ if (updated) {
851
+ updates.configOptions = updated;
852
+ }
853
+ }
854
+ }
855
+ if (Object.keys(updates).length > 0) {
856
+ await this.persist.updateSession({ ...record, ...updates });
857
+ }
858
+ }
859
+ }
515
860
  onSessionEvent(sessionId, listener) {
516
861
  const listeners = this.eventListeners.get(sessionId) ?? /* @__PURE__ */ new Set();
517
862
  listeners.add(listener);
@@ -528,16 +873,16 @@ var SandboxAgent = class _SandboxAgent {
528
873
  };
529
874
  }
530
875
  async getHealth() {
531
- return this.requestJson("GET", `${API_PREFIX}/health`);
876
+ return this.requestHealth();
532
877
  }
533
878
  async listAgents(options) {
534
879
  return this.requestJson("GET", `${API_PREFIX}/agents`, {
535
- query: options?.config ? { config: "true" } : void 0
880
+ query: toAgentQuery(options)
536
881
  });
537
882
  }
538
883
  async getAgent(agent, options) {
539
884
  return this.requestJson("GET", `${API_PREFIX}/agents/${encodeURIComponent(agent)}`, {
540
- query: options?.config ? { config: "true" } : void 0
885
+ query: toAgentQuery(options)
541
886
  });
542
887
  }
543
888
  async installAgent(agent, request = {}) {
@@ -609,25 +954,141 @@ var SandboxAgent = class _SandboxAgent {
609
954
  async deleteSkillsConfig(query) {
610
955
  await this.requestRaw("DELETE", `${API_PREFIX}/config/skills`, { query });
611
956
  }
957
+ async getProcessConfig() {
958
+ return this.requestJson("GET", `${API_PREFIX}/processes/config`);
959
+ }
960
+ async setProcessConfig(config) {
961
+ return this.requestJson("POST", `${API_PREFIX}/processes/config`, {
962
+ body: config
963
+ });
964
+ }
965
+ async createProcess(request) {
966
+ return this.requestJson("POST", `${API_PREFIX}/processes`, {
967
+ body: request
968
+ });
969
+ }
970
+ async runProcess(request) {
971
+ return this.requestJson("POST", `${API_PREFIX}/processes/run`, {
972
+ body: request
973
+ });
974
+ }
975
+ async listProcesses() {
976
+ return this.requestJson("GET", `${API_PREFIX}/processes`);
977
+ }
978
+ async getProcess(id) {
979
+ return this.requestJson("GET", `${API_PREFIX}/processes/${encodeURIComponent(id)}`);
980
+ }
981
+ async stopProcess(id, query) {
982
+ return this.requestJson("POST", `${API_PREFIX}/processes/${encodeURIComponent(id)}/stop`, {
983
+ query
984
+ });
985
+ }
986
+ async killProcess(id, query) {
987
+ return this.requestJson("POST", `${API_PREFIX}/processes/${encodeURIComponent(id)}/kill`, {
988
+ query
989
+ });
990
+ }
991
+ async deleteProcess(id) {
992
+ await this.requestRaw("DELETE", `${API_PREFIX}/processes/${encodeURIComponent(id)}`);
993
+ }
994
+ async getProcessLogs(id, query = {}) {
995
+ return this.requestJson("GET", `${API_PREFIX}/processes/${encodeURIComponent(id)}/logs`, {
996
+ query
997
+ });
998
+ }
999
+ async followProcessLogs(id, listener, query = {}) {
1000
+ const abortController = new AbortController();
1001
+ const response = await this.requestRaw(
1002
+ "GET",
1003
+ `${API_PREFIX}/processes/${encodeURIComponent(id)}/logs`,
1004
+ {
1005
+ query: { ...query, follow: true },
1006
+ accept: "text/event-stream",
1007
+ signal: abortController.signal
1008
+ }
1009
+ );
1010
+ if (!response.body) {
1011
+ abortController.abort();
1012
+ throw new Error("SSE stream is not readable in this environment.");
1013
+ }
1014
+ const closed = consumeProcessLogSse(response.body, listener, abortController.signal);
1015
+ return {
1016
+ close: () => abortController.abort(),
1017
+ closed
1018
+ };
1019
+ }
1020
+ async sendProcessInput(id, request) {
1021
+ return this.requestJson("POST", `${API_PREFIX}/processes/${encodeURIComponent(id)}/input`, {
1022
+ body: request
1023
+ });
1024
+ }
1025
+ async resizeProcessTerminal(id, request) {
1026
+ return this.requestJson(
1027
+ "POST",
1028
+ `${API_PREFIX}/processes/${encodeURIComponent(id)}/terminal/resize`,
1029
+ {
1030
+ body: request
1031
+ }
1032
+ );
1033
+ }
1034
+ buildProcessTerminalWebSocketUrl(id, options = {}) {
1035
+ return toWebSocketUrl(
1036
+ this.buildUrl(`${API_PREFIX}/processes/${encodeURIComponent(id)}/terminal/ws`, {
1037
+ access_token: options.accessToken ?? this.token
1038
+ })
1039
+ );
1040
+ }
1041
+ connectProcessTerminalWebSocket(id, options = {}) {
1042
+ const WebSocketCtor = options.WebSocket ?? globalThis.WebSocket;
1043
+ if (!WebSocketCtor) {
1044
+ throw new Error("WebSocket API is not available; provide a WebSocket implementation.");
1045
+ }
1046
+ return new WebSocketCtor(
1047
+ this.buildProcessTerminalWebSocketUrl(id, {
1048
+ accessToken: options.accessToken
1049
+ }),
1050
+ options.protocols
1051
+ );
1052
+ }
612
1053
  async getLiveConnection(agent) {
1054
+ await this.awaitHealthy();
613
1055
  const existing = this.liveConnections.get(agent);
614
1056
  if (existing) {
615
1057
  return existing;
616
1058
  }
617
- const serverId = `sdk-${agent}-${randomId()}`;
618
- const created = await LiveAcpConnection.create({
619
- baseUrl: this.baseUrl,
620
- token: this.token,
621
- fetcher: this.fetcher,
622
- headers: this.defaultHeaders,
623
- agent,
624
- serverId,
625
- onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
626
- void this.persistObservedEnvelope(connection, envelope, direction, localSessionId);
1059
+ const pending = this.pendingLiveConnections.get(agent);
1060
+ if (pending) {
1061
+ return pending;
1062
+ }
1063
+ const creating = (async () => {
1064
+ const serverId = `sdk-${agent}-${randomId()}`;
1065
+ const created = await LiveAcpConnection.create({
1066
+ baseUrl: this.baseUrl,
1067
+ token: this.token,
1068
+ fetcher: this.fetcher,
1069
+ headers: this.defaultHeaders,
1070
+ agent,
1071
+ serverId,
1072
+ onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
1073
+ void this.persistObservedEnvelope(connection, envelope, direction, localSessionId);
1074
+ }
1075
+ });
1076
+ const raced = this.liveConnections.get(agent);
1077
+ if (raced) {
1078
+ await created.close();
1079
+ return raced;
627
1080
  }
628
- });
629
- this.liveConnections.set(agent, created);
630
- return created;
1081
+ this.liveConnections.set(agent, created);
1082
+ return created;
1083
+ })();
1084
+ this.pendingLiveConnections.set(agent, creating);
1085
+ try {
1086
+ return await creating;
1087
+ } finally {
1088
+ if (this.pendingLiveConnections.get(agent) === creating) {
1089
+ this.pendingLiveConnections.delete(agent);
1090
+ }
1091
+ }
631
1092
  }
632
1093
  async persistObservedEnvelope(connection, envelope, direction, localSessionId) {
633
1094
  if (!localSessionId) {
@@ -643,6 +1104,7 @@ var SandboxAgent = class _SandboxAgent {
643
1104
  payload: cloneEnvelope(envelope)
644
1105
  };
645
1106
  await this.persist.insertEvent(event);
1107
+ await this.persistSessionStateFromEvent(localSessionId, envelope, direction);
646
1108
  const listeners = this.eventListeners.get(localSessionId);
647
1109
  if (!listeners || listeners.size === 0) {
648
1110
  return;
@@ -651,6 +1113,46 @@ var SandboxAgent = class _SandboxAgent {
651
1113
  listener(event);
652
1114
  }
653
1115
  }
1116
+ async persistSessionStateFromEvent(sessionId, envelope, direction) {
1117
+ if (direction !== "inbound") {
1118
+ return;
1119
+ }
1120
+ if (envelopeMethod(envelope) !== "session/update") {
1121
+ return;
1122
+ }
1123
+ const update = envelopeSessionUpdate(envelope);
1124
+ if (!update || typeof update.sessionUpdate !== "string") {
1125
+ return;
1126
+ }
1127
+ const record = await this.persist.getSession(sessionId);
1128
+ if (!record) {
1129
+ return;
1130
+ }
1131
+ if (update.sessionUpdate === "config_option_update") {
1132
+ const configOptions = normalizeSessionConfigOptions(update.configOptions);
1133
+ if (configOptions) {
1134
+ await this.persist.updateSession({
1135
+ ...record,
1136
+ configOptions
1137
+ });
1138
+ }
1139
+ return;
1140
+ }
1141
+ if (update.sessionUpdate === "current_mode_update") {
1142
+ const modeId = typeof update.currentModeId === "string" ? update.currentModeId : null;
1143
+ if (!modeId) {
1144
+ return;
1145
+ }
1146
+ const nextModes = applyCurrentMode(record.modes, modeId);
1147
+ if (!nextModes) {
1148
+ return;
1149
+ }
1150
+ await this.persist.updateSession({
1151
+ ...record,
1152
+ modes: nextModes
1153
+ });
1154
+ }
1155
+ }
654
1156
  async allocateSessionEventIndex(sessionId) {
655
1157
  await this.ensureSessionEventIndexSeeded(sessionId);
656
1158
  const nextIndex = this.nextSessionEventIndexBySession.get(sessionId) ?? 1;
@@ -736,7 +1238,8 @@ var SandboxAgent = class _SandboxAgent {
736
1238
  body: options.body,
737
1239
  headers: options.headers,
738
1240
  accept: options.accept ?? "application/json",
739
- signal: options.signal
1241
+ signal: options.signal,
1242
+ skipReadyWait: options.skipReadyWait
740
1243
  });
741
1244
  if (response.status === 204) {
742
1245
  return void 0;
@@ -744,6 +1247,9 @@ var SandboxAgent = class _SandboxAgent {
744
1247
  return await response.json();
745
1248
  }
746
1249
  async requestRaw(method, path, options = {}) {
1250
+ if (!options.skipReadyWait) {
1251
+ await this.awaitHealthy(options.signal);
1252
+ }
747
1253
  const url = this.buildUrl(path, options.query);
748
1254
  const headers = this.buildHeaders(options.headers);
749
1255
  if (options.accept) {
@@ -773,6 +1279,64 @@ var SandboxAgent = class _SandboxAgent {
773
1279
  }
774
1280
  return response;
775
1281
  }
1282
+ startHealthWait() {
1283
+ if (!this.healthWait.enabled || this.healthPromise) {
1284
+ return;
1285
+ }
1286
+ this.healthPromise = this.runHealthWait().catch((error) => {
1287
+ this.healthError = error instanceof Error ? error : new Error(String(error));
1288
+ });
1289
+ }
1290
+ async awaitHealthy(signal) {
1291
+ if (!this.healthPromise) {
1292
+ throwIfAborted(signal);
1293
+ return;
1294
+ }
1295
+ await waitForAbortable(this.healthPromise, signal);
1296
+ throwIfAborted(signal);
1297
+ if (this.healthError) {
1298
+ throw this.healthError;
1299
+ }
1300
+ }
1301
+ async runHealthWait() {
1302
+ const signal = this.healthWait.enabled ? anyAbortSignal([this.healthWait.signal, this.healthWaitAbortController.signal]) : void 0;
1303
+ const startedAt = Date.now();
1304
+ const deadline = typeof this.healthWait.timeoutMs === "number" ? startedAt + this.healthWait.timeoutMs : void 0;
1305
+ let delayMs = HEALTH_WAIT_MIN_DELAY_MS;
1306
+ let nextLogAt = startedAt + HEALTH_WAIT_LOG_AFTER_MS;
1307
+ let lastError;
1308
+ while (!this.disposed && (deadline === void 0 || Date.now() < deadline)) {
1309
+ throwIfAborted(signal);
1310
+ try {
1311
+ const health = await this.requestHealth({ signal });
1312
+ if (health.status === "ok") {
1313
+ return;
1314
+ }
1315
+ lastError = new Error(`Unexpected health response: ${JSON.stringify(health)}`);
1316
+ } catch (error) {
1317
+ if (isAbortError(error)) {
1318
+ throw error;
1319
+ }
1320
+ lastError = error;
1321
+ }
1322
+ const now = Date.now();
1323
+ if (now >= nextLogAt) {
1324
+ const details = formatHealthWaitError(lastError);
1325
+ console.warn(
1326
+ `sandbox-agent at ${this.baseUrl} is not healthy after ${now - startedAt}ms; still waiting (${details})`
1327
+ );
1328
+ nextLogAt = now + HEALTH_WAIT_LOG_EVERY_MS;
1329
+ }
1330
+ await sleep(delayMs, signal);
1331
+ delayMs = Math.min(HEALTH_WAIT_MAX_DELAY_MS, delayMs * 2);
1332
+ }
1333
+ if (this.disposed) {
1334
+ return;
1335
+ }
1336
+ throw new Error(
1337
+ `Timed out waiting for sandbox-agent health after ${this.healthWait.timeoutMs}ms (${formatHealthWaitError(lastError)})`
1338
+ );
1339
+ }
776
1340
  buildHeaders(extra) {
777
1341
  const headers = new Headers(this.defaultHeaders ?? void 0);
778
1342
  if (this.token) {
@@ -796,6 +1360,12 @@ var SandboxAgent = class _SandboxAgent {
796
1360
  }
797
1361
  return url.toString();
798
1362
  }
1363
+ async requestHealth(options = {}) {
1364
+ return this.requestJson("GET", `${API_PREFIX}/health`, {
1365
+ signal: options.signal,
1366
+ skipReadyWait: true
1367
+ });
1368
+ }
799
1369
  };
800
1370
  async function autoAuthenticate(acp, methods) {
801
1371
  const envBased = methods.find(
@@ -809,6 +1379,15 @@ async function autoAuthenticate(acp, methods) {
809
1379
  } catch {
810
1380
  }
811
1381
  }
1382
+ function toAgentQuery(options) {
1383
+ if (!options) {
1384
+ return void 0;
1385
+ }
1386
+ return {
1387
+ config: options.config,
1388
+ no_cache: options.noCache
1389
+ };
1390
+ }
812
1391
  function normalizeSessionInit(value) {
813
1392
  if (!value) {
814
1393
  return {
@@ -916,6 +1495,20 @@ function normalizePositiveInt(value, fallback) {
916
1495
  }
917
1496
  return Math.floor(value);
918
1497
  }
1498
+ function normalizeHealthWaitOptions(value, signal) {
1499
+ if (value === false) {
1500
+ return { enabled: false };
1501
+ }
1502
+ if (value === true || value === void 0) {
1503
+ return { enabled: true, signal };
1504
+ }
1505
+ const timeoutMs = typeof value.timeoutMs === "number" && Number.isFinite(value.timeoutMs) && value.timeoutMs > 0 ? Math.floor(value.timeoutMs) : void 0;
1506
+ return {
1507
+ enabled: true,
1508
+ signal,
1509
+ timeoutMs
1510
+ };
1511
+ }
919
1512
  function normalizeSpawnOptions(spawn, defaultEnabled) {
920
1513
  if (spawn === false) {
921
1514
  return { enabled: false };
@@ -939,9 +1532,282 @@ async function readProblem(response) {
939
1532
  return void 0;
940
1533
  }
941
1534
  }
1535
+ function normalizeSessionConfigOptions(value) {
1536
+ if (!Array.isArray(value)) {
1537
+ return void 0;
1538
+ }
1539
+ const normalized = value.filter(isSessionConfigOption);
1540
+ return cloneConfigOptions(normalized) ?? [];
1541
+ }
1542
+ function extractConfigOptionsFromSetResponse(response) {
1543
+ if (!isRecord(response)) {
1544
+ return void 0;
1545
+ }
1546
+ return normalizeSessionConfigOptions(response.configOptions);
1547
+ }
1548
+ function findConfigOptionByCategory(options, category) {
1549
+ return options.find((option) => option.category === category);
1550
+ }
1551
+ function findConfigOptionById(options, configId) {
1552
+ return options.find((option) => option.id === configId);
1553
+ }
1554
+ function uniqueCategories(options) {
1555
+ return [...new Set(options.map((option) => option.category).filter((value) => !!value))].sort();
1556
+ }
1557
+ function extractConfigValues(option) {
1558
+ if (!isRecord(option) || option.type !== "select" || !Array.isArray(option.options)) {
1559
+ return [];
1560
+ }
1561
+ const values = [];
1562
+ for (const entry of option.options) {
1563
+ if (isRecord(entry) && typeof entry.value === "string") {
1564
+ values.push(entry.value);
1565
+ continue;
1566
+ }
1567
+ if (isRecord(entry) && Array.isArray(entry.options)) {
1568
+ for (const nested of entry.options) {
1569
+ if (isRecord(nested) && typeof nested.value === "string") {
1570
+ values.push(nested.value);
1571
+ }
1572
+ }
1573
+ }
1574
+ }
1575
+ return [...new Set(values)];
1576
+ }
1577
+ function extractKnownModeIds(modes) {
1578
+ if (!modes || !Array.isArray(modes.availableModes)) {
1579
+ return [];
1580
+ }
1581
+ return modes.availableModes.map((mode) => typeof mode.id === "string" ? mode.id : null).filter((value) => !!value);
1582
+ }
1583
+ function applyCurrentMode(modes, currentModeId) {
1584
+ if (modes && Array.isArray(modes.availableModes)) {
1585
+ return {
1586
+ ...modes,
1587
+ currentModeId
1588
+ };
1589
+ }
1590
+ return {
1591
+ currentModeId,
1592
+ availableModes: []
1593
+ };
1594
+ }
1595
+ function applyConfigOptionValue(configOptions, configId, value) {
1596
+ const idx = configOptions.findIndex((o) => o.id === configId);
1597
+ if (idx === -1) {
1598
+ return null;
1599
+ }
1600
+ const updated = cloneConfigOptions(configOptions) ?? [];
1601
+ updated[idx] = { ...updated[idx], currentValue: value };
1602
+ return updated;
1603
+ }
1604
+ function envelopeSessionUpdate(message) {
1605
+ if (!isRecord(message) || !("params" in message) || !isRecord(message.params)) {
1606
+ return null;
1607
+ }
1608
+ if (!("update" in message.params) || !isRecord(message.params.update)) {
1609
+ return null;
1610
+ }
1611
+ return message.params.update;
1612
+ }
1613
+ function cloneConfigOptions(value) {
1614
+ if (!value) {
1615
+ return void 0;
1616
+ }
1617
+ return JSON.parse(JSON.stringify(value));
1618
+ }
1619
+ function cloneModes(value) {
1620
+ if (!value) {
1621
+ return null;
1622
+ }
1623
+ return JSON.parse(JSON.stringify(value));
1624
+ }
1625
+ function isSessionConfigOption(value) {
1626
+ return isRecord(value) && typeof value.id === "string" && typeof value.name === "string" && typeof value.type === "string";
1627
+ }
1628
+ function toTitleCase(input) {
1629
+ if (!input) {
1630
+ return "";
1631
+ }
1632
+ return input.split(/[_\s-]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
1633
+ }
1634
+ function formatHealthWaitError(error) {
1635
+ if (error instanceof Error && error.message) {
1636
+ return error.message;
1637
+ }
1638
+ if (error === void 0 || error === null) {
1639
+ return "unknown error";
1640
+ }
1641
+ return String(error);
1642
+ }
1643
+ function anyAbortSignal(signals) {
1644
+ const active = signals.filter((signal) => Boolean(signal));
1645
+ if (active.length === 0) {
1646
+ return void 0;
1647
+ }
1648
+ if (active.length === 1) {
1649
+ return active[0];
1650
+ }
1651
+ const controller = new AbortController();
1652
+ const onAbort = (event) => {
1653
+ cleanup();
1654
+ const signal = event.target;
1655
+ controller.abort(signal.reason ?? createAbortError());
1656
+ };
1657
+ const cleanup = () => {
1658
+ for (const signal of active) {
1659
+ signal.removeEventListener("abort", onAbort);
1660
+ }
1661
+ };
1662
+ for (const signal of active) {
1663
+ if (signal.aborted) {
1664
+ controller.abort(signal.reason ?? createAbortError());
1665
+ return controller.signal;
1666
+ }
1667
+ }
1668
+ for (const signal of active) {
1669
+ signal.addEventListener("abort", onAbort, { once: true });
1670
+ }
1671
+ return controller.signal;
1672
+ }
1673
+ function throwIfAborted(signal) {
1674
+ if (!signal?.aborted) {
1675
+ return;
1676
+ }
1677
+ throw signal.reason instanceof Error ? signal.reason : createAbortError(signal.reason);
1678
+ }
1679
+ async function waitForAbortable(promise, signal) {
1680
+ if (!signal) {
1681
+ return promise;
1682
+ }
1683
+ throwIfAborted(signal);
1684
+ return new Promise((resolve, reject) => {
1685
+ const onAbort = () => {
1686
+ cleanup();
1687
+ reject(signal.reason instanceof Error ? signal.reason : createAbortError(signal.reason));
1688
+ };
1689
+ const cleanup = () => {
1690
+ signal.removeEventListener("abort", onAbort);
1691
+ };
1692
+ signal.addEventListener("abort", onAbort, { once: true });
1693
+ promise.then(
1694
+ (value) => {
1695
+ cleanup();
1696
+ resolve(value);
1697
+ },
1698
+ (error) => {
1699
+ cleanup();
1700
+ reject(error);
1701
+ }
1702
+ );
1703
+ });
1704
+ }
1705
+ async function consumeProcessLogSse(body, listener, signal) {
1706
+ const reader = body.getReader();
1707
+ const decoder = new TextDecoder();
1708
+ let buffer = "";
1709
+ try {
1710
+ while (!signal.aborted) {
1711
+ const { done, value } = await reader.read();
1712
+ if (done) {
1713
+ return;
1714
+ }
1715
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
1716
+ let separatorIndex = buffer.indexOf("\n\n");
1717
+ while (separatorIndex !== -1) {
1718
+ const chunk = buffer.slice(0, separatorIndex);
1719
+ buffer = buffer.slice(separatorIndex + 2);
1720
+ const entry = parseProcessLogSseChunk(chunk);
1721
+ if (entry) {
1722
+ listener(entry);
1723
+ }
1724
+ separatorIndex = buffer.indexOf("\n\n");
1725
+ }
1726
+ }
1727
+ } catch (error) {
1728
+ if (signal.aborted || isAbortError(error)) {
1729
+ return;
1730
+ }
1731
+ throw error;
1732
+ } finally {
1733
+ reader.releaseLock();
1734
+ }
1735
+ }
1736
+ function parseProcessLogSseChunk(chunk) {
1737
+ if (!chunk.trim()) {
1738
+ return null;
1739
+ }
1740
+ let eventName = "message";
1741
+ const dataLines = [];
1742
+ for (const line of chunk.split("\n")) {
1743
+ if (!line || line.startsWith(":")) {
1744
+ continue;
1745
+ }
1746
+ if (line.startsWith("event:")) {
1747
+ eventName = line.slice(6).trim();
1748
+ continue;
1749
+ }
1750
+ if (line.startsWith("data:")) {
1751
+ dataLines.push(line.slice(5).trimStart());
1752
+ }
1753
+ }
1754
+ if (eventName !== "log") {
1755
+ return null;
1756
+ }
1757
+ const data = dataLines.join("\n");
1758
+ if (!data.trim()) {
1759
+ return null;
1760
+ }
1761
+ return JSON.parse(data);
1762
+ }
1763
+ function toWebSocketUrl(url) {
1764
+ const parsed = new URL(url);
1765
+ if (parsed.protocol === "http:") {
1766
+ parsed.protocol = "ws:";
1767
+ } else if (parsed.protocol === "https:") {
1768
+ parsed.protocol = "wss:";
1769
+ }
1770
+ return parsed.toString();
1771
+ }
1772
+ function isAbortError(error) {
1773
+ return error instanceof Error && error.name === "AbortError";
1774
+ }
1775
+ function createAbortError(reason) {
1776
+ if (reason instanceof Error) {
1777
+ return reason;
1778
+ }
1779
+ const message = typeof reason === "string" ? reason : "This operation was aborted.";
1780
+ if (typeof DOMException !== "undefined") {
1781
+ return new DOMException(message, "AbortError");
1782
+ }
1783
+ const error = new Error(message);
1784
+ error.name = "AbortError";
1785
+ return error;
1786
+ }
1787
+ function sleep(ms, signal) {
1788
+ if (!signal) {
1789
+ return new Promise((resolve) => setTimeout(resolve, ms));
1790
+ }
1791
+ throwIfAborted(signal);
1792
+ return new Promise((resolve, reject) => {
1793
+ const timer = setTimeout(() => {
1794
+ cleanup();
1795
+ resolve();
1796
+ }, ms);
1797
+ const onAbort = () => {
1798
+ cleanup();
1799
+ reject(signal.reason instanceof Error ? signal.reason : createAbortError(signal.reason));
1800
+ };
1801
+ const cleanup = () => {
1802
+ clearTimeout(timer);
1803
+ signal.removeEventListener("abort", onAbort);
1804
+ };
1805
+ signal.addEventListener("abort", onAbort, { once: true });
1806
+ });
1807
+ }
942
1808
 
943
1809
  // src/index.ts
944
- import { AcpRpcError } from "acp-http-client";
1810
+ import { AcpRpcError as AcpRpcError2 } from "acp-http-client";
945
1811
 
946
1812
  // src/inspector.ts
947
1813
  function buildInspectorUrl(options) {
@@ -957,12 +1823,15 @@ function buildInspectorUrl(options) {
957
1823
  return `${normalized}/ui/${queryString ? `?${queryString}` : ""}`;
958
1824
  }
959
1825
  export {
960
- AcpRpcError,
1826
+ AcpRpcError2 as AcpRpcError,
961
1827
  InMemorySessionPersistDriver,
962
1828
  LiveAcpConnection,
963
1829
  SandboxAgent,
964
1830
  SandboxAgentError,
965
1831
  Session,
1832
+ UnsupportedSessionCategoryError,
1833
+ UnsupportedSessionConfigOptionError,
1834
+ UnsupportedSessionValueError,
966
1835
  buildInspectorUrl
967
1836
  };
968
1837
  //# sourceMappingURL=index.js.map