@memgrafter/flatagents 0.10.0 → 2.0.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/MACHINES.md CHANGED
@@ -22,7 +22,7 @@
22
22
  ```yaml
23
23
  # profiles.yml — agents reference by name
24
24
  spec: flatprofiles
25
- spec_version: "0.10.0"
25
+ spec_version: "2.0.0"
26
26
  data:
27
27
  model_profiles:
28
28
  fast: { provider: cerebras, name: zai-glm-4.6, temperature: 0.6 }
@@ -34,6 +34,13 @@ data:
34
34
  Agent model field: `"fast"` | `{ profile: "fast", temperature: 0.9 }` | `{ provider: x, name: y }`
35
35
  Resolution: default → profile → overrides → override
36
36
 
37
+ ## Agent References
38
+
39
+ `data.agents` values may be:
40
+ - String path to a flatagent config
41
+ - Inline flatagent config (`spec: flatagent`)
42
+ - Typed adapter ref: `{ type: "flatagent" | "smolagents" | "pi-agent", ref?: "...", config?: {...} }`
43
+
37
44
  ## State Fields
38
45
 
39
46
  | Field | Purpose |
@@ -49,6 +56,7 @@ Resolution: default → profile → overrides → override
49
56
  | `on_error` | State name or `{ default: x, ErrorType: y }` |
50
57
  | `transitions` | `[{ condition: "expr", to: state }, { to: default }]` |
51
58
  | `mode` | `settled` (all) / `any` (first) for parallel |
59
+ | `wait_for` | Channel to wait for external signal (Jinja2 template) |
52
60
  | `timeout` | Seconds (0=forever) |
53
61
 
54
62
  ## Patterns
@@ -80,6 +88,19 @@ launch: background_task
80
88
  launch_input: { data: "{{ context.data }}" }
81
89
  ```
82
90
 
91
+ **Wait for signal** (checkpoint, exit, resume on signal):
92
+ ```yaml
93
+ wait_for_approval:
94
+ wait_for: "approval/{{ context.task_id }}"
95
+ timeout: 86400
96
+ output_to_context:
97
+ approved: "{{ output.approved }}"
98
+ transitions:
99
+ - condition: "context.approved"
100
+ to: continue
101
+ - to: rejected
102
+ ```
103
+
83
104
  ## Distributed Worker Pattern
84
105
 
85
106
  Use hook actions (e.g., `DistributedWorkerHooks`) with a `RegistrationBackend` + `WorkBackend` to build worker pools.
@@ -102,6 +123,31 @@ states:
102
123
 
103
124
  See `sdk/examples/distributed_worker/` for a full example.
104
125
 
126
+ ## Signals & Triggers
127
+
128
+ Machine pauses at `wait_for` state → checkpoints with `waiting_channel` → process exits. Nothing running.
129
+
130
+ **Signal delivery**: External process writes signal → trigger fires → dispatcher resumes matching machines.
131
+
132
+ ```
133
+ send("approval/task-001", {approved: true})
134
+ → SQLite INSERT + touch trigger file
135
+ → launchd/systemd starts dispatcher
136
+ → dispatcher queries checkpoints WHERE waiting_channel = "approval/task-001"
137
+ → resumes machine from checkpoint, signal data as output.*
138
+ ```
139
+
140
+ **Channel semantics**:
141
+ - Addressed: `"approval/{{ context.task_id }}"` → one waiter
142
+ - Broadcast: `"quota/openai"` → N waiters (dispatcher controls limit)
143
+
144
+ **10,000 waiting machines** = 10,000 rows in SQLite. Zero processes, zero memory.
145
+
146
+ **Trigger backends**: `none` (polling), `file` (launchd/systemd — nothing running), `socket` (UDS, in-process).
147
+ DynamoDB Streams is implicit (no trigger code needed).
148
+
149
+ **Signal backends**: `memory` (testing), `sqlite` (local durable), `dynamodb` (AWS).
150
+
105
151
  ## Context Variables
106
152
 
107
153
  `context.*` (all states), `input.*` (initial), `output.*` (in output_to_context), `item`/`as` (foreach)
@@ -123,3 +169,12 @@ class MyHooks(MachineHooks):
123
169
  persistence: { enabled: true, backend: local } # local | memory
124
170
  ```
125
171
  Resume: `machine.execute(resume_from=execution_id)`
172
+
173
+ ## SDKs
174
+
175
+ ### Python SDKs
176
+ - **flatagents** (agents): `pip install flatagents[litellm]`
177
+ - **flatmachines** (orchestration): `pip install flatmachines[flatagents]`
178
+
179
+ ### JavaScript SDK
180
+ A single JS SDK lives under [`sdk/js`](./sdk/js). It follows the same specs but is not yet split into separate FlatAgents/FlatMachines packages.
package/dist/index.d.mts CHANGED
@@ -1,3 +1,39 @@
1
+ declare class WebhookHooks implements MachineHooks {
2
+ private url;
3
+ constructor(url: string);
4
+ private send;
5
+ onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
6
+ onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
7
+ onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
8
+ onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
9
+ onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
10
+ }
11
+ declare class CompositeHooks implements MachineHooks {
12
+ private hooks;
13
+ constructor(hooks: MachineHooks[]);
14
+ onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
15
+ onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
16
+ onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
17
+ onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
18
+ onTransition(from: string, to: string, context: Record<string, any>): Promise<string>;
19
+ onError(state: string, error: Error, context: Record<string, any>): Promise<string | null>;
20
+ onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
21
+ }
22
+ /**
23
+ * Name-based registry for resolving hooks from machine config.
24
+ *
25
+ * Machine configs reference hooks by name (e.g., hooks: "my-hooks").
26
+ * The registry maps names to factory classes/functions and resolves
27
+ * them at runtime, keeping configs language-agnostic.
28
+ */
29
+ declare class HooksRegistry {
30
+ private factories;
31
+ register(name: string, factory: HooksFactory): void;
32
+ has(name: string): boolean;
33
+ resolve(ref: HooksRef): MachineHooks;
34
+ private resolveSingle;
35
+ }
36
+
1
37
  /**
2
38
  * Model configuration for an agent.
3
39
  * Can be inline config, profile reference, or profile with overrides.
@@ -200,9 +236,18 @@ interface ResultBackend {
200
236
  exists(uri: string): Promise<boolean>;
201
237
  delete(uri: string): Promise<void>;
202
238
  }
239
+ type HooksRef = string | HooksRefConfig | Array<string | HooksRefConfig>;
240
+ interface HooksRefConfig {
241
+ name: string;
242
+ args?: Record<string, any>;
243
+ }
244
+ type HooksFactory = {
245
+ new (args?: Record<string, any>): MachineHooks;
246
+ } | ((args?: Record<string, any>) => MachineHooks);
203
247
  interface MachineOptions {
204
248
  config: MachineConfig | string;
205
249
  hooks?: MachineHooks;
250
+ hooksRegistry?: HooksRegistry;
206
251
  persistence?: PersistenceBackend;
207
252
  resultBackend?: ResultBackend;
208
253
  executionLock?: ExecutionLock;
@@ -409,6 +454,7 @@ declare class FlatMachine {
409
454
  private context;
410
455
  private input;
411
456
  private hooks?;
457
+ private _hooksRegistry;
412
458
  private checkpointManager?;
413
459
  private resultBackend?;
414
460
  private executionLock;
@@ -420,6 +466,8 @@ declare class FlatMachine {
420
466
  private currentState?;
421
467
  private currentStep;
422
468
  constructor(options: MachineOptions);
469
+ get hooksRegistry(): HooksRegistry;
470
+ private resolveHooks;
423
471
  execute(input?: Record<string, any>, resumeSnapshot?: MachineSnapshot): Promise<any>;
424
472
  private executeInternal;
425
473
  resume(executionId: string): Promise<any>;
@@ -574,28 +622,6 @@ declare class MDAPVotingExecution implements ExecutionType {
574
622
  }
575
623
  declare function getExecutionType(config?: ExecutionConfig): ExecutionType;
576
624
 
577
- declare class WebhookHooks implements MachineHooks {
578
- private url;
579
- constructor(url: string);
580
- private send;
581
- onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
582
- onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
583
- onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
584
- onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
585
- onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
586
- }
587
- declare class CompositeHooks implements MachineHooks {
588
- private hooks;
589
- constructor(hooks: MachineHooks[]);
590
- onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
591
- onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
592
- onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
593
- onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
594
- onTransition(from: string, to: string, context: Record<string, any>): Promise<string>;
595
- onError(state: string, error: Error, context: Record<string, any>): Promise<string | null>;
596
- onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
597
- }
598
-
599
625
  declare class MemoryBackend implements PersistenceBackend {
600
626
  private store;
601
627
  save(key: string, snapshot: MachineSnapshot): Promise<void>;
@@ -670,4 +696,4 @@ declare class LocalFileLock implements ExecutionLock {
670
696
  release(key: string): Promise<void>;
671
697
  }
672
698
 
673
- export { type AgentConfig, type AgentOptions, type BackendConfig, CheckpointManager, CompositeHooks, DefaultExecution, type ExecutionConfig, type ExecutionLock, type ExecutionType, FlatAgent, FlatMachine, type LLMBackend, type LLMBackendConfig, type LLMOptions, LocalFileBackend, LocalFileLock, type MCPServer, MCPToolProvider, MDAPVotingExecution, type MachineConfig, type MachineHooks, type MachineOptions, type MachineSnapshot, MemoryBackend, type Message, MockLLMBackend, type MockResponse, type ModelConfig, type ModelProfileConfig, NoOpLock, ParallelExecution, type PersistenceBackend, ProfileManager, type ProfiledModelConfig, type ProfilesConfig, type ResultBackend, RetryExecution, type State, type TemplateAllowlist, type ToolCall, type ToolDefinition, type ToolFilter, VercelAIBackend, WebhookHooks, evaluate, getExecutionType, inMemoryResultBackend, resolveModelConfig, setTemplateAllowlist };
699
+ export { type AgentConfig, type AgentOptions, type BackendConfig, CheckpointManager, CompositeHooks, DefaultExecution, type ExecutionConfig, type ExecutionLock, type ExecutionType, FlatAgent, FlatMachine, type HooksFactory, type HooksRef, type HooksRefConfig, HooksRegistry, type LLMBackend, type LLMBackendConfig, type LLMOptions, LocalFileBackend, LocalFileLock, type MCPServer, MCPToolProvider, MDAPVotingExecution, type MachineConfig, type MachineHooks, type MachineOptions, type MachineSnapshot, MemoryBackend, type Message, MockLLMBackend, type MockResponse, type ModelConfig, type ModelProfileConfig, NoOpLock, ParallelExecution, type PersistenceBackend, ProfileManager, type ProfiledModelConfig, type ProfilesConfig, type ResultBackend, RetryExecution, type State, type TemplateAllowlist, type ToolCall, type ToolDefinition, type ToolFilter, VercelAIBackend, WebhookHooks, evaluate, getExecutionType, inMemoryResultBackend, resolveModelConfig, setTemplateAllowlist };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,39 @@
1
+ declare class WebhookHooks implements MachineHooks {
2
+ private url;
3
+ constructor(url: string);
4
+ private send;
5
+ onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
6
+ onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
7
+ onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
8
+ onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
9
+ onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
10
+ }
11
+ declare class CompositeHooks implements MachineHooks {
12
+ private hooks;
13
+ constructor(hooks: MachineHooks[]);
14
+ onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
15
+ onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
16
+ onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
17
+ onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
18
+ onTransition(from: string, to: string, context: Record<string, any>): Promise<string>;
19
+ onError(state: string, error: Error, context: Record<string, any>): Promise<string | null>;
20
+ onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
21
+ }
22
+ /**
23
+ * Name-based registry for resolving hooks from machine config.
24
+ *
25
+ * Machine configs reference hooks by name (e.g., hooks: "my-hooks").
26
+ * The registry maps names to factory classes/functions and resolves
27
+ * them at runtime, keeping configs language-agnostic.
28
+ */
29
+ declare class HooksRegistry {
30
+ private factories;
31
+ register(name: string, factory: HooksFactory): void;
32
+ has(name: string): boolean;
33
+ resolve(ref: HooksRef): MachineHooks;
34
+ private resolveSingle;
35
+ }
36
+
1
37
  /**
2
38
  * Model configuration for an agent.
3
39
  * Can be inline config, profile reference, or profile with overrides.
@@ -200,9 +236,18 @@ interface ResultBackend {
200
236
  exists(uri: string): Promise<boolean>;
201
237
  delete(uri: string): Promise<void>;
202
238
  }
239
+ type HooksRef = string | HooksRefConfig | Array<string | HooksRefConfig>;
240
+ interface HooksRefConfig {
241
+ name: string;
242
+ args?: Record<string, any>;
243
+ }
244
+ type HooksFactory = {
245
+ new (args?: Record<string, any>): MachineHooks;
246
+ } | ((args?: Record<string, any>) => MachineHooks);
203
247
  interface MachineOptions {
204
248
  config: MachineConfig | string;
205
249
  hooks?: MachineHooks;
250
+ hooksRegistry?: HooksRegistry;
206
251
  persistence?: PersistenceBackend;
207
252
  resultBackend?: ResultBackend;
208
253
  executionLock?: ExecutionLock;
@@ -409,6 +454,7 @@ declare class FlatMachine {
409
454
  private context;
410
455
  private input;
411
456
  private hooks?;
457
+ private _hooksRegistry;
412
458
  private checkpointManager?;
413
459
  private resultBackend?;
414
460
  private executionLock;
@@ -420,6 +466,8 @@ declare class FlatMachine {
420
466
  private currentState?;
421
467
  private currentStep;
422
468
  constructor(options: MachineOptions);
469
+ get hooksRegistry(): HooksRegistry;
470
+ private resolveHooks;
423
471
  execute(input?: Record<string, any>, resumeSnapshot?: MachineSnapshot): Promise<any>;
424
472
  private executeInternal;
425
473
  resume(executionId: string): Promise<any>;
@@ -574,28 +622,6 @@ declare class MDAPVotingExecution implements ExecutionType {
574
622
  }
575
623
  declare function getExecutionType(config?: ExecutionConfig): ExecutionType;
576
624
 
577
- declare class WebhookHooks implements MachineHooks {
578
- private url;
579
- constructor(url: string);
580
- private send;
581
- onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
582
- onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
583
- onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
584
- onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
585
- onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
586
- }
587
- declare class CompositeHooks implements MachineHooks {
588
- private hooks;
589
- constructor(hooks: MachineHooks[]);
590
- onMachineStart(context: Record<string, any>): Promise<Record<string, any>>;
591
- onMachineEnd(context: Record<string, any>, output: any): Promise<any>;
592
- onStateEnter(state: string, context: Record<string, any>): Promise<Record<string, any>>;
593
- onStateExit(state: string, context: Record<string, any>, output: any): Promise<any>;
594
- onTransition(from: string, to: string, context: Record<string, any>): Promise<string>;
595
- onError(state: string, error: Error, context: Record<string, any>): Promise<string | null>;
596
- onAction(action: string, context: Record<string, any>): Promise<Record<string, any>>;
597
- }
598
-
599
625
  declare class MemoryBackend implements PersistenceBackend {
600
626
  private store;
601
627
  save(key: string, snapshot: MachineSnapshot): Promise<void>;
@@ -670,4 +696,4 @@ declare class LocalFileLock implements ExecutionLock {
670
696
  release(key: string): Promise<void>;
671
697
  }
672
698
 
673
- export { type AgentConfig, type AgentOptions, type BackendConfig, CheckpointManager, CompositeHooks, DefaultExecution, type ExecutionConfig, type ExecutionLock, type ExecutionType, FlatAgent, FlatMachine, type LLMBackend, type LLMBackendConfig, type LLMOptions, LocalFileBackend, LocalFileLock, type MCPServer, MCPToolProvider, MDAPVotingExecution, type MachineConfig, type MachineHooks, type MachineOptions, type MachineSnapshot, MemoryBackend, type Message, MockLLMBackend, type MockResponse, type ModelConfig, type ModelProfileConfig, NoOpLock, ParallelExecution, type PersistenceBackend, ProfileManager, type ProfiledModelConfig, type ProfilesConfig, type ResultBackend, RetryExecution, type State, type TemplateAllowlist, type ToolCall, type ToolDefinition, type ToolFilter, VercelAIBackend, WebhookHooks, evaluate, getExecutionType, inMemoryResultBackend, resolveModelConfig, setTemplateAllowlist };
699
+ export { type AgentConfig, type AgentOptions, type BackendConfig, CheckpointManager, CompositeHooks, DefaultExecution, type ExecutionConfig, type ExecutionLock, type ExecutionType, FlatAgent, FlatMachine, type HooksFactory, type HooksRef, type HooksRefConfig, HooksRegistry, type LLMBackend, type LLMBackendConfig, type LLMOptions, LocalFileBackend, LocalFileLock, type MCPServer, MCPToolProvider, MDAPVotingExecution, type MachineConfig, type MachineHooks, type MachineOptions, type MachineSnapshot, MemoryBackend, type Message, MockLLMBackend, type MockResponse, type ModelConfig, type ModelProfileConfig, NoOpLock, ParallelExecution, type PersistenceBackend, ProfileManager, type ProfiledModelConfig, type ProfilesConfig, type ResultBackend, RetryExecution, type State, type TemplateAllowlist, type ToolCall, type ToolDefinition, type ToolFilter, VercelAIBackend, WebhookHooks, evaluate, getExecutionType, inMemoryResultBackend, resolveModelConfig, setTemplateAllowlist };
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  DefaultExecution: () => DefaultExecution,
36
36
  FlatAgent: () => FlatAgent,
37
37
  FlatMachine: () => FlatMachine,
38
+ HooksRegistry: () => HooksRegistry,
38
39
  LocalFileBackend: () => LocalFileBackend,
39
40
  LocalFileLock: () => LocalFileLock,
40
41
  MCPToolProvider: () => MCPToolProvider,
@@ -1006,6 +1007,179 @@ var LocalFileLock = class {
1006
1007
  }
1007
1008
  };
1008
1009
 
1010
+ // src/hooks.ts
1011
+ var WebhookHooks = class {
1012
+ constructor(url) {
1013
+ this.url = url;
1014
+ }
1015
+ async send(event, data) {
1016
+ try {
1017
+ const body = JSON.stringify({ event, ...data, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, (key, value) => {
1018
+ if (typeof value === "object" && value !== null) {
1019
+ const seen = /* @__PURE__ */ new WeakSet();
1020
+ return JSON.parse(JSON.stringify(value, (k, v) => {
1021
+ if (typeof v === "object" && v !== null) {
1022
+ if (seen.has(v)) return "[Circular]";
1023
+ seen.add(v);
1024
+ }
1025
+ return v;
1026
+ }));
1027
+ }
1028
+ return value;
1029
+ });
1030
+ await fetch(this.url, {
1031
+ method: "POST",
1032
+ headers: { "Content-Type": "application/json" },
1033
+ body
1034
+ });
1035
+ } catch {
1036
+ }
1037
+ }
1038
+ async onMachineStart(context) {
1039
+ await this.send("machine_start", { context });
1040
+ return context;
1041
+ }
1042
+ async onMachineEnd(context, output) {
1043
+ await this.send("machine_end", { context, output });
1044
+ return output;
1045
+ }
1046
+ async onStateEnter(state, context) {
1047
+ await this.send("state_enter", { state, context });
1048
+ return context;
1049
+ }
1050
+ async onStateExit(state, context, output) {
1051
+ await this.send("state_exit", { state, context, output });
1052
+ return output;
1053
+ }
1054
+ async onAction(action, context) {
1055
+ await this.send("action", { action, context });
1056
+ return context;
1057
+ }
1058
+ };
1059
+ var CompositeHooks = class {
1060
+ constructor(hooks) {
1061
+ this.hooks = hooks;
1062
+ }
1063
+ async onMachineStart(context) {
1064
+ let result = context;
1065
+ for (const hook of this.hooks) {
1066
+ if (hook.onMachineStart) {
1067
+ try {
1068
+ result = await hook.onMachineStart(result);
1069
+ } catch {
1070
+ }
1071
+ }
1072
+ }
1073
+ return result;
1074
+ }
1075
+ async onMachineEnd(context, output) {
1076
+ let result = output;
1077
+ for (const hook of this.hooks) {
1078
+ if (hook.onMachineEnd) {
1079
+ try {
1080
+ result = await hook.onMachineEnd(context, result);
1081
+ } catch {
1082
+ }
1083
+ }
1084
+ }
1085
+ return result;
1086
+ }
1087
+ async onStateEnter(state, context) {
1088
+ let result = context;
1089
+ for (const hook of this.hooks) {
1090
+ if (hook.onStateEnter) {
1091
+ try {
1092
+ result = await hook.onStateEnter(state, result);
1093
+ } catch {
1094
+ }
1095
+ }
1096
+ }
1097
+ return result;
1098
+ }
1099
+ async onStateExit(state, context, output) {
1100
+ let result = output;
1101
+ for (const hook of this.hooks) {
1102
+ if (hook.onStateExit) {
1103
+ try {
1104
+ result = await hook.onStateExit(state, context, result);
1105
+ } catch {
1106
+ }
1107
+ }
1108
+ }
1109
+ return result;
1110
+ }
1111
+ async onTransition(from, to, context) {
1112
+ let result = to;
1113
+ for (const hook of this.hooks) {
1114
+ if (hook.onTransition) {
1115
+ try {
1116
+ result = await hook.onTransition(from, result, context);
1117
+ } catch {
1118
+ }
1119
+ }
1120
+ }
1121
+ return result;
1122
+ }
1123
+ async onError(state, error, context) {
1124
+ let result = null;
1125
+ for (const hook of this.hooks) {
1126
+ if (hook.onError) {
1127
+ try {
1128
+ const hookResult = await hook.onError(state, error, context);
1129
+ if (hookResult !== null) result = hookResult;
1130
+ } catch {
1131
+ }
1132
+ }
1133
+ }
1134
+ return result;
1135
+ }
1136
+ async onAction(action, context) {
1137
+ let result = context;
1138
+ for (const hook of this.hooks) {
1139
+ if (hook.onAction) {
1140
+ try {
1141
+ result = await hook.onAction(action, result);
1142
+ } catch {
1143
+ }
1144
+ }
1145
+ }
1146
+ return result;
1147
+ }
1148
+ };
1149
+ var HooksRegistry = class {
1150
+ constructor() {
1151
+ this.factories = /* @__PURE__ */ new Map();
1152
+ }
1153
+ register(name, factory) {
1154
+ this.factories.set(name, factory);
1155
+ }
1156
+ has(name) {
1157
+ return this.factories.has(name);
1158
+ }
1159
+ resolve(ref) {
1160
+ if (Array.isArray(ref)) {
1161
+ const hooks = ref.map((entry) => this.resolveSingle(entry));
1162
+ return new CompositeHooks(hooks);
1163
+ }
1164
+ return this.resolveSingle(ref);
1165
+ }
1166
+ resolveSingle(ref) {
1167
+ const name = typeof ref === "string" ? ref : ref.name;
1168
+ const args = typeof ref === "string" ? void 0 : ref.args;
1169
+ const factory = this.factories.get(name);
1170
+ if (!factory) {
1171
+ throw new Error(
1172
+ `No hooks registered for name '${name}'. Registered: [${[...this.factories.keys()].join(", ")}]`
1173
+ );
1174
+ }
1175
+ try {
1176
+ return new factory(args);
1177
+ } catch {
1178
+ return factory(args);
1179
+ }
1180
+ }
1181
+ };
1182
+
1009
1183
  // src/flatmachine.ts
1010
1184
  var FlatMachine = class _FlatMachine {
1011
1185
  constructor(options) {
@@ -1017,7 +1191,8 @@ var FlatMachine = class _FlatMachine {
1017
1191
  this.pendingLaunches = [];
1018
1192
  this.currentStep = 0;
1019
1193
  this.config = typeof options.config === "string" ? yaml3.parse((0, import_fs5.readFileSync)(options.config, "utf-8")) : options.config;
1020
- this.hooks = options.hooks;
1194
+ this._hooksRegistry = options.hooksRegistry ?? new HooksRegistry();
1195
+ this.hooks = this.resolveHooks(options.hooks);
1021
1196
  this.configDir = options.configDir ?? process.cwd();
1022
1197
  this.profilesFile = this.resolveProfilesFile(options.profilesFile);
1023
1198
  this.executionId = options.executionId ?? this.executionId;
@@ -1040,6 +1215,15 @@ var FlatMachine = class _FlatMachine {
1040
1215
  this.checkpointEvents = new Set(events);
1041
1216
  }
1042
1217
  }
1218
+ get hooksRegistry() {
1219
+ return this._hooksRegistry;
1220
+ }
1221
+ resolveHooks(explicit) {
1222
+ if (explicit) return explicit;
1223
+ const hooksConfig = this.config.data.hooks;
1224
+ if (!hooksConfig) return void 0;
1225
+ return this._hooksRegistry.resolve(hooksConfig);
1226
+ }
1043
1227
  async execute(input, resumeSnapshot) {
1044
1228
  if (this.config.data.expression_engine === "cel") {
1045
1229
  throw new Error("expression_engine 'cel' is not supported in the JS SDK yet");
@@ -1323,7 +1507,7 @@ var FlatMachine = class _FlatMachine {
1323
1507
  config: resolved.config,
1324
1508
  configDir: resolved.configDir,
1325
1509
  resultBackend: this.resultBackend,
1326
- hooks: this.hooks,
1510
+ hooksRegistry: this._hooksRegistry,
1327
1511
  executionId: overrides?.executionId,
1328
1512
  parentExecutionId: overrides?.parentExecutionId,
1329
1513
  profilesFile: this.profilesFile
@@ -1533,146 +1717,6 @@ var FlatMachine = class _FlatMachine {
1533
1717
  return { [firstKey]: results[firstKey] };
1534
1718
  }
1535
1719
  };
1536
-
1537
- // src/hooks.ts
1538
- var WebhookHooks = class {
1539
- constructor(url) {
1540
- this.url = url;
1541
- }
1542
- async send(event, data) {
1543
- try {
1544
- const body = JSON.stringify({ event, ...data, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, (key, value) => {
1545
- if (typeof value === "object" && value !== null) {
1546
- const seen = /* @__PURE__ */ new WeakSet();
1547
- return JSON.parse(JSON.stringify(value, (k, v) => {
1548
- if (typeof v === "object" && v !== null) {
1549
- if (seen.has(v)) return "[Circular]";
1550
- seen.add(v);
1551
- }
1552
- return v;
1553
- }));
1554
- }
1555
- return value;
1556
- });
1557
- await fetch(this.url, {
1558
- method: "POST",
1559
- headers: { "Content-Type": "application/json" },
1560
- body
1561
- });
1562
- } catch {
1563
- }
1564
- }
1565
- async onMachineStart(context) {
1566
- await this.send("machine_start", { context });
1567
- return context;
1568
- }
1569
- async onMachineEnd(context, output) {
1570
- await this.send("machine_end", { context, output });
1571
- return output;
1572
- }
1573
- async onStateEnter(state, context) {
1574
- await this.send("state_enter", { state, context });
1575
- return context;
1576
- }
1577
- async onStateExit(state, context, output) {
1578
- await this.send("state_exit", { state, context, output });
1579
- return output;
1580
- }
1581
- async onAction(action, context) {
1582
- await this.send("action", { action, context });
1583
- return context;
1584
- }
1585
- };
1586
- var CompositeHooks = class {
1587
- constructor(hooks) {
1588
- this.hooks = hooks;
1589
- }
1590
- async onMachineStart(context) {
1591
- let result = context;
1592
- for (const hook of this.hooks) {
1593
- if (hook.onMachineStart) {
1594
- try {
1595
- result = await hook.onMachineStart(result);
1596
- } catch {
1597
- }
1598
- }
1599
- }
1600
- return result;
1601
- }
1602
- async onMachineEnd(context, output) {
1603
- let result = output;
1604
- for (const hook of this.hooks) {
1605
- if (hook.onMachineEnd) {
1606
- try {
1607
- result = await hook.onMachineEnd(context, result);
1608
- } catch {
1609
- }
1610
- }
1611
- }
1612
- return result;
1613
- }
1614
- async onStateEnter(state, context) {
1615
- let result = context;
1616
- for (const hook of this.hooks) {
1617
- if (hook.onStateEnter) {
1618
- try {
1619
- result = await hook.onStateEnter(state, result);
1620
- } catch {
1621
- }
1622
- }
1623
- }
1624
- return result;
1625
- }
1626
- async onStateExit(state, context, output) {
1627
- let result = output;
1628
- for (const hook of this.hooks) {
1629
- if (hook.onStateExit) {
1630
- try {
1631
- result = await hook.onStateExit(state, context, result);
1632
- } catch {
1633
- }
1634
- }
1635
- }
1636
- return result;
1637
- }
1638
- async onTransition(from, to, context) {
1639
- let result = to;
1640
- for (const hook of this.hooks) {
1641
- if (hook.onTransition) {
1642
- try {
1643
- result = await hook.onTransition(from, result, context);
1644
- } catch {
1645
- }
1646
- }
1647
- }
1648
- return result;
1649
- }
1650
- async onError(state, error, context) {
1651
- let result = null;
1652
- for (const hook of this.hooks) {
1653
- if (hook.onError) {
1654
- try {
1655
- const hookResult = await hook.onError(state, error, context);
1656
- if (hookResult !== null) result = hookResult;
1657
- } catch {
1658
- }
1659
- }
1660
- }
1661
- return result;
1662
- }
1663
- async onAction(action, context) {
1664
- let result = context;
1665
- for (const hook of this.hooks) {
1666
- if (hook.onAction) {
1667
- try {
1668
- result = await hook.onAction(action, result);
1669
- } catch {
1670
- }
1671
- }
1672
- }
1673
- return result;
1674
- }
1675
- };
1676
1720
  // Annotate the CommonJS export names for ESM import in node:
1677
1721
  0 && (module.exports = {
1678
1722
  CheckpointManager,
@@ -1680,6 +1724,7 @@ var CompositeHooks = class {
1680
1724
  DefaultExecution,
1681
1725
  FlatAgent,
1682
1726
  FlatMachine,
1727
+ HooksRegistry,
1683
1728
  LocalFileBackend,
1684
1729
  LocalFileLock,
1685
1730
  MCPToolProvider,