@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.
Files changed (258) hide show
  1. package/README.md +25 -6
  2. package/dist/axom-patch/cli/README.md +129 -12
  3. package/dist/axom-patch/cli/account-limits/generate.py +15 -12
  4. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
  5. package/dist/axom-patch/cli/claude-account-limits.json +9 -9
  6. package/dist/axom-patch/cli/claude-replay-lifecycle.json +16 -0
  7. package/dist/axom-patch/cli/codex-account-limits.json +11 -11
  8. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  9. package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
  10. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  11. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  12. package/dist/axom-patch/cli/nightly-task.md +55 -13
  13. package/dist/axom-patch/cli/probes/claude-replay-lifecycle.mjs +31 -0
  14. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  15. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  16. package/dist/axom-patch/cli/probes/zcode-env-account.mjs +81 -0
  17. package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +7 -0
  18. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1687 -0
  19. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +277 -0
  20. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
  21. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
  22. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  23. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  24. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  25. package/dist/axom-patch/cli/runtime.json +19 -19
  26. package/dist/axom-patch/cli/zcode/env-account.mjs +13 -0
  27. package/dist/axom-patch/cli/zcode/generate-env-account.py +52 -0
  28. package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
  29. package/dist/axom-patch/cli/zcode-env-account.json +58 -0
  30. package/dist/axom-patch/cli/zcode-models.json +9 -10
  31. package/dist/axom-patch/cli/zcode-subagent-subwork.json +10 -11
  32. package/dist/cli.js +11 -45
  33. package/dist/commands/auth.js +4 -1
  34. package/dist/commands/knowledge.js +47 -0
  35. package/dist/commands/update.js +126 -3
  36. package/dist/commands/workstation.js +101 -164
  37. package/dist/commands/workstationRegistrations.js +116 -0
  38. package/dist/credentialBroker.js +13 -2
  39. package/dist/credentials.js +16 -0
  40. package/dist/install/claude/commands.js +15 -15
  41. package/dist/keepers/terminal/keeper.mjs +1089 -673
  42. package/dist/keepers/terminal/manifest.json +4 -4
  43. package/dist/mcp.js +1 -1
  44. package/dist/native/darwin-x64/axom-quic +0 -0
  45. package/dist/workstation/acp/acpAgents.js +113 -102
  46. package/dist/workstation/acp/acpClient.js +26 -0
  47. package/dist/workstation/acp/builds/install/npm.js +99 -10
  48. package/dist/workstation/acp/initializeResponse.js +6 -1
  49. package/dist/workstation/acp/managedPatches.js +189 -25
  50. package/dist/workstation/acp/managedRuntime.js +314 -0
  51. package/dist/workstation/acp/outbox.js +12 -5
  52. package/dist/workstation/acp/patchAssets.js +40 -0
  53. package/dist/workstation/acp/permission.js +2 -2
  54. package/dist/workstation/acp/processor/checkpoint.js +26 -34
  55. package/dist/workstation/acp/processor/control.js +7 -80
  56. package/dist/workstation/acp/processor/jsonrpc.js +8 -0
  57. package/dist/workstation/acp/processor/runtime.js +5 -81
  58. package/dist/workstation/acp/processor/session.js +54 -82
  59. package/dist/workstation/acp/processor/state.js +7 -13
  60. package/dist/workstation/acp/processor/step.js +3 -20
  61. package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
  62. package/dist/workstation/acp/processor/transport/local.js +19 -3
  63. package/dist/workstation/acp/processor/updates.js +156 -105
  64. package/dist/workstation/acp/processorSession.js +20 -173
  65. package/dist/workstation/acp/registry/catalog.js +65 -39
  66. package/dist/workstation/acp/registry/launch.js +16 -1
  67. package/dist/workstation/acp/switching.js +0 -3
  68. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  69. package/dist/workstation/acp/test/keeperWorld.js +32 -3
  70. package/dist/workstation/acp/test/raw/sources.js +16 -130
  71. package/dist/workstation/acp/test/realAdapter.js +1 -47
  72. package/dist/workstation/acp/test/scriptAgent.js +11 -3
  73. package/dist/workstation/agentFlow.js +1 -1
  74. package/dist/workstation/agentSessions.js +114 -166
  75. package/dist/workstation/channels/agent.js +9 -1
  76. package/dist/workstation/channels/computer.js +27 -4
  77. package/dist/workstation/channels/management.js +130 -15
  78. package/dist/workstation/channels/url.js +1 -1
  79. package/dist/workstation/config.js +33 -9
  80. package/dist/workstation/connection/encryptedChannel.js +4 -1
  81. package/dist/workstation/control/bindingStore.js +204 -0
  82. package/dist/workstation/control/commands.js +125 -0
  83. package/dist/workstation/control/daemonIdentity.js +143 -0
  84. package/dist/workstation/control/deviceCommands.js +191 -0
  85. package/dist/workstation/control/deviceProtocol.js +68 -0
  86. package/dist/workstation/control/enrollment.js +106 -0
  87. package/dist/workstation/control/inspect.js +71 -0
  88. package/dist/workstation/control/locks.js +110 -0
  89. package/dist/workstation/control/protocol.js +40 -0
  90. package/dist/workstation/control/restartGuard.js +46 -0
  91. package/dist/workstation/control/rootFormat.js +16 -0
  92. package/dist/workstation/control/runtimeClient.js +152 -0
  93. package/dist/workstation/control/runtimeHost.js +235 -0
  94. package/dist/workstation/control/serverIdentity.js +35 -0
  95. package/dist/workstation/control/updateTransition.js +91 -0
  96. package/dist/workstation/daemon.js +266 -264
  97. package/dist/workstation/daemonctl.js +226 -25
  98. package/dist/workstation/desktopControl/audio.js +99 -0
  99. package/dist/workstation/desktopControl/automationDevice.js +219 -0
  100. package/dist/workstation/desktopControl/automationJson.js +74 -0
  101. package/dist/workstation/desktopControl/automationLocalState.js +23 -0
  102. package/dist/workstation/desktopControl/automationProtocol.js +253 -0
  103. package/dist/workstation/desktopControl/automationSchema.generated.js +1 -0
  104. package/dist/workstation/desktopControl/automationSession.js +81 -0
  105. package/dist/workstation/desktopControl/automationTransport.js +138 -0
  106. package/dist/workstation/desktopControl/capabilities.js +93 -0
  107. package/dist/workstation/desktopControl/captureModes.js +9 -0
  108. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  109. package/dist/workstation/desktopControl/deviceAdmission.js +229 -0
  110. package/dist/workstation/desktopControl/deviceOwnership.js +130 -0
  111. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  112. package/dist/workstation/desktopControl/helperProcess.js +602 -0
  113. package/dist/workstation/desktopControl/hostGrant.js +65 -0
  114. package/dist/workstation/desktopControl/lobby.js +131 -0
  115. package/dist/workstation/desktopControl/macProvider.js +156 -0
  116. package/dist/workstation/desktopControl/media.js +331 -0
  117. package/dist/workstation/desktopControl/nativeDeviceAdmission.js +207 -0
  118. package/dist/workstation/desktopControl/nativeProvider.js +1044 -0
  119. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  120. package/dist/workstation/desktopControl/overview.js +58 -0
  121. package/dist/workstation/desktopControl/ownershipProtocol.js +31 -0
  122. package/dist/workstation/desktopControl/protocol.js +311 -0
  123. package/dist/workstation/desktopControl/provider.js +1 -0
  124. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  125. package/dist/workstation/desktopControl/rateController.js +226 -0
  126. package/dist/workstation/desktopControl/supervisor.js +1774 -0
  127. package/dist/workstation/desktopControl/transport.js +27 -0
  128. package/dist/workstation/desktopControl/wallpaper.js +98 -0
  129. package/dist/workstation/exec/policy.js +5 -0
  130. package/dist/workstation/files/client.js +10 -1
  131. package/dist/workstation/files/credentials.js +142 -0
  132. package/dist/workstation/files/host.js +9 -2
  133. package/dist/workstation/files/text.js +42 -8
  134. package/dist/workstation/journal.js +5 -2
  135. package/dist/workstation/keepers/acp/client.js +38 -2
  136. package/dist/workstation/keepers/kit/buildVersion.js +35 -11
  137. package/dist/workstation/keepers/kit/bundle.js +21 -17
  138. package/dist/workstation/keepers/kit/client.js +2 -2
  139. package/dist/workstation/keepers/kit/launcher.js +123 -7
  140. package/dist/workstation/keepers/kit/paths.js +2 -1
  141. package/dist/workstation/keepers/legacy.js +16 -2
  142. package/dist/workstation/keepers/policy.js +5 -34
  143. package/dist/workstation/keepers/terminal/client.js +115 -23
  144. package/dist/workstation/keepers/terminal/exec.js +6 -3
  145. package/dist/workstation/keepers/terminal/frames.js +73 -0
  146. package/dist/workstation/keepers/terminal/generations.js +5 -8
  147. package/dist/workstation/keepers/terminal/gridProtocol.js +16 -0
  148. package/dist/workstation/keepers/terminal/input.js +146 -0
  149. package/dist/workstation/keepers/terminal/keeper.js +22 -8
  150. package/dist/workstation/keepers/terminal/main.js +1 -1
  151. package/dist/workstation/keepers/terminal/metadata.js +138 -0
  152. package/dist/workstation/keepers/terminal/protocol.js +4 -28
  153. package/dist/workstation/keepers/terminal/pty.js +1 -330
  154. package/dist/workstation/keepers/terminal/screen.js +246 -0
  155. package/dist/workstation/keepers/terminal/sessions.js +526 -0
  156. package/dist/workstation/managedInstall.js +57 -0
  157. package/dist/workstation/oauth/deviceAuthorization.js +31 -17
  158. package/dist/workstation/protocol/agent.js +2 -15
  159. package/dist/workstation/protocol/management.js +113 -5
  160. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  161. package/dist/workstation/rd/artifact.js +120 -0
  162. package/dist/workstation/rd/control.js +117 -0
  163. package/dist/workstation/rd/display.js +29 -0
  164. package/dist/workstation/rd/host.js +213 -0
  165. package/dist/workstation/rd/migrationReadiness.js +46 -0
  166. package/dist/workstation/rd/provider.js +256 -0
  167. package/dist/workstation/rd/sessions.js +391 -0
  168. package/dist/workstation/rd/state.js +57 -0
  169. package/dist/workstation/rd/update.js +168 -0
  170. package/dist/workstation/rd/upkeep.js +80 -0
  171. package/dist/workstation/register.js +27 -37
  172. package/dist/workstation/registrations/enrollment.js +250 -0
  173. package/dist/workstation/registrations/enrollmentPending.js +361 -0
  174. package/dist/workstation/registrations/migration.js +356 -0
  175. package/dist/workstation/registrations/model.js +34 -0
  176. package/dist/workstation/registrations/removal.js +222 -0
  177. package/dist/workstation/registrations/store.js +737 -0
  178. package/dist/workstation/runtime/desktopBinding.js +20 -0
  179. package/dist/workstation/runtime/deviceDesktop.js +193 -0
  180. package/dist/workstation/runtime/deviceRuntime.js +568 -0
  181. package/dist/workstation/runtime/deviceServices.js +117 -0
  182. package/dist/workstation/runtime/events.js +20 -0
  183. package/dist/workstation/runtime/ownership.js +100 -0
  184. package/dist/workstation/runtime/registrationRuntime.js +575 -0
  185. package/dist/workstation/runtime/remoteDesktopAdapter.js +186 -0
  186. package/dist/workstation/runtime/removalActivity.js +184 -0
  187. package/dist/workstation/service/installer.js +197 -14
  188. package/dist/workstation/service/launchd.js +2 -0
  189. package/dist/workstation/service/systemd.js +1 -0
  190. package/dist/workstation/storageMetrics.js +118 -0
  191. package/dist/workstation/systemMetrics.js +47 -92
  192. package/dist/workstation/toolchain.js +94 -0
  193. package/dist/workstation/tools/browser.js +28 -4
  194. package/dist/workstation/tools/client.js +8 -27
  195. package/dist/workstation/tools/info.js +6 -13
  196. package/dist/workstation/tools/terminalRouter.js +17 -172
  197. package/dist/workstation/tools/validation.js +55 -1
  198. package/dist/workstation/update/archiveEvidence.js +206 -0
  199. package/dist/workstation/update/artifact.js +2 -1
  200. package/dist/workstation/update/coordinator.js +34 -13
  201. package/dist/workstation/update/deviceCompatibility.js +119 -0
  202. package/dist/workstation/update/deviceCoordinator.js +369 -0
  203. package/dist/workstation/update/deviceHealth.js +104 -0
  204. package/dist/workstation/update/deviceHealthProtocol.js +26 -0
  205. package/dist/workstation/update/deviceRecoveryLifecycle.js +46 -0
  206. package/dist/workstation/update/deviceRollback.js +78 -0
  207. package/dist/workstation/update/globalInstall.js +186 -0
  208. package/dist/workstation/update/legacyBridge.js +414 -0
  209. package/dist/workstation/update/legacyOwner.js +39 -0
  210. package/dist/workstation/update/npmInstaller.js +51 -25
  211. package/dist/workstation/update/privateDurability.js +61 -0
  212. package/dist/workstation/update/rdRollback.js +145 -0
  213. package/dist/workstation/update/recoveryPlan.js +124 -0
  214. package/dist/workstation/update/runUpdate.js +10 -3
  215. package/dist/workstation/update/taskRegistry.js +161 -3
  216. package/dist/workstation/update/watchdog.js +29806 -333
  217. package/dist/workstation/update/watchdogProcess.js +72 -0
  218. package/package.json +11 -7
  219. package/skills/axom/SKILL.md +11 -11
  220. package/skills/axom-definition/SKILL.md +3 -3
  221. package/skills/axom-platform/SKILL.md +3 -3
  222. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  223. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
  224. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  225. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  226. package/dist/commands/guide.js +0 -38
  227. package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
  228. package/dist/runtime-deps/qoder-sdk/package.json +0 -8
  229. package/dist/workstation/acp/delegationMcp.js +0 -24
  230. package/dist/workstation/acp/processor/sideChannel.js +0 -80
  231. package/dist/workstation/acp/qoderStartup.js +0 -45
  232. package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
  233. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
  234. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
  235. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
  236. package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
  237. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  238. package/dist/workstation/qoderAdapter/contract.js +0 -78
  239. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
  240. package/dist/workstation/qoderAdapter/history.js +0 -85
  241. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  242. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  243. package/dist/workstation/qoderAdapter/main.js +0 -95
  244. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  245. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  246. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  247. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  248. package/dist/workstation/qoderAdapter/profile.js +0 -93
  249. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
  250. package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
  251. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
  252. package/dist/workstation/qoderAdapter/session.js +0 -319
  253. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  254. package/dist/workstation/qoderAdapter/subwork.js +0 -85
  255. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  256. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  257. package/dist/workstation/qoderAdapter/usage.js +0 -37
  258. /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 local = new LocalTransport({ configDir: this.configDir });
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(this.configDir, 'agents'), tasks);
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
- import { captureText, CERTIFIED_QODER, down, qoderCaptures, up, update } from './qoderCaptures.js';
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 Qoder cached `session/resume` (the certified runtime advertising
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: CERTIFIED_QODER } }),
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) => (name.startsWith(REAL_QODER_PREFIX) ? realQoderSource(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
- const qoder = (await qoderCaptures()).map((capture) => ({ ...capture, kind: 'qoder' }));
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 capture = captureRecords(source);
202
- const after = new Map();
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 config.sdkQodercli ? undefined : 'set AXOM_QODER_SDK_QODERCLI to qodercli';
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 = () => send({ id: message.id, result: { protocolVersion: 1, agentCapabilities: { loadSession: !config.noLoad, ...(config.mcpHttp ? { mcpCapabilities: { http: true } } : {}) }, authMethods: [],
122
- agentInfo: { name: 'script-agent', version: config.version ?? '1.0.0' } } });
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`, `agent_delegation_renewed`, `agent_provider_task_stopped`, `agent_backfill_done`), levels
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