Effects
Effects are scheduled side-effect handlers that run outside normal tool execution. They are ideal for delayed notifications, background cleanup, and async operations that should not block a model step.
1. Effect Definition
1.1 Purpose
Unlike tools, effects:
- do not return output to the LLM
- can be delayed for future execution
- run independently from immediate conversation turn processing
1.2 defineEffect
Effects MUST be declared with defineEffect() and exported as the default export from a file in agents/effects/.
With args schema:
import { z } from 'zod';
import { defineEffect } from '@standardagents/spec';
export default defineEffect(
'Send a reminder email',
z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
async (state, args) => {
// runtime-specific implementation
}
);
Without args schema:
import { defineEffect } from '@standardagents/spec';
export default defineEffect(
'Clean up stale records',
async (state) => {
// runtime-specific implementation
}
);
1.3 Naming
- Effect names are file-based (
agents/effects/send_digest.ts=>send_digest) - Names SHOULD use
snake_case - Names MUST be unique within the active namespace
2. Scheduling Effects
Effects are scheduled through ThreadState.scheduleEffect(name, args, delay?).
await state.scheduleEffect(
'send_reminder_email',
{ to: '[email protected]', subject: 'Reminder', body: 'Hello!' },
30 * 60 * 1000
);
2.1 Packed Effect Names
For packed package effects, runtimes should support qualified names:
{packageId}/{effectName}
Example:
await state.scheduleEffect('standardagent-sales/send_digest', { accountId: 'acct_123' }, 0);
This avoids collisions between global and package-local effect names.
3. Execution Semantics
Implementations:
- MUST enqueue effect execution asynchronously
- MUST preserve scheduled args for execution time
- SHOULD expose inspection and cancellation APIs (
getScheduledEffects,removeScheduledEffect) - SHOULD keep effect handlers idempotent where possible
Effect execution is runtime-managed and may occur after the originating flow has completed.
4. Type Definitions
export type Effect<State = ThreadState, Args extends ToolArgs | null = null> =
Args extends ToolArgs
? (state: State, args: z.infer<Args>) => Promise<void> | void
: (state: State) => Promise<void> | void;
export type EffectDefinition<State = ThreadState, Args extends ToolArgs | null = null> =
[string, Args, Effect<State, Args>];
See also: