@myagentroam/agent 0.9.79 → 0.9.81

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.
@@ -72,6 +72,9 @@ export class AnthropicMessagesAdapter {
72
72
  ],
73
73
  messages: anthropicMessages(request.messages, request.images, request.tools, this.configuration.id),
74
74
  tools,
75
+ ...(request.toolChoice === undefined
76
+ ? {}
77
+ : { tool_choice: { type: request.toolChoice } }),
75
78
  stream: true,
76
79
  max_tokens: this.configuration.maxOutputTokens,
77
80
  output_config: {
@@ -60,6 +60,8 @@ export interface ModelRequest {
60
60
  messages: readonly ModelMessage[];
61
61
  tools: readonly ClientToolDefinition[];
62
62
  allowTools?: boolean;
63
+ /** Preserve the visible tool catalog while controlling whether the model may call it. */
64
+ toolChoice?: 'auto' | 'none';
63
65
  reasoningEffort?: MarAgentReasoningEffort;
64
66
  promptCacheKey?: string;
65
67
  images?: readonly {
@@ -551,6 +551,7 @@ class OpenAiResponsesTurnSession {
551
551
  this.adapter.configuration.responsesPreviousResponseId &&
552
552
  this.state.lastRequest === undefined &&
553
553
  request.allowTools !== false &&
554
+ request.toolChoice !== 'none' &&
554
555
  (request.tools.length > 0 || this.adapter.configuration.hostedWebSearch)) {
555
556
  const prefix = prepared.fullBody.input.slice(0, request.system ? 2 : 1);
556
557
  const warmBody = { ...prepared.fullBody, input: prefix, generate: false };
@@ -804,6 +805,7 @@ function responsesRequestBody(configuration, request, messages, prefixIdentity =
804
805
  });
805
806
  if (request.allowTools !== false && configuration.hostedWebSearch)
806
807
  tools.push({ type: 'web_search' });
808
+ const toolChoice = request.toolChoice ?? 'auto';
807
809
  const lite = configuration.responsesEncoding === 'LITE';
808
810
  const input = openAiInput(messages, request.images, configuration.id, true);
809
811
  if (lite) {
@@ -829,10 +831,10 @@ function responsesRequestBody(configuration, request, messages, prefixIdentity =
829
831
  model: configuration.modelId,
830
832
  ...(lite
831
833
  ? {
832
- tool_choice: 'auto',
834
+ tool_choice: toolChoice,
833
835
  client_metadata: { [RESPONSES_LITE_METADATA]: 'true' }
834
836
  }
835
- : { instructions: request.system, tools }),
837
+ : { instructions: request.system, tools, tool_choice: toolChoice }),
836
838
  input,
837
839
  stream: true,
838
840
  parallel_tool_calls: !lite,
@@ -22,6 +22,10 @@ export declare class CompactionOperation {
22
22
  private report;
23
23
  }
24
24
  export declare const DEFAULT_COMPACTION_FOCUS = "Create a durable continuation state for the next model invocation.";
25
+ export declare function buildNativeCompactionMessages(messages: readonly ModelMessage[], input?: {
26
+ focus?: string;
27
+ highDensityCompaction?: boolean;
28
+ }): ModelMessage[];
25
29
  export declare function buildCompactionMessages(evidence: string, highDensityCompaction?: boolean): ModelMessage[];
26
30
  export declare function buildCompactionInput(messages: readonly ModelMessage[], input: {
27
31
  focus?: string;
@@ -91,6 +91,23 @@ export class CompactionOperation {
91
91
  }
92
92
  }
93
93
  export const DEFAULT_COMPACTION_FOCUS = 'Create a durable continuation state for the next model invocation.';
94
+ export function buildNativeCompactionMessages(messages, input = {}) {
95
+ const focus = input.focus?.trim();
96
+ const contract = input.highDensityCompaction ? highDensityCompactionPrompt() : compactionPrompt();
97
+ return [
98
+ ...messages.map((message) => {
99
+ if (!message.images?.length)
100
+ return message;
101
+ const textOnly = { ...message };
102
+ delete textOnly.images;
103
+ return textOnly;
104
+ }),
105
+ {
106
+ role: 'user',
107
+ content: focus ? `Compaction focus: ${focus}\n\n${contract}` : contract
108
+ }
109
+ ];
110
+ }
94
111
  export function buildCompactionMessages(evidence, highDensityCompaction = false) {
95
112
  return [
96
113
  { role: 'user', content: evidence },
package/dist/sdk/agent.js CHANGED
@@ -14,7 +14,7 @@ import { ContextGcRuntime } from '../runtime/context-gc-runtime.js';
14
14
  import { buildContextProjection } from '../runtime/context-projection.js';
15
15
  import { countModelTokens, countTextLines, truncateModelText } from '../runtime/token-budget.js';
16
16
  import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
17
- import { DEFAULT_COMPACTION_FOCUS, CompactionOperation, buildCompactionInput, buildCompactionMessages, buildCompactedHistory, compactedHistoryMessages, continuationMessage } from '../runtime/compact.js';
17
+ import { DEFAULT_COMPACTION_FOCUS, CompactionOperation, buildCompactionInput, buildCompactionMessages, buildNativeCompactionMessages, buildCompactedHistory, compactedHistoryMessages, continuationMessage } from '../runtime/compact.js';
18
18
  import { buildSystemPrompt, MAR_AGENT_PROMPT_VERSION } from '../prompts/index.js';
19
19
  import { buildEnvironmentContext } from '../prompts/core.js';
20
20
  import { modelOutputContinuationMessage } from '../prompts/output.js';
@@ -427,30 +427,27 @@ export async function createMarAgent(options) {
427
427
  : tool.name !== 'web_fetch' ||
428
428
  description.capabilities.includes('web.fetch');
429
429
  });
430
- const instructionContext = mode === 'compact'
431
- ? undefined
432
- : await loadAgentInstructions({
433
- home: description.homeDirectory,
434
- workspace: instructionWorkspace,
435
- availableTools: new Set(availableTools.map((tool) => tool.name)),
436
- ...(options.workspaceMapping === undefined
437
- ? {}
438
- : { workspaceDisplayPath: logicalWorkspace })
439
- });
440
- let agentInstructionMessage = mode === 'compact'
441
- ? undefined
442
- : renderAgentInstructions(instructionContext?.entries ?? [], previousMayContainAgentInstructions);
443
- let agentInstructionInsertIndex = mode === 'compact' ? undefined : Math.max(0, messages.length - 1);
444
- if (agentInstructionInsertIndex &&
430
+ const instructionContext = await loadAgentInstructions({
431
+ home: description.homeDirectory,
432
+ workspace: instructionWorkspace,
433
+ availableTools: new Set(availableTools.map((tool) => tool.name)),
434
+ ...(options.workspaceMapping === undefined
435
+ ? {}
436
+ : { workspaceDisplayPath: logicalWorkspace })
437
+ });
438
+ let agentInstructionMessage = renderAgentInstructions(instructionContext?.entries ?? [], previousMayContainAgentInstructions);
439
+ let agentInstructionInsertIndex = mode === 'compact'
440
+ ? contextualInstructionInsertIndex(messages)
441
+ : Math.max(0, messages.length - 1);
442
+ if (mode !== 'compact' &&
443
+ agentInstructionInsertIndex &&
445
444
  messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
446
445
  agentInstructionInsertIndex--;
447
446
  const tools = withCodeModeResultTypes(availableTools.filter((tool) => tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
448
- const executionTools = mode === 'compact' ? [] : tools;
449
- const hostResources = mode === 'compact'
450
- ? { processes: [] }
451
- : ((await options.host.listSessionResources?.(sessionId)) ?? {
452
- processes: []
453
- });
447
+ const executionTools = tools;
448
+ const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
449
+ processes: []
450
+ };
454
451
  const retainedSessionResources = {
455
452
  ...(hostResources.idleTtlMs === undefined
456
453
  ? {}
@@ -458,8 +455,8 @@ export async function createMarAgent(options) {
458
455
  processes: hostResources.processes,
459
456
  subagents: subagents?.listSessionResources() ?? []
460
457
  };
461
- const buildExecutionSystemPrompt = (availableTools = executionTools) => buildSystemPrompt({
462
- mode,
458
+ const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => buildSystemPrompt({
459
+ mode: promptMode,
463
460
  platform: description.platform,
464
461
  ...(description.architecture ? { architecture: description.architecture } : {}),
465
462
  ...(description.shell ? { shell: description.shell } : {}),
@@ -503,16 +500,39 @@ export async function createMarAgent(options) {
503
500
  .filter(Boolean)
504
501
  .join('\n\n')
505
502
  });
506
- const initialSystemPrompt = buildExecutionSystemPrompt();
503
+ const initialSystemPrompt = mode === 'compact'
504
+ ? buildExecutionSystemPrompt(executionTools, 'run')
505
+ : buildExecutionSystemPrompt();
506
+ let useManualCompactionFallback;
507
507
  if (mode === 'compact') {
508
- const compactOverheadTokens = estimatedRequestTokens(initialSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), []);
508
+ const compactSystemPrompt = initialSystemPrompt;
509
+ const compactHistory = [...messages];
510
+ const projectedHistory = normalizeMessagesForModel(insertContextualInstructions(compactHistory, agentInstructionInsertIndex, agentInstructionMessage), { supportsImages: false });
511
+ const nativeMessages = buildNativeCompactionMessages(projectedHistory, {
512
+ focus: input.prompt,
513
+ highDensityCompaction: selectedModel.highDensityCompaction
514
+ });
515
+ const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
509
516
  const compactInputTokens = selectedModel.contextWindowTokens - compactOverheadTokens;
510
517
  if (compactInputTokens <= 0)
511
518
  throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
512
- messages.splice(0, messages.length, ...buildCompactionMessages(buildCompactionInput(messages, {
519
+ const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
513
520
  focus: input.prompt,
514
521
  maxTokens: compactInputTokens
515
- }), selectedModel.highDensityCompaction));
522
+ }), selectedModel.highDensityCompaction);
523
+ const useNative = estimatedRequestTokens(compactSystemPrompt, nativeMessages, executionTools) <=
524
+ selectedModel.contextWindowTokens;
525
+ messages.splice(0, messages.length, ...(useNative ? nativeMessages : fallbackMessages()));
526
+ if (useNative) {
527
+ let available = true;
528
+ useManualCompactionFallback = () => {
529
+ if (!available)
530
+ return false;
531
+ available = false;
532
+ messages.splice(0, messages.length, ...fallbackMessages());
533
+ return true;
534
+ };
535
+ }
516
536
  }
517
537
  await store.append(sessionId, {
518
538
  type: 'execution.header',
@@ -535,21 +555,25 @@ export async function createMarAgent(options) {
535
555
  return false;
536
556
  const retainCurrent = current.role === 'user';
537
557
  let compactSummary = '';
538
- const compactSystemPrompt = buildSystemPrompt({
539
- mode: 'compact',
540
- platform: description.platform,
541
- workspace: logicalWorkspace,
542
- tools: [],
543
- highDensityCompaction: selectedModel.highDensityCompaction,
544
- extension: options.systemInstruction
558
+ const compactSystemPrompt = buildExecutionSystemPrompt(executionTools, 'run');
559
+ const compactHistory = retainCurrent ? messages.slice(0, -1) : messages;
560
+ const compactInstructionInsertIndex = retainCurrent
561
+ ? contextualInstructionInsertIndex(compactHistory)
562
+ : agentInstructionInsertIndex;
563
+ const projectedHistory = normalizeMessagesForModel(insertContextualInstructions(compactHistory, compactInstructionInsertIndex, agentInstructionMessage), { supportsImages: false });
564
+ const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
565
+ highDensityCompaction: selectedModel.highDensityCompaction
545
566
  });
546
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), []);
567
+ const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
547
568
  const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
548
569
  if (compactInputTokens === 0)
549
570
  throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
550
- const compactSource = buildCompactionInput(retainCurrent ? messages.slice(0, -1) : messages, {
571
+ let usingNativeMessages = estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, executionTools) <=
572
+ selectedModel.contextWindowTokens;
573
+ const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
551
574
  maxTokens: Math.max(1, compactInputTokens)
552
- });
575
+ }), selectedModel.highDensityCompaction);
576
+ let compactMessages = usingNativeMessages ? nativeCompactMessages : fallbackMessages();
553
577
  const compactionId = randomUUID();
554
578
  await emit({ type: 'context.compacting', compactionId });
555
579
  let latestUsage;
@@ -563,9 +587,9 @@ export async function createMarAgent(options) {
563
587
  for await (const event of operation.start(selected, {
564
588
  retryState: compactionRetryState,
565
589
  system: compactSystemPrompt,
566
- messages: buildCompactionMessages(compactSource, selectedModel.highDensityCompaction),
567
- tools: [],
568
- allowTools: false,
590
+ messages: compactMessages,
591
+ tools: executionTools,
592
+ toolChoice: 'none',
569
593
  promptCacheKey: sessionId,
570
594
  onAttemptDiagnostic: recordModelAttempt('COMPACTION')
571
595
  })) {
@@ -576,6 +600,8 @@ export async function createMarAgent(options) {
576
600
  latestUsage = event.usage;
577
601
  await emit({ type: 'usage.updated', ...publicModelUsage(event.usage) });
578
602
  }
603
+ else if (event.type === 'client_tool.call')
604
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
579
605
  }
580
606
  break;
581
607
  }
@@ -584,6 +610,13 @@ export async function createMarAgent(options) {
584
610
  sessionRuntime.responsesChain = undefined;
585
611
  continue;
586
612
  }
613
+ if (isProviderContextOverflow(error) && usingNativeMessages) {
614
+ usingNativeMessages = false;
615
+ compactMessages = fallbackMessages();
616
+ compactionRetryState.attempts = 0;
617
+ sessionRuntime.responsesChain = undefined;
618
+ continue;
619
+ }
587
620
  if (isProviderContextOverflow(error))
588
621
  throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The compaction request exceeds the model context window.', { cause: error });
589
622
  throw error;
@@ -644,8 +677,10 @@ export async function createMarAgent(options) {
644
677
  let incompleteToolCall = false;
645
678
  await ensureContextBudget();
646
679
  const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
647
- const roundTools = toolsAllowed ? executionTools : [];
648
- const systemPrompt = buildExecutionSystemPrompt(roundTools);
680
+ const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
681
+ const systemPrompt = mode === 'compact'
682
+ ? buildExecutionSystemPrompt(roundTools, 'run')
683
+ : buildExecutionSystemPrompt(roundTools);
649
684
  const requestSignature = createHash('sha256')
650
685
  .update(stableJson({
651
686
  modelId: selectedModel.id,
@@ -667,9 +702,11 @@ export async function createMarAgent(options) {
667
702
  deltaMessages: requestContextMessages.slice(sessionRuntime.responsesChain.messageCount)
668
703
  }
669
704
  : undefined;
670
- const modelMessages = normalizeMessagesForModel(insertContextualInstructions(requestContextMessages, agentInstructionInsertIndex, agentInstructionMessage), {
671
- supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
672
- });
705
+ const modelMessages = mode === 'compact'
706
+ ? normalizeMessagesForModel(requestContextMessages, { supportsImages: false })
707
+ : normalizeMessagesForModel(insertContextualInstructions(requestContextMessages, agentInstructionInsertIndex, agentInstructionMessage), {
708
+ supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
709
+ });
673
710
  let sawModelSemanticEvent = false;
674
711
  const roundMessageCount = messages.length;
675
712
  const persistToolCalls = async (calls) => {
@@ -705,7 +742,11 @@ export async function createMarAgent(options) {
705
742
  system: systemPrompt,
706
743
  messages: modelMessages,
707
744
  tools: roundTools,
708
- ...(toolsAllowed ? {} : { allowTools: false }),
745
+ ...(mode === 'compact'
746
+ ? { toolChoice: 'none' }
747
+ : toolsAllowed
748
+ ? {}
749
+ : { allowTools: false }),
709
750
  promptCacheKey: sessionId,
710
751
  ...(continuation ? { continuation } : {}),
711
752
  reasoningEffort,
@@ -821,6 +862,8 @@ export async function createMarAgent(options) {
821
862
  });
822
863
  }
823
864
  else if (event.type === 'client_tool.call') {
865
+ if (mode === 'compact')
866
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
824
867
  sawTool = true;
825
868
  pendingTools.push(event);
826
869
  }
@@ -851,6 +894,16 @@ export async function createMarAgent(options) {
851
894
  if (pendingTools.length === 0)
852
895
  continue;
853
896
  }
897
+ else if (mode === 'compact' &&
898
+ !sawModelSemanticEvent &&
899
+ isProviderContextOverflow(error) &&
900
+ useManualCompactionFallback?.()) {
901
+ sessionRuntime.responsesChain = undefined;
902
+ modelRetryState.attempts = 0;
903
+ responseId = undefined;
904
+ finalAnswer = '';
905
+ continue;
906
+ }
854
907
  else if (mode !== 'compact' &&
855
908
  !sawModelSemanticEvent &&
856
909
  isProviderContextOverflow(error)) {
@@ -1652,6 +1705,14 @@ function insertContextualInstructions(messages, index, content, environmentConte
1652
1705
  });
1653
1706
  return result;
1654
1707
  }
1708
+ function contextualInstructionInsertIndex(messages) {
1709
+ let index = messages.findLastIndex((message) => message.role === 'user' && message.contextKind !== 'environment');
1710
+ if (index < 0)
1711
+ return 0;
1712
+ if (index > 0 && messages[index - 1]?.contextKind === 'environment')
1713
+ index--;
1714
+ return index;
1715
+ }
1655
1716
  function estimatedRequestTokens(system, messages, tools, images) {
1656
1717
  const transientImageCount = messages.reduce((total, message) => total + (message.images?.length ?? 0), images?.length ?? 0);
1657
1718
  const textMessages = messages.map((message) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.9.79",
3
+ "version": "0.9.81",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",