mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 06:52:47 +00:00
✨ feat: stateful_code_sessions capability for warm Code API sandbox sessions
Wire the @librechat/agents stateful sandbox sub-config behind a new, off-by-default stateful_code_sessions agent capability. createRun sets toolExecution.sandbox.statefulSessions when code execution is active in the run AND the capability is enabled; execute_code and bash_tool factories get the param so their descriptions hedge toward persistence. Rides the existing variable-not-literal runConfig pattern, so it no-ops until @librechat/agents is bumped to the version shipping the sandbox sub-config.
This commit is contained in:
parent
73c43ded25
commit
19d736811e
5 changed files with 86 additions and 1 deletions
|
|
@ -22,6 +22,7 @@ const {
|
|||
Permissions,
|
||||
EToolResources,
|
||||
PermissionTypes,
|
||||
AgentCapabilities,
|
||||
} = require('librechat-data-provider');
|
||||
const {
|
||||
availableTools,
|
||||
|
|
@ -51,7 +52,7 @@ const { createFileSearchTool, primeFiles: primeSearchFiles } = require('./fileSe
|
|||
const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process');
|
||||
const { getUserPluginAuthValue } = require('~/server/services/PluginService');
|
||||
const { loadAuthValues } = require('~/server/services/Tools/credentials');
|
||||
const { getMCPServerTools } = require('~/server/services/Config');
|
||||
const { getMCPServerTools, checkCapability } = require('~/server/services/Config');
|
||||
const { getMCPServersRegistry } = require('~/config');
|
||||
const { getRoleByName, setMemory, deleteMemory, getFormattedMemories } = require('~/models');
|
||||
|
||||
|
|
@ -301,10 +302,19 @@ const loadTools = async ({
|
|||
if (files?.length) {
|
||||
primedCodeFiles = files;
|
||||
}
|
||||
/* Hedge the execute_code description toward persistence only when the
|
||||
* admin `stateful_code_sessions` capability is on (off by default); the
|
||||
* matching wire hint is set in the run config. Older @librechat/agents
|
||||
* ignore the param. */
|
||||
const statefulSessions = await checkCapability(
|
||||
options.req,
|
||||
AgentCapabilities.stateful_code_sessions,
|
||||
);
|
||||
return createCodeExecutionTool({
|
||||
user_id: user,
|
||||
files,
|
||||
authHeaders: () => getCodeApiAuthHeaders(options.req),
|
||||
statefulSessions,
|
||||
});
|
||||
};
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -1483,6 +1483,16 @@ async function loadToolsForExecution({
|
|||
enabledCapabilities?.has(AgentCapabilities.execute_code) === true &&
|
||||
agent?.tools?.includes(Tools.execute_code) === true;
|
||||
|
||||
/**
|
||||
* Opt bash_tool into the hedged stateful-session description. Gated on code
|
||||
* execution being enabled AND the admin `stateful_code_sessions` capability;
|
||||
* off by default. Sets prompt text only (the wire hint is set at run config).
|
||||
* PTC keeps its stateless prompt in v1. Older @librechat/agents ignore the param.
|
||||
*/
|
||||
const statefulCodeSessions =
|
||||
codeExecutionEnabled &&
|
||||
enabledCapabilities?.has(AgentCapabilities.stateful_code_sessions) === true;
|
||||
|
||||
const isPTC =
|
||||
isPTCRequested &&
|
||||
enabledCapabilities.has(AgentCapabilities.programmatic_tools) &&
|
||||
|
|
@ -1534,6 +1544,7 @@ async function loadToolsForExecution({
|
|||
try {
|
||||
const bashTool = createBashExecutionTool({
|
||||
authHeaders: () => getCodeApiAuthHeaders(req),
|
||||
statefulSessions: statefulCodeSessions,
|
||||
});
|
||||
allLoadedTools.push(bashTool);
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Run, Providers, Constants } from '@librechat/agents';
|
|||
import {
|
||||
KnownEndpoints,
|
||||
EModelEndpoint,
|
||||
AgentCapabilities,
|
||||
MAX_SUBAGENT_DEPTH,
|
||||
MAX_SUBAGENT_RUN_CONFIGS,
|
||||
extractEnvVariable,
|
||||
|
|
@ -778,6 +779,26 @@ function isAskUserQuestionAdminDisabled(appConfig?: AppConfig): boolean {
|
|||
return appConfig?.filteredTools?.includes(ASK_USER_QUESTION_TOOL_NAME) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this run opts its remote sandbox tools into best-effort stateful
|
||||
* runtime sessions (a warm per-session MicroVM workspace on the Code API).
|
||||
* Requires BOTH code execution active in the run (`codeEnvActive`, i.e. some
|
||||
* reachable agent has execute_code/bash) AND the admin `stateful_code_sessions`
|
||||
* capability — a stateful sandbox session is meaningless without code
|
||||
* execution. Off by default: the capability is absent from
|
||||
* `defaultAgentCapabilities`, and the SDK derives the session hint from
|
||||
* `thread_id` (= conversationId), so this is never a trust boundary.
|
||||
*/
|
||||
export function resolveStatefulCodeSessions(
|
||||
codeEnvActive: boolean,
|
||||
agentsEndpointConfig: { capabilities?: AgentCapabilities[] } | undefined,
|
||||
): boolean {
|
||||
if (!codeEnvActive) {
|
||||
return false;
|
||||
}
|
||||
return new Set(agentsEndpointConfig?.capabilities).has(AgentCapabilities.stateful_code_sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any agent reachable in the run — primary, handoff/parallel, or a
|
||||
* nested subagent — opts into cross-turn `reasoning_content` reconstruction.
|
||||
|
|
@ -1282,6 +1303,12 @@ export async function createRun({
|
|||
* and the resume route). When disabled, nothing attaches and the run is identical
|
||||
* to before this feature shipped.
|
||||
*/
|
||||
// `enableToolOutputReferences` (bash present anywhere in the run) doubles as
|
||||
// the "code execution active" signal for the stateful-session gate.
|
||||
const statefulCodeSessions = resolveStatefulCodeSessions(
|
||||
enableToolOutputReferences,
|
||||
agentsEndpointConfig,
|
||||
);
|
||||
// Resolve the effective policy through the single seam so per-agent / per-skill
|
||||
// sources can layer in later without touching this call site (see
|
||||
// `resolveToolApprovalPolicy`). Only the endpoint layer is wired today, so this
|
||||
|
|
@ -1378,6 +1405,15 @@ export async function createRun({
|
|||
...(enableToolOutputReferences && {
|
||||
toolOutputReferences: { enabled: true },
|
||||
}),
|
||||
// Best-effort stateful runtime sessions on the remote Code API. The SDK
|
||||
// stamps a per-conversation session hint on execute_code/bash requests and
|
||||
// hedges those tools' descriptions; the transport is otherwise unchanged.
|
||||
// `engine` is omitted (defaults to `sandbox`) and the hint defaults to
|
||||
// thread_id. Requires @librechat/agents with `toolExecution.sandbox`;
|
||||
// older versions ignore the field.
|
||||
...(statefulCodeSessions && {
|
||||
toolExecution: { sandbox: { statefulSessions: true } },
|
||||
}),
|
||||
// HITL opt-in: the `humanInTheLoop` switch + the PreToolUse policy hook. Spread
|
||||
// here (not just `compileOptions.checkpointer` above) so an `ask` decision raises
|
||||
// a real interrupt — without these the run would never pause. Absent when disabled.
|
||||
|
|
|
|||
27
packages/api/src/agents/statefulCodeSessions.spec.ts
Normal file
27
packages/api/src/agents/statefulCodeSessions.spec.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { AgentCapabilities } from 'librechat-data-provider';
|
||||
import { resolveStatefulCodeSessions } from './run';
|
||||
|
||||
describe('resolveStatefulCodeSessions', () => {
|
||||
const withCap = {
|
||||
capabilities: [AgentCapabilities.execute_code, AgentCapabilities.stateful_code_sessions],
|
||||
};
|
||||
const withoutCap = { capabilities: [AgentCapabilities.execute_code] };
|
||||
|
||||
it('enables only when code execution is active AND the capability is present', () => {
|
||||
expect(resolveStatefulCodeSessions(true, withCap)).toBe(true);
|
||||
});
|
||||
|
||||
it('stays off when the capability is absent, even with code execution active', () => {
|
||||
expect(resolveStatefulCodeSessions(true, withoutCap)).toBe(false);
|
||||
});
|
||||
|
||||
it('stays off when code execution is inactive, even with the capability present', () => {
|
||||
expect(resolveStatefulCodeSessions(false, withCap)).toBe(false);
|
||||
});
|
||||
|
||||
it('stays off (default) when the endpoint config or capabilities are missing', () => {
|
||||
expect(resolveStatefulCodeSessions(true, undefined)).toBe(false);
|
||||
expect(resolveStatefulCodeSessions(true, {})).toBe(false);
|
||||
expect(resolveStatefulCodeSessions(true, { capabilities: [] })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -565,6 +565,7 @@ export enum AgentCapabilities {
|
|||
end_after_tools = 'end_after_tools',
|
||||
deferred_tools = 'deferred_tools',
|
||||
execute_code = 'execute_code',
|
||||
stateful_code_sessions = 'stateful_code_sessions',
|
||||
file_search = 'file_search',
|
||||
web_search = 'web_search',
|
||||
artifacts = 'artifacts',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue