@myagentroam/agent 0.9.158 → 0.9.160

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 +21 -0
  61. package/dist/agent-core/model/openai-responses.d.ts +3 -1
  62. package/dist/agent-core/model/openai-responses.js +248 -97
  63. package/dist/agent-core/model/text-blocks.d.ts +7 -0
  64. package/dist/agent-core/model/text-blocks.js +40 -0
  65. package/dist/agent-core/prompts/index.d.ts +6 -2
  66. package/dist/agent-core/prompts/index.js +38 -24
  67. package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
  68. package/dist/agent-core/runtime/context/normalize.js +21 -1
  69. package/dist/agent-core/runtime/context/projection.js +35 -1
  70. package/dist/agent-core/runtime/context/request-budget.js +5 -2
  71. package/dist/agent-core/runtime/context/restore.d.ts +5 -0
  72. package/dist/agent-core/runtime/context/restore.js +47 -4
  73. package/dist/agent-core/runtime/context/usage.js +2 -3
  74. package/dist/agent-core/runtime/contracts.d.ts +2 -0
  75. package/dist/agent-core/runtime/execution.js +5 -2
  76. package/dist/agent-core/sdk/agent.js +177 -40
  77. package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
  78. package/dist/agent-core/sdk/extension-agents.js +51 -0
  79. package/dist/agent-core/sdk/extension-models.d.ts +13 -0
  80. package/dist/agent-core/sdk/extension-models.js +128 -0
  81. package/dist/agent-core/sdk/extension-session.d.ts +30 -0
  82. package/dist/agent-core/sdk/extension-session.js +103 -0
  83. package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
  84. package/dist/agent-core/sdk/extension-tools.js +126 -0
  85. package/dist/agent-core/sdk/session.d.ts +26 -2
  86. package/dist/agent-core/sdk/session.js +1215 -506
  87. package/dist/agent-core/sdk/types.d.ts +3 -0
  88. package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
  89. package/dist/agent-core/session/jsonl-store.d.ts +28 -8
  90. package/dist/agent-core/session/jsonl-store.js +124 -84
  91. package/dist/agent-core/session/types.d.ts +4 -1
  92. package/dist/agent-core/storage/file-lock.d.ts +4 -0
  93. package/dist/agent-core/storage/file-lock.js +83 -0
  94. package/dist/agent-core/subagent/contracts.d.ts +11 -1
  95. package/dist/agent-core/subagent/scheduler.d.ts +8 -0
  96. package/dist/agent-core/subagent/session-controller.d.ts +22 -1
  97. package/dist/agent-core/subagent/session-controller.js +355 -98
  98. package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
  99. package/dist/agent-core/tools/code-mode/cells.js +5 -15
  100. package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
  101. package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
  102. package/dist/agent-core/tools/code-mode/index.js +1 -2
  103. package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
  104. package/dist/agent-core/tools/code-mode/observations.js +16 -5
  105. package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
  106. package/dist/agent-core/tools/execution.d.ts +11 -2
  107. package/dist/agent-core/tools/execution.js +58 -21
  108. package/dist/agent-core/tools/input.d.ts +2 -0
  109. package/dist/agent-core/tools/input.js +36 -0
  110. package/dist/agent-core/tools/local/apply-patch.js +21 -17
  111. package/dist/agent-core/tools/local/exec.d.ts +4 -4
  112. package/dist/agent-core/tools/local/exec.js +234 -62
  113. package/dist/agent-core/tools/local/registry.d.ts +6 -0
  114. package/dist/agent-core/tools/local/registry.js +60 -9
  115. package/dist/agent-core/tools/local/view-image.js +108 -2
  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
@@ -10,12 +10,22 @@ import { createSubagentBackend } from './subagent-backend.js';
10
10
  import { discoveredMcpToolNames, rebindMcpToolSearchOutputs } from '../tools/mcp/history.js';
11
11
  import { currentExecutionImages } from '../tools/output-projection.js';
12
12
  import { createHash, randomUUID } from 'node:crypto';
13
+ import { ExecutionToolSources } from './extension-tools.js';
14
+ import { ExtensionRequestBridge, ExtensionRequestSnapshots } from '../extensions/model-request.js';
15
+ import { normalizeSessionMessagesInput, sessionMessagesPage } from '../extensions/session-messages.js';
16
+ import { transformCoreModelStream } from '../extensions/model-stream.js';
17
+ import { SessionInputCoordinator, recordSessionInputCommit } from '../extensions/session-input.js';
18
+ import { createExtensionSessionBackend } from './extension-session.js';
19
+ import { invokeCorePromptSubmission } from '../extensions/prompt-input.js';
20
+ import { createExtensionAgentsBackend } from './extension-agents.js';
21
+ import { coreMcpCatalog } from '../extensions/mcp.js';
13
22
  import { isDeepStrictEqual } from 'node:util';
14
23
  import { MarAgentError, describeMarAgentError } from '../error.js';
15
24
  import { usesResponsesConfigurationUpdates } from '../model/configuration.js';
16
25
  import { buildEnvironmentContext } from '../prompts/core.js';
17
26
  import { executionBudgetEnvironmentEntry } from '../prompts/execution-budget.js';
18
- import { MAR_AGENT_PROMPT_VERSION, buildSystemPrompt } from '../prompts/index.js';
27
+ import { MAR_AGENT_PROMPT_VERSION, buildSystemPromptSections } from '../prompts/index.js';
28
+ import { renderPromptSections } from '../extensions/prompt-composition.js';
19
29
  import { retainedSessionResourcesSnapshot } from '../prompts/resources.js';
20
30
  import { childSubagentInstruction } from '../prompts/subagent.js';
21
31
  import { AsyncQueue } from '../runtime/async-queue.js';
@@ -82,6 +92,32 @@ export function createSessionOpener(services) {
82
92
  delete sessionRuntime.subagentsOpening;
83
93
  }
84
94
  }
95
+ const buildSessionPromptSections = (availableTools, promptMode, selectedModel, executionSystemInstruction, skillCatalog) => buildSystemPromptSections({
96
+ mode: promptMode,
97
+ platform: description.platform,
98
+ ...(description.architecture ? { architecture: description.architecture } : {}),
99
+ ...(description.shell ? { shell: description.shell } : {}),
100
+ ...(description.guaranteedCommands
101
+ ? { guaranteedCommands: description.guaranteedCommands }
102
+ : {}),
103
+ workspace: logicalWorkspace,
104
+ tools: availableTools
105
+ .filter((tool) => !tool.name.startsWith('mcp__'))
106
+ .map((tool) => tool.name),
107
+ directTools: availableTools
108
+ .filter((tool) => !tool.name.startsWith('mcp__') &&
109
+ allowsDirectTool(tool, availableTools.some((candidate) => candidate.name === 'code_exec')))
110
+ .map((tool) => tool.name),
111
+ highDensityCompaction: selectedModel.highDensityCompaction,
112
+ extension: [
113
+ executionSystemInstruction,
114
+ skillCatalog,
115
+ sessionSource.type === 'subagent' ? childSubagentInstruction : undefined
116
+ ]
117
+ .filter(Boolean)
118
+ .join('\n\n')
119
+ });
120
+ let promptDefaults;
85
121
  const sessionAdapter = (modelId, configuredModels) => {
86
122
  let selected = sessionRuntime.adapters.get(modelId);
87
123
  if (selected === undefined) {
@@ -99,27 +135,133 @@ export function createSessionOpener(services) {
99
135
  sessionRuntime.adapters.clear();
100
136
  sessionRuntime.adapterGeneration = generation;
101
137
  };
138
+ const sessionInputs = () => (sessionRuntime.inputs ??= new SessionInputCoordinator((receipt) => services.extensions.session(sessionId)?.diagnose({
139
+ code: receipt.errorCode ?? 'MAR_AGENT_SESSION_INPUT_FAILED',
140
+ operationId: receipt.operationId,
141
+ sessionId
142
+ })));
143
+ // Only physical record commits and projection replacement enter this lane, never Hooks.
144
+ const withRecordProjection = async (action) => {
145
+ const pending = (sessionRuntime.recordProjection ?? Promise.resolve())
146
+ .catch(() => undefined)
147
+ .then(action);
148
+ sessionRuntime.recordProjection = pending;
149
+ try {
150
+ return await pending;
151
+ }
152
+ finally {
153
+ if (sessionRuntime.recordProjection === pending)
154
+ delete sessionRuntime.recordProjection;
155
+ }
156
+ };
157
+ const appendExtensionMessage = async (entry, request, call) => {
158
+ call.signal.throwIfAborted();
159
+ const release = await store.acquireSessionWriteLease(sessionId, sessionWriterOwner);
160
+ try {
161
+ return await withRecordProjection(async () => {
162
+ call.signal.throwIfAborted();
163
+ const message = projectAppendedModelMessage(request.input);
164
+ const record = await store.append(sessionId, {
165
+ type: message ? 'model.context' : 'session.notice',
166
+ payload: {
167
+ ...(message ?? {
168
+ content: request.input.content,
169
+ textBlocks: request.input.textBlocks
170
+ }),
171
+ source: extensionInputSource(call, request.input.attribution)
172
+ },
173
+ ...(call.executionId ? { executionId: call.executionId } : {}),
174
+ ...(call.turnId ? { turnId: call.turnId } : {})
175
+ }, { signal: call.signal });
176
+ const result = {
177
+ changed: true,
178
+ modelChanged: !!message,
179
+ recordId: record.recordId,
180
+ ...(call.executionId ? { executionId: call.executionId } : {})
181
+ };
182
+ recordSessionInputCommit(entry, result);
183
+ if (message)
184
+ sessionRuntime.appendedMessages?.append(record.recordId, message);
185
+ return result;
186
+ });
187
+ }
188
+ finally {
189
+ await release();
190
+ }
191
+ };
102
192
  const execution = (mode, input, internal = {}) => {
103
193
  services.assertCanStart();
104
194
  if ([...active.keys()].some((key) => key.startsWith(`${sessionId}:`)))
105
195
  throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Session already has an active execution.');
106
196
  const executionId = randomUUID(), turnId = internal.turnId ?? randomUUID(), queue = new AsyncQueue(), controller = new AbortController();
197
+ const abortSource = () => controller.abort(internal.signal?.reason);
198
+ internal.signal?.addEventListener('abort', abortSource, { once: true });
199
+ if (internal.signal?.aborted)
200
+ abortSource();
107
201
  let executionEnvironment = {
108
202
  ...currentEnvironment,
109
203
  ...currentLocalTimeContext()
110
204
  };
111
205
  const { models: executionModels, defaultModelId: executionDefaultModelId, generation: executionModelGeneration } = services.modelState();
112
206
  const executionSystemInstruction = settings.systemInstruction;
207
+ promptDefaults = {
208
+ executionId,
209
+ modelId: input.modelId ?? executionDefaultModelId,
210
+ mode: mode === 'compact' ? 'run' : mode,
211
+ models: executionModels,
212
+ instruction: executionSystemInstruction
213
+ };
113
214
  const manualCompactionId = mode === 'compact' ? randomUUID() : undefined;
114
215
  active.set(`${sessionId}:${executionId}`, controller);
115
- const mailbox = [...(internal.initialMailbox ?? [])];
216
+ const inputs = sessionInputs();
217
+ const acceptParent = (message) => {
218
+ const origin = message.delivery?.origin;
219
+ const signal = AbortSignal.any([
220
+ controller.signal,
221
+ ...(message.delivery?.signal === undefined ? [] : [message.delivery.signal])
222
+ ]);
223
+ if (signal.aborted) {
224
+ message.delivery?.settled?.('cancelled');
225
+ return;
226
+ }
227
+ const accept = (call) => {
228
+ const entry = inputs.accept({ kind: 'submit', input: { text: message.text } }, call, (request, call, leaf) => leaf(request, call), 'accepted', message);
229
+ void entry.terminal.then(() => {
230
+ if (entry.receipt.status === 'cancelled' || entry.receipt.status === 'failed')
231
+ message.delivery?.settled?.(entry.receipt.status);
232
+ });
233
+ };
234
+ if (origin) {
235
+ accept(Object.freeze({
236
+ ...origin,
237
+ sessionId,
238
+ sessionEpoch: services.extensions.session(sessionId)?.epoch ?? 0,
239
+ signal,
240
+ executionId,
241
+ turnId
242
+ }));
243
+ return;
244
+ }
245
+ services.extensions.operations.enter({
246
+ sessionId,
247
+ sessionEpoch: services.extensions.session(sessionId)?.epoch ?? 0,
248
+ actor: 'sdk',
249
+ entry: 'native-api',
250
+ signal,
251
+ executionId,
252
+ turnId
253
+ }, accept);
254
+ };
255
+ for (const message of internal.initialMailbox ?? [])
256
+ acceptParent(message);
116
257
  let acceptsMailbox = sessionSource.type === 'subagent' && mode !== 'compact';
117
258
  let sequence = 0;
118
259
  let manualCompactionStarted = false;
119
260
  let emitTail = Promise.resolve();
120
261
  let exhaustedModelAttempt;
121
262
  // Serialize durable events before either the local stream or Host observes them.
122
- const emit = (event) => {
263
+ const emit = (event, assertLive) => {
264
+ assertLive?.();
123
265
  const full = {
124
266
  ...event,
125
267
  eventId: randomUUID(),
@@ -130,7 +272,7 @@ export function createSessionOpener(services) {
130
272
  turnId
131
273
  };
132
274
  const operation = emitTail.then(async () => {
133
- // Transport-only progress events never become recoverable history.
275
+ assertLive?.();
134
276
  const persistent = eventForPersistence(full);
135
277
  if (persistent !== undefined)
136
278
  await store.append(sessionId, {
@@ -170,7 +312,7 @@ export function createSessionOpener(services) {
170
312
  emitTail = operation.catch(() => undefined);
171
313
  return operation;
172
314
  };
173
- const createCompactionOperation = (compactionId) => new CompactionOperation(controller.signal, (progress) => {
315
+ const createCompactionOperation = (compactionId, signal = controller.signal) => new CompactionOperation(signal, (progress) => {
174
316
  const operation = emitTail.then(async () => {
175
317
  await store.append(sessionId, {
176
318
  type: 'compact.progress',
@@ -190,13 +332,30 @@ export function createSessionOpener(services) {
190
332
  (rolloutBudgetOptions ? new RolloutBudget(rolloutBudgetOptions) : undefined);
191
333
  const ownsRolloutBudget = rolloutBudget !== undefined && internal.sharedRolloutBudget === undefined;
192
334
  let releaseSession;
335
+ let executionToolSources;
193
336
  let selectedTurn;
194
337
  const contextGc = new ContextGcRuntime();
195
338
  let selectedAdapter;
196
339
  const executionScopedAdapters = new Set();
197
340
  const subagents = sessionSource.type === 'user' ? sessionRuntime.subagents : undefined;
198
341
  try {
342
+ controller.signal.throwIfAborted();
199
343
  releaseSession = await store.acquireSessionLease(sessionId, sessionWriterOwner);
344
+ if (!inputs.executing)
345
+ await inputs.idle();
346
+ let immediateTarget;
347
+ const pendingAppends = [];
348
+ sessionRuntime.appendedMessages = {
349
+ executionId,
350
+ append(recordId, message) {
351
+ pendingAppends.push({
352
+ recordId,
353
+ message
354
+ });
355
+ if (immediateTarget)
356
+ immediateTarget.push(message);
357
+ }
358
+ };
200
359
  await refreshSessionAdapters(executionModelGeneration);
201
360
  const sessionState = await store.readExecutionState(sessionId);
202
361
  if (!store.hasRetainedSessionLease(sessionId, sessionWriterOwner))
@@ -231,6 +390,12 @@ export function createSessionOpener(services) {
231
390
  includeImages: mode !== 'compact'
232
391
  });
233
392
  const messages = restored.messages;
393
+ const restoredRecordIds = new Set(contextRecords.map((record) => record.recordId));
394
+ for (const pending of pendingAppends)
395
+ if (!restoredRecordIds.has(pending.recordId))
396
+ messages.push(pending.message);
397
+ pendingAppends.length = 0;
398
+ immediateTarget = messages;
234
399
  let recordedEnvironment = restored.environmentState;
235
400
  let projectedExecutionBudgetStage;
236
401
  const appendExecutionBudgetEnvironment = () => {
@@ -249,28 +414,86 @@ export function createSessionOpener(services) {
249
414
  const entry = executionBudgetEnvironmentEntry(currentStage === 'normal' ? 'inactive' : currentStage);
250
415
  messages.push(environmentContextUpdate(executionEnvironment, executionEnvironment, [entry]));
251
416
  };
252
- const appendMailboxMessages = async () => {
253
- let appended = false;
254
- while (mailbox.length > 0) {
255
- const pending = mailbox.splice(0);
256
- for (const { text: prompt, delivery } of pending) {
257
- await store.append(sessionId, {
258
- type: 'turn.user',
259
- payload: {
260
- prompt,
261
- inputSource: 'parent_agent',
262
- ...(delivery ? { messageId: delivery.messageId } : {})
263
- },
264
- executionId,
265
- turnId
266
- });
267
- messages.push({ role: 'user', content: prompt });
268
- delivery?.applied();
269
- appended = true;
270
- }
271
- }
272
- return appended;
417
+ let compactContext;
418
+ const writeInputRecord = async (content, type, metadata, call, committed) => {
419
+ call.signal.throwIfAborted();
420
+ if (content.images?.length && !selectedModel.inputCapabilities.includes('IMAGE'))
421
+ throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
422
+ await withRecordProjection(async () => {
423
+ call.signal.throwIfAborted();
424
+ const images = content.images?.length
425
+ ? await store.persistInputImages(sessionId, content.images)
426
+ : [];
427
+ const record = await store.append(sessionId, {
428
+ type,
429
+ payload: {
430
+ ...metadata,
431
+ prompt: content.text,
432
+ ...(content.modelText === undefined ? {} : { modelText: content.modelText }),
433
+ ...(content.context === undefined ? {} : { context: content.context }),
434
+ ...(content.modelContext === undefined
435
+ ? {}
436
+ : { modelContext: content.modelContext }),
437
+ ...(images.length ? { images } : {})
438
+ },
439
+ executionId,
440
+ turnId
441
+ }, { signal: call.signal });
442
+ committed(content, record.recordId);
443
+ for (const text of content.modelContext ?? content.context ?? [])
444
+ messages.push({ role: 'user', content: text });
445
+ });
273
446
  };
447
+ const appendMailboxMessages = async () => inputs.drain(async (entry, request, call) => {
448
+ call.signal.throwIfAborted();
449
+ if (request.kind === 'compact')
450
+ return { changed: await compactContext(request.input), executionId };
451
+ if (request.kind === 'append') {
452
+ const result = await appendExtensionMessage(entry, request, call);
453
+ return { ...result, executionId };
454
+ }
455
+ const parent = entry.parentMessage;
456
+ let outcome;
457
+ const content = {
458
+ text: request.input.text,
459
+ ...(request.input.modelText === undefined
460
+ ? {}
461
+ : { modelText: request.input.modelText }),
462
+ ...(request.input.context === undefined
463
+ ? {}
464
+ : { context: request.input.context }),
465
+ ...(request.input.modelContext === undefined
466
+ ? {}
467
+ : { modelContext: request.input.modelContext }),
468
+ ...(request.input.images === undefined ? {} : { images: request.input.images })
469
+ };
470
+ await writeInputRecord(content, parent && !parent.delivery?.origin ? 'turn.user' : 'extension.input', parent && !parent.delivery?.origin
471
+ ? {
472
+ inputSource: 'parent_agent',
473
+ ...(parent.delivery ? { messageId: parent.delivery.messageId } : {})
474
+ }
475
+ : {
476
+ source: extensionInputSource(call, request.input.provenance, request.input.modelAttribution),
477
+ ...(parent?.delivery ? { messageId: parent.delivery.messageId } : {})
478
+ }, call, (actual, recordId) => {
479
+ outcome = { changed: true, executionId, recordId };
480
+ recordSessionInputCommit(entry, outcome);
481
+ messages.push({
482
+ role: 'user',
483
+ content: actual.modelText ?? actual.text,
484
+ ...(actual.images?.length
485
+ ? {
486
+ images: actual.images.map(({ mimeType, dataBase64 }) => ({
487
+ mimeType,
488
+ dataBase64
489
+ }))
490
+ }
491
+ : {})
492
+ });
493
+ parent?.delivery?.applied();
494
+ });
495
+ return outcome;
496
+ }, { executionId, turnId, signal: controller.signal });
274
497
  const recordEnvironment = async () => {
275
498
  const message = environmentContextUpdate(executionEnvironment, recordedEnvironment);
276
499
  if (!message)
@@ -284,44 +507,11 @@ export function createSessionOpener(services) {
284
507
  messages.push(message);
285
508
  recordedEnvironment = executionEnvironment;
286
509
  };
287
- let compactionUserMessages = [
288
- ...restored.userMessages,
289
- ...(mode === 'compact' ? [] : [input.prompt])
290
- ];
510
+ let compactionUserMessages = [...restored.userMessages];
291
511
  const hasRestoredUserImages = messages.some((message) => message.role === 'user' && Boolean(message.images?.length));
292
- const modelInputImages = mode === 'compact'
293
- ? undefined
294
- : input.images?.map(({ mimeType, dataBase64 }) => ({
295
- mimeType,
296
- dataBase64
297
- }));
512
+ let modelInputImages;
298
513
  const currentExecutionToolImages = [];
299
514
  let currentExecutionUserIndex;
300
- if (mode !== 'compact') {
301
- await recordEnvironment();
302
- const inputImageMetadata = input.images?.length
303
- ? await store.persistInputImages(sessionId, input.images)
304
- : [];
305
- // A failed sync can still leave a complete record; lease recovery decides image ownership.
306
- await store.append(sessionId, {
307
- type: 'turn.user',
308
- payload: {
309
- prompt: input.prompt,
310
- ...(internal.delivery ? { messageId: internal.delivery.messageId } : {}),
311
- ...(internal.inputSource === undefined
312
- ? {}
313
- : { inputSource: internal.inputSource }),
314
- ...(input.clientMessageId ? { clientMessageId: input.clientMessageId } : {}),
315
- ...(inputImageMetadata.length ? { images: inputImageMetadata } : {})
316
- },
317
- executionId,
318
- turnId
319
- });
320
- messages.push({ role: 'user', content: input.prompt });
321
- currentExecutionUserIndex = messages.length - 1;
322
- internal.delivery?.applied();
323
- await appendMailboxMessages();
324
- }
325
515
  await emit({ type: 'execution.started', mode });
326
516
  if (restored.ignoredImages > 0)
327
517
  await store.append(sessionId, {
@@ -357,25 +547,6 @@ export function createSessionOpener(services) {
357
547
  ? requestReasoningEffort
358
548
  : (lastConfigurationEffort(messages, selectedModel.id) ?? requestReasoningEffort)
359
549
  : undefined;
360
- if (mode !== 'compact' &&
361
- useEffortUpdates &&
362
- effectiveReasoningEffort !== reasoningEffort) {
363
- const update = {
364
- role: 'provider',
365
- content: '',
366
- provider: 'OPENAI_RESPONSES',
367
- providerModelId: selectedModel.id,
368
- item: { type: 'configuration_update', reasoning: { effort: reasoningEffort } }
369
- };
370
- messages.push(update);
371
- await store.append(sessionId, {
372
- type: 'model.context',
373
- payload: update,
374
- executionId,
375
- turnId
376
- });
377
- effectiveReasoningEffort = reasoningEffort;
378
- }
379
550
  if (mode !== 'compact' && subagents)
380
551
  await subagents.beginParentExecution({
381
552
  executionId,
@@ -410,16 +581,13 @@ export function createSessionOpener(services) {
410
581
  role: turn.role,
411
582
  initialMailbox: turn.initialMailbox,
412
583
  turnId: turn.turnId,
584
+ ...(turn.origin === undefined ? {} : { origin: turn.origin }),
413
585
  mcpExecutionScopeId: executionId,
414
586
  ...(turn.delivery ? { delivery: turn.delivery } : {}),
415
587
  ...(rolloutBudget === undefined ? {} : { sharedRolloutBudget: rolloutBudget })
416
588
  });
417
589
  }
418
590
  });
419
- if (mode !== 'compact' &&
420
- (input.images?.length || hasRestoredUserImages) &&
421
- !selectedModel.inputCapabilities.includes('IMAGE'))
422
- throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
423
591
  const catalogContext = {
424
592
  sessionId,
425
593
  executionId,
@@ -470,6 +638,49 @@ export function createSessionOpener(services) {
470
638
  const combinedTools = [...builtinTools, ...(extensions.tools ?? []), ...mcpTools];
471
639
  if (new Set(combinedTools.map((tool) => tool.name)).size !== combinedTools.length)
472
640
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool names conflict.');
641
+ const executionSourceNames = new Set(combinedTools.map((tool) => tool.name));
642
+ const supplementalBindings = services.extensions.options
643
+ .baseTools?.(sessionId, { executionId })
644
+ ?.filter((binding) => !executionSourceNames.has(binding.definition.name)) ?? [];
645
+ const sources = (executionToolSources = new ExecutionToolSources(executionId, sessionId, combinedTools, builtinTools, mcpCatalog, supplementalBindings));
646
+ const sourceBindings = sources.bindings;
647
+ const coreExtensions = await services.extensions.prepare(sessionId, controller.signal, sourceBindings, sessionSource.type === 'user' &&
648
+ (mode === 'run' || mode === 'plan') &&
649
+ !internal.automaticInput);
650
+ sessionRuntime.toolSources = sources;
651
+ const operation = (run) => services.extensions.operations.run({
652
+ sessionId,
653
+ sessionEpoch: coreExtensions.epoch,
654
+ executionId,
655
+ turnId,
656
+ actor: 'sdk',
657
+ entry: 'native-api',
658
+ signal: controller.signal
659
+ }, run);
660
+ await operation((call) => coreExtensions.ready(call));
661
+ const captureTools = () => operation((call) => {
662
+ return coreExtensions.capture(call, false, sourceBindings.filter((binding) => !supplementalBindings.includes(binding) || binding.available?.(call) !== false));
663
+ });
664
+ const streamModel = (start, signal = controller.signal) => transformCoreModelStream(start, signal, (event, signal) => operation((call) => coreExtensions.modelEvent(event, Object.freeze({ ...call, signal }))), () => coreExtensions.diagnose({
665
+ code: 'MAR_AGENT_MODEL_STREAM_CLEANUP_FAILED',
666
+ sessionId
667
+ }));
668
+ const transformModelRequest = async (candidate, bindings = captureTools(), modelId = selectedModel.id) => {
669
+ let lowered = candidate;
670
+ if (coreExtensions.hooks.hasInterceptors('model.request')) {
671
+ const bridge = new ExtensionRequestBridge(modelId, candidate);
672
+ const request = await operation((call) => coreExtensions.hooks.invoke('model.request', bridge.view, call, async (view) => view));
673
+ lowered = bridge.lower(request);
674
+ }
675
+ for (const definition of lowered.tools) {
676
+ const binding = bindings.get(definition.name);
677
+ const { description: _description, ...contract } = definition;
678
+ const { description: _capturedDescription, ...capturedContract } = binding?.definition ?? {};
679
+ if (!binding || !isDeepStrictEqual(contract, capturedContract))
680
+ throw new MarAgentError('MAR_AGENT_EXTENSION_MODEL_REQUEST_INVALID', 'Request tools do not match captured sources.');
681
+ }
682
+ return lowered;
683
+ };
473
684
  const toolEligible = (tool) => {
474
685
  toolCallers(tool);
475
686
  if (sessionSource.type === 'subagent' && tool.agentScope === 'ROOT_ONLY')
@@ -486,9 +697,12 @@ export function createSessionOpener(services) {
486
697
  : tool.name !== 'view_image' ||
487
698
  selectedModelForTools.inputCapabilities.includes('IMAGE');
488
699
  };
489
- mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
700
+ const updateMcpRegistry = () => mcpRegistry.update(coreMcpCatalog([...mcpCatalog.servers, ...coreExtensions.mcp.servers()], captureTools()), (tool) => toolEligible(tool.definition), { nativeToolNames: true });
701
+ updateMcpRegistry();
490
702
  rebindMcpToolSearchOutputs(messages, mcpRegistry);
491
- const availableTools = combinedTools.filter(toolEligible);
703
+ const availableTools = [...captureTools().values()]
704
+ .map((binding) => binding.definition)
705
+ .filter(toolEligible);
492
706
  const instructionContext = await loadAgentInstructions({
493
707
  home: description.homeDirectory,
494
708
  workspace: instructionWorkspace,
@@ -500,14 +714,11 @@ export function createSessionOpener(services) {
500
714
  });
501
715
  const instructionEntries = instructionContext?.entries ?? [];
502
716
  const currentInstructionSnapshot = renderAgentInstructions(instructionEntries, false);
503
- let agentInstructionInsertIndex = mode === 'compact'
504
- ? contextualInstructionInsertIndex(messages)
505
- : (currentExecutionUserIndex ?? Math.max(0, messages.length - 1));
506
- if (mode !== 'compact' &&
507
- agentInstructionInsertIndex &&
508
- messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
509
- agentInstructionInsertIndex--;
510
- const baseTools = () => [...builtinTools, ...(extensions.tools ?? [])].filter((tool) => toolEligible(tool) &&
717
+ let agentInstructionInsertIndex = mode === 'compact' ? contextualInstructionInsertIndex(messages) : messages.length;
718
+ const baseTools = () => [...captureTools().values()]
719
+ .filter((binding) => binding.mcp === undefined)
720
+ .map((binding) => binding.definition)
721
+ .filter((tool) => toolEligible(tool) &&
511
722
  (tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
512
723
  let executionTools = mcpRegistry.project(baseTools());
513
724
  const restoreMcpDiscovery = () => {
@@ -547,30 +758,15 @@ export function createSessionOpener(services) {
547
758
  sessionRuntime.contextualWorldState = contextualWorldState;
548
759
  if (!isDeepStrictEqual(previousContextualWorldState, contextualWorldState))
549
760
  await persistContextualWorldState(store, sessionId, contextualWorldState);
550
- const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => buildSystemPrompt({
551
- mode: promptMode,
552
- platform: description.platform,
553
- ...(description.architecture ? { architecture: description.architecture } : {}),
554
- ...(description.shell ? { shell: description.shell } : {}),
555
- ...(description.guaranteedCommands
556
- ? { guaranteedCommands: description.guaranteedCommands }
557
- : {}),
558
- workspace: logicalWorkspace,
559
- tools: availableTools
560
- .filter((tool) => !tool.name.startsWith('mcp__'))
561
- .map((tool) => tool.name),
562
- directTools: availableTools
563
- .filter((tool) => !tool.name.startsWith('mcp__') &&
564
- allowsDirectTool(tool, availableTools.some((candidate) => candidate.name === 'code_exec')))
565
- .map((tool) => tool.name),
566
- highDensityCompaction: selectedModel.highDensityCompaction,
567
- extension: [
568
- executionSystemInstruction,
569
- instructionContext?.skillCatalog,
570
- sessionSource.type === 'subagent' ? childSubagentInstruction : undefined
571
- ]
572
- .filter(Boolean)
573
- .join('\n\n')
761
+ const buildExecutionPromptSections = (availableTools = executionTools, promptMode = mode) => buildSessionPromptSections(availableTools, promptMode, selectedModel, executionSystemInstruction, instructionContext?.skillCatalog);
762
+ const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => renderPromptSections(buildExecutionPromptSections(availableTools, promptMode));
763
+ const promptComposition = (availableTools, promptMode) => ({
764
+ sections: buildExecutionPromptSections(availableTools, promptMode),
765
+ facts: {
766
+ modelId: selectedModel.id,
767
+ mode: promptMode,
768
+ tools: availableTools.map((tool) => tool.name)
769
+ }
574
770
  });
575
771
  const syncMcpWorldState = async (index) => {
576
772
  const snapshot = mcpRegistry.navigation();
@@ -593,302 +789,487 @@ export function createSessionOpener(services) {
593
789
  };
594
790
  if (mode !== 'compact')
595
791
  await syncMcpWorldState(agentInstructionInsertIndex);
596
- const initialSystemPrompt = mode === 'compact'
597
- ? buildExecutionSystemPrompt(executionTools, 'run')
598
- : buildExecutionSystemPrompt();
599
- let useManualCompactionFallback;
600
- if (mode === 'compact') {
601
- const compactSystemPrompt = initialSystemPrompt;
602
- const compactHistory = [...messages];
603
- const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
604
- const nativeMessages = buildNativeCompactionMessages(projectedHistory, {
605
- focus: input.prompt,
606
- highDensityCompaction: selectedModel.highDensityCompaction
607
- });
608
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
609
- const compactInputTokens = selectedModel.contextWindowTokens - compactOverheadTokens;
610
- if (compactInputTokens <= 0)
611
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
612
- const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
613
- focus: input.prompt,
614
- maxTokens: compactInputTokens
615
- }), selectedModel.highDensityCompaction);
616
- const useNative = shouldUseNativeCompaction({
617
- estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeMessages, executionTools),
618
- contextWindowTokens: selectedModel.contextWindowTokens,
619
- latestUsage: lastServerUsage,
620
- historyMessages: compactHistory,
621
- appendedMessages: nativeMessages.slice(projectedHistory.length)
622
- });
623
- messages.splice(0, messages.length, ...(useNative ? nativeMessages : fallbackMessages()));
624
- if (useNative) {
625
- let available = true;
626
- useManualCompactionFallback = () => {
627
- if (!available)
628
- return false;
629
- available = false;
630
- messages.splice(0, messages.length, ...fallbackMessages());
631
- return true;
632
- };
633
- }
634
- }
635
- await store.append(sessionId, {
636
- type: 'execution.header',
637
- payload: {
638
- promptVersion: MAR_AGENT_PROMPT_VERSION,
639
- promptHash: createHash('sha256').update(initialSystemPrompt).digest('hex'),
640
- modelId: selectedModel.id,
641
- modelName: selectedModel.name,
642
- reasoningEffort,
643
- contextWindowTokens: selectedModel.contextWindowTokens,
644
- mode
645
- },
646
- executionId,
647
- turnId
792
+ const writeCompactionRecord = async (summary, _automatic, signal, write) => withRecordProjection(async () => {
793
+ signal.throwIfAborted();
794
+ const stored = await write(summary, { signal });
795
+ inputs.recordCompactCommit({ changed: true, recordId: stored.recordId, executionId });
796
+ return stored;
648
797
  });
649
- let lastServerContextTokens = lastServerUsage?.contextInputTokens;
650
- const compactContext = async () => {
651
- const current = messages.at(-1);
652
- if (!current || messages.length < 2)
653
- return false;
654
- const retainCurrent = current.role === 'user' && current.contextKind !== 'environment';
655
- let compactSummary = '';
656
- const compactSystemPrompt = buildExecutionSystemPrompt(executionTools, 'run');
657
- const compactHistory = retainCurrent ? messages.slice(0, -1) : messages;
658
- const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
659
- const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
660
- highDensityCompaction: selectedModel.highDensityCompaction
661
- });
662
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
663
- const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
664
- if (compactInputTokens === 0)
665
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
666
- let usingNativeMessages = shouldUseNativeCompaction({
667
- estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, executionTools),
668
- contextWindowTokens: selectedModel.contextWindowTokens,
669
- latestUsage: lastServerUsage,
670
- historyMessages: compactHistory,
671
- appendedMessages: nativeCompactMessages.slice(projectedHistory.length)
672
- });
673
- const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
674
- maxTokens: Math.max(1, compactInputTokens)
675
- }), selectedModel.highDensityCompaction);
676
- let compactMessages = usingNativeMessages ? nativeCompactMessages : fallbackMessages();
677
- const compactionId = randomUUID();
678
- await emit({ type: 'context.compacting', compactionId });
679
- let latestUsage;
680
- const compactionRetryState = { attempts: 0 };
681
- const operation = createCompactionOperation(compactionId);
682
- try {
683
- while (true) {
684
- compactSummary = '';
685
- latestUsage = undefined;
686
- try {
687
- for await (const event of operation.start(selected, {
688
- retryState: compactionRetryState,
689
- system: compactSystemPrompt,
690
- messages: compactMessages,
691
- tools: executionTools,
692
- toolChoice: 'none',
693
- promptCacheKey: sessionId,
694
- reasoningEffort: requestReasoningEffort ?? reasoningEffort,
695
- onAttemptDiagnostic: recordModelAttempt('COMPACTION')
696
- })) {
697
- if (event.type === 'text.completed' &&
698
- (event.phase ?? 'final_answer') === 'final_answer')
699
- compactSummary = event.text.trim();
700
- else if (event.type === 'usage') {
701
- latestUsage = event.usage;
702
- await emit({ type: 'usage.updated', ...publicModelUsage(event.usage) });
798
+ const performCompact = (input, leaf) => operation((call) => inputs.performCompact(input, call, (commit) => coreExtensions.hooks.invoke('session.compact', input, call, async (input, effective) => {
799
+ effective.signal.throwIfAborted();
800
+ if (input.focus !== undefined && typeof input.focus !== 'string')
801
+ throw new MarAgentError('MAR_AGENT_SESSION_INPUT_INVALID', 'Compaction focus is invalid.');
802
+ const outcome = await leaf(input, effective);
803
+ commit(outcome);
804
+ return outcome;
805
+ })));
806
+ const captureRoundToolScope = () => {
807
+ const roundTools = executionTools;
808
+ const availableBindings = captureTools();
809
+ const roundBindings = new Map(roundTools.map((tool) => [tool.name, availableBindings.get(tool.name)]));
810
+ const defaultComposition = promptComposition(roundTools, mode === 'compact' ? 'run' : mode);
811
+ const defaultSystemPrompt = renderPromptSections(defaultComposition.sections);
812
+ let systemPrompt = defaultSystemPrompt;
813
+ const roundMcpExpectations = new Map(roundTools
814
+ .filter((tool) => roundBindings.get(tool.name)?.mcp !== undefined)
815
+ .map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
816
+ const parentFrame = subagents === undefined || mode === 'compact'
817
+ ? undefined
818
+ : subagents.captureParentFrame(executionId);
819
+ const toolContext = {
820
+ host: host,
821
+ todo,
822
+ ...(subagents === undefined || mode === 'compact'
823
+ ? {}
824
+ : {
825
+ subagents: coreExtensions.bindAgents(subagents.captureExecution(executionId))
826
+ }),
827
+ mode,
828
+ sessionId,
829
+ executionId,
830
+ modelId: selectedModel.id,
831
+ signal: controller.signal,
832
+ emit,
833
+ skills: instructionContext?.skills ?? new Map(),
834
+ invokeTool: (call, context, caller) => {
835
+ const invoke = () => coreExtensions.withTools(roundBindings, sessionRuntime.scheduleTool, () => services.extensions.operations.enter({
836
+ sessionId,
837
+ sessionEpoch: coreExtensions.epoch,
838
+ executionId,
839
+ turnId,
840
+ actor: caller === 'programmatic' ? 'code-mode' : 'model',
841
+ entry: caller,
842
+ signal: context.signal
843
+ }, (operation) => coreExtensions.invoke(call, operation, roundBindings)));
844
+ return sources.withFrame(context, () => parentFrame === undefined ? invoke() : parentFrame(invoke));
845
+ }
846
+ };
847
+ const recentImages = () => currentExecutionImages(modelInputImages, currentExecutionToolImages);
848
+ const toolScope = {
849
+ context: toolContext,
850
+ tools: roundTools,
851
+ expectations: roundMcpExpectations,
852
+ extensionToolNames: new Set([...roundBindings.values()]
853
+ .filter((binding) => binding.source.kind === 'host' || binding.source.kind === 'extension')
854
+ .map((binding) => binding.definition.name)),
855
+ schedule: sessionRuntime.scheduleTool,
856
+ assertLive: () => coreExtensions.assertLive(),
857
+ assertExecutionLive: () => sources.assertActive(),
858
+ emit: (event, assertLive) => emit(event, () => {
859
+ coreExtensions.assertLive();
860
+ assertLive?.();
861
+ }),
862
+ searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
863
+ signal: searchSignal,
864
+ base: baseTools(),
865
+ window: selectedModel.contextWindowTokens,
866
+ system: systemPrompt
867
+ }),
868
+ recentImages,
869
+ captureCodeContext: () => ({
870
+ ...toolContext,
871
+ recentImages: recentImages()
872
+ }),
873
+ codeModeContext: async (call) => {
874
+ const stateSnapshot = call.name === 'code_exec' ? await store.readCodeModeState(sessionId) : [];
875
+ return {
876
+ retainSession: async () => {
877
+ coreExtensions.assertLive();
878
+ const release = await store.retainSessionLease(sessionId, sessionWriterOwner);
879
+ let unpin;
880
+ try {
881
+ unpin = coreExtensions.pin();
703
882
  }
704
- else if (event.type === 'client_tool.call')
705
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
706
- }
707
- break;
708
- }
709
- catch (error) {
710
- if (await recoverModelStream(error, compactionRetryState, operation.signal)) {
711
- continue;
712
- }
713
- if (isProviderContextOverflow(error) && usingNativeMessages) {
714
- usingNativeMessages = false;
715
- compactMessages = fallbackMessages();
716
- compactionRetryState.attempts = 0;
717
- continue;
718
- }
719
- if (isProviderContextOverflow(error))
720
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The compaction request exceeds the model context window.', { cause: error });
721
- throw error;
722
- }
883
+ catch (error) {
884
+ try {
885
+ await release();
886
+ }
887
+ catch {
888
+ coreExtensions.diagnose({
889
+ code: 'MAR_AGENT_EXTENSION_CLEANUP_FAILED',
890
+ sessionId
891
+ });
892
+ }
893
+ throw error;
894
+ }
895
+ return async () => {
896
+ try {
897
+ await release();
898
+ }
899
+ finally {
900
+ unpin();
901
+ }
902
+ };
903
+ },
904
+ onSettled: async (result) => {
905
+ await store.append(sessionId, {
906
+ type: 'code.cell.completed',
907
+ payload: { ...result, callId: call.callId },
908
+ executionId,
909
+ turnId
910
+ });
911
+ },
912
+ supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
913
+ tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
914
+ state: stateSnapshot,
915
+ commitState: (mutations) => store.commitCodeModeState(sessionId, mutations, {
916
+ executionId,
917
+ turnId,
918
+ expectedState: stateSnapshot
919
+ })
920
+ };
723
921
  }
922
+ };
923
+ return {
924
+ roundTools,
925
+ roundBindings,
926
+ toolScope,
927
+ async composeSystemPrompt() {
928
+ const composed = await operation((call) => coreExtensions.composeModelSystem(defaultComposition, call));
929
+ systemPrompt = composed.text;
930
+ return composed;
931
+ }
932
+ };
933
+ };
934
+ sources.bind(captureRoundToolScope().toolScope);
935
+ sessionRuntime.requestMessages = {
936
+ executionId,
937
+ read: () => projectSessionRequestMessages(messages.map((message, index) => index === currentExecutionUserIndex && modelInputImages?.length
938
+ ? { ...message, images: modelInputImages }
939
+ : message), mode === 'compact' ? [] : contextualWorldState.entries, mode !== 'compact' && selectedModel.inputCapabilities.includes('IMAGE'), baseTools().some((tool) => tool.name === 'code_exec'))
940
+ };
941
+ if (mode !== 'compact') {
942
+ let entered = false;
943
+ const writeInput = async (content, call) => {
944
+ call.signal.throwIfAborted();
945
+ if ((content.images?.length || hasRestoredUserImages) &&
946
+ !selectedModel.inputCapabilities.includes('IMAGE'))
947
+ throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
948
+ if (entered) {
949
+ await sessionRuntime.sessionBackend.enqueue({ kind: 'submit', input: { ...content, delivery: 'idle' } }, call, (request, effective, next) => next(request, effective));
950
+ return {};
951
+ }
952
+ await recordEnvironment();
953
+ call.signal.throwIfAborted();
954
+ await writeInputRecord(content, internal.inputSource === 'extension' ? 'extension.input' : 'turn.user', {
955
+ ...(internal.extensionSource ? { source: internal.extensionSource } : {}),
956
+ ...(internal.delivery ? { messageId: internal.delivery.messageId } : {}),
957
+ ...(internal.inputSource === undefined
958
+ ? {}
959
+ : { inputSource: internal.inputSource }),
960
+ ...(input.clientMessageId ? { clientMessageId: input.clientMessageId } : {})
961
+ }, call, (actual, recordId) => {
962
+ entered = true;
963
+ compactionUserMessages.push(actual.text);
964
+ modelInputImages = actual.images?.map(({ mimeType, dataBase64 }) => ({
965
+ mimeType,
966
+ dataBase64
967
+ }));
968
+ messages.push({ role: 'user', content: actual.modelText ?? actual.text });
969
+ currentExecutionUserIndex = messages.length - 1;
970
+ internal.inputCommitted?.(recordId);
971
+ internal.delivery?.applied();
972
+ });
973
+ return {};
974
+ };
975
+ const content = {
976
+ text: input.prompt,
977
+ ...(internal.modelText === undefined ? {} : { modelText: internal.modelText }),
978
+ ...(internal.inputContext === undefined ? {} : { context: internal.inputContext }),
979
+ ...(internal.modelContext === undefined
980
+ ? {}
981
+ : { modelContext: internal.modelContext }),
982
+ ...(input.images === undefined ? {} : { images: input.images })
983
+ };
984
+ await operation((call) => internal.promptAdmitted
985
+ ? writeInput(content, call)
986
+ : invokeCorePromptSubmission(coreExtensions.hooks, content, call, writeInput));
987
+ if (!entered) {
988
+ await emit({ type: 'execution.completed' });
989
+ return { executionId, status: 'completed', finalAnswer: '', inputHandled: true };
724
990
  }
725
- finally {
726
- await operation.dispose();
727
- }
728
- if (!compactSummary)
729
- throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Automatic compact failed.');
730
- rolloutBudget?.recordUsage(latestUsage);
731
- const replacementHistory = buildCompactedHistory(compactSummary, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent ? current.content : undefined);
732
- const compactRecord = await store.compact(sessionId, {
733
- summary: compactSummary,
734
- replacementHistory,
735
- automatic: true,
736
- environment: executionEnvironment,
737
- modelId: selectedModel.id,
738
- contextWindowTokens: selectedModel.contextWindowTokens,
739
- ...(latestUsage === undefined ? {} : { usage: latestUsage }),
991
+ }
992
+ if (mode !== 'compact' &&
993
+ useEffortUpdates &&
994
+ effectiveReasoningEffort !== reasoningEffort) {
995
+ const update = {
996
+ role: 'provider',
997
+ content: '',
998
+ provider: 'OPENAI_RESPONSES',
999
+ providerModelId: selectedModel.id,
1000
+ item: { type: 'configuration_update', reasoning: { effort: reasoningEffort } }
1001
+ };
1002
+ messages.push(update);
1003
+ await store.append(sessionId, {
1004
+ type: 'model.context',
1005
+ payload: update,
740
1006
  executionId,
741
1007
  turnId
742
1008
  });
743
- contextGc.reset();
744
- messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory));
745
- restoreMcpDiscovery();
746
- projectedExecutionBudgetStage = undefined;
747
- recordedEnvironment = executionEnvironment;
748
- compactionUserMessages = replacementHistory
749
- .filter((message) => message.kind === 'user_input')
750
- .map((message) => message.content);
751
- agentInstructionInsertIndex = Math.max(0, messages.length - 1);
752
- currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
753
- contextualWorldState = resetContextualWorldState(compactRecord.recordId, agentInstructionInsertIndex, {
754
- instructionSnapshot: currentInstructionSnapshot,
755
- instructionContent: renderAgentInstructions(instructionEntries, true),
756
- resourceSnapshot: currentResourceSnapshot,
757
- previousMayContainResources: compactSummary.includes('# Retained Session resources')
758
- });
759
- sessionRuntime.contextualWorldState = contextualWorldState;
760
- await persistContextualWorldState(store, sessionId, contextualWorldState);
761
- lastServerContextTokens = undefined;
762
- lastServerUsage = undefined;
763
- requestReasoningEffort = reasoningEffort;
764
1009
  effectiveReasoningEffort = reasoningEffort;
765
- await emit({ type: 'context.compacted', compactionId });
766
- return true;
767
- };
768
- const ensureContextBudget = async () => {
769
- if (mode === 'compact' || lastServerContextTokens === undefined)
770
- return;
771
- const hardLimit = selectedModel.contextWindowTokens;
772
- const compactLimit = Math.floor((hardLimit * AGENT_EXECUTION_POLICY.automaticCompactionPercent) / 100);
773
- if (lastServerContextTokens < compactLimit)
774
- return;
775
- if (!(await compactContext()))
776
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Provider usage reached the compaction threshold but no history can be compacted.');
777
- };
778
- executionLoop = new ExecutionLoop({
779
- mode,
780
- sourceType: sessionSource.type,
781
- signal: controller.signal,
782
- model: selectedModel,
783
- messages,
784
- toolImages: currentExecutionToolImages,
785
- rolloutBudget,
786
- publish: emit,
787
- appendContext: async (message) => {
788
- await store.append(sessionId, {
789
- type: 'model.context',
790
- payload: message,
791
- executionId,
792
- turnId
1010
+ }
1011
+ const runPreparedExecution = async (compactFocus = input.prompt, compactSignal = controller.signal) => {
1012
+ const initialSystemPrompt = mode === 'compact'
1013
+ ? buildExecutionSystemPrompt(executionTools, 'run')
1014
+ : buildExecutionSystemPrompt();
1015
+ let useManualCompactionFallback;
1016
+ const manualAppendBoundary = pendingAppends.length;
1017
+ if (mode === 'compact') {
1018
+ const compactSystemPrompt = initialSystemPrompt;
1019
+ const compactHistory = [...messages];
1020
+ const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
1021
+ const nativeMessages = buildNativeCompactionMessages(projectedHistory, {
1022
+ focus: compactFocus,
1023
+ highDensityCompaction: selectedModel.highDensityCompaction
793
1024
  });
794
- },
795
- prepareRound: async (pendingOutputContinuation) => {
796
- await appendMailboxMessages();
797
- appendExecutionBudgetEnvironment();
798
- executionTools = mcpRegistry.project(baseTools());
799
- if (mode !== 'compact')
800
- await syncMcpWorldState(messages.length);
801
- await ensureContextBudget();
802
- if (mode !== 'compact')
803
- await syncMcpWorldState(messages.length);
804
- appendExecutionBudgetEnvironment();
805
- if (mode !== 'compact') {
806
- executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
807
- await recordEnvironment();
1025
+ const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
1026
+ const compactInputTokens = selectedModel.contextWindowTokens - compactOverheadTokens;
1027
+ if (compactInputTokens <= 0)
1028
+ throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
1029
+ const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
1030
+ focus: compactFocus,
1031
+ maxTokens: compactInputTokens
1032
+ }), selectedModel.highDensityCompaction);
1033
+ const useNative = shouldUseNativeCompaction({
1034
+ estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeMessages, executionTools),
1035
+ contextWindowTokens: selectedModel.contextWindowTokens,
1036
+ latestUsage: lastServerUsage,
1037
+ historyMessages: compactHistory,
1038
+ appendedMessages: nativeMessages.slice(projectedHistory.length)
1039
+ });
1040
+ messages.splice(0, messages.length, ...(useNative ? nativeMessages : fallbackMessages()));
1041
+ if (useNative) {
1042
+ let available = true;
1043
+ useManualCompactionFallback = () => {
1044
+ if (!available)
1045
+ return false;
1046
+ available = false;
1047
+ messages.splice(0, messages.length, ...fallbackMessages(), ...pendingAppends.slice(manualAppendBoundary).map((entry) => entry.message));
1048
+ return true;
1049
+ };
808
1050
  }
809
- const roundTools = executionTools;
810
- const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_exec');
811
- const residentTools = mcpRegistry.projectDirect(baseTools());
812
- const requestTools = (() => {
813
- if (selectedModel.protocol !== 'OPENAI_RESPONSES')
814
- return projectCodeModeTools(roundTools, residentTools);
815
- const directNames = new Set(residentTools.map((tool) => tool.name));
816
- // Discovery and caller policy are independent: keep nested declarations for active tools.
817
- return projectCodeModeTools(roundTools, residentTools).filter((tool) => directNames.has(tool.name));
818
- })();
819
- const systemPrompt = mode === 'compact'
820
- ? buildExecutionSystemPrompt(roundTools, 'run')
821
- : buildExecutionSystemPrompt(roundTools);
822
- const requestContextMessages = pendingOutputContinuation === undefined
823
- ? messages
824
- : [...messages, pendingOutputContinuation];
825
- const modelMessages = mode === 'compact'
826
- ? normalizeMessagesForModel(requestContextMessages, { supportsImages: false })
827
- : normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
828
- supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
829
- });
830
- const roundMcpExpectations = new Map(roundTools
831
- .filter((tool) => tool.name.startsWith('mcp__'))
832
- .map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
833
- const toolContext = {
834
- host: host,
835
- todo,
836
- ...(subagents === undefined ? {} : { subagents }),
837
- mode,
838
- sessionId,
839
- executionId,
1051
+ }
1052
+ await store.append(sessionId, {
1053
+ type: 'execution.header',
1054
+ payload: {
1055
+ promptVersion: MAR_AGENT_PROMPT_VERSION,
1056
+ promptHash: createHash('sha256').update(initialSystemPrompt).digest('hex'),
840
1057
  modelId: selectedModel.id,
841
- signal: controller.signal,
842
- emit,
843
- skills: instructionContext?.skills ?? new Map()
844
- };
845
- const recentImages = () => currentExecutionImages(modelInputImages, currentExecutionToolImages);
846
- const toolScope = {
847
- context: toolContext,
848
- tools: roundTools,
849
- expectations: roundMcpExpectations,
850
- extensionToolNames: new Set((extensions.tools ?? []).map((tool) => tool.name)),
851
- schedule: sessionRuntime.scheduleTool,
852
- emit,
853
- searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
854
- signal: searchSignal,
855
- base: baseTools(),
856
- window: selectedModel.contextWindowTokens,
857
- system: systemPrompt
858
- }),
859
- recentImages,
860
- captureCodeContext: () => ({
861
- ...toolContext,
862
- ...(subagents ? { subagents: subagents.captureExecution() } : {}),
863
- recentImages: recentImages()
864
- }),
865
- codeModeContext: async (call) => {
866
- const stateSnapshot = call.name === 'code_exec' ? await store.readCodeModeState(sessionId) : [];
867
- return {
868
- retainSession: () => store.retainSessionLease(sessionId, sessionWriterOwner),
869
- onSettled: async (result) => {
870
- await store.append(sessionId, {
871
- type: 'code.cell.completed',
872
- payload: { ...result, callId: call.callId },
873
- executionId,
874
- turnId
875
- });
876
- },
877
- supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
878
- tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
879
- state: stateSnapshot,
880
- commitState: (mutations) => store.commitCodeModeState(sessionId, mutations, {
881
- executionId,
882
- turnId,
883
- expectedState: stateSnapshot
884
- })
885
- };
1058
+ modelName: selectedModel.name,
1059
+ reasoningEffort,
1060
+ contextWindowTokens: selectedModel.contextWindowTokens,
1061
+ mode
1062
+ },
1063
+ executionId,
1064
+ turnId
1065
+ });
1066
+ let lastServerContextTokens = lastServerUsage?.contextInputTokens;
1067
+ const compactLeaf = async (input = {}, signal = controller.signal) => {
1068
+ if (messages.length < 2)
1069
+ return false;
1070
+ let compactSummary = '';
1071
+ const current = messages.at(-1);
1072
+ const retainCurrent = current.role === 'user' && current.contextKind !== 'environment';
1073
+ const compactHistory = retainCurrent ? messages.slice(0, -1) : [...messages];
1074
+ const appendBoundary = pendingAppends.length;
1075
+ const compactSystemPrompt = await operation((call) => coreExtensions.compose(promptComposition(executionTools, 'run'), call));
1076
+ const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
1077
+ const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
1078
+ ...(input.focus === undefined ? {} : { focus: input.focus }),
1079
+ highDensityCompaction: selectedModel.highDensityCompaction
1080
+ });
1081
+ const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
1082
+ const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
1083
+ if (compactInputTokens === 0)
1084
+ throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
1085
+ let usingNativeMessages = shouldUseNativeCompaction({
1086
+ estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, executionTools),
1087
+ contextWindowTokens: selectedModel.contextWindowTokens,
1088
+ latestUsage: lastServerUsage,
1089
+ historyMessages: compactHistory,
1090
+ appendedMessages: nativeCompactMessages.slice(projectedHistory.length)
1091
+ });
1092
+ const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
1093
+ ...(input.focus === undefined ? {} : { focus: input.focus }),
1094
+ maxTokens: Math.max(1, compactInputTokens)
1095
+ }), selectedModel.highDensityCompaction);
1096
+ let compactMessages = usingNativeMessages
1097
+ ? nativeCompactMessages
1098
+ : fallbackMessages();
1099
+ const compactionId = randomUUID();
1100
+ await emit({ type: 'context.compacting', compactionId });
1101
+ let latestUsage;
1102
+ const compactionRetryState = { attempts: 0 };
1103
+ const compaction = createCompactionOperation(compactionId, signal);
1104
+ try {
1105
+ while (true) {
1106
+ compactSummary = '';
1107
+ latestUsage = undefined;
1108
+ try {
1109
+ const request = await transformModelRequest({
1110
+ retryState: compactionRetryState,
1111
+ system: compactSystemPrompt,
1112
+ messages: compactMessages,
1113
+ tools: executionTools,
1114
+ toolChoice: 'none',
1115
+ promptCacheKey: sessionId,
1116
+ reasoningEffort: requestReasoningEffort ?? reasoningEffort,
1117
+ onAttemptDiagnostic: recordModelAttempt('COMPACTION')
1118
+ });
1119
+ for await (const event of streamModel((signal) => compaction.start({ start: (request) => selected.start(request, signal) }, request), compaction.signal)) {
1120
+ if (event.type === 'text.completed' &&
1121
+ (event.phase ?? 'final_answer') === 'final_answer')
1122
+ compactSummary = event.text.trim();
1123
+ else if (event.type === 'usage') {
1124
+ latestUsage = event.usage;
1125
+ await emit({ type: 'usage.updated', ...publicModelUsage(event.usage) });
1126
+ }
1127
+ else if (event.type === 'client_tool.call')
1128
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
1129
+ }
1130
+ break;
1131
+ }
1132
+ catch (error) {
1133
+ if (await recoverModelStream(error, compactionRetryState, compaction.signal)) {
1134
+ continue;
1135
+ }
1136
+ if (isProviderContextOverflow(error) && usingNativeMessages) {
1137
+ usingNativeMessages = false;
1138
+ compactMessages = fallbackMessages();
1139
+ compactionRetryState.attempts = 0;
1140
+ continue;
1141
+ }
1142
+ if (isProviderContextOverflow(error))
1143
+ throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The compaction request exceeds the model context window.', { cause: error });
1144
+ throw error;
1145
+ }
886
1146
  }
887
- };
888
- return {
889
- request: {
1147
+ }
1148
+ finally {
1149
+ await compaction.dispose();
1150
+ }
1151
+ if (!compactSummary)
1152
+ throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Automatic compact failed.');
1153
+ rolloutBudget?.recordUsage(latestUsage);
1154
+ let replacementHistory = buildCompactedHistory(compactSummary, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent ? current.content : undefined);
1155
+ signal.throwIfAborted();
1156
+ const compactRecord = await writeCompactionRecord(compactSummary, true, signal, async (actual, options) => {
1157
+ compactSummary = actual;
1158
+ replacementHistory = buildCompactedHistory(actual, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent ? current.content : undefined);
1159
+ const retainedModelContext = pendingAppends
1160
+ .slice(appendBoundary)
1161
+ .map((entry) => entry.message);
1162
+ const record = await store.compact(sessionId, {
1163
+ summary: compactSummary,
1164
+ replacementHistory,
1165
+ retainedModelContext,
1166
+ automatic: true,
1167
+ environment: executionEnvironment,
1168
+ modelId: selectedModel.id,
1169
+ contextWindowTokens: selectedModel.contextWindowTokens,
1170
+ ...(latestUsage === undefined ? {} : { usage: latestUsage }),
1171
+ executionId,
1172
+ turnId
1173
+ }, options);
1174
+ messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory), ...retainedModelContext);
1175
+ pendingAppends.length = 0;
1176
+ return record;
1177
+ });
1178
+ contextGc.reset();
1179
+ restoreMcpDiscovery();
1180
+ projectedExecutionBudgetStage = undefined;
1181
+ recordedEnvironment = executionEnvironment;
1182
+ compactionUserMessages = replacementHistory
1183
+ .filter((message) => message.kind === 'user_input')
1184
+ .map((message) => message.content);
1185
+ agentInstructionInsertIndex = Math.max(0, messages.length - 1);
1186
+ currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
1187
+ contextualWorldState = resetContextualWorldState(compactRecord.recordId, agentInstructionInsertIndex, {
1188
+ instructionSnapshot: currentInstructionSnapshot,
1189
+ instructionContent: renderAgentInstructions(instructionEntries, true),
1190
+ resourceSnapshot: currentResourceSnapshot,
1191
+ previousMayContainResources: compactSummary.includes('# Retained Session resources')
1192
+ });
1193
+ sessionRuntime.contextualWorldState = contextualWorldState;
1194
+ await persistContextualWorldState(store, sessionId, contextualWorldState);
1195
+ lastServerContextTokens = undefined;
1196
+ lastServerUsage = undefined;
1197
+ requestReasoningEffort = reasoningEffort;
1198
+ effectiveReasoningEffort = reasoningEffort;
1199
+ await emit({ type: 'context.compacted', compactionId });
1200
+ return true;
1201
+ };
1202
+ compactContext = async (input = {}) => {
1203
+ sources.bind(captureRoundToolScope().toolScope);
1204
+ return (await performCompact(input, async (effective, call) => ({
1205
+ changed: await compactLeaf(effective, call.signal),
1206
+ executionId
1207
+ }))).changed;
1208
+ };
1209
+ const ensureContextBudget = async () => {
1210
+ if (mode === 'compact' || lastServerContextTokens === undefined)
1211
+ return;
1212
+ const hardLimit = selectedModel.contextWindowTokens;
1213
+ const compactLimit = Math.floor((hardLimit * AGENT_EXECUTION_POLICY.automaticCompactionPercent) / 100);
1214
+ if (lastServerContextTokens < compactLimit)
1215
+ return;
1216
+ if (!(await compactContext()))
1217
+ throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Provider usage reached the compaction threshold but no history can be compacted.');
1218
+ };
1219
+ executionLoop = new ExecutionLoop({
1220
+ mode,
1221
+ sourceType: sessionSource.type,
1222
+ signal: controller.signal,
1223
+ model: selectedModel,
1224
+ messages,
1225
+ toolImages: currentExecutionToolImages,
1226
+ rolloutBudget,
1227
+ publish: emit,
1228
+ appendContext: (message) => withRecordProjection(async () => {
1229
+ await store.append(sessionId, {
1230
+ type: 'model.context',
1231
+ payload: message,
1232
+ executionId,
1233
+ turnId
1234
+ });
1235
+ }),
1236
+ prepareRound: async (pendingOutputContinuation) => {
1237
+ if (mode !== 'compact')
1238
+ await appendMailboxMessages();
1239
+ appendExecutionBudgetEnvironment();
1240
+ updateMcpRegistry();
1241
+ executionTools = mcpRegistry.project(baseTools());
1242
+ if (mode !== 'compact')
1243
+ await syncMcpWorldState(messages.length);
1244
+ await ensureContextBudget();
1245
+ if (mode !== 'compact')
1246
+ await syncMcpWorldState(messages.length);
1247
+ appendExecutionBudgetEnvironment();
1248
+ if (mode !== 'compact') {
1249
+ executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
1250
+ await recordEnvironment();
1251
+ }
1252
+ const { roundTools, roundBindings, toolScope, composeSystemPrompt } = captureRoundToolScope();
1253
+ const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_exec');
1254
+ const residentTools = mcpRegistry.projectDirect(baseTools());
1255
+ const requestTools = (() => {
1256
+ if (selectedModel.protocol !== 'OPENAI_RESPONSES')
1257
+ return projectCodeModeTools(roundTools, residentTools);
1258
+ const directNames = new Set(residentTools.map((tool) => tool.name));
1259
+ // Discovery and caller policy are independent: keep nested declarations for active tools.
1260
+ return projectCodeModeTools(roundTools, residentTools).filter((tool) => directNames.has(tool.name));
1261
+ })();
1262
+ const requestContextMessages = pendingOutputContinuation === undefined
1263
+ ? messages
1264
+ : [...messages, pendingOutputContinuation];
1265
+ const modelMessages = projectSessionRequestMessages(requestContextMessages, mode === 'compact' ? [] : contextualWorldState.entries, mode !== 'compact' && selectedModel.inputCapabilities.includes('IMAGE'), codeModeEnabled);
1266
+ sources.bind(toolScope);
1267
+ const composedSystem = await composeSystemPrompt();
1268
+ const systemPrompt = composedSystem.text;
1269
+ const candidateRequest = {
890
1270
  system: systemPrompt,
891
- messages: modelMessages.map((message) => projectCodeModeSearchResult(message, codeModeEnabled)),
1271
+ ...(composedSystem.blocks ? { systemBlocks: composedSystem.blocks } : {}),
1272
+ messages: modelMessages,
892
1273
  tools: requestTools,
893
1274
  ...(mode === 'compact' ? { toolChoice: 'none' } : {}),
894
1275
  promptCacheKey: sessionId,
@@ -896,114 +1277,152 @@ export function createSessionOpener(services) {
896
1277
  ...(requestReasoningEffort === undefined ? {} : { requestReasoningEffort }),
897
1278
  ...(modelInputImages?.length ? { images: modelInputImages } : {}),
898
1279
  onAttemptDiagnostic: recordModelAttempt(mode === 'compact' ? 'COMPACTION' : 'EXECUTION')
899
- },
900
- toolScope
901
- };
902
- },
903
- startModel: async function* (request) {
904
- if (manualCompactionId !== undefined && !manualCompactionStarted) {
905
- manualCompactionStarted = true;
906
- manualCompaction = createCompactionOperation(manualCompactionId);
907
- await emit({ type: 'context.compacting', compactionId: manualCompactionId });
908
- }
909
- yield* manualCompaction
910
- ? manualCompaction.start(selected, request)
911
- : selected.start(request, controller.signal);
912
- },
913
- recoverySignal: () => manualCompaction?.signal ?? controller.signal,
914
- compactContext,
915
- compactionFallback: useManualCompactionFallback,
916
- observeContextTokens: (tokens) => {
917
- lastServerContextTokens = tokens;
918
- },
919
- observeUsage: (usage) => {
920
- if (usage !== undefined) {
921
- lastServerContextTokens = usage.contextInputTokens;
922
- lastServerUsage = { ...usage, historyMessages: [...messages] };
923
- }
924
- },
925
- checkpoint: async (round, latestUsage) => {
926
- if (mode !== 'compact' &&
927
- sessionSource.type !== 'subagent' &&
928
- latestUsage !== undefined) {
929
- // Only a committed GC checkpoint may replace active context and invalidate continuation.
930
- const committed = await contextGc.run({
931
- store,
932
- sessionId,
933
- system: round.request.system,
934
- tools: round.toolScope.tools,
935
- modelId: selectedModel.id,
936
- contextWindowTokens: selectedModel.contextWindowTokens,
937
- usage: latestUsage,
938
- modelProtocol: selectedModel.protocol,
939
- responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
940
- workspace: logicalWorkspace,
941
- platform: description.platform,
942
- getActiveProcessIds: async () => {
943
- const resources = await host
944
- .listSessionResources?.(sessionId)
945
- .catch(() => undefined);
946
- return new Set((resources?.processes ?? [])
947
- .filter((process) => process.status === 'running')
948
- .map((process) => process.processId));
1280
+ };
1281
+ return {
1282
+ request: await transformModelRequest(candidateRequest, roundBindings),
1283
+ toolScope
1284
+ };
1285
+ },
1286
+ startModel: async function* (request) {
1287
+ const snapshots = mode === 'compact'
1288
+ ? undefined
1289
+ : (sessionRuntime.requestSnapshots ??= new ExtensionRequestSnapshots());
1290
+ const snapshot = snapshots?.record(selectedModel.id, request);
1291
+ if (manualCompactionId !== undefined && !manualCompactionStarted) {
1292
+ manualCompactionStarted = true;
1293
+ manualCompaction = createCompactionOperation(manualCompactionId, compactSignal);
1294
+ await emit({ type: 'context.compacting', compactionId: manualCompactionId });
1295
+ }
1296
+ const compaction = manualCompaction;
1297
+ yield* streamModel(async function* (nativeSignal) {
1298
+ const events = compaction
1299
+ ? compaction.start({ start: (value) => selected.start(value, nativeSignal) }, request)
1300
+ : selected.start(request, nativeSignal);
1301
+ for await (const event of events) {
1302
+ if (snapshot && event.type === 'completed' && event.reason !== 'cancelled')
1303
+ snapshots.complete(snapshot);
1304
+ yield event;
949
1305
  }
950
- });
951
- if (committed) {
952
- selected.resetContinuation?.();
953
- const refreshed = await restoreMessages(store, sessionId, committed.records, {
954
- includeImages: true,
955
- currentTurnId: turnId,
956
- projection: committed.projection
957
- });
958
- messages.splice(0, messages.length, ...refreshed.messages);
959
- restoreMcpDiscovery();
960
- projectedExecutionBudgetStage = undefined;
961
- recordedEnvironment = refreshed.environmentState;
962
- compactionUserMessages = [...refreshed.userMessages];
963
- agentInstructionInsertIndex =
964
- refreshed.currentTurnUserIndex ?? Math.max(0, messages.length - 1);
965
- if (agentInstructionInsertIndex &&
966
- messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
967
- agentInstructionInsertIndex--;
968
- currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
969
- contextualWorldState = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), agentInstructionInsertIndex, {
970
- instructionSnapshot: currentInstructionSnapshot,
971
- instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined)),
972
- resourceSnapshot: currentResourceSnapshot,
973
- previousMayContainResources: compactHistoryMayContainRetainedResources(committed.records)
1306
+ }, compaction?.signal ?? controller.signal);
1307
+ },
1308
+ recoverySignal: () => manualCompaction?.signal ?? controller.signal,
1309
+ compactContext,
1310
+ compactionFallback: useManualCompactionFallback,
1311
+ observeContextTokens: (tokens) => {
1312
+ lastServerContextTokens = tokens;
1313
+ },
1314
+ observeUsage: (usage) => {
1315
+ if (usage !== undefined) {
1316
+ lastServerContextTokens = usage.contextInputTokens;
1317
+ lastServerUsage = { ...usage, historyMessages: [...messages] };
1318
+ }
1319
+ },
1320
+ checkpoint: async (round, latestUsage) => {
1321
+ if (mode !== 'compact' &&
1322
+ sessionSource.type !== 'subagent' &&
1323
+ latestUsage !== undefined) {
1324
+ // Only a committed GC checkpoint may replace active context and invalidate continuation.
1325
+ const appendBoundary = pendingAppends.length;
1326
+ const committed = await contextGc.run({
1327
+ store,
1328
+ sessionId,
1329
+ system: round.request.system,
1330
+ tools: round.toolScope.tools,
1331
+ modelId: selectedModel.id,
1332
+ contextWindowTokens: selectedModel.contextWindowTokens,
1333
+ usage: latestUsage,
1334
+ modelProtocol: selectedModel.protocol,
1335
+ responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
1336
+ workspace: logicalWorkspace,
1337
+ platform: description.platform,
1338
+ getActiveProcessIds: async () => {
1339
+ const resources = await host
1340
+ .listSessionResources?.(sessionId)
1341
+ .catch(() => undefined);
1342
+ return new Set((resources?.processes ?? [])
1343
+ .filter((process) => process.status === 'running')
1344
+ .map((process) => process.processId));
1345
+ }
974
1346
  });
975
- sessionRuntime.contextualWorldState = contextualWorldState;
976
- await persistContextualWorldState(store, sessionId, contextualWorldState);
1347
+ if (committed) {
1348
+ selected.resetContinuation?.();
1349
+ const refreshed = await restoreMessages(store, sessionId, committed.records, {
1350
+ includeImages: true,
1351
+ currentTurnId: turnId,
1352
+ projection: committed.projection
1353
+ });
1354
+ const restoredIds = new Set(committed.records.map((record) => record.recordId));
1355
+ const lateMessages = pendingAppends
1356
+ .slice(appendBoundary)
1357
+ .filter((entry) => !restoredIds.has(entry.recordId))
1358
+ .map((entry) => entry.message);
1359
+ messages.splice(0, messages.length, ...refreshed.messages, ...lateMessages);
1360
+ restoreMcpDiscovery();
1361
+ projectedExecutionBudgetStage = undefined;
1362
+ recordedEnvironment = refreshed.environmentState;
1363
+ compactionUserMessages = [...refreshed.userMessages];
1364
+ agentInstructionInsertIndex =
1365
+ refreshed.currentTurnUserIndex ?? Math.max(0, messages.length - 1);
1366
+ if (agentInstructionInsertIndex &&
1367
+ messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
1368
+ agentInstructionInsertIndex--;
1369
+ currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
1370
+ contextualWorldState = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), agentInstructionInsertIndex, {
1371
+ instructionSnapshot: currentInstructionSnapshot,
1372
+ instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined)),
1373
+ resourceSnapshot: currentResourceSnapshot,
1374
+ previousMayContainResources: compactHistoryMayContainRetainedResources(committed.records)
1375
+ });
1376
+ sessionRuntime.contextualWorldState = contextualWorldState;
1377
+ await persistContextualWorldState(store, sessionId, contextualWorldState);
1378
+ }
1379
+ }
1380
+ },
1381
+ mailbox: {
1382
+ hasPending: () => inputs.hasBoundaryPending && !inputs.executing && mode !== 'compact',
1383
+ append: appendMailboxMessages,
1384
+ close: () => {
1385
+ acceptsMailbox = false;
977
1386
  }
978
1387
  }
979
- },
980
- mailbox: {
981
- hasPending: () => mailbox.length > 0,
982
- append: appendMailboxMessages,
983
- close: () => {
984
- acceptsMailbox = false;
985
- }
1388
+ });
1389
+ const completed = await executionLoop.run();
1390
+ finalAnswer = completed.finalAnswer;
1391
+ const compactSummary = mode === 'compact' ? finalAnswer.trim() : '';
1392
+ if (manualCompaction) {
1393
+ manualCompaction.signal.throwIfAborted();
1394
+ await manualCompaction.dispose();
986
1395
  }
987
- });
988
- const completed = await executionLoop.run();
989
- finalAnswer = completed.finalAnswer;
990
- const compactSummary = mode === 'compact' ? finalAnswer.trim() : '';
991
- if (manualCompaction) {
992
- manualCompaction.signal.throwIfAborted();
993
- await manualCompaction.dispose();
994
- }
1396
+ if (mode === 'compact')
1397
+ await writeCompactionRecord(compactSummary, false, compactSignal, async (actual, options) => {
1398
+ const retainedModelContext = pendingAppends
1399
+ .slice(manualAppendBoundary)
1400
+ .map((entry) => entry.message);
1401
+ const replacementHistory = buildCompactedHistory(actual, compactionUserMessages, compactUserTokenLimit);
1402
+ const record = await store.compact(sessionId, {
1403
+ summary: actual,
1404
+ retainedModelContext,
1405
+ replacementHistory,
1406
+ modelId: selectedModel.id,
1407
+ contextWindowTokens: selectedModel.contextWindowTokens,
1408
+ ...(completed.usage === undefined ? {} : { usage: completed.usage }),
1409
+ executionId,
1410
+ turnId
1411
+ }, options);
1412
+ messages.splice(0, messages.length, ...compactedHistoryMessages(replacementHistory), ...retainedModelContext);
1413
+ pendingAppends.length = 0;
1414
+ return record;
1415
+ });
1416
+ if (mode === 'compact' && manualCompactionId !== undefined)
1417
+ await emit({ type: 'context.compacted', compactionId: manualCompactionId });
1418
+ };
995
1419
  if (mode === 'compact')
996
- await store.compact(sessionId, {
997
- summary: compactSummary,
998
- replacementHistory: buildCompactedHistory(compactSummary, compactionUserMessages, compactUserTokenLimit),
999
- modelId: selectedModel.id,
1000
- contextWindowTokens: selectedModel.contextWindowTokens,
1001
- ...(completed.usage === undefined ? {} : { usage: completed.usage }),
1002
- executionId,
1003
- turnId
1420
+ await performCompact({ focus: input.prompt }, async (effective, call) => {
1421
+ await runPreparedExecution(effective.focus ?? DEFAULT_COMPACTION_FOCUS, call.signal);
1422
+ return { changed: true, executionId };
1004
1423
  });
1005
- if (mode === 'compact' && manualCompactionId !== undefined)
1006
- await emit({ type: 'context.compacted', compactionId: manualCompactionId });
1424
+ else
1425
+ await runPreparedExecution();
1007
1426
  subagents?.endParentExecution(executionId);
1008
1427
  await emit({ type: 'execution.completed' });
1009
1428
  return {
@@ -1036,7 +1455,17 @@ export function createSessionOpener(services) {
1036
1455
  };
1037
1456
  }
1038
1457
  finally {
1458
+ if (sessionRuntime.requestMessages?.executionId === executionId)
1459
+ delete sessionRuntime.requestMessages;
1460
+ if (sessionRuntime.appendedMessages?.executionId === executionId)
1461
+ delete sessionRuntime.appendedMessages;
1462
+ if (promptDefaults?.executionId === executionId)
1463
+ promptDefaults = undefined;
1039
1464
  acceptsMailbox = false;
1465
+ executionToolSources?.close();
1466
+ if (sessionRuntime.toolSources === executionToolSources)
1467
+ delete sessionRuntime.toolSources;
1468
+ internal.signal?.removeEventListener('abort', abortSource);
1040
1469
  if (ownsRolloutBudget)
1041
1470
  rolloutBudget.discard();
1042
1471
  await manualCompaction?.dispose();
@@ -1054,12 +1483,19 @@ export function createSessionOpener(services) {
1054
1483
  subagents?.endParentExecution(executionId);
1055
1484
  await releaseSession?.();
1056
1485
  active.delete(`${sessionId}:${executionId}`);
1486
+ sessionRuntime.sessionBackend?.kick();
1057
1487
  queue.close();
1058
1488
  }
1059
1489
  })();
1060
1490
  const executionKey = `${sessionId}:${executionId}`;
1061
1491
  executionResults.set(executionKey, result);
1062
- void result.then(() => executionResults.delete(executionKey), () => executionResults.delete(executionKey));
1492
+ void result.then((outcome) => {
1493
+ inputs.settleExecution(executionId, outcome.status);
1494
+ executionResults.delete(executionKey);
1495
+ }, () => {
1496
+ inputs.settleExecution(executionId, 'failed');
1497
+ executionResults.delete(executionKey);
1498
+ });
1063
1499
  return {
1064
1500
  executionId,
1065
1501
  turnId,
@@ -1072,11 +1508,251 @@ export function createSessionOpener(services) {
1072
1508
  enqueueMessage: (message, delivery) => {
1073
1509
  if (!acceptsMailbox || controller.signal.aborted)
1074
1510
  return false;
1075
- mailbox.push({ text: message, ...(delivery ? { delivery } : {}) });
1511
+ acceptParent({ text: message, ...(delivery ? { delivery } : {}) });
1076
1512
  return true;
1077
1513
  }
1078
1514
  };
1079
1515
  };
1516
+ sessionRuntime.sessionBackend ??= createExtensionSessionBackend({
1517
+ describe: async (call) => {
1518
+ call.signal.throwIfAborted();
1519
+ const current = await store.getSession(sessionId);
1520
+ const records = await store.read(sessionId);
1521
+ call.signal.throwIfAborted();
1522
+ const state = promptDefaults ?? services.modelState();
1523
+ const modelId = promptDefaults?.modelId ?? services.modelState().defaultModelId;
1524
+ const model = state.models.get(modelId);
1525
+ if (!model)
1526
+ throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'Selected model is unavailable.');
1527
+ const userTurns = records.filter((record) => {
1528
+ if (record.type !== 'turn.user')
1529
+ return false;
1530
+ const payload = record.payload;
1531
+ return payload.inputSource === undefined;
1532
+ }).length;
1533
+ return {
1534
+ sessionId,
1535
+ ...(logicalWorkspace ? { cwd: logicalWorkspace, root: logicalWorkspace } : {}),
1536
+ modelId,
1537
+ modelName: model.name,
1538
+ startedAt: Date.parse(current.createdAt),
1539
+ source: structuredClone(sessionSource),
1540
+ userTurns
1541
+ };
1542
+ },
1543
+ composePrompt: async (facts, call) => {
1544
+ call.signal.throwIfAborted();
1545
+ const defaults = promptDefaults;
1546
+ const state = defaults ?? services.modelState();
1547
+ const modelId = facts.modelId ?? defaults?.modelId ?? services.modelState().defaultModelId;
1548
+ const model = state.models.get(modelId);
1549
+ if (!model)
1550
+ throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'Selected model is unavailable.');
1551
+ const mode = facts.mode ?? defaults?.mode ?? 'run';
1552
+ const bindings = services.extensions.session(sessionId)?.capture(call);
1553
+ if (!bindings)
1554
+ throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_UNAVAILABLE', 'Session prompt tool sources are unavailable.');
1555
+ const definitions = [...bindings.values()]
1556
+ .filter((binding) => binding.available?.(call) !== false)
1557
+ .map((binding) => binding.definition)
1558
+ .filter((tool) => !(sessionSource.type === 'subagent' &&
1559
+ (tool.agentScope === 'ROOT_ONLY' ||
1560
+ tool.name.startsWith('agent_') ||
1561
+ tool.name === 'question')) &&
1562
+ !(tool.name === 'question' && mode !== 'plan') &&
1563
+ !(tool.name === 'view_image' && !model.inputCapabilities.includes('IMAGE')) &&
1564
+ !(['code_exec', 'code_exec_wait'].includes(tool.name) &&
1565
+ (!model.codeMode || !description.capabilities.includes('code.execute'))));
1566
+ const byName = new Map(definitions.map((tool) => [tool.name, tool]));
1567
+ const tools = facts.tools === undefined
1568
+ ? definitions
1569
+ : facts.tools.flatMap((name) => {
1570
+ const tool = byName.get(name);
1571
+ return tool ? [tool] : [];
1572
+ });
1573
+ const executionId = call.executionId ?? defaults?.executionId;
1574
+ if (executionId === undefined && host.listExecutionExtensions && !host.listSessionSkills)
1575
+ throw new MarAgentError('MAR_AGENT_EXTENSION_PROMPT_UNAVAILABLE', 'The Host does not provide an idle Session Skill catalog.');
1576
+ const hostSkills = executionId !== undefined
1577
+ ? ((await host.listExecutionExtensions?.({ sessionId, modelId, executionId }))
1578
+ ?.skills ?? [])
1579
+ : ((await host.listSessionSkills?.({ sessionId, modelId })) ?? []);
1580
+ call.signal.throwIfAborted();
1581
+ const instructions = await loadAgentInstructions({
1582
+ home: description.homeDirectory,
1583
+ workspace: instructionWorkspace,
1584
+ availableTools: new Set(tools.map((tool) => tool.name)),
1585
+ hostSkills,
1586
+ ...(settings.workspaceMapping === undefined
1587
+ ? {}
1588
+ : { workspaceDisplayPath: logicalWorkspace })
1589
+ });
1590
+ call.signal.throwIfAborted();
1591
+ return {
1592
+ sections: buildSessionPromptSections(tools, mode, model, defaults?.instruction ?? settings.systemInstruction, instructions?.skillCatalog),
1593
+ facts: { modelId, mode, tools: tools.map((tool) => tool.name) }
1594
+ };
1595
+ },
1596
+ captureRequest: async (call, input) => {
1597
+ call.signal.throwIfAborted();
1598
+ return sessionRuntime.requestSnapshots?.capture(input);
1599
+ },
1600
+ sessionId,
1601
+ coordinator: sessionInputs,
1602
+ activeExecutionId: () => [...active.keys()]
1603
+ .find((key) => key.startsWith(`${sessionId}:`))
1604
+ ?.slice(sessionId.length + 1),
1605
+ append: appendExtensionMessage,
1606
+ messages: async (input, call) => {
1607
+ const selection = normalizeSessionMessagesInput(input);
1608
+ call.signal.throwIfAborted();
1609
+ let target = sessionId;
1610
+ let targetModelId;
1611
+ if (selection.agentId !== undefined) {
1612
+ if (!sessionRuntime.agentsBackend)
1613
+ throw new MarAgentError('MAR_AGENT_SESSION_INPUT_UNAVAILABLE', 'Session child message access is unavailable.');
1614
+ const child = await sessionRuntime.agentsBackend.inspect({ agentId: selection.agentId }, call);
1615
+ target = child.agentId;
1616
+ targetModelId = child.modelId;
1617
+ }
1618
+ call.signal.throwIfAborted();
1619
+ const targetRuntime = sessionRuntimes.get(target);
1620
+ if (selection.view === 'request' && targetRuntime?.requestMessages)
1621
+ return sessionMessagesPage(targetRuntime.requestMessages.read(), selection);
1622
+ const modelState = services.modelState();
1623
+ const requestModel = selection.view === 'request'
1624
+ ? modelState.models.get(targetModelId ?? modelState.defaultModelId)
1625
+ : undefined;
1626
+ if (selection.view === 'request' && !requestModel)
1627
+ throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'Selected model is unavailable.');
1628
+ const includeImages = selection.includeImages &&
1629
+ (requestModel === undefined || requestModel.inputCapabilities.includes('IMAGE'));
1630
+ const state = await store.readExecutionState(target);
1631
+ call.signal.throwIfAborted();
1632
+ const restored = await restoreMessages(store, target, state.contextRecords, {
1633
+ includeImages,
1634
+ strictImages: includeImages
1635
+ });
1636
+ call.signal.throwIfAborted();
1637
+ if (requestModel) {
1638
+ const generation = latestContextualWorldStateGeneration(state.contextRecords);
1639
+ const cachedWorld = targetRuntime?.contextualWorldState;
1640
+ const world = cachedWorld?.generation === generation
1641
+ ? cachedWorld
1642
+ : parsePersistedContextualWorldState(await store.readContextualWorldState(target));
1643
+ call.signal.throwIfAborted();
1644
+ return sessionMessagesPage(projectSessionRequestMessages(restored.messages, world?.generation === generation ? (world?.entries ?? []) : [], requestModel.inputCapabilities.includes('IMAGE'), requestModel.codeMode), selection);
1645
+ }
1646
+ return sessionMessagesPage(restored.messages, selection, restored.toolOutcomes);
1647
+ },
1648
+ launch: async (entry, request, call) => {
1649
+ if (sessionSource.type !== 'user')
1650
+ throw new MarAgentError('MAR_AGENT_SESSION_INPUT_UNAVAILABLE', 'Idle subagent input must use its task controller.');
1651
+ let resolveInput;
1652
+ let rejectInput;
1653
+ const inputCommitted = new Promise((resolve, reject) => {
1654
+ resolveInput = resolve;
1655
+ rejectInput = reject;
1656
+ });
1657
+ void inputCommitted.catch(() => undefined);
1658
+ const handle = execution(request.kind === 'compact' ? 'compact' : 'run', {
1659
+ prompt: request.kind === 'compact'
1660
+ ? (request.input.focus ?? DEFAULT_COMPACTION_FOCUS)
1661
+ : request.input.text,
1662
+ ...(request.kind === 'submit' && request.input.images !== undefined
1663
+ ? { images: request.input.images }
1664
+ : {})
1665
+ }, {
1666
+ automaticInput: true,
1667
+ inputCommitted: (recordId) => {
1668
+ const outcome = { changed: true, recordId, executionId: handle.executionId };
1669
+ recordSessionInputCommit(entry, outcome);
1670
+ resolveInput(outcome);
1671
+ },
1672
+ ...(request.kind === 'submit' ? { promptAdmitted: true } : {}),
1673
+ ...(call.actor === 'sdk' && call.extensionId === undefined
1674
+ ? {}
1675
+ : { inputSource: 'extension' }),
1676
+ ...(request.kind === 'submit' && request.input.modelText !== undefined
1677
+ ? { modelText: request.input.modelText }
1678
+ : {}),
1679
+ ...(request.kind === 'submit' && request.input.context !== undefined
1680
+ ? { inputContext: request.input.context }
1681
+ : {}),
1682
+ ...(request.kind === 'submit' && request.input.modelContext !== undefined
1683
+ ? { modelContext: request.input.modelContext }
1684
+ : {}),
1685
+ ...(call.actor === 'sdk' && call.extensionId === undefined
1686
+ ? {}
1687
+ : {
1688
+ extensionSource: extensionInputSource(call, request.kind === 'submit' ? request.input.provenance : undefined, request.kind === 'submit' ? request.input.modelAttribution : undefined)
1689
+ })
1690
+ });
1691
+ void handle.result().then((result) => {
1692
+ if (request.kind === 'compact')
1693
+ resolveInput({
1694
+ changed: result.status === 'completed',
1695
+ executionId: handle.executionId
1696
+ });
1697
+ else
1698
+ rejectInput(new MarAgentError(result.errorCode ??
1699
+ (result.status === 'cancelled'
1700
+ ? 'MAR_AGENT_EXECUTION_CANCELLED'
1701
+ : 'MAR_AGENT_SESSION_INPUT_FAILED'), 'Execution ended before its input record was committed.'));
1702
+ }, rejectInput);
1703
+ return Object.assign(handle, { inputCommitted });
1704
+ },
1705
+ diagnose: (code, operationId) => services.extensions.session(sessionId)?.diagnose({ code, operationId, sessionId })
1706
+ });
1707
+ if (sessionSource.type === 'user')
1708
+ sessionRuntime.agentsBackend ??= createExtensionAgentsBackend({
1709
+ existingController: () => sessionRuntime.subagents,
1710
+ controller: async () => {
1711
+ services.assertCanStart();
1712
+ if (sessionRuntime.subagents === undefined)
1713
+ await openSession(sessionId);
1714
+ if (!sessionRuntime.subagents)
1715
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Session task controller is unavailable.');
1716
+ return sessionRuntime.subagents;
1717
+ },
1718
+ parent: (controller, call) => {
1719
+ const current = call.executionId === undefined
1720
+ ? undefined
1721
+ : controller.parentExecution(call.executionId);
1722
+ if (call.executionId !== undefined && !current)
1723
+ throw new MarAgentError('MAR_AGENT_SUBAGENT_FAILED', 'Parent Execution source is unavailable.');
1724
+ const state = services.modelState();
1725
+ const selected = state.models.get(state.defaultModelId);
1726
+ return {
1727
+ ...(current ?? {
1728
+ modelId: selected.id,
1729
+ reasoningEffort: selected.defaultReasoningEffort,
1730
+ models: state.models,
1731
+ emitTerminal: async () => { },
1732
+ startTurn: async (turn) => {
1733
+ const child = await openSession(turn.sessionId);
1734
+ return child.startSubagentTurn({
1735
+ prompt: turn.prompt,
1736
+ modelId: turn.modelId,
1737
+ reasoningEffort: turn.reasoningEffort,
1738
+ role: turn.role,
1739
+ initialMailbox: turn.initialMailbox,
1740
+ turnId: turn.turnId,
1741
+ ...(turn.origin === undefined ? {} : { origin: turn.origin }),
1742
+ ...(turn.delivery === undefined ? {} : { delivery: turn.delivery })
1743
+ });
1744
+ }
1745
+ }),
1746
+ operationId: call.operationId,
1747
+ origin: call,
1748
+ signal: call.signal,
1749
+ ...(call.ownerInstanceId === undefined
1750
+ ? {}
1751
+ : { ownerInstanceId: call.ownerInstanceId }),
1752
+ ...(call.extensionId === undefined ? {} : { extensionId: call.extensionId })
1753
+ };
1754
+ }
1755
+ });
1080
1756
  return {
1081
1757
  id: sessionId,
1082
1758
  run: (input) => execution('run', input),
@@ -1092,10 +1768,16 @@ export function createSessionOpener(services) {
1092
1768
  reasoningEffort: turn.reasoningEffort
1093
1769
  }, {
1094
1770
  publishHostEvents: false,
1095
- inputSource: 'parent_agent',
1771
+ ...(turn.origin === undefined ? {} : { signal: turn.origin.signal }),
1772
+ inputSource: turn.origin === undefined ? 'parent_agent' : 'extension',
1773
+ ...(turn.origin === undefined
1774
+ ? {}
1775
+ : { extensionSource: extensionInputSource(turn.origin) }),
1096
1776
  initialMailbox: turn.initialMailbox,
1097
1777
  turnId: turn.turnId,
1098
- mcpExecutionScopeId: turn.mcpExecutionScopeId,
1778
+ ...(turn.mcpExecutionScopeId === undefined
1779
+ ? {}
1780
+ : { mcpExecutionScopeId: turn.mcpExecutionScopeId }),
1099
1781
  ...(turn.delivery ? { delivery: turn.delivery } : {}),
1100
1782
  ...(turn.sharedRolloutBudget === undefined
1101
1783
  ? {}
@@ -1105,6 +1787,16 @@ export function createSessionOpener(services) {
1105
1787
  };
1106
1788
  return openSession;
1107
1789
  }
1790
+ function extensionInputSource(call, provenance, modelAttribution) {
1791
+ return {
1792
+ operationId: call.operationId,
1793
+ actor: call.actor,
1794
+ ...(call.extensionId ? { extensionId: call.extensionId } : {}),
1795
+ ...(call.ownerInstanceId ? { ownerInstanceId: call.ownerInstanceId } : {}),
1796
+ ...(provenance !== undefined ? { provenance } : {}),
1797
+ ...(modelAttribution === undefined ? {} : { modelAttribution })
1798
+ };
1799
+ }
1108
1800
  function modelAttemptFailureDiagnostic(diagnostic, summary) {
1109
1801
  if (summary === undefined)
1110
1802
  return diagnostic;
@@ -1115,3 +1807,20 @@ function modelAttemptFailureDiagnostic(diagnostic, summary) {
1115
1807
  message: `${prefix}${[...diagnostic.message].slice(0, availableCharacters).join('')}`
1116
1808
  };
1117
1809
  }
1810
+ /** Shared, side-effect-free projection for the real request and current Session request view. */
1811
+ function projectSessionRequestMessages(messages, entries, supportsImages, codeMode) {
1812
+ return normalizeMessagesForModel(insertContextualWorldState(messages, entries), {
1813
+ supportsImages
1814
+ }).map((message) => projectCodeModeSearchResult(message, codeMode));
1815
+ }
1816
+ function projectAppendedModelMessage(input) {
1817
+ if (input.visibility === 'transcript')
1818
+ return undefined;
1819
+ return {
1820
+ role: 'user',
1821
+ content: input.content,
1822
+ ...(input.textBlocks === undefined
1823
+ ? {}
1824
+ : { textBlocks: input.textBlocks.map((block) => ({ text: block.text })) })
1825
+ };
1826
+ }