tjs-lang 0.8.1 → 0.8.3

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 (78) hide show
  1. package/CLAUDE.md +13 -4
  2. package/demo/autocomplete.test.ts +37 -0
  3. package/demo/docs.json +698 -8
  4. package/demo/examples.test.ts +6 -2
  5. package/demo/src/autocomplete.ts +40 -2
  6. package/demo/src/introspection-bridge.ts +140 -0
  7. package/demo/src/introspection-doc.test.ts +63 -0
  8. package/demo/src/playground-shared.ts +101 -16
  9. package/demo/src/playground-test-results.test.ts +112 -0
  10. package/demo/src/tjs-playground.ts +32 -5
  11. package/dist/examples/modules/dist/main.d.ts +34 -0
  12. package/dist/examples/modules/dist/math.d.ts +120 -0
  13. package/dist/index.js +115 -112
  14. package/dist/index.js.map +4 -4
  15. package/dist/src/lang/core.d.ts +1 -1
  16. package/dist/src/lang/dialect.d.ts +35 -0
  17. package/dist/src/lang/emitters/ast.d.ts +1 -1
  18. package/dist/src/lang/emitters/js-tests.d.ts +7 -0
  19. package/dist/src/lang/emitters/js.d.ts +12 -0
  20. package/dist/src/lang/index.d.ts +2 -1
  21. package/dist/src/lang/parser-types.d.ts +17 -0
  22. package/dist/src/lang/parser.d.ts +18 -0
  23. package/dist/src/lang/transpiler.d.ts +1 -0
  24. package/dist/src/lang/types.d.ts +18 -0
  25. package/dist/src/vm/runtime.d.ts +18 -0
  26. package/dist/src/vm/vm.d.ts +15 -1
  27. package/dist/tjs-batteries.js +2 -2
  28. package/dist/tjs-batteries.js.map +2 -2
  29. package/dist/tjs-eval.js +43 -43
  30. package/dist/tjs-eval.js.map +3 -3
  31. package/dist/tjs-from-ts.js +1 -1
  32. package/dist/tjs-from-ts.js.map +2 -2
  33. package/dist/tjs-lang.js +76 -73
  34. package/dist/tjs-lang.js.map +4 -4
  35. package/dist/tjs-vm.js +49 -49
  36. package/dist/tjs-vm.js.map +3 -3
  37. package/editors/codemirror/ajs-language.ts +130 -44
  38. package/editors/codemirror/completion-source.test.ts +114 -0
  39. package/editors/introspect-value.test.ts +61 -0
  40. package/editors/introspect-value.ts +86 -0
  41. package/editors/scope-symbols.test.ts +113 -0
  42. package/editors/scope-symbols.ts +173 -0
  43. package/llms.txt +1 -0
  44. package/package.json +21 -1
  45. package/src/batteries/audit.ts +3 -2
  46. package/src/cli/commands/check.ts +3 -2
  47. package/src/cli/commands/emit.ts +4 -2
  48. package/src/cli/commands/run.ts +6 -2
  49. package/src/cli/commands/types.ts +2 -2
  50. package/src/lang/codegen.test.ts +4 -1
  51. package/src/lang/core.ts +6 -4
  52. package/src/lang/dialect.test.ts +63 -0
  53. package/src/lang/dialect.ts +50 -0
  54. package/src/lang/emitters/ast.ts +145 -2
  55. package/src/lang/emitters/js-tests.ts +46 -37
  56. package/src/lang/emitters/js.ts +19 -2
  57. package/src/lang/features.test.ts +6 -5
  58. package/src/lang/index.ts +40 -5
  59. package/src/lang/parser-types.ts +17 -0
  60. package/src/lang/parser.test.ts +12 -6
  61. package/src/lang/parser.ts +113 -3
  62. package/src/lang/predicate-schema.test.ts +97 -0
  63. package/src/lang/predicate-schema.ts +168 -0
  64. package/src/lang/predicate.test.ts +184 -0
  65. package/src/lang/predicate.ts +550 -0
  66. package/src/lang/subset-invariant.test.ts +90 -0
  67. package/src/lang/suggest.test.ts +84 -0
  68. package/src/lang/transpiler.ts +34 -0
  69. package/src/lang/types.ts +18 -0
  70. package/src/lang/wasm.test.ts +8 -2
  71. package/src/linalg/linalg.test.ts +8 -2
  72. package/src/use-cases/batteries.test.ts +4 -0
  73. package/src/use-cases/local-helpers.test.ts +219 -0
  74. package/src/use-cases/timeout-overrides.test.ts +169 -0
  75. package/src/vm/atom-effects.test.ts +72 -0
  76. package/src/vm/atoms/batteries.ts +29 -3
  77. package/src/vm/runtime.ts +140 -4
  78. package/src/vm/vm.ts +47 -9
@@ -51,7 +51,12 @@ export const storeVectorize = defineAtom(
51
51
  const resolvedText = resolveValue(text, ctx)
52
52
  return vectorCap.embed(resolvedText)
53
53
  },
54
- { docs: 'Generate embeddings using vector battery', cost: 20 }
54
+ // Network embedding call; a cold model can exceed the 1s atom default.
55
+ {
56
+ docs: 'Generate embeddings using vector battery',
57
+ cost: 20,
58
+ timeoutMs: 60000,
59
+ }
55
60
  )
56
61
 
57
62
  // store.createCollection
@@ -97,7 +102,12 @@ export const storeVectorAdd = defineAtom(
97
102
 
98
103
  return storeCap.vectorAdd(resolvedColl, resolvedDoc)
99
104
  },
100
- { docs: 'Add a document to a vector store collection', cost: 5 }
105
+ // May embed the doc via the store (network IO); allow for a cold model.
106
+ {
107
+ docs: 'Add a document to a vector store collection',
108
+ cost: 5,
109
+ timeoutMs: 60000,
110
+ }
101
111
  )
102
112
 
103
113
  // store.search (Vector Search)
@@ -167,7 +177,11 @@ export const llmPredictBattery = defineAtom(
167
177
  resolvedFormat
168
178
  )
169
179
  },
170
- { docs: 'Generate completion using LLM battery', cost: 100 }
180
+ {
181
+ docs: 'Generate completion using LLM battery',
182
+ cost: 100,
183
+ timeoutMs: 120000,
184
+ }
171
185
  )
172
186
 
173
187
  // Vision battery interface (multimodal)
@@ -215,3 +229,15 @@ export const llmVision = defineAtom(
215
229
  },
216
230
  { docs: 'Analyze images using a vision model', timeoutMs: 120000, cost: 150 }
217
231
  )
232
+
233
+ // Every battery atom is IO (embedding/LLM/vector-store network calls).
234
+ for (const atom of [
235
+ storeVectorize,
236
+ storeCreateCollection,
237
+ storeVectorAdd,
238
+ storeSearch,
239
+ llmPredictBattery,
240
+ llmVision,
241
+ ]) {
242
+ atom.effects = 'io'
243
+ }
package/src/vm/runtime.ts CHANGED
@@ -151,6 +151,11 @@ export type CostOverride =
151
151
  | number
152
152
  | ((input: any, ctx: RuntimeContext) => number)
153
153
 
154
+ /** Timeout override: static number (ms) or dynamic function. 0 disables. */
155
+ export type TimeoutOverride =
156
+ | number
157
+ | ((input: any, ctx: RuntimeContext) => number)
158
+
154
159
  export interface RuntimeContext {
155
160
  fuel: { current: number }
156
161
  args: Record<string, any>
@@ -165,12 +170,28 @@ export interface RuntimeContext {
165
170
  warnings?: string[] // Non-fatal warnings (e.g., console.warn)
166
171
  signal?: AbortSignal // External abort signal for timeout enforcement
167
172
  costOverrides?: Record<string, CostOverride> // Per-atom cost overrides
173
+ timeoutOverrides?: Record<string, TimeoutOverride> // Per-atom timeout overrides (ms, 0 disables)
168
174
  context?: Record<string, any> // Immutable request-scoped metadata (auth, permissions, etc.)
169
175
  runCodeDepth?: number // Track nested runCode calls to prevent infinite recursion
176
+ localCall?: boolean // Inside a callLocal helper body — return may be a non-object scalar
177
+ helpers?: Record<string, { steps: any[]; paramNames: string[] }> // Local helper bodies, called by name
178
+ callDepth?: number // Helper call nesting depth — guards against host-stack overflow on deep recursion
170
179
  }
171
180
 
172
181
  export type AtomExec = (step: any, ctx: RuntimeContext) => Promise<void>
173
182
 
183
+ /**
184
+ * Effect classification of an atom.
185
+ * - `'pure'`: deterministic, no IO, no capability access, no observable side
186
+ * effects — safe inside a synchronous predicate (see the predicate verifier).
187
+ * - `'io'`: touches `ctx.capabilities` (fetch/store/llm/agent/code), or is
188
+ * nondeterministic (random/uuid), or has side effects (console). Not allowed
189
+ * in a predicate.
190
+ * Defaults to `'pure'`; effectful atoms must opt into `'io'`. The invariant
191
+ * "anything touching ctx.capabilities is tagged 'io'" is guarded by a test.
192
+ */
193
+ export type AtomEffects = 'pure' | 'io'
194
+
174
195
  export interface AtomDef {
175
196
  op: OpCode
176
197
  inputSchema: any
@@ -179,6 +200,7 @@ export interface AtomDef {
179
200
  docs?: string
180
201
  timeoutMs?: number
181
202
  cost?: number | ((input: any, ctx: RuntimeContext) => number)
203
+ effects?: AtomEffects
182
204
  }
183
205
 
184
206
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -190,6 +212,8 @@ export interface AtomOptions {
190
212
  docs?: string
191
213
  timeoutMs?: number
192
214
  cost?: number | ((input: any, ctx: RuntimeContext) => number)
215
+ /** Effect class — defaults to `'pure'`; effectful atoms set `'io'`. */
216
+ effects?: AtomEffects
193
217
  }
194
218
 
195
219
  export interface RunResult {
@@ -1338,6 +1362,7 @@ export function defineAtom<I extends Record<string, any>, O = any>(
1338
1362
  docs = '',
1339
1363
  timeoutMs = 1000,
1340
1364
  cost = 1,
1365
+ effects = 'pure',
1341
1366
  } = typeof options === 'string' ? { docs: options } : options
1342
1367
 
1343
1368
  const exec: AtomExec = async (step: any, ctx: RuntimeContext) => {
@@ -1363,18 +1388,25 @@ export function defineAtom<I extends Record<string, any>, O = any>(
1363
1388
  return
1364
1389
  }
1365
1390
 
1366
- // 3. Execution with Timeout
1391
+ // 3. Execution with Timeout (per-atom override > atom default)
1392
+ const overrideTimeout = ctx.timeoutOverrides?.[op]
1393
+ const baseTimeout =
1394
+ overrideTimeout !== undefined ? overrideTimeout : timeoutMs
1395
+ const effectiveTimeout =
1396
+ typeof baseTimeout === 'function'
1397
+ ? baseTimeout(inputData, ctx)
1398
+ : baseTimeout
1367
1399
  let timer: any
1368
1400
  const execute = async () => fn(step as I, ctx)
1369
1401
 
1370
1402
  result =
1371
- timeoutMs > 0
1403
+ effectiveTimeout > 0
1372
1404
  ? await Promise.race([
1373
1405
  execute(),
1374
1406
  new Promise<never>((_, reject) => {
1375
1407
  timer = setTimeout(
1376
1408
  () => reject(new Error(`Atom '${op}' timed out`)),
1377
- timeoutMs
1409
+ effectiveTimeout
1378
1410
  )
1379
1411
  }),
1380
1412
  ]).finally(() => clearTimeout(timer))
@@ -1430,6 +1462,7 @@ export function defineAtom<I extends Record<string, any>, O = any>(
1430
1462
  docs,
1431
1463
  timeoutMs,
1432
1464
  cost,
1465
+ effects,
1433
1466
  create: (input: I) => ({ op, ...input }),
1434
1467
  }
1435
1468
  }
@@ -1562,8 +1595,11 @@ export const ret = defineAtom(
1562
1595
  if ('value' in step) {
1563
1596
  const res = resolveValue(step.value, ctx)
1564
1597
 
1565
- // Enforce object returns — agents must return objects for composability
1598
+ // Enforce object returns — agents must return objects for composability.
1599
+ // Helper bodies (callLocal) are exempt: they're internal calls and may
1600
+ // return scalars, arrays, etc. like ordinary functions.
1566
1601
  if (
1602
+ !ctx.localCall &&
1567
1603
  res !== undefined &&
1568
1604
  res !== null &&
1569
1605
  !isAgentError(res) &&
@@ -1786,6 +1822,71 @@ export const scope = defineAtom(
1786
1822
  { docs: 'Create new scope', timeoutMs: 0, cost: 0.1 }
1787
1823
  )
1788
1824
 
1825
+ /**
1826
+ * Maximum helper-call nesting depth. Fuel + timeout bound the total *work* a
1827
+ * recursive helper can do, but deeply-nested `await seq.exec` would overflow
1828
+ * the host JS stack (an uncatchable RangeError) before fuel runs out. This cap
1829
+ * converts runaway recursion into a clean monadic error instead.
1830
+ */
1831
+ export const MAX_CALL_DEPTH = 256
1832
+
1833
+ export const callLocal = defineAtom(
1834
+ 'callLocal',
1835
+ s.object({
1836
+ name: s.string,
1837
+ args: s.array(s.any),
1838
+ }),
1839
+ undefined,
1840
+ async ({ name, args }, ctx) => {
1841
+ const helper = ctx.helpers?.[name as string]
1842
+ if (!helper) {
1843
+ ctx.error = new AgentError(`Unknown helper: ${name}`, 'callLocal')
1844
+ return ctx.error
1845
+ }
1846
+
1847
+ const depth = (ctx.callDepth ?? 0) + 1
1848
+ if (depth > MAX_CALL_DEPTH) {
1849
+ ctx.error = new AgentError(
1850
+ `Maximum helper call depth (${MAX_CALL_DEPTH}) exceeded — likely infinite recursion in '${name}'`,
1851
+ 'callLocal'
1852
+ )
1853
+ return ctx.error
1854
+ }
1855
+
1856
+ // Resolve each argument expression in the caller's scope
1857
+ const resolvedArgs = (args as any[]).map((arg) => resolveValue(arg, ctx))
1858
+
1859
+ // Isolated scope: helpers are top-level sibling functions, not nested
1860
+ // closures, so they see ONLY their params — never the caller's locals.
1861
+ // Capabilities/fuel/resolver/etc. are shared via the spread; state and
1862
+ // consts start fresh. localCall exempts the helper's `return` from the
1863
+ // agent object-return contract (helpers may return scalars/arrays like
1864
+ // ordinary functions). callDepth guards against host-stack overflow.
1865
+ const scopedCtx: RuntimeContext = {
1866
+ ...ctx,
1867
+ state: {},
1868
+ consts: new Set(),
1869
+ output: undefined,
1870
+ error: undefined,
1871
+ localCall: true,
1872
+ callDepth: depth,
1873
+ }
1874
+ for (let i = 0; i < helper.paramNames.length; i++) {
1875
+ scopedCtx.state[helper.paramNames[i]] = resolvedArgs[i]
1876
+ }
1877
+
1878
+ await seq.exec({ op: 'seq', steps: helper.steps } as any, scopedCtx)
1879
+
1880
+ // Propagate errors but NOT output — the helper's return becomes this
1881
+ // atom's result, captured into the caller's named result variable by
1882
+ // the standard exec wrapper. Unlike `scope`, it does not bubble up
1883
+ // to ctx.output (so a helper return doesn't exit the caller agent).
1884
+ if (scopedCtx.error) ctx.error = scopedCtx.error
1885
+ return scopedCtx.output
1886
+ },
1887
+ { docs: 'Invoke a local helper function by name', timeoutMs: 0, cost: 0.1 }
1888
+ )
1889
+
1789
1890
  // 3. List (Cost 1)
1790
1891
 
1791
1892
  /*#
@@ -2991,6 +3092,7 @@ export const coreAtoms = {
2991
3092
  varsLet,
2992
3093
  varsExport,
2993
3094
  scope,
3095
+ callLocal,
2994
3096
  map,
2995
3097
  filter,
2996
3098
  reduce,
@@ -3029,3 +3131,37 @@ export const coreAtoms = {
3029
3131
  releaseProcedure,
3030
3132
  clearExpiredProcedures,
3031
3133
  }
3134
+
3135
+ /**
3136
+ * Effectful core atoms — anything that touches `ctx.capabilities`
3137
+ * (fetch/store/llm/agent/code), is nondeterministic (random/uuid), or has
3138
+ * observable side effects (console). Tagged centrally so the list reads as one
3139
+ * audit surface; a test asserts every capability-touching atom is in here.
3140
+ * Everything else defaults to `effects: 'pure'`.
3141
+ */
3142
+ export const EFFECTFUL_CORE_OPS = [
3143
+ 'httpFetch',
3144
+ 'storeGet',
3145
+ 'storeSet',
3146
+ 'storeQuery',
3147
+ 'storeVectorSearch',
3148
+ 'llmPredict',
3149
+ 'agentRun',
3150
+ 'transpileCode',
3151
+ 'runCode',
3152
+ 'random',
3153
+ 'uuid',
3154
+ 'consoleLog',
3155
+ 'consoleWarn',
3156
+ 'consoleError',
3157
+ 'storeProcedure',
3158
+ 'releaseProcedure',
3159
+ 'clearExpiredProcedures',
3160
+ 'cache',
3161
+ 'memoize',
3162
+ ] as const
3163
+
3164
+ for (const op of EFFECTFUL_CORE_OPS) {
3165
+ const atom = (coreAtoms as Record<string, AtomDef>)[op]
3166
+ if (atom) atom.effects = 'io'
3167
+ }
package/src/vm/vm.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  type RunResult,
5
5
  type RuntimeContext,
6
6
  type CostOverride,
7
+ type TimeoutOverride,
7
8
  coreAtoms,
8
9
  AgentError,
9
10
  isProcedureToken,
@@ -13,16 +14,49 @@ import { TypedBuilder, type BaseNode, type BuilderType } from '../builder'
13
14
  import { validate } from 'tosijs-schema'
14
15
  import { transpile } from '../lang/core'
15
16
 
16
- /** Default timeout multiplier: milliseconds per fuel unit */
17
- const FUEL_TO_MS = 10 // 1000 fuel = 10 seconds
17
+ /**
18
+ * Floor for the run-level default timeout. The actual default is derived from
19
+ * the registered atoms (slowest atom × 2 — see `defaultRunTimeout`), but never
20
+ * drops below this for a VM whose atoms are all fast.
21
+ */
22
+ const MIN_DEFAULT_RUN_TIMEOUT_MS = 60_000
18
23
 
19
24
  export class AgentVM<M extends Record<string, Atom<any, any>>> {
20
25
  readonly atoms: typeof coreAtoms & M
21
26
 
27
+ private _defaultRunTimeout?: number
28
+
22
29
  constructor(customAtoms: M = {} as M) {
23
30
  this.atoms = { ...coreAtoms, ...customAtoms }
24
31
  }
25
32
 
33
+ /**
34
+ * Default run-level wall-clock timeout when `run()` is given no explicit
35
+ * `timeoutMs`. Derived as `max(per-atom timeoutMs) × 2` over the registered
36
+ * atoms (with headroom for an agent that chains a couple of slow calls), so
37
+ * the run-level backstop can never be shorter than the slowest single atom's
38
+ * own budget — otherwise that per-atom budget would be dead config (e.g.
39
+ * `llmVision`/`llmPredictBattery` are 120s; a fixed 60s run default would kill
40
+ * them mid-call). Atoms with `timeoutMs: 0` (no timeout, e.g. `seq`) are
41
+ * excluded; the result is floored at {@link MIN_DEFAULT_RUN_TIMEOUT_MS}.
42
+ * Self-adjusting: registering a slower custom atom raises the default.
43
+ */
44
+ get defaultRunTimeout(): number {
45
+ if (this._defaultRunTimeout === undefined) {
46
+ let slowest = 0
47
+ for (const atom of Object.values(this.atoms)) {
48
+ // undefined timeoutMs means the per-atom default (1000ms); 0 means none.
49
+ const t = (atom as any).timeoutMs ?? 1000
50
+ if (t > 0 && t > slowest) slowest = t
51
+ }
52
+ this._defaultRunTimeout = Math.max(
53
+ MIN_DEFAULT_RUN_TIMEOUT_MS,
54
+ slowest * 2
55
+ )
56
+ }
57
+ return this._defaultRunTimeout
58
+ }
59
+
26
60
  get builder(): BuilderType<typeof coreAtoms & M> {
27
61
  return new TypedBuilder(this.atoms) as any
28
62
  }
@@ -77,9 +111,10 @@ export class AgentVM<M extends Record<string, Atom<any, any>>> {
77
111
  fuel?: number
78
112
  capabilities?: Capabilities
79
113
  trace?: boolean
80
- timeoutMs?: number // Override automatic timeout (fuel * FUEL_TO_MS)
114
+ timeoutMs?: number // Wall-clock cap on the whole run (default: slowest atom × 2, min 60s — see defaultRunTimeout)
81
115
  signal?: AbortSignal // External abort signal (e.g., from caller)
82
116
  costOverrides?: Record<string, CostOverride> // Per-atom fuel cost overrides
117
+ timeoutOverrides?: Record<string, TimeoutOverride> // Per-atom timeout overrides (ms, 0 disables)
83
118
  context?: Record<string, any> // Request-scoped metadata (auth, permissions, etc.)
84
119
  } = {}
85
120
  ): Promise<RunResult> {
@@ -105,9 +140,10 @@ export class AgentVM<M extends Record<string, Atom<any, any>>> {
105
140
 
106
141
  const startFuel = options.fuel ?? 1000
107
142
 
108
- // Calculate timeout from fuel budget (generous: 10ms per fuel unit)
109
- // Can be overridden with explicit timeoutMs option
110
- const timeoutMs = options.timeoutMs ?? startFuel * FUEL_TO_MS
143
+ // Run-level wall-clock timeout. Agents are typically IO-bound; the default
144
+ // is derived from the registered atoms (slowest × 2) so it always covers the
145
+ // slowest atom's own budget. See `defaultRunTimeout`.
146
+ const timeoutMs = options.timeoutMs ?? this.defaultRunTimeout
111
147
 
112
148
  // Default Capabilities
113
149
  const capabilities = options.capabilities ?? {}
@@ -160,8 +196,10 @@ export class AgentVM<M extends Record<string, Atom<any, any>>> {
160
196
  output: undefined,
161
197
  signal: controller.signal,
162
198
  costOverrides: options.costOverrides,
199
+ timeoutOverrides: options.timeoutOverrides,
163
200
  context: options.context,
164
201
  warnings, // Shared warnings array
202
+ helpers: (ast as any).helpers, // Local helper bodies, called by name via callLocal
165
203
  }
166
204
 
167
205
  if (options.trace) {
@@ -197,7 +235,7 @@ export class AgentVM<M extends Record<string, Atom<any, any>>> {
197
235
  controller.signal.addEventListener('abort', () => {
198
236
  reject(
199
237
  new Error(
200
- `Execution timeout after ${timeoutMs}ms (fuel: ${startFuel}). Consider increasing fuel or optimizing your agent.`
238
+ `Execution timeout after ${timeoutMs}ms. Pass a higher \`timeoutMs\` to vm.run() or set per-atom \`timeoutOverrides\` for slow IO atoms.`
201
239
  )
202
240
  )
203
241
  })
@@ -205,7 +243,7 @@ export class AgentVM<M extends Record<string, Atom<any, any>>> {
205
243
  if (controller.signal.aborted) {
206
244
  reject(
207
245
  new Error(
208
- `Execution timeout after ${timeoutMs}ms (fuel: ${startFuel}). Consider increasing fuel or optimizing your agent.`
246
+ `Execution timeout after ${timeoutMs}ms. Pass a higher \`timeoutMs\` to vm.run() or set per-atom \`timeoutOverrides\` for slow IO atoms.`
209
247
  )
210
248
  )
211
249
  }
@@ -219,7 +257,7 @@ export class AgentVM<M extends Record<string, Atom<any, any>>> {
219
257
  controller.signal.aborted
220
258
  ) {
221
259
  ctx.error = new AgentError(
222
- `Execution timeout after ${timeoutMs}ms (fuel: ${startFuel}). Consider increasing fuel or optimizing your agent.`,
260
+ `Execution timeout after ${timeoutMs}ms. Pass a higher \`timeoutMs\` to vm.run() or set per-atom \`timeoutOverrides\` for slow IO atoms.`,
223
261
  'vm.run'
224
262
  )
225
263
  } else {