@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
@@ -2,30 +2,29 @@
2
2
  "id": "zcode-account-limits",
3
3
  "package": "zcode-acp-server",
4
4
  "versions": [
5
- "0.69.3",
6
- "0.69.4"
5
+ "0.80.0"
7
6
  ],
8
7
  "edits": [
9
8
  {
10
9
  "file": "dist/index.js",
11
- "before_sha256": "3f7286e9380681ca22bce5ed5d4faaaad595edee745589337babe4d3f2e0113d",
12
- "after_sha256": "8ae15d912cd451fa155fadf8fa7707553a51477d2f3974a1deb0ebf9bed55a00",
13
- "before": " .onRequest(\"account/usage_stats\", z.object({}).passthrough(), () => accountUsageStats())",
14
- "after": " .onRequest(axomQuota.READ, z.object({}).passthrough(), async () => {\n const { buildAccountProviderConfig, codingPlanRequestAuthFor } = await import(\"./config/account-provider.js\");\n const { resolveQuotaHost } = await import(\"./quota/client.js\");\n let apiKey = null, family = \"\";\n for (const [providerId, provider] of Object.entries(buildAccountProviderConfig()?.providers ?? {})) {\n if (!provider?.access?.entitled) continue;\n const auth = codingPlanRequestAuthFor(providerId);\n if (auth?.apiKey) { apiKey = auth.apiKey; family = providerId; break; }\n }\n if (!apiKey) throw new Error(\"No entitled GLM coding plan: sign in once in the ZCode desktop app\");\n const resp = await fetch(resolveQuotaHost(family.startsWith(\"account:zai\") ? \"https://api.z.ai\" : \"\") + \"/api/monitor/usage/quota/limit\", { headers: { Accept: \"application/json, text/plain, */*\", Authorization: \"Bearer \" + apiKey }, signal: AbortSignal.timeout(8000) });\n const text = await resp.text();\n let json = null; try { json = text ? JSON.parse(text) : null; } catch {}\n if (resp.status === 429 || !json || json.success !== true) throw new Error(\"GLM quota read failed\" + (json?.msg ? \": \" + json.msg : \" (HTTP \" + resp.status + \")\"));\n const snapshot = axomQuota.zcode({ provider: family, level: json.data?.level, limits: json.data?.limits });\n axomQuotaNotify(snapshot);\n return snapshot;\n })\n .onRequest(\"account/usage_stats\", z.object({}).passthrough(), () => accountUsageStats())"
10
+ "before_sha256": "982001fdae54eb211ed3ad0af459035ce42696df2303ee0a50e94aff07b4f1b8",
11
+ "after_sha256": "82fc48b741090d8a178e5dca26f226960f9c2f222badc9c334347cc4b6300d22",
12
+ "before": " .onRequest(\"account/usage_stats\", z.object({}).passthrough(), () => accountUsageStatsViaHub())",
13
+ "after": " .onRequest(axomQuota.READ, z.object({}).passthrough(), async () => {\n const { buildAccountProviderConfig, codingPlanRequestAuthFor } = await import(\"./config/account-provider.js\");\n const { resolveQuotaHost } = await import(\"./quota/client.js\");\n let apiKey = null, family = \"\";\n for (const [providerId, provider] of Object.entries(buildAccountProviderConfig()?.providers ?? {})) {\n if (!provider?.access?.entitled) continue;\n const auth = codingPlanRequestAuthFor(providerId);\n if (auth?.apiKey) { apiKey = auth.apiKey; family = providerId; break; }\n }\n if (!apiKey) throw new Error(\"No entitled GLM coding plan: sign in once in the ZCode desktop app\");\n const resp = await fetch(resolveQuotaHost(family.startsWith(\"account:zai\") ? \"https://api.z.ai\" : \"\") + \"/api/monitor/usage/quota/limit\", { headers: { Accept: \"application/json, text/plain, */*\", Authorization: \"Bearer \" + apiKey }, signal: AbortSignal.timeout(8000) });\n const text = await resp.text();\n let json = null; try { json = text ? JSON.parse(text) : null; } catch {}\n if (resp.status === 429 || !json || json.success !== true) throw new Error(\"GLM quota read failed\" + (json?.msg ? \": \" + json.msg : \" (HTTP \" + resp.status + \")\"));\n const snapshot = axomQuota.zcode({ provider: family, level: json.data?.level, limits: json.data?.limits });\n axomQuotaNotify(snapshot);\n return snapshot;\n })\n .onRequest(\"account/usage_stats\", z.object({}).passthrough(), () => accountUsageStatsViaHub())"
15
14
  },
16
15
  {
17
16
  "file": "dist/index.js",
18
- "before_sha256": "8ae15d912cd451fa155fadf8fa7707553a51477d2f3974a1deb0ebf9bed55a00",
19
- "after_sha256": "a3047f20115eb8bcad7dfee1eac6689a73127f589dce6131138b22e9ca2712d1",
17
+ "before_sha256": "82fc48b741090d8a178e5dca26f226960f9c2f222badc9c334347cc4b6300d22",
18
+ "after_sha256": "f0cff75cf1da73f39f72d6ac7f32b6e9811ecef5743a987f3fb043dbe7275d96",
20
19
  "before": " .onRequest(\"initialize\", (ctx) => server.initialize(ctx.params, ctx.client))",
21
20
  "after": " .onRequest(\"initialize\", async (ctx) => { const result = await server.initialize(ctx.params, ctx.client); const caps = result.agentCapabilities ?? {}; result.agentCapabilities = { ...caps, _meta: { ...caps._meta, \"axom/accountRateLimits\": { ...axomQuota.CAPABILITY, provider: \"zcode\" } } }; return result; })"
22
21
  },
23
22
  {
24
23
  "file": "dist/index.js",
25
- "before_sha256": "a3047f20115eb8bcad7dfee1eac6689a73127f589dce6131138b22e9ca2712d1",
26
- "after_sha256": "1dcc36538826c874d851254d56f91b9f9f12a93652668688f5ed53baa803823f",
24
+ "before_sha256": "f0cff75cf1da73f39f72d6ac7f32b6e9811ecef5743a987f3fb043dbe7275d96",
25
+ "after_sha256": "00e43e449e3e428778a8b4ee2f1ab4535d28113bf7e1504e8215cba76d6629b4",
27
26
  "before": "#!/usr/bin/env node\n/**\n * Entry point: wire the ZcodeAcpServer to a stdio ACP stream.\n *\n * The ACP",
28
- "after": "#!/usr/bin/env node\n\nconst axomQuota = (() => {\n/** Account quota normalization. No I/O: passive events must never trigger a query. */\nconst READ = '_axom/account/rateLimits/read';\nconst UPDATED = '_axom/account/rateLimits/updated';\nconst CAPABILITY = { version: 2, read: true, updated: true };\nconst percent = v => typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : null;\nconst finite = v => { if (v == null || typeof v === 'boolean') return null; const n = typeof v === 'number' ? v : Number(v); return Number.isFinite(n) ? n : null; };\nconst time = (v, milliseconds = false) => {\n if (v == null || v === 0) return null;\n const d = new Date(typeof v === 'number' ? v * (milliseconds ? 1 : 1000) : v);\n return Number.isFinite(d.getTime()) ? d.toISOString() : null;\n};\nconst status = v => ({ allowed: 'allowed', allowed_warning: 'warning', rejected: 'blocked', normal: 'allowed' }[v] ?? 'unknown');\n/** Canonical window key rides the snapshot; the UI owns the key → display-label translation.\n * Windows without a canonical key keep their vendor content untouched. */\nconst windowKey = (seconds, period) => period === 'month' ? 'monthly' : seconds === 18000 ? 'five_hour' : seconds === 604800 ? 'weekly' : null;\nconst base = (provider, mode, raw, observedAt) => ({ schemaVersion: 1, updateMode: 'snapshot', provider, billingMode: mode, availability: mode === 'subscription' ? 'available' : mode === 'unknown' ? 'unknown' : 'not_applicable', observedAt: observedAt ?? new Date().toISOString(), plan: null, limits: [], balances: [], _meta: { ['_' + provider]: structuredClone(raw) } });\nfunction codex(raw, authType = 'chatgpt', observedAt) {\n const out = base('codex', ['chatgpt', 'chatgptAuthTokens'].includes(authType) ? 'subscription' : ['apiKey','apikey','amazonBedrock'].includes(authType) ? 'non_subscription' : 'unknown', raw, observedAt);\n if (out.billingMode !== 'subscription') return out;\n const buckets = Object.entries(raw.rateLimitsByLimitId ?? {});\n if (!buckets.length && raw.rateLimits) buckets.push([raw.rateLimits.limitId ?? 'codex', raw.rateLimits]);\n for (const [id, bucket] of buckets) {\n if (!bucket) continue;\n if (bucket.planType) out.plan = { id: bucket.planType, label: bucket.planType };\n for (const name of ['primary', 'secondary']) {\n const w = bucket[name]; if (!w) continue;\n const seconds = w.windowDurationMins == null ? null : w.windowDurationMins * 60;\n out.limits.push({ id: `${id}.${name}`, label: bucket.limitName ?? id, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: percent(w.usedPercent), resetsAt: time(w.resetsAt), status: bucket.rateLimitReachedType ? 'blocked' : 'unknown', scope: { bucket: id } });\n }\n if (bucket.credits) out.balances.push({ id: `${id}.credits`, label: 'Credits', remaining: bucket.credits.balance ?? null, unit: 'credits', unlimited: bucket.credits.unlimited === true });\n }\n // Account IDs must not escape via provider extensions.\n delete out._meta._codex.accountId;\n return out;\n}\nfunction claude(raw, observedAt) {\n const mode = raw.billing_mode ?? (raw.subscription_type || raw.rate_limits_available === true ? 'subscription' : 'unknown');\n const out = base('claude', mode, raw, observedAt);\n out.plan = raw.subscription_type ? { id: raw.subscription_type, label: raw.subscription_type } : null;\n if (mode === 'subscription' && !raw.rate_limits_available) out.availability = 'unavailable';\n const limits = raw.rate_limits ?? {};\n const rows = Array.isArray(limits.limits) ? limits.limits : [];\n if (rows.length) {\n rows.forEach((w, i) => {\n const seconds = w.group === 'session' ? 18000 : w.group === 'weekly' ? 604800 : null;\n out.limits.push({ id: `claude.${w.kind}.${i}`, label: w.scope?.model?.display_name ?? w.kind, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: percent(w.percent), resetsAt: time(w.resets_at), status: status(w.severity), active: w.is_active ?? null, scope: w.scope ?? null });\n });\n } else for (const [id, seconds] of [['five_hour', 18000], ['seven_day', 604800], ['seven_day_opus',604800], ['seven_day_sonnet',604800]]) {\n const w = limits[id]; if (!w) continue;\n out.limits.push({ id: `claude.${id}`, label: id, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: percent(w.utilization), resetsAt: time(w.resets_at), status: 'unknown', scope: id.endsWith('opus') || id.endsWith('sonnet') ? { model: id.split('_').at(-1) } : null });\n }\n const spend=limits.spend;\n if(spend?.enabled && spend.used && spend.limit) out.limits.push({id:'claude.extra_spend',label:'Extra usage',kind:'spend',windowSeconds:null,usedPercent:percent(spend.percent),resetsAt:null,status:status(spend.severity),used:String(spend.used.amount_minor),limit:String(spend.limit.amount_minor),unit:'money',currency:spend.used.currency,exponent:spend.used.exponent});\n if(spend?.balance?.amount_minor != null) out.balances.push({id:'claude.extra_balance',label:'Extra usage balance',remaining:String(spend.balance.amount_minor),unit:'money',currency:spend.balance.currency,exponent:spend.balance.exponent});\n return out;\n}\nfunction claudeEvent(raw, previous = null, observedAt) {\n const out = previous ? structuredClone(previous) : base('claude',raw.rateLimitType?'subscription':'unknown',{},observedAt);\n out.updateMode = 'delta';\n out.balances = [];\n out.observedAt = observedAt ?? new Date().toISOString();\n out._meta._claude = { ...out._meta._claude, rateLimitEvent: raw };\n const seconds = raw.rateLimitType === 'five_hour' ? 18000 : raw.rateLimitType?.startsWith('seven_day') ? 604800 : null;\n if (seconds !== null) {\n const id = `claude.${raw.rateLimitType}`;\n const scoped = !['five_hour','seven_day'].includes(raw.rateLimitType);\n const prior=out.limits.find(w => w.windowSeconds===seconds && JSON.stringify(w.scope??null)===JSON.stringify(scoped ? {model:raw.rateLimitType.split('_').at(-1)}:null));\n const nextReset=time(raw.resetsAt);\n const rolled=prior?.resetsAt && nextReset && Math.abs(Date.parse(nextReset)-Date.parse(prior.resetsAt))>1000;\n const row = { id, label: raw.rateLimitType, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: raw.utilization == null ? (rolled ? null : prior?.usedPercent ?? null) : percent(raw.utilization * 100), resetsAt: raw.resetsAt == null ? prior?.resetsAt ?? null : time(raw.resetsAt), status: status(raw.status), scope: scoped ? { model: raw.rateLimitType.split('_').at(-1) } : null };\n // Event utilization is a ratio, unlike get_usage's percentage. Match the existing window without duplicating it.\n const i = out.limits.findIndex(w => w.windowSeconds === seconds && JSON.stringify(w.scope ?? null) === JSON.stringify(row.scope));\n out.limits = [i >= 0 ? { ...out.limits[i], ...row, id: out.limits[i].id } : row];\n } else out.limits = [];\n return out;\n}\nfunction kimi(raw, observedAt) {\n const quota = raw.quota ?? raw;\n const out = base('kimi','subscription',quota,observedAt);\n for (const [id, seconds] of [['limit5h',18000],['limit7d',604800],['monthTotal',null]]) {\n const w = quota.usages?.[id]; if (!w) continue;\n out.limits.push({ id: `kimi.${id}`, label: id, kind: 'subscription', windowSeconds: seconds, period: id === 'monthTotal' ? 'month' : null, key: windowKey(seconds, id === 'monthTotal' ? 'month' : null), usedPercent: percent(w.usedRatio * 100), resetsAt: time(w.resetAt), status: 'unknown' });\n }\n const wallet = quota.extraUsage;\n if (wallet) out.balances.push({ id:'kimi.extra',label:'Extra usage',remaining:String(wallet.balanceCents),unit:'money',currency:wallet.currency,exponent:2 });\n return out;\n}\n/** GLM quota `percentage` is the USED share (measured: 5% for 58/1000); counters win when present.\n * Counter-derived shares are rounded to 2 decimals — 100-94.2 keeps fp noise otherwise. */\nconst glmPercent = limit => {\n const used = finite(limit.currentValue), remaining = finite(limit.remaining), total = finite(limit.usage);\n const cap = remaining !== null && used !== null ? remaining + used : total;\n const round2 = v => v == null ? null : Math.round(v * 100) / 100;\n if (cap !== null && cap > 0 && remaining !== null && remaining >= 0 && remaining <= cap) return round2(100 - percent(remaining / cap * 100));\n if (cap !== null && cap > 0 && used !== null && used >= 0 && used <= cap) return round2(percent(used / cap * 100));\n return percent(limit.percentage);\n};\nfunction zcode(raw, observedAt) {\n // Raw: the GLM quota endpoint's data block — { level, limits: [{ type, number, usage,\n // currentValue, remaining, percentage, nextResetTime(epoch ms), usageDetails }] }. Which windows\n // exist is plan-dependent (measured pro plan: 5h prompts + a tool/MCP allowance; no weekly row —\n // a `number: 7` window maps when a plan returns one, and nothing is invented when it doesn't).\n const limits = Array.isArray(raw?.limits) ? raw.limits : [];\n const level = typeof raw?.level === 'string' && raw.level ? raw.level : null;\n const out = base('zcode', limits.length ? 'subscription' : 'unknown', raw, observedAt);\n out.plan = level ? { id: level, label: level } : null;\n let tools = 0;\n limits.forEach((limit, i) => {\n if (!limit || typeof limit !== 'object') return;\n const resetsAt = time(limit.nextResetTime, true);\n if (limit.type === 'TOKENS_LIMIT') {\n // GLM window convention: number 5 → the 5-hour prompt window, 7 → weekly.\n // Ids mirror the adapter's own /quota keys (token_5h / token_week).\n const seconds = limit.number === 5 ? 18000 : limit.number === 7 ? 604800 : null;\n const label = limit.number === 5 ? '5h' : limit.number === 7 ? 'Week' : `token ${limit.number ?? i}`;\n const id = limit.number === 5 ? 'zcode.token_5h' : limit.number === 7 ? 'zcode.token_week' : `zcode.token_${limit.number ?? i}`;\n out.limits.push({ id, label, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: glmPercent(limit), resetsAt, status: 'unknown' });\n } else if (limit.type === 'TIME_LIMIT' || limit.type === 'MCP_LIMIT') {\n // Built-in tool/MCP allowance: absolute counters (unit 'calls'); per-model detail stays in _meta.\n const used = finite(limit.currentValue), total = finite(limit.usage), remaining = finite(limit.remaining);\n out.limits.push({ id: tools ? `zcode.mcp.${i}` : 'zcode.mcp', label: 'MCP', kind: 'subscription', windowSeconds: null, usedPercent: glmPercent(limit), ...(used !== null && total !== null ? { used: String(used), limit: String(total), unit: 'calls' } : {}), resetsAt, status: remaining === 0 ? 'blocked' : used !== null || limit.percentage != null ? 'allowed' : 'unknown' });\n tools++;\n }\n });\n return out;\n}\nfunction commandcode(raw, observedAt) {\n const subscription = raw.subscription?.data ?? raw.subscription;\n const windows = raw.credits?.windowLimits ?? raw.windowLimits;\n const subscribed = ['active','trialing','past_due'].includes(subscription?.status) || windows?.limited === true;\n const mode = subscribed ? 'subscription' : subscription || windows ? 'non_subscription' : 'unknown';\n const out = base('commandcode',mode,raw,observedAt);\n out.plan = subscription?.planId ? { id:subscription.planId,label:subscription.planId } : null;\n for (const [id, seconds] of [['fiveHour',18000],['weekly',604800]]) {\n const w=windows?.[id]; if (!w) continue;\n out.limits.push({ id:`commandcode.${id}`,label:id,kind:'subscription',windowSeconds:seconds,key:windowKey(seconds),usedPercent:w.cap > 0 ? percent(w.used / w.cap * 100) : null,used:String(w.used),limit:String(w.cap),unit:'credits',resetsAt:time(w.resetAt,true),status:w.exceeded === true ? 'blocked' : 'allowed' });\n }\n const credits=raw.credits?.credits ?? raw.credits;\n if (subscribed && credits?.monthlyCredits != null) {\n const cap=raw.monthly?.limit;\n const known=typeof cap==='number' && Number.isFinite(cap) && cap>0;\n const used=known ? Math.max(0,cap-credits.monthlyCredits) : null;\n out.limits.push({id:'commandcode.monthly',label:'Monthly',kind:'subscription',windowSeconds:null,period:'month',key:windowKey(null,'month'),usedPercent:known?percent(used/cap*100):null,resetsAt:time(subscription?.currentPeriodEnd,true),status:'unknown',...(known?{used:String(used),limit:String(cap),unit:'credits'}:{})});\n }\n for (const [id,label] of [['monthlyCredits','Monthly credits'],['purchasedCredits','Purchased credits'],['freeCredits','Free credits']]) if (credits?.[id] != null) out.balances.push({ id:`commandcode.${id}`,label,remaining:String(credits[id]),unit:'credits' });\n return out;\n}\n\nreturn {READ,UPDATED,CAPABILITY,codex,claude,claudeEvent,kimi,commandcode,zcode};\n})();\nconst axomQuotaNotify = snapshot => process.stdout.write(JSON.stringify({jsonrpc:\"2.0\",method:axomQuota.UPDATED,params:snapshot})+\"\\n\");\n/**\n * Entry point: wire the ZcodeAcpServer to a stdio ACP stream.\n *\n * The ACP"
27
+ "after": "#!/usr/bin/env node\n\nconst axomQuota = (() => {\n/** Account quota normalization. No I/O: passive events must never trigger a query. */\nconst READ = '_axom/account/rateLimits/read';\nconst UPDATED = '_axom/account/rateLimits/updated';\nconst CAPABILITY = { version: 2, read: true, updated: true };\nconst percent = v => typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : null;\nconst finite = v => { if (v == null || typeof v === 'boolean') return null; const n = typeof v === 'number' ? v : Number(v); return Number.isFinite(n) ? n : null; };\nconst time = (v, milliseconds = false) => {\n if (v == null || v === 0) return null;\n const d = new Date(typeof v === 'number' ? v * (milliseconds ? 1 : 1000) : v);\n return Number.isFinite(d.getTime()) ? d.toISOString() : null;\n};\nconst status = v => ({ allowed: 'allowed', allowed_warning: 'warning', rejected: 'blocked', normal: 'allowed' }[v] ?? 'unknown');\n/** Canonical window key rides the snapshot; the UI owns the key → display-label translation.\n * Windows without a canonical key keep their vendor content untouched. */\nconst windowKey = (seconds, period) => period === 'month' ? 'monthly' : seconds === 18000 ? 'five_hour' : seconds === 604800 ? 'weekly' : null;\nconst base = (provider, mode, raw, observedAt) => ({ schemaVersion: 1, updateMode: 'snapshot', provider, billingMode: mode, availability: mode === 'subscription' ? 'available' : mode === 'unknown' ? 'unknown' : 'not_applicable', observedAt: observedAt ?? new Date().toISOString(), plan: null, limits: [], balances: [], _meta: { ['_' + provider]: structuredClone(raw) } });\nfunction codex(raw, authType = 'chatgpt', observedAt) {\n const out = base('codex', ['chatgpt', 'chatgptAuthTokens'].includes(authType) ? 'subscription' : ['apiKey','apikey','amazonBedrock'].includes(authType) ? 'non_subscription' : 'unknown', raw, observedAt);\n if (out.billingMode !== 'subscription') return out;\n const buckets = Object.entries(raw.rateLimitsByLimitId ?? {});\n if (!buckets.length && raw.rateLimits) buckets.push([raw.rateLimits.limitId ?? 'codex', raw.rateLimits]);\n for (const [id, bucket] of buckets) {\n if (!bucket) continue;\n if (bucket.planType) out.plan = { id: bucket.planType, label: bucket.planType };\n for (const name of ['primary', 'secondary']) {\n const w = bucket[name]; if (!w) continue;\n const seconds = w.windowDurationMins == null ? null : w.windowDurationMins * 60;\n out.limits.push({ id: `${id}.${name}`, label: bucket.limitName ?? id, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: percent(w.usedPercent), resetsAt: time(w.resetsAt), status: bucket.rateLimitReachedType ? 'blocked' : 'unknown', scope: { bucket: id } });\n }\n if (bucket.credits) out.balances.push({ id: `${id}.credits`, label: 'Credits', remaining: bucket.credits.balance ?? null, unit: 'credits', unlimited: bucket.credits.unlimited === true });\n }\n // Account IDs must not escape via provider extensions.\n delete out._meta._codex.accountId;\n return out;\n}\n/** App-server notifications describe changed buckets, not the complete account read. */\nfunction codexEvent(raw, authType, observedAt) {\n return { ...codex(raw, authType, observedAt), updateMode: 'delta' };\n}\nfunction claude(raw, observedAt) {\n const mode = raw.billing_mode ?? (raw.subscription_type || raw.rate_limits_available === true ? 'subscription' : 'unknown');\n const out = base('claude', mode, raw, observedAt);\n out.plan = raw.subscription_type ? { id: raw.subscription_type, label: raw.subscription_type } : null;\n if (mode === 'subscription' && !raw.rate_limits_available) out.availability = 'unavailable';\n const limits = raw.rate_limits ?? {};\n const rows = Array.isArray(limits.limits) ? limits.limits : [];\n if (rows.length) {\n rows.forEach((w, i) => {\n const seconds = w.group === 'session' ? 18000 : w.group === 'weekly' ? 604800 : null;\n out.limits.push({ id: `claude.${w.kind}.${i}`, label: w.scope?.model?.display_name ?? w.kind, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: percent(w.percent), resetsAt: time(w.resets_at), status: status(w.severity), active: w.is_active ?? null, scope: w.scope ?? null });\n });\n } else for (const [id, seconds] of [['five_hour', 18000], ['seven_day', 604800], ['seven_day_opus',604800], ['seven_day_sonnet',604800]]) {\n const w = limits[id]; if (!w) continue;\n out.limits.push({ id: `claude.${id}`, label: id, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: percent(w.utilization), resetsAt: time(w.resets_at), status: 'unknown', scope: id.endsWith('opus') || id.endsWith('sonnet') ? { model: id.split('_').at(-1) } : null });\n }\n const spend=limits.spend;\n if(spend?.enabled && spend.used && spend.limit) out.limits.push({id:'claude.extra_spend',label:'Extra usage',kind:'spend',windowSeconds:null,usedPercent:percent(spend.percent),resetsAt:null,status:status(spend.severity),used:String(spend.used.amount_minor),limit:String(spend.limit.amount_minor),unit:'money',currency:spend.used.currency,exponent:spend.used.exponent});\n if(spend?.balance?.amount_minor != null) out.balances.push({id:'claude.extra_balance',label:'Extra usage balance',remaining:String(spend.balance.amount_minor),unit:'money',currency:spend.balance.currency,exponent:spend.balance.exponent});\n return out;\n}\nfunction claudeEvent(raw, previous = null, observedAt) {\n const out = previous ? structuredClone(previous) : base('claude',raw.rateLimitType?'subscription':'unknown',{},observedAt);\n out.updateMode = 'delta';\n out.balances = [];\n out.observedAt = observedAt ?? new Date().toISOString();\n // Other sessions may have refreshed the account since this process's cached read.\n // Send only facts from this event; the server owns the merged account observation.\n out.plan = null;\n out._meta = { _claude: { rateLimitEvent: raw } };\n const seconds = raw.rateLimitType === 'five_hour' ? 18000 : raw.rateLimitType?.startsWith('seven_day') ? 604800 : null;\n if (seconds !== null) {\n const id = `claude.${raw.rateLimitType}`;\n const scoped = !['five_hour','seven_day'].includes(raw.rateLimitType);\n const prior=out.limits.find(w => w.windowSeconds===seconds && JSON.stringify(w.scope??null)===JSON.stringify(scoped ? {model:raw.rateLimitType.split('_').at(-1)}:null));\n const row = { id: prior?.id ?? id, label: raw.rateLimitType, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: raw.utilization == null ? null : percent(raw.utilization * 100), resetsAt: time(raw.resetsAt), status: status(raw.status), scope: scoped ? { model: raw.rateLimitType.split('_').at(-1) } : null };\n // Event utilization is a ratio, unlike get_usage's percentage. Match the existing window without duplicating it.\n out.limits = [row];\n } else out.limits = [];\n return out;\n}\nfunction kimi(raw, observedAt) {\n const quota = raw.quota ?? raw;\n const out = base('kimi','subscription',quota,observedAt);\n for (const [id, seconds] of [['limit5h',18000],['limit7d',604800],['monthTotal',null]]) {\n const w = quota.usages?.[id]; if (!w) continue;\n out.limits.push({ id: `kimi.${id}`, label: id, kind: 'subscription', windowSeconds: seconds, period: id === 'monthTotal' ? 'month' : null, key: windowKey(seconds, id === 'monthTotal' ? 'month' : null), usedPercent: percent(w.usedRatio * 100), resetsAt: time(w.resetAt), status: 'unknown' });\n }\n const wallet = quota.extraUsage;\n if (wallet) out.balances.push({ id:'kimi.extra',label:'Extra usage',remaining:String(wallet.balanceCents),unit:'money',currency:wallet.currency,exponent:2 });\n return out;\n}\n/** GLM quota `percentage` is the USED share (measured: 5% for 58/1000); counters win when present.\n * Counter-derived shares are rounded to 2 decimals — 100-94.2 keeps fp noise otherwise. */\nconst glmPercent = limit => {\n const used = finite(limit.currentValue), remaining = finite(limit.remaining), total = finite(limit.usage);\n const cap = remaining !== null && used !== null ? remaining + used : total;\n const round2 = v => v == null ? null : Math.round(v * 100) / 100;\n if (cap !== null && cap > 0 && remaining !== null && remaining >= 0 && remaining <= cap) return round2(100 - percent(remaining / cap * 100));\n if (cap !== null && cap > 0 && used !== null && used >= 0 && used <= cap) return round2(percent(used / cap * 100));\n return percent(limit.percentage);\n};\nfunction zcode(raw, observedAt) {\n // Raw: the GLM quota endpoint's data block — { level, limits: [{ type, number, usage,\n // currentValue, remaining, percentage, nextResetTime(epoch ms), usageDetails }] }. Which windows\n // exist is plan-dependent (measured pro plan: 5h prompts + a tool/MCP allowance; no weekly row —\n // a `number: 7` window maps when a plan returns one, and nothing is invented when it doesn't).\n const limits = Array.isArray(raw?.limits) ? raw.limits : [];\n const level = typeof raw?.level === 'string' && raw.level ? raw.level : null;\n const out = base('zcode', limits.length ? 'subscription' : 'unknown', raw, observedAt);\n out.plan = level ? { id: level, label: level } : null;\n let tools = 0;\n limits.forEach((limit, i) => {\n if (!limit || typeof limit !== 'object') return;\n const resetsAt = time(limit.nextResetTime, true);\n if (limit.type === 'TOKENS_LIMIT') {\n // GLM window convention: number 5 → the 5-hour prompt window, 7 → weekly.\n // Ids mirror the adapter's own /quota keys (token_5h / token_week).\n const seconds = limit.number === 5 ? 18000 : limit.number === 7 ? 604800 : null;\n const label = limit.number === 5 ? '5h' : limit.number === 7 ? 'Week' : `token ${limit.number ?? i}`;\n const id = limit.number === 5 ? 'zcode.token_5h' : limit.number === 7 ? 'zcode.token_week' : `zcode.token_${limit.number ?? i}`;\n out.limits.push({ id, label, kind: 'subscription', windowSeconds: seconds, key: windowKey(seconds), usedPercent: glmPercent(limit), resetsAt, status: 'unknown' });\n } else if (limit.type === 'TIME_LIMIT' || limit.type === 'MCP_LIMIT') {\n // Built-in tool/MCP allowance: absolute counters (unit 'calls'); per-model detail stays in _meta.\n const used = finite(limit.currentValue), total = finite(limit.usage), remaining = finite(limit.remaining);\n out.limits.push({ id: tools ? `zcode.mcp.${i}` : 'zcode.mcp', label: 'MCP', kind: 'subscription', windowSeconds: null, usedPercent: glmPercent(limit), ...(used !== null && total !== null ? { used: String(used), limit: String(total), unit: 'calls' } : {}), resetsAt, status: remaining === 0 ? 'blocked' : used !== null || limit.percentage != null ? 'allowed' : 'unknown' });\n tools++;\n }\n });\n return out;\n}\nfunction commandcode(raw, observedAt) {\n const subscription = raw.subscription?.data ?? raw.subscription;\n const windows = raw.credits?.windowLimits ?? raw.windowLimits;\n const subscribed = ['active','trialing','past_due'].includes(subscription?.status) || windows?.limited === true;\n const mode = subscribed ? 'subscription' : subscription || windows ? 'non_subscription' : 'unknown';\n const out = base('commandcode',mode,raw,observedAt);\n out.plan = subscription?.planId ? { id:subscription.planId,label:subscription.planId } : null;\n for (const [id, seconds] of [['fiveHour',18000],['weekly',604800]]) {\n const w=windows?.[id]; if (!w) continue;\n out.limits.push({ id:`commandcode.${id}`,label:id,kind:'subscription',windowSeconds:seconds,key:windowKey(seconds),usedPercent:w.cap > 0 ? percent(w.used / w.cap * 100) : null,used:String(w.used),limit:String(w.cap),unit:'credits',resetsAt:time(w.resetAt,true),status:w.exceeded === true ? 'blocked' : 'allowed' });\n }\n const credits=raw.credits?.credits ?? raw.credits;\n if (subscribed && credits?.monthlyCredits != null) {\n const cap=raw.monthly?.limit;\n const known=typeof cap==='number' && Number.isFinite(cap) && cap>0;\n const used=known ? Math.max(0,cap-credits.monthlyCredits) : null;\n out.limits.push({id:'commandcode.monthly',label:'Monthly',kind:'subscription',windowSeconds:null,period:'month',key:windowKey(null,'month'),usedPercent:known?percent(used/cap*100):null,resetsAt:time(subscription?.currentPeriodEnd,true),status:'unknown',...(known?{used:String(used),limit:String(cap),unit:'credits'}:{})});\n }\n for (const [id,label] of [['monthlyCredits','Monthly credits'],['purchasedCredits','Purchased credits'],['freeCredits','Free credits']]) if (credits?.[id] != null) out.balances.push({ id:`commandcode.${id}`,label,remaining:String(credits[id]),unit:'credits' });\n return out;\n}\n\nreturn {READ,UPDATED,CAPABILITY,codex,codexEvent,claude,claudeEvent,kimi,commandcode,zcode};\n})();\nconst axomQuotaNotify = snapshot => process.stdout.write(JSON.stringify({jsonrpc:\"2.0\",method:axomQuota.UPDATED,params:snapshot})+\"\\n\");\n/**\n * Entry point: wire the ZcodeAcpServer to a stdio ACP stream.\n *\n * The ACP"
29
28
  }
30
29
  ]
31
30
  }
@@ -0,0 +1,58 @@
1
+ {
2
+ "id": "zcode-env-account",
3
+ "package": "zcode-acp-server",
4
+ "versions": [
5
+ "0.80.0"
6
+ ],
7
+ "assets": [
8
+ {
9
+ "source": "zcode/env-account.mjs",
10
+ "target": "dist/config/axom-env-account.mjs",
11
+ "sha256": "b936067b9ee2ee42dfd9b331e5c642022c38a8739dc6e66d7bcfeb2e293ff046"
12
+ }
13
+ ],
14
+ "edits": [
15
+ {
16
+ "file": "dist/config/bridge-accounts.js",
17
+ "before_sha256": "aefcb48f6cf64085e3cf3fca76ec16a0e1edf65e34033a7be41a89ed1521492f",
18
+ "after_sha256": "935683330d9c23e50ded074a5218b7651a6344a5026dd83f7a3a3b0d9aadec97",
19
+ "before": "import { randomUUID } from \"node:crypto\";",
20
+ "after": "import { readEnvAccount } from \"./axom-env-account.mjs\";\nimport { randomUUID } from \"node:crypto\";"
21
+ },
22
+ {
23
+ "file": "dist/config/bridge-accounts.js",
24
+ "before_sha256": "935683330d9c23e50ded074a5218b7651a6344a5026dd83f7a3a3b0d9aadec97",
25
+ "after_sha256": "7000e117941758917c00f6564e1333385541e79777ef712c8a7870d454fbd7d1",
26
+ "before": "function overlayAccounts() {\n try {\n return readBridgeAccounts();\n }\n catch (error) {\n warnOnce(`bridge-accounts: ignoring saved accounts. ${error.message}`);\n return {};\n }\n}",
27
+ "after": "function overlayAccounts() {\n const env = readEnvAccount(codingPlanCatalog);\n try {\n return { ...readBridgeAccounts(), ...env };\n }\n catch (error) {\n warnOnce(`bridge-accounts: ignoring saved accounts. ${error.message}`);\n return env;\n }\n}"
28
+ },
29
+ {
30
+ "file": "dist/index.js",
31
+ "before_sha256": "00e43e449e3e428778a8b4ee2f1ab4535d28113bf7e1504e8215cba76d6629b4",
32
+ "after_sha256": "d07d3900a6d88e29d4e18d562b4298cddf362e792e5a861ad839ba79cc2c92e7",
33
+ "before": "const result = await server.initialize(ctx.params, ctx.client);",
34
+ "after": "if (process.env.ZCODE_API_KEY) (await import(\"./config/bridge-accounts.js\")).bridgeOverlays(); const result = await server.initialize(ctx.params, ctx.client);"
35
+ },
36
+ {
37
+ "file": "dist/index.js",
38
+ "before_sha256": "d07d3900a6d88e29d4e18d562b4298cddf362e792e5a861ad839ba79cc2c92e7",
39
+ "after_sha256": "f5945f9fa858c03ea183176c57b0c1794925779f0c8b26d9acd970316351c548",
40
+ "before": "const { buildAccountProviderConfig, codingPlanRequestAuthFor } = await import(\"./config/account-provider.js\");",
41
+ "after": "const { buildAccountProviderConfig, codingPlanRequestAuthFor, configProviderIdFor } = await import(\"./config/account-provider.js\");"
42
+ },
43
+ {
44
+ "file": "dist/index.js",
45
+ "before_sha256": "f5945f9fa858c03ea183176c57b0c1794925779f0c8b26d9acd970316351c548",
46
+ "after_sha256": "28fcf1c87700d0da9b7820e778d507208ae66c22b340d02f1c3719cba2888a62",
47
+ "before": "if (!provider?.access?.entitled) continue;",
48
+ "after": "if (!provider?.access?.entitled || (process.env.ZCODE_API_KEY && configProviderIdFor(providerId) !== process.env.ZCODE_PROVIDER)) continue;"
49
+ },
50
+ {
51
+ "file": "dist/handlers/session.js",
52
+ "before_sha256": "ba9c2d4c131356661d5c79a89c840cb2d7a5dca65005c1d65bb5dd72871a7dfe",
53
+ "after_sha256": "bdf68cef3906a597f5f568a3f53cca880391bd7f6ab5700ba8ed3a2572509d34",
54
+ "before": "const pinnedModel = process.env.ZCODE_MODEL;\n if (pinnedProvider && pinnedModel)",
55
+ "after": "const pinnedModel = process.env.ZCODE_MODEL ?? (process.env.ZCODE_API_KEY ? loadAllModels()[0]?.modelId : undefined);\n if (pinnedProvider && pinnedModel)"
56
+ }
57
+ ]
58
+ }
@@ -2,35 +2,34 @@
2
2
  "id": "zcode-models",
3
3
  "package": "zcode-acp-server",
4
4
  "versions": [
5
- "0.69.3",
6
- "0.69.4"
5
+ "0.80.0"
7
6
  ],
8
7
  "edits": [
9
8
  {
10
9
  "file": "dist/config/options.js",
11
- "before_sha256": "7f30e0dbf281280b7043cc686990eff3785bc62206a4b369313b27333cf9c330",
12
- "after_sha256": "4641e5097c667e9b351ee7f0dee89a5a517332f452b1fc878e0396775b9870f8",
10
+ "before_sha256": "40605e6a76bfe9df5af4101b3b09c66c63247e245645603a51c42a85044f7826",
11
+ "after_sha256": "508042a42e89da936df13f0330cb79669a4e876781bf178cc529ec2d8d8684e0",
13
12
  "before": "import { configProviderIdFor } from \"./account-provider.js\";",
14
13
  "after": "import { buildAccountProviderConfig as axomAccountProviders, codingPlanRequestAuthFor as axomPlanAuth, configProviderIdFor } from \"./account-provider.js\";"
15
14
  },
16
15
  {
17
16
  "file": "dist/config/options.js",
18
- "before_sha256": "4641e5097c667e9b351ee7f0dee89a5a517332f452b1fc878e0396775b9870f8",
19
- "after_sha256": "286ad8d2b6f734ae1168cbe2095c25660016db10eee6ccc4d285a64fe8cc2cd4",
17
+ "before_sha256": "508042a42e89da936df13f0330cb79669a4e876781bf178cc529ec2d8d8684e0",
18
+ "after_sha256": "e46ef7e6ff4418b54eacb333e8f55968abbbf9488427939925e126e95a555f61",
20
19
  "before": "/** Read the config.json contents (UTF-8). Throws on read/parse failure. */",
21
20
  "after": "function axomFreshInstallModels() {\n const fallback = { providerId: DEFAULT_PROVIDER_ID, providerName: DEFAULT_PROVIDER_NAME, modelId: DEFAULT_MODEL_ID };\n try {\n const out = [];\n for (const [pid, p] of Object.entries(axomAccountProviders()?.providers ?? {})) {\n if (!p?.access?.entitled || !axomPlanAuth(pid)?.apiKey) continue;\n const providerId = configProviderIdFor(pid);\n for (const modelId of p.builtinModelIds ?? []) {\n if (out.some((m) => m.providerId === providerId && m.modelId === modelId)) continue;\n out.push({ providerId, providerName: providerId, modelId });\n }\n }\n if (out.length === 0) return [fallback];\n return out.some((m) => m.providerId === fallback.providerId && m.modelId === fallback.modelId)\n ? [fallback, ...out.filter((m) => !(m.providerId === fallback.providerId && m.modelId === fallback.modelId))]\n : [fallback, ...out];\n }\n catch {\n return [fallback];\n }\n}\n\n/** Read the config.json contents (UTF-8). Throws on read/parse failure. */"
22
21
  },
23
22
  {
24
23
  "file": "dist/config/options.js",
25
- "before_sha256": "286ad8d2b6f734ae1168cbe2095c25660016db10eee6ccc4d285a64fe8cc2cd4",
26
- "after_sha256": "c6fae88f596ee0cbb633317c0f83c2c883a2b2a5d8978bfc4fd6a6d152f77ccb",
24
+ "before_sha256": "e46ef7e6ff4418b54eacb333e8f55968abbbf9488427939925e126e95a555f61",
25
+ "after_sha256": "e15b0b9977c6be120a53fba1fa282b4fd00194f9f49ebc94b221a8aa3bd04101",
27
26
  "before": " if (out.length === 0 && Object.keys(cfg.provider ?? {}).length === 0) {\n return [\n {\n providerId: DEFAULT_PROVIDER_ID,\n providerName: DEFAULT_PROVIDER_NAME,\n modelId: DEFAULT_MODEL_ID,\n },\n ];\n }",
28
27
  "after": " if (out.length === 0 && Object.keys(cfg.provider ?? {}).length === 0) {\n return axomFreshInstallModels();\n }"
29
28
  },
30
29
  {
31
30
  "file": "dist/config/options.js",
32
- "before_sha256": "c6fae88f596ee0cbb633317c0f83c2c883a2b2a5d8978bfc4fd6a6d152f77ccb",
33
- "after_sha256": "d12eded70f22d8d9010de9949c0809b1f56f2c33635899764ca9f893fec0d256",
31
+ "before_sha256": "e15b0b9977c6be120a53fba1fa282b4fd00194f9f49ebc94b221a8aa3bd04101",
32
+ "after_sha256": "e4624ca2590a3e577eeb73a7e7f9a961623dd486a5d78d238ed1db7a09a16a16",
34
33
  "before": " if (out.length > 0)\n return out;\n return [\n {\n providerId: DEFAULT_PROVIDER_ID,\n providerName: DEFAULT_PROVIDER_NAME,\n modelId: DEFAULT_MODEL_ID,\n },\n ];\n }\n}",
35
34
  "after": " if (out.length > 0)\n return out;\n return axomFreshInstallModels();\n }\n}"
36
35
  }
@@ -2,8 +2,7 @@
2
2
  "id": "zcode-subagent-subwork",
3
3
  "package": "zcode-acp-server",
4
4
  "versions": [
5
- "0.69.3",
6
- "0.69.4"
5
+ "0.80.0"
7
6
  ],
8
7
  "edits": [
9
8
  {
@@ -29,15 +28,15 @@
29
28
  },
30
29
  {
31
30
  "file": "dist/server.js",
32
- "before_sha256": "547d640a68ad56a97bd587348ea38ee3eca57d1fad9ea1d14e04c486e1efc85a",
33
- "after_sha256": "df37fa0b7e5873d13a2cdd71a5fbc931a7ea9ffca521b0218d60a7a9b11e1d3c",
31
+ "before_sha256": "42db5e33c8abdceaede811ae6ca318a08c23d9681052299c7226ddc41fe561b9",
32
+ "after_sha256": "e29a42ff87e89ab8141425b47c70bb12d97f7beba6cf2cc99a410fd779b04596",
34
33
  "before": "export class ZcodeAcpServer {",
35
- "after": "/** Axom patch `zcode-subagent-subwork`: normalize ZCode sub-agents and background\n * tasks into the draft ACP subagent variants (RFD #1992) and the AIR async-task\n * extension — the same contracts claude-agent-acp ships. The backend already emits\n * first-class lifecycle events on `session.updated` (agentId/childSessionId for\n * sub-agents, taskId for background tasks); this listener forwards them so hosts\n * can project subworks, and dispatch.js re-scopes a foreground sub-agent's internal\n * `tool_subagent_*` tool calls onto its child session. */\nclass AxomZcodeSubagents {\n constructor(server, zcodeSid) {\n this.server = server;\n this.sid = zcodeSid;\n /** agentId → { agentId, child, background, terminal, pendingState, pendingP, resultSeen, preview } */\n this.agents = new Map();\n /** parentToolCallId → agent record (foreground result flush). */\n this.parents = new Map();\n /** taskId → { terminal } (bash/exec background tasks only). */\n this.tasks = new Map();\n }\n /** Draft RFD #1992 negotiated — the axom daemon always declares this. */\n nativeOk() {\n const caps = this.server.clientCapabilities;\n return !!(caps && caps.subagents);\n }\n /** AIR async-task capability declared (`_meta.jetbrains.air.capabilities`). */\n asyncOk() {\n const air = this.server.clientCapabilities?._meta?.jetbrains?.air;\n return !!(air && Array.isArray(air.capabilities) && air.capabilities.includes(\"asyncTasks\"));\n }\n handleEvent(ev) {\n try {\n void this.onEvent(ev);\n }\n catch {\n // Never crash the bridge from a normalization observer.\n }\n }\n async onEvent(ev) {\n if (!ev || ev.type !== \"session.updated\") {\n this.onToolEvent(ev);\n return;\n }\n const p = ev.payload ?? {};\n if (typeof p.agentId === \"string\" && p.agentId && typeof p.childSessionId === \"string\" && p.childSessionId) {\n this.onAgent(p);\n return;\n }\n // Sub-agent background tasks also carry taskId with taskKind \"subagent\" —\n // the agentId path above owns those. Workflow runs have their own poller.\n if (typeof p.taskId === \"string\" && p.taskId && p.taskKind !== \"subagent\" && p.taskKind !== \"workflow\") {\n this.onTask(p);\n }\n }\n onAgent(p) {\n if (!this.nativeOk())\n return;\n const agentId = p.agentId;\n const child = p.childSessionId;\n const status = typeof p.status === \"string\" ? p.status : \"running\";\n let rec = this.agents.get(agentId);\n if (!rec) {\n rec = { agentId, child, background: p.background === true, terminal: false, pendingState: null, pendingP: null, resultSeen: false, preview: null };\n this.agents.set(agentId, rec);\n if (typeof p.parentToolCallId === \"string\" && p.parentToolCallId)\n this.parents.set(p.parentToolCallId, rec);\n // First sighting already terminal (adapter restart): adopt — emit the\n // spawn so the host can still link the child, then the state below.\n if (status !== \"running\")\n rec.resultSeen = true;\n this.emit({\n sessionUpdate: \"subagent_spawned\",\n subagentSessionId: child,\n name: (typeof p.description === \"string\" && p.description) || (typeof p.agentType === \"string\" && p.agentType) || \"Subagent\",\n task: typeof p.prompt === \"string\" ? p.prompt : (p.description ?? \"\"),\n capabilities: {},\n _meta: { zcode: { agentId, parentToolCallId: p.parentToolCallId, background: rec.background, adopted: status !== \"running\" } },\n });\n }\n if (rec.child !== child)\n return;\n const mapped = status === \"completed\" ? \"completed\"\n : status === \"failed\" || status === \"error\" ? \"failed\"\n : status === \"cancelled\" || status === \"stopped\" ? \"cancelled\"\n : status === \"disconnected\" ? \"disconnected\" : null;\n if (!mapped || rec.terminal)\n return;\n if (rec.background || rec.resultSeen || mapped !== \"completed\") {\n this.finishAgent(rec, mapped, p);\n return;\n }\n // Foreground completion: hold the state until the parent Agent tool's own\n // result event arrives (one event later, measured) so the child-scoped\n // preview precedes the terminal state on the wire.\n rec.pendingState = mapped;\n rec.pendingP = p;\n }\n finishAgent(rec, state, p) {\n rec.terminal = true;\n if (rec.preview) {\n this.emitChild(rec.child, { sessionUpdate: \"agent_message_chunk\", content: { type: \"text\", text: rec.preview } });\n rec.preview = null;\n }\n const meta = { zcode: { agentId: rec.agentId } };\n if (p) {\n for (const key of [\"parentToolCallId\", \"outputFile\"])\n if (typeof p[key] === \"string\")\n meta.zcode[key] = p[key];\n if (typeof p.totalTokens === \"number\")\n meta.zcode.totalTokens = p.totalTokens;\n if (typeof p.totalDurationMs === \"number\")\n meta.zcode.totalDurationMs = p.totalDurationMs;\n }\n this.emit({ sessionUpdate: \"subagent_state_update\", subagentSessionId: rec.child, state, _meta: meta });\n }\n onToolEvent(ev) {\n if (!ev || ev.type !== \"tool.updated\")\n return;\n const p = ev.payload ?? {};\n if (p.kind !== \"result\" || typeof p.toolCallId !== \"string\")\n return;\n const rec = this.parents.get(p.toolCallId);\n if (!rec || rec.terminal || rec.background)\n return;\n rec.resultSeen = true;\n const text = axomSubagentResultPreview(p.result);\n if (text)\n rec.preview = text;\n if (rec.pendingState)\n this.finishAgent(rec, rec.pendingState, rec.pendingP);\n }\n onTask(p) {\n if (!this.asyncOk())\n return;\n const taskId = p.taskId;\n let task = this.tasks.get(taskId);\n const status = typeof p.status === \"string\" ? p.status : \"running\";\n if (!task) {\n task = { terminal: false };\n this.tasks.set(taskId, task);\n this.emit({\n sessionUpdate: \"async_task_spawned\",\n asyncTaskId: taskId,\n name: (typeof p.description === \"string\" && p.description) || (typeof p.command === \"string\" && p.command) || \"background task\",\n taskType: typeof p.taskKind === \"string\" ? p.taskKind : \"shell\",\n description: typeof p.description === \"string\" ? p.description : \"\",\n showInTranscript: false,\n canStop: p.cancellable === true,\n ...(typeof p.outputPath === \"string\" ? { outputFilePath: p.outputPath } : {}),\n ...(typeof p.toolCallId === \"string\" ? { toolCallId: p.toolCallId } : {}),\n _meta: { zcode: { taskKind: p.taskKind, command: p.command } },\n });\n }\n const mapped = status === \"completed\" ? \"completed\"\n : status === \"failed\" || status === \"error\" ? \"failed\"\n : status === \"cancelled\" ? \"stopped\" : null;\n if (!mapped || task.terminal)\n return;\n task.terminal = true;\n this.emit({\n sessionUpdate: \"async_task_state_update\",\n asyncTaskId: taskId,\n state: mapped,\n ...(typeof p.toolCallId === \"string\" ? { toolCallId: p.toolCallId } : {}),\n });\n }\n emit(update) {\n void this.server.notifyByZcodeSid(this.sid, update);\n }\n emitChild(childId, update) {\n const server = this.server;\n if (!server.clients || server.clients.size === 0)\n return;\n void enqueueSessionSend(childId, () => server.clients.broadcast().notify(\"session/update\", { sessionId: childId, update }));\n }\n}\n/** The parent Agent tool's result carries the sub-agent's final message plus an\n * `agentId:` line and a `<usage>` block that belong to the envelope, not the\n * answer. The backend delivers the payload as a JSON string or as an already\n * parsed object (both measured) — accept either. */\nfunction axomSubagentResultPreview(result) {\n let content = null;\n if (typeof result === \"string\") {\n try {\n const parsed = JSON.parse(result);\n if (parsed && typeof parsed.content === \"string\")\n content = parsed.content;\n }\n catch {\n content = result;\n }\n }\n else if (result && typeof result === \"object\" && typeof result.content === \"string\") {\n content = result.content;\n }\n if (typeof content !== \"string\" || !content.trim())\n return null;\n const stripped = content\n .replace(/<usage>[\\s\\S]*?<\\/usage>/g, \"\")\n .split(\"\\n\")\n .filter((line) => !/^agentId:\\s*\\S+/.test(line))\n .join(\"\\n\")\n .trim();\n return stripped ? stripped.slice(0, 2000) : null;\n}\nexport { AxomZcodeSubagents };\nexport class ZcodeAcpServer {"
34
+ "after": "/** Axom patch `zcode-subagent-subwork`: normalize ZCode sub-agents and background\n * tasks into the draft ACP subagent variants (RFD #1992) and the AIR async-task\n * extension — the same contracts claude-agent-acp ships. The backend already emits\n * first-class lifecycle events on `session.updated` (agentId/childSessionId for\n * sub-agents, taskId for background tasks); this listener forwards them so hosts\n * can project subworks, and dispatch.js re-scopes a foreground sub-agent's internal\n * `tool_subagent_*` tool calls onto its child session. */\nclass AxomZcodeSubagents {\n constructor(server, zcodeSid) {\n this.server = server;\n this.sid = zcodeSid;\n /** agentId → { agentId, child, background, terminal, pendingState, pendingP, resultSeen, preview } */\n this.agents = new Map();\n /** parentToolCallId → agent record (foreground result flush). */\n this.parents = new Map();\n /** taskId → { terminal } (bash/exec background tasks only). */\n this.tasks = new Map();\n }\n /** Draft RFD #1992 negotiated — the axom daemon always declares this. */\n nativeOk() {\n const caps = this.server.clientCapabilities;\n return !!(caps && caps.subagents);\n }\n /** AIR async-task capability declared (`_meta.jetbrains.air.capabilities`). */\n asyncOk() {\n const air = this.server.clientCapabilities?._meta?.jetbrains?.air;\n return !!(air && Array.isArray(air.capabilities) && air.capabilities.includes(\"asyncTasks\"));\n }\n handleEvent(ev) {\n try {\n void this.onEvent(ev);\n }\n catch {\n // Never crash the bridge from a normalization observer.\n }\n }\n async onEvent(ev) {\n if (!ev || ev.type !== \"session.updated\") {\n this.onToolEvent(ev);\n return;\n }\n const p = ev.payload ?? {};\n if (typeof p.agentId === \"string\" && p.agentId && typeof p.childSessionId === \"string\" && p.childSessionId) {\n this.onAgent(p);\n return;\n }\n // Sub-agent background tasks also carry taskId with taskKind \"subagent\" —\n // the agentId path above owns those. Workflow runs have their own poller.\n if (typeof p.taskId === \"string\" && p.taskId && p.taskKind !== \"subagent\" && p.taskKind !== \"workflow\") {\n this.onTask(p);\n }\n }\n onAgent(p) {\n if (!this.nativeOk())\n return;\n const agentId = p.agentId;\n const child = p.childSessionId;\n const status = typeof p.status === \"string\" ? p.status : \"running\";\n let rec = this.agents.get(agentId);\n if (!rec) {\n rec = { agentId, child, background: p.background === true, terminal: false, pendingState: null, pendingP: null, resultSeen: false, preview: null };\n this.agents.set(agentId, rec);\n if (typeof p.parentToolCallId === \"string\" && p.parentToolCallId)\n this.parents.set(p.parentToolCallId, rec);\n // First sighting already terminal (adapter restart): adopt — emit the\n // spawn so the host can still link the child, then the state below.\n if (status !== \"running\")\n rec.resultSeen = true;\n this.emit({\n sessionUpdate: \"subagent_spawned\",\n subagentSessionId: child,\n name: (typeof p.description === \"string\" && p.description) || (typeof p.agentType === \"string\" && p.agentType) || \"Subagent\",\n task: typeof p.prompt === \"string\" ? p.prompt : (p.description ?? \"\"),\n capabilities: {},\n _meta: { zcode: { agentId, parentToolCallId: p.parentToolCallId, background: rec.background, adopted: status !== \"running\" } },\n });\n }\n if (rec.child !== child)\n return;\n const mapped = status === \"completed\" ? \"completed\"\n : status === \"failed\" || status === \"error\" ? \"failed\"\n : status === \"cancelled\" || status === \"stopped\" ? \"cancelled\"\n : status === \"disconnected\" ? \"disconnected\" : null;\n if (!mapped || rec.terminal)\n return;\n if (rec.background || rec.resultSeen || mapped !== \"completed\") {\n this.finishAgent(rec, mapped, p);\n return;\n }\n // Foreground completion: hold the state until the parent Agent tool's own\n // result event arrives (one event later, measured) so the child-scoped\n // preview precedes the terminal state on the wire.\n rec.pendingState = mapped;\n rec.pendingP = p;\n }\n finishAgent(rec, state, p) {\n rec.terminal = true;\n if (rec.preview) {\n this.emitChild(rec.child, { sessionUpdate: \"agent_message_chunk\", content: { type: \"text\", text: rec.preview } });\n rec.preview = null;\n }\n const meta = { zcode: { agentId: rec.agentId } };\n if (p) {\n for (const key of [\"parentToolCallId\", \"outputFile\"])\n if (typeof p[key] === \"string\")\n meta.zcode[key] = p[key];\n if (typeof p.totalTokens === \"number\")\n meta.zcode.totalTokens = p.totalTokens;\n if (typeof p.totalDurationMs === \"number\")\n meta.zcode.totalDurationMs = p.totalDurationMs;\n }\n this.emit({ sessionUpdate: \"subagent_state_update\", subagentSessionId: rec.child, state, _meta: meta });\n }\n onToolEvent(ev) {\n if (!ev || ev.type !== \"tool.updated\")\n return;\n const p = ev.payload ?? {};\n if (p.kind !== \"result\" || typeof p.toolCallId !== \"string\")\n return;\n const rec = this.parents.get(p.toolCallId);\n if (!rec || rec.terminal || rec.background)\n return;\n rec.resultSeen = true;\n const text = axomSubagentResultPreview(p.result);\n if (text)\n rec.preview = text;\n if (rec.pendingState)\n this.finishAgent(rec, rec.pendingState, rec.pendingP);\n }\n onTask(p) {\n if (!this.asyncOk())\n return;\n const taskId = p.taskId;\n let task = this.tasks.get(taskId);\n const status = typeof p.status === \"string\" ? p.status : \"running\";\n if (!task) {\n task = { terminal: false };\n this.tasks.set(taskId, task);\n this.emit({\n sessionUpdate: \"async_task_spawned\",\n asyncTaskId: taskId,\n name: (typeof p.description === \"string\" && p.description) || (typeof p.command === \"string\" && p.command) || \"background task\",\n taskType: typeof p.taskKind === \"string\" ? p.taskKind : \"shell\",\n description: typeof p.description === \"string\" ? p.description : \"\",\n showInTranscript: false,\n canStop: p.cancellable === true,\n ...(typeof p.outputPath === \"string\" ? { outputFilePath: p.outputPath } : {}),\n ...(typeof p.toolCallId === \"string\" ? { toolCallId: p.toolCallId } : {}),\n _meta: { zcode: { taskKind: p.taskKind, command: p.command } },\n });\n }\n // Preserve cumulative backend output without reviving already settled tasks.\n const output = p.outputTail ?? p.stdoutTail;\n if (!task.terminal && typeof output === \"string\" && output && output !== task.outputTail) {\n task.outputTail = output;\n this.emit({\n sessionUpdate: \"async_task_progress\", asyncTaskId: taskId,\n ...(typeof p.toolCallId === \"string\" ? { toolCallId: p.toolCallId } : {}),\n ...(typeof p.outputPath === \"string\" ? { outputFilePath: p.outputPath } : {}),\n _meta: { zcode: { outputTail: output } },\n });\n }\n const mapped = status === \"completed\" ? \"completed\"\n : status === \"failed\" || status === \"error\" ? \"failed\"\n : status === \"cancelled\" ? \"stopped\" : null;\n if (!mapped || task.terminal)\n return;\n task.terminal = true;\n this.emit({\n sessionUpdate: \"async_task_state_update\",\n asyncTaskId: taskId,\n state: mapped,\n ...(typeof p.toolCallId === \"string\" ? { toolCallId: p.toolCallId } : {}),\n });\n }\n emit(update) {\n void this.server.notifyByZcodeSid(this.sid, update);\n }\n emitChild(childId, update) {\n const server = this.server;\n if (!server.clients || server.clients.size === 0)\n return;\n void enqueueSessionSend(childId, () => server.clients.broadcast().notify(\"session/update\", { sessionId: childId, update }));\n }\n}\n/** The parent Agent tool's result carries the sub-agent's final message plus an\n * `agentId:` line and a `<usage>` block that belong to the envelope, not the\n * answer. The backend delivers the payload as a JSON string or as an already\n * parsed object (both measured) — accept either. */\nfunction axomSubagentResultPreview(result) {\n let content = null;\n if (typeof result === \"string\") {\n try {\n const parsed = JSON.parse(result);\n if (parsed && typeof parsed.content === \"string\")\n content = parsed.content;\n }\n catch {\n content = result;\n }\n }\n else if (result && typeof result === \"object\" && typeof result.content === \"string\") {\n content = result.content;\n }\n if (typeof content !== \"string\" || !content.trim())\n return null;\n const stripped = content\n .replace(/<usage>[\\s\\S]*?<\\/usage>/g, \"\")\n .split(\"\\n\")\n .filter((line) => !/^agentId:\\s*\\S+/.test(line))\n .join(\"\\n\")\n .trim();\n return stripped ? stripped.slice(0, 2000) : null;\n}\nexport { AxomZcodeSubagents };\nexport class ZcodeAcpServer {"
36
35
  },
37
36
  {
38
37
  "file": "dist/server.js",
39
- "before_sha256": "df37fa0b7e5873d13a2cdd71a5fbc931a7ea9ffca521b0218d60a7a9b11e1d3c",
40
- "after_sha256": "55d776521298e0a59d7512be34aeb2b86bc10a4cce9b038a9387ab860ab1aa2c",
38
+ "before_sha256": "e29a42ff87e89ab8141425b47c70bb12d97f7beba6cf2cc99a410fd779b04596",
39
+ "after_sha256": "00aa402eebd8537507c324371b3baaad851f3e409c97b3394b8c3e7fefc0e498",
41
40
  "before": " backend.registerEventListener(zcodeSid, new SessionTitleListener(this, zcodeSid));\n this.backgroundListenerBackend.set(zcodeSid, backend);",
42
41
  "after": " backend.registerEventListener(zcodeSid, new SessionTitleListener(this, zcodeSid));\n backend.registerEventListener(zcodeSid, new AxomZcodeSubagents(this, zcodeSid));\n this.backgroundListenerBackend.set(zcodeSid, backend);"
43
42
  },
@@ -57,15 +56,15 @@
57
56
  },
58
57
  {
59
58
  "file": "dist/handlers/session.js",
60
- "before_sha256": "e36756aa50f823d97df4861566fafc2ea6b36d7baf6d1f9aa9f918930e1c40a6",
61
- "after_sha256": "799343ea4036b518ad7d411d192c696ebe821a4eb2b32e87c0b8b5f288cf509f",
59
+ "before_sha256": "d78d4a702ff01fc539de62c84cdb04c5a5c1486dc6de6554edf2b8ac5c3b788d",
60
+ "after_sha256": "cf260853e2940ce841f8ac0f8788574a30a047c4da38006099ecca4bf1bced72",
62
61
  "before": "async function adoptStoredTitle(server, acpSid, zcodeSid) {\n if (server.sessionTitles.has(acpSid))\n return;",
63
62
  "after": "async function adoptStoredTitle(server, acpSid, zcodeSid) {\n // Axom patch `zcode-subagent-subwork`: the in-memory title is usually the first\n // prompt's echo; the backend's stored generated title is the real answer, and the\n // live titleUpdated push is routinely lost at a backend respawn boundary — this\n // load-time read is then the only surviving copy. A user-pinned rename still wins.\n if (server.sessionTitles.has(acpSid) && server.titleUserSetBy.has(acpSid))\n return;"
64
63
  },
65
64
  {
66
65
  "file": "dist/handlers/session.js",
67
- "before_sha256": "799343ea4036b518ad7d411d192c696ebe821a4eb2b32e87c0b8b5f288cf509f",
68
- "after_sha256": "e28f651c842a85e308e12f3e97d742bcb49a2f470530f7fe3babe4c04fdca4c9",
66
+ "before_sha256": "cf260853e2940ce841f8ac0f8788574a30a047c4da38006099ecca4bf1bced72",
67
+ "after_sha256": "ba9c2d4c131356661d5c79a89c840cb2d7a5dca65005c1d65bb5dd72871a7dfe",
69
68
  "before": " if (hit?.title) {\n server.sessionTitles.set(acpSid, hit.title);\n server.touchSessionSummary(acpSid, hit.title);\n refreshTerminalTabTitle(server, acpSid);\n log(`adopted stored title for ${acpSid.slice(0, 8)}: ${hit.title}`);\n }",
70
69
  "after": " if (hit?.title && server.sessionTitles.get(acpSid) !== hit.title) {\n server.sessionTitles.set(acpSid, hit.title);\n server.touchSessionSummary(acpSid, hit.title);\n refreshTerminalTabTitle(server, acpSid);\n // Axom patch `zcode-subagent-subwork`: broadcast so hosts sync the stored\n // generated title on (re-)attach — the live push was lost at a respawn.\n void sendSessionUpdate(server.clients.broadcast(), acpSid, {\n sessionUpdate: \"session_info_update\",\n title: hit.title,\n updatedAt: new Date().toISOString(),\n }).catch(() => { });\n log(`adopted stored title for ${acpSid.slice(0, 8)}: ${hit.title}`);\n }"
71
70
  }
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { realpathSync } from 'node:fs';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
5
  import { authAction, logoutAction } from './commands/auth.js';
6
6
  import { registerExecuteCommand } from './commands/execute.js';
7
- import { registerGuideCommands } from './commands/guide.js';
7
+ import { registerKnowledgeCommands } from './commands/knowledge.js';
8
8
  import { registerRecordCommands } from './commands/record.js';
9
9
  import { registerSkillsCommands } from './commands/skills.js';
10
10
  import { registerStructCommands } from './commands/struct.js';
@@ -13,9 +13,9 @@ import { registerWorkstationCommands } from './commands/workstation.js';
13
13
  import { whoamiAction } from './commands/whoami.js';
14
14
  import { runCredentialBroker } from './credentials.js';
15
15
  import { runCommand } from './output.js';
16
- import { install, restart, start, status, stop, uninstall } from './workstation/daemonctl.js';
16
+ import { clientDir } from './workstation/config.js';
17
+ import { executeDeviceLifecycle, inspectLocalDevice } from './workstation/control/deviceCommands.js';
17
18
  import { runDaemon } from './workstation/daemon.js';
18
- import { registerWorkstation } from './workstation/register.js';
19
19
  import { detectSupervision } from './workstation/supervision.js';
20
20
  import { CLIENT_VERSION } from './workstation/version.js';
21
21
  export function buildProgram() {
@@ -39,58 +39,24 @@ export function buildProgram() {
39
39
  program
40
40
  .command('__credential-broker', { hidden: true })
41
41
  .action(() => runCredentialBroker());
42
- const qoderCommand = program.command('qoder-acp')
43
- .description('Prepare or run the Qoder SDK ACP adapter')
44
- .option('--qodercli <path>', 'Explicit Qoder CLI executable')
45
- .option('--prepare-sdk', 'Install the separate SDK runtime, then exit');
46
- qoderCommand.action(async (options) => {
47
- if (Boolean(options.qodercli) === Boolean(options.prepareSdk))
48
- qoderCommand.error('Choose exactly one of --prepare-sdk or --qodercli <path>');
49
- try {
50
- if (options.prepareSdk) {
51
- const { prepareSdkRuntime } = await import('./workstation/qoderAdapter/sdkRuntimeInstall.js');
52
- const controller = new AbortController();
53
- const cancel = () => controller.abort();
54
- process.once('SIGINT', cancel);
55
- process.once('SIGTERM', cancel);
56
- try {
57
- console.error('Preparing Qoder SDK…');
58
- const result = await prepareSdkRuntime({ signal: controller.signal });
59
- console.error(`Qoder SDK ready${result.alreadyInstalled ? ' (already prepared)' : ''}: ${result.instance}`);
60
- }
61
- finally {
62
- process.removeListener('SIGINT', cancel);
63
- process.removeListener('SIGTERM', cancel);
64
- }
65
- }
66
- else {
67
- const { runQoderAdapter } = await import('./workstation/qoderAdapter/main.js');
68
- await runQoderAdapter(options.qodercli);
69
- }
70
- }
71
- catch (error) {
72
- console.error(error instanceof Error ? error.message : String(error));
73
- process.exitCode = 1;
74
- }
75
- });
76
42
  registerStructCommands(program);
77
43
  registerRecordCommands(program);
78
44
  registerExecuteCommand(program);
79
- registerGuideCommands(program);
45
+ registerKnowledgeCommands(program);
80
46
  registerSkillsCommands(program);
81
47
  registerUpdateCommand(program);
82
48
  registerWorkstationCommands(program, {
83
- start, stop, restart, install, uninstall, runDaemon, registerWorkstation,
49
+ start: () => executeDeviceLifecycle('start', clientDir()),
50
+ stop: () => executeDeviceLifecycle('stop', clientDir()),
51
+ restart: () => executeDeviceLifecycle('restart', clientDir()),
52
+ install: () => executeDeviceLifecycle('install', clientDir()),
53
+ uninstall: () => executeDeviceLifecycle('uninstall', clientDir()), runDaemon,
84
54
  status: async () => {
85
- const current = await status();
86
- return current.running
87
- ? `running connected to ${current.server ?? 'unknown server'} as ${current.workstationId ?? 'unknown workstation'}`
88
- : 'stopped';
55
+ const current = await inspectLocalDevice(clientDir());
56
+ return `${current.runtime.state}; ${current.registrations.length} workstation registrations`;
89
57
  },
90
58
  log: console.log,
91
- authenticate: (server) => authAction({ server }),
92
59
  detectSupervision,
93
- isInteractive: () => Boolean(process.stdin.isTTY),
94
60
  });
95
61
  return program;
96
62
  }
@@ -5,9 +5,12 @@ import { authorizeDevice } from '../workstation/oauth/deviceAuthorization.js';
5
5
  import { revokeAndClearTokens, revokeRefreshToken, saveTokens } from '../credentials.js';
6
6
  import { cliRequestedScopes } from '../authScopes.js';
7
7
  export const DEFAULT_SERVER_URL = 'http://localhost:8080';
8
+ export function resolveAuthServer(config, explicit) {
9
+ return resolveServer(config, explicit ?? config.defaultServer ?? DEFAULT_SERVER_URL);
10
+ }
8
11
  export async function authAction(options, deps = {}) {
9
12
  const config = await loadConfig();
10
- const server = resolveServer(config, options.server ?? config.defaultServer ?? DEFAULT_SERVER_URL);
13
+ const server = resolveAuthServer(config, options.server);
11
14
  const host = hostname();
12
15
  const { accessToken, refreshToken, expiresInSeconds, scopes } = await authorizeDevice(server, `Axomwork CLI (${host})`, host, deps, cliRequestedScopes(options.cowork));
13
16
  const updated = await loadConfig();
@@ -0,0 +1,47 @@
1
+ import { loadConfig, resolveServer, UsageError } from '../config.js';
2
+ import { callAxomTool } from '../mcp.js';
3
+ import { printOutcome, runCommand } from '../output.js';
4
+ export const GUIDE_RENAMED_MESSAGE = 'axom guide was renamed: use axom knowledge get|search';
5
+ export function registerKnowledgeCommands(program) {
6
+ const knowledge = program.command('knowledge').description('Read and search the Axomwork manual');
7
+ const serverOption = () => program.opts().server;
8
+ knowledge
9
+ .command('get')
10
+ .description('Get a manual page, or the table of contents when no path is given')
11
+ .argument('[path]', 'manual page path, e.g. script-language/sdk/records.md')
12
+ .option('--locale <locale>', 'manual locale (en|zh)')
13
+ .action((path, options) => runCommand(() => knowledgeGetAction({ path, locale: options.locale, server: serverOption() })));
14
+ knowledge
15
+ .command('search')
16
+ .description('Search the manual with keywords or a question')
17
+ .argument('<query>')
18
+ .option('--locale <locale>', 'manual locale (en|zh)')
19
+ .option('--limit <n>', 'maximum hits')
20
+ .action((query, options) => runCommand(() => knowledgeSearchAction({ query, locale: options.locale, limit: options.limit, server: serverOption() })));
21
+ // Deliberately not an alias: old scripts must fail loudly and learn the new name.
22
+ program
23
+ .command('guide', { hidden: true })
24
+ .allowUnknownOption()
25
+ .allowExcessArguments()
26
+ .argument('[args...]')
27
+ .helpOption(false)
28
+ .action(() => runCommand(async () => { throw new UsageError(GUIDE_RENAMED_MESSAGE); }));
29
+ }
30
+ export async function knowledgeGetAction(options) {
31
+ const server = resolveServer(await loadConfig(), options.server);
32
+ const args = {};
33
+ if (options.path !== undefined)
34
+ args.path = options.path;
35
+ if (options.locale !== undefined)
36
+ args.locale = options.locale;
37
+ printOutcome(await callAxomTool(server, 'axom__knowledge__get', args));
38
+ }
39
+ export async function knowledgeSearchAction(options) {
40
+ const server = resolveServer(await loadConfig(), options.server);
41
+ const args = { query: options.query };
42
+ if (options.locale !== undefined)
43
+ args.locale = options.locale;
44
+ if (options.limit !== undefined)
45
+ args.limit = Number(options.limit);
46
+ printOutcome(await callAxomTool(server, 'axom__knowledge__search', args));
47
+ }