@myagentroam/agent 0.11.39 → 0.11.41

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.
Files changed (166) hide show
  1. package/dist/cli/catalog-file.d.ts +2 -0
  2. package/dist/cli/main.js +11 -4
  3. package/dist/cli/subagent/control.d.ts +56 -0
  4. package/dist/cli/subagent/control.js +219 -0
  5. package/dist/cli/subagent/history.d.ts +17 -0
  6. package/dist/cli/subagent/history.js +85 -0
  7. package/dist/cli/subagent/index.d.ts +10 -0
  8. package/dist/cli/subagent/index.js +94 -0
  9. package/dist/cli/subagent/native.d.ts +91 -0
  10. package/dist/cli/subagent/native.js +359 -0
  11. package/dist/cli/subagent/observations.d.ts +57 -0
  12. package/dist/cli/subagent/observations.js +351 -0
  13. package/dist/cli/subagent/query.d.ts +4 -0
  14. package/dist/cli/subagent/query.js +114 -0
  15. package/dist/cli/subagent/resources.d.ts +3 -0
  16. package/dist/cli/subagent/resources.js +112 -0
  17. package/dist/cli/subagent/snapshot.d.ts +77 -0
  18. package/dist/cli/subagent/snapshot.js +127 -0
  19. package/dist/cli/subagent/start.d.ts +24 -0
  20. package/dist/cli/subagent/start.js +201 -0
  21. package/dist/cli/subagent/wait-output.d.ts +19 -0
  22. package/dist/cli/subagent/wait-output.js +56 -0
  23. package/dist/cli/subagent/wait.d.ts +175 -0
  24. package/dist/cli/subagent/wait.js +453 -0
  25. package/dist/extensions/contracts.d.ts +2 -5
  26. package/dist/extensions/manager.d.ts +0 -2
  27. package/dist/extensions/manager.js +1 -2
  28. package/dist/extensions/models.d.ts +3 -0
  29. package/dist/extensions/operation.d.ts +8 -0
  30. package/dist/extensions/operation.js +3 -0
  31. package/dist/extensions/services.d.ts +9 -1
  32. package/dist/extensions/services.js +26 -3
  33. package/dist/extensions/session-context.d.ts +18 -0
  34. package/dist/extensions/session-context.js +4 -0
  35. package/dist/extensions/session-input.d.ts +2 -2
  36. package/dist/extensions/session-input.js +3 -4
  37. package/dist/extensions/session-messages.d.ts +1 -1
  38. package/dist/extensions/session-tools.js +20 -3
  39. package/dist/extensions/session.d.ts +1 -4
  40. package/dist/extensions/session.js +2 -16
  41. package/dist/extensions/tools.js +21 -3
  42. package/dist/extensions.d.ts +5 -2
  43. package/dist/extensions.js +2 -1
  44. package/dist/host/local-agent-host.d.ts +2 -2
  45. package/dist/host/local-exec-tool.d.ts +1 -1
  46. package/dist/host/local-exec-tool.js +28 -3
  47. package/dist/host/local-tool-registry.d.ts +2 -2
  48. package/dist/index.d.ts +2 -2
  49. package/dist/model/configuration.d.ts +3 -1
  50. package/dist/model/configuration.js +1 -0
  51. package/dist/model/openai-responses.js +5 -0
  52. package/dist/model/responses-request.js +9 -0
  53. package/dist/prompts/index.js +1 -3
  54. package/dist/prompts/permissions.d.ts +1 -1
  55. package/dist/prompts/permissions.js +1 -0
  56. package/dist/prompts/resources.d.ts +1 -5
  57. package/dist/prompts/resources.js +3 -12
  58. package/dist/prompts/subagent.d.ts +1 -1
  59. package/dist/prompts/subagent.js +1 -1
  60. package/dist/runtime/context/gc-candidates-web.d.ts +6 -0
  61. package/dist/runtime/context/gc-candidates-web.js +53 -1
  62. package/dist/runtime/context/gc-candidates.js +1 -9
  63. package/dist/runtime/context/gc.js +19 -11
  64. package/dist/runtime/context/projection.js +1 -0
  65. package/dist/runtime/events.d.ts +4 -3
  66. package/dist/runtime/execution-handle.d.ts +2 -0
  67. package/dist/runtime/wait.js +12 -2
  68. package/dist/sdk/agent.js +94 -5
  69. package/dist/sdk/execution-context-coordinator.d.ts +2 -3
  70. package/dist/sdk/execution-context-coordinator.js +3 -3
  71. package/dist/sdk/execution-input.d.ts +3 -4
  72. package/dist/sdk/execution-input.js +31 -25
  73. package/dist/sdk/execution-round-preparation.d.ts +1 -1
  74. package/dist/sdk/execution-round-preparation.js +4 -4
  75. package/dist/sdk/execution-tool-catalog.js +2 -10
  76. package/dist/sdk/execution-tool-scope.d.ts +1 -3
  77. package/dist/sdk/execution-tool-scope.js +10 -12
  78. package/dist/sdk/extension-models.js +3 -0
  79. package/dist/sdk/extension-session-launch.d.ts +3 -2
  80. package/dist/sdk/extension-session-launch.js +2 -3
  81. package/dist/sdk/extension-tools.js +0 -2
  82. package/dist/sdk/native-steer.d.ts +2 -2
  83. package/dist/sdk/native-steer.js +2 -2
  84. package/dist/sdk/session-composition.d.ts +2 -17
  85. package/dist/sdk/session-execution-factory.d.ts +2 -3
  86. package/dist/sdk/session-execution-factory.js +20 -114
  87. package/dist/sdk/session-execution.d.ts +13 -7
  88. package/dist/sdk/session-execution.js +11 -3
  89. package/dist/sdk/session-extension-messages.d.ts +1 -3
  90. package/dist/sdk/session-extension-messages.js +14 -9
  91. package/dist/sdk/session-extension-prompt.js +1 -3
  92. package/dist/sdk/session-maintenance.d.ts +1 -0
  93. package/dist/sdk/session-maintenance.js +16 -7
  94. package/dist/sdk/session-open.d.ts +2 -3
  95. package/dist/sdk/session-open.js +5 -24
  96. package/dist/sdk/session-runtime.d.ts +19 -8
  97. package/dist/sdk/session-runtime.js +36 -30
  98. package/dist/sdk/session.d.ts +3 -2
  99. package/dist/sdk/session.js +22 -89
  100. package/dist/sdk/types.d.ts +36 -0
  101. package/dist/session/catalog.d.ts +22 -1
  102. package/dist/session/catalog.js +102 -0
  103. package/dist/session/context-gc-checkpoint.js +30 -1
  104. package/dist/session/context-gc-contract.d.ts +7 -0
  105. package/dist/session/context-gc-replacements.d.ts +3 -0
  106. package/dist/session/context-gc-replacements.js +26 -0
  107. package/dist/session/store/contracts.d.ts +2 -2
  108. package/dist/session/store/jsonl/directory-index.d.ts +1 -1
  109. package/dist/session/store/jsonl/directory-index.js +18 -17
  110. package/dist/session/store/jsonl/history.js +1 -1
  111. package/dist/session/store/jsonl/index-transaction.js +2 -8
  112. package/dist/session/store/jsonl/jsonl-store.d.ts +1 -1
  113. package/dist/session/store/jsonl/jsonl-store.js +24 -9
  114. package/dist/session/store/jsonl/resources.js +5 -1
  115. package/dist/session/types.d.ts +1 -0
  116. package/dist/tools/agent/sleep.js +1 -1
  117. package/dist/tools/callers.js +1 -12
  118. package/dist/tools/code-mode/dispatch.js +5 -2
  119. package/dist/tools/code-mode/index.d.ts +1 -1
  120. package/dist/tools/code-mode/index.js +4 -2
  121. package/dist/tools/contracts.d.ts +20 -0
  122. package/dist/tools/execution-limits.d.ts +5 -20
  123. package/dist/tools/execution-limits.js +0 -2
  124. package/dist/tools/execution.js +16 -4
  125. package/dist/tools/local/exec.js +8 -2
  126. package/dist/tools/output-projection.d.ts +5 -2
  127. package/dist/tools/output-projection.js +133 -92
  128. package/dist/tools/permissions.d.ts +0 -1
  129. package/dist/tools/permissions.js +1 -9
  130. package/dist/tools/registry.js +1 -13
  131. package/dist/tools/runtime-context.d.ts +1 -21
  132. package/package.json +2 -2
  133. package/dist/extensions/agents.d.ts +0 -100
  134. package/dist/extensions/agents.js +0 -121
  135. package/dist/runtime/context/gc-candidates-subagent.d.ts +0 -6
  136. package/dist/runtime/context/gc-candidates-subagent.js +0 -100
  137. package/dist/sdk/extension-agent-controller.d.ts +0 -3
  138. package/dist/sdk/extension-agent-controller.js +0 -69
  139. package/dist/sdk/extension-agents.d.ts +0 -9
  140. package/dist/sdk/extension-agents.js +0 -58
  141. package/dist/sdk/subagent-backend.d.ts +0 -3
  142. package/dist/sdk/subagent-backend.js +0 -49
  143. package/dist/subagent/contracts.d.ts +0 -107
  144. package/dist/subagent/contracts.js +0 -1
  145. package/dist/subagent/limits.d.ts +0 -17
  146. package/dist/subagent/limits.js +0 -17
  147. package/dist/subagent/model-update.d.ts +0 -2
  148. package/dist/subagent/model-update.js +0 -81
  149. package/dist/subagent/session-controller.d.ts +0 -87
  150. package/dist/subagent/session-controller.js +0 -1343
  151. package/dist/subagent/task.d.ts +0 -134
  152. package/dist/subagent/task.js +0 -436
  153. package/dist/subagent/turn-contracts.d.ts +0 -64
  154. package/dist/subagent/turn-contracts.js +0 -1
  155. package/dist/subagent/wait-targets.d.ts +0 -10
  156. package/dist/subagent/wait-targets.js +0 -26
  157. package/dist/subagent/wait.d.ts +0 -26
  158. package/dist/subagent/wait.js +0 -73
  159. package/dist/tools/agent/agent-cancel.d.ts +0 -7
  160. package/dist/tools/agent/agent-cancel.js +0 -29
  161. package/dist/tools/agent/agent-message.d.ts +0 -7
  162. package/dist/tools/agent/agent-message.js +0 -57
  163. package/dist/tools/agent/agent-start.d.ts +0 -12
  164. package/dist/tools/agent/agent-start.js +0 -106
  165. package/dist/tools/agent/agent-wait.d.ts +0 -7
  166. package/dist/tools/agent/agent-wait.js +0 -91
@@ -0,0 +1,351 @@
1
+ import { boundedUtf8, projectChildEvent, publicChildSnapshot } from './snapshot.js';
2
+ /** One warm parent Session's live observations; Execution remains owned by Core. */
3
+ export class SessionObservations {
4
+ full;
5
+ agent;
6
+ constructor(full = false, agent) {
7
+ this.full = full;
8
+ this.agent = agent;
9
+ }
10
+ #subscriptions = new Map();
11
+ #items = new Map();
12
+ #snapshots = new Map();
13
+ #terminals = new Map();
14
+ #latestTerminal = new Map();
15
+ #consumedTerminals = new Set();
16
+ #receipts = new Map();
17
+ #listeners = new Set();
18
+ #revision = 0;
19
+ #closed = false;
20
+ wake = {
21
+ pending: () => [...this.#items.values()].some((item) => item.wakes),
22
+ subscribe: (listener) => {
23
+ if (!this.#closed)
24
+ this.#listeners.add(listener);
25
+ return () => this.#listeners.delete(listener);
26
+ }
27
+ };
28
+ observe(session) {
29
+ if (this.#closed || this.#subscriptions.has(session.id))
30
+ return;
31
+ const subscription = session.subscribe((event) => {
32
+ this.#publish(session.id, event);
33
+ if (isTerminal(event))
34
+ this.#unobserve(session.id);
35
+ });
36
+ this.#subscriptions.set(session.id, subscription.unsubscribe);
37
+ if (subscription.terminalEvent) {
38
+ this.#publish(session.id, subscription.terminalEvent);
39
+ this.#unobserve(session.id);
40
+ }
41
+ }
42
+ #unobserve(sessionId) {
43
+ this.#subscriptions.get(sessionId)?.();
44
+ this.#subscriptions.delete(sessionId);
45
+ }
46
+ #publish(sessionId, event) {
47
+ if (this.#closed)
48
+ return;
49
+ const snapshotKey = JSON.stringify([sessionId, event.executionId]);
50
+ if ((event.type === 'message.completed' && (this.full || event.phase === 'final_answer')) ||
51
+ (event.type === 'tool.completed' && (this.full || event.toolName === 'apply_patch')) ||
52
+ (this.full && (event.type === 'tool.started' || event.type === 'tool.failed')) ||
53
+ event.type === 'execution.failed' ||
54
+ event.type === 'execution.cancelled') {
55
+ const snapshot = this.#snapshots.get(snapshotKey) ?? { changedFiles: [] };
56
+ projectChildEvent(snapshot, event, this.full);
57
+ this.#snapshots.set(snapshotKey, snapshot);
58
+ }
59
+ const item = event.type === 'message.completed'
60
+ ? { kind: 'message', value: event.text }
61
+ : event.type === 'execution.completed' ||
62
+ event.type === 'execution.cancelled' ||
63
+ event.type === 'execution.failed'
64
+ ? { kind: 'terminal', value: event.type }
65
+ : undefined;
66
+ if (!item)
67
+ return;
68
+ if (item.kind === 'terminal' && this.agent) {
69
+ this.#terminals.set(snapshotKey, event);
70
+ this.#latestTerminal.set(sessionId, snapshotKey);
71
+ }
72
+ const key = JSON.stringify([sessionId, event.executionId, item.kind]);
73
+ const bounded = this.full
74
+ ? { value: item.value, truncated: false }
75
+ : boundedUtf8(item.value, 2000);
76
+ this.#items.set(key, {
77
+ sessionId,
78
+ executionId: event.executionId,
79
+ ...item,
80
+ value: bounded.value,
81
+ ...(bounded.truncated ? { truncated: true } : {}),
82
+ ...(event.type === 'execution.failed'
83
+ ? {
84
+ errorCode: event.code,
85
+ ...(event.message === undefined
86
+ ? {}
87
+ : {
88
+ errorMessage: this.full ? event.message : boundedUtf8(event.message, 1000).value
89
+ })
90
+ }
91
+ : {}),
92
+ ...(event.type === 'execution.completed' && event.permissionStop !== undefined
93
+ ? {
94
+ permissionStop: {
95
+ ...event.permissionStop,
96
+ reason: this.full
97
+ ? event.permissionStop.reason
98
+ : boundedUtf8(event.permissionStop.reason, 1000).value
99
+ }
100
+ }
101
+ : {}),
102
+ ...(event.type === 'message.completed' && event.phase === 'final_answer'
103
+ ? { final: true }
104
+ : {}),
105
+ revision: ++this.#revision,
106
+ wakes: item.kind === 'message'
107
+ });
108
+ if (item.kind === 'message')
109
+ for (const listener of [...this.#listeners]) {
110
+ try {
111
+ listener();
112
+ }
113
+ catch {
114
+ /* Listener failures cannot affect Core event delivery. */
115
+ }
116
+ }
117
+ }
118
+ /** Upward reports are collected at the parent's next natural round, without waking it. */
119
+ report(sessionId, executionId, value) {
120
+ if (this.#closed)
121
+ return false;
122
+ const key = JSON.stringify([sessionId, executionId, 'report']);
123
+ const bounded = boundedUtf8(value, 2000);
124
+ this.#items.set(key, {
125
+ sessionId,
126
+ executionId,
127
+ kind: 'message',
128
+ value: bounded.value,
129
+ ...(bounded.truncated ? { truncated: true } : {}),
130
+ revision: ++this.#revision,
131
+ wakes: false
132
+ });
133
+ return true;
134
+ }
135
+ snapshot(sessionId, executionId) {
136
+ const stored = this.#snapshots.get(JSON.stringify([sessionId, executionId]));
137
+ return stored
138
+ ? {
139
+ ...stored,
140
+ changedFiles: [...stored.changedFiles],
141
+ ...(stored.latestMessage ? { latestMessage: { ...stored.latestMessage } } : {}),
142
+ ...(stored.latestActivity ? { latestActivity: { ...stored.latestActivity } } : {}),
143
+ ...(stored.verification ? { verification: [...stored.verification] } : {}),
144
+ ...(stored.evidence ? { evidence: stored.evidence.map((item) => ({ ...item })) } : {})
145
+ }
146
+ : { changedFiles: [] };
147
+ }
148
+ terminal(sessionId) {
149
+ const key = this.#latestTerminal.get(sessionId);
150
+ return key === undefined ? undefined : this.#terminals.get(key);
151
+ }
152
+ #releaseSnapshot(sessionId, executionId) {
153
+ if (this.#terminals.has(JSON.stringify([sessionId, executionId])) ||
154
+ [...this.#items.values()].some((item) => item.sessionId === sessionId && item.executionId === executionId))
155
+ return;
156
+ this.#snapshots.delete(JSON.stringify([sessionId, executionId]));
157
+ }
158
+ /** A consumed observation may be discarded only after its terminal is present in Session history. */
159
+ #verifyTerminal(sessionId, executionId) {
160
+ const key = JSON.stringify([sessionId, executionId]);
161
+ const terminal = this.#terminals.get(key);
162
+ if (!this.agent || !terminal) {
163
+ this.#releaseSnapshot(sessionId, executionId);
164
+ return;
165
+ }
166
+ void this.agent
167
+ .sessionHistory(sessionId, { limit: 100 })
168
+ .then((page) => {
169
+ if (this.#closed || this.#terminals.get(key) !== terminal)
170
+ return;
171
+ if (!page.entries.some((entry) => entry.kind === 'event' && entry.event.eventId === terminal.eventId))
172
+ return;
173
+ this.#terminals.delete(key);
174
+ if (this.#latestTerminal.get(sessionId) === key)
175
+ this.#latestTerminal.delete(sessionId);
176
+ this.#releaseSnapshot(sessionId, executionId);
177
+ })
178
+ .catch(() => {
179
+ /* Retain the observation if persistence cannot be confirmed. */
180
+ });
181
+ }
182
+ rememberReceipt(sessionId, receipt) {
183
+ if (!this.#closed)
184
+ this.#receipts.set(sessionId, receipt);
185
+ }
186
+ receipt(sessionId) {
187
+ return this.#receipts.get(sessionId);
188
+ }
189
+ capture(sessionId, executionId) {
190
+ const items = [...this.#items.entries()].filter(([, item]) => item.sessionId === sessionId &&
191
+ (executionId === undefined || item.executionId === executionId));
192
+ const first = items[0]?.[1];
193
+ if (!first)
194
+ return undefined;
195
+ const captured = items.filter(([, item]) => item.executionId === first.executionId);
196
+ const snapshotKey = JSON.stringify([sessionId, first.executionId]);
197
+ const snapshot = captured.some(([, item]) => item.kind === 'terminal')
198
+ ? publicChildSnapshot(this.#snapshots.get(snapshotKey))
199
+ : {};
200
+ return {
201
+ sessionId,
202
+ executionId: first.executionId,
203
+ snapshot,
204
+ updates: captured.map(([, item]) => ({
205
+ kind: item.kind,
206
+ value: item.value,
207
+ ...(item.final ? { final: true } : {}),
208
+ ...(item.truncated ? { truncated: true } : {}),
209
+ ...(item.errorCode === undefined ? {} : { errorCode: item.errorCode }),
210
+ ...(item.errorMessage === undefined ? {} : { errorMessage: item.errorMessage }),
211
+ ...(item.permissionStop === undefined ? {} : { permissionStop: item.permissionStop })
212
+ })),
213
+ acknowledge: () => {
214
+ for (const [key, item] of captured) {
215
+ if (this.#items.get(key)?.revision !== item.revision)
216
+ continue;
217
+ this.#items.delete(key);
218
+ if (item.kind === 'terminal')
219
+ this.claimTerminal(item.sessionId, item.executionId);
220
+ }
221
+ if (captured.some(([, item]) => item.kind === 'terminal'))
222
+ this.#verifyTerminal(sessionId, first.executionId);
223
+ }
224
+ };
225
+ }
226
+ /** Capture the currently unread versions without consuming later events. */
227
+ acknowledgement(sessionId) {
228
+ const captured = [...this.#items.entries()].filter(([, item]) => item.sessionId === sessionId);
229
+ return () => {
230
+ for (const [key, item] of captured) {
231
+ if (this.#items.get(key)?.revision !== item.revision)
232
+ continue;
233
+ this.#items.delete(key);
234
+ if (item.kind === 'terminal')
235
+ this.claimTerminal(item.sessionId, item.executionId);
236
+ }
237
+ for (const [, item] of captured) {
238
+ if (item.kind !== 'terminal' ||
239
+ [...this.#items.values()].some((remaining) => remaining.sessionId === item.sessionId && remaining.executionId === item.executionId))
240
+ continue;
241
+ this.#verifyTerminal(item.sessionId, item.executionId);
242
+ }
243
+ };
244
+ }
245
+ /** Claim a terminal projection once per warm parent, including restored history. */
246
+ claimTerminal(sessionId, executionId) {
247
+ const key = JSON.stringify([sessionId, executionId]);
248
+ if (this.#consumedTerminals.has(key))
249
+ return false;
250
+ this.#consumedTerminals.add(key);
251
+ return true;
252
+ }
253
+ collect(signal) {
254
+ signal.throwIfAborted();
255
+ const ids = [...new Set([...this.#items.values()].map((item) => item.sessionId))].slice(0, 8);
256
+ return ids.flatMap((id) => {
257
+ const item = this.capture(id);
258
+ if (!item)
259
+ return [];
260
+ const updates = item.snapshot.summary && !item.snapshot.summaryTruncated
261
+ ? item.updates.filter((update) => !('final' in update && update.final))
262
+ : item.updates;
263
+ const content = boundedObservation({
264
+ agentId: id,
265
+ executionId: item.executionId,
266
+ ...item.snapshot,
267
+ updates
268
+ });
269
+ return [{ content, acknowledge: item.acknowledge }];
270
+ });
271
+ }
272
+ close() {
273
+ if (this.#closed)
274
+ return;
275
+ this.#closed = true;
276
+ for (const unsubscribe of this.#subscriptions.values())
277
+ unsubscribe();
278
+ this.#subscriptions.clear();
279
+ this.#listeners.clear();
280
+ this.#items.clear();
281
+ this.#snapshots.clear();
282
+ this.#terminals.clear();
283
+ this.#latestTerminal.clear();
284
+ this.#consumedTerminals.clear();
285
+ this.#receipts.clear();
286
+ }
287
+ }
288
+ function isTerminal(event) {
289
+ return (event.type === 'execution.completed' ||
290
+ event.type === 'execution.cancelled' ||
291
+ event.type === 'execution.failed');
292
+ }
293
+ function boundedObservation(input) {
294
+ const data = {
295
+ ...input,
296
+ ...(input.changedFiles ? { changedFiles: [...input.changedFiles] } : {}),
297
+ updates: input.updates.map((update) => ({ ...update }))
298
+ };
299
+ let updatesTruncated = false;
300
+ const render = () => `<mar_subagent_update>\n${JSON.stringify({
301
+ ...data,
302
+ ...(updatesTruncated ? { updatesTruncated: true } : {})
303
+ }).replace(/[<>&]/g, (character) => character === '<' ? '\\u003c' : character === '>' ? '\\u003e' : '\\u0026')}\n</mar_subagent_update>`;
304
+ const tooLarge = () => Buffer.byteLength(render(), 'utf8') > 32768;
305
+ while (tooLarge() && data.changedFiles?.length) {
306
+ data.changedFiles.pop();
307
+ data.changedFilesTruncated = true;
308
+ }
309
+ const shorten = (value, set) => {
310
+ let low = 0;
311
+ let high = Buffer.byteLength(value, 'utf8');
312
+ while (low < high) {
313
+ const middle = Math.ceil((low + high) / 2);
314
+ set(boundedUtf8(value, middle).value);
315
+ if (tooLarge())
316
+ high = middle - 1;
317
+ else
318
+ low = middle;
319
+ }
320
+ set(boundedUtf8(value, low).value);
321
+ };
322
+ if (tooLarge() && data.summary !== undefined) {
323
+ const summary = data.summary;
324
+ data.summaryTruncated = true;
325
+ shorten(summary, (value) => {
326
+ data.summary = value;
327
+ });
328
+ }
329
+ for (const update of data.updates) {
330
+ if (!tooLarge())
331
+ break;
332
+ const value = update.value;
333
+ update.truncated = true;
334
+ shorten(value, (text) => {
335
+ update.value = text;
336
+ });
337
+ }
338
+ while (tooLarge() && data.updates.length) {
339
+ data.updates.pop();
340
+ updatesTruncated = true;
341
+ }
342
+ if (tooLarge()) {
343
+ const fallback = {
344
+ agentId: boundedUtf8(data.agentId, 512).value,
345
+ executionId: boundedUtf8(data.executionId, 512).value,
346
+ detailsTruncated: true
347
+ };
348
+ return `<mar_subagent_update>\n${JSON.stringify(fallback).replace(/[<>&]/g, (character) => character === '<' ? '\\u003c' : character === '>' ? '\\u003e' : '\\u0026')}\n</mar_subagent_update>`;
349
+ }
350
+ return render();
351
+ }
@@ -0,0 +1,4 @@
1
+ import { type Agent } from '@myagentroam/agent';
2
+ import type { SessionExtensionContext } from '@myagentroam/agent/extensions';
3
+ /** Read-only local discovery uses the Core catalog and never opens historical Sessions. */
4
+ export declare function registerQueryTools(context: SessionExtensionContext, agent: Agent): void;
@@ -0,0 +1,114 @@
1
+ import { AgentError } from '@myagentroam/agent';
2
+ /** Read-only local discovery uses the Core catalog and never opens historical Sessions. */
3
+ export function registerQueryTools(context, agent) {
4
+ context.tools.register({
5
+ name: 'agent_list',
6
+ description: 'List direct child Sessions or eligible local models.',
7
+ allowedCallers: ['direct', 'programmatic'],
8
+ inputSchema: {
9
+ type: 'object',
10
+ properties: {
11
+ scope: { type: 'string', enum: ['children', 'profile'] },
12
+ limit: { type: 'integer', minimum: 1, maximum: 100 },
13
+ cursor: { type: 'string', minLength: 1 }
14
+ },
15
+ additionalProperties: false
16
+ },
17
+ async execute(raw) {
18
+ const input = raw;
19
+ const current = await agent.getSession(context.sessionId);
20
+ const data = input.scope === 'profile'
21
+ ? {
22
+ models: (context.models?.list() ?? []).filter((model) => model.conversationModel && model.subagentModel)
23
+ }
24
+ : await agent
25
+ .listSessions({
26
+ parentSessionId: context.sessionId,
27
+ ...(input.limit === undefined ? {} : { limit: input.limit }),
28
+ ...(input.cursor === undefined ? {} : { cursor: input.cursor })
29
+ })
30
+ .then((page) => ({
31
+ current: {
32
+ agentId: current.id,
33
+ parentSessionId: current.source.parentSessionId ?? null
34
+ },
35
+ agents: page.sessions
36
+ .filter((session) => session.workspace === current.workspace)
37
+ .map((session) => ({
38
+ agentId: session.id,
39
+ title: session.title,
40
+ parentSessionId: session.source.parentSessionId,
41
+ modelId: session.modelId,
42
+ lastActivityAt: session.lastActivityAt
43
+ })),
44
+ ...(page.nextCursor ? { nextCursor: page.nextCursor } : {})
45
+ }));
46
+ return { data, content: JSON.stringify(data) };
47
+ }
48
+ });
49
+ context.tools.register({
50
+ name: 'agent_view',
51
+ description: 'View a direct child or parent Session and its public history.',
52
+ allowedCallers: ['direct', 'programmatic'],
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ agentId: { type: 'string', minLength: 1 },
57
+ includeHistory: { type: 'boolean' },
58
+ limit: { type: 'integer', minimum: 1, maximum: 100 },
59
+ cursor: { type: 'string', minLength: 1 }
60
+ },
61
+ required: ['agentId'],
62
+ additionalProperties: false
63
+ },
64
+ async execute(raw) {
65
+ const input = raw;
66
+ const [actor, session] = await Promise.all([
67
+ agent.getSession(context.sessionId),
68
+ agent.getSession(input.agentId)
69
+ ]);
70
+ if (actor.workspace !== session.workspace ||
71
+ (session.id !== actor.id &&
72
+ session.source.parentSessionId !== actor.id &&
73
+ actor.source.parentSessionId !== session.id))
74
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
75
+ const history = input.includeHistory
76
+ ? await agent.sessionHistory(session.id, {
77
+ ...(input.limit === undefined ? {} : { limit: input.limit }),
78
+ ...(input.cursor === undefined ? {} : { cursor: input.cursor })
79
+ })
80
+ : undefined;
81
+ const data = {
82
+ agentId: session.id,
83
+ title: session.title,
84
+ parentSessionId: session.source.parentSessionId ?? null,
85
+ modelId: session.modelId,
86
+ reasoningEffort: session.reasoningEffort,
87
+ lastActivityAt: session.lastActivityAt,
88
+ ...(history
89
+ ? {
90
+ history: {
91
+ entries: history.entries.flatMap((entry) => {
92
+ if (entry.kind === 'user')
93
+ return [{ kind: 'user', sequence: entry.sequence, text: entry.text }];
94
+ if (entry.event.type === 'message.completed')
95
+ return [
96
+ {
97
+ kind: 'message',
98
+ sequence: entry.sequence,
99
+ text: entry.event.text,
100
+ phase: entry.event.phase,
101
+ executionId: entry.event.executionId
102
+ }
103
+ ];
104
+ return [];
105
+ }),
106
+ ...(history.nextCursor ? { nextCursor: history.nextCursor } : {})
107
+ }
108
+ }
109
+ : {})
110
+ };
111
+ return { data, content: JSON.stringify(data) };
112
+ }
113
+ });
114
+ }
@@ -0,0 +1,3 @@
1
+ import { type Agent } from '@myagentroam/agent';
2
+ /** Project live direct children first, followed by the sixteen most recently active terminals. */
3
+ export declare function childResources(agent: Agent, parentId: string, signal: AbortSignal, disposed: () => boolean, history?: Pick<Agent, 'sessionHistory'>): Promise<string[]>;
@@ -0,0 +1,112 @@
1
+ import { AgentError } from '@myagentroam/agent';
2
+ import { historicalExecutionStatus } from './history.js';
3
+ /** Project live direct children first, followed by the sixteen most recently active terminals. */
4
+ export async function childResources(agent, parentId, signal, disposed, history = agent) {
5
+ signal.throwIfAborted();
6
+ const parent = await agent.getSession(parentId);
7
+ signal.throwIfAborted();
8
+ if (disposed())
9
+ return [];
10
+ const live = new Map();
11
+ const registration = agent.observeSessionExecutions((session) => {
12
+ if (session.source.parentSessionId === parentId && session.workspace === parent.workspace)
13
+ live.set(session.id, session);
14
+ });
15
+ try {
16
+ for (const session of registration.sessions)
17
+ if (session.source.parentSessionId === parentId && session.workspace === parent.workspace)
18
+ live.set(session.id, session);
19
+ const terminals = [];
20
+ let cursor;
21
+ let truncated = false;
22
+ do {
23
+ const page = await agent.listSessions({
24
+ parentSessionId: parentId,
25
+ limit: 100,
26
+ ...(cursor === undefined ? {} : { cursor })
27
+ });
28
+ signal.throwIfAborted();
29
+ if (disposed())
30
+ return [];
31
+ for (const [index, session] of page.sessions.entries()) {
32
+ if (session.workspace !== parent.workspace)
33
+ continue;
34
+ const knownLive = live.get(session.id);
35
+ if (knownLive) {
36
+ const snapshot = knownLive.subscribe(() => { });
37
+ snapshot.unsubscribe();
38
+ if (snapshot.activeExecutionId && !snapshot.terminalEvent)
39
+ continue;
40
+ }
41
+ let state;
42
+ try {
43
+ state = await historicalExecutionStatus(agent, session.id, signal, { history });
44
+ }
45
+ catch (error) {
46
+ if (error instanceof AgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND')
47
+ continue;
48
+ // A concurrent or foreign owner is not evidence of an interrupted Execution.
49
+ // Leave its state to an explicit query instead of breaking the parent context.
50
+ if (!(error instanceof AgentError) || error.code !== 'MAR_AGENT_SESSION_LOCKED')
51
+ throw error;
52
+ truncated = true;
53
+ continue;
54
+ }
55
+ if (disposed())
56
+ return [];
57
+ if (state.status === 'idle')
58
+ continue;
59
+ terminals.push({
60
+ agentId: session.id,
61
+ status: state.status,
62
+ description: session.source.type === 'subagent' ? session.source.description : session.title
63
+ });
64
+ if (terminals.length === 16) {
65
+ truncated ||= index + 1 < page.sessions.length || !!page.nextCursor;
66
+ break;
67
+ }
68
+ }
69
+ cursor = terminals.length === 16 ? undefined : page.nextCursor;
70
+ } while (cursor);
71
+ const active = [];
72
+ for (const session of live.values()) {
73
+ signal.throwIfAborted();
74
+ if (disposed())
75
+ return [];
76
+ const snapshot = session.subscribe(() => { });
77
+ snapshot.unsubscribe();
78
+ if (!snapshot.activeExecutionId || snapshot.terminalEvent)
79
+ continue;
80
+ let summary;
81
+ try {
82
+ summary = await agent.getSession(session.id);
83
+ }
84
+ catch (error) {
85
+ if (error instanceof AgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND')
86
+ continue;
87
+ throw error;
88
+ }
89
+ signal.throwIfAborted();
90
+ if (disposed())
91
+ return [];
92
+ active.push({
93
+ agentId: session.id,
94
+ status: 'running',
95
+ description: summary.source.type === 'subagent' ? summary.source.description : summary.title
96
+ });
97
+ }
98
+ const activeIds = new Set(active.map((child) => child.agentId));
99
+ const children = [...active, ...terminals.filter((child) => !activeIds.has(child.agentId))];
100
+ return children.length || truncated
101
+ ? [
102
+ `Child Sessions (use agent_list for details): ${JSON.stringify({
103
+ children,
104
+ truncated
105
+ }).replace(/[<>&]/g, (c) => (c === '<' ? '\\u003c' : c === '>' ? '\\u003e' : '\\u0026'))}`
106
+ ]
107
+ : [];
108
+ }
109
+ finally {
110
+ registration.unsubscribe();
111
+ }
112
+ }
@@ -0,0 +1,77 @@
1
+ import type { AgentEvent } from '@myagentroam/agent';
2
+ /** A bounded projection of one execution's public events, not an execution owner. */
3
+ export type ChildSnapshot = {
4
+ summary?: string;
5
+ summaryTruncated?: boolean;
6
+ turnId?: string;
7
+ changedFiles: string[];
8
+ changedFilesTruncated?: boolean;
9
+ latestMessage?: {
10
+ itemId: string;
11
+ turnId: string;
12
+ phase: 'commentary' | 'final_answer';
13
+ text: string;
14
+ timestamp: string;
15
+ truncated: boolean;
16
+ };
17
+ latestActivity?: {
18
+ kind: 'message';
19
+ timestamp: string;
20
+ phase: 'commentary' | 'final_answer';
21
+ } | {
22
+ kind: 'tool';
23
+ timestamp: string;
24
+ toolName: string;
25
+ status: 'started' | 'completed' | 'failed';
26
+ };
27
+ verification?: string[];
28
+ evidence?: Array<{
29
+ path: string;
30
+ startLine?: number;
31
+ endLine?: number;
32
+ }>;
33
+ };
34
+ export declare function boundedUtf8(value: string, maxBytes: number): {
35
+ value: string;
36
+ truncated: boolean;
37
+ };
38
+ export declare function projectChildEvent(snapshot: ChildSnapshot, event: AgentEvent, full?: boolean): void;
39
+ export declare function publicChildSnapshot(snapshot: ChildSnapshot | undefined): {
40
+ changedFilesTruncated?: boolean;
41
+ changedFiles?: string[];
42
+ turnId?: string;
43
+ summaryTruncated?: boolean;
44
+ summary?: string;
45
+ };
46
+ /** Full native details are never included in model-facing projections. */
47
+ export declare function nativeChildSnapshot(snapshot: ChildSnapshot | undefined): {
48
+ evidence?: {
49
+ path: string;
50
+ startLine?: number;
51
+ endLine?: number;
52
+ }[];
53
+ verification?: string[];
54
+ latestActivity?: {
55
+ kind: "message";
56
+ timestamp: string;
57
+ phase: "commentary" | "final_answer";
58
+ } | {
59
+ kind: "tool";
60
+ timestamp: string;
61
+ toolName: string;
62
+ status: "started" | "completed" | "failed";
63
+ };
64
+ latestMessage?: {
65
+ itemId: string;
66
+ turnId: string;
67
+ phase: "commentary" | "final_answer";
68
+ text: string;
69
+ timestamp: string;
70
+ truncated: boolean;
71
+ };
72
+ changedFilesTruncated?: boolean;
73
+ changedFiles?: string[];
74
+ turnId?: string;
75
+ summaryTruncated?: boolean;
76
+ summary?: string;
77
+ };