| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- /**
- * Service Definition for the workflow capability seam. Service providers execute orchestration scripts;
- * observe-only lifecycle events never expose run control.
- * @module @deepseek-ai/dsh-workflow
- */
- import { Context, Service } from 'cordis'
- import { HarnessError } from '@deepseek-ai/dsh-llm'
- import type {
- WorkflowAgentEndInfo,
- WorkflowAgentInfo,
- WorkflowResultInfo,
- WorkflowRun,
- WorkflowRunInfo,
- WorkflowStartRequest,
- } from './types.ts'
- export { WorkflowRunId } from './types.ts'
- export type {
- WorkflowAgentEndInfo,
- WorkflowAgentInfo,
- WorkflowAgentOutcome,
- WorkflowMeta,
- WorkflowPhase,
- WorkflowResult,
- WorkflowResultInfo,
- WorkflowRun,
- WorkflowRunInfo,
- WorkflowStartRequest,
- WorkflowStopReason,
- } from './types.ts'
- declare module 'cordis' {
- interface Context {
- workflows: WorkflowService
- }
- interface Events {
- /**
- * A workflow run started — the script's meta block validated, the body
- * about to execute. Paired with {@link Events['workflow/end']}.
- * @param info - the run's identity snapshot (id + meta).
- * @mode emit
- */
- 'workflow/start'(info: WorkflowRunInfo): void
- /**
- * The script entered a phase (a `phase(title)` call) — progress grouping
- * for observers; no execution semantics.
- * @param info - the run's identity snapshot.
- * @param title - the phase title, verbatim.
- * @mode emit
- */
- 'workflow/phase'(info: WorkflowRunInfo, title: string): void
- /**
- * The script emitted a narration line (a `log(message)` call).
- * @param info - the run's identity snapshot.
- * @param message - the logged message, verbatim.
- * @mode emit
- */
- 'workflow/log'(info: WorkflowRunInfo, message: string): void
- /**
- * One `agent()` call established a published child run. Paired with
- * {@link Events['workflow/agent-end']} by `agent.seq`. A call that never
- * receives a published run from the provider emits neither
- * event in this pair.
- * @param info - the run's identity snapshot.
- * @param agent - the call's sequence number, label, phase, and child id.
- * @mode emit
- */
- 'workflow/agent-start'(info: WorkflowRunInfo, agent: WorkflowAgentInfo): void
- /**
- * One `agent()` call settled (clean result, child failure, or run
- * cancellation). Paired with {@link Events['workflow/agent-start']} by
- * `agent.seq`, exactly once per started call on every stop path — on an
- * engine termination path (a worker killed past its grace) the end is
- * engine-synthesized with outcome `'cancelled'`.
- * @param info - the run's identity snapshot.
- * @param agent - the call identity plus its outcome.
- * @mode emit
- */
- 'workflow/agent-end'(info: WorkflowRunInfo, agent: WorkflowAgentEndInfo): void
- /**
- * A workflow run settled (any stop reason). Fired when
- * {@link WorkflowRun.result} resolves. Paired with
- * {@link Events['workflow/start']}.
- * @param info - the run's identity snapshot.
- * @param result - the outcome data (stop reason, error, agent count) —
- * deliberately WITHOUT the result value (see {@link WorkflowResultInfo}).
- * @mode emit
- */
- 'workflow/end'(info: WorkflowRunInfo, result: WorkflowResultInfo): void
- }
- }
- /** The full set of `workflow/*` event names {@link WorkflowService.emitWorkflowEvent} dispatches. */
- export type WorkflowEventName =
- | 'workflow/start'
- | 'workflow/phase'
- | 'workflow/log'
- | 'workflow/agent-start'
- | 'workflow/agent-end'
- | 'workflow/end'
- /**
- * Machine-routable fatal workflow failures: parse/meta/argument/schema errors,
- * resource caps, subagent infrastructure failures, unserializable boundary
- * values, and cancellation. An ordinary child failure resolves its item to
- * `null` and is not one of these fatal codes.
- */
- export type WorkflowErrorCode =
- | 'SCRIPT_PARSE'
- | 'META_INVALID'
- | 'INVALID_ARGUMENT'
- | 'UNSUPPORTED_OPTION'
- | 'UNSUPPORTED_SCHEMA'
- | 'AGENT_CAP'
- | 'ITEM_CAP'
- | 'AGENT_START'
- | 'AGENT_RESULT'
- | 'RESULT_UNSERIALIZABLE'
- | 'CANCELLED'
- /**
- * Typed error for workflow-seam failures. Extends {@link HarnessError}, so the
- * `code` is machine-routable taxonomy. `fatal` drives the combinator
- * discipline: `parallel()`/`pipeline()` re-throw a fatal error (a typo'd
- * option or a tripped cap must kill the script loudly), and reserve the
- * per-item `null` for child-run failures and ordinary in-stage script errors.
- * Every {@link WorkflowErrorCode} is fatal; the flag exists so the
- * distinction is explicit at every catch site rather than implied.
- */
- export class WorkflowError extends HarnessError {
- /** Whether combinators must propagate this error instead of nulling the item. */
- readonly fatal: boolean
- constructor(message: string, code: WorkflowErrorCode, options?: ErrorOptions & { fatal?: boolean }) {
- super(message, code, options)
- this.name = 'WorkflowError'
- this.fatal = options?.fatal ?? true
- }
- }
- /**
- * Whether combinators must re-throw `error` instead of mapping the item to `null`.
- * @param error - any thrown value; fatality is host `instanceof` (unforgeable from a script realm).
- * @returns true iff `error` is a {@link WorkflowError} whose `fatal` flag is set.
- */
- export function isFatalWorkflowError(error: unknown): boolean {
- return error instanceof WorkflowError && error.fatal
- }
- /**
- * Workflow Service Definition contract. Invalid requests throw before publication; a live
- * run is holder-owned, its result never rejects, cancellation and disposal are
- * bounded, and disposal waits for child cleanup within that bound. Lifecycle
- * listener failures are contained, and `workflow/end` fires exactly once as the
- * result settles.
- */
- export abstract class WorkflowService extends Service {
- constructor(ctx: Context) {
- super(ctx, 'workflows')
- }
- /**
- * Parse and execute a workflow script.
- * @param request - the script, its `args`, the parent agent, and an
- * optional cancel signal.
- * @returns the live run; its `result` resolves when the script settles.
- */
- abstract start(request: WorkflowStartRequest): WorkflowRun
- /**
- * Emit a lifecycle event while containing and logging each listener failure.
- * @param name - the `workflow/*` event to dispatch.
- * @param args - the event's payload, matching its declared signature.
- */
- protected emitWorkflowEvent(name: WorkflowEventName, ...args: unknown[]): void {
- for (const callback of this.ctx.events.dispatch('emit', [name, ...args])) {
- try {
- const returned: unknown = (callback as (...payload: unknown[]) => unknown)(...args)
- void Promise.resolve(returned).catch((error: unknown) => {
- this.ctx.logger.warn(`workflow: ${name} listener rejected: ${renderListenerError(error)}`)
- })
- } catch (error: unknown) {
- this.ctx.logger.warn(`workflow: ${name} listener threw: ${renderListenerError(error)}`)
- }
- }
- }
- }
- /**
- * Render any thrown value without violating listener containment.
- * @param error - any thrown value.
- * @returns `String(error)`, or a fixed label when even coercion throws.
- */
- function renderListenerError(error: unknown): string {
- try {
- return String(error)
- } catch {
- // String coercion itself may throw.
- return '[unrenderable thrown value]'
- }
- }
- export default WorkflowService
|