@myagentroam/agent 0.9.79 → 0.9.80

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,16 +427,14 @@ 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
- });
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
+ });
440
438
  let agentInstructionMessage = mode === 'compact'
441
439
  ? undefined
442
440
  : renderAgentInstructions(instructionContext?.entries ?? [], previousMayContainAgentInstructions);
@@ -445,12 +443,10 @@ export async function createMarAgent(options) {
445
443
  messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
446
444
  agentInstructionInsertIndex--;
447
445
  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
- });
446
+ const executionTools = tools;
447
+ const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
448
+ processes: []
449
+ };
454
450
  const retainedSessionResources = {
455
451
  ...(hostResources.idleTtlMs === undefined
456
452
  ? {}
@@ -458,8 +454,8 @@ export async function createMarAgent(options) {
458
454
  processes: hostResources.processes,
459
455
  subagents: subagents?.listSessionResources() ?? []
460
456
  };
461
- const buildExecutionSystemPrompt = (availableTools = executionTools) => buildSystemPrompt({
462
- mode,
457
+ const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => buildSystemPrompt({
458
+ mode: promptMode,
463
459
  platform: description.platform,
464
460
  ...(description.architecture ? { architecture: description.architecture } : {}),
465
461
  ...(description.shell ? { shell: description.shell } : {}),
@@ -503,16 +499,38 @@ export async function createMarAgent(options) {
503
499
  .filter(Boolean)
504
500
  .join('\n\n')
505
501
  });
506
- const initialSystemPrompt = buildExecutionSystemPrompt();
502
+ const initialSystemPrompt = mode === 'compact'
503
+ ? buildExecutionSystemPrompt(executionTools, 'run')
504
+ : buildExecutionSystemPrompt();
505
+ let useManualCompactionFallback;
507
506
  if (mode === 'compact') {
508
- const compactOverheadTokens = estimatedRequestTokens(initialSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), []);
507
+ const compactSystemPrompt = initialSystemPrompt;
508
+ const compactHistory = [...messages];
509
+ const nativeMessages = buildNativeCompactionMessages(messages, {
510
+ focus: input.prompt,
511
+ highDensityCompaction: selectedModel.highDensityCompaction
512
+ });
513
+ const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
509
514
  const compactInputTokens = selectedModel.contextWindowTokens - compactOverheadTokens;
510
515
  if (compactInputTokens <= 0)
511
516
  throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
512
- messages.splice(0, messages.length, ...buildCompactionMessages(buildCompactionInput(messages, {
517
+ const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
513
518
  focus: input.prompt,
514
519
  maxTokens: compactInputTokens
515
- }), selectedModel.highDensityCompaction));
520
+ }), selectedModel.highDensityCompaction);
521
+ const useNative = estimatedRequestTokens(compactSystemPrompt, nativeMessages, executionTools) <=
522
+ selectedModel.contextWindowTokens;
523
+ messages.splice(0, messages.length, ...(useNative ? nativeMessages : fallbackMessages()));
524
+ if (useNative) {
525
+ let available = true;
526
+ useManualCompactionFallback = () => {
527
+ if (!available)
528
+ return false;
529
+ available = false;
530
+ messages.splice(0, messages.length, ...fallbackMessages());
531
+ return true;
532
+ };
533
+ }
516
534
  }
517
535
  await store.append(sessionId, {
518
536
  type: 'execution.header',
@@ -535,21 +553,21 @@ export async function createMarAgent(options) {
535
553
  return false;
536
554
  const retainCurrent = current.role === 'user';
537
555
  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
556
+ const compactSystemPrompt = buildExecutionSystemPrompt(executionTools, 'run');
557
+ const compactHistory = retainCurrent ? messages.slice(0, -1) : messages;
558
+ const nativeCompactMessages = buildNativeCompactionMessages(compactHistory, {
559
+ highDensityCompaction: selectedModel.highDensityCompaction
545
560
  });
546
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), []);
561
+ const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
547
562
  const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
548
563
  if (compactInputTokens === 0)
549
564
  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, {
565
+ let usingNativeMessages = estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, executionTools) <=
566
+ selectedModel.contextWindowTokens;
567
+ const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
551
568
  maxTokens: Math.max(1, compactInputTokens)
552
- });
569
+ }), selectedModel.highDensityCompaction);
570
+ let compactMessages = usingNativeMessages ? nativeCompactMessages : fallbackMessages();
553
571
  const compactionId = randomUUID();
554
572
  await emit({ type: 'context.compacting', compactionId });
555
573
  let latestUsage;
@@ -563,9 +581,9 @@ export async function createMarAgent(options) {
563
581
  for await (const event of operation.start(selected, {
564
582
  retryState: compactionRetryState,
565
583
  system: compactSystemPrompt,
566
- messages: buildCompactionMessages(compactSource, selectedModel.highDensityCompaction),
567
- tools: [],
568
- allowTools: false,
584
+ messages: compactMessages,
585
+ tools: executionTools,
586
+ toolChoice: 'none',
569
587
  promptCacheKey: sessionId,
570
588
  onAttemptDiagnostic: recordModelAttempt('COMPACTION')
571
589
  })) {
@@ -576,6 +594,8 @@ export async function createMarAgent(options) {
576
594
  latestUsage = event.usage;
577
595
  await emit({ type: 'usage.updated', ...publicModelUsage(event.usage) });
578
596
  }
597
+ else if (event.type === 'client_tool.call')
598
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
579
599
  }
580
600
  break;
581
601
  }
@@ -584,6 +604,13 @@ export async function createMarAgent(options) {
584
604
  sessionRuntime.responsesChain = undefined;
585
605
  continue;
586
606
  }
607
+ if (isProviderContextOverflow(error) && usingNativeMessages) {
608
+ usingNativeMessages = false;
609
+ compactMessages = fallbackMessages();
610
+ compactionRetryState.attempts = 0;
611
+ sessionRuntime.responsesChain = undefined;
612
+ continue;
613
+ }
587
614
  if (isProviderContextOverflow(error))
588
615
  throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The compaction request exceeds the model context window.', { cause: error });
589
616
  throw error;
@@ -644,8 +671,10 @@ export async function createMarAgent(options) {
644
671
  let incompleteToolCall = false;
645
672
  await ensureContextBudget();
646
673
  const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
647
- const roundTools = toolsAllowed ? executionTools : [];
648
- const systemPrompt = buildExecutionSystemPrompt(roundTools);
674
+ const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
675
+ const systemPrompt = mode === 'compact'
676
+ ? buildExecutionSystemPrompt(roundTools, 'run')
677
+ : buildExecutionSystemPrompt(roundTools);
649
678
  const requestSignature = createHash('sha256')
650
679
  .update(stableJson({
651
680
  modelId: selectedModel.id,
@@ -705,7 +734,11 @@ export async function createMarAgent(options) {
705
734
  system: systemPrompt,
706
735
  messages: modelMessages,
707
736
  tools: roundTools,
708
- ...(toolsAllowed ? {} : { allowTools: false }),
737
+ ...(mode === 'compact'
738
+ ? { toolChoice: 'none' }
739
+ : toolsAllowed
740
+ ? {}
741
+ : { allowTools: false }),
709
742
  promptCacheKey: sessionId,
710
743
  ...(continuation ? { continuation } : {}),
711
744
  reasoningEffort,
@@ -821,6 +854,8 @@ export async function createMarAgent(options) {
821
854
  });
822
855
  }
823
856
  else if (event.type === 'client_tool.call') {
857
+ if (mode === 'compact')
858
+ throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
824
859
  sawTool = true;
825
860
  pendingTools.push(event);
826
861
  }
@@ -851,6 +886,16 @@ export async function createMarAgent(options) {
851
886
  if (pendingTools.length === 0)
852
887
  continue;
853
888
  }
889
+ else if (mode === 'compact' &&
890
+ !sawModelSemanticEvent &&
891
+ isProviderContextOverflow(error) &&
892
+ useManualCompactionFallback?.()) {
893
+ sessionRuntime.responsesChain = undefined;
894
+ modelRetryState.attempts = 0;
895
+ responseId = undefined;
896
+ finalAnswer = '';
897
+ continue;
898
+ }
854
899
  else if (mode !== 'compact' &&
855
900
  !sawModelSemanticEvent &&
856
901
  isProviderContextOverflow(error)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.9.79",
3
+ "version": "0.9.80",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",