@platless/axom-cli 0.5.9 → 0.6.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +18 -5
  2. package/dist/axom-patch/cli/README.md +100 -12
  3. package/dist/axom-patch/cli/account-limits/generate.py +15 -12
  4. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
  5. package/dist/axom-patch/cli/claude-account-limits.json +9 -9
  6. package/dist/axom-patch/cli/codex-account-limits.json +11 -11
  7. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  8. package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
  9. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  10. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  11. package/dist/axom-patch/cli/nightly-task.md +55 -13
  12. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  13. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  14. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
  15. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
  16. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
  17. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
  18. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  19. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  20. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  21. package/dist/axom-patch/cli/runtime.json +16 -18
  22. package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
  23. package/dist/axom-patch/cli/zcode-models.json +1 -2
  24. package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
  25. package/dist/cli.js +4 -36
  26. package/dist/commands/knowledge.js +47 -0
  27. package/dist/commands/update.js +65 -3
  28. package/dist/commands/workstation.js +73 -7
  29. package/dist/install/claude/commands.js +13 -13
  30. package/dist/mcp.js +1 -1
  31. package/dist/native/darwin-arm64/axom-quic +0 -0
  32. package/dist/workstation/acp/acpAgents.js +102 -98
  33. package/dist/workstation/acp/acpClient.js +26 -0
  34. package/dist/workstation/acp/builds/install/npm.js +99 -10
  35. package/dist/workstation/acp/initializeResponse.js +6 -1
  36. package/dist/workstation/acp/managedPatches.js +189 -25
  37. package/dist/workstation/acp/managedRuntime.js +314 -0
  38. package/dist/workstation/acp/patchAssets.js +40 -0
  39. package/dist/workstation/acp/processor/checkpoint.js +25 -34
  40. package/dist/workstation/acp/processor/control.js +5 -78
  41. package/dist/workstation/acp/processor/runtime.js +5 -81
  42. package/dist/workstation/acp/processor/session.js +51 -80
  43. package/dist/workstation/acp/processor/state.js +6 -13
  44. package/dist/workstation/acp/processor/step.js +3 -20
  45. package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
  46. package/dist/workstation/acp/processor/updates.js +131 -105
  47. package/dist/workstation/acp/processorSession.js +8 -172
  48. package/dist/workstation/acp/registry/catalog.js +65 -39
  49. package/dist/workstation/acp/registry/launch.js +16 -1
  50. package/dist/workstation/acp/switching.js +0 -3
  51. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  52. package/dist/workstation/acp/test/keeperWorld.js +28 -0
  53. package/dist/workstation/acp/test/raw/sources.js +16 -130
  54. package/dist/workstation/acp/test/realAdapter.js +1 -47
  55. package/dist/workstation/acp/test/scriptAgent.js +11 -3
  56. package/dist/workstation/agentFlow.js +1 -1
  57. package/dist/workstation/agentSessions.js +38 -143
  58. package/dist/workstation/channels/management.js +95 -11
  59. package/dist/workstation/channels/url.js +1 -1
  60. package/dist/workstation/config.js +16 -4
  61. package/dist/workstation/connection/encryptedChannel.js +4 -1
  62. package/dist/workstation/control/bindingStore.js +200 -0
  63. package/dist/workstation/control/commands.js +124 -0
  64. package/dist/workstation/control/daemonIdentity.js +53 -0
  65. package/dist/workstation/control/enrollment.js +106 -0
  66. package/dist/workstation/control/inspect.js +71 -0
  67. package/dist/workstation/control/locks.js +63 -0
  68. package/dist/workstation/control/protocol.js +40 -0
  69. package/dist/workstation/control/restartGuard.js +46 -0
  70. package/dist/workstation/control/runtimeClient.js +97 -0
  71. package/dist/workstation/control/runtimeHost.js +148 -0
  72. package/dist/workstation/control/serverIdentity.js +35 -0
  73. package/dist/workstation/daemon.js +106 -6
  74. package/dist/workstation/daemonctl.js +77 -14
  75. package/dist/workstation/desktopControl/audio.js +99 -0
  76. package/dist/workstation/desktopControl/capabilities.js +92 -0
  77. package/dist/workstation/desktopControl/captureModes.js +9 -0
  78. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  79. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  80. package/dist/workstation/desktopControl/helperProcess.js +353 -0
  81. package/dist/workstation/desktopControl/lobby.js +129 -0
  82. package/dist/workstation/desktopControl/macProvider.js +155 -0
  83. package/dist/workstation/desktopControl/media.js +331 -0
  84. package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
  85. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  86. package/dist/workstation/desktopControl/overview.js +58 -0
  87. package/dist/workstation/desktopControl/protocol.js +302 -0
  88. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  89. package/dist/workstation/desktopControl/rateController.js +226 -0
  90. package/dist/workstation/desktopControl/supervisor.js +1681 -0
  91. package/dist/workstation/desktopControl/transport.js +27 -0
  92. package/dist/workstation/desktopControl/wallpaper.js +78 -0
  93. package/dist/workstation/files/credentials.js +142 -0
  94. package/dist/workstation/files/text.js +42 -8
  95. package/dist/workstation/keepers/kit/bundle.js +21 -17
  96. package/dist/workstation/keepers/kit/launcher.js +47 -7
  97. package/dist/workstation/keepers/kit/paths.js +2 -1
  98. package/dist/workstation/managedInstall.js +57 -0
  99. package/dist/workstation/protocol/agent.js +2 -15
  100. package/dist/workstation/protocol/management.js +93 -4
  101. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  102. package/dist/workstation/rd/artifact.js +119 -0
  103. package/dist/workstation/rd/control.js +119 -0
  104. package/dist/workstation/rd/display.js +29 -0
  105. package/dist/workstation/rd/provider.js +222 -0
  106. package/dist/workstation/rd/sessions.js +391 -0
  107. package/dist/workstation/rd/state.js +45 -0
  108. package/dist/workstation/rd/update.js +171 -0
  109. package/dist/workstation/rd/upkeep.js +38 -0
  110. package/dist/workstation/register.js +27 -37
  111. package/dist/workstation/service/installer.js +104 -13
  112. package/dist/workstation/service/launchd.js +1 -0
  113. package/dist/workstation/storageMetrics.js +118 -0
  114. package/dist/workstation/systemMetrics.js +44 -92
  115. package/dist/workstation/toolchain.js +94 -0
  116. package/dist/workstation/tools/browser.js +28 -4
  117. package/dist/workstation/update/artifact.js +2 -1
  118. package/dist/workstation/update/globalInstall.js +116 -0
  119. package/dist/workstation/update/npmInstaller.js +49 -23
  120. package/dist/workstation/update/rdRollback.js +145 -0
  121. package/dist/workstation/update/runUpdate.js +3 -2
  122. package/dist/workstation/update/watchdog.js +16790 -334
  123. package/package.json +5 -5
  124. package/skills/axom/SKILL.md +11 -11
  125. package/skills/axom-definition/SKILL.md +3 -3
  126. package/skills/axom-platform/SKILL.md +3 -3
  127. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  128. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
  129. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  130. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  131. package/dist/commands/guide.js +0 -38
  132. package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
  133. package/dist/runtime-deps/qoder-sdk/package.json +0 -8
  134. package/dist/workstation/acp/delegationMcp.js +0 -24
  135. package/dist/workstation/acp/processor/sideChannel.js +0 -80
  136. package/dist/workstation/acp/qoderStartup.js +0 -45
  137. package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
  138. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
  139. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
  140. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
  141. package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
  142. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  143. package/dist/workstation/qoderAdapter/contract.js +0 -78
  144. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
  145. package/dist/workstation/qoderAdapter/history.js +0 -85
  146. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  147. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  148. package/dist/workstation/qoderAdapter/main.js +0 -95
  149. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  150. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  151. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  152. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  153. package/dist/workstation/qoderAdapter/profile.js +0 -93
  154. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
  155. package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
  156. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
  157. package/dist/workstation/qoderAdapter/session.js +0 -319
  158. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  159. package/dist/workstation/qoderAdapter/subwork.js +0 -85
  160. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  161. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  162. package/dist/workstation/qoderAdapter/usage.js +0 -37
  163. /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { homedir } from 'node:os';
1
+ import { assertRuntimeRequest, loadRuntimeRelease, managedRuntimeFor, verifyManagedRuntime } from './managedRuntime.js';
2
2
  /**
3
3
  * The daemon's single entry point into ACP — what `agentSessions.ts` holds where it used to hold a
4
4
  * `Map<string, AgentBridge>`.
@@ -32,7 +32,6 @@ import { homedir } from 'node:os';
32
32
  * reaching the network or running `npx`.
33
33
  */
34
34
  import { RequestError } from '@agentclientprotocol/sdk';
35
- import { certifiedRuntime } from '../qoderAdapter/foregroundOutput.js';
36
35
  import { existsSync } from 'node:fs';
37
36
  import { readdir } from 'node:fs/promises';
38
37
  import path from 'node:path';
@@ -45,7 +44,7 @@ import { deleteNativeSessions, listNativeSessions } from './adoption.js';
45
44
  import { AcpAuth } from './auth.js';
46
45
  import { binaryTargetFor, installBinary, platformTriple } from './binaryInstall.js';
47
46
  import { binaryBuild } from './builds/install/binary.js';
48
- import { defaultNpmCommand, ensurePristine, npmBuild } from './builds/install/npm.js';
47
+ import { defaultNpmCommand, ensurePristine, installedDependencies, npmBuild } from './builds/install/npm.js';
49
48
  import { installUvBuild } from './builds/install/uv.js';
50
49
  import { adoptNpxCopy, findNpxCopy, legacyBinaryVersions, moveLegacyBinary, RESERVED_ROOT_NAMES } from './builds/migrate.js';
51
50
  import { AdapterNotInstalledError, AdapterStillInstallingError, binaryBuildDir, npmPristineDir, inside } from './builds/model.js';
@@ -57,14 +56,12 @@ import { BuildStore } from './builds/store.js';
57
56
  import { selectPackagePatches, patchDigest } from './managedPatches.js';
58
57
  import { nativeExtensionFor, nativeAccountLimitsVersionFor } from './patchAssets.js';
59
58
  import { ACCOUNT_LIMITS_READ_TIMEOUT_MS } from '../protocol/accountLimits.js';
60
- import { buildSessionMcpServers } from './delegationMcp.js';
61
59
  import { overlayEnvSecrets } from './envSecrets.js';
62
- import { AdoptionRefusedError, ProcessorSession } from './processorSession.js';
60
+ import { AdoptionRefusedError, ProcessorSession, readOutboxTail } from './processorSession.js';
63
61
  import { hostsRunningAdapter } from './processor/transport/generations.js';
64
62
  import { isOutboxTail } from './outbox.js';
65
63
  import { LocalTransport } from './processor/transport/local.js';
66
64
  import { AcpKeeperUnavailableError } from '../keepers/acp/client.js';
67
- import { prepareQoderStartup, QODER_MANAGED_VERSION } from './qoderStartup.js';
68
65
  import { friendlyOrdinaryInstallError, friendlyPinnedVersionUnavailableAndFallbackFailed, friendlyUvMissingError, logInstallFailureDetail, } from './installErrors.js';
69
66
  import { ADAPTER_INSTALL_TIMEOUT_MS, npmCacheDir, npmPackageName, npmPackageVersion, pickInstalledVersion, findOnPath, probeEnvironment, runDependencyInstall, } from './installProbe.js';
70
67
  import { createDeferredPermissionSink, registerPermissionHandlers } from './permission.js';
@@ -498,11 +495,14 @@ export class AcpAgents {
498
495
  quotaReaders = new Map();
499
496
  /** The `uv` uv builds are installed with; see {@link AcpAgentsOptions.runUv}. */
500
497
  runUv;
498
+ serverOrigin;
501
499
  /** Injectable `npm` for adapter builds; see {@link AcpAgentsOptions.npmCommand}. */
502
500
  npmCommand;
503
501
  nodePath;
504
502
  binaryFetch;
505
503
  patchRecipes;
504
+ managedRuntimePolicies;
505
+ patchAssetRoot;
506
506
  /** `builds.json` and the per-agent install/switch/GC lock (spec §6.1). */
507
507
  builds;
508
508
  /** The registry with catalog supplements but without overrides applied — {@link effectiveEntries} applies them, keeping launchEnv apart. */
@@ -575,9 +575,12 @@ export class AcpAgents {
575
575
  this.runCommand = options.runCommand ?? runDependencyInstall;
576
576
  this.runUv = options.runUv ?? defaultRunUv;
577
577
  this.nodePath = options.nodePath ?? process.execPath;
578
+ this.serverOrigin = options.serverOrigin;
578
579
  this.npmCommand = options.npmCommand ?? defaultNpmCommand(this.nodePath, this.platform);
579
580
  this.binaryFetch = options.binaryFetch;
580
581
  this.patchRecipes = options.patchRecipes;
582
+ this.managedRuntimePolicies = options.managedRuntimePolicies;
583
+ this.patchAssetRoot = options.patchAssetRoot;
581
584
  this.commandRetryFirstBroadcastMs = options.commandRetryFirstBroadcastMs ?? COMMAND_RETRY_FIRST_BROADCAST_MS;
582
585
  this.openInstallBudgetMs = options.openInstallBudgetMs ?? OPEN_INSTALL_BUDGET_MS;
583
586
  this.adapterRoot = path.join(options.cacheDir, 'acp-adapters');
@@ -956,7 +959,7 @@ export class AcpAgents {
956
959
  spec.onInstallOutcome?.(outcome);
957
960
  }));
958
961
  }
959
- let resolved = await phase('prepare-launch', () => this.resolveLaunchFor(spec.agentId, spec.command, spec.args));
962
+ let resolved = await phase('prepare-launch', () => this.resolveLaunchFor(spec.agentId, spec.command, spec.args, spec.recordedBuildId));
960
963
  // Spec §5.5.6 (Task 24): a session the adoption could not continue is switched by this open — its process is closed and a new
961
964
  // one loads the native session, never falling back to `session/new`.
962
965
  if (this.transports.keeper && spec.sessionRef !== undefined && spec.nativeSessionId) {
@@ -966,20 +969,9 @@ export class AcpAgents {
966
969
  }
967
970
  // GC never takes the build between resolving it and the spawn (whose keeper-acp label references it from then on).
968
971
  const releaseLease = this.leaseBuild(spec.agentId, resolved.launch.label?.buildId);
969
- const startup = spec.agentId === 'qoder' && spec.delegationMcp
970
- ? await phase('prepare-qoder', () => prepareQoderStartup(resolved.launch, spec.delegationMcp, spec.cwd, spec.env, spec.agentEnv))
971
- : undefined;
972
- if (startup)
973
- resolved = { launch: startup.launch, source: resolved.source };
974
972
  let session;
975
973
  try {
976
- session = await phase('initialize', () => this.startProcessorSession(resolved, spec, startup, installed));
977
- }
978
- catch (error) {
979
- await startup?.dispose();
980
- if (startup)
981
- throw new Error('Managed Qoder startup could not initialize');
982
- throw error;
974
+ session = await phase('initialize', () => this.startProcessorSession(resolved, spec, installed));
983
975
  }
984
976
  finally {
985
977
  releaseLease();
@@ -987,14 +979,8 @@ export class AcpAgents {
987
979
  const facts = session.initializeFacts;
988
980
  this.observe(spec.agentId, observationOfFacts(facts));
989
981
  try {
990
- if (startup && (session.agentInfo?.name !== 'qoder-cli' || session.agentInfo.version !== QODER_MANAGED_VERSION)) {
991
- throw new Error('Managed Qoder runtime identity is not certified');
992
- }
993
- // Qoder 1.1.61 accepts HTTP MCP at startup, but rejects that entry in ACP new/load.
994
- const sessionSpec = { ...spec, managedHistoryResumeRequired: !!spec.delegationMcp || spec.managedHistoryResumeRequired,
995
- ...(startup ? { delegationMcp: undefined, requireSessionLoad: spec.requireSessionLoad || !!spec.nativeSessionId } : {}) };
996
982
  const operation = spec.nativeSessionId && facts.capabilities.loadSession ? 'session/load' : 'session/new';
997
- const started = await phase(operation, () => withDeadline(this.startSession(session, facts, sessionSpec), this.sessionStartTimeoutMs, `Agent '${spec.agentId}' ${operation}`, AgentSessionStartTimeoutError));
983
+ const started = await phase(operation, () => withDeadline(this.startSession(session, facts, spec), this.sessionStartTimeoutMs, `Agent '${spec.agentId}' ${operation}`, AgentSessionStartTimeoutError));
998
984
  // Only here, never before: the agent accepted a session, which is the one piece of evidence
999
985
  // ACP offers that it is not waiting on credentials. Recording it up at `initialize` time
1000
986
  // would claim authentication for an agent about to reject `session/new` with auth_required.
@@ -1002,32 +988,31 @@ export class AcpAgents {
1002
988
  return { session, nativeSessionId: started.nativeSessionId };
1003
989
  }
1004
990
  catch (error) {
1005
- await session.close('auto').catch(() => undefined);
1006
- await startup?.dispose();
991
+ // A recorded-build reopen is the only path allowed to retain an empty keeper record after
992
+ // bootstrap failure: the old native session still belongs to that package, and releasing an
993
+ // acknowledged transport would make the next retry silently select the mutable current build.
994
+ await session.close(spec.recordedBuildId !== undefined ? 'keep' : 'auto').catch(() => undefined);
1007
995
  // Marked, not merely rethrown: everything above this `try` failed before the agent said
1008
996
  // anything, and only a failure from here on can be an `auth_required`. See
1009
997
  // {@link AgentRefusedSessionError} — `agentSessions.ts` is the reader.
1010
- throw new AgentRefusedSessionError(error, startup ? 'Managed Qoder session could not open' : undefined);
998
+ throw new AgentRefusedSessionError(error, undefined);
1011
999
  }
1012
1000
  }
1013
1001
  /**
1014
1002
  * Spawn the adapter behind a {@link ProcessorSession} and wait for its `initialize` (spec §5.3: keeper-acp, or the local
1015
1003
  * fallback when keeper-acp cannot be reached — §5.7). The spawn spec carries the **complete** environment (keeper-acp never
1016
- * reads its own), the daemon's working directory (where `connectAgent` started adapters), a managed startup's secrets for
1017
- * stderr redaction and its temporary files to remove on exit; the label is opaque to the keeper.
1004
+ * reads its own), the daemon's working directory (where `connectAgent` started adapters); the label is opaque to the keeper.
1018
1005
  */
1019
- async startProcessorSession(resolved, spec, startup, installOutcome) {
1020
- const { spawn, label: baseLabel, launch } = this.spawnSpecOf(resolved, spec, startup);
1006
+ async startProcessorSession(resolved, spec, installOutcome) {
1007
+ const { spawn, label: baseLabel, launch } = this.spawnSpecOf(resolved, spec);
1021
1008
  const ref = spec.sessionRef;
1022
1009
  const sessionKey = ref ?? `open-${randomUUID()}`;
1023
- // A ref whose earlier life ran in the daemon (the local fallback, or a migrated legacy journal) and left frames the server has
1024
- // not applied continues there for this life, so they are resent from where they are; a drained one is released, its watermark
1025
- // kept as the floor. Either way the processor numbers after everything that ref ever held (spec §5.5).
1026
1010
  const resumeFrom = spec.seqBase?.() ?? 0;
1027
1011
  const local = await this.localLeftover(sessionKey);
1028
1012
  let seqBase = Math.max(resumeFrom, local?.lastSeq ?? 0);
1029
- const stayLocal = local !== undefined && local.lastSeq > resumeFrom && local.unacknowledged;
1030
- if (local && !stayLocal)
1013
+ const pendingLocal = local !== undefined && local.lastSeq > resumeFrom && local.unacknowledged;
1014
+ const stayLocal = pendingLocal && !this.transports.keeper;
1015
+ if (local && !pendingLocal)
1031
1016
  await this.localTransport().release(sessionKey).catch(() => undefined);
1032
1017
  // Sessions an earlier daemon left in keeper-acp are discovered (and adopted) before keeper-acp spawns anything for this one.
1033
1018
  if (this.transports.keeper)
@@ -1056,7 +1041,6 @@ export class AcpAgents {
1056
1041
  spawn, seqBase, backfill: spec.backfill === true,
1057
1042
  ...(installOutcome ? { installOutcome } : {}),
1058
1043
  launch,
1059
- ...(startup ? { onClosed: () => startup.dispose() } : {}),
1060
1044
  };
1061
1045
  let session;
1062
1046
  if (this.transports.keeper && !stayLocal) {
@@ -1079,10 +1063,15 @@ export class AcpAgents {
1079
1063
  else
1080
1064
  this.heldOnOlder.delete(keeperKey); // a reopen hold on an older generation that is gone now
1081
1065
  try {
1066
+ const imports = pendingLocal ? await readOutboxTail(this.localTransport(), sessionKey, local.outbox) : undefined;
1067
+ if (imports && local && imports.length !== local.outbox.next - Math.max(local.outbox.first, local.outbox.committed + 1)) {
1068
+ throw new Error('The local session outbox could not be read completely');
1069
+ }
1082
1070
  session = await ProcessorSession.start({ ...options, sessionKey: keeperKey, transport: target.transport,
1071
+ ...(imports ? { imports } : {}),
1083
1072
  label: { ...baseLabel, ...(keyGen > 0 ? { keyGen } : {}) },
1084
1073
  onDegrade: this.recordDegraded(keeperKey, target.gen ?? 'current'),
1085
- ...(target.gen !== undefined ? { onClosed: () => this.olderSessionClosed(keeperKey, options.onClosed) } : {}) });
1074
+ ...(target.gen !== undefined ? { onClosed: () => this.olderSessionClosed(keeperKey) } : {}) });
1086
1075
  }
1087
1076
  catch (error) {
1088
1077
  this.heldOnOlder.delete(keeperKey);
@@ -1113,22 +1102,26 @@ export class AcpAgents {
1113
1102
  await session.initialized(this.initializeTimeoutMs);
1114
1103
  }
1115
1104
  catch (error) {
1116
- await session.close('auto').catch(() => undefined);
1105
+ await session.close(spec.recordedBuildId !== undefined ? 'keep' : 'auto').catch(() => undefined);
1117
1106
  throw error;
1118
1107
  }
1108
+ if (pendingLocal && session.transportMode === 'keeper') {
1109
+ await this.localTransport().release(sessionKey).catch((error) => {
1110
+ console.warn(`[acp] releasing the migrated local outbox for ${sessionKey} failed: ${errorMessage(error)}`);
1111
+ });
1112
+ }
1119
1113
  return session;
1120
1114
  }
1121
1115
  /**
1122
1116
  * The spawn spec, label and launch diagnostics of a launch (spec §5.3): the **complete** environment (keeper-acp never reads its
1123
- * own), the daemon's working directory (where `connectAgent` started adapters), a managed startup's secrets for stderr redaction and
1124
- * its temporary files to remove on exit; the label is opaque to the keeper.
1117
+ * own), the daemon's working directory (where `connectAgent` started adapters), the label is opaque to the keeper.
1125
1118
  */
1126
- spawnSpecOf(resolved, spec, startup) {
1119
+ spawnSpecOf(resolved, spec) {
1127
1120
  const { launch } = resolved;
1128
1121
  const env = completeEnvironment(withPathPrefix({ ...process.env, ...launch.env, ...spec.env, ...spec.agentEnv }, launch.pathPrefix));
1129
1122
  const cwd = process.cwd();
1130
- // keeper-acp takes at most ACP_STDERR_SECRETS_MAX; a managed startup's own secrets come first.
1131
- const allSecrets = [...(startup?.secrets ?? []), ...overlayEnvSecrets(spec)];
1123
+ // keeper-acp takes at most ACP_STDERR_SECRETS_MAX.
1124
+ const allSecrets = [...overlayEnvSecrets(spec)];
1132
1125
  if (allSecrets.length > ACP_STDERR_SECRETS_MAX) {
1133
1126
  console.warn(`[acp] agent '${spec.agentId}': ${allSecrets.length - ACP_STDERR_SECRETS_MAX} of ${allSecrets.length} env values ` +
1134
1127
  `are not redacted from its stderr (keeper-acp takes at most ${ACP_STDERR_SECRETS_MAX})`);
@@ -1137,7 +1130,6 @@ export class AcpAgents {
1137
1130
  const spawn = {
1138
1131
  command: launch.command, args: [...launch.args], env, cwd,
1139
1132
  ...(stderrSecrets.length > 0 ? { stderrSecrets } : {}),
1140
- ...(startup ? { removeOnExit: [startup.dir] } : {}),
1141
1133
  };
1142
1134
  const label = {
1143
1135
  sessionRef: spec.sessionRef ?? null, agentId: spec.agentId, agentKey: spec.agentKey ?? null,
@@ -1450,9 +1442,6 @@ export class AcpAgents {
1450
1442
  else if (!nativeSessionId) {
1451
1443
  outcome = refuse('the session has no native session to load');
1452
1444
  }
1453
- else if (spec.delegationMcp || from.delegation) {
1454
- outcome = refuse('a managed delegation session is not moved');
1455
- }
1456
1445
  else {
1457
1446
  outcome = await this.builds.withAgentLock(spec.agentId, () => this.switchLocked(from, { ...spec, sessionRef: ref, nativeSessionId }, request))
1458
1447
  .catch((error) => refuse(`the switch failed before anything was stopped: ${errorMessage(error)}`, false));
@@ -1731,6 +1720,7 @@ export class AcpAgents {
1731
1720
  source: 'registry',
1732
1721
  launch: await launchForBuild(build, this.adapterRoot, {
1733
1722
  nodePath: this.nodePath, launchEnv: catalogLaunchEnv(agentId, build.registryVersion), basePath: this.probe.path,
1723
+ ...this.runtimeAssets(agentId), allowLegacy: true,
1734
1724
  }),
1735
1725
  };
1736
1726
  }
@@ -1892,12 +1882,11 @@ export class AcpAgents {
1892
1882
  return this.unlistedGens.has(gen) || [...this.heldOnOlder.values()].includes(gen);
1893
1883
  }
1894
1884
  /** A session of this daemon on an older generation ended: that generation may be idle now. */
1895
- olderSessionClosed(sessionKey, also) {
1885
+ olderSessionClosed(sessionKey) {
1896
1886
  // A reopen's retire keeps the hold: the respawn goes into the same generation right behind it (fix round 2).
1897
1887
  if (!this.reopenHolds.has(sessionKey))
1898
1888
  this.heldOnOlder.delete(sessionKey);
1899
1889
  void this.retireIdleGenerations();
1900
- return also?.();
1901
1890
  }
1902
1891
  /**
1903
1892
  * A keeper-hosted session degraded (keeper-acp gone, or poisoned): its outbox head is remembered, so a later open of the ref that
@@ -1979,6 +1968,7 @@ export class AcpAgents {
1979
1968
  const tail = summary.outbox.committedTail;
1980
1969
  const tailSeq = typeof tail?.seq === 'number' ? tail.seq : 0;
1981
1970
  return {
1971
+ outbox: summary.outbox,
1982
1972
  lastSeq: Math.max(summary.outbox.lastSeq ?? 0, tailSeq),
1983
1973
  unacknowledged: summary.outbox.next > Math.max(summary.outbox.first, summary.outbox.committed + 1),
1984
1974
  };
@@ -2113,60 +2103,35 @@ export class AcpAgents {
2113
2103
  async startSession(session, facts, spec) {
2114
2104
  const nativeSessionId = spec.nativeSessionId;
2115
2105
  const capabilities = facts.capabilities;
2116
- if (nativeSessionId && spec.managedHistoryResumeRequired) {
2117
- throw new Error('Managed native history replay has no certified boundary');
2118
- }
2119
- if (spec.delegationMcp && !capabilities.mcpHttp) {
2120
- throw new Error(`Agent '${spec.agentId}' does not advertise HTTP MCP support`);
2121
- }
2122
2106
  if (spec.requireSessionLoad && !nativeSessionId) {
2123
2107
  throw new Error('Refreshing settings requires an existing native session ID');
2124
2108
  }
2125
- const historyHome = spec.agentEnv.HOME ?? spec.env.HOME ?? homedir();
2126
- // The finished MCP list (the lease checked here, against the clock, before any note is written); the bearer is a secretRef
2127
- // in the note and the down record, its value only in this daemon's memory and the adapter's stdin (spec §13).
2128
- const servers = buildSessionMcpServers(spec.mcpServers, spec.delegationMcp);
2109
+ const mcpServers = [...(spec.mcpServers ?? [])];
2129
2110
  const redact = [];
2130
- const mcpServers = servers.map((server) => {
2131
- if (!('headers' in server) || !spec.delegationMcp || server.name !== 'axom-delegation')
2132
- return server;
2133
- return { ...server, headers: server.headers.map((header) => {
2134
- const ref = session.secret(header.value);
2135
- redact.push(ref);
2136
- return { ...header, value: ref };
2137
- }) };
2138
- });
2139
- const delegation = spec.delegationMcp && spec.delegation ? {
2140
- expiresAtEpochMs: spec.delegationMcp.expiresAtEpochMs, leaseId: spec.delegation.leaseId,
2141
- ...(spec.delegation.bindingId !== undefined && spec.delegation.instanceId !== undefined
2142
- ? { bindingId: spec.delegation.bindingId, instanceId: spec.delegation.instanceId } : {}),
2143
- } : undefined;
2144
- const request = { cwd: spec.cwd, mcpServers, redact, ...(delegation ? { delegation } : {}) };
2111
+ const request = { cwd: spec.cwd, mcpServers, redact };
2145
2112
  const refused = (result) => (result.code !== undefined ? new RequestError(result.code, result.error) : new Error(result.error));
2146
- // Qoder's load may emit old chunks after its response. Cached reconnects need context, not
2147
- // another history import. Actual adoption/backfill and explicit settings loads still use load.
2148
- if (nativeSessionId && certifiedRuntime(facts.agentInfo) && capabilities.resume &&
2149
- !spec.backfill && !spec.requireSessionLoad && !spec.delegationMcp) {
2113
+ // The adapter declares cached reconnect semantics in initialize; explicit history/settings loads still use load.
2114
+ if (nativeSessionId && facts.cachedReconnect === 'resume' && capabilities.resume &&
2115
+ !spec.backfill && !spec.requireSessionLoad) {
2150
2116
  const result = await session.openSession({ ...request, mode: 'resume', sessionId: nativeSessionId });
2151
2117
  if (!result.ok)
2152
2118
  throw refused(result); // Never silently replace this cached conversation with a fresh native root.
2153
- await session.enableRepair(nativeSessionId, spec.cwd, historyHome, true);
2154
2119
  return { nativeSessionId };
2155
2120
  }
2156
2121
  if (nativeSessionId) {
2157
2122
  if (!capabilities.loadSession) {
2158
- if (spec.requireSessionLoad || spec.delegationMcp)
2123
+ if (spec.requireSessionLoad)
2159
2124
  throw new Error(`Agent '${spec.agentId}' does not support session/load`);
2160
2125
  console.warn(`[acp] agent '${spec.agentId}' does not support session/load; starting a new session ` +
2161
2126
  `instead of continuing '${nativeSessionId}'`);
2162
2127
  }
2163
2128
  else {
2164
- const result = await session.openSession({ ...request, mode: 'load', sessionId: nativeSessionId, repair: { historyHome } });
2129
+ const result = await session.openSession({ ...request, mode: 'load', sessionId: nativeSessionId });
2165
2130
  if (result.ok) {
2166
2131
  await session.endHistory();
2167
2132
  return { nativeSessionId };
2168
2133
  }
2169
- if (spec.requireSessionLoad || spec.delegationMcp)
2134
+ if (spec.requireSessionLoad)
2170
2135
  throw refused(result);
2171
2136
  console.warn(`[acp] session/load for '${nativeSessionId}' on agent '${spec.agentId}' failed ` +
2172
2137
  `(${result.error}); starting a new session instead`);
@@ -2175,8 +2140,6 @@ export class AcpAgents {
2175
2140
  const result = await session.openSession({ ...request, mode: 'new' });
2176
2141
  if (!result.ok)
2177
2142
  throw refused(result);
2178
- if (result.nativeSessionId)
2179
- await session.enableRepair(result.nativeSessionId, spec.cwd, historyHome);
2180
2143
  return { nativeSessionId: result.nativeSessionId };
2181
2144
  }
2182
2145
  /**
@@ -2570,6 +2533,9 @@ export class AcpAgents {
2570
2533
  ...(outcome.pinnedVersionUnavailable ? { pinnedVersionUnavailable: outcome.pinnedVersionUnavailable } : {}),
2571
2534
  };
2572
2535
  }
2536
+ runtimeAssets(agentId) {
2537
+ return { policy: managedRuntimeFor(agentId, this.managedRuntimePolicies), assetRoot: this.patchAssetRoot, recipes: this.patchRecipes };
2538
+ }
2573
2539
  /** The registry as Axom installs it (catalog supplements, pins, launchEnv kept apart) — see `registry/catalog.ts`. */
2574
2540
  async effective() {
2575
2541
  return effectiveEntries(await this.baseRegistry.load());
@@ -2590,18 +2556,21 @@ export class AcpAgents {
2590
2556
  currentBuildId) {
2591
2557
  const agent = entry.agent;
2592
2558
  const { npx, uvx, binary } = agent.distribution;
2559
+ const policy = this.runtimeAssets(agent.id).policy;
2560
+ if (policy && (!npx || agent.version !== policy.version))
2561
+ return { ok: false, error: 'Managed runtime Catalog/distribution differs from bundled release policy' };
2593
2562
  const source = entry.pinnedBy ?? 'registry';
2594
2563
  const commit = (build) => this.builds.commit(build, { makeCurrent: build.buildId !== currentBuildId, setBy });
2595
2564
  if (npx) {
2596
- const pristine = await this.installPristineWithFallback(agent, npx.package, timeoutMs);
2565
+ const pristine = await this.installPristineWithFallback(agent, timeoutMs);
2597
2566
  if (!pristine.ok)
2598
2567
  return pristine;
2599
2568
  try {
2600
2569
  const build = await npmBuild({
2601
- store: this.builds, agentId: agent.id, name: npmPackageName(npx.package), version: pristine.version,
2570
+ store: this.builds, agentId: agent.id, name: npx.tarball ? npx.package : npmPackageName(npx.package), version: pristine.version,
2602
2571
  args: npx.args ?? [], env: npx.env ?? {}, registryVersion: entry.registryVersion,
2603
2572
  source: pristine.pinnedVersionUnavailable ? 'unpinned-fallback' : source, installedBy: setBy,
2604
- nodePath: this.nodePath,
2573
+ nodePath: this.nodePath, ...this.runtimeAssets(agent.id),
2605
2574
  ...(this.patchRecipes ? { recipes: this.patchRecipes } : {}),
2606
2575
  });
2607
2576
  const recorded = pristine.pinnedVersionUnavailable
@@ -2693,18 +2662,24 @@ export class AcpAgents {
2693
2662
  * have), one retry unpinned, recording the version that actually landed. This is the **only** place
2694
2663
  * that fallback exists now: a launch never resolves a version (spec §6.2 "ETARGET 回退移到安装期").
2695
2664
  */
2696
- async installPristineWithFallback(agent, pinnedSpec, timeoutMs) {
2697
- const name = npmPackageName(pinnedSpec);
2698
- const version = npmPackageVersion(pinnedSpec);
2665
+ async installPristineWithFallback(agent, timeoutMs) {
2666
+ const distribution = agent.distribution.npx;
2667
+ const pinnedSpec = distribution.package;
2668
+ const name = distribution.tarball ? distribution.package : npmPackageName(pinnedSpec);
2669
+ const version = distribution.tarball ? agent.version : npmPackageVersion(pinnedSpec);
2670
+ if (distribution.tarball && !this.serverOrigin)
2671
+ return { ok: false, error: 'Adapter is served by the Axom server; this workstation has no server configured' };
2672
+ const tarball = distribution.tarball ? { url: new URL(distribution.tarball.path, this.serverOrigin).href, sha512: distribution.tarball.sha512 } : undefined;
2673
+ const runtime = this.runtimeAssets(agent.id);
2699
2674
  const budget = timeoutMs ?? ADAPTER_INSTALL_TIMEOUT_MS;
2700
2675
  const install = (pinned) => ensurePristine({
2701
- store: this.builds, name, ...(pinned !== undefined ? { version: pinned } : {}), npmCommand: this.npmCommand, timeoutMs: budget,
2676
+ store: this.builds, name, ...runtime, ...(pinned !== undefined ? { version: pinned } : {}), npmCommand: this.npmCommand, timeoutMs: budget, ...(tarball ? { tarball } : {}),
2702
2677
  }).catch((error) => ({ ok: false, error: errorMessage(error), stderr: '' }));
2703
2678
  const outcome = await install(version);
2704
2679
  if (outcome.ok)
2705
2680
  return outcome;
2706
2681
  const existingVersion = (await this.builds.current(agent.id))?.version;
2707
- if (version === undefined || !stderrShowsMissingPackageVersion(outcome.stderr)) {
2682
+ if (runtime.policy || tarball || version === undefined || !stderrShowsMissingPackageVersion(outcome.stderr)) {
2708
2683
  logInstallFailureDetail(agent.id, 'npx', pinnedSpec, outcome.stderr || outcome.error);
2709
2684
  return { ok: false, error: friendlyOrdinaryInstallError(agent.id, outcome.stderr || outcome.error, existingVersion) };
2710
2685
  }
@@ -2779,6 +2754,24 @@ export class AcpAgents {
2779
2754
  const current = await this.builds.current(agentId);
2780
2755
  if (!current)
2781
2756
  return undefined;
2757
+ const runtime = this.runtimeAssets(agentId);
2758
+ if (runtime.policy) {
2759
+ const npx = entry.agent.distribution.npx;
2760
+ if (!npx || entry.agent.version !== runtime.policy.version)
2761
+ throw new Error('Managed runtime Catalog/distribution differs from bundled policy');
2762
+ assertRuntimeRequest(runtime.policy, npx.tarball ? npx.package : npmPackageName(npx.package), npx.tarball ? entry.agent.version : npmPackageVersion(npx.package));
2763
+ const release = await loadRuntimeRelease(runtime.policy, runtime);
2764
+ if (current.runtimeReleaseId === release.id || (!current.runtimeReleaseId && current.package === runtime.policy.package && current.version === runtime.policy.version)) {
2765
+ await verifyManagedRuntime({ ...runtime, policy: runtime.policy, build: current, storeRoot: this.adapterRoot, nodePath: this.nodePath, initialize: false });
2766
+ return undefined;
2767
+ }
2768
+ const outcome = await this.installAdapter(entry, 'axom', timeoutMs, current.buildId);
2769
+ if (!outcome.ok)
2770
+ throw new Error(outcome.error);
2771
+ if (!outcome.build || outcome.build.buildId === current.buildId)
2772
+ return undefined;
2773
+ return { agentId, from: current.buildId, to: outcome.build.buildId, reason: 'certified-pin' };
2774
+ }
2782
2775
  // The pin this CLI certifies was already tried and npm did not have it: the current build is the
2783
2776
  // install-time fallback for exactly this pin. Trying again on every daemon start would only repeat
2784
2777
  // the same network round trip and land on the same build.
@@ -2796,7 +2789,7 @@ export class AcpAgents {
2796
2789
  if ((current.kind !== 'npm' && current.kind !== 'npm-patched') || !current.package)
2797
2790
  return undefined;
2798
2791
  const pristine = inside(this.adapterRoot, npmPristineDir(current.package, current.version));
2799
- const recipes = await selectPackagePatches(current.package, current.version, inside(path.join(pristine, 'node_modules'), current.package), this.patchRecipes);
2792
+ const recipes = await selectPackagePatches(current.package, current.version, inside(path.join(pristine, 'node_modules'), current.package), this.patchRecipes, await installedDependencies(pristine));
2800
2793
  const wanted = recipes.length ? patchDigest(recipes) : undefined;
2801
2794
  if (wanted === current.patchDigest)
2802
2795
  return undefined;
@@ -2890,6 +2883,8 @@ export class AcpAgents {
2890
2883
  if (!entry)
2891
2884
  return undefined;
2892
2885
  const { npx, uvx, binary } = entry.agent.distribution;
2886
+ if (this.runtimeAssets(agentId).policy && !npx)
2887
+ throw new Error('Managed runtime migration requires its certified npm distribution');
2893
2888
  if (npx)
2894
2889
  return this.migrateFromNpx(entry, npx.package, npmCache);
2895
2890
  // uv: the user's own uv tool dir is not ours to take over; the first open installs a build.
@@ -2903,6 +2898,12 @@ export class AcpAgents {
2903
2898
  const name = npmPackageName(spec);
2904
2899
  // Exactly what the pre-build launch ran: `npx <name>@<effective version>`.
2905
2900
  const version = requestedVersionOf(entry);
2901
+ const runtime = this.runtimeAssets(agentId);
2902
+ if (runtime.policy) {
2903
+ if (entry.agent.version !== runtime.policy.version)
2904
+ throw new Error('Managed runtime Catalog version mismatch');
2905
+ assertRuntimeRequest(runtime.policy, name, version);
2906
+ }
2906
2907
  const copy = await findNpxCopy(npmCache, name, version);
2907
2908
  if (!copy)
2908
2909
  return undefined;
@@ -2912,7 +2913,7 @@ export class AcpAgents {
2912
2913
  ...await npmBuild({
2913
2914
  store: this.builds, agentId, name, version, args: npx.args ?? [], env: npx.env ?? {},
2914
2915
  registryVersion: entry.registryVersion, source: entry.pinnedBy ?? 'registry', installedBy: 'axom',
2915
- nodePath: this.nodePath, ...(this.patchRecipes ? { recipes: this.patchRecipes } : {}),
2916
+ nodePath: this.nodePath, ...runtime, ...(this.patchRecipes ? { recipes: this.patchRecipes } : {}),
2916
2917
  }),
2917
2918
  migratedFrom: 'npx-cache',
2918
2919
  };
@@ -3064,6 +3065,7 @@ export class AcpAgents {
3064
3065
  return launchForBuild(current, this.adapterRoot, {
3065
3066
  nodePath: this.nodePath,
3066
3067
  launchEnv: catalogLaunchEnv(agentId, current.registryVersion),
3068
+ ...this.runtimeAssets(agentId),
3067
3069
  basePath: this.probe.path,
3068
3070
  });
3069
3071
  }
@@ -3075,9 +3077,11 @@ export class AcpAgents {
3075
3077
  throw new AdapterNotInstalledError(agentId);
3076
3078
  }
3077
3079
  /** A config-supplied `command` is the whole launch spec (ruling R3); otherwise the current build decides. */
3078
- async resolveLaunchFor(agentId, command, args) {
3080
+ async resolveLaunchFor(agentId, command, args, recordedBuildId) {
3079
3081
  if (command)
3080
3082
  return { launch: { command, args, env: {} }, source: 'command-override' };
3083
+ if (recordedBuildId !== undefined)
3084
+ return this.launchOfBuild(agentId, recordedBuildId);
3081
3085
  return { source: 'registry', launch: await this.launchSpecFor(agentId) };
3082
3086
  }
3083
3087
  /**
@@ -359,6 +359,19 @@ export async function closeAgentTransport(child, connection, graceMs = CHILD_EXI
359
359
  await connection.closed.catch(() => { });
360
360
  }
361
361
  const DEFAULT_INITIALIZE_TIMEOUT_MS = 30_000;
362
+ /**
363
+ * 登录 URL 扩展通知的方法名与形状(见 {@link connectAgent} 内的注册点注释)。解析器**不抛**:
364
+ * 形状不对返回 `undefined`,处理器对 `undefined` 不做事——坏参数只少一张卡,不该伤到连接。
365
+ */
366
+ const AUTH_URL_NOTIFICATION_METHOD = '_codebuddy.ai/authUrl';
367
+ function authUrlNotificationParams(raw) {
368
+ if (typeof raw !== 'object' || raw === null)
369
+ return undefined;
370
+ const authUrl = raw.authUrl;
371
+ if (typeof authUrl !== 'string' || !/^https?:\/\//.test(authUrl))
372
+ return undefined;
373
+ return { authUrl };
374
+ }
362
375
  /**
363
376
  * Spawns an ACP agent subprocess, connects to it over ndjson stdio, and negotiates capabilities
364
377
  * via `initialize`. `env` and `agentEnv` are overlay layers on top of the current process
@@ -393,6 +406,19 @@ export async function connectAgent(spec, env, agentEnv, options = {}) {
393
406
  // connection registers the permission handlers).
394
407
  const generation = randomUUID();
395
408
  options.configure?.(app, { generation });
409
+ // 登录 URL 的第三条通道:扩展通知。实测(codebuddy-code 2.171.0,2026-10-21):CodeBuddy 适配器在
410
+ // 应答 `authenticate` 期间推送 `_codebuddy.ai/authUrl`(`{authUrl, provider}`)——既不走
411
+ // `elicitation/create`,也不打 stderr(它把链接写进自己的日志文件、并打开工作站本机的浏览器,远程
412
+ // 读者两头都看不见)。方法名是厂商命名空间,按实测常量注册;形状校验独立于方法名做(authUrl 必须是
413
+ // http(s) URL),坏形状静默丢弃而不是让解析器抛错——一个登录链接不值得一条连接。与 stderr 通道同
414
+ // 一个 `present` 下游,URL 去重与落定收回由 `createLoginUrlSink`(acpAgents.authenticate)统一负责,
415
+ // 这里只负责递到同一只手里。id 前缀 `ext-auth-url-` 标出来源,与 `stderr-auth-url-` 同一条规矩。
416
+ if (options.present) {
417
+ app.onNotification(AUTH_URL_NOTIFICATION_METHOD, authUrlNotificationParams, (ctx) => {
418
+ if (ctx.params)
419
+ options.present?.(`ext-auth-url-${randomUUID()}`, ctx.params.authUrl);
420
+ });
421
+ }
396
422
  const connection = app.connect(stream);
397
423
  const timeoutMs = options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS;
398
424
  let timeoutHandle;