struere 0.3.6 → 0.3.7
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/dist/bin/struere.js
CHANGED
|
@@ -17480,6 +17480,332 @@ function getEnvLocal(deploymentUrl) {
|
|
|
17480
17480
|
return `STRUERE_DEPLOYMENT_URL=${deploymentUrl}
|
|
17481
17481
|
`;
|
|
17482
17482
|
}
|
|
17483
|
+
function getClaudeMd(name) {
|
|
17484
|
+
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
17485
|
+
return `# ${displayName} Agent
|
|
17486
|
+
|
|
17487
|
+
This is a Struere AI agent project. Struere is a framework for building production AI agents with built-in data management, event tracking, and job scheduling.
|
|
17488
|
+
|
|
17489
|
+
## Project Structure
|
|
17490
|
+
|
|
17491
|
+
\`\`\`
|
|
17492
|
+
src/
|
|
17493
|
+
\u251C\u2500\u2500 agent.ts # Agent definition (system prompt, model, tools)
|
|
17494
|
+
\u251C\u2500\u2500 context.ts # Dynamic context injection per request
|
|
17495
|
+
\u251C\u2500\u2500 tools.ts # Custom tool definitions
|
|
17496
|
+
\u2514\u2500\u2500 workflows/ # Multi-step workflow definitions
|
|
17497
|
+
tests/
|
|
17498
|
+
\u2514\u2500\u2500 *.test.yaml # YAML-based conversation tests
|
|
17499
|
+
struere.json # Project configuration (agentId, team, slug)
|
|
17500
|
+
struere.config.ts # Framework settings (port, CORS, logging)
|
|
17501
|
+
\`\`\`
|
|
17502
|
+
|
|
17503
|
+
## Agent Definition
|
|
17504
|
+
|
|
17505
|
+
Define your agent in \`src/agent.ts\`:
|
|
17506
|
+
|
|
17507
|
+
\`\`\`typescript
|
|
17508
|
+
import { defineAgent } from 'struere'
|
|
17509
|
+
import { tools } from './tools'
|
|
17510
|
+
import { context } from './context'
|
|
17511
|
+
|
|
17512
|
+
export default defineAgent({
|
|
17513
|
+
name: 'my-agent',
|
|
17514
|
+
version: '0.1.0',
|
|
17515
|
+
description: 'My AI Agent',
|
|
17516
|
+
model: {
|
|
17517
|
+
provider: 'anthropic',
|
|
17518
|
+
name: 'claude-sonnet-4-20250514',
|
|
17519
|
+
temperature: 0.7,
|
|
17520
|
+
maxTokens: 4096,
|
|
17521
|
+
},
|
|
17522
|
+
systemPrompt: \\\`You are a helpful assistant.
|
|
17523
|
+
|
|
17524
|
+
Current time: {{datetime}}
|
|
17525
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}\\\`,
|
|
17526
|
+
tools,
|
|
17527
|
+
context,
|
|
17528
|
+
state: {
|
|
17529
|
+
storage: 'memory',
|
|
17530
|
+
ttl: 3600,
|
|
17531
|
+
},
|
|
17532
|
+
})
|
|
17533
|
+
\`\`\`
|
|
17534
|
+
|
|
17535
|
+
## System Prompt Templates
|
|
17536
|
+
|
|
17537
|
+
System prompts support dynamic \`{{...}}\` templates that are resolved at runtime before the LLM call.
|
|
17538
|
+
|
|
17539
|
+
### Available Variables
|
|
17540
|
+
|
|
17541
|
+
| Variable | Description |
|
|
17542
|
+
|----------|-------------|
|
|
17543
|
+
| \`{{organizationId}}\` | Current organization ID |
|
|
17544
|
+
| \`{{userId}}\` | Current user ID |
|
|
17545
|
+
| \`{{threadId}}\` | Conversation thread ID |
|
|
17546
|
+
| \`{{agentId}}\` | Agent ID |
|
|
17547
|
+
| \`{{agent.name}}\` | Agent name |
|
|
17548
|
+
| \`{{agent.slug}}\` | Agent slug |
|
|
17549
|
+
| \`{{thread.metadata.X}}\` | Thread metadata field X |
|
|
17550
|
+
| \`{{message}}\` | Current user message |
|
|
17551
|
+
| \`{{timestamp}}\` | Unix timestamp (ms) |
|
|
17552
|
+
| \`{{datetime}}\` | ISO 8601 datetime |
|
|
17553
|
+
|
|
17554
|
+
### Function Calls
|
|
17555
|
+
|
|
17556
|
+
Call any agent tool directly in the system prompt:
|
|
17557
|
+
|
|
17558
|
+
\`\`\`
|
|
17559
|
+
{{entity.get({"id": "ent_123"})}}
|
|
17560
|
+
{{entity.query({"type": "customer", "limit": 5})}}
|
|
17561
|
+
{{event.query({"entityId": "ent_123", "limit": 10})}}
|
|
17562
|
+
\`\`\`
|
|
17563
|
+
|
|
17564
|
+
### Nested Templates
|
|
17565
|
+
|
|
17566
|
+
Variables can be used inside function arguments:
|
|
17567
|
+
|
|
17568
|
+
\`\`\`
|
|
17569
|
+
{{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
17570
|
+
\`\`\`
|
|
17571
|
+
|
|
17572
|
+
### Error Handling
|
|
17573
|
+
|
|
17574
|
+
Failed templates are replaced with inline errors:
|
|
17575
|
+
\`\`\`
|
|
17576
|
+
[TEMPLATE_ERROR: variableName not found]
|
|
17577
|
+
[TEMPLATE_ERROR: toolName - error message]
|
|
17578
|
+
\`\`\`
|
|
17579
|
+
|
|
17580
|
+
## Custom Tools
|
|
17581
|
+
|
|
17582
|
+
Define tools in \`src/tools.ts\`:
|
|
17583
|
+
|
|
17584
|
+
\`\`\`typescript
|
|
17585
|
+
import { defineTools } from 'struere'
|
|
17586
|
+
|
|
17587
|
+
export const tools = defineTools([
|
|
17588
|
+
{
|
|
17589
|
+
name: 'search_products',
|
|
17590
|
+
description: 'Search the product catalog',
|
|
17591
|
+
parameters: {
|
|
17592
|
+
type: 'object',
|
|
17593
|
+
properties: {
|
|
17594
|
+
query: { type: 'string', description: 'Search query' },
|
|
17595
|
+
limit: { type: 'number', description: 'Max results' },
|
|
17596
|
+
},
|
|
17597
|
+
required: ['query'],
|
|
17598
|
+
},
|
|
17599
|
+
handler: async (params) => {
|
|
17600
|
+
const results = await searchProducts(params.query, params.limit ?? 10)
|
|
17601
|
+
return { products: results }
|
|
17602
|
+
},
|
|
17603
|
+
},
|
|
17604
|
+
])
|
|
17605
|
+
\`\`\`
|
|
17606
|
+
|
|
17607
|
+
Custom tool handlers are executed in a sandboxed Cloudflare Worker environment. They can make HTTP requests to allowlisted domains:
|
|
17608
|
+
- api.openai.com, api.anthropic.com, api.stripe.com
|
|
17609
|
+
- api.sendgrid.com, api.twilio.com, hooks.slack.com
|
|
17610
|
+
- discord.com, api.github.com
|
|
17611
|
+
|
|
17612
|
+
## Built-in Tools
|
|
17613
|
+
|
|
17614
|
+
Agents have access to these built-in tools for data management:
|
|
17615
|
+
|
|
17616
|
+
### Entity Tools
|
|
17617
|
+
|
|
17618
|
+
| Tool | Description |
|
|
17619
|
+
|------|-------------|
|
|
17620
|
+
| \`entity.create\` | Create a new entity |
|
|
17621
|
+
| \`entity.get\` | Get entity by ID |
|
|
17622
|
+
| \`entity.query\` | Query entities by type/filters |
|
|
17623
|
+
| \`entity.update\` | Update entity data |
|
|
17624
|
+
| \`entity.delete\` | Soft-delete entity |
|
|
17625
|
+
| \`entity.link\` | Create entity relation |
|
|
17626
|
+
| \`entity.unlink\` | Remove entity relation |
|
|
17627
|
+
|
|
17628
|
+
Example entity operations:
|
|
17629
|
+
\`\`\`json
|
|
17630
|
+
// entity.create
|
|
17631
|
+
{ "type": "customer", "data": { "name": "John", "email": "john@example.com" } }
|
|
17632
|
+
|
|
17633
|
+
// entity.query
|
|
17634
|
+
{ "type": "customer", "filters": { "status": "active" }, "limit": 10 }
|
|
17635
|
+
|
|
17636
|
+
// entity.update
|
|
17637
|
+
{ "id": "ent_123", "data": { "status": "vip" } }
|
|
17638
|
+
\`\`\`
|
|
17639
|
+
|
|
17640
|
+
### Event Tools
|
|
17641
|
+
|
|
17642
|
+
| Tool | Description |
|
|
17643
|
+
|------|-------------|
|
|
17644
|
+
| \`event.emit\` | Emit a custom event |
|
|
17645
|
+
| \`event.query\` | Query event history |
|
|
17646
|
+
|
|
17647
|
+
Example event operations:
|
|
17648
|
+
\`\`\`json
|
|
17649
|
+
// event.emit
|
|
17650
|
+
{ "entityId": "ent_123", "eventType": "order.placed", "payload": { "amount": 99.99 } }
|
|
17651
|
+
|
|
17652
|
+
// event.query
|
|
17653
|
+
{ "entityId": "ent_123", "eventType": "order.*", "limit": 20 }
|
|
17654
|
+
\`\`\`
|
|
17655
|
+
|
|
17656
|
+
### Job Tools
|
|
17657
|
+
|
|
17658
|
+
| Tool | Description |
|
|
17659
|
+
|------|-------------|
|
|
17660
|
+
| \`job.enqueue\` | Schedule a background job |
|
|
17661
|
+
| \`job.status\` | Get job status |
|
|
17662
|
+
|
|
17663
|
+
Example job operations:
|
|
17664
|
+
\`\`\`json
|
|
17665
|
+
// job.enqueue
|
|
17666
|
+
{ "jobType": "send_email", "payload": { "to": "user@example.com" }, "scheduledFor": 1706745600000 }
|
|
17667
|
+
|
|
17668
|
+
// job.status
|
|
17669
|
+
{ "id": "job_abc123" }
|
|
17670
|
+
\`\`\`
|
|
17671
|
+
|
|
17672
|
+
## Context Function
|
|
17673
|
+
|
|
17674
|
+
Inject dynamic context per request in \`src/context.ts\`:
|
|
17675
|
+
|
|
17676
|
+
\`\`\`typescript
|
|
17677
|
+
import { defineContext } from 'struere'
|
|
17678
|
+
|
|
17679
|
+
export const context = defineContext(async (request) => {
|
|
17680
|
+
const { conversationId, userId, channel, state } = request
|
|
17681
|
+
|
|
17682
|
+
const userProfile = await fetchUserProfile(userId)
|
|
17683
|
+
|
|
17684
|
+
return {
|
|
17685
|
+
additionalContext: \\\`
|
|
17686
|
+
User: \${userProfile.name} (\${userProfile.tier} tier)
|
|
17687
|
+
Conversation: \${conversationId}
|
|
17688
|
+
Channel: \${channel}
|
|
17689
|
+
\\\`,
|
|
17690
|
+
variables: {
|
|
17691
|
+
userId,
|
|
17692
|
+
userTier: userProfile.tier,
|
|
17693
|
+
timestamp: new Date().toISOString(),
|
|
17694
|
+
},
|
|
17695
|
+
}
|
|
17696
|
+
})
|
|
17697
|
+
\`\`\`
|
|
17698
|
+
|
|
17699
|
+
## Testing
|
|
17700
|
+
|
|
17701
|
+
Write YAML-based conversation tests in \`tests/\`:
|
|
17702
|
+
|
|
17703
|
+
\`\`\`yaml
|
|
17704
|
+
name: Order flow test
|
|
17705
|
+
description: Test the complete order flow
|
|
17706
|
+
|
|
17707
|
+
conversation:
|
|
17708
|
+
- role: user
|
|
17709
|
+
content: I want to order a pizza
|
|
17710
|
+
- role: assistant
|
|
17711
|
+
assertions:
|
|
17712
|
+
- type: contains
|
|
17713
|
+
value: size
|
|
17714
|
+
- type: toolCalled
|
|
17715
|
+
value: get_menu
|
|
17716
|
+
|
|
17717
|
+
- role: user
|
|
17718
|
+
content: Large pepperoni please
|
|
17719
|
+
- role: assistant
|
|
17720
|
+
assertions:
|
|
17721
|
+
- type: toolCalled
|
|
17722
|
+
value: entity.create
|
|
17723
|
+
\`\`\`
|
|
17724
|
+
|
|
17725
|
+
### Assertion Types
|
|
17726
|
+
|
|
17727
|
+
| Type | Description |
|
|
17728
|
+
|------|-------------|
|
|
17729
|
+
| \`contains\` | Response contains substring |
|
|
17730
|
+
| \`matches\` | Response matches regex |
|
|
17731
|
+
| \`toolCalled\` | Specific tool was called |
|
|
17732
|
+
| \`noToolCalled\` | No tools were called |
|
|
17733
|
+
|
|
17734
|
+
Run tests with:
|
|
17735
|
+
\`\`\`bash
|
|
17736
|
+
bun run test
|
|
17737
|
+
\`\`\`
|
|
17738
|
+
|
|
17739
|
+
## CLI Commands
|
|
17740
|
+
|
|
17741
|
+
| Command | Description |
|
|
17742
|
+
|---------|-------------|
|
|
17743
|
+
| \`struere dev\` | Start development mode (live sync to Convex) |
|
|
17744
|
+
| \`struere build\` | Validate agent configuration |
|
|
17745
|
+
| \`struere deploy\` | Deploy agent to production |
|
|
17746
|
+
| \`struere test\` | Run YAML conversation tests |
|
|
17747
|
+
| \`struere logs\` | View recent execution logs |
|
|
17748
|
+
| \`struere state\` | Inspect conversation thread state |
|
|
17749
|
+
|
|
17750
|
+
## Thread Metadata
|
|
17751
|
+
|
|
17752
|
+
Set thread metadata when creating conversations to provide context:
|
|
17753
|
+
|
|
17754
|
+
\`\`\`typescript
|
|
17755
|
+
// Via API
|
|
17756
|
+
POST /v1/chat
|
|
17757
|
+
{
|
|
17758
|
+
"agentId": "agent_123",
|
|
17759
|
+
"message": "Hello",
|
|
17760
|
+
"metadata": {
|
|
17761
|
+
"customerId": "ent_customer_456",
|
|
17762
|
+
"channel": "web",
|
|
17763
|
+
"language": "en"
|
|
17764
|
+
}
|
|
17765
|
+
}
|
|
17766
|
+
\`\`\`
|
|
17767
|
+
|
|
17768
|
+
Access in system prompt:
|
|
17769
|
+
\`\`\`
|
|
17770
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
17771
|
+
Channel: {{thread.metadata.channel}}
|
|
17772
|
+
\`\`\`
|
|
17773
|
+
|
|
17774
|
+
## Development Workflow
|
|
17775
|
+
|
|
17776
|
+
1. **Edit agent configuration** in \`src/agent.ts\`
|
|
17777
|
+
2. **Run \`bun run dev\`** to sync changes to Convex
|
|
17778
|
+
3. **Test via API** or dashboard chat interface
|
|
17779
|
+
4. **Write tests** in \`tests/*.test.yaml\`
|
|
17780
|
+
5. **Deploy** with \`bun run deploy\`
|
|
17781
|
+
|
|
17782
|
+
## API Endpoints
|
|
17783
|
+
|
|
17784
|
+
| Endpoint | Method | Description |
|
|
17785
|
+
|----------|--------|-------------|
|
|
17786
|
+
| \`/v1/chat\` | POST | Chat by agent ID |
|
|
17787
|
+
| \`/v1/agents/:slug/chat\` | POST | Chat by agent slug |
|
|
17788
|
+
|
|
17789
|
+
Authentication: Bearer token (API key from dashboard)
|
|
17790
|
+
|
|
17791
|
+
\`\`\`bash
|
|
17792
|
+
curl -X POST https://your-deployment.convex.cloud/v1/chat \\
|
|
17793
|
+
-H "Authorization: Bearer sk_live_..." \\
|
|
17794
|
+
-H "Content-Type: application/json" \\
|
|
17795
|
+
-d '{"agentId": "...", "message": "Hello"}'
|
|
17796
|
+
\`\`\`
|
|
17797
|
+
|
|
17798
|
+
## Best Practices
|
|
17799
|
+
|
|
17800
|
+
1. **System Prompts**: Use templates for dynamic data instead of hardcoding
|
|
17801
|
+
2. **Tools**: Keep tool handlers focused and stateless
|
|
17802
|
+
3. **Entities**: Model your domain data as entity types
|
|
17803
|
+
4. **Events**: Emit events for audit trails and analytics
|
|
17804
|
+
5. **Jobs**: Use jobs for async operations (emails, notifications)
|
|
17805
|
+
6. **Testing**: Write tests for critical conversation flows
|
|
17806
|
+
7. **Context**: Use context for user-specific personalization
|
|
17807
|
+
`;
|
|
17808
|
+
}
|
|
17483
17809
|
|
|
17484
17810
|
// src/cli/utils/scaffold.ts
|
|
17485
17811
|
function ensureDir(filePath) {
|
|
@@ -17519,7 +17845,8 @@ function scaffoldAgentFiles(cwd, projectName) {
|
|
|
17519
17845
|
"src/tools.ts": getToolsTs(),
|
|
17520
17846
|
"src/workflows/.gitkeep": "",
|
|
17521
17847
|
"tests/basic.test.yaml": getBasicTestYaml(),
|
|
17522
|
-
".env.example": getEnvExample()
|
|
17848
|
+
".env.example": getEnvExample(),
|
|
17849
|
+
".claude.md": getClaudeMd(projectName)
|
|
17523
17850
|
};
|
|
17524
17851
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
17525
17852
|
const fullPath = join3(cwd, relativePath);
|
|
@@ -18768,9 +19095,17 @@ var whoamiCommand = new Command("whoami").description("Show current logged in us
|
|
|
18768
19095
|
// package.json
|
|
18769
19096
|
var package_default = {
|
|
18770
19097
|
name: "struere",
|
|
18771
|
-
version: "0.3.
|
|
19098
|
+
version: "0.3.7",
|
|
18772
19099
|
description: "Build, test, and deploy AI agents",
|
|
18773
|
-
keywords: [
|
|
19100
|
+
keywords: [
|
|
19101
|
+
"ai",
|
|
19102
|
+
"agents",
|
|
19103
|
+
"llm",
|
|
19104
|
+
"anthropic",
|
|
19105
|
+
"openai",
|
|
19106
|
+
"framework",
|
|
19107
|
+
"cli"
|
|
19108
|
+
],
|
|
18774
19109
|
author: "struere",
|
|
18775
19110
|
license: "MIT",
|
|
18776
19111
|
publishConfig: {
|
|
@@ -18797,7 +19132,9 @@ var package_default = {
|
|
|
18797
19132
|
types: "./dist/index.d.ts"
|
|
18798
19133
|
}
|
|
18799
19134
|
},
|
|
18800
|
-
files: [
|
|
19135
|
+
files: [
|
|
19136
|
+
"dist"
|
|
19137
|
+
],
|
|
18801
19138
|
scripts: {
|
|
18802
19139
|
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
18803
19140
|
dev: "tsc --watch",
|
package/dist/cli/index.js
CHANGED
|
@@ -819,6 +819,332 @@ function getEnvLocal(deploymentUrl) {
|
|
|
819
819
|
return `STRUERE_DEPLOYMENT_URL=${deploymentUrl}
|
|
820
820
|
`;
|
|
821
821
|
}
|
|
822
|
+
function getClaudeMd(name) {
|
|
823
|
+
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
824
|
+
return `# ${displayName} Agent
|
|
825
|
+
|
|
826
|
+
This is a Struere AI agent project. Struere is a framework for building production AI agents with built-in data management, event tracking, and job scheduling.
|
|
827
|
+
|
|
828
|
+
## Project Structure
|
|
829
|
+
|
|
830
|
+
\`\`\`
|
|
831
|
+
src/
|
|
832
|
+
\u251C\u2500\u2500 agent.ts # Agent definition (system prompt, model, tools)
|
|
833
|
+
\u251C\u2500\u2500 context.ts # Dynamic context injection per request
|
|
834
|
+
\u251C\u2500\u2500 tools.ts # Custom tool definitions
|
|
835
|
+
\u2514\u2500\u2500 workflows/ # Multi-step workflow definitions
|
|
836
|
+
tests/
|
|
837
|
+
\u2514\u2500\u2500 *.test.yaml # YAML-based conversation tests
|
|
838
|
+
struere.json # Project configuration (agentId, team, slug)
|
|
839
|
+
struere.config.ts # Framework settings (port, CORS, logging)
|
|
840
|
+
\`\`\`
|
|
841
|
+
|
|
842
|
+
## Agent Definition
|
|
843
|
+
|
|
844
|
+
Define your agent in \`src/agent.ts\`:
|
|
845
|
+
|
|
846
|
+
\`\`\`typescript
|
|
847
|
+
import { defineAgent } from 'struere'
|
|
848
|
+
import { tools } from './tools'
|
|
849
|
+
import { context } from './context'
|
|
850
|
+
|
|
851
|
+
export default defineAgent({
|
|
852
|
+
name: 'my-agent',
|
|
853
|
+
version: '0.1.0',
|
|
854
|
+
description: 'My AI Agent',
|
|
855
|
+
model: {
|
|
856
|
+
provider: 'anthropic',
|
|
857
|
+
name: 'claude-sonnet-4-20250514',
|
|
858
|
+
temperature: 0.7,
|
|
859
|
+
maxTokens: 4096,
|
|
860
|
+
},
|
|
861
|
+
systemPrompt: \\\`You are a helpful assistant.
|
|
862
|
+
|
|
863
|
+
Current time: {{datetime}}
|
|
864
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}\\\`,
|
|
865
|
+
tools,
|
|
866
|
+
context,
|
|
867
|
+
state: {
|
|
868
|
+
storage: 'memory',
|
|
869
|
+
ttl: 3600,
|
|
870
|
+
},
|
|
871
|
+
})
|
|
872
|
+
\`\`\`
|
|
873
|
+
|
|
874
|
+
## System Prompt Templates
|
|
875
|
+
|
|
876
|
+
System prompts support dynamic \`{{...}}\` templates that are resolved at runtime before the LLM call.
|
|
877
|
+
|
|
878
|
+
### Available Variables
|
|
879
|
+
|
|
880
|
+
| Variable | Description |
|
|
881
|
+
|----------|-------------|
|
|
882
|
+
| \`{{organizationId}}\` | Current organization ID |
|
|
883
|
+
| \`{{userId}}\` | Current user ID |
|
|
884
|
+
| \`{{threadId}}\` | Conversation thread ID |
|
|
885
|
+
| \`{{agentId}}\` | Agent ID |
|
|
886
|
+
| \`{{agent.name}}\` | Agent name |
|
|
887
|
+
| \`{{agent.slug}}\` | Agent slug |
|
|
888
|
+
| \`{{thread.metadata.X}}\` | Thread metadata field X |
|
|
889
|
+
| \`{{message}}\` | Current user message |
|
|
890
|
+
| \`{{timestamp}}\` | Unix timestamp (ms) |
|
|
891
|
+
| \`{{datetime}}\` | ISO 8601 datetime |
|
|
892
|
+
|
|
893
|
+
### Function Calls
|
|
894
|
+
|
|
895
|
+
Call any agent tool directly in the system prompt:
|
|
896
|
+
|
|
897
|
+
\`\`\`
|
|
898
|
+
{{entity.get({"id": "ent_123"})}}
|
|
899
|
+
{{entity.query({"type": "customer", "limit": 5})}}
|
|
900
|
+
{{event.query({"entityId": "ent_123", "limit": 10})}}
|
|
901
|
+
\`\`\`
|
|
902
|
+
|
|
903
|
+
### Nested Templates
|
|
904
|
+
|
|
905
|
+
Variables can be used inside function arguments:
|
|
906
|
+
|
|
907
|
+
\`\`\`
|
|
908
|
+
{{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
909
|
+
\`\`\`
|
|
910
|
+
|
|
911
|
+
### Error Handling
|
|
912
|
+
|
|
913
|
+
Failed templates are replaced with inline errors:
|
|
914
|
+
\`\`\`
|
|
915
|
+
[TEMPLATE_ERROR: variableName not found]
|
|
916
|
+
[TEMPLATE_ERROR: toolName - error message]
|
|
917
|
+
\`\`\`
|
|
918
|
+
|
|
919
|
+
## Custom Tools
|
|
920
|
+
|
|
921
|
+
Define tools in \`src/tools.ts\`:
|
|
922
|
+
|
|
923
|
+
\`\`\`typescript
|
|
924
|
+
import { defineTools } from 'struere'
|
|
925
|
+
|
|
926
|
+
export const tools = defineTools([
|
|
927
|
+
{
|
|
928
|
+
name: 'search_products',
|
|
929
|
+
description: 'Search the product catalog',
|
|
930
|
+
parameters: {
|
|
931
|
+
type: 'object',
|
|
932
|
+
properties: {
|
|
933
|
+
query: { type: 'string', description: 'Search query' },
|
|
934
|
+
limit: { type: 'number', description: 'Max results' },
|
|
935
|
+
},
|
|
936
|
+
required: ['query'],
|
|
937
|
+
},
|
|
938
|
+
handler: async (params) => {
|
|
939
|
+
const results = await searchProducts(params.query, params.limit ?? 10)
|
|
940
|
+
return { products: results }
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
])
|
|
944
|
+
\`\`\`
|
|
945
|
+
|
|
946
|
+
Custom tool handlers are executed in a sandboxed Cloudflare Worker environment. They can make HTTP requests to allowlisted domains:
|
|
947
|
+
- api.openai.com, api.anthropic.com, api.stripe.com
|
|
948
|
+
- api.sendgrid.com, api.twilio.com, hooks.slack.com
|
|
949
|
+
- discord.com, api.github.com
|
|
950
|
+
|
|
951
|
+
## Built-in Tools
|
|
952
|
+
|
|
953
|
+
Agents have access to these built-in tools for data management:
|
|
954
|
+
|
|
955
|
+
### Entity Tools
|
|
956
|
+
|
|
957
|
+
| Tool | Description |
|
|
958
|
+
|------|-------------|
|
|
959
|
+
| \`entity.create\` | Create a new entity |
|
|
960
|
+
| \`entity.get\` | Get entity by ID |
|
|
961
|
+
| \`entity.query\` | Query entities by type/filters |
|
|
962
|
+
| \`entity.update\` | Update entity data |
|
|
963
|
+
| \`entity.delete\` | Soft-delete entity |
|
|
964
|
+
| \`entity.link\` | Create entity relation |
|
|
965
|
+
| \`entity.unlink\` | Remove entity relation |
|
|
966
|
+
|
|
967
|
+
Example entity operations:
|
|
968
|
+
\`\`\`json
|
|
969
|
+
// entity.create
|
|
970
|
+
{ "type": "customer", "data": { "name": "John", "email": "john@example.com" } }
|
|
971
|
+
|
|
972
|
+
// entity.query
|
|
973
|
+
{ "type": "customer", "filters": { "status": "active" }, "limit": 10 }
|
|
974
|
+
|
|
975
|
+
// entity.update
|
|
976
|
+
{ "id": "ent_123", "data": { "status": "vip" } }
|
|
977
|
+
\`\`\`
|
|
978
|
+
|
|
979
|
+
### Event Tools
|
|
980
|
+
|
|
981
|
+
| Tool | Description |
|
|
982
|
+
|------|-------------|
|
|
983
|
+
| \`event.emit\` | Emit a custom event |
|
|
984
|
+
| \`event.query\` | Query event history |
|
|
985
|
+
|
|
986
|
+
Example event operations:
|
|
987
|
+
\`\`\`json
|
|
988
|
+
// event.emit
|
|
989
|
+
{ "entityId": "ent_123", "eventType": "order.placed", "payload": { "amount": 99.99 } }
|
|
990
|
+
|
|
991
|
+
// event.query
|
|
992
|
+
{ "entityId": "ent_123", "eventType": "order.*", "limit": 20 }
|
|
993
|
+
\`\`\`
|
|
994
|
+
|
|
995
|
+
### Job Tools
|
|
996
|
+
|
|
997
|
+
| Tool | Description |
|
|
998
|
+
|------|-------------|
|
|
999
|
+
| \`job.enqueue\` | Schedule a background job |
|
|
1000
|
+
| \`job.status\` | Get job status |
|
|
1001
|
+
|
|
1002
|
+
Example job operations:
|
|
1003
|
+
\`\`\`json
|
|
1004
|
+
// job.enqueue
|
|
1005
|
+
{ "jobType": "send_email", "payload": { "to": "user@example.com" }, "scheduledFor": 1706745600000 }
|
|
1006
|
+
|
|
1007
|
+
// job.status
|
|
1008
|
+
{ "id": "job_abc123" }
|
|
1009
|
+
\`\`\`
|
|
1010
|
+
|
|
1011
|
+
## Context Function
|
|
1012
|
+
|
|
1013
|
+
Inject dynamic context per request in \`src/context.ts\`:
|
|
1014
|
+
|
|
1015
|
+
\`\`\`typescript
|
|
1016
|
+
import { defineContext } from 'struere'
|
|
1017
|
+
|
|
1018
|
+
export const context = defineContext(async (request) => {
|
|
1019
|
+
const { conversationId, userId, channel, state } = request
|
|
1020
|
+
|
|
1021
|
+
const userProfile = await fetchUserProfile(userId)
|
|
1022
|
+
|
|
1023
|
+
return {
|
|
1024
|
+
additionalContext: \\\`
|
|
1025
|
+
User: \${userProfile.name} (\${userProfile.tier} tier)
|
|
1026
|
+
Conversation: \${conversationId}
|
|
1027
|
+
Channel: \${channel}
|
|
1028
|
+
\\\`,
|
|
1029
|
+
variables: {
|
|
1030
|
+
userId,
|
|
1031
|
+
userTier: userProfile.tier,
|
|
1032
|
+
timestamp: new Date().toISOString(),
|
|
1033
|
+
},
|
|
1034
|
+
}
|
|
1035
|
+
})
|
|
1036
|
+
\`\`\`
|
|
1037
|
+
|
|
1038
|
+
## Testing
|
|
1039
|
+
|
|
1040
|
+
Write YAML-based conversation tests in \`tests/\`:
|
|
1041
|
+
|
|
1042
|
+
\`\`\`yaml
|
|
1043
|
+
name: Order flow test
|
|
1044
|
+
description: Test the complete order flow
|
|
1045
|
+
|
|
1046
|
+
conversation:
|
|
1047
|
+
- role: user
|
|
1048
|
+
content: I want to order a pizza
|
|
1049
|
+
- role: assistant
|
|
1050
|
+
assertions:
|
|
1051
|
+
- type: contains
|
|
1052
|
+
value: size
|
|
1053
|
+
- type: toolCalled
|
|
1054
|
+
value: get_menu
|
|
1055
|
+
|
|
1056
|
+
- role: user
|
|
1057
|
+
content: Large pepperoni please
|
|
1058
|
+
- role: assistant
|
|
1059
|
+
assertions:
|
|
1060
|
+
- type: toolCalled
|
|
1061
|
+
value: entity.create
|
|
1062
|
+
\`\`\`
|
|
1063
|
+
|
|
1064
|
+
### Assertion Types
|
|
1065
|
+
|
|
1066
|
+
| Type | Description |
|
|
1067
|
+
|------|-------------|
|
|
1068
|
+
| \`contains\` | Response contains substring |
|
|
1069
|
+
| \`matches\` | Response matches regex |
|
|
1070
|
+
| \`toolCalled\` | Specific tool was called |
|
|
1071
|
+
| \`noToolCalled\` | No tools were called |
|
|
1072
|
+
|
|
1073
|
+
Run tests with:
|
|
1074
|
+
\`\`\`bash
|
|
1075
|
+
bun run test
|
|
1076
|
+
\`\`\`
|
|
1077
|
+
|
|
1078
|
+
## CLI Commands
|
|
1079
|
+
|
|
1080
|
+
| Command | Description |
|
|
1081
|
+
|---------|-------------|
|
|
1082
|
+
| \`struere dev\` | Start development mode (live sync to Convex) |
|
|
1083
|
+
| \`struere build\` | Validate agent configuration |
|
|
1084
|
+
| \`struere deploy\` | Deploy agent to production |
|
|
1085
|
+
| \`struere test\` | Run YAML conversation tests |
|
|
1086
|
+
| \`struere logs\` | View recent execution logs |
|
|
1087
|
+
| \`struere state\` | Inspect conversation thread state |
|
|
1088
|
+
|
|
1089
|
+
## Thread Metadata
|
|
1090
|
+
|
|
1091
|
+
Set thread metadata when creating conversations to provide context:
|
|
1092
|
+
|
|
1093
|
+
\`\`\`typescript
|
|
1094
|
+
// Via API
|
|
1095
|
+
POST /v1/chat
|
|
1096
|
+
{
|
|
1097
|
+
"agentId": "agent_123",
|
|
1098
|
+
"message": "Hello",
|
|
1099
|
+
"metadata": {
|
|
1100
|
+
"customerId": "ent_customer_456",
|
|
1101
|
+
"channel": "web",
|
|
1102
|
+
"language": "en"
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
\`\`\`
|
|
1106
|
+
|
|
1107
|
+
Access in system prompt:
|
|
1108
|
+
\`\`\`
|
|
1109
|
+
Customer: {{entity.get({"id": "{{thread.metadata.customerId}}"})}}
|
|
1110
|
+
Channel: {{thread.metadata.channel}}
|
|
1111
|
+
\`\`\`
|
|
1112
|
+
|
|
1113
|
+
## Development Workflow
|
|
1114
|
+
|
|
1115
|
+
1. **Edit agent configuration** in \`src/agent.ts\`
|
|
1116
|
+
2. **Run \`bun run dev\`** to sync changes to Convex
|
|
1117
|
+
3. **Test via API** or dashboard chat interface
|
|
1118
|
+
4. **Write tests** in \`tests/*.test.yaml\`
|
|
1119
|
+
5. **Deploy** with \`bun run deploy\`
|
|
1120
|
+
|
|
1121
|
+
## API Endpoints
|
|
1122
|
+
|
|
1123
|
+
| Endpoint | Method | Description |
|
|
1124
|
+
|----------|--------|-------------|
|
|
1125
|
+
| \`/v1/chat\` | POST | Chat by agent ID |
|
|
1126
|
+
| \`/v1/agents/:slug/chat\` | POST | Chat by agent slug |
|
|
1127
|
+
|
|
1128
|
+
Authentication: Bearer token (API key from dashboard)
|
|
1129
|
+
|
|
1130
|
+
\`\`\`bash
|
|
1131
|
+
curl -X POST https://your-deployment.convex.cloud/v1/chat \\
|
|
1132
|
+
-H "Authorization: Bearer sk_live_..." \\
|
|
1133
|
+
-H "Content-Type: application/json" \\
|
|
1134
|
+
-d '{"agentId": "...", "message": "Hello"}'
|
|
1135
|
+
\`\`\`
|
|
1136
|
+
|
|
1137
|
+
## Best Practices
|
|
1138
|
+
|
|
1139
|
+
1. **System Prompts**: Use templates for dynamic data instead of hardcoding
|
|
1140
|
+
2. **Tools**: Keep tool handlers focused and stateless
|
|
1141
|
+
3. **Entities**: Model your domain data as entity types
|
|
1142
|
+
4. **Events**: Emit events for audit trails and analytics
|
|
1143
|
+
5. **Jobs**: Use jobs for async operations (emails, notifications)
|
|
1144
|
+
6. **Testing**: Write tests for critical conversation flows
|
|
1145
|
+
7. **Context**: Use context for user-specific personalization
|
|
1146
|
+
`;
|
|
1147
|
+
}
|
|
822
1148
|
|
|
823
1149
|
// src/cli/utils/scaffold.ts
|
|
824
1150
|
function ensureDir(filePath) {
|
|
@@ -858,7 +1184,8 @@ function scaffoldAgentFiles(cwd, projectName) {
|
|
|
858
1184
|
"src/tools.ts": getToolsTs(),
|
|
859
1185
|
"src/workflows/.gitkeep": "",
|
|
860
1186
|
"tests/basic.test.yaml": getBasicTestYaml(),
|
|
861
|
-
".env.example": getEnvExample()
|
|
1187
|
+
".env.example": getEnvExample(),
|
|
1188
|
+
".claude.md": getClaudeMd(projectName)
|
|
862
1189
|
};
|
|
863
1190
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
864
1191
|
const fullPath = join3(cwd, relativePath);
|
|
@@ -2133,9 +2460,17 @@ var whoamiCommand = new Command11("whoami").description("Show current logged in
|
|
|
2133
2460
|
// package.json
|
|
2134
2461
|
var package_default = {
|
|
2135
2462
|
name: "struere",
|
|
2136
|
-
version: "0.3.
|
|
2463
|
+
version: "0.3.7",
|
|
2137
2464
|
description: "Build, test, and deploy AI agents",
|
|
2138
|
-
keywords: [
|
|
2465
|
+
keywords: [
|
|
2466
|
+
"ai",
|
|
2467
|
+
"agents",
|
|
2468
|
+
"llm",
|
|
2469
|
+
"anthropic",
|
|
2470
|
+
"openai",
|
|
2471
|
+
"framework",
|
|
2472
|
+
"cli"
|
|
2473
|
+
],
|
|
2139
2474
|
author: "struere",
|
|
2140
2475
|
license: "MIT",
|
|
2141
2476
|
publishConfig: {
|
|
@@ -2162,7 +2497,9 @@ var package_default = {
|
|
|
2162
2497
|
types: "./dist/index.d.ts"
|
|
2163
2498
|
}
|
|
2164
2499
|
},
|
|
2165
|
-
files: [
|
|
2500
|
+
files: [
|
|
2501
|
+
"dist"
|
|
2502
|
+
],
|
|
2166
2503
|
scripts: {
|
|
2167
2504
|
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
2168
2505
|
dev: "tsc --watch",
|
|
@@ -9,4 +9,5 @@ export declare function getEnvExample(): string;
|
|
|
9
9
|
export declare function getGitignore(): string;
|
|
10
10
|
export declare function getStruereJson(agentId: string, team: string, slug: string, name: string): string;
|
|
11
11
|
export declare function getEnvLocal(deploymentUrl: string): string;
|
|
12
|
+
export declare function getClaudeMd(name: string): string;
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuBnD;AAED,wBAAgB,WAAW,IAAI,MAAM,CAsBpC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAgBzC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoC/C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAkBrC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAoDnC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAmBzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAgBrC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAahG;AAED,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuBnD;AAED,wBAAgB,WAAW,IAAI,MAAM,CAsBpC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAgBzC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoC/C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAkBrC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAoDnC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAmBzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAgBrC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAahG;AAED,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyUhD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/scaffold.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/scaffold.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAeD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAexF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAgCrF;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,CA+BnF;AAsBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "struere",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Build, test, and deploy AI agents",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agents",
|
|
8
|
+
"llm",
|
|
9
|
+
"anthropic",
|
|
10
|
+
"openai",
|
|
11
|
+
"framework",
|
|
12
|
+
"cli"
|
|
13
|
+
],
|
|
6
14
|
"author": "struere",
|
|
7
15
|
"license": "MIT",
|
|
8
16
|
"publishConfig": {
|
|
@@ -29,7 +37,9 @@
|
|
|
29
37
|
"types": "./dist/index.d.ts"
|
|
30
38
|
}
|
|
31
39
|
},
|
|
32
|
-
"files": [
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
33
43
|
"scripts": {
|
|
34
44
|
"build": "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
35
45
|
"dev": "tsc --watch",
|