@myagentroam/node 0.11.54 → 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.
@@ -1,4 +1,4 @@
1
- import { marctlSessionSummary } from './session-recent-activity.js';
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 parents = new Map(options.database
24
- .listSessionRecords(options.workspaceId)
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
- sessions = (await Promise.all(ids.map((id) => known.get(id) ?? options.projection.resolve(id, true)))).filter((session) => session !== undefined && session.workspaceId === options.workspaceId);
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
- if (this.refreshRead !== undefined &&
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
- const update = await this.refresh('initial', INITIAL_TURN_LIMIT);
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
- const changed = signature !== this.signature;
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
- const update = await watch.observation.initial();
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
- return activity(runtime, sessionId).status;
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.11.54",
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/agent": "0.11.54",
42
- "@myagentroam/protocol": "0.11.54",
43
- "@myagentroam/code-navigation": "0.11.1",
44
- "@myagentroam/marctl": "0.11.11"
41
+ "@myagentroam/agent": "0.11.55",
42
+ "@myagentroam/marctl": "0.11.11",
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"