@fabricorg/platform 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +48 -2
  3. package/dist/actions/index.cjs +45 -26
  4. package/dist/actions/index.cjs.map +1 -1
  5. package/dist/actions/index.d.cts +225 -226
  6. package/dist/actions/index.d.ts +225 -226
  7. package/dist/actions/index.js +41 -2
  8. package/dist/actions/index.js.map +1 -1
  9. package/dist/adapters/index.cjs +696 -66
  10. package/dist/adapters/index.cjs.map +1 -1
  11. package/dist/adapters/index.d.cts +367 -365
  12. package/dist/adapters/index.d.ts +367 -365
  13. package/dist/adapters/index.js +682 -2
  14. package/dist/adapters/index.js.map +1 -1
  15. package/dist/agent-hitl/index.cjs +21 -10
  16. package/dist/agent-hitl/index.cjs.map +1 -1
  17. package/dist/agent-hitl/index.d.cts +30 -30
  18. package/dist/agent-hitl/index.d.ts +30 -30
  19. package/dist/agent-hitl/index.js +21 -2
  20. package/dist/agent-hitl/index.js.map +1 -1
  21. package/dist/certification/index.cjs +109 -24
  22. package/dist/certification/index.cjs.map +1 -1
  23. package/dist/certification/index.d.cts +33 -39
  24. package/dist/certification/index.d.ts +33 -39
  25. package/dist/certification/index.js +107 -8
  26. package/dist/certification/index.js.map +1 -1
  27. package/dist/discovery/index.cjs +12 -10
  28. package/dist/discovery/index.cjs.map +1 -1
  29. package/dist/discovery/index.d.cts +2 -10
  30. package/dist/discovery/index.d.ts +2 -10
  31. package/dist/discovery/index.js +12 -2
  32. package/dist/discovery/index.js.map +1 -1
  33. package/dist/engine-B4csEOEc.js +223 -0
  34. package/dist/engine-B4csEOEc.js.map +1 -0
  35. package/dist/engine-BqScuhrT.cjs +246 -0
  36. package/dist/engine-BqScuhrT.cjs.map +1 -0
  37. package/dist/events/index.cjs +39 -10
  38. package/dist/events/index.cjs.map +1 -1
  39. package/dist/events/index.d.cts +43 -43
  40. package/dist/events/index.d.ts +43 -43
  41. package/dist/events/index.js +39 -2
  42. package/dist/events/index.js.map +1 -1
  43. package/dist/evidence/index.cjs +12 -50
  44. package/dist/evidence/index.d.cts +2 -229
  45. package/dist/evidence/index.d.ts +2 -229
  46. package/dist/evidence/index.js +1 -5
  47. package/dist/evidence-BuCICb0c.js +516 -0
  48. package/dist/evidence-BuCICb0c.js.map +1 -0
  49. package/dist/evidence-Cq48eDvA.cjs +575 -0
  50. package/dist/evidence-Cq48eDvA.cjs.map +1 -0
  51. package/dist/governance/index.cjs +24 -18
  52. package/dist/governance/index.cjs.map +1 -1
  53. package/dist/governance/index.d.cts +80 -83
  54. package/dist/governance/index.d.ts +80 -83
  55. package/dist/governance/index.js +22 -2
  56. package/dist/governance/index.js.map +1 -1
  57. package/dist/ids/index.cjs +52 -22
  58. package/dist/ids/index.cjs.map +1 -1
  59. package/dist/ids/index.d.cts +17 -17
  60. package/dist/ids/index.d.ts +17 -17
  61. package/dist/ids/index.js +49 -2
  62. package/dist/ids/index.js.map +1 -1
  63. package/dist/index-9jasmw76.d.cts +124 -0
  64. package/dist/index-BXpeFdk2.d.cts +190 -0
  65. package/dist/index-BXpeFdk2.d.ts +190 -0
  66. package/dist/index-BnbsKZsT.d.cts +231 -0
  67. package/dist/index-CIl1rYRv.d.cts +11 -0
  68. package/dist/index-CIl1rYRv.d.ts +11 -0
  69. package/dist/index-CVHA1ypT.d.cts +262 -0
  70. package/dist/index-DZ4mnNb7.d.cts +78 -0
  71. package/dist/index-DZ4mnNb7.d.ts +78 -0
  72. package/dist/index-IqcDvl69.d.ts +124 -0
  73. package/dist/index-fmUiRBXu.d.cts +57 -0
  74. package/dist/index-fmUiRBXu.d.ts +57 -0
  75. package/dist/index-sPUPs8pr.d.ts +262 -0
  76. package/dist/index-z9-CYOIo.d.ts +231 -0
  77. package/dist/index.cjs +111 -385
  78. package/dist/index.d.cts +18 -17
  79. package/dist/index.d.ts +18 -17
  80. package/dist/index.js +19 -20
  81. package/dist/manifest/index.cjs +224 -35
  82. package/dist/manifest/index.cjs.map +1 -1
  83. package/dist/manifest/index.d.cts +130 -131
  84. package/dist/manifest/index.d.ts +130 -131
  85. package/dist/manifest/index.js +219 -7
  86. package/dist/manifest/index.js.map +1 -1
  87. package/dist/modules/index.cjs +281 -42
  88. package/dist/modules/index.cjs.map +1 -1
  89. package/dist/modules/index.d.cts +157 -159
  90. package/dist/modules/index.d.ts +157 -159
  91. package/dist/modules/index.js +274 -6
  92. package/dist/modules/index.js.map +1 -1
  93. package/dist/objects/index.cjs +355 -22
  94. package/dist/objects/index.cjs.map +1 -1
  95. package/dist/objects/index.d.cts +2 -77
  96. package/dist/objects/index.d.ts +2 -77
  97. package/dist/objects/index.js +352 -2
  98. package/dist/objects/index.js.map +1 -1
  99. package/dist/policies/index.cjs +14 -48
  100. package/dist/policies/index.d.cts +2 -247
  101. package/dist/policies/index.d.ts +2 -247
  102. package/dist/policies/index.js +1 -3
  103. package/dist/policies-DH_GCXQ-.js +627 -0
  104. package/dist/policies-DH_GCXQ-.js.map +1 -0
  105. package/dist/policies-Nxbp1CH8.cjs +698 -0
  106. package/dist/policies-Nxbp1CH8.cjs.map +1 -0
  107. package/dist/privacy/index.cjs +26 -43
  108. package/dist/privacy/index.cjs.map +1 -1
  109. package/dist/privacy/index.d.cts +2 -189
  110. package/dist/privacy/index.d.ts +2 -189
  111. package/dist/privacy/index.js +18 -3
  112. package/dist/privacy/index.js.map +1 -1
  113. package/dist/projections/index.cjs +241 -23
  114. package/dist/projections/index.cjs.map +1 -1
  115. package/dist/projections/index.d.cts +2 -125
  116. package/dist/projections/index.d.ts +2 -125
  117. package/dist/projections/index.js +238 -3
  118. package/dist/projections/index.js.map +1 -1
  119. package/dist/state-machines/index.cjs +76 -18
  120. package/dist/state-machines/index.cjs.map +1 -1
  121. package/dist/state-machines/index.d.cts +2 -55
  122. package/dist/state-machines/index.d.ts +2 -55
  123. package/dist/state-machines/index.js +74 -2
  124. package/dist/state-machines/index.js.map +1 -1
  125. package/dist/testing/index.cjs +27 -19
  126. package/dist/testing/index.cjs.map +1 -1
  127. package/dist/testing/index.d.cts +17 -19
  128. package/dist/testing/index.d.ts +17 -19
  129. package/dist/testing/index.js +25 -3
  130. package/dist/testing/index.js.map +1 -1
  131. package/package.json +6 -5
  132. package/dist/chunk-2AE6TMAX.cjs +0 -44
  133. package/dist/chunk-2AE6TMAX.cjs.map +0 -1
  134. package/dist/chunk-6HY35TKB.cjs +0 -491
  135. package/dist/chunk-6HY35TKB.cjs.map +0 -1
  136. package/dist/chunk-6UGEIDLC.cjs +0 -26
  137. package/dist/chunk-6UGEIDLC.cjs.map +0 -1
  138. package/dist/chunk-7GAWSUOG.js +0 -64
  139. package/dist/chunk-7GAWSUOG.js.map +0 -1
  140. package/dist/chunk-AFXFAEWA.js +0 -673
  141. package/dist/chunk-AFXFAEWA.js.map +0 -1
  142. package/dist/chunk-AFXKSIVI.cjs +0 -540
  143. package/dist/chunk-AFXKSIVI.cjs.map +0 -1
  144. package/dist/chunk-B5DOGDPG.js +0 -42
  145. package/dist/chunk-B5DOGDPG.js.map +0 -1
  146. package/dist/chunk-BJGZE3GE.js +0 -20
  147. package/dist/chunk-BJGZE3GE.js.map +0 -1
  148. package/dist/chunk-CRZXTZ36.cjs +0 -258
  149. package/dist/chunk-CRZXTZ36.cjs.map +0 -1
  150. package/dist/chunk-D4SWRBUH.cjs +0 -22
  151. package/dist/chunk-D4SWRBUH.cjs.map +0 -1
  152. package/dist/chunk-DBTAUI6C.cjs +0 -395
  153. package/dist/chunk-DBTAUI6C.cjs.map +0 -1
  154. package/dist/chunk-EMSAFNPX.js +0 -98
  155. package/dist/chunk-EMSAFNPX.js.map +0 -1
  156. package/dist/chunk-F3E5SXPN.cjs +0 -28
  157. package/dist/chunk-F3E5SXPN.cjs.map +0 -1
  158. package/dist/chunk-HEYMMVDP.js +0 -480
  159. package/dist/chunk-HEYMMVDP.js.map +0 -1
  160. package/dist/chunk-IFW43ELT.cjs +0 -331
  161. package/dist/chunk-IFW43ELT.cjs.map +0 -1
  162. package/dist/chunk-LFE74IJX.js +0 -20
  163. package/dist/chunk-LFE74IJX.js.map +0 -1
  164. package/dist/chunk-LZSE6QGQ.cjs +0 -60
  165. package/dist/chunk-LZSE6QGQ.cjs.map +0 -1
  166. package/dist/chunk-MCWSUGB3.cjs +0 -102
  167. package/dist/chunk-MCWSUGB3.cjs.map +0 -1
  168. package/dist/chunk-MLBALEUR.js +0 -290
  169. package/dist/chunk-MLBALEUR.js.map +0 -1
  170. package/dist/chunk-NNXQZPB3.cjs +0 -31
  171. package/dist/chunk-NNXQZPB3.cjs.map +0 -1
  172. package/dist/chunk-OFCCZ27Z.js +0 -26
  173. package/dist/chunk-OFCCZ27Z.js.map +0 -1
  174. package/dist/chunk-PDN5DOLQ.js +0 -242
  175. package/dist/chunk-PDN5DOLQ.js.map +0 -1
  176. package/dist/chunk-PIDZQAEL.js +0 -50
  177. package/dist/chunk-PIDZQAEL.js.map +0 -1
  178. package/dist/chunk-Q4Q4JPR3.js +0 -524
  179. package/dist/chunk-Q4Q4JPR3.js.map +0 -1
  180. package/dist/chunk-RALMUSOX.js +0 -55
  181. package/dist/chunk-RALMUSOX.js.map +0 -1
  182. package/dist/chunk-SNTV45LH.cjs +0 -295
  183. package/dist/chunk-SNTV45LH.cjs.map +0 -1
  184. package/dist/chunk-TBS2S5AX.cjs +0 -40
  185. package/dist/chunk-TBS2S5AX.cjs.map +0 -1
  186. package/dist/chunk-TYNIFC7J.js +0 -253
  187. package/dist/chunk-TYNIFC7J.js.map +0 -1
  188. package/dist/chunk-UGWGHTNC.cjs +0 -56
  189. package/dist/chunk-UGWGHTNC.cjs.map +0 -1
  190. package/dist/chunk-WI7CWNYI.cjs +0 -68
  191. package/dist/chunk-WI7CWNYI.cjs.map +0 -1
  192. package/dist/chunk-WNZP53GI.js +0 -386
  193. package/dist/chunk-WNZP53GI.js.map +0 -1
  194. package/dist/chunk-WQ3BYI7T.cjs +0 -249
  195. package/dist/chunk-WQ3BYI7T.cjs.map +0 -1
  196. package/dist/chunk-Y2PJLRIY.js +0 -36
  197. package/dist/chunk-Y2PJLRIY.js.map +0 -1
  198. package/dist/chunk-YGIPN3TU.cjs +0 -684
  199. package/dist/chunk-YGIPN3TU.cjs.map +0 -1
  200. package/dist/chunk-ZAEVQSB5.js +0 -326
  201. package/dist/chunk-ZAEVQSB5.js.map +0 -1
  202. package/dist/chunk-ZMQTV5OJ.js +0 -27
  203. package/dist/chunk-ZMQTV5OJ.js.map +0 -1
  204. package/dist/evidence/index.cjs.map +0 -1
  205. package/dist/evidence/index.js.map +0 -1
  206. package/dist/index.cjs.map +0 -1
  207. package/dist/index.js.map +0 -1
  208. package/dist/policies/index.cjs.map +0 -1
  209. package/dist/policies/index.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # @fabricorg/platform
2
2
 
3
+ ## 1.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - a205c48: Reconcile unknown adapter outcomes (issue #76). Platform's README describes the Host behavior; Platform runtime code is unchanged.
8
+
9
+ **Release note: failure semantics (issue #76).** Returned `timeout` outcomes, timeouts thrown from `adapter.execute` (including an aborted adapter deadline), contradictory results in either direction (`success: true` with a non-success outcome, or `success: false` with `outcome: "success"`), uninterpretable replies (non-objects, `null`, or arrays), getters that throw before Host interprets the reply, and unrecognised outcome strings such as `"timed_out"` now end `reconciliation_required` instead of `failed`, using the existing `adapter_outcome_ambiguous` kind and `AdapterInvocationAmbiguous` event payload and stable ID. Returned `ambiguous` outcomes use the same settlement path. These unknown outcomes are not retried by the retry helper. Governance stores receive `ExternalReconciliation` evidence even without provider `outcomeEvidence`, because a timed-out call may have applied its effect without returning an external reference.
10
+
11
+ Host detects a timeout thrown from `adapter.execute` from the adapter deadline's aborted signal or an error message matching `/timeout|deadline|timed?\s*out/i`. A definite rejection whose message matches therefore also routes to reconciliation; the matcher does not prove a provider timeout. Reverse contradictions are classified as ambiguous for retry and circuit-breaker accounting, rather than as success.
12
+
13
+ Host copies the reply's contract fields and other own enumerable fields into a plain object before interpretation, reading each top-level field once. A getter that throws during the copy takes the unreadable-reply path. Object replies retain Host's previous truthiness reading of `success`, including truthy non-boolean values from JavaScript adapters.
14
+
15
+ Unknown-outcome settlement uses `transactionWithEvents` when the transaction has `updateAdapterInvocation`, governance `appendExternalReconciliation` where applicable, and leased `updateLeasedActionInvocation` where required. Host commits the ambiguous checkpoint, stable ambiguity event, reconciliation record, and fenced terminal verdict together, in that write order. It retries the transaction once on a non-lease error; a second non-lease failure selects the verdict-first fallback. Lease loss raises `RecoverableFinalizationError` without retry or fallback.
16
+
17
+ The fallback writes `reconciliation_required` first through the existing verdict helper (one retry except on lease loss), then writes checkpoint, event, and reconciliation evidence. After the verdict commits, Host attempts each remaining evidence write (checkpoint, event, record), then surfaces the first error as `RecoverableFinalizationError`. This includes attempting the reconciliation record after an event-write failure and attempting both event and record when both checkpoint shapes fail. Evidence failure can leave a terminal evidence gap that recovery does not repair. If both verdict attempts fail, Host raises `RecoverableFinalizationError` without attempting an unfenced checkpoint. With the original action definition and step order available, recovery re-settles a saved ambiguous row before parked-handoff shortcuts, stale-action gates, authorization, or handler execution, for idempotent and non-idempotent actions. The pending-handoff shortcut retains its base condition (running with a pending handoff and no lease owner, without requiring `parkedAt`); its position after the new recorded-ambiguity check is the change. For a running invocation whose action resolves, `executeInvocation` reads each adapter step's row before the shortcut, stopping on recorded ambiguity or a failed read. A failed read surfaces `RecoverableFinalizationError`, where base 939bcad returned the parked result. If the action no longer resolves, the shortcut still returns the handoff before the unknown-action failure.
18
+
19
+ The optional `AdapterInvocationRecord.reconciliation` checkpoint preserves redacted reason, external reference, and the original event subject; the subject is excluded from the returned `AdapterReconciliationOutcome`. Recovery does not scan the space's event log. Legacy rows use the recorded error as message and reason, and default to the `AdapterInvocation` subject keyed by `adapterInvocationId`. Stable event and reconciliation IDs retain deduplication.
20
+
21
+ Ordinary adapter writes omit the migration 8 column. On checkpoint-write failure, fallback retries once without `reconciliation`, retaining legacy `status: "ambiguous"` plus `error`. Schemas without migration 8 still settle unknown outcomes, without the checkpoint metadata. Atomic PostgreSQL failures roll back before this legacy write runs outside the transaction. Apply migration 8 (`adapter_reconciliation_checkpoint`) through controlled `ensureSchema()` to retain metadata. Existing migration checksums are unchanged. Custom stores should persist the optional checkpoint through adapter create/update/read methods.
22
+
23
+ On a store without atomic support, if the verdict cannot be written at all, no durable write after the call succeeds (neither verdict nor checkpoint), as after a crash mid-call. Idempotent recovery may re-run a leftover running step under the same `adapterInvocationId` idempotency key. The same exposure applies if two atomic attempts roll back and the fallback verdict cannot be written. Non-idempotent recovery retains its stale-action gate without an ambiguous checkpoint. Before another provider attempt, Host resets a prior failed row to `running` and clears its old error.
24
+
25
+ Post-success Host write failures still end `failed`, as in 7.6.0, even though the provider succeeded. Accepted-handoff persistence and its `ExternalOperationAccepted` event, the succeeded marker, attestation extraction and recording, and `AdapterInvocationSucceeded` failures again write the adapter row `failed` and fail the invocation. Later execution returns that terminal verdict; lease loss retains its fencing error. If fencing refuses the failure verdict after the adapter row was saved as `failed`, idempotent recovery can re-run the step; this pre-existing 7.6.0 case remains for follow-up. A follow-up issue will cover these post-success failures. The existing 7.6.0 degraded-success paths for withheld output and refused handoffs retain their reconciliation semantics and documented re-call exposure when the verdict cannot be persisted. Final `after_adapters` domain-event/invocation completion retains its recovery path.
26
+
27
+ A deadline sweep followed by a late completion callback can settle `completed` when the `ambiguous` evidence of a later step was never written.
28
+
29
+ Circuit-open refusals, deadline expiry before execution (including between retry attempts), `prepareAttempt` store errors, returned `permanent_failure`, exhausted `transient_failure`, and exhausted non-timeout adapter throws retain `failed` and their existing event shapes. Adapters already express definite non-effects through `permanent_failure` and `transient_failure`; there is no new adapter-side definite-failure flag. `after_adapters` completion events remain withheld while reconciliation is required.
30
+
31
+ - 939bcad: Build packages with tsdown in place of tsup. Preserve the published ESM/CommonJS
32
+ entry paths, ES2022 target, declaration filenames, and raw external module
33
+ specifier spellings, including the existing Node built-in spellings.
34
+
35
+ Generated JavaScript, source-map contents, declaration formatting, and internal
36
+ chunk layout change. Comparison with the saved tsup build found unchanged named
37
+ runtime exports and public declaration signatures. Declaration maps remain
38
+ unpublished. Facade maps are no longer published for Platform's root, evidence,
39
+ and policies entries, or experience-runtime's contracts entry (both `.js.map`
40
+ and `.cjs.map`). Their new JavaScript output has no source-map URL comment.
41
+
42
+ CommonJS interop: 29 of 72 baseline CJS files expose an `__esModule` marker;
43
+ 0 of 57 tsdown CJS files do. The compared package entries have no default runtime
44
+ export, and the emitted `.d.cts` files declare none. Typed consumers of the
45
+ unchanged declared exports are unaffected. For entries whose marker was removed,
46
+ an untyped CJS `import X from` compiled with a Babel-style default-interop helper
47
+ now yields the namespace instead of `undefined`.
48
+
49
+ ## 1.10.0
50
+
51
+ ### Minor Changes
52
+
53
+ - 5d93521: Add `ReconciliationRequiredError` and `isReconciliationRequiredError` to Platform's root and policies exports (issue #70). The error accepts a message and optional cause; its stable name and `EXECUTION_RECONCILIATION_REQUIRED` code support recognition across package copies.
54
+
55
+ Host honours this signal at the handler-transaction catch, including a store wrapper detecting that its own recovery receipt no longer matches the action's declared events or result schema before handing it back. Wrappers cannot tell which Host transaction they wrap: the same API also serves finalization and external completion. Translate only conditions the store detects itself, not Host internal errors matched by message text; those errors have no public stable code or message contract. Put non-sensitive operator detail in the message because `cause` is not stored. It redacts the message, persists terminal `reconciliation_required`, and returns consistency and HITL metadata. The existing status telemetry fires through persistence. Re-execution returns the terminal record without running the handler; adapters do not run after this signal. Atomic `transactionWithEvents` stores roll back the attempt's domain writes, domain events, and `StateTransitioned` together. On the `transaction`-only fallback, Host appends events directly and the built-in stores do not roll back: domain writes and any events already appended during the attempt stay persisted beside the `reconciliation_required` record, as they already do beside `failed`. A custom fallback transaction may roll back domain writes, but Host does not make event appends atomic with those writes.
56
+
57
+ **Release note: failure semantics (issue #70).** Previously these handler-transaction throws recorded terminal `failed`. Opting into `ReconciliationRequiredError` now requests an operator decision where a domain effect may already have committed. Ordinary handler errors, pre-handler governance/validation, state guards, adapters, and finalization retain their existing failure or recovery semantics. `after_adapters` finalization still uses `RecoverableFinalizationError`. Workers continue counting returned reconciliation results as `failed`. No new event is introduced. Host's Platform peer remains `workspace:^` so published packages require the Platform release containing the runtime exports.
58
+
3
59
  ## 1.9.0
4
60
 
5
61
  ### Minor Changes
package/README.md CHANGED
@@ -118,11 +118,57 @@ Every `invokeAction(id, params)` call goes through:
118
118
  leased, idempotent invocation below its `retryableExecutionMaxAttempts` cap (default 5), after
119
119
  lease expiry, before policy evidence is written. Inline execution, non-idempotent actions,
120
120
  approval resume, and exhausted attempts fail terminally. Obligation resolution and persistence
121
- are outside this retry region. This signal does not change handler or adapter retry behavior.
121
+ are outside this retry region. This signal does not change handler or adapter retry behavior. Host also applies
122
+ `recoveryMaxAttempts` before resumed leased execution. It uses the same claim counter and defaults
123
+ to `max(5, retryableExecutionMaxAttempts)`. An explicit lower recovery cap is rejected.
122
124
 
123
125
  2. **State-machine validation** — if the action's target entity is bound to a state machine, the Host reads authoritative state at the mutation boundary, validates the requested transition, and executes its guard with trusted entity, actor, parameter, invocation, and database context. A false or throwing guard fails closed.
124
126
  3. **Handler** — `actionDef.handler(ctx, params)` runs inside a single `TDb` transaction. Handler parses its own input via its `schema` (single source of truth).
125
- 4. **Adapters** — for each `actionDef.adapterSteps`, the registered implementation runs via `executeWithAdapterRetry` (idempotent retry, exponential backoff, and an optional per-adapter circuit breaker; see [Adapter circuit breakers](#adapter-circuit-breakers)). Adapters may classify outcomes as `success`, `transient_failure`, `permanent_failure`, `timeout`, or `ambiguous`; only transient idempotent outcomes are retried. Ambiguous outcomes are routed to `reconciliation_required` with durable evidence.
127
+
128
+ A handler or its transaction wrapper may throw `ReconciliationRequiredError(message, { cause })`
129
+ when a domain effect may already have committed and an operator must decide what to do.
130
+ Import it and `isReconciliationRequiredError` from `@fabricorg/platform` or
131
+ `@fabricorg/platform/policies`. The stable name and `EXECUTION_RECONCILIATION_REQUIRED` code
132
+ let the predicate recognise a second package copy. Platform Host honours this signal at its
133
+ handler-transaction catch, recording terminal `reconciliation_required` instead of `failed`.
134
+ For example, a recovery-receipt store can check its own stored result against the current action
135
+ schema and declared events before handing it back, and signal a mismatch. Wrappers cannot tell
136
+ which Host transaction they wrap and should not translate internal Host errors by matching messages.
137
+ Put non-sensitive operator detail in the message because `cause` is not stored. Atomic
138
+ `transactionWithEvents` stores roll back the attempt's domain events and `StateTransitioned`
139
+ together; built-in `transaction`-only stores leave domain writes and already appended events
140
+ persisted beside the reconciliation record. This does not change pre-handler governance,
141
+ state-guard, adapter, or finalization failure semantics; see the
142
+ [Host boundary and rollback contract](../platform-host/README.md#atomic-mutation-unit-of-work).
143
+
144
+ 4. **Adapters** — for each `actionDef.adapterSteps`, the registered implementation runs via
145
+ `executeWithAdapterRetry` (idempotent retry, exponential backoff, and an optional per-adapter
146
+ circuit breaker; see [Adapter circuit breakers](#adapter-circuit-breakers)). Adapters may
147
+ classify outcomes as `success`, `transient_failure`, `permanent_failure`, `timeout`, or
148
+ `ambiguous`; only transient idempotent outcomes are retried. Platform Host routes returned
149
+ `ambiguous` and `timeout` outcomes, timeouts thrown from `adapter.execute` (detected from the
150
+ adapter deadline's abort signal or an error message matching
151
+ `/timeout|deadline|timed?\s*out/i`), contradictory results in either direction (`success:
152
+ true` with a non-success outcome, or `success: false` with `outcome: "success"`),
153
+ uninterpretable replies (including getters that throw before Host interprets the reply), and
154
+ unrecognised outcome strings to `reconciliation_required`. Settlement is atomic on capable
155
+ stores and verdict-first otherwise; unwritable fallback verdicts can leave the step eligible
156
+ for recovery under its idempotency key, while evidence failures after a committed verdict can
157
+ leave evidence gaps. Post-success Host write failures recover from durable success/handoff
158
+ checkpoints without calling that provider again, or settle `reconciliation_required` when
159
+ Host cannot save the outcome after one write retry. Recovery repairs stable-ID events and
160
+ attestations. The optional Host `recoveryMaxAttempts` (positive integer, default `max(5,
161
+ retryableExecutionMaxAttempts)`) bounds worker claims before execution; exhaustion settles
162
+ reconciliation. With no durable checkpoint or verdict, only idempotent actions replay a
163
+ running step under the stable key within that budget. Non-idempotent reclaim settles running
164
+ or succeeded steps to reconciliation; no rows or only failed rows retain failure. A deadline
165
+ sweep followed by a late callback can settle `completed` if a later step's ambiguous evidence
166
+ was never written. See the [Host recovery limits](../platform-host/README.md). Host store
167
+ errors after a durable success or handoff, including `prepareAttempt` on a later step, are
168
+ recoverable. Pre-call deadline expiry and circuit-open refusals retain `failed` semantics, as
169
+ do returned permanent failures, exhausted transient failures, and exhausted non-timeout
170
+ adapter throws. Adapters use `permanent_failure` or `transient_failure` for definite
171
+ non-effects; no additional definite-failure flag is needed.
126
172
  5. **Events** — domain events are appended; projections derive read models on next replay.
127
173
  6. **Completion** — invocation status is marked, telemetry emitted.
128
174
 
@@ -1,28 +1,47 @@
1
- 'use strict';
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region actions/index.ts
3
+ const ACTOR_TYPES = [
4
+ "natural_person",
5
+ "agent",
6
+ "system",
7
+ "service_account",
8
+ "external_system",
9
+ "integration"
10
+ ];
11
+ function isActorType(value) {
12
+ return ACTOR_TYPES.includes(value);
13
+ }
14
+ function assertActorType(value) {
15
+ if (!isActorType(value)) throw new Error(`Invalid actorType: ${value}`);
16
+ }
17
+ function assertActionId(actionId) {
18
+ if (!/^[a-z][a-z0-9-]*\.[a-z][a-z0-9_]*$/.test(actionId)) throw new Error(`Invalid action ID: ${actionId}`);
19
+ }
20
+ function createActionRegistry(definitions = []) {
21
+ const actions = /* @__PURE__ */ new Map();
22
+ const register = (definition) => {
23
+ assertActionId(definition.actionId);
24
+ if (actions.has(definition.actionId)) throw new Error(`Action "${definition.actionId}" is already registered`);
25
+ if (definition.mutatesDomain && definition.emitsEvents.length === 0) throw new Error(`Action "${definition.actionId}" mutates domain but emits no events. Every mutating action must emit at least one AssetEvent.`);
26
+ if (definition.admitSubmission !== void 0 && typeof definition.admitSubmission !== "function") throw new Error(`Action "${definition.actionId}" declares admitSubmission that is not a function.`);
27
+ actions.set(definition.actionId, definition);
28
+ };
29
+ for (const definition of definitions) register(definition);
30
+ return {
31
+ register,
32
+ registerIfMissing(definition) {
33
+ if (!actions.has(definition.actionId)) register(definition);
34
+ },
35
+ resolve: (actionId) => actions.get(actionId),
36
+ has: (actionId) => actions.has(actionId),
37
+ ids: () => Array.from(actions.keys())
38
+ };
39
+ }
40
+ //#endregion
41
+ exports.ACTOR_TYPES = ACTOR_TYPES;
42
+ exports.assertActionId = assertActionId;
43
+ exports.assertActorType = assertActorType;
44
+ exports.createActionRegistry = createActionRegistry;
45
+ exports.isActorType = isActorType;
2
46
 
3
- var chunkUGWGHTNC_cjs = require('../chunk-UGWGHTNC.cjs');
4
-
5
-
6
-
7
- Object.defineProperty(exports, "ACTOR_TYPES", {
8
- enumerable: true,
9
- get: function () { return chunkUGWGHTNC_cjs.ACTOR_TYPES; }
10
- });
11
- Object.defineProperty(exports, "assertActionId", {
12
- enumerable: true,
13
- get: function () { return chunkUGWGHTNC_cjs.assertActionId; }
14
- });
15
- Object.defineProperty(exports, "assertActorType", {
16
- enumerable: true,
17
- get: function () { return chunkUGWGHTNC_cjs.assertActorType; }
18
- });
19
- Object.defineProperty(exports, "createActionRegistry", {
20
- enumerable: true,
21
- get: function () { return chunkUGWGHTNC_cjs.createActionRegistry; }
22
- });
23
- Object.defineProperty(exports, "isActorType", {
24
- enumerable: true,
25
- get: function () { return chunkUGWGHTNC_cjs.isActorType; }
26
- });
27
- //# sourceMappingURL=index.cjs.map
28
47
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../actions/index.ts"],"sourcesContent":["import type { SurfaceDeclaration } from \"../discovery\";\nexport type ActionId = `${string}.${string}`;\n\nexport interface SchemaParseIssue {\n\tpath: PropertyKey[];\n\tmessage: string;\n}\n\nexport type SchemaParseResult<T = unknown> =\n\t| { success: true; data: T }\n\t| { success: false; error: { issues: SchemaParseIssue[] } };\n\nexport interface SchemaValidator<T = unknown> {\n\tparse(input: unknown): T;\n\tsafeParse(input: unknown): SchemaParseResult<T>;\n}\n\nexport type ActorType =\n\t| \"natural_person\"\n\t| \"agent\"\n\t| \"system\"\n\t| \"service_account\"\n\t| \"external_system\"\n\t| \"integration\";\n\nexport const ACTOR_TYPES: readonly ActorType[] = [\n\t\"natural_person\",\n\t\"agent\",\n\t\"system\",\n\t\"service_account\",\n\t\"external_system\",\n\t\"integration\",\n] as const;\n\nexport function isActorType(value: string): value is ActorType {\n\treturn (ACTOR_TYPES as readonly string[]).includes(value);\n}\n\nexport function assertActorType(value: string): asserts value is ActorType {\n\tif (!isActorType(value)) {\n\t\tthrow new Error(`Invalid actorType: ${value}`);\n\t}\n}\n\nexport type ActionStatus =\n\t| \"pending\"\n\t| \"running\"\n\t| \"completed\"\n\t| \"failed\"\n\t| \"blocked_by_policy\"\n\t| \"waiting_for_approval\"\n\t| \"reconciliation_required\"\n\t| \"validation_failed\";\n\nexport interface FabricRuntimeServices {\n\treadonly [serviceName: string]: unknown;\n}\n\nexport interface ActionContext<TDb = unknown> {\n\tactionInvocationId: string;\n\ttenantId: string;\n\tspaceId: string;\n\tactorId: string;\n\tactorType: ActorType;\n\tcorrelationId: string;\n\tcausationId?: string;\n\tprovenance?: { source: string; auditAttributes?: Record<string, string> };\n\tdb: TDb;\n\tservices?: FabricRuntimeServices;\n\t/**\n\t * When the host admitted this invocation's submission through the\n\t * action's `admitSubmission` check, read from the durable invocation\n\t * record. Absent when the action declares no check, or when the runtime\n\t * that built this context does not run declared admission checks (a host\n\t * older than the one that introduced them). A handler whose action\n\t * declares `admitSubmission` can refuse to run without it.\n\t */\n\tadmittedAt?: Date;\n}\n\n/**\n * Trusted facts a host gives an action's `admitSubmission` check. Scope and\n * actor come from the host's authenticated submission, never from the\n * parameters.\n */\nexport interface ActionAdmissionInput {\n\treadonly actionId: ActionId;\n\treadonly tenantId: string;\n\treadonly spaceId: string;\n\treadonly actorId: string;\n\treadonly actorType: ActorType;\n\t/**\n\t * The submitted parameters, unvalidated, as a copy made for this check.\n\t * `@fabricorg/platform-host` records its own copy of the same value, taken\n\t * before the check ran, and only if the check admits it; changing this\n\t * copy changes nothing the host records.\n\t */\n\treadonly parameters: Readonly<Record<string, unknown>>;\n\t/**\n\t * Aborted when the host's admission deadline passes. Hosts pass an\n\t * `AbortSignal`. `@fabricorg/platform-host` refuses an answer that settles\n\t * at or after the deadline, even one that settles before the abort.\n\t */\n\treadonly signal: ActionAdmissionSignal;\n}\n\n/**\n * The part of an `AbortSignal` an admission check can rely on. Declared\n * structurally so this module compiles without DOM or Node type libraries; a\n * host passes a real `AbortSignal`, which satisfies it.\n */\nexport interface ActionAdmissionSignal {\n\treadonly aborted: boolean;\n\taddEventListener(type: \"abort\", listener: () => void, options?: { once?: boolean }): void;\n\tremoveEventListener(type: \"abort\", listener: () => void): void;\n}\n\n/**\n * An admission check's answer. Only `{ admitted: true }` admits. A refusal's\n * `code` must be a fixed code chosen by the action (lower-case first letter,\n * then letters, digits, `_`, `.`, `:` or `-`, at most 128 characters), never\n * a submitted value: the host puts it in the error it throws.\n */\nexport type ActionAdmissionDecision =\n\t| { readonly admitted: true }\n\t| { readonly admitted: false; readonly code: string };\n\n/**\n * Decides whether a submission may be recorded at all. A host that supports\n * it runs the check after authorizing the submitter and before it writes\n * anything durable for the submission; a refusal, a throw, a timeout or any\n * answer other than `{ admitted: true }` refuses the submission.\n */\nexport type ActionAdmission = (input: ActionAdmissionInput) => Promise<ActionAdmissionDecision>;\n\nexport interface ActionResult {\n\tsuccess: boolean;\n\tdata?: Record<string, unknown>;\n\terror?: string;\n}\n\nexport type ActionHandler<TDb = unknown> = (\n\tctx: ActionContext<TDb>,\n\tparams: unknown,\n) => Promise<ActionResult>;\n\nexport interface AdapterStepRetryPolicy {\n\tidempotent: boolean;\n\tmaxAttempts?: number;\n\tinitialDelayMs?: number;\n\tbackoffMultiplier?: number;\n\tmaxDelayMs?: number;\n}\n\nexport interface AdapterStep {\n\tadapterType: string;\n\toperation: string;\n\tretryPolicy?: AdapterStepRetryPolicy;\n\tgetInput: (\n\t\tparams: unknown,\n\t\thandlerResult: Record<string, unknown>,\n\t) => Record<string, unknown> | undefined;\n}\n\nexport interface ActionStateMachineBinding {\n\tentityType: string;\n\ttargetState: string | ((params: unknown) => string);\n\tgetEntityId: (params: unknown) => string | undefined;\n}\n\nexport interface ActionDefinition<TDb = unknown> extends SurfaceDeclaration {\n\tactionId: ActionId;\n\tnamespace: string;\n\tversion: number;\n\tschema: SchemaValidator;\n\t/**\n\t * Optional check a host runs on every submission before it records\n\t * anything durable for it (invocation, idempotency key, events, errors).\n\t * Use it when parameters must be validated against trusted context, for\n\t * example a secret reference the tenant's secret store must hold, before\n\t * they may be persisted. `@fabricorg/platform-host` runs it from the\n\t * release that introduced it; an older host ignores it, so a handler that\n\t * relies on it should require `ActionContext.admittedAt`.\n\t */\n\tadmitSubmission?: ActionAdmission;\n\t/** Optional validation contract for the public handler result data. */\n\tresultSchema?: SchemaValidator;\n\thandler?: ActionHandler<TDb>;\n\trequiredRoles?: string[];\n\trequiredPermissions?: string[];\n\tpolicies?: `${string}.v${number}`[];\n\temitsEvents: string[];\n\tidempotent: boolean;\n\tmutatesDomain: boolean;\n\t/** Portable execution semantics used by hosts, gateways, and generated capability contracts. */\n\texecution?: ActionExecutionContract;\n\tstateMachine?: ActionStateMachineBinding;\n\tadapterSteps?: AdapterStep[];\n\t/** Completion actions can attest only after all external adapter work succeeds. */\n\teventPhase?: \"before_adapters\" | \"after_adapters\";\n\t/**\n\t * If \"saga\", the action is a multi-step orchestration. The worker dispatches\n\t * to a registered saga implementation instead of running `handler` directly.\n\t */\n\tkind?: \"atomic\" | \"saga\";\n}\n\nexport type ActionConnectivity = \"online-required\" | \"offline-capturable\";\nexport type AuthorityMoment = \"capture\" | \"execution\" | \"both\";\nexport type ActionIdempotencyStance = \"required\" | \"supported\" | \"prohibited\";\nexport type ActionCompletionMode = \"immediate\" | \"accepted\" | \"long-running\";\nexport type ActionConsistency = \"authoritative-now\" | \"provisional-until-reconciled\";\nexport type ActionDataSensitivity = \"public\" | \"internal\" | \"confidential\" | \"restricted\";\n\nexport interface ActionExecutionContract {\n\tconnectivity: ActionConnectivity;\n\tauthorityMoment: AuthorityMoment;\n\tidempotency: ActionIdempotencyStance;\n\t/**\n\t * `immediate`: the invocation settles within the host's execution.\n\t * `accepted` and `long-running`: an adapter may hand the work to an\n\t * external system and the invocation stays running until that system\n\t * completes it by reference through the host's completion ingress.\n\t * Declare `completionDeadlineMs` on a `long-running` action so a callback\n\t * that never comes is noticed rather than waited on forever.\n\t */\n\tcompletion: ActionCompletionMode;\n\t/**\n\t * How long an accepted handoff may wait for its completion before the host\n\t * moves the invocation to reconciliation. Without it, nothing sweeps the\n\t * handoff. Refused on `immediate`, since nothing external completes it.\n\t */\n\tcompletionDeadlineMs?: number;\n\tconsistency: ActionConsistency;\n\tsensitivity?: ActionDataSensitivity;\n}\n\nexport interface SagaContext {\n\tactionInvocationId: string;\n\ttenantId: string;\n\tspaceId: string;\n\tparameters: unknown;\n\tsleep?: (delayMs: number) => Promise<void>;\n\tquery?: <T = unknown>(model: string, operation: string, args: unknown) => Promise<T>;\n\t/**\n\t * Wait for a named external signal carrying a payload of `T`, with a timeout.\n\t *\n\t * Resolves with the payload of the first signal received, or `null` if the timeout\n\t * fires first. Subsequent distinct signals with the same name received before\n\t * another `awaitSignal` call are buffered in FIFO order so none is lost.\n\t *\n\t * A signal may carry a stable signal ID that the sender keeps the same on each redelivery\n\t * (for example a webhook delivery ID or an approval decision ID). A runtime drops\n\t * a signal whose ID it has already received for this saga, so a sender retrying\n\t * after a timeout does not wake the saga twice. Signals without an ID are not\n\t * deduplicated. `@fabricorg/platform-temporal` provides `createSagaSignalInbox()`\n\t * for Temporal runtimes.\n\t *\n\t * Intended for agent-driven and webhook-driven coordination: replace polling loops\n\t * with push-based wakeups while preserving the saga's deterministic replay\n\t * semantics (the runtime is responsible for capturing signals into workflow history).\n\t *\n\t * Optional. Runtimes that cannot implement push-based signaling (e.g. an in-memory\n\t * test harness) may omit this; sagas should treat absence as a fallback condition\n\t * and use `sleep` + `query` polling instead.\n\t *\n\t * Signal *senders* are runtime-specific (e.g. a Temporal `WorkflowClient.signal()`\n\t * call from an action handler). Apps that need to send signals from action\n\t * handlers should expose a coordination service via `ActionContext.services`\n\t * — the platform does not prescribe the sender API.\n\t *\n\t * @example\n\t * const payload = await ctx.awaitSignal?.<{ result: Record<string, unknown> }>(\n\t * \"userInputProcessed\",\n\t * 5 * 60 * 1000,\n\t * );\n\t * if (payload) { /* handle pushed result *\\/ }\n\t * else { /* timeout — fall back to deterministic path *\\/ }\n\t */\n\tawaitSignal?: <T = unknown>(signalName: string, timeoutMs: number) => Promise<T | null>;\n}\n\nexport interface SagaChildActionInput {\n\tsuffix: string;\n\tactionId: string;\n\tparameters: Record<string, unknown>;\n}\n\nexport type SagaRunChild = (input: SagaChildActionInput) => Promise<Record<string, unknown>>;\n\nexport interface SagaCompletionEvent {\n\teventType: string;\n\tsubjectType: string;\n\tsubjectId: string;\n\tpayload: unknown;\n\teventSchemaVersion?: number;\n}\n\nexport interface SagaCompletion {\n\tresultData: Record<string, unknown>;\n\tevent?: SagaCompletionEvent;\n}\n\nexport type SagaImplementation = (\n\tctx: SagaContext,\n\trunChild: SagaRunChild,\n) => Promise<SagaCompletion>;\n\nexport function assertActionId(actionId: string): asserts actionId is ActionId {\n\tif (!/^[a-z][a-z0-9-]*\\.[a-z][a-z0-9_]*$/.test(actionId)) {\n\t\tthrow new Error(`Invalid action ID: ${actionId}`);\n\t}\n}\n\nexport interface ActionRegistry<TDb = unknown> {\n\tregister(definition: ActionDefinition<TDb>): void;\n\tregisterIfMissing(definition: ActionDefinition<TDb>): void;\n\tresolve(actionId: ActionId): ActionDefinition<TDb> | undefined;\n\thas(actionId: string): actionId is ActionId;\n\tids(): ActionId[];\n}\n\nexport function createActionRegistry<TDb = unknown>(definitions: readonly ActionDefinition<TDb>[] = []): ActionRegistry<TDb> {\n\tconst actions = new Map<ActionId, ActionDefinition<TDb>>();\n\tconst register = (definition: ActionDefinition<TDb>): void => {\n\t\tassertActionId(definition.actionId);\n\t\tif (actions.has(definition.actionId)) throw new Error(`Action \"${definition.actionId}\" is already registered`);\n\t\tif (definition.mutatesDomain && definition.emitsEvents.length === 0) {\n\t\t\tthrow new Error(`Action \"${definition.actionId}\" mutates domain but emits no events. Every mutating action must emit at least one AssetEvent.`);\n\t\t}\n\t\tif (definition.admitSubmission !== undefined && typeof definition.admitSubmission !== \"function\") {\n\t\t\tthrow new Error(`Action \"${definition.actionId}\" declares admitSubmission that is not a function.`);\n\t\t}\n\t\tactions.set(definition.actionId, definition);\n\t};\n\tfor (const definition of definitions) register(definition);\n\treturn {\n\t\tregister,\n\t\tregisterIfMissing(definition) {\n\t\t\tif (!actions.has(definition.actionId)) register(definition);\n\t\t},\n\t\tresolve: (actionId) => actions.get(actionId),\n\t\thas: (actionId): actionId is ActionId => actions.has(actionId as ActionId),\n\t\tids: () => Array.from(actions.keys()),\n\t};\n}\n"],"mappings":";;AAyBA,MAAa,cAAoC;CAChD;CACA;CACA;CACA;CACA;CACA;AACD;AAEA,SAAgB,YAAY,OAAmC;CAC9D,OAAQ,YAAkC,SAAS,KAAK;AACzD;AAEA,SAAgB,gBAAgB,OAA2C;CAC1E,IAAI,CAAC,YAAY,KAAK,GACrB,MAAM,IAAI,MAAM,sBAAsB,OAAO;AAE/C;AA0QA,SAAgB,eAAe,UAAgD;CAC9E,IAAI,CAAC,qCAAqC,KAAK,QAAQ,GACtD,MAAM,IAAI,MAAM,sBAAsB,UAAU;AAElD;AAUA,SAAgB,qBAAoC,cAAgD,CAAC,GAAwB;CAC5H,MAAM,0BAAU,IAAI,IAAqC;CACzD,MAAM,YAAY,eAA4C;EAC7D,eAAe,WAAW,QAAQ;EAClC,IAAI,QAAQ,IAAI,WAAW,QAAQ,GAAG,MAAM,IAAI,MAAM,WAAW,WAAW,SAAS,wBAAwB;EAC7G,IAAI,WAAW,iBAAiB,WAAW,YAAY,WAAW,GACjE,MAAM,IAAI,MAAM,WAAW,WAAW,SAAS,+FAA+F;EAE/I,IAAI,WAAW,oBAAoB,KAAA,KAAa,OAAO,WAAW,oBAAoB,YACrF,MAAM,IAAI,MAAM,WAAW,WAAW,SAAS,mDAAmD;EAEnG,QAAQ,IAAI,WAAW,UAAU,UAAU;CAC5C;CACA,KAAK,MAAM,cAAc,aAAa,SAAS,UAAU;CACzD,OAAO;EACN;EACA,kBAAkB,YAAY;GAC7B,IAAI,CAAC,QAAQ,IAAI,WAAW,QAAQ,GAAG,SAAS,UAAU;EAC3D;EACA,UAAU,aAAa,QAAQ,IAAI,QAAQ;EAC3C,MAAM,aAAmC,QAAQ,IAAI,QAAoB;EACzE,WAAW,MAAM,KAAK,QAAQ,KAAK,CAAC;CACrC;AACD"}