Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions src/agents/AgentContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class AgentContext {
): AgentContext {
const {
agentId,
codeSessionKey,
name,
provider,
clientOptions,
Expand Down Expand Up @@ -92,6 +93,7 @@ export class AgentContext {

const agentContext = new AgentContext({
agentId,
codeSessionKey,
name: name ?? agentId,
provider,
clientOptions,
Expand Down Expand Up @@ -167,6 +169,8 @@ export class AgentContext {

/** Agent identifier */
agentId: string;
/** Partition for this agent's transient code session and file refs. */
codeSessionKey?: string;
/** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
name?: string;
/** Provider for this specific agent */
Expand Down Expand Up @@ -370,6 +374,7 @@ export class AgentContext {

constructor({
agentId,
codeSessionKey,
name,
provider,
clientOptions,
Expand All @@ -394,6 +399,7 @@ export class AgentContext {
maxToolResultChars,
}: {
agentId: string;
codeSessionKey?: string;
name?: string;
provider: Providers;
clientOptions?: t.ClientOptions;
Expand All @@ -418,6 +424,7 @@ export class AgentContext {
maxToolResultChars?: number;
}) {
this.agentId = agentId;
this.codeSessionKey = codeSessionKey;
this.name = name;
this.provider = provider;
this.clientOptions = clientOptions;
Expand Down
2 changes: 2 additions & 0 deletions src/graphs/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2391,6 +2391,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
agentLangfuse: agentContext?.langfuse,
eventDrivenMode: true,
sessions: this.sessions,
codeSessionKey: agentContext?.codeSessionKey,
Comment thread
danny-avila marked this conversation as resolved.
toolDefinitions: toolDefMap,
// `agentId` is the subagent-scope marker — set ONLY for child-run
// graphs (hooks fire for child scopes too, via the inherited
Expand Down Expand Up @@ -2471,6 +2472,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
StandardGraph.handleToolCallErrorStatic(this, data, metadata),
toolRegistry: agentContext?.toolRegistry,
sessions: this.sessions,
codeSessionKey: agentContext?.codeSessionKey,
toolExecution: this.toolExecution,
codeSessionToolNames: this.codeSessionToolNames,
interruptingToolNames: effectiveInterruptingToolNames,
Expand Down
15 changes: 10 additions & 5 deletions src/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ function toCodeEnvFile(file: t.FileRef, execSessionId: string): t.CodeEnvFile {

function getCodeSessionContext(
graph: StandardGraph,
name: string
name: string,
agentContext?: AgentContext
): t.ToolCallRequest['codeSessionContext'] | undefined {
if (
!CODE_EXECUTION_TOOLS.has(name) &&
Expand All @@ -269,9 +270,9 @@ function getCodeSessionContext(
return undefined;
}

const codeSession = graph.sessions.get(Constants.EXECUTE_CODE) as
| t.CodeSessionContext
| undefined;
const codeSession = graph.sessions.get(
agentContext?.codeSessionKey ?? Constants.EXECUTE_CODE
) as t.CodeSessionContext | undefined;
if (codeSession?.session_id == null || codeSession.session_id === '') {
return undefined;
}
Expand Down Expand Up @@ -748,7 +749,11 @@ function createEagerToolExecutionPlan(args: {
name: toolCall.name,
args: toolCall.args,
stepId: graph.toolCallStepIds.get(toolCall.id!) ?? '',
codeSessionContext: getCodeSessionContext(graph, toolCall.name),
codeSessionContext: getCodeSessionContext(
graph,
toolCall.name,
agentContext
Comment thread
danny-avila marked this conversation as resolved.
),
})),
usageCount: graph.getEagerEventToolUsageCount(agentContext?.agentId),
});
Expand Down
37 changes: 24 additions & 13 deletions src/tools/BashExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
appendFailedExecutionFileReminder,
appendTmpScratchReminder,
appendCodeSessionFileSummary,
addCodeApiExecutionProfileHeader,
emptyOutputMessage,
buildCodeApiHttpErrorMessage,
CodeApiRequestError,
Expand All @@ -21,9 +22,6 @@ import { Constants } from '@/common';

config();

const baseEndpoint = getCodeBaseURL();
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;

export const BashExecutionToolSchema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -177,15 +175,21 @@ export const BashExecutionToolDefinition = {
function createBashExecutionTool(
params: t.BashExecutionToolParams | null = {}
): DynamicStructuredTool {
const execEndpoint = `${params?.baseUrl ?? getCodeBaseURL()}/exec`;

return tool(
async (rawInput, config) => {
/* `statefulSessions` is prompt-only — keep it out of the wire body. */
/* `statefulSessions` drives the prompt and gates runtime affinity hints;
* keep the flag itself out of the wire body. */
const {
authHeaders,
statefulSessions: _statefulSessions,
baseUrl: _baseUrl,
executionProfile,
runtimeSessionHint,
statefulSessions,
...executionParams
} = params ?? {};
void _statefulSessions;
void _baseUrl;
/* Drop any model-supplied `runtime_session_hint` from the raw args: the
* hint must only come from ToolNode's injected `_runtime_session_hint`
* (below), never from the tool call itself. */
Expand Down Expand Up @@ -217,11 +221,15 @@ function createBashExecutionTool(
...executionParams,
};

const effectiveRuntimeSessionHint =
runtimeSessionHint ?? _runtime_session_hint;
if (
typeof _runtime_session_hint === 'string' &&
_runtime_session_hint !== ''
statefulSessions === true &&
executionProfile !== 'default' &&
typeof effectiveRuntimeSessionHint === 'string' &&
effectiveRuntimeSessionHint !== ''
) {
postData.runtime_session_hint = _runtime_session_hint;
postData.runtime_session_hint = effectiveRuntimeSessionHint;
}

/* See `CodeExecutor.ts` for the rationale — `/files/<session_id>`
Expand All @@ -248,19 +256,22 @@ function createBashExecutionTool(
headers: {
'Content-Type': 'application/json',
'User-Agent': 'LibreChat/1.0',
...resolvedAuthHeaders,
...addCodeApiExecutionProfileHeader(
resolvedAuthHeaders,
executionProfile
),
},
body: JSON.stringify(postData),
};

const proxyAgent = resolveFetchProxyAgent(EXEC_ENDPOINT);
const proxyAgent = resolveFetchProxyAgent(execEndpoint);
if (proxyAgent) {
fetchOptions.agent = proxyAgent;
}
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
const response = await fetch(execEndpoint, fetchOptions);
if (!response.ok) {
throw new CodeApiRequestError(
await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)
await buildCodeApiHttpErrorMessage('POST', execEndpoint, response)
);
}

Expand Down
22 changes: 14 additions & 8 deletions src/tools/BashProgrammaticToolCalling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,17 @@ export function createBashProgrammaticToolCallingTool(
);
}

/* Stateful sessions: hint on the INITIAL request only (continuations
* bind via continuation_token). Wire-only in v1 — BashPTC keeps its
* stateless prompt. */
/* The hint rides the INITIAL request only; continuation_token binds
* later round-trips. Prefer trusted per-agent factory context over
* legacy ToolNode injection. Explicit default profiles always drop it.
* BashPTC keeps its stateless runtime prompt in v1. */
const selectedRuntimeSessionHint =
initParams.runtimeSessionHint ?? _runtime_session_hint;
const runtimeSessionHint =
typeof _runtime_session_hint === 'string' &&
_runtime_session_hint !== ''
? _runtime_session_hint
initParams.executionProfile !== 'default' &&
typeof selectedRuntimeSessionHint === 'string' &&
selectedRuntimeSessionHint !== ''
? selectedRuntimeSessionHint
: undefined;

let response = await makeRequest(
Expand All @@ -385,7 +389,8 @@ export function createBashProgrammaticToolCallingTool(
: {}),
},
proxy,
initParams.authHeaders
initParams.authHeaders,
initParams.executionProfile
);

// ====================================================================
Expand Down Expand Up @@ -425,7 +430,8 @@ export function createBashProgrammaticToolCallingTool(
tool_results: toolResults,
},
proxy,
initParams.authHeaders
initParams.authHeaders,
initParams.executionProfile
);
}

Expand Down
57 changes: 41 additions & 16 deletions src/tools/CodeExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export const CodeExecutionToolSchema = {
required: ['lang', 'code'],
} as const;

const baseEndpoint = getCodeBaseURL();
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
export const CODE_API_EXPECTED_PROFILE_HEADER =
'X-CodeAPI-Expected-Profile';

type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];

Expand Down Expand Up @@ -226,6 +226,19 @@ export async function resolveCodeApiAuthHeaders(
return authHeaders;
}

export function addCodeApiExecutionProfileHeader(
headers: t.CodeApiAuthHeaderMap,
executionProfile?: t.CodeApiExecutionProfile
): t.CodeApiAuthHeaderMap {
if (executionProfile == null) {
return headers;
}
return {
...headers,
[CODE_API_EXPECTED_PROFILE_HEADER]: executionProfile,
};
}

export async function buildCodeApiHttpErrorMessage(
_method: string,
_endpoint: string,
Expand Down Expand Up @@ -333,16 +346,21 @@ export const CodeExecutionToolDefinition = {
function createCodeExecutionTool(
params: t.CodeExecutionToolParams | null = {}
): DynamicStructuredTool {
const execEndpoint = `${params?.baseUrl ?? getCodeBaseURL()}/exec`;
Comment thread
danny-avila marked this conversation as resolved.
Outdated

return tool(
async (rawInput, config) => {
/* `statefulSessions` is a prompt-only flag (drives the description);
* keep it out of the wire body. */
/* `statefulSessions` drives the description and gates trusted runtime
* affinity hints. Keep the flag itself out of the wire body. */
const {
authHeaders,
statefulSessions: _statefulSessions,
baseUrl: _baseUrl,
executionProfile,
runtimeSessionHint,
statefulSessions,
...executionParams
} = params ?? {};
void _statefulSessions;
void _baseUrl;
/* Drop any model-supplied `runtime_session_hint` from the raw args: the
* hint is host-controlled and must only ever come from ToolNode's
* injected `_runtime_session_hint` (below). Spreading `...rest` into
Expand Down Expand Up @@ -383,14 +401,18 @@ function createCodeExecutionTool(
...executionParams,
};

/* Stateful sessions: forward the hint so the Code API can route this
* execution to a warm per-session runtime. Additive — stateless
* servers ignore the unknown field. */
/* Stateful sessions: prefer the per-agent factory hint, falling back to
* legacy ToolNode injection. Explicit default-profile tools always drop
* the hint so a mixed graph cannot promote them to the stateful path. */
const effectiveRuntimeSessionHint =
runtimeSessionHint ?? _runtime_session_hint;
Comment thread
danny-avila marked this conversation as resolved.
Outdated
if (
typeof _runtime_session_hint === 'string' &&
_runtime_session_hint !== ''
statefulSessions === true &&
executionProfile !== 'default' &&
typeof effectiveRuntimeSessionHint === 'string' &&
effectiveRuntimeSessionHint !== ''
) {
postData.runtime_session_hint = _runtime_session_hint;
postData.runtime_session_hint = effectiveRuntimeSessionHint;
}

/* File injection: `_injected_files` from ToolNode (set when host
Expand Down Expand Up @@ -421,19 +443,22 @@ function createCodeExecutionTool(
headers: {
'Content-Type': 'application/json',
'User-Agent': 'LibreChat/1.0',
...resolvedAuthHeaders,
...addCodeApiExecutionProfileHeader(
resolvedAuthHeaders,
executionProfile
),
},
body: JSON.stringify(postData),
};

const proxyAgent = resolveFetchProxyAgent(EXEC_ENDPOINT);
const proxyAgent = resolveFetchProxyAgent(execEndpoint);
if (proxyAgent) {
fetchOptions.agent = proxyAgent;
}
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
const response = await fetch(execEndpoint, fetchOptions);
if (!response.ok) {
throw new CodeApiRequestError(
await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)
await buildCodeApiHttpErrorMessage('POST', execEndpoint, response)
);
}

Expand Down
Loading
Loading