dialai 0.1.0 → 1.1.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/.claude/skills/dial-machine/SKILL.md +401 -0
- package/.claude/skills/dial-machine/references/api-reference.md +509 -0
- package/.claude/skills/dial-machine/references/patterns.md +628 -0
- package/.claude/skills/spec-for-ralph/SKILL.md +542 -0
- package/.claude/specs/llm-audit-log.md +280 -0
- package/LICENSE +21 -55
- package/README.md +32 -38
- package/dist/dialai/alignment.d.ts +51 -0
- package/dist/dialai/alignment.d.ts.map +1 -0
- package/dist/dialai/alignment.js +125 -0
- package/dist/dialai/alignment.js.map +1 -0
- package/dist/dialai/api.d.ts +131 -0
- package/dist/dialai/api.d.ts.map +1 -0
- package/dist/dialai/api.js +788 -0
- package/dist/dialai/api.js.map +1 -0
- package/dist/dialai/cli.d.ts +14 -0
- package/dist/dialai/cli.d.ts.map +1 -0
- package/dist/dialai/cli.js +83 -0
- package/dist/dialai/cli.js.map +1 -0
- package/dist/dialai/config.d.ts +42 -0
- package/dist/dialai/config.d.ts.map +1 -0
- package/dist/dialai/config.js +46 -0
- package/dist/dialai/config.js.map +1 -0
- package/dist/dialai/engine.d.ts +38 -0
- package/dist/dialai/engine.d.ts.map +1 -0
- package/dist/dialai/engine.js +276 -0
- package/dist/dialai/engine.js.map +1 -0
- package/dist/dialai/evaluation.d.ts +42 -0
- package/dist/dialai/evaluation.d.ts.map +1 -0
- package/dist/dialai/evaluation.js +106 -0
- package/dist/dialai/evaluation.js.map +1 -0
- package/dist/dialai/exemplars.d.ts +17 -0
- package/dist/dialai/exemplars.d.ts.map +1 -0
- package/dist/dialai/exemplars.js +33 -0
- package/dist/dialai/exemplars.js.map +1 -0
- package/dist/dialai/http-server.d.ts +11 -0
- package/dist/dialai/http-server.d.ts.map +1 -0
- package/dist/dialai/http-server.js +479 -0
- package/dist/dialai/http-server.js.map +1 -0
- package/dist/dialai/index.d.ts +22 -6
- package/dist/dialai/index.d.ts.map +1 -1
- package/dist/dialai/index.js +56 -6
- package/dist/dialai/index.js.map +1 -1
- package/dist/dialai/llm.d.ts +51 -0
- package/dist/dialai/llm.d.ts.map +1 -0
- package/dist/dialai/llm.js +213 -0
- package/dist/dialai/llm.js.map +1 -0
- package/dist/dialai/mcp.d.ts +16 -0
- package/dist/dialai/mcp.d.ts.map +1 -0
- package/dist/dialai/mcp.js +437 -0
- package/dist/dialai/mcp.js.map +1 -0
- package/dist/dialai/migrations/001-initial-schema.d.ts +8 -0
- package/dist/dialai/migrations/001-initial-schema.d.ts.map +1 -0
- package/dist/dialai/migrations/001-initial-schema.js +176 -0
- package/dist/dialai/migrations/001-initial-schema.js.map +1 -0
- package/dist/dialai/migrations/002-llm-audit-log.d.ts +8 -0
- package/dist/dialai/migrations/002-llm-audit-log.d.ts.map +1 -0
- package/dist/dialai/migrations/002-llm-audit-log.js +41 -0
- package/dist/dialai/migrations/002-llm-audit-log.js.map +1 -0
- package/dist/dialai/migrations/migrate.d.ts +11 -0
- package/dist/dialai/migrations/migrate.d.ts.map +1 -0
- package/dist/dialai/migrations/migrate.js +82 -0
- package/dist/dialai/migrations/migrate.js.map +1 -0
- package/dist/dialai/monitoring.d.ts +17 -0
- package/dist/dialai/monitoring.d.ts.map +1 -0
- package/dist/dialai/monitoring.js +173 -0
- package/dist/dialai/monitoring.js.map +1 -0
- package/dist/dialai/proxy-client.d.ts +23 -0
- package/dist/dialai/proxy-client.d.ts.map +1 -0
- package/dist/dialai/proxy-client.js +150 -0
- package/dist/dialai/proxy-client.js.map +1 -0
- package/dist/dialai/store-memory.d.ts +9 -0
- package/dist/dialai/store-memory.d.ts.map +1 -0
- package/dist/dialai/store-memory.js +135 -0
- package/dist/dialai/store-memory.js.map +1 -0
- package/dist/dialai/store-postgres.d.ts +9 -0
- package/dist/dialai/store-postgres.d.ts.map +1 -0
- package/dist/dialai/store-postgres.js +514 -0
- package/dist/dialai/store-postgres.js.map +1 -0
- package/dist/dialai/store.d.ts +46 -0
- package/dist/dialai/store.d.ts.map +1 -0
- package/dist/dialai/store.js +32 -0
- package/dist/dialai/store.js.map +1 -0
- package/dist/dialai/strategies.d.ts +43 -0
- package/dist/dialai/strategies.d.ts.map +1 -0
- package/dist/dialai/strategies.js +171 -0
- package/dist/dialai/strategies.js.map +1 -0
- package/dist/dialai/test-db.d.ts +15 -0
- package/dist/dialai/test-db.d.ts.map +1 -0
- package/dist/dialai/test-db.js +50 -0
- package/dist/dialai/test-db.js.map +1 -0
- package/dist/dialai/types.d.ts +610 -0
- package/dist/dialai/types.d.ts.map +1 -0
- package/dist/dialai/types.js +7 -0
- package/dist/dialai/types.js.map +1 -0
- package/dist/dialai/utils.d.ts +31 -0
- package/dist/dialai/utils.d.ts.map +1 -0
- package/dist/dialai/utils.js +89 -0
- package/dist/dialai/utils.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -7
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dial-machine
|
|
3
|
+
description: >
|
|
4
|
+
This skill should be used when the user asks to "implement a dial machine",
|
|
5
|
+
"create a dial workflow", "add dial to my project", "run a dial machine",
|
|
6
|
+
"set up dialai", "configure specialists", "build a state machine with AI and
|
|
7
|
+
human decision-makers", or mentions dialai, DIAL framework, progressive collapse,
|
|
8
|
+
proposers and arbiters, or alignment-based consensus in a consumer project.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# DIAL Machine Skill
|
|
12
|
+
|
|
13
|
+
DIAL (Dynamic Integration between AI and Labor) is a TypeScript framework for coordinating AI and human specialists making decisions within state machines. Specialists are either **proposers** (suggest state transitions) or **arbiters** (evaluate consensus among proposals). The framework tracks alignment scores between AI and human decisions, enabling progressive collapse — high-alignment specialists eventually run autonomously without human oversight.
|
|
14
|
+
|
|
15
|
+
For full API types and function signatures, see `references/api-reference.md`. For copy-paste-ready machine patterns, see `references/patterns.md`.
|
|
16
|
+
|
|
17
|
+
## Project Setup
|
|
18
|
+
|
|
19
|
+
1. Check if `dialai` is already in `package.json` dependencies. If not, install it:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install dialai
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. Confirm the project has `"type": "module"` in `package.json`. DIAL is ESM-only.
|
|
26
|
+
|
|
27
|
+
3. Import from the top-level package:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { runSession, createSession, registerProposer, registerArbiter } from "dialai";
|
|
31
|
+
import type { MachineDefinition, ProposerContext, ArbiterContext } from "dialai";
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Define a Machine
|
|
35
|
+
|
|
36
|
+
A `MachineDefinition` requires four fields:
|
|
37
|
+
|
|
38
|
+
| Field | Type | Description |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `machineName` | `string` | Unique identifier for this machine type |
|
|
41
|
+
| `initialState` | `string` | State where sessions start |
|
|
42
|
+
| `goalState` | `string` | Rest state where the session is headed (must have no transitions) |
|
|
43
|
+
| `states` | `Record<string, StateDefinition>` | Map of state names to definitions |
|
|
44
|
+
|
|
45
|
+
Optional fields: `specialists` (array of `SpecialistDefinition`), `consensusThreshold` (number, 0-1).
|
|
46
|
+
|
|
47
|
+
Each `StateDefinition` has:
|
|
48
|
+
- `prompt` (string) — the decision question for specialists in this state
|
|
49
|
+
- `transitions` (Record<string, string>) — map of transition names to target states
|
|
50
|
+
- `consensusThreshold` (number) — override threshold for this state
|
|
51
|
+
- `specialists` (SpecialistDefinition[]) — per-state specialist declarations
|
|
52
|
+
|
|
53
|
+
### JSON Example
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"machineName": "document-review",
|
|
58
|
+
"initialState": "draft",
|
|
59
|
+
"goalState": "published",
|
|
60
|
+
"states": {
|
|
61
|
+
"draft": {
|
|
62
|
+
"prompt": "Review the document. Approve or request changes?",
|
|
63
|
+
"transitions": {
|
|
64
|
+
"approve": "published",
|
|
65
|
+
"request_changes": "revision"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"revision": {
|
|
69
|
+
"prompt": "Changes made. Approve now?",
|
|
70
|
+
"transitions": {
|
|
71
|
+
"approve": "published",
|
|
72
|
+
"request_changes": "revision"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"published": {}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### TypeScript Example
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import type { MachineDefinition } from "dialai";
|
|
84
|
+
|
|
85
|
+
const machine: MachineDefinition = {
|
|
86
|
+
machineName: "document-review",
|
|
87
|
+
initialState: "draft",
|
|
88
|
+
goalState: "published",
|
|
89
|
+
states: {
|
|
90
|
+
draft: {
|
|
91
|
+
prompt: "Review the document. Approve or request changes?",
|
|
92
|
+
transitions: {
|
|
93
|
+
approve: "published",
|
|
94
|
+
request_changes: "revision",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
revision: {
|
|
98
|
+
prompt: "Changes made. Approve now?",
|
|
99
|
+
transitions: {
|
|
100
|
+
approve: "published",
|
|
101
|
+
request_changes: "revision",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
published: {},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Configure Specialists
|
|
110
|
+
|
|
111
|
+
Specialists have two roles:
|
|
112
|
+
- **Proposer** — suggests which transition to take (`role: "proposer"`)
|
|
113
|
+
- **Arbiter** — evaluates proposals and determines consensus (`role: "arbiter"`)
|
|
114
|
+
|
|
115
|
+
### Attachment Methods
|
|
116
|
+
|
|
117
|
+
**1. Machine-level JSON** — embedded in the machine definition:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"machineName": "my-machine",
|
|
122
|
+
"initialState": "start",
|
|
123
|
+
"goalState": "end",
|
|
124
|
+
"specialists": [
|
|
125
|
+
{ "role": "proposer", "specialistId": "ai-1", "strategyFnName": "firstAvailable" },
|
|
126
|
+
{ "role": "arbiter", "specialistId": "judge", "strategyFnName": "firstProposal" }
|
|
127
|
+
],
|
|
128
|
+
"states": {
|
|
129
|
+
"start": {
|
|
130
|
+
"prompt": "Proceed?",
|
|
131
|
+
"transitions": { "go": "end" }
|
|
132
|
+
},
|
|
133
|
+
"end": {}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**2. Per-state** — different specialists for different states:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"states": {
|
|
143
|
+
"triage": {
|
|
144
|
+
"prompt": "How should we handle this?",
|
|
145
|
+
"transitions": { "escalate": "review", "resolve": "done" },
|
|
146
|
+
"specialists": [
|
|
147
|
+
{ "role": "proposer", "specialistId": "triage-bot", "strategyFnName": "firstAvailable" },
|
|
148
|
+
{ "role": "arbiter", "specialistId": "triage-arbiter", "strategyFnName": "firstProposal" }
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"review": {
|
|
152
|
+
"prompt": "Senior review complete?",
|
|
153
|
+
"transitions": { "approve": "done" },
|
|
154
|
+
"specialists": [
|
|
155
|
+
{ "role": "proposer", "specialistId": "senior-bot", "strategyFnName": "firstAvailable" },
|
|
156
|
+
{ "role": "arbiter", "specialistId": "senior-arbiter", "strategyFnName": "alignmentMargin" }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**3. Programmatic** — registered via API calls:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { registerProposer, registerArbiter } from "dialai";
|
|
167
|
+
|
|
168
|
+
await registerProposer({
|
|
169
|
+
specialistId: "ai-reviewer",
|
|
170
|
+
machineName: "document-review",
|
|
171
|
+
strategyFnName: "firstAvailable",
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
await registerArbiter({
|
|
175
|
+
specialistId: "consensus-judge",
|
|
176
|
+
machineName: "document-review",
|
|
177
|
+
strategyFnName: "alignmentMargin",
|
|
178
|
+
threshold: 0.6,
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Built-in Strategies
|
|
183
|
+
|
|
184
|
+
| Strategy | Role | Behavior |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `firstAvailable` | proposer | Returns the first transition in the map |
|
|
187
|
+
| `lastAvailable` | proposer | Returns the last transition in the map |
|
|
188
|
+
| `random` | proposer | Returns a random transition |
|
|
189
|
+
| `firstProposal` | arbiter | Accepts the first proposal by timestamp |
|
|
190
|
+
| `alignmentMargin` | arbiter | Alignment-weighted margin consensus (see below) |
|
|
191
|
+
|
|
192
|
+
**`alignmentMargin` algorithm**: Groups proposals by transition name, scores each group by summing proposer alignment scores, computes `margin = (leader - runnerUp) / totalAlignment`. Consensus when `margin >= threshold`. Single proposal auto-approves. Cold start (all alignment = 0) requires human input.
|
|
193
|
+
|
|
194
|
+
### Execution Modes
|
|
195
|
+
|
|
196
|
+
Each specialist must have exactly one execution mode:
|
|
197
|
+
|
|
198
|
+
| Mode | Fields | Description |
|
|
199
|
+
|---|---|---|
|
|
200
|
+
| Built-in strategy | `strategyFnName` | Name of a built-in strategy |
|
|
201
|
+
| Custom function | `strategyFn` | Local async function |
|
|
202
|
+
| Webhook | `strategyWebhookUrl` + `webhookTokenName` | External HTTP endpoint |
|
|
203
|
+
| LLM (local context) | `contextFn` + `modelId` | You provide context, DIAL calls the LLM |
|
|
204
|
+
| LLM (webhook context) | `contextWebhookUrl` + `modelId` + `webhookTokenName` | Webhook provides context, DIAL calls the LLM |
|
|
205
|
+
|
|
206
|
+
Arbiters support the first three modes only (no LLM modes).
|
|
207
|
+
|
|
208
|
+
## Run the Machine
|
|
209
|
+
|
|
210
|
+
### Method 1: CLI
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npx dialai machine.json
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
This registers default specialists (`firstAvailable` proposer, `firstProposal` arbiter) if none are specified in the JSON, and runs the machine to completion.
|
|
217
|
+
|
|
218
|
+
### Method 2: `runSession()` One-Liner
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { runSession } from "dialai";
|
|
222
|
+
|
|
223
|
+
const session = await runSession(machine);
|
|
224
|
+
console.log(session.currentState); // goalState
|
|
225
|
+
console.log(session.history); // TransitionRecord[]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`runSession` creates a session, registers specialists from the machine definition (or defaults), and loops `tick()` until the session reaches `goalState` or needs human intervention.
|
|
229
|
+
|
|
230
|
+
### Method 3: Manual Decision Cycle
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import {
|
|
234
|
+
createSession,
|
|
235
|
+
getSession,
|
|
236
|
+
registerProposer,
|
|
237
|
+
registerArbiter,
|
|
238
|
+
submitProposal,
|
|
239
|
+
submitArbitration,
|
|
240
|
+
} from "dialai";
|
|
241
|
+
|
|
242
|
+
// 1. Create session
|
|
243
|
+
const session = await createSession(machine);
|
|
244
|
+
|
|
245
|
+
// 2. Register specialists
|
|
246
|
+
await registerProposer({
|
|
247
|
+
specialistId: "ai-1",
|
|
248
|
+
machineName: machine.machineName,
|
|
249
|
+
strategyFnName: "firstAvailable",
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
await registerArbiter({
|
|
253
|
+
specialistId: "arbiter-1",
|
|
254
|
+
machineName: machine.machineName,
|
|
255
|
+
strategyFnName: "firstProposal",
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// 3. Submit proposals (omit transitionName to invoke the registered strategy)
|
|
259
|
+
await submitProposal({
|
|
260
|
+
sessionId: session.sessionId,
|
|
261
|
+
specialistId: "ai-1",
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// 4. Submit arbitration (evaluates consensus, executes transition if reached)
|
|
265
|
+
const result = await submitArbitration({
|
|
266
|
+
sessionId: session.sessionId,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
console.log(result.executed); // true
|
|
270
|
+
console.log(result.toState); // next state
|
|
271
|
+
|
|
272
|
+
// 5. Fetch updated session
|
|
273
|
+
const updated = await getSession(session.sessionId);
|
|
274
|
+
console.log(updated.currentState);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Custom Strategy Functions
|
|
278
|
+
|
|
279
|
+
### Proposer `strategyFn`
|
|
280
|
+
|
|
281
|
+
Receives a `ProposerContext` and returns `{ transitionName, toState, reasoning }`:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
await registerProposer({
|
|
285
|
+
specialistId: "smart-proposer",
|
|
286
|
+
machineName: "my-machine",
|
|
287
|
+
strategyFn: async (ctx: ProposerContext) => {
|
|
288
|
+
// ctx.currentState — current state name
|
|
289
|
+
// ctx.prompt — decision prompt for this state
|
|
290
|
+
// ctx.transitions — Record<string, string> of available transitions
|
|
291
|
+
// ctx.history — TransitionRecord[] of previous transitions
|
|
292
|
+
// ctx.metaJson — session-level metadata
|
|
293
|
+
|
|
294
|
+
const [name, target] = Object.entries(ctx.transitions)[0];
|
|
295
|
+
return {
|
|
296
|
+
transitionName: name,
|
|
297
|
+
toState: target,
|
|
298
|
+
reasoning: "Chose first transition based on custom logic",
|
|
299
|
+
};
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Arbiter `strategyFn`
|
|
305
|
+
|
|
306
|
+
Receives an `ArbiterContext` and returns `{ consensusReached, winningProposalId?, reasoning }`:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
await registerArbiter({
|
|
310
|
+
specialistId: "custom-arbiter",
|
|
311
|
+
machineName: "my-machine",
|
|
312
|
+
strategyFn: async (ctx: ArbiterContext) => {
|
|
313
|
+
// ctx.proposals — Proposal[] in this round
|
|
314
|
+
// ctx.alignmentScores — Record<string, number> by specialistId
|
|
315
|
+
// ctx.threshold — configured threshold
|
|
316
|
+
// ctx.currentState — current state name
|
|
317
|
+
// ctx.prompt — decision prompt
|
|
318
|
+
// ctx.history — TransitionRecord[]
|
|
319
|
+
// ctx.metaJson — session-level metadata
|
|
320
|
+
|
|
321
|
+
if (ctx.proposals.length === 0) {
|
|
322
|
+
return { consensusReached: false, reasoning: "No proposals" };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Simple: pick the first proposal
|
|
326
|
+
return {
|
|
327
|
+
consensusReached: true,
|
|
328
|
+
winningProposalId: ctx.proposals[0].proposalId,
|
|
329
|
+
reasoning: "Custom arbiter selected first proposal",
|
|
330
|
+
};
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### LLM Mode
|
|
336
|
+
|
|
337
|
+
Use `contextFn` + `modelId` to let DIAL call an LLM with your context:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
await registerProposer({
|
|
341
|
+
specialistId: "llm-proposer",
|
|
342
|
+
machineName: "my-machine",
|
|
343
|
+
contextFn: async (ctx: ProposerContext) => {
|
|
344
|
+
// Return a string prompt for the LLM
|
|
345
|
+
return `You are reviewing a document in state "${ctx.currentState}".
|
|
346
|
+
The prompt is: ${ctx.prompt}
|
|
347
|
+
Available transitions: ${JSON.stringify(ctx.transitions)}
|
|
348
|
+
Previous history: ${ctx.history.map(h => h.transitionName).join(" -> ")}
|
|
349
|
+
|
|
350
|
+
Choose the best transition and explain why.`;
|
|
351
|
+
},
|
|
352
|
+
modelId: "anthropic/claude-sonnet-4",
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Set `OPENROUTER_API_TOKEN` in the environment (or `DIALAI_LLM_BASE_URL` for a different OpenAI-compatible provider).
|
|
357
|
+
|
|
358
|
+
## Testing
|
|
359
|
+
|
|
360
|
+
Use vitest with `clear()` to reset state between tests:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { clear, runSession } from "dialai";
|
|
364
|
+
import { describe, it, beforeEach, expect } from "vitest";
|
|
365
|
+
import type { MachineDefinition } from "dialai";
|
|
366
|
+
|
|
367
|
+
const machine: MachineDefinition = {
|
|
368
|
+
machineName: "test-machine",
|
|
369
|
+
initialState: "start",
|
|
370
|
+
goalState: "end",
|
|
371
|
+
states: {
|
|
372
|
+
start: {
|
|
373
|
+
prompt: "Proceed?",
|
|
374
|
+
transitions: { go: "end" },
|
|
375
|
+
},
|
|
376
|
+
end: {},
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
describe("my machine", () => {
|
|
381
|
+
beforeEach(async () => {
|
|
382
|
+
await clear(); // Reset all sessions, specialists, proposals
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("reaches goal state", async () => {
|
|
386
|
+
const session = await runSession(machine);
|
|
387
|
+
expect(session.currentState).toBe("end");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("records transition history", async () => {
|
|
391
|
+
const session = await runSession(machine);
|
|
392
|
+
expect(session.history).toHaveLength(1);
|
|
393
|
+
expect(session.history[0].transitionName).toBe("go");
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Additional Resources
|
|
399
|
+
|
|
400
|
+
- `references/api-reference.md` — Full API types, function signatures, built-in strategies, environment variables
|
|
401
|
+
- `references/patterns.md` — Copy-paste-ready machine patterns (minimal, pipeline, branching, human-in-the-loop, multi-agent, LLM-powered, testing, anti-patterns)
|