@crediolabs/policy-synth 0.2.0 → 0.3.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.
- package/README.md +4 -4
- package/dist/adapters/interpreter/adapter.d.ts +19 -25
- package/dist/adapters/interpreter/adapter.js +54 -487
- 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 -19
- package/dist/install/build-add-context-rule.js +3 -98
- package/dist/install/build-install-policy.d.ts +4 -53
- package/dist/install/build-install-policy.js +44 -123
- package/dist/install/index.d.ts +0 -2
- package/dist/install/index.js +0 -7
- 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/record/freshness.d.ts +17 -11
- package/dist/record/freshness.js +28 -18
- 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 +36 -139
- package/dist/run/index.js +125 -437
- package/dist/run/schemas.d.ts +168 -1929
- package/dist/run/schemas.js +40 -269
- package/dist/{synth → simulate}/deny-cases.d.ts +4 -8
- 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/index.d.ts +0 -5
- package/dist/synth/index.js +0 -5
- 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-cjs/adapters/interpreter/adapter.d.ts +19 -25
- package/dist-cjs/adapters/interpreter/adapter.js +56 -489
- 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 -19
- package/dist-cjs/install/build-add-context-rule.js +1 -97
- package/dist-cjs/install/build-install-policy.d.ts +4 -53
- package/dist-cjs/install/build-install-policy.js +44 -122
- package/dist-cjs/install/index.d.ts +0 -2
- package/dist-cjs/install/index.js +2 -23
- 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/record/freshness.d.ts +17 -11
- package/dist-cjs/record/freshness.js +28 -18
- 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 +36 -139
- package/dist-cjs/run/index.js +125 -444
- package/dist-cjs/run/schemas.d.ts +168 -1929
- package/dist-cjs/run/schemas.js +41 -270
- package/dist-cjs/{synth → simulate}/deny-cases.d.ts +4 -8
- 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/index.d.ts +0 -5
- package/dist-cjs/synth/index.js +1 -11
- 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/package.json +5 -5
- 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 +5 -139
- package/src/install/build-install-policy.ts +93 -214
- package/src/install/index.ts +0 -34
- 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/record/freshness.ts +29 -18
- 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 +144 -572
- package/src/run/schemas.ts +42 -291
- 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/adapters/interpreter/index.d.ts +0 -1
- package/dist/adapters/interpreter/index.js +0 -2
- package/dist/adapters/oz/adapter.d.ts +0 -20
- package/dist/adapters/oz/adapter.js +0 -295
- package/dist/adapters/oz/index.d.ts +0 -1
- package/dist/adapters/oz/index.js +0 -2
- package/dist/codegen/compile-gate.d.ts +0 -33
- package/dist/codegen/compile-gate.js +0 -124
- package/dist/codegen/index.d.ts +0 -2
- package/dist/codegen/index.js +0 -8
- package/dist/codegen/template.d.ts +0 -18
- package/dist/codegen/template.js +0 -148
- package/dist/install/authority-overlap.d.ts +0 -134
- package/dist/install/authority-overlap.js +0 -0
- package/dist/install/build-install-predicate.d.ts +0 -96
- package/dist/install/build-install-predicate.js +0 -444
- package/dist/install/build-merge-policy.d.ts +0 -70
- package/dist/install/build-merge-policy.js +0 -130
- package/dist/install/plan-merge-policy.d.ts +0 -49
- package/dist/install/plan-merge-policy.js +0 -86
- package/dist/install/read-account-rules.d.ts +0 -100
- package/dist/install/read-account-rules.js +0 -283
- package/dist/ir/index.d.ts +0 -1
- package/dist/ir/index.js +0 -2
- package/dist/ir/types.d.ts +0 -140
- package/dist/ir/types.js +0 -11
- package/dist/mandate/index.d.ts +0 -2
- package/dist/mandate/index.js +0 -2
- package/dist/mandate/to-ir.d.ts +0 -3
- package/dist/mandate/to-ir.js +0 -60
- package/dist/mandate/types.d.ts +0 -20
- package/dist/mandate/types.js +0 -8
- package/dist/seams/index.d.ts +0 -1
- package/dist/seams/index.js +0 -2
- package/dist/seams/types.d.ts +0 -66
- package/dist/seams/types.js +0 -11
- package/dist/synth/deny-cases.js +0 -562
- package/dist/synth/evaluate.d.ts +0 -39
- package/dist/synth/evaluate.js +0 -551
- package/dist/synth/harness.d.ts +0 -28
- package/dist/synth/harness.js +0 -47
- package/dist/synth/minimize.d.ts +0 -4
- package/dist/synth/minimize.js +0 -38
- package/dist/synth/permit-context.d.ts +0 -15
- package/dist/synth/permit-context.js +0 -116
- package/dist/synth/predicate-literals.d.ts +0 -5
- package/dist/synth/predicate-literals.js +0 -25
- package/dist/synth/synthesize-from-mandate.d.ts +0 -20
- package/dist/synth/synthesize-from-mandate.js +0 -59
- package/dist/verify/envelope.d.ts +0 -15
- package/dist/verify/envelope.js +0 -22
- package/dist/verify/index.d.ts +0 -3
- package/dist/verify/index.js +0 -3
- package/dist/verify/simulate.d.ts +0 -31
- package/dist/verify/simulate.js +0 -258
- package/dist/verify/verify.d.ts +0 -21
- package/dist/verify/verify.js +0 -189
- package/dist-cjs/adapters/interpreter/index.d.ts +0 -1
- package/dist-cjs/adapters/interpreter/index.js +0 -8
- package/dist-cjs/adapters/oz/adapter.d.ts +0 -20
- package/dist-cjs/adapters/oz/adapter.js +0 -300
- package/dist-cjs/adapters/oz/index.d.ts +0 -1
- package/dist-cjs/adapters/oz/index.js +0 -8
- package/dist-cjs/codegen/compile-gate.d.ts +0 -33
- package/dist-cjs/codegen/compile-gate.js +0 -128
- package/dist-cjs/codegen/index.d.ts +0 -2
- package/dist-cjs/codegen/index.js +0 -14
- package/dist-cjs/codegen/template.d.ts +0 -18
- package/dist-cjs/codegen/template.js +0 -151
- package/dist-cjs/install/authority-overlap.d.ts +0 -134
- package/dist-cjs/install/authority-overlap.js +0 -0
- package/dist-cjs/install/build-install-predicate.d.ts +0 -96
- package/dist-cjs/install/build-install-predicate.js +0 -479
- package/dist-cjs/install/build-merge-policy.d.ts +0 -70
- package/dist-cjs/install/build-merge-policy.js +0 -134
- package/dist-cjs/install/plan-merge-policy.d.ts +0 -49
- package/dist-cjs/install/plan-merge-policy.js +0 -90
- package/dist-cjs/install/read-account-rules.d.ts +0 -100
- package/dist-cjs/install/read-account-rules.js +0 -296
- package/dist-cjs/ir/index.d.ts +0 -1
- package/dist-cjs/ir/index.js +0 -3
- package/dist-cjs/ir/types.d.ts +0 -140
- package/dist-cjs/ir/types.js +0 -12
- package/dist-cjs/mandate/index.d.ts +0 -2
- package/dist-cjs/mandate/index.js +0 -6
- package/dist-cjs/mandate/to-ir.d.ts +0 -3
- package/dist-cjs/mandate/to-ir.js +0 -63
- package/dist-cjs/mandate/types.d.ts +0 -20
- package/dist-cjs/mandate/types.js +0 -9
- package/dist-cjs/seams/index.d.ts +0 -1
- package/dist-cjs/seams/index.js +0 -3
- package/dist-cjs/seams/types.d.ts +0 -66
- package/dist-cjs/seams/types.js +0 -12
- package/dist-cjs/synth/deny-cases.js +0 -568
- package/dist-cjs/synth/evaluate.d.ts +0 -39
- package/dist-cjs/synth/evaluate.js +0 -554
- package/dist-cjs/synth/harness.d.ts +0 -28
- package/dist-cjs/synth/harness.js +0 -50
- package/dist-cjs/synth/minimize.d.ts +0 -4
- package/dist-cjs/synth/minimize.js +0 -41
- package/dist-cjs/synth/permit-context.d.ts +0 -15
- package/dist-cjs/synth/permit-context.js +0 -119
- package/dist-cjs/synth/predicate-literals.d.ts +0 -5
- package/dist-cjs/synth/predicate-literals.js +0 -28
- package/dist-cjs/synth/synthesize-from-mandate.d.ts +0 -20
- package/dist-cjs/synth/synthesize-from-mandate.js +0 -62
- package/dist-cjs/verify/envelope.d.ts +0 -15
- package/dist-cjs/verify/envelope.js +0 -23
- package/dist-cjs/verify/index.d.ts +0 -3
- package/dist-cjs/verify/index.js +0 -8
- package/dist-cjs/verify/simulate.d.ts +0 -31
- package/dist-cjs/verify/simulate.js +0 -261
- package/dist-cjs/verify/verify.d.ts +0 -21
- package/dist-cjs/verify/verify.js +0 -192
- 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/install/authority-overlap.ts +0 -0
- package/src/install/build-merge-policy.ts +0 -219
- package/src/install/plan-merge-policy.ts +0 -133
- package/src/install/read-account-rules.ts +0 -376
- 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
|
@@ -1,202 +1,105 @@
|
|
|
1
|
-
// src/synth/compose-from-recording.ts - facts + scope ->
|
|
1
|
+
// src/synth/compose-from-recording.ts - facts + scope -> a composed rule.
|
|
2
2
|
//
|
|
3
3
|
// Fail-closed composition rules:
|
|
4
4
|
// - unknown top-level protocol (registry.identifyProtocol returns null) ->
|
|
5
|
-
// emit no
|
|
6
|
-
//
|
|
7
|
-
// call never compiles to a permissive
|
|
8
|
-
// -
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// the
|
|
13
|
-
// (userResponses.windowSeconds). A single recorded spend does NOT authorise
|
|
14
|
-
// that amount every window, so the observed amount is NEVER used as an
|
|
15
|
-
// auto-ceiling: missing limit -> AMOUNT_BOUND_MISSING (observed amount
|
|
16
|
-
// offered as a suggestion); missing window -> DURATION_UNSPECIFIED. EVERY
|
|
5
|
+
// emit no constraint from the spend; scope is kept (contract + method) and
|
|
6
|
+
// every inferred bound surfaces as a descriptive warning. An unrecognised
|
|
7
|
+
// call never compiles to a permissive policy.
|
|
8
|
+
// - a spend cap is emitted ONLY when the caller supplies `limitAmount` AND
|
|
9
|
+
// the call carries an amount argument to bind it to. A single recorded
|
|
10
|
+
// spend does NOT authorise that amount on every call, so the observed
|
|
11
|
+
// amount is NEVER used as an auto-ceiling: without both,
|
|
12
|
+
// AMOUNT_BOUND_MISSING carries the observed amount as a suggestion. EVERY
|
|
17
13
|
// spent token is handled - none is silently dropped.
|
|
18
|
-
// -
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// - the IR carries ONLY constraints justified by the recording + explicit
|
|
22
|
-
// user input. Nothing invented: no oracle price fabricated from a slippage
|
|
23
|
-
// bound, no synthetic exact-path compare. Those needs surface as warnings.
|
|
24
|
-
//
|
|
25
|
-
// Split rule (P3 wiring): `ComposeResult` carries BOTH `ir` (OZ-shape) and
|
|
26
|
-
// `interpreterIr` (predicate-shape). Each constraint is routed to EXACTLY ONE
|
|
27
|
-
// adapter:
|
|
28
|
-
// - `window_spent(token, w) <= limit` where `token === scope.contract` and
|
|
29
|
-
// protocol is known -> `ir` (OZ lowers to spending_limit).
|
|
30
|
-
// - everything else (recipient allowlists, per-method scope.method,
|
|
31
|
-
// invocation_count, eq_seq swap paths, oracle_price, AND window_spent
|
|
32
|
-
// where token != scope.contract i.e. SoroSwap input-token cap) ->
|
|
33
|
-
// `interpreterIr`.
|
|
34
|
-
//
|
|
35
|
-
// This prevents the interpreter adapter from emitting a duplicate
|
|
36
|
-
// `window_spent` predicate leaf alongside an OZ `spending_limit` primitive
|
|
37
|
-
// covering the same spend semantic - the two adapters never overlap.
|
|
14
|
+
// - the IR carries ONLY constraints justified by the recording plus an
|
|
15
|
+
// explicit bound, no synthetic exact-path compare. Other needs surface as
|
|
16
|
+
// warnings.
|
|
38
17
|
//
|
|
39
18
|
// Default policy is `deny_all` (OZ context rules are deny-by-default).
|
|
40
19
|
import { identifyProtocol } from "../registry/identify.js";
|
|
41
|
-
|
|
20
|
+
import { getAbi } from "../registry/protocols.js";
|
|
21
|
+
/** Compose a rule from the lowered facts + the resolved scope.
|
|
42
22
|
* Pure (no randomness, no clock); same inputs -> byte-identical result. */
|
|
43
23
|
export function composeFromRecording(facts, scopeContract, topLevel, opts) {
|
|
44
|
-
const interpreterEnabled = opts.interpreterEnabled === true;
|
|
45
24
|
const ambiguities = [];
|
|
46
25
|
const warnings = [];
|
|
47
|
-
const ozConstraints = [];
|
|
48
26
|
const interpreterConstraints = [];
|
|
49
|
-
// Route to the matching IR. When the interpreter is enabled, constraints the
|
|
50
|
-
// OZ adapter cannot lower go there; otherwise they go to OZ (which flags them
|
|
51
|
-
// as uncovered) so today's warning-driven behaviour is preserved.
|
|
52
|
-
const routeToAdapter = (cond) => {
|
|
53
|
-
if (interpreterEnabled)
|
|
54
|
-
interpreterConstraints.push(cond);
|
|
55
|
-
else
|
|
56
|
-
ozConstraints.push(cond);
|
|
57
|
-
};
|
|
58
27
|
const protocol = topLevel
|
|
59
28
|
? identifyProtocol(topLevel.contract, topLevel.fn, topLevel.args, opts.network)
|
|
60
29
|
: null;
|
|
61
30
|
const known = protocol !== null;
|
|
62
|
-
const windowSeconds = opts.userResponses?.windowSeconds;
|
|
63
31
|
const limitAmount = opts.userResponses?.limitAmount;
|
|
64
32
|
const spendTokens = Object.keys(facts.spendByToken);
|
|
65
|
-
// Outgoing spend ->
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// Routing: a `window_spent(token, w) <= limit` constraint goes to the OZ IR
|
|
71
|
-
// only when token === scope.contract (OZ's spending_limit binds the
|
|
72
|
-
// CallContract target, not a token parameter). Otherwise it goes to the
|
|
73
|
-
// interpreter IR.
|
|
33
|
+
// Outgoing spend -> a cap on the call's own amount ARGUMENT. The interpreter
|
|
34
|
+
// is passed one authorised call, not the transaction's token movements, so a
|
|
35
|
+
// rolling per-window total is not something it can read; the enforceable
|
|
36
|
+
// shape is a bound on the amount the call itself carries. A multi-token flow
|
|
37
|
+
// has no single argument to bind, so each token surfaces AMOUNT_BOUND_MISSING.
|
|
74
38
|
if (spendTokens.length > 0 && topLevel) {
|
|
75
|
-
let durationFlagged = false;
|
|
76
39
|
for (const token of spendTokens) {
|
|
77
40
|
const observed = facts.spendByToken[token];
|
|
78
41
|
if (observed === undefined)
|
|
79
42
|
continue;
|
|
80
43
|
if (!known) {
|
|
81
|
-
warnings.push(`spend of ${observed} (token ${token}) not bounded: unrecognised protocol,
|
|
44
|
+
warnings.push(`spend of ${observed} (token ${token}) not bounded: unrecognised protocol, so the amount argument cannot be located`);
|
|
82
45
|
continue;
|
|
83
46
|
}
|
|
84
47
|
const limit = spendTokens.length === 1 ? limitAmount : undefined;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
code: 'DURATION_UNSPECIFIED',
|
|
88
|
-
question: `Recording shows a ${observed}-${token} spend. What rolling window (seconds) should the spending_limit use?`,
|
|
89
|
-
});
|
|
90
|
-
durationFlagged = true;
|
|
91
|
-
}
|
|
92
|
-
if (limit === undefined) {
|
|
48
|
+
const amountArgIndex = limitArgumentIndex(protocol, topLevel);
|
|
49
|
+
if (limit === undefined || amountArgIndex === null) {
|
|
93
50
|
ambiguities.push({
|
|
94
51
|
code: 'AMOUNT_BOUND_MISSING',
|
|
95
|
-
question: `Recording shows a ${observed}-${token} spend, but a single spend does not authorise that amount every
|
|
52
|
+
question: `Recording shows a ${observed}-${token} spend, but a single spend does not authorise that amount on every call. What per-call cap should apply? (observed amount, suggestion only: ${observed})`,
|
|
96
53
|
});
|
|
97
54
|
continue;
|
|
98
55
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// accumulate and the counter would never move. OZ reports the case it
|
|
105
|
-
// cannot cover (limit pins to the context contract) - the honest
|
|
106
|
-
// outcome; the old fallback produced an interpreter predicate that
|
|
107
|
-
// silently never bound.
|
|
108
|
-
ozConstraints.push({
|
|
109
|
-
op: 'compare',
|
|
110
|
-
compare: {
|
|
111
|
-
selector: { kind: 'window_spent', token, windowSeconds },
|
|
112
|
-
operator: 'lte',
|
|
113
|
-
value: limit,
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
if (interpreterEnabled && token !== scopeContract) {
|
|
117
|
-
warnings.push(`rolling spend cap on ${token} cannot be enforced on chain: OZ spending_limit pins the limit to the context contract, and the interpreter cannot observe token movements. Bound the per-call value with an argument cap plus an invocation-count limit instead.`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
56
|
+
interpreterConstraints.push({
|
|
57
|
+
op: 'lte',
|
|
58
|
+
left: { kind: 'call_arg', index: amountArgIndex },
|
|
59
|
+
right: { kind: 'literal_i128', value: limit },
|
|
60
|
+
});
|
|
120
61
|
}
|
|
121
62
|
}
|
|
122
|
-
// Incoming-only / frequency intent:
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
// invocation_count); otherwise to the OZ IR (which flags it as uncovered).
|
|
63
|
+
// Incoming-only / frequency intent: the interpreter is passed one authorised
|
|
64
|
+
// call, so it cannot count prior calls - no frequency bound is emitted and
|
|
65
|
+
// never a fabricated `<= 1`. The need surfaces as a prompt plus a warning.
|
|
126
66
|
//
|
|
127
67
|
// A recognised swap is NOT an incoming-only flow: it has an outgoing input
|
|
128
68
|
// leg whose spend simply was not attributed to the source account
|
|
129
69
|
// (fee-sponsored / holder != source). Its real restrictions - exact path,
|
|
130
70
|
// recipient, input-amount cap - come from the protocol-specific pass, so it
|
|
131
|
-
// does NOT get the incoming-only frequency prompt.
|
|
132
|
-
// rate-limit the swap can still supply an invocationLimit + window, which
|
|
133
|
-
// lowers to an invocation_count for any flow.
|
|
71
|
+
// does NOT get the incoming-only frequency prompt.
|
|
134
72
|
if (spendTokens.length === 0 && topLevel) {
|
|
135
|
-
const invocationLimit = opts.userResponses?.invocationLimit;
|
|
136
73
|
const isRecognisedSwap = protocol?.protocol === 'soroswap';
|
|
137
|
-
if (
|
|
138
|
-
routeToAdapter({
|
|
139
|
-
op: 'compare',
|
|
140
|
-
compare: {
|
|
141
|
-
selector: { kind: 'invocation_count', windowSeconds },
|
|
142
|
-
// `lt`, not `lte`: the leaf reports the calls ALREADY made in the
|
|
143
|
-
// window, so `< N` is what permits N of them. With `lte` a limit
|
|
144
|
-
// of N let an N+1th call through.
|
|
145
|
-
operator: 'lt',
|
|
146
|
-
value: String(invocationLimit),
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
else if (!isRecognisedSwap) {
|
|
74
|
+
if (!isRecognisedSwap) {
|
|
151
75
|
ambiguities.push({
|
|
152
76
|
code: 'FREQUENCY_BOUND_MISSING',
|
|
153
|
-
question: 'Incoming-only flow -
|
|
77
|
+
question: 'Incoming-only flow - the policy does not bound how often this call may be made. Bound it outside the policy, or scope the rule more tightly.',
|
|
154
78
|
});
|
|
155
|
-
warnings.push('frequency
|
|
79
|
+
warnings.push('incoming-only flow: call frequency is NOT bounded - the interpreter reads only the authorized call, so it cannot count prior calls');
|
|
156
80
|
}
|
|
157
81
|
}
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
if (oracleBounds) {
|
|
163
|
-
for (const b of oracleBounds) {
|
|
164
|
-
routeToAdapter({
|
|
165
|
-
op: 'compare',
|
|
166
|
-
compare: {
|
|
167
|
-
selector: { kind: 'oracle_price', asset: b.asset },
|
|
168
|
-
operator: b.operator,
|
|
169
|
-
value: b.value,
|
|
170
|
-
valueDecimals: b.decimals,
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
// Observed recipient allowlist (SEP-41) is a real, recorded constraint the OZ
|
|
176
|
-
// adapter flags as not covered; the interpreter adapter lowers it to an `in`
|
|
177
|
-
// predicate. Unknown protocols emit nothing here. SoroSwap's swap recipient
|
|
178
|
-
// (arg[3]) is the source-of-truth for the swapRecipientAllowlist surface.
|
|
82
|
+
// Observed recipient allowlist (SEP-41) is a real, recorded constraint the
|
|
83
|
+
// interpreter adapter lowers to an `in` predicate. Unknown protocols emit
|
|
84
|
+
// nothing here. SoroSwap's swap recipient (arg[3]) is the source-of-truth for
|
|
85
|
+
// the swapRecipientAllowlist surface.
|
|
179
86
|
if (topLevel && protocol !== null) {
|
|
180
|
-
// A SoroSwap input-amount cap binds the caller's limitAmount to
|
|
181
|
-
//
|
|
182
|
-
//
|
|
87
|
+
// A SoroSwap input-amount cap binds the caller's limitAmount to the swap's
|
|
88
|
+
// input-amount argument ONLY when no outgoing spend was detected for the
|
|
89
|
+
// source account - i.e. the input token never moved FROM the source
|
|
183
90
|
// (fee-sponsored swaps, or a holder != source). When a spend WAS detected,
|
|
184
|
-
// the
|
|
185
|
-
//
|
|
91
|
+
// the cap above already consumed the limit, so this one is skipped to avoid
|
|
92
|
+
// binding one limit to two different args.
|
|
186
93
|
const swapInputAmountCap = spendTokens.length === 0 ? limitAmount : undefined;
|
|
187
|
-
appendProtocolSpecificConstraints(interpreterConstraints,
|
|
94
|
+
appendProtocolSpecificConstraints(interpreterConstraints, warnings, ambiguities, facts, topLevel, protocol, opts.userResponses?.swapRecipientAllowlist, swapInputAmountCap);
|
|
188
95
|
}
|
|
189
|
-
// `scope.method`
|
|
190
|
-
//
|
|
191
|
-
// `call_fn == <method>` predicate leaf (a real restriction); the OZ adapter
|
|
192
|
-
// flags it as uncovered (CallContract alone permits any method on the
|
|
193
|
-
// contract).
|
|
96
|
+
// The interpreter adapter lowers `scope.method` into a `call_fn == <method>`
|
|
97
|
+
// predicate leaf.
|
|
194
98
|
const scope = { contract: scopeContract };
|
|
195
99
|
if (topLevel?.fn)
|
|
196
100
|
scope.method = topLevel.fn;
|
|
197
101
|
const buildRule = (constraints) => {
|
|
198
102
|
const rule = {
|
|
199
|
-
roles: [],
|
|
200
103
|
scope: { ...scope },
|
|
201
104
|
constraints,
|
|
202
105
|
};
|
|
@@ -205,39 +108,47 @@ export function composeFromRecording(facts, scopeContract, topLevel, opts) {
|
|
|
205
108
|
}
|
|
206
109
|
return rule;
|
|
207
110
|
};
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
111
|
+
return { interpreterRule: buildRule(interpreterConstraints), ambiguities, warnings };
|
|
112
|
+
}
|
|
113
|
+
/** Index of the argument a caller-supplied `limitAmount` binds to, or null when
|
|
114
|
+
* the call carries no such argument. The interpreter is passed one authorised
|
|
115
|
+
* call, not the transaction's token movements, so bounding an argument the
|
|
116
|
+
* call itself carries is the only enforceable shape. */
|
|
117
|
+
function limitArgumentIndex(protocol, topLevel) {
|
|
118
|
+
if (!protocol || !topLevel)
|
|
119
|
+
return null;
|
|
120
|
+
const named = amountArgumentIndex(protocol);
|
|
121
|
+
if (named !== null)
|
|
122
|
+
return named;
|
|
123
|
+
// SoroSwap names its input argument `amount_in` / `amount_in_max`, so the
|
|
124
|
+
// lookup by name misses it and the index is function-specific.
|
|
125
|
+
if (protocol.protocol !== 'soroswap')
|
|
126
|
+
return null;
|
|
127
|
+
const i = soroswapInputAmountArgIndex(protocol.fn);
|
|
128
|
+
// Defense in depth: identification's argsMatchAbi already pins it to i128.
|
|
129
|
+
return i !== undefined && topLevel.args[i]?.type === 'i128' ? i : null;
|
|
219
130
|
}
|
|
220
|
-
/**
|
|
221
|
-
*
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
131
|
+
/** Index of the argument the protocol ABI calls `amount`, or null when the ABI
|
|
132
|
+
* does not name one. */
|
|
133
|
+
function amountArgumentIndex(protocol) {
|
|
134
|
+
const abi = getAbi(protocol.protocol)[protocol.fn];
|
|
135
|
+
if (!abi)
|
|
136
|
+
return null;
|
|
137
|
+
const i = abi.args.findIndex((a) => a.name === 'amount');
|
|
138
|
+
return i >= 0 ? i : null;
|
|
139
|
+
}
|
|
140
|
+
/** Add the protocol-specific constraints the recording justifies: recipient
|
|
141
|
+
* allowlists, exact swap paths and per-call argument caps. */
|
|
142
|
+
function appendProtocolSpecificConstraints(interpreterConstraints, warnings, ambiguities, facts, topLevel, protocol, swapRecipientAllowlist, swapInputAmountCap) {
|
|
230
143
|
// SEP-41 transfer / mint: the `to` arg (index 1) is the recipient. Emit a
|
|
231
144
|
// single-element allowlist; the interpreter adapter lowers it to `in`.
|
|
232
|
-
// When interpreter is not enabled, route to OZ so the caller sees today's
|
|
233
|
-
// `value allowlist on arg 1` warning.
|
|
234
145
|
if (protocol.protocol === 'sep41' && (protocol.fn === 'transfer' || protocol.fn === 'mint')) {
|
|
235
146
|
const toArg = topLevel.args[1];
|
|
236
147
|
if (toArg && toArg.type === 'address') {
|
|
237
|
-
|
|
148
|
+
interpreterConstraints.push({
|
|
238
149
|
op: 'in',
|
|
239
|
-
|
|
240
|
-
|
|
150
|
+
needle: { kind: 'call_arg', index: 1 },
|
|
151
|
+
haystack: [{ kind: 'literal_address', value: toArg.value }],
|
|
241
152
|
});
|
|
242
153
|
}
|
|
243
154
|
}
|
|
@@ -252,10 +163,10 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
252
163
|
// claim(from, reserve_token_ids, to).
|
|
253
164
|
const toArg = topLevel.args[2];
|
|
254
165
|
if (toArg && toArg.type === 'address') {
|
|
255
|
-
|
|
166
|
+
interpreterConstraints.push({
|
|
256
167
|
op: 'in',
|
|
257
|
-
|
|
258
|
-
|
|
168
|
+
needle: { kind: 'call_arg', index: 2 },
|
|
169
|
+
haystack: [{ kind: 'literal_address', value: toArg.value }],
|
|
259
170
|
});
|
|
260
171
|
}
|
|
261
172
|
}
|
|
@@ -269,17 +180,14 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
269
180
|
// -> Borrow on a different asset, any amount, then auction fills). Length
|
|
270
181
|
// + per-element pinning is total; a quantifier over elements is not. If we
|
|
271
182
|
// cannot emit BOTH, we surface AMBIGUITY rather than emit a partial bind.
|
|
272
|
-
if (protocol.protocol === 'blend' && protocol.fn === 'submit'
|
|
183
|
+
if (protocol.protocol === 'blend' && protocol.fn === 'submit') {
|
|
273
184
|
const requestsArg = topLevel.args[3];
|
|
274
185
|
if (requestsArg && requestsArg.type === 'vec') {
|
|
275
186
|
const elements = requestsArg.value;
|
|
276
187
|
interpreterConstraints.push({
|
|
277
|
-
op: '
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
operator: 'eq',
|
|
281
|
-
value: String(elements.length),
|
|
282
|
-
},
|
|
188
|
+
op: 'eq',
|
|
189
|
+
left: { kind: 'call_arg_len', index: 3 },
|
|
190
|
+
right: { kind: 'literal_u32', value: elements.length },
|
|
283
191
|
});
|
|
284
192
|
for (let i = 0; i < elements.length; i++) {
|
|
285
193
|
const element = elements[i];
|
|
@@ -320,46 +228,19 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
320
228
|
continue;
|
|
321
229
|
}
|
|
322
230
|
interpreterConstraints.push({
|
|
323
|
-
op: '
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
kind: 'arg_field',
|
|
327
|
-
argIndex: 3,
|
|
328
|
-
element: i,
|
|
329
|
-
field: 'request_type',
|
|
330
|
-
scalarType: 'u32',
|
|
331
|
-
},
|
|
332
|
-
operator: 'eq',
|
|
333
|
-
value: requestType,
|
|
334
|
-
},
|
|
231
|
+
op: 'eq',
|
|
232
|
+
left: { kind: 'call_arg_field', index: 3, element: i, field: 'request_type' },
|
|
233
|
+
right: { kind: 'literal_u32', value: Number.parseInt(requestType, 10) },
|
|
335
234
|
});
|
|
336
235
|
interpreterConstraints.push({
|
|
337
|
-
op: '
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
kind: 'arg_field',
|
|
341
|
-
argIndex: 3,
|
|
342
|
-
element: i,
|
|
343
|
-
field: 'address',
|
|
344
|
-
scalarType: 'address',
|
|
345
|
-
},
|
|
346
|
-
operator: 'eq',
|
|
347
|
-
value: address,
|
|
348
|
-
},
|
|
236
|
+
op: 'eq',
|
|
237
|
+
left: { kind: 'call_arg_field', index: 3, element: i, field: 'address' },
|
|
238
|
+
right: { kind: 'literal_address', value: address },
|
|
349
239
|
});
|
|
350
240
|
interpreterConstraints.push({
|
|
351
|
-
op: '
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
kind: 'arg_field',
|
|
355
|
-
argIndex: 3,
|
|
356
|
-
element: i,
|
|
357
|
-
field: 'amount',
|
|
358
|
-
scalarType: 'i128',
|
|
359
|
-
},
|
|
360
|
-
operator: 'lte',
|
|
361
|
-
value: amount,
|
|
362
|
-
},
|
|
241
|
+
op: 'lte',
|
|
242
|
+
left: { kind: 'call_arg_field', index: 3, element: i, field: 'amount' },
|
|
243
|
+
right: { kind: 'literal_i128', value: amount },
|
|
363
244
|
});
|
|
364
245
|
}
|
|
365
246
|
}
|
|
@@ -373,17 +254,20 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
373
254
|
if (protocol.protocol === 'soroswap') {
|
|
374
255
|
const paths = facts.allowedPaths?.[topLevel.contract];
|
|
375
256
|
const route = paths?.length === 1 ? paths[0] : undefined;
|
|
376
|
-
if (route && route.length > 0
|
|
257
|
+
if (route && route.length > 0) {
|
|
377
258
|
const pathArgIndex = 2; // SoroSwap swap_exact_tokens_for_tokens: args = [amount_in, amount_out_min, path, to, deadline]
|
|
378
259
|
interpreterConstraints.push({
|
|
379
|
-
op: '
|
|
380
|
-
|
|
381
|
-
|
|
260
|
+
op: 'eq',
|
|
261
|
+
left: { kind: 'call_arg', index: pathArgIndex },
|
|
262
|
+
right: {
|
|
263
|
+
kind: 'literal_vec',
|
|
264
|
+
elements: route.map((v) => ({ kind: 'literal_address', value: v })),
|
|
265
|
+
},
|
|
382
266
|
});
|
|
383
267
|
}
|
|
384
268
|
else {
|
|
385
269
|
const pathText = route && route.length > 0 ? `; observed path: ${route.join(' -> ')}` : '';
|
|
386
|
-
warnings.push(`SoroSwap swap:
|
|
270
|
+
warnings.push(`SoroSwap swap: the exact hop path needs the interpreter predicate${pathText}`);
|
|
387
271
|
}
|
|
388
272
|
// Input-amount cap: bind the caller's limitAmount to the swap's input-amount
|
|
389
273
|
// argument as `call_arg[i] <= limit`. The index is function-specific -
|
|
@@ -393,43 +277,20 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
393
277
|
// as arg[1] (its arg[0] is the exact OUTPUT, so binding arg[0] there would
|
|
394
278
|
// cap the wrong value and leave the input unbounded). This is a per-call cap
|
|
395
279
|
// that does NOT depend on attributing a token movement to the source account
|
|
396
|
-
// (the fee-sponsored / holder != source case, where
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
// it uncovered (a warning). The `type === 'i128'` check is defense in depth -
|
|
401
|
-
// protocol identification's argsMatchAbi already pins the input arg to i128.
|
|
280
|
+
// (the fee-sponsored / holder != source case, where no spend is detected).
|
|
281
|
+
// Fail-closed: it only ever restricts the permitted input. The
|
|
282
|
+
// `type === 'i128'` check is defense in depth - protocol identification's
|
|
283
|
+
// argsMatchAbi already pins the input arg to i128.
|
|
402
284
|
const inputArgIndex = soroswapInputAmountArgIndex(protocol.fn);
|
|
403
285
|
const inputAmountArg = inputArgIndex !== undefined ? topLevel.args[inputArgIndex] : undefined;
|
|
404
286
|
if (swapInputAmountCap !== undefined &&
|
|
405
287
|
inputArgIndex !== undefined &&
|
|
406
288
|
inputAmountArg &&
|
|
407
289
|
inputAmountArg.type === 'i128') {
|
|
408
|
-
|
|
409
|
-
op: '
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
operator: 'lte',
|
|
413
|
-
value: swapInputAmountCap,
|
|
414
|
-
},
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
// Slippage floor: `out >= in * num/den`. Only when the caller supplied the
|
|
418
|
-
// ratio - see `swapMinOutRatio`. Without it the output arg stays free,
|
|
419
|
-
// which is the case the unbounded-swap warning above describes.
|
|
420
|
-
const outMinArgIndex = soroswapMinOutArgIndex(protocol.fn);
|
|
421
|
-
const minOutRatio = swapMinOutRatio;
|
|
422
|
-
if (minOutRatio !== undefined &&
|
|
423
|
-
inputArgIndex !== undefined &&
|
|
424
|
-
outMinArgIndex !== undefined &&
|
|
425
|
-
inputAmountArg &&
|
|
426
|
-
inputAmountArg.type === 'i128') {
|
|
427
|
-
routeToAdapter({
|
|
428
|
-
op: 'slippage_floor',
|
|
429
|
-
outArgIndex: outMinArgIndex,
|
|
430
|
-
inArgIndex: inputArgIndex,
|
|
431
|
-
num: minOutRatio.num,
|
|
432
|
-
den: minOutRatio.den,
|
|
290
|
+
interpreterConstraints.push({
|
|
291
|
+
op: 'lte',
|
|
292
|
+
left: { kind: 'call_arg', index: inputArgIndex },
|
|
293
|
+
right: { kind: 'literal_i128', value: swapInputAmountCap },
|
|
433
294
|
});
|
|
434
295
|
}
|
|
435
296
|
// Swap recipient (call_arg[3]): when the caller supplies
|
|
@@ -440,23 +301,20 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
440
301
|
// it unconstrained would permit an arbitrary recipient (an evil twin with
|
|
441
302
|
// call_arg[3] = attacker_wallet). RECIPIENT_ALLOWLIST_EMPTY is still
|
|
442
303
|
// surfaced, but as INFORMATIONAL (the recipient was pinned; here is how to
|
|
443
|
-
// widen it), never as a silent free pass.
|
|
444
|
-
// via the interpreter predicate, so the pin (and the ambiguity) apply only
|
|
445
|
-
// when the interpreter is enabled - otherwise the swap recipient is ignored
|
|
446
|
-
// (today's behaviour, matching the other SoroSwap constraints).
|
|
304
|
+
// widen it), never as a silent free pass.
|
|
447
305
|
const recipientArg = topLevel.args[3];
|
|
448
306
|
if (swapRecipientAllowlist && swapRecipientAllowlist.length > 0) {
|
|
449
|
-
|
|
307
|
+
interpreterConstraints.push({
|
|
450
308
|
op: 'in',
|
|
451
|
-
|
|
452
|
-
|
|
309
|
+
needle: { kind: 'call_arg', index: 3 },
|
|
310
|
+
haystack: swapRecipientAllowlist.map((v) => ({ kind: 'literal_address', value: v })),
|
|
453
311
|
});
|
|
454
312
|
}
|
|
455
|
-
else if (
|
|
313
|
+
else if (recipientArg && recipientArg.type === 'address') {
|
|
456
314
|
interpreterConstraints.push({
|
|
457
315
|
op: 'in',
|
|
458
|
-
|
|
459
|
-
|
|
316
|
+
needle: { kind: 'call_arg', index: 3 },
|
|
317
|
+
haystack: [{ kind: 'literal_address', value: recipientArg.value }],
|
|
460
318
|
});
|
|
461
319
|
ambiguities.push({
|
|
462
320
|
code: 'RECIPIENT_ALLOWLIST_EMPTY',
|
|
@@ -465,26 +323,6 @@ function appendProtocolSpecificConstraints(interpreterConstraints, routeToAdapte
|
|
|
465
323
|
}
|
|
466
324
|
}
|
|
467
325
|
}
|
|
468
|
-
/** Positional index of the input-amount argument for a recognized SoroSwap swap
|
|
469
|
-
* function. `swap_exact_tokens_for_tokens` and `swap_exact_in_for_tokens` take
|
|
470
|
-
* the exact input as arg[0]; `swap_tokens_for_exact_tokens` takes the maximum
|
|
471
|
-
* input (`amount_in_max`) as arg[1] - its arg[0] is the exact OUTPUT. Any other
|
|
472
|
-
* function has no positional input-amount argument -> undefined (no cap bound). */
|
|
473
|
-
/** The argument carrying the swap's MINIMUM ACCEPTABLE OUTPUT.
|
|
474
|
-
*
|
|
475
|
-
* Only the exact-input entrypoints have one: `swap_tokens_for_exact_tokens`
|
|
476
|
-
* fixes the output and varies the input, so its output needs no floor (its
|
|
477
|
-
* arg[1] is `amount_in_max`, already bounded by the input cap). Returning
|
|
478
|
-
* undefined there keeps a floor from being pinned to the wrong argument. */
|
|
479
|
-
function soroswapMinOutArgIndex(fn) {
|
|
480
|
-
switch (fn) {
|
|
481
|
-
case 'swap_exact_tokens_for_tokens':
|
|
482
|
-
case 'swap_exact_in_for_tokens':
|
|
483
|
-
return 1;
|
|
484
|
-
default:
|
|
485
|
-
return undefined;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
326
|
function soroswapInputAmountArgIndex(fn) {
|
|
489
327
|
switch (fn) {
|
|
490
328
|
case 'swap_exact_tokens_for_tokens':
|
package/dist/synth/index.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
export { isStellarAddress } from './address.ts';
|
|
2
2
|
export { type ComposeOptions, type ComposeResult, composeFromRecording, } from './compose-from-recording.ts';
|
|
3
|
-
export { type DenyCase, type GeneratedCases, generateCases, } from './deny-cases.ts';
|
|
4
|
-
export { type EvalContext, type EvalResult, evaluate } from './evaluate.ts';
|
|
5
|
-
export { type HarnessFailure, type HarnessResult, runHarness, } from './harness.ts';
|
|
6
3
|
export { type IntentFacts, lower } from './lower.ts';
|
|
7
|
-
export { minimize } from './minimize.ts';
|
|
8
4
|
export { type DecideScopeOptions, decideScope, type ScopeDecision, scopeToContextRuleType, } from './scope.ts';
|
|
9
|
-
export { synthesizeFromMandate } from './synthesize-from-mandate.ts';
|
|
10
5
|
export { type __TestInterpreterAdapterOptions, type SynthesizeFromRecordingOptions, synthesizeFromRecording, } from './synthesize-from-recording.ts';
|
package/dist/synth/index.js
CHANGED
|
@@ -4,11 +4,6 @@
|
|
|
4
4
|
// and re-deriving it elsewhere means a second address check that can drift.
|
|
5
5
|
export { isStellarAddress } from "./address.js";
|
|
6
6
|
export { composeFromRecording, } from "./compose-from-recording.js";
|
|
7
|
-
export { generateCases, } from "./deny-cases.js";
|
|
8
|
-
export { evaluate } from "./evaluate.js";
|
|
9
|
-
export { runHarness, } from "./harness.js";
|
|
10
7
|
export { lower } from "./lower.js";
|
|
11
|
-
export { minimize } from "./minimize.js";
|
|
12
8
|
export { decideScope, scopeToContextRuleType, } from "./scope.js";
|
|
13
|
-
export { synthesizeFromMandate } from "./synthesize-from-mandate.js";
|
|
14
9
|
export { synthesizeFromRecording, } from "./synthesize-from-recording.js";
|