@myagentroam/agent 0.9.135 → 0.9.137
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.
- package/dist/cli/main.js +1 -1
- package/dist/host/local-agent-host.d.ts +9 -7
- package/dist/host/local-agent-host.js +77 -5
- package/dist/host/local-process-executor.d.ts +2 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -0
- package/dist/model/catalog-file.d.ts +2 -0
- package/dist/model/configuration.d.ts +5 -0
- package/dist/model/configuration.js +8 -0
- package/dist/model/contracts.d.ts +7 -0
- package/dist/model/openai-responses.d.ts +1 -1
- package/dist/model/openai-responses.js +85 -20
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.js +2 -2
- package/dist/prompts/workflow.d.ts +1 -1
- package/dist/prompts/workflow.js +9 -2
- package/dist/runtime/context-gc-runtime.d.ts +1 -0
- package/dist/runtime/context-gc-runtime.js +3 -0
- package/dist/runtime/context-gc-tokens.d.ts +1 -0
- package/dist/runtime/context-gc-tokens.js +1 -1
- package/dist/runtime/context-gc.d.ts +1 -0
- package/dist/runtime/context-gc.js +85 -8
- package/dist/runtime/context-projection.js +19 -2
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +29 -2
- package/dist/sdk/agent.js +249 -26
- package/dist/sdk/mcp-catalog.d.ts +13 -0
- package/dist/sdk/mcp-catalog.js +219 -0
- package/dist/sdk/types.d.ts +53 -1
- package/dist/session/context-gc-checkpoint.d.ts +7 -1
- package/dist/session/context-gc-checkpoint.js +28 -2
- package/dist/session/context-gc-replacements.d.ts +7 -0
- package/dist/session/context-gc-replacements.js +26 -0
- package/dist/session/jsonl-store.d.ts +6 -0
- package/dist/session/jsonl-store.js +92 -54
- package/dist/subagent/scheduler.d.ts +3 -3
- package/dist/subagent/session-controller.d.ts +1 -1
- package/dist/tools/agent-cancel.js +1 -0
- package/dist/tools/agent-message.js +1 -0
- package/dist/tools/agent-start.js +1 -0
- package/dist/tools/agent-wait.js +1 -0
- package/dist/tools/apply-patch.js +2 -1
- package/dist/tools/callers.d.ts +6 -0
- package/dist/tools/callers.js +23 -0
- package/dist/tools/code-mode/engine.d.ts +43 -0
- package/dist/tools/code-mode/engine.js +1 -0
- package/dist/tools/code-mode/quickjs-engine.d.ts +2 -0
- package/dist/tools/code-mode/quickjs-engine.js +454 -0
- package/dist/tools/code-mode/state.d.ts +20 -0
- package/dist/tools/code-mode/state.js +82 -0
- package/dist/tools/code-mode/worker.d.ts +1 -0
- package/dist/tools/code-mode/worker.js +65 -0
- package/dist/tools/code-mode.d.ts +8 -4
- package/dist/tools/code-mode.js +154 -120
- package/dist/tools/exec.js +1 -0
- package/dist/tools/execution-limits.d.ts +8 -0
- package/dist/tools/execution-limits.js +8 -0
- package/dist/tools/local-registry.d.ts +2 -0
- package/dist/tools/local-registry.js +1 -1
- package/dist/tools/question.js +1 -0
- package/dist/tools/read.js +1 -0
- package/dist/tools/registry.js +3 -0
- package/dist/tools/runtime-context.d.ts +2 -0
- package/dist/tools/runtime.js +3 -1
- package/dist/tools/skill.js +1 -0
- package/dist/tools/todo-read.js +1 -0
- package/dist/tools/todo-write.js +1 -0
- package/dist/tools/tool-search.d.ts +43 -0
- package/dist/tools/tool-search.js +284 -0
- package/dist/tools/view-image.js +1 -0
- package/dist/tools/web-fetch.js +1 -0
- package/dist/tools/web-search.js +1 -0
- package/package.json +3 -1
package/dist/sdk/agent.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isDeepStrictEqual } from 'node:util';
|
|
|
3
3
|
import { describeMarAgentError, MarAgentError } from '../error.js';
|
|
4
4
|
import { negotiateHost } from '../host/contracts.js';
|
|
5
5
|
import { AnthropicMessagesAdapter, isAnthropicRecoverableStreamError } from '../model/anthropic-messages.js';
|
|
6
|
-
import { marAgentReasoningEffortSchema, resolveModelDefaultReasoningEffort } from '../model/configuration.js';
|
|
6
|
+
import { marAgentReasoningEffortSchema, resolveModelDefaultReasoningEffort, usesResponsesConfigurationUpdates } from '../model/configuration.js';
|
|
7
7
|
import { modelRetryMaxAttempts, isRetryableModelRequestError, waitForModelRetry } from '../model/http.js';
|
|
8
8
|
import { OpenAiResponsesAdapter, isOpenAiResponsesRecoverableStreamError } from '../model/openai-responses.js';
|
|
9
9
|
import { AsyncQueue } from '../runtime/async-queue.js';
|
|
@@ -11,9 +11,11 @@ import { PlanOutputPrefix, splitPlanOutput } from '../runtime/plan-output.js';
|
|
|
11
11
|
import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
|
|
12
12
|
import { RolloutBudget, resolveRolloutBudgetOptions } from '../runtime/rollout-budget.js';
|
|
13
13
|
import { ContextGcRuntime } from '../runtime/context-gc-runtime.js';
|
|
14
|
-
import { buildContextProjection } from '../runtime/context-projection.js';
|
|
14
|
+
import { buildContextProjection, providerToolCallId, providerToolName } 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 { DeferredMcpRegistry } from '../tools/tool-search.js';
|
|
18
|
+
import { assertMcpToolCatalog } from './mcp-catalog.js';
|
|
17
19
|
import { DEFAULT_COMPACTION_FOCUS, CompactionOperation, buildCompactionInput, buildCompactionMessages, buildNativeCompactionMessages, buildCompactedHistory, compactedHistoryMessages, continuationMessage } from '../runtime/compact.js';
|
|
18
20
|
import { buildSystemPrompt, MAR_AGENT_PROMPT_VERSION } from '../prompts/index.js';
|
|
19
21
|
import { buildEnvironmentContext } from '../prompts/core.js';
|
|
@@ -31,8 +33,10 @@ import { BUILTIN_TOOL_DEFINITIONS } from '../tools/registry.js';
|
|
|
31
33
|
import { agentStartToolDefinitionWithModels } from '../tools/agent-start.js';
|
|
32
34
|
import { executeRegisteredTool } from '../tools/runtime.js';
|
|
33
35
|
import { scheduleToolCallBatch } from '../tools/scheduler.js';
|
|
34
|
-
import {
|
|
36
|
+
import { projectCodeModeSearchResult, projectCodeModeTools } from '../tools/code-mode.js';
|
|
37
|
+
import { allowsDirectTool, allowsToolCaller, toolCallers } from '../tools/callers.js';
|
|
35
38
|
import { SubagentSessionController } from '../subagent/session-controller.js';
|
|
39
|
+
const MODEL_PERFORMANCE_MIN_INTERVAL_MS = 1_000;
|
|
36
40
|
/** Retry only classified recoverable failures within the attempt budget; never retry cancellation. */
|
|
37
41
|
async function recoverModelStream(error, retryState, signal) {
|
|
38
42
|
if (signal.aborted ||
|
|
@@ -261,6 +265,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
261
265
|
releaseSession = await store.acquireSessionLease(sessionId);
|
|
262
266
|
await refreshSessionAdapters(executionModelGeneration);
|
|
263
267
|
const sessionState = await store.readExecutionState(sessionId);
|
|
268
|
+
let codeModeState = await store.readCodeModeState(sessionId);
|
|
264
269
|
const todo = TodoStore.restore(sessionState.todo);
|
|
265
270
|
const contextRecords = sessionState.contextRecords;
|
|
266
271
|
const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
|
|
@@ -410,9 +415,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
410
415
|
let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
|
|
411
416
|
const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
|
|
412
417
|
const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
|
|
413
|
-
const useEffortUpdates = selectedModel
|
|
414
|
-
selectedModel.responsesEncoding !== 'LITE' &&
|
|
415
|
-
selectedModel.codexCredential === true;
|
|
418
|
+
const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
|
|
416
419
|
const previousReasoningEffort = useEffortUpdates
|
|
417
420
|
? reasoningEffortBaseline(contextRecords, selectedModel.id)
|
|
418
421
|
: undefined;
|
|
@@ -496,7 +499,32 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
496
499
|
: { executionScopeId: internal.mcpExecutionScopeId }
|
|
497
500
|
: { claimExecutionScope: true })
|
|
498
501
|
};
|
|
499
|
-
const
|
|
502
|
+
const readMcpCatalog = async () => {
|
|
503
|
+
if (options.host.listMcpToolCatalog) {
|
|
504
|
+
const catalog = await options.host.listMcpToolCatalog(catalogContext);
|
|
505
|
+
assertMcpToolCatalog(catalog);
|
|
506
|
+
return catalog;
|
|
507
|
+
}
|
|
508
|
+
const definitions = (await options.host.listMcpTools?.(catalogContext)) ?? [];
|
|
509
|
+
return {
|
|
510
|
+
revision: createHash('sha256').update(JSON.stringify(definitions)).digest('hex'),
|
|
511
|
+
servers: [{ id: 'legacy', name: 'External MCP' }],
|
|
512
|
+
tools: definitions.map((definition) => ({
|
|
513
|
+
definition,
|
|
514
|
+
serverId: 'legacy',
|
|
515
|
+
rawName: definition.name,
|
|
516
|
+
sourceIdentity: `legacy:${executionId}`,
|
|
517
|
+
definitionRevision: createHash('sha256')
|
|
518
|
+
.update(JSON.stringify(definition))
|
|
519
|
+
.digest('hex'),
|
|
520
|
+
lifetime: 'execution',
|
|
521
|
+
exposure: 'deferred'
|
|
522
|
+
}))
|
|
523
|
+
};
|
|
524
|
+
};
|
|
525
|
+
const mcpCatalog = await readMcpCatalog();
|
|
526
|
+
const mcpRegistry = new DeferredMcpRegistry(discoveredMcpToolNames(messages));
|
|
527
|
+
const mcpTools = mcpCatalog.tools.map((tool) => tool.definition);
|
|
500
528
|
const extensions = (await options.host.listExecutionExtensions?.(catalogContext)) ?? {};
|
|
501
529
|
if (extensions.tools?.some((tool) => tool.name.startsWith('mcp__')))
|
|
502
530
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tools cannot use the MCP namespace.');
|
|
@@ -511,12 +539,15 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
511
539
|
const combinedTools = [...builtinTools, ...(extensions.tools ?? []), ...mcpTools];
|
|
512
540
|
if (new Set(combinedTools.map((tool) => tool.name)).size !== combinedTools.length)
|
|
513
541
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool names conflict.');
|
|
514
|
-
const
|
|
542
|
+
const toolEligible = (tool) => {
|
|
543
|
+
toolCallers(tool);
|
|
515
544
|
if (sessionSource.type === 'subagent' && tool.agentScope === 'ROOT_ONLY')
|
|
516
545
|
return false;
|
|
517
546
|
if (sessionSource.type === 'subagent' &&
|
|
518
547
|
(tool.name.startsWith('agent_') || tool.name === 'question'))
|
|
519
548
|
return false;
|
|
549
|
+
if (tool.name === 'tool_search')
|
|
550
|
+
return mcpRegistry.hasDeferred;
|
|
520
551
|
return tool.name === 'code_mode'
|
|
521
552
|
? selectedModelForTools.codeMode && description.capabilities.includes('code.execute')
|
|
522
553
|
: tool.name === 'question'
|
|
@@ -527,7 +558,10 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
527
558
|
: tool.name === 'view_image'
|
|
528
559
|
? selectedModelForTools.inputCapabilities.includes('IMAGE')
|
|
529
560
|
: tool.name !== 'web_fetch' || description.capabilities.includes('web.fetch');
|
|
530
|
-
}
|
|
561
|
+
};
|
|
562
|
+
mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
|
|
563
|
+
rebindMcpToolSearchOutputs(messages, mcpRegistry);
|
|
564
|
+
const availableTools = combinedTools.filter(toolEligible);
|
|
531
565
|
const instructionContext = await loadAgentInstructions({
|
|
532
566
|
home: description.homeDirectory,
|
|
533
567
|
workspace: instructionWorkspace,
|
|
@@ -546,8 +580,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
546
580
|
agentInstructionInsertIndex &&
|
|
547
581
|
messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
|
|
548
582
|
agentInstructionInsertIndex--;
|
|
549
|
-
const
|
|
550
|
-
|
|
583
|
+
const baseTools = () => [...builtinTools, ...(extensions.tools ?? [])].filter((tool) => toolEligible(tool) &&
|
|
584
|
+
(tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
|
|
585
|
+
let executionTools = mcpRegistry.project(baseTools());
|
|
586
|
+
const restoreMcpDiscovery = () => {
|
|
587
|
+
mcpRegistry.restore(discoveredMcpToolNames(messages));
|
|
588
|
+
rebindMcpToolSearchOutputs(messages, mcpRegistry);
|
|
589
|
+
executionTools = mcpRegistry.project(baseTools());
|
|
590
|
+
};
|
|
551
591
|
const loadRetainedSessionResources = async () => {
|
|
552
592
|
const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
|
|
553
593
|
processes: []
|
|
@@ -581,7 +621,13 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
581
621
|
? { guaranteedCommands: description.guaranteedCommands }
|
|
582
622
|
: {}),
|
|
583
623
|
workspace: logicalWorkspace,
|
|
584
|
-
tools: availableTools
|
|
624
|
+
tools: availableTools
|
|
625
|
+
.filter((tool) => !tool.name.startsWith('mcp__'))
|
|
626
|
+
.map((tool) => tool.name),
|
|
627
|
+
directTools: availableTools
|
|
628
|
+
.filter((tool) => !tool.name.startsWith('mcp__') &&
|
|
629
|
+
allowsDirectTool(tool, availableTools.some((candidate) => candidate.name === 'code_mode')))
|
|
630
|
+
.map((tool) => tool.name),
|
|
585
631
|
highDensityCompaction: selectedModel.highDensityCompaction,
|
|
586
632
|
extension: [
|
|
587
633
|
options.systemInstruction,
|
|
@@ -591,6 +637,27 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
591
637
|
.filter(Boolean)
|
|
592
638
|
.join('\n\n')
|
|
593
639
|
});
|
|
640
|
+
const syncMcpWorldState = async (index) => {
|
|
641
|
+
const snapshot = mcpRegistry.navigation();
|
|
642
|
+
if (contextualWorldState.mcpSnapshot === snapshot)
|
|
643
|
+
return;
|
|
644
|
+
contextualWorldState = {
|
|
645
|
+
...contextualWorldState,
|
|
646
|
+
mcpSnapshot: snapshot,
|
|
647
|
+
entries: [
|
|
648
|
+
...contextualWorldState.entries,
|
|
649
|
+
{
|
|
650
|
+
index,
|
|
651
|
+
kind: 'mcp',
|
|
652
|
+
content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
};
|
|
656
|
+
sessionRuntime.contextualWorldState = contextualWorldState;
|
|
657
|
+
await persistContextualWorldState(store, sessionId, contextualWorldState);
|
|
658
|
+
};
|
|
659
|
+
if (mode !== 'compact')
|
|
660
|
+
await syncMcpWorldState(agentInstructionInsertIndex);
|
|
594
661
|
const initialSystemPrompt = mode === 'compact'
|
|
595
662
|
? buildExecutionSystemPrompt(executionTools, 'run')
|
|
596
663
|
: buildExecutionSystemPrompt();
|
|
@@ -740,6 +807,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
740
807
|
});
|
|
741
808
|
contextGc.reset();
|
|
742
809
|
messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory));
|
|
810
|
+
restoreMcpDiscovery();
|
|
743
811
|
projectedExecutionBudgetStage = undefined;
|
|
744
812
|
recordedEnvironment = executionEnvironment;
|
|
745
813
|
compactionUserMessages = replacementHistory
|
|
@@ -785,7 +853,12 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
785
853
|
let roundServerUsage;
|
|
786
854
|
const pendingTools = [];
|
|
787
855
|
let incompleteToolCall = false;
|
|
856
|
+
executionTools = mcpRegistry.project(baseTools());
|
|
857
|
+
if (mode !== 'compact')
|
|
858
|
+
await syncMcpWorldState(messages.length);
|
|
788
859
|
await ensureContextBudget();
|
|
860
|
+
if (mode !== 'compact')
|
|
861
|
+
await syncMcpWorldState(messages.length);
|
|
789
862
|
appendExecutionBudgetEnvironment();
|
|
790
863
|
if (mode !== 'compact') {
|
|
791
864
|
executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
|
|
@@ -793,6 +866,16 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
793
866
|
}
|
|
794
867
|
const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
|
|
795
868
|
const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
|
|
869
|
+
const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_mode');
|
|
870
|
+
const residentTools = mcpRegistry.projectDirect(baseTools());
|
|
871
|
+
const requestTools = (() => {
|
|
872
|
+
if (selectedModel.protocol !== 'OPENAI_RESPONSES' ||
|
|
873
|
+
selectedModel.responsesEncoding === 'LITE')
|
|
874
|
+
return projectCodeModeTools(roundTools, residentTools);
|
|
875
|
+
const directNames = new Set(residentTools.map((tool) => tool.name));
|
|
876
|
+
// Discovery and caller policy are independent: keep nested declarations for active tools.
|
|
877
|
+
return projectCodeModeTools(roundTools, residentTools).filter((tool) => directNames.has(tool.name));
|
|
878
|
+
})();
|
|
796
879
|
const systemPrompt = mode === 'compact'
|
|
797
880
|
? buildExecutionSystemPrompt(roundTools, 'run')
|
|
798
881
|
: buildExecutionSystemPrompt(roundTools);
|
|
@@ -804,8 +887,47 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
804
887
|
: normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
|
|
805
888
|
supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
|
|
806
889
|
});
|
|
890
|
+
const roundMcpExpectations = new Map(roundTools
|
|
891
|
+
.filter((tool) => tool.name.startsWith('mcp__'))
|
|
892
|
+
.map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
|
|
807
893
|
let sawModelSemanticEvent = false;
|
|
894
|
+
let requestStartedAt;
|
|
895
|
+
let firstModelResponseAt;
|
|
896
|
+
let requestCompletedAt;
|
|
897
|
+
let requestOutputTokens;
|
|
898
|
+
let outputTokenRevision = 0;
|
|
899
|
+
let lastPerformanceEmittedAt;
|
|
900
|
+
let lastPublishedOutputTokenRevision = 0;
|
|
901
|
+
let performanceEmitted = false;
|
|
808
902
|
const roundMessageCount = messages.length;
|
|
903
|
+
const emitPerformance = async (sampledAt, force = false) => {
|
|
904
|
+
if (mode === 'compact' || firstModelResponseAt === undefined)
|
|
905
|
+
return;
|
|
906
|
+
const outputDurationMs = Math.max(0, Math.round(sampledAt - firstModelResponseAt));
|
|
907
|
+
const outputTokens = requestOutputTokens;
|
|
908
|
+
const tokensPerSecond = outputTokens !== undefined && outputDurationMs > 0
|
|
909
|
+
? Number(((outputTokens * 1000) / outputDurationMs).toFixed(2))
|
|
910
|
+
: undefined;
|
|
911
|
+
if (performanceEmitted &&
|
|
912
|
+
(tokensPerSecond === undefined ||
|
|
913
|
+
outputTokenRevision === lastPublishedOutputTokenRevision))
|
|
914
|
+
return;
|
|
915
|
+
if (!force &&
|
|
916
|
+
lastPerformanceEmittedAt !== undefined &&
|
|
917
|
+
sampledAt - lastPerformanceEmittedAt < MODEL_PERFORMANCE_MIN_INTERVAL_MS)
|
|
918
|
+
return;
|
|
919
|
+
await emit({
|
|
920
|
+
type: 'model.performance',
|
|
921
|
+
latencyMs: Math.max(0, Math.round(firstModelResponseAt - requestStartedAt)),
|
|
922
|
+
...(outputTokens === undefined ? {} : { outputTokens }),
|
|
923
|
+
outputDurationMs,
|
|
924
|
+
...(tokensPerSecond === undefined ? {} : { tokensPerSecond })
|
|
925
|
+
});
|
|
926
|
+
performanceEmitted = true;
|
|
927
|
+
lastPerformanceEmittedAt = sampledAt;
|
|
928
|
+
if (tokensPerSecond !== undefined)
|
|
929
|
+
lastPublishedOutputTokenRevision = outputTokenRevision;
|
|
930
|
+
};
|
|
809
931
|
const persistToolCalls = async (calls) => {
|
|
810
932
|
for (const call of calls) {
|
|
811
933
|
const message = {
|
|
@@ -834,11 +956,12 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
834
956
|
const startModel = manualCompaction
|
|
835
957
|
? (request) => manualCompaction.start(selected, request)
|
|
836
958
|
: (request) => selected.start(request, controller.signal);
|
|
959
|
+
requestStartedAt = performance.now();
|
|
837
960
|
for await (const event of startModel({
|
|
838
961
|
retryState: modelRetryState,
|
|
839
962
|
system: systemPrompt,
|
|
840
|
-
messages: modelMessages,
|
|
841
|
-
tools:
|
|
963
|
+
messages: modelMessages.map((message) => projectCodeModeSearchResult(message, codeModeEnabled)),
|
|
964
|
+
tools: requestTools,
|
|
842
965
|
...(mode === 'compact'
|
|
843
966
|
? { toolChoice: 'none' }
|
|
844
967
|
: toolsAllowed
|
|
@@ -850,8 +973,11 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
850
973
|
...(modelInputImages?.length ? { images: modelInputImages } : {}),
|
|
851
974
|
onAttemptDiagnostic: recordModelAttempt(mode === 'compact' ? 'COMPACTION' : 'EXECUTION')
|
|
852
975
|
})) {
|
|
853
|
-
|
|
976
|
+
const modelResponseAt = mode !== 'compact' && isModelResponseEvent(event) ? performance.now() : undefined;
|
|
977
|
+
if (modelResponseAt !== undefined) {
|
|
854
978
|
sawModelSemanticEvent = true;
|
|
979
|
+
firstModelResponseAt ??= modelResponseAt;
|
|
980
|
+
}
|
|
855
981
|
if (event.type === 'response.item') {
|
|
856
982
|
// Compaction keeps only its successful checkpoint, never intermediate native output.
|
|
857
983
|
if (mode === 'compact')
|
|
@@ -884,13 +1010,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
884
1010
|
}
|
|
885
1011
|
delta = prefix.push(delta);
|
|
886
1012
|
}
|
|
887
|
-
if (mode !== 'compact' && delta)
|
|
1013
|
+
if (mode !== 'compact' && delta) {
|
|
888
1014
|
await emit({
|
|
889
1015
|
type: 'message.delta',
|
|
890
1016
|
itemId: event.itemId,
|
|
891
1017
|
phase: event.phase,
|
|
892
1018
|
delta
|
|
893
1019
|
});
|
|
1020
|
+
}
|
|
894
1021
|
}
|
|
895
1022
|
else if (event.type === 'text.completed') {
|
|
896
1023
|
const phase = event.phase ?? 'final_answer';
|
|
@@ -904,7 +1031,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
904
1031
|
const itemId = index === 0 ? event.itemId : `${event.itemId}:plan-part:${index}`;
|
|
905
1032
|
if (part.kind === 'plan')
|
|
906
1033
|
await emit({ type: 'plan.completed', itemId, text: part.text });
|
|
907
|
-
else
|
|
1034
|
+
else {
|
|
908
1035
|
await emit({
|
|
909
1036
|
type: 'message.completed',
|
|
910
1037
|
itemId,
|
|
@@ -913,9 +1040,10 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
913
1040
|
modelId: selectedModel.id,
|
|
914
1041
|
modelName: selectedModel.name
|
|
915
1042
|
});
|
|
1043
|
+
}
|
|
916
1044
|
}
|
|
917
1045
|
}
|
|
918
|
-
else
|
|
1046
|
+
else {
|
|
919
1047
|
await emit({
|
|
920
1048
|
type: 'message.completed',
|
|
921
1049
|
itemId: event.itemId,
|
|
@@ -924,11 +1052,21 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
924
1052
|
modelId: selectedModel.id,
|
|
925
1053
|
modelName: selectedModel.name
|
|
926
1054
|
});
|
|
1055
|
+
}
|
|
927
1056
|
}
|
|
928
1057
|
messages.push({ role: 'assistant', content: event.text });
|
|
929
1058
|
}
|
|
930
1059
|
else if (event.type === 'usage') {
|
|
931
1060
|
latestUsage = event.usage;
|
|
1061
|
+
const outputTokens = typeof event.usage.outputTokens === 'number' &&
|
|
1062
|
+
Number.isSafeInteger(event.usage.outputTokens) &&
|
|
1063
|
+
event.usage.outputTokens >= 0
|
|
1064
|
+
? event.usage.outputTokens
|
|
1065
|
+
: undefined;
|
|
1066
|
+
if (outputTokens !== undefined) {
|
|
1067
|
+
requestOutputTokens = outputTokens;
|
|
1068
|
+
outputTokenRevision++;
|
|
1069
|
+
}
|
|
932
1070
|
const reportedContext = event.usage.contextInputTokens ?? event.usage.inputTokens;
|
|
933
1071
|
if (reportedContext !== undefined &&
|
|
934
1072
|
Number.isSafeInteger(reportedContext) &&
|
|
@@ -974,8 +1112,16 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
974
1112
|
sawTool = true;
|
|
975
1113
|
pendingTools.push(event);
|
|
976
1114
|
}
|
|
977
|
-
else if (event.type === 'completed')
|
|
1115
|
+
else if (event.type === 'completed') {
|
|
978
1116
|
stop = event.reason;
|
|
1117
|
+
requestCompletedAt = performance.now();
|
|
1118
|
+
}
|
|
1119
|
+
if (modelResponseAt !== undefined)
|
|
1120
|
+
await emitPerformance(modelResponseAt);
|
|
1121
|
+
}
|
|
1122
|
+
if (mode !== 'compact' && firstModelResponseAt !== undefined) {
|
|
1123
|
+
const completedAt = requestCompletedAt ?? performance.now();
|
|
1124
|
+
await emitPerformance(completedAt, true);
|
|
979
1125
|
}
|
|
980
1126
|
pendingOutputContinuation = undefined;
|
|
981
1127
|
incompleteToolCall = pendingTools.some((call) => call.argumentError !== undefined);
|
|
@@ -1031,21 +1177,31 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1031
1177
|
finalAnswer = '';
|
|
1032
1178
|
continue;
|
|
1033
1179
|
}
|
|
1034
|
-
const executeCall = async (call, signal = controller.signal) => {
|
|
1180
|
+
const executeCall = async (call, signal = controller.signal, caller = 'direct', parentCallId) => {
|
|
1035
1181
|
await emit({
|
|
1036
1182
|
type: 'tool.started',
|
|
1037
1183
|
toolName: call.name,
|
|
1038
1184
|
callId: call.callId,
|
|
1185
|
+
...(parentCallId === undefined ? {} : { parentCallId }),
|
|
1039
1186
|
input: call.arguments
|
|
1040
1187
|
});
|
|
1041
1188
|
try {
|
|
1042
1189
|
if (call.argumentError)
|
|
1043
1190
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', call.argumentError);
|
|
1044
|
-
if (!
|
|
1191
|
+
if (!roundTools.some((tool) => tool.name === call.name && allowsToolCaller(tool, caller)))
|
|
1045
1192
|
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool is unavailable in this execution.');
|
|
1046
1193
|
const output = await executeRegisteredTool(call.name, call.arguments, call.callId, {
|
|
1047
1194
|
host: options.host,
|
|
1048
1195
|
todo,
|
|
1196
|
+
...(roundMcpExpectations.get(call.name)
|
|
1197
|
+
? { expectedMcpTool: roundMcpExpectations.get(call.name) }
|
|
1198
|
+
: {}),
|
|
1199
|
+
searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
|
|
1200
|
+
signal: searchSignal,
|
|
1201
|
+
base: baseTools(),
|
|
1202
|
+
window: selectedModel.contextWindowTokens,
|
|
1203
|
+
system: systemPrompt
|
|
1204
|
+
}),
|
|
1049
1205
|
...(subagents === undefined ? {} : { subagents }),
|
|
1050
1206
|
mode,
|
|
1051
1207
|
sessionId,
|
|
@@ -1053,14 +1209,22 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1053
1209
|
signal,
|
|
1054
1210
|
codeMode: {
|
|
1055
1211
|
supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
|
|
1056
|
-
tools: roundTools,
|
|
1212
|
+
tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
|
|
1213
|
+
state: codeModeState,
|
|
1214
|
+
commitState: async (mutations) => {
|
|
1215
|
+
codeModeState = await store.commitCodeModeState(sessionId, mutations, {
|
|
1216
|
+
executionId,
|
|
1217
|
+
turnId
|
|
1218
|
+
});
|
|
1219
|
+
return codeModeState;
|
|
1220
|
+
},
|
|
1057
1221
|
invoke: async (nested, nestedSignal) => {
|
|
1058
1222
|
const result = await executeCall({
|
|
1059
1223
|
type: 'client_tool.call',
|
|
1060
1224
|
name: nested.name,
|
|
1061
1225
|
arguments: nested.arguments,
|
|
1062
1226
|
callId: nested.callId ?? randomUUID()
|
|
1063
|
-
}, nestedSignal);
|
|
1227
|
+
}, nestedSignal, 'programmatic', call.callId);
|
|
1064
1228
|
if (result.failed)
|
|
1065
1229
|
throw result.error;
|
|
1066
1230
|
return { ...result.output, content: result.content };
|
|
@@ -1082,6 +1246,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1082
1246
|
type: 'tool.completed',
|
|
1083
1247
|
toolName: call.name,
|
|
1084
1248
|
callId: call.callId,
|
|
1249
|
+
...(parentCallId === undefined ? {} : { parentCallId }),
|
|
1085
1250
|
summary: eventSummary,
|
|
1086
1251
|
truncated: output.truncated === true ||
|
|
1087
1252
|
modelOutput.truncated ||
|
|
@@ -1103,6 +1268,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1103
1268
|
type: 'tool.failed',
|
|
1104
1269
|
toolName: call.name,
|
|
1105
1270
|
callId: call.callId,
|
|
1271
|
+
...(parentCallId === undefined ? {} : { parentCallId }),
|
|
1106
1272
|
code: diagnostic.code,
|
|
1107
1273
|
message: diagnostic.message
|
|
1108
1274
|
});
|
|
@@ -1127,10 +1293,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1127
1293
|
mimeType: image.mimeType,
|
|
1128
1294
|
data: Buffer.from(image.dataBase64, 'base64')
|
|
1129
1295
|
})));
|
|
1296
|
+
const toolSearchTools = result.call.name === 'tool_search' && !result.failed
|
|
1297
|
+
? result.output.toolSearchTools
|
|
1298
|
+
: undefined;
|
|
1130
1299
|
const message = {
|
|
1131
1300
|
role: 'tool',
|
|
1132
1301
|
callId: result.call.callId,
|
|
1133
1302
|
content: result.content,
|
|
1303
|
+
...(toolSearchTools === undefined ? {} : { toolSearchTools }),
|
|
1134
1304
|
...('images' in result && result.images?.length ? { images: result.images } : {})
|
|
1135
1305
|
};
|
|
1136
1306
|
messages.push(message);
|
|
@@ -1139,7 +1309,10 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1139
1309
|
payload: {
|
|
1140
1310
|
role: message.role,
|
|
1141
1311
|
callId: message.callId,
|
|
1142
|
-
content: message.content
|
|
1312
|
+
content: message.content,
|
|
1313
|
+
...(message.toolSearchTools === undefined
|
|
1314
|
+
? {}
|
|
1315
|
+
: { toolSearchTools: message.toolSearchTools })
|
|
1143
1316
|
},
|
|
1144
1317
|
executionId,
|
|
1145
1318
|
turnId
|
|
@@ -1182,6 +1355,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1182
1355
|
contextWindowTokens: selectedModel.contextWindowTokens,
|
|
1183
1356
|
usage: latestUsage,
|
|
1184
1357
|
modelProtocol: selectedModel.protocol,
|
|
1358
|
+
responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
|
|
1185
1359
|
workspace: logicalWorkspace,
|
|
1186
1360
|
platform: description.platform,
|
|
1187
1361
|
getActiveProcessIds: async () => {
|
|
@@ -1201,6 +1375,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1201
1375
|
projection: committed.projection
|
|
1202
1376
|
});
|
|
1203
1377
|
messages.splice(0, messages.length, ...refreshed.messages);
|
|
1378
|
+
restoreMcpDiscovery();
|
|
1204
1379
|
projectedExecutionBudgetStage = undefined;
|
|
1205
1380
|
recordedEnvironment = refreshed.environmentState;
|
|
1206
1381
|
compactionUserMessages = [...refreshed.userMessages];
|
|
@@ -1986,7 +2161,7 @@ function parsePersistedContextualWorldState(payload) {
|
|
|
1986
2161
|
const entry = value;
|
|
1987
2162
|
if (!Number.isSafeInteger(entry.index) ||
|
|
1988
2163
|
entry.index < 0 ||
|
|
1989
|
-
(entry.kind !== 'instructions' && entry.kind !== 'resources') ||
|
|
2164
|
+
(entry.kind !== 'instructions' && entry.kind !== 'resources' && entry.kind !== 'mcp') ||
|
|
1990
2165
|
typeof entry.content !== 'string' ||
|
|
1991
2166
|
entry.content.length === 0)
|
|
1992
2167
|
return undefined;
|
|
@@ -2004,6 +2179,7 @@ function parsePersistedContextualWorldState(payload) {
|
|
|
2004
2179
|
initialized: true,
|
|
2005
2180
|
instructionSnapshot,
|
|
2006
2181
|
resourceSnapshot,
|
|
2182
|
+
mcpSnapshot: typeof record.mcpSnapshot === 'string' ? record.mcpSnapshot : undefined,
|
|
2007
2183
|
entries
|
|
2008
2184
|
};
|
|
2009
2185
|
}
|
|
@@ -2021,6 +2197,7 @@ async function persistContextualWorldState(store, sessionId, state) {
|
|
|
2021
2197
|
generation: state.generation ?? null,
|
|
2022
2198
|
instructionSnapshot: state.instructionSnapshot ?? null,
|
|
2023
2199
|
resourceSnapshot: state.resourceSnapshot ?? null,
|
|
2200
|
+
mcpSnapshot: state.mcpSnapshot ?? null,
|
|
2024
2201
|
entries: state.entries
|
|
2025
2202
|
});
|
|
2026
2203
|
}
|
|
@@ -2038,6 +2215,7 @@ function updateContextualWorldState(state, index, current) {
|
|
|
2038
2215
|
entries.push({ index, kind: 'resources', content: resourceContent });
|
|
2039
2216
|
return {
|
|
2040
2217
|
generation: state.generation,
|
|
2218
|
+
mcpSnapshot: state.mcpSnapshot,
|
|
2041
2219
|
initialized: true,
|
|
2042
2220
|
instructionSnapshot: current.instructionSnapshot,
|
|
2043
2221
|
resourceSnapshot: current.resourceSnapshot,
|
|
@@ -2100,6 +2278,38 @@ function stableJson(value) {
|
|
|
2100
2278
|
.join(',')}}`;
|
|
2101
2279
|
return JSON.stringify(value) ?? 'undefined';
|
|
2102
2280
|
}
|
|
2281
|
+
function discoveredMcpToolNames(messages) {
|
|
2282
|
+
const calls = toolSearchCallIds(messages);
|
|
2283
|
+
const names = new Set();
|
|
2284
|
+
for (const message of messages)
|
|
2285
|
+
for (const tool of calls.has(message.callId ?? '') ? (message.toolSearchTools ?? []) : [])
|
|
2286
|
+
if (tool.name.startsWith('mcp__'))
|
|
2287
|
+
names.add(tool.name);
|
|
2288
|
+
return [...names];
|
|
2289
|
+
}
|
|
2290
|
+
function rebindMcpToolSearchOutputs(messages, registry) {
|
|
2291
|
+
const calls = toolSearchCallIds(messages);
|
|
2292
|
+
for (const message of messages) {
|
|
2293
|
+
if (!message.toolSearchTools)
|
|
2294
|
+
continue;
|
|
2295
|
+
if (!calls.has(message.callId ?? '')) {
|
|
2296
|
+
delete message.toolSearchTools;
|
|
2297
|
+
continue;
|
|
2298
|
+
}
|
|
2299
|
+
const definitions = registry.restoredDefinitions(message.toolSearchTools.map((tool) => tool.name));
|
|
2300
|
+
message.toolSearchTools = definitions;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
function toolSearchCallIds(messages) {
|
|
2304
|
+
return new Set(messages
|
|
2305
|
+
.filter((message) => (message.role === 'tool_call' &&
|
|
2306
|
+
message.name === 'tool_search' &&
|
|
2307
|
+
typeof message.callId === 'string') ||
|
|
2308
|
+
(message.role === 'provider' &&
|
|
2309
|
+
providerToolName(message) === 'tool_search' &&
|
|
2310
|
+
providerToolCallId(message) !== undefined))
|
|
2311
|
+
.map((message) => message.role === 'provider' ? providerToolCallId(message) : message.callId));
|
|
2312
|
+
}
|
|
2103
2313
|
/** Project only known, validated tool result shapes into public structured events. */
|
|
2104
2314
|
function structuredToolEventData(toolName, value) {
|
|
2105
2315
|
if (!isRecord(value))
|
|
@@ -2196,13 +2406,26 @@ function structuredToolArtifacts(value) {
|
|
|
2196
2406
|
}
|
|
2197
2407
|
/** Keep streaming deltas live-only; exec output has one durable copy in model context records. */
|
|
2198
2408
|
function eventForPersistence(event) {
|
|
2199
|
-
if (event.type === 'message.delta' ||
|
|
2409
|
+
if (event.type === 'message.delta' ||
|
|
2410
|
+
event.type === 'context.compacting' ||
|
|
2411
|
+
event.type === 'model.performance' ||
|
|
2412
|
+
event.type === 'message.performance')
|
|
2200
2413
|
return undefined;
|
|
2201
2414
|
if (event.type !== 'tool.completed' || event.toolName !== 'exec' || !event.data)
|
|
2202
2415
|
return event;
|
|
2203
2416
|
const metadata = Object.fromEntries(Object.entries(event.data).filter(([key]) => key !== 'stdout' && key !== 'stderr'));
|
|
2204
2417
|
return { ...event, data: metadata };
|
|
2205
2418
|
}
|
|
2419
|
+
function isModelResponseEvent(event) {
|
|
2420
|
+
if (event.type === 'text.delta')
|
|
2421
|
+
return event.delta.length > 0;
|
|
2422
|
+
if (event.type === 'text.completed' || event.type === 'reasoning.summary')
|
|
2423
|
+
return event.text.length > 0;
|
|
2424
|
+
return (event.type === 'response.item' ||
|
|
2425
|
+
event.type === 'client_tool.call' ||
|
|
2426
|
+
event.type === 'hosted_tool' ||
|
|
2427
|
+
event.type === 'citation');
|
|
2428
|
+
}
|
|
2206
2429
|
function isRecord(value) {
|
|
2207
2430
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
2208
2431
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ClientToolDefinition } from '../model/contracts.js';
|
|
2
|
+
import type { McpToolCatalog } from './types.js';
|
|
3
|
+
export declare const MCP_DEFINITION_LIMITS: Readonly<{
|
|
4
|
+
depth: 64;
|
|
5
|
+
nodes: 32768;
|
|
6
|
+
definitionBytes: number;
|
|
7
|
+
catalogBytes: number;
|
|
8
|
+
tools: 10000;
|
|
9
|
+
servers: 256;
|
|
10
|
+
}>;
|
|
11
|
+
/** Shared Host and Node publication boundary, not a provider-specific conversion. */
|
|
12
|
+
export declare function assertMcpToolDefinition(value: unknown): asserts value is ClientToolDefinition;
|
|
13
|
+
export declare function assertMcpToolCatalog(value: unknown): asserts value is McpToolCatalog;
|