zayra-task-engine 0.0.1

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 ADDED
@@ -0,0 +1,268 @@
1
+ # zayra-task-engine
2
+
3
+ The intelligent workflow execution system of **ZAYRA AI**. It manages tasks, multi-step workflows, automations, and their execution states — planning and running the work ZAYRA's AI decides to do, with permission checks in front of every run.
4
+
5
+ This is version `0.0.1`. It contains **no frontend/UI code, no hardcoded workflows, and no AI provider logic** — only task planning and execution flow.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install zayra-task-engine
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```js
16
+ import { TaskManager } from "zayra-task-engine";
17
+
18
+ const tasks = new TaskManager();
19
+
20
+ const task = tasks.createTask({ name: "Send weekly report" });
21
+ await tasks.startTask(task.id);
22
+ await tasks.completeTask(task.id, { sent: true });
23
+
24
+ task.state; // "completed"
25
+ ```
26
+
27
+ ## Workflows (multi-step tasks)
28
+
29
+ ```js
30
+ const task = tasks.createWorkflow("Research topic", [
31
+ "Search information",
32
+ "Analyze results",
33
+ "Generate summary",
34
+ ]);
35
+
36
+ await tasks.startTask(task.id); // runs each step in order automatically
37
+
38
+ tasks.getProgress(task.id);
39
+ // { total: 3, completed: 3, failed: 0, remaining: 0, percent: 100 }
40
+ ```
41
+
42
+ Steps run themselves through an injected runner (see **Tool integration** below) — `startTask()` walks the list, stops the whole task at the first failed step, and completes it automatically once every step succeeds.
43
+
44
+ ## Task manager API
45
+
46
+ ```js
47
+ tasks.createTask({ name, type?, steps?, metadata? }); // register, doesn't start
48
+ await tasks.startTask(id); // permission check -> RUNNING -> runs steps if any
49
+ tasks.pauseTask(id); // RUNNING -> WAITING
50
+ await tasks.resumeTask(id); // WAITING -> RUNNING, continues remaining steps
51
+ tasks.cancelTask(id); // -> CANCELLED, also clears any pending schedule
52
+ await tasks.completeTask(id, result?); // -> COMPLETED, saves to memory if configured
53
+ await tasks.failTask(id, error?); // -> FAILED
54
+
55
+ tasks.getTask(id);
56
+ tasks.listTasks({ state?: "running" });
57
+ ```
58
+
59
+ ## Task types
60
+
61
+ ```js
62
+ import { TaskType } from "zayra-task-engine";
63
+
64
+ TaskType.SIMPLE; // "simple" - one action, no sub-steps
65
+ TaskType.MULTI_STEP; // "multi_step" - an explicit workflow
66
+ TaskType.SCHEDULED; // "scheduled" - meant to run via scheduleTask()
67
+ TaskType.AUTOMATION; // "automation" - a recurring/triggered task
68
+ TaskType.AI_GENERATED; // "ai_generated" - steps planned by an AI orchestrator at start time
69
+ ```
70
+
71
+ `type` doesn't change how a task is executed by itself — it's metadata consumers (and the AI integration below) can act on.
72
+
73
+ ## Task state management
74
+
75
+ ```js
76
+ import { TaskState } from "zayra-task-engine";
77
+
78
+ TaskState.CREATED; // just made, not started
79
+ TaskState.RUNNING; // in progress
80
+ TaskState.WAITING; // paused, waiting to resume
81
+ TaskState.COMPLETED; // finished successfully — terminal
82
+ TaskState.FAILED; // a step or the task itself failed — terminal
83
+ TaskState.CANCELLED; // cancelled by caller — terminal
84
+ ```
85
+
86
+ Only specific transitions are allowed (enforced by `Task#transitionTo()`, used internally by every manager method): `created -> running|cancelled`, `running -> waiting|completed|failed|cancelled`, `waiting -> running|cancelled`. Trying to move a completed/failed/cancelled task anywhere throws `TaskError`.
87
+
88
+ ## Tool integration
89
+
90
+ Tasks don't call tools themselves — `TaskManager` accepts an injected `toolRunner` function, typically backed by `zayra-tools`:
91
+
92
+ ```js
93
+ import { TaskManager } from "zayra-task-engine";
94
+ import { ToolRegistry, executeTool } from "zayra-tools";
95
+
96
+ const toolRegistry = new ToolRegistry();
97
+ // ...register tools...
98
+
99
+ const tasks = new TaskManager({
100
+ toolRunner: async (step, { task }) => {
101
+ const tool = toolRegistry.getTool(step.action.toolName);
102
+ const result = await executeTool(tool, step.action.input);
103
+ if (!result.success) throw new Error(result.error.message);
104
+ return result.data;
105
+ },
106
+ });
107
+ ```
108
+
109
+ Flow, per spec: **Task -> Tool selection -> Execution -> Result**. `zayra-task-engine` never imports `zayra-tools` directly (see **Design note** below) — `step.action` is whatever shape your `toolRunner` expects.
110
+
111
+ ## AI integration
112
+
113
+ An AI orchestrator can create tasks, plan their steps, and let the engine execute and monitor them:
114
+
115
+ ```js
116
+ const tasks = new TaskManager({
117
+ aiOrchestrator: {
118
+ async planSteps(task) {
119
+ // ask an AI model what steps this task needs, return step names/definitions
120
+ return ["Search flights", "Compare prices", "Book cheapest option"];
121
+ },
122
+ },
123
+ toolRunner /* ...as above... */,
124
+ });
125
+
126
+ const task = tasks.createTask({ name: "Book a flight to Tokyo", type: "ai_generated" });
127
+ await tasks.startTask(task.id); // planSteps() runs first since the task has no steps yet, then they execute
128
+ ```
129
+
130
+ `aiOrchestrator` matches the shape a future `zayra-ai-orchestrator` package would provide — inject any object with a `planSteps(task)` method today.
131
+
132
+ ## Memory integration
133
+
134
+ ```js
135
+ import { MemoryManager } from "zayra-memory";
136
+
137
+ const memory = new MemoryManager();
138
+ const tasks = new TaskManager({
139
+ memoryAdapter: { save: (entry) => memory.save(entry) },
140
+ });
141
+
142
+ await tasks.completeTask(task.id, { summary: "Report sent to 12 recipients" });
143
+ // memory.save() was called with a summary of the completed task
144
+ ```
145
+
146
+ ## Security rules
147
+
148
+ ```js
149
+ import { TaskPermissionManager } from "zayra-task-engine";
150
+
151
+ const permissions = new TaskPermissionManager();
152
+ permissions.blockTaskType("automation"); // block a whole task type
153
+ permissions.grantSystemPermission("network:external");
154
+ permissions.grantUserPermission("network:external"); // both required to actually grant it
155
+
156
+ const tasks = new TaskManager({ permissions });
157
+ ```
158
+
159
+ `startTask()` always runs the permission check first — a task whose type is blocked, or that declares a `metadata.permissions` requirement not granted by **both** the system and the user, throws `TaskError` and never runs. This is the same allow-if-both-agree model `zayra-tools`' `PermissionManager` uses, so a shared permission set can back both packages.
160
+
161
+ ## Scheduling support
162
+
163
+ ```js
164
+ tasks.scheduleTask(task.id, { delayMs: 60_000 }); // auto-starts in 1 minute
165
+ tasks.scheduleTask(task.id, { at: "2026-08-01T09:00:00Z" });
166
+
167
+ tasks.unscheduleTask(task.id); // cancel without cancelling the task itself
168
+ ```
169
+
170
+ A minimal timer-based scheduler in `0.0.1` — a placeholder that keeps `TaskScheduler`'s shape stable for a future cron-style/recurring version.
171
+
172
+ ## Events
173
+
174
+ ```js
175
+ import { TaskEvent } from "zayra-task-engine";
176
+
177
+ tasks.events.on(TaskEvent.CREATED, ({ task }) => console.log(`${task.name} created`));
178
+ tasks.events.on(TaskEvent.STARTED, ({ task }) => console.log(`${task.name} started`));
179
+ tasks.events.on(TaskEvent.COMPLETED, ({ task }) => console.log(`${task.name} completed`));
180
+ tasks.events.on(TaskEvent.FAILED, ({ task }) => console.log(`${task.name} failed`));
181
+ tasks.events.on(TaskEvent.PAUSED, ({ task }) => {});
182
+ tasks.events.on(TaskEvent.RESUMED, ({ task }) => {});
183
+ tasks.events.on(TaskEvent.CANCELLED, ({ task }) => {});
184
+ tasks.events.on(TaskEvent.STEP_STARTED, ({ task, step }) => {});
185
+ tasks.events.on(TaskEvent.STEP_COMPLETED, ({ task, step }) => {});
186
+ ```
187
+
188
+ A small built-in event bus for now — a drop-in placeholder for a future `zayra-events` package, matching the same `on()`/`off()`/`emit()` shape `zayra-plugins` already uses, so this code won't need to change when that lands.
189
+
190
+ ## Errors
191
+
192
+ - `TaskError` — base error class; carries `err.taskId`
193
+ - `ExecutionError` — a step failed, timed out, or had no runner available; carries `err.stepId`
194
+ - `WorkflowError` — an invalid workflow definition (empty step list, malformed step)
195
+
196
+ ```js
197
+ import { TaskError, ExecutionError, WorkflowError } from "zayra-task-engine";
198
+
199
+ try {
200
+ await tasks.startTask(task.id);
201
+ } catch (err) {
202
+ if (err instanceof TaskError) {
203
+ console.error(`Task ${err.taskId} failed to start:`, err.message);
204
+ }
205
+ }
206
+ ```
207
+
208
+ ## Design note: dependency-free by convention
209
+
210
+ Like every other `zayra-*` package, `zayra-task-engine` ships with **zero npm dependencies**, including on its own siblings (`zayra-tools`, `zayra-memory`, `zayra-ai-orchestrator`, `zayra-events`). All of that integration happens through the injected adapters shown above (`toolRunner`, `memoryAdapter`, `aiOrchestrator`, `permissions`) rather than imports. This keeps each package usable standalone, testable without the rest of ZAYRA installed, and free to swap implementations later without touching this package's internals.
211
+
212
+ ## What this package deliberately does NOT do
213
+
214
+ Per its design rules:
215
+
216
+ - No frontend/UI code
217
+ - No hardcoded workflows — every workflow is built at runtime from the steps you (or an AI orchestrator) provide
218
+ - No AI provider logic — model calls belong to `zayra-provider` / `zayra-ai-router`, not here
219
+ - No execution of unsafe operations, bypassing of permissions, or exposing of secrets — every `startTask()` runs the permission check first
220
+
221
+ ## Project structure
222
+
223
+ ```
224
+ zayra-task-engine/
225
+ src/
226
+ index.js # public entry point
227
+ manager.js # TaskManager — createTask/startTask/pauseTask/resumeTask/cancelTask/completeTask/failTask
228
+ task.js # Task entity, createStep(), state-transition guarding
229
+ workflow.js # defineWorkflow(), workflowProgress()
230
+ executor.js # executeStep() — safe, timeout-aware step execution
231
+ permissions.js # TaskPermissionManager — Security Rules
232
+ scheduler.js # TaskScheduler — timer-based scheduling support
233
+ events.js # EventBus + TaskEvent
234
+ types.js # TaskType, TaskState, allowed transitions
235
+ errors.js # TaskError, ExecutionError, WorkflowError
236
+ package.json
237
+ README.md
238
+ ```
239
+
240
+ ## API reference
241
+
242
+ ### `class TaskManager`
243
+
244
+ | Method | Description |
245
+ | --- | --- |
246
+ | `new TaskManager(options?)` | `options.permissions`, `options.toolRunner`, `options.memoryAdapter`, `options.aiOrchestrator`, `options.stepTimeoutMs`. |
247
+ | `createTask(input)` | Registers a task. Doesn't start it. |
248
+ | `createWorkflow(name, steps, options?)` | Convenience: creates a `MULTI_STEP` task from step names/definitions. |
249
+ | `startTask(id)` | Permission check, then `RUNNING`; plans steps via `aiOrchestrator` if `AI_GENERATED` with none; runs any steps. |
250
+ | `pauseTask(id)` / `resumeTask(id)` | `RUNNING <-> WAITING`; resuming continues remaining steps. |
251
+ | `cancelTask(id)` | -> `CANCELLED`; also clears any pending schedule. |
252
+ | `completeTask(id, result?)` | -> `COMPLETED`; saves a summary via `memoryAdapter` if configured. |
253
+ | `failTask(id, error?)` | -> `FAILED`. |
254
+ | `getTask(id)` / `listTasks(filter?)` | Access registered tasks. `filter.state` restricts by `TaskState`. |
255
+ | `scheduleTask(id, when)` / `unscheduleTask(id)` | `when: { delayMs? , at? }`. |
256
+ | `getProgress(id)` | `{ total, completed, failed, remaining, percent }`. |
257
+
258
+ ### Other exports
259
+
260
+ `Task`, `createStep()`, `defineWorkflow()`, `workflowProgress()`, `executeStep()`, `TaskPermissionManager`, `TaskScheduler`, `EventBus`, `TaskEvent`, `TaskType`, `TaskState`, `isValidTaskType()`, `isValidTaskState()`, `canTransition()`, `TaskError`, `ExecutionError`, `WorkflowError`
261
+
262
+ ## Compatibility
263
+
264
+ Designed to work with `zayra-core`, `zayra-events`, `zayra-tools`, `zayra-memory`, and a future `zayra-ai-orchestrator`.
265
+
266
+ ## License
267
+
268
+ MIT
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "zayra-task-engine",
3
+ "version": "0.0.1",
4
+ "description": "Intelligent workflow execution system for ZAYRA AI — plans, tracks, and runs tasks, multi-step workflows, and automations, with permission checks and event support.",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ },
17
+ "scripts": {
18
+ "test": "node --test \"test/**/*.test.js\""
19
+ },
20
+ "keywords": [
21
+ "zayra",
22
+ "ai",
23
+ "task-engine",
24
+ "workflow",
25
+ "automation",
26
+ "orchestration",
27
+ "agent"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "dependencies": {}
32
+ }
package/src/errors.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * errors.js
3
+ *
4
+ * Custom error hierarchy for zayra-task-engine (spec section: "Error
5
+ * Handling"). Never fails silently — every error thrown inside this
6
+ * package is one of these classes.
7
+ */
8
+
9
+ /**
10
+ * Base error class for all zayra-task-engine errors.
11
+ */
12
+ export class TaskError extends Error {
13
+ /**
14
+ * @param {string} message
15
+ * @param {object} [options]
16
+ * @param {string} [options.code]
17
+ * @param {string} [options.taskId]
18
+ * @param {unknown} [options.cause]
19
+ */
20
+ constructor(message, options = {}) {
21
+ super(message);
22
+
23
+ this.name = this.constructor.name;
24
+ this.code = options.code ?? "TASK_ERROR";
25
+ this.taskId = options.taskId ?? null;
26
+
27
+ if (options.cause !== undefined) {
28
+ this.cause = options.cause;
29
+ }
30
+
31
+ if (typeof Error.captureStackTrace === "function") {
32
+ Error.captureStackTrace(this, this.constructor);
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Thrown when a step fails to execute, times out, or has no runner
39
+ * available to execute it.
40
+ */
41
+ export class ExecutionError extends TaskError {
42
+ constructor(message, options = {}) {
43
+ super(message, { code: "EXECUTION_ERROR", ...options });
44
+ this.stepId = options.stepId ?? null;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Thrown for invalid workflow definitions — an empty or non-array
50
+ * step list, or a malformed individual step.
51
+ */
52
+ export class WorkflowError extends TaskError {
53
+ constructor(message, options = {}) {
54
+ super(message, { code: "WORKFLOW_ERROR", ...options });
55
+ }
56
+ }
57
+
58
+ export default {
59
+ TaskError,
60
+ ExecutionError,
61
+ WorkflowError,
62
+ };
package/src/events.js ADDED
@@ -0,0 +1,124 @@
1
+ /**
2
+ * events.js
3
+ *
4
+ * Event Support (spec section: "Event Support" — zayra-events).
5
+ *
6
+ * zayra-events doesn't exist yet as a separate package, so — same
7
+ * pattern already used by zayra-plugins — this file implements a
8
+ * small, dependency-free event emitter itself, matching that same
9
+ * on()/off()/emit() API. Swapping this out for a real zayra-events
10
+ * client later only means replacing this file's internals; every
11
+ * other file here only ever calls .on()/.off()/.emit().
12
+ */
13
+
14
+ /**
15
+ * Task lifecycle event names. The spec explicitly lists
16
+ * task.created / task.started / task.completed / task.failed;
17
+ * task.paused, task.resumed, task.cancelled, and the per-step events
18
+ * are natural extensions of the same six task-manager operations
19
+ * (createTask/startTask/pauseTask/resumeTask/cancelTask/completeTask)
20
+ * and the workflow step loop.
21
+ *
22
+ * @readonly
23
+ * @enum {string}
24
+ */
25
+ export const TaskEvent = Object.freeze({
26
+ CREATED: "task.created",
27
+ STARTED: "task.started",
28
+ PAUSED: "task.paused",
29
+ RESUMED: "task.resumed",
30
+ COMPLETED: "task.completed",
31
+ FAILED: "task.failed",
32
+ CANCELLED: "task.cancelled",
33
+ STEP_STARTED: "task.step.started",
34
+ STEP_COMPLETED: "task.step.completed",
35
+ });
36
+
37
+ /**
38
+ * Minimal synchronous event emitter.
39
+ *
40
+ * @example
41
+ * const events = new EventBus();
42
+ * events.on(TaskEvent.COMPLETED, ({ task }) => console.log(`${task.name} done`));
43
+ * events.emit(TaskEvent.COMPLETED, { task });
44
+ */
45
+ export class EventBus {
46
+ constructor() {
47
+ /** @type {Map<string, Set<Function>>} */
48
+ this._listeners = new Map();
49
+ }
50
+
51
+ /**
52
+ * @param {string} eventName
53
+ * @param {(payload: any) => void} handler
54
+ * @returns {() => void} An unsubscribe function.
55
+ */
56
+ on(eventName, handler) {
57
+ if (typeof handler !== "function") {
58
+ throw new TypeError("EventBus.on() requires a function handler.");
59
+ }
60
+ if (!this._listeners.has(eventName)) {
61
+ this._listeners.set(eventName, new Set());
62
+ }
63
+ this._listeners.get(eventName).add(handler);
64
+ return () => this.off(eventName, handler);
65
+ }
66
+
67
+ /**
68
+ * @param {string} eventName
69
+ * @param {(payload: any) => void} handler
70
+ * @returns {() => void} An unsubscribe function.
71
+ */
72
+ once(eventName, handler) {
73
+ const wrapped = (payload) => {
74
+ this.off(eventName, wrapped);
75
+ handler(payload);
76
+ };
77
+ return this.on(eventName, wrapped);
78
+ }
79
+
80
+ /**
81
+ * @param {string} eventName
82
+ * @param {(payload: any) => void} handler
83
+ */
84
+ off(eventName, handler) {
85
+ this._listeners.get(eventName)?.delete(handler);
86
+ }
87
+
88
+ /**
89
+ * Emits an event synchronously. A throwing handler is caught and
90
+ * logged rather than propagating, so one bad listener can't break
91
+ * the emit chain or crash the task lifecycle step that triggered it.
92
+ * @param {string} eventName
93
+ * @param {any} [payload]
94
+ */
95
+ emit(eventName, payload) {
96
+ const handlers = this._listeners.get(eventName);
97
+ if (!handlers || handlers.size === 0) {
98
+ return;
99
+ }
100
+ for (const handler of handlers) {
101
+ try {
102
+ handler(payload);
103
+ } catch (err) {
104
+ console.error(`[zayra-task-engine] Event handler for "${eventName}" threw:`, err);
105
+ }
106
+ }
107
+ }
108
+
109
+ /** @param {string} eventName @returns {number} */
110
+ listenerCount(eventName) {
111
+ return this._listeners.get(eventName)?.size ?? 0;
112
+ }
113
+
114
+ /** @param {string} [eventName] */
115
+ removeAllListeners(eventName) {
116
+ if (eventName) {
117
+ this._listeners.delete(eventName);
118
+ } else {
119
+ this._listeners.clear();
120
+ }
121
+ }
122
+ }
123
+
124
+ export default EventBus;
@@ -0,0 +1,110 @@
1
+ /**
2
+ * executor.js
3
+ *
4
+ * Safe step execution (spec sections: "Tool Integration" — Task ->
5
+ * Tool selection -> Execution -> Result — and "Error Handling":
6
+ * failed steps, timeouts).
7
+ *
8
+ * zayra-tools already exists as a sibling package, but per this
9
+ * project's convention every zayra-* package ships with zero npm
10
+ * dependencies on its siblings (see each package.json's empty
11
+ * "dependencies") — integration happens through dependency injection
12
+ * instead. Callers pass a `runner(step, context)` function (e.g. one
13
+ * backed by zayra-tools' ToolRegistry + executeTool()); this module
14
+ * just adds the safety net around it: an optional timeout, and a
15
+ * standard { success, data, error } result that never throws —
16
+ * matching zayra-tools' own executeTool() result shape.
17
+ */
18
+
19
+ import { ExecutionError } from "./errors.js";
20
+
21
+ /**
22
+ * @typedef {object} StepResult
23
+ * @property {boolean} success
24
+ * @property {any} data
25
+ * @property {{ name: string, code: string, message: string }|null} error
26
+ */
27
+
28
+ /**
29
+ * Serializes any thrown value into the result format's `error` shape.
30
+ * @param {unknown} err
31
+ * @returns {{ name: string, code: string, message: string }}
32
+ */
33
+ function serializeError(err) {
34
+ if (err instanceof Error) {
35
+ return {
36
+ name: err.name,
37
+ code: err.code ?? "EXECUTION_ERROR",
38
+ message: err.message,
39
+ };
40
+ }
41
+ return { name: "Error", code: "EXECUTION_ERROR", message: String(err) };
42
+ }
43
+
44
+ /**
45
+ * Races a promise against a timeout, rejecting with an ExecutionError
46
+ * if the timeout wins.
47
+ * @param {Promise<any>} promise
48
+ * @param {number} timeoutMs
49
+ * @param {string} stepId
50
+ * @returns {Promise<any>}
51
+ */
52
+ function withTimeout(promise, timeoutMs, stepId) {
53
+ return new Promise((resolve, reject) => {
54
+ const timer = setTimeout(() => {
55
+ reject(new ExecutionError(`Step timed out after ${timeoutMs}ms.`, { stepId, code: "TIMEOUT" }));
56
+ }, timeoutMs);
57
+
58
+ promise.then(
59
+ (value) => {
60
+ clearTimeout(timer);
61
+ resolve(value);
62
+ },
63
+ (err) => {
64
+ clearTimeout(timer);
65
+ reject(err);
66
+ }
67
+ );
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Executes a single step through an injected runner function,
73
+ * enforcing an optional timeout and normalizing the result. Never
74
+ * rejects.
75
+ *
76
+ * @param {import("./task.js").Step} step
77
+ * @param {(step: import("./task.js").Step, context: object) => Promise<any>} runner - Injected step runner, e.g. one that looks up a tool by `step.action` and calls zayra-tools' executeTool().
78
+ * @param {object} [context] - Passed through to the runner (e.g. `{ task }`).
79
+ * @param {number} [timeoutMs] - Optional timeout in ms. Omit/0 for no timeout.
80
+ * @returns {Promise<StepResult>}
81
+ */
82
+ export async function executeStep(step, runner, context = {}, timeoutMs) {
83
+ if (typeof runner !== "function") {
84
+ const err = new ExecutionError(
85
+ `No step runner was provided for step "${step.name}" (expected a function).`,
86
+ { stepId: step.id }
87
+ );
88
+ return { success: false, data: null, error: serializeError(err) };
89
+ }
90
+
91
+ try {
92
+ const data = timeoutMs
93
+ ? await withTimeout(runner(step, context), timeoutMs, step.id)
94
+ : await runner(step, context);
95
+ return { success: true, data: data === undefined ? null : data, error: null };
96
+ } catch (err) {
97
+ const wrapped =
98
+ err instanceof ExecutionError
99
+ ? err
100
+ : new ExecutionError(`Step "${step.name}" failed: ${err.message}`, {
101
+ stepId: step.id,
102
+ cause: err,
103
+ });
104
+ return { success: false, data: null, error: serializeError(wrapped) };
105
+ }
106
+ }
107
+
108
+ export default {
109
+ executeStep,
110
+ };
package/src/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * index.js
3
+ *
4
+ * Public entry point for the zayra-task-engine package.
5
+ *
6
+ * Anything a consumer needs should be exported from here — they
7
+ * should never need to import from "zayra-task-engine/src/...". This
8
+ * keeps the internal file layout free to change without breaking
9
+ * zayra-core, zayra-ai-orchestrator, zayra-tools, or zayra-memory
10
+ * integrations built against this package.
11
+ */
12
+
13
+ export { TaskManager } from "./manager.js";
14
+ export { Task, createStep } from "./task.js";
15
+ export { defineWorkflow, workflowProgress } from "./workflow.js";
16
+ export { executeStep } from "./executor.js";
17
+ export { TaskPermissionManager } from "./permissions.js";
18
+ export { TaskScheduler } from "./scheduler.js";
19
+ export { EventBus, TaskEvent } from "./events.js";
20
+ export { TaskType, TaskState, isValidTaskType, isValidTaskState, canTransition } from "./types.js";
21
+ export { TaskError, ExecutionError, WorkflowError } from "./errors.js";
22
+
23
+ export { TaskManager as default } from "./manager.js";