@crediolabs/policy-synth 0.1.18 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +6 -5
  2. package/dist/adapters/interpreter/adapter.d.ts +19 -25
  3. package/dist/adapters/interpreter/adapter.js +54 -487
  4. package/dist/adapters/interpreter/index.d.ts +1 -1
  5. package/dist/adapters/interpreter/index.js +1 -1
  6. package/dist/adapters/oz/adapter.js +1 -15
  7. package/dist/errors.d.ts +1 -1
  8. package/dist/index.d.ts +1 -4
  9. package/dist/index.js +1 -4
  10. package/dist/install/build-add-context-rule.d.ts +3 -11
  11. package/dist/install/build-add-context-rule.js +2 -97
  12. package/dist/install/build-install-policy.d.ts +4 -53
  13. package/dist/install/build-install-policy.js +44 -123
  14. package/dist/ir/index.d.ts +1 -1
  15. package/dist/ir/types.d.ts +7 -76
  16. package/dist/ir/types.js +0 -2
  17. package/dist/predicate/decode.d.ts +1 -1
  18. package/dist/predicate/decode.js +2 -70
  19. package/dist/predicate/encode.js +45 -229
  20. package/dist/predicate/from-json.js +1 -42
  21. package/dist/record/decode.js +3 -8
  22. package/dist/review-card/builder.d.ts +4 -3
  23. package/dist/review-card/builder.js +18 -162
  24. package/dist/review-card/cross-check.js +2 -105
  25. package/dist/review-card/render-leaf.d.ts +4 -0
  26. package/dist/review-card/render-leaf.js +47 -0
  27. package/dist/run/index.d.ts +35 -53
  28. package/dist/run/index.js +115 -156
  29. package/dist/run/schemas.d.ts +168 -1650
  30. package/dist/run/schemas.js +36 -199
  31. package/dist/seams/types.d.ts +0 -2
  32. package/dist/simulate/deny-cases.d.ts +26 -0
  33. package/dist/simulate/deny-cases.js +282 -0
  34. package/dist/simulate/evaluate.d.ts +18 -0
  35. package/dist/simulate/evaluate.js +271 -0
  36. package/dist/simulate/index.d.ts +4 -0
  37. package/dist/simulate/index.js +8 -0
  38. package/dist/synth/compose-from-recording.d.ts +24 -51
  39. package/dist/synth/compose-from-recording.js +120 -282
  40. package/dist/synth/deny-cases.d.ts +5 -7
  41. package/dist/synth/deny-cases.js +23 -373
  42. package/dist/synth/evaluate.d.ts +0 -21
  43. package/dist/synth/evaluate.js +56 -336
  44. package/dist/synth/index.d.ts +0 -5
  45. package/dist/synth/index.js +0 -5
  46. package/dist/synth/permit-context.d.ts +2 -9
  47. package/dist/synth/permit-context.js +1 -82
  48. package/dist/synth/synthesize-from-recording.d.ts +1 -29
  49. package/dist/synth/synthesize-from-recording.js +41 -356
  50. package/dist/types.d.ts +8 -60
  51. package/dist/types.js +5 -2
  52. package/dist/verify/simulate.d.ts +1 -14
  53. package/dist/verify/simulate.js +2 -88
  54. package/dist/verify/verify.d.ts +1 -7
  55. package/dist/verify/verify.js +0 -8
  56. package/dist-cjs/adapters/interpreter/adapter.d.ts +19 -25
  57. package/dist-cjs/adapters/interpreter/adapter.js +56 -489
  58. package/dist-cjs/adapters/interpreter/index.d.ts +1 -1
  59. package/dist-cjs/adapters/interpreter/index.js +1 -2
  60. package/dist-cjs/adapters/oz/adapter.js +1 -15
  61. package/dist-cjs/errors.d.ts +1 -1
  62. package/dist-cjs/index.d.ts +1 -4
  63. package/dist-cjs/index.js +1 -4
  64. package/dist-cjs/install/build-add-context-rule.d.ts +3 -11
  65. package/dist-cjs/install/build-add-context-rule.js +1 -96
  66. package/dist-cjs/install/build-install-policy.d.ts +4 -53
  67. package/dist-cjs/install/build-install-policy.js +44 -122
  68. package/dist-cjs/ir/index.d.ts +1 -1
  69. package/dist-cjs/ir/types.d.ts +7 -76
  70. package/dist-cjs/ir/types.js +0 -2
  71. package/dist-cjs/predicate/decode.d.ts +1 -1
  72. package/dist-cjs/predicate/decode.js +2 -70
  73. package/dist-cjs/predicate/encode.js +45 -229
  74. package/dist-cjs/predicate/from-json.js +1 -42
  75. package/dist-cjs/record/decode.js +3 -8
  76. package/dist-cjs/review-card/builder.d.ts +4 -3
  77. package/dist-cjs/review-card/builder.js +23 -167
  78. package/dist-cjs/review-card/cross-check.js +7 -110
  79. package/dist-cjs/review-card/render-leaf.d.ts +4 -0
  80. package/dist-cjs/review-card/render-leaf.js +52 -0
  81. package/dist-cjs/run/index.d.ts +35 -53
  82. package/dist-cjs/run/index.js +116 -162
  83. package/dist-cjs/run/schemas.d.ts +168 -1650
  84. package/dist-cjs/run/schemas.js +37 -200
  85. package/dist-cjs/seams/types.d.ts +0 -2
  86. package/dist-cjs/simulate/deny-cases.d.ts +26 -0
  87. package/dist-cjs/simulate/deny-cases.js +286 -0
  88. package/dist-cjs/simulate/evaluate.d.ts +18 -0
  89. package/dist-cjs/simulate/evaluate.js +274 -0
  90. package/dist-cjs/simulate/index.d.ts +4 -0
  91. package/dist-cjs/simulate/index.js +13 -0
  92. package/dist-cjs/synth/compose-from-recording.d.ts +24 -51
  93. package/dist-cjs/synth/compose-from-recording.js +120 -282
  94. package/dist-cjs/synth/deny-cases.d.ts +5 -7
  95. package/dist-cjs/synth/deny-cases.js +23 -374
  96. package/dist-cjs/synth/evaluate.d.ts +0 -21
  97. package/dist-cjs/synth/evaluate.js +57 -337
  98. package/dist-cjs/synth/index.d.ts +0 -5
  99. package/dist-cjs/synth/index.js +1 -11
  100. package/dist-cjs/synth/permit-context.d.ts +2 -9
  101. package/dist-cjs/synth/permit-context.js +1 -82
  102. package/dist-cjs/synth/synthesize-from-recording.d.ts +1 -29
  103. package/dist-cjs/synth/synthesize-from-recording.js +39 -354
  104. package/dist-cjs/types.d.ts +8 -60
  105. package/dist-cjs/types.js +6 -3
  106. package/dist-cjs/verify/simulate.d.ts +1 -14
  107. package/dist-cjs/verify/simulate.js +2 -88
  108. package/dist-cjs/verify/verify.d.ts +1 -7
  109. package/dist-cjs/verify/verify.js +0 -8
  110. package/package.json +4 -4
  111. package/src/adapters/interpreter/adapter.ts +76 -572
  112. package/src/errors.ts +0 -19
  113. package/src/index.ts +1 -4
  114. package/src/install/build-add-context-rule.ts +4 -127
  115. package/src/install/build-install-policy.ts +93 -214
  116. package/src/predicate/decode.ts +2 -70
  117. package/src/predicate/encode.ts +49 -261
  118. package/src/predicate/from-json.ts +1 -43
  119. package/src/record/decode.ts +3 -8
  120. package/src/review-card/builder.ts +19 -168
  121. package/src/review-card/cross-check.ts +3 -105
  122. package/src/review-card/render-leaf.ts +48 -0
  123. package/src/run/index.ts +130 -186
  124. package/src/run/schemas.ts +38 -214
  125. package/src/simulate/deny-cases.ts +334 -0
  126. package/src/simulate/evaluate.ts +284 -0
  127. package/src/simulate/index.ts +11 -0
  128. package/src/synth/compose-from-recording.ts +148 -347
  129. package/src/synth/index.ts +0 -13
  130. package/src/synth/synthesize-from-recording.ts +43 -456
  131. package/src/types.ts +13 -49
  132. package/dist/install/build-install-predicate.d.ts +0 -96
  133. package/dist/install/build-install-predicate.js +0 -444
  134. package/dist-cjs/install/build-install-predicate.d.ts +0 -96
  135. package/dist-cjs/install/build-install-predicate.js +0 -479
  136. package/src/adapters/interpreter/index.ts +0 -8
  137. package/src/adapters/oz/adapter.ts +0 -376
  138. package/src/adapters/oz/index.ts +0 -9
  139. package/src/codegen/compile-gate.ts +0 -167
  140. package/src/codegen/index.ts +0 -17
  141. package/src/codegen/template.ts +0 -165
  142. package/src/ir/index.ts +0 -13
  143. package/src/ir/types.ts +0 -132
  144. package/src/mandate/index.ts +0 -4
  145. package/src/mandate/to-ir.ts +0 -71
  146. package/src/mandate/types.ts +0 -21
  147. package/src/seams/index.ts +0 -11
  148. package/src/seams/types.ts +0 -81
  149. package/src/synth/deny-cases.ts +0 -663
  150. package/src/synth/evaluate.ts +0 -613
  151. package/src/synth/harness.ts +0 -68
  152. package/src/synth/minimize.ts +0 -48
  153. package/src/synth/permit-context.ts +0 -136
  154. package/src/synth/predicate-literals.ts +0 -27
  155. package/src/synth/synthesize-from-mandate.ts +0 -82
  156. package/src/verify/envelope.ts +0 -28
  157. package/src/verify/index.ts +0 -5
  158. package/src/verify/simulate.ts +0 -311
  159. package/src/verify/verify.ts +0 -243
@@ -18,13 +18,13 @@
18
18
  // No business logic. No retries. No session state. The same call shape can
19
19
  // drive the CLI (which calls into the same core directly without MCP).
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.VerifyPolicyInputSchema = exports.ToolErrorSchema = exports.TESTNET_RPC_URL = exports.SynthesizePolicyInputSchema = exports.SimulatePolicyInputSchema = exports.RPC_URL_BY_NETWORK = exports.RevokePolicyInputSchema = exports.RecordTransactionInputSchema = exports.RecordedTransactionSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.OzAdapterConfigSchema = exports.OraclePriceFixtureSchema = exports.NetworkSchema = exports.MandateSpecSchema = exports.MAINNET_RPC_URL = exports.InterpreterOptionsSchema = exports.InstallPolicyInputSchema = exports.GetInterpreterInfoInputSchema = exports.ComposeUserResponsesSchema = void 0;
21
+ exports.ToolErrorSchema = exports.TESTNET_RPC_URL = exports.SynthesizePolicyInputSchema = exports.RPC_URL_BY_NETWORK = exports.RevokePolicyInputSchema = exports.RecordTransactionInputSchema = exports.RecordedTransactionSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.NetworkSchema = exports.MAINNET_RPC_URL = exports.InterpreterOptionsSchema = exports.InstallPolicyInputSchema = exports.GetInterpreterInfoInputSchema = exports.ComposeUserResponsesSchema = void 0;
22
22
  exports.runRecordTransaction = runRecordTransaction;
23
23
  exports.runSynthesizePolicy = runSynthesizePolicy;
24
- exports.runSimulatePolicy = runSimulatePolicy;
25
- exports.runVerifyPolicy = runVerifyPolicy;
26
24
  exports.runInstallPolicy = runInstallPolicy;
27
25
  exports.runRevokePolicy = runRevokePolicy;
26
+ exports.runSimulatePolicy = runSimulatePolicy;
27
+ exports.runVerifyPolicy = runVerifyPolicy;
28
28
  exports.runGetInterpreterInfo = runGetInterpreterInfo;
29
29
  exports.caughtError = caughtError;
30
30
  const node_crypto_1 = require("node:crypto");
@@ -32,7 +32,7 @@ const stellar_sdk_1 = require("@stellar/stellar-sdk");
32
32
  const index_ts_1 = require("../index.js");
33
33
  const build_install_policy_ts_1 = require("../install/build-install-policy.js");
34
34
  const get_interpreter_info_ts_1 = require("../install/get-interpreter-info.js");
35
- const index_ts_2 = require("../verify/index.js");
35
+ const index_ts_2 = require("../simulate/index.js");
36
36
  const schemas_ts_1 = require("./schemas.js");
37
37
  // Re-export the underlying Zod schemas so the MCP package (and any other
38
38
  // downstream consumer) can import the canonical input shapes from the same
@@ -44,10 +44,7 @@ Object.defineProperty(exports, "GetInterpreterInfoInputSchema", { enumerable: tr
44
44
  Object.defineProperty(exports, "InstallPolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.InstallPolicyInputSchema; } });
45
45
  Object.defineProperty(exports, "InterpreterOptionsSchema", { enumerable: true, get: function () { return schemas_ts_2.InterpreterOptionsSchema; } });
46
46
  Object.defineProperty(exports, "MAINNET_RPC_URL", { enumerable: true, get: function () { return schemas_ts_2.MAINNET_RPC_URL; } });
47
- Object.defineProperty(exports, "MandateSpecSchema", { enumerable: true, get: function () { return schemas_ts_2.MandateSpecSchema; } });
48
47
  Object.defineProperty(exports, "NetworkSchema", { enumerable: true, get: function () { return schemas_ts_2.NetworkSchema; } });
49
- Object.defineProperty(exports, "OraclePriceFixtureSchema", { enumerable: true, get: function () { return schemas_ts_2.OraclePriceFixtureSchema; } });
50
- Object.defineProperty(exports, "OzAdapterConfigSchema", { enumerable: true, get: function () { return schemas_ts_2.OzAdapterConfigSchema; } });
51
48
  Object.defineProperty(exports, "PINNED_INTERPRETER_ADDRESS_BY_NETWORK", { enumerable: true, get: function () { return schemas_ts_2.PINNED_INTERPRETER_ADDRESS_BY_NETWORK; } });
52
49
  Object.defineProperty(exports, "PINNED_INTERPRETER_GRAMMAR_VERSION", { enumerable: true, get: function () { return schemas_ts_2.PINNED_INTERPRETER_GRAMMAR_VERSION; } });
53
50
  Object.defineProperty(exports, "PINNED_INTERPRETER_MAINNET_ADDRESS", { enumerable: true, get: function () { return schemas_ts_2.PINNED_INTERPRETER_MAINNET_ADDRESS; } });
@@ -59,11 +56,9 @@ Object.defineProperty(exports, "RecordedTransactionSchema", { enumerable: true,
59
56
  Object.defineProperty(exports, "RecordTransactionInputSchema", { enumerable: true, get: function () { return schemas_ts_2.RecordTransactionInputSchema; } });
60
57
  Object.defineProperty(exports, "RevokePolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.RevokePolicyInputSchema; } });
61
58
  Object.defineProperty(exports, "RPC_URL_BY_NETWORK", { enumerable: true, get: function () { return schemas_ts_2.RPC_URL_BY_NETWORK; } });
62
- Object.defineProperty(exports, "SimulatePolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.SimulatePolicyInputSchema; } });
63
59
  Object.defineProperty(exports, "SynthesizePolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.SynthesizePolicyInputSchema; } });
64
60
  Object.defineProperty(exports, "TESTNET_RPC_URL", { enumerable: true, get: function () { return schemas_ts_2.TESTNET_RPC_URL; } });
65
61
  Object.defineProperty(exports, "ToolErrorSchema", { enumerable: true, get: function () { return schemas_ts_2.ToolErrorSchema; } });
66
- Object.defineProperty(exports, "VerifyPolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.VerifyPolicyInputSchema; } });
67
62
  /** Map every tool name to its canonical domain error code. Replaces a 7-way
68
63
  * if/else so adding a tool adds one line here rather than a new branch in
69
64
  * each envelope call. */
@@ -90,10 +85,7 @@ const TOOL_ERROR_CODE = {
90
85
  async function runRecordTransaction(raw) {
91
86
  const parsed = schemas_ts_1.RecordTransactionInputSchema.safeParse(raw);
92
87
  if (!parsed.success) {
93
- return {
94
- ok: false,
95
- error: validationError('record_transaction', parsed.error.issues),
96
- };
88
+ return { ok: false, error: validationError('record_transaction', parsed.error.issues) };
97
89
  }
98
90
  const input = parsed.data;
99
91
  // Strip the wire-only `confidenceOverride` and pass the rest straight through.
@@ -109,14 +101,10 @@ async function runRecordTransaction(raw) {
109
101
  return await (0, index_ts_1.recordTransaction)(coreInput);
110
102
  }
111
103
  catch (e) {
112
- return {
113
- ok: false,
114
- error: caughtError('record_transaction', 'RECORDING_FAILED', e),
115
- };
104
+ return toolFailure('record_transaction', e);
116
105
  }
117
106
  }
118
107
  /** `synthesize_policy` body - discriminated union on `source`:
119
- * - `mandate` -> synthesizeFromMandate
120
108
  * - `recording` -> synthesizeFromRecording
121
109
  * Exposing BOTH front-ends through ONE tool keeps the MCP surface tiny while
122
110
  * letting the agent pick the deterministic or the inferred path. The CLI
@@ -130,21 +118,10 @@ async function runRecordTransaction(raw) {
130
118
  async function runSynthesizePolicy(raw) {
131
119
  const parsed = schemas_ts_1.SynthesizePolicyInputSchema.safeParse(raw);
132
120
  if (!parsed.success) {
133
- return {
134
- ok: false,
135
- error: validationError('synthesize_policy', parsed.error.issues),
136
- };
121
+ return { ok: false, error: validationError('synthesize_policy', parsed.error.issues) };
137
122
  }
138
123
  const input = parsed.data;
139
- const ozConfig = resolveOzConfig(input);
140
124
  try {
141
- if (input.source === 'mandate') {
142
- // Zod's optional fields widen to `T | undefined`, which the core's
143
- // exact-optional MandateSpec rejects; the schema already validated the
144
- // shape, so assert it (same pattern as the recordedTx cast below).
145
- return await (0, index_ts_1.synthesizeFromMandate)(input.mandate, ozConfig, input.explain === true ? { explain: true } : {});
146
- }
147
- // recording source
148
125
  const recorded = input.recordedTx;
149
126
  return await (0, index_ts_1.synthesizeFromRecording)(recorded, {
150
127
  network: input.network,
@@ -154,112 +131,16 @@ async function runSynthesizePolicy(raw) {
154
131
  : {}),
155
132
  ...(input.interpreter !== undefined ? { interpreter: input.interpreter } : {}),
156
133
  ...(input.explain === true ? { explain: true } : {}),
157
- }, ozConfig);
158
- }
159
- catch (e) {
160
- return {
161
- ok: false,
162
- error: caughtError('synthesize_policy', 'SYNTHESIS_ERROR', e),
163
- };
164
- }
165
- }
166
- function resolveOzConfig(input) {
167
- if (input.ozConfig)
168
- return input.ozConfig;
169
- // The mandate path is network-agnostic; fall back to mainnet so the
170
- // placeholder OZ instance addresses are deterministic.
171
- return (0, index_ts_1.placeholderOzConfig)('mainnet');
172
- }
173
- /** `simulate_policy` body - thin wrapper over `simulatePolicy`. The engine
174
- * already returns fail-closed `{ok:false, error}` for runtime failures
175
- * (SIMULATION_ERROR), so the try/catch envelope is for raw SDK throws
176
- * only - same pattern as the other two wrappers. The predicate is
177
- * passed inline (stateless by design; no `proposed_policy_id` lookup). */
178
- async function runSimulatePolicy(raw) {
179
- const parsed = schemas_ts_1.SimulatePolicyInputSchema.safeParse(raw);
180
- if (!parsed.success) {
181
- return {
182
- ok: false,
183
- error: validationError('simulate_policy', parsed.error.issues),
184
- };
185
- }
186
- const input = parsed.data;
187
- try {
188
- // The recursive PredicateNodeSchema + ContractInvocationSchema are
189
- // typed `z.ZodType<unknown>` to survive TS's circular inference; the
190
- // engine wants typed `PredicateNode | null` + `RecordedTransaction`.
191
- // The schema already validated the shape, so assert through the
192
- // unknown back to the core types. Same pattern as the recordedTx
193
- // cast in `runSynthesizePolicy`.
194
- return (0, index_ts_2.simulatePolicy)(input.predicate, input.permitTx, {
195
- ...(input.validUntilLedger !== undefined
196
- ? { validUntilLedger: input.validUntilLedger }
197
- : {}),
198
- ...(input.oraclePricesByAsset !== undefined
199
- ? { oraclePricesByAsset: input.oraclePricesByAsset }
200
- : {}),
201
- });
202
- }
203
- catch (e) {
204
- return {
205
- ok: false,
206
- error: caughtError('simulate_policy', 'SIMULATION_ERROR', e),
207
- };
208
- }
209
- }
210
- /** `verify_policy` body - thin wrapper over `verifyPolicy`. The engine
211
- * already returns `{ok:false, error}` with code VERIFICATION_FAILED when
212
- * the minimality check fails; the try/catch envelope is for raw SDK
213
- * throws only. Mirrors `runSimulatePolicy` exactly. */
214
- async function runVerifyPolicy(raw) {
215
- const parsed = schemas_ts_1.VerifyPolicyInputSchema.safeParse(raw);
216
- if (!parsed.success) {
217
- return {
218
- ok: false,
219
- error: validationError('verify_policy', parsed.error.issues),
220
- };
221
- }
222
- const input = parsed.data;
223
- try {
224
- // Same cast as runSimulatePolicy: the recursive schemas are typed
225
- // `unknown`; the engine wants typed `PredicateNode` +
226
- // `RecordedTransaction`. The schema already validated the shape.
227
- return (0, index_ts_2.verifyPolicy)(input.predicate, input.permitTx, {
228
- ...(input.validUntilLedger !== undefined ? { validUntilLedger: input.validUntilLedger } : {}),
229
- ...(input.oraclePricesByAsset !== undefined
230
- ? { oraclePricesByAsset: input.oraclePricesByAsset }
231
- : {}),
232
134
  });
233
135
  }
234
136
  catch (e) {
235
- return {
236
- ok: false,
237
- error: caughtError('verify_policy', 'VERIFICATION_FAILED', e),
238
- };
137
+ return toolFailure('synthesize_policy', e);
239
138
  }
240
139
  }
241
- /** `install_policy` body - thin wrapper over `buildInstallPolicyXdr`.
242
- * Returns the unsigned Soroban transaction envelope (base64 XDR) the
243
- * wallet signs. The wallet signature IS the user-confirmation step - no
244
- * `action_id` two-call pair (the server is stateless, see server.ts:10-12).
245
- * One call installs the policy outright: `add_context_rule` carries the
246
- * predicate to the interpreter in its `policies` install_param, so no
247
- * separate `interpreter.install` call is needed or possible.
248
- *
249
- * Default-deny: an interpreter policy address other than the pinned
250
- * interpreter for the selected network is REFUSED (the smart account
251
- * would delegate to an interpreter the caller controls); the same
252
- * applies to a non-pinned RPC URL (the auth nonce the wallet signs
253
- * comes from the RPC). Both gates accept an explicit opt-in flag.
254
- * Pin selection follows `input.network` (defaults to `testnet` so the
255
- * pre-mainnet callers keep working unchanged). */
256
140
  async function runInstallPolicy(raw) {
257
141
  const parsed = schemas_ts_1.InstallPolicyInputSchema.safeParse(raw);
258
142
  if (!parsed.success) {
259
- return {
260
- ok: false,
261
- error: validationError('install_policy', parsed.error.issues),
262
- };
143
+ return { ok: false, error: validationError('install_policy', parsed.error.issues) };
263
144
  }
264
145
  const input = parsed.data;
265
146
  const network = input.network ?? 'testnet';
@@ -279,10 +160,7 @@ async function runInstallPolicy(raw) {
279
160
  rpcClient = buildRpcClientFromInput(input.rpcUrl, network);
280
161
  }
281
162
  catch (e) {
282
- return {
283
- ok: false,
284
- error: caughtError('install_policy', 'INSTALL_BUILD_FAILED', e),
285
- };
163
+ return toolFailure('install_policy', e);
286
164
  }
287
165
  try {
288
166
  const interpreterPolicy = input.rule.policies.find((p) => p.kind === 'interpreter');
@@ -304,10 +182,7 @@ async function runInstallPolicy(raw) {
304
182
  return { ok: true, data: result };
305
183
  }
306
184
  catch (e) {
307
- return {
308
- ok: false,
309
- error: caughtError('install_policy', 'INSTALL_BUILD_FAILED', e),
310
- };
185
+ return toolFailure('install_policy', e);
311
186
  }
312
187
  }
313
188
  /** `revoke_policy` body - thin wrapper over `buildRevokePolicyXdr`.
@@ -322,10 +197,7 @@ async function runInstallPolicy(raw) {
322
197
  async function runRevokePolicy(raw) {
323
198
  const parsed = schemas_ts_1.RevokePolicyInputSchema.safeParse(raw);
324
199
  if (!parsed.success) {
325
- return {
326
- ok: false,
327
- error: validationError('revoke_policy', parsed.error.issues),
328
- };
200
+ return { ok: false, error: validationError('revoke_policy', parsed.error.issues) };
329
201
  }
330
202
  const input = parsed.data;
331
203
  const network = input.network ?? 'testnet';
@@ -339,10 +211,7 @@ async function runRevokePolicy(raw) {
339
211
  rpcClient = buildRpcClientFromInput(input.rpcUrl, network);
340
212
  }
341
213
  catch (e) {
342
- return {
343
- ok: false,
344
- error: caughtError('revoke_policy', 'REVOKE_BUILD_FAILED', e),
345
- };
214
+ return toolFailure('revoke_policy', e);
346
215
  }
347
216
  try {
348
217
  const result = await (0, build_install_policy_ts_1.buildRevokePolicyXdr)({
@@ -356,10 +225,7 @@ async function runRevokePolicy(raw) {
356
225
  return { ok: true, data: result };
357
226
  }
358
227
  catch (e) {
359
- return {
360
- ok: false,
361
- error: caughtError('revoke_policy', 'REVOKE_BUILD_FAILED', e),
362
- };
228
+ return toolFailure('revoke_policy', e);
363
229
  }
364
230
  }
365
231
  /** `get_interpreter_info` body - thin wrapper over `getInterpreterInfo`.
@@ -381,14 +247,105 @@ async function runRevokePolicy(raw) {
381
247
  * a non-pinned `rpcUrl` would silently bind the caller to a host they
382
248
  * picked. The pin is enforced here too, with the same `allowUnpinnedRpcUrl`
383
249
  * opt-in as install/revoke. */
384
- async function runGetInterpreterInfo(raw) {
385
- const parsed = schemas_ts_1.GetInterpreterInfoInputSchema.safeParse(raw);
250
+ /** The call a predicate is evaluated against: the single top-level invocation
251
+ * the smart account authorises. `Policy::enforce` receives one `Context`, not
252
+ * a sub-invocation tree, so simulating anything deeper would claim a
253
+ * guarantee the contract does not make. */
254
+ function evalContextFromRecording(tx) {
255
+ const top = tx.invocations[0];
256
+ if (!top)
257
+ return null;
258
+ return { contract: top.contract, fn: top.fn, args: top.args };
259
+ }
260
+ /** Both `simulate_policy` and `verify_policy` evaluate against a context derived
261
+ * from `permitTx`; neither has anything to evaluate when the recording carries
262
+ * no top-level invocation. */
263
+ function noInvocationError(toolName) {
264
+ return {
265
+ code: TOOL_ERROR_CODE[toolName],
266
+ message: `${toolName}: permitTx carries no invocation to evaluate`,
267
+ severity: 'error',
268
+ retryable: false,
269
+ };
270
+ }
271
+ /** `simulate_policy` body - evaluate a predicate against one recorded call.
272
+ *
273
+ * The evaluator is a second implementation of the on-chain semantics, and the
274
+ * conformance harness asserts it agrees with the Rust interpreter case for
275
+ * case. A verdict here is therefore a claim about what the contract would do,
276
+ * not a guess. */
277
+ function runSimulatePolicy(raw) {
278
+ const parsed = schemas_ts_1.SimulatePolicyInputSchema.safeParse(raw);
279
+ if (!parsed.success) {
280
+ return { ok: false, error: validationError('simulate_policy', parsed.error.issues) };
281
+ }
282
+ const input = parsed.data;
283
+ const ctx = evalContextFromRecording(input.permitTx);
284
+ if (!ctx)
285
+ return { ok: false, error: noInvocationError('simulate_policy') };
286
+ try {
287
+ const res = (0, index_ts_2.evaluate)(input.predicate, ctx);
288
+ return {
289
+ ok: true,
290
+ data: {
291
+ permitted: res.permit,
292
+ reason: res.permit ? null : res.reason,
293
+ call: { contract: ctx.contract, fn: ctx.fn, argCount: ctx.args.length },
294
+ },
295
+ };
296
+ }
297
+ catch (e) {
298
+ return toolFailure('simulate_policy', e);
299
+ }
300
+ }
301
+ /** `verify_policy` body - the permit case plus a generated deny case per
302
+ * dimension.
303
+ *
304
+ * Two failure modes are being checked, and they are not the same thing. A
305
+ * permit case that denies means the policy is too STRICT: it would refuse the
306
+ * very transaction it was synthesised from. A deny case that permits means it
307
+ * is too LOOSE: some mutation of that transaction still gets through. `ok` is
308
+ * true only when neither holds. */
309
+ function runVerifyPolicy(raw) {
310
+ const parsed = schemas_ts_1.VerifyPolicyInputSchema.safeParse(raw);
386
311
  if (!parsed.success) {
312
+ return { ok: false, error: validationError('verify_policy', parsed.error.issues) };
313
+ }
314
+ const input = parsed.data;
315
+ const ctx = evalContextFromRecording(input.permitTx);
316
+ if (!ctx)
317
+ return { ok: false, error: noInvocationError('verify_policy') };
318
+ try {
319
+ const predicate = input.predicate;
320
+ const cases = (0, index_ts_2.generateCases)(predicate, ctx);
321
+ const permitRes = (0, index_ts_2.evaluate)(predicate, cases.permit);
322
+ const denies = cases.denies.map((d) => {
323
+ const r = (0, index_ts_2.evaluate)(predicate, d.ctx);
324
+ return {
325
+ dimension: d.dimension,
326
+ denied: !r.permit,
327
+ reason: r.permit ? null : r.reason,
328
+ };
329
+ });
387
330
  return {
388
- ok: false,
389
- error: validationError('get_interpreter_info', parsed.error.issues),
331
+ ok: true,
332
+ data: {
333
+ ok: permitRes.permit && denies.every((d) => d.denied),
334
+ permit: { permitted: permitRes.permit, reason: permitRes.permit ? null : permitRes.reason },
335
+ denies,
336
+ dimensionsCovered: denies.length,
337
+ },
390
338
  };
391
339
  }
340
+ catch (e) {
341
+ return toolFailure('verify_policy', e);
342
+ }
343
+ }
344
+ async function runGetInterpreterInfo(raw) {
345
+ const parsed = schemas_ts_1.GetInterpreterInfoInputSchema.safeParse(raw);
346
+ if (!parsed.success) {
347
+ return { ok: false, error: validationError('get_interpreter_info', parsed.error.issues) };
348
+ }
392
349
  const input = parsed.data;
393
350
  const network = input.network ?? 'testnet';
394
351
  const expectedRpc = schemas_ts_1.RPC_URL_BY_NETWORK[network];
@@ -421,10 +378,7 @@ async function runGetInterpreterInfo(raw) {
421
378
  return { ok: true, data: info };
422
379
  }
423
380
  catch (e) {
424
- return {
425
- ok: false,
426
- error: caughtError('get_interpreter_info', 'RECORDING_FAILED', e),
427
- };
381
+ return toolFailure('get_interpreter_info', e);
428
382
  }
429
383
  }
430
384
  /** Build an InstallRpcClient from an optional URL override + selected
@@ -478,13 +432,8 @@ function enforceInterpreterPin(policies, allowUnpinned, expectedInterpreterAddre
478
432
  function enforceRpcPin(toolName, rpcUrl, allowUnpinned, expectedRpc, network) {
479
433
  if (!rpcUrl || rpcUrl === expectedRpc || allowUnpinned === true)
480
434
  return null;
481
- const code = toolName === 'install_policy'
482
- ? 'INSTALL_BUILD_FAILED'
483
- : toolName === 'revoke_policy'
484
- ? 'REVOKE_BUILD_FAILED'
485
- : 'RECORDING_FAILED';
486
435
  return {
487
- code,
436
+ code: TOOL_ERROR_CODE[toolName],
488
437
  message: `${toolName}: rpcUrl must equal the pinned ${expectedRpc} (${network}); set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
489
438
  severity: 'error',
490
439
  retryable: false,
@@ -511,6 +460,11 @@ function validationError(toolName, issues) {
511
460
  * error in `details` for the agent to inspect. Exported as a test-only seam
512
461
  * so the suite in run/index.test.ts can drive the envelope path without
513
462
  * standing up a full recordTransaction pipeline. */
463
+ /** The fail-closed return every tool body uses in its `catch`. Taking only the
464
+ * tool name keeps the tool -> error-code mapping in TOOL_ERROR_CODE alone. */
465
+ function toolFailure(toolName, e) {
466
+ return { ok: false, error: caughtError(toolName, TOOL_ERROR_CODE[toolName], e) };
467
+ }
514
468
  function caughtError(toolName, code, e) {
515
469
  return {
516
470
  code,