@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.
- package/README.md +18 -5
- package/dist/axom-patch/cli/README.md +100 -12
- package/dist/axom-patch/cli/account-limits/generate.py +15 -12
- package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
- package/dist/axom-patch/cli/claude-account-limits.json +9 -9
- package/dist/axom-patch/cli/codex-account-limits.json +11 -11
- package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
- package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
- package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
- package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
- package/dist/axom-patch/cli/nightly-task.md +55 -13
- package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
- package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
- package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
- package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
- package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
- package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
- package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
- package/dist/axom-patch/cli/qoder-steering.md +20 -25
- package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
- package/dist/axom-patch/cli/runtime.json +16 -18
- package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
- package/dist/axom-patch/cli/zcode-models.json +1 -2
- package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
- package/dist/cli.js +4 -36
- package/dist/commands/knowledge.js +47 -0
- package/dist/commands/update.js +65 -3
- package/dist/commands/workstation.js +73 -7
- package/dist/install/claude/commands.js +13 -13
- package/dist/mcp.js +1 -1
- package/dist/native/darwin-arm64/axom-quic +0 -0
- package/dist/workstation/acp/acpAgents.js +102 -98
- package/dist/workstation/acp/acpClient.js +26 -0
- package/dist/workstation/acp/builds/install/npm.js +99 -10
- package/dist/workstation/acp/initializeResponse.js +6 -1
- package/dist/workstation/acp/managedPatches.js +189 -25
- package/dist/workstation/acp/managedRuntime.js +314 -0
- package/dist/workstation/acp/patchAssets.js +40 -0
- package/dist/workstation/acp/processor/checkpoint.js +25 -34
- package/dist/workstation/acp/processor/control.js +5 -78
- package/dist/workstation/acp/processor/runtime.js +5 -81
- package/dist/workstation/acp/processor/session.js +51 -80
- package/dist/workstation/acp/processor/state.js +6 -13
- package/dist/workstation/acp/processor/step.js +3 -20
- package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
- package/dist/workstation/acp/processor/updates.js +131 -105
- package/dist/workstation/acp/processorSession.js +8 -172
- package/dist/workstation/acp/registry/catalog.js +65 -39
- package/dist/workstation/acp/registry/launch.js +16 -1
- package/dist/workstation/acp/switching.js +0 -3
- package/dist/workstation/acp/test/fakeAgent.js +13 -0
- package/dist/workstation/acp/test/keeperWorld.js +28 -0
- package/dist/workstation/acp/test/raw/sources.js +16 -130
- package/dist/workstation/acp/test/realAdapter.js +1 -47
- package/dist/workstation/acp/test/scriptAgent.js +11 -3
- package/dist/workstation/agentFlow.js +1 -1
- package/dist/workstation/agentSessions.js +38 -143
- package/dist/workstation/channels/management.js +95 -11
- package/dist/workstation/channels/url.js +1 -1
- package/dist/workstation/config.js +16 -4
- package/dist/workstation/connection/encryptedChannel.js +4 -1
- package/dist/workstation/control/bindingStore.js +200 -0
- package/dist/workstation/control/commands.js +124 -0
- package/dist/workstation/control/daemonIdentity.js +53 -0
- package/dist/workstation/control/enrollment.js +106 -0
- package/dist/workstation/control/inspect.js +71 -0
- package/dist/workstation/control/locks.js +63 -0
- package/dist/workstation/control/protocol.js +40 -0
- package/dist/workstation/control/restartGuard.js +46 -0
- package/dist/workstation/control/runtimeClient.js +97 -0
- package/dist/workstation/control/runtimeHost.js +148 -0
- package/dist/workstation/control/serverIdentity.js +35 -0
- package/dist/workstation/daemon.js +106 -6
- package/dist/workstation/daemonctl.js +77 -14
- package/dist/workstation/desktopControl/audio.js +99 -0
- package/dist/workstation/desktopControl/capabilities.js +92 -0
- package/dist/workstation/desktopControl/captureModes.js +9 -0
- package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
- package/dist/workstation/desktopControl/deviceSockets.js +246 -0
- package/dist/workstation/desktopControl/helperProcess.js +353 -0
- package/dist/workstation/desktopControl/lobby.js +129 -0
- package/dist/workstation/desktopControl/macProvider.js +155 -0
- package/dist/workstation/desktopControl/media.js +331 -0
- package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
- package/dist/workstation/desktopControl/networkEvidence.js +43 -0
- package/dist/workstation/desktopControl/overview.js +58 -0
- package/dist/workstation/desktopControl/protocol.js +302 -0
- package/dist/workstation/desktopControl/quicMedia.js +240 -0
- package/dist/workstation/desktopControl/rateController.js +226 -0
- package/dist/workstation/desktopControl/supervisor.js +1681 -0
- package/dist/workstation/desktopControl/transport.js +27 -0
- package/dist/workstation/desktopControl/wallpaper.js +78 -0
- package/dist/workstation/files/credentials.js +142 -0
- package/dist/workstation/files/text.js +42 -8
- package/dist/workstation/keepers/kit/bundle.js +21 -17
- package/dist/workstation/keepers/kit/launcher.js +47 -7
- package/dist/workstation/keepers/kit/paths.js +2 -1
- package/dist/workstation/managedInstall.js +57 -0
- package/dist/workstation/protocol/agent.js +2 -15
- package/dist/workstation/protocol/management.js +93 -4
- package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
- package/dist/workstation/rd/artifact.js +119 -0
- package/dist/workstation/rd/control.js +119 -0
- package/dist/workstation/rd/display.js +29 -0
- package/dist/workstation/rd/provider.js +222 -0
- package/dist/workstation/rd/sessions.js +391 -0
- package/dist/workstation/rd/state.js +45 -0
- package/dist/workstation/rd/update.js +171 -0
- package/dist/workstation/rd/upkeep.js +38 -0
- package/dist/workstation/register.js +27 -37
- package/dist/workstation/service/installer.js +104 -13
- package/dist/workstation/service/launchd.js +1 -0
- package/dist/workstation/storageMetrics.js +118 -0
- package/dist/workstation/systemMetrics.js +44 -92
- package/dist/workstation/toolchain.js +94 -0
- package/dist/workstation/tools/browser.js +28 -4
- package/dist/workstation/update/artifact.js +2 -1
- package/dist/workstation/update/globalInstall.js +116 -0
- package/dist/workstation/update/npmInstaller.js +49 -23
- package/dist/workstation/update/rdRollback.js +145 -0
- package/dist/workstation/update/runUpdate.js +3 -2
- package/dist/workstation/update/watchdog.js +16790 -334
- package/package.json +5 -5
- package/skills/axom/SKILL.md +11 -11
- package/skills/axom-definition/SKILL.md +3 -3
- package/skills/axom-platform/SKILL.md +3 -3
- package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
- package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
- package/dist/axom-patch/cli/qoder-steering.json +0 -52
- package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
- package/dist/commands/guide.js +0 -38
- package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
- package/dist/runtime-deps/qoder-sdk/package.json +0 -8
- package/dist/workstation/acp/delegationMcp.js +0 -24
- package/dist/workstation/acp/processor/sideChannel.js +0 -80
- package/dist/workstation/acp/qoderStartup.js +0 -45
- package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
- package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
- package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
- package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
- package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
- package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
- package/dist/workstation/qoderAdapter/contract.js +0 -78
- package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
- package/dist/workstation/qoderAdapter/history.js +0 -85
- package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
- package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
- package/dist/workstation/qoderAdapter/main.js +0 -95
- package/dist/workstation/qoderAdapter/mapper.js +0 -117
- package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
- package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
- package/dist/workstation/qoderAdapter/permissions.js +0 -61
- package/dist/workstation/qoderAdapter/profile.js +0 -93
- package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
- package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
- package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
- package/dist/workstation/qoderAdapter/session.js +0 -319
- package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
- package/dist/workstation/qoderAdapter/subwork.js +0 -85
- package/dist/workstation/qoderAdapter/taskControl.js +0 -38
- package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
- package/dist/workstation/qoderAdapter/usage.js +0 -37
- /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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,
|
|
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,
|
|
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
|
-
|
|
1006
|
-
|
|
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,
|
|
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)
|
|
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,
|
|
1020
|
-
const { spawn, label: baseLabel, launch } = this.spawnSpecOf(resolved, spec
|
|
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
|
|
1030
|
-
|
|
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
|
|
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),
|
|
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
|
|
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
|
|
1131
|
-
const allSecrets = [...
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
2147
|
-
|
|
2148
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
2697
|
-
const
|
|
2698
|
-
const
|
|
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;
|