Skip to content

fix new dsl doesnt do native import #9

Description

@umutbasal

to ensure compatibility with the new DSL, we are removing the ESM import and callback approach for parsing JS bodies. These are essentially "fake" JS files without any package import capabilities, and certain syntax issues can lead to execution problems

Workflow Runtime: Old vs New Comparison

Old: ES Module Import

// loader.ts
const mod = await import(file);
return { meta: mod.meta, run: mod.default };
// workflow file
import { readFile } from "fs/promises";

export const meta = {
  name: "review",
  description: "Review files",
};

export default async function ({ agent, pipeline, parallel, phase, log, args }) {
  phase("Discover");
  const files = await agent("Find .ts files", { schema: { type: "array", items: { type: "string" } } });
  return { files };
}

New: Text Read + AsyncFunction with Globals

// loader.ts
const source = await readFile(file, "utf-8");
const meta = extractMeta(source);   // regex + new Function('return ({...})')()
const body = extractBody(source);    // strips export const meta block

// executor.ts
const fn = new AsyncFunction("agent", "pipeline", "parallel", "phase", "log", "args", body);
return fn(runtime.agent, runtime.pipeline, runtime.parallel, runtime.phase, runtime.log, args);
// workflow file
export const meta = {
  name: "review",
  description: "Review files",
};

phase("Discover");
const files = await agent("Find .ts files", { schema: { type: "array", items: { type: "string" } } });
return { files };

Comparison

Old (import()) New (AsyncFunction)
Workflow file format Requires export default async function wrapper Flat script — no wrapper needed
Static import statements Work natively Broken — must use await import() instead
export const meta Real ES export, parsed by engine Cosmetic — extracted via regex + eval
Source maps / stack traces Accurate line numbers Offset (meta block stripped)
Syntax error reporting V8/JSC-level, points to exact file + line Generic "error in anonymous function"
Module caching Bun/Node caches the module Fresh eval every execution
Relative imports Resolve correctly from workflow dir Not applicable (no static imports)
File discovery/reads One import() call does everything readFile + regex parse + new Function
Concurrent execution safety Fully isolated (each import is a module) Fully isolated (args passed to function)
Boilerplate for workflow authors ~2 lines (export default async function ({...}) { + }) Zero
Compatible with Claude-style workflows No — Claude outputs flat scripts with implicit globals Yes — direct match to the DSL
return at top level Inside function body (valid) Becomes return of AsyncFunction (valid)
Dynamic await import() Works Works
TypeScript workflow files Handled by Bun's loader Would need separate transpile step

Key Tradeoff

Old = robust module system, full JS/TS features, but requires the export default function ceremony.

New = zero-boilerplate flat scripts that match what Claude naturally generates, but sacrifices static imports and proper error reporting.

The target DSL (53 real-world workflows) uses zero static imports — all I/O is delegated to agent(). This makes the "no static imports" downside irrelevant for the intended use case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions