@relayflows/sdk 2.0.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 (231) hide show
  1. package/dist/authored-flow-error.d.ts +9 -0
  2. package/dist/authored-flow-error.d.ts.map +1 -0
  3. package/dist/authored-flow-error.js +13 -0
  4. package/dist/authored-flow-error.js.map +1 -0
  5. package/dist/authored-flow-executor.d.ts +28 -0
  6. package/dist/authored-flow-executor.d.ts.map +1 -0
  7. package/dist/authored-flow-executor.js +203 -0
  8. package/dist/authored-flow-executor.js.map +1 -0
  9. package/dist/authored-flow-lifecycle.d.ts +61 -0
  10. package/dist/authored-flow-lifecycle.d.ts.map +1 -0
  11. package/dist/authored-flow-lifecycle.js +284 -0
  12. package/dist/authored-flow-lifecycle.js.map +1 -0
  13. package/dist/authored-flow-loader.d.ts +7 -0
  14. package/dist/authored-flow-loader.d.ts.map +1 -0
  15. package/dist/authored-flow-loader.js +39 -0
  16. package/dist/authored-flow-loader.js.map +1 -0
  17. package/dist/authored-flow-operation.d.ts +43 -0
  18. package/dist/authored-flow-operation.d.ts.map +1 -0
  19. package/dist/authored-flow-operation.js +176 -0
  20. package/dist/authored-flow-operation.js.map +1 -0
  21. package/dist/authored-flow.d.ts +12 -0
  22. package/dist/authored-flow.d.ts.map +1 -0
  23. package/dist/authored-flow.js +13 -0
  24. package/dist/authored-flow.js.map +1 -0
  25. package/dist/authored-promise-graph.d.ts +67 -0
  26. package/dist/authored-promise-graph.d.ts.map +1 -0
  27. package/dist/authored-promise-graph.js +206 -0
  28. package/dist/authored-promise-graph.js.map +1 -0
  29. package/dist/backlog-picker.d.ts +55 -0
  30. package/dist/backlog-picker.d.ts.map +1 -0
  31. package/dist/backlog-picker.js +117 -0
  32. package/dist/backlog-picker.js.map +1 -0
  33. package/dist/canonical.d.ts +18 -0
  34. package/dist/canonical.d.ts.map +1 -0
  35. package/dist/canonical.js +55 -0
  36. package/dist/canonical.js.map +1 -0
  37. package/dist/cli/check.d.ts +30 -0
  38. package/dist/cli/check.d.ts.map +1 -0
  39. package/dist/cli/check.js +324 -0
  40. package/dist/cli/check.js.map +1 -0
  41. package/dist/cli/direct-run.d.ts +3 -0
  42. package/dist/cli/direct-run.d.ts.map +1 -0
  43. package/dist/cli/direct-run.js +69 -0
  44. package/dist/cli/direct-run.js.map +1 -0
  45. package/dist/cli/hn-monitor.d.ts +107 -0
  46. package/dist/cli/hn-monitor.d.ts.map +1 -0
  47. package/dist/cli/hn-monitor.js +220 -0
  48. package/dist/cli/hn-monitor.js.map +1 -0
  49. package/dist/cli/interruptible-sleep.d.ts +13 -0
  50. package/dist/cli/interruptible-sleep.d.ts.map +1 -0
  51. package/dist/cli/interruptible-sleep.js +31 -0
  52. package/dist/cli/interruptible-sleep.js.map +1 -0
  53. package/dist/cli/run.d.ts +54 -0
  54. package/dist/cli/run.d.ts.map +1 -0
  55. package/dist/cli/run.js +316 -0
  56. package/dist/cli/run.js.map +1 -0
  57. package/dist/cli/tick-runner.d.ts +151 -0
  58. package/dist/cli/tick-runner.d.ts.map +1 -0
  59. package/dist/cli/tick-runner.js +258 -0
  60. package/dist/cli/tick-runner.js.map +1 -0
  61. package/dist/cli-adapter.d.ts +29 -0
  62. package/dist/cli-adapter.d.ts.map +1 -0
  63. package/dist/cli-adapter.js +92 -0
  64. package/dist/cli-adapter.js.map +1 -0
  65. package/dist/cli-executable.d.ts +3 -0
  66. package/dist/cli-executable.d.ts.map +1 -0
  67. package/dist/cli-executable.js +10 -0
  68. package/dist/cli-executable.js.map +1 -0
  69. package/dist/cli.d.ts +9 -0
  70. package/dist/cli.d.ts.map +1 -0
  71. package/dist/cli.js +348 -0
  72. package/dist/cli.js.map +1 -0
  73. package/dist/compile.d.ts +42 -0
  74. package/dist/compile.d.ts.map +1 -0
  75. package/dist/compile.js +515 -0
  76. package/dist/compile.js.map +1 -0
  77. package/dist/demo-hn-monitor.d.ts +2 -0
  78. package/dist/demo-hn-monitor.d.ts.map +1 -0
  79. package/dist/demo-hn-monitor.js +118 -0
  80. package/dist/demo-hn-monitor.js.map +1 -0
  81. package/dist/dir-watcher-poller.d.ts +73 -0
  82. package/dist/dir-watcher-poller.d.ts.map +1 -0
  83. package/dist/dir-watcher-poller.js +80 -0
  84. package/dist/dir-watcher-poller.js.map +1 -0
  85. package/dist/direct-input.d.ts +10 -0
  86. package/dist/direct-input.d.ts.map +1 -0
  87. package/dist/direct-input.js +61 -0
  88. package/dist/direct-input.js.map +1 -0
  89. package/dist/failure-kinds.d.ts +25 -0
  90. package/dist/failure-kinds.d.ts.map +1 -0
  91. package/dist/failure-kinds.js +64 -0
  92. package/dist/failure-kinds.js.map +1 -0
  93. package/dist/gate-contract.d.ts +24 -0
  94. package/dist/gate-contract.d.ts.map +1 -0
  95. package/dist/gate-contract.js +50 -0
  96. package/dist/gate-contract.js.map +1 -0
  97. package/dist/hn-poller.d.ts +50 -0
  98. package/dist/hn-poller.d.ts.map +1 -0
  99. package/dist/hn-poller.js +79 -0
  100. package/dist/hn-poller.js.map +1 -0
  101. package/dist/index.d.ts +21 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +30 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/journal-client.d.ts +122 -0
  106. package/dist/journal-client.d.ts.map +1 -0
  107. package/dist/journal-client.js +277 -0
  108. package/dist/journal-client.js.map +1 -0
  109. package/dist/json-schema-bound.d.ts +7 -0
  110. package/dist/json-schema-bound.d.ts.map +1 -0
  111. package/dist/json-schema-bound.js +353 -0
  112. package/dist/json-schema-bound.js.map +1 -0
  113. package/dist/json-schema.d.ts +5 -0
  114. package/dist/json-schema.d.ts.map +1 -0
  115. package/dist/json-schema.js +72 -0
  116. package/dist/json-schema.js.map +1 -0
  117. package/dist/json-value.d.ts +6 -0
  118. package/dist/json-value.d.ts.map +1 -0
  119. package/dist/json-value.js +94 -0
  120. package/dist/json-value.js.map +1 -0
  121. package/dist/model-name.d.ts +7 -0
  122. package/dist/model-name.d.ts.map +1 -0
  123. package/dist/model-name.js +21 -0
  124. package/dist/model-name.js.map +1 -0
  125. package/dist/output-schema.d.ts +8 -0
  126. package/dist/output-schema.d.ts.map +1 -0
  127. package/dist/output-schema.js +28 -0
  128. package/dist/output-schema.js.map +1 -0
  129. package/dist/preflight.d.ts +80 -0
  130. package/dist/preflight.d.ts.map +1 -0
  131. package/dist/preflight.js +361 -0
  132. package/dist/preflight.js.map +1 -0
  133. package/dist/protocol.d.ts +359 -0
  134. package/dist/protocol.d.ts.map +1 -0
  135. package/dist/protocol.js +15 -0
  136. package/dist/protocol.js.map +1 -0
  137. package/dist/spec.d.ts +294 -0
  138. package/dist/spec.d.ts.map +1 -0
  139. package/dist/spec.js +13 -0
  140. package/dist/spec.js.map +1 -0
  141. package/dist/step-dependencies.d.ts +2 -0
  142. package/dist/step-dependencies.d.ts.map +1 -0
  143. package/dist/step-dependencies.js +88 -0
  144. package/dist/step-dependencies.js.map +1 -0
  145. package/dist/step-fields.d.ts +17 -0
  146. package/dist/step-fields.d.ts.map +1 -0
  147. package/dist/step-fields.js +32 -0
  148. package/dist/step-fields.js.map +1 -0
  149. package/dist/tick-source.d.ts +205 -0
  150. package/dist/tick-source.d.ts.map +1 -0
  151. package/dist/tick-source.js +234 -0
  152. package/dist/tick-source.js.map +1 -0
  153. package/dist/unknown-keys.d.ts +7 -0
  154. package/dist/unknown-keys.d.ts.map +1 -0
  155. package/dist/unknown-keys.js +47 -0
  156. package/dist/unknown-keys.js.map +1 -0
  157. package/dist/validate.d.ts +7 -0
  158. package/dist/validate.d.ts.map +1 -0
  159. package/dist/validate.js +458 -0
  160. package/dist/validate.js.map +1 -0
  161. package/dist/work-package-consumer.d.ts +33 -0
  162. package/dist/work-package-consumer.d.ts.map +1 -0
  163. package/dist/work-package-consumer.js +44 -0
  164. package/dist/work-package-consumer.js.map +1 -0
  165. package/dist/work-package-validator.d.ts +15 -0
  166. package/dist/work-package-validator.d.ts.map +1 -0
  167. package/dist/work-package-validator.js +72 -0
  168. package/dist/work-package-validator.js.map +1 -0
  169. package/dist/worker-cli.d.ts +16 -0
  170. package/dist/worker-cli.d.ts.map +1 -0
  171. package/dist/worker-cli.js +77 -0
  172. package/dist/worker-cli.js.map +1 -0
  173. package/dist/worker.d.ts +57 -0
  174. package/dist/worker.d.ts.map +1 -0
  175. package/dist/worker.js +129 -0
  176. package/dist/worker.js.map +1 -0
  177. package/dist/wrapper-runtime.d.ts +10 -0
  178. package/dist/wrapper-runtime.d.ts.map +1 -0
  179. package/dist/wrapper-runtime.js +83 -0
  180. package/dist/wrapper-runtime.js.map +1 -0
  181. package/dist/wrapper-session.d.ts +16 -0
  182. package/dist/wrapper-session.d.ts.map +1 -0
  183. package/dist/wrapper-session.js +244 -0
  184. package/dist/wrapper-session.js.map +1 -0
  185. package/package.json +50 -0
  186. package/src/authored-flow-error.ts +31 -0
  187. package/src/authored-flow-executor.ts +365 -0
  188. package/src/authored-flow-lifecycle.ts +344 -0
  189. package/src/authored-flow-loader.ts +44 -0
  190. package/src/authored-flow-operation.ts +252 -0
  191. package/src/authored-flow.ts +21 -0
  192. package/src/authored-promise-graph.ts +196 -0
  193. package/src/backlog-picker.ts +153 -0
  194. package/src/canonical.ts +57 -0
  195. package/src/cli/check.ts +391 -0
  196. package/src/cli/direct-run.ts +86 -0
  197. package/src/cli/hn-monitor.ts +286 -0
  198. package/src/cli/interruptible-sleep.ts +27 -0
  199. package/src/cli/run.ts +439 -0
  200. package/src/cli/tick-runner.ts +378 -0
  201. package/src/cli-adapter.ts +115 -0
  202. package/src/cli-executable.ts +12 -0
  203. package/src/cli.ts +364 -0
  204. package/src/compile.ts +573 -0
  205. package/src/demo-hn-monitor.ts +132 -0
  206. package/src/dir-watcher-poller.ts +122 -0
  207. package/src/direct-input.ts +69 -0
  208. package/src/failure-kinds.ts +77 -0
  209. package/src/gate-contract.ts +72 -0
  210. package/src/hn-poller.ts +104 -0
  211. package/src/index.ts +195 -0
  212. package/src/journal-client.ts +379 -0
  213. package/src/json-schema-bound.ts +357 -0
  214. package/src/json-schema.ts +65 -0
  215. package/src/json-value.ts +110 -0
  216. package/src/model-name.ts +20 -0
  217. package/src/output-schema.ts +33 -0
  218. package/src/preflight.ts +482 -0
  219. package/src/protocol.ts +364 -0
  220. package/src/spec.ts +343 -0
  221. package/src/step-dependencies.ts +100 -0
  222. package/src/step-fields.ts +36 -0
  223. package/src/tick-source.ts +334 -0
  224. package/src/unknown-keys.ts +52 -0
  225. package/src/validate.ts +503 -0
  226. package/src/work-package-consumer.ts +73 -0
  227. package/src/work-package-validator.ts +90 -0
  228. package/src/worker-cli.ts +110 -0
  229. package/src/worker.ts +150 -0
  230. package/src/wrapper-runtime.ts +93 -0
  231. package/src/wrapper-session.ts +287 -0
@@ -0,0 +1,364 @@
1
+ // Journal protocol v0 — the SDK boundary (kernel DESIGN.md §5).
2
+ //
3
+ // Transport: newline-delimited JSON over a unix socket at
4
+ // `<data-dir>/relayflowd.sock`. Requests `{id, verb, params}`; responses
5
+ // `{id, ok: true, result}` or `{id, ok: false, error: {code, message}}`;
6
+ // server-pushed events `{event, data}` (no `id`). Any verb whose journal
7
+ // append fails returns `error{code: "journal_write_failed"}` and the affected
8
+ // step fails — the protocol is fail-closed like everything behind it.
9
+ //
10
+ // This module is the typed wire surface; `journal-client.ts` implements it.
11
+
12
+ import type { KernelRunSpec, StepType } from './spec.js';
13
+
14
+ /** Stamped per segment; readers read every past version, writers write newest. */
15
+ export const PROTOCOL_VERSION = 0 as const;
16
+
17
+ /** A journal append that fails fails the step (AGENTS.md rule 4). */
18
+ export const JOURNAL_WRITE_FAILED = 'journal_write_failed' as const;
19
+
20
+ export type ProtocolError = { code: string; message: string };
21
+
22
+ /** Client -> server. */
23
+ export interface Request<P = unknown> {
24
+ id: string;
25
+ verb: string;
26
+ params: P;
27
+ }
28
+
29
+ /** Server -> client response, correlated by `id`. */
30
+ export type Response<R = unknown> =
31
+ | { id: string; ok: true; result: R }
32
+ | { id: string; ok: false; error: ProtocolError };
33
+
34
+ /** Server -> client push (no `id`). */
35
+ export interface ServerEvent {
36
+ event: string;
37
+ data: unknown;
38
+ }
39
+
40
+ // --- Verb set (gate 1 minimal) ---------------------------------------------
41
+ // Verb names mirror kernel DESIGN.md §5 verbatim.
42
+
43
+ export type Verb =
44
+ | 'hello'
45
+ | 'run.start'
46
+ | 'run.resume'
47
+ | 'run.cancel'
48
+ | 'run.get'
49
+ | 'run.watch'
50
+ | 'worker.attach'
51
+ | 'step.heartbeat'
52
+ | 'effect.record'
53
+ | 'effect.confirm'
54
+ | 'step.complete'
55
+ | 'event.emit'
56
+ | 'event.submit'
57
+ | 'stream.append'
58
+ | 'stream.read'
59
+ | 'journal.read';
60
+
61
+ // --- Typed params / results -------------------------------------------------
62
+
63
+ export interface HelloParams {
64
+ protocol: 0;
65
+ client: string;
66
+ }
67
+ export interface HelloResult {
68
+ protocol: 0;
69
+ server: string;
70
+ }
71
+
72
+ export interface RunStartParams {
73
+ /**
74
+ * The kernel spec dialect — the ONE boundary shape `RunSpec::parse`
75
+ * accepts (snake_case, flat v0 verification, defaults materialized).
76
+ * The authoring `FlowSpec` never crosses the wire; `JournalClient.runStart`
77
+ * converts via `toKernelSpec`.
78
+ */
79
+ spec: KernelRunSpec;
80
+ }
81
+ export type RunStatus = 'running' | 'completed' | 'failed' | 'interrupted' | 'parked';
82
+ export type RunCompletionReason = 'success' | 'step_failed' | 'canceled' | 'budget_exceeded';
83
+
84
+ export interface RunOutcome {
85
+ run_id: string;
86
+ status: RunStatus;
87
+ completion_reason: RunCompletionReason | null;
88
+ completed_steps: number;
89
+ }
90
+ export type RunStartResult = RunOutcome;
91
+
92
+ export interface RunResumeParams {
93
+ run_id: string;
94
+ }
95
+ export type RunResumeResult = RunOutcome;
96
+
97
+ export interface RunCancelParams {
98
+ run_id: string;
99
+ }
100
+ export type RunCancelResult = RunOutcome;
101
+
102
+ export interface RunGetParams {
103
+ run_id: string;
104
+ }
105
+ export type StepStatus =
106
+ | 'pending'
107
+ | 'runnable'
108
+ | 'running'
109
+ | 'backoff'
110
+ | 'waiting'
111
+ | 'needs_human'
112
+ | 'done';
113
+ export interface StepSnapshot {
114
+ type: StepType;
115
+ state: StepStatus;
116
+ lease_deadline_ms?: number;
117
+ }
118
+ export interface RunGetResult {
119
+ run_id: string;
120
+ status: RunStatus;
121
+ steps: Record<string, StepSnapshot>;
122
+ budget: { tokens_in: number; tokens_out: number; dollars: string };
123
+ }
124
+
125
+ export interface RunWatchParams {
126
+ run_id: string;
127
+ }
128
+ export interface RunWatchResult {
129
+ watching: string;
130
+ }
131
+ /** `run.watch` opens a push stream of `{event: "entry", data: Entry}`. */
132
+
133
+ export interface WorkerAttachParams {
134
+ worker_id: string;
135
+ step_types: StepType[];
136
+ /** Maximum concurrent assignments. Omitted means the conservative default 1. */
137
+ capacity?: number;
138
+ /**
139
+ * The surfaces this worker holds, as opaque revisions/offsets. Required when
140
+ * `step_types` includes `agent` — an agent attempt's start pins come from
141
+ * here (Appendix A rule 2), so an agent worker with no pins is refused at
142
+ * attach rather than failing in the middle of a run.
143
+ */
144
+ pins?: Pins;
145
+ }
146
+ export interface WorkerAttachResult {
147
+ worker_id: string;
148
+ }
149
+ /**
150
+ * Wake-context payload assembled kernel-side at `subscription.matched`.
151
+ * The two fields typed below are the ones every current consumer keys
152
+ * against; additional kernel-side fields flow through as extra
153
+ * properties without breaking this type.
154
+ */
155
+ export interface WakeContext {
156
+ triggering_event?: {
157
+ type: string;
158
+ payload?: unknown;
159
+ };
160
+ epoch_summary?: {
161
+ open_steps?: string[];
162
+ };
163
+ [additionalKernelFields: string]: unknown;
164
+ }
165
+
166
+ /** Server then pushes `step.dispatch` events to the attached worker. */
167
+ export interface StepDispatchEvent {
168
+ run_id: string;
169
+ step_id: string;
170
+ attempt: number;
171
+ step_type: StepType;
172
+ spec: unknown;
173
+ lease_id: string;
174
+ idempotency_key: string;
175
+ pins: Pins;
176
+ /**
177
+ * The `wake_context` payload from the run's `subscription.matched`
178
+ * journal entry — carries the triggering event and any epoch summary
179
+ * the flow needs to know why this step is running. Present when the
180
+ * run was spawned by an event; `undefined` for runs started directly
181
+ * (no trigger fired). A real agent needs this to see the event
182
+ * payload (e.g. the HN story ID).
183
+ *
184
+ * Shape is assembled kernel-side in
185
+ * `kernel/relayflowd/src/engine/wake.rs` (grep for
186
+ * `wake_context` — the Rust field is written with quoted
187
+ * JSON keys so `"wake_context":` hits the assembly site
188
+ * directly). The narrow type below pins the two fields
189
+ * every consumer currently reads —
190
+ * `triggering_event.type`/`payload` and `epoch_summary` — while
191
+ * keeping the container `unknown`-permissive so a kernel-side
192
+ * addition (a new nested field) does not break the SDK type. A
193
+ * kernel-side RENAME of these two fields does break every
194
+ * consumer; that is deliberate and preferable to silent drift.
195
+ */
196
+ wake_context?: WakeContext;
197
+ recovery?: {
198
+ mode: 'reset' | 'inspect' | 'manual';
199
+ restore_pins?: Pins;
200
+ previous_completion_reason?: CompletionReason;
201
+ trajectory_tail?: unknown;
202
+ };
203
+ lease_deadline_ms: number;
204
+ }
205
+
206
+ export interface StepHeartbeatParams {
207
+ run_id: string;
208
+ step_id: string;
209
+ attempt: number;
210
+ lease_id: string;
211
+ }
212
+ export interface StepHeartbeatResult {
213
+ lease_deadline_ms: number;
214
+ }
215
+
216
+ /** completionReason mirrors kernel DESIGN.md §1.3. */
217
+ export type CompletionReason =
218
+ | 'success'
219
+ | 'verification_failed'
220
+ | 'retries_exhausted'
221
+ | 'lease_expired'
222
+ | 'crashed'
223
+ | 'timeout'
224
+ | 'worker_error'
225
+ | 'budget_exceeded'
226
+ | 'canceled';
227
+
228
+ export interface Pins {
229
+ workspace?: { surface: string; revision_id: string }[];
230
+ streams?: { stream: string; read_offset: number }[];
231
+ }
232
+
233
+ export interface EffectRef {
234
+ surface_path: string;
235
+ idempotency_key: string;
236
+ }
237
+
238
+ export interface EffectRecordParams {
239
+ run_id: string;
240
+ step_id: string;
241
+ attempt: number;
242
+ idempotency_key: string;
243
+ surface_path: string;
244
+ revision_before: string;
245
+ revision_after: string;
246
+ }
247
+ export interface EffectRecordResult {
248
+ /**
249
+ * True only when a *confirmed* election already covers this
250
+ * `(step_id, idempotency_key, surface_path)`: the writeback provably
251
+ * happened, so this attempt must not call the provider. An election that was
252
+ * never confirmed does not dedupe — this attempt reclaims it and owes the
253
+ * call, which is what keeps an effect from being lost to a worker that died
254
+ * between recording and performing.
255
+ */
256
+ deduped: boolean;
257
+ }
258
+
259
+ /** Phase two: the elected attempt performed the writeback. */
260
+ export interface EffectConfirmParams {
261
+ run_id: string;
262
+ step_id: string;
263
+ attempt: number;
264
+ idempotency_key: string;
265
+ surface_path: string;
266
+ }
267
+ export interface EffectConfirmResult {
268
+ confirmed: string;
269
+ }
270
+
271
+ export interface StepCompleteParams {
272
+ run_id: string;
273
+ step_id: string;
274
+ attempt: number;
275
+ idempotency_key: string;
276
+ completionReason: CompletionReason;
277
+ output?: unknown;
278
+ usage?: { tokens_in: number; tokens_out: number; dollars: string };
279
+ started_pins?: Pins;
280
+ end_pins?: Pins;
281
+ effects?: EffectRef[];
282
+ /** `inspect` evidence for the next attempt. Rejected over 16 KiB of JSON. */
283
+ trajectory_tail?: unknown;
284
+ }
285
+ export type StepCompleteResult = RunOutcome;
286
+
287
+ export interface EventEmitParams {
288
+ run_id: string;
289
+ event_key: string;
290
+ payload: unknown;
291
+ }
292
+ export interface EventEmitResult {
293
+ matched: number;
294
+ }
295
+
296
+ /**
297
+ * Submit an external event to a flow that declares an event trigger. The
298
+ * kernel matches it against the spec's subscriptions and, on a first match,
299
+ * spawns a run; a repeat of the same (flow, subscription, key) is deduped.
300
+ */
301
+ export interface EventSubmitParams {
302
+ spec: unknown;
303
+ event: {
304
+ type: string;
305
+ payload?: unknown;
306
+ /** Overrides the subscription's dedupeKeyTemplate when supplied. */
307
+ key?: string;
308
+ };
309
+ }
310
+ export interface EventSubmitResult {
311
+ matched: boolean;
312
+ deduped: boolean;
313
+ subscription_id?: string | null;
314
+ run?: unknown;
315
+ }
316
+
317
+ export interface StreamAppendParams {
318
+ run_id: string;
319
+ stream: string;
320
+ message: unknown;
321
+ }
322
+ export interface StreamAppendResult {
323
+ offset: number;
324
+ }
325
+
326
+ export interface StreamReadParams {
327
+ run_id: string;
328
+ stream: string;
329
+ from_offset: number;
330
+ limit?: number;
331
+ }
332
+ export interface StreamReadResult {
333
+ messages: unknown[];
334
+ next_offset: number;
335
+ }
336
+
337
+ export interface JournalReadParams {
338
+ run_id: string;
339
+ from_seq: number;
340
+ limit?: number;
341
+ }
342
+ export interface JournalReadResult {
343
+ entries: unknown[];
344
+ }
345
+
346
+ /** Typed map of verb -> { params, result }. Used by the client for type-safety. */
347
+ export interface VerbContract {
348
+ hello: { params: HelloParams; result: HelloResult };
349
+ 'run.start': { params: RunStartParams; result: RunStartResult };
350
+ 'run.resume': { params: RunResumeParams; result: RunResumeResult };
351
+ 'run.cancel': { params: RunCancelParams; result: RunCancelResult };
352
+ 'run.get': { params: RunGetParams; result: RunGetResult };
353
+ 'run.watch': { params: RunWatchParams; result: RunWatchResult };
354
+ 'worker.attach': { params: WorkerAttachParams; result: WorkerAttachResult };
355
+ 'step.heartbeat': { params: StepHeartbeatParams; result: StepHeartbeatResult };
356
+ 'effect.record': { params: EffectRecordParams; result: EffectRecordResult };
357
+ 'effect.confirm': { params: EffectConfirmParams; result: EffectConfirmResult };
358
+ 'step.complete': { params: StepCompleteParams; result: StepCompleteResult };
359
+ 'event.emit': { params: EventEmitParams; result: EventEmitResult };
360
+ 'event.submit': { params: EventSubmitParams; result: EventSubmitResult };
361
+ 'stream.append': { params: StreamAppendParams; result: StreamAppendResult };
362
+ 'stream.read': { params: StreamReadParams; result: StreamReadResult };
363
+ 'journal.read': { params: JournalReadParams; result: JournalReadResult };
364
+ }
package/src/spec.ts ADDED
@@ -0,0 +1,343 @@
1
+ // Spec types for Relayflows — data, not code (RFC-0001 settled decision #5).
2
+ //
3
+ // Mirrors RFC-0001 §1's ladder. Every rung is a legal relayflow:
4
+ // deterministic step # a pure script — no LLM anywhere (legal)
5
+ // + llm step # a bare model call — prompt in, verified output out
6
+ // + agent step # a harnessed agent in a workspace — artifact + diff + trajectory
7
+ //
8
+ // Step types are exactly `deterministic | llm | agent` (AGENTS.md rule 7).
9
+ // Zero-agent flows are legal: a spec with only `deterministic` (and/or `llm`)
10
+ // steps is valid. Nothing here requires an `agent` step.
11
+
12
+ import type { JsonOutputSchema } from './output-schema.js';
13
+ export type { JsonOutputSchema } from './output-schema.js';
14
+
15
+ /** The three rungs of the ladder (RFC §1; AGENTS.md rule 7). */
16
+ export type StepType = 'deterministic' | 'llm' | 'agent';
17
+
18
+ /**
19
+ * Verification is control flow, not decoration (kernel DESIGN.md §3).
20
+ * v0 gates are deterministic so verification is kernel-side and replayable.
21
+ */
22
+ export type VerificationGateType = 'exit_code' | 'output_contains' | 'json_schema';
23
+
24
+ /**
25
+ * `exit_code == 0` — the implicit gate for deterministic steps. v0 judges
26
+ * exactly zero (kernel DESIGN.md §4); it is not configurable, so this gate
27
+ * carries no parameters. Writing it explicitly is allowed and compiles to the
28
+ * same kernel spec as omitting it.
29
+ */
30
+ export interface ExitCodeGate {
31
+ type: 'exit_code';
32
+ }
33
+
34
+ /** Step output (stdout_tail / llm value, stringified) contains `value`. */
35
+ export interface OutputContainsGate {
36
+ type: 'output_contains';
37
+ value: string;
38
+ }
39
+
40
+ /** Step output validates against a JSON Schema. Used for `llm` structured output. */
41
+ export interface JsonSchemaGate {
42
+ type: 'json_schema';
43
+ schema: boolean | Record<string, unknown>;
44
+ }
45
+
46
+ export type VerificationSpec = ExitCodeGate | OutputContainsGate | JsonSchemaGate;
47
+ export type OutputVerificationSpec = OutputContainsGate | JsonSchemaGate;
48
+
49
+ /**
50
+ * Agent-step recovery modes (RFC Appendix A rule 4). Default is `reset`.
51
+ * `deterministic` / `llm` steps have no workspace, so these do not apply.
52
+ */
53
+ export type RecoveryMode = 'reset' | 'inspect' | 'manual';
54
+
55
+ /**
56
+ * Declared mutable surfaces for an agent step (RFC Appendix A rule 1).
57
+ * Anything undeclared is outside the contract and outside the step's
58
+ * permissions (gate 8 makes this enforceable, not advisory).
59
+ */
60
+ export interface WorkspaceSurface {
61
+ /** Relayfile mount path, or a named worktree. */
62
+ surface: string;
63
+ }
64
+
65
+ export interface StreamSurface {
66
+ /** Durable channel the agent may write (kernel DESIGN.md §1.8). */
67
+ stream: string;
68
+ }
69
+
70
+ export interface AgentSurfaces {
71
+ workspace?: WorkspaceSurface[];
72
+ streams?: StreamSurface[];
73
+ /** Integration writeback paths — mount writes per gate 6. */
74
+ external?: string[];
75
+ }
76
+
77
+ /** Permission model for an agent step (gate 8). `readonly` provably cannot write. */
78
+ export interface PermissionsSpec {
79
+ fileGlobs?: string[];
80
+ networkAllowlist?: string[];
81
+ accessPreset?: 'readonly' | 'readwrite';
82
+ }
83
+
84
+ /**
85
+ * Budget envelope. Per decision #10 every token has exactly one owner: an
86
+ * injected context pack spends the consuming step's budget. Money is a decimal
87
+ * string at the boundary (kernel DESIGN.md §1: no floats for money); tokens
88
+ * are integers.
89
+ */
90
+ export interface BudgetSpec {
91
+ maxTokensIn?: number;
92
+ maxTokensOut?: number;
93
+ /** Decimal string, e.g. "1.50". */
94
+ maxDollars?: string;
95
+ }
96
+
97
+ /** Fields shared by every step on the ladder. */
98
+ export interface BaseStepSpec {
99
+ /** Stable step identity; journaled as `step_id` and hashed into the idempotency key. */
100
+ id: string;
101
+ type: StepType;
102
+ /** Step dependencies — a step runs only after these complete. */
103
+ dependsOn?: string[];
104
+ /** Semantic retry bound (kernel DESIGN.md §1.2 `max_iterations`). Default 1. */
105
+ maxIterations?: number;
106
+ }
107
+
108
+ /**
109
+ * Rung 1 — a pure script. Executed by the `relayflowd` binary: spawn command,
110
+ * capture stdout/exit code. Output = `{exit_code, stdout_tail}`. Gate-1
111
+ * deterministic steps are pure (no pins).
112
+ */
113
+ export interface DeterministicStepSpec extends BaseStepSpec {
114
+ type: 'deterministic';
115
+ command: string;
116
+ /** Wall-clock command timeout; worker-backed verbs own their dispatch timeout. */
117
+ timeoutMs?: number;
118
+ /** Omit to get the implicit `exit_code` gate. */
119
+ verification?: VerificationSpec;
120
+ }
121
+
122
+ /**
123
+ * Rung 2 — a bare model call. No workspace, output is a value. The kernel
124
+ * never calls a model: it dispatches to an attached SDK worker (§5) which
125
+ * returns `{output, usage}`; the kernel then runs the verification gate.
126
+ */
127
+ export interface LlmStepSpec extends BaseStepSpec {
128
+ type: 'llm';
129
+ prompt: string;
130
+ verification?: OutputVerificationSpec;
131
+ model?: string;
132
+ /** Inert preflight declaration; overrides the flow/project CLI default. */
133
+ cli?: string;
134
+ /**
135
+ * Structured-output authoring sugar. Compiles to the existing `json_schema`
136
+ * verification primitive and is removed before the kernel boundary.
137
+ */
138
+ output?: JsonOutputSchema;
139
+ }
140
+
141
+ /**
142
+ * Rung 3 — a harnessed agent in a workspace. Dispatched like `llm`, plus
143
+ * Appendix A in full: pins declared workspace revisions and stream offsets;
144
+ * every writeback is a journaled `effect.recorded` deduped by
145
+ * `(step_id, idempotency_key, surface_path)`.
146
+ */
147
+ export interface AgentStepSpec extends BaseStepSpec {
148
+ type: 'agent';
149
+ instruction: string;
150
+ verification?: OutputVerificationSpec;
151
+ /** Named authoring declaration selected from `FlowSpec.agents`. Compiled away. */
152
+ agent?: string;
153
+ /** Inert preflight declaration; overrides the flow/project CLI default. */
154
+ cli?: string;
155
+ /**
156
+ * Model the declared CLI must use. Raw Claude/Codex adapters receive their
157
+ * real model flag; an identified Relayflows wrapper receives it in its
158
+ * same-process execution request. Declared here so the choice is journaled with the step
159
+ * instead of being ambient host state.
160
+ */
161
+ model?: string;
162
+ surfaces?: AgentSurfaces;
163
+ recoveryMode?: RecoveryMode;
164
+ permissions?: PermissionsSpec;
165
+ /**
166
+ * Structured-output authoring sugar. A successful CLI JSON object is the parsed
167
+ * value; the kernel persists it only after `json_schema` verification.
168
+ */
169
+ output?: JsonOutputSchema;
170
+ }
171
+
172
+ export type StepSpec = DeterministicStepSpec | LlmStepSpec | AgentStepSpec;
173
+
174
+ /**
175
+ * Reusable authoring declaration for an agent CLI/model pair. Both fields are
176
+ * required so selecting a named agent can never inherit a host model. The
177
+ * compiler lowers these values into the selected kernel agent step at the
178
+ * journal boundary; the kernel never receives this map or a new step field.
179
+ */
180
+ export interface NamedAgentSpec {
181
+ cli: string;
182
+ model: string;
183
+ }
184
+
185
+ /**
186
+ * A trigger is an entry condition, not a scheduler (RFC-0001 gate 2). It names
187
+ * the event type that wakes the flow, the payload subset that must match, the
188
+ * template that derives the dedupe key, and the silence budget after which the
189
+ * kernel's liveness sweep declares the subscription dead.
190
+ *
191
+ * The event-subscription fields were shipped in `testdata/` long before this
192
+ * interface described them: `hn-monitor.flow.yaml`, `dir-watcher.flow.yaml`
193
+ * and `event-triggered-flow.yaml` all carry `eventType`, `pattern` and
194
+ * `dedupeKeyTemplate`, and `validate.ts` has always accepted them. The type
195
+ * still said "inert gate-1 declaration" with only `id` and `executor`, so the
196
+ * authoring dialect disagreed with both the shipped specs and the kernel.
197
+ */
198
+ export interface TriggerSpec {
199
+ id: string;
200
+ /** Executor registration required before this trigger may start a run. */
201
+ executor: string;
202
+ /** Event type this trigger subscribes to. Lowers to `event_type`. */
203
+ eventType?: string;
204
+ /** Recursive-subset match against the event payload. Lowers to `pattern`. */
205
+ pattern?: Record<string, unknown>;
206
+ /** Derives the dedupe key. Lowers to `dedupe_key_template`. */
207
+ dedupeKeyTemplate?: string;
208
+ /**
209
+ * Silence budget in milliseconds. When no matching event arrives inside it,
210
+ * the kernel's liveness sweep journals `subscription.stale` and emits a
211
+ * `relayflowd: subscription.stale ...` line
212
+ * (`kernel/relayflowd/src/server/liveness.rs`). Omitted means the engine
213
+ * default (`DEFAULT_STALE_AFTER_MS`, 5 minutes) applies — which is a
214
+ * decision the author did not make, not the absence of a budget.
215
+ *
216
+ * A flow that is never triggered is silently zero (RFC-0001 §"Trigger
217
+ * liveness"), so declaring this is how a schedule stops being able to die
218
+ * quietly. Lowers to `stale_after_ms`.
219
+ */
220
+ staleAfterMs?: number;
221
+ }
222
+
223
+ /**
224
+ * A Relayflow spec in the authoring shape — the composable unit (RFC settled
225
+ * decision #5). Schema-validated, diffable, signable (gate 8), and emittable
226
+ * by a step (gate 9 self-authoring). What the kernel inlines in `run.spawned`
227
+ * is this spec mapped to the kernel dialect (`toKernelSpec`).
228
+ */
229
+ export interface FlowSpec {
230
+ /** Spec schema semver (RFC §7). Compilers always emit latest. */
231
+ version: string;
232
+ name?: string;
233
+ description?: string;
234
+ /** Inert preflight default for llm/agent steps that do not declare a CLI. */
235
+ cli?: string;
236
+ /** Named authoring declarations. Compiled into agent steps, never journaled as a new primitive. */
237
+ agents?: Record<string, NamedAgentSpec>;
238
+ /** Declarations checked by preflight; gate 1 never dispatches them. */
239
+ triggers?: TriggerSpec[];
240
+ steps: StepSpec[];
241
+ budget?: BudgetSpec;
242
+ }
243
+
244
+ /** Current spec schema version emitted by this SDK. */
245
+ export const SPEC_SCHEMA_VERSION = '0.1.0';
246
+
247
+ // --- The kernel dialect ------------------------------------------------------
248
+ //
249
+ // The authoring types above are TypeScript-idiomatic (camelCase, tagged
250
+ // verification sugar). The *boundary artifact* is singular: the kernel's spec
251
+ // dialect — snake_case keys, semver `version`, flat v0 verification, defaults
252
+ // materialized. `toKernelSpec` (compile.ts) maps authoring → kernel; parity
253
+ // with `kernel/relayflowd-core/src/spec.rs` is pinned bit-for-bit by
254
+ // `tests/spec-parity.test.ts` and the kernel's `tests/spec_parity.rs` over the
255
+ // shared `testdata/` fixture.
256
+
257
+ export interface KernelRetryPolicy {
258
+ initial_backoff_ms: number;
259
+ max_backoff_ms: number;
260
+ multiplier: number;
261
+ jitter_percent: number;
262
+ }
263
+
264
+ /**
265
+ * Flat v0 gates (kernel DESIGN.md §4): `exit_code == 0` is implicit for
266
+ * deterministic steps; these two are optional and combinable. An empty object
267
+ * means "implicit gates only".
268
+ */
269
+ export interface KernelVerificationSpec {
270
+ output_contains?: string;
271
+ json_schema?: boolean | Record<string, unknown>;
272
+ }
273
+
274
+ export interface KernelStepCommon {
275
+ id: string;
276
+ depends_on: string[];
277
+ max_iterations: number;
278
+ retry: KernelRetryPolicy;
279
+ verification: KernelVerificationSpec;
280
+ }
281
+
282
+ export interface KernelDeterministicStep extends KernelStepCommon {
283
+ type: 'deterministic';
284
+ command: string;
285
+ timeout_ms?: number;
286
+ }
287
+
288
+ export interface KernelLlmStep extends KernelStepCommon {
289
+ type: 'llm';
290
+ prompt: string;
291
+ model?: string;
292
+ cli?: string;
293
+ }
294
+
295
+ export interface KernelAgentSurfaces {
296
+ workspace?: { surface: string }[];
297
+ streams?: { stream: string }[];
298
+ external?: string[];
299
+ }
300
+
301
+ export interface KernelPermissionsSpec {
302
+ file_globs?: string[];
303
+ network_allowlist?: string[];
304
+ access_preset?: 'readonly' | 'readwrite';
305
+ }
306
+
307
+ export interface KernelAgentStep extends KernelStepCommon {
308
+ type: 'agent';
309
+ instruction: string;
310
+ cli?: string;
311
+ model?: string;
312
+ recovery_mode: RecoveryMode;
313
+ surfaces?: KernelAgentSurfaces;
314
+ permissions?: KernelPermissionsSpec;
315
+ }
316
+
317
+ export type KernelStepSpec = KernelDeterministicStep | KernelLlmStep | KernelAgentStep;
318
+
319
+ export interface KernelBudgetSpec {
320
+ max_tokens_in?: number;
321
+ max_tokens_out?: number;
322
+ max_dollars?: string;
323
+ }
324
+
325
+ export interface KernelTriggerSpec {
326
+ id: string;
327
+ executor: string;
328
+ event_type?: string;
329
+ pattern?: Record<string, unknown>;
330
+ dedupe_key_template?: string;
331
+ stale_after_ms?: number;
332
+ }
333
+
334
+ /** The compiled spec as the kernel parses, journals, and hashes it. */
335
+ export interface KernelRunSpec {
336
+ version: string;
337
+ name?: string;
338
+ description?: string;
339
+ cli?: string;
340
+ triggers?: KernelTriggerSpec[];
341
+ steps: KernelStepSpec[];
342
+ budget?: KernelBudgetSpec;
343
+ }