.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
For durable memory, skill, and self-review writes, the action handler can enqueue
an async execAgent run instead of doing the write synchronously:
action.user-memory.handle | action.skill-management.handle | self-iteration source
-> stamp AgentSignal operation marker
-> enqueue execAgent
-> agent.execution.completed with selfIteration finalState
-> completion policy
-> buildSelfIterationReceipts(...)
-> receipt store
The immediate runtime chain still emits signal.action.applied | skipped | failed
for the enqueue result. User-visible memory and skill receipts are projected from
the completion event, not from the enqueue action.
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:
apps/server/src/services/agentSignal/index.tsapps/server/src/services/agentSignal/sources/index.tsapps/server/src/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/source/sourceTypes.tspackages/agent-signal/src/source/sourceEvent.tspackages/agent-signal/src/source/scopeKey.tspackages/agent-signal/src/types/events.tspackages/agent-signal/src/types/builtin.tsapps/server/src/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:
packages/agent-signal/src/source/sourceTypes.tsBuild normalized sources in:
packages/agent-signal/src/source/sourceEvent.tsapps/server/src/services/agentSignal/sources/buildSource.tsapps/server/src/services/agentSignal/sources/renderers/*packages/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:
apps/server/src/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:
apps/server/src/services/agentSignal/sources/index.tsapps/server/src/services/agentSignal/runtime/context.tsapps/server/src/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:
apps/server/src/workflows/agentSignal/index.tsapps/server/src/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 | action.skill-management.handle
-> signal.action.applied | skipped | failed
The current policy also includes optional tool-outcome projection, skill management, deferred completion skill synthesis, nightly review, and completion fan-out:
action.user-memory.handle | action.skill-management.handle
-> enqueue execAgent with AgentSignal marker
-> agent.execution.completed
-> completion policy
-> buildSelfIterationReceipts(...)
-> memory | skill | review receipts
For skill synthesis, an inbound user-message candidate can be parked during the
foreground chain and resumed after agent.execution.completed, where the full
trajectory and tool outcomes are available.
Read:
apps/server/src/services/agentSignal/policies/analyzeIntent/index.tsapps/server/src/services/agentSignal/policies/analyzeIntent/feedbackSatisfaction.tsapps/server/src/services/agentSignal/policies/analyzeIntent/feedbackDomain.tsapps/server/src/services/agentSignal/policies/analyzeIntent/feedbackAction.tsapps/server/src/services/agentSignal/policies/analyzeIntent/actions/userMemory.tsapps/server/src/services/agentSignal/policies/analyzeIntent/actions/skillManagement.tsapps/server/src/services/agentSignal/policies/analyzeIntent/completionSkillSynthesis.tsapps/server/src/services/agentSignal/policies/completionPolicy.tsapps/server/src/services/agentSignal/services/selfIteration/completion/buildSelfIterationReceipts.tsapps/server/src/services/agentSignal/services/selfIteration/completion/selfIterationCompletionHandler.ts