@agentshouse/kit 0.1.0-alpha.6 → 0.1.0-alpha.7

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/README.md CHANGED
@@ -10,7 +10,7 @@ On Linux `amd64` or `arm64` with a working Docker Engine, run the published,
10
10
  versioned bootstrap once:
11
11
 
12
12
  ```sh
13
- bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.6/connect-linux.sh)
13
+ bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.7/connect-linux.sh)
14
14
  ```
15
15
 
16
16
  The release asset pins one immutable multi-architecture image digest. It creates
@@ -48,7 +48,8 @@ Runtime dependencies stay minimal and exactly pinned in `package.json` and
48
48
  `package-lock.json`: House's own `@agentshouse/mdmodel`, `yaml`, which reads
49
49
  House's text answers, and the Agent Client Protocol SDK
50
50
  `@agentclientprotocol/sdk` with its peer `zod`, which is this Kit's one protocol
51
- client for the compiled adapter contracts. The SDK's approval
51
+ client for the compiled adapter contracts, and `ws` for the authenticated Kit
52
+ control stream. The SDK's approval
52
53
  receipt is [`docs/agents/acp-sdk-dependency-approval.json`](docs/agents/acp-sdk-dependency-approval.json),
53
54
  which the User wrote once this adoption's proof had passed. The adapter
54
55
  packages themselves are Environment prerequisites, not Kit dependencies, and
@@ -575,6 +576,17 @@ minimum's major - under
575
576
  `$HOUSE_KIT_HOME/agents/<kind>/releases/<release>/`, never touching what the
576
577
  owner installed globally, and an acquired route's command is that installed
577
578
  executable rather than a path anyone writes into `kit.json`.
579
+ The resident Kit also listens for an `agents` availability pointer on its
580
+ control stream. It reads the whole wanted list from House over HTTPS, removes
581
+ Kit-owned CLI directories that are no longer wanted, installs newly wanted
582
+ kinds, and reports its held state immediately, including while another Agent
583
+ run remains active. A newly wanted kind that needs provider authorization uses
584
+ the existing guided House login ceremony; Kit reports readiness again when it
585
+ ends. A reconnect reads the list again, so a pointer missed during a stream
586
+ outage changes no authority.
587
+ Removal waits for an active worker pass or any unresolved live WorkerSession
588
+ binding to stop using a CLI directory; the Kit keeps reporting that kind until
589
+ it removes the directory.
578
590
 
579
591
  Every candidate release is proved before it serves: the Kit installs it beside
580
592
  the current release, runs the contract's own conformance check - the protocol's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/kit",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.7",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,6 +14,7 @@
14
14
  "dependencies": {
15
15
  "@agentclientprotocol/sdk": "1.5.0",
16
16
  "@agentshouse/mdmodel": "0.1.0-alpha.13",
17
+ "ws": "8.22.0",
17
18
  "yaml": "2.9.1",
18
19
  "zod": "4.6.5"
19
20
  },
package/src/agents.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
- import { readFile, rm } from "node:fs/promises";
2
+ import { readFile, readdir, rm } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import {
@@ -11,6 +11,7 @@ import {
11
11
  } from "./adapter.js";
12
12
  import { inheritedEnvironment } from "./command.js";
13
13
  import {
14
+ ADAPTER_KINDS,
14
15
  AGENT_HOME,
15
16
  AGENT_INSTALL_TIMEOUT_MS,
16
17
  AGENT_RELEASES_HOME,
@@ -261,6 +262,60 @@ export async function updateDeclaredAgents(options, configuration, dependencies
261
262
  return updated;
262
263
  }
263
264
 
265
+ export async function removeUndeclaredAgents(directory, wanted, keep = false) {
266
+ const home = join(directory, AGENT_HOME);
267
+ let entries;
268
+ try {
269
+ entries = await readdir(home, { withFileTypes: true });
270
+ } catch (error) {
271
+ if (error.code === "ENOENT") return [];
272
+ throw error;
273
+ }
274
+ const retained = [];
275
+ for (const entry of entries) {
276
+ if (!wanted.includes(entry.name)) {
277
+ if (keep && ADAPTER_KINDS.includes(entry.name)) retained.push(entry.name);
278
+ else await rm(join(home, entry.name), { recursive: true, force: true });
279
+ }
280
+ }
281
+ return retained;
282
+ }
283
+
284
+ export async function reconcileAgentKinds(
285
+ options,
286
+ session,
287
+ configuration,
288
+ dependencies = {},
289
+ signal = session.signal,
290
+ state = {},
291
+ ) {
292
+ const selected = { ...configuration, agents: await desiredAgents(session, signal) };
293
+ state.onDesired?.(selected.agents);
294
+ const newlyWanted = selected.agents.filter((kind) => !state.lastDesired?.includes(kind));
295
+ const retained = await removeUndeclaredAgents(options.directory, selected.agents, state.busy);
296
+ const pointerKinds = state.busy
297
+ ? (
298
+ await Promise.all(
299
+ newlyWanted.map(async (kind) =>
300
+ (await readAgentState(options.directory, kind)).release === null ? kind : null,
301
+ ),
302
+ )
303
+ ).filter((kind) => kind !== null)
304
+ : newlyWanted;
305
+ const updated = await updateDeclaredAgents(
306
+ { directory: options.directory, at: dependencies.now?.(), forced: state.forced },
307
+ { ...selected, agents: state.pointer ? pointerKinds : selected.agents },
308
+ dependencies,
309
+ );
310
+ const readiness = await agentReadiness(
311
+ { directory: options.directory },
312
+ { ...selected, agents: [...selected.agents, ...retained] },
313
+ dependencies,
314
+ );
315
+ await reportAgentCapabilities(session, readiness.agents, signal);
316
+ return { selected, updated, reported: readiness.agents, refused: readiness.refused, retained };
317
+ }
318
+
264
319
  export async function agentSignedIn(directory, kind, dependencies = {}) {
265
320
  const contract = compiledContract(kind);
266
321
  const state = await readAgentState(directory, kind);
@@ -341,15 +396,15 @@ export async function updateAgents(options, dependencies = {}) {
341
396
  return maintainControl(
342
397
  session,
343
398
  async (signal) => {
344
- const selected = { ...configuration, agents: await desiredAgents(session, signal) };
345
- const updated = await updateDeclaredAgents(
346
- { directory: options.directory, at: dependencies.now?.(), forced: true },
347
- selected,
399
+ const result = await reconcileAgentKinds(
400
+ options,
401
+ session,
402
+ configuration,
348
403
  dependencies,
404
+ signal,
405
+ { forced: true },
349
406
  );
350
- const { agents } = await agentReadiness(options, selected, dependencies);
351
- await reportAgentCapabilities(session, agents, signal);
352
- return { updated, reported: agents };
407
+ return { updated: result.updated, reported: result.reported };
353
408
  },
354
409
  options.signal,
355
410
  );
package/src/contract.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CLAUDE_PASTED_CODE, CODEX_DEVICE_CODE, GROK_DEVICE_CODE } from "./agent-logins.js";
2
2
 
3
- export const KIT_CONTRACT_VERSION = "2026-09-27-private-room-scope";
3
+ export const KIT_CONTRACT_VERSION = "2026-09-27-wanted-agent-clis";
4
4
  export const KIT_CAPTURE_BATCH_MAX = 100;
5
5
  export const COMMAND_INPUT_MAX_BYTES = 4 * 1024;
6
6
  export const AUTHORIZATION_OUTPUT_MAX_BYTES = 4 * 1024;
@@ -0,0 +1,84 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import WebSocket from "ws";
3
+ import { KIT_CONTRACT_VERSION } from "./contract.js";
4
+ import { heldControl } from "./house.js";
5
+ import { KitRefusal } from "./refusal.js";
6
+
7
+ const PROTOCOL = "house.kit.stream.1";
8
+
9
+ function streamAddress(house) {
10
+ const url = new URL("/kit/stream", house);
11
+ if (url.protocol === "https:") url.protocol = "wss:";
12
+ else if (url.protocol === "http:") url.protocol = "ws:";
13
+ else throw new KitRefusal("kit_house_unavailable", "/kit/stream");
14
+ return url;
15
+ }
16
+
17
+ async function connected(session, signal, available, dependencies) {
18
+ const authorization = await heldControl(session, signal);
19
+ return new Promise((resolve, reject) => {
20
+ const Socket = dependencies.workSocket ?? WebSocket;
21
+ const socket = new Socket(streamAddress(session.house), PROTOCOL, {
22
+ headers: { authorization, "x-house-kit-version": KIT_CONTRACT_VERSION },
23
+ handshakeTimeout: 10_000,
24
+ maxPayload: 4096,
25
+ perMessageDeflate: false,
26
+ });
27
+ let finished = false;
28
+ const finish = (error = null) => {
29
+ if (finished) return;
30
+ finished = true;
31
+ signal.removeEventListener("abort", stop);
32
+ if (error === null) resolve();
33
+ else reject(error);
34
+ };
35
+ const stop = () => {
36
+ socket.terminate();
37
+ finish();
38
+ };
39
+ signal.addEventListener("abort", stop, { once: true });
40
+ socket.on("open", available);
41
+ socket.on("message", (data) => {
42
+ let frame;
43
+ try {
44
+ frame = JSON.parse(data.toString());
45
+ } catch {
46
+ return;
47
+ }
48
+ if (frame?.type === "work_available" && frame.subject === "agents") available();
49
+ });
50
+ socket.on("unexpected-response", (_request, response) => {
51
+ response.resume();
52
+ const refusal =
53
+ response.statusCode === 409 ? new KitRefusal("kit_upgrade_required") : null;
54
+ socket.terminate();
55
+ finish(refusal);
56
+ });
57
+ socket.on("error", () => {
58
+ socket.terminate();
59
+ finish();
60
+ });
61
+ socket.on("close", () => finish());
62
+ if (signal.aborted) stop();
63
+ });
64
+ }
65
+
66
+ export async function watchKitAgentWork(session, signal, available, dependencies = {}) {
67
+ let retryMs = 500;
68
+ while (!signal.aborted) {
69
+ const began = Date.now();
70
+ try {
71
+ await connected(session, signal, available, dependencies);
72
+ } catch (error) {
73
+ if (error instanceof KitRefusal && error.code === "kit_upgrade_required") throw error;
74
+ if (signal.aborted) return;
75
+ }
76
+ if (signal.aborted) return;
77
+ retryMs = Date.now() - began >= 30_000 ? 1000 : Math.min(retryMs * 2, 30_000);
78
+ try {
79
+ await delay(dependencies.workReconnectMs ?? retryMs, undefined, { signal });
80
+ } catch (error) {
81
+ if (!signal.aborted) throw error;
82
+ }
83
+ }
84
+ }
package/src/setup.js CHANGED
@@ -114,14 +114,14 @@ async function signedIn(module) {
114
114
  }
115
115
  }
116
116
 
117
- async function setUpModule(context, module) {
117
+ async function setUpModule(context, module, install = true) {
118
118
  const summary = (status, reason = null) => ({
119
119
  category: module.category,
120
120
  name: module.name,
121
121
  status,
122
122
  reason,
123
123
  });
124
- if (module.install !== null) {
124
+ if (install && module.install !== null) {
125
125
  try {
126
126
  await module.install();
127
127
  } catch (error) {
@@ -144,10 +144,15 @@ async function setUpModule(context, module) {
144
144
  return summary("ready");
145
145
  }
146
146
 
147
+ export function signInWantedAgent(context, kind) {
148
+ return setUpModule(context, agentModule(context, kind), false);
149
+ }
150
+
147
151
  export async function runSetup(context, request) {
148
152
  await prepareLaunchBase(launchBase(context.dependencies));
149
153
  const modules = [];
150
154
  for (const kind of context.configuration.agents) {
155
+ await context.waitAgentLogin?.(kind);
151
156
  modules.push(await setUpModule(context, agentModule(context, kind)));
152
157
  }
153
158
  for (const instance of context.configuration.instances) {
package/src/worker.js CHANGED
@@ -2,7 +2,11 @@ import { randomBytes } from "node:crypto";
2
2
  import { join } from "node:path";
3
3
  import { setTimeout as delay } from "node:timers/promises";
4
4
  import { fileURLToPath } from "node:url";
5
- import { agentReadiness, installedAgentCommand, updateDeclaredAgents } from "./agents.js";
5
+ import {
6
+ installedAgentCommand,
7
+ readAgentState,
8
+ reconcileAgentKinds,
9
+ } from "./agents.js";
6
10
  import { readBindings, removeBinding, writeBinding } from "./binding.js";
7
11
  import { readContinuation, readContinuationRuns, writeContinuation, removeContinuation } from "./continuation.js";
8
12
  import { openBridge } from "./bridge.js";
@@ -12,6 +16,7 @@ import { openAssistantDrafts } from "./draft.js";
12
16
  import { localReply, localRequest, reconcileInbound } from "./inbound.js";
13
17
  import { localFiles } from "./local-files.js";
14
18
  import { inheritedEnvironment } from "./command.js";
19
+ import { watchKitAgentWork } from "./kit-work-stream.js";
15
20
  import { readConfiguration } from "./configuration.js";
16
21
  import { launchBase, readyLaunchDirectory } from "./directory.js";
17
22
  import {
@@ -31,7 +36,6 @@ import {
31
36
  acceptAssistantText,
32
37
  acquireCommands,
33
38
  acquireSetup,
34
- reportAgentCapabilities,
35
39
  acquireOneRun,
36
40
  acquireOneResumption,
37
41
  admitInteraction,
@@ -40,7 +44,6 @@ import {
40
44
  rejectResumption,
41
45
  callHouseMcp,
42
46
  commitRun,
43
- desiredAgents,
44
47
  maintainControl,
45
48
  mutatingCall,
46
49
  observeSessionStart,
@@ -58,7 +61,7 @@ import {
58
61
  streamAssistantDraft,
59
62
  } from "./house.js";
60
63
  import { operationId } from "./operation.js";
61
- import { runSetup } from "./setup.js";
64
+ import { runSetup, signInWantedAgent } from "./setup.js";
62
65
  import { readSettlements, removeSettlement, writeSettlement } from "./settlement.js";
63
66
  import { failedReport, terminalReport } from "./terminal.js";
64
67
  import {
@@ -666,26 +669,33 @@ async function afterEpochReport(context, pass) {
666
669
  return pass();
667
670
  }
668
671
 
669
- async function workerPass(context, dependencies) {
670
- const { configuration, session, signal } = context;
672
+ async function workerPass(context, dependencies, baseConfiguration) {
673
+ const { session, signal } = context;
671
674
  const reconciled = await reconcile(context);
672
675
  if (reconciled.run !== null || reconciled.unresolved.length > 0) return reconciled;
676
+ context.busy = false;
677
+ const needsLogin = await queueAgentRefresh(context, dependencies, signal, baseConfiguration);
678
+ context.startLogins?.(needsLogin);
679
+ context.busy = true;
680
+ const { configuration } = context;
673
681
  context.inbound = await reconcileInbound(context.directory, session);
674
682
  const setup = await acquireSetup(session, signal);
675
683
  if (setup !== null) {
676
684
  context.setup = await runSetup(
677
- { session, configuration, directory: context.directory, dependencies, signal },
685
+ {
686
+ session,
687
+ configuration,
688
+ directory: context.directory,
689
+ dependencies,
690
+ signal,
691
+ waitAgentLogin: context.waitAgentLogin,
692
+ },
678
693
  setup.request,
679
694
  );
695
+ await context.recordSetupLogins?.(context.setup);
696
+ const needsLogin = await queueAgentRefresh(context, dependencies, signal, undefined, true);
697
+ context.startLogins?.(needsLogin);
680
698
  }
681
- context.agents = await updateDeclaredAgents(
682
- { directory: context.directory, at: dependencies.now?.() },
683
- configuration,
684
- dependencies,
685
- );
686
- const readiness = await agentReadiness({ directory: context.directory }, configuration, dependencies);
687
- await reportAgentCapabilities(session, readiness.agents);
688
- if (readiness.refused !== null) throw readiness.refused;
689
699
  const resumption = await acquireOneResumption(session);
690
700
  if (resumption !== null) return { ...reconciled, run: await superviseResumption(context, resumption) };
691
701
  const assignment = await acquireOneRun(session);
@@ -709,15 +719,59 @@ async function workerContext(options, dependencies) {
709
719
  directory: options.directory,
710
720
  files: { originals: [], failures: [] },
711
721
  epochReport: null,
722
+ agentRefresh: Promise.resolve(),
723
+ configurationPath: options.configuration,
724
+ busy: false,
725
+ deferredRemoval: false,
712
726
  };
713
727
  return { context, tmux };
714
728
  }
715
729
 
730
+ async function refreshAgentCapabilities(context, dependencies, signal, configuration, pointer = false) {
731
+ const base = configuration ?? (await readConfiguration(context.configurationPath));
732
+ const busy = context.busy || (await readBindings(context.directory)).length > 0;
733
+ const result = await reconcileAgentKinds(
734
+ { directory: context.directory },
735
+ context.session,
736
+ base,
737
+ dependencies,
738
+ signal,
739
+ {
740
+ pointer,
741
+ busy,
742
+ lastDesired: context.lastDesired,
743
+ onDesired: (wanted) => context.cancelLogins?.(wanted),
744
+ },
745
+ );
746
+ context.deferredRemoval = result.retained.length > 0;
747
+ context.configuration = result.selected;
748
+ context.agents = result.updated;
749
+ context.lastDesired = [...result.selected.agents];
750
+ if (result.refused !== null) throw result.refused;
751
+ return result.reported.filter(
752
+ (agent) =>
753
+ result.selected.agents.includes(agent.kind) && agent.release !== null && !agent.signed_in,
754
+ );
755
+ }
756
+
757
+ function queueAgentRefresh(context, dependencies, signal, configuration, pointer = false) {
758
+ const refreshed = context.agentRefresh.then(() =>
759
+ refreshAgentCapabilities(context, dependencies, signal, configuration, pointer),
760
+ );
761
+ context.agentRefresh = refreshed.catch(() => {});
762
+ return refreshed;
763
+ }
764
+
716
765
  async function controlledPass(context, dependencies, configuration, signal) {
717
- context.configuration = { ...configuration, agents: await desiredAgents(context.session, signal) };
718
766
  context.signal = signal;
719
767
  context.session.signal = signal;
720
- return afterEpochReport(context, () => workerPass(context, dependencies));
768
+ context.busy = true;
769
+ try {
770
+ return await afterEpochReport(context, () => workerPass(context, dependencies, configuration));
771
+ } finally {
772
+ context.busy = false;
773
+ if (context.deferredRemoval) context.onIdle?.();
774
+ }
721
775
  }
722
776
 
723
777
  export async function runWorkers(options, dependencies = {}) {
@@ -740,17 +794,132 @@ export async function runWorkers(options, dependencies = {}) {
740
794
  };
741
795
  }
742
796
 
743
- export async function runResident(options, dependencies = {}) {
744
- const { context } = await workerContext(options, dependencies);
745
- await maintainControl(context.session, async (signal) => {
746
- while (!signal.aborted) {
747
- const configuration = await readConfiguration(options.configuration);
748
- await controlledPass(context, dependencies, configuration, signal);
797
+ function residentWake() {
798
+ let pending = false;
799
+ let release = null;
800
+ return {
801
+ notify() {
802
+ pending = true;
803
+ release?.();
804
+ },
805
+ async wait(milliseconds, signal) {
806
+ if (pending) {
807
+ pending = false;
808
+ return;
809
+ }
810
+ const pause = new AbortController();
749
811
  try {
750
- await delay(dependencies.residentIntervalMs ?? 30_000, undefined, { signal });
812
+ await Promise.race([
813
+ new Promise((resolve) => {
814
+ release = resolve;
815
+ }),
816
+ delay(milliseconds, undefined, { signal: AbortSignal.any([signal, pause.signal]) }),
817
+ ]);
751
818
  } catch (error) {
752
819
  if (!signal.aborted) throw error;
820
+ } finally {
821
+ release = null;
822
+ pending = false;
823
+ pause.abort();
824
+ }
825
+ },
826
+ };
827
+ }
828
+
829
+ export async function runResident(options, dependencies = {}) {
830
+ const { context } = await workerContext(options, dependencies);
831
+ await maintainControl(context.session, async (signal) => {
832
+ context.busy = true;
833
+ const wake = residentWake();
834
+ const watcher = new AbortController();
835
+ let failure = null;
836
+ const logins = new Map();
837
+ const attemptedLogins = new Map();
838
+ context.cancelLogins = (wanted) => {
839
+ for (const [kind, running] of logins) {
840
+ if (!wanted.includes(kind)) running.controller.abort();
841
+ }
842
+ for (const kind of attemptedLogins.keys()) {
843
+ if (!wanted.includes(kind)) attemptedLogins.delete(kind);
753
844
  }
845
+ };
846
+ context.recordSetupLogins = async (modules) => {
847
+ for (const module of modules) {
848
+ if (module.category !== "agent" || module.status !== "needs_attention") continue;
849
+ const release = (await readAgentState(context.directory, module.name)).release;
850
+ if (release !== null) attemptedLogins.set(module.name, release);
851
+ }
852
+ };
853
+ context.startLogins = (agents) => {
854
+ for (const agent of agents) {
855
+ const { kind, release } = agent;
856
+ if (logins.has(kind) || attemptedLogins.get(kind) === release) continue;
857
+ const controller = new AbortController();
858
+ const loginSignal = AbortSignal.any([signal, controller.signal]);
859
+ const task = (async () => {
860
+ if ((await acquireSetup(context.session, loginSignal)) !== null) return;
861
+ attemptedLogins.set(kind, release);
862
+ await signInWantedAgent({ ...context, dependencies, signal: loginSignal }, kind);
863
+ await queueAgentRefresh(context, dependencies, signal, undefined, true);
864
+ })()
865
+ .catch((error) => {
866
+ if (loginSignal.aborted) return;
867
+ failure = error;
868
+ wake.notify();
869
+ })
870
+ .finally(() => logins.delete(kind));
871
+ logins.set(kind, { controller, task });
872
+ }
873
+ };
874
+ context.waitAgentLogin = async (kind) => {
875
+ await logins.get(kind)?.task;
876
+ };
877
+ let pointerPending = false;
878
+ let pointerTask = null;
879
+ const pointer = () => {
880
+ pointerPending = true;
881
+ if (pointerTask !== null) return;
882
+ pointerTask = (async () => {
883
+ while (pointerPending && !signal.aborted) {
884
+ pointerPending = false;
885
+ const needsLogin = await queueAgentRefresh(context, dependencies, signal, undefined, true);
886
+ context.startLogins(needsLogin);
887
+ }
888
+ })()
889
+ .catch((error) => {
890
+ if (signal.aborted) return;
891
+ failure = error;
892
+ wake.notify();
893
+ })
894
+ .finally(() => {
895
+ pointerTask = null;
896
+ if (pointerPending && !signal.aborted) pointer();
897
+ });
898
+ };
899
+ context.onIdle = pointer;
900
+ const watching = (dependencies.watchKitAgentWork ?? watchKitAgentWork)(
901
+ context.session,
902
+ AbortSignal.any([signal, watcher.signal]),
903
+ pointer,
904
+ dependencies,
905
+ ).catch((error) => {
906
+ failure = error;
907
+ wake.notify();
908
+ });
909
+ try {
910
+ while (!signal.aborted) {
911
+ if (failure !== null) throw failure;
912
+ const configuration = await readConfiguration(options.configuration);
913
+ await controlledPass(context, dependencies, configuration, signal);
914
+ await wake.wait(dependencies.residentIntervalMs ?? 30_000, signal);
915
+ }
916
+ } finally {
917
+ watcher.abort();
918
+ await watching;
919
+ if (pointerTask !== null) await pointerTask;
920
+ for (const running of logins.values()) running.controller.abort();
921
+ await Promise.all([...logins.values()].map((running) => running.task));
922
+ await context.agentRefresh;
754
923
  }
755
924
  }, options.signal);
756
925
  }