Skip to content
中文

defineDurableTask ​

KIT · function · Runtime value

Compile a durable task definition into a resumable runner.

Each run call locks the task id in the store, loads the checkpoint and resumes at nextStepIndex. A checkpoint from another definition id or revision, or one that fails the predicates below, is rejected instead of being repaired, because silently accepting it would replay App side effects under a different contract.

Import ​

ts
import { defineDurableTask } from '@jangts/agent-core/kits/durable';
js
import { defineDurableTask } from '@jangts/agent-core/kits/durable';

Other public entry points:@jangts/agent-core/types, @jangts/agent-core/kits, @jangts/agent-core/kits/durable.

Definition ​

Type parameters ​

Type parameterConstraintDefault type
State——
App——

Call ​

ts
defineDurableTask<State, App>(definition: DurableTaskDefinition<State, App>, options?: DurableTaskOptions<State>): DurableTask<State, App>

Compile a durable task definition into a resumable runner.

Each run call locks the task id in the store, loads the checkpoint and resumes at nextStepIndex. A checkpoint from another definition id or revision, or one that fails the predicates below, is rejected instead of being repaired, because silently accepting it would replay App side effects under a different contract.

Type parameterConstraintDefault type
State——
App——
ParameterTypePassingDescription
definitionDurableTaskDefinition<State, App>Required—
optionsDurableTaskOptions<State>Optional—
Fields of definition
FieldTypeRequiredDescription
idstringRequiredRead-only. See the type definition and usage context.
revisionstringRequiredRead-only. Definition revision; stored in the checkpoint so resumed work matches its code.
stepsreadonly DurableStep<State, App>[]RequiredRead-only. Steps in execution order; the checkpoint stores an index into this list.
isState(state: unknown) => state is StateRequiredValidates a loaded checkpoint state before any step runs.
authorize((checkpoint: Readonly<DurableCheckpoint<State>>, context: OperationContext<App>) => import("../common.js").MaybePromise<void>) | undefinedOptionalRecheck current identity, source versions and grants on every explicit resume.
Fields of options
FieldTypeRequiredDescription
now(() => number) | undefinedOptionalRead-only. Clock override for deadlines and timestamps; defaults to Date.now.
storeDurableStore<State> | undefinedOptionalRead-only. Default store; a call may pass its own store instead.

Returns: DurableTask<State, App>.

Return value structure
FieldTypeRequiredDescription
run(request: { readonly taskId: string; readonly store?: DurableStore<State> | undefined; }, context: OperationContext<App>) => Promise<State>RequiredRuns or resumes the task.

Examples ​

First understand one execution with an isolated in-memory checkpoint ​

Beginner · Node.js

This is an isolated demonstration of the minimal storage contract; real cross-process or cross-request recovery must use an App store with exclusivity and durability guarantees.

ts
import { Durable } from '@jangts/agent-core/kits/durable';
import type { DurableCheckpoint, DurableStore } from '@jangts/agent-core/kits/durable';
type State = { count: number };
const records = new Map<string, DurableCheckpoint<State>>([
  ['demo-task', { version: 2, definitionId: 'count', revision: '1', nextStepIndex: 0, attemptCount: 0, maxAttempts: 3, deadlineAtMs: Date.now() + 60_000, status: 'ready', state: { count: 0 } }],
]);
let tail = Promise.resolve();
// 隔离示例:进程内串行锁和内存记录,不是生产持久化实现。
const store: DurableStore<State> = {
  exclusive(_id, operation) { const result = tail.then(operation); tail = result.then(() => {}, () => {}); return result; },
  async load(id) { const record = records.get(id); return record && structuredClone(record); },
  async save(id, record) { records.set(id, structuredClone(record)); },
};
const isState = (value: unknown): value is State => !!value && typeof value === 'object' && 'count' in value && typeof value.count === 'number';
const task = Durable.defineDurableTask<State, undefined>({
  id: 'count', revision: '1', isState,
  steps: [{ id: 'increment', replayPolicy: 'safe', async run(state) { return { count: state.count + 1 }; } }],
});
const context = { app: undefined, signal: new AbortController().signal };
console.log(await task.run({ taskId: 'demo-task', store }, context));
console.log(records.get('demo-task')?.attemptCount);
js
import { Durable } from '@jangts/agent-core/kits/durable';
const records = new Map([
    ['demo-task', { version: 2, definitionId: 'count', revision: '1', nextStepIndex: 0, attemptCount: 0, maxAttempts: 3, deadlineAtMs: Date.now() + 60_000, status: 'ready', state: { count: 0 } }],
]);
let tail = Promise.resolve();
// 隔离示例:进程内串行锁和内存记录,不是生产持久化实现。
const store = {
    exclusive(_id, operation) { const result = tail.then(operation); tail = result.then(() => { }, () => { }); return result; },
    async load(id) { const record = records.get(id); return record && structuredClone(record); },
    async save(id, record) { records.set(id, structuredClone(record)); },
};
const isState = (value) => !!value && typeof value === 'object' && 'count' in value && typeof value.count === 'number';
const task = Durable.defineDurableTask({
    id: 'count', revision: '1', isState,
    steps: [{ id: 'increment', replayPolicy: 'safe', async run(state) { return { count: state.count + 1 }; } }],
});
const context = { app: undefined, signal: new AbortController().signal };
console.log(await task.run({ taskId: 'demo-task', store }, context));
console.log(records.get('demo-task')?.attemptCount);

Add a real reconciliation policy for interruptions to the same run ​

Configuration · Node.js

The example does not presume to interpret "cannot confirm whether it committed" as failure or re-execution; production reconciliation must query the real business system.

ts
import { Durable } from '@jangts/agent-core/kits/durable';
import type { DurableCheckpoint, DurableStore } from '@jangts/agent-core/kits/durable';
type State = { count: number };
const records = new Map<string, DurableCheckpoint<State>>([
  ['demo-task', { version: 2, definitionId: 'count', revision: '1', nextStepIndex: 0, attemptCount: 0, maxAttempts: 3, deadlineAtMs: Date.now() + 60_000, status: 'ready', state: { count: 0 } }],
]);
let tail = Promise.resolve();
// 隔离示例:进程内串行锁和内存记录,不是生产持久化实现。
const store: DurableStore<State> = {
  exclusive(_id, operation) { const result = tail.then(operation); tail = result.then(() => {}, () => {}); return result; },
  async load(id) { const record = records.get(id); return record && structuredClone(record); },
  async save(id, record) { records.set(id, structuredClone(record)); },
};
const isState = (value: unknown): value is State => !!value && typeof value === 'object' && 'count' in value && typeof value.count === 'number';
// 假设业务服务以 taskId 为幂等键保存真实提交结果;这里用内存 Map 演示。
const committed = new Map<string, number>();
const task = Durable.defineDurableTask<State, { key: string }>({
  id: 'count', revision: '1', isState,
  steps: [{ id: 'commit', replayPolicy: 'reconcile',
    async run(state, { app }) {
      const value = committed.get(app.key) ?? state.count + 1;
      committed.set(app.key, value);
      return { count: value };
    },
    async reconcile(state, { app }) {
      const value = committed.get(app.key);
      if (value === undefined) throw new Error('没有可核实的提交结果,需要 App 决定后续动作');
      return { count: value };
    },
  }],
});
const context = { app: { key: 'demo-task' }, signal: new AbortController().signal };
console.log(await task.run({ taskId: 'demo-task', store }, context));
console.log(await task.run({ taskId: 'demo-task', store }, context)); // 已完成记录不会再次执行步骤
console.log(committed.size);
js
import { Durable } from '@jangts/agent-core/kits/durable';
const records = new Map([
    ['demo-task', { version: 2, definitionId: 'count', revision: '1', nextStepIndex: 0, attemptCount: 0, maxAttempts: 3, deadlineAtMs: Date.now() + 60_000, status: 'ready', state: { count: 0 } }],
]);
let tail = Promise.resolve();
// 隔离示例:进程内串行锁和内存记录,不是生产持久化实现。
const store = {
    exclusive(_id, operation) { const result = tail.then(operation); tail = result.then(() => { }, () => { }); return result; },
    async load(id) { const record = records.get(id); return record && structuredClone(record); },
    async save(id, record) { records.set(id, structuredClone(record)); },
};
const isState = (value) => !!value && typeof value === 'object' && 'count' in value && typeof value.count === 'number';
// 假设业务服务以 taskId 为幂等键保存真实提交结果;这里用内存 Map 演示。
const committed = new Map();
const task = Durable.defineDurableTask({
    id: 'count', revision: '1', isState,
    steps: [{ id: 'commit', replayPolicy: 'reconcile',
            async run(state, { app }) {
                const value = committed.get(app.key) ?? state.count + 1;
                committed.set(app.key, value);
                return { count: value };
            },
            async reconcile(state, { app }) {
                const value = committed.get(app.key);
                if (value === undefined)
                    throw new Error('没有可核实的提交结果,需要 App 决定后续动作');
                return { count: value };
            },
        }],
});
const context = { app: { key: 'demo-task' }, signal: new AbortController().signal };
console.log(await task.run({ taskId: 'demo-task', store }, context));
console.log(await task.run({ taskId: 'demo-task', store }, context)); // 已完成记录不会再次执行步骤
console.log(committed.size);

API reference, usage examples and developer guides.