Skip to content

Commit 6c3e5c8

Browse files
committed
[Agent Builder] add sub-agent configuration
1 parent ffcc13e commit 6c3e5c8

35 files changed

Lines changed: 1369 additions & 109 deletions

x-pack/platform/packages/shared/agent-builder/agent-builder-common/agents/agent_ids.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ describe('validateAgentId', () => {
7272
const error = validateAgentId({ agentId, builtIn: true });
7373
expect(error).toBeUndefined();
7474
});
75+
76+
test('rejects the reserved id "_self" for user-created agents', () => {
77+
const error = validateAgentId({ agentId: '_self', builtIn: false });
78+
expect(error).toBe(`Agent id "_self" is reserved and cannot be used.`);
79+
});
80+
81+
test('rejects the reserved id "_self" for built-in agents', () => {
82+
const error = validateAgentId({ agentId: '_self', builtIn: true });
83+
expect(error).toBe(`Agent id "_self" is reserved and cannot be used.`);
84+
});
7585
});
7686

7787
describe('agentId regexp', () => {

x-pack/platform/packages/shared/agent-builder/agent-builder-common/agents/agent_ids.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import { hasNamespaceName, isInProtectedNamespace } from '../base/namespaces';
9+
import { SELF_AGENT_ID } from './constants';
910

1011
// - Must start and end with letter or digit
1112
// - Can contain letters, digits, hyphens, underscores and dots
@@ -21,6 +22,9 @@ export const validateAgentId = ({
2122
agentId: string;
2223
builtIn: boolean;
2324
}): string | undefined => {
25+
if (agentId === SELF_AGENT_ID) {
26+
return `Agent id "${SELF_AGENT_ID}" is reserved and cannot be used.`;
27+
}
2428
if (!agentIdRegexp.test(agentId)) {
2529
return `Agent ids must start and end with a letter or number, and can only contain lowercase letters, numbers, dots, hyphens and underscores`;
2630
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
/**
9+
* Reserved agent id acting as a self-fork sentinel in
10+
* {@link AgentConfiguration.subagent_ids}. Not a real, storable agent id —
11+
* `validateAgentId` rejects it on both `builtIn: true` and `builtIn: false`
12+
* paths. At runtime, `_self` in a resolved allowlist substitutes to the
13+
* executing agent's real id only at the `SubAgentExecutor.executeSubAgent`
14+
* call seam; everywhere else the sentinel stays visible so telemetry, logs,
15+
* and the LLM tool schema can distinguish self-fork from an explicit id.
16+
*/
17+
export const SELF_AGENT_ID = '_self' as const;

x-pack/platform/packages/shared/agent-builder/agent-builder-common/agents/definition.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ export interface AgentConfiguration {
152152
* the accuracy and token efficiency.
153153
* */
154154
ai_indices?: string[];
155+
156+
/**
157+
* Allowlist of agent ids this agent may spawn as sub-agents via the
158+
* `run_subagent` tool. Entries are either real agent ids or the sentinel
159+
* `SELF_AGENT_ID` (`'_self'`) which resolves to the executing agent itself.
160+
*
161+
* Missing / empty → the sub-agent tools (`run_subagent`, `send_message`,
162+
* `sleep`) are not registered for this agent. Non-empty → the resolved,
163+
* access-filtered list becomes the `agent_id` enum on `run_subagent`.
164+
*/
165+
subagent_ids?: string[];
155166
}
156167

157168
/**

x-pack/platform/packages/shared/agent-builder/agent-builder-common/agents/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export {
3131
type AgentAccessControlPrincipalType,
3232
} from './access_control';
3333
export { agentIdRegexp, agentIdMaxLength, validateAgentId } from './agent_ids';
34+
export { SELF_AGENT_ID } from './constants';
3435
export { AgentExecutionErrorCode } from './execution_errors';
3536
export { AgentExecutionMode, SubagentExecutionMode } from './execution_mode';
3637
export { ExecutionStatus, type SerializedExecutionError } from './execution_status';

x-pack/platform/packages/shared/agent-builder/agent-builder-common/chat/conversation.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -672,9 +672,30 @@ export interface ConversationInternalState {
672672
/** Active todo list for the current conversation. Replaced wholesale on each write. */
673673
todos?: TodoItem[];
674674
/**
675-
* Map of persistent sub-agent name → child conversation id.
675+
* Map of persistent sub-agent name → entry describing the child conversation
676+
* and the agent id that backs it. The `agent_id` is what the parent's
677+
* `subagent_ids` allowlist filters on when deciding whether `send_message`
678+
* can still reach the child (§3.5 of the configurable-subagents design).
679+
*
680+
* `agent_id` is either a real agent id or the sentinel `SELF_AGENT_ID`
681+
* (`'_self'`), stored as-is at creation time and matched by exact string.
676682
*/
677-
subagents?: Record<string, string>;
683+
subagents?: Record<string, SubagentEntry>;
684+
}
685+
686+
/**
687+
* Value stored per persistent sub-agent name on the parent conversation's
688+
* {@link ConversationInternalState.subagents} map.
689+
*/
690+
export interface SubagentEntry {
691+
/** ID of the child conversation. */
692+
conversation_id: string;
693+
/**
694+
* Agent id backing this persistent sub-agent — either a real agent id or
695+
* the `SELF_AGENT_ID` sentinel. Written by `run_subagent` at persistent
696+
* creation time using the LLM's `agent_id` choice.
697+
*/
698+
agent_id: string;
678699
}
679700

680701
export interface BackgroundExecutionCompletedAt {

x-pack/platform/packages/shared/agent-builder/agent-builder-common/chat/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export {
4949
type TodoStatus,
5050
type BackgroundExecutionState,
5151
type BackgroundExecutionCompletedAt,
52+
type SubagentEntry,
5253
type BackgroundAgentCompleteStep,
5354
isBackgroundAgentCompleteStep,
5455
type TodosStep,

x-pack/platform/packages/shared/agent-builder/agent-builder-common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ export {
228228
isCompactionStep,
229229
isBackgroundAgentCompleteStep,
230230
type BackgroundAgentCompleteStep,
231+
type SubagentEntry,
231232
type SubagentRosterEntry,
232233
type SubagentRosterUpdatedStep,
233234
type SubagentRosterUpdatedStepData,

x-pack/platform/packages/shared/agent-builder/agent-builder-server/agents/provider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import type { AgentBuilderHooks } from '../hooks/types';
4949
import type { ToolRegistry } from '../tools';
5050
import type { AgentBuilderAnalytics, AgentBuilderTracking } from '../telemetry';
5151
import type { AiIndexResolver } from './ai_index_resolver';
52+
import type { AgentRegistry } from './registry';
5253

5354
/**
5455
* Read/write conversation store contract exposed to agent handlers.
@@ -311,6 +312,12 @@ export interface AgentHandlerContext {
311312
* Sub-agent executor for spawning child agent executions.
312313
*/
313314
subAgentExecutor: SubAgentExecutor;
315+
/**
316+
* Agent registry scoped to the current user. Used to resolve peer agents by
317+
* id — e.g. to look up descriptions for entries in `configuration.subagent_ids`
318+
* when composing the `run_subagent` tool schema.
319+
*/
320+
agentRegistry: AgentRegistry;
314321
/**
315322
* Conversation store client scoped to the current user. Prefer this over
316323
* issuing raw ES queries against the conversation index.

x-pack/platform/plugins/shared/agent_builder/public/application/components/agents/edit/agent_form_validation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,5 +144,6 @@ export const agentFormSchema = z.object({
144144
plugin_ids: z.array(z.string()).optional(),
145145
connector_ids: z.array(z.string()).optional(),
146146
ai_indices: z.array(z.string()).optional(),
147+
subagent_ids: z.array(z.string()).optional(),
147148
}),
148149
});

0 commit comments

Comments
 (0)