@myagentroam/agent 0.9.135 → 0.9.136
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/workflow.js +2 -0
- 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 +208 -25
- package/dist/sdk/mcp-catalog.d.ts +13 -0
- package/dist/sdk/mcp-catalog.js +219 -0
- package/dist/sdk/types.d.ts +50 -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 +4 -0
- package/dist/tools/callers.js +18 -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 +5 -3
- package/dist/tools/code-mode.js +129 -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 +278 -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,7 +33,8 @@ 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 { projectCodeModeTools } from '../tools/code-mode.js';
|
|
37
|
+
import { allowsToolCaller, toolCallers } from '../tools/callers.js';
|
|
35
38
|
import { SubagentSessionController } from '../subagent/session-controller.js';
|
|
36
39
|
/** Retry only classified recoverable failures within the attempt budget; never retry cancellation. */
|
|
37
40
|
async function recoverModelStream(error, retryState, signal) {
|
|
@@ -261,6 +264,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
261
264
|
releaseSession = await store.acquireSessionLease(sessionId);
|
|
262
265
|
await refreshSessionAdapters(executionModelGeneration);
|
|
263
266
|
const sessionState = await store.readExecutionState(sessionId);
|
|
267
|
+
let codeModeState = await store.readCodeModeState(sessionId);
|
|
264
268
|
const todo = TodoStore.restore(sessionState.todo);
|
|
265
269
|
const contextRecords = sessionState.contextRecords;
|
|
266
270
|
const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
|
|
@@ -410,9 +414,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
410
414
|
let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
|
|
411
415
|
const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
|
|
412
416
|
const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
|
|
413
|
-
const useEffortUpdates = selectedModel
|
|
414
|
-
selectedModel.responsesEncoding !== 'LITE' &&
|
|
415
|
-
selectedModel.codexCredential === true;
|
|
417
|
+
const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
|
|
416
418
|
const previousReasoningEffort = useEffortUpdates
|
|
417
419
|
? reasoningEffortBaseline(contextRecords, selectedModel.id)
|
|
418
420
|
: undefined;
|
|
@@ -496,7 +498,32 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
496
498
|
: { executionScopeId: internal.mcpExecutionScopeId }
|
|
497
499
|
: { claimExecutionScope: true })
|
|
498
500
|
};
|
|
499
|
-
const
|
|
501
|
+
const readMcpCatalog = async () => {
|
|
502
|
+
if (options.host.listMcpToolCatalog) {
|
|
503
|
+
const catalog = await options.host.listMcpToolCatalog(catalogContext);
|
|
504
|
+
assertMcpToolCatalog(catalog);
|
|
505
|
+
return catalog;
|
|
506
|
+
}
|
|
507
|
+
const definitions = (await options.host.listMcpTools?.(catalogContext)) ?? [];
|
|
508
|
+
return {
|
|
509
|
+
revision: createHash('sha256').update(JSON.stringify(definitions)).digest('hex'),
|
|
510
|
+
servers: [{ id: 'legacy', name: 'External MCP' }],
|
|
511
|
+
tools: definitions.map((definition) => ({
|
|
512
|
+
definition,
|
|
513
|
+
serverId: 'legacy',
|
|
514
|
+
rawName: definition.name,
|
|
515
|
+
sourceIdentity: `legacy:${executionId}`,
|
|
516
|
+
definitionRevision: createHash('sha256')
|
|
517
|
+
.update(JSON.stringify(definition))
|
|
518
|
+
.digest('hex'),
|
|
519
|
+
lifetime: 'execution',
|
|
520
|
+
exposure: 'deferred'
|
|
521
|
+
}))
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
const mcpCatalog = await readMcpCatalog();
|
|
525
|
+
const mcpRegistry = new DeferredMcpRegistry(discoveredMcpToolNames(messages));
|
|
526
|
+
const mcpTools = mcpCatalog.tools.map((tool) => tool.definition);
|
|
500
527
|
const extensions = (await options.host.listExecutionExtensions?.(catalogContext)) ?? {};
|
|
501
528
|
if (extensions.tools?.some((tool) => tool.name.startsWith('mcp__')))
|
|
502
529
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tools cannot use the MCP namespace.');
|
|
@@ -511,12 +538,15 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
511
538
|
const combinedTools = [...builtinTools, ...(extensions.tools ?? []), ...mcpTools];
|
|
512
539
|
if (new Set(combinedTools.map((tool) => tool.name)).size !== combinedTools.length)
|
|
513
540
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool names conflict.');
|
|
514
|
-
const
|
|
541
|
+
const toolEligible = (tool) => {
|
|
542
|
+
toolCallers(tool);
|
|
515
543
|
if (sessionSource.type === 'subagent' && tool.agentScope === 'ROOT_ONLY')
|
|
516
544
|
return false;
|
|
517
545
|
if (sessionSource.type === 'subagent' &&
|
|
518
546
|
(tool.name.startsWith('agent_') || tool.name === 'question'))
|
|
519
547
|
return false;
|
|
548
|
+
if (tool.name === 'tool_search')
|
|
549
|
+
return mcpRegistry.hasDeferred;
|
|
520
550
|
return tool.name === 'code_mode'
|
|
521
551
|
? selectedModelForTools.codeMode && description.capabilities.includes('code.execute')
|
|
522
552
|
: tool.name === 'question'
|
|
@@ -527,7 +557,10 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
527
557
|
: tool.name === 'view_image'
|
|
528
558
|
? selectedModelForTools.inputCapabilities.includes('IMAGE')
|
|
529
559
|
: tool.name !== 'web_fetch' || description.capabilities.includes('web.fetch');
|
|
530
|
-
}
|
|
560
|
+
};
|
|
561
|
+
mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
|
|
562
|
+
rebindMcpToolSearchOutputs(messages, mcpRegistry);
|
|
563
|
+
const availableTools = combinedTools.filter(toolEligible);
|
|
531
564
|
const instructionContext = await loadAgentInstructions({
|
|
532
565
|
home: description.homeDirectory,
|
|
533
566
|
workspace: instructionWorkspace,
|
|
@@ -546,8 +579,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
546
579
|
agentInstructionInsertIndex &&
|
|
547
580
|
messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
|
|
548
581
|
agentInstructionInsertIndex--;
|
|
549
|
-
const
|
|
550
|
-
|
|
582
|
+
const baseTools = () => [...builtinTools, ...(extensions.tools ?? [])].filter((tool) => toolEligible(tool) &&
|
|
583
|
+
(tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
|
|
584
|
+
let executionTools = mcpRegistry.project(baseTools());
|
|
585
|
+
const restoreMcpDiscovery = () => {
|
|
586
|
+
mcpRegistry.restore(discoveredMcpToolNames(messages));
|
|
587
|
+
rebindMcpToolSearchOutputs(messages, mcpRegistry);
|
|
588
|
+
executionTools = mcpRegistry.project(baseTools());
|
|
589
|
+
};
|
|
551
590
|
const loadRetainedSessionResources = async () => {
|
|
552
591
|
const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
|
|
553
592
|
processes: []
|
|
@@ -581,7 +620,9 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
581
620
|
? { guaranteedCommands: description.guaranteedCommands }
|
|
582
621
|
: {}),
|
|
583
622
|
workspace: logicalWorkspace,
|
|
584
|
-
tools: availableTools
|
|
623
|
+
tools: availableTools
|
|
624
|
+
.filter((tool) => !tool.name.startsWith('mcp__'))
|
|
625
|
+
.map((tool) => tool.name),
|
|
585
626
|
highDensityCompaction: selectedModel.highDensityCompaction,
|
|
586
627
|
extension: [
|
|
587
628
|
options.systemInstruction,
|
|
@@ -591,6 +632,27 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
591
632
|
.filter(Boolean)
|
|
592
633
|
.join('\n\n')
|
|
593
634
|
});
|
|
635
|
+
const syncMcpWorldState = async (index) => {
|
|
636
|
+
const snapshot = mcpRegistry.navigation();
|
|
637
|
+
if (contextualWorldState.mcpSnapshot === snapshot)
|
|
638
|
+
return;
|
|
639
|
+
contextualWorldState = {
|
|
640
|
+
...contextualWorldState,
|
|
641
|
+
mcpSnapshot: snapshot,
|
|
642
|
+
entries: [
|
|
643
|
+
...contextualWorldState.entries,
|
|
644
|
+
{
|
|
645
|
+
index,
|
|
646
|
+
kind: 'mcp',
|
|
647
|
+
content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
};
|
|
651
|
+
sessionRuntime.contextualWorldState = contextualWorldState;
|
|
652
|
+
await persistContextualWorldState(store, sessionId, contextualWorldState);
|
|
653
|
+
};
|
|
654
|
+
if (mode !== 'compact')
|
|
655
|
+
await syncMcpWorldState(agentInstructionInsertIndex);
|
|
594
656
|
const initialSystemPrompt = mode === 'compact'
|
|
595
657
|
? buildExecutionSystemPrompt(executionTools, 'run')
|
|
596
658
|
: buildExecutionSystemPrompt();
|
|
@@ -740,6 +802,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
740
802
|
});
|
|
741
803
|
contextGc.reset();
|
|
742
804
|
messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory));
|
|
805
|
+
restoreMcpDiscovery();
|
|
743
806
|
projectedExecutionBudgetStage = undefined;
|
|
744
807
|
recordedEnvironment = executionEnvironment;
|
|
745
808
|
compactionUserMessages = replacementHistory
|
|
@@ -785,7 +848,12 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
785
848
|
let roundServerUsage;
|
|
786
849
|
const pendingTools = [];
|
|
787
850
|
let incompleteToolCall = false;
|
|
851
|
+
executionTools = mcpRegistry.project(baseTools());
|
|
852
|
+
if (mode !== 'compact')
|
|
853
|
+
await syncMcpWorldState(messages.length);
|
|
788
854
|
await ensureContextBudget();
|
|
855
|
+
if (mode !== 'compact')
|
|
856
|
+
await syncMcpWorldState(messages.length);
|
|
789
857
|
appendExecutionBudgetEnvironment();
|
|
790
858
|
if (mode !== 'compact') {
|
|
791
859
|
executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
|
|
@@ -793,6 +861,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
793
861
|
}
|
|
794
862
|
const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
|
|
795
863
|
const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
|
|
864
|
+
const requestTools = (() => {
|
|
865
|
+
if (selectedModel.protocol !== 'OPENAI_RESPONSES' ||
|
|
866
|
+
selectedModel.responsesEncoding === 'LITE')
|
|
867
|
+
return projectCodeModeTools(roundTools);
|
|
868
|
+
const directNames = new Set(mcpRegistry.projectDirect(baseTools()).map((tool) => tool.name));
|
|
869
|
+
// Discovery and caller policy are independent: keep nested declarations for active tools.
|
|
870
|
+
return projectCodeModeTools(roundTools).filter((tool) => directNames.has(tool.name));
|
|
871
|
+
})();
|
|
796
872
|
const systemPrompt = mode === 'compact'
|
|
797
873
|
? buildExecutionSystemPrompt(roundTools, 'run')
|
|
798
874
|
: buildExecutionSystemPrompt(roundTools);
|
|
@@ -804,7 +880,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
804
880
|
: normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
|
|
805
881
|
supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
|
|
806
882
|
});
|
|
883
|
+
const roundMcpExpectations = new Map(roundTools
|
|
884
|
+
.filter((tool) => tool.name.startsWith('mcp__'))
|
|
885
|
+
.map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
|
|
807
886
|
let sawModelSemanticEvent = false;
|
|
887
|
+
let requestStartedAt;
|
|
888
|
+
let firstVisibleOutputAt;
|
|
889
|
+
let requestCompletedAt;
|
|
890
|
+
let requestOutputTokens;
|
|
808
891
|
const roundMessageCount = messages.length;
|
|
809
892
|
const persistToolCalls = async (calls) => {
|
|
810
893
|
for (const call of calls) {
|
|
@@ -834,11 +917,17 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
834
917
|
const startModel = manualCompaction
|
|
835
918
|
? (request) => manualCompaction.start(selected, request)
|
|
836
919
|
: (request) => selected.start(request, controller.signal);
|
|
920
|
+
requestStartedAt = performance.now();
|
|
837
921
|
for await (const event of startModel({
|
|
838
922
|
retryState: modelRetryState,
|
|
839
923
|
system: systemPrompt,
|
|
840
|
-
messages: modelMessages
|
|
841
|
-
|
|
924
|
+
messages: modelMessages.map((message) => message.toolSearchTools
|
|
925
|
+
? {
|
|
926
|
+
...message,
|
|
927
|
+
toolSearchTools: message.toolSearchTools.filter((tool) => allowsToolCaller(tool, 'direct'))
|
|
928
|
+
}
|
|
929
|
+
: message),
|
|
930
|
+
tools: requestTools,
|
|
842
931
|
...(mode === 'compact'
|
|
843
932
|
? { toolChoice: 'none' }
|
|
844
933
|
: toolsAllowed
|
|
@@ -884,13 +973,15 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
884
973
|
}
|
|
885
974
|
delta = prefix.push(delta);
|
|
886
975
|
}
|
|
887
|
-
if (mode !== 'compact' && delta)
|
|
976
|
+
if (mode !== 'compact' && delta) {
|
|
977
|
+
firstVisibleOutputAt ??= performance.now();
|
|
888
978
|
await emit({
|
|
889
979
|
type: 'message.delta',
|
|
890
980
|
itemId: event.itemId,
|
|
891
981
|
phase: event.phase,
|
|
892
982
|
delta
|
|
893
983
|
});
|
|
984
|
+
}
|
|
894
985
|
}
|
|
895
986
|
else if (event.type === 'text.completed') {
|
|
896
987
|
const phase = event.phase ?? 'final_answer';
|
|
@@ -898,13 +989,15 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
898
989
|
finalAnswer = event.text;
|
|
899
990
|
planOutputPrefixes.delete(event.itemId);
|
|
900
991
|
if (mode !== 'compact') {
|
|
992
|
+
if (event.text.length > 0)
|
|
993
|
+
firstVisibleOutputAt ??= performance.now();
|
|
901
994
|
if (mode === 'plan' && sessionSource.type !== 'subagent') {
|
|
902
995
|
const parts = splitPlanOutput(event.text);
|
|
903
996
|
for (const [index, part] of parts.entries()) {
|
|
904
997
|
const itemId = index === 0 ? event.itemId : `${event.itemId}:plan-part:${index}`;
|
|
905
998
|
if (part.kind === 'plan')
|
|
906
999
|
await emit({ type: 'plan.completed', itemId, text: part.text });
|
|
907
|
-
else
|
|
1000
|
+
else {
|
|
908
1001
|
await emit({
|
|
909
1002
|
type: 'message.completed',
|
|
910
1003
|
itemId,
|
|
@@ -913,9 +1006,10 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
913
1006
|
modelId: selectedModel.id,
|
|
914
1007
|
modelName: selectedModel.name
|
|
915
1008
|
});
|
|
1009
|
+
}
|
|
916
1010
|
}
|
|
917
1011
|
}
|
|
918
|
-
else
|
|
1012
|
+
else {
|
|
919
1013
|
await emit({
|
|
920
1014
|
type: 'message.completed',
|
|
921
1015
|
itemId: event.itemId,
|
|
@@ -924,11 +1018,18 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
924
1018
|
modelId: selectedModel.id,
|
|
925
1019
|
modelName: selectedModel.name
|
|
926
1020
|
});
|
|
1021
|
+
}
|
|
927
1022
|
}
|
|
928
1023
|
messages.push({ role: 'assistant', content: event.text });
|
|
929
1024
|
}
|
|
930
1025
|
else if (event.type === 'usage') {
|
|
931
1026
|
latestUsage = event.usage;
|
|
1027
|
+
requestOutputTokens =
|
|
1028
|
+
typeof event.usage.outputTokens === 'number' &&
|
|
1029
|
+
Number.isSafeInteger(event.usage.outputTokens) &&
|
|
1030
|
+
event.usage.outputTokens >= 0
|
|
1031
|
+
? event.usage.outputTokens
|
|
1032
|
+
: undefined;
|
|
932
1033
|
const reportedContext = event.usage.contextInputTokens ?? event.usage.inputTokens;
|
|
933
1034
|
if (reportedContext !== undefined &&
|
|
934
1035
|
Number.isSafeInteger(reportedContext) &&
|
|
@@ -974,8 +1075,26 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
974
1075
|
sawTool = true;
|
|
975
1076
|
pendingTools.push(event);
|
|
976
1077
|
}
|
|
977
|
-
else if (event.type === 'completed')
|
|
1078
|
+
else if (event.type === 'completed') {
|
|
978
1079
|
stop = event.reason;
|
|
1080
|
+
requestCompletedAt = performance.now();
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
if (mode !== 'compact' && firstVisibleOutputAt !== undefined) {
|
|
1084
|
+
const completedAt = requestCompletedAt ?? performance.now();
|
|
1085
|
+
const ttftMs = Math.max(0, Math.round(firstVisibleOutputAt - requestStartedAt));
|
|
1086
|
+
const outputDurationMs = Math.max(0, Math.round(completedAt - firstVisibleOutputAt));
|
|
1087
|
+
const outputTokens = requestOutputTokens;
|
|
1088
|
+
const tokensPerSecond = outputTokens !== undefined && outputDurationMs > 0
|
|
1089
|
+
? Number(((outputTokens * 1000) / outputDurationMs).toFixed(2))
|
|
1090
|
+
: undefined;
|
|
1091
|
+
await emit({
|
|
1092
|
+
type: 'model.performance',
|
|
1093
|
+
ttftMs,
|
|
1094
|
+
...(outputTokens === undefined ? {} : { outputTokens }),
|
|
1095
|
+
outputDurationMs,
|
|
1096
|
+
...(tokensPerSecond === undefined ? {} : { tokensPerSecond })
|
|
1097
|
+
});
|
|
979
1098
|
}
|
|
980
1099
|
pendingOutputContinuation = undefined;
|
|
981
1100
|
incompleteToolCall = pendingTools.some((call) => call.argumentError !== undefined);
|
|
@@ -1031,7 +1150,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1031
1150
|
finalAnswer = '';
|
|
1032
1151
|
continue;
|
|
1033
1152
|
}
|
|
1034
|
-
const executeCall = async (call, signal = controller.signal) => {
|
|
1153
|
+
const executeCall = async (call, signal = controller.signal, caller = 'direct') => {
|
|
1035
1154
|
await emit({
|
|
1036
1155
|
type: 'tool.started',
|
|
1037
1156
|
toolName: call.name,
|
|
@@ -1041,11 +1160,20 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1041
1160
|
try {
|
|
1042
1161
|
if (call.argumentError)
|
|
1043
1162
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', call.argumentError);
|
|
1044
|
-
if (!
|
|
1163
|
+
if (!roundTools.some((tool) => tool.name === call.name && allowsToolCaller(tool, caller)))
|
|
1045
1164
|
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool is unavailable in this execution.');
|
|
1046
1165
|
const output = await executeRegisteredTool(call.name, call.arguments, call.callId, {
|
|
1047
1166
|
host: options.host,
|
|
1048
1167
|
todo,
|
|
1168
|
+
...(roundMcpExpectations.get(call.name)
|
|
1169
|
+
? { expectedMcpTool: roundMcpExpectations.get(call.name) }
|
|
1170
|
+
: {}),
|
|
1171
|
+
searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
|
|
1172
|
+
signal: searchSignal,
|
|
1173
|
+
base: baseTools(),
|
|
1174
|
+
window: selectedModel.contextWindowTokens,
|
|
1175
|
+
system: systemPrompt
|
|
1176
|
+
}),
|
|
1049
1177
|
...(subagents === undefined ? {} : { subagents }),
|
|
1050
1178
|
mode,
|
|
1051
1179
|
sessionId,
|
|
@@ -1053,14 +1181,22 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1053
1181
|
signal,
|
|
1054
1182
|
codeMode: {
|
|
1055
1183
|
supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
|
|
1056
|
-
tools: roundTools,
|
|
1184
|
+
tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
|
|
1185
|
+
state: codeModeState,
|
|
1186
|
+
commitState: async (mutations) => {
|
|
1187
|
+
codeModeState = await store.commitCodeModeState(sessionId, mutations, {
|
|
1188
|
+
executionId,
|
|
1189
|
+
turnId
|
|
1190
|
+
});
|
|
1191
|
+
return codeModeState;
|
|
1192
|
+
},
|
|
1057
1193
|
invoke: async (nested, nestedSignal) => {
|
|
1058
1194
|
const result = await executeCall({
|
|
1059
1195
|
type: 'client_tool.call',
|
|
1060
1196
|
name: nested.name,
|
|
1061
1197
|
arguments: nested.arguments,
|
|
1062
1198
|
callId: nested.callId ?? randomUUID()
|
|
1063
|
-
}, nestedSignal);
|
|
1199
|
+
}, nestedSignal, 'programmatic');
|
|
1064
1200
|
if (result.failed)
|
|
1065
1201
|
throw result.error;
|
|
1066
1202
|
return { ...result.output, content: result.content };
|
|
@@ -1127,10 +1263,14 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1127
1263
|
mimeType: image.mimeType,
|
|
1128
1264
|
data: Buffer.from(image.dataBase64, 'base64')
|
|
1129
1265
|
})));
|
|
1266
|
+
const toolSearchTools = result.call.name === 'tool_search' && !result.failed
|
|
1267
|
+
? result.output.toolSearchTools
|
|
1268
|
+
: undefined;
|
|
1130
1269
|
const message = {
|
|
1131
1270
|
role: 'tool',
|
|
1132
1271
|
callId: result.call.callId,
|
|
1133
1272
|
content: result.content,
|
|
1273
|
+
...(toolSearchTools === undefined ? {} : { toolSearchTools }),
|
|
1134
1274
|
...('images' in result && result.images?.length ? { images: result.images } : {})
|
|
1135
1275
|
};
|
|
1136
1276
|
messages.push(message);
|
|
@@ -1139,7 +1279,10 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1139
1279
|
payload: {
|
|
1140
1280
|
role: message.role,
|
|
1141
1281
|
callId: message.callId,
|
|
1142
|
-
content: message.content
|
|
1282
|
+
content: message.content,
|
|
1283
|
+
...(message.toolSearchTools === undefined
|
|
1284
|
+
? {}
|
|
1285
|
+
: { toolSearchTools: message.toolSearchTools })
|
|
1143
1286
|
},
|
|
1144
1287
|
executionId,
|
|
1145
1288
|
turnId
|
|
@@ -1182,6 +1325,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1182
1325
|
contextWindowTokens: selectedModel.contextWindowTokens,
|
|
1183
1326
|
usage: latestUsage,
|
|
1184
1327
|
modelProtocol: selectedModel.protocol,
|
|
1328
|
+
responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
|
|
1185
1329
|
workspace: logicalWorkspace,
|
|
1186
1330
|
platform: description.platform,
|
|
1187
1331
|
getActiveProcessIds: async () => {
|
|
@@ -1201,6 +1345,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1201
1345
|
projection: committed.projection
|
|
1202
1346
|
});
|
|
1203
1347
|
messages.splice(0, messages.length, ...refreshed.messages);
|
|
1348
|
+
restoreMcpDiscovery();
|
|
1204
1349
|
projectedExecutionBudgetStage = undefined;
|
|
1205
1350
|
recordedEnvironment = refreshed.environmentState;
|
|
1206
1351
|
compactionUserMessages = [...refreshed.userMessages];
|
|
@@ -1986,7 +2131,7 @@ function parsePersistedContextualWorldState(payload) {
|
|
|
1986
2131
|
const entry = value;
|
|
1987
2132
|
if (!Number.isSafeInteger(entry.index) ||
|
|
1988
2133
|
entry.index < 0 ||
|
|
1989
|
-
(entry.kind !== 'instructions' && entry.kind !== 'resources') ||
|
|
2134
|
+
(entry.kind !== 'instructions' && entry.kind !== 'resources' && entry.kind !== 'mcp') ||
|
|
1990
2135
|
typeof entry.content !== 'string' ||
|
|
1991
2136
|
entry.content.length === 0)
|
|
1992
2137
|
return undefined;
|
|
@@ -2004,6 +2149,7 @@ function parsePersistedContextualWorldState(payload) {
|
|
|
2004
2149
|
initialized: true,
|
|
2005
2150
|
instructionSnapshot,
|
|
2006
2151
|
resourceSnapshot,
|
|
2152
|
+
mcpSnapshot: typeof record.mcpSnapshot === 'string' ? record.mcpSnapshot : undefined,
|
|
2007
2153
|
entries
|
|
2008
2154
|
};
|
|
2009
2155
|
}
|
|
@@ -2021,6 +2167,7 @@ async function persistContextualWorldState(store, sessionId, state) {
|
|
|
2021
2167
|
generation: state.generation ?? null,
|
|
2022
2168
|
instructionSnapshot: state.instructionSnapshot ?? null,
|
|
2023
2169
|
resourceSnapshot: state.resourceSnapshot ?? null,
|
|
2170
|
+
mcpSnapshot: state.mcpSnapshot ?? null,
|
|
2024
2171
|
entries: state.entries
|
|
2025
2172
|
});
|
|
2026
2173
|
}
|
|
@@ -2038,6 +2185,7 @@ function updateContextualWorldState(state, index, current) {
|
|
|
2038
2185
|
entries.push({ index, kind: 'resources', content: resourceContent });
|
|
2039
2186
|
return {
|
|
2040
2187
|
generation: state.generation,
|
|
2188
|
+
mcpSnapshot: state.mcpSnapshot,
|
|
2041
2189
|
initialized: true,
|
|
2042
2190
|
instructionSnapshot: current.instructionSnapshot,
|
|
2043
2191
|
resourceSnapshot: current.resourceSnapshot,
|
|
@@ -2100,6 +2248,38 @@ function stableJson(value) {
|
|
|
2100
2248
|
.join(',')}}`;
|
|
2101
2249
|
return JSON.stringify(value) ?? 'undefined';
|
|
2102
2250
|
}
|
|
2251
|
+
function discoveredMcpToolNames(messages) {
|
|
2252
|
+
const calls = toolSearchCallIds(messages);
|
|
2253
|
+
const names = new Set();
|
|
2254
|
+
for (const message of messages)
|
|
2255
|
+
for (const tool of calls.has(message.callId ?? '') ? (message.toolSearchTools ?? []) : [])
|
|
2256
|
+
if (tool.name.startsWith('mcp__'))
|
|
2257
|
+
names.add(tool.name);
|
|
2258
|
+
return [...names];
|
|
2259
|
+
}
|
|
2260
|
+
function rebindMcpToolSearchOutputs(messages, registry) {
|
|
2261
|
+
const calls = toolSearchCallIds(messages);
|
|
2262
|
+
for (const message of messages) {
|
|
2263
|
+
if (!message.toolSearchTools)
|
|
2264
|
+
continue;
|
|
2265
|
+
if (!calls.has(message.callId ?? '')) {
|
|
2266
|
+
delete message.toolSearchTools;
|
|
2267
|
+
continue;
|
|
2268
|
+
}
|
|
2269
|
+
const definitions = registry.restoredDefinitions(message.toolSearchTools.map((tool) => tool.name));
|
|
2270
|
+
message.toolSearchTools = definitions;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
function toolSearchCallIds(messages) {
|
|
2274
|
+
return new Set(messages
|
|
2275
|
+
.filter((message) => (message.role === 'tool_call' &&
|
|
2276
|
+
message.name === 'tool_search' &&
|
|
2277
|
+
typeof message.callId === 'string') ||
|
|
2278
|
+
(message.role === 'provider' &&
|
|
2279
|
+
providerToolName(message) === 'tool_search' &&
|
|
2280
|
+
providerToolCallId(message) !== undefined))
|
|
2281
|
+
.map((message) => message.role === 'provider' ? providerToolCallId(message) : message.callId));
|
|
2282
|
+
}
|
|
2103
2283
|
/** Project only known, validated tool result shapes into public structured events. */
|
|
2104
2284
|
function structuredToolEventData(toolName, value) {
|
|
2105
2285
|
if (!isRecord(value))
|
|
@@ -2196,7 +2376,10 @@ function structuredToolArtifacts(value) {
|
|
|
2196
2376
|
}
|
|
2197
2377
|
/** Keep streaming deltas live-only; exec output has one durable copy in model context records. */
|
|
2198
2378
|
function eventForPersistence(event) {
|
|
2199
|
-
if (event.type === 'message.delta' ||
|
|
2379
|
+
if (event.type === 'message.delta' ||
|
|
2380
|
+
event.type === 'context.compacting' ||
|
|
2381
|
+
event.type === 'model.performance' ||
|
|
2382
|
+
event.type === 'message.performance')
|
|
2200
2383
|
return undefined;
|
|
2201
2384
|
if (event.type !== 'tool.completed' || event.toolName !== 'exec' || !event.data)
|
|
2202
2385
|
return event;
|
|
@@ -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;
|