reactive-fsm 1.1.0 → 1.2.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/README.md CHANGED
@@ -1,27 +1,45 @@
1
1
  # reactive-fsm
2
2
 
3
- Reactive Finite State Machine for AI agent orchestration. Zero-dependency core with conditional tool gating, loop shield, and adapters for Vercel AI SDK and OpenAI SDK.
3
+ [![npm](https://img.shields.io/npm/v/reactive-fsm)](https://www.npmjs.com/package/reactive-fsm)
4
+ [![license](https://img.shields.io/npm/l/reactive-fsm)](https://github.com/roddcode/reactive-fsm/blob/main/LICENSE)
5
+
6
+ **Deterministic control for AI agents with tool calling.** Define what tools an LLM can call at each step of a conversation. Zero dependencies. TypeScript.
7
+
8
+ ```mermaid
9
+ stateDiagram-v2
10
+ GREETING: GREETING<br/><i>greet, show_services</i>
11
+ IDENTITY: IDENTITY<br/><i>collect_name, validate_dni</i>
12
+ BOOKING: BOOKING<br/><i>check_slots, reserve</i>
13
+ PAYMENT: PAYMENT<br/><i>process_payment</i>
14
+ CONFIRMED: CONFIRMED<br/><i>send_receipt</i>
15
+ [*] --> GREETING
16
+ [*] --> IDENTITY
17
+ [*] --> BOOKING
18
+ [*] --> PAYMENT
19
+ [*] --> CONFIRMED
20
+ GREETING --> [*]
21
+ IDENTITY --> [*]
22
+ BOOKING --> [*]
23
+ PAYMENT --> [*]
24
+ CONFIRMED --> [*]
25
+ ```
26
+
27
+ > Generated by `fsm.toMermaid()`. Paste into any README, PR, or Notion.
28
+
29
+ ## What it does
30
+
31
+ - **Tool gating per state.** In `PAYMENT`, the LLM sees only `process_payment` and `cancel_order`. No browsing, no upselling, no hallucinating tools that don't belong.
32
+ - **Loop shield.** Detects when the LLM enters a tool-calling loop and forces it to stop. Configurable threshold. Prevents runaway token costs.
33
+ - **Gate refresh mid-turn.** If a tool execution changes the conversation state (e.g., identity validated → move to booking), tools update immediately without restarting the FSM.
34
+ - **Cross-session resume.** `snapshot()` returns `{ state }`. Store it anywhere. Next serverless invocation picks up where it left off.
35
+ - **5 providers, 1 API.** Vercel AI SDK, OpenAI, Anthropic, LangChain, Google Gemini. Same FSM config, different adapter.
36
+
37
+ ## Get started
4
38
 
5
39
  ```bash
6
40
  pnpm add reactive-fsm
7
- # o npm install reactive-fsm
8
41
  ```
9
42
 
10
- ## Concepts
11
-
12
- | Concept | Role |
13
- |---------|------|
14
- | **FSM** | Manages states and which tool names are allowed at each state |
15
- | **Tool Gating** | Registry of tool entries with runtime conditions and builders, filtered by gate |
16
- | **Loop Shield** | Detects consecutive tool-call loops and signals the adapter to force-stop |
17
- | **Adapters** | Translate the FSM into SDK-native format (Vercel AI SDK, OpenAI) |
18
-
19
- ---
20
-
21
- ## Quick Start
22
-
23
- ### 1. Define your FSM
24
-
25
43
  ```typescript
26
44
  import { createFSM } from 'reactive-fsm'
27
45
 
@@ -38,394 +56,182 @@ const fsm = createFSM({
38
56
 
39
57
  fsm.currentState // 'IDENTITY'
40
58
  fsm.allowedTools // ['validate_dni']
59
+
41
60
  fsm.transitionTo('BOOKING')
42
61
  fsm.allowedTools // ['check_availability', 'reserve_slot']
43
62
  ```
44
63
 
45
- ### 2. Build a tool registry
46
-
47
- ```typescript
48
- import type { ToolEntry } from 'reactive-fsm'
49
-
50
- interface AppCtx { contactId: string; role: 'user' | 'admin' }
51
-
52
- const registry: ToolEntry<AppCtx>[] = [
53
- {
54
- name: 'validate_dni',
55
- gates: ['IDENTITY'],
56
- build: (ctx) => ({
57
- description: `Validate DNI for ${ctx.contactId}`,
58
- parameters: {},
59
- execute: async () => ({ valid: true }),
60
- }),
61
- },
62
- {
63
- name: 'check_availability',
64
- gates: ['BOOKING'],
65
- build: () => ({
66
- description: 'Check slot availability',
67
- parameters: {},
68
- execute: async () => ({ slots: ['10:00', '11:00'] }),
69
- }),
70
- },
71
- {
72
- name: 'reserve_slot',
73
- gates: ['BOOKING'],
74
- condition: (ctx) => ctx.contactId !== '',
75
- build: () => ({
76
- description: 'Reserve a time slot',
77
- parameters: {},
78
- execute: async () => ({ booked: true }),
79
- }),
80
- },
81
- {
82
- name: 'admin_panel',
83
- gates: ['IDENTITY', 'BOOKING', 'PAYMENT'],
84
- condition: (ctx) => ctx.role === 'admin',
85
- build: () => ({
86
- description: 'Access admin controls',
87
- parameters: {},
88
- execute: async () => ({ panel: 'open' }),
89
- }),
90
- },
91
- ]
92
- ```
93
-
94
- ### 3. Use an adapter
95
-
96
- #### Vercel AI SDK
64
+ ### With Vercel AI SDK (3 lines)
97
65
 
98
66
  ```typescript
99
67
  import { createVercelAdapter } from 'reactive-fsm/adapters/vercel-ai'
100
- import { generateText } from 'ai'
101
- import { openai } from '@ai-sdk/openai'
102
68
 
103
- const adapter = createVercelAdapter(fsm, registry, appCtx)
69
+ const adapter = createVercelAdapter(fsm, registry, context)
104
70
 
105
71
  const response = await generateText({
106
72
  model: openai('gpt-4o'),
107
- messages: [{ role: 'user', content: 'Quiero agendar una cita' }],
108
- ...adapter.inject(), // { tools, prepareStep }
73
+ messages,
74
+ ...adapter.inject(), // { tools, prepareStep }
109
75
  })
110
76
  ```
111
77
 
112
- #### OpenAI SDK
78
+ ### With OpenAI (imperative loop)
113
79
 
114
80
  ```typescript
115
81
  import { createOpenAIAdapter } from 'reactive-fsm/adapters/openai'
116
- import OpenAI from 'openai'
117
82
 
118
- const openai = new OpenAI()
119
- const adapter = createOpenAIAdapter(fsm, registry, appCtx)
120
-
121
- const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
122
- { role: 'user', content: 'Quiero agendar una cita' },
123
- ]
83
+ const adapter = createOpenAIAdapter(fsm, registry, context)
124
84
 
125
85
  while (true) {
126
86
  const { tools } = adapter.inject()
127
-
128
87
  const response = await openai.chat.completions.create({
129
- model: 'gpt-4o',
130
- messages,
131
- tools,
88
+ model: 'gpt-4o', messages, tools,
132
89
  tool_choice: adapter.isLooping() ? 'none' : 'auto',
133
90
  })
134
-
135
- const msg = response.choices[0].message
136
- messages.push(msg)
137
-
138
- if (msg.tool_calls?.length) {
139
- adapter.registerToolCall()
140
-
141
- // Execute tools, push results to messages
142
- for (const tc of msg.tool_calls) {
143
- const result = await executeTool(tc.function.name, tc.function.arguments)
144
- messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result) })
145
- }
146
-
147
- // If a tool mutated the DB state, refresh the gate
148
- // adapter.refreshGate('BOOKING')
149
- continue
150
- }
151
-
152
- console.log(msg.content) // final response
153
- break
154
- }
155
- ```
156
-
157
- #### Anthropic SDK
158
-
159
- ```typescript
160
- import { createAnthropicAdapter } from 'reactive-fsm/adapters/anthropic'
161
- import Anthropic from '@anthropic-ai/sdk'
162
-
163
- const anthropic = new Anthropic()
164
- const adapter = createAnthropicAdapter(fsm, registry, appCtx)
165
-
166
- const messages = [{ role: 'user' as const, content: 'Quiero agendar una cita' }]
167
-
168
- while (true) {
169
- const { tools } = adapter.inject()
170
-
171
- const response = await anthropic.messages.create({
172
- model: 'claude-sonnet-4-20250514',
173
- max_tokens: 1024,
174
- messages,
175
- tools,
176
- })
177
-
178
- const toolUses = response.content.filter(c => c.type === 'tool_use')
179
- messages.push({ role: 'assistant', content: response.content })
180
-
181
- if (toolUses.length > 0) {
182
- adapter.registerToolCall()
183
- if (adapter.isLooping()) break
184
-
185
- const toolResults = toolUses.map(tc => ({
186
- type: 'tool_result' as const,
187
- tool_use_id: tc.id,
188
- content: JSON.stringify(await executeTool(tc.name, tc.input)),
189
- }))
190
- messages.push({ role: 'user', content: toolResults })
191
- continue
192
- }
193
-
194
- return response.content.find(c => c.type === 'text')?.text
195
- }
196
- ```
197
-
198
- #### LangChain
199
-
200
- ```typescript
201
- import { wrapWithFSM } from 'reactive-fsm/adapters/langchain'
202
-
203
- const wrapper = wrapWithFSM(fsm, registry, appCtx)
204
- // wrapper.getTools() returns available tools for current gate
205
- // wrapper.shouldStop() — check before each agent step
206
- // wrapper.onToolCall() — call after each tool invocation
207
- // wrapper.onStateChange('BOOKING') — refresh gate mid-flow
208
-
209
- // Integrate with your existing LangChain executor:
210
- const currentTools = Object.values(wrapper.getTools())
211
- // …pass to your agent, re-fetch tools each iteration
212
- ```
213
-
214
- #### Google Gemini
215
-
216
- ```typescript
217
- import { createGeminiAdapter } from 'reactive-fsm/adapters/gemini'
218
- import { GoogleGenerativeAI } from '@google/generative-ai'
219
-
220
- const genAI = new GoogleGenerativeAI(apiKey)
221
- const adapter = createGeminiAdapter(fsm, registry, appCtx)
222
- const model = genAI.getGenerativeModel({
223
- model: 'gemini-2.0-flash',
224
- tools: adapter.inject().tools,
225
- })
226
-
227
- const chat = model.startChat()
228
-
229
- while (true) {
230
- const { tools } = adapter.inject()
231
- const result = await chat.sendMessage(userMessage)
232
- const calls = result.response.functionCalls()
233
-
234
- if (calls?.length) {
91
+ if (response.choices[0].message.tool_calls?.length) {
235
92
  adapter.registerToolCall()
236
- if (adapter.isLooping()) break
237
-
238
- const parts = calls.map(call => ({
239
- functionResponse: { name: call.name, response: executeTool(call.name, call.args) },
240
- }))
241
- await chat.sendMessage(parts)
242
93
  continue
243
94
  }
244
-
245
- return result.response.text()
95
+ return response.choices[0].message.content
246
96
  }
247
97
  ```
248
98
 
249
- ---
250
-
251
- ## Real-World Patterns
99
+ ## Real-world use cases
252
100
 
253
- ### Booking Funnel (3 gates)
101
+ ### Booking funnel
254
102
 
255
103
  ```typescript
256
- const bookingFSM = createFSM({
104
+ const booking = createFSM({
257
105
  initialState: 'GREETING',
258
106
  states: ['GREETING', 'IDENTITY', 'SCHEDULING', 'CONFIRMED'],
259
107
  tools: {
260
- GREETING: ['greet', 'show_catalog'],
261
- IDENTITY: ['collect_name', 'collect_dni', 'show_catalog'],
262
- SCHEDULING: ['check_slots', 'book_appointment'],
263
- CONFIRMED: ['send_confirmation'],
108
+ GREETING: ['greet', 'show_services'],
109
+ IDENTITY: ['collect_name', 'collect_contact'],
110
+ SCHEDULING: ['check_slots', 'reserve_appointment'],
111
+ CONFIRMED: ['send_confirmation', 'add_to_calendar'],
112
+ },
113
+ prompts: {
114
+ SCHEDULING: 'Offer available slots. Do not ask for identity again.',
264
115
  },
265
116
  loopShield: { enabled: true, maxConsecutiveTools: 3 },
266
117
  })
267
-
268
- // Flow: GREETING → user shows interest → IDENTITY → collects data → SCHEDULING → booked → CONFIRMED
269
118
  ```
270
119
 
271
- ### Payment Gate (block everything except payment)
120
+ In `SCHEDULING`, `collect_name` is gone. The LLM cannot loop back asking for data it already has.
121
+
122
+ ### Payment gate
272
123
 
273
124
  ```typescript
274
- const paymentFSM = createFSM({
275
- initialState: 'SHOPPING',
276
- states: ['SHOPPING', 'PAYMENT', 'CONFIRMED'],
125
+ const checkout = createFSM({
126
+ initialState: 'BROWSING',
127
+ states: ['BROWSING', 'CART', 'PAYMENT', 'CONFIRMED'],
277
128
  tools: {
278
- SHOPPING: ['browse', 'add_to_cart', 'checkout'],
279
- PAYMENT: ['process_payment', 'cancel_order'], // only these two
129
+ BROWSING: ['search', 'add_to_cart'],
130
+ CART: ['view_cart', 'apply_coupon', 'checkout'],
131
+ PAYMENT: ['process_payment', 'cancel_order'],
280
132
  CONFIRMED: ['send_receipt'],
281
133
  },
134
+ guard: (from, to) => to === 'PAYMENT' && from !== 'CART' ? false : true,
282
135
  })
283
-
284
- // When entering PAYMENT, the adapter removes all shopping tools.
285
- // The LLM can ONLY call process_payment or cancel_order.
286
136
  ```
287
137
 
288
- ### Conditional Tool: Admin vs User
138
+ In `PAYMENT`: 2 tools. The LLM cannot browse, cannot suggest alternatives, cannot negotiate. The guard blocks jumping from BROWSING straight to PAYMENT.
139
+
140
+ ### Customer support with forced escalation
289
141
 
290
142
  ```typescript
291
- const registry: ToolEntry<{ role: 'user' | 'admin' }>[] = [
292
- { name: 'view_profile', gates: ['DASHBOARD'], build: () => ({ /* ... */ }) },
293
- {
294
- name: 'delete_user',
295
- gates: ['DASHBOARD'],
296
- condition: (ctx) => ctx.role === 'admin',
297
- build: () => ({ /* ... */ }),
143
+ const support = createFSM({
144
+ initialState: 'TRIAGE',
145
+ states: ['TRIAGE', 'DIAGNOSIS', 'SOLUTION', 'ESCALATION'],
146
+ tools: {
147
+ TRIAGE: ['classify_issue', 'verify_account'],
148
+ DIAGNOSIS: ['search_kb', 'check_status'],
149
+ SOLUTION: ['apply_fix', 'send_guide'],
150
+ ESCALATION: ['transfer_to_human'],
298
151
  },
299
- ]
300
-
301
- // Regular user → only view_profile
302
- // Admin → view_profile + delete_user
152
+ loopShield: { enabled: true, maxConsecutiveTools: 3 },
153
+ })
303
154
  ```
304
155
 
305
- ---
156
+ Loop shield triggers after 3 failed fix attempts. `ESCALATION` has exactly one tool. The bot cannot stay in the loop — it must escalate.
157
+
158
+ ## Adapters
159
+
160
+ | Provider | Import | Loop shield |
161
+ |----------|--------|-------------|
162
+ | Vercel AI SDK | `reactive-fsm/adapters/vercel-ai` | Automatic via `prepareStep` |
163
+ | OpenAI | `reactive-fsm/adapters/openai` | Manual via `isLooping()` |
164
+ | Anthropic | `reactive-fsm/adapters/anthropic` | Manual via `isLooping()` |
165
+ | LangChain | `reactive-fsm/adapters/langchain` | Manual via `shouldStop()` |
166
+ | Google Gemini | `reactive-fsm/adapters/gemini` | Manual via `isLooping()` |
167
+
168
+ All adapters share the same pattern: build an FSM once, pass it to any adapter, call `inject()` to get provider-formatted tools.
306
169
 
307
- ## API Reference
170
+ ## vs LangGraph
308
171
 
309
- ### `createFSM(config)`
172
+ | | reactive-fsm | LangGraph |
173
+ |---|---|---|
174
+ | Dependencies | 0 (core) | LangChain + sub-dependencies |
175
+ | Bundle size | ~7 KB | ~5 MB |
176
+ | Define a 3-state FSM | 5 lines | 30+ lines |
177
+ | Tool gating per state | Declarative (`tools: { ... }`) | Manual (`route_tools(state)`) |
178
+ | Loop shield | Built-in | Not built-in |
179
+ | Gate refresh mid-turn | Automatic | Manual |
180
+ | Providers | Any (5 adapters) | LangChain ecosystem |
181
+
182
+ [Full migration guide →](#) _(coming soon)_
183
+
184
+ ## API
310
185
 
311
186
  ```typescript
312
187
  interface FSMConfig {
313
188
  initialState: string
314
189
  states: string[]
315
- tools: Record<string, string[]> // state → tool names
316
- loopShield?: LoopShieldConfig
190
+ tools: Record<string, string[]>
191
+ loopShield?: { enabled: boolean; maxConsecutiveTools: number }
192
+ onTransition?: (from: string, to: string) => void
193
+ guard?: (from: string, to: string) => boolean // veto transitions
194
+ prompts?: Record<string, string> // state → system prompt
195
+ snapshot?: { state: string } // restore from saved state
317
196
  }
318
197
 
319
- interface FSMInstance {
320
- readonly currentState: string
321
- readonly allowedTools: string[]
322
- readonly isLooping: boolean
323
- transitionTo(state: string): void
198
+ interface FSMPublic {
199
+ currentState: string // getter
200
+ allowedTools: string[] // getter
201
+ isLooping: boolean // getter
202
+ transitionTo(state): void
203
+ snapshot(): { state: string }
204
+ toMermaid(): string // Mermaid stateDiagram-v2
324
205
  }
325
206
  ```
326
207
 
327
- ### `buildToolsForGate(gate, ctx, registry)`
328
-
329
208
  ```typescript
330
209
  interface ToolEntry<TContext = any> {
331
210
  name: string
332
211
  gates: string[]
333
- condition?: (ctx: TContext) => boolean // optional runtime filter
334
- build: (ctx: TContext) => unknown // returns the tool definition
335
- }
336
-
337
- function buildToolsForGate<TContext>(
338
- gate: string,
339
- ctx: TContext,
340
- registry: ToolEntry<TContext>[]
341
- ): Record<string, unknown>
342
- ```
343
-
344
- ### `createVercelAdapter(fsm, registry, context)`
345
-
346
- ```typescript
347
- const adapter = createVercelAdapter(fsm, registry, context)
348
-
349
- const { tools, prepareStep } = adapter.inject()
350
- adapter.refreshGate('PAYMENT') // external gate change (e.g., after DB mutation)
351
- ```
352
-
353
- `prepareStep` automatically handles loop shield detection by counting consecutive tool steps from the history. When `maxConsecutiveTools` is exceeded, it returns `{ toolChoice: 'none' }`.
354
-
355
- ### `createOpenAIAdapter(fsm, registry, context)`
356
-
357
- ```typescript
358
- const adapter = createOpenAIAdapter(fsm, registry, context)
359
-
360
- const { tools } = adapter.inject() // → { tools: OpenAI-formatted array }
361
- adapter.isLooping() // → boolean (check before each API call)
362
- adapter.registerToolCall() // → track tool call for loop shield
363
- adapter.resetLoopShield() // → reset counter
364
- adapter.refreshGate('PAYMENT') // → external gate change
365
- ```
366
-
367
- Since the OpenAI SDK is stateless, the consumer controls the orchestration loop. Use `isLooping()` to decide `tool_choice` and `registerToolCall()` after each API call that invoked tools.
368
-
369
- ### `createLoopShield(config)`
370
-
371
- ```typescript
372
- interface LoopShieldConfig {
373
- enabled: boolean
374
- maxConsecutiveTools: number
212
+ condition?: (ctx: TContext) => boolean
213
+ build: (ctx: TContext) => unknown
214
+ schema?: unknown // Zod/Valibot/ArkType schema
375
215
  }
376
216
 
377
- const shield = createLoopShield({ enabled: true, maxConsecutiveTools: 3 })
378
- shield.registerToolCall()
379
- shield.isLooping() // boolean
380
- shield.reset()
217
+ buildToolsForGate(gate, ctx, registry): Record<string, unknown>
218
+ validateWith(schema, args): { ok, data } | { ok, error }
381
219
  ```
382
220
 
383
- ---
384
-
385
- ## Why reactive-fsm
386
-
387
- | | reactive-fsm | LangGraph | Manual |
388
- |---|---|---|---|
389
- | Dependencies | **0** (core) | LangChain + several | None |
390
- | Bundle size | **~7 KB** | ~5 MB | — |
391
- | Setup | **3 lines** | 30+ lines | 50+ lines |
392
- | Loop shield | **Built-in** | None | Manual |
393
- | Tool gating | **Per-state** | Manual filter | Manual |
394
- | Gate refresh | **Automatic** | None | None |
395
- | Provider | **Any** | LangChain-only | Any |
396
- | TypeScript | **100% typed** | Partial | Varies |
397
-
398
- ---
399
-
400
221
  ## Architecture
401
222
 
402
223
  ```
403
- src/
404
- ├── core/
405
- │ ├── machine.ts # createFSM() — state management
406
- │ ├── tool-gating.ts # buildToolsForGate() — conditional tool injection
407
- │ └── loop-shield.ts # createLoopShield() — cognitive loop detection
408
- ├── adapters/
409
- │ ├── vercel-ai.ts # createVercelAdapter() — Vercel AI SDK
410
- │ └── openai.ts # createOpenAIAdapter() — OpenAI SDK
411
- └── index.ts
412
- ```
224
+ src/core/ — zero npm dependencies
225
+ machine.ts createFSM()
226
+ tool-gating.ts buildToolsForGate(), validateWith()
227
+ loop-shield.ts createLoopShield()
413
228
 
414
- - **Core has zero npm dependencies.** Only TypeScript types.
415
- - **Adapters are optional.** `ai` and `openai` are optional peerDependencies.
416
- - **`ToolEntry<TContext>`** is fully generic — you define your own context shape.
417
-
418
- ---
419
-
420
- ## Loop Shield
421
-
422
- The loop shield prevents LLMs from entering infinite tool-calling loops.
423
-
424
- - **Vercel adapter**: `prepareStep` counts consecutive tool steps from history and forces `toolChoice: 'none'` when `maxConsecutiveTools` is exceeded.
425
- - **OpenAI adapter**: the consumer checks `isLooping()` before each API call and sets `tool_choice: 'none'` when it returns `true`. Calls `registerToolCall()` after each tool-invoking response.
426
-
427
- ---
428
-
429
- ## License
229
+ src/adapters/ — optional peer dependencies
230
+ vercel-ai.ts createVercelAdapter()
231
+ openai.ts createOpenAIAdapter()
232
+ anthropic.ts createAnthropicAdapter()
233
+ langchain.ts wrapWithFSM()
234
+ gemini.ts createGeminiAdapter()
235
+ ```
430
236
 
431
- MIT
237
+ MIT · [GitHub](https://github.com/roddcode/reactive-fsm) · [Issues](https://github.com/roddcode/reactive-fsm/issues)
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa,CAAC,QAAQ,GAAG,GAAG;IAC3C,MAAM,IAAI,kBAAkB,CAAC;IAC7B,SAAS,IAAI,OAAO,CAAC;IACrB,gBAAgB,IAAI,IAAI,CAAC;IACzB,eAAe,IAAI,IAAI,CAAC;IACxB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,GAAG,EAChD,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAC/B,OAAO,EAAE,QAAQ,GAChB,aAAa,CAAC,QAAQ,CAAC,CAwBzB"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa,CAAC,QAAQ,GAAG,GAAG;IAC3C,MAAM,IAAI,kBAAkB,CAAC;IAC7B,SAAS,IAAI,OAAO,CAAC;IACrB,gBAAgB,IAAI,IAAI,CAAC;IACzB,eAAe,IAAI,IAAI,CAAC;IACxB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,GAAG,EAChD,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAC/B,OAAO,EAAE,QAAQ,GAChB,aAAa,CAAC,QAAQ,CAAC,CAyBzB"}
@@ -17,6 +17,7 @@ export function createOpenAIAdapter(fsm, registry, context) {
17
17
  },
18
18
  refreshGate(state) {
19
19
  _fsm._setState(state);
20
+ _fsm._resetLoopShield();
20
21
  },
21
22
  };
22
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,qBAAqB,CAAC;AAcxE,MAAM,UAAU,mBAAmB,CACjC,GAAc,EACd,QAA+B,EAC/B,OAAiB;IAEjB,MAAM,IAAI,GAAG,GAAkB,CAAC;IAChC,OAAO;QACL,MAAM;YACJ,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YACxE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,CAAC;QAED,SAAS;YACP,OAAO,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;QAED,gBAAgB;YACd,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;QAED,eAAe;YACb,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,qBAAqB,CAAC;AAcxE,MAAM,UAAU,mBAAmB,CACjC,GAAc,EACd,QAA+B,EAC/B,OAAiB;IAEjB,MAAM,IAAI,GAAG,GAAkB,CAAC;IAChC,OAAO;QACL,MAAM;YACJ,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YACxE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,CAAC;QAED,SAAS;YACP,OAAO,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;QAED,gBAAgB;YACd,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;QAED,eAAe;YACb,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/adapters/vercel-ai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxD,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;CAC7G;AAED,MAAM,WAAW,aAAa,CAAC,QAAQ,GAAG,GAAG;IAC3C,MAAM,IAAI,kBAAkB,CAAC;IAC7B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAiBD,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,GAAG,EAChD,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAC/B,OAAO,EAAE,QAAQ,GAChB,aAAa,CAAC,QAAQ,CAAC,CA6CzB"}
1
+ {"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/adapters/vercel-ai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxD,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;CAC7G;AAED,MAAM,WAAW,aAAa,CAAC,QAAQ,GAAG,GAAG;IAC3C,MAAM,IAAI,kBAAkB,CAAC;IAC7B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAiBD,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,GAAG,EAChD,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAC/B,OAAO,EAAE,QAAQ,GAChB,aAAa,CAAC,QAAQ,CAAC,CA8CzB"}
@@ -45,6 +45,7 @@ export function createVercelAdapter(fsm, registry, context) {
45
45
  },
46
46
  refreshGate(state) {
47
47
  _fsm._setState(state);
48
+ _fsm._resetLoopShield();
48
49
  },
49
50
  };
50
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vercel-ai.js","sourceRoot":"","sources":["../../src/adapters/vercel-ai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,qBAAqB,CAAC;AAsBxE,SAAS,SAAS,CAChB,MAA+B,EAC/B,IAAY,EACZ,GAAY,EACZ,QAAqB;IAErB,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,GAAc,EACd,QAA+B,EAC/B,OAAiB;IAEjB,MAAM,IAAI,GAAG,GAAkB,CAAC;IAChC,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,IAAI,WAAW,GAAkB,IAAI,CAAC;IAEtC,OAAO;QACL,MAAM;YACJ,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;YAChC,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YAEjD,OAAO;gBACL,KAAK;gBACL,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,EAA2B,EAAE,EAAE;oBACxD,IAAI,oBAAoB,GAAG,CAAC,CAAC;oBAC7B,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC3C,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;4BAC1C,oBAAoB,EAAE,CAAC;wBACzB,CAAC;6BAAM,CAAC;4BACN,MAAM;wBACR,CAAC;oBACH,CAAC;oBAED,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,oBAAoB,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC9C,IAAI,CAAC,iBAAiB,EAAE,CAAC;oBAC3B,CAAC;oBAED,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;wBACnB,OAAO,EAAE,UAAU,EAAE,MAAe,EAAE,CAAC;oBACzC,CAAC;oBAED,IAAI,IAAI,CAAC,YAAY,KAAK,WAAW,EAAE,CAAC;wBACtC,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;wBAChC,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;oBACnD,CAAC;oBAED,OAAO,EAAE,CAAC;gBACZ,CAAC;aACF,CAAC;QACJ,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"vercel-ai.js","sourceRoot":"","sources":["../../src/adapters/vercel-ai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,qBAAqB,CAAC;AAsBxE,SAAS,SAAS,CAChB,MAA+B,EAC/B,IAAY,EACZ,GAAY,EACZ,QAAqB;IAErB,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,GAAc,EACd,QAA+B,EAC/B,OAAiB;IAEjB,MAAM,IAAI,GAAG,GAAkB,CAAC;IAChC,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,IAAI,WAAW,GAAkB,IAAI,CAAC;IAEtC,OAAO;QACL,MAAM;YACJ,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;YAChC,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YAEjD,OAAO;gBACL,KAAK;gBACL,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,EAA2B,EAAE,EAAE;oBACxD,IAAI,oBAAoB,GAAG,CAAC,CAAC;oBAC7B,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC3C,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;4BAC1C,oBAAoB,EAAE,CAAC;wBACzB,CAAC;6BAAM,CAAC;4BACN,MAAM;wBACR,CAAC;oBACH,CAAC;oBAED,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,oBAAoB,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC9C,IAAI,CAAC,iBAAiB,EAAE,CAAC;oBAC3B,CAAC;oBAED,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;wBACnB,OAAO,EAAE,UAAU,EAAE,MAAe,EAAE,CAAC;oBACzC,CAAC;oBAED,IAAI,IAAI,CAAC,YAAY,KAAK,WAAW,EAAE,CAAC;wBACtC,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;wBAChC,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;oBACnD,CAAC;oBAED,OAAO,EAAE,CAAC;gBACZ,CAAC;aACF,CAAC;QACJ,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,15 +1,26 @@
1
1
  import { type LoopShieldConfig } from './loop-shield';
2
+ export interface FSMSnapshot {
3
+ state: string;
4
+ }
2
5
  export interface FSMConfig {
3
6
  initialState: string;
4
7
  states: string[];
5
8
  tools: Record<string, string[]>;
6
9
  loopShield?: LoopShieldConfig;
10
+ onTransition?: (from: string, to: string) => void;
11
+ guard?: (from: string, to: string) => boolean;
12
+ prompts?: Record<string, string>;
13
+ snapshot?: FSMSnapshot;
14
+ context?: Record<string, unknown>;
7
15
  }
8
16
  export interface FSMPublic {
9
17
  readonly currentState: string;
10
18
  readonly allowedTools: string[];
11
19
  readonly isLooping: boolean;
12
20
  transitionTo(state: string): void;
21
+ snapshot(): FSMSnapshot;
22
+ toMermaid(): string;
23
+ context: Record<string, unknown>;
13
24
  }
14
25
  export interface FSMInstance extends FSMPublic {
15
26
  _registerToolCall(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../src/core/machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAExE,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,iBAAiB,IAAI,IAAI,CAAC;IAC1B,gBAAgB,IAAI,IAAI,CAAC;IACzB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAoDxD"}
1
+ {"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../src/core/machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAExE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,IAAI,WAAW,CAAC;IACxB,SAAS,IAAI,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,iBAAiB,IAAI,IAAI,CAAC;IAC1B,gBAAgB,IAAI,IAAI,CAAC;IACzB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAuFxD"}
@@ -4,11 +4,23 @@ export function createFSM(config) {
4
4
  if (!validStates.has(config.initialState)) {
5
5
  throw new Error(`Initial state "${config.initialState}" is not in the states list`);
6
6
  }
7
- let _currentState = config.initialState;
7
+ if (config.snapshot && !validStates.has(config.snapshot.state)) {
8
+ throw new Error(`Snapshot state "${config.snapshot.state}" is not in the states list`);
9
+ }
10
+ let _currentState = config.snapshot?.state ?? config.initialState;
11
+ const _context = config.context ? { ...config.context } : {};
8
12
  const shield = createLoopShield(config.loopShield ?? { enabled: false, maxConsecutiveTools: 3 });
9
13
  function getAllowedTools() {
10
14
  return config.tools[_currentState] ?? [];
11
15
  }
16
+ function doTransition(state) {
17
+ const from = _currentState;
18
+ if (config.guard && !config.guard(from, state)) {
19
+ throw new Error(`Guard blocked transition from "${from}" to "${state}"`);
20
+ }
21
+ _currentState = state;
22
+ config.onTransition?.(from, state);
23
+ }
12
24
  return {
13
25
  get currentState() {
14
26
  return _currentState;
@@ -23,7 +35,7 @@ export function createFSM(config) {
23
35
  if (!validStates.has(state)) {
24
36
  throw new Error(`Invalid state "${state}". Valid states: ${config.states.join(', ')}`);
25
37
  }
26
- _currentState = state;
38
+ doTransition(state);
27
39
  },
28
40
  _registerToolCall() {
29
41
  shield.registerToolCall();
@@ -35,8 +47,25 @@ export function createFSM(config) {
35
47
  if (!validStates.has(state)) {
36
48
  throw new Error(`Invalid state "${state}". Valid states: ${config.states.join(', ')}`);
37
49
  }
38
- _currentState = state;
50
+ doTransition(state);
51
+ },
52
+ snapshot() {
53
+ return { state: _currentState };
54
+ },
55
+ toMermaid() {
56
+ const lines = ['stateDiagram-v2'];
57
+ for (const state of config.states) {
58
+ const tools = (config.tools[state] ?? []).join('<br/>');
59
+ const label = tools ? `${state}<br/><i>${tools}</i>` : state;
60
+ lines.push(` ${state}: ${label}`);
61
+ }
62
+ for (const state of config.states) {
63
+ lines.push(` [*] --> ${state}`);
64
+ lines.push(` ${state} --> [*]`);
65
+ }
66
+ return lines.join('\n');
39
67
  },
68
+ context: _context,
40
69
  };
41
70
  }
42
71
  //# sourceMappingURL=machine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/core/machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAyB,MAAM,eAAe,CAAC;AAsBxE,MAAM,UAAU,SAAS,CAAC,MAAiB;IACzC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAE3C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,CAAC,YAAY,6BAA6B,CAAC,CAAC;IACtF,CAAC;IAED,IAAI,aAAa,GAAG,MAAM,CAAC,YAAY,CAAC;IAExC,MAAM,MAAM,GAAG,gBAAgB,CAC7B,MAAM,CAAC,UAAU,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAE,CAChE,CAAC;IAEF,SAAS,eAAe;QACtB,OAAO,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;IAED,OAAO;QACL,IAAI,YAAY;YACd,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,IAAI,YAAY;YACd,OAAO,eAAe,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,SAAS;YACX,OAAO,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5B,CAAC;QAED,YAAY,CAAC,KAAa;YACxB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,oBAAoB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzF,CAAC;YACD,aAAa,GAAG,KAAK,CAAC;QACxB,CAAC;QAED,iBAAiB;YACf,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC5B,CAAC;QAED,gBAAgB;YACd,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QAED,SAAS,CAAC,KAAa;YACrB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,oBAAoB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzF,CAAC;YACD,aAAa,GAAG,KAAK,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/core/machine.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,14 @@ export interface ToolEntry<TContext = any> {
3
3
  gates: string[];
4
4
  condition?: (ctx: TContext) => boolean;
5
5
  build: (ctx: TContext) => unknown;
6
+ schema?: unknown;
6
7
  }
8
+ export declare function validateWith(schema: unknown, args: unknown): {
9
+ ok: true;
10
+ data: unknown;
11
+ } | {
12
+ ok: false;
13
+ error: string;
14
+ };
7
15
  export declare function buildToolsForGate<TContext = any>(gate: string, ctx: TContext, registry: ToolEntry<TContext>[]): Record<string, unknown>;
8
16
  //# sourceMappingURL=tool-gating.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-gating.d.ts","sourceRoot":"","sources":["../../src/core/tool-gating.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS,CAAC,QAAQ,GAAG,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC;IACvC,KAAK,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,GAAG,GAAG,EAC9C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,QAAQ,EACb,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
1
+ {"version":3,"file":"tool-gating.d.ts","sourceRoot":"","sources":["../../src/core/tool-gating.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS,CAAC,QAAQ,GAAG,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC;IACvC,KAAK,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAMD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,GACZ;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAa5D;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,GAAG,GAAG,EAC9C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,QAAQ,EACb,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
@@ -1,3 +1,17 @@
1
+ export function validateWith(schema, args) {
2
+ const s = schema;
3
+ if (!s || typeof s.safeParse !== 'function') {
4
+ return { ok: true, data: args };
5
+ }
6
+ const result = s.safeParse(args);
7
+ if (result.success) {
8
+ const data = 'data' in result ? result.data : 'output' in result ? result.output : args;
9
+ return { ok: true, data };
10
+ }
11
+ const issues = result.error?.issues ?? result.issues ?? [];
12
+ const error = issues.map((i) => i.message).join(', ') || 'Validation failed';
13
+ return { ok: false, error };
14
+ }
1
15
  export function buildToolsForGate(gate, ctx, registry) {
2
16
  return Object.fromEntries(registry
3
17
  .filter((entry) => entry.gates.includes(gate))
@@ -1 +1 @@
1
- {"version":3,"file":"tool-gating.js","sourceRoot":"","sources":["../../src/core/tool-gating.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,GAAa,EACb,QAA+B;IAE/B,OAAO,MAAM,CAAC,WAAW,CACvB,QAAQ;SACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;SAC7C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;SACxE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAClD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tool-gating.js","sourceRoot":"","sources":["../../src/core/tool-gating.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,YAAY,CAC1B,MAAe,EACf,IAAa;IAEb,MAAM,CAAC,GAAG,MAAsC,CAAC;IACjD,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC5C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAE,MAAkC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACrH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,mBAAmB,CAAC;IAClG,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,GAAa,EACb,QAA+B;IAE/B,OAAO,MAAM,CAAC,WAAW,CACvB,QAAQ;SACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;SAC7C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;SACxE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAClD,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createFSM as _createFSM } from './core/machine';
2
- import type { FSMConfig, FSMPublic } from './core/machine';
2
+ import type { FSMConfig, FSMPublic, FSMSnapshot } from './core/machine';
3
3
  export { _createFSM as createFSM };
4
- export type { FSMConfig, FSMPublic };
5
- export { buildToolsForGate } from './core/tool-gating';
4
+ export type { FSMConfig, FSMPublic, FSMSnapshot };
5
+ export { buildToolsForGate, validateWith } from './core/tool-gating';
6
6
  export type { ToolEntry } from './core/tool-gating';
7
7
  export { createLoopShield } from './core/loop-shield';
8
8
  export type { LoopShieldConfig, LoopShieldInstance } from './core/loop-shield';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,UAAU,IAAI,SAAS,EAAE,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EAAE,UAAU,IAAI,SAAS,EAAE,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createFSM as _createFSM } from './core/machine';
2
2
  export { _createFSM as createFSM };
3
- export { buildToolsForGate } from './core/tool-gating';
3
+ export { buildToolsForGate, validateWith } from './core/tool-gating';
4
4
  export { createLoopShield } from './core/loop-shield';
5
5
  export { createVercelAdapter } from './adapters/vercel-ai';
6
6
  export { createOpenAIAdapter } from './adapters/openai';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,EAAE,UAAU,IAAI,SAAS,EAAE,CAAC;AAGnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAQ3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAMxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAG9D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,EAAE,UAAU,IAAI,SAAS,EAAE,CAAC;AAGnC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAQ3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAMxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAG9D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reactive-fsm",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Reactive Finite State Machine for AI agent orchestration — zero dependencies core, conditional tool gating, loop shield, adapters for Vercel AI SDK, OpenAI, Anthropic, LangChain, and Google Gemini",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",