@crediolabs/policy-synth 0.3.0 → 0.4.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 (152) hide show
  1. package/dist/install/authority-overlap.d.ts +101 -0
  2. package/dist/install/authority-overlap.js +227 -0
  3. package/dist/install/index.d.ts +1 -0
  4. package/dist/install/index.js +4 -0
  5. package/dist/record/freshness.d.ts +17 -11
  6. package/dist/record/freshness.js +28 -18
  7. package/dist/record/index.d.ts +10 -0
  8. package/dist/record/index.js +32 -1
  9. package/dist/record/rpc.d.ts +4 -0
  10. package/dist/record/rpc.js +4 -1
  11. package/dist/registry/identify.d.ts +10 -1
  12. package/dist/registry/identify.js +4 -1
  13. package/dist/registry/on-chain-spec.d.ts +37 -0
  14. package/dist/registry/on-chain-spec.js +152 -0
  15. package/dist/run/index.d.ts +25 -4
  16. package/dist/run/index.js +65 -4
  17. package/dist/run/schemas.d.ts +313 -0
  18. package/dist/run/schemas.js +52 -0
  19. package/dist/synth/declare.d.ts +30 -0
  20. package/dist/synth/declare.js +98 -0
  21. package/dist/synth/index.d.ts +1 -0
  22. package/dist/synth/index.js +1 -0
  23. package/dist-cjs/install/authority-overlap.d.ts +101 -0
  24. package/dist-cjs/install/authority-overlap.js +236 -0
  25. package/dist-cjs/install/index.d.ts +1 -0
  26. package/dist-cjs/install/index.js +13 -2
  27. package/dist-cjs/record/freshness.d.ts +17 -11
  28. package/dist-cjs/record/freshness.js +28 -18
  29. package/dist-cjs/record/index.d.ts +10 -0
  30. package/dist-cjs/record/index.js +31 -0
  31. package/dist-cjs/record/rpc.d.ts +4 -0
  32. package/dist-cjs/record/rpc.js +7 -3
  33. package/dist-cjs/registry/identify.d.ts +10 -1
  34. package/dist-cjs/registry/identify.js +4 -0
  35. package/dist-cjs/registry/on-chain-spec.d.ts +37 -0
  36. package/dist-cjs/registry/on-chain-spec.js +159 -0
  37. package/dist-cjs/run/index.d.ts +25 -4
  38. package/dist-cjs/run/index.js +65 -2
  39. package/dist-cjs/run/schemas.d.ts +313 -0
  40. package/dist-cjs/run/schemas.js +53 -1
  41. package/dist-cjs/synth/declare.d.ts +30 -0
  42. package/dist-cjs/synth/declare.js +101 -0
  43. package/dist-cjs/synth/index.d.ts +1 -0
  44. package/dist-cjs/synth/index.js +3 -1
  45. package/package.json +2 -2
  46. package/src/install/authority-overlap.ts +312 -0
  47. package/src/install/index.ts +20 -0
  48. package/src/record/freshness.ts +29 -18
  49. package/src/record/index.ts +59 -2
  50. package/src/record/rpc.ts +4 -1
  51. package/src/registry/identify.ts +4 -1
  52. package/src/registry/on-chain-spec.ts +168 -0
  53. package/src/run/index.ts +79 -2
  54. package/src/run/schemas.ts +57 -0
  55. package/src/synth/declare.ts +157 -0
  56. package/src/synth/index.ts +5 -0
  57. package/dist/adapters/interpreter/index.d.ts +0 -1
  58. package/dist/adapters/interpreter/index.js +0 -2
  59. package/dist/adapters/oz/adapter.d.ts +0 -20
  60. package/dist/adapters/oz/adapter.js +0 -281
  61. package/dist/adapters/oz/index.d.ts +0 -1
  62. package/dist/adapters/oz/index.js +0 -2
  63. package/dist/codegen/compile-gate.d.ts +0 -33
  64. package/dist/codegen/compile-gate.js +0 -124
  65. package/dist/codegen/index.d.ts +0 -2
  66. package/dist/codegen/index.js +0 -8
  67. package/dist/codegen/template.d.ts +0 -18
  68. package/dist/codegen/template.js +0 -148
  69. package/dist/ir/index.d.ts +0 -1
  70. package/dist/ir/index.js +0 -2
  71. package/dist/ir/types.d.ts +0 -71
  72. package/dist/ir/types.js +0 -9
  73. package/dist/mandate/index.d.ts +0 -2
  74. package/dist/mandate/index.js +0 -2
  75. package/dist/mandate/to-ir.d.ts +0 -3
  76. package/dist/mandate/to-ir.js +0 -60
  77. package/dist/mandate/types.d.ts +0 -20
  78. package/dist/mandate/types.js +0 -8
  79. package/dist/seams/index.d.ts +0 -1
  80. package/dist/seams/index.js +0 -2
  81. package/dist/seams/types.d.ts +0 -64
  82. package/dist/seams/types.js +0 -11
  83. package/dist/synth/deny-cases.d.ts +0 -28
  84. package/dist/synth/deny-cases.js +0 -212
  85. package/dist/synth/evaluate.d.ts +0 -18
  86. package/dist/synth/evaluate.js +0 -271
  87. package/dist/synth/harness.d.ts +0 -28
  88. package/dist/synth/harness.js +0 -47
  89. package/dist/synth/minimize.d.ts +0 -4
  90. package/dist/synth/minimize.js +0 -38
  91. package/dist/synth/permit-context.d.ts +0 -8
  92. package/dist/synth/permit-context.js +0 -35
  93. package/dist/synth/predicate-literals.d.ts +0 -5
  94. package/dist/synth/predicate-literals.js +0 -25
  95. package/dist/synth/synthesize-from-mandate.d.ts +0 -20
  96. package/dist/synth/synthesize-from-mandate.js +0 -59
  97. package/dist/verify/envelope.d.ts +0 -15
  98. package/dist/verify/envelope.js +0 -22
  99. package/dist/verify/index.d.ts +0 -3
  100. package/dist/verify/index.js +0 -3
  101. package/dist/verify/simulate.d.ts +0 -18
  102. package/dist/verify/simulate.js +0 -172
  103. package/dist/verify/verify.d.ts +0 -15
  104. package/dist/verify/verify.js +0 -181
  105. package/dist-cjs/adapters/interpreter/index.d.ts +0 -1
  106. package/dist-cjs/adapters/interpreter/index.js +0 -7
  107. package/dist-cjs/adapters/oz/adapter.d.ts +0 -20
  108. package/dist-cjs/adapters/oz/adapter.js +0 -286
  109. package/dist-cjs/adapters/oz/index.d.ts +0 -1
  110. package/dist-cjs/adapters/oz/index.js +0 -8
  111. package/dist-cjs/codegen/compile-gate.d.ts +0 -33
  112. package/dist-cjs/codegen/compile-gate.js +0 -128
  113. package/dist-cjs/codegen/index.d.ts +0 -2
  114. package/dist-cjs/codegen/index.js +0 -14
  115. package/dist-cjs/codegen/template.d.ts +0 -18
  116. package/dist-cjs/codegen/template.js +0 -151
  117. package/dist-cjs/ir/index.d.ts +0 -1
  118. package/dist-cjs/ir/index.js +0 -3
  119. package/dist-cjs/ir/types.d.ts +0 -71
  120. package/dist-cjs/ir/types.js +0 -10
  121. package/dist-cjs/mandate/index.d.ts +0 -2
  122. package/dist-cjs/mandate/index.js +0 -6
  123. package/dist-cjs/mandate/to-ir.d.ts +0 -3
  124. package/dist-cjs/mandate/to-ir.js +0 -63
  125. package/dist-cjs/mandate/types.d.ts +0 -20
  126. package/dist-cjs/mandate/types.js +0 -9
  127. package/dist-cjs/seams/index.d.ts +0 -1
  128. package/dist-cjs/seams/index.js +0 -3
  129. package/dist-cjs/seams/types.d.ts +0 -64
  130. package/dist-cjs/seams/types.js +0 -12
  131. package/dist-cjs/synth/deny-cases.d.ts +0 -28
  132. package/dist-cjs/synth/deny-cases.js +0 -217
  133. package/dist-cjs/synth/evaluate.d.ts +0 -18
  134. package/dist-cjs/synth/evaluate.js +0 -274
  135. package/dist-cjs/synth/harness.d.ts +0 -28
  136. package/dist-cjs/synth/harness.js +0 -50
  137. package/dist-cjs/synth/minimize.d.ts +0 -4
  138. package/dist-cjs/synth/minimize.js +0 -41
  139. package/dist-cjs/synth/permit-context.d.ts +0 -8
  140. package/dist-cjs/synth/permit-context.js +0 -38
  141. package/dist-cjs/synth/predicate-literals.d.ts +0 -5
  142. package/dist-cjs/synth/predicate-literals.js +0 -28
  143. package/dist-cjs/synth/synthesize-from-mandate.d.ts +0 -20
  144. package/dist-cjs/synth/synthesize-from-mandate.js +0 -62
  145. package/dist-cjs/verify/envelope.d.ts +0 -15
  146. package/dist-cjs/verify/envelope.js +0 -23
  147. package/dist-cjs/verify/index.d.ts +0 -3
  148. package/dist-cjs/verify/index.js +0 -8
  149. package/dist-cjs/verify/simulate.d.ts +0 -18
  150. package/dist-cjs/verify/simulate.js +0 -175
  151. package/dist-cjs/verify/verify.d.ts +0 -15
  152. package/dist-cjs/verify/verify.js +0 -184
@@ -0,0 +1,152 @@
1
+ // src/registry/on-chain-spec.ts - read a contract's own interface off chain.
2
+ //
3
+ // The compiled-in registry covers the protocols we pinned by hand. Everything
4
+ // else was reported as `no-abi`, which read as "this contract does not
5
+ // describe itself" when it almost always does: a Soroban contract embeds a
6
+ // typed spec in its wasm, and the network will hand it over. The recorder was
7
+ // refusing calls whose interface was one RPC round-trip away.
8
+ //
9
+ // What this buys is NARROW and deliberately so. A fetched spec says what the
10
+ // contract's arguments ARE, not what they MEAN. So a call verified against it
11
+ // is recognised - the decode is trustworthy, the confidence gate stops
12
+ // refusing it - but the arguments carry the contract's own parameter NAMES
13
+ // rather than a curated meaning, and nothing here infers which argument is a
14
+ // spend or a recipient. A pinned protocol still outranks a fetched spec for
15
+ // exactly that reason, and `identifyProtocol` is consulted first.
16
+ import { contract as sdkContract } from '@stellar/stellar-sdk';
17
+ import { argsMatchAbi } from "./identify.js";
18
+ /** Map an XDR spec type to the ScVal subset vocabulary the matcher uses.
19
+ *
20
+ * Returns null for a type the recorder's ScVal subset cannot represent. That
21
+ * is deliberate: an argument we cannot type is an argument we cannot check,
22
+ * and claiming a match on it would be the fail-OPEN direction. A function
23
+ * with any such argument is dropped from the derived ABI, so a call to it
24
+ * stays unrecognised rather than being waved through. */
25
+ export function abiTypeFromSpecType(specTypeName) {
26
+ switch (specTypeName) {
27
+ case 'scSpecTypeAddress':
28
+ return 'address';
29
+ case 'scSpecTypeI128':
30
+ return 'i128';
31
+ case 'scSpecTypeU64':
32
+ case 'scSpecTypeI64':
33
+ return 'u64';
34
+ case 'scSpecTypeU32':
35
+ case 'scSpecTypeI32':
36
+ return 'u32';
37
+ case 'scSpecTypeSymbol':
38
+ return 'symbol';
39
+ case 'scSpecTypeVec':
40
+ return 'vec';
41
+ case 'scSpecTypeBytes':
42
+ case 'scSpecTypeBytesN':
43
+ return 'bytes';
44
+ case 'scSpecTypeMap':
45
+ return 'map';
46
+ default:
47
+ return null;
48
+ }
49
+ }
50
+ /** Build a `SpecFetcher` over the SDK's contract client. */
51
+ export function specFetcherFromRpc(rpcUrl, networkPassphrase) {
52
+ return {
53
+ async contractSpec(contractId) {
54
+ try {
55
+ const client = await sdkContract.Client.from({ contractId, networkPassphrase, rpcUrl });
56
+ return client.spec ?? null;
57
+ }
58
+ catch {
59
+ // A contract with no spec, an unreachable RPC and a bad address all
60
+ // land here and all mean the same thing to the caller: no interface
61
+ // was obtained, so the contract stays unrecognised. Fail closed.
62
+ return null;
63
+ }
64
+ },
65
+ };
66
+ }
67
+ /** Convert a fetched spec into the ABI shape `identifyProtocol` already
68
+ * matches against. Functions with an argument outside the ScVal subset are
69
+ * OMITTED rather than partially typed. */
70
+ export function abiFromSpec(spec) {
71
+ const abi = {};
72
+ for (const fn of spec.funcs()) {
73
+ const name = fn.name().toString();
74
+ // The constructor is not callable after deployment, so a recorded
75
+ // invocation can never be one. Keeping it would only widen the surface.
76
+ if (name === '__constructor')
77
+ continue;
78
+ const args = [];
79
+ let usable = true;
80
+ for (const input of fn.inputs()) {
81
+ const type = abiTypeFromSpecType(input.type().switch().name);
82
+ if (type === null) {
83
+ usable = false;
84
+ break;
85
+ }
86
+ const argName = input.name().toString();
87
+ args.push({
88
+ name: argName,
89
+ type,
90
+ // The contract's own parameter name is the honest description. It is
91
+ // NOT a curated meaning - nothing here knows whether `value` is a
92
+ // spend - so downstream must not treat it as one.
93
+ meaning: `${argName} (from the contract's on-chain interface)`,
94
+ });
95
+ }
96
+ if (usable)
97
+ abi[name] = { args };
98
+ }
99
+ return abi;
100
+ }
101
+ /** Fetch and convert in one step. Returns null when no usable interface was
102
+ * obtained, which the caller must treat as "unrecognised". */
103
+ export async function fetchContractAbi(contractId, fetcher) {
104
+ const spec = await fetcher.contractSpec(contractId);
105
+ if (spec === null)
106
+ return null;
107
+ const abi = abiFromSpec(spec);
108
+ return Object.keys(abi).length === 0 ? null : abi;
109
+ }
110
+ /** Every contract invoked anywhere in the tree, with the calls made on it. */
111
+ function callsByContract(invocations) {
112
+ const out = new Map();
113
+ const walk = (inv) => {
114
+ const list = out.get(inv.contract) ?? [];
115
+ list.push({ fn: inv.fn, args: inv.args });
116
+ out.set(inv.contract, list);
117
+ for (const sub of inv.subInvocations)
118
+ walk(sub);
119
+ };
120
+ for (const inv of invocations)
121
+ walk(inv);
122
+ return out;
123
+ }
124
+ /** Contracts whose EVERY recorded call matches their own published interface.
125
+ *
126
+ * All-or-nothing per contract, deliberately. A contract where one call
127
+ * verifies and another does not is a contract we do not understand, and
128
+ * marking it recognised would raise confidence on the strength of the call
129
+ * we happened to check. The unverified call is the one that matters.
130
+ *
131
+ * A contract with no fetchable spec, or a call naming a function absent from
132
+ * it, simply stays unknown - this only ever ADDS recognition, so a failure
133
+ * here degrades to today's behaviour rather than to a wrong answer. */
134
+ export async function resolveContractsByOnChainSpec(invocations, candidates, fetcher) {
135
+ const resolved = new Set();
136
+ const calls = callsByContract(invocations);
137
+ for (const contract of new Set(candidates)) {
138
+ const made = calls.get(contract);
139
+ if (!made || made.length === 0)
140
+ continue;
141
+ const abi = await fetchContractAbi(contract, fetcher);
142
+ if (abi === null)
143
+ continue;
144
+ const everyCallVerifies = made.every((c) => {
145
+ const entry = abi[c.fn];
146
+ return entry !== undefined && argsMatchAbi(entry.args, c.args);
147
+ });
148
+ if (everyCallVerifies)
149
+ resolved.add(contract);
150
+ }
151
+ return resolved;
152
+ }
@@ -1,14 +1,15 @@
1
1
  import { type ErrorCode, type PredicateNode, type ProposedPolicy, type RecordedTransaction, type ToolError, type ToolResponse } from '../index.ts';
2
+ import { type AuthorityOverlap } from '../install/authority-overlap.ts';
2
3
  import { type BuildInstallPolicyResult, type BuildRevokePolicyResult } from '../install/build-install-policy.ts';
3
4
  import { getInterpreterInfo } from '../install/get-interpreter-info.ts';
4
5
  import { type RecordTransactionInput, type SimulatePolicyInput, type SynthesizePolicyInput, type VerifyPolicyInput } from './schemas.ts';
5
- export type { GetInterpreterInfoInput, InstallPolicyInput, RecordTransactionInput, RevokePolicyInput, SimulatePolicyInput, SynthesizePolicyInput, VerifyPolicyInput, } from './schemas.ts';
6
- export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, NetworkSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, } from './schemas.ts';
6
+ export type { DeclarePolicyInput, GetInterpreterInfoInput, InstallPolicyInput, RecordTransactionInput, RevokePolicyInput, SimulatePolicyInput, SynthesizePolicyInput, VerifyPolicyInput, } from './schemas.ts';
7
+ export { ComposeUserResponsesSchema, DeclarePolicyInputSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, NetworkSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, } from './schemas.ts';
7
8
  export type RunRecordTransactionInput = RecordTransactionInput;
8
9
  export type RunSynthesizePolicyInput = SynthesizePolicyInput;
9
10
  export type RunSimulatePolicyInput = SimulatePolicyInput;
10
11
  export type RunVerifyPolicyInput = VerifyPolicyInput;
11
- type RunToolName = 'record_transaction' | 'synthesize_policy' | 'simulate_policy' | 'verify_policy' | 'install_policy' | 'revoke_policy' | 'get_interpreter_info';
12
+ type RunToolName = 'record_transaction' | 'synthesize_policy' | 'declare_policy' | 'simulate_policy' | 'verify_policy' | 'install_policy' | 'revoke_policy' | 'get_interpreter_info';
12
13
  /** `record_transaction` body - wraps `recordTransaction`. The tool input
13
14
  * matches the core RecordInput minus the injected `fetcher` (the transport
14
15
  * layer does not own the RPC). Returns the core ToolResponse unchanged.
@@ -37,7 +38,9 @@ export declare function runSynthesizePolicy(raw: unknown): Promise<ToolResponse<
37
38
  predicateTree: PredicateNode | null;
38
39
  };
39
40
  }>;
40
- export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<BuildInstallPolicyResult>>;
41
+ export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<BuildInstallPolicyResult & {
42
+ authorityScan: AuthorityOverlap[] | null;
43
+ }>>;
41
44
  /** `revoke_policy` body - thin wrapper over `buildRevokePolicyXdr`.
42
45
  * Emits an unsigned XDR for `account.remove_context_rule(ruleId)`; the
43
46
  * smart account itself handles uninstalling each attached policy. Auth
@@ -63,6 +66,24 @@ export declare function runSimulatePolicy(raw: unknown): ToolResponse<{
63
66
  argCount: number;
64
67
  };
65
68
  }>;
69
+ /** `declare_policy` body - the DECLARATIVE front-end.
70
+ *
71
+ * `synthesize_policy` infers a predicate from a transaction that happened;
72
+ * this takes the constraint stated outright. No RPC, no decoding and no
73
+ * parseConfidence, so nothing here can be refused for a contract the registry
74
+ * does not recognise - which is most of the point of having it.
75
+ *
76
+ * The returned `warnings` are load-bearing, not decoration. An argument index
77
+ * the caller did not supply is DEFAULTED to the SEP-41 position, and a bound
78
+ * on the wrong argument constrains something the caller did not mean without
79
+ * ever announcing itself, so a caller that ignores warnings can install a
80
+ * predicate that reads correctly and binds nothing. */
81
+ export declare function runDeclarePolicy(raw: unknown): ToolResponse<{
82
+ predicate: PredicateNode;
83
+ encodedPredicate: string;
84
+ predicateHash: string;
85
+ warnings: string[];
86
+ }>;
66
87
  /** `verify_policy` body - the permit case plus a generated deny case per
67
88
  * dimension.
68
89
  *
package/dist/run/index.js CHANGED
@@ -18,22 +18,25 @@
18
18
  // drive the CLI (which calls into the same core directly without MCP).
19
19
  import { createHash } from 'node:crypto';
20
20
  import { rpc } from '@stellar/stellar-sdk';
21
- import { recordTransaction, synthesizeFromRecording, } from "../index.js";
21
+ import { declarePredicate, encodePredicate, recordTransaction, synthesizeFromRecording, } from "../index.js";
22
+ import { findAuthorityOverlaps, } from "../install/authority-overlap.js";
22
23
  import { buildInstallPolicyXdr, buildRevokePolicyXdr, rpcClientFromServer, } from "../install/build-install-policy.js";
23
24
  import { getInterpreterInfo } from "../install/get-interpreter-info.js";
25
+ import { decodePredicate } from "../predicate/decode.js";
24
26
  import { evaluate, generateCases } from "../simulate/index.js";
25
- import { GetInterpreterInfoInputSchema, InstallPolicyInputSchema, NETWORK_PASSPHRASES, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_WASM_SHA256, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, VerifyPolicyInputSchema, } from "./schemas.js";
27
+ import { DeclarePolicyInputSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, NETWORK_PASSPHRASES, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_WASM_SHA256, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, VerifyPolicyInputSchema, } from "./schemas.js";
26
28
  // Re-export the underlying Zod schemas so the MCP package (and any other
27
29
  // downstream consumer) can import the canonical input shapes from the same
28
30
  // module that owns the tool-body glue. The strict schemas are the source of
29
31
  // truth - MCP tool shapes are derived from them.
30
- export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, NetworkSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, } from "./schemas.js";
32
+ export { ComposeUserResponsesSchema, DeclarePolicyInputSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, NetworkSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, } from "./schemas.js";
31
33
  /** Map every tool name to its canonical domain error code. Replaces a 7-way
32
34
  * if/else so adding a tool adds one line here rather than a new branch in
33
35
  * each envelope call. */
34
36
  const TOOL_ERROR_CODE = {
35
37
  record_transaction: 'RECORDING_FAILED',
36
38
  synthesize_policy: 'SYNTHESIS_ERROR',
39
+ declare_policy: 'SYNTHESIS_ERROR',
37
40
  simulate_policy: 'SIMULATION_ERROR',
38
41
  verify_policy: 'VERIFICATION_FAILED',
39
42
  install_policy: 'INSTALL_BUILD_FAILED',
@@ -148,7 +151,27 @@ export async function runInstallPolicy(raw) {
148
151
  rpc: rpcClient,
149
152
  ...(input.baseFee !== undefined ? { baseFee: input.baseFee } : {}),
150
153
  });
151
- return { ok: true, data: result };
154
+ // Cross-rule scan, when the caller supplied what else is on the account.
155
+ // ABSENT is reported as `null` rather than an empty list: "we did not
156
+ // look" and "we looked and found nothing" are different answers, and
157
+ // collapsing them would let a caller read silence as safety.
158
+ const authorityScan = input.existingRules === undefined
159
+ ? null
160
+ : findAuthorityOverlaps({
161
+ intended: {
162
+ // `add_context_rule` gets its id FROM the account, so there is
163
+ // no existing rule this install replaces. A sentinel no real id
164
+ // can equal keeps every observed rule in scope.
165
+ ruleId: -1,
166
+ contextType: input.rule.contextRuleType,
167
+ signers: input.rule.signers,
168
+ predicate: decodePredicate(encodedPredicate),
169
+ },
170
+ // The schema types `predicate` loosely (it is the shared
171
+ // PredicateNodeSchema); the shape is already validated.
172
+ existing: input.existingRules,
173
+ });
174
+ return { ok: true, data: { ...result, authorityScan } };
152
175
  }
153
176
  catch (e) {
154
177
  return toolFailure('install_policy', e);
@@ -267,6 +290,44 @@ export function runSimulatePolicy(raw) {
267
290
  return toolFailure('simulate_policy', e);
268
291
  }
269
292
  }
293
+ /** `declare_policy` body - the DECLARATIVE front-end.
294
+ *
295
+ * `synthesize_policy` infers a predicate from a transaction that happened;
296
+ * this takes the constraint stated outright. No RPC, no decoding and no
297
+ * parseConfidence, so nothing here can be refused for a contract the registry
298
+ * does not recognise - which is most of the point of having it.
299
+ *
300
+ * The returned `warnings` are load-bearing, not decoration. An argument index
301
+ * the caller did not supply is DEFAULTED to the SEP-41 position, and a bound
302
+ * on the wrong argument constrains something the caller did not mean without
303
+ * ever announcing itself, so a caller that ignores warnings can install a
304
+ * predicate that reads correctly and binds nothing. */
305
+ export function runDeclarePolicy(raw) {
306
+ const parsed = DeclarePolicyInputSchema.safeParse(raw);
307
+ if (!parsed.success) {
308
+ return { ok: false, error: validationError('declare_policy', parsed.error.issues) };
309
+ }
310
+ try {
311
+ // Rebuilt field-by-field rather than passed through: the schema's
312
+ // optionals are `T | undefined` and `PolicyDeclaration`'s are absent-or-T,
313
+ // which `exactOptionalPropertyTypes` treats as different.
314
+ const d = parsed.data;
315
+ const { predicate, warnings } = declarePredicate({
316
+ fn: d.fn,
317
+ ...(d.contract !== undefined ? { contract: d.contract } : {}),
318
+ ...(d.maxAmount !== undefined ? { maxAmount: d.maxAmount } : {}),
319
+ ...(d.amountArgIndex !== undefined ? { amountArgIndex: d.amountArgIndex } : {}),
320
+ ...(d.recipients !== undefined ? { recipients: d.recipients } : {}),
321
+ ...(d.recipientArgIndex !== undefined ? { recipientArgIndex: d.recipientArgIndex } : {}),
322
+ ...(d.allowZeroCap !== undefined ? { allowZeroCap: d.allowZeroCap } : {}),
323
+ });
324
+ const { encodedPredicate, predicateHash } = encodePredicate(predicate);
325
+ return { ok: true, data: { predicate, encodedPredicate, predicateHash, warnings } };
326
+ }
327
+ catch (e) {
328
+ return toolFailure('declare_policy', e);
329
+ }
330
+ }
270
331
  /** `verify_policy` body - the permit case plus a generated deny case per
271
332
  * dimension.
272
333
  *
@@ -1494,6 +1494,104 @@ export declare const VerifyPolicyInputSchema: z.ZodObject<{
1494
1494
  predicate?: unknown;
1495
1495
  }>;
1496
1496
  export type VerifyPolicyInput = z.infer<typeof VerifyPolicyInputSchema>;
1497
+ /** A rule ALREADY on the account, as the caller observed it. Supplying these
1498
+ * turns on the cross-rule authority scan: a signer belonging to several rules
1499
+ * picks which one applies, so a predicate only constrains a key when the
1500
+ * policed rule is the only rule that key is on. */
1501
+ export declare const ObservedRuleSchema: z.ZodObject<{
1502
+ id: z.ZodNumber;
1503
+ contextType: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1504
+ kind: z.ZodLiteral<"default">;
1505
+ }, "strip", z.ZodTypeAny, {
1506
+ kind: "default";
1507
+ }, {
1508
+ kind: "default";
1509
+ }>, z.ZodObject<{
1510
+ kind: z.ZodLiteral<"call_contract">;
1511
+ contract: z.ZodString;
1512
+ }, "strip", z.ZodTypeAny, {
1513
+ kind: "call_contract";
1514
+ contract: string;
1515
+ }, {
1516
+ kind: "call_contract";
1517
+ contract: string;
1518
+ }>, z.ZodObject<{
1519
+ kind: z.ZodLiteral<"create_contract">;
1520
+ wasmHash: z.ZodString;
1521
+ }, "strip", z.ZodTypeAny, {
1522
+ kind: "create_contract";
1523
+ wasmHash: string;
1524
+ }, {
1525
+ kind: "create_contract";
1526
+ wasmHash: string;
1527
+ }>]>;
1528
+ signers: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1529
+ kind: z.ZodLiteral<"delegated">;
1530
+ address: z.ZodString;
1531
+ }, "strip", z.ZodTypeAny, {
1532
+ address: string;
1533
+ kind: "delegated";
1534
+ }, {
1535
+ address: string;
1536
+ kind: "delegated";
1537
+ }>, z.ZodObject<{
1538
+ kind: z.ZodLiteral<"external">;
1539
+ verifier: z.ZodString;
1540
+ keyBytes: z.ZodString;
1541
+ }, "strip", z.ZodTypeAny, {
1542
+ kind: "external";
1543
+ verifier: string;
1544
+ keyBytes: string;
1545
+ }, {
1546
+ kind: "external";
1547
+ verifier: string;
1548
+ keyBytes: string;
1549
+ }>]>, "many">;
1550
+ policyAddresses: z.ZodArray<z.ZodString, "many">;
1551
+ predicate: z.ZodOptional<z.ZodType<unknown, z.ZodTypeDef, unknown>>;
1552
+ }, "strip", z.ZodTypeAny, {
1553
+ signers: ({
1554
+ address: string;
1555
+ kind: "delegated";
1556
+ } | {
1557
+ kind: "external";
1558
+ verifier: string;
1559
+ keyBytes: string;
1560
+ })[];
1561
+ id: number;
1562
+ contextType: {
1563
+ kind: "default";
1564
+ } | {
1565
+ kind: "call_contract";
1566
+ contract: string;
1567
+ } | {
1568
+ kind: "create_contract";
1569
+ wasmHash: string;
1570
+ };
1571
+ policyAddresses: string[];
1572
+ predicate?: unknown;
1573
+ }, {
1574
+ signers: ({
1575
+ address: string;
1576
+ kind: "delegated";
1577
+ } | {
1578
+ kind: "external";
1579
+ verifier: string;
1580
+ keyBytes: string;
1581
+ })[];
1582
+ id: number;
1583
+ contextType: {
1584
+ kind: "default";
1585
+ } | {
1586
+ kind: "call_contract";
1587
+ contract: string;
1588
+ } | {
1589
+ kind: "create_contract";
1590
+ wasmHash: string;
1591
+ };
1592
+ policyAddresses: string[];
1593
+ predicate?: unknown;
1594
+ }>;
1497
1595
  /** Pinned interpreter address (testnet).
1498
1596
  * Single source for the MCP layer; do not embed elsewhere. */
1499
1597
  export declare const PINNED_INTERPRETER_TESTNET_ADDRESS = "CCL336TCK2Y5OFNRCMN2M3HVPBCEX4PW5H6EQ5VW5NPMXOCP4ESB5XR4";
@@ -1533,7 +1631,134 @@ export declare const RPC_URL_BY_NETWORK: Record<Network, string>;
1533
1631
  * matching passphrase when the wallet signs (a mismatch yields invalid
1534
1632
  * hashes). */
1535
1633
  export declare const NETWORK_PASSPHRASES: Record<Network, string>;
1634
+ export declare const DeclarePolicyInputSchema: z.ZodObject<{
1635
+ fn: z.ZodString;
1636
+ contract: z.ZodOptional<z.ZodString>;
1637
+ /** Smallest unit, unsigned decimal STRING - an i128 is wider than
1638
+ * Number.MAX_SAFE_INTEGER, so a number here would silently round. */
1639
+ maxAmount: z.ZodOptional<z.ZodString>;
1640
+ amountArgIndex: z.ZodOptional<z.ZodNumber>;
1641
+ recipients: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1642
+ recipientArgIndex: z.ZodOptional<z.ZodNumber>;
1643
+ allowZeroCap: z.ZodOptional<z.ZodBoolean>;
1644
+ }, "strict", z.ZodTypeAny, {
1645
+ fn: string;
1646
+ contract?: string | undefined;
1647
+ maxAmount?: string | undefined;
1648
+ amountArgIndex?: number | undefined;
1649
+ recipients?: string[] | undefined;
1650
+ recipientArgIndex?: number | undefined;
1651
+ allowZeroCap?: boolean | undefined;
1652
+ }, {
1653
+ fn: string;
1654
+ contract?: string | undefined;
1655
+ maxAmount?: string | undefined;
1656
+ amountArgIndex?: number | undefined;
1657
+ recipients?: string[] | undefined;
1658
+ recipientArgIndex?: number | undefined;
1659
+ allowZeroCap?: boolean | undefined;
1660
+ }>;
1661
+ export type DeclarePolicyInput = z.infer<typeof DeclarePolicyInputSchema>;
1536
1662
  export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1663
+ /** Rules already on the account. Supplying them turns on the cross-rule
1664
+ * authority scan, which reports every existing rule a signer of this
1665
+ * install could name INSTEAD - including an unpoliced one, against which
1666
+ * the predicate never runs. Absent means the scan is skipped, and the
1667
+ * result says so rather than reporting "no overlaps found". */
1668
+ existingRules: z.ZodOptional<z.ZodArray<z.ZodObject<{
1669
+ id: z.ZodNumber;
1670
+ contextType: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1671
+ kind: z.ZodLiteral<"default">;
1672
+ }, "strip", z.ZodTypeAny, {
1673
+ kind: "default";
1674
+ }, {
1675
+ kind: "default";
1676
+ }>, z.ZodObject<{
1677
+ kind: z.ZodLiteral<"call_contract">;
1678
+ contract: z.ZodString;
1679
+ }, "strip", z.ZodTypeAny, {
1680
+ kind: "call_contract";
1681
+ contract: string;
1682
+ }, {
1683
+ kind: "call_contract";
1684
+ contract: string;
1685
+ }>, z.ZodObject<{
1686
+ kind: z.ZodLiteral<"create_contract">;
1687
+ wasmHash: z.ZodString;
1688
+ }, "strip", z.ZodTypeAny, {
1689
+ kind: "create_contract";
1690
+ wasmHash: string;
1691
+ }, {
1692
+ kind: "create_contract";
1693
+ wasmHash: string;
1694
+ }>]>;
1695
+ signers: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1696
+ kind: z.ZodLiteral<"delegated">;
1697
+ address: z.ZodString;
1698
+ }, "strip", z.ZodTypeAny, {
1699
+ address: string;
1700
+ kind: "delegated";
1701
+ }, {
1702
+ address: string;
1703
+ kind: "delegated";
1704
+ }>, z.ZodObject<{
1705
+ kind: z.ZodLiteral<"external">;
1706
+ verifier: z.ZodString;
1707
+ keyBytes: z.ZodString;
1708
+ }, "strip", z.ZodTypeAny, {
1709
+ kind: "external";
1710
+ verifier: string;
1711
+ keyBytes: string;
1712
+ }, {
1713
+ kind: "external";
1714
+ verifier: string;
1715
+ keyBytes: string;
1716
+ }>]>, "many">;
1717
+ policyAddresses: z.ZodArray<z.ZodString, "many">;
1718
+ predicate: z.ZodOptional<z.ZodType<unknown, z.ZodTypeDef, unknown>>;
1719
+ }, "strip", z.ZodTypeAny, {
1720
+ signers: ({
1721
+ address: string;
1722
+ kind: "delegated";
1723
+ } | {
1724
+ kind: "external";
1725
+ verifier: string;
1726
+ keyBytes: string;
1727
+ })[];
1728
+ id: number;
1729
+ contextType: {
1730
+ kind: "default";
1731
+ } | {
1732
+ kind: "call_contract";
1733
+ contract: string;
1734
+ } | {
1735
+ kind: "create_contract";
1736
+ wasmHash: string;
1737
+ };
1738
+ policyAddresses: string[];
1739
+ predicate?: unknown;
1740
+ }, {
1741
+ signers: ({
1742
+ address: string;
1743
+ kind: "delegated";
1744
+ } | {
1745
+ kind: "external";
1746
+ verifier: string;
1747
+ keyBytes: string;
1748
+ })[];
1749
+ id: number;
1750
+ contextType: {
1751
+ kind: "default";
1752
+ } | {
1753
+ kind: "call_contract";
1754
+ contract: string;
1755
+ } | {
1756
+ kind: "create_contract";
1757
+ wasmHash: string;
1758
+ };
1759
+ policyAddresses: string[];
1760
+ predicate?: unknown;
1761
+ }>, "many">>;
1537
1762
  /** The smart account contract address (C...) that will receive the rule. */
1538
1763
  smartAccount: z.ZodString;
1539
1764
  /** The signer that authorises the install (G... wallet). Used only for
@@ -1918,6 +2143,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1918
2143
  [k: string]: unknown;
1919
2144
  };
1920
2145
  network?: "mainnet" | "testnet" | undefined;
2146
+ existingRules?: {
2147
+ signers: ({
2148
+ address: string;
2149
+ kind: "delegated";
2150
+ } | {
2151
+ kind: "external";
2152
+ verifier: string;
2153
+ keyBytes: string;
2154
+ })[];
2155
+ id: number;
2156
+ contextType: {
2157
+ kind: "default";
2158
+ } | {
2159
+ kind: "call_contract";
2160
+ contract: string;
2161
+ } | {
2162
+ kind: "create_contract";
2163
+ wasmHash: string;
2164
+ };
2165
+ policyAddresses: string[];
2166
+ predicate?: unknown;
2167
+ }[] | undefined;
1921
2168
  rpcUrl?: string | undefined;
1922
2169
  allowUnpinnedRpcUrl?: boolean | undefined;
1923
2170
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -1955,6 +2202,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1955
2202
  [k: string]: unknown;
1956
2203
  };
1957
2204
  network?: "mainnet" | "testnet" | undefined;
2205
+ existingRules?: {
2206
+ signers: ({
2207
+ address: string;
2208
+ kind: "delegated";
2209
+ } | {
2210
+ kind: "external";
2211
+ verifier: string;
2212
+ keyBytes: string;
2213
+ })[];
2214
+ id: number;
2215
+ contextType: {
2216
+ kind: "default";
2217
+ } | {
2218
+ kind: "call_contract";
2219
+ contract: string;
2220
+ } | {
2221
+ kind: "create_contract";
2222
+ wasmHash: string;
2223
+ };
2224
+ policyAddresses: string[];
2225
+ predicate?: unknown;
2226
+ }[] | undefined;
1958
2227
  rpcUrl?: string | undefined;
1959
2228
  allowUnpinnedRpcUrl?: boolean | undefined;
1960
2229
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -1992,6 +2261,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1992
2261
  [k: string]: unknown;
1993
2262
  };
1994
2263
  network?: "mainnet" | "testnet" | undefined;
2264
+ existingRules?: {
2265
+ signers: ({
2266
+ address: string;
2267
+ kind: "delegated";
2268
+ } | {
2269
+ kind: "external";
2270
+ verifier: string;
2271
+ keyBytes: string;
2272
+ })[];
2273
+ id: number;
2274
+ contextType: {
2275
+ kind: "default";
2276
+ } | {
2277
+ kind: "call_contract";
2278
+ contract: string;
2279
+ } | {
2280
+ kind: "create_contract";
2281
+ wasmHash: string;
2282
+ };
2283
+ policyAddresses: string[];
2284
+ predicate?: unknown;
2285
+ }[] | undefined;
1995
2286
  rpcUrl?: string | undefined;
1996
2287
  allowUnpinnedRpcUrl?: boolean | undefined;
1997
2288
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -2029,6 +2320,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
2029
2320
  [k: string]: unknown;
2030
2321
  };
2031
2322
  network?: "mainnet" | "testnet" | undefined;
2323
+ existingRules?: {
2324
+ signers: ({
2325
+ address: string;
2326
+ kind: "delegated";
2327
+ } | {
2328
+ kind: "external";
2329
+ verifier: string;
2330
+ keyBytes: string;
2331
+ })[];
2332
+ id: number;
2333
+ contextType: {
2334
+ kind: "default";
2335
+ } | {
2336
+ kind: "call_contract";
2337
+ contract: string;
2338
+ } | {
2339
+ kind: "create_contract";
2340
+ wasmHash: string;
2341
+ };
2342
+ policyAddresses: string[];
2343
+ predicate?: unknown;
2344
+ }[] | undefined;
2032
2345
  rpcUrl?: string | undefined;
2033
2346
  allowUnpinnedRpcUrl?: boolean | undefined;
2034
2347
  allowUnpinnedInterpreter?: boolean | undefined;