@myagentroam/node 0.1.3 → 0.1.6

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 (195) hide show
  1. package/README.md +9 -1
  2. package/dist/capabilities.d.ts +2 -0
  3. package/dist/capabilities.js +15 -4
  4. package/dist/claude-agent-sdk.d.ts +4 -16
  5. package/dist/claude-agent-sdk.js +18 -79
  6. package/dist/claude-channel.js +0 -1
  7. package/dist/codex-app-server.d.ts +5 -0
  8. package/dist/codex-app-server.js +26 -5
  9. package/dist/config.d.ts +3 -0
  10. package/dist/config.js +18 -2
  11. package/dist/connector/node-connector-options.d.ts +26 -0
  12. package/dist/connector/node-connector-options.js +1 -0
  13. package/dist/connector/node-operation-router.d.ts +9 -0
  14. package/dist/connector/node-operation-router.js +22 -0
  15. package/dist/connector.d.ts +62 -356
  16. package/dist/connector.js +714 -6176
  17. package/dist/control-outbound-scheduler.d.ts +26 -0
  18. package/dist/control-outbound-scheduler.js +121 -0
  19. package/dist/database.d.ts +4 -0
  20. package/dist/database.js +15 -10
  21. package/dist/event-buffer.js +0 -1
  22. package/dist/fake-runner.js +0 -1
  23. package/dist/health.js +0 -1
  24. package/dist/main.js +6 -1
  25. package/dist/migrations/v001.js +0 -1
  26. package/dist/migrations/v002.js +0 -1
  27. package/dist/migrations/v003.d.ts +4 -0
  28. package/dist/migrations/v003.js +44 -0
  29. package/dist/native-jsonl-reader.d.ts +14 -0
  30. package/dist/native-jsonl-reader.js +97 -0
  31. package/dist/native-session-history.d.ts +41 -4
  32. package/dist/native-session-history.js +340 -159
  33. package/dist/opencode-runtime.d.ts +33 -0
  34. package/dist/opencode-runtime.js +80 -0
  35. package/dist/opencode-server.d.ts +47 -0
  36. package/dist/opencode-server.js +250 -0
  37. package/dist/operational.js +10 -3
  38. package/dist/process-tree.js +0 -1
  39. package/dist/runner/abstract-runner.d.ts +153 -0
  40. package/dist/runner/abstract-runner.js +154 -0
  41. package/dist/runner/claude/managed-run-controller.d.ts +52 -0
  42. package/dist/runner/claude/managed-run-controller.js +326 -0
  43. package/dist/runner/claude-code-runner.d.ts +70 -0
  44. package/dist/runner/claude-code-runner.js +286 -0
  45. package/dist/runner/codex/conversation-parser.d.ts +59 -0
  46. package/dist/runner/codex/conversation-parser.js +816 -0
  47. package/dist/runner/codex/managed-run-controller.d.ts +80 -0
  48. package/dist/runner/codex/managed-run-controller.js +388 -0
  49. package/dist/runner/codex-runner.d.ts +85 -0
  50. package/dist/runner/codex-runner.js +365 -0
  51. package/dist/runner/fake-test-runner.d.ts +26 -0
  52. package/dist/runner/fake-test-runner.js +47 -0
  53. package/dist/runner/opencode/conversation-parser.d.ts +27 -0
  54. package/dist/runner/opencode/conversation-parser.js +217 -0
  55. package/dist/runner/opencode/managed-run-controller.d.ts +76 -0
  56. package/dist/runner/opencode/managed-run-controller.js +451 -0
  57. package/dist/runner/opencode-runner.d.ts +60 -0
  58. package/dist/runner/opencode-runner.js +330 -0
  59. package/dist/runner/runner-registry.d.ts +19 -0
  60. package/dist/runner/runner-registry.js +75 -0
  61. package/dist/runner-command-engine.js +0 -1
  62. package/dist/runner-profiles.d.ts +4 -0
  63. package/dist/runner-profiles.js +76 -3
  64. package/dist/runner-usage.d.ts +2 -2
  65. package/dist/runner-usage.js +3 -6
  66. package/dist/runtime-state.js +26 -14
  67. package/dist/service/attachment-domain-state.d.ts +5 -0
  68. package/dist/service/attachment-domain-state.js +5 -0
  69. package/dist/service/conversation-history-service.d.ts +34 -0
  70. package/dist/service/conversation-history-service.js +158 -0
  71. package/dist/service/external-session-resume-service.d.ts +32 -0
  72. package/dist/service/external-session-resume-service.js +98 -0
  73. package/dist/service/fake-managed-run-service.d.ts +29 -0
  74. package/dist/service/fake-managed-run-service.js +62 -0
  75. package/dist/service/managed-runner-session-service.d.ts +12 -0
  76. package/dist/service/managed-runner-session-service.js +35 -0
  77. package/dist/service/native-session-projection-service.d.ts +51 -0
  78. package/dist/service/native-session-projection-service.js +271 -0
  79. package/dist/service/native-session-watch-service.d.ts +37 -0
  80. package/dist/service/native-session-watch-service.js +150 -0
  81. package/dist/service/node-connection-lifecycle-service.d.ts +37 -0
  82. package/dist/service/node-connection-lifecycle-service.js +85 -0
  83. package/dist/service/node-control-channel.d.ts +32 -0
  84. package/dist/service/node-control-channel.js +85 -0
  85. package/dist/service/node-control-message-service.d.ts +28 -0
  86. package/dist/service/node-control-message-service.js +104 -0
  87. package/dist/service/node-request-service.d.ts +14 -0
  88. package/dist/service/node-request-service.js +50 -0
  89. package/dist/service/node-run-event-bridge.d.ts +40 -0
  90. package/dist/service/node-run-event-bridge.js +83 -0
  91. package/dist/service/node-terminal-channel.d.ts +29 -0
  92. package/dist/service/node-terminal-channel.js +182 -0
  93. package/dist/service/node-upgrade-coordinator.d.ts +29 -0
  94. package/dist/service/node-upgrade-coordinator.js +137 -0
  95. package/dist/service/node-workspace-coordinator.d.ts +32 -0
  96. package/dist/service/node-workspace-coordinator.js +80 -0
  97. package/dist/service/run-attachment-service.d.ts +37 -0
  98. package/dist/service/run-attachment-service.js +160 -0
  99. package/dist/service/run-domain-state.d.ts +4 -0
  100. package/dist/service/run-domain-state.js +4 -0
  101. package/dist/service/run-event-service.d.ts +31 -0
  102. package/dist/service/run-event-service.js +102 -0
  103. package/dist/service/run-workbench-service.d.ts +27 -0
  104. package/dist/service/run-workbench-service.js +113 -0
  105. package/dist/service/runner-channel-message-service.d.ts +13 -0
  106. package/dist/service/runner-channel-message-service.js +20 -0
  107. package/dist/service/runner-interaction-service.d.ts +40 -0
  108. package/dist/service/runner-interaction-service.js +246 -0
  109. package/dist/service/runner-service.d.ts +44 -0
  110. package/dist/service/runner-service.js +68 -0
  111. package/dist/service/session-catalog-service.d.ts +45 -0
  112. package/dist/service/session-catalog-service.js +224 -0
  113. package/dist/service/session-command-service.d.ts +23 -0
  114. package/dist/service/session-command-service.js +69 -0
  115. package/dist/service/session-context-service.d.ts +34 -0
  116. package/dist/service/session-context-service.js +87 -0
  117. package/dist/service/session-domain-state.d.ts +8 -0
  118. package/dist/service/session-domain-state.js +8 -0
  119. package/dist/service/session-identity-service.d.ts +24 -0
  120. package/dist/service/session-identity-service.js +86 -0
  121. package/dist/service/session-lifecycle-service.d.ts +31 -0
  122. package/dist/service/session-lifecycle-service.js +242 -0
  123. package/dist/service/session-message-service.d.ts +33 -0
  124. package/dist/service/session-message-service.js +157 -0
  125. package/dist/service/session-presentation-service.d.ts +33 -0
  126. package/dist/service/session-presentation-service.js +36 -0
  127. package/dist/service/session-query-service.d.ts +31 -0
  128. package/dist/service/session-query-service.js +77 -0
  129. package/dist/service/terminal-relay-state.d.ts +6 -0
  130. package/dist/service/terminal-relay-state.js +3 -0
  131. package/dist/service/terminal-service.d.ts +12 -0
  132. package/dist/service/terminal-service.js +52 -0
  133. package/dist/service/workbench-event-service.d.ts +21 -0
  134. package/dist/service/workbench-event-service.js +53 -0
  135. package/dist/service/workbench-manifest-service.d.ts +89 -0
  136. package/dist/service/workbench-manifest-service.js +70 -0
  137. package/dist/service/workspace-change-service.d.ts +9 -0
  138. package/dist/service/workspace-change-service.js +86 -0
  139. package/dist/service/workspace-content-search-service.d.ts +14 -0
  140. package/dist/service/workspace-content-search-service.js +477 -0
  141. package/dist/service/workspace-domain-state.d.ts +75 -0
  142. package/dist/service/workspace-domain-state.js +40 -0
  143. package/dist/service/workspace-file-service.d.ts +17 -0
  144. package/dist/service/workspace-file-service.js +109 -0
  145. package/dist/service/workspace-queue-workbench-service.d.ts +55 -0
  146. package/dist/service/workspace-queue-workbench-service.js +226 -0
  147. package/dist/service/workspace-service.d.ts +15 -0
  148. package/dist/service/workspace-service.js +67 -0
  149. package/dist/service/workspace-session-service.d.ts +23 -0
  150. package/dist/service/workspace-session-service.js +139 -0
  151. package/dist/service/workspace-upload-service.d.ts +37 -0
  152. package/dist/service/workspace-upload-service.js +240 -0
  153. package/dist/service/workspace-watch-service.d.ts +18 -0
  154. package/dist/service/workspace-watch-service.js +120 -0
  155. package/dist/service/workspace-workbench-service.d.ts +47 -0
  156. package/dist/service/workspace-workbench-service.js +223 -0
  157. package/dist/service.js +2 -3
  158. package/dist/storage.js +0 -1
  159. package/dist/supervisor.d.ts +1 -0
  160. package/dist/supervisor.js +154 -0
  161. package/dist/terminal.d.ts +1 -0
  162. package/dist/terminal.js +1 -2
  163. package/dist/util/node-operation-parsers.d.ts +92 -0
  164. package/dist/util/node-operation-parsers.js +382 -0
  165. package/dist/util/runner-native-session-parsers.d.ts +72 -0
  166. package/dist/util/runner-native-session-parsers.js +381 -0
  167. package/dist/util/secret-environment.d.ts +1 -0
  168. package/dist/util/secret-environment.js +25 -0
  169. package/dist/workspace.d.ts +62 -2
  170. package/dist/workspace.js +539 -57
  171. package/package.json +3 -2
  172. package/dist/capabilities.js.map +0 -1
  173. package/dist/claude-agent-sdk.js.map +0 -1
  174. package/dist/claude-channel.js.map +0 -1
  175. package/dist/codex-app-server.js.map +0 -1
  176. package/dist/config.js.map +0 -1
  177. package/dist/connector.js.map +0 -1
  178. package/dist/database.js.map +0 -1
  179. package/dist/event-buffer.js.map +0 -1
  180. package/dist/fake-runner.js.map +0 -1
  181. package/dist/health.js.map +0 -1
  182. package/dist/main.js.map +0 -1
  183. package/dist/migrations/v001.js.map +0 -1
  184. package/dist/migrations/v002.js.map +0 -1
  185. package/dist/native-session-history.js.map +0 -1
  186. package/dist/operational.js.map +0 -1
  187. package/dist/process-tree.js.map +0 -1
  188. package/dist/runner-command-engine.js.map +0 -1
  189. package/dist/runner-profiles.js.map +0 -1
  190. package/dist/runner-usage.js.map +0 -1
  191. package/dist/runtime-state.js.map +0 -1
  192. package/dist/service.js.map +0 -1
  193. package/dist/storage.js.map +0 -1
  194. package/dist/terminal.js.map +0 -1
  195. package/dist/workspace.js.map +0 -1
@@ -0,0 +1,26 @@
1
+ import type { NodeEnvelope } from '@myagentroam/protocol';
2
+ interface WritableControlSocket {
3
+ readonly readyState: number;
4
+ readonly bufferedAmount: number;
5
+ send(value: string): void;
6
+ }
7
+ /** Keeps control responses responsive while reliable Workbench events are busy. */
8
+ export declare class ControlOutboundScheduler {
9
+ private readonly socket;
10
+ private readonly high;
11
+ private readonly normal;
12
+ private scheduled;
13
+ private retryTimer;
14
+ private backpressureStartedAt;
15
+ private backpressureLogged;
16
+ private maximumHighDepth;
17
+ private maximumNormalDepth;
18
+ constructor(socket: () => WritableControlSocket | undefined);
19
+ enqueue(envelope: NodeEnvelope, priority: 'high' | 'normal'): void;
20
+ reset(): void;
21
+ private schedule;
22
+ private flush;
23
+ private waitForBackpressure;
24
+ private finishBackpressure;
25
+ }
26
+ export {};
@@ -0,0 +1,121 @@
1
+ import { nodeLog } from './operational.js';
2
+ const MAX_FRAMES_PER_TURN = 32;
3
+ const MAX_BYTES_PER_TURN = 256 * 1024;
4
+ const SOCKET_NORMAL_DIRECT_WATER_BYTES = 256 * 1024;
5
+ const SOCKET_HIGH_WATER_BYTES = 1024 * 1024;
6
+ const BACKPRESSURE_RETRY_MS = 5;
7
+ /** Keeps control responses responsive while reliable Workbench events are busy. */
8
+ export class ControlOutboundScheduler {
9
+ socket;
10
+ high = [];
11
+ normal = [];
12
+ scheduled = false;
13
+ retryTimer;
14
+ backpressureStartedAt;
15
+ backpressureLogged = false;
16
+ maximumHighDepth = 0;
17
+ maximumNormalDepth = 0;
18
+ constructor(socket) {
19
+ this.socket = socket;
20
+ }
21
+ enqueue(envelope, priority) {
22
+ const socket = this.socket();
23
+ const bufferedAmount = socket?.bufferedAmount ?? 0;
24
+ if (socket !== undefined &&
25
+ socket.readyState === 1 &&
26
+ this.high.length === 0 &&
27
+ (priority === 'high' || this.normal.length === 0) &&
28
+ bufferedAmount <= SOCKET_NORMAL_DIRECT_WATER_BYTES) {
29
+ socket.send(JSON.stringify(envelope));
30
+ return;
31
+ }
32
+ (priority === 'high' ? this.high : this.normal).push(envelope);
33
+ this.maximumHighDepth = Math.max(this.maximumHighDepth, this.high.length);
34
+ this.maximumNormalDepth = Math.max(this.maximumNormalDepth, this.normal.length);
35
+ this.schedule();
36
+ }
37
+ reset() {
38
+ this.high.length = 0;
39
+ this.normal.length = 0;
40
+ this.scheduled = false;
41
+ if (this.retryTimer !== undefined)
42
+ clearTimeout(this.retryTimer);
43
+ this.retryTimer = undefined;
44
+ this.finishBackpressure('reset');
45
+ }
46
+ schedule() {
47
+ if (this.scheduled || this.retryTimer !== undefined)
48
+ return;
49
+ this.scheduled = true;
50
+ setImmediate(() => this.flush());
51
+ }
52
+ flush() {
53
+ this.scheduled = false;
54
+ const socket = this.socket();
55
+ if (socket === undefined || socket.readyState !== 1)
56
+ return;
57
+ if ((socket.bufferedAmount ?? 0) > SOCKET_HIGH_WATER_BYTES) {
58
+ this.waitForBackpressure(socket, 'high');
59
+ return;
60
+ }
61
+ let frames = 0;
62
+ let bytes = 0;
63
+ while (frames < MAX_FRAMES_PER_TURN) {
64
+ const queue = this.high.length > 0 ? this.high : this.normal;
65
+ const envelope = queue[0];
66
+ if (envelope === undefined)
67
+ break;
68
+ // Reliable Workbench events may be replayed in a large burst after a
69
+ // reconnect. Stop normal traffic early so a later HTTP/control response
70
+ // always has WebSocket buffer headroom instead of waiting behind that burst.
71
+ if (queue === this.normal &&
72
+ (socket.bufferedAmount ?? 0) > SOCKET_NORMAL_DIRECT_WATER_BYTES) {
73
+ this.waitForBackpressure(socket, 'normal');
74
+ return;
75
+ }
76
+ const serialized = JSON.stringify(envelope);
77
+ const size = Buffer.byteLength(serialized);
78
+ if (frames > 0 && bytes + size > MAX_BYTES_PER_TURN)
79
+ break;
80
+ queue.shift();
81
+ socket.send(serialized);
82
+ frames += 1;
83
+ bytes += size;
84
+ if ((socket.bufferedAmount ?? 0) > SOCKET_HIGH_WATER_BYTES)
85
+ break;
86
+ }
87
+ if (this.high.length > 0 || this.normal.length > 0)
88
+ this.schedule();
89
+ else
90
+ this.finishBackpressure('drained');
91
+ }
92
+ waitForBackpressure(socket, level) {
93
+ this.backpressureStartedAt ??= Date.now();
94
+ if (!this.backpressureLogged && Date.now() - this.backpressureStartedAt >= 250) {
95
+ this.backpressureLogged = true;
96
+ nodeLog('control.outbound.backpressure', {
97
+ level,
98
+ bufferedBytes: socket.bufferedAmount,
99
+ highDepth: this.high.length,
100
+ normalDepth: this.normal.length
101
+ });
102
+ }
103
+ this.retryTimer = setTimeout(() => {
104
+ this.retryTimer = undefined;
105
+ this.schedule();
106
+ }, BACKPRESSURE_RETRY_MS);
107
+ }
108
+ finishBackpressure(reason) {
109
+ if (this.backpressureLogged)
110
+ nodeLog('control.outbound.backpressure.recovered', {
111
+ reason,
112
+ durationMs: Date.now() - (this.backpressureStartedAt ?? Date.now()),
113
+ maximumHighDepth: this.maximumHighDepth,
114
+ maximumNormalDepth: this.maximumNormalDepth
115
+ });
116
+ this.backpressureStartedAt = undefined;
117
+ this.backpressureLogged = false;
118
+ this.maximumHighDepth = 0;
119
+ this.maximumNormalDepth = 0;
120
+ }
121
+ }
@@ -106,6 +106,10 @@ export interface NodeConversationTurn {
106
106
  readonly model: string | null;
107
107
  readonly effort: string | null;
108
108
  readonly access: string | null;
109
+ readonly rewind?: {
110
+ readonly before: string | null;
111
+ readonly after: string;
112
+ };
109
113
  readonly items: readonly NodeConversationItem[];
110
114
  readonly startedAt: number | null;
111
115
  readonly completedAt: number | null;
package/dist/database.js CHANGED
@@ -4,8 +4,9 @@ import { dirname } from 'node:path';
4
4
  import { DatabaseSync } from 'node:sqlite';
5
5
  import { runnerDefaultConfigurationSchema } from '@myagentroam/protocol';
6
6
  import { assertNodeSchema, NODE_SCHEMA_V1 } from './migrations/v001.js';
7
- import { assertNodeSchemaV2, migrateNodeSchemaV2, NODE_SCHEMA_VERSION_V2 } from './migrations/v002.js';
8
- const NODE_SCHEMA_VERSION = NODE_SCHEMA_VERSION_V2;
7
+ import { assertNodeSchemaV2, migrateNodeSchemaV2 } from './migrations/v002.js';
8
+ import { assertNodeSchemaV3, migrateNodeSchemaV3, NODE_SCHEMA_VERSION_V3 } from './migrations/v003.js';
9
+ const NODE_SCHEMA_VERSION = NODE_SCHEMA_VERSION_V3;
9
10
  export class NodeDatabase {
10
11
  now;
11
12
  activeWorkspaceLeaseCount() {
@@ -610,7 +611,7 @@ export class NodeDatabase {
610
611
  ORDER BY rowid DESC LIMIT ?`)
611
612
  .all(input.sessionId, input.nativeOnly === true ? 1 : 0, cursor?.value ?? null, cursor?.value ?? 0, limit + 1);
612
613
  const page = rows.slice(0, limit);
613
- const turns = page.map((row) => this.readConversationTurn(row));
614
+ const turns = page.map((row) => this.readConversationTurn(row)).reverse();
614
615
  const last = page.at(-1);
615
616
  return {
616
617
  turns,
@@ -1025,6 +1026,8 @@ function migrate(database) {
1025
1026
  .prepare('INSERT INTO node_schema_migrations (version, applied_at) VALUES (?, ?), (?, ?)')
1026
1027
  .run(1, Date.now(), 2, Date.now());
1027
1028
  database.exec('COMMIT');
1029
+ migrateNodeSchemaV3(database);
1030
+ assertNodeSchemaV3(database);
1028
1031
  return;
1029
1032
  }
1030
1033
  catch (error) {
@@ -1061,6 +1064,8 @@ function migrate(database) {
1061
1064
  }
1062
1065
  migrateNodeSchemaV2(database);
1063
1066
  assertNodeSchemaV2(database);
1067
+ migrateNodeSchemaV3(database);
1068
+ assertNodeSchemaV3(database);
1064
1069
  return;
1065
1070
  }
1066
1071
  if (current === 0) {
@@ -1082,12 +1087,12 @@ function migrate(database) {
1082
1087
  .get();
1083
1088
  if (numberValue(afterBaseline, 'version') === 1)
1084
1089
  migrateNodeSchemaV2(database);
1085
- if (NODE_SCHEMA_VERSION === 2) {
1086
- assertNodeSchemaV2(database);
1087
- }
1088
- else {
1089
- assertNodeSchema(database);
1090
- }
1090
+ const afterV2 = database
1091
+ .prepare('SELECT MAX(version) AS version FROM node_schema_migrations')
1092
+ .get();
1093
+ if (numberValue(afterV2, 'version') === 2)
1094
+ migrateNodeSchemaV3(database);
1095
+ assertNodeSchemaV3(database);
1091
1096
  }
1092
1097
  function sessionMetadata(row) {
1093
1098
  const raw = JSON.parse(stringValue(row, 'metadata'));
@@ -1130,6 +1135,7 @@ function initializeEphemeralRuntimeTables(database) {
1130
1135
  if (match === null || !runtimeTables.has(match[1] ?? ''))
1131
1136
  continue;
1132
1137
  database.exec(statement
1138
+ .replaceAll("runner IN ('codex', 'claude-code')", "runner IN ('codex', 'claude-code', 'opencode')")
1133
1139
  .replace(/^CREATE TABLE IF NOT EXISTS/, 'CREATE TEMP TABLE IF NOT EXISTS')
1134
1140
  .replace('CREATE TEMP TABLE IF NOT EXISTS agent_sessions', 'CREATE TEMP TABLE IF NOT EXISTS legacy_agent_sessions')
1135
1141
  .replaceAll('REFERENCES agent_sessions', 'REFERENCES legacy_agent_sessions')
@@ -1389,4 +1395,3 @@ function transitionAllowed(current, next) {
1389
1395
  (current === 'CANCELLING' &&
1390
1396
  (next === 'CANCELLED' || next === 'FAILED' || next === 'INTERRUPTED')));
1391
1397
  }
1392
- //# sourceMappingURL=database.js.map
@@ -26,4 +26,3 @@ export class ReliableWorkbenchEventBuffer {
26
26
  return this.pending.size;
27
27
  }
28
28
  }
29
- //# sourceMappingURL=event-buffer.js.map
@@ -127,4 +127,3 @@ export class FakeRunner {
127
127
  resolve(undefined);
128
128
  }
129
129
  }
130
- //# sourceMappingURL=fake-runner.js.map
package/dist/health.js CHANGED
@@ -1,4 +1,3 @@
1
1
  export function getNodeHealth() {
2
2
  return { status: 'ok', platform: process.platform };
3
3
  }
4
- //# sourceMappingURL=health.js.map
package/dist/main.js CHANGED
@@ -4,6 +4,7 @@ import { NodeConnector } from './connector.js';
4
4
  import { loadNodeConfig, nodeConfigPath, nodeDatabasePath } from './config.js';
5
5
  import { getNodeHealth } from './health.js';
6
6
  import { removeServiceDefinition, writeServiceDefinition } from './service.js';
7
+ import { superviseNode } from './supervisor.js';
7
8
  const [command = 'status', ...args] = process.argv.slice(2);
8
9
  const config = readOption(args, '--config') ?? nodeConfigPath();
9
10
  const output = readOption(args, '--output') ?? defaultServicePath();
@@ -14,6 +15,11 @@ if (command === 'run') {
14
15
  process.once(signal, () => connector.stop());
15
16
  }
16
17
  }
18
+ else if (command === 'supervise') {
19
+ if (process.argv[1] === undefined)
20
+ throw new Error('NODE_ENTRYPOINT_UNAVAILABLE');
21
+ await superviseNode(config, process.argv[1]);
22
+ }
17
23
  else if (command === 'install') {
18
24
  await writeCurrentServiceDefinition(output, config);
19
25
  process.stdout.write(`${output}\n`);
@@ -44,4 +50,3 @@ async function writeCurrentServiceDefinition(output, configPath) {
44
50
  const nodeConfig = await loadNodeConfig(configPath);
45
51
  await writeServiceDefinition(output, process.execPath, configPath, process.platform, process.argv[1], [dirname(nodeDatabasePath(nodeConfig, configPath)), ...nodeConfig.allowedRoots]);
46
52
  }
47
- //# sourceMappingURL=main.js.map
@@ -246,4 +246,3 @@ export function assertNodeSchema(database) {
246
246
  throw new Error('NODE_DATABASE_BASELINE_MISMATCH:approvals');
247
247
  }
248
248
  }
249
- //# sourceMappingURL=v001.js.map
@@ -141,4 +141,3 @@ function integer(row, key) {
141
141
  function nullableInteger(row, key) {
142
142
  return row[key] === null || row[key] === undefined ? null : integer(row, key);
143
143
  }
144
- //# sourceMappingURL=v002.js.map
@@ -0,0 +1,4 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ export declare const NODE_SCHEMA_VERSION_V3 = 3;
3
+ export declare function migrateNodeSchemaV3(database: DatabaseSync): void;
4
+ export declare function assertNodeSchemaV3(database: DatabaseSync): void;
@@ -0,0 +1,44 @@
1
+ export const NODE_SCHEMA_VERSION_V3 = 3;
2
+ export function migrateNodeSchemaV3(database) {
3
+ database.exec('PRAGMA foreign_keys = OFF');
4
+ database.exec('BEGIN IMMEDIATE');
5
+ try {
6
+ database.exec(`ALTER TABLE agent_sessions RENAME TO agent_sessions_v2;
7
+ CREATE TABLE agent_sessions (
8
+ id TEXT PRIMARY KEY,
9
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
10
+ runner TEXT NOT NULL CHECK (runner IN ('codex', 'claude-code', 'opencode')),
11
+ pin_order INTEGER CHECK (pin_order IS NULL OR pin_order >= 0),
12
+ metadata TEXT NOT NULL CHECK (json_valid(metadata))
13
+ ) STRICT;
14
+ INSERT INTO agent_sessions SELECT * FROM agent_sessions_v2;
15
+ DROP TABLE agent_sessions_v2;
16
+ ALTER TABLE runner_defaults RENAME TO runner_defaults_v2;
17
+ CREATE TABLE runner_defaults (
18
+ runner TEXT PRIMARY KEY CHECK (runner IN ('codex', 'claude-code', 'opencode')),
19
+ model TEXT NOT NULL, effort TEXT NOT NULL, access TEXT NOT NULL
20
+ ) STRICT;
21
+ INSERT INTO runner_defaults SELECT * FROM runner_defaults_v2;
22
+ DROP TABLE runner_defaults_v2;`);
23
+ database
24
+ .prepare('INSERT INTO node_schema_migrations (version, applied_at) VALUES (?, ?)')
25
+ .run(NODE_SCHEMA_VERSION_V3, Date.now());
26
+ database.exec('COMMIT');
27
+ }
28
+ catch (error) {
29
+ database.exec('ROLLBACK');
30
+ throw error;
31
+ }
32
+ finally {
33
+ database.exec('PRAGMA foreign_keys = ON');
34
+ }
35
+ }
36
+ export function assertNodeSchemaV3(database) {
37
+ for (const table of ['agent_sessions', 'runner_defaults']) {
38
+ const row = database
39
+ .prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?")
40
+ .get(table);
41
+ if (typeof row?.sql !== 'string' || !row.sql.includes("'opencode'"))
42
+ throw new Error(`NODE_DATABASE_SCHEMA_INVALID:${table}:runner`);
43
+ }
44
+ }
@@ -0,0 +1,14 @@
1
+ export interface JsonlWindow {
2
+ readonly content: string;
3
+ readonly truncated: boolean;
4
+ readonly size: number;
5
+ }
6
+ /** Reads bounded head/tail windows without ever loading an unbounded transcript. */
7
+ export declare function readJsonlWindow(path: string, options: {
8
+ readonly headBytes?: number;
9
+ readonly tailBytes?: number;
10
+ }): Promise<JsonlWindow | undefined>;
11
+ /** Parses records one at a time, avoiding the allocation of a full line array. */
12
+ export declare function forEachJsonlRecord(content: string, visit: (record: Record<string, unknown>) => void): void;
13
+ export declare function listJsonlFiles(directory: string): Promise<readonly string[]>;
14
+ export declare function mapConcurrent<T, R>(values: readonly T[], concurrency: number, map: (value: T, index: number) => Promise<R>): Promise<readonly R[]>;
@@ -0,0 +1,97 @@
1
+ import { open, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ /** Reads bounded head/tail windows without ever loading an unbounded transcript. */
4
+ export async function readJsonlWindow(path, options) {
5
+ try {
6
+ const handle = await open(path, 'r');
7
+ try {
8
+ const metadata = await handle.stat();
9
+ const headSize = Math.min(options.headBytes ?? 0, metadata.size);
10
+ const remaining = metadata.size - headSize;
11
+ const tailSize = Math.min(options.tailBytes ?? 0, remaining);
12
+ const head = await readAt(handle, headSize, 0);
13
+ const tailOffset = metadata.size - tailSize;
14
+ const tail = await readAt(handle, tailSize, tailOffset);
15
+ const contiguous = headSize > 0 && tailSize > 0 && tailOffset === headSize;
16
+ return {
17
+ content: contiguous
18
+ ? Buffer.concat([head, tail]).toString('utf8')
19
+ : `${head.toString('utf8')}${headSize > 0 && tailSize > 0 ? '\n' : ''}${tail.toString('utf8')}`,
20
+ truncated: headSize + tailSize < metadata.size,
21
+ size: metadata.size
22
+ };
23
+ }
24
+ finally {
25
+ await handle.close();
26
+ }
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ /** Parses records one at a time, avoiding the allocation of a full line array. */
33
+ export function forEachJsonlRecord(content, visit) {
34
+ let start = 0;
35
+ while (start < content.length) {
36
+ const newline = content.indexOf('\n', start);
37
+ const end = newline < 0 ? content.length : newline;
38
+ if (end > start) {
39
+ try {
40
+ const value = JSON.parse(content.slice(start, end));
41
+ if (isRecord(value))
42
+ visit(value);
43
+ }
44
+ catch {
45
+ // A partial or damaged record does not hide subsequent valid records.
46
+ }
47
+ }
48
+ if (newline < 0)
49
+ return;
50
+ start = newline + 1;
51
+ }
52
+ }
53
+ export async function listJsonlFiles(directory) {
54
+ const files = [];
55
+ const walk = async (current) => {
56
+ let entries;
57
+ try {
58
+ entries = await readdir(current, { withFileTypes: true });
59
+ }
60
+ catch {
61
+ return;
62
+ }
63
+ for (const entry of entries) {
64
+ const path = join(current, entry.name);
65
+ if (entry.isDirectory())
66
+ await walk(path);
67
+ else if (entry.isFile() && entry.name.endsWith('.jsonl'))
68
+ files.push(path);
69
+ }
70
+ };
71
+ await walk(directory);
72
+ return files;
73
+ }
74
+ export async function mapConcurrent(values, concurrency, map) {
75
+ const results = Array(values.length);
76
+ let next = 0;
77
+ const worker = async () => {
78
+ while (true) {
79
+ const index = next++;
80
+ if (index >= values.length)
81
+ return;
82
+ results[index] = await map(values[index], index);
83
+ }
84
+ };
85
+ await Promise.all(Array.from({ length: Math.min(Math.max(1, concurrency), values.length) }, worker));
86
+ return results;
87
+ }
88
+ async function readAt(handle, size, position) {
89
+ if (size === 0)
90
+ return Buffer.alloc(0);
91
+ const buffer = Buffer.allocUnsafe(size);
92
+ const { bytesRead } = await handle.read(buffer, 0, size, position);
93
+ return buffer.subarray(0, bytesRead);
94
+ }
95
+ function isRecord(value) {
96
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
97
+ }
@@ -4,6 +4,7 @@ export interface NativeTranscriptMessage {
4
4
  readonly role: 'USER' | 'ASSISTANT';
5
5
  readonly text: string;
6
6
  readonly createdAt: number | null;
7
+ readonly nativeId?: string;
7
8
  }
8
9
  export interface NativeTranscriptToolCall {
9
10
  readonly kind: 'tool_call';
@@ -14,10 +15,35 @@ export interface NativeTranscriptToolCall {
14
15
  readonly inputTruncated: boolean;
15
16
  readonly outputSummary: string | null;
16
17
  readonly outputTruncated: boolean;
18
+ /** Metadata only: native transcript image bytes are never retained here. */
19
+ readonly imageAttachments?: readonly NativeTranscriptImageAttachment[];
17
20
  readonly failed: boolean;
18
21
  readonly createdAt: number | null;
22
+ readonly nativeId?: string;
19
23
  }
20
- export type NativeTranscriptItem = NativeTranscriptMessage | NativeTranscriptToolCall;
24
+ export interface NativeTranscriptUnknown {
25
+ readonly kind: 'unknown';
26
+ readonly sourceEventType: string;
27
+ readonly label: string;
28
+ readonly createdAt: number | null;
29
+ readonly nativeId?: string;
30
+ }
31
+ export interface NativeTranscriptReasoningSummary {
32
+ readonly kind: 'reasoning_summary';
33
+ readonly sections: readonly {
34
+ readonly index: number;
35
+ readonly text: string;
36
+ }[];
37
+ readonly createdAt: number | null;
38
+ readonly nativeId?: string;
39
+ }
40
+ export interface NativeTranscriptImageAttachment {
41
+ readonly name: string;
42
+ readonly mime: string;
43
+ /** Codex-owned local artifact path; never sent to Web. */
44
+ readonly localPath?: string;
45
+ }
46
+ export type NativeTranscriptItem = NativeTranscriptMessage | NativeTranscriptToolCall | NativeTranscriptReasoningSummary | NativeTranscriptUnknown;
21
47
  export interface NativeSessionHistory {
22
48
  readonly runner: RunnerName;
23
49
  readonly externalSessionId: string;
@@ -39,9 +65,11 @@ export interface NativeCodexContextUsage {
39
65
  readonly usedTokens: number;
40
66
  readonly maxTokens: number;
41
67
  }
42
- export type NativeCodexTaskActivity = 'ACTIVE' | 'IDLE' | 'STALE';
43
- /** Reads the latest durable Codex task lifecycle event without exposing it. */
44
- export declare function readCodexNativeTaskActivity(workspacePath: string, externalSessionId: string, now?: number): Promise<NativeCodexTaskActivity | undefined>;
68
+ export interface NativeClaudeContextUsage {
69
+ readonly model: string;
70
+ readonly usedTokens: number;
71
+ readonly maxTokens: number;
72
+ }
45
73
  /**
46
74
  * Read-only adapter for the supported vendor JSONL stores. It requires an
47
75
  * explicit cwd on the record and returns normalized visible messages and tool
@@ -63,6 +91,15 @@ export declare function readNativeSession(runner: RunnerName, workspacePath: str
63
91
  * full-history scan.
64
92
  */
65
93
  export declare function readCodexNativeContextUsage(workspacePath: string, externalSessionId: string): Promise<NativeCodexContextUsage | undefined>;
94
+ /** Reads the latest actual Claude prompt occupancy without starting a CLI/SDK control stream. */
95
+ export declare function readClaudeNativeContextUsage(workspacePath: string, externalSessionId: string, options?: {
96
+ readonly environment?: NodeJS.ProcessEnv;
97
+ readonly settingsPath?: string;
98
+ }): Promise<NativeClaudeContextUsage | undefined>;
99
+ export declare function claudeContextWindowTokens(options?: {
100
+ readonly environment?: NodeJS.ProcessEnv;
101
+ readonly settingsPath?: string;
102
+ }): Promise<number>;
66
103
  /**
67
104
  * Permanently removes exactly one previously discovered transcript. The
68
105
  * caller supplies no path: the in-process discovery index resolves it, then