@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
@@ -44,7 +44,7 @@ const MAX_READ_RECORDS = 1024;
44
44
  const MAX_READ_BYTES = 1536 * 1024;
45
45
  const MAX_GRACE_MS = 10 * 60 * 1000;
46
46
  const EXEC_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
47
- const START_KEYS = ['execId', 'argv', 'cwd', 'env', 'deadlineEpochMs', 'killGraceMs', 'retainHeadBytes', 'maxUncommittedBytes', 'onKeeperLoss', 'label', 'jobId'];
47
+ const START_KEYS = ['execId', 'argv', 'cwd', 'env', 'deadlineEpochMs', 'killGraceMs', 'retainHeadBytes', 'maxUncommittedBytes', 'onKeeperLoss', 'label', 'jobId', 'registrationId'];
48
48
  /**
49
49
  * Whether a kept record stays in keep.jsonl once the daemon committed through `committed`. The head reserve (kept `out`/`err`
50
50
  * records: the first `retainHeadBytes` of each stream) and the spawn record survive every non-final commit: the daemon shapes the
@@ -305,11 +305,14 @@ export class ExecHost {
305
305
  bad('Invalid jobId');
306
306
  if (this.runningCount() >= (this.options.runningLimit ?? EXEC_RUNNING_LIMIT))
307
307
  throw new KeeperMethodError(ExecErrorCode.limit, 'Too many running commands in keeper-terminal');
308
+ const registrationId = params.registrationId;
309
+ if (registrationId !== undefined && (typeof registrationId !== 'string' || !registrationId || registrationId.length > 128 || /[\x00-\x1f]/.test(registrationId)))
310
+ bad('Invalid registrationId');
308
311
  const meta = {
309
312
  v: 1, execId, label: params.label.slice(0, EXEC_LABEL_CHARS), ...(jobId ? { jobId: jobId } : {}),
310
313
  argv: argv, cwd, envKeys: Object.keys(env).sort(),
311
314
  deadlineEpochMs: typeof deadline === 'number' ? deadline : null, killGraceMs, retainHeadBytes, maxUncommittedBytes, onKeeperLoss: onKeeperLoss,
312
- createdAtMs: this.now(), owner: this.options.owner, spawn: null, exit: null, logReclaimed: null,
315
+ createdAtMs: this.now(), owner: this.options.owner, ...(typeof registrationId === 'string' ? { registrationId } : {}), spawn: null, exit: null, logReclaimed: null,
313
316
  };
314
317
  // Synchronously up to the spawn: a concurrent start of the same id finds the entry and waits for it.
315
318
  let entry;
@@ -811,7 +814,7 @@ export class ExecHost {
811
814
  describe(entry) {
812
815
  const { meta } = entry;
813
816
  return {
814
- execId: meta.execId, label: meta.label, ...(meta.jobId ? { jobId: meta.jobId } : {}), state: this.stateOf(entry),
817
+ execId: meta.execId, label: meta.label, ...(meta.registrationId ? { registrationId: meta.registrationId } : {}), ...(meta.jobId ? { jobId: meta.jobId } : {}), state: this.stateOf(entry),
815
818
  pid: meta.spawn?.pid ?? null, pgid: meta.spawn?.pgid ?? null, startTime: meta.spawn?.startTime ?? null,
816
819
  createdAtMs: meta.createdAtMs, deadlineEpochMs: meta.deadlineEpochMs, onKeeperLoss: meta.onKeeperLoss, exit: meta.exit,
817
820
  head: this.headOf(entry), committed: entry.committed, dropped: entry.dropped,
@@ -0,0 +1,73 @@
1
+ import { KeeperMethodError } from '../kit/protocol.js';
2
+ import { TERMINAL_GRID_LIMITS as limits } from './gridProtocol.js';
3
+ /** Complete immutable snapshots, with deltas computed against any retained base. */
4
+ export class TerminalFrames {
5
+ cache = [];
6
+ bytes = 0;
7
+ /** A failed/oversized capture leaves both the revision and recovery cache untouched. */
8
+ publish(snapshot, session, geometry) {
9
+ validate(snapshot, session);
10
+ const previous = this.cache.at(-1)?.frame;
11
+ const sameSession = previous?.incarnation === session.incarnation && previous.id === session.id;
12
+ const nextGeometry = geometry ?? (sameSession ? previous.geometry + Number(previous.cols !== snapshot.cols || previous.rows !== snapshot.rows) : 1);
13
+ if (!Number.isSafeInteger(nextGeometry) || nextGeometry < 1 || (sameSession && nextGeometry < previous.geometry))
14
+ throw new Error('Invalid terminal geometry');
15
+ const next = structuredClone({
16
+ ...snapshot, schema: 1, id: session.id, incarnation: session.incarnation,
17
+ revision: sameSession ? previous.revision + 1 : 1, baseRevision: 0,
18
+ geometry: nextGeometry, full: true, session,
19
+ });
20
+ if (sameSession && equalContent(previous, next))
21
+ return;
22
+ const bytes = resultBytes(next);
23
+ if (!sameSession) {
24
+ this.cache = [];
25
+ this.bytes = 0;
26
+ }
27
+ this.cache.push({ frame: next, bytes });
28
+ this.bytes += bytes;
29
+ while (this.cache.length > limits.maxCachedFrames || this.bytes > limits.maxFrameCacheBytes)
30
+ this.bytes -= this.cache.shift().bytes;
31
+ }
32
+ poll(after) {
33
+ const current = this.cache.at(-1)?.frame;
34
+ if (!current)
35
+ throw new Error('Terminal frame has not been captured');
36
+ if (after === current.revision)
37
+ return structuredClone({ ...current, full: false, baseRevision: after, styles: [], changedRows: [] });
38
+ const base = after === 0 ? undefined : this.cache.find(entry => entry.frame.revision === after)?.frame;
39
+ if (!base || base.geometry !== current.geometry || base.buffer !== current.buffer || base.incarnation !== current.incarnation)
40
+ return structuredClone(current);
41
+ const changedRows = current.changedRows.filter((row, index) => semanticRow(row, current) !== semanticRow(base.changedRows[index], base));
42
+ const delta = { ...current, full: false, baseRevision: after, changedRows };
43
+ resultBytes(delta);
44
+ return structuredClone(delta);
45
+ }
46
+ cacheStats() { return { frames: this.cache.length, bytes: this.bytes }; }
47
+ }
48
+ function semanticRow(row, frame) {
49
+ return JSON.stringify([row.wrapped ?? false, row.runs.flatMap(run => run.cells.map(cell => [cell.text, cell.width, frame.styles[run.style]]))]);
50
+ }
51
+ function equalContent(a, b) {
52
+ const header = (frame) => JSON.stringify([
53
+ frame.geometry, frame.cols, frame.rows, frame.buffer, frame.viewport, frame.cursor, frame.modes, frame.session,
54
+ ]);
55
+ return header(a) === header(b) && a.changedRows.every((row, index) => semanticRow(row, a) === semanticRow(b.changedRows[index], b));
56
+ }
57
+ function resultBytes(frame) {
58
+ const bytes = Buffer.byteLength(JSON.stringify(frame));
59
+ // Leave room for the keeper response envelope and the tool response wrapper.
60
+ if (bytes > limits.maxResultBytes - 1024)
61
+ throw new KeeperMethodError('pty_snapshot_too_large', 'Terminal frame exceeds result limit; resize to a smaller viewport');
62
+ return bytes;
63
+ }
64
+ function validate(snapshot, session) {
65
+ if (snapshot.cols < limits.minCols || snapshot.cols > limits.maxCols || snapshot.rows < limits.minRows || snapshot.rows > limits.maxRows
66
+ || snapshot.cols !== session.cols || snapshot.rows !== session.rows || snapshot.changedRows.length !== snapshot.rows)
67
+ throw new Error('Terminal snapshot must contain a complete matching grid');
68
+ for (const [index, row] of snapshot.changedRows.entries()) {
69
+ if (row.index !== index || row.runs.reduce((count, run) => count + run.cells.length, 0) !== snapshot.cols
70
+ || row.runs.some(run => !snapshot.styles[run.style] || run.cells.some(cell => typeof cell.text !== 'string' || ![0, 1, 2].includes(cell.width))))
71
+ throw new Error('Terminal snapshot must contain complete rows and styles');
72
+ }
73
+ }
@@ -2,11 +2,8 @@
2
2
  // CLI upgrade an OLDER generation can still host PTYs and execs while a newer one takes new work. Nothing here starts an older
3
3
  // generation: only the current build's client launches keeper-terminal. Exempt from the keeper-side import guard (daemon module).
4
4
  //
5
- // - New work (a `terminal.create`, an exec start) goes to the SERVING generation: the current build's, unless the replacement policy
6
- // (keepers/policy.ts) keeps an older, still compatible generation serving (`replacement: 'available'` and nobody asked to replace
7
- // it — spec §8.3: an available update never does anything by itself).
8
- // - Everything else is routed by OWNER: a PTY id or an exec id is served by the generation that runs it, through a client bound to
9
- // that generation (tools/terminalRouter.ts, exec/policy.ts).
5
+ // Durable exec work uses the serving picker and retains owner routing across generations.
6
+ // Native terminal tools bypass this selection and always use `primary` (tools/terminalRouter.ts).
10
7
  import path from 'node:path';
11
8
  import { readKeeperManifest } from '../kit/bundle.js';
12
9
  import { TerminalKeeperClient } from './client.js';
@@ -93,7 +90,7 @@ export class TerminalGenerations {
93
90
  }
94
91
  return older !== undefined && older !== current ? { gen: older, client: await this.client(older) } : { gen: current, client: this.primary };
95
92
  }
96
- /** `ExecKeeperGenerations.serving` / the router's create target. */
93
+ /** Durable exec creation target. Native terminal creation uses primary directly. */
97
94
  async serving() { return (await this.servingGeneration()).client; }
98
95
  /** Every live generation with its client (the current build's only when it runs). Starts nothing. */
99
96
  async running() {
@@ -109,9 +106,9 @@ export class TerminalGenerations {
109
106
  const serving = await this.serving();
110
107
  return (await this.running()).map((generation) => generation.client).filter((client) => client !== serving);
111
108
  }
112
- /** `tools.info` terminal fields from the serving generation when it already runs, else `fallback`. Starts nothing. */
109
+ /** Native terminal status belongs only to the current build. Starts nothing. */
113
110
  async terminalStatus(fallback) {
114
- return (await this.serving()).terminalStatus(fallback);
111
+ return this.primary.terminalStatus(fallback);
115
112
  }
116
113
  /** Drops the connection to `gen` (it was stopped). */
117
114
  forget(gen) {
@@ -0,0 +1,16 @@
1
+ /** Native terminal wire contract. No runtime/platform dependencies. */
2
+ export const TERMINAL_GRID_SCHEMA = 1;
3
+ export const TERMINAL_KEEPER_PROTOCOL = 2;
4
+ export const TERMINAL_GRID_CAPABILITY = 'pty-grid-v1';
5
+ export const TERMINAL_GRID_LIMITS = {
6
+ minCols: 20, maxCols: 300, minRows: 5, maxRows: 100,
7
+ maxSessions: 8, maxAttachments: 16, leaseMs: 15_000, scrollbackLines: 10_000,
8
+ maxInputBytes: 64 * 1024, maxRequestBytes: 128 * 1024, maxResultBytes: 2 * 1024 * 1024,
9
+ maxCachedFrames: 8, maxFrameCacheBytes: 8 * 1024 * 1024,
10
+ };
11
+ export const TERMINAL_MODIFIERS = { shift: 1, alt: 2, ctrl: 4, meta: 8 };
12
+ export const TERMINAL_STYLE_FLAGS = {
13
+ bold: 1, dim: 2, italic: 4, underline: 8, blink: 16, inverse: 32, invisible: 64, strike: 128,
14
+ };
15
+ export const TERMINAL_DEFAULT_COLOR = -1;
16
+ export const TERMINAL_RGB_COLOR_BASE = 0x1000000;
@@ -0,0 +1,146 @@
1
+ import { KeeperMethodError } from '../kit/protocol.js';
2
+ import { TERMINAL_GRID_LIMITS } from './gridProtocol.js';
3
+ const esc = '\x1b';
4
+ const modifiers = (mask) => 1 + (mask & 1) + (mask & 2) + (mask & 4) + (mask & 8);
5
+ const arrows = { 79: 'C', 80: 'D', 81: 'B', 82: 'A', 74: 'H', 77: 'F' };
6
+ const tilde = { 73: 2, 76: 3, 75: 5, 78: 6, 62: 15, 63: 17, 64: 18, 65: 19, 66: 20, 67: 21, 68: 23, 69: 24 };
7
+ const keypad = {
8
+ 84: ['/', 'o'], 85: ['*', 'j'], 86: ['-', 'm'], 87: ['+', 'k'], 88: ['\r', 'M'],
9
+ 89: ['1', 'q'], 90: ['2', 'r'], 91: ['3', 's'], 92: ['4', 't'], 93: ['5', 'u'], 94: ['6', 'v'],
10
+ 95: ['7', 'w'], 96: ['8', 'x'], 97: ['9', 'y'], 98: ['0', 'p'], 99: ['.', 'n'], 103: ['=', 'X'],
11
+ };
12
+ /** One attachment's only held input is its mouse buttons; modifiers are explicit per event. */
13
+ export class TerminalInputEncoder {
14
+ buttons = new Set();
15
+ release() { this.buttons.clear(); }
16
+ encode(event, screen) {
17
+ switch (event.kind) {
18
+ case 'release':
19
+ this.release();
20
+ return '';
21
+ case 'text': return event.text;
22
+ case 'paste': return screen.modes.bracketedPaste ? `${esc}[200~${event.text}${esc}[201~` : event.text;
23
+ case 'key': return event.down ? encodeKey(event.usage, event.modifiers, screen.modes) : '';
24
+ case 'button': {
25
+ if (event.down)
26
+ this.buttons.add(event.button);
27
+ else
28
+ this.buttons.delete(event.button);
29
+ if (screen.modes.mouseTracking === 'none' || (!event.down && screen.modes.mouseTracking === 'x10'))
30
+ return '';
31
+ return mouse(event.button, event.x, event.y, event.modifiers, screen.modes, !event.down);
32
+ }
33
+ case 'pointer': {
34
+ const tracking = screen.modes.mouseTracking;
35
+ if (tracking !== 'any' && !(tracking === 'drag' && this.buttons.size))
36
+ return '';
37
+ return mouse(32 + (this.buttons.values().next().value ?? 3), event.x, event.y, event.modifiers, screen.modes);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ export function createTerminalInput(screen) {
43
+ const clients = new Map();
44
+ const encoder = (id) => { let value = clients.get(id); if (!value) {
45
+ value = new TerminalInputEncoder();
46
+ clients.set(id, value);
47
+ } return value; };
48
+ return {
49
+ input(event, clientId, context) { return encoder(clientId).encode(event, context); },
50
+ scroll(gesture, _clientId, context) {
51
+ if (!gesture.lines)
52
+ return '';
53
+ const state = screen.state();
54
+ if (context.modes.mouseTracking === 'x10')
55
+ return '';
56
+ if (context.modes.mouseTracking !== 'none')
57
+ return repeat(mouse(gesture.lines < 0 ? 64 : 65, gesture.x, gesture.y, gesture.modifiers, context.modes), Math.abs(gesture.lines));
58
+ if (state.buffer === 'normal')
59
+ return undefined;
60
+ if (!context.modes.alternateScroll)
61
+ return '';
62
+ return repeat(encodeKey(gesture.lines < 0 ? 82 : 81, gesture.modifiers, context.modes), Math.abs(gesture.lines));
63
+ },
64
+ release(clientId) { clients.get(clientId)?.release(); clients.delete(clientId); },
65
+ dispose() { clients.clear(); },
66
+ };
67
+ }
68
+ function repeat(bytes, count) {
69
+ if (!bytes)
70
+ return '';
71
+ if (count > Math.floor(TERMINAL_GRID_LIMITS.maxInputBytes / Buffer.byteLength(bytes)))
72
+ throw new KeeperMethodError('bad_request', 'Terminal input exceeds limit');
73
+ if (typeof bytes === 'string')
74
+ return bytes.repeat(count);
75
+ return Buffer.concat(Array.from({ length: count }, () => bytes));
76
+ }
77
+ function mouse(button, x, y, mask, modes, release = false) {
78
+ // The wire carries cells, so pixel encoding cannot be represented faithfully.
79
+ if (modes.mouseEncoding === 'sgr-pixels')
80
+ return '';
81
+ const mods = modes.mouseTracking === 'x10' ? 0 : (mask & 1 ? 4 : 0) + (mask & 2 ? 8 : 0) + (mask & 4 ? 16 : 0);
82
+ if (modes.mouseEncoding === 'sgr')
83
+ return `${esc}[<${button + mods};${x + 1};${y + 1}${release ? 'm' : 'M'}`;
84
+ const code = (release ? 3 : button) + mods + 32;
85
+ if (modes.mouseEncoding === 'urxvt')
86
+ return `${esc}[${code};${x + 1};${y + 1}M`;
87
+ if (modes.mouseEncoding === 'default' && (x > 222 || y > 222))
88
+ return '';
89
+ if (modes.mouseEncoding === 'default')
90
+ return Buffer.from([27, 91, 77, code, x + 33, y + 33]);
91
+ return `${esc}[M${String.fromCharCode(code, x + 33, y + 33)}`;
92
+ }
93
+ function encodeKey(usage, mask, modes) {
94
+ const mod = modifiers(mask);
95
+ if (arrows[usage])
96
+ return mod > 1 ? `${esc}[1;${mod}${arrows[usage]}` : `${esc}${modes.applicationCursor ? 'O' : '['}${arrows[usage]}`;
97
+ if (tilde[usage])
98
+ return `${esc}[${tilde[usage]}${mod > 1 ? `;${mod}` : ''}~`;
99
+ if (usage >= 58 && usage <= 61)
100
+ return mod > 1 ? `${esc}[1;${mod}${String.fromCharCode(80 + usage - 58)}` : `${esc}O${String.fromCharCode(80 + usage - 58)}`;
101
+ if (keypad[usage]) {
102
+ if (mask & 8 && usage !== 88)
103
+ return '';
104
+ return modes.applicationKeypad ? mod > 1 ? `${esc}[1;${mod}${keypad[usage][1]}` : `${esc}O${keypad[usage][1]}` : prefixed(keypad[usage][0], mask);
105
+ }
106
+ if (usage === 40)
107
+ return prefixed('\r', mask);
108
+ if (usage === 41)
109
+ return esc;
110
+ if (usage === 42)
111
+ return prefixed(mask & 4 ? '\b' : '\x7f', mask);
112
+ if (usage === 43)
113
+ return mask & 1 ? `${esc}[Z` : prefixed('\t', mask);
114
+ if (mask & 8)
115
+ return ''; // Native clients handle local Meta shortcuts.
116
+ let text = '';
117
+ if (usage >= 4 && usage <= 29)
118
+ text = String.fromCharCode((mask & 1 ? 65 : 97) + usage - 4);
119
+ else if (usage >= 30 && usage <= 39)
120
+ text = (mask & 1 ? '!@#$%^&*()' : '1234567890')[usage - 30];
121
+ else {
122
+ const punctuation = { 44: [' ', ' '], 45: ['-', '_'], 46: ['=', '+'], 47: ['[', '{'], 48: [']', '}'], 49: ['\\', '|'], 50: ['#', '~'], 51: [';', ':'], 52: ["'", '"'], 53: ['`', '~'], 54: [',', '<'], 55: ['.', '>'], 56: ['/', '?'] };
123
+ text = punctuation[usage]?.[mask & 1 ? 1 : 0] ?? '';
124
+ }
125
+ if (mask & 4) {
126
+ const upper = text.toUpperCase();
127
+ if (upper >= '@' && upper <= '_')
128
+ text = String.fromCharCode(upper.charCodeAt(0) - 64);
129
+ else if (text === ' ' || text === '2' || text === '`')
130
+ text = '\0';
131
+ else if (text === '3')
132
+ text = esc;
133
+ else if (text === '4')
134
+ text = '\x1c';
135
+ else if (text === '5')
136
+ text = '\x1d';
137
+ else if (text === '6')
138
+ text = '\x1e';
139
+ else if (text === '7' || text === '/')
140
+ text = '\x1f';
141
+ else if (text === '8' || text === '?')
142
+ text = '\x7f';
143
+ }
144
+ return prefixed(text, mask);
145
+ }
146
+ function prefixed(text, mask) { return text && mask & 2 ? esc + text : text; }
@@ -1,8 +1,9 @@
1
- // keeper-terminal's methods on the kit server: `pty.*` over PtySessions, `exec.*` over ExecHost, plus their activity samplers.
1
+ // keeper-terminal's methods on the kit server: `pty.*` over NativePtySessions, `exec.*` over ExecHost, plus their activity samplers.
2
2
  // Keeper-side module: node builtins and kit siblings only (the import guard enforces it).
3
3
  import { constants, promises as fs } from 'node:fs';
4
4
  import { absolutePath, badRequest as bad, environment } from './params.js';
5
- import { CREATE_KEYS, PtySessions } from './pty.js';
5
+ import { createRequire } from 'node:module';
6
+ import { NativePtySessions, NATIVE_PTY_OPERATIONS } from './sessions.js';
6
7
  import { KeeperMethodError } from '../kit/protocol.js';
7
8
  import { EXEC_CAPABILITY, ExecErrorCode, PTY_CAPABILITY, execStream } from './protocol.js';
8
9
  /**
@@ -21,18 +22,31 @@ async function launchOf(p) {
21
22
  bad('Terminal shell is not an executable file');
22
23
  return { cwd, shell, env };
23
24
  }
24
- export const KEEPER_CREATE_KEYS = [...CREATE_KEYS, 'shell', 'env'];
25
+ export const KEEPER_CREATE_KEYS = ['id', 'cwd', 'shell', 'env', 'registrationId'];
25
26
  export function createTerminalKeeper(options = {}) {
26
- const sessions = new PtySessions(options);
27
+ let available = false;
28
+ const sessions = new NativePtySessions({ ...options, loadPty: () => {
29
+ const module = (options.loadPty ?? (() => createRequire(import.meta.url)('node-pty')))();
30
+ available = true;
31
+ return module;
32
+ } });
27
33
  const exec = options.exec;
28
34
  return {
29
35
  sessions,
30
36
  exec,
31
- capabilities: [...(sessions.available ? [PTY_CAPABILITY] : []), ...(exec ? [EXEC_CAPABILITY] : [])],
37
+ capabilities: [...(available ? [PTY_CAPABILITY] : []), ...(exec ? [EXEC_CAPABILITY] : [])],
32
38
  install(server) {
33
- server.register('pty.create', (params) => sessions.create(params, KEEPER_CREATE_KEYS, () => launchOf(params)), { startsWork: true });
34
- server.register('pty.list', (params) => sessions.list(params));
35
- for (const op of ['attach', 'poll', 'write', 'resize', 'takeover', 'detach', 'close'])
39
+ server.register('pty.create', (params) => {
40
+ if (Object.keys(params).some(key => !KEEPER_CREATE_KEYS.includes(key)))
41
+ bad('Unexpected tool parameter');
42
+ return sessions.create({ id: params.id, ...(params.cwd !== undefined ? { cwd: params.cwd } : {}), ...(params.registrationId !== undefined ? { registrationId: params.registrationId } : {}) }, () => launchOf(params));
43
+ }, { startsWork: true });
44
+ server.register('pty.list', (params) => {
45
+ if (Object.keys(params).length)
46
+ bad('Unexpected tool parameter');
47
+ return sessions.list();
48
+ });
49
+ for (const op of NATIVE_PTY_OPERATIONS)
36
50
  server.register(`pty.${op}`, (params) => sessions.operate(op, params));
37
51
  server.addActivitySampler(() => sessions.activity());
38
52
  if (!exec)
@@ -8,7 +8,7 @@ import path, { sep } from 'node:path';
8
8
  import { runKeeperMain } from '../kit/bootstrap.js';
9
9
  import { ExecHost } from './exec.js';
10
10
  import { createTerminalKeeper } from './keeper.js';
11
- export const protocolVersion = 1;
11
+ export const protocolVersion = 2;
12
12
  /**
13
13
  * node-pty from the bundle snapshot's own `node_modules` (copied next to keeper.mjs), never one found further up the
14
14
  * directory tree: the snapshot is what the build hashed. Missing or foreign → PTYs unavailable (`pty` capability absent).
@@ -0,0 +1,138 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { readlink } from 'node:fs/promises';
3
+ import { hostname as localHostname } from 'node:os';
4
+ import path from 'node:path';
5
+ const clean = (value) => [...value.replace(/[\x00-\x1f\x7f-\x9f]/g, '').trim()].slice(0, 256).join('');
6
+ const validCwd = (value) => value.startsWith('/') && !/[\x00-\x1f\x7f-\x9f]/.test(value);
7
+ /** Resolved display metadata; native xterm hooks are the only OSC parser. */
8
+ export class TerminalMetadata {
9
+ options;
10
+ cwd;
11
+ cwdSource = 'launch';
12
+ explicitTitle = '';
13
+ listeners;
14
+ now;
15
+ hostname;
16
+ lastLookup = -Infinity;
17
+ oscRevision = 0;
18
+ closed = false;
19
+ pending;
20
+ cancelLookup;
21
+ timer;
22
+ constructor(options) {
23
+ this.options = options;
24
+ this.cwd = options.cwd;
25
+ this.now = options.now ?? Date.now;
26
+ this.hostname = (options.hostname ?? localHostname()).toLowerCase();
27
+ this.listeners = [options.screen.onTitleChange(title => {
28
+ if (this.closed)
29
+ return;
30
+ const previous = this.title;
31
+ this.explicitTitle = clean(title);
32
+ if (this.title !== previous)
33
+ options.changed();
34
+ }), options.screen.onCwdChange(uri => this.osc7(uri))];
35
+ }
36
+ get title() { return this.explicitTitle || clean(path.posix.basename(this.cwd) || this.cwd) || 'Terminal'; }
37
+ read() { return { title: this.title, cwd: this.cwd, cwdSource: this.cwdSource }; }
38
+ /** Called once by the host factory, independently of output and attachments. */
39
+ start() {
40
+ if (this.closed || this.timer)
41
+ return;
42
+ void this.refresh();
43
+ this.timer = setInterval(() => { void this.refresh(); }, 2000);
44
+ this.timer.unref();
45
+ }
46
+ refresh() {
47
+ if (this.closed || this.options.live?.() === false)
48
+ return Promise.resolve();
49
+ if (this.pending)
50
+ return this.pending;
51
+ if (this.now() - this.lastLookup < 2000)
52
+ return Promise.resolve();
53
+ this.lastLookup = this.now();
54
+ const revision = this.oscRevision;
55
+ let timeout;
56
+ const bounded = new Promise(resolve => {
57
+ timeout = setTimeout(() => resolve(null), 1000);
58
+ this.cancelLookup = () => resolve(null);
59
+ // Normalize synchronous failures too; no failed lookup invalidates the last cwd.
60
+ try {
61
+ void (this.options.lookup ?? lookupProcessCwd)(this.options.pid).then(resolve, () => resolve(null));
62
+ }
63
+ catch {
64
+ resolve(null);
65
+ }
66
+ });
67
+ this.pending = bounded.then(cwd => {
68
+ if (!this.closed && this.options.live?.() !== false && revision === this.oscRevision && cwd && validCwd(cwd))
69
+ this.updateCwd(cwd, 'process');
70
+ }).finally(() => { clearTimeout(timeout); this.cancelLookup = undefined; this.pending = undefined; });
71
+ return this.pending;
72
+ }
73
+ dispose() {
74
+ if (this.closed)
75
+ return;
76
+ this.closed = true;
77
+ clearInterval(this.timer);
78
+ this.timer = undefined;
79
+ this.cancelLookup?.();
80
+ for (const listener of this.listeners)
81
+ listener.dispose();
82
+ }
83
+ osc7(uri) {
84
+ if (this.closed)
85
+ return;
86
+ try {
87
+ if (/[\x00-\x1f\x7f-\x9f]/.test(uri))
88
+ return;
89
+ const url = new URL(uri);
90
+ if (url.protocol !== 'file:' || url.username || url.password || url.port || uri.includes('?') || uri.includes('#'))
91
+ return;
92
+ const host = url.hostname.toLowerCase();
93
+ if (host && host !== 'localhost' && host !== this.hostname)
94
+ return;
95
+ const cwd = decodeURIComponent(url.pathname);
96
+ if (!validCwd(cwd))
97
+ return;
98
+ this.oscRevision++;
99
+ this.updateCwd(cwd, 'osc7');
100
+ }
101
+ catch { /* malformed/untrusted OSC payload is data, never executable */ }
102
+ }
103
+ updateCwd(cwd, source) {
104
+ if (cwd === this.cwd && source === this.cwdSource)
105
+ return;
106
+ this.cwd = cwd;
107
+ this.cwdSource = source;
108
+ this.options.changed();
109
+ }
110
+ }
111
+ /** No shell interpolation: lsof receives the shell pid only as an argv element. */
112
+ export async function lookupProcessCwd(pid, platform = process.platform) {
113
+ if (!Number.isSafeInteger(pid) || pid <= 0)
114
+ return null;
115
+ if (platform === 'linux') {
116
+ try {
117
+ return await readlink(`/proc/${pid}/cwd`);
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ }
123
+ if (platform !== 'darwin')
124
+ return null;
125
+ return new Promise(resolve => {
126
+ execFile('lsof', ['-a', '-p', String(pid), '-d', 'cwd', '-Fn'], { timeout: 1000, maxBuffer: 64 * 1024 }, (error, stdout) => {
127
+ resolve(error ? null : stdout.split('\n').find(line => line.startsWith('n/'))?.slice(1) ?? null);
128
+ });
129
+ });
130
+ }
131
+ export function createTerminalMetadata(context) {
132
+ // Capture initial values before installing metadata; read() must never call session().
133
+ const initial = context.session();
134
+ const metadata = new TerminalMetadata({ screen: context.screen, cwd: initial.cwd, pid: context.pty.pid,
135
+ changed: context.changed, live: () => !context.session().exited });
136
+ metadata.start();
137
+ return metadata;
138
+ }
@@ -1,32 +1,8 @@
1
- // keeper-terminal's wire vocabulary, shared by the keeper (`main.ts`, `pty.ts`) and the daemon (`client.ts`).
2
- // Keeper-side module: node builtins and kit siblings only (the import guard enforces it). Spec §4.1.
3
- /** The keeper protocol this code speaks. `main.ts` declares the same number for the bundler (a test keeps them equal). */
4
- export const TERMINAL_PROTOCOL_VERSION = 1;
5
- /** `keeper.hello` capability: node-pty loaded from the bundle snapshot, so `pty.*` works. Absent = PTYs unavailable. */
6
- export const PTY_CAPABILITY = 'pty';
7
- /** The legacy keeper's wording (tools/terminal.ts at the merge-base) when node-pty does not load; every `pty.*` call but `pty.list` answers it. */
1
+ // Terminal transport is a direct native-grid protocol replacement. Durable exec vocabulary follows.
2
+ export const TERMINAL_PROTOCOL_VERSION = 2;
3
+ export const PTY_CAPABILITY = 'pty-grid-v1';
8
4
  export const PTY_UNAVAILABLE_ERROR = 'Terminal native dependency unavailable; reinstall CLI with optional node-pty support';
9
- /** Ring of rendered output kept per session, the largest poll delta, the scrollback serialised into a snapshot. */
10
- export const PTY_HISTORY_BYTES = 1024 * 1024;
11
- export const PTY_MAX_POLL_BYTES = 64 * 1024;
12
- export const PTY_SCROLLBACK = 1000;
13
- /** A snapshot must fit one tool result (2 MiB, the same as keeper-terminal's outbound frame cap). */
14
- export const PTY_RESULT_BYTES = 2 * 1024 * 1024;
15
- export const PTY_SESSION_LIMIT = 8;
16
- export const PTY_INPUT_BYTES = 64 * 1024;
17
- /** Longest activity label (the foreground process basename; never its arguments). */
18
- export const PTY_LABEL_CHARS = 64;
19
- export const PTY_METHODS = ['pty.create', 'pty.attach', 'pty.poll', 'pty.write', 'pty.resize', 'pty.takeover', 'pty.detach', 'pty.close', 'pty.list'];
20
- /** Typed error codes of `pty.*` (the message keeps today's `terminal.*` wording). */
21
- export const PtyErrorCode = {
22
- unavailable: 'pty_unavailable',
23
- badRequest: 'bad_request',
24
- notFound: 'pty_not_found',
25
- notController: 'pty_not_controller',
26
- limit: 'pty_limit',
27
- exited: 'pty_exited',
28
- snapshotTooLarge: 'pty_snapshot_too_large',
29
- };
5
+ export const PTY_METHODS = ['pty.list', 'pty.create', 'pty.attach', 'pty.poll', 'pty.resize', 'pty.input', 'pty.scroll', 'pty.detach', 'pty.close'];
30
6
  // ---- exec (spec §4.2): non-interactive commands with durable per-exec output logs ----------------------------------
31
7
  /** `keeper.hello` capability: `exec.*` is served. */
32
8
  export const EXEC_CAPABILITY = 'exec';