@platless/axom-cli 0.5.9 → 0.6.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +18 -5
  2. package/dist/axom-patch/cli/README.md +100 -12
  3. package/dist/axom-patch/cli/account-limits/generate.py +15 -12
  4. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
  5. package/dist/axom-patch/cli/claude-account-limits.json +9 -9
  6. package/dist/axom-patch/cli/codex-account-limits.json +11 -11
  7. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  8. package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
  9. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  10. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  11. package/dist/axom-patch/cli/nightly-task.md +55 -13
  12. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  13. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  14. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
  15. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
  16. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
  17. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
  18. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  19. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  20. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  21. package/dist/axom-patch/cli/runtime.json +16 -18
  22. package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
  23. package/dist/axom-patch/cli/zcode-models.json +1 -2
  24. package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
  25. package/dist/cli.js +4 -36
  26. package/dist/commands/knowledge.js +47 -0
  27. package/dist/commands/update.js +65 -3
  28. package/dist/commands/workstation.js +73 -7
  29. package/dist/install/claude/commands.js +13 -13
  30. package/dist/mcp.js +1 -1
  31. package/dist/native/darwin-arm64/axom-quic +0 -0
  32. package/dist/workstation/acp/acpAgents.js +102 -98
  33. package/dist/workstation/acp/acpClient.js +26 -0
  34. package/dist/workstation/acp/builds/install/npm.js +99 -10
  35. package/dist/workstation/acp/initializeResponse.js +6 -1
  36. package/dist/workstation/acp/managedPatches.js +189 -25
  37. package/dist/workstation/acp/managedRuntime.js +314 -0
  38. package/dist/workstation/acp/patchAssets.js +40 -0
  39. package/dist/workstation/acp/processor/checkpoint.js +25 -34
  40. package/dist/workstation/acp/processor/control.js +5 -78
  41. package/dist/workstation/acp/processor/runtime.js +5 -81
  42. package/dist/workstation/acp/processor/session.js +51 -80
  43. package/dist/workstation/acp/processor/state.js +6 -13
  44. package/dist/workstation/acp/processor/step.js +3 -20
  45. package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
  46. package/dist/workstation/acp/processor/updates.js +131 -105
  47. package/dist/workstation/acp/processorSession.js +8 -172
  48. package/dist/workstation/acp/registry/catalog.js +65 -39
  49. package/dist/workstation/acp/registry/launch.js +16 -1
  50. package/dist/workstation/acp/switching.js +0 -3
  51. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  52. package/dist/workstation/acp/test/keeperWorld.js +28 -0
  53. package/dist/workstation/acp/test/raw/sources.js +16 -130
  54. package/dist/workstation/acp/test/realAdapter.js +1 -47
  55. package/dist/workstation/acp/test/scriptAgent.js +11 -3
  56. package/dist/workstation/agentFlow.js +1 -1
  57. package/dist/workstation/agentSessions.js +38 -143
  58. package/dist/workstation/channels/management.js +95 -11
  59. package/dist/workstation/channels/url.js +1 -1
  60. package/dist/workstation/config.js +16 -4
  61. package/dist/workstation/connection/encryptedChannel.js +4 -1
  62. package/dist/workstation/control/bindingStore.js +200 -0
  63. package/dist/workstation/control/commands.js +124 -0
  64. package/dist/workstation/control/daemonIdentity.js +53 -0
  65. package/dist/workstation/control/enrollment.js +106 -0
  66. package/dist/workstation/control/inspect.js +71 -0
  67. package/dist/workstation/control/locks.js +63 -0
  68. package/dist/workstation/control/protocol.js +40 -0
  69. package/dist/workstation/control/restartGuard.js +46 -0
  70. package/dist/workstation/control/runtimeClient.js +97 -0
  71. package/dist/workstation/control/runtimeHost.js +148 -0
  72. package/dist/workstation/control/serverIdentity.js +35 -0
  73. package/dist/workstation/daemon.js +106 -6
  74. package/dist/workstation/daemonctl.js +77 -14
  75. package/dist/workstation/desktopControl/audio.js +99 -0
  76. package/dist/workstation/desktopControl/capabilities.js +92 -0
  77. package/dist/workstation/desktopControl/captureModes.js +9 -0
  78. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  79. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  80. package/dist/workstation/desktopControl/helperProcess.js +353 -0
  81. package/dist/workstation/desktopControl/lobby.js +129 -0
  82. package/dist/workstation/desktopControl/macProvider.js +155 -0
  83. package/dist/workstation/desktopControl/media.js +331 -0
  84. package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
  85. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  86. package/dist/workstation/desktopControl/overview.js +58 -0
  87. package/dist/workstation/desktopControl/protocol.js +302 -0
  88. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  89. package/dist/workstation/desktopControl/rateController.js +226 -0
  90. package/dist/workstation/desktopControl/supervisor.js +1681 -0
  91. package/dist/workstation/desktopControl/transport.js +27 -0
  92. package/dist/workstation/desktopControl/wallpaper.js +78 -0
  93. package/dist/workstation/files/credentials.js +142 -0
  94. package/dist/workstation/files/text.js +42 -8
  95. package/dist/workstation/keepers/kit/bundle.js +21 -17
  96. package/dist/workstation/keepers/kit/launcher.js +47 -7
  97. package/dist/workstation/keepers/kit/paths.js +2 -1
  98. package/dist/workstation/managedInstall.js +57 -0
  99. package/dist/workstation/protocol/agent.js +2 -15
  100. package/dist/workstation/protocol/management.js +93 -4
  101. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  102. package/dist/workstation/rd/artifact.js +119 -0
  103. package/dist/workstation/rd/control.js +119 -0
  104. package/dist/workstation/rd/display.js +29 -0
  105. package/dist/workstation/rd/provider.js +222 -0
  106. package/dist/workstation/rd/sessions.js +391 -0
  107. package/dist/workstation/rd/state.js +45 -0
  108. package/dist/workstation/rd/update.js +171 -0
  109. package/dist/workstation/rd/upkeep.js +38 -0
  110. package/dist/workstation/register.js +27 -37
  111. package/dist/workstation/service/installer.js +104 -13
  112. package/dist/workstation/service/launchd.js +1 -0
  113. package/dist/workstation/storageMetrics.js +118 -0
  114. package/dist/workstation/systemMetrics.js +44 -92
  115. package/dist/workstation/toolchain.js +94 -0
  116. package/dist/workstation/tools/browser.js +28 -4
  117. package/dist/workstation/update/artifact.js +2 -1
  118. package/dist/workstation/update/globalInstall.js +116 -0
  119. package/dist/workstation/update/npmInstaller.js +49 -23
  120. package/dist/workstation/update/rdRollback.js +145 -0
  121. package/dist/workstation/update/runUpdate.js +3 -2
  122. package/dist/workstation/update/watchdog.js +16790 -334
  123. package/package.json +5 -5
  124. package/skills/axom/SKILL.md +11 -11
  125. package/skills/axom-definition/SKILL.md +3 -3
  126. package/skills/axom-platform/SKILL.md +3 -3
  127. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  128. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
  129. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  130. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  131. package/dist/commands/guide.js +0 -38
  132. package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
  133. package/dist/runtime-deps/qoder-sdk/package.json +0 -8
  134. package/dist/workstation/acp/delegationMcp.js +0 -24
  135. package/dist/workstation/acp/processor/sideChannel.js +0 -80
  136. package/dist/workstation/acp/qoderStartup.js +0 -45
  137. package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
  138. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
  139. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
  140. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
  141. package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
  142. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  143. package/dist/workstation/qoderAdapter/contract.js +0 -78
  144. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
  145. package/dist/workstation/qoderAdapter/history.js +0 -85
  146. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  147. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  148. package/dist/workstation/qoderAdapter/main.js +0 -95
  149. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  150. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  151. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  152. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  153. package/dist/workstation/qoderAdapter/profile.js +0 -93
  154. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
  155. package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
  156. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
  157. package/dist/workstation/qoderAdapter/session.js +0 -319
  158. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  159. package/dist/workstation/qoderAdapter/subwork.js +0 -85
  160. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  161. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  162. package/dist/workstation/qoderAdapter/usage.js +0 -37
  163. /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
@@ -0,0 +1,314 @@
1
+ /** Bundled release certification is the trust root; writable receipts never authorize a runtime. */
2
+ import { spawn } from 'node:child_process';
3
+ import { constants } from 'node:fs';
4
+ import { lstat, mkdtemp, open, readFile, readdir, realpath, rm } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import path from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import semver from 'semver';
9
+ import { inside, assertSafeAgentId, assertSafePackageName } from './builds/model.js';
10
+ import { nodeVersionOf } from './builds/nodeGate.js';
11
+ import { expectedPatchedFiles, patchDigest, verifyPatchedFiles } from './managedPatches.js';
12
+ import { managedRuntimePolicies, packagePatches, patchAssetRoot, validatePackagePatch, validatePatchPath } from './patchAssets.js';
13
+ export const REQUIRED_RUNTIME_CHECKS = ['initialize', 'ordinary-input', 'permissions', 'images-settings', 'parallel-subagents', 'background-reconnect', 'next-append', 'next-button', 'stop', 'cached-resume', 'legacy-migration', 'explicit-load', 'replay', 'install-gates', 'platform'];
14
+ const hashPattern = /^[a-f0-9]{64}$/;
15
+ const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
16
+ function requireThat(condition, message) {
17
+ if (!condition)
18
+ throw new Error(`Managed runtime release: ${message}`);
19
+ }
20
+ function exactVersion(value) { return typeof value === 'string' && semver.valid(value) === value && semver.prerelease(value) === null; }
21
+ export function managedRuntimeFor(agent, policies = managedRuntimePolicies) {
22
+ const found = policies.filter(policy => policy.agent === agent);
23
+ requireThat(found.length <= 1, 'duplicate agent policy');
24
+ return found[0];
25
+ }
26
+ export function assertRuntimeRequest(policy, name, version) {
27
+ assertSafeAgentId(policy.agent);
28
+ assertSafePackageName(policy.package);
29
+ validatePatchPath(policy.release);
30
+ requireThat(exactVersion(policy.version) && policy.ignore_scripts === true, 'invalid exact version or script policy');
31
+ requireThat(Array.isArray(policy.required_patches) && policy.required_patches.length > 0 && policy.required_patches.every(id => typeof id === 'string' && id.length > 0) && new Set(policy.required_patches).size === policy.required_patches.length, 'invalid required patches');
32
+ requireThat(name === policy.package && version === policy.version, `only certified ${policy.package}@${policy.version} may be installed`);
33
+ }
34
+ /**
35
+ * Reject aliases in every component; use nonblocking nofollow opens so a FIFO cannot hang
36
+ * verification. Root-owned system symlinks (`/var -> /private/var` on macOS, in front of every
37
+ * per-user tmpdir) are crossed, not refused: a root-owned link to a root-owned, non-group-writable
38
+ * directory is not plantable by the running user. Every user-owned symlink is still refused.
39
+ */
40
+ async function ownedFile(filename) {
41
+ const full = path.resolve(filename);
42
+ let current = path.parse(full).root;
43
+ const systemSymlink = async (uid) => {
44
+ if (uid !== 0 || process.getuid?.() === 0)
45
+ return false;
46
+ try {
47
+ const target = await realpath(current);
48
+ const stat = await lstat(target);
49
+ return stat.isDirectory() && stat.uid === 0 && (stat.mode & 0o022) === 0;
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ };
55
+ for (const part of full.slice(current.length).split(path.sep).filter(Boolean)) {
56
+ current = path.join(current, part);
57
+ const info = await lstat(current);
58
+ requireThat(!info.isSymbolicLink() || (await systemSymlink(info.uid)), 'symlink in runtime path');
59
+ }
60
+ const file = await open(full, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
61
+ if (!(await file.stat()).isFile()) {
62
+ await file.close();
63
+ throw new Error('Managed runtime requires a regular file');
64
+ }
65
+ return file;
66
+ }
67
+ async function readReleaseAsset(relative, assets) {
68
+ validatePatchPath(relative);
69
+ const base = assets.assetRoot ?? patchAssetRoot;
70
+ const handle = await ownedFile(inside(base instanceof URL ? fileURLToPath(base) : base, relative));
71
+ try {
72
+ requireThat((await handle.stat()).size <= 8 * 1024 * 1024, 'manifest too large');
73
+ return JSON.parse(await handle.readFile('utf8'));
74
+ }
75
+ finally {
76
+ await handle.close();
77
+ }
78
+ }
79
+ export async function loadRuntimeRelease(policy, assets = {}) {
80
+ assertRuntimeRequest(policy, policy.package, policy.version);
81
+ return validateRuntimeRelease(await readReleaseAsset(policy.release, assets), policy, assets);
82
+ }
83
+ function validateRuntimeRelease(r, policy, assets, retained = false) {
84
+ assertRuntimeRequest(policy, policy.package, policy.version);
85
+ requireThat(r?.schema === 1 && typeof r.id === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(r.id), 'invalid schema/id');
86
+ requireThat(r.package === policy.package && r.version === policy.version, 'policy/package/version mismatch');
87
+ requireThat(typeof r.integrity === 'string' && /^sha512-[A-Za-z0-9+/]{86}==$/.test(r.integrity), 'invalid archive integrity');
88
+ requireThat(r.bin && typeof r.bin.name === 'string' && r.bin.name.length > 0, 'invalid bin');
89
+ validatePatchPath(r.bin.path);
90
+ requireThat(r.bin.args === undefined || (Array.isArray(r.bin.args) && r.bin.args.every(arg => typeof arg === 'string' && !arg.includes('\0'))), 'invalid static argv');
91
+ requireThat(r.agentInfo && typeof r.agentInfo.name === 'string' && r.agentInfo.name.length > 0 && exactVersion(r.agentInfo.version), 'invalid agent identity');
92
+ requireThat(r.node && typeof r.node.range === 'string' && semver.validRange(r.node.range), 'invalid Node range');
93
+ requireThat(Array.isArray(r.historyVersions) && r.historyVersions.includes(r.version) && r.historyVersions.every(exactVersion) && new Set(r.historyVersions).size === r.historyVersions.length, 'invalid history versions');
94
+ requireThat(same(r.patches, policy.required_patches), 'ordered required patches mismatch');
95
+ requireThat(r.files && typeof r.files === 'object' && !Array.isArray(r.files) && Object.keys(r.files).length > 0, 'missing trusted files');
96
+ for (const [file, hash] of Object.entries(r.files)) {
97
+ validatePatchPath(file);
98
+ requireThat(typeof hash === 'string' && hashPattern.test(hash), 'invalid trusted file digest');
99
+ }
100
+ requireThat(r.files['package.json'] && r.files[r.bin.path], 'manifest and executable must be trusted files');
101
+ requireThat(r.capabilities?.cachedReconnect === 'resume' && same(r.capabilities.steering, ['next']) && r.capabilities.subagents === true, 'invalid capabilities');
102
+ const c = r.certification;
103
+ requireThat(c && typeof c.suite === 'string' && c.suite.length > 0 && Array.isArray(c.checks) && Array.isArray(c.platforms), 'missing certification');
104
+ requireThat(c.checks.length === REQUIRED_RUNTIME_CHECKS.length && new Set(c.checks.map(v => v.name)).size === c.checks.length && REQUIRED_RUNTIME_CHECKS.every(name => c.checks.some(v => v.name === name)), 'incomplete or duplicate required checks');
105
+ requireThat(c.checks.every(v => ['PASS', 'NOT_PASS'].includes(v.status) && hashPattern.test(v.evidenceSha256)), 'invalid certification check status');
106
+ requireThat(c.platforms.every(v => typeof v.platform === 'string' && typeof v.arch === 'string' && ['PASS', 'NOT_PASS'].includes(v.status) && hashPattern.test(v.evidenceSha256)) && new Set(c.platforms.map(v => `${v.platform}/${v.arch}`)).size === c.platforms.length, 'invalid platform records');
107
+ requireThat(c.platforms.some(v => v.platform === process.platform && v.arch === process.arch), 'platform record missing');
108
+ requireThat(r.runtimeTools === undefined || Array.isArray(r.runtimeTools), 'invalid runtime tools');
109
+ const tools = r.runtimeTools ?? [];
110
+ requireThat(new Set(tools.map(v => `${v.package?.name ?? r.package}/${v.platform}/${v.arch}/${v.path}`)).size === tools.length, 'duplicate runtime tools');
111
+ for (const tool of tools) {
112
+ validatePatchPath(tool.path);
113
+ if (tool.package !== undefined) {
114
+ requireThat(tool.package && typeof tool.package.name === 'string' && typeof tool.package.version === 'string' && semver.valid(tool.package.version) === tool.package.version, 'invalid exact tool package identity');
115
+ assertSafePackageName(tool.package.name);
116
+ }
117
+ const primary = tool.package === undefined || tool.package.name === r.package;
118
+ requireThat(typeof tool.platform === 'string' && typeof tool.arch === 'string' && hashPattern.test(tool.sha256) && (!primary || r.files[tool.path] === tool.sha256) && c.platforms.some(v => v.platform === tool.platform && v.arch === tool.arch), 'tool must match a trusted digest and declared platform');
119
+ }
120
+ if (retained)
121
+ requireThat(typeof r.patchDigest === 'string' && hashPattern.test(r.patchDigest), 'retained release requires trusted patch digest');
122
+ else {
123
+ const digest = patchDigest(runtimeRecipes(policy, r, assets.recipes));
124
+ requireThat(r.patchDigest === undefined || r.patchDigest === digest, 'release patch digest disagrees with bundled recipes');
125
+ }
126
+ return r;
127
+ }
128
+ export function runtimeRecipes(policy, release, recipes = packagePatches) {
129
+ const selected = recipes.filter(recipe => recipe.package === policy.package && recipe.versions?.includes(policy.version));
130
+ requireThat(same(selected.map(v => v.id), policy.required_patches), 'missing or unordered exact patch recipe');
131
+ for (const recipe of selected) {
132
+ validatePackagePatch(recipe);
133
+ requireThat(recipe.bin === undefined || recipe.bin === release.bin.name, 'patch bin disagrees with release');
134
+ }
135
+ for (const [qualified, hash] of Object.entries(expectedPatchedFiles(selected))) {
136
+ requireThat(release.files[qualified.slice(policy.package.length + 1)] === hash, 'patch digest disagrees with trusted release files');
137
+ }
138
+ return selected;
139
+ }
140
+ /** Reverse the declared edits to derive trusted pristine hashes, without trusting package metadata. */
141
+ function pristineFiles(release, recipes) {
142
+ const files = { ...release.files };
143
+ for (const recipe of [...recipes].reverse()) {
144
+ for (const asset of recipe.assets ?? [])
145
+ delete files[asset.target];
146
+ for (const edit of [...recipe.edits].reverse()) {
147
+ requireThat(files[edit.file] === edit.after_sha256 && hashPattern.test(edit.before_sha256), 'inconsistent patch hash chain');
148
+ files[edit.file] = edit.before_sha256;
149
+ }
150
+ }
151
+ return files;
152
+ }
153
+ export async function verifyRuntimeTree(options) {
154
+ const r = await loadRuntimeRelease(options.policy, options);
155
+ return verifyReleaseTree(options, r);
156
+ }
157
+ async function verifyReleaseTree(options, r) {
158
+ const pkg = inside(path.join(options.treeRoot, 'node_modules'), r.package);
159
+ const files = options.pristine ? pristineFiles(r, runtimeRecipes(options.policy, r, options.recipes)) : r.files;
160
+ await verifyPatchedFiles(pkg, files);
161
+ // Extra executable code or links must not survive a cache adoption, even with matching package.json.
162
+ const actual = [];
163
+ const walk = async (dir, prefix = '') => {
164
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
165
+ const rel = prefix + entry.name;
166
+ if (entry.isDirectory())
167
+ await walk(path.join(dir, entry.name), rel + '/');
168
+ else {
169
+ requireThat(entry.isFile(), 'non-regular runtime asset');
170
+ actual.push(rel);
171
+ }
172
+ }
173
+ };
174
+ await walk(pkg);
175
+ requireThat(same(actual.sort(), Object.keys(files).sort()), 'unexpected or missing runtime files');
176
+ const metadata = JSON.parse(await readFile(path.join(pkg, 'package.json'), 'utf8'));
177
+ requireThat(metadata.name === r.package && metadata.version === r.version && metadata.bin?.[r.bin.name] === r.bin.path, 'package identity/bin mismatch');
178
+ for (const tool of r.runtimeTools ?? []) {
179
+ if (tool.platform !== process.platform || tool.arch !== process.arch)
180
+ continue;
181
+ const toolPackage = tool.package ?? { name: r.package, version: r.version };
182
+ const toolDir = inside(path.join(options.treeRoot, 'node_modules'), toolPackage.name);
183
+ const manifest = await ownedFile(path.join(toolDir, 'package.json'));
184
+ try {
185
+ const identity = JSON.parse(await manifest.readFile('utf8'));
186
+ requireThat(identity.name === toolPackage.name && identity.version === toolPackage.version, 'runtime tool package identity mismatch');
187
+ }
188
+ finally {
189
+ await manifest.close();
190
+ }
191
+ // External tools are authenticated by this explicit record, not by a claim about their whole package.
192
+ await verifyPatchedFiles(toolDir, { [tool.path]: tool.sha256 });
193
+ const handle = await ownedFile(inside(toolDir, tool.path));
194
+ try {
195
+ if (options.prepareTools)
196
+ await handle.chmod(0o755);
197
+ requireThat(process.platform === 'win32' || ((await handle.stat()).mode & 0o111) !== 0, `runtime tool not executable: ${tool.path}`);
198
+ }
199
+ finally {
200
+ await handle.close();
201
+ }
202
+ }
203
+ return r;
204
+ }
205
+ export async function verifyManagedRuntime(options) {
206
+ const release = await loadRuntimeRelease(options.policy, options);
207
+ await verifyReleaseBuild(options, release, patchDigest(runtimeRecipes(options.policy, release, options.recipes)));
208
+ }
209
+ /** Explicit recorded bindings/rollback only. Every historical authority is bundled and listed, never a writable receipt. */
210
+ export async function verifyRecordedManagedRuntime(options) {
211
+ const active = await loadRuntimeRelease(options.policy, options);
212
+ if (options.build.runtimeReleaseId === active.id) {
213
+ await verifyReleaseBuild(options, active, patchDigest(runtimeRecipes(options.policy, active, options.recipes)));
214
+ return;
215
+ }
216
+ const paths = options.policy.retained_releases ?? [];
217
+ requireThat(Array.isArray(paths) && new Set(paths).size === paths.length && !paths.includes(options.policy.release), 'duplicate or active retained release path');
218
+ const ids = new Set([active.id]);
219
+ let match;
220
+ for (const relative of paths) {
221
+ const release = await readReleaseAsset(relative, options);
222
+ requireThat(release?.schema === 1 && typeof release.id === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(release.id) && !ids.has(release.id), 'invalid, duplicate or active retained release identity');
223
+ ids.add(release.id);
224
+ if (release.id === options.build.runtimeReleaseId)
225
+ match = { release, path: relative };
226
+ }
227
+ requireThat(match, 'recorded release is not explicitly retained');
228
+ const policy = { agent: options.policy.agent, package: match.release.package, version: match.release.version, release: match.path, required_patches: match.release.patches, ignore_scripts: true };
229
+ const release = validateRuntimeRelease(match.release, policy, options, true);
230
+ requireThat(release.package !== options.policy.package || semver.lte(release.version, options.policy.version), 'retained release is a future version');
231
+ await verifyReleaseBuild({ ...options, policy }, release, release.patchDigest);
232
+ }
233
+ async function verifyReleaseBuild(options, release, digest) {
234
+ const { policy, build } = options;
235
+ requireThat(release.certification.checks.length === REQUIRED_RUNTIME_CHECKS.length && release.certification.checks.every(v => v.status === 'PASS'), 'release certification is NOT_PASS');
236
+ requireThat(release.certification.platforms.some(v => v.platform === process.platform && v.arch === process.arch && v.status === 'PASS'), 'platform is NOT_PASS');
237
+ assertRuntimeRequest(policy, build.package ?? '', build.version);
238
+ await verifyReleaseTree({ ...options, treeRoot: inside(options.storeRoot, build.dir), pristine: false }, release);
239
+ requireThat(build.agentId === policy.agent && build.runtimeReleaseId === release.id, 'build release identity mismatch');
240
+ requireThat(build.kind === 'npm-patched' && build.patchDigest === digest && same(build.patches, release.patches), 'build patch identity mismatch');
241
+ requireThat(build.launch.entry === `node_modules/${release.package}/${release.bin.path}` && build.launch.runtime === 'node', 'build executable/dispatcher mismatch');
242
+ requireThat(same(build.launch.args, release.bin.args ?? []), 'build static argv mismatch');
243
+ const nodeVersion = await nodeVersionOf(options.nodePath);
244
+ requireThat(semver.satisfies(nodeVersion, release.node.range, { includePrerelease: false }), `Node ${nodeVersion} outside ${release.node.range}`);
245
+ if (options.initialize)
246
+ await initializeProbe(build, options.storeRoot, options.nodePath, release);
247
+ }
248
+ /** One initialize only, no credentials or user HOME, bounded streams/time, awaited process cleanup. */
249
+ async function initializeProbe(build, storeRoot, nodePath, release) {
250
+ const home = await mkdtemp(path.join(tmpdir(), 'runtime-probe-'));
251
+ try {
252
+ await new Promise((resolve, reject) => {
253
+ const dir = inside(storeRoot, build.dir);
254
+ const child = spawn(nodePath, [inside(dir, build.launch.entry), ...build.launch.args], {
255
+ cwd: home, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'],
256
+ env: { PATH: [path.dirname(nodePath), ...(build.launch.pathDirs ?? []).map(p => inside(dir, p)), process.env.PATH ?? ''].join(path.delimiter), HOME: home, USERPROFILE: home, APPDATA: home, LOCALAPPDATA: home, XDG_CONFIG_HOME: home, XDG_CACHE_HOME: home, TMPDIR: home, TEMP: home, TMP: home, ...(process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {}) },
257
+ });
258
+ let output = '';
259
+ let bytes = 0;
260
+ let stderrBytes = 0;
261
+ let response = false;
262
+ let failure;
263
+ const fail = (message) => { failure ??= new Error(`Managed runtime initialize: ${message}`); child.kill('SIGKILL'); };
264
+ const timeout = setTimeout(() => fail('timed out'), 30_000);
265
+ child.on('error', error => { failure ??= error; });
266
+ child.stdin.on('error', () => { });
267
+ child.stderr.on('data', (data) => { stderrBytes += data.length; if (stderrBytes > 1024 * 1024)
268
+ fail('stderr limit exceeded'); });
269
+ child.stdout.on('data', (data) => {
270
+ bytes += data.length;
271
+ if (bytes > 1024 * 1024) {
272
+ fail('stdout limit exceeded');
273
+ return;
274
+ }
275
+ output += data.toString('utf8');
276
+ let end;
277
+ while ((end = output.indexOf('\n')) >= 0) {
278
+ const line = output.slice(0, end);
279
+ output = output.slice(end + 1);
280
+ try {
281
+ requireThat(!response, 'extra initialize response');
282
+ const msg = JSON.parse(line);
283
+ const result = msg.result;
284
+ requireThat(msg.jsonrpc === '2.0' && msg.id === 'axom-runtime-initialize' && !Object.hasOwn(msg, 'error') && !Object.hasOwn(msg, 'method') && result?.protocolVersion === 1, 'invalid correlated initialize result');
285
+ requireThat(result.agentInfo?.name === release.agentInfo.name && result.agentInfo?.version === release.agentInfo.version, 'initialize agent identity mismatch');
286
+ requireThat(result._meta?.axom?.releaseId === release.id && result._meta?.axom?.cachedReconnect === 'resume', 'initialize release/resume mismatch');
287
+ requireThat(result.agentCapabilities?.loadSession === true && result.agentCapabilities?.sessionCapabilities?.resume && typeof result.agentCapabilities.sessionCapabilities.resume === 'object' && !Array.isArray(result.agentCapabilities.sessionCapabilities.resume), 'missing resume capability');
288
+ requireThat(result._meta?.steering?.supported === true && same(result._meta.steering.timings, ['next']), 'missing next capability');
289
+ response = true;
290
+ child.stdin.end();
291
+ // Await graceful EOF cleanup within the same overall deadline. Native agents may
292
+ // flush telemetry before exiting; a second, shorter deadline can kill a healthy one.
293
+ }
294
+ catch (error) {
295
+ fail(error.message);
296
+ }
297
+ }
298
+ });
299
+ child.once('close', (code) => {
300
+ clearTimeout(timeout);
301
+ if (!failure && (!response || output.trim() || code !== 0))
302
+ failure = new Error('Managed runtime initialize: incomplete response or startup failure');
303
+ if (failure)
304
+ reject(failure);
305
+ else
306
+ resolve();
307
+ });
308
+ child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id: 'axom-runtime-initialize', method: 'initialize', params: { protocolVersion: 1, clientInfo: { name: 'axom-runtime-check', version: '1.0.0' }, clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: true } } }) + '\n');
309
+ });
310
+ }
311
+ finally {
312
+ await rm(home, { recursive: true, force: true });
313
+ }
314
+ }
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import semver from 'semver';
4
+ import { assertSafePackageName } from './builds/model.js';
4
5
  // Production reads only assets shipped in this CLI. The .ts path is for source-based tests.
5
6
  export const patchAssetRoot = new URL(import.meta.url.endsWith('.ts') ? '../../../../axom-patch/cli/' : '../../axom-patch/cli/', import.meta.url);
6
7
  const runtime = JSON.parse(readFileSync(new URL('runtime.json', patchAssetRoot), 'utf8'));
@@ -26,11 +27,27 @@ export function validateOverride(entry) {
26
27
  throw new Error(`Axom patch ${entry.id}: pinned ${entry.distribution.version} must not sort above ${until}`);
27
28
  }
28
29
  }
30
+ /** Portable relative paths only: no platform-specific interpretation or normalization. */
31
+ export function validatePatchPath(value) {
32
+ if (typeof value !== 'string' || !value || /[\\:\0]/.test(value) || value.split('/').some(part => !part || part === '.' || part === '..')) {
33
+ throw new Error('Axom patch path escapes its root or is not a portable relative path');
34
+ }
35
+ }
29
36
  export function validatePackagePatch(recipe) {
30
37
  validateCondition(recipe.id, recipe.versions, recipe.until_version, 'versions', 'until_version');
31
38
  if (recipe.versions === undefined && recipe.until_version === undefined) {
32
39
  throw new Error(`Axom patch ${recipe.id}: needs versions or until_version`);
33
40
  }
41
+ for (const edit of recipe.edits)
42
+ validatePatchPath(edit.file);
43
+ if (recipe.assets !== undefined && !Array.isArray(recipe.assets))
44
+ throw new Error('Axom patch assets must be an array');
45
+ for (const asset of recipe.assets ?? []) {
46
+ validatePatchPath(asset.source);
47
+ validatePatchPath(asset.target);
48
+ if (!/^[a-f0-9]{64}$/.test(asset.sha256))
49
+ throw new Error('Axom patch asset sha256 must be a lowercase SHA-256 digest');
50
+ }
34
51
  }
35
52
  function inRange(version, when, until) {
36
53
  if (when)
@@ -78,3 +95,26 @@ export function nativeExtensionFor(agent, version) {
78
95
  export function nativeAccountLimitsVersionFor(agent, version) {
79
96
  return runtime.native_extensions?.find(entry => entry.agent === agent && entry.versions.includes(version))?.accountLimitsVersion ?? 2;
80
97
  }
98
+ /** One entry per package; the only policy is skipping npm lifecycle scripts a package would run in the user's HOME. */
99
+ export function validateInstallPolicies(policies) {
100
+ if (!Array.isArray(policies))
101
+ throw new Error('Axom install_policies must be an array');
102
+ const seen = new Set();
103
+ for (const policy of policies) {
104
+ if (typeof policy?.package !== 'string')
105
+ throw new Error('Axom install policy needs a package name');
106
+ assertSafePackageName(policy.package);
107
+ if (seen.has(policy.package))
108
+ throw new Error(`Axom install policy for ${policy.package} is duplicated`);
109
+ if (policy.ignore_scripts !== true)
110
+ throw new Error(`Axom install policy for ${policy.package} must set ignore_scripts: true`);
111
+ seen.add(policy.package);
112
+ }
113
+ return policies;
114
+ }
115
+ export const installPolicies = validateInstallPolicies(runtime.install_policies ?? []);
116
+ export function installPolicyFor(name, policies = installPolicies) {
117
+ return policies.find((policy) => policy.package === name);
118
+ }
119
+ /** Optional strict release policies; activation is an explicit bundled manifest change. */
120
+ export const managedRuntimePolicies = runtime.managed_runtimes ?? [];
@@ -1,35 +1,16 @@
1
+ /** Checkpoint schema answers whether state is readable; processorVersion identifies output semantics. Readable older processor versions remain restorable; replay differences across versions do not count as divergence. Version 9 moves adapter-specific compatibility folds out and removes retired optional state. */
2
+ import { initialState, PROCESSOR_STATE_SCHEMA } from './state.js';
1
3
  /**
2
- * Processor checkpoints (spec 2026-10-11 §5.5): the state after record `offset`, as JSON.
3
- *
4
- * ## Two versions, two questions
5
- *
6
- * - `schema` answers "can this daemon **read** it?". An unknown schema throws
7
- * {@link CheckpointUnreadableError}; the caller then replays from the session's start (outbox and
8
- * watermark cover the frames already sent), or, if the log was truncated, marks the session
9
- * degraded and switches it at the next open (§5.5.6). Readability of older schemas is a test-guarded
10
- * promise: never change the meaning of an existing field without bumping `schema` and adding a
11
- * reader for the old one.
12
- * - `processorVersion` answers "would **this** code have produced the same frames?". Bump it whenever
13
- * `step` can emit different output for the same records. A readable checkpoint from another
14
- * processor version is still restored — that is the whole point of cutting a head checkpoint at
15
- * upgrade — but the caller must not count replay-vs-outbox differences as divergence then.
16
- *
17
- * Checkpoints are cut only on record boundaries (a synchronous `step` leaves no half-processed record)
18
- * and carry no secrets (`state.ts`).
4
+ * Output history: 2 — turns, cards, seq and semantic-only request checks;
5
+ * 3 — session updates, binding, config, steering and validated events;
6
+ * 4 — spawn-bound commands and history dropped counts; prompt closes history;
7
+ * 5 — callback snapshots, runtime revisions and adapter compatibility folds;
8
+ * 6 — child status precedes process results; ended calls release data;
9
+ * 7 — install outcomes become step events;
10
+ * 8 — loaded-session levels fold live; output gaps become steps;
11
+ * 9 — adapter compatibility folds moved out and retired optional state removed.
19
12
  */
20
- import { PROCESSOR_STATE_SCHEMA } from './state.js';
21
- /**
22
- * Bump when `step`'s output for the same records changes. 2: Task 17a (turns, cards, `seq`; prompt
23
- * methods refused by `rpc.request`). 3: Task 17b (`session/update` mapped, the open session bound
24
- * from its response, config/steer/levels; events validated before numbering). 4: commands carry
25
- * their `spawnId`; `processor.history_end` carries `droppedCount`; a prompt closes open history.
26
- * 5: Task 17c (the callback snapshot in the runtime diagnostics and its revision bumps; Qoder SDK
27
- * cards carry a `callbackContext`; the Qoder adapter boundaries; side-channel notes). 6: a process call's
28
- * result waits for its child's first status; ended/delivered calls keep no dead data.
29
- * 7: Task 20 — the `agent_install_outcome` note becomes a `step` event (it was an unknown note).
30
- * 8: Task 21 — a level update after a load's response is folded live, not as history; the `agent_output_gap` note is a `step`.
31
- */
32
- export const PROCESSOR_VERSION = 8;
13
+ export const PROCESSOR_VERSION = 9;
33
14
  export const CHECKPOINT_SCHEMA = 1;
34
15
  export class CheckpointUnreadableError extends Error {
35
16
  }
@@ -66,22 +47,32 @@ export function decodeCheckpoint(blob) {
66
47
  throw new CheckpointUnreadableError('checkpoint has no processorVersion');
67
48
  if (typeof offset !== 'number' || !Number.isSafeInteger(offset) || offset < -1)
68
49
  throw new CheckpointUnreadableError('checkpoint offset is invalid');
69
- if (!isRecord(state) || state.schema !== PROCESSOR_STATE_SCHEMA)
50
+ if (!isRecord(state) || (state.schema !== 4 && state.schema !== PROCESSOR_STATE_SCHEMA))
70
51
  throw new CheckpointUnreadableError('checkpoint state schema is unknown');
71
52
  if (state.offset !== offset)
72
53
  throw new CheckpointUnreadableError('checkpoint offset disagrees with its state');
73
54
  for (const key of ['process', 'initialize', 'outbound', 'inbound', 'diagnostics', 'permissions', 'elicitations',
74
55
  'permissionOrigins', 'permissionParents', 'unresolvedPermissionOrigins', 'open', 'mapperScopes', 'message', 'steers',
75
- 'asyncTasks', 'runtime', 'callbacks', 'boundaries']) {
56
+ 'asyncTasks', 'runtime', 'callbacks']) {
76
57
  if (!isRecord(state[key]))
77
58
  throw new CheckpointUnreadableError(`checkpoint state has no ${key}`);
78
59
  }
79
60
  if (typeof state.seq !== 'number' || !Array.isArray(state.turns) || !Array.isArray(state.settledPermissions)) {
80
61
  throw new CheckpointUnreadableError('checkpoint state has no session layer (seq/turns)');
81
62
  }
82
- if (!Array.isArray(state.steered) || !('configOptions' in state) || !('delegation' in state) || !('history' in state)
63
+ if (!Array.isArray(state.steered) || !('configOptions' in state) || !('history' in state)
83
64
  || !('activity' in state)) {
84
65
  throw new CheckpointUnreadableError('checkpoint state has no open-session layer (Task 17b)');
85
66
  }
86
- return { schema: CHECKPOINT_SCHEMA, processorVersion, offset, state: clone(state) };
67
+ return { schema: CHECKPOINT_SCHEMA, processorVersion, offset, state: clone({ ...(state.schema === 4 ? upgradeStateFromSchema4(state) : state), ...(!Object.hasOwn(state, 'draftSubagents') ? { draftSubagents: { children: {}, overflow: false } } : {}) }) };
68
+ }
69
+ /** Read only the current neutral keys; retired adapter state is deliberately not copied. */
70
+ function upgradeStateFromSchema4(state) {
71
+ const keys = [...Object.keys(initialState()), 'closed', 'accountAuthBillingModes', 'accountLimits'];
72
+ const current = Object.fromEntries(keys.filter((key) => Object.hasOwn(state, key)).map((key) => [key, state[key]]));
73
+ const runtime = state.runtime;
74
+ return { ...current, schema: PROCESSOR_STATE_SCHEMA, runtime: {
75
+ revision: runtime.revision, available: runtime.available,
76
+ ...(Object.hasOwn(runtime, 'launch') ? { launch: runtime.launch } : {}),
77
+ } };
87
78
  }
@@ -1,42 +1,13 @@
1
- /**
2
- * The open session and its controls, as notes and their responses (Task 17b): `session/new|load|resume`,
3
- * `historyEnd`, `session/set_config_option`/`set_mode`, `_session/steering`, `_session/async_task/stop`,
4
- * delegation's non-secret fields and the runtime `launch` (spec 2026-10-11 §5.4).
5
- *
6
- * ## Opening, and binding from the response
7
- *
8
- * `agent_session_open{mode, cwd, sessionId?, mcpServers?, redact?, delegation?}` sends one request —
9
- * the params `AcpSession.open` (`buildSession`), `replayNativeSession` (`session/load`) and the Qoder
10
- * resume path build, `_meta` included (`newSessionMetaFor`, decided from the initialize reading). The
11
- * MCP server list arrives finished: the daemon validated the delegation lease (`buildSessionMcpServers`,
12
- * which reads the clock) before it wrote the note, and the bearer in it is a `secretRef` token the
13
- * executor substitutes into stdin only (`redact`, spec §13). The **response** binds the root
14
- * (`bindRootSession`) and sets the config snapshot — no pre-bind buffer, no window. A failed open is
15
- * reported (`processor.open_result`) and never retried as `session/new` here.
16
- *
17
- * ## Replies
18
- *
19
- * `agent_config_option_set` and `agent_steered` are RPC replies: unnumbered frames carrying the
20
- * server's frame `id` from the note, built as `agentSessions.ts` builds them. A steer's blocks travel
21
- * like a prompt's (`fill: {blocksRef}`); if the executor lost them, the same `agent_prompt_failed` note
22
- * fails the steer.
23
- *
24
- * ## `steeredIds` (spec §5.5.4)
25
- *
26
- * {@link steeredIds}: steers whose line reached the agent and that are unanswered, plus the last
27
- * {@link STEERED_HISTORY_LIMIT} the agent answered `injected`. A `promptRequired`, a refusal, or a
28
- * line that never reached the agent is not steered: the server delivers that message the ordinary way.
29
- */
1
+ /** Open-session binding, configuration, steering and launch notes. A response binds the native root; a failed load never silently creates another session here. The daemon supplies the finished MCP list with secrets replaced by references. */
30
2
  import { newSessionMetaFor } from '../activityReporting.js';
31
3
  import { flatChoicesOf, modesAlreadyRepresented, modelsAlreadyRepresented, normalizeConfigOptions, SYNTHESIZED_MODES_CONFIG_ID, SYNTHESIZED_MODEL_CONFIG_ID } from '../configOptions.js';
32
4
  import { ASYNC_TASK_STOP_METHOD, mapSteerOutcome, STEER_METHOD } from '../steering.js';
33
5
  import { emitEvent, isRecord } from './emit.js';
34
6
  import { settlementErrorCode } from './jsonrpc.js';
35
7
  import { sameCause } from './record.js';
36
- import { onLaunchNote, onRootBound } from './runtime.js';
8
+ import { onLaunchNote } from './runtime.js';
37
9
  import { bindRootSession, closeErrorText, connected } from './session.js';
38
10
  import { getOwn, putOwn, STEERED_HISTORY_LIMIT, } from './state.js';
39
- import { closeRepair } from './sideChannel.js';
40
11
  import { endHistory } from './updates.js';
41
12
  const OPEN_METHODS = { new: 'session/new', load: 'session/load', resume: 'session/resume' };
42
13
  export const NOT_INITIALIZED = 'The ACP connection has not finished initialize';
@@ -51,30 +22,12 @@ function request(state, out, method, params, kind, ctx, extras = {}) {
51
22
  function isStringArray(value) {
52
23
  return Array.isArray(value) && value.every((item) => typeof item === 'string');
53
24
  }
54
- function delegationOf(value) {
55
- if (value === undefined)
56
- return null;
57
- if (!isRecord(value))
58
- return undefined;
59
- const { expiresAtEpochMs, leaseId, bindingId, instanceId } = value;
60
- if (!Number.isSafeInteger(expiresAtEpochMs) || typeof leaseId !== 'string' || leaseId === ''
61
- || (bindingId !== undefined && typeof bindingId !== 'string') || (instanceId !== undefined && typeof instanceId !== 'string')
62
- || (bindingId !== undefined) !== (instanceId !== undefined))
63
- return undefined;
64
- return {
65
- expiresAtEpochMs: expiresAtEpochMs, leaseId,
66
- ...(bindingId !== undefined ? { bindingId, instanceId: instanceId, renewalRevision: 0 } : {}),
67
- };
68
- }
69
25
  // ---- open ---------------------------------------------------------------------------------------
70
- /** `agent_session_open{mode, cwd, sessionId?, mcpServers?, redact?, delegation?}`. */
71
26
  export function onOpenNote(state, note, out) {
72
27
  const { mode, cwd, sessionId, mcpServers, redact } = note;
73
- const delegation = delegationOf(note.delegation);
74
28
  if ((mode !== 'new' && mode !== 'load' && mode !== 'resume') || typeof cwd !== 'string' || cwd === ''
75
29
  || (mode === 'new' ? sessionId !== undefined : typeof sessionId !== 'string' || sessionId === '')
76
- || (mcpServers !== undefined && !Array.isArray(mcpServers)) || (redact !== undefined && !isStringArray(redact))
77
- || delegation === undefined) {
30
+ || (mcpServers !== undefined && !Array.isArray(mcpServers)) || (redact !== undefined && !isStringArray(redact))) {
78
31
  state.diagnostics.droppedNotes += 1;
79
32
  return;
80
33
  }
@@ -92,7 +45,7 @@ export function onOpenNote(state, note, out) {
92
45
  : mode === 'load'
93
46
  ? { sessionId, cwd, mcpServers: servers, ...(meta ? { _meta: meta } : {}) }
94
47
  : { sessionId, cwd, mcpServers: servers };
95
- const pending = request(state, out, OPEN_METHODS[mode], params, 'open', { mode, ...(sessionId !== undefined ? { sessionId } : {}), ...(delegation ? { delegation } : {}) }, redact !== undefined && redact.length > 0 ? { redact } : {});
48
+ const pending = request(state, out, OPEN_METHODS[mode], params, 'open', { mode, ...(sessionId !== undefined ? { sessionId } : {}) }, redact !== undefined && redact.length > 0 ? { redact } : {});
96
49
  state.open = { status: 'pending', requestId: pending.id, mode };
97
50
  if (mode === 'load')
98
51
  state.history = { rootSessionId: sessionId, scopes: {}, messages: [], droppedCount: 0 };
@@ -109,7 +62,6 @@ export function settleOpen(state, entry, settlement, out) {
109
62
  state.open = { status: 'failed', mode, error, ...(code !== undefined ? { code } : {}) };
110
63
  endHistory(state, out, false);
111
64
  // A failed open closes the attached session, and its repair with it (`AcpSession.close`).
112
- closeRepair(state);
113
65
  out.unjournaled({ t: 'processor.open_result', ok: false, mode, error, ...(code !== undefined ? { code } : {}) });
114
66
  return;
115
67
  }
@@ -117,10 +69,8 @@ export function settleOpen(state, entry, settlement, out) {
117
69
  const nativeSessionId = mode === 'new' ? (typeof result.sessionId === 'string' ? result.sessionId : '') : ctx.sessionId;
118
70
  if (nativeSessionId.length > 0) {
119
71
  bindRootSession(state, nativeSessionId);
120
- onRootBound(state, out, nativeSessionId);
121
72
  }
122
73
  state.configOptions = normalizeConfigOptions(result.configOptions, result.modes, result.models);
123
- state.delegation = ctx.delegation ?? null;
124
74
  state.open = { status: 'open', mode };
125
75
  out.unjournaled({
126
76
  t: 'processor.open_result', ok: true, mode,
@@ -280,7 +230,7 @@ export function steeredIds(state) {
280
230
  const inFlight = Object.entries(state.steers).filter(([, steer]) => steer.sent).map(([id]) => id);
281
231
  return [...state.steered, ...inFlight];
282
232
  }
283
- // ---- background tasks, delegation, launch -------------------------------------------------------
233
+ // ---- background tasks, launch -------------------------------------------------------
284
234
  /** `agent_async_task_stop{asyncTaskId}` — `asyncTaskStopRequest`; its answer is diagnostics only. */
285
235
  export function onAsyncTaskStopNote(state, note, out) {
286
236
  const { asyncTaskId } = note;
@@ -290,29 +240,6 @@ export function onAsyncTaskStopNote(state, note, out) {
290
240
  }
291
241
  request(state, out, ASYNC_TASK_STOP_METHOD, { sessionId: state.rootSessionId, asyncTaskId }, 'rpc', undefined);
292
242
  }
293
- /**
294
- * `agent_delegation_renewed{expiresAtEpochMs, renewalRevision}` — a renewal the daemon accepted
295
- * (`agent_delegation_renew`'s checks read the clock, so the decision is the daemon's; the note is
296
- * its record).
297
- */
298
- export function onDelegationRenewedNote(state, note) {
299
- const { expiresAtEpochMs, renewalRevision } = note;
300
- const delegation = state.delegation;
301
- if (!delegation || delegation.bindingId === undefined || !Number.isSafeInteger(expiresAtEpochMs) || !Number.isSafeInteger(renewalRevision)) {
302
- state.diagnostics.droppedNotes += 1;
303
- return;
304
- }
305
- delegation.expiresAtEpochMs = expiresAtEpochMs;
306
- delegation.renewalRevision = renewalRevision;
307
- }
308
- /** `agent_delegation_refresh_pending{}` — a refreshed lease waits for the session to go idle. */
309
- export function onDelegationRefreshPendingNote(state) {
310
- if (!state.delegation) {
311
- state.diagnostics.droppedNotes += 1;
312
- return;
313
- }
314
- state.delegation.refreshPending = true;
315
- }
316
243
  /** `agent_launch{launch}` (see `runtime.ts`). */
317
244
  export function onLaunch(state, note) {
318
245
  if (!onLaunchNote(state, note.launch))