TS · 10

Signal & SignalType

A Signal is a plain object matching the envelope schema. Its field names stay snake_case (trace_id, parent_id) so the JSON is byte-identical to the Python side. See the envelope spec for the wire-level reference.

interface + codecSignal

createSignal() fills defaults and validates; encode()/decode() round-trip the wire format; reply() builds a child signal sharing the trace_id.

signal.ts
interface Signal {
  v:         string;            // "1"
  id:        string;            // evt_<ULID>
  trace_id:  string;            // trc_<ULID>
  parent_id: string | null;
  type:      SignalType;
  directed:  Directed | null;   // unified addressing
  ts:        string;            // RFC 3339 UTC
  payload:   Json;
  meta:      Json;
}

interface Directed {            // precedence on receive: id > type > capabilities
  id:           string | null;  // direct address (neuron_id or engram_id)
  type:         string | null;  // type routing (neuron type or engram_kind)
  capabilities: string[];       // capability routing
}
// Producer identity rides directed too: an AGENT_OUTPUT's directed.id
// is the neuron that produced it (there is no Signal.neuron field).

createSignal(input: NewSignalInput): Signal;   // fills defaults + validates
validateSignal(signal: Signal): void;          // throws on violation
encode(signal: Signal): Uint8Array;             // UTF-8 JSON bytes
decode(data: Uint8Array | string): Signal;
reply(source: Signal, opts: { type; payload?; directed?; meta? }): Signal;
directedTo(id?, type?, capabilities?): Directed;
const + typeSignalType

A frozen const object plus a union type. AXON_TYPES and SYNAPSE_TYPES are ReadonlySets controlling who may emit which type.

signal-type.ts
// SignalType is a const object (+ a union type), not a TS enum:
const SignalType = {
  TASK: "TASK",
  AGENT_OUTPUT: "AGENT_OUTPUT",
  FINAL: "FINAL",
  ERROR: "ERROR",
  // … TASK_OFFER, BID, TASK_AWARDED, TASK_DECLINED,
  //    THOUGHT_DELTA, PLAN, TOOL_CALL, TOOL_RESULT,
  //    MEMORY_APPEND, ESCALATION, CONSENSUS, CONTEXT_SYNC,
  //    CRITIQUE, CLARIFICATION, PERMISSION,
  //    PERMISSION_DECISION, CLARIFICATION_ANSWER,
  //    RECALL, RECALLED, IMPRINT, IMPRINTED, DISCOVER,
  REGISTER: "REGISTER",
  DEREGISTER: "DEREGISTER",
  HEARTBEAT: "HEARTBEAT",
  // Workflow control  -  cooperative cancellation of a whole trace
  STOP: "STOP",
  STOPPED: "STOPPED",
} as const;
type SignalType = (typeof SignalType)[keyof typeof SignalType];

// Sets controlling who may emit what:
AXON_TYPES: ReadonlySet<SignalType>;
SYNAPSE_TYPES: ReadonlySet<SignalType>;
buildersstopSignal(...) / stoppedSignal(...)

Envelope builders for the workflow-control pair. stopSignal({ traceId, rollback?, reason? }) builds a STOP; stoppedSignal({ traceId, parentId, rolledBack?, cancelled?, compensated?, node? }) builds the ack. You rarely call these directly - dendrite.emitStop / stopTrace build and publish them for you.

Have a feature in mind?

The protocol, SDKs, and CLI are still pre-1.0. If something here is missing, ambiguous, or wrong - open an issue and propose a change. Every breaking change is debated in DECISIONS.md first.