@agentproto/workflow-loader 0.2.5 → 0.2.6

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.
package/dist/index.mjs CHANGED
@@ -1,15 +1,73 @@
1
- import { readFile, stat } from 'fs/promises';
1
+ import { readFile, stat, readdir } from 'fs/promises';
2
2
  import { createHash } from 'crypto';
3
3
  import { isAbsolute, join, dirname } from 'path';
4
4
  import { pathToFileURL } from 'url';
5
5
  import { parseWorkflowManifest, workflowFromManifest } from '@agentproto/workflow/manifest';
6
6
  import { assertKnownStepRefs } from '@agentproto/workflow';
7
+ import { register } from 'module';
7
8
 
8
9
  /**
9
10
  * @agentproto/workflow-loader v0.1.0-alpha
10
11
  * Disk loader + entry reconciliation for AIP-15 WORKFLOW.md.
11
12
  */
12
13
 
14
+ var ENTRY_GRAPH_VERSION_PARAM = "agentproto_v";
15
+ var ENTRY_GRAPH_HOOK_SOURCE = `
16
+ const PARAM = ${JSON.stringify(ENTRY_GRAPH_VERSION_PARAM)}
17
+ export async function resolve(specifier, context, nextResolve) {
18
+ const result = await nextResolve(specifier, context)
19
+ const relative = specifier.startsWith("./") || specifier.startsWith("../")
20
+ if (!relative || !context.parentURL || !result.url.startsWith("file:") || result.url.includes("/node_modules/")) return result
21
+ let version
22
+ try {
23
+ version = new URL(context.parentURL).searchParams.get(PARAM)
24
+ } catch {
25
+ return result
26
+ }
27
+ if (version === null) return result
28
+ const url = new URL(result.url)
29
+ url.searchParams.set(PARAM, version)
30
+ return { ...result, url: url.href }
31
+ }
32
+ `;
33
+ var SCRIPT_EXT = /\.(?:mjs|js|cjs|json)$/;
34
+ var MAX_DEPTH = 3;
35
+ async function newestMtimeMs(dir, depth) {
36
+ let newest = 0;
37
+ let entries;
38
+ try {
39
+ entries = await readdir(dir, { withFileTypes: true });
40
+ } catch {
41
+ return 0;
42
+ }
43
+ for (const e of entries) {
44
+ if (e.name === "node_modules" || e.name.startsWith(".")) continue;
45
+ const p = join(dir, e.name);
46
+ if (e.isDirectory()) {
47
+ if (depth < MAX_DEPTH) newest = Math.max(newest, await newestMtimeMs(p, depth + 1));
48
+ } else if (SCRIPT_EXT.test(e.name)) {
49
+ try {
50
+ newest = Math.max(newest, (await stat(p)).mtimeMs);
51
+ } catch {
52
+ }
53
+ }
54
+ }
55
+ return newest;
56
+ }
57
+ function entryGraphVersion(entryDir) {
58
+ return newestMtimeMs(entryDir, 0);
59
+ }
60
+ var hookRegistered = false;
61
+ function registerEntryGraphHook() {
62
+ if (hookRegistered) return true;
63
+ try {
64
+ register(`data:text/javascript,${encodeURIComponent(ENTRY_GRAPH_HOOK_SOURCE)}`);
65
+ hookRegistered = true;
66
+ } catch {
67
+ return false;
68
+ }
69
+ return true;
70
+ }
13
71
 
14
72
  // src/reconcile.ts
15
73
  var WorkflowReconcileError = class extends Error {
@@ -183,9 +241,13 @@ async function importEntryHandle(workflowMdPath, entry) {
183
241
  let href = pathToFileURL(abs).href;
184
242
  if (!process.env.VITEST) {
185
243
  try {
186
- const url = pathToFileURL(abs);
187
- url.searchParams.set("v", String((await stat(abs)).mtimeMs));
188
- href = url.href;
244
+ const version = await entryGraphVersion(dirname(abs));
245
+ if (version > 0) {
246
+ const url = pathToFileURL(abs);
247
+ url.searchParams.set(ENTRY_GRAPH_VERSION_PARAM, String(version));
248
+ registerEntryGraphHook();
249
+ href = url.href;
250
+ }
189
251
  } catch {
190
252
  }
191
253
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reconcile.ts","../src/load-workflow.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Reconcile a WORKFLOW.md manifest against the handle its `entry` module\n * exports. An entry-backed workflow declares its step graph twice — once as\n * governance-facing manifest data, once as the runnable handle — so the two\n * MUST agree, or the manifest is lying about what actually runs. This checks\n * the workflow id and the top-level step (id, kind) sequence; nested step\n * lists (map / loop / parallel bodies) are the entry's concern.\n */\n\nimport type { WorkflowManifest } from \"@agentproto/workflow/manifest\"\nimport type { WorkflowHandle } from \"@agentproto/workflow\"\n\nexport class WorkflowReconcileError extends Error {\n constructor(message: string) {\n super(`reconcileEntry: ${message}`)\n this.name = \"WorkflowReconcileError\"\n }\n}\n\ninterface IdKind {\n readonly id: string\n readonly kind: string\n}\n\nconst idKind = (steps: readonly IdKind[]): IdKind[] =>\n steps.map((s) => ({ id: s.id, kind: s.kind }))\n\nconst summary = (steps: readonly IdKind[]): string =>\n steps.map((s) => `${s.id}:${s.kind}`).join(\" → \") || \"(none)\"\n\nexport function reconcileEntry(\n manifest: WorkflowManifest,\n handle: WorkflowHandle,\n): void {\n const fm = manifest.frontmatter\n if (fm.id !== handle.id) {\n throw new WorkflowReconcileError(\n `manifest id '${fm.id}' ≠ entry id '${handle.id}'.`,\n )\n }\n const declared = idKind(fm.steps as readonly IdKind[])\n const actual = idKind(handle.steps as readonly IdKind[])\n if (declared.length !== actual.length) {\n throw new WorkflowReconcileError(\n `step count differs — manifest declares ${declared.length} [${summary(declared)}], ` +\n `entry has ${actual.length} [${summary(actual)}].`,\n )\n }\n for (let i = 0; i < declared.length; i++) {\n const d = declared[i]!\n const a = actual[i]!\n if (d.id !== a.id || d.kind !== a.kind) {\n throw new WorkflowReconcileError(\n `step ${i} differs — manifest '${d.id}:${d.kind}' ≠ entry '${a.id}:${a.kind}'. ` +\n `manifest [${summary(declared)}] vs entry [${summary(actual)}].`,\n )\n }\n }\n}\n","/**\n * loadWorkflowHandle — read a WORKFLOW.md off disk into a WorkflowHandle.\n *\n * The pure packages stop at the page boundary: `@agentproto/workflow` parses a\n * manifest *string*, `@agentproto/workflow-runtime` compiles an in-memory\n * handle. Neither touches the filesystem. This is the host-side seam that does:\n *\n * read file → parse manifest → (if `entry`) import the module + reconcile\n *\n * With no `entry`, the manifest IS the workflow (purely-declarative path). With\n * an `entry`, its default-exported handle is the source of truth for runtime\n * step logic and the manifest is reconciled against it — the two MUST agree.\n *\n * The entry import mirrors the CLI's adapter loader: resolve relative to the\n * manifest's directory, import via a `file://` URL so an absolute path works\n * without an `exports` declaration.\n */\n\nimport { readFile, stat } from \"node:fs/promises\"\nimport { createHash } from \"node:crypto\"\nimport { dirname, isAbsolute, join } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\n\nimport {\n parseWorkflowManifest,\n workflowFromManifest,\n type WorkflowManifest,\n} from \"@agentproto/workflow/manifest\"\nimport type { WorkflowHandle } from \"@agentproto/workflow\"\nimport { assertKnownStepRefs } from \"@agentproto/workflow\"\n\nimport { reconcileEntry } from \"./reconcile.js\"\n\nexport class WorkflowLoadError extends Error {\n constructor(message: string) {\n super(`loadWorkflow: ${message}`)\n this.name = \"WorkflowLoadError\"\n }\n}\n\n/** A handle is a plain object carrying an `id` and a `steps` array. */\nfunction isWorkflowHandle(v: unknown): v is WorkflowHandle {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as { id?: unknown }).id === \"string\" &&\n Array.isArray((v as { steps?: unknown }).steps)\n )\n}\n\n/**\n * Translate a declarative `subworkflow` step's `with:` block into the `inputs`\n * projection the runtime compiler threads into `step.input(b)` (AIP-16 ref\n * grammar: `$input.*`, `$steps.<id>.*`, literals — resolved against the\n * PARENT's bindings). Without this, a manifest `with:` is dead documentation:\n * the child receives the parent's raw input verbatim. Steps without `with:`\n * are left untouched. Nested step lists (map / loop / parallel bodies and\n * branch arms) are walked too.\n */\nfunction translateSubworkflowWith(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n steps: unknown,\n knownStepIds: ReadonlySet<string>,\n): void {\n if (!Array.isArray(steps)) return\n for (const step of steps) {\n if (step === null || typeof step !== \"object\") continue\n const s = step as Record<string, unknown>\n if (s.kind === \"subworkflow\" && s.with !== undefined) {\n const id = typeof s.id === \"string\" ? s.id : \"(unid)\"\n if (s.inputs !== undefined) {\n throw new WorkflowLoadError(\n `subworkflow step '${id}' declares both 'with' and 'inputs' — use one`,\n )\n }\n assertKnownStepRefs(\n s.with,\n knownStepIds,\n `subworkflow step '${id}' with`,\n {\n makeError: (message) => new WorkflowLoadError(message),\n extendLabel: true,\n },\n )\n s.inputs = s.with\n delete s.with\n }\n translateSubworkflowWith(s.steps, knownStepIds)\n if (Array.isArray(s.branches)) {\n for (const br of s.branches)\n translateSubworkflowWith((br as Record<string, unknown>)?.steps, knownStepIds)\n }\n }\n}\n\n/**\n * Resolve every `kind: \"agent\"` step's `harness.promptFile` (AIP-15 P2),\n * relative to the WORKFLOW.md's own directory: read the file's raw bytes,\n * use its decoded text as the step's `prompt` (replacing any inline one),\n * and record `harness.promptSha` (sha256 hex of the raw bytes) on the step\n * so a consumer can verify exactly which prompt version a run used. Nested\n * step lists (map / loop / parallel bodies, branch arms) are walked too —\n * same traversal shape as {@link translateSubworkflowWith}.\n */\nasync function applyAgentHarnessPromptFiles(\n steps: unknown,\n workflowMdPath: string,\n): Promise<void> {\n if (!Array.isArray(steps)) return\n for (const step of steps) {\n if (step === null || typeof step !== \"object\") continue\n const s = step as Record<string, unknown>\n if (s.kind === \"agent\" && s.harness && typeof s.harness === \"object\") {\n const harness = s.harness as Record<string, unknown>\n const promptFile = harness.promptFile\n if (typeof promptFile === \"string\" && promptFile.length > 0) {\n const abs = isAbsolute(promptFile)\n ? promptFile\n : join(dirname(workflowMdPath), promptFile)\n let bytes: Buffer\n try {\n bytes = await readFile(abs)\n } catch (err) {\n const id = typeof s.id === \"string\" ? s.id : \"(unid)\"\n throw new WorkflowLoadError(\n `agent step '${id}': cannot read harness.promptFile '${promptFile}': ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n s.prompt = bytes.toString(\"utf8\").trim()\n harness.promptSha = createHash(\"sha256\").update(bytes).digest(\"hex\")\n }\n await applyAgentHarnessKnowledge(harness, s, workflowMdPath)\n }\n await applyAgentHarnessPromptFiles(s.steps, workflowMdPath)\n if (Array.isArray(s.branches)) {\n for (const br of s.branches) {\n await applyAgentHarnessPromptFiles((br as Record<string, unknown>)?.steps, workflowMdPath)\n }\n }\n }\n}\n\n/**\n * True when a selector string carries a `$…` run-time reference — such a\n * string is left untouched here and resolved per run by\n * `resolveKnowledgeSelectors` in `@agentproto/workflow-runtime`.\n */\nfunction hasRef(s: string): boolean {\n return s.includes(\"$\")\n}\n\n/**\n * Resolve every `harness.knowledge[]` selector's `workspace` (AIP-15 P2),\n * relative to the WORKFLOW.md's own directory — the same rule\n * `harness.promptFile` follows — rewriting it in place to the absolute path\n * so the runtime materializer never re-resolves against a different cwd. A\n * workspace directory that does not exist fails the load, as does a `mode`\n * other than the v1-only `\"files\"`.\n *\n * Exception: a selector whose `workspace` (or any tag/kind string) contains a\n * `$` carries run-time references (AIP-16 grammar) — its strings are left\n * verbatim, no relative resolution and no existence check happens here, and\n * the selector is flagged `deferred: true` (an internal field the runtime\n * consumes and strips; a user-authored `deferred` is rejected).\n */\nasync function applyAgentHarnessKnowledge(\n harness: Record<string, unknown>,\n step: Record<string, unknown>,\n workflowMdPath: string,\n): Promise<void> {\n const knowledge = harness.knowledge\n if (!Array.isArray(knowledge)) return\n const id = typeof step.id === \"string\" ? step.id : \"(unid)\"\n for (const [i, sel] of knowledge.entries()) {\n if (sel === null || typeof sel !== \"object\") continue\n const selector = sel as Record<string, unknown>\n if (selector.mode !== undefined && selector.mode !== \"files\") {\n throw new WorkflowLoadError(\n `agent step '${id}': harness.knowledge[${i}].mode must be \"files\" (v1 supports no other mode); got ${\n typeof selector.mode === \"string\" ? `\"${selector.mode}\"` : String(selector.mode)\n }`,\n )\n }\n if (selector.deferred !== undefined) {\n throw new WorkflowLoadError(\n `agent step '${id}': harness.knowledge[${i}].deferred is an internal loader field and cannot be authored`,\n )\n }\n const workspace = selector.workspace\n if (typeof workspace !== \"string\" || workspace.length === 0) continue\n const tagStrings = [\n ...(Array.isArray(selector.anyOf) ? selector.anyOf : []),\n ...(Array.isArray(selector.allOf) ? selector.allOf : []),\n ...(Array.isArray(selector.kinds) ? selector.kinds : []),\n ].filter((t): t is string => typeof t === \"string\")\n if (hasRef(workspace) || tagStrings.some(hasRef)) {\n selector.deferred = true\n // A workspace without a ref still resolves (and is still existence-\n // checked) against the WORKFLOW.md dir even when only the tags/kinds\n // carry refs — otherwise a relative `./corpus` would be joined to the\n // run cwd at materialization time, which is not its base.\n if (hasRef(workspace)) continue\n }\n const abs = isAbsolute(workspace)\n ? workspace\n : join(dirname(workflowMdPath), workspace)\n let st\n try {\n st = await stat(abs)\n } catch {\n st = null\n }\n if (st === null || !st.isDirectory()) {\n throw new WorkflowLoadError(\n `agent step '${id}': harness.knowledge[${i}].workspace '${workspace}' does not name an existing directory (resolved to '${abs}')`,\n )\n }\n selector.workspace = abs\n }\n}\n\n/** Collect every step id declared anywhere in a manifest step list,\n * including nested map / loop / parallel / branch bodies. */\nfunction collectManifestStepIds(steps: unknown, ids: Set<string> = new Set()): Set<string> {\n if (!Array.isArray(steps)) return ids\n for (const step of steps) {\n if (step === null || typeof step !== \"object\") continue\n const s = step as Record<string, unknown>\n if (typeof s.id === \"string\") ids.add(s.id)\n collectManifestStepIds(s.steps, ids)\n if (Array.isArray(s.branches)) {\n for (const br of s.branches)\n collectManifestStepIds((br as Record<string, unknown>)?.steps, ids)\n }\n }\n return ids\n}\n\nasync function importEntryHandle(\n workflowMdPath: string,\n entry: string,\n): Promise<WorkflowHandle> {\n const abs = isAbsolute(entry) ? entry : join(dirname(workflowMdPath), entry)\n\n // Cache-bust the ESM import by mtime so a long-lived daemon re-reads an edited\n // entry.mjs instead of serving Node's process-lifetime URL-cached module —\n // which, because the manifest IS re-read fresh, would otherwise fail\n // reconcileEntry with a spurious step-count mismatch after an edit. Fresh\n // daemons / CI boot cold, so they never need it — this only helps the dev loop.\n //\n // Skipped under the Vite/vitest transform (`process.env.VITEST`): Vite owns\n // import() resolution there and rejects a query on a file URL (and a failed\n // attempt poisons its resolver for the plain retry too), while a test process\n // never has the long-lived-daemon staleness this guards against.\n let href = pathToFileURL(abs).href\n if (!process.env.VITEST) {\n try {\n const url = pathToFileURL(abs)\n url.searchParams.set(\"v\", String((await stat(abs)).mtimeMs))\n href = url.href\n } catch {\n // stat race with an in-flight edit — fall back to the plain URL.\n }\n }\n\n let mod: Record<string, unknown>\n try {\n mod = (await import(href)) as Record<string, unknown>\n } catch (err) {\n throw new WorkflowLoadError(\n `cannot import entry '${entry}': ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const exported = mod.default\n if (!isWorkflowHandle(exported)) {\n throw new WorkflowLoadError(\n `entry '${entry}' must default-export a WorkflowHandle (e.g. from defineWorkflow) ` +\n `carrying an 'id' + 'steps'; got ${exported === undefined ? \"no default export\" : typeof exported}.`,\n )\n }\n return exported\n}\n\n/**\n * Load and validate a WORKFLOW.md at `workflowMdPath` into a WorkflowHandle.\n * Throws {@link WorkflowLoadError} on a missing/unreadable file or a bad entry\n * module, the parser's error on invalid frontmatter, and\n * {@link WorkflowReconcileError} when an entry's graph disagrees with the\n * manifest. The returned handle still needs `compileWorkflow` + a tool registry\n * to run — this stops at the validated, in-memory handle.\n */\nexport async function loadWorkflowHandle(\n workflowMdPath: string,\n): Promise<WorkflowHandle> {\n let source: string\n try {\n source = await readFile(workflowMdPath, \"utf8\")\n } catch (err) {\n throw new WorkflowLoadError(\n `cannot read '${workflowMdPath}': ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const manifest: WorkflowManifest = parseWorkflowManifest(source)\n // gray-matter memoizes its YAML parse by content — a second load of an\n // identical manifest would otherwise hand back the SAME objects a previous\n // load already rewrote in place (absolute workspaces, prompt text, the\n // internal `deferred` flag), breaking the authored-field rejection and\n // re-resolving against a stale base. Clone so every load starts clean.\n manifest.frontmatter = structuredClone(manifest.frontmatter)\n const entry = manifest.frontmatter.entry\n if (!entry) {\n const knownStepIds = collectManifestStepIds(manifest.frontmatter.steps)\n translateSubworkflowWith(manifest.frontmatter.steps, knownStepIds)\n await applyAgentHarnessPromptFiles(manifest.frontmatter.steps, workflowMdPath)\n return workflowFromManifest(manifest)\n }\n const handle = await importEntryHandle(workflowMdPath, entry)\n reconcileEntry(manifest, handle)\n return handle\n}\n"]}
1
+ {"version":3,"sources":["../src/entry-graph.ts","../src/reconcile.ts","../src/load-workflow.ts"],"names":["join","stat"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Fresh import graph for a workflow entry module.\n *\n * `import(entry + \"?v=mtime\")` re-evaluates the entry itself, but Node keys\n * its module cache by URL, so the entry's RELATIVE imports (`./cron-rules.mjs`)\n * keep resolving to the copy cached when the daemon booted: after a `git pull`\n * the entry ran new code against stale helpers until the daemon restarted.\n *\n * A resolve hook closes that: every relative import made from a module that\n * carries the version param inherits the same param, so the whole\n * entry-local graph is re-read when (and only when) a file in the entry\n * directory changed. Bare specifiers (`node_modules`, workspace packages)\n * are never versioned, so shared library singletons stay single instances.\n */\n\nimport { readdir, stat } from \"node:fs/promises\"\nimport { register } from \"node:module\"\nimport { join } from \"node:path\"\n\n/** Query param that carries the entry-graph version. */\nexport const ENTRY_GRAPH_VERSION_PARAM = \"agentproto_v\"\n\n/** Source of the resolve hook, loaded through a `data:` URL so no file has to\n * ship next to the bundle. Exported so tests can run it in a real process. */\nexport const ENTRY_GRAPH_HOOK_SOURCE = `\nconst PARAM = ${JSON.stringify(ENTRY_GRAPH_VERSION_PARAM)}\nexport async function resolve(specifier, context, nextResolve) {\n const result = await nextResolve(specifier, context)\n const relative = specifier.startsWith(\"./\") || specifier.startsWith(\"../\")\n if (!relative || !context.parentURL || !result.url.startsWith(\"file:\") || result.url.includes(\"/node_modules/\")) return result\n let version\n try {\n version = new URL(context.parentURL).searchParams.get(PARAM)\n } catch {\n return result\n }\n if (version === null) return result\n const url = new URL(result.url)\n url.searchParams.set(PARAM, version)\n return { ...result, url: url.href }\n}\n`\n\nconst SCRIPT_EXT = /\\.(?:mjs|js|cjs|json)$/\nconst MAX_DEPTH = 3\n\nasync function newestMtimeMs(dir: string, depth: number): Promise<number> {\n let newest = 0\n let entries: import(\"node:fs\").Dirent[]\n try {\n entries = await readdir(dir, { withFileTypes: true })\n } catch {\n return 0\n }\n for (const e of entries) {\n if (e.name === \"node_modules\" || e.name.startsWith(\".\")) continue\n const p = join(dir, e.name)\n if (e.isDirectory()) {\n if (depth < MAX_DEPTH) newest = Math.max(newest, await newestMtimeMs(p, depth + 1))\n } else if (SCRIPT_EXT.test(e.name)) {\n try {\n newest = Math.max(newest, (await stat(p)).mtimeMs)\n } catch {\n // file vanished mid-scan — the next load picks it up.\n }\n }\n }\n return newest\n}\n\n/** Version of an entry's local graph: the newest mtime of any script/JSON file\n * under the entry directory. Changes iff a file there was edited or replaced. */\nexport function entryGraphVersion(entryDir: string): Promise<number> {\n return newestMtimeMs(entryDir, 0)\n}\n\nlet hookRegistered = false\n\n/** Register the resolve hook once per process. Returns false (and the caller\n * falls back to versioning the entry alone) when the runtime can't. */\nexport function registerEntryGraphHook(): boolean {\n if (hookRegistered) return true\n try {\n register(`data:text/javascript,${encodeURIComponent(ENTRY_GRAPH_HOOK_SOURCE)}`)\n hookRegistered = true\n } catch {\n return false\n }\n return true\n}\n","/**\n * Reconcile a WORKFLOW.md manifest against the handle its `entry` module\n * exports. An entry-backed workflow declares its step graph twice — once as\n * governance-facing manifest data, once as the runnable handle — so the two\n * MUST agree, or the manifest is lying about what actually runs. This checks\n * the workflow id and the top-level step (id, kind) sequence; nested step\n * lists (map / loop / parallel bodies) are the entry's concern.\n */\n\nimport type { WorkflowManifest } from \"@agentproto/workflow/manifest\"\nimport type { WorkflowHandle } from \"@agentproto/workflow\"\n\nexport class WorkflowReconcileError extends Error {\n constructor(message: string) {\n super(`reconcileEntry: ${message}`)\n this.name = \"WorkflowReconcileError\"\n }\n}\n\ninterface IdKind {\n readonly id: string\n readonly kind: string\n}\n\nconst idKind = (steps: readonly IdKind[]): IdKind[] =>\n steps.map((s) => ({ id: s.id, kind: s.kind }))\n\nconst summary = (steps: readonly IdKind[]): string =>\n steps.map((s) => `${s.id}:${s.kind}`).join(\" → \") || \"(none)\"\n\nexport function reconcileEntry(\n manifest: WorkflowManifest,\n handle: WorkflowHandle,\n): void {\n const fm = manifest.frontmatter\n if (fm.id !== handle.id) {\n throw new WorkflowReconcileError(\n `manifest id '${fm.id}' ≠ entry id '${handle.id}'.`,\n )\n }\n const declared = idKind(fm.steps as readonly IdKind[])\n const actual = idKind(handle.steps as readonly IdKind[])\n if (declared.length !== actual.length) {\n throw new WorkflowReconcileError(\n `step count differs — manifest declares ${declared.length} [${summary(declared)}], ` +\n `entry has ${actual.length} [${summary(actual)}].`,\n )\n }\n for (let i = 0; i < declared.length; i++) {\n const d = declared[i]!\n const a = actual[i]!\n if (d.id !== a.id || d.kind !== a.kind) {\n throw new WorkflowReconcileError(\n `step ${i} differs — manifest '${d.id}:${d.kind}' ≠ entry '${a.id}:${a.kind}'. ` +\n `manifest [${summary(declared)}] vs entry [${summary(actual)}].`,\n )\n }\n }\n}\n","/**\n * loadWorkflowHandle — read a WORKFLOW.md off disk into a WorkflowHandle.\n *\n * The pure packages stop at the page boundary: `@agentproto/workflow` parses a\n * manifest *string*, `@agentproto/workflow-runtime` compiles an in-memory\n * handle. Neither touches the filesystem. This is the host-side seam that does:\n *\n * read file → parse manifest → (if `entry`) import the module + reconcile\n *\n * With no `entry`, the manifest IS the workflow (purely-declarative path). With\n * an `entry`, its default-exported handle is the source of truth for runtime\n * step logic and the manifest is reconciled against it — the two MUST agree.\n *\n * The entry import mirrors the CLI's adapter loader: resolve relative to the\n * manifest's directory, import via a `file://` URL so an absolute path works\n * without an `exports` declaration.\n */\n\nimport { readFile, stat } from \"node:fs/promises\"\nimport { createHash } from \"node:crypto\"\nimport { dirname, isAbsolute, join } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\n\nimport {\n parseWorkflowManifest,\n workflowFromManifest,\n type WorkflowManifest,\n} from \"@agentproto/workflow/manifest\"\nimport type { WorkflowHandle } from \"@agentproto/workflow\"\nimport { assertKnownStepRefs } from \"@agentproto/workflow\"\n\nimport { ENTRY_GRAPH_VERSION_PARAM, entryGraphVersion, registerEntryGraphHook } from \"./entry-graph.js\"\nimport { reconcileEntry } from \"./reconcile.js\"\n\nexport class WorkflowLoadError extends Error {\n constructor(message: string) {\n super(`loadWorkflow: ${message}`)\n this.name = \"WorkflowLoadError\"\n }\n}\n\n/** A handle is a plain object carrying an `id` and a `steps` array. */\nfunction isWorkflowHandle(v: unknown): v is WorkflowHandle {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as { id?: unknown }).id === \"string\" &&\n Array.isArray((v as { steps?: unknown }).steps)\n )\n}\n\n/**\n * Translate a declarative `subworkflow` step's `with:` block into the `inputs`\n * projection the runtime compiler threads into `step.input(b)` (AIP-16 ref\n * grammar: `$input.*`, `$steps.<id>.*`, literals — resolved against the\n * PARENT's bindings). Without this, a manifest `with:` is dead documentation:\n * the child receives the parent's raw input verbatim. Steps without `with:`\n * are left untouched. Nested step lists (map / loop / parallel bodies and\n * branch arms) are walked too.\n */\nfunction translateSubworkflowWith(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n steps: unknown,\n knownStepIds: ReadonlySet<string>,\n): void {\n if (!Array.isArray(steps)) return\n for (const step of steps) {\n if (step === null || typeof step !== \"object\") continue\n const s = step as Record<string, unknown>\n if (s.kind === \"subworkflow\" && s.with !== undefined) {\n const id = typeof s.id === \"string\" ? s.id : \"(unid)\"\n if (s.inputs !== undefined) {\n throw new WorkflowLoadError(\n `subworkflow step '${id}' declares both 'with' and 'inputs' — use one`,\n )\n }\n assertKnownStepRefs(\n s.with,\n knownStepIds,\n `subworkflow step '${id}' with`,\n {\n makeError: (message) => new WorkflowLoadError(message),\n extendLabel: true,\n },\n )\n s.inputs = s.with\n delete s.with\n }\n translateSubworkflowWith(s.steps, knownStepIds)\n if (Array.isArray(s.branches)) {\n for (const br of s.branches)\n translateSubworkflowWith((br as Record<string, unknown>)?.steps, knownStepIds)\n }\n }\n}\n\n/**\n * Resolve every `kind: \"agent\"` step's `harness.promptFile` (AIP-15 P2),\n * relative to the WORKFLOW.md's own directory: read the file's raw bytes,\n * use its decoded text as the step's `prompt` (replacing any inline one),\n * and record `harness.promptSha` (sha256 hex of the raw bytes) on the step\n * so a consumer can verify exactly which prompt version a run used. Nested\n * step lists (map / loop / parallel bodies, branch arms) are walked too —\n * same traversal shape as {@link translateSubworkflowWith}.\n */\nasync function applyAgentHarnessPromptFiles(\n steps: unknown,\n workflowMdPath: string,\n): Promise<void> {\n if (!Array.isArray(steps)) return\n for (const step of steps) {\n if (step === null || typeof step !== \"object\") continue\n const s = step as Record<string, unknown>\n if (s.kind === \"agent\" && s.harness && typeof s.harness === \"object\") {\n const harness = s.harness as Record<string, unknown>\n const promptFile = harness.promptFile\n if (typeof promptFile === \"string\" && promptFile.length > 0) {\n const abs = isAbsolute(promptFile)\n ? promptFile\n : join(dirname(workflowMdPath), promptFile)\n let bytes: Buffer\n try {\n bytes = await readFile(abs)\n } catch (err) {\n const id = typeof s.id === \"string\" ? s.id : \"(unid)\"\n throw new WorkflowLoadError(\n `agent step '${id}': cannot read harness.promptFile '${promptFile}': ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n s.prompt = bytes.toString(\"utf8\").trim()\n harness.promptSha = createHash(\"sha256\").update(bytes).digest(\"hex\")\n }\n await applyAgentHarnessKnowledge(harness, s, workflowMdPath)\n }\n await applyAgentHarnessPromptFiles(s.steps, workflowMdPath)\n if (Array.isArray(s.branches)) {\n for (const br of s.branches) {\n await applyAgentHarnessPromptFiles((br as Record<string, unknown>)?.steps, workflowMdPath)\n }\n }\n }\n}\n\n/**\n * True when a selector string carries a `$…` run-time reference — such a\n * string is left untouched here and resolved per run by\n * `resolveKnowledgeSelectors` in `@agentproto/workflow-runtime`.\n */\nfunction hasRef(s: string): boolean {\n return s.includes(\"$\")\n}\n\n/**\n * Resolve every `harness.knowledge[]` selector's `workspace` (AIP-15 P2),\n * relative to the WORKFLOW.md's own directory — the same rule\n * `harness.promptFile` follows — rewriting it in place to the absolute path\n * so the runtime materializer never re-resolves against a different cwd. A\n * workspace directory that does not exist fails the load, as does a `mode`\n * other than the v1-only `\"files\"`.\n *\n * Exception: a selector whose `workspace` (or any tag/kind string) contains a\n * `$` carries run-time references (AIP-16 grammar) — its strings are left\n * verbatim, no relative resolution and no existence check happens here, and\n * the selector is flagged `deferred: true` (an internal field the runtime\n * consumes and strips; a user-authored `deferred` is rejected).\n */\nasync function applyAgentHarnessKnowledge(\n harness: Record<string, unknown>,\n step: Record<string, unknown>,\n workflowMdPath: string,\n): Promise<void> {\n const knowledge = harness.knowledge\n if (!Array.isArray(knowledge)) return\n const id = typeof step.id === \"string\" ? step.id : \"(unid)\"\n for (const [i, sel] of knowledge.entries()) {\n if (sel === null || typeof sel !== \"object\") continue\n const selector = sel as Record<string, unknown>\n if (selector.mode !== undefined && selector.mode !== \"files\") {\n throw new WorkflowLoadError(\n `agent step '${id}': harness.knowledge[${i}].mode must be \"files\" (v1 supports no other mode); got ${\n typeof selector.mode === \"string\" ? `\"${selector.mode}\"` : String(selector.mode)\n }`,\n )\n }\n if (selector.deferred !== undefined) {\n throw new WorkflowLoadError(\n `agent step '${id}': harness.knowledge[${i}].deferred is an internal loader field and cannot be authored`,\n )\n }\n const workspace = selector.workspace\n if (typeof workspace !== \"string\" || workspace.length === 0) continue\n const tagStrings = [\n ...(Array.isArray(selector.anyOf) ? selector.anyOf : []),\n ...(Array.isArray(selector.allOf) ? selector.allOf : []),\n ...(Array.isArray(selector.kinds) ? selector.kinds : []),\n ].filter((t): t is string => typeof t === \"string\")\n if (hasRef(workspace) || tagStrings.some(hasRef)) {\n selector.deferred = true\n // A workspace without a ref still resolves (and is still existence-\n // checked) against the WORKFLOW.md dir even when only the tags/kinds\n // carry refs — otherwise a relative `./corpus` would be joined to the\n // run cwd at materialization time, which is not its base.\n if (hasRef(workspace)) continue\n }\n const abs = isAbsolute(workspace)\n ? workspace\n : join(dirname(workflowMdPath), workspace)\n let st\n try {\n st = await stat(abs)\n } catch {\n st = null\n }\n if (st === null || !st.isDirectory()) {\n throw new WorkflowLoadError(\n `agent step '${id}': harness.knowledge[${i}].workspace '${workspace}' does not name an existing directory (resolved to '${abs}')`,\n )\n }\n selector.workspace = abs\n }\n}\n\n/** Collect every step id declared anywhere in a manifest step list,\n * including nested map / loop / parallel / branch bodies. */\nfunction collectManifestStepIds(steps: unknown, ids: Set<string> = new Set()): Set<string> {\n if (!Array.isArray(steps)) return ids\n for (const step of steps) {\n if (step === null || typeof step !== \"object\") continue\n const s = step as Record<string, unknown>\n if (typeof s.id === \"string\") ids.add(s.id)\n collectManifestStepIds(s.steps, ids)\n if (Array.isArray(s.branches)) {\n for (const br of s.branches)\n collectManifestStepIds((br as Record<string, unknown>)?.steps, ids)\n }\n }\n return ids\n}\n\nasync function importEntryHandle(\n workflowMdPath: string,\n entry: string,\n): Promise<WorkflowHandle> {\n const abs = isAbsolute(entry) ? entry : join(dirname(workflowMdPath), entry)\n\n // Cache-bust the ESM import so a long-lived daemon re-reads an edited entry\n // AND its relative imports instead of serving Node's process-lifetime\n // URL-cached modules — which, because the manifest IS re-read fresh, would\n // otherwise fail reconcileEntry with a spurious step-count mismatch after an\n // edit, or silently run a new entry against stale helper modules. The\n // version is the newest mtime in the entry directory; a resolve hook\n // (entry-graph.ts) propagates it to the entry's relative imports. Fresh\n // daemons / CI boot cold, so they never need it — this only helps the dev loop.\n //\n // Skipped under the Vite/vitest transform (`process.env.VITEST`): Vite owns\n // import() resolution there and rejects a query on a file URL (and a failed\n // attempt poisons its resolver for the plain retry too), while a test process\n // never has the long-lived-daemon staleness this guards against.\n let href = pathToFileURL(abs).href\n if (!process.env.VITEST) {\n try {\n const version = await entryGraphVersion(dirname(abs))\n if (version > 0) {\n const url = pathToFileURL(abs)\n url.searchParams.set(ENTRY_GRAPH_VERSION_PARAM, String(version))\n registerEntryGraphHook()\n href = url.href\n }\n } catch {\n // scan race with an in-flight edit — fall back to the plain URL.\n }\n }\n\n let mod: Record<string, unknown>\n try {\n mod = (await import(href)) as Record<string, unknown>\n } catch (err) {\n throw new WorkflowLoadError(\n `cannot import entry '${entry}': ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const exported = mod.default\n if (!isWorkflowHandle(exported)) {\n throw new WorkflowLoadError(\n `entry '${entry}' must default-export a WorkflowHandle (e.g. from defineWorkflow) ` +\n `carrying an 'id' + 'steps'; got ${exported === undefined ? \"no default export\" : typeof exported}.`,\n )\n }\n return exported\n}\n\n/**\n * Load and validate a WORKFLOW.md at `workflowMdPath` into a WorkflowHandle.\n * Throws {@link WorkflowLoadError} on a missing/unreadable file or a bad entry\n * module, the parser's error on invalid frontmatter, and\n * {@link WorkflowReconcileError} when an entry's graph disagrees with the\n * manifest. The returned handle still needs `compileWorkflow` + a tool registry\n * to run — this stops at the validated, in-memory handle.\n */\nexport async function loadWorkflowHandle(\n workflowMdPath: string,\n): Promise<WorkflowHandle> {\n let source: string\n try {\n source = await readFile(workflowMdPath, \"utf8\")\n } catch (err) {\n throw new WorkflowLoadError(\n `cannot read '${workflowMdPath}': ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const manifest: WorkflowManifest = parseWorkflowManifest(source)\n // gray-matter memoizes its YAML parse by content — a second load of an\n // identical manifest would otherwise hand back the SAME objects a previous\n // load already rewrote in place (absolute workspaces, prompt text, the\n // internal `deferred` flag), breaking the authored-field rejection and\n // re-resolving against a stale base. Clone so every load starts clean.\n manifest.frontmatter = structuredClone(manifest.frontmatter)\n const entry = manifest.frontmatter.entry\n if (!entry) {\n const knownStepIds = collectManifestStepIds(manifest.frontmatter.steps)\n translateSubworkflowWith(manifest.frontmatter.steps, knownStepIds)\n await applyAgentHarnessPromptFiles(manifest.frontmatter.steps, workflowMdPath)\n return workflowFromManifest(manifest)\n }\n const handle = await importEntryHandle(workflowMdPath, entry)\n reconcileEntry(manifest, handle)\n return handle\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentproto/workflow-loader",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Host-side loader for AIP-15 WORKFLOW.md: reads a manifest off disk, imports its optional TypeScript entry module, and reconciles the entry's step graph against the declared manifest. The I/O layer the pure @agentproto/workflow + @agentproto/workflow-runtime packages deliberately omit.",
5
5
  "keywords": [
6
6
  "agentproto",