@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.
- package/README.md +6 -5
- package/dist/adapters/interpreter/adapter.d.ts +19 -25
- package/dist/adapters/interpreter/adapter.js +54 -487
- package/dist/adapters/interpreter/index.d.ts +1 -1
- package/dist/adapters/interpreter/index.js +1 -1
- package/dist/adapters/oz/adapter.js +1 -15
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/install/build-add-context-rule.d.ts +3 -11
- package/dist/install/build-add-context-rule.js +2 -97
- package/dist/install/build-install-policy.d.ts +4 -53
- package/dist/install/build-install-policy.js +44 -123
- package/dist/ir/index.d.ts +1 -1
- package/dist/ir/types.d.ts +7 -76
- package/dist/ir/types.js +0 -2
- package/dist/predicate/decode.d.ts +1 -1
- package/dist/predicate/decode.js +2 -70
- package/dist/predicate/encode.js +45 -229
- package/dist/predicate/from-json.js +1 -42
- package/dist/record/decode.js +3 -8
- package/dist/review-card/builder.d.ts +4 -3
- package/dist/review-card/builder.js +18 -162
- package/dist/review-card/cross-check.js +2 -105
- package/dist/review-card/render-leaf.d.ts +4 -0
- package/dist/review-card/render-leaf.js +47 -0
- package/dist/run/index.d.ts +35 -53
- package/dist/run/index.js +115 -156
- package/dist/run/schemas.d.ts +168 -1650
- package/dist/run/schemas.js +36 -199
- package/dist/seams/types.d.ts +0 -2
- package/dist/simulate/deny-cases.d.ts +26 -0
- package/dist/simulate/deny-cases.js +282 -0
- package/dist/simulate/evaluate.d.ts +18 -0
- package/dist/simulate/evaluate.js +271 -0
- package/dist/simulate/index.d.ts +4 -0
- package/dist/simulate/index.js +8 -0
- package/dist/synth/compose-from-recording.d.ts +24 -51
- package/dist/synth/compose-from-recording.js +120 -282
- package/dist/synth/deny-cases.d.ts +5 -7
- package/dist/synth/deny-cases.js +23 -373
- package/dist/synth/evaluate.d.ts +0 -21
- package/dist/synth/evaluate.js +56 -336
- package/dist/synth/index.d.ts +0 -5
- package/dist/synth/index.js +0 -5
- package/dist/synth/permit-context.d.ts +2 -9
- package/dist/synth/permit-context.js +1 -82
- package/dist/synth/synthesize-from-recording.d.ts +1 -29
- package/dist/synth/synthesize-from-recording.js +41 -356
- package/dist/types.d.ts +8 -60
- package/dist/types.js +5 -2
- package/dist/verify/simulate.d.ts +1 -14
- package/dist/verify/simulate.js +2 -88
- package/dist/verify/verify.d.ts +1 -7
- package/dist/verify/verify.js +0 -8
- package/dist-cjs/adapters/interpreter/adapter.d.ts +19 -25
- package/dist-cjs/adapters/interpreter/adapter.js +56 -489
- package/dist-cjs/adapters/interpreter/index.d.ts +1 -1
- package/dist-cjs/adapters/interpreter/index.js +1 -2
- package/dist-cjs/adapters/oz/adapter.js +1 -15
- package/dist-cjs/errors.d.ts +1 -1
- package/dist-cjs/index.d.ts +1 -4
- package/dist-cjs/index.js +1 -4
- package/dist-cjs/install/build-add-context-rule.d.ts +3 -11
- package/dist-cjs/install/build-add-context-rule.js +1 -96
- package/dist-cjs/install/build-install-policy.d.ts +4 -53
- package/dist-cjs/install/build-install-policy.js +44 -122
- package/dist-cjs/ir/index.d.ts +1 -1
- package/dist-cjs/ir/types.d.ts +7 -76
- package/dist-cjs/ir/types.js +0 -2
- package/dist-cjs/predicate/decode.d.ts +1 -1
- package/dist-cjs/predicate/decode.js +2 -70
- package/dist-cjs/predicate/encode.js +45 -229
- package/dist-cjs/predicate/from-json.js +1 -42
- package/dist-cjs/record/decode.js +3 -8
- package/dist-cjs/review-card/builder.d.ts +4 -3
- package/dist-cjs/review-card/builder.js +23 -167
- package/dist-cjs/review-card/cross-check.js +7 -110
- package/dist-cjs/review-card/render-leaf.d.ts +4 -0
- package/dist-cjs/review-card/render-leaf.js +52 -0
- package/dist-cjs/run/index.d.ts +35 -53
- package/dist-cjs/run/index.js +116 -162
- package/dist-cjs/run/schemas.d.ts +168 -1650
- package/dist-cjs/run/schemas.js +37 -200
- package/dist-cjs/seams/types.d.ts +0 -2
- package/dist-cjs/simulate/deny-cases.d.ts +26 -0
- package/dist-cjs/simulate/deny-cases.js +286 -0
- package/dist-cjs/simulate/evaluate.d.ts +18 -0
- package/dist-cjs/simulate/evaluate.js +274 -0
- package/dist-cjs/simulate/index.d.ts +4 -0
- package/dist-cjs/simulate/index.js +13 -0
- package/dist-cjs/synth/compose-from-recording.d.ts +24 -51
- package/dist-cjs/synth/compose-from-recording.js +120 -282
- package/dist-cjs/synth/deny-cases.d.ts +5 -7
- package/dist-cjs/synth/deny-cases.js +23 -374
- package/dist-cjs/synth/evaluate.d.ts +0 -21
- package/dist-cjs/synth/evaluate.js +57 -337
- package/dist-cjs/synth/index.d.ts +0 -5
- package/dist-cjs/synth/index.js +1 -11
- package/dist-cjs/synth/permit-context.d.ts +2 -9
- package/dist-cjs/synth/permit-context.js +1 -82
- package/dist-cjs/synth/synthesize-from-recording.d.ts +1 -29
- package/dist-cjs/synth/synthesize-from-recording.js +39 -354
- package/dist-cjs/types.d.ts +8 -60
- package/dist-cjs/types.js +6 -3
- package/dist-cjs/verify/simulate.d.ts +1 -14
- package/dist-cjs/verify/simulate.js +2 -88
- package/dist-cjs/verify/verify.d.ts +1 -7
- package/dist-cjs/verify/verify.js +0 -8
- package/package.json +4 -4
- package/src/adapters/interpreter/adapter.ts +76 -572
- package/src/errors.ts +0 -19
- package/src/index.ts +1 -4
- package/src/install/build-add-context-rule.ts +4 -127
- package/src/install/build-install-policy.ts +93 -214
- package/src/predicate/decode.ts +2 -70
- package/src/predicate/encode.ts +49 -261
- package/src/predicate/from-json.ts +1 -43
- package/src/record/decode.ts +3 -8
- package/src/review-card/builder.ts +19 -168
- package/src/review-card/cross-check.ts +3 -105
- package/src/review-card/render-leaf.ts +48 -0
- package/src/run/index.ts +130 -186
- package/src/run/schemas.ts +38 -214
- package/src/simulate/deny-cases.ts +334 -0
- package/src/simulate/evaluate.ts +284 -0
- package/src/simulate/index.ts +11 -0
- package/src/synth/compose-from-recording.ts +148 -347
- package/src/synth/index.ts +0 -13
- package/src/synth/synthesize-from-recording.ts +43 -456
- package/src/types.ts +13 -49
- package/dist/install/build-install-predicate.d.ts +0 -96
- package/dist/install/build-install-predicate.js +0 -444
- package/dist-cjs/install/build-install-predicate.d.ts +0 -96
- package/dist-cjs/install/build-install-predicate.js +0 -479
- package/src/adapters/interpreter/index.ts +0 -8
- package/src/adapters/oz/adapter.ts +0 -376
- package/src/adapters/oz/index.ts +0 -9
- package/src/codegen/compile-gate.ts +0 -167
- package/src/codegen/index.ts +0 -17
- package/src/codegen/template.ts +0 -165
- package/src/ir/index.ts +0 -13
- package/src/ir/types.ts +0 -132
- package/src/mandate/index.ts +0 -4
- package/src/mandate/to-ir.ts +0 -71
- package/src/mandate/types.ts +0 -21
- package/src/seams/index.ts +0 -11
- package/src/seams/types.ts +0 -81
- package/src/synth/deny-cases.ts +0 -663
- package/src/synth/evaluate.ts +0 -613
- package/src/synth/harness.ts +0 -68
- package/src/synth/minimize.ts +0 -48
- package/src/synth/permit-context.ts +0 -136
- package/src/synth/predicate-literals.ts +0 -27
- package/src/synth/synthesize-from-mandate.ts +0 -82
- package/src/verify/envelope.ts +0 -28
- package/src/verify/index.ts +0 -5
- package/src/verify/simulate.ts +0 -311
- package/src/verify/verify.ts +0 -243
package/src/run/schemas.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
//
|
|
9
9
|
// i128 amounts and other large integers are carried as base-10 decimal strings
|
|
10
10
|
// end-to-end. Networks are pinned to the same closed set the core defines.
|
|
11
|
-
//
|
|
12
|
-
//
|
|
11
|
+
// `source` is a single-variant discriminator (`recording`); it stays a union
|
|
12
|
+
// so an unknown value is rejected with a discriminator error.
|
|
13
13
|
//
|
|
14
14
|
// This module is the SINGLE source of truth for these shapes. The MCP package
|
|
15
15
|
// imports them here so its tool-shape bindings stay in step; the CLI imports
|
|
@@ -113,62 +113,13 @@ export const RecordedTransactionSchema = z
|
|
|
113
113
|
})
|
|
114
114
|
.passthrough()
|
|
115
115
|
|
|
116
|
-
/** MandateSpec mirrors the core MandateSpec. The deterministic Mandate
|
|
117
|
-
* front-end needs no parseConfidence; the tool adapter injects the full
|
|
118
|
-
* confidence after synthesis so the orchestrator can compare. */
|
|
119
|
-
export const MandateSpecSchema = z
|
|
120
|
-
.object({
|
|
121
|
-
chain: z.literal('stellar'),
|
|
122
|
-
contract: z.string(),
|
|
123
|
-
method: z.string().optional(),
|
|
124
|
-
spendingLimit: z
|
|
125
|
-
.object({
|
|
126
|
-
token: z.string(),
|
|
127
|
-
limit: z.string().regex(/^[0-9]+$/),
|
|
128
|
-
windowSeconds: z.number().int().positive(),
|
|
129
|
-
})
|
|
130
|
-
.optional(),
|
|
131
|
-
// A threshold of 0 means "0 approvals", which is not a real M-of-N gate.
|
|
132
|
-
approvalThreshold: z.number().int().positive().optional(),
|
|
133
|
-
recipients: z.array(z.string()).optional(),
|
|
134
|
-
expiry: z
|
|
135
|
-
.object({
|
|
136
|
-
validUntilLedger: z.number().int().positive().max(U32_MAX).optional(),
|
|
137
|
-
validUntilUnixSeconds: z.number().int().positive().optional(),
|
|
138
|
-
})
|
|
139
|
-
.optional(),
|
|
140
|
-
})
|
|
141
|
-
.passthrough()
|
|
142
|
-
// TS-F4/F6: a `recipients` allowlist is only meaningful against a SEP-41
|
|
143
|
-
// method whose arg 1 IS the recipient (SAC/SEP-41 `transfer(from, to,
|
|
144
|
-
// amount)` and SEP-41 `mint(to, amount)`). Without this gate the
|
|
145
|
-
// `to-ir.ts` lowering pins the allowlist to `RECIPIENT_ARG_INDEX = 1`
|
|
146
|
-
// for any contract+method, which would let a non-SEP-41 method's
|
|
147
|
-
// arg[1] (e.g. an amount, an op type, an arbitrary address payload)
|
|
148
|
-
// be silently constrained as if it were a recipient. Refusing
|
|
149
|
-
// non-SEP-41 methods at the boundary is the fail-closed shape.
|
|
150
|
-
.refine(
|
|
151
|
-
(v) =>
|
|
152
|
-
v.recipients === undefined ||
|
|
153
|
-
v.recipients.length === 0 ||
|
|
154
|
-
v.method === 'transfer' ||
|
|
155
|
-
v.method === 'mint',
|
|
156
|
-
{
|
|
157
|
-
message:
|
|
158
|
-
'recipients is only valid when method is a SEP-41 method (transfer or mint); other methods do not have a recipient at arg[1]',
|
|
159
|
-
}
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
/** ComposeUserResponses mirrors the core. */
|
|
163
116
|
export const ComposeUserResponsesSchema = z
|
|
164
117
|
.object({
|
|
165
|
-
windowSeconds: z.number().int().positive().optional(),
|
|
166
118
|
validUntilLedger: z.number().int().positive().max(U32_MAX).optional(),
|
|
167
119
|
limitAmount: z
|
|
168
120
|
.string()
|
|
169
121
|
.regex(/^[0-9]+$/)
|
|
170
122
|
.optional(),
|
|
171
|
-
invocationLimit: z.number().int().positive().optional(),
|
|
172
123
|
// Swap recipient allowlist (SoroSwap call_arg[3]). Each entry must be a
|
|
173
124
|
// Stellar address (G... wallet or C... contract); supplying it REPLACES the
|
|
174
125
|
// default pin to the recorded recipient. Validated with the shared StrKey
|
|
@@ -177,19 +128,10 @@ export const ComposeUserResponsesSchema = z
|
|
|
177
128
|
.array(z.string().refine(isStellarAddress, 'must be a Stellar address (G... or C...)'))
|
|
178
129
|
.optional(),
|
|
179
130
|
})
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
network: NetworkSchema,
|
|
185
|
-
instances: z.object({
|
|
186
|
-
spending_limit: z.string(),
|
|
187
|
-
simple_threshold: z.string(),
|
|
188
|
-
weighted_threshold: z.string(),
|
|
189
|
-
}),
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// ===== record_transaction =====
|
|
131
|
+
// Strict, not passthrough: a response key the synthesiser no longer reads
|
|
132
|
+
// (or a typo) would otherwise be accepted in silence, and the caller would
|
|
133
|
+
// believe they had answered a question that is still open.
|
|
134
|
+
.strict()
|
|
193
135
|
|
|
194
136
|
export const RecordTransactionInputSchema = z
|
|
195
137
|
.object({
|
|
@@ -206,48 +148,18 @@ export const RecordTransactionInputSchema = z
|
|
|
206
148
|
})
|
|
207
149
|
export type RecordTransactionInput = z.infer<typeof RecordTransactionInputSchema>
|
|
208
150
|
|
|
209
|
-
// ===== synthesize_policy =====
|
|
210
|
-
//
|
|
211
|
-
// Discriminated union on `source` exposes BOTH front-ends through ONE tool.
|
|
212
|
-
// - `source: 'mandate'` -> calls synthesizeFromMandate
|
|
213
|
-
// - `source: 'recording'` -> calls synthesizeFromRecording
|
|
214
|
-
|
|
215
|
-
export const SynthesizePolicyMandateInputSchema = z.object({
|
|
216
|
-
source: z.literal('mandate'),
|
|
217
|
-
mandate: MandateSpecSchema,
|
|
218
|
-
ozConfig: OzAdapterConfigSchema.optional(),
|
|
219
|
-
// --explain opt-in. When true, the orchestrator attaches the
|
|
220
|
-
// in-memory predicate tree (null for the mandate path) + a minimal
|
|
221
|
-
// honest SimulationResult to the success envelope. Absent or false
|
|
222
|
-
// -> the success envelope is unchanged (byte-identical to today).
|
|
223
|
-
explain: z.boolean().optional(),
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
/** Interpreter opt-in for the recording path. Present -> constraints OZ cannot
|
|
227
|
-
* express (per-method scoping, invocation-count windows, oracle bounds, exact
|
|
228
|
-
* hop paths) lower to a real interpreter predicate document instead of being
|
|
229
|
-
* surfaced as warnings. The core deep-validates `smartAccountAddress` (a C...
|
|
230
|
-
* contract, not the recording's G... source) and the tighten-only oracle
|
|
231
|
-
* bounds; the schema stays light so the core owns the friendly ToolErrors. */
|
|
232
151
|
export const InterpreterOptionsSchema = z.object({
|
|
233
152
|
smartAccountAddress: z.string(),
|
|
234
153
|
installNonce: z.number().int().positive().optional(),
|
|
235
|
-
oracleParams: z
|
|
236
|
-
.object({
|
|
237
|
-
maxStalenessSeconds: z.number().int().positive().optional(),
|
|
238
|
-
maxDeviationBps: z.number().int().positive().optional(),
|
|
239
|
-
})
|
|
240
|
-
.optional(),
|
|
241
154
|
})
|
|
242
155
|
|
|
243
|
-
export const
|
|
156
|
+
export const SynthesizePolicyInputSchema = z.object({
|
|
244
157
|
source: z.literal('recording'),
|
|
245
158
|
recordedTx: RecordedTransactionSchema,
|
|
246
159
|
network: NetworkSchema,
|
|
247
160
|
userResponses: ComposeUserResponsesSchema.optional(),
|
|
248
161
|
confidenceOverride: z.object({ threshold: z.number().min(0).max(1) }).optional(),
|
|
249
162
|
interpreter: InterpreterOptionsSchema.optional(),
|
|
250
|
-
ozConfig: OzAdapterConfigSchema.optional(),
|
|
251
163
|
// --explain opt-in. When true, the orchestrator attaches the
|
|
252
164
|
// in-memory PredicateNode + the corresponding SimulationResult
|
|
253
165
|
// (real one from the self-verify pipeline when the interpreter is
|
|
@@ -259,10 +171,6 @@ export const SynthesizePolicyRecordingInputSchema = z.object({
|
|
|
259
171
|
explain: z.boolean().optional(),
|
|
260
172
|
})
|
|
261
173
|
|
|
262
|
-
export const SynthesizePolicyInputSchema = z.discriminatedUnion('source', [
|
|
263
|
-
SynthesizePolicyMandateInputSchema,
|
|
264
|
-
SynthesizePolicyRecordingInputSchema,
|
|
265
|
-
])
|
|
266
174
|
export type SynthesizePolicyInput = z.infer<typeof SynthesizePolicyInputSchema>
|
|
267
175
|
|
|
268
176
|
// ===== PredicateNode / PredicateLeaf =====
|
|
@@ -293,41 +201,6 @@ export const PredicateLeafSchema: z.ZodType<unknown> = z.lazy(() =>
|
|
|
293
201
|
element: z.number().int().nonnegative(),
|
|
294
202
|
field: z.string(),
|
|
295
203
|
}),
|
|
296
|
-
// num/den are i128 on chain; JSON numbers lose precision past 2^53,
|
|
297
|
-
// so the wire is a decimal string. The contract refuses `den == 0` and
|
|
298
|
-
// `num <= 0` / `den <= 0` at install (types.ts:162-163 + the install
|
|
299
|
-
// gate); mirroring that bound here closes the gap where simulate /
|
|
300
|
-
// verify green-light a policy the install gate will reject. The
|
|
301
|
-
// positive-only regex is the cheapest unambiguous check.
|
|
302
|
-
z.object({
|
|
303
|
-
kind: z.literal('call_arg_scaled'),
|
|
304
|
-
index: z.number().int().nonnegative(),
|
|
305
|
-
num: z.string().regex(/^[1-9][0-9]*$/),
|
|
306
|
-
den: z.string().regex(/^[1-9][0-9]*$/),
|
|
307
|
-
}),
|
|
308
|
-
z.object({ kind: z.literal('amount'), token: z.string() }),
|
|
309
|
-
z.object({
|
|
310
|
-
kind: z.literal('window_spent'),
|
|
311
|
-
token: z.string(),
|
|
312
|
-
windowSeconds: z.number().int().positive(),
|
|
313
|
-
}),
|
|
314
|
-
z.object({ kind: z.literal('now') }),
|
|
315
|
-
// `valid_until` is NOT in the public predicate grammar: the encoder +
|
|
316
|
-
// decoder already throw on it, so accepting it here would let a
|
|
317
|
-
// hand-crafted payload through the schema and only surface a 200+
|
|
318
|
-
// character internal-commentary error at encode time. Drop it - the
|
|
319
|
-
// policy's expiry is carried at the install layer (MandateSpec +
|
|
320
|
-
// validUntilLedger) instead.
|
|
321
|
-
z.object({
|
|
322
|
-
kind: z.literal('invocation_count_in_window'),
|
|
323
|
-
windowSecs: z.number().int().positive(),
|
|
324
|
-
}),
|
|
325
|
-
z.object({ kind: z.literal('oracle_price'), asset: z.string() }),
|
|
326
|
-
z.object({
|
|
327
|
-
kind: z.literal('oracle_threshold'),
|
|
328
|
-
value: z.string().regex(/^-?[0-9]+$/),
|
|
329
|
-
decimals: z.number().int().nonnegative(),
|
|
330
|
-
}),
|
|
331
204
|
z.object({ kind: z.literal('literal_address'), value: z.string() }),
|
|
332
205
|
z.object({ kind: z.literal('literal_i128'), value: z.string().regex(/^-?[0-9]+$/) }),
|
|
333
206
|
z.object({ kind: z.literal('literal_symbol'), value: z.string() }),
|
|
@@ -339,39 +212,23 @@ export const PredicateLeafSchema: z.ZodType<unknown> = z.lazy(() =>
|
|
|
339
212
|
kind: z.literal('literal_u32'),
|
|
340
213
|
value: z.number().int().nonnegative().max(U32_MAX),
|
|
341
214
|
}),
|
|
342
|
-
z.object({ kind: z.literal('literal_u64'), value: z.string().regex(/^[0-9]+$/) }),
|
|
343
|
-
// `literal_bytes` is hex on chain. `Buffer.from(value, 'hex')` silently
|
|
344
|
-
// drops non-hex chars AND yields an empty buffer when the whole input
|
|
345
|
-
// is non-hex - so a user passing 'zzzz' would get a predicate that
|
|
346
|
-
// compares against empty bytes instead of being rejected. The
|
|
347
|
-
// strict even-length hex regex closes that gap at the boundary.
|
|
348
|
-
z.object({
|
|
349
|
-
kind: z.literal('literal_bytes'),
|
|
350
|
-
value: z
|
|
351
|
-
.string()
|
|
352
|
-
.regex(/^[0-9a-fA-F]*$/)
|
|
353
|
-
.refine((v) => v.length % 2 === 0, 'must be even-length hex'),
|
|
354
|
-
}),
|
|
355
215
|
// elements are themselves PredicateLeaf; lazy to break the cycle.
|
|
356
216
|
z.object({ kind: z.literal('literal_vec'), elements: z.array(PredicateLeafSchema) }),
|
|
357
217
|
])
|
|
358
218
|
)
|
|
359
219
|
|
|
360
220
|
/** PredicateNode mirrors the core `PredicateNode` union. Recursive through
|
|
361
|
-
* `and
|
|
362
|
-
* type-safe. */
|
|
221
|
+
* `and`; the lazy + annotation pattern keeps the recursion type-safe. */
|
|
363
222
|
export const PredicateNodeSchema: z.ZodType<unknown> = z.lazy(() =>
|
|
364
223
|
z.union([
|
|
365
224
|
z.object({ op: z.literal('and'), children: z.array(PredicateNodeSchema) }),
|
|
366
|
-
z.object({ op: z.literal('or'), children: z.array(PredicateNodeSchema) }),
|
|
367
|
-
z.object({ op: z.literal('not'), child: PredicateNodeSchema }),
|
|
368
225
|
z.object({
|
|
369
226
|
op: z.literal('eq'),
|
|
370
227
|
left: PredicateLeafSchema,
|
|
371
228
|
right: PredicateLeafSchema,
|
|
372
229
|
}),
|
|
373
230
|
z.object({
|
|
374
|
-
op: z.
|
|
231
|
+
op: z.literal('lte'),
|
|
375
232
|
left: PredicateLeafSchema,
|
|
376
233
|
right: PredicateLeafSchema,
|
|
377
234
|
}),
|
|
@@ -385,42 +242,18 @@ export const PredicateNodeSchema: z.ZodType<unknown> = z.lazy(() =>
|
|
|
385
242
|
|
|
386
243
|
// ===== simulate_policy / verify_policy =====
|
|
387
244
|
//
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
// payload cannot leak a u32 overflow into the engine's permit context.
|
|
393
|
-
|
|
394
|
-
/** Oracle price fixture - mirror of the entry type in `SimulateOptions` /
|
|
395
|
-
* `VerifyOptions`. Discriminated by presence of `error` vs `price`. */
|
|
396
|
-
export const OraclePriceFixtureSchema = z.union([
|
|
397
|
-
z.object({
|
|
398
|
-
price: z.string().regex(/^[0-9]+$/),
|
|
399
|
-
timestampSeconds: z.number().int().nonnegative(),
|
|
400
|
-
}),
|
|
401
|
-
z.object({
|
|
402
|
-
error: z.enum(['stale', 'missing', 'deviation', 'paused', 'decimals', 'fingerprint']),
|
|
403
|
-
}),
|
|
404
|
-
])
|
|
405
|
-
|
|
245
|
+
// Both tools evaluate the same predicate against the same recording, so they
|
|
246
|
+
// take the same input. A null predicate used to mean "OZ built-in policies
|
|
247
|
+
// only"; that backend is gone, so every policy carries a predicate and there is
|
|
248
|
+
// nothing to simulate without one.
|
|
406
249
|
export const SimulatePolicyInputSchema = z.object({
|
|
407
|
-
|
|
408
|
-
// does not. The asymmetry is mirrored at the schema boundary.
|
|
409
|
-
predicate: PredicateNodeSchema.nullable(),
|
|
250
|
+
predicate: PredicateNodeSchema,
|
|
410
251
|
permitTx: RecordedTransactionSchema,
|
|
411
252
|
validUntilLedger: z.number().int().positive().max(U32_MAX).optional(),
|
|
412
|
-
oraclePricesByAsset: z.record(z.string(), OraclePriceFixtureSchema).optional(),
|
|
413
253
|
})
|
|
414
254
|
export type SimulatePolicyInput = z.infer<typeof SimulatePolicyInputSchema>
|
|
415
255
|
|
|
416
|
-
export const VerifyPolicyInputSchema =
|
|
417
|
-
// verifyPolicy requires a non-null predicate; the engine refuses
|
|
418
|
-
// `null` outright. The schema mirrors that contract.
|
|
419
|
-
predicate: PredicateNodeSchema,
|
|
420
|
-
permitTx: RecordedTransactionSchema,
|
|
421
|
-
validUntilLedger: z.number().int().positive().max(U32_MAX).optional(),
|
|
422
|
-
oraclePricesByAsset: z.record(z.string(), OraclePriceFixtureSchema).optional(),
|
|
423
|
-
})
|
|
256
|
+
export const VerifyPolicyInputSchema = SimulatePolicyInputSchema
|
|
424
257
|
export type VerifyPolicyInput = z.infer<typeof VerifyPolicyInputSchema>
|
|
425
258
|
|
|
426
259
|
// ===== install_policy / revoke_policy / get_interpreter_info =====
|
|
@@ -435,7 +268,7 @@ export type VerifyPolicyInput = z.infer<typeof VerifyPolicyInputSchema>
|
|
|
435
268
|
// optional `grammar_version()` RPC call to check whether the deployed contract
|
|
436
269
|
// matches the pin. A mismatch is worth MORE than a fabricated audit field.
|
|
437
270
|
//
|
|
438
|
-
// All three input schemas live BELOW the
|
|
271
|
+
// All three input schemas live BELOW the ContextRuleDraftSchema declaration
|
|
439
272
|
// so the const reference there is not in the temporal dead zone (no JS hoisting
|
|
440
273
|
// for `const`).
|
|
441
274
|
|
|
@@ -448,7 +281,7 @@ export type VerifyPolicyInput = z.infer<typeof VerifyPolicyInputSchema>
|
|
|
448
281
|
const MAX_SIGNERS_PER_RULE = 15
|
|
449
282
|
const MAX_POLICIES_PER_RULE = 5
|
|
450
283
|
|
|
451
|
-
const
|
|
284
|
+
const ContextRuleDraftSchema = z
|
|
452
285
|
.object({
|
|
453
286
|
contextRuleType: z.discriminatedUnion('kind', [
|
|
454
287
|
z.object({ kind: z.literal('default') }),
|
|
@@ -471,21 +304,11 @@ const MandateSpecSchemaForRule = z
|
|
|
471
304
|
.max(MAX_SIGNERS_PER_RULE),
|
|
472
305
|
policies: z
|
|
473
306
|
.array(
|
|
474
|
-
z.
|
|
475
|
-
z.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}),
|
|
480
|
-
z.object({
|
|
481
|
-
kind: z.literal('oz_builtin'),
|
|
482
|
-
primitive: z.object({
|
|
483
|
-
primitive: z.enum(['spending_limit', 'simple_threshold', 'weighted_threshold']),
|
|
484
|
-
params: z.record(z.unknown()),
|
|
485
|
-
}),
|
|
486
|
-
instanceAddress: z.string(),
|
|
487
|
-
}),
|
|
488
|
-
])
|
|
307
|
+
z.object({
|
|
308
|
+
kind: z.literal('interpreter'),
|
|
309
|
+
interpreterAddress: z.string(),
|
|
310
|
+
predicateBlobBase64: z.string().min(1),
|
|
311
|
+
})
|
|
489
312
|
)
|
|
490
313
|
.max(MAX_POLICIES_PER_RULE),
|
|
491
314
|
})
|
|
@@ -502,23 +325,28 @@ const MandateSpecSchemaForRule = z
|
|
|
502
325
|
/** Pinned interpreter address (testnet).
|
|
503
326
|
* Single source for the MCP layer; do not embed elsewhere. */
|
|
504
327
|
export const PINNED_INTERPRETER_TESTNET_ADDRESS =
|
|
505
|
-
'
|
|
506
|
-
|
|
507
|
-
/** Pinned interpreter address (mainnet).
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
* ids are network-scoped. UNAUDITED at the time of writing.
|
|
328
|
+
'CCL336TCK2Y5OFNRCMN2M3HVPBCEX4PW5H6EQ5VW5NPMXOCP4ESB5XR4'
|
|
329
|
+
|
|
330
|
+
/** Pinned interpreter address (mainnet), redeployed 2026-08-22 from a reproducible build. The mainnet
|
|
331
|
+
* interpreter IS the binary exercised on testnet - both instances were created
|
|
332
|
+
* from the same uploaded wasm hash (see PINNED_INTERPRETER_WASM_SHA256), and
|
|
333
|
+
* both were read back with `grammar_version()` returning 3. The address differs
|
|
334
|
+
* because instance ids are network-scoped. UNAUDITED at the time of writing.
|
|
335
|
+
*
|
|
336
|
+
* These four constants move together or not at all. The grammar version and
|
|
337
|
+
* wasm hash are single values covering BOTH networks, so re-pinning one network
|
|
338
|
+
* alone would have the builder emit a version the other network refuses - with
|
|
339
|
+
* a green test run, since `grammar-version-parity.test.ts` would then pass. */
|
|
512
340
|
export const PINNED_INTERPRETER_MAINNET_ADDRESS =
|
|
513
|
-
'
|
|
341
|
+
'CBZXLSTQUITBFZHQH6XRXF3XIVRQR4RHRI64Q5WELS5KGY3ZKJPFWDPF'
|
|
514
342
|
|
|
515
343
|
/** Pinned interpreter wasm sha256 (hex). */
|
|
516
344
|
export const PINNED_INTERPRETER_WASM_SHA256 =
|
|
517
|
-
'
|
|
345
|
+
'a2b36e8ac5a61caf3757af26aa79e83f2995b451099f44772383806a55fe3414'
|
|
518
346
|
|
|
519
347
|
/** The grammar version the interpreter enforces (matches SELF_VERSION in
|
|
520
348
|
* contracts/policy-interpreter/src/version.rs). */
|
|
521
|
-
export const PINNED_INTERPRETER_GRAMMAR_VERSION =
|
|
349
|
+
export const PINNED_INTERPRETER_GRAMMAR_VERSION = 3
|
|
522
350
|
|
|
523
351
|
/** Default Soroban RPC for the install / revoke / info tools. The recorder
|
|
524
352
|
* keeps its own copy in record/rpc.ts because it hands back a fetcher rather
|
|
@@ -545,10 +373,6 @@ export const RPC_URL_BY_NETWORK: Record<Network, string> = {
|
|
|
545
373
|
mainnet: MAINNET_RPC_URL,
|
|
546
374
|
}
|
|
547
375
|
|
|
548
|
-
/** Soroban `valid_until` window (ledgers) added to the latest ledger when
|
|
549
|
-
* building the auth entry. ~25 minutes at 5s/ledger. */
|
|
550
|
-
export const DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300
|
|
551
|
-
|
|
552
376
|
/** Stellar network passphrases. Pinned here so the XDR envelope uses the
|
|
553
377
|
* matching passphrase when the wallet signs (a mismatch yields invalid
|
|
554
378
|
* hashes). */
|
|
@@ -587,7 +411,7 @@ export const InstallPolicyInputSchema = z
|
|
|
587
411
|
* pin and RPC pin do not move by themselves). */
|
|
588
412
|
network: NetworkSchema.optional(),
|
|
589
413
|
/** The proposed rule draft. Mirrors the core `ContextRuleDraft` shape. */
|
|
590
|
-
rule:
|
|
414
|
+
rule: ContextRuleDraftSchema,
|
|
591
415
|
/** Per-rule install nonce; 1 for a fresh install. */
|
|
592
416
|
installNonce: z.number().int().positive(),
|
|
593
417
|
/** Optional RPC URL override. Defaults to the pinned RPC for the
|