@inneranimalmedia/agentsam-sdk 1.1.0 → 1.2.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.
@@ -1,162 +1,260 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
- export function scaffoldProject({ projectName, lane, provider, agent, cfAccountId }) {
5
- const dir = path.resolve(process.cwd(), projectName);
4
+ const LANE_KEYS = {
5
+ 'Full Stack': 'fullstack',
6
+ CMS: 'cms',
7
+ 'Data Solutions': 'data',
8
+ 'Customer Management': 'crm',
9
+ 'Creative & Design': 'creative',
10
+ };
11
+
12
+ const AGENT_FOR_LANE = {
13
+ fullstack: 'orchestrator',
14
+ cms: 'cms',
15
+ data: 'data',
16
+ crm: 'crm',
17
+ creative: 'creative',
18
+ };
19
+
20
+ function write(file, content) {
21
+ fs.mkdirSync(path.dirname(file), { recursive: true });
22
+ fs.writeFileSync(file, content.trimStart());
23
+ }
24
+
25
+ function slugify(value) {
26
+ return String(value || 'agentsam-project')
27
+ .trim()
28
+ .toLowerCase()
29
+ .replace(/[^a-z0-9-]+/g, '-')
30
+ .replace(/^-+|-+$/g, '') || 'agentsam-project';
31
+ }
32
+
33
+ function workerTemplate({ projectName, laneKey, agent }) {
34
+ return `
35
+ import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
36
+
37
+ export default {
38
+ async fetch(request, env, ctx) {
39
+ const agent = new AgentSam({
40
+ env,
41
+ ctx,
42
+ project: '${projectName}',
43
+ lane: '${laneKey}',
44
+ agent: '${agent}',
45
+ });
46
+
47
+ return agent.handle(request);
48
+ },
49
+ };
50
+ `;
51
+ }
52
+
53
+ function migrationTemplate({ projectName, laneKey }) {
54
+ return `
55
+ -- AgentSam core schema for ${projectName}
56
+ CREATE TABLE IF NOT EXISTS agent_sessions (
57
+ id TEXT PRIMARY KEY,
58
+ agent TEXT NOT NULL,
59
+ lane TEXT NOT NULL,
60
+ goal TEXT,
61
+ status TEXT NOT NULL DEFAULT 'created',
62
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
63
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
64
+ );
65
+
66
+ CREATE TABLE IF NOT EXISTS agent_messages (
67
+ id TEXT PRIMARY KEY,
68
+ session_id TEXT NOT NULL,
69
+ role TEXT NOT NULL,
70
+ content TEXT NOT NULL,
71
+ metadata_json TEXT,
72
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
73
+ FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
74
+ );
75
+
76
+ CREATE TABLE IF NOT EXISTS agent_tool_calls (
77
+ id TEXT PRIMARY KEY,
78
+ session_id TEXT NOT NULL,
79
+ tool_name TEXT NOT NULL,
80
+ status TEXT NOT NULL DEFAULT 'queued',
81
+ input_json TEXT,
82
+ output_json TEXT,
83
+ requires_approval INTEGER NOT NULL DEFAULT 0,
84
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
85
+ completed_at TEXT,
86
+ FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
87
+ );
88
+
89
+ ${laneKey === 'cms' ? `
90
+ CREATE TABLE IF NOT EXISTS cms_pages (
91
+ id TEXT PRIMARY KEY,
92
+ slug TEXT NOT NULL UNIQUE,
93
+ title TEXT NOT NULL,
94
+ status TEXT NOT NULL DEFAULT 'draft',
95
+ hero_asset_key TEXT,
96
+ content_json TEXT NOT NULL DEFAULT '{}',
97
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
98
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
99
+ );
100
+
101
+ CREATE TABLE IF NOT EXISTS cms_assets (
102
+ id TEXT PRIMARY KEY,
103
+ r2_key TEXT NOT NULL UNIQUE,
104
+ title TEXT,
105
+ alt_text TEXT,
106
+ metadata_json TEXT,
107
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
108
+ );
109
+ ` : ''}
110
+ `;
111
+ }
112
+
113
+ function readmeTemplate({ projectName, lane, provider, agent }) {
114
+ return `
115
+ # ${projectName}
116
+
117
+ Scaffolded with [@inneranimalmedia/agentsam-sdk](https://www.npmjs.com/package/@inneranimalmedia/agentsam-sdk).
118
+
119
+ ## Lane
120
+
121
+ **${lane}** — ${agent} agent, ${provider}
122
+
123
+ ## Setup
124
+
125
+ \`\`\`bash
126
+ cp .env.example .env
127
+ npm install
128
+ \`\`\`
129
+
130
+ ## Dev
131
+
132
+ \`\`\`bash
133
+ npm run dev
134
+ npm run smoke
135
+ \`\`\`
136
+
137
+ ## Deploy
138
+
139
+ \`\`\`bash
140
+ npm run deploy
141
+ \`\`\`
142
+
143
+ ## Endpoints
144
+
145
+ - \`GET /api/health\`
146
+ - \`GET /api/agentsam/info\`
147
+ - \`POST /api/agentsam/session\`
148
+ - \`POST /api/agentsam/message\`
149
+ `;
150
+ }
151
+
152
+ export function scaffoldProject({
153
+ projectName,
154
+ lane,
155
+ provider,
156
+ agent,
157
+ cfAccountId,
158
+ sdkVersion = '1.1.1',
159
+ }) {
160
+ const safeName = slugify(projectName);
161
+ const dir = path.resolve(process.cwd(), safeName);
162
+ const laneKey = LANE_KEYS[lane] ?? 'fullstack';
163
+ const selectedAgent = agent || AGENT_FOR_LANE[laneKey] || 'orchestrator';
164
+ const sdkRange = `^${sdkVersion.split('.').slice(0, 2).join('.')}.0`;
6
165
 
7
166
  if (fs.existsSync(dir)) {
8
- console.error(`\n ✗ Directory "${projectName}" already exists.\n`);
9
- process.exit(1);
167
+ throw new Error(`Directory "${safeName}" already exists.`);
10
168
  }
11
169
 
12
170
  fs.mkdirSync(dir, { recursive: true });
13
171
  fs.mkdirSync(`${dir}/src`, { recursive: true });
172
+ fs.mkdirSync(`${dir}/migrations`, { recursive: true });
14
173
 
15
- // agentsam.config.js
16
- fs.writeFileSync(`${dir}/agentsam.config.js`, `
174
+ write(`${dir}/agentsam.config.js`, `
17
175
  export default {
18
- project: '${projectName}',
19
- lane: '${lane}',
176
+ project: '${safeName}',
177
+ lane: '${laneKey}',
20
178
  provider: '${provider}',
21
- agent: '${agent}',
179
+ agent: '${selectedAgent}',
22
180
  cloudflare: {
23
181
  accountId: '${cfAccountId || ''}',
24
182
  },
25
183
  api: {
26
- baseUrl: 'https://api.inneranimalmedia.com',
184
+ baseUrl: '/api/agentsam',
27
185
  },
28
186
  };
29
- `.trimStart());
187
+ `);
30
188
 
31
- // .env.example
32
- fs.writeFileSync(`${dir}/.env.example`, `
189
+ write(`${dir}/.env.example`, `
33
190
  AGENTSAM_API_KEY=
34
191
  CLOUDFLARE_ACCOUNT_ID=${cfAccountId || ''}
35
192
  CLOUDFLARE_API_TOKEN=
36
- `.trimStart());
193
+ `);
37
194
 
38
- // .gitignore
39
- fs.writeFileSync(`${dir}/.gitignore`, `
195
+ write(`${dir}/.gitignore`, `
40
196
  node_modules/
41
197
  .env
42
198
  .dev.vars
43
199
  dist/
44
- `.trimStart());
200
+ .wrangler/
201
+ `);
45
202
 
46
- // package.json
47
- fs.writeFileSync(`${dir}/package.json`, JSON.stringify({
48
- name: projectName,
203
+ write(`${dir}/package.json`, `${JSON.stringify({
204
+ name: safeName,
49
205
  version: '0.1.0',
50
206
  type: 'module',
207
+ private: true,
51
208
  scripts: {
52
209
  dev: 'wrangler dev',
53
210
  deploy: 'wrangler deploy',
211
+ smoke: 'node ./scripts/smoke.mjs',
54
212
  },
55
213
  dependencies: {
56
- '@inneranimalmedia/agentsam-sdk': '^1.0.0',
214
+ '@inneranimalmedia/agentsam-sdk': sdkRange,
57
215
  },
58
- }, null, 2));
59
-
60
- // Lane-specific files
61
- if (lane === 'Full Stack' || lane === 'Data Solutions') {
62
- fs.mkdirSync(`${dir}/migrations`, { recursive: true });
63
- fs.writeFileSync(`${dir}/migrations/0001_init.sql`, `
64
- -- Initial schema for ${projectName}
65
- CREATE TABLE IF NOT EXISTS agent_sessions (
66
- id TEXT PRIMARY KEY,
67
- user_id TEXT NOT NULL,
68
- created_at INTEGER DEFAULT (unixepoch())
69
- );
70
- `.trimStart());
71
- }
216
+ devDependencies: {
217
+ wrangler: '^4.0.0',
218
+ },
219
+ }, null, 2)}\n`);
72
220
 
73
- if (lane === 'Full Stack' || provider === 'Cloudflare Workers' || provider === 'GitHub + Cloudflare') {
74
- fs.writeFileSync(`${dir}/wrangler.toml`, `
75
- name = "${projectName}"
221
+ write(`${dir}/wrangler.toml`, `
222
+ name = "${safeName}"
76
223
  main = "src/index.js"
77
- compatibility_date = "2025-01-01"
224
+ compatibility_date = "2026-06-27"
78
225
 
79
226
  [[d1_databases]]
80
227
  binding = "DB"
81
- database_name = "${projectName}-db"
82
- database_id = ""
83
- `.trimStart());
84
- }
228
+ database_name = "${safeName}-db"
229
+ database_id = "REPLACE_WITH_D1_DATABASE_ID"
85
230
 
86
- // src/index.js — lane-specific worker
87
- const workers = {
88
- 'Full Stack': `
89
- import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
231
+ [[kv_namespaces]]
232
+ binding = "KV"
233
+ id = "REPLACE_WITH_KV_NAMESPACE_ID"
90
234
 
91
- export default {
92
- async fetch(request, env, ctx) {
93
- const agent = new AgentSam({ env, agent: 'orchestrator' });
94
- return agent.handle(request);
95
- },
96
- };
97
- `,
98
- 'Data Solutions': `
99
- import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
235
+ [[r2_buckets]]
236
+ binding = "R2"
237
+ bucket_name = "${safeName}"
238
+ `);
100
239
 
101
- export default {
102
- async fetch(request, env, ctx) {
103
- const agent = new AgentSam({ env, agent: 'data' });
104
- return agent.handle(request);
105
- },
106
- };
107
- `,
108
- 'Customer Management': `
109
- import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
240
+ write(`${dir}/migrations/0001_agentsam_core.sql`, migrationTemplate({ projectName: safeName, laneKey }));
241
+ write(`${dir}/src/index.js`, workerTemplate({ projectName: safeName, laneKey, agent: selectedAgent }));
242
+ write(`${dir}/README.md`, readmeTemplate({ projectName: safeName, lane, provider, agent: selectedAgent }));
110
243
 
111
- export default {
112
- async fetch(request, env, ctx) {
113
- const agent = new AgentSam({ env, agent: 'crm' });
114
- return agent.handle(request);
115
- },
116
- };
117
- `,
118
- 'Creative & Design': `
244
+ write(`${dir}/scripts/smoke.mjs`, `
119
245
  import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
120
246
 
121
- export default {
122
- async fetch(request, env, ctx) {
123
- const agent = new AgentSam({ env, agent: 'creative' });
124
- return agent.handle(request);
125
- },
126
- };
127
- `,
128
- };
129
-
130
- fs.writeFileSync(`${dir}/src/index.js`, workers[lane].trimStart());
131
-
132
- // README
133
- fs.writeFileSync(`${dir}/README.md`, `
134
- # ${projectName}
135
-
136
- Scaffolded with [Agent Sam SDK](https://www.npmjs.com/package/@inneranimalmedia/agentsam-sdk).
247
+ const app = new AgentSam({ project: '${safeName}', lane: '${laneKey}', agent: '${selectedAgent}' });
248
+ const res = await app.handle(new Request('https://example.com/api/health'));
249
+ const data = await res.json();
137
250
 
138
- ## Lane
139
- **${lane}** — ${agent} agent, ${provider}
140
-
141
- ## Setup
142
-
143
- \`\`\`bash
144
- cp .env.example .env
145
- npm install
146
- \`\`\`
147
-
148
- ## Dev
149
-
150
- \`\`\`bash
151
- npm run dev
152
- \`\`\`
153
-
154
- ## Deploy
251
+ if (!data.ok) {
252
+ console.error(data);
253
+ process.exit(1);
254
+ }
155
255
 
156
- \`\`\`bash
157
- npm run deploy
158
- \`\`\`
159
- `.trimStart());
256
+ console.log('AgentSam smoke test passed:', data);
257
+ `);
160
258
 
161
259
  return dir;
162
260
  }
@@ -0,0 +1,53 @@
1
+ export async function createSession({ env, agent, lane, goal }) {
2
+ const now = new Date().toISOString();
3
+ const session = {
4
+ id: crypto.randomUUID(),
5
+ agent,
6
+ lane,
7
+ goal: goal ?? null,
8
+ status: 'created',
9
+ created_at: now,
10
+ updated_at: now,
11
+ };
12
+
13
+ if (env?.KV?.put) {
14
+ await env.KV.put(`agentsam:session:${session.id}`, JSON.stringify(session), { expirationTtl: 60 * 60 * 24 * 7 });
15
+ }
16
+
17
+ if (env?.DB?.prepare) {
18
+ await env.DB.prepare(
19
+ `CREATE TABLE IF NOT EXISTS agent_sessions (
20
+ id TEXT PRIMARY KEY,
21
+ agent TEXT NOT NULL,
22
+ lane TEXT NOT NULL,
23
+ goal TEXT,
24
+ status TEXT NOT NULL,
25
+ created_at TEXT NOT NULL,
26
+ updated_at TEXT NOT NULL
27
+ )`
28
+ ).run();
29
+
30
+ await env.DB.prepare(
31
+ `INSERT INTO agent_sessions (id, agent, lane, goal, status, created_at, updated_at)
32
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
33
+ ).bind(session.id, session.agent, session.lane, session.goal, session.status, session.created_at, session.updated_at).run();
34
+ }
35
+
36
+ return session;
37
+ }
38
+
39
+ export async function getSession({ env, sessionId }) {
40
+ if (!sessionId) return null;
41
+
42
+ if (env?.KV?.get) {
43
+ const raw = await env.KV.get(`agentsam:session:${sessionId}`);
44
+ if (raw) return JSON.parse(raw);
45
+ }
46
+
47
+ if (env?.DB?.prepare) {
48
+ const row = await env.DB.prepare('SELECT * FROM agent_sessions WHERE id = ?').bind(sessionId).first();
49
+ if (row) return row;
50
+ }
51
+
52
+ return null;
53
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Canonical slash-command surface for Agent Sam SDK CLI / shell UX.
3
+ * Consumed by the gorilla-shell example and future `agentsam shell` PTY bridge.
4
+ */
5
+
6
+ export const SHELL_THEMES = ['NIGHT', 'DAY', 'LAVA', 'VOID'];
7
+
8
+ /** @type {Array<{ cmd: string, description: string, lane?: string }>} */
9
+ export const SLASH_COMMANDS = [
10
+ { cmd: '/cd', description: 'Change working directory', lane: 'terminal' },
11
+ { cmd: '/pwd', description: 'Print working directory', lane: 'terminal' },
12
+ { cmd: '/gh', description: 'GitHub operations (status, PR, issue)', lane: 'git' },
13
+ { cmd: '/wrangler', description: 'Wrangler deploy and bindings', lane: 'deploy' },
14
+ { cmd: '/workspace', description: 'Switch or show active workspace', lane: 'platform' },
15
+ { cmd: '/samiam', description: 'Agent Sam quick invoke', lane: 'agent' },
16
+ { cmd: '/claude', description: 'Route prompt to Claude arm', lane: 'agent' },
17
+ { cmd: '/codex', description: 'Route prompt to Codex arm', lane: 'agent' },
18
+ { cmd: '/tail', description: 'Tail worker logs', lane: 'observability' },
19
+ { cmd: '/d1', description: 'D1 query helper', lane: 'data' },
20
+ { cmd: '/deploy', description: 'Deploy worker (sandbox or prod gate)', lane: 'deploy' },
21
+ { cmd: '/buddy', description: 'Open Agent Sam buddy panel (MCP)', lane: 'agent' },
22
+ { cmd: '/status', description: 'Platform + project health', lane: 'platform' },
23
+ { cmd: '/diagnostics', description: 'Run diagnostic scenario', lane: 'platform' },
24
+ ];
25
+
26
+ /** Shell UX rollout phases (gorilla-shell → SDK default CLI experience). */
27
+ export const SHELL_PHASES = [
28
+ { id: '0-prototype', label: 'Visual prototype + demo scenarios', status: 'complete' },
29
+ { id: 'pty-connection', label: 'ExecOS / iam-pty WebSocket bridge', status: 'next' },
30
+ { id: 'hud-layer', label: 'Quest log, tool gate, XP HUD', status: 'planned' },
31
+ { id: 'buddy-system', label: 'In-shell Agent Sam via MCP', status: 'planned' },
32
+ { id: 'dashboard-embed', label: 'Embeddable shell for IAM dashboard', status: 'planned' },
33
+ { id: 'standalone-pwa', label: 'Installable PWA / SDK default shell', status: 'planned' },
34
+ ];
35
+
36
+ export function listSlashCommands(opts = {}) {
37
+ const lane = opts.lane ? String(opts.lane).trim().toLowerCase() : '';
38
+ if (!lane) return [...SLASH_COMMANDS];
39
+ return SLASH_COMMANDS.filter((row) => !row.lane || row.lane === lane);
40
+ }
@@ -0,0 +1,38 @@
1
+ const CATALOG = {
2
+ fullstack: [
3
+ { name: 'plan', description: 'Break a goal into implementation steps.' },
4
+ { name: 'code', description: 'Generate or revise application code.' },
5
+ { name: 'deploy', description: 'Prepare deployment steps and checks.' },
6
+ ],
7
+ cms: [
8
+ { name: 'page', description: 'Create and revise pages.' },
9
+ { name: 'asset', description: 'Attach cover images and media.' },
10
+ { name: 'publish', description: 'Draft, preview, and publish content.' },
11
+ ],
12
+ data: [
13
+ { name: 'schema', description: 'Inspect and plan database schemas.' },
14
+ { name: 'query', description: 'Draft safe SQL queries.' },
15
+ { name: 'migration', description: 'Plan D1 or Postgres migrations.' },
16
+ ],
17
+ crm: [
18
+ { name: 'contact', description: 'Organize contacts and accounts.' },
19
+ { name: 'workflow', description: 'Coordinate customer workflows.' },
20
+ ],
21
+ creative: [
22
+ { name: 'brief', description: 'Turn creative intent into a production brief.' },
23
+ { name: 'asset', description: 'Plan media, 3D, and design assets.' },
24
+ ],
25
+ };
26
+
27
+ export function normalizeLane(lane = 'fullstack') {
28
+ return String(lane).toLowerCase().replace(/\s+/g, '').replace('&', '');
29
+ }
30
+
31
+ export function getToolCatalog(lane = 'fullstack') {
32
+ const key = normalizeLane(lane);
33
+ if (key.includes('cms')) return CATALOG.cms;
34
+ if (key.includes('data')) return CATALOG.data;
35
+ if (key.includes('customer') || key.includes('crm')) return CATALOG.crm;
36
+ if (key.includes('creative') || key.includes('design')) return CATALOG.creative;
37
+ return CATALOG.fullstack;
38
+ }
@@ -0,0 +1,2 @@
1
+ export const LANES = ['fullstack', 'cms', 'data', 'crm', 'creative'];
2
+ export const AGENTS = ['orchestrator', 'cms', 'data', 'crm', 'creative'];
package/test/smoke.mjs ADDED
@@ -0,0 +1,42 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { AgentSam, routeIntent, getToolCatalog } from '../src/index.js';
6
+ import { scaffoldProject } from '../src/lib/scaffold.js';
7
+
8
+ const app = new AgentSam({ project: 'smoke', lane: 'cms', agent: 'cms' });
9
+ let res = await app.handle(new Request('https://example.com/api/health'));
10
+ assert.equal(res.status, 200);
11
+ assert.equal((await res.json()).ok, true);
12
+
13
+ res = await app.handle(new Request('https://example.com/api/agentsam/info'));
14
+ const info = await res.json();
15
+ assert.equal(info.agent, 'cms');
16
+ assert.ok(info.capabilities.includes('page'));
17
+
18
+ res = await app.handle(new Request('https://example.com/api/agentsam/message', {
19
+ method: 'POST',
20
+ body: JSON.stringify({ message: 'create an analytics page in the cms' }),
21
+ }));
22
+ const message = await res.json();
23
+ assert.equal(message.agent, 'cms');
24
+ assert.equal(message.intent, 'cms_build');
25
+
26
+ assert.equal(routeIntent({ message: 'drop table users' }).requires_approval, true);
27
+ assert.ok(getToolCatalog('Data Solutions').some((tool) => tool.name === 'query'));
28
+
29
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-sdk-test-'));
30
+ const cwd = process.cwd();
31
+ process.chdir(tmp);
32
+ const dir = scaffoldProject({ projectName: 'CMS Demo', lane: 'CMS', provider: 'Cloudflare Workers', agent: 'cms' });
33
+ process.chdir(cwd);
34
+ assert.ok(fs.existsSync(path.join(dir, 'src/index.js')));
35
+ assert.ok(fs.readFileSync(path.join(dir, 'migrations/0001_agentsam_core.sql'), 'utf8').includes('cms_pages'));
36
+ assert.ok(fs.readFileSync(path.join(dir, 'package.json'), 'utf8').includes('wrangler'));
37
+
38
+ import { SLASH_COMMANDS, listSlashCommands } from '../src/lib/slash-commands.js';
39
+ assert.ok(SLASH_COMMANDS.some((c) => c.cmd === '/deploy'));
40
+ assert.equal(listSlashCommands({ lane: 'deploy' }).length, 2);
41
+
42
+ console.log('SDK smoke tests passed');