@myagentroam/node 0.9.121 → 0.9.124

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.
@@ -1,10 +1,4 @@
1
1
  import { spawn, spawnSync } from 'node:child_process';
2
- import { randomUUID } from 'node:crypto';
3
- import { existsSync } from 'node:fs';
4
- import { unlink } from 'node:fs/promises';
5
- import { connect } from 'node:net';
6
- import { tmpdir } from 'node:os';
7
- import { join } from 'node:path';
8
2
  import { createInterface } from 'node:readline';
9
3
  import { minimalRunnerEnvironment, platformCliInvocation, runRunnerProbe } from './operational.js';
10
4
  const REQUEST_TIMEOUT_MS = 15_000;
@@ -45,15 +39,13 @@ export function isCodexRolloutMissingError(error) {
45
39
  /^thread not loaded:\s*/i.test(error.rpcMessage)));
46
40
  }
47
41
  /**
48
- * Local JSON-RPC client for a Node-supervised Codex App Server. Linux/macOS
49
- * use a private Unix socket by default; Windows uses stdio. Neither is exposed
50
- * to MAR Server or the browser.
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.
51
44
  */
52
45
  export class CodexAppServerClient {
53
46
  options;
54
47
  child;
55
48
  transport;
56
- socketPath;
57
49
  nextId = 1;
58
50
  pending = new Map();
59
51
  pendingDrainWaiters = new Set();
@@ -105,43 +97,15 @@ export class CodexAppServerClient {
105
97
  }
106
98
  async startManaged() {
107
99
  const command = this.options.command ?? 'codex';
108
- const mode = this.transportMode();
109
- this.socketPath =
110
- mode === 'unix' ? (this.options.socketPath ?? privateSocketPath()) : undefined;
111
- const baseArgs = this.options.args ?? ['app-server'];
112
- const args = [
113
- ...baseArgs,
114
- ...(mode === 'unix' ? ['--listen', `unix://${this.socketPath}`] : [])
115
- ];
100
+ const args = this.options.args ?? ['app-server'];
116
101
  const spawnProcess = this.options.spawnProcess ?? defaultSpawn;
117
- let child = spawnProcess(command, args, {
102
+ const child = spawnProcess(command, args, {
118
103
  ...(this.options.cwd === undefined ? {} : { cwd: this.options.cwd }),
119
104
  env: { ...minimalRunnerEnvironment(), ...this.environment }
120
105
  });
121
106
  this.child = child;
122
- let socket;
123
- if (mode === 'unix') {
124
- try {
125
- socket = await connectUnix(this.socketPath, (this.options.transport ?? 'auto') === 'auto'
126
- ? Math.min(this.options.timeoutMs ?? REQUEST_TIMEOUT_MS, 1_000)
127
- : (this.options.timeoutMs ?? REQUEST_TIMEOUT_MS));
128
- }
129
- catch (error) {
130
- if ((this.options.transport ?? 'auto') !== 'auto')
131
- throw error;
132
- child.kill();
133
- if (this.socketPath !== undefined && existsSync(this.socketPath))
134
- await unlink(this.socketPath);
135
- this.socketPath = undefined;
136
- child = spawnProcess(command, baseArgs, {
137
- ...(this.options.cwd === undefined ? {} : { cwd: this.options.cwd }),
138
- env: { ...minimalRunnerEnvironment(), ...this.environment }
139
- });
140
- this.child = child;
141
- }
142
- }
143
- this.transport = socket ?? stdioTransport(child);
144
- const lines = createInterface({ input: socket ?? child.stdout });
107
+ this.transport = stdioTransport(child);
108
+ const lines = createInterface({ input: child.stdout });
145
109
  lines.on('line', (line) => this.handleLine(line));
146
110
  const managedChild = child;
147
111
  child.once('error', (error) => {
@@ -180,9 +144,6 @@ export class CodexAppServerClient {
180
144
  this.transport = undefined;
181
145
  this.child?.kill();
182
146
  this.child = undefined;
183
- if (this.socketPath !== undefined && existsSync(this.socketPath))
184
- void unlink(this.socketPath);
185
- this.socketPath = undefined;
186
147
  this.failAll(new Error('CODEX_APP_SERVER_STOPPED'));
187
148
  }
188
149
  onNotification(listener) {
@@ -331,15 +292,6 @@ export class CodexAppServerClient {
331
292
  }
332
293
  this.resolvePendingDrain();
333
294
  }
334
- transportMode() {
335
- const requested = this.options.transport ?? 'auto';
336
- if (requested === 'auto')
337
- return process.platform === 'linux' || process.platform === 'darwin' ? 'unix' : 'stdio';
338
- if (requested === 'unix' && process.platform !== 'linux' && process.platform !== 'darwin') {
339
- throw new Error('CODEX_UNIX_SOCKET_PLATFORM_UNSUPPORTED');
340
- }
341
- return requested;
342
- }
343
295
  }
344
296
  function codexThreadConfiguration(cwd, configuration) {
345
297
  return {
@@ -472,28 +424,3 @@ function stdioTransport(child) {
472
424
  destroy: () => child.stdout.destroy()
473
425
  };
474
426
  }
475
- export function privateSocketPath(directory = tmpdir(), processId = process.pid, entropy = randomUUID()) {
476
- // macOS has a shorter Unix-domain socket pathname limit than Linux, and its
477
- // per-user TMPDIR is comparatively long. The process ID plus 64 random bits
478
- // keeps concurrent sockets distinct while leaving room for that directory.
479
- return join(directory, `m-${processId}-${entropy.replaceAll('-', '').slice(0, 16)}.sock`);
480
- }
481
- function connectUnix(socketPath, timeoutMs) {
482
- return new Promise((resolve, reject) => {
483
- const deadline = Date.now() + timeoutMs;
484
- const attempt = () => {
485
- const socket = connect(socketPath);
486
- const retry = (error) => {
487
- socket.destroy();
488
- if (Date.now() >= deadline) {
489
- reject(new Error(`CODEX_UNIX_SOCKET_CONNECT_FAILED:${error.message}`));
490
- return;
491
- }
492
- setTimeout(attempt, 25);
493
- };
494
- socket.once('connect', () => resolve(socket));
495
- socket.once('error', retry);
496
- };
497
- attempt();
498
- });
499
- }
package/dist/database.js CHANGED
@@ -7,7 +7,7 @@ import { mcpCatalogEntrySchema, mcpInstallationConfigurationSchema, mcpRuntimeDe
7
7
  import { assertNodeSchemaV13, NODE_SCHEMA_V13, NODE_SCHEMA_VERSION_V13 } from './migrations/v013.js';
8
8
  // Future released migrations must be contiguous, immutable and begin at v14; Demo is excluded.
9
9
  export const NODE_SCHEMA_MIGRATIONS = [];
10
- const LEGACY_NODE_SCHEMA_V12_HISTORY = Array.from({ length: 12 }, (_, index) => index + 1);
10
+ const LEGACY_NODE_SCHEMA_V12 = NODE_SCHEMA_VERSION_V13 - 1;
11
11
  export class NodeDatabase {
12
12
  now;
13
13
  raw;
@@ -366,8 +366,12 @@ export function migrate(database, catalog = NODE_SCHEMA_MIGRATIONS) {
366
366
  const versions = database
367
367
  .prepare('SELECT version FROM node_schema_migrations ORDER BY version')
368
368
  .all().map((row) => numberValue(row, 'version'));
369
- if (versions.length === LEGACY_NODE_SCHEMA_V12_HISTORY.length &&
370
- versions.every((version, index) => version === LEGACY_NODE_SCHEMA_V12_HISTORY[index])) {
369
+ const firstLegacyVersion = versions[0];
370
+ if (firstLegacyVersion !== undefined &&
371
+ firstLegacyVersion >= 1 &&
372
+ firstLegacyVersion <= 4 &&
373
+ versions.length === LEGACY_NODE_SCHEMA_V12 - firstLegacyVersion + 1 &&
374
+ versions.every((version, index) => version === firstLegacyVersion + index)) {
371
375
  // v13 is a history-compaction baseline. The final v12 schema is still the v13 schema;
372
376
  // validate it before atomically replacing only the migration ledger.
373
377
  assertNodeSchemaV13(database);
@@ -123,7 +123,7 @@ export class ConversationHistoryService {
123
123
  if (activeCursor !== undefined ||
124
124
  this.options.hasManagedActiveRun(session) ||
125
125
  this.hasManagedRuntimeHistory(session))
126
- return this.readManagedActive(session, input, activeCursor, snapshotSequence, readAt);
126
+ return this.readManagedRuntime(session, input, activeCursor, snapshotSequence, readAt);
127
127
  return this.readHistory(session, input, snapshotSequence, readAt);
128
128
  }
129
129
  async readHistoryPage(session, input) {
@@ -140,7 +140,7 @@ export class ConversationHistoryService {
140
140
  cursor: null
141
141
  }
142
142
  : undefined;
143
- return this.readManagedActive(session, input, olderCursor, this.options.snapshotSequence(), Date.now());
143
+ return this.readManagedRuntime(session, input, olderCursor, this.options.snapshotSequence(), Date.now());
144
144
  }
145
145
  return this.readHistory(session, input, this.options.snapshotSequence(), Date.now());
146
146
  }
@@ -367,6 +367,38 @@ export class ConversationHistoryService {
367
367
  deferredOlderHistory: true
368
368
  });
369
369
  }
370
+ async readManagedRuntime(session, input, cursor, snapshotSequence, readAt) {
371
+ const page = await this.readManagedActive(session, input, cursor, snapshotSequence, readAt);
372
+ if (input.cursor !== undefined ||
373
+ session.externalSessionId === null ||
374
+ this.options.hasManagedActiveRun(session) ||
375
+ !this.hasManagedRuntimeHistory(session) ||
376
+ !page.turns.some((turn) => turn.rewind === undefined &&
377
+ terminalConversationTurn(turn.status) &&
378
+ conversationClientMessageId(turn) !== null))
379
+ return page;
380
+ // A completed managed Turn keeps its live items and Run identity until Runtime cleanup.
381
+ // Only the SDK's matched native Turn may supply a destructive history boundary.
382
+ const official = await this.readOfficial(session, {
383
+ limit: Math.min(500, Math.max(30, page.turns.length + 1))
384
+ }).catch(() => undefined);
385
+ if (official === undefined)
386
+ return page;
387
+ return {
388
+ ...page,
389
+ turns: page.turns.map((turn) => {
390
+ if (turn.rewind !== undefined || !terminalConversationTurn(turn.status))
391
+ return turn;
392
+ const messageId = conversationClientMessageId(turn);
393
+ if (messageId === null)
394
+ return turn;
395
+ const native = official.turns.find((candidate) => candidate.rewind !== undefined &&
396
+ candidate.userItemId !== null &&
397
+ conversationClientMessageId(candidate) === messageId);
398
+ return native?.rewind === undefined ? turn : { ...turn, rewind: native.rewind };
399
+ })
400
+ };
401
+ }
370
402
  async refreshExternalActivity(session) {
371
403
  const runner = this.options.runners.require(session.runner);
372
404
  if (!runner.available(this.options.capabilities())) {
@@ -583,6 +615,9 @@ function sameConversationTurn(left, right) {
583
615
  const leftMessageId = conversationClientMessageId(left);
584
616
  return leftMessageId !== null && leftMessageId === conversationClientMessageId(right);
585
617
  }
618
+ function terminalConversationTurn(status) {
619
+ return ['SUCCEEDED', 'FAILED', 'CANCELLED', 'INTERRUPTED', 'LOST'].includes(status);
620
+ }
586
621
  function conversationClientMessageId(turn) {
587
622
  for (const item of turn.items) {
588
623
  if (item.kind !== 'user_message' || !isPlainRecord(item.payload))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.121",
3
+ "version": "0.9.124",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -30,9 +30,9 @@
30
30
  "npm": "11.12.1",
31
31
  "ws": "^8.21.3",
32
32
  "zod": "4.4.3",
33
- "@myagentroam/agent": "0.9.121",
34
- "@myagentroam/code-navigation": "0.9.121",
35
- "@myagentroam/protocol": "0.9.121"
33
+ "@myagentroam/agent": "0.9.124",
34
+ "@myagentroam/protocol": "0.9.124",
35
+ "@myagentroam/code-navigation": "0.9.124"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/ws": "^8.18.1"