@grimoirelabs/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/builders/expressions.d.ts +33 -0
  2. package/dist/builders/expressions.d.ts.map +1 -0
  3. package/dist/builders/expressions.js +57 -0
  4. package/dist/builders/expressions.js.map +1 -0
  5. package/dist/builders/index.d.ts +44 -0
  6. package/dist/builders/index.d.ts.map +1 -0
  7. package/dist/builders/index.js +32 -0
  8. package/dist/builders/index.js.map +1 -0
  9. package/dist/builders/spell-builder.d.ts +124 -0
  10. package/dist/builders/spell-builder.d.ts.map +1 -0
  11. package/dist/builders/spell-builder.js +299 -0
  12. package/dist/builders/spell-builder.js.map +1 -0
  13. package/dist/builders/step-builder.d.ts +212 -0
  14. package/dist/builders/step-builder.d.ts.map +1 -0
  15. package/dist/builders/step-builder.js +499 -0
  16. package/dist/builders/step-builder.js.map +1 -0
  17. package/dist/compiler/expression-parser.d.ts +14 -0
  18. package/dist/compiler/expression-parser.d.ts.map +1 -0
  19. package/dist/compiler/expression-parser.js +460 -0
  20. package/dist/compiler/expression-parser.js.map +1 -0
  21. package/dist/compiler/grimoire/ast.d.ts +450 -0
  22. package/dist/compiler/grimoire/ast.d.ts.map +1 -0
  23. package/dist/compiler/grimoire/ast.js +19 -0
  24. package/dist/compiler/grimoire/ast.js.map +1 -0
  25. package/dist/compiler/grimoire/errors.d.ts +65 -0
  26. package/dist/compiler/grimoire/errors.d.ts.map +1 -0
  27. package/dist/compiler/grimoire/errors.js +86 -0
  28. package/dist/compiler/grimoire/errors.js.map +1 -0
  29. package/dist/compiler/grimoire/index.d.ts +24 -0
  30. package/dist/compiler/grimoire/index.d.ts.map +1 -0
  31. package/dist/compiler/grimoire/index.js +63 -0
  32. package/dist/compiler/grimoire/index.js.map +1 -0
  33. package/dist/compiler/grimoire/parser.d.ts +135 -0
  34. package/dist/compiler/grimoire/parser.d.ts.map +1 -0
  35. package/dist/compiler/grimoire/parser.js +2148 -0
  36. package/dist/compiler/grimoire/parser.js.map +1 -0
  37. package/dist/compiler/grimoire/tokenizer.d.ts +59 -0
  38. package/dist/compiler/grimoire/tokenizer.d.ts.map +1 -0
  39. package/dist/compiler/grimoire/tokenizer.js +509 -0
  40. package/dist/compiler/grimoire/tokenizer.js.map +1 -0
  41. package/dist/compiler/grimoire/transformer.d.ts +71 -0
  42. package/dist/compiler/grimoire/transformer.d.ts.map +1 -0
  43. package/dist/compiler/grimoire/transformer.js +1011 -0
  44. package/dist/compiler/grimoire/transformer.js.map +1 -0
  45. package/dist/compiler/index.d.ts +45 -0
  46. package/dist/compiler/index.d.ts.map +1 -0
  47. package/dist/compiler/index.js +97 -0
  48. package/dist/compiler/index.js.map +1 -0
  49. package/dist/compiler/ir-generator.d.ts +16 -0
  50. package/dist/compiler/ir-generator.d.ts.map +1 -0
  51. package/dist/compiler/ir-generator.js +997 -0
  52. package/dist/compiler/ir-generator.js.map +1 -0
  53. package/dist/compiler/validator.d.ts +15 -0
  54. package/dist/compiler/validator.d.ts.map +1 -0
  55. package/dist/compiler/validator.js +401 -0
  56. package/dist/compiler/validator.js.map +1 -0
  57. package/dist/index.d.ts +16 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +17 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/runtime/circuit-breaker.d.ts +59 -0
  62. package/dist/runtime/circuit-breaker.d.ts.map +1 -0
  63. package/dist/runtime/circuit-breaker.js +155 -0
  64. package/dist/runtime/circuit-breaker.js.map +1 -0
  65. package/dist/runtime/context.d.ts +92 -0
  66. package/dist/runtime/context.d.ts.map +1 -0
  67. package/dist/runtime/context.js +219 -0
  68. package/dist/runtime/context.js.map +1 -0
  69. package/dist/runtime/error-classifier.d.ts +16 -0
  70. package/dist/runtime/error-classifier.d.ts.map +1 -0
  71. package/dist/runtime/error-classifier.js +38 -0
  72. package/dist/runtime/error-classifier.js.map +1 -0
  73. package/dist/runtime/expression-evaluator.d.ts +36 -0
  74. package/dist/runtime/expression-evaluator.d.ts.map +1 -0
  75. package/dist/runtime/expression-evaluator.js +391 -0
  76. package/dist/runtime/expression-evaluator.js.map +1 -0
  77. package/dist/runtime/index.d.ts +12 -0
  78. package/dist/runtime/index.d.ts.map +1 -0
  79. package/dist/runtime/index.js +11 -0
  80. package/dist/runtime/index.js.map +1 -0
  81. package/dist/runtime/interpreter.d.ts +59 -0
  82. package/dist/runtime/interpreter.d.ts.map +1 -0
  83. package/dist/runtime/interpreter.js +414 -0
  84. package/dist/runtime/interpreter.js.map +1 -0
  85. package/dist/runtime/skills/registry.d.ts +11 -0
  86. package/dist/runtime/skills/registry.d.ts.map +1 -0
  87. package/dist/runtime/skills/registry.js +73 -0
  88. package/dist/runtime/skills/registry.js.map +1 -0
  89. package/dist/runtime/sqlite-state-store.d.ts +28 -0
  90. package/dist/runtime/sqlite-state-store.d.ts.map +1 -0
  91. package/dist/runtime/sqlite-state-store.js +180 -0
  92. package/dist/runtime/sqlite-state-store.js.map +1 -0
  93. package/dist/runtime/state-store.d.ts +52 -0
  94. package/dist/runtime/state-store.d.ts.map +1 -0
  95. package/dist/runtime/state-store.js +32 -0
  96. package/dist/runtime/state-store.js.map +1 -0
  97. package/dist/runtime/steps/action.d.ts +17 -0
  98. package/dist/runtime/steps/action.d.ts.map +1 -0
  99. package/dist/runtime/steps/action.js +430 -0
  100. package/dist/runtime/steps/action.js.map +1 -0
  101. package/dist/runtime/steps/advisory.d.ts +28 -0
  102. package/dist/runtime/steps/advisory.d.ts.map +1 -0
  103. package/dist/runtime/steps/advisory.js +209 -0
  104. package/dist/runtime/steps/advisory.js.map +1 -0
  105. package/dist/runtime/steps/compute.d.ts +9 -0
  106. package/dist/runtime/steps/compute.d.ts.map +1 -0
  107. package/dist/runtime/steps/compute.js +74 -0
  108. package/dist/runtime/steps/compute.js.map +1 -0
  109. package/dist/runtime/steps/conditional.d.ts +14 -0
  110. package/dist/runtime/steps/conditional.d.ts.map +1 -0
  111. package/dist/runtime/steps/conditional.js +58 -0
  112. package/dist/runtime/steps/conditional.js.map +1 -0
  113. package/dist/runtime/steps/emit.d.ts +9 -0
  114. package/dist/runtime/steps/emit.d.ts.map +1 -0
  115. package/dist/runtime/steps/emit.js +70 -0
  116. package/dist/runtime/steps/emit.js.map +1 -0
  117. package/dist/runtime/steps/halt.d.ts +9 -0
  118. package/dist/runtime/steps/halt.d.ts.map +1 -0
  119. package/dist/runtime/steps/halt.js +19 -0
  120. package/dist/runtime/steps/halt.js.map +1 -0
  121. package/dist/runtime/steps/loop.d.ts +14 -0
  122. package/dist/runtime/steps/loop.d.ts.map +1 -0
  123. package/dist/runtime/steps/loop.js +109 -0
  124. package/dist/runtime/steps/loop.js.map +1 -0
  125. package/dist/runtime/steps/parallel.d.ts +9 -0
  126. package/dist/runtime/steps/parallel.d.ts.map +1 -0
  127. package/dist/runtime/steps/parallel.js +87 -0
  128. package/dist/runtime/steps/parallel.js.map +1 -0
  129. package/dist/runtime/steps/pipeline.d.ts +9 -0
  130. package/dist/runtime/steps/pipeline.d.ts.map +1 -0
  131. package/dist/runtime/steps/pipeline.js +125 -0
  132. package/dist/runtime/steps/pipeline.js.map +1 -0
  133. package/dist/runtime/steps/try.d.ts +13 -0
  134. package/dist/runtime/steps/try.d.ts.map +1 -0
  135. package/dist/runtime/steps/try.js +222 -0
  136. package/dist/runtime/steps/try.js.map +1 -0
  137. package/dist/runtime/steps/wait.d.ts +9 -0
  138. package/dist/runtime/steps/wait.d.ts.map +1 -0
  139. package/dist/runtime/steps/wait.js +38 -0
  140. package/dist/runtime/steps/wait.js.map +1 -0
  141. package/dist/types/actions.d.ts +162 -0
  142. package/dist/types/actions.d.ts.map +1 -0
  143. package/dist/types/actions.js +5 -0
  144. package/dist/types/actions.js.map +1 -0
  145. package/dist/types/execution.d.ts +276 -0
  146. package/dist/types/execution.d.ts.map +1 -0
  147. package/dist/types/execution.js +5 -0
  148. package/dist/types/execution.js.map +1 -0
  149. package/dist/types/expressions.d.ts +100 -0
  150. package/dist/types/expressions.d.ts.map +1 -0
  151. package/dist/types/expressions.js +48 -0
  152. package/dist/types/expressions.js.map +1 -0
  153. package/dist/types/index.d.ts +14 -0
  154. package/dist/types/index.d.ts.map +1 -0
  155. package/dist/types/index.js +5 -0
  156. package/dist/types/index.js.map +1 -0
  157. package/dist/types/ir.d.ts +187 -0
  158. package/dist/types/ir.d.ts.map +1 -0
  159. package/dist/types/ir.js +5 -0
  160. package/dist/types/ir.js.map +1 -0
  161. package/dist/types/policy.d.ts +123 -0
  162. package/dist/types/policy.d.ts.map +1 -0
  163. package/dist/types/policy.js +5 -0
  164. package/dist/types/policy.js.map +1 -0
  165. package/dist/types/primitives.d.ts +76 -0
  166. package/dist/types/primitives.d.ts.map +1 -0
  167. package/dist/types/primitives.js +10 -0
  168. package/dist/types/primitives.js.map +1 -0
  169. package/dist/types/steps.d.ts +226 -0
  170. package/dist/types/steps.d.ts.map +1 -0
  171. package/dist/types/steps.js +5 -0
  172. package/dist/types/steps.js.map +1 -0
  173. package/dist/venues/index.d.ts +6 -0
  174. package/dist/venues/index.d.ts.map +1 -0
  175. package/dist/venues/index.js +26 -0
  176. package/dist/venues/index.js.map +1 -0
  177. package/dist/venues/types.d.ts +40 -0
  178. package/dist/venues/types.d.ts.map +1 -0
  179. package/dist/venues/types.js +5 -0
  180. package/dist/venues/types.js.map +1 -0
  181. package/dist/wallet/executor.d.ts +109 -0
  182. package/dist/wallet/executor.d.ts.map +1 -0
  183. package/dist/wallet/executor.js +354 -0
  184. package/dist/wallet/executor.js.map +1 -0
  185. package/dist/wallet/index.d.ts +14 -0
  186. package/dist/wallet/index.d.ts.map +1 -0
  187. package/dist/wallet/index.js +13 -0
  188. package/dist/wallet/index.js.map +1 -0
  189. package/dist/wallet/keystore.d.ts +44 -0
  190. package/dist/wallet/keystore.d.ts.map +1 -0
  191. package/dist/wallet/keystore.js +296 -0
  192. package/dist/wallet/keystore.js.map +1 -0
  193. package/dist/wallet/provider.d.ts +111 -0
  194. package/dist/wallet/provider.d.ts.map +1 -0
  195. package/dist/wallet/provider.js +309 -0
  196. package/dist/wallet/provider.js.map +1 -0
  197. package/dist/wallet/tx-builder.d.ts +85 -0
  198. package/dist/wallet/tx-builder.d.ts.map +1 -0
  199. package/dist/wallet/tx-builder.js +290 -0
  200. package/dist/wallet/tx-builder.js.map +1 -0
  201. package/dist/wallet/types.d.ts +116 -0
  202. package/dist/wallet/types.d.ts.map +1 -0
  203. package/dist/wallet/types.js +86 -0
  204. package/dist/wallet/types.js.map +1 -0
  205. package/package.json +39 -0
@@ -0,0 +1,997 @@
1
+ /**
2
+ * IR Generator
3
+ * Transforms SpellSource AST into SpellIR
4
+ */
5
+ import { parseExpression } from "./expression-parser.js";
6
+ /**
7
+ * Generate IR from parsed SpellSource
8
+ */
9
+ export function generateIR(source) {
10
+ const errors = [];
11
+ const warnings = [];
12
+ // Generate unique ID and hash
13
+ const id = source.spell;
14
+ const hash = generateHash(JSON.stringify(source));
15
+ // Transform venues
16
+ const aliases = [];
17
+ if (source.venues) {
18
+ for (const [alias, venue] of Object.entries(source.venues)) {
19
+ aliases.push({
20
+ alias,
21
+ chain: venue.chain,
22
+ address: venue.address,
23
+ label: venue.label,
24
+ });
25
+ }
26
+ }
27
+ // Transform assets
28
+ const assets = [];
29
+ if (source.assets) {
30
+ for (const [symbol, asset] of Object.entries(source.assets)) {
31
+ assets.push({
32
+ symbol,
33
+ chain: asset.chain,
34
+ address: asset.address,
35
+ decimals: asset.decimals,
36
+ });
37
+ }
38
+ }
39
+ // Transform params
40
+ const params = [];
41
+ if (source.params) {
42
+ for (const [name, value] of Object.entries(source.params)) {
43
+ if (typeof value === "object" && value !== null && "type" in value) {
44
+ const extended = value;
45
+ params.push({
46
+ name,
47
+ type: (extended.type ?? "number"),
48
+ asset: extended.asset,
49
+ default: extended.default,
50
+ min: extended.min,
51
+ max: extended.max,
52
+ });
53
+ }
54
+ else {
55
+ // Simple form - infer type from value
56
+ params.push({
57
+ name,
58
+ type: inferType(value),
59
+ default: value,
60
+ });
61
+ }
62
+ }
63
+ }
64
+ // Transform state
65
+ const state = {
66
+ persistent: {},
67
+ ephemeral: {},
68
+ };
69
+ if (source.state?.persistent) {
70
+ for (const [key, value] of Object.entries(source.state.persistent)) {
71
+ state.persistent[key] = { key, initialValue: value };
72
+ }
73
+ }
74
+ if (source.state?.ephemeral) {
75
+ for (const [key, value] of Object.entries(source.state.ephemeral)) {
76
+ state.ephemeral[key] = { key, initialValue: value };
77
+ }
78
+ }
79
+ // Transform skills
80
+ const skills = [];
81
+ if (source.skills) {
82
+ for (const [name, skill] of Object.entries(source.skills)) {
83
+ skills.push({
84
+ name,
85
+ type: skill.type,
86
+ adapters: skill.adapters,
87
+ defaultConstraints: skill.default_constraints
88
+ ? { maxSlippage: skill.default_constraints.max_slippage }
89
+ : undefined,
90
+ });
91
+ }
92
+ }
93
+ // Transform advisors
94
+ const advisors = [];
95
+ if (source.advisors) {
96
+ for (const [name, advisor] of Object.entries(source.advisors)) {
97
+ advisors.push({
98
+ name,
99
+ model: advisor.model,
100
+ scope: "read-only",
101
+ systemPrompt: advisor.system_prompt,
102
+ skills: advisor.skills,
103
+ allowedTools: advisor.allowed_tools,
104
+ mcp: advisor.mcp,
105
+ defaultTimeout: advisor.timeout,
106
+ defaultFallback: advisor.fallback,
107
+ rateLimit: advisor.rate_limit
108
+ ? {
109
+ maxCallsPerRun: advisor.rate_limit.max_per_run ?? 10,
110
+ maxCallsPerHour: advisor.rate_limit.max_per_hour ?? 100,
111
+ }
112
+ : undefined,
113
+ });
114
+ }
115
+ }
116
+ // Transform triggers
117
+ const triggers = [];
118
+ if (source.trigger) {
119
+ const trigger = transformTrigger(source.trigger);
120
+ if (trigger) {
121
+ triggers.push(trigger);
122
+ }
123
+ }
124
+ else {
125
+ // Default to manual trigger
126
+ triggers.push({ type: "manual" });
127
+ }
128
+ // Transform steps and build source map
129
+ const steps = [];
130
+ const stepIds = new Set();
131
+ const sourceMap = {};
132
+ if (source.steps) {
133
+ for (const rawStep of source.steps) {
134
+ try {
135
+ const step = transformStep(rawStep, stepIds, errors);
136
+ if (step) {
137
+ steps.push(step);
138
+ stepIds.add(step.id);
139
+ // Extract source location from transformer metadata
140
+ const loc = rawStep._sourceLocation;
141
+ if (loc) {
142
+ sourceMap[step.id] = { line: loc.line, column: loc.column };
143
+ }
144
+ }
145
+ }
146
+ catch (e) {
147
+ errors.push({
148
+ code: "STEP_TRANSFORM_ERROR",
149
+ message: `Failed to transform step: ${e.message}`,
150
+ });
151
+ }
152
+ }
153
+ }
154
+ // Transform guards
155
+ const guards = [];
156
+ if (source.guards) {
157
+ for (const rawGuard of source.guards) {
158
+ try {
159
+ const guard = transformGuard(rawGuard, errors);
160
+ if (guard) {
161
+ guards.push(guard);
162
+ }
163
+ }
164
+ catch (e) {
165
+ errors.push({
166
+ code: "GUARD_TRANSFORM_ERROR",
167
+ message: `Failed to transform guard: ${e.message}`,
168
+ });
169
+ }
170
+ }
171
+ }
172
+ // Auto-add "default" advisor if referenced by advisory steps but not declared
173
+ const advisorNames = new Set(advisors.map((a) => a.name));
174
+ const referencedAdvisors = new Set();
175
+ for (const step of steps) {
176
+ if (step.kind === "advisory" && !advisorNames.has(step.advisor)) {
177
+ referencedAdvisors.add(step.advisor);
178
+ }
179
+ }
180
+ for (const name of referencedAdvisors) {
181
+ advisors.push({
182
+ name,
183
+ model: "sonnet",
184
+ scope: "read-only",
185
+ });
186
+ }
187
+ if (errors.length > 0) {
188
+ return { success: false, errors, warnings };
189
+ }
190
+ const ir = {
191
+ id,
192
+ version: source.version,
193
+ meta: {
194
+ name: source.spell,
195
+ description: source.description,
196
+ created: Date.now(),
197
+ hash,
198
+ },
199
+ aliases,
200
+ assets,
201
+ skills,
202
+ advisors,
203
+ params,
204
+ state,
205
+ steps,
206
+ guards,
207
+ triggers,
208
+ sourceMap: Object.keys(sourceMap).length > 0 ? sourceMap : undefined,
209
+ };
210
+ return { success: true, ir, errors, warnings };
211
+ }
212
+ /**
213
+ * Generate a simple hash for content addressing
214
+ */
215
+ function generateHash(content) {
216
+ let hash = 0;
217
+ for (let i = 0; i < content.length; i++) {
218
+ const char = content.charCodeAt(i);
219
+ hash = (hash << 5) - hash + char;
220
+ hash = hash & hash;
221
+ }
222
+ return Math.abs(hash).toString(16).padStart(8, "0");
223
+ }
224
+ /**
225
+ * Infer parameter type from value
226
+ */
227
+ function inferType(value) {
228
+ if (typeof value === "boolean")
229
+ return "bool";
230
+ if (typeof value === "number")
231
+ return "number";
232
+ if (typeof value === "string") {
233
+ if (value.startsWith("0x") && value.length === 42)
234
+ return "address";
235
+ return "string";
236
+ }
237
+ return "string";
238
+ }
239
+ /**
240
+ * Transform trigger
241
+ */
242
+ function transformTrigger(raw) {
243
+ if ("manual" in raw && raw.manual) {
244
+ return { type: "manual" };
245
+ }
246
+ if ("schedule" in raw) {
247
+ return { type: "schedule", cron: raw.schedule };
248
+ }
249
+ if ("condition" in raw) {
250
+ return {
251
+ type: "condition",
252
+ expression: raw.condition,
253
+ pollInterval: raw.poll_interval ?? 60,
254
+ };
255
+ }
256
+ if ("event" in raw) {
257
+ return {
258
+ type: "event",
259
+ event: raw.event,
260
+ filter: raw.filter,
261
+ };
262
+ }
263
+ if ("any" in raw && Array.isArray(raw.any)) {
264
+ const triggers = [];
265
+ for (const t of raw.any) {
266
+ const transformed = transformTrigger(t);
267
+ if (transformed) {
268
+ triggers.push(transformed);
269
+ }
270
+ }
271
+ return { type: "any", triggers };
272
+ }
273
+ return null;
274
+ }
275
+ /**
276
+ * Transform a raw step object into a Step
277
+ */
278
+ function transformStep(raw, existingIds, errors) {
279
+ const id = raw.id;
280
+ if (!id) {
281
+ errors.push({ code: "MISSING_STEP_ID", message: "Step must have an 'id' field" });
282
+ return null;
283
+ }
284
+ if (existingIds.has(id)) {
285
+ errors.push({ code: "DUPLICATE_STEP_ID", message: `Duplicate step id '${id}'` });
286
+ return null;
287
+ }
288
+ // Compute step
289
+ if ("compute" in raw) {
290
+ const assignments = [];
291
+ const compute = raw.compute;
292
+ for (const [variable, exprValue] of Object.entries(compute)) {
293
+ try {
294
+ let expression;
295
+ if (typeof exprValue === "string") {
296
+ expression = parseExpression(exprValue);
297
+ }
298
+ else if (typeof exprValue === "number") {
299
+ expression = {
300
+ kind: "literal",
301
+ value: exprValue,
302
+ type: Number.isInteger(exprValue) ? "int" : "float",
303
+ };
304
+ }
305
+ else if (typeof exprValue === "boolean") {
306
+ expression = { kind: "literal", value: exprValue, type: "bool" };
307
+ }
308
+ else {
309
+ throw new Error(`Unsupported value type: ${typeof exprValue}`);
310
+ }
311
+ assignments.push({ variable, expression });
312
+ }
313
+ catch (e) {
314
+ errors.push({
315
+ code: "EXPRESSION_PARSE_ERROR",
316
+ message: `Failed to parse expression for '${variable}': ${e.message}`,
317
+ });
318
+ }
319
+ }
320
+ return {
321
+ kind: "compute",
322
+ id,
323
+ assignments,
324
+ dependsOn: [],
325
+ };
326
+ }
327
+ // Conditional step
328
+ if ("if" in raw && ("then" in raw || "action" in raw)) {
329
+ // Simple conditional with action
330
+ if ("action" in raw) {
331
+ const conditionStr = raw.if;
332
+ let condition;
333
+ try {
334
+ condition = parseExpression(conditionStr);
335
+ }
336
+ catch (e) {
337
+ errors.push({
338
+ code: "EXPRESSION_PARSE_ERROR",
339
+ message: `Failed to parse condition: ${e.message}`,
340
+ });
341
+ return null;
342
+ }
343
+ // Transform the action
344
+ const action = transformAction(raw.action, errors);
345
+ if (!action)
346
+ return null;
347
+ const constraints = transformConstraints(raw.constraints, errors);
348
+ const onFailure = raw.on_failure ?? "revert";
349
+ // Create action step with condition check
350
+ const actionStep = {
351
+ kind: "action",
352
+ id,
353
+ action,
354
+ constraints,
355
+ onFailure: onFailure,
356
+ dependsOn: [],
357
+ };
358
+ // Wrap in conditional
359
+ const _conditionalStep = {
360
+ kind: "conditional",
361
+ id: `${id}_cond`,
362
+ condition,
363
+ thenSteps: [id],
364
+ elseSteps: [],
365
+ dependsOn: [],
366
+ };
367
+ // Return the action step (conditional logic handled at execution)
368
+ // For now, store condition in dependsOn comment
369
+ return actionStep;
370
+ }
371
+ // Full conditional with then/else
372
+ const conditionStr = raw.if;
373
+ let condition;
374
+ try {
375
+ condition = parseExpression(conditionStr);
376
+ }
377
+ catch (e) {
378
+ errors.push({
379
+ code: "EXPRESSION_PARSE_ERROR",
380
+ message: `Failed to parse condition: ${e.message}`,
381
+ });
382
+ return null;
383
+ }
384
+ return {
385
+ kind: "conditional",
386
+ id,
387
+ condition,
388
+ thenSteps: raw.then ?? [],
389
+ elseSteps: raw.else ?? [],
390
+ dependsOn: [],
391
+ };
392
+ }
393
+ // Action step (without condition)
394
+ if ("action" in raw) {
395
+ const action = transformAction(raw.action, errors);
396
+ if (!action)
397
+ return null;
398
+ const constraints = transformConstraints(raw.constraints, errors);
399
+ const onFailure = raw.on_failure ?? "revert";
400
+ return {
401
+ kind: "action",
402
+ id,
403
+ action,
404
+ skill: raw.skill,
405
+ constraints,
406
+ outputBinding: raw.output,
407
+ onFailure: onFailure,
408
+ dependsOn: [],
409
+ };
410
+ }
411
+ // Loop step
412
+ if ("repeat" in raw || "for" in raw || "loop" in raw) {
413
+ return transformLoopStep(raw, id, errors);
414
+ }
415
+ // Try step (from atomic blocks or explicit try)
416
+ if ("try" in raw) {
417
+ return transformTryStep(raw, id);
418
+ }
419
+ // Wait step
420
+ if ("wait" in raw) {
421
+ return {
422
+ kind: "wait",
423
+ id,
424
+ duration: raw.wait,
425
+ dependsOn: [],
426
+ };
427
+ }
428
+ // Emit step
429
+ if ("emit" in raw) {
430
+ const emit = raw.emit;
431
+ const event = emit.event;
432
+ const dataRaw = emit.data;
433
+ const data = {};
434
+ for (const [key, exprStr] of Object.entries(dataRaw)) {
435
+ try {
436
+ data[key] = parseExpression(exprStr);
437
+ }
438
+ catch (e) {
439
+ errors.push({
440
+ code: "EXPRESSION_PARSE_ERROR",
441
+ message: `Failed to parse emit data '${key}': ${e.message}`,
442
+ });
443
+ }
444
+ }
445
+ return {
446
+ kind: "emit",
447
+ id,
448
+ event,
449
+ data,
450
+ dependsOn: [],
451
+ };
452
+ }
453
+ // Halt step (if explicitly marked)
454
+ if ("halt" in raw) {
455
+ return {
456
+ kind: "halt",
457
+ id,
458
+ reason: raw.halt,
459
+ dependsOn: [],
460
+ };
461
+ }
462
+ // Advisory step (AI consultation)
463
+ if ("advisory" in raw) {
464
+ const advisory = raw.advisory;
465
+ const prompt = advisory.prompt;
466
+ const advisor = advisory.advisor ?? "default";
467
+ const timeout = advisory.timeout ?? 30;
468
+ const fallbackValue = advisory.fallback ?? true;
469
+ const outputBinding = advisory.output ?? `${id}_result`;
470
+ const outputSchemaRaw = advisory.output_schema;
471
+ const outputSchema = outputSchemaRaw
472
+ ? parseOutputSchema(outputSchemaRaw)
473
+ : { type: "boolean" };
474
+ return {
475
+ kind: "advisory",
476
+ id,
477
+ advisor,
478
+ prompt,
479
+ outputSchema,
480
+ outputBinding,
481
+ timeout,
482
+ fallback: fallbackToExpression(fallbackValue, errors),
483
+ dependsOn: [],
484
+ };
485
+ }
486
+ // Parallel step
487
+ if ("parallel" in raw) {
488
+ const parallel = raw.parallel;
489
+ const branchesRaw = parallel.branches ?? [];
490
+ const branches = branchesRaw.map((b) => ({
491
+ id: `${id}_${b.name}`,
492
+ name: b.name,
493
+ steps: b.steps ?? [],
494
+ }));
495
+ const joinRaw = parallel.join;
496
+ let join = { type: "all" };
497
+ if (joinRaw?.type) {
498
+ const type = joinRaw.type;
499
+ if (type === "best") {
500
+ const metricStr = joinRaw.metric;
501
+ join = {
502
+ type: "best",
503
+ metric: metricStr ? parseExpression(metricStr) : parseExpression("0"),
504
+ order: joinRaw.order ?? "max",
505
+ };
506
+ }
507
+ else if (type === "any") {
508
+ join = { type: "any", count: joinRaw.count ?? 1 };
509
+ }
510
+ else if (type === "majority") {
511
+ join = { type: "majority" };
512
+ }
513
+ else if (type === "first") {
514
+ join = { type: "first" };
515
+ }
516
+ else {
517
+ join = { type: "all" };
518
+ }
519
+ }
520
+ return {
521
+ kind: "parallel",
522
+ id,
523
+ branches,
524
+ join,
525
+ onFail: parallel.on_fail ?? "abort",
526
+ timeout: parallel.timeout,
527
+ outputBinding: raw.output,
528
+ dependsOn: [],
529
+ };
530
+ }
531
+ // Pipeline step
532
+ if ("pipeline" in raw) {
533
+ const pipeline = raw.pipeline;
534
+ const sourceStr = pipeline.source;
535
+ const stagesRaw = pipeline.stages ?? [];
536
+ const stages = stagesRaw.map((stage) => {
537
+ const op = stage.op;
538
+ if (op === "reduce") {
539
+ return {
540
+ op: "reduce",
541
+ step: stage.step,
542
+ initial: parseExpression(String(stage.initial ?? "0")),
543
+ };
544
+ }
545
+ if (op === "take") {
546
+ return { op: "take", count: stage.count };
547
+ }
548
+ if (op === "skip") {
549
+ return { op: "skip", count: stage.count };
550
+ }
551
+ if (op === "sort") {
552
+ return {
553
+ op: "sort",
554
+ by: parseExpression(String(stage.by ?? "0")),
555
+ order: stage.order ?? "asc",
556
+ };
557
+ }
558
+ if (op === "where") {
559
+ return { op: "where", predicate: parseExpression(String(stage.predicate ?? "false")) };
560
+ }
561
+ return { op: op ?? "map", step: stage.step };
562
+ });
563
+ return {
564
+ kind: "pipeline",
565
+ id,
566
+ source: parseExpression(sourceStr),
567
+ stages,
568
+ parallel: pipeline.parallel ?? false,
569
+ outputBinding: raw.output,
570
+ dependsOn: [],
571
+ };
572
+ }
573
+ errors.push({ code: "UNKNOWN_STEP_TYPE", message: `Unknown step type for id '${id}'` });
574
+ return null;
575
+ }
576
+ /**
577
+ * Transform action object
578
+ */
579
+ function transformAction(raw, errors) {
580
+ const type = raw.type;
581
+ switch (type) {
582
+ case "swap":
583
+ return {
584
+ type: "swap",
585
+ venue: raw.venue,
586
+ assetIn: raw.asset_in,
587
+ assetOut: raw.asset_out,
588
+ amount: parseExpressionSafe(raw.amount, errors),
589
+ mode: raw.mode ?? "exact_in",
590
+ };
591
+ case "lend":
592
+ return {
593
+ type: "lend",
594
+ venue: raw.venue,
595
+ asset: raw.asset,
596
+ amount: raw.amount === "max" ? "max" : parseExpressionSafe(raw.amount, errors),
597
+ };
598
+ case "withdraw":
599
+ return {
600
+ type: "withdraw",
601
+ venue: raw.venue,
602
+ asset: raw.asset,
603
+ amount: raw.amount === "max" ? "max" : parseExpressionSafe(raw.amount, errors),
604
+ };
605
+ case "borrow":
606
+ return {
607
+ type: "borrow",
608
+ venue: raw.venue,
609
+ asset: raw.asset,
610
+ amount: parseExpressionSafe(raw.amount, errors),
611
+ collateral: raw.collateral,
612
+ };
613
+ case "repay":
614
+ return {
615
+ type: "repay",
616
+ venue: raw.venue,
617
+ asset: raw.asset,
618
+ amount: raw.amount === "max" ? "max" : parseExpressionSafe(raw.amount, errors),
619
+ };
620
+ case "stake":
621
+ return {
622
+ type: "stake",
623
+ venue: raw.venue,
624
+ asset: raw.asset,
625
+ amount: parseExpressionSafe(raw.amount, errors),
626
+ };
627
+ case "unstake":
628
+ return {
629
+ type: "unstake",
630
+ venue: raw.venue,
631
+ asset: raw.asset,
632
+ amount: raw.amount === "max" ? "max" : parseExpressionSafe(raw.amount, errors),
633
+ };
634
+ case "claim":
635
+ return {
636
+ type: "claim",
637
+ venue: raw.venue,
638
+ assets: raw.assets,
639
+ };
640
+ case "bridge": {
641
+ const toChainValue = raw.to_chain ?? raw.toChain;
642
+ if (toChainValue === undefined) {
643
+ errors.push({
644
+ code: "MISSING_BRIDGE_CHAIN",
645
+ message: "Bridge action requires to_chain",
646
+ });
647
+ return null;
648
+ }
649
+ return {
650
+ type: "bridge",
651
+ venue: raw.venue,
652
+ asset: raw.asset,
653
+ amount: parseExpressionSafe(raw.amount, errors),
654
+ toChain: parseExpressionSafe(toChainValue, errors),
655
+ };
656
+ }
657
+ case "transfer": {
658
+ const toValue = raw.to;
659
+ const isAddress = typeof toValue === "string" && toValue.startsWith("0x") && toValue.length === 42;
660
+ return {
661
+ type: "transfer",
662
+ asset: raw.asset,
663
+ amount: parseExpressionSafe(raw.amount, errors),
664
+ to: isAddress ? toValue : parseExpressionSafe(toValue, errors),
665
+ };
666
+ }
667
+ default:
668
+ errors.push({ code: "UNKNOWN_ACTION_TYPE", message: `Unknown action type '${type}'` });
669
+ return null;
670
+ }
671
+ }
672
+ /**
673
+ * Parse expression safely, returning a literal on failure
674
+ */
675
+ function parseExpressionSafe(input, errors) {
676
+ if (typeof input === "number") {
677
+ return { kind: "literal", value: input, type: "int" };
678
+ }
679
+ if (typeof input === "boolean") {
680
+ return { kind: "literal", value: input, type: "bool" };
681
+ }
682
+ try {
683
+ return parseExpression(input);
684
+ }
685
+ catch (e) {
686
+ errors.push({
687
+ code: "EXPRESSION_PARSE_ERROR",
688
+ message: `Failed to parse expression '${input}': ${e.message}`,
689
+ });
690
+ return { kind: "literal", value: 0, type: "int" };
691
+ }
692
+ }
693
+ function fallbackToExpression(input, errors) {
694
+ if (input && typeof input === "object") {
695
+ if ("__expr" in input) {
696
+ const exprValue = input.__expr;
697
+ try {
698
+ return parseExpression(String(exprValue));
699
+ }
700
+ catch (e) {
701
+ errors.push({
702
+ code: "EXPRESSION_PARSE_ERROR",
703
+ message: `Failed to parse fallback expression '${exprValue}': ${e.message}`,
704
+ });
705
+ return { kind: "literal", value: false, type: "bool" };
706
+ }
707
+ }
708
+ if ("__literal" in input) {
709
+ return literalFromValue(input.__literal);
710
+ }
711
+ return literalFromValue(input);
712
+ }
713
+ if (typeof input === "boolean") {
714
+ return { kind: "literal", value: input, type: "bool" };
715
+ }
716
+ if (typeof input === "number") {
717
+ return {
718
+ kind: "literal",
719
+ value: input,
720
+ type: Number.isInteger(input) ? "int" : "float",
721
+ };
722
+ }
723
+ if (typeof input === "bigint") {
724
+ return { kind: "literal", value: input, type: "int" };
725
+ }
726
+ if (typeof input === "string") {
727
+ return { kind: "literal", value: input, type: "string" };
728
+ }
729
+ try {
730
+ return parseExpression(String(input));
731
+ }
732
+ catch (e) {
733
+ errors.push({
734
+ code: "EXPRESSION_PARSE_ERROR",
735
+ message: `Failed to parse fallback expression '${String(input)}': ${e.message}`,
736
+ });
737
+ return { kind: "literal", value: false, type: "bool" };
738
+ }
739
+ }
740
+ function literalFromValue(input) {
741
+ if (typeof input === "boolean") {
742
+ return { kind: "literal", value: input, type: "bool" };
743
+ }
744
+ if (typeof input === "number") {
745
+ return {
746
+ kind: "literal",
747
+ value: input,
748
+ type: Number.isInteger(input) ? "int" : "float",
749
+ };
750
+ }
751
+ if (typeof input === "bigint") {
752
+ return { kind: "literal", value: input, type: "int" };
753
+ }
754
+ if (typeof input === "string") {
755
+ return { kind: "literal", value: input, type: "string" };
756
+ }
757
+ return { kind: "literal", value: input, type: "json" };
758
+ }
759
+ function parseOutputSchema(raw) {
760
+ const type = raw.type;
761
+ switch (type) {
762
+ case "enum":
763
+ return {
764
+ type: "enum",
765
+ values: raw.values ?? [],
766
+ };
767
+ case "number":
768
+ return {
769
+ type: "number",
770
+ min: raw.min,
771
+ max: raw.max,
772
+ };
773
+ case "string":
774
+ return {
775
+ type: "string",
776
+ minLength: raw.min_length ?? raw.minLength,
777
+ maxLength: raw.max_length ?? raw.maxLength,
778
+ pattern: raw.pattern,
779
+ };
780
+ case "object": {
781
+ const fieldsRaw = raw.fields;
782
+ const fields = fieldsRaw
783
+ ? Object.fromEntries(Object.entries(fieldsRaw).map(([key, value]) => [
784
+ key,
785
+ parseOutputSchema(value),
786
+ ]))
787
+ : undefined;
788
+ return { type: "object", fields };
789
+ }
790
+ case "array": {
791
+ const itemsRaw = raw.items;
792
+ return {
793
+ type: "array",
794
+ items: itemsRaw ? parseOutputSchema(itemsRaw) : undefined,
795
+ };
796
+ }
797
+ default:
798
+ return { type: "boolean" };
799
+ }
800
+ }
801
+ /**
802
+ * Transform constraints
803
+ */
804
+ function transformConstraints(raw, errors) {
805
+ if (!raw)
806
+ return {};
807
+ const minOutputRaw = (raw.min_output ?? raw.minOutput);
808
+ const maxInputRaw = (raw.max_input ?? raw.maxInput);
809
+ const minLiquidityRaw = (raw.min_liquidity ?? raw.minLiquidity);
810
+ const maxGasRaw = (raw.max_gas ?? raw.maxGas);
811
+ const maxPriceImpactRaw = (raw.max_price_impact ?? raw.maxPriceImpact);
812
+ const requireQuoteRaw = (raw.require_quote ?? raw.requireQuote);
813
+ const requireSimulationRaw = (raw.require_simulation ?? raw.requireSimulation);
814
+ return {
815
+ maxSlippageBps: raw.max_slippage,
816
+ maxPriceImpactBps: typeof maxPriceImpactRaw === "number"
817
+ ? maxPriceImpactRaw
818
+ : typeof maxPriceImpactRaw === "string"
819
+ ? Number.parseFloat(maxPriceImpactRaw)
820
+ : undefined,
821
+ deadline: raw.deadline,
822
+ minOutput: minOutputRaw !== undefined ? parseExpressionSafe(minOutputRaw, errors) : undefined,
823
+ maxInput: maxInputRaw !== undefined ? parseExpressionSafe(maxInputRaw, errors) : undefined,
824
+ minLiquidity: minLiquidityRaw !== undefined ? parseExpressionSafe(minLiquidityRaw, errors) : undefined,
825
+ maxGas: maxGasRaw !== undefined ? parseExpressionSafe(maxGasRaw, errors) : undefined,
826
+ requireQuote: requireQuoteRaw !== undefined ? parseExpressionSafe(requireQuoteRaw, errors) : undefined,
827
+ requireSimulation: requireSimulationRaw !== undefined
828
+ ? parseExpressionSafe(requireSimulationRaw, errors)
829
+ : undefined,
830
+ };
831
+ }
832
+ /**
833
+ * Transform loop step
834
+ */
835
+ function transformLoopStep(raw, id, errors) {
836
+ let loopType;
837
+ if ("repeat" in raw) {
838
+ loopType = { type: "repeat", count: raw.repeat };
839
+ }
840
+ else if ("for" in raw) {
841
+ const forClause = raw.for;
842
+ // Parse "var in expression"
843
+ const match = forClause.match(/^(\w+)\s+in\s+(.+)$/);
844
+ if (!match) {
845
+ errors.push({ code: "INVALID_FOR_CLAUSE", message: `Invalid for clause: ${forClause}` });
846
+ return null;
847
+ }
848
+ const [, variable, sourceStr] = match;
849
+ if (!variable || !sourceStr) {
850
+ errors.push({ code: "INVALID_FOR_CLAUSE", message: `Invalid for clause: ${forClause}` });
851
+ return null;
852
+ }
853
+ try {
854
+ const source = parseExpression(sourceStr);
855
+ loopType = { type: "for", variable, source };
856
+ }
857
+ catch (e) {
858
+ errors.push({
859
+ code: "EXPRESSION_PARSE_ERROR",
860
+ message: `Failed to parse for source: ${e.message}`,
861
+ });
862
+ return null;
863
+ }
864
+ }
865
+ else if ("loop" in raw) {
866
+ const loop = raw.loop;
867
+ try {
868
+ const condition = parseExpression(loop.until);
869
+ loopType = { type: "until", condition };
870
+ }
871
+ catch (e) {
872
+ errors.push({
873
+ code: "EXPRESSION_PARSE_ERROR",
874
+ message: `Failed to parse loop condition: ${e.message}`,
875
+ });
876
+ return null;
877
+ }
878
+ }
879
+ else {
880
+ return null;
881
+ }
882
+ const maxIterations = raw.max ?? raw.loop?.max ?? 100;
883
+ return {
884
+ kind: "loop",
885
+ id,
886
+ loopType,
887
+ bodySteps: raw.steps ?? [],
888
+ maxIterations,
889
+ parallel: raw.parallel,
890
+ outputBinding: raw.output,
891
+ dependsOn: [],
892
+ };
893
+ }
894
+ /**
895
+ * Transform guard
896
+ */
897
+ function transformGuard(raw, errors) {
898
+ if (!raw.id) {
899
+ errors.push({ code: "MISSING_GUARD_ID", message: "Guard must have an 'id' field" });
900
+ return null;
901
+ }
902
+ if (raw.advisory) {
903
+ // Advisory guard
904
+ return {
905
+ id: raw.id,
906
+ advisor: raw.advisory,
907
+ check: raw.check ?? "",
908
+ severity: raw.severity,
909
+ fallback: raw.fallback ?? true,
910
+ };
911
+ }
912
+ // Expression guard
913
+ if (!raw.check) {
914
+ errors.push({
915
+ code: "MISSING_GUARD_CHECK",
916
+ message: `Guard '${raw.id}' must have a 'check' field`,
917
+ });
918
+ return null;
919
+ }
920
+ try {
921
+ const check = parseExpression(raw.check);
922
+ return {
923
+ id: raw.id,
924
+ check,
925
+ severity: raw.severity,
926
+ message: raw.message ?? "",
927
+ };
928
+ }
929
+ catch (e) {
930
+ errors.push({
931
+ code: "EXPRESSION_PARSE_ERROR",
932
+ message: `Failed to parse guard check: ${e.message}`,
933
+ });
934
+ return null;
935
+ }
936
+ }
937
+ const VALID_ERROR_TYPES = new Set([
938
+ "slippage_exceeded",
939
+ "insufficient_liquidity",
940
+ "insufficient_balance",
941
+ "venue_unavailable",
942
+ "deadline_exceeded",
943
+ "simulation_failed",
944
+ "policy_violation",
945
+ "guard_failed",
946
+ "tx_reverted",
947
+ "gas_exceeded",
948
+ ]);
949
+ /**
950
+ * Transform a try step from SpellSource into TryStep IR.
951
+ *
952
+ * The transformer emits: { id, try: string[], catch: Array<{ error, action?, steps?, retry? }> }
953
+ * We convert to the TryStep type expected by the runtime.
954
+ */
955
+ function transformTryStep(raw, id) {
956
+ const trySteps = raw.try ?? [];
957
+ const rawCatch = raw.catch ?? [];
958
+ const finallySteps = raw.finally ?? undefined;
959
+ const catchBlocks = rawCatch.map((c) => {
960
+ const errorField = c.error ?? "*";
961
+ const errorType = errorField === "*"
962
+ ? "*"
963
+ : VALID_ERROR_TYPES.has(errorField)
964
+ ? errorField
965
+ : "*";
966
+ // Map "revert" → "rollback" to match CatchBlock action type
967
+ let action;
968
+ if (c.action) {
969
+ const rawAction = c.action;
970
+ action = rawAction === "revert" ? "rollback" : rawAction;
971
+ }
972
+ const block = { errorType };
973
+ if (action)
974
+ block.action = action;
975
+ if (c.steps)
976
+ block.steps = c.steps;
977
+ if (c.retry) {
978
+ const retry = c.retry;
979
+ block.retry = {
980
+ maxAttempts: retry.maxAttempts ?? 3,
981
+ backoff: retry.backoff?.backoff ?? "none",
982
+ backoffBase: retry.backoffBase,
983
+ maxBackoff: retry.maxBackoff,
984
+ };
985
+ }
986
+ return block;
987
+ });
988
+ return {
989
+ kind: "try",
990
+ id,
991
+ trySteps,
992
+ catchBlocks,
993
+ finallySteps: finallySteps && finallySteps.length > 0 ? finallySteps : undefined,
994
+ dependsOn: [],
995
+ };
996
+ }
997
+ //# sourceMappingURL=ir-generator.js.map