@swirls/looms 0.2.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/actor/host.js +4 -6
  3. package/dist/actor/index.d.ts +1 -1
  4. package/dist/actor/index.js +1 -1
  5. package/dist/actor/routing.d.ts +5 -0
  6. package/dist/actor/routing.js +14 -0
  7. package/dist/agent/agent-session.js +1 -0
  8. package/dist/agent/debugger/agent-chat.js +45 -11
  9. package/dist/agent/debugger/summarize.js +2 -0
  10. package/dist/agent/effects.d.ts +30 -0
  11. package/dist/agent/events.d.ts +2 -0
  12. package/dist/agent/events.js +2 -0
  13. package/dist/agent/llm.js +1 -8
  14. package/dist/agent/module.d.ts +604 -534
  15. package/dist/agent/module.js +11 -7
  16. package/dist/agent/projections.d.ts +5 -0
  17. package/dist/agent/projections.js +43 -2
  18. package/dist/agent/scope.d.ts +2 -0
  19. package/dist/agent/signals.d.ts +5 -0
  20. package/dist/agent/signals.js +7 -0
  21. package/dist/agent/threads.js +35 -21
  22. package/dist/agent/transcript.d.ts +7 -0
  23. package/dist/agent/transcript.js +19 -0
  24. package/dist/agent/types.d.ts +10 -0
  25. package/dist/agent/types.js +4 -0
  26. package/dist/approval/debugger/approvals-view.js +5 -3
  27. package/dist/approval/effects.d.ts +12 -0
  28. package/dist/approval/module.d.ts +24 -0
  29. package/dist/client/client.d.ts +292 -0
  30. package/dist/client/client.js +17 -1
  31. package/dist/cloudflare/router.d.ts +1 -1
  32. package/dist/cloudflare/router.js +6 -8
  33. package/dist/core/effects.d.ts +7 -0
  34. package/dist/core/effects.js +1 -0
  35. package/dist/core/fold.js +12 -6
  36. package/dist/core/fork.d.ts +35 -0
  37. package/dist/core/fork.js +129 -0
  38. package/dist/core/index.d.ts +1 -0
  39. package/dist/core/index.js +1 -0
  40. package/dist/core/module-scope.d.ts +1 -0
  41. package/dist/core/module.d.ts +2 -2
  42. package/dist/core/projection.js +1 -0
  43. package/dist/core/protocol.d.ts +10 -1
  44. package/dist/core/protocol.js +11 -0
  45. package/dist/core/replay.d.ts +8 -0
  46. package/dist/core/snapshots.js +3 -17
  47. package/dist/core/state.d.ts +14 -0
  48. package/dist/core/state.js +25 -2
  49. package/dist/core/store.d.ts +1 -0
  50. package/dist/debugger/app/assets/{index-Qb371t_O.js → index-BevQYa-U.js} +2 -2
  51. package/dist/debugger/app/assets/index-CgDzWJg7.css +2 -0
  52. package/dist/debugger/app/assets/routes-B17Om93i.js +47 -0
  53. package/dist/debugger/app/index.html +2 -2
  54. package/dist/debugger/components/composer.d.ts +5 -2
  55. package/dist/debugger/components/composer.js +5 -4
  56. package/dist/debugger/components/run-tree.js +2 -5
  57. package/dist/debugger/events/catalog.js +1 -3
  58. package/dist/debugger/events/protocol-summarize.js +7 -0
  59. package/dist/debugger/lib/event-counts.d.ts +2 -2
  60. package/dist/debugger/lib/event-counts.js +5 -9
  61. package/dist/debugger/shell/debugger-shell.js +1 -1
  62. package/dist/debugger/shell/run-controls.d.ts +2 -1
  63. package/dist/debugger/shell/run-controls.js +40 -13
  64. package/dist/debugger/shell/run-search.js +1 -3
  65. package/dist/evaluator/effects.d.ts +12 -0
  66. package/dist/evaluator/module.d.ts +24 -0
  67. package/dist/projectors/index-model.d.ts +13 -7
  68. package/dist/projectors/index-model.js +21 -26
  69. package/dist/projectors/memory.js +19 -11
  70. package/dist/projectors/postgres.js +20 -19
  71. package/dist/projectors/schema.js +20 -18
  72. package/dist/projectors/sqlite.js +32 -27
  73. package/dist/react/materialize.js +16 -11
  74. package/dist/react/react.d.ts +6 -2
  75. package/dist/react/react.js +5 -2
  76. package/dist/react/tables.d.ts +3 -2
  77. package/dist/runtime/cancellation.js +14 -27
  78. package/dist/runtime/errors.d.ts +39 -0
  79. package/dist/runtime/errors.js +32 -0
  80. package/dist/runtime/helpers.d.ts +2 -0
  81. package/dist/runtime/helpers.js +23 -1
  82. package/dist/runtime/index.d.ts +1 -1
  83. package/dist/runtime/index.js +1 -1
  84. package/dist/runtime/looms.d.ts +4 -0
  85. package/dist/runtime/looms.js +1 -0
  86. package/dist/runtime/run-start.js +1 -3
  87. package/dist/runtime/runtime.d.ts +1 -1
  88. package/dist/runtime/runtime.js +45 -18
  89. package/dist/runtime/server/http.d.ts +2 -0
  90. package/dist/runtime/server/http.js +17 -2
  91. package/dist/runtime/server/routes/runs.js +35 -2
  92. package/dist/runtime/types.d.ts +8 -0
  93. package/dist/runtime/wake-engine.js +5 -2
  94. package/dist/s2/snapshot-store.js +2 -6
  95. package/dist/workflow/effects.d.ts +24 -0
  96. package/dist/workflow/effects.js +1 -0
  97. package/dist/workflow/module.d.ts +48 -0
  98. package/package.json +1 -1
  99. package/dist/debugger/app/assets/index-B3Ah8WAE.css +0 -2
  100. package/dist/debugger/app/assets/routes-CjtEHgPQ.js +0 -47
@@ -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';
@@ -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'> & {
@@ -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. Return an empty array when the event interrupts
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 {
@@ -174,6 +174,7 @@ export function toThreadTree(state) {
174
174
  status: 'running',
175
175
  rootThreadId: state.rootThreadId,
176
176
  startIdentity: null,
177
+ forkedFrom: null,
177
178
  threads: state.records,
178
179
  waits: {},
179
180
  outstandingEffects: [],
@@ -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']>;
@@ -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',
@@ -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;
@@ -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
- let lastSnap;
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
- let latestSnapIdx = -1;
70
- for (let i = events.length - 1; i >= 0; i--) {
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);
@@ -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;
@@ -1,7 +1,8 @@
1
- import { Effect, Schema } from 'effect';
1
+ import { Effect, Predicate, Schema } from 'effect';
2
2
  import { RuntimeEffectSchema, WaitConditionSchema, } from './effects.js';
3
3
  import { JsonValueSchema } from './envelope.js';
4
4
  import { DEFAULT_DEFINITION_VERSION } from './module.js';
5
+ import { isTerminalStatus } from './thread.js';
5
6
  export const RunStateSchema = Schema.Struct({
6
7
  runId: Schema.String,
7
8
  status: Schema.Union([
@@ -20,6 +21,7 @@ export const RunStateSchema = Schema.Struct({
20
21
  idempotencyKey: Schema.NullOr(Schema.String),
21
22
  rootThreadId: Schema.String,
22
23
  })).pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))),
24
+ forkedFrom: Schema.NullOr(Schema.Struct({ runId: Schema.String, seq: Schema.Finite })).pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))),
23
25
  threads: Schema.Record(Schema.String, Schema.Struct({
24
26
  threadId: Schema.String,
25
27
  kind: Schema.String,
@@ -80,6 +82,7 @@ export function emptyRunState(runId) {
80
82
  status: 'running',
81
83
  rootThreadId: null,
82
84
  startIdentity: null,
85
+ forkedFrom: null,
83
86
  threads: {},
84
87
  waits: {},
85
88
  outstandingEffects: [],
@@ -89,7 +92,27 @@ export function emptyRunState(runId) {
89
92
  };
90
93
  }
91
94
  export function isRunTerminal(state) {
92
- return state.status === 'completed' || state.status === 'failed' || state.status === 'cancelled';
95
+ return isTerminalStatus(state.status);
96
+ }
97
+ /** Every thread below `threadId`, in the order they were started. */
98
+ export function descendantThreadIds(state, threadId) {
99
+ return Object.values(state.threads)
100
+ .filter((record) => {
101
+ let parentThreadId = record.parentThreadId;
102
+ while (parentThreadId) {
103
+ if (parentThreadId === threadId) {
104
+ return true;
105
+ }
106
+ parentThreadId = state.threads[parentThreadId]?.parentThreadId ?? null;
107
+ }
108
+ return false;
109
+ })
110
+ .map((record) => record.threadId);
111
+ }
112
+ /** The status a `runtime.run.completed` payload ends the run with: an error means it failed. */
113
+ export function completedRunStatus(payload) {
114
+ const error = Predicate.isObject(payload) ? payload.error : undefined;
115
+ return Predicate.isString(error) && error.length > 0 ? 'failed' : 'completed';
93
116
  }
94
117
  export function isRunParked(state) {
95
118
  if (isRunTerminal(state)) {
@@ -91,6 +91,7 @@ export interface StreamBounds {
91
91
  readonly tail: number;
92
92
  }
93
93
  export interface AppendOptions {
94
+ /** Fail unless the stream is at this tail. The batch is numbered from `expectedTail + 1`. */
94
95
  readonly expectedTail?: number;
95
96
  /**
96
97
  * Atomically claims an ingress command with the append. Reusing the key