@typeonce/effect-machine 0.11.0 → 0.13.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.
Files changed (83) hide show
  1. package/README.md +89 -13
  2. package/dist/Machine.d.ts +396 -127
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +21 -19
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +1 -0
  7. package/dist/internal/machine/atom.d.ts.map +1 -1
  8. package/dist/internal/machine/atom.js +23 -0
  9. package/dist/internal/machine/atom.js.map +1 -1
  10. package/dist/internal/machine/cluster.js +2 -2
  11. package/dist/internal/machine/cluster.js.map +1 -1
  12. package/dist/internal/machine/commandRuntime.d.ts.map +1 -1
  13. package/dist/internal/machine/commandRuntime.js +2 -5
  14. package/dist/internal/machine/commandRuntime.js.map +1 -1
  15. package/dist/internal/machine/configuration.d.ts +12 -7
  16. package/dist/internal/machine/configuration.d.ts.map +1 -1
  17. package/dist/internal/machine/configuration.js +9 -5
  18. package/dist/internal/machine/configuration.js.map +1 -1
  19. package/dist/internal/machine/executionPlan.d.ts +4 -3
  20. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  21. package/dist/internal/machine/executionPlan.js +68 -43
  22. package/dist/internal/machine/executionPlan.js.map +1 -1
  23. package/dist/internal/machine/initialization.d.ts +5 -0
  24. package/dist/internal/machine/initialization.d.ts.map +1 -0
  25. package/dist/internal/machine/initialization.js +61 -0
  26. package/dist/internal/machine/initialization.js.map +1 -0
  27. package/dist/internal/machine/inspectionRuntime.d.ts +25 -0
  28. package/dist/internal/machine/inspectionRuntime.d.ts.map +1 -0
  29. package/dist/internal/machine/inspectionRuntime.js +79 -0
  30. package/dist/internal/machine/inspectionRuntime.js.map +1 -0
  31. package/dist/internal/machine/invocation.d.ts +2 -1
  32. package/dist/internal/machine/invocation.d.ts.map +1 -1
  33. package/dist/internal/machine/invocation.js +18 -11
  34. package/dist/internal/machine/invocation.js.map +1 -1
  35. package/dist/internal/machine/machine.d.ts +1 -2
  36. package/dist/internal/machine/machine.d.ts.map +1 -1
  37. package/dist/internal/machine/machine.js +21 -6
  38. package/dist/internal/machine/machine.js.map +1 -1
  39. package/dist/internal/machine/planner.d.ts +17 -2
  40. package/dist/internal/machine/planner.d.ts.map +1 -1
  41. package/dist/internal/machine/planner.js +137 -64
  42. package/dist/internal/machine/planner.js.map +1 -1
  43. package/dist/internal/machine/process.d.ts.map +1 -1
  44. package/dist/internal/machine/process.js +7 -5
  45. package/dist/internal/machine/process.js.map +1 -1
  46. package/dist/internal/machine/runtime.d.ts +29 -5
  47. package/dist/internal/machine/runtime.d.ts.map +1 -1
  48. package/dist/internal/machine/runtime.js +440 -64
  49. package/dist/internal/machine/runtime.js.map +1 -1
  50. package/dist/internal/machine/symbols.d.ts +2 -0
  51. package/dist/internal/machine/symbols.d.ts.map +1 -1
  52. package/dist/internal/machine/symbols.js +2 -0
  53. package/dist/internal/machine/symbols.js.map +1 -1
  54. package/dist/internal/machine/topology.d.ts +13 -0
  55. package/dist/internal/machine/topology.d.ts.map +1 -1
  56. package/dist/internal/machine/topology.js +9 -0
  57. package/dist/internal/machine/topology.js.map +1 -1
  58. package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
  59. package/dist/internal/testing/machine/finiteModel.js +3 -3
  60. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  61. package/dist/unstable/reactivity/AtomMachine.d.ts +9 -0
  62. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  63. package/dist/unstable/reactivity/AtomMachine.js +9 -0
  64. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  65. package/docs/agent-guide.md +130 -20
  66. package/package.json +5 -5
  67. package/src/Machine.ts +548 -217
  68. package/src/internal/machine/atom.ts +26 -0
  69. package/src/internal/machine/cluster.ts +2 -2
  70. package/src/internal/machine/commandRuntime.ts +2 -7
  71. package/src/internal/machine/configuration.ts +38 -13
  72. package/src/internal/machine/executionPlan.ts +85 -52
  73. package/src/internal/machine/initialization.ts +73 -0
  74. package/src/internal/machine/inspectionRuntime.ts +102 -0
  75. package/src/internal/machine/invocation.ts +38 -13
  76. package/src/internal/machine/machine.ts +31 -6
  77. package/src/internal/machine/planner.ts +166 -66
  78. package/src/internal/machine/process.ts +7 -5
  79. package/src/internal/machine/runtime.ts +663 -91
  80. package/src/internal/machine/symbols.ts +3 -0
  81. package/src/internal/machine/topology.ts +27 -0
  82. package/src/internal/testing/machine/finiteModel.ts +8 -7
  83. package/src/unstable/reactivity/AtomMachine.ts +12 -0
@@ -17,6 +17,7 @@ import * as Scope from "effect/Scope";
17
17
  import * as Stream from "effect/Stream";
18
18
  import * as SynchronizedRef from "effect/SynchronizedRef";
19
19
  import { ChildAlreadyExistsError, StoppedError } from "./errors.js";
20
+ import * as InspectionRuntime from "./inspectionRuntime.js";
20
21
  /** @internal */
21
22
  export const activeSnapshotObserver = Symbol.for("effect/Machine/activeSnapshotObserver");
22
23
  /** @internal */
@@ -30,17 +31,22 @@ export const isAcknowledgedMessage = (message) => typeof message === "object" &&
30
31
  export const messageEvent = (message) => isAcknowledgedMessage(message) ? message.event : message;
31
32
  const succeedAcknowledgedMessage = (message, delivery) => {
32
33
  if (message !== undefined && isAcknowledgedMessage(message)) {
33
- Deferred.doneUnsafe(message.deferred, Effect.succeed(delivery));
34
+ if (message.deferred !== undefined) {
35
+ Deferred.doneUnsafe(message.deferred, Effect.succeed(delivery));
36
+ }
37
+ message.inspection?.complete(delivery);
34
38
  }
35
39
  };
36
40
  const failAcknowledgedMessage = (message, cause) => {
37
41
  if (message !== undefined && isAcknowledgedMessage(message)) {
38
- Deferred.doneUnsafe(message.deferred, Effect.failCause(cause));
42
+ if (message.deferred !== undefined)
43
+ Deferred.doneUnsafe(message.deferred, Effect.failCause(cause));
39
44
  }
40
45
  };
41
46
  const stopAcknowledgedMessage = (message) => {
42
47
  if (message !== undefined && isAcknowledgedMessage(message)) {
43
- Deferred.doneUnsafe(message.deferred, Effect.fail(new StoppedError()));
48
+ if (message.deferred !== undefined)
49
+ Deferred.doneUnsafe(message.deferred, Effect.fail(new StoppedError()));
44
50
  }
45
51
  };
46
52
  const offerChildObservation = (observer, value) => {
@@ -112,7 +118,10 @@ const registerChild = (registry, key, token, ref, descriptor) => {
112
118
  publishRegistryChange(registry);
113
119
  return true;
114
120
  };
115
- const isProcessAddress = (value) => typeof value === "object" && value !== null && "send" in value && typeof value.send === "function";
121
+ const isMachineTarget = (value) => typeof value === "object" && value !== null && "send" in value && typeof value.send === "function";
122
+ const sendMachineTarget = (target, event, source, causedBy) => "inspectionSubject" in target && "sendInspected" in target && typeof target.sendInspected === "function"
123
+ ? target.sendInspected(event, source, causedBy)
124
+ : target.send(event);
116
125
  const executionIsChildless = (execution) => execution?._tag === "Childless" || execution?.childless === true;
117
126
  const offerCompactMailbox = (mailbox, event) => {
118
127
  const items = mailbox.items ?? [];
@@ -205,6 +214,63 @@ const makeProcessRuntime = Effect.sync(() => {
205
214
  nextSessionId: Effect.sync(() => `machine:${sessionIdCounter++}`)
206
215
  };
207
216
  });
217
+ const inspectionSubject = (logic, id, sessionId) => ({
218
+ id,
219
+ sessionId,
220
+ kind: logic.inspection?.kind ?? "Logic"
221
+ });
222
+ const messageCausation = (message, initializing) => initializing
223
+ ? { _tag: "Initialization" }
224
+ : message !== undefined && isAcknowledgedMessage(message) && message.inspection !== undefined
225
+ ? { _tag: "Macrostep", macrostepId: message.inspection.macrostepId }
226
+ : undefined;
227
+ const makeInspectedMessage = (inspection, subject, event, source, causedBy, deferred) => {
228
+ const deliveryId = inspection.nextDeliveryId();
229
+ const macrostepId = inspection.nextMacrostepId();
230
+ const inspected = {
231
+ deliveryId,
232
+ macrostepId,
233
+ source,
234
+ event,
235
+ causedBy,
236
+ complete: (delivery) => {
237
+ const microsteps = InspectionRuntime.microsteps(delivery.plan);
238
+ inspection.publishUnsafe({
239
+ _tag: "EventProcessed",
240
+ subject,
241
+ macrostepId,
242
+ deliveryId,
243
+ source,
244
+ event,
245
+ before: { status: "active", state: delivery.before },
246
+ after: { status: "active", state: delivery.after },
247
+ handled: microsteps.some((microstep) => microstep.transitions.length > 0),
248
+ configurationChanged: microsteps.some((microstep) => microstep.changed),
249
+ microsteps
250
+ });
251
+ }
252
+ };
253
+ return {
254
+ [AcknowledgedMessageTypeId]: true,
255
+ event,
256
+ ...(deferred === undefined ? undefined : { deferred }),
257
+ inspection: inspected
258
+ };
259
+ };
260
+ const publishInspectedSent = (inspection, subject, message) => {
261
+ if (!isAcknowledgedMessage(message) || message.inspection === undefined)
262
+ return;
263
+ const delivery = message.inspection;
264
+ inspection.publishUnsafe({
265
+ _tag: "EventSent",
266
+ subject,
267
+ deliveryId: delivery.deliveryId,
268
+ source: delivery.source,
269
+ target: InspectionRuntime.endpoint(subject),
270
+ event: delivery.event,
271
+ causedBy: delivery.causedBy
272
+ });
273
+ };
208
274
  class OwnedChildRuntimeImpl {
209
275
  registry;
210
276
  self;
@@ -252,13 +318,14 @@ class OwnedChildRuntimeImpl {
252
318
  });
253
319
  const parent = {
254
320
  ...this.self,
255
- send: (event) => options.sendParent(isCurrent, event)
321
+ send: (event) => options.sendParent(isCurrent, event),
322
+ sendInspected: (event, source, causedBy) => isCurrent() ? sendMachineTarget(this.self, event, source, causedBy) : Effect.void
256
323
  };
257
324
  const startOptions = {
258
325
  detached: true,
259
326
  id: options.id,
260
327
  sendParent: (event) => options.sendParent(isCurrent, event),
261
- onOutcome: (outcome) => options.onOutcome(isCurrent, outcome),
328
+ onOutcome: (outcome) => options.onOutcome(isCurrent, outcome, startedChild?.sessionId),
262
329
  ...(options.onSnapshot === undefined
263
330
  ? undefined
264
331
  : { onSnapshot: (snapshot) => options.onSnapshot(isCurrent, snapshot) }),
@@ -269,7 +336,19 @@ class OwnedChildRuntimeImpl {
269
336
  onStopSync: () => unregisterChild(this.registry, options.id, token),
270
337
  skipStoppedOutcome: true,
271
338
  parent,
272
- runtime: this.runtime
339
+ runtime: this.runtime,
340
+ origin: { _tag: "Invoke", ownerPath: options.path, invokeId: options.duplicateId },
341
+ ...(options.activityKind === undefined || this.runtime.inspection === undefined ||
342
+ this.self.inspectionSubject === undefined
343
+ ? undefined
344
+ : {
345
+ activity: {
346
+ id: options.duplicateId,
347
+ owner: this.self.inspectionSubject,
348
+ ownerPath: options.path,
349
+ kind: options.activityKind
350
+ }
351
+ })
273
352
  };
274
353
  const execution = logic.execution;
275
354
  const synchronous = this.services !== undefined && options.onSnapshot === undefined &&
@@ -320,6 +399,8 @@ class OwnedChildRuntimeImpl {
320
399
  }
321
400
  const noChildChanges = Stream.succeed(Option.none()).pipe(Stream.concat(Stream.never));
322
401
  const noParentSend = (_event) => Effect.void;
402
+ const noInspectInitial = (_paths, _microsteps) => { };
403
+ const noCausation = () => undefined;
323
404
  const EmissionsClosed = Symbol("effect/Machine/EmissionsClosed");
324
405
  const makeEmissionRuntime = () => {
325
406
  let emissions;
@@ -450,7 +531,7 @@ const makeChildRuntimeSync = (self, runtime, services) => {
450
531
  })), Effect.as(takeChildObservations(observer)));
451
532
  }))));
452
533
  };
453
- const sendTo = (child, event) => {
534
+ const sendTo = (child, event, source, causedBy) => {
454
535
  const id = typeof child === "string" ? child : child.id;
455
536
  return Effect.suspend(() => {
456
537
  if (registry.closed) {
@@ -458,7 +539,7 @@ const makeChildRuntimeSync = (self, runtime, services) => {
458
539
  }
459
540
  const entry = registry.children.get(id);
460
541
  return entry !== undefined && matchesChild(entry, child)
461
- ? entry.ref.send(event)
542
+ ? sendMachineTarget(entry.ref, event, source, causedBy)
462
543
  : Effect.void;
463
544
  });
464
545
  };
@@ -502,7 +583,8 @@ const makeChildRuntimeSync = (self, runtime, services) => {
502
583
  }).pipe(Effect.andThen(register(key, token, child, spawnOptions?.descriptor)), Effect.flatMap((registered) => registered ? Effect.void : requestChildStop)),
503
584
  onStop: unregister(key, token),
504
585
  parent: self,
505
- runtime
586
+ runtime,
587
+ origin: { _tag: "Spawn", address: spawnOptions?.id }
506
588
  }).pipe(Effect.onExit((exit) => Exit.isFailure(exit)
507
589
  ? unregister(key, token).pipe(Effect.andThen(startedChild === undefined ? Effect.void : startedChild.stop))
508
590
  : Effect.void), Scope.provide(registry.scope));
@@ -526,6 +608,13 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
526
608
  const { detached, id: requestedId, onOutcome, onReady, onReadySync, onSnapshot, onStop, onStopSync, parent, runtime, sendParent: overrideSendParent } = options;
527
609
  const sessionId = options.sessionId ?? (yield* runtime.nextSessionId);
528
610
  const id = requestedId ?? sessionId;
611
+ const inspector = runtime.inspection;
612
+ const subject = inspector === undefined ? undefined : inspectionSubject(logic, id, sessionId);
613
+ const activity = inspector === undefined || options.activity === undefined
614
+ ? undefined
615
+ : { ...options.activity, sessionId };
616
+ let initialEntryPaths;
617
+ let initialMicrosteps;
529
618
  const queue = yield* Queue.unbounded();
530
619
  const emissions = options.emissions ?? makeEmissionRuntime();
531
620
  const termination = yield* Deferred.make();
@@ -534,6 +623,15 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
534
623
  let initializing = true;
535
624
  let inFlightMessage;
536
625
  const requestStop = Deferred.succeed(termination, { _tag: "Stopped" }).pipe(Effect.asVoid);
626
+ const offerDirect = (message) => Queue.offer(queue, message).pipe(Effect.flatMap((accepted) => accepted ? Effect.void : Effect.fail(new StoppedError())));
627
+ const offerInspected = inspector === undefined ? undefined : (event, source, causedBy, deferred) => Effect.suspend(() => {
628
+ const message = inspector.isActive()
629
+ ? makeInspectedMessage(inspector, subject, event, source, causedBy, deferred)
630
+ : deferred === undefined
631
+ ? event
632
+ : { [AcknowledgedMessageTypeId]: true, event, deferred };
633
+ return offerDirect(message).pipe(Effect.tap(() => Effect.sync(() => publishInspectedSent(inspector, subject, message))));
634
+ });
537
635
  const self = {
538
636
  id,
539
637
  sessionId,
@@ -545,17 +643,21 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
545
643
  stop: Effect.suspend(() => initializing
546
644
  ? requestStop
547
645
  : requestStop.pipe(Effect.andThen(Effect.never))),
548
- send: (event) => Queue.offer(queue, event).pipe(Effect.flatMap((accepted) => accepted ? Effect.void : Effect.fail(new StoppedError())))
646
+ send: inspector === undefined
647
+ ? (event) => offerDirect(event)
648
+ : (event) => offerInspected(event, undefined, undefined),
649
+ ...(inspector === undefined
650
+ ? undefined
651
+ : { inspectionSubject: subject, sendInspected: offerInspected })
549
652
  };
550
653
  const sendAcknowledged = logic.execution?._tag !== "Compiled"
551
654
  ? undefined
552
- : (event) => Effect.uninterruptibleMask((restore) => Deferred.make().pipe(Effect.flatMap((deferred) => Queue.offer(queue, {
553
- [AcknowledgedMessageTypeId]: true,
554
- event,
555
- deferred
556
- }).pipe(Effect.flatMap((accepted) => accepted
557
- ? restore(Deferred.await(deferred))
558
- : Effect.fail(new StoppedError())))), Effect.map((delivery) => delivery)));
655
+ : (event) => Effect.uninterruptibleMask((restore) => Deferred.make().pipe(Effect.flatMap((deferred) => {
656
+ const offered = inspector === undefined
657
+ ? offerDirect({ [AcknowledgedMessageTypeId]: true, event, deferred })
658
+ : offerInspected(event, undefined, undefined, deferred);
659
+ return offered.pipe(Effect.andThen(restore(Deferred.await(deferred))));
660
+ }), Effect.map((delivery) => delivery)));
559
661
  let { changes: childChanges, close: closeChildren, get: getChild, owned: ownedChildren, sendTo, spawn, stop: stopChild } = childlessRuntime;
560
662
  if (!executionIsChildless(logic.execution)) {
561
663
  ;
@@ -569,24 +671,68 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
569
671
  stop: stopChild
570
672
  } = yield* makeChildRuntime(self, runtime));
571
673
  }
572
- const cleanupStartupFailure = (exit) => Exit.isFailure(exit)
573
- ? closeChildren(exit).pipe(Effect.andThen(emissions.close()))
574
- : Effect.void;
674
+ const cleanupStartupFailure = (exit) => {
675
+ if (Exit.isSuccess(exit))
676
+ return Effect.void;
677
+ if (inspector !== undefined) {
678
+ inspector.publishUnsafe(activity === undefined
679
+ ? { _tag: "StartFailed", subject: subject, cause: exit.cause }
680
+ : { _tag: "ActivityStopped", subject: activity.owner, activity, exit });
681
+ }
682
+ return closeChildren(exit).pipe(Effect.andThen(emissions.close()), Effect.andThen(options.inspectionRoot === true && inspector !== undefined ? inspector.close : Effect.void));
683
+ };
575
684
  const cleanup = onStopSync === undefined ? onStop ?? Effect.void : Effect.sync(onStopSync);
576
- const sendParent = overrideSendParent ?? (parent === undefined ? noParentSend : parent.send);
685
+ const currentCausation = inspector === undefined
686
+ ? noCausation
687
+ : () => messageCausation(inFlightMessage, initializing);
688
+ const sendParent = overrideSendParent ?? (parent === undefined
689
+ ? noParentSend
690
+ : inspector === undefined
691
+ ? parent.send
692
+ : (event) => sendMachineTarget(parent, event, subject, currentCausation()));
693
+ const emit = inspector === undefined
694
+ ? emissions.emit
695
+ : (event) => emissions.emit(event).pipe(Effect.tap(() => Effect.sync(() => inspector.publishUnsafe({
696
+ _tag: "Emitted",
697
+ subject: subject,
698
+ emission: event,
699
+ causedBy: currentCausation()
700
+ }))));
701
+ const sendToTarget = inspector === undefined
702
+ ? ((target, event) => isMachineTarget(target) ? target.send(event) : sendTo(target, event))
703
+ : ((target, event) => isMachineTarget(target)
704
+ ? sendMachineTarget(target, event, subject, currentCausation())
705
+ : sendTo(target, event, subject, currentCausation()));
577
706
  const scope = {
578
707
  self,
579
708
  parent,
580
709
  spawn,
581
710
  sendParent,
582
- emit: emissions.emit,
583
- sendTo: ((target, event) => isProcessAddress(target) ? target.send(event) : sendTo(target, event)),
711
+ emit,
712
+ sendTo: sendToTarget,
584
713
  stopChild,
585
714
  failCause: (cause) => Deferred.succeed(termination, {
586
715
  _tag: "Failure",
587
716
  cause: cause
588
- }).pipe(Effect.asVoid)
717
+ }).pipe(Effect.asVoid),
718
+ inspectInitial: inspector === undefined
719
+ ? noInspectInitial
720
+ : (paths, microsteps = []) => {
721
+ initialEntryPaths = paths;
722
+ initialMicrosteps = microsteps;
723
+ }
589
724
  };
725
+ if (inspector !== undefined) {
726
+ inspector.publishUnsafe(activity === undefined
727
+ ? {
728
+ _tag: "Created",
729
+ subject: subject,
730
+ parent: parent?.inspectionSubject,
731
+ origin: options.origin ?? { _tag: "Root" },
732
+ definition: logic.inspection?.definition
733
+ }
734
+ : { _tag: "ActivityStarted", subject: activity.owner, activity });
735
+ }
590
736
  const initial = yield* logic.initial(scope).pipe(Effect.onExit(cleanupStartupFailure), Effect.ensuring(Effect.sync(() => {
591
737
  initializing = false;
592
738
  })));
@@ -599,6 +745,15 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
599
745
  state: initial
600
746
  }
601
747
  });
748
+ if (activity === undefined) {
749
+ inspector?.publishUnsafe({
750
+ _tag: "Initialized",
751
+ subject: subject,
752
+ snapshot: { status: "active", state: initial },
753
+ initialEntryPaths: initialEntryPaths ?? [],
754
+ microsteps: InspectionRuntime.microsteps({ microsteps: initialMicrosteps ?? [] })
755
+ });
756
+ }
602
757
  const publishSnapshot = onSnapshot === undefined
603
758
  ? (snapshot) => snapshot.changes === undefined
604
759
  ? Effect.succeed(snapshot)
@@ -658,16 +813,46 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
658
813
  terminalizing: true,
659
814
  changes: current.changes
660
815
  })).pipe(Effect.flatMap(publishSnapshot), Effect.flatMap(completeIfTerminal), Effect.asVoid);
661
- const setActiveState = (state) => updateSnapshot((snapshot) => Effect.succeed(snapshot.status === "active"
816
+ const setActiveStateDirect = (state) => updateSnapshot((snapshot) => Effect.succeed(snapshot.status === "active"
662
817
  ? {
663
818
  status: "active",
664
819
  state
665
820
  }
666
821
  : undefined)).pipe(Effect.asVoid);
822
+ const setActiveState = inspector === undefined ?
823
+ setActiveStateDirect :
824
+ (state) => SynchronizedRef.get(current).pipe(Effect.flatMap((before) => updateSnapshot((snapshot) => Effect.succeed(snapshot.status === "active"
825
+ ? {
826
+ status: "active",
827
+ state
828
+ }
829
+ : undefined)).pipe(Effect.tap((after) => Effect.sync(() => {
830
+ if (logic.inspection?.kind === "Machine" || after === undefined)
831
+ return;
832
+ inspector.publishUnsafe({
833
+ _tag: "StateChanged",
834
+ subject: subject,
835
+ before: before.snapshot.state,
836
+ after: state,
837
+ causedByDeliveryId: inFlightMessage !== undefined && isAcknowledgedMessage(inFlightMessage)
838
+ ? inFlightMessage.inspection?.deliveryId
839
+ : undefined
840
+ });
841
+ })))), Effect.asVoid);
667
842
  const terminalizeWith = (snapshot, exit, completeDone) => {
668
843
  const notifyOutcome = onOutcome === undefined || (snapshot.status === "stopped" && options.skipStoppedOutcome === true)
669
844
  ? Effect.void
670
845
  : Effect.suspend(() => onOutcome(classifyOutcome(snapshot))).pipe(Effect.exit, Effect.asVoid);
846
+ const closeEmissionsAndInspect = inspector === undefined
847
+ ? emissions.close()
848
+ : emissions.close().pipe(Effect.andThen(Effect.sync(() => inspector.publishUnsafe(activity === undefined
849
+ ? { _tag: "Terminated", subject: subject, snapshot }
850
+ : {
851
+ _tag: "ActivityStopped",
852
+ subject: activity.owner,
853
+ activity,
854
+ exit: snapshot.status === "stopped" ? Exit.interrupt() : exit
855
+ }))));
671
856
  return Effect.uninterruptible(Effect.sync(() => {
672
857
  while (true) {
673
858
  const pending = Queue.takeUnsafe(queue);
@@ -675,7 +860,7 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
675
860
  break;
676
861
  stopAcknowledgedMessage(pending.value);
677
862
  }
678
- }).pipe(Effect.andThen(Queue.shutdown(queue)), Effect.andThen(closeChildren(exit)), Effect.andThen(setAndPublishSnapshot(snapshot)), Effect.andThen(emissions.close()), Effect.andThen(Effect.sync(() => {
863
+ }).pipe(Effect.andThen(Queue.shutdown(queue)), Effect.andThen(closeChildren(exit)), Effect.andThen(setAndPublishSnapshot(snapshot)), Effect.andThen(closeEmissionsAndInspect), Effect.andThen(Effect.sync(() => {
679
864
  if (Exit.isFailure(exit)) {
680
865
  failAcknowledgedMessage(inFlightMessage, exit.cause);
681
866
  }
@@ -683,7 +868,7 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
683
868
  stopAcknowledgedMessage(inFlightMessage);
684
869
  }
685
870
  inFlightMessage = undefined;
686
- })), Effect.andThen(notifyOutcome), Effect.andThen(cleanup), Effect.andThen(completeDone)));
871
+ })), Effect.andThen(notifyOutcome), Effect.andThen(cleanup), Effect.andThen(options.inspectionRoot === true && inspector !== undefined ? inspector.close : Effect.void), Effect.andThen(completeDone)));
687
872
  };
688
873
  const reserveStoppedSnapshot = reserveTerminalSnapshot((snapshot) => ({
689
874
  status: "stopped",
@@ -726,20 +911,52 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
726
911
  inFlightMessage = undefined;
727
912
  }
728
913
  };
914
+ const receive = inspector === undefined || logic.execution?._tag === "Compiled"
915
+ ? Queue.take(queue).pipe(Effect.map(messageEvent))
916
+ : Queue.take(queue).pipe(Effect.tap((message) => Effect.sync(() => {
917
+ inFlightMessage = message;
918
+ })), Effect.map(messageEvent));
919
+ const poll = inspector === undefined || logic.execution?._tag === "Compiled"
920
+ ? Queue.poll(queue).pipe(Effect.map(Option.map(messageEvent)))
921
+ : Queue.poll(queue).pipe(Effect.tap((message) => Effect.sync(() => {
922
+ if (Option.isSome(message))
923
+ inFlightMessage = message.value;
924
+ })), Effect.map(Option.map(messageEvent)));
925
+ const updateStateDirect = (f) => updateSnapshot((snapshot) => snapshot.status === "active"
926
+ ? f(snapshot.state).pipe(Effect.map((state) => ({
927
+ status: "active",
928
+ state
929
+ })))
930
+ : Effect.succeed(undefined)).pipe(Effect.asVoid);
931
+ const updateState = inspector === undefined
932
+ ? updateStateDirect
933
+ : (f) => SynchronizedRef.get(current).pipe(Effect.flatMap((before) => updateSnapshot((snapshot) => snapshot.status === "active"
934
+ ? f(snapshot.state).pipe(Effect.map((state) => ({
935
+ status: "active",
936
+ state
937
+ })))
938
+ : Effect.succeed(undefined)).pipe(Effect.tap((after) => Effect.sync(() => {
939
+ if (logic.inspection?.kind === "Machine" || after?.status !== "active")
940
+ return;
941
+ inspector.publishUnsafe({
942
+ _tag: "StateChanged",
943
+ subject: subject,
944
+ before: before.snapshot.state,
945
+ after: after.state,
946
+ causedByDeliveryId: inFlightMessage !== undefined && isAcknowledgedMessage(inFlightMessage)
947
+ ? inFlightMessage.inspection?.deliveryId
948
+ : undefined
949
+ });
950
+ })))), Effect.asVoid);
729
951
  const context = {
730
952
  ...scope,
731
953
  ...(logic.execution?._tag === "Compiled" && !logic.execution.childless ? { ownedChildren } : undefined),
732
954
  ...acknowledgedContext,
733
- receive: Queue.take(queue).pipe(Effect.map(messageEvent)),
734
- poll: Queue.poll(queue).pipe(Effect.map(Option.map(messageEvent))),
955
+ receive,
956
+ poll,
735
957
  state: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot.state)),
736
958
  setState: setActiveState,
737
- updateState: (f) => updateSnapshot((snapshot) => snapshot.status === "active"
738
- ? f(snapshot.state).pipe(Effect.map((state) => ({
739
- status: "active",
740
- state
741
- })))
742
- : Effect.succeed(undefined)).pipe(Effect.asVoid)
959
+ updateState
743
960
  };
744
961
  const getOrCreateChanges = SynchronizedRef.modifyEffect(current, (current) => {
745
962
  if (current.snapshot.status !== "active") {
@@ -765,6 +982,9 @@ const startGenericInternal = Effect.fnUntraced(function* (logic, options) {
765
982
  const ref = {
766
983
  id,
767
984
  sessionId,
985
+ ...(inspector === undefined
986
+ ? undefined
987
+ : { inspectionSubject: subject, sendInspected: offerInspected }),
768
988
  state: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot.state)),
769
989
  snapshot: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot)),
770
990
  changes: changesStream,
@@ -852,7 +1072,11 @@ class CompiledProcess {
852
1072
  services;
853
1073
  id;
854
1074
  sessionId;
1075
+ inspectionSubject;
855
1076
  send;
1077
+ sendInspected(event, source, causedBy) {
1078
+ return this.offerEvent(event, source, causedBy);
1079
+ }
856
1080
  [acknowledgedSend](event) {
857
1081
  return this.sendAcknowledgedEffect(event);
858
1082
  }
@@ -884,41 +1108,125 @@ class CompiledProcess {
884
1108
  inFlightMessage;
885
1109
  externalEmissions;
886
1110
  emissionsPubSub;
1111
+ activity;
1112
+ initialEntryPaths;
1113
+ initialMicrosteps;
1114
+ initializing;
887
1115
  constructor(logic, options, services, sessionId) {
888
1116
  this.logic = logic;
889
1117
  this.options = options;
890
1118
  this.services = services;
891
1119
  this.sessionId = sessionId;
892
1120
  this.id = options.id ?? sessionId;
1121
+ const inspector = options.runtime.inspection;
1122
+ if (inspector !== undefined) {
1123
+ this.inspectionSubject = inspectionSubject(logic, this.id, sessionId);
1124
+ this.initializing = true;
1125
+ if (options.activity !== undefined)
1126
+ this.activity = { ...options.activity, sessionId };
1127
+ }
893
1128
  this.externalEmissions = options.emissions;
894
- this.send = (event) => this.offerMessage(event);
1129
+ this.send = inspector === undefined
1130
+ ? (event) => this.offerMessage(event)
1131
+ : (event) => this.offerEvent(event, undefined, undefined);
895
1132
  this.address = {
896
1133
  id: this.id,
897
1134
  sessionId,
898
1135
  stop: Effect.suspend(() => this.stopFromProcess()),
899
- send: this.send
1136
+ send: this.send,
1137
+ ...(inspector === undefined
1138
+ ? undefined
1139
+ : {
1140
+ inspectionSubject: this.inspectionSubject,
1141
+ sendInspected: (event, source, causedBy) => this.offerEvent(event, source, causedBy)
1142
+ })
900
1143
  };
901
1144
  }
1145
+ get inspector() {
1146
+ return this.options.runtime.inspection;
1147
+ }
1148
+ causation() {
1149
+ return messageCausation(this.inFlightMessage, this.initializing === true);
1150
+ }
1151
+ publishCreated() {
1152
+ const inspector = this.inspector;
1153
+ const subject = this.inspectionSubject;
1154
+ if (inspector === undefined || subject === undefined)
1155
+ return;
1156
+ inspector.publishUnsafe(this.activity === undefined
1157
+ ? {
1158
+ _tag: "Created",
1159
+ subject,
1160
+ parent: this.options.parent?.inspectionSubject,
1161
+ origin: this.options.origin ?? { _tag: "Root" },
1162
+ definition: this.logic.inspection?.definition
1163
+ }
1164
+ : { _tag: "ActivityStarted", subject: this.activity.owner, activity: this.activity });
1165
+ }
1166
+ publishInitialized(state) {
1167
+ this.initializing = false;
1168
+ if (this.activity !== undefined)
1169
+ return;
1170
+ const inspector = this.inspector;
1171
+ const subject = this.inspectionSubject;
1172
+ if (inspector === undefined || subject === undefined)
1173
+ return;
1174
+ inspector.publishUnsafe({
1175
+ _tag: "Initialized",
1176
+ subject,
1177
+ snapshot: { status: "active", state },
1178
+ initialEntryPaths: this.initialEntryPaths ?? [],
1179
+ microsteps: InspectionRuntime.microsteps({ microsteps: this.initialMicrosteps ?? [] })
1180
+ });
1181
+ }
1182
+ publishStartFailed(cause) {
1183
+ this.initializing = false;
1184
+ const inspector = this.inspector;
1185
+ const subject = this.inspectionSubject;
1186
+ if (inspector === undefined || subject === undefined)
1187
+ return;
1188
+ inspector.publishUnsafe(this.activity === undefined
1189
+ ? { _tag: "StartFailed", subject, cause }
1190
+ : {
1191
+ _tag: "ActivityStopped",
1192
+ subject: this.activity.owner,
1193
+ activity: this.activity,
1194
+ exit: Exit.failCause(cause)
1195
+ });
1196
+ }
902
1197
  get execution() {
903
1198
  return this.logic.execution;
904
1199
  }
905
1200
  initializeCompiledSync() {
1201
+ this.publishCreated();
906
1202
  if (!this.execution.childless) {
907
1203
  this.childRuntime = makeChildRuntimeSync(this.address, this.options.runtime, this.services);
908
1204
  }
909
1205
  const parent = this.options.parent;
910
- const sendParent = this.options.sendParent ?? (parent === undefined ? noParentSend : parent.send);
1206
+ const sendParent = this.options.sendParent ?? (parent === undefined
1207
+ ? noParentSend
1208
+ : this.inspector === undefined
1209
+ ? parent.send
1210
+ : (event) => sendMachineTarget(parent, event, this.inspectionSubject, this.causation()));
911
1211
  this.processScope = {
912
1212
  self: this.address,
913
1213
  parent,
914
1214
  spawn: this.childRuntime.spawn,
915
1215
  sendParent,
916
1216
  emit: (event) => this.emitEvent(event),
917
- sendTo: ((target, event) => isProcessAddress(target)
918
- ? target.send(event)
919
- : this.childRuntime.sendTo(target, event)),
1217
+ sendTo: ((target, event) => isMachineTarget(target)
1218
+ ? this.inspector === undefined
1219
+ ? target.send(event)
1220
+ : sendMachineTarget(target, event, this.inspectionSubject, this.causation())
1221
+ : this.childRuntime.sendTo(target, event, this.inspectionSubject, this.causation())),
920
1222
  stopChild: this.childRuntime.stop,
921
- failCause: (cause) => this.failCause(cause)
1223
+ failCause: (cause) => this.failCause(cause),
1224
+ inspectInitial: this.inspector === undefined
1225
+ ? noInspectInitial
1226
+ : (paths, microsteps = []) => {
1227
+ this.initialEntryPaths = paths;
1228
+ this.initialMicrosteps = microsteps;
1229
+ }
922
1230
  };
923
1231
  const compiledInitial = this.execution.initialSync;
924
1232
  let initialized;
@@ -927,6 +1235,7 @@ class CompiledProcess {
927
1235
  }
928
1236
  catch (error) {
929
1237
  this.runState = "Idle";
1238
+ this.publishStartFailed(Cause.fail(error));
930
1239
  return Effect.fail(error);
931
1240
  }
932
1241
  this.runState = "Idle";
@@ -936,6 +1245,7 @@ class CompiledProcess {
936
1245
  changes: undefined,
937
1246
  snapshot: { status: "active", state: initialized.state }
938
1247
  };
1248
+ this.publishInitialized(initialized.state);
939
1249
  this.compiledContext = new CompiledProcessContextImpl(this.processScope, this.childRuntime.owned, this);
940
1250
  if ("executionState" in initialized) {
941
1251
  this.compiledContext.executionState = initialized.executionState;
@@ -957,29 +1267,46 @@ class CompiledProcess {
957
1267
  initialize() {
958
1268
  const self = this;
959
1269
  return Effect.gen(function* () {
1270
+ self.publishCreated();
960
1271
  if (!self.execution.childless) {
961
1272
  self.childRuntime = yield* makeChildRuntime(self.address, self.options.runtime, self.services);
962
1273
  }
963
1274
  const parent = self.options.parent;
964
- const sendParent = self.options.sendParent ?? (parent === undefined ? noParentSend : parent.send);
1275
+ const sendParent = self.options.sendParent ?? (parent === undefined
1276
+ ? noParentSend
1277
+ : self.inspector === undefined
1278
+ ? parent.send
1279
+ : (event) => sendMachineTarget(parent, event, self.inspectionSubject, self.causation()));
965
1280
  self.processScope = {
966
1281
  self: self.address,
967
1282
  parent,
968
1283
  spawn: self.childRuntime.spawn,
969
1284
  sendParent,
970
1285
  emit: (event) => self.emitEvent(event),
971
- sendTo: ((target, event) => isProcessAddress(target)
972
- ? target.send(event)
973
- : self.childRuntime.sendTo(target, event)),
1286
+ sendTo: ((target, event) => isMachineTarget(target)
1287
+ ? self.inspector === undefined
1288
+ ? target.send(event)
1289
+ : sendMachineTarget(target, event, self.inspectionSubject, self.causation())
1290
+ : self.childRuntime.sendTo(target, event, self.inspectionSubject, self.causation())),
974
1291
  stopChild: self.childRuntime.stop,
975
- failCause: (cause) => self.failCause(cause)
1292
+ failCause: (cause) => self.failCause(cause),
1293
+ inspectInitial: self.inspector === undefined
1294
+ ? noInspectInitial
1295
+ : (paths, microsteps = []) => {
1296
+ self.initialEntryPaths = paths;
1297
+ self.initialMicrosteps = microsteps;
1298
+ }
976
1299
  };
977
1300
  const cleanupStartupFailure = (exit) => Exit.isFailure(exit) ? self.childRuntime.close(exit) : Effect.void;
978
1301
  const compiledInitial = self.execution.initial;
979
1302
  const initializeEffect = compiledInitial === undefined
980
1303
  ? self.logic.initial(self.processScope).pipe(Effect.map((state) => ({ state, done: undefined, output: undefined })))
981
1304
  : compiledInitial(self.processScope);
982
- const initialized = yield* initializeEffect.pipe(Effect.onExit(cleanupStartupFailure), Effect.ensuring(Effect.sync(() => {
1305
+ const initialized = yield* initializeEffect.pipe(Effect.onExit((exit) => {
1306
+ if (Exit.isFailure(exit))
1307
+ self.publishStartFailed(exit.cause);
1308
+ return cleanupStartupFailure(exit);
1309
+ }), Effect.ensuring(Effect.sync(() => {
983
1310
  self.runState = "Idle";
984
1311
  })));
985
1312
  const initial = initialized.state;
@@ -989,6 +1316,7 @@ class CompiledProcess {
989
1316
  changes: undefined,
990
1317
  snapshot: { status: "active", state: initial }
991
1318
  };
1319
+ self.publishInitialized(initial);
992
1320
  if (self.execution.drain._tag === "Process") {
993
1321
  self.processContext = {
994
1322
  ...self.processScope,
@@ -1103,11 +1431,19 @@ class CompiledProcess {
1103
1431
  : Effect.void));
1104
1432
  }
1105
1433
  sendAcknowledgedEffect(event) {
1106
- return Effect.uninterruptibleMask((restore) => Deferred.make().pipe(Effect.flatMap((deferred) => this.offerMessage({
1107
- [AcknowledgedMessageTypeId]: true,
1108
- event,
1109
- deferred
1110
- }).pipe(Effect.andThen(restore(Deferred.await(deferred)))))));
1434
+ return Effect.uninterruptibleMask((restore) => Deferred.make().pipe(Effect.flatMap((deferred) => this.offerEvent(event, undefined, undefined, deferred).pipe(Effect.andThen(restore(Deferred.await(deferred)))))));
1435
+ }
1436
+ offerEvent(event, source, causedBy, deferred) {
1437
+ return Effect.suspend(() => {
1438
+ const inspector = this.inspector;
1439
+ const subject = this.inspectionSubject;
1440
+ const message = inspector?.isActive() === true && subject !== undefined
1441
+ ? makeInspectedMessage(inspector, subject, event, source, causedBy, deferred)
1442
+ : deferred === undefined
1443
+ ? event
1444
+ : { [AcknowledgedMessageTypeId]: true, event, deferred };
1445
+ return this.offerMessage(message);
1446
+ });
1111
1447
  }
1112
1448
  offerMessage(message) {
1113
1449
  return Effect.uninterruptible(Effect.suspend(() => {
@@ -1115,6 +1451,10 @@ class CompiledProcess {
1115
1451
  return Effect.fail(new StoppedError());
1116
1452
  }
1117
1453
  offerCompactMailbox(this.mailbox, message);
1454
+ const inspector = this.inspector;
1455
+ const subject = this.inspectionSubject;
1456
+ if (inspector !== undefined && subject !== undefined)
1457
+ publishInspectedSent(inspector, subject, message);
1118
1458
  this.offerRevision += 1;
1119
1459
  if (this.runState === "Draining") {
1120
1460
  return Effect.void;
@@ -1153,6 +1493,7 @@ class CompiledProcess {
1153
1493
  this.lifecycle !== "Active" ||
1154
1494
  (this.options.onOutcome !== undefined && this.options.skipStoppedOutcome !== true) ||
1155
1495
  this.options.onStop !== undefined ||
1496
+ this.inspector !== undefined ||
1156
1497
  this.current.changes !== undefined ||
1157
1498
  this.current.terminalizing || this.current.snapshot.status !== "active") {
1158
1499
  return false;
@@ -1278,9 +1619,21 @@ class CompiledProcess {
1278
1619
  (requested._tag === "Stopped" && this.options.skipStoppedOutcome === true)
1279
1620
  ? Effect.void
1280
1621
  : Effect.suspend(() => this.options.onOutcome(classifyOutcome(snapshot))).pipe(Effect.exit, Effect.asVoid);
1622
+ const inspector = this.inspector;
1623
+ const subject = this.inspectionSubject;
1624
+ const closeEmissionsAndInspect = inspector === undefined || subject === undefined
1625
+ ? this.closeEmissions()
1626
+ : this.closeEmissions().pipe(Effect.andThen(Effect.sync(() => inspector.publishUnsafe(this.activity === undefined
1627
+ ? { _tag: "Terminated", subject, snapshot }
1628
+ : {
1629
+ _tag: "ActivityStopped",
1630
+ subject: this.activity.owner,
1631
+ activity: this.activity,
1632
+ exit: snapshot.status === "stopped" ? Exit.interrupt() : exit
1633
+ }))));
1281
1634
  return Effect.uninterruptible(Effect.sync(() => {
1282
1635
  closeCompactMailbox(this.mailbox);
1283
- }).pipe(Effect.andThen(this.childRuntime.close(exit)), Effect.andThen(this.setAndPublishSnapshot(snapshot)), Effect.andThen(this.closeEmissions()), Effect.andThen(Effect.sync(() => {
1636
+ }).pipe(Effect.andThen(this.childRuntime.close(exit)), Effect.andThen(this.setAndPublishSnapshot(snapshot)), Effect.andThen(closeEmissionsAndInspect), Effect.andThen(Effect.sync(() => {
1284
1637
  if (requested._tag === "Failure") {
1285
1638
  failAcknowledgedMessage(this.inFlightMessage, requested.cause);
1286
1639
  }
@@ -1288,7 +1641,9 @@ class CompiledProcess {
1288
1641
  stopAcknowledgedMessage(this.inFlightMessage);
1289
1642
  }
1290
1643
  this.inFlightMessage = undefined;
1291
- })), Effect.andThen(notifyOutcome), Effect.andThen(this.options.onStop ?? Effect.void), Effect.andThen(Effect.sync(() => {
1644
+ })), Effect.andThen(notifyOutcome), Effect.andThen(this.options.onStop ?? Effect.void), Effect.andThen(this.options.inspectionRoot === true && this.inspector !== undefined
1645
+ ? this.inspector.close
1646
+ : Effect.void), Effect.andThen(Effect.sync(() => {
1292
1647
  this.options.onStopSync?.();
1293
1648
  this.runState = "Idle";
1294
1649
  this.worker = undefined;
@@ -1320,11 +1675,21 @@ class CompiledProcess {
1320
1675
  : PubSub.publish(snapshot.changes, Exit.succeed(undefined)).pipe(Effect.asVoid);
1321
1676
  }
1322
1677
  emitEvent(event) {
1323
- if (this.externalEmissions !== undefined)
1324
- return this.externalEmissions.emit(event);
1325
- return Effect.suspend(() => this.emissionsPubSub === undefined || this.emissionsPubSub === EmissionsClosed
1326
- ? Effect.void
1327
- : PubSub.publish(this.emissionsPubSub, event).pipe(Effect.asVoid));
1678
+ const publish = this.externalEmissions !== undefined ?
1679
+ this.externalEmissions.emit(event) :
1680
+ Effect.suspend(() => this.emissionsPubSub === undefined || this.emissionsPubSub === EmissionsClosed
1681
+ ? Effect.void
1682
+ : PubSub.publish(this.emissionsPubSub, event).pipe(Effect.asVoid));
1683
+ const inspector = this.inspector;
1684
+ const subject = this.inspectionSubject;
1685
+ if (inspector === undefined || subject === undefined)
1686
+ return publish;
1687
+ return publish.pipe(Effect.tap(() => Effect.sync(() => inspector.publishUnsafe({
1688
+ _tag: "Emitted",
1689
+ subject,
1690
+ emission: event,
1691
+ causedBy: this.causation()
1692
+ }))));
1328
1693
  }
1329
1694
  shutdownEmissions() {
1330
1695
  return this.closeEmissions();
@@ -1533,7 +1898,11 @@ const startCompactCompiledInternal = Effect.fnUntraced(function* (logic, options
1533
1898
  options.onReady === undefined && options.onSnapshot === undefined
1534
1899
  ? process.initializeCompiledSync()
1535
1900
  : process.initialize();
1536
- return yield* initialize.pipe(Effect.onExit((exit) => Exit.isFailure(exit) ? process.shutdownEmissions() : Effect.void));
1901
+ return yield* initialize.pipe(Effect.onExit((exit) => Exit.isFailure(exit)
1902
+ ? process.shutdownEmissions().pipe(Effect.andThen(options.inspectionRoot === true && options.runtime.inspection !== undefined
1903
+ ? options.runtime.inspection.close
1904
+ : Effect.void))
1905
+ : Effect.void));
1537
1906
  });
1538
1907
  const startLogicInternal = ((logic, options) => logic.execution?._tag === "Compiled"
1539
1908
  ? startCompactCompiledInternal(logic, options)
@@ -1579,6 +1948,8 @@ export const startProcess = Effect.fnUntraced(function* (logic, options) {
1579
1948
  const prepareProcessWithStrategy = Effect.fnUntraced(function* (logic, strategy, options) {
1580
1949
  const runtime = yield* makeProcessRuntime;
1581
1950
  const sessionId = yield* runtime.nextSessionId;
1951
+ const inspection = yield* InspectionRuntime.make(sessionId);
1952
+ runtime.inspection = inspection;
1582
1953
  const emissions = makeEmissionRuntime();
1583
1954
  const started = yield* Deferred.make();
1584
1955
  const internalOptions = options === undefined
@@ -1586,14 +1957,18 @@ const prepareProcessWithStrategy = Effect.fnUntraced(function* (logic, strategy,
1586
1957
  detached: true,
1587
1958
  emissions,
1588
1959
  runtime,
1589
- sessionId
1960
+ sessionId,
1961
+ inspectionRoot: true,
1962
+ origin: { _tag: "Root" }
1590
1963
  }
1591
1964
  : {
1592
1965
  ...options,
1593
1966
  detached: true,
1594
1967
  emissions,
1595
1968
  runtime,
1596
- sessionId
1969
+ sessionId,
1970
+ inspectionRoot: true,
1971
+ origin: { _tag: "Root" }
1597
1972
  };
1598
1973
  const initialize = strategy === "generic"
1599
1974
  ? startGenericInternal(logic, internalOptions)
@@ -1608,6 +1983,7 @@ const prepareProcessWithStrategy = Effect.fnUntraced(function* (logic, strategy,
1608
1983
  sessionId,
1609
1984
  changes: Stream.unwrap(Deferred.await(started).pipe(Effect.map((ref) => ref.changes))),
1610
1985
  emissions: emissions.stream,
1986
+ inspection: inspection.stream,
1611
1987
  start
1612
1988
  };
1613
1989
  });