@myagentroam/node 0.10.24 → 0.10.26
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/NOTICE +26 -0
- package/dist/connector.js +20 -5
- package/dist/runner/abstract-runner.js +8 -2
- package/dist/runner/mar-agent/conversation-normalizer.js +14 -3
- package/dist/runner/mar-agent/history-projection.js +6 -3
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +1 -0
- package/dist/runner/mar-agent/managed-run-controller.js +90 -7
- package/dist/runner/mar-agent-runner.js +6 -1
- package/dist/runtime-state.js +6 -0
- package/dist/service/goal-runtime.js +4 -2
- package/dist/service/mail-notification-control.js +42 -5
- package/dist/service/mail-notification-mcp-server.js +32 -3
- package/dist/service/mail-notification-mcp-stdio.js +28 -2
- package/dist/service/native-session-projection-service.js +6 -2
- package/dist/service/run-event-service.js +11 -4
- package/dist/service/runner-service.js +6 -2
- package/dist/service/session-lifecycle-service.js +2 -2
- package/package.json +4 -4
package/NOTICE
CHANGED
|
@@ -9,3 +9,29 @@ Author and licensing contact: aCHAOS (achaosxyz@gmail.com)
|
|
|
9
9
|
Licensed under MyAgentRoam Enterprise Source-Available License 1.0; see
|
|
10
10
|
ee/LICENSE and LICENSING.md. Independent core and third-party notices remain
|
|
11
11
|
applicable; this attribution does not transfer any contributor's copyright.
|
|
12
|
+
|
|
13
|
+
Third-party supplement: http_ece@1.2.0 (Web Push encryption dependency)
|
|
14
|
+
The npm archive omits its license file; the upstream v1.2.0 text follows.
|
|
15
|
+
Source: https://github.com/martinthomson/encrypted-content-encoding/blob/0562510a30819f52424724a6fd5504becacd98a1/LICENSE
|
|
16
|
+
|
|
17
|
+
The MIT License (MIT)
|
|
18
|
+
|
|
19
|
+
Copyright (c) 2015 Martin Thomson
|
|
20
|
+
|
|
21
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
22
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
23
|
+
in the Software without restriction, including without limitation the rights
|
|
24
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
25
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
26
|
+
furnished to do so, subject to the following conditions:
|
|
27
|
+
|
|
28
|
+
The above copyright notice and this permission notice shall be included in all
|
|
29
|
+
copies or substantial portions of the Software.
|
|
30
|
+
|
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
33
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
34
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
35
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
36
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
37
|
+
SOFTWARE.
|
package/dist/connector.js
CHANGED
|
@@ -310,7 +310,10 @@ export class NodeConnector {
|
|
|
310
310
|
throw new Error('SCHEDULE_SERVICE_UNAVAILABLE');
|
|
311
311
|
return this.sessionScheduleService.createFromMcp(input);
|
|
312
312
|
}, (sessionId) => this.runtime.getAgentSession(sessionId)?.externalSessionId ?? sessionId);
|
|
313
|
-
mailNotificationMcpServer = new MailNotificationMcpServer((capability) => resolveMailNotificationCapability(this.runtime, capability), (request) => this.mailNotificationControl.request(request)
|
|
313
|
+
mailNotificationMcpServer = new MailNotificationMcpServer((capability) => resolveMailNotificationCapability(this.runtime, capability), (request) => this.mailNotificationControl.request(request), {
|
|
314
|
+
supported: () => this.mailNotificationControl.supportsChannels(),
|
|
315
|
+
send: (request) => this.mailNotificationControl.notification(request)
|
|
316
|
+
});
|
|
314
317
|
runnerUsageReader;
|
|
315
318
|
terminalManager;
|
|
316
319
|
upgradeCoordinator;
|
|
@@ -392,7 +395,7 @@ export class NodeConnector {
|
|
|
392
395
|
executionStarted: (executionId) => this.continuousRunCoordinator.executionStarted(executionId),
|
|
393
396
|
keepRunActive: (sessionId) => (this.goalRetryTimers.has(sessionId) || this.goalContinuationPending.has(sessionId)) &&
|
|
394
397
|
this.goalRuntime.get(sessionId)?.status === 'active',
|
|
395
|
-
executionTerminating: (executionId, status) => {
|
|
398
|
+
executionTerminating: (executionId, status, permissionStop) => {
|
|
396
399
|
this.gitOperationService.executionFinished(executionId);
|
|
397
400
|
const pending = this.continuousRunCoordinator.pending(executionId);
|
|
398
401
|
if (pending?.goal === undefined)
|
|
@@ -401,6 +404,11 @@ export class NodeConnector {
|
|
|
401
404
|
const progressed = this.goalExecutionProgress.delete(executionId);
|
|
402
405
|
if (!this.goalGenerationCurrent(pending.sessionId, pending.goal))
|
|
403
406
|
return;
|
|
407
|
+
if (permissionStop !== undefined) {
|
|
408
|
+
this.retireGoalContinuation(pending.sessionId);
|
|
409
|
+
this.continuousRunCoordinator.removeGoalQueue(pending.sessionId);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
404
412
|
const disposition = this.goalRuntime.recordExecutionResult(pending.sessionId, {
|
|
405
413
|
failed: status === 'FAILED',
|
|
406
414
|
progressed: status !== 'SUCCEEDED' || progressed
|
|
@@ -597,6 +605,8 @@ export class NodeConnector {
|
|
|
597
605
|
});
|
|
598
606
|
this.capabilities = this.projectComputerUseCapability(this.capabilities);
|
|
599
607
|
this.marAgentRunController = new MarAgentManagedRunController(this.marAgentClient, {
|
|
608
|
+
createApproval: (input) => this.runtime.createApproval(input),
|
|
609
|
+
publishApproval: (input) => this.publishApprovalForRun(input),
|
|
600
610
|
active: this.runState.active,
|
|
601
611
|
adopt: (input) => this.runtime.adoptRun(input),
|
|
602
612
|
promote: (sessionId, nativeSessionId) => this.sessionIdentityService.promote(sessionId, nativeSessionId),
|
|
@@ -910,7 +920,7 @@ export class NodeConnector {
|
|
|
910
920
|
targetId: input.workspaceId,
|
|
911
921
|
catalogEntryId: 'mar-internal-mail',
|
|
912
922
|
serverName: 'mar_notification',
|
|
913
|
-
displayName: 'MAR
|
|
923
|
+
displayName: 'MAR notification',
|
|
914
924
|
version: '1.0.0',
|
|
915
925
|
enabled: true,
|
|
916
926
|
runtime: { transport: 'HTTP', url: 'https://127.0.0.1.invalid/mcp', headers: {} },
|
|
@@ -1135,7 +1145,9 @@ export class NodeConnector {
|
|
|
1135
1145
|
return undefined;
|
|
1136
1146
|
const current = this.goalRuntime.get(session.id);
|
|
1137
1147
|
if (current?.status === 'active') {
|
|
1138
|
-
|
|
1148
|
+
const idlePausedGoal = this.runtime.activeSessionRun(session.id) === undefined &&
|
|
1149
|
+
this.runtime.sessionQueue(session.id).paused;
|
|
1150
|
+
if (intent !== 'REPLACE_CURRENT' && !idlePausedGoal)
|
|
1139
1151
|
return undefined;
|
|
1140
1152
|
this.invalidateGoalContinuation(session.id);
|
|
1141
1153
|
const resumed = this.goalRuntime.resume(session.id);
|
|
@@ -1236,6 +1248,7 @@ export class NodeConnector {
|
|
|
1236
1248
|
respondUserInput: (runId, requestId, answers) => this.openCodeRunController.respondUserInput(runId, requestId, answers)
|
|
1237
1249
|
});
|
|
1238
1250
|
this.marAgentClient.bindControl({
|
|
1251
|
+
decideApproval: (input) => this.marAgentRunController.decideApproval(input),
|
|
1239
1252
|
start: (envelope) => this.marAgentRunController.start(envelope),
|
|
1240
1253
|
cancel: (runId, intent) => this.marAgentRunController.cancel(runId, intent),
|
|
1241
1254
|
respondUserInput: (runId, requestId, answers) => this.marAgentRunController.respondUserInput(runId, requestId, answers)
|
|
@@ -1325,6 +1338,7 @@ export class NodeConnector {
|
|
|
1325
1338
|
config: () => this.config,
|
|
1326
1339
|
register: async (nodeId, credential, payload) => {
|
|
1327
1340
|
await this.connectionLifecycle.register(nodeId, credential);
|
|
1341
|
+
this.mailNotificationControl.setFeatures(payload);
|
|
1328
1342
|
this.usageOutbox?.connected(payload);
|
|
1329
1343
|
},
|
|
1330
1344
|
usageAck: (payload) => this.usageOutbox?.handle(createEnvelope('usage.ack', payload)),
|
|
@@ -1340,6 +1354,7 @@ export class NodeConnector {
|
|
|
1340
1354
|
connected: (payload) => {
|
|
1341
1355
|
this.usageOutbox?.connected(payload);
|
|
1342
1356
|
this.connectionLifecycle.connected();
|
|
1357
|
+
this.mailNotificationControl.setFeatures(payload);
|
|
1343
1358
|
},
|
|
1344
1359
|
close: (code, reason) => this.controlChannel.close(code, reason),
|
|
1345
1360
|
inspectWorkspace: (envelope) => this.workspaceCoordinator.inspect(envelope),
|
|
@@ -2092,7 +2107,7 @@ export class NodeConnector {
|
|
|
2092
2107
|
return goalExit;
|
|
2093
2108
|
}
|
|
2094
2109
|
prepareGoalSession(session) {
|
|
2095
|
-
const access = goalUnrestrictedAccess(session.runner);
|
|
2110
|
+
const access = goalUnrestrictedAccess(session.runner, session.access);
|
|
2096
2111
|
const requiresUpdate = session.access !== access;
|
|
2097
2112
|
const changed = session.runner !== 'mar-agent' && requiresUpdate;
|
|
2098
2113
|
const prepared = requiresUpdate
|
|
@@ -219,7 +219,12 @@ function resolvedMcpServers(raw, secrets) {
|
|
|
219
219
|
mar_create_scheduled_task: ['direct', 'programmatic'],
|
|
220
220
|
mar_update_scheduled_task: ['direct', 'programmatic']
|
|
221
221
|
}
|
|
222
|
-
: {
|
|
222
|
+
: {
|
|
223
|
+
mar_send_email_notification: ['direct', 'programmatic'],
|
|
224
|
+
...(record['notificationChannels'] === true
|
|
225
|
+
? { mar_send_notification: ['direct', 'programmatic'] }
|
|
226
|
+
: {})
|
|
227
|
+
},
|
|
223
228
|
transport: 'STDIO',
|
|
224
229
|
command: record['command'],
|
|
225
230
|
args: record['args'],
|
|
@@ -236,7 +241,8 @@ function resolvedMcpServers(raw, secrets) {
|
|
|
236
241
|
}
|
|
237
242
|
: {
|
|
238
243
|
MAR_MAIL_MCP_ENDPOINT: record['endpoint'],
|
|
239
|
-
MAR_MAIL_MCP_TOKEN: secrets[record['secretName']]
|
|
244
|
+
MAR_MAIL_MCP_TOKEN: secrets[record['secretName']],
|
|
245
|
+
MAR_NOTIFICATION_CHANNELS: record['notificationChannels'] === true ? '1' : '0'
|
|
240
246
|
})
|
|
241
247
|
});
|
|
242
248
|
continue;
|
|
@@ -77,9 +77,12 @@ export class MarAgentConversationNormalizer {
|
|
|
77
77
|
return [];
|
|
78
78
|
if (event.type === 'tool.started')
|
|
79
79
|
return this.#toolStarted(event);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return [
|
|
80
|
+
const permissionStop = permissionStopForToolEvent(event);
|
|
81
|
+
const outerItems = event.type === 'tool.completed' ? this.#toolCompleted(event) : this.#toolFailed(event);
|
|
82
|
+
return [
|
|
83
|
+
...withPermissionStop(outerItems, permissionStop),
|
|
84
|
+
...this.#codeModeToolResults(event)
|
|
85
|
+
];
|
|
83
86
|
}
|
|
84
87
|
if (event.type === 'todo.updated')
|
|
85
88
|
return [
|
|
@@ -351,6 +354,14 @@ export class MarAgentConversationNormalizer {
|
|
|
351
354
|
return [];
|
|
352
355
|
}
|
|
353
356
|
}
|
|
357
|
+
function permissionStopForToolEvent(event) {
|
|
358
|
+
const direct = record(event)?.permissionStop;
|
|
359
|
+
const cell = record(record(event)?.data)?.permissionStop;
|
|
360
|
+
return (direct ?? cell);
|
|
361
|
+
}
|
|
362
|
+
function withPermissionStop(items, permissionStop) {
|
|
363
|
+
return permissionStop === undefined ? items : items.map((item) => ({ ...item, permissionStop }));
|
|
364
|
+
}
|
|
354
365
|
function codeModeToolResults(event) {
|
|
355
366
|
if ((event.type !== 'tool.completed' && event.type !== 'tool.failed') ||
|
|
356
367
|
(event.toolName !== 'code_exec' && event.toolName !== 'code_exec_wait'))
|
|
@@ -76,7 +76,9 @@ export function projectMarAgentHistory(session, entries, options = {}) {
|
|
|
76
76
|
turns.set(event.turnId, {
|
|
77
77
|
...turn,
|
|
78
78
|
status: event.type === 'execution.completed'
|
|
79
|
-
?
|
|
79
|
+
? event.permissionStop === undefined
|
|
80
|
+
? 'SUCCEEDED'
|
|
81
|
+
: 'INTERRUPTED'
|
|
80
82
|
: event.type === 'execution.failed'
|
|
81
83
|
? 'FAILED'
|
|
82
84
|
: 'INTERRUPTED',
|
|
@@ -90,12 +92,12 @@ export function projectMarAgentHistory(session, entries, options = {}) {
|
|
|
90
92
|
const attachments = projected.generatedImages === undefined
|
|
91
93
|
? []
|
|
92
94
|
: (options.registerGeneratedImages?.(event.sessionId, projected.generatedImages) ?? []);
|
|
93
|
-
replace(projectionTurn, historyItem(session, projectionTurn, projected.itemId, projected.kind, projected.status, attachments.length === 0 ? projected.payload : { ...projected.payload, attachments }, at));
|
|
95
|
+
replace(projectionTurn, historyItem(session, projectionTurn, projected.itemId, projected.kind, projected.status, attachments.length === 0 ? projected.payload : { ...projected.payload, attachments }, at, projected.permissionStop));
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
return [...turns.values()];
|
|
97
99
|
}
|
|
98
|
-
function historyItem(session, turn, id, kind, status, payload, at) {
|
|
100
|
+
function historyItem(session, turn, id, kind, status, payload, at, permissionStop) {
|
|
99
101
|
return {
|
|
100
102
|
id,
|
|
101
103
|
sessionId: session.id,
|
|
@@ -107,6 +109,7 @@ function historyItem(session, turn, id, kind, status, payload, at) {
|
|
|
107
109
|
kind,
|
|
108
110
|
status,
|
|
109
111
|
payload,
|
|
112
|
+
...(permissionStop === undefined ? {} : { permissionStop }),
|
|
110
113
|
startedAt: at,
|
|
111
114
|
completedAt: status === 'IN_PROGRESS' ? null : at
|
|
112
115
|
};
|
|
@@ -29,6 +29,7 @@ export class MarAgentManagedRunController {
|
|
|
29
29
|
#runtimeCreations = new Map();
|
|
30
30
|
#sessionReleases = new Map();
|
|
31
31
|
#questions = new Map();
|
|
32
|
+
#approvals = new Map();
|
|
32
33
|
#normalizers = new Map();
|
|
33
34
|
#resourceIdleTtlMs;
|
|
34
35
|
#retainEnvironment;
|
|
@@ -202,6 +203,7 @@ export class MarAgentManagedRunController {
|
|
|
202
203
|
? { runtimeScopeId: payload.marAgentRuntimeScopeId }
|
|
203
204
|
: {}),
|
|
204
205
|
effort: typeof payload.effort === 'string' ? payload.effort : '',
|
|
206
|
+
...(payload.access === undefined ? {} : { access: payload.access }),
|
|
205
207
|
mode: payload.collaborationMode === 'plan' ? 'plan' : 'run',
|
|
206
208
|
mcpServers,
|
|
207
209
|
images: images.map((image) => ({
|
|
@@ -414,6 +416,9 @@ export class MarAgentManagedRunController {
|
|
|
414
416
|
...(input.clientMessageId ? { clientMessageId: input.clientMessageId } : {}),
|
|
415
417
|
modelId: input.model.id,
|
|
416
418
|
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
419
|
+
...(input.mode === 'compact' || input.access === undefined || input.access === null
|
|
420
|
+
? {}
|
|
421
|
+
: { permissionMode: input.access }),
|
|
417
422
|
images: input.images
|
|
418
423
|
};
|
|
419
424
|
const metadata = input.mode === 'compact' || !runtime.automaticTitleEnabled
|
|
@@ -475,20 +480,34 @@ export class MarAgentManagedRunController {
|
|
|
475
480
|
});
|
|
476
481
|
});
|
|
477
482
|
await this.host.syncTitle(session.id, input.cwd);
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
+
const permissionStop = result.status === 'completed' ? result.permissionStop : undefined;
|
|
484
|
+
const status = permissionStop !== undefined
|
|
485
|
+
? 'INTERRUPTED'
|
|
486
|
+
: result.status === 'completed'
|
|
487
|
+
? 'SUCCEEDED'
|
|
488
|
+
: result.status === 'cancelled'
|
|
489
|
+
? 'CANCELLED'
|
|
490
|
+
: 'FAILED';
|
|
483
491
|
this.#prepareRuntimeForTerminal(input.runId, runtime);
|
|
484
492
|
this.host.emit(input.runId, status === 'SUCCEEDED'
|
|
485
493
|
? 'run.completed'
|
|
486
494
|
: status === 'CANCELLED'
|
|
487
495
|
? 'run.cancelled'
|
|
488
|
-
: '
|
|
496
|
+
: status === 'FAILED'
|
|
497
|
+
? 'run.failed'
|
|
498
|
+
: 'run.completed', {
|
|
489
499
|
status,
|
|
490
500
|
...(result.errorCode ? { code: result.errorCode } : {}),
|
|
491
|
-
...(result.errorMessage ? { message: result.errorMessage } : {})
|
|
501
|
+
...(result.errorMessage ? { message: result.errorMessage } : {}),
|
|
502
|
+
...(permissionStop === undefined
|
|
503
|
+
? {}
|
|
504
|
+
: {
|
|
505
|
+
permissionStop,
|
|
506
|
+
code: permissionStop.action === 'ask'
|
|
507
|
+
? 'MAR_AGENT_PERMISSION_ASK'
|
|
508
|
+
: 'MAR_AGENT_PERMISSION_DENIED',
|
|
509
|
+
message: permissionStop.reason
|
|
510
|
+
})
|
|
492
511
|
}, status);
|
|
493
512
|
}
|
|
494
513
|
catch (error) {
|
|
@@ -656,6 +675,14 @@ export class MarAgentManagedRunController {
|
|
|
656
675
|
const runId = runtime?.activeRunId;
|
|
657
676
|
return runId === undefined ? {} : this.requestQuestion(runId, request, signal);
|
|
658
677
|
},
|
|
678
|
+
approval: this.host.createApproval && this.host.publishApproval
|
|
679
|
+
? (request, signal) => {
|
|
680
|
+
const runId = runtime?.activeRunId;
|
|
681
|
+
if (runId === undefined)
|
|
682
|
+
return Promise.resolve('REJECTED');
|
|
683
|
+
return this.requestApproval(runId, request, signal);
|
|
684
|
+
}
|
|
685
|
+
: undefined,
|
|
659
686
|
mcpCatalog: () => mcp.catalog(),
|
|
660
687
|
callMcp: async (call, signal, context) => mcp.call(call.name, call.arguments, signal, context?.expectedTool),
|
|
661
688
|
resourceIdleTtlMs: this.#resourceIdleTtlMs
|
|
@@ -829,7 +856,62 @@ export class MarAgentManagedRunController {
|
|
|
829
856
|
cancel();
|
|
830
857
|
});
|
|
831
858
|
}
|
|
859
|
+
decideApproval(input) {
|
|
860
|
+
const pending = this.#approvals.get(input.approvalId);
|
|
861
|
+
if (!pending || pending.runId !== input.runId)
|
|
862
|
+
return false;
|
|
863
|
+
pending.cleanup();
|
|
864
|
+
pending.resolve(input.decision);
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
requestApproval(runId, request, signal) {
|
|
868
|
+
signal.throwIfAborted();
|
|
869
|
+
return new Promise((resolve, reject) => {
|
|
870
|
+
const cleanup = () => {
|
|
871
|
+
signal.removeEventListener('abort', cancel);
|
|
872
|
+
this.#approvals.delete(request.approvalId);
|
|
873
|
+
};
|
|
874
|
+
const cancel = () => {
|
|
875
|
+
cleanup();
|
|
876
|
+
if (signal.reason instanceof Error &&
|
|
877
|
+
'code' in signal.reason &&
|
|
878
|
+
signal.reason.code === 'MAR_AGENT_APPROVAL_EXPIRED')
|
|
879
|
+
this.host.emit(runId, 'approval.expired', { approvalId: request.approvalId });
|
|
880
|
+
reject(signal.reason);
|
|
881
|
+
};
|
|
882
|
+
const input = {
|
|
883
|
+
runId,
|
|
884
|
+
approvalId: request.approvalId,
|
|
885
|
+
expiresAt: request.expiresAt,
|
|
886
|
+
payload: {
|
|
887
|
+
toolName: request.source.toolName,
|
|
888
|
+
input: request.input,
|
|
889
|
+
reason: request.reason,
|
|
890
|
+
source: request.source
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
try {
|
|
894
|
+
this.host.createApproval(input);
|
|
895
|
+
this.#approvals.set(request.approvalId, { runId, resolve, cleanup });
|
|
896
|
+
signal.addEventListener('abort', cancel, { once: true });
|
|
897
|
+
if (signal.aborted)
|
|
898
|
+
cancel();
|
|
899
|
+
else
|
|
900
|
+
this.host.publishApproval(input);
|
|
901
|
+
}
|
|
902
|
+
catch (error) {
|
|
903
|
+
cleanup();
|
|
904
|
+
reject(error);
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
}
|
|
832
908
|
clearInteractions(runId) {
|
|
909
|
+
for (const pending of this.#approvals.values()) {
|
|
910
|
+
if (pending.runId !== runId)
|
|
911
|
+
continue;
|
|
912
|
+
pending.cleanup();
|
|
913
|
+
pending.resolve('REJECTED');
|
|
914
|
+
}
|
|
833
915
|
for (const [requestId, pending] of this.#questions) {
|
|
834
916
|
if (pending.runId !== runId)
|
|
835
917
|
continue;
|
|
@@ -869,6 +951,7 @@ export class MarAgentManagedRunController {
|
|
|
869
951
|
kind: item.kind,
|
|
870
952
|
status: item.status,
|
|
871
953
|
payload: attachments.length === 0 ? item.payload : { ...item.payload, attachments },
|
|
954
|
+
...(item.permissionStop === undefined ? {} : { permissionStop: item.permissionStop }),
|
|
872
955
|
...(item.merge === undefined ? {} : { merge: item.merge })
|
|
873
956
|
});
|
|
874
957
|
}
|
|
@@ -106,6 +106,11 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
106
106
|
...(index === 0 ? { isDefault: true } : {})
|
|
107
107
|
})),
|
|
108
108
|
accessOptions: [
|
|
109
|
+
{
|
|
110
|
+
id: 'auto',
|
|
111
|
+
label: 'Auto',
|
|
112
|
+
description: 'MAR Agent may stop before a tool call and request confirmation. Host validation remains active and no Agent sandbox is provided.'
|
|
113
|
+
},
|
|
109
114
|
{
|
|
110
115
|
id: 'full-access',
|
|
111
116
|
label: 'Full access',
|
|
@@ -188,7 +193,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
188
193
|
runner: 'mar-agent',
|
|
189
194
|
model: model?.id ?? '',
|
|
190
195
|
effort: model?.defaultEffort ?? '',
|
|
191
|
-
access: '
|
|
196
|
+
access: 'auto'
|
|
192
197
|
};
|
|
193
198
|
}
|
|
194
199
|
presentSession(session, capabilities, context) {
|
package/dist/runtime-state.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { permissionStopSchema } from '@myagentroam/protocol';
|
|
2
3
|
/**
|
|
3
4
|
* Deliberately process-local runtime domain. No method in this class writes
|
|
4
5
|
* into Node SQLite: a Node restart loses active runs, approvals, leases and
|
|
@@ -1329,6 +1330,9 @@ export class NodeRuntimeState {
|
|
|
1329
1330
|
kind: projected.kind,
|
|
1330
1331
|
status,
|
|
1331
1332
|
payload: mergeConversationItemPayload(existing?.payload, projected),
|
|
1333
|
+
...(projected.permissionStop === undefined
|
|
1334
|
+
? {}
|
|
1335
|
+
: { permissionStop: projected.permissionStop }),
|
|
1332
1336
|
startedAt: existing?.startedAt ?? event.createdAt,
|
|
1333
1337
|
completedAt: completedConversationItem(status) ? event.createdAt : null
|
|
1334
1338
|
};
|
|
@@ -1442,11 +1446,13 @@ function conversationItemFromEvent(event) {
|
|
|
1442
1446
|
!isRecord(payload))
|
|
1443
1447
|
return undefined;
|
|
1444
1448
|
const append = event.payload.append;
|
|
1449
|
+
const permissionStop = permissionStopSchema.safeParse(event.payload.permissionStop);
|
|
1445
1450
|
return {
|
|
1446
1451
|
itemId,
|
|
1447
1452
|
kind,
|
|
1448
1453
|
status,
|
|
1449
1454
|
payload,
|
|
1455
|
+
...(permissionStop.success ? { permissionStop: permissionStop.data } : {}),
|
|
1450
1456
|
merge: event.payload.merge === true,
|
|
1451
1457
|
...(append === 'sections' || append === 'outputPreview' || append === 'outputSummary'
|
|
1452
1458
|
? { append }
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { parseComposerTags, serializeComposerTag } from '@myagentroam/protocol';
|
|
3
|
-
export function goalUnrestrictedAccess(runner) {
|
|
4
|
-
if (runner === '
|
|
3
|
+
export function goalUnrestrictedAccess(runner, currentAccess) {
|
|
4
|
+
if (runner === 'mar-agent')
|
|
5
|
+
return currentAccess ?? 'full-access';
|
|
6
|
+
if (runner === 'codex')
|
|
5
7
|
return 'full-access';
|
|
6
8
|
if (runner === 'claude-code')
|
|
7
9
|
return 'bypass';
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { createEnvelope, mailNotificationRequestSchema, mailNotificationResponseSchema } from '@myagentroam/protocol';
|
|
1
|
+
import { createEnvelope, notificationChannelsRequestSchema, notificationChannelsResponseSchema, mailNotificationRequestSchema, mailNotificationResponseSchema } from '@myagentroam/protocol';
|
|
2
2
|
export class MailNotificationControl {
|
|
3
3
|
sendEnvelope;
|
|
4
4
|
pending = new Map();
|
|
5
5
|
generation = 0;
|
|
6
6
|
connectedToServer = false;
|
|
7
7
|
closed = false;
|
|
8
|
+
channelsAvailable = false;
|
|
9
|
+
supportsChannels() {
|
|
10
|
+
return this.channelsAvailable && this.connectedToServer && !this.closed;
|
|
11
|
+
}
|
|
12
|
+
setFeatures(payload) {
|
|
13
|
+
const features = payload;
|
|
14
|
+
this.channelsAvailable = features?.notificationChannels?.apiVersion === 1;
|
|
15
|
+
}
|
|
8
16
|
constructor(sendEnvelope) {
|
|
9
17
|
this.sendEnvelope = sendEnvelope;
|
|
10
18
|
}
|
|
@@ -14,6 +22,7 @@ export class MailNotificationControl {
|
|
|
14
22
|
}
|
|
15
23
|
disconnected() {
|
|
16
24
|
this.connectedToServer = false;
|
|
25
|
+
this.channelsAvailable = false;
|
|
17
26
|
this.generation++;
|
|
18
27
|
for (const pending of [...this.pending.values()])
|
|
19
28
|
pending.reject(new Error('MAIL_DELIVERY_UNCERTAIN'));
|
|
@@ -23,11 +32,23 @@ export class MailNotificationControl {
|
|
|
23
32
|
this.disconnected();
|
|
24
33
|
}
|
|
25
34
|
handle(envelope) {
|
|
26
|
-
if (
|
|
35
|
+
if (!['mail.notification.response', 'notification.channels.response'].includes(envelope.type))
|
|
27
36
|
return false;
|
|
28
37
|
const pending = envelope.replyTo === undefined ? undefined : this.pending.get(envelope.replyTo);
|
|
29
|
-
if (pending === undefined ||
|
|
38
|
+
if (pending === undefined ||
|
|
39
|
+
pending.generation !== this.generation ||
|
|
40
|
+
pending.channel !== envelope.type)
|
|
41
|
+
return true;
|
|
42
|
+
if (envelope.type === 'notification.channels.response') {
|
|
43
|
+
const result = notificationChannelsResponseSchema.safeParse(envelope.payload);
|
|
44
|
+
if (!result.success)
|
|
45
|
+
pending.reject(new Error('NOTIFICATION_RESPONSE_INVALID'));
|
|
46
|
+
else if ('error' in result.data)
|
|
47
|
+
pending.reject(new Error(result.data.error.code));
|
|
48
|
+
else
|
|
49
|
+
pending.resolve(result.data);
|
|
30
50
|
return true;
|
|
51
|
+
}
|
|
31
52
|
const parsed = mailNotificationResponseSchema.safeParse(envelope.payload);
|
|
32
53
|
if (!parsed.success)
|
|
33
54
|
pending.reject(new Error('MAIL_RESPONSE_INVALID'));
|
|
@@ -38,9 +59,22 @@ export class MailNotificationControl {
|
|
|
38
59
|
return true;
|
|
39
60
|
}
|
|
40
61
|
request(input) {
|
|
62
|
+
return this.requestEnvelope(createEnvelope('mail.notification.send', mailNotificationRequestSchema.parse(input))).then(() => undefined);
|
|
63
|
+
}
|
|
64
|
+
notification(input) {
|
|
65
|
+
if (!this.supportsChannels())
|
|
66
|
+
return Promise.reject(new Error('NOTIFICATION_UNSUPPORTED'));
|
|
67
|
+
return this.requestEnvelope(createEnvelope('notification.channels.send', notificationChannelsRequestSchema.parse(input))).then((result) => {
|
|
68
|
+
if (result === undefined)
|
|
69
|
+
throw new Error('NOTIFICATION_RESPONSE_INVALID');
|
|
70
|
+
if (result.callId !== input.callId)
|
|
71
|
+
throw new Error('NOTIFICATION_RESPONSE_INVALID');
|
|
72
|
+
return result;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
requestEnvelope(envelope) {
|
|
41
76
|
if (!this.connectedToServer || this.closed)
|
|
42
77
|
return Promise.reject(new Error('MAIL_SERVER_UNAVAILABLE'));
|
|
43
|
-
const envelope = createEnvelope('mail.notification.send', mailNotificationRequestSchema.parse(input));
|
|
44
78
|
return new Promise((resolve, reject) => {
|
|
45
79
|
const settle = (action) => {
|
|
46
80
|
const pending = this.pending.get(envelope.id);
|
|
@@ -54,7 +88,10 @@ export class MailNotificationControl {
|
|
|
54
88
|
timer.unref();
|
|
55
89
|
this.pending.set(envelope.id, {
|
|
56
90
|
generation: this.generation,
|
|
57
|
-
|
|
91
|
+
channel: envelope.type === 'mail.notification.send'
|
|
92
|
+
? 'mail.notification.response'
|
|
93
|
+
: 'notification.channels.response',
|
|
94
|
+
resolve: (result) => settle(() => resolve(result)),
|
|
58
95
|
reject: (error) => settle(() => reject(error)),
|
|
59
96
|
timer
|
|
60
97
|
});
|
|
@@ -5,19 +5,21 @@ import { createServer } from 'node:net';
|
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { mailNotificationRequestSchema } from '@myagentroam/protocol';
|
|
8
|
+
import { notificationInputSchema, notificationChannelsRequestSchema, mailNotificationRequestSchema } from '@myagentroam/protocol';
|
|
9
9
|
export class MailNotificationMcpServer {
|
|
10
10
|
resolve;
|
|
11
11
|
send;
|
|
12
|
+
channels;
|
|
12
13
|
capabilities = new Map();
|
|
13
14
|
sockets = new Set();
|
|
14
15
|
endpoint = process.platform === 'win32'
|
|
15
16
|
? `\\\\.\\pipe\\mar-mail-${process.pid}-${randomUUID()}`
|
|
16
17
|
: join(tmpdir(), `mar-mail-${process.pid}-${randomUUID()}.sock`);
|
|
17
18
|
listening;
|
|
18
|
-
constructor(resolve, send) {
|
|
19
|
+
constructor(resolve, send, channels) {
|
|
19
20
|
this.resolve = resolve;
|
|
20
21
|
this.send = send;
|
|
22
|
+
this.channels = channels;
|
|
21
23
|
}
|
|
22
24
|
async installation(capability) {
|
|
23
25
|
await this.listen();
|
|
@@ -27,6 +29,7 @@ export class MailNotificationMcpServer {
|
|
|
27
29
|
return {
|
|
28
30
|
installation: {
|
|
29
31
|
__marMail: true,
|
|
32
|
+
notificationChannels: this.channels?.supported() === true,
|
|
30
33
|
serverName: 'mar_notification',
|
|
31
34
|
enabled: true,
|
|
32
35
|
command: process.execPath,
|
|
@@ -94,6 +97,32 @@ export class MailNotificationMcpServer {
|
|
|
94
97
|
const current = this.resolve(capability);
|
|
95
98
|
if (current === undefined)
|
|
96
99
|
throw new Error('MAIL_CAPABILITY_STALE');
|
|
100
|
+
if (request['tool'] === 'mar_send_notification') {
|
|
101
|
+
if (this.channels?.supported() !== true)
|
|
102
|
+
throw new Error('NOTIFICATION_UNSUPPORTED');
|
|
103
|
+
const args = request['arguments'];
|
|
104
|
+
if (args === null || typeof args !== 'object' || Array.isArray(args))
|
|
105
|
+
throw new Error('NOTIFICATION_INPUT_INVALID');
|
|
106
|
+
const { callId, ...input } = args;
|
|
107
|
+
const parsed = notificationInputSchema.safeParse(input);
|
|
108
|
+
if (!parsed.success)
|
|
109
|
+
throw new Error('NOTIFICATION_INPUT_INVALID');
|
|
110
|
+
const value = notificationChannelsRequestSchema.safeParse({
|
|
111
|
+
...parsed.data,
|
|
112
|
+
...current,
|
|
113
|
+
callId,
|
|
114
|
+
apiVersion: 1,
|
|
115
|
+
eventType: 'notification',
|
|
116
|
+
kind: 'AGENT_MESSAGE',
|
|
117
|
+
occurredAt: Date.now(),
|
|
118
|
+
sessionTitle: current.sessionId
|
|
119
|
+
});
|
|
120
|
+
if (!value.success)
|
|
121
|
+
throw new Error('NOTIFICATION_INPUT_INVALID');
|
|
122
|
+
const result = await this.channels.send(value.data);
|
|
123
|
+
socket.end(`${JSON.stringify({ result })}\n`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
97
126
|
if (request['tool'] !== 'mar_send_email_notification')
|
|
98
127
|
throw new Error('MAIL_TOOL_INVALID');
|
|
99
128
|
const args = request['arguments'];
|
|
@@ -121,7 +150,7 @@ export class MailNotificationMcpServer {
|
|
|
121
150
|
socket.end(`${JSON.stringify({ result: { accepted: true } })}\n`);
|
|
122
151
|
}
|
|
123
152
|
catch (error) {
|
|
124
|
-
const code = error instanceof Error && /^
|
|
153
|
+
const code = error instanceof Error && /^(MAIL|NOTIFICATION)_[A-Z0-9_]+$/u.test(error.message)
|
|
125
154
|
? error.message
|
|
126
155
|
: 'MAIL_DELIVERY_UNCERTAIN';
|
|
127
156
|
socket.end(`${JSON.stringify({ error: code })}\n`);
|
|
@@ -3,6 +3,7 @@ import { connect } from 'node:net';
|
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
+
import { notificationInputSchema } from '@myagentroam/protocol';
|
|
6
7
|
const endpoint = process.env['MAR_MAIL_MCP_ENDPOINT'];
|
|
7
8
|
const token = process.env['MAR_MAIL_MCP_TOKEN'];
|
|
8
9
|
if (endpoint === undefined || token === undefined)
|
|
@@ -29,13 +30,38 @@ server.registerTool('mar_send_email_notification', {
|
|
|
29
30
|
const value = await request({ ...args, callId: randomUUID() });
|
|
30
31
|
return { content: [{ type: 'text', text: JSON.stringify(value) }] };
|
|
31
32
|
});
|
|
33
|
+
if (process.env['MAR_NOTIFICATION_CHANNELS'] === '1') {
|
|
34
|
+
server.registerTool('mar_send_notification', {
|
|
35
|
+
description: 'Send a real notification from the current authorized Workspace execution. Defaults to push to the initiating user; email requires an explicitly authorized recipientEmail. Channels and registered Push clients are independent. Push may be suppressed while any workbench connection is open or unavailable due to system, browser, or OS settings. Acceptance is not delivery. Do not disclose secrets or send to unauthorized recipients. Each invocation is a new side effect; do not retry after an uncertain or partial result.',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
channels: z
|
|
38
|
+
.array(z.enum(['push', 'email']))
|
|
39
|
+
.min(1)
|
|
40
|
+
.max(2)
|
|
41
|
+
.optional(),
|
|
42
|
+
title: z
|
|
43
|
+
.string()
|
|
44
|
+
.max(160)
|
|
45
|
+
.regex(/^[^\r\n]*$/u)
|
|
46
|
+
.optional(),
|
|
47
|
+
message: z.string().min(1).max(4000),
|
|
48
|
+
recipientEmail: z.string().email().max(320).optional(),
|
|
49
|
+
templateId: z.enum(['', 'notification-en-US', 'notification-zh-CN']).optional()
|
|
50
|
+
}
|
|
51
|
+
}, async (args) => {
|
|
52
|
+
const input = notificationInputSchema.parse(args);
|
|
53
|
+
const value = await request({ ...input, callId: randomUUID() }, 'mar_send_notification');
|
|
54
|
+
return { content: [{ type: 'text', text: JSON.stringify(value) }] };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
32
57
|
await server.connect(new StdioServerTransport());
|
|
33
|
-
function request(args) {
|
|
58
|
+
function request(args, tool = 'mar_send_email_notification') {
|
|
34
59
|
return new Promise((resolve, reject) => {
|
|
35
60
|
const socket = connect(endpoint);
|
|
61
|
+
socket.setTimeout(35_000, () => socket.destroy(new Error('MAIL_DELIVERY_UNCERTAIN')));
|
|
36
62
|
let body = '';
|
|
37
63
|
socket.setEncoding('utf8');
|
|
38
|
-
socket.once('connect', () => socket.write(`${JSON.stringify({ token, tool
|
|
64
|
+
socket.once('connect', () => socket.write(`${JSON.stringify({ token, tool, arguments: args })}\n`));
|
|
39
65
|
socket.on('data', (chunk) => {
|
|
40
66
|
body += chunk;
|
|
41
67
|
if (body.length > 16_000)
|
|
@@ -130,9 +130,13 @@ export class NativeSessionProjectionService {
|
|
|
130
130
|
effort: typeof session.effort === 'string' ? session.effort : fallback.effort,
|
|
131
131
|
access: typeof session.access === 'string' && session.access.trim().length > 0
|
|
132
132
|
? session.access
|
|
133
|
-
:
|
|
133
|
+
: session.runner === 'mar-agent'
|
|
134
|
+
? 'full-access'
|
|
135
|
+
: fallback.access
|
|
134
136
|
};
|
|
135
|
-
return session.runner === 'mar-agent'
|
|
137
|
+
return session.runner === 'mar-agent' &&
|
|
138
|
+
configuration.access !== 'auto' &&
|
|
139
|
+
configuration.access !== 'full-access'
|
|
136
140
|
? { ...configuration, access: 'full-access' }
|
|
137
141
|
: configuration;
|
|
138
142
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runPerformanceSchema } from '@myagentroam/protocol';
|
|
1
|
+
import { permissionStopSchema, runPerformanceSchema } from '@myagentroam/protocol';
|
|
2
2
|
import { nodeLog } from '../operational.js';
|
|
3
3
|
import { isPlainRecord, isTerminalRunStatus } from '../util/node-operation-parsers.js';
|
|
4
4
|
const COALESCE_MS = 200;
|
|
@@ -122,13 +122,20 @@ export class RunEventService {
|
|
|
122
122
|
let finalStatus;
|
|
123
123
|
let manuallyInterrupted = false;
|
|
124
124
|
if (terminalCurrentExecution) {
|
|
125
|
-
|
|
125
|
+
const parsedStop = isPlainRecord(payload)
|
|
126
|
+
? permissionStopSchema.safeParse(payload.permissionStop)
|
|
127
|
+
: undefined;
|
|
128
|
+
const permissionStop = parsedStop?.success ? parsedStop.data : undefined;
|
|
129
|
+
if (permissionStop !== undefined)
|
|
130
|
+
this.options.runtime.pauseSessionQueue(run.sessionId);
|
|
131
|
+
this.options.executionTerminating?.(executionId, status, permissionStop);
|
|
126
132
|
this.options.state.active.delete(executionId);
|
|
127
133
|
const interrupted = this.options.state.interruptRequested.delete(executionId);
|
|
128
134
|
manuallyInterrupted = interrupted;
|
|
129
135
|
const queue = this.options.runtime.sessionQueue(run.sessionId);
|
|
130
|
-
const continues =
|
|
131
|
-
|
|
136
|
+
const continues = permissionStop === undefined &&
|
|
137
|
+
((!queue.paused && queue.itemIds.length > 0) ||
|
|
138
|
+
this.options.keepRunActive?.(run.sessionId) === true);
|
|
132
139
|
if (!continues) {
|
|
133
140
|
finalStatus =
|
|
134
141
|
status === 'CANCELLED' || interrupted ? 'INTERRUPTED' : status;
|
|
@@ -52,7 +52,9 @@ export class RunnerService {
|
|
|
52
52
|
const defaults = this.database()
|
|
53
53
|
.getRunnerDefaults()
|
|
54
54
|
.map((configuration) => {
|
|
55
|
-
if (configuration.runner !== 'mar-agent' ||
|
|
55
|
+
if (configuration.runner !== 'mar-agent' ||
|
|
56
|
+
configuration.access === 'auto' ||
|
|
57
|
+
configuration.access === 'full-access')
|
|
56
58
|
return configuration;
|
|
57
59
|
const normalized = {
|
|
58
60
|
...configuration,
|
|
@@ -80,7 +82,9 @@ export class RunnerService {
|
|
|
80
82
|
const parsed = runnerDefaultConfigurationSchema.safeParse(data);
|
|
81
83
|
if (!parsed.success)
|
|
82
84
|
throw new Error('RUNNER_DEFAULT_INVALID');
|
|
83
|
-
if (parsed.data.runner === 'mar-agent' &&
|
|
85
|
+
if (parsed.data.runner === 'mar-agent' &&
|
|
86
|
+
parsed.data.access !== 'auto' &&
|
|
87
|
+
parsed.data.access !== 'full-access')
|
|
84
88
|
throw new Error('RUNNER_DEFAULT_INVALID');
|
|
85
89
|
return { defaults: this.database().saveRunnerDefaults(parsed.data) };
|
|
86
90
|
}
|
|
@@ -95,7 +95,7 @@ export class SessionLifecycleService {
|
|
|
95
95
|
? fallback.access
|
|
96
96
|
: (saved?.access ?? fallback.access)
|
|
97
97
|
};
|
|
98
|
-
if (runner.name === 'mar-agent' &&
|
|
98
|
+
if (runner.name === 'mar-agent' && !['auto', 'full-access'].includes(configuration.access))
|
|
99
99
|
throw new Error('SESSION_INVALID');
|
|
100
100
|
const session = this.options.runtime.createAgentSession({
|
|
101
101
|
workspaceId: input.workspaceId,
|
|
@@ -129,7 +129,7 @@ export class SessionLifecycleService {
|
|
|
129
129
|
(await this.options.projection.createMetadataProjection(input.sessionId));
|
|
130
130
|
if (current === undefined)
|
|
131
131
|
throw new Error('SESSION_NOT_FOUND');
|
|
132
|
-
if (current.runner === 'mar-agent' &&
|
|
132
|
+
if (current.runner === 'mar-agent' && !['auto', 'full-access'].includes(input.access))
|
|
133
133
|
throw new Error('SESSION_INVALID');
|
|
134
134
|
const session = this.options.runtime.updateAgentSessionConfiguration({
|
|
135
135
|
sessionId: current.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.26",
|
|
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.10.
|
|
40
|
+
"@myagentroam/agent": "0.10.26",
|
|
41
41
|
"@myagentroam/code-navigation": "0.10.1",
|
|
42
|
-
"@myagentroam/
|
|
43
|
-
"@myagentroam/
|
|
42
|
+
"@myagentroam/protocol": "0.10.26",
|
|
43
|
+
"@myagentroam/marctl": "0.10.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/ws": "^8.18.1"
|