beddel 1.0.6 → 1.0.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.
Files changed (29) hide show
  1. package/README.md +2 -1
  2. package/dist/agents/{business-analyzer.yaml → google-business/business-analyzer.yaml} +41 -27
  3. package/dist/agents/index.d.ts +24 -3
  4. package/dist/agents/index.d.ts.map +1 -1
  5. package/dist/agents/index.js +52 -11
  6. package/dist/agents/marketing/newsletter-signup.yaml +106 -0
  7. package/dist/server/handler.js +4 -4
  8. package/docs/architecture/api-reference.md +194 -11
  9. package/docs/architecture/components.md +14 -10
  10. package/docs/architecture/core-workflows.md +1 -0
  11. package/docs/architecture/source-tree.md +39 -18
  12. package/docs/prd/requirements.md +1 -1
  13. package/package.json +2 -2
  14. package/src/agents/{business-analyzer.yaml → google-business/business-analyzer.yaml} +41 -27
  15. package/src/agents/index.ts +63 -13
  16. package/src/agents/marketing/newsletter-signup.yaml +106 -0
  17. package/src/server/handler.ts +4 -4
  18. /package/dist/agents/{assistant-bedrock.yaml → chat/assistant-bedrock.yaml} +0 -0
  19. /package/dist/agents/{assistant-openrouter.yaml → chat/assistant-openrouter.yaml} +0 -0
  20. /package/dist/agents/{assistant.yaml → chat/assistant.yaml} +0 -0
  21. /package/dist/agents/{multi-step-assistant.yaml → examples/multi-step-assistant.yaml} +0 -0
  22. /package/dist/agents/{assistant-gitmcp.yaml → mcp/assistant-gitmcp.yaml} +0 -0
  23. /package/dist/agents/{text-generator.yaml → utility/text-generator.yaml} +0 -0
  24. /package/src/agents/{assistant-bedrock.yaml → chat/assistant-bedrock.yaml} +0 -0
  25. /package/src/agents/{assistant-openrouter.yaml → chat/assistant-openrouter.yaml} +0 -0
  26. /package/src/agents/{assistant.yaml → chat/assistant.yaml} +0 -0
  27. /package/src/agents/{multi-step-assistant.yaml → examples/multi-step-assistant.yaml} +0 -0
  28. /package/src/agents/{assistant-gitmcp.yaml → mcp/assistant-gitmcp.yaml} +0 -0
  29. /package/src/agents/{text-generator.yaml → utility/text-generator.yaml} +0 -0
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Beddel Protocol
2
2
 
3
3
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
- [![npm version](https://img.shields.io/badge/npm-1.0.4-brightgreen.svg)](https://www.npmjs.com/package/beddel)
4
+ [![npm version](https://img.shields.io/badge/npm-1.0.7-brightgreen.svg)](https://www.npmjs.com/package/beddel)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
6
6
  [![AI SDK](https://img.shields.io/badge/AI%20SDK-v6-purple.svg)](https://sdk.vercel.ai/)
7
7
 
@@ -236,6 +236,7 @@ workflow:
236
236
  |---------|-------------|---------|
237
237
  | `$input.*` | Access request input | `$input.messages` |
238
238
  | `$stepResult.varName.*` | Access step result | `$stepResult.llmOutput.text` |
239
+ | `$env.*` | Access environment variables | `$env.NOTION_DATABASE_ID` |
239
240
 
240
241
  ## Built-in Tools
241
242
 
@@ -13,8 +13,9 @@
13
13
 
14
14
  metadata:
15
15
  name: "Business Analyzer"
16
- version: "1.0.0"
16
+ version: "1.1.0"
17
17
  description: "Analyzes business reviews and generates actionable insights"
18
+ builtin: true
18
19
 
19
20
  workflow:
20
21
  # Step 1: Fetch all reviews with auto-pagination
@@ -46,20 +47,28 @@ workflow:
46
47
  5. Trend analysis comparing recent vs older reviews
47
48
  6. Actionable recommendations
48
49
 
49
- Format your response as structured JSON.
50
+ Return ONLY valid JSON (no markdown, no ```):
51
+ {
52
+ "sentimentScore": 8,
53
+ "positiveThemes": [{"theme": "Great service", "count": 15}],
54
+ "improvementAreas": [{"area": "Wait times", "count": 5}],
55
+ "urgentReviews": [{"reviewId": "xxx", "rating": 1, "issue": "summary"}],
56
+ "trendAnalysis": "Recent reviews show improvement...",
57
+ "recommendations": ["Focus on...", "Consider..."]
58
+ }
50
59
  messages:
51
60
  - role: "user"
52
- content: |
53
- Analyze these business reviews:
54
-
55
- Total Reviews: $stepResult.reviewsData.totalReviewCount
56
- Average Rating: $stepResult.reviewsData.averageRating
57
-
58
- Reviews Data:
59
- $stepResult.reviewsData.reviews
61
+ content: "$input.reviewsPayload"
62
+ result: "analysisRaw"
63
+
64
+ # Step 3: Parse analysis JSON
65
+ - id: "parse-analysis"
66
+ type: "output-generator"
67
+ config:
68
+ json: "$stepResult.analysisRaw.text"
60
69
  result: "analysis"
61
70
 
62
- # Step 3: Generate response suggestions for negative reviews
71
+ # Step 4: Generate response suggestions for negative reviews
63
72
  - id: "generate-responses"
64
73
  type: "llm"
65
74
  config:
@@ -76,24 +85,29 @@ workflow:
76
85
  - Keep responses under 150 words
77
86
  - Be specific to the complaint mentioned
78
87
 
79
- Format as JSON array with reviewId and suggestedResponse.
88
+ Return ONLY valid JSON array (no markdown, no ```):
89
+ [{"reviewId": "xxx", "rating": 2, "originalComment": "...", "suggestedResponse": "..."}]
90
+
91
+ If no negative reviews, return: []
80
92
  messages:
81
93
  - role: "user"
82
- content: |
83
- Generate response suggestions for these reviews:
84
- $stepResult.reviewsData.reviews
85
- result: "responseSuggestions"
94
+ content: "$input.reviewsPayload"
95
+ result: "responsesRaw"
86
96
 
87
- # Step 4: Compile final report
88
- - id: "compile-report"
97
+ # Step 5: Parse responses JSON
98
+ - id: "parse-responses"
89
99
  type: "output-generator"
90
100
  config:
91
- template:
92
- summary:
93
- totalReviews: "$stepResult.reviewsData.totalReviewCount"
94
- averageRating: "$stepResult.reviewsData.averageRating"
95
- analysisDate: "$input.analysisDate"
96
- analysis: "$stepResult.analysis.text"
97
- responseSuggestions: "$stepResult.responseSuggestions.text"
98
- rawReviews: "$stepResult.reviewsData.reviews"
99
- result: "finalReport"
101
+ json: "$stepResult.responsesRaw.text"
102
+ result: "responseSuggestions"
103
+
104
+ # Explicit Return: Define the API response contract
105
+ return:
106
+ success: true
107
+ summary:
108
+ totalReviews: "$stepResult.reviewsData.totalReviewCount"
109
+ averageRating: "$stepResult.reviewsData.averageRating"
110
+ pagesFetched: "$stepResult.reviewsData.data.pagesFetched"
111
+ analysis: "$stepResult.analysis"
112
+ responseSuggestions: "$stepResult.responseSuggestions"
113
+ reviews: "$stepResult.reviewsData.reviews"
@@ -3,12 +3,24 @@
3
3
  *
4
4
  * Lists all agents bundled with the beddel package.
5
5
  * These are available automatically without user configuration.
6
+ *
7
+ * Agents are organized by category:
8
+ * - chat/ Streaming chat assistants (different providers)
9
+ * - mcp/ MCP server integrations (GitMCP, Context7, etc.)
10
+ * - google-business/ Google Business Profile agents
11
+ * - marketing/ Lead capture, newsletters, CRM
12
+ * - utility/ General-purpose tools
13
+ * - examples/ Demo pipelines showing multi-step workflows
14
+ */
15
+ /**
16
+ * Built-in agent definitions with their category paths
6
17
  */
18
+ export declare const BUILTIN_AGENT_PATHS: Record<string, string>;
7
19
  /**
8
20
  * List of built-in agent IDs available in the package
9
21
  */
10
- export declare const BUILTIN_AGENTS: readonly ["assistant", "assistant-bedrock", "assistant-openrouter", "assistant-gitmcp", "text-generator", "multi-step-assistant", "business-analyzer"];
11
- export type BuiltinAgentId = typeof BUILTIN_AGENTS[number];
22
+ export declare const BUILTIN_AGENTS: readonly string[];
23
+ export type BuiltinAgentId = keyof typeof BUILTIN_AGENT_PATHS;
12
24
  /**
13
25
  * Get the absolute path to the built-in agents directory
14
26
  */
@@ -20,5 +32,14 @@ export declare function isBuiltinAgent(agentId: string): agentId is BuiltinAgent
20
32
  /**
21
33
  * Get the full path to a built-in agent YAML file
22
34
  */
23
- export declare function getBuiltinAgentPath(agentId: BuiltinAgentId): string;
35
+ export declare function getBuiltinAgentPath(agentId: string): string | null;
36
+ /**
37
+ * Get all agents in a specific category
38
+ */
39
+ export declare function getAgentsByCategory(category: string): string[];
40
+ /**
41
+ * Available categories
42
+ */
43
+ export declare const AGENT_CATEGORIES: readonly ["chat", "mcp", "google-business", "marketing", "utility", "examples"];
44
+ export type AgentCategory = typeof AGENT_CATEGORIES[number];
24
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH;;GAEG;AACH,eAAO,MAAM,cAAc,wJAQjB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;AAE3D;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,IAAI,cAAc,CAEzE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAEnE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoBtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAuC,SAAS,MAAM,EAAE,CAAC;AAEpF,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,mBAAmB,CAAC;AAE9D;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,IAAI,cAAc,CAEzE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIlE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAI9D;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,iFAOnB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC"}
@@ -3,24 +3,43 @@
3
3
  *
4
4
  * Lists all agents bundled with the beddel package.
5
5
  * These are available automatically without user configuration.
6
+ *
7
+ * Agents are organized by category:
8
+ * - chat/ Streaming chat assistants (different providers)
9
+ * - mcp/ MCP server integrations (GitMCP, Context7, etc.)
10
+ * - google-business/ Google Business Profile agents
11
+ * - marketing/ Lead capture, newsletters, CRM
12
+ * - utility/ General-purpose tools
13
+ * - examples/ Demo pipelines showing multi-step workflows
6
14
  */
7
15
  import { fileURLToPath } from 'url';
8
16
  import { dirname, join } from 'path';
9
17
  // Get the directory where built-in agents are located
10
18
  const __filename = fileURLToPath(import.meta.url);
11
19
  const __dirname = dirname(__filename);
20
+ /**
21
+ * Built-in agent definitions with their category paths
22
+ */
23
+ export const BUILTIN_AGENT_PATHS = {
24
+ // Chat assistants
25
+ 'assistant': 'chat/assistant.yaml',
26
+ 'assistant-bedrock': 'chat/assistant-bedrock.yaml',
27
+ 'assistant-openrouter': 'chat/assistant-openrouter.yaml',
28
+ // MCP integrations
29
+ 'assistant-gitmcp': 'mcp/assistant-gitmcp.yaml',
30
+ // Google Business
31
+ 'business-analyzer': 'google-business/business-analyzer.yaml',
32
+ // Marketing
33
+ 'newsletter-signup': 'marketing/newsletter-signup.yaml',
34
+ // Utility
35
+ 'text-generator': 'utility/text-generator.yaml',
36
+ // Examples
37
+ 'multi-step-assistant': 'examples/multi-step-assistant.yaml',
38
+ };
12
39
  /**
13
40
  * List of built-in agent IDs available in the package
14
41
  */
15
- export const BUILTIN_AGENTS = [
16
- 'assistant',
17
- 'assistant-bedrock',
18
- 'assistant-openrouter',
19
- 'assistant-gitmcp',
20
- 'text-generator',
21
- 'multi-step-assistant',
22
- 'business-analyzer',
23
- ];
42
+ export const BUILTIN_AGENTS = Object.keys(BUILTIN_AGENT_PATHS);
24
43
  /**
25
44
  * Get the absolute path to the built-in agents directory
26
45
  */
@@ -31,11 +50,33 @@ export function getBuiltinAgentsPath() {
31
50
  * Check if an agent ID is a built-in agent
32
51
  */
33
52
  export function isBuiltinAgent(agentId) {
34
- return BUILTIN_AGENTS.includes(agentId);
53
+ return agentId in BUILTIN_AGENT_PATHS;
35
54
  }
36
55
  /**
37
56
  * Get the full path to a built-in agent YAML file
38
57
  */
39
58
  export function getBuiltinAgentPath(agentId) {
40
- return join(__dirname, `${agentId}.yaml`);
59
+ const relativePath = BUILTIN_AGENT_PATHS[agentId];
60
+ if (!relativePath)
61
+ return null;
62
+ return join(__dirname, relativePath);
63
+ }
64
+ /**
65
+ * Get all agents in a specific category
66
+ */
67
+ export function getAgentsByCategory(category) {
68
+ return Object.entries(BUILTIN_AGENT_PATHS)
69
+ .filter(([_, path]) => path.startsWith(`${category}/`))
70
+ .map(([id]) => id);
41
71
  }
72
+ /**
73
+ * Available categories
74
+ */
75
+ export const AGENT_CATEGORIES = [
76
+ 'chat',
77
+ 'mcp',
78
+ 'google-business',
79
+ 'marketing',
80
+ 'utility',
81
+ 'examples',
82
+ ];
@@ -0,0 +1,106 @@
1
+ # Newsletter Signup Agent
2
+ # Registers users to the newsletter with AI analysis and saves to Notion
3
+ #
4
+ # Required Environment Variables:
5
+ # - NOTION_TOKEN (Internal Integration Secret)
6
+ # - NOTION_DATABASE_ID (Database ID for newsletter signups)
7
+ # - GEMINI_API_KEY (for AI analysis)
8
+ #
9
+ # Input:
10
+ # - name: User's name
11
+ # - email: User's email
12
+ # - message: Optional message from user
13
+ # - createdAt: ISO date string
14
+
15
+ metadata:
16
+ name: "Newsletter Signup Agent"
17
+ version: "1.3.0"
18
+ description: "Registers users to the newsletter with AI analysis and saves to Notion"
19
+ builtin: true
20
+
21
+ workflow:
22
+ # Step 1: Analyze user profile and generate structured JSON
23
+ - id: "analyze-user"
24
+ type: "llm"
25
+ config:
26
+ provider: "google"
27
+ model: "gemini-2.0-flash-exp"
28
+ system: |
29
+ You are a lead analyst for a technology newsletter.
30
+
31
+ Analyze the user data and return ONLY a valid JSON (no markdown, no ```):
32
+ {
33
+ "tags": [{"name": "Tag1"}],
34
+ "sentiment": {"name": "Positive"},
35
+ "summary": "One-line summary"
36
+ }
37
+
38
+ IMPORTANT RULES:
39
+ - tags: Array of objects with "name". Allowed values: "Support", "Sales", "Partnership", "Feedback"
40
+ - sentiment: Object with "name". Allowed values: "Positive", "Neutral", "Negative"
41
+ - summary: Short summary string (maximum 100 characters)
42
+
43
+ Example for a positive message about the product:
44
+ {"tags": [{"name": "Feedback"}], "sentiment": {"name": "Positive"}, "summary": "User praises the product"}
45
+
46
+ If there is no significant message:
47
+ {"tags": [{"name": "Feedback"}], "sentiment": {"name": "Neutral"}, "summary": "New newsletter subscriber"}
48
+
49
+ Return ONLY the JSON, nothing else.
50
+ messages:
51
+ - role: "user"
52
+ content: |
53
+ Name: $input.name
54
+ Email: $input.email
55
+ Message: $input.message
56
+ result: "userAnalysis"
57
+
58
+ # Step 2: Parse the JSON from LLM
59
+ - id: "parse-analysis"
60
+ type: "output-generator"
61
+ config:
62
+ json: "$stepResult.userAnalysis.text"
63
+ result: "parsedAnalysis"
64
+
65
+ # Step 3: Create Notion page with structured data
66
+ - id: "create-notion-entry"
67
+ type: "notion"
68
+ config:
69
+ action: "createPage"
70
+ parent:
71
+ type: "database_id"
72
+ database_id: "$env.NOTION_DATABASE_ID"
73
+ properties:
74
+ Name:
75
+ title:
76
+ - text:
77
+ content: "$input.name"
78
+ Email:
79
+ email: "$input.email"
80
+ Message:
81
+ rich_text:
82
+ - text:
83
+ content: "$input.message"
84
+ Summary:
85
+ rich_text:
86
+ - text:
87
+ content: "$stepResult.parsedAnalysis.summary"
88
+ Tags:
89
+ multi_select: "$stepResult.parsedAnalysis.tags"
90
+ Sentiment:
91
+ select: "$stepResult.parsedAnalysis.sentiment"
92
+ CreatedAt:
93
+ date:
94
+ start: "$input.createdAt"
95
+ result: "notionResult"
96
+
97
+ # Explicit Return: Define the API response contract
98
+ return:
99
+ success: true
100
+ message: "Registration completed successfully!"
101
+ notionPageId: "$stepResult.notionResult.pageId"
102
+ notionUrl: "$stepResult.notionResult.url"
103
+ analysis:
104
+ tags: "$stepResult.parsedAnalysis.tags"
105
+ sentiment: "$stepResult.parsedAnalysis.sentiment"
106
+ summary: "$stepResult.parsedAnalysis.summary"
@@ -2,7 +2,7 @@ import { loadYaml } from '../core/parser';
2
2
  import { WorkflowExecutor } from '../core/workflow';
3
3
  import { join, normalize } from 'path';
4
4
  import { access } from 'fs/promises';
5
- import { getBuiltinAgentsPath } from '../agents';
5
+ import { getBuiltinAgentPath } from '../agents';
6
6
  /**
7
7
  * Validate agentId to prevent path traversal attacks.
8
8
  * Only allows alphanumeric characters, hyphens, and underscores.
@@ -39,10 +39,10 @@ async function resolveAgentPath(agentId, userAgentsPath, disableBuiltinAgents) {
39
39
  if (userPath.startsWith(basePath) && await fileExists(userPath)) {
40
40
  return userPath;
41
41
  }
42
- // 2. Fallback: built-in agents from package
42
+ // 2. Fallback: built-in agents from package (supports subfolder structure)
43
43
  if (!disableBuiltinAgents) {
44
- const builtinPath = join(getBuiltinAgentsPath(), `${agentId}.yaml`);
45
- if (await fileExists(builtinPath)) {
44
+ const builtinPath = getBuiltinAgentPath(agentId);
45
+ if (builtinPath && await fileExists(builtinPath)) {
46
46
  return builtinPath;
47
47
  }
48
48
  }
@@ -1,6 +1,6 @@
1
1
  # API Reference
2
2
 
3
- > **Beddel Protocol v1.0.4** — Complete API documentation for all public exports.
3
+ > **Beddel Protocol v1.0.6** — Complete API documentation for all public exports.
4
4
 
5
5
  ---
6
6
 
@@ -31,7 +31,7 @@ console.log(yaml.metadata.name); // "Streaming Assistant"
31
31
 
32
32
  #### `resolveVariables(template: unknown, context: ExecutionContext): unknown`
33
33
 
34
- Resolve variable references (`$input.*`, `$stepResult.*`) in templates.
34
+ Resolve variable references (`$input.*`, `$stepResult.*`, `$env.*`) in templates.
35
35
 
36
36
  ```typescript
37
37
  import { resolveVariables } from 'beddel';
@@ -81,6 +81,8 @@ Map of primitive step types to their handler functions.
81
81
  - `output-generator` — Deterministic JSON transform
82
82
  - `call-agent` — Sub-agent invocation
83
83
  - `mcp-tool` — External MCP server tool execution
84
+ - `google-business` — Google Business Profile API integration
85
+ - `notion` — Notion workspace integration (pages, databases, blocks)
84
86
 
85
87
  #### `toolRegistry: Record<string, ToolImplementation>`
86
88
 
@@ -240,6 +242,59 @@ import type {
240
242
 
241
243
  ---
242
244
 
245
+ ## YAML Workflow Structure
246
+
247
+ ### Explicit Return Template
248
+
249
+ The `return` property at the workflow level defines the exact shape of the API response. This provides explicit control over the API contract, separating internal workflow state from the public response.
250
+
251
+ **Without `return`:** API returns all accumulated step variables (internal state exposed)
252
+
253
+ **With `return`:** API returns only the resolved template (clean contract)
254
+
255
+ ```yaml
256
+ metadata:
257
+ name: "Newsletter Signup"
258
+ version: "1.0.0"
259
+
260
+ workflow:
261
+ - id: "analyze"
262
+ type: "llm"
263
+ config:
264
+ # ... LLM config ...
265
+ result: "analysis" # Stored internally
266
+
267
+ - id: "save"
268
+ type: "notion"
269
+ config:
270
+ # ... Notion config ...
271
+ result: "notionResult" # Stored internally
272
+
273
+ # Explicit API response shape
274
+ return:
275
+ success: true
276
+ pageId: "$stepResult.notionResult.pageId"
277
+ url: "$stepResult.notionResult.url"
278
+ summary: "$stepResult.analysis.text"
279
+ ```
280
+
281
+ **Response:**
282
+ ```json
283
+ {
284
+ "success": true,
285
+ "pageId": "abc123...",
286
+ "url": "https://notion.so/...",
287
+ "summary": "Analysis text..."
288
+ }
289
+ ```
290
+
291
+ **Return Resolution Order:**
292
+ 1. If `return` is defined → resolve and return the template
293
+ 2. If last step has no `result` → return last step's output directly
294
+ 3. Otherwise → return all accumulated variables
295
+
296
+ ---
297
+
243
298
  ## Primitives
244
299
 
245
300
  ### `chat` Primitive
@@ -316,7 +371,16 @@ workflow:
316
371
 
317
372
  ### `output-generator` Primitive
318
373
 
319
- Deterministic JSON transform using variable resolution.
374
+ Deterministic JSON transform using variable resolution. Supports optional JSON parsing from LLM text output.
375
+
376
+ **Config Options:**
377
+
378
+ | Property | Type | Required | Description |
379
+ |----------|------|----------|-------------|
380
+ | `template` | `object` | No | JSON template with variable references |
381
+ | `json` | `string` | No | Variable reference to parse as JSON (e.g., `$stepResult.llmOutput.text`) |
382
+
383
+ **Basic Usage:**
320
384
 
321
385
  ```yaml
322
386
  workflow:
@@ -329,6 +393,36 @@ workflow:
329
393
  result: "finalOutput"
330
394
  ```
331
395
 
396
+ **JSON Parsing from LLM Output:**
397
+
398
+ When LLM returns JSON as text, use the `json` parameter to parse it and access fields via `$json.*`:
399
+
400
+ ```yaml
401
+ workflow:
402
+ # Step 1: LLM generates JSON
403
+ - id: "analyze"
404
+ type: "llm"
405
+ config:
406
+ system: "Return JSON: {\"tags\": [\"tag1\"], \"sentiment\": \"Positive\"}"
407
+ messages: "$input.messages"
408
+ result: "analysis"
409
+
410
+ # Step 2: Parse JSON and extract fields
411
+ - id: "parse"
412
+ type: "output-generator"
413
+ config:
414
+ json: "$stepResult.analysis.text"
415
+ template:
416
+ tags: "$json.tags"
417
+ sentiment: "$json.sentiment"
418
+ result: "parsed"
419
+ ```
420
+
421
+ The `json` parameter:
422
+ - Extracts JSON from markdown code blocks if present
423
+ - Parses the JSON and makes it available as `$json.*`
424
+ - If no template is provided, returns the parsed JSON directly
425
+
332
426
  ### `mcp-tool` Primitive
333
427
 
334
428
  Connect to external MCP servers via SSE and execute tools.
@@ -389,18 +483,102 @@ workflow:
389
483
  messages: "$input.messages"
390
484
  ```
391
485
 
486
+ ### `notion` Primitive
487
+
488
+ Integrate with Notion API for pages, databases, blocks, and search.
489
+
490
+ **Environment Variables:**
491
+ - `NOTION_TOKEN` — Internal Integration Secret (starts with `ntn_`)
492
+
493
+ **Supported Actions:**
494
+
495
+ | Action | Description | Required Config |
496
+ |--------|-------------|-----------------|
497
+ | `search` | Search pages and databases | `query?`, `filter?` |
498
+ | `getPage` | Retrieve a page by ID | `pageId` |
499
+ | `createPage` | Create a new page | `parent`, `properties` |
500
+ | `updatePage` | Update page properties | `pageId`, `properties?` |
501
+ | `getDatabase` | Retrieve database schema | `databaseId` |
502
+ | `queryDatabase` | Query database with filters | `databaseId`, `filter?`, `sorts?` |
503
+ | `getBlocks` | Get block children | `blockId` or `pageId` |
504
+ | `appendBlocks` | Append blocks to page/block | `blockId` or `pageId`, `children` |
505
+ | `createDatabase` | Create a new database | `parent`, `properties`, `title?` |
506
+
507
+ **Example:**
508
+
509
+ ```yaml
510
+ workflow:
511
+ - id: "create-entry"
512
+ type: "notion"
513
+ config:
514
+ action: "createPage"
515
+ parent:
516
+ type: "database_id"
517
+ database_id: "a7661a0a-c5b7-47a0-98f8-fd07789d1647"
518
+ properties:
519
+ Name:
520
+ title:
521
+ - text:
522
+ content: "$input.name"
523
+ Email:
524
+ email: "$input.email"
525
+ result: "notionResult"
526
+ ```
527
+
528
+ > See `packages/beddel/docs/primitives/notion-primitive.md` for full documentation.
529
+
530
+ ### `google-business` Primitive
531
+
532
+ Integrate with Google Business Profile APIs for reviews, posts, Q&A, and metrics.
533
+
534
+ **Environment Variables:**
535
+ - `GOOGLE_CLIENT_ID` — OAuth2 Client ID
536
+ - `GOOGLE_CLIENT_SECRET` — OAuth2 Client Secret
537
+ - `GOOGLE_REFRESH_TOKEN` — OAuth2 Refresh Token
538
+
539
+ **Supported Actions:**
540
+
541
+ | Action | Description |
542
+ |--------|-------------|
543
+ | `listReviews` | Fetch reviews with auto-pagination |
544
+ | `replyReview` | Reply to a specific review |
545
+ | `batchGetReviews` | Fetch from multiple locations |
546
+ | `createPost` | Create a local post |
547
+ | `listPosts` | List all posts |
548
+ | `getMetrics` | Fetch performance metrics |
549
+ | `listQuestions` | List Q&A |
550
+ | `answerQuestion` | Answer a question |
551
+
552
+ **Example:**
553
+
554
+ ```yaml
555
+ workflow:
556
+ - id: "fetch-reviews"
557
+ type: "google-business"
558
+ config:
559
+ action: "listReviews"
560
+ accountId: "$input.accountId"
561
+ locationId: "$input.locationId"
562
+ pageSize: 100
563
+ result: "reviewsData"
564
+ ```
565
+
566
+ > See `packages/beddel/docs/primitives/google-business-primitive.md` for full documentation.
567
+
392
568
  ---
393
569
 
394
570
  ## Built-in Agents
395
571
 
396
- | Agent ID | Provider | Description |
397
- |----------|----------|-------------|
398
- | `assistant` | Google | Streaming chat assistant |
399
- | `assistant-bedrock` | Bedrock | Llama 3.2 assistant |
400
- | `assistant-openrouter` | OpenRouter | Free tier assistant |
401
- | `assistant-gitmcp` | Google + MCP | Documentation assistant via GitMCP |
402
- | `text-generator` | Google | Text generation (non-streaming) |
403
- | `multi-step-assistant` | Google | 4-step analysis pipeline |
572
+ | Agent ID | Category | Provider | Description |
573
+ |----------|----------|----------|-------------|
574
+ | `assistant` | `chat/` | Google | Streaming chat assistant |
575
+ | `assistant-bedrock` | `chat/` | Bedrock | Llama 3.2 assistant |
576
+ | `assistant-openrouter` | `chat/` | OpenRouter | Free tier assistant |
577
+ | `assistant-gitmcp` | `mcp/` | Google + MCP | Documentation assistant via GitMCP |
578
+ | `business-analyzer` | `google-business/` | Google | Business reviews analyzer |
579
+ | `newsletter-signup` | `marketing/` | Google | Lead capture with Notion integration |
580
+ | `text-generator` | `utility/` | Google | Text generation (non-streaming) |
581
+ | `multi-step-assistant` | `examples/` | Google | 4-step analysis pipeline |
404
582
 
405
583
  ---
406
584
 
@@ -412,6 +590,7 @@ workflow:
412
590
  interface ParsedYaml {
413
591
  metadata: YamlMetadata;
414
592
  workflow: WorkflowStep[];
593
+ return?: unknown; // Optional explicit return template
415
594
  }
416
595
  ```
417
596
 
@@ -456,3 +635,7 @@ type PrimitiveHandler = (
456
635
  | 2024-12-26 | 1.0.3 | OpenRouter provider, built-in agents |
457
636
  | 2024-12-27 | 1.0.4 | Separated `chat` and `llm` primitives, implemented `call-agent` |
458
637
  | 2024-12-28 | 1.0.5 | Added `mcp-tool` primitive, `assistant-gitmcp` agent, system prompt variable resolution |
638
+ | 2024-12-30 | 1.0.6 | Added `google-business` primitive for Google Business Profile API |
639
+ | 2026-01-01 | 1.0.7 | Added `notion` primitive for Notion API integration |
640
+ | 2026-01-19 | 1.0.8 | Added `json` parameter to `output-generator`, explicit `return` template support |
641
+ | 2026-01-19 | 1.0.9 | Reorganized built-in agents into category subfolders, added `newsletter-signup` agent |
@@ -31,7 +31,7 @@
31
31
 
32
32
  ### Variable Resolver (`src/core/variable-resolver.ts`)
33
33
 
34
- **Responsibility:** Resolve `$input.*` and `$stepResult.*` variable references.
34
+ **Responsibility:** Resolve `$input.*`, `$stepResult.*`, and `$env.*` variable references.
35
35
 
36
36
  **Key Interfaces:**
37
37
  - `resolveVariables(template: any, context: ExecutionContext): any`
@@ -249,16 +249,20 @@ This design is cleaner than a `stream: true/false` flag because:
249
249
 
250
250
  ## Built-in Agents (`src/agents/`)
251
251
 
252
- Pre-configured agents bundled with the package:
252
+ Pre-configured agents bundled with the package, organized by category:
253
253
 
254
- | Agent | Type | Description |
255
- |-------|------|-------------|
256
- | `assistant.yaml` | `chat` | Google Gemini streaming assistant |
257
- | `assistant-bedrock.yaml` | `chat` | Amazon Bedrock assistant |
258
- | `assistant-openrouter.yaml` | `chat` | OpenRouter free tier assistant |
259
- | `assistant-gitmcp.yaml` | `mcp-tool` + `chat` | Documentation assistant via GitMCP |
260
- | `text-generator.yaml` | `llm` | Text generation (non-streaming) |
261
- | `multi-step-assistant.yaml` | `call-agent` + `llm` | 4-step analysis pipeline |
254
+ | Agent | Category | Type | Description |
255
+ |-------|----------|------|-------------|
256
+ | `assistant` | `chat/` | `chat` | Google Gemini streaming assistant |
257
+ | `assistant-bedrock` | `chat/` | `chat` | Amazon Bedrock assistant |
258
+ | `assistant-openrouter` | `chat/` | `chat` | OpenRouter free tier assistant |
259
+ | `assistant-gitmcp` | `mcp/` | `mcp-tool` + `chat` | Documentation assistant via GitMCP |
260
+ | `business-analyzer` | `google-business/` | `google-business` + `llm` | Business reviews analyzer |
261
+ | `newsletter-signup` | `marketing/` | `llm` + `notion` | Lead capture with Notion |
262
+ | `text-generator` | `utility/` | `llm` | Text generation (non-streaming) |
263
+ | `multi-step-assistant` | `examples/` | `call-agent` + `llm` | 4-step analysis pipeline |
264
+
265
+ **Categories:** `chat/`, `mcp/`, `google-business/`, `marketing/`, `utility/`, `examples/`
262
266
 
263
267
  ---
264
268
 
@@ -167,6 +167,7 @@ workflow:
167
167
  |---------|-------------|---------|
168
168
  | `$input.*` | Access request input data | `$input.messages` |
169
169
  | `$stepResult.varName.*` | Access step result by name | `$stepResult.llmOutput.text` |
170
+ | `$env.*` | Access environment variables | `$env.NOTION_DATABASE_ID` |
170
171
 
171
172
  ---
172
173
 
@@ -7,13 +7,21 @@ packages/beddel/
7
7
  │ ├── server.ts # Server handler barrel export
8
8
  │ ├── client.ts # Client exports (types only, browser-safe)
9
9
  │ ├── agents/ # Built-in agents (bundled with package)
10
- │ │ ├── index.ts # Built-in agents registry
11
- │ │ ├── assistant.yaml # Google Gemini streaming assistant
12
- │ │ ├── assistant-bedrock.yaml # Amazon Bedrock assistant
13
- │ │ ├── assistant-openrouter.yaml # OpenRouter assistant
14
- │ │ ├── text-generator.yaml # Text generation (non-streaming)
15
- │ │ ├── multi-step-assistant.yaml # 4-step analysis pipeline
16
- │ │ └── assistant-gitmcp.yaml # GitMCP documentation assistant
10
+ │ │ ├── index.ts # Built-in agents registry (BUILTIN_AGENT_PATHS)
11
+ │ │ ├── chat/ # Streaming chat assistants
12
+ │ │ ├── assistant.yaml
13
+ │ │ ├── assistant-bedrock.yaml
14
+ │ │ │ └── assistant-openrouter.yaml
15
+ │ │ ├── mcp/ # MCP server integrations
16
+ │ │ └── assistant-gitmcp.yaml
17
+ │ │ ├── google-business/ # Google Business Profile agents
18
+ │ │ │ └── business-analyzer.yaml
19
+ │ │ ├── marketing/ # Lead capture, newsletters
20
+ │ │ │ └── newsletter-signup.yaml
21
+ │ │ ├── utility/ # General-purpose tools
22
+ │ │ │ └── text-generator.yaml
23
+ │ │ └── examples/ # Demo pipelines
24
+ │ │ └── multi-step-assistant.yaml
17
25
  │ ├── core/
18
26
  │ │ ├── parser.ts # YAML parsing (FAILSAFE_SCHEMA)
19
27
  │ │ ├── workflow.ts # WorkflowExecutor class
@@ -86,20 +94,33 @@ primitives/
86
94
 
87
95
  ## Built-in Agents
88
96
 
89
- Agents bundled with the package, available without configuration:
90
-
91
- | File | Type | Provider | Description |
92
- |------|------|----------|-------------|
93
- | `assistant.yaml` | `chat` | Google | Streaming chat assistant |
94
- | `assistant-bedrock.yaml` | `chat` | Bedrock | Llama 3.2 assistant |
95
- | `assistant-openrouter.yaml` | `chat` | OpenRouter | Free tier assistant |
96
- | `assistant-gitmcp.yaml` | `mcp-tool` + `chat` | Google + MCP | Documentation assistant via GitMCP |
97
- | `text-generator.yaml` | `llm` | Google | Text generation |
98
- | `multi-step-assistant.yaml` | `call-agent` + `llm` | Google | 4-step pipeline |
97
+ Agents bundled with the package, available without configuration. Organized by category:
98
+
99
+ | Agent ID | Category | Type | Provider | Description |
100
+ |----------|----------|------|----------|-------------|
101
+ | `assistant` | `chat/` | `chat` | Google | Streaming chat assistant |
102
+ | `assistant-bedrock` | `chat/` | `chat` | Bedrock | Llama 3.2 assistant |
103
+ | `assistant-openrouter` | `chat/` | `chat` | OpenRouter | Free tier assistant |
104
+ | `assistant-gitmcp` | `mcp/` | `mcp-tool` + `chat` | Google + MCP | Documentation assistant via GitMCP |
105
+ | `business-analyzer` | `google-business/` | `google-business` + `llm` | Google | Business reviews analyzer |
106
+ | `newsletter-signup` | `marketing/` | `llm` + `notion` | Google | Lead capture with Notion |
107
+ | `text-generator` | `utility/` | `llm` | Google | Text generation |
108
+ | `multi-step-assistant` | `examples/` | `call-agent` + `llm` | Google | 4-step pipeline |
109
+
110
+ **Agent Categories:**
111
+
112
+ | Category | Folder | Description |
113
+ |----------|--------|-------------|
114
+ | Chat | `chat/` | Streaming chat assistants (different providers) |
115
+ | MCP | `mcp/` | MCP server integrations (GitMCP, Context7) |
116
+ | Google Business | `google-business/` | Google Business Profile API agents |
117
+ | Marketing | `marketing/` | Lead capture, newsletters, CRM |
118
+ | Utility | `utility/` | General-purpose tools |
119
+ | Examples | `examples/` | Demo pipelines |
99
120
 
100
121
  **Resolution Order:**
101
122
  1. User agents (`src/agents/*.yaml`) — allows override
102
- 2. Built-in agents (package) — fallback
123
+ 2. Built-in agents (package, via `BUILTIN_AGENT_PATHS` map) — fallback
103
124
 
104
125
  ---
105
126
 
@@ -8,7 +8,7 @@
8
8
  - `stream: true` → returns `Response` from `streamText`
9
9
  - `stream: false` → returns JSON object from `generateText`
10
10
  - **FR4:** The executor MUST immediately return a `Response` when a primitive returns a stream
11
- - **FR5:** The system MUST resolve variables using `$input.*` and `$stepResult.*` syntax
11
+ - **FR5:** The system MUST resolve variables using `$input.*`, `$stepResult.*`, and `$env.*` syntax
12
12
  - **FR6:** The `output-generator` primitive MUST perform deterministic JSON transformation without LLM calls
13
13
  - **FR7:** The `call-agent` primitive MUST allow recursive invocation of other YAML workflow files
14
14
  - **FR8:** The system MUST expose a REST endpoint (`POST /api/beddel/chat`) for chat interactions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beddel",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Declarative Sequential Pipeline Executor for YAML workflows with native streaming and Vercel AI SDK v6 support",
5
5
  "author": "Bota na Rede",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsc && npm run copy-agents",
34
- "copy-agents": "cp -r src/agents/*.yaml dist/agents/",
34
+ "copy-agents": "rsync -av --include='*/' --include='*.yaml' --exclude='*' src/agents/ dist/agents/",
35
35
  "dev": "tsc --watch"
36
36
  },
37
37
  "dependencies": {
@@ -13,8 +13,9 @@
13
13
 
14
14
  metadata:
15
15
  name: "Business Analyzer"
16
- version: "1.0.0"
16
+ version: "1.1.0"
17
17
  description: "Analyzes business reviews and generates actionable insights"
18
+ builtin: true
18
19
 
19
20
  workflow:
20
21
  # Step 1: Fetch all reviews with auto-pagination
@@ -46,20 +47,28 @@ workflow:
46
47
  5. Trend analysis comparing recent vs older reviews
47
48
  6. Actionable recommendations
48
49
 
49
- Format your response as structured JSON.
50
+ Return ONLY valid JSON (no markdown, no ```):
51
+ {
52
+ "sentimentScore": 8,
53
+ "positiveThemes": [{"theme": "Great service", "count": 15}],
54
+ "improvementAreas": [{"area": "Wait times", "count": 5}],
55
+ "urgentReviews": [{"reviewId": "xxx", "rating": 1, "issue": "summary"}],
56
+ "trendAnalysis": "Recent reviews show improvement...",
57
+ "recommendations": ["Focus on...", "Consider..."]
58
+ }
50
59
  messages:
51
60
  - role: "user"
52
- content: |
53
- Analyze these business reviews:
54
-
55
- Total Reviews: $stepResult.reviewsData.totalReviewCount
56
- Average Rating: $stepResult.reviewsData.averageRating
57
-
58
- Reviews Data:
59
- $stepResult.reviewsData.reviews
61
+ content: "$input.reviewsPayload"
62
+ result: "analysisRaw"
63
+
64
+ # Step 3: Parse analysis JSON
65
+ - id: "parse-analysis"
66
+ type: "output-generator"
67
+ config:
68
+ json: "$stepResult.analysisRaw.text"
60
69
  result: "analysis"
61
70
 
62
- # Step 3: Generate response suggestions for negative reviews
71
+ # Step 4: Generate response suggestions for negative reviews
63
72
  - id: "generate-responses"
64
73
  type: "llm"
65
74
  config:
@@ -76,24 +85,29 @@ workflow:
76
85
  - Keep responses under 150 words
77
86
  - Be specific to the complaint mentioned
78
87
 
79
- Format as JSON array with reviewId and suggestedResponse.
88
+ Return ONLY valid JSON array (no markdown, no ```):
89
+ [{"reviewId": "xxx", "rating": 2, "originalComment": "...", "suggestedResponse": "..."}]
90
+
91
+ If no negative reviews, return: []
80
92
  messages:
81
93
  - role: "user"
82
- content: |
83
- Generate response suggestions for these reviews:
84
- $stepResult.reviewsData.reviews
85
- result: "responseSuggestions"
94
+ content: "$input.reviewsPayload"
95
+ result: "responsesRaw"
86
96
 
87
- # Step 4: Compile final report
88
- - id: "compile-report"
97
+ # Step 5: Parse responses JSON
98
+ - id: "parse-responses"
89
99
  type: "output-generator"
90
100
  config:
91
- template:
92
- summary:
93
- totalReviews: "$stepResult.reviewsData.totalReviewCount"
94
- averageRating: "$stepResult.reviewsData.averageRating"
95
- analysisDate: "$input.analysisDate"
96
- analysis: "$stepResult.analysis.text"
97
- responseSuggestions: "$stepResult.responseSuggestions.text"
98
- rawReviews: "$stepResult.reviewsData.reviews"
99
- result: "finalReport"
101
+ json: "$stepResult.responsesRaw.text"
102
+ result: "responseSuggestions"
103
+
104
+ # Explicit Return: Define the API response contract
105
+ return:
106
+ success: true
107
+ summary:
108
+ totalReviews: "$stepResult.reviewsData.totalReviewCount"
109
+ averageRating: "$stepResult.reviewsData.averageRating"
110
+ pagesFetched: "$stepResult.reviewsData.data.pagesFetched"
111
+ analysis: "$stepResult.analysis"
112
+ responseSuggestions: "$stepResult.responseSuggestions"
113
+ reviews: "$stepResult.reviewsData.reviews"
@@ -3,6 +3,14 @@
3
3
  *
4
4
  * Lists all agents bundled with the beddel package.
5
5
  * These are available automatically without user configuration.
6
+ *
7
+ * Agents are organized by category:
8
+ * - chat/ Streaming chat assistants (different providers)
9
+ * - mcp/ MCP server integrations (GitMCP, Context7, etc.)
10
+ * - google-business/ Google Business Profile agents
11
+ * - marketing/ Lead capture, newsletters, CRM
12
+ * - utility/ General-purpose tools
13
+ * - examples/ Demo pipelines showing multi-step workflows
6
14
  */
7
15
 
8
16
  import { fileURLToPath } from 'url';
@@ -12,20 +20,37 @@ import { dirname, join } from 'path';
12
20
  const __filename = fileURLToPath(import.meta.url);
13
21
  const __dirname = dirname(__filename);
14
22
 
23
+ /**
24
+ * Built-in agent definitions with their category paths
25
+ */
26
+ export const BUILTIN_AGENT_PATHS: Record<string, string> = {
27
+ // Chat assistants
28
+ 'assistant': 'chat/assistant.yaml',
29
+ 'assistant-bedrock': 'chat/assistant-bedrock.yaml',
30
+ 'assistant-openrouter': 'chat/assistant-openrouter.yaml',
31
+
32
+ // MCP integrations
33
+ 'assistant-gitmcp': 'mcp/assistant-gitmcp.yaml',
34
+
35
+ // Google Business
36
+ 'business-analyzer': 'google-business/business-analyzer.yaml',
37
+
38
+ // Marketing
39
+ 'newsletter-signup': 'marketing/newsletter-signup.yaml',
40
+
41
+ // Utility
42
+ 'text-generator': 'utility/text-generator.yaml',
43
+
44
+ // Examples
45
+ 'multi-step-assistant': 'examples/multi-step-assistant.yaml',
46
+ };
47
+
15
48
  /**
16
49
  * List of built-in agent IDs available in the package
17
50
  */
18
- export const BUILTIN_AGENTS = [
19
- 'assistant',
20
- 'assistant-bedrock',
21
- 'assistant-openrouter',
22
- 'assistant-gitmcp',
23
- 'text-generator',
24
- 'multi-step-assistant',
25
- 'business-analyzer',
26
- ] as const;
51
+ export const BUILTIN_AGENTS = Object.keys(BUILTIN_AGENT_PATHS) as readonly string[];
27
52
 
28
- export type BuiltinAgentId = typeof BUILTIN_AGENTS[number];
53
+ export type BuiltinAgentId = keyof typeof BUILTIN_AGENT_PATHS;
29
54
 
30
55
  /**
31
56
  * Get the absolute path to the built-in agents directory
@@ -38,12 +63,37 @@ export function getBuiltinAgentsPath(): string {
38
63
  * Check if an agent ID is a built-in agent
39
64
  */
40
65
  export function isBuiltinAgent(agentId: string): agentId is BuiltinAgentId {
41
- return BUILTIN_AGENTS.includes(agentId as BuiltinAgentId);
66
+ return agentId in BUILTIN_AGENT_PATHS;
42
67
  }
43
68
 
44
69
  /**
45
70
  * Get the full path to a built-in agent YAML file
46
71
  */
47
- export function getBuiltinAgentPath(agentId: BuiltinAgentId): string {
48
- return join(__dirname, `${agentId}.yaml`);
72
+ export function getBuiltinAgentPath(agentId: string): string | null {
73
+ const relativePath = BUILTIN_AGENT_PATHS[agentId];
74
+ if (!relativePath) return null;
75
+ return join(__dirname, relativePath);
76
+ }
77
+
78
+ /**
79
+ * Get all agents in a specific category
80
+ */
81
+ export function getAgentsByCategory(category: string): string[] {
82
+ return Object.entries(BUILTIN_AGENT_PATHS)
83
+ .filter(([_, path]) => path.startsWith(`${category}/`))
84
+ .map(([id]) => id);
49
85
  }
86
+
87
+ /**
88
+ * Available categories
89
+ */
90
+ export const AGENT_CATEGORIES = [
91
+ 'chat',
92
+ 'mcp',
93
+ 'google-business',
94
+ 'marketing',
95
+ 'utility',
96
+ 'examples',
97
+ ] as const;
98
+
99
+ export type AgentCategory = typeof AGENT_CATEGORIES[number];
@@ -0,0 +1,106 @@
1
+ # Newsletter Signup Agent
2
+ # Registers users to the newsletter with AI analysis and saves to Notion
3
+ #
4
+ # Required Environment Variables:
5
+ # - NOTION_TOKEN (Internal Integration Secret)
6
+ # - NOTION_DATABASE_ID (Database ID for newsletter signups)
7
+ # - GEMINI_API_KEY (for AI analysis)
8
+ #
9
+ # Input:
10
+ # - name: User's name
11
+ # - email: User's email
12
+ # - message: Optional message from user
13
+ # - createdAt: ISO date string
14
+
15
+ metadata:
16
+ name: "Newsletter Signup Agent"
17
+ version: "1.3.0"
18
+ description: "Registers users to the newsletter with AI analysis and saves to Notion"
19
+ builtin: true
20
+
21
+ workflow:
22
+ # Step 1: Analyze user profile and generate structured JSON
23
+ - id: "analyze-user"
24
+ type: "llm"
25
+ config:
26
+ provider: "google"
27
+ model: "gemini-2.0-flash-exp"
28
+ system: |
29
+ You are a lead analyst for a technology newsletter.
30
+
31
+ Analyze the user data and return ONLY a valid JSON (no markdown, no ```):
32
+ {
33
+ "tags": [{"name": "Tag1"}],
34
+ "sentiment": {"name": "Positive"},
35
+ "summary": "One-line summary"
36
+ }
37
+
38
+ IMPORTANT RULES:
39
+ - tags: Array of objects with "name". Allowed values: "Support", "Sales", "Partnership", "Feedback"
40
+ - sentiment: Object with "name". Allowed values: "Positive", "Neutral", "Negative"
41
+ - summary: Short summary string (maximum 100 characters)
42
+
43
+ Example for a positive message about the product:
44
+ {"tags": [{"name": "Feedback"}], "sentiment": {"name": "Positive"}, "summary": "User praises the product"}
45
+
46
+ If there is no significant message:
47
+ {"tags": [{"name": "Feedback"}], "sentiment": {"name": "Neutral"}, "summary": "New newsletter subscriber"}
48
+
49
+ Return ONLY the JSON, nothing else.
50
+ messages:
51
+ - role: "user"
52
+ content: |
53
+ Name: $input.name
54
+ Email: $input.email
55
+ Message: $input.message
56
+ result: "userAnalysis"
57
+
58
+ # Step 2: Parse the JSON from LLM
59
+ - id: "parse-analysis"
60
+ type: "output-generator"
61
+ config:
62
+ json: "$stepResult.userAnalysis.text"
63
+ result: "parsedAnalysis"
64
+
65
+ # Step 3: Create Notion page with structured data
66
+ - id: "create-notion-entry"
67
+ type: "notion"
68
+ config:
69
+ action: "createPage"
70
+ parent:
71
+ type: "database_id"
72
+ database_id: "$env.NOTION_DATABASE_ID"
73
+ properties:
74
+ Name:
75
+ title:
76
+ - text:
77
+ content: "$input.name"
78
+ Email:
79
+ email: "$input.email"
80
+ Message:
81
+ rich_text:
82
+ - text:
83
+ content: "$input.message"
84
+ Summary:
85
+ rich_text:
86
+ - text:
87
+ content: "$stepResult.parsedAnalysis.summary"
88
+ Tags:
89
+ multi_select: "$stepResult.parsedAnalysis.tags"
90
+ Sentiment:
91
+ select: "$stepResult.parsedAnalysis.sentiment"
92
+ CreatedAt:
93
+ date:
94
+ start: "$input.createdAt"
95
+ result: "notionResult"
96
+
97
+ # Explicit Return: Define the API response contract
98
+ return:
99
+ success: true
100
+ message: "Registration completed successfully!"
101
+ notionPageId: "$stepResult.notionResult.pageId"
102
+ notionUrl: "$stepResult.notionResult.url"
103
+ analysis:
104
+ tags: "$stepResult.parsedAnalysis.tags"
105
+ sentiment: "$stepResult.parsedAnalysis.sentiment"
106
+ summary: "$stepResult.parsedAnalysis.summary"
@@ -3,7 +3,7 @@ import { loadYaml } from '../core/parser';
3
3
  import { WorkflowExecutor } from '../core/workflow';
4
4
  import { join, normalize } from 'path';
5
5
  import { access } from 'fs/promises';
6
- import { getBuiltinAgentsPath } from '../agents';
6
+ import { getBuiltinAgentPath } from '../agents';
7
7
 
8
8
  export interface BeddelHandlerOptions {
9
9
  /** Path to user-defined agents (relative to CWD). Default: 'src/agents' */
@@ -57,10 +57,10 @@ async function resolveAgentPath(
57
57
  return userPath;
58
58
  }
59
59
 
60
- // 2. Fallback: built-in agents from package
60
+ // 2. Fallback: built-in agents from package (supports subfolder structure)
61
61
  if (!disableBuiltinAgents) {
62
- const builtinPath = join(getBuiltinAgentsPath(), `${agentId}.yaml`);
63
- if (await fileExists(builtinPath)) {
62
+ const builtinPath = getBuiltinAgentPath(agentId);
63
+ if (builtinPath && await fileExists(builtinPath)) {
64
64
  return builtinPath;
65
65
  }
66
66
  }