agent-state-machine 2.0.14 → 2.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.
Files changed (67) hide show
  1. package/bin/cli.js +1 -1
  2. package/lib/index.js +33 -0
  3. package/lib/remote/client.js +7 -2
  4. package/lib/runtime/agent.js +102 -67
  5. package/lib/runtime/index.js +13 -0
  6. package/lib/runtime/interaction.js +304 -0
  7. package/lib/runtime/prompt.js +39 -12
  8. package/lib/runtime/runtime.js +11 -10
  9. package/package.json +2 -1
  10. package/templates/project-builder/README.md +119 -0
  11. package/templates/project-builder/agents/assumptions-clarifier.md +65 -0
  12. package/templates/project-builder/agents/code-reviewer.md +81 -0
  13. package/templates/project-builder/agents/code-writer.md +74 -0
  14. package/templates/project-builder/agents/requirements-clarifier.md +55 -0
  15. package/templates/project-builder/agents/response-interpreter.md +25 -0
  16. package/templates/project-builder/agents/roadmap-generator.md +73 -0
  17. package/templates/project-builder/agents/sanity-checker.md +45 -0
  18. package/templates/project-builder/agents/sanity-runner.js +161 -0
  19. package/templates/project-builder/agents/scope-clarifier.md +44 -0
  20. package/templates/project-builder/agents/security-clarifier.md +71 -0
  21. package/templates/project-builder/agents/security-reviewer.md +71 -0
  22. package/templates/project-builder/agents/task-planner.md +62 -0
  23. package/templates/project-builder/agents/test-planner.md +76 -0
  24. package/templates/project-builder/config.js +13 -0
  25. package/templates/project-builder/scripts/interaction-helpers.js +33 -0
  26. package/templates/project-builder/scripts/mac-notification.js +24 -0
  27. package/templates/project-builder/scripts/text-human.js +92 -0
  28. package/templates/project-builder/scripts/workflow-helpers.js +122 -0
  29. package/templates/project-builder/state/current.json +9 -0
  30. package/templates/project-builder/state/history.jsonl +0 -0
  31. package/templates/project-builder/steering/config.json +5 -0
  32. package/templates/project-builder/steering/global.md +19 -0
  33. package/templates/project-builder/workflow.js +554 -0
  34. package/templates/starter/README.md +118 -0
  35. package/templates/starter/agents/example.js +36 -0
  36. package/templates/starter/agents/yoda-greeter.md +12 -0
  37. package/templates/starter/agents/yoda-name-collector.md +12 -0
  38. package/templates/starter/config.js +12 -0
  39. package/templates/starter/interactions/.gitkeep +0 -0
  40. package/templates/starter/scripts/mac-notification.js +24 -0
  41. package/templates/starter/state/current.json +9 -0
  42. package/templates/starter/state/history.jsonl +0 -0
  43. package/templates/starter/steering/config.json +5 -0
  44. package/templates/starter/steering/global.md +19 -0
  45. package/templates/starter/workflow.js +52 -0
  46. package/vercel-server/api/session/[token].js +3 -3
  47. package/vercel-server/api/submit/[token].js +5 -3
  48. package/vercel-server/local-server.js +33 -6
  49. package/vercel-server/public/remote/index.html +17 -0
  50. package/vercel-server/ui/index.html +9 -1012
  51. package/vercel-server/ui/package-lock.json +2650 -0
  52. package/vercel-server/ui/package.json +25 -0
  53. package/vercel-server/ui/postcss.config.js +6 -0
  54. package/vercel-server/ui/src/App.jsx +236 -0
  55. package/vercel-server/ui/src/components/ChoiceInteraction.jsx +127 -0
  56. package/vercel-server/ui/src/components/ConfirmInteraction.jsx +51 -0
  57. package/vercel-server/ui/src/components/ContentCard.jsx +161 -0
  58. package/vercel-server/ui/src/components/CopyButton.jsx +27 -0
  59. package/vercel-server/ui/src/components/EventsLog.jsx +82 -0
  60. package/vercel-server/ui/src/components/Footer.jsx +66 -0
  61. package/vercel-server/ui/src/components/Header.jsx +38 -0
  62. package/vercel-server/ui/src/components/InteractionForm.jsx +42 -0
  63. package/vercel-server/ui/src/components/TextInteraction.jsx +72 -0
  64. package/vercel-server/ui/src/index.css +145 -0
  65. package/vercel-server/ui/src/main.jsx +8 -0
  66. package/vercel-server/ui/tailwind.config.js +19 -0
  67. package/vercel-server/ui/vite.config.js +11 -0
@@ -0,0 +1,71 @@
1
+ ---
2
+ model: med
3
+ format: json
4
+ ---
5
+
6
+ # Security Reviewer Agent
7
+
8
+ You are a security review specialist. Review tasks and implementations for security concerns.
9
+
10
+ ## Context
11
+ Task: {{task}}
12
+ Phase: {{phase}}
13
+ Scope: {{scope}}
14
+ Stage: {{stage}}
15
+ {{#if implementation}}
16
+ Implementation: {{implementation}}
17
+ {{/if}}
18
+ {{#if feedback}}
19
+ Previous Feedback: {{feedback}}
20
+ {{/if}}
21
+
22
+ ## Instructions
23
+
24
+ Perform a security review appropriate to the stage:
25
+
26
+ **Pre-Implementation Review (stage: pre-implementation):**
27
+ - Identify potential security concerns for the task
28
+ - Recommend secure implementation patterns
29
+ - Flag any high-risk areas requiring extra attention
30
+ - Suggest security tests to include
31
+
32
+ **Post-Implementation Review (stage: post-implementation):**
33
+ - Review the implementation for security issues
34
+ - Check for common vulnerabilities (OWASP Top 10)
35
+ - Verify secure coding practices
36
+ - Identify any remaining security debt
37
+
38
+ ## Output Format
39
+
40
+ Return a valid JSON object:
41
+
42
+ {
43
+ "stage": "pre-implementation",
44
+ "riskLevel": "low",
45
+ "findings": [
46
+ {
47
+ "type": "recommendation",
48
+ "severity": "medium",
49
+ "description": "Consider input validation for user data",
50
+ "recommendation": "Use schema validation library"
51
+ }
52
+ ],
53
+ "securityChecklist": [
54
+ {"item": "Validate all user inputs", "status": "pending"},
55
+ {"item": "Use parameterized queries", "status": "pending"},
56
+ {"item": "Implement rate limiting", "status": "na"}
57
+ ],
58
+ "approved": true,
59
+ "blockers": []
60
+ }
61
+
62
+ **Security Focus Areas:**
63
+ - Input validation and sanitization
64
+ - Authentication and authorization
65
+ - Data encryption (at rest and in transit)
66
+ - Error handling and logging
67
+ - Dependency vulnerabilities
68
+ - Injection attacks (SQL, XSS, command injection)
69
+ - Secure configuration
70
+
71
+ Be thorough but pragmatic. Not every task has major security implications.
@@ -0,0 +1,62 @@
1
+ ---
2
+ model: high
3
+ format: json
4
+ ---
5
+
6
+ # Task Planner Agent
7
+
8
+ You are a task breakdown specialist. Generate detailed task lists for a specific phase as structured JSON.
9
+
10
+ ## Context
11
+ Project Description: {{projectDescription}}
12
+ Scope: {{scope}}
13
+ Requirements: {{requirements}}
14
+ Phase Number: {{phaseIndex}}
15
+ Phase Details: {{phase}}
16
+ {{#if feedback}}
17
+ User Feedback: {{feedback}}
18
+ {{/if}}
19
+
20
+ ## Instructions
21
+
22
+ Break down the phase into specific, actionable tasks. Each task should:
23
+ - Be small enough to complete in a focused work session
24
+ - Have a clear definition of done
25
+ - Include a sanity check the user can verify
26
+
27
+ **Task Principles:**
28
+ - One task = one concern (don't combine unrelated work)
29
+ - Tasks should be independently verifiable
30
+ - Order tasks by dependency (what must come first)
31
+ - Include setup/preparation tasks if needed
32
+
33
+ ## Output Format
34
+
35
+ Return a valid JSON object (no markdown code blocks, just raw JSON):
36
+
37
+ {
38
+ "phaseNumber": 1,
39
+ "phaseTitle": "Phase Title",
40
+ "tasks": [
41
+ {
42
+ "id": 1,
43
+ "title": "Task Title",
44
+ "description": "What needs to be done",
45
+ "doneDefinition": "Specific completion criteria that can be verified",
46
+ "sanityCheck": "How the user can verify this is working correctly",
47
+ "stage": "pending"
48
+ },
49
+ {
50
+ "id": 2,
51
+ "title": "Task Title",
52
+ "description": "What needs to be done",
53
+ "doneDefinition": "Specific completion criteria",
54
+ "sanityCheck": "Verification method",
55
+ "stage": "pending"
56
+ }
57
+ ]
58
+ }
59
+
60
+ **Stage values:** pending, in_progress, completed, failed
61
+
62
+ Keep tasks focused and achievable. Aim for 3-8 tasks per phase depending on complexity. Every task MUST have a doneDefinition and sanityCheck.
@@ -0,0 +1,76 @@
1
+ ---
2
+ model: med
3
+ format: json
4
+ ---
5
+
6
+ # Test Planner Agent
7
+
8
+ You are a test planning specialist. Create test plans for tasks before implementation.
9
+
10
+ ## Context
11
+ Task: {{task}}
12
+ Phase: {{phase}}
13
+ Requirements: {{requirements}}
14
+ Security Considerations: {{securityConsiderations}}
15
+ {{#if feedback}}
16
+ Previous Feedback: {{feedback}}
17
+ {{/if}}
18
+
19
+ ## Instructions
20
+
21
+ Create a comprehensive test plan for the task. Include:
22
+
23
+ **Test Categories:**
24
+ - Unit tests (individual functions/components)
25
+ - Integration tests (component interactions)
26
+ - Security tests (based on security review)
27
+ - Edge case tests (boundary conditions)
28
+
29
+ **Test Principles:**
30
+ - Test behavior, not implementation
31
+ - Cover happy path and error cases
32
+ - Include tests for security concerns flagged in review
33
+ - Prioritize tests by risk and importance
34
+
35
+ ## Output Format
36
+
37
+ Return a valid JSON object:
38
+
39
+ {
40
+ "testPlan": {
41
+ "summary": "Brief description of testing approach",
42
+ "unitTests": [
43
+ {
44
+ "name": "should validate user input",
45
+ "description": "Verify input sanitization works correctly",
46
+ "expectedBehavior": "Invalid input should be rejected with error message",
47
+ "priority": "high"
48
+ }
49
+ ],
50
+ "integrationTests": [
51
+ {
52
+ "name": "should save and retrieve data",
53
+ "description": "Verify database integration works",
54
+ "components": ["API", "Database"],
55
+ "priority": "high"
56
+ }
57
+ ],
58
+ "securityTests": [
59
+ {
60
+ "name": "should prevent SQL injection",
61
+ "threat": "SQL injection via user input",
62
+ "testMethod": "Attempt injection with malicious strings",
63
+ "priority": "high"
64
+ }
65
+ ],
66
+ "edgeCases": [
67
+ {
68
+ "scenario": "Empty input handling",
69
+ "expectedBehavior": "Return validation error"
70
+ }
71
+ ]
72
+ },
73
+ "testingNotes": "Any special considerations or setup needed"
74
+ }
75
+
76
+ Focus on tests that validate the definition of done. Don't over-test trivial functionality.
@@ -0,0 +1,13 @@
1
+ export const config = {
2
+ models: {
3
+ low: "gemini",
4
+ med: "gemini",
5
+ high: "gemini",
6
+ },
7
+ apiKeys: {
8
+ gemini: process.env.GEMINI_API_KEY,
9
+ anthropic: process.env.ANTHROPIC_API_KEY,
10
+ openai: process.env.OPENAI_API_KEY,
11
+ },
12
+ remotePath: "TczrLmUecnqZPpPhBTrvU374CGlfzDfINrr0eN0nMgQ",
13
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Interaction helpers for project-builder template
3
+ *
4
+ * Re-exports core interaction utilities from agent-state-machine
5
+ * and adds an LLM-based interpreter for ambiguous responses.
6
+ */
7
+
8
+ import {
9
+ agent,
10
+ createInteraction,
11
+ formatInteractionPrompt,
12
+ normalizeInteraction,
13
+ parseInteractionResponse
14
+ } from 'agent-state-machine';
15
+
16
+ // Re-export core utilities
17
+ export { createInteraction, formatInteractionPrompt, normalizeInteraction };
18
+
19
+ /**
20
+ * Parse a response with LLM interpreter fallback
21
+ *
22
+ * Uses the response-interpreter agent when fast-path matching fails.
23
+ */
24
+ export async function parseResponse(interaction, rawResponse) {
25
+ return parseInteractionResponse(interaction, rawResponse, async (int, raw) => {
26
+ // Use the response-interpreter agent to interpret ambiguous responses
27
+ const result = await agent('response-interpreter', {
28
+ userResponse: raw,
29
+ interaction: int
30
+ });
31
+ return result;
32
+ });
33
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { existsSync } from "node:fs";
5
+
6
+ function escAppleScript(s) {
7
+ return String(s).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
8
+ }
9
+
10
+ function notify(title = "Notification", message = "Everything finished!") {
11
+ const script = `display notification "${escAppleScript(message)}" with title "${escAppleScript(title)}"`;
12
+ spawnSync("osascript", ["-e", script], { stdio: "ignore" });
13
+
14
+ const soundPath = "/System/Library/Sounds/Glass.aiff";
15
+ const fallbackPath = "/System/Library/Sounds/Ping.aiff";
16
+
17
+ if (existsSync(soundPath)) {
18
+ spawnSync("afplay", [soundPath], { stdio: "ignore" });
19
+ } else if (existsSync(fallbackPath)) {
20
+ spawnSync("afplay", [fallbackPath], { stdio: "ignore" });
21
+ }
22
+ }
23
+
24
+ export { notify };
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import nodemailer from "nodemailer";
5
+
6
+ function loadEnvFile() {
7
+ if (typeof process.loadEnvFile === "function") {
8
+ process.loadEnvFile();
9
+ return;
10
+ }
11
+
12
+ const envPath = ".env";
13
+ if (!existsSync(envPath)) {
14
+ return;
15
+ }
16
+
17
+ const lines = readFileSync(envPath, "utf8").split(/\r?\n/);
18
+ for (const line of lines) {
19
+ const trimmed = line.trim();
20
+ if (!trimmed || trimmed.startsWith("#")) {
21
+ continue;
22
+ }
23
+
24
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
25
+ if (!match) {
26
+ continue;
27
+ }
28
+
29
+ const key = match[1];
30
+ if (process.env[key] !== undefined) {
31
+ continue;
32
+ }
33
+
34
+ let value = match[2] ?? "";
35
+ if (
36
+ (value.startsWith("\"") && value.endsWith("\"")) ||
37
+ (value.startsWith("'") && value.endsWith("'"))
38
+ ) {
39
+ value = value.slice(1, -1);
40
+ }
41
+
42
+ process.env[key] = value;
43
+ }
44
+ }
45
+
46
+ function requireEnv(name) {
47
+ const value = process.env[name];
48
+ if (!value) {
49
+ throw new Error(`Missing required env var: ${name}`);
50
+ }
51
+ return value;
52
+ }
53
+
54
+ function createTransport() {
55
+ const host = requireEnv("SMTP_HOST");
56
+ const port = Number(requireEnv("SMTP_PORT"));
57
+ const user = requireEnv("SMTP_USER");
58
+ const pass = requireEnv("SMTP_PASS");
59
+ const secure = String(process.env.SMTP_SECURE || "").toLowerCase() === "true";
60
+
61
+ return nodemailer.createTransport({
62
+ host,
63
+ port,
64
+ secure,
65
+ auth: {
66
+ user,
67
+ pass,
68
+ },
69
+ });
70
+ }
71
+
72
+ async function textHuman(message) {
73
+ loadEnvFile();
74
+
75
+ if (!message || typeof message !== "string") {
76
+ throw new Error("textHuman(message) requires a non-empty string.");
77
+ }
78
+
79
+ const from = process.env.SMS_FROM || requireEnv("SMTP_FROM");
80
+ const to = requireEnv("SMS_TO");
81
+ const transporter = createTransport();
82
+
83
+ const info = await transporter.sendMail({
84
+ from,
85
+ to,
86
+ subject: "",
87
+ text: message,
88
+ });
89
+ void info;
90
+ }
91
+
92
+ export { textHuman };
@@ -0,0 +1,122 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { memory } from 'agent-state-machine';
4
+
5
+ // Write markdown file to workflow state directory
6
+ function writeMarkdownFile(stateDir, filename, content) {
7
+ if (!fs.existsSync(stateDir)) fs.mkdirSync(stateDir, { recursive: true });
8
+ const filePath = path.join(stateDir, filename);
9
+ fs.writeFileSync(filePath, content);
10
+ console.log(` [File] Updated: ${filename}`);
11
+ return filePath;
12
+ }
13
+
14
+ // Strict approval parsing - only accepts explicit approval
15
+ function isApproval(response) {
16
+ if (!response || typeof response !== 'string') return false;
17
+ const trimmed = response.trim().toLowerCase();
18
+ // Must start with 'a' or be exactly 'approve/approved/yes/y'
19
+ return /^a\b/.test(trimmed) ||
20
+ /^approve/.test(trimmed) ||
21
+ /^yes\b/.test(trimmed) ||
22
+ /^y\b/.test(trimmed);
23
+ }
24
+
25
+ // Generate markdown from roadmap JSON
26
+ function renderRoadmapMarkdown(roadmap) {
27
+ if (!roadmap || !roadmap.phases) return '# Project Roadmap\n\nNo phases defined.';
28
+
29
+ let md = `# Project Roadmap: ${roadmap.title || 'Untitled Project'}\n\n`;
30
+
31
+ for (const phase of roadmap.phases) {
32
+ const status = phase.completed ? ' [COMPLETED]' : '';
33
+ md += `## Phase ${phase.number}: ${phase.title}${status}\n`;
34
+ md += `**Objective:** ${phase.objective || 'No objective specified'}\n\n`;
35
+
36
+ for (const item of phase.checklist || []) {
37
+ const check = item.completed ? 'x' : ' ';
38
+ md += `- [${check}] ${item.text}\n`;
39
+ }
40
+ md += '\n';
41
+ }
42
+
43
+ if (roadmap.notes && roadmap.notes.length > 0) {
44
+ md += '---\n\n**Notes:**\n';
45
+ for (const note of roadmap.notes) {
46
+ md += `- ${note}\n`;
47
+ }
48
+ }
49
+
50
+ return md;
51
+ }
52
+
53
+ // Generate markdown from tasks JSON
54
+ function renderTasksMarkdown(phaseNumber, phaseTitle, tasks) {
55
+ if (!tasks || !Array.isArray(tasks)) return `# Phase ${phaseNumber} Tasks\n\nNo tasks defined.`;
56
+
57
+ let md = `# Phase ${phaseNumber} Tasks: ${phaseTitle}\n\n`;
58
+
59
+ for (const task of tasks) {
60
+ const status = task.stage === 'completed' ? ' [COMPLETED]' :
61
+ task.stage === 'in_progress' ? ' [IN PROGRESS]' : '';
62
+ md += `## Task ${task.id}: ${task.title}${status}\n`;
63
+ md += `**Description:** ${task.description || 'No description'}\n\n`;
64
+ md += `**Definition of Done:**\n- ${task.doneDefinition || 'Task completed successfully'}\n\n`;
65
+ md += `**Sanity Check:**\n- ${task.sanityCheck || 'Review the implementation and confirm it meets requirements.'}\n\n`;
66
+ md += '---\n\n';
67
+ }
68
+
69
+ md += '## Checklist Summary\n';
70
+ for (const task of tasks) {
71
+ const check = task.stage === 'completed' ? 'x' : ' ';
72
+ md += `- [${check}] Task ${task.id}: ${task.title}\n`;
73
+ }
74
+
75
+ return md;
76
+ }
77
+
78
+ // Task stage management
79
+ const TASK_STAGES = {
80
+ PENDING: 'pending',
81
+ SECURITY_PRE: 'security_pre',
82
+ TEST_PLANNING: 'test_planning',
83
+ IMPLEMENTING: 'implementing',
84
+ CODE_REVIEW: 'code_review',
85
+ SECURITY_POST: 'security_post',
86
+ SANITY_CHECK: 'sanity_check',
87
+ AWAITING_APPROVAL: 'awaiting_approval',
88
+ COMPLETED: 'completed',
89
+ FAILED: 'failed'
90
+ };
91
+
92
+ function getTaskStage(phaseIndex, taskId) {
93
+ const key = `phase_${phaseIndex}_task_${taskId}_stage`;
94
+ return memory[key] || TASK_STAGES.PENDING;
95
+ }
96
+
97
+ function setTaskStage(phaseIndex, taskId, stage) {
98
+ const key = `phase_${phaseIndex}_task_${taskId}_stage`;
99
+ memory[key] = stage;
100
+ }
101
+
102
+ function getTaskData(phaseIndex, taskId, dataKey) {
103
+ const key = `phase_${phaseIndex}_task_${taskId}_${dataKey}`;
104
+ return memory[key];
105
+ }
106
+
107
+ function setTaskData(phaseIndex, taskId, dataKey, value) {
108
+ const key = `phase_${phaseIndex}_task_${taskId}_${dataKey}`;
109
+ memory[key] = value;
110
+ }
111
+
112
+ export {
113
+ writeMarkdownFile,
114
+ isApproval,
115
+ renderRoadmapMarkdown,
116
+ renderTasksMarkdown,
117
+ TASK_STAGES,
118
+ getTaskStage,
119
+ setTaskStage,
120
+ getTaskData,
121
+ setTaskData
122
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "format": "native",
3
+ "status": "IDLE",
4
+ "memory": {},
5
+ "_pendingInteraction": null,
6
+ "_error": null,
7
+ "startedAt": null,
8
+ "lastUpdatedAt": "__NOW_ISO__"
9
+ }
File without changes
@@ -0,0 +1,5 @@
1
+ {
2
+ "_comment": "Steering configuration",
3
+ "enabled": true,
4
+ "globalPrompt": "global.md"
5
+ }
@@ -0,0 +1,19 @@
1
+ # Global Steering Prompt
2
+
3
+ This content is included with every agent execution in the project-builder workflow.
4
+
5
+ ## Guidelines
6
+
7
+ - Process data carefully and validate inputs
8
+ - Return well-structured JSON when applicable
9
+ - Log meaningful progress messages
10
+ - Handle errors gracefully
11
+
12
+ ## Helpful Tips
13
+ - Scripts live in /workflows/project-builder/scripts/
14
+ - .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.
15
+ - Shell execution via `run_shell_command` is unavailable, so reproduce the script's intended side effect to fulfill the task.
16
+
17
+ ## Notes
18
+
19
+ This file is automatically loaded and passed to every agent in the workflow via `context._steering.global`.