@myagentroam/agent 0.9.159 → 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 (128) hide show
  1. package/dist/agent-core/extensions/agents.d.ts +69 -0
  2. package/dist/agent-core/extensions/agents.js +160 -0
  3. package/dist/agent-core/extensions/contracts.d.ts +260 -0
  4. package/dist/agent-core/extensions/contracts.js +1 -0
  5. package/dist/agent-core/extensions/data.d.ts +84 -0
  6. package/dist/agent-core/extensions/data.js +83 -0
  7. package/dist/agent-core/extensions/environment.d.ts +27 -0
  8. package/dist/agent-core/extensions/environment.js +52 -0
  9. package/dist/agent-core/extensions/files.d.ts +64 -0
  10. package/dist/agent-core/extensions/files.js +26 -0
  11. package/dist/agent-core/extensions/hooks.d.ts +32 -0
  12. package/dist/agent-core/extensions/hooks.js +135 -0
  13. package/dist/agent-core/extensions/manager.d.ts +39 -0
  14. package/dist/agent-core/extensions/manager.js +458 -0
  15. package/dist/agent-core/extensions/mcp.d.ts +99 -0
  16. package/dist/agent-core/extensions/mcp.js +433 -0
  17. package/dist/agent-core/extensions/model-request.d.ts +60 -0
  18. package/dist/agent-core/extensions/model-request.js +167 -0
  19. package/dist/agent-core/extensions/model-stream.d.ts +9 -0
  20. package/dist/agent-core/extensions/model-stream.js +101 -0
  21. package/dist/agent-core/extensions/models.d.ts +43 -0
  22. package/dist/agent-core/extensions/models.js +1 -0
  23. package/dist/agent-core/extensions/network.d.ts +32 -0
  24. package/dist/agent-core/extensions/network.js +97 -0
  25. package/dist/agent-core/extensions/operation.d.ts +29 -0
  26. package/dist/agent-core/extensions/operation.js +98 -0
  27. package/dist/agent-core/extensions/processes.d.ts +52 -0
  28. package/dist/agent-core/extensions/processes.js +283 -0
  29. package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
  30. package/dist/agent-core/extensions/prompt-composition.js +106 -0
  31. package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
  32. package/dist/agent-core/extensions/prompt-input.js +46 -0
  33. package/dist/agent-core/extensions/resources.d.ts +27 -0
  34. package/dist/agent-core/extensions/resources.js +221 -0
  35. package/dist/agent-core/extensions/services.d.ts +31 -0
  36. package/dist/agent-core/extensions/services.js +61 -0
  37. package/dist/agent-core/extensions/session-input.d.ts +151 -0
  38. package/dist/agent-core/extensions/session-input.js +351 -0
  39. package/dist/agent-core/extensions/session-messages.d.ts +41 -0
  40. package/dist/agent-core/extensions/session-messages.js +76 -0
  41. package/dist/agent-core/extensions/session.d.ts +68 -0
  42. package/dist/agent-core/extensions/session.js +593 -0
  43. package/dist/agent-core/extensions/settings.d.ts +59 -0
  44. package/dist/agent-core/extensions/settings.js +115 -0
  45. package/dist/agent-core/extensions/store.d.ts +10 -0
  46. package/dist/agent-core/extensions/store.js +131 -0
  47. package/dist/agent-core/extensions/tools.d.ts +61 -0
  48. package/dist/agent-core/extensions/tools.js +266 -0
  49. package/dist/agent-core/host/contracts.d.ts +25 -0
  50. package/dist/agent-core/host/local-agent-host.d.ts +10 -1
  51. package/dist/agent-core/host/local-agent-host.js +132 -20
  52. package/dist/agent-core/host/local-file-access.d.ts +30 -0
  53. package/dist/agent-core/host/local-file-access.js +107 -0
  54. package/dist/agent-core/host/local-network-access.d.ts +6 -0
  55. package/dist/agent-core/host/local-network-access.js +97 -0
  56. package/dist/agent-core/host/local-process-executor.d.ts +5 -1
  57. package/dist/agent-core/host/local-session-environment.d.ts +18 -0
  58. package/dist/agent-core/host/local-session-environment.js +102 -0
  59. package/dist/agent-core/model/anthropic-messages.js +73 -23
  60. package/dist/agent-core/model/contracts.d.ts +12 -0
  61. package/dist/agent-core/model/openai-responses.js +73 -12
  62. package/dist/agent-core/model/text-blocks.d.ts +7 -0
  63. package/dist/agent-core/model/text-blocks.js +40 -0
  64. package/dist/agent-core/prompts/index.d.ts +6 -2
  65. package/dist/agent-core/prompts/index.js +38 -24
  66. package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
  67. package/dist/agent-core/runtime/context/normalize.js +21 -1
  68. package/dist/agent-core/runtime/context/projection.js +35 -1
  69. package/dist/agent-core/runtime/context/request-budget.js +5 -2
  70. package/dist/agent-core/runtime/context/restore.d.ts +5 -0
  71. package/dist/agent-core/runtime/context/restore.js +47 -4
  72. package/dist/agent-core/runtime/context/usage.js +2 -3
  73. package/dist/agent-core/runtime/contracts.d.ts +2 -0
  74. package/dist/agent-core/runtime/execution.js +5 -2
  75. package/dist/agent-core/sdk/agent.js +177 -40
  76. package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
  77. package/dist/agent-core/sdk/extension-agents.js +51 -0
  78. package/dist/agent-core/sdk/extension-models.d.ts +13 -0
  79. package/dist/agent-core/sdk/extension-models.js +128 -0
  80. package/dist/agent-core/sdk/extension-session.d.ts +30 -0
  81. package/dist/agent-core/sdk/extension-session.js +103 -0
  82. package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
  83. package/dist/agent-core/sdk/extension-tools.js +126 -0
  84. package/dist/agent-core/sdk/session.d.ts +26 -2
  85. package/dist/agent-core/sdk/session.js +1215 -506
  86. package/dist/agent-core/sdk/types.d.ts +3 -0
  87. package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
  88. package/dist/agent-core/session/jsonl-store.d.ts +28 -8
  89. package/dist/agent-core/session/jsonl-store.js +124 -84
  90. package/dist/agent-core/session/types.d.ts +4 -1
  91. package/dist/agent-core/storage/file-lock.d.ts +4 -0
  92. package/dist/agent-core/storage/file-lock.js +83 -0
  93. package/dist/agent-core/subagent/contracts.d.ts +11 -1
  94. package/dist/agent-core/subagent/scheduler.d.ts +8 -0
  95. package/dist/agent-core/subagent/session-controller.d.ts +22 -1
  96. package/dist/agent-core/subagent/session-controller.js +355 -98
  97. package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
  98. package/dist/agent-core/tools/code-mode/cells.js +5 -15
  99. package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
  100. package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
  101. package/dist/agent-core/tools/code-mode/index.js +1 -2
  102. package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
  103. package/dist/agent-core/tools/code-mode/observations.js +16 -5
  104. package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
  105. package/dist/agent-core/tools/execution.d.ts +11 -2
  106. package/dist/agent-core/tools/execution.js +58 -21
  107. package/dist/agent-core/tools/input.d.ts +2 -0
  108. package/dist/agent-core/tools/input.js +36 -0
  109. package/dist/agent-core/tools/local/apply-patch.js +21 -17
  110. package/dist/agent-core/tools/local/exec.d.ts +4 -4
  111. package/dist/agent-core/tools/local/exec.js +234 -62
  112. package/dist/agent-core/tools/local/registry.d.ts +6 -0
  113. package/dist/agent-core/tools/local/registry.js +60 -9
  114. package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
  115. package/dist/agent-core/tools/mcp/catalog.js +4 -4
  116. package/dist/agent-core/tools/mcp/history.js +2 -2
  117. package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
  118. package/dist/agent-core/tools/mcp/tool-search.js +2 -2
  119. package/dist/agent-core/tools/output-projection.d.ts +3 -1
  120. package/dist/agent-core/tools/output-projection.js +11 -2
  121. package/dist/agent-core/tools/runtime-context.d.ts +1 -0
  122. package/dist/agent-core/tools/runtime.d.ts +3 -1
  123. package/dist/agent-core/tools/runtime.js +22 -7
  124. package/dist/agent-core/tools/scheduler.d.ts +2 -0
  125. package/dist/agent-core/tools/scheduler.js +43 -6
  126. package/dist/index.d.ts +21 -0
  127. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
  128. package/package.json +1 -1
@@ -5,6 +5,7 @@ import { resolveModelDefaultReasoningEffort } from './configuration.js';
5
5
  import { isRetryableUpstreamOverloadCode, number, parseSse, record, text, observeModelStreamEvent, modelStreamEventObservedAt } from './sse.js';
6
6
  import { fetchModelResponse, modelRetryMaxAttempts, isRetryableModelRequestError, isRetryableModelTransportError, waitForModelRetry } from './http.js';
7
7
  import { anthropicThinkingBlock, assertAnthropicThinkingBudget } from './anthropic-thinking.js';
8
+ import { resolveModelTextBlocks } from './text-blocks.js';
8
9
  export class AnthropicMessagesAdapter {
9
10
  configuration;
10
11
  constructor(configuration) {
@@ -74,20 +75,25 @@ export class AnthropicMessagesAdapter {
74
75
  },
75
76
  body: JSON.stringify({
76
77
  model: this.configuration.modelId,
77
- system: [
78
- {
79
- type: 'text',
80
- text: request.system,
81
- cache_control: { type: 'ephemeral' }
82
- }
83
- ],
84
- messages: anthropicMessages(request.messages, request.images, request.tools, this.configuration.id),
78
+ system: request.systemBlocks !== undefined
79
+ ? anthropicTextBlocks(request.system, request.systemBlocks)
80
+ : request.system
81
+ ? [
82
+ {
83
+ type: 'text',
84
+ text: request.system,
85
+ cache_control: { type: 'ephemeral' }
86
+ }
87
+ ]
88
+ : [],
89
+ messages: anthropicMessages(request.messages, request.images, request.tools, this.configuration.id, request.systemBlocks === undefined &&
90
+ request.messages.every((message) => message.textBlocks === undefined), request.cachePrefixMessages),
85
91
  tools,
86
92
  ...(request.toolChoice === undefined
87
93
  ? {}
88
94
  : { tool_choice: { type: request.toolChoice } }),
89
95
  stream: true,
90
- max_tokens: this.configuration.maxOutputTokens,
96
+ max_tokens: Math.min(request.outputLimit ?? this.configuration.maxOutputTokens, this.configuration.maxOutputTokens),
91
97
  output_config: {
92
98
  effort: request.reasoningEffort?.trim() ||
93
99
  resolveModelDefaultReasoningEffort(this.configuration)
@@ -452,12 +458,20 @@ function boundedError(value, maximum) {
452
458
  return undefined;
453
459
  return [...normalized].slice(0, maximum).join('');
454
460
  }
455
- function anthropicMessages(messages, images, tools = [], modelId) {
461
+ function anthropicMessages(messages, images, tools = [], modelId, automaticCache = true, cachePrefixMessages) {
462
+ if (cachePrefixMessages !== undefined &&
463
+ (!Number.isSafeInteger(cachePrefixMessages) ||
464
+ cachePrefixMessages < 0 ||
465
+ cachePrefixMessages > messages.length))
466
+ throw new MarAgentError('MAR_AGENT_MODEL_REQUEST_INVALID', 'The captured cache prefix is invalid.');
456
467
  const imageMessage = messages.findLastIndex((message) => message.role === 'user');
457
468
  const toolDefinitions = new Map(tools.map((tool) => [tool.name, tool]));
458
469
  const result = [];
459
470
  let thinkingBytes = 0;
460
471
  for (const [index, message] of messages.entries()) {
472
+ if (index === cachePrefixMessages)
473
+ for (const prior of result.slice(-2))
474
+ prior.content = withCacheBreakpoint(prior.content);
461
475
  const userImages = message.role === 'user'
462
476
  ? uniqueModelImages(message.images, index === imageMessage ? images : undefined)
463
477
  : [];
@@ -494,6 +508,7 @@ function anthropicMessages(messages, images, tools = [], modelId) {
494
508
  const block = {
495
509
  type: 'tool_result',
496
510
  tool_use_id: message.callId,
511
+ ...(message.isError === undefined ? {} : { is_error: message.isError }),
497
512
  content: observationModelParts(message)?.map((part) => part.type === 'text'
498
513
  ? { type: 'text', text: part.text }
499
514
  : {
@@ -504,10 +519,10 @@ function anthropicMessages(messages, images, tools = [], modelId) {
504
519
  data: part.image.dataBase64
505
520
  }
506
521
  }) ??
507
- (message.images?.length
522
+ (message.textBlocks !== undefined
508
523
  ? [
509
- { type: 'text', text: message.content },
510
- ...message.images.map((image) => ({
524
+ ...anthropicTextBlocks(message.content, message.textBlocks),
525
+ ...(message.images ?? []).map((image) => ({
511
526
  type: 'image',
512
527
  source: {
513
528
  type: 'base64',
@@ -516,7 +531,19 @@ function anthropicMessages(messages, images, tools = [], modelId) {
516
531
  }
517
532
  }))
518
533
  ]
519
- : message.content)
534
+ : message.images?.length
535
+ ? [
536
+ { type: 'text', text: message.content },
537
+ ...message.images.map((image) => ({
538
+ type: 'image',
539
+ source: {
540
+ type: 'base64',
541
+ media_type: image.mimeType,
542
+ data: image.dataBase64
543
+ }
544
+ }))
545
+ ]
546
+ : message.content)
520
547
  };
521
548
  const previous = result.at(-1);
522
549
  if (previous?.role === 'user' && Array.isArray(previous.content))
@@ -526,19 +553,21 @@ function anthropicMessages(messages, images, tools = [], modelId) {
526
553
  continue;
527
554
  }
528
555
  if (message.role === 'assistant') {
529
- if (!message.content)
556
+ if (!message.content && message.textBlocks === undefined)
530
557
  continue;
531
- const block = { type: 'text', text: message.content };
558
+ const blocks = message.textBlocks !== undefined
559
+ ? anthropicTextBlocks(message.content, message.textBlocks)
560
+ : [{ type: 'text', text: message.content }];
532
561
  const previous = result.at(-1);
533
562
  if (previous?.role === 'assistant' && Array.isArray(previous.content))
534
- previous.content.push(block);
563
+ previous.content.push(...blocks);
535
564
  else
536
- result.push({ role: 'assistant', content: [block] });
565
+ result.push({ role: 'assistant', content: blocks });
537
566
  continue;
538
567
  }
539
568
  result.push({
540
569
  role: message.role,
541
- content: userImages.length > 0
570
+ content: message.textBlocks !== undefined
542
571
  ? [
543
572
  ...userImages.map((image) => ({
544
573
  type: 'image',
@@ -548,15 +577,36 @@ function anthropicMessages(messages, images, tools = [], modelId) {
548
577
  data: image.dataBase64
549
578
  }
550
579
  })),
551
- { type: 'text', text: message.content }
580
+ ...anthropicTextBlocks(message.content, message.textBlocks)
552
581
  ]
553
- : message.content
582
+ : userImages.length > 0
583
+ ? [
584
+ ...userImages.map((image) => ({
585
+ type: 'image',
586
+ source: {
587
+ type: 'base64',
588
+ media_type: image.mimeType,
589
+ data: image.dataBase64
590
+ }
591
+ })),
592
+ { type: 'text', text: message.content }
593
+ ]
594
+ : message.content
554
595
  });
555
596
  }
556
- for (const message of result.slice(-2))
557
- message.content = withCacheBreakpoint(message.content);
597
+ if (cachePrefixMessages === messages.length ||
598
+ (cachePrefixMessages === undefined && automaticCache))
599
+ for (const message of result.slice(-2))
600
+ message.content = withCacheBreakpoint(message.content);
558
601
  return result;
559
602
  }
603
+ function anthropicTextBlocks(text, blocks) {
604
+ return resolveModelTextBlocks(text, blocks).map((block) => ({
605
+ type: 'text',
606
+ text: block.text,
607
+ ...(block.cache ? { cache_control: { type: 'ephemeral' } } : {})
608
+ }));
609
+ }
560
610
  function uniqueModelImages(...groups) {
561
611
  const seen = new Set();
562
612
  return groups.flatMap((group) => (group ?? []).flatMap((image) => {
@@ -1,10 +1,17 @@
1
1
  import type { ClientToolDefinition } from '../tools/contracts.js';
2
2
  import type { MarAgentModelConfiguration, MarAgentReasoningEffort, ModelCredentialAcquireReason } from './configuration.js';
3
3
  export type { ClientToolDefinition, ToolCaller } from '../tools/contracts.js';
4
+ export interface ModelTextBlock {
5
+ readonly text: string;
6
+ readonly cache?: true;
7
+ }
4
8
  export interface ModelMessage {
9
+ /** Model-facing tool-result status; independent of the actual execution outcome. */
10
+ isError?: boolean;
5
11
  calls?: readonly import('../tools/code-mode/observations.js').CodeToolCallRecord[];
6
12
  role: 'user' | 'assistant' | 'tool' | 'tool_call' | 'provider';
7
13
  content: string;
14
+ textBlocks?: readonly ModelTextBlock[];
8
15
  contextKind?: 'environment' | 'private';
9
16
  callId?: string;
10
17
  name?: string;
@@ -64,8 +71,13 @@ export interface ModelRequest {
64
71
  /** Shared local attempt accounting across Adapter retries and Runtime continuation. Never sent upstream. */
65
72
  retryState?: ModelRetryState;
66
73
  system: string;
74
+ systemBlocks?: readonly ModelTextBlock[];
75
+ /** Retain the automatic cache boundary of an actual captured prefix, excluding a fork tail. */
76
+ cachePrefixMessages?: number;
67
77
  messages: readonly ModelMessage[];
68
78
  tools: readonly ClientToolDefinition[];
79
+ /** Optional per-request cap; adapters never exceed their configured output ceiling. */
80
+ outputLimit?: number;
69
81
  allowTools?: boolean;
70
82
  /** Preserve the visible tool catalog while controlling whether the model may call it. */
71
83
  toolChoice?: 'auto' | 'none';
@@ -7,6 +7,7 @@ import WebSocket from 'ws';
7
7
  import { isRetryableUpstreamOverloadCode, number, parseSse, record, text, observeModelStreamEvent, modelStreamEventObservedAt } from './sse.js';
8
8
  import { connectSocks5Target, fetchModelResponse, isRetryableModelHttpStatus, isRetryableModelTransportError, modelRetryMaxAttempts, isRetryableModelRequestError, modelRetryDelay, waitForModelRetry } from './http.js';
9
9
  import { acquireModelCredential, appendModelCredentialHeaders } from './runtime-credential.js';
10
+ import { resolveModelTextBlocks } from './text-blocks.js';
10
11
  const WEBSOCKET_IDLE_TIMEOUT_MS = 300_000;
11
12
  const WEBSOCKET_SEND_FRAME_BYTES = 1024 * 1024;
12
13
  export const OPENAI_RESPONSES_EARLY_EOF_REASON = 'OPENAI_RESPONSES_EARLY_EOF';
@@ -861,6 +862,11 @@ function isLocalContinuationReset(error) {
861
862
  return error instanceof MarAgentError && error.code === 'MAR_AGENT_RESPONSES_CONTINUATION_RESET';
862
863
  }
863
864
  function responsesRequestBody(configuration, request, messages) {
865
+ if ((request.systemBlocks !== undefined &&
866
+ resolveModelTextBlocks(request.system, request.systemBlocks).some((block) => block.cache)) ||
867
+ messages.some((message) => message.textBlocks !== undefined &&
868
+ resolveModelTextBlocks(message.content, message.textBlocks).some((block) => block.cache)))
869
+ throw new MarAgentError('MAR_AGENT_MODEL_CAPABILITY_UNAVAILABLE', 'This model protocol does not support explicit text-block cache markers.');
864
870
  const tools = request.allowTools === false
865
871
  ? []
866
872
  : request.tools.map((tool) => tool.nativeType === 'tool_search'
@@ -887,14 +893,27 @@ function responsesRequestBody(configuration, request, messages) {
887
893
  tools.push({ type: 'web_search' });
888
894
  const toolChoice = request.toolChoice ?? 'auto';
889
895
  const input = openAiInput(messages, request.images, configuration.id, true, usesResponsesConfigurationUpdates(configuration));
896
+ if (request.systemBlocks !== undefined && (request.systemBlocks.length || request.system.length))
897
+ input.unshift({
898
+ role: 'developer',
899
+ content: resolveModelTextBlocks(request.system, request.systemBlocks).map((block) => ({
900
+ type: 'input_text',
901
+ text: block.text
902
+ }))
903
+ });
890
904
  return {
891
905
  model: configuration.modelId,
906
+ ...(request.outputLimit === undefined
907
+ ? {}
908
+ : {
909
+ max_output_tokens: Math.min(request.outputLimit, configuration.maxOutputTokens)
910
+ }),
892
911
  ...(configuration.serviceTier !== undefined
893
912
  ? { service_tier: configuration.serviceTier }
894
913
  : configuration.fastMode === true
895
914
  ? { service_tier: 'priority' }
896
915
  : {}),
897
- instructions: request.system,
916
+ instructions: request.systemBlocks === undefined ? request.system : '',
898
917
  tools,
899
918
  tool_choice: toolChoice,
900
919
  input,
@@ -939,6 +958,7 @@ function responsesContinuationPropertiesMatch(previous, current) {
939
958
  'instructions',
940
959
  'tools',
941
960
  'tool_choice',
961
+ 'max_output_tokens',
942
962
  'parallel_tool_calls',
943
963
  'reasoning',
944
964
  'store',
@@ -1687,8 +1707,8 @@ function strictFunctionSchema(value, optional = false) {
1687
1707
  return value;
1688
1708
  const schema = value;
1689
1709
  const supportedSchema = Object.fromEntries(Object.entries(schema).filter(([keyword]) => keyword !== 'propertyNames'));
1690
- if (schema.type === 'object' && schema.properties && typeof schema.properties === 'object') {
1691
- const properties = schema.properties;
1710
+ if (schema.type === 'object') {
1711
+ const properties = record(schema.properties);
1692
1712
  const required = new Set(Array.isArray(schema.required) ? schema.required : []);
1693
1713
  return {
1694
1714
  ...supportedSchema,
@@ -1861,40 +1881,81 @@ function openAiInputItem(message, images, customCallIds = new Set(), functionCal
1861
1881
  type: 'input_image',
1862
1882
  image_url: `data:${part.image.mimeType};base64,${part.image.dataBase64}`
1863
1883
  }) ??
1864
- (message.images?.length
1884
+ (message.textBlocks !== undefined
1865
1885
  ? [
1866
- { type: 'input_text', text: message.content },
1867
- ...message.images.map((image) => ({
1886
+ ...resolveModelTextBlocks(message.content, message.textBlocks).map((block) => ({
1887
+ type: 'input_text',
1888
+ text: block.text
1889
+ })),
1890
+ ...(message.images ?? []).map((image) => ({
1868
1891
  type: 'input_image',
1869
1892
  image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1870
1893
  }))
1871
1894
  ]
1872
- : message.content)
1895
+ : message.images?.length
1896
+ ? [
1897
+ { type: 'input_text', text: message.content },
1898
+ ...message.images.map((image) => ({
1899
+ type: 'input_image',
1900
+ image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1901
+ }))
1902
+ ]
1903
+ : message.content)
1873
1904
  };
1874
1905
  return {
1875
1906
  role: message.role,
1876
- content: userImages.length > 0
1907
+ content: message.textBlocks !== undefined
1877
1908
  ? [
1878
- { type: 'input_text', text: message.content },
1909
+ ...resolveModelTextBlocks(message.content, message.textBlocks).map((block) => ({
1910
+ type: message.role === 'assistant' ? 'output_text' : 'input_text',
1911
+ text: block.text
1912
+ })),
1879
1913
  ...userImages.map((image) => ({
1880
1914
  type: 'input_image',
1881
1915
  image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1882
1916
  }))
1883
1917
  ]
1884
- : message.content
1918
+ : userImages.length > 0
1919
+ ? [
1920
+ { type: 'input_text', text: message.content },
1921
+ ...userImages.map((image) => ({
1922
+ type: 'input_image',
1923
+ image_url: `data:${image.mimeType};base64,${image.dataBase64}`
1924
+ }))
1925
+ ]
1926
+ : message.content
1885
1927
  };
1886
1928
  }
1887
1929
  function responsesFunctionTool(tool) {
1930
+ const strict = !hasOpenFunctionObject(tool.inputSchema);
1888
1931
  return tool.inputMode === 'freeform'
1889
1932
  ? { type: 'custom', name: tool.name, description: tool.description }
1890
1933
  : {
1891
1934
  type: 'function',
1892
1935
  name: tool.name,
1893
1936
  description: tool.description,
1894
- parameters: strictFunctionSchema(tool.inputSchema),
1895
- strict: true
1937
+ parameters: strict ? strictFunctionSchema(tool.inputSchema) : tool.inputSchema,
1938
+ strict
1896
1939
  };
1897
1940
  }
1941
+ function hasOpenFunctionObject(value) {
1942
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
1943
+ return false;
1944
+ const schema = record(value);
1945
+ if ((schema.type === 'object' || (Array.isArray(schema.type) && schema.type.includes('object'))) &&
1946
+ (schema.additionalProperties === true ||
1947
+ (schema.additionalProperties !== null && typeof schema.additionalProperties === 'object') ||
1948
+ (schema.properties === undefined && schema.additionalProperties !== false)))
1949
+ return true;
1950
+ const children = [
1951
+ ...Object.values(record(schema.properties)),
1952
+ ...Object.values(record(schema.$defs)),
1953
+ ...Object.values(record(schema.definitions)),
1954
+ schema.items,
1955
+ ...[schema.anyOf, schema.oneOf, schema.allOf].flatMap((items) => Array.isArray(items) ? items : [])
1956
+ ];
1957
+ return children.some(hasOpenFunctionObject);
1958
+ }
1898
1959
  function uniqueModelImages(...groups) {
1899
1960
  const seen = new Set();
1900
1961
  return groups.flatMap((group) => (group ?? []).flatMap((image) => {
@@ -0,0 +1,7 @@
1
+ import type { ModelTextBlock } from './contracts.js';
2
+ /** Preserve explicit blocks only while their complete texts still match the actual prefix. */
3
+ export declare function resolveModelTextBlocks(text: string, blocks: readonly ModelTextBlock[]): readonly ModelTextBlock[];
4
+ export declare function modelTextInput(value: string | readonly ModelTextBlock[]): {
5
+ text: string;
6
+ blocks?: readonly ModelTextBlock[];
7
+ };
@@ -0,0 +1,40 @@
1
+ import { MarAgentError } from '../error.js';
2
+ /** Preserve explicit blocks only while their complete texts still match the actual prefix. */
3
+ export function resolveModelTextBlocks(text, blocks) {
4
+ if (typeof text !== 'string' || !Array.isArray(blocks))
5
+ throw invalid();
6
+ const copied = blocks.map((block) => {
7
+ if (!block ||
8
+ typeof block !== 'object' ||
9
+ typeof block.text !== 'string' ||
10
+ (block.cache !== undefined && block.cache !== true))
11
+ throw invalid();
12
+ return { text: block.text, ...(block.cache === true ? { cache: true } : {}) };
13
+ });
14
+ let offset = 0;
15
+ const result = [];
16
+ for (const block of copied) {
17
+ if (!text.startsWith(block.text, offset))
18
+ break;
19
+ result.push(block);
20
+ offset += block.text.length;
21
+ }
22
+ if (offset < text.length)
23
+ result.push({ text: text.slice(offset) });
24
+ return result;
25
+ }
26
+ export function modelTextInput(value) {
27
+ if (typeof value === 'string')
28
+ return { text: value };
29
+ if (!Array.isArray(value))
30
+ throw invalid();
31
+ for (const block of value) {
32
+ if (!block || typeof block !== 'object' || typeof block.text !== 'string')
33
+ throw invalid();
34
+ }
35
+ const text = value.map((block) => block.text).join('');
36
+ return { text, blocks: resolveModelTextBlocks(text, value) };
37
+ }
38
+ function invalid() {
39
+ return new MarAgentError('MAR_AGENT_MODEL_TEXT_BLOCK_INVALID', 'Model text blocks are invalid.');
40
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ExecutionMode } from '../runtime/contracts.js';
2
+ import { type CorePromptPart } from '../extensions/prompt-composition.js';
2
3
  export declare const MAR_AGENT_PROMPT_VERSION = "1.60";
3
- export declare function buildSystemPrompt(input: {
4
+ interface SystemPromptInput {
4
5
  mode: ExecutionMode;
5
6
  platform: string;
6
7
  architecture?: string;
@@ -14,4 +15,7 @@ export declare function buildSystemPrompt(input: {
14
15
  directTools?: readonly string[];
15
16
  highDensityCompaction?: boolean;
16
17
  extension?: string | undefined;
17
- }): string;
18
+ }
19
+ export declare function buildSystemPrompt(input: SystemPromptInput): string;
20
+ export declare function buildSystemPromptSections(input: SystemPromptInput): readonly CorePromptPart[];
21
+ export {};
@@ -1,3 +1,4 @@
1
+ import { renderPromptSections } from '../extensions/prompt-composition.js';
1
2
  import { compactionPrompt, highDensityCompactionPrompt } from './compact.js';
2
3
  import { environmentPrompt, identityPrompt, instructionPriorityPrompt, workspaceDisciplinePrompt } from './core.js';
3
4
  import { modePrompt } from './modes.js';
@@ -6,6 +7,9 @@ import { subagentPrompt } from './subagent.js';
6
7
  import { interactionPrompt, longRunningPrompt, safetyPrompt, toolUsagePrompt, workflowPrompt } from './workflow.js';
7
8
  export const MAR_AGENT_PROMPT_VERSION = '1.60';
8
9
  export function buildSystemPrompt(input) {
10
+ return renderPromptSections(buildSystemPromptSections(input));
11
+ }
12
+ export function buildSystemPromptSections(input) {
9
13
  const toolNames = new Set(input.tools);
10
14
  const hasLongRunningCapability = [
11
15
  'exec',
@@ -15,28 +19,38 @@ export function buildSystemPrompt(input) {
15
19
  'agent_wait',
16
20
  'agent_cancel'
17
21
  ].some((name) => toolNames.has(name));
18
- return [
19
- identityPrompt(),
20
- instructionPriorityPrompt,
21
- workspaceDisciplinePrompt,
22
- workflowPrompt,
23
- interactionPrompt,
24
- safetyPrompt,
25
- outputStylePrompt,
26
- environmentPrompt(input),
27
- input.tools.length > 0 ? toolUsagePrompt(input.tools, input.directTools) : '',
28
- hasLongRunningCapability ? longRunningPrompt(input.tools) : '',
29
- modePrompt(input.mode, {
30
- questionAvailable: toolNames.has('question')
31
- }),
32
- input.mode === 'compact'
33
- ? input.highDensityCompaction
34
- ? highDensityCompactionPrompt()
35
- : compactionPrompt()
36
- : '',
37
- toolNames.has('agent_start') ? subagentPrompt : '',
38
- input.extension ? `# Caller, project, and selected Skill instructions\n${input.extension}` : ''
39
- ]
40
- .filter(Boolean)
41
- .join('\n\n');
22
+ const values = [
23
+ ['identity', identityPrompt()],
24
+ ['instruction-priority', instructionPriorityPrompt],
25
+ ['workspace-discipline', workspaceDisciplinePrompt],
26
+ ['workflow', workflowPrompt],
27
+ ['interaction', interactionPrompt],
28
+ ['safety', safetyPrompt],
29
+ ['output-style', outputStylePrompt],
30
+ ['environment-rules', environmentPrompt(input)],
31
+ ['tools', input.tools.length > 0 ? toolUsagePrompt(input.tools, input.directTools) : ''],
32
+ ['long-running', hasLongRunningCapability ? longRunningPrompt(input.tools) : ''],
33
+ [
34
+ 'mode',
35
+ modePrompt(input.mode, {
36
+ questionAvailable: toolNames.has('question')
37
+ })
38
+ ],
39
+ [
40
+ 'compaction',
41
+ input.mode === 'compact'
42
+ ? input.highDensityCompaction
43
+ ? highDensityCompactionPrompt()
44
+ : compactionPrompt()
45
+ : ''
46
+ ],
47
+ ['subagent', toolNames.has('agent_start') ? subagentPrompt : ''],
48
+ [
49
+ 'caller-instructions',
50
+ input.extension
51
+ ? `# Caller, project, and selected Skill instructions\n${input.extension}`
52
+ : ''
53
+ ]
54
+ ];
55
+ return Object.freeze(values.map(([id, text]) => Object.freeze({ id: `core:${id}`, text: text || null })));
42
56
  }
@@ -1,5 +1,6 @@
1
1
  import { openAiInput } from '../../model/openai-responses.js';
2
2
  import { countModelTokens } from '../token-budget.js';
3
+ import { observationModelText } from '../../tools/code-mode/observations.js';
3
4
  export { MODEL_TOKEN_ESTIMATOR as CONTEXT_GC_TOKEN_ESTIMATOR } from '../token-budget.js';
4
5
  /** Uses Responses lowering, including native alias and cross-model filtering. */
5
6
  export function contextGcTokenEntries(input, messages) {
@@ -17,7 +18,11 @@ function serializedEntries(input, messages) {
17
18
  : messages
18
19
  .filter((message) => message.role !== 'provider' ||
19
20
  (message.provider === 'ANTHROPIC_MESSAGES' && message.providerModelId === input.modelId))
20
- .map(({ observations: _observations, observationId: _observationId, ...message }) => message);
21
+ .map((original) => {
22
+ const { observations: _observations, observationId: _observationId, ...message } = original;
23
+ const content = original.role === 'tool' ? observationModelText(original) : undefined;
24
+ return content === undefined ? message : { ...message, content };
25
+ });
21
26
  return values.map((value, index) => {
22
27
  const item = value;
23
28
  const callId = item.call_id ?? item.callId;
@@ -18,7 +18,27 @@ export function normalizeMessagesForModel(messages, input) {
18
18
  content: `[tool result unavailable for call ${callId}: the execution was interrupted before the result was recorded]`
19
19
  });
20
20
  }
21
- return result;
21
+ // A record committed during tool execution may precede its results in physical history.
22
+ // Delay only user rows in the request projection; never reorder persisted events.
23
+ const projected = [];
24
+ const deferred = [];
25
+ const pending = new Set();
26
+ for (const message of result) {
27
+ if (message.role === 'user' && pending.size) {
28
+ deferred.push(message);
29
+ continue;
30
+ }
31
+ projected.push(message);
32
+ const callId = pendingToolCallId(message);
33
+ if (callId)
34
+ pending.add(callId);
35
+ if (message.role === 'tool' && message.callId)
36
+ pending.delete(message.callId);
37
+ if (!pending.size && deferred.length)
38
+ projected.push(...deferred.splice(0));
39
+ }
40
+ projected.push(...deferred);
41
+ return projected;
22
42
  }
23
43
  function pendingToolCallId(message) {
24
44
  if (message.role === 'tool_call')
@@ -74,7 +74,9 @@ export function applyContextGcReplacementsToMessages(messages, messageSources, r
74
74
  next = {
75
75
  ...next,
76
76
  observations,
77
- content: renderObservationResult(observations, next.content)
77
+ content: next.textBlocks === undefined
78
+ ? renderObservationResult(observations, next.content)
79
+ : next.content
78
80
  };
79
81
  }
80
82
  if (replacement && target && targetMatchesReplacement(target, replacement.target)) {
@@ -94,6 +96,11 @@ export function applyContextGcReplacementsToMessages(messages, messageSources, r
94
96
  next.role === 'tool')
95
97
  next = { ...next, content: replacement.replacementContent };
96
98
  }
99
+ if (next.textBlocks !== undefined && next.content !== message.content)
100
+ next = {
101
+ ...next,
102
+ textBlocks: [{ text: next.content }]
103
+ };
97
104
  projected.push({ message: next, source });
98
105
  }
99
106
  return {
@@ -142,6 +149,19 @@ export function restoreModelContext(payload, providerModelId) {
142
149
  const byteLimit = inputLimit === undefined ? MAX_MODEL_CONTEXT_BYTES : inputLimit * 6 + 4096;
143
150
  if (Buffer.byteLength(JSON.stringify(payload)) > byteLimit)
144
151
  return undefined;
152
+ if (payload.role === 'user' && typeof payload.content === 'string') {
153
+ const blocks = payload.textBlocks;
154
+ const textBlocks = Array.isArray(blocks) &&
155
+ Array.from(blocks).every((block) => block && typeof block.text === 'string') &&
156
+ blocks.map((block) => block.text).join('') === payload.content
157
+ ? blocks.map((block) => ({ text: block.text }))
158
+ : undefined;
159
+ return {
160
+ role: 'user',
161
+ content: payload.content,
162
+ ...(textBlocks === undefined ? {} : { textBlocks })
163
+ };
164
+ }
145
165
  if (payload.role === 'tool' &&
146
166
  typeof payload.content === 'string' &&
147
167
  typeof payload.callId === 'string')
@@ -149,6 +169,20 @@ export function restoreModelContext(payload, providerModelId) {
149
169
  role: 'tool',
150
170
  content: payload.content,
151
171
  callId: payload.callId,
172
+ ...(typeof payload.name === 'string' ? { name: payload.name } : {}),
173
+ ...(typeof payload.isError === 'boolean' ? { isError: payload.isError } : {}),
174
+ ...(Array.isArray(payload.textBlocks) &&
175
+ payload.textBlocks.every((block) => block &&
176
+ typeof block.text === 'string' &&
177
+ (block.cache === undefined || block.cache === true)) &&
178
+ payload.textBlocks.map((block) => block.text).join('') === payload.content
179
+ ? {
180
+ textBlocks: payload.textBlocks.map((block) => ({
181
+ text: block.text,
182
+ ...(block.cache === true ? { cache: true } : {})
183
+ }))
184
+ }
185
+ : {}),
152
186
  ...(parseCodeCalls(payload.calls) ? { calls: parseCodeCalls(payload.calls) } : {}),
153
187
  ...(parseObservations(payload.observations)
154
188
  ? { observations: parseObservations(payload.observations) }
@@ -1,10 +1,13 @@
1
1
  import { AGENT_EXECUTION_POLICY } from '../execution-policy.js';
2
2
  import { countModelTokens } from '../token-budget.js';
3
+ import { observationModelText } from '../../tools/code-mode/observations.js';
3
4
  export function estimatedRequestTokens(system, messages, tools, images) {
4
5
  const transientImageCount = messages.reduce((total, message) => total + (message.images?.length ?? 0), images?.length ?? 0);
5
6
  const textMessages = messages.map((message) => {
6
- const textMessage = { ...message };
7
- delete textMessage.images;
7
+ const { images: _images, observations: _observations, observationId: _observationId, ...textMessage } = message;
8
+ const content = message.role === 'tool' ? observationModelText(message) : undefined;
9
+ if (content !== undefined)
10
+ textMessage.content = content;
8
11
  return textMessage;
9
12
  });
10
13
  return (countModelTokens(`${system}${stableJson(textMessages)}${stableJson(tools)}`) +