experimental-ash 0.23.0 → 0.24.0
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/CHANGELOG.md +22 -0
- package/dist/docs/internals/hooks.md +13 -16
- package/dist/docs/internals/message-runtime.md +1 -1
- package/dist/docs/public/auth-and-route-protection.md +3 -3
- package/dist/docs/public/typescript-api.md +2 -2
- package/dist/src/channel/types.d.ts +10 -12
- package/dist/src/chunks/{dev-authored-source-watcher-d_35Mp8T.js → dev-authored-source-watcher-B4PaZGUr.js} +1 -1
- package/dist/src/chunks/host-DsW72Q-w.js +65 -0
- package/dist/src/chunks/{paths-YoCQlavu.js → paths-OknjaYR8.js} +24 -24
- package/dist/src/chunks/prewarm-B4YblQ5m.js +6 -0
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +2 -2
- package/dist/src/compiled/@workflow/core/classify-error.d.ts +4 -3
- package/dist/src/compiled/@workflow/core/encryption.d.ts +7 -1
- package/dist/src/compiled/@workflow/core/index.js +2 -2
- package/dist/src/compiled/@workflow/core/package.json +1 -1
- package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +47 -0
- package/dist/src/compiled/@workflow/core/runtime/replay-budget.d.ts +98 -0
- package/dist/src/compiled/@workflow/core/runtime.js +27 -27
- package/dist/src/compiled/@workflow/core/serialization/types.d.ts +14 -0
- package/dist/src/compiled/@workflow/core/serialization.d.ts +8 -0
- package/dist/src/compiled/@workflow/core/symbols.d.ts +16 -0
- package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
- package/dist/src/compiled/@workflow/core/workflow.js +1 -1
- package/dist/src/compiled/@workflow/errors/error-codes.d.ts +5 -1
- package/dist/src/compiled/@workflow/errors/index.d.ts +15 -1
- package/dist/src/compiled/@workflow/errors/index.js +1 -1
- package/dist/src/compiled/@workflow/errors/package.json +1 -1
- package/dist/src/compiled/_chunks/workflow/{context-errors-zbKocOyk.js → context-errors-Bbvvp-li.js} +2 -2
- package/dist/src/compiled/_chunks/workflow/{dist-0iNBqPYp.js → dist-C7wPwOI9.js} +2 -2
- package/dist/src/compiled/_chunks/workflow/{dist-D774SUM4.js → dist-C_oiE-l7.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/resume-hook-C3VWUPii.js +12 -0
- package/dist/src/compiled/_chunks/workflow/sleep-QTkC1VFe.js +1 -0
- package/dist/src/compiled/_chunks/workflow/{symbols-D-4tVV8x.js → symbols-QezhMuLg.js} +1 -1
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/execution/await-authorization-orchestrator.d.ts +2 -1
- package/dist/src/execution/await-authorization-orchestrator.js +4 -0
- package/dist/src/execution/connection-auth-steps.d.ts +4 -0
- package/dist/src/execution/connection-auth-steps.js +9 -11
- package/dist/src/execution/node-step.d.ts +4 -5
- package/dist/src/execution/subagent-adapter.d.ts +0 -27
- package/dist/src/execution/subagent-adapter.js +2 -66
- package/dist/src/execution/subagent-hitl-proxy.d.ts +2 -2
- package/dist/src/execution/subagent-hitl-proxy.js +2 -2
- package/dist/src/execution/task-mode.d.ts +3 -3
- package/dist/src/execution/task-mode.js +3 -3
- package/dist/src/execution/turn-workflow.d.ts +41 -0
- package/dist/src/execution/turn-workflow.js +96 -0
- package/dist/src/execution/workflow-entry.js +77 -87
- package/dist/src/execution/workflow-errors.d.ts +14 -0
- package/dist/src/execution/workflow-errors.js +54 -0
- package/dist/src/execution/workflow-runtime.d.ts +34 -3
- package/dist/src/execution/workflow-runtime.js +52 -10
- package/dist/src/execution/workflow-steps.d.ts +27 -2
- package/dist/src/execution/workflow-steps.js +31 -26
- package/dist/src/harness/instrumentation-config.js +14 -7
- package/dist/src/harness/messages.d.ts +7 -7
- package/dist/src/harness/messages.js +4 -4
- package/dist/src/harness/runtime-actions.d.ts +4 -4
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/workflow-bundle/workflow-builders.d.ts +1 -1
- package/dist/src/internal/workflow-bundle/workflow-builders.js +20 -8
- package/dist/src/internal/workflow-bundle/workflow-transformer.d.ts +13 -0
- package/dist/src/internal/workflow-bundle/workflow-transformer.js +10 -4
- package/package.json +4 -4
- package/dist/src/chunks/host-tji7W0Nn.js +0 -65
- package/dist/src/chunks/prewarm-6duWvvb5.js +0 -6
- package/dist/src/compiled/_chunks/workflow/resume-hook-CL8Ed91K.js +0 -12
- package/dist/src/compiled/_chunks/workflow/sleep-Dn3i9nxI.js +0 -1
- package/dist/src/execution/continuous-entry.d.ts +0 -59
- package/dist/src/execution/continuous-entry.js +0 -487
- package/dist/src/execution/continuous-runtime.d.ts +0 -17
- package/dist/src/execution/continuous-runtime.js +0 -123
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{O as e,b as t,t as n,x as r,y as i}from"../../chunks/paths-OknjaYR8.js";import{d as a,f as o,h as s}from"../../chunks/types-MZUhN0Zy.js";import{createCliTheme as c,renderCliBanner as l,renderCliSection as u}from"../ui/output.js";async function d(e){let t=await f(e);return{application:n(t?.project.appRoot??e),compiledState:t,messaging:{createSessionRoutePath:o,continueSessionRoutePattern:a,streamRoutePattern:s}}}async function f(n){try{return await t({startPath:n})}catch(t){if(t instanceof i)return t.result;if(t instanceof e||t instanceof r)return null;throw t}}function p(e,t){return`${e} ${t}${e===1?``:`s`}`}function m(e,t){return`${`${e} error${e===1?``:`s`}`}, ${`${t} warning${t===1?``:`s`}`}`}function h(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function g(e,t){let n=await d(t),r=n.compiledState,i=n.application,a=c(),o=[{label:`App Root`,value:i.appRoot}],s=[{label:`Workflow Build`,value:i.workflowBuildDir},{label:`Output`,value:i.outputDir}],f=[];r===null?o.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(o.push({label:`Agent Root`,value:r.project.agentRoot},{label:`Layout`,value:r.project.layout},{label:`Compile`,tone:h(r.metadata.status),value:r.metadata.status},{label:`Diagnostics`,tone:r.metadata.discovery.summary.errors>0?`danger`:r.metadata.discovery.summary.warnings>0?`warning`:`success`,value:m(r.metadata.discovery.summary.errors,r.metadata.discovery.summary.warnings)},{label:`Instructions`,value:r.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:p(r.manifest.skills.length,`skill`)}),s.unshift({label:`Compiled Manifest`,value:r.paths.compiledManifestPath},{label:`Discovery Manifest`,value:r.paths.discoveryManifestPath},{label:`Diagnostics`,value:r.paths.diagnosticsPath},{label:`Module Map`,value:r.paths.moduleMapPath},{label:`Metadata`,value:r.paths.compileMetadataPath}),f.push(r.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:r.manifest.instructions.logicalPath})),e.log([l(a,{subtitle:`Resolved application paths and the active message contract.`,title:`Ash Info`}),``,u(a,{rows:o,title:`Application`}),``,u(a,{rows:s,title:`Artifacts`}),...r===null?[]:[``,u(a,{rows:f,title:`Instructions`})],``,u(a,{rows:[{label:`Workflow ID`,value:i.workflowId},{label:`Source Dir`,value:i.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${n.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${n.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${n.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
|
|
2
2
|
`))}export{g as printApplicationInfo};
|
package/dist/src/cli/run.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{t as e}from"../chunks/package-DmsQgn4v.js";import{createCliTheme as t,renderCliTaggedLine as n}from"./ui/output.js";import{i as r,n as i,r as a,t as o}from"../chunks/url-BVRhVE2O.js";import{resolve as s}from"node:path";async function c(){return(await import(`../chunks/host-
|
|
1
|
+
import{t as e}from"../chunks/package-DmsQgn4v.js";import{createCliTheme as t,renderCliTaggedLine as n}from"./ui/output.js";import{i as r,n as i,r as a,t as o}from"../chunks/url-BVRhVE2O.js";import{resolve as s}from"node:path";async function c(){return(await import(`../chunks/host-DsW72Q-w.js`).then(e=>e.t)).buildHost}async function l(){return(await import(`./commands/info.js`)).printApplicationInfo}async function u(){return(await import(`./dev/repl.js`)).runDevelopmentRepl}async function d(){return(await import(`../evals/cli/eval.js`)).runEvalCommand}async function f(){return(await import(`../chunks/host-DsW72Q-w.js`).then(e=>e.t)).startHost}function p(e=process.cwd()){return s(e)}function m(e){return`Ash (v${e})`}function h(e){return e.name()===`info`||e.name()===`dev`}async function g(e){await new Promise((t,n)=>{let r=!1,i=()=>{process.off(`SIGINT`,a),process.off(`SIGTERM`,a)},a=()=>{r||(r=!0,i(),e.close().then(t,n))};process.once(`SIGINT`,a),process.once(`SIGTERM`,a)})}function _(e){if(!/^-?\d+$/.test(e))throw new r(`Expected a numeric port, received "${e}".`);let t=Number(e);if(!Number.isInteger(t))throw new r(`Expected a numeric port, received "${e}".`);if(t<0||t>65535)throw new r(`Expected a port between 0 and 65535, received "${e}".`);return t}function v(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function y(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function b(e){if(e.url){if(e.host!==void 0)throw new r(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new r(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new r(`The --no-repl option cannot be used with --url.`);return e.url}}function x(r,a){let s=p(),y=e().version,x=new i,S=t();return x.name(`ash`).description(`Build and run an Ash application.`).version(y).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{h(t)&&r.log(m(y))}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}}),x.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`./dev/environment.js`);e(s);let t=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${t}`,tag:`build`,tone:`success`}))}),x.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,_).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,o).addHelpText(`after`,`
|
|
2
2
|
You can also pass a bare URL as the only argument, for example: ash dev https://example.com
|
|
3
3
|
`).action(async e=>{let t=b(e),{loadDevelopmentEnvironmentFiles:i}=await import(`./dev/environment.js`);if(i(s),t){if(r.log(n(S,{message:`REPL connecting to ${t}`,tag:`dev`,tone:`info`})),!v()){r.log(n(S,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}r.log(``),await(a.runDevelopmentRepl??await u())({serverUrl:t});return}let o=await(a.startHost??await f())(s,{host:e.host,port:e.port,schedules:e.schedules===!0}),c=!1,l=async()=>{c||(c=!0,await o.close())};try{if(r.log(n(S,{message:`server listening at ${o.url}`,tag:`dev`,tone:`success`})),e.repl===!1)return await g({close:l});if(!v())return r.log(n(S,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await g({close:l});r.log(``),await(a.runDevelopmentRepl??await u())({serverUrl:o.url})}finally{await l()}}),x.command(`info`).description(`Print resolved application information.`).action(async()=>{await(a.printApplicationInfo??await l())(r,s)}),x.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await(a.runEvalCommand??await d())(e,r)}),x}async function S(e=process.argv.slice(2),t=console,n={}){let r=x(t,n),i=e.length===0?[`info`]:y(e);try{await r.parseAsync(i,{from:`user`})}catch(e){if(e instanceof a){if(e.exitCode===0)return;throw Error(e.message)}throw e}}export{S as runCli};
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"@standard-schema/spec": "1.1.0",
|
|
16
16
|
"turndown": "7.2.4",
|
|
17
17
|
"@vercel/sandbox": "2.0.0-beta.14",
|
|
18
|
-
"@workflow/core": "5.0.0-beta.
|
|
19
|
-
"@workflow/errors": "5.0.0-beta.
|
|
18
|
+
"@workflow/core": "5.0.0-beta.7",
|
|
19
|
+
"@workflow/errors": "5.0.0-beta.4",
|
|
20
20
|
"zod": "4.4.3",
|
|
21
21
|
"zod-validation-error": "5.0.0"
|
|
22
22
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type RunErrorCode } from '#compiled/@workflow/errors/index.js';
|
|
1
|
+
import { type RunErrorCode, WorkflowWorldError } from '#compiled/@workflow/errors/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Classify an error that caused a workflow run to fail.
|
|
4
4
|
*
|
|
5
5
|
* After the structural separation of infrastructure vs user code error
|
|
6
6
|
* handling, the only errors that reach the `run_failed` try/catch are:
|
|
7
7
|
* - User code errors (throws from workflow functions, propagated step failures)
|
|
8
|
-
* - WorkflowRuntimeError and subclasses (
|
|
9
|
-
*
|
|
8
|
+
* - WorkflowRuntimeError and subclasses (missing timestamps, workflow/step
|
|
9
|
+
* not registered, corrupted event log, etc.)
|
|
10
10
|
*
|
|
11
11
|
* Uses each subclass's `.is()` static (a name-based duck check) instead of
|
|
12
12
|
* a single `instanceof` check because workflows execute in a separate
|
|
@@ -15,5 +15,6 @@ import { type RunErrorCode } from '#compiled/@workflow/errors/index.js';
|
|
|
15
15
|
* thrown inside the workflow VM and we'd misclassify genuine runtime
|
|
16
16
|
* errors as user errors.
|
|
17
17
|
*/
|
|
18
|
+
export declare function isWorldContractError(err: unknown): err is WorkflowWorldError;
|
|
18
19
|
export declare function classifyRunError(err: unknown): RunErrorCode;
|
|
19
20
|
//# sourceMappingURL=classify-error.d.ts.map
|
|
@@ -22,10 +22,16 @@ export type CryptoKey = import('node:crypto').webcrypto.CryptoKey;
|
|
|
22
22
|
* Callers should call this once per run (after `getEncryptionKeyForRun()`)
|
|
23
23
|
* and pass the resulting `CryptoKey` to all subsequent encrypt/decrypt calls.
|
|
24
24
|
*
|
|
25
|
+
* Pass `usages: ['encrypt']` (or `['decrypt']`) for cross-run scenarios
|
|
26
|
+
* where the caller should not be able to perform the inverse operation
|
|
27
|
+
* with the key — for example a child workflow writing into a parent
|
|
28
|
+
* run's forwarded WritableStream only needs to encrypt, never decrypt.
|
|
29
|
+
*
|
|
25
30
|
* @param raw - Raw 32-byte AES-256 key (from World.getEncryptionKeyForRun)
|
|
31
|
+
* @param usages - Key usages. Defaults to `['encrypt', 'decrypt']`.
|
|
26
32
|
* @returns CryptoKey ready for AES-GCM operations
|
|
27
33
|
*/
|
|
28
|
-
export declare function importKey(raw: Uint8Array): Promise<import("crypto").webcrypto.CryptoKey>;
|
|
34
|
+
export declare function importKey(raw: Uint8Array, usages?: ReadonlyArray<'encrypt' | 'decrypt'>): Promise<import("crypto").webcrypto.CryptoKey>;
|
|
29
35
|
/**
|
|
30
36
|
* Encrypt data using AES-256-GCM.
|
|
31
37
|
*
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)}`)}return await
|
|
1
|
+
import{i as e,s as t}from"../../_chunks/workflow/dist-C7wPwOI9.js";import{i as n,n as r,r as i}from"../../_chunks/workflow/context-errors-Bbvvp-li.js";import{c as a,s as o}from"../../_chunks/workflow/symbols-QezhMuLg.js";import{Bt as s,Ht as c,L as l,S as u,Vt as d,h as f,n as p,w as m,z as h}from"../../_chunks/workflow/resume-hook-C3VWUPii.js";import{t as g}from"../../_chunks/workflow/sleep-QTkC1VFe.js";function _(e){i(`createHook()`,`https://workflow-sdk.dev/docs/api-reference/workflow/create-hook`,_)}function v(e){i(`createWebhook()`,`https://workflow-sdk.dev/docs/api-reference/workflow/create-webhook`,v)}function y({schema:e}={}){function t(e){i(`defineHook().create()`,`https://workflow-sdk.dev/docs/api-reference/workflow/define-hook`,t)}return{create:t,async resume(t,n){if(!e?.[`~standard`])return await p(t,n);let r=e[`~standard`].validate(n);if(r instanceof Promise&&(r=await r),r.issues){let e=r.issues.map(e=>{let t=e.path?.map(e=>String(typeof e==`object`&&e?e.key:e)).join(`.`);return t?` at "${t}": ${e.message}`:` ${e.message}`});throw Error(`Hook payload did not match the defined schema:\n${e.join(`
|
|
2
|
+
`)}`)}return await p(t,r.value)}}}function b(){let e=h.getStore();return e||r(`getStepMetadata()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-step-metadata`,b),e.stepMetadata}function x(){let e=h.getStore();return e||n(`getWorkflowMetadata()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-workflow-metadata`,x),e.workflowMetadata}function S(e={}){let t=h.getStore();t||n(`getWritable()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-writable`,S);let{namespace:r}=e,i=t.workflowMetadata.workflowRunId,p=l(i,r),g=m(u(globalThis,t.ops,i,t.encryptionKey),t.encryptionKey),_=new f(i,p),v=s();return t.ops.push(v.promise),d(g.readable,_,v).catch(()=>{}),c(g.writable,v),Object.defineProperty(g.writable,o,{value:p,writable:!1}),Object.defineProperty(g.writable,a,{value:i,writable:!1}),g.writable}export{e as FatalError,t as RetryableError,_ as createHook,v as createWebhook,y as defineHook,b as getStepMetadata,x as getWorkflowMetadata,S as getWritable,g as sleep};
|
|
@@ -1,4 +1,51 @@
|
|
|
1
1
|
export declare const MAX_QUEUE_DELIVERIES = 48;
|
|
2
|
+
/**
|
|
3
|
+
* Default maximum time allowed for the *replay* portion of a single workflow
|
|
4
|
+
* handler invocation (in ms). This budget only covers deterministic-replay
|
|
5
|
+
* and workflow-VM execution between step boundaries — inline step bodies
|
|
6
|
+
* (`"use step"` functions invoked via `executeStep`) do NOT count against
|
|
7
|
+
* it. Step bodies are bounded separately by the platform's function
|
|
8
|
+
* `maxDuration` (e.g. 800s on Vercel Pro Fluid) and `NO_INLINE_REPLAY_AFTER_MS`.
|
|
9
|
+
*
|
|
10
|
+
* If the non-step ("replay") time within a single invocation exceeds this
|
|
11
|
+
* budget, the handler exits so the queue can retry. After
|
|
12
|
+
* `REPLAY_TIMEOUT_MAX_RETRIES` exhausted attempts the run is failed with
|
|
13
|
+
* `RUN_ERROR_CODES.REPLAY_TIMEOUT`.
|
|
14
|
+
*
|
|
15
|
+
* Note that on Vercel Hobby (standard functions), the platform `maxDuration`
|
|
16
|
+
* is 60s — well below this budget, so the platform SIGTERM will fire first
|
|
17
|
+
* and the queue will re-deliver until the visibility window expires. With
|
|
18
|
+
* Fluid Compute on Hobby the per-function ceiling rises to 300s, still
|
|
19
|
+
* under the default budget.
|
|
20
|
+
*
|
|
21
|
+
* Override via the `WORKFLOW_REPLAY_TIMEOUT_MS` env var (clamped to
|
|
22
|
+
* `MIN_REPLAY_TIMEOUT_MS`..`MAX_REPLAY_TIMEOUT_MS`).
|
|
23
|
+
*/
|
|
2
24
|
export declare const REPLAY_TIMEOUT_MS = 240000;
|
|
25
|
+
/** Lower bound for the replay-timeout env var override. */
|
|
26
|
+
export declare const MIN_REPLAY_TIMEOUT_MS = 30000;
|
|
27
|
+
/**
|
|
28
|
+
* Upper bound for the replay-timeout env var override. 780s leaves ≥20s of
|
|
29
|
+
* headroom under Vercel Pro Fluid's 800s function ceiling so the handler
|
|
30
|
+
* can write `run_failed` before SIGTERM.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MAX_REPLAY_TIMEOUT_MS = 780000;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the effective replay-timeout budget for the current process.
|
|
35
|
+
*
|
|
36
|
+
* Reads `process.env.WORKFLOW_REPLAY_TIMEOUT_MS` lazily so tests and
|
|
37
|
+
* deployments can override per invocation. Invalid / out-of-range values
|
|
38
|
+
* fall back to a safe value (no throw — the env var is an escape hatch,
|
|
39
|
+
* not a hard requirement) and emit a one-time warning so misconfiguration
|
|
40
|
+
* is observable.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getReplayTimeoutMs(): number;
|
|
43
|
+
/**
|
|
44
|
+
* Reset the warn-once cache. Test-only — exported so unit tests can
|
|
45
|
+
* exercise the warn path repeatedly without sharing state.
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export declare function _resetReplayTimeoutWarnCacheForTests(): void;
|
|
3
50
|
export declare const REPLAY_TIMEOUT_MAX_RETRIES = 3;
|
|
4
51
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-invocation accounting of the *non-step* portion of a workflow
|
|
3
|
+
* handler run: deterministic event-log replay, workflow-VM execution
|
|
4
|
+
* between step boundaries, suspension handling, queue round-trips, etc.
|
|
5
|
+
* Inline step bodies (`"use step"` functions invoked via `executeStep`)
|
|
6
|
+
* are intentionally excluded — they are bounded by the platform's
|
|
7
|
+
* function `maxDuration` and the `NO_INLINE_REPLAY_AFTER_MS` early-return
|
|
8
|
+
* guard.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const budget = new ReplayBudget();
|
|
14
|
+
* // …non-step work happens here, accumulates against the budget…
|
|
15
|
+
* budget.pause();
|
|
16
|
+
* try {
|
|
17
|
+
* await executeStep(...); // not charged
|
|
18
|
+
* } finally {
|
|
19
|
+
* budget.resume();
|
|
20
|
+
* }
|
|
21
|
+
* // back to charging
|
|
22
|
+
* if (budget.isExhausted()) { ... }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Implementation notes:
|
|
26
|
+
*
|
|
27
|
+
* - `pause()` and `resume()` are idempotent: calling `pause()` while
|
|
28
|
+
* already paused (or `resume()` while already resumed) is a no-op.
|
|
29
|
+
* This protects against double-counting in future refactors that nest
|
|
30
|
+
* step execution or take an early-return path between a `pause()` and
|
|
31
|
+
* the matching `resume()`.
|
|
32
|
+
* - `isExhausted()` is checked at loop boundaries by the caller — the
|
|
33
|
+
* budget itself does not arm any timers. This means an in-flight
|
|
34
|
+
* pathological `runWorkflow` call (e.g. a huge event-log replay) can
|
|
35
|
+
* overshoot the budget by up to one iteration's worth of work before
|
|
36
|
+
* the next check fires. In practice the 20s headroom built into
|
|
37
|
+
* `MAX_REPLAY_TIMEOUT_MS` (and the function `maxDuration` ceiling)
|
|
38
|
+
* gives us slack; the old `setTimeout`-based approach also ultimately
|
|
39
|
+
* relied on the platform SIGTERM as the hard backstop.
|
|
40
|
+
*/
|
|
41
|
+
export declare class ReplayBudget {
|
|
42
|
+
private readonly limitMs;
|
|
43
|
+
private elapsedMs;
|
|
44
|
+
private intervalStart;
|
|
45
|
+
constructor(limitMs?: number);
|
|
46
|
+
/**
|
|
47
|
+
* The configured replay-timeout limit, in ms. Useful for log messages.
|
|
48
|
+
*/
|
|
49
|
+
get configuredLimitMs(): number;
|
|
50
|
+
/**
|
|
51
|
+
* Total non-step time accumulated so far, in ms. Includes the
|
|
52
|
+
* currently-active interval if the budget is not paused.
|
|
53
|
+
*/
|
|
54
|
+
elapsed(): number;
|
|
55
|
+
/**
|
|
56
|
+
* Stop counting elapsed time toward the budget. Idempotent — safe to
|
|
57
|
+
* call multiple times in a row; subsequent calls are no-ops until
|
|
58
|
+
* `resume()` reopens an interval.
|
|
59
|
+
*/
|
|
60
|
+
pause(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Resume counting elapsed time toward the budget. Idempotent — safe to
|
|
63
|
+
* call multiple times in a row; subsequent calls re-anchor the
|
|
64
|
+
* interval start to `now()`, which is fine because no time accrues
|
|
65
|
+
* between back-to-back `resume()` calls.
|
|
66
|
+
*/
|
|
67
|
+
resume(): void;
|
|
68
|
+
/**
|
|
69
|
+
* True if the budget has been exhausted (`elapsed() >= limitMs`).
|
|
70
|
+
* Callers should invoke `handleExhausted(...)` afterward and return
|
|
71
|
+
* from the handler.
|
|
72
|
+
*/
|
|
73
|
+
isExhausted(): boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fail the run (or retry, on early attempts) when the replay budget is
|
|
77
|
+
* exhausted. The handling depends on whether the underlying World
|
|
78
|
+
* supports `process.exit(1)` as a queue redelivery signal (see
|
|
79
|
+
* `World.processExitTriggersQueueRedelivery`):
|
|
80
|
+
*
|
|
81
|
+
* - **Managed-platform Worlds** (`world-vercel`): on attempts <=
|
|
82
|
+
* `REPLAY_TIMEOUT_MAX_RETRIES` exit the process so the platform fails
|
|
83
|
+
* the invocation and the queue redelivers; on the next attempt write
|
|
84
|
+
* `run_failed` with `RUN_ERROR_CODES.REPLAY_TIMEOUT` and exit.
|
|
85
|
+
*
|
|
86
|
+
* - **In-process Worlds** (`world-local`, dev servers): calling
|
|
87
|
+
* `process.exit()` would terminate the host (e.g. `pnpm dev`), so
|
|
88
|
+
* instead log a warning, write `run_failed` best-effort, and return.
|
|
89
|
+
* The framework completes the request normally.
|
|
90
|
+
*/
|
|
91
|
+
export declare function handleReplayBudgetExhausted(args: {
|
|
92
|
+
runId: string;
|
|
93
|
+
workflowName: string;
|
|
94
|
+
requestId: string | undefined;
|
|
95
|
+
attempt: number;
|
|
96
|
+
limitMs: number;
|
|
97
|
+
}): Promise<void>;
|
|
98
|
+
//# sourceMappingURL=replay-budget.d.ts.map
|