mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
🧵 feat: Background-Native Code Execution Tools (#14532)
The code-execution pair (execute_code/bash_tool) now defaults INTO background dispatch whenever the run_in_background capability is enabled, the same way the SDK's coding tools carry `intent` natively: enabling the capability is enough, with no per-tool or per-spec flag required. An explicit run_in_background: false opts the pair out (by definition name, marker projection, or a narrowing spec selection's wildcard), and the builder Code toggle flips to opt-out semantics: absent reads as on, and turning it off persists an explicit false. A spec's runInBackground: false now synthesizes an explicit wildcard opt-out instead of staying a silent no-op. Pre-native, false and absent were behaviorally identical, so a config that wrote false must not silently flip to backgrounding code. The ephemeral toggle's false stays no-policy: it is a badge default, not a decision.
This commit is contained in:
parent
3f02efdef9
commit
d6c2dc5d8e
9 changed files with 198 additions and 39 deletions
|
|
@ -11,11 +11,11 @@ import {
|
|||
} from '@librechat/client';
|
||||
import type { AgentForm } from '~/common';
|
||||
import { useAgentCapabilities, useGetAgentsConfig, useLocalize } from '~/hooks';
|
||||
import { withBooleanOption } from '~/hooks/Agents/useMCPToolOptions';
|
||||
import { withNativeBooleanOptOut } from '~/hooks/Agents/useMCPToolOptions';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
/** Tools sharing the code-execution sandbox; the single builder toggle opts
|
||||
* the whole pair into background dispatch. */
|
||||
/** Tools sharing the code-execution sandbox; background-native, so the single
|
||||
* builder toggle persists (or clears) the pair's explicit opt-out. */
|
||||
const CODE_BACKGROUND_TOOL_IDS: string[] = [Tools.execute_code, Tools.bash_tool];
|
||||
|
||||
export default function Background() {
|
||||
|
|
@ -24,17 +24,19 @@ export default function Background() {
|
|||
const { backgroundToolsEnabled } = useAgentCapabilities(agentsConfig?.capabilities);
|
||||
const { control, getValues, setValue } = useFormContext<AgentForm>();
|
||||
const toolOptions = useWatch({ control, name: 'tool_options' });
|
||||
/** Either key enables the pair at runtime (the backend expands the code
|
||||
* opt-in across both), so the switch must reflect either. */
|
||||
const enabled = CODE_BACKGROUND_TOOL_IDS.some(
|
||||
(toolId) => toolOptions?.[toolId]?.run_in_background === true,
|
||||
);
|
||||
/** The pair is background-NATIVE: no entry means on. Mirrors the server's
|
||||
* resolution precedence for the runtime def — its own `bash_tool` key
|
||||
* first, then the `execute_code` capability marker, then the native
|
||||
* default (`resolveToolOption` in packages/api). */
|
||||
const enabled =
|
||||
(toolOptions?.[Tools.bash_tool]?.run_in_background ??
|
||||
toolOptions?.[Tools.execute_code]?.run_in_background) !== false;
|
||||
|
||||
const handleChange = useCallback(
|
||||
(value: boolean) => {
|
||||
let updated = getValues('tool_options') || {};
|
||||
for (const toolId of CODE_BACKGROUND_TOOL_IDS) {
|
||||
updated = withBooleanOption(updated, toolId, 'run_in_background', value);
|
||||
updated = withNativeBooleanOptOut(updated, toolId, 'run_in_background', value);
|
||||
}
|
||||
setValue('tool_options', updated, { shouldDirty: true });
|
||||
},
|
||||
|
|
|
|||
|
|
@ -64,6 +64,36 @@ export function withBooleanOption(
|
|||
return updatedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counterpart of {@link withBooleanOption} for flags whose ABSENCE means
|
||||
* default-on (background-native code execution): enabling clears the entry so
|
||||
* the native default applies; disabling persists an explicit `false`, which a
|
||||
* missing key can no longer express.
|
||||
*/
|
||||
export function withNativeBooleanOptOut(
|
||||
options: AgentToolOptions,
|
||||
toolId: string,
|
||||
key: BooleanToolOptionKey,
|
||||
enabled: boolean,
|
||||
): AgentToolOptions {
|
||||
const updatedOptions: AgentToolOptions = { ...options };
|
||||
const currentToolOptions = updatedOptions[toolId];
|
||||
if (!enabled) {
|
||||
updatedOptions[toolId] = { ...currentToolOptions, [key]: false };
|
||||
return updatedOptions;
|
||||
}
|
||||
if (!currentToolOptions || !(key in currentToolOptions)) {
|
||||
return updatedOptions;
|
||||
}
|
||||
const { [key]: _omit, ...restOptions } = currentToolOptions;
|
||||
if (Object.keys(restOptions).length === 0) {
|
||||
delete updatedOptions[toolId];
|
||||
} else {
|
||||
updatedOptions[toolId] = restOptions;
|
||||
}
|
||||
return updatedOptions;
|
||||
}
|
||||
|
||||
/** Read/toggle handlers for one boolean per-tool option key (single + bulk). */
|
||||
function useBooleanToolOption(
|
||||
key: BooleanToolOptionKey,
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@
|
|||
"com_nav_help": "Help",
|
||||
"com_nav_help_faq": "Help & FAQ",
|
||||
"com_nav_info_balance": "Balance shows how many token credits you have left to use. Token credits translate to monetary value (e.g., 1000 credits = $0.001 USD)",
|
||||
"com_nav_info_code_background": "When enabled, the model can run code executions in the background: the conversation continues immediately while the code runs, and the model retrieves the result later with the background task tool. Output and generated files still attach to the original code run. Requires the app-level background tools capability.",
|
||||
"com_nav_info_code_background": "Code executions can run in the background by default: the conversation continues immediately while the code runs, and the model retrieves the result later with the background task tool. Output and generated files still attach to the original code run. Turn this off to require every code execution to finish before the conversation continues. Requires the app-level background tools capability.",
|
||||
"com_nav_info_default_temporary_chat": "When enabled, new chats will start with temporary chat mode activated by default. Temporary chats are not saved to your history.",
|
||||
"com_nav_info_during_run_action": "Choose what pressing Enter does while the assistant is still responding. \"Steer the response\" inserts your message into the current response at its next step; \"Queue for after\" sends it as a new turn once the response finishes. You can always override this per message from the send button.",
|
||||
"com_nav_info_enter_to_send": "When enabled, pressing `ENTER` will send your message. When disabled, pressing Enter will add a new line, and you'll need to press `CTRL + ENTER` / `⌘ + ENTER` to send your message.",
|
||||
|
|
|
|||
|
|
@ -418,11 +418,11 @@ describe('loadAgent', () => {
|
|||
expect(result?.tool_options).toEqual({ '*': { run_in_background: true } });
|
||||
});
|
||||
|
||||
test('synthesizes background tool_options from a model spec (runInBackground: true), and not without it', async () => {
|
||||
test('synthesizes background tool_options from a model spec: true opts in, false is an explicit opt-out, absent is no policy', async () => {
|
||||
const { EPHEMERAL_AGENT_ID } = Constants;
|
||||
mockGetMCPServerTools.mockResolvedValue({ crm_lookup: { name: 'crm_lookup' } });
|
||||
|
||||
const buildReq = (specName: string, runInBackground: boolean): LoadAgentParams['req'] =>
|
||||
const buildReq = (specName: string, runInBackground?: boolean): LoadAgentParams['req'] =>
|
||||
({
|
||||
user: { id: 'user123' },
|
||||
body: {},
|
||||
|
|
@ -458,6 +458,21 @@ describe('loadAgent', () => {
|
|||
expect(withFlag?.tool_options).toEqual({ '*': { run_in_background: true } });
|
||||
|
||||
const withoutFlag = await loadAgent(
|
||||
{
|
||||
req: buildReq('bg-absent', undefined),
|
||||
spec: 'bg-absent',
|
||||
agent_id: EPHEMERAL_AGENT_ID as string,
|
||||
endpoint: 'openai',
|
||||
model_parameters: { model: 'gpt-4' } as unknown as AgentModelParameters,
|
||||
},
|
||||
deps,
|
||||
);
|
||||
expect(withoutFlag?.tool_options).toBeUndefined();
|
||||
|
||||
/** `false` must synthesize an explicit wildcard opt-out (not stay a
|
||||
* no-op): the background-native code pair would otherwise default on
|
||||
* against an admin's written `runInBackground: false`. */
|
||||
const withFalse = await loadAgent(
|
||||
{
|
||||
req: buildReq('bg-off', false),
|
||||
spec: 'bg-off',
|
||||
|
|
@ -467,7 +482,7 @@ describe('loadAgent', () => {
|
|||
},
|
||||
deps,
|
||||
);
|
||||
expect(withoutFlag?.tool_options).toBeUndefined();
|
||||
expect(withFalse?.tool_options).toEqual({ '*': { run_in_background: false } });
|
||||
});
|
||||
|
||||
test('should enable full skill scope for ephemeral model spec with skills true', async () => {
|
||||
|
|
|
|||
|
|
@ -293,16 +293,25 @@ describe('registerBackgroundTaskTool', () => {
|
|||
});
|
||||
|
||||
describe('synthesizeBackgroundToolOptions', () => {
|
||||
it('returns undefined when neither the ephemeral toggle nor the model spec enables it', () => {
|
||||
it('returns undefined when neither the ephemeral toggle nor the model spec carries a policy', () => {
|
||||
expect(synthesizeBackgroundToolOptions({})).toBeUndefined();
|
||||
/** The ephemeral toggle is a badge default, not a decision — its `false`
|
||||
* stays no-policy so the background-native code pair keeps its default. */
|
||||
expect(
|
||||
synthesizeBackgroundToolOptions({
|
||||
ephemeralAgent: { run_in_background: false },
|
||||
modelSpec: { runInBackground: false },
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('records a spec runInBackground: false as an explicit "none", like the empty list', () => {
|
||||
/** Pre-native, `false` and absent were behaviorally identical (off); a
|
||||
* config that wrote `false` must not silently flip to backgrounding code. */
|
||||
expect(synthesizeBackgroundToolOptions({ modelSpec: { runInBackground: false } })).toEqual({
|
||||
[TOOL_SELECTION_WILDCARD]: { run_in_background: false },
|
||||
});
|
||||
});
|
||||
|
||||
it('records boolean/ephemeral modes as a wildcard opt-in (no name enumeration)', () => {
|
||||
const expected = { [TOOL_SELECTION_WILDCARD]: { run_in_background: true } };
|
||||
expect(synthesizeBackgroundToolOptions({ modelSpec: { runInBackground: true } })).toEqual(
|
||||
|
|
@ -402,6 +411,77 @@ describe('selection policy at injection time', () => {
|
|||
expect(backgroundToolNames).toEqual(['bash_tool']);
|
||||
});
|
||||
|
||||
it('backgrounds the code pair natively, with no tool_options at all', () => {
|
||||
const defs = [mcpDef('bash_tool'), mcpDef('search_mcp_docs')];
|
||||
const registry: LCToolRegistry = new Map(defs.map((d) => [d.name, { ...d }]));
|
||||
const result = applyBackgroundToolCalls({
|
||||
toolDefinitions: defs,
|
||||
toolRegistry: registry,
|
||||
toolOptions: undefined,
|
||||
});
|
||||
expect(result.backgroundToolNames).toEqual(['bash_tool']);
|
||||
const bashDef = result.toolDefinitions.find((d) => d.name === 'bash_tool');
|
||||
expect(
|
||||
(bashDef?.parameters as { properties: Record<string, unknown> }).properties[
|
||||
RUN_IN_BACKGROUND_ARG
|
||||
],
|
||||
).toBeDefined();
|
||||
const searchDef = result.toolDefinitions.find((d) => d.name === 'search_mcp_docs');
|
||||
expect(
|
||||
(searchDef?.parameters as { properties: Record<string, unknown> }).properties[
|
||||
RUN_IN_BACKGROUND_ARG
|
||||
],
|
||||
).toBeUndefined();
|
||||
expect(registry.has(CHECK_BACKGROUND_TASK_NAME)).toBe(true);
|
||||
});
|
||||
|
||||
it('an explicit false opts the native pair out — by definition name or by marker projection', () => {
|
||||
const byName = applyBackgroundToolCalls({
|
||||
toolDefinitions: [mcpDef('bash_tool')],
|
||||
toolRegistry: new Map(),
|
||||
toolOptions: { bash_tool: { run_in_background: false } },
|
||||
});
|
||||
expect(byName.backgroundToolNames).toEqual([]);
|
||||
|
||||
const registry: LCToolRegistry = new Map();
|
||||
const byMarker = applyBackgroundToolCalls({
|
||||
toolDefinitions: [mcpDef('bash_tool')],
|
||||
toolRegistry: registry,
|
||||
toolOptions: { execute_code: { run_in_background: false } },
|
||||
capabilityToolNames: new Map([['execute_code', ['read_file', 'bash_tool']]]),
|
||||
});
|
||||
expect(byMarker.backgroundToolNames).toEqual([]);
|
||||
expect(registry.has(CHECK_BACKGROUND_TASK_NAME)).toBe(false);
|
||||
});
|
||||
|
||||
it('a narrowing selection that omits the code pair opts it out via the wildcard', () => {
|
||||
const options = synthesizeBackgroundToolOptions({
|
||||
modelSpec: { runInBackground: ['slow_report_mcp_analytics'] },
|
||||
});
|
||||
const { backgroundToolNames } = applyBackgroundToolCalls({
|
||||
toolDefinitions: [mcpDef('bash_tool'), mcpDef('slow_report_mcp_analytics')],
|
||||
toolRegistry: undefined,
|
||||
toolOptions: options,
|
||||
});
|
||||
expect(backgroundToolNames).toEqual(['slow_report_mcp_analytics']);
|
||||
});
|
||||
|
||||
it('a selection can name the code pair by its runtime name (bash_tool)', () => {
|
||||
const warn = jest.spyOn(logger, 'warn').mockImplementation(() => logger);
|
||||
const options = synthesizeBackgroundToolOptions({
|
||||
modelSpec: { runInBackground: ['bash_tool'] },
|
||||
});
|
||||
const { backgroundToolNames } = applyBackgroundToolCalls({
|
||||
toolDefinitions: [mcpDef('bash_tool'), mcpDef('search_mcp_docs')],
|
||||
toolRegistry: undefined,
|
||||
toolOptions: options,
|
||||
capabilityToolNames: new Map([['execute_code', ['read_file', 'bash_tool']]]),
|
||||
});
|
||||
expect(backgroundToolNames).toEqual(['bash_tool']);
|
||||
expect(warn).not.toHaveBeenCalled();
|
||||
warn.mockRestore();
|
||||
});
|
||||
|
||||
it('still enforces eligibility for explicitly named tools, and diagnoses them', () => {
|
||||
/** Backgrounding these would silently drop attachments/citations or break
|
||||
* artifact continuity, so a list must not be able to force them on. */
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@
|
|||
* Opt-in mirrors `deferred_tools`: an admin capability
|
||||
* (`AgentCapabilities.run_in_background`) gates the feature, and a per-tool
|
||||
* `tool_options[name].run_in_background` flag turns it on for a given tool,
|
||||
* which injects a `run_in_background` boolean into that tool's schema.
|
||||
* which injects a `run_in_background` boolean into that tool's schema. The
|
||||
* code-execution pair (`execute_code`/`bash_tool`) is background-NATIVE:
|
||||
* while the capability is enabled it defaults on without a per-tool flag, and
|
||||
* an explicit `run_in_background: false` opts it out.
|
||||
*
|
||||
* @module packages/api/src/agents/background
|
||||
*/
|
||||
|
|
@ -115,6 +118,20 @@ export function isBackgroundEligibleToolName(name: string): boolean {
|
|||
return !name.startsWith(AgentConstants.LC_TRANSFER_TO_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools that are background-NATIVE: they default INTO background dispatch
|
||||
* while the capability is enabled, and an explicit `run_in_background: false`
|
||||
* opts one out. Code executions are the paradigmatic slow, detachable call —
|
||||
* they flow through the generic execute path and their completion is
|
||||
* harvested onto the dispatch turn — so they carry the param without
|
||||
* per-agent opt-in, the same way the SDK's coding tools carry `intent`
|
||||
* natively. Mirrors `NATIVE_INTENT_TOOL_NAMES` in `intent.ts`.
|
||||
*/
|
||||
export const NATIVE_BACKGROUND_TOOL_NAMES: ReadonlySet<string> = new Set<string>([
|
||||
String(AgentConstants.EXECUTE_CODE),
|
||||
String(AgentConstants.BASH_TOOL),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Coerces tool-call args to an object, parsing a stringified JSON object (some
|
||||
* providers deliver args as a string). Returns undefined for non-object args.
|
||||
|
|
@ -344,11 +361,14 @@ export function registerBackgroundTaskTool(params: {
|
|||
* Opt-in resolves per FINAL definition via {@link resolveToolOption}
|
||||
* (explicit name → capability marker projection → wildcard), so a saved
|
||||
* agent's `execute_code` entry reaches `bash_tool` and a spec selection
|
||||
* reaches lazily-registered definitions. Both saved agents and
|
||||
* ephemeral/model-spec agents reach this with `tool_options` populated, so
|
||||
* the logic is written once. When a narrowing selection is present, names
|
||||
* that never took effect — including markers whose every runtime definition
|
||||
* is background-excluded, like `memory` — are warned about here.
|
||||
* reaches lazily-registered definitions. When no policy speaks at all, the
|
||||
* background-native code pair defaults IN — so this pass runs on every
|
||||
* capability-enabled request, not just explicitly opted-in agents. Both
|
||||
* saved agents and ephemeral/model-spec agents reach this with the same
|
||||
* `tool_options` shape, so the logic is written once. When a narrowing
|
||||
* selection is present, names that never took effect — including markers
|
||||
* whose every runtime definition is background-excluded, like `memory` —
|
||||
* are warned about here.
|
||||
*/
|
||||
export function applyBackgroundToolCalls(params: {
|
||||
toolDefinitions: LCTool[] | undefined;
|
||||
|
|
@ -366,9 +386,6 @@ export function applyBackgroundToolCalls(params: {
|
|||
}): { toolDefinitions: LCTool[]; backgroundToolNames: string[] } {
|
||||
const { toolRegistry, toolOptions, capabilityToolNames, excludeTool } = params;
|
||||
const defs = params.toolDefinitions ?? [];
|
||||
if (!toolOptions || !Object.values(toolOptions).some((o) => o?.run_in_background === true)) {
|
||||
return { toolDefinitions: defs, backgroundToolNames: [] };
|
||||
}
|
||||
const selectionNames = getSelectionNames(toolOptions, 'run_in_background');
|
||||
const effectiveSources = new Set<string>();
|
||||
|
||||
|
|
@ -380,11 +397,8 @@ export function applyBackgroundToolCalls(params: {
|
|||
toolOptions,
|
||||
capabilityToolNames,
|
||||
);
|
||||
if (
|
||||
resolved?.value !== true ||
|
||||
!isBackgroundEligibleToolName(def.name) ||
|
||||
excludeTool?.(def.name) === true
|
||||
) {
|
||||
const optedIn = resolved != null ? resolved.value : NATIVE_BACKGROUND_TOOL_NAMES.has(def.name);
|
||||
if (!optedIn || !isBackgroundEligibleToolName(def.name) || excludeTool?.(def.name) === true) {
|
||||
return def;
|
||||
}
|
||||
if (!canInjectRunInBackgroundParam(def)) {
|
||||
|
|
@ -393,7 +407,9 @@ export function applyBackgroundToolCalls(params: {
|
|||
);
|
||||
return def;
|
||||
}
|
||||
effectiveSources.add(resolved.source);
|
||||
if (resolved != null) {
|
||||
effectiveSources.add(resolved.source);
|
||||
}
|
||||
backgroundToolNames.push(def.name);
|
||||
const injected = injectRunInBackgroundParam(def);
|
||||
if (injected === def) {
|
||||
|
|
@ -427,6 +443,13 @@ export function applyBackgroundToolCalls(params: {
|
|||
* letting the model detach every other tool in the spec. The ephemeral toggle
|
||||
* stays boolean and never narrows; it has no per-tool UI to drive it.
|
||||
*
|
||||
* A spec's `runInBackground: false` is the boolean spelling of the empty
|
||||
* list — an explicit "none" that also opts the background-native code pair
|
||||
* out. Pre-native, `false` was behaviorally identical to omitting the field,
|
||||
* so a config that wrote it must not silently flip to backgrounding code.
|
||||
* The EPHEMERAL toggle's `false` stays no-policy — a badge default, not a
|
||||
* decision — so the native default holds for ephemeral chats.
|
||||
*
|
||||
* The selection is recorded as policy (wildcard default + verbatim names)
|
||||
* and resolved against the FINAL definition set in
|
||||
* `applyBackgroundToolCalls`, so capability markers and lazily-expanded MCP
|
||||
|
|
@ -438,9 +461,10 @@ export function synthesizeBackgroundToolOptions(sources: {
|
|||
ephemeralAgent?: { run_in_background?: boolean } | null;
|
||||
modelSpec?: { runInBackground?: boolean | string[] } | null;
|
||||
}): AgentToolOptions | undefined {
|
||||
const specSelection = sources.modelSpec?.runInBackground;
|
||||
return synthesizeSelectionToolOptions(
|
||||
'run_in_background',
|
||||
sources.modelSpec?.runInBackground,
|
||||
specSelection === false ? [] : specSelection,
|
||||
sources.ephemeralAgent?.run_in_background === true,
|
||||
BACKGROUND_SELECTION_LABEL,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -99,8 +99,9 @@ export interface DiscoverConnectedAgentsParams {
|
|||
/**
|
||||
* Run-level `run_in_background` capability gate. Forwarded verbatim so a
|
||||
* handoff/connected agent's own event-driven tools with
|
||||
* `tool_options[tool].run_in_background` get the injected param + poll tool,
|
||||
* matching how the same agent behaves when run as the primary.
|
||||
* `tool_options[tool].run_in_background` (and its background-native code
|
||||
* pair) get the injected param + poll tool, matching how the same agent
|
||||
* behaves when run as the primary.
|
||||
*/
|
||||
backgroundToolsAvailable?: InitializeAgentParams['backgroundToolsAvailable'];
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -428,7 +428,8 @@ export interface InitializeAgentParams {
|
|||
/**
|
||||
* Whether the `run_in_background` capability is enabled for this run. When
|
||||
* true, tools the agent opted in via `tool_options[name].run_in_background`
|
||||
* get a `run_in_background` schema param and the `check_background_task` poll
|
||||
* (plus the background-native code pair, unless explicitly opted out) get a
|
||||
* `run_in_background` schema param and the `check_background_task` poll
|
||||
* tool is registered.
|
||||
*/
|
||||
backgroundToolsAvailable?: boolean;
|
||||
|
|
@ -1221,7 +1222,8 @@ export async function initializeAgent(
|
|||
* Inject the `run_in_background` param into eligible opted-in tools and
|
||||
* register the `check_background_task` poll tool. Runs after all built-in
|
||||
* tool registration so the full, final `toolDefinitions` set is considered.
|
||||
* Opt-in is per-tool via `tool_options` for both saved and ephemeral agents.
|
||||
* Opt-in is per-tool via `tool_options` for both saved and ephemeral agents;
|
||||
* the code-execution pair is background-native and needs no opt-in.
|
||||
*/
|
||||
let backgroundToolNames: string[] | undefined;
|
||||
if (params.backgroundToolsAvailable === true) {
|
||||
|
|
|
|||
|
|
@ -57,10 +57,15 @@ export type TModelSpec = {
|
|||
askUserQuestion?: boolean;
|
||||
/**
|
||||
* Let the model dispatch tool calls in the background (poll results via
|
||||
* `check_background_task`). `true` opts in every eligible tool; a string
|
||||
* array opts in only the named tools, matched against the spec's resolved
|
||||
* tool ids (e.g. `['slow_report_mcp_analytics']`). Requires the
|
||||
* `run_in_background` agent capability to be enabled by the admin.
|
||||
* `check_background_task`). Code execution is background-NATIVE: when the
|
||||
* admin enables the `run_in_background` agent capability, an
|
||||
* `executeCode: true` spec backgrounds code with no flag here. `true` opts
|
||||
* in every other eligible tool; a string array opts in only the named
|
||||
* tools, matched against the spec's resolved tool ids (e.g.
|
||||
* `['slow_report_mcp_analytics']`; `execute_code` and `bash_tool` both
|
||||
* select the code pair). `false`, the empty list, or a list that omits the
|
||||
* code pair explicitly opts it back out. Requires the `run_in_background`
|
||||
* agent capability.
|
||||
*/
|
||||
runInBackground?: boolean | string[];
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue