@myagentroam/node 0.11.36 → 0.11.38

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 (145) hide show
  1. package/dist/application/execution-lifecycle.js +54 -0
  2. package/dist/application/node-lifecycle.js +104 -0
  3. package/dist/application/node-operation-admission.js +51 -0
  4. package/dist/application/node-operation-contracts.js +1 -0
  5. package/dist/application/runner-composition.js +22 -0
  6. package/dist/application/session-projection-composition.js +26 -0
  7. package/dist/application/workspace-application.js +61 -0
  8. package/dist/application/workspace-composition.js +21 -0
  9. package/dist/capabilities.js +2 -3
  10. package/dist/codex-app-server.js +1 -471
  11. package/dist/connection/authorization.js +26 -0
  12. package/dist/{service → connection}/direct-transfer-service.js +1 -1
  13. package/dist/connection/endpoints.js +32 -0
  14. package/dist/connector.js +145 -253
  15. package/dist/control-outbound-scheduler.js +3 -0
  16. package/dist/database.js +34 -14
  17. package/dist/git-askpass.js +1 -1
  18. package/dist/git-execution-helper.js +1 -1
  19. package/dist/hosting/hosted-conversation-file-identity.js +27 -0
  20. package/dist/hosting/hosted-conversation-file-operations.js +44 -0
  21. package/dist/hosting/hosted-conversation-node-service.js +974 -0
  22. package/dist/hosting/hosted-conversation-runs.js +599 -0
  23. package/dist/hosting/hosted-conversation-templates.js +280 -0
  24. package/dist/hosting/hosted-conversation-uploads.js +344 -0
  25. package/dist/hosting/hosted-conversation-watch.js +194 -0
  26. package/dist/hosting/runtime-contracts.js +1 -0
  27. package/dist/runner/abstract-runner.js +4 -13
  28. package/dist/{claude-agent-sdk.js → runner/claude/claude-agent-sdk.js} +2 -2
  29. package/dist/runner/claude/conversation-parser.js +2 -1
  30. package/dist/runner/claude/managed-run-controller.js +6 -7
  31. package/dist/runner/claude/message-parsers.js +2 -1
  32. package/dist/runner/claude/native-transcript.js +2 -1
  33. package/dist/runner/claude/tool-normalizer.js +1 -1
  34. package/dist/runner/claude-code-runner.js +2 -2
  35. package/dist/runner/codex/codex-app-server.js +471 -0
  36. package/dist/runner/codex/conversation-parser.js +4 -2
  37. package/dist/runner/codex/managed-run-controller.js +7 -9
  38. package/dist/runner/codex/native-transcript.js +1 -1
  39. package/dist/runner/codex/tool-normalizer.js +1 -1
  40. package/dist/runner/codex-runner.js +9 -27
  41. package/dist/runner/contracts.js +1 -0
  42. package/dist/runner/mar-agent/conversation-normalizer.js +2 -2
  43. package/dist/runner/mar-agent/history-projection.js +1 -1
  44. package/dist/runner/mar-agent/hosted-conversation-runtime.js +6 -10
  45. package/dist/runner/mar-agent/managed-run-controller.js +85 -61
  46. package/dist/runner/mar-agent/runtime-model-factory.js +0 -3
  47. package/dist/runner/mar-agent-runner.js +1 -1
  48. package/dist/runner/normalization.js +42 -0
  49. package/dist/runner/opencode/conversation-parser.js +1 -1
  50. package/dist/runner/opencode/managed-run-controller.js +6 -7
  51. package/dist/{opencode-server.js → runner/opencode/opencode-server.js} +2 -2
  52. package/dist/runner/opencode/tool-normalizer.js +1 -1
  53. package/dist/runner/opencode-runner.js +4 -5
  54. package/dist/runner/runner-registry.js +4 -1
  55. package/dist/runtime-state.js +48 -372
  56. package/dist/service/computer-use-enablement-service.js +1 -1
  57. package/dist/service/continuous-run-coordinator.js +52 -7
  58. package/dist/service/conversation-history-service.js +4 -4
  59. package/dist/service/conversation-hosting-capability-service.js +1 -1
  60. package/dist/service/conversation-hosting-enablement-service.js +1 -1
  61. package/dist/service/conversation-hosting-lifecycle-service.js +3 -3
  62. package/dist/service/conversation-hosting-maintenance-service.js +1 -1
  63. package/dist/service/conversation-observation.js +1 -1
  64. package/dist/service/conversation-segment-service.js +1 -1
  65. package/dist/service/external-session-resume-service.js +47 -27
  66. package/dist/service/fake-managed-run-service.js +3 -7
  67. package/dist/service/git-operation-service.js +1 -1
  68. package/dist/service/goal-continuation-coordinator.js +14 -7
  69. package/dist/service/mar-agent-model-transport-test-service.js +1 -1
  70. package/dist/service/native-session-watch-service.js +1 -1
  71. package/dist/service/node-connection-lifecycle-service.js +6 -3
  72. package/dist/service/node-control-channel.js +48 -11
  73. package/dist/service/node-request-service.js +1 -1
  74. package/dist/service/node-terminal-channel.js +2 -1
  75. package/dist/service/node-workspace-coordinator.js +3 -2
  76. package/dist/service/notification-mcp-bridge.js +6 -2
  77. package/dist/service/recent-run-projection-service.js +1 -1
  78. package/dist/service/restart-session-restorer.js +1 -1
  79. package/dist/service/run-event-service.js +49 -42
  80. package/dist/service/run-workbench-service.js +3 -3
  81. package/dist/service/runner-interaction-service.js +6 -5
  82. package/dist/service/runner-service.js +1 -1
  83. package/dist/service/session-recent-activity.js +2 -1
  84. package/dist/service/skill-directory-service.js +1 -1
  85. package/dist/service/skill-install-service.js +2 -2
  86. package/dist/service/terminal-service.js +1 -1
  87. package/dist/service/workbench-event-projection.js +3 -2
  88. package/dist/service/workbench-event-service.js +1 -1
  89. package/dist/service/workspace-service.js +3 -2
  90. package/dist/session/attachments.js +110 -0
  91. package/dist/session/contracts.js +1 -0
  92. package/dist/{util → session}/conversation-identity.js +1 -1
  93. package/dist/session/input-validation.js +15 -0
  94. package/dist/{service → session}/native-session-projection-service.js +40 -11
  95. package/dist/session/pagination.js +77 -0
  96. package/dist/{util → session}/runner-native-session-parsers.js +2 -1
  97. package/dist/session/runtime/approvals.js +90 -0
  98. package/dist/session/runtime/conversation-projection.js +310 -0
  99. package/dist/session/runtime/type-guards.js +19 -0
  100. package/dist/{service → session}/session-catalog-service.js +5 -4
  101. package/dist/{service → session}/session-command-service.js +3 -3
  102. package/dist/{service → session}/session-identity-service.js +1 -1
  103. package/dist/{service → session}/session-lifecycle-service.js +18 -7
  104. package/dist/{service → session}/session-message-service.js +6 -5
  105. package/dist/{service → session}/session-presentation-service.js +1 -1
  106. package/dist/{service → session}/session-query-service.js +2 -2
  107. package/dist/{service → session}/session-schedule-service.js +2 -2
  108. package/dist/util/records.js +3 -0
  109. package/dist/workspace/contracts.js +1 -0
  110. package/dist/workspace/file-index.js +534 -0
  111. package/dist/workspace/files.js +466 -0
  112. package/dist/workspace/git.js +991 -0
  113. package/dist/workspace/path-access.js +184 -0
  114. package/dist/workspace/utf8.js +12 -0
  115. package/dist/{service → workspace}/workspace-change-service.js +45 -21
  116. package/dist/{service → workspace}/workspace-code-navigation-service.js +6 -6
  117. package/dist/{service → workspace}/workspace-file-manager-service.js +1 -1
  118. package/dist/{service → workspace}/workspace-file-service.js +16 -16
  119. package/dist/{service → workspace}/workspace-git-exclude-service.js +1 -1
  120. package/dist/{service → workspace}/workspace-queue-workbench-service.js +3 -2
  121. package/dist/{service → workspace}/workspace-session-service.js +45 -40
  122. package/dist/{service → workspace}/workspace-upload-service.js +28 -28
  123. package/dist/{service → workspace}/workspace-watch-service.js +29 -28
  124. package/dist/{service → workspace}/workspace-workbench-service.js +4 -3
  125. package/package.json +4 -4
  126. package/dist/service/hosted-conversation-node-service.js +0 -2252
  127. package/dist/service/run-domain-state.js +0 -4
  128. package/dist/service/session-domain-state.js +0 -9
  129. package/dist/service/workspace-domain-state.js +0 -41
  130. package/dist/util/node-operation-parsers.js +0 -320
  131. package/dist/workspace.js +0 -2168
  132. /package/dist/{connector → application}/node-connector-options.js +0 -0
  133. /package/dist/{util → connection}/node-operation-data.js +0 -0
  134. /package/dist/{connector → connection}/node-operation-router.js +0 -0
  135. /package/dist/{service → hosting}/hosted-conversation-sandbox-profile.js +0 -0
  136. /package/dist/{service → hosting}/hosted-conversation-store.js +0 -0
  137. /package/dist/{util → mcp}/secret-environment.js +0 -0
  138. /package/dist/{claude-channel.js → runner/claude/claude-channel.js} +0 -0
  139. /package/dist/{util → runner}/personal-instructions.js +0 -0
  140. /package/dist/{util → runner}/runner-error.js +0 -0
  141. /package/dist/{service/conversation-work-process.js → session/runtime/work-process.js} +0 -0
  142. /package/dist/{service → session}/session-context-service.js +0 -0
  143. /package/dist/{util → workspace}/git-credential-client.js +0 -0
  144. /package/dist/{service → workspace}/workspace-content-search-service.js +0 -0
  145. /package/dist/{service → workspace}/workspace-watch-snapshot-service.js +0 -0
@@ -0,0 +1,471 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { minimalRunnerEnvironment, platformCliInvocation, runRunnerProbe } from '../../operational.js';
4
+ const REQUEST_TIMEOUT_MS = 15_000;
5
+ /**
6
+ * Maps the facts that the Node can establish to the unified session controls.
7
+ * Native-session activation is validated atomically with the first message.
8
+ */
9
+ export function codexSessionControl(input) {
10
+ void [input.available, input.nativeThreadId, input.nativeControl];
11
+ if (input.activeTurn) {
12
+ return {
13
+ level: 'MANAGED',
14
+ capabilities: ['history.read', 'events.live', 'turn.steer', 'turn.interrupt']
15
+ };
16
+ }
17
+ return {
18
+ level: 'MANAGED',
19
+ capabilities: ['history.read', 'turn.start']
20
+ };
21
+ }
22
+ export class CodexAppServerRpcError extends Error {
23
+ code;
24
+ rpcMessage;
25
+ data;
26
+ constructor(payload) {
27
+ const rpcMessage = typeof payload.message === 'string' ? payload.message : 'Codex App Server request failed';
28
+ super(`CODEX_APP_SERVER_RPC_ERROR:${JSON.stringify(payload)}`);
29
+ this.name = 'CodexAppServerRpcError';
30
+ this.code = payload.code;
31
+ this.rpcMessage = rpcMessage;
32
+ this.data = payload.data;
33
+ }
34
+ }
35
+ export function isCodexRolloutMissingError(error) {
36
+ return (error instanceof CodexAppServerRpcError &&
37
+ error.code === -32600 &&
38
+ (/^no rollout found for thread id\b/i.test(error.rpcMessage) ||
39
+ /^thread not loaded:\s*/i.test(error.rpcMessage)));
40
+ }
41
+ /**
42
+ * Local JSON-RPC client for a Node-supervised Codex App Server over stdio.
43
+ * The transport is not exposed to MAR Server or the browser.
44
+ */
45
+ export class CodexAppServerClient {
46
+ options;
47
+ child;
48
+ transport;
49
+ nextId = 1;
50
+ pending = new Map();
51
+ pendingDrainWaiters = new Set();
52
+ notifications = new Set();
53
+ environment = {};
54
+ starting;
55
+ restarting;
56
+ lifecycleRevision = 0;
57
+ constructor(options = {}) {
58
+ this.options = options;
59
+ }
60
+ configureEnvironment(environment) {
61
+ if (this.child !== undefined)
62
+ throw new Error('CODEX_APP_SERVER_ALREADY_STARTED');
63
+ this.environment = { ...environment };
64
+ }
65
+ clearEnvironment() {
66
+ if (this.child !== undefined)
67
+ throw new Error('CODEX_APP_SERVER_ALREADY_STARTED');
68
+ this.environment = {};
69
+ }
70
+ start() {
71
+ if (this.restarting !== undefined)
72
+ return this.restarting;
73
+ if (this.starting !== undefined)
74
+ return this.starting;
75
+ if (this.child !== undefined)
76
+ return Promise.resolve();
77
+ const starting = this.startManaged();
78
+ this.starting = starting;
79
+ const clearStarting = () => {
80
+ if (this.starting === starting)
81
+ this.starting = undefined;
82
+ };
83
+ void starting.then(clearStarting, clearStarting);
84
+ return starting;
85
+ }
86
+ restart() {
87
+ if (this.restarting !== undefined)
88
+ return this.restarting;
89
+ const restarting = this.restartManaged(this.lifecycleRevision);
90
+ this.restarting = restarting;
91
+ const clearRestarting = () => {
92
+ if (this.restarting === restarting)
93
+ this.restarting = undefined;
94
+ };
95
+ void restarting.then(clearRestarting, clearRestarting);
96
+ return restarting;
97
+ }
98
+ async startManaged() {
99
+ const command = this.options.command ?? 'codex';
100
+ const args = this.options.args ?? ['app-server'];
101
+ const spawnProcess = this.options.spawnProcess ?? defaultSpawn;
102
+ const child = spawnProcess(command, args, {
103
+ ...(this.options.cwd === undefined ? {} : { cwd: this.options.cwd }),
104
+ env: { ...minimalRunnerEnvironment(), ...this.environment }
105
+ });
106
+ this.child = child;
107
+ this.transport = stdioTransport(child);
108
+ const lines = createInterface({ input: child.stdout });
109
+ lines.on('line', (line) => this.handleLine(line));
110
+ const managedChild = child;
111
+ child.once('error', (error) => {
112
+ if (this.child !== managedChild)
113
+ return;
114
+ this.failAll(new Error(`CODEX_APP_SERVER_START_FAILED:${error.message}`));
115
+ });
116
+ child.once('exit', () => {
117
+ if (this.child !== managedChild)
118
+ return;
119
+ this.child = undefined;
120
+ this.failAll(new Error('CODEX_APP_SERVER_EXITED'));
121
+ });
122
+ await this.requestDirect('initialize', {
123
+ clientInfo: { name: 'codex_vscode', title: 'MyAgentRoam', version: '0.1.4' },
124
+ capabilities: { experimentalApi: true }
125
+ });
126
+ this.notify('initialized', {});
127
+ }
128
+ async restartManaged(lifecycleRevision) {
129
+ await Promise.resolve();
130
+ if (this.starting !== undefined)
131
+ await this.starting;
132
+ await this.waitForPendingRequests();
133
+ if (this.lifecycleRevision !== lifecycleRevision)
134
+ throw new Error('CODEX_APP_SERVER_STOPPED');
135
+ this.stopManaged();
136
+ await this.startManaged();
137
+ }
138
+ stop() {
139
+ this.lifecycleRevision += 1;
140
+ this.stopManaged();
141
+ }
142
+ async stopAndWait() {
143
+ const child = this.child;
144
+ if (child === undefined) {
145
+ this.stop();
146
+ return;
147
+ }
148
+ const exited = new Promise((resolve, reject) => {
149
+ if (child.exitCode !== null || child.signalCode !== null)
150
+ resolve();
151
+ else {
152
+ let forcedTimeout;
153
+ const onExit = () => {
154
+ clearTimeout(timeout);
155
+ if (forcedTimeout !== undefined)
156
+ clearTimeout(forcedTimeout);
157
+ resolve();
158
+ };
159
+ const timeout = setTimeout(() => {
160
+ child.kill('SIGKILL');
161
+ forcedTimeout = setTimeout(() => {
162
+ child.off('exit', onExit);
163
+ reject(new Error('CODEX_APP_SERVER_STOP_TIMEOUT'));
164
+ }, 2_000);
165
+ }, 2_000);
166
+ child.once('exit', onExit);
167
+ }
168
+ });
169
+ this.stop();
170
+ await exited;
171
+ }
172
+ stopManaged() {
173
+ this.transport?.destroy();
174
+ this.transport = undefined;
175
+ this.child?.kill();
176
+ this.child = undefined;
177
+ this.failAll(new Error('CODEX_APP_SERVER_STOPPED'));
178
+ }
179
+ onNotification(listener) {
180
+ this.notifications.add(listener);
181
+ return () => this.notifications.delete(listener);
182
+ }
183
+ async request(method, params) {
184
+ if (this.restarting !== undefined)
185
+ await this.restarting;
186
+ else if (this.starting !== undefined)
187
+ await this.starting;
188
+ return this.requestDirect(method, params);
189
+ }
190
+ requestDirect(method, params) {
191
+ const transport = this.transport;
192
+ if (transport === undefined || !transport.writable)
193
+ throw new Error('CODEX_APP_SERVER_UNAVAILABLE');
194
+ const id = this.nextId++;
195
+ const timeoutMs = this.options.timeoutMs ?? REQUEST_TIMEOUT_MS;
196
+ const result = new Promise((resolve, reject) => {
197
+ const timer = setTimeout(() => {
198
+ this.pending.delete(id);
199
+ this.resolvePendingDrain();
200
+ reject(new Error('CODEX_APP_SERVER_TIMEOUT'));
201
+ }, timeoutMs);
202
+ this.pending.set(id, { resolve, reject, timer });
203
+ });
204
+ transport.write(`${JSON.stringify({ method, id, params })}\n`);
205
+ return result;
206
+ }
207
+ waitForPendingRequests() {
208
+ if (this.pending.size === 0)
209
+ return Promise.resolve();
210
+ return new Promise((resolve) => this.pendingDrainWaiters.add(resolve));
211
+ }
212
+ resolvePendingDrain() {
213
+ if (this.pending.size !== 0)
214
+ return;
215
+ for (const resolve of this.pendingDrainWaiters)
216
+ resolve();
217
+ this.pendingDrainWaiters.clear();
218
+ }
219
+ notify(method, params) {
220
+ const transport = this.transport;
221
+ if (transport === undefined || !transport.writable)
222
+ throw new Error('CODEX_APP_SERVER_UNAVAILABLE');
223
+ transport.write(`${JSON.stringify({ method, params })}\n`);
224
+ }
225
+ respond(requestId, result) {
226
+ const transport = this.transport;
227
+ if (transport === undefined || !transport.writable)
228
+ throw new Error('CODEX_APP_SERVER_UNAVAILABLE');
229
+ transport.write(`${JSON.stringify({ id: requestId, result })}\n`);
230
+ }
231
+ startThread(cwd, configuration = {}) {
232
+ return this.request('thread/start', {
233
+ cwd,
234
+ experimentalRawEvents: true,
235
+ ...codexThreadConfiguration(cwd, configuration)
236
+ });
237
+ }
238
+ listThreads(params = {}) {
239
+ return this.request('thread/list', params);
240
+ }
241
+ readThread(threadId) {
242
+ return this.request('thread/read', { threadId, includeTurns: false });
243
+ }
244
+ listCollaborationModes() {
245
+ return this.request('collaborationMode/list', {});
246
+ }
247
+ listModels(params) {
248
+ return this.request('model/list', params);
249
+ }
250
+ forkThread(threadId, cwd, lastTurnId, configuration = {}) {
251
+ return this.request('thread/fork', {
252
+ threadId,
253
+ lastTurnId,
254
+ cwd,
255
+ ...codexThreadConfiguration(cwd, configuration)
256
+ });
257
+ }
258
+ revertThread(threadId, beforeTurnId) {
259
+ return this.request('thread/revert', { threadId, beforeTurnId });
260
+ }
261
+ listThreadTurns(threadId, input) {
262
+ return this.request('thread/turns/list', {
263
+ threadId,
264
+ ...input,
265
+ itemsView: 'full',
266
+ sortDirection: 'desc'
267
+ });
268
+ }
269
+ setThreadName(threadId, name) {
270
+ return this.request('thread/name/set', { threadId, name });
271
+ }
272
+ resumeThread(threadId, cwd, configuration = {}) {
273
+ return this.request('thread/resume', {
274
+ threadId,
275
+ cwd,
276
+ ...codexThreadConfiguration(cwd, configuration)
277
+ });
278
+ }
279
+ startTurn(threadId, text, cwd, configuration = {}, attachments = []) {
280
+ return this.request('turn/start', {
281
+ threadId,
282
+ cwd,
283
+ input: [{ type: 'text', text }, ...attachments],
284
+ ...codexTurnConfiguration(cwd, configuration)
285
+ });
286
+ }
287
+ steerTurn(threadId, expectedTurnId, text) {
288
+ return this.request('turn/steer', {
289
+ threadId,
290
+ expectedTurnId,
291
+ input: [{ type: 'text', text }]
292
+ });
293
+ }
294
+ interruptTurn(threadId, turnId) {
295
+ return this.request('turn/interrupt', { threadId, turnId });
296
+ }
297
+ handleLine(line) {
298
+ let message;
299
+ try {
300
+ message = JSON.parse(line);
301
+ }
302
+ catch {
303
+ return;
304
+ }
305
+ if ((typeof message.id === 'number' || typeof message.id === 'string') &&
306
+ typeof message.method === 'string') {
307
+ for (const listener of this.notifications) {
308
+ listener({ method: message.method, params: message.params, requestId: message.id });
309
+ }
310
+ return;
311
+ }
312
+ if (typeof message.id === 'number') {
313
+ const pending = this.pending.get(message.id);
314
+ if (pending === undefined)
315
+ return;
316
+ clearTimeout(pending.timer);
317
+ this.pending.delete(message.id);
318
+ this.resolvePendingDrain();
319
+ if (message.error !== undefined) {
320
+ pending.reject(new CodexAppServerRpcError(isRecord(message.error) ? message.error : { message: String(message.error) }));
321
+ }
322
+ else {
323
+ pending.resolve(message.result);
324
+ }
325
+ return;
326
+ }
327
+ if (typeof message.method === 'string') {
328
+ for (const listener of this.notifications)
329
+ listener({ method: message.method, params: message.params });
330
+ }
331
+ }
332
+ failAll(error) {
333
+ for (const [id, pending] of this.pending) {
334
+ clearTimeout(pending.timer);
335
+ pending.reject(error);
336
+ this.pending.delete(id);
337
+ }
338
+ this.resolvePendingDrain();
339
+ }
340
+ }
341
+ function codexThreadConfiguration(cwd, configuration) {
342
+ return {
343
+ ...modelOption(configuration.model),
344
+ ...(configuration.developerInstructions === undefined
345
+ ? {}
346
+ : { developerInstructions: configuration.developerInstructions }),
347
+ ...(configuration.mcpServers === undefined
348
+ ? {}
349
+ : {
350
+ config: {
351
+ mcp_servers: configuration.mcpServers
352
+ }
353
+ }),
354
+ ...codexPermissionConfiguration(cwd, configuration.access, 'thread')
355
+ };
356
+ }
357
+ function codexTurnConfiguration(cwd, configuration) {
358
+ const effort = typeof configuration.effort === 'string' && configuration.effort.trim().length > 0
359
+ ? configuration.effort
360
+ : null;
361
+ return {
362
+ ...modelOption(configuration.model),
363
+ ...(effort === null ? {} : { effort }),
364
+ ...(configuration.serviceTier === 'fast' ? { serviceTier: 'fast' } : {}),
365
+ ...(configuration.collaborationMode === undefined
366
+ ? {}
367
+ : {
368
+ collaborationMode: {
369
+ mode: configuration.collaborationMode,
370
+ settings: {
371
+ model: configuration.model ?? 'gpt-5.6-sol',
372
+ reasoningEffort: effort
373
+ }
374
+ }
375
+ }),
376
+ ...codexPermissionConfiguration(cwd, configuration.access, 'turn')
377
+ };
378
+ }
379
+ function modelOption(model) {
380
+ return model === undefined || model === null ? {} : { model };
381
+ }
382
+ function codexPermissionConfiguration(cwd, access, phase) {
383
+ const sandbox = access === 'read-only'
384
+ ? 'read-only'
385
+ : access === 'workspace-write' || access === 'on-request'
386
+ ? 'workspace-write'
387
+ : access === 'full-access'
388
+ ? 'danger-full-access'
389
+ : undefined;
390
+ const approvalPolicy = access === 'workspace-write' || access === 'full-access' ? 'never' : 'on-request';
391
+ if (phase === 'thread')
392
+ return {
393
+ ...(sandbox === undefined ? {} : { sandbox }),
394
+ approvalPolicy
395
+ };
396
+ return {
397
+ ...(sandbox === undefined
398
+ ? {}
399
+ : {
400
+ sandboxPolicy: sandbox === 'read-only'
401
+ ? { type: 'readOnly' }
402
+ : sandbox === 'danger-full-access'
403
+ ? { type: 'dangerFullAccess' }
404
+ : { type: 'workspaceWrite', writableRoots: [cwd], networkAccess: true }
405
+ }),
406
+ approvalPolicy
407
+ };
408
+ }
409
+ export function probeCodex(command = 'codex') {
410
+ const invocation = platformCliInvocation(command, ['--version']);
411
+ const result = spawnSync(invocation.command, invocation.args, {
412
+ windowsHide: true,
413
+ env: minimalRunnerEnvironment(),
414
+ timeout: 3_000,
415
+ encoding: 'utf8'
416
+ });
417
+ if (result.status !== 0 || result.error !== undefined)
418
+ return { available: false, compatible: false };
419
+ const version = `${result.stdout}${result.stderr}`.trim().slice(0, 128);
420
+ return {
421
+ available: true,
422
+ ...(version.length === 0 ? {} : { version }),
423
+ // The CLI version is metadata only. Actual compatibility is established
424
+ // by the App Server initialize handshake when the client starts.
425
+ compatible: true
426
+ };
427
+ }
428
+ export async function probeCodexAsync(command = 'codex') {
429
+ const invocation = platformCliInvocation(command, ['--version']);
430
+ const result = await runRunnerProbe(invocation.command, invocation.args, {
431
+ env: minimalRunnerEnvironment(),
432
+ timeoutMs: 3_000
433
+ });
434
+ if (result.status !== 0)
435
+ return { available: false, compatible: false };
436
+ const version = `${result.stdout}${result.stderr}`.trim().slice(0, 128);
437
+ return {
438
+ available: true,
439
+ ...(version.length === 0 ? {} : { version }),
440
+ // Do not reject a newer CLI before its App Server has had a chance to
441
+ // negotiate the protocol it actually supports.
442
+ compatible: true
443
+ };
444
+ }
445
+ export function isSupportedCodexVersion(version) {
446
+ return version.trim().length > 0;
447
+ }
448
+ function defaultSpawn(command, args, options) {
449
+ const invocation = platformCliInvocation(command, args);
450
+ return spawn(invocation.command, invocation.args, {
451
+ stdio: ['pipe', 'pipe', 'pipe'],
452
+ shell: false,
453
+ windowsHide: true,
454
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
455
+ ...(options.env === undefined ? {} : { env: options.env })
456
+ });
457
+ }
458
+ function isRecord(value) {
459
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
460
+ }
461
+ function stdioTransport(child) {
462
+ return {
463
+ get writable() {
464
+ return child.stdin.writable;
465
+ },
466
+ write: (data) => child.stdin.write(data),
467
+ on: (event, listener) => child.stdout.on(event, listener),
468
+ once: (event, listener) => child.stdout.once(event, listener),
469
+ destroy: () => child.stdout.destroy()
470
+ };
471
+ }
@@ -127,7 +127,7 @@ export class CodexUsageNormalizer {
127
127
  }
128
128
  }
129
129
  import { basename, extname } from 'node:path';
130
- import { runnerErrorProjection } from '../../util/runner-error.js';
130
+ import { runnerErrorProjection } from '../runner-error.js';
131
131
  import { conversationImageAttachmentSchema } from '@myagentroam/protocol';
132
132
  import { parseCodexComposerImageMarkers } from './composer-image-markers.js';
133
133
  import { normalizeCodexTool } from './tool-normalizer.js';
@@ -1334,7 +1334,9 @@ export function isCodexImageBlock(value) {
1334
1334
  return false;
1335
1335
  return value.type === 'image' || value.type === 'input_image' || value.type === 'output_image';
1336
1336
  }
1337
- import { compactRunnerText, compactRunnerValue, boundedConversationItemId, isCanonicalBase64, isPlainRecord, nativePageEnd } from '../../util/node-operation-parsers.js';
1337
+ import { compactRunnerText, compactRunnerValue, boundedConversationItemId, nativePageEnd } from '../normalization.js';
1338
+ import { isPlainRecord } from '../../util/records.js';
1339
+ import { isCanonicalBase64 } from '../../session/attachments.js';
1338
1340
  export function extractId(result, key) {
1339
1341
  if (typeof result === 'object' &&
1340
1342
  result !== null &&
@@ -2,12 +2,13 @@ import { NODE_MCP } from '../../mcp/naming.js';
2
2
  import { nodeExecutionEnvironment } from '../../node-execution-environment.js';
3
3
  import { extractId } from './conversation-parser.js';
4
4
  import { normalizeCodexApproval } from './tool-normalizer.js';
5
- import { parseSecretEnvironment } from '../../util/secret-environment.js';
5
+ import { parseSecretEnvironment } from '../../mcp/secret-environment.js';
6
6
  import { safeErrorCode } from '../../util/safe-error.js';
7
- import { runnerErrorProjection } from '../../util/runner-error.js';
7
+ import { runnerErrorProjection } from '../runner-error.js';
8
8
  import { ResponseLatencyTracker } from '../../service/response-latency-tracker.js';
9
9
  import { GenerationTimingTracker } from '../../service/generation-timing-tracker.js';
10
- import { boundedConversationItemId, compactRunnerText, isPlainRecord } from '../../util/node-operation-parsers.js';
10
+ import { boundedConversationItemId, compactRunnerText } from '../normalization.js';
11
+ import { isPlainRecord } from '../../util/records.js';
11
12
  import { codexImageAttachments, codexLiveConversationItem, codexSourceItemId, CodexUsageNormalizer, codexUserInputQuestions } from './conversation-parser.js';
12
13
  export function resolveCodexNotificationRun(runs, input) {
13
14
  const entries = [...runs.entries()];
@@ -107,7 +108,7 @@ export class CodexManagedRunController {
107
108
  externalSessionId: threadId,
108
109
  ...(initiatedByUserId === undefined ? {} : { initiatedByUserId })
109
110
  });
110
- this.host.addActive(runId);
111
+ this.host.executionAccepted(runId);
111
112
  this.runner.execution.runs.set(runId, {
112
113
  sessionId: session.id,
113
114
  threadId,
@@ -131,7 +132,6 @@ export class CodexManagedRunController {
131
132
  this.usage.delete(runId);
132
133
  this.runner.execution.runs.delete(runId);
133
134
  this.runner.releaseEnvironment(runId);
134
- this.host.removeActive(runId);
135
135
  throw error;
136
136
  }
137
137
  }
@@ -372,7 +372,6 @@ export class CodexManagedRunController {
372
372
  this.runner.releaseEnvironment(runId);
373
373
  else
374
374
  void titleRefresh.finally(() => this.runner.releaseEnvironment(runId));
375
- this.host.removeActive(runId);
376
375
  this.finishing.delete(runId);
377
376
  };
378
377
  if (usage.hasRollout && turnId !== undefined) {
@@ -577,7 +576,7 @@ export class CodexManagedRunController {
577
576
  typeof payload.input !== 'string' ||
578
577
  typeof payload.cwd !== 'string' ||
579
578
  this.host.intercepted() ||
580
- this.host.active.has(payload.runId))
579
+ this.host.isExecutionActive(payload.runId))
581
580
  return;
582
581
  const { runId, sessionId, input, cwd } = payload;
583
582
  let attachments;
@@ -619,7 +618,7 @@ export class CodexManagedRunController {
619
618
  this.host.emit(runId, 'run.rejected', { code: safeErrorCode(error, 'SECRET_ENVIRONMENT_INVALID') }, 'FAILED');
620
619
  return;
621
620
  }
622
- this.host.addActive(runId);
621
+ this.host.executionAccepted(runId);
623
622
  void this.run({
624
623
  runId,
625
624
  sessionId,
@@ -739,7 +738,6 @@ export class CodexManagedRunController {
739
738
  this.usage.delete(runId);
740
739
  this.runner.execution.runs.delete(runId);
741
740
  this.runner.releaseEnvironment(runId);
742
- this.host.removeActive(runId);
743
741
  }
744
742
  }
745
743
  emitError(runId, error, fallbackCode) {
@@ -1,7 +1,7 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { basename, join } from 'node:path';
3
3
  import { forEachJsonlRecord } from '../../native-jsonl-reader.js';
4
- import { compactRunnerValue as compactValue } from '../../util/node-operation-parsers.js';
4
+ import { compactRunnerValue as compactValue } from '../normalization.js';
5
5
  import { stripGoalStartPrompt, publicGoalHistoryText } from '../../service/goal-runtime.js';
6
6
  import { parseCodexComposerImageMarkers } from './composer-image-markers.js';
7
7
  import { codexTranscriptSourceItemId } from './conversation-parser.js';
@@ -1,4 +1,4 @@
1
- import { compactRunnerText } from '../../util/node-operation-parsers.js';
1
+ import { compactRunnerText } from '../normalization.js';
2
2
  export function normalizeCodexApproval(method) {
3
3
  return method === 'item/commandExecution/requestApproval'
4
4
  ? { toolName: 'command_execution', reason: 'Command execution requires approval.' }
@@ -1,5 +1,5 @@
1
1
  import { withMarctlGuide } from '../mcp/marctl-guide.js';
2
- import { CodexAppServerClient, isCodexRolloutMissingError } from '../codex-app-server.js';
2
+ import { CodexAppServerClient, isCodexRolloutMissingError } from './codex/codex-app-server.js';
3
3
  import { AbstractRunner } from './abstract-runner.js';
4
4
  import { declaredRunnerProfiles } from '../runner-profiles.js';
5
5
  import { homedir } from 'node:os';
@@ -7,12 +7,12 @@ import { resolve } from 'node:path';
7
7
  import { discoverAllNativeSessions } from '../native-session-history.js';
8
8
  import { readCodexNativeContextUsage } from './codex/native-context-usage.js';
9
9
  import { codexThreadCwd, extractCodexThreads } from './codex/conversation-parser.js';
10
- import { deduplicateDirectSessions, isWithinWorkspace, latestNativeActivity } from '../util/runner-native-session-parsers.js';
10
+ import { deduplicateDirectSessions, isWithinWorkspace, latestNativeActivity } from '../session/runner-native-session-parsers.js';
11
11
  import { codexThreadHistoryModes, codexPaginatedThread, codexPaginatedCursor, codexPaginatedConversationPage } from './codex/conversation-parser.js';
12
- import { codexSessionControl } from '../codex-app-server.js';
12
+ import { codexSessionControl } from './codex/codex-app-server.js';
13
13
  import { extractId } from './codex/conversation-parser.js';
14
14
  import { nodeLog } from '../operational.js';
15
- import { conversationClientMessageId } from '../util/conversation-identity.js';
15
+ import { conversationClientMessageId } from '../session/conversation-identity.js';
16
16
  const CODEX_MODEL_CACHE_MS = 10 * 60 * 1_000;
17
17
  const CODEX_MODEL_FAILURE_RETRY_MS = 30 * 1_000;
18
18
  const CODEX_MODEL_PAGE_SIZE = 100;
@@ -245,40 +245,22 @@ export class CodexRunner extends AbstractRunner {
245
245
  usesRunnerTitleForRename() {
246
246
  return true;
247
247
  }
248
- async renameNative(session, input, database) {
248
+ async renameNative(session, input) {
249
249
  if (session.externalSessionId === null)
250
250
  return;
251
251
  if (input.title !== undefined) {
252
252
  await this.start();
253
253
  await this.setThreadName(session.externalSessionId, input.title ?? '');
254
254
  }
255
- database.saveNativeSessionMetadata({
256
- runner: this.name,
257
- externalSessionId: session.externalSessionId,
258
- workspaceId: session.workspaceId,
259
- ...(input.title === undefined ? {} : { runnerTitle: input.title }),
260
- ...(input.title === undefined
261
- ? {}
262
- : { runnerTitlePending: input.title !== null && input.title.trim().length > 0 }),
263
- ...(input.pinned === undefined ? {} : { pinned: input.pinned })
264
- });
265
255
  }
266
- projectedRunnerTitle(metadata, input, database) {
256
+ projectedRunnerTitle(metadata, input) {
267
257
  const cached = metadata?.runnerTitle ?? null;
268
258
  if (input.titleOrigin !== 'OFFICIAL' || input.title === undefined)
269
- return cached ?? input.title ?? null;
259
+ return { title: cached ?? input.title ?? null, clearPending: false };
270
260
  if (metadata?.runnerTitlePending === true) {
271
- if (input.title === cached)
272
- database?.saveNativeSessionMetadata({
273
- runner: this.name,
274
- externalSessionId: input.externalSessionId,
275
- workspaceId: input.workspaceId,
276
- runnerTitle: cached,
277
- runnerTitlePending: false
278
- });
279
- return cached ?? input.title;
261
+ return { title: cached ?? input.title, clearPending: input.title === cached };
280
262
  }
281
- return input.title;
263
+ return { title: input.title, clearPending: false };
282
264
  }
283
265
  async readConversationHistory(readers) {
284
266
  const page = await readers.official();
@@ -0,0 +1 @@
1
+ export {};
@@ -4,8 +4,8 @@ export function normalizeMarAgentUsage(usage) {
4
4
  const { executionId: _executionId, ...record } = usage;
5
5
  return { ...record, schemaVersion: 1, coverage: 'request' };
6
6
  }
7
- import { boundedConversationItemId, compactRunnerText, compactRunnerValue } from '../../util/node-operation-parsers.js';
8
- import { runnerErrorProjection } from '../../util/runner-error.js';
7
+ import { boundedConversationItemId, compactRunnerText, compactRunnerValue } from '../normalization.js';
8
+ import { runnerErrorProjection } from '../runner-error.js';
9
9
  import { toolPresentationSchema } from '@myagentroam/protocol';
10
10
  export class MarAgentConversationNormalizer {
11
11
  input(entry) {
@@ -1,6 +1,6 @@
1
1
  import { MarAgentConversationNormalizer } from './conversation-normalizer.js';
2
2
  import { publicGoalHistoryText } from '../../service/goal-runtime.js';
3
- import { boundedConversationItemId } from '../../util/node-operation-parsers.js';
3
+ import { boundedConversationItemId } from '../normalization.js';
4
4
  export function projectMarAgentHistory(session, entries, options = {}) {
5
5
  const turns = new Map();
6
6
  const normalizer = new MarAgentConversationNormalizer();