@telorun/sdk 0.75.0 → 0.79.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 (64) hide show
  1. package/dist/cancellation.d.ts +50 -1
  2. package/dist/cancellation.d.ts.map +1 -1
  3. package/dist/contract-errors.d.ts +8 -1
  4. package/dist/contract-errors.d.ts.map +1 -1
  5. package/dist/contract-errors.js +8 -0
  6. package/dist/durable-run.d.ts +310 -0
  7. package/dist/durable-run.d.ts.map +1 -0
  8. package/dist/durable-run.js +223 -0
  9. package/dist/durable-suspension.d.ts +143 -0
  10. package/dist/durable-suspension.d.ts.map +1 -0
  11. package/dist/durable-suspension.js +153 -0
  12. package/dist/durable-target-encoding.d.ts +49 -0
  13. package/dist/durable-target-encoding.d.ts.map +1 -0
  14. package/dist/durable-target-encoding.js +121 -0
  15. package/dist/duration.d.ts +1 -1
  16. package/dist/duration.js +5 -5
  17. package/dist/evaluation-context.d.ts +16 -0
  18. package/dist/evaluation-context.d.ts.map +1 -1
  19. package/dist/index.d.ts +5 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5 -0
  22. package/dist/invoke-step.d.ts +86 -1
  23. package/dist/invoke-step.d.ts.map +1 -1
  24. package/dist/invoke-step.js +261 -18
  25. package/dist/resource-context.d.ts +23 -0
  26. package/dist/resource-context.d.ts.map +1 -1
  27. package/dist/resource-instance.d.ts +21 -1
  28. package/dist/resource-instance.d.ts.map +1 -1
  29. package/dist/resource-instance.js +6 -2
  30. package/dist/step-engine.d.ts +170 -0
  31. package/dist/step-engine.d.ts.map +1 -0
  32. package/dist/step-engine.js +365 -0
  33. package/dist/type-schema-ref.d.ts.map +1 -1
  34. package/dist/type-schema-ref.js +18 -0
  35. package/dist/zone-attribute.d.ts +101 -0
  36. package/dist/zone-attribute.d.ts.map +1 -0
  37. package/dist/zone-attribute.js +130 -0
  38. package/dist/zone-attributes/entries/atomic.json +7 -0
  39. package/dist/zone-attributes/entries/idempotent.json +6 -0
  40. package/dist/zone-attributes/entries/index.d.ts +3 -0
  41. package/dist/zone-attributes/entries/index.d.ts.map +1 -0
  42. package/dist/zone-attributes/entries/index.js +13 -0
  43. package/dist/zone-attributes/entries/no-suspend.json +6 -0
  44. package/dist/zone-attributes/entries/replayed.json +6 -0
  45. package/package.json +1 -1
  46. package/src/cancellation.ts +50 -1
  47. package/src/contract-errors.ts +9 -0
  48. package/src/durable-run.ts +450 -0
  49. package/src/durable-suspension.ts +188 -0
  50. package/src/durable-target-encoding.ts +181 -0
  51. package/src/duration.ts +5 -5
  52. package/src/evaluation-context.ts +17 -0
  53. package/src/index.ts +5 -1
  54. package/src/invoke-step.ts +378 -24
  55. package/src/resource-context.ts +23 -0
  56. package/src/resource-instance.ts +32 -2
  57. package/src/step-engine.ts +627 -0
  58. package/src/type-schema-ref.ts +16 -0
  59. package/src/zone-attribute.ts +208 -0
  60. package/src/zone-attributes/entries/atomic.json +7 -0
  61. package/src/zone-attributes/entries/idempotent.json +6 -0
  62. package/src/zone-attributes/entries/index.ts +14 -0
  63. package/src/zone-attributes/entries/no-suspend.json +6 -0
  64. package/src/zone-attributes/entries/replayed.json +6 -0
@@ -1,3 +1,4 @@
1
+ import type { DurableRunHandle } from "./durable-run.js";
1
2
  import type { ResourceHandle } from "./resource-instance.js";
2
3
  /**
3
4
  * Cooperative invocation cancellation — the standard source/token split.
@@ -66,6 +67,49 @@ export interface InvokeContext {
66
67
  readonly traceId?: string;
67
68
  /** Zones open around this invocation, outermost first. Absent = none. */
68
69
  readonly zones?: readonly ZoneEntry[];
70
+ /**
71
+ * The durable run this invocation is executing inside, when there is one —
72
+ * the replay seam the step engine journals through
73
+ * (`kernel/specs/durable-execution.md`).
74
+ *
75
+ * **Its own member, not a `ZoneEntry` payload.** The durable zone IS a real
76
+ * zone and rides the stack above, but an entry is three identities *because*
77
+ * that keeps it ABI-serializable and stops any controller reading another
78
+ * module's open state off the stack. A run handle is a live object with
79
+ * methods, and hanging it on the entry would trade that property away for
80
+ * every zone, durable or not. The landed payload rule (provider-private state
81
+ * lives on an instance injected across the boundary) cannot carry it either —
82
+ * a nested `Run.Sequence` holds no durable reference, so a sequence two levels
83
+ * down has no injected instance to read from.
84
+ *
85
+ * The consequence, stated rather than implied: unlike {@link zones}, this
86
+ * member does **not** cross the ABI. The kernel is a pure conduit — it carries
87
+ * the handle and never calls it — so a second runtime threads a handle it
88
+ * owns rather than deserializing this one.
89
+ *
90
+ * Picked up by every nested dispatch, which is what makes nesting work with no
91
+ * per-module effort: a nested sequence, in this module or across an import
92
+ * boundary, journals its steps under the outer step's path, so a crash inside
93
+ * it resumes inside it.
94
+ */
95
+ readonly durable?: DurableRunHandle;
96
+ /**
97
+ * The journal path of the step whose dispatch led here, when this invocation
98
+ * is inside a durable run.
99
+ *
100
+ * The other half of carriage, and without it nesting is silently wrong rather
101
+ * than merely unsupported: a nested step body that started its own paths at
102
+ * `steps` would record `steps/<name>` for every body in the run, so two nested
103
+ * bodies with a same-named step share one key. First-writer-wins then hands
104
+ * the second the first's RESULT — and when both dispatch the same target there
105
+ * is no mismatch to detect, so the run continues with a value produced for a
106
+ * different step.
107
+ *
108
+ * A nested engine reads this as the base its own step paths hang under, which
109
+ * is what makes "a crash inside a nested body resumes inside it" true. Absent
110
+ * at the top of a run, where the base is `steps`.
111
+ */
112
+ readonly durablePath?: string;
69
113
  }
70
114
  /**
71
115
  * The ONE way to build a context derived from another. A fresh object literal
@@ -77,7 +121,12 @@ export interface InvokeContext {
77
121
  */
78
122
  export declare function deriveContext(base: InvokeContext, overrides: Partial<InvokeContext>): InvokeContext;
79
123
  /** Terminal status of a span — maps to OpenTelemetry span status. */
80
- export type SpanOutcome = "ok" | "failed" | "rejected" | "cancelled";
124
+ /** How an invocation ended.
125
+ *
126
+ * `parked` is its own outcome and not a flavour of failure: a suspended
127
+ * invocation neither succeeded nor failed, and recording it as failed would
128
+ * make a trace say the run broke every time it waited. */
129
+ export type SpanOutcome = "ok" | "failed" | "rejected" | "cancelled" | "parked";
81
130
  /** Options for {@link ResourceContext.openSpan}. */
82
131
  export interface OpenSpanOptions {
83
132
  /** The resource the span is attributed to (an inbound transport's listener,
@@ -1 +1 @@
1
- {"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../src/cancellation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../src/cancellation.ts"],"names":[],"mappings":"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"}
@@ -22,7 +22,14 @@ export declare const ERR_OUTPUT_INVALID = "ERR_OUTPUT_INVALID";
22
22
  * data, the contract itself is unusable, and enforcement must fail rather than
23
23
  * quietly switch itself off. */
24
24
  export declare const ERR_CONTRACT_UNRESOLVABLE = "ERR_CONTRACT_UNRESOLVABLE";
25
- export declare const AMBIENT_CONTRACT_ERROR_CODES: readonly ["ERR_INPUT_INVALID", "ERR_OUTPUT_INVALID", "ERR_CONTRACT_UNRESOLVABLE"];
25
+ /** A contract slot declaring `x-telo-schema-projection-from` named a declaration
26
+ * that could not be projected. The same failure as {@link
27
+ * ERR_CONTRACT_UNRESOLVABLE} one level down: the slot promises the shape of a
28
+ * referenced declaration, so leaving it unprojected enforces nothing exactly
29
+ * where it claims to enforce something. Its own code because the repair is
30
+ * different — fix the reference, not the type registration. */
31
+ export declare const ERR_SCHEMA_PROJECTION_UNRESOLVED = "ERR_SCHEMA_PROJECTION_UNRESOLVED";
32
+ export declare const AMBIENT_CONTRACT_ERROR_CODES: readonly ["ERR_INPUT_INVALID", "ERR_OUTPUT_INVALID", "ERR_CONTRACT_UNRESOLVABLE", "ERR_SCHEMA_PROJECTION_UNRESOLVED"];
26
33
  export type AmbientContractErrorCode = (typeof AMBIENT_CONTRACT_ERROR_CODES)[number];
27
34
  /** True when a code is raised by the kernel's contract enforcement rather than
28
35
  * declared by a kind. Callers use it to accept the code in a `catches:` entry
@@ -1 +1 @@
1
- {"version":3,"file":"contract-errors.d.ts","sourceRoot":"","sources":["../src/contract-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD;;;iCAGiC;AACjC,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE,eAAO,MAAM,4BAA4B,mFAI/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAIrF;;6DAE6D;AAC7D,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhE"}
1
+ {"version":3,"file":"contract-errors.d.ts","sourceRoot":"","sources":["../src/contract-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAEvD;;;iCAGiC;AACjC,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE;;;;;gEAKgE;AAChE,eAAO,MAAM,gCAAgC,qCAAqC,CAAC;AAEnF,eAAO,MAAM,4BAA4B,uHAK/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAIrF;;6DAE6D;AAC7D,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhE"}
@@ -22,10 +22,18 @@ export const ERR_OUTPUT_INVALID = "ERR_OUTPUT_INVALID";
22
22
  * data, the contract itself is unusable, and enforcement must fail rather than
23
23
  * quietly switch itself off. */
24
24
  export const ERR_CONTRACT_UNRESOLVABLE = "ERR_CONTRACT_UNRESOLVABLE";
25
+ /** A contract slot declaring `x-telo-schema-projection-from` named a declaration
26
+ * that could not be projected. The same failure as {@link
27
+ * ERR_CONTRACT_UNRESOLVABLE} one level down: the slot promises the shape of a
28
+ * referenced declaration, so leaving it unprojected enforces nothing exactly
29
+ * where it claims to enforce something. Its own code because the repair is
30
+ * different — fix the reference, not the type registration. */
31
+ export const ERR_SCHEMA_PROJECTION_UNRESOLVED = "ERR_SCHEMA_PROJECTION_UNRESOLVED";
25
32
  export const AMBIENT_CONTRACT_ERROR_CODES = [
26
33
  ERR_INPUT_INVALID,
27
34
  ERR_OUTPUT_INVALID,
28
35
  ERR_CONTRACT_UNRESOLVABLE,
36
+ ERR_SCHEMA_PROJECTION_UNRESOLVED,
29
37
  ];
30
38
  const AMBIENT = new Set(AMBIENT_CONTRACT_ERROR_CODES);
31
39
  /** True when a code is raised by the kernel's contract enforcement rather than
@@ -0,0 +1,310 @@
1
+ /**
2
+ * The durable-execution replay seam — `kernel/specs/durable-execution.md`.
3
+ *
4
+ * Durable execution is **journal plus deterministic replay**, and Telo can have
5
+ * it because of a property that fell out of `Run.Sequence`'s design: control
6
+ * flow is a finite, DECLARED set of CEL expressions over run state, not
7
+ * arbitrary code. So replay is re-running the step list while returning recorded
8
+ * values instead of computing them. No continuation capture.
9
+ *
10
+ * **What is shared is this narrow seam, not a portable engine vocabulary.** The
11
+ * temptation is to abstract durable execution itself — one surface every backend
12
+ * implements — and it fails the same way twice: the abstraction becomes the
13
+ * union of every engine's lifecycle model (identity policy, schedule overlap,
14
+ * cancel-versus-terminate, deployment pinning), and each backend still loses the
15
+ * half of its own model that did not generalize. Portability is close to
16
+ * worthless here — in-flight runs do not migrate between engines, the
17
+ * configuration shares nothing, and nobody switches durable engines twice.
18
+ *
19
+ * The real constraint is that **the step engine must not fork**. It lives in
20
+ * this package, which is symlinked into every controller bundle rather than
21
+ * inlined, so there is exactly one implementation however many backends exist.
22
+ * What still needs a seam is the other half — *whether and where a step
23
+ * executes* — and that is all this file is.
24
+ *
25
+ * **The kernel is a pure conduit**: it carries the handle on `InvokeContext` and
26
+ * never calls it, which is why the contract lives here rather than in the kernel
27
+ * — the split logging already makes between `Logger` / `RecordBuffer` and the
28
+ * `Telo.LogSink` abstract. The consequence is stated rather than implied: unlike
29
+ * `zones`, this member does NOT cross the ABI, so a second runtime threads a
30
+ * handle it owns.
31
+ */
32
+ import type { InvokeContext, ZoneEntry } from "./cancellation.js";
33
+ import type { OpenZoneAttributes, ZoneAttributes } from "./zone-attribute.js";
34
+ /**
35
+ * WHERE a step's target is declared, as opposed to which live object it is.
36
+ *
37
+ * This is the part of `step()` that nothing may move later, and it is what keeps
38
+ * the seam from being theatre. A target arrives at the engine as a **live
39
+ * instance** — Phase-5 injection has already replaced the `!ref` sentinel — and
40
+ * instance identity is process-local by construction (`ResourceHandle.ref` is
41
+ * declaration-site *diagnostics*, and there is deliberately no reverse
42
+ * handle→instance mapping). A backend asked to execute a step somewhere else
43
+ * would therefore have nothing to resolve, which would make the remote half
44
+ * unreachable and reduce `step(path, target, inputs)` to lookup-plus-record
45
+ * under a longer name.
46
+ *
47
+ * The three forms follow the three ways a resource is declared, and each is
48
+ * derivable identically by the analyzer and at runtime. The ENCODING — how one
49
+ * is written into bytes that cross a process boundary — is deliberately not
50
+ * fixed here: nothing in this slice sends one anywhere, and a normative format
51
+ * frozen with no consumer is the failure the sequencing rule exists to prevent.
52
+ */
53
+ export interface DurableTarget {
54
+ /** Canonical `<module>.<Kind>` of the target. */
55
+ readonly kind: string;
56
+ /** The target's `metadata.name`. Names are dot-free by the reference
57
+ * grammar's load-bearing invariant, so `(module, name)` is unambiguous. */
58
+ readonly name: string;
59
+ /** Source of the module that DECLARED the target, when known — the half that
60
+ * disambiguates two libraries each declaring a `store`. */
61
+ readonly module?: string;
62
+ /**
63
+ * The target is declared inside a `with:` scope.
64
+ *
65
+ * Separate from {@link scope} because the two are known at different places: a
66
+ * step engine can see THAT its target came from a scope — it resolved the name
67
+ * there — while the tuple identifying which scope RUN needs the step path the
68
+ * scope was opened at, which a scope handle is built without. Recording only
69
+ * the tuple would make an underivable scope indistinguishable from a
70
+ * module-level target, and that is the one difference that must not be lost:
71
+ * a scoped instance encoded as module-level resolves, at the far end, to a
72
+ * DIFFERENT resource that merely shares its name.
73
+ */
74
+ readonly scoped?: true;
75
+ /** A `with:`-scoped instance: the scope run is what makes it distinct, and
76
+ * inside a durable run a scope run is opened by a step at a determined path,
77
+ * so the tuple stays deterministic. */
78
+ readonly scope?: {
79
+ readonly owner: string;
80
+ readonly site: string;
81
+ readonly stepPath: string;
82
+ };
83
+ /** An inline declaration: anonymous in the manifest, not anonymous in the
84
+ * graph — the call graph already gives one its own node. */
85
+ readonly pointer?: string;
86
+ }
87
+ /** Why a decision was recorded. Carried for diagnostics and for a backend that
88
+ * wants to render a run; the engine's own behaviour does not branch on it. */
89
+ export type DurableDecisionKind = "inputs" | "predicate" | "condition" | "collection" | "switch" | "value";
90
+ /**
91
+ * The three operations a backend implements, and the middle one is the whole
92
+ * design.
93
+ *
94
+ * A fourth member is a **question, not an operation** ({@link writesInside}).
95
+ */
96
+ export interface DurableRunHandle {
97
+ /** This run's identity, as the backend minted or accepted it. */
98
+ readonly runId: string;
99
+ /**
100
+ * Hand over an effect to be performed: the backend decides **whether**
101
+ * (replay returns the recorded result) and **where** (in process now, or
102
+ * shipped elsewhere and awaited).
103
+ *
104
+ * `execute` performs the in-process dispatch. Passing it is NOT the
105
+ * lookup-plus-record decomposition this seam rejects — there the CALLER
106
+ * performed the effect between two halves of one operation, which silently
107
+ * fixed the step engine and the resource graph in one process. Here the
108
+ * backend decides; `execute` is merely the local capability it may choose to
109
+ * use, and a relocating backend ignores it and ships `target` instead.
110
+ *
111
+ * Wherever a step ends up running, the executing side MUST dispatch through
112
+ * its kernel's invocation chokepoint, so the invocation contract, tracing,
113
+ * zones and observed state hold identically. A backend may move WHERE a step
114
+ * executes; it may not move it outside the runtime's dispatch.
115
+ */
116
+ step(path: string, target: DurableTarget | undefined, inputs: unknown, execute: () => Promise<unknown>): Promise<unknown>;
117
+ /**
118
+ * Record a control-flow decision on first execution and return it verbatim on
119
+ * replay — a resolved input set, a branch predicate, a loop condition, an
120
+ * iteration collection.
121
+ *
122
+ * **This is the load-bearing half.** The tempting claim — "a run's entire
123
+ * mutable state is the `steps` map" — is FALSE: the CEL scope a step's inputs
124
+ * and a branch's predicate evaluate against also carries `resources.<name>`
125
+ * snapshots, `resources.<name>.status` (a live reading, republished on every
126
+ * dispatch BY DESIGN), provider values, variables and secrets. Re-evaluating
127
+ * any of those in a fresh process against freshly-created resources can yield
128
+ * a different answer, and the sharpest case is silent: an iteration whose
129
+ * collection comes from a resource read returns a different order on resume,
130
+ * index N now names a different element, and the journal hands back the
131
+ * recorded result for that path — with the same target, so no mismatch is
132
+ * detectable. Wrong results, no error.
133
+ *
134
+ * Recording the value rather than a digest is deliberate. Digest-and-detect is
135
+ * equally closed for DETECTION and much cheaper, and it is wrong: observed
136
+ * state is *defined* as a live reading, so a run would fail on every resume
137
+ * where the world had moved, which it usually has. That is not durability; it
138
+ * is fragility with good error messages.
139
+ *
140
+ * Replay is then a pure function of `(journal, manifest)` — a CLOSURE
141
+ * property, and closure is what makes this survive an ambient value source
142
+ * added years from now without anyone re-auditing a list.
143
+ */
144
+ decide<T>(path: string, kind: DurableDecisionKind, compute: () => T): Promise<T>;
145
+ /**
146
+ * Suspend the run until a time or a token.
147
+ *
148
+ * **A park is recorded, not merely thrown.** `where` is what makes it
149
+ * recoverable: the step path is where a resume re-enters and where a delivery
150
+ * writes its payload, so a backend that took only `until` could wake a run
151
+ * without knowing what it was waiting at. The parking resource's name rides
152
+ * along for diagnostics, since "run 41 is parked" is not an operator's answer.
153
+ *
154
+ * Called through {@link parkRun}, never directly — the latch that catches a
155
+ * swallowed suspension is set there, so a backend cannot forget it.
156
+ */
157
+ park(where: {
158
+ readonly path: string;
159
+ readonly resource: string;
160
+ }, until: {
161
+ readonly at?: number;
162
+ readonly token?: string;
163
+ }): Promise<never>;
164
+ /**
165
+ * Does this handle's own recording land inside the given zone's atomicity?
166
+ *
167
+ * A question, not an operation — and it is what lets the step engine stop
168
+ * collapsing an `atomic` zone when collapsing would be pessimistic. A
169
+ * collapsed atomic zone is at-least-once (the whole zone re-runs on resume,
170
+ * because a crash between COMMIT and the journal write leaves work done and
171
+ * unrecorded) and that is unavoidable ONLY while the journal is somewhere
172
+ * else. When the journal writes into the very transaction whose effects it
173
+ * records, COMMIT is atomic over both and the window closes.
174
+ *
175
+ * So the collapse rule reads the attribute correctly rather than overriding
176
+ * it: `atomic` says *effects inside are discarded together*, and collapse
177
+ * follows only when the journal's own writes are NOT among them.
178
+ *
179
+ * Every backend that cannot answer yes returns false and behaves exactly as it
180
+ * would have without the question existing.
181
+ */
182
+ writesInside(zone: ZoneEntry): boolean;
183
+ /**
184
+ * Told when a region was collapsed to one entry, and why.
185
+ *
186
+ * Optional, and a NOTIFICATION rather than a question: the collapse decision
187
+ * is the step engine's, and the handle is being informed so it can report.
188
+ * That reporting is a conformance requirement rather than a nicety — whether a
189
+ * deployment got exactly-once or at-least-once turns on whether the journal's
190
+ * writes land inside the transaction's atomicity, which is a runtime
191
+ * coincidence the manifest cannot show. A durability feature whose guarantee
192
+ * is decided invisibly has to say which way it resolved.
193
+ */
194
+ noteZoneMode?(info: ZoneJournalingMode): void;
195
+ }
196
+ /**
197
+ * How one region resolved, at the moment it resolved.
198
+ *
199
+ * Both outcomes are reported, not only the collapsed one, and that is the point:
200
+ * `perStep` is the exactly-once regime and it is reached by an ATTESTATION made
201
+ * at runtime, so an operator asking "did this deployment get exactly-once"
202
+ * needs the affirmative answer as much as the negative. One field to filter on
203
+ * (`mode`) rather than the presence or absence of a record.
204
+ */
205
+ export interface ZoneJournalingMode {
206
+ /** The providing kind — `Sql.Transaction`, `Idempotency.Once`. */
207
+ readonly zone: string;
208
+ readonly attribute: "atomic" | "idempotent";
209
+ /** `collapsed`: the region records one entry and re-runs whole on resume.
210
+ * `perStep`: each step is recorded, and a rollback discards the records with
211
+ * the effects they describe. */
212
+ readonly mode: "collapsed" | "perStep";
213
+ /** The author's own sentence from the attribute, so the reason an operator
214
+ * reads is the manifest's rather than a generic one. */
215
+ readonly reason: string;
216
+ /** Why `perStep` was reached. Only `writesInside` today — the handle attested
217
+ * that its own records land inside this zone's atomicity — and named rather
218
+ * than implied, so a second attestation route is additive. */
219
+ readonly attestation?: "writesInside";
220
+ }
221
+ /**
222
+ * Compose a step path — the journal's key, and the reason journaling lives in
223
+ * the step engine at all.
224
+ *
225
+ * A step path is the only naturally deterministic key available, and it survives
226
+ * CONCURRENCY where a per-run call ordinal would not: two branches of a
227
+ * concurrent fan-out interleave their dispatches, so an ordinal would number
228
+ * them differently on every run while their paths stay fixed. It is also what
229
+ * makes each branch of a fan-out an independently resumable subtree.
230
+ *
231
+ * Segments are joined with `/`; a repetition (a loop turn, an iteration element)
232
+ * qualifies its segment with `[index]`. Both are properties of the WRITTEN
233
+ * structure plus the run's own decisions, never of wall-clock order.
234
+ */
235
+ export declare function stepPath(...segments: (string | number)[]): string;
236
+ /** True when a run handle is ambient — the test the step engine makes before it
237
+ * journals anything, so a non-durable sequence pays nothing. */
238
+ export declare function durableHandleOf(ctx: InvokeContext | undefined): DurableRunHandle | undefined;
239
+ /**
240
+ * What the step engine consults before journaling anything — the collapse rule,
241
+ * read off the ambient zone stack.
242
+ *
243
+ * > A region collapses to one entry when **re-running it is safe** — because its
244
+ * > effects are discarded together, or because re-running is a no-op.
245
+ *
246
+ * The rule has no fields in it, at either end. It used to be a caller-side
247
+ * `checkpoint: collapse` plus a callee-side `requireCheckpoints:` veto, which
248
+ * was wrong four ways at once: a boolean where every neighbouring annotation
249
+ * carries a reason; the opposite polarity from *everything journaled by
250
+ * default*, so forgetting to veto was silent; a veto available only on
251
+ * `Run.Sequence`, leaving a collapsed script or imported invocable unprotected;
252
+ * and a contradiction check to reconcile it with atomicity. Underneath all four,
253
+ * collapse was sold as a cost lever while being a CORRECTNESS decision — it
254
+ * silently converts exactly-once into at-least-once.
255
+ *
256
+ * A region with a property is a zone, so it is declared the way every other
257
+ * region property is. Nothing collapses a sequence because nothing wrapped it.
258
+ */
259
+ export declare function collapsesJournalEntries(zones: readonly {
260
+ kind: string;
261
+ attributes: ZoneAttributes;
262
+ entry: ZoneEntry;
263
+ }[], handle: DurableRunHandle): {
264
+ collapsed: boolean;
265
+ resolutions: ZoneJournalingMode[];
266
+ };
267
+ /** The zone-reading half of a step context — declared here so both the leaf and
268
+ * the engine read the collapse rule through one signature. */
269
+ export interface ZoneReadingContext {
270
+ zoneAttributes?(ctx?: InvokeContext): readonly OpenZoneAttributes[];
271
+ }
272
+ /**
273
+ * Should the step engine record nothing of its own right here?
274
+ *
275
+ * True inside a collapsed region, where per-step entries would describe work
276
+ * that is about to happen again — the region re-runs whole on resume, which is
277
+ * exactly what its author's attribute claims is safe.
278
+ *
279
+ * A host with no zone machinery reads as "no zone open", which journals MORE
280
+ * rather than less: the safe direction, since an unjournaled effect re-executes
281
+ * silently while a redundant entry costs a write.
282
+ */
283
+ export declare function journalingSuppressed(ctx: ZoneReadingContext, invokeCtx: InvokeContext | undefined, handle: DurableRunHandle): boolean;
284
+ /**
285
+ * Reject a value that cannot survive being recorded and read back.
286
+ *
287
+ * **`JSON.stringify` is not the test, and believing it was left the gate open.**
288
+ * A live handle has no enumerable state, so `JSON.stringify(stream)` returns
289
+ * `{}` and throws nothing — the one case the spec names first would have been
290
+ * recorded as an empty object and replayed as one, which is silent corruption
291
+ * rather than the loud failure §6 requires. The static half is deliberately only
292
+ * a warning ("the runtime is the gate"), so a gate that cannot see the case
293
+ * leaves it unenforced end to end.
294
+ *
295
+ * So a live value is detected STRUCTURALLY, by the value-type vocabulary's own
296
+ * binding table: an entry declaring `live: true` names a binding, and the host's
297
+ * table maps that binding to the constructor an assertion tests against. No type
298
+ * name is written here, so a live type added later is covered by its entry
299
+ * alone — the same reason the static rule reads the `live` field rather than
300
+ * naming `Telo.Stream`.
301
+ *
302
+ * Lives in the SDK rather than in a backend because it is a property of the
303
+ * CONTRACT (spec §6), not of one journal: a backend that skipped it would be
304
+ * non-conforming in a way nothing else could catch.
305
+ */
306
+ export declare function assertJournalable(value: unknown, where: {
307
+ run: string;
308
+ path: string;
309
+ }): void;
310
+ //# sourceMappingURL=durable-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-run.d.ts","sourceRoot":"","sources":["../src/durable-run.ts"],"names":[],"mappings":"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"}