octaflow 0.27.0 → 0.29.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/README.md CHANGED
@@ -203,6 +203,7 @@ next to what your steps actually do.
203
203
  | 🎯 | **Decisions** — typed choices with a confidence from a swappable decider (a function, a model, your own); below a threshold a person settles it |
204
204
  | ⏳ | **Deadlines** — a budget on a suspended step (fail, or continue with a stand-in answer) and on a whole run |
205
205
  | ♻️ | **Retry a failed run** — `retryWorkflow` resumes from the failure point; completed steps keep their output |
206
+ | 🧹 | **Delete the runs about a record** — `purgeWorkflows({ entityRef })` cancels and deletes them with their sub-workflow runs and steps, for an erasure or a deleted record; refuses only while a step is executing |
206
207
  | 🚦 | **Concurrency & rate limiting** — per-step-type caps and token buckets via a pluggable gate |
207
208
  | ⏰ | **Cron / scheduled starts** — fire workflows on a schedule (pg-boss) |
208
209
  | 🔑 | **Start idempotency** — a dedup key collapses double-clicks / overlapping ticks |
@@ -327,7 +328,7 @@ Full docs — concepts, every feature, production wiring and the API reference
327
328
  | [Deadlines](https://octaflow.octabits.io/core/deadlines/) | budgets for a suspended step and for a whole run |
328
329
  | [Heartbeats](https://octaflow.octabits.io/core/heartbeats/) | liveness for long steps, and interrupting one that was cancelled |
329
330
  | [Postgres & pg-boss](https://octaflow.octabits.io/running/postgres-and-pg-boss/) | production wiring, workers, DLQ, cron |
330
- | [Cancellation & recovery](https://octaflow.octabits.io/running/cancellation-and-recovery/) | cancelling a run, sweeping steps a crash left behind, and retrying a failed run |
331
+ | [Cancellation & recovery](https://octaflow.octabits.io/running/cancellation-and-recovery/) | cancelling a run, sweeping steps a crash left behind, retrying a failed run, and deleting the runs about a record |
331
332
  | [Observability](https://octaflow.octabits.io/running/observability/) · [Live progress](https://octaflow.octabits.io/running/live-progress/) | lifecycle events, spans, and streaming them to a browser |
332
333
  | [The AI add-on](https://octaflow.octabits.io/extending/ai/) | token/cost capture, quota, usage rollups |
333
334
  | [Extending](https://octaflow.octabits.io/extending/interfaces/) | custom stores, dispatchers and gates |
@@ -1,7 +1,7 @@
1
- import { A as TypedStep, H as RetryPolicy, M as TypedWorkflow, _t as FlowErrorShape, f as Decider, j as TypedStepContext, vt as Result } from "../index-BGmJYvTt.js";
1
+ import { A as TypedStep, H as RetryPolicy, M as TypedWorkflow, f as Decider, j as TypedStepContext, vt as FlowErrorShape, yt as Result } from "../index-B8SvtiYn.js";
2
2
  import { n as Logger } from "../logger-BCus3AOa.js";
3
- import { i as BuildStepContextArgs, o as WorkflowHooks } from "../hooks-BGOHvR37.js";
4
- import { C as isSubprocessorResidency, S as embeddingDimensionsFor, _ as EmbeddingProviderMeta, a as AiProcessingRegion, b as OpenAiCompatibleProviderId, c as AiProviderModelOption, d as AiVendorResidency, f as DEFAULT_AI_PROVIDER, g as EmbeddingProviderId, h as EMBEDDING_PROVIDER_IDS, i as AiEndpointResidency, l as AiResidencyError, m as EMBEDDING_PROVIDERS, n as AI_PROVIDER_IDS, o as AiProviderId, p as DEFAULT_EMBEDDING_PROVIDER, r as AiEndpointRegion, s as AiProviderMeta, t as AI_PROVIDERS, u as AiResidencyResult, v as OPENAI_COMPATIBLE_PROVIDER, w as resolveEndpointResidency, x as OpenAiCompatibleProviderMeta, y as OPENAI_COMPATIBLE_PROVIDER_ID } from "../provider-registry-BvXBqJDR.js";
3
+ import { i as BuildStepContextArgs, o as WorkflowHooks } from "../hooks-BV-Vany9.js";
4
+ import { C as isSubprocessorResidency, S as embeddingDimensionsFor, _ as EmbeddingProviderMeta, a as AiProcessingRegion, b as OpenAiCompatibleProviderId, c as AiProviderModelOption, d as AiVendorResidency, f as DEFAULT_AI_PROVIDER, g as EmbeddingProviderId, h as EMBEDDING_PROVIDER_IDS, i as AiEndpointResidency, l as AiResidencyError, m as EMBEDDING_PROVIDERS, n as AI_PROVIDER_IDS, o as AiProviderId, p as DEFAULT_EMBEDDING_PROVIDER, r as AiEndpointRegion, s as AiProviderMeta, t as AI_PROVIDERS, u as AiResidencyResult, v as OPENAI_COMPATIBLE_PROVIDER, w as resolveEndpointResidency, x as OpenAiCompatibleProviderMeta, y as OPENAI_COMPATIBLE_PROVIDER_ID } from "../provider-registry-C_mOfdHH.js";
5
5
  import { z } from "zod";
6
6
  import { EmbeddingModelV4, LanguageModelV4, SharedV4ProviderMetadata } from "@ai-sdk/provider";
7
7
  //#region src/ai/instrumented-model.d.ts
@@ -73,8 +73,8 @@ export interface TokenUsage {
73
73
  cacheWriteTokens: number;
74
74
  }
75
75
  /**
76
- * Default pricing, USD per million tokens — Anthropic as of 2026-09, Mistral and
77
- * the OpenAI embeddings as of 2026-08. A best-effort estimate, not a billing
76
+ * Default pricing, USD per million tokens — Anthropic as of 2026-10, Mistral and
77
+ * the OpenAI embeddings as of 2026-08, the OpenAI language models as of 2026-10. A best-effort estimate, not a billing
78
78
  * record: a BYOK caller can name any model id, and an unknown id prices at the
79
79
  * priciest row (safe overcounting — see {@link createCostEstimator}). Retired ids
80
80
  * are kept rather than deleted: dropping one silently reprices every historical
@@ -507,7 +507,8 @@ export declare function createRefusalAwareModel(model: LanguageModelV4): Languag
507
507
  /**
508
508
  * Per-model API capability switches.
509
509
  *
510
- * Claude 4.6+ models (Opus 4.6/4.7/4.8/5, Sonnet 4.6/5, Fable/Mythos 5) use
510
+ * Claude 4.6+ models (Opus 4.6/4.7/4.8/5/5.5, Sonnet 4.6/5/5.5, Haiku 5.5,
511
+ * Fable/Mythos 5) use
511
512
  * adaptive thinking; on 4.7+ the legacy `thinking: { type: 'enabled',
512
513
  * budget_tokens }` shape and non-default sampling params (`temperature`,
513
514
  * `top_p`, `top_k`) are rejected with a 400. Older models (Haiku 4.5,
@@ -519,7 +520,7 @@ export declare function createRefusalAwareModel(model: LanguageModelV4): Languag
519
520
  * functions are that branch, written once. `providerId` is a plain string:
520
521
  * only `'anthropic'` has provider-specific shapes today.
521
522
  */
522
- /** True for Claude 4.6+ / 5-family models: adaptive thinking, no budgetTokens, no sampling params. */
523
+ /** True for Claude 4.6+ / 5-family models (`claude-opus-5` covers `claude-opus-5-5`, and so on): adaptive thinking, no budgetTokens, no sampling params. */
523
524
  export declare function supportsAdaptiveThinking(modelId: string): boolean;
524
525
  export interface AnthropicThinkingSettings {
525
526
  temperature?: number;
package/dist/ai/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { D as ok, E as err, T as noopLogger, _ as defineStep, m as buildWorkflow } from "../core-C9h3-KFr.js";
2
2
  import { a as readRefusalDetails, i as isAiRefusalError, n as REFUSAL_FINISH_REASON, r as createRefusalAwareModel, t as AiRefusalError } from "../refusal-BsL2HPDb.js";
3
- import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, i as DEFAULT_EMBEDDING_PROVIDER, l as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as OPENAI_COMPATIBLE_PROVIDER, t as AI_PROVIDERS, u as isSubprocessorResidency } from "../provider-registry-BRwjC6_3.js";
3
+ import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, i as DEFAULT_EMBEDDING_PROVIDER, l as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as OPENAI_COMPATIBLE_PROVIDER, t as AI_PROVIDERS, u as isSubprocessorResidency } from "../provider-registry-B91lgOmu.js";
4
4
  import { z } from "zod";
5
5
  import { Output, generateText, wrapEmbeddingModel, wrapLanguageModel } from "ai";
6
6
  //#region src/ai/instrumented-model.ts
@@ -125,8 +125,8 @@ function createInstrumentedEmbeddingModel(model, accumulator) {
125
125
  //#endregion
126
126
  //#region src/ai/cost.ts
127
127
  /**
128
- * Default pricing, USD per million tokens — Anthropic as of 2026-09, Mistral and
129
- * the OpenAI embeddings as of 2026-08. A best-effort estimate, not a billing
128
+ * Default pricing, USD per million tokens — Anthropic as of 2026-10, Mistral and
129
+ * the OpenAI embeddings as of 2026-08, the OpenAI language models as of 2026-10. A best-effort estimate, not a billing
130
130
  * record: a BYOK caller can name any model id, and an unknown id prices at the
131
131
  * priciest row (safe overcounting — see {@link createCostEstimator}). Retired ids
132
132
  * are kept rather than deleted: dropping one silently reprices every historical
@@ -170,6 +170,12 @@ const DEFAULT_MODEL_PRICING = {
170
170
  cacheReadPerMillion: .25,
171
171
  cacheWritePerMillion: 12.5
172
172
  },
173
+ "claude-opus-5-5": {
174
+ inputPerMillion: 4,
175
+ outputPerMillion: 20,
176
+ cacheReadPerMillion: .2,
177
+ cacheWritePerMillion: 5
178
+ },
173
179
  "claude-opus-5": {
174
180
  inputPerMillion: 5,
175
181
  outputPerMillion: 25,
@@ -182,6 +188,18 @@ const DEFAULT_MODEL_PRICING = {
182
188
  cacheReadPerMillion: .2,
183
189
  cacheWritePerMillion: 2.5
184
190
  },
191
+ "claude-sonnet-5-5": {
192
+ inputPerMillion: 2,
193
+ outputPerMillion: 10,
194
+ cacheReadPerMillion: .2,
195
+ cacheWritePerMillion: 2.5
196
+ },
197
+ "claude-haiku-5-5": {
198
+ inputPerMillion: .1,
199
+ outputPerMillion: .5,
200
+ cacheReadPerMillion: .01,
201
+ cacheWritePerMillion: .125
202
+ },
185
203
  "claude-opus-4-8": {
186
204
  inputPerMillion: 5,
187
205
  outputPerMillion: 25,
@@ -265,6 +283,30 @@ const DEFAULT_MODEL_PRICING = {
265
283
  outputPerMillion: 0,
266
284
  cacheReadPerMillion: 0,
267
285
  cacheWritePerMillion: 0
286
+ },
287
+ "gpt-6-astra": {
288
+ inputPerMillion: 10,
289
+ outputPerMillion: 50,
290
+ cacheReadPerMillion: 1,
291
+ cacheWritePerMillion: 12.5
292
+ },
293
+ "gpt-6.1-sol": {
294
+ inputPerMillion: 2,
295
+ outputPerMillion: 10,
296
+ cacheReadPerMillion: .1,
297
+ cacheWritePerMillion: 2.5
298
+ },
299
+ "gpt-6-sol": {
300
+ inputPerMillion: 2,
301
+ outputPerMillion: 10,
302
+ cacheReadPerMillion: .2,
303
+ cacheWritePerMillion: 2.5
304
+ },
305
+ "gpt-6-luna": {
306
+ inputPerMillion: .1,
307
+ outputPerMillion: .5,
308
+ cacheReadPerMillion: .01,
309
+ cacheWritePerMillion: .125
268
310
  }
269
311
  };
270
312
  /**
@@ -627,7 +669,8 @@ function createAiUsageAggregationService(deps) {
627
669
  /**
628
670
  * Per-model API capability switches.
629
671
  *
630
- * Claude 4.6+ models (Opus 4.6/4.7/4.8/5, Sonnet 4.6/5, Fable/Mythos 5) use
672
+ * Claude 4.6+ models (Opus 4.6/4.7/4.8/5/5.5, Sonnet 4.6/5/5.5, Haiku 5.5,
673
+ * Fable/Mythos 5) use
631
674
  * adaptive thinking; on 4.7+ the legacy `thinking: { type: 'enabled',
632
675
  * budget_tokens }` shape and non-default sampling params (`temperature`,
633
676
  * `top_p`, `top_k`) are rejected with a 400. Older models (Haiku 4.5,
@@ -647,9 +690,10 @@ const ADAPTIVE_THINKING_MODEL_PREFIXES = [
647
690
  "claude-opus-4-7",
648
691
  "claude-opus-4-6",
649
692
  "claude-sonnet-5",
650
- "claude-sonnet-4-6"
693
+ "claude-sonnet-4-6",
694
+ "claude-haiku-5"
651
695
  ];
652
- /** True for Claude 4.6+ / 5-family models: adaptive thinking, no budgetTokens, no sampling params. */
696
+ /** True for Claude 4.6+ / 5-family models (`claude-opus-5` covers `claude-opus-5-5`, and so on): adaptive thinking, no budgetTokens, no sampling params. */
653
697
  function supportsAdaptiveThinking(modelId) {
654
698
  return ADAPTIVE_THINKING_MODEL_PREFIXES.some((prefix) => modelId.startsWith(prefix));
655
699
  }
@@ -1,4 +1,4 @@
1
- import { b as OpenAiCompatibleProviderId, g as EmbeddingProviderId, i as AiEndpointResidency, o as AiProviderId } from "../../provider-registry-BvXBqJDR.js";
1
+ import { b as OpenAiCompatibleProviderId, g as EmbeddingProviderId, i as AiEndpointResidency, o as AiProviderId } from "../../provider-registry-C_mOfdHH.js";
2
2
  import { EmbeddingModelV4, LanguageModelV4 } from "@ai-sdk/provider";
3
3
  //#region src/ai/providers/index.d.ts
4
4
  export type LanguageModelFactory = (modelId: string) => LanguageModelV4;
@@ -1,5 +1,5 @@
1
1
  import { r as createRefusalAwareModel } from "../../refusal-BsL2HPDb.js";
2
- import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, t as AI_PROVIDERS } from "../../provider-registry-BRwjC6_3.js";
2
+ import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, t as AI_PROVIDERS } from "../../provider-registry-B91lgOmu.js";
3
3
  import { wrapLanguageModel } from "ai";
4
4
  //#region src/ai/providers/index.ts
5
5
  const LLM_LOADERS = {
@@ -16,6 +16,13 @@ const LLM_LOADERS = {
16
16
  apiKey,
17
17
  baseURL
18
18
  });
19
+ },
20
+ openai: async (apiKey, baseURL) => {
21
+ const { createOpenAI } = await import("@ai-sdk/openai");
22
+ return createOpenAI({
23
+ apiKey,
24
+ baseURL
25
+ });
19
26
  }
20
27
  };
21
28
  const EMBEDDING_LOADERS = {
@@ -1,2 +1,2 @@
1
- import { C as isSubprocessorResidency, S as embeddingDimensionsFor, _ as EmbeddingProviderMeta, a as AiProcessingRegion, b as OpenAiCompatibleProviderId, c as AiProviderModelOption, d as AiVendorResidency, f as DEFAULT_AI_PROVIDER, g as EmbeddingProviderId, h as EMBEDDING_PROVIDER_IDS, i as AiEndpointResidency, l as AiResidencyError, m as EMBEDDING_PROVIDERS, n as AI_PROVIDER_IDS, o as AiProviderId, p as DEFAULT_EMBEDDING_PROVIDER, r as AiEndpointRegion, s as AiProviderMeta, t as AI_PROVIDERS, u as AiResidencyResult, v as OPENAI_COMPATIBLE_PROVIDER, w as resolveEndpointResidency, x as OpenAiCompatibleProviderMeta, y as OPENAI_COMPATIBLE_PROVIDER_ID } from "../provider-registry-BvXBqJDR.js";
1
+ import { C as isSubprocessorResidency, S as embeddingDimensionsFor, _ as EmbeddingProviderMeta, a as AiProcessingRegion, b as OpenAiCompatibleProviderId, c as AiProviderModelOption, d as AiVendorResidency, f as DEFAULT_AI_PROVIDER, g as EmbeddingProviderId, h as EMBEDDING_PROVIDER_IDS, i as AiEndpointResidency, l as AiResidencyError, m as EMBEDDING_PROVIDERS, n as AI_PROVIDER_IDS, o as AiProviderId, p as DEFAULT_EMBEDDING_PROVIDER, r as AiEndpointRegion, s as AiProviderMeta, t as AI_PROVIDERS, u as AiResidencyResult, v as OPENAI_COMPATIBLE_PROVIDER, w as resolveEndpointResidency, x as OpenAiCompatibleProviderMeta, y as OPENAI_COMPATIBLE_PROVIDER_ID } from "../provider-registry-C_mOfdHH.js";
2
2
  export { AI_PROVIDERS, AI_PROVIDER_IDS, AiEndpointRegion, AiEndpointResidency, AiProcessingRegion, AiProviderId, AiProviderMeta, AiProviderModelOption, AiResidencyError, AiResidencyResult, AiVendorResidency, DEFAULT_AI_PROVIDER, DEFAULT_EMBEDDING_PROVIDER, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, EmbeddingProviderId, EmbeddingProviderMeta, OPENAI_COMPATIBLE_PROVIDER, OPENAI_COMPATIBLE_PROVIDER_ID, OpenAiCompatibleProviderId, OpenAiCompatibleProviderMeta, embeddingDimensionsFor, isSubprocessorResidency, resolveEndpointResidency };
@@ -1,2 +1,2 @@
1
- import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, i as DEFAULT_EMBEDDING_PROVIDER, l as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as OPENAI_COMPATIBLE_PROVIDER, t as AI_PROVIDERS, u as isSubprocessorResidency } from "../provider-registry-BRwjC6_3.js";
1
+ import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, i as DEFAULT_EMBEDDING_PROVIDER, l as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as OPENAI_COMPATIBLE_PROVIDER, t as AI_PROVIDERS, u as isSubprocessorResidency } from "../provider-registry-B91lgOmu.js";
2
2
  export { AI_PROVIDERS, AI_PROVIDER_IDS, DEFAULT_AI_PROVIDER, DEFAULT_EMBEDDING_PROVIDER, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, OPENAI_COMPATIBLE_PROVIDER, OPENAI_COMPATIBLE_PROVIDER_ID, embeddingDimensionsFor, isSubprocessorResidency, resolveEndpointResidency };
@@ -1,4 +1,4 @@
1
- import { $ as StepId, _t as FlowErrorShape, ot as WorkflowId, vt as Result } from "./index-BGmJYvTt.js";
1
+ import { $ as StepId, ot as WorkflowId, vt as FlowErrorShape, yt as Result } from "./index-B8SvtiYn.js";
2
2
  //#region src/core/dispatcher.d.ts
3
3
  /**
4
4
  * What a step job is for. A queue carries two kinds of work for a step, and
@@ -1,4 +1,4 @@
1
- import { $ as StepId, U as StartOptions, _t as FlowErrorShape, at as WorkflowDefinition, et as StepRecord, lt as WorkflowRecord, ot as WorkflowId, vt as Result } from "./index-BGmJYvTt.js";
1
+ import { $ as StepId, U as StartOptions, at as WorkflowDefinition, et as StepRecord, lt as WorkflowRecord, ot as WorkflowId, vt as FlowErrorShape, yt as Result } from "./index-B8SvtiYn.js";
2
2
  //#region src/core/hooks.d.ts
3
3
  interface BeforeStartArgs {
4
4
  definition: WorkflowDefinition;
@@ -373,12 +373,25 @@ interface WorkflowNotRetryableError extends FlowErrorShape {
373
373
  key: 'workflow_not_retryable';
374
374
  status: WorkflowStatus;
375
375
  }
376
+ /**
377
+ * `purgeWorkflows` refused: a step of one of the runs is executing right now (a worker
378
+ * holds it, or a compensation handler is running). Nothing was cancelled or deleted.
379
+ */
380
+ interface WorkflowStepRunningError extends FlowErrorShape {
381
+ key: 'workflow_step_running';
382
+ /** The steps that are executing, by run. */
383
+ running: Array<{
384
+ workflowId: WorkflowId;
385
+ stepKey: string;
386
+ status: StepStatus;
387
+ }>;
388
+ }
376
389
  /**
377
390
  * Engine error type. The named members cover flow-core's own failures; the
378
391
  * bare `FlowErrorShape` arm lets hooks (e.g. an AI quota guard in `onBeforeStart`)
379
392
  * reject a start with a domain-specific error key the engine just passes through.
380
393
  */
381
- type FlowError = WorkflowNotFoundError | InvalidWorkflowDefinitionError | StepHandlerNotFoundError | WorkflowNotRetryableError | StepError | FlowErrorShape;
394
+ type FlowError = WorkflowNotFoundError | InvalidWorkflowDefinitionError | StepHandlerNotFoundError | WorkflowNotRetryableError | WorkflowStepRunningError | StepError | FlowErrorShape;
382
395
  //#endregion
383
396
  //#region src/core/defineStep.d.ts
384
397
  /**
@@ -993,4 +1006,4 @@ declare function toPublicStep(step: StepRecord): PublicWorkflowStep;
993
1006
  */
994
1007
  declare function toPublicWorkflow(workflow: WorkflowWithSteps): PublicWorkflow;
995
1008
  //#endregion
996
- export { StepId as $, TypedStep as A, FlowError as B, defineDecisionOutcomeStep as C, resolveDecision as D, functionDecider as E, defineMapStep as F, StepCompensationContext as G, RetryPolicy as H, defineSleepStep as I, StepError as J, StepConditionHandler as K, defineStep as L, TypedWorkflow as M, WorkflowOutput as N, StepDeps as O, buildWorkflow as P, StepHandlerRegistry as Q, defineSubWorkflowStep as R, DecisionStep as S, defineDecisionStep as T, StartOptions as U, InvalidWorkflowDefinitionError as V, StepCompensateHandler as W, StepHandler as X, StepExecutionContext as Y, StepHandlerNotFoundError as Z, DecisionOutput as _, FlowErrorShape as _t, STEP_DISPLAY_STATUS as a, WorkflowDefinition as at, DecisionResponse as b, ok as bt, WORKFLOW_STATUS_SCHEMA as c, WorkflowNotRetryableError as ct, toPublicWorkflow as d, WorkflowWithSteps as dt, StepRecord as et, Decider as f, JoinRule as ft, DecisionOutcome as g, isTerminalStepStatus as gt, DecisionChoices as h, computeReadiness as ht, PublicWorkflowStep as i, WorkflowCreatedResult as it, TypedStepContext as j, StepOutput as k, toDisplayStepStatus as l, WorkflowRecord as lt, DecisionAnswers as m, ReadinessPlan as mt, PUBLIC_WORKFLOW_STEP_SCHEMA as n, StepStatus as nt, STEP_DISPLAY_STATUS_SCHEMA as o, WorkflowId as ot, DecisionAnswer as p, PlannedSkip as pt, StepDefinition as q, PublicWorkflow as r, WaitTimeoutPolicy as rt, StepDisplayStatus as s, WorkflowNotFoundError as st, PUBLIC_WORKFLOW_SCHEMA as t, StepRegistration as tt, toPublicStep as u, WorkflowStatus as ut, DecisionQuestions as v, Result as vt, defineDecisionReviewStep as w, DecisionReview as x, DecisionRequest as y, err as yt, defineWaitStep as z };
1009
+ export { StepId as $, TypedStep as A, FlowError as B, defineDecisionOutcomeStep as C, resolveDecision as D, functionDecider as E, defineMapStep as F, StepCompensationContext as G, RetryPolicy as H, defineSleepStep as I, StepError as J, StepConditionHandler as K, defineStep as L, TypedWorkflow as M, WorkflowOutput as N, StepDeps as O, buildWorkflow as P, StepHandlerRegistry as Q, defineSubWorkflowStep as R, DecisionStep as S, defineDecisionStep as T, StartOptions as U, InvalidWorkflowDefinitionError as V, StepCompensateHandler as W, StepHandler as X, StepExecutionContext as Y, StepHandlerNotFoundError as Z, DecisionOutput as _, isTerminalStepStatus as _t, STEP_DISPLAY_STATUS as a, WorkflowDefinition as at, DecisionResponse as b, err as bt, WORKFLOW_STATUS_SCHEMA as c, WorkflowNotRetryableError as ct, toPublicWorkflow as d, WorkflowStepRunningError as dt, StepRecord as et, Decider as f, WorkflowWithSteps as ft, DecisionOutcome as g, computeReadiness as gt, DecisionChoices as h, ReadinessPlan as ht, PublicWorkflowStep as i, WorkflowCreatedResult as it, TypedStepContext as j, StepOutput as k, toDisplayStepStatus as l, WorkflowRecord as lt, DecisionAnswers as m, PlannedSkip as mt, PUBLIC_WORKFLOW_STEP_SCHEMA as n, StepStatus as nt, STEP_DISPLAY_STATUS_SCHEMA as o, WorkflowId as ot, DecisionAnswer as p, JoinRule as pt, StepDefinition as q, PublicWorkflow as r, WaitTimeoutPolicy as rt, StepDisplayStatus as s, WorkflowNotFoundError as st, PUBLIC_WORKFLOW_SCHEMA as t, StepRegistration as tt, toPublicStep as u, WorkflowStatus as ut, DecisionQuestions as v, FlowErrorShape as vt, defineDecisionReviewStep as w, DecisionReview as x, ok as xt, DecisionRequest as y, Result as yt, defineWaitStep as z };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { $ as StepId, A as TypedStep, B as FlowError, C as defineDecisionOutcomeStep, D as resolveDecision, E as functionDecider, F as defineMapStep, G as StepCompensationContext, H as RetryPolicy, I as defineSleepStep, J as StepError, K as StepConditionHandler, L as defineStep, M as TypedWorkflow, N as WorkflowOutput, O as StepDeps, P as buildWorkflow, Q as StepHandlerRegistry, R as defineSubWorkflowStep, S as DecisionStep, T as defineDecisionStep, U as StartOptions, V as InvalidWorkflowDefinitionError, W as StepCompensateHandler, X as StepHandler, Y as StepExecutionContext, Z as StepHandlerNotFoundError, _ as DecisionOutput, _t as FlowErrorShape, a as STEP_DISPLAY_STATUS, at as WorkflowDefinition, b as DecisionResponse, bt as ok, c as WORKFLOW_STATUS_SCHEMA, ct as WorkflowNotRetryableError, d as toPublicWorkflow, dt as WorkflowWithSteps, et as StepRecord, f as Decider, ft as JoinRule, g as DecisionOutcome, gt as isTerminalStepStatus, h as DecisionChoices, ht as computeReadiness, i as PublicWorkflowStep, it as WorkflowCreatedResult, j as TypedStepContext, k as StepOutput, l as toDisplayStepStatus, lt as WorkflowRecord, m as DecisionAnswers, mt as ReadinessPlan, n as PUBLIC_WORKFLOW_STEP_SCHEMA, nt as StepStatus, o as STEP_DISPLAY_STATUS_SCHEMA, ot as WorkflowId, p as DecisionAnswer, pt as PlannedSkip, q as StepDefinition, r as PublicWorkflow, rt as WaitTimeoutPolicy, s as StepDisplayStatus, st as WorkflowNotFoundError, t as PUBLIC_WORKFLOW_SCHEMA, tt as StepRegistration, u as toPublicStep, ut as WorkflowStatus, v as DecisionQuestions, vt as Result, w as defineDecisionReviewStep, x as DecisionReview, y as DecisionRequest, yt as err, z as defineWaitStep } from "./index-BGmJYvTt.js";
1
+ import { $ as StepId, A as TypedStep, B as FlowError, C as defineDecisionOutcomeStep, D as resolveDecision, E as functionDecider, F as defineMapStep, G as StepCompensationContext, H as RetryPolicy, I as defineSleepStep, J as StepError, K as StepConditionHandler, L as defineStep, M as TypedWorkflow, N as WorkflowOutput, O as StepDeps, P as buildWorkflow, Q as StepHandlerRegistry, R as defineSubWorkflowStep, S as DecisionStep, T as defineDecisionStep, U as StartOptions, V as InvalidWorkflowDefinitionError, W as StepCompensateHandler, X as StepHandler, Y as StepExecutionContext, Z as StepHandlerNotFoundError, _ as DecisionOutput, _t as isTerminalStepStatus, a as STEP_DISPLAY_STATUS, at as WorkflowDefinition, b as DecisionResponse, bt as err, c as WORKFLOW_STATUS_SCHEMA, ct as WorkflowNotRetryableError, d as toPublicWorkflow, dt as WorkflowStepRunningError, et as StepRecord, f as Decider, ft as WorkflowWithSteps, g as DecisionOutcome, gt as computeReadiness, h as DecisionChoices, ht as ReadinessPlan, i as PublicWorkflowStep, it as WorkflowCreatedResult, j as TypedStepContext, k as StepOutput, l as toDisplayStepStatus, lt as WorkflowRecord, m as DecisionAnswers, mt as PlannedSkip, n as PUBLIC_WORKFLOW_STEP_SCHEMA, nt as StepStatus, o as STEP_DISPLAY_STATUS_SCHEMA, ot as WorkflowId, p as DecisionAnswer, pt as JoinRule, q as StepDefinition, r as PublicWorkflow, rt as WaitTimeoutPolicy, s as StepDisplayStatus, st as WorkflowNotFoundError, t as PUBLIC_WORKFLOW_SCHEMA, tt as StepRegistration, u as toPublicStep, ut as WorkflowStatus, v as DecisionQuestions, vt as FlowErrorShape, w as defineDecisionReviewStep, x as DecisionReview, xt as ok, y as DecisionRequest, yt as Result, z as defineWaitStep } from "./index-B8SvtiYn.js";
2
2
  import { n as Logger, r as noopLogger, t as LogAttributes } from "./logger-BCus3AOa.js";
3
- import { A as ListWorkflowsFilters, C as AddChildStep, D as CreatedWorkflow, E as CreateWorkflowStep, M as TransactionalScope, N as WorkflowStore, O as FailStepParams, S as createInMemoryStepGate, T as CreateWorkflowParams, _ as InMemoryStepGateConfig, a as FlowSpan, b as StepGateDecision, c as RecordingObserver, d as createMeterObserver, f as createRecordingObserver, g as ConcurrencyRule, h as noopTracer, i as FlowObserver, j as ReopenWorkflowParams, k as FinishWorkflowParams, l as RecordingTracer, m as noopObserver, n as FlowEventType, o as FlowTracer, p as createRecordingTracer, r as FlowMeter, s as RecordedSpan, t as FlowEvent, u as combineObservers, v as RateRule, w as CompleteStepParams, x as StepGateRequest, y as StepGate } from "./observability-C-jfi2DX.js";
4
- import { i as EnqueueOptions, n as DispatchStepPayload, r as Dispatcher, t as DispatchKind } from "./dispatcher-Bzkqq0Ax.js";
5
- import { a as WorkflowCompletedArgs, i as BuildStepContextArgs, n as BeforeStartArgs, o as WorkflowHooks, r as BeforeStartResult, t as AfterStepArgs } from "./hooks-BGOHvR37.js";
3
+ import { A as ListWorkflowsFilters, C as AddChildStep, D as CreatedWorkflow, E as CreateWorkflowStep, M as TransactionalScope, N as WorkflowStore, O as FailStepParams, P as WorkflowTreeFilter, S as createInMemoryStepGate, T as CreateWorkflowParams, _ as InMemoryStepGateConfig, a as FlowSpan, b as StepGateDecision, c as RecordingObserver, d as createMeterObserver, f as createRecordingObserver, g as ConcurrencyRule, h as noopTracer, i as FlowObserver, j as ReopenWorkflowParams, k as FinishWorkflowParams, l as RecordingTracer, m as noopObserver, n as FlowEventType, o as FlowTracer, p as createRecordingTracer, r as FlowMeter, s as RecordedSpan, t as FlowEvent, u as combineObservers, v as RateRule, w as CompleteStepParams, x as StepGateRequest, y as StepGate } from "./observability-DRUJOpiQ.js";
4
+ import { i as EnqueueOptions, n as DispatchStepPayload, r as Dispatcher, t as DispatchKind } from "./dispatcher-B4haXU4w.js";
5
+ import { a as WorkflowCompletedArgs, i as BuildStepContextArgs, n as BeforeStartArgs, o as WorkflowHooks, r as BeforeStartResult, t as AfterStepArgs } from "./hooks-BV-Vany9.js";
6
6
  //#region src/core/registry.d.ts
7
7
  /**
8
8
  * Creates an in-memory step handler registry.
@@ -114,6 +114,11 @@ export interface RecoverySweepResult {
114
114
  /** Workflows failed for outliving `StartOptions.timeoutMs`. */
115
115
  expiredWorkflows: number;
116
116
  }
117
+ /** What `purgeWorkflows` deleted. */
118
+ export interface PurgeWorkflowsResult {
119
+ /** Every run deleted, the sub-workflow runs below the matching ones included, ascending. */
120
+ workflowIds: WorkflowId[];
121
+ }
117
122
  /** What `retryWorkflow` put back in flight. */
118
123
  export interface RetryWorkflowResult {
119
124
  workflowId: WorkflowId;
@@ -137,6 +142,7 @@ export declare function createWorkflowEngine<TContext = unknown>(deps: WorkflowE
137
142
  recoverStuckWorkflows: () => Promise<RecoverySweepResult>;
138
143
  retryWorkflow: (workflowId: WorkflowId) => Promise<Result<RetryWorkflowResult, FlowError>>;
139
144
  cancelWorkflow: (workflowId: WorkflowId) => Promise<Result<void, FlowError>>;
145
+ purgeWorkflows: (filter: WorkflowTreeFilter) => Promise<Result<PurgeWorkflowsResult, FlowError>>;
140
146
  getWorkflowStatus: (workflowId: WorkflowId) => Promise<Result<WorkflowWithSteps, FlowError>>;
141
147
  listWorkflows: (filters?: {
142
148
  status?: WorkflowStatus;
@@ -162,4 +168,4 @@ export declare function createInMemoryWorkflowStore(partitionKey?: string): Work
162
168
  };
163
169
  };
164
170
  //#endregion
165
- export { AddChildStep, AfterStepArgs, BeforeStartArgs, BeforeStartResult, BuildStepContextArgs, CompleteStepParams, ConcurrencyRule, CreateWorkflowParams, CreateWorkflowStep, CreatedWorkflow, Decider, DecisionAnswer, DecisionAnswers, DecisionChoices, DecisionOutcome, DecisionOutput, DecisionQuestions, DecisionRequest, DecisionResponse, DecisionReview, DecisionStep, DispatchKind, DispatchStepPayload, Dispatcher, EnqueueOptions, FailStepParams, FinishWorkflowParams, FlowError, FlowErrorShape, FlowEvent, FlowEventType, FlowMeter, FlowObserver, FlowSpan, FlowTracer, InMemoryStepGateConfig, InvalidWorkflowDefinitionError, JoinRule, ListWorkflowsFilters, LogAttributes, Logger, PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, PlannedSkip, PublicWorkflow, PublicWorkflowStep, RateRule, ReadinessPlan, RecordedSpan, RecordingObserver, RecordingTracer, ReopenWorkflowParams, Result, RetryPolicy, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, StartOptions, StepCompensateHandler, StepCompensationContext, StepConditionHandler, StepDefinition, StepDeps, StepDisplayStatus, StepError, StepExecutionContext, StepGate, StepGateDecision, StepGateRequest, StepHandler, StepHandlerNotFoundError, StepHandlerRegistry, StepId, StepOutput, StepRecord, StepRegistration, StepStatus, TransactionalScope, TypedStep, TypedStepContext, TypedWorkflow, WORKFLOW_STATUS_SCHEMA, WaitTimeoutPolicy, WorkflowCompletedArgs, WorkflowCreatedResult, WorkflowDefinition, WorkflowHooks, WorkflowId, WorkflowNotFoundError, WorkflowNotRetryableError, WorkflowOutput, WorkflowRecord, WorkflowStatus, WorkflowStore, WorkflowWithSteps, buildWorkflow, combineObservers, computeReadiness, createInMemoryStepGate, createMeterObserver, createRecordingObserver, createRecordingTracer, defineDecisionOutcomeStep, defineDecisionReviewStep, defineDecisionStep, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, functionDecider, isTerminalStepStatus, noopLogger, noopObserver, noopTracer, ok, resolveDecision, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
171
+ export { AddChildStep, AfterStepArgs, BeforeStartArgs, BeforeStartResult, BuildStepContextArgs, CompleteStepParams, ConcurrencyRule, CreateWorkflowParams, CreateWorkflowStep, CreatedWorkflow, Decider, DecisionAnswer, DecisionAnswers, DecisionChoices, DecisionOutcome, DecisionOutput, DecisionQuestions, DecisionRequest, DecisionResponse, DecisionReview, DecisionStep, DispatchKind, DispatchStepPayload, Dispatcher, EnqueueOptions, FailStepParams, FinishWorkflowParams, FlowError, FlowErrorShape, FlowEvent, FlowEventType, FlowMeter, FlowObserver, FlowSpan, FlowTracer, InMemoryStepGateConfig, InvalidWorkflowDefinitionError, JoinRule, ListWorkflowsFilters, LogAttributes, Logger, PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, PlannedSkip, PublicWorkflow, PublicWorkflowStep, RateRule, ReadinessPlan, RecordedSpan, RecordingObserver, RecordingTracer, ReopenWorkflowParams, Result, RetryPolicy, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, StartOptions, StepCompensateHandler, StepCompensationContext, StepConditionHandler, StepDefinition, StepDeps, StepDisplayStatus, StepError, StepExecutionContext, StepGate, StepGateDecision, StepGateRequest, StepHandler, StepHandlerNotFoundError, StepHandlerRegistry, StepId, StepOutput, StepRecord, StepRegistration, StepStatus, TransactionalScope, TypedStep, TypedStepContext, TypedWorkflow, WORKFLOW_STATUS_SCHEMA, WaitTimeoutPolicy, WorkflowCompletedArgs, WorkflowCreatedResult, WorkflowDefinition, WorkflowHooks, WorkflowId, WorkflowNotFoundError, WorkflowNotRetryableError, WorkflowOutput, WorkflowRecord, WorkflowStatus, WorkflowStepRunningError, WorkflowStore, WorkflowTreeFilter, WorkflowWithSteps, buildWorkflow, combineObservers, computeReadiness, createInMemoryStepGate, createMeterObserver, createRecordingObserver, createRecordingTracer, defineDecisionOutcomeStep, defineDecisionReviewStep, defineDecisionStep, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, functionDecider, isTerminalStepStatus, noopLogger, noopObserver, noopTracer, ok, resolveDecision, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
package/dist/index.js CHANGED
@@ -1715,6 +1715,60 @@ function createWorkflowEngine(deps) {
1715
1715
  value: void 0
1716
1716
  };
1717
1717
  }
1718
+ /**
1719
+ * Delete every run about one entity — the runs whose `entityRef` matches and every
1720
+ * sub-workflow run below them — with all of their steps: what a model was given and
1721
+ * what it said goes with them. For erasing a person, or deleting the record the runs
1722
+ * were about.
1723
+ *
1724
+ * Refuses with `workflow_step_running` while any of those runs has a step executing
1725
+ * (`running`, or `compensating`): a worker is inside its handler, and deleting the run
1726
+ * under it would let it write about something that no longer exists. Nothing is
1727
+ * cancelled or deleted then; try again once the step settles. A run that only *waits*
1728
+ * — for a person (`waiting`), a sleep, a sub-workflow, queued work — does not stop it.
1729
+ *
1730
+ * Otherwise every active run is cancelled first, parents before children, exactly as
1731
+ * `cancelWorkflow` would (a matching sub-workflow run whose parent is not being purged
1732
+ * fails the parent's step), and then all of them are deleted. Whatever was still queued
1733
+ * for them — a step job, a wait deadline, a `resumeStep` — finds no run and ends in
1734
+ * `workflow_not_found`; nothing is written back. `workflow.cancelled` and then
1735
+ * `workflow.purged` are emitted per run.
1736
+ *
1737
+ * Runs, not history: rows a host wrote elsewhere keyed by the run id (an event sink's
1738
+ * `flow_step_event`, a ledger entry, a usage rollup) are the host's to keep or delete —
1739
+ * the ids come back for that.
1740
+ */
1741
+ async function purgeWorkflows(filter) {
1742
+ const tree = await store.listWorkflowTree(filter);
1743
+ if (tree.length === 0) return {
1744
+ ok: true,
1745
+ value: { workflowIds: [] }
1746
+ };
1747
+ const running = tree.flatMap((w) => w.steps.filter((s) => s.status === "running" || s.status === "compensating").map((s) => ({
1748
+ workflowId: w.id,
1749
+ stepKey: s.key,
1750
+ status: s.status
1751
+ })));
1752
+ if (running.length > 0) return {
1753
+ ok: false,
1754
+ error: {
1755
+ key: "workflow_step_running",
1756
+ running,
1757
+ message: `A step is executing in ${[...new Set(running.map((r) => r.workflowId))].map((id) => `run ${id}`).join(", ")}; try again once it settles`
1758
+ }
1759
+ };
1760
+ for (const w of tree) if (w.status === "pending" || w.status === "running") await cancelWorkflow(w.id);
1761
+ const workflowIds = await store.deleteWorkflows(filter);
1762
+ logger.info("Workflows purged", { count: workflowIds.length });
1763
+ for (const workflowId of workflowIds) emit({
1764
+ type: "workflow.purged",
1765
+ workflowId
1766
+ });
1767
+ return {
1768
+ ok: true,
1769
+ value: { workflowIds }
1770
+ };
1771
+ }
1718
1772
  async function getWorkflowStatus(workflowId) {
1719
1773
  const workflow = await store.getWorkflow(workflowId);
1720
1774
  if (!workflow) return {
@@ -1945,6 +1999,7 @@ function createWorkflowEngine(deps) {
1945
1999
  recoverStuckWorkflows,
1946
2000
  retryWorkflow,
1947
2001
  cancelWorkflow,
2002
+ purgeWorkflows,
1948
2003
  getWorkflowStatus,
1949
2004
  listWorkflows
1950
2005
  };
@@ -2190,6 +2245,35 @@ function createInMemoryWorkflowStore(partitionKey = "default") {
2190
2245
  steps: stepsOf(w.id).map(clone)
2191
2246
  }));
2192
2247
  }
2248
+ /** The runs about the entity and every sub-workflow run below them, ascending by id. */
2249
+ function treeOf(filter) {
2250
+ const ids = /* @__PURE__ */ new Set();
2251
+ for (const w of workflows.values()) if (w.entityRef === filter.entityRef) ids.add(w.id);
2252
+ let grew = ids.size > 0;
2253
+ while (grew) {
2254
+ grew = false;
2255
+ for (const w of workflows.values()) if (w.parentWorkflowId != null && ids.has(w.parentWorkflowId) && !ids.has(w.id)) {
2256
+ ids.add(w.id);
2257
+ grew = true;
2258
+ }
2259
+ }
2260
+ return Array.from(ids, (id) => workflows.get(id)).sort((a, b) => a.id - b.id);
2261
+ }
2262
+ async function listWorkflowTree(filter) {
2263
+ return treeOf(filter).map((w) => ({
2264
+ ...clone(w),
2265
+ steps: stepsOf(w.id).map(clone)
2266
+ }));
2267
+ }
2268
+ async function deleteWorkflows(filter) {
2269
+ const doomed = treeOf(filter);
2270
+ for (const w of doomed) {
2271
+ for (const s of stepsOf(w.id)) steps.delete(s.id);
2272
+ workflows.delete(w.id);
2273
+ if (w.idempotencyKey != null && byIdempotency.get(w.idempotencyKey) === w.id) byIdempotency.delete(w.idempotencyKey);
2274
+ }
2275
+ return doomed.map((w) => w.id);
2276
+ }
2193
2277
  async function listRunningWorkflows() {
2194
2278
  return Array.from(workflows.values()).filter((w) => w.status === "running").map(clone);
2195
2279
  }
@@ -2222,6 +2306,8 @@ function createInMemoryWorkflowStore(partitionKey = "default") {
2222
2306
  skipPendingSteps,
2223
2307
  finishWorkflow,
2224
2308
  listWorkflows,
2309
+ listWorkflowTree,
2310
+ deleteWorkflows,
2225
2311
  listRunningWorkflows,
2226
2312
  findStuckSteps,
2227
2313
  _dump() {
@@ -1,4 +1,4 @@
1
- import { $ as StepId, dt as WorkflowWithSteps, et as StepRecord, lt as WorkflowRecord, ot as WorkflowId, ut as WorkflowStatus } from "./index-BGmJYvTt.js";
1
+ import { $ as StepId, et as StepRecord, ft as WorkflowWithSteps, lt as WorkflowRecord, ot as WorkflowId, ut as WorkflowStatus } from "./index-B8SvtiYn.js";
2
2
  //#region src/core/store.d.ts
3
3
  interface CreateWorkflowStep {
4
4
  key: string;
@@ -75,6 +75,15 @@ interface ListWorkflowsFilters {
75
75
  entityRef?: string;
76
76
  limit?: number;
77
77
  }
78
+ /**
79
+ * The runs {@link WorkflowStore.listWorkflowTree} and {@link WorkflowStore.deleteWorkflows}
80
+ * address: every run of the store's partition whose `entityRef` matches, **plus every
81
+ * sub-workflow run below them**, at any depth — a child run carries its parent's id, not
82
+ * its entity, so it would otherwise be missed.
83
+ */
84
+ interface WorkflowTreeFilter {
85
+ entityRef: string;
86
+ }
78
87
  /**
79
88
  * A store bound to an open transaction, plus an opaque handle a capable
80
89
  * {@link Dispatcher} can join so a job and the state change that produced it
@@ -197,6 +206,22 @@ interface WorkflowStore {
197
206
  reopenWorkflow(params: ReopenWorkflowParams): Promise<void>;
198
207
  /** List workflows (with their steps) for status/dashboard reads. */
199
208
  listWorkflows(filters: ListWorkflowsFilters): Promise<WorkflowWithSteps[]>;
209
+ /**
210
+ * Every run the filter addresses (see {@link WorkflowTreeFilter}), with its steps,
211
+ * ordered by id — so a parent always comes before its children. No limit: the engine
212
+ * reads this to decide whether the whole set may go, and a page would hide a run.
213
+ */
214
+ listWorkflowTree(filter: WorkflowTreeFilter): Promise<WorkflowWithSteps[]>;
215
+ /**
216
+ * Delete every run the filter addresses, with all of their steps (map children
217
+ * included), and return the ids of the runs deleted, ascending.
218
+ *
219
+ * Implementations MUST delete the runs and their steps together — one statement or
220
+ * one transaction — so no step row outlives its run, whatever foreign keys the host
221
+ * declared. Nothing is checked here: whether a run may go is the engine's question
222
+ * (`purgeWorkflows`), asked before it calls this.
223
+ */
224
+ deleteWorkflows(filter: WorkflowTreeFilter): Promise<WorkflowId[]>;
200
225
  /** All workflows currently in `running` state (for the stuck-step sweeper). */
201
226
  listRunningWorkflows(): Promise<WorkflowRecord[]>;
202
227
  /**
@@ -275,7 +300,12 @@ declare function createInMemoryStepGate(config?: InMemoryStepGateConfig): StepGa
275
300
  /** Lifecycle event types the engine emits at every workflow/step transition. */
276
301
  type FlowEventType = 'workflow.started' | 'workflow.completed' | 'workflow.failed' | 'workflow.cancelled' |
277
302
  /** A `failed` workflow was put back in flight by `retryWorkflow`. */
278
- 'workflow.retried' | 'step.started' | 'step.completed' | 'step.failed' | 'step.retrying' | 'step.skipped' | 'step.waiting' | 'step.resumed' |
303
+ 'workflow.retried' |
304
+ /**
305
+ * The run was deleted by `purgeWorkflows`, with its steps. Emitted after the delete,
306
+ * once per run; nothing about the run can be read any more.
307
+ */
308
+ 'workflow.purged' | 'step.started' | 'step.completed' | 'step.failed' | 'step.retrying' | 'step.skipped' | 'step.waiting' | 'step.resumed' |
279
309
  /**
280
310
  * A suspended step's wait budget elapsed. Always followed by the event for
281
311
  * what the policy did — `step.failed`, or `step.completed` for `{ output }`.
@@ -381,4 +411,4 @@ interface RecordingTracer extends FlowTracer {
381
411
  }
382
412
  declare function createRecordingTracer(): RecordingTracer;
383
413
  //#endregion
384
- export { ListWorkflowsFilters as A, AddChildStep as C, CreatedWorkflow as D, CreateWorkflowStep as E, TransactionalScope as M, WorkflowStore as N, FailStepParams as O, createInMemoryStepGate as S, CreateWorkflowParams as T, InMemoryStepGateConfig as _, FlowSpan as a, StepGateDecision as b, RecordingObserver as c, createMeterObserver as d, createRecordingObserver as f, ConcurrencyRule as g, noopTracer as h, FlowObserver as i, ReopenWorkflowParams as j, FinishWorkflowParams as k, RecordingTracer as l, noopObserver as m, FlowEventType as n, FlowTracer as o, createRecordingTracer as p, FlowMeter as r, RecordedSpan as s, FlowEvent as t, combineObservers as u, RateRule as v, CompleteStepParams as w, StepGateRequest as x, StepGate as y };
414
+ export { ListWorkflowsFilters as A, AddChildStep as C, CreatedWorkflow as D, CreateWorkflowStep as E, TransactionalScope as M, WorkflowStore as N, FailStepParams as O, WorkflowTreeFilter as P, createInMemoryStepGate as S, CreateWorkflowParams as T, InMemoryStepGateConfig as _, FlowSpan as a, StepGateDecision as b, RecordingObserver as c, createMeterObserver as d, createRecordingObserver as f, ConcurrencyRule as g, noopTracer as h, FlowObserver as i, ReopenWorkflowParams as j, FinishWorkflowParams as k, RecordingTracer as l, noopObserver as m, FlowEventType as n, FlowTracer as o, createRecordingTracer as p, FlowMeter as r, RecordedSpan as s, FlowEvent as t, combineObservers as u, RateRule as v, CompleteStepParams as w, StepGateRequest as x, StepGate as y };
@@ -1,6 +1,6 @@
1
- import "../index-BGmJYvTt.js";
1
+ import "../index-B8SvtiYn.js";
2
2
  import { n as Logger } from "../logger-BCus3AOa.js";
3
- import { r as Dispatcher } from "../dispatcher-Bzkqq0Ax.js";
3
+ import { r as Dispatcher } from "../dispatcher-B4haXU4w.js";
4
4
  import { z } from "zod";
5
5
  import { PgBoss } from "pg-boss";
6
6
  //#region src/pg-boss/payload.d.ts
@@ -1,5 +1,5 @@
1
- import { ot as WorkflowId } from "../index-BGmJYvTt.js";
2
- import { N as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-C-jfi2DX.js";
1
+ import { ot as WorkflowId } from "../index-B8SvtiYn.js";
2
+ import { N as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-DRUJOpiQ.js";
3
3
  import { Pool } from "pg";
4
4
  //#region src/postgres/ddl.d.ts
5
5
  /**
@@ -410,9 +410,12 @@ function createWorkflowStore(deps) {
410
410
  }
411
411
  args.push(filters.limit ?? 50);
412
412
  const limitIdx = args.length;
413
- const wfRes = await exec.query(`SELECT * FROM ${WF} WHERE ${conds.join(" AND ")} ORDER BY id DESC LIMIT $${limitIdx}`, args);
414
- if (wfRes.rows.length === 0) return [];
415
- const ids = wfRes.rows.map((r) => Number(r.id));
413
+ return withSteps((await exec.query(`SELECT * FROM ${WF} WHERE ${conds.join(" AND ")} ORDER BY id DESC LIMIT $${limitIdx}`, args)).rows);
414
+ }
415
+ /** Attach each run's steps in one query. */
416
+ async function withSteps(rows) {
417
+ if (rows.length === 0) return [];
418
+ const ids = rows.map((r) => Number(r.id));
416
419
  const stepRes = await exec.query(`SELECT * FROM ${STEP} WHERE workflow_id = ANY($1::bigint[]) AND partition_key = $2 ORDER BY id`, [ids, partitionKey]);
417
420
  const stepsByWf = /* @__PURE__ */ new Map();
418
421
  for (const row of stepRes.rows) {
@@ -421,11 +424,33 @@ function createWorkflowStore(deps) {
421
424
  list.push(s);
422
425
  stepsByWf.set(s.workflowId, list);
423
426
  }
424
- return wfRes.rows.map((r) => ({
427
+ return rows.map((r) => ({
425
428
  ...mapWorkflow(r),
426
429
  steps: stepsByWf.get(Number(r.id)) ?? []
427
430
  }));
428
431
  }
432
+ /**
433
+ * The runs about the entity and every sub-workflow run below them, as a recursive CTE
434
+ * over `parent_workflow_id` (`$1` partition, `$2` entity). Shared by the read and the
435
+ * delete so the two can never address different sets.
436
+ */
437
+ const TREE = `tree(id) AS (
438
+ SELECT id FROM ${WF} WHERE partition_key = $1 AND entity_ref = $2
439
+ UNION
440
+ SELECT c.id FROM ${WF} c JOIN tree t ON c.parent_workflow_id = t.id WHERE c.partition_key = $1
441
+ )`;
442
+ async function listWorkflowTree(filter) {
443
+ return withSteps((await exec.query(`WITH RECURSIVE ${TREE}
444
+ SELECT w.* FROM ${WF} w JOIN tree t ON t.id = w.id WHERE w.partition_key = $1 ORDER BY w.id`, [partitionKey, filter.entityRef])).rows);
445
+ }
446
+ async function deleteWorkflows(filter) {
447
+ return (await exec.query(`WITH RECURSIVE ${TREE},
448
+ gone_steps AS (
449
+ DELETE FROM ${STEP} s USING tree t WHERE s.workflow_id = t.id AND s.partition_key = $1
450
+ )
451
+ DELETE FROM ${WF} w USING tree t WHERE w.id = t.id AND w.partition_key = $1
452
+ RETURNING w.id`, [partitionKey, filter.entityRef])).rows.map((r) => Number(r.id)).sort((a, b) => a - b);
453
+ }
429
454
  async function listRunningWorkflows() {
430
455
  return (await exec.query(`SELECT * FROM ${WF} WHERE partition_key = $1 AND status = 'running'`, [partitionKey])).rows.map(mapWorkflow);
431
456
  }
@@ -482,6 +507,8 @@ function createWorkflowStore(deps) {
482
507
  skipPendingSteps,
483
508
  finishWorkflow,
484
509
  listWorkflows,
510
+ listWorkflowTree,
511
+ deleteWorkflows,
485
512
  listRunningWorkflows,
486
513
  findStuckSteps
487
514
  };
@@ -9,7 +9,11 @@
9
9
  * Adding a provider: an id here with its metadata, a factory in
10
10
  * `providers/`, and pricing rows in `cost.ts`.
11
11
  */
12
- const AI_PROVIDER_IDS = ["anthropic", "mistral"];
12
+ const AI_PROVIDER_IDS = [
13
+ "anthropic",
14
+ "mistral",
15
+ "openai"
16
+ ];
13
17
  const EMBEDDING_PROVIDER_IDS = ["openai", "mistral"];
14
18
  /**
15
19
  * What an unset provider resolves to: the EU-established vendor, so a deployment
@@ -29,27 +33,31 @@ const AI_PROVIDERS = {
29
33
  legalName: "Anthropic PBC",
30
34
  country: "US"
31
35
  },
32
- defaultModel: "claude-sonnet-5",
33
- classifierModel: "claude-haiku-4-5",
34
- testModel: "claude-haiku-4-5",
36
+ defaultModel: "claude-sonnet-5-5",
37
+ classifierModel: "claude-haiku-5-5",
38
+ testModel: "claude-haiku-5-5",
35
39
  keyPrefix: "sk-ant-",
36
40
  consoleKeyUrl: "https://console.anthropic.com/settings/keys",
37
41
  modelOptions: [
38
42
  {
39
- label: "Claude Opus 5",
40
- value: "claude-opus-5"
43
+ label: "Claude Opus 5.5",
44
+ value: "claude-opus-5-5"
41
45
  },
42
46
  {
43
- label: "Claude Opus 4.8",
44
- value: "claude-opus-4-8"
47
+ label: "Claude Sonnet 5.5",
48
+ value: "claude-sonnet-5-5"
45
49
  },
46
50
  {
47
- label: "Claude Sonnet 5",
48
- value: "claude-sonnet-5"
51
+ label: "Claude Haiku 5.5",
52
+ value: "claude-haiku-5-5"
53
+ },
54
+ {
55
+ label: "Claude Opus 5",
56
+ value: "claude-opus-5"
49
57
  },
50
58
  {
51
- label: "Claude Haiku 4.5",
52
- value: "claude-haiku-4-5"
59
+ label: "Claude Sonnet 5",
60
+ value: "claude-sonnet-5"
53
61
  }
54
62
  ]
55
63
  },
@@ -80,6 +88,34 @@ const AI_PROVIDERS = {
80
88
  value: "mistral-small-latest"
81
89
  }
82
90
  ]
91
+ },
92
+ openai: {
93
+ id: "openai",
94
+ label: "OpenAI",
95
+ residency: {
96
+ region: "third-country",
97
+ legalName: "OpenAI, L.L.C.",
98
+ country: "US"
99
+ },
100
+ defaultModel: "gpt-6.1-sol",
101
+ classifierModel: "gpt-6-luna",
102
+ testModel: "gpt-6-luna",
103
+ keyPrefix: "sk-",
104
+ consoleKeyUrl: "https://platform.openai.com/api-keys",
105
+ modelOptions: [
106
+ {
107
+ label: "GPT-6 Astra",
108
+ value: "gpt-6-astra"
109
+ },
110
+ {
111
+ label: "GPT-6.1 Sol",
112
+ value: "gpt-6.1-sol"
113
+ },
114
+ {
115
+ label: "GPT-6 Luna",
116
+ value: "gpt-6-luna"
117
+ }
118
+ ]
83
119
  }
84
120
  };
85
121
  const EMBEDDING_PROVIDERS = {
@@ -9,7 +9,7 @@
9
9
  * Adding a provider: an id here with its metadata, a factory in
10
10
  * `providers/`, and pricing rows in `cost.ts`.
11
11
  */
12
- declare const AI_PROVIDER_IDS: readonly ['anthropic', 'mistral'];
12
+ declare const AI_PROVIDER_IDS: readonly ['anthropic', 'mistral', 'openai'];
13
13
  type AiProviderId = (typeof AI_PROVIDER_IDS)[number];
14
14
  declare const EMBEDDING_PROVIDER_IDS: readonly ['openai', 'mistral'];
15
15
  type EmbeddingProviderId = (typeof EMBEDDING_PROVIDER_IDS)[number];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octaflow",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Durable DAG workflow engine: Zod-typed steps over Postgres + pg-boss, with an optional AI add-on (token/cost/quota). Layered single package.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -116,18 +116,18 @@
116
116
  }
117
117
  },
118
118
  "devDependencies": {
119
- "@ai-sdk/anthropic": "^4.0.71",
120
- "@ai-sdk/mistral": "^4.0.56",
121
- "@ai-sdk/openai": "^4.0.83",
122
- "@ai-sdk/openai-compatible": "^3.0.62",
123
- "@ai-sdk/provider": "^4.0.21",
119
+ "@ai-sdk/anthropic": "^4.0.74",
120
+ "@ai-sdk/mistral": "^4.0.59",
121
+ "@ai-sdk/openai": "^4.0.86",
122
+ "@ai-sdk/openai-compatible": "^3.0.65",
123
+ "@ai-sdk/provider": "^4.0.24",
124
124
  "@testcontainers/postgresql": "^12.2.0",
125
125
  "@types/node": "^26.6.4",
126
126
  "@types/pg": "^8.23.1",
127
- "ai": "^7.0.127",
127
+ "ai": "^7.0.130",
128
128
  "drizzle-orm": "^0.45.3",
129
129
  "pg": "^8.23.1",
130
- "pg-boss": "^12.35.1",
130
+ "pg-boss": "^12.37.0",
131
131
  "testcontainers": "^12.2.0",
132
132
  "tsdown": "^0.23.0",
133
133
  "typescript": "^7.0.2",