@crediolabs/policy-synth 0.1.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/dist/adapters/oz/adapter.d.ts +20 -0
- package/dist/adapters/oz/adapter.js +282 -0
- package/dist/adapters/oz/index.d.ts +1 -0
- package/dist/adapters/oz/index.js +2 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.js +2 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/ir/index.d.ts +1 -0
- package/dist/ir/index.js +2 -0
- package/dist/ir/types.d.ts +97 -0
- package/dist/ir/types.js +11 -0
- package/dist/mandate/index.d.ts +2 -0
- package/dist/mandate/index.js +2 -0
- package/dist/mandate/to-ir.d.ts +3 -0
- package/dist/mandate/to-ir.js +60 -0
- package/dist/mandate/types.d.ts +20 -0
- package/dist/mandate/types.js +8 -0
- package/dist/record/decode.d.ts +76 -0
- package/dist/record/decode.js +372 -0
- package/dist/record/freshness.d.ts +17 -0
- package/dist/record/freshness.js +50 -0
- package/dist/record/index.d.ts +21 -0
- package/dist/record/index.js +163 -0
- package/dist/record/movements.d.ts +20 -0
- package/dist/record/movements.js +187 -0
- package/dist/record/rpc.d.ts +22 -0
- package/dist/record/rpc.js +70 -0
- package/dist/record/validate.d.ts +22 -0
- package/dist/record/validate.js +60 -0
- package/dist/registry/identify.d.ts +11 -0
- package/dist/registry/identify.js +84 -0
- package/dist/registry/index.d.ts +3 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/known-addresses.d.ts +16 -0
- package/dist/registry/known-addresses.js +49 -0
- package/dist/registry/protocols.d.ts +38 -0
- package/dist/registry/protocols.js +149 -0
- package/dist/seams/index.d.ts +1 -0
- package/dist/seams/index.js +2 -0
- package/dist/seams/types.d.ts +66 -0
- package/dist/seams/types.js +11 -0
- package/dist/synth/compose-from-recording.d.ts +36 -0
- package/dist/synth/compose-from-recording.js +162 -0
- package/dist/synth/index.d.ts +5 -0
- package/dist/synth/index.js +6 -0
- package/dist/synth/lower.d.ts +23 -0
- package/dist/synth/lower.js +116 -0
- package/dist/synth/scope.d.ts +26 -0
- package/dist/synth/scope.js +77 -0
- package/dist/synth/synthesize-from-mandate.d.ts +5 -0
- package/dist/synth/synthesize-from-mandate.js +34 -0
- package/dist/synth/synthesize-from-recording.d.ts +17 -0
- package/dist/synth/synthesize-from-recording.js +178 -0
- package/dist/types.d.ts +249 -0
- package/dist/types.js +35 -0
- package/package.json +37 -0
- package/src/adapters/oz/adapter.ts +363 -0
- package/src/adapters/oz/index.ts +9 -0
- package/src/errors.ts +79 -0
- package/src/index.ts +9 -0
- package/src/ir/index.ts +13 -0
- package/src/ir/types.ts +94 -0
- package/src/mandate/index.ts +4 -0
- package/src/mandate/to-ir.ts +71 -0
- package/src/mandate/types.ts +21 -0
- package/src/record/decode.ts +500 -0
- package/src/record/freshness.ts +63 -0
- package/src/record/index.ts +224 -0
- package/src/record/movements.ts +188 -0
- package/src/record/rpc.ts +88 -0
- package/src/record/validate.ts +75 -0
- package/src/registry/identify.ts +99 -0
- package/src/registry/index.ts +24 -0
- package/src/registry/known-addresses.ts +71 -0
- package/src/registry/protocols.ts +176 -0
- package/src/seams/index.ts +11 -0
- package/src/seams/types.ts +81 -0
- package/src/synth/compose-from-recording.ts +226 -0
- package/src/synth/index.ts +19 -0
- package/src/synth/lower.ts +144 -0
- package/src/synth/scope.ts +108 -0
- package/src/synth/synthesize-from-mandate.ts +46 -0
- package/src/synth/synthesize-from-recording.ts +226 -0
- package/src/types.ts +218 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// src/types.ts - shared domain types (single source of truth)
|
|
2
|
+
|
|
3
|
+
export type Network = 'mainnet' | 'testnet'
|
|
4
|
+
|
|
5
|
+
/** Decoded Soroban value - the normalised subset synthesis needs. */
|
|
6
|
+
export type ScVal =
|
|
7
|
+
| { type: 'address'; value: string }
|
|
8
|
+
| { type: 'i128'; value: string }
|
|
9
|
+
| { type: 'u64'; value: string }
|
|
10
|
+
| { type: 'u32'; value: string }
|
|
11
|
+
| { type: 'symbol'; value: string }
|
|
12
|
+
| { type: 'vec'; value: ScVal[] }
|
|
13
|
+
| { type: 'bytes'; value: string }
|
|
14
|
+
| { type: 'other'; value: string }
|
|
15
|
+
|
|
16
|
+
/** OZ Accounts framework hard limits (verified against canonical OZ source). */
|
|
17
|
+
export const OZ_LIMITS = {
|
|
18
|
+
maxPoliciesPerRule: 5,
|
|
19
|
+
maxSignersPerRule: 15,
|
|
20
|
+
// NO per-account rule cap - OZ imposes none.
|
|
21
|
+
} as const
|
|
22
|
+
|
|
23
|
+
/** Soroban / Stellar protocol limits used to bound inputs fail-closed. Single
|
|
24
|
+
* source so the schema boundary and the core validators cannot drift. */
|
|
25
|
+
export const SOROBAN_LIMITS = {
|
|
26
|
+
/** `valid_until` and other ledger-sequence fields are u32. */
|
|
27
|
+
u32Max: 4294967295,
|
|
28
|
+
/** Upper bound on top-level invocations in a recorded transaction; a real
|
|
29
|
+
* Stellar tx caps operations well below this. Bounds a hand-crafted payload. */
|
|
30
|
+
maxInvocations: 512,
|
|
31
|
+
/** Stellar targets a ~5s ledger close time (DAY_IN_LEDGERS = 17280 = 86400/5),
|
|
32
|
+
* used to convert a spend window in seconds to OZ `period_ledgers`. */
|
|
33
|
+
secondsPerLedger: 5,
|
|
34
|
+
} as const
|
|
35
|
+
|
|
36
|
+
/** Predicate-document caps - enforced fail-closed at install by BOTH the synth (TS)
|
|
37
|
+
* AND the interpreter (Rust). Single source: a CI test asserts the two cap sets are
|
|
38
|
+
* byte-identical (TS reads the same JSON manifest the Rust build emits). If they
|
|
39
|
+
* disagree, the interpreter is authoritative. Rationale per cap:
|
|
40
|
+
* - MAX_DEPTH = 5: 3 walkthroughs need depth 2; budget = 2x growth + 1 for `not` chain.
|
|
41
|
+
* - MAX_LEAVES = 200: allowlists (recipients, paths) typically < 100; budget = 2x + 1 nested allowlist.
|
|
42
|
+
* - MAX_PREDICATE_BYTES = 32 KB: well under the per-ledger-entry 64 KB persistent-storage limit.
|
|
43
|
+
* - MAX_ORACLE_READS = 6: two-round confirmation per asset consumes 2 reads; cap accommodates a predicate that references up to 3 unique oracle assets (3 * 2 = 6 reads). Stays a fail-closed install-time cap.
|
|
44
|
+
* - MAX_IN_OPERAND_COUNT = 32: allowlist < 32 is a UX-natural bound. */
|
|
45
|
+
export const PREDICATE_CAPS = {
|
|
46
|
+
MAX_DEPTH: 5,
|
|
47
|
+
MAX_LEAVES: 200,
|
|
48
|
+
MAX_PREDICATE_BYTES: 32 * 1024,
|
|
49
|
+
MAX_ORACLE_READS: 6,
|
|
50
|
+
MAX_IN_OPERAND_COUNT: 32,
|
|
51
|
+
} as const
|
|
52
|
+
|
|
53
|
+
/** OZ context-rule draft - the unit the install builder builds add_context_rule from. */
|
|
54
|
+
export type ContextRuleDraft = {
|
|
55
|
+
contextRuleType:
|
|
56
|
+
| { kind: 'default' }
|
|
57
|
+
| { kind: 'call_contract'; contract: string }
|
|
58
|
+
| { kind: 'create_contract'; wasmHash: string }
|
|
59
|
+
name: string
|
|
60
|
+
/** Ledger sequence (u32). Soroban `valid_until: Option<u32>`. */
|
|
61
|
+
validUntilLedger: number | null
|
|
62
|
+
/** Mirrors OZ `Signer` enum exactly: `Delegated(Address) | External(Address, Bytes)`.
|
|
63
|
+
* The verifier address + key bytes for an external signer are carried in the
|
|
64
|
+
* `External` variant - there is no `Key` or `SmartAccount` variant, and no
|
|
65
|
+
* master/non-master discriminator lives on the type itself. */
|
|
66
|
+
signers: SignerDraft[]
|
|
67
|
+
policies: PolicyRef[]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Mirrors OZ `Signer` exactly: `Signer::Delegated(Address) | Signer::External(Address, Bytes)`. */
|
|
71
|
+
export type SignerDraft =
|
|
72
|
+
| { kind: 'delegated'; address: string }
|
|
73
|
+
| { kind: 'external'; verifier: string; keyBytes: string }
|
|
74
|
+
|
|
75
|
+
/** Reference to one policy attached to a context rule. */
|
|
76
|
+
export type PolicyRef =
|
|
77
|
+
| { kind: 'oz_builtin'; primitive: OZPrimitiveConfig; instanceAddress: string }
|
|
78
|
+
| { kind: 'interpreter'; interpreterAddress: string; predicateBlobBase64: string }
|
|
79
|
+
|
|
80
|
+
/** A serialised predicate document stored against a (smart_account, rule_id) pair. */
|
|
81
|
+
export interface PolicyDocument {
|
|
82
|
+
/** Grammar version baked into the interpreter wasm. Fail-closed strict match at install
|
|
83
|
+
* AND at evaluate (defence in depth). NOT the per-edit revision. */
|
|
84
|
+
grammarVersion: 1
|
|
85
|
+
/** Per-rule install nonce. Must equal the interpreter's stored nonce + 1 at install.
|
|
86
|
+
* First install accepts nonce = 1 with stored nonce = 0. Re-install is structurally
|
|
87
|
+
* possible by incrementing the nonce; uninstall removes the nonce with state so a
|
|
88
|
+
* subsequent install returns to nonce = 1. Replay of an old install denied. */
|
|
89
|
+
installNonce: number
|
|
90
|
+
/** Canonical ScVal encoding (NOT JSON) - the version-match bypass is closed by the
|
|
91
|
+
* on-chain serialisation, not by a parser. */
|
|
92
|
+
encodedPredicate: string // base64 of canonical ScVal
|
|
93
|
+
/** Hash of the post-canonicalisation bytes of encodedPredicate. Stored alongside the
|
|
94
|
+
* doc and re-verified at evaluate. */
|
|
95
|
+
predicateHash: string
|
|
96
|
+
/** Per-policy overrides of oracle thresholds; absent = use defaults.
|
|
97
|
+
* Overrides may TIGHTEN only (never widen) - the interpreter rejects any per-policy
|
|
98
|
+
* value looser than the wasm-level defaults at install. */
|
|
99
|
+
oracleParams?: {
|
|
100
|
+
/** Seconds; must be <= MAX_ORACLE_STALENESS_SECONDS (600). */
|
|
101
|
+
maxStalenessSeconds?: number
|
|
102
|
+
/** Basis points; must be <= MAX_ORACLE_DEVIATION_BPS (200). */
|
|
103
|
+
maxDeviationBps?: number
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The versioned predicate AST (interpreter side). Tagged union. */
|
|
108
|
+
export type PredicateNode =
|
|
109
|
+
| { op: 'and'; children: PredicateNode[] }
|
|
110
|
+
| { op: 'or'; children: PredicateNode[] }
|
|
111
|
+
| { op: 'not'; child: PredicateNode }
|
|
112
|
+
| { op: 'eq'; left: PredicateLeaf; right: PredicateLeaf }
|
|
113
|
+
| { op: 'lt' | 'lte' | 'gt' | 'gte'; left: PredicateLeaf; right: PredicateLeaf }
|
|
114
|
+
| { op: 'in'; needle: PredicateLeaf; haystack: PredicateLeaf[] }
|
|
115
|
+
|
|
116
|
+
export type PredicateLeaf =
|
|
117
|
+
| { kind: 'call_contract' }
|
|
118
|
+
| { kind: 'call_fn' }
|
|
119
|
+
| { kind: 'call_arg'; index: number }
|
|
120
|
+
// `call_sub_invocation[i]` is NOT in v1 grammar. OZ `Policy::enforce`
|
|
121
|
+
// receives one `Context`, not a sub-invocation tree; sub-invocations live under
|
|
122
|
+
// `InvokerContractAuthEntry::Contract(SubContractInvocation)`, not in the
|
|
123
|
+
// enforce Context. Scope is per-authorized-context; a v1.1 sub-invocation
|
|
124
|
+
// tree leaf is gated on the enforce-Context-shape spike result.
|
|
125
|
+
| { kind: 'amount'; token: string }
|
|
126
|
+
| { kind: 'window_spent'; token: string }
|
|
127
|
+
| { kind: 'now' }
|
|
128
|
+
| { kind: 'valid_until' }
|
|
129
|
+
| { kind: 'invocation_count_in_window'; windowSecs: number }
|
|
130
|
+
| { kind: 'oracle_price'; asset: string } // Stellar Address (SAC address for SEP-41; XLM uses its network SAC)
|
|
131
|
+
|
|
132
|
+
export interface OZPrimitiveConfig {
|
|
133
|
+
primitive: 'spending_limit' | 'simple_threshold' | 'weighted_threshold'
|
|
134
|
+
params: Record<string, unknown>
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ContractInvocation {
|
|
138
|
+
contract: string
|
|
139
|
+
fn: string
|
|
140
|
+
args: ScVal[]
|
|
141
|
+
subInvocations: ContractInvocation[]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface TokenMovement {
|
|
145
|
+
token: string
|
|
146
|
+
from: string
|
|
147
|
+
to: string
|
|
148
|
+
amount: string
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface OnChainEvent {
|
|
152
|
+
contract: string
|
|
153
|
+
topics: string[]
|
|
154
|
+
data: ScVal
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface RecordedTransaction {
|
|
158
|
+
network: Network
|
|
159
|
+
/** Authorising signers (the agent / owner set). */
|
|
160
|
+
signers: string[]
|
|
161
|
+
/** Top-level call + nested sub-invocations captured for analysis:
|
|
162
|
+
* v1 grammar matches only the top-level call's own `(contract, fn_name, args)`
|
|
163
|
+
* that `Policy::enforce` receives - sub-invocations are NOT walked in v1. */
|
|
164
|
+
invocations: ContractInvocation[]
|
|
165
|
+
/** All token movements (from events). */
|
|
166
|
+
tokenMovements: TokenMovement[]
|
|
167
|
+
/** Raw on-chain events from getTransaction (used for validation). */
|
|
168
|
+
events: OnChainEvent[]
|
|
169
|
+
/** Soroban auth entries (which signers, which contracts, which fns). */
|
|
170
|
+
authEntries: unknown[]
|
|
171
|
+
ledgerSequence: number
|
|
172
|
+
fetchedAt: number
|
|
173
|
+
/** The parsed-decoding freshness, computed by the recorder. The synth refuses on
|
|
174
|
+
* parseConfidence.overall < parseConfidence.thresholdUsed. */
|
|
175
|
+
parseConfidence: ParseConfidence
|
|
176
|
+
/** Source account (already recorded, needed by install + reviewer). */
|
|
177
|
+
sourceAccount: string
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface ProposedPolicy {
|
|
181
|
+
contextRule: ContextRuleDraft
|
|
182
|
+
policyDocuments: PolicyDocument[]
|
|
183
|
+
policyRefs: PolicyRef[]
|
|
184
|
+
/** Parsed-decoding freshness mirror; the orchestrator refuses when below threshold. */
|
|
185
|
+
parseConfidence: ParseConfidence
|
|
186
|
+
warnings: string[]
|
|
187
|
+
ambiguities: AmbiguityPrompt[]
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface AmbiguityPrompt {
|
|
191
|
+
code: AmbiguityCode
|
|
192
|
+
question: string
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Catalogue of named ambiguity codes (the skill's prompt files are one-per-code).
|
|
196
|
+
* Adding a code is a synthesizer decision; the prompt authoring must not
|
|
197
|
+
* invent codes outside this union. */
|
|
198
|
+
export type AmbiguityCode =
|
|
199
|
+
| 'DURATION_UNSPECIFIED'
|
|
200
|
+
| 'AMOUNT_BOUND_MISSING'
|
|
201
|
+
| 'RECIPIENT_ALLOWLIST_EMPTY'
|
|
202
|
+
| 'FREQUENCY_BOUND_MISSING'
|
|
203
|
+
| 'ORACLE_ASSET_UNKNOWN'
|
|
204
|
+
| 'MULTIPLE_UNRELATED_TARGETS'
|
|
205
|
+
|
|
206
|
+
/** Structured recording freshness. `overall` is the gate threshold (default 1.0); the
|
|
207
|
+
* breakdown is the diagnostic the user / agent sees when the gate fires. The
|
|
208
|
+
* computation rule is pinned: `overall = 1 - (unknownContracts + opaqueScVals) / total`. */
|
|
209
|
+
export interface ParseConfidence {
|
|
210
|
+
overall: number // 0..1
|
|
211
|
+
knownContracts: string[]
|
|
212
|
+
unknownContracts: Array<{
|
|
213
|
+
contract: string
|
|
214
|
+
reason: 'no-abi' | 'version-mismatch' | 'opaque-result'
|
|
215
|
+
}>
|
|
216
|
+
opaqueScVals: Array<{ path: string; type: string }>
|
|
217
|
+
thresholdUsed: number // 1.0 by default; > 0 only via explicit confidence_override
|
|
218
|
+
}
|