@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,453 @@
1
+ import { AgentError } from '@myagentroam/agent';
2
+ import { waitForToolCondition } from '@myagentroam/agent/extensions';
3
+ import { target } from './control.js';
4
+ import { boundedUtf8, nativeChildSnapshot, projectChildEvent, publicChildSnapshot } from './snapshot.js';
5
+ import { modelWaitAgent } from './wait-output.js';
6
+ function terminalStatus(type) {
7
+ if (type === 'execution.completed')
8
+ return 'completed';
9
+ if (type === 'execution.failed')
10
+ return 'failed';
11
+ if (type === 'execution.cancelled')
12
+ return 'cancelled';
13
+ return undefined;
14
+ }
15
+ function terminalFact(event, full = false) {
16
+ const status = terminalStatus(event?.type);
17
+ if (!status || !event)
18
+ return undefined;
19
+ return {
20
+ status,
21
+ ...(event.type === 'execution.failed'
22
+ ? {
23
+ errorCode: event.code,
24
+ ...(event.message === undefined
25
+ ? {}
26
+ : { errorMessage: full ? event.message : boundedUtf8(event.message, 1000).value })
27
+ }
28
+ : {}),
29
+ ...(event.type === 'execution.completed' && event.permissionStop !== undefined
30
+ ? {
31
+ permissionStop: full
32
+ ? event.permissionStop
33
+ : {
34
+ ...event.permissionStop,
35
+ reason: boundedUtf8(event.permissionStop.reason, 1000).value
36
+ }
37
+ }
38
+ : {})
39
+ };
40
+ }
41
+ async function captureTarget(agent, session, observations, signal, history) {
42
+ observations.observe(session);
43
+ let executionId;
44
+ let terminal;
45
+ let projection = { changedFiles: [] };
46
+ const listeners = new Set();
47
+ const snapshot = session.subscribe((event) => {
48
+ if (event.executionId !== executionId)
49
+ return;
50
+ projectChildEvent(projection, event, observations.full);
51
+ const fact = terminalFact(event, observations.full);
52
+ if (!fact)
53
+ return;
54
+ terminal = fact;
55
+ for (const listener of listeners)
56
+ listener();
57
+ });
58
+ executionId = snapshot.activeExecutionId;
59
+ if (executionId)
60
+ projection = observations.snapshot(session.id, executionId);
61
+ terminal = terminalFact(snapshot.terminalEvent, observations.full);
62
+ if (executionId)
63
+ return {
64
+ session,
65
+ executionId,
66
+ startedBeforeTerminal: !terminal,
67
+ terminal: () => terminal,
68
+ snapshot: () => projection,
69
+ subscribe: (listener) => {
70
+ listeners.add(listener);
71
+ return () => {
72
+ listeners.delete(listener);
73
+ };
74
+ },
75
+ close: () => {
76
+ listeners.clear();
77
+ snapshot.unsubscribe();
78
+ }
79
+ };
80
+ snapshot.unsubscribe();
81
+ let cursor;
82
+ let last;
83
+ const pages = [];
84
+ do {
85
+ signal.throwIfAborted();
86
+ const page = await history.sessionHistory(session.id, {
87
+ limit: 100,
88
+ ...(cursor === undefined ? {} : { cursor })
89
+ });
90
+ signal.throwIfAborted();
91
+ if (!last)
92
+ last = page.entries
93
+ .flatMap((entry) => (entry.kind === 'event' ? [entry.event] : []))
94
+ .findLast((event) => event.type === 'execution.started' || terminalStatus(event.type));
95
+ const events = page.entries.flatMap((entry) => entry.kind === 'event' && entry.event.executionId === last?.executionId ? [entry.event] : []);
96
+ pages.push(events);
97
+ cursor = events.some((event) => event.type === 'execution.started')
98
+ ? undefined
99
+ : page.nextCursor;
100
+ } while (cursor);
101
+ const observed = observations.terminal(session.id);
102
+ if (observed &&
103
+ (observed.executionId !== last?.executionId || last.type === 'execution.started')) {
104
+ const fact = terminalFact(observed, observations.full);
105
+ return {
106
+ session,
107
+ executionId: observed.executionId,
108
+ terminal: () => fact,
109
+ snapshot: () => observations.snapshot(session.id, observed.executionId),
110
+ subscribe: () => () => { },
111
+ close: () => { }
112
+ };
113
+ }
114
+ if (last &&
115
+ (terminalStatus(last.type) ||
116
+ (last.type === 'execution.started' &&
117
+ (await agent.sessionExecutionWasInterrupted(session.id, last.executionId, { signal }))))) {
118
+ for (const events of pages.reverse())
119
+ for (const event of events)
120
+ projectChildEvent(projection, event, observations.full);
121
+ const finalEvent = last;
122
+ const fact = finalEvent.type === 'execution.started'
123
+ ? { status: 'interrupted' }
124
+ : terminalFact(finalEvent, observations.full);
125
+ return {
126
+ session,
127
+ executionId: finalEvent.executionId,
128
+ terminal: () => fact,
129
+ snapshot: () => projection,
130
+ subscribe: () => () => { },
131
+ close: () => { }
132
+ };
133
+ }
134
+ throw new AgentError('MAR_AGENT_SUBAGENT_EXECUTION_NOT_FOUND', 'Session has no Execution to wait for.');
135
+ }
136
+ /** A foreground wait observes fixed Execution identities, never owning or cancelling them. */
137
+ export function registerWait(context, agent, observations, history = agent) {
138
+ context.tools.register({
139
+ name: 'agent_wait',
140
+ description: 'Wait for direct child completion or a message; defaults to completion for 30 seconds.',
141
+ allowedCallers: ['direct', 'programmatic'],
142
+ inputSchema: {
143
+ type: 'object',
144
+ properties: {
145
+ agentId: { type: ['string', 'null'], minLength: 1 },
146
+ agentIds: {
147
+ type: ['array', 'null'],
148
+ minItems: 1,
149
+ maxItems: 8,
150
+ uniqueItems: true,
151
+ items: { type: 'string', minLength: 1 }
152
+ },
153
+ mode: { type: ['string', 'null'], enum: ['any', 'all', null] },
154
+ waitFor: { type: ['string', 'null'], enum: ['message', 'completion', null] },
155
+ waitMs: { type: ['integer', 'null'], minimum: 0, maximum: 300000 }
156
+ },
157
+ additionalProperties: false
158
+ },
159
+ async execute(raw, call) {
160
+ const value = raw;
161
+ const input = {
162
+ agentId: value.agentId ?? undefined,
163
+ agentIds: value.agentIds ?? undefined,
164
+ mode: value.mode ?? undefined,
165
+ waitFor: value.waitFor ?? undefined,
166
+ waitMs: value.waitMs ?? undefined
167
+ };
168
+ return waitForChild(context, agent, observations, input, call, 'model', observations, history);
169
+ }
170
+ });
171
+ }
172
+ /** Model and native callers share target capture and acknowledgement, not their limits or cursors. */
173
+ export async function waitForChild(context, agent, observations, input, call, mode, receipts = observations, history = agent) {
174
+ call.signal.throwIfAborted();
175
+ if ((input.agentId !== undefined && input.agentIds !== undefined) ||
176
+ (input.mode !== undefined && input.agentIds === undefined) ||
177
+ (input.agentIds &&
178
+ (!input.agentIds.length ||
179
+ (mode === 'model' && input.agentIds.length > 8) ||
180
+ new Set(input.agentIds).size !== input.agentIds.length)))
181
+ throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Invalid wait targets.');
182
+ if (input.untilSettled && (mode !== 'native' || !input.agentId || input.waitFor === 'message'))
183
+ throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'An unbounded wait requires one completion target.');
184
+ if (input.waitMs !== undefined &&
185
+ (!Number.isSafeInteger(input.waitMs) ||
186
+ input.waitMs < 0 ||
187
+ (mode === 'model' && input.waitMs > 300000)))
188
+ throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Invalid wait duration.');
189
+ const single = input.agentIds === undefined;
190
+ let ids = input.agentIds ?? (input.agentId ? [input.agentId] : undefined);
191
+ if (!ids) {
192
+ const parent = await agent.getSession(context.sessionId);
193
+ call.signal.throwIfAborted();
194
+ const registration = agent.observeSessionExecutions(() => { });
195
+ ids = registration.sessions
196
+ .filter((session) => session.source.parentSessionId === context.sessionId &&
197
+ session.workspace === parent.workspace)
198
+ .map((session) => session.id);
199
+ registration.unsubscribe();
200
+ if (!ids.length) {
201
+ const candidates = [];
202
+ let cursor;
203
+ do {
204
+ call.signal.throwIfAborted();
205
+ const page = await agent.listSessions({
206
+ parentSessionId: context.sessionId,
207
+ limit: 100,
208
+ ...(cursor === undefined ? {} : { cursor })
209
+ });
210
+ call.signal.throwIfAborted();
211
+ candidates.push(...page.sessions
212
+ .filter((session) => session.workspace === parent.workspace)
213
+ .map(({ id, createdAt }) => ({ id, createdAt })));
214
+ cursor = page.nextCursor;
215
+ } while (cursor);
216
+ candidates.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
217
+ for (const candidate of candidates) {
218
+ call.signal.throwIfAborted();
219
+ try {
220
+ const child = await agent.getSession(candidate.id);
221
+ if (child.source.parentSessionId === context.sessionId &&
222
+ child.workspace === parent.workspace) {
223
+ ids = [candidate.id];
224
+ break;
225
+ }
226
+ }
227
+ catch (error) {
228
+ if (!(error instanceof AgentError) || error.code !== 'MAR_AGENT_SESSION_NOT_FOUND')
229
+ throw error;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ if (!ids.length)
235
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'No direct child Session is available.');
236
+ const sessions = await Promise.all(ids.map(async (id) => {
237
+ const result = await target(agent, context.sessionId, id);
238
+ if (!result.child)
239
+ throw new AgentError('MAR_AGENT_SUBAGENT_NOT_FOUND', 'Session is not available.');
240
+ const source = result.summary.source;
241
+ return {
242
+ session: result.session,
243
+ metadata: {
244
+ description: source.type === 'subagent' ? source.description : result.summary.title,
245
+ ...(source.type === 'subagent'
246
+ ? {
247
+ role: source.role,
248
+ ...(source.extensionId === undefined ? {} : { extensionId: source.extensionId })
249
+ }
250
+ : {}),
251
+ ...(result.summary.modelId === undefined ? {} : { modelId: result.summary.modelId }),
252
+ ...(result.summary.modelName === undefined
253
+ ? {}
254
+ : { modelName: result.summary.modelName }),
255
+ ...(result.summary.reasoningEffort === undefined
256
+ ? {}
257
+ : { reasoningEffort: result.summary.reasoningEffort })
258
+ }
259
+ };
260
+ }));
261
+ const targets = [];
262
+ const receiptIdentities = [];
263
+ try {
264
+ for (const { session, metadata } of sessions) {
265
+ const captured = await captureTarget(agent, session, observations, call.signal, history);
266
+ targets.push({ ...captured, metadata });
267
+ const identity = receipts.receipt(session.id);
268
+ const historical = mode === 'model' && !identity && captured.executionId
269
+ ? await agent.sessionInputReceipt(session.id, {
270
+ executionId: captured.executionId,
271
+ source: {
272
+ sessionId: context.sessionId,
273
+ extensionId: 'subagent',
274
+ actors: ['model', 'code-mode']
275
+ },
276
+ signal: call.signal
277
+ })
278
+ : undefined;
279
+ receiptIdentities.push(identity ??
280
+ (historical && {
281
+ clientMessageId: historical.clientMessageId,
282
+ executionId: historical.executionId
283
+ }));
284
+ }
285
+ call.signal.throwIfAborted();
286
+ const waitFor = input.waitFor ?? 'completion';
287
+ const read = (item) => {
288
+ const observation = item.executionId
289
+ ? observations.capture(item.session.id, item.executionId)
290
+ : undefined;
291
+ const snapshot = item.session.subscribe(() => { });
292
+ snapshot.unsubscribe();
293
+ const observed = observation?.updates.find((update) => update.kind === 'terminal');
294
+ const observedStatus = terminalStatus(observed?.value);
295
+ const terminal = item.terminal() ??
296
+ (snapshot.activeExecutionId === item.executionId
297
+ ? terminalFact(snapshot.terminalEvent)
298
+ : undefined) ??
299
+ (observedStatus === undefined
300
+ ? undefined
301
+ : {
302
+ status: observedStatus,
303
+ ...(observed?.errorCode === undefined ? {} : { errorCode: observed.errorCode }),
304
+ ...(observed?.errorMessage === undefined
305
+ ? {}
306
+ : { errorMessage: observed.errorMessage }),
307
+ ...(observed?.permissionStop === undefined
308
+ ? {}
309
+ : { permissionStop: observed.permissionStop })
310
+ });
311
+ const message = observation?.updates.some((update) => update.kind === 'message') === true;
312
+ return { observation, terminal, ready: !!terminal || (waitFor === 'message' && message) };
313
+ };
314
+ const ready = () => {
315
+ const items = targets.map(read);
316
+ return input.mode === 'all'
317
+ ? items.every((item) => item.ready)
318
+ : items.some((item) => item.ready);
319
+ };
320
+ const duration = input.untilSettled
321
+ ? Number.MAX_SAFE_INTEGER
322
+ : (input.waitMs ?? (mode === 'model' ? 30000 : 0));
323
+ let outcome = 'snapshot';
324
+ if (duration !== 0)
325
+ outcome = await waitForToolCondition({
326
+ ready,
327
+ subscribe: (listener) => {
328
+ const releases = [
329
+ observations.wake.subscribe(listener),
330
+ ...targets.map((target) => target.subscribe(listener))
331
+ ];
332
+ return () => {
333
+ for (const release of releases)
334
+ release();
335
+ };
336
+ },
337
+ waitMs: mode === 'model' ? Math.max(10000, duration) : duration,
338
+ signal: call.signal,
339
+ ...(mode === 'model' ? { interrupt: call.waitInterrupt } : {})
340
+ });
341
+ call.signal.throwIfAborted();
342
+ const values = targets.map((item) => ({ item, ...read(item) }));
343
+ const selected = single
344
+ ? [
345
+ values.find((value) => value.ready) ??
346
+ values.find((value) => value.observation) ??
347
+ values[0]
348
+ ]
349
+ : values;
350
+ const receiptFacts = await Promise.all(selected.map(async ({ item }) => {
351
+ const identity = receiptIdentities[targets.indexOf(item)];
352
+ if (!identity)
353
+ return undefined;
354
+ const fact = await agent.sessionInputReceipt(item.session.id, identity);
355
+ return fact &&
356
+ (fact.executionId === item.executionId || fact.appliedExecutionId === item.executionId)
357
+ ? fact
358
+ : undefined;
359
+ }));
360
+ call.signal.throwIfAborted();
361
+ const agents = selected.map(({ item, observation, terminal, ready: isReady }, index) => {
362
+ const waitOutcome = outcome === 'snapshot'
363
+ ? 'snapshot'
364
+ : terminal
365
+ ? 'completion'
366
+ : isReady
367
+ ? 'message'
368
+ : outcome === 'interrupted'
369
+ ? 'interrupted'
370
+ : outcome === 'timeout'
371
+ ? 'timeout'
372
+ : 'snapshot';
373
+ const include = outcome !== 'snapshot' && (isReady || outcome === 'timeout' || outcome === 'interrupted');
374
+ const freshTerminal = include &&
375
+ terminal &&
376
+ item.executionId &&
377
+ (observations.claimTerminal(item.session.id, item.executionId) ||
378
+ item.startedBeforeTerminal);
379
+ const progress = mode === 'native' && include && !terminal && observation
380
+ ? nativeChildSnapshot(item.snapshot())
381
+ : undefined;
382
+ const projection = freshTerminal
383
+ ? mode === 'native'
384
+ ? nativeChildSnapshot(item.snapshot())
385
+ : publicChildSnapshot(item.snapshot())
386
+ : progress
387
+ ? {
388
+ ...(progress.latestMessage ? { latestMessage: progress.latestMessage } : {}),
389
+ ...(progress.latestActivity ? { latestActivity: progress.latestActivity } : {})
390
+ }
391
+ : {};
392
+ const updates = mode === 'model' &&
393
+ 'summary' in projection &&
394
+ projection.summary &&
395
+ !projection.summaryTruncated
396
+ ? observation?.updates.filter((update) => !('final' in update && update.final))
397
+ : observation?.updates;
398
+ if (include)
399
+ observation?.acknowledge();
400
+ return {
401
+ agentId: item.session.id,
402
+ ...(item.executionId ? { executionId: item.executionId } : {}),
403
+ ...(mode === 'native' ? item.metadata : {}),
404
+ status: terminal?.status ?? 'running',
405
+ waitOutcome,
406
+ ...(terminal?.errorCode === undefined ? {} : { errorCode: terminal.errorCode }),
407
+ ...(terminal?.errorMessage === undefined ? {} : { errorMessage: terminal.errorMessage }),
408
+ ...(terminal?.permissionStop === undefined
409
+ ? {}
410
+ : { permissionStop: terminal.permissionStop }),
411
+ ...projection,
412
+ ...(receiptFacts[index] ? { receipt: receiptFacts[index] } : {}),
413
+ ...(include && updates?.length ? { updates } : {})
414
+ };
415
+ });
416
+ const groupOutcome = outcome === 'ready'
417
+ ? values.filter((value) => value.ready).every((value) => value.terminal)
418
+ ? 'completion'
419
+ : 'message'
420
+ : outcome;
421
+ const data = single
422
+ ? {
423
+ ...agents[0],
424
+ waitFor,
425
+ ...(groupOutcome === 'interrupted' ? { waitOutcome: 'interrupted', reason: 'steer' } : {})
426
+ }
427
+ : {
428
+ mode: input.mode ?? 'any',
429
+ waitFor,
430
+ waitOutcome: groupOutcome,
431
+ ...(groupOutcome === 'interrupted' ? { reason: 'steer' } : {}),
432
+ agents,
433
+ pendingAgentIds: values
434
+ .filter((value) => !value.ready)
435
+ .map((value) => value.item.session.id)
436
+ };
437
+ if (mode === 'native')
438
+ return { data, content: JSON.stringify(data) };
439
+ const modelAgents = agents.map((item) => modelWaitAgent(item, Math.floor(34000 / agents.length)));
440
+ const modelData = single
441
+ ? {
442
+ ...modelAgents[0],
443
+ waitFor,
444
+ ...(groupOutcome === 'interrupted' ? { waitOutcome: 'interrupted', reason: 'steer' } : {})
445
+ }
446
+ : { ...data, agents: modelAgents };
447
+ return { data, content: JSON.stringify(modelData) };
448
+ }
449
+ finally {
450
+ for (const item of targets)
451
+ item.close();
452
+ }
453
+ }
@@ -3,7 +3,6 @@ import type { ExtensionPromptSection } from './prompt-composition.js';
3
3
  import type { ExtensionCapabilitySnapshot } from './capabilities.js';
4
4
  import type { ExtensionPromptComposition } from './prompt-composition.js';
5
5
  import type { ExtensionModelsApi } from './models.js';
6
- import type { ExtensionAgentsApi } from './agents.js';
7
6
  import type { ExtensionDataApi } from './data.js';
8
7
  import type { ExtensionMcpApi } from './mcp.js';
9
8
  import type { ExtensionFilesApi } from './files.js';
@@ -18,7 +17,6 @@ import type { ExtensionOperationContext } from './operation.js';
18
17
  import type { ExtensionDisposer, ExtensionResourceRegistration, ExtensionResourceScope } from './resources.js';
19
18
  import type { ExtensionServiceRegistry } from './services.js';
20
19
  import type { ExtensionToolRef, ExtensionToolInvocation, ExtensionToolCheck, ExtensionToolSpec } from './tools.js';
21
- import type { DomainAgentOperations, DomainAgentFacts } from './agents.js';
22
20
  import type { DomainCompactOperations, DomainCompactFacts } from './compaction.js';
23
21
  import type { DomainEnvironmentOperations, DomainEnvironmentFacts } from './environment.js';
24
22
  import type { DomainFileOperations, DomainFileFacts } from './files.js';
@@ -32,9 +30,9 @@ import type { DomainPromptInputOperations, DomainPromptInputFacts } from './prom
32
30
  import type { DomainSessionOperations, DomainSessionFacts } from './session-input.js';
33
31
  import type { DomainSettingsOperations, DomainSettingsFacts } from './settings.js';
34
32
  import type { DomainToolOperations, DomainToolFacts } from './tools.js';
35
- export interface ExtensionDomainOperations extends DomainAgentOperations, DomainCompactOperations, DomainEnvironmentOperations, DomainFileOperations, DomainMcpOperations, DomainModelRequestOperations, DomainModelStreamOperations, DomainNetworkOperations, DomainProcessOperations, DomainPromptCompositionOperations, DomainPromptInputOperations, DomainSessionOperations, DomainSettingsOperations, DomainToolOperations {
33
+ export interface ExtensionDomainOperations extends DomainCompactOperations, DomainEnvironmentOperations, DomainFileOperations, DomainMcpOperations, DomainModelRequestOperations, DomainModelStreamOperations, DomainNetworkOperations, DomainProcessOperations, DomainPromptCompositionOperations, DomainPromptInputOperations, DomainSessionOperations, DomainSettingsOperations, DomainToolOperations {
36
34
  }
37
- export interface ExtensionDomainFacts extends DomainAgentFacts, DomainCompactFacts, DomainEnvironmentFacts, DomainFileFacts, DomainMcpFacts, DomainModelRequestFacts, DomainModelStreamFacts, DomainNetworkFacts, DomainProcessFacts, DomainPromptCompositionFacts, DomainPromptInputFacts, DomainSessionFacts, DomainSettingsFacts, DomainToolFacts {
35
+ export interface ExtensionDomainFacts extends DomainCompactFacts, DomainEnvironmentFacts, DomainFileFacts, DomainMcpFacts, DomainModelRequestFacts, DomainModelStreamFacts, DomainNetworkFacts, DomainProcessFacts, DomainPromptCompositionFacts, DomainPromptInputFacts, DomainSessionFacts, DomainSettingsFacts, DomainToolFacts {
38
36
  }
39
37
  export interface ExtensionHookRegistrar {
40
38
  intercept<Key extends keyof ExtensionDomainOperations>(key: Key, handler: ExtensionInterceptor<ExtensionDomainOperations[Key]['input'], ExtensionDomainOperations[Key]['output']>, options?: ExtensionHookOptions): ExtensionResourceRegistration;
@@ -55,7 +53,6 @@ export interface AgentExtensionContext {
55
53
  }
56
54
  export interface SessionExtensionContext extends AgentExtensionContext {
57
55
  readonly mcp?: ExtensionMcpApi;
58
- readonly agents?: ExtensionAgentsApi;
59
56
  readonly environment?: ExtensionEnvironmentApi;
60
57
  readonly settings?: ExtensionSettingsApi;
61
58
  readonly processes?: ExtensionProcessesApi;
@@ -7,7 +7,6 @@ import type { ExtensionModelsBackend } from './models.js';
7
7
  import type { ExtensionCheckpointBackend, ExtensionStoreBackend } from './data.js';
8
8
  import type { ExtensionSessionBackend } from './session-input.js';
9
9
  import type { AgentHostExtensionPorts } from './host-ports.js';
10
- import { type ExtensionAgentsBackend } from './agents.js';
11
10
  export interface ExtensionManagerOptions {
12
11
  readonly toolRuntime: ExtensionToolRuntime;
13
12
  readonly hostPorts?: AgentHostExtensionPorts;
@@ -19,7 +18,6 @@ export interface ExtensionManagerOptions {
19
18
  readonly checkpoint?: ExtensionCheckpointBackend;
20
19
  readonly store?: ExtensionStoreBackend;
21
20
  readonly sessionBackend?: (sessionId: string) => ExtensionSessionBackend | undefined;
22
- readonly agentsBackend?: (sessionId: string) => ExtensionAgentsBackend | undefined;
23
21
  readonly allowTool?: (sessionId: string, name: string, definition?: ExtensionToolBinding['definition']) => boolean;
24
22
  readonly scheduleTool?: (sessionId: string) => ExtensionSession['scheduleTool'];
25
23
  }
@@ -4,7 +4,6 @@ import { ExtensionResourceScope } from './resources.js';
4
4
  import { ExtensionServices } from './services.js';
5
5
  import { ExtensionSession } from './session.js';
6
6
  import { coreSettingsBinding, settingsCapabilities } from './settings.js';
7
- import { agentCapabilities } from './agents.js';
8
7
  /** Agent admission coordinates a single composition generation, never parallel replacement loops. */
9
8
  export class ExtensionManager {
10
9
  options;
@@ -109,7 +108,7 @@ export class ExtensionManager {
109
108
  throw new AgentError('MAR_AGENT_EXTENSION_COMPOSITION_FAILED', 'Required extension initialization failed.');
110
109
  let session = this.#sessions.get(sessionId);
111
110
  if (!session) {
112
- session = new ExtensionSession(this.options.toolRuntime, sessionId, this.#epochs.get(sessionId) ?? 0, this.#agentServices, this.operations, agentCapabilities(this.capabilities, this.options.agentsBackend?.(sessionId) !== undefined), (diagnostic) => this.#diagnose(diagnostic), (context) => this.options.baseTools?.(sessionId, context) ?? [], this.options.models, this.options.checkpoint, this.options.sessionBackend?.(sessionId), this.options.hostPorts, this.options.agentsBackend?.(sessionId), (name, definition) => this.options.allowTool?.(sessionId, name, definition) ?? true, this.options.scheduleTool?.(sessionId), this.options.store);
111
+ session = new ExtensionSession(this.options.toolRuntime, sessionId, this.#epochs.get(sessionId) ?? 0, this.#agentServices, this.operations, this.capabilities, (diagnostic) => this.#diagnose(diagnostic), (context) => this.options.baseTools?.(sessionId, context) ?? [], this.options.models, this.options.checkpoint, this.options.sessionBackend?.(sessionId), this.options.hostPorts, (name, definition) => this.options.allowTool?.(sessionId, name, definition) ?? true, this.options.scheduleTool?.(sessionId), this.options.store);
113
112
  this.#sessions.set(sessionId, session);
114
113
  try {
115
114
  await this.#attach(session, loadingSignal, baseTools);
@@ -13,6 +13,9 @@ export interface ExtensionModelDescription {
13
13
  readonly maxOutputTokens: number;
14
14
  readonly inputCapabilities: readonly string[];
15
15
  readonly reasoningEfforts: readonly AgentReasoningEffort[];
16
+ readonly defaultReasoningEffort: AgentReasoningEffort;
17
+ readonly conversationModel: boolean;
18
+ readonly subagentModel: boolean;
16
19
  readonly isDefault: boolean;
17
20
  }
18
21
  export type ExtensionModelTextBlock = ModelTextBlock;
@@ -10,6 +10,14 @@ export interface ExtensionOperationContext {
10
10
  /** Correlation with the actual enclosing tool call; not an authority token. */
11
11
  readonly toolCallId?: string;
12
12
  readonly parentToolCallId?: string;
13
+ /** Wakes a foreground tool wait when its Execution receives a steering input. */
14
+ readonly waitInterrupt?: import('../runtime/wait.js').ToolWaitInterrupt;
15
+ /** Present only for the exact tool invocation explicitly approved by the Host. */
16
+ readonly permission?: Readonly<{
17
+ readonly decision: 'approved';
18
+ }>;
19
+ /** Effective execution permission mode, when the invocation has an execution owner. */
20
+ readonly permissionMode?: import('../tools/permission-contract.js').PermissionMode;
13
21
  readonly actor: 'model' | 'sdk' | 'extension' | 'code-mode';
14
22
  readonly entry: 'direct' | 'programmatic' | 'native-api';
15
23
  readonly signal: AbortSignal;
@@ -59,6 +59,9 @@ export class ExtensionOperations {
59
59
  ...((identity.parentToolCallId ?? parent.parentToolCallId) === undefined
60
60
  ? {}
61
61
  : { parentToolCallId: identity.parentToolCallId ?? parent.parentToolCallId }),
62
+ ...((identity.waitInterrupt ?? parent.waitInterrupt) === undefined
63
+ ? {}
64
+ : { waitInterrupt: identity.waitInterrupt ?? parent.waitInterrupt }),
62
65
  entry: parent.entry,
63
66
  signal: AbortSignal.any([identity.signal, parent.signal])
64
67
  }
@@ -1,9 +1,16 @@
1
1
  import type { ExtensionResourceScope } from './resources.js';
2
+ import type { ExtensionOperationRunner } from './operation.js';
3
+ export interface ExtensionServiceConsumer {
4
+ readonly run: ExtensionOperationRunner;
5
+ readonly onDispose: (dispose: () => void | Promise<void>) => void;
6
+ }
2
7
  export interface ExtensionServiceKey<T> {
3
8
  readonly id: string;
4
9
  readonly scope: 'agent' | 'session';
5
10
  /** Compile-time service contract; never examined at runtime. */
6
11
  readonly contract?: (implementation: T) => T;
12
+ /** Bind a service to the framework-owned consuming Session scope. */
13
+ readonly bindConsumer?: (implementation: T, consumer: ExtensionServiceConsumer) => T;
7
14
  }
8
15
  export interface ExtensionServiceRegistry {
9
16
  provide<T>(key: ExtensionServiceKey<T>, implementation: T): void;
@@ -23,9 +30,10 @@ export declare class ExtensionServices {
23
30
  readonly id: string;
24
31
  readonly scope: 'agent' | 'session';
25
32
  }): boolean;
33
+ resolve<T>(key: ExtensionServiceKey<T>): T | undefined;
26
34
  hasOwned(key: {
27
35
  readonly id: string;
28
36
  readonly scope: 'agent' | 'session';
29
37
  }, owner: ExtensionResourceScope): boolean;
30
- bind(owner: ExtensionResourceScope): ExtensionServiceRegistry;
38
+ bind(owner: ExtensionResourceScope, run?: ExtensionOperationRunner): ExtensionServiceRegistry;
31
39
  }
@@ -11,16 +11,30 @@ export class ExtensionServices {
11
11
  has(key) {
12
12
  return this.#find(key) !== undefined;
13
13
  }
14
+ resolve(key) {
15
+ return this.#find(key)?.value;
16
+ }
14
17
  hasOwned(key, owner) {
15
18
  return (key.scope === this.scope &&
16
19
  (this.#entries.get(key.id)?.some((entry) => entry.owner === owner) ?? false));
17
20
  }
18
- bind(owner) {
21
+ bind(owner, run) {
19
22
  const consume = (key, options) => {
20
23
  const entry = this.#find(key);
21
24
  if (!entry && !options?.optional)
22
25
  throw new AgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Required service is unavailable.');
23
- return entry?.value;
26
+ if (!entry)
27
+ return undefined;
28
+ if (!entry.bindConsumer)
29
+ return entry.value;
30
+ if (!run)
31
+ throw new AgentError('MAR_AGENT_EXTENSION_SERVICE_SCOPE', 'Service requires a Session consumer.');
32
+ return entry.bindConsumer(entry.value, {
33
+ run,
34
+ onDispose: (dispose) => {
35
+ owner.effect(() => dispose);
36
+ }
37
+ });
24
38
  };
25
39
  const publish = (key, value, replace) => {
26
40
  if (key.scope !== this.scope)
@@ -32,7 +46,16 @@ export class ExtensionServices {
32
46
  throw new AgentError('MAR_AGENT_EXTENSION_SERVICE_CONFLICT', 'Service is already provided.');
33
47
  if (!entries.length && replace)
34
48
  throw new AgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Service replacement has no lower provider.');
35
- const entry = { value, owner };
49
+ const binder = key.bindConsumer;
50
+ const entry = {
51
+ value,
52
+ owner,
53
+ ...(binder
54
+ ? {
55
+ bindConsumer: (implementation, consumer) => binder(implementation, consumer)
56
+ }
57
+ : {})
58
+ };
36
59
  owner.effect(() => () => {
37
60
  const index = entries.indexOf(entry);
38
61
  if (index !== -1)
@@ -0,0 +1,18 @@
1
+ import type { ExtensionServiceKey } from './services.js';
2
+ /** Context proposed by a Session-scoped extension, confirmed only after durable submission. */
3
+ export interface ExtensionSessionContextItem {
4
+ readonly content: string;
5
+ /** Synchronously confirm only the version captured by collect, not later observations. */
6
+ acknowledge(): void;
7
+ }
8
+ export interface ExtensionSessionContextProvider {
9
+ collect(signal: AbortSignal): readonly ExtensionSessionContextItem[] | Promise<readonly ExtensionSessionContextItem[]>;
10
+ /** Bounded resource summary for normal and compact context reconstruction; consumes no updates. */
11
+ resources?(signal: AbortSignal): readonly string[] | Promise<readonly string[]>;
12
+ /** Wakes an active wait when unread model-visible context becomes available. */
13
+ readonly wake?: {
14
+ pending(): boolean;
15
+ subscribe(listener: () => void): () => void;
16
+ };
17
+ }
18
+ export declare const SESSION_CONTEXT_PROVIDER: ExtensionServiceKey<ExtensionSessionContextProvider>;
@@ -0,0 +1,4 @@
1
+ export const SESSION_CONTEXT_PROVIDER = {
2
+ id: 'core.session-context-provider',
3
+ scope: 'session'
4
+ };