@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
|
@@ -16,8 +16,10 @@ import { expect, vi } from 'vitest';
|
|
|
16
16
|
import { killKeepersUnder, tempDir } from '../../../test/keeperKit.js';
|
|
17
17
|
import { AcpKeeperClient } from '../../keepers/acp/client.js';
|
|
18
18
|
import { computeBuildVersion } from '../../keepers/kit/buildVersion.js';
|
|
19
|
+
import { readKeeperManifest } from '../../keepers/kit/bundle.js';
|
|
19
20
|
import { keeperGen } from '../../keepers/kit/identity.js';
|
|
20
21
|
import { KeeperLauncher } from '../../keepers/kit/launcher.js';
|
|
22
|
+
import { acquireStartupLock, releaseStartupLock } from '../../keepers/kit/lock.js';
|
|
21
23
|
import { AgentSessionManager } from '../../agentSessions.js';
|
|
22
24
|
import { AGENT_WIRE_VERSION } from '../../protocol/agent.js';
|
|
23
25
|
import { TaskRegistry } from '../../update/taskRegistry.js';
|
|
@@ -97,6 +99,32 @@ export class KeeperWorld {
|
|
|
97
99
|
this.agentDir = this.newAgentDir(`${prefix}agent-`);
|
|
98
100
|
this.launcher = this.launcherFor(realDist);
|
|
99
101
|
}
|
|
102
|
+
/** These integrations require keeper hosting, rather than fallback after an unrelated lock-port collision. */
|
|
103
|
+
static async create(prefix, includeOlderGeneration = false) {
|
|
104
|
+
const current = keeperGen((await readKeeperManifest(join(realDist, 'acp'))).buildVersion);
|
|
105
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
106
|
+
const candidate = new this(prefix);
|
|
107
|
+
const locks = [];
|
|
108
|
+
try {
|
|
109
|
+
const generations = [current];
|
|
110
|
+
if (includeOlderGeneration)
|
|
111
|
+
generations.push(candidate.olderGeneration().oldGen);
|
|
112
|
+
for (const gen of new Set(generations)) {
|
|
113
|
+
locks.push(await acquireStartupLock(candidate.launcher.pathsFor('acp', gen).lockPort));
|
|
114
|
+
}
|
|
115
|
+
return candidate;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
await candidate.dispose();
|
|
119
|
+
if (error.code !== 'EADDRINUSE')
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await Promise.all(locks.map(releaseStartupLock));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
throw new Error('No unoccupied keeper-acp integration fixture identity');
|
|
127
|
+
}
|
|
100
128
|
launcherFor(distDir) {
|
|
101
129
|
return new KeeperLauncher({ configDir: this.configDir, workstationId: 'ws-t21', server: 'http://test.invalid', distDir, useSystemd: false,
|
|
102
130
|
startTimeoutMs: 20_000, umask: 0o022 });
|
|
@@ -116,12 +144,13 @@ export class KeeperWorld {
|
|
|
116
144
|
const real = new KeeperTransport(new AcpKeeperClient({ launcher: use }));
|
|
117
145
|
const keeper = (over.wrapKeeper ? over.wrapKeeper(real) : real);
|
|
118
146
|
const generations = keeperGenerations({ launcher: use, current: keeper, log: () => { } });
|
|
119
|
-
const
|
|
147
|
+
const journalRoot = use.ownership ? join(this.configDir, 'registrations', use.ownership.registrationId) : this.configDir;
|
|
148
|
+
const local = new LocalTransport({ configDir: journalRoot, ownership: use.ownership });
|
|
120
149
|
const acp = new AcpAgents({ cacheDir: this.configDir, registry: { load: async () => [] }, transports: { keeper, local }, keeperGenerations: generations,
|
|
121
150
|
sessionStartTimeoutMs: 15_000, ...over.acp });
|
|
122
151
|
const tasks = new TaskRegistry();
|
|
123
152
|
const sent = [];
|
|
124
|
-
const manager = new AgentSessionManager(acp, null, join(
|
|
153
|
+
const manager = new AgentSessionManager(acp, null, join(journalRoot, 'agents'), tasks, () => true, use.ownership);
|
|
125
154
|
manager.bindSend((frame) => { sent.push(frame); server.apply(frame); }, AGENT_WIRE_VERSION);
|
|
126
155
|
let killed = false;
|
|
127
156
|
const result = {
|
|
@@ -195,7 +224,7 @@ export class KeeperWorld {
|
|
|
195
224
|
const n = this.processes.length + 1;
|
|
196
225
|
const framesOut = join(this.configDir, `frames-daemon-${n}.jsonl`);
|
|
197
226
|
const stderrFile = join(this.configDir, `daemon-${n}.stderr`);
|
|
198
|
-
const child = spawn(join(packageRoot, 'node_modules', '.bin', 'vite-node'), [DAEMON_LIFE,
|
|
227
|
+
const child = spawn(join(packageRoot, 'node_modules', '.bin', 'vite-node'), ['--config', join(packageRoot, 'src/test/fixtures/nodeRuntime.vite.config.ts'), DAEMON_LIFE,
|
|
199
228
|
JSON.stringify({ configDir: this.configDir, distDir: realDist, framesOut, faults })], { cwd: packageRoot, stdio: ['pipe', 'ignore', 'pipe'] });
|
|
200
229
|
let stderr = '';
|
|
201
230
|
child.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8'); appendFileSync(stderrFile, chunk); });
|
|
@@ -1,29 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
* Every capture Task 17d's gate runs on, and how each becomes a processor raw log.
|
|
3
|
-
*
|
|
4
|
-
* - `real`: the committed, sanitised real captures (`*.jsonl` in this directory, by discovery).
|
|
5
|
-
* - `qoder`: Qoder captures wrapped from the update-level Qoder fixtures (`qoderCaptures.ts`) — there
|
|
6
|
-
* is no raw Qoder capture; a real one is T23's.
|
|
7
|
-
* - `synthetic`: hand-written captures that pin the known ordering fixes — experiment 0.1's wire
|
|
8
|
-
* (analysis B §0.1) and out-of-turn chunks followed by the next prompt (17b) — an interrupt
|
|
9
|
-
* mid-turn (`session/cancel`), which no real capture has, and (T19 prerequisite) a `session/load`
|
|
10
|
-
* with history and a Qoder `session/resume`.
|
|
11
|
-
*
|
|
12
|
-
* {@link buildLog} plays the daemon: the transducer (`fixtures.ts`), plus, for Qoder, the
|
|
13
|
-
* `qoder_terminal_repair` note where `AcpAgents` enables the repair today (after the `session/new`
|
|
14
|
-
* response) and the side-file notes the daemon's `BackgroundSideNotes` reader appends once the native
|
|
15
|
-
* rows exist (after the turn, as the live repair sees them).
|
|
16
|
-
*/
|
|
17
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
18
|
-
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
19
|
-
import { tmpdir } from 'node:os';
|
|
20
|
-
import { dirname, join } from 'node:path';
|
|
21
|
-
import { BackgroundSideNotes, nativeTerminalHistoryPath } from '../../../qoderAdapter/backgroundTerminal.js';
|
|
1
|
+
import { join } from 'node:path';
|
|
22
2
|
import { decodeStreamRecord } from '../../processor/record.js';
|
|
23
|
-
import { sideDemand } from '../../processor/sideChannel.js';
|
|
24
|
-
import { run } from '../../processor/test/harness.js';
|
|
25
3
|
import { fixtureNames, RAW_DIR, readFixtureText, transduce } from './fixtures.js';
|
|
26
|
-
|
|
4
|
+
/** Lines → kit-stream ndjson (a synthetic `proc spawn` first, `t` = position). */
|
|
5
|
+
export function captureText(lines) {
|
|
6
|
+
const records = [JSON.stringify({ o: 0, t: 0, k: 'proc', kind: 'spawn', spawnId: 'capture', pid: 0, pgid: 0, startTime: 0 })];
|
|
7
|
+
lines.forEach((line, index) => records.push(JSON.stringify({ o: index + 1, t: index + 1, k: line.k, l: JSON.stringify(line.message) })));
|
|
8
|
+
return `${records.join('\n')}\n`;
|
|
9
|
+
}
|
|
10
|
+
export const up = (message) => ({ k: 'up', message });
|
|
11
|
+
export const down = (message) => ({ k: 'down', message });
|
|
12
|
+
export const update = (sessionId, value) => up({ jsonrpc: '2.0', method: 'session/update', params: { sessionId, update: value } });
|
|
27
13
|
const SYNTHETIC_PROMPT = [{ type: 'text', text: 'go' }];
|
|
28
14
|
const opening = (sessionId) => [
|
|
29
15
|
down({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: 1, clientCapabilities: {} } }),
|
|
@@ -117,7 +103,7 @@ function loadThenPrompt() {
|
|
|
117
103
|
]) };
|
|
118
104
|
}
|
|
119
105
|
/**
|
|
120
|
-
* T19 prerequisite: a
|
|
106
|
+
* T19 prerequisite: a cached `session/resume` (an adapter advertising
|
|
121
107
|
* `sessionCapabilities.resume`) — no history, a config snapshot on the response, then one turn.
|
|
122
108
|
*/
|
|
123
109
|
function resumeThenPrompt() {
|
|
@@ -125,7 +111,7 @@ function resumeThenPrompt() {
|
|
|
125
111
|
return { name: 'synthetic-resume', kind: 'synthetic', text: captureText([
|
|
126
112
|
down({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: 1, clientCapabilities: {} } }),
|
|
127
113
|
up({ jsonrpc: '2.0', id: 1, result: { protocolVersion: 1, agentCapabilities: { loadSession: true, sessionCapabilities: { resume: {} } },
|
|
128
|
-
authMethods: [], agentInfo:
|
|
114
|
+
authMethods: [], agentInfo: { name: '@agentclientprotocol/claude-agent-acp', version: '1.0.0' } } }),
|
|
129
115
|
down({ jsonrpc: '2.0', id: 2, method: 'session/resume', params: { sessionId: S, cwd: '/work/synthetic', mcpServers: [] } }),
|
|
130
116
|
up({ jsonrpc: '2.0', id: 2, result: { configOptions: [{ id: 'model', name: 'Model', type: 'select', currentValue: 'model-0001',
|
|
131
117
|
options: [{ value: 'model-0001', name: 'xxxxx' }, { value: 'model-0002', name: 'xxxxx' }] }] } }),
|
|
@@ -134,116 +120,16 @@ function resumeThenPrompt() {
|
|
|
134
120
|
up({ jsonrpc: '2.0', id: 3, result: { stopReason: 'end_turn' } }),
|
|
135
121
|
]) };
|
|
136
122
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Real Qoder captures (T30a/T30b): `qoder-real-<what>.jsonl`, written by `scripts/acp-capture.mjs` against `qodercli --acp` or the SDK
|
|
139
|
-
* adapter and sanitised by `axom-cli/scripts/acp-sanitize.mjs` (with `--side` when the capture has one). Each is discovered like any
|
|
140
|
-
* real capture, joins every gate that runs on all sources (`processor/rawFixtures.test.ts`, the goldens of
|
|
141
|
-
* `processor/equivalence.test.ts`), and is played as the daemon plays a live Qoder session: the repair note right after the open's
|
|
142
|
-
* response (`session/new`) or right after a load's open note, for the certified runtime only (`nativeTerminalHistoryPath`), and the
|
|
143
|
-
* side file `<name>.side.ndjson` — the root native transcript rows — read through the daemon's `BackgroundSideNotes` after the turn.
|
|
144
|
-
*/
|
|
145
|
-
export const REAL_QODER_PREFIX = 'qoder-real-';
|
|
146
|
-
export const sideFileOf = (fixture) => fixture.replace(/\.jsonl$/, '.side.ndjson');
|
|
147
|
-
/** The discovered real Qoder capture names (without `.jsonl`), for the static source lists of the gates. */
|
|
148
|
-
export const realQoderNames = () => fixtureNames().filter((name) => name.startsWith(REAL_QODER_PREFIX)).map((name) => name.replace(/\.jsonl$/, ''));
|
|
149
|
-
/** The capture's first open (`session/new` / `load` / `resume`), its response and the adapter's `initialize` identity. */
|
|
150
|
-
export function openFacts(capture) {
|
|
151
|
-
const lines = capture.filter((r) => r.kind === 'up' || r.kind === 'down')
|
|
152
|
-
.map((r) => { try {
|
|
153
|
-
return { r, m: JSON.parse(r.line) };
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
return undefined;
|
|
157
|
-
} }).filter((x) => x !== undefined);
|
|
158
|
-
const responseTo = (id) => lines.find(({ r, m }) => r.kind === 'up' && m.method === undefined && JSON.stringify(m.id) === JSON.stringify(id));
|
|
159
|
-
const init = lines.find(({ r, m }) => r.kind === 'down' && m.method === 'initialize');
|
|
160
|
-
const agentInfo = init ? responseTo(init.m.id)?.m.result?.agentInfo : undefined;
|
|
161
|
-
const open = lines.find(({ r, m }) => r.kind === 'down' && ['session/new', 'session/load', 'session/resume'].includes(m.method ?? ''));
|
|
162
|
-
if (!open)
|
|
163
|
-
return undefined;
|
|
164
|
-
const response = responseTo(open.m.id);
|
|
165
|
-
if (!response)
|
|
166
|
-
return undefined;
|
|
167
|
-
const mode = open.m.method.slice('session/'.length);
|
|
168
|
-
const sessionId = String(mode === 'new' ? response.m.result?.sessionId : open.m.params?.sessionId);
|
|
169
|
-
return { mode, responseO: response.r.o, sessionId, cwd: String(open.m.params?.cwd), ...(agentInfo ? { agentInfo } : {}) };
|
|
170
|
-
}
|
|
171
|
-
/** A real Qoder source from a capture's text and its side file's text (if any). */
|
|
172
|
-
export function qoderRealSource(name, text, sideText, path) {
|
|
173
|
-
const facts = openFacts(text.split('\n').filter((line) => line.trim()).map((line) => decodeStreamRecord(JSON.parse(line))));
|
|
174
|
-
return { name, kind: 'qoder-real', text, ...(path ? { path } : {}),
|
|
175
|
-
...(facts ? { sessionId: facts.sessionId, cwd: facts.cwd } : {}),
|
|
176
|
-
...(sideText !== undefined ? { nativeRows: sideText.split('\n').filter((line) => line.trim()).map((line) => JSON.parse(line)) } : {}) };
|
|
177
|
-
}
|
|
178
|
-
function realQoderSource(fixture) {
|
|
179
|
-
const side = join(RAW_DIR, sideFileOf(fixture));
|
|
180
|
-
return qoderRealSource(fixture.replace(/\.jsonl$/, ''), readFixtureText(fixture), existsSync(side) ? readFileSync(side, 'utf8') : undefined, join(RAW_DIR, fixture));
|
|
181
|
-
}
|
|
182
123
|
export async function allSources() {
|
|
183
|
-
const real = fixtureNames().map((name) => (
|
|
124
|
+
const real = fixtureNames().map((name) => ({
|
|
184
125
|
name: name.replace(/\.jsonl$/, ''), kind: 'real', text: readFixtureText(name), path: join(RAW_DIR, name),
|
|
185
126
|
}));
|
|
186
|
-
|
|
187
|
-
return [...real, ...qoder, experiment01(), outOfTurnThenPrompt(), interruptMidTurn(), loadThenPrompt(), resumeThenPrompt()];
|
|
127
|
+
return [...real, experiment01(), outOfTurnThenPrompt(), interruptMidTurn(), loadThenPrompt(), resumeThenPrompt()];
|
|
188
128
|
}
|
|
189
129
|
export function captureRecords(source) {
|
|
190
130
|
return source.text.split('\n').filter((line) => line.trim()).map((line) => decodeStreamRecord(JSON.parse(line)));
|
|
191
131
|
}
|
|
192
|
-
/** Write the Qoder native rows where the certified repair reads them under `home`. */
|
|
193
|
-
export async function writeNativeRows(source, home) {
|
|
194
|
-
const path = nativeTerminalHistoryPath(CERTIFIED_QODER, source.sessionId, source.cwd, home);
|
|
195
|
-
await mkdir(dirname(path), { recursive: true });
|
|
196
|
-
await writeFile(path, `${source.nativeRows.map((row) => JSON.stringify(row)).join('\n')}\n`);
|
|
197
|
-
return path;
|
|
198
|
-
}
|
|
199
|
-
/** The processor raw log for `source` (see the file comment). */
|
|
200
132
|
export async function buildLog(source, options = {}) {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
const repairNote = (nativeSessionId) => [{ kind: 'note', note: { type: 'qoder_terminal_repair', nativeSessionId } }];
|
|
204
|
-
let afterLoadOpen;
|
|
205
|
-
if (source.kind === 'qoder') {
|
|
206
|
-
const openResponse = capture.find((record) => record.kind === 'up' && JSON.parse(record.line).id === 2);
|
|
207
|
-
after.set(openResponse.o, () => repairNote(source.sessionId));
|
|
208
|
-
}
|
|
209
|
-
else if (source.kind === 'qoder-real') {
|
|
210
|
-
// `AcpAgents.startSession`: the repair is enabled only where `nativeTerminalHistoryPath` certifies the runtime.
|
|
211
|
-
const facts = openFacts(capture);
|
|
212
|
-
if (facts && nativeTerminalHistoryPath(facts.agentInfo, facts.sessionId, facts.cwd, '/home/certification')) {
|
|
213
|
-
if (facts.mode === 'load')
|
|
214
|
-
afterLoadOpen = repairNote;
|
|
215
|
-
else
|
|
216
|
-
after.set(facts.responseO, () => repairNote(facts.sessionId));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
const transduced = transduce(capture, { after, ...(afterLoadOpen ? { afterLoadOpen } : {}),
|
|
220
|
-
...(options.launch !== undefined ? { launch: options.launch } : {}) });
|
|
221
|
-
let sideNotes = 0;
|
|
222
|
-
if (source.nativeRows && source.sessionId && source.cwd && after.size + (afterLoadOpen ? 1 : 0) > 0) {
|
|
223
|
-
const home = await mkdtemp(join(tmpdir(), 'axom-raw-side-'));
|
|
224
|
-
try {
|
|
225
|
-
const path = await writeNativeRows(source, home);
|
|
226
|
-
const reader = new BackgroundSideNotes({ path, nativeSessionId: source.sessionId, cwd: source.cwd });
|
|
227
|
-
let next = transduced.session.log.head;
|
|
228
|
-
// T19's loop: ask the processor's current demand, read, append the notes, fold; until a poll
|
|
229
|
-
// brings nothing new (the live repair's 1 s timer, without the timer).
|
|
230
|
-
for (let poll = 0; poll < 8; poll += 1) {
|
|
231
|
-
const demand = sideDemand(transduced.processor.state);
|
|
232
|
-
if (!demand)
|
|
233
|
-
break;
|
|
234
|
-
const notes = await reader.poll(demand);
|
|
235
|
-
for (const note of notes)
|
|
236
|
-
transduced.session.log.append({ kind: 'note', note });
|
|
237
|
-
next = run(transduced.processor, transduced.session, next);
|
|
238
|
-
sideNotes += notes.length;
|
|
239
|
-
if (notes.length === 0)
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
reader.close();
|
|
243
|
-
}
|
|
244
|
-
finally {
|
|
245
|
-
await rm(home, { recursive: true, force: true });
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return { ...transduced, records: transduced.session.log.read(0), source, sideNotes };
|
|
133
|
+
const transduced = transduce(captureRecords(source), options);
|
|
134
|
+
return { ...transduced, records: transduced.session.log.read(0), source, sideNotes: 0 };
|
|
249
135
|
}
|
|
@@ -1,37 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The opt-in real-adapter harness on the processor path (Task 30a). It replaces the real-adapter coverage Task 29 deleted with
|
|
3
|
-
* `AcpSession` (`qoderContract.test.ts`'s `AXOM_ACP_CONTRACT_COMMAND` certification, `providerLifecycleBaseline`,
|
|
4
|
-
* `qoderAdapter/acpHostIntegration`): every case opens through `AcpAgents.open` → `ProcessorSession`, over the local transport and
|
|
5
|
-
* over a real keeper-acp (the built bundle in `dist/keepers`), and reads what the daemon would — the session's frames and its raw log.
|
|
6
|
-
*
|
|
7
|
-
* ## Isolation
|
|
8
|
-
*
|
|
9
|
-
* Every run gets its own temp config dir (keeper-acp's sockets, raw logs and outboxes live there, never `~/.config/axom`), its own
|
|
10
|
-
* temp workspace and its own temp HOME. The adapter is started by a small launcher ({@link writeLauncher}) that:
|
|
11
|
-
* - puts it in a process group of its own (the owned-group cleanup of the deleted certification), and
|
|
12
|
-
* - replaces its environment with a minimal one read from a 0600 file in the run dir: PATH, the temp HOME, temp `CLAUDE_CONFIG_DIR`,
|
|
13
|
-
* `CODEX_HOME`, `XDG_CONFIG_HOME` (all created), the build's own launch env, and only the variables named in
|
|
14
|
-
* `AXOM_REAL_ADAPTER_ENV_PASS`. Nothing else of this process's environment (API keys, `CLAUDE_CODE_*`, tokens) reaches the
|
|
15
|
-
* adapter, and the environment is never in the spawn's argv (keeper-acp records argv in its label).
|
|
16
|
-
*
|
|
17
|
-
* A case that needs a logged-in adapter uses `AXOM_REAL_ADAPTER_AUTH_HOME` as HOME instead of a temp one: a directory the operator
|
|
18
|
-
* prepared for this purpose (e.g. `HOME=<dir> qodercli` once, then log in). The harness never defaults to the real HOME.
|
|
19
|
-
*
|
|
20
|
-
* ## Flags (all off: `npm test` runs only the hermetic cases and reports the rest as skipped, with the reason)
|
|
21
|
-
*
|
|
22
|
-
* | variable | effect |
|
|
23
|
-
* |---|---|
|
|
24
|
-
* | `AXOM_REAL_ADAPTER_HARNESS=1` | enables the real-adapter tiers at all |
|
|
25
|
-
* | `AXOM_REAL_ADAPTER_AGENTS=claude-acp,codex-acp` | registry adapters for the no-prompt tier, installed into the temp config dir |
|
|
26
|
-
* | `AXOM_ACP_CONTRACT_COMMAND` / `AXOM_ACP_CONTRACT_ARGS` | an adapter command (and a JSON argv, default `["--acp"]`): the no-prompt tier, and the Qoder certification's adapter |
|
|
27
|
-
* | `AXOM_REAL_ADAPTER_PROMPTS=1` | also runs the cases that send prompts (model usage, see each case's cost) |
|
|
28
|
-
* | `AXOM_REAL_ADAPTER_AUTH_HOME` | HOME for the prompt cases (required by them) |
|
|
29
|
-
* | `AXOM_REAL_ADAPTER_ENV_PASS=A,B` | extra variables passed through to the adapter (token-based auth) |
|
|
30
|
-
* | `AXOM_REAL_ADAPTER_TRANSPORTS=local,keeper` | which transports to run on (default both) |
|
|
31
|
-
* | `AXOM_QODER_SDK_QODERCLI=<path>` | the real SDK adapter tier: `node dist/cli.js qoder-acp --qodercli <path>` (prompts) |
|
|
32
|
-
*/
|
|
33
|
-
import { spawn } from 'node:child_process';
|
|
34
|
-
import { existsSync } from 'node:fs';
|
|
35
1
|
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
36
2
|
import { tmpdir } from 'node:os';
|
|
37
3
|
import path, { join } from 'node:path';
|
|
@@ -68,7 +34,6 @@ export function realAdapterConfig(env = process.env) {
|
|
|
68
34
|
...(env.AXOM_REAL_ADAPTER_AUTH_HOME ? { authHome: env.AXOM_REAL_ADAPTER_AUTH_HOME } : {}),
|
|
69
35
|
envPass: list(env.AXOM_REAL_ADAPTER_ENV_PASS),
|
|
70
36
|
transports: transports.length > 0 ? transports : ['local', 'keeper'],
|
|
71
|
-
...(env.AXOM_QODER_SDK_QODERCLI ? { sdkQodercli: env.AXOM_QODER_SDK_QODERCLI } : {}),
|
|
72
37
|
};
|
|
73
38
|
}
|
|
74
39
|
/** Why a tier does not run (undefined: it runs). Shown as the skipped test's name. */
|
|
@@ -84,7 +49,7 @@ export function skipReason(config, tier) {
|
|
|
84
49
|
return 'needs a logged-in adapter: set AXOM_REAL_ADAPTER_AUTH_HOME';
|
|
85
50
|
if (tier === 'qoder')
|
|
86
51
|
return config.command ? undefined : 'set AXOM_ACP_CONTRACT_COMMAND to qodercli';
|
|
87
|
-
return
|
|
52
|
+
return undefined;
|
|
88
53
|
}
|
|
89
54
|
/**
|
|
90
55
|
* A provider history failure fails certification but never prevents local teardown; each local step is guarded on its own, so one
|
|
@@ -364,14 +329,3 @@ export async function historyOf(opened) {
|
|
|
364
329
|
history.push(event);
|
|
365
330
|
return history;
|
|
366
331
|
}
|
|
367
|
-
/** `node dist/cli.js` (the built CLI), for the real SDK adapter (`qoder-acp --qodercli`). */
|
|
368
|
-
export const BUILT_CLI = join(PACKAGE_ROOT, 'dist', 'cli.js');
|
|
369
|
-
export const builtCliExists = () => existsSync(BUILT_CLI);
|
|
370
|
-
/** Runs `node dist/cli.js qoder-acp --prepare-sdk` once (network: the SDK runtime) under `env`. */
|
|
371
|
-
export async function prepareSdkRuntime(env) {
|
|
372
|
-
await new Promise((resolve, reject) => {
|
|
373
|
-
const child = spawn(process.execPath, [BUILT_CLI, 'qoder-acp', '--prepare-sdk'], { env, stdio: ['ignore', 'inherit', 'inherit'] });
|
|
374
|
-
child.once('error', reject);
|
|
375
|
-
child.once('exit', (code) => (code === 0 ? resolve() : reject(new Error(`qoder-acp --prepare-sdk exited ${code}`))));
|
|
376
|
-
});
|
|
377
|
-
}
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
// stderrMcpHeaders?: boolean on session/new and session/load: every MCP header value received, on stderr (raw and JSON)
|
|
20
20
|
// version?: string initialize's agentInfo.version (default '1.0.0') (Task 24)
|
|
21
21
|
// noLoad?: boolean initialize does not advertise loadSession
|
|
22
|
+
// failInitializeOn?: string initialize answers an error once this marker exists
|
|
22
23
|
// failLoad?: boolean session/load answers an error
|
|
24
|
+
// failLoadOn?: string session/load answers an error once this marker exists
|
|
23
25
|
// loadWaitFor?: string session/load waits for this file before it answers
|
|
24
26
|
// historyOnLoad?: string[] agent_message_chunk texts replayed (as history) before session/load answers
|
|
25
27
|
// configOptions?: object[] session/new and session/load answer these configOptions (load: as configured — the agent keeps
|
|
@@ -118,8 +120,14 @@ function onLine(line) {
|
|
|
118
120
|
}
|
|
119
121
|
switch (message.method) {
|
|
120
122
|
case 'initialize': {
|
|
121
|
-
const answer = () =>
|
|
122
|
-
|
|
123
|
+
const answer = () => {
|
|
124
|
+
if (config.failInitializeOn && existsSync(at(config.failInitializeOn))) {
|
|
125
|
+
send({ id: message.id, error: { code: -32002, message: 'Initialize refused: old build unavailable' } });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
send({ id: message.id, result: { protocolVersion: 1, agentCapabilities: { loadSession: !config.noLoad, ...(config.mcpHttp ? { mcpCapabilities: { http: true } } : {}) }, authMethods: [],
|
|
129
|
+
agentInfo: { name: 'script-agent', version: config.version ?? '1.0.0' } } });
|
|
130
|
+
};
|
|
123
131
|
if (config.initializeWaitFor)
|
|
124
132
|
void waitForFile(config.initializeWaitFor).then(answer);
|
|
125
133
|
else
|
|
@@ -135,7 +143,7 @@ function onLine(line) {
|
|
|
135
143
|
void (async () => {
|
|
136
144
|
if (config.loadWaitFor)
|
|
137
145
|
await waitForFile(config.loadWaitFor);
|
|
138
|
-
if (config.failLoad) {
|
|
146
|
+
if (config.failLoad || (config.failLoadOn && existsSync(at(config.failLoadOn)))) {
|
|
139
147
|
send({ id: message.id, error: { code: -32002, message: 'Resource not found: session' } });
|
|
140
148
|
return;
|
|
141
149
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* window could ever open for them;
|
|
22
22
|
* - **control frames** bypass both and are written at once: RPC replies (`agent_opened`, `agent_steered`,
|
|
23
23
|
* `agent_config_option_set`, `agent_native_sessions`, `agent_native_deleted`, `agent_verified`, `agent_authenticated`,
|
|
24
|
-
* `agent_installed`, `
|
|
24
|
+
* `agent_installed`, `agent_provider_task_stopped`, `agent_backfill_done`), levels
|
|
25
25
|
* (`agent_session_state`, `agent_runtime_profile`, `agent_provider_task_event`), `agent_session_lost`, `agent_resume_offer`,
|
|
26
26
|
* `agent_auth_url`/`agent_auth_done`, and the channel's own heartbeat. They are small and rare, an answer the server is waiting
|
|
27
27
|
* for (a 10–120 s timeout) must not queue behind a session's backlog, and an answer to a user (a permission card's state) must
|