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