@swirls/looms 0.1.0 → 0.3.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.
- package/CHANGELOG.md +37 -0
- package/dist/actor/host.js +4 -5
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/routing.d.ts +5 -0
- package/dist/actor/routing.js +14 -0
- package/dist/agent/agent-session.js +1 -0
- package/dist/agent/debugger/agent-chat.d.ts +2 -0
- package/dist/agent/debugger/agent-chat.js +210 -0
- package/dist/agent/debugger/index.d.ts +5 -0
- package/dist/agent/debugger/index.js +19 -0
- package/dist/agent/debugger/summarize.d.ts +2 -0
- package/dist/agent/debugger/summarize.js +53 -0
- package/dist/agent/debugger/token-usage.d.ts +3 -0
- package/dist/agent/debugger/token-usage.js +11 -0
- package/dist/agent/definitions.d.ts +3 -11
- package/dist/agent/definitions.js +5 -6
- package/dist/agent/effects.d.ts +30 -0
- package/dist/agent/events.d.ts +2 -0
- package/dist/agent/events.js +2 -0
- package/dist/agent/llm.js +1 -8
- package/dist/agent/module.d.ts +604 -534
- package/dist/agent/module.js +11 -7
- package/dist/agent/projections.d.ts +5 -0
- package/dist/agent/projections.js +43 -2
- package/dist/agent/scope.d.ts +2 -0
- package/dist/agent/signals.d.ts +7 -0
- package/dist/agent/signals.js +25 -16
- package/dist/agent/threads.js +35 -21
- package/dist/agent/tool-schema.d.ts +1 -17
- package/dist/agent/tool-schema.js +3 -42
- package/dist/agent/transcript.d.ts +7 -0
- package/dist/agent/transcript.js +19 -0
- package/dist/agent/types.d.ts +10 -0
- package/dist/agent/types.js +4 -0
- package/dist/ai-vercel/index.d.ts +1 -1
- package/dist/ai-vercel/vercel-evaluator.d.ts +21 -8
- package/dist/ai-vercel/vercel-evaluator.js +7 -2
- package/dist/approval/debugger/approvals-view.d.ts +3 -0
- package/dist/approval/debugger/approvals-view.js +18 -0
- package/dist/approval/debugger/index.d.ts +4 -0
- package/dist/approval/debugger/index.js +11 -0
- package/dist/approval/debugger/summarize.d.ts +2 -0
- package/dist/approval/debugger/summarize.js +17 -0
- package/dist/approval/effects.d.ts +12 -0
- package/dist/approval/module.d.ts +24 -0
- package/dist/client/client.d.ts +310 -0
- package/dist/client/client.js +20 -2
- package/dist/cloudflare/router.d.ts +1 -1
- package/dist/cloudflare/router.js +6 -8
- package/dist/core/compose.js +1 -0
- package/dist/core/debugger-host.d.ts +16 -0
- package/dist/core/debugger-host.js +1 -0
- package/dist/core/effects.d.ts +7 -0
- package/dist/core/effects.js +1 -0
- package/dist/core/fold.js +12 -6
- package/dist/core/fork.d.ts +35 -0
- package/dist/core/fork.js +129 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +2 -0
- package/dist/core/module-scope.d.ts +1 -0
- package/dist/core/module.d.ts +4 -2
- package/dist/core/projection.js +1 -0
- package/dist/core/protocol.d.ts +10 -1
- package/dist/core/protocol.js +11 -0
- package/dist/core/published.d.ts +43 -0
- package/dist/core/published.js +44 -0
- package/dist/core/replay.d.ts +8 -0
- package/dist/core/schema.d.ts +3 -1
- package/dist/core/schema.js +38 -0
- package/dist/core/snapshot-store.d.ts +4 -0
- package/dist/core/snapshot-store.js +7 -0
- package/dist/core/snapshots.js +3 -17
- package/dist/core/sqlite-store.js +39 -0
- package/dist/core/state.d.ts +14 -0
- package/dist/core/state.js +25 -2
- package/dist/core/store.d.ts +1 -0
- package/dist/debugger/app/assets/index-BevQYa-U.js +22 -0
- package/dist/debugger/app/assets/index-CgDzWJg7.css +2 -0
- package/dist/debugger/app/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/dist/debugger/app/assets/routes-B17Om93i.js +47 -0
- package/dist/debugger/app/index.html +14 -0
- package/dist/debugger/components/composer.d.ts +20 -0
- package/dist/debugger/components/composer.js +26 -0
- package/dist/debugger/components/debugger-split.d.ts +2 -1
- package/dist/debugger/components/debugger-split.js +2 -2
- package/dist/debugger/components/event-inspector.d.ts +2 -1
- package/dist/debugger/components/event-inspector.js +25 -16
- package/dist/debugger/components/event-stream.d.ts +2 -1
- package/dist/debugger/components/event-stream.js +12 -71
- package/dist/debugger/components/follow-list.d.ts +19 -0
- package/dist/debugger/components/follow-list.js +96 -0
- package/dist/debugger/components/json-tree.d.ts +11 -0
- package/dist/debugger/components/json-tree.js +120 -0
- package/dist/debugger/components/json-view.js +2 -2
- package/dist/debugger/components/run-tree.js +50 -20
- package/dist/debugger/components/status-dot.d.ts +4 -0
- package/dist/debugger/components/status-dot.js +6 -2
- package/dist/debugger/context.d.ts +14 -0
- package/dist/debugger/context.js +17 -0
- package/dist/debugger/contracts.d.ts +7 -4
- package/dist/debugger/events/builtin-families.d.ts +3 -0
- package/dist/debugger/events/builtin-families.js +9 -0
- package/dist/debugger/events/catalog.d.ts +3 -4
- package/dist/debugger/events/catalog.js +25 -17
- package/dist/debugger/events/protocol-summarize.d.ts +1 -2
- package/dist/debugger/events/protocol-summarize.js +36 -37
- package/dist/debugger/index.d.ts +20 -6
- package/dist/debugger/index.js +19 -5
- package/dist/debugger/lib/cn.d.ts +1 -0
- package/dist/debugger/lib/cn.js +3 -0
- package/dist/debugger/lib/event-counts.d.ts +2 -2
- package/dist/debugger/lib/event-counts.js +5 -9
- package/dist/debugger/lib/event-meta.d.ts +2 -3
- package/dist/debugger/lib/event-meta.js +6 -11
- package/dist/debugger/lib/json.d.ts +25 -0
- package/dist/debugger/lib/json.js +87 -0
- package/dist/debugger/lib/payload.d.ts +7 -0
- package/dist/debugger/lib/payload.js +11 -0
- package/dist/debugger/lib/time.d.ts +3 -0
- package/dist/debugger/lib/time.js +24 -0
- package/dist/debugger/plugin.d.ts +36 -0
- package/dist/debugger/plugin.js +14 -0
- package/dist/debugger/server/index.d.ts +14 -0
- package/dist/debugger/server/index.js +114 -0
- package/dist/debugger/shell/debugger-shell.d.ts +15 -0
- package/dist/debugger/shell/debugger-shell.js +43 -0
- package/dist/debugger/shell/definition-key.d.ts +3 -0
- package/dist/debugger/shell/definition-key.js +4 -0
- package/dist/debugger/shell/events-pane.d.ts +12 -0
- package/dist/debugger/shell/events-pane.js +25 -0
- package/dist/debugger/shell/projections-panel.d.ts +4 -0
- package/dist/debugger/shell/projections-panel.js +59 -0
- package/dist/debugger/shell/run-controls.d.ts +5 -0
- package/dist/debugger/shell/run-controls.js +99 -0
- package/dist/debugger/shell/run-search.d.ts +6 -0
- package/dist/debugger/shell/run-search.js +100 -0
- package/dist/debugger/shell/run-type-menu.d.ts +6 -0
- package/dist/debugger/shell/run-type-menu.js +11 -0
- package/dist/debugger/shell/schema-form.d.ts +31 -0
- package/dist/debugger/shell/schema-form.js +117 -0
- package/dist/debugger/shell/start-form.d.ts +3 -0
- package/dist/debugger/shell/start-form.js +75 -0
- package/dist/debugger/shell/use-start-run.d.ts +14 -0
- package/dist/debugger/shell/use-start-run.js +32 -0
- package/dist/debugger/shell/workspace.d.ts +6 -0
- package/dist/debugger/shell/workspace.js +15 -0
- package/dist/debugger/styles.css +111 -27
- package/dist/debugger/ui/button.d.ts +24 -0
- package/dist/debugger/ui/button.js +24 -0
- package/dist/debugger/ui/command.d.ts +9 -0
- package/dist/debugger/ui/command.js +24 -0
- package/dist/debugger/ui/dropdown-menu.d.ts +13 -0
- package/dist/debugger/ui/dropdown-menu.js +37 -0
- package/dist/debugger/ui/panel.d.ts +14 -0
- package/dist/debugger/ui/panel.js +16 -0
- package/dist/debugger/ui/popover.d.ts +7 -0
- package/dist/debugger/ui/popover.js +17 -0
- package/dist/debugger/ui/resizable.d.ts +7 -0
- package/dist/debugger/ui/resizable.js +14 -0
- package/dist/debugger/ui/scroll-area.d.ts +2 -1
- package/dist/debugger/ui/scroll-area.js +5 -3
- package/dist/debugger/ui/select.d.ts +10 -0
- package/dist/debugger/ui/select.js +21 -0
- package/dist/debugger/ui/tabs.js +2 -2
- package/dist/debugger/ui/textarea.d.ts +3 -0
- package/dist/debugger/ui/textarea.js +7 -0
- package/dist/evaluator/effects.d.ts +12 -0
- package/dist/evaluator/module.d.ts +24 -0
- package/dist/projectors/index-model.d.ts +13 -7
- package/dist/projectors/index-model.js +21 -26
- package/dist/projectors/memory.js +19 -11
- package/dist/projectors/postgres.js +20 -19
- package/dist/projectors/schema.js +20 -18
- package/dist/projectors/sqlite.js +32 -27
- package/dist/react/materialize.js +16 -11
- package/dist/react/react.d.ts +6 -2
- package/dist/react/react.js +5 -2
- package/dist/react/store.d.ts +1 -1
- package/dist/react/store.js +1 -1
- package/dist/react/tables.d.ts +3 -2
- package/dist/runtime/cancellation.js +14 -27
- package/dist/runtime/errors.d.ts +39 -0
- package/dist/runtime/errors.js +32 -0
- package/dist/runtime/helpers.d.ts +2 -0
- package/dist/runtime/helpers.js +23 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/looms.d.ts +13 -1
- package/dist/runtime/looms.js +5 -2
- package/dist/runtime/repository.js +16 -1
- package/dist/runtime/run-start.js +1 -3
- package/dist/runtime/runtime.d.ts +1 -1
- package/dist/runtime/runtime.js +60 -18
- package/dist/runtime/server/auth.d.ts +1 -0
- package/dist/runtime/server/auth.js +5 -0
- package/dist/runtime/server/debugger-host.d.ts +11 -0
- package/dist/runtime/server/debugger-host.js +32 -0
- package/dist/runtime/server/fetch-handler.d.ts +5 -1
- package/dist/runtime/server/fetch-handler.js +24 -13
- package/dist/runtime/server/http.d.ts +3 -0
- package/dist/runtime/server/http.js +21 -2
- package/dist/runtime/server/routes/definitions.d.ts +3 -0
- package/dist/runtime/server/routes/definitions.js +20 -0
- package/dist/runtime/server/routes/runs.js +43 -3
- package/dist/runtime/server.d.ts +3 -1
- package/dist/runtime/server.js +3 -1
- package/dist/runtime/types.d.ts +11 -1
- package/dist/runtime/wake-engine.js +5 -2
- package/dist/s2/snapshot-store.js +45 -7
- package/dist/workflow/debugger/index.d.ts +4 -0
- package/dist/workflow/debugger/index.js +11 -0
- package/dist/workflow/debugger/nodes-view.d.ts +3 -0
- package/dist/workflow/debugger/nodes-view.js +13 -0
- package/dist/workflow/debugger/summarize.d.ts +2 -0
- package/dist/workflow/debugger/summarize.js +30 -0
- package/dist/workflow/effects.d.ts +24 -0
- package/dist/workflow/effects.js +1 -0
- package/dist/workflow/module.d.ts +48 -0
- package/package.json +20 -1
- package/dist/debugger/events/domain-summarize.d.ts +0 -3
- package/dist/debugger/events/domain-summarize.js +0 -107
- package/dist/debugger/events/family.d.ts +0 -8
- package/dist/debugger/events/family.js +0 -44
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Data, Effect, Predicate } from 'effect';
|
|
2
|
+
import { isPrimitiveEffect, isWithdrawnError, } from './effects.js';
|
|
3
|
+
import { createEvent } from './envelope.js';
|
|
4
|
+
import { foldEvent, foldRun } from './fold.js';
|
|
5
|
+
import { emptyRunState, isRunTerminal } from './state.js';
|
|
6
|
+
import { isTerminalStatus } from './thread.js';
|
|
7
|
+
export class ForkSourceError extends Data.TaggedError('ForkSourceError') {
|
|
8
|
+
constructor(runId, message) {
|
|
9
|
+
super({ runId, message });
|
|
10
|
+
this.name = 'ForkSourceError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function payloadThreadId(event) {
|
|
14
|
+
const threadId = Predicate.isObject(event.payload) ? event.payload.threadId : undefined;
|
|
15
|
+
return Predicate.isString(threadId) ? threadId : event.threadId;
|
|
16
|
+
}
|
|
17
|
+
function rootTerminal(state) {
|
|
18
|
+
const root = state.rootThreadId ? state.threads[state.rootThreadId] : undefined;
|
|
19
|
+
return isRunTerminal(state) || (root !== undefined && isTerminalStatus(root.status));
|
|
20
|
+
}
|
|
21
|
+
/** Where the cancellation that wrote `event` began, when it recorded that; a seq is an index + 1. */
|
|
22
|
+
function cancellationStart(event) {
|
|
23
|
+
const resumeSeq = event.type === 'runtime.thread.cancelled' && Predicate.isObject(event.payload)
|
|
24
|
+
? event.payload.resumeSeq
|
|
25
|
+
: undefined;
|
|
26
|
+
return Predicate.isNumber(resumeSeq) ? resumeSeq : undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fold the source until its root thread ends. Everything from that event on is how the run
|
|
30
|
+
* finished, so the fork leaves it out; a cancellation that recorded where it began is left
|
|
31
|
+
* out from there.
|
|
32
|
+
*/
|
|
33
|
+
function scanSource(events, registry) {
|
|
34
|
+
const effects = new Map();
|
|
35
|
+
const failedAt = new Map();
|
|
36
|
+
let state = emptyRunState(events[0]?.runId ?? '');
|
|
37
|
+
for (const [index, event] of events.entries()) {
|
|
38
|
+
state = foldEvent(state, event, registry);
|
|
39
|
+
for (const item of state.outstandingEffects) {
|
|
40
|
+
effects.set(item.effectId, item.effect);
|
|
41
|
+
}
|
|
42
|
+
for (const record of Object.values(state.threads)) {
|
|
43
|
+
if (record.status === 'failed' && !failedAt.has(record.threadId)) {
|
|
44
|
+
failedAt.set(record.threadId, index);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (rootTerminal(state)) {
|
|
48
|
+
const root = state.rootThreadId ? state.threads[state.rootThreadId] : undefined;
|
|
49
|
+
const rootFailed = root?.status === 'failed' || state.status === 'failed';
|
|
50
|
+
return { cut: cancellationStart(event) ?? index, effects, failedAt, rootFailed };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { cut: events.length, effects, failedAt, rootFailed: false };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Walk back from the root's failure to the effect outcome that started it, so the fork
|
|
57
|
+
* re-issues that effect instead of replaying a failure the modules already folded in.
|
|
58
|
+
*/
|
|
59
|
+
function failureOrigin(events, scan, registry) {
|
|
60
|
+
const indexById = new Map(events.map((event, index) => [event.id, index]));
|
|
61
|
+
/** A failed thread leads back to where the fold failed it, anything else to its cause. */
|
|
62
|
+
const previous = (index) => {
|
|
63
|
+
const event = events[index];
|
|
64
|
+
const threadId = event.type === 'runtime.thread.failed' ? payloadThreadId(event) : null;
|
|
65
|
+
const failedAt = threadId ? scan.failedAt.get(threadId) : undefined;
|
|
66
|
+
const cause = event.causationId ? indexById.get(event.causationId) : undefined;
|
|
67
|
+
const next = failedAt !== undefined && failedAt < index ? failedAt : cause;
|
|
68
|
+
return next !== undefined && next < index ? next : undefined;
|
|
69
|
+
};
|
|
70
|
+
for (let index = scan.cut; index !== undefined; index = previous(index)) {
|
|
71
|
+
const event = events[index];
|
|
72
|
+
const effect = event.effectId ? scan.effects.get(event.effectId) : undefined;
|
|
73
|
+
const error = Predicate.isObject(event.payload) ? event.payload.error : undefined;
|
|
74
|
+
const withdrawn = Predicate.isString(error) && isWithdrawnError(error);
|
|
75
|
+
if (effect &&
|
|
76
|
+
!isPrimitiveEffect(effect) &&
|
|
77
|
+
!registry.effects.get(effect.type)?.deterministic &&
|
|
78
|
+
!withdrawn) {
|
|
79
|
+
return index;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return scan.cut;
|
|
83
|
+
}
|
|
84
|
+
function copyEvent(event, runId, reissued) {
|
|
85
|
+
if (event.ephemeral && event.effectId && reissued.has(event.effectId)) {
|
|
86
|
+
return { ...event, runId, type: 'runtime.fork.elided', payload: { type: event.type } };
|
|
87
|
+
}
|
|
88
|
+
if (event.type === 'runtime.snapshot.taken' && Predicate.isObject(event.payload)) {
|
|
89
|
+
const { state: _state, ...pointer } = event.payload;
|
|
90
|
+
return { ...event, runId, payload: pointer };
|
|
91
|
+
}
|
|
92
|
+
if (event.type === 'runtime.run.started') {
|
|
93
|
+
return { ...event, runId, idempotencyKey: null };
|
|
94
|
+
}
|
|
95
|
+
return { ...event, runId };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Plan a new run that continues where the source run ended. The fork copies the source's
|
|
99
|
+
* history up to the point the run finished, keeping sequence numbers so effect ids still
|
|
100
|
+
* line up, and its next wake re-issues whatever effects were outstanding there.
|
|
101
|
+
*/
|
|
102
|
+
export function planFork(input) {
|
|
103
|
+
const { sourceRunId, runId, events, registry } = input;
|
|
104
|
+
if (events.length === 0) {
|
|
105
|
+
return Effect.fail(new ForkSourceError(sourceRunId, `Run "${sourceRunId}" has no events`));
|
|
106
|
+
}
|
|
107
|
+
if (events.some((event, index) => event.seq !== index + 1)) {
|
|
108
|
+
return Effect.fail(new ForkSourceError(sourceRunId, `Run "${sourceRunId}" does not have its full history from seq 1; it cannot be forked`));
|
|
109
|
+
}
|
|
110
|
+
const scan = scanSource(events, registry);
|
|
111
|
+
const cut = scan.rootFailed ? failureOrigin(events, scan, registry) : scan.cut;
|
|
112
|
+
const prefix = events.slice(0, cut);
|
|
113
|
+
const atCut = foldRun(prefix, registry, { runId });
|
|
114
|
+
if (!atCut.rootThreadId) {
|
|
115
|
+
return Effect.fail(new ForkSourceError(sourceRunId, `Run "${sourceRunId}" never started a root thread`));
|
|
116
|
+
}
|
|
117
|
+
const reissued = new Set(atCut.outstandingEffects.map((item) => item.effectId));
|
|
118
|
+
const marker = createEvent(runId, {
|
|
119
|
+
type: 'runtime.run.forked',
|
|
120
|
+
payload: { sourceRunId, sourceSeq: cut },
|
|
121
|
+
threadId: null,
|
|
122
|
+
origin: { type: 'system' },
|
|
123
|
+
}, { seq: cut + 1 });
|
|
124
|
+
return Effect.succeed({
|
|
125
|
+
events: [...prefix.map((event) => copyEvent(event, runId, reissued)), marker],
|
|
126
|
+
sourceSeq: cut,
|
|
127
|
+
rootThreadId: atCut.rootThreadId,
|
|
128
|
+
});
|
|
129
|
+
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './state';
|
|
|
13
13
|
export * from './match';
|
|
14
14
|
export * from './fold';
|
|
15
15
|
export * from './replay';
|
|
16
|
+
export * from './fork';
|
|
16
17
|
export * from './projection';
|
|
17
18
|
export * from './module';
|
|
18
19
|
export * from './kind';
|
|
@@ -28,3 +29,5 @@ export * from './coalesce';
|
|
|
28
29
|
export * from './event-index';
|
|
29
30
|
export * from './limits';
|
|
30
31
|
export * from './definition-store';
|
|
32
|
+
export * from './published';
|
|
33
|
+
export type { DebuggerHost, DebuggerRequest } from './debugger-host';
|
package/dist/core/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from './state.js';
|
|
|
12
12
|
export * from './match.js';
|
|
13
13
|
export * from './fold.js';
|
|
14
14
|
export * from './replay.js';
|
|
15
|
+
export * from './fork.js';
|
|
15
16
|
export * from './projection.js';
|
|
16
17
|
export * from './module.js';
|
|
17
18
|
export * from './kind.js';
|
|
@@ -27,3 +28,4 @@ export * from './coalesce.js';
|
|
|
27
28
|
export * from './event-index.js';
|
|
28
29
|
export * from './limits.js';
|
|
29
30
|
export * from './definition-store.js';
|
|
31
|
+
export * from './published.js';
|
|
@@ -65,6 +65,7 @@ export interface ModuleScope<TNamespace extends string, U extends EventEnvelope<
|
|
|
65
65
|
input?: TSchema;
|
|
66
66
|
retry?: RetryPolicy;
|
|
67
67
|
execution?: EffectExecution;
|
|
68
|
+
deterministic?: boolean;
|
|
68
69
|
execute?: (input: TInput, ctx: ScopedEffectContext<EventInputOf<U>>) => EffectHandlerResult<R, EventInputOf<U>, HandlerError>;
|
|
69
70
|
}): NoInfer<EffectDefinition<TInput, R, EventInputOf<U>>>;
|
|
70
71
|
input<K extends keyof (TEvents extends EventCatalog<any, infer E> ? E : never) & string>(key: K, payload: InferPayload<(TEvents extends EventCatalog<any, infer E> ? E : never)[K]>, meta?: Omit<EventInput, 'type' | 'payload'>): Omit<EventInput, 'type' | 'payload'> & {
|
package/dist/core/module.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export type NextEffectHandler = (effect: RuntimeEffect, ctx: EffectContext) => E
|
|
|
10
10
|
export type EffectMiddleware = (effect: RuntimeEffect, ctx: EffectContext, next: NextEffectHandler) => Effect.Effect<ReadonlyArray<EventInput>, Error>;
|
|
11
11
|
/**
|
|
12
12
|
* Names the threads whose in-process effects an admitted signal event interrupts, so a running
|
|
13
|
-
* effect (such as a model call) can stop early.
|
|
14
|
-
* nothing.
|
|
13
|
+
* effect (such as a model call) can stop early. The runtime also interrupts each named thread's
|
|
14
|
+
* descendants. Return an empty array when the event interrupts nothing.
|
|
15
15
|
*/
|
|
16
16
|
export type SignalInterruptRule = (event: EventInput) => readonly string[];
|
|
17
17
|
export interface RuntimeModuleDependency {
|
|
@@ -24,6 +24,7 @@ export interface DefinitionRef {
|
|
|
24
24
|
readonly name: string;
|
|
25
25
|
readonly version?: string;
|
|
26
26
|
readonly input?: SchemaInput | undefined;
|
|
27
|
+
readonly description?: string | undefined;
|
|
27
28
|
}
|
|
28
29
|
export declare const DEFAULT_DEFINITION_VERSION = "v1";
|
|
29
30
|
export declare function definitionKey(kind: string, name: string, version: string): string;
|
|
@@ -33,6 +34,7 @@ export interface RegisteredDefinition {
|
|
|
33
34
|
readonly name: string;
|
|
34
35
|
readonly version: string;
|
|
35
36
|
readonly input?: SchemaInput | undefined;
|
|
37
|
+
readonly description?: string | undefined;
|
|
36
38
|
readonly value: DefinitionRef;
|
|
37
39
|
}
|
|
38
40
|
/**
|
package/dist/core/projection.js
CHANGED
package/dist/core/protocol.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export declare const protocolCatalog: import("./catalog").EventCatalog<"runtime"
|
|
|
17
17
|
'run.cancelled': Schema.Struct<{
|
|
18
18
|
readonly reason: Schema.optional<Schema.String>;
|
|
19
19
|
}>;
|
|
20
|
+
'run.forked': Schema.Struct<{
|
|
21
|
+
readonly sourceRunId: Schema.String;
|
|
22
|
+
readonly sourceSeq: Schema.Finite;
|
|
23
|
+
}>;
|
|
24
|
+
'fork.elided': Schema.Struct<{
|
|
25
|
+
readonly type: Schema.String;
|
|
26
|
+
}>;
|
|
20
27
|
'thread.started': Schema.Struct<{
|
|
21
28
|
readonly threadId: Schema.String;
|
|
22
29
|
readonly kind: Schema.String;
|
|
@@ -36,6 +43,8 @@ export declare const protocolCatalog: import("./catalog").EventCatalog<"runtime"
|
|
|
36
43
|
'thread.cancelled': Schema.Struct<{
|
|
37
44
|
readonly threadId: Schema.String;
|
|
38
45
|
readonly reason: Schema.optional<Schema.String>;
|
|
46
|
+
/** The run's last seq before this cancellation began; a fork of the run resumes there. */
|
|
47
|
+
readonly resumeSeq: Schema.optional<Schema.Finite>;
|
|
39
48
|
}>;
|
|
40
49
|
'thread.cancel.requested': Schema.Struct<{
|
|
41
50
|
readonly threadId: Schema.String;
|
|
@@ -138,7 +147,7 @@ export declare const protocolCatalog: import("./catalog").EventCatalog<"runtime"
|
|
|
138
147
|
}>;
|
|
139
148
|
export type ProtocolEvent = EventsOfCatalog<typeof protocolCatalog>;
|
|
140
149
|
export type ProtocolEventInput = EventInputOf<typeof protocolCatalog>;
|
|
141
|
-
export declare const PROTOCOL_TYPES: readonly ['runtime.run.started', 'runtime.run.completed', 'runtime.run.cancelled', 'runtime.thread.started', 'runtime.thread.completed', 'runtime.thread.failed', 'runtime.thread.cancelled', 'runtime.thread.cancel.requested', 'runtime.wait.registered', 'runtime.wait.satisfied', 'runtime.timer.set', 'runtime.timer.fired', 'runtime.effect.failed', 'runtime.effect.attempt.started', 'runtime.effect.queued', 'runtime.effect.dispatched', 'runtime.effect.worker.started', 'runtime.effect.heartbeat', 'runtime.effect.cancel.requested', 'runtime.effect.cancelled', 'runtime.effect.completed', 'runtime.effect.timed_out', 'runtime.effect.ambiguous', 'runtime.effect.retry.scheduled', 'runtime.snapshot.taken', 'runtime.signal.received'];
|
|
150
|
+
export declare const PROTOCOL_TYPES: readonly ['runtime.run.started', 'runtime.run.completed', 'runtime.run.cancelled', 'runtime.run.forked', 'runtime.fork.elided', 'runtime.thread.started', 'runtime.thread.completed', 'runtime.thread.failed', 'runtime.thread.cancelled', 'runtime.thread.cancel.requested', 'runtime.wait.registered', 'runtime.wait.satisfied', 'runtime.timer.set', 'runtime.timer.fired', 'runtime.effect.failed', 'runtime.effect.attempt.started', 'runtime.effect.queued', 'runtime.effect.dispatched', 'runtime.effect.worker.started', 'runtime.effect.heartbeat', 'runtime.effect.cancel.requested', 'runtime.effect.cancelled', 'runtime.effect.completed', 'runtime.effect.timed_out', 'runtime.effect.ambiguous', 'runtime.effect.retry.scheduled', 'runtime.snapshot.taken', 'runtime.signal.received'];
|
|
142
151
|
export type ProtocolEventType = (typeof PROTOCOL_TYPES)[number];
|
|
143
152
|
export declare function isProtocolType(type: string): type is ProtocolEventType;
|
|
144
153
|
export type RunStartedPayload = Schema.Schema.Type<(typeof protocolCatalog.entries)['run.started']>;
|
package/dist/core/protocol.js
CHANGED
|
@@ -20,6 +20,13 @@ export const protocolCatalog = defineEventCatalog('runtime', {
|
|
|
20
20
|
'run.cancelled': Schema.Struct({
|
|
21
21
|
reason: Schema.optional(Schema.String),
|
|
22
22
|
}),
|
|
23
|
+
'run.forked': Schema.Struct({
|
|
24
|
+
sourceRunId: Schema.String,
|
|
25
|
+
sourceSeq: Schema.Finite,
|
|
26
|
+
}),
|
|
27
|
+
'fork.elided': Schema.Struct({
|
|
28
|
+
type: Schema.String,
|
|
29
|
+
}),
|
|
23
30
|
'thread.started': Schema.Struct({
|
|
24
31
|
threadId: Schema.String,
|
|
25
32
|
kind: Schema.String,
|
|
@@ -39,6 +46,8 @@ export const protocolCatalog = defineEventCatalog('runtime', {
|
|
|
39
46
|
'thread.cancelled': Schema.Struct({
|
|
40
47
|
threadId: Schema.String,
|
|
41
48
|
reason: Schema.optional(Schema.String),
|
|
49
|
+
/** The run's last seq before this cancellation began; a fork of the run resumes there. */
|
|
50
|
+
resumeSeq: Schema.optional(Schema.Finite),
|
|
42
51
|
}),
|
|
43
52
|
'thread.cancel.requested': Schema.Struct({
|
|
44
53
|
threadId: Schema.String,
|
|
@@ -138,6 +147,8 @@ export const PROTOCOL_TYPES = [
|
|
|
138
147
|
'runtime.run.started',
|
|
139
148
|
'runtime.run.completed',
|
|
140
149
|
'runtime.run.cancelled',
|
|
150
|
+
'runtime.run.forked',
|
|
151
|
+
'runtime.fork.elided',
|
|
141
152
|
'runtime.thread.started',
|
|
142
153
|
'runtime.thread.completed',
|
|
143
154
|
'runtime.thread.failed',
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { RegisteredDefinition } from './module';
|
|
3
|
+
import { type RunState } from './state';
|
|
4
|
+
/** A registered definition as `GET /definitions` publishes it. */
|
|
5
|
+
export declare const PublishedDefinitionSchema: Schema.Struct<{
|
|
6
|
+
readonly kind: Schema.String;
|
|
7
|
+
readonly name: Schema.String;
|
|
8
|
+
readonly version: Schema.String;
|
|
9
|
+
readonly description: Schema.optional<Schema.String>;
|
|
10
|
+
/** JSON Schema derived from the definition's `input`. */
|
|
11
|
+
readonly inputSchema: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
12
|
+
}>;
|
|
13
|
+
export type PublishedDefinition = Schema.Schema.Type<typeof PublishedDefinitionSchema>;
|
|
14
|
+
export declare const DefinitionCatalogSchema: Schema.Struct<{
|
|
15
|
+
readonly definitions: Schema.$Array<Schema.Struct<{
|
|
16
|
+
readonly kind: Schema.String;
|
|
17
|
+
readonly name: Schema.String;
|
|
18
|
+
readonly version: Schema.String;
|
|
19
|
+
readonly description: Schema.optional<Schema.String>;
|
|
20
|
+
/** JSON Schema derived from the definition's `input`. */
|
|
21
|
+
readonly inputSchema: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
22
|
+
}>>;
|
|
23
|
+
readonly projections: Schema.$Array<Schema.String>;
|
|
24
|
+
}>;
|
|
25
|
+
export type DefinitionCatalog = Schema.Schema.Type<typeof DefinitionCatalogSchema>;
|
|
26
|
+
export declare function publishDefinition(definition: RegisteredDefinition): PublishedDefinition;
|
|
27
|
+
/** A run as `GET /runs/summaries` lists it. */
|
|
28
|
+
export declare const RunSummarySchema: Schema.Struct<{
|
|
29
|
+
readonly runId: Schema.String;
|
|
30
|
+
readonly status: Schema.Union<readonly [Schema.Literal<"running">, Schema.Literal<"completed">, Schema.Literal<"failed">, Schema.Literal<"cancelled">]>;
|
|
31
|
+
readonly kind: Schema.NullOr<Schema.String>;
|
|
32
|
+
readonly definitionName: Schema.NullOr<Schema.String>;
|
|
33
|
+
}>;
|
|
34
|
+
export type RunSummary = Schema.Schema.Type<typeof RunSummarySchema>;
|
|
35
|
+
export declare function summarizeRun(state: RunState): RunSummary;
|
|
36
|
+
export declare const RunSummariesSchema: Schema.Struct<{
|
|
37
|
+
readonly runs: Schema.$Array<Schema.Struct<{
|
|
38
|
+
readonly runId: Schema.String;
|
|
39
|
+
readonly status: Schema.Union<readonly [Schema.Literal<"running">, Schema.Literal<"completed">, Schema.Literal<"failed">, Schema.Literal<"cancelled">]>;
|
|
40
|
+
readonly kind: Schema.NullOr<Schema.String>;
|
|
41
|
+
readonly definitionName: Schema.NullOr<Schema.String>;
|
|
42
|
+
}>>;
|
|
43
|
+
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { JsonValueSchema } from './envelope.js';
|
|
3
|
+
import { jsonSchemaOfInput } from './schema.js';
|
|
4
|
+
import { RunStateSchema } from './state.js';
|
|
5
|
+
/** A registered definition as `GET /definitions` publishes it. */
|
|
6
|
+
export const PublishedDefinitionSchema = Schema.Struct({
|
|
7
|
+
kind: Schema.String,
|
|
8
|
+
name: Schema.String,
|
|
9
|
+
version: Schema.String,
|
|
10
|
+
description: Schema.optional(Schema.String),
|
|
11
|
+
/** JSON Schema derived from the definition's `input`. */
|
|
12
|
+
inputSchema: Schema.optional(JsonValueSchema),
|
|
13
|
+
});
|
|
14
|
+
export const DefinitionCatalogSchema = Schema.Struct({
|
|
15
|
+
definitions: Schema.Array(PublishedDefinitionSchema),
|
|
16
|
+
projections: Schema.Array(Schema.String),
|
|
17
|
+
});
|
|
18
|
+
export function publishDefinition(definition) {
|
|
19
|
+
return {
|
|
20
|
+
kind: definition.kind,
|
|
21
|
+
name: definition.name,
|
|
22
|
+
version: definition.version,
|
|
23
|
+
description: definition.description,
|
|
24
|
+
inputSchema: jsonSchemaOfInput(definition.input),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** A run as `GET /runs/summaries` lists it. */
|
|
28
|
+
export const RunSummarySchema = Schema.Struct({
|
|
29
|
+
runId: Schema.String,
|
|
30
|
+
status: RunStateSchema.fields.status,
|
|
31
|
+
kind: Schema.NullOr(Schema.String),
|
|
32
|
+
definitionName: Schema.NullOr(Schema.String),
|
|
33
|
+
});
|
|
34
|
+
export function summarizeRun(state) {
|
|
35
|
+
return {
|
|
36
|
+
runId: state.runId,
|
|
37
|
+
status: state.status,
|
|
38
|
+
kind: state.startIdentity?.kind ?? null,
|
|
39
|
+
definitionName: state.startIdentity?.definitionName ?? null,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export const RunSummariesSchema = Schema.Struct({
|
|
43
|
+
runs: Schema.Array(RunSummarySchema),
|
|
44
|
+
});
|
package/dist/core/replay.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export declare const ReplayStepSchema: Schema.Struct<{
|
|
|
49
49
|
readonly idempotencyKey: Schema.NullOr<Schema.String>;
|
|
50
50
|
readonly rootThreadId: Schema.String;
|
|
51
51
|
}>>, never>;
|
|
52
|
+
readonly forkedFrom: Schema.withDecodingDefaultKey<Schema.NullOr<Schema.Struct<{
|
|
53
|
+
readonly runId: Schema.String;
|
|
54
|
+
readonly seq: Schema.Finite;
|
|
55
|
+
}>>, never>;
|
|
52
56
|
readonly threads: Schema.$Record<Schema.String, Schema.Struct<{
|
|
53
57
|
readonly threadId: Schema.String;
|
|
54
58
|
readonly kind: Schema.String;
|
|
@@ -161,6 +165,10 @@ export declare const ReplayStepSchema: Schema.Struct<{
|
|
|
161
165
|
readonly idempotencyKey: Schema.NullOr<Schema.String>;
|
|
162
166
|
readonly rootThreadId: Schema.String;
|
|
163
167
|
}>>, never>;
|
|
168
|
+
readonly forkedFrom: Schema.withDecodingDefaultKey<Schema.NullOr<Schema.Struct<{
|
|
169
|
+
readonly runId: Schema.String;
|
|
170
|
+
readonly seq: Schema.Finite;
|
|
171
|
+
}>>, never>;
|
|
164
172
|
readonly threads: Schema.$Record<Schema.String, Schema.Struct<{
|
|
165
173
|
readonly threadId: Schema.String;
|
|
166
174
|
readonly kind: Schema.String;
|
package/dist/core/schema.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
import { Effect, Schema } from 'effect';
|
|
3
|
-
import type
|
|
3
|
+
import { type JsonValue } from './types';
|
|
4
4
|
declare const InvalidInputError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
5
5
|
readonly _tag: "InvalidInputError";
|
|
6
6
|
} & Readonly<A>;
|
|
@@ -21,4 +21,6 @@ export declare function validateInput<TSchema extends SchemaInput>(schema: TSche
|
|
|
21
21
|
export declare function validateInput(schema: null | undefined, raw: JsonValue): Promise<JsonValue>;
|
|
22
22
|
export declare function validateInputSync<TSchema extends SchemaInput>(schema: TSchema, raw: JsonValue): InferSchemaOutput<TSchema>;
|
|
23
23
|
export declare function validateInputSync(schema: null | undefined, raw: JsonValue): JsonValue;
|
|
24
|
+
/** JSON Schema for a definition input, when the schema can describe itself. */
|
|
25
|
+
export declare function jsonSchemaOfInput(input: SchemaInput | null | undefined): JsonValue | undefined;
|
|
24
26
|
export {};
|
package/dist/core/schema.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Data, Effect, Predicate, Schema } from 'effect';
|
|
2
|
+
import { isJsonObject } from './types.js';
|
|
2
3
|
export class InvalidInputError extends Data.TaggedError('InvalidInputError') {
|
|
3
4
|
issues;
|
|
4
5
|
constructor(text, issues = []) {
|
|
@@ -73,3 +74,40 @@ export function validateInputSync(schema, raw) {
|
|
|
73
74
|
}
|
|
74
75
|
return result.value;
|
|
75
76
|
}
|
|
77
|
+
function isStandardJsonSchemaHolder(value) {
|
|
78
|
+
return '~standard' in value;
|
|
79
|
+
}
|
|
80
|
+
function jsonSchemaFromHolder(value) {
|
|
81
|
+
if (!isStandardJsonSchemaHolder(value)) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
const input = value['~standard'].jsonSchema?.input;
|
|
85
|
+
if (!input) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const schema = input({ target: 'draft-2020-12' });
|
|
90
|
+
return isJsonObject(schema) ? schema : undefined;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** JSON Schema for a definition input, when the schema can describe itself. */
|
|
97
|
+
export function jsonSchemaOfInput(input) {
|
|
98
|
+
if (!input) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
if (isStandardSchema(input)) {
|
|
102
|
+
return jsonSchemaFromHolder(input);
|
|
103
|
+
}
|
|
104
|
+
if (!Schema.isSchema(input)) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
return jsonSchemaFromHolder(Schema.toStandardJSONSchemaV1(input));
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context, Effect, Option } from 'effect';
|
|
2
2
|
import { SnapshotPayloadTooLargeError } from './limits';
|
|
3
|
+
import type { RunSummary } from './published';
|
|
3
4
|
import type { RunState } from './state';
|
|
4
5
|
import type { EventStore } from './store';
|
|
5
6
|
/**
|
|
@@ -29,6 +30,9 @@ export interface SnapshotStore {
|
|
|
29
30
|
readonly save: (snapshot: RunSnapshot) => Effect.Effect<void, SnapshotStoreError | SnapshotPayloadTooLargeError>;
|
|
30
31
|
readonly listCursors?: (runId: string) => Effect.Effect<number[], SnapshotStoreError>;
|
|
31
32
|
readonly prune?: (runId: string, keepLatest: number) => Effect.Effect<void, SnapshotStoreError>;
|
|
33
|
+
/** Upsert the list-row for a run. Written where the cursor already advanced. */
|
|
34
|
+
readonly saveHeader: (header: RunSummary) => Effect.Effect<void, SnapshotStoreError>;
|
|
35
|
+
readonly listHeaders: Effect.Effect<readonly RunSummary[], SnapshotStoreError>;
|
|
32
36
|
}
|
|
33
37
|
declare const SnapshotStoreTag_base: Context.ServiceClass<SnapshotStoreTag, "looms/SnapshotStore", SnapshotStore>;
|
|
34
38
|
export declare class SnapshotStoreTag extends SnapshotStoreTag_base {
|
|
@@ -21,6 +21,7 @@ export function snapshotStoreOf(store) {
|
|
|
21
21
|
}
|
|
22
22
|
export const makeMemorySnapshotStore = Effect.gen(function* () {
|
|
23
23
|
const snapshots = yield* Ref.make(new Map());
|
|
24
|
+
const headers = yield* Ref.make(new Map());
|
|
24
25
|
const service = {
|
|
25
26
|
loadLatest: (runId) => Effect.gen(function* () {
|
|
26
27
|
const map = yield* Ref.get(snapshots);
|
|
@@ -54,6 +55,12 @@ export const makeMemorySnapshotStore = Effect.gen(function* () {
|
|
|
54
55
|
next.set(runId, list.slice(list.length - keepLatest));
|
|
55
56
|
return next;
|
|
56
57
|
}),
|
|
58
|
+
saveHeader: (header) => Ref.update(headers, (map) => {
|
|
59
|
+
const next = new Map(map);
|
|
60
|
+
next.set(header.runId, header);
|
|
61
|
+
return next;
|
|
62
|
+
}),
|
|
63
|
+
listHeaders: Ref.get(headers).pipe(Effect.map((map) => [...map.values()])),
|
|
57
64
|
};
|
|
58
65
|
return service;
|
|
59
66
|
});
|
package/dist/core/snapshots.js
CHANGED
|
@@ -21,14 +21,7 @@ export function shouldTakeSnapshot(events, every = DEFAULT_SNAPSHOT_EVERY) {
|
|
|
21
21
|
if (durable.length === 0) {
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
for (let i = events.length - 1; i >= 0; i--) {
|
|
26
|
-
const candidate = events[i];
|
|
27
|
-
if (candidate?.type === 'runtime.snapshot.taken') {
|
|
28
|
-
lastSnap = candidate;
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
24
|
+
const lastSnap = events.findLast((e) => e.type === 'runtime.snapshot.taken');
|
|
32
25
|
const since = lastSnap ? durable.filter((e) => e.seq > lastSnap.seq).length : durable.length;
|
|
33
26
|
return since >= every;
|
|
34
27
|
}
|
|
@@ -66,17 +59,10 @@ export function buildSnapshotMarker(runId, cursor, stateHash, options) {
|
|
|
66
59
|
}
|
|
67
60
|
export function foldFromSnapshots(events, registry, options) {
|
|
68
61
|
const runId = options?.runId ?? events[0]?.runId ?? 'unknown';
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (events[i]?.type === 'runtime.snapshot.taken') {
|
|
72
|
-
latestSnapIdx = i;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (latestSnapIdx < 0) {
|
|
62
|
+
const snap = events.findLast((e) => e.type === 'runtime.snapshot.taken');
|
|
63
|
+
if (!snap) {
|
|
77
64
|
return foldRun(events, registry, { runId });
|
|
78
65
|
}
|
|
79
|
-
const snap = events[latestSnapIdx];
|
|
80
66
|
const decoded = Schema.decodeUnknownSync(SnapshotTakenPayloadSchema)(snap.payload);
|
|
81
67
|
if (decoded.state !== undefined) {
|
|
82
68
|
const trailing = events.filter((e) => e.seq > decoded.seq);
|
|
@@ -35,6 +35,13 @@ CREATE TABLE IF NOT EXISTS looms_commands (
|
|
|
35
35
|
committed_tail INTEGER NOT NULL,
|
|
36
36
|
PRIMARY KEY (run_id, idempotency_key)
|
|
37
37
|
);
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS looms_run_headers (
|
|
40
|
+
run_id TEXT PRIMARY KEY,
|
|
41
|
+
status TEXT NOT NULL,
|
|
42
|
+
kind TEXT,
|
|
43
|
+
definition_name TEXT
|
|
44
|
+
);
|
|
38
45
|
`.trim();
|
|
39
46
|
const StreamRowSchema = Schema.Struct({
|
|
40
47
|
run_id: Schema.String,
|
|
@@ -124,6 +131,21 @@ function toSnapshot(row) {
|
|
|
124
131
|
state,
|
|
125
132
|
};
|
|
126
133
|
}
|
|
134
|
+
const HeaderRowSchema = Schema.Struct({
|
|
135
|
+
run_id: Schema.String,
|
|
136
|
+
status: RunStateSchema.fields.status,
|
|
137
|
+
kind: Schema.NullOr(Schema.String),
|
|
138
|
+
definition_name: Schema.NullOr(Schema.String),
|
|
139
|
+
});
|
|
140
|
+
const decodeHeaderRow = Schema.decodeUnknownSync(HeaderRowSchema);
|
|
141
|
+
function headerFromRow(row) {
|
|
142
|
+
return {
|
|
143
|
+
runId: row.run_id,
|
|
144
|
+
status: row.status,
|
|
145
|
+
kind: row.kind,
|
|
146
|
+
definitionName: row.definition_name,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
127
149
|
function createSqliteSnapshotStore(exec) {
|
|
128
150
|
return {
|
|
129
151
|
loadLatest: (runId) => Effect.try({
|
|
@@ -175,6 +197,23 @@ function createSqliteSnapshotStore(exec) {
|
|
|
175
197
|
},
|
|
176
198
|
catch: (cause) => new SnapshotStoreError(`Failed to prune snapshots for ${runId}`, cause),
|
|
177
199
|
}),
|
|
200
|
+
saveHeader: (header) => Effect.try({
|
|
201
|
+
try: () => {
|
|
202
|
+
exec.run(`INSERT INTO looms_run_headers (run_id, status, kind, definition_name)
|
|
203
|
+
VALUES (?, ?, ?, ?)
|
|
204
|
+
ON CONFLICT (run_id) DO UPDATE SET
|
|
205
|
+
status = excluded.status,
|
|
206
|
+
kind = excluded.kind,
|
|
207
|
+
definition_name = excluded.definition_name`, [header.runId, header.status, header.kind, header.definitionName]);
|
|
208
|
+
},
|
|
209
|
+
catch: (cause) => new SnapshotStoreError(`Failed to save run header for ${header.runId}`, cause),
|
|
210
|
+
}),
|
|
211
|
+
listHeaders: Effect.try({
|
|
212
|
+
try: () => exec
|
|
213
|
+
.rows('SELECT run_id, status, kind, definition_name FROM looms_run_headers')
|
|
214
|
+
.map((row) => headerFromRow(decodeHeaderRow(row))),
|
|
215
|
+
catch: (cause) => new SnapshotStoreError('Failed to list run headers', cause),
|
|
216
|
+
}),
|
|
178
217
|
};
|
|
179
218
|
}
|
|
180
219
|
function createSqliteEventStore(exec) {
|
package/dist/core/state.d.ts
CHANGED
|
@@ -46,11 +46,17 @@ export interface RunStartIdentity {
|
|
|
46
46
|
idempotencyKey: string | null;
|
|
47
47
|
rootThreadId: string;
|
|
48
48
|
}
|
|
49
|
+
/** The run and last sequence a fork copied its history from. */
|
|
50
|
+
export interface RunForkSource {
|
|
51
|
+
runId: string;
|
|
52
|
+
seq: number;
|
|
53
|
+
}
|
|
49
54
|
export interface RunState {
|
|
50
55
|
runId: string;
|
|
51
56
|
status: RunStatus;
|
|
52
57
|
rootThreadId: string | null;
|
|
53
58
|
startIdentity: RunStartIdentity | null;
|
|
59
|
+
forkedFrom: RunForkSource | null;
|
|
54
60
|
threads: {
|
|
55
61
|
[threadId: string]: ThreadRecord;
|
|
56
62
|
};
|
|
@@ -77,6 +83,10 @@ export declare const RunStateSchema: Schema.Struct<{
|
|
|
77
83
|
readonly idempotencyKey: Schema.NullOr<Schema.String>;
|
|
78
84
|
readonly rootThreadId: Schema.String;
|
|
79
85
|
}>>, never>;
|
|
86
|
+
readonly forkedFrom: Schema.withDecodingDefaultKey<Schema.NullOr<Schema.Struct<{
|
|
87
|
+
readonly runId: Schema.String;
|
|
88
|
+
readonly seq: Schema.Finite;
|
|
89
|
+
}>>, never>;
|
|
80
90
|
readonly threads: Schema.$Record<Schema.String, Schema.Struct<{
|
|
81
91
|
readonly threadId: Schema.String;
|
|
82
92
|
readonly kind: Schema.String;
|
|
@@ -178,4 +188,8 @@ export declare const RunStateSchema: Schema.Struct<{
|
|
|
178
188
|
}>;
|
|
179
189
|
export declare function emptyRunState(runId: string): RunState;
|
|
180
190
|
export declare function isRunTerminal(state: RunState): boolean;
|
|
191
|
+
/** Every thread below `threadId`, in the order they were started. */
|
|
192
|
+
export declare function descendantThreadIds(state: RunState, threadId: string): string[];
|
|
193
|
+
/** The status a `runtime.run.completed` payload ends the run with: an error means it failed. */
|
|
194
|
+
export declare function completedRunStatus(payload: JsonValue): 'completed' | 'failed';
|
|
181
195
|
export declare function isRunParked(state: RunState): boolean;
|