diff --git a/packages/eve/src/compiler/manifest.ts b/packages/eve/src/compiler/manifest.ts index aec6a0b59..fe01ef639 100644 --- a/packages/eve/src/compiler/manifest.ts +++ b/packages/eve/src/compiler/manifest.ts @@ -429,6 +429,7 @@ const compiledAgentConfigSchema: z.ZodType = z experimental: z .object({ subagentPersistentSessions: z.boolean().optional(), + tasks: z.boolean().optional(), workflow: compiledAgentWorkflowDefinitionSchema.optional(), }) .strict() @@ -834,6 +835,7 @@ export function createCompiledAgentNodeManifest(input: { ? undefined : { subagentPersistentSessions: input.config.experimental.subagentPersistentSessions, + tasks: input.config.experimental.tasks, workflow: input.config.experimental.workflow === undefined ? undefined diff --git a/packages/eve/src/compiler/normalize-agent-config.ts b/packages/eve/src/compiler/normalize-agent-config.ts index 710f6fc68..42b7dd496 100644 --- a/packages/eve/src/compiler/normalize-agent-config.ts +++ b/packages/eve/src/compiler/normalize-agent-config.ts @@ -180,6 +180,10 @@ function normalizeExperimentalDefinition( compiledExperimental.subagentPersistentSessions = experimental.subagentPersistentSessions; } + if (experimental.tasks !== undefined) { + compiledExperimental.tasks = experimental.tasks; + } + if (experimental.workflow !== undefined) { compiledExperimental.workflow = { world: experimental.workflow.world, diff --git a/packages/eve/src/compiler/normalize-manifest.test.ts b/packages/eve/src/compiler/normalize-manifest.test.ts index d09f7afd5..b754b0ebd 100644 --- a/packages/eve/src/compiler/normalize-manifest.test.ts +++ b/packages/eve/src/compiler/normalize-manifest.test.ts @@ -148,6 +148,53 @@ describe("compileAgentManifest", () => { }); }); + it("rejects background-task configuration on subagents", async () => { + const subagentManifest = createAgentSourceManifest({ + agentId: "research", + agentRoot: "/app/agent/subagents/research", + appRoot: "/app", + configModule: createModuleSourceRef({ + logicalPath: "agent.ts", + }), + }); + const manifest = createAgentSourceManifest({ + agentId: "root", + agentRoot: "/app/agent", + appRoot: "/app", + subagents: [ + createLocalSubagentSourceRef({ + entryPath: "subagents/research/agent.ts", + logicalPath: "subagents/research", + manifest: subagentManifest, + rootPath: "/app/agent/subagents/research", + subagentId: "research", + }), + ], + }); + + mocks.compileAgentConfig.mockImplementation(async (input: AgentSourceManifest) => { + if (input.agentId === "research") { + return createConfig({ + description: "Research subagent", + name: "research", + experimental: { + tasks: true, + }, + }); + } + + return createConfig({ name: "root" }); + }); + mocks.loadModuleBackedDefinition.mockResolvedValue({ + description: "Research subagent", + model: "openai/gpt-5.5", + }); + + await expect(compileAgentManifest(manifest)).rejects.toThrow( + 'Remove "experimental.tasks" from "research"', + ); + }); + it("compiles experimental Workflow tool configuration", async () => { const manifest = createAgentSourceManifest({ agentId: "root", diff --git a/packages/eve/src/compiler/normalize-manifest.ts b/packages/eve/src/compiler/normalize-manifest.ts index 1e9bf0b88..f0da08259 100644 --- a/packages/eve/src/compiler/normalize-manifest.ts +++ b/packages/eve/src/compiler/normalize-manifest.ts @@ -64,7 +64,7 @@ async function compileAgentNodeManifest( options: { readonly agentConfigDefinition?: unknown; readonly externalDependencies?: readonly string[]; - readonly allowWorkflowConfig?: boolean; + readonly allowRootOnlyConfig?: boolean; } = {}, ): Promise { const rawConfig = Object.hasOwn(options, "agentConfigDefinition") @@ -72,11 +72,16 @@ async function compileAgentNodeManifest( definition: options.agentConfigDefinition, }) : await compileAgentConfig(manifest, context); - if (options.allowWorkflowConfig === false && rawConfig.experimental?.workflow !== undefined) { + if (options.allowRootOnlyConfig === false && rawConfig.experimental?.workflow !== undefined) { throw new Error( `Workflow runtime configuration is only supported on the root agent config. Remove "experimental.workflow" from "${manifest.agentId}".`, ); } + if (options.allowRootOnlyConfig === false && rawConfig.experimental?.tasks !== undefined) { + throw new Error( + `Background tasks are only supported on the root agent config. Remove "experimental.tasks" from "${manifest.agentId}".`, + ); + } const externalDependencies = mergeExternalDependencies( options.externalDependencies, rawConfig.build?.externalDependencies, diff --git a/packages/eve/src/compiler/normalize-subagent.ts b/packages/eve/src/compiler/normalize-subagent.ts index bbabc7a8b..f334c9a27 100644 --- a/packages/eve/src/compiler/normalize-subagent.ts +++ b/packages/eve/src/compiler/normalize-subagent.ts @@ -45,7 +45,7 @@ export type CompileAgentNodeManifestFn = ( options?: { readonly agentConfigDefinition?: unknown; readonly externalDependencies?: readonly string[]; - readonly allowWorkflowConfig?: boolean; + readonly allowRootOnlyConfig?: boolean; }, ) => Promise; @@ -204,7 +204,7 @@ async function compileSubagent(input: { input.context, { agentConfigDefinition: input.agentConfigDefinition, - allowWorkflowConfig: false, + allowRootOnlyConfig: false, externalDependencies: input.externalDependencies, }, ); diff --git a/packages/eve/src/internal/authored-definition/core.test.ts b/packages/eve/src/internal/authored-definition/core.test.ts index cc22eb334..804303124 100644 --- a/packages/eve/src/internal/authored-definition/core.test.ts +++ b/packages/eve/src/internal/authored-definition/core.test.ts @@ -266,6 +266,34 @@ describe("normalizeAgentDefinition", () => { ), ).toThrow('"experimental.subagentPersistentSessions" must be a boolean.'); }); + + it("accepts a boolean tasks flag", () => { + const definition = normalizeAgentDefinition( + { + model: "openai/gpt-5.5", + experimental: { + tasks: true, + }, + }, + FAILURE_MESSAGE, + ); + + expect(definition.experimental?.tasks).toBe(true); + }); + + it("rejects non-boolean tasks values", () => { + expect(() => + normalizeAgentDefinition( + { + model: "openai/gpt-5.5", + experimental: { + tasks: "yes", + }, + }, + FAILURE_MESSAGE, + ), + ).toThrow('"experimental.tasks" must be a boolean.'); + }); }); describe("normalizeScheduleDefinition", () => { diff --git a/packages/eve/src/internal/authored-definition/core.ts b/packages/eve/src/internal/authored-definition/core.ts index 348ed5a2a..c595fde49 100644 --- a/packages/eve/src/internal/authored-definition/core.ts +++ b/packages/eve/src/internal/authored-definition/core.ts @@ -257,7 +257,7 @@ function normalizeAgentExperimentalDefinition( message: string, ): NonNullable { const record = expectObjectRecord(value, message); - expectOnlyKnownKeys(record, ["subagentPersistentSessions", "workflow"], message); + expectOnlyKnownKeys(record, ["subagentPersistentSessions", "tasks", "workflow"], message); const normalizedDefinition: Mutable> = {}; if (record.subagentPersistentSessions !== undefined) { @@ -267,6 +267,13 @@ function normalizeAgentExperimentalDefinition( normalizedDefinition.subagentPersistentSessions = record.subagentPersistentSessions; } + if (record.tasks !== undefined) { + if (typeof record.tasks !== "boolean") { + throw new Error(`${message} "experimental.tasks" must be a boolean.`); + } + normalizedDefinition.tasks = record.tasks; + } + if (record.workflow !== undefined) { normalizedDefinition.workflow = normalizeAgentWorkflowDefinition(record.workflow, message); } diff --git a/packages/eve/src/runtime/resolve-agent.ts b/packages/eve/src/runtime/resolve-agent.ts index 65ebf719e..1ded9a230 100644 --- a/packages/eve/src/runtime/resolve-agent.ts +++ b/packages/eve/src/runtime/resolve-agent.ts @@ -228,6 +228,7 @@ function createResolvedAgentConfig(manifest: CompiledAgentNodeManifest): Resolve if (manifest.config.experimental !== undefined) { config.experimental = { subagentPersistentSessions: manifest.config.experimental.subagentPersistentSessions, + tasks: manifest.config.experimental.tasks, workflow: manifest.config.experimental.workflow === undefined ? undefined diff --git a/packages/eve/src/shared/agent-definition.ts b/packages/eve/src/shared/agent-definition.ts index e79311fb7..11f98f9a2 100644 --- a/packages/eve/src/shared/agent-definition.ts +++ b/packages/eve/src/shared/agent-definition.ts @@ -203,6 +203,13 @@ export interface AgentExperimentalDefinition { * When unset, delegated children run as one-shot tasks. */ readonly subagentPersistentSessions?: boolean; + /** + * Runs this agent's delegated subagent calls as durable background tasks. + * The originating tool call returns a task receipt immediately and the + * model manages the work through the `task_*` framework tools. Implies + * persistent-session children for subagent dispatch. Root agents only. + */ + readonly tasks?: boolean; /** * Durable Workflow runtime configuration. Root agents may use this to select * the Workflow world backing sessions and runs.