agent-state-machine 2.0.13 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +19 -6
  2. package/bin/cli.js +23 -5
  3. package/lib/setup.js +82 -388
  4. package/package.json +2 -1
  5. package/templates/project-builder/README.md +119 -0
  6. package/templates/project-builder/agents/assumptions-clarifier.md +66 -0
  7. package/templates/project-builder/agents/code-reviewer.md +82 -0
  8. package/templates/project-builder/agents/code-writer.md +75 -0
  9. package/templates/project-builder/agents/requirements-clarifier.md +56 -0
  10. package/templates/project-builder/agents/roadmap-generator.md +74 -0
  11. package/templates/project-builder/agents/scope-clarifier.md +45 -0
  12. package/templates/project-builder/agents/security-clarifier.md +72 -0
  13. package/templates/project-builder/agents/security-reviewer.md +72 -0
  14. package/templates/project-builder/agents/task-planner.md +63 -0
  15. package/templates/project-builder/agents/test-planner.md +77 -0
  16. package/templates/project-builder/config.js +13 -0
  17. package/templates/project-builder/scripts/mac-notification.js +24 -0
  18. package/templates/project-builder/scripts/text-human.js +92 -0
  19. package/templates/project-builder/scripts/workflow-helpers.js +167 -0
  20. package/templates/project-builder/state/current.json +9 -0
  21. package/templates/project-builder/state/history.jsonl +0 -0
  22. package/templates/project-builder/steering/config.json +5 -0
  23. package/templates/project-builder/steering/global.md +19 -0
  24. package/templates/project-builder/workflow.js +394 -0
  25. package/templates/starter/README.md +118 -0
  26. package/templates/starter/agents/example.js +36 -0
  27. package/templates/starter/agents/yoda-greeter.md +12 -0
  28. package/templates/starter/agents/yoda-name-collector.md +12 -0
  29. package/templates/starter/config.js +12 -0
  30. package/templates/starter/interactions/.gitkeep +0 -0
  31. package/templates/starter/scripts/mac-notification.js +24 -0
  32. package/templates/starter/state/current.json +9 -0
  33. package/templates/starter/state/history.jsonl +0 -0
  34. package/templates/starter/steering/config.json +5 -0
  35. package/templates/starter/steering/global.md +19 -0
  36. package/templates/starter/workflow.js +52 -0
package/README.md CHANGED
@@ -6,6 +6,7 @@ You write normal `async/await` code. The runtime handles:
6
6
  - **Auto-persisted** `memory` (saved to disk on mutation)
7
7
  - **Human-in-the-loop** blocking via `askHuman()` or agent-driven interactions
8
8
  - Local **JS agents** + **Markdown agents** (LLM-powered)
9
+ - **Agent retries** with history logging for failures
9
10
 
10
11
  ---
11
12
 
@@ -43,14 +44,19 @@ Requirements: Node.js >= 16.
43
44
 
44
45
  ```bash
45
46
  state-machine --setup <workflow-name>
47
+ state-machine --setup <workflow-name> --template <template-name>
46
48
  state-machine run <workflow-name>
47
49
  state-machine run <workflow-name> -reset
48
50
  state-machine run <workflow-name> -reset-hard
49
51
 
52
+ state-machine -reset <workflow-name>
53
+ state-machine -reset-hard <workflow-name>
50
54
 
51
55
  state-machine history <workflow-name> [limit]
52
56
  ```
53
57
 
58
+ Templates live in `templates/` and `starter` is used by default.
59
+
54
60
  Workflows live in:
55
61
 
56
62
  ```text
@@ -101,8 +107,8 @@ export default async function() {
101
107
 
102
108
  console.log('Example agent memory.userInfo:', memory.userInfo || userInfo);
103
109
 
104
- // Context is provided automatically
105
- const { greeting } = await agent('yoda-greeter', { userLocation });
110
+ // Context is explicit: pass what the agent needs
111
+ const { greeting } = await agent('yoda-greeter', { userLocation, memory });
106
112
  console.log('Example agent greeting:', greeting);
107
113
 
108
114
  // Or you can provide context manually
@@ -137,7 +143,7 @@ If the process is interrupted, running `state-machine run <workflow-name>` again
137
143
 
138
144
  ## Core API
139
145
 
140
- ### `agent(name, params?)`
146
+ ### `agent(name, params?, options?)`
141
147
 
142
148
  Runs `workflows/<name>/agents/<agent>.(js|mjs|cjs)` or `<agent>.md`.
143
149
 
@@ -146,6 +152,12 @@ const out = await agent('review', { file: 'src/app.js' });
146
152
  memory.lastReview = out;
147
153
  ```
148
154
 
155
+ Options:
156
+ - `retry` (number | false): default `2` (3 total attempts). Use `false` to disable retries.
157
+ - `steering` (string | string[]): extra steering files to load from `workflows/<name>/steering/`.
158
+
159
+ Context is explicit: only `params` are provided to agents unless you pass additional data.
160
+
149
161
  ### `memory`
150
162
 
151
163
  A persisted object for your workflow.
@@ -203,9 +215,8 @@ import { llm } from 'agent-state-machine';
203
215
 
204
216
  export default async function handler(context) {
205
217
  // context includes:
206
- // - persisted memory (spread into the object)
207
218
  // - params passed to agent(name, params)
208
- // - context._steering (global steering prompt/config)
219
+ // - context._steering (global + optional additional steering content)
209
220
  // - context._config (models/apiKeys/workflowDir)
210
221
  return { ok: true };
211
222
  }
@@ -240,11 +251,13 @@ The runtime will block execution and wait for your response in the terminal.
240
251
  ### Markdown agents (`.md`)
241
252
 
242
253
  Markdown agents are LLM-backed prompt templates with optional frontmatter.
254
+ Frontmatter can include `steering` to load additional files from `workflows/<name>/steering/`.
243
255
 
244
256
  ```md
245
257
  ---
246
258
  model: smart
247
259
  output: greeting
260
+ steering: tone, product
248
261
  ---
249
262
  Generate a friendly greeting for {{name}}.
250
263
  ```
@@ -296,7 +309,7 @@ The runtime captures the fully-built prompt in `state/history.jsonl`, viewable i
296
309
  Native JS workflows persist to:
297
310
 
298
311
  - `workflows/<name>/state/current.json` — status, memory, pending interaction
299
- - `workflows/<name>/state/history.jsonl` — event log (newest entries first)
312
+ - `workflows/<name>/state/history.jsonl` — event log (newest entries first, includes agent retry/failure entries)
300
313
  - `workflows/<name>/interactions/*.md` — human input files (when paused)
301
314
 
302
315
  ## License
package/bin/cli.js CHANGED
@@ -13,8 +13,13 @@ import { startLocalServer } from '../vercel-server/local-server.js';
13
13
  const __filename = fileURLToPath(import.meta.url);
14
14
  const __dirname = path.dirname(__filename);
15
15
 
16
- const args = process.argv.slice(2);
17
- const command = args[0];
16
+ let args = process.argv.slice(2);
17
+ let command = args[0];
18
+ const legacyResetCommand = command === '-reset' || command === '-reset-hard';
19
+ if (legacyResetCommand) {
20
+ command = command.slice(1);
21
+ args = [command, ...args.slice(1)];
22
+ }
18
23
 
19
24
  function getVersion() {
20
25
  try {
@@ -34,13 +39,15 @@ function printHelp() {
34
39
  Agent State Machine CLI (Native JS Workflows Only) v${getVersion()}
35
40
 
36
41
  Usage:
37
- state-machine --setup <workflow-name> Create a new workflow project
42
+ state-machine --setup <workflow-name> [--template <template-name>] Create a new workflow project
38
43
  state-machine run <workflow-name> Run a workflow (remote follow enabled by default)
39
44
  state-machine run <workflow-name> -l Run with local server (localhost:3000)
40
45
  state-machine run <workflow-name> -n Generate a new remote follow path
41
46
  state-machine run <workflow-name> -reset Reset workflow state before running
42
47
  state-machine run <workflow-name> -reset-hard Hard reset workflow before running
43
48
 
49
+ state-machine -reset <workflow-name> Reset workflow state (legacy alias)
50
+ state-machine -reset-hard <workflow-name> Hard reset workflow (legacy alias)
44
51
  state-machine status [workflow-name] Show current state (or list all)
45
52
  state-machine history <workflow-name> [limit] Show execution history logs
46
53
  state-machine reset <workflow-name> Reset workflow state (clears memory/state)
@@ -50,6 +57,7 @@ Usage:
50
57
 
51
58
  Options:
52
59
  --setup, -s Initialize a new workflow with directory structure
60
+ --template, -t Template name for --setup (default: starter)
53
61
  --local, -l Use local server instead of remote (starts on localhost:3000)
54
62
  --new, -n Generate a new remote follow path
55
63
  -reset Reset workflow state before running
@@ -421,10 +429,20 @@ async function main() {
421
429
  const workflowName = args[1];
422
430
  if (!workflowName) {
423
431
  console.error('Error: Workflow name required');
424
- console.error('Usage: state-machine --setup <workflow-name>');
432
+ console.error('Usage: state-machine --setup <workflow-name> [--template <template-name>]');
425
433
  process.exit(1);
426
434
  }
427
- await setup(workflowName);
435
+ const templateFlagIndex = args.findIndex((arg) => arg === '--template' || arg === '-t');
436
+ let templateName = null;
437
+ if (templateFlagIndex !== -1) {
438
+ templateName = args[templateFlagIndex + 1];
439
+ if (!templateName || templateName.startsWith('-')) {
440
+ console.error('Error: Template name required');
441
+ console.error('Usage: state-machine --setup <workflow-name> [--template <template-name>]');
442
+ process.exit(1);
443
+ }
444
+ }
445
+ await setup(workflowName, { template: templateName || undefined });
428
446
  process.exit(0);
429
447
  }
430
448
 
package/lib/setup.js CHANGED
@@ -4,13 +4,75 @@
4
4
 
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ const DEFAULT_TEMPLATE = 'starter';
13
+
14
+ function getTemplatesDir() {
15
+ return path.join(__dirname, '..', 'templates');
16
+ }
17
+
18
+ function listTemplates(templatesDir) {
19
+ if (!fs.existsSync(templatesDir)) return [];
20
+ return fs.readdirSync(templatesDir, { withFileTypes: true })
21
+ .filter((entry) => entry.isDirectory())
22
+ .map((entry) => entry.name)
23
+ .sort();
24
+ }
25
+
26
+ function applyReplacements(content, replacements) {
27
+ let output = content;
28
+ for (const [token, value] of Object.entries(replacements)) {
29
+ output = output.split(token).join(value);
30
+ }
31
+ return output;
32
+ }
33
+
34
+ function copyTemplateDir(srcDir, destDir, replacements, createdPaths) {
35
+ fs.mkdirSync(destDir, { recursive: true });
36
+
37
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
38
+ for (const entry of entries) {
39
+ const srcPath = path.join(srcDir, entry.name);
40
+ const destPath = path.join(destDir, entry.name);
41
+
42
+ if (entry.isDirectory()) {
43
+ copyTemplateDir(srcPath, destPath, replacements, createdPaths);
44
+ continue;
45
+ }
46
+
47
+ if (!entry.isFile()) {
48
+ continue;
49
+ }
50
+
51
+ let written = false;
52
+ try {
53
+ const content = fs.readFileSync(srcPath, 'utf-8');
54
+ const replaced = applyReplacements(content, replacements);
55
+ fs.writeFileSync(destPath, replaced);
56
+ written = true;
57
+ } catch {
58
+ // Fallback for non-text files
59
+ }
60
+
61
+ if (!written) {
62
+ fs.copyFileSync(srcPath, destPath);
63
+ }
64
+
65
+ createdPaths.push(destPath);
66
+ }
67
+ }
7
68
 
8
69
  /**
9
70
  * Setup a new workflow with directory structure
10
71
  */
11
- async function setup(workflowName) {
72
+ async function setup(workflowName, options = {}) {
12
73
  const workflowsDir = path.join(process.cwd(), 'workflows');
13
74
  const workflowDir = path.join(workflowsDir, workflowName);
75
+ const templateName = options.template || DEFAULT_TEMPLATE;
14
76
 
15
77
  // Check if workflow already exists
16
78
  if (fs.existsSync(workflowDir)) {
@@ -18,401 +80,33 @@ async function setup(workflowName) {
18
80
  process.exit(1);
19
81
  }
20
82
 
21
- console.log(`\nCreating workflow: ${workflowName}`);
22
- console.log('─'.repeat(40));
23
-
24
- // Create directory structure (native JS workflow only)
25
- const dirs = [
26
- workflowDir,
27
- path.join(workflowDir, 'agents'),
28
- path.join(workflowDir, 'interactions'),
29
- path.join(workflowDir, 'state'),
30
- path.join(workflowDir, 'steering'),
31
- path.join(workflowDir, 'scripts')
32
- ];
33
-
34
- dirs.forEach((dir) => {
35
- fs.mkdirSync(dir, { recursive: true });
36
- console.log(` Created: ${path.relative(process.cwd(), dir)}/`);
37
- });
38
-
39
- // Ensure this workflow folder is ESM (so workflow.js + agents/*.js can use import/export)
40
- // const workflowPkg = {
41
- // name: `workflow-${workflowName}`,
42
- // private: true,
43
- // type: 'module'
44
- // };
45
- // const workflowPkgFile = path.join(workflowDir, 'package.json');
46
- // fs.writeFileSync(workflowPkgFile, JSON.stringify(workflowPkg, null, 2));
47
- // console.log(` Created: ${path.relative(process.cwd(), workflowPkgFile)}`);
48
-
49
- // Create workflow.js (native JS format)
50
- const workflowJs = `/**
51
- /**
52
- * ${workflowName} Workflow
53
- *
54
- * Native JavaScript workflow - write normal async/await code!
55
- *
56
- * Features:
57
- * - memory object auto-persists to disk (use memory guards for idempotency)
58
- * - Use standard JS control flow (if, for, etc.)
59
- * - Interactive prompts pause and wait for user input
60
- */
61
-
62
- import { agent, memory, askHuman, parallel } from 'agent-state-machine';
63
- import { notify } from './scripts/mac-notification.js';
64
-
65
- export default async function() {
66
- console.log('Starting ${workflowName} workflow...');
67
-
68
- // Example: Get user input (saved to memory)
69
- const userLocation = await askHuman('Where do you live?');
70
- console.log('Example prompt answer:', userLocation);
71
-
72
- const userInfo = await agent('yoda-name-collector');
73
- memory.userInfo = userInfo;
74
-
75
- // Provide context
76
- // const userInfo = await agent('yoda-name-collector', { name: 'Luke' });
77
-
78
- console.log('Example agent memory.userInfo:', memory.userInfo || userInfo);
79
-
80
- // Context is provided automatically
81
- const { greeting } = await agent('yoda-greeter', { userLocation });
82
- console.log('Example agent greeting:', greeting);
83
-
84
- // Or you can provide context manually
85
- // await agent('yoda-greeter', userInfo);
86
-
87
- // Example: Parallel execution
88
- // const [a, b, c] = await parallel([
89
- // agent('yoda-greeter', { name: 'the names augustus but friends call me gus' }),
90
- // agent('yoda-greeter', { name: 'uriah' }),
91
- // agent('yoda-greeter', { name: 'lucas' })
92
- // ]);
83
+ const templatesDir = getTemplatesDir();
84
+ const templateDir = path.join(templatesDir, templateName);
93
85
 
94
- // console.log('a: ' + JSON.stringify(a))
95
- // console.log('b: ' + JSON.stringify(b))
96
- // console.log('c: ' + JSON.stringify(c))
97
-
98
- notify(['${workflowName}', userInfo.name || userInfo + ' has been greeted!']);
99
-
100
- console.log('Workflow completed!');
101
- }
102
- `;
103
-
104
- const workflowFile = path.join(workflowDir, 'workflow.js');
105
- fs.writeFileSync(workflowFile, workflowJs);
106
- console.log(` Created: ${path.relative(process.cwd(), workflowFile)}`);
107
-
108
- const configJs = `export const config = {
109
- models: {
110
- low: "gemini",
111
- med: "codex --model gpt-5.2",
112
- high: "claude -m claude-opus-4-20250514 -p",
113
- },
114
- apiKeys: {
115
- gemini: process.env.GEMINI_API_KEY,
116
- anthropic: process.env.ANTHROPIC_API_KEY,
117
- openai: process.env.OPENAI_API_KEY,
118
- }
119
- };
120
- `;
121
-
122
- const configFile = path.join(workflowDir, 'config.js');
123
- fs.writeFileSync(configFile, configJs);
124
- console.log(` Created: ${path.relative(process.cwd(), configFile)}`);
125
-
126
- // Create example JS agent (ESM)
127
- // Create example JS agent (ESM)
128
- const exampleAgent = `/**
129
- * Example Agent for ${workflowName}
130
- *
131
- * Agents are async functions that receive a context object and return a result.
132
- * - Context includes: persisted memory (spread), params, _steering, _config
133
- */
134
-
135
- import { llm } from 'agent-state-machine';
136
-
137
- export default async function handler(context) {
138
- console.log('[Agent: example] Processing...');
139
-
140
- // Access global steering prompt if available
141
- if (context._steering?.global) {
142
- console.log('[Agent: example] Steering loaded (' + context._steering.global.length + ' chars)');
86
+ if (!fs.existsSync(templateDir)) {
87
+ const available = listTemplates(templatesDir);
88
+ console.error(`Error: Template '${templateName}' not found.`);
89
+ if (available.length > 0) {
90
+ console.error(`Available templates: ${available.join(', ')}`);
91
+ }
92
+ process.exit(1);
143
93
  }
144
94
 
145
- // Example: Call an LLM (configure models in config.js)
146
- // const response = await llm(context, {
147
- // model: 'smart',
148
- // prompt: 'Say hello and describe what you can help with.'
149
- // });
150
- // console.log('[Agent: example] LLM response:', response.text);
151
-
152
- return {
153
- ok: true,
154
- received: Object.keys(context).filter((k) => !String(k).startsWith('_')),
155
- processedAt: new Date().toISOString()
156
- };
157
- }
158
-
159
- export const meta = {
160
- name: 'example',
161
- description: 'An example agent to get you started',
162
- version: '1.0.0'
163
- };
164
- `;
165
-
166
- const agentFile = path.join(workflowDir, 'agents', 'example.js');
167
- fs.writeFileSync(agentFile, exampleAgent);
168
- console.log(` Created: ${path.relative(process.cwd(), agentFile)}`);
169
-
170
- // Create example markdown agent
171
- const yodaGreeterAgent = `---
172
- model: low
173
- output: greeting
174
- ---
175
-
176
- # Greeting Task
177
-
178
- Generate a friendly greeting for {{name}} from {{location}} in a yoda style. Prompt user for their actual {{name}} if you dont have it.
179
-
180
- Once you have it create a yoda-greeting.md file in root dir with the greeting.
181
-
182
- You are a fast, direct worker. Do NOT investigate the codebase or read files unless strictly necessary. Perform the requested action immediately using the provided context. Avoid "thinking" steps or creating plans if the task is simple.
183
- `;
184
-
185
- const yodaNameCollectorAgent = `---
186
- model: low
187
- output: name
188
- ---
189
-
190
- # Name Collection Task
191
-
192
- Ask for users name in a yoda style. Unless you have it already.
193
-
194
- Keep it brief and warm.
195
-
196
- You are a fast, direct worker. Do NOT investigate the codebase or read files unless strictly necessary. Perform the requested action immediately using the provided context. Avoid "thinking" steps or creating plans if the task is simple.
197
- `;
198
-
199
- const yodaNameCollectorAgentFile = path.join(workflowDir, 'agents', 'yoda-name-collector.md');
200
- fs.writeFileSync(yodaNameCollectorAgentFile, yodaNameCollectorAgent);
201
-
202
- const yodaGreeterFile = path.join(workflowDir, 'agents', 'yoda-greeter.md');
203
- fs.writeFileSync(yodaGreeterFile, yodaGreeterAgent);
204
-
205
- console.log(` Created: ${path.relative(process.cwd(), yodaGreeterFile)}`);
206
- console.log(` Created: ${path.relative(process.cwd(), yodaNameCollectorAgentFile)}`);
207
-
208
- // Create initial state (native format)
209
- const initialState = {
210
- format: 'native',
211
- status: 'IDLE',
212
- memory: {},
213
- _pendingInteraction: null,
214
- _error: null,
215
- startedAt: null,
216
- lastUpdatedAt: new Date().toISOString()
217
- };
218
- const stateFile = path.join(workflowDir, 'state', 'current.json');
219
- fs.writeFileSync(stateFile, JSON.stringify(initialState, null, 2));
220
- console.log(` Created: ${path.relative(process.cwd(), stateFile)}`);
221
-
222
- // Create empty history file
223
- const historyFile = path.join(workflowDir, 'state', 'history.jsonl');
224
- fs.writeFileSync(historyFile, '');
225
- console.log(` Created: ${path.relative(process.cwd(), historyFile)}`);
95
+ console.log(`\nCreating workflow: ${workflowName}`);
96
+ console.log(`Using template: ${templateName}`);
97
+ console.log(''.repeat(40));
226
98
 
227
- // Create steering config
228
- const steeringConfig = {
229
- _comment: 'Steering configuration',
230
- enabled: true,
231
- globalPrompt: 'global.md'
99
+ const replacements = {
100
+ '__WORKFLOW_NAME__': workflowName,
101
+ '__NOW_ISO__': new Date().toISOString()
232
102
  };
233
- const steeringFile = path.join(workflowDir, 'steering', 'config.json');
234
- fs.writeFileSync(steeringFile, JSON.stringify(steeringConfig, null, 2));
235
- console.log(` Created: ${path.relative(process.cwd(), steeringFile)}`);
236
-
237
- // Create global.md steering prompt
238
- const globalMd = `# Global Steering Prompt
239
-
240
- This content is included with every agent execution in the ${workflowName} workflow.
241
-
242
- ## Guidelines
243
-
244
- - Process data carefully and validate inputs
245
- - Return well-structured JSON when applicable
246
- - Log meaningful progress messages
247
- - Handle errors gracefully
248
-
249
- ## Helpful Tips
250
- - Scripts live in /workflows/project-builder/scripts/
251
- - .gitignore includes /workflows so include ignored files when searching for a script and use \`search_file_content\` with the \`no_ignore\` flag to read the contents of script files.
252
- - Shell execution via \`run_shell_command\` is unavailable, so reproduce the script's intended side effect to fulfill the task.
253
-
254
- ## Notes
255
103
 
256
- This file is automatically loaded and passed to every agent in the workflow via \`context._steering.global\`.
257
- `;
258
- const globalMdFile = path.join(workflowDir, 'steering', 'global.md');
259
- fs.writeFileSync(globalMdFile, globalMd);
260
- console.log(` Created: ${path.relative(process.cwd(), globalMdFile)}`);
104
+ const createdPaths = [];
105
+ copyTemplateDir(templateDir, workflowDir, replacements, createdPaths);
261
106
 
262
- // Create mac-notification.js script
263
- const macNotificationScript = `"use strict";
264
-
265
- import { spawnSync } from "node:child_process";
266
- import { existsSync } from "node:fs";
267
-
268
- function escAppleScript(s) {
269
- return String(s).replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"');
270
- }
271
-
272
- function notify(title = "Notification", message = "Everything finished!") {
273
- const script = \`display notification "\${escAppleScript(message)}" with title "\${escAppleScript(title)}"\`;
274
- spawnSync("osascript", ["-e", script], { stdio: "ignore" });
275
-
276
- const soundPath = "/System/Library/Sounds/Glass.aiff";
277
- const fallbackPath = "/System/Library/Sounds/Ping.aiff";
278
-
279
- if (existsSync(soundPath)) {
280
- spawnSync("afplay", [soundPath], { stdio: "ignore" });
281
- } else if (existsSync(fallbackPath)) {
282
- spawnSync("afplay", [fallbackPath], { stdio: "ignore" });
107
+ for (const createdPath of createdPaths) {
108
+ console.log(` Created: ${path.relative(process.cwd(), createdPath)}`);
283
109
  }
284
- }
285
-
286
- export { notify };
287
- `;
288
- const notificationFile = path.join(workflowDir, 'scripts', 'mac-notification.js');
289
- fs.writeFileSync(notificationFile, macNotificationScript);
290
- console.log(` Created: ${path.relative(process.cwd(), notificationFile)}`);
291
-
292
- // Create README
293
- const readme = `# ${workflowName}
294
-
295
- A workflow created with agent-state-machine (native JS format).
296
-
297
- ## Structure
298
-
299
- \\\`\\\`\\\`
300
- ${workflowName}/
301
- ├── workflow.js # Native JS workflow (async/await)
302
- ├── config.js # Model/API key configuration
303
- ├── package.json # Sets "type": "module" for this workflow folder
304
- ├── agents/ # Custom agents (.js/.mjs/.cjs or .md)
305
- ├── interactions/ # Human-in-the-loop inputs (created at runtime)
306
- ├── state/ # Runtime state (current.json, history.jsonl)
307
- └── steering/ # Steering configuration
308
- \\\`\\\`\\\`
309
-
310
- ## Usage
311
-
312
- Edit \`config.js\` to set models and API keys for this workflow.
313
-
314
- Run the workflow (or resume if interrupted):
315
- \\\`\\\`\\\`bash
316
- state-machine run ${workflowName}
317
- \\\`\\\`\\\`
318
-
319
- Check status:
320
- \\\`\\\`\\\`bash
321
- state-machine status ${workflowName}
322
- \\\`\\\`\\\`
323
-
324
- View history:
325
- \\\`\\\`\\\`bash
326
- state-machine history ${workflowName}
327
- \\\`\\\`\\\`
328
-
329
- View trace logs in browser with live updates:
330
- \\\`\\\`\\\`bash
331
- state-machine follow ${workflowName}
332
- \\\`\\\`\\\`
333
-
334
- Reset state (clears memory/state):
335
- \\\`\\\`\\\`bash
336
- state-machine reset ${workflowName}
337
- \\\`\\\`\\\`
338
-
339
- Hard reset (clears everything: history/interactions/memory):
340
- \\\`\\\`\\\`bash
341
- state-machine reset-hard ${workflowName}
342
- \\\`\\\`\\\`
343
-
344
- ## Writing Workflows
345
-
346
- Edit \`workflow.js\` - write normal async JavaScript:
347
-
348
- \\\`\\\`\\\`js
349
- import { agent, memory, askHuman, parallel } from 'agent-state-machine';
350
-
351
- export default async function() {
352
- console.log('Starting project-builder workflow...');
353
-
354
- // Example: Get user input (saved to memory)
355
- const userLocation = await askHuman('Where do you live?');
356
- console.log('Example prompt answer:', userLocation);
357
-
358
- const userInfo = await agent('yoda-name-collector');
359
- memory.userInfo = userInfo;
360
-
361
- // Provide context
362
- // const userInfo = await agent('yoda-name-collector', { name: 'Luke' });
363
-
364
- console.log('Example agent memory.userInfo:', memory.userInfo || userInfo);
365
-
366
- // Context is provided automatically
367
- const { greeting } = await agent('yoda-greeter', { userLocation });
368
- console.log('Example agent greeting:', greeting);
369
-
370
- // Or you can provide context manually
371
- // await agent('yoda-greeter', userInfo);
372
-
373
- // Example: Parallel execution
374
- // const [a, b, c] = await parallel([
375
- // agent('yoda-greeter', { name: 'the names augustus but friends call me gus' }),
376
- // agent('yoda-greeter', { name: 'uriah' }),
377
- // agent('yoda-greeter', { name: 'lucas' })
378
- // ]);
379
-
380
- // console.log('a: ' + JSON.stringify(a))
381
- // console.log('b: ' + JSON.stringify(b))
382
- // console.log('c: ' + JSON.stringify(c))
383
-
384
- notify(['project-builder', userInfo.name || userInfo + ' has been greeted!']);
385
-
386
- console.log('Workflow completed!');
387
- }
388
- \\\`\\\`\\\`
389
-
390
- ## Creating Agents
391
-
392
- **JavaScript agent** (\`agents/my-agent.js\`):
393
-
394
- \\\`\\\`\\\`js
395
- import { llm } from 'agent-state-machine';
396
-
397
- export default async function handler(context) {
398
- const response = await llm(context, { model: 'smart', prompt: 'Hello!' });
399
- return { greeting: response.text };
400
- }
401
- \\\`\\\`\\\`
402
-
403
- **Markdown agent** (\`agents/greeter.md\`):
404
-
405
- \\\`\\\`\\\`md
406
- ---
407
- model: fast
408
- output: greeting
409
- ---
410
- Generate a greeting for {{name}}.
411
- \\\`\\\`\\\`
412
- `;
413
- const readmeFile = path.join(workflowDir, 'README.md');
414
- fs.writeFileSync(readmeFile, readme);
415
- console.log(` Created: ${path.relative(process.cwd(), readmeFile)}`);
416
110
 
417
111
  console.log('─'.repeat(40));
418
112
  console.log(`\n✓ Workflow '${workflowName}' created successfully!\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-state-machine",
3
- "version": "2.0.13",
3
+ "version": "2.0.15",
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",
@@ -28,6 +28,7 @@
28
28
  "files": [
29
29
  "bin",
30
30
  "lib",
31
+ "templates",
31
32
  "vercel-server/local-server.js",
32
33
  "vercel-server/public",
33
34
  "vercel-server/ui",