@myagentroam/node 0.11.53 → 0.11.55
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/service/agent-network-list.js +24 -9
- package/dist/service/conversation-history-service.js +2 -0
- package/dist/service/conversation-observation.js +15 -5
- package/dist/service/native-session-watch-service.js +2 -1
- package/dist/service/session-recent-activity.js +20 -5
- package/dist/session/session-catalog-service.js +11 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sessionActivityStatus } from './session-recent-activity.js';
|
|
2
2
|
/** Read only registered relationships and live Node facts; never discover or resume a Runner. */
|
|
3
3
|
export async function listAgentNetworkSessions(source, input, options) {
|
|
4
4
|
if (Object.keys(input).some((key) => !['scope', 'query', 'limit'].includes(key)) ||
|
|
@@ -20,9 +20,8 @@ export async function listAgentNetworkSessions(source, input, options) {
|
|
|
20
20
|
throw new Error('AGENT_TARGET_REQUIRED');
|
|
21
21
|
if (!options.database)
|
|
22
22
|
throw new Error('DATABASE_UNAVAILABLE');
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
.map((record) => [record.id, record.metadata.parentSessionId ?? null]));
|
|
23
|
+
const records = options.database.listSessionRecords(options.workspaceId);
|
|
24
|
+
const parents = new Map(records.map((record) => [record.id, record.metadata.parentSessionId ?? null]));
|
|
26
25
|
for (const session of known.values())
|
|
27
26
|
parents.set(session.id, session.parentSessionId ?? null);
|
|
28
27
|
let root = source.id;
|
|
@@ -49,7 +48,24 @@ export async function listAgentNetworkSessions(source, input, options) {
|
|
|
49
48
|
const ids = [root];
|
|
50
49
|
for (let i = 0; i < ids.length; i++)
|
|
51
50
|
ids.push(...(children.get(ids[i]) ?? []).sort());
|
|
52
|
-
|
|
51
|
+
const cached = new Map(options.projection.cachedNative().map((session) => [session.id, session]));
|
|
52
|
+
const workspace = options.database.getWorkspace(options.workspaceId);
|
|
53
|
+
const registered = new Map(workspace === undefined
|
|
54
|
+
? []
|
|
55
|
+
: records.map((record) => [
|
|
56
|
+
record.id,
|
|
57
|
+
{
|
|
58
|
+
id: record.id,
|
|
59
|
+
workspaceId: record.workspaceId,
|
|
60
|
+
parentSessionId: record.metadata.parentSessionId ?? null,
|
|
61
|
+
customTitle: record.metadata.customTitle,
|
|
62
|
+
runnerTitle: record.metadata.runnerTitle,
|
|
63
|
+
lastActivityAt: 0
|
|
64
|
+
}
|
|
65
|
+
]));
|
|
66
|
+
sessions = ids
|
|
67
|
+
.map((id) => known.get(id) ?? cached.get(id) ?? registered.get(id))
|
|
68
|
+
.filter((session) => session !== undefined && session.workspaceId === options.workspaceId);
|
|
53
69
|
}
|
|
54
70
|
else {
|
|
55
71
|
sessions = [...known.values()];
|
|
@@ -57,12 +73,11 @@ export async function listAgentNetworkSessions(source, input, options) {
|
|
|
57
73
|
const query = input.query?.trim().normalize('NFKC').toLocaleLowerCase() ?? '';
|
|
58
74
|
const matches = sessions
|
|
59
75
|
.map((session) => {
|
|
60
|
-
const { id, title, status } = marctlSessionSummary(options.runtime, session);
|
|
61
76
|
return {
|
|
62
|
-
id,
|
|
77
|
+
id: session.id,
|
|
63
78
|
parentSessionId: session.parentSessionId ?? null,
|
|
64
|
-
title,
|
|
65
|
-
status,
|
|
79
|
+
title: session.customTitle ?? session.runnerTitle ?? 'Untitled session',
|
|
80
|
+
status: sessionActivityStatus(options.runtime, session.id),
|
|
66
81
|
lastActivityAt: session.lastActivityAt
|
|
67
82
|
};
|
|
68
83
|
})
|
|
@@ -535,6 +535,8 @@ export class ConversationHistoryService {
|
|
|
535
535
|
nodeLog('native.session.history.official-read.completed', {
|
|
536
536
|
runner: session.runner,
|
|
537
537
|
sessionId: session.id,
|
|
538
|
+
limit: input.limit ?? null,
|
|
539
|
+
hasCursor: input.cursor !== undefined,
|
|
538
540
|
durationMs
|
|
539
541
|
});
|
|
540
542
|
}
|
|
@@ -17,6 +17,7 @@ export class ConversationObservation {
|
|
|
17
17
|
historyRead;
|
|
18
18
|
refreshRead;
|
|
19
19
|
refreshMode;
|
|
20
|
+
refreshLimit = 0;
|
|
20
21
|
eventRevision = 0;
|
|
21
22
|
transitionGeneration = 0;
|
|
22
23
|
closed = false;
|
|
@@ -34,7 +35,7 @@ export class ConversationObservation {
|
|
|
34
35
|
this.closed = true;
|
|
35
36
|
this.page = this.observedPage = this.runtimePage = undefined;
|
|
36
37
|
}
|
|
37
|
-
async initial() {
|
|
38
|
+
async initial(limit = INITIAL_TURN_LIMIT) {
|
|
38
39
|
if (this.closed)
|
|
39
40
|
return undefined;
|
|
40
41
|
if (this.options.managedActive(this.session)) {
|
|
@@ -42,13 +43,15 @@ export class ConversationObservation {
|
|
|
42
43
|
return { page: await this.runningWindow(), changed: false, handoff: false, snapshot: false };
|
|
43
44
|
}
|
|
44
45
|
// A fresh initial must not join an older observation's smaller or stale window.
|
|
45
|
-
|
|
46
|
-
(this.page !== undefined || this.refreshMode === 'observation'))
|
|
46
|
+
while (this.refreshRead !== undefined &&
|
|
47
|
+
(this.page !== undefined || this.refreshMode === 'observation' || this.refreshLimit < limit))
|
|
47
48
|
await this.refreshRead.catch(() => undefined);
|
|
48
49
|
if (this.closed)
|
|
49
50
|
return undefined;
|
|
50
51
|
const joinedInitial = this.refreshRead !== undefined;
|
|
51
|
-
|
|
52
|
+
// A narrow initial must not discard coverage already shared with existing subscribers.
|
|
53
|
+
const initialLimit = Math.max(limit, Math.min(this.page?.turns.length ?? 0, EMITTED_TURN_LIMIT));
|
|
54
|
+
const update = await this.refresh('initial', initialLimit);
|
|
52
55
|
if (this.closed || update === undefined)
|
|
53
56
|
return undefined;
|
|
54
57
|
if (this.options.managedActive(this.session)) {
|
|
@@ -64,10 +67,12 @@ export class ConversationObservation {
|
|
|
64
67
|
if (this.refreshRead === read) {
|
|
65
68
|
this.refreshRead = undefined;
|
|
66
69
|
this.refreshMode = undefined;
|
|
70
|
+
this.refreshLimit = 0;
|
|
67
71
|
}
|
|
68
72
|
});
|
|
69
73
|
this.refreshRead = read;
|
|
70
74
|
this.refreshMode = mode;
|
|
75
|
+
this.refreshLimit = limit;
|
|
71
76
|
return read;
|
|
72
77
|
}
|
|
73
78
|
async performRefresh(mode, limit) {
|
|
@@ -116,7 +121,12 @@ export class ConversationObservation {
|
|
|
116
121
|
source: accepted.source,
|
|
117
122
|
turns: accepted.turns.slice(-EMITTED_TURN_LIMIT)
|
|
118
123
|
});
|
|
119
|
-
|
|
124
|
+
// Expanding a bounded initial window with an unchanged older prefix is not a live update.
|
|
125
|
+
const previousCount = this.page?.turns.length ?? 0;
|
|
126
|
+
const comparison = previousCount > 0 && previousCount < EMITTED_TURN_LIMIT
|
|
127
|
+
? JSON.stringify({ source: accepted.source, turns: accepted.turns.slice(-previousCount) })
|
|
128
|
+
: signature;
|
|
129
|
+
const changed = comparison !== this.signature;
|
|
120
130
|
this.signature = signature;
|
|
121
131
|
// Keep coverage metadata current even when the visible content did not change.
|
|
122
132
|
this.page = accepted;
|
|
@@ -313,7 +313,8 @@ export class NativeSessionWatchService {
|
|
|
313
313
|
if (watch === undefined)
|
|
314
314
|
return;
|
|
315
315
|
watch.observation.session = session;
|
|
316
|
-
|
|
316
|
+
// Every Turn contributes at least one Segment, including an empty Turn.
|
|
317
|
+
const update = await watch.observation.initial(lease.unit === 'SEGMENT' ? INITIAL_SEGMENT_LIMIT : undefined);
|
|
317
318
|
if (this.leases.get(lease.ownerKey) !== lease || this.watches.get(session.id) !== watch)
|
|
318
319
|
return;
|
|
319
320
|
if (update === undefined)
|
|
@@ -50,10 +50,7 @@ function activity(runtime, sessionId) {
|
|
|
50
50
|
if (!['tool_call', 'command_execution', 'file_change'].includes(item.kind))
|
|
51
51
|
continue;
|
|
52
52
|
const presentation = isPlainRecord(payload.toolPresentation) ? payload.toolPresentation : {};
|
|
53
|
-
if (run?.status === 'RUNNING' &&
|
|
54
|
-
item.status === 'IN_PROGRESS' &&
|
|
55
|
-
presentation.kind === 'SLEEP' &&
|
|
56
|
-
typeof presentation.startedAt === 'string')
|
|
53
|
+
if (run?.status === 'RUNNING' && isSleepingTool(item))
|
|
57
54
|
sleeping = true;
|
|
58
55
|
if (recentTools.length === 3 && !isLatest)
|
|
59
56
|
continue;
|
|
@@ -82,7 +79,25 @@ export function marctlSessionProgress(runtime, sessionId) {
|
|
|
82
79
|
return { ...progress, ...pendingApprovals(runtime, sessionId) };
|
|
83
80
|
}
|
|
84
81
|
export function sessionActivityStatus(runtime, sessionId) {
|
|
85
|
-
|
|
82
|
+
const run = runtime.activeSessionRun(sessionId);
|
|
83
|
+
if (run === undefined || isTerminalRunStatus(run.status))
|
|
84
|
+
return 'IDLE';
|
|
85
|
+
if (run.status !== 'RUNNING')
|
|
86
|
+
return 'RUNNING';
|
|
87
|
+
const sleeping = runtime
|
|
88
|
+
.conversationTail(sessionId)
|
|
89
|
+
.some((turn) => turn.runId === run.id && turn.items.some(isSleepingTool));
|
|
90
|
+
return sleeping ? 'SLEEPING' : 'RUNNING';
|
|
91
|
+
}
|
|
92
|
+
function isSleepingTool(item) {
|
|
93
|
+
if (item.parentItemId !== null ||
|
|
94
|
+
item.status !== 'IN_PROGRESS' ||
|
|
95
|
+
!['tool_call', 'command_execution', 'file_change'].includes(item.kind))
|
|
96
|
+
return false;
|
|
97
|
+
const presentation = isPlainRecord(item.payload) ? item.payload.toolPresentation : undefined;
|
|
98
|
+
return (isPlainRecord(presentation) &&
|
|
99
|
+
presentation.kind === 'SLEEP' &&
|
|
100
|
+
typeof presentation.startedAt === 'string');
|
|
86
101
|
}
|
|
87
102
|
/** Summarizes current approval facts without exposing tool input or native payloads. */
|
|
88
103
|
function pendingApprovals(runtime, sessionId) {
|
|
@@ -76,6 +76,15 @@ export class SessionCatalogService {
|
|
|
76
76
|
ancestors.add(parent.id);
|
|
77
77
|
root = parent;
|
|
78
78
|
}
|
|
79
|
+
const native = this.options
|
|
80
|
+
.cachedNative()
|
|
81
|
+
.filter((session) => session.workspaceId === input.workspaceId);
|
|
82
|
+
const projections = this.options.projection.projectionsForNative(native);
|
|
83
|
+
const activityById = new Map();
|
|
84
|
+
for (const source of native) {
|
|
85
|
+
const id = projections.get(source)?.id ?? source.id;
|
|
86
|
+
activityById.set(id, latestSessionActivityAt(activityById.get(id) ?? 0, source.lastActivityAt));
|
|
87
|
+
}
|
|
79
88
|
const queue = [root];
|
|
80
89
|
const visited = new Set([root.id]);
|
|
81
90
|
const nodes = [];
|
|
@@ -93,7 +102,8 @@ export class SessionCatalogService {
|
|
|
93
102
|
'Untitled session',
|
|
94
103
|
runner: session.runner,
|
|
95
104
|
activity: live && 'activity' in live ? live.activity : 'IDLE',
|
|
96
|
-
status: sessionActivityStatus(this.options.runtime, session.id)
|
|
105
|
+
status: sessionActivityStatus(this.options.runtime, session.id),
|
|
106
|
+
lastActivityAt: latestSessionActivityAt(live?.lastActivityAt ?? 0, activityById.get(session.id) ?? 0)
|
|
97
107
|
});
|
|
98
108
|
const children = [...database.listChildSessionRecords(input.workspaceId, session.id)];
|
|
99
109
|
if (input.includeAncestors === true)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.55",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"sharp": "0.35.5",
|
|
39
39
|
"ws": "^8.21.3",
|
|
40
40
|
"zod": "4.4.3",
|
|
41
|
-
"@myagentroam/
|
|
41
|
+
"@myagentroam/agent": "0.11.55",
|
|
42
42
|
"@myagentroam/marctl": "0.11.11",
|
|
43
|
-
"@myagentroam/
|
|
44
|
-
"@myagentroam/
|
|
43
|
+
"@myagentroam/protocol": "0.11.55",
|
|
44
|
+
"@myagentroam/code-navigation": "0.11.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/ws": "^8.18.1"
|