@myagentroam/node 0.12.6 → 0.12.8
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/session-projection-composition.js +2 -1
- package/dist/connector.js +3 -10
- package/dist/marctl/package.json +1 -1
- package/dist/runner/claude-code-runner.js +18 -3
- package/dist/service/session-recent-activity.js +2 -1
- package/dist/session/native-session-projection-service.js +39 -17
- package/dist/session/session-catalog-service.js +18 -10
- package/dist/session/session-presentation-service.js +7 -0
- package/package.json +5 -5
|
@@ -10,7 +10,8 @@ export function composeSessionProjection(input) {
|
|
|
10
10
|
runners: input.runners,
|
|
11
11
|
capabilities: input.capabilities,
|
|
12
12
|
hasManagedActiveRun: (session) => hasManagedActiveRun(input.runtime, session),
|
|
13
|
-
projectionForNative: (session) => native.projectionForNative(session)
|
|
13
|
+
projectionForNative: (session) => native.projectionForNative(session),
|
|
14
|
+
cachedNativeActivityAt: (session) => native.cachedActivityAt(session)
|
|
14
15
|
});
|
|
15
16
|
const native = new NativeSessionProjectionService({
|
|
16
17
|
database: input.database,
|
package/dist/connector.js
CHANGED
|
@@ -546,21 +546,14 @@ export class NodeConnector {
|
|
|
546
546
|
const run = this.runtime.activeSessionRun(currentSessionId);
|
|
547
547
|
if (!run || !run.initiatedByUserId)
|
|
548
548
|
return [];
|
|
549
|
-
const
|
|
550
|
-
workspaceId: currentSession.workspaceId,
|
|
551
|
-
registeredOnly: true,
|
|
552
|
-
view: 'marctl',
|
|
553
|
-
parentSessionId: currentSessionId,
|
|
554
|
-
limit: 100
|
|
555
|
-
});
|
|
549
|
+
const data = await this.sessionCatalogService.listRegisteredChildren(currentSession.workspaceId, currentSessionId);
|
|
556
550
|
signal.throwIfAborted();
|
|
557
|
-
const data = result;
|
|
558
551
|
const truncated = data.nextCursor != null;
|
|
559
|
-
if (
|
|
552
|
+
if (data.sessions.length === 0 && !truncated)
|
|
560
553
|
return [];
|
|
561
554
|
return [
|
|
562
555
|
`Child Sessions (use agent_list for details): ${JSON.stringify({
|
|
563
|
-
sessions: data.sessions
|
|
556
|
+
sessions: data.sessions,
|
|
564
557
|
truncated
|
|
565
558
|
}).replace(/[<>&]/g, (c) => c === '<' ? '\\u003c' : c === '>' ? '\\u003e' : '\\u0026')}`
|
|
566
559
|
];
|
package/dist/marctl/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"0.12.
|
|
1
|
+
{"type":"module","version":"0.12.8"}
|
|
@@ -217,17 +217,31 @@ export class ClaudeCodeRunner extends AbstractRunner {
|
|
|
217
217
|
return this.execution.managedSessionByNativeId.get(nativeSessionId);
|
|
218
218
|
}
|
|
219
219
|
nativeAliasesFor(managedSessionId) {
|
|
220
|
-
return [...this.execution.
|
|
221
|
-
.filter(([, sessionId]) => sessionId === managedSessionId)
|
|
222
|
-
.map(([nativeSessionId]) => nativeSessionId);
|
|
220
|
+
return [...(this.execution.nativeIdsByManagedSession.get(managedSessionId) ?? [])];
|
|
223
221
|
}
|
|
224
222
|
forgetNativeAlias(nativeSessionId) {
|
|
223
|
+
const managedSessionId = this.execution.managedSessionByNativeId.get(nativeSessionId);
|
|
224
|
+
if (managedSessionId !== undefined) {
|
|
225
|
+
const aliases = this.execution.nativeIdsByManagedSession.get(managedSessionId);
|
|
226
|
+
aliases?.delete(nativeSessionId);
|
|
227
|
+
if (aliases?.size === 0)
|
|
228
|
+
this.execution.nativeIdsByManagedSession.delete(managedSessionId);
|
|
229
|
+
}
|
|
225
230
|
this.execution.managedSessionByNativeId.delete(nativeSessionId);
|
|
226
231
|
this.execution.managedNativeIds.delete(nativeSessionId);
|
|
227
232
|
}
|
|
228
233
|
rememberManagedNative(session, nativeSessionId) {
|
|
234
|
+
const previousId = this.execution.managedSessionByNativeId.get(nativeSessionId);
|
|
235
|
+
if (previousId !== undefined && previousId !== session.id)
|
|
236
|
+
this.forgetNativeAlias(nativeSessionId);
|
|
229
237
|
this.execution.managedNativeIds.add(nativeSessionId);
|
|
230
238
|
this.execution.managedSessionByNativeId.set(nativeSessionId, session.id);
|
|
239
|
+
let aliases = this.execution.nativeIdsByManagedSession.get(session.id);
|
|
240
|
+
if (aliases === undefined) {
|
|
241
|
+
aliases = new Set();
|
|
242
|
+
this.execution.nativeIdsByManagedSession.set(session.id, aliases);
|
|
243
|
+
}
|
|
244
|
+
aliases.add(nativeSessionId);
|
|
231
245
|
}
|
|
232
246
|
async readManagedRunnerTitle(session, nativeSessionId) {
|
|
233
247
|
let history = await readNativeSession(this.name, session.cwd, nativeSessionId);
|
|
@@ -299,6 +313,7 @@ export class ClaudeExecutionState {
|
|
|
299
313
|
runs = new Map();
|
|
300
314
|
managedNativeIds = new Set();
|
|
301
315
|
managedSessionByNativeId = new Map();
|
|
316
|
+
nativeIdsByManagedSession = new Map();
|
|
302
317
|
approvals = new Map();
|
|
303
318
|
userInputs = new Map();
|
|
304
319
|
}
|
|
@@ -132,7 +132,8 @@ function identity(session) {
|
|
|
132
132
|
runner: session.runner
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
/** Neutral catalog summary shared by CLI presentation and internal Agent context. */
|
|
136
|
+
export function sessionActivitySummary(runtime, session) {
|
|
136
137
|
const { status, runId, latestActivity } = activity(runtime, session.id);
|
|
137
138
|
if (latestActivity === null)
|
|
138
139
|
return { ...identity(session), status, runId, latestActivity: null };
|
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
import { deduplicateDirectSessions } from './runner-native-session-parsers.js';
|
|
2
|
+
import { latestSessionActivityAt } from './session-presentation-service.js';
|
|
2
3
|
/** Owns native identity/metadata projection, not Runner execution.
|
|
3
4
|
* Discovery borrows its caller's lifetime; cancellation revokes database projection before
|
|
4
5
|
* releasing the wait on a third-party read that may not itself support interruption.
|
|
6
|
+
* Ordered revisions keep older discovery snapshots from replacing newer reads or history updates.
|
|
5
7
|
*/
|
|
6
8
|
export class NativeSessionProjectionService {
|
|
7
9
|
options;
|
|
10
|
+
revision = 0;
|
|
8
11
|
directNative = new Map();
|
|
9
12
|
cachedNative() {
|
|
10
|
-
return [...this.directNative.values()];
|
|
13
|
+
return [...this.directNative.values()].map((entry) => entry.session);
|
|
14
|
+
}
|
|
15
|
+
cachedSessionsFor(sessions) {
|
|
16
|
+
return sessions.flatMap((session) => {
|
|
17
|
+
const current = this.directNative.get(session.id)?.session;
|
|
18
|
+
return current?.runner === session.runner && current.workspaceId === session.workspaceId
|
|
19
|
+
? [current]
|
|
20
|
+
: [];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
cachedActivityAt(session) {
|
|
24
|
+
let at = 0;
|
|
25
|
+
for (const id of this.nativeIds(session)) {
|
|
26
|
+
const native = this.directNative.get(id)?.session;
|
|
27
|
+
if (native?.runner === session.runner && native.workspaceId === session.workspaceId)
|
|
28
|
+
at = latestSessionActivityAt(at, native.lastActivityAt);
|
|
29
|
+
}
|
|
30
|
+
return at;
|
|
11
31
|
}
|
|
12
32
|
constructor(options) {
|
|
13
33
|
this.options = options;
|
|
14
34
|
}
|
|
15
35
|
async discover(runnerName, workspaceId, cleanup = false, signal) {
|
|
16
36
|
signal?.throwIfAborted();
|
|
37
|
+
const revision = ++this.revision;
|
|
17
38
|
const database = this.requireDatabase();
|
|
18
39
|
const workspace = workspaceId === undefined ? undefined : database.getWorkspace(workspaceId);
|
|
19
40
|
if (workspaceId !== undefined && workspace === undefined)
|
|
@@ -33,11 +54,8 @@ export class NativeSessionProjectionService {
|
|
|
33
54
|
signal?.throwIfAborted();
|
|
34
55
|
const ordered = deduplicateDirectSessions(sessions);
|
|
35
56
|
if (!cleanup) {
|
|
36
|
-
this.
|
|
37
|
-
return
|
|
38
|
-
const accepted = this.directNative.get(session.id);
|
|
39
|
-
return accepted === undefined ? [] : [accepted];
|
|
40
|
-
});
|
|
57
|
+
this.rememberAtRevision(ordered, revision);
|
|
58
|
+
return this.cachedSessionsFor(ordered);
|
|
41
59
|
}
|
|
42
60
|
const candidates = new Map(ordered.map((session) => [session.id, session]));
|
|
43
61
|
return ordered.flatMap((session) => {
|
|
@@ -59,16 +77,18 @@ export class NativeSessionProjectionService {
|
|
|
59
77
|
return { sessions: sessions.map(this.options.present) };
|
|
60
78
|
}
|
|
61
79
|
async list() {
|
|
80
|
+
const revision = ++this.revision;
|
|
62
81
|
const discovered = await Promise.all(this.options.runners
|
|
63
82
|
.advertised()
|
|
64
83
|
.filter((runner) => runner.available(this.options.capabilities()))
|
|
65
84
|
.map((runner) => this.discover(runner.name)));
|
|
66
85
|
const sessions = deduplicateDirectSessions(discovered.flat());
|
|
67
|
-
this.
|
|
68
|
-
return sessions.map(this.options.present);
|
|
86
|
+
this.rememberAtRevision(sessions, revision);
|
|
87
|
+
return this.cachedSessionsFor(sessions).map(this.options.present);
|
|
69
88
|
}
|
|
70
89
|
async discoverWorkspace(workspaceId, onRunnerDiscovered, cleanup = false, signal) {
|
|
71
90
|
signal?.throwIfAborted();
|
|
91
|
+
const revision = ++this.revision;
|
|
72
92
|
const discovered = await Promise.allSettled(this.options.runners
|
|
73
93
|
.advertised()
|
|
74
94
|
.filter((runner) => runner.available(this.options.capabilities()))
|
|
@@ -85,11 +105,11 @@ export class NativeSessionProjectionService {
|
|
|
85
105
|
throw failed.reason;
|
|
86
106
|
const sessions = deduplicateDirectSessions(discovered.flatMap((result) => (result.status === 'fulfilled' ? result.value : [])));
|
|
87
107
|
if (!cleanup)
|
|
88
|
-
this.
|
|
89
|
-
return sessions;
|
|
108
|
+
this.rememberAtRevision(sessions, revision);
|
|
109
|
+
return cleanup ? sessions : this.cachedSessionsFor(sessions);
|
|
90
110
|
}
|
|
91
111
|
async resolve(sessionId, registeredOnly = false) {
|
|
92
|
-
const cached = this.directNative.get(sessionId);
|
|
112
|
+
const cached = this.directNative.get(sessionId)?.session;
|
|
93
113
|
if (cached !== undefined)
|
|
94
114
|
return cached;
|
|
95
115
|
const database = this.options.database();
|
|
@@ -279,6 +299,9 @@ export class NativeSessionProjectionService {
|
|
|
279
299
|
return true;
|
|
280
300
|
}
|
|
281
301
|
remember(sessions) {
|
|
302
|
+
this.rememberAtRevision(sessions, ++this.revision);
|
|
303
|
+
}
|
|
304
|
+
rememberAtRevision(sessions, revision) {
|
|
282
305
|
const database = this.options.database();
|
|
283
306
|
const candidates = new Map(sessions.map((session) => [session.id, session]));
|
|
284
307
|
const valid = new Map(sessions.map((session) => [session.id, this.validParent(session, candidates)]));
|
|
@@ -312,7 +335,9 @@ export class NativeSessionProjectionService {
|
|
|
312
335
|
for (const session of accepted)
|
|
313
336
|
visit(session);
|
|
314
337
|
for (const session of ordered) {
|
|
315
|
-
this.directNative.
|
|
338
|
+
if ((this.directNative.get(session.id)?.revision ?? 0) > revision)
|
|
339
|
+
continue;
|
|
340
|
+
this.directNative.set(session.id, { session, revision });
|
|
316
341
|
if (database === undefined || session.externalSessionId === null)
|
|
317
342
|
continue;
|
|
318
343
|
const metadata = database.getNativeSessionMetadata(session.runner, session.externalSessionId, session.workspaceId);
|
|
@@ -339,10 +364,7 @@ export class NativeSessionProjectionService {
|
|
|
339
364
|
record.id,
|
|
340
365
|
record.metadata.customTitle ?? record.metadata.runnerTitle ?? 'Untitled session'
|
|
341
366
|
]));
|
|
342
|
-
for (const session of [
|
|
343
|
-
...this.directNative.values(),
|
|
344
|
-
...this.options.runtime.listAgentSessions()
|
|
345
|
-
]) {
|
|
367
|
+
for (const session of [...this.cachedNative(), ...this.options.runtime.listAgentSessions()]) {
|
|
346
368
|
if (session.workspaceId !== workspaceId)
|
|
347
369
|
continue;
|
|
348
370
|
const metadata = recordById.get(session.id)?.metadata;
|
|
@@ -425,7 +447,7 @@ export class NativeSessionProjectionService {
|
|
|
425
447
|
}
|
|
426
448
|
}
|
|
427
449
|
const directIds = new Set();
|
|
428
|
-
for (const [directId, direct] of this.directNative) {
|
|
450
|
+
for (const [directId, { session: direct }] of this.directNative) {
|
|
429
451
|
if (direct.runner === session.runner &&
|
|
430
452
|
direct.cwd === session.cwd &&
|
|
431
453
|
direct.externalSessionId !== null &&
|
|
@@ -5,10 +5,11 @@ import { nodeLog } from '../operational.js';
|
|
|
5
5
|
import { isTerminalRunStatus } from './runtime/type-guards.js';
|
|
6
6
|
import { sessionPage } from './pagination.js';
|
|
7
7
|
import { latestSessionActivityAt } from './session-presentation-service.js';
|
|
8
|
-
import {
|
|
8
|
+
import { sessionActivitySummary, sessionActivityStatus } from '../service/session-recent-activity.js';
|
|
9
9
|
import { WorkspaceSessionCleanupBudget, WorkspaceSessionCleanupDeadline } from '../service/workspace-session-cleanup-budget.js';
|
|
10
10
|
/** Owns catalog queries and Session catalog mutations; borrows the Workspace native cache.
|
|
11
|
-
* Public operations
|
|
11
|
+
* Public operations adapt transport views; internal readers use typed queries and neutral
|
|
12
|
+
* activity summaries. Both share query rules, never transport/admission routing.
|
|
12
13
|
*/
|
|
13
14
|
export class SessionCatalogService {
|
|
14
15
|
options;
|
|
@@ -18,12 +19,15 @@ export class SessionCatalogService {
|
|
|
18
19
|
}
|
|
19
20
|
/** Internal observation uses the same catalog query without public operation dispatch. */
|
|
20
21
|
listWorkspacePage(workspaceId, sessionGroup) {
|
|
21
|
-
return this.
|
|
22
|
+
return this.queryPage({
|
|
22
23
|
workspaceId,
|
|
23
24
|
limit: 30,
|
|
24
25
|
...(sessionGroup === undefined ? {} : { sessionGroup })
|
|
25
26
|
});
|
|
26
27
|
}
|
|
28
|
+
async listRegisteredChildren(workspaceId, parentSessionId) {
|
|
29
|
+
return this.summarizePage(await this.queryPage({ workspaceId, parentSessionId, registeredOnly: true, limit: 100 }));
|
|
30
|
+
}
|
|
27
31
|
operations() {
|
|
28
32
|
return {
|
|
29
33
|
'session.list': (data) => this.list(record(data)),
|
|
@@ -35,6 +39,16 @@ export class SessionCatalogService {
|
|
|
35
39
|
};
|
|
36
40
|
}
|
|
37
41
|
async list(input) {
|
|
42
|
+
const page = await this.queryPage(input);
|
|
43
|
+
return input.view === 'marctl' ? { view: 'marctl', ...this.summarizePage(page) } : page;
|
|
44
|
+
}
|
|
45
|
+
summarizePage(page) {
|
|
46
|
+
return {
|
|
47
|
+
sessions: page.sessions.map((session) => sessionActivitySummary(this.options.runtime, session)),
|
|
48
|
+
nextCursor: page.nextCursor
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async queryPage(input) {
|
|
38
52
|
if (input.sessionGroup !== undefined &&
|
|
39
53
|
input.sessionGroup !== 'MAIN' &&
|
|
40
54
|
input.sessionGroup !== 'CHILD')
|
|
@@ -55,13 +69,7 @@ export class SessionCatalogService {
|
|
|
55
69
|
.normalize('NFKC')
|
|
56
70
|
.toLocaleLowerCase()
|
|
57
71
|
.includes(query))), input);
|
|
58
|
-
return
|
|
59
|
-
? {
|
|
60
|
-
view: 'marctl',
|
|
61
|
-
sessions: page.sessions.map((session) => marctlSessionSummary(this.options.runtime, session)),
|
|
62
|
-
nextCursor: page.nextCursor
|
|
63
|
-
}
|
|
64
|
-
: { ...page, runningCounts };
|
|
72
|
+
return { ...page, runningCounts };
|
|
65
73
|
}
|
|
66
74
|
async graph(input) {
|
|
67
75
|
if (typeof input.workspaceId !== 'string')
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { ScheduledSessionStore } from '../service/scheduled-session-store.js';
|
|
2
|
+
/** Projects current ownership and observed native activity for every Session consumer.
|
|
3
|
+
* Borrows Runtime and the native cache; presentation never discovers or reads history.
|
|
4
|
+
*/
|
|
2
5
|
export class SessionPresentationService {
|
|
3
6
|
options;
|
|
4
7
|
constructor(options) {
|
|
@@ -13,6 +16,10 @@ export class SessionPresentationService {
|
|
|
13
16
|
...current,
|
|
14
17
|
lastActivityAt: latestSessionActivityAt(current.lastActivityAt, session.lastActivityAt)
|
|
15
18
|
};
|
|
19
|
+
session = {
|
|
20
|
+
...session,
|
|
21
|
+
lastActivityAt: latestSessionActivityAt(session.lastActivityAt, this.options.cachedNativeActivityAt(session))
|
|
22
|
+
};
|
|
16
23
|
const database = this.options.database();
|
|
17
24
|
if (session.externalSessionId !== null && database !== undefined) {
|
|
18
25
|
const metadata = database.getNativeSessionMetadata(session.runner, session.externalSessionId, session.workspaceId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.8",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -42,14 +42,14 @@
|
|
|
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.8",
|
|
46
|
+
"@myagentroam/protocol": "0.12.8"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/turndown": "^5.0.6",
|
|
50
50
|
"@types/ws": "^8.18.1",
|
|
51
|
-
"@myagentroam/
|
|
52
|
-
"@myagentroam/
|
|
51
|
+
"@myagentroam/code-navigation": "0.11.1",
|
|
52
|
+
"@myagentroam/marctl": "0.12.8"
|
|
53
53
|
},
|
|
54
54
|
"exports": {
|
|
55
55
|
"./config": {
|