diff --git a/packages/workflow/src/bundle/build-descriptor.ts b/packages/workflow/src/bundle/build-descriptor.ts index 3930b22..cdf7bf0 100644 --- a/packages/workflow/src/bundle/build-descriptor.ts +++ b/packages/workflow/src/bundle/build-descriptor.ts @@ -1,7 +1,7 @@ import * as z from "zod/v4"; import type { RoleMeta, WorkflowDefinition } from "../types.js"; -import type { WorkflowDescriptor, WorkflowRoleSchema } from "./workflow-descriptor.js"; +import type { WorkflowDescriptor, WorkflowRoleSchema } from "./types.js"; function stripJsonSchemaMeta(json: Record): WorkflowRoleSchema { const { $schema: _drop, ...rest } = json; diff --git a/packages/workflow/src/bundle/bundle-validator.ts b/packages/workflow/src/bundle/bundle-validator.ts index 6e11dc5..0e8e38a 100644 --- a/packages/workflow/src/bundle/bundle-validator.ts +++ b/packages/workflow/src/bundle/bundle-validator.ts @@ -10,6 +10,11 @@ import type { Program, VariableDeclaration, } from "acorn"; +import * as acorn from "acorn"; + +import { err, ok, type Result } from "../util/index.js"; + +import type { WorkflowBundleValidationInput } from "./types.js"; /** Acorn Node with index-access for property traversal. */ type AcornNode = Node & { [key: string]: unknown }; @@ -22,17 +27,6 @@ function narrowNode(node: Node): T { return node as unknown as T; } -import * as acorn from "acorn"; - -import { err, ok, type Result } from "../util/result.js"; - -export type WorkflowBundleValidationInput = { - /** Absolute or relative path (used for `.esm.js` suffix checks). */ - filePath: string; - /** UTF-8 source of the bundle. */ - source: string; -}; - function endsWithEsmJs(path: string): boolean { return path.endsWith(".esm.js"); } diff --git a/packages/workflow/src/bundle/extract-bundle-exports.ts b/packages/workflow/src/bundle/extract-bundle-exports.ts index 35f0d66..72f99a9 100644 --- a/packages/workflow/src/bundle/extract-bundle-exports.ts +++ b/packages/workflow/src/bundle/extract-bundle-exports.ts @@ -1,20 +1,10 @@ import type { WorkflowFn } from "../types.js"; -import { err, ok, type Result } from "../util/result.js"; +import { err, ok, type Result } from "../util/index.js"; import { importWorkflowBundleModule } from "./bundle-import-env.js"; import { ensureUncagedWorkflowSymlink } from "./ensure-uncaged-workflow-symlink.js"; -import type { WorkflowDescriptor } from "./workflow-descriptor.js"; +import type { ExtractBundleExportsOptions, ExtractedBundleExports } from "./types.js"; import { validateWorkflowDescriptor } from "./workflow-descriptor.js"; -export type ExtractedBundleExports = { - run: WorkflowFn; - descriptor: WorkflowDescriptor; -}; - -export type ExtractBundleExportsOptions = { - /** When set, ensures `node_modules/@uncaged/workflow` exists under this root before import. */ - storageRoot: string | null; -}; - /** Load a workflow `.esm.js` bundle and read its named exports (`run`, `descriptor`). */ export async function extractBundleExports( bundlePath: string, diff --git a/packages/workflow/src/bundle/generate-descriptor.ts b/packages/workflow/src/bundle/generate-descriptor.ts index 1405ba7..396586a 100644 --- a/packages/workflow/src/bundle/generate-descriptor.ts +++ b/packages/workflow/src/bundle/generate-descriptor.ts @@ -1,6 +1,6 @@ import { stringify } from "yaml"; -import type { WorkflowDescriptor } from "./workflow-descriptor.js"; +import type { WorkflowDescriptor } from "./types.js"; /** Serialize a validated workflow descriptor to YAML for storage next to the bundle. */ export function stringifyWorkflowDescriptor(descriptor: WorkflowDescriptor): string { diff --git a/packages/workflow/src/bundle/index.ts b/packages/workflow/src/bundle/index.ts new file mode 100644 index 0000000..f9f7e0e --- /dev/null +++ b/packages/workflow/src/bundle/index.ts @@ -0,0 +1,15 @@ +export { buildDescriptor } from "./build-descriptor.js"; +export { importWorkflowBundleModule } from "./bundle-import-env.js"; +export { validateWorkflowBundle } from "./bundle-validator.js"; +export { ensureUncagedWorkflowSymlink } from "./ensure-uncaged-workflow-symlink.js"; +export { extractBundleExports } from "./extract-bundle-exports.js"; +export { stringifyWorkflowDescriptor } from "./generate-descriptor.js"; +export type { + ExtractBundleExportsOptions, + ExtractedBundleExports, + WorkflowBundleValidationInput, + WorkflowDescriptor, + WorkflowRoleDescriptor, + WorkflowRoleSchema, +} from "./types.js"; +export { validateWorkflowDescriptor } from "./workflow-descriptor.js"; diff --git a/packages/workflow/src/bundle/types.ts b/packages/workflow/src/bundle/types.ts new file mode 100644 index 0000000..92c5416 --- /dev/null +++ b/packages/workflow/src/bundle/types.ts @@ -0,0 +1,32 @@ +import type { WorkflowFn } from "../types.js"; + +/** JSON Schema fragment describing one role's `meta` shape (subset supported by code generation). */ +export type WorkflowRoleSchema = Record; + +export type WorkflowRoleDescriptor = { + description: string; + schema: WorkflowRoleSchema; +}; + +/** Workflow metadata exported as `export const descriptor` from `.esm.js` bundles. */ +export type WorkflowDescriptor = { + description: string; + roles: Record; +}; + +export type WorkflowBundleValidationInput = { + /** Absolute or relative path (used for `.esm.js` suffix checks). */ + filePath: string; + /** UTF-8 source of the bundle. */ + source: string; +}; + +export type ExtractedBundleExports = { + run: WorkflowFn; + descriptor: WorkflowDescriptor; +}; + +export type ExtractBundleExportsOptions = { + /** When set, ensures `node_modules/@uncaged/workflow` exists under this root before import. */ + storageRoot: string | null; +}; diff --git a/packages/workflow/src/bundle/workflow-descriptor.ts b/packages/workflow/src/bundle/workflow-descriptor.ts index ca86ce9..e21851b 100644 --- a/packages/workflow/src/bundle/workflow-descriptor.ts +++ b/packages/workflow/src/bundle/workflow-descriptor.ts @@ -1,18 +1,6 @@ -import { err, ok, type Result } from "../util/result.js"; +import { err, ok, type Result } from "../util/index.js"; -/** JSON Schema fragment describing one role's `meta` shape (subset supported by code generation). */ -export type WorkflowRoleSchema = Record; - -export type WorkflowRoleDescriptor = { - description: string; - schema: WorkflowRoleSchema; -}; - -/** Workflow metadata exported as `export const descriptor` from `.esm.js` bundles. */ -export type WorkflowDescriptor = { - description: string; - roles: Record; -}; +import type { WorkflowDescriptor, WorkflowRoleDescriptor, WorkflowRoleSchema } from "./types.js"; export function validateWorkflowDescriptor(value: unknown): Result { if (value === null || typeof value !== "object" || Array.isArray(value)) { diff --git a/packages/workflow/src/cas/cas.ts b/packages/workflow/src/cas/cas.ts index 513e6b1..32df91d 100644 --- a/packages/workflow/src/cas/cas.ts +++ b/packages/workflow/src/cas/cas.ts @@ -2,13 +2,7 @@ import { mkdir, readdir, readFile, rename, unlink, writeFile } from "node:fs/pro import { join } from "node:path"; import { hashString } from "./hash.js"; - -export type CasStore = { - put(content: string): Promise; - get(hash: string): Promise; - delete(hash: string): Promise; - list(): Promise; -}; +import type { CasStore } from "./types.js"; export function createCasStore(casDir: string): CasStore { async function ensureDir(): Promise { diff --git a/packages/workflow/src/cas/hash.ts b/packages/workflow/src/cas/hash.ts index e332e8e..edf7207 100644 --- a/packages/workflow/src/cas/hash.ts +++ b/packages/workflow/src/cas/hash.ts @@ -2,7 +2,7 @@ import { Buffer } from "node:buffer"; import XXH from "xxhashjs"; -import { encodeUint64AsCrockford } from "../util/base32.js"; +import { encodeUint64AsCrockford } from "../util/index.js"; function digestToUint64(digest: { toString(radix?: number): string }): bigint { const hex = digest.toString(16).padStart(16, "0"); diff --git a/packages/workflow/src/cas/index.ts b/packages/workflow/src/cas/index.ts new file mode 100644 index 0000000..532efb1 --- /dev/null +++ b/packages/workflow/src/cas/index.ts @@ -0,0 +1,18 @@ +export { createCasStore, createThreadCas } from "./cas.js"; +export { hashString, hashWorkflowBundleBytes } from "./hash.js"; +export { + createContentMerkleNode, + getContentMerklePayload, + parseMerkleNode, + putContentMerkleNode, + putStepMerkleNode, + putThreadMerkleNode, + serializeMerkleNode, +} from "./merkle.js"; +export type { + CasStore, + MerkleNode, + MerkleNodeType, + StepMerklePayload, + ThreadMerklePayload, +} from "./types.js"; diff --git a/packages/workflow/src/cas/merkle.ts b/packages/workflow/src/cas/merkle.ts index dac3927..1838c1e 100644 --- a/packages/workflow/src/cas/merkle.ts +++ b/packages/workflow/src/cas/merkle.ts @@ -1,14 +1,6 @@ import { parse, stringify } from "yaml"; -import type { CasStore } from "./cas.js"; - -export type MerkleNodeType = "content" | "step" | "thread"; - -export type MerkleNode = { - type: MerkleNodeType; - payload: string | Record; - children: string[]; -}; +import type { CasStore, MerkleNode, StepMerklePayload, ThreadMerklePayload } from "./types.js"; export function serializeMerkleNode(node: MerkleNode): string { return stringify( @@ -53,20 +45,6 @@ export function createContentMerkleNode(payload: string): MerkleNode { return { type: "content", payload, children: [] }; } -export type StepMerklePayload = { - role: string; - meta: Record; -}; - -export type ThreadMerklePayload = { - workflow: string; - threadId: string; - result: { - returnCode: number; - summary: string; - }; -}; - /** Serializes a step Merkle node (role + meta + content child) and stores it in CAS. */ export async function putStepMerkleNode( store: CasStore, diff --git a/packages/workflow/src/cas/types.ts b/packages/workflow/src/cas/types.ts new file mode 100644 index 0000000..9295cb5 --- /dev/null +++ b/packages/workflow/src/cas/types.ts @@ -0,0 +1,28 @@ +export type CasStore = { + put(content: string): Promise; + get(hash: string): Promise; + delete(hash: string): Promise; + list(): Promise; +}; + +export type MerkleNodeType = "content" | "step" | "thread"; + +export type MerkleNode = { + type: MerkleNodeType; + payload: string | Record; + children: string[]; +}; + +export type StepMerklePayload = { + role: string; + meta: Record; +}; + +export type ThreadMerklePayload = { + workflow: string; + threadId: string; + result: { + returnCode: number; + summary: string; + }; +}; diff --git a/packages/workflow/src/engine/create-workflow.ts b/packages/workflow/src/engine/create-workflow.ts index 19b72ea..133b7a1 100644 --- a/packages/workflow/src/engine/create-workflow.ts +++ b/packages/workflow/src/engine/create-workflow.ts @@ -1,7 +1,6 @@ -import type { CasStore } from "../cas/cas.js"; -import { putContentMerkleNode } from "../cas/merkle.js"; -import { buildExtractUserContent, type ExtractFn } from "../extract/extract-fn.js"; -import { reactExtract } from "../extract/react-extract.js"; +import type { CasStore } from "../cas/index.js"; +import { putContentMerkleNode } from "../cas/index.js"; +import { buildExtractUserContent, type ExtractFn, reactExtract } from "../extract/index.js"; import { type AgentBinding, type AgentContext, @@ -20,7 +19,7 @@ import { type WorkflowFn, type WorkflowFnOptions, } from "../types.js"; -import { mergeRefsWithContentHash } from "../util/refs-field.js"; +import { mergeRefsWithContentHash } from "../util/index.js"; function isRoleNext( next: (keyof M & string) | typeof END, diff --git a/packages/workflow/src/engine/engine.ts b/packages/workflow/src/engine/engine.ts index 931da51..3f76956 100644 --- a/packages/workflow/src/engine/engine.ts +++ b/packages/workflow/src/engine/engine.ts @@ -1,8 +1,12 @@ import { appendFile, mkdir } from "node:fs/promises"; import { dirname } from "node:path"; -import type { CasStore } from "../cas/cas.js"; -import { getContentMerklePayload, putStepMerkleNode, putThreadMerkleNode } from "../cas/merkle.js"; +import { + type CasStore, + getContentMerklePayload, + putStepMerkleNode, + putThreadMerkleNode, +} from "../cas/index.js"; import type { ThreadInput, WorkflowCompletion, @@ -10,41 +14,9 @@ import type { WorkflowFnOptions, WorkflowResult, } from "../types.js"; -import type { LogFn } from "../util/logger.js"; -import { normalizeRefsField } from "../util/refs-field.js"; +import { type LogFn, normalizeRefsField } from "../util/index.js"; -export type ExecuteThreadIo = { - threadId: string; - hash: string; - dataJsonlPath: string; - infoJsonlPath: string; - cas: CasStore; -}; - -/** One persisted role line in `.data.jsonl` (engine adds these for fork replay before running the generator). */ -export type PrefilledDiskStep = { - role: string; - contentHash: string; - meta: Record; - refs: string[]; - timestamp: number; -}; - -export type ExecuteThreadOptions = { - maxRounds: number; - /** Passed to the bundle as `WorkflowFnOptions.depth`. */ - depth: number; - signal: AbortSignal; - /** Invoked after each successful yield (and outer-loop checks); used for pause/resume. */ - awaitAfterEachYield: () => Promise; - /** When non-null, written into the start record so tooling can trace lineage. */ - forkSourceThreadId: string | null; - /** - * Written to `.data.jsonl` immediately after the start record, before the generator runs. - * Must match `input.steps` length and order when present. - */ - prefilledDiskSteps: PrefilledDiskStep[] | null; -}; +import type { ExecuteThreadIo, ExecuteThreadOptions } from "./types.js"; async function appendDataLine(path: string, record: unknown): Promise { const line = `${JSON.stringify(record)}\n`; diff --git a/packages/workflow/src/engine/fork-thread.ts b/packages/workflow/src/engine/fork-thread.ts index 6ee82bd..10dfd02 100644 --- a/packages/workflow/src/engine/fork-thread.ts +++ b/packages/workflow/src/engine/fork-thread.ts @@ -1,18 +1,7 @@ -import type { RoleOutput, WorkflowCompletion } from "../types.js"; -import { normalizeRefsField } from "../util/refs-field.js"; -import { err, ok, type Result } from "../util/result.js"; +import type { WorkflowCompletion } from "../types.js"; +import { err, normalizeRefsField, ok, type Result } from "../util/index.js"; -/** Role steps replayed from `.data.jsonl`, including persisted timestamps. */ -export type ForkHistoricalStep = RoleOutput & { timestamp: number }; - -export type ParsedThreadStartRecord = { - workflowName: string; - hash: string; - threadId: string; - prompt: string; - maxRounds: number; - depth: number; -}; +import type { ForkHistoricalStep, ForkPlan, ParsedThreadStartRecord } from "./types.js"; /** Recognizes a persisted workflow completion line (no `role`; has numeric `returnCode` and string `summary`). Omits `rootHash` when absent. */ export function tryParseWorkflowResultRecord( @@ -228,15 +217,6 @@ export function selectForkHistoricalSteps( return ok(roleSteps.slice(0, idx + 1)); } -export type ForkPlan = { - workflowName: string; - hash: string; - sourceThreadId: string; - prompt: string; - runOptions: { maxRounds: number; depth: number }; - historicalSteps: ForkHistoricalStep[]; -}; - /** * Read `.data.jsonl` text and compute fork payload for the worker `run` command. */ diff --git a/packages/workflow/src/engine/gc.ts b/packages/workflow/src/engine/gc.ts index 36674b5..f566688 100644 --- a/packages/workflow/src/engine/gc.ts +++ b/packages/workflow/src/engine/gc.ts @@ -1,16 +1,9 @@ import { readdir, readFile } from "node:fs/promises"; import { join } from "node:path"; -import { type CasStore, createCasStore } from "../cas/cas.js"; -import { err, ok, type Result } from "../util/result.js"; -import { getGlobalCasDir } from "../util/storage-root.js"; +import { type CasStore, createCasStore } from "../cas/index.js"; +import { err, getGlobalCasDir, ok, type Result } from "../util/index.js"; import { parseThreadDataJsonl } from "./fork-thread.js"; - -export type GcResult = { - scannedThreads: number; - activeRefs: number; - deletedEntries: number; - deletedHashes: string[]; -}; +import type { GcResult } from "./types.js"; async function listThreadDataJsonlPaths(storageRoot: string): Promise> { const logsRoot = join(storageRoot, "logs"); diff --git a/packages/workflow/src/engine/index.ts b/packages/workflow/src/engine/index.ts new file mode 100644 index 0000000..3ad776b --- /dev/null +++ b/packages/workflow/src/engine/index.ts @@ -0,0 +1,22 @@ +export { createWorkflow } from "./create-workflow.js"; +export { executeThread } from "./engine.js"; +export { + buildForkPlan, + parseThreadDataJsonl, + selectForkHistoricalSteps, + tryParseRoleStepRecord, + tryParseWorkflowResultRecord, +} from "./fork-thread.js"; +export { garbageCollectCas } from "./gc.js"; +export { createThreadPauseGate } from "./thread-pause-gate.js"; +export type { + ExecuteThreadIo, + ExecuteThreadOptions, + ForkHistoricalStep, + ForkPlan, + GcResult, + ParsedThreadStartRecord, + PrefilledDiskStep, + ThreadPauseGate, +} from "./types.js"; +export { getWorkerHostScriptPath } from "./worker-entry-path.js"; diff --git a/packages/workflow/src/engine/thread-pause-gate.ts b/packages/workflow/src/engine/thread-pause-gate.ts index 109d8b2..6a14aef 100644 --- a/packages/workflow/src/engine/thread-pause-gate.ts +++ b/packages/workflow/src/engine/thread-pause-gate.ts @@ -1,11 +1,6 @@ -import { err, ok, type Result } from "../util/result.js"; +import { err, ok, type Result } from "../util/index.js"; -export type ThreadPauseGate = { - awaitAfterYield: () => Promise; - pause: () => Result; - resume: () => Result; - isPaused: () => boolean; -}; +import type { ThreadPauseGate } from "./types.js"; /** * Pause/resume gate for workflow threads: after each generator yield the engine awaits diff --git a/packages/workflow/src/engine/types.ts b/packages/workflow/src/engine/types.ts new file mode 100644 index 0000000..a99ac01 --- /dev/null +++ b/packages/workflow/src/engine/types.ts @@ -0,0 +1,71 @@ +import type { CasStore } from "../cas/index.js"; +import type { RoleOutput } from "../types.js"; +import type { Result } from "../util/index.js"; + +export type ExecuteThreadIo = { + threadId: string; + hash: string; + dataJsonlPath: string; + infoJsonlPath: string; + cas: CasStore; +}; + +/** One persisted role line in `.data.jsonl` (engine adds these for fork replay before running the generator). */ +export type PrefilledDiskStep = { + role: string; + contentHash: string; + meta: Record; + refs: string[]; + timestamp: number; +}; + +export type ExecuteThreadOptions = { + maxRounds: number; + /** Passed to the bundle as `WorkflowFnOptions.depth`. */ + depth: number; + signal: AbortSignal; + /** Invoked after each successful yield (and outer-loop checks); used for pause/resume. */ + awaitAfterEachYield: () => Promise; + /** When non-null, written into the start record so tooling can trace lineage. */ + forkSourceThreadId: string | null; + /** + * Written to `.data.jsonl` immediately after the start record, before the generator runs. + * Must match `input.steps` length and order when present. + */ + prefilledDiskSteps: PrefilledDiskStep[] | null; +}; + +/** Role steps replayed from `.data.jsonl`, including persisted timestamps. */ +export type ForkHistoricalStep = RoleOutput & { timestamp: number }; + +export type ParsedThreadStartRecord = { + workflowName: string; + hash: string; + threadId: string; + prompt: string; + maxRounds: number; + depth: number; +}; + +export type ForkPlan = { + workflowName: string; + hash: string; + sourceThreadId: string; + prompt: string; + runOptions: { maxRounds: number; depth: number }; + historicalSteps: ForkHistoricalStep[]; +}; + +export type GcResult = { + scannedThreads: number; + activeRefs: number; + deletedEntries: number; + deletedHashes: string[]; +}; + +export type ThreadPauseGate = { + awaitAfterYield: () => Promise; + pause: () => Result; + resume: () => Result; + isPaused: () => boolean; +}; diff --git a/packages/workflow/src/engine/worker.ts b/packages/workflow/src/engine/worker.ts index ce62cb3..e98ee80 100644 --- a/packages/workflow/src/engine/worker.ts +++ b/packages/workflow/src/engine/worker.ts @@ -1,17 +1,20 @@ import { appendFile, mkdir, unlink, writeFile } from "node:fs/promises"; import { createServer, type Socket } from "node:net"; import { dirname, join } from "node:path"; -import { importWorkflowBundleModule } from "../bundle/bundle-import-env.js"; -import { ensureUncagedWorkflowSymlink } from "../bundle/ensure-uncaged-workflow-symlink.js"; -import { createCasStore } from "../cas/cas.js"; +import { ensureUncagedWorkflowSymlink, importWorkflowBundleModule } from "../bundle/index.js"; +import { createCasStore } from "../cas/index.js"; import type { RoleOutput, WorkflowFn, WorkflowResult } from "../types.js"; -import { createLogger } from "../util/logger.js"; -import { normalizeRefsField } from "../util/refs-field.js"; -import { err, ok, type Result } from "../util/result.js"; -import { getGlobalCasDir } from "../util/storage-root.js"; -import type { PrefilledDiskStep } from "./engine.js"; -import { type ExecuteThreadIo, executeThread } from "./engine.js"; -import { createThreadPauseGate, type ThreadPauseGate } from "./thread-pause-gate.js"; +import { + createLogger, + err, + getGlobalCasDir, + normalizeRefsField, + ok, + type Result, +} from "../util/index.js"; +import { executeThread } from "./engine.js"; +import { createThreadPauseGate } from "./thread-pause-gate.js"; +import type { ExecuteThreadIo, PrefilledDiskStep, ThreadPauseGate } from "./types.js"; const bootLog = createLogger({ sink: { kind: "stderr" } }); diff --git a/packages/workflow/src/extract-provider.ts b/packages/workflow/src/extract-provider.ts index 61ec220..215ece7 100644 --- a/packages/workflow/src/extract-provider.ts +++ b/packages/workflow/src/extract-provider.ts @@ -1,8 +1,7 @@ -import { readWorkflowRegistry } from "./registry/registry.js"; -import type { WorkflowConfig } from "./registry/registry-types.js"; +import type { WorkflowConfig } from "./registry/index.js"; +import { readWorkflowRegistry } from "./registry/index.js"; import type { LlmProvider } from "./types.js"; -import { err, ok, type Result } from "./util/result.js"; -import { getDefaultWorkflowStorageRoot } from "./util/storage-root.js"; +import { err, getDefaultWorkflowStorageRoot, ok, type Result } from "./util/index.js"; const DEFAULT_WORKFLOW_AS_AGENT_MAX_DEPTH = 3; diff --git a/packages/workflow/src/extract/extract-fn.ts b/packages/workflow/src/extract/extract-fn.ts index 19931d7..86da016 100644 --- a/packages/workflow/src/extract/extract-fn.ts +++ b/packages/workflow/src/extract/extract-fn.ts @@ -1,13 +1,9 @@ import type * as z from "zod/v4"; -import { getContentMerklePayload } from "../cas/merkle.js"; + +import { getContentMerklePayload } from "../cas/index.js"; import type { ExtractContext, LlmProvider } from "../types.js"; import { llmExtractWithRetry } from "./llm-extract.js"; - -export type ExtractFn = >( - schema: z.ZodType, - prompt: string, - ctx: ExtractContext, -) => Promise; +import type { ExtractFn } from "./types.js"; /** Builds the user-side extraction prompt (thread + agent output + instruction). */ export async function buildExtractUserContent( diff --git a/packages/workflow/src/extract/index.ts b/packages/workflow/src/extract/index.ts new file mode 100644 index 0000000..40b650d --- /dev/null +++ b/packages/workflow/src/extract/index.ts @@ -0,0 +1,17 @@ +export { + buildExtractUserContent, + createExtract, +} from "./extract-fn.js"; +export { + extractFunctionToolFromZodSchema, + llmErrorToCause, + llmExtract, + llmExtractWithRetry, +} from "./llm-extract.js"; +export { reactExtract } from "./react-extract.js"; +export type { + ExtractFn, + LlmError, + LlmExtractArgs, + ReactExtractArgs, +} from "./types.js"; diff --git a/packages/workflow/src/extract/llm-extract.ts b/packages/workflow/src/extract/llm-extract.ts index b1d90df..ca9a4f3 100644 --- a/packages/workflow/src/extract/llm-extract.ts +++ b/packages/workflow/src/extract/llm-extract.ts @@ -1,20 +1,8 @@ import * as z from "zod/v4"; -import type { LlmProvider } from "../types.js"; -import { err, ok, type Result } from "../util/result.js"; -export type LlmExtractArgs = { - text: string; - schema: z.ZodType; - provider: LlmProvider; -}; +import { err, ok, type Result } from "../util/index.js"; -export type LlmError = - | { kind: "http_error"; status: number; body: string } - | { kind: "invalid_response_json"; message: string } - | { kind: "no_tool_call"; preview: string } - | { kind: "tool_arguments_invalid_json"; message: string } - | { kind: "schema_validation_failed"; message: string } - | { kind: "network_error"; message: string }; +import type { LlmError, LlmExtractArgs } from "./types.js"; function chatCompletionsUrl(baseUrl: string): string { const trimmed = baseUrl.replace(/\/+$/, ""); diff --git a/packages/workflow/src/extract/react-extract.ts b/packages/workflow/src/extract/react-extract.ts index 732907b..5801b20 100644 --- a/packages/workflow/src/extract/react-extract.ts +++ b/packages/workflow/src/extract/react-extract.ts @@ -1,16 +1,11 @@ import type * as z from "zod/v4"; -import type { CasStore } from "../cas/cas.js"; +import type { CasStore } from "../cas/index.js"; import type { LlmProvider } from "../types.js"; -import { err, ok, type Result } from "../util/result.js"; -import { extractFunctionToolFromZodSchema } from "./llm-extract.js"; +import { err, ok, type Result } from "../util/index.js"; -export type ReactExtractArgs> = { - text: string; - schema: z.ZodType; - provider: LlmProvider; - cas: CasStore; -}; +import { extractFunctionToolFromZodSchema } from "./llm-extract.js"; +import type { ReactExtractArgs } from "./types.js"; const MAX_REACT_ROUNDS = 10; diff --git a/packages/workflow/src/extract/types.ts b/packages/workflow/src/extract/types.ts new file mode 100644 index 0000000..011db70 --- /dev/null +++ b/packages/workflow/src/extract/types.ts @@ -0,0 +1,31 @@ +import type * as z from "zod/v4"; + +import type { CasStore } from "../cas/index.js"; +import type { ExtractContext, LlmProvider } from "../types.js"; + +export type ExtractFn = >( + schema: z.ZodType, + prompt: string, + ctx: ExtractContext, +) => Promise; + +export type ReactExtractArgs> = { + text: string; + schema: z.ZodType; + provider: LlmProvider; + cas: CasStore; +}; + +export type LlmExtractArgs = { + text: string; + schema: z.ZodType; + provider: LlmProvider; +}; + +export type LlmError = + | { kind: "http_error"; status: number; body: string } + | { kind: "invalid_response_json"; message: string } + | { kind: "no_tool_call"; preview: string } + | { kind: "tool_arguments_invalid_json"; message: string } + | { kind: "schema_validation_failed"; message: string } + | { kind: "network_error"; message: string }; diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index 4dfbdbd..262e919 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -1,24 +1,23 @@ -export { buildDescriptor } from "./bundle/build-descriptor.js"; -export { - validateWorkflowBundle, - type WorkflowBundleValidationInput, -} from "./bundle/bundle-validator.js"; export { + buildDescriptor, type ExtractedBundleExports, extractBundleExports, -} from "./bundle/extract-bundle-exports.js"; -export { stringifyWorkflowDescriptor } from "./bundle/generate-descriptor.js"; -export { + stringifyWorkflowDescriptor, + validateWorkflowBundle, validateWorkflowDescriptor, + type WorkflowBundleValidationInput, type WorkflowDescriptor, type WorkflowRoleDescriptor, type WorkflowRoleSchema, -} from "./bundle/workflow-descriptor.js"; -export { type CasStore, createCasStore, createThreadCas } from "./cas/cas.js"; -export { hashString, hashWorkflowBundleBytes } from "./cas/hash.js"; +} from "./bundle/index.js"; export { + type CasStore, + createCasStore, createContentMerkleNode, + createThreadCas, getContentMerklePayload, + hashString, + hashWorkflowBundleBytes, type MerkleNode, type MerkleNodeType, parseMerkleNode, @@ -28,35 +27,37 @@ export { type StepMerklePayload, serializeMerkleNode, type ThreadMerklePayload, -} from "./cas/merkle.js"; -export { createWorkflow } from "./engine/create-workflow.js"; +} from "./cas/index.js"; export { + buildForkPlan, + createThreadPauseGate, + createWorkflow, type ExecuteThreadIo, type ExecuteThreadOptions, executeThread, - type PrefilledDiskStep, -} from "./engine/engine.js"; -export { - buildForkPlan, type ForkHistoricalStep, type ForkPlan, + type GcResult, + garbageCollectCas, + getWorkerHostScriptPath, type ParsedThreadStartRecord, + type PrefilledDiskStep, parseThreadDataJsonl, selectForkHistoricalSteps, + type ThreadPauseGate, tryParseRoleStepRecord, tryParseWorkflowResultRecord, -} from "./engine/fork-thread.js"; -export { type GcResult, garbageCollectCas } from "./engine/gc.js"; -export { createThreadPauseGate, type ThreadPauseGate } from "./engine/thread-pause-gate.js"; -export { getWorkerHostScriptPath } from "./engine/worker-entry-path.js"; -export { createExtract, type ExtractFn } from "./extract/extract-fn.js"; +} from "./engine/index.js"; export { + createExtract, + type ExtractFn, type LlmError, llmErrorToCause, llmExtract, llmExtractWithRetry, -} from "./extract/llm-extract.js"; -export { type ReactExtractArgs, reactExtract } from "./extract/react-extract.js"; + type ReactExtractArgs, + reactExtract, +} from "./extract/index.js"; export { getExtractProvider } from "./extract-provider.js"; export { type ExtractProviderConfig, @@ -74,7 +75,7 @@ export { type WorkflowRegistryFile, workflowRegistryPath, writeWorkflowRegistry, -} from "./registry/registry.js"; +} from "./registry/index.js"; export { type AgentBinding, type AgentContext, @@ -101,18 +102,19 @@ export { } from "./types.js"; export { CROCKFORD_BASE32_ALPHABET, + type CreateLoggerOptions, + createLogger, decodeCrockfordBase32Bits, decodeCrockfordToUint64, encodeCrockfordBase32Bits, encodeUint64AsCrockford, -} from "./util/base32.js"; -export { - type CreateLoggerOptions, - createLogger, + err, + generateUlid, + getDefaultWorkflowStorageRoot, + getGlobalCasDir, type LogFn, type LoggerSink, -} from "./util/logger.js"; -export { err, ok, type Result } from "./util/result.js"; -export { getDefaultWorkflowStorageRoot, getGlobalCasDir } from "./util/storage-root.js"; -export { generateUlid } from "./util/ulid.js"; + ok, + type Result, +} from "./util/index.js"; export { type WorkflowAsAgentOptions, workflowAsAgent } from "./workflow-as-agent.js"; diff --git a/packages/workflow/src/registry/index.ts b/packages/workflow/src/registry/index.ts new file mode 100644 index 0000000..6ee97d3 --- /dev/null +++ b/packages/workflow/src/registry/index.ts @@ -0,0 +1,19 @@ +export { + getRegisteredWorkflow, + listRegisteredWorkflowNames, + parseWorkflowRegistryYaml, + readWorkflowRegistry, + registerWorkflowVersion, + rollbackWorkflowToHistoryHash, + stringifyWorkflowRegistryYaml, + unregisterWorkflow, + workflowRegistryPath, + writeWorkflowRegistry, +} from "./registry.js"; +export type { + ExtractProviderConfig, + WorkflowConfig, + WorkflowHistoryEntry, + WorkflowRegistryEntry, + WorkflowRegistryFile, +} from "./types.js"; diff --git a/packages/workflow/src/registry/registry-normalize.ts b/packages/workflow/src/registry/registry-normalize.ts index 9dd0020..5d70c09 100644 --- a/packages/workflow/src/registry/registry-normalize.ts +++ b/packages/workflow/src/registry/registry-normalize.ts @@ -1,11 +1,11 @@ -import { err, ok, type Result } from "../util/result.js"; +import { err, ok, type Result } from "../util/index.js"; import type { ExtractProviderConfig, WorkflowConfig, WorkflowHistoryEntry, WorkflowRegistryEntry, WorkflowRegistryFile, -} from "./registry-types.js"; +} from "./types.js"; function resolveRegistryApiKey(raw: string): Result { if (raw.startsWith("env:")) { diff --git a/packages/workflow/src/registry/registry.ts b/packages/workflow/src/registry/registry.ts index 1a9a593..947bc4f 100644 --- a/packages/workflow/src/registry/registry.ts +++ b/packages/workflow/src/registry/registry.ts @@ -2,21 +2,9 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; import { parseDocument, stringify } from "yaml"; -import { err, ok, type Result } from "../util/result.js"; +import { err, ok, type Result } from "../util/index.js"; import { normalizeWorkflowRegistryRoot } from "./registry-normalize.js"; -import type { - WorkflowHistoryEntry, - WorkflowRegistryEntry, - WorkflowRegistryFile, -} from "./registry-types.js"; - -export type { - ExtractProviderConfig, - WorkflowConfig, - WorkflowHistoryEntry, - WorkflowRegistryEntry, - WorkflowRegistryFile, -} from "./registry-types.js"; +import type { WorkflowHistoryEntry, WorkflowRegistryEntry, WorkflowRegistryFile } from "./types.js"; export function workflowRegistryPath(storageRoot: string): string { return join(storageRoot, "workflow.yaml"); diff --git a/packages/workflow/src/registry/registry-types.ts b/packages/workflow/src/registry/types.ts similarity index 100% rename from packages/workflow/src/registry/registry-types.ts rename to packages/workflow/src/registry/types.ts diff --git a/packages/workflow/src/types.ts b/packages/workflow/src/types.ts index e68aa6f..0536c7f 100644 --- a/packages/workflow/src/types.ts +++ b/packages/workflow/src/types.ts @@ -1,6 +1,6 @@ import type * as z from "zod/v4"; -import type { CasStore } from "./cas/cas.js"; +import type { CasStore } from "./cas/index.js"; /** Sentinel values for automaton control flow. */ export const START = "__start__" as const; diff --git a/packages/workflow/src/util/base32.ts b/packages/workflow/src/util/base32.ts index f7cd939..330fa14 100644 --- a/packages/workflow/src/util/base32.ts +++ b/packages/workflow/src/util/base32.ts @@ -1,4 +1,5 @@ -import { err, ok, type Result } from "./result.js"; +import { err, ok } from "./result.js"; +import type { Result } from "./types.js"; /** Crockford Base32 alphabet (no I, L, O, U) — exactly 32 symbols. */ export const CROCKFORD_BASE32_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; diff --git a/packages/workflow/src/util/index.ts b/packages/workflow/src/util/index.ts new file mode 100644 index 0000000..85ee59b --- /dev/null +++ b/packages/workflow/src/util/index.ts @@ -0,0 +1,13 @@ +export { + CROCKFORD_BASE32_ALPHABET, + decodeCrockfordBase32Bits, + decodeCrockfordToUint64, + encodeCrockfordBase32Bits, + encodeUint64AsCrockford, +} from "./base32.js"; +export { createLogger } from "./logger.js"; +export { mergeRefsWithContentHash, normalizeRefsField } from "./refs-field.js"; +export { err, ok } from "./result.js"; +export { getDefaultWorkflowStorageRoot, getGlobalCasDir } from "./storage-root.js"; +export type { CreateLoggerOptions, LogFn, LoggerSink, Result } from "./types.js"; +export { generateUlid } from "./ulid.js"; diff --git a/packages/workflow/src/util/logger.ts b/packages/workflow/src/util/logger.ts index 26a019c..2305def 100644 --- a/packages/workflow/src/util/logger.ts +++ b/packages/workflow/src/util/logger.ts @@ -1,6 +1,7 @@ import { appendFileSync } from "node:fs"; import { CROCKFORD_BASE32_ALPHABET } from "./base32.js"; +import type { CreateLoggerOptions, LogFn } from "./types.js"; const TAG_LENGTH = 8; @@ -22,14 +23,6 @@ function assertValidLogTag(tag: string): void { } } -export type LoggerSink = { kind: "stderr" } | { kind: "file"; path: string }; - -export type CreateLoggerOptions = { - sink: LoggerSink; -}; - -export type LogFn = (tag: string, content: string) => void; - /** Append one JSONL log record: `{ tag, content, timestamp }` per RFC-001. */ export function createLogger(options: CreateLoggerOptions): LogFn { if (options.sink.kind === "stderr") { diff --git a/packages/workflow/src/util/result.ts b/packages/workflow/src/util/result.ts index 39e40a2..6f92f67 100644 --- a/packages/workflow/src/util/result.ts +++ b/packages/workflow/src/util/result.ts @@ -1,4 +1,4 @@ -export type Result = { ok: true; value: T } | { ok: false; error: E }; +import type { Result } from "./types.js"; export function ok(value: T): Result { return { ok: true, value }; diff --git a/packages/workflow/src/util/types.ts b/packages/workflow/src/util/types.ts new file mode 100644 index 0000000..e55327e --- /dev/null +++ b/packages/workflow/src/util/types.ts @@ -0,0 +1,9 @@ +export type Result = { ok: true; value: T } | { ok: false; error: E }; + +export type LoggerSink = { kind: "stderr" } | { kind: "file"; path: string }; + +export type CreateLoggerOptions = { + sink: LoggerSink; +}; + +export type LogFn = (tag: string, content: string) => void; diff --git a/packages/workflow/src/workflow-as-agent.ts b/packages/workflow/src/workflow-as-agent.ts index 8bce743..48a3dac 100644 --- a/packages/workflow/src/workflow-as-agent.ts +++ b/packages/workflow/src/workflow-as-agent.ts @@ -1,14 +1,18 @@ import { join } from "node:path"; -import { extractBundleExports } from "./bundle/extract-bundle-exports.js"; -import { createCasStore } from "./cas/cas.js"; -import { type ExecuteThreadIo, executeThread } from "./engine/engine.js"; +import { extractBundleExports } from "./bundle/index.js"; +import { createCasStore } from "./cas/index.js"; +import type { ExecuteThreadIo } from "./engine/index.js"; +import { executeThread } from "./engine/index.js"; import { getWorkflowAsAgentMaxDepth } from "./extract-provider.js"; -import { getRegisteredWorkflow, readWorkflowRegistry } from "./registry/registry.js"; +import { getRegisteredWorkflow, readWorkflowRegistry } from "./registry/index.js"; import type { AgentContext, AgentFn, ThreadInput } from "./types.js"; -import { createLogger } from "./util/logger.js"; -import { getDefaultWorkflowStorageRoot, getGlobalCasDir } from "./util/storage-root.js"; -import { generateUlid } from "./util/ulid.js"; +import { + createLogger, + generateUlid, + getDefaultWorkflowStorageRoot, + getGlobalCasDir, +} from "./util/index.js"; export type WorkflowAsAgentOptions = { /** When `null`, uses `getDefaultWorkflowStorageRoot()`. */