@m4trix/core 0.8.1 → 0.10.0

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.
@@ -1,4 +1,4 @@
1
- import { Brand, Schema, Effect, PubSub, Queue, Cause } from 'effect';
1
+ import { Brand, Schema, Tracer, Layer, Effect, Exit, PubSub, Queue, Cause } from 'effect';
2
2
  export { Schema as S } from 'effect';
3
3
  import { randomUUID } from 'crypto';
4
4
 
@@ -72,8 +72,66 @@ var Channel = {
72
72
  };
73
73
  }
74
74
  };
75
+
76
+ // src/helper/types/noop.ts
77
+ var asyncNoop = async () => {
78
+ };
79
+
80
+ // src/matrix/agent-network/stores/inmemory-network-store.ts
81
+ var createInMemoryNetworkStore = () => {
82
+ const store = /* @__PURE__ */ new Map();
83
+ return {
84
+ storeEvent: (contextId, runId, event) => {
85
+ let byRun = store.get(contextId);
86
+ if (!byRun) {
87
+ byRun = /* @__PURE__ */ new Map();
88
+ store.set(contextId, byRun);
89
+ }
90
+ let events = byRun.get(runId);
91
+ if (!events) {
92
+ events = [];
93
+ byRun.set(runId, events);
94
+ }
95
+ events.push(event);
96
+ },
97
+ getEvents: (contextId, runId) => {
98
+ const events = store.get(contextId)?.get(runId);
99
+ return events ? [...events] : [];
100
+ },
101
+ getContextEvents: (contextId) => {
102
+ const byRun = store.get(contextId);
103
+ const result = /* @__PURE__ */ new Map();
104
+ if (byRun) {
105
+ for (const [runId, events] of byRun) {
106
+ result.set(runId, [...events]);
107
+ }
108
+ }
109
+ return result;
110
+ },
111
+ getFullStore: () => {
112
+ const result = /* @__PURE__ */ new Map();
113
+ for (const [contextId, byRun] of store) {
114
+ const contextMap = /* @__PURE__ */ new Map();
115
+ for (const [runId, events] of byRun) {
116
+ contextMap.set(runId, [...events]);
117
+ }
118
+ result.set(contextId, contextMap);
119
+ }
120
+ return result;
121
+ },
122
+ persist: () => asyncNoop(),
123
+ load: () => asyncNoop()
124
+ };
125
+ };
126
+
127
+ // src/matrix/agent-network/event-plane.ts
75
128
  var DEFAULT_CAPACITY = 16;
76
- var createEventPlane = (network, capacity = DEFAULT_CAPACITY) => Effect.gen(function* () {
129
+ var createEventPlane = (options) => Effect.gen(function* () {
130
+ const {
131
+ network,
132
+ capacity = DEFAULT_CAPACITY,
133
+ store = createInMemoryNetworkStore()
134
+ } = options;
77
135
  const channels = network.getChannels();
78
136
  const pubsubs = /* @__PURE__ */ new Map();
79
137
  for (const channel of channels.values()) {
@@ -86,12 +144,59 @@ var createEventPlane = (network, capacity = DEFAULT_CAPACITY) => Effect.gen(func
86
144
  throw new Error(`Channel not found: ${channel}`);
87
145
  return p;
88
146
  };
89
- const publish = (channel, envelope) => PubSub.publish(getPubsub(channel), envelope);
90
- const publishToChannels = (targetChannels, envelope) => Effect.all(
91
- targetChannels.map((c) => publish(c.name, envelope)),
92
- { concurrency: "unbounded" }
93
- ).pipe(Effect.map((results) => results.every(Boolean)));
147
+ const recordEvent = (envelope) => {
148
+ const { contextId, runId } = envelope.meta;
149
+ store.storeEvent(contextId, runId, envelope);
150
+ };
151
+ const publishToPubSub = (channel, envelope) => PubSub.publish(getPubsub(channel), envelope);
152
+ const publish = (channel, envelope) => Effect.sync(() => recordEvent(envelope)).pipe(
153
+ Effect.flatMap(() => publishToPubSub(channel, envelope)),
154
+ Effect.withSpan("event.publish", {
155
+ attributes: {
156
+ "event.name": envelope.name,
157
+ "event.payload": payloadForSpan(envelope.payload),
158
+ channel,
159
+ runId: envelope.meta.runId,
160
+ contextId: envelope.meta.contextId
161
+ }
162
+ })
163
+ );
164
+ const publishToChannels = (targetChannels, envelope) => Effect.sync(() => recordEvent(envelope)).pipe(
165
+ Effect.flatMap(
166
+ () => Effect.all(
167
+ targetChannels.map((c) => publishToPubSub(c.name, envelope)),
168
+ { concurrency: "unbounded" }
169
+ )
170
+ ),
171
+ Effect.map((results) => results.every(Boolean)),
172
+ Effect.withSpan("event.publish", {
173
+ attributes: {
174
+ "event.name": envelope.name,
175
+ "event.payload": payloadForSpan(envelope.payload),
176
+ runId: envelope.meta.runId,
177
+ contextId: envelope.meta.contextId
178
+ }
179
+ })
180
+ );
94
181
  const subscribe = (channel) => PubSub.subscribe(getPubsub(channel));
182
+ const getRunEvents = (runId, contextId) => {
183
+ return store.getEvents(contextId, runId).slice();
184
+ };
185
+ const getContextEvents = (contextId) => {
186
+ const byRun = store.getContextEvents(contextId);
187
+ const map = /* @__PURE__ */ new Map();
188
+ const all = [];
189
+ for (const [runId, events] of byRun) {
190
+ const readonlyEvents = events.slice();
191
+ map.set(runId, readonlyEvents);
192
+ all.push(...readonlyEvents);
193
+ }
194
+ return {
195
+ all,
196
+ byRun: (runId) => map.get(runId) ?? [],
197
+ map
198
+ };
199
+ };
95
200
  const shutdown = Effect.all([...pubsubs.values()].map(PubSub.shutdown), {
96
201
  concurrency: "unbounded"
97
202
  }).pipe(Effect.asVoid);
@@ -99,42 +204,77 @@ var createEventPlane = (network, capacity = DEFAULT_CAPACITY) => Effect.gen(func
99
204
  publish,
100
205
  publishToChannels,
101
206
  subscribe,
207
+ getRunEvents,
208
+ getContextEvents,
102
209
  shutdown
103
210
  };
104
211
  });
105
- var runSubscriber = (agent, publishesTo, dequeue, plane, emitQueue) => Effect.gen(function* () {
212
+ function payloadForSpan(payload, maxLen = 500) {
213
+ try {
214
+ const s = JSON.stringify(payload);
215
+ return s.length > maxLen ? `${s.slice(0, maxLen)}...` : s;
216
+ } catch {
217
+ return String(payload);
218
+ }
219
+ }
220
+ var runSubscriber = (agent, publishesTo, dequeue, plane, emitQueue, channelName) => Effect.gen(function* () {
106
221
  const listensTo = agent.getListensTo?.() ?? [];
222
+ const agentId = agent.getId();
107
223
  const processOne = () => Effect.gen(function* () {
108
224
  const envelope = yield* Queue.take(dequeue);
109
225
  if (listensTo.length > 0 && !listensTo.includes(envelope.name)) {
110
226
  return;
111
227
  }
112
- yield* Effect.tryPromise({
113
- try: () => agent.invoke({
114
- triggerEvent: envelope,
115
- emit: (userEvent) => {
116
- const fullEnvelope = {
117
- name: userEvent.name,
118
- meta: envelope.meta,
119
- payload: userEvent.payload
120
- };
121
- if (emitQueue) {
122
- Effect.runPromise(
123
- Queue.offer(emitQueue, {
124
- channels: publishesTo,
125
- envelope: fullEnvelope
126
- })
127
- ).catch(() => {
128
- });
129
- } else {
130
- Effect.runFork(
131
- plane.publishToChannels(publishesTo, fullEnvelope)
132
- );
133
- }
228
+ const runEvents = plane.getRunEvents(
229
+ envelope.meta.runId,
230
+ envelope.meta.contextId
231
+ );
232
+ const contextEvents = plane.getContextEvents(envelope.meta.contextId);
233
+ yield* Effect.withSpan("agent.listen", {
234
+ attributes: {
235
+ agentId,
236
+ "event.name": envelope.name,
237
+ "event.payload": payloadForSpan(envelope.payload),
238
+ ...channelName !== void 0 && { channel: channelName }
239
+ }
240
+ })(
241
+ Effect.withSpan("agent.invoke", {
242
+ attributes: {
243
+ agentId,
244
+ "event.name": envelope.name,
245
+ "event.payload": payloadForSpan(envelope.payload)
134
246
  }
135
- }),
136
- catch: (e) => e
137
- });
247
+ })(
248
+ Effect.tryPromise({
249
+ try: () => agent.invoke({
250
+ triggerEvent: envelope,
251
+ emit: (userEvent) => {
252
+ const fullEnvelope = {
253
+ name: userEvent.name,
254
+ meta: envelope.meta,
255
+ payload: userEvent.payload
256
+ };
257
+ if (emitQueue) {
258
+ Effect.runPromise(
259
+ Queue.offer(emitQueue, {
260
+ channels: publishesTo,
261
+ envelope: fullEnvelope
262
+ })
263
+ ).catch(() => {
264
+ });
265
+ } else {
266
+ Effect.runFork(
267
+ plane.publishToChannels(publishesTo, fullEnvelope)
268
+ );
269
+ }
270
+ },
271
+ runEvents,
272
+ contextEvents
273
+ }),
274
+ catch: (e) => e
275
+ })
276
+ )
277
+ );
138
278
  }).pipe(
139
279
  Effect.catchAllCause(
140
280
  (cause) => Cause.isInterrupted(cause) ? Effect.void : Effect.sync(() => {
@@ -156,7 +296,8 @@ var run = (network, plane, options) => Effect.gen(function* () {
156
296
  reg.publishesTo,
157
297
  dequeue,
158
298
  plane,
159
- emitQueue
299
+ emitQueue,
300
+ channel.name
160
301
  );
161
302
  }
162
303
  }
@@ -225,7 +366,16 @@ function streamFromDequeue(take, signal, eventFilter) {
225
366
  };
226
367
  }
227
368
  function expose(network, options) {
228
- const { auth, select, plane: providedPlane, onRequest, startEventName = "request" } = options;
369
+ const {
370
+ auth,
371
+ select,
372
+ plane: providedPlane,
373
+ onRequest,
374
+ triggerEvents,
375
+ tracingLayer
376
+ } = options;
377
+ const triggerEventDef = triggerEvents?.[0];
378
+ const triggerEventName = triggerEventDef?.name ?? "request";
229
379
  const channels = resolveChannels(network, select);
230
380
  const eventFilter = select?.events;
231
381
  const mainChannel = network.getMainChannel();
@@ -236,7 +386,7 @@ function expose(network, options) {
236
386
  const payload = await extractPayload(req);
237
387
  const signal = req.request?.signal;
238
388
  const program = Effect.gen(function* () {
239
- const plane = providedPlane ?? (yield* createEventPlane(network));
389
+ const plane = providedPlane ?? (yield* createEventPlane({ network, store: network.getStore() }));
240
390
  if (!providedPlane) {
241
391
  const emitQueue = yield* Queue.unbounded();
242
392
  yield* Effect.fork(
@@ -252,25 +402,46 @@ function expose(network, options) {
252
402
  yield* Effect.sleep("10 millis");
253
403
  }
254
404
  const targetChannel = mainChannel?.name ?? channels[0];
255
- const emitStartEvent = (p) => {
256
- const pld = p ?? payload;
405
+ let runId = req.runId ?? crypto.randomUUID();
406
+ let contextId = req.contextId ?? crypto.randomUUID();
407
+ const setRunId = (id) => {
408
+ runId = id;
409
+ };
410
+ const setContextId = (id) => {
411
+ contextId = id;
412
+ };
413
+ const emitStartEvent = (opts) => {
414
+ const meta = {
415
+ runId: opts.runId,
416
+ contextId: opts.contextId
417
+ };
257
418
  const envelope = {
258
- name: startEventName,
259
- meta: { runId: crypto.randomUUID() },
260
- payload: pld
419
+ name: opts.event.name,
420
+ meta,
421
+ payload: opts.event.payload
261
422
  };
262
- Effect.runPromise(plane.publish(targetChannel, envelope)).catch(() => {
263
- });
423
+ Effect.runPromise(plane.publish(targetChannel, envelope)).catch(
424
+ () => {
425
+ }
426
+ );
264
427
  };
265
428
  const dequeue = yield* plane.subscribe(channels[0]);
266
429
  if (onRequest) {
267
430
  yield* Effect.tryPromise(
268
- () => Promise.resolve(onRequest({ emitStartEvent, req, payload }))
431
+ () => Promise.resolve(
432
+ onRequest({
433
+ setRunId,
434
+ setContextId,
435
+ emitStartEvent,
436
+ req,
437
+ payload
438
+ })
439
+ )
269
440
  );
270
441
  } else if (!providedPlane) {
271
442
  const envelope = {
272
- name: startEventName,
273
- meta: { runId: crypto.randomUUID() },
443
+ name: triggerEventName,
444
+ meta: { runId, contextId },
274
445
  payload
275
446
  };
276
447
  yield* plane.publish(targetChannel, envelope);
@@ -283,7 +454,8 @@ function expose(network, options) {
283
454
  }
284
455
  return stream;
285
456
  });
286
- return Effect.runPromise(program.pipe(Effect.scoped));
457
+ const runnable = tracingLayer ? program.pipe(Effect.provide(tracingLayer), Effect.scoped) : program.pipe(Effect.scoped);
458
+ return Effect.runPromise(runnable);
287
459
  };
288
460
  return {
289
461
  protocol: "sse",
@@ -315,6 +487,7 @@ var AgentNetwork = class _AgentNetwork {
315
487
  this.channels = /* @__PURE__ */ new Map();
316
488
  this.agentRegistrations = /* @__PURE__ */ new Map();
317
489
  this.spawnerRegistrations = [];
490
+ this._store = createInMemoryNetworkStore();
318
491
  }
319
492
  /* ─── Public Static Factory ─── */
320
493
  static setup(callback) {
@@ -402,6 +575,10 @@ var AgentNetwork = class _AgentNetwork {
402
575
  getSpawnerRegistrations() {
403
576
  return this.spawnerRegistrations;
404
577
  }
578
+ /** Store defined at network setup time. Shared across all event planes created for this network. */
579
+ getStore() {
580
+ return this._store;
581
+ }
405
582
  /**
406
583
  * Expose the network as a streamable API (e.g. SSE). Returns an ExposedAPI
407
584
  * that adapters (NextEndpoint, ExpressEndpoint) consume to produce streamed
@@ -409,7 +586,7 @@ var AgentNetwork = class _AgentNetwork {
409
586
  *
410
587
  * @example
411
588
  * const api = network.expose({ protocol: "sse", auth, select });
412
- * export const GET = NextEndpoint.from(api).handler();
589
+ * export const GET = NextEndpoint.from(api, { requestToContextId, requestToRunId }).handler();
413
590
  */
414
591
  expose(options) {
415
592
  return expose(this, options);
@@ -427,7 +604,11 @@ var AgentNetwork = class _AgentNetwork {
427
604
  }
428
605
  runScoped(network, capacity) {
429
606
  return Effect.gen(function* () {
430
- const plane = yield* createEventPlane(network, capacity);
607
+ const plane = yield* createEventPlane({
608
+ network,
609
+ capacity,
610
+ store: network.getStore()
611
+ });
431
612
  yield* Effect.fork(run(network, plane));
432
613
  return plane;
433
614
  });
@@ -435,7 +616,7 @@ var AgentNetwork = class _AgentNetwork {
435
616
  };
436
617
  var EventMetaSchema = Schema.Struct({
437
618
  runId: Schema.String,
438
- contextId: Schema.optional(Schema.String),
619
+ contextId: Schema.String,
439
620
  correlationId: Schema.optional(Schema.String),
440
621
  causationId: Schema.optional(Schema.String),
441
622
  ts: Schema.optional(Schema.Number)
@@ -458,9 +639,7 @@ var AgentNetworkEvent = {
458
639
  const makeBound = (meta, payload2) => Effect.runSync(
459
640
  decodeEnvelope({ name, meta, payload: payload2 })
460
641
  );
461
- const makeEffect = (payload2) => decodePayload(payload2).pipe(
462
- Effect.map((p) => ({ name, payload: p }))
463
- );
642
+ const makeEffect = (payload2) => decodePayload(payload2).pipe(Effect.map((p) => ({ name, payload: p })));
464
643
  const makeBoundEffect = (meta, payload2) => decodeEnvelope({ name, meta, payload: payload2 });
465
644
  const is = Schema.is(envelopeSchema);
466
645
  return {
@@ -493,13 +672,19 @@ var Agent = class {
493
672
  return __privateGet(this, _listensTo);
494
673
  }
495
674
  async invoke(options) {
496
- const { triggerEvent, emit } = options ?? {};
675
+ const { triggerEvent, emit, runEvents, contextEvents } = options ?? {};
497
676
  const emitFn = emit ?? ((_event) => {
498
677
  });
499
678
  await __privateGet(this, _logic).call(this, {
500
679
  params: __privateGet(this, _params),
501
680
  triggerEvent: triggerEvent ?? void 0,
502
- emit: emitFn
681
+ emit: emitFn,
682
+ runEvents: runEvents ?? [],
683
+ contextEvents: contextEvents ?? {
684
+ all: [],
685
+ byRun: () => [],
686
+ map: /* @__PURE__ */ new Map()
687
+ }
503
688
  });
504
689
  }
505
690
  getId() {
@@ -612,14 +797,19 @@ function toSSEStream(source, signal) {
612
797
 
613
798
  // src/matrix/io/adapters/next-endpoint.ts
614
799
  var NextEndpoint = {
615
- from(api) {
800
+ from(api, options) {
616
801
  if (api.protocol !== "sse") {
617
802
  throw new Error(`NextEndpoint: unsupported protocol "${api.protocol}"`);
618
803
  }
804
+ const { requestToContextId, requestToRunId } = options;
619
805
  return {
620
806
  handler() {
621
807
  return async (request) => {
622
- const req = { request };
808
+ const req = {
809
+ request,
810
+ contextId: requestToContextId(request),
811
+ runId: requestToRunId(request)
812
+ };
623
813
  try {
624
814
  const encoder = new TextEncoder();
625
815
  const { readable, writable } = new TransformStream();
@@ -664,12 +854,13 @@ var NextEndpoint = {
664
854
 
665
855
  // src/matrix/io/adapters/express-endpoint.ts
666
856
  var ExpressEndpoint = {
667
- from(api) {
857
+ from(api, options) {
668
858
  if (api.protocol !== "sse") {
669
859
  throw new Error(
670
860
  `ExpressEndpoint: unsupported protocol "${api.protocol}"`
671
861
  );
672
862
  }
863
+ const { requestToContextId, requestToRunId } = options;
673
864
  return {
674
865
  handler() {
675
866
  return async (req, res) => {
@@ -678,7 +869,9 @@ var ExpressEndpoint = {
678
869
  const exposeReq = {
679
870
  request: { signal: controller.signal },
680
871
  req,
681
- res
872
+ res,
873
+ contextId: requestToContextId(req),
874
+ runId: requestToRunId(req)
682
875
  };
683
876
  try {
684
877
  const encoder = new TextEncoder();
@@ -710,7 +903,79 @@ var ExpressEndpoint = {
710
903
  };
711
904
  }
712
905
  };
906
+ var randomHexString = (length) => {
907
+ const chars = "abcdef0123456789";
908
+ let result = "";
909
+ for (let i = 0; i < length; i++) {
910
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
911
+ }
912
+ return result;
913
+ };
914
+ var ConsoleSpan = class {
915
+ constructor(name, parent, context, links, startTime, kind, depth) {
916
+ this.name = name;
917
+ this.parent = parent;
918
+ this.context = context;
919
+ this.startTime = startTime;
920
+ this.kind = kind;
921
+ this.depth = depth;
922
+ this._tag = "Span";
923
+ this.sampled = true;
924
+ this.attributes = /* @__PURE__ */ new Map();
925
+ this.links = [];
926
+ this.traceId = parent._tag === "Some" ? parent.value.traceId : randomHexString(32);
927
+ this.spanId = randomHexString(16);
928
+ this.links = Array.from(links);
929
+ this.status = { _tag: "Started", startTime };
930
+ }
931
+ end(endTime, exit) {
932
+ if (this.status._tag === "Ended")
933
+ return;
934
+ const startTime = this.status.startTime;
935
+ const durationNs = endTime - startTime;
936
+ const durationMs = Number(durationNs) / 1e6;
937
+ const indent = " ".repeat(this.depth);
938
+ const attrs = Object.fromEntries(this.attributes);
939
+ const status = Exit.isSuccess(exit) ? "ok" : "error";
940
+ console.log(
941
+ `${indent}[trace] ${this.name} ${durationMs.toFixed(2)}ms (${status})`,
942
+ Object.keys(attrs).length > 0 ? attrs : ""
943
+ );
944
+ this.status = { _tag: "Ended", startTime, endTime, exit };
945
+ }
946
+ attribute(key, value) {
947
+ this.attributes.set(key, value);
948
+ }
949
+ event(_name, _startTime, _attributes) {
950
+ }
951
+ addLinks(links) {
952
+ this.links.push(...links);
953
+ }
954
+ };
955
+ function getDepth(parent) {
956
+ if (parent._tag === "None")
957
+ return 0;
958
+ const p = parent.value;
959
+ if (p._tag === "ExternalSpan")
960
+ return 0;
961
+ return 1 + getDepth(p.parent);
962
+ }
963
+ var consoleTracer = Tracer.make({
964
+ span: (name, parent, context, links, startTime, kind) => new ConsoleSpan(
965
+ name,
966
+ parent,
967
+ context,
968
+ links,
969
+ startTime,
970
+ kind,
971
+ getDepth(parent)
972
+ ),
973
+ context: (f) => f()
974
+ });
975
+ var consoleTracerLayer = Layer.setTracer(
976
+ consoleTracer
977
+ );
713
978
 
714
- export { Agent, AgentFactory, AgentNetwork, AgentNetworkEvent, Channel, ChannelName, ConfiguredChannel, EventMetaSchema, ExposeAuthError, ExpressEndpoint, NextEndpoint, Sink, formatSSE, isHttpStreamSink, toSSEStream };
979
+ export { Agent, AgentFactory, AgentNetwork, AgentNetworkEvent, Channel, ChannelName, ConfiguredChannel, EventMetaSchema, ExposeAuthError, ExpressEndpoint, NextEndpoint, Sink, consoleTracer, consoleTracerLayer, formatSSE, isHttpStreamSink, toSSEStream };
715
980
  //# sourceMappingURL=out.js.map
716
981
  //# sourceMappingURL=index.js.map