@cosmonapse/sdk 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -45,6 +45,8 @@ declare const SignalType: {
45
45
  readonly IMPRINT: "IMPRINT";
46
46
  readonly IMPRINTED: "IMPRINTED";
47
47
  readonly DISCOVER: "DISCOVER";
48
+ readonly STOP: "STOP";
49
+ readonly STOPPED: "STOPPED";
48
50
  };
49
51
  type SignalType = (typeof SignalType)[keyof typeof SignalType];
50
52
  /** Types the Axon (skill) is allowed to produce. */
@@ -153,6 +155,10 @@ declare function taskSignal(args: {
153
155
  directed?: DirectedInput;
154
156
  contextRef?: string;
155
157
  capabilities?: string[];
158
+ /** Terminal-handler finalize: the worker Dendrite that runs the addressed
159
+ * (or routed) Axon promotes a successful AGENT_OUTPUT by also emitting
160
+ * FINAL on the trace. Set automatically by `dispatch({ scope: "terminal" })`. */
161
+ finalize?: boolean;
156
162
  meta?: Json;
157
163
  }): Signal;
158
164
  /** [A] Wrap a Neuron's raw output in a neutral AGENT_OUTPUT envelope. */
@@ -224,17 +230,23 @@ declare function errorSignal(args: {
224
230
  * [A] A participant connecting to the Synapse and declaring its capabilities.
225
231
  *
226
232
  * Both Neurons and Engrams register the same way: `directed.id` is the
227
- * participant id, `directed.type` its type (neuron type or engram_kind), and
228
- * `directed.capabilities` its capability list. Pass `engram: true` for an
229
- * Engram so receivers record it as an Engram registration. `capabilities` is
230
- * mirrored into the payload for registry stores; when omitted it falls back to
231
- * `directed.capabilities`.
233
+ * participant id, `directed.type` its kind (`neuron_kind` for Neurons,
234
+ * `engram_kind` for Engrams), and `directed.capabilities` its capability list.
235
+ *
236
+ * Every REGISTER carries one universal discriminator, `payload.role`
237
+ * (`"neuron"` or `"engram"`): the single field every consumer (Dendrite
238
+ * registry, Prism, doppler) checks to classify the participant. `role`
239
+ * defaults from the `engram` flag when omitted. The legacy
240
+ * `payload.engram = true` marker is still emitted for Engrams as an alias.
241
+ * `capabilities` is mirrored into the payload for registry stores; when
242
+ * omitted it falls back to `directed.capabilities`.
232
243
  */
233
244
  declare function registerSignal(args: {
234
245
  directed: DirectedInput;
235
246
  capabilities?: string[];
236
247
  version?: string;
237
248
  engram?: boolean;
249
+ role?: "neuron" | "engram";
238
250
  meta?: Json;
239
251
  }): Signal;
240
252
  /** [A] A participant disconnecting from the Synapse. */
@@ -277,6 +289,30 @@ declare function bidSignal(args: {
277
289
  confidence?: number;
278
290
  meta?: Json;
279
291
  }): Signal;
292
+ /** [C] Award a TASK_OFFER to one bidder. The winning Axon's Dendrite treats
293
+ * this exactly like a TASK: `input` is the work payload, `directed`
294
+ * addresses the winner. `winningBid` carries the accepted bid for
295
+ * observability; `finalize` propagates the terminal-handler-finalize tag
296
+ * into the TASK the winner's Dendrite synthesises. */
297
+ declare function taskAwardedSignal(args: {
298
+ traceId: string;
299
+ parentId: string;
300
+ input: Json;
301
+ directed?: DirectedInput;
302
+ winningBid?: Json;
303
+ contextRef?: string;
304
+ finalize?: boolean;
305
+ meta?: Json;
306
+ }): Signal;
307
+ /** [C/A] Decline a TASK_OFFER. Producers emit this for losing bidders after
308
+ * picking a winner (informational); workers may emit it proactively. */
309
+ declare function taskDeclinedSignal(args: {
310
+ traceId: string;
311
+ parentId: string;
312
+ directed?: DirectedInput;
313
+ reason?: string;
314
+ meta?: Json;
315
+ }): Signal;
280
316
  /** [C] Peer review of another Neuron's output. `verdict`: 'pass' | 'fail' | 'revise'. */
281
317
  declare function critiqueSignal(args: {
282
318
  traceId: string;
@@ -422,6 +458,68 @@ declare function imprintedSignal(args: {
422
458
  directed?: DirectedInput;
423
459
  meta?: Json;
424
460
  }): Signal;
461
+ /** [C] Broadcast a cooperative-cancellation request for a whole trace.
462
+ * `rollback` only reverses Engram state via the saga journal; external side
463
+ * effects a Neuron caused through an Axon are not reversible. */
464
+ declare function stopSignal(args: {
465
+ traceId: string;
466
+ parentId?: string | null;
467
+ rollback?: boolean;
468
+ reason?: string;
469
+ directed?: DirectedInput;
470
+ meta?: Json;
471
+ }): Signal;
472
+ /** [C] Ack from one Dendrite that it has quiesced its share of a trace.
473
+ * `parentId` MUST be the STOP's id so the originator can correlate acks. */
474
+ declare function stoppedSignal(args: {
475
+ traceId: string;
476
+ parentId?: string | null;
477
+ node?: string;
478
+ rolledBack?: boolean;
479
+ cancelled?: number;
480
+ compensated?: number;
481
+ directed?: DirectedInput;
482
+ meta?: Json;
483
+ }): Signal;
484
+
485
+ /**
486
+ * @cosmonapse/sdk - retry policy for the dispatch + wait shape.
487
+ *
488
+ * Consumed by `Dendrite.dispatchAndWait({ retry })` and
489
+ * `Dendrite.runWithRetry(...)`. Retry only fits the request/reply shape, where
490
+ * the Dendrite owns the whole arc (dispatch -> wait -> close) and can
491
+ * transparently re-dispatch. The streaming shapes hand the live Pathway to the
492
+ * caller, so retry there would orphan the caller's subscriptions.
493
+ *
494
+ * "Stuck" = no terminal within `timeoutMs` (a TimeoutError from Pathway.wait),
495
+ * the Pathway closing before a terminal (PathwayClosedError), or a returned
496
+ * ERROR flagged `recoverable`. New-trace retries STOP the abandoned attempt
497
+ * before the next try so a stalled worker can't outlive the retry.
498
+ */
499
+
500
+ type RetryOutcome = Signal | Error;
501
+ /** Default predicate: retry on timeout, a Pathway closed before a terminal, or
502
+ * a returned ERROR flagged `recoverable`. FINAL / AGENT_OUTPUT /
503
+ * CLARIFICATION / PERMISSION are never retried. */
504
+ declare function defaultRetryOn(outcome: RetryOutcome): boolean;
505
+ interface RetryStrategy {
506
+ /** Total tries including the first (>= 1). Default 3. */
507
+ maxAttempts?: number;
508
+ /** Per-attempt terminal timeout in ms. Default 30000. */
509
+ timeoutMs?: number;
510
+ /** attempt -> ms to sleep before the next try (0-based). Default 0. */
511
+ backoffMs?: (attempt: number) => number;
512
+ /** outcome -> whether to retry. Default {@link defaultRetryOn}. */
513
+ retryOn?: (outcome: RetryOutcome) => boolean;
514
+ /** Fresh trace per attempt (and STOP the abandoned one). Default true. */
515
+ newTrace?: boolean;
516
+ /** Also roll back the abandoned attempt's Engram writes. Default false. */
517
+ rollbackOnRetry?: boolean;
518
+ /** Hook fired just before a re-dispatch. */
519
+ onRetry?: (attempt: number, outcome: RetryOutcome) => void;
520
+ /** Reason carried on the preemptive STOP. Default "retry". */
521
+ reason?: string;
522
+ }
425
523
 
426
524
  /**
427
525
  * @cosmonapse/sdk - synapse
@@ -984,6 +1082,22 @@ declare class LifecycleHooks<O> {
984
1082
  private spawnLoop;
985
1083
  }
986
1084
 
1085
+ /**
1086
+ * @cosmonapse/sdk - ambient trace context
1087
+ *
1088
+ * The (traceId, parentId) of the TASK currently being handled, carried in an
1089
+ * AsyncLocalStorage so code that runs *inside* a task but without explicit
1090
+ * trace plumbing - e.g. a `detects*` hook calling `dendrite.imprint` -
1091
+ * inherits the task's trace instead of minting a fresh one. The TS
1092
+ * counterpart to Python's `cosmonapse.envelope.trace_context` ContextVar.
1093
+ * Async-safe: each async execution context sees its own binding.
1094
+ */
1095
+ /** Return the ambient (traceId, parentId) of the task being handled, or null. */
1096
+ declare function ambientTrace(): readonly [string, string] | null;
1097
+ /** Bind the ambient (traceId, parentId) for the duration of `fn`. Set by
1098
+ * `Axon.handleTask` around the whole handling pass. */
1099
+ declare function runWithTraceContext<T>(traceId: string, parentId: string, fn: () => T): T;
1100
+
987
1101
  /**
988
1102
  * @cosmonapse/sdk - neuron contract
989
1103
  *
@@ -1005,7 +1119,33 @@ declare class LifecycleHooks<O> {
1005
1119
  * returns - a JSON-serialisable object, a {@link ClarificationOutput}, or a
1006
1120
  * {@link PermissionRequestOutput}.
1007
1121
  */
1008
- type NeuronFn = (input: Json, context: unknown[]) => Promise<Json> | Json;
1122
+ /**
1123
+ * Engram helpers handed to a Neuron as its (optional) third argument when the
1124
+ * hosting Axon declares `engrams: [...]` bindings. The TS counterpart to the
1125
+ * Python SDK's parameter-introspected `recall=` / `imprint=` injection - TS
1126
+ * cannot introspect parameter names, so the helpers ride a context object the
1127
+ * Neuron is free to ignore.
1128
+ */
1129
+ interface NeuronHelpers {
1130
+ recall(name: string, args: {
1131
+ query: Record<string, unknown>;
1132
+ filters?: Record<string, unknown>;
1133
+ contextRef?: string;
1134
+ deadlineMs?: number;
1135
+ recallMode?: "first" | "merge" | "all";
1136
+ minConfidence?: number;
1137
+ meta?: Record<string, unknown>;
1138
+ }): Promise<unknown>;
1139
+ imprint(name: string, args: {
1140
+ op: "add" | "append" | "merge" | "upsert" | "delete";
1141
+ entry: Record<string, unknown>;
1142
+ mergeKey?: string;
1143
+ awaitAck?: boolean;
1144
+ deadlineMs?: number;
1145
+ meta?: Record<string, unknown>;
1146
+ }): Promise<unknown>;
1147
+ }
1148
+ type NeuronFn = (input: Json, context: unknown[], helpers?: NeuronHelpers) => Promise<Json> | Json;
1009
1149
  /**
1010
1150
  * A NeuronFn that also exposes an async `close()` to release any resource it
1011
1151
  * holds (e.g. an MCP subprocess). The Axon calls `close()` automatically when
@@ -1069,6 +1209,163 @@ declare function errorResult(message: string, opts?: {
1069
1209
  /** Type guard: did the Neuron / recogniser return an error marker? */
1070
1210
  declare function isErrorOutput(output: unknown): output is ErrorOutput;
1071
1211
 
1212
+ /**
1213
+ * @cosmonapse/sdk - Engram (shared memory)
1214
+ *
1215
+ * Ported from the Python `cosmonapse.engram` package (see ENGRAM_DESIGN.md).
1216
+ * An Engram is the synapse-side participant that services RECALL / IMPRINT
1217
+ * signals. Engrams are NOT Neurons: they never produce AGENT_OUTPUT. A hosting
1218
+ * Dendrite mounts one via `dendrite.attachEngram(engram)`.
1219
+ *
1220
+ * This module is the value layer: the data types, the `Engram` contract, and
1221
+ * the default in-process `InMemoryEngram`. The SQLite / Postgres backends live
1222
+ * in `engram-sqlite.ts` / `engram-postgres.ts`; the caller-side correlation
1223
+ * table lives in `engram-client.ts`.
1224
+ */
1225
+
1226
+ type RecallMode = "first" | "merge" | "all";
1227
+ type ImprintOp = "add" | "append" | "merge" | "upsert" | "delete";
1228
+ /** One search result. `score` is backend-dependent; relational backends use 1.0. */
1229
+ interface Hit {
1230
+ id: string;
1231
+ entry: Json;
1232
+ score: number;
1233
+ }
1234
+ /** What a recall() call returns to the caller. */
1235
+ interface RecallResult {
1236
+ hits: Hit[];
1237
+ engramIds: string[];
1238
+ truncated: boolean;
1239
+ tookMs: number | null;
1240
+ }
1241
+ /** What an imprint() call returns to the caller. */
1242
+ interface ImprintReceipt {
1243
+ engramId: string;
1244
+ op: string;
1245
+ id: string | null;
1246
+ version: number | null;
1247
+ tookMs: number | null;
1248
+ error: string | null;
1249
+ /** Convenience: true when `error` is null. */
1250
+ ok: boolean;
1251
+ }
1252
+ interface EngramBindingInit {
1253
+ name: string;
1254
+ directedId?: string;
1255
+ directedType?: string;
1256
+ defaultDeadlineMs?: number;
1257
+ defaultRecallMode?: RecallMode;
1258
+ }
1259
+ /**
1260
+ * Declarative wiring of one Engram into an Axon. The Neuron addresses an Engram
1261
+ * by the stable local `name`; `directedId` / `directedType` determine how
1262
+ * RECALL/IMPRINT are routed on the wire. At least one of them must be set.
1263
+ */
1264
+ declare class EngramBinding {
1265
+ readonly name: string;
1266
+ readonly directedId: string | null;
1267
+ readonly directedType: string | null;
1268
+ readonly defaultDeadlineMs: number | null;
1269
+ readonly defaultRecallMode: RecallMode;
1270
+ constructor(init: EngramBindingInit);
1271
+ /** Build the `Directed` addressing this Engram. */
1272
+ toDirected(): Directed;
1273
+ }
1274
+ declare class EngramError extends Error {
1275
+ constructor(message?: string);
1276
+ }
1277
+ /** Raised when a RECALL or IMPRINT deadline elapses with no response. */
1278
+ declare class EngramTimeout extends EngramError {
1279
+ }
1280
+ /** Raised when the containing TASK terminates while a call is in flight. */
1281
+ declare class EngramCancelled extends EngramError {
1282
+ }
1283
+ /** Raised when a Neuron asks for an Engram binding its Axon was not wired to. */
1284
+ declare class EngramNotBound extends EngramError {
1285
+ }
1286
+ /** Raised by a backend that must shed load (surfaces as an IMPRINTED error). */
1287
+ declare class EngramOverloaded extends EngramError {
1288
+ }
1289
+ interface RecallOptions {
1290
+ filters?: Json;
1291
+ contextRef?: string;
1292
+ deadlineMs?: number;
1293
+ minConfidence?: number;
1294
+ }
1295
+ interface ImprintOptions {
1296
+ mergeKey?: string;
1297
+ /** Originating IMPRINT signal id; backends use it for idempotency. */
1298
+ imprintId?: string;
1299
+ /**
1300
+ * Containing TASK's trace. When set, a backend that supports rollback
1301
+ * records the inverse of this write in a per-trace saga journal (via
1302
+ * `sagaRecord`) so the whole trace can be reversed by `compensate`. A
1303
+ * missing traceId means "do not journal" - exactly how `compensate`
1304
+ * replays inverse ops without re-journaling them.
1305
+ */
1306
+ traceId?: string;
1307
+ }
1308
+ /**
1309
+ * Storage wrapper. One backend per Engram instance. Every backend implements
1310
+ * this exact interface; the test suite runs against any conforming Engram.
1311
+ */
1312
+ declare abstract class Engram {
1313
+ abstract engramId: string;
1314
+ abstract engramKind: string;
1315
+ abstract capabilities: string[];
1316
+ version: string | null;
1317
+ /** Open backend resources (DB pool, file handle, ...). */
1318
+ abstract connect(): Promise<void>;
1319
+ /** Release backend resources. */
1320
+ abstract close(): Promise<void>;
1321
+ /** Return matching entries. Empty array on a miss; never throw on a miss. */
1322
+ abstract recall(query: Json, opts?: RecallOptions): Promise<Hit[]>;
1323
+ /** Write to the backend. `op` is one of add | append | merge | upsert | delete. */
1324
+ abstract imprint(op: ImprintOp, entry: Json, opts?: ImprintOptions): Promise<ImprintReceipt>;
1325
+ protected sagaJournal: Map<string, Array<{
1326
+ op: ImprintOp;
1327
+ entry: Json;
1328
+ mergeKey: string | undefined;
1329
+ }>>;
1330
+ protected sagaRecord(traceId: string | undefined, op: ImprintOp, entry: Json, mergeKey?: string): void;
1331
+ /** Reverse every journaled write for `traceId` (LIFO) and discard the
1332
+ * journal. Returns the number of inverse ops applied. Best-effort. Only
1333
+ * Engram state is reversed - external side effects are out of scope. */
1334
+ compensate(traceId: string): Promise<number>;
1335
+ /** Discard the trace's saga journal without reversing anything. Called at
1336
+ * the workflow commit point (FINAL/ERROR on the trace). */
1337
+ commit(traceId: string): Promise<void>;
1338
+ /** Return false if this Engram cannot satisfy the query. Default: serve all. */
1339
+ canServe(_query: Json): Promise<boolean>;
1340
+ }
1341
+ interface InMemoryEngramInit {
1342
+ engramId?: string;
1343
+ engramKind?: string;
1344
+ capabilities?: string[];
1345
+ version?: string | null;
1346
+ }
1347
+ /** Dict-backed Engram. The default backend for tests and local dev. */
1348
+ declare class InMemoryEngram extends Engram {
1349
+ engramId: string;
1350
+ engramKind: string;
1351
+ capabilities: string[];
1352
+ private entries;
1353
+ private byMergeKey;
1354
+ private imprintSeen;
1355
+ constructor(init?: InMemoryEngramInit);
1356
+ connect(): Promise<void>;
1357
+ close(): Promise<void>;
1358
+ recall(query: Json, opts?: RecallOptions): Promise<Hit[]>;
1359
+ imprint(op: ImprintOp, entry: Json, opts?: ImprintOptions): Promise<ImprintReceipt>;
1360
+ /** Test/debug helper - NOT part of the Engram contract. */
1361
+ snapshot(): Json[];
1362
+ private makeEntry;
1363
+ private store;
1364
+ private evict;
1365
+ }
1366
+ /** Conservative deep merge: dicts merge, lists concat-dedup, scalars overwrite. */
1367
+ declare function deepMerge(base: unknown, incoming: unknown): unknown;
1368
+
1072
1369
  /**
1073
1370
  * @cosmonapse/sdk - MCP-server Neuron
1074
1371
  *
@@ -1146,6 +1443,18 @@ declare function mcpNeuron(opts: McpNeuronOptions): CloseableNeuronFn;
1146
1443
  * Output: `{ response: "<text>", meta: <raw provider payload> }`.
1147
1444
  */
1148
1445
 
1446
+ type Dict = Record<string, unknown>;
1447
+ /**
1448
+ * Render the close-the-loop TASK shapes into a prompt continuation.
1449
+ *
1450
+ * `respondToClarification` re-dispatches `{ clarification: { question,
1451
+ * answer, ... } }` and `respondToPermission` re-dispatches `{ permission:
1452
+ * { action, granted, reason?, ttl_ms?, ... } }`. Built-in LLM Neurons have no
1453
+ * native understanding of those keys, so without this rendering every default
1454
+ * close-the-loop flow died with "expects 'prompt' or 'messages'". Custom
1455
+ * NeuronFns can read the raw objects directly and never hit this path.
1456
+ */
1457
+ declare function followupPrompt(input: Dict): string | null;
1149
1458
  interface OllamaNeuronOptions {
1150
1459
  /** Ollama model tag, e.g. "llama3", "mistral", "phi3". */
1151
1460
  model: string;
@@ -1282,152 +1591,549 @@ declare function neuron(source: "anthropic", opts: AnthropicNeuronOptions): Neur
1282
1591
  declare function neuron(source: OpenAICompatAlias, opts?: OpenAICompatNeuronOptions): NeuronFn;
1283
1592
 
1284
1593
  /**
1285
- * @cosmonapse/sdk - dendrite
1594
+ * @cosmonapse/sdk - pathway
1286
1595
  *
1287
- * The synapse-side participant, ported from `cosmonapse.dendrite`.
1596
+ * The Pathway primitive - a per-trace event handle, ported from
1597
+ * `cosmonapse.pathway`.
1288
1598
  *
1289
- * Construction is minimal: only `synapse` is required. Everything else is
1290
- * opt-in:
1291
- * - Attach Axons -> subscribes to TASK, emits REGISTER / HEARTBEAT /
1292
- * DEREGISTER, routes inbound TASKs to the right Axon.
1293
- * - Register handlers -> subscribes to that AXON_TYPE and dispatches.
1294
- * - heartbeatMs = 0 -> the heartbeat loop never starts.
1599
+ * A Pathway is the client-side observation surface for one logical workflow,
1600
+ * identified by its `trace_id`. Open one with
1601
+ * `dendrite.dispatch({ neuron, input })` (you become the *originator*), or
1602
+ * `dendrite.observePathway(traceId)` to watch a trace another peer started
1603
+ * (*observer*). Every Signal whose `trace_id` matches is delivered into it.
1295
1604
  *
1296
- * The Dendrite does NOT own the Synapse - the caller builds and closes it.
1605
+ * Three consumption shapes on the same primitive:
1297
1606
  *
1298
- * There is no separate Cortex class: every Dendrite has dispatchTask /
1299
- * emitFinal / emitError / emit plus the inbound-handler hooks. `Cortex` is
1300
- * kept as a back-compat alias.
1607
+ * - `await pathway.wait()` - resolve on the next AGENT_OUTPUT /
1608
+ * CLARIFICATION / PERMISSION / ERROR / FINAL (request/reply shape).
1609
+ * - `pathway.on(SignalType.X, fn)` - callback per matching Signal
1610
+ * (reactive shape).
1611
+ * - `for await (const sig of pathway)` - iterate every Signal until close
1612
+ * (streaming shape).
1301
1613
  *
1302
- * Lifecycle: call `await dendrite.start()` / `await dendrite.stop()`, or use
1303
- * `await using dendrite = new Dendrite({...}); await dendrite.start();` - the
1304
- * Symbol.asyncDispose implementation calls stop() automatically when the scope
1305
- * exits. This is the TS counterpart to Python's `async with dendrite:`.
1614
+ * The shapes compose: callbacks, iteration and `wait()` each observe every
1615
+ * Signal independently - broadcasting, not draining a queue.
1306
1616
  *
1307
- * LifecycleHooks (onConnect / onRefresh / onSchedule) are wired in: connect
1308
- * hooks fire and schedule loops launch at the end of start(); refresh hooks
1309
- * fire on every heartbeat tick and whenever a REGISTER / DEREGISTER / HEARTBEAT
1310
- * updates the registry; all loops stop in stop(). Attached Axons' hooks are
1311
- * driven alongside the Dendrite's own.
1617
+ * Lifecycle: auto-closes on the first FINAL or ERROR; close explicitly with
1618
+ * `await pathway.close()`; the owning Dendrite closes survivors on `stop()`.
1312
1619
  */
1313
1620
 
1314
- declare global {
1315
- interface SymbolConstructor {
1316
- readonly asyncDispose: unique symbol;
1317
- }
1318
- }
1319
- type SignalHandler = (signal: Signal) => void | Promise<void>;
1320
- /** Raised when an emit violates the protocol (e.g. emitting an Axon-only type). */
1321
- declare class DendriteProtocolError extends Error {
1621
+ /** Signals that auto-close a Pathway. AGENT_OUTPUT alone does NOT close it
1622
+ * because a streaming workflow may produce several before finalising. */
1623
+ declare const TERMINAL_TYPES: ReadonlySet<SignalType>;
1624
+ /**
1625
+ * Signal types that flow through a Pathway. Excludes management types
1626
+ * (REGISTER / DEREGISTER / HEARTBEAT / DISCOVER - own trace_id space) and
1627
+ * TASK (the originator knows it dispatched; excluding TASK also avoids a
1628
+ * double subscription on Dendrites that both host Axons and dispatch).
1629
+ */
1630
+ declare const PATHWAY_TYPES: ReadonlySet<SignalType>;
1631
+ type PathwaySignalHandler = (signal: Signal) => void | Promise<void>;
1632
+ type PathwayCloseHook = (pathway: Pathway) => void | Promise<void>;
1633
+ /** Raised when `wait()` is called on (or interrupted by) a closed Pathway. */
1634
+ declare class PathwayClosedError extends Error {
1322
1635
  constructor(message: string);
1323
1636
  }
1324
-
1325
- interface DendriteOptions {
1326
- synapse: Synapse;
1327
- /** Optional registry. When set, the Dendrite mirrors its own Axons and
1328
- * tracks the namespace-wide Neuron view from REGISTER/DEREGISTER/HEARTBEAT. */
1329
- registryStore?: RegistryStore;
1330
- namespace?: string;
1331
- dendriteId?: string;
1332
- /** Per-attached-Axon heartbeat interval in ms. 0 disables the loop. */
1333
- heartbeatMs?: number;
1334
- /** Re-emit REGISTER on every heartbeat tick so late joiners catch up. */
1335
- reregisterOnHeartbeat?: boolean;
1336
- }
1337
- declare class Dendrite {
1338
- readonly synapse: Synapse;
1339
- readonly registryStore: RegistryStore | null;
1340
- readonly namespace: string;
1341
- readonly dendriteId: string;
1342
- private readonly heartbeatMs;
1343
- private readonly reregisterOnHeartbeat;
1344
- private readonly _axons;
1637
+ type PathwayRole = "originator" | "observer";
1638
+ type PathwayScope = "all" | "terminal";
1639
+ interface PathwayOptions {
1640
+ traceId: string;
1641
+ /** Per-operation correlation key: when set, the owning Dendrite routes
1642
+ * inbound Signals here by `signal.parent_id === parentId` (request/reply)
1643
+ * instead of by trace. This is what lets request/reply clients (e.g. an
1644
+ * EngramClient, `awaitDecision`) be thin wrappers over a Pathway. */
1645
+ parentId?: string | null;
1646
+ role?: PathwayRole;
1647
+ onClose?: PathwayCloseHook;
1648
+ /** "all" (default): every PATHWAY_TYPES Signal on the trace. "terminal":
1649
+ * only FINAL / ERROR / CLARIFICATION / PERMISSION - registered `on()`
1650
+ * callbacks still fire for scoped-out types (explicit interest). */
1651
+ scope?: PathwayScope;
1652
+ }
1653
+ declare class Pathway implements AsyncIterable<Signal> {
1654
+ readonly traceId: string;
1655
+ readonly parentId: string | null;
1656
+ readonly role: PathwayRole;
1657
+ readonly scope: PathwayScope;
1658
+ private readonly scopeFilter;
1659
+ private readonly onCloseHook;
1345
1660
  private readonly handlers;
1346
- private taskSub;
1347
- private readonly inboundSubs;
1348
- private heartbeatTimer;
1349
- private heartbeatStopped;
1350
- private running;
1351
- /** @internal - lifecycle hooks for this Dendrite. */
1352
- readonly hooks: LifecycleHooks<Dendrite>;
1353
- constructor(opts: DendriteOptions);
1354
- get axons(): ReadonlyMap<string, Axon>;
1355
- axon(neuronId: string): Axon | undefined;
1356
- attachAxon(axon: Axon): void;
1357
- private on;
1358
- onAgentOutput(fn: SignalHandler): SignalHandler;
1359
- onClarification(fn: SignalHandler): SignalHandler;
1360
- /**
1361
- * Register a handler fired on inbound PERMISSION requests - the *answering*
1362
- * side. A central Cortex or a peer Dendrite evaluates the request (often
1363
- * consulting an Engram of standing grants, keyed per-neuron) and replies via
1364
- * {@link respondToPermission} (re-dispatch a TASK with the verdict) or
1365
- * {@link grantPermission} / {@link denyPermission} (emit a discrete
1366
- * PERMISSION_DECISION). It may also imprint the decision into an Engram so
1367
- * future recalls hit.
1368
- */
1369
- onPermission(fn: SignalHandler): SignalHandler;
1370
- onErrorSignal(fn: SignalHandler): SignalHandler;
1371
- onRegister(fn: SignalHandler): SignalHandler;
1372
- onDeregister(fn: SignalHandler): SignalHandler;
1373
- onHeartbeat(fn: SignalHandler): SignalHandler;
1374
- /** Register a fire-once handler called after start() completes. */
1375
- onConnect(fn: ConnectHook<Dendrite>): ConnectHook<Dendrite>;
1376
- /** Register a handler called whenever this Dendrite's state refreshes. */
1377
- onRefresh(fn: RefreshHook<Dendrite>): RefreshHook<Dendrite>;
1378
- /** Register a periodic handler that runs every `everyMs` until stop(). */
1379
- onSchedule(everyMs: number, fn: ScheduleHook<Dendrite>): ScheduleHook<Dendrite>;
1380
- /** Manually fire a refresh event (reason defaults to "manual"). */
1381
- refresh(opts?: {
1382
- reason?: string;
1383
- neuronId?: string | null;
1384
- extra?: Record<string, unknown>;
1385
- }): Promise<void>;
1386
- start(): Promise<void>;
1387
- /**
1388
- * Heartbeat as a self-scheduling async loop rather than `setInterval`.
1389
- *
1390
- * Why not setInterval: it fires on a fixed wall-clock cadence regardless of
1391
- * whether the previous tick finished, so under load ticks overlap and the
1392
- * effective interval drifts; and because the callback is sync, any rejection
1393
- * from the async work inside is an unhandled rejection that setInterval
1394
- * silently drops. Here each tick is fully awaited, its errors are caught, and
1395
- * only then is the next tick scheduled - matching the Python SDK's
1396
- * asyncio.Task semantics (structured error handling + clean cancellation).
1397
- */
1398
- private startHeartbeatLoop;
1399
- stop(reason?: string): Promise<void>;
1400
- /**
1401
- * Explicit-resource-management hook so a Dendrite can be used with
1402
- * `await using` - the TS equivalent of Python's `async with dendrite:`.
1403
- *
1404
- * ```ts
1405
- * await using dendrite = new Dendrite({ synapse });
1406
- * dendrite.attachAxon(axon);
1407
- * await dendrite.start();
1408
- * // ... stop() runs automatically when this scope exits, even on throw.
1409
- * ```
1410
- *
1411
- * Idempotent: stop() is a no-op if the Dendrite was never started or already
1412
- * stopped. As with stop(), the caller still owns the Synapse/registry store.
1413
- */
1661
+ private waiters;
1662
+ private buffered;
1663
+ private closed_;
1664
+ private iterPush;
1665
+ private iterPull;
1666
+ constructor(opts: PathwayOptions);
1667
+ get closed(): boolean;
1668
+ /** Resolve on the next AGENT_OUTPUT, CLARIFICATION, PERMISSION, ERROR or
1669
+ * FINAL. Rejects with PathwayClosedError if the Pathway closes first, and
1670
+ * with a TimeoutError-named Error if `timeoutMs` elapses. */
1671
+ wait(timeoutMs?: number): Promise<Signal>;
1672
+ /** Resolve on the next Signal of the given type. */
1673
+ waitFor(type: SignalType, timeoutMs?: number): Promise<Signal>;
1674
+ private waitForTypes;
1675
+ /** Register a callback fired for each Signal of the given type. */
1676
+ on(type: SignalType, fn: PathwaySignalHandler): PathwaySignalHandler;
1677
+ [Symbol.asyncIterator](): AsyncIterator<Signal>;
1678
+ private iterEmit;
1679
+ /** Close the Pathway. Idempotent. Pending waits reject with
1680
+ * PathwayClosedError; iteration completes; the onClose hook fires once. */
1681
+ close(): Promise<void>;
1682
+ /** `await using pathway = ...` support. */
1414
1683
  [Symbol.asyncDispose](): Promise<void>;
1415
- private requireStore;
1416
- /** All known records, optionally filtered (live records only by default). */
1417
- registrySnapshot(opts?: ListOptions): Promise<NeuronRecord[]>;
1418
- /** Live (non-deregistered) records, optionally filtered by capability. */
1419
- findNeurons(opts?: {
1684
+ /** @internal */
1685
+ _deliver(signal: Signal): Promise<void>;
1686
+ private fireHandlers;
1687
+ }
1688
+
1689
+ /**
1690
+ * @cosmonapse/sdk - Engram caller-side client
1691
+ *
1692
+ * Ported from `cosmonapse.engram.client`. EngramClient is the caller-side
1693
+ * bridge: it builds RECALL / IMPRINT envelopes, publishes them, registers
1694
+ * pending promises keyed by the envelope id, and resolves them when a matching
1695
+ * RECALLED / IMPRINTED arrives (correlated by `parent_id`). It enforces
1696
+ * per-call deadlines and cancels in-flight calls when a TASK terminates.
1697
+ *
1698
+ * To avoid an import cycle with the Dendrite, the client depends only on a
1699
+ * minimal {@link EngramPublisher} (the Dendrite implements it by passing
1700
+ * itself in). The Dendrite owns the subscription to RECALLED / IMPRINTED and
1701
+ * calls `deliver(signal)` for each inbound.
1702
+ */
1703
+
1704
+ /** The slice of the Dendrite the client needs: a way to put a Signal on the wire. */
1705
+ interface EngramPublisher {
1706
+ publish(signal: Signal): Promise<void>;
1707
+ }
1708
+ interface RecallCallArgs {
1709
+ binding?: EngramBinding;
1710
+ engramId?: string;
1711
+ engramKind?: string;
1712
+ query: Json;
1713
+ filters?: Json;
1714
+ contextRef?: string;
1715
+ deadlineMs?: number;
1716
+ recallMode?: RecallMode;
1717
+ minConfidence?: number;
1718
+ traceId: string;
1719
+ parentId: string;
1720
+ meta?: Json;
1721
+ }
1722
+ interface ImprintCallArgs {
1723
+ binding?: EngramBinding;
1724
+ engramId?: string;
1725
+ engramKind?: string;
1726
+ op: ImprintOp;
1727
+ entry: Json;
1728
+ mergeKey?: string;
1729
+ awaitAck?: boolean;
1730
+ deadlineMs?: number;
1731
+ traceId: string;
1732
+ parentId: string;
1733
+ meta?: Json;
1734
+ }
1735
+ declare class EngramClient {
1736
+ private readonly publisher;
1737
+ private pendingRecalls;
1738
+ private pendingImprints;
1739
+ private byTrace;
1740
+ constructor(publisher: EngramPublisher);
1741
+ recall(args: RecallCallArgs): Promise<RecallResult>;
1742
+ imprint(args: ImprintCallArgs): Promise<ImprintReceipt | null>;
1743
+ /** Match RECALLED / IMPRINTED by parent_id and resolve pendings. */
1744
+ deliver(sig: Signal): void;
1745
+ /** Cancel every in-flight recall/imprint on a trace (FINAL/ERROR or shutdown). */
1746
+ cancelTrace(traceId: string): void;
1747
+ cancelAll(): void;
1748
+ private onRecallDeadline;
1749
+ private onImprintDeadline;
1750
+ private track;
1751
+ private cleanupRecall;
1752
+ private cleanupImprint;
1753
+ private discardTrace;
1754
+ }
1755
+
1756
+ /**
1757
+ * @cosmonapse/sdk - dendrite
1758
+ *
1759
+ * The synapse-side participant, ported from `cosmonapse.dendrite`.
1760
+ *
1761
+ * Construction is minimal: only `synapse` is required. Everything else is
1762
+ * opt-in:
1763
+ * - Attach Axons -> subscribes to TASK (addressed broadcast) and the
1764
+ * capability-routed subject (queue-grouped), emits
1765
+ * REGISTER / HEARTBEAT / DEREGISTER, routes inbound
1766
+ * TASKs to the right Axon.
1767
+ * - Register handlers -> subscribes to that SignalType and dispatches.
1768
+ * - heartbeatMs = 0 -> the heartbeat loop never starts.
1769
+ *
1770
+ * The Dendrite does NOT own the Synapse - the caller builds and closes it.
1771
+ *
1772
+ * There is no separate Cortex class: every Dendrite has the dispatch family
1773
+ * (dispatch / dispatchAndWait / dispatchAndSubscribe / dispatchOffer /
1774
+ * dispatchTask), emitFinal / emitError / the cognition emit helpers, plus the
1775
+ * inbound-handler hooks. `Cortex` is kept as a back-compat alias.
1776
+ *
1777
+ * Unified dispatch: `dispatch()` returns a {@link Pathway} scoped to the
1778
+ * trace - await it, attach callbacks, or iterate. `scope: "terminal"`
1779
+ * additionally tags the TASK with `payload.finalize` (terminal-handler
1780
+ * finalize): the worker Dendrite that runs the Axon promotes a successful
1781
+ * AGENT_OUTPUT by also emitting FINAL, so terminal-scoped Pathways resolve
1782
+ * against stock workers.
1783
+ *
1784
+ * Lifecycle: `await dendrite.start()` / `await dendrite.stop()`, or
1785
+ * `await using dendrite = new Dendrite({...})`.
1786
+ */
1787
+
1788
+ declare global {
1789
+ interface SymbolConstructor {
1790
+ readonly asyncDispose: unique symbol;
1791
+ }
1792
+ }
1793
+ type SignalHandler = (signal: Signal) => void | Promise<void>;
1794
+ /** Optional narrowing for handler registration - the TS counterpart to the
1795
+ * Python decorators' `neuron=` / `capability=` / `trace_id=` kwargs. */
1796
+ interface HandlerFilter {
1797
+ neuron?: string;
1798
+ capability?: string;
1799
+ traceId?: string;
1800
+ }
1801
+ /** Raised when an emit violates the protocol (e.g. emitting an Axon-only type). */
1802
+ declare class DendriteProtocolError extends Error {
1803
+ constructor(message: string);
1804
+ }
1805
+
1806
+ type DendriteRole = "orchestrator" | "worker";
1807
+ interface DendriteOptions {
1808
+ synapse: Synapse;
1809
+ /** Optional registry. When set, the Dendrite mirrors its own Axons and
1810
+ * tracks the namespace-wide Neuron view from REGISTER/DEREGISTER/HEARTBEAT. */
1811
+ registryStore?: RegistryStore;
1812
+ namespace?: string;
1813
+ dendriteId?: string;
1814
+ /** Per-attached-Axon heartbeat interval in ms. 0 disables the loop. */
1815
+ heartbeatMs?: number;
1816
+ /** Re-emit REGISTER on every heartbeat tick so late joiners catch up. */
1817
+ reregisterOnHeartbeat?: boolean;
1818
+ /** "orchestrator" (default, may dispatch TASKs) or "worker" (hosts Axons;
1819
+ * TASK initiation is refused, everything else is role-agnostic). */
1820
+ role?: DendriteRole;
1821
+ /** Default bidder (default true): a Dendrite hosting Axons answers
1822
+ * TASK_OFFERs whose capability set a hosted Axon covers, with cost 0 /
1823
+ * confidence 1 - unless a user onTaskOffer handler is registered, which
1824
+ * suppresses the default bidder entirely. */
1825
+ autoBid?: boolean;
1826
+ /** Liveness: a registered Neuron whose last heartbeat is older than this is
1827
+ * marked deregistered by the heartbeat loop's sweep. Default: 3 heartbeat
1828
+ * intervals; 0 disables. */
1829
+ staleAfterMs?: number;
1830
+ }
1831
+ interface DispatchArgs {
1832
+ neuron?: string;
1833
+ input: Json;
1834
+ traceId?: string;
1835
+ parentId?: string | null;
1836
+ contextRef?: string;
1837
+ capabilities?: string[];
1838
+ meta?: Json;
1839
+ }
1840
+ declare class Dendrite {
1841
+ readonly synapse: Synapse;
1842
+ readonly registryStore: RegistryStore | null;
1843
+ readonly namespace: string;
1844
+ readonly dendriteId: string;
1845
+ readonly role: DendriteRole;
1846
+ private readonly heartbeatMs;
1847
+ private readonly reregisterOnHeartbeat;
1848
+ private readonly autoBid;
1849
+ private readonly staleAfterMs;
1850
+ private readonly _axons;
1851
+ private readonly handlers;
1852
+ private taskSub;
1853
+ private routedTaskSub;
1854
+ private readonly inboundSubs;
1855
+ private readonly inflightSubs;
1856
+ private readonly pendingSubs;
1857
+ /** Recently seen CLARIFICATION_ANSWER / PERMISSION_DECISION signals keyed
1858
+ * by parent_id, so {@link awaitDecision} can serve an answer that arrived
1859
+ * before it was called (an in-process synapse can deliver the whole
1860
+ * request->answer chain within the original publish). Bounded FIFO. */
1861
+ private readonly recentDecisions;
1862
+ /** Hosted Engrams keyed by engramId, plus a kind index so RECALL/IMPRINT
1863
+ * addressed by engramKind reach every matching host. */
1864
+ private readonly _engrams;
1865
+ private readonly traceAborts;
1866
+ private readonly engramKindIndex;
1867
+ /** Engrams learned from peer REGISTER signals (possibly out-of-process). */
1868
+ private readonly _engramRegistrations;
1869
+ private readonly engramRegKindIndex;
1870
+ /** Caller-side correlation table for RECALL/IMPRINT awaiting
1871
+ * RECALLED/IMPRINTED. The Dendrite owns the subscriptions and feeds it. */
1872
+ readonly engramClient: EngramClient;
1873
+ private heartbeatTimer;
1874
+ private heartbeatStopped;
1875
+ private running;
1876
+ /** Open Pathways keyed by trace_id (dispatch / observePathway). */
1877
+ private readonly pathways;
1878
+ /** Per-operation Pathways keyed by the issuing request's id (matched
1879
+ * against inbound parent_id) - the generic request/reply primitive
1880
+ * behind awaitDecision (and a future EngramClient wiring). */
1881
+ private readonly opPathways;
1882
+ /** @internal - lifecycle hooks for this Dendrite. */
1883
+ readonly hooks: LifecycleHooks<Dendrite>;
1884
+ constructor(opts: DendriteOptions);
1885
+ get axons(): ReadonlyMap<string, Axon>;
1886
+ axon(neuronId: string): Axon | undefined;
1887
+ /** Aggregate of every attached Axon's capabilities, deduplicated + sorted. */
1888
+ get capabilities(): string[];
1889
+ /** Canonical queue-group name for this Dendrite's aggregate caps, or null
1890
+ * when no Axons are attached. Identical Dendrites share a group. */
1891
+ private capQueueGroup;
1892
+ private requireOrchestrator;
1893
+ /**
1894
+ * Attach an Axon to a *stopped* Dendrite. Throws if the Dendrite is
1895
+ * running - a running Dendrite needs the async activation path
1896
+ * (subscriptions, queue-group refresh, REGISTER): use
1897
+ * `await dendrite.addAxon(axon)` instead, which works in both states.
1898
+ */
1899
+ attachAxon(axon: Axon): void;
1900
+ private attachAxonRecord;
1901
+ /**
1902
+ * Attach an Axon; if the Dendrite is running, activate it live: ensure the
1903
+ * addressed + routed TASK subscriptions exist (re-keying the routed queue
1904
+ * group for the new aggregate cap profile), subscribe TASK_AWARDED /
1905
+ * DISCOVER (and TASK_OFFER when autoBid), mirror to the registry store,
1906
+ * emit REGISTER, and fire the Axon's onConnect hooks.
1907
+ */
1908
+ addAxon(axon: Axon): Promise<void>;
1909
+ /** Detach an Axon. If running: deregister, tear down its hooks, and re-key
1910
+ * (or drop) the TASK subscriptions for the changed cap profile. */
1911
+ detachAxon(neuronId: string, opts?: {
1912
+ reason?: string;
1913
+ }): Promise<void>;
1914
+ /**
1915
+ * Mount an Engram on this Dendrite. After attachment (and start), the
1916
+ * Dendrite subscribes to RECALL/IMPRINT, routes Signals addressed to
1917
+ * `engram.engramId` or matching `engram.engramKind` to the instance, and
1918
+ * announces it on the Synapse with an engram REGISTER. The Engram still
1919
+ * owns its backend lifecycle: `connect()` on start(), `close()` on stop().
1920
+ * When the Dendrite is already running, the backend is connected and the
1921
+ * subscriptions/REGISTER are established immediately.
1922
+ */
1923
+ attachEngram(engram: Engram): Promise<void>;
1924
+ /** Remove a hosted Engram. Closes its backend if the Dendrite is running. */
1925
+ detachEngram(engramId: string): Promise<void>;
1926
+ get engrams(): ReadonlyMap<string, Engram>;
1927
+ /** Engrams learned via REGISTER, keyed by directed.id (or directed.type
1928
+ * when no id), including in-process ones. */
1929
+ get engramRegistrations(): ReadonlyMap<string, Directed>;
1930
+ /** True when an Engram with this id/kind is reachable - hosted
1931
+ * in-process or learned from a peer's REGISTER. */
1932
+ isEngramKnown(opts: {
1933
+ engramId?: string;
1934
+ engramKind?: string;
1935
+ }): boolean;
1936
+ /** (Re)subscribe the capability-routed TASK subscription so its queue
1937
+ * group matches the *current* aggregate cap profile. */
1938
+ private refreshRoutedSub;
1939
+ private wrapWithFilter;
1940
+ private neuronHasCapability;
1941
+ private on;
1942
+ /**
1943
+ * Generic handler registration for *any* SignalType - the escape hatch
1944
+ * behind every named `on*` helper. New protocol types are observable the
1945
+ * day they exist. Supports the same filters as the named helpers.
1946
+ */
1947
+ onSignal(type: SignalType, fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1948
+ /** Await until inbound subscriptions exist for `types` - removes the
1949
+ * late-registration race deterministically. Idempotent. */
1950
+ ensureSubscribed(...types: SignalType[]): Promise<void>;
1951
+ onAgentOutput(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1952
+ onClarification(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1953
+ /**
1954
+ * Register a handler fired on inbound PERMISSION requests - the *answering*
1955
+ * side. Reply via {@link respondToPermission} (re-dispatch a TASK with the
1956
+ * verdict) or {@link grantPermission} / {@link denyPermission} (emit a
1957
+ * discrete PERMISSION_DECISION).
1958
+ */
1959
+ onPermission(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1960
+ onErrorSignal(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1961
+ /** Register a handler fired on FINAL - workflow conclusion. */
1962
+ onFinal(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1963
+ /** Observe inbound TASKs (audit/logging). Observation only - Axon routing
1964
+ * happens on its own subscription and is unaffected. */
1965
+ onTaskSignal(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1966
+ onRegister(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1967
+ onDeregister(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1968
+ onHeartbeat(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1969
+ onDiscover(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1970
+ onPlan(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1971
+ onThoughtDelta(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1972
+ onToolCall(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1973
+ onToolResult(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1974
+ onMemoryAppend(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1975
+ onCritique(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1976
+ onEscalation(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1977
+ onConsensus(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1978
+ onContextSync(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1979
+ /** Workers use this to evaluate offers and call {@link bid} to compete.
1980
+ * Registering it suppresses the default auto-bidder entirely. */
1981
+ onTaskOffer(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1982
+ /** Observe BIDs (market observability). dispatchOffer collects its own. */
1983
+ onBid(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1984
+ /** Observe TASK_AWARDED. The hosting Dendrite's award-to-TASK synthesis is
1985
+ * unaffected by handlers here. */
1986
+ onTaskAwarded(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1987
+ /** e.g. release a reservation made while bidding. */
1988
+ onTaskDeclined(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1989
+ /** Fired on CLARIFICATION_ANSWER - correlate by `sig.parent_id === the
1990
+ * CLARIFICATION's id`, or use {@link awaitDecision}. */
1991
+ onClarificationAnswer(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1992
+ /** Fired on PERMISSION_DECISION - correlate by parent_id, or use
1993
+ * {@link awaitDecision}. */
1994
+ onPermissionDecision(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1995
+ onRecalled(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1996
+ onImprinted(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1997
+ onRecallSignal(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1998
+ onImprintSignal(fn: SignalHandler, filter?: HandlerFilter): SignalHandler;
1999
+ private static readonly TRACE_DEFAULT_TYPES;
2000
+ /** Register one handler for multiple types narrowed to a single workflow. */
2001
+ onTrace(traceId: string, fn: SignalHandler, types?: SignalType[]): SignalHandler;
2002
+ onConnect(fn: ConnectHook<Dendrite>): ConnectHook<Dendrite>;
2003
+ onRefresh(fn: RefreshHook<Dendrite>): RefreshHook<Dendrite>;
2004
+ onSchedule(everyMs: number, fn: ScheduleHook<Dendrite>): ScheduleHook<Dendrite>;
2005
+ refresh(opts?: {
2006
+ reason?: string;
2007
+ neuronId?: string | null;
2008
+ extra?: Record<string, unknown>;
2009
+ }): Promise<void>;
2010
+ start(): Promise<void>;
2011
+ private startHeartbeatLoop;
2012
+ stop(reason?: string): Promise<void>;
2013
+ [Symbol.asyncDispose](): Promise<void>;
2014
+ private requireStore;
2015
+ /** All known records, optionally filtered (live records only by default). */
2016
+ registrySnapshot(opts?: ListOptions & {
2017
+ maxAgeMs?: number;
2018
+ }): Promise<NeuronRecord[]>;
2019
+ /** Live (non-deregistered) records, optionally filtered by capability.
2020
+ * `maxAgeMs` additionally drops records whose last heartbeat is older -
2021
+ * a read-side freshness guard when the background sweep can't be relied on. */
2022
+ findNeurons(opts?: {
1420
2023
  capability?: string;
2024
+ maxAgeMs?: number;
1421
2025
  }): Promise<NeuronRecord[]>;
1422
- dispatchTask(args: {
1423
- neuron: string;
2026
+ private static filterFresh;
2027
+ /**
2028
+ * Emit a TASK. Addressed (`neuron`) or capability-routed (`capabilities`)
2029
+ * - at least one must be set. `finalize: true` tags the TASK so the
2030
+ * handling worker Dendrite promotes a successful AGENT_OUTPUT to FINAL
2031
+ * (terminal-handler finalize - see {@link dispatch}). Only
2032
+ * orchestrator-role Dendrites may dispatch.
2033
+ */
2034
+ dispatchTask(args: DispatchArgs & {
2035
+ finalize?: boolean;
2036
+ }): Promise<Signal>;
2037
+ /** Publish a TASK to the right subject for its routing mode. Addressed ->
2038
+ * broadcast subject; capability-routed -> queue-grouped routed subject. */
2039
+ private publishTask;
2040
+ /**
2041
+ * Dispatch a TASK and return a {@link Pathway} scoped to its trace -
2042
+ * await it, attach callbacks, or iterate:
2043
+ *
2044
+ * ```ts
2045
+ * // 1) sequential / request-reply
2046
+ * const pw = await orch.dispatch({ neuron: "summarize", input });
2047
+ * const out = await pw.wait();
2048
+ *
2049
+ * // 2) reactive
2050
+ * pw.on(SignalType.PLAN, (sig) => { ... });
2051
+ *
2052
+ * // 3) streaming
2053
+ * for await (const sig of pw) { ... }
2054
+ * ```
2055
+ *
2056
+ * `capabilities` instead of `neuron` gives event-driven dispatch. Delivery
2057
+ * is exactly-once within a queue group (identical cap profiles) but
2058
+ * **at-least-once across heterogeneous groups** - use
2059
+ * {@link dispatchOffer} when overlapping profiles need an atomic claim.
2060
+ *
2061
+ * `scope: "terminal"` filters delivery to FINAL / ERROR / CLARIFICATION /
2062
+ * PERMISSION. `finalize` (default: true exactly when scope is "terminal")
2063
+ * tags the TASK for terminal-handler finalize: the worker Dendrite promotes
2064
+ * a successful AGENT_OUTPUT by also emitting FINAL - a default Axon never
2065
+ * emits FINAL itself, so a terminal-scoped Pathway would otherwise never
2066
+ * resolve against stock workers.
2067
+ */
2068
+ dispatch(args: DispatchArgs & {
2069
+ scope?: PathwayScope;
2070
+ finalize?: boolean;
2071
+ }): Promise<Pathway>;
2072
+ /** Sync-shape sugar: dispatch, await the first matching Signal, close the
2073
+ * Pathway, return the Signal. Use `scope: "terminal"` to wait only for
2074
+ * FINAL / ERROR / CLARIFICATION / PERMISSION. */
2075
+ dispatchAndWait(args: DispatchArgs & {
2076
+ scope?: PathwayScope;
2077
+ finalize?: boolean;
2078
+ timeoutMs?: number;
2079
+ retry?: RetryStrategy;
2080
+ }): Promise<Signal>;
2081
+ /** Async-shape sugar: dispatch, return the live Pathway immediately. The
2082
+ * caller attaches `pw.on(...)` callbacks or iterates. */
2083
+ dispatchAndSubscribe(args: DispatchArgs & {
2084
+ scope?: PathwayScope;
2085
+ finalize?: boolean;
2086
+ }): Promise<Pathway>;
2087
+ /** Open a Pathway in *observer* role for a trace another peer started. */
2088
+ observePathway(traceId: string): Promise<Pathway>;
2089
+ private ensurePathwaySubs;
2090
+ private openOpPathway;
2091
+ private cancelOpPathways;
2092
+ /**
2093
+ * Await the discrete answer to a CLARIFICATION or PERMISSION request.
2094
+ *
2095
+ * Opens a per-operation Pathway keyed on `request.id` and resolves on the
2096
+ * first CLARIFICATION_ANSWER / PERMISSION_DECISION whose parent_id matches.
2097
+ * The awaitable counterpart to {@link onClarificationAnswer} /
2098
+ * {@link onPermissionDecision}.
2099
+ */
2100
+ awaitDecision(request: Signal, opts?: {
2101
+ timeoutMs?: number;
2102
+ }): Promise<Signal>;
2103
+ /**
2104
+ * Broadcast a TASK_OFFER, collect BIDs for `deadlineMs`, award the winner
2105
+ * per `select` ("first_bid" | "lowest_cost" | "highest_confidence"), and
2106
+ * return a Pathway scoped to the resulting workflow. Losers get
2107
+ * TASK_DECLINED. Throws a TimeoutError-named Error when no BID arrives.
2108
+ * `finalize` follows the same rule as {@link dispatch}.
2109
+ */
2110
+ dispatchOffer(args: {
1424
2111
  input: Json;
2112
+ capabilities?: string[];
2113
+ deadlineMs?: number;
2114
+ select?: "first_bid" | "lowest_cost" | "highest_confidence";
1425
2115
  traceId?: string;
1426
2116
  parentId?: string | null;
1427
2117
  contextRef?: string;
1428
- capabilities?: string[];
2118
+ meta?: Json;
2119
+ scope?: PathwayScope;
2120
+ finalize?: boolean;
2121
+ }): Promise<Pathway>;
2122
+ /**
2123
+ * Emit a BID in response to a TASK_OFFER, on behalf of the local Axon named
2124
+ * by `neuron`. Bypasses the role guard - a worker bidding announces
2125
+ * capability, not orchestration.
2126
+ */
2127
+ bid(offer: Signal, args: {
2128
+ neuron: string;
2129
+ cost: number;
2130
+ etaMs?: number;
2131
+ confidence?: number;
1429
2132
  meta?: Json;
1430
2133
  }): Promise<Signal>;
2134
+ /** Default bidder: first hosted Axon whose caps cover the offer answers
2135
+ * (cost 0, confidence 1). No-op when nothing matches. */
2136
+ private maybeAutoBid;
1431
2137
  emitFinal(args: {
1432
2138
  traceId: string;
1433
2139
  parentId: string;
@@ -1442,16 +2148,109 @@ declare class Dendrite {
1442
2148
  recoverable?: boolean;
1443
2149
  meta?: Json;
1444
2150
  }): Promise<Signal>;
1445
- /** Emit a synapse-side Signal. Refuses Axon-owned types. */
2151
+ emitPlan(args: {
2152
+ traceId: string;
2153
+ parentId: string;
2154
+ steps: unknown[];
2155
+ rationale?: string;
2156
+ neuron?: string;
2157
+ meta?: Json;
2158
+ }): Promise<Signal>;
2159
+ emitThoughtDelta(args: {
2160
+ traceId: string;
2161
+ parentId: string;
2162
+ delta: string;
2163
+ seq?: number;
2164
+ neuron?: string;
2165
+ meta?: Json;
2166
+ }): Promise<Signal>;
2167
+ emitToolCall(args: {
2168
+ traceId: string;
2169
+ parentId: string;
2170
+ tool: string;
2171
+ args_: Json;
2172
+ callId?: string;
2173
+ neuron?: string;
2174
+ meta?: Json;
2175
+ }): Promise<Signal>;
2176
+ emitToolResult(args: {
2177
+ traceId: string;
2178
+ parentId: string;
2179
+ tool: string;
2180
+ result?: unknown;
2181
+ error?: string;
2182
+ callId?: string;
2183
+ neuron?: string;
2184
+ meta?: Json;
2185
+ }): Promise<Signal>;
2186
+ emitMemoryAppend(args: {
2187
+ traceId: string;
2188
+ parentId: string;
2189
+ key: string;
2190
+ value: unknown;
2191
+ neuron?: string;
2192
+ meta?: Json;
2193
+ }): Promise<Signal>;
2194
+ emitCritique(args: {
2195
+ traceId: string;
2196
+ parentId: string;
2197
+ targetEventId: string;
2198
+ issues: Json[];
2199
+ verdict: "pass" | "fail" | "revise";
2200
+ neuron?: string;
2201
+ meta?: Json;
2202
+ }): Promise<Signal>;
2203
+ emitEscalation(args: {
2204
+ traceId: string;
2205
+ parentId: string;
2206
+ reason: string;
2207
+ target?: string;
2208
+ context?: Json;
2209
+ neuron?: string;
2210
+ meta?: Json;
2211
+ }): Promise<Signal>;
2212
+ emitConsensus(args: {
2213
+ traceId: string;
2214
+ parentId: string;
2215
+ members: string[];
2216
+ verdict: string;
2217
+ votes?: Json;
2218
+ neuron?: string;
2219
+ meta?: Json;
2220
+ }): Promise<Signal>;
2221
+ emitContextSync(args: {
2222
+ traceId: string;
2223
+ parentId: string;
2224
+ snapshot: Json;
2225
+ version?: string;
2226
+ neuron?: string;
2227
+ meta?: Json;
2228
+ }): Promise<Signal>;
2229
+ /**
2230
+ * Reply to a CLARIFICATION by re-dispatching a TASK with the answer. The
2231
+ * new TASK is addressed by default to the asking Neuron, with parentId =
2232
+ * the clarification's id and the original traceId carried over. Input
2233
+ * shape: `{ clarification: { question, answer, ...extra } }`.
2234
+ */
2235
+ respondToClarification(request: Signal, opts: {
2236
+ answer: unknown;
2237
+ extra?: Json;
2238
+ neuron?: string;
2239
+ meta?: Json;
2240
+ }): Promise<Signal>;
2241
+ /**
2242
+ * Reply to an ESCALATION by dispatching a TASK to the escalation target
2243
+ * (default: `payload.target`). Default input:
2244
+ * `{ escalation: { reason, context, from } }`.
2245
+ */
2246
+ respondToEscalation(request: Signal, opts?: {
2247
+ neuron?: string;
2248
+ input?: Json;
2249
+ meta?: Json;
2250
+ }): Promise<Signal>;
1446
2251
  /**
1447
2252
  * Reply to a PERMISSION by re-dispatching a TASK carrying the verdict.
1448
- *
1449
- * The "send it back to the axon" path: the follow-up TASK is addressed by
1450
- * default to the Neuron that asked (`signal.neuron`), with `parentId` = the
1451
- * PERMISSION's id and the original `traceId` carried over, so the Neuron
1452
- * resumes on the same thread and can imprint the decision into an Engram (or
1453
- * recall it next time). New TASK input: `{ permission: { action, granted,
1454
- * reason?, ttlMs?, ...extra } }`.
2253
+ * Input shape: `{ permission: { action, granted, reason?, ttl_ms?, ...extra } }`.
1455
2254
  */
1456
2255
  respondToPermission(request: Signal, opts: {
1457
2256
  granted: boolean;
@@ -1461,8 +2260,7 @@ declare class Dendrite {
1461
2260
  neuron?: string;
1462
2261
  meta?: Json;
1463
2262
  }): Promise<Signal>;
1464
- /** Approve a PERMISSION request. `ttlMs` optionally advertises how long the
1465
- * grant is valid so the requester can cache it (e.g. in an Engram). */
2263
+ /** Approve a PERMISSION request. */
1466
2264
  grantPermission(request: Signal, opts?: {
1467
2265
  reason?: string;
1468
2266
  ttlMs?: number;
@@ -1474,24 +2272,116 @@ declare class Dendrite {
1474
2272
  meta?: Json;
1475
2273
  }): Promise<Signal>;
1476
2274
  private decidePermission;
1477
- /** Answer a *blocking* CLARIFICATION (the Neuron called ask(...) and is
1478
- * awaiting). Distinct from the legacy return-marker flow. */
2275
+ /** Answer a CLARIFICATION with a discrete CLARIFICATION_ANSWER signal
2276
+ * (parent_id = the request's id). Consumers pick it up via
2277
+ * {@link onClarificationAnswer} or {@link awaitDecision}. Distinct from
2278
+ * {@link respondToClarification}, which re-dispatches a TASK. */
1479
2279
  answerClarification(request: Signal, answer: unknown, opts?: {
1480
2280
  meta?: Json;
1481
2281
  }): Promise<Signal>;
2282
+ /** Emit a synapse-side Signal. Refuses Axon-owned types; TASK initiation
2283
+ * additionally requires orchestrator role. */
1482
2284
  emit(signal: Signal): Promise<void>;
1483
2285
  publish(signal: Signal): Promise<void>;
1484
2286
  subscribe(type: SignalType, handler: MessageHandler, opts?: {
1485
2287
  queueGroup?: string;
1486
2288
  }): Promise<Subscription>;
1487
2289
  private subject;
2290
+ /** Subject for capability-routed TASKs (queue-grouped consumers). */
2291
+ private routedSubject;
1488
2292
  private ensureInboundSub;
2293
+ /**
2294
+ * Route an inbound TASK to a local Axon. Addressed: by neuron_id (drop if
2295
+ * not hosted here). Capability-routed: first local Axon whose caps superset
2296
+ * the request. After publishing the reply, apply terminal-handler finalize:
2297
+ * a TASK tagged `payload.finalize` promotes a successful AGENT_OUTPUT by
2298
+ * also emitting FINAL (parented to the AGENT_OUTPUT, attributed to the
2299
+ * producing Neuron). Only AGENT_OUTPUT is promoted - CLARIFICATION /
2300
+ * PERMISSION pause the workflow and ERROR is already terminal.
2301
+ */
1489
2302
  private onTask;
2303
+ private registerTraceAbort;
2304
+ private unregisterTraceAbort;
2305
+ /** Resolve to the promise's value, or to null if the signal aborts first. */
2306
+ private raceAbort;
2307
+ private onStop;
2308
+ /** Broadcast a STOP for `traceId` (orchestrator-gated). Best-effort and
2309
+ * idempotent. */
2310
+ emitStop(args: {
2311
+ traceId: string;
2312
+ rollback?: boolean;
2313
+ reason?: string;
2314
+ }): Promise<Signal>;
2315
+ /** Stop a whole workflow. With `collectAcks` returns the STOPPED acks seen
2316
+ * within `timeoutMs` (best effort). */
2317
+ stopTrace(traceId: string, opts?: {
2318
+ rollback?: boolean;
2319
+ reason?: string;
2320
+ collectAcks?: boolean;
2321
+ timeoutMs?: number;
2322
+ }): Promise<Signal[]>;
2323
+ private safeStop;
2324
+ /** Dispatch and wait, retrying per `retry` until a non-retryable outcome or
2325
+ * attempts are exhausted. Returns the resolved Signal; re-throws the last
2326
+ * error when every attempt failed with an exception. */
2327
+ runWithRetry(args: DispatchArgs & {
2328
+ scope?: PathwayScope;
2329
+ finalize?: boolean;
2330
+ timeoutMs?: number;
2331
+ retry: RetryStrategy;
2332
+ }): Promise<Signal>;
1490
2333
  private emitRegister;
1491
2334
  private emitDeregister;
1492
2335
  private heartbeatTick;
2336
+ /** Mark Neurons deregistered when their last heartbeat is older than
2337
+ * `staleAfterMs`. Own hosted Axons were touched immediately before the
2338
+ * sweep, so they never qualify. */
2339
+ private sweepStaleNeurons;
1493
2340
  private mirrorToStore;
2341
+ /** Respond to a DISCOVER by re-emitting REGISTER for matching Axons. */
2342
+ private respondToDiscover;
1494
2343
  private dispatchInbound;
2344
+ /** Pick the hosted Engrams that should respond to a RECALL/IMPRINT.
2345
+ * directed.id (engramId) wins over directed.type (engramKind). */
2346
+ private resolveEngramTargets;
2347
+ private onRecall;
2348
+ private onImprint;
2349
+ private emitEngramRegister;
2350
+ private isEngramRegister;
2351
+ private recordEngramRegistration;
2352
+ /** Resolve (traceId, parentId) for a caller-side engram op: explicit ids
2353
+ * win, then the ambient task context (bound by Axon.handleTask), then a
2354
+ * freshly minted trace (the pre-task-hydration shape). */
2355
+ private static resolveTrace;
2356
+ /** Emit RECALL and await RECALLED. Trace attribution: explicit ids win,
2357
+ * then the ambient task context, then a fresh trace. */
2358
+ recall(args: {
2359
+ engramId?: string;
2360
+ engramKind?: string;
2361
+ query: Json;
2362
+ filters?: Json;
2363
+ contextRef?: string;
2364
+ deadlineMs?: number;
2365
+ recallMode?: RecallMode;
2366
+ minConfidence?: number;
2367
+ traceId?: string;
2368
+ parentId?: string;
2369
+ meta?: Json;
2370
+ }): Promise<RecallResult>;
2371
+ /** Emit IMPRINT. Resolves null unless `awaitAck: true`. Trace attribution
2372
+ * as {@link recall}. */
2373
+ imprint(args: {
2374
+ engramId?: string;
2375
+ engramKind?: string;
2376
+ op: ImprintOp;
2377
+ entry: Json;
2378
+ mergeKey?: string;
2379
+ awaitAck?: boolean;
2380
+ deadlineMs?: number;
2381
+ traceId?: string;
2382
+ parentId?: string;
2383
+ meta?: Json;
2384
+ }): Promise<ImprintReceipt | null>;
1495
2385
  private updateRegistry;
1496
2386
  }
1497
2387
  /** Back-compat alias - a Cortex is just a Dendrite. */
@@ -1545,25 +2435,49 @@ interface AxonOptions {
1545
2435
  neuronFn: NeuronFn;
1546
2436
  capabilities?: string[];
1547
2437
  version?: string;
2438
+ /** Participant kind carried on REGISTER as `directed.type` - the Neuron-side
2439
+ * analogue of an Engram's `engram_kind`. Defaults to `"neuron"`. */
2440
+ neuronKind?: string;
1548
2441
  contextFetcher?: ContextFetcher;
1549
2442
  /** Recognition the Axon applies to the Neuron's raw output before wrapping. */
1550
2443
  outputParser?: OutputParser;
2444
+ /**
2445
+ * Engram bindings the Neuron may address. Keyed by `binding.name` - the
2446
+ * Neuron passes that name to `helpers.recall(...)` / `helpers.imprint(...)`
2447
+ * (the helpers object is the Neuron's optional third argument). The Axon
2448
+ * enforces the whitelist, so a Neuron cannot hit an Engram it was not
2449
+ * declared to depend on.
2450
+ */
2451
+ engrams?: EngramBinding[];
1551
2452
  }
1552
2453
  /** Axon metadata accepted by the source-paired factories. */
1553
2454
  interface AxonExtra {
1554
2455
  capabilities?: string[];
1555
2456
  version?: string;
2457
+ /** Participant kind carried on REGISTER as `directed.type` (default "neuron"). */
2458
+ neuronKind?: string;
1556
2459
  contextFetcher?: ContextFetcher;
1557
2460
  /** Attach the source's recogniser (default true). */
1558
2461
  recognize?: boolean;
2462
+ /**
2463
+ * Append {@link COSMO_INTENT_SYSTEM_PROMPT} to the source's `system` prompt
2464
+ * so the model knows the `{"cosmo": ...}` convention the recogniser parses.
2465
+ * Default: true exactly when `recognize` is on and the source accepts a
2466
+ * `system` option (every LLM source except `huggingface`; `mcp` is never
2467
+ * taught). Pass false to opt out; passing true for an unsupported source
2468
+ * throws.
2469
+ */
2470
+ teachIntents?: boolean;
1559
2471
  }
1560
2472
  declare class Axon {
1561
2473
  readonly neuronId: string;
1562
2474
  readonly capabilities: string[];
1563
2475
  readonly version: string | undefined;
2476
+ readonly neuronKind: string;
1564
2477
  private readonly fn;
1565
2478
  private readonly contextFetcher;
1566
2479
  private readonly outputParser;
2480
+ private readonly engramBindings;
1567
2481
  private dendrite;
1568
2482
  /**
1569
2483
  * Decorator-registered recognisers, one bucket per capability (the asking
@@ -1572,9 +2486,21 @@ declare class Axon {
1572
2486
  * output by {@link applyRecognisers}.
1573
2487
  */
1574
2488
  private readonly recognisers;
2489
+ /** Pre-task hooks (beforeTask): transform/validate/reject the TASK input
2490
+ * before the Neuron runs. */
2491
+ private readonly beforeTaskHooks;
1575
2492
  /** @internal - lifecycle hooks, driven by the hosting Dendrite. */
1576
2493
  readonly hooks: LifecycleHooks<Axon>;
1577
2494
  constructor(opts: AxonOptions);
2495
+ /** Declared Engram bindings, keyed by name. */
2496
+ get engrams(): ReadonlyMap<string, EngramBinding>;
2497
+ private resolveBinding;
2498
+ /** Build the per-task helpers object handed to the Neuron as its third
2499
+ * argument. Helpers throw EngramNotBound for undeclared names and
2500
+ * require a hosting Dendrite (the only thing the Axon pulls from it). */
2501
+ private buildHelpers;
2502
+ /** Resolve the teach-intents decision and return (possibly augmented) source opts. */
2503
+ private static applyTeachIntents;
1578
2504
  private static build;
1579
2505
  /** Axon paired with any registered Neuron source + its recogniser. */
1580
2506
  static fromSource(source: NeuronSource, neuronId: string, opts: any, extra?: AxonExtra): Axon;
@@ -1588,6 +2514,16 @@ declare class Axon {
1588
2514
  static huggingface(neuronId: string, opts: HuggingFaceNeuronOptions, extra?: AxonExtra): Axon;
1589
2515
  /** Axon paired with a stdio MCP server. */
1590
2516
  static mcp(neuronId: string, opts: McpNeuronOptions, extra?: AxonExtra): Axon;
2517
+ /**
2518
+ * Register a pre-task hook over the TASK's `input`. Runs before the Neuron.
2519
+ * Sync or async; multiple hooks run in registration order, each receiving
2520
+ * the previous one's result. Return a (new) object to replace the input,
2521
+ * return null/undefined to pass through unchanged, or throw to reject the
2522
+ * TASK (surfaces as an ERROR Signal, code NEURON_EXCEPTION). The natural
2523
+ * place for input normalisation or per-Axon policy checks.
2524
+ */
2525
+ beforeTask(fn: (input: Json) => unknown | Promise<unknown>): (input: Json) => unknown | Promise<unknown>;
2526
+ private applyBeforeTask;
1591
2527
  /** Detector returning the AGENT_OUTPUT payload, or null to wrap verbatim. */
1592
2528
  detectsOutput(fn: Recogniser): Recogniser;
1593
2529
  /** Detector returning `{ question, context? }` to emit CLARIFICATION, or null. */
@@ -1612,217 +2548,30 @@ declare class Axon {
1612
2548
  [ATTACH](dendrite: Dendrite): void;
1613
2549
  /** Package-internal: invoked via the {@link DETACH} symbol. */
1614
2550
  [DETACH](): void;
1615
- /** Run the Neuron and return AGENT_OUTPUT / CLARIFICATION / ERROR. */
2551
+ /** Run the Neuron and return AGENT_OUTPUT / CLARIFICATION / ERROR.
2552
+ *
2553
+ * Binds the TASK's (traceId, parentId=task.id) as the ambient trace
2554
+ * context for the whole handling pass - neuronFn, detectors, and hooks
2555
+ * included - so engram calls made without explicit trace plumbing (e.g.
2556
+ * `dendrite.imprint` from a `detectsOutput` hook) are attributed to this
2557
+ * task's trace. */
1616
2558
  handleTask(task: Signal): Promise<Signal>;
2559
+ private handleTaskInner;
1617
2560
  }
2561
+ /**
2562
+ * System-prompt fragment teaching an LLM the `cosmo` intent convention.
2563
+ * Without it a hosted model never knows it *can* clarify / request permission
2564
+ * / signal a structured error, so the recognisers have nothing to recognise.
2565
+ * `Axon.fromSource(..., { recognize: true })` (the default) appends this to
2566
+ * the source's `system` prompt for system-capable LLM sources; opt out with
2567
+ * `teachIntents: false`.
2568
+ */
2569
+ declare const COSMO_INTENT_SYSTEM_PROMPT: string;
1618
2570
  /** Recogniser for LLM sources returning `{ response: text, meta }`. */
1619
2571
  declare function parseLlmIntents(raw: unknown): unknown;
1620
2572
  /** Recogniser for the `mcp` source: `is_error` -> ERROR, else pass through. */
1621
2573
  declare function parseMcpIntents(raw: unknown): unknown;
1622
2574
 
1623
- /**
1624
- * @cosmonapse/sdk - Engram (shared memory)
1625
- *
1626
- * Ported from the Python `cosmonapse.engram` package (see ENGRAM_DESIGN.md).
1627
- * An Engram is the synapse-side participant that services RECALL / IMPRINT
1628
- * signals. Engrams are NOT Neurons: they never produce AGENT_OUTPUT. A hosting
1629
- * Dendrite mounts one via `dendrite.attachEngram(engram)`.
1630
- *
1631
- * This module is the value layer: the data types, the `Engram` contract, and
1632
- * the default in-process `InMemoryEngram`. The SQLite / Postgres backends live
1633
- * in `engram-sqlite.ts` / `engram-postgres.ts`; the caller-side correlation
1634
- * table lives in `engram-client.ts`.
1635
- */
1636
-
1637
- type RecallMode = "first" | "merge" | "all";
1638
- type ImprintOp = "add" | "append" | "merge" | "upsert" | "delete";
1639
- /** One search result. `score` is backend-dependent; relational backends use 1.0. */
1640
- interface Hit {
1641
- id: string;
1642
- entry: Json;
1643
- score: number;
1644
- }
1645
- /** What a recall() call returns to the caller. */
1646
- interface RecallResult {
1647
- hits: Hit[];
1648
- engramIds: string[];
1649
- truncated: boolean;
1650
- tookMs: number | null;
1651
- }
1652
- /** What an imprint() call returns to the caller. */
1653
- interface ImprintReceipt {
1654
- engramId: string;
1655
- op: string;
1656
- id: string | null;
1657
- version: number | null;
1658
- tookMs: number | null;
1659
- error: string | null;
1660
- /** Convenience: true when `error` is null. */
1661
- ok: boolean;
1662
- }
1663
- interface EngramBindingInit {
1664
- name: string;
1665
- directedId?: string;
1666
- directedType?: string;
1667
- defaultDeadlineMs?: number;
1668
- defaultRecallMode?: RecallMode;
1669
- }
1670
- /**
1671
- * Declarative wiring of one Engram into an Axon. The Neuron addresses an Engram
1672
- * by the stable local `name`; `directedId` / `directedType` determine how
1673
- * RECALL/IMPRINT are routed on the wire. At least one of them must be set.
1674
- */
1675
- declare class EngramBinding {
1676
- readonly name: string;
1677
- readonly directedId: string | null;
1678
- readonly directedType: string | null;
1679
- readonly defaultDeadlineMs: number | null;
1680
- readonly defaultRecallMode: RecallMode;
1681
- constructor(init: EngramBindingInit);
1682
- /** Build the `Directed` addressing this Engram. */
1683
- toDirected(): Directed;
1684
- }
1685
- declare class EngramError extends Error {
1686
- constructor(message?: string);
1687
- }
1688
- /** Raised when a RECALL or IMPRINT deadline elapses with no response. */
1689
- declare class EngramTimeout extends EngramError {
1690
- }
1691
- /** Raised when the containing TASK terminates while a call is in flight. */
1692
- declare class EngramCancelled extends EngramError {
1693
- }
1694
- /** Raised when a Neuron asks for an Engram binding its Axon was not wired to. */
1695
- declare class EngramNotBound extends EngramError {
1696
- }
1697
- /** Raised by a backend that must shed load (surfaces as an IMPRINTED error). */
1698
- declare class EngramOverloaded extends EngramError {
1699
- }
1700
- interface RecallOptions {
1701
- filters?: Json;
1702
- contextRef?: string;
1703
- deadlineMs?: number;
1704
- minConfidence?: number;
1705
- }
1706
- interface ImprintOptions {
1707
- mergeKey?: string;
1708
- /** Originating IMPRINT signal id; backends use it for idempotency. */
1709
- imprintId?: string;
1710
- }
1711
- /**
1712
- * Storage wrapper. One backend per Engram instance. Every backend implements
1713
- * this exact interface; the test suite runs against any conforming Engram.
1714
- */
1715
- declare abstract class Engram {
1716
- abstract engramId: string;
1717
- abstract engramKind: string;
1718
- abstract capabilities: string[];
1719
- version: string | null;
1720
- /** Open backend resources (DB pool, file handle, ...). */
1721
- abstract connect(): Promise<void>;
1722
- /** Release backend resources. */
1723
- abstract close(): Promise<void>;
1724
- /** Return matching entries. Empty array on a miss; never throw on a miss. */
1725
- abstract recall(query: Json, opts?: RecallOptions): Promise<Hit[]>;
1726
- /** Write to the backend. `op` is one of add | append | merge | upsert | delete. */
1727
- abstract imprint(op: ImprintOp, entry: Json, opts?: ImprintOptions): Promise<ImprintReceipt>;
1728
- /** Return false if this Engram cannot satisfy the query. Default: serve all. */
1729
- canServe(_query: Json): Promise<boolean>;
1730
- }
1731
- interface InMemoryEngramInit {
1732
- engramId?: string;
1733
- engramKind?: string;
1734
- capabilities?: string[];
1735
- version?: string | null;
1736
- }
1737
- /** Dict-backed Engram. The default backend for tests and local dev. */
1738
- declare class InMemoryEngram extends Engram {
1739
- engramId: string;
1740
- engramKind: string;
1741
- capabilities: string[];
1742
- private entries;
1743
- private byMergeKey;
1744
- private imprintSeen;
1745
- constructor(init?: InMemoryEngramInit);
1746
- connect(): Promise<void>;
1747
- close(): Promise<void>;
1748
- recall(query: Json, opts?: RecallOptions): Promise<Hit[]>;
1749
- imprint(op: ImprintOp, entry: Json, opts?: ImprintOptions): Promise<ImprintReceipt>;
1750
- /** Test/debug helper - NOT part of the Engram contract. */
1751
- snapshot(): Json[];
1752
- private makeEntry;
1753
- private store;
1754
- private evict;
1755
- }
1756
- /** Conservative deep merge: dicts merge, lists concat-dedup, scalars overwrite. */
1757
- declare function deepMerge(base: unknown, incoming: unknown): unknown;
1758
-
1759
- /**
1760
- * @cosmonapse/sdk - Engram caller-side client
1761
- *
1762
- * Ported from `cosmonapse.engram.client`. EngramClient is the caller-side
1763
- * bridge: it builds RECALL / IMPRINT envelopes, publishes them, registers
1764
- * pending promises keyed by the envelope id, and resolves them when a matching
1765
- * RECALLED / IMPRINTED arrives (correlated by `parent_id`). It enforces
1766
- * per-call deadlines and cancels in-flight calls when a TASK terminates.
1767
- *
1768
- * To avoid an import cycle with the Dendrite, the client depends only on a
1769
- * minimal {@link EngramPublisher} (the Dendrite implements it by passing
1770
- * itself in). The Dendrite owns the subscription to RECALLED / IMPRINTED and
1771
- * calls `deliver(signal)` for each inbound.
1772
- */
1773
-
1774
- /** The slice of the Dendrite the client needs: a way to put a Signal on the wire. */
1775
- interface EngramPublisher {
1776
- publish(signal: Signal): Promise<void>;
1777
- }
1778
- interface RecallCallArgs {
1779
- binding?: EngramBinding;
1780
- engramId?: string;
1781
- engramKind?: string;
1782
- query: Json;
1783
- filters?: Json;
1784
- contextRef?: string;
1785
- deadlineMs?: number;
1786
- recallMode?: RecallMode;
1787
- minConfidence?: number;
1788
- traceId: string;
1789
- parentId: string;
1790
- meta?: Json;
1791
- }
1792
- interface ImprintCallArgs {
1793
- binding?: EngramBinding;
1794
- engramId?: string;
1795
- engramKind?: string;
1796
- op: ImprintOp;
1797
- entry: Json;
1798
- mergeKey?: string;
1799
- awaitAck?: boolean;
1800
- deadlineMs?: number;
1801
- traceId: string;
1802
- parentId: string;
1803
- meta?: Json;
1804
- }
1805
- declare class EngramClient {
1806
- private readonly publisher;
1807
- private pendingRecalls;
1808
- private pendingImprints;
1809
- private byTrace;
1810
- constructor(publisher: EngramPublisher);
1811
- recall(args: RecallCallArgs): Promise<RecallResult>;
1812
- imprint(args: ImprintCallArgs): Promise<ImprintReceipt | null>;
1813
- /** Match RECALLED / IMPRINTED by parent_id and resolve pendings. */
1814
- deliver(sig: Signal): void;
1815
- /** Cancel every in-flight recall/imprint on a trace (FINAL/ERROR or shutdown). */
1816
- cancelTrace(traceId: string): void;
1817
- cancelAll(): void;
1818
- private onRecallDeadline;
1819
- private onImprintDeadline;
1820
- private track;
1821
- private cleanupRecall;
1822
- private cleanupImprint;
1823
- private discardTrace;
1824
- }
1825
-
1826
2575
  /**
1827
2576
  * @cosmonapse/sdk - SQLite Engram
1828
2577
  *
@@ -1912,4 +2661,4 @@ declare class PostgresEngram extends Engram {
1912
2661
  */
1913
2662
  declare const VERSION: string;
1914
2663
 
1915
- export { AXON_TYPES, type AnthropicNeuronOptions, Axon, type AxonExtra, type AxonOptions, type ClarificationOutput, type CloseableNeuronFn, type ConnectHook, type ContextFetcher, Cortex, DendriteProtocolError as CortexProtocolError, Dendrite, type DendriteOptions, DendriteProtocolError, DevSynapse, type DevSynapseOptions, DevSynapseServer, type DevSynapseServerOptions, type Directed, type DirectedInput, Engram, EngramBinding, type EngramBindingInit, EngramCancelled, EngramClient, EngramError, EngramNotBound, EngramOverloaded, type EngramPublisher, EngramTimeout, type ErrorOutput, type Hit, type HuggingFaceNeuronOptions, type ImprintCallArgs, type ImprintOp, type ImprintOptions, type ImprintReceipt, InMemoryEngram, type InMemoryEngramInit, type Json, KafkaSynapse, type KafkaSynapseOptions, LifecycleHooks, type ListOptions, type McpNeuronOptions, MemoryRegistryStore, MemorySynapse, type MessageHandler, NatsSynapse, type NatsSynapseOptions, type NeuronFn, type NeuronRecord, type NeuronRecordInit, type NeuronSource, type NeuronStatus, type NewSignalInput, type OllamaNeuronOptions, type OpenAICompatNeuronOptions, type OpenAINeuronOptions, type OutputParser, type PermissionRequestOutput, PostgresEngram, type PostgresEngramInit, PostgresRegistryStore, type PostgresRegistryStoreOptions, type RecallCallArgs, type RecallMode, type RecallOptions, type RecallResult, type Recogniser, type RefreshEvent, type RefreshHook, type RegistryStore, type RequestOptions, SYNAPSE_TYPES, type ScheduleHook, type Signal, type SignalHandler, SignalType, SqliteEngram, type SqliteEngramInit, SqliteRegistryStore, type SubscribeOptions, type Subscription, type Synapse, VERSION, agentOutputSignal, anthropicNeuron, bidSignal, clarificationAnswerSignal, clarificationSignal, clarify, connectSynapse, consensusSignal, contextSyncSignal, createSignal, critiqueSignal, decode, deepMerge, deregisterSignal, directedTo, discoverSignal, encode, errorResult, errorSignal, escalationSignal, finalSignal, heartbeatSignal, huggingFaceNeuron, imprintSignal, imprintedSignal, isClarification, isErrorOutput, isPermissionRequest, mcpNeuron, memoryAppendSignal, neuron, neuronRecord, newEngramId, newEventId, newTraceId, normalizeDirected, ollamaNeuron, openaiNeuron, parseLlmIntents, parseMcpIntents, permissionDecisionSignal, permissionRequest, permissionSignal, planSignal, recallSignal, recalledSignal, registerSignal, reply, standardMcpServers, synapseFromUrl, taskOfferSignal, taskSignal, thoughtDeltaSignal, toolCallSignal, toolResultSignal, validateSignal };
2664
+ export { AXON_TYPES, type AnthropicNeuronOptions, Axon, type AxonExtra, type AxonOptions, COSMO_INTENT_SYSTEM_PROMPT, type ClarificationOutput, type CloseableNeuronFn, type ConnectHook, type ContextFetcher, Cortex, DendriteProtocolError as CortexProtocolError, Dendrite, type DendriteOptions, DendriteProtocolError, type DendriteRole, DevSynapse, type DevSynapseOptions, DevSynapseServer, type DevSynapseServerOptions, type Directed, type DirectedInput, Engram, EngramBinding, type EngramBindingInit, EngramCancelled, EngramClient, EngramError, EngramNotBound, EngramOverloaded, type EngramPublisher, EngramTimeout, type ErrorOutput, type HandlerFilter, type Hit, type HuggingFaceNeuronOptions, type ImprintCallArgs, type ImprintOp, type ImprintOptions, type ImprintReceipt, InMemoryEngram, type InMemoryEngramInit, type Json, KafkaSynapse, type KafkaSynapseOptions, LifecycleHooks, type ListOptions, type McpNeuronOptions, MemoryRegistryStore, MemorySynapse, type MessageHandler, NatsSynapse, type NatsSynapseOptions, type NeuronFn, type NeuronHelpers, type NeuronRecord, type NeuronRecordInit, type NeuronSource, type NeuronStatus, type NewSignalInput, type OllamaNeuronOptions, type OpenAICompatNeuronOptions, type OpenAINeuronOptions, type OutputParser, PATHWAY_TYPES, Pathway, type PathwayCloseHook, PathwayClosedError, type PathwayOptions, type PathwayRole, type PathwayScope, type PathwaySignalHandler, type PermissionRequestOutput, PostgresEngram, type PostgresEngramInit, PostgresRegistryStore, type PostgresRegistryStoreOptions, type RecallCallArgs, type RecallMode, type RecallOptions, type RecallResult, type Recogniser, type RefreshEvent, type RefreshHook, type RegistryStore, type RequestOptions, type RetryOutcome, type RetryStrategy, SYNAPSE_TYPES, type ScheduleHook, type Signal, type SignalHandler, SignalType, SqliteEngram, type SqliteEngramInit, SqliteRegistryStore, type SubscribeOptions, type Subscription, type Synapse, TERMINAL_TYPES, VERSION, agentOutputSignal, ambientTrace, anthropicNeuron, bidSignal, clarificationAnswerSignal, clarificationSignal, clarify, connectSynapse, consensusSignal, contextSyncSignal, createSignal, critiqueSignal, decode, deepMerge, defaultRetryOn, deregisterSignal, directedTo, discoverSignal, encode, errorResult, errorSignal, escalationSignal, finalSignal, followupPrompt, heartbeatSignal, huggingFaceNeuron, imprintSignal, imprintedSignal, isClarification, isErrorOutput, isPermissionRequest, mcpNeuron, memoryAppendSignal, neuron, neuronRecord, newEngramId, newEventId, newTraceId, normalizeDirected, ollamaNeuron, openaiNeuron, parseLlmIntents, parseMcpIntents, permissionDecisionSignal, permissionRequest, permissionSignal, planSignal, recallSignal, recalledSignal, registerSignal, reply, runWithTraceContext, standardMcpServers, stopSignal, stoppedSignal, synapseFromUrl, taskAwardedSignal, taskDeclinedSignal, taskOfferSignal, taskSignal, thoughtDeltaSignal, toolCallSignal, toolResultSignal, validateSignal };