.agents/skills/agent-signal/references/handlers.md
Use the middleware helpers in src/server/services/agentSignal/runtime/middleware.ts.
They provide:
defineSourceHandler(...)defineSignalHandler(...)defineActionHandler(...)defineAgentSignalHandlers(...)These helpers do two jobs:
listen points at concrete source, signal, or action typesEach handler receives:
RuntimeProcessorContextThe context gives you:
scopeKeynow()runtimeState.getGuardState(lane)runtimeState.touchGuardState(lane, now?)Read:
src/server/services/agentSignal/runtime/context.tsReturn one of these shapes:
void: no fan-out, stop at this handler{ status: 'dispatch', signals?, actions? }: continue the chain{ status: 'wait', pending? }: pause for later host coordination{ status: 'schedule', nextHop }: schedule another hop{ status: 'conclude', concluded? }: stop with a terminal runtime resultExecutorResult: only for action handlers that performed a concrete side effectRead:
packages/agent-signal/src/base/types.tssrc/server/services/agentSignal/runtime/AgentSignalScheduler.tsUse defineAgentSignalHandlers([...]) to bundle related handlers into one policy.
Example from analyzeIntent:
return defineAgentSignalHandlers([
createFeedbackSatisfactionJudgeProcessor(...),
createFeedbackDomainJudgeSignalHandler(...),
createFeedbackActionPlannerSignalHandler(),
defineUserMemoryActionHandler(...),
]);
That bundle is later passed into the runtime via:
createDefaultAgentSignalPolicies(...)createAgentSignalRuntime({ policies })Read:
src/server/services/agentSignal/policies/index.tssrc/server/services/agentSignal/policies/analyzeIntent/index.tsUse a source handler when you are interpreting a producer event into semantic signals.
Reference:
src/server/services/agentSignal/policies/analyzeIntent/feedbackSatisfaction.tsPattern:
return defineSourceHandler(
AGENT_SIGNAL_SOURCE_TYPES.agentUserMessage,
'agent.user.message:my-handler',
async (source, ctx): Promise<RuntimeProcessorResult | void> => {
// interpret source payload
// optionally use ctx.runtimeState
return {
signals: [
/* one or more semantic signals */
],
status: 'dispatch',
};
},
);
Write source handlers when:
Use a signal handler when one semantic state should branch into more semantic states or planned actions.
References:
src/server/services/agentSignal/policies/analyzeIntent/feedbackDomain.tssrc/server/services/agentSignal/policies/analyzeIntent/feedbackAction.tsPattern:
return defineSignalHandler(
MY_SIGNAL_TYPE,
'signal.my-policy-router',
async (signal): Promise<RuntimeProcessorResult | void> => {
return {
actions: [
/* planned work */
],
status: 'dispatch',
};
},
);
Use signal handlers for:
Use an action handler when the runtime should do actual work.
Reference:
src/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.tsPattern:
return defineActionHandler(
MY_ACTION_TYPE,
'action.my-policy-executor',
async (action, ctx): Promise<ExecutorResult> => {
// run service/tool/model side effect
// check idempotency if needed
return {
actionId: action.actionId,
attempt: {
completedAt: ctx.now(),
current: 1,
startedAt,
status: 'succeeded',
},
status: 'applied',
};
},
);
Keep these rules:
actionIderrorExecutorResult into built-in result signalsUse this split:
src/server/services/agentSignal/sourceTypes.tssrc/server/services/agentSignal/policies/types.tspackages/agent-signal/src/base/types.tsDo not put app-specific signal catalogs into packages/agent-signal. That package should stay generic and reusable.
Choose source when:
Choose signal when:
Choose action when:
If a handler both interprets meaning and performs side effects, split it. That keeps chains inspectable and testable.
Prefer focused tests near the touched code.
Useful references:
src/server/services/agentSignal/runtime/__tests__/AgentSignalRuntime.test.tssrc/server/services/agentSignal/__tests__/index.integration.test.tssrc/server/services/agentSignal/policies/analyzeIntent/__tests__/*src/server/services/agentSignal/policies/analyzeIntent/actions/__tests__/*Test at the smallest level that proves the behavior: