@myagentroam/agent 0.9.173 → 0.9.175

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 (29) hide show
  1. package/dist/agent-core/extensions/compaction.d.ts +1 -1
  2. package/dist/agent-core/extensions/model-request.js +7 -1
  3. package/dist/agent-core/extensions/model-stream.js +5 -1
  4. package/dist/agent-core/model/anthropic-messages.js +35 -25
  5. package/dist/agent-core/model/catalog-file.d.ts +0 -2
  6. package/dist/agent-core/model/catalog-file.js +7 -0
  7. package/dist/agent-core/model/configuration.d.ts +0 -1
  8. package/dist/agent-core/model/configuration.js +0 -1
  9. package/dist/agent-core/model/contracts.d.ts +13 -0
  10. package/dist/agent-core/model/openai-responses.js +57 -48
  11. package/dist/agent-core/model/sse.js +59 -9
  12. package/dist/agent-core/prompts/compact.d.ts +0 -1
  13. package/dist/agent-core/prompts/compact.js +0 -56
  14. package/dist/agent-core/prompts/index.d.ts +0 -1
  15. package/dist/agent-core/prompts/index.js +2 -9
  16. package/dist/agent-core/runtime/compaction/history.d.ts +1 -2
  17. package/dist/agent-core/runtime/compaction/history.js +4 -4
  18. package/dist/agent-core/runtime/events.d.ts +1 -1
  19. package/dist/agent-core/runtime/execution.js +35 -24
  20. package/dist/agent-core/runtime/model-generation-timing.d.ts +12 -0
  21. package/dist/agent-core/runtime/model-generation-timing.js +39 -0
  22. package/dist/agent-core/sdk/session.js +7 -11
  23. package/dist/agent-core/tools/agent/agent-start.js +3 -1
  24. package/dist/agent-core/tools/code-mode/index.js +10 -5
  25. package/dist/agent-core/tools/definition-projection.d.ts +7 -0
  26. package/dist/agent-core/tools/definition-projection.js +26 -0
  27. package/dist/agent-core/tools/mcp/tool-search.js +12 -4
  28. package/dist/cli/main.js +6 -4
  29. package/package.json +1 -1
@@ -24,7 +24,7 @@ export interface CoreCompactContext {
24
24
  readonly messages: readonly CoreCompactMessage[];
25
25
  readonly focus?: string;
26
26
  readonly summaryTokenBudget: number;
27
- /** Return the Core compaction contract selected for this operation's main model snapshot. */
27
+ /** Return the Core's default structured handoff contract, independent of model configuration. */
28
28
  readonly getPrompt: () => string;
29
29
  /** Private, bounded telemetry only; reporting never changes the generation result. */
30
30
  readonly reportDiagnostic?: (diagnostic: CoreCompactDiagnostic) => Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { isDeepStrictEqual } from 'node:util';
3
3
  import { resolveModelTextBlocks } from '../model/text-blocks.js';
4
+ import { orderToolDefinitions, stableToolDefinition } from '../tools/definition-projection.js';
4
5
  import { MarAgentError } from '../error.js';
5
6
  /** Only actual SDK model dispatch records snapshots; public views carry no raw payload authority. */
6
7
  export class ExtensionRequestSnapshots {
@@ -151,6 +152,11 @@ export class ExtensionRequestBridge {
151
152
  };
152
153
  });
153
154
  const { toolChoice: _toolChoice, reasoningEffort: _reasoningEffort, images: _images, systemBlocks: _systemBlocks, ...retained } = this.original;
155
+ // Middleware selects declarations; it must not reorder a retained discovery prefix.
156
+ const order = new Map(this.original.tools.map((tool, index) => [tool.name, index]));
157
+ const tools = orderToolDefinitions(structuredClone(view.tools))
158
+ .sort((left, right) => (order.get(left.name) ?? order.size) - (order.get(right.name) ?? order.size))
159
+ .map(stableToolDefinition);
154
160
  return {
155
161
  ...retained,
156
162
  system: view.system,
@@ -158,7 +164,7 @@ export class ExtensionRequestBridge {
158
164
  ? {}
159
165
  : { systemBlocks: resolveModelTextBlocks(view.system, view.systemBlocks) }),
160
166
  messages,
161
- tools: structuredClone(view.tools),
167
+ tools,
162
168
  ...(view.toolChoice ? { toolChoice: view.toolChoice } : {}),
163
169
  ...(view.reasoningEffort ? { reasoningEffort: view.reasoningEffort } : {}),
164
170
  ...(view.images ? { images: structuredClone(view.images) } : {})
@@ -59,7 +59,10 @@ function validEvent(event) {
59
59
  return typeof event.sourceRef === 'string';
60
60
  case 'response.started':
61
61
  return typeof event.responseId === 'string';
62
+ case 'output.started':
62
63
  case 'output.delta':
64
+ case 'output.paused':
65
+ case 'output.completed':
63
66
  case 'output.reset':
64
67
  return true;
65
68
  case 'text.delta':
@@ -87,7 +90,8 @@ function validEvent(event) {
87
90
  typeof event.citation.id === 'string' &&
88
91
  typeof event.citation.url === 'string');
89
92
  case 'usage':
90
- return (!!event.usage &&
93
+ return ((event.final === undefined || typeof event.final === 'boolean') &&
94
+ !!event.usage &&
91
95
  typeof event.usage === 'object' &&
92
96
  Object.values(event.usage).every((value) => value === undefined ||
93
97
  (typeof value === 'number' && Number.isFinite(value) && value >= 0)));
@@ -1,4 +1,5 @@
1
1
  import { observationModelParts } from '../tools/code-mode/observations.js';
2
+ import { stableToolSchema } from '../tools/definition-projection.js';
2
3
  import { randomUUID } from 'node:crypto';
3
4
  import { MarAgentError } from '../error.js';
4
5
  import { resolveModelDefaultReasoningEffort } from './configuration.js';
@@ -38,7 +39,7 @@ export class AnthropicMessagesAdapter {
38
39
  await reportModelAttempt(request, { ...attemptDiagnostic, state: 'STARTED' });
39
40
  try {
40
41
  for await (const event of this.startAttempt(request, signal)) {
41
- if (event.type === 'output.delta') {
42
+ if (event.type.startsWith('output.')) {
42
43
  resetOutputTiming = true;
43
44
  yield event;
44
45
  continue;
@@ -97,7 +98,7 @@ export class AnthropicMessagesAdapter {
97
98
  : request.tools.map((tool) => ({
98
99
  name: tool.name,
99
100
  description: tool.description,
100
- input_schema: tool.inputSchema
101
+ input_schema: stableToolSchema(tool.inputSchema)
101
102
  }));
102
103
  const response = await fetchModelResponse(`${this.configuration.baseUrl.replace(/\/$/, '')}/v1/messages`, {
103
104
  method: 'POST',
@@ -149,18 +150,19 @@ export class AnthropicMessagesAdapter {
149
150
  if (type === 'message_start') {
150
151
  const message = record(event.message);
151
152
  responseId = text(message.id) ?? responseId;
152
- yield { type: 'response.started', responseId };
153
+ yield observeModelStreamEvent({ type: 'response.started', responseId }, modelStreamEventObservedAt(raw));
153
154
  const usage = record(message.usage);
154
155
  const inputTokens = inputTokenCount(usage.input_tokens);
155
156
  const cacheReadTokens = inputTokenCount(usage.cache_read_input_tokens);
156
157
  const cacheWriteTokens = inputTokenCount(usage.cache_creation_input_tokens);
157
158
  latestInputUsage = inputUsage(inputTokens, cacheReadTokens, cacheWriteTokens);
158
- yield {
159
+ yield observeModelStreamEvent({
159
160
  type: 'usage',
161
+ final: false,
160
162
  usage: {
161
163
  ...latestInputUsage
162
164
  }
163
- };
165
+ }, modelStreamEventObservedAt(raw));
164
166
  }
165
167
  else if (type === 'content_block_start') {
166
168
  const block = record(event.content_block);
@@ -173,16 +175,18 @@ export class AnthropicMessagesAdapter {
173
175
  for (const blockIndex of pendingTextBlocks) {
174
176
  const pending = blocks.get(blockIndex);
175
177
  if (pending?.type === 'text')
176
- yield {
178
+ yield observeModelStreamEvent({
177
179
  type: 'text.completed',
178
180
  itemId: pending.id,
179
181
  phase: 'commentary',
180
182
  text: pending.text
181
- };
183
+ }, modelStreamEventObservedAt(raw));
182
184
  }
183
185
  pendingTextBlocks.clear();
184
186
  }
185
187
  const id = text(block.id) ?? `${responseId}:content-${index}`;
188
+ if (['text', 'thinking', 'redacted_thinking', 'tool_use', 'server_tool_use'].includes(blockType))
189
+ yield observeModelStreamEvent({ type: 'output.started' }, modelStreamEventObservedAt(raw));
186
190
  blocks.set(index, {
187
191
  type: blockType,
188
192
  id,
@@ -193,18 +197,18 @@ export class AnthropicMessagesAdapter {
193
197
  ...(blockType === 'redacted_thinking' ? { data: block.data } : {})
194
198
  });
195
199
  if (blockType === 'text' && text(block.text))
196
- yield {
200
+ yield observeModelStreamEvent({
197
201
  type: 'text.delta',
198
202
  itemId: id,
199
203
  phase: 'commentary',
200
204
  delta: text(block.text) ?? ''
201
- };
205
+ }, modelStreamEventObservedAt(raw));
202
206
  else if (blockType === 'server_tool_use') {
203
207
  const toolName = text(block.name) ?? 'unknown';
204
208
  const kind = toolName === 'web_fetch' ? 'WEB_FETCH' : 'WEB_SEARCH';
205
209
  const summary = anthropicHostedInputSummary(record(block.input));
206
210
  hostedTools.set(id, { kind, toolName, summary });
207
- yield {
211
+ yield observeModelStreamEvent({
208
212
  type: 'hosted_tool',
209
213
  itemId: id,
210
214
  kind,
@@ -212,7 +216,7 @@ export class AnthropicMessagesAdapter {
212
216
  ...(Object.keys(summary).length ? { input: summary } : {}),
213
217
  status: 'RUNNING',
214
218
  summary
215
- };
219
+ }, modelStreamEventObservedAt(raw));
216
220
  }
217
221
  else if (blockType === 'web_search_tool_result' ||
218
222
  blockType === 'web_fetch_tool_result') {
@@ -227,7 +231,7 @@ export class AnthropicMessagesAdapter {
227
231
  ...(Array.isArray(result) && result.length > 20 ? { sourcesTruncated: true } : {})
228
232
  };
229
233
  hostedTools.set(toolId, { ...hosted, summary });
230
- yield {
234
+ yield observeModelStreamEvent({
231
235
  type: 'hosted_tool',
232
236
  itemId: toolId,
233
237
  kind: hosted.kind,
@@ -246,7 +250,7 @@ export class AnthropicMessagesAdapter {
246
250
  ...(summary.sourcesTruncated ? { truncated: true } : {}),
247
251
  status: anthropicHostedResultFailed(result) ? 'FAILED' : 'COMPLETED',
248
252
  summary
249
- };
253
+ }, modelStreamEventObservedAt(raw));
250
254
  }
251
255
  }
252
256
  }
@@ -282,10 +286,10 @@ export class AnthropicMessagesAdapter {
282
286
  const citation = record(delta.citation);
283
287
  const url = text(citation.url);
284
288
  if (url)
285
- yield {
289
+ yield observeModelStreamEvent({
286
290
  type: 'citation',
287
291
  citation: { id: `${block.id}-citation`, url, title: text(citation.title) }
288
- };
292
+ }, modelStreamEventObservedAt(raw));
289
293
  }
290
294
  }
291
295
  else if (type === 'content_block_stop') {
@@ -301,12 +305,13 @@ export class AnthropicMessagesAdapter {
301
305
  throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Anthropic returned an invalid or oversized native thinking block.');
302
306
  thinkingBytes += Buffer.byteLength(JSON.stringify(item));
303
307
  assertAnthropicThinkingBudget(thinkingBytes);
304
- yield { type: 'response.item', provider: 'ANTHROPIC_MESSAGES', item };
308
+ yield observeModelStreamEvent({ type: 'response.item', provider: 'ANTHROPIC_MESSAGES', item }, modelStreamEventObservedAt(raw));
305
309
  }
306
310
  if (block.type === 'thinking' && block.text)
307
- yield { type: 'reasoning.summary', itemId: block.id, text: block.text };
311
+ yield observeModelStreamEvent({ type: 'reasoning.summary', itemId: block.id, text: block.text }, modelStreamEventObservedAt(raw));
308
312
  }
309
313
  else if (block?.type === 'server_tool_use') {
314
+ yield observeModelStreamEvent({ type: 'output.paused' }, modelStreamEventObservedAt(raw));
310
315
  const hosted = hostedTools.get(block.id);
311
316
  if (hosted !== undefined) {
312
317
  let input;
@@ -319,7 +324,7 @@ export class AnthropicMessagesAdapter {
319
324
  const summary = { ...hosted.summary, ...anthropicHostedInputSummary(record(input)) };
320
325
  if (summary.query !== hosted.summary.query || summary.url !== hosted.summary.url) {
321
326
  hostedTools.set(block.id, { ...hosted, summary });
322
- yield {
327
+ yield observeModelStreamEvent({
323
328
  type: 'hosted_tool',
324
329
  itemId: block.id,
325
330
  kind: hosted.kind,
@@ -327,7 +332,7 @@ export class AnthropicMessagesAdapter {
327
332
  input: anthropicHostedInputSummary(record(input)),
328
333
  status: 'RUNNING',
329
334
  summary
330
- };
335
+ }, modelStreamEventObservedAt(raw));
331
336
  }
332
337
  }
333
338
  }
@@ -340,18 +345,20 @@ export class AnthropicMessagesAdapter {
340
345
  catch {
341
346
  argumentError = 'Tool arguments are invalid JSON.';
342
347
  }
343
- yield {
348
+ yield observeModelStreamEvent({
344
349
  type: 'client_tool.call',
345
350
  callId: block.id,
346
351
  name: block.name ?? 'unknown',
347
352
  arguments: args,
348
353
  ...(argumentError ? { argumentError } : {})
349
- };
354
+ }, modelStreamEventObservedAt(raw));
350
355
  }
351
356
  }
352
357
  else if (type === 'message_delta') {
353
358
  const delta = record(event.delta);
354
359
  const reason = text(delta.stop_reason);
360
+ if (reason !== undefined)
361
+ yield observeModelStreamEvent({ type: 'output.completed' }, modelStreamEventObservedAt(raw));
355
362
  terminal =
356
363
  reason === 'tool_use'
357
364
  ? 'tool_use'
@@ -364,7 +371,7 @@ export class AnthropicMessagesAdapter {
364
371
  for (const blockIndex of pendingTextBlocks) {
365
372
  const block = blocks.get(blockIndex);
366
373
  if (block?.type === 'text')
367
- yield { type: 'text.completed', itemId: block.id, phase, text: block.text };
374
+ yield observeModelStreamEvent({ type: 'text.completed', itemId: block.id, phase, text: block.text }, modelStreamEventObservedAt(raw));
368
375
  }
369
376
  pendingTextBlocks.clear();
370
377
  const usage = record(event.usage);
@@ -375,13 +382,14 @@ export class AnthropicMessagesAdapter {
375
382
  const zeroPlaceholder = inputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0;
376
383
  if (!zeroPlaceholder)
377
384
  latestInputUsage = inputUsage(inputTokens ?? latestInputUsage?.inputTokens, cacheReadTokens ?? latestInputUsage?.cacheReadTokens, cacheWriteTokens ?? latestInputUsage?.cacheWriteTokens);
378
- yield {
385
+ yield observeModelStreamEvent({
379
386
  type: 'usage',
387
+ final: reason !== undefined,
380
388
  usage: {
381
389
  ...latestInputUsage,
382
390
  outputTokens: number(usage.output_tokens)
383
391
  }
384
- };
392
+ }, modelStreamEventObservedAt(raw));
385
393
  }
386
394
  else if (type === 'message_stop') {
387
395
  yield observeModelStreamEvent({ type: 'completed', reason: terminal ?? 'completed' }, modelStreamEventObservedAt(raw));
@@ -399,7 +407,7 @@ export class AnthropicMessagesAdapter {
399
407
  : {});
400
408
  }
401
409
  else
402
- yield { type: 'unknown', nativeType: type };
410
+ yield observeModelStreamEvent({ type: 'unknown', nativeType: type }, modelStreamEventObservedAt(raw));
403
411
  }
404
412
  if (terminal === undefined)
405
413
  throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Anthropic Messages stream ended without a terminal event.', {
@@ -462,6 +470,8 @@ function anthropicHostedResultFailed(result) {
462
470
  return entries.some((entry) => text(record(entry).type)?.endsWith('_tool_result_error'));
463
471
  }
464
472
  function isSemanticEvent(event) {
473
+ if (event.type.startsWith('output.'))
474
+ return false;
465
475
  return event.type !== 'response.started' && event.type !== 'usage';
466
476
  }
467
477
  export function isAnthropicRecoverableStreamError(error) {
@@ -28,7 +28,6 @@ export declare function serializeModel(model: CliModelConfiguration): {
28
28
  defaultReasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max" | "ultra" | undefined;
29
29
  titleGeneration: boolean;
30
30
  codeMode: boolean;
31
- highDensityCompaction: boolean;
32
31
  fastMode?: boolean | undefined;
33
32
  serviceTier?: "default" | "auto" | "flex" | "priority" | "fast" | "ultrafast" | undefined;
34
33
  hostedWebSearch: boolean;
@@ -55,7 +54,6 @@ export declare function serializeModel(model: CliModelConfiguration): {
55
54
  defaultReasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max" | "ultra" | undefined;
56
55
  titleGeneration: boolean;
57
56
  codeMode: boolean;
58
- highDensityCompaction: boolean;
59
57
  fastMode?: boolean | undefined;
60
58
  serviceTier?: "default" | "auto" | "flex" | "priority" | "fast" | "ultrafast" | undefined;
61
59
  hostedWebSearch: boolean;
@@ -52,6 +52,13 @@ function parseCliModelCatalog(input, options = {}) {
52
52
  return parseModelCatalog(input, options);
53
53
  const externalCredentialModels = new Set();
54
54
  const normalizedModels = input.models.map((model, index) => {
55
+ if (isRecord(model) && Object.hasOwn(model, 'highDensityCompaction')) {
56
+ if (typeof model.highDensityCompaction !== 'boolean')
57
+ throw invalidCatalog();
58
+ const { highDensityCompaction: _compaction, ...configuration } = model;
59
+ void _compaction;
60
+ model = configuration;
61
+ }
55
62
  if (isRecord(model) && Object.hasOwn(model, 'responsesEncoding')) {
56
63
  if (model.protocol !== 'OPENAI_RESPONSES' ||
57
64
  (model.responsesEncoding !== 'STANDARD' && model.responsesEncoding !== 'LITE'))
@@ -69,7 +69,6 @@ declare const rawModelSchema: z.ZodObject<{
69
69
  }>>;
70
70
  titleGeneration: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
71
71
  codeMode: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
72
- highDensityCompaction: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
73
72
  fastMode: z.ZodOptional<z.ZodBoolean>;
74
73
  serviceTier: z.ZodOptional<z.ZodEnum<{
75
74
  default: "default";
@@ -49,7 +49,6 @@ const rawModelSchema = z
49
49
  defaultReasoningEffort: marAgentReasoningEffortSchema.optional(),
50
50
  titleGeneration: z.boolean().optional().default(false),
51
51
  codeMode: z.boolean().optional().default(false),
52
- highDensityCompaction: z.boolean().optional().default(false),
53
52
  fastMode: z.boolean().optional(),
54
53
  serviceTier: z.enum(['auto', 'default', 'flex', 'priority', 'fast', 'ultrafast']).optional(),
55
54
  hostedWebSearch: z.boolean(),
@@ -51,6 +51,12 @@ export interface ModelAttemptDiagnostic {
51
51
  connectionId?: string;
52
52
  connectionReused?: boolean;
53
53
  proxyType?: 'DIRECT' | 'SOCKS5';
54
+ /** Hashes of the lowered full logical request; never include credentials or raw bodies. */
55
+ requestInstructionsHash?: string;
56
+ requestToolsHash?: string;
57
+ requestPropertiesHash?: string;
58
+ requestInputHash?: string;
59
+ requestInputItems?: number;
54
60
  /** Actual serialized WebSocket request size, including inline image Base64. */
55
61
  requestBytes?: number;
56
62
  imageCount?: number;
@@ -113,8 +119,14 @@ export type ModelStopReason = 'completed' | 'tool_use' | 'pause' | 'max_output'
113
119
  export type ModelEvent = {
114
120
  type: 'response.started';
115
121
  responseId: string;
122
+ } | {
123
+ type: 'output.started';
116
124
  } | {
117
125
  type: 'output.delta';
126
+ } | {
127
+ type: 'output.paused';
128
+ } | {
129
+ type: 'output.completed';
118
130
  } | {
119
131
  type: 'output.reset';
120
132
  } | {
@@ -173,6 +185,7 @@ export type ModelEvent = {
173
185
  } | {
174
186
  type: 'usage';
175
187
  usage: ModelUsage;
188
+ final?: boolean;
176
189
  } | {
177
190
  type: 'unknown';
178
191
  nativeType: string;