.agents/skills/agent-signal/references/architecture.md
Use this mental model first:
producer
-> emitAgentSignalSourceEvent(...) or enqueueAgentSignalSourceEvent(...)
-> emitSourceEvent(...)
-> dedupe + scope lock + source normalization
-> runtime.emitNormalized(source)
-> source handlers
-> signal handlers
-> action handlers
-> built-in result signals
-> observability projection + persistence
The scheduler is queue-driven, not hard-coded for one policy:
source node
-> matching source handlers
-> dispatch signals/actions
-> matching signal handlers
-> dispatch more signals/actions
-> matching action handlers
-> ExecutorResult
-> signal.action.applied | signal.action.skipped | signal.action.failed
Read:
src/server/services/agentSignal/index.tssrc/server/services/agentSignal/sources/index.tssrc/server/services/agentSignal/runtime/AgentSignalScheduler.tspackages/agent-signalTreat this as the shared semantic core.
It provides:
createSource, createSignal, createActionRuntimeProcessorResult and ExecutorResultRead:
packages/agent-signal/src/base/types.tspackages/agent-signal/src/base/builders.tspackages/agent-signal/src/types/events.tspackages/agent-signal/src/types/builtin.tssrc/server/services/agentSignalTreat this as the server-owned implementation layer.
It owns:
packages/observability-otel/src/modules/agent-signalTreat this as shared OTEL ownership for Agent Signal metrics and tracer instances.
A source is the normalized external fact that started the chain.
Examples:
agent.user.messageruntime.before_stepruntime.after_stepclient.runtime.startbot.message.mergedDefine source payloads in:
src/server/services/agentSignal/sourceTypes.tsBuild normalized sources in:
src/server/services/agentSignal/sources/buildSource.tspackages/agent-signal/src/base/builders.tsA signal is a semantic interpretation. Signals should be reusable and meaning-oriented.
Examples from analyzeIntent:
signal.feedback.satisfactionsignal.feedback.domain.memorysignal.feedback.domain.promptsignal.feedback.domain.skillDefine server-owned signal types in:
src/server/services/agentSignal/policies/types.tsAn action is a concrete side effect the runtime should execute.
Example:
action.user-memory.handleAction handlers usually:
ExecutorResultA policy is an installable bundle of handlers. It is the composition unit that turns the generic runtime into a feature.
Example:
createAnalyzeIntentPolicy(...)"Procedure" is not a first-class type in this runtime. Use the word to describe one end-to-end use case:
When a user asks for "the procedure", document the flow above and point to the exact producer, handlers, and execution entrypoint.
scopeKey is the serialization boundary for related work. It is used for:
Read:
src/server/services/agentSignal/sources/index.tssrc/server/services/agentSignal/runtime/context.tssrc/server/services/agentSignal/constants.tsUse enqueueAgentSignalSourceEvent(...) when the work should stay quiet and out-of-band. That path:
scopeKeyAgentSignalWorkflowrunAgentSignalWorkflowThis is the preferred path when the UI request should finish immediately and the policy can run in the background.
Read:
src/server/workflows/agentSignal/index.tssrc/server/workflows/agentSignal/run.tsanalyzeIntentUse analyzeIntent as the reference chain:
agent.user.message
-> feedback satisfaction source handler
-> signal.feedback.satisfaction
-> feedback domain signal handler
-> signal.feedback.domain.*
-> feedback action planner
-> action.user-memory.handle
-> signal.action.applied | skipped | failed
Read:
src/server/services/agentSignal/policies/analyzeIntent/index.tssrc/server/services/agentSignal/policies/analyzeIntent/feedbackSatisfaction.tssrc/server/services/agentSignal/policies/analyzeIntent/feedbackDomain.tssrc/server/services/agentSignal/policies/analyzeIntent/feedbackAction.tssrc/server/services/agentSignal/policies/analyzeIntent/actions/userMemory.ts