@myagentroam/agent 0.9.159 → 0.9.161

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 (130) hide show
  1. package/dist/agent-core/extensions/agents.d.ts +69 -0
  2. package/dist/agent-core/extensions/agents.js +160 -0
  3. package/dist/agent-core/extensions/contracts.d.ts +260 -0
  4. package/dist/agent-core/extensions/contracts.js +1 -0
  5. package/dist/agent-core/extensions/data.d.ts +84 -0
  6. package/dist/agent-core/extensions/data.js +83 -0
  7. package/dist/agent-core/extensions/environment.d.ts +27 -0
  8. package/dist/agent-core/extensions/environment.js +52 -0
  9. package/dist/agent-core/extensions/files.d.ts +64 -0
  10. package/dist/agent-core/extensions/files.js +26 -0
  11. package/dist/agent-core/extensions/hooks.d.ts +32 -0
  12. package/dist/agent-core/extensions/hooks.js +135 -0
  13. package/dist/agent-core/extensions/manager.d.ts +39 -0
  14. package/dist/agent-core/extensions/manager.js +458 -0
  15. package/dist/agent-core/extensions/mcp.d.ts +99 -0
  16. package/dist/agent-core/extensions/mcp.js +433 -0
  17. package/dist/agent-core/extensions/model-request.d.ts +60 -0
  18. package/dist/agent-core/extensions/model-request.js +167 -0
  19. package/dist/agent-core/extensions/model-stream.d.ts +9 -0
  20. package/dist/agent-core/extensions/model-stream.js +101 -0
  21. package/dist/agent-core/extensions/models.d.ts +43 -0
  22. package/dist/agent-core/extensions/models.js +1 -0
  23. package/dist/agent-core/extensions/network.d.ts +32 -0
  24. package/dist/agent-core/extensions/network.js +97 -0
  25. package/dist/agent-core/extensions/operation.d.ts +29 -0
  26. package/dist/agent-core/extensions/operation.js +98 -0
  27. package/dist/agent-core/extensions/processes.d.ts +52 -0
  28. package/dist/agent-core/extensions/processes.js +283 -0
  29. package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
  30. package/dist/agent-core/extensions/prompt-composition.js +106 -0
  31. package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
  32. package/dist/agent-core/extensions/prompt-input.js +46 -0
  33. package/dist/agent-core/extensions/resources.d.ts +27 -0
  34. package/dist/agent-core/extensions/resources.js +221 -0
  35. package/dist/agent-core/extensions/services.d.ts +31 -0
  36. package/dist/agent-core/extensions/services.js +61 -0
  37. package/dist/agent-core/extensions/session-input.d.ts +151 -0
  38. package/dist/agent-core/extensions/session-input.js +351 -0
  39. package/dist/agent-core/extensions/session-messages.d.ts +41 -0
  40. package/dist/agent-core/extensions/session-messages.js +76 -0
  41. package/dist/agent-core/extensions/session.d.ts +68 -0
  42. package/dist/agent-core/extensions/session.js +593 -0
  43. package/dist/agent-core/extensions/settings.d.ts +59 -0
  44. package/dist/agent-core/extensions/settings.js +115 -0
  45. package/dist/agent-core/extensions/store.d.ts +10 -0
  46. package/dist/agent-core/extensions/store.js +131 -0
  47. package/dist/agent-core/extensions/tools.d.ts +61 -0
  48. package/dist/agent-core/extensions/tools.js +266 -0
  49. package/dist/agent-core/host/contracts.d.ts +25 -0
  50. package/dist/agent-core/host/local-agent-host.d.ts +10 -1
  51. package/dist/agent-core/host/local-agent-host.js +132 -20
  52. package/dist/agent-core/host/local-file-access.d.ts +30 -0
  53. package/dist/agent-core/host/local-file-access.js +107 -0
  54. package/dist/agent-core/host/local-network-access.d.ts +6 -0
  55. package/dist/agent-core/host/local-network-access.js +97 -0
  56. package/dist/agent-core/host/local-process-executor.d.ts +5 -1
  57. package/dist/agent-core/host/local-session-environment.d.ts +18 -0
  58. package/dist/agent-core/host/local-session-environment.js +102 -0
  59. package/dist/agent-core/model/anthropic-messages.js +73 -23
  60. package/dist/agent-core/model/contracts.d.ts +12 -0
  61. package/dist/agent-core/model/openai-responses.js +73 -12
  62. package/dist/agent-core/model/text-blocks.d.ts +7 -0
  63. package/dist/agent-core/model/text-blocks.js +40 -0
  64. package/dist/agent-core/prompts/index.d.ts +6 -2
  65. package/dist/agent-core/prompts/index.js +38 -24
  66. package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
  67. package/dist/agent-core/runtime/context/normalize.js +21 -1
  68. package/dist/agent-core/runtime/context/projection.js +35 -1
  69. package/dist/agent-core/runtime/context/request-budget.js +5 -2
  70. package/dist/agent-core/runtime/context/restore.d.ts +5 -0
  71. package/dist/agent-core/runtime/context/restore.js +47 -4
  72. package/dist/agent-core/runtime/context/usage.js +2 -3
  73. package/dist/agent-core/runtime/contracts.d.ts +2 -0
  74. package/dist/agent-core/runtime/execution.js +5 -2
  75. package/dist/agent-core/sdk/agent.js +177 -40
  76. package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
  77. package/dist/agent-core/sdk/extension-agents.js +51 -0
  78. package/dist/agent-core/sdk/extension-models.d.ts +13 -0
  79. package/dist/agent-core/sdk/extension-models.js +128 -0
  80. package/dist/agent-core/sdk/extension-session.d.ts +30 -0
  81. package/dist/agent-core/sdk/extension-session.js +103 -0
  82. package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
  83. package/dist/agent-core/sdk/extension-tools.js +126 -0
  84. package/dist/agent-core/sdk/session.d.ts +26 -2
  85. package/dist/agent-core/sdk/session.js +1215 -506
  86. package/dist/agent-core/sdk/types.d.ts +3 -0
  87. package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
  88. package/dist/agent-core/session/jsonl-store.d.ts +28 -8
  89. package/dist/agent-core/session/jsonl-store.js +124 -84
  90. package/dist/agent-core/session/types.d.ts +4 -1
  91. package/dist/agent-core/storage/file-lock.d.ts +4 -0
  92. package/dist/agent-core/storage/file-lock.js +83 -0
  93. package/dist/agent-core/subagent/contracts.d.ts +11 -1
  94. package/dist/agent-core/subagent/scheduler.d.ts +8 -0
  95. package/dist/agent-core/subagent/session-controller.d.ts +22 -1
  96. package/dist/agent-core/subagent/session-controller.js +355 -98
  97. package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
  98. package/dist/agent-core/tools/code-mode/cells.js +5 -15
  99. package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
  100. package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
  101. package/dist/agent-core/tools/code-mode/index.js +1 -2
  102. package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
  103. package/dist/agent-core/tools/code-mode/observations.js +16 -5
  104. package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
  105. package/dist/agent-core/tools/execution-limits.d.ts +0 -1
  106. package/dist/agent-core/tools/execution-limits.js +0 -1
  107. package/dist/agent-core/tools/execution.d.ts +11 -2
  108. package/dist/agent-core/tools/execution.js +58 -21
  109. package/dist/agent-core/tools/input.d.ts +2 -0
  110. package/dist/agent-core/tools/input.js +36 -0
  111. package/dist/agent-core/tools/local/apply-patch.js +21 -17
  112. package/dist/agent-core/tools/local/exec.d.ts +4 -4
  113. package/dist/agent-core/tools/local/exec.js +242 -63
  114. package/dist/agent-core/tools/local/registry.d.ts +6 -0
  115. package/dist/agent-core/tools/local/registry.js +60 -9
  116. package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
  117. package/dist/agent-core/tools/mcp/catalog.js +4 -4
  118. package/dist/agent-core/tools/mcp/history.js +2 -2
  119. package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
  120. package/dist/agent-core/tools/mcp/tool-search.js +2 -2
  121. package/dist/agent-core/tools/output-projection.d.ts +3 -1
  122. package/dist/agent-core/tools/output-projection.js +11 -2
  123. package/dist/agent-core/tools/runtime-context.d.ts +1 -0
  124. package/dist/agent-core/tools/runtime.d.ts +3 -1
  125. package/dist/agent-core/tools/runtime.js +22 -7
  126. package/dist/agent-core/tools/scheduler.d.ts +2 -0
  127. package/dist/agent-core/tools/scheduler.js +43 -6
  128. package/dist/index.d.ts +21 -0
  129. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
  130. package/package.json +1 -1
@@ -0,0 +1,458 @@
1
+ import { MarAgentError } from '../error.js';
2
+ import { ExtensionOperations } from './operation.js';
3
+ import { ExtensionResourceScope } from './resources.js';
4
+ import { ExtensionServices } from './services.js';
5
+ import { ExtensionSession } from './session.js';
6
+ import { ExtensionStore } from './store.js';
7
+ import { coreSettingsBinding, settingsCapabilities } from './settings.js';
8
+ import { agentCapabilities } from './agents.js';
9
+ /** Agent admission coordinates a single composition generation, never parallel replacement loops. */
10
+ export class ExtensionManager {
11
+ options;
12
+ operations = new ExtensionOperations();
13
+ #diagnostics = [];
14
+ #sessions = new Map();
15
+ #epochs = new Map();
16
+ #controller = new AbortController();
17
+ #agentServices = new ExtensionServices('agent');
18
+ #agentScopes = [];
19
+ #setupFailures = new Set();
20
+ #strictSetupFailure = false;
21
+ #strictSessions = new Set();
22
+ #cleanupFailedSessions = new Set();
23
+ #cleanupFailed = false;
24
+ #snapshot;
25
+ #orders = {
26
+ agent: { extensions: [], failures: [] },
27
+ session: { extensions: [], failures: [] }
28
+ };
29
+ #tail = Promise.resolve();
30
+ #disposal;
31
+ #endings = new WeakMap();
32
+ capabilities;
33
+ constructor(options) {
34
+ this.options = options;
35
+ const capabilities = Object.freeze([...new Set(options.capabilities)]);
36
+ this.capabilities = Object.freeze({
37
+ has: (id) => capabilities.includes(id),
38
+ list: () => capabilities
39
+ });
40
+ }
41
+ diagnostics() {
42
+ return [...this.#diagnostics];
43
+ }
44
+ session(id) {
45
+ return this.#sessions.get(id);
46
+ }
47
+ get revision() {
48
+ return this.#snapshot?.revision;
49
+ }
50
+ hasReferences() {
51
+ return [...this.#sessions.values()].some((session) => session.hasReferences);
52
+ }
53
+ prepare(sessionId, signal, baseTools, refresh = true) {
54
+ const loadingSignal = AbortSignal.any([signal, this.#controller.signal]);
55
+ // A retiring owner may join a child whose admission would otherwise queue behind disposal.
56
+ if (loadingSignal.aborted)
57
+ return Promise.reject(loadingSignal.reason);
58
+ if (this.#sessions.get(sessionId)?.isEnding)
59
+ return Promise.reject(new MarAgentError('MAR_AGENT_SESSION_CLOSING', 'Session is closing.'));
60
+ return this.#serial(async () => {
61
+ loadingSignal.throwIfAborted();
62
+ if (this.#sessions.get(sessionId)?.isEnding)
63
+ throw new MarAgentError('MAR_AGENT_SESSION_CLOSING', 'Session is closing.');
64
+ if (this.#cleanupFailed || this.#cleanupFailedSessions.has(sessionId))
65
+ throw new MarAgentError('MAR_AGENT_EXTENSION_CLEANUP_FAILED', 'Retired extension resources did not clean up.');
66
+ const source = this.options.extensions;
67
+ const resolved = !refresh && this.#snapshot
68
+ ? this.#snapshot
69
+ : Array.isArray(source)
70
+ ? { revision: 'static', extensions: source }
71
+ : await source.resolve({ signal: loadingSignal });
72
+ loadingSignal.throwIfAborted();
73
+ this.#validateSnapshot(resolved);
74
+ if (!this.#snapshot || this.#snapshot.revision !== resolved.revision) {
75
+ if (this.#snapshot && (this.options.busyExcept(sessionId) || this.hasReferences())) {
76
+ this.#diagnose({
77
+ code: 'MAR_AGENT_EXTENSION_UPDATE_PENDING',
78
+ sessionId,
79
+ revision: resolved.revision
80
+ });
81
+ }
82
+ else {
83
+ const orders = {
84
+ agent: ordered(resolved.extensions, 'agent'),
85
+ session: ordered(resolved.extensions, 'session')
86
+ };
87
+ await this.#retire();
88
+ loadingSignal.throwIfAborted();
89
+ this.#snapshot = {
90
+ revision: resolved.revision,
91
+ extensions: resolved.extensions.map((extension) => Object.freeze({
92
+ ...extension,
93
+ bindings: Object.freeze({ ...(extension.bindings ?? {}) })
94
+ }))
95
+ };
96
+ this.#orders = orders;
97
+ try {
98
+ await this.#setup(loadingSignal);
99
+ }
100
+ catch (error) {
101
+ if (loadingSignal.aborted) {
102
+ await this.#retire();
103
+ this.#snapshot = undefined;
104
+ }
105
+ throw error;
106
+ }
107
+ }
108
+ }
109
+ if (this.#strictSetupFailure)
110
+ throw new MarAgentError('MAR_AGENT_EXTENSION_COMPOSITION_FAILED', 'Required extension initialization failed.');
111
+ let session = this.#sessions.get(sessionId);
112
+ if (!session) {
113
+ session = new ExtensionSession(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));
114
+ this.#sessions.set(sessionId, session);
115
+ try {
116
+ await this.#attach(session, loadingSignal, baseTools);
117
+ }
118
+ catch (error) {
119
+ if (loadingSignal.aborted) {
120
+ await session.dispose();
121
+ this.#sessions.delete(sessionId);
122
+ }
123
+ throw error;
124
+ }
125
+ }
126
+ if (this.#strictSessions.has(sessionId))
127
+ throw new MarAgentError('MAR_AGENT_EXTENSION_COMPOSITION_FAILED', 'Required Session extension initialization failed.');
128
+ return session;
129
+ });
130
+ }
131
+ clearSession(id) {
132
+ this.retireSession(id);
133
+ return this.#serial(async () => {
134
+ try {
135
+ await this.#sessions.get(id)?.dispose();
136
+ }
137
+ catch (error) {
138
+ this.#cleanupFailedSessions.add(id);
139
+ this.#diagnose({ code: 'MAR_AGENT_EXTENSION_CLEANUP_FAILED', sessionId: id });
140
+ throw error;
141
+ }
142
+ this.#sessions.delete(id);
143
+ this.#strictSessions.delete(id);
144
+ this.#epochs.set(id, (this.#epochs.get(id) ?? 0) + 1);
145
+ });
146
+ }
147
+ async endSession(id, reason, resumable = true) {
148
+ const session = this.#sessions.get(id);
149
+ if (!session)
150
+ return;
151
+ const previous = this.#endings.get(session);
152
+ if (previous)
153
+ return previous;
154
+ session.isEnding = true;
155
+ const controller = new AbortController();
156
+ const ending = Promise.resolve().then(async () => {
157
+ try {
158
+ await this.operations.enter({
159
+ sessionId: id,
160
+ sessionEpoch: session.epoch,
161
+ actor: 'sdk',
162
+ entry: 'native-api',
163
+ signal: controller.signal
164
+ }, (call) => session.hooks.emit('session.ending', { sessionId: id, reason, resumable }, call));
165
+ }
166
+ finally {
167
+ controller.abort();
168
+ }
169
+ });
170
+ this.#endings.set(session, ending);
171
+ return ending;
172
+ }
173
+ retireSession(id) {
174
+ this.#sessions.get(id)?.retire();
175
+ }
176
+ dispose() {
177
+ if (this.#disposal)
178
+ return this.#disposal;
179
+ this.#controller.abort();
180
+ const endings = [];
181
+ for (const [id, session] of this.#sessions) {
182
+ if (session.hooks.hasObservers('session.ending'))
183
+ endings.push(this.endSession(id, 'other'));
184
+ else
185
+ session.retire();
186
+ }
187
+ for (const scope of this.#agentScopes)
188
+ scope.retire();
189
+ this.#disposal = (async () => {
190
+ await Promise.all(endings);
191
+ for (const id of this.#sessions.keys())
192
+ this.retireSession(id);
193
+ await this.#serial(() => this.#retire());
194
+ })();
195
+ return this.#disposal;
196
+ }
197
+ async #setup(signal) {
198
+ for (const extension of this.#orders.agent.failures) {
199
+ this.#setupFailures.add(extension.id);
200
+ this.#strictSetupFailure ||= extension.failurePolicy === 'fail-composition';
201
+ this.#diagnose({
202
+ code: 'MAR_AGENT_EXTENSION_DEPENDENCY_CYCLE',
203
+ extensionId: extension.id,
204
+ revision: this.#snapshot.revision
205
+ });
206
+ }
207
+ for (const extension of this.#orders.agent.extensions) {
208
+ const scope = new ExtensionResourceScope();
209
+ try {
210
+ this.#check(extension, 'agent', this.#agentServices);
211
+ const context = {
212
+ ...(extension.dataDirectory === undefined
213
+ ? {}
214
+ : {
215
+ data: {
216
+ store: new ExtensionStore(extension.dataDirectory).bind((operation) => scope.run(operation))
217
+ }
218
+ }),
219
+ resources: scope,
220
+ services: this.#agentServices.bind(scope),
221
+ capabilities: settingsCapabilities(this.capabilities, coreSettingsBinding(extension.bindings) !== undefined),
222
+ bindings: extension.bindings ?? {},
223
+ observability: {
224
+ log: (message) => this.#diagnose({
225
+ code: 'MAR_AGENT_EXTENSION_LOG',
226
+ extensionId: extension.id,
227
+ message: message.slice(0, 1000)
228
+ })
229
+ }
230
+ };
231
+ await ExtensionResourceScope.initialize(async () => {
232
+ const release = await extension.setup?.(context);
233
+ if (release)
234
+ scope.effect(() => release);
235
+ await scope.ready();
236
+ });
237
+ this.#checkProvides(extension, 'agent', this.#agentServices, scope);
238
+ signal.throwIfAborted();
239
+ this.#agentScopes.push(scope);
240
+ }
241
+ catch (error) {
242
+ this.#setupFailures.add(extension.id);
243
+ this.#strictSetupFailure ||= extension.failurePolicy === 'fail-composition';
244
+ this.#diagnose({
245
+ code: failureCode(error),
246
+ extensionId: extension.id,
247
+ revision: this.#snapshot.revision
248
+ });
249
+ try {
250
+ await scope.dispose();
251
+ }
252
+ catch (cleanupError) {
253
+ this.#agentScopes.push(scope);
254
+ this.#cleanupFailed = true;
255
+ this.#diagnose({
256
+ code: 'MAR_AGENT_EXTENSION_CLEANUP_FAILED',
257
+ extensionId: extension.id,
258
+ revision: this.#snapshot.revision
259
+ });
260
+ throw cleanupError;
261
+ }
262
+ signal.throwIfAborted();
263
+ }
264
+ }
265
+ for (const scope of this.#agentScopes)
266
+ scope.activate();
267
+ }
268
+ async #attach(session, signal, baseTools) {
269
+ for (const extension of this.#orders.session.failures) {
270
+ if (extension.failurePolicy === 'fail-composition')
271
+ this.#strictSessions.add(session.id);
272
+ this.#diagnose({
273
+ code: 'MAR_AGENT_EXTENSION_DEPENDENCY_CYCLE',
274
+ extensionId: extension.id,
275
+ sessionId: session.id,
276
+ revision: this.#snapshot.revision
277
+ });
278
+ }
279
+ for (const extension of this.#orders.session.extensions) {
280
+ if (this.#setupFailures.has(extension.id))
281
+ continue;
282
+ const scope = new ExtensionResourceScope();
283
+ try {
284
+ this.#check(extension, 'session', session.services, session.capabilities);
285
+ await ExtensionResourceScope.initialize(async () => {
286
+ const release = await extension.attachSession?.(session.context(extension, scope));
287
+ if (release)
288
+ scope.effect(() => release);
289
+ await scope.ready();
290
+ });
291
+ this.#checkProvides(extension, 'session', session.services, scope);
292
+ session.capture(undefined, true, baseTools);
293
+ signal.throwIfAborted();
294
+ session.scopes.push(scope);
295
+ }
296
+ catch (error) {
297
+ if (extension.failurePolicy === 'fail-composition')
298
+ this.#strictSessions.add(session.id);
299
+ this.#diagnose({
300
+ code: failureCode(error),
301
+ extensionId: extension.id,
302
+ sessionId: session.id,
303
+ revision: this.#snapshot.revision
304
+ });
305
+ try {
306
+ await scope.dispose();
307
+ }
308
+ catch (cleanupError) {
309
+ session.scopes.push(scope);
310
+ this.#cleanupFailedSessions.add(session.id);
311
+ this.#diagnose({
312
+ code: 'MAR_AGENT_EXTENSION_CLEANUP_FAILED',
313
+ extensionId: extension.id,
314
+ sessionId: session.id,
315
+ revision: this.#snapshot.revision
316
+ });
317
+ throw cleanupError;
318
+ }
319
+ signal.throwIfAborted();
320
+ }
321
+ }
322
+ session.activate();
323
+ }
324
+ #check(extension, scope, services, availableCapabilities = this.capabilities) {
325
+ if (extension.apiVersion !== '1.0')
326
+ throw new MarAgentError('MAR_AGENT_EXTENSION_API_INCOMPATIBLE', 'Extension API version is incompatible.');
327
+ const capabilities = settingsCapabilities(availableCapabilities, coreSettingsBinding(extension.bindings) !== undefined);
328
+ if (extension.requires?.some((requirement) => !capabilities.has(requirement.id)))
329
+ throw new MarAgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Required capability is unavailable.');
330
+ if (scope === 'agent' && !extension.setup)
331
+ return;
332
+ for (const dependency of extension.serviceDependencies ?? []) {
333
+ if (scope === 'agent' && dependency.key.scope !== 'agent')
334
+ continue;
335
+ if (scope === 'session' && dependency.key.scope === 'agent' && extension.setup)
336
+ continue;
337
+ if (dependency.key.scope === scope || scope === 'session') {
338
+ if (!dependency.optional && !services.has(dependency.key))
339
+ throw new MarAgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Required service is unavailable.');
340
+ }
341
+ }
342
+ }
343
+ #checkProvides(extension, scope, services, owner) {
344
+ if (extension.provides?.some((declaration) => declaration.key.scope === scope && !services.hasOwned(declaration.key, owner)))
345
+ throw new MarAgentError('MAR_AGENT_EXTENSION_DEPENDENCY_MISSING', 'Declared service was not provided.');
346
+ }
347
+ #validateSnapshot(snapshot) {
348
+ if (!snapshot ||
349
+ typeof snapshot.revision !== 'string' ||
350
+ !snapshot.revision ||
351
+ !Array.isArray(snapshot.extensions) ||
352
+ snapshot.extensions.some((extension) => !extension || typeof extension.id !== 'string' || !extension.id.trim()) ||
353
+ new Set(snapshot.extensions.map((extension) => extension.id)).size !==
354
+ snapshot.extensions.length)
355
+ throw new MarAgentError('MAR_AGENT_EXTENSION_SOURCE_INVALID', 'Extension snapshot is inconsistent.');
356
+ }
357
+ async #retire() {
358
+ for (const session of this.#sessions.values())
359
+ session.retire();
360
+ for (const scope of this.#agentScopes)
361
+ scope.retire();
362
+ let failures = 0;
363
+ for (const session of this.#sessions.values()) {
364
+ try {
365
+ await session.dispose();
366
+ }
367
+ catch {
368
+ failures++;
369
+ }
370
+ }
371
+ for (const scope of [...this.#agentScopes].reverse()) {
372
+ try {
373
+ await scope.dispose();
374
+ }
375
+ catch {
376
+ failures++;
377
+ }
378
+ }
379
+ if (failures) {
380
+ this.#cleanupFailed = true;
381
+ this.#diagnose({ code: 'MAR_AGENT_EXTENSION_CLEANUP_FAILED' });
382
+ throw new MarAgentError('MAR_AGENT_EXTENSION_CLEANUP_FAILED', 'Composition cleanup failed.', {
383
+ details: { failures }
384
+ });
385
+ }
386
+ this.#sessions.clear();
387
+ this.#agentScopes = [];
388
+ this.#agentServices = new ExtensionServices('agent');
389
+ this.#setupFailures.clear();
390
+ this.#strictSetupFailure = false;
391
+ this.#strictSessions.clear();
392
+ this.#snapshot = undefined;
393
+ }
394
+ #diagnose(diagnostic) {
395
+ this.#diagnostics.push(Object.freeze({ ...diagnostic }));
396
+ if (this.#diagnostics.length > 200)
397
+ this.#diagnostics.shift();
398
+ }
399
+ #serial(operation) {
400
+ const result = this.#tail.then(operation);
401
+ this.#tail = result.catch(() => undefined);
402
+ return result;
403
+ }
404
+ }
405
+ function failureCode(error) {
406
+ return error instanceof MarAgentError ? error.code : 'MAR_AGENT_EXTENSION_INITIALIZATION_FAILED';
407
+ }
408
+ /** Declared providers precede consumers; array order remains the tiebreaker for unrelated items. */
409
+ function ordered(extensions, scope) {
410
+ const providers = new Map();
411
+ for (const extension of extensions)
412
+ for (const declaration of extension.provides ?? []) {
413
+ if (declaration.key.scope !== scope)
414
+ continue;
415
+ const entries = providers.get(declaration.key.id) ?? [];
416
+ if (!declaration.intent || declaration.intent === 'provide')
417
+ entries.unshift(extension);
418
+ else
419
+ entries.push(extension);
420
+ providers.set(declaration.key.id, entries);
421
+ }
422
+ const dependencies = new Map(extensions.map((extension) => [extension, new Set()]));
423
+ for (const entries of providers.values())
424
+ for (let index = 1; index < entries.length; index++)
425
+ dependencies.get(entries[index]).add(entries[index - 1]);
426
+ for (const extension of extensions)
427
+ for (const dependency of extension.serviceDependencies ?? []) {
428
+ if (dependency.key.scope !== scope || dependency.optional)
429
+ continue;
430
+ const provider = providers.get(dependency.key.id)?.at(-1);
431
+ if (provider && provider !== extension)
432
+ dependencies.get(extension).add(provider);
433
+ }
434
+ const result = [];
435
+ const visiting = new Set();
436
+ const visited = new Set();
437
+ const failed = new Set();
438
+ const visit = (extension) => {
439
+ if (visited.has(extension))
440
+ return !failed.has(extension);
441
+ if (visiting.has(extension)) {
442
+ failed.add(extension);
443
+ return false;
444
+ }
445
+ visiting.add(extension);
446
+ for (const dependency of dependencies.get(extension))
447
+ if (!visit(dependency))
448
+ failed.add(extension);
449
+ visiting.delete(extension);
450
+ visited.add(extension);
451
+ if (!failed.has(extension))
452
+ result.push(extension);
453
+ return !failed.has(extension);
454
+ };
455
+ for (const extension of extensions)
456
+ visit(extension);
457
+ return { extensions: result, failures: extensions.filter((extension) => failed.has(extension)) };
458
+ }
@@ -0,0 +1,99 @@
1
+ import type { McpToolCatalog, ToolExecutionResult } from '../tools/contracts.js';
2
+ import { type CoreJsonValue } from './data.js';
3
+ import type { CoreDomainFacts, CoreDomainOperations } from './contracts.js';
4
+ import type { ExtensionHooks } from './hooks.js';
5
+ import type { CoreOperationContext } from './operation.js';
6
+ import { ExtensionResourceScope, type CoreResourceRegistration } from './resources.js';
7
+ import type { CoreToolBinding, CoreToolRef, CoreToolInvocation, ExtensionTools } from './tools.js';
8
+ export interface CoreMcpServerDefinition {
9
+ readonly id: string;
10
+ readonly configuration: CoreJsonValue;
11
+ }
12
+ export interface CoreMcpServerRef {
13
+ readonly definition: CoreMcpServerDefinition;
14
+ readonly source: Readonly<{
15
+ extensionId: string;
16
+ ownerInstanceId: string;
17
+ revision: string;
18
+ }>;
19
+ }
20
+ export interface CoreMcpServerRegistration extends CoreResourceRegistration {
21
+ readonly ref: CoreMcpServerRef;
22
+ }
23
+ export interface CoreMcpConnectionRef {
24
+ readonly id: string;
25
+ readonly serverId: string;
26
+ readonly source: CoreMcpServerRef['source'];
27
+ }
28
+ export interface CoreMcpConnectInput {
29
+ readonly server: CoreMcpServerRef | string;
30
+ readonly signal?: AbortSignal;
31
+ }
32
+ export interface CoreMcpConnectionInput {
33
+ readonly connection: CoreMcpConnectionRef;
34
+ readonly signal?: AbortSignal;
35
+ }
36
+ export interface CoreMcpCallInput {
37
+ readonly tool: CoreToolRef;
38
+ readonly arguments: unknown;
39
+ readonly callId?: string;
40
+ readonly signal?: AbortSignal;
41
+ }
42
+ export interface CoreMcpCatalog {
43
+ readonly revision: string;
44
+ readonly servers: McpToolCatalog['servers'];
45
+ readonly tools: readonly CoreToolRef[];
46
+ }
47
+ export interface CoreMcpApi {
48
+ register(definition: CoreMcpServerDefinition): CoreMcpServerRegistration;
49
+ servers(): readonly CoreMcpServerRef[];
50
+ connect(input: CoreMcpConnectInput): Promise<CoreMcpConnectionRef>;
51
+ connections(): readonly CoreMcpConnectionRef[];
52
+ catalog(input: CoreMcpConnectionInput): Promise<CoreMcpCatalog>;
53
+ call(input: CoreMcpCallInput): Promise<ToolExecutionResult>;
54
+ disconnect(input: CoreMcpConnectionInput): Promise<void>;
55
+ }
56
+ export interface CoreMcpToolCall {
57
+ readonly name: string;
58
+ readonly serverId: string;
59
+ readonly rawName: string;
60
+ readonly arguments: unknown;
61
+ readonly callId?: string;
62
+ readonly expectedTool: Readonly<{
63
+ sourceIdentity: string;
64
+ definitionRevision: string;
65
+ }>;
66
+ }
67
+ /** A lease on the application's existing MCP Client. Core never implements a transport. */
68
+ export interface CoreMcpConnectionBackend {
69
+ catalog(call: CoreOperationContext): Promise<McpToolCatalog>;
70
+ call(input: CoreMcpToolCall, call: CoreOperationContext): Promise<ToolExecutionResult>;
71
+ close(): Promise<void>;
72
+ }
73
+ export interface CoreMcpBackend {
74
+ connect(definition: CoreMcpServerDefinition, call: CoreOperationContext): Promise<CoreMcpConnectionBackend>;
75
+ }
76
+ export interface CoreMcpCompletion {
77
+ readonly operation: 'connect' | 'catalog' | 'call' | 'disconnect';
78
+ readonly connectionId: string;
79
+ readonly toolName?: string;
80
+ readonly toolCount?: number;
81
+ }
82
+ type Run = <Value>(operation: (call: CoreOperationContext) => Promise<Value>, signal?: AbortSignal) => Promise<Value>;
83
+ /** One Session's declarations and owned Client leases feed its sole tool registry. */
84
+ export declare class ExtensionMcp {
85
+ #private;
86
+ readonly tools: ExtensionTools;
87
+ readonly hooks: ExtensionHooks<CoreDomainOperations, CoreDomainFacts>;
88
+ readonly getTools: (call: CoreOperationContext) => ReadonlyMap<string, CoreToolBinding>;
89
+ readonly toolRef: (binding: CoreToolBinding) => CoreToolRef;
90
+ readonly isMcpRef: (ref: CoreToolRef) => boolean;
91
+ readonly invoke: (input: CoreToolInvocation, call: CoreOperationContext) => Promise<ToolExecutionResult>;
92
+ readonly diagnose: () => void;
93
+ constructor(tools: ExtensionTools, hooks: ExtensionHooks<CoreDomainOperations, CoreDomainFacts>, getTools: (call: CoreOperationContext) => ReadonlyMap<string, CoreToolBinding>, toolRef: (binding: CoreToolBinding) => CoreToolRef, isMcpRef: (ref: CoreToolRef) => boolean, invoke: (input: CoreToolInvocation, call: CoreOperationContext) => Promise<ToolExecutionResult>, diagnose: () => void);
94
+ bind(owner: ExtensionResourceScope, extensionId: string, backend: CoreMcpBackend, run: Run): CoreMcpApi;
95
+ servers(): McpToolCatalog['servers'];
96
+ }
97
+ /** Project the same captured bindings used by invocation; never rediscover a backend by name. */
98
+ export declare function coreMcpCatalog(servers: McpToolCatalog['servers'], bindings: ReadonlyMap<string, CoreToolBinding>): McpToolCatalog;
99
+ export {};