@myagentroam/node 0.11.24 → 0.11.25

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.
@@ -356,7 +356,8 @@ export class MarAgentConversationNormalizer {
356
356
  if (observation.id !== undefined)
357
357
  this.#codeModeObservationIds.add(observation.id);
358
358
  this.#codeModeSourceCallIds.add(observation.source.callId);
359
- items.push(...projected.map((item) => 'callId' in event ? { ...item, parentItemId: itemId('tool', event.callId) } : item));
359
+ // An execution/observation container does not own these public tool records.
360
+ items.push(...projected);
360
361
  }
361
362
  return items;
362
363
  }
@@ -102,7 +102,7 @@ export function projectMarAgentHistory(session, entries, options = {}) {
102
102
  const attachments = projected.generatedImages === undefined
103
103
  ? []
104
104
  : (options.registerGeneratedImages?.(event.sessionId, projected.generatedImages) ?? []);
105
- replace(projectionTurn, historyItem(session, projectionTurn, projected.itemId, projected.kind, projected.status, attachments.length === 0 ? projected.payload : { ...projected.payload, attachments }, at, projected.permissionStop, projected.parentItemId));
105
+ replace(projectionTurn, historyItem(session, projectionTurn, projected.itemId, projected.kind, projected.status, attachments.length === 0 ? projected.payload : { ...projected.payload, attachments }, at, projected.permissionStop));
106
106
  }
107
107
  }
108
108
  return [...turns.values()];
@@ -129,13 +129,13 @@ function record(value) {
129
129
  ? value
130
130
  : undefined;
131
131
  }
132
- function historyItem(session, turn, id, kind, status, payload, at, permissionStop, parentItemId) {
132
+ function historyItem(session, turn, id, kind, status, payload, at, permissionStop) {
133
133
  return {
134
134
  id,
135
135
  sessionId: session.id,
136
136
  turnId: turn.id,
137
137
  runId: turn.runId,
138
- parentItemId: parentItemId ?? null,
138
+ parentItemId: null,
139
139
  sourceSequence: null,
140
140
  revision: 0,
141
141
  kind,
@@ -701,7 +701,6 @@ export class MarAgentManagedRunController {
701
701
  []);
702
702
  this.host.emitItem(runId, {
703
703
  itemId: item.itemId,
704
- ...(item.parentItemId === undefined ? {} : { parentItemId: item.parentItemId }),
705
704
  kind: item.kind,
706
705
  status: item.status,
707
706
  payload: attachments.length === 0 ? item.payload : { ...item.payload, attachments },
@@ -1,9 +1,11 @@
1
1
  import { createSocket } from 'node:dgram';
2
+ import { isIP } from 'node:net';
2
3
  import { directDataControlFrameSchema, directNodeCommandSchema } from '@myagentroam/protocol';
3
4
  import { TerminalDirectService } from './terminal-direct-service.js';
4
5
  import { IceUdpMuxListener as NativeIceUdpMuxListener } from 'node-datachannel';
5
6
  import { RTCPeerConnection } from 'node-datachannel/polyfill';
6
7
  const MAX_HOST_CANDIDATES = 32;
8
+ const LOOPBACK_CANDIDATE_PRIORITY = '2130706431';
7
9
  const MAX_ACTIVE_TRANSPORTS = 128;
8
10
  const MAX_ACTIVE_TRANSFERS = 128;
9
11
  const DATA_CHANNEL_HIGH_WATER_BYTES = 4 * 1024 * 1024;
@@ -159,6 +161,7 @@ export class DirectTransferService {
159
161
  };
160
162
  transport.leaseTimer = this.startLeaseWatch(() => transport.leaseExpiresAt, () => transport.lastLeaseAliveAt, () => this.removeTransport(transport, 'DIRECT_TRANSFER_CONTROL_UNAVAILABLE'));
161
163
  this.transports.set(command.directSessionId, transport);
164
+ const sentLoopbackAddresses = new Set();
162
165
  peer.onicecandidate = (event) => {
163
166
  const candidate = event.candidate;
164
167
  if (candidate === null) {
@@ -170,8 +173,29 @@ export class DirectTransferService {
170
173
  return;
171
174
  }
172
175
  const json = candidate.toJSON();
173
- if (typeof json.candidate !== 'string' ||
174
- !acceptHostCandidate(json.candidate, transport.sentCandidateKeys))
176
+ if (typeof json.candidate !== 'string')
177
+ return;
178
+ const loopback = loopbackHostCandidate(json.candidate);
179
+ const loopbackAddress = loopback?.trim().split(/\s+/u)[4];
180
+ if (loopback !== undefined &&
181
+ loopbackAddress !== undefined &&
182
+ !sentLoopbackAddresses.has(loopbackAddress)) {
183
+ sentLoopbackAddresses.add(loopbackAddress);
184
+ if (acceptHostCandidate(loopback, transport.sentCandidateKeys))
185
+ this.emit(command.directSessionId, {
186
+ type: 'direct.signal',
187
+ directSessionId: command.directSessionId,
188
+ signal: {
189
+ kind: 'CANDIDATE',
190
+ candidate: {
191
+ candidate: loopback,
192
+ sdpMid: json.sdpMid ?? null,
193
+ sdpMLineIndex: json.sdpMLineIndex ?? null
194
+ }
195
+ }
196
+ });
197
+ }
198
+ if (!acceptHostCandidate(json.candidate, transport.sentCandidateKeys))
175
199
  return;
176
200
  this.emit(command.directSessionId, {
177
201
  type: 'direct.signal',
@@ -543,6 +567,19 @@ export function probeUdp4Port(port) {
543
567
  socket.bind({ port, address: '0.0.0.0', exclusive: true }, () => socket.close(() => resolve()));
544
568
  });
545
569
  }
570
+ export function loopbackHostCandidate(candidate) {
571
+ if (!safeHostCandidate(candidate))
572
+ return undefined;
573
+ const address = candidate.trim().split(/\s+/u)[4];
574
+ const family = isIP(address ?? '');
575
+ const loopback = family === 4 ? '127.0.0.1' : family === 6 ? '::1' : undefined;
576
+ if (loopback === undefined || address === loopback)
577
+ return undefined;
578
+ const fields = candidate.trim().split(/\s+/u);
579
+ fields[3] = LOOPBACK_CANDIDATE_PRIORITY;
580
+ fields[4] = loopback;
581
+ return fields.join(' ');
582
+ }
546
583
  export function fingerprint(sdp) {
547
584
  const match = sdp?.match(/^a=fingerprint:(sha-256 [A-F0-9:]+)\r?$/imu);
548
585
  return match?.[1];
@@ -583,20 +620,26 @@ function filterIceCandidates(sdp, accepted) {
583
620
  if (sdp === undefined)
584
621
  return { sdp: undefined };
585
622
  const included = new Set();
586
- const lines = sdp.split(/\r?\n/u).filter((line) => {
623
+ const lines = sdp.split(/\r?\n/u).flatMap((line) => {
587
624
  if (!line.startsWith('a=candidate:'))
588
- return true;
625
+ return [line];
589
626
  const candidate = line.slice(2);
590
627
  if (!safeHostCandidate(candidate))
591
- return false;
592
- const key = candidateKey(candidate);
593
- if (key === undefined || included.has(key))
594
- return false;
595
- if (!accepted.has(key) && accepted.size >= MAX_HOST_CANDIDATES)
596
- return false;
597
- accepted.add(key);
598
- included.add(key);
599
- return true;
628
+ return [];
629
+ const loopback = loopbackHostCandidate(candidate);
630
+ const candidates = loopback === undefined ? [candidate] : [loopback, candidate];
631
+ const retained = [];
632
+ for (const value of candidates) {
633
+ const key = candidateKey(value);
634
+ if (key === undefined || included.has(key))
635
+ continue;
636
+ if (!accepted.has(key) && accepted.size >= MAX_HOST_CANDIDATES)
637
+ continue;
638
+ accepted.add(key);
639
+ included.add(key);
640
+ retained.push(`a=${value}`);
641
+ }
642
+ return retained;
600
643
  });
601
644
  return { sdp: lines.join('\r\n') };
602
645
  }
@@ -0,0 +1,49 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { dirname } from 'node:path';
3
+ import { resolveWorkspaceFileManagerTarget } from '../workspace.js';
4
+ export function fileManagerInvocation(platform, target) {
5
+ if (platform === 'win32')
6
+ return {
7
+ command: 'explorer.exe',
8
+ args: target.kind === 'FILE' ? ['/select,', target.path] : [target.path]
9
+ };
10
+ if (platform === 'darwin')
11
+ return { command: 'open', args: target.kind === 'FILE' ? ['-R', target.path] : [target.path] };
12
+ if (platform === 'linux')
13
+ return {
14
+ command: 'xdg-open',
15
+ args: [target.kind === 'FILE' ? dirname(target.path) : target.path]
16
+ };
17
+ throw new Error('FILE_MANAGER_UNAVAILABLE');
18
+ }
19
+ export async function revealWorkspaceFileInFileManager(workspaceRoot, requestedPath, options = {}) {
20
+ const target = await resolveWorkspaceFileManagerTarget(workspaceRoot, requestedPath);
21
+ const platform = options.platform ?? process.platform;
22
+ const invocation = fileManagerInvocation(platform, target);
23
+ const spawnProcess = options.spawnProcess ?? spawn;
24
+ let child;
25
+ try {
26
+ child = spawnProcess(invocation.command, invocation.args, {
27
+ detached: true,
28
+ stdio: 'ignore',
29
+ windowsHide: true
30
+ });
31
+ await new Promise((resolve, reject) => {
32
+ child.once('error', reject);
33
+ if (platform === 'win32')
34
+ child.once('spawn', () => resolve());
35
+ else
36
+ child.once('exit', (code) => {
37
+ if (code === 0)
38
+ resolve();
39
+ else
40
+ reject(new Error('FILE_MANAGER_UNAVAILABLE'));
41
+ });
42
+ });
43
+ }
44
+ catch (error) {
45
+ throw new Error('FILE_MANAGER_UNAVAILABLE', { cause: error });
46
+ }
47
+ child.unref();
48
+ return { revealed: true };
49
+ }
@@ -2,6 +2,7 @@ import { basename, dirname, isAbsolute, relative, resolve, win32 } from 'node:pa
2
2
  import { lstat, realpath } from 'node:fs/promises';
3
3
  import { isComposerAttachmentResourcePath } from '@myagentroam/protocol';
4
4
  import { listWorkspaceFiles, mutateWorkspaceFile, normalizeFilePreviewPath, readAbsoluteFileContentRange, readWorkspaceFileContentRange, readAbsoluteTextFile, readWorkspaceTextFile, writeWorkspaceTextFile, WorkspaceFileIndex } from '../workspace.js';
5
+ import { revealWorkspaceFileInFileManager } from './workspace-file-manager-service.js';
5
6
  const INDEX_CACHE_LIMIT = 32;
6
7
  const INDEX_CACHE_BYTE_LIMIT = 128 * 1024 * 1024;
7
8
  const INDEX_QUEUE_LIMIT = 16;
@@ -224,6 +225,11 @@ export class WorkspaceFileService {
224
225
  return result;
225
226
  });
226
227
  }
228
+ async reveal(workspace, input) {
229
+ if (typeof input.path !== 'string')
230
+ throw new Error('FILE_REVEAL_INVALID');
231
+ return revealWorkspaceFileInFileManager(workspace.path, input.path);
232
+ }
227
233
  async serializeMutation(workspaceId, operation) {
228
234
  const previous = this.mutationTails.get(workspaceId) ?? Promise.resolve();
229
235
  const result = previous.then(operation, operation);
@@ -41,6 +41,10 @@ export class WorkspaceWorkbenchService {
41
41
  'workspace.file.direct.metadata': (data) => this.directFileMetadata(record(data)),
42
42
  'workspace.files.search': (data) => this.searchFiles(record(data)),
43
43
  'workspace.files.mutate': (data) => this.mutateFile(record(data)),
44
+ 'workspace.files.reveal': (data) => {
45
+ const input = record(data);
46
+ return this.options.files.reveal(this.options.requireWorkspace(input.workspaceId), input);
47
+ },
44
48
  'workspace.content.search': (data) => this.searchContent(record(data)),
45
49
  'workspace.symbol.prepare': (data) => this.prepareSymbolIndex(record(data)),
46
50
  'workspace.symbol.definition': (data) => this.symbolDefinition(record(data)),
package/dist/workspace.js CHANGED
@@ -1084,6 +1084,15 @@ async function resolveWorkspaceEntryMetadata(root, requestedPath) {
1084
1084
  const metadata = await lstat(candidate);
1085
1085
  return { path: candidate, metadata };
1086
1086
  }
1087
+ export async function resolveWorkspaceFileManagerTarget(workspaceRoot, requestedPath) {
1088
+ const root = await resolveRegisteredWorkspaceRoot(workspaceRoot);
1089
+ const entry = await resolveWorkspaceEntryMetadata(root, requestedPath);
1090
+ if (entry.metadata.isDirectory())
1091
+ return { path: entry.path, kind: 'DIRECTORY' };
1092
+ if (entry.metadata.isFile())
1093
+ return { path: entry.path, kind: 'FILE' };
1094
+ throw new Error('FILE_NOT_REGULAR');
1095
+ }
1087
1096
  async function inspectWorkspaceIndexEntry(root, requestedPath, name, excludes) {
1088
1097
  try {
1089
1098
  const entry = await resolveWorkspaceEntryMetadata(root, requestedPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.11.24",
3
+ "version": "0.11.25",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -37,10 +37,10 @@
37
37
  "playwright": "1.62.1",
38
38
  "ws": "^8.21.3",
39
39
  "zod": "4.4.3",
40
- "@myagentroam/agent": "0.11.24",
41
- "@myagentroam/code-navigation": "0.11.0",
42
- "@myagentroam/protocol": "0.11.24",
43
- "@myagentroam/marctl": "0.11.1"
40
+ "@myagentroam/agent": "0.11.25",
41
+ "@myagentroam/protocol": "0.11.25",
42
+ "@myagentroam/marctl": "0.11.1",
43
+ "@myagentroam/code-navigation": "0.11.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/ws": "^8.18.1"