@myagentroam/node 0.12.4 → 0.12.6
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/application/scheduled-execution-service.js +60 -0
- package/dist/connector.js +207 -261
- package/dist/marctl/package.json +1 -1
- package/dist/runner/claude-code-runner.js +4 -0
- package/dist/runner/codex-runner.js +11 -4
- package/dist/runner/mar-agent/agent-network-extension.js +2 -1
- package/dist/runner/opencode/opencode-server.js +37 -12
- package/dist/runner/opencode-runner.js +21 -7
- package/dist/service/agent-network-run-projection.js +60 -0
- package/dist/service/composer-message-input.js +1 -1
- package/dist/service/session-recent-activity.js +3 -3
- package/dist/service/skill-directory-service.js +10 -31
- package/dist/service/skill-node-operation-service.js +3 -5
- package/dist/session/native-session-projection-service.js +41 -6
- package/dist/session/session-catalog-service.js +11 -0
- package/dist/workspace/workspace-session-service.js +58 -8
- package/package.json +4 -4
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { nodeLog } from '../operational.js';
|
|
2
2
|
const WATCH_INTERVAL_MS = 30_000;
|
|
3
3
|
const CACHE_TTL_MS = 15_000;
|
|
4
|
+
/** Owns native discovery caches, periodic observation and internal page/discovery admission.
|
|
5
|
+
* Cancels owned discovery generations before joining reads; Runner execution remains with the Runner.
|
|
6
|
+
*/
|
|
4
7
|
export class WorkspaceSessionService {
|
|
5
8
|
options;
|
|
9
|
+
accepting = true;
|
|
10
|
+
pageReads = new Set();
|
|
11
|
+
discoveryReads = new Map();
|
|
6
12
|
cacheGenerations = new Map();
|
|
7
13
|
initialReads = new Map();
|
|
8
14
|
incompleteDiscoveries = new Set();
|
|
@@ -15,10 +21,37 @@ export class WorkspaceSessionService {
|
|
|
15
21
|
constructor(options) {
|
|
16
22
|
this.options = options;
|
|
17
23
|
}
|
|
24
|
+
async listPage(workspaceId, sessionGroup) {
|
|
25
|
+
if (!this.accepting)
|
|
26
|
+
throw new Error('NODE_STOPPED');
|
|
27
|
+
const read = this.options.listPage(workspaceId, sessionGroup);
|
|
28
|
+
this.pageReads.add(read);
|
|
29
|
+
try {
|
|
30
|
+
return await read;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
this.pageReads.delete(read);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
resume() {
|
|
37
|
+
this.accepting = true;
|
|
38
|
+
}
|
|
39
|
+
async stop() {
|
|
40
|
+
this.accepting = false;
|
|
41
|
+
for (const controller of this.discoveryReads.values())
|
|
42
|
+
controller.abort(new Error('NODE_STOPPED'));
|
|
43
|
+
for (const id of this.sessionWatchTimers.keys())
|
|
44
|
+
this.stopWatch(id);
|
|
45
|
+
await Promise.allSettled([...this.pageReads, ...this.sessionWatchReads.values()]);
|
|
46
|
+
// Retired cache generations also remain owned until their cancellable projections settle.
|
|
47
|
+
await Promise.allSettled([...this.discoveryReads.keys()]);
|
|
48
|
+
}
|
|
18
49
|
cachedSnapshot(workspaceId) {
|
|
19
50
|
return this.sessionCache.get(workspaceId)?.sessions ?? [];
|
|
20
51
|
}
|
|
21
52
|
ensureWatch(workspaceId) {
|
|
53
|
+
if (!this.accepting)
|
|
54
|
+
return;
|
|
22
55
|
if (this.sessionWatchTimers.has(workspaceId) || this.sessionWatchReads.has(workspaceId))
|
|
23
56
|
return;
|
|
24
57
|
void this.runWatchCycle(workspaceId);
|
|
@@ -35,11 +68,15 @@ export class WorkspaceSessionService {
|
|
|
35
68
|
return this.incompleteDiscoveries.has(workspaceId);
|
|
36
69
|
}
|
|
37
70
|
async cached(workspaceId) {
|
|
71
|
+
if (!this.accepting)
|
|
72
|
+
throw new Error('NODE_STOPPED');
|
|
38
73
|
const cached = this.sessionCache.get(workspaceId);
|
|
39
74
|
if (cached !== undefined && cached.expiresAt > Date.now())
|
|
40
75
|
return cached.sessions;
|
|
41
76
|
let discovery = this.sessionCacheReads.get(workspaceId);
|
|
42
77
|
if (discovery === undefined) {
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
const signal = controller.signal;
|
|
43
80
|
// A retired read must not regain ownership when its Workspace state is reclaimed.
|
|
44
81
|
const generation = Symbol();
|
|
45
82
|
this.cacheGenerations.set(workspaceId, generation);
|
|
@@ -59,6 +96,8 @@ export class WorkspaceSessionService {
|
|
|
59
96
|
let batchPublished = false;
|
|
60
97
|
let notifyCompletion = false;
|
|
61
98
|
const publish = (sessions, notify = true) => {
|
|
99
|
+
if (signal.aborted)
|
|
100
|
+
return;
|
|
62
101
|
if (this.cacheGenerations.get(workspaceId) !== generation)
|
|
63
102
|
return;
|
|
64
103
|
const previous = this.sessionCache.get(workspaceId)?.sessions;
|
|
@@ -66,13 +105,16 @@ export class WorkspaceSessionService {
|
|
|
66
105
|
sessions,
|
|
67
106
|
expiresAt: this.staleDiscoveries.has(workspaceId) ? 0 : Date.now() + CACHE_TTL_MS
|
|
68
107
|
});
|
|
69
|
-
if (
|
|
108
|
+
if (this.accepting &&
|
|
109
|
+
notify &&
|
|
70
110
|
previous !== undefined &&
|
|
71
111
|
sessionSignature(previous) !== sessionSignature(sessions))
|
|
72
112
|
this.options.invalidate(workspaceId);
|
|
73
113
|
};
|
|
74
114
|
const read = this.options
|
|
75
115
|
.discover(workspaceId, (runner, sessions) => {
|
|
116
|
+
if (signal.aborted)
|
|
117
|
+
return;
|
|
76
118
|
if (!acceptingBatches || this.cacheGenerations.get(workspaceId) !== generation)
|
|
77
119
|
return;
|
|
78
120
|
const previous = this.sessionCache.get(workspaceId)?.sessions;
|
|
@@ -86,8 +128,9 @@ export class WorkspaceSessionService {
|
|
|
86
128
|
publish(merged);
|
|
87
129
|
batchPublished = true;
|
|
88
130
|
resolveInitial(merged);
|
|
89
|
-
})
|
|
131
|
+
}, signal)
|
|
90
132
|
.then((sessions) => {
|
|
133
|
+
signal.throwIfAborted();
|
|
91
134
|
if (this.cacheGenerations.get(workspaceId) === generation)
|
|
92
135
|
this.incompleteDiscoveries.delete(workspaceId);
|
|
93
136
|
const previous = this.sessionCache.get(workspaceId)?.sessions;
|
|
@@ -100,7 +143,7 @@ export class WorkspaceSessionService {
|
|
|
100
143
|
})
|
|
101
144
|
.catch((error) => {
|
|
102
145
|
rejectInitial(error);
|
|
103
|
-
if (this.cacheGenerations.get(workspaceId) === generation) {
|
|
146
|
+
if (!signal.aborted && this.cacheGenerations.get(workspaceId) === generation) {
|
|
104
147
|
nodeLog('native.session.discovery.failed', { workspaceId });
|
|
105
148
|
const partial = this.sessionCache.get(workspaceId);
|
|
106
149
|
if (partial !== undefined)
|
|
@@ -109,6 +152,7 @@ export class WorkspaceSessionService {
|
|
|
109
152
|
throw error;
|
|
110
153
|
})
|
|
111
154
|
.finally(() => {
|
|
155
|
+
this.discoveryReads.delete(read);
|
|
112
156
|
acceptingBatches = false;
|
|
113
157
|
if (this.sessionCacheReads.get(workspaceId) === read) {
|
|
114
158
|
this.sessionCacheReads.delete(workspaceId);
|
|
@@ -118,7 +162,10 @@ export class WorkspaceSessionService {
|
|
|
118
162
|
}
|
|
119
163
|
if (this.initialReads.get(workspaceId) === initial)
|
|
120
164
|
this.initialReads.delete(workspaceId);
|
|
121
|
-
if (
|
|
165
|
+
if (this.accepting &&
|
|
166
|
+
!signal.aborted &&
|
|
167
|
+
notifyCompletion &&
|
|
168
|
+
this.cacheGenerations.get(workspaceId) === generation)
|
|
122
169
|
this.options.invalidate(workspaceId);
|
|
123
170
|
if (!this.sessionCacheReads.has(workspaceId) &&
|
|
124
171
|
!this.sessionCache.has(workspaceId) &&
|
|
@@ -126,6 +173,7 @@ export class WorkspaceSessionService {
|
|
|
126
173
|
this.cacheGenerations.delete(workspaceId);
|
|
127
174
|
});
|
|
128
175
|
discovery = read;
|
|
176
|
+
this.discoveryReads.set(read, controller);
|
|
129
177
|
this.sessionCacheReads.set(workspaceId, discovery);
|
|
130
178
|
// Later Runner failures cannot become unhandled after the first batch was returned.
|
|
131
179
|
void discovery.catch(() => undefined);
|
|
@@ -176,7 +224,9 @@ export class WorkspaceSessionService {
|
|
|
176
224
|
}
|
|
177
225
|
async runWatchCycle(workspaceId) {
|
|
178
226
|
await this.refreshWatch(workspaceId);
|
|
179
|
-
if (!this.
|
|
227
|
+
if (!this.accepting ||
|
|
228
|
+
!this.options.active(workspaceId) ||
|
|
229
|
+
this.sessionWatchTimers.has(workspaceId))
|
|
180
230
|
return;
|
|
181
231
|
const timer = setTimeout(() => {
|
|
182
232
|
this.sessionWatchTimers.delete(workspaceId);
|
|
@@ -186,11 +236,11 @@ export class WorkspaceSessionService {
|
|
|
186
236
|
this.sessionWatchTimers.set(workspaceId, timer);
|
|
187
237
|
}
|
|
188
238
|
async performRefresh(workspaceId) {
|
|
189
|
-
if (!this.options.active(workspaceId))
|
|
239
|
+
if (!this.accepting || !this.options.active(workspaceId))
|
|
190
240
|
return this.stopWatch(workspaceId);
|
|
191
241
|
try {
|
|
192
|
-
const signature = JSON.stringify(await this.
|
|
193
|
-
if (!this.options.active(workspaceId))
|
|
242
|
+
const signature = JSON.stringify(await this.listPage(workspaceId));
|
|
243
|
+
if (!this.accepting || !this.options.active(workspaceId))
|
|
194
244
|
return this.stopWatch(workspaceId);
|
|
195
245
|
const previous = this.sessionWatchSignatures.get(workspaceId);
|
|
196
246
|
this.sessionWatchSignatures.set(workspaceId, signature);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
"turndown-plugin-gfm": "^1.0.2",
|
|
43
43
|
"ws": "^8.21.3",
|
|
44
44
|
"zod": "4.4.3",
|
|
45
|
-
"@myagentroam/agent": "0.12.
|
|
46
|
-
"@myagentroam/protocol": "0.12.
|
|
45
|
+
"@myagentroam/agent": "0.12.6",
|
|
46
|
+
"@myagentroam/protocol": "0.12.6"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/turndown": "^5.0.6",
|
|
50
50
|
"@types/ws": "^8.18.1",
|
|
51
|
-
"@myagentroam/marctl": "0.12.
|
|
51
|
+
"@myagentroam/marctl": "0.12.6",
|
|
52
52
|
"@myagentroam/code-navigation": "0.11.1"
|
|
53
53
|
},
|
|
54
54
|
"exports": {
|