@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
@@ -1,24 +1,22 @@
1
1
  /** Qoder 1.1.61 drops native background task notifications from both live ACP and session/load.
2
2
  * This read-only compatibility boundary repairs only a known root Agent tool, never a session.
3
3
  * Lifecycle/removal contract: axom-patch/cli/qoder-background-terminal.md.
4
+ *
5
+ * The impure half (Task 17c): this file reads the side files; everything the repair decides is the
6
+ * pure fold in `backgroundTerminalFold.ts`. {@link BackgroundSideNotes} is the daemon side of the deterministic
7
+ * processor: a poll returns the facts as `qoder_side` note payloads for the raw log; the processor folds them only when it
8
+ * reads those notes, so a replay never touches the files. (The old live path's in-process driver, `BackgroundTerminalRepair`,
9
+ * was deleted with that path in Task 29.)
4
10
  */
5
11
  import { constants } from 'node:fs';
6
12
  import { open } from 'node:fs/promises';
7
13
  import { dirname, isAbsolute, join, resolve } from 'node:path';
8
14
  import { BackgroundProcess } from './backgroundProcess.js';
15
+ import { address, MAX_TASKS, object, rootRowFacts, sideNotes, } from './backgroundTerminalFold.js';
16
+ export { TerminalRepairFold, newTerminalRepairData, sideNotes } from './backgroundTerminalFold.js';
9
17
  const CHUNK = 64 * 1024;
10
18
  const MAX_LINE = 2 * 1024 * 1024;
11
19
  const MAX_POLL = 4 * 1024 * 1024;
12
- const MAX_TASKS = 512;
13
- const MAX_RESULT = 128 * 1024;
14
- const MAX_RESULTS = 4 * 1024 * 1024;
15
- const TERMINALS = new Set(['completed', 'failed', 'cancelled']);
16
- function object(value) {
17
- return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
18
- }
19
- function address(value) {
20
- return typeof value === 'string' && /^[a-zA-Z0-9_-]{1,160}$/.test(value) ? value : undefined;
21
- }
22
20
  export function nativeTerminalHistoryPath(identity, sessionId, cwd, historyHome) {
23
21
  if (identity?.name !== 'qoder-cli' || identity.version !== '1.1.61' ||
24
22
  !address(sessionId) || !isAbsolute(cwd) || !isAbsolute(historyHome))
@@ -27,89 +25,50 @@ export function nativeTerminalHistoryPath(identity, sessionId, cwd, historyHome)
27
25
  // identity: every accepted row is checked again against the exact native session and directory.
28
26
  return join(historyHome, '.qoder', 'projects', resolve(cwd).replace(/[^a-zA-Z0-9]/g, '-'), sessionId + '.jsonl');
29
27
  }
30
- export class BackgroundTerminalRepair {
28
+ /**
29
+ * Reads the root transcript and the background children's transcripts incrementally (≤ 4 MiB per
30
+ * poll and file, lines ≤ 2 MiB, `O_NOFOLLOW`, never written) and hands each derived {@link SideFact}
31
+ * to the sink, in transcript order: root facts, then each followed child's rows and catch-up status,
32
+ * then `poll_end` — only when the root read completed. Its offsets are its own (not checkpointed): a
33
+ * new reader starts at 0 and the fold drops what it already has.
34
+ */
35
+ export class BackgroundSideReader {
31
36
  options;
32
- calls = new Map();
33
37
  launches = new Map();
34
- terminals = new Map();
35
- delivered = new Set();
36
38
  processes = new Map();
37
- pendingProcesses = new Set();
38
39
  offset = 0;
39
40
  fileIdentity = '';
40
41
  pendingLine = Buffer.alloc(0);
41
42
  skipLine = false;
42
43
  closed = false;
43
- timer;
44
- inFlight;
45
44
  warned = false;
46
- resultBytes = 0;
47
45
  restoring;
48
46
  constructor(options) {
49
47
  this.options = options;
50
48
  this.restoring = options.restoreCalls === true;
51
49
  }
52
- start() {
53
- if (this.closed || this.timer)
54
- return;
55
- this.timer = setInterval(() => { void this.poll(); }, 1000);
56
- this.timer.unref();
57
- }
58
- observe(event) {
59
- if (this.closed || (event.acpSessionId && event.acpSessionId !== this.options.nativeSessionId) ||
60
- !['tool_call', 'tool_call_update'].includes(event.eventName))
61
- return;
62
- const payload = event.payload;
63
- const id = address(payload.toolCallId);
64
- if (!id)
65
- return;
66
- let call = this.calls.get(id);
67
- const input = object(payload.rawInput);
68
- if (!call && input.run_in_background === true && typeof input.prompt === 'string' &&
69
- (input.subagent_type == null || typeof input.subagent_type === 'string') && payload.kind === 'think' &&
70
- object(object(payload._meta).qoder).toolName === 'Agent' && this.calls.size < MAX_TASKS) {
71
- const meta = object(object(payload._meta).qoder);
72
- call = { input, invocationComplete: false, nativeTerminal: false,
73
- process: this.options.includeProcess === true && meta.version === 1 && meta.source === 'native-acp-patch' };
74
- this.calls.set(id, call);
75
- }
76
- if (!call)
50
+ /** One poll. Never rejects: a missing, replaced or unreadable file is not evidence. */
51
+ async read(demand, sink) {
52
+ if (this.closed || (!this.restoring && !demand.active()))
77
53
  return;
78
- if (payload.status === 'completed')
79
- call.invocationComplete = true;
80
- const output = object(payload.rawOutput);
81
- if (output.kind === 'agent-result' && (TERMINALS.has(String(output.state)) || output.state === 'error')) {
82
- call.nativeTerminal = true; // Upstream's structured evidence always wins.
54
+ try {
55
+ await this.readIncrement(demand, sink);
83
56
  }
84
- }
85
- poll() {
86
- if (this.closed || (!this.restoring && ![...this.calls].some(([id, call]) => !this.delivered.has(id) && !call.nativeTerminal)))
87
- return Promise.resolve();
88
- if (!this.inFlight) {
89
- this.inFlight = this.readIncrement().catch(error => {
90
- const code = object(error).code;
91
- if (!['ENOENT', 'ELOOP', 'ENOTDIR', 'EACCES'].includes(String(code)) && !this.warned) {
92
- this.warned = true;
93
- console.warn('[acp:qoder] background terminal repair unavailable; retaining native ACP state');
94
- }
95
- }).finally(() => { this.inFlight = undefined; });
57
+ catch (error) {
58
+ const code = object(error).code;
59
+ if (!['ENOENT', 'ELOOP', 'ENOTDIR', 'EACCES'].includes(String(code)) && !this.warned) {
60
+ this.warned = true;
61
+ console.warn('[acp:qoder] background terminal repair unavailable; retaining native ACP state');
62
+ }
96
63
  }
97
- return this.inFlight;
98
64
  }
99
65
  close() {
100
66
  this.closed = true;
101
- if (this.timer)
102
- clearInterval(this.timer);
103
- this.timer = undefined;
104
- this.calls.clear();
105
67
  this.launches.clear();
106
- this.terminals.clear();
107
- this.delivered.clear();
108
68
  this.processes.clear();
109
- this.pendingProcesses.clear();
110
69
  this.pendingLine = Buffer.alloc(0);
111
70
  }
112
- async readIncrement() {
71
+ async readIncrement(demand, sink) {
113
72
  // O_NOFOLLOW avoids importing a transcript through a replaced symlink. File is never written.
114
73
  const file = await open(this.options.path, constants.O_RDONLY | constants.O_NOFOLLOW);
115
74
  try {
@@ -130,29 +89,32 @@ export class BackgroundTerminalRepair {
130
89
  if (!bytesRead)
131
90
  break;
132
91
  this.offset += bytesRead;
133
- this.lines(chunk.subarray(0, bytesRead));
92
+ this.lines(chunk.subarray(0, bytesRead), sink);
134
93
  }
135
94
  if (this.offset >= stat.size)
136
95
  this.restoring = false;
137
96
  if (!this.closed) {
138
- await this.pollProcesses();
97
+ await this.pollProcesses(demand, sink);
139
98
  if (!this.closed)
140
- this.publish();
99
+ sink({ kind: 'poll_end' });
141
100
  }
142
101
  }
143
102
  finally {
144
103
  await file.close();
145
104
  }
146
105
  }
147
- lines(chunk) {
106
+ lines(chunk, sink) {
148
107
  let start = 0;
149
108
  for (let end = chunk.indexOf(10); end !== -1; end = chunk.indexOf(10, start)) {
150
109
  if (!this.skipLine && this.pendingLine.length + end - start <= MAX_LINE) {
151
110
  const line = Buffer.concat([this.pendingLine, chunk.subarray(start, end)]).toString('utf8');
111
+ let row;
152
112
  try {
153
- this.consume(object(JSON.parse(line)));
113
+ row = object(JSON.parse(line));
154
114
  }
155
115
  catch { /* partial/corrupt non-evidence */ }
116
+ if (row)
117
+ this.consume(row, sink);
156
118
  }
157
119
  this.pendingLine = Buffer.alloc(0);
158
120
  this.skipLine = false;
@@ -167,96 +129,65 @@ export class BackgroundTerminalRepair {
167
129
  this.skipLine = true;
168
130
  }
169
131
  }
170
- consume(row) {
171
- if (row.sessionId !== this.options.nativeSessionId || row.cwd !== this.options.cwd ||
172
- row.isSidechain !== false)
173
- return;
174
- const content = object(row.message).content;
175
- if (!Array.isArray(content))
176
- return;
177
- // resume deliberately sends no historical ACP openings. Recover only certified root Agent
178
- // background inputs; no opening is emitted and the existing exact-ID notification rules apply.
179
- if (this.options.restoreCalls && row.type === 'assistant') {
180
- for (const block of content.map(object)) {
181
- const input = object(block.input), id = address(block.id);
182
- if (block.type === 'tool_use' && block.name === 'Agent' && id && input.run_in_background === true &&
183
- typeof input.prompt === 'string' && (input.subagent_type == null || typeof input.subagent_type === 'string') &&
184
- !this.calls.has(id) && this.calls.size < MAX_TASKS) {
185
- this.calls.set(id, { input, invocationComplete: false, nativeTerminal: false, process: false });
186
- }
187
- }
188
- return;
189
- }
190
- if (row.type !== 'user')
191
- return;
192
- const output = object(row.toolUseResult);
193
- if (output.isAsync === true && output.status === 'async_launched') {
194
- const results = content.map(object).filter(c => c.type === 'tool_result');
195
- const id = results.length === 1 ? address(results[0].tool_use_id) : undefined;
196
- const agent = address(output.agentId);
197
- if (this.options.restoreCalls && id && this.calls.has(id))
198
- this.calls.get(id).invocationComplete = true;
199
- if (id && agent && this.launches.size < MAX_TASKS && !this.launches.has(id))
200
- this.launches.set(id, agent);
201
- return;
202
- }
203
- if (row.isMeta !== true || object(row.origin).kind !== 'task-notification')
204
- return;
205
- for (const block of content.map(object)) {
206
- if (block.type !== 'text' || typeof block.text !== 'string' ||
207
- !block.text.startsWith('[SYSTEM NOTIFICATION - NOT USER INPUT]\n'))
208
- continue;
209
- // Match the authoritative envelope before result text; tags inside a result cannot change IDs.
210
- const match = block.text.match(/<task-notification>\n<task-id>([a-zA-Z0-9_-]{1,160})<\/task-id>\n<tool-use-id>([a-zA-Z0-9_-]{1,160})<\/tool-use-id>\n(?:<output-file>[^\n]*<\/output-file>\n)?<status>(completed|failed|cancelled)<\/status>\n<summary>[^\n]*<\/summary>\n<result>([\s\S]*)<\/result>\n(?:<usage>[^\n]*<\/usage>\n)?<\/task-notification>\s*$/);
211
- const bytes = match ? Buffer.byteLength(match[4]) : 0;
212
- if (match && bytes <= MAX_RESULT && this.resultBytes + bytes <= MAX_RESULTS &&
213
- this.terminals.size < MAX_TASKS && !this.terminals.has(match[2])) {
214
- this.resultBytes += bytes;
215
- this.terminals.set(match[2], { taskId: match[1], state: match[3], result: match[4] });
132
+ consume(row, sink) {
133
+ const { nativeSessionId, cwd } = this.options;
134
+ for (const fact of rootRowFacts(row, { nativeSessionId, cwd, restoreCalls: this.options.restoreCalls === true })) {
135
+ // The reader's own copy of the receipts (same rule as the fold's): which child transcripts exist.
136
+ if (fact.kind === 'launch' && fact.agentId && this.launches.size < MAX_TASKS && !this.launches.has(fact.toolUseId)) {
137
+ this.launches.set(fact.toolUseId, fact.agentId);
216
138
  }
139
+ sink(fact);
217
140
  }
218
141
  }
219
- publish() {
220
- for (const [id, terminal] of this.terminals) {
221
- const call = this.calls.get(id), agentId = this.launches.get(id);
222
- if (this.delivered.has(id) || this.pendingProcesses.has(id) || !call?.invocationComplete || call.nativeTerminal || !agentId)
223
- continue;
224
- this.delivered.add(id);
225
- this.options.emit({ eventName: 'tool_call_update', payload: {
226
- name: 'Agent', kind: 'think', toolCallId: id, rawInput: call.input, status: 'completed',
227
- rawOutput: { kind: 'agent-result', state: terminal.state, agentId, result: terminal.result },
228
- content: [{ type: 'content', content: { type: 'text', text: terminal.result } }],
229
- _meta: { qoder: { toolName: 'Agent' }, axomNativeTerminalRepair: {
230
- patch: 'qoder-background-terminal', nativeSessionId: this.options.nativeSessionId,
231
- taskId: terminal.taskId, source: 'native-task-notification',
232
- } },
233
- } });
234
- }
235
- }
236
- async pollProcesses() {
142
+ async pollProcesses(demand, sink) {
237
143
  for (const [id, agentId] of this.launches) {
238
- const call = this.calls.get(id);
239
- if (this.closed || this.delivered.has(id) || !call?.process || call.nativeTerminal)
144
+ if (this.closed || !demand.wantsProcess(id))
240
145
  continue;
241
146
  let process = this.processes.get(id);
242
147
  if (!process) {
243
148
  process = new BackgroundProcess({
244
149
  path: join(dirname(this.options.path), this.options.nativeSessionId, 'subagents', `agent-${agentId}.jsonl`),
245
150
  sessionId: this.options.nativeSessionId, cwd: this.options.cwd, agentId, parentToolUseId: id,
246
- emit: event => { if (!this.closed)
247
- this.options.emit(event); },
151
+ emit: () => { },
152
+ onRow: (uuid, events) => { if (!this.closed)
153
+ sink({ kind: 'child', toolUseId: id, uuid, events }); },
248
154
  });
249
155
  this.processes.set(id, process);
250
156
  }
157
+ let caughtUp;
251
158
  try {
252
- if (await process.poll())
253
- this.pendingProcesses.delete(id);
254
- else
255
- this.pendingProcesses.add(id);
159
+ caughtUp = await process.poll();
256
160
  }
257
161
  catch {
258
- this.pendingProcesses.delete(id); /* missing/corrupt transcript is not execution evidence */
162
+ caughtUp = true; /* missing/corrupt transcript is not execution evidence */
259
163
  }
164
+ sink({ kind: 'child_status', toolUseId: id, caughtUp });
260
165
  }
261
166
  }
262
167
  }
168
+ /**
169
+ * The daemon side of the processor (T19 runs it on its own timer, for a session whose processor
170
+ * folded a `qoder_terminal_repair` note): one {@link poll} reads what is new and returns the note
171
+ * payloads to append to the raw log, in order. `demand` is asked of the processor's current state
172
+ * (`acp/processor/sideChannel.ts` `sideDemand`); what it answered is recorded only through the facts,
173
+ * so the processor's fold never depends on when the daemon polled.
174
+ */
175
+ export class BackgroundSideNotes {
176
+ reader;
177
+ /** Facts over `MAX_SIDE_FACT_BYTES` that could not be recorded (diagnostics). */
178
+ droppedFacts = 0;
179
+ constructor(options) {
180
+ this.reader = new BackgroundSideReader(options);
181
+ }
182
+ async poll(demand) {
183
+ const notes = [];
184
+ await this.reader.read(demand, (fact) => {
185
+ const encoded = sideNotes(fact);
186
+ if (encoded.length === 0)
187
+ this.droppedFacts += 1;
188
+ notes.push(...encoded);
189
+ });
190
+ return notes;
191
+ }
192
+ close() { this.reader.close(); }
193
+ }