diff --git a/.changeset/calm-cats-flow.md b/.changeset/calm-cats-flow.md new file mode 100644 index 000000000..1c0edf3f6 --- /dev/null +++ b/.changeset/calm-cats-flow.md @@ -0,0 +1,5 @@ +--- +"eve": patch +--- + +Update the vendored Workflow SDK packages to the latest 5.0 beta releases. eve now delegates world target selection and construction to the upstream SDK instead of maintaining parallel factory and compatibility logic, and no longer disables stable Workflow Turbo mode. diff --git a/docs/agent-config.md b/docs/agent-config.md index a61a87a92..eddccabdb 100644 --- a/docs/agent-config.md +++ b/docs/agent-config.md @@ -191,7 +191,7 @@ pnpm add @workflow/world-postgres@5.0.0-beta.x ``` The npm `latest` tag can lag behind that line, so an unpinned install may pull -an incompatible major that fails with `ZodError: invalid_union` at run replay. +an incompatible protocol version that the Workflow SDK rejects during initialization. Put credentials and host-specific options in runtime environment variables read by the world package, not in `agent.ts`. For the Postgres world, that means diff --git a/docs/concepts/execution-model-and-durability.md b/docs/concepts/execution-model-and-durability.md index 5db410771..64014d3ff 100644 --- a/docs/concepts/execution-model-and-durability.md +++ b/docs/concepts/execution-model-and-durability.md @@ -36,7 +36,7 @@ export default defineAgent({ }); ``` -The world package backs workflow state, queues, hooks, and streams. Keep secrets and deployment-specific options in runtime environment variables read by that package, not in `agent.ts`. The selected world must match eve's bundled `@workflow/*` line (currently the `5.0.0-beta` line); pin it explicitly, since a mismatched world fails with a `ZodError: invalid_union` during run replay. See the [deployment guide](../guides/deployment#8-deploy-without-vercel) for the install command, plus [agent.ts](../agent-config#workflow-world) and [Workflow Worlds](https://workflow-sdk.dev/worlds). +The world package backs workflow state, queues, hooks, and streams. Keep secrets and deployment-specific options in runtime environment variables read by that package, not in `agent.ts`. Custom worlds must implement the runtime protocol expected by eve's vendored `@workflow/*` packages (currently the `5.0.0-beta` line); the Workflow SDK rejects incompatible protocol versions during initialization. See the [deployment guide](../guides/deployment#8-deploy-without-vercel) for the install command, plus [agent.ts](../agent-config#workflow-world) and [Workflow Worlds](https://workflow-sdk.dev/worlds). ## Resuming after a crash diff --git a/docs/guides/deployment.md b/docs/guides/deployment.md index ccd0066b2..2183debdd 100644 --- a/docs/guides/deployment.md +++ b/docs/guides/deployment.md @@ -140,7 +140,7 @@ Eve writes the standard Nitro output under `.output/` instead of Vercel Build Ou Self-deployed agents should make the Vercel-specific choices explicit: -- Let the Workflow SDK use its default local world, which stores workflow state under `.workflow-data`, configure your host so that directory is on persistent storage, or select another world with `experimental.workflow.world` in the root `agent.ts`. When you select a custom world, install a world package built against the same `@workflow/*` line as your eve release (currently the `5.0.0-beta` line). The npm `latest` tag may lag, so pin the version explicitly, for example `pnpm add @workflow/world-postgres@5.0.0-beta.x`. A mismatched world (such as a `4.x` package against a `5.x` core) fails with a `ZodError: invalid_union` during run replay. +- Let the Workflow SDK use its default local world, which stores workflow state under `.workflow-data`, configure your host so that directory is on persistent storage, or select another world with `experimental.workflow.world` in the root `agent.ts`. When you select a custom world, install a world package built against the same `@workflow/*` line as your eve release (currently the `5.0.0-beta` line). The npm `latest` tag may lag, so pin the version explicitly, for example `pnpm add @workflow/world-postgres@5.0.0-beta.x`. The Workflow SDK rejects worlds with an incompatible runtime protocol during initialization. - If you put a reverse proxy or ingress in front of eve, forward **both** `/eve/` and `/.well-known/workflow/`. The workflow world delivers run callbacks to `/.well-known/workflow/v1/flow`; a proxy restricted to `/eve/` lets sessions start but silently stalls runs forever, because the callbacks never reach eve. - Install the AI SDK package for your provider, then use a direct provider model object and `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` when you want no Gateway dependency. - Use `AI_GATEWAY_API_KEY` if you still want Gateway routing from a non-Vercel host. diff --git a/packages/eve/package.json b/packages/eve/package.json index 055ec717c..a97f2a580 100644 --- a/packages/eve/package.json +++ b/packages/eve/package.json @@ -314,11 +314,13 @@ "@vercel/oidc": "3.5.0", "@vercel/sandbox": "catalog:", "@vercel/sdk": "1.28.1", - "@workflow/core": "5.0.0-beta.26", - "@workflow/errors": "5.0.0-beta.8", + "@workflow/core": "5.0.0-beta.28", + "@workflow/errors": "5.0.0-beta.10", "@workflow/serde": "5.0.0-beta.2", - "@workflow/world": "5.0.0-beta.14", - "@workflow/world-local": "5.0.0-beta.22", + "@workflow/utils": "5.0.0-beta.6", + "@workflow/world": "5.0.0-beta.16", + "@workflow/world-local": "5.0.0-beta.24", + "@workflow/world-vercel": "5.0.0-beta.24", "ai": "catalog:", "autoevals": "0.0.132", "chat": "4.31.0", diff --git a/packages/eve/scripts/vendor-compiled/@workflow/world-local.mjs b/packages/eve/scripts/vendor-compiled/@workflow/world-local.mjs new file mode 100644 index 000000000..dbd48b6b3 --- /dev/null +++ b/packages/eve/scripts/vendor-compiled/@workflow/world-local.mjs @@ -0,0 +1,57 @@ +import { readFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; + +const require = createRequire(import.meta.url); + +function resolveWorkflowWorldLocalVersion() { + let currentPath = dirname(require.resolve("@workflow/world-local")); + + while (true) { + const manifestPath = join(currentPath, "package.json"); + + try { + const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); + + if (manifest.name === "@workflow/world-local" && typeof manifest.version === "string") { + return manifest.version; + } + } catch { + // Keep walking toward the package root. + } + + const parentPath = dirname(currentPath); + + if (parentPath === currentPath) { + throw new Error("Failed to resolve @workflow/world-local package version."); + } + + currentPath = parentPath; + } +} + +const workflowWorldLocalVersion = resolveWorkflowWorldLocalVersion(); + +const workflowWorldLocalVersionPlugin = { + name: "eve-workflow-world-local-version", + transform(source, id) { + if (!id.endsWith("/@workflow/world-local/dist/init.js")) { + return undefined; + } + + return { + code: source.replace( + "version: 'bundled',", + `version: ${JSON.stringify(workflowWorldLocalVersion)},`, + ), + map: null, + }; + }, +}; + +export default { + packageName: "@workflow/world-local", + compiledPath: "@workflow/world-local", + chunkGroup: "workflow", + plugins: [workflowWorldLocalVersionPlugin], +}; diff --git a/packages/eve/scripts/vendor-compiled/@workflow/world-vercel.mjs b/packages/eve/scripts/vendor-compiled/@workflow/world-vercel.mjs new file mode 100644 index 000000000..8aaf250cd --- /dev/null +++ b/packages/eve/scripts/vendor-compiled/@workflow/world-vercel.mjs @@ -0,0 +1,5 @@ +export default { + packageName: "@workflow/world-vercel", + compiledPath: "@workflow/world-vercel", + chunkGroup: "workflow", +}; diff --git a/packages/eve/scripts/vendor-compiled/index.mjs b/packages/eve/scripts/vendor-compiled/index.mjs index 92e662cf2..747c26258 100644 --- a/packages/eve/scripts/vendor-compiled/index.mjs +++ b/packages/eve/scripts/vendor-compiled/index.mjs @@ -24,6 +24,8 @@ import workflowCore from "./@workflow/core.mjs"; import workflowErrors from "./@workflow/errors.mjs"; import workflowSerde from "./@workflow/serde.mjs"; import workflowWorld from "./@workflow/world.mjs"; +import workflowWorldLocal from "./@workflow/world-local.mjs"; +import workflowWorldVercel from "./@workflow/world-vercel.mjs"; import chat from "./chat.mjs"; import chokidar from "./chokidar.mjs"; @@ -72,6 +74,8 @@ export const MODULES = [ workflowErrors, workflowSerde, workflowWorld, + workflowWorldLocal, + workflowWorldVercel, zod, zodValidationError, ]; diff --git a/packages/eve/src/internal/application/compiled-artifacts.test.ts b/packages/eve/src/internal/application/compiled-artifacts.test.ts index 1def18309..5b2e13b34 100644 --- a/packages/eve/src/internal/application/compiled-artifacts.test.ts +++ b/packages/eve/src/internal/application/compiled-artifacts.test.ts @@ -1,66 +1,42 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; -import { COMPILE_METADATA_KIND, COMPILE_METADATA_VERSION } from "#compiler/artifacts.js"; -import type { CompileAgentResult } from "#compiler/compile-agent.js"; -import { createCompiledAgentManifest } from "#compiler/manifest.js"; -import { createCompiledArtifactsBootstrapSource } from "#internal/application/compiled-artifacts.js"; -import { classifyModelRouting } from "#internal/classify-model-routing.js"; +import { createWorkflowWorldPluginSource } from "#internal/application/compiled-artifacts.js"; -describe("createCompiledArtifactsBootstrapSource", () => { - it("generates static Workflow world bootstrap imports from compiled config", async () => { - const source = await createCompiledArtifactsBootstrapSource({ - compileResult: { - manifest: createCompiledAgentManifest({ - agentRoot: "/app/agent", - appRoot: "/app", - config: { - model: { - id: "openai/gpt-5.5", - routing: classifyModelRouting("openai/gpt-5.5"), - }, - name: "app", - experimental: { - workflow: { - world: "@acme/eve-world", - }, - }, - }, - }), - metadata: { - compile: { moduleMap: { path: "module-map.mjs", sha256: "0" } }, - discovery: { - diagnostics: { path: "diagnostics.json", sha256: "0" }, - manifest: { path: "manifest.json", sha256: "0" }, - sourceGraphHash: "0", - summary: { errors: 0, warnings: 0 }, - }, - generator: { name: "eve", version: "0.0.0-test" }, - kind: COMPILE_METADATA_KIND, - status: "ready", - version: COMPILE_METADATA_VERSION, - }, - } as CompileAgentResult, - installModulePath: "/eve/src/runtime/loaders/bundled-artifacts.ts", - metadata: { - compile: { moduleMap: { path: "module-map.mjs", sha256: "0" } }, - discovery: { - diagnostics: { path: "diagnostics.json", sha256: "0" }, - manifest: { path: "manifest.json", sha256: "0" }, - sourceGraphHash: "0", - summary: { errors: 0, warnings: 0 }, - }, - generator: { name: "eve", version: "0.0.0-test" }, - kind: COMPILE_METADATA_KIND, - status: "ready", - version: COMPILE_METADATA_VERSION, - }, - moduleMapPath: "/app/.eve/compile/compiled-artifacts-bootstrap.mjs", - }); +describe("createWorkflowWorldPluginSource", () => { + afterEach(() => { + delete process.env.VERCEL_DEPLOYMENT_ID; + delete process.env.WORKFLOW_TARGET_WORLD; + }); + it("imports a configured world package and delegates its construction to Workflow", () => { + const source = createWorkflowWorldPluginSource( + "@acme/eve-world", + "/app/.eve/compile/compiled-artifacts-bootstrap.mjs", + ); + + expect(source).toContain('import "/app/.eve/compile/compiled-artifacts-bootstrap.mjs";'); expect(source).toContain('import * as workflowWorldModule from "@acme/eve-world";'); - expect(source).toContain('installEveWorkflowQueueNamespace("app");'); + expect(source).toContain("import { validateWorkflowWorld } from "); expect(source).toContain( - 'await installConfiguredWorkflowWorld({ module: workflowWorldModule, packageName: "@acme/eve-world" });', + "const workflowWorld = await createWorldFromModule(workflowWorldModule);", + ); + expect(source).toContain( + 'validateWorkflowWorld({ packageName: "@acme/eve-world", world: workflowWorld });', + ); + expect(source).toContain("setWorld(workflowWorld);"); + expect(source).toContain("await getWorld();"); + expect(source).toContain("await workflowWorld.start?.();"); + }); + + it("selects vendored local and Vercel world packages with Workflow's selector", () => { + expect(createWorkflowWorldPluginSource(undefined)).toContain( + "/compiled/@workflow/world-local/index.js", + ); + + process.env.VERCEL_DEPLOYMENT_ID = "deployment-id"; + + expect(createWorkflowWorldPluginSource(undefined)).toContain( + "/compiled/@workflow/world-vercel/index.js", ); }); }); diff --git a/packages/eve/src/internal/application/compiled-artifacts.ts b/packages/eve/src/internal/application/compiled-artifacts.ts index 925f40686..bce21b0be 100644 --- a/packages/eve/src/internal/application/compiled-artifacts.ts +++ b/packages/eve/src/internal/application/compiled-artifacts.ts @@ -5,8 +5,12 @@ import { join } from "node:path"; import type { CompileMetadata } from "#compiler/artifacts.js"; import type { CompileAgentResult } from "#compiler/compile-agent.js"; import { createCompiledModuleMapSource } from "#compiler/module-map.js"; +import { getWorldImport } from "@workflow/utils"; import { stringifyEsmImportSpecifier } from "#internal/application/import-specifier.js"; -import { resolvePackageSourceFilePath } from "#internal/application/package.js"; +import { + resolvePackageCompiledFilePath, + resolvePackageSourceFilePath, +} from "#internal/application/package.js"; import type { AgentWorkflowWorldDefinition } from "#shared/agent-definition.js"; /** @@ -19,6 +23,8 @@ export interface GeneratedCompiledArtifactsFiles { * workflow handlers. */ bootstrapPath: string; + /** Nitro plugin that installs the selected vendored Workflow world. */ + workflowWorldPluginPath: string; /** * Optional Nitro plugin that imports the authored instrumentation module * from the application when present. @@ -44,6 +50,7 @@ export async function writeCompiledArtifactsFiles(input: { }): Promise { const bootstrapPath = join(input.outDir, "compiled-artifacts-bootstrap.mjs"); const instrumentationPluginPath = join(input.outDir, "compiled-artifacts-instrumentation.mjs"); + const workflowWorldPluginPath = join(input.outDir, "compiled-artifacts-workflow-world.mjs"); const instrumentationPath = resolveInstrumentationModule(input.compileResult.manifest.agentRoot); await mkdir(input.outDir, { recursive: true }); @@ -56,6 +63,13 @@ export async function writeCompiledArtifactsFiles(input: { metadata: input.compileResult.metadata, }), ); + await writeFile( + workflowWorldPluginPath, + createWorkflowWorldPluginSource( + input.compileResult.manifest.config.experimental?.workflow?.world, + bootstrapPath, + ), + ); if (instrumentationPath !== undefined) { await writeFile( @@ -70,6 +84,7 @@ export async function writeCompiledArtifactsFiles(input: { const generatedArtifacts: GeneratedCompiledArtifactsFiles = { bootstrapPath, + workflowWorldPluginPath, }; if (instrumentationPath !== undefined) { @@ -109,7 +124,6 @@ export async function createCompiledArtifactsBootstrapSource(input: { metadata: CompileMetadata; moduleMapPath: string; }): Promise { - const workflowWorld = input.compileResult.manifest.config.experimental?.workflow?.world; const agentName = input.compileResult.manifest.config.name; const moduleMapSource = stripCompiledModuleMapExports( createCompiledModuleMapSource({ @@ -123,7 +137,6 @@ export async function createCompiledArtifactsBootstrapSource(input: { "// Generated by eve. Do not edit by hand.", `import { installBundledCompiledArtifacts } from ${stringifyEsmImportSpecifier(input.installModulePath)};`, `import { installEveWorkflowQueueNamespace } from ${stringifyEsmImportSpecifier(resolvePackageSourceFilePath("src/internal/workflow/queue-namespace.ts"))};`, - ...createWorkflowWorldBootstrapImports(workflowWorld), "", `installEveWorkflowQueueNamespace(${JSON.stringify(agentName)});`, "", @@ -142,7 +155,6 @@ export async function createCompiledArtifactsBootstrapSource(input: { "}", "", "installCompiledArtifactsBootstrap();", - ...createWorkflowWorldBootstrapBody(workflowWorld), "", "// Default export satisfies the Nitro plugin contract so this file", "// can be used directly as a Nitro plugin without a separate wrapper.", @@ -158,30 +170,44 @@ export async function createCompiledArtifactsBootstrapSource(input: { ].join("\n"); } -function createWorkflowWorldBootstrapImports( - world: AgentWorkflowWorldDefinition | undefined, -): string[] { - if (world === undefined) { - return []; - } - - return [ - `import * as workflowWorldModule from ${stringifyEsmImportSpecifier(world)};`, - `import { installConfiguredWorkflowWorld } from ${stringifyEsmImportSpecifier(resolvePackageSourceFilePath("src/internal/workflow/configure-world.ts"))};`, - ]; -} - -function createWorkflowWorldBootstrapBody( - world: AgentWorkflowWorldDefinition | undefined, -): string[] { - if (world === undefined) { - return []; - } +export function createWorkflowWorldPluginSource( + configuredWorld: AgentWorkflowWorldDefinition | undefined, + compiledArtifactsBootstrapPath?: string, +): string { + const packageName = getWorldImport( + configuredWorld === undefined + ? process.env + : { ...process.env, WORKFLOW_TARGET_WORLD: configuredWorld }, + ); + const importSpecifier = + packageName === "@workflow/world-local" || packageName === "@workflow/world-vercel" + ? resolvePackageCompiledFilePath(`src/compiled/${packageName}/index.js`) + : packageName; + const workflowRuntimeImportSpecifier = resolvePackageCompiledFilePath( + "src/compiled/@workflow/core/runtime.js", + ); + const workflowWorldValidationImportSpecifier = resolvePackageSourceFilePath( + "src/internal/workflow/validate-world.ts", + ); return [ + "// Generated by eve. Do not edit by hand.", + ...(compiledArtifactsBootstrapPath === undefined + ? [] + : [`import ${stringifyEsmImportSpecifier(compiledArtifactsBootstrapPath)};`]), + `import * as workflowWorldModule from ${stringifyEsmImportSpecifier(importSpecifier)};`, + `import { createWorldFromModule, getWorld, setWorld } from ${stringifyEsmImportSpecifier(workflowRuntimeImportSpecifier)};`, + `import { validateWorkflowWorld } from ${stringifyEsmImportSpecifier(workflowWorldValidationImportSpecifier)};`, "", - `await installConfiguredWorkflowWorld({ module: workflowWorldModule, packageName: ${JSON.stringify(world)} });`, - ]; + "const workflowWorld = await createWorldFromModule(workflowWorldModule);", + `validateWorkflowWorld({ packageName: ${JSON.stringify(configuredWorld)}, world: workflowWorld });`, + "setWorld(workflowWorld);", + "await getWorld();", + "await workflowWorld.start?.();", + "", + "export default function installWorkflowWorldPlugin() {}", + "", + ].join("\n"); } function createInstrumentationPluginSource(input: { diff --git a/packages/eve/src/internal/application/package.test.ts b/packages/eve/src/internal/application/package.test.ts index 137665b5f..0e29ca45a 100644 --- a/packages/eve/src/internal/application/package.test.ts +++ b/packages/eve/src/internal/application/package.test.ts @@ -25,8 +25,6 @@ describe("resolveWorkflowModulePath", () => { describe("resolveExpectedWorkflowVersion", () => { it("reads the @workflow/core line from eve's own package.json", () => { - // Single source of truth: eve declares the workflow line it bundles in its - // own package.json, so this resolves to a concrete prerelease version. expect(resolveExpectedWorkflowVersion()).toMatch(/^\d+\.\d+\.\d+/); }); }); diff --git a/packages/eve/src/internal/application/package.ts b/packages/eve/src/internal/application/package.ts index adf1bd81d..79e96adb2 100644 --- a/packages/eve/src/internal/application/package.ts +++ b/packages/eve/src/internal/application/package.ts @@ -161,7 +161,10 @@ export function resolvePackageDependencyPath(specifier: string): string { return require.resolve(specifier); } -function resolvePackageCompiledFilePath(relativeCompiledPath: string): string { +/** + * Resolves one vendored compiled asset from the current eve installation. + */ +export function resolvePackageCompiledFilePath(relativeCompiledPath: string): string { const packageBuildRoot = resolvePackageBuildRoot(); if (packageBuildRoot !== null) { @@ -278,11 +281,11 @@ function readWorkflowVersionFromManifest(value: unknown): string | undefined { * eve's own `package.json`. * * This is the single source of truth for the `@workflow/*` line eve targets, so - * compatibility checks (see `assertWorkflowWorldCompatibility`) never hardcode a - * version. eve's `package.json` is published with its `devDependencies` intact - * even though those packages are vendored, so the entry is readable from an - * installed eve as well as a source checkout. Returns `undefined` when the - * entry cannot be read so callers can no-op rather than fail. + * compatibility checks never hardcode a version. eve's `package.json` is + * published with its `devDependencies` intact even though those packages are + * vendored, so the entry is readable from an installed eve as well as a source + * checkout. Returns `undefined` when the entry cannot be read so callers can + * no-op rather than fail. */ export function resolveExpectedWorkflowVersion(): string | undefined { const packageRoot = tryResolvePackageRoot(); diff --git a/packages/eve/src/internal/nitro/host/build-application.scenario.test.ts b/packages/eve/src/internal/nitro/host/build-application.scenario.test.ts index a69c7c764..5936a3108 100644 --- a/packages/eve/src/internal/nitro/host/build-application.scenario.test.ts +++ b/packages/eve/src/internal/nitro/host/build-application.scenario.test.ts @@ -116,6 +116,12 @@ function createPreparedHost(appRoot: string): PreparedApplicationHost { } as unknown as PreparedApplicationHost["compileResult"], compiledArtifacts: { bootstrapPath: join(appRoot, ".eve", "compile", "compiled-artifacts-bootstrap.mjs"), + workflowWorldPluginPath: join( + appRoot, + ".eve", + "compile", + "compiled-artifacts-workflow-world.mjs", + ), } as PreparedApplicationHost["compiledArtifacts"], scheduleRegistrations: [], schedules: [], diff --git a/packages/eve/src/internal/nitro/host/configure-nitro-routes.test.ts b/packages/eve/src/internal/nitro/host/configure-nitro-routes.test.ts index 4b0846432..1676858eb 100644 --- a/packages/eve/src/internal/nitro/host/configure-nitro-routes.test.ts +++ b/packages/eve/src/internal/nitro/host/configure-nitro-routes.test.ts @@ -37,6 +37,7 @@ interface PreparedApplicationHostStub { }; compiledArtifacts: { bootstrapPath: string; + workflowWorldPluginPath: string; }; scheduleRegistrations: []; schedules: []; @@ -117,6 +118,7 @@ function createPreparedHost( } = {}, ): PreparedApplicationHost { const appRoot = input.appRoot ?? "G:\\projects\\test-eve"; + const pathSeparator = appRoot.includes("\\") ? "\\" : "/"; const preparedHost: PreparedApplicationHostStub = { appRoot, @@ -139,6 +141,7 @@ function createPreparedHost( }, compiledArtifacts: { bootstrapPath: `${appRoot}\\.eve\\compiled-artifacts-bootstrap.mjs`, + workflowWorldPluginPath: `${appRoot}${pathSeparator}.eve${pathSeparator}compiled-artifacts-workflow-world.mjs`, }, scheduleRegistrations: [], schedules: [], @@ -264,6 +267,9 @@ describe("configureNitroRoutes", () => { const workflowHandlerSource = readWriteFileSourceMatching("/workflow/workflows-handler.mjs"); expect(workflowHandlerSource).toContain('import { POST } from "./workflows.mjs";'); + expect(workflowHandlerSource).toContain( + 'import "../../.eve/compiled-artifacts-workflow-world.mjs";', + ); expect(workflowHandlerSource).toContain( 'import { getWorld as __eveGetWorkflowWorld } from "file:///G:/projects/test-eve/node_modules/.pnpm/eve@0.3.0/node_modules/eve/dist/src/compiled/@workflow/core/runtime.js";', ); diff --git a/packages/eve/src/internal/nitro/host/configure-nitro-routes.ts b/packages/eve/src/internal/nitro/host/configure-nitro-routes.ts index 9f3a9b521..ffe216806 100644 --- a/packages/eve/src/internal/nitro/host/configure-nitro-routes.ts +++ b/packages/eve/src/internal/nitro/host/configure-nitro-routes.ts @@ -113,6 +113,7 @@ async function addWorkflowFileHandler( directHandlers?: ReadonlyArray; route: string; runtimeImportSpecifier?: string; + workflowWorldPluginPath?: string; }, ): Promise { const handlerPath = join( @@ -122,6 +123,10 @@ async function addWorkflowFileHandler( const handlerDirectoryPath = dirname(handlerPath); const bundlePath = createRelativeImportSpecifier(handlerDirectoryPath, input.bundlePath); const directHandlers = input.directHandlers ?? []; + const workflowWorldPluginImportSpecifier = + directHandlers.length > 0 && input.workflowWorldPluginPath !== undefined + ? createRelativeImportSpecifier(handlerDirectoryPath, input.workflowWorldPluginPath) + : undefined; const directHandlerImports = directHandlers.map((entry) => { const importSpecifier = createRelativeImportSpecifier(handlerDirectoryPath, entry.bundlePath); return { @@ -138,6 +143,7 @@ async function addWorkflowFileHandler( bundlePath, directHandlers: directHandlerImports, runtimeImportSpecifier: input.runtimeImportSpecifier, + workflowWorldPluginImportSpecifier, }), ); @@ -165,6 +171,7 @@ function buildWorkflowFileHandlerSource(input: { queuePrefix: string; }>; runtimeImportSpecifier?: string; + workflowWorldPluginImportSpecifier?: string; }): string { const lines: string[] = [ "// Generated by eve. Do not edit by hand.", @@ -193,6 +200,10 @@ function buildWorkflowFileHandlerSource(input: { ); } + if (input.workflowWorldPluginImportSpecifier !== undefined) { + lines.push(`import ${JSON.stringify(input.workflowWorldPluginImportSpecifier)};`); + } + lines.push( `import { getWorld as __eveGetWorkflowWorld } from ${JSON.stringify(input.runtimeImportSpecifier)};`, "", @@ -421,6 +432,7 @@ export async function configureNitroRoutes( directHandlers: directHandlerEntries, route: "/.well-known/workflow/v1/flow", runtimeImportSpecifier, + workflowWorldPluginPath: preparedHost.compiledArtifacts.workflowWorldPluginPath, }); } diff --git a/packages/eve/src/internal/nitro/host/create-application-nitro.scenario.test.ts b/packages/eve/src/internal/nitro/host/create-application-nitro.scenario.test.ts index 604a1b116..7ce13c155 100644 --- a/packages/eve/src/internal/nitro/host/create-application-nitro.scenario.test.ts +++ b/packages/eve/src/internal/nitro/host/create-application-nitro.scenario.test.ts @@ -135,6 +135,7 @@ function createPreparedHost(): PreparedApplicationHost { } as unknown as PreparedApplicationHost["compileResult"], compiledArtifacts: { bootstrapPath: `${appRoot}/.eve/bootstrap.mjs`, + workflowWorldPluginPath: `${appRoot}/.eve/workflow-world.mjs`, } as PreparedApplicationHost["compiledArtifacts"], scheduleRegistrations: [], schedules: [], @@ -152,6 +153,22 @@ describe("createApplicationNitro", () => { delete process.env.VERCEL; }); + it("installs compiled artifacts before constructing the Workflow world", async () => { + const nitroStub = createNitroStub(); + createNitroMock.mockResolvedValueOnce(nitroStub.nitro); + + const { createApplicationNitro } = + await import("#internal/nitro/host/create-application-nitro.js"); + const preparedHost = createPreparedHost(); + await createApplicationNitro(preparedHost, true); + + const plugins = createNitroMock.mock.calls[0]?.[0].plugins as string[]; + + expect(plugins.indexOf(preparedHost.compiledArtifacts.bootstrapPath)).toBeLessThan( + plugins.indexOf(preparedHost.compiledArtifacts.workflowWorldPluginPath), + ); + }); + it("preserves workflow bundle side effects and skips workflow transform for cached bundles", async () => { const nitroStub = createNitroStub(); createNitroMock.mockResolvedValueOnce(nitroStub.nitro); diff --git a/packages/eve/src/internal/nitro/host/create-application-nitro.ts b/packages/eve/src/internal/nitro/host/create-application-nitro.ts index 1fbd73707..a582125ce 100644 --- a/packages/eve/src/internal/nitro/host/create-application-nitro.ts +++ b/packages/eve/src/internal/nitro/host/create-application-nitro.ts @@ -675,6 +675,8 @@ export async function createApplicationNitro( includesApplicationSurface(surface) && (dev || manifestHasWebSocketChannel(preparedHost.compileResult.manifest)); const nitroPlugins: string[] = []; + nitroPlugins.push(preparedHost.compiledArtifacts.bootstrapPath); + nitroPlugins.push(preparedHost.compiledArtifacts.workflowWorldPluginPath); if (!dev) { // Stops all tracked sandboxes when the production server shuts // down. Dev servers are excluded: the dev CLI parent already stops @@ -691,7 +693,6 @@ export async function createApplicationNitro( if (preparedHost.compiledArtifacts.instrumentationPluginPath !== undefined) { nitroPlugins.push(preparedHost.compiledArtifacts.instrumentationPluginPath); } - nitroPlugins.push(preparedHost.compiledArtifacts.bootstrapPath); await prepareEveVersionedCacheDirectory(nitroBuildDir); const nitro = await createNitro( { diff --git a/packages/eve/src/internal/workflow/configure-world.test.ts b/packages/eve/src/internal/workflow/configure-world.test.ts deleted file mode 100644 index 4c2c4fcfc..000000000 --- a/packages/eve/src/internal/workflow/configure-world.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { installConfiguredWorkflowWorld } from "#internal/workflow/configure-world.js"; - -const mocks = vi.hoisted(() => ({ - setWorld: vi.fn(), -})); - -vi.mock("#compiled/@workflow/core/runtime.js", () => ({ - setWorld: mocks.setWorld, -})); - -describe("installConfiguredWorkflowWorld", () => { - beforeEach(() => { - mocks.setWorld.mockClear(); - }); - - it("installs and starts a world from the module default export", async () => { - const world = createMockWorld(); - const createWorld = vi.fn(() => world); - - await expect( - installConfiguredWorkflowWorld({ - module: { default: createWorld }, - }), - ).resolves.toBe(world); - - expect(createWorld).toHaveBeenCalledOnce(); - expect(mocks.setWorld).toHaveBeenCalledWith(world); - expect(world.start).toHaveBeenCalledOnce(); - }); - - it("falls back to a createWorld export when no export name is configured", async () => { - const world = createMockWorld(); - - await installConfiguredWorkflowWorld({ - module: { createWorld: () => world }, - }); - - expect(mocks.setWorld).toHaveBeenCalledWith(world); - }); - - it("rejects modules without a default or createWorld factory", async () => { - await expect( - installConfiguredWorkflowWorld({ - module: {}, - }), - ).rejects.toThrow( - 'Configured Workflow world module must export a default function or "createWorld" function.', - ); - - expect(mocks.setWorld).not.toHaveBeenCalled(); - }); - - it("rejects factories that do not return a Workflow World", async () => { - await expect( - installConfiguredWorkflowWorld({ - module: { default: () => ({}) }, - }), - ).rejects.toThrow("Configured Workflow world factory did not return a valid World."); - - expect(mocks.setWorld).not.toHaveBeenCalled(); - }); -}); - -function createMockWorld() { - return { - createQueueHandler: vi.fn(), - events: {}, - start: vi.fn(), - }; -} diff --git a/packages/eve/src/internal/workflow/configure-world.ts b/packages/eve/src/internal/workflow/configure-world.ts deleted file mode 100644 index 19ff173a2..000000000 --- a/packages/eve/src/internal/workflow/configure-world.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { createRequire } from "node:module"; -import { readFileSync } from "node:fs"; - -import type { World } from "#compiled/@workflow/world/index.js"; -import { resolveExpectedWorkflowVersion } from "#internal/application/package.js"; -import { setWorld } from "#internal/workflow/runtime.js"; -import { - assertWorkflowWorldCompatibility, - type WorkflowWorldManifest, -} from "#internal/workflow/world-compatibility.js"; - -export interface ConfiguredWorkflowWorldModule { - readonly [name: string]: unknown; - readonly default?: unknown; -} - -export interface InstallConfiguredWorkflowWorldInput { - readonly module: ConfiguredWorkflowWorldModule | (() => unknown); - /** - * Package name of the configured world (e.g. `@workflow/world-postgres`), - * derived from the agent manifest's `experimental.workflow.world`. Used to - * resolve the world's `package.json` for the boot-time compatibility check. - */ - readonly packageName?: string; -} - -/** - * Installs a Workflow world selected by the compiled agent config. - */ -export async function installConfiguredWorkflowWorld( - input: InstallConfiguredWorkflowWorldInput, -): Promise { - assertConfiguredWorldCompatibility(input.packageName); - const world = await createWorkflowWorld(input); - setWorld(world); - await world.start?.(); - return world; -} - -/** - * Fails fast at boot when the configured world's declared `@workflow/*` line is - * incompatible with the line this eve release bundles. Best-effort: any failure - * to resolve or read the world's `package.json` is swallowed so we never turn a - * readable-but-unverifiable setup into a boot failure. - */ -function assertConfiguredWorldCompatibility(packageName: string | undefined): void { - if (packageName === undefined) { - return; - } - - const expectedWorkflowVersion = resolveExpectedWorkflowVersion(); - - if (expectedWorkflowVersion === undefined) { - return; - } - - let worldManifest: WorkflowWorldManifest; - try { - const require = createRequire(import.meta.url); - const manifestPath = require.resolve(`${packageName}/package.json`); - worldManifest = JSON.parse(readFileSync(manifestPath, "utf8")) as WorkflowWorldManifest; - } catch { - return; - } - - assertWorkflowWorldCompatibility({ - expectedWorkflowVersion, - worldManifest, - worldPackageName: packageName, - }); -} - -async function createWorkflowWorld(input: InstallConfiguredWorkflowWorldInput): Promise { - const factory = resolveWorkflowWorldFactory(input); - const world = await factory(); - - if (!isWorkflowWorld(world)) { - throw new Error("Configured Workflow world factory did not return a valid World."); - } - - return world; -} - -function resolveWorkflowWorldFactory(input: InstallConfiguredWorkflowWorldInput): () => unknown { - if (typeof input.module === "function") { - return input.module; - } - - if (typeof input.module.default === "function") { - return input.module.default as () => unknown; - } - - if (typeof input.module.createWorld === "function") { - return input.module.createWorld as () => unknown; - } - - throw new Error( - 'Configured Workflow world module must export a default function or "createWorld" function.', - ); -} - -function isWorkflowWorld(value: unknown): value is World { - return ( - typeof value === "object" && - value !== null && - "createQueueHandler" in value && - typeof value.createQueueHandler === "function" && - "events" in value && - typeof value.events === "object" && - value.events !== null - ); -} diff --git a/packages/eve/src/internal/workflow/runtime.ts b/packages/eve/src/internal/workflow/runtime.ts index 71bb520bd..649eb02d7 100644 --- a/packages/eve/src/internal/workflow/runtime.ts +++ b/packages/eve/src/internal/workflow/runtime.ts @@ -1,9 +1,5 @@ import * as workflowRuntime from "#compiled/@workflow/core/runtime.js"; -// Workflow turbo backgrounds run_started and forces optimistic inline start. -// Keep eve on the fully ordered runtime path until that beta behavior is safe. -process.env.WORKFLOW_TURBO = "0"; - export * from "#compiled/@workflow/core/runtime.js"; export type { StartOptionsWithoutDeploymentId, diff --git a/packages/eve/src/internal/workflow/validate-world.test.ts b/packages/eve/src/internal/workflow/validate-world.test.ts new file mode 100644 index 000000000..ce10f36a2 --- /dev/null +++ b/packages/eve/src/internal/workflow/validate-world.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it, vi } from "vitest"; + +import { validateWorkflowWorld } from "#internal/workflow/validate-world.js"; + +describe("validateWorkflowWorld", () => { + it("accepts a valid Workflow world", () => { + expect(() => + validateWorkflowWorld({ + world: createMockWorld(), + }), + ).not.toThrow(); + }); + + it("rejects worlds without queue handlers", () => { + expect(() => + validateWorkflowWorld({ + world: { + events: {}, + specVersion: 5, + }, + }), + ).toThrow("Configured Workflow world factory did not return a valid World."); + }); + + it("rejects worlds without event storage", () => { + expect(() => + validateWorkflowWorld({ + world: { + createQueueHandler: vi.fn(), + specVersion: 5, + }, + }), + ).toThrow("Configured Workflow world factory did not return a valid World."); + }); + + it("rejects worlds without a spec version", () => { + expect(() => + validateWorkflowWorld({ + world: { + createQueueHandler: vi.fn(), + events: {}, + }, + }), + ).toThrow("Configured Workflow world factory did not return a valid World."); + }); +}); + +function createMockWorld() { + return { + createQueueHandler: vi.fn(), + events: {}, + specVersion: 5, + }; +} diff --git a/packages/eve/src/internal/workflow/validate-world.ts b/packages/eve/src/internal/workflow/validate-world.ts new file mode 100644 index 000000000..8462cedc0 --- /dev/null +++ b/packages/eve/src/internal/workflow/validate-world.ts @@ -0,0 +1,73 @@ +import { readFileSync } from "node:fs"; +import { createRequire } from "node:module"; + +import type { World } from "#compiled/@workflow/world/index.js"; +import { resolveExpectedWorkflowVersion } from "#internal/application/package.js"; +import { + assertWorkflowWorldCompatibility, + type WorkflowWorldManifest, +} from "#internal/workflow/world-compatibility.js"; + +export interface ValidateWorkflowWorldInput { + /** + * Package name of the configured world, used to resolve its package manifest + * for the boot-time compatibility check. + */ + readonly packageName?: string; + readonly world: unknown; +} + +/** + * Validates a Workflow world before eve installs it as the runtime singleton. + */ +export function validateWorkflowWorld(input: ValidateWorkflowWorldInput): asserts input is { + readonly packageName?: string; + readonly world: World; +} { + assertConfiguredWorldCompatibility(input.packageName); + + if (!isWorkflowWorld(input.world)) { + throw new Error("Configured Workflow world factory did not return a valid World."); + } +} + +function assertConfiguredWorldCompatibility(packageName: string | undefined): void { + if (packageName === undefined) { + return; + } + + const expectedWorkflowVersion = resolveExpectedWorkflowVersion(); + + if (expectedWorkflowVersion === undefined) { + return; + } + + let worldManifest: WorkflowWorldManifest; + try { + const require = createRequire(import.meta.url); + const manifestPath = require.resolve(`${packageName}/package.json`); + worldManifest = JSON.parse(readFileSync(manifestPath, "utf8")) as WorkflowWorldManifest; + } catch { + return; + } + + assertWorkflowWorldCompatibility({ + expectedWorkflowVersion, + worldManifest, + worldPackageName: packageName, + }); +} + +function isWorkflowWorld(value: unknown): value is World { + return ( + typeof value === "object" && + value !== null && + "createQueueHandler" in value && + typeof value.createQueueHandler === "function" && + "events" in value && + typeof value.events === "object" && + value.events !== null && + "specVersion" in value && + typeof value.specVersion === "number" + ); +} diff --git a/packages/eve/src/internal/workflow/world-compatibility.ts b/packages/eve/src/internal/workflow/world-compatibility.ts index 53f02cf95..1446f51fb 100644 --- a/packages/eve/src/internal/workflow/world-compatibility.ts +++ b/packages/eve/src/internal/workflow/world-compatibility.ts @@ -19,7 +19,7 @@ export interface AssertWorkflowWorldCompatibilityInput { readonly worldPackageName: string; /** Parsed `package.json` of the installed configured world. */ readonly worldManifest: WorkflowWorldManifest; - /** The `@workflow/core` version this eve release bundles (its single source of truth). */ + /** The `@workflow/core` version this eve release bundles. */ readonly expectedWorkflowVersion: string; } @@ -31,15 +31,11 @@ interface VersionLine { /** * Parses the leading semver-ish coordinates out of a version or simple range. * - * This is intentionally tiny — eve only needs the major number and any + * This is intentionally tiny: eve only needs the major number and any * prerelease tag to detect a definite line mismatch, so we avoid pulling in a - * full semver parser (keeping `nitro` as eve's only runtime dependency). - * Returns `undefined` when the major cannot be determined so callers can no-op - * rather than risk a false-positive boot failure. + * full semver parser. */ function parseVersionLine(value: string): VersionLine | undefined { - // Strip a single leading range operator (`^`, `~`, `>=`, etc.) and any - // surrounding whitespace; we only inspect the first concrete version. const match = /(\d+)\.(?:\d+|x|\*)(?:\.(?:\d+|x|\*))?(?:-([0-9A-Za-z.-]+))?/.exec(value.trim()); if (match === null) { @@ -53,8 +49,6 @@ function parseVersionLine(value: string): VersionLine | undefined { } const prerelease = match[2]; - // Reduce `beta.13` / `beta.24` to the line tag `beta` so different patch - // builds on the same prerelease line compare equal. const prereleaseTag = prerelease === undefined ? undefined : prerelease.split(".")[0]; return { major, prereleaseTag }; @@ -79,10 +73,6 @@ function isDefiniteLineMismatch(world: VersionLine, expected: VersionLine): bool return true; } - // Same major: a definite mismatch only when both sides declare a prerelease - // tag and the tags differ (e.g. `beta` vs `alpha`). A world targeting a - // stable release of the same major (no prerelease tag) is treated as - // compatible-enough — we only fail on unambiguous divergence. return ( world.prereleaseTag !== undefined && expected.prereleaseTag !== undefined && @@ -99,17 +89,6 @@ function formatExpectedLine(line: VersionLine): string { /** * Fails fast when a configured Workflow world targets a `@workflow/*` major or * prerelease line that is incompatible with the line this eve release bundles. - * - * The check is deliberately conservative: it throws only on a *definite* - * mismatch (a different major version, or a different prerelease tag on the - * same major). When the world's declared `@workflow/*` dependency is missing or - * its version cannot be parsed, this is a no-op so eve never turns an - * ambiguous-but-possibly-fine setup into a hard boot failure. The deeper, - * fully version-aware compatibility check belongs in `@workflow/core`; this is - * a low-risk early signal that surfaces an actionable message instead of a - * cryptic Zod error deep in workflow replay. - * - * @throws Error when the world declares an incompatible `@workflow/*` line. */ export function assertWorkflowWorldCompatibility( input: AssertWorkflowWorldCompatibilityInput, diff --git a/packages/eve/test/scenarios/compiled-vendor-assets.scenario.test.ts b/packages/eve/test/scenarios/compiled-vendor-assets.scenario.test.ts index 0831dacff..78b3cc74d 100644 --- a/packages/eve/test/scenarios/compiled-vendor-assets.scenario.test.ts +++ b/packages/eve/test/scenarios/compiled-vendor-assets.scenario.test.ts @@ -173,6 +173,16 @@ describe("compiled vendor assets", () => { expect(runtimeRunDts).toContain("from '../_workflow-serde.js'"); }); + it("vendors the Workflow world targets selected by generated Nitro plugins", async () => { + const [localWorld, vercelWorld] = await Promise.all([ + readFile(join(COMPILED_VENDOR_ROOT, "@workflow/world-local/index.js"), "utf8"), + readFile(join(COMPILED_VENDOR_ROOT, "@workflow/world-vercel/index.js"), "utf8"), + ]); + + expect(localWorld).toContain("createWorld"); + expect(vercelWorld).toContain("createWorld"); + }); + it("copies the complete @vercel/sandbox declaration tree from the installed package", async () => { const [upstreamEntries, vendoredEntries] = await Promise.all([ readdir(VERCEL_SANDBOX_DIST_ROOT, { recursive: true }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c81f36e6b..2fbec2be0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -917,20 +917,26 @@ importers: specifier: 1.28.1 version: 1.28.1 '@workflow/core': - specifier: 5.0.0-beta.26 - version: 5.0.0-beta.26(@opentelemetry/api@1.9.1)(ws@8.21.0) + specifier: 5.0.0-beta.28 + version: 5.0.0-beta.28(@opentelemetry/api@1.9.1)(ws@8.21.0) '@workflow/errors': - specifier: 5.0.0-beta.8 - version: 5.0.0-beta.8 + specifier: 5.0.0-beta.10 + version: 5.0.0-beta.10 '@workflow/serde': specifier: 5.0.0-beta.2 version: 5.0.0-beta.2 + '@workflow/utils': + specifier: 5.0.0-beta.6 + version: 5.0.0-beta.6 '@workflow/world': - specifier: 5.0.0-beta.14 - version: 5.0.0-beta.14 + specifier: 5.0.0-beta.16 + version: 5.0.0-beta.16 '@workflow/world-local': - specifier: 5.0.0-beta.22 - version: 5.0.0-beta.22(@opentelemetry/api@1.9.1) + specifier: 5.0.0-beta.24 + version: 5.0.0-beta.24(@opentelemetry/api@1.9.1) + '@workflow/world-vercel': + specifier: 5.0.0-beta.24 + version: 5.0.0-beta.24(@opentelemetry/api@1.9.1) ai: specifier: 'catalog:' version: 7.0.0(zod@4.4.3) @@ -7196,16 +7202,16 @@ packages: '@webgpu/types@0.1.71': resolution: {integrity: sha512-mMy8/ODcKhab808co15eW+yN+HgXoQxRQHTiBV9Mrvl1r0ufnid7YOcI+gi4eUWSWl9ezD6TW2KXccrL8HCh2A==} - '@workflow/core@5.0.0-beta.26': - resolution: {integrity: sha512-sdx0+ypCaOpJnHihyKIycCH60zaE3J9Ad4/KCtrtAD7MXoPABFZ1z5IUehfcyhyMXBTVd3vy3OCSeLjz742Oqg==} + '@workflow/core@5.0.0-beta.28': + resolution: {integrity: sha512-E+WXISYLQOxR0+YyHRgY/A1rjZwwv5oy1kw+7YFD6yhad63gMggskeREM5GWfwa+geBtL9J3KfyXilYjbMXDwA==} peerDependencies: '@opentelemetry/api': '1' peerDependenciesMeta: '@opentelemetry/api': optional: true - '@workflow/errors@5.0.0-beta.8': - resolution: {integrity: sha512-ngTSMEQkObwMLjppqal7y4qA/mfmIRBeOpWcki5WdyI5S3Tv1VO2sJCgB7xT9Zdvh6Nel8r63wjWtxZV/wv3Mw==} + '@workflow/errors@5.0.0-beta.10': + resolution: {integrity: sha512-l/6+Ukys3FBVjbND3Vs+PDgCYgM9LjFnEPQmD7mTLWkgBg9nvlb8Th09MKhDRo2rBX86VT2lcpXtEThubjasuQ==} '@workflow/serde@4.1.0': resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} @@ -7216,27 +7222,27 @@ packages: '@workflow/serde@5.0.0-beta.2': resolution: {integrity: sha512-INB+FcEKQkkFZa+s53sEMTNRFiBa2g9vzjsNuEDagvWxtI0t/cnCyoc57i7kS7nj+3/vGeRjO4Yn5ccbT0hyBQ==} - '@workflow/utils@5.0.0-beta.4': - resolution: {integrity: sha512-IO+4DjRo3FJONPtMHdTXBf04vSX3RHPnG9HdCLHyTZNGFqcekapZu35RpUy3VLsP/J1uU6LtebZEE53qV1HNqw==} + '@workflow/utils@5.0.0-beta.6': + resolution: {integrity: sha512-YMZqvtRMzlmWkgshURp4ilvcY8VMrNxwXxDEXQ/gkppKWhJ1xdJBpq3plZLf8LWS7JHXd32Wqll/UvQeARu93Q==} - '@workflow/world-local@5.0.0-beta.22': - resolution: {integrity: sha512-W+33lx80A3vd1ieRP5/G01H9tux+UP8WHY7k5RcAia3WW9uwP8sTmg2j+m6TIDAzMrz5QoUVKrFpEy1xkDarIQ==} + '@workflow/world-local@5.0.0-beta.24': + resolution: {integrity: sha512-sM86Zp1dva4JCHOaqM6VxiQVhe0CZ8DhtqKIMmkYNlWbTAZgh+FdKaxqFWLVC28kBpuTU6XjB6W3hBfw3z4u4Q==} peerDependencies: '@opentelemetry/api': '1' peerDependenciesMeta: '@opentelemetry/api': optional: true - '@workflow/world-vercel@5.0.0-beta.22': - resolution: {integrity: sha512-KLk3T/B+Mdn2dU/6Us1MKial/D5IaaS7T1enaSnC5pSp1PVG7u3v5VJNW3uE7UtM/Xg5BQrWvj2GCpQwofBH+A==} + '@workflow/world-vercel@5.0.0-beta.24': + resolution: {integrity: sha512-Gm2/2rXlQUKJLEXYBOnY/zCiIOLGrAHfOQR49GxMkLXfsmqnlPIkAUW2cdLdM8vY4gL7OdgnVUVPICBP3xMwFA==} peerDependencies: '@opentelemetry/api': '1' peerDependenciesMeta: '@opentelemetry/api': optional: true - '@workflow/world@5.0.0-beta.14': - resolution: {integrity: sha512-9vWQNfvtK0k7QTCPY1OR84qlEAjfHTOms4oHmyI2LNE7G2NNTB8ydJnjNwP+qiSb6OS7jHWRcImsPWNUODR0mQ==} + '@workflow/world@5.0.0-beta.16': + resolution: {integrity: sha512-yrkom7SkUC6oLKkkzN0wLjGOYlpPlwV8G7+LdYzZNHmI6pCRbVPPP3PaJAWwJ9beqB2uHPtJOUTS287NLDM01w==} abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} @@ -19389,19 +19395,19 @@ snapshots: '@webgpu/types@0.1.71': {} - '@workflow/core@5.0.0-beta.26(@opentelemetry/api@1.9.1)(ws@8.21.0)': + '@workflow/core@5.0.0-beta.28(@opentelemetry/api@1.9.1)(ws@8.21.0)': dependencies: '@aws-sdk/credential-provider-web-identity': 3.972.49 '@jridgewell/trace-mapping': 0.3.31 '@standard-schema/spec': 1.0.0 '@types/ms': 2.1.0 '@vercel/functions': 3.7.1(@aws-sdk/credential-provider-web-identity@3.972.49)(ws@8.21.0) - '@workflow/errors': 5.0.0-beta.8 + '@workflow/errors': 5.0.0-beta.10 '@workflow/serde': 5.0.0-beta.2 - '@workflow/utils': 5.0.0-beta.4 - '@workflow/world': 5.0.0-beta.14 - '@workflow/world-local': 5.0.0-beta.22(@opentelemetry/api@1.9.1) - '@workflow/world-vercel': 5.0.0-beta.22(@opentelemetry/api@1.9.1) + '@workflow/utils': 5.0.0-beta.6 + '@workflow/world': 5.0.0-beta.16 + '@workflow/world-local': 5.0.0-beta.24(@opentelemetry/api@1.9.1) + '@workflow/world-vercel': 5.0.0-beta.24(@opentelemetry/api@1.9.1) debug: 4.4.3 devalue: 5.8.1 ms: 2.1.3 @@ -19416,9 +19422,9 @@ snapshots: - supports-color - ws - '@workflow/errors@5.0.0-beta.8': + '@workflow/errors@5.0.0-beta.10': dependencies: - '@workflow/utils': 5.0.0-beta.4 + '@workflow/utils': 5.0.0-beta.6 ms: 2.1.3 '@workflow/serde@4.1.0': {} @@ -19427,16 +19433,16 @@ snapshots: '@workflow/serde@5.0.0-beta.2': {} - '@workflow/utils@5.0.0-beta.4': + '@workflow/utils@5.0.0-beta.6': dependencies: ms: 2.1.3 - '@workflow/world-local@5.0.0-beta.22(@opentelemetry/api@1.9.1)': + '@workflow/world-local@5.0.0-beta.24(@opentelemetry/api@1.9.1)': dependencies: '@vercel/queue': 0.3.1 - '@workflow/errors': 5.0.0-beta.8 - '@workflow/utils': 5.0.0-beta.4 - '@workflow/world': 5.0.0-beta.14 + '@workflow/errors': 5.0.0-beta.10 + '@workflow/utils': 5.0.0-beta.6 + '@workflow/world': 5.0.0-beta.16 async-sema: 3.1.1 ulid: 3.0.2 undici: 7.28.0 @@ -19444,19 +19450,19 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 - '@workflow/world-vercel@5.0.0-beta.22(@opentelemetry/api@1.9.1)': + '@workflow/world-vercel@5.0.0-beta.24(@opentelemetry/api@1.9.1)': dependencies: '@vercel/oidc': 3.2.0 '@vercel/queue': 0.3.1 - '@workflow/errors': 5.0.0-beta.8 - '@workflow/world': 5.0.0-beta.14 + '@workflow/errors': 5.0.0-beta.10 + '@workflow/world': 5.0.0-beta.16 cbor-x: 1.6.0 undici: 7.28.0 zod: 4.3.6 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@workflow/world@5.0.0-beta.14': + '@workflow/world@5.0.0-beta.16': dependencies: ulid: 3.0.2 zod: 4.3.6