agent-state-machine 2.1.9 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-state-machine",
3
- "version": "2.1.9",
3
+ "version": "2.2.1",
4
4
  "type": "module",
5
5
  "description": "A workflow orchestrator for running agents and scripts in sequence with state management",
6
6
  "main": "lib/index.js",
@@ -1,60 +1,97 @@
1
- # project-builder
1
+ # agent-state-machine
2
2
 
3
- A workflow created with agent-state-machine (native JS format).
3
+ A workflow runner for building **linear, stateful agent workflows** in plain JavaScript.
4
4
 
5
- ## Structure
5
+ You write normal `async/await` code. The runtime handles:
6
+ - **Auto-persisted** `memory` (saved to disk on mutation)
7
+ - **Auto-tracked** `fileTree` (detects file changes made by agents via Git)
8
+ - **Human-in-the-loop** blocking via `askHuman()` or agent-driven interactions
9
+ - Local **JS agents** + **Markdown agents** (LLM-powered)
10
+ - **Agent retries** with history logging for failures
6
11
 
7
- \`\`\`
8
- project-builder/
9
- ├── workflow.js # Native JS workflow (async/await)
10
- ├── config.js # Model/API key configuration
11
- ├── package.json # Sets "type": "module" for this workflow folder
12
- ├── agents/ # Custom agents (.js/.mjs/.cjs or .md)
13
- ├── interactions/ # Human-in-the-loop inputs (created at runtime)
14
- ├── state/ # Runtime state (current.json, history.jsonl)
15
- └── steering/ # Steering configuration
16
- \`\`\`
12
+ ---
13
+
14
+ ## Install
15
+
16
+ You need to install the package **globally** to get the CLI, and **locally** in your project so your workflow can import the library.
17
17
 
18
- ## Usage
18
+ ### Global CLI
19
+ Provides the `state-machine` command.
19
20
 
20
- Edit `config.js` to set models and API keys for this workflow.
21
+ ```bash
22
+ # npm
23
+ npm i -g agent-state-machine
21
24
 
22
- Run the workflow (or resume if interrupted):
23
- \`\`\`bash
24
- state-machine run project-builder
25
- \`\`\`
25
+ # pnpm
26
+ pnpm add -g agent-state-machine
27
+ ```
26
28
 
27
- Check status:
28
- \`\`\`bash
29
- state-machine status project-builder
30
- \`\`\`
29
+ ### Local Library
30
+ Required so your `workflow.js` can `import { agent, memory, fileTree } from 'agent-state-machine'`.
31
31
 
32
- View history:
33
- \`\`\`bash
34
- state-machine history project-builder
35
- \`\`\`
32
+ ```bash
33
+ # npm
34
+ npm i agent-state-machine
36
35
 
37
- View trace logs in browser with live updates:
38
- \`\`\`bash
39
- state-machine follow project-builder
40
- \`\`\`
36
+ # pnpm (for monorepos/turbo, install in root)
37
+ pnpm add agent-state-machine -w
38
+ ```
39
+
40
+ Requirements: Node.js >= 16.
41
+
42
+ ---
41
43
 
42
- Reset state (clears memory/state):
43
- \`\`\`bash
44
- state-machine reset project-builder
45
- \`\`\`
44
+ ## CLI
46
45
 
47
- Hard reset (clears everything: history/interactions/memory):
48
- \`\`\`bash
49
- state-machine reset-hard project-builder
50
- \`\`\`
46
+ ```bash
47
+ state-machine --setup <workflow-name>
48
+ state-machine --setup <workflow-name> --template <template-name>
49
+ state-machine run <workflow-name>
50
+ state-machine run <workflow-name> -reset
51
+ state-machine run <workflow-name> -reset-hard
51
52
 
52
- ## Writing Workflows
53
+ state-machine -reset <workflow-name>
54
+ state-machine -reset-hard <workflow-name>
53
55
 
54
- Edit `workflow.js` - write normal async JavaScript:
56
+ state-machine history <workflow-name> [limit]
57
+ ```
58
+
59
+ Templates live in `templates/` and `starter` is used by default.
60
+
61
+ Workflows live in:
62
+
63
+ ```text
64
+ workflows/<name>/
65
+ ├── workflow.js # Native JS workflow (async/await)
66
+ ├── config.js # Model/API key configuration
67
+ ├── package.json # Sets "type": "module" for this workflow folder
68
+ ├── agents/ # Custom agents (.js/.mjs/.cjs or .md)
69
+ ├── interactions/ # Human-in-the-loop files (auto-created)
70
+ ├── state/ # current.json, history.jsonl
71
+ └── steering/ # global.md + config.json
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Writing workflows (native JS)
77
+
78
+ Edit `config.js` to set models and API keys for the workflow.
79
+
80
+ ```js
81
+ /**
82
+ /**
83
+ * project-builder Workflow
84
+ *
85
+ * Native JavaScript workflow - write normal async/await code!
86
+ *
87
+ * Features:
88
+ * - memory object auto-persists to disk (use memory guards for idempotency)
89
+ * - Use standard JS control flow (if, for, etc.)
90
+ * - Interactive prompts pause and wait for user input
91
+ */
55
92
 
56
- \`\`\`js
57
93
  import { agent, memory, askHuman, parallel } from 'agent-state-machine';
94
+ import { notify } from './scripts/mac-notification.js';
58
95
 
59
96
  export default async function() {
60
97
  console.log('Starting project-builder workflow...');
@@ -71,8 +108,8 @@ export default async function() {
71
108
 
72
109
  console.log('Example agent memory.userInfo:', memory.userInfo || userInfo);
73
110
 
74
- // Context is provided automatically
75
- const { greeting } = await agent('yoda-greeter', { userLocation });
111
+ // Context is explicit: pass what the agent needs
112
+ const { greeting } = await agent('yoda-greeter', { userLocation, memory });
76
113
  console.log('Example agent greeting:', greeting);
77
114
 
78
115
  // Or you can provide context manually
@@ -93,27 +130,238 @@ export default async function() {
93
130
 
94
131
  console.log('Workflow completed!');
95
132
  }
96
- \`\`\`
133
+ ```
134
+
135
+ ### Resuming workflows
136
+
137
+ `state-machine run` restarts your workflow from the top, loading the persisted state.
138
+
139
+ If the workflow needs human input, it will **block inline** in the terminal. You can answer in the terminal, edit `interactions/<slug>.md`, or respond in the browser.
140
+
141
+ If the process is interrupted, running `state-machine run <workflow-name>` again will continue execution (assuming your workflow uses `memory` to skip completed steps).
142
+
143
+ ---
144
+
145
+ ## Core API
146
+
147
+ ### `agent(name, params?, options?)`
148
+
149
+ Runs `workflows/<name>/agents/<agent>.(js|mjs|cjs)` or `<agent>.md`.
150
+
151
+ ```js
152
+ const out = await agent('review', { file: 'src/app.js' });
153
+ memory.lastReview = out;
154
+ ```
155
+
156
+ Options:
157
+ - `retry` (number | false): default `2` (3 total attempts). Use `false` to disable retries.
158
+ - `steering` (string | string[]): extra steering files to load from `workflows/<name>/steering/`.
159
+
160
+ Context is explicit: only `params` are provided to agents unless you pass additional data.
161
+
162
+ ### `memory`
163
+
164
+ A persisted object for your workflow.
165
+
166
+ - Mutations auto-save to `workflows/<name>/state/current.json`.
167
+ - Use it as your "long-lived state" between runs.
168
+
169
+ ```js
170
+ memory.count = (memory.count || 0) + 1;
171
+ ```
172
+
173
+ ### `fileTree`
174
+
175
+ Auto-tracked file changes made by agents.
176
+
177
+ - Before each `await agent(...)`, the runtime captures a Git baseline
178
+ - After the agent completes, it detects created/modified/deleted files
179
+ - Changes are stored in `memory.fileTree` and persisted to `current.json`
180
+
181
+ ```js
182
+ // Files are auto-tracked when agents create them
183
+ await agent('code-writer', { task: 'Create auth module' });
184
+
185
+ // Access tracked files
186
+ console.log(memory.fileTree);
187
+ // { "src/auth.js": { status: "created", createdBy: "code-writer", ... } }
188
+
189
+ // Pass file context to other agents
190
+ await agent('code-reviewer', { fileTree: memory.fileTree });
191
+ ```
192
+
193
+ Configuration in `config.js`:
194
+
195
+ ```js
196
+ export const config = {
197
+ // ... models and apiKeys ...
198
+ projectRoot: process.env.PROJECT_ROOT, // defaults to ../.. from workflow
199
+ fileTracking: true, // enable/disable (default: true)
200
+ fileTrackingIgnore: ['node_modules/**', '.git/**', 'dist/**'],
201
+ fileTrackingKeepDeleted: false // keep deleted files in tree
202
+ };
203
+ ```
204
+
205
+ ### `trackFile(path, options?)` / `untrackFile(path)`
206
+
207
+ Manual file tracking utilities:
208
+
209
+ ```js
210
+ import { trackFile, getFileTree, untrackFile } from 'agent-state-machine';
211
+
212
+ trackFile('README.md', { caption: 'Project docs' });
213
+ const tree = getFileTree();
214
+ untrackFile('old-file.js');
215
+ ```
97
216
 
98
- ## Creating Agents
217
+ ### `askHuman(question, options?)`
99
218
 
100
- **JavaScript agent** (`agents/my-agent.js`):
219
+ Gets user input.
101
220
 
102
- \`\`\`js
221
+ - In a TTY, it prompts in the terminal (or via the browser when remote follow is enabled).
222
+ - Otherwise it creates `interactions/<slug>.md` and blocks until you confirm in the terminal (or respond in the browser).
223
+
224
+ ```js
225
+ const repo = await askHuman('What repo should I work on?', { slug: 'repo' });
226
+ memory.repo = repo;
227
+ ```
228
+
229
+ ### `parallel([...])` / `parallelLimit([...], limit)`
230
+
231
+ Run multiple `agent()` calls concurrently:
232
+
233
+ ```js
234
+ import { agent, parallel, parallelLimit } from 'agent-state-machine';
235
+
236
+ const [a, b] = await parallel([
237
+ agent('review', { file: 'src/a.js' }),
238
+ agent('review', { file: 'src/b.js' }),
239
+ ]);
240
+
241
+ const results = await parallelLimit(
242
+ ['a.js', 'b.js', 'c.js'].map(f => agent('review', { file: f })),
243
+ 2
244
+ );
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Agents
250
+
251
+ Agents live in `workflows/<workflow>/agents/`.
252
+
253
+ ### JavaScript agents
254
+
255
+ **ESM (`.js` / `.mjs`)**:
256
+
257
+ ```js
258
+ // workflows/<name>/agents/example.js
103
259
  import { llm } from 'agent-state-machine';
104
260
 
105
261
  export default async function handler(context) {
106
- const response = await llm(context, { model: 'smart', prompt: 'Hello!' });
107
- return { greeting: response.text };
262
+ // context includes:
263
+ // - params passed to agent(name, params)
264
+ // - context._steering (global + optional additional steering content)
265
+ // - context._config (models/apiKeys/workflowDir/projectRoot)
266
+
267
+ // Optionally return _files to annotate tracked files
268
+ return {
269
+ ok: true,
270
+ _files: [{ path: 'src/example.js', caption: 'Example module' }]
271
+ };
272
+ }
273
+ ```
274
+
275
+ **CommonJS (`.cjs`)** (only if you prefer CJS):
276
+
277
+ ```js
278
+ // workflows/<name>/agents/example.cjs
279
+ async function handler(context) {
280
+ return { ok: true };
108
281
  }
109
- \`\`\`
110
282
 
111
- **Markdown agent** (`agents/greeter.md`):
283
+ module.exports = handler;
284
+ module.exports.handler = handler;
285
+ ```
286
+
287
+ If you need to request human input from a JS agent, return an `_interaction` payload:
288
+
289
+ ```js
290
+ return {
291
+ _interaction: {
292
+ slug: 'approval',
293
+ targetKey: 'approval',
294
+ content: 'Please approve this change (yes/no).'
295
+ }
296
+ };
297
+ ```
298
+
299
+ The runtime will block execution and wait for your response in the terminal.
300
+
301
+ ### Markdown agents (`.md`)
112
302
 
113
- \`\`\`md
303
+ Markdown agents are LLM-backed prompt templates with optional frontmatter.
304
+ Frontmatter can include `steering` to load additional files from `workflows/<name>/steering/`.
305
+
306
+ ```md
114
307
  ---
115
- model: fast
308
+ model: smart
116
309
  output: greeting
310
+ steering: tone, product
311
+ ---
312
+ Generate a friendly greeting for {{name}}.
313
+ ```
314
+
315
+ Calling it:
316
+
317
+ ```js
318
+ const { greeting } = await agent('greeter', { name: 'Sam' });
319
+ memory.greeting = greeting;
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Models & LLM execution
325
+
326
+ In your workflow’s `export const config = { models: { ... } }`, each model value can be:
327
+
328
+ ### CLI command
329
+
330
+ ```js
331
+ export const config = {
332
+ models: {
333
+ smart: "claude -m claude-sonnet-4-20250514 -p"
334
+ }
335
+ };
336
+ ```
337
+
338
+ ### API target
339
+
340
+ Format: `api:<provider>:<model>`
341
+
342
+ ```js
343
+ export const config = {
344
+ models: {
345
+ smart: "api:openai:gpt-4.1-mini"
346
+ },
347
+ apiKeys: {
348
+ openai: process.env.OPENAI_API_KEY
349
+ }
350
+ };
351
+ ```
352
+
353
+ The runtime captures the fully-built prompt in `state/history.jsonl`, viewable in the browser with live updates when running with the `--local` flag or via the remote URL. Remote follow links persist across runs (stored in `config.js`) unless you pass `-n`/`--new` to regenerate.
354
+
117
355
  ---
118
- Generate a greeting for {{name}}.
119
- \`\`\`
356
+
357
+ ## State & persistence
358
+
359
+ Native JS workflows persist to:
360
+
361
+ - `workflows/<name>/state/current.json` — status, memory (includes fileTree), pending interaction
362
+ - `workflows/<name>/state/history.jsonl` — event log (newest entries first, includes agent retry/failure entries)
363
+ - `workflows/<name>/interactions/*.md` — human input files (when paused)
364
+
365
+ ## License
366
+
367
+ MIT
@@ -6,7 +6,9 @@ const DEFAULT_TIMEOUT_MS = 30000;
6
6
 
7
7
  export default async function sanityRunner(context) {
8
8
  const { checks = [], setup, teardown } = context;
9
- const cwd = context?._config?.workflowDir || process.cwd();
9
+ const workflowDir = context?._config?.workflowDir || process.cwd();
10
+ const projectRoot = context?._config?.projectRoot || workflowDir;
11
+ const cwd = projectRoot;
10
12
  const results = [];
11
13
 
12
14
  let setupError = null;
@@ -9,5 +9,16 @@ export const config = {
9
9
  gemini: process.env.GEMINI_API_KEY,
10
10
  anthropic: process.env.ANTHROPIC_API_KEY,
11
11
  openai: process.env.OPENAI_API_KEY,
12
- }
12
+ },
13
+
14
+ // File tracking (all optional - shown with defaults)
15
+ // projectRoot: process.env.PROJECT_ROOT, // Defaults to ../.. from workflow
16
+ // fileTracking: true, // Enable/disable file tracking
17
+ // fileTrackingIgnore: [ // Glob patterns to ignore
18
+ // 'node_modules/**',
19
+ // '.git/**',
20
+ // 'dist/**',
21
+ // 'workflows/**'
22
+ // ],
23
+ // fileTrackingKeepDeleted: false // Keep deleted files in tree
13
24
  };