@inneranimalmedia/agentsam-sdk 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DEVELOPMENT.md ADDED
@@ -0,0 +1,55 @@
1
+ # Developing agentsam-sdk with Inner Animal Media
2
+
3
+ ## Local smoke test
4
+
5
+ ```bash
6
+ cd agentsam-sdk
7
+ npm test
8
+ ```
9
+
10
+ ## Link into a scaffolded project
11
+
12
+ ```bash
13
+ cd agentsam-sdk && npm link
14
+ cd /path/to/your-project && npm link @inneranimalmedia/agentsam-sdk
15
+ npm run dev
16
+ ```
17
+
18
+ ## Link into inneranimalmedia (monorepo-style)
19
+
20
+ From the platform repo:
21
+
22
+ ```bash
23
+ cd inneranimalmedia
24
+ npm link ../agentsam-sdk
25
+ ```
26
+
27
+ Then in any Worker or script:
28
+
29
+ ```js
30
+ import { AgentSam } from '@inneranimalmedia/agentsam-sdk';
31
+ ```
32
+
33
+ ## Non-interactive init (CI / smoke)
34
+
35
+ ```bash
36
+ npx agentsam init \
37
+ --name my-agent \
38
+ --lane fullstack \
39
+ --provider cloudflare \
40
+ --agent orchestrator \
41
+ --yes
42
+ ```
43
+
44
+ ## Publish checklist
45
+
46
+ 1. `npm test` passes
47
+ 2. Bump version in `package.json`
48
+ 3. `npm publish --access public`
49
+ 4. Tag: `git tag v1.1.1 && git push origin v1.1.1`
50
+
51
+ ## Known gaps (roadmap)
52
+
53
+ - `agentsam deploy`, `status`, `logs` — not implemented yet
54
+ - CMS lane in README — CLI has 4 lanes today (Full Stack, Data, CRM, Creative)
55
+ - Published API route may differ; stub mode works without `AGENTSAM_API_KEY`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Inner Animal Media
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # Agent Sam SDK
2
+
3
+ > The AI agent layer for developers who need more than a chatbot.
4
+
5
+ Agent Sam is a full-stack autonomous agent SDK built on Cloudflare Workers, D1, Supabase, Durable Objects, and MCP — designed to converse, plan, and execute real work across your entire stack. CMS websites, full-stack applications, data pipelines, creative workflows, terminal execution, deployments, and multi-step agentic pipelines — all through one unified agent interface.
6
+
7
+ ---
8
+
9
+ ## What is Agent Sam?
10
+
11
+ Agent Sam is a platform operator AI. It sits on top of your infrastructure and can:
12
+
13
+ - **Understand intent** — natural language in, real actions out
14
+ - **Execute across surfaces** — terminal, database, browser, CAD, deploy, MCP tools
15
+ - **Route intelligently** — local machine, cloud VM, or sandboxed environment based on context
16
+ - **Gate high-risk actions** — approval flows before anything destructive runs
17
+ - **Leave an audit trail** — every tool call, command run, and decision is logged
18
+
19
+ It is not a wrapper around a chat API. It is a command fabric with a conversation interface.
20
+
21
+ ---
22
+
23
+ ## Quickstart
24
+
25
+ \`\`\`bash
26
+ npx @inneranimalmedia/agentsam-sdk init
27
+ \`\`\`
28
+
29
+ Non-interactive (CI / scripts):
30
+
31
+ \`\`\`bash
32
+ npx @inneranimalmedia/agentsam-sdk init \\
33
+ --name my-agent \\
34
+ --lane fullstack \\
35
+ --provider cloudflare \\
36
+ --agent orchestrator \\
37
+ --yes
38
+ \`\`\`
39
+
40
+ Scaffolded workers expose \`GET /health\` and \`POST /chat\` immediately (stub mode without an API key).
41
+
42
+ See [DEVELOPMENT.md](./DEVELOPMENT.md) for linking the SDK into Inner Animal Media locally.
43
+
44
+ ---
45
+
46
+ ## Installation
47
+
48
+ \`\`\`bash
49
+ npm install @inneranimalmedia/agentsam-sdk
50
+ \`\`\`
51
+
52
+ ---
53
+
54
+ ## Lanes
55
+
56
+ | Lane | Best For |
57
+ |------|----------|
58
+ | **Full Stack** | End-to-end apps — agent chat, terminal, deploy, D1, R2, Durable Objects, KV |
59
+ | **CMS** | Content-managed websites — pages, assets, themes, navigation, live edit |
60
+ | **Data Solutions** | Database ops, migrations, queries, Supabase pgvector, Hyperdrive pipelines |
61
+ | **Customer Management** | CRM, contacts, billing, client workflows, multi-tenant isolation |
62
+ | **Creative & Design** | CAD, 3D, media generation, content pipelines |
63
+
64
+ ---
65
+
66
+ ## Agents
67
+
68
+ | Agent | Role |
69
+ |-------|------|
70
+ | **Orchestrator** | General purpose — routes across all lanes and tools |
71
+ | **CMS Agent** | Pages, sections, assets, themes, publishing workflows |
72
+ | **Data Agent** | D1, Supabase, Hyperdrive, migrations, vector search |
73
+ | **CRM Agent** | Customer records, contacts, billing, client isolation |
74
+ | **Creative Agent** | Design commands, 3D generation, CAD, media pipelines |
75
+
76
+ ---
77
+
78
+ ## How It Works
79
+
80
+ \`\`\`
81
+ User Intent (chat or CLI)
82
+
83
+ Agent Sam — intent classification + command match
84
+
85
+ Tool Catalog (D1) — policy check + approval gate
86
+
87
+ Execution — terminal / D1 / Supabase / R2 / KV / DO / browser / deploy / MCP
88
+
89
+ Telemetry — every action logged, measured, improvable
90
+ \`\`\`
91
+
92
+ Capabilities are data-driven — new tools are added via D1, not Worker redeployments. The same tool catalog powers the dashboard, the CLI, and any MCP-connected client like Cursor or Claude Desktop.
93
+
94
+ ---
95
+
96
+ ## Infrastructure
97
+
98
+ Agent Sam scaffolds and operates across the full Cloudflare + Supabase stack:
99
+
100
+ | Layer | Technology | Role |
101
+ |-------|------------|------|
102
+ | **Compute** | Cloudflare Workers | Edge runtime, API, agent dispatch |
103
+ | **Relational DB** | D1 (SQLite) | Tool catalog, sessions, telemetry, CMS, auth |
104
+ | **Vector DB** | Supabase pgvector via Hyperdrive | RAG, semantic search, agent memory |
105
+ | **Object Storage** | R2 | Assets, media, bundles, CMS content |
106
+ | **Key-Value** | Workers KV | Cache, CMS drafts, feature flags |
107
+ | **Stateful Sessions** | Durable Objects | Terminal sessions, collab, real-time state |
108
+ | **Terminal** | ExecOS over cloudflared tunnel | Shell execution, deploy, git, wrangler |
109
+ | **AI Router** | Anthropic + OpenAI | Adaptive Thompson sampling across models |
110
+ | **Protocol** | MCP (Model Context Protocol) | External agent surface for Cursor, Claude, etc. |
111
+
112
+ ---
113
+
114
+ ## Execution Lanes
115
+
116
+ Agent Sam routes work to the right environment automatically:
117
+
118
+ | Lane | Environment | When |
119
+ |------|-------------|------|
120
+ | Local | Your machine | Fastest dev loop |
121
+ | Cloud | Always-on VM via tunnel | Machine asleep or offsite |
122
+ | Sandbox | Isolated workspace | Safe experiments, tenant isolation |
123
+
124
+ ---
125
+
126
+ ## What Gets Scaffolded
127
+
128
+ Running \`agentsam init\` generates a production-ready project for your lane:
129
+
130
+ **All lanes include:**
131
+ - \`agentsam.config.js\` — project config, lane, provider, agent
132
+ - \`wrangler.toml\` — Worker, D1, R2, KV, Durable Object bindings
133
+ - \`.env.example\` — all required secrets pre-listed
134
+ - \`src/index.js\` — Worker entry point wired to your agent
135
+ - \`README.md\` — setup and deploy instructions
136
+
137
+ **CMS lane adds:**
138
+ - Page, section, asset, and theme schema migrations
139
+ - CMS worker with live edit, draft/publish, and R2 asset pipeline
140
+
141
+ **Data lane adds:**
142
+ - D1 + Supabase Hyperdrive connection config
143
+ - Vector embedding pipeline scaffold
144
+ - Migration templates for core data models
145
+
146
+ **Full Stack adds:**
147
+ - Durable Object session scaffold
148
+ - Auth tables and session management
149
+ - Full agent chat + tool loop worker
150
+
151
+ ---
152
+
153
+ ## Multi-Tenant & Client Policy
154
+
155
+ Agent Sam is built for isolation from the ground up:
156
+
157
+ - Each user or client gets a scoped workspace
158
+ - Terminal execution is path-isolated — no cross-tenant access
159
+ - AI usage is policy-gated — BYOK, managed, or disabled per client
160
+ - D1 and R2 are scoped per tenant at the binding level
161
+ - Every action produces an audit trail
162
+
163
+ ---
164
+
165
+ ## Roadmap
166
+
167
+ - [ ] \`agentsam deploy\` — push your project from CLI
168
+ - [ ] \`agentsam status\` — live agent and infrastructure health
169
+ - [ ] \`agentsam logs\` — tail tool call and command logs
170
+ - [ ] \`agentsam kit\` — install pre-built capability kits
171
+ - [ ] Kit marketplace — CMS, ecommerce, nonprofit, SaaS starters
172
+ - [ ] BYOK AI key support per project
173
+
174
+ ---
175
+
176
+ ## Built By
177
+
178
+ [Inner Animal Media](https://inneranimalmedia.com) — Agent Sam is the operator brain behind the Inner Animal Media platform. The SDK is how we share that infrastructure with other developers.
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ MIT
package/package.json CHANGED
@@ -1,14 +1,33 @@
1
1
  {
2
2
  "name": "@inneranimalmedia/agentsam-sdk",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Agent Sam is a full-stack AI agent SDK for autonomous task execution — covering data management, creative workflows, design commands, and multi-step agentic pipelines.",
5
- "main": "src/index.js",
6
5
  "type": "module",
6
+ "main": "./src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./package.json": "./package.json"
10
+ },
7
11
  "bin": {
8
- "agentsam": "src/cli.js"
12
+ "agentsam": "./src/cli.js"
9
13
  },
14
+ "files": [
15
+ "src",
16
+ "test",
17
+ "README.md",
18
+ "LICENSE",
19
+ "DEVELOPMENT.md"
20
+ ],
10
21
  "scripts": {
11
- "test": "node src/index.js"
22
+ "test": "node test/smoke.mjs",
23
+ "smoke": "node test/smoke.mjs",
24
+ "prepublishOnly": "npm test"
25
+ },
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
12
31
  },
13
32
  "repository": {
14
33
  "type": "git",
@@ -0,0 +1,67 @@
1
+ import { json, notFound, readJson } from './lib/responses.js';
2
+ import { createSession, getSession } from './lib/sessions.js';
3
+ import { routeIntent } from './lib/router.js';
4
+ import { getToolCatalog } from './lib/tools.js';
5
+
6
+ export class AgentSam {
7
+ constructor(options = {}) {
8
+ this.env = options.env ?? {};
9
+ this.agent = options.agent ?? 'orchestrator';
10
+ this.lane = options.lane ?? 'fullstack';
11
+ this.project = options.project ?? 'agentsam-project';
12
+ }
13
+
14
+ async handle(request) {
15
+ const url = new URL(request.url);
16
+ const path = url.pathname.replace(/\/$/, '') || '/';
17
+
18
+ if (request.method === 'GET' && path === '/api/health') {
19
+ return json({ ok: true, service: 'AgentSam', agent: this.agent, lane: this.lane, status: 'online' });
20
+ }
21
+
22
+ if (request.method === 'GET' && path === '/api/agentsam/info') {
23
+ return json({
24
+ ok: true,
25
+ name: 'AgentSam SDK',
26
+ project: this.project,
27
+ agent: this.agent,
28
+ lane: this.lane,
29
+ capabilities: getToolCatalog(this.lane).map((tool) => tool.name),
30
+ });
31
+ }
32
+
33
+ if (request.method === 'POST' && path === '/api/agentsam/session') {
34
+ const body = await readJson(request);
35
+ const session = await createSession({ env: this.env, agent: this.agent, lane: this.lane, goal: body.goal });
36
+ return json({ ok: true, session });
37
+ }
38
+
39
+ if (request.method === 'GET' && path.startsWith('/api/agentsam/session/')) {
40
+ const sessionId = path.split('/').pop();
41
+ const session = await getSession({ env: this.env, sessionId });
42
+ if (!session) return json({ ok: false, error: 'session_not_found' }, 404);
43
+ return json({ ok: true, session });
44
+ }
45
+
46
+ if (request.method === 'POST' && path === '/api/agentsam/message') {
47
+ const body = await readJson(request);
48
+ const result = routeIntent({
49
+ message: body.message ?? body.goal ?? '',
50
+ agent: body.agent ?? this.agent,
51
+ lane: body.lane ?? this.lane,
52
+ });
53
+
54
+ return json({
55
+ ok: true,
56
+ session_id: body.session_id ?? null,
57
+ agent: result.agent,
58
+ lane: result.lane,
59
+ intent: result.intent,
60
+ next_steps: result.next_steps,
61
+ requires_approval: result.requires_approval,
62
+ });
63
+ }
64
+
65
+ return notFound();
66
+ }
67
+ }
package/src/cli.js CHANGED
@@ -1,16 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import readline from 'readline';
4
+ import pkg from '../package.json' with { type: 'json' };
4
5
  import { scaffoldProject } from './lib/scaffold.js';
5
6
 
6
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7
- const ask = (q) => new Promise(resolve => rl.question(q, resolve));
7
+ const VERSION = pkg.version;
8
+
9
+ function createPrompt() {
10
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11
+ return {
12
+ ask: (q) => new Promise((resolve) => rl.question(q, resolve)),
13
+ close: () => rl.close(),
14
+ };
15
+ }
8
16
 
9
17
  const LANES = {
10
18
  '1': 'Full Stack',
11
- '2': 'Data Solutions',
12
- '3': 'Customer Management',
13
- '4': 'Creative & Design',
19
+ '2': 'CMS',
20
+ '3': 'Data Solutions',
21
+ '4': 'Customer Management',
22
+ '5': 'Creative & Design',
23
+ };
24
+
25
+ const LANE_BY_SLUG = {
26
+ fullstack: 'Full Stack',
27
+ cms: 'CMS',
28
+ data: 'Data Solutions',
29
+ crm: 'Customer Management',
30
+ creative: 'Creative & Design',
14
31
  };
15
32
 
16
33
  const PROVIDERS = {
@@ -19,61 +36,88 @@ const PROVIDERS = {
19
36
  '3': 'Local / Self-hosted',
20
37
  };
21
38
 
39
+ const PROVIDER_BY_SLUG = {
40
+ cloudflare: 'Cloudflare Workers',
41
+ github: 'GitHub + Cloudflare',
42
+ local: 'Local / Self-hosted',
43
+ };
44
+
22
45
  const AGENTS = {
23
46
  '1': 'orchestrator',
24
- '2': 'data',
25
- '3': 'crm',
26
- '4': 'creative',
47
+ '2': 'cms',
48
+ '3': 'data',
49
+ '4': 'crm',
50
+ '5': 'creative',
27
51
  };
28
52
 
29
- async function init() {
53
+ function laneKeyFromLabel(label) {
54
+ const entry = Object.entries(LANES).find(([, v]) => v === label);
55
+ return entry?.[0] ?? '1';
56
+ }
57
+
58
+ function printHelp() {
30
59
  console.log(`
31
- ╔═══════════════════════════════════╗
32
- ║ Agent Sam SDK — Init ║
33
- ╚═══════════════════════════════════╝
34
- `);
60
+ Agent Sam SDK — CLI v${VERSION}
35
61
 
36
- const projectName = await ask(' Project name: ');
62
+ Usage:
63
+ agentsam init [options] Scaffold a new Agent Sam project
64
+ agentsam --version Print version
65
+ agentsam --help Show this help
37
66
 
38
- console.log(`
39
- Select a lane:
40
- 1) Full Stack
41
- 2) Data Solutions
42
- 3) Customer Management
43
- 4) Creative & Design
67
+ Init options (non-interactive):
68
+ --name <name> Project directory name
69
+ --lane <fullstack|cms|data|crm|creative>
70
+ --provider <cloudflare|github|local>
71
+ --agent <orchestrator|cms|data|crm|creative>
72
+ --cf-account <id> Cloudflare account ID
73
+ --yes Skip confirmation prompt
44
74
  `);
45
- const laneKey = await ask(' Lane [1-4]: ');
46
- const lane = LANES[laneKey] ?? 'Full Stack';
75
+ }
47
76
 
48
- let provider = 'Cloudflare Workers';
49
- if (laneKey === '1') {
50
- console.log(`
51
- Select a provider:
52
- 1) Cloudflare Workers
53
- 2) GitHub + Cloudflare
54
- 3) Local / Self-hosted
55
- `);
56
- const providerKey = await ask(' Provider [1-3]: ');
57
- provider = PROVIDERS[providerKey] ?? 'Cloudflare Workers';
77
+ function parseInitArgs(argv) {
78
+ const opts = {
79
+ projectName: '',
80
+ lane: '',
81
+ provider: '',
82
+ agent: '',
83
+ cfAccountId: '',
84
+ yes: false,
85
+ };
86
+
87
+ for (let i = 0; i < argv.length; i += 1) {
88
+ const arg = argv[i];
89
+ if (arg === '--yes' || arg === '-y') opts.yes = true;
90
+ else if (arg === '--name') opts.projectName = argv[++i] || '';
91
+ else if (arg === '--lane') {
92
+ const slug = argv[++i] || '';
93
+ opts.lane = LANE_BY_SLUG[slug] || slug;
94
+ } else if (arg === '--provider') {
95
+ const slug = argv[++i] || '';
96
+ opts.provider = PROVIDER_BY_SLUG[slug] || slug;
97
+ } else if (arg === '--agent') opts.agent = argv[++i] || '';
98
+ else if (arg === '--cf-account') opts.cfAccountId = argv[++i] || '';
58
99
  }
59
100
 
60
- console.log(`
61
- Select your default Agent Sam:
62
- 1) Orchestrator — general purpose, routes to all lanes
63
- 2) Data Agent — database ops, migrations, queries
64
- 3) CRM Agent — customer management, contacts, billing
65
- 4) Creative Agent — design, 3D, media, content
66
- `);
67
- const agentKey = await ask(' Agent [1-4]: ');
68
- const agent = AGENTS[agentKey] ?? 'orchestrator';
101
+ return opts;
102
+ }
69
103
 
70
- const cfAccountId = await ask(' Cloudflare Account ID (enter to skip): ');
104
+ async function runInit(config) {
105
+ const {
106
+ projectName,
107
+ lane,
108
+ provider,
109
+ agent,
110
+ cfAccountId = '',
111
+ yes = false,
112
+ prompt = null,
113
+ } = config;
71
114
 
115
+ const safeDir = projectName.trim();
72
116
  console.log(`
73
117
  ┌─────────────────────────────────────┐
74
118
  │ Agent Sam Project Config │
75
119
  ├─────────────────────────────────────┤
76
- │ Project: ${projectName.padEnd(25)}│
120
+ │ Project: ${safeDir.padEnd(25)}│
77
121
  │ Lane: ${lane.padEnd(25)}│
78
122
  │ Provider: ${provider.padEnd(25)}│
79
123
  │ Agent: ${agent.padEnd(25)}│
@@ -81,37 +125,141 @@ async function init() {
81
125
  └─────────────────────────────────────┘
82
126
  `);
83
127
 
84
- const confirm = await ask(' Scaffold project? (y/n): ');
128
+ let confirm = 'y';
129
+ if (!yes && prompt) {
130
+ confirm = await prompt.ask(' Scaffold project? (y/n): ');
131
+ }
85
132
 
86
133
  if (confirm.toLowerCase() === 'y') {
87
- const dir = scaffoldProject({ projectName, lane, provider, agent, cfAccountId });
88
- console.log(`
134
+ try {
135
+ const dir = scaffoldProject({
136
+ projectName: safeDir,
137
+ lane,
138
+ provider,
139
+ agent,
140
+ cfAccountId,
141
+ sdkVersion: VERSION,
142
+ });
143
+ console.log(`
89
144
  ✓ Project created at ${dir}
90
145
 
91
146
  Next steps:
92
- cd ${projectName}
147
+ cd ${dir.split('/').pop()}
93
148
  cp .env.example .env
94
149
  npm install
150
+ npm run smoke
95
151
  npm run dev
96
152
  `);
153
+ } catch (error) {
154
+ console.error(`\n ✗ ${error.message}\n`);
155
+ process.exitCode = 1;
156
+ }
97
157
  } else {
98
158
  console.log('\n Cancelled.\n');
99
159
  }
100
-
101
- rl.close();
102
160
  }
103
161
 
104
- const command = process.argv[2];
162
+ async function initInteractive(partial = {}) {
163
+ const prompt = createPrompt();
105
164
 
106
- if (command === 'init') {
107
- init();
108
- } else {
109
165
  console.log(`
110
- Agent Sam SDK — CLI
166
+ ╔═══════════════════════════════════╗
167
+ ║ Agent Sam SDK — Init ║
168
+ ╚═══════════════════════════════════╝
169
+ `);
111
170
 
112
- Usage:
113
- agentsam init Initialize a new Agent Sam project
171
+ const projectName = partial.projectName || (await prompt.ask(' Project name: '));
172
+
173
+ if (!partial.lane) {
174
+ console.log(`
175
+ Select a lane:
176
+ 1) Full Stack
177
+ 2) CMS
178
+ 3) Data Solutions
179
+ 4) Customer Management
180
+ 5) Creative & Design
181
+ `);
182
+ }
183
+ const laneKey = partial.lane ? laneKeyFromLabel(partial.lane) : await prompt.ask(' Lane [1-5]: ');
184
+ const lane = partial.lane || LANES[laneKey] || 'Full Stack';
185
+
186
+ console.log(`
187
+ Select a provider:
188
+ 1) Cloudflare Workers
189
+ 2) GitHub + Cloudflare
190
+ 3) Local / Self-hosted
191
+ `);
192
+ const providerKey = partial.provider
193
+ ? Object.entries(PROVIDERS).find(([, v]) => v === partial.provider)?.[0] || '1'
194
+ : await prompt.ask(' Provider [1-3]: ');
195
+ const provider = partial.provider || PROVIDERS[providerKey] || 'Cloudflare Workers';
114
196
 
115
- Version: 1.0.0
197
+ if (!partial.agent) {
198
+ console.log(`
199
+ Select your default Agent Sam:
200
+ 1) Orchestrator — general purpose, routes to all lanes
201
+ 2) CMS Agent — pages, sections, assets, publishing workflows
202
+ 3) Data Agent — database ops, migrations, queries
203
+ 4) CRM Agent — customer management, contacts, billing
204
+ 5) Creative Agent — design, 3D, media, content
116
205
  `);
206
+ }
207
+ const agentKey = partial.agent
208
+ ? Object.entries(AGENTS).find(([, v]) => v === partial.agent)?.[0] || '1'
209
+ : await prompt.ask(' Agent [1-5]: ');
210
+ const agent = partial.agent || AGENTS[agentKey] || 'orchestrator';
211
+
212
+ const cfAccountId =
213
+ partial.cfAccountId !== undefined && partial.cfAccountId !== ''
214
+ ? partial.cfAccountId
215
+ : await prompt.ask(' Cloudflare Account ID (enter to skip): ');
216
+
217
+ await runInit({
218
+ projectName,
219
+ lane,
220
+ provider,
221
+ agent,
222
+ cfAccountId,
223
+ yes: partial.yes,
224
+ prompt,
225
+ });
226
+
227
+ prompt.close();
228
+ }
229
+
230
+ async function initFromArgs(argv) {
231
+ const opts = parseInitArgs(argv);
232
+ if (!opts.projectName) {
233
+ console.error('\n ✗ --name is required for non-interactive init.\n');
234
+ process.exit(1);
235
+ }
236
+ await runInit({
237
+ projectName: opts.projectName,
238
+ lane: opts.lane || 'Full Stack',
239
+ provider: opts.provider || 'Cloudflare Workers',
240
+ agent: opts.agent || 'orchestrator',
241
+ cfAccountId: opts.cfAccountId,
242
+ yes: true,
243
+ prompt: null,
244
+ });
245
+ }
246
+
247
+ const command = process.argv[2];
248
+ const rest = process.argv.slice(3);
249
+
250
+ if (command === '--version' || command === '-v') {
251
+ console.log(VERSION);
252
+ } else if (command === '--help' || command === '-h' || !command) {
253
+ printHelp();
254
+ } else if (command === 'init') {
255
+ const hasFlags = rest.some((a) => a.startsWith('--'));
256
+ if (hasFlags) {
257
+ await initFromArgs(rest);
258
+ } else {
259
+ await initInteractive({});
260
+ }
261
+ } else {
262
+ console.error(`\n Unknown command: ${command}\n`);
263
+ printHelp();
264
+ process.exit(1);
117
265
  }
package/src/index.js CHANGED
@@ -1,5 +1,11 @@
1
- // @inneranimalmedia/agentsam-sdk
2
- // Agent Sam SDK — entry point
1
+ // @inneranimalmedia/agentsam-sdk — public API
3
2
 
4
- export const version = '1.0.0';
5
- export const name = 'agentsam-sdk';
3
+ import pkg from '../package.json' with { type: 'json' };
4
+
5
+ export { AgentSam } from './AgentSam.js';
6
+ export { routeIntent } from './lib/router.js';
7
+ export { getToolCatalog } from './lib/tools.js';
8
+ export { scaffoldProject } from './lib/scaffold.js';
9
+
10
+ export const version = pkg.version;
11
+ export const name = pkg.name;
@@ -0,0 +1,21 @@
1
+ export function json(data, status = 200, headers = {}) {
2
+ return new Response(JSON.stringify(data, null, 2), {
3
+ status,
4
+ headers: {
5
+ 'content-type': 'application/json; charset=utf-8',
6
+ ...headers,
7
+ },
8
+ });
9
+ }
10
+
11
+ export function notFound() {
12
+ return json({ ok: false, error: 'not_found' }, 404);
13
+ }
14
+
15
+ export async function readJson(request) {
16
+ try {
17
+ return await request.json();
18
+ } catch {
19
+ return {};
20
+ }
21
+ }
@@ -0,0 +1,51 @@
1
+ export function routeIntent({ message = '', agent = 'orchestrator', lane = 'fullstack' }) {
2
+ const text = String(message).toLowerCase();
3
+
4
+ if (/delete|drop table|destroy|wipe|purge|production/.test(text)) {
5
+ return {
6
+ agent,
7
+ lane,
8
+ intent: 'high_risk_action',
9
+ requires_approval: true,
10
+ next_steps: ['Summarize intended change', 'Request explicit approval', 'Run only after approval'],
11
+ };
12
+ }
13
+
14
+ if (/page|cms|publish|section|hero|content/.test(text)) {
15
+ return {
16
+ agent: 'cms',
17
+ lane: 'cms',
18
+ intent: 'cms_build',
19
+ requires_approval: false,
20
+ next_steps: ['Create page outline', 'Attach visual assets', 'Prepare draft for preview'],
21
+ };
22
+ }
23
+
24
+ if (/database|sql|query|migration|schema|supabase|d1/.test(text)) {
25
+ return {
26
+ agent: 'data',
27
+ lane: 'data',
28
+ intent: 'data_task',
29
+ requires_approval: false,
30
+ next_steps: ['Inspect schema', 'Draft safe query or migration', 'Return reviewable plan'],
31
+ };
32
+ }
33
+
34
+ if (/design|3d|cad|media|video|image|creative/.test(text)) {
35
+ return {
36
+ agent: 'creative',
37
+ lane: 'creative',
38
+ intent: 'creative_task',
39
+ requires_approval: false,
40
+ next_steps: ['Create production brief', 'Identify assets needed', 'Prepare generation or build steps'],
41
+ };
42
+ }
43
+
44
+ return {
45
+ agent,
46
+ lane,
47
+ intent: 'general_build',
48
+ requires_approval: false,
49
+ next_steps: ['Understand goal', 'Plan the work', 'Route to the right specialist'],
50
+ };
51
+ }
@@ -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,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,38 @@
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
+ console.log('SDK smoke tests passed');