@relayflows/sdk 2.0.34 → 2.0.36

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 (162) hide show
  1. package/dist/adapters/base.d.ts +16 -0
  2. package/dist/adapters/base.d.ts.map +1 -1
  3. package/dist/adapters/claude.d.ts.map +1 -1
  4. package/dist/adapters/claude.js +15 -0
  5. package/dist/adapters/claude.js.map +1 -1
  6. package/dist/adapters/codex.d.ts.map +1 -1
  7. package/dist/adapters/codex.js +4 -0
  8. package/dist/adapters/codex.js.map +1 -1
  9. package/dist/adapters/usage-limit.d.ts +2 -0
  10. package/dist/adapters/usage-limit.d.ts.map +1 -0
  11. package/dist/adapters/usage-limit.js +17 -0
  12. package/dist/adapters/usage-limit.js.map +1 -0
  13. package/dist/authored-activity.d.ts +17 -0
  14. package/dist/authored-activity.d.ts.map +1 -0
  15. package/dist/authored-activity.js +218 -0
  16. package/dist/authored-activity.js.map +1 -0
  17. package/dist/authored-child-run.d.ts +34 -0
  18. package/dist/authored-child-run.d.ts.map +1 -0
  19. package/dist/authored-child-run.js +128 -0
  20. package/dist/authored-child-run.js.map +1 -0
  21. package/dist/authored-dispatch.d.ts +24 -0
  22. package/dist/authored-dispatch.d.ts.map +1 -0
  23. package/dist/authored-dispatch.js +81 -0
  24. package/dist/authored-dispatch.js.map +1 -0
  25. package/dist/authored-flow-error.d.ts +32 -3
  26. package/dist/authored-flow-error.d.ts.map +1 -1
  27. package/dist/authored-flow-error.js +3 -1
  28. package/dist/authored-flow-error.js.map +1 -1
  29. package/dist/authored-flow-executor.d.ts +26 -1
  30. package/dist/authored-flow-executor.d.ts.map +1 -1
  31. package/dist/authored-flow-executor.js +122 -16
  32. package/dist/authored-flow-executor.js.map +1 -1
  33. package/dist/authored-flow-loader.d.ts.map +1 -1
  34. package/dist/authored-flow-loader.js +9 -0
  35. package/dist/authored-flow-loader.js.map +1 -1
  36. package/dist/authored-mcp.js +2 -2
  37. package/dist/authored-mcp.js.map +1 -1
  38. package/dist/authored-node-entry.js +4 -1
  39. package/dist/authored-node-entry.js.map +1 -1
  40. package/dist/authored-node-runner.d.ts.map +1 -1
  41. package/dist/authored-node-runner.js +40 -8
  42. package/dist/authored-node-runner.js.map +1 -1
  43. package/dist/authored-plugin-effect.js +2 -2
  44. package/dist/authored-plugin-effect.js.map +1 -1
  45. package/dist/authored-root.d.ts +10 -7
  46. package/dist/authored-root.d.ts.map +1 -1
  47. package/dist/authored-root.js +20 -0
  48. package/dist/authored-root.js.map +1 -1
  49. package/dist/authored-step-output.d.ts +24 -8
  50. package/dist/authored-step-output.d.ts.map +1 -1
  51. package/dist/authored-step-output.js +81 -15
  52. package/dist/authored-step-output.js.map +1 -1
  53. package/dist/authored-subscription-park.d.ts +5 -0
  54. package/dist/authored-subscription-park.d.ts.map +1 -0
  55. package/dist/authored-subscription-park.js +47 -0
  56. package/dist/authored-subscription-park.js.map +1 -0
  57. package/dist/authored-worker-step.d.ts +3 -1
  58. package/dist/authored-worker-step.d.ts.map +1 -1
  59. package/dist/authored-worker-step.js +25 -9
  60. package/dist/authored-worker-step.js.map +1 -1
  61. package/dist/cli/check-activities.d.ts +8 -0
  62. package/dist/cli/check-activities.d.ts.map +1 -0
  63. package/dist/cli/check-activities.js +155 -0
  64. package/dist/cli/check-activities.js.map +1 -0
  65. package/dist/cli/cli-probe.d.ts.map +1 -1
  66. package/dist/cli/cli-probe.js +14 -2
  67. package/dist/cli/cli-probe.js.map +1 -1
  68. package/dist/cli/direct-run.d.ts.map +1 -1
  69. package/dist/cli/direct-run.js +10 -2
  70. package/dist/cli/direct-run.js.map +1 -1
  71. package/dist/cli/run.d.ts +23 -5
  72. package/dist/cli/run.d.ts.map +1 -1
  73. package/dist/cli/run.js +32 -5
  74. package/dist/cli/run.js.map +1 -1
  75. package/dist/cli/subscription-report.d.ts +5 -0
  76. package/dist/cli/subscription-report.d.ts.map +1 -0
  77. package/dist/cli/subscription-report.js +48 -0
  78. package/dist/cli/subscription-report.js.map +1 -0
  79. package/dist/cli-adapter.d.ts +4 -2
  80. package/dist/cli-adapter.d.ts.map +1 -1
  81. package/dist/cli-adapter.js +4 -0
  82. package/dist/cli-adapter.js.map +1 -1
  83. package/dist/cli.d.ts +1 -1
  84. package/dist/cli.d.ts.map +1 -1
  85. package/dist/cli.js +16 -5
  86. package/dist/cli.js.map +1 -1
  87. package/dist/cloud-mirror-step.d.ts +2 -0
  88. package/dist/cloud-mirror-step.d.ts.map +1 -1
  89. package/dist/cloud-mirror-step.js +9 -1
  90. package/dist/cloud-mirror-step.js.map +1 -1
  91. package/dist/cloud-mirror.d.ts.map +1 -1
  92. package/dist/cloud-mirror.js +17 -2
  93. package/dist/cloud-mirror.js.map +1 -1
  94. package/dist/failure-kinds.d.ts +2 -2
  95. package/dist/failure-kinds.d.ts.map +1 -1
  96. package/dist/failure-kinds.js +2 -0
  97. package/dist/failure-kinds.js.map +1 -1
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/journal-client.d.ts +17 -2
  102. package/dist/journal-client.d.ts.map +1 -1
  103. package/dist/journal-client.js +33 -2
  104. package/dist/journal-client.js.map +1 -1
  105. package/dist/journal-reader.d.ts.map +1 -1
  106. package/dist/journal-reader.js +2 -0
  107. package/dist/journal-reader.js.map +1 -1
  108. package/dist/local-agent.d.ts +4 -1
  109. package/dist/local-agent.d.ts.map +1 -1
  110. package/dist/local-agent.js +14 -4
  111. package/dist/local-agent.js.map +1 -1
  112. package/dist/preflight.d.ts +6 -0
  113. package/dist/preflight.d.ts.map +1 -1
  114. package/dist/preflight.js +36 -10
  115. package/dist/preflight.js.map +1 -1
  116. package/dist/protocol.d.ts +166 -1
  117. package/dist/protocol.d.ts.map +1 -1
  118. package/dist/step-settling.d.ts +11 -0
  119. package/dist/step-settling.d.ts.map +1 -0
  120. package/dist/step-settling.js +22 -0
  121. package/dist/step-settling.js.map +1 -0
  122. package/dist/worker-slots.d.ts +15 -0
  123. package/dist/worker-slots.d.ts.map +1 -1
  124. package/dist/worker-slots.js +40 -3
  125. package/dist/worker-slots.js.map +1 -1
  126. package/package.json +2 -2
  127. package/src/adapters/base.ts +19 -0
  128. package/src/adapters/claude.ts +15 -0
  129. package/src/adapters/codex.ts +6 -0
  130. package/src/adapters/usage-limit.ts +17 -0
  131. package/src/authored-activity.ts +251 -0
  132. package/src/authored-child-run.ts +138 -0
  133. package/src/authored-dispatch.ts +113 -0
  134. package/src/authored-flow-error.ts +36 -0
  135. package/src/authored-flow-executor.ts +166 -15
  136. package/src/authored-flow-loader.ts +12 -0
  137. package/src/authored-mcp.ts +2 -2
  138. package/src/authored-node-entry.ts +4 -1
  139. package/src/authored-node-runner.ts +41 -8
  140. package/src/authored-plugin-effect.ts +2 -2
  141. package/src/authored-root.ts +28 -4
  142. package/src/authored-step-output.ts +88 -15
  143. package/src/authored-subscription-park.ts +46 -0
  144. package/src/authored-worker-step.ts +24 -8
  145. package/src/cli/check-activities.ts +144 -0
  146. package/src/cli/cli-probe.ts +15 -3
  147. package/src/cli/direct-run.ts +9 -0
  148. package/src/cli/run.ts +56 -9
  149. package/src/cli/subscription-report.ts +51 -0
  150. package/src/cli-adapter.ts +8 -2
  151. package/src/cli.ts +16 -6
  152. package/src/cloud-mirror-step.ts +16 -2
  153. package/src/cloud-mirror.ts +15 -2
  154. package/src/failure-kinds.ts +2 -0
  155. package/src/index.ts +6 -0
  156. package/src/journal-client.ts +47 -3
  157. package/src/journal-reader.ts +2 -0
  158. package/src/local-agent.ts +17 -3
  159. package/src/preflight.ts +46 -10
  160. package/src/protocol.ts +129 -0
  161. package/src/step-settling.ts +19 -0
  162. package/src/worker-slots.ts +63 -4
package/src/protocol.ts CHANGED
@@ -53,8 +53,16 @@ export type Verb =
53
53
  | 'effect.confirm'
54
54
  | 'step.complete'
55
55
  | 'step.wait'
56
+ | 'subscription.park'
56
57
  | 'event.emit'
57
58
  | 'event.submit'
59
+ | 'subscription.open'
60
+ | 'subscription.activate'
61
+ | 'subscription.deliver'
62
+ | 'subscription.inspect'
63
+ | 'subscription.fence_overflow'
64
+ | 'subscription.next'
65
+ | 'subscription.close'
58
66
  | 'channel.append'
59
67
  | 'channel.receive'
60
68
  | 'channel.ack'
@@ -362,6 +370,10 @@ export interface EventEmitParams {
362
370
  run_id: string;
363
371
  event_key: string;
364
372
  payload: unknown;
373
+ /** Provider delivery id for body activities. Required by Cloud; optional for legacy exact waits. */
374
+ delivery_id?: string;
375
+ /** Provider actor identity, used by the local router adapter for self filtering. */
376
+ actor?: string;
365
377
  }
366
378
  export interface EventEmitResult {
367
379
  matched: number;
@@ -388,6 +400,111 @@ export interface EventSubmitResult {
388
400
  run?: unknown;
389
401
  }
390
402
 
403
+ /** Body-level activity open; the server acknowledges only after its fenced binding is durable. */
404
+ export interface SubscriptionOpenParams {
405
+ run_id: string;
406
+ subscription_id: string;
407
+ event_types: string[];
408
+ pattern?: Record<string, unknown>;
409
+ settle_ms: number;
410
+ idle_ms: number;
411
+ deadline_ms: number;
412
+ include_self: boolean;
413
+ }
414
+ export type SubscriptionOpenResult =
415
+ | {
416
+ /** Immutable snapshot from the durable `subscription.prepared` entry. */
417
+ state: 'prepared';
418
+ subscription_id: string;
419
+ event_types: string[];
420
+ pattern?: Record<string, unknown>;
421
+ stream: string;
422
+ settle_ms: number;
423
+ idle_ms: number;
424
+ deadline_at_ms: number;
425
+ include_self: boolean;
426
+ }
427
+ | {
428
+ state: 'active';
429
+ subscription_id: string;
430
+ stream: string;
431
+ deadline_at_ms: number;
432
+ };
433
+
434
+ /** Cloud supplies this receipt only after it has durably fenced its binding. */
435
+ export interface SubscriptionActivateParams {
436
+ run_id: string;
437
+ subscription_id: string;
438
+ ingress_offset: number;
439
+ router_binding: Record<string, unknown>;
440
+ }
441
+ export interface SubscriptionActivateResult {
442
+ state: 'active';
443
+ subscription_id: string;
444
+ stream: string;
445
+ deadline_at_ms: number;
446
+ }
447
+
448
+ /** Targeted Cloud ingress; receipt must equal the journaled activation receipt. */
449
+ export interface SubscriptionDeliverParams {
450
+ run_id: string;
451
+ subscription_id: string;
452
+ router_binding: Record<string, unknown>;
453
+ delivery_id: string;
454
+ frame: unknown;
455
+ }
456
+ export interface SubscriptionDeliverResult {
457
+ /** False for an idempotent duplicate or a frame that closes on overflow. */
458
+ appended: boolean;
459
+ reason?: 'duplicate' | 'overflow';
460
+ }
461
+
462
+ /** Read-only projection of the kernel's durable subscription records. */
463
+ export interface SubscriptionSnapshot {
464
+ subscriptionId: string;
465
+ state: 'prepared' | 'active' | 'closed';
466
+ completionReason?: 'closed' | 'run_completed' | 'canceled' | 'deadline' | 'overflow';
467
+ routerBinding?: Record<string, unknown>;
468
+ ingressOffset?: number;
469
+ unreadFrames: number;
470
+ unreadBytes: number;
471
+ settleMs: number;
472
+ idleAtMs?: number;
473
+ deadlineAtMs: number;
474
+ }
475
+ export interface SubscriptionInspectParams { run_id: string }
476
+ export interface SubscriptionInspectResult { subscriptions: SubscriptionSnapshot[] }
477
+ export interface SubscriptionFenceOverflowParams {
478
+ run_id: string;
479
+ subscription_id: string;
480
+ router_binding: Record<string, unknown>;
481
+ }
482
+ export interface SubscriptionFenceOverflowResult { fenced: true }
483
+
484
+ export interface SubscriptionNextParams {
485
+ run_id: string;
486
+ subscription_id: string;
487
+ /** Internal durable receipt for the prior normal wake, supplied with the next pull. */
488
+ acknowledge_wait_id?: string;
489
+ /** Body call ordinal: replay this pull's durable wake across process restarts. */
490
+ sequence?: number;
491
+ }
492
+ export type SubscriptionNextResult =
493
+ | { kind: 'suspended'; subscription_id: string; stream: string; deadline_at_ms: number }
494
+ | { kind: 'events'; events: unknown[]; offset: number; acknowledge_wait_id?: string }
495
+ | { kind: 'idle'; acknowledge_wait_id?: string }
496
+ | { kind: 'deadline'; pending: { from: number; to: number } | null }
497
+ | { kind: 'overflow'; retained: number; bytes: number; from: number };
498
+
499
+ export interface SubscriptionCloseParams {
500
+ run_id: string;
501
+ subscription_id: string;
502
+ completion_reason: 'closed' | 'run_completed' | 'canceled';
503
+ }
504
+ export interface SubscriptionCloseResult {
505
+ closed: string;
506
+ }
507
+
391
508
  export interface StreamAppendParams {
392
509
  run_id: string;
393
510
  stream: string;
@@ -441,8 +558,20 @@ export interface VerbContract {
441
558
  'effect.confirm': { params: EffectConfirmParams; result: EffectConfirmResult };
442
559
  'step.complete': { params: StepCompleteParams; result: StepCompleteResult };
443
560
  'step.wait': { params: StepWaitParams; result: StepWaitResult };
561
+ 'subscription.park': {
562
+ params: { run_id: string; step_id: string; attempt: number; idempotency_key: string;
563
+ subscription_id: string; phase: 'activation' | 'event_wait' };
564
+ result: RunOutcome;
565
+ };
444
566
  'event.emit': { params: EventEmitParams; result: EventEmitResult };
445
567
  'event.submit': { params: EventSubmitParams; result: EventSubmitResult };
568
+ 'subscription.open': { params: SubscriptionOpenParams; result: SubscriptionOpenResult };
569
+ 'subscription.activate': { params: SubscriptionActivateParams; result: SubscriptionActivateResult };
570
+ 'subscription.deliver': { params: SubscriptionDeliverParams; result: SubscriptionDeliverResult };
571
+ 'subscription.inspect': { params: SubscriptionInspectParams; result: SubscriptionInspectResult };
572
+ 'subscription.fence_overflow': { params: SubscriptionFenceOverflowParams; result: SubscriptionFenceOverflowResult };
573
+ 'subscription.next': { params: SubscriptionNextParams; result: SubscriptionNextResult };
574
+ 'subscription.close': { params: SubscriptionCloseParams; result: SubscriptionCloseResult };
446
575
  'stream.append': { params: StreamAppendParams; result: StreamAppendResult };
447
576
  'stream.read': { params: StreamReadParams; result: StreamReadResult };
448
577
  'journal.read': { params: JournalReadParams; result: JournalReadResult };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A step's LAST settling `step.completed`: the verdict the kernel settled the
3
+ * step on. `retry` and `park` completions record an attempt the kernel did
4
+ * NOT settle it on (relayflowd-core `Disposition`) — crash recovery's
5
+ * `crashed`, a `worker_error` with iterations left — and precede the attempt
6
+ * that did, so neither the first completion nor a completion count is the
7
+ * step's verdict. Shared by the authored reader and the Bun result verifier,
8
+ * which must agree on which completion a claim is judged against.
9
+ */
10
+ export function settlingCompletion<T>(entries: readonly T[], stepId: string): T | undefined {
11
+ return entries.filter(entry => {
12
+ if (typeof entry !== 'object' || entry === null) return false;
13
+ const { entry_type: type, step_id: step, payload } = entry as Record<string, unknown>;
14
+ if (type !== 'step.completed' || step !== stepId) return false;
15
+ const disposition = typeof payload === 'object' && payload !== null
16
+ ? (payload as Record<string, unknown>)['disposition'] : undefined;
17
+ return disposition !== 'retry' && disposition !== 'park';
18
+ }).at(-1);
19
+ }
@@ -7,6 +7,16 @@ export function isAgentCapacity(value: unknown): value is number {
7
7
  return Number.isInteger(value) && (value as number) >= 1 && (value as number) <= MAX_LOCAL_AGENT_CAPACITY;
8
8
  }
9
9
 
10
+ export interface WorkerSlotScope {
11
+ run<T>(work: () => Promise<T>): Promise<T>;
12
+ /** Refuse this flow's queued/future work without closing the shared pool. */
13
+ close(reason: unknown): void;
14
+ }
15
+
16
+ interface ScopeState {
17
+ closed?: { readonly reason: unknown };
18
+ }
19
+
10
20
  /**
11
21
  * First-come admission to a worker that holds `capacity` dispatches at once.
12
22
  *
@@ -19,7 +29,11 @@ export function isAgentCapacity(value: unknown): value is number {
19
29
  */
20
30
  export class WorkerSlots {
21
31
  private held = 0;
22
- private readonly waiting: Array<{ resolve: () => void; reject: (reason: unknown) => void }> = [];
32
+ private readonly waiting: Array<{
33
+ resolve: () => void;
34
+ reject: (reason: unknown) => void;
35
+ scope?: ScopeState;
36
+ }> = [];
23
37
  private closed: { readonly reason: unknown } | undefined;
24
38
 
25
39
  constructor(readonly capacity: number) {
@@ -27,18 +41,37 @@ export class WorkerSlots {
27
41
  }
28
42
 
29
43
  async run<T>(work: () => Promise<T>): Promise<T> {
44
+ return this.runInScope(work);
45
+ }
46
+
47
+ /** One independently closable admission scope over this shared capacity. */
48
+ scope(): WorkerSlotScope {
49
+ const scope: ScopeState = {};
50
+ return Object.freeze({
51
+ run: <T>(work: () => Promise<T>) => this.runInScope(work, scope),
52
+ close: (reason: unknown) => this.closeScope(scope, reason),
53
+ });
54
+ }
55
+
56
+ private async runInScope<T>(work: () => Promise<T>, scope?: ScopeState): Promise<T> {
30
57
  if (this.closed !== undefined) throw this.closed.reason;
58
+ if (scope?.closed !== undefined) throw scope.closed.reason;
31
59
  if (this.held < this.capacity) this.held++;
32
60
  // A released slot is handed straight to the next waiter, so `held` never
33
61
  // dips below capacity while anyone is queued and no later caller can jump it.
34
62
  else {
35
- await new Promise<void>((resolve, reject) => this.waiting.push({ resolve, reject }));
63
+ await new Promise<void>((resolve, reject) => this.waiting.push({ resolve, reject, scope }));
36
64
  // Woken with the slot, but the body may have failed in between (see release).
37
65
  const closed = this.closedReason();
38
66
  if (closed !== undefined) {
39
67
  this.release();
40
68
  throw closed.reason;
41
69
  }
70
+ const scopeClosed = scopeClosedReason(scope);
71
+ if (scopeClosed !== undefined) {
72
+ this.release();
73
+ throw scopeClosed.reason;
74
+ }
42
75
  }
43
76
  try {
44
77
  return await work();
@@ -66,17 +99,28 @@ export class WorkerSlots {
66
99
  return;
67
100
  }
68
101
  setImmediate(() => {
69
- if (this.closed === undefined) {
102
+ if (this.closed === undefined && next.scope?.closed === undefined) {
70
103
  next.resolve();
71
104
  return;
72
105
  }
73
106
  // Closed meanwhile. `close` could not see this waiter (already taken off
74
107
  // the queue), so refuse it here, and pass the slot on instead of leaking it.
75
- next.reject(this.closed.reason);
108
+ next.reject(this.closed?.reason ?? next.scope!.closed!.reason);
76
109
  this.release();
77
110
  });
78
111
  }
79
112
 
113
+ private closeScope(scope: ScopeState, reason: unknown): void {
114
+ if (scope.closed !== undefined) return;
115
+ scope.closed = { reason };
116
+ for (let index = this.waiting.length - 1; index >= 0; index--) {
117
+ const waiter = this.waiting[index]!;
118
+ if (waiter.scope !== scope) continue;
119
+ this.waiting.splice(index, 1);
120
+ waiter.reject(reason);
121
+ }
122
+ }
123
+
80
124
  /**
81
125
  * Refuse every queued and future admission with `reason`; work already
82
126
  * holding a slot is not interrupted. The authored body calls this when it
@@ -90,3 +134,18 @@ export class WorkerSlots {
90
134
  for (const waiter of this.waiting.splice(0)) waiter.reject(reason);
91
135
  }
92
136
  }
137
+
138
+ /** Read through a call so a check after an `await` is not narrowed away. */
139
+ function scopeClosedReason(scope?: ScopeState): { readonly reason: unknown } | undefined {
140
+ return scope?.closed;
141
+ }
142
+
143
+ /** One run-tree-wide capacity pool, shared by parent and child flows. */
144
+ export interface AuthoredWorkerSlots {
145
+ readonly agent: WorkerSlots;
146
+ readonly llm: WorkerSlots;
147
+ }
148
+
149
+ export function authoredWorkerSlots(capacity: number): AuthoredWorkerSlots {
150
+ return Object.freeze({ agent: new WorkerSlots(capacity), llm: new WorkerSlots(capacity) });
151
+ }