@swirls/looms 0.4.0 → 0.5.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 (241) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/actor/cell.d.ts +21 -2
  3. package/dist/actor/cell.js +15 -3
  4. package/dist/actor/host.d.ts +10 -2
  5. package/dist/actor/host.js +27 -4
  6. package/dist/actor/index.d.ts +2 -1
  7. package/dist/actor/index.js +1 -1
  8. package/dist/actor/routing.d.ts +3 -0
  9. package/dist/actor/routing.js +13 -2
  10. package/dist/agent/definitions-store.d.ts +1 -1
  11. package/dist/agent/definitions-store.js +1 -1
  12. package/dist/agent/definitions.d.ts +48 -0
  13. package/dist/agent/definitions.js +44 -0
  14. package/dist/agent/effects.d.ts +184 -0
  15. package/dist/agent/effects.js +172 -40
  16. package/dist/agent/events.d.ts +89 -0
  17. package/dist/agent/events.js +25 -0
  18. package/dist/agent/module.d.ts +412 -0
  19. package/dist/agent/module.js +5 -1
  20. package/dist/agent/scope.d.ts +88 -0
  21. package/dist/agent/threads.js +39 -3
  22. package/dist/agent/types.d.ts +4 -0
  23. package/dist/approval/effects.d.ts +40 -0
  24. package/dist/approval/module.d.ts +80 -0
  25. package/dist/client/client.d.ts +468 -119
  26. package/dist/client/client.js +214 -21
  27. package/dist/client/index.d.ts +1 -1
  28. package/dist/client/sse.d.ts +1 -1
  29. package/dist/client/sse.js +2 -2
  30. package/dist/cloudflare/durable-object.d.ts +66 -2
  31. package/dist/cloudflare/durable-object.js +96 -1
  32. package/dist/cloudflare/event-store.d.ts +7 -3
  33. package/dist/cloudflare/event-store.js +16 -3
  34. package/dist/cloudflare/index.d.ts +7 -1
  35. package/dist/cloudflare/index.js +7 -1
  36. package/dist/cloudflare/memory-queue.d.ts +18 -0
  37. package/dist/cloudflare/memory-queue.js +53 -0
  38. package/dist/cloudflare/queue-consumer.d.ts +52 -0
  39. package/dist/cloudflare/queue-consumer.js +102 -0
  40. package/dist/cloudflare/queue-message.d.ts +31 -0
  41. package/dist/cloudflare/queue-message.js +33 -0
  42. package/dist/cloudflare/queue-pull.d.ts +62 -0
  43. package/dist/cloudflare/queue-pull.js +130 -0
  44. package/dist/cloudflare/queue-worker.d.ts +39 -0
  45. package/dist/cloudflare/queue-worker.js +62 -0
  46. package/dist/cloudflare/r2-blob-store.d.ts +33 -0
  47. package/dist/cloudflare/r2-blob-store.js +60 -0
  48. package/dist/cloudflare/router.d.ts +4 -1
  49. package/dist/cloudflare/router.js +8 -3
  50. package/dist/cloudflare/websocket.d.ts +27 -0
  51. package/dist/cloudflare/websocket.js +215 -0
  52. package/dist/core/blob-offload.d.ts +21 -0
  53. package/dist/core/blob-offload.js +143 -0
  54. package/dist/core/blob-store.d.ts +45 -0
  55. package/dist/core/blob-store.js +164 -0
  56. package/dist/core/blobs.d.ts +37 -0
  57. package/dist/core/blobs.js +160 -0
  58. package/dist/core/definition-resolver.d.ts +40 -0
  59. package/dist/core/definition-resolver.js +129 -0
  60. package/dist/core/definition-store.d.ts +6 -0
  61. package/dist/core/definition-store.js +37 -1
  62. package/dist/core/effects.d.ts +86 -3
  63. package/dist/core/effects.js +99 -3
  64. package/dist/core/envelope.d.ts +9 -0
  65. package/dist/core/envelope.js +4 -0
  66. package/dist/core/event-codec.js +3 -0
  67. package/dist/core/fold.js +83 -1
  68. package/dist/core/ids.d.ts +2 -0
  69. package/dist/core/ids.js +4 -0
  70. package/dist/core/index.d.ts +8 -0
  71. package/dist/core/index.js +8 -0
  72. package/dist/core/launcher.d.ts +97 -0
  73. package/dist/core/launcher.js +67 -0
  74. package/dist/core/module-scope.d.ts +7 -3
  75. package/dist/core/module-scope.js +3 -0
  76. package/dist/core/module.d.ts +10 -0
  77. package/dist/core/projection.js +4 -0
  78. package/dist/core/protocol.d.ts +36 -1
  79. package/dist/core/protocol.js +35 -0
  80. package/dist/core/published.d.ts +2 -0
  81. package/dist/core/published.js +4 -1
  82. package/dist/core/replay.d.ts +96 -0
  83. package/dist/core/run-context.d.ts +33 -0
  84. package/dist/core/run-context.js +23 -0
  85. package/dist/core/sqlite-store.js +57 -5
  86. package/dist/core/state.d.ts +60 -0
  87. package/dist/core/state.js +19 -0
  88. package/dist/core/store.d.ts +6 -0
  89. package/dist/core/store.js +7 -0
  90. package/dist/core/subscription.d.ts +97 -0
  91. package/dist/core/subscription.js +224 -0
  92. package/dist/core/thread.d.ts +7 -0
  93. package/dist/core/thread.js +1 -0
  94. package/dist/core/trace.d.ts +21 -0
  95. package/dist/core/trace.js +45 -0
  96. package/dist/core/worker-callback.d.ts +25 -1
  97. package/dist/debugger/app/assets/{index-CgDzWJg7.css → index-B7GdT1UA.css} +1 -1
  98. package/dist/debugger/app/assets/index-C31SnCuN.js +22 -0
  99. package/dist/debugger/app/assets/routes-DPA3O5AF.js +47 -0
  100. package/dist/debugger/app/index.html +2 -2
  101. package/dist/debugger/components/event-inspector.d.ts +3 -1
  102. package/dist/debugger/components/event-inspector.js +25 -4
  103. package/dist/debugger/components/event-stream.d.ts +4 -1
  104. package/dist/debugger/components/event-stream.js +2 -2
  105. package/dist/debugger/components/run-tree.d.ts +5 -2
  106. package/dist/debugger/components/run-tree.js +10 -4
  107. package/dist/debugger/context.d.ts +8 -1
  108. package/dist/debugger/context.js +15 -3
  109. package/dist/debugger/events/protocol-summarize.js +18 -3
  110. package/dist/debugger/index.d.ts +1 -1
  111. package/dist/debugger/index.js +1 -1
  112. package/dist/debugger/shell/debugger-shell.js +1 -1
  113. package/dist/debugger/shell/events-pane.js +4 -3
  114. package/dist/debugger/styles.css +1 -0
  115. package/dist/evaluator/definitions-store.d.ts +3 -4
  116. package/dist/evaluator/definitions-store.js +3 -3
  117. package/dist/evaluator/definitions.d.ts +4 -1
  118. package/dist/evaluator/definitions.js +5 -2
  119. package/dist/evaluator/effects.d.ts +74 -33
  120. package/dist/evaluator/effects.js +6 -2
  121. package/dist/evaluator/module.d.ts +150 -66
  122. package/dist/evaluator/module.js +7 -3
  123. package/dist/evaluator/threads.d.ts +2 -0
  124. package/dist/evaluator/threads.js +4 -0
  125. package/dist/projectors/event-log.d.ts +87 -0
  126. package/dist/projectors/event-log.js +396 -0
  127. package/dist/projectors/index-model.d.ts +12 -0
  128. package/dist/projectors/index-model.js +12 -3
  129. package/dist/projectors/memory.js +7 -0
  130. package/dist/projectors/postgres.d.ts +1 -0
  131. package/dist/projectors/postgres.js +32 -15
  132. package/dist/projectors/schema.d.ts +5 -0
  133. package/dist/projectors/schema.js +26 -1
  134. package/dist/projectors/sqlite.js +47 -19
  135. package/dist/projectors/with-projectors.d.ts +7 -1
  136. package/dist/projectors/with-projectors.js +13 -3
  137. package/dist/react/materialize.js +5 -1
  138. package/dist/react/react.d.ts +10 -2
  139. package/dist/react/react.js +43 -19
  140. package/dist/react/store.d.ts +11 -2
  141. package/dist/react/store.js +104 -12
  142. package/dist/react/tables.d.ts +4 -1
  143. package/dist/runtime/cancellation.d.ts +2 -1
  144. package/dist/runtime/cancellation.js +35 -2
  145. package/dist/runtime/child-run.d.ts +10 -0
  146. package/dist/runtime/child-run.js +58 -0
  147. package/dist/runtime/deadline-transition-planner.js +11 -2
  148. package/dist/runtime/dispatch-effect.d.ts +2 -2
  149. package/dist/runtime/dispatch-effect.js +43 -19
  150. package/dist/runtime/errors.d.ts +14 -1
  151. package/dist/runtime/errors.js +10 -0
  152. package/dist/runtime/filesystem-blob-store.d.ts +6 -0
  153. package/dist/runtime/filesystem-blob-store.js +59 -0
  154. package/dist/runtime/helpers.d.ts +15 -6
  155. package/dist/runtime/helpers.js +46 -6
  156. package/dist/runtime/index.d.ts +4 -2
  157. package/dist/runtime/index.js +3 -1
  158. package/dist/runtime/local-effect-executor.d.ts +11 -2
  159. package/dist/runtime/local-effect-executor.js +80 -4
  160. package/dist/runtime/looms.d.ts +38 -4
  161. package/dist/runtime/looms.js +17 -3
  162. package/dist/runtime/repository.d.ts +5 -4
  163. package/dist/runtime/repository.js +5 -2
  164. package/dist/runtime/retry-policy.d.ts +5 -0
  165. package/dist/runtime/retry-policy.js +10 -2
  166. package/dist/runtime/run-start.d.ts +1 -1
  167. package/dist/runtime/run-start.js +10 -0
  168. package/dist/runtime/runtime.d.ts +2 -2
  169. package/dist/runtime/runtime.js +191 -68
  170. package/dist/runtime/server/auth.d.ts +3 -0
  171. package/dist/runtime/server/fetch-handler.d.ts +2 -2
  172. package/dist/runtime/server/fetch-handler.js +35 -4
  173. package/dist/runtime/server/grant.d.ts +49 -0
  174. package/dist/runtime/server/grant.js +177 -0
  175. package/dist/runtime/server/routes/effects.js +47 -5
  176. package/dist/runtime/server/routes/runs.js +6 -1
  177. package/dist/runtime/server/run-route.d.ts +18 -0
  178. package/dist/runtime/server/run-route.js +26 -0
  179. package/dist/runtime/server.d.ts +3 -1
  180. package/dist/runtime/server.js +3 -1
  181. package/dist/runtime/sse.js +26 -4
  182. package/dist/runtime/subscription.d.ts +8 -0
  183. package/dist/runtime/subscription.js +23 -0
  184. package/dist/runtime/types.d.ts +87 -8
  185. package/dist/runtime/wake-engine.d.ts +4 -2
  186. package/dist/runtime/wake-engine.js +57 -6
  187. package/dist/runtime/worker-callbacks.d.ts +12 -2
  188. package/dist/runtime/worker-callbacks.js +102 -72
  189. package/dist/runtime/worker-effect-dispatch.d.ts +14 -5
  190. package/dist/runtime/worker-effect-dispatch.js +84 -18
  191. package/dist/schedule/debugger/index.d.ts +4 -0
  192. package/dist/schedule/debugger/index.js +11 -0
  193. package/dist/schedule/debugger/schedule-view.d.ts +3 -0
  194. package/dist/schedule/debugger/schedule-view.js +68 -0
  195. package/dist/schedule/debugger/summarize.d.ts +2 -0
  196. package/dist/schedule/debugger/summarize.js +43 -0
  197. package/dist/schedule/definitions-store.d.ts +10 -0
  198. package/dist/schedule/definitions-store.js +8 -0
  199. package/dist/schedule/definitions.d.ts +57 -0
  200. package/dist/schedule/definitions.js +30 -0
  201. package/dist/schedule/effects.d.ts +756 -0
  202. package/dist/schedule/effects.js +244 -0
  203. package/dist/schedule/ensure.d.ts +55 -0
  204. package/dist/schedule/ensure.js +109 -0
  205. package/dist/schedule/events.d.ts +116 -0
  206. package/dist/schedule/events.js +49 -0
  207. package/dist/schedule/index.d.ts +11 -0
  208. package/dist/schedule/index.js +11 -0
  209. package/dist/schedule/module.d.ts +1474 -0
  210. package/dist/schedule/module.js +22 -0
  211. package/dist/schedule/projections.d.ts +55 -0
  212. package/dist/schedule/projections.js +88 -0
  213. package/dist/schedule/scope.d.ts +121 -0
  214. package/dist/schedule/scope.js +7 -0
  215. package/dist/schedule/signals.d.ts +25 -0
  216. package/dist/schedule/signals.js +33 -0
  217. package/dist/schedule/specs.d.ts +59 -0
  218. package/dist/schedule/specs.js +231 -0
  219. package/dist/schedule/threads.d.ts +25 -0
  220. package/dist/schedule/threads.js +259 -0
  221. package/dist/testing/corpus-module.d.ts +34 -0
  222. package/dist/testing/corpus-module.js +87 -0
  223. package/dist/testing/index.d.ts +2 -0
  224. package/dist/testing/index.js +5 -6
  225. package/dist/testing/replay-corpus.d.ts +22 -0
  226. package/dist/testing/replay-corpus.js +50 -0
  227. package/dist/workflow/definitions-store.d.ts +1 -1
  228. package/dist/workflow/definitions-store.js +1 -1
  229. package/dist/workflow/definitions.d.ts +14 -0
  230. package/dist/workflow/effects.d.ts +122 -0
  231. package/dist/workflow/effects.js +16 -6
  232. package/dist/workflow/events.d.ts +42 -0
  233. package/dist/workflow/events.js +4 -0
  234. package/dist/workflow/module.d.ts +345 -32
  235. package/dist/workflow/module.js +7 -3
  236. package/dist/workflow/scope.d.ts +42 -0
  237. package/dist/workflow/threads.d.ts +72 -0
  238. package/dist/workflow/threads.js +106 -38
  239. package/package.json +10 -1
  240. package/dist/debugger/app/assets/index-D--9esm9.js +0 -22
  241. package/dist/debugger/app/assets/routes-Dakrdusf.js +0 -47
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Looms debugger</title>
7
- <script type="module" crossorigin src="./assets/index-D--9esm9.js"></script>
7
+ <script type="module" crossorigin src="./assets/index-C31SnCuN.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-CbXtAM7H.js">
9
- <link rel="stylesheet" crossorigin href="./assets/index-CgDzWJg7.css">
9
+ <link rel="stylesheet" crossorigin href="./assets/index-B7GdT1UA.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -1,6 +1,8 @@
1
+ import type { OpenRunTarget } from '../context';
1
2
  import type { DebuggerEvent, ReplayLoader } from '../contracts';
2
- export declare function EventInspector({ event, loadReplayStep, onClose, }: {
3
+ export declare function EventInspector({ event, loadReplayStep, onClose, onOpenRun, }: {
3
4
  event: DebuggerEvent;
4
5
  loadReplayStep?: ReplayLoader;
5
6
  onClose?: () => void;
7
+ onOpenRun?: (runId: string, target?: OpenRunTarget) => void;
6
8
  }): import("react").JSX.Element;
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { X } from 'lucide-react';
2
+ import { ExternalLink, X } from 'lucide-react';
3
3
  import { useEffect, useState } from 'react';
4
- import { asJson } from '@swirls/looms/core';
4
+ import { asJson, readRunLaunchSource } from '@swirls/looms/core';
5
+ import { jsonFields, jsonText } from '../lib/payload.js';
5
6
  import { Button } from '../ui/button.js';
6
7
  import { ScrollArea } from '../ui/scroll-area.js';
7
8
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs.js';
@@ -12,9 +13,29 @@ function Pane({ value, children }) {
12
13
  function StepJson({ step, pick, }) {
13
14
  return step ? (_jsx(JsonTree, { value: pick(step), defaultExpandDepth: 2 })) : (_jsx("p", { className: "text-muted-foreground text-xs", children: "Loading\u2026" }));
14
15
  }
15
- export function EventInspector({ event, loadReplayStep, onClose, }) {
16
+ /** The other run a launch event points at: the run it launched, or the run that launched it. */
17
+ function linkedRun(event) {
18
+ const payload = jsonFields(event.payload);
19
+ if (event.type === 'runtime.run.launched') {
20
+ const runId = jsonText(payload.runId);
21
+ return runId ? { label: 'Open launched run', runId } : undefined;
22
+ }
23
+ if (event.type === 'runtime.run.started') {
24
+ const source = readRunLaunchSource(payload.launchedBy);
25
+ return source
26
+ ? {
27
+ label: 'Open launching run',
28
+ runId: source.runId,
29
+ target: { seq: source.causationSeq },
30
+ }
31
+ : undefined;
32
+ }
33
+ return undefined;
34
+ }
35
+ export function EventInspector({ event, loadReplayStep, onClose, onOpenRun, }) {
16
36
  const [step, setStep] = useState(null);
17
37
  const canReplay = loadReplayStep !== undefined;
38
+ const link = onOpenRun ? linkedRun(event) : undefined;
18
39
  useEffect(() => {
19
40
  if (!loadReplayStep) {
20
41
  setStep(null);
@@ -30,7 +51,7 @@ export function EventInspector({ event, loadReplayStep, onClose, }) {
30
51
  cancelled = true;
31
52
  };
32
53
  }, [event.runId, event.seq, loadReplayStep]);
33
- return (_jsx("div", { className: "border-border bg-sidebar flex h-[45%] min-h-40 shrink-0 flex-col border-t", children: _jsxs(Tabs, { defaultValue: "payload", className: "min-h-0 flex-1 gap-0", children: [_jsxs("div", { className: "flex h-10 shrink-0 items-center gap-2 pr-2 pl-3", children: [_jsxs("span", { className: "text-muted-foreground font-mono text-[11px] tabular-nums", children: ["#", event.seq] }), _jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: event.type }), _jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "payload", children: "Payload" }), _jsx(TabsTrigger, { value: "envelope", children: "Envelope" }), canReplay ? _jsx(TabsTrigger, { value: "state", children: "State" }) : null, canReplay ? _jsx(TabsTrigger, { value: "effects", children: "Effects" }) : null] }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Close inspector", onClick: onClose, children: _jsx(X, {}) })) : null] }), _jsx(Pane, { value: "payload", children: _jsx(JsonTree, { value: event.payload, defaultExpandDepth: 2 }) }), _jsx(Pane, { value: "envelope", children: _jsx(JsonTree, { value: asJson({
54
+ return (_jsx("div", { className: "border-border bg-sidebar flex h-[45%] min-h-40 shrink-0 flex-col border-t", children: _jsxs(Tabs, { defaultValue: "payload", className: "min-h-0 flex-1 gap-0", children: [_jsxs("div", { className: "flex h-10 shrink-0 items-center gap-2 pr-2 pl-3", children: [_jsxs("span", { className: "text-muted-foreground font-mono text-[11px] tabular-nums", children: ["#", event.seq] }), _jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: event.type }), link && onOpenRun ? (_jsxs(Button, { variant: "ghost", size: "sm", title: link.runId, onClick: () => onOpenRun(link.runId, link.target), children: [_jsx(ExternalLink, {}), link.label] })) : null, _jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "payload", children: "Payload" }), _jsx(TabsTrigger, { value: "envelope", children: "Envelope" }), canReplay ? _jsx(TabsTrigger, { value: "state", children: "State" }) : null, canReplay ? _jsx(TabsTrigger, { value: "effects", children: "Effects" }) : null] }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Close inspector", onClick: onClose, children: _jsx(X, {}) })) : null] }), _jsx(Pane, { value: "payload", children: _jsx(JsonTree, { value: event.payload, defaultExpandDepth: 2 }) }), _jsx(Pane, { value: "envelope", children: _jsx(JsonTree, { value: asJson({
34
55
  id: event.id,
35
56
  runId: event.runId,
36
57
  seq: event.seq,
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
+ import type { OpenRunTarget } from '../context';
2
3
  import type { DebuggerEvent, EventStreamCatalog, ReplayLoader } from '../contracts';
3
- export declare function EventStream<TEvent extends DebuggerEvent>({ events, startedAt, threadId, threadLabel, onClearThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, }: {
4
+ export declare function EventStream<TEvent extends DebuggerEvent>({ events, startedAt, threadId, threadLabel, onClearThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, onOpenRun, }: {
4
5
  events: readonly TEvent[];
5
6
  startedAt?: number;
6
7
  threadId?: string;
@@ -11,4 +12,6 @@ export declare function EventStream<TEvent extends DebuggerEvent>({ events, star
11
12
  catalog?: EventStreamCatalog<TEvent>;
12
13
  loadReplayStep?: ReplayLoader;
13
14
  renderInspector?: (selected: TEvent) => ReactNode;
15
+ /** Follows launch links from the inspector to another run. */
16
+ onOpenRun?: (runId: string, target?: OpenRunTarget) => void;
14
17
  }): import("react").JSX.Element;
@@ -12,7 +12,7 @@ import { PanelHeader, PanelTitle } from '../ui/panel.js';
12
12
  import { EventInspector } from './event-inspector.js';
13
13
  import { FollowList } from './follow-list.js';
14
14
  const ROW_ESTIMATE = 26;
15
- export function EventStream({ events, startedAt, threadId, threadLabel, onClearThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, }) {
15
+ export function EventStream({ events, startedAt, threadId, threadLabel, onClearThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, onOpenRun, }) {
16
16
  const eventCatalog = catalog ?? defaultEventCatalog;
17
17
  const [query, setQuery] = useState('');
18
18
  const [follow, setFollow] = useState(true);
@@ -49,7 +49,7 @@ export function EventStream({ events, startedAt, threadId, threadLabel, onClearT
49
49
  const hiddenFamilyCount = eventCatalog.families.length - families.size;
50
50
  const filtered = visible.length !== events.length;
51
51
  return (_jsxs("div", { className: "debugger-event-stream flex h-full min-h-0 flex-col", children: [_jsxs(PanelHeader, { className: "gap-1.5 pr-2", children: [_jsx(PanelTitle, { className: "hidden @md:flex", meta: filtered ? `${visible.length}/${events.length}` : events.length, children: "Events" }), _jsxs("div", { className: "relative ml-auto min-w-0 flex-1 @md:max-w-64", children: [_jsx(Search, { "aria-hidden": true, className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2 size-3 -translate-y-1/2" }), _jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: "Search events", "aria-label": "Search events", className: "h-7 w-full pl-7 text-xs" })] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon-sm", className: "relative", "aria-label": "Filter event families", children: [_jsx(SlidersHorizontal, {}), hiddenFamilyCount > 0 ? (_jsx("span", { className: "bg-foreground text-background absolute top-0.5 right-0.5 flex size-3.5 items-center justify-center rounded-full font-mono text-[8px]", children: hiddenFamilyCount })) : null] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: "Event families" }), eventCatalog.families.map((family) => (_jsxs(DropdownMenuCheckboxItem, { checked: families.has(family), onCheckedChange: () => toggleFamily(family), onSelect: (event) => event.preventDefault(), className: "font-mono", children: [_jsx("span", { "aria-hidden": true, className: "size-1.5 rounded-full", style: { backgroundColor: eventCatalog.familyColor(family) } }), family] }, family)))] })] }), _jsxs(Button, { variant: "ghost", size: "sm", "aria-pressed": follow, "aria-label": "Follow newest events", onClick: () => setFollow(!follow), className: "aria-pressed:bg-accent aria-pressed:text-foreground px-2", children: [_jsx(ArrowDownToLine, {}), _jsx("span", { className: "hidden @lg:inline", children: "Follow" })] })] }), threadId ? (_jsxs("div", { className: "border-border flex h-8 shrink-0 items-center gap-2 border-b px-3 text-[11px]", children: [_jsx("span", { className: "text-muted-foreground", children: "Thread" }), _jsxs("span", { className: "bg-accent text-foreground inline-flex h-5 min-w-0 items-center gap-1 rounded-sm pr-0.5 pl-1.5 font-mono", children: [_jsx("span", { className: "truncate", title: threadId, children: threadLabel ?? shortId(threadId) }), onClearThread ? (_jsx("button", { type: "button", "aria-label": "Show all threads", onClick: onClearThread, className: "text-muted-foreground hover:text-foreground hover:bg-background/60 flex size-4 items-center justify-center rounded-sm", children: _jsx(X, { className: "size-3" }) })) : null] })] })) : null, _jsx(FollowList, { items: visible, getKey: (event) => event.id, estimateSize: ROW_ESTIMATE, overscan: 16, follow: follow, onFollowChange: setFollow, className: "min-h-32", itemClassName: "px-1.5", empty: _jsx("p", { className: "text-muted-foreground p-6 text-center text-xs", children: events.length === 0 ? 'Waiting for events…' : 'No events match these filters.' }), renderItem: (event) => (_jsx(EventRow, { event: event, catalog: eventCatalog, startedAt: startedAt, selected: selectedSeq === event.seq, onSelectSeq: onSelectSeq })) }), selected
52
- ? (renderInspector?.(selected) ?? (_jsx(EventInspector, { event: selected, loadReplayStep: loadReplayStep, onClose: () => onSelectSeq(undefined) })))
52
+ ? (renderInspector?.(selected) ?? (_jsx(EventInspector, { event: selected, loadReplayStep: loadReplayStep, onClose: () => onSelectSeq(undefined), onOpenRun: onOpenRun })))
53
53
  : null] }));
54
54
  }
55
55
  function EventRow({ event, catalog, startedAt, selected, onSelectSeq, }) {
@@ -1,5 +1,5 @@
1
- import type { ThreadNode } from '@swirls/looms/core';
2
- export declare function RunTree({ runId, runStatus, tree, eventCounts, totalEventCount, selectedThread, onSelectThread, formatRunLabel, showHeading, }: {
1
+ import type { RunLaunchSource, ThreadNode } from '@swirls/looms/core';
2
+ export declare function RunTree({ runId, runStatus, tree, eventCounts, totalEventCount, selectedThread, onSelectThread, launchedBy, onOpenLauncher, formatRunLabel, showHeading, }: {
3
3
  runId: string;
4
4
  runStatus: string;
5
5
  tree: {
@@ -9,6 +9,9 @@ export declare function RunTree({ runId, runStatus, tree, eventCounts, totalEven
9
9
  totalEventCount?: number;
10
10
  selectedThread?: string;
11
11
  onSelectThread: (threadId: string | undefined) => void;
12
+ /** The effect in another run that launched this one. */
13
+ launchedBy?: RunLaunchSource;
14
+ onOpenLauncher?: (source: RunLaunchSource) => void;
12
15
  formatRunLabel?: (runId: string) => string;
13
16
  showHeading?: boolean;
14
17
  }): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useVirtualizer } from '@tanstack/react-virtual';
3
- import { ChevronRight } from 'lucide-react';
3
+ import { ChevronRight, CornerLeftUp } from 'lucide-react';
4
4
  import { useCallback, useMemo, useRef, useState } from 'react';
5
5
  import { cn, shortId } from '../lib/cn.js';
6
6
  import { PanelHeader, PanelTitle } from '../ui/panel.js';
@@ -33,7 +33,13 @@ function NodeRow({ row, selected, count, collapsed, onToggle, onSelect, }) {
33
33
  const hasChildren = node.children.length > 0;
34
34
  return (_jsxs("div", { className: cn('group/row relative flex h-7 items-center rounded-md pr-2 text-xs', selected ? 'bg-accent text-foreground' : 'hover:bg-accent/50'), style: { paddingLeft: 4 + depth * INDENT }, title: `${node.definitionName} · ${node.kind} · ${node.status}`, children: [Array.from({ length: depth }, (_, level) => (_jsx("span", { "aria-hidden": true, className: "bg-border absolute inset-y-0 w-px", style: { left: 12 + level * INDENT } }, level))), hasChildren ? (_jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground hover:bg-background/40 flex size-4 shrink-0 items-center justify-center rounded-sm", onClick: () => onToggle(node.threadId), "aria-label": collapsed ? 'Expand thread' : 'Collapse thread', "aria-expanded": !collapsed, children: _jsx(ChevronRight, { className: cn('ease-snappy size-3 transition-transform duration-100', !collapsed && 'rotate-90') }) })) : (_jsx("span", { className: "size-4 shrink-0" })), _jsxs("button", { type: "button", className: "flex h-full min-w-0 flex-1 items-center gap-2 pl-1 text-left", onClick: () => onSelect(node.threadId), children: [_jsx(StatusDot, { status: node.status }), _jsxs("span", { className: "min-w-0 truncate", children: [_jsx("span", { className: "font-medium", children: node.definitionName }), _jsx("span", { className: "text-muted-foreground ml-1.5 font-mono text-[10px]", children: node.kind })] }), _jsx("span", { className: "text-muted-foreground ml-auto shrink-0 font-mono text-[10px] tabular-nums", children: count })] })] }));
35
35
  }
36
- export function RunTree({ runId, runStatus, tree, eventCounts, totalEventCount, selectedThread, onSelectThread, formatRunLabel = shortId, showHeading = true, }) {
36
+ function LaunchedByRow({ source, onOpen, }) {
37
+ const label = (_jsxs(_Fragment, { children: [_jsx(CornerLeftUp, { className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground shrink-0", children: "Launched by" }), _jsx("span", { className: "min-w-0 truncate font-mono text-[10px]", children: source.runId }), _jsxs("span", { className: "text-muted-foreground ml-auto shrink-0 font-mono text-[10px] tabular-nums", children: ["#", source.causationSeq] })] }));
38
+ const title = `Effect ${source.effectId} on thread ${source.threadId}, requested by event #${source.causationSeq} of ${source.runId}`;
39
+ const className = 'flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs';
40
+ return onOpen ? (_jsx("button", { type: "button", title: title, onClick: () => onOpen(source), className: cn(className, 'hover:bg-accent/50'), children: label })) : (_jsx("div", { title: title, className: className, children: label }));
41
+ }
42
+ export function RunTree({ runId, runStatus, tree, eventCounts, totalEventCount, selectedThread, onSelectThread, launchedBy, onOpenLauncher, formatRunLabel = shortId, showHeading = true, }) {
37
43
  const [collapsed, setCollapsed] = useState(() => new Set());
38
44
  const runCount = totalEventCount ?? eventCounts.get('run') ?? 0;
39
45
  const rows = useMemo(() => flatten(tree.root, collapsed), [tree.root, collapsed]);
@@ -58,7 +64,7 @@ export function RunTree({ runId, runStatus, tree, eventCounts, totalEventCount,
58
64
  return next;
59
65
  });
60
66
  }, []);
61
- return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [showHeading ? (_jsxs(PanelHeader, { children: [_jsx(PanelTitle, { children: "Run tree" }), _jsx(StatusBadge, { status: runStatus, className: "ml-auto" })] })) : null, _jsx("div", { className: "px-2 pt-2", children: _jsxs("button", { type: "button", onClick: () => onSelectThread(undefined), className: cn('flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs', !selectedThread ? 'bg-accent text-foreground' : 'hover:bg-accent/50'), children: [_jsx("span", { className: "text-muted-foreground", children: "All threads" }), _jsx("span", { className: "text-muted-foreground/70 truncate font-mono text-[10px]", children: formatRunLabel(runId) }), _jsx("span", { className: "text-muted-foreground ml-auto font-mono text-[10px] tabular-nums", children: runCount })] }) }), rows.length === 0 ? (_jsx("p", { className: "text-muted-foreground px-4 py-2 text-xs", children: "Waiting for threads\u2026" })) : (_jsx(ScrollArea, { viewportRef: parentRef, className: "min-h-0 flex-1", children: _jsx("div", { className: "relative mx-2 mb-2", style: { height: virtualizer.getTotalSize() }, children: virtualizer.getVirtualItems().map((item) => {
67
+ return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [showHeading ? (_jsxs(PanelHeader, { children: [_jsx(PanelTitle, { children: "Run tree" }), _jsx(StatusBadge, { status: runStatus, className: "ml-auto" })] })) : null, _jsxs("div", { className: "px-2 pt-2", children: [launchedBy ? _jsx(LaunchedByRow, { source: launchedBy, onOpen: onOpenLauncher }) : null, _jsxs("button", { type: "button", onClick: () => onSelectThread(undefined), className: cn('flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs', !selectedThread ? 'bg-accent text-foreground' : 'hover:bg-accent/50'), children: [_jsx("span", { className: "text-muted-foreground", children: "All threads" }), _jsx("span", { className: "text-muted-foreground/70 truncate font-mono text-[10px]", children: formatRunLabel(runId) }), _jsx("span", { className: "text-muted-foreground ml-auto font-mono text-[10px] tabular-nums", children: runCount })] })] }), rows.length === 0 ? (_jsx("p", { className: "text-muted-foreground px-4 py-2 text-xs", children: "Waiting for threads\u2026" })) : (_jsx(ScrollArea, { viewportRef: parentRef, className: "min-h-0 flex-1", children: _jsx("div", { className: "relative mx-2 mb-2", style: { height: virtualizer.getTotalSize() }, children: virtualizer.getVirtualItems().map((item) => {
62
68
  const row = rows[item.index];
63
69
  if (!row) {
64
70
  return null;
@@ -1,14 +1,21 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { type LoomsClient } from '@swirls/looms/client';
3
3
  import type { DebuggerPlugin } from './plugin';
4
+ /** Where to land in a run another view links to. */
5
+ export interface OpenRunTarget {
6
+ readonly seq?: number;
7
+ }
4
8
  export interface DebuggerContextValue {
5
9
  readonly client: LoomsClient;
6
10
  readonly plugins: readonly DebuggerPlugin[];
11
+ /** Opens another run, for example the run a schedule fired or the run that launched this one. */
12
+ readonly openRun?: (runId: string, target?: OpenRunTarget) => void;
7
13
  }
8
14
  /** Supplies one Looms host to both the API client and the run stores. */
9
- export declare function DebuggerProvider({ endpoint, plugins, children, }: {
15
+ export declare function DebuggerProvider({ endpoint, plugins, openRun, children, }: {
10
16
  endpoint?: string;
11
17
  plugins: readonly DebuggerPlugin[];
18
+ openRun?: (runId: string, target?: OpenRunTarget) => void;
12
19
  children: ReactNode;
13
20
  }): import("react").JSX.Element;
14
21
  export declare function useDebugger(): DebuggerContextValue;
@@ -1,11 +1,23 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useContext, useMemo } from 'react';
2
+ import { createContext, useCallback, useContext, useMemo, useRef } from 'react';
3
3
  import { createLoomsClient } from '@swirls/looms/client';
4
4
  import { LoomsProvider } from '@swirls/looms/react';
5
5
  const DebuggerContext = createContext(null);
6
6
  /** Supplies one Looms host to both the API client and the run stores. */
7
- export function DebuggerProvider({ endpoint = '', plugins, children, }) {
8
- const value = useMemo(() => ({ client: createLoomsClient({ baseUrl: endpoint }), plugins }), [endpoint, plugins]);
7
+ export function DebuggerProvider({ endpoint = '', plugins, openRun, children, }) {
8
+ const openRunRef = useRef(openRun);
9
+ openRunRef.current = openRun;
10
+ const hasOpenRun = openRun !== undefined;
11
+ const stableOpenRun = useCallback((runId, target) => {
12
+ openRunRef.current?.(runId, target);
13
+ }, []);
14
+ const value = useMemo(() => {
15
+ const client = createLoomsClient({ baseUrl: endpoint });
16
+ if (!hasOpenRun) {
17
+ return { client, plugins };
18
+ }
19
+ return { client, plugins, openRun: stableOpenRun };
20
+ }, [endpoint, plugins, hasOpenRun, stableOpenRun]);
9
21
  return (_jsx(LoomsProvider, { endpoint: endpoint, children: _jsx(DebuggerContext.Provider, { value: value, children: children }) }));
10
22
  }
11
23
  export function useDebugger() {
@@ -1,4 +1,4 @@
1
- import { isJsonNumber, isJsonString, isProtocolType } from '@swirls/looms/core';
1
+ import { isJsonNumber, isJsonString, isProtocolType, readRunLaunchSource } from '@swirls/looms/core';
2
2
  import { compactJson } from '../lib/cn.js';
3
3
  import { jsonFields, jsonNumberText, jsonText } from '../lib/payload.js';
4
4
  export function summarizeProtocolEvent(event) {
@@ -7,11 +7,14 @@ export function summarizeProtocolEvent(event) {
7
7
  }
8
8
  const payload = jsonFields(event.payload);
9
9
  switch (event.type) {
10
- case 'runtime.run.started':
10
+ case 'runtime.run.started': {
11
+ const definition = `${jsonText(payload.kind) ?? 'run'}:${jsonText(payload.definitionName) ?? 'unknown'}`;
12
+ const launchedBy = readRunLaunchSource(payload.launchedBy);
11
13
  return {
12
14
  title: 'run started',
13
- detail: `${jsonText(payload.kind) ?? 'run'}:${jsonText(payload.definitionName) ?? 'unknown'}`,
15
+ detail: launchedBy ? `${definition} · launched by ${launchedBy.runId}` : definition,
14
16
  };
17
+ }
15
18
  case 'runtime.run.completed':
16
19
  return {
17
20
  title: jsonText(payload.error) ? 'run failed' : 'run completed',
@@ -19,6 +22,18 @@ export function summarizeProtocolEvent(event) {
19
22
  };
20
23
  case 'runtime.run.cancelled':
21
24
  return { title: 'run cancelled', detail: jsonText(payload.reason) };
25
+ case 'runtime.run.launched':
26
+ return {
27
+ title: 'run launched',
28
+ detail: `${jsonText(payload.kind) ?? 'run'}:${jsonText(payload.definitionName) ?? 'unknown'} · ${jsonText(payload.runId) ?? 'unknown'}`,
29
+ };
30
+ case 'runtime.child.settled':
31
+ return {
32
+ title: 'child settled',
33
+ detail: `${jsonText(payload.status) ?? 'done'} · ${jsonText(payload.runId) ?? ''}`,
34
+ };
35
+ case 'runtime.child.close.requested':
36
+ return { title: 'child close requested', detail: jsonText(payload.policy) };
22
37
  case 'runtime.run.forked':
23
38
  return {
24
39
  title: 'run forked',
@@ -1,7 +1,7 @@
1
1
  export type { DebuggerEvent, EventStreamCatalog, EventSummary, ReplayLoader, ReplayStep, RunTreeModel, ThreadTree, } from './contracts';
2
2
  export { createEventCatalog, defaultEventCatalog } from './events/catalog';
3
3
  export { summarizeProtocolEvent } from './events/protocol-summarize';
4
- export { DebuggerProvider, useDebugger, type DebuggerContextValue } from './context';
4
+ export { DebuggerProvider, useDebugger, type DebuggerContextValue, type OpenRunTarget, } from './context';
5
5
  export { projectionFor, workspaceFor, type DebuggerPlugin, type EventFamilyView, type ProjectionView, type WorkspaceContext, type WorkspaceView, } from './plugin';
6
6
  export { DebuggerShell, type DebuggerSelection } from './shell/debugger-shell';
7
7
  export { StartForm } from './shell/start-form';
@@ -1,6 +1,6 @@
1
1
  export { createEventCatalog, defaultEventCatalog } from './events/catalog.js';
2
2
  export { summarizeProtocolEvent } from './events/protocol-summarize.js';
3
- export { DebuggerProvider, useDebugger } from './context.js';
3
+ export { DebuggerProvider, useDebugger, } from './context.js';
4
4
  export { projectionFor, workspaceFor, } from './plugin.js';
5
5
  export { DebuggerShell } from './shell/debugger-shell.js';
6
6
  export { StartForm } from './shell/start-form.js';
@@ -39,5 +39,5 @@ function DebuggerFrame({ selection, onSelectionChange, }) {
39
39
  }) }), _jsxs("div", { className: "ml-auto flex min-w-0 items-center gap-2", children: [error ? (_jsx("p", { className: "text-status-failed truncate text-[11px]", title: error, children: error })) : null, selection.run ? (_jsx(RunControls, { runId: selection.run, onForked: (run) => onSelectionChange({ kind, name, run }) })) : null] })] }), _jsxs(ResizablePanelGroup, { id: "looms-debugger-columns", className: "min-h-0 flex-1", defaultLayout: layout.defaultLayout, onLayoutChanged: layout.onLayoutChanged, children: [_jsx(ResizablePanel, { id: "tree", defaultSize: "17%", minSize: "12rem", maxSize: "30%", className: "bg-sidebar", children: _jsx("section", { "aria-label": "Run tree", className: "h-full min-h-0 overflow-hidden", children: selection.run ? (_jsx(RunTreePane, { runId: selection.run, threadId: selection.thread, onSelectThread: (thread) => onSelectionChange({ ...selection, thread, seq: undefined }) })) : (_jsxs("div", { className: "flex h-full flex-col", children: [_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: "Run tree" }) }), _jsx(EmptyState, { icon: _jsx(ListTree, {}), title: "No run selected", children: "Threads and child runs appear here once a run starts." })] })) }) }), _jsx(ResizableHandle, {}), _jsx(ResizablePanel, { id: "workspace", defaultSize: "29%", minSize: "16rem", children: _jsx("section", { "aria-label": "Workspace", className: "@container h-full min-h-0 overflow-hidden", children: _jsx(Workspace, { definition: selected, runId: selection.run, onStarted: (run) => onSelectionChange({ kind, name, run }) }) }) }), _jsx(ResizableHandle, {}), _jsx(ResizablePanel, { id: "events", defaultSize: "32%", minSize: "16rem", children: _jsx("section", { "aria-label": "Events", className: "h-full min-h-0 overflow-hidden", children: selection.run ? (_jsx(EventsPane, { runId: selection.run, threadId: selection.thread, seq: selection.seq, onSelectSeq: (seq) => onSelectionChange({ ...selection, seq }), onClearThread: () => onSelectionChange({ ...selection, thread: undefined }) })) : (_jsxs("div", { className: "flex h-full flex-col", children: [_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: "Events" }) }), _jsx(EmptyState, { icon: _jsx(Activity, {}), title: "No events yet", children: "Events stream in live once a run starts." })] })) }) }), _jsx(ResizableHandle, {}), _jsx(ResizablePanel, { id: "projections", defaultSize: "22%", minSize: "14rem", className: "bg-sidebar", children: _jsx("section", { "aria-label": "Projections", className: "h-full min-h-0 overflow-hidden", children: _jsx(ProjectionsPanel, { runId: selection.run, names: catalog.projections }) }) })] })] }));
40
40
  }
41
41
  export function DebuggerShell({ plugins, selection, onSelectionChange, endpoint, }) {
42
- return (_jsx(DebuggerProvider, { endpoint: endpoint, plugins: plugins, children: _jsx(DebuggerFrame, { selection: selection, onSelectionChange: onSelectionChange }) }));
42
+ return (_jsx(DebuggerProvider, { endpoint: endpoint, plugins: plugins, openRun: (runId, target) => onSelectionChange({ run: runId, seq: target?.seq }), children: _jsx(DebuggerFrame, { selection: selection, onSelectionChange: onSelectionChange }) }));
43
43
  }
@@ -7,19 +7,20 @@ import { RunTree } from '../components/run-tree.js';
7
7
  import { useDebugger } from '../context.js';
8
8
  import { createEventCatalog } from '../events/catalog.js';
9
9
  export function EventsPane({ runId, threadId, seq, onSelectSeq, onClearThread, }) {
10
- const { client, plugins } = useDebugger();
10
+ const { client, plugins, openRun } = useDebugger();
11
11
  const catalog = useMemo(() => createEventCatalog(plugins), [plugins]);
12
12
  const store = useRunStore(runId);
13
13
  const events = useRunEvents(store);
14
14
  const summary = useRunSummary(store);
15
15
  const tree = useThreadTree(store);
16
16
  const loadReplayStep = useCallback(({ runId: replayRunId, seq: replaySeq }) => client.replayTo(replayRunId, replaySeq).then((result) => result.step), [client]);
17
- return (_jsx(EventStream, { events: events, startedAt: summary.startedAt, threadId: threadId, threadLabel: findThreadName(tree.root, threadId), onClearThread: onClearThread, selectedSeq: seq, onSelectSeq: onSelectSeq, loadReplayStep: loadReplayStep, catalog: catalog }));
17
+ return (_jsx(EventStream, { events: events, startedAt: summary.startedAt, threadId: threadId, threadLabel: findThreadName(tree.root, threadId), onClearThread: onClearThread, selectedSeq: seq, onSelectSeq: onSelectSeq, loadReplayStep: loadReplayStep, catalog: catalog, onOpenRun: openRun }));
18
18
  }
19
19
  export function RunTreePane({ runId, threadId, onSelectThread, }) {
20
+ const { openRun } = useDebugger();
20
21
  const store = useRunStore(runId);
21
22
  const summary = useRunSummary(store);
22
23
  const tree = useThreadTree(store);
23
24
  const counts = useEventCounts(store);
24
- return (_jsx(RunTree, { runId: runId, runStatus: summary.status, tree: tree, eventCounts: counts, selectedThread: threadId, onSelectThread: onSelectThread }));
25
+ return (_jsx(RunTree, { runId: runId, runStatus: summary.status, tree: tree, eventCounts: counts, selectedThread: threadId, onSelectThread: onSelectThread, launchedBy: summary.launchedBy, onOpenLauncher: openRun ? (source) => openRun(source.runId, { seq: source.causationSeq }) : undefined }));
25
26
  }
@@ -2,6 +2,7 @@
2
2
  @source "../agent/debugger/**/*.js";
3
3
  @source "../workflow/debugger/**/*.js";
4
4
  @source "../approval/debugger/**/*.js";
5
+ @source "../schedule/debugger/**/*.js";
5
6
 
6
7
  @theme inline {
7
8
  --color-background: var(--background);
@@ -1,11 +1,10 @@
1
1
  import { Context, Layer } from 'effect';
2
+ import { type DefinitionStore } from '@swirls/looms/core';
2
3
  import type { EvaluatorDefinition } from './definitions';
3
- export interface EvaluatorDefinitionStore {
4
- get(name: string): EvaluatorDefinition | undefined;
5
- }
4
+ export type EvaluatorDefinitionStore = DefinitionStore<EvaluatorDefinition>;
6
5
  declare const EvaluatorDefinitionsTag_base: Context.ServiceClass<EvaluatorDefinitionsTag, "looms/EvaluatorDefinitions", EvaluatorDefinitionStore>;
7
6
  export declare class EvaluatorDefinitionsTag extends EvaluatorDefinitionsTag_base {
8
7
  }
9
8
  export declare function makeEvaluatorDefinitionStore(definitions: ReadonlyArray<EvaluatorDefinition>): EvaluatorDefinitionStore;
10
- export declare const EvaluatorDefinitionsLive: (definitions: ReadonlyArray<EvaluatorDefinition>) => Layer.Layer<EvaluatorDefinitionsTag, never, never>;
9
+ export declare const EvaluatorDefinitionsLive: (definitions: EvaluatorDefinitionStore) => Layer.Layer<EvaluatorDefinitionsTag, never, never>;
11
10
  export {};
@@ -1,8 +1,8 @@
1
1
  import { Context, Layer } from 'effect';
2
+ import { makeDefinitionStore } from '@swirls/looms/core';
2
3
  export class EvaluatorDefinitionsTag extends Context.Service()('looms/EvaluatorDefinitions') {
3
4
  }
4
5
  export function makeEvaluatorDefinitionStore(definitions) {
5
- const map = new Map(definitions.map((def) => [def.name, def]));
6
- return { get: (name) => map.get(name) };
6
+ return makeDefinitionStore('evaluator', definitions);
7
7
  }
8
- export const EvaluatorDefinitionsLive = (definitions) => Layer.succeed(EvaluatorDefinitionsTag, makeEvaluatorDefinitionStore(definitions));
8
+ export const EvaluatorDefinitionsLive = (definitions) => Layer.succeed(EvaluatorDefinitionsTag, definitions);
@@ -5,6 +5,7 @@ import type { EvaluatorQuestions, EvaluatorRouteArgs, EvaluatorRouteDecision, Ev
5
5
  export interface EvaluatorDefinition<TName extends string = string, TInput = JsonValue> {
6
6
  readonly kind: 'evaluator';
7
7
  readonly name: TName;
8
+ readonly version: string;
8
9
  readonly description?: string;
9
10
  /** Provider model id. Omit it to use the adapter default. */
10
11
  readonly model?: string;
@@ -15,4 +16,6 @@ export interface EvaluatorDefinition<TName extends string = string, TInput = Jso
15
16
  route?(this: void, args: EvaluatorRouteArgs): EvaluatorRouteDecision;
16
17
  }
17
18
  export type AnyEvaluatorDefinition = EvaluatorDefinition;
18
- export declare function defineEvaluator<TName extends string, TInput = JsonValue>(def: Omit<EvaluatorDefinition<TName, TInput>, 'kind'>): EvaluatorDefinition<TName, TInput>;
19
+ export declare function defineEvaluator<TName extends string, TInput = JsonValue>(def: Omit<EvaluatorDefinition<TName, TInput>, 'kind' | 'version'> & {
20
+ readonly version?: string;
21
+ }): EvaluatorDefinition<TName, TInput>;
@@ -1,4 +1,7 @@
1
- import { defineKind } from '@swirls/looms/core';
1
+ import { DEFAULT_DEFINITION_VERSION, defineKind } from '@swirls/looms/core';
2
2
  export function defineEvaluator(def) {
3
- return defineKind('evaluator', def);
3
+ return defineKind('evaluator', {
4
+ ...def,
5
+ version: def.version ?? DEFAULT_DEFINITION_VERSION,
6
+ });
4
7
  }
@@ -4,8 +4,55 @@ import { EvaluatorTag } from './evaluator';
4
4
  export declare const evaluateEffect: NoInfer<import("@swirls/looms/core").EffectDefinition<{
5
5
  readonly evaluationId: string;
6
6
  readonly definitionName?: string | undefined;
7
+ readonly definitionVersion?: string | undefined;
7
8
  readonly input?: Schema.Json | undefined;
8
9
  }, EvaluatorDefinitionsTag | EvaluatorTag, (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
10
+ readonly type: "evaluator.evaluated";
11
+ readonly payload: {
12
+ readonly evaluationId: string;
13
+ readonly definitionName?: string | undefined;
14
+ readonly answers: {
15
+ readonly [x: string]: {
16
+ readonly type: "boolean";
17
+ readonly value: boolean;
18
+ readonly probability: number;
19
+ } | {
20
+ readonly type: "choice";
21
+ readonly value: string;
22
+ } | {
23
+ readonly type: "score";
24
+ readonly value: number;
25
+ };
26
+ };
27
+ readonly route: string;
28
+ readonly reason: "model" | "skipped" | "unavailable" | "uncertain";
29
+ readonly usage?: {
30
+ readonly input: number;
31
+ readonly output: number;
32
+ } | undefined;
33
+ };
34
+ }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
35
+ readonly type: "evaluator.requested";
36
+ readonly payload: {
37
+ readonly evaluationId: string;
38
+ readonly definitionName?: string | undefined;
39
+ readonly state: string;
40
+ readonly model?: string | undefined;
41
+ };
42
+ }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
43
+ readonly type: "runtime.child.close.requested";
44
+ readonly payload: {
45
+ readonly policy: "terminate" | "wait";
46
+ };
47
+ }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
48
+ readonly type: "runtime.child.settled";
49
+ readonly payload: {
50
+ readonly runId: string;
51
+ readonly status: "cancelled" | "completed" | "failed";
52
+ readonly output: Schema.Json;
53
+ readonly error: string | null;
54
+ };
55
+ }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
9
56
  readonly type: "runtime.effect.ambiguous";
10
57
  readonly payload: {
11
58
  readonly effectId: string;
@@ -49,6 +96,8 @@ export declare const evaluateEffect: NoInfer<import("@swirls/looms/core").Effect
49
96
  readonly payload: {
50
97
  readonly effectId: string;
51
98
  readonly error: string;
99
+ readonly nonRetryable?: boolean | undefined;
100
+ readonly retryAfterMs?: number | undefined;
52
101
  };
53
102
  }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
54
103
  readonly type: "runtime.effect.heartbeat";
@@ -57,6 +106,7 @@ export declare const evaluateEffect: NoInfer<import("@swirls/looms/core").Effect
57
106
  readonly attempt: number;
58
107
  readonly heartbeatAt: number;
59
108
  readonly deadlineAt: number | null;
109
+ readonly details?: Schema.Json | undefined;
60
110
  };
61
111
  }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
62
112
  readonly type: "runtime.effect.queued";
@@ -109,6 +159,16 @@ export declare const evaluateEffect: NoInfer<import("@swirls/looms/core").Effect
109
159
  readonly sourceRunId: string;
110
160
  readonly sourceSeq: number;
111
161
  };
162
+ }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
163
+ readonly type: "runtime.run.launched";
164
+ readonly payload: {
165
+ readonly runId: string;
166
+ readonly kind: string;
167
+ readonly definitionName: string;
168
+ readonly definitionVersion: string;
169
+ readonly effectId: string;
170
+ readonly close: "terminate" | "wait";
171
+ };
112
172
  }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
113
173
  readonly type: "runtime.run.started";
114
174
  readonly payload: {
@@ -118,6 +178,20 @@ export declare const evaluateEffect: NoInfer<import("@swirls/looms/core").Effect
118
178
  readonly definitionVersion: string;
119
179
  readonly input: Schema.Json;
120
180
  readonly requestedThreadId?: string | null | undefined;
181
+ readonly launchedBy?: {
182
+ readonly runId: string;
183
+ readonly threadId: string;
184
+ readonly effectId: string;
185
+ readonly causationId: string;
186
+ readonly causationSeq: number;
187
+ } | undefined;
188
+ readonly attributes?: {
189
+ readonly [x: string]: string;
190
+ } | undefined;
191
+ readonly trace?: {
192
+ readonly traceparent: string;
193
+ readonly tracestate?: string | undefined;
194
+ } | undefined;
121
195
  };
122
196
  }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
123
197
  readonly type: "runtime.signal.received";
@@ -202,37 +276,4 @@ export declare const evaluateEffect: NoInfer<import("@swirls/looms/core").Effect
202
276
  readonly payload: Schema.Json;
203
277
  };
204
278
  };
205
- }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
206
- readonly type: "evaluator.evaluated";
207
- readonly payload: {
208
- readonly evaluationId: string;
209
- readonly definitionName?: string | undefined;
210
- readonly answers: {
211
- readonly [x: string]: {
212
- readonly type: "boolean";
213
- readonly value: boolean;
214
- readonly probability: number;
215
- } | {
216
- readonly type: "choice";
217
- readonly value: string;
218
- } | {
219
- readonly type: "score";
220
- readonly value: number;
221
- };
222
- };
223
- readonly route: string;
224
- readonly reason: "model" | "skipped" | "unavailable" | "uncertain";
225
- readonly usage?: {
226
- readonly input: number;
227
- readonly output: number;
228
- } | undefined;
229
- };
230
- }) | (Omit<import("@swirls/looms/core").EventInput, "payload" | "type"> & {
231
- readonly type: "evaluator.requested";
232
- readonly payload: {
233
- readonly evaluationId: string;
234
- readonly definitionName?: string | undefined;
235
- readonly state: string;
236
- readonly model?: string | undefined;
237
- };
238
279
  })>>;
@@ -1,11 +1,12 @@
1
1
  import { Effect, Schema } from 'effect';
2
- import { stringifyJson, validateInputEffect } from '@swirls/looms/core';
2
+ import { DEFAULT_DEFINITION_VERSION, stringifyJson, validateInputEffect, } from '@swirls/looms/core';
3
3
  import { EvaluatorDefinitionsTag } from './definitions-store.js';
4
4
  import { EvaluatorTag } from './evaluator.js';
5
5
  import { evaluatorModule } from './scope.js';
6
6
  const EvaluateInput = Schema.Struct({
7
7
  evaluationId: Schema.String,
8
8
  definitionName: Schema.optional(Schema.String),
9
+ definitionVersion: Schema.optional(Schema.String),
9
10
  input: Schema.optional(Schema.Json),
10
11
  });
11
12
  function errorMessage(cause) {
@@ -36,7 +37,10 @@ export const evaluateEffect = evaluatorModule.effect({
36
37
  const threadId = ctx.threadId;
37
38
  const definitions = yield* EvaluatorDefinitionsTag;
38
39
  const evaluator = yield* EvaluatorTag;
39
- const definition = input.definitionName ? definitions.get(input.definitionName) : undefined;
40
+ const version = input.definitionVersion ?? DEFAULT_DEFINITION_VERSION;
41
+ const definition = input.definitionName
42
+ ? yield* definitions.resolve(input.definitionName, version)
43
+ : undefined;
40
44
  if (!definition) {
41
45
  const name = input.definitionName ?? 'unknown';
42
46
  return failed(threadId, `Unknown evaluator definition for ${name}`);