@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.
- package/dist/runner/mar-agent/conversation-normalizer.js +2 -1
- package/dist/runner/mar-agent/history-projection.js +3 -3
- package/dist/runner/mar-agent/managed-run-controller.js +0 -1
- package/dist/service/direct-transfer-service.js +56 -13
- package/dist/service/workspace-file-manager-service.js +49 -0
- package/dist/service/workspace-file-service.js +6 -0
- package/dist/service/workspace-workbench-service.js +4 -0
- package/dist/workspace.js +9 -0
- package/package.json +5 -5
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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).
|
|
623
|
+
const lines = sdp.split(/\r?\n/u).flatMap((line) => {
|
|
587
624
|
if (!line.startsWith('a=candidate:'))
|
|
588
|
-
return
|
|
625
|
+
return [line];
|
|
589
626
|
const candidate = line.slice(2);
|
|
590
627
|
if (!safeHostCandidate(candidate))
|
|
591
|
-
return
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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.
|
|
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.
|
|
41
|
-
"@myagentroam/
|
|
42
|
-
"@myagentroam/
|
|
43
|
-
"@myagentroam/
|
|
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"
|