@cosmonapse/sdk 0.1.2
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.cjs +4258 -0
- package/dist/index.d.cts +1915 -0
- package/dist/index.d.ts +1915 -0
- package/dist/index.js +4137 -0
- package/package.json +75 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1915 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cosmonapse/sdk - envelope
|
|
3
|
+
*
|
|
4
|
+
* Signal envelope types and codec. The TypeScript surface mirrors the Python
|
|
5
|
+
* `cosmonapse.envelope` module 1:1 (see ENVELOPE_SPEC.md §7). Every message
|
|
6
|
+
* crossing the Synapse is a Signal - a JSON object conforming to this schema.
|
|
7
|
+
*
|
|
8
|
+
* Producer tags (who emits each type):
|
|
9
|
+
* [A] Axon (skill/connector)
|
|
10
|
+
* [C] Cortex (developer-built orchestrating component)
|
|
11
|
+
*/
|
|
12
|
+
/** Return a prefixed event ULID: `evt_<26-char ULID>`. */
|
|
13
|
+
declare function newEventId(): string;
|
|
14
|
+
/** Return a prefixed trace ULID: `trc_<26-char ULID>`. */
|
|
15
|
+
declare function newTraceId(): string;
|
|
16
|
+
/** Return a prefixed Engram entry ULID: `eng_<26-char ULID>`. */
|
|
17
|
+
declare function newEngramId(): string;
|
|
18
|
+
declare const SignalType: {
|
|
19
|
+
readonly TASK: "TASK";
|
|
20
|
+
readonly AGENT_OUTPUT: "AGENT_OUTPUT";
|
|
21
|
+
readonly FINAL: "FINAL";
|
|
22
|
+
readonly ERROR: "ERROR";
|
|
23
|
+
readonly TASK_OFFER: "TASK_OFFER";
|
|
24
|
+
readonly BID: "BID";
|
|
25
|
+
readonly TASK_AWARDED: "TASK_AWARDED";
|
|
26
|
+
readonly TASK_DECLINED: "TASK_DECLINED";
|
|
27
|
+
readonly THOUGHT_DELTA: "THOUGHT_DELTA";
|
|
28
|
+
readonly PLAN: "PLAN";
|
|
29
|
+
readonly TOOL_CALL: "TOOL_CALL";
|
|
30
|
+
readonly TOOL_RESULT: "TOOL_RESULT";
|
|
31
|
+
readonly MEMORY_APPEND: "MEMORY_APPEND";
|
|
32
|
+
readonly ESCALATION: "ESCALATION";
|
|
33
|
+
readonly CONSENSUS: "CONSENSUS";
|
|
34
|
+
readonly CONTEXT_SYNC: "CONTEXT_SYNC";
|
|
35
|
+
readonly CRITIQUE: "CRITIQUE";
|
|
36
|
+
readonly CLARIFICATION: "CLARIFICATION";
|
|
37
|
+
readonly PERMISSION: "PERMISSION";
|
|
38
|
+
readonly PERMISSION_DECISION: "PERMISSION_DECISION";
|
|
39
|
+
readonly CLARIFICATION_ANSWER: "CLARIFICATION_ANSWER";
|
|
40
|
+
readonly REGISTER: "REGISTER";
|
|
41
|
+
readonly DEREGISTER: "DEREGISTER";
|
|
42
|
+
readonly HEARTBEAT: "HEARTBEAT";
|
|
43
|
+
readonly RECALL: "RECALL";
|
|
44
|
+
readonly RECALLED: "RECALLED";
|
|
45
|
+
readonly IMPRINT: "IMPRINT";
|
|
46
|
+
readonly IMPRINTED: "IMPRINTED";
|
|
47
|
+
readonly DISCOVER: "DISCOVER";
|
|
48
|
+
};
|
|
49
|
+
type SignalType = (typeof SignalType)[keyof typeof SignalType];
|
|
50
|
+
/** Types the Axon (skill) is allowed to produce. */
|
|
51
|
+
declare const AXON_TYPES: ReadonlySet<SignalType>;
|
|
52
|
+
/** Types the Cortex (orchestrator) is allowed to produce. */
|
|
53
|
+
declare const SYNAPSE_TYPES: ReadonlySet<SignalType>;
|
|
54
|
+
/**
|
|
55
|
+
* Unified addressing for a Signal. Mirrors Python `cosmonapse.envelope.Directed`
|
|
56
|
+
* 1:1 so the wire shape is identical across SDKs.
|
|
57
|
+
*
|
|
58
|
+
* A Signal may be addressed three ways, in precedence order:
|
|
59
|
+
* - `id` Direct address. A `neuron_id` for TASK-family routing, or an
|
|
60
|
+
* `engram_id` for RECALL/IMPRINT.
|
|
61
|
+
* - `type` Type-based routing. A neuron type, or an `engram_kind`.
|
|
62
|
+
* - `capabilities` Capability-based routing.
|
|
63
|
+
*
|
|
64
|
+
* `id` wins over `type`, which wins over `capabilities` on the receiving side.
|
|
65
|
+
*/
|
|
66
|
+
interface Directed {
|
|
67
|
+
id: string | null;
|
|
68
|
+
type: string | null;
|
|
69
|
+
capabilities: string[];
|
|
70
|
+
}
|
|
71
|
+
/** A partial Directed accepted at call sites; missing fields default to null/[]. */
|
|
72
|
+
type DirectedInput = Partial<Directed> | null;
|
|
73
|
+
/** Normalise a partial Directed (or null) into a full Directed (or null). */
|
|
74
|
+
declare function normalizeDirected(d: DirectedInput | undefined): Directed | null;
|
|
75
|
+
/** Small helper for building a {@link Directed} at call sites. */
|
|
76
|
+
declare function directedTo(id?: string | null, opts?: {
|
|
77
|
+
type?: string | null;
|
|
78
|
+
capabilities?: string[];
|
|
79
|
+
}): Directed;
|
|
80
|
+
type Json = Record<string, unknown>;
|
|
81
|
+
/**
|
|
82
|
+
* The universal envelope for every message crossing the Synapse.
|
|
83
|
+
*
|
|
84
|
+
* - `v` Protocol version. Always `"1"` for this release.
|
|
85
|
+
* - `id` Unique event ID. Format: `evt_<26-char ULID>`.
|
|
86
|
+
* - `trace_id` Groups all Signals belonging to one logical workflow (`trc_<ULID>`).
|
|
87
|
+
* - `parent_id` The id of the Signal that caused this one. Optional.
|
|
88
|
+
* - `type` One of the {@link SignalType} values.
|
|
89
|
+
* - `directed` Unified addressing (id / type / capabilities). Null when unset.
|
|
90
|
+
* - `ts` RFC 3339 UTC timestamp of emission.
|
|
91
|
+
* - `payload` Type-specific content.
|
|
92
|
+
* - `meta` Non-semantic annotations: model name, token counts, cost, etc.
|
|
93
|
+
*/
|
|
94
|
+
interface Signal {
|
|
95
|
+
v: string;
|
|
96
|
+
id: string;
|
|
97
|
+
trace_id: string;
|
|
98
|
+
parent_id: string | null;
|
|
99
|
+
type: SignalType;
|
|
100
|
+
directed: Directed | null;
|
|
101
|
+
ts: string;
|
|
102
|
+
payload: Json;
|
|
103
|
+
meta: Json;
|
|
104
|
+
}
|
|
105
|
+
interface NewSignalInput {
|
|
106
|
+
type: SignalType;
|
|
107
|
+
trace_id?: string;
|
|
108
|
+
parent_id?: string | null;
|
|
109
|
+
directed?: DirectedInput;
|
|
110
|
+
payload?: Json;
|
|
111
|
+
meta?: Json;
|
|
112
|
+
v?: string;
|
|
113
|
+
id?: string;
|
|
114
|
+
ts?: string;
|
|
115
|
+
}
|
|
116
|
+
/** Construct a fully-populated, validated Signal, filling protocol defaults. */
|
|
117
|
+
declare function createSignal(input: NewSignalInput): Signal;
|
|
118
|
+
/** Throw if the envelope's identifier fields violate the spec. */
|
|
119
|
+
declare function validateSignal(signal: Signal): void;
|
|
120
|
+
/** Serialise to UTF-8 JSON bytes for wire transmission. */
|
|
121
|
+
declare function encode(signal: Signal): Uint8Array;
|
|
122
|
+
/** Deserialise from JSON bytes or string into a validated Signal. */
|
|
123
|
+
declare function decode(data: Uint8Array | string): Signal;
|
|
124
|
+
/**
|
|
125
|
+
* Construct a reply Signal that shares `source`'s trace_id and sets
|
|
126
|
+
* `parent_id` to `source`'s id.
|
|
127
|
+
*/
|
|
128
|
+
declare function reply(source: Signal, opts: {
|
|
129
|
+
type: SignalType;
|
|
130
|
+
payload?: Json;
|
|
131
|
+
directed?: DirectedInput;
|
|
132
|
+
meta?: Json;
|
|
133
|
+
}): Signal;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @cosmonapse/sdk - typed signal builders
|
|
137
|
+
*
|
|
138
|
+
* Convenience constructors for the common Signal types. These are NOT required
|
|
139
|
+
* by the protocol - the protocol only requires a valid Signal with the correct
|
|
140
|
+
* `type` and `payload`. They mirror the helpers in `cosmonapse.envelope`.
|
|
141
|
+
*
|
|
142
|
+
* Addressing is carried by the envelope `directed` field (see {@link Directed}).
|
|
143
|
+
* Builders accept a `directed` (full or partial) rather than a bare `neuron`
|
|
144
|
+
* string; higher-level helpers (e.g. `Dendrite.dispatchTask`) keep a `neuron`
|
|
145
|
+
* argument for ergonomics and wrap it into `{ id: neuron }`.
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/** [C] Dispatch a unit of work to a Neuron. */
|
|
149
|
+
declare function taskSignal(args: {
|
|
150
|
+
input: Json;
|
|
151
|
+
traceId?: string;
|
|
152
|
+
parentId?: string | null;
|
|
153
|
+
directed?: DirectedInput;
|
|
154
|
+
contextRef?: string;
|
|
155
|
+
capabilities?: string[];
|
|
156
|
+
meta?: Json;
|
|
157
|
+
}): Signal;
|
|
158
|
+
/** [A] Wrap a Neuron's raw output in a neutral AGENT_OUTPUT envelope. */
|
|
159
|
+
declare function agentOutputSignal(args: {
|
|
160
|
+
traceId: string;
|
|
161
|
+
parentId: string;
|
|
162
|
+
directed?: DirectedInput;
|
|
163
|
+
output: Json;
|
|
164
|
+
meta?: Json;
|
|
165
|
+
}): Signal;
|
|
166
|
+
/** [A] The Neuron needs more information before it can complete the task. */
|
|
167
|
+
declare function clarificationSignal(args: {
|
|
168
|
+
traceId: string;
|
|
169
|
+
parentId: string;
|
|
170
|
+
directed?: DirectedInput;
|
|
171
|
+
question: string;
|
|
172
|
+
context?: Json;
|
|
173
|
+
meta?: Json;
|
|
174
|
+
}): Signal;
|
|
175
|
+
/** [A] A Neuron asks permission to perform `action` before doing it. */
|
|
176
|
+
declare function permissionSignal(args: {
|
|
177
|
+
traceId: string;
|
|
178
|
+
parentId: string;
|
|
179
|
+
directed?: DirectedInput;
|
|
180
|
+
action: string;
|
|
181
|
+
scope?: Json;
|
|
182
|
+
reason?: string;
|
|
183
|
+
context?: Json;
|
|
184
|
+
meta?: Json;
|
|
185
|
+
}): Signal;
|
|
186
|
+
/** [C] Verdict on a PERMISSION request. `parentId` MUST be the PERMISSION's id. */
|
|
187
|
+
declare function permissionDecisionSignal(args: {
|
|
188
|
+
traceId: string;
|
|
189
|
+
parentId: string;
|
|
190
|
+
granted: boolean;
|
|
191
|
+
directed?: DirectedInput;
|
|
192
|
+
reason?: string;
|
|
193
|
+
ttlMs?: number;
|
|
194
|
+
meta?: Json;
|
|
195
|
+
}): Signal;
|
|
196
|
+
/** [C] Answer to a blocking CLARIFICATION. `parentId` MUST be the CLARIFICATION's id. */
|
|
197
|
+
declare function clarificationAnswerSignal(args: {
|
|
198
|
+
traceId: string;
|
|
199
|
+
parentId: string;
|
|
200
|
+
answer: unknown;
|
|
201
|
+
directed?: DirectedInput;
|
|
202
|
+
meta?: Json;
|
|
203
|
+
}): Signal;
|
|
204
|
+
/** [C] Workflow concluded. `result` carries the terminal output. */
|
|
205
|
+
declare function finalSignal(args: {
|
|
206
|
+
traceId: string;
|
|
207
|
+
parentId: string;
|
|
208
|
+
result: Json;
|
|
209
|
+
directed?: DirectedInput;
|
|
210
|
+
cost?: Json;
|
|
211
|
+
meta?: Json;
|
|
212
|
+
}): Signal;
|
|
213
|
+
/** [A][C] Something went wrong. `recoverable` lets the Cortex retry or reroute. */
|
|
214
|
+
declare function errorSignal(args: {
|
|
215
|
+
traceId: string;
|
|
216
|
+
code: string;
|
|
217
|
+
message: string;
|
|
218
|
+
parentId?: string | null;
|
|
219
|
+
directed?: DirectedInput;
|
|
220
|
+
recoverable?: boolean;
|
|
221
|
+
meta?: Json;
|
|
222
|
+
}): Signal;
|
|
223
|
+
/**
|
|
224
|
+
* [A] A participant connecting to the Synapse and declaring its capabilities.
|
|
225
|
+
*
|
|
226
|
+
* 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`.
|
|
232
|
+
*/
|
|
233
|
+
declare function registerSignal(args: {
|
|
234
|
+
directed: DirectedInput;
|
|
235
|
+
capabilities?: string[];
|
|
236
|
+
version?: string;
|
|
237
|
+
engram?: boolean;
|
|
238
|
+
meta?: Json;
|
|
239
|
+
}): Signal;
|
|
240
|
+
/** [A] A participant disconnecting from the Synapse. */
|
|
241
|
+
declare function deregisterSignal(args: {
|
|
242
|
+
directed: DirectedInput;
|
|
243
|
+
reason?: string;
|
|
244
|
+
meta?: Json;
|
|
245
|
+
}): Signal;
|
|
246
|
+
/** [A] Periodic liveness signal from a participant. */
|
|
247
|
+
declare function heartbeatSignal(args: {
|
|
248
|
+
directed: DirectedInput;
|
|
249
|
+
status?: string;
|
|
250
|
+
meta?: Json;
|
|
251
|
+
}): Signal;
|
|
252
|
+
/** [C] Write a value to the shared Engram under the given key. */
|
|
253
|
+
declare function memoryAppendSignal(args: {
|
|
254
|
+
traceId: string;
|
|
255
|
+
parentId: string;
|
|
256
|
+
key: string;
|
|
257
|
+
value: unknown;
|
|
258
|
+
directed?: DirectedInput;
|
|
259
|
+
meta?: Json;
|
|
260
|
+
}): Signal;
|
|
261
|
+
/** [C] Broadcast a task to candidate Neurons for competitive bidding. */
|
|
262
|
+
declare function taskOfferSignal(args: {
|
|
263
|
+
traceId: string;
|
|
264
|
+
input: Json;
|
|
265
|
+
parentId?: string | null;
|
|
266
|
+
capabilities?: string[];
|
|
267
|
+
deadlineMs?: number;
|
|
268
|
+
meta?: Json;
|
|
269
|
+
}): Signal;
|
|
270
|
+
/** [C] Cortex bids on behalf of a Neuron in response to a TASK_OFFER. */
|
|
271
|
+
declare function bidSignal(args: {
|
|
272
|
+
traceId: string;
|
|
273
|
+
parentId: string;
|
|
274
|
+
directed?: DirectedInput;
|
|
275
|
+
cost: number;
|
|
276
|
+
etaMs?: number;
|
|
277
|
+
confidence?: number;
|
|
278
|
+
meta?: Json;
|
|
279
|
+
}): Signal;
|
|
280
|
+
/** [C] Peer review of another Neuron's output. `verdict`: 'pass' | 'fail' | 'revise'. */
|
|
281
|
+
declare function critiqueSignal(args: {
|
|
282
|
+
traceId: string;
|
|
283
|
+
parentId: string;
|
|
284
|
+
targetEventId: string;
|
|
285
|
+
issues: Json[];
|
|
286
|
+
verdict: "pass" | "fail" | "revise";
|
|
287
|
+
directed?: DirectedInput;
|
|
288
|
+
meta?: Json;
|
|
289
|
+
}): Signal;
|
|
290
|
+
/** [C] Structured plan emitted before execution. */
|
|
291
|
+
declare function planSignal(args: {
|
|
292
|
+
traceId: string;
|
|
293
|
+
parentId: string;
|
|
294
|
+
steps: Json[];
|
|
295
|
+
rationale?: string;
|
|
296
|
+
directed?: DirectedInput;
|
|
297
|
+
meta?: Json;
|
|
298
|
+
}): Signal;
|
|
299
|
+
/** [C] Streaming reasoning chunk. */
|
|
300
|
+
declare function thoughtDeltaSignal(args: {
|
|
301
|
+
traceId: string;
|
|
302
|
+
parentId: string;
|
|
303
|
+
delta: string;
|
|
304
|
+
seq?: number;
|
|
305
|
+
directed?: DirectedInput;
|
|
306
|
+
meta?: Json;
|
|
307
|
+
}): Signal;
|
|
308
|
+
/** [C] Neuron invoking an external tool. */
|
|
309
|
+
declare function toolCallSignal(args: {
|
|
310
|
+
traceId: string;
|
|
311
|
+
parentId: string;
|
|
312
|
+
tool: string;
|
|
313
|
+
args: Json;
|
|
314
|
+
callId?: string;
|
|
315
|
+
directed?: DirectedInput;
|
|
316
|
+
meta?: Json;
|
|
317
|
+
}): Signal;
|
|
318
|
+
/** [C] Result returned from a tool. Set exactly one of `result` / `error`. */
|
|
319
|
+
declare function toolResultSignal(args: {
|
|
320
|
+
traceId: string;
|
|
321
|
+
parentId: string;
|
|
322
|
+
tool: string;
|
|
323
|
+
result?: unknown;
|
|
324
|
+
error?: string;
|
|
325
|
+
callId?: string;
|
|
326
|
+
directed?: DirectedInput;
|
|
327
|
+
meta?: Json;
|
|
328
|
+
}): Signal;
|
|
329
|
+
/** [C] Escalate a task or sub-decision to a higher-authority Neuron. */
|
|
330
|
+
declare function escalationSignal(args: {
|
|
331
|
+
traceId: string;
|
|
332
|
+
parentId: string;
|
|
333
|
+
reason: string;
|
|
334
|
+
target?: string;
|
|
335
|
+
context?: Json;
|
|
336
|
+
directed?: DirectedInput;
|
|
337
|
+
meta?: Json;
|
|
338
|
+
}): Signal;
|
|
339
|
+
/** [C] Record a consensus outcome among multiple Neurons. */
|
|
340
|
+
declare function consensusSignal(args: {
|
|
341
|
+
traceId: string;
|
|
342
|
+
parentId: string;
|
|
343
|
+
members: string[];
|
|
344
|
+
verdict: string;
|
|
345
|
+
votes?: Json;
|
|
346
|
+
directed?: DirectedInput;
|
|
347
|
+
meta?: Json;
|
|
348
|
+
}): Signal;
|
|
349
|
+
/** [C] Share/synchronise context across Neurons. */
|
|
350
|
+
declare function contextSyncSignal(args: {
|
|
351
|
+
traceId: string;
|
|
352
|
+
parentId: string;
|
|
353
|
+
snapshot: Json;
|
|
354
|
+
version?: string;
|
|
355
|
+
directed?: DirectedInput;
|
|
356
|
+
meta?: Json;
|
|
357
|
+
}): Signal;
|
|
358
|
+
/**
|
|
359
|
+
* [C] Solicit a REGISTER snapshot from peers on a namespace. `neuron` /
|
|
360
|
+
* `capabilities` are payload filter fields (which participants to discover),
|
|
361
|
+
* not envelope addressing.
|
|
362
|
+
*/
|
|
363
|
+
declare function discoverSignal(args?: {
|
|
364
|
+
neuron?: string;
|
|
365
|
+
capabilities?: string[];
|
|
366
|
+
traceId?: string;
|
|
367
|
+
parentId?: string | null;
|
|
368
|
+
meta?: Json;
|
|
369
|
+
}): Signal;
|
|
370
|
+
/**
|
|
371
|
+
* [D] Memory-recall request. Inherits `traceId` from the containing TASK and
|
|
372
|
+
* MUST be addressed: `directed` needs at least one of `id` (engram_id) or
|
|
373
|
+
* `type` (engram_kind). `recallMode` controls fan-out: `"first"` (one winner),
|
|
374
|
+
* `"merge"` (caller merges all by deadline), `"all"` (stream each).
|
|
375
|
+
*/
|
|
376
|
+
declare function recallSignal(args: {
|
|
377
|
+
traceId: string;
|
|
378
|
+
parentId: string;
|
|
379
|
+
directed: DirectedInput;
|
|
380
|
+
query: Json;
|
|
381
|
+
filters?: Json;
|
|
382
|
+
contextRef?: string;
|
|
383
|
+
deadlineMs?: number;
|
|
384
|
+
minConfidence?: number;
|
|
385
|
+
recallMode?: "first" | "merge" | "all";
|
|
386
|
+
meta?: Json;
|
|
387
|
+
}): Signal;
|
|
388
|
+
/** [D] Response from one Engram to a RECALL. `parentId` MUST be the RECALL's id. */
|
|
389
|
+
declare function recalledSignal(args: {
|
|
390
|
+
traceId: string;
|
|
391
|
+
parentId: string;
|
|
392
|
+
engramId: string;
|
|
393
|
+
hits: Json[];
|
|
394
|
+
truncated?: boolean;
|
|
395
|
+
tookMs?: number;
|
|
396
|
+
directed?: DirectedInput;
|
|
397
|
+
meta?: Json;
|
|
398
|
+
}): Signal;
|
|
399
|
+
/**
|
|
400
|
+
* [D] Memory-write request. `op` is one of add | append | merge | upsert |
|
|
401
|
+
* delete; `mergeKey` is required for merge/upsert. MUST be addressed.
|
|
402
|
+
*/
|
|
403
|
+
declare function imprintSignal(args: {
|
|
404
|
+
traceId: string;
|
|
405
|
+
parentId: string;
|
|
406
|
+
directed: DirectedInput;
|
|
407
|
+
op: "add" | "append" | "merge" | "upsert" | "delete";
|
|
408
|
+
entry: Json;
|
|
409
|
+
mergeKey?: string;
|
|
410
|
+
meta?: Json;
|
|
411
|
+
}): Signal;
|
|
412
|
+
/** [D] Receipt of a completed IMPRINT. `parentId` MUST be the IMPRINT's id. */
|
|
413
|
+
declare function imprintedSignal(args: {
|
|
414
|
+
traceId: string;
|
|
415
|
+
parentId: string;
|
|
416
|
+
engramId: string;
|
|
417
|
+
op: string;
|
|
418
|
+
id?: string;
|
|
419
|
+
version?: number;
|
|
420
|
+
tookMs?: number;
|
|
421
|
+
error?: string;
|
|
422
|
+
directed?: DirectedInput;
|
|
423
|
+
meta?: Json;
|
|
424
|
+
}): Signal;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @cosmonapse/sdk - synapse
|
|
428
|
+
*
|
|
429
|
+
* The Synapse interface (five methods) and the in-process MemorySynapse,
|
|
430
|
+
* ported 1:1 from `cosmonapse.synapse.base` / `cosmonapse.synapse.memory`.
|
|
431
|
+
*
|
|
432
|
+
* MemorySynapse is NOT a throwaway test double - it is the adapter that backs
|
|
433
|
+
* the local dev experience, and any code written against it works unchanged
|
|
434
|
+
* against a networked adapter. NatsSynapse is ported (synapse-nats.ts); the
|
|
435
|
+
* Kafka adapter is still outstanding - see PORTING_STATUS.md.
|
|
436
|
+
*
|
|
437
|
+
* Subject convention (ENVELOPE_SPEC.md §10):
|
|
438
|
+
* cosmonapse.<namespace>.<type> e.g. cosmonapse.team_a.TASK
|
|
439
|
+
* cosmonapse.> subscribe to everything
|
|
440
|
+
*
|
|
441
|
+
* Wildcards (same as NATS):
|
|
442
|
+
* * matches exactly one token
|
|
443
|
+
* > matches one or more trailing tokens (must be the last token)
|
|
444
|
+
*/
|
|
445
|
+
|
|
446
|
+
/** A subscriber callback. May be sync or async; async handlers are awaited. */
|
|
447
|
+
type MessageHandler = (signal: Signal) => void | Promise<void>;
|
|
448
|
+
/** Handle for an active subscription. Used to unsubscribe cleanly. */
|
|
449
|
+
interface Subscription {
|
|
450
|
+
unsubscribe(): Promise<void>;
|
|
451
|
+
}
|
|
452
|
+
interface SubscribeOptions {
|
|
453
|
+
/**
|
|
454
|
+
* If set, only one subscriber in the group receives each message
|
|
455
|
+
* (round-robin load balancing). Doppler subscribers must NOT use a group.
|
|
456
|
+
*/
|
|
457
|
+
queueGroup?: string;
|
|
458
|
+
}
|
|
459
|
+
interface RequestOptions {
|
|
460
|
+
timeoutMs?: number;
|
|
461
|
+
}
|
|
462
|
+
/** Abstract base for all Cosmonapse synapse adapters. */
|
|
463
|
+
interface Synapse {
|
|
464
|
+
/** Establish connection / initialise in-memory state. */
|
|
465
|
+
connect(): Promise<void>;
|
|
466
|
+
/** Gracefully disconnect and release resources. */
|
|
467
|
+
close(): Promise<void>;
|
|
468
|
+
/** Publish a Signal to the given subject. */
|
|
469
|
+
publish(subject: string, signal: Signal): Promise<void>;
|
|
470
|
+
/** Subscribe to a subject pattern (exact or wildcard). */
|
|
471
|
+
subscribe(subject: string, handler: MessageHandler, opts?: SubscribeOptions): Promise<Subscription>;
|
|
472
|
+
/** Publish a Signal and wait for exactly one reply. */
|
|
473
|
+
request(subject: string, signal: Signal, opts?: RequestOptions): Promise<Signal>;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* In-process synapse. Supports fan-out, queue groups (round-robin),
|
|
477
|
+
* wildcard subjects, and request/reply. Single-threaded by design.
|
|
478
|
+
*/
|
|
479
|
+
declare class MemorySynapse implements Synapse {
|
|
480
|
+
private subs;
|
|
481
|
+
private counter;
|
|
482
|
+
private connected;
|
|
483
|
+
private rrCounters;
|
|
484
|
+
connect(): Promise<void>;
|
|
485
|
+
close(): Promise<void>;
|
|
486
|
+
private nextId;
|
|
487
|
+
/** @internal */
|
|
488
|
+
_removeHandler(subject: string, handlerId: number): void;
|
|
489
|
+
/**
|
|
490
|
+
* Return true if `subject` matches `pattern`.
|
|
491
|
+
* * matches any single token (no dots)
|
|
492
|
+
* > matches any sequence of trailing tokens
|
|
493
|
+
*/
|
|
494
|
+
static matches(pattern: string, subject: string): boolean;
|
|
495
|
+
publish(subject: string, signal: Signal): Promise<void>;
|
|
496
|
+
private deliver;
|
|
497
|
+
subscribe(subject: string, handler: MessageHandler, opts?: SubscribeOptions): Promise<Subscription>;
|
|
498
|
+
request(subject: string, signal: Signal, opts?: RequestOptions): Promise<Signal>;
|
|
499
|
+
/**
|
|
500
|
+
* Convenience: send `reply` to the `_reply_to` subject stored in
|
|
501
|
+
* `original.meta`. Used by request/reply responders.
|
|
502
|
+
*/
|
|
503
|
+
replyTo(original: Signal, reply: Signal): Promise<void>;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @cosmonapse/sdk - NATS synapse adapter
|
|
508
|
+
*
|
|
509
|
+
* NATS maps onto the Synapse contract directly:
|
|
510
|
+
* - subjects use the same `cosmonapse.<namespace>.<TYPE>` convention
|
|
511
|
+
* - `*` and `>` wildcards are native NATS - no translation needed
|
|
512
|
+
* - queue groups are native (`queue` on subscribe)
|
|
513
|
+
* - request/reply is native (`nc.request`)
|
|
514
|
+
*
|
|
515
|
+
* The `nats` package (nats.js) is **lazy-imported** inside connect(), so this
|
|
516
|
+
* module is safe to load even when `nats` isn't installed. It is declared as
|
|
517
|
+
* an optional dependency: npm i nats
|
|
518
|
+
*
|
|
519
|
+
* Ported from `cosmonapse.synapse.nats`. One enhancement over the Python
|
|
520
|
+
* adapter: the inbound bridge stashes the NATS reply subject into
|
|
521
|
+
* `signal.meta._reply_to`, and `replyTo()` publishes there - so the SAME
|
|
522
|
+
* request/reply responder code works against MemorySynapse and NatsSynapse.
|
|
523
|
+
*/
|
|
524
|
+
|
|
525
|
+
interface NatsSynapseOptions {
|
|
526
|
+
/** NATS server URL or list of URLs. */
|
|
527
|
+
url?: string | string[];
|
|
528
|
+
}
|
|
529
|
+
/** NATS-backed Synapse. Interchangeable with MemorySynapse. */
|
|
530
|
+
declare class NatsSynapse implements Synapse {
|
|
531
|
+
private readonly url;
|
|
532
|
+
private nc;
|
|
533
|
+
private connected;
|
|
534
|
+
constructor(opts?: NatsSynapseOptions);
|
|
535
|
+
connect(): Promise<void>;
|
|
536
|
+
close(): Promise<void>;
|
|
537
|
+
private requireConn;
|
|
538
|
+
publish(subject: string, signal: Signal): Promise<void>;
|
|
539
|
+
subscribe(subject: string, handler: MessageHandler, opts?: SubscribeOptions): Promise<Subscription>;
|
|
540
|
+
request(subject: string, signal: Signal, opts?: RequestOptions): Promise<Signal>;
|
|
541
|
+
/**
|
|
542
|
+
* Send `reply` to the `_reply_to` subject the inbound bridge stashed in
|
|
543
|
+
* `original.meta` (the native NATS inbox). Mirrors MemorySynapse.replyTo so
|
|
544
|
+
* request/reply responder code is adapter-agnostic.
|
|
545
|
+
*/
|
|
546
|
+
replyTo(original: Signal, reply: Signal): Promise<void>;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* @cosmonapse/sdk - local dev synapse
|
|
551
|
+
*
|
|
552
|
+
* A tiny TCP + NDJSON broker, ported 1:1 from `cosmonapse.synapse.dev`.
|
|
553
|
+
*
|
|
554
|
+
* `cosmo synapse start memory` boots a {@link DevSynapseServer} and prints a URL
|
|
555
|
+
* like `cosmo://127.0.0.1:7070`. Any process can then connect to that URL with
|
|
556
|
+
* `const synapse = await connectSynapse("cosmo://...")` and hand the result to a
|
|
557
|
+
* `new Dendrite({ synapse, namespace })` to start exchanging Signals.
|
|
558
|
+
*
|
|
559
|
+
* This is **not** a production synapse. It is the equivalent of MemorySynapse
|
|
560
|
+
* for the case where Axons, Dendrites and Cortices live in separate processes
|
|
561
|
+
* on a developer's laptop. For production use NatsSynapse or KafkaSynapse.
|
|
562
|
+
*
|
|
563
|
+
* Wire protocol (NDJSON over TCP, UTF-8, `\n` framed) - identical to the Python
|
|
564
|
+
* server so the two interoperate:
|
|
565
|
+
*
|
|
566
|
+
* Client -> Server:
|
|
567
|
+
* {"op":"hello"} | {"op":"ping"}
|
|
568
|
+
* {"op":"pub","subject":"a.b.c","frame":"<utf8 JSON of Signal>"}
|
|
569
|
+
* {"op":"sub","sub_id":"s1","subject":"a.b.*","queue_group":null}
|
|
570
|
+
* {"op":"unsub","sub_id":"s1"}
|
|
571
|
+
* {"op":"ns_register","namespace":"dev","transport":"memory"}
|
|
572
|
+
* {"op":"mgmt_list"} | {"op":"mgmt_info","namespace":"dev"} | {"op":"mgmt_stop","namespace":"dev"}
|
|
573
|
+
*
|
|
574
|
+
* Server -> Client:
|
|
575
|
+
* {"op":"welcome"} | {"op":"pong"}
|
|
576
|
+
* {"op":"msg","sub_id":"s1","subject":"a.b.c","frame":"<utf8 JSON>"}
|
|
577
|
+
* {"op":"err","message":"..."}
|
|
578
|
+
* {"op":"ns_registered","namespace":"dev"}
|
|
579
|
+
* {"op":"mgmt_ns_list","namespaces":[...]} | {"op":"mgmt_ns_info",...}
|
|
580
|
+
* {"op":"mgmt_stop_ack","namespace":"dev"} | {"op":"ns_stopping","namespace":"dev"}
|
|
581
|
+
*
|
|
582
|
+
* Subject matching uses the same `*` / `>` wildcards as MemorySynapse and NATS.
|
|
583
|
+
* Queue groups load-balance round-robin within the group; subscribers with no
|
|
584
|
+
* queue_group (Dopplers) each receive every matching message.
|
|
585
|
+
*/
|
|
586
|
+
|
|
587
|
+
interface DevSynapseServerOptions {
|
|
588
|
+
host?: string;
|
|
589
|
+
port?: number;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* TCP server speaking the dev-synapse wire protocol.
|
|
593
|
+
*
|
|
594
|
+
* ```ts
|
|
595
|
+
* const server = new DevSynapseServer({ host: "127.0.0.1", port: 7070 });
|
|
596
|
+
* await server.start();
|
|
597
|
+
* console.log(server.url); // cosmo://127.0.0.1:7070
|
|
598
|
+
* // ...
|
|
599
|
+
* await server.stop();
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
declare class DevSynapseServer {
|
|
603
|
+
private _host;
|
|
604
|
+
private _port;
|
|
605
|
+
private server;
|
|
606
|
+
private readonly sessions;
|
|
607
|
+
private readonly rrCounters;
|
|
608
|
+
private readonly namespaces;
|
|
609
|
+
/**
|
|
610
|
+
* Optional observer hook: every published Signal is passed here as
|
|
611
|
+
* (subject, frame) before fan-out. Used by `cosmo synapse start` to stream
|
|
612
|
+
* to stdout.
|
|
613
|
+
*/
|
|
614
|
+
onSignal: ((subject: string, frame: string) => void) | null;
|
|
615
|
+
constructor(opts?: DevSynapseServerOptions);
|
|
616
|
+
get host(): string;
|
|
617
|
+
get port(): number;
|
|
618
|
+
get url(): string;
|
|
619
|
+
get sessionCount(): number;
|
|
620
|
+
start(): Promise<void>;
|
|
621
|
+
stop(): Promise<void>;
|
|
622
|
+
private handleClient;
|
|
623
|
+
private handleOp;
|
|
624
|
+
private deliver;
|
|
625
|
+
}
|
|
626
|
+
interface DevSynapseOptions {
|
|
627
|
+
host?: string;
|
|
628
|
+
port?: number;
|
|
629
|
+
/** Convenience: pass `cosmo://host:port` instead of host/port. */
|
|
630
|
+
url?: string;
|
|
631
|
+
}
|
|
632
|
+
/** TCP / NDJSON client speaking to a {@link DevSynapseServer}. */
|
|
633
|
+
declare class DevSynapse implements Synapse {
|
|
634
|
+
private _host;
|
|
635
|
+
private _port;
|
|
636
|
+
private socket;
|
|
637
|
+
private connected;
|
|
638
|
+
private readonly handlers;
|
|
639
|
+
private subCounter;
|
|
640
|
+
private readonly splitter;
|
|
641
|
+
constructor(opts?: DevSynapseOptions);
|
|
642
|
+
get url(): string;
|
|
643
|
+
connect(): Promise<void>;
|
|
644
|
+
close(): Promise<void>;
|
|
645
|
+
private onFrame;
|
|
646
|
+
private send;
|
|
647
|
+
/** @internal - used by DevSubscription. */
|
|
648
|
+
_sendUnsub(subId: string): Promise<void>;
|
|
649
|
+
publish(subject: string, signal: Signal): Promise<void>;
|
|
650
|
+
subscribe(subject: string, handler: MessageHandler, opts?: SubscribeOptions): Promise<Subscription>;
|
|
651
|
+
request(subject: string, signal: Signal, opts?: RequestOptions): Promise<Signal>;
|
|
652
|
+
/** Send `reply` to the `_reply_to` subject stored in `original.meta`. */
|
|
653
|
+
replyTo(original: Signal, reply: Signal): Promise<void>;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* @cosmonapse/sdk - Kafka synapse adapter
|
|
658
|
+
*
|
|
659
|
+
* Ported from `cosmonapse.synapse.kafka`.
|
|
660
|
+
*
|
|
661
|
+
* Kafka does not map onto the Synapse contract as cleanly as NATS - a few
|
|
662
|
+
* translations are needed:
|
|
663
|
+
*
|
|
664
|
+
* Cosmonapse Kafka
|
|
665
|
+
* -------------------------- ----------------------------------
|
|
666
|
+
* subject `a.b.TYPE` topic `a.b.TYPE` (verbatim)
|
|
667
|
+
* wildcard `a.b.*` / `a.>` regex consumer subscription
|
|
668
|
+
* (`^a\.b\.[^.]+$` / `^a\..+$`)
|
|
669
|
+
* queueGroup="workers" consumer `groupId="workers"`
|
|
670
|
+
* no queueGroup (Doppler) consumer `groupId=<unique>` so it joins its
|
|
671
|
+
* own group and sees every message
|
|
672
|
+
* request / reply per-call reply topic; the requester
|
|
673
|
+
* subscribes to its inbox before publishing,
|
|
674
|
+
* then awaits one message whose parent_id
|
|
675
|
+
* matches the request's signal id.
|
|
676
|
+
*
|
|
677
|
+
* The `kafkajs` library is **lazy-imported** - the module loads fine without it;
|
|
678
|
+
* `connect()` raises a clear error if it is missing. Install: npm i kafkajs
|
|
679
|
+
*
|
|
680
|
+
* Caveats
|
|
681
|
+
* -------
|
|
682
|
+
* - Kafka topics must exist (`auto.create.topics.enable=true` on the broker if
|
|
683
|
+
* you want them created on first publish).
|
|
684
|
+
* - High-fan-out request/reply is poorly suited to Kafka. Prefer NATS for that;
|
|
685
|
+
* use Kafka where you want the long-term audit log of every Signal.
|
|
686
|
+
*/
|
|
687
|
+
|
|
688
|
+
interface KafkaSynapseOptions {
|
|
689
|
+
/** Kafka broker(s), e.g. "localhost:9092" or a list. */
|
|
690
|
+
bootstrapServers?: string | string[];
|
|
691
|
+
/** Optional client identifier. */
|
|
692
|
+
clientId?: string;
|
|
693
|
+
}
|
|
694
|
+
/** Kafka-backed Synapse. Interchangeable with MemorySynapse. */
|
|
695
|
+
declare class KafkaSynapse implements Synapse {
|
|
696
|
+
private readonly brokers;
|
|
697
|
+
private readonly clientId;
|
|
698
|
+
private kafka;
|
|
699
|
+
private producer;
|
|
700
|
+
private readonly consumers;
|
|
701
|
+
private connected;
|
|
702
|
+
constructor(opts?: KafkaSynapseOptions);
|
|
703
|
+
connect(): Promise<void>;
|
|
704
|
+
close(): Promise<void>;
|
|
705
|
+
publish(subject: string, signal: Signal): Promise<void>;
|
|
706
|
+
subscribe(subject: string, handler: MessageHandler, opts?: SubscribeOptions): Promise<Subscription>;
|
|
707
|
+
request(subject: string, signal: Signal, opts?: RequestOptions): Promise<Signal>;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* @cosmonapse/sdk - synapse URL factory
|
|
712
|
+
*
|
|
713
|
+
* Synapse URL -> Synapse factory and connector. Ported 1:1 from
|
|
714
|
+
* `cosmonapse._url`.
|
|
715
|
+
*
|
|
716
|
+
* A Dendrite (or Cortex) does NOT own the Synapse. It uses a Synapse that the
|
|
717
|
+
* caller has built and connected. The caller is also responsible for closing it.
|
|
718
|
+
*
|
|
719
|
+
* cosmo://host:port -> DevSynapse (local dev / `cosmo dev synapse`)
|
|
720
|
+
* nats://host:port -> NatsSynapse
|
|
721
|
+
* kafka://host:port -> KafkaSynapse
|
|
722
|
+
*
|
|
723
|
+
* For the in-process MemorySynapse, construct it directly - a URL would be
|
|
724
|
+
* ambiguous across processes.
|
|
725
|
+
*/
|
|
726
|
+
|
|
727
|
+
/** Build (but do not connect) a Synapse from a Cosmonapse synapse URL. */
|
|
728
|
+
declare function synapseFromUrl(url: string): Synapse;
|
|
729
|
+
/**
|
|
730
|
+
* Build a Synapse from `url` and `.connect()` it. Return the connected Synapse
|
|
731
|
+
* so the caller can pass it to Dendrites / Cortices and close it when finished:
|
|
732
|
+
*
|
|
733
|
+
* ```ts
|
|
734
|
+
* const synapse = await connectSynapse("cosmo://127.0.0.1:7070");
|
|
735
|
+
* try {
|
|
736
|
+
* const dendrite = new Dendrite({ synapse, registryStore });
|
|
737
|
+
* await dendrite.start();
|
|
738
|
+
* // ...
|
|
739
|
+
* } finally {
|
|
740
|
+
* await synapse.close();
|
|
741
|
+
* }
|
|
742
|
+
* ```
|
|
743
|
+
*
|
|
744
|
+
* Multiple Dendrites and Cortices can share the same Synapse instance. Closing
|
|
745
|
+
* the Synapse is the caller's responsibility - no component will close it.
|
|
746
|
+
*/
|
|
747
|
+
declare function connectSynapse(url: string): Promise<Synapse>;
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* @cosmonapse/sdk - registry store
|
|
751
|
+
*
|
|
752
|
+
* The one mandatory persistent surface: a live view of the Neurons a namespace
|
|
753
|
+
* has seen. Ported from `cosmonapse.storage.base` / `cosmonapse.storage.memory`.
|
|
754
|
+
*
|
|
755
|
+
* Timestamps are RFC 3339 strings (matching the envelope's `ts`), so records
|
|
756
|
+
* serialise cleanly to JSON. A backend is conformant iff it behaves like
|
|
757
|
+
* MemoryRegistryStore (the reference implementation).
|
|
758
|
+
*
|
|
759
|
+
* NAMING - snake_case is deliberate. NeuronRecord fields (`neuron_id`,
|
|
760
|
+
* `last_heartbeat`, `registered_at`) intentionally use snake_case rather than
|
|
761
|
+
* the usual TS camelCase because a record IS the on-the-wire / on-disk shape:
|
|
762
|
+
* it round-trips verbatim through JSON and across the Python and TS SDKs, which
|
|
763
|
+
* share one registry schema. Renaming to camelCase here would force a
|
|
764
|
+
* translation layer at every Synapse and storage boundary and break
|
|
765
|
+
* cross-language parity. Treat these field names as part of the wire contract,
|
|
766
|
+
* not as a TS style choice. (See PORTING_STATUS.md if a camelCase view/adapter
|
|
767
|
+
* is ever added on top.)
|
|
768
|
+
*
|
|
769
|
+
* (Python additionally ships sqlite/postgres backends; only the in-memory
|
|
770
|
+
* backend is ported here - see PORTING_STATUS.md. Implement the RegistryStore
|
|
771
|
+
* interface for others.)
|
|
772
|
+
*/
|
|
773
|
+
type NeuronStatus = "registered" | "draining" | "deregistered";
|
|
774
|
+
/**
|
|
775
|
+
* A live view of one Neuron the namespace has seen.
|
|
776
|
+
*
|
|
777
|
+
* Fields are snake_case on purpose - this is the serialised wire/registry
|
|
778
|
+
* shape shared with the Python SDK, not an idiomatic TS object. See the file
|
|
779
|
+
* header for the rationale.
|
|
780
|
+
*/
|
|
781
|
+
interface NeuronRecord {
|
|
782
|
+
neuron_id: string;
|
|
783
|
+
capabilities: string[];
|
|
784
|
+
version: string | null;
|
|
785
|
+
status: NeuronStatus;
|
|
786
|
+
last_heartbeat: string | null;
|
|
787
|
+
registered_at: string;
|
|
788
|
+
}
|
|
789
|
+
interface NeuronRecordInit {
|
|
790
|
+
neuron_id: string;
|
|
791
|
+
capabilities?: string[];
|
|
792
|
+
version?: string | null;
|
|
793
|
+
status?: NeuronStatus;
|
|
794
|
+
last_heartbeat?: string | null;
|
|
795
|
+
registered_at?: string;
|
|
796
|
+
}
|
|
797
|
+
/** Build a NeuronRecord, filling defaults (status "registered", now()). */
|
|
798
|
+
declare function neuronRecord(init: NeuronRecordInit): NeuronRecord;
|
|
799
|
+
interface ListOptions {
|
|
800
|
+
capability?: string;
|
|
801
|
+
includeDeregistered?: boolean;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Abstract registry store. All methods are async. Implementations must be safe
|
|
805
|
+
* for concurrent calls within one event loop; cross-process consistency is a
|
|
806
|
+
* backend concern (use a shared DB when multiple Cortices share state).
|
|
807
|
+
*/
|
|
808
|
+
interface RegistryStore {
|
|
809
|
+
connect(): Promise<void>;
|
|
810
|
+
close(): Promise<void>;
|
|
811
|
+
/** Insert or update a record by neuron_id (called on REGISTER). */
|
|
812
|
+
upsert(record: NeuronRecord): Promise<void>;
|
|
813
|
+
/** Mark an existing record deregistered. No-op if unknown. */
|
|
814
|
+
markDeregistered(neuronId: string): Promise<void>;
|
|
815
|
+
/**
|
|
816
|
+
* Update last_heartbeat (and optionally status). If unknown, the backend
|
|
817
|
+
* MAY create a thin record - tolerating heartbeats that arrive before
|
|
818
|
+
* REGISTER.
|
|
819
|
+
*/
|
|
820
|
+
touchHeartbeat(neuronId: string, ts: string, status?: NeuronStatus): Promise<void>;
|
|
821
|
+
get(neuronId: string): Promise<NeuronRecord | null>;
|
|
822
|
+
list(opts?: ListOptions): Promise<NeuronRecord[]>;
|
|
823
|
+
}
|
|
824
|
+
/** In-process RegistryStore. Default backend; reset on process restart. */
|
|
825
|
+
declare class MemoryRegistryStore implements RegistryStore {
|
|
826
|
+
private records;
|
|
827
|
+
connect(): Promise<void>;
|
|
828
|
+
close(): Promise<void>;
|
|
829
|
+
upsert(record: NeuronRecord): Promise<void>;
|
|
830
|
+
markDeregistered(neuronId: string): Promise<void>;
|
|
831
|
+
touchHeartbeat(neuronId: string, ts: string, status?: NeuronStatus): Promise<void>;
|
|
832
|
+
get(neuronId: string): Promise<NeuronRecord | null>;
|
|
833
|
+
list(opts?: ListOptions): Promise<NeuronRecord[]>;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* @cosmonapse/sdk - SQLite registry store
|
|
838
|
+
*
|
|
839
|
+
* Ported from `cosmonapse.storage.sqlite`. A single file on disk (or
|
|
840
|
+
* `:memory:`), backed by `better-sqlite3` (lazy-imported, optional dependency:
|
|
841
|
+
* `npm i better-sqlite3`). The Python port uses stdlib sqlite3; Node has no
|
|
842
|
+
* stable built-in equivalent, so we use the de-facto standard driver.
|
|
843
|
+
*
|
|
844
|
+
* The schema is created on `connect()` if it does not already exist. Records
|
|
845
|
+
* round-trip the same on-disk shape as the Python SDK: capabilities as a JSON
|
|
846
|
+
* array, timestamps as RFC 3339 strings.
|
|
847
|
+
*/
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* SQLite-backed RegistryStore.
|
|
851
|
+
*
|
|
852
|
+
* @param path Filesystem path to the DB file. Use `:memory:` for an ephemeral
|
|
853
|
+
* in-process DB (useful for tests; not shared across connections).
|
|
854
|
+
*/
|
|
855
|
+
declare class SqliteRegistryStore implements RegistryStore {
|
|
856
|
+
private readonly path;
|
|
857
|
+
private db;
|
|
858
|
+
constructor(path?: string);
|
|
859
|
+
connect(): Promise<void>;
|
|
860
|
+
close(): Promise<void>;
|
|
861
|
+
private require;
|
|
862
|
+
upsert(record: NeuronRecord): Promise<void>;
|
|
863
|
+
markDeregistered(neuronId: string): Promise<void>;
|
|
864
|
+
touchHeartbeat(neuronId: string, ts: string, status?: NeuronStatus): Promise<void>;
|
|
865
|
+
get(neuronId: string): Promise<NeuronRecord | null>;
|
|
866
|
+
list(opts?: ListOptions): Promise<NeuronRecord[]>;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* @cosmonapse/sdk - Postgres registry store
|
|
871
|
+
*
|
|
872
|
+
* Ported from `cosmonapse.storage.postgres`. Backed by `pg` (node-postgres),
|
|
873
|
+
* lazy-imported as an optional dependency: `npm i pg`. Only `connect()` raises
|
|
874
|
+
* a clear error when the package is missing.
|
|
875
|
+
*
|
|
876
|
+
* The schema is bootstrapped on first `connect()`. Use a dedicated schema /
|
|
877
|
+
* database for the Cortex if you don't want it sharing a namespace with your
|
|
878
|
+
* application tables.
|
|
879
|
+
*
|
|
880
|
+
* Records round-trip the same shape as the Python SDK: capabilities as JSONB,
|
|
881
|
+
* timestamps as RFC 3339 strings on the wire.
|
|
882
|
+
*/
|
|
883
|
+
|
|
884
|
+
interface PostgresRegistryStoreOptions {
|
|
885
|
+
/** Postgres DSN, e.g. "postgresql://user:pass@host:5432/db". */
|
|
886
|
+
dsn: string;
|
|
887
|
+
minSize?: number;
|
|
888
|
+
maxSize?: number;
|
|
889
|
+
}
|
|
890
|
+
/** Postgres-backed RegistryStore via node-postgres connection pool. */
|
|
891
|
+
declare class PostgresRegistryStore implements RegistryStore {
|
|
892
|
+
private readonly dsn;
|
|
893
|
+
private readonly minSize;
|
|
894
|
+
private readonly maxSize;
|
|
895
|
+
private pool;
|
|
896
|
+
constructor(opts: PostgresRegistryStoreOptions);
|
|
897
|
+
connect(): Promise<void>;
|
|
898
|
+
close(): Promise<void>;
|
|
899
|
+
private require;
|
|
900
|
+
upsert(record: NeuronRecord): Promise<void>;
|
|
901
|
+
markDeregistered(neuronId: string): Promise<void>;
|
|
902
|
+
touchHeartbeat(neuronId: string, ts: string, status?: NeuronStatus): Promise<void>;
|
|
903
|
+
get(neuronId: string): Promise<NeuronRecord | null>;
|
|
904
|
+
list(opts?: ListOptions): Promise<NeuronRecord[]>;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* @cosmonapse/sdk - lifecycle hooks
|
|
909
|
+
*
|
|
910
|
+
* Shared lifecycle-hook surface for Axon and Dendrite / Cortex, ported from
|
|
911
|
+
* `cosmonapse._hooks`. Three hook kinds cover both the centralised
|
|
912
|
+
* (orchestrator-first) and decentralised (peer-to-peer) cases:
|
|
913
|
+
*
|
|
914
|
+
* onConnect fire-once after the component finishes its own connect
|
|
915
|
+
* handshake (Axon attached + registered, Dendrite up on the
|
|
916
|
+
* Synapse).
|
|
917
|
+
* onRefresh fired whenever the component's observable state refreshes -
|
|
918
|
+
* heartbeat tick, REGISTER / DEREGISTER / HEARTBEAT seen by the
|
|
919
|
+
* registry, or a manual `refresh()`. The handler receives a
|
|
920
|
+
* {@link RefreshEvent} describing what changed.
|
|
921
|
+
* onSchedule developer-supplied periodic task. Runs as a background loop
|
|
922
|
+
* every `everyMs` until the component stops.
|
|
923
|
+
*
|
|
924
|
+
* Decentralised use case - each Dendrite can announce itself on connect,
|
|
925
|
+
* reconcile peers on refresh, and gossip on a schedule, so peer-to-peer
|
|
926
|
+
* fabrics emerge without the SDK baking in an orchestration model.
|
|
927
|
+
*
|
|
928
|
+
* Python uses a mixin; TypeScript favours composition, so the host holds a
|
|
929
|
+
* `LifecycleHooks` instance and drives it (`_fireConnect`, `_launchSchedule`,
|
|
930
|
+
* `_fireRefresh`, `_stopHooks`). The `on*` decorators are re-exposed on the
|
|
931
|
+
* host (Axon / Dendrite) for ergonomics.
|
|
932
|
+
*/
|
|
933
|
+
/** Context passed to onRefresh hooks. */
|
|
934
|
+
interface RefreshEvent {
|
|
935
|
+
/** "heartbeat" | "register" | "deregister" | "manual" | "scheduled" */
|
|
936
|
+
reason: string;
|
|
937
|
+
/** The neuron implicated in the change, if any. */
|
|
938
|
+
neuronId?: string | null;
|
|
939
|
+
/** Free-form bag for component-specific detail. */
|
|
940
|
+
extra: Record<string, unknown>;
|
|
941
|
+
}
|
|
942
|
+
type ConnectHook<O> = (owner: O) => void | Promise<void>;
|
|
943
|
+
type RefreshHook<O> = (owner: O, event: RefreshEvent) => void | Promise<void>;
|
|
944
|
+
type ScheduleHook<O> = (owner: O) => void | Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* Lifecycle-hook registry + driver. Generic over the owner type so handlers
|
|
947
|
+
* receive a fully-typed back-reference to the component they're attached to.
|
|
948
|
+
*/
|
|
949
|
+
declare class LifecycleHooks<O> {
|
|
950
|
+
private readonly owner;
|
|
951
|
+
private readonly connectHooks;
|
|
952
|
+
private readonly refreshHooks;
|
|
953
|
+
private readonly scheduleHooks;
|
|
954
|
+
private readonly timers;
|
|
955
|
+
private started;
|
|
956
|
+
constructor(owner: O);
|
|
957
|
+
/** Register a fire-once handler called after the host finishes start(). */
|
|
958
|
+
onConnect(fn: ConnectHook<O>): ConnectHook<O>;
|
|
959
|
+
/** Register a handler called whenever the host's state refreshes. */
|
|
960
|
+
onRefresh(fn: RefreshHook<O>): RefreshHook<O>;
|
|
961
|
+
/**
|
|
962
|
+
* Register a periodic handler. The background loop runs every `everyMs`
|
|
963
|
+
* for the lifetime of the host. If the host is already running, the loop
|
|
964
|
+
* starts immediately.
|
|
965
|
+
*/
|
|
966
|
+
onSchedule(everyMs: number, fn: ScheduleHook<O>): ScheduleHook<O>;
|
|
967
|
+
/** @internal */
|
|
968
|
+
_fireConnect(): Promise<void>;
|
|
969
|
+
/** @internal */
|
|
970
|
+
_fireRefresh(event: RefreshEvent): Promise<void>;
|
|
971
|
+
/** @internal */
|
|
972
|
+
_launchSchedule(): void;
|
|
973
|
+
/** @internal */
|
|
974
|
+
_stopHooks(): void;
|
|
975
|
+
/**
|
|
976
|
+
* Manually fire a refresh event. Useful when a developer's own code knows
|
|
977
|
+
* internal state has changed.
|
|
978
|
+
*/
|
|
979
|
+
refresh(opts?: {
|
|
980
|
+
reason?: string;
|
|
981
|
+
neuronId?: string | null;
|
|
982
|
+
extra?: Record<string, unknown>;
|
|
983
|
+
}): Promise<void>;
|
|
984
|
+
private spawnLoop;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* @cosmonapse/sdk - neuron contract
|
|
989
|
+
*
|
|
990
|
+
* A Neuron is a pure function - `(input, context) => output`. It has zero
|
|
991
|
+
* knowledge of the protocol, envelopes, trace IDs, or workflow semantics.
|
|
992
|
+
* Any existing LLM-driven agent can be wrapped to satisfy this signature and
|
|
993
|
+
* become a protocol participant with no modification.
|
|
994
|
+
*
|
|
995
|
+
* Provider-backed Neuron factories (Ollama / HuggingFace over `fetch`) are
|
|
996
|
+
* available via `ollamaNeuron` / `huggingFaceNeuron` (see neuron-http.ts) or the
|
|
997
|
+
* unified `neuron("ollama" | "huggingface", …)` factory - or bring your own
|
|
998
|
+
* async function that satisfies this signature.
|
|
999
|
+
*/
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* The Neuron function type.
|
|
1003
|
+
* input - `payload.input` from the TASK envelope (arbitrary JSON).
|
|
1004
|
+
* context - resolved by the Axon from `payload.context_ref` (empty if none).
|
|
1005
|
+
* returns - a JSON-serialisable object, a {@link ClarificationOutput}, or a
|
|
1006
|
+
* {@link PermissionRequestOutput}.
|
|
1007
|
+
*/
|
|
1008
|
+
type NeuronFn = (input: Json, context: unknown[]) => Promise<Json> | Json;
|
|
1009
|
+
/**
|
|
1010
|
+
* A NeuronFn that also exposes an async `close()` to release any resource it
|
|
1011
|
+
* holds (e.g. an MCP subprocess). The Axon calls `close()` automatically when
|
|
1012
|
+
* it deregisters.
|
|
1013
|
+
*/
|
|
1014
|
+
interface CloseableNeuronFn extends NeuronFn {
|
|
1015
|
+
close(): Promise<void>;
|
|
1016
|
+
}
|
|
1017
|
+
/** Async fetcher the Axon uses to resolve a `context_ref` into context items. */
|
|
1018
|
+
type ContextFetcher = (ref: string) => Promise<unknown[]> | unknown[];
|
|
1019
|
+
/**
|
|
1020
|
+
* Marker a Neuron returns to request more information instead of producing a
|
|
1021
|
+
* result. The Axon converts this into a CLARIFICATION signal.
|
|
1022
|
+
*/
|
|
1023
|
+
interface ClarificationOutput {
|
|
1024
|
+
__clarification__: true;
|
|
1025
|
+
question: string;
|
|
1026
|
+
context?: Json;
|
|
1027
|
+
}
|
|
1028
|
+
/** Build a clarification result for a Neuron to return. */
|
|
1029
|
+
declare function clarify(question: string, context?: Json): ClarificationOutput;
|
|
1030
|
+
/** Type guard: did the Neuron return a clarification marker? */
|
|
1031
|
+
declare function isClarification(output: unknown): output is ClarificationOutput;
|
|
1032
|
+
/**
|
|
1033
|
+
* Marker a Neuron returns to request permission to perform `action` instead of
|
|
1034
|
+
* producing a result. The Axon converts this into a PERMISSION signal. Same
|
|
1035
|
+
* return-and-resume shape as {@link ClarificationOutput}: the Neuron typically
|
|
1036
|
+
* tries `recall(...)` against an Engram first and only returns this on a miss.
|
|
1037
|
+
*/
|
|
1038
|
+
interface PermissionRequestOutput {
|
|
1039
|
+
__permission__: true;
|
|
1040
|
+
action: string;
|
|
1041
|
+
scope?: Json;
|
|
1042
|
+
reason?: string;
|
|
1043
|
+
context?: Json;
|
|
1044
|
+
}
|
|
1045
|
+
/** Build a permission-request result for a Neuron to return. */
|
|
1046
|
+
declare function permissionRequest(action: string, opts?: {
|
|
1047
|
+
scope?: Json;
|
|
1048
|
+
reason?: string;
|
|
1049
|
+
context?: Json;
|
|
1050
|
+
}): PermissionRequestOutput;
|
|
1051
|
+
/** Type guard: did the Neuron return a permission-request marker? */
|
|
1052
|
+
declare function isPermissionRequest(output: unknown): output is PermissionRequestOutput;
|
|
1053
|
+
/**
|
|
1054
|
+
* Marker that yields an ERROR signal without throwing. A recogniser (e.g. the
|
|
1055
|
+
* MCP one on `is_error`) returns this so the Axon emits ERROR with a supplied
|
|
1056
|
+
* code/message instead of a generic NEURON_EXCEPTION.
|
|
1057
|
+
*/
|
|
1058
|
+
interface ErrorOutput {
|
|
1059
|
+
__error__: true;
|
|
1060
|
+
code?: string;
|
|
1061
|
+
message?: string;
|
|
1062
|
+
recoverable?: boolean;
|
|
1063
|
+
}
|
|
1064
|
+
/** Build an error result for a Neuron / recogniser to return. */
|
|
1065
|
+
declare function errorResult(message: string, opts?: {
|
|
1066
|
+
code?: string;
|
|
1067
|
+
recoverable?: boolean;
|
|
1068
|
+
}): ErrorOutput;
|
|
1069
|
+
/** Type guard: did the Neuron / recogniser return an error marker? */
|
|
1070
|
+
declare function isErrorOutput(output: unknown): output is ErrorOutput;
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* @cosmonapse/sdk - MCP-server Neuron
|
|
1074
|
+
*
|
|
1075
|
+
* Wrap **any stdio MCP server** as a Neuron. This is a thin client wrapper -
|
|
1076
|
+
* it does NOT implement an MCP server. It spawns an existing server as a
|
|
1077
|
+
* subprocess, speaks MCP over stdio via `@modelcontextprotocol/sdk`, and
|
|
1078
|
+
* exposes that server's tools behind the NeuronFn signature. A TASK becomes an
|
|
1079
|
+
* MCP `tools/call`; the tool result becomes the Neuron output.
|
|
1080
|
+
*
|
|
1081
|
+
* The `@modelcontextprotocol/sdk` package is an optional peer dependency and is
|
|
1082
|
+
* imported lazily, so projects that don't use MCP neurons don't need it.
|
|
1083
|
+
*
|
|
1084
|
+
* ```ts
|
|
1085
|
+
* import { Axon, mcpNeuron } from "@cosmonapse/sdk";
|
|
1086
|
+
*
|
|
1087
|
+
* const files = mcpNeuron({ server: "filesystem", args: ["/data"], tool: "read_file" });
|
|
1088
|
+
* const axon = new Axon({ neuronId: "files", neuronFn: files });
|
|
1089
|
+
* ```
|
|
1090
|
+
*
|
|
1091
|
+
* Input dict:
|
|
1092
|
+
* tool Tool to call (falls back to `opts.tool`, or the sole tool).
|
|
1093
|
+
* arguments | args Tool arguments (object). If omitted, all non-control keys
|
|
1094
|
+
* are used as arguments.
|
|
1095
|
+
* __list_tools__ When truthy, return the server's tool catalogue.
|
|
1096
|
+
*
|
|
1097
|
+
* Output (`tools/call`):
|
|
1098
|
+
* { response, result, is_error, content, meta:{tool,server,command} }
|
|
1099
|
+
* Output (`__list_tools__`):
|
|
1100
|
+
* { tools: [{ name, description, input_schema }] }
|
|
1101
|
+
*/
|
|
1102
|
+
|
|
1103
|
+
interface McpNeuronOptions {
|
|
1104
|
+
/** Executable to spawn (e.g. "npx", "uvx"). Required unless `server` is set. */
|
|
1105
|
+
command?: string;
|
|
1106
|
+
/** Arguments. Appended after a preset's args when `server` is set. */
|
|
1107
|
+
args?: string[];
|
|
1108
|
+
/** Name of a standard server preset (see {@link standardMcpServers}). */
|
|
1109
|
+
server?: string;
|
|
1110
|
+
/** Extra environment variables for the subprocess. */
|
|
1111
|
+
env?: Record<string, string>;
|
|
1112
|
+
/** Working directory for the subprocess. */
|
|
1113
|
+
cwd?: string;
|
|
1114
|
+
/** Default tool name used when the input omits `tool`. */
|
|
1115
|
+
tool?: string;
|
|
1116
|
+
clientName?: string;
|
|
1117
|
+
clientVersion?: string;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Launch specs for standard, separately-published MCP servers. We wrap them -
|
|
1121
|
+
* we do not ship them. Anything in `opts.args` is appended (e.g. allowed dirs
|
|
1122
|
+
* for filesystem, `--repository <path>` for git).
|
|
1123
|
+
*/
|
|
1124
|
+
declare const standardMcpServers: Record<string, {
|
|
1125
|
+
command: string;
|
|
1126
|
+
args: string[];
|
|
1127
|
+
note: string;
|
|
1128
|
+
}>;
|
|
1129
|
+
declare function mcpNeuron(opts: McpNeuronOptions): CloseableNeuronFn;
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* @cosmonapse/sdk - provider-backed Neurons (LLM over HTTP)
|
|
1133
|
+
*
|
|
1134
|
+
* Ported from `cosmonapse.neuron` (`_OllamaNeuron` / `_HuggingFaceNeuron`) and
|
|
1135
|
+
* `cosmonapse._neuron_base`. Wrap a running LLM server behind the `NeuronFn`
|
|
1136
|
+
* signature so it slots straight into an Axon.
|
|
1137
|
+
*
|
|
1138
|
+
* Uses the global `fetch` (Node 18+), so there is no extra dependency - the
|
|
1139
|
+
* Python port needs `httpx`; in Node the runtime ships the client.
|
|
1140
|
+
*
|
|
1141
|
+
* Input convention (shared with the Python SDK):
|
|
1142
|
+
* - `prompt` (string) - single-turn input, or
|
|
1143
|
+
* - `messages` (OpenAI-style `[{ role, content }]`) - multi-turn / system.
|
|
1144
|
+
* (`text` / `query` / `content` are also accepted as a prompt alias.)
|
|
1145
|
+
*
|
|
1146
|
+
* Output: `{ response: "<text>", meta: <raw provider payload> }`.
|
|
1147
|
+
*/
|
|
1148
|
+
|
|
1149
|
+
interface OllamaNeuronOptions {
|
|
1150
|
+
/** Ollama model tag, e.g. "llama3", "mistral", "phi3". */
|
|
1151
|
+
model: string;
|
|
1152
|
+
/** Base URL of the Ollama daemon. Default "http://localhost:11434". */
|
|
1153
|
+
endpoint?: string;
|
|
1154
|
+
/** Optional system prompt injected before any user message. */
|
|
1155
|
+
system?: string;
|
|
1156
|
+
temperature?: number;
|
|
1157
|
+
/** Maximum tokens to generate (`num_predict` in Ollama). */
|
|
1158
|
+
maxTokens?: number;
|
|
1159
|
+
/** HTTP timeout in ms. Default 120_000. */
|
|
1160
|
+
timeoutMs?: number;
|
|
1161
|
+
}
|
|
1162
|
+
/** Wrap a running Ollama daemon as a NeuronFn. */
|
|
1163
|
+
declare function ollamaNeuron(opts: OllamaNeuronOptions): NeuronFn;
|
|
1164
|
+
interface HuggingFaceNeuronOptions {
|
|
1165
|
+
/** Base URL of the inference server, e.g. "http://localhost:8080". */
|
|
1166
|
+
endpoint: string;
|
|
1167
|
+
/** Model name forwarded in the chat-completions body (required for vLLM). */
|
|
1168
|
+
model?: string;
|
|
1169
|
+
/** Force the `/v1/chat/completions` path even for plain prompts. */
|
|
1170
|
+
useChatApi?: boolean;
|
|
1171
|
+
temperature?: number;
|
|
1172
|
+
/** Maximum tokens to generate. Default 512. */
|
|
1173
|
+
maxNewTokens?: number;
|
|
1174
|
+
/** Bearer token - use your HF Hub token for hosted endpoints. */
|
|
1175
|
+
apiKey?: string;
|
|
1176
|
+
/** HTTP timeout in ms. Default 120_000. */
|
|
1177
|
+
timeoutMs?: number;
|
|
1178
|
+
}
|
|
1179
|
+
/** Wrap a HuggingFace TGI endpoint (or any OpenAI-compatible server) as a NeuronFn. */
|
|
1180
|
+
declare function huggingFaceNeuron(opts: HuggingFaceNeuronOptions): NeuronFn;
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* @cosmonapse/sdk - hosted-LLM provider Neurons (OpenAI, Anthropic)
|
|
1184
|
+
*
|
|
1185
|
+
* Ported from `cosmonapse.neuron` (`_OpenAINeuron` / `_AnthropicNeuron`). Wrap a
|
|
1186
|
+
* hosted LLM API behind the `NeuronFn` signature so it slots straight into an
|
|
1187
|
+
* Axon. Uses the global `fetch` (Node 18+), so there is no extra dependency -
|
|
1188
|
+
* the Python port needs `httpx`; in Node the runtime ships the client.
|
|
1189
|
+
*
|
|
1190
|
+
* Input convention (shared with the Python SDK):
|
|
1191
|
+
* - `prompt` (string) - single-turn input, or
|
|
1192
|
+
* - `messages` (OpenAI-style `[{ role, content }]`) - multi-turn / system.
|
|
1193
|
+
* (`text` / `query` / `content` are also accepted as a prompt alias.)
|
|
1194
|
+
*
|
|
1195
|
+
* Output: `{ response: "<text>", meta: <raw provider payload> }`.
|
|
1196
|
+
*/
|
|
1197
|
+
|
|
1198
|
+
interface OpenAINeuronOptions {
|
|
1199
|
+
/** Chat model name, e.g. "gpt-4o", "gpt-4o-mini". */
|
|
1200
|
+
model: string;
|
|
1201
|
+
/** API key. If omitted, falls back to the `OPENAI_API_KEY` env var. */
|
|
1202
|
+
apiKey?: string;
|
|
1203
|
+
/** API base URL. Default "https://api.openai.com/v1" (point at Azure/proxies). */
|
|
1204
|
+
endpoint?: string;
|
|
1205
|
+
temperature?: number;
|
|
1206
|
+
/** Maximum tokens to generate. */
|
|
1207
|
+
maxTokens?: number;
|
|
1208
|
+
/** Optional system prompt injected as the first `system` message. */
|
|
1209
|
+
system?: string;
|
|
1210
|
+
/** HTTP timeout in ms. Default 120_000. */
|
|
1211
|
+
timeoutMs?: number;
|
|
1212
|
+
}
|
|
1213
|
+
/** Wrap the OpenAI Chat Completions API (or a compatible proxy) as a NeuronFn. */
|
|
1214
|
+
declare function openaiNeuron(opts: OpenAINeuronOptions): NeuronFn;
|
|
1215
|
+
interface AnthropicNeuronOptions {
|
|
1216
|
+
/** Claude model name, e.g. "claude-opus-4-6", "claude-sonnet-4-6". */
|
|
1217
|
+
model: string;
|
|
1218
|
+
/** API key. If omitted, falls back to the `ANTHROPIC_API_KEY` env var. */
|
|
1219
|
+
apiKey?: string;
|
|
1220
|
+
/**
|
|
1221
|
+
* Optional system prompt. Sent as the top-level `system` field (the Anthropic
|
|
1222
|
+
* API does not accept a `system` role inside `messages`).
|
|
1223
|
+
*/
|
|
1224
|
+
system?: string;
|
|
1225
|
+
/** Maximum tokens to generate. Required by the API; defaults to 1024. */
|
|
1226
|
+
maxTokens?: number;
|
|
1227
|
+
temperature?: number;
|
|
1228
|
+
/** HTTP timeout in ms. Default 120_000. */
|
|
1229
|
+
timeoutMs?: number;
|
|
1230
|
+
}
|
|
1231
|
+
/** Wrap the Anthropic Messages API as a NeuronFn. */
|
|
1232
|
+
declare function anthropicNeuron(opts: AnthropicNeuronOptions): NeuronFn;
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* @cosmonapse/sdk - unified Neuron factory
|
|
1236
|
+
*
|
|
1237
|
+
* Mirrors the Python `Neuron(source=...)` ergonomics in TypeScript: pick a
|
|
1238
|
+
* source, get back a NeuronFn you hand straight to an Axon. A Neuron is any
|
|
1239
|
+
* unit of real-world behaviour - an MCP server or an LLM provider today, with
|
|
1240
|
+
* room for more sources later.
|
|
1241
|
+
*
|
|
1242
|
+
* ```ts
|
|
1243
|
+
* import { Axon, neuron } from "@cosmonapse/sdk";
|
|
1244
|
+
*
|
|
1245
|
+
* new Axon({ neuronId: "files", neuronFn: neuron("mcp", { server: "filesystem", args: ["/data"] }) });
|
|
1246
|
+
* new Axon({ neuronId: "chat", neuronFn: neuron("ollama", { model: "llama3" }) });
|
|
1247
|
+
* new Axon({ neuronId: "gpt", neuronFn: neuron("openai", { model: "gpt-4o-mini" }) });
|
|
1248
|
+
* ```
|
|
1249
|
+
*
|
|
1250
|
+
* NOTE - there is no "express" / "http" / "api" source. An HTTP API is not a
|
|
1251
|
+
* Neuron: instead of wrapping a web app behind an Axon, keep your framework
|
|
1252
|
+
* (Express, Fastify, …) on the outside as an HTTP boundary and dispatch TASK
|
|
1253
|
+
* Signals from inside its route handlers via an orchestrator Dendrite. See the
|
|
1254
|
+
* `real-world-neurons` example.
|
|
1255
|
+
*
|
|
1256
|
+
* WHICH TO USE - `neuron(source, opts)` is the recommended, source-agnostic
|
|
1257
|
+
* entry point and the one mirrored from the Python SDK; prefer it in app code.
|
|
1258
|
+
* The standalone `mcpNeuron(...)` / `ollamaNeuron(...)` exports are the
|
|
1259
|
+
* lower-level primitives `neuron()` delegates to: reach for them only when you
|
|
1260
|
+
* want a single source's exact option type without the union, or to tree-shake
|
|
1261
|
+
* away the others. They are not a second, parallel API - same behaviour,
|
|
1262
|
+
* narrower surface.
|
|
1263
|
+
*/
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* Options for the OpenAI-compatible hosted aliases (`groq` / `openrouter` /
|
|
1267
|
+
* `together` / `mistral`). These are pre-configured {@link huggingFaceNeuron}s:
|
|
1268
|
+
* `endpoint` defaults to the provider's base URL and `apiKey` falls back to the
|
|
1269
|
+
* provider's env var, but both (and any other HuggingFace option) can be
|
|
1270
|
+
* overridden.
|
|
1271
|
+
*/
|
|
1272
|
+
type OpenAICompatNeuronOptions = Omit<HuggingFaceNeuronOptions, "endpoint"> & {
|
|
1273
|
+
endpoint?: string;
|
|
1274
|
+
};
|
|
1275
|
+
type OpenAICompatAlias = "groq" | "openrouter" | "together" | "mistral";
|
|
1276
|
+
type NeuronSource = "mcp" | "ollama" | "huggingface" | "hf" | "openai" | "anthropic" | OpenAICompatAlias;
|
|
1277
|
+
declare function neuron(source: "mcp", opts: McpNeuronOptions): CloseableNeuronFn;
|
|
1278
|
+
declare function neuron(source: "ollama", opts: OllamaNeuronOptions): NeuronFn;
|
|
1279
|
+
declare function neuron(source: "huggingface" | "hf", opts: HuggingFaceNeuronOptions): NeuronFn;
|
|
1280
|
+
declare function neuron(source: "openai", opts: OpenAINeuronOptions): NeuronFn;
|
|
1281
|
+
declare function neuron(source: "anthropic", opts: AnthropicNeuronOptions): NeuronFn;
|
|
1282
|
+
declare function neuron(source: OpenAICompatAlias, opts?: OpenAICompatNeuronOptions): NeuronFn;
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* @cosmonapse/sdk - dendrite
|
|
1286
|
+
*
|
|
1287
|
+
* The synapse-side participant, ported from `cosmonapse.dendrite`.
|
|
1288
|
+
*
|
|
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.
|
|
1295
|
+
*
|
|
1296
|
+
* The Dendrite does NOT own the Synapse - the caller builds and closes it.
|
|
1297
|
+
*
|
|
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.
|
|
1301
|
+
*
|
|
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:`.
|
|
1306
|
+
*
|
|
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.
|
|
1312
|
+
*/
|
|
1313
|
+
|
|
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 {
|
|
1322
|
+
constructor(message: string);
|
|
1323
|
+
}
|
|
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;
|
|
1345
|
+
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
|
+
*/
|
|
1414
|
+
[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?: {
|
|
1420
|
+
capability?: string;
|
|
1421
|
+
}): Promise<NeuronRecord[]>;
|
|
1422
|
+
dispatchTask(args: {
|
|
1423
|
+
neuron: string;
|
|
1424
|
+
input: Json;
|
|
1425
|
+
traceId?: string;
|
|
1426
|
+
parentId?: string | null;
|
|
1427
|
+
contextRef?: string;
|
|
1428
|
+
capabilities?: string[];
|
|
1429
|
+
meta?: Json;
|
|
1430
|
+
}): Promise<Signal>;
|
|
1431
|
+
emitFinal(args: {
|
|
1432
|
+
traceId: string;
|
|
1433
|
+
parentId: string;
|
|
1434
|
+
result: Json;
|
|
1435
|
+
meta?: Json;
|
|
1436
|
+
}): Promise<Signal>;
|
|
1437
|
+
emitError(args: {
|
|
1438
|
+
traceId: string;
|
|
1439
|
+
parentId?: string | null;
|
|
1440
|
+
code: string;
|
|
1441
|
+
message: string;
|
|
1442
|
+
recoverable?: boolean;
|
|
1443
|
+
meta?: Json;
|
|
1444
|
+
}): Promise<Signal>;
|
|
1445
|
+
/** Emit a synapse-side Signal. Refuses Axon-owned types. */
|
|
1446
|
+
/**
|
|
1447
|
+
* 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 } }`.
|
|
1455
|
+
*/
|
|
1456
|
+
respondToPermission(request: Signal, opts: {
|
|
1457
|
+
granted: boolean;
|
|
1458
|
+
reason?: string;
|
|
1459
|
+
ttlMs?: number;
|
|
1460
|
+
extra?: Json;
|
|
1461
|
+
neuron?: string;
|
|
1462
|
+
meta?: Json;
|
|
1463
|
+
}): 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). */
|
|
1466
|
+
grantPermission(request: Signal, opts?: {
|
|
1467
|
+
reason?: string;
|
|
1468
|
+
ttlMs?: number;
|
|
1469
|
+
meta?: Json;
|
|
1470
|
+
}): Promise<Signal>;
|
|
1471
|
+
/** Reject a PERMISSION request. */
|
|
1472
|
+
denyPermission(request: Signal, opts?: {
|
|
1473
|
+
reason?: string;
|
|
1474
|
+
meta?: Json;
|
|
1475
|
+
}): Promise<Signal>;
|
|
1476
|
+
private decidePermission;
|
|
1477
|
+
/** Answer a *blocking* CLARIFICATION (the Neuron called ask(...) and is
|
|
1478
|
+
* awaiting). Distinct from the legacy return-marker flow. */
|
|
1479
|
+
answerClarification(request: Signal, answer: unknown, opts?: {
|
|
1480
|
+
meta?: Json;
|
|
1481
|
+
}): Promise<Signal>;
|
|
1482
|
+
emit(signal: Signal): Promise<void>;
|
|
1483
|
+
publish(signal: Signal): Promise<void>;
|
|
1484
|
+
subscribe(type: SignalType, handler: MessageHandler, opts?: {
|
|
1485
|
+
queueGroup?: string;
|
|
1486
|
+
}): Promise<Subscription>;
|
|
1487
|
+
private subject;
|
|
1488
|
+
private ensureInboundSub;
|
|
1489
|
+
private onTask;
|
|
1490
|
+
private emitRegister;
|
|
1491
|
+
private emitDeregister;
|
|
1492
|
+
private heartbeatTick;
|
|
1493
|
+
private mirrorToStore;
|
|
1494
|
+
private dispatchInbound;
|
|
1495
|
+
private updateRegistry;
|
|
1496
|
+
}
|
|
1497
|
+
/** Back-compat alias - a Cortex is just a Dendrite. */
|
|
1498
|
+
declare const Cortex: typeof Dendrite;
|
|
1499
|
+
type Cortex = Dendrite;
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* @cosmonapse/sdk - axon
|
|
1503
|
+
*
|
|
1504
|
+
* Agent-side tool that turns a Neuron's raw output into a protocol-valid
|
|
1505
|
+
* Signal. Ported from `cosmonapse.axon`.
|
|
1506
|
+
*
|
|
1507
|
+
* The Axon does NOT touch the Synapse. It owns:
|
|
1508
|
+
* - the Neuron's identity (neuronId, capabilities, version)
|
|
1509
|
+
* - the body of the tool (the NeuronFn)
|
|
1510
|
+
* - response validation:
|
|
1511
|
+
* normal return -> AGENT_OUTPUT
|
|
1512
|
+
* clarification marker -> CLARIFICATION
|
|
1513
|
+
* thrown error -> ERROR
|
|
1514
|
+
*
|
|
1515
|
+
* Like the Python Axon, this one carries LifecycleHooks (onConnect /
|
|
1516
|
+
* onRefresh / onSchedule). The hosting Dendrite drives them: it fires the
|
|
1517
|
+
* connect hooks and launches the schedule loops once the Axon is attached and
|
|
1518
|
+
* registered, and stops them when the Dendrite stops.
|
|
1519
|
+
*/
|
|
1520
|
+
|
|
1521
|
+
/**
|
|
1522
|
+
* Package-internal keys for the attach/detach handshake. These are deliberately
|
|
1523
|
+
* NOT re-exported from index.ts, so only same-package code (the Dendrite) can
|
|
1524
|
+
* name them and invoke the methods. This enforces "internal" at the language
|
|
1525
|
+
* level - which a `@internal` JSDoc tag on a `public` method does not. External
|
|
1526
|
+
* consumers have no way to reference these symbols, so `axon[ATTACH](...)` is
|
|
1527
|
+
* effectively private to the package.
|
|
1528
|
+
*/
|
|
1529
|
+
declare const ATTACH: unique symbol;
|
|
1530
|
+
declare const DETACH: unique symbol;
|
|
1531
|
+
/**
|
|
1532
|
+
* Recognises a Neuron's *native* output (an LLM's `{ response }`, an MCP
|
|
1533
|
+
* server's `{ is_error }`) and normalises it into the marker dict the Axon
|
|
1534
|
+
* understands. The recognition the Axon applies before wrapping. May throw to
|
|
1535
|
+
* yield an ERROR signal.
|
|
1536
|
+
*/
|
|
1537
|
+
type OutputParser = (raw: unknown) => unknown;
|
|
1538
|
+
/**
|
|
1539
|
+
* A detector registered via `axon.detects*`. Returns the intent's fields on a
|
|
1540
|
+
* match, or null/undefined to fall through. May be sync or async.
|
|
1541
|
+
*/
|
|
1542
|
+
type Recogniser = (raw: unknown) => unknown | Promise<unknown>;
|
|
1543
|
+
interface AxonOptions {
|
|
1544
|
+
neuronId: string;
|
|
1545
|
+
neuronFn: NeuronFn;
|
|
1546
|
+
capabilities?: string[];
|
|
1547
|
+
version?: string;
|
|
1548
|
+
contextFetcher?: ContextFetcher;
|
|
1549
|
+
/** Recognition the Axon applies to the Neuron's raw output before wrapping. */
|
|
1550
|
+
outputParser?: OutputParser;
|
|
1551
|
+
}
|
|
1552
|
+
/** Axon metadata accepted by the source-paired factories. */
|
|
1553
|
+
interface AxonExtra {
|
|
1554
|
+
capabilities?: string[];
|
|
1555
|
+
version?: string;
|
|
1556
|
+
contextFetcher?: ContextFetcher;
|
|
1557
|
+
/** Attach the source's recogniser (default true). */
|
|
1558
|
+
recognize?: boolean;
|
|
1559
|
+
}
|
|
1560
|
+
declare class Axon {
|
|
1561
|
+
readonly neuronId: string;
|
|
1562
|
+
readonly capabilities: string[];
|
|
1563
|
+
readonly version: string | undefined;
|
|
1564
|
+
private readonly fn;
|
|
1565
|
+
private readonly contextFetcher;
|
|
1566
|
+
private readonly outputParser;
|
|
1567
|
+
private dendrite;
|
|
1568
|
+
/**
|
|
1569
|
+
* Decorator-registered recognisers, one bucket per capability (the asking
|
|
1570
|
+
* side; named `detects*` to stay distinct from the Dendrite's `on*` inbound
|
|
1571
|
+
* handlers). Applied in precedence error -> clarification -> permission ->
|
|
1572
|
+
* output by {@link applyRecognisers}.
|
|
1573
|
+
*/
|
|
1574
|
+
private readonly recognisers;
|
|
1575
|
+
/** @internal - lifecycle hooks, driven by the hosting Dendrite. */
|
|
1576
|
+
readonly hooks: LifecycleHooks<Axon>;
|
|
1577
|
+
constructor(opts: AxonOptions);
|
|
1578
|
+
private static build;
|
|
1579
|
+
/** Axon paired with any registered Neuron source + its recogniser. */
|
|
1580
|
+
static fromSource(source: NeuronSource, neuronId: string, opts: any, extra?: AxonExtra): Axon;
|
|
1581
|
+
/** Axon paired with the OpenAI Chat Completions API. */
|
|
1582
|
+
static openai(neuronId: string, opts: OpenAINeuronOptions, extra?: AxonExtra): Axon;
|
|
1583
|
+
/** Axon paired with the Anthropic Messages API. */
|
|
1584
|
+
static anthropic(neuronId: string, opts: AnthropicNeuronOptions, extra?: AxonExtra): Axon;
|
|
1585
|
+
/** Axon paired with a local Ollama daemon. */
|
|
1586
|
+
static ollama(neuronId: string, opts: OllamaNeuronOptions, extra?: AxonExtra): Axon;
|
|
1587
|
+
/** Axon paired with a HuggingFace TGI / OpenAI-compatible endpoint. */
|
|
1588
|
+
static huggingface(neuronId: string, opts: HuggingFaceNeuronOptions, extra?: AxonExtra): Axon;
|
|
1589
|
+
/** Axon paired with a stdio MCP server. */
|
|
1590
|
+
static mcp(neuronId: string, opts: McpNeuronOptions, extra?: AxonExtra): Axon;
|
|
1591
|
+
/** Detector returning the AGENT_OUTPUT payload, or null to wrap verbatim. */
|
|
1592
|
+
detectsOutput(fn: Recogniser): Recogniser;
|
|
1593
|
+
/** Detector returning `{ question, context? }` to emit CLARIFICATION, or null. */
|
|
1594
|
+
detectsClarification(fn: Recogniser): Recogniser;
|
|
1595
|
+
/** Detector returning `{ action, scope?, reason?, context? }` for PERMISSION, or null. */
|
|
1596
|
+
detectsPermission(fn: Recogniser): Recogniser;
|
|
1597
|
+
/** Detector returning `{ code?, message?, recoverable? }` to emit ERROR, or null. */
|
|
1598
|
+
detectsError(fn: Recogniser): Recogniser;
|
|
1599
|
+
private applyRecognisers;
|
|
1600
|
+
/** Register a fire-once handler called after this Axon connects (attaches + registers). */
|
|
1601
|
+
onConnect(fn: ConnectHook<Axon>): ConnectHook<Axon>;
|
|
1602
|
+
/** Register a handler called whenever this Axon's observable state refreshes. */
|
|
1603
|
+
onRefresh(fn: RefreshHook<Axon>): RefreshHook<Axon>;
|
|
1604
|
+
/** Register a periodic handler that runs every `everyMs` while the host runs. */
|
|
1605
|
+
onSchedule(everyMs: number, fn: ScheduleHook<Axon>): ScheduleHook<Axon>;
|
|
1606
|
+
/**
|
|
1607
|
+
* Package-internal: invoked by Dendrite.attachAxon via the {@link ATTACH}
|
|
1608
|
+
* symbol. Not callable by external consumers (the symbol is not exported from
|
|
1609
|
+
* index.ts), so this replaces the previous `@internal`-comment-only contract
|
|
1610
|
+
* with real, enforced encapsulation.
|
|
1611
|
+
*/
|
|
1612
|
+
[ATTACH](dendrite: Dendrite): void;
|
|
1613
|
+
/** Package-internal: invoked via the {@link DETACH} symbol. */
|
|
1614
|
+
[DETACH](): void;
|
|
1615
|
+
/** Run the Neuron and return AGENT_OUTPUT / CLARIFICATION / ERROR. */
|
|
1616
|
+
handleTask(task: Signal): Promise<Signal>;
|
|
1617
|
+
}
|
|
1618
|
+
/** Recogniser for LLM sources returning `{ response: text, meta }`. */
|
|
1619
|
+
declare function parseLlmIntents(raw: unknown): unknown;
|
|
1620
|
+
/** Recogniser for the `mcp` source: `is_error` -> ERROR, else pass through. */
|
|
1621
|
+
declare function parseMcpIntents(raw: unknown): unknown;
|
|
1622
|
+
|
|
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
|
+
/**
|
|
1827
|
+
* @cosmonapse/sdk - SQLite Engram
|
|
1828
|
+
*
|
|
1829
|
+
* Ported from `cosmonapse.engram.sqlite`. A single file on disk (or
|
|
1830
|
+
* `:memory:`), backed by `better-sqlite3` (lazy-imported, optional dependency:
|
|
1831
|
+
* `npm i better-sqlite3`). The Python port uses stdlib sqlite3 on a threadpool;
|
|
1832
|
+
* better-sqlite3 is synchronous, so the async `Engram` methods simply wrap
|
|
1833
|
+
* synchronous calls.
|
|
1834
|
+
*
|
|
1835
|
+
* Recall surface matches InMemoryEngram:
|
|
1836
|
+
* query = { text?, tag?, merge_key?, top_k = 50 }
|
|
1837
|
+
* filters = { tags?: string[], since?: iso, until?: iso }
|
|
1838
|
+
*/
|
|
1839
|
+
|
|
1840
|
+
interface SqliteEngramInit {
|
|
1841
|
+
path?: string;
|
|
1842
|
+
engramId?: string;
|
|
1843
|
+
engramKind?: string;
|
|
1844
|
+
capabilities?: string[];
|
|
1845
|
+
version?: string | null;
|
|
1846
|
+
}
|
|
1847
|
+
/** SQLite-backed Engram (optional `better-sqlite3`). */
|
|
1848
|
+
declare class SqliteEngram extends Engram {
|
|
1849
|
+
engramId: string;
|
|
1850
|
+
engramKind: string;
|
|
1851
|
+
capabilities: string[];
|
|
1852
|
+
private readonly path;
|
|
1853
|
+
private db;
|
|
1854
|
+
constructor(init?: SqliteEngramInit);
|
|
1855
|
+
connect(): Promise<void>;
|
|
1856
|
+
close(): Promise<void>;
|
|
1857
|
+
private require;
|
|
1858
|
+
recall(query: Json, opts?: RecallOptions): Promise<Hit[]>;
|
|
1859
|
+
imprint(op: ImprintOp, entry: Json, opts?: ImprintOptions): Promise<ImprintReceipt>;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* @cosmonapse/sdk - Postgres Engram
|
|
1864
|
+
*
|
|
1865
|
+
* Ported from `cosmonapse.engram.postgres`. JSONB content + GIN-indexed tags,
|
|
1866
|
+
* backed by `pg` (node-postgres; lazy-imported, optional dependency:
|
|
1867
|
+
* `npm i pg`). The Python port uses asyncpg; `pg` is the de-facto Node driver.
|
|
1868
|
+
*
|
|
1869
|
+
* Recall surface matches SqliteEngram for portability:
|
|
1870
|
+
* query = { text?, tag?, merge_key?, top_k = 50 }
|
|
1871
|
+
* filters = { tags?: string[], since?: iso, until?: iso }
|
|
1872
|
+
*/
|
|
1873
|
+
|
|
1874
|
+
interface PostgresEngramInit {
|
|
1875
|
+
dsn: string;
|
|
1876
|
+
engramId?: string;
|
|
1877
|
+
engramKind?: string;
|
|
1878
|
+
capabilities?: string[];
|
|
1879
|
+
version?: string | null;
|
|
1880
|
+
minSize?: number;
|
|
1881
|
+
maxSize?: number;
|
|
1882
|
+
}
|
|
1883
|
+
/** Postgres-backed Engram (optional `pg`). */
|
|
1884
|
+
declare class PostgresEngram extends Engram {
|
|
1885
|
+
engramId: string;
|
|
1886
|
+
engramKind: string;
|
|
1887
|
+
capabilities: string[];
|
|
1888
|
+
private readonly dsn;
|
|
1889
|
+
private readonly minSize;
|
|
1890
|
+
private readonly maxSize;
|
|
1891
|
+
private pool;
|
|
1892
|
+
constructor(init: PostgresEngramInit);
|
|
1893
|
+
connect(): Promise<void>;
|
|
1894
|
+
close(): Promise<void>;
|
|
1895
|
+
private require;
|
|
1896
|
+
recall(query: Json, opts?: RecallOptions): Promise<Hit[]>;
|
|
1897
|
+
imprint(op: ImprintOp, entry: Json, opts?: ImprintOptions): Promise<ImprintReceipt>;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* @cosmonapse/sdk
|
|
1902
|
+
*
|
|
1903
|
+
* TypeScript surface of the Cosmonapse envelope protocol. This entry point
|
|
1904
|
+
* re-exports the envelope types/codec and the typed signal builders. It is the
|
|
1905
|
+
* 1:1 counterpart to the Python `cosmonapse` package's envelope module.
|
|
1906
|
+
*
|
|
1907
|
+
* Ported and functional: envelope, builders, MemorySynapse, NatsSynapse,
|
|
1908
|
+
* DevSynapse, KafkaSynapse, the RegistryStore (in-memory + sqlite + postgres) +
|
|
1909
|
+
* Dendrite registry mirror, LifecycleHooks, connectSynapse, Neuron (MCP /
|
|
1910
|
+
* Ollama / HuggingFace), Axon and Dendrite. Any remaining intentional
|
|
1911
|
+
* differences are documented in PORTING_STATUS.md.
|
|
1912
|
+
*/
|
|
1913
|
+
declare const VERSION: string;
|
|
1914
|
+
|
|
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 };
|