@swirls/looms 0.0.1
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/LICENSE +190 -0
- package/README.md +9 -0
- package/dist/actor/cell.d.ts +27 -0
- package/dist/actor/cell.js +41 -0
- package/dist/actor/host.d.ts +27 -0
- package/dist/actor/host.js +82 -0
- package/dist/actor/index.d.ts +3 -0
- package/dist/actor/index.js +3 -0
- package/dist/actor/routing.d.ts +15 -0
- package/dist/actor/routing.js +80 -0
- package/dist/agent/definitions-store.d.ts +11 -0
- package/dist/agent/definitions-store.js +10 -0
- package/dist/agent/definitions.d.ts +152 -0
- package/dist/agent/definitions.js +74 -0
- package/dist/agent/effects.d.ts +425 -0
- package/dist/agent/effects.js +318 -0
- package/dist/agent/events.d.ts +170 -0
- package/dist/agent/events.js +61 -0
- package/dist/agent/index.d.ts +13 -0
- package/dist/agent/index.js +13 -0
- package/dist/agent/llm.d.ts +35 -0
- package/dist/agent/llm.js +48 -0
- package/dist/agent/module.d.ts +1037 -0
- package/dist/agent/module.js +27 -0
- package/dist/agent/projections.d.ts +17 -0
- package/dist/agent/projections.js +81 -0
- package/dist/agent/scope.d.ts +171 -0
- package/dist/agent/scope.js +7 -0
- package/dist/agent/signals.d.ts +17 -0
- package/dist/agent/signals.js +19 -0
- package/dist/agent/stop-when.d.ts +12 -0
- package/dist/agent/stop-when.js +6 -0
- package/dist/agent/threads.d.ts +34 -0
- package/dist/agent/threads.js +407 -0
- package/dist/agent/tool-schema.d.ts +21 -0
- package/dist/agent/tool-schema.js +67 -0
- package/dist/agent/types.d.ts +87 -0
- package/dist/agent/types.js +32 -0
- package/dist/ai-vercel/index.d.ts +2 -0
- package/dist/ai-vercel/index.js +2 -0
- package/dist/ai-vercel/messages.d.ts +9 -0
- package/dist/ai-vercel/messages.js +68 -0
- package/dist/ai-vercel/vercel-llm.d.ts +8 -0
- package/dist/ai-vercel/vercel-llm.js +98 -0
- package/dist/approval/effects.d.ts +133 -0
- package/dist/approval/effects.js +13 -0
- package/dist/approval/events.d.ts +52 -0
- package/dist/approval/events.js +28 -0
- package/dist/approval/index.d.ts +6 -0
- package/dist/approval/index.js +6 -0
- package/dist/approval/module.d.ts +342 -0
- package/dist/approval/module.js +10 -0
- package/dist/approval/projections.d.ts +42 -0
- package/dist/approval/projections.js +57 -0
- package/dist/approval/scope.d.ts +45 -0
- package/dist/approval/scope.js +7 -0
- package/dist/approval/signals.d.ts +19 -0
- package/dist/approval/signals.js +34 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +6 -0
- package/dist/cli/commands.d.ts +13 -0
- package/dist/cli/commands.js +133 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/client/client.d.ts +69 -0
- package/dist/client/client.js +179 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -0
- package/dist/client/sse.d.ts +10 -0
- package/dist/client/sse.js +103 -0
- package/dist/cloudflare/alarm-scheduler.d.ts +9 -0
- package/dist/cloudflare/alarm-scheduler.js +13 -0
- package/dist/cloudflare/durable-object.d.ts +24 -0
- package/dist/cloudflare/durable-object.js +43 -0
- package/dist/cloudflare/event-store.d.ts +10 -0
- package/dist/cloudflare/event-store.js +18 -0
- package/dist/cloudflare/index.d.ts +4 -0
- package/dist/cloudflare/index.js +4 -0
- package/dist/cloudflare/router.d.ts +12 -0
- package/dist/cloudflare/router.js +26 -0
- package/dist/core/bun-sqlite.d.ts +13 -0
- package/dist/core/bun-sqlite.js +19 -0
- package/dist/core/catalog.d.ts +49 -0
- package/dist/core/catalog.js +39 -0
- package/dist/core/coalesce.d.ts +21 -0
- package/dist/core/coalesce.js +178 -0
- package/dist/core/compose.d.ts +59 -0
- package/dist/core/compose.js +97 -0
- package/dist/core/cursor.d.ts +16 -0
- package/dist/core/cursor.js +46 -0
- package/dist/core/effects.d.ts +110 -0
- package/dist/core/effects.js +99 -0
- package/dist/core/envelope.d.ts +79 -0
- package/dist/core/envelope.js +68 -0
- package/dist/core/event-codec.d.ts +8 -0
- package/dist/core/event-codec.js +82 -0
- package/dist/core/event-index.d.ts +28 -0
- package/dist/core/event-index.js +86 -0
- package/dist/core/fold.d.ts +19 -0
- package/dist/core/fold.js +362 -0
- package/dist/core/ids.d.ts +11 -0
- package/dist/core/ids.js +47 -0
- package/dist/core/index.d.ts +26 -0
- package/dist/core/index.js +25 -0
- package/dist/core/match.d.ts +8 -0
- package/dist/core/match.js +58 -0
- package/dist/core/module-scope.d.ts +99 -0
- package/dist/core/module-scope.js +72 -0
- package/dist/core/module.d.ts +74 -0
- package/dist/core/module.js +3 -0
- package/dist/core/projection.d.ts +83 -0
- package/dist/core/projection.js +241 -0
- package/dist/core/protocol.d.ts +99 -0
- package/dist/core/protocol.js +101 -0
- package/dist/core/replay.d.ts +13 -0
- package/dist/core/replay.js +34 -0
- package/dist/core/schema.d.ts +35 -0
- package/dist/core/schema.js +130 -0
- package/dist/core/serialize.d.ts +14 -0
- package/dist/core/serialize.js +42 -0
- package/dist/core/snapshot-store.d.ts +39 -0
- package/dist/core/snapshot-store.js +53 -0
- package/dist/core/snapshots.d.ts +26 -0
- package/dist/core/snapshots.js +100 -0
- package/dist/core/sqlite-store.d.ts +31 -0
- package/dist/core/sqlite-store.js +328 -0
- package/dist/core/state.d.ts +43 -0
- package/dist/core/state.js +31 -0
- package/dist/core/store.d.ts +146 -0
- package/dist/core/store.js +229 -0
- package/dist/core/thread.d.ts +41 -0
- package/dist/core/thread.js +13 -0
- package/dist/core/types.d.ts +27 -0
- package/dist/core/types.js +42 -0
- package/dist/core/validate-event.d.ts +19 -0
- package/dist/core/validate-event.js +42 -0
- package/dist/debugger/components/debugger-split.d.ts +23 -0
- package/dist/debugger/components/debugger-split.js +25 -0
- package/dist/debugger/components/event-inspector.d.ts +5 -0
- package/dist/debugger/components/event-inspector.js +38 -0
- package/dist/debugger/components/event-stream.d.ts +13 -0
- package/dist/debugger/components/event-stream.js +119 -0
- package/dist/debugger/components/json-view.d.ts +5 -0
- package/dist/debugger/components/json-view.js +5 -0
- package/dist/debugger/components/run-tree.d.ts +14 -0
- package/dist/debugger/components/run-tree.js +38 -0
- package/dist/debugger/components/status-dot.d.ts +3 -0
- package/dist/debugger/components/status-dot.js +5 -0
- package/dist/debugger/contracts.d.ts +22 -0
- package/dist/debugger/contracts.js +1 -0
- package/dist/debugger/events/catalog.d.ts +6 -0
- package/dist/debugger/events/catalog.js +27 -0
- package/dist/debugger/events/domain-summarize.d.ts +3 -0
- package/dist/debugger/events/domain-summarize.js +107 -0
- package/dist/debugger/events/family.d.ts +8 -0
- package/dist/debugger/events/family.js +44 -0
- package/dist/debugger/events/protocol-summarize.d.ts +3 -0
- package/dist/debugger/events/protocol-summarize.js +70 -0
- package/dist/debugger/index.d.ts +17 -0
- package/dist/debugger/index.js +16 -0
- package/dist/debugger/lib/cn.d.ts +5 -0
- package/dist/debugger/lib/cn.js +17 -0
- package/dist/debugger/lib/event-counts.d.ts +5 -0
- package/dist/debugger/lib/event-counts.js +23 -0
- package/dist/debugger/lib/event-meta.d.ts +8 -0
- package/dist/debugger/lib/event-meta.js +20 -0
- package/dist/debugger/lib/project-run.d.ts +9 -0
- package/dist/debugger/lib/project-run.js +14 -0
- package/dist/debugger/lib/status.d.ts +3 -0
- package/dist/debugger/lib/status.js +31 -0
- package/dist/debugger/lib/visible-events.d.ts +8 -0
- package/dist/debugger/lib/visible-events.js +21 -0
- package/dist/debugger/styles.css +71 -0
- package/dist/debugger/ui/checkbox.d.ts +4 -0
- package/dist/debugger/ui/checkbox.js +9 -0
- package/dist/debugger/ui/input.d.ts +3 -0
- package/dist/debugger/ui/input.js +7 -0
- package/dist/debugger/ui/scroll-area.d.ts +9 -0
- package/dist/debugger/ui/scroll-area.js +10 -0
- package/dist/debugger/ui/tabs.d.ts +7 -0
- package/dist/debugger/ui/tabs.js +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/projectors/index-model.d.ts +56 -0
- package/dist/projectors/index-model.js +117 -0
- package/dist/projectors/index.d.ts +4 -0
- package/dist/projectors/index.js +3 -0
- package/dist/projectors/memory.d.ts +5 -0
- package/dist/projectors/memory.js +77 -0
- package/dist/projectors/postgres.d.ts +9 -0
- package/dist/projectors/postgres.js +108 -0
- package/dist/projectors/projector.d.ts +11 -0
- package/dist/projectors/projector.js +1 -0
- package/dist/projectors/schema.d.ts +5 -0
- package/dist/projectors/schema.js +43 -0
- package/dist/projectors/sqlite.d.ts +9 -0
- package/dist/projectors/sqlite.js +103 -0
- package/dist/projectors/with-projectors.d.ts +17 -0
- package/dist/projectors/with-projectors.js +55 -0
- package/dist/react/derived.d.ts +19 -0
- package/dist/react/derived.js +50 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +5 -0
- package/dist/react/materialize.d.ts +3 -0
- package/dist/react/materialize.js +86 -0
- package/dist/react/notify.d.ts +18 -0
- package/dist/react/notify.js +80 -0
- package/dist/react/react.d.ts +61 -0
- package/dist/react/react.js +187 -0
- package/dist/react/register.d.ts +31 -0
- package/dist/react/register.js +1 -0
- package/dist/react/sse.d.ts +1 -0
- package/dist/react/sse.js +1 -0
- package/dist/react/store.d.ts +56 -0
- package/dist/react/store.js +340 -0
- package/dist/react/tables.d.ts +21 -0
- package/dist/react/tables.js +6 -0
- package/dist/runtime/dispatch-effect.d.ts +3 -0
- package/dist/runtime/dispatch-effect.js +113 -0
- package/dist/runtime/events-api.d.ts +4 -0
- package/dist/runtime/events-api.js +84 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/looms.d.ts +74 -0
- package/dist/runtime/looms.js +109 -0
- package/dist/runtime/runtime-helpers.d.ts +51 -0
- package/dist/runtime/runtime-helpers.js +230 -0
- package/dist/runtime/runtime.d.ts +91 -0
- package/dist/runtime/runtime.js +531 -0
- package/dist/runtime/server.d.ts +17 -0
- package/dist/runtime/server.js +226 -0
- package/dist/runtime/sse.d.ts +15 -0
- package/dist/runtime/sse.js +96 -0
- package/dist/runtime/wake-scheduler.d.ts +8 -0
- package/dist/runtime/wake-scheduler.js +38 -0
- package/dist/s2/config.d.ts +27 -0
- package/dist/s2/config.js +54 -0
- package/dist/s2/index.d.ts +6 -0
- package/dist/s2/index.js +6 -0
- package/dist/s2/lite.d.ts +26 -0
- package/dist/s2/lite.js +150 -0
- package/dist/s2/projector.d.ts +8 -0
- package/dist/s2/projector.js +28 -0
- package/dist/s2/serialize.d.ts +1 -0
- package/dist/s2/serialize.js +1 -0
- package/dist/s2/snapshot-store.d.ts +17 -0
- package/dist/s2/snapshot-store.js +320 -0
- package/dist/s2/store.d.ts +35 -0
- package/dist/s2/store.js +337 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.js +63 -0
- package/dist/workflow/definitions-store.d.ts +11 -0
- package/dist/workflow/definitions-store.js +8 -0
- package/dist/workflow/definitions.d.ts +82 -0
- package/dist/workflow/definitions.js +32 -0
- package/dist/workflow/effects.d.ts +305 -0
- package/dist/workflow/effects.js +208 -0
- package/dist/workflow/events.d.ts +60 -0
- package/dist/workflow/events.js +39 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/module.d.ts +926 -0
- package/dist/workflow/module.js +19 -0
- package/dist/workflow/projections.d.ts +30 -0
- package/dist/workflow/projections.js +43 -0
- package/dist/workflow/scope.d.ts +61 -0
- package/dist/workflow/scope.js +7 -0
- package/dist/workflow/threads.d.ts +171 -0
- package/dist/workflow/threads.js +287 -0
- package/package.json +183 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import type { EventInput } from './envelope';
|
|
3
|
+
import { type InferDefinedSchema, type SchemaInput } from './schema';
|
|
4
|
+
import { type JsonValue } from './types';
|
|
5
|
+
export type WaitOnEvent = {
|
|
6
|
+
type: string | readonly string[];
|
|
7
|
+
match?: JsonValue;
|
|
8
|
+
};
|
|
9
|
+
export type WaitOnTimer = {
|
|
10
|
+
timerAt: number;
|
|
11
|
+
};
|
|
12
|
+
export type WaitCondition = WaitOnEvent | WaitOnTimer;
|
|
13
|
+
export type SpawnEffect = {
|
|
14
|
+
type: 'runtime.spawn';
|
|
15
|
+
childThreadId: string;
|
|
16
|
+
kind: string;
|
|
17
|
+
definitionName: string;
|
|
18
|
+
input: JsonValue;
|
|
19
|
+
};
|
|
20
|
+
export type WaitEffect = {
|
|
21
|
+
type: 'runtime.wait';
|
|
22
|
+
waitId: string;
|
|
23
|
+
on: WaitCondition;
|
|
24
|
+
tag?: JsonValue;
|
|
25
|
+
};
|
|
26
|
+
export type EmitEffect<E extends EventInput = EventInput> = {
|
|
27
|
+
type: 'runtime.emit';
|
|
28
|
+
event: E;
|
|
29
|
+
};
|
|
30
|
+
export type CompleteEffect = {
|
|
31
|
+
type: 'runtime.complete';
|
|
32
|
+
output: JsonValue;
|
|
33
|
+
};
|
|
34
|
+
export type FailEffect = {
|
|
35
|
+
type: 'runtime.fail';
|
|
36
|
+
error: string;
|
|
37
|
+
};
|
|
38
|
+
export type CancelEffect = {
|
|
39
|
+
type: 'runtime.cancel';
|
|
40
|
+
threadId: string;
|
|
41
|
+
};
|
|
42
|
+
export interface RetryPolicy {
|
|
43
|
+
readonly maxAttempts: number;
|
|
44
|
+
readonly backoffMs?: number;
|
|
45
|
+
readonly maxBackoffMs?: number;
|
|
46
|
+
}
|
|
47
|
+
export type InvokeEffect = {
|
|
48
|
+
type: string;
|
|
49
|
+
input: JsonValue;
|
|
50
|
+
tag?: string;
|
|
51
|
+
};
|
|
52
|
+
export type PrimitiveEffect = SpawnEffect | WaitEffect | EmitEffect | CompleteEffect | FailEffect | CancelEffect;
|
|
53
|
+
export type RuntimeEffect = PrimitiveEffect | InvokeEffect;
|
|
54
|
+
export declare function isPrimitiveEffect(effect: RuntimeEffect): effect is PrimitiveEffect;
|
|
55
|
+
export declare function isWaitOnTimer(on: WaitCondition): on is WaitOnTimer;
|
|
56
|
+
export declare function isWaitOnEvent(on: WaitCondition): on is WaitOnEvent;
|
|
57
|
+
export declare const WITHDRAWN_ERROR_PREFIX = "withdrawn:";
|
|
58
|
+
export declare function withdrawnError(siblingEffectId: string): string;
|
|
59
|
+
export declare function isWithdrawnError(error: string): boolean;
|
|
60
|
+
export interface EffectContext {
|
|
61
|
+
readonly effectId: string;
|
|
62
|
+
readonly runId: string;
|
|
63
|
+
readonly threadId: string;
|
|
64
|
+
readonly causingEventId: string;
|
|
65
|
+
emit(event: EventInput): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
export interface ScopedEffectContext<E extends EventInput = EventInput> {
|
|
68
|
+
readonly effectId: string;
|
|
69
|
+
readonly runId: string;
|
|
70
|
+
readonly threadId: string;
|
|
71
|
+
readonly causingEventId: string;
|
|
72
|
+
emit(event: E): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
export type EffectInputSchema = SchemaInput;
|
|
75
|
+
export type EffectHandlerResult<R = never, E extends EventInput = EventInput> = ReadonlyArray<E> | Promise<ReadonlyArray<E>> | Effect.Effect<ReadonlyArray<E>, Error, R>;
|
|
76
|
+
export interface EffectDefinition<TInput = JsonValue, R = any, E extends EventInput = EventInput> {
|
|
77
|
+
readonly type: string;
|
|
78
|
+
readonly input?: SchemaInput;
|
|
79
|
+
readonly retry?: RetryPolicy;
|
|
80
|
+
execute(input: TInput, ctx: EffectContext): Effect.Effect<ReadonlyArray<E>, Error, R>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Define an effect handler. A run's actor cell executes each effect at most
|
|
84
|
+
* once while it is alive. If the cell dies mid-execution, the replacement
|
|
85
|
+
* actor may re-run the handler (at-least-once across failover). Keep handlers
|
|
86
|
+
* idempotent when they have external side effects.
|
|
87
|
+
*/
|
|
88
|
+
export declare function defineEffect<TSchema = undefined, TInput = InferDefinedSchema<TSchema>, R = never, E extends EventInput = EventInput>(def: {
|
|
89
|
+
type: string;
|
|
90
|
+
input?: TSchema;
|
|
91
|
+
retry?: RetryPolicy;
|
|
92
|
+
execute: (input: TInput, ctx: EffectContext) => EffectHandlerResult<R, E>;
|
|
93
|
+
}): EffectDefinition<TInput, R, E>;
|
|
94
|
+
export declare function spawn(args: {
|
|
95
|
+
childThreadId?: string;
|
|
96
|
+
kind: string;
|
|
97
|
+
definitionName: string;
|
|
98
|
+
input: JsonValue;
|
|
99
|
+
}): SpawnEffect;
|
|
100
|
+
export declare function wait(args: {
|
|
101
|
+
waitId: string;
|
|
102
|
+
on: WaitCondition;
|
|
103
|
+
tag?: JsonValue;
|
|
104
|
+
}): WaitEffect;
|
|
105
|
+
export declare function emit<E extends EventInput = EventInput>(event: E): EmitEffect<E>;
|
|
106
|
+
export declare function complete(output: JsonValue): CompleteEffect;
|
|
107
|
+
export declare function fail(error: string): FailEffect;
|
|
108
|
+
export declare function cancel(threadId: string): CancelEffect;
|
|
109
|
+
export declare function invoke<TInput>(definition: EffectDefinition<TInput, any, any>, input: TInput, tag?: string): InvokeEffect;
|
|
110
|
+
export declare function invoke(type: string, input: JsonValue, tag?: string): InvokeEffect;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Effect, Predicate } from 'effect';
|
|
2
|
+
import { validateInputEffect } from './schema.js';
|
|
3
|
+
import { asJson } from './types.js';
|
|
4
|
+
export function isPrimitiveEffect(effect) {
|
|
5
|
+
switch (effect.type) {
|
|
6
|
+
case 'runtime.spawn':
|
|
7
|
+
case 'runtime.wait':
|
|
8
|
+
case 'runtime.emit':
|
|
9
|
+
case 'runtime.complete':
|
|
10
|
+
case 'runtime.fail':
|
|
11
|
+
case 'runtime.cancel':
|
|
12
|
+
return true;
|
|
13
|
+
default:
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function isWaitOnTimer(on) {
|
|
18
|
+
return 'timerAt' in on;
|
|
19
|
+
}
|
|
20
|
+
export function isWaitOnEvent(on) {
|
|
21
|
+
return 'type' in on;
|
|
22
|
+
}
|
|
23
|
+
export const WITHDRAWN_ERROR_PREFIX = 'withdrawn:';
|
|
24
|
+
export function withdrawnError(siblingEffectId) {
|
|
25
|
+
return `${WITHDRAWN_ERROR_PREFIX} sibling effect ${siblingEffectId} failed`;
|
|
26
|
+
}
|
|
27
|
+
export function isWithdrawnError(error) {
|
|
28
|
+
return error.startsWith(WITHDRAWN_ERROR_PREFIX);
|
|
29
|
+
}
|
|
30
|
+
function liftHandlerResult(result) {
|
|
31
|
+
if (Effect.isEffect(result)) {
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
if (result instanceof Promise) {
|
|
35
|
+
return Effect.tryPromise({
|
|
36
|
+
try: () => result,
|
|
37
|
+
catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return Effect.succeed(result);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Define an effect handler. A run's actor cell executes each effect at most
|
|
44
|
+
* once while it is alive. If the cell dies mid-execution, the replacement
|
|
45
|
+
* actor may re-run the handler (at-least-once across failover). Keep handlers
|
|
46
|
+
* idempotent when they have external side effects.
|
|
47
|
+
*/
|
|
48
|
+
export function defineEffect(def) {
|
|
49
|
+
const schema = def.input;
|
|
50
|
+
// SAFETY: execute validates raw JsonValue against the optional schema before the typed handler.
|
|
51
|
+
return {
|
|
52
|
+
type: def.type,
|
|
53
|
+
input: schema,
|
|
54
|
+
retry: def.retry,
|
|
55
|
+
execute: (raw, ctx) => Effect.gen(function* () {
|
|
56
|
+
const input = schema ? yield* validateInputEffect(schema, raw) : raw;
|
|
57
|
+
// SAFETY: input has been validated against schema or is unconstrained raw input
|
|
58
|
+
return yield* liftHandlerResult(def.execute(input, ctx));
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function spawn(args) {
|
|
63
|
+
const childThreadId = args.childThreadId ?? '';
|
|
64
|
+
return {
|
|
65
|
+
type: 'runtime.spawn',
|
|
66
|
+
childThreadId,
|
|
67
|
+
kind: args.kind,
|
|
68
|
+
definitionName: args.definitionName,
|
|
69
|
+
input: args.input,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function wait(args) {
|
|
73
|
+
const effect = {
|
|
74
|
+
type: 'runtime.wait',
|
|
75
|
+
waitId: args.waitId,
|
|
76
|
+
on: args.on,
|
|
77
|
+
};
|
|
78
|
+
if (args.tag !== undefined) {
|
|
79
|
+
effect.tag = args.tag;
|
|
80
|
+
}
|
|
81
|
+
return effect;
|
|
82
|
+
}
|
|
83
|
+
export function emit(event) {
|
|
84
|
+
return { type: 'runtime.emit', event };
|
|
85
|
+
}
|
|
86
|
+
export function complete(output) {
|
|
87
|
+
return { type: 'runtime.complete', output };
|
|
88
|
+
}
|
|
89
|
+
export function fail(error) {
|
|
90
|
+
return { type: 'runtime.fail', error };
|
|
91
|
+
}
|
|
92
|
+
export function cancel(threadId) {
|
|
93
|
+
return { type: 'runtime.cancel', threadId };
|
|
94
|
+
}
|
|
95
|
+
export function invoke(typeOrDef, input, tag) {
|
|
96
|
+
const type = Predicate.isString(typeOrDef) ? typeOrDef : typeOrDef.type;
|
|
97
|
+
const jsonInput = asJson(input);
|
|
98
|
+
return tag !== undefined ? { type, input: jsonInput, tag } : { type, input: jsonInput };
|
|
99
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { JsonValue } from './types';
|
|
3
|
+
export declare const JsonValueSchema: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
4
|
+
export declare const EventOriginSchema: Schema.Union<readonly [Schema.Struct<{
|
|
5
|
+
readonly type: Schema.Literal<"thread">;
|
|
6
|
+
readonly threadId: Schema.String;
|
|
7
|
+
}>, Schema.Struct<{
|
|
8
|
+
readonly type: Schema.Literal<"external">;
|
|
9
|
+
readonly actorId: Schema.optional<Schema.String>;
|
|
10
|
+
}>, Schema.Struct<{
|
|
11
|
+
readonly type: Schema.Literal<"system">;
|
|
12
|
+
}>]>;
|
|
13
|
+
export type EventOrigin = Schema.Schema.Type<typeof EventOriginSchema>;
|
|
14
|
+
export declare const EventEnvelopeSchema: Schema.Struct<{
|
|
15
|
+
readonly id: Schema.String;
|
|
16
|
+
readonly runId: Schema.String;
|
|
17
|
+
readonly seq: Schema.Number;
|
|
18
|
+
readonly ts: Schema.Number;
|
|
19
|
+
readonly type: Schema.String;
|
|
20
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
21
|
+
readonly threadId: Schema.NullOr<Schema.String>;
|
|
22
|
+
readonly parentThreadId: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
23
|
+
readonly causationId: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
24
|
+
readonly correlationId: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
25
|
+
readonly effectId: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
26
|
+
readonly origin: Schema.Union<readonly [Schema.Struct<{
|
|
27
|
+
readonly type: Schema.Literal<"thread">;
|
|
28
|
+
readonly threadId: Schema.String;
|
|
29
|
+
}>, Schema.Struct<{
|
|
30
|
+
readonly type: Schema.Literal<"external">;
|
|
31
|
+
readonly actorId: Schema.optional<Schema.String>;
|
|
32
|
+
}>, Schema.Struct<{
|
|
33
|
+
readonly type: Schema.Literal<"system">;
|
|
34
|
+
}>]>;
|
|
35
|
+
readonly ephemeral: Schema.optional<Schema.Boolean>;
|
|
36
|
+
readonly idempotencyKey: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
37
|
+
}>;
|
|
38
|
+
export type EventEnvelope<TType extends string = string, TPayload = JsonValue> = {
|
|
39
|
+
id: string;
|
|
40
|
+
runId: string;
|
|
41
|
+
seq: number;
|
|
42
|
+
ts: number;
|
|
43
|
+
type: TType;
|
|
44
|
+
payload: TPayload;
|
|
45
|
+
threadId: string | null;
|
|
46
|
+
parentThreadId?: string | null;
|
|
47
|
+
causationId?: string | null;
|
|
48
|
+
correlationId?: string | null;
|
|
49
|
+
effectId?: string | null;
|
|
50
|
+
origin: EventOrigin;
|
|
51
|
+
ephemeral?: boolean;
|
|
52
|
+
idempotencyKey?: string | null;
|
|
53
|
+
};
|
|
54
|
+
export type TypedEvent<TType extends string, TPayload = JsonValue> = EventEnvelope<TType, TPayload>;
|
|
55
|
+
export type EventInput<TType extends string = string, TPayload = JsonValue> = {
|
|
56
|
+
type: TType;
|
|
57
|
+
payload: TPayload;
|
|
58
|
+
threadId?: string | null;
|
|
59
|
+
parentThreadId?: string | null;
|
|
60
|
+
causationId?: string | null;
|
|
61
|
+
correlationId?: string | null;
|
|
62
|
+
effectId?: string | null;
|
|
63
|
+
origin?: EventOrigin;
|
|
64
|
+
ephemeral?: boolean;
|
|
65
|
+
id?: string;
|
|
66
|
+
ts?: number;
|
|
67
|
+
idempotencyKey?: string | null;
|
|
68
|
+
};
|
|
69
|
+
export type AppendableEvent = Omit<EventEnvelope, 'seq'> & {
|
|
70
|
+
seq?: number;
|
|
71
|
+
};
|
|
72
|
+
export declare function payloadAsJson(payload: JsonValue): JsonValue;
|
|
73
|
+
export declare function withAssignedSeq(partial: AppendableEvent, runId: string, seq: number): EventEnvelope;
|
|
74
|
+
export declare function createEvent<TType extends string>(runId: string, input: Omit<EventInput, 'type'> & {
|
|
75
|
+
type: TType;
|
|
76
|
+
}, options?: {
|
|
77
|
+
seq?: number;
|
|
78
|
+
}): EventEnvelope<TType>;
|
|
79
|
+
export declare function fromWireEvent(wire: Schema.Schema.Type<typeof EventEnvelopeSchema>): EventEnvelope;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { createEventId } from './ids.js';
|
|
3
|
+
export const JsonValueSchema = Schema.Json;
|
|
4
|
+
export const EventOriginSchema = Schema.Union([
|
|
5
|
+
Schema.Struct({
|
|
6
|
+
type: Schema.Literal('thread'),
|
|
7
|
+
threadId: Schema.String,
|
|
8
|
+
}),
|
|
9
|
+
Schema.Struct({
|
|
10
|
+
type: Schema.Literal('external'),
|
|
11
|
+
actorId: Schema.optional(Schema.String),
|
|
12
|
+
}),
|
|
13
|
+
Schema.Struct({
|
|
14
|
+
type: Schema.Literal('system'),
|
|
15
|
+
}),
|
|
16
|
+
]);
|
|
17
|
+
export const EventEnvelopeSchema = Schema.Struct({
|
|
18
|
+
id: Schema.String,
|
|
19
|
+
runId: Schema.String,
|
|
20
|
+
seq: Schema.Number,
|
|
21
|
+
ts: Schema.Number,
|
|
22
|
+
type: Schema.String,
|
|
23
|
+
payload: JsonValueSchema,
|
|
24
|
+
threadId: Schema.NullOr(Schema.String),
|
|
25
|
+
parentThreadId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
26
|
+
causationId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
27
|
+
correlationId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
28
|
+
effectId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
29
|
+
origin: EventOriginSchema,
|
|
30
|
+
ephemeral: Schema.optional(Schema.Boolean),
|
|
31
|
+
idempotencyKey: Schema.optional(Schema.NullOr(Schema.String)),
|
|
32
|
+
});
|
|
33
|
+
export function payloadAsJson(payload) {
|
|
34
|
+
// SAFETY: round-trip through JSON yields JsonValue.
|
|
35
|
+
return JSON.parse(JSON.stringify(payload));
|
|
36
|
+
}
|
|
37
|
+
export function withAssignedSeq(partial, runId, seq) {
|
|
38
|
+
return { ...partial, runId, seq };
|
|
39
|
+
}
|
|
40
|
+
export function createEvent(runId, input, options) {
|
|
41
|
+
const threadId = input.threadId ?? null;
|
|
42
|
+
const parentThreadId = input.parentThreadId ?? null;
|
|
43
|
+
return {
|
|
44
|
+
id: input.id ?? createEventId(),
|
|
45
|
+
runId,
|
|
46
|
+
seq: options?.seq ?? 0,
|
|
47
|
+
ts: input.ts ?? Date.now(),
|
|
48
|
+
type: input.type,
|
|
49
|
+
payload: input.payload,
|
|
50
|
+
threadId,
|
|
51
|
+
parentThreadId,
|
|
52
|
+
causationId: input.causationId,
|
|
53
|
+
correlationId: input.correlationId,
|
|
54
|
+
effectId: input.effectId,
|
|
55
|
+
origin: input.origin ?? { type: 'system' },
|
|
56
|
+
ephemeral: input.ephemeral,
|
|
57
|
+
idempotencyKey: input.idempotencyKey ?? null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function fromWireEvent(wire) {
|
|
61
|
+
const threadId = wire.threadId ?? null;
|
|
62
|
+
const parentThreadId = wire.parentThreadId ?? null;
|
|
63
|
+
return {
|
|
64
|
+
...wire,
|
|
65
|
+
threadId,
|
|
66
|
+
parentThreadId,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AppendableEvent, EventEnvelope } from './envelope';
|
|
2
|
+
import type { JsonValue } from './types';
|
|
3
|
+
/** JSON-safe event envelope for SSE and pull responses. */
|
|
4
|
+
export type EncodedLoomsEvent = EventEnvelope;
|
|
5
|
+
export declare function encodeLoomsEvent(event: EventEnvelope): EncodedLoomsEvent;
|
|
6
|
+
export declare function encodeAppendableEvent(event: AppendableEvent): EncodedLoomsEvent;
|
|
7
|
+
export declare function decodeLoomsEvent(raw: JsonValue | EncodedLoomsEvent, runId: string): EventEnvelope;
|
|
8
|
+
export declare function decodeAppendableEvent(raw: JsonValue | EncodedLoomsEvent, runId: string): AppendableEvent | undefined;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Predicate } from 'effect';
|
|
2
|
+
import { createEvent, payloadAsJson } from './envelope.js';
|
|
3
|
+
import { createEventId } from './ids.js';
|
|
4
|
+
export function encodeLoomsEvent(event) {
|
|
5
|
+
return {
|
|
6
|
+
...event,
|
|
7
|
+
payload: payloadAsJson(event.payload),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function encodeAppendableEvent(event) {
|
|
11
|
+
return {
|
|
12
|
+
id: event.id ?? createEventId(),
|
|
13
|
+
runId: event.runId,
|
|
14
|
+
seq: event.seq ?? 0,
|
|
15
|
+
ts: event.ts ?? Date.now(),
|
|
16
|
+
type: event.type,
|
|
17
|
+
payload: payloadAsJson(event.payload),
|
|
18
|
+
threadId: event.threadId ?? null,
|
|
19
|
+
parentThreadId: event.parentThreadId ?? null,
|
|
20
|
+
causationId: event.causationId ?? null,
|
|
21
|
+
correlationId: event.correlationId ?? null,
|
|
22
|
+
effectId: event.effectId ?? null,
|
|
23
|
+
ephemeral: event.ephemeral ?? false,
|
|
24
|
+
origin: event.origin,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function readOrigin(raw) {
|
|
28
|
+
if ('type' in raw && raw.type === 'thread') {
|
|
29
|
+
const threadId = 'threadId' in raw && Predicate.isString(raw.threadId) ? raw.threadId : '';
|
|
30
|
+
return { type: 'thread', threadId };
|
|
31
|
+
}
|
|
32
|
+
if ('type' in raw && raw.type === 'external') {
|
|
33
|
+
const actorId = 'actorId' in raw && Predicate.isString(raw.actorId) ? raw.actorId : undefined;
|
|
34
|
+
return actorId ? { type: 'external', actorId } : { type: 'external' };
|
|
35
|
+
}
|
|
36
|
+
return { type: 'system' };
|
|
37
|
+
}
|
|
38
|
+
export function decodeLoomsEvent(raw, runId) {
|
|
39
|
+
if (!Predicate.isReadonlyObject(raw)) {
|
|
40
|
+
throw new Error('Invalid event payload: expected object');
|
|
41
|
+
}
|
|
42
|
+
if ('type' in raw && Predicate.isString(raw.type)) {
|
|
43
|
+
const rawPayload = 'payload' in raw ? raw.payload : {};
|
|
44
|
+
// SAFETY: payload is JSON-compatible.
|
|
45
|
+
const payload = rawPayload;
|
|
46
|
+
const id = 'id' in raw && Predicate.isString(raw.id) ? raw.id : createEventId();
|
|
47
|
+
const ts = 'ts' in raw && Predicate.isNumber(raw.ts) ? raw.ts : Date.now();
|
|
48
|
+
const seq = 'seq' in raw && Predicate.isNumber(raw.seq) ? raw.seq : 0;
|
|
49
|
+
const ephemeral = 'ephemeral' in raw && Predicate.isBoolean(raw.ephemeral) ? raw.ephemeral : false;
|
|
50
|
+
const itemRunId = 'runId' in raw && Predicate.isString(raw.runId) ? raw.runId : runId;
|
|
51
|
+
const threadId = 'threadId' in raw && Predicate.isString(raw.threadId) ? raw.threadId : null;
|
|
52
|
+
const parentThreadId = 'parentThreadId' in raw && Predicate.isString(raw.parentThreadId) ? raw.parentThreadId : null;
|
|
53
|
+
const origin = 'origin' in raw && Predicate.isReadonlyObject(raw.origin)
|
|
54
|
+
? readOrigin(raw.origin)
|
|
55
|
+
: { type: 'system' };
|
|
56
|
+
return createEvent(itemRunId, {
|
|
57
|
+
id,
|
|
58
|
+
ts,
|
|
59
|
+
type: raw.type,
|
|
60
|
+
payload,
|
|
61
|
+
threadId,
|
|
62
|
+
parentThreadId,
|
|
63
|
+
causationId: 'causationId' in raw && Predicate.isString(raw.causationId) ? raw.causationId : null,
|
|
64
|
+
correlationId: 'correlationId' in raw && Predicate.isString(raw.correlationId)
|
|
65
|
+
? raw.correlationId
|
|
66
|
+
: null,
|
|
67
|
+
effectId: 'effectId' in raw && Predicate.isString(raw.effectId) ? raw.effectId : null,
|
|
68
|
+
ephemeral,
|
|
69
|
+
origin,
|
|
70
|
+
}, { seq });
|
|
71
|
+
}
|
|
72
|
+
throw new Error('Invalid event payload: missing type');
|
|
73
|
+
}
|
|
74
|
+
export function decodeAppendableEvent(raw, runId) {
|
|
75
|
+
try {
|
|
76
|
+
const { seq: _seq, ...rest } = decodeLoomsEvent(raw, runId);
|
|
77
|
+
return rest;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { EventEnvelope } from './envelope';
|
|
2
|
+
/** Thread bucket key used by run-tree counts (`threadId` or `'run'` for run-scoped). */
|
|
3
|
+
export declare function eventThreadKey(event: {
|
|
4
|
+
threadId?: string | null;
|
|
5
|
+
}): string;
|
|
6
|
+
/**
|
|
7
|
+
* Append-only indexes over an event log. Thread/count snapshots are rebuilt
|
|
8
|
+
* lazily on read so coalesced applies avoid repeated O(n) freezes.
|
|
9
|
+
*/
|
|
10
|
+
export declare class EventIndex<TEvent extends EventEnvelope = EventEnvelope> {
|
|
11
|
+
private counts;
|
|
12
|
+
private countsSnapshot;
|
|
13
|
+
private countsDirty;
|
|
14
|
+
private byThread;
|
|
15
|
+
private byThreadSnapshots;
|
|
16
|
+
private dirtyThreads;
|
|
17
|
+
private bySeqMap;
|
|
18
|
+
private latestByType;
|
|
19
|
+
private startedAtTimestamp;
|
|
20
|
+
append(batch: readonly TEvent[]): void;
|
|
21
|
+
private publish;
|
|
22
|
+
getCounts(): ReadonlyMap<string, number>;
|
|
23
|
+
getByThread(threadId: string): readonly TEvent[];
|
|
24
|
+
getBySeq(seq: number): TEvent | undefined;
|
|
25
|
+
getStartedAt(): number | undefined;
|
|
26
|
+
getLatest(type: string): TEvent | undefined;
|
|
27
|
+
clear(): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const EMPTY_EVENTS = Object.freeze([]);
|
|
2
|
+
/** Thread bucket key used by run-tree counts (`threadId` or `'run'` for run-scoped). */
|
|
3
|
+
export function eventThreadKey(event) {
|
|
4
|
+
return event.threadId ?? 'run';
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Append-only indexes over an event log. Thread/count snapshots are rebuilt
|
|
8
|
+
* lazily on read so coalesced applies avoid repeated O(n) freezes.
|
|
9
|
+
*/
|
|
10
|
+
export class EventIndex {
|
|
11
|
+
counts = new Map();
|
|
12
|
+
countsSnapshot = new Map();
|
|
13
|
+
countsDirty = false;
|
|
14
|
+
byThread = new Map();
|
|
15
|
+
byThreadSnapshots = new Map();
|
|
16
|
+
dirtyThreads = new Set();
|
|
17
|
+
bySeqMap = new Map();
|
|
18
|
+
latestByType = new Map();
|
|
19
|
+
startedAtTimestamp = undefined;
|
|
20
|
+
append(batch) {
|
|
21
|
+
if (batch.length === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
for (const event of batch) {
|
|
25
|
+
this.bySeqMap.set(event.seq, event);
|
|
26
|
+
this.latestByType.set(event.type, event);
|
|
27
|
+
if (this.startedAtTimestamp === undefined && event.type === 'runtime.run.started') {
|
|
28
|
+
this.startedAtTimestamp = event.ts;
|
|
29
|
+
}
|
|
30
|
+
const threadKey = eventThreadKey(event);
|
|
31
|
+
this.counts.set(threadKey, (this.counts.get(threadKey) ?? 0) + 1);
|
|
32
|
+
this.dirtyThreads.add(threadKey);
|
|
33
|
+
this.countsDirty = true;
|
|
34
|
+
let threadList = this.byThread.get(threadKey);
|
|
35
|
+
if (!threadList) {
|
|
36
|
+
threadList = [];
|
|
37
|
+
this.byThread.set(threadKey, threadList);
|
|
38
|
+
}
|
|
39
|
+
threadList.push(event);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
publish() {
|
|
43
|
+
if (this.dirtyThreads.size > 0) {
|
|
44
|
+
for (const threadKey of this.dirtyThreads) {
|
|
45
|
+
const list = this.byThread.get(threadKey);
|
|
46
|
+
if (list) {
|
|
47
|
+
this.byThreadSnapshots.set(threadKey, Object.freeze(list.slice()));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
this.dirtyThreads.clear();
|
|
51
|
+
}
|
|
52
|
+
if (this.countsDirty) {
|
|
53
|
+
this.countsSnapshot = new Map(this.counts);
|
|
54
|
+
this.countsDirty = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
getCounts() {
|
|
58
|
+
this.publish();
|
|
59
|
+
return this.countsSnapshot;
|
|
60
|
+
}
|
|
61
|
+
getByThread(threadId) {
|
|
62
|
+
this.publish();
|
|
63
|
+
// SAFETY: EMPTY_EVENTS is an empty frozen array compatible with any TEvent[].
|
|
64
|
+
return this.byThreadSnapshots.get(threadId) ?? EMPTY_EVENTS;
|
|
65
|
+
}
|
|
66
|
+
getBySeq(seq) {
|
|
67
|
+
return this.bySeqMap.get(seq);
|
|
68
|
+
}
|
|
69
|
+
getStartedAt() {
|
|
70
|
+
return this.startedAtTimestamp;
|
|
71
|
+
}
|
|
72
|
+
getLatest(type) {
|
|
73
|
+
return this.latestByType.get(type);
|
|
74
|
+
}
|
|
75
|
+
clear() {
|
|
76
|
+
this.counts.clear();
|
|
77
|
+
this.countsSnapshot = new Map();
|
|
78
|
+
this.countsDirty = false;
|
|
79
|
+
this.byThread.clear();
|
|
80
|
+
this.byThreadSnapshots.clear();
|
|
81
|
+
this.dirtyThreads.clear();
|
|
82
|
+
this.bySeqMap.clear();
|
|
83
|
+
this.latestByType.clear();
|
|
84
|
+
this.startedAtTimestamp = undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Stream, type Effect } from 'effect';
|
|
2
|
+
import type { EventEnvelope } from './envelope';
|
|
3
|
+
import type { RunState } from './state';
|
|
4
|
+
import { type ThreadDefinition } from './thread';
|
|
5
|
+
export interface FoldRegistry {
|
|
6
|
+
readonly threads: ReadonlyMap<string, ThreadDefinition>;
|
|
7
|
+
}
|
|
8
|
+
export declare function foldEvent(state: RunState, event: EventEnvelope, registry: FoldRegistry): RunState;
|
|
9
|
+
export declare function foldRun(events: readonly EventEnvelope[], registry: FoldRegistry, options?: {
|
|
10
|
+
runId?: string;
|
|
11
|
+
initial?: RunState;
|
|
12
|
+
}): RunState;
|
|
13
|
+
/**
|
|
14
|
+
* Folds an Effect Stream of events into RunState without buffering all events into memory.
|
|
15
|
+
*/
|
|
16
|
+
export declare function foldStream<E = never, R = never>(stream: Stream.Stream<EventEnvelope, E, R>, registry: FoldRegistry, options?: {
|
|
17
|
+
runId?: string;
|
|
18
|
+
initial?: RunState;
|
|
19
|
+
}): Effect.Effect<RunState, E, R>;
|