otto-intel-mcp 0.1.2 β†’ 0.1.4

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 (239) hide show
  1. package/README.md +76 -18
  2. package/dist/account-userop-policy-definition.d.ts +14 -0
  3. package/dist/account-userop-policy-definition.js +12 -0
  4. package/dist/account-userop-policy-relation.d.ts +371 -0
  5. package/dist/account-userop-policy-relation.js +87 -0
  6. package/dist/adapter/cdp-signer.d.ts +76 -9
  7. package/dist/adapter/cdp-signer.js +274 -33
  8. package/dist/adapter/evm-call-failure.d.ts +29 -0
  9. package/dist/adapter/evm-call-failure.js +109 -0
  10. package/dist/adapter/refusal.d.ts +1 -1
  11. package/dist/adapter/refusal.js +13 -0
  12. package/dist/adapter/route-simulator.d.ts +91 -0
  13. package/dist/adapter/route-simulator.js +143 -0
  14. package/dist/adapter-cdp-index.d.ts +1 -1
  15. package/dist/executable-equities.d.ts +55 -0
  16. package/dist/executable-equities.js +116 -0
  17. package/dist/executable-yield-markets.d.ts +261 -0
  18. package/dist/executable-yield-markets.js +154 -0
  19. package/dist/execution-account-gas-quote.d.ts +141 -0
  20. package/dist/execution-account-gas-quote.js +443 -0
  21. package/dist/execution-account-gas-review.d.ts +123 -0
  22. package/dist/execution-account-gas-review.js +104 -0
  23. package/dist/execution-account-gas-runtime.d.ts +33 -0
  24. package/dist/execution-account-gas-runtime.js +131 -0
  25. package/dist/execution-account-gas-view.d.ts +936 -0
  26. package/dist/execution-account-gas-view.js +56 -0
  27. package/dist/execution-config.d.ts +3 -0
  28. package/dist/execution-config.js +4 -0
  29. package/dist/execution-delegated-definition.d.ts +7 -6
  30. package/dist/execution-delegated-definition.js +13 -2
  31. package/dist/execution-delegation-admin-definition.d.ts +127 -5
  32. package/dist/execution-delegation-admin-definition.js +155 -5
  33. package/dist/execution-delegation-admin.d.ts +48 -2
  34. package/dist/execution-delegation-admin.js +422 -12
  35. package/dist/execution-delegation-cap-store.d.ts +386 -0
  36. package/dist/execution-delegation-cap-store.js +1150 -0
  37. package/dist/execution-delegation-exit-definition.d.ts +309 -0
  38. package/dist/execution-delegation-exit-definition.js +204 -0
  39. package/dist/execution-delegation-exit-store.d.ts +114 -0
  40. package/dist/execution-delegation-exit-store.js +671 -0
  41. package/dist/execution-delegation-exit.d.ts +4 -0
  42. package/dist/execution-delegation-exit.js +614 -0
  43. package/dist/execution-delegation-finality.d.ts +22 -0
  44. package/dist/execution-delegation-finality.js +90 -0
  45. package/dist/execution-delegation-operation-store.d.ts +129 -0
  46. package/dist/execution-delegation-operation-store.js +1107 -0
  47. package/dist/execution-delegation-operations-definition.d.ts +282 -0
  48. package/dist/execution-delegation-operations-definition.js +117 -0
  49. package/dist/execution-delegation-operations.d.ts +6 -0
  50. package/dist/execution-delegation-operations.js +282 -0
  51. package/dist/execution-delegation-policy.d.ts +154 -8
  52. package/dist/execution-delegation-policy.js +273 -40
  53. package/dist/execution-delegation.d.ts +51 -0
  54. package/dist/execution-delegation.js +441 -31
  55. package/dist/execution-errors.d.ts +1 -1
  56. package/dist/execution-errors.js +122 -0
  57. package/dist/execution-index.d.ts +7 -2
  58. package/dist/execution-index.js +7 -2
  59. package/dist/execution-journal-schema.d.ts +96 -0
  60. package/dist/execution-journal-schema.js +368 -0
  61. package/dist/execution-journal-tables.d.ts +14 -0
  62. package/dist/execution-journal-tables.js +227 -0
  63. package/dist/execution-policy-compatibility.d.ts +34 -0
  64. package/dist/execution-policy-compatibility.js +76 -0
  65. package/dist/execution-registration.d.ts +54 -2
  66. package/dist/execution-registration.js +151 -5
  67. package/dist/execution-sponsored-account-profile.d.ts +32 -0
  68. package/dist/execution-sponsored-account-profile.js +59 -0
  69. package/dist/execution-sponsored-account.d.ts +1519 -0
  70. package/dist/execution-sponsored-account.js +222 -0
  71. package/dist/execution-sponsored-definition.d.ts +1713 -0
  72. package/dist/execution-sponsored-definition.js +185 -0
  73. package/dist/execution-sponsored-gas-policy.d.ts +27 -0
  74. package/dist/execution-sponsored-gas-policy.js +110 -0
  75. package/dist/execution-sponsored-hosted.d.ts +3 -0
  76. package/dist/execution-sponsored-hosted.js +71 -0
  77. package/dist/execution-sponsored-index.d.ts +1322 -0
  78. package/dist/execution-sponsored-index.js +102 -0
  79. package/dist/execution-sponsored-mcp-definition.d.ts +1662 -0
  80. package/dist/execution-sponsored-mcp-definition.js +94 -0
  81. package/dist/execution-sponsored-receipt.d.ts +47 -0
  82. package/dist/execution-sponsored-receipt.js +74 -0
  83. package/dist/execution-sponsored-registration.d.ts +14 -0
  84. package/dist/execution-sponsored-registration.js +77 -0
  85. package/dist/execution-sponsored-runtime.d.ts +84 -0
  86. package/dist/execution-sponsored-runtime.js +149 -0
  87. package/dist/execution-sponsored-settlement.d.ts +15 -0
  88. package/dist/execution-sponsored-settlement.js +88 -0
  89. package/dist/execution-sponsored-store.d.ts +67 -0
  90. package/dist/execution-sponsored-store.js +517 -0
  91. package/dist/execution-sponsored-transport.d.ts +90 -0
  92. package/dist/execution-sponsored-transport.js +180 -0
  93. package/dist/execution-tool-definitions.d.ts +21 -21
  94. package/dist/execution-tool-definitions.js +0 -7
  95. package/dist/execution-tools.d.ts +53 -1
  96. package/dist/execution-tools.js +171 -13
  97. package/dist/execution-worker-guard.d.ts +28 -0
  98. package/dist/execution-worker-guard.js +124 -0
  99. package/dist/hybrid-yield-index.d.ts +10 -0
  100. package/dist/hybrid-yield-index.js +11 -0
  101. package/dist/hybrid-yield-markets.d.ts +96 -0
  102. package/dist/hybrid-yield-markets.js +124 -0
  103. package/dist/hybrid-yield.d.ts +411 -0
  104. package/dist/hybrid-yield.js +155 -0
  105. package/dist/hyperliquid-order-builder.js +4 -0
  106. package/dist/lifi-execution-client.d.ts +6 -0
  107. package/dist/lifi-execution-client.js +3 -0
  108. package/dist/lp-userop-account.d.ts +461 -0
  109. package/dist/lp-userop-account.js +417 -0
  110. package/dist/lp-userop-authority.d.ts +49 -0
  111. package/dist/lp-userop-authority.js +211 -0
  112. package/dist/lp-userop-definition.d.ts +233 -0
  113. package/dist/lp-userop-definition.js +85 -0
  114. package/dist/lp-userop-hosted.d.ts +3 -0
  115. package/dist/lp-userop-hosted.js +73 -0
  116. package/dist/lp-userop-index.d.ts +1569 -0
  117. package/dist/lp-userop-index.js +112 -0
  118. package/dist/lp-userop-mcp-definition.d.ts +1220 -0
  119. package/dist/lp-userop-mcp-definition.js +46 -0
  120. package/dist/lp-userop-operation.d.ts +1230 -0
  121. package/dist/lp-userop-operation.js +140 -0
  122. package/dist/lp-userop-registration.d.ts +14 -0
  123. package/dist/lp-userop-registration.js +70 -0
  124. package/dist/lp-userop-settlement.d.ts +89 -0
  125. package/dist/lp-userop-settlement.js +184 -0
  126. package/dist/lp-userop.d.ts +643 -0
  127. package/dist/lp-userop.js +343 -0
  128. package/dist/native-yield-account-definition.d.ts +1192 -0
  129. package/dist/native-yield-account-definition.js +232 -0
  130. package/dist/native-yield-account-gas.d.ts +58 -0
  131. package/dist/native-yield-account-gas.js +228 -0
  132. package/dist/native-yield-account-index.d.ts +23 -0
  133. package/dist/native-yield-account-index.js +28 -0
  134. package/dist/native-yield-account-policy.d.ts +168 -0
  135. package/dist/native-yield-account-policy.js +525 -0
  136. package/dist/native-yield-account-rpc.d.ts +8 -0
  137. package/dist/native-yield-account-rpc.js +128 -0
  138. package/dist/native-yield-account-runtime.d.ts +71 -0
  139. package/dist/native-yield-account-runtime.js +125 -0
  140. package/dist/native-yield-account-store.d.ts +40 -0
  141. package/dist/native-yield-account-store.js +307 -0
  142. package/dist/native-yield-account.d.ts +662 -0
  143. package/dist/native-yield-account.js +371 -0
  144. package/dist/native-yield-consent-store.d.ts +20 -0
  145. package/dist/native-yield-consent-store.js +317 -0
  146. package/dist/native-yield-definition.d.ts +288 -0
  147. package/dist/native-yield-definition.js +105 -0
  148. package/dist/native-yield-reader.d.ts +4 -0
  149. package/dist/native-yield-reader.js +137 -0
  150. package/dist/native-yield-settlement.d.ts +48 -0
  151. package/dist/native-yield-settlement.js +167 -0
  152. package/dist/native-yield-simulation.d.ts +18 -0
  153. package/dist/native-yield-simulation.js +45 -0
  154. package/dist/native-yield-userop-account.d.ts +675 -0
  155. package/dist/native-yield-userop-account.js +357 -0
  156. package/dist/native-yield-userop-authority.d.ts +214 -0
  157. package/dist/native-yield-userop-authority.js +296 -0
  158. package/dist/native-yield-userop-definition.d.ts +331 -0
  159. package/dist/native-yield-userop-definition.js +105 -0
  160. package/dist/native-yield-userop-hosted.d.ts +3 -0
  161. package/dist/native-yield-userop-hosted.js +71 -0
  162. package/dist/native-yield-userop-index.d.ts +1522 -0
  163. package/dist/native-yield-userop-index.js +142 -0
  164. package/dist/native-yield-userop-mcp-definition.d.ts +1356 -0
  165. package/dist/native-yield-userop-mcp-definition.js +50 -0
  166. package/dist/native-yield-userop-operation.d.ts +845 -0
  167. package/dist/native-yield-userop-operation.js +127 -0
  168. package/dist/native-yield-userop-policy.d.ts +23 -0
  169. package/dist/native-yield-userop-policy.js +25 -0
  170. package/dist/native-yield-userop-registration.d.ts +17 -0
  171. package/dist/native-yield-userop-registration.js +76 -0
  172. package/dist/native-yield-userop-settlement.d.ts +8 -0
  173. package/dist/native-yield-userop-settlement.js +79 -0
  174. package/dist/native-yield-userop-simulation.d.ts +18 -0
  175. package/dist/native-yield-userop-simulation.js +34 -0
  176. package/dist/native-yield-userop.d.ts +35 -0
  177. package/dist/native-yield-userop.js +156 -0
  178. package/dist/native-yield-verify.d.ts +30 -0
  179. package/dist/native-yield-verify.js +141 -0
  180. package/dist/native-yield.d.ts +17 -0
  181. package/dist/native-yield.js +119 -0
  182. package/dist/stdio-main.js +0 -0
  183. package/dist/stock-buy-definition.d.ts +338 -0
  184. package/dist/stock-buy-definition.js +127 -0
  185. package/dist/stock-buy-oracle.d.ts +8 -0
  186. package/dist/stock-buy-oracle.js +22 -0
  187. package/dist/stock-buy.d.ts +17 -0
  188. package/dist/stock-buy.js +164 -0
  189. package/dist/tool-definitions.d.ts +3 -1
  190. package/dist/tool-definitions.js +55 -2
  191. package/dist/vendor/lp-v3/lp-v3-atomic-contract.d.ts +445 -0
  192. package/dist/vendor/lp-v3/lp-v3-atomic-contract.js +90 -0
  193. package/dist/vendor/lp-v3/lp-v3-atomic-evidence.d.ts +14 -0
  194. package/dist/vendor/lp-v3/lp-v3-atomic-evidence.js +82 -0
  195. package/dist/vendor/lp-v3/lp-v3-atomic-index.d.ts +80 -0
  196. package/dist/vendor/lp-v3/lp-v3-atomic-index.js +19 -0
  197. package/dist/vendor/lp-v3/lp-v3-atomic-plan.d.ts +43 -0
  198. package/dist/vendor/lp-v3/lp-v3-atomic-plan.js +112 -0
  199. package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.d.ts +312 -0
  200. package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.js +41 -0
  201. package/dist/vendor/lp-v3/lp-v3-atomic-read-index.d.ts +3 -0
  202. package/dist/vendor/lp-v3/lp-v3-atomic-read-index.js +3 -0
  203. package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.d.ts +49 -0
  204. package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.js +88 -0
  205. package/dist/vendor/lp-v3/lp-v3-atomic-read.d.ts +17 -0
  206. package/dist/vendor/lp-v3/lp-v3-atomic-read.js +232 -0
  207. package/dist/vendor/lp-v3/lp-v3-atomic-settlement.d.ts +105 -0
  208. package/dist/vendor/lp-v3/lp-v3-atomic-settlement.js +162 -0
  209. package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.d.ts +606 -0
  210. package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.js +59 -0
  211. package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.d.ts +168 -0
  212. package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.js +23 -0
  213. package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.d.ts +32 -0
  214. package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.js +228 -0
  215. package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.d.ts +11 -0
  216. package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.js +91 -0
  217. package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.d.ts +9 -0
  218. package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.js +124 -0
  219. package/dist/vendor/lp-v3/lp-v3-contract.d.ts +538 -0
  220. package/dist/vendor/lp-v3/lp-v3-contract.js +137 -0
  221. package/dist/vendor/lp-v3/lp-v3-evidence.d.ts +25 -0
  222. package/dist/vendor/lp-v3/lp-v3-evidence.js +168 -0
  223. package/dist/vendor/lp-v3/lp-v3-plan.d.ts +22 -0
  224. package/dist/vendor/lp-v3/lp-v3-plan.js +135 -0
  225. package/dist/vendor/lp-v3/types.d.ts +131 -0
  226. package/dist/vendor/lp-v3/types.js +6 -0
  227. package/dist/vendor/lp-v3/v4-math.d.ts +18 -0
  228. package/dist/vendor/lp-v3/v4-math.js +90 -0
  229. package/dist/yield-deposit-definition.d.ts +374 -0
  230. package/dist/yield-deposit-definition.js +123 -0
  231. package/dist/yield-deposit.d.ts +16 -0
  232. package/dist/yield-deposit.js +149 -0
  233. package/dist/yield-route-semantics.d.ts +27 -0
  234. package/dist/yield-route-semantics.js +71 -0
  235. package/dist/yield-withdraw-definition.d.ts +357 -0
  236. package/dist/yield-withdraw-definition.js +90 -0
  237. package/dist/yield-withdraw.d.ts +11 -0
  238. package/dist/yield-withdraw.js +122 -0
  239. package/package.json +239 -7
@@ -0,0 +1,282 @@
1
+ import { decodeFunctionData, getAddress, parseTransaction } from 'viem';
2
+ import { assertDelegationCaller } from './execution-delegation.js';
3
+ import { verifyDelegatedStepFinality } from './execution-delegation-finality.js';
4
+ import { DELEGATED_SEND_RESULT_SCHEMA, } from './execution-delegated-definition.js';
5
+ import { confirmedDelegationOperationResult, DELEGATION_OPERATIONS_INPUT_SCHEMA, DELEGATION_OPERATIONS_RESULT_SCHEMA, DELEGATION_RECONCILE_INPUT_SCHEMA, } from './execution-delegation-operations-definition.js';
6
+ import { DELEGATION_OPERATION_JOURNAL_SCHEMA_VERSION, } from './execution-delegation-operation-store.js';
7
+ import { ExecutionWorkerBusyError } from './execution-worker-guard.js';
8
+ import { refuse } from './execution-errors.js';
9
+ /** Treat a structurally valid but internally contradictory store snapshot as corrupt. */
10
+ function coherentSnapshot(userId, snapshot) {
11
+ if (!snapshot.unresolved && !snapshot.journal.ready)
12
+ return false;
13
+ const artifactIds = snapshot.operations.map((operation) => operation.artifactId);
14
+ if (new Set(artifactIds).size !== artifactIds.length)
15
+ return false;
16
+ for (const operation of snapshot.operations) {
17
+ if (operation.userId !== userId)
18
+ return false;
19
+ const stepIndexes = operation.steps.map((step) => step.index);
20
+ if (new Set(stepIndexes).size !== stepIndexes.length)
21
+ return false;
22
+ const hasUnresolvedStep = operation.steps.some((step) => step.state === 'prepared' || step.state === 'submitted' || step.state === 'unknown');
23
+ if (!snapshot.unresolved &&
24
+ (operation.state === 'prepared' || operation.state === 'submitted' || operation.state === 'unknown' || hasUnresolvedStep))
25
+ return false;
26
+ if (operation.state !== 'confirmed') {
27
+ if (operation.result !== undefined)
28
+ return false;
29
+ if (operation.state === 'failed' && hasUnresolvedStep)
30
+ return false;
31
+ continue;
32
+ }
33
+ if (!confirmedDelegationOperationResult(operation))
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ function unavailable(userId) {
39
+ return Object.freeze({
40
+ user_id: userId,
41
+ store_available: false,
42
+ journal: Object.freeze({
43
+ schema_version: DELEGATION_OPERATION_JOURNAL_SCHEMA_VERSION,
44
+ ready: false,
45
+ coverage_started_at: null,
46
+ }),
47
+ unresolved: true,
48
+ operations: [],
49
+ });
50
+ }
51
+ export async function readDelegationOperations(runtime, rawInput, caller) {
52
+ const services = runtime.delegation;
53
+ if (!services)
54
+ return refuse('DELEGATION_UNAVAILABLE', 'delegation is not configured on this server');
55
+ // Header first: malformed arguments reveal nothing before the server caller is accepted.
56
+ assertDelegationCaller(services, caller);
57
+ const parsed = DELEGATION_OPERATIONS_INPUT_SCHEMA.safeParse(rawInput);
58
+ if (!parsed.success) {
59
+ const issue = parsed.error.issues[0];
60
+ return refuse('DELEGATION_INPUT_INVALID', `input does not satisfy the operation lookup schema: ${issue ? `${issue.path.join('.') || '(root)'}: ${issue.message}` : 'unknown issue'}`);
61
+ }
62
+ const user = await services.rig.validateAccessToken(parsed.data.accessToken);
63
+ if (!user)
64
+ return refuse('DELEGATION_TOKEN_INVALID', 'the access token does not resolve to a CDP end user');
65
+ if (!services.operationStore.available)
66
+ return unavailable(user.userId);
67
+ try {
68
+ const snapshot = await services.operationStore.snapshotForUser(user.userId, parsed.data.artifactId);
69
+ if (!coherentSnapshot(user.userId, snapshot))
70
+ return unavailable(user.userId);
71
+ const result = {
72
+ user_id: user.userId,
73
+ store_available: true,
74
+ journal: {
75
+ schema_version: snapshot.journal.schemaVersion,
76
+ ready: snapshot.journal.ready,
77
+ coverage_started_at: snapshot.journal.coverageStartedAt,
78
+ },
79
+ unresolved: snapshot.unresolved,
80
+ operations: snapshot.operations.map((operation) => {
81
+ const confirmed = operation.state === 'confirmed'
82
+ ? confirmedDelegationOperationResult(operation)
83
+ : undefined;
84
+ return {
85
+ artifact_id: operation.artifactId,
86
+ account: getAddress(operation.account),
87
+ chain_id: operation.chainId,
88
+ state: operation.state,
89
+ created_at: operation.createdAt,
90
+ updated_at: operation.updatedAt,
91
+ steps: operation.steps.map((step) => ({
92
+ index: step.index,
93
+ ...(step.transactionHash ? { transaction_hash: step.transactionHash } : {}),
94
+ state: step.state,
95
+ })),
96
+ ...(confirmed ? { result: confirmed } : {}),
97
+ };
98
+ }),
99
+ };
100
+ const checked = DELEGATION_OPERATIONS_RESULT_SCHEMA.safeParse(result);
101
+ return checked.success
102
+ ? Object.freeze(checked.data)
103
+ : unavailable(user.userId);
104
+ }
105
+ catch {
106
+ // An unreadable/corrupt store is never presented as an empty safe history.
107
+ return unavailable(user.userId);
108
+ }
109
+ }
110
+ const APPROVE_ABI = [
111
+ {
112
+ type: 'function', name: 'approve', stateMutability: 'nonpayable',
113
+ inputs: [{ name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }],
114
+ outputs: [{ name: 'success', type: 'bool' }],
115
+ },
116
+ {
117
+ type: 'function', name: 'allowance', stateMutability: 'view',
118
+ inputs: [{ name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }],
119
+ outputs: [{ name: 'allowance', type: 'uint256' }],
120
+ },
121
+ ];
122
+ function hasCompleteDelegatedSuccessShape(operation) {
123
+ return !(operation.steps.length !== 3 ||
124
+ operation.steps.some((step, index) => step.index !== index ||
125
+ step.kind !== ['approve_reset', 'approve', 'call'][index] ||
126
+ step.state !== 'confirmed' ||
127
+ step.receipt?.status !== 'success' ||
128
+ step.receipt.transactionHash.toLowerCase() !== step.transactionHash?.toLowerCase()));
129
+ }
130
+ function recoveredDelegatedSuccess(operation) {
131
+ if (!operation.recoveryContext || !hasCompleteDelegatedSuccessShape(operation))
132
+ return undefined;
133
+ const steps = operation.steps.map((step) => {
134
+ const transaction = parseTransaction(step.request.transaction);
135
+ if (!transaction.to || !step.transactionHash || !step.receipt)
136
+ throw new Error('a confirmed recovery step is incomplete');
137
+ return {
138
+ index: step.index,
139
+ kind: step.kind,
140
+ to: getAddress(transaction.to),
141
+ transaction_hash: step.transactionHash,
142
+ status: 'success',
143
+ block_number: step.receipt.blockNumber,
144
+ gas_used: step.receipt.gasUsed,
145
+ };
146
+ });
147
+ const parsed = DELEGATED_SEND_RESULT_SCHEMA.safeParse({ ...operation.recoveryContext, steps });
148
+ if (!parsed.success)
149
+ throw new Error('the durable recovery result does not satisfy its output schema');
150
+ return Object.freeze(parsed.data);
151
+ }
152
+ async function allowanceIsZero(pub, operation) {
153
+ // The bounded halt clear, when present, is the freshest durable request naming the plan's token and
154
+ // spender. Otherwise the reset request names them even when it reverted. A failed reset may leave an
155
+ // allowance that predates this plan, so absence of a confirmed approval is never evidence of zero.
156
+ // Every non-empty interrupted plan must prove allowance at one exact finalized canonical block before
157
+ // its unified claim can be released.
158
+ const reset = operation.steps.find((step) => step.index === 0 && step.kind === 'approve_reset');
159
+ const clear = operation.steps.find((step) => step.index === 3 && step.kind === 'allowance_clear');
160
+ const zeroRequest = clear ?? reset;
161
+ if (!zeroRequest)
162
+ return false;
163
+ try {
164
+ const transaction = parseTransaction(zeroRequest.request.transaction);
165
+ if (!transaction.to || !transaction.data)
166
+ return false;
167
+ const decoded = decodeFunctionData({ abi: APPROVE_ABI, data: transaction.data });
168
+ if (decoded.functionName !== 'approve')
169
+ return false;
170
+ const [spender, amount] = decoded.args;
171
+ if (amount !== 0n)
172
+ return false;
173
+ const finalized = await pub.getBlock({ blockTag: 'finalized' });
174
+ if (finalized.number === null || finalized.hash === null)
175
+ return false;
176
+ const allowance = await pub.readContract({
177
+ address: getAddress(transaction.to),
178
+ abi: APPROVE_ABI,
179
+ functionName: 'allowance',
180
+ args: [getAddress(operation.account), getAddress(spender)],
181
+ blockNumber: finalized.number,
182
+ });
183
+ const canonical = await pub.getBlock({ blockNumber: finalized.number });
184
+ return canonical.number === finalized.number &&
185
+ canonical.hash?.toLowerCase() === finalized.hash.toLowerCase() && allowance === 0n;
186
+ }
187
+ catch {
188
+ return false;
189
+ }
190
+ }
191
+ /** Known-hash-only recovery. The original worker's fixed guard must be acquirable before any mutation. */
192
+ export async function reconcileDelegationOperation(runtime, rawInput, caller) {
193
+ const services = runtime.delegation;
194
+ if (!services)
195
+ return refuse('DELEGATION_UNAVAILABLE', 'delegation is not configured on this server');
196
+ assertDelegationCaller(services, caller);
197
+ const parsed = DELEGATION_RECONCILE_INPUT_SCHEMA.safeParse(rawInput);
198
+ if (!parsed.success)
199
+ return refuse('DELEGATION_INPUT_INVALID', 'input does not satisfy the delegated reconcile schema');
200
+ const user = await services.rig.validateAccessToken(parsed.data.accessToken);
201
+ if (!user)
202
+ return refuse('DELEGATION_TOKEN_INVALID', 'the access token does not resolve to a CDP end user');
203
+ if (!services.operationStore.available || !services.workerGuards?.available)
204
+ return refuse('DELEGATION_OPERATION_STORE_UNAVAILABLE', 'the operation journal or worker guard is unavailable');
205
+ const initial = await services.operationStore.readRecoveryArtifact(parsed.data.artifactId);
206
+ if (!initial || initial.userId !== user.userId)
207
+ return refuse('DELEGATION_REPLAY_MISMATCH', 'this delegated operation does not belong to the authenticated end user');
208
+ if (initial.state === 'confirmed' || initial.state === 'failed')
209
+ return readDelegationOperations(runtime, parsed.data, caller);
210
+ let guard;
211
+ try {
212
+ guard = await services.workerGuards.acquire('delegated', parsed.data.artifactId);
213
+ }
214
+ catch (error) {
215
+ return error instanceof ExecutionWorkerBusyError
216
+ ? refuse('DELEGATION_OPERATION_UNRESOLVED', 'the original delegated worker is still active')
217
+ : refuse('DELEGATION_OPERATION_STORE_UNAVAILABLE', 'the permanent delegated worker guard is missing or corrupt');
218
+ }
219
+ try {
220
+ // Re-read after acquiring the liveness proof. A late terminal journal write wins and needs no recovery.
221
+ let operation = await services.operationStore.readRecoveryArtifact(parsed.data.artifactId);
222
+ if (!operation || operation.userId !== user.userId)
223
+ return refuse('DELEGATION_OPERATION_STORE_UNAVAILABLE', 'the delegated operation changed while acquiring recovery ownership');
224
+ if (operation.state === 'confirmed' || operation.state === 'failed')
225
+ return readDelegationOperations(runtime, parsed.data, caller);
226
+ const binding = {
227
+ artifactId: operation.artifactId,
228
+ userId: operation.userId,
229
+ account: operation.account,
230
+ chainId: operation.chainId,
231
+ fingerprint: operation.fingerprint,
232
+ };
233
+ const verifiedReceiptSteps = new Set();
234
+ for (const step of operation.steps) {
235
+ const reconciled = await verifyDelegatedStepFinality(services.rig.rig.pub, operation, step);
236
+ if (reconciled.outcome === 'unverified')
237
+ continue;
238
+ if (step.receipt)
239
+ verifiedReceiptSteps.add(step.index);
240
+ if (reconciled.outcome === 'seal') {
241
+ await services.operationStore.markStepReceipt(binding, step.index, step.idempotencyKey, reconciled.receipt, runtime.nowMs());
242
+ verifiedReceiptSteps.add(step.index);
243
+ operation = (await services.operationStore.readRecoveryArtifact(operation.artifactId));
244
+ }
245
+ }
246
+ const unresolved = operation.steps.some((step) => step.state === 'prepared' || step.state === 'submitted' || step.state === 'unknown');
247
+ if (operation.steps.length === 0) {
248
+ // The permanent guard proves the original worker is gone; no durable step means the journal's
249
+ // required pre-handoff write never happened, so zero CDP requests were possible.
250
+ if (operation.state !== 'prepared')
251
+ return refuse('DELEGATION_OPERATION_STORE_UNAVAILABLE', 'a zero-step delegated operation has an impossible aggregate state; its claim remains unresolved');
252
+ await services.operationStore.finalizeFailure(binding, 'DELEGATED_PLAN_NOT_SUBMITTED_RECONCILED', runtime.nowMs());
253
+ }
254
+ else if (!unresolved &&
255
+ operation.steps.every((step) => !step.receipt || verifiedReceiptSteps.has(step.index))) {
256
+ const completeSuccess = hasCompleteDelegatedSuccessShape(operation);
257
+ // A complete economic success can never fall through to the partial-plan allowance rule. A
258
+ // pre-context worker lacks the static result fields needed to reconstruct its response, so the
259
+ // exact safe classification is unresolved until an operator resolves it from external evidence.
260
+ if (completeSuccess && !operation.recoveryContext)
261
+ return readDelegationOperations(runtime, parsed.data, caller);
262
+ let recovered;
263
+ try {
264
+ recovered = recoveredDelegatedSuccess(operation);
265
+ }
266
+ catch {
267
+ return refuse('DELEGATION_OPERATION_STORE_UNAVAILABLE', 'the durable delegated result context cannot reconstruct the canonical success receipt');
268
+ }
269
+ if (recovered) {
270
+ await services.operationStore.finalizeSuccess(binding, recovered, runtime.nowMs());
271
+ }
272
+ else if (!completeSuccess && operation.recoveryContext && await allowanceIsZero(services.rig.rig.pub, operation)) {
273
+ await services.operationStore.finalizeFailure(binding, 'DELEGATED_PLAN_INTERRUPTED_RECONCILED', runtime.nowMs());
274
+ }
275
+ }
276
+ return readDelegationOperations(runtime, parsed.data, caller);
277
+ }
278
+ finally {
279
+ guard.release();
280
+ }
281
+ }
282
+ //# sourceMappingURL=execution-delegation-operations.js.map
@@ -30,6 +30,7 @@
30
30
  * Description obeys the SDK's regex (`/^[A-Za-z0-9 ,.]{1,50}$/`): no hyphens, no parentheses.
31
31
  */
32
32
  import { type Address } from 'viem';
33
+ export { policyRulesDigest } from './execution-policy-compatibility.js';
33
34
  /**
34
35
  * The LINEAGE: a project policy is Otto's own only when its description matches the ANCHORED pattern
35
36
  * `^OTTO model b swap fence v<N>$` (`modelBLineageVersion` returns N). The fence tool updates a lineage policy
@@ -37,10 +38,30 @@ import { type Address } from 'viem';
37
38
  * the prefix in the middle, a suffix, no description β€” is foreign and is never touched.
38
39
  */
39
40
  export declare const MODEL_B_POLICY_LINEAGE_PREFIX = "OTTO model b swap fence";
40
- /** This binary's fence version. Bump it whenever the rules change shape or caps. */
41
- export declare const MODEL_B_POLICY_VERSION: 1;
42
- /** The versioned match key. */
43
- export declare const MODEL_B_POLICY_NAME_V1: string;
41
+ /**
42
+ * This binary's fence version. Bump it whenever the rules change shape or caps.
43
+ *
44
+ * πŸ”΄ v2 (Founder word `fence v2`, pending β€” Yield Copilot withdraw, 2026-09-03). v2 ADDS to v1: an approve
45
+ * rule per registry Morpho share token (spender = the diamond only, bounded by
46
+ * `MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC`) so a delegated vault WITHDRAW can route shares β†’ USDC through the
47
+ * same fenced GenericSwapV3 entry, and a sign-only accept rule for a USDC `transfer` (see
48
+ * `modelBProjectPolicyRules`). The router rule is UNCHANGED and still carries no receiver condition β€” that
49
+ * is the person's only way to move value out of an Otto/CDP wallet today (ws-7, 2026-09-03), and the
50
+ * receiver pin lives in Otto's delegated verifier.
51
+ *
52
+ * TRANSITION: a v2 binary ACCEPTS a v1 fence (swaps and deposits keep working; a withdraw under v1 is
53
+ * refused by name) and the fence tool upgrades v1 β†’ v2 in place only when it is run β€” deploying this code
54
+ * writes nothing to the CDP project.
55
+ */
56
+ export declare const MODEL_B_POLICY_VERSION: 2;
57
+ /** The v1 match key β€” the fence every permission before `fence v2` was minted under; still accepted for swaps and deposits. */
58
+ export declare const MODEL_B_POLICY_NAME_V1: "OTTO model b swap fence v1";
59
+ /** The v2 match key β€” what the fence tool writes; the only fence that admits a delegated Yield Copilot withdraw. */
60
+ export declare const MODEL_B_POLICY_NAME_V2: string;
61
+ /** The CURRENT match key (v2). */
62
+ export declare const MODEL_B_POLICY_NAME: string;
63
+ /** Every lineage version this binary accepts on read-back, oldest first. */
64
+ export declare const MODEL_B_POLICY_ACCEPTED_VERSIONS: readonly (1 | 2)[];
44
65
  /** The lineage version N of a policy description, or undefined when the description is not of our lineage. */
45
66
  export declare function modelBLineageVersion(description: unknown): number | undefined;
46
67
  export declare const MODEL_B_POLICY_NETWORK: "base";
@@ -53,19 +74,63 @@ export declare const MODEL_B_CHAIN_ID: 8453;
53
74
  */
54
75
  export declare const MODEL_B_DELEGATION_EXPIRY_DEFAULT_DAYS: 90;
55
76
  export declare const MODEL_B_DELEGATION_EXPIRY_MAX_DAYS: 180;
56
- /** A requested expiry, bounded: a hostile, absent or over-long value never reaches the mint. */
77
+ /**
78
+ * The floor `boundedDelegationExpiry` has always enforced, NAMED β€” it was a literal `1` inside the bound,
79
+ * so the reserve schema could not state the range it accepts without hand-copying the number, and a
80
+ * hand-copied bound is a bound that drifts. Mirrors `MODEL_B_DELEGATION_EXPIRY_MIN_DAYS` in the dApp.
81
+ */
82
+ export declare const MODEL_B_DELEGATION_EXPIRY_MIN_DAYS: 1;
83
+ /**
84
+ * A requested expiry, bounded: a hostile, absent or over-long value never reaches the mint.
85
+ *
86
+ * πŸ”΄ THE RESULT LANDS ON A WHOLE SECOND (round 3). CDP stores `expiresAt` at whole-second granularity and
87
+ * truncates milliseconds toward zero β€” verified live on the dev project 2026-09-02 (`…:46.137Z` came back
88
+ * as `…:46Z` from `createDelegation` AND from `getDelegationForEndUser`; `…:45.999Z` came back as
89
+ * `…:45Z`, so it truncates rather than rounds). Everything downstream β€” the reservation, the durable row
90
+ * and every enforcement read β€” keys on that second, so the bound produces it directly rather than leaving
91
+ * a millisecond remainder for one of them to drop and another to keep.
92
+ */
57
93
  export declare function boundedDelegationExpiry(requestedDays: unknown, nowMs: number): {
58
94
  readonly days: number;
59
95
  readonly expiresAt: string;
60
96
  };
97
+ /**
98
+ * The mint-expiry helpers live with the store that owns the reserved second; re-exported here because
99
+ * this is the module every consumer of the Model-B numbers already imports.
100
+ */
101
+ export { MINT_EXPIRY_FILLER_MS as MODEL_B_MINT_EXPIRY_FILLER_MS } from './execution-delegation-cap-store.js';
102
+ export { mintExpiryForSecond as mintExpiryFor } from './execution-delegation-cap-store.js';
103
+ export { MODEL_B_CAP_RULESET_VERSION } from './execution-delegation-cap-store.js';
61
104
  /**
62
105
  * The per-swap cap in USD β€” the Founder's number (word `fence cap 5000`, 2026-08-29). It bounds ONE swap:
63
106
  * the engine carries no daily or total limit, and no copy may imply one. Changing it = a new fence version.
64
107
  * Engine conditions are on raw calldata, so the cap is expressed per allowed input token: v1 allows USDC
65
108
  * only, where the mapping is exact (5,000 USD = 5,000,000,000 atomic on the approve amount, which is what
66
109
  * bounds the swap's `fromAmount` on-chain).
110
+ *
111
+ * πŸ”΄ THIS IS THE CEILING, SHARED BY EVERY DELEGATED USER β€” NOT ANY ONE PERSON'S CAP (Founder word
112
+ * `delegate cap choice`, 2026-09-02). Coinbase's engine offers end-user accounts exactly one fence, the
113
+ * project-scope policy, so the number compiled into those rules is the same for everybody and CANNOT be
114
+ * narrowed per user by the engine. A person who wants a lower number β€” "try it with $10" β€” gets it from
115
+ * Otto's own server: their choice is recorded against their grant (`execution-delegation-cap-store.ts`)
116
+ * and enforced on every delegated send, on top of this ceiling and never above it.
67
117
  */
68
118
  export declare const MODEL_B_PER_SWAP_CAP_USD: 5000;
119
+ /**
120
+ * The floor of the range a person may choose. One whole dollar: the smallest cap that can still pass a
121
+ * real swap, and low enough that "try it with $10" is nowhere near it.
122
+ */
123
+ export declare const MODEL_B_PER_SWAP_CAP_MIN_USD: 1;
124
+ /**
125
+ * A requested per-swap cap, bounded β€” the same shape as `boundedDelegationExpiry`, and the ONLY place a
126
+ * requested number becomes a cap.
127
+ *
128
+ * πŸ”΄ IT REFUSES; IT NEVER CLAMPS. A hostile 50,000 is not silently turned into 5,000: a caller that asked
129
+ * for a number outside the range is a caller whose intent this server does not know, and quietly
130
+ * substituting one would make "the cap you chose" a claim nobody could check. Absent means the ceiling β€”
131
+ * the terms every grant already carried before a choice existed.
132
+ */
133
+ export declare function boundedPerSwapCap(requestedUsd: unknown): number;
69
134
  export interface ModelBInputToken {
70
135
  readonly symbol: string;
71
136
  readonly address: Address;
@@ -80,6 +145,28 @@ export declare const BASE_USDC: Address;
80
145
  */
81
146
  export declare const MODEL_B_INPUT_TOKENS_V1: readonly ModelBInputToken[];
82
147
  export declare function modelBInputToken(token: Address): ModelBInputToken | undefined;
148
+ /**
149
+ * πŸ”΄ v2 β€” the share tokens a delegated Yield Copilot WITHDRAW may approve to the diamond: exactly the
150
+ * registry's executable Morpho vaults (ERC-4626 shares, 18 decimals), derived from the registry so a vault
151
+ * added or removed there changes the fence shape (= a new digest) rather than drifting past it.
152
+ */
153
+ export interface ModelBWithdrawToken {
154
+ readonly symbol: string;
155
+ readonly address: Address;
156
+ readonly decimals: 18;
157
+ readonly marketId: string;
158
+ }
159
+ export declare const MODEL_B_WITHDRAW_TOKENS_V2: readonly ModelBWithdrawToken[];
160
+ export declare function modelBWithdrawToken(token: Address): ModelBWithdrawToken | undefined;
161
+ /**
162
+ * The engine-side bound on ONE withdraw's approve, in share atomic units: 5,000 shares. A USDC vault's
163
+ * share is worth β‰₯ 1 USDC by construction at inception and accrues from there (gtUSDCp β‰ˆ 1.107 USDC on
164
+ * 2026-09-03), so 5,000 shares β‰ˆ the 5,000 USD per-swap ceiling at par and somewhat more as yield accrues;
165
+ * after a vault loss the bound only tightens. It is the residual bound for a re-pointed withdraw, the same
166
+ * class as the swap rule's; the person's own cap does NOT apply to a withdraw (see the send gate: a withdraw
167
+ * returns the person's own assets to the person and never increases exposure).
168
+ */
169
+ export declare const MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC: bigint;
83
170
  /** The cap in the token's atomic units. */
84
171
  export declare function modelBPerSwapCapAtomic(token: ModelBInputToken, capUsd?: number): bigint;
85
172
  /** The two GenericSwapV3 ERC20β†’ERC20 entry points a same-chain swap plan may call. */
@@ -206,10 +293,26 @@ export type DelegationPolicyCriterion = {
206
293
  };
207
294
  export interface DelegationPolicyRule {
208
295
  readonly action: 'accept';
209
- readonly operation: 'sendEndUserEvmTransaction';
296
+ /** `sendEndUserEvmTransaction` = the delegated send; `signEndUserEvmTransaction` (v2) = sign-only, the client broadcasts. */
297
+ readonly operation: 'sendEndUserEvmTransaction' | 'signEndUserEvmTransaction';
210
298
  readonly criteria: readonly DelegationPolicyCriterion[];
211
299
  }
212
300
  /** The static v1 rules: one approve rule per allow-listed input token, one router rule. */
301
+ /** The v1 rules, frozen as history: USDC approve to the diamond up to the cap + the router rule. */
302
+ export declare function modelBProjectPolicyRulesV1(capUsd?: number): readonly DelegationPolicyRule[];
303
+ /**
304
+ * The v2 rules = v1 + (a) one approve rule per registry Morpho share token, spender = the diamond only,
305
+ * value ≀ `MODEL_B_WITHDRAW_SHARE_CAP_ATOMIC`, ethValue 0 β€” the delegated withdraw's allowance leg; the
306
+ * router rule is UNCHANGED (no receiver condition; the person's exit path); (b) a SIGN-ONLY accept rule
307
+ * (`signEndUserEvmTransaction`) for a USDC `transfer(to, value)` with value ≀ the per-swap cap and NO
308
+ * recipient condition β€” the person's own USDC exit without a swap, signed in their browser and broadcast
309
+ * by the page. The CDP policy schema carries no initiator/session criterion, so the engine cannot tell a
310
+ * user-signed send from a delegated one on the SAME operation; what it can do is fence a DIFFERENT
311
+ * operation, and Otto's delegated signer only ever calls the send operation (`adapter/cdp-signer.ts`).
312
+ * Stated residual: the developer-side end-user client also exposes `signEvmTransaction`, so under a
313
+ * delegation a compromised Otto could sign a USDC transfer to any address up to the cap β€” the same
314
+ * class and bound as the router rule's missing receiver condition, not a new one.
315
+ */
213
316
  export declare function modelBProjectPolicyRules(capUsd?: number): readonly DelegationPolicyRule[];
214
317
  /** The body PR-2's installer creates or updates: scope, versioned description, the v1 rules. */
215
318
  export declare function modelBProjectPolicyBody(): {
@@ -217,9 +320,15 @@ export declare function modelBProjectPolicyBody(): {
217
320
  readonly description: string;
218
321
  readonly rules: readonly DelegationPolicyRule[];
219
322
  };
220
- /** SHA-256 over the canonical JSON of a rule list β€” the fence's integrity check at read-back. */
221
- export declare function policyRulesDigest(rules: unknown): string;
222
323
  export declare const MODEL_B_POLICY_DIGEST_V1: string;
324
+ export declare const MODEL_B_POLICY_DIGEST_V2: string;
325
+ /** The CURRENT digest (v2). */
326
+ export declare const MODEL_B_POLICY_DIGEST: string;
327
+ /** name β†’ {version, digest} for every accepted lineage version. */
328
+ export declare const MODEL_B_POLICY_DIGEST_BY_NAME: ReadonlyMap<string, {
329
+ readonly version: 1 | 2;
330
+ readonly digest: string;
331
+ }>;
223
332
  /** What the server reads back from `cdp.policies.listPolicies({ scope: 'project' })`, structurally. */
224
333
  export interface DelegationPolicyRecord {
225
334
  readonly id: string;
@@ -231,6 +340,9 @@ export type ModelBFenceReadBack = {
231
340
  readonly present: true;
232
341
  readonly id: string;
233
342
  readonly rulesDigest: string;
343
+ /** Which accepted lineage version reads back; a delegated withdraw needs 2. */
344
+ readonly version: 1 | 2;
345
+ readonly name: string;
234
346
  } | {
235
347
  readonly present: false;
236
348
  readonly reason: 'absent' | 'ambiguous' | 'name_mismatch' | 'digest_mismatch';
@@ -243,6 +355,8 @@ export type ModelBFenceReadBack = {
243
355
  * "absent" β€” including a policy of the right name whose rules drifted.
244
356
  */
245
357
  export declare function readBackModelBFence(policies: readonly DelegationPolicyRecord[]): ModelBFenceReadBack;
358
+ /** True when the read-back is THIS binary's fence version β€” what the fence tool leaves alone. */
359
+ export declare function fenceIsCurrent(readBack: ModelBFenceReadBack): boolean;
246
360
  /**
247
361
  * The same bound the engine enforces, checked server-side BEFORE the first step is sent: a plan the
248
362
  * engine would refuse is refused here without spending an allowance reset on it.
@@ -255,3 +369,35 @@ export declare function assertWithinModelBFence(plan: {
255
369
  readonly token: ModelBInputToken;
256
370
  readonly capAtomic: bigint;
257
371
  };
372
+ /**
373
+ * πŸ”΄ v2 β€” the delegated WITHDRAW's fence: the approve token must be a registry share token, the spender the
374
+ * diamond, the approve amount within the share cap, and the OUTPUT token USDC (a withdraw is "shares β†’ the
375
+ * person's USDC", never a swap into something else under the withdraw's cap-free rule). The receiver pin
376
+ * (== the signing account) is the verifier's, already applied before this runs.
377
+ */
378
+ export declare function assertWithdrawWithinModelBFence(plan: {
379
+ readonly token: Address;
380
+ readonly spender: Address;
381
+ readonly gross: bigint;
382
+ readonly outputToken: Address;
383
+ }): {
384
+ readonly token: ModelBWithdrawToken;
385
+ };
386
+ /**
387
+ * THE PERSON'S OWN CAP, enforced after the engine's (Founder word `delegate cap choice`, 2026-09-02).
388
+ *
389
+ * πŸ”΄ WHY THIS IS A SEPARATE CHECK AND A SEPARATE CODE. `assertWithinModelBFence` above answers "would
390
+ * Coinbase's engine have signed this?" β€” it is the shared ceiling and it is also enforced on-chain by the
391
+ * project policy, so a plan past it never reaches a signature whatever this server does. THIS check
392
+ * answers a different question: "is it inside the number THIS PERSON chose when they minted?" The engine
393
+ * cannot ask it (one project policy, shared by every delegated user), so Otto's server is the only place
394
+ * it can be asked, and a plan that fails it is one the engine WOULD have signed.
395
+ *
396
+ * Folding the two into one call with a smaller number would pass every shape-level test while making the
397
+ * two facts indistinguishable in the refusal, in the logs and in the tests. They are kept apart so a
398
+ * mutation that drops the user's cap reddens on its own named code rather than hiding behind the ceiling.
399
+ */
400
+ export declare function assertWithinUserPerSwapCap(grossAtomic: bigint, capUsd: number, token: ModelBInputToken): {
401
+ readonly capUsd: number;
402
+ readonly capAtomic: bigint;
403
+ };