@crediolabs/policy-synth 0.1.18 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +6 -5
  2. package/dist/adapters/interpreter/adapter.d.ts +19 -25
  3. package/dist/adapters/interpreter/adapter.js +54 -487
  4. package/dist/adapters/interpreter/index.d.ts +1 -1
  5. package/dist/adapters/interpreter/index.js +1 -1
  6. package/dist/adapters/oz/adapter.js +1 -15
  7. package/dist/errors.d.ts +1 -1
  8. package/dist/index.d.ts +1 -4
  9. package/dist/index.js +1 -4
  10. package/dist/install/build-add-context-rule.d.ts +3 -11
  11. package/dist/install/build-add-context-rule.js +2 -97
  12. package/dist/install/build-install-policy.d.ts +4 -53
  13. package/dist/install/build-install-policy.js +44 -123
  14. package/dist/ir/index.d.ts +1 -1
  15. package/dist/ir/types.d.ts +7 -76
  16. package/dist/ir/types.js +0 -2
  17. package/dist/predicate/decode.d.ts +1 -1
  18. package/dist/predicate/decode.js +2 -70
  19. package/dist/predicate/encode.js +45 -229
  20. package/dist/predicate/from-json.js +1 -42
  21. package/dist/record/decode.js +3 -8
  22. package/dist/review-card/builder.d.ts +4 -3
  23. package/dist/review-card/builder.js +18 -162
  24. package/dist/review-card/cross-check.js +2 -105
  25. package/dist/review-card/render-leaf.d.ts +4 -0
  26. package/dist/review-card/render-leaf.js +47 -0
  27. package/dist/run/index.d.ts +35 -53
  28. package/dist/run/index.js +115 -156
  29. package/dist/run/schemas.d.ts +168 -1650
  30. package/dist/run/schemas.js +36 -199
  31. package/dist/seams/types.d.ts +0 -2
  32. package/dist/simulate/deny-cases.d.ts +26 -0
  33. package/dist/simulate/deny-cases.js +282 -0
  34. package/dist/simulate/evaluate.d.ts +18 -0
  35. package/dist/simulate/evaluate.js +271 -0
  36. package/dist/simulate/index.d.ts +4 -0
  37. package/dist/simulate/index.js +8 -0
  38. package/dist/synth/compose-from-recording.d.ts +24 -51
  39. package/dist/synth/compose-from-recording.js +120 -282
  40. package/dist/synth/deny-cases.d.ts +5 -7
  41. package/dist/synth/deny-cases.js +23 -373
  42. package/dist/synth/evaluate.d.ts +0 -21
  43. package/dist/synth/evaluate.js +56 -336
  44. package/dist/synth/index.d.ts +0 -5
  45. package/dist/synth/index.js +0 -5
  46. package/dist/synth/permit-context.d.ts +2 -9
  47. package/dist/synth/permit-context.js +1 -82
  48. package/dist/synth/synthesize-from-recording.d.ts +1 -29
  49. package/dist/synth/synthesize-from-recording.js +41 -356
  50. package/dist/types.d.ts +8 -60
  51. package/dist/types.js +5 -2
  52. package/dist/verify/simulate.d.ts +1 -14
  53. package/dist/verify/simulate.js +2 -88
  54. package/dist/verify/verify.d.ts +1 -7
  55. package/dist/verify/verify.js +0 -8
  56. package/dist-cjs/adapters/interpreter/adapter.d.ts +19 -25
  57. package/dist-cjs/adapters/interpreter/adapter.js +56 -489
  58. package/dist-cjs/adapters/interpreter/index.d.ts +1 -1
  59. package/dist-cjs/adapters/interpreter/index.js +1 -2
  60. package/dist-cjs/adapters/oz/adapter.js +1 -15
  61. package/dist-cjs/errors.d.ts +1 -1
  62. package/dist-cjs/index.d.ts +1 -4
  63. package/dist-cjs/index.js +1 -4
  64. package/dist-cjs/install/build-add-context-rule.d.ts +3 -11
  65. package/dist-cjs/install/build-add-context-rule.js +1 -96
  66. package/dist-cjs/install/build-install-policy.d.ts +4 -53
  67. package/dist-cjs/install/build-install-policy.js +44 -122
  68. package/dist-cjs/ir/index.d.ts +1 -1
  69. package/dist-cjs/ir/types.d.ts +7 -76
  70. package/dist-cjs/ir/types.js +0 -2
  71. package/dist-cjs/predicate/decode.d.ts +1 -1
  72. package/dist-cjs/predicate/decode.js +2 -70
  73. package/dist-cjs/predicate/encode.js +45 -229
  74. package/dist-cjs/predicate/from-json.js +1 -42
  75. package/dist-cjs/record/decode.js +3 -8
  76. package/dist-cjs/review-card/builder.d.ts +4 -3
  77. package/dist-cjs/review-card/builder.js +23 -167
  78. package/dist-cjs/review-card/cross-check.js +7 -110
  79. package/dist-cjs/review-card/render-leaf.d.ts +4 -0
  80. package/dist-cjs/review-card/render-leaf.js +52 -0
  81. package/dist-cjs/run/index.d.ts +35 -53
  82. package/dist-cjs/run/index.js +116 -162
  83. package/dist-cjs/run/schemas.d.ts +168 -1650
  84. package/dist-cjs/run/schemas.js +37 -200
  85. package/dist-cjs/seams/types.d.ts +0 -2
  86. package/dist-cjs/simulate/deny-cases.d.ts +26 -0
  87. package/dist-cjs/simulate/deny-cases.js +286 -0
  88. package/dist-cjs/simulate/evaluate.d.ts +18 -0
  89. package/dist-cjs/simulate/evaluate.js +274 -0
  90. package/dist-cjs/simulate/index.d.ts +4 -0
  91. package/dist-cjs/simulate/index.js +13 -0
  92. package/dist-cjs/synth/compose-from-recording.d.ts +24 -51
  93. package/dist-cjs/synth/compose-from-recording.js +120 -282
  94. package/dist-cjs/synth/deny-cases.d.ts +5 -7
  95. package/dist-cjs/synth/deny-cases.js +23 -374
  96. package/dist-cjs/synth/evaluate.d.ts +0 -21
  97. package/dist-cjs/synth/evaluate.js +57 -337
  98. package/dist-cjs/synth/index.d.ts +0 -5
  99. package/dist-cjs/synth/index.js +1 -11
  100. package/dist-cjs/synth/permit-context.d.ts +2 -9
  101. package/dist-cjs/synth/permit-context.js +1 -82
  102. package/dist-cjs/synth/synthesize-from-recording.d.ts +1 -29
  103. package/dist-cjs/synth/synthesize-from-recording.js +39 -354
  104. package/dist-cjs/types.d.ts +8 -60
  105. package/dist-cjs/types.js +6 -3
  106. package/dist-cjs/verify/simulate.d.ts +1 -14
  107. package/dist-cjs/verify/simulate.js +2 -88
  108. package/dist-cjs/verify/verify.d.ts +1 -7
  109. package/dist-cjs/verify/verify.js +0 -8
  110. package/package.json +4 -4
  111. package/src/adapters/interpreter/adapter.ts +76 -572
  112. package/src/errors.ts +0 -19
  113. package/src/index.ts +1 -4
  114. package/src/install/build-add-context-rule.ts +4 -127
  115. package/src/install/build-install-policy.ts +93 -214
  116. package/src/predicate/decode.ts +2 -70
  117. package/src/predicate/encode.ts +49 -261
  118. package/src/predicate/from-json.ts +1 -43
  119. package/src/record/decode.ts +3 -8
  120. package/src/review-card/builder.ts +19 -168
  121. package/src/review-card/cross-check.ts +3 -105
  122. package/src/review-card/render-leaf.ts +48 -0
  123. package/src/run/index.ts +130 -186
  124. package/src/run/schemas.ts +38 -214
  125. package/src/simulate/deny-cases.ts +334 -0
  126. package/src/simulate/evaluate.ts +284 -0
  127. package/src/simulate/index.ts +11 -0
  128. package/src/synth/compose-from-recording.ts +148 -347
  129. package/src/synth/index.ts +0 -13
  130. package/src/synth/synthesize-from-recording.ts +43 -456
  131. package/src/types.ts +13 -49
  132. package/dist/install/build-install-predicate.d.ts +0 -96
  133. package/dist/install/build-install-predicate.js +0 -444
  134. package/dist-cjs/install/build-install-predicate.d.ts +0 -96
  135. package/dist-cjs/install/build-install-predicate.js +0 -479
  136. package/src/adapters/interpreter/index.ts +0 -8
  137. package/src/adapters/oz/adapter.ts +0 -376
  138. package/src/adapters/oz/index.ts +0 -9
  139. package/src/codegen/compile-gate.ts +0 -167
  140. package/src/codegen/index.ts +0 -17
  141. package/src/codegen/template.ts +0 -165
  142. package/src/ir/index.ts +0 -13
  143. package/src/ir/types.ts +0 -132
  144. package/src/mandate/index.ts +0 -4
  145. package/src/mandate/to-ir.ts +0 -71
  146. package/src/mandate/types.ts +0 -21
  147. package/src/seams/index.ts +0 -11
  148. package/src/seams/types.ts +0 -81
  149. package/src/synth/deny-cases.ts +0 -663
  150. package/src/synth/evaluate.ts +0 -613
  151. package/src/synth/harness.ts +0 -68
  152. package/src/synth/minimize.ts +0 -48
  153. package/src/synth/permit-context.ts +0 -136
  154. package/src/synth/predicate-literals.ts +0 -27
  155. package/src/synth/synthesize-from-mandate.ts +0 -82
  156. package/src/verify/envelope.ts +0 -28
  157. package/src/verify/index.ts +0 -5
  158. package/src/verify/simulate.ts +0 -311
  159. package/src/verify/verify.ts +0 -243
@@ -9,14 +9,14 @@
9
9
  //
10
10
  // i128 amounts and other large integers are carried as base-10 decimal strings
11
11
  // end-to-end. Networks are pinned to the same closed set the core defines.
12
- // The discriminated union on `source` exposes BOTH synthesize_policy
13
- // front-ends through a single tool input.
12
+ // `source` is a single-variant discriminator (`recording`); it stays a union
13
+ // so an unknown value is rejected with a discriminator error.
14
14
  //
15
15
  // This module is the SINGLE source of truth for these shapes. The MCP package
16
16
  // imports them here so its tool-shape bindings stay in step; the CLI imports
17
17
  // them here so it can build the same args envelope the MCP transport builds.
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.ToolErrorSchema = exports.GetInterpreterInfoInputSchema = exports.RevokePolicyInputSchema = exports.InstallPolicyInputSchema = exports.NETWORK_PASSPHRASES = exports.DEFAULT_AUTH_VALID_UNTIL_LEDGERS = exports.RPC_URL_BY_NETWORK = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.MAINNET_RPC_URL = exports.TESTNET_RPC_URL = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.VerifyPolicyInputSchema = exports.SimulatePolicyInputSchema = exports.OraclePriceFixtureSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.SynthesizePolicyInputSchema = exports.SynthesizePolicyRecordingInputSchema = exports.InterpreterOptionsSchema = exports.SynthesizePolicyMandateInputSchema = exports.RecordTransactionInputSchema = exports.OzAdapterConfigSchema = exports.ComposeUserResponsesSchema = exports.MandateSpecSchema = exports.RecordedTransactionSchema = exports.ParseConfidenceSchema = exports.OnChainEventSchema = exports.TokenMovementSchema = exports.ContractInvocationSchema = exports.ScValSchema = exports.NetworkSchema = void 0;
19
+ exports.ToolErrorSchema = exports.GetInterpreterInfoInputSchema = exports.RevokePolicyInputSchema = exports.InstallPolicyInputSchema = exports.NETWORK_PASSPHRASES = exports.RPC_URL_BY_NETWORK = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.MAINNET_RPC_URL = exports.TESTNET_RPC_URL = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.VerifyPolicyInputSchema = exports.SimulatePolicyInputSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.SynthesizePolicyInputSchema = exports.InterpreterOptionsSchema = exports.RecordTransactionInputSchema = exports.ComposeUserResponsesSchema = exports.RecordedTransactionSchema = exports.ParseConfidenceSchema = exports.OnChainEventSchema = exports.TokenMovementSchema = exports.ContractInvocationSchema = exports.ScValSchema = exports.NetworkSchema = void 0;
20
20
  const zod_1 = require("zod");
21
21
  const address_ts_1 = require("../synth/address.js");
22
22
  /** Soroban `valid_until` is a u32 ledger sequence; a value above this cannot be
@@ -101,56 +101,13 @@ exports.RecordedTransactionSchema = zod_1.z
101
101
  sourceAccount: zod_1.z.string(),
102
102
  })
103
103
  .passthrough();
104
- /** MandateSpec mirrors the core MandateSpec. The deterministic Mandate
105
- * front-end needs no parseConfidence; the tool adapter injects the full
106
- * confidence after synthesis so the orchestrator can compare. */
107
- exports.MandateSpecSchema = zod_1.z
108
- .object({
109
- chain: zod_1.z.literal('stellar'),
110
- contract: zod_1.z.string(),
111
- method: zod_1.z.string().optional(),
112
- spendingLimit: zod_1.z
113
- .object({
114
- token: zod_1.z.string(),
115
- limit: zod_1.z.string().regex(/^[0-9]+$/),
116
- windowSeconds: zod_1.z.number().int().positive(),
117
- })
118
- .optional(),
119
- // A threshold of 0 means "0 approvals", which is not a real M-of-N gate.
120
- approvalThreshold: zod_1.z.number().int().positive().optional(),
121
- recipients: zod_1.z.array(zod_1.z.string()).optional(),
122
- expiry: zod_1.z
123
- .object({
124
- validUntilLedger: zod_1.z.number().int().positive().max(U32_MAX).optional(),
125
- validUntilUnixSeconds: zod_1.z.number().int().positive().optional(),
126
- })
127
- .optional(),
128
- })
129
- .passthrough()
130
- // TS-F4/F6: a `recipients` allowlist is only meaningful against a SEP-41
131
- // method whose arg 1 IS the recipient (SAC/SEP-41 `transfer(from, to,
132
- // amount)` and SEP-41 `mint(to, amount)`). Without this gate the
133
- // `to-ir.ts` lowering pins the allowlist to `RECIPIENT_ARG_INDEX = 1`
134
- // for any contract+method, which would let a non-SEP-41 method's
135
- // arg[1] (e.g. an amount, an op type, an arbitrary address payload)
136
- // be silently constrained as if it were a recipient. Refusing
137
- // non-SEP-41 methods at the boundary is the fail-closed shape.
138
- .refine((v) => v.recipients === undefined ||
139
- v.recipients.length === 0 ||
140
- v.method === 'transfer' ||
141
- v.method === 'mint', {
142
- message: 'recipients is only valid when method is a SEP-41 method (transfer or mint); other methods do not have a recipient at arg[1]',
143
- });
144
- /** ComposeUserResponses mirrors the core. */
145
104
  exports.ComposeUserResponsesSchema = zod_1.z
146
105
  .object({
147
- windowSeconds: zod_1.z.number().int().positive().optional(),
148
106
  validUntilLedger: zod_1.z.number().int().positive().max(U32_MAX).optional(),
149
107
  limitAmount: zod_1.z
150
108
  .string()
151
109
  .regex(/^[0-9]+$/)
152
110
  .optional(),
153
- invocationLimit: zod_1.z.number().int().positive().optional(),
154
111
  // Swap recipient allowlist (SoroSwap call_arg[3]). Each entry must be a
155
112
  // Stellar address (G... wallet or C... contract); supplying it REPLACES the
156
113
  // default pin to the recorded recipient. Validated with the shared StrKey
@@ -159,17 +116,10 @@ exports.ComposeUserResponsesSchema = zod_1.z
159
116
  .array(zod_1.z.string().refine(address_ts_1.isStellarAddress, 'must be a Stellar address (G... or C...)'))
160
117
  .optional(),
161
118
  })
162
- .passthrough();
163
- /** OzAdapterConfig - the per-network OZ built-in instance addresses. */
164
- exports.OzAdapterConfigSchema = zod_1.z.object({
165
- network: exports.NetworkSchema,
166
- instances: zod_1.z.object({
167
- spending_limit: zod_1.z.string(),
168
- simple_threshold: zod_1.z.string(),
169
- weighted_threshold: zod_1.z.string(),
170
- }),
171
- });
172
- // ===== record_transaction =====
119
+ // Strict, not passthrough: a response key the synthesiser no longer reads
120
+ // (or a typo) would otherwise be accepted in silence, and the caller would
121
+ // believe they had answered a question that is still open.
122
+ .strict();
173
123
  exports.RecordTransactionInputSchema = zod_1.z
174
124
  .object({
175
125
  hash: zod_1.z.string().min(1).optional(),
@@ -183,45 +133,17 @@ exports.RecordTransactionInputSchema = zod_1.z
183
133
  .refine((v) => Boolean(v.hash) || Boolean(v.xdr), {
184
134
  message: 'one of `hash` or `xdr` is required',
185
135
  });
186
- // ===== synthesize_policy =====
187
- //
188
- // Discriminated union on `source` exposes BOTH front-ends through ONE tool.
189
- // - `source: 'mandate'` -> calls synthesizeFromMandate
190
- // - `source: 'recording'` -> calls synthesizeFromRecording
191
- exports.SynthesizePolicyMandateInputSchema = zod_1.z.object({
192
- source: zod_1.z.literal('mandate'),
193
- mandate: exports.MandateSpecSchema,
194
- ozConfig: exports.OzAdapterConfigSchema.optional(),
195
- // --explain opt-in. When true, the orchestrator attaches the
196
- // in-memory predicate tree (null for the mandate path) + a minimal
197
- // honest SimulationResult to the success envelope. Absent or false
198
- // -> the success envelope is unchanged (byte-identical to today).
199
- explain: zod_1.z.boolean().optional(),
200
- });
201
- /** Interpreter opt-in for the recording path. Present -> constraints OZ cannot
202
- * express (per-method scoping, invocation-count windows, oracle bounds, exact
203
- * hop paths) lower to a real interpreter predicate document instead of being
204
- * surfaced as warnings. The core deep-validates `smartAccountAddress` (a C...
205
- * contract, not the recording's G... source) and the tighten-only oracle
206
- * bounds; the schema stays light so the core owns the friendly ToolErrors. */
207
136
  exports.InterpreterOptionsSchema = zod_1.z.object({
208
137
  smartAccountAddress: zod_1.z.string(),
209
138
  installNonce: zod_1.z.number().int().positive().optional(),
210
- oracleParams: zod_1.z
211
- .object({
212
- maxStalenessSeconds: zod_1.z.number().int().positive().optional(),
213
- maxDeviationBps: zod_1.z.number().int().positive().optional(),
214
- })
215
- .optional(),
216
139
  });
217
- exports.SynthesizePolicyRecordingInputSchema = zod_1.z.object({
140
+ exports.SynthesizePolicyInputSchema = zod_1.z.object({
218
141
  source: zod_1.z.literal('recording'),
219
142
  recordedTx: exports.RecordedTransactionSchema,
220
143
  network: exports.NetworkSchema,
221
144
  userResponses: exports.ComposeUserResponsesSchema.optional(),
222
145
  confidenceOverride: zod_1.z.object({ threshold: zod_1.z.number().min(0).max(1) }).optional(),
223
146
  interpreter: exports.InterpreterOptionsSchema.optional(),
224
- ozConfig: exports.OzAdapterConfigSchema.optional(),
225
147
  // --explain opt-in. When true, the orchestrator attaches the
226
148
  // in-memory PredicateNode + the corresponding SimulationResult
227
149
  // (real one from the self-verify pipeline when the interpreter is
@@ -232,10 +154,6 @@ exports.SynthesizePolicyRecordingInputSchema = zod_1.z.object({
232
154
  // explain.
233
155
  explain: zod_1.z.boolean().optional(),
234
156
  });
235
- exports.SynthesizePolicyInputSchema = zod_1.z.discriminatedUnion('source', [
236
- exports.SynthesizePolicyMandateInputSchema,
237
- exports.SynthesizePolicyRecordingInputSchema,
238
- ]);
239
157
  // ===== PredicateNode / PredicateLeaf =====
240
158
  //
241
159
  // Hand-written zod mirrors of the predicate grammar in types.ts:132-180.
@@ -262,41 +180,6 @@ exports.PredicateLeafSchema = zod_1.z.lazy(() => zod_1.z.union([
262
180
  element: zod_1.z.number().int().nonnegative(),
263
181
  field: zod_1.z.string(),
264
182
  }),
265
- // num/den are i128 on chain; JSON numbers lose precision past 2^53,
266
- // so the wire is a decimal string. The contract refuses `den == 0` and
267
- // `num <= 0` / `den <= 0` at install (types.ts:162-163 + the install
268
- // gate); mirroring that bound here closes the gap where simulate /
269
- // verify green-light a policy the install gate will reject. The
270
- // positive-only regex is the cheapest unambiguous check.
271
- zod_1.z.object({
272
- kind: zod_1.z.literal('call_arg_scaled'),
273
- index: zod_1.z.number().int().nonnegative(),
274
- num: zod_1.z.string().regex(/^[1-9][0-9]*$/),
275
- den: zod_1.z.string().regex(/^[1-9][0-9]*$/),
276
- }),
277
- zod_1.z.object({ kind: zod_1.z.literal('amount'), token: zod_1.z.string() }),
278
- zod_1.z.object({
279
- kind: zod_1.z.literal('window_spent'),
280
- token: zod_1.z.string(),
281
- windowSeconds: zod_1.z.number().int().positive(),
282
- }),
283
- zod_1.z.object({ kind: zod_1.z.literal('now') }),
284
- // `valid_until` is NOT in the public predicate grammar: the encoder +
285
- // decoder already throw on it, so accepting it here would let a
286
- // hand-crafted payload through the schema and only surface a 200+
287
- // character internal-commentary error at encode time. Drop it - the
288
- // policy's expiry is carried at the install layer (MandateSpec +
289
- // validUntilLedger) instead.
290
- zod_1.z.object({
291
- kind: zod_1.z.literal('invocation_count_in_window'),
292
- windowSecs: zod_1.z.number().int().positive(),
293
- }),
294
- zod_1.z.object({ kind: zod_1.z.literal('oracle_price'), asset: zod_1.z.string() }),
295
- zod_1.z.object({
296
- kind: zod_1.z.literal('oracle_threshold'),
297
- value: zod_1.z.string().regex(/^-?[0-9]+$/),
298
- decimals: zod_1.z.number().int().nonnegative(),
299
- }),
300
183
  zod_1.z.object({ kind: zod_1.z.literal('literal_address'), value: zod_1.z.string() }),
301
184
  zod_1.z.object({ kind: zod_1.z.literal('literal_i128'), value: zod_1.z.string().regex(/^-?[0-9]+$/) }),
302
185
  zod_1.z.object({ kind: zod_1.z.literal('literal_symbol'), value: zod_1.z.string() }),
@@ -308,36 +191,20 @@ exports.PredicateLeafSchema = zod_1.z.lazy(() => zod_1.z.union([
308
191
  kind: zod_1.z.literal('literal_u32'),
309
192
  value: zod_1.z.number().int().nonnegative().max(U32_MAX),
310
193
  }),
311
- zod_1.z.object({ kind: zod_1.z.literal('literal_u64'), value: zod_1.z.string().regex(/^[0-9]+$/) }),
312
- // `literal_bytes` is hex on chain. `Buffer.from(value, 'hex')` silently
313
- // drops non-hex chars AND yields an empty buffer when the whole input
314
- // is non-hex - so a user passing 'zzzz' would get a predicate that
315
- // compares against empty bytes instead of being rejected. The
316
- // strict even-length hex regex closes that gap at the boundary.
317
- zod_1.z.object({
318
- kind: zod_1.z.literal('literal_bytes'),
319
- value: zod_1.z
320
- .string()
321
- .regex(/^[0-9a-fA-F]*$/)
322
- .refine((v) => v.length % 2 === 0, 'must be even-length hex'),
323
- }),
324
194
  // elements are themselves PredicateLeaf; lazy to break the cycle.
325
195
  zod_1.z.object({ kind: zod_1.z.literal('literal_vec'), elements: zod_1.z.array(exports.PredicateLeafSchema) }),
326
196
  ]));
327
197
  /** PredicateNode mirrors the core `PredicateNode` union. Recursive through
328
- * `and`/`or`/`not`; the lazy + annotation pattern keeps the recursion
329
- * type-safe. */
198
+ * `and`; the lazy + annotation pattern keeps the recursion type-safe. */
330
199
  exports.PredicateNodeSchema = zod_1.z.lazy(() => zod_1.z.union([
331
200
  zod_1.z.object({ op: zod_1.z.literal('and'), children: zod_1.z.array(exports.PredicateNodeSchema) }),
332
- zod_1.z.object({ op: zod_1.z.literal('or'), children: zod_1.z.array(exports.PredicateNodeSchema) }),
333
- zod_1.z.object({ op: zod_1.z.literal('not'), child: exports.PredicateNodeSchema }),
334
201
  zod_1.z.object({
335
202
  op: zod_1.z.literal('eq'),
336
203
  left: exports.PredicateLeafSchema,
337
204
  right: exports.PredicateLeafSchema,
338
205
  }),
339
206
  zod_1.z.object({
340
- op: zod_1.z.enum(['lt', 'lte', 'gt', 'gte']),
207
+ op: zod_1.z.literal('lte'),
341
208
  left: exports.PredicateLeafSchema,
342
209
  right: exports.PredicateLeafSchema,
343
210
  }),
@@ -349,38 +216,16 @@ exports.PredicateNodeSchema = zod_1.z.lazy(() => zod_1.z.union([
349
216
  ]));
350
217
  // ===== simulate_policy / verify_policy =====
351
218
  //
352
- // The oracle fixture shape mirrors `SimulateOptions` / `VerifyOptions`
353
- // in packages/policy-synth/src/verify/{simulate,verify}.ts - the same
354
- // sorted set of error enum values is used by both engines. u32
355
- // `validUntilLedger` is bounded at the boundary so a hand-crafted
356
- // payload cannot leak a u32 overflow into the engine's permit context.
357
- /** Oracle price fixture - mirror of the entry type in `SimulateOptions` /
358
- * `VerifyOptions`. Discriminated by presence of `error` vs `price`. */
359
- exports.OraclePriceFixtureSchema = zod_1.z.union([
360
- zod_1.z.object({
361
- price: zod_1.z.string().regex(/^[0-9]+$/),
362
- timestampSeconds: zod_1.z.number().int().nonnegative(),
363
- }),
364
- zod_1.z.object({
365
- error: zod_1.z.enum(['stale', 'missing', 'deviation', 'paused', 'decimals', 'fingerprint']),
366
- }),
367
- ]);
219
+ // Both tools evaluate the same predicate against the same recording, so they
220
+ // take the same input. A null predicate used to mean "OZ built-in policies
221
+ // only"; that backend is gone, so every policy carries a predicate and there is
222
+ // nothing to simulate without one.
368
223
  exports.SimulatePolicyInputSchema = zod_1.z.object({
369
- // simulatePolicy accepts a null predicate (OZ-only policy); verifyPolicy
370
- // does not. The asymmetry is mirrored at the schema boundary.
371
- predicate: exports.PredicateNodeSchema.nullable(),
372
- permitTx: exports.RecordedTransactionSchema,
373
- validUntilLedger: zod_1.z.number().int().positive().max(U32_MAX).optional(),
374
- oraclePricesByAsset: zod_1.z.record(zod_1.z.string(), exports.OraclePriceFixtureSchema).optional(),
375
- });
376
- exports.VerifyPolicyInputSchema = zod_1.z.object({
377
- // verifyPolicy requires a non-null predicate; the engine refuses
378
- // `null` outright. The schema mirrors that contract.
379
224
  predicate: exports.PredicateNodeSchema,
380
225
  permitTx: exports.RecordedTransactionSchema,
381
226
  validUntilLedger: zod_1.z.number().int().positive().max(U32_MAX).optional(),
382
- oraclePricesByAsset: zod_1.z.record(zod_1.z.string(), exports.OraclePriceFixtureSchema).optional(),
383
227
  });
228
+ exports.VerifyPolicyInputSchema = exports.SimulatePolicyInputSchema;
384
229
  // ===== install_policy / revoke_policy / get_interpreter_info =====
385
230
  //
386
231
  // The install/revoke pair drives the OZ smart-account admin flow. Both surface
@@ -393,7 +238,7 @@ exports.VerifyPolicyInputSchema = zod_1.z.object({
393
238
  // optional `grammar_version()` RPC call to check whether the deployed contract
394
239
  // matches the pin. A mismatch is worth MORE than a fabricated audit field.
395
240
  //
396
- // All three input schemas live BELOW the MandateSpecSchemaForRule declaration
241
+ // All three input schemas live BELOW the ContextRuleDraftSchema declaration
397
242
  // so the const reference there is not in the temporal dead zone (no JS hoisting
398
243
  // for `const`).
399
244
  /** Minimal rule-draft schema for the install input. Mirrors ContextRuleDraft
@@ -404,7 +249,7 @@ exports.VerifyPolicyInputSchema = zod_1.z.object({
404
249
  * the temporal dead zone (no JS hoisting for `const`). */
405
250
  const MAX_SIGNERS_PER_RULE = 15;
406
251
  const MAX_POLICIES_PER_RULE = 5;
407
- const MandateSpecSchemaForRule = zod_1.z
252
+ const ContextRuleDraftSchema = zod_1.z
408
253
  .object({
409
254
  contextRuleType: zod_1.z.discriminatedUnion('kind', [
410
255
  zod_1.z.object({ kind: zod_1.z.literal('default') }),
@@ -424,21 +269,11 @@ const MandateSpecSchemaForRule = zod_1.z
424
269
  ]))
425
270
  .max(MAX_SIGNERS_PER_RULE),
426
271
  policies: zod_1.z
427
- .array(zod_1.z.discriminatedUnion('kind', [
428
- zod_1.z.object({
429
- kind: zod_1.z.literal('interpreter'),
430
- interpreterAddress: zod_1.z.string(),
431
- predicateBlobBase64: zod_1.z.string().min(1),
432
- }),
433
- zod_1.z.object({
434
- kind: zod_1.z.literal('oz_builtin'),
435
- primitive: zod_1.z.object({
436
- primitive: zod_1.z.enum(['spending_limit', 'simple_threshold', 'weighted_threshold']),
437
- params: zod_1.z.record(zod_1.z.unknown()),
438
- }),
439
- instanceAddress: zod_1.z.string(),
440
- }),
441
- ]))
272
+ .array(zod_1.z.object({
273
+ kind: zod_1.z.literal('interpreter'),
274
+ interpreterAddress: zod_1.z.string(),
275
+ predicateBlobBase64: zod_1.z.string().min(1),
276
+ }))
442
277
  .max(MAX_POLICIES_PER_RULE),
443
278
  })
444
279
  .passthrough()
@@ -448,18 +283,23 @@ const MandateSpecSchemaForRule = zod_1.z
448
283
  // existing four.
449
284
  /** Pinned interpreter address (testnet).
450
285
  * Single source for the MCP layer; do not embed elsewhere. */
451
- exports.PINNED_INTERPRETER_TESTNET_ADDRESS = 'CDR4NLV22STCXFGZPNKDQTEANWLF7LZ6AJLY6B7CLJXKHDZGYJWIOKGP';
452
- /** Pinned interpreter address (mainnet). Mainnet
453
- * has now been deployed (2026-08-04); the mainnet interpreter IS the binary
454
- * that was exercised on testnet (same wasm sha256, see
455
- * PINNED_INTERPRETER_WASM_SHA256). The address differs because instance
456
- * ids are network-scoped. UNAUDITED at the time of writing. */
457
- exports.PINNED_INTERPRETER_MAINNET_ADDRESS = 'CALZAMUPREIRY4TULBEXIK77AUTOEJG63XLCPUWEHHQDOVK6ZVVS7VQ2';
286
+ exports.PINNED_INTERPRETER_TESTNET_ADDRESS = 'CCL336TCK2Y5OFNRCMN2M3HVPBCEX4PW5H6EQ5VW5NPMXOCP4ESB5XR4';
287
+ /** Pinned interpreter address (mainnet), redeployed 2026-08-22 from a reproducible build. The mainnet
288
+ * interpreter IS the binary exercised on testnet - both instances were created
289
+ * from the same uploaded wasm hash (see PINNED_INTERPRETER_WASM_SHA256), and
290
+ * both were read back with `grammar_version()` returning 3. The address differs
291
+ * because instance ids are network-scoped. UNAUDITED at the time of writing.
292
+ *
293
+ * These four constants move together or not at all. The grammar version and
294
+ * wasm hash are single values covering BOTH networks, so re-pinning one network
295
+ * alone would have the builder emit a version the other network refuses - with
296
+ * a green test run, since `grammar-version-parity.test.ts` would then pass. */
297
+ exports.PINNED_INTERPRETER_MAINNET_ADDRESS = 'CBZXLSTQUITBFZHQH6XRXF3XIVRQR4RHRI64Q5WELS5KGY3ZKJPFWDPF';
458
298
  /** Pinned interpreter wasm sha256 (hex). */
459
- exports.PINNED_INTERPRETER_WASM_SHA256 = '6e6c13d93e197aa380303a42cd120f5ddb080dd36ef2a343ee1dbd04ca52a443';
299
+ exports.PINNED_INTERPRETER_WASM_SHA256 = 'a2b36e8ac5a61caf3757af26aa79e83f2995b451099f44772383806a55fe3414';
460
300
  /** The grammar version the interpreter enforces (matches SELF_VERSION in
461
301
  * contracts/policy-interpreter/src/version.rs). */
462
- exports.PINNED_INTERPRETER_GRAMMAR_VERSION = 1;
302
+ exports.PINNED_INTERPRETER_GRAMMAR_VERSION = 3;
463
303
  /** Default Soroban RPC for the install / revoke / info tools. The recorder
464
304
  * keeps its own copy in record/rpc.ts because it hands back a fetcher rather
465
305
  * than a Server; the two are deliberately different surfaces, so this is
@@ -482,9 +322,6 @@ exports.RPC_URL_BY_NETWORK = {
482
322
  testnet: exports.TESTNET_RPC_URL,
483
323
  mainnet: exports.MAINNET_RPC_URL,
484
324
  };
485
- /** Soroban `valid_until` window (ledgers) added to the latest ledger when
486
- * building the auth entry. ~25 minutes at 5s/ledger. */
487
- exports.DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300;
488
325
  /** Stellar network passphrases. Pinned here so the XDR envelope uses the
489
326
  * matching passphrase when the wallet signs (a mismatch yields invalid
490
327
  * hashes). */
@@ -521,7 +358,7 @@ exports.InstallPolicyInputSchema = zod_1.z
521
358
  * pin and RPC pin do not move by themselves). */
522
359
  network: exports.NetworkSchema.optional(),
523
360
  /** The proposed rule draft. Mirrors the core `ContextRuleDraft` shape. */
524
- rule: MandateSpecSchemaForRule,
361
+ rule: ContextRuleDraftSchema,
525
362
  /** Per-rule install nonce; 1 for a fresh install. */
526
363
  installNonce: zod_1.z.number().int().positive(),
527
364
  /** Optional RPC URL override. Defaults to the pinned RPC for the
@@ -25,9 +25,7 @@ export type CustodyMode = 'enforce' | 'log_only';
25
25
  /** What a backend can express. A construct needing a false flag is flagged
26
26
  * `uncovered` by the adapter, never silently dropped. */
27
27
  export interface CustodyCapabilities {
28
- supportsOraclePrice: boolean;
29
28
  supportsSpendWindow: boolean;
30
- supportsInvocationCount: boolean;
31
29
  supportsTimeExpiry: boolean;
32
30
  supportsThreshold: boolean;
33
31
  supportsGeneralPredicate: boolean;
@@ -0,0 +1,26 @@
1
+ import type { PredicateNode, ScVal } from '../types.ts';
2
+ import type { EvalContext } from './evaluate.ts';
3
+ export interface DenyCase {
4
+ dimension: string;
5
+ ctx: EvalContext;
6
+ /** Canonical reason the Rust interpreter returns for this dimension.
7
+ * Optional: when set, `runHarness` asserts the TS evaluator emits this
8
+ * exact string, so a future TS/Rust reason divergence fails CI. When
9
+ * absent the case is only checked on the boolean decision. */
10
+ expectedReason?: string;
11
+ }
12
+ export interface GeneratedCases {
13
+ permit: EvalContext;
14
+ denies: DenyCase[];
15
+ }
16
+ /** Build deterministic model-evaluated alternatives without mutating the intended call.
17
+ *
18
+ * @param predicate - the synthesized predicate
19
+ * @param permitCtx - EvalContext for the intended (permitted) call
20
+ * @param dimensions - optional whitelist of dimension names to emit; when omitted
21
+ * all known dimensions (including over-permissiveness mutations)
22
+ * are emitted. */
23
+ export declare function generateCases(predicate: PredicateNode, permitCtx: EvalContext, dimensions?: string[]): GeneratedCases;
24
+ /** Deep-copy an ScVal so a mutation cannot alias the recorded call.
25
+ * Exported so the permit-context builder shares this one implementation. */
26
+ export declare function cloneScVal(value: ScVal, depth?: number): ScVal;