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.
- package/README.md +19 -6
- package/bin/cli.js +23 -5
- package/lib/setup.js +82 -388
- package/package.json +2 -1
- package/templates/project-builder/README.md +119 -0
- package/templates/project-builder/agents/assumptions-clarifier.md +66 -0
- package/templates/project-builder/agents/code-reviewer.md +82 -0
- package/templates/project-builder/agents/code-writer.md +75 -0
- package/templates/project-builder/agents/requirements-clarifier.md +56 -0
- package/templates/project-builder/agents/roadmap-generator.md +74 -0
- package/templates/project-builder/agents/scope-clarifier.md +45 -0
- package/templates/project-builder/agents/security-clarifier.md +72 -0
- package/templates/project-builder/agents/security-reviewer.md +72 -0
- package/templates/project-builder/agents/task-planner.md +63 -0
- package/templates/project-builder/agents/test-planner.md +77 -0
- package/templates/project-builder/config.js +13 -0
- package/templates/project-builder/scripts/mac-notification.js +24 -0
- package/templates/project-builder/scripts/text-human.js +92 -0
- package/templates/project-builder/scripts/workflow-helpers.js +167 -0
- package/templates/project-builder/state/current.json +9 -0
- package/templates/project-builder/state/history.jsonl +0 -0
- package/templates/project-builder/steering/config.json +5 -0
- package/templates/project-builder/steering/global.md +19 -0
- package/templates/project-builder/workflow.js +394 -0
- package/templates/starter/README.md +118 -0
- package/templates/starter/agents/example.js +36 -0
- package/templates/starter/agents/yoda-greeter.md +12 -0
- package/templates/starter/agents/yoda-name-collector.md +12 -0
- package/templates/starter/config.js +12 -0
- package/templates/starter/interactions/.gitkeep +0 -0
- package/templates/starter/scripts/mac-notification.js +24 -0
- package/templates/starter/state/current.json +9 -0
- package/templates/starter/state/history.jsonl +0 -0
- package/templates/starter/steering/config.json +5 -0
- package/templates/starter/steering/global.md +19 -0
- 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
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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.
|
|
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",
|