@dexto/orchestration 1.5.8

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.
Files changed (50) hide show
  1. package/LICENSE +44 -0
  2. package/dist/agent-controller.cjs +265 -0
  3. package/dist/agent-controller.d.cts +116 -0
  4. package/dist/agent-controller.d.ts +116 -0
  5. package/dist/agent-controller.js +241 -0
  6. package/dist/condition-engine.cjs +276 -0
  7. package/dist/condition-engine.d.cts +87 -0
  8. package/dist/condition-engine.d.ts +87 -0
  9. package/dist/condition-engine.js +252 -0
  10. package/dist/index.cjs +57 -0
  11. package/dist/index.d.cts +11 -0
  12. package/dist/index.d.ts +11 -0
  13. package/dist/index.js +32 -0
  14. package/dist/signal-bus.cjs +186 -0
  15. package/dist/signal-bus.d.cts +78 -0
  16. package/dist/signal-bus.d.ts +78 -0
  17. package/dist/signal-bus.js +162 -0
  18. package/dist/task-registry.cjs +345 -0
  19. package/dist/task-registry.d.cts +124 -0
  20. package/dist/task-registry.d.ts +124 -0
  21. package/dist/task-registry.js +321 -0
  22. package/dist/tools/check-task.cjs +65 -0
  23. package/dist/tools/check-task.d.cts +56 -0
  24. package/dist/tools/check-task.d.ts +56 -0
  25. package/dist/tools/check-task.js +40 -0
  26. package/dist/tools/index.cjs +51 -0
  27. package/dist/tools/index.d.cts +10 -0
  28. package/dist/tools/index.d.ts +10 -0
  29. package/dist/tools/index.js +19 -0
  30. package/dist/tools/list-tasks.cjs +80 -0
  31. package/dist/tools/list-tasks.d.cts +64 -0
  32. package/dist/tools/list-tasks.d.ts +64 -0
  33. package/dist/tools/list-tasks.js +55 -0
  34. package/dist/tools/start-task.cjs +149 -0
  35. package/dist/tools/start-task.d.cts +102 -0
  36. package/dist/tools/start-task.d.ts +102 -0
  37. package/dist/tools/start-task.js +123 -0
  38. package/dist/tools/types.cjs +16 -0
  39. package/dist/tools/types.d.cts +30 -0
  40. package/dist/tools/types.d.ts +30 -0
  41. package/dist/tools/types.js +0 -0
  42. package/dist/tools/wait-for.cjs +116 -0
  43. package/dist/tools/wait-for.d.cts +74 -0
  44. package/dist/tools/wait-for.d.ts +74 -0
  45. package/dist/tools/wait-for.js +91 -0
  46. package/dist/types.cjs +16 -0
  47. package/dist/types.d.cts +165 -0
  48. package/dist/types.d.ts +165 -0
  49. package/dist/types.js +0 -0
  50. package/package.json +38 -0
@@ -0,0 +1,241 @@
1
+ import { SignalBus } from "./signal-bus.js";
2
+ import { TaskRegistry } from "./task-registry.js";
3
+ import { ConditionEngine } from "./condition-engine.js";
4
+ class AgentController {
5
+ agent;
6
+ logger;
7
+ state = "idle";
8
+ sessionId;
9
+ /** Signal bus for event routing */
10
+ signalBus;
11
+ /** Task registry for tracking background tasks */
12
+ taskRegistry;
13
+ /** Condition engine for evaluating wait conditions */
14
+ conditionEngine;
15
+ /** Signals that arrived while agent was busy */
16
+ pendingSignals = [];
17
+ /** Unsubscribe function for notify listener */
18
+ notifyUnsubscribe;
19
+ constructor(config) {
20
+ this.agent = config.agent;
21
+ if (config.logger) {
22
+ this.logger = config.logger;
23
+ }
24
+ this.sessionId = config.sessionId ?? `session-${Date.now()}`;
25
+ this.signalBus = new SignalBus();
26
+ this.taskRegistry = new TaskRegistry(this.signalBus, config.taskRegistry);
27
+ this.conditionEngine = new ConditionEngine(this.taskRegistry, this.signalBus, this.logger);
28
+ this.setupNotifyListener();
29
+ }
30
+ /**
31
+ * Set up listener for tasks with notify=true
32
+ */
33
+ setupNotifyListener() {
34
+ this.notifyUnsubscribe = this.signalBus.onAny((signal) => {
35
+ if (signal.type === "task:completed" || signal.type === "task:failed" || signal.type === "task:cancelled") {
36
+ const entry = this.taskRegistry.get(signal.taskId);
37
+ if (entry?.notify) {
38
+ if (this.state === "idle") {
39
+ this.logger?.debug(`Auto-notify triggered for task ${signal.taskId}`);
40
+ void this.processNotify(signal).catch((error) => {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ const signalContext = `signal.type=${signal.type} taskId=${signal.taskId}`;
43
+ this.logger?.error?.(
44
+ `AgentController.processNotify failed for ${signalContext}: ${message}`
45
+ );
46
+ });
47
+ } else {
48
+ this.pendingSignals.push(signal);
49
+ }
50
+ }
51
+ }
52
+ });
53
+ }
54
+ /**
55
+ * Process an auto-notify task completion
56
+ */
57
+ async processNotify(signal) {
58
+ if (this.state !== "idle") {
59
+ this.pendingSignals.push(signal);
60
+ return;
61
+ }
62
+ try {
63
+ this.state = "processing";
64
+ const taskInfo = signal.type === "task:completed" || signal.type === "task:failed" || signal.type === "task:cancelled" ? this.taskRegistry.getInfo(signal.taskId) : void 0;
65
+ const contextMessage = this.buildNotifyContext(signal, taskInfo);
66
+ await this.agent.generate(contextMessage, this.sessionId);
67
+ if (taskInfo) {
68
+ this.taskRegistry.acknowledgeNotify([taskInfo.taskId]);
69
+ }
70
+ } finally {
71
+ this.state = "idle";
72
+ this.processPendingSignals();
73
+ }
74
+ }
75
+ /**
76
+ * Build context message for auto-notify
77
+ */
78
+ buildNotifyContext(signal, taskInfo) {
79
+ if (signal.type === "task:completed" && taskInfo) {
80
+ const resultStr = typeof taskInfo.result === "string" ? taskInfo.result : JSON.stringify(taskInfo.result, null, 2);
81
+ const durationLine = taskInfo.duration !== void 0 ? `Duration: ${taskInfo.duration}ms
82
+ ` : "";
83
+ return `[Background Task Completed]
84
+ Task ID: ${taskInfo.taskId}
85
+ Type: ${taskInfo.type}
86
+ Description: ${taskInfo.description}
87
+ ` + durationLine + `Result:
88
+ ${resultStr}`;
89
+ }
90
+ if (signal.type === "task:failed" && taskInfo) {
91
+ return `[Background Task Failed]
92
+ Task ID: ${taskInfo.taskId}
93
+ Type: ${taskInfo.type}
94
+ Description: ${taskInfo.description}
95
+ Error: ${taskInfo.error}`;
96
+ }
97
+ if (signal.type === "task:cancelled" && taskInfo) {
98
+ const cancelReason = taskInfo.error ?? "Cancelled";
99
+ return `[Background Task Cancelled]
100
+ Task ID: ${taskInfo.taskId}
101
+ Type: ${taskInfo.type}
102
+ Description: ${taskInfo.description}
103
+ Reason: ${cancelReason}`;
104
+ }
105
+ return `[Background Signal]
106
+ ${JSON.stringify(signal, null, 2)}`;
107
+ }
108
+ /**
109
+ * Process any pending signals
110
+ */
111
+ processPendingSignals() {
112
+ while (this.pendingSignals.length > 0 && this.state === "idle") {
113
+ const signal = this.pendingSignals.shift();
114
+ if (signal) {
115
+ void this.processNotify(signal).catch((error) => {
116
+ const message = error instanceof Error ? error.message : String(error);
117
+ const signalContext = signal.type === "task:completed" || signal.type === "task:failed" || signal.type === "task:cancelled" ? `signal.type=${signal.type} taskId=${signal.taskId}` : `signal.type=${signal.type}`;
118
+ this.logger?.error?.(
119
+ `AgentController.processNotify failed for ${signalContext}: ${message}`
120
+ );
121
+ });
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * Process user input and generate response
127
+ * @param content User message content
128
+ * @returns Agent response
129
+ */
130
+ async process(content) {
131
+ if (this.state !== "idle") {
132
+ throw new Error(`Cannot process while agent is ${this.state}`);
133
+ }
134
+ try {
135
+ this.state = "processing";
136
+ const { contextPrefix, notifyTaskIds } = this.buildTaskContext();
137
+ const fullContent = contextPrefix ? `${contextPrefix}
138
+
139
+ ${content}` : content;
140
+ const response = await this.agent.generate(fullContent, this.sessionId);
141
+ if (notifyTaskIds.length > 0) {
142
+ this.taskRegistry.acknowledgeNotify(notifyTaskIds);
143
+ }
144
+ return response.content;
145
+ } finally {
146
+ this.state = "idle";
147
+ this.processPendingSignals();
148
+ }
149
+ }
150
+ /**
151
+ * Process a signal trigger (e.g., from external source)
152
+ */
153
+ async processSignal(signal) {
154
+ if (this.state !== "idle") {
155
+ this.pendingSignals.push(signal);
156
+ return;
157
+ }
158
+ await this.processNotify(signal);
159
+ }
160
+ /**
161
+ * Build context about pending/completed tasks
162
+ */
163
+ buildTaskContext() {
164
+ const running = this.taskRegistry.list({ status: "running" });
165
+ const notifyPending = this.taskRegistry.getNotifyPending();
166
+ if (running.length === 0 && notifyPending.length === 0) {
167
+ return { contextPrefix: "", notifyTaskIds: [] };
168
+ }
169
+ const parts = [];
170
+ if (running.length > 0) {
171
+ parts.push(
172
+ `[Background Tasks Running: ${running.length}]
173
+ ` + running.map((t) => `- ${t.taskId}: ${t.description}`).join("\n")
174
+ );
175
+ }
176
+ if (notifyPending.length > 0) {
177
+ parts.push(
178
+ `[Background Tasks Completed: ${notifyPending.length}]
179
+ ` + notifyPending.map((t) => {
180
+ const status = t.error ? `FAILED: ${t.error}` : t.status === "cancelled" ? "CANCELLED" : "SUCCESS";
181
+ return `- ${t.taskId}: ${t.description} [${status}]`;
182
+ }).join("\n")
183
+ );
184
+ }
185
+ return {
186
+ contextPrefix: parts.join("\n\n"),
187
+ notifyTaskIds: notifyPending.map((task) => task.taskId)
188
+ };
189
+ }
190
+ /**
191
+ * Get current agent state
192
+ */
193
+ getState() {
194
+ return this.state;
195
+ }
196
+ /**
197
+ * Get the wrapped agent
198
+ */
199
+ getAgent() {
200
+ return this.agent;
201
+ }
202
+ /**
203
+ * Get session ID
204
+ */
205
+ getSessionId() {
206
+ return this.sessionId;
207
+ }
208
+ /**
209
+ * Inject a signal for processing
210
+ */
211
+ injectSignal(signal) {
212
+ this.signalBus.emit(signal);
213
+ }
214
+ /**
215
+ * Clean up resources
216
+ */
217
+ cleanup() {
218
+ if (this.notifyUnsubscribe) {
219
+ this.notifyUnsubscribe();
220
+ }
221
+ this.pendingSignals = [];
222
+ this.signalBus.clear();
223
+ this.taskRegistry.clear();
224
+ }
225
+ /**
226
+ * Start the agent (delegates to wrapped agent)
227
+ */
228
+ async start() {
229
+ await this.agent.start();
230
+ }
231
+ /**
232
+ * Stop the agent (delegates to wrapped agent)
233
+ */
234
+ async stop() {
235
+ this.cleanup();
236
+ await this.agent.stop();
237
+ }
238
+ }
239
+ export {
240
+ AgentController
241
+ };
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var condition_engine_exports = {};
20
+ __export(condition_engine_exports, {
21
+ ConditionEngine: () => ConditionEngine
22
+ });
23
+ module.exports = __toCommonJS(condition_engine_exports);
24
+ var import_crypto = require("crypto");
25
+ class ConditionEngine {
26
+ constructor(taskRegistry, signalBus, logger) {
27
+ this.taskRegistry = taskRegistry;
28
+ this.signalBus = signalBus;
29
+ this.logger = logger;
30
+ }
31
+ /**
32
+ * Wait for a condition to be met
33
+ * @param condition Wait condition to evaluate
34
+ * @returns Promise resolving to the signal(s) that satisfied the condition
35
+ */
36
+ async wait(condition) {
37
+ const immediate = this.check(condition);
38
+ if (immediate) {
39
+ return immediate;
40
+ }
41
+ return this.evaluate(condition);
42
+ }
43
+ /**
44
+ * Check if a condition is already satisfied (non-blocking)
45
+ * @returns WaitResult if satisfied, null if not
46
+ */
47
+ check(condition) {
48
+ switch (condition.type) {
49
+ case "task":
50
+ return this.checkTask(condition.taskId);
51
+ case "any":
52
+ return this.checkAny(condition.conditions);
53
+ case "all":
54
+ return this.checkAll(condition.conditions);
55
+ case "timeout":
56
+ return null;
57
+ case "race":
58
+ return this.check(condition.task);
59
+ }
60
+ }
61
+ /**
62
+ * Check if a single task is completed
63
+ */
64
+ checkTask(taskId) {
65
+ const result = this.taskRegistry.getResult(taskId);
66
+ if (!result) {
67
+ return null;
68
+ }
69
+ if (result.status === "completed") {
70
+ return {
71
+ signal: {
72
+ type: "task:completed",
73
+ taskId,
74
+ result: result.result
75
+ }
76
+ };
77
+ }
78
+ if (result.status === "failed") {
79
+ return {
80
+ signal: {
81
+ type: "task:failed",
82
+ taskId,
83
+ error: result.error ?? "Unknown error"
84
+ }
85
+ };
86
+ }
87
+ if (result.status === "cancelled") {
88
+ return {
89
+ signal: {
90
+ type: "task:cancelled",
91
+ taskId
92
+ }
93
+ };
94
+ }
95
+ return null;
96
+ }
97
+ /**
98
+ * Check if any of the conditions is satisfied
99
+ */
100
+ checkAny(conditions) {
101
+ for (const condition of conditions) {
102
+ const result = this.check(condition);
103
+ if (result) {
104
+ return result;
105
+ }
106
+ }
107
+ return null;
108
+ }
109
+ /**
110
+ * Check if all conditions are satisfied
111
+ */
112
+ checkAll(conditions) {
113
+ const signals = [];
114
+ for (const condition of conditions) {
115
+ const result = this.check(condition);
116
+ if (!result) {
117
+ return null;
118
+ }
119
+ signals.push(result.signal);
120
+ }
121
+ const primarySignal = signals[0];
122
+ if (!primarySignal) {
123
+ throw new Error("Internal error: no signals in checkAll result");
124
+ }
125
+ return {
126
+ signal: primarySignal,
127
+ allSignals: signals
128
+ };
129
+ }
130
+ /**
131
+ * Evaluate a condition asynchronously
132
+ */
133
+ async evaluate(condition) {
134
+ switch (condition.type) {
135
+ case "task":
136
+ return this.evaluateTask(condition.taskId);
137
+ case "any":
138
+ return this.evaluateAny(condition.conditions);
139
+ case "all":
140
+ return this.evaluateAll(condition.conditions);
141
+ case "timeout":
142
+ return this.evaluateTimeout(condition.ms, condition.conditionId);
143
+ case "race":
144
+ return this.evaluateRace(condition.task, condition.timeout);
145
+ }
146
+ }
147
+ /**
148
+ * Wait for a single task to complete
149
+ *
150
+ * Uses subscribe-then-check pattern to avoid race conditions where
151
+ * the task completes between checking and subscribing.
152
+ */
153
+ async evaluateTask(taskId) {
154
+ this.logger?.debug(`[ConditionEngine] evaluateTask called for taskId=${taskId}`);
155
+ return new Promise((resolve) => {
156
+ let unsubscribe;
157
+ const handler = (signal) => {
158
+ this.logger?.debug(
159
+ `[ConditionEngine] Received signal: type=${signal.type}, taskId=${"taskId" in signal ? signal.taskId : "N/A"}`
160
+ );
161
+ if ((signal.type === "task:completed" || signal.type === "task:failed" || signal.type === "task:cancelled") && signal.taskId === taskId) {
162
+ this.logger?.debug(
163
+ `[ConditionEngine] Signal matches taskId=${taskId}, resolving`
164
+ );
165
+ if (unsubscribe) {
166
+ unsubscribe();
167
+ }
168
+ resolve({ signal });
169
+ }
170
+ };
171
+ unsubscribe = this.signalBus.onAny(handler);
172
+ this.logger?.debug(`[ConditionEngine] Subscribed to signals for taskId=${taskId}`);
173
+ const immediate = this.checkTask(taskId);
174
+ this.logger?.debug(
175
+ `[ConditionEngine] checkTask(${taskId}) returned: ${immediate ? "found" : "null"}`
176
+ );
177
+ if (immediate) {
178
+ unsubscribe();
179
+ resolve(immediate);
180
+ }
181
+ });
182
+ }
183
+ /**
184
+ * Wait for any of the conditions to be satisfied
185
+ */
186
+ async evaluateAny(conditions) {
187
+ const immediate = this.checkAny(conditions);
188
+ if (immediate) {
189
+ return immediate;
190
+ }
191
+ const promises = conditions.map((c) => this.evaluate(c));
192
+ const result = await Promise.race(promises);
193
+ return result;
194
+ }
195
+ /**
196
+ * Wait for all conditions to be satisfied
197
+ */
198
+ async evaluateAll(conditions) {
199
+ const immediate = this.checkAll(conditions);
200
+ if (immediate) {
201
+ return immediate;
202
+ }
203
+ const results = await Promise.all(conditions.map((c) => this.evaluate(c)));
204
+ const signals = results.map((r) => r.signal);
205
+ const primarySignal = signals[0];
206
+ if (!primarySignal) {
207
+ throw new Error("Internal error: no signals in evaluateAll result");
208
+ }
209
+ return {
210
+ signal: primarySignal,
211
+ allSignals: signals
212
+ };
213
+ }
214
+ /**
215
+ * Wait for a timeout
216
+ */
217
+ async evaluateTimeout(ms, conditionId) {
218
+ await new Promise((resolve) => setTimeout(resolve, ms));
219
+ const signal = {
220
+ type: "timeout",
221
+ conditionId
222
+ };
223
+ this.signalBus.emit(signal);
224
+ return { signal };
225
+ }
226
+ /**
227
+ * Race a task condition against a timeout
228
+ */
229
+ async evaluateRace(taskCondition, timeoutCondition) {
230
+ const immediate = this.check(taskCondition);
231
+ if (immediate) {
232
+ return immediate;
233
+ }
234
+ const result = await Promise.race([
235
+ this.evaluate(taskCondition),
236
+ this.evaluate(timeoutCondition)
237
+ ]);
238
+ return result;
239
+ }
240
+ /**
241
+ * Helper to create a race condition with timeout
242
+ */
243
+ static createRaceWithTimeout(taskId, timeoutMs) {
244
+ return {
245
+ type: "race",
246
+ task: { type: "task", taskId },
247
+ timeout: {
248
+ type: "timeout",
249
+ ms: timeoutMs,
250
+ conditionId: `timeout-${(0, import_crypto.randomUUID)().slice(0, 8)}`
251
+ }
252
+ };
253
+ }
254
+ /**
255
+ * Helper to create an 'any' condition from task IDs
256
+ */
257
+ static createAnyTask(taskIds) {
258
+ return {
259
+ type: "any",
260
+ conditions: taskIds.map((taskId) => ({ type: "task", taskId }))
261
+ };
262
+ }
263
+ /**
264
+ * Helper to create an 'all' condition from task IDs
265
+ */
266
+ static createAllTasks(taskIds) {
267
+ return {
268
+ type: "all",
269
+ conditions: taskIds.map((taskId) => ({ type: "task", taskId }))
270
+ };
271
+ }
272
+ }
273
+ // Annotate the CommonJS export names for ESM import in node:
274
+ 0 && (module.exports = {
275
+ ConditionEngine
276
+ });
@@ -0,0 +1,87 @@
1
+ import { WaitCondition, WaitResult } from './types.cjs';
2
+ import { SignalBus } from './signal-bus.cjs';
3
+ import { TaskRegistry } from './task-registry.cjs';
4
+
5
+ /**
6
+ * ConditionEngine
7
+ *
8
+ * Evaluates wait conditions and resolves when met.
9
+ * Supports single task, any/all of multiple tasks, timeouts, and races.
10
+ */
11
+
12
+ type LoggerLike = {
13
+ debug: (message: string) => void;
14
+ };
15
+ /**
16
+ * ConditionEngine - Evaluates composable wait conditions
17
+ */
18
+ declare class ConditionEngine {
19
+ private taskRegistry;
20
+ private signalBus;
21
+ private logger?;
22
+ constructor(taskRegistry: TaskRegistry, signalBus: SignalBus, logger?: LoggerLike | undefined);
23
+ /**
24
+ * Wait for a condition to be met
25
+ * @param condition Wait condition to evaluate
26
+ * @returns Promise resolving to the signal(s) that satisfied the condition
27
+ */
28
+ wait(condition: WaitCondition): Promise<WaitResult>;
29
+ /**
30
+ * Check if a condition is already satisfied (non-blocking)
31
+ * @returns WaitResult if satisfied, null if not
32
+ */
33
+ check(condition: WaitCondition): WaitResult | null;
34
+ /**
35
+ * Check if a single task is completed
36
+ */
37
+ private checkTask;
38
+ /**
39
+ * Check if any of the conditions is satisfied
40
+ */
41
+ private checkAny;
42
+ /**
43
+ * Check if all conditions are satisfied
44
+ */
45
+ private checkAll;
46
+ /**
47
+ * Evaluate a condition asynchronously
48
+ */
49
+ private evaluate;
50
+ /**
51
+ * Wait for a single task to complete
52
+ *
53
+ * Uses subscribe-then-check pattern to avoid race conditions where
54
+ * the task completes between checking and subscribing.
55
+ */
56
+ private evaluateTask;
57
+ /**
58
+ * Wait for any of the conditions to be satisfied
59
+ */
60
+ private evaluateAny;
61
+ /**
62
+ * Wait for all conditions to be satisfied
63
+ */
64
+ private evaluateAll;
65
+ /**
66
+ * Wait for a timeout
67
+ */
68
+ private evaluateTimeout;
69
+ /**
70
+ * Race a task condition against a timeout
71
+ */
72
+ private evaluateRace;
73
+ /**
74
+ * Helper to create a race condition with timeout
75
+ */
76
+ static createRaceWithTimeout(taskId: string, timeoutMs: number): WaitCondition;
77
+ /**
78
+ * Helper to create an 'any' condition from task IDs
79
+ */
80
+ static createAnyTask(taskIds: string[]): WaitCondition;
81
+ /**
82
+ * Helper to create an 'all' condition from task IDs
83
+ */
84
+ static createAllTasks(taskIds: string[]): WaitCondition;
85
+ }
86
+
87
+ export { ConditionEngine };
@@ -0,0 +1,87 @@
1
+ import { WaitCondition, WaitResult } from './types.js';
2
+ import { SignalBus } from './signal-bus.js';
3
+ import { TaskRegistry } from './task-registry.js';
4
+
5
+ /**
6
+ * ConditionEngine
7
+ *
8
+ * Evaluates wait conditions and resolves when met.
9
+ * Supports single task, any/all of multiple tasks, timeouts, and races.
10
+ */
11
+
12
+ type LoggerLike = {
13
+ debug: (message: string) => void;
14
+ };
15
+ /**
16
+ * ConditionEngine - Evaluates composable wait conditions
17
+ */
18
+ declare class ConditionEngine {
19
+ private taskRegistry;
20
+ private signalBus;
21
+ private logger?;
22
+ constructor(taskRegistry: TaskRegistry, signalBus: SignalBus, logger?: LoggerLike | undefined);
23
+ /**
24
+ * Wait for a condition to be met
25
+ * @param condition Wait condition to evaluate
26
+ * @returns Promise resolving to the signal(s) that satisfied the condition
27
+ */
28
+ wait(condition: WaitCondition): Promise<WaitResult>;
29
+ /**
30
+ * Check if a condition is already satisfied (non-blocking)
31
+ * @returns WaitResult if satisfied, null if not
32
+ */
33
+ check(condition: WaitCondition): WaitResult | null;
34
+ /**
35
+ * Check if a single task is completed
36
+ */
37
+ private checkTask;
38
+ /**
39
+ * Check if any of the conditions is satisfied
40
+ */
41
+ private checkAny;
42
+ /**
43
+ * Check if all conditions are satisfied
44
+ */
45
+ private checkAll;
46
+ /**
47
+ * Evaluate a condition asynchronously
48
+ */
49
+ private evaluate;
50
+ /**
51
+ * Wait for a single task to complete
52
+ *
53
+ * Uses subscribe-then-check pattern to avoid race conditions where
54
+ * the task completes between checking and subscribing.
55
+ */
56
+ private evaluateTask;
57
+ /**
58
+ * Wait for any of the conditions to be satisfied
59
+ */
60
+ private evaluateAny;
61
+ /**
62
+ * Wait for all conditions to be satisfied
63
+ */
64
+ private evaluateAll;
65
+ /**
66
+ * Wait for a timeout
67
+ */
68
+ private evaluateTimeout;
69
+ /**
70
+ * Race a task condition against a timeout
71
+ */
72
+ private evaluateRace;
73
+ /**
74
+ * Helper to create a race condition with timeout
75
+ */
76
+ static createRaceWithTimeout(taskId: string, timeoutMs: number): WaitCondition;
77
+ /**
78
+ * Helper to create an 'any' condition from task IDs
79
+ */
80
+ static createAnyTask(taskIds: string[]): WaitCondition;
81
+ /**
82
+ * Helper to create an 'all' condition from task IDs
83
+ */
84
+ static createAllTasks(taskIds: string[]): WaitCondition;
85
+ }
86
+
87
+ export { ConditionEngine };