Skip to content

feat: Load subagent configs for OpenAI-compatible /v1 endpoints (parity with handoffs, PR #12740) #13898

Description

@andrewgl504

What it does

The OpenAI-compatible endpoints /api/agents/v1/chat/completions and /api/agents/v1/responses do not load an agent's Subagents (spawn-as-tool) configuration. When an agent with enabled subagents is invoked over /v1, no agent in the graph can spawn subagents — not the primary, not handoff targets — because the feature is never instantiated by those controllers. The same agent invoked through the browser/JWT path (/api/agents/*) spawns subagents normally.

This is the subagent analogue of #12726 / PR #12740, which fixed handoffs/chains (graph edges) to load on /v1 via the shared discoverConnectedAgents helper. Handoffs work on /v1 today; subagent-spawn does not, and there's no equivalent port.

Root cause (code map)

The subagent loader lives only on the JWT path:

  • api/server/services/Endpoints/agents/initialize.js — loadSubagentsFor (closure) + resolveSubagentTrees (BFS) populate config.subagentAgentConfigs, gated by subagentsCapabilityEnabled (endpoints.agents.capabilities includes subagents) and per-agent agent.subagents.enabled. Depth guard MAX_SUBAGENT_GRAPH_NODES; cycle guard via a per-config max-depth map.

It is absent from the /v1 controllers:

  • api/server/controllers/agents/openai.js and responses.js call discoverConnectedAgents(...) (handoff BFS) and then build runAgents = [primaryConfig, ...handoffAgentConfigs.values()] for createRun. They never invoke the subagent loader, so every config.subagentAgentConfigs stays undefined.

The runtime already supports it: packages/api/src/agents/run.ts::buildSubagentConfigs transforms agent.subagentAgentConfigs into SDK SubagentConfig[] — it simply receives nothing on the /v1 path. So this looks like a controller-wiring gap, not a runtime change.

Proposed fix (mirrors #12740)

  1. Extract loadSubagentsFor / resolveSubagentTrees from initialize.js into a shared, DI-friendly helper in packages/api/src/agents/ (e.g. alongside discoverConnectedAgents), and refactor the JWT path to call it (no behavior change).
  2. Call the helper in both /v1 controllers after discoverConnectedAgents and before createRun, gated by subagentsCapabilityEnabled.
  3. Replicate the capability-disabled strip (config.subagents/subagentAgentConfigs = undefined).

Security note (important review point)

The JWT loader does no load-time ACL check (it relies on the SDK's spawn-time permission callback). The /v1 route gates on REMOTE_AGENT + VIEW, and discoverConnectedAgents deliberately re-checks each handoff target against that resource type. The subagent port should apply the same REMOTE_AGENT + VIEW check so a headless API key cannot spawn agents its user can't see.

Use case

Invoking an orchestrator agent headlessly (server-to-server, via sk- API key) and expecting the same multi-agent behavior as the browser. Today handoffs survive the API boundary but subagent-spawn silently no-ops, so an agent's advertised capabilities differ by entry path.

Environment

  • LibreChat v0.8.7-rc1
  • Confirmed against current main (openai.js / responses.js / initialize.js).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions