@platless/axom-cli 0.5.9 → 0.6.52
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 +25 -6
- package/dist/axom-patch/cli/README.md +129 -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/claude-replay-lifecycle.json +16 -0
- 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/claude-replay-lifecycle.mjs +31 -0
- 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/probes/zcode-env-account.mjs +81 -0
- package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +7 -0
- package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1687 -0
- package/dist/axom-patch/cli/qoder/releases/1.1.75.json +277 -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 +19 -19
- package/dist/axom-patch/cli/zcode/env-account.mjs +13 -0
- package/dist/axom-patch/cli/zcode/generate-env-account.py +52 -0
- package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
- package/dist/axom-patch/cli/zcode-env-account.json +58 -0
- package/dist/axom-patch/cli/zcode-models.json +9 -10
- package/dist/axom-patch/cli/zcode-subagent-subwork.json +10 -11
- package/dist/cli.js +11 -45
- package/dist/commands/auth.js +4 -1
- package/dist/commands/knowledge.js +47 -0
- package/dist/commands/update.js +126 -3
- package/dist/commands/workstation.js +101 -164
- package/dist/commands/workstationRegistrations.js +116 -0
- package/dist/credentialBroker.js +13 -2
- package/dist/credentials.js +16 -0
- package/dist/install/claude/commands.js +15 -15
- package/dist/keepers/terminal/keeper.mjs +1089 -673
- package/dist/keepers/terminal/manifest.json +4 -4
- package/dist/mcp.js +1 -1
- package/dist/native/darwin-x64/axom-quic +0 -0
- package/dist/workstation/acp/acpAgents.js +113 -102
- 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/outbox.js +12 -5
- package/dist/workstation/acp/patchAssets.js +40 -0
- package/dist/workstation/acp/permission.js +2 -2
- package/dist/workstation/acp/processor/checkpoint.js +26 -34
- package/dist/workstation/acp/processor/control.js +7 -80
- package/dist/workstation/acp/processor/jsonrpc.js +8 -0
- package/dist/workstation/acp/processor/runtime.js +5 -81
- package/dist/workstation/acp/processor/session.js +54 -82
- package/dist/workstation/acp/processor/state.js +7 -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/transport/local.js +19 -3
- package/dist/workstation/acp/processor/updates.js +156 -105
- package/dist/workstation/acp/processorSession.js +20 -173
- 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 +32 -3
- 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 +114 -166
- package/dist/workstation/channels/agent.js +9 -1
- package/dist/workstation/channels/computer.js +27 -4
- package/dist/workstation/channels/management.js +130 -15
- package/dist/workstation/channels/url.js +1 -1
- package/dist/workstation/config.js +33 -9
- package/dist/workstation/connection/encryptedChannel.js +4 -1
- package/dist/workstation/control/bindingStore.js +204 -0
- package/dist/workstation/control/commands.js +125 -0
- package/dist/workstation/control/daemonIdentity.js +143 -0
- package/dist/workstation/control/deviceCommands.js +191 -0
- package/dist/workstation/control/deviceProtocol.js +68 -0
- package/dist/workstation/control/enrollment.js +106 -0
- package/dist/workstation/control/inspect.js +71 -0
- package/dist/workstation/control/locks.js +110 -0
- package/dist/workstation/control/protocol.js +40 -0
- package/dist/workstation/control/restartGuard.js +46 -0
- package/dist/workstation/control/rootFormat.js +16 -0
- package/dist/workstation/control/runtimeClient.js +152 -0
- package/dist/workstation/control/runtimeHost.js +235 -0
- package/dist/workstation/control/serverIdentity.js +35 -0
- package/dist/workstation/control/updateTransition.js +91 -0
- package/dist/workstation/daemon.js +266 -264
- package/dist/workstation/daemonctl.js +226 -25
- package/dist/workstation/desktopControl/audio.js +99 -0
- package/dist/workstation/desktopControl/automationDevice.js +219 -0
- package/dist/workstation/desktopControl/automationJson.js +74 -0
- package/dist/workstation/desktopControl/automationLocalState.js +23 -0
- package/dist/workstation/desktopControl/automationProtocol.js +253 -0
- package/dist/workstation/desktopControl/automationSchema.generated.js +1 -0
- package/dist/workstation/desktopControl/automationSession.js +81 -0
- package/dist/workstation/desktopControl/automationTransport.js +138 -0
- package/dist/workstation/desktopControl/capabilities.js +93 -0
- package/dist/workstation/desktopControl/captureModes.js +9 -0
- package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
- package/dist/workstation/desktopControl/deviceAdmission.js +229 -0
- package/dist/workstation/desktopControl/deviceOwnership.js +130 -0
- package/dist/workstation/desktopControl/deviceSockets.js +246 -0
- package/dist/workstation/desktopControl/helperProcess.js +602 -0
- package/dist/workstation/desktopControl/hostGrant.js +65 -0
- package/dist/workstation/desktopControl/lobby.js +131 -0
- package/dist/workstation/desktopControl/macProvider.js +156 -0
- package/dist/workstation/desktopControl/media.js +331 -0
- package/dist/workstation/desktopControl/nativeDeviceAdmission.js +207 -0
- package/dist/workstation/desktopControl/nativeProvider.js +1044 -0
- package/dist/workstation/desktopControl/networkEvidence.js +43 -0
- package/dist/workstation/desktopControl/overview.js +58 -0
- package/dist/workstation/desktopControl/ownershipProtocol.js +31 -0
- package/dist/workstation/desktopControl/protocol.js +311 -0
- package/dist/workstation/desktopControl/provider.js +1 -0
- package/dist/workstation/desktopControl/quicMedia.js +240 -0
- package/dist/workstation/desktopControl/rateController.js +226 -0
- package/dist/workstation/desktopControl/supervisor.js +1774 -0
- package/dist/workstation/desktopControl/transport.js +27 -0
- package/dist/workstation/desktopControl/wallpaper.js +98 -0
- package/dist/workstation/exec/policy.js +5 -0
- package/dist/workstation/files/client.js +10 -1
- package/dist/workstation/files/credentials.js +142 -0
- package/dist/workstation/files/host.js +9 -2
- package/dist/workstation/files/text.js +42 -8
- package/dist/workstation/journal.js +5 -2
- package/dist/workstation/keepers/acp/client.js +38 -2
- package/dist/workstation/keepers/kit/buildVersion.js +35 -11
- package/dist/workstation/keepers/kit/bundle.js +21 -17
- package/dist/workstation/keepers/kit/client.js +2 -2
- package/dist/workstation/keepers/kit/launcher.js +123 -7
- package/dist/workstation/keepers/kit/paths.js +2 -1
- package/dist/workstation/keepers/legacy.js +16 -2
- package/dist/workstation/keepers/policy.js +5 -34
- package/dist/workstation/keepers/terminal/client.js +115 -23
- package/dist/workstation/keepers/terminal/exec.js +6 -3
- package/dist/workstation/keepers/terminal/frames.js +73 -0
- package/dist/workstation/keepers/terminal/generations.js +5 -8
- package/dist/workstation/keepers/terminal/gridProtocol.js +16 -0
- package/dist/workstation/keepers/terminal/input.js +146 -0
- package/dist/workstation/keepers/terminal/keeper.js +22 -8
- package/dist/workstation/keepers/terminal/main.js +1 -1
- package/dist/workstation/keepers/terminal/metadata.js +138 -0
- package/dist/workstation/keepers/terminal/protocol.js +4 -28
- package/dist/workstation/keepers/terminal/pty.js +1 -330
- package/dist/workstation/keepers/terminal/screen.js +246 -0
- package/dist/workstation/keepers/terminal/sessions.js +526 -0
- package/dist/workstation/managedInstall.js +57 -0
- package/dist/workstation/oauth/deviceAuthorization.js +31 -17
- package/dist/workstation/protocol/agent.js +2 -15
- package/dist/workstation/protocol/management.js +113 -5
- package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
- package/dist/workstation/rd/artifact.js +120 -0
- package/dist/workstation/rd/control.js +117 -0
- package/dist/workstation/rd/display.js +29 -0
- package/dist/workstation/rd/host.js +213 -0
- package/dist/workstation/rd/migrationReadiness.js +46 -0
- package/dist/workstation/rd/provider.js +256 -0
- package/dist/workstation/rd/sessions.js +391 -0
- package/dist/workstation/rd/state.js +57 -0
- package/dist/workstation/rd/update.js +168 -0
- package/dist/workstation/rd/upkeep.js +80 -0
- package/dist/workstation/register.js +27 -37
- package/dist/workstation/registrations/enrollment.js +250 -0
- package/dist/workstation/registrations/enrollmentPending.js +361 -0
- package/dist/workstation/registrations/migration.js +356 -0
- package/dist/workstation/registrations/model.js +34 -0
- package/dist/workstation/registrations/removal.js +222 -0
- package/dist/workstation/registrations/store.js +737 -0
- package/dist/workstation/runtime/desktopBinding.js +20 -0
- package/dist/workstation/runtime/deviceDesktop.js +193 -0
- package/dist/workstation/runtime/deviceRuntime.js +568 -0
- package/dist/workstation/runtime/deviceServices.js +117 -0
- package/dist/workstation/runtime/events.js +20 -0
- package/dist/workstation/runtime/ownership.js +100 -0
- package/dist/workstation/runtime/registrationRuntime.js +575 -0
- package/dist/workstation/runtime/remoteDesktopAdapter.js +186 -0
- package/dist/workstation/runtime/removalActivity.js +184 -0
- package/dist/workstation/service/installer.js +197 -14
- package/dist/workstation/service/launchd.js +2 -0
- package/dist/workstation/service/systemd.js +1 -0
- package/dist/workstation/storageMetrics.js +118 -0
- package/dist/workstation/systemMetrics.js +47 -92
- package/dist/workstation/toolchain.js +94 -0
- package/dist/workstation/tools/browser.js +28 -4
- package/dist/workstation/tools/client.js +8 -27
- package/dist/workstation/tools/info.js +6 -13
- package/dist/workstation/tools/terminalRouter.js +17 -172
- package/dist/workstation/tools/validation.js +55 -1
- package/dist/workstation/update/archiveEvidence.js +206 -0
- package/dist/workstation/update/artifact.js +2 -1
- package/dist/workstation/update/coordinator.js +34 -13
- package/dist/workstation/update/deviceCompatibility.js +119 -0
- package/dist/workstation/update/deviceCoordinator.js +369 -0
- package/dist/workstation/update/deviceHealth.js +104 -0
- package/dist/workstation/update/deviceHealthProtocol.js +26 -0
- package/dist/workstation/update/deviceRecoveryLifecycle.js +46 -0
- package/dist/workstation/update/deviceRollback.js +78 -0
- package/dist/workstation/update/globalInstall.js +186 -0
- package/dist/workstation/update/legacyBridge.js +414 -0
- package/dist/workstation/update/legacyOwner.js +39 -0
- package/dist/workstation/update/npmInstaller.js +51 -25
- package/dist/workstation/update/privateDurability.js +61 -0
- package/dist/workstation/update/rdRollback.js +145 -0
- package/dist/workstation/update/recoveryPlan.js +124 -0
- package/dist/workstation/update/runUpdate.js +10 -3
- package/dist/workstation/update/taskRegistry.js +161 -3
- package/dist/workstation/update/watchdog.js +29806 -333
- package/dist/workstation/update/watchdogProcess.js +72 -0
- package/package.json +11 -7
- 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/automationPreparation.js} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "terminal",
|
|
3
|
-
"protocolVersion":
|
|
4
|
-
"buildVersion": "
|
|
3
|
+
"protocolVersion": 2,
|
|
4
|
+
"buildVersion": "80542c28e2e2ce561c3acc8dab28422a0df482b778665e982272e923261c775c",
|
|
5
5
|
"daemonProtocol": [
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
2,
|
|
7
|
+
2
|
|
8
8
|
]
|
|
9
9
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -98,7 +98,7 @@ export function isInsufficientScope(error) {
|
|
|
98
98
|
return /\b403\b/.test(message);
|
|
99
99
|
}
|
|
100
100
|
/** Abilities every credential has (Ability registry: BASELINE with the MCP surface). */
|
|
101
|
-
const BASELINE_TOOL_SCOPES = new Set(['system', '
|
|
101
|
+
const BASELINE_TOOL_SCOPES = new Set(['system', 'knowledge', 'app_struct']);
|
|
102
102
|
const TOOL_SCOPE = /^axom__([a-z][a-z0-9]*(?:_[a-z0-9]+)*)__[a-z]/;
|
|
103
103
|
const ADMIN_NOTE = ' Scopes marked "Requires an administrator account" on the consent page also need an administrator user.';
|
|
104
104
|
/** An external tool's middle segment IS the OAuth scope that grants it (spec 2026-09-26 §3.5). */
|
|
Binary file
|
|
@@ -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. */
|
|
@@ -510,6 +510,7 @@ export class AcpAgents {
|
|
|
510
510
|
/** {@link OPEN_INSTALL_BUDGET_MS}, overridable for tests. */
|
|
511
511
|
openInstallBudgetMs;
|
|
512
512
|
cacheDir;
|
|
513
|
+
ownership;
|
|
513
514
|
transports;
|
|
514
515
|
generations;
|
|
515
516
|
/** The once-per-daemon adoption's discovery ({@link adoptKeeperSessions}). */
|
|
@@ -575,14 +576,18 @@ export class AcpAgents {
|
|
|
575
576
|
this.runCommand = options.runCommand ?? runDependencyInstall;
|
|
576
577
|
this.runUv = options.runUv ?? defaultRunUv;
|
|
577
578
|
this.nodePath = options.nodePath ?? process.execPath;
|
|
579
|
+
this.serverOrigin = options.serverOrigin;
|
|
578
580
|
this.npmCommand = options.npmCommand ?? defaultNpmCommand(this.nodePath, this.platform);
|
|
579
581
|
this.binaryFetch = options.binaryFetch;
|
|
580
582
|
this.patchRecipes = options.patchRecipes;
|
|
583
|
+
this.managedRuntimePolicies = options.managedRuntimePolicies;
|
|
584
|
+
this.patchAssetRoot = options.patchAssetRoot;
|
|
581
585
|
this.commandRetryFirstBroadcastMs = options.commandRetryFirstBroadcastMs ?? COMMAND_RETRY_FIRST_BROADCAST_MS;
|
|
582
586
|
this.openInstallBudgetMs = options.openInstallBudgetMs ?? OPEN_INSTALL_BUDGET_MS;
|
|
583
|
-
this.adapterRoot = path.join(options.cacheDir, 'acp-adapters');
|
|
587
|
+
this.adapterRoot = path.join(options.adapterCacheDirectory ?? options.cacheDir, 'acp-adapters');
|
|
584
588
|
this.builds = new BuildStore(this.adapterRoot);
|
|
585
589
|
this.cacheDir = options.cacheDir;
|
|
590
|
+
this.ownership = options.ownership;
|
|
586
591
|
this.transports = { ...options.transports };
|
|
587
592
|
this.generations = options.keeperGenerations;
|
|
588
593
|
this.initializeTimeoutMs = options.initializeTimeoutMs ?? DEFAULT_SESSION_INITIALIZE_TIMEOUT_MS;
|
|
@@ -956,7 +961,7 @@ export class AcpAgents {
|
|
|
956
961
|
spec.onInstallOutcome?.(outcome);
|
|
957
962
|
}));
|
|
958
963
|
}
|
|
959
|
-
let resolved = await phase('prepare-launch', () => this.resolveLaunchFor(spec.agentId, spec.command, spec.args));
|
|
964
|
+
let resolved = await phase('prepare-launch', () => this.resolveLaunchFor(spec.agentId, spec.command, spec.args, spec.recordedBuildId));
|
|
960
965
|
// 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
966
|
// one loads the native session, never falling back to `session/new`.
|
|
962
967
|
if (this.transports.keeper && spec.sessionRef !== undefined && spec.nativeSessionId) {
|
|
@@ -966,20 +971,9 @@ export class AcpAgents {
|
|
|
966
971
|
}
|
|
967
972
|
// GC never takes the build between resolving it and the spawn (whose keeper-acp label references it from then on).
|
|
968
973
|
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
974
|
let session;
|
|
975
975
|
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;
|
|
976
|
+
session = await phase('initialize', () => this.startProcessorSession(resolved, spec, installed));
|
|
983
977
|
}
|
|
984
978
|
finally {
|
|
985
979
|
releaseLease();
|
|
@@ -987,14 +981,8 @@ export class AcpAgents {
|
|
|
987
981
|
const facts = session.initializeFacts;
|
|
988
982
|
this.observe(spec.agentId, observationOfFacts(facts));
|
|
989
983
|
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
984
|
const operation = spec.nativeSessionId && facts.capabilities.loadSession ? 'session/load' : 'session/new';
|
|
997
|
-
const started = await phase(operation, () => withDeadline(this.startSession(session, facts,
|
|
985
|
+
const started = await phase(operation, () => withDeadline(this.startSession(session, facts, spec), this.sessionStartTimeoutMs, `Agent '${spec.agentId}' ${operation}`, AgentSessionStartTimeoutError));
|
|
998
986
|
// Only here, never before: the agent accepted a session, which is the one piece of evidence
|
|
999
987
|
// ACP offers that it is not waiting on credentials. Recording it up at `initialize` time
|
|
1000
988
|
// would claim authentication for an agent about to reject `session/new` with auth_required.
|
|
@@ -1002,32 +990,31 @@ export class AcpAgents {
|
|
|
1002
990
|
return { session, nativeSessionId: started.nativeSessionId };
|
|
1003
991
|
}
|
|
1004
992
|
catch (error) {
|
|
1005
|
-
|
|
1006
|
-
|
|
993
|
+
// A recorded-build reopen is the only path allowed to retain an empty keeper record after
|
|
994
|
+
// bootstrap failure: the old native session still belongs to that package, and releasing an
|
|
995
|
+
// acknowledged transport would make the next retry silently select the mutable current build.
|
|
996
|
+
await session.close(spec.recordedBuildId !== undefined ? 'keep' : 'auto').catch(() => undefined);
|
|
1007
997
|
// Marked, not merely rethrown: everything above this `try` failed before the agent said
|
|
1008
998
|
// anything, and only a failure from here on can be an `auth_required`. See
|
|
1009
999
|
// {@link AgentRefusedSessionError} — `agentSessions.ts` is the reader.
|
|
1010
|
-
throw new AgentRefusedSessionError(error,
|
|
1000
|
+
throw new AgentRefusedSessionError(error, undefined);
|
|
1011
1001
|
}
|
|
1012
1002
|
}
|
|
1013
1003
|
/**
|
|
1014
1004
|
* Spawn the adapter behind a {@link ProcessorSession} and wait for its `initialize` (spec §5.3: keeper-acp, or the local
|
|
1015
1005
|
* 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.
|
|
1006
|
+
* reads its own), the daemon's working directory (where `connectAgent` started adapters); the label is opaque to the keeper.
|
|
1018
1007
|
*/
|
|
1019
|
-
async startProcessorSession(resolved, spec,
|
|
1020
|
-
const { spawn, label: baseLabel, launch } = this.spawnSpecOf(resolved, spec
|
|
1008
|
+
async startProcessorSession(resolved, spec, installOutcome) {
|
|
1009
|
+
const { spawn, label: baseLabel, launch } = this.spawnSpecOf(resolved, spec);
|
|
1021
1010
|
const ref = spec.sessionRef;
|
|
1022
1011
|
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
1012
|
const resumeFrom = spec.seqBase?.() ?? 0;
|
|
1027
1013
|
const local = await this.localLeftover(sessionKey);
|
|
1028
1014
|
let seqBase = Math.max(resumeFrom, local?.lastSeq ?? 0);
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1015
|
+
const pendingLocal = local !== undefined && local.lastSeq > resumeFrom && local.unacknowledged;
|
|
1016
|
+
const stayLocal = pendingLocal && !this.transports.keeper;
|
|
1017
|
+
if (local && !pendingLocal)
|
|
1031
1018
|
await this.localTransport().release(sessionKey).catch(() => undefined);
|
|
1032
1019
|
// Sessions an earlier daemon left in keeper-acp are discovered (and adopted) before keeper-acp spawns anything for this one.
|
|
1033
1020
|
if (this.transports.keeper)
|
|
@@ -1056,7 +1043,6 @@ export class AcpAgents {
|
|
|
1056
1043
|
spawn, seqBase, backfill: spec.backfill === true,
|
|
1057
1044
|
...(installOutcome ? { installOutcome } : {}),
|
|
1058
1045
|
launch,
|
|
1059
|
-
...(startup ? { onClosed: () => startup.dispose() } : {}),
|
|
1060
1046
|
};
|
|
1061
1047
|
let session;
|
|
1062
1048
|
if (this.transports.keeper && !stayLocal) {
|
|
@@ -1079,10 +1065,15 @@ export class AcpAgents {
|
|
|
1079
1065
|
else
|
|
1080
1066
|
this.heldOnOlder.delete(keeperKey); // a reopen hold on an older generation that is gone now
|
|
1081
1067
|
try {
|
|
1068
|
+
const imports = pendingLocal ? await readOutboxTail(this.localTransport(), sessionKey, local.outbox) : undefined;
|
|
1069
|
+
if (imports && local && imports.length !== local.outbox.next - Math.max(local.outbox.first, local.outbox.committed + 1)) {
|
|
1070
|
+
throw new Error('The local session outbox could not be read completely');
|
|
1071
|
+
}
|
|
1082
1072
|
session = await ProcessorSession.start({ ...options, sessionKey: keeperKey, transport: target.transport,
|
|
1073
|
+
...(imports ? { imports } : {}),
|
|
1083
1074
|
label: { ...baseLabel, ...(keyGen > 0 ? { keyGen } : {}) },
|
|
1084
1075
|
onDegrade: this.recordDegraded(keeperKey, target.gen ?? 'current'),
|
|
1085
|
-
...(target.gen !== undefined ? { onClosed: () => this.olderSessionClosed(keeperKey
|
|
1076
|
+
...(target.gen !== undefined ? { onClosed: () => this.olderSessionClosed(keeperKey) } : {}) });
|
|
1086
1077
|
}
|
|
1087
1078
|
catch (error) {
|
|
1088
1079
|
this.heldOnOlder.delete(keeperKey);
|
|
@@ -1113,22 +1104,26 @@ export class AcpAgents {
|
|
|
1113
1104
|
await session.initialized(this.initializeTimeoutMs);
|
|
1114
1105
|
}
|
|
1115
1106
|
catch (error) {
|
|
1116
|
-
await session.close('auto').catch(() => undefined);
|
|
1107
|
+
await session.close(spec.recordedBuildId !== undefined ? 'keep' : 'auto').catch(() => undefined);
|
|
1117
1108
|
throw error;
|
|
1118
1109
|
}
|
|
1110
|
+
if (pendingLocal && session.transportMode === 'keeper') {
|
|
1111
|
+
await this.localTransport().release(sessionKey).catch((error) => {
|
|
1112
|
+
console.warn(`[acp] releasing the migrated local outbox for ${sessionKey} failed: ${errorMessage(error)}`);
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1119
1115
|
return session;
|
|
1120
1116
|
}
|
|
1121
1117
|
/**
|
|
1122
1118
|
* 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.
|
|
1119
|
+
* own), the daemon's working directory (where `connectAgent` started adapters), the label is opaque to the keeper.
|
|
1125
1120
|
*/
|
|
1126
|
-
spawnSpecOf(resolved, spec
|
|
1121
|
+
spawnSpecOf(resolved, spec) {
|
|
1127
1122
|
const { launch } = resolved;
|
|
1128
1123
|
const env = completeEnvironment(withPathPrefix({ ...process.env, ...launch.env, ...spec.env, ...spec.agentEnv }, launch.pathPrefix));
|
|
1129
1124
|
const cwd = process.cwd();
|
|
1130
|
-
// keeper-acp takes at most ACP_STDERR_SECRETS_MAX
|
|
1131
|
-
const allSecrets = [...
|
|
1125
|
+
// keeper-acp takes at most ACP_STDERR_SECRETS_MAX.
|
|
1126
|
+
const allSecrets = [...overlayEnvSecrets(spec)];
|
|
1132
1127
|
if (allSecrets.length > ACP_STDERR_SECRETS_MAX) {
|
|
1133
1128
|
console.warn(`[acp] agent '${spec.agentId}': ${allSecrets.length - ACP_STDERR_SECRETS_MAX} of ${allSecrets.length} env values ` +
|
|
1134
1129
|
`are not redacted from its stderr (keeper-acp takes at most ${ACP_STDERR_SECRETS_MAX})`);
|
|
@@ -1137,7 +1132,6 @@ export class AcpAgents {
|
|
|
1137
1132
|
const spawn = {
|
|
1138
1133
|
command: launch.command, args: [...launch.args], env, cwd,
|
|
1139
1134
|
...(stderrSecrets.length > 0 ? { stderrSecrets } : {}),
|
|
1140
|
-
...(startup ? { removeOnExit: [startup.dir] } : {}),
|
|
1141
1135
|
};
|
|
1142
1136
|
const label = {
|
|
1143
1137
|
sessionRef: spec.sessionRef ?? null, agentId: spec.agentId, agentKey: spec.agentKey ?? null,
|
|
@@ -1450,9 +1444,6 @@ export class AcpAgents {
|
|
|
1450
1444
|
else if (!nativeSessionId) {
|
|
1451
1445
|
outcome = refuse('the session has no native session to load');
|
|
1452
1446
|
}
|
|
1453
|
-
else if (spec.delegationMcp || from.delegation) {
|
|
1454
|
-
outcome = refuse('a managed delegation session is not moved');
|
|
1455
|
-
}
|
|
1456
1447
|
else {
|
|
1457
1448
|
outcome = await this.builds.withAgentLock(spec.agentId, () => this.switchLocked(from, { ...spec, sessionRef: ref, nativeSessionId }, request))
|
|
1458
1449
|
.catch((error) => refuse(`the switch failed before anything was stopped: ${errorMessage(error)}`, false));
|
|
@@ -1731,6 +1722,7 @@ export class AcpAgents {
|
|
|
1731
1722
|
source: 'registry',
|
|
1732
1723
|
launch: await launchForBuild(build, this.adapterRoot, {
|
|
1733
1724
|
nodePath: this.nodePath, launchEnv: catalogLaunchEnv(agentId, build.registryVersion), basePath: this.probe.path,
|
|
1725
|
+
...this.runtimeAssets(agentId), allowLegacy: true,
|
|
1734
1726
|
}),
|
|
1735
1727
|
};
|
|
1736
1728
|
}
|
|
@@ -1853,6 +1845,8 @@ export class AcpAgents {
|
|
|
1853
1845
|
* be listed. One at a time; never rejects (failures are logged).
|
|
1854
1846
|
*/
|
|
1855
1847
|
collectBuilds(options = {}) {
|
|
1848
|
+
if (this.ownership)
|
|
1849
|
+
return Promise.resolve({ removed: [], discarded: [], kept: [], legacyRemoved: [], skipped: 'Device-wide build references are incomplete' });
|
|
1856
1850
|
const previous = this.collecting;
|
|
1857
1851
|
let run;
|
|
1858
1852
|
run = (async () => {
|
|
@@ -1892,12 +1886,11 @@ export class AcpAgents {
|
|
|
1892
1886
|
return this.unlistedGens.has(gen) || [...this.heldOnOlder.values()].includes(gen);
|
|
1893
1887
|
}
|
|
1894
1888
|
/** A session of this daemon on an older generation ended: that generation may be idle now. */
|
|
1895
|
-
olderSessionClosed(sessionKey
|
|
1889
|
+
olderSessionClosed(sessionKey) {
|
|
1896
1890
|
// A reopen's retire keeps the hold: the respawn goes into the same generation right behind it (fix round 2).
|
|
1897
1891
|
if (!this.reopenHolds.has(sessionKey))
|
|
1898
1892
|
this.heldOnOlder.delete(sessionKey);
|
|
1899
1893
|
void this.retireIdleGenerations();
|
|
1900
|
-
return also?.();
|
|
1901
1894
|
}
|
|
1902
1895
|
/**
|
|
1903
1896
|
* 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 +1972,7 @@ export class AcpAgents {
|
|
|
1979
1972
|
const tail = summary.outbox.committedTail;
|
|
1980
1973
|
const tailSeq = typeof tail?.seq === 'number' ? tail.seq : 0;
|
|
1981
1974
|
return {
|
|
1975
|
+
outbox: summary.outbox,
|
|
1982
1976
|
lastSeq: Math.max(summary.outbox.lastSeq ?? 0, tailSeq),
|
|
1983
1977
|
unacknowledged: summary.outbox.next > Math.max(summary.outbox.first, summary.outbox.committed + 1),
|
|
1984
1978
|
};
|
|
@@ -2075,15 +2069,18 @@ export class AcpAgents {
|
|
|
2075
2069
|
}
|
|
2076
2070
|
/** The fallback transport (spec §5.7), made on first use. */
|
|
2077
2071
|
localTransport() {
|
|
2078
|
-
this.transports.local ??= new LocalTransport({ configDir: this.cacheDir });
|
|
2072
|
+
this.transports.local ??= new LocalTransport({ configDir: this.cacheDir, ownership: this.ownership });
|
|
2079
2073
|
return this.transports.local;
|
|
2080
2074
|
}
|
|
2081
2075
|
/** Daemon shutdown: keeper-acp's connection is dropped (its adapters keep running); local adapters are stopped. */
|
|
2082
|
-
closeTransports() {
|
|
2076
|
+
async closeTransports() {
|
|
2083
2077
|
this.clearQuotaReaders();
|
|
2084
2078
|
this.transports.keeper?.close();
|
|
2085
2079
|
this.generations?.close();
|
|
2086
|
-
this.transports.local
|
|
2080
|
+
if (this.transports.local instanceof LocalTransport)
|
|
2081
|
+
await this.transports.local.dispose();
|
|
2082
|
+
else
|
|
2083
|
+
this.transports.local?.close();
|
|
2087
2084
|
}
|
|
2088
2085
|
clearQuotaReaders(agentId) {
|
|
2089
2086
|
for (const [key, reader] of this.quotaReaders)
|
|
@@ -2113,60 +2110,35 @@ export class AcpAgents {
|
|
|
2113
2110
|
async startSession(session, facts, spec) {
|
|
2114
2111
|
const nativeSessionId = spec.nativeSessionId;
|
|
2115
2112
|
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
2113
|
if (spec.requireSessionLoad && !nativeSessionId) {
|
|
2123
2114
|
throw new Error('Refreshing settings requires an existing native session ID');
|
|
2124
2115
|
}
|
|
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);
|
|
2116
|
+
const mcpServers = [...(spec.mcpServers ?? [])];
|
|
2129
2117
|
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 } : {}) };
|
|
2118
|
+
const request = { cwd: spec.cwd, mcpServers, redact };
|
|
2145
2119
|
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) {
|
|
2120
|
+
// The adapter declares cached reconnect semantics in initialize; explicit history/settings loads still use load.
|
|
2121
|
+
if (nativeSessionId && facts.cachedReconnect === 'resume' && capabilities.resume &&
|
|
2122
|
+
!spec.backfill && !spec.requireSessionLoad) {
|
|
2150
2123
|
const result = await session.openSession({ ...request, mode: 'resume', sessionId: nativeSessionId });
|
|
2151
2124
|
if (!result.ok)
|
|
2152
2125
|
throw refused(result); // Never silently replace this cached conversation with a fresh native root.
|
|
2153
|
-
await session.enableRepair(nativeSessionId, spec.cwd, historyHome, true);
|
|
2154
2126
|
return { nativeSessionId };
|
|
2155
2127
|
}
|
|
2156
2128
|
if (nativeSessionId) {
|
|
2157
2129
|
if (!capabilities.loadSession) {
|
|
2158
|
-
if (spec.requireSessionLoad
|
|
2130
|
+
if (spec.requireSessionLoad)
|
|
2159
2131
|
throw new Error(`Agent '${spec.agentId}' does not support session/load`);
|
|
2160
2132
|
console.warn(`[acp] agent '${spec.agentId}' does not support session/load; starting a new session ` +
|
|
2161
2133
|
`instead of continuing '${nativeSessionId}'`);
|
|
2162
2134
|
}
|
|
2163
2135
|
else {
|
|
2164
|
-
const result = await session.openSession({ ...request, mode: 'load', sessionId: nativeSessionId
|
|
2136
|
+
const result = await session.openSession({ ...request, mode: 'load', sessionId: nativeSessionId });
|
|
2165
2137
|
if (result.ok) {
|
|
2166
2138
|
await session.endHistory();
|
|
2167
2139
|
return { nativeSessionId };
|
|
2168
2140
|
}
|
|
2169
|
-
if (spec.requireSessionLoad
|
|
2141
|
+
if (spec.requireSessionLoad)
|
|
2170
2142
|
throw refused(result);
|
|
2171
2143
|
console.warn(`[acp] session/load for '${nativeSessionId}' on agent '${spec.agentId}' failed ` +
|
|
2172
2144
|
`(${result.error}); starting a new session instead`);
|
|
@@ -2175,8 +2147,6 @@ export class AcpAgents {
|
|
|
2175
2147
|
const result = await session.openSession({ ...request, mode: 'new' });
|
|
2176
2148
|
if (!result.ok)
|
|
2177
2149
|
throw refused(result);
|
|
2178
|
-
if (result.nativeSessionId)
|
|
2179
|
-
await session.enableRepair(result.nativeSessionId, spec.cwd, historyHome);
|
|
2180
2150
|
return { nativeSessionId: result.nativeSessionId };
|
|
2181
2151
|
}
|
|
2182
2152
|
/**
|
|
@@ -2570,6 +2540,9 @@ export class AcpAgents {
|
|
|
2570
2540
|
...(outcome.pinnedVersionUnavailable ? { pinnedVersionUnavailable: outcome.pinnedVersionUnavailable } : {}),
|
|
2571
2541
|
};
|
|
2572
2542
|
}
|
|
2543
|
+
runtimeAssets(agentId) {
|
|
2544
|
+
return { policy: managedRuntimeFor(agentId, this.managedRuntimePolicies), assetRoot: this.patchAssetRoot, recipes: this.patchRecipes };
|
|
2545
|
+
}
|
|
2573
2546
|
/** The registry as Axom installs it (catalog supplements, pins, launchEnv kept apart) — see `registry/catalog.ts`. */
|
|
2574
2547
|
async effective() {
|
|
2575
2548
|
return effectiveEntries(await this.baseRegistry.load());
|
|
@@ -2590,18 +2563,21 @@ export class AcpAgents {
|
|
|
2590
2563
|
currentBuildId) {
|
|
2591
2564
|
const agent = entry.agent;
|
|
2592
2565
|
const { npx, uvx, binary } = agent.distribution;
|
|
2566
|
+
const policy = this.runtimeAssets(agent.id).policy;
|
|
2567
|
+
if (policy && (!npx || agent.version !== policy.version))
|
|
2568
|
+
return { ok: false, error: 'Managed runtime Catalog/distribution differs from bundled release policy' };
|
|
2593
2569
|
const source = entry.pinnedBy ?? 'registry';
|
|
2594
2570
|
const commit = (build) => this.builds.commit(build, { makeCurrent: build.buildId !== currentBuildId, setBy });
|
|
2595
2571
|
if (npx) {
|
|
2596
|
-
const pristine = await this.installPristineWithFallback(agent,
|
|
2572
|
+
const pristine = await this.installPristineWithFallback(agent, timeoutMs);
|
|
2597
2573
|
if (!pristine.ok)
|
|
2598
2574
|
return pristine;
|
|
2599
2575
|
try {
|
|
2600
2576
|
const build = await npmBuild({
|
|
2601
|
-
store: this.builds, agentId: agent.id, name: npmPackageName(npx.package), version: pristine.version,
|
|
2577
|
+
store: this.builds, agentId: agent.id, name: npx.tarball ? npx.package : npmPackageName(npx.package), version: pristine.version,
|
|
2602
2578
|
args: npx.args ?? [], env: npx.env ?? {}, registryVersion: entry.registryVersion,
|
|
2603
2579
|
source: pristine.pinnedVersionUnavailable ? 'unpinned-fallback' : source, installedBy: setBy,
|
|
2604
|
-
nodePath: this.nodePath,
|
|
2580
|
+
nodePath: this.nodePath, ...this.runtimeAssets(agent.id),
|
|
2605
2581
|
...(this.patchRecipes ? { recipes: this.patchRecipes } : {}),
|
|
2606
2582
|
});
|
|
2607
2583
|
const recorded = pristine.pinnedVersionUnavailable
|
|
@@ -2693,18 +2669,24 @@ export class AcpAgents {
|
|
|
2693
2669
|
* have), one retry unpinned, recording the version that actually landed. This is the **only** place
|
|
2694
2670
|
* that fallback exists now: a launch never resolves a version (spec §6.2 "ETARGET 回退移到安装期").
|
|
2695
2671
|
*/
|
|
2696
|
-
async installPristineWithFallback(agent,
|
|
2697
|
-
const
|
|
2698
|
-
const
|
|
2672
|
+
async installPristineWithFallback(agent, timeoutMs) {
|
|
2673
|
+
const distribution = agent.distribution.npx;
|
|
2674
|
+
const pinnedSpec = distribution.package;
|
|
2675
|
+
const name = distribution.tarball ? distribution.package : npmPackageName(pinnedSpec);
|
|
2676
|
+
const version = distribution.tarball ? agent.version : npmPackageVersion(pinnedSpec);
|
|
2677
|
+
if (distribution.tarball && !this.serverOrigin)
|
|
2678
|
+
return { ok: false, error: 'Adapter is served by the Axom server; this workstation has no server configured' };
|
|
2679
|
+
const tarball = distribution.tarball ? { url: new URL(distribution.tarball.path, this.serverOrigin).href, sha512: distribution.tarball.sha512 } : undefined;
|
|
2680
|
+
const runtime = this.runtimeAssets(agent.id);
|
|
2699
2681
|
const budget = timeoutMs ?? ADAPTER_INSTALL_TIMEOUT_MS;
|
|
2700
2682
|
const install = (pinned) => ensurePristine({
|
|
2701
|
-
store: this.builds, name, ...(pinned !== undefined ? { version: pinned } : {}), npmCommand: this.npmCommand, timeoutMs: budget,
|
|
2683
|
+
store: this.builds, name, ...runtime, ...(pinned !== undefined ? { version: pinned } : {}), npmCommand: this.npmCommand, timeoutMs: budget, ...(tarball ? { tarball } : {}),
|
|
2702
2684
|
}).catch((error) => ({ ok: false, error: errorMessage(error), stderr: '' }));
|
|
2703
2685
|
const outcome = await install(version);
|
|
2704
2686
|
if (outcome.ok)
|
|
2705
2687
|
return outcome;
|
|
2706
2688
|
const existingVersion = (await this.builds.current(agent.id))?.version;
|
|
2707
|
-
if (version === undefined || !stderrShowsMissingPackageVersion(outcome.stderr)) {
|
|
2689
|
+
if (runtime.policy || tarball || version === undefined || !stderrShowsMissingPackageVersion(outcome.stderr)) {
|
|
2708
2690
|
logInstallFailureDetail(agent.id, 'npx', pinnedSpec, outcome.stderr || outcome.error);
|
|
2709
2691
|
return { ok: false, error: friendlyOrdinaryInstallError(agent.id, outcome.stderr || outcome.error, existingVersion) };
|
|
2710
2692
|
}
|
|
@@ -2779,6 +2761,24 @@ export class AcpAgents {
|
|
|
2779
2761
|
const current = await this.builds.current(agentId);
|
|
2780
2762
|
if (!current)
|
|
2781
2763
|
return undefined;
|
|
2764
|
+
const runtime = this.runtimeAssets(agentId);
|
|
2765
|
+
if (runtime.policy) {
|
|
2766
|
+
const npx = entry.agent.distribution.npx;
|
|
2767
|
+
if (!npx || entry.agent.version !== runtime.policy.version)
|
|
2768
|
+
throw new Error('Managed runtime Catalog/distribution differs from bundled policy');
|
|
2769
|
+
assertRuntimeRequest(runtime.policy, npx.tarball ? npx.package : npmPackageName(npx.package), npx.tarball ? entry.agent.version : npmPackageVersion(npx.package));
|
|
2770
|
+
const release = await loadRuntimeRelease(runtime.policy, runtime);
|
|
2771
|
+
if (current.runtimeReleaseId === release.id || (!current.runtimeReleaseId && current.package === runtime.policy.package && current.version === runtime.policy.version)) {
|
|
2772
|
+
await verifyManagedRuntime({ ...runtime, policy: runtime.policy, build: current, storeRoot: this.adapterRoot, nodePath: this.nodePath, initialize: false });
|
|
2773
|
+
return undefined;
|
|
2774
|
+
}
|
|
2775
|
+
const outcome = await this.installAdapter(entry, 'axom', timeoutMs, current.buildId);
|
|
2776
|
+
if (!outcome.ok)
|
|
2777
|
+
throw new Error(outcome.error);
|
|
2778
|
+
if (!outcome.build || outcome.build.buildId === current.buildId)
|
|
2779
|
+
return undefined;
|
|
2780
|
+
return { agentId, from: current.buildId, to: outcome.build.buildId, reason: 'certified-pin' };
|
|
2781
|
+
}
|
|
2782
2782
|
// The pin this CLI certifies was already tried and npm did not have it: the current build is the
|
|
2783
2783
|
// install-time fallback for exactly this pin. Trying again on every daemon start would only repeat
|
|
2784
2784
|
// the same network round trip and land on the same build.
|
|
@@ -2796,7 +2796,7 @@ export class AcpAgents {
|
|
|
2796
2796
|
if ((current.kind !== 'npm' && current.kind !== 'npm-patched') || !current.package)
|
|
2797
2797
|
return undefined;
|
|
2798
2798
|
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);
|
|
2799
|
+
const recipes = await selectPackagePatches(current.package, current.version, inside(path.join(pristine, 'node_modules'), current.package), this.patchRecipes, await installedDependencies(pristine));
|
|
2800
2800
|
const wanted = recipes.length ? patchDigest(recipes) : undefined;
|
|
2801
2801
|
if (wanted === current.patchDigest)
|
|
2802
2802
|
return undefined;
|
|
@@ -2890,6 +2890,8 @@ export class AcpAgents {
|
|
|
2890
2890
|
if (!entry)
|
|
2891
2891
|
return undefined;
|
|
2892
2892
|
const { npx, uvx, binary } = entry.agent.distribution;
|
|
2893
|
+
if (this.runtimeAssets(agentId).policy && !npx)
|
|
2894
|
+
throw new Error('Managed runtime migration requires its certified npm distribution');
|
|
2893
2895
|
if (npx)
|
|
2894
2896
|
return this.migrateFromNpx(entry, npx.package, npmCache);
|
|
2895
2897
|
// uv: the user's own uv tool dir is not ours to take over; the first open installs a build.
|
|
@@ -2903,6 +2905,12 @@ export class AcpAgents {
|
|
|
2903
2905
|
const name = npmPackageName(spec);
|
|
2904
2906
|
// Exactly what the pre-build launch ran: `npx <name>@<effective version>`.
|
|
2905
2907
|
const version = requestedVersionOf(entry);
|
|
2908
|
+
const runtime = this.runtimeAssets(agentId);
|
|
2909
|
+
if (runtime.policy) {
|
|
2910
|
+
if (entry.agent.version !== runtime.policy.version)
|
|
2911
|
+
throw new Error('Managed runtime Catalog version mismatch');
|
|
2912
|
+
assertRuntimeRequest(runtime.policy, name, version);
|
|
2913
|
+
}
|
|
2906
2914
|
const copy = await findNpxCopy(npmCache, name, version);
|
|
2907
2915
|
if (!copy)
|
|
2908
2916
|
return undefined;
|
|
@@ -2912,7 +2920,7 @@ export class AcpAgents {
|
|
|
2912
2920
|
...await npmBuild({
|
|
2913
2921
|
store: this.builds, agentId, name, version, args: npx.args ?? [], env: npx.env ?? {},
|
|
2914
2922
|
registryVersion: entry.registryVersion, source: entry.pinnedBy ?? 'registry', installedBy: 'axom',
|
|
2915
|
-
nodePath: this.nodePath, ...(this.patchRecipes ? { recipes: this.patchRecipes } : {}),
|
|
2923
|
+
nodePath: this.nodePath, ...runtime, ...(this.patchRecipes ? { recipes: this.patchRecipes } : {}),
|
|
2916
2924
|
}),
|
|
2917
2925
|
migratedFrom: 'npx-cache',
|
|
2918
2926
|
};
|
|
@@ -3064,6 +3072,7 @@ export class AcpAgents {
|
|
|
3064
3072
|
return launchForBuild(current, this.adapterRoot, {
|
|
3065
3073
|
nodePath: this.nodePath,
|
|
3066
3074
|
launchEnv: catalogLaunchEnv(agentId, current.registryVersion),
|
|
3075
|
+
...this.runtimeAssets(agentId),
|
|
3067
3076
|
basePath: this.probe.path,
|
|
3068
3077
|
});
|
|
3069
3078
|
}
|
|
@@ -3075,9 +3084,11 @@ export class AcpAgents {
|
|
|
3075
3084
|
throw new AdapterNotInstalledError(agentId);
|
|
3076
3085
|
}
|
|
3077
3086
|
/** A config-supplied `command` is the whole launch spec (ruling R3); otherwise the current build decides. */
|
|
3078
|
-
async resolveLaunchFor(agentId, command, args) {
|
|
3087
|
+
async resolveLaunchFor(agentId, command, args, recordedBuildId) {
|
|
3079
3088
|
if (command)
|
|
3080
3089
|
return { launch: { command, args, env: {} }, source: 'command-override' };
|
|
3090
|
+
if (recordedBuildId !== undefined)
|
|
3091
|
+
return this.launchOfBuild(agentId, recordedBuildId);
|
|
3081
3092
|
return { source: 'registry', launch: await this.launchSpecFor(agentId) };
|
|
3082
3093
|
}
|
|
3083
3094
|
/**
|
|
@@ -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;
|