Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,17 @@ call.
A **Resume Projection** is the durable manifest view produced from an
Execution Record. A projection is scoped to one exact fork and resume attempt;
an unscoped projection fails closed when a parent tool-call ID is ambiguous.

## Tool Caller Capabilities

A **Caller Capability Projection** is the effective classification of tool
definitions by the contexts permitted by `allowed_callers`: direct model calls,
programmatic code execution, both, or neither. Prompt guidance, model binding,
and runtime dispatch derive from this projection rather than recomputing caller
rules independently.

A **Programmatic Tool Manifest** is the exact list of registered tool names a
programmatic invocation declares that its code depends on. Mixed direct and
code-execution configurations require the manifest so caller policy can reject
direct-only dependencies before starting an execution runtime without parsing
the submitted programming language.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@librechat/agents",
"version": "3.6.9",
"version": "3.6.10",
"reova": {
"enabled": true,
"endpoint": "https://telemetry.reo.dev/data"
Expand Down
176 changes: 109 additions & 67 deletions src/agents/AgentContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ import {
Constants,
Providers,
} from '@/common';
import {
isProgrammaticRunnerAutoBound,
resolveLocalToolRegistry,
} from '@/tools/local/resolveLocalExecutionTools';
import {
allowsToolCaller,
isToolDefinitionActive,
isProgrammaticControlTool,
resolveCallerCapabilityProjection,
} from '@/tools/CallerCapabilities';
import { createSchemaOnlyTools } from '@/tools/schema';
import { apportionTokenCounts } from '@/utils/tokens';
import { isThinkingEnabled } from '@/llm/request';
Expand Down Expand Up @@ -59,7 +69,8 @@ export class AgentContext {
static fromConfig(
agentConfig: t.AgentInputs,
tokenCounter?: t.TokenCounter,
indexTokenCountMap?: Record<string, number>
indexTokenCountMap?: Record<string, number>,
toolExecution?: t.ToolExecutionConfig
): AgentContext {
const {
agentId,
Expand Down Expand Up @@ -103,6 +114,7 @@ export class AgentContext {
tools,
toolMap,
toolRegistry,
toolExecution,
toolDefinitions,
instructions,
additionalInstructions: additional_instructions,
Expand Down Expand Up @@ -269,6 +281,8 @@ export class AgentContext {
* Used for tool search and programmatic tool calling.
*/
toolRegistry?: t.LCToolRegistry;
/** Run-scoped backend used to identify auto-bound programmatic runners. */
private toolExecution?: t.ToolExecutionConfig;
/**
* Serializable tool definitions for event-driven execution.
* When provided, ToolNode operates in event-driven mode.
Expand Down Expand Up @@ -385,6 +399,7 @@ export class AgentContext {
tools,
toolMap,
toolRegistry,
toolExecution,
toolDefinitions,
instructions,
additionalInstructions,
Expand All @@ -410,6 +425,7 @@ export class AgentContext {
tools?: t.GraphTools;
toolMap?: t.ToolMap;
toolRegistry?: t.LCToolRegistry;
toolExecution?: t.ToolExecutionConfig;
toolDefinitions?: t.LCTool[];
instructions?: string;
additionalInstructions?: string;
Expand All @@ -434,7 +450,11 @@ export class AgentContext {
this.tokenCounter = tokenCounter;
this.tools = tools;
this.toolMap = toolMap;
this.toolRegistry = toolRegistry;
this.toolRegistry = resolveLocalToolRegistry({
toolRegistry,
toolExecution,
});
this.toolExecution = toolExecution;
this.toolDefinitions = toolDefinitions;
this.instructions = instructions;
this.additionalInstructions = additionalInstructions;
Expand All @@ -461,37 +481,46 @@ export class AgentContext {
}
}

/**
* Builds instructions text for tools that are ONLY callable via programmatic code execution.
* These tools cannot be called directly by the LLM but are available through the
* configured programmatic tool.
*
* Includes:
* - Code_execution-only tools that are NOT deferred
* - Code_execution-only tools that ARE deferred but have been discovered via tool search
*/
/** Builds the caller boundary and schemas for programmatic-only tools. */
private buildProgrammaticOnlyToolsInstructions(): string {
if (!this.toolRegistry) return '';

const programmaticOnlyTools: t.LCTool[] = [];
for (const [name, toolDef] of this.toolRegistry) {
const allowedCallers = toolDef.allowed_callers ?? ['direct'];
const isCodeExecutionOnly =
allowedCallers.includes('code_execution') &&
!allowedCallers.includes('direct');

if (!isCodeExecutionOnly) continue;

const isDeferred = toolDef.defer_loading === true;
const isDiscovered = this.discoveredToolNames.has(name);
if (!isDeferred || isDiscovered) {
programmaticOnlyTools.push(toolDef);
}
const capabilities = resolveCallerCapabilityProjection(
this.toolRegistry.values(),
(toolDef) => isToolDefinitionActive(toolDef, this.discoveredToolNames)
);
const programmaticOnlyTools = capabilities.codeExecutionOnlyTools;
const programmaticToolNames = capabilities.codeExecutionTools.map(
(toolDef) => toolDef.name
);
const directOnlyToolNames = capabilities.directOnlyTools
.map((toolDef) => toolDef.name)
.filter((name) => !isProgrammaticControlTool(name));

const programmaticTools = this.getProgrammaticToolInstructionTargets();
if (programmaticTools.length === 0) return '';
const programmaticRunnerNames = programmaticTools
.map((tool) => `\`${tool.name}\``)
.join(' or ');
const quotedProgrammaticNames =
programmaticToolNames.length > 0
? programmaticToolNames.map((name) => `\`${name}\``).join(', ')
: 'none';
const directOnlyBoundary =
directOnlyToolNames.length > 0
? `\nCall these tools directly; never list them in the \`tool_manifest\` or reference them inside ${programmaticRunnerNames}: ${directOnlyToolNames
.map((name) => `\`${name}\``)
.join(', ')}. Every ${programmaticRunnerNames} call must include a \`tool_manifest\` containing the exact registered names used by its code; the manifest is validated before execution starts.`
: '';
const boundary =
'\n\n## Programmatic Tool Calling\n\n' +
`Only these tools may be invoked inside ${programmaticRunnerNames}: ${quotedProgrammaticNames}.` +
directOnlyBoundary;

if (programmaticOnlyTools.length === 0) {
return boundary;
}

if (programmaticOnlyTools.length === 0) return '';

const programmaticTool = this.getProgrammaticToolInstructionTarget();
const toolDescriptions = programmaticOnlyTools
.map((tool) => {
let desc = `- **${tool.name}**`;
Expand All @@ -506,41 +535,64 @@ export class AgentContext {
.join('\n\n');

return (
'\n\n## Programmatic-Only Tools\n\n' +
`The following tools are available exclusively through the \`${programmaticTool.name}\` tool. ` +
`You cannot call these tools directly; instead, use \`${programmaticTool.name}\` with ${programmaticTool.language} code that invokes them.\n\n` +
boundary +
'\n\n### Programmatic-Only Tools\n\n' +
`The following tools are available exclusively through ${programmaticRunnerNames}. ` +
`You cannot call these tools directly; instead, ${programmaticTools
.map(
(tool) =>
`use \`${tool.name}\` with ${tool.codeGuidance} that invokes them`
)
.join(', or ')}.\n\n` +
toolDescriptions
);
}

private getProgrammaticToolInstructionTarget(): {
private getProgrammaticToolInstructionTargets(): Array<{
name: string;
language: 'bash' | 'Python';
} {
if (this.hasAvailableTool(Constants.BASH_PROGRAMMATIC_TOOL_CALLING)) {
return {
codeGuidance: string;
}> {
const targets: Array<{ name: string; codeGuidance: string }> = [];
if (
this.hasBoundTool(Constants.BASH_PROGRAMMATIC_TOOL_CALLING) ||
isProgrammaticRunnerAutoBound(
Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
this.toolExecution
)
) {
targets.push({
name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
language: 'bash',
};
codeGuidance: 'Bash code',
});
}

if (this.hasAvailableTool(Constants.PROGRAMMATIC_TOOL_CALLING)) {
return { name: Constants.PROGRAMMATIC_TOOL_CALLING, language: 'Python' };
if (
this.hasBoundTool(Constants.PROGRAMMATIC_TOOL_CALLING) ||
isProgrammaticRunnerAutoBound(
Constants.PROGRAMMATIC_TOOL_CALLING,
this.toolExecution
)
) {
const localDefault =
this.toolExecution?.engine === 'local' ||
this.toolExecution?.engine === 'cloudflare-sandbox';
targets.push({
name: Constants.PROGRAMMATIC_TOOL_CALLING,
codeGuidance: localDefault
? 'Bash code by default, or set `lang: "py"` to use Python code'
: 'Python code',
});
}

return { name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING, language: 'bash' };
return targets;
}

private hasAvailableTool(name: string): boolean {
if (this.toolDefinitions?.some((tool) => tool.name === name) === true)
return true;
if (
this.tools?.some((tool) => 'name' in tool && tool.name === name) === true
) {
return true;
}
if (this.toolMap?.has(name) === true) return true;
return this.toolRegistry?.has(name) === true;
private hasBoundTool(name: string): boolean {
return (
this.getToolsForBinding()?.some(
(tool) => 'name' in tool && tool.name === name
) === true
);
}

/**
Expand Down Expand Up @@ -1083,15 +1135,10 @@ export class AgentContext {
* alone left programmatic-only definitions counted in
* `toolSchemaTokens` even though they were never bound.
*/
return this.toolDefinitions.filter((def) => {
const allowedCallers = def.allowed_callers ?? ['direct'];
if (!allowedCallers.includes('direct')) {
return false;
}
return (
def.defer_loading !== true || this.discoveredToolNames.has(def.name)
);
});
return resolveCallerCapabilityProjection(
this.toolDefinitions,
(toolDef) => isToolDefinitionActive(toolDef, this.discoveredToolNames)
).directTools;
}

/**
Expand Down Expand Up @@ -1807,14 +1854,9 @@ export class AgentContext {
return true;
}

if (this.discoveredToolNames.has(tool.name)) {
const allowedCallers = toolDef.allowed_callers ?? ['direct'];
return allowedCallers.includes('direct');
}

const allowedCallers = toolDef.allowed_callers ?? ['direct'];
return (
allowedCallers.includes('direct') && toolDef.defer_loading !== true
allowsToolCaller(toolDef, 'direct') &&
isToolDefinitionActive(toolDef, this.discoveredToolNames)
);
});
}
Expand Down
Loading
Loading