@platless/axom-cli 0.3.13 → 0.5.9

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 (235) hide show
  1. package/dist/axom-patch/cli/README.md +30 -8
  2. package/dist/axom-patch/cli/account-limits/README.md +27 -0
  3. package/dist/axom-patch/cli/account-limits/antigravity-proxy.mjs +47 -0
  4. package/dist/axom-patch/cli/account-limits/antigravity-read.mjs +90 -0
  5. package/dist/axom-patch/cli/account-limits/antigravity.mjs +46 -0
  6. package/dist/axom-patch/cli/account-limits/generate.py +120 -0
  7. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +64 -0
  8. package/dist/axom-patch/cli/account-limits/normalize.mjs +151 -0
  9. package/dist/axom-patch/cli/claude-account-limits.json +37 -0
  10. package/dist/axom-patch/cli/codex-account-limits.json +37 -0
  11. package/dist/axom-patch/cli/commandcode-account-limits.json +30 -0
  12. package/dist/axom-patch/cli/nightly-task.md +152 -0
  13. package/dist/axom-patch/cli/pi-subagent-subwork.json +23 -0
  14. package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +5 -1
  15. package/dist/axom-patch/cli/probes/pi-subagent-subwork.mjs +211 -0
  16. package/dist/axom-patch/cli/probes/qoder-steering.mjs +1 -1
  17. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +20 -8
  18. package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +257 -0
  19. package/dist/axom-patch/cli/qoder-background-terminal.md +2 -1
  20. package/dist/axom-patch/cli/qoder-foreground-output.md +4 -3
  21. package/dist/axom-patch/cli/qoder-steering.json +15 -15
  22. package/dist/axom-patch/cli/qoder-subagent-origin.json +21 -21
  23. package/dist/axom-patch/cli/runtime.json +34 -8
  24. package/dist/axom-patch/cli/zcode-account-limits.json +31 -0
  25. package/dist/axom-patch/cli/zcode-models.json +38 -0
  26. package/dist/axom-patch/cli/zcode-subagent-subwork.json +73 -0
  27. package/dist/cli.js +33 -6
  28. package/dist/keepers/acp/keeper.mjs +3083 -0
  29. package/dist/keepers/acp/manifest.json +9 -0
  30. package/dist/keepers/terminal/keeper.mjs +8469 -0
  31. package/dist/keepers/terminal/manifest.json +9 -0
  32. package/dist/keepers/terminal/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  33. package/dist/keepers/terminal/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  34. package/dist/keepers/terminal/node_modules/node-pty/lib/index.js +52 -0
  35. package/dist/keepers/terminal/node_modules/node-pty/lib/interfaces.js +7 -0
  36. package/dist/keepers/terminal/node_modules/node-pty/lib/shared/conout.js +10 -0
  37. package/dist/keepers/terminal/node_modules/node-pty/lib/terminal.js +190 -0
  38. package/dist/keepers/terminal/node_modules/node-pty/lib/types.js +7 -0
  39. package/dist/keepers/terminal/node_modules/node-pty/lib/unixTerminal.js +351 -0
  40. package/dist/keepers/terminal/node_modules/node-pty/lib/utils.js +38 -0
  41. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
  42. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
  43. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsTerminal.js +209 -0
  44. package/dist/keepers/terminal/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  45. package/dist/keepers/terminal/node_modules/node-pty/package.json +66 -0
  46. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  47. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  48. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  49. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  50. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  51. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  52. package/dist/runtime-deps/qoder-sdk/package-lock.json +1189 -0
  53. package/dist/runtime-deps/qoder-sdk/package.json +8 -0
  54. package/dist/test/keeperKit.js +67 -0
  55. package/dist/workstation/acp/acpAgents.js +2163 -611
  56. package/dist/workstation/acp/acpClient.js +60 -497
  57. package/dist/workstation/acp/adoption.js +10 -396
  58. package/dist/workstation/acp/binaryInstall.js +100 -27
  59. package/dist/workstation/acp/builds/gc.js +245 -0
  60. package/dist/workstation/acp/builds/install/binary.js +28 -0
  61. package/dist/workstation/acp/builds/install/npm.js +235 -0
  62. package/dist/workstation/acp/builds/install/uv.js +254 -0
  63. package/dist/workstation/acp/builds/migrate.js +195 -0
  64. package/dist/workstation/acp/builds/model.js +128 -0
  65. package/dist/workstation/acp/builds/nodeGate.js +100 -0
  66. package/dist/workstation/acp/builds/store.js +352 -0
  67. package/dist/workstation/acp/commandTableProbe.js +68 -0
  68. package/dist/workstation/acp/configOptions.js +103 -1
  69. package/dist/workstation/acp/envSecrets.js +27 -0
  70. package/dist/workstation/acp/initializeResponse.js +285 -0
  71. package/dist/workstation/acp/installErrors.js +12 -2
  72. package/dist/workstation/acp/installProbe.js +1 -1
  73. package/dist/workstation/acp/managedPatches.js +76 -160
  74. package/dist/workstation/acp/outbox.js +330 -0
  75. package/dist/workstation/acp/patchAssets.js +20 -3
  76. package/dist/workstation/acp/permission.js +42 -10
  77. package/dist/workstation/acp/processor/checkpoint.js +87 -0
  78. package/dist/workstation/acp/processor/commands.js +35 -0
  79. package/dist/workstation/acp/processor/control.js +355 -0
  80. package/dist/workstation/acp/processor/emit.js +26 -0
  81. package/dist/workstation/acp/processor/jsonrpc.js +172 -0
  82. package/dist/workstation/acp/processor/memoryLog.js +49 -0
  83. package/dist/workstation/acp/processor/origins.js +48 -0
  84. package/dist/workstation/acp/processor/output.js +67 -0
  85. package/dist/workstation/acp/processor/projection.js +42 -0
  86. package/dist/workstation/acp/processor/record.js +128 -0
  87. package/dist/workstation/acp/processor/runtime.js +181 -0
  88. package/dist/workstation/acp/processor/sdkSchemas.js +61 -0
  89. package/dist/workstation/acp/processor/session.js +525 -0
  90. package/dist/workstation/acp/processor/sideChannel.js +80 -0
  91. package/dist/workstation/acp/processor/state.js +74 -0
  92. package/dist/workstation/acp/processor/step.js +459 -0
  93. package/dist/workstation/acp/processor/test/fixtureLog.js +65 -0
  94. package/dist/workstation/acp/processor/test/harness.js +120 -0
  95. package/dist/workstation/acp/processor/test/largeIdEcho.js +50 -0
  96. package/dist/workstation/acp/processor/test/rawAgent.js +70 -0
  97. package/dist/workstation/acp/processor/transport/generations.js +76 -0
  98. package/dist/workstation/acp/processor/transport/keeper.js +130 -0
  99. package/dist/workstation/acp/processor/transport/local.js +269 -0
  100. package/dist/workstation/acp/processor/transport/types.js +78 -0
  101. package/dist/workstation/acp/processor/updates.js +364 -0
  102. package/dist/workstation/acp/processorSession.js +1512 -0
  103. package/dist/workstation/acp/qoderStartup.js +1 -1
  104. package/dist/workstation/acp/registry/catalog.js +154 -32
  105. package/dist/workstation/acp/registry/launch.js +61 -119
  106. package/dist/workstation/acp/sessionUpdateMapper.js +18 -7
  107. package/dist/workstation/acp/steering.js +58 -0
  108. package/dist/workstation/acp/switching.js +114 -0
  109. package/dist/workstation/acp/terminalLogin.js +16 -42
  110. package/dist/workstation/acp/terminalLoginSpec.js +41 -0
  111. package/dist/workstation/acp/test/adapterFixtures.js +256 -0
  112. package/dist/workstation/acp/test/daemonLife.js +106 -0
  113. package/dist/workstation/acp/test/keeperWorld.js +267 -0
  114. package/dist/workstation/acp/test/memoryOutbox.js +55 -0
  115. package/dist/workstation/acp/test/raw/fixtures.js +196 -0
  116. package/dist/workstation/acp/test/raw/qoderCaptures.js +125 -0
  117. package/dist/workstation/acp/test/raw/sanitize.js +344 -0
  118. package/dist/workstation/acp/test/raw/sources.js +249 -0
  119. package/dist/workstation/acp/test/realAdapter.js +377 -0
  120. package/dist/workstation/acp/test/scriptAgent.js +206 -0
  121. package/dist/workstation/acp/test/sessionFrames.js +42 -0
  122. package/dist/workstation/acp/timedCommand.js +4 -2
  123. package/dist/workstation/acp/uvxInstall.js +72 -122
  124. package/dist/workstation/agentFlow.js +103 -0
  125. package/dist/workstation/agentSessions.js +1324 -474
  126. package/dist/workstation/channels/agent.js +7 -5
  127. package/dist/workstation/channels/computer.js +69 -21
  128. package/dist/workstation/channels/computerExecs.js +200 -0
  129. package/dist/workstation/channels/management.js +84 -4
  130. package/dist/workstation/daemon.js +288 -7
  131. package/dist/workstation/daemonctl.js +28 -2
  132. package/dist/workstation/exec/attach.js +126 -0
  133. package/dist/workstation/exec/command.js +40 -0
  134. package/dist/workstation/exec/durable.js +517 -0
  135. package/dist/workstation/exec/fallback.js +106 -0
  136. package/dist/workstation/exec/policy.js +837 -0
  137. package/dist/workstation/exec/shaping.js +211 -0
  138. package/dist/workstation/exec.js +3 -112
  139. package/dist/workstation/files/adapters/computerFileFrames.js +51 -0
  140. package/dist/workstation/files/adapters/toolRequests.js +121 -0
  141. package/dist/workstation/files/client.js +455 -0
  142. package/dist/workstation/files/errors.js +28 -0
  143. package/dist/workstation/files/fence.js +350 -0
  144. package/dist/workstation/files/find/fileIndex.js +153 -0
  145. package/dist/workstation/files/find/fuzzy.js +155 -0
  146. package/dist/workstation/files/host.js +122 -0
  147. package/dist/workstation/files/hostProtocol.js +2 -0
  148. package/dist/workstation/files/media/cache.js +620 -0
  149. package/dist/workstation/files/media/encoding.js +308 -0
  150. package/dist/workstation/files/media/estimator.js +100 -0
  151. package/dist/workstation/files/media/media.js +325 -0
  152. package/dist/workstation/files/media/packedAudio.js +23 -0
  153. package/dist/workstation/files/media/scheduler.js +551 -0
  154. package/dist/workstation/files/media/types.js +5 -0
  155. package/dist/workstation/files/scope.js +45 -0
  156. package/dist/workstation/files/structured.js +284 -0
  157. package/dist/workstation/{fileOps.js → files/text.js} +117 -38
  158. package/dist/workstation/files/uploads.js +351 -0
  159. package/dist/workstation/journal.js +65 -236
  160. package/dist/workstation/keepers/acp/client.js +101 -0
  161. package/dist/workstation/keepers/acp/envelope.js +62 -0
  162. package/dist/workstation/keepers/acp/host.js +419 -0
  163. package/dist/workstation/keepers/acp/main.js +18 -0
  164. package/dist/workstation/keepers/acp/memoryStream.js +121 -0
  165. package/dist/workstation/keepers/acp/params.js +137 -0
  166. package/dist/workstation/keepers/acp/protocol.js +66 -0
  167. package/dist/workstation/keepers/acp/recover.js +120 -0
  168. package/dist/workstation/keepers/acp/session.js +584 -0
  169. package/dist/workstation/keepers/acp/stderr.js +154 -0
  170. package/dist/workstation/keepers/kit/activity.js +103 -0
  171. package/dist/workstation/keepers/kit/bootstrap.js +164 -0
  172. package/dist/workstation/keepers/kit/buildVersion.js +51 -0
  173. package/dist/workstation/keepers/kit/bundle.js +120 -0
  174. package/dist/workstation/keepers/kit/client.js +432 -0
  175. package/dist/workstation/keepers/kit/identity.js +21 -0
  176. package/dist/workstation/keepers/kit/ipc.js +112 -0
  177. package/dist/workstation/keepers/kit/launchConfig.js +8 -0
  178. package/dist/workstation/keepers/kit/launcher.js +249 -0
  179. package/dist/workstation/keepers/kit/lock.js +123 -0
  180. package/dist/workstation/keepers/kit/orphanSweep.js +86 -0
  181. package/dist/workstation/keepers/kit/paths.js +75 -0
  182. package/dist/workstation/keepers/kit/protocol.js +43 -0
  183. package/dist/workstation/keepers/kit/server.js +518 -0
  184. package/dist/workstation/keepers/kit/streamLog.js +697 -0
  185. package/dist/workstation/keepers/legacy.js +296 -0
  186. package/dist/workstation/keepers/policy.js +576 -0
  187. package/dist/workstation/keepers/stop.js +34 -0
  188. package/dist/workstation/keepers/terminal/client.js +213 -0
  189. package/dist/workstation/keepers/terminal/exec.js +981 -0
  190. package/dist/workstation/keepers/terminal/generations.js +129 -0
  191. package/dist/workstation/keepers/terminal/keeper.js +62 -0
  192. package/dist/workstation/keepers/terminal/main.js +43 -0
  193. package/dist/workstation/keepers/terminal/orphanSweep.js +3 -0
  194. package/dist/workstation/keepers/terminal/params.js +27 -0
  195. package/dist/workstation/keepers/terminal/protocol.js +56 -0
  196. package/dist/workstation/keepers/terminal/pty.js +330 -0
  197. package/dist/workstation/protocol/accountLimits.js +47 -0
  198. package/dist/workstation/protocol/agent.js +48 -4
  199. package/dist/workstation/protocol/common.js +30 -0
  200. package/dist/workstation/protocol/computer.js +15 -4
  201. package/dist/workstation/protocol/management.js +50 -2
  202. package/dist/workstation/qoderAdapter/backgroundProcess.js +18 -6
  203. package/dist/workstation/qoderAdapter/backgroundTerminal.js +78 -147
  204. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +372 -0
  205. package/dist/workstation/qoderAdapter/callbackContext.js +76 -0
  206. package/dist/workstation/qoderAdapter/foregroundOutput.js +15 -9
  207. package/dist/workstation/qoderAdapter/history.js +1 -3
  208. package/dist/workstation/qoderAdapter/main.js +4 -2
  209. package/dist/workstation/qoderAdapter/sdkDriver.js +1 -2
  210. package/dist/workstation/qoderAdapter/sdkRuntime.js +93 -0
  211. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +145 -0
  212. package/dist/workstation/qoderAdapter/session.js +1 -1
  213. package/dist/workstation/qoderAdapter/subwork.js +15 -7
  214. package/dist/workstation/systemMetrics.js +110 -1
  215. package/dist/workstation/tools/client.js +9 -1
  216. package/dist/workstation/tools/info.js +50 -0
  217. package/dist/workstation/tools/keeperProcess.js +15 -5
  218. package/dist/workstation/tools/router.js +55 -0
  219. package/dist/workstation/tools/terminalRouter.js +222 -0
  220. package/dist/workstation/tools/validation.js +37 -1
  221. package/dist/workstation/update/coordinator.js +3 -0
  222. package/dist/workstation/update/ptyPreflight.js +21 -0
  223. package/dist/workstation/update/taskRegistry.js +105 -11
  224. package/package.json +10 -7
  225. package/dist/workstation/acp/acpSession.js +0 -1475
  226. package/dist/workstation/acp/contract.js +0 -499
  227. package/dist/workstation/acp/draftInterceptor.js +0 -163
  228. package/dist/workstation/acp/sessionScope.js +0 -19
  229. package/dist/workstation/filePaths.js +0 -174
  230. package/dist/workstation/fileWorker.js +0 -81
  231. package/dist/workstation/fileWorkerClient.js +0 -121
  232. package/dist/workstation/qoderAdapter/taskEvent.js +0 -26
  233. package/dist/workstation/tools/core.js +0 -50
  234. package/dist/workstation/tools/files.js +0 -525
  235. package/dist/workstation/tools/terminal.js +0 -214
@@ -0,0 +1,67 @@
1
+ /**
2
+ * What one record produced: its down commands (each with its `(o, i)` cause) and its frames (each with
3
+ * its `(o, i)` src). Shared by `step.ts` (connection layer) and `session.ts` (turns and cards).
4
+ */
5
+ import { CommandBudgetExceededError, MAX_COMMANDS_PER_RECORD, errorLine, notificationLine, outboundRequestId, requestLine, resultLine, } from './jsonrpc.js';
6
+ /** A command was produced with no running adapter process to address it to — a processor bug. */
7
+ export class CommandWithoutSpawnError extends Error {
8
+ }
9
+ export class RecordOutput {
10
+ o;
11
+ spawnId;
12
+ commands = [];
13
+ frames = [];
14
+ /**
15
+ * @param spawnId the running spawn **when a command is pushed** (read then, not at construction: a
16
+ * `spawn` record closes the old process and addresses its `initialize` to the new one).
17
+ */
18
+ constructor(o, spawnId = () => undefined) {
19
+ this.o = o;
20
+ this.spawnId = spawnId;
21
+ }
22
+ push(line, extras = {}) {
23
+ const i = this.commands.length;
24
+ if (i >= MAX_COMMANDS_PER_RECORD) {
25
+ throw new CommandBudgetExceededError(`record ${this.o} produced more than ${MAX_COMMANDS_PER_RECORD} commands`);
26
+ }
27
+ const spawnId = this.spawnId();
28
+ if (spawnId === undefined)
29
+ throw new CommandWithoutSpawnError(`record ${this.o} produced a command with no running adapter process`);
30
+ const cause = { o: this.o, i };
31
+ this.commands.push({
32
+ cause,
33
+ spawnId,
34
+ line: line(i),
35
+ ...(extras.annotation !== undefined ? { annotation: extras.annotation } : {}),
36
+ ...(extras.fill !== undefined ? { fill: extras.fill } : {}),
37
+ ...(extras.redact !== undefined ? { redact: extras.redact } : {}),
38
+ });
39
+ return cause;
40
+ }
41
+ /** Throws `CommandBudgetExceededError` once this record already produced 16 commands (`outboundRequestId`). */
42
+ request(method, params, extras) {
43
+ let id = -1;
44
+ const cause = this.push((index) => {
45
+ id = outboundRequestId({ o: this.o, i: index });
46
+ return requestLine(id, method, params);
47
+ }, extras);
48
+ return { id, cause };
49
+ }
50
+ notify(method, params) {
51
+ this.push(() => notificationLine(method, params));
52
+ }
53
+ result(id, result) {
54
+ this.push(() => resultLine(id, result));
55
+ }
56
+ error(id, error) {
57
+ this.push(() => errorLine(id, error));
58
+ }
59
+ /** A journaled frame; the caller has already numbered it. */
60
+ frame(frame) {
61
+ this.frames.push({ journaled: true, frame, src: { o: this.o, i: this.frames.length } });
62
+ }
63
+ /** An unnumbered frame: a level, an RPC reply, or a `processor.*` frame for the daemon. */
64
+ unjournaled(frame) {
65
+ this.frames.push({ journaled: false, frame, src: { o: this.o, i: this.frames.length } });
66
+ }
67
+ }
@@ -0,0 +1,42 @@
1
+ import { AGENT_WIRE_RUNTIME_PROFILE_VERSION, AGENT_WIRE_SCOPED_ORIGIN_VERSION, AGENT_WIRE_SESSION_AUTH_METHODS_VERSION, AGENT_WIRE_SESSION_STATE_VERSION, } from '../../protocol/agent.js';
2
+ function withSessionRef(frame, sessionRef) {
3
+ const { t, ...rest } = frame;
4
+ return { t, sessionRef, ...rest };
5
+ }
6
+ export function projectFrame(output, context) {
7
+ const frame = output.frame;
8
+ const t = frame.t;
9
+ if (typeof t !== 'string' || t.startsWith('processor.'))
10
+ return { frame: null };
11
+ const wire = context.serverAgentWire;
12
+ switch (t) {
13
+ case 'agent_event': {
14
+ if (frame.acpSessionId !== undefined && wire < AGENT_WIRE_SCOPED_ORIGIN_VERSION) {
15
+ const payload = frame.payload;
16
+ if (frame.eventName === 'permission_request' && typeof payload?.requestId === 'string') {
17
+ return { frame: null, note: { type: 'agent_answer_undeliverable', requestId: payload.requestId } };
18
+ }
19
+ return { frame: null };
20
+ }
21
+ return { frame: withSessionRef(frame, context.sessionRef) };
22
+ }
23
+ case 'agent_turn_done': {
24
+ const { agentRefused, errorCode, ...rest } = frame;
25
+ const projected = withSessionRef(rest, context.sessionRef);
26
+ if (agentRefused === true && rest.status === 'failed' && wire >= AGENT_WIRE_SESSION_AUTH_METHODS_VERSION) {
27
+ if (context.authMethods)
28
+ projected.authMethods = context.authMethods;
29
+ if (typeof errorCode === 'number')
30
+ projected.errorCode = errorCode;
31
+ }
32
+ return { frame: projected };
33
+ }
34
+ case 'agent_session_state':
35
+ return { frame: wire >= AGENT_WIRE_SESSION_STATE_VERSION ? withSessionRef(frame, context.sessionRef) : null };
36
+ case 'agent_runtime_profile':
37
+ return { frame: wire >= AGENT_WIRE_RUNTIME_PROFILE_VERSION ? withSessionRef(frame, context.sessionRef) : null };
38
+ default:
39
+ // RPC replies carry the server's request `id`, not a `sessionRef`.
40
+ return { frame: { ...frame } };
41
+ }
42
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The raw records the ACP processor folds over (spec 2026-10-11 §5.3), and their on-stream spelling.
3
+ *
4
+ * ## Two spellings, one record
5
+ *
6
+ * keeper-acp stores each record as one line of a kit stream (`{o, t, k, …}`, §3.4) with short keys
7
+ * (`up{l}`, `down{l, a, redact?}`, `proc{kind, …}`, `note{n}`, `err{l}` — the names in
8
+ * interface-contracts). The processor works on the long, typed spelling below. {@link encodeStreamRecord}
9
+ * and {@link decodeStreamRecord} are the only bridge, so neither side has to know the other's names.
10
+ *
11
+ * ## What each kind is
12
+ *
13
+ * - `up`: one line the adapter wrote to stdout — a JSON-RPC message, verbatim.
14
+ * - `down`: one line written to the adapter's stdin. `annotation.cause = {o, i}` names the record
15
+ * (and the command index within it) whose processing produced the line; that is what makes
16
+ * commands idempotent (`commands.ts`). `annotation` is otherwise opaque to the keeper. `redact`
17
+ * lists the `secretRef`s whose values were replaced in the *logged* copy of the line — stdin got
18
+ * the real value, the log never does (§13). It names refs, never secrets.
19
+ * - `proc`: keeper-synthesised process events — `spawn{spawnId, pid, pgid, startTime}` and
20
+ * `exit{code, signal, stderrTail, reason?}`.
21
+ * - `note`: an opaque record the daemon wrote (server commands, its own decisions). Notes carry no
22
+ * secrets and no large payloads (§5.4): secrets by `secretRef`, large blocks by reference to the
23
+ * one `down` record that carries them.
24
+ * - `err`: one already-redacted stderr line.
25
+ *
26
+ * `o` is the record's offset in the session's raw stream (monotonic, gap-free per stream) and `t` is
27
+ * the keeper's receive time in epoch ms — the **only** clock the processor may read.
28
+ */
29
+ export function sameCause(a, b) {
30
+ return a !== undefined && a.o === b.o && a.i === b.i;
31
+ }
32
+ /** The map key for a cause; `o` and `i` are integers so this is injective. */
33
+ export function causeKey(cause) {
34
+ return `${cause.o}:${cause.i}`;
35
+ }
36
+ export class MalformedRecordError extends Error {
37
+ }
38
+ export function encodeStreamRecord(record) {
39
+ const { o, t } = record;
40
+ switch (record.kind) {
41
+ case 'up':
42
+ return { o, t, k: 'up', l: record.line };
43
+ case 'down':
44
+ return {
45
+ o, t, k: 'down', l: record.line,
46
+ ...(record.annotation !== undefined ? { a: record.annotation } : {}),
47
+ ...(record.redact !== undefined ? { redact: record.redact } : {}),
48
+ };
49
+ case 'proc': {
50
+ const { type, ...rest } = record.proc;
51
+ return { o, t, k: 'proc', kind: type, ...rest };
52
+ }
53
+ case 'note':
54
+ return { o, t, k: 'note', n: record.note };
55
+ case 'err':
56
+ return { o, t, k: 'err', l: record.line };
57
+ }
58
+ }
59
+ function isRecord(value) {
60
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
61
+ }
62
+ function str(value, what) {
63
+ if (typeof value !== 'string')
64
+ throw new MalformedRecordError(`${what} is not a string`);
65
+ return value;
66
+ }
67
+ /** Inverse of {@link encodeStreamRecord}. Throws {@link MalformedRecordError} on anything else. */
68
+ export function decodeStreamRecord(value) {
69
+ if (!isRecord(value))
70
+ throw new MalformedRecordError('record is not an object');
71
+ const { o, t, k } = value;
72
+ if (typeof o !== 'number' || !Number.isSafeInteger(o) || o < 0)
73
+ throw new MalformedRecordError('record offset is not a non-negative integer');
74
+ if (typeof t !== 'number' || !Number.isFinite(t))
75
+ throw new MalformedRecordError('record time is not a number');
76
+ switch (k) {
77
+ case 'up':
78
+ return { o, t, kind: 'up', line: str(value.l, 'up.l') };
79
+ case 'err':
80
+ return { o, t, kind: 'err', line: str(value.l, 'err.l') };
81
+ case 'down': {
82
+ const a = value.a;
83
+ let annotation;
84
+ if (a !== undefined) {
85
+ if (!isRecord(a) || !isRecord(a.cause) || !Number.isSafeInteger(a.cause.o) || !Number.isSafeInteger(a.cause.i)) {
86
+ throw new MalformedRecordError('down.a.cause is not {o, i}');
87
+ }
88
+ annotation = a;
89
+ }
90
+ const redact = value.redact;
91
+ if (redact !== undefined && !(Array.isArray(redact) && redact.every((r) => typeof r === 'string'))) {
92
+ throw new MalformedRecordError('down.redact is not a string array');
93
+ }
94
+ return {
95
+ o, t, kind: 'down', line: str(value.l, 'down.l'),
96
+ ...(annotation !== undefined ? { annotation } : {}),
97
+ ...(redact !== undefined ? { redact: redact } : {}),
98
+ };
99
+ }
100
+ case 'proc': {
101
+ if (value.kind === 'spawn') {
102
+ const { spawnId, pid, pgid, startTime } = value;
103
+ if (typeof spawnId !== 'string' || typeof pid !== 'number' || typeof pgid !== 'number'
104
+ || (typeof startTime !== 'string' && typeof startTime !== 'number')) {
105
+ throw new MalformedRecordError('proc spawn is missing spawnId/pid/pgid/startTime');
106
+ }
107
+ return { o, t, kind: 'proc', proc: { type: 'spawn', spawnId, pid, pgid, startTime } };
108
+ }
109
+ if (value.kind === 'exit') {
110
+ const { code, signal, stderrTail, reason } = value;
111
+ if ((code !== null && typeof code !== 'number') || (signal !== null && typeof signal !== 'string')
112
+ || typeof stderrTail !== 'string' || (reason !== undefined && typeof reason !== 'string')) {
113
+ throw new MalformedRecordError('proc exit is missing code/signal/stderrTail');
114
+ }
115
+ return { o, t, kind: 'proc', proc: { type: 'exit', code, signal, stderrTail, ...(reason !== undefined ? { reason } : {}) } };
116
+ }
117
+ throw new MalformedRecordError(`unknown proc kind ${JSON.stringify(value.kind)}`);
118
+ }
119
+ case 'note': {
120
+ const n = value.n;
121
+ if (!isRecord(n) || typeof n.type !== 'string')
122
+ throw new MalformedRecordError('note.n has no string type');
123
+ return { o, t, kind: 'note', note: n };
124
+ }
125
+ default:
126
+ throw new MalformedRecordError(`unknown record kind ${JSON.stringify(k)}`);
127
+ }
128
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The two level signals (spec §5.4: "电平类帧从状态现算"): the agent's self-reported activity
3
+ * (`agent_session_state`) and the runtime diagnostics (`agent_runtime_profile`). Neither is numbered
4
+ * or journaled (analysis B §5); both are folded into state from the log, so after a restart they are
5
+ * recomputed by replay and re-sent with `agent_opened` ({@link activityRaw}, {@link runtimeDiagnostics}).
6
+ *
7
+ * - **Activity** — `connectAgent`'s `_claude/sdkMessage` handler (`claudeActivityRaw`) and the mapper's
8
+ * `codexActivityRaw` on the root session's `session_info_update`, as `AcpSession.reportActivity`
9
+ * joins them. Both sinks are bound only once the session is open, so earlier pushes are dropped,
10
+ * as today.
11
+ * - **Diagnostics** — `generation` is the keeper's `spawnId` (analysis B B2: no `randomUUID`), and
12
+ * `revision` is a fold: +1 on process exit and on every applied Qoder profile (the `connectAgent`
13
+ * closure, `acpClient.ts`). `launch` arrives in an `agent_launch` note the daemon writes after the
14
+ * spawn (analysis B D5: computed once, at spawn). Task 17c: the permission callback snapshot
15
+ * (`callbacks`, {@link callbackSnapshot}) — published, and the revision bumped, on every change to the
16
+ * session's pending/settled permission cards, as `AcpSession.publishCallbacks` does for every agent.
17
+ */
18
+ import { claudeActivityRaw } from '../activityReporting.js';
19
+ import { parseQoderProfile, sameProfileIdentity } from '../../qoderAdapter/profile.js';
20
+ import { connectionGenerationOf } from '../../qoderAdapter/callbackContext.js';
21
+ import { isRecord } from './emit.js';
22
+ import { getOwn, initialRuntime, PENDING_PROFILE_LIMIT, putOwn } from './state.js';
23
+ function opened(state) {
24
+ return state.open.status === 'open';
25
+ }
26
+ /** One agent-reported activity push (vendor-verbatim): a level, never numbered. */
27
+ export function reportActivity(state, out, raw, t) {
28
+ if (!opened(state))
29
+ return;
30
+ state.activity = { raw, t };
31
+ out.unjournaled({ t: 'agent_session_state', vendor: vendorOf(state), raw });
32
+ }
33
+ /** `InitializeResponse.agentInfo.name` (`agentSessions.ts`'s `vendor`), `''` when the agent named none. */
34
+ export function vendorOf(state) {
35
+ return state.initialize.status === 'ready' ? state.initialize.facts.agentInfo?.name ?? '' : '';
36
+ }
37
+ /** `_claude/sdkMessage` — only the `session_state_changed` system message is activity. */
38
+ export function onClaudeSdkMessage(state, out, params, t) {
39
+ const raw = claudeActivityRaw(isRecord(params) ? params : {});
40
+ if (raw)
41
+ reportActivity(state, out, raw, t);
42
+ }
43
+ /** `agent_opened.activityRaw`: the last push, if any. */
44
+ export function activityRaw(state) {
45
+ return state.activity?.raw;
46
+ }
47
+ /** The current adapter process's `spawnId` (running, or the one that exited). */
48
+ export function currentSpawnId(state) {
49
+ return state.process.status === 'running' ? state.process.spawn.spawnId
50
+ : state.process.status === 'exited' ? state.process.spawnId : undefined;
51
+ }
52
+ /**
53
+ * `AgentRuntimeDiagnostics.callbacks` — the snapshot `AcpSession.publishCallbacks` last published,
54
+ * recomputed from state (every change to the cards it lists publishes, so the two are the same), or
55
+ * `undefined` before the first publish. Pending and settled are listed only while `complete`.
56
+ */
57
+ export function callbackSnapshot(state) {
58
+ const { complete, rootSessionId } = state.callbacks;
59
+ const spawnId = currentSpawnId(state);
60
+ if (rootSessionId === null || spawnId === undefined)
61
+ return undefined;
62
+ return {
63
+ rootSessionId, connectionGeneration: connectionGenerationOf(spawnId), complete,
64
+ pending: complete ? Object.values(state.permissions).map((card) => ({
65
+ requestId: card.requestId, originNativeSessionId: card.origin, rpcRequestId: card.rpcRequestId,
66
+ })) : [],
67
+ settled: complete ? state.settledPermissions.map(({ requestId, reason }) => ({ requestId, reason })) : [],
68
+ };
69
+ }
70
+ /** The diagnostics as `agent_runtime_profile`/`agent_opened.runtimeDiagnostics` carry them, or `undefined` before a spawn and its `agent_launch` note. */
71
+ export function runtimeDiagnostics(state) {
72
+ const spawnId = currentSpawnId(state);
73
+ const { revision, available, launch, profile } = state.runtime;
74
+ if (spawnId === undefined || launch === undefined)
75
+ return undefined;
76
+ const callbacks = callbackSnapshot(state);
77
+ return { generation: spawnId, revision, available, launch, ...(profile !== undefined ? { profile } : {}),
78
+ ...(callbacks !== undefined ? { callbacks } : {}) };
79
+ }
80
+ /**
81
+ * `AcpSession.publishCallbacks` → `connectAgent`'s `publishCallbacks`: the snapshot changed, the
82
+ * revision goes up and the level is re-sent. Ignored before a root is known (today: a snapshot for a
83
+ * session the connection has not bound). Callers change the cards first, then publish.
84
+ */
85
+ export function publishCallbacks(state, out) {
86
+ const root = state.rootSessionId ?? state.history?.rootSessionId ?? null;
87
+ if (root === null)
88
+ return;
89
+ state.callbacks.rootSessionId = root;
90
+ state.runtime.revision += 1;
91
+ changed(state, out);
92
+ }
93
+ /** A diagnostics change: published (unnumbered) once the session is open, as `onRuntimeDiagnostics` is. */
94
+ function changed(state, out) {
95
+ if (!opened(state))
96
+ return;
97
+ const diagnostics = runtimeDiagnostics(state);
98
+ if (diagnostics)
99
+ out.unjournaled({ t: 'agent_runtime_profile', diagnostics: JSON.parse(JSON.stringify(diagnostics)) });
100
+ }
101
+ export function resetRuntime(state) {
102
+ state.runtime = initialRuntime();
103
+ state.activity = null;
104
+ }
105
+ /** `agent_launch{launch}` — what `connectAgent` computes at spawn (`launchSource`, `findOnPath`, platform). */
106
+ export function onLaunchNote(state, launch) {
107
+ if (!isRecord(launch) || state.process.status !== 'running')
108
+ return false;
109
+ const { source, executable, platform, architecture, transport } = launch;
110
+ if (!['command-override', 'registry', 'registry-fallback', 'direct'].includes(source) || typeof executable !== 'string'
111
+ || typeof platform !== 'string' || typeof architecture !== 'string' || transport !== 'stdio')
112
+ return false;
113
+ state.runtime.launch = { source: source, executable: executable.slice(0, 4096), platform, architecture, transport };
114
+ return true;
115
+ }
116
+ /** The `initialize` result's `_meta.qoderProfile` (`connectAgent`, right after the handshake). */
117
+ export function onInitialized(state, result) {
118
+ const meta = isRecord(result) && isRecord(result._meta) ? result._meta : undefined;
119
+ const profile = parseQoderProfile(meta?.qoderProfile);
120
+ if (profile)
121
+ state.runtime.profile = profile;
122
+ else
123
+ delete state.runtime.profile;
124
+ }
125
+ /** The child exited: unavailable, profile no longer observed (`child.once('exit')` in `connectAgent`). */
126
+ export function onProcessExit(state, out) {
127
+ const runtime = state.runtime;
128
+ runtime.revision += 1;
129
+ runtime.available = false;
130
+ if (runtime.profile)
131
+ runtime.profile = { ...runtime.profile, observerHealthy: false, rootConfirmed: false };
132
+ changed(state, out);
133
+ }
134
+ function applyProfile(state, out, sessionId, revision, profile) {
135
+ const runtime = state.runtime;
136
+ const previous = runtime.profile;
137
+ if (!previous || !runtime.available || sessionId !== state.rootSessionId || revision <= runtime.adapterRevision
138
+ || !sameProfileIdentity(previous, profile) || profile.rootSessionId !== state.rootSessionId
139
+ || (previous.runtimeGeneration !== undefined && profile.runtimeGeneration !== previous.runtimeGeneration)
140
+ || (profile.rootConfirmed === true && !profile.runtimeGeneration))
141
+ return;
142
+ runtime.adapterRevision = revision;
143
+ runtime.revision += 1;
144
+ runtime.profile = profile;
145
+ changed(state, out);
146
+ }
147
+ /** `_axom/qoder/profile{sessionId, revision, profile}` — the `connectAgent` handler, over state. */
148
+ export function onProfileNotification(state, out, params) {
149
+ if (!isRecord(params))
150
+ return;
151
+ const profile = parseQoderProfile(params.profile);
152
+ const { sessionId, revision } = params;
153
+ if (!state.runtime.profile || !profile || typeof sessionId !== 'string' || profile.rootSessionId !== sessionId
154
+ || !Number.isSafeInteger(revision) || revision <= 0 || !sameProfileIdentity(state.runtime.profile, profile))
155
+ return;
156
+ if (state.rootSessionId !== null) {
157
+ applyProfile(state, out, sessionId, revision, profile);
158
+ return;
159
+ }
160
+ // ACP may notify before its new/load reply: bounded candidates, evidence only once the reply binds.
161
+ const pending = state.runtime.pendingProfiles;
162
+ const previous = getOwn(pending, sessionId);
163
+ if (previous && (revision <= previous.revision || profile.runtimeGeneration !== previous.profile.runtimeGeneration))
164
+ return;
165
+ if (!previous && Object.keys(pending).length >= PENDING_PROFILE_LIMIT)
166
+ return;
167
+ putOwn(pending, sessionId, { revision: revision, profile });
168
+ }
169
+ /** The root just bound (`bindDraftUpdates`' diagnostics half): drop a profile for another root, apply a pending one. */
170
+ export function onRootBound(state, out, sessionId) {
171
+ const runtime = state.runtime;
172
+ const current = runtime.profile;
173
+ if (current?.rootSessionId && current.rootSessionId !== sessionId) {
174
+ const { rootSessionId: _root, runtimeGeneration: _generation, ...rest } = current;
175
+ runtime.profile = { ...rest, rootConfirmed: false, initialized: false, observerHealthy: false };
176
+ }
177
+ const pending = getOwn(runtime.pendingProfiles, sessionId);
178
+ runtime.pendingProfiles = {};
179
+ if (pending)
180
+ applyProfile(state, out, sessionId, pending.revision, pending.profile);
181
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The ACP SDK's own zod schemas, used synchronously as a schema library (spec 2026-10-11 §5.4: "SDK
3
+ * 只作 schema 库(同步 zod 校验/剥离)").
4
+ *
5
+ * ## Why these schemas, and why loaded this way
6
+ *
7
+ * The live path validates and strips every inbound message with the SDK's generated schemas
8
+ * (`dist/schema/zod.gen.js` — e.g. `ClientApp` parses `session/update` through `zSessionNotification`,
9
+ * `session/request_permission` through `zRequestPermissionRequest`). The processor has to apply the
10
+ * **same** schemas or moving a session onto it changes payload bytes (analysis B F2). Copying them
11
+ * would drift on the next SDK bump; importing them is the only honest option.
12
+ *
13
+ * The package's `exports` map does not expose `zod.gen.js`, so a bare subpath import is refused. It is
14
+ * reached instead by resolving the package entry and taking its sibling — a plain file path, which
15
+ * `exports` does not police — and loaded with `require` (synchronous `require(esm)`, Node ≥ 22.12;
16
+ * `engines.node` is `>=24`). This happens once, at module load: nothing here runs inside `step`.
17
+ * `guard.test.ts` allows exactly this file to import `node:module`.
18
+ *
19
+ * ## The pin
20
+ *
21
+ * `dist/schema/zod.gen.js` is the SDK's **private** layout, so `package.json` pins the SDK to one
22
+ * exact version (no range) and `sdkSchemas.test.ts` checks the installed version is that pin and the
23
+ * file resolves — an SDK bump has to be a deliberate change that re-runs that test.
24
+ */
25
+ import { createRequire } from 'node:module';
26
+ const require = createRequire(import.meta.url);
27
+ /** The absolute path of the SDK's generated zod module (a sibling of its `acp.js` entry). */
28
+ export function sdkZodGenPath(resolve = require.resolve) {
29
+ const entry = resolve('@agentclientprotocol/sdk');
30
+ const suffix = 'acp.js';
31
+ if (!entry.endsWith(suffix)) {
32
+ throw new Error(`Unexpected @agentclientprotocol/sdk entry point ${entry}: the processor reads the SDK's private dist/schema/zod.gen.js next to dist/acp.js; re-check the pinned SDK version`);
33
+ }
34
+ return `${entry.slice(0, -suffix.length)}schema/zod.gen.js`;
35
+ }
36
+ function loadZodGen() {
37
+ const path = sdkZodGenPath();
38
+ try {
39
+ return require(path);
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Cannot load the ACP SDK's zod schemas from ${path} (private SDK layout; re-check the pinned SDK version): ${error instanceof Error ? error.message : String(error)}`);
43
+ }
44
+ }
45
+ const zodGen = loadZodGen();
46
+ function schema(name) {
47
+ const found = zodGen[name];
48
+ if (!found || typeof found.safeParse !== 'function')
49
+ throw new Error(`@agentclientprotocol/sdk has no schema ${name}`);
50
+ return found;
51
+ }
52
+ /** Inbound (agent → client) request params, by method — the schemas `clientRequestSpecs` uses. */
53
+ export const INBOUND_REQUEST_SCHEMAS = {
54
+ 'session/request_permission': schema('zRequestPermissionRequest'),
55
+ 'elicitation/create': schema('zCreateElicitationRequest'),
56
+ };
57
+ /** Inbound notification params, by method — the schemas `clientNotificationSpecs` uses. */
58
+ export const INBOUND_NOTIFICATION_SCHEMAS = {
59
+ 'session/update': schema('zSessionNotification'),
60
+ 'elicitation/complete': schema('zCompleteElicitationNotification'),
61
+ };