@ema.co/mcp-toolkit 2026.2.27 → 2026.2.28

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 (58) hide show
  1. package/.context/public/guides/ema-user-guide.md +7 -6
  2. package/.context/public/guides/mcp-tools-guide.md +46 -23
  3. package/dist/config/index.js +11 -0
  4. package/dist/config/workflow-patterns.js +361 -0
  5. package/dist/mcp/autobuilder.js +2 -2
  6. package/dist/mcp/domain/generation-schema.js +15 -9
  7. package/dist/mcp/domain/structural-rules.js +3 -3
  8. package/dist/mcp/domain/validation-rules.js +20 -27
  9. package/dist/mcp/domain/workflow-generator.js +3 -3
  10. package/dist/mcp/domain/workflow-graph.js +1 -1
  11. package/dist/mcp/guidance.js +60 -1
  12. package/dist/mcp/handlers/conversation/adapter.js +13 -0
  13. package/dist/mcp/handlers/conversation/create.js +19 -0
  14. package/dist/mcp/handlers/conversation/delete.js +18 -0
  15. package/dist/mcp/handlers/conversation/formatters.js +62 -0
  16. package/dist/mcp/handlers/conversation/history.js +15 -0
  17. package/dist/mcp/handlers/conversation/index.js +43 -0
  18. package/dist/mcp/handlers/conversation/list.js +40 -0
  19. package/dist/mcp/handlers/conversation/messages.js +13 -0
  20. package/dist/mcp/handlers/conversation/rename.js +16 -0
  21. package/dist/mcp/handlers/conversation/send.js +90 -0
  22. package/dist/mcp/handlers/data/index.js +169 -3
  23. package/dist/mcp/handlers/feedback/client-id.js +49 -0
  24. package/dist/mcp/handlers/feedback/coalesce.js +167 -0
  25. package/dist/mcp/handlers/feedback/index.js +42 -1
  26. package/dist/mcp/handlers/feedback/outbox.js +301 -0
  27. package/dist/mcp/handlers/feedback/probes.js +127 -0
  28. package/dist/mcp/handlers/feedback/remote-store.js +59 -0
  29. package/dist/mcp/handlers/feedback/store.js +13 -1
  30. package/dist/mcp/handlers/persona/delete.js +7 -28
  31. package/dist/mcp/handlers/persona/update.js +7 -26
  32. package/dist/mcp/handlers/persona/version.js +30 -15
  33. package/dist/mcp/handlers/template/adapter.js +23 -0
  34. package/dist/mcp/handlers/template/crud.js +174 -0
  35. package/dist/mcp/handlers/template/index.js +6 -7
  36. package/dist/mcp/handlers/workflow/adapter.js +30 -46
  37. package/dist/mcp/handlers/workflow/index.js +2 -2
  38. package/dist/mcp/handlers/workflow/validation.js +2 -2
  39. package/dist/mcp/knowledge-guidance-topics.js +90 -53
  40. package/dist/mcp/knowledge.js +7 -357
  41. package/dist/mcp/prompts.js +5 -5
  42. package/dist/mcp/resources-dynamic.js +46 -38
  43. package/dist/mcp/resources-validation.js +5 -5
  44. package/dist/mcp/server.js +38 -5
  45. package/dist/mcp/tools.js +340 -8
  46. package/dist/sdk/client-adapter.js +90 -2
  47. package/dist/sdk/client.js +7 -0
  48. package/dist/sdk/ema-client.js +242 -27
  49. package/dist/sdk/generated/agent-catalog.js +96 -39
  50. package/dist/sdk/generated/deprecated-actions.js +1 -1
  51. package/dist/sdk/grpc-client.js +67 -5
  52. package/dist/sync/central-factory.js +86 -0
  53. package/dist/sync/central-version-storage.js +387 -0
  54. package/dist/sync/dis-port.js +75 -0
  55. package/dist/sync/version-policy.js +29 -31
  56. package/dist/sync/version-storage-interface.js +11 -0
  57. package/dist/sync/version-storage.js +22 -22
  58. package/package.json +2 -1
@@ -98,7 +98,7 @@ audience: public
98
98
  |------------|---------|
99
99
  | `chat_categorizer` | Classifies user intent |
100
100
  | `search` | Retrieves relevant documents |
101
- | `respond_for_external_actions` | Generates answer with citations using search/tool results |
101
+ | `respond_for_external_actions` | Explains external_action_caller results in conversation context |
102
102
  | `external_action_caller` | Invokes external APIs/tools |
103
103
  | `call_llm` | Custom LLM response generation |
104
104
  | `generate_document` | Creates Word/PDF documents |
@@ -661,12 +661,13 @@ outputs:
661
661
  Generate answers using LLMs.
662
662
 
663
663
  ```yaml
664
- # Response Generation Example
665
- name: "respond_for_external_actions"
666
- action: "actions.emainternal.respond_for_external_actions"
664
+ # Response Generation Example (most common: call_llm with named_inputs)
665
+ name: "respond"
666
+ action: "actions.emainternal.call_llm"
667
667
  inputs:
668
668
  query: "{{trigger.user_query}}"
669
- external_action_result: "{{search.search_results}}"
669
+ named_inputs_Search_Results: "{{search.search_results}}"
670
+ named_inputs_Conversation: "{{trigger.chat_conversation}}"
670
671
  user_instructions: |
671
672
  You are an HR assistant. Answer questions using only
672
673
  the provided documents. Cite your sources.
@@ -1112,7 +1113,7 @@ When an AI Employee isn't working correctly:
1112
1113
  # Graceful degradation example
1113
1114
  runIf:
1114
1115
  condition: "{{search.search_results.length}} > 0"
1115
- onTrue: "respond_for_external_actions"
1116
+ onTrue: "respond"
1116
1117
  onFalse: "fallback_response"
1117
1118
 
1118
1119
  # Fallback configuration
@@ -3,6 +3,7 @@ title: "Ema MCP Tools Guide"
3
3
  date: 2026-02-13
4
4
  audience: public
5
5
  ---
6
+
6
7
  # Ema MCP Tools Guide
7
8
 
8
9
  This MCP server is intentionally designed for **LLM + agent ergonomics**:
@@ -23,14 +24,14 @@ Some clients show a **prefixed name** (for example Cursor: `mcp_ema_persona`). A
23
24
 
24
25
  ## The tool set (6 public tools)
25
26
 
26
- | Tool | Purpose | Key Parameter |
27
- |------|---------|---------------|
28
- | `persona` | AI Employee management (CRUD, data, snapshots, sanitization) | `method` (required) |
29
- | `catalog` | Browse platform catalog (actions, templates, widgets, voices, patterns, concepts) | `method` + `type` (required) |
30
- | `workflow` | Get workflow data, validate, deploy LLM-generated workflows, optimize | `mode` (required) |
31
- | `sync` | Sync personas between environments | `method` (required) |
32
- | `env` | List available environments | No params |
33
- | `toolkit_feedback` | Submit feedback about the toolkit | `method` (required) |
27
+ | Tool | Purpose | Key Parameter |
28
+ | ------------------ | --------------------------------------------------------------------------------- | ---------------------------- |
29
+ | `persona` | AI Employee management (CRUD, data, snapshots, sanitization) | `method` (required) |
30
+ | `catalog` | Browse platform catalog (actions, templates, widgets, voices, patterns, concepts) | `method` + `type` (required) |
31
+ | `workflow` | Get workflow data, validate, deploy LLM-generated workflows, optimize | `mode` (required) |
32
+ | `sync` | Sync personas between environments | `method` (required) |
33
+ | `env` | List available environments | No params |
34
+ | `toolkit_feedback` | Submit feedback about the toolkit | `method` (required) |
34
35
 
35
36
  **LLM does all thinking** (analyze, compare, generate). MCP only provides data and executes.
36
37
 
@@ -106,6 +107,7 @@ persona(method="create", from="source-id", name="Demo Clone", actions=["standard
106
107
  ```
107
108
 
108
109
  **Available aliases:**
110
+
109
111
  - `"copy-data"` - Copy data from source
110
112
  - `"copy-and-sanitize"` - Copy then sanitize
111
113
  - `"standard-demo-setup"` - Copy, sanitize, snapshot
@@ -123,6 +125,7 @@ persona(method="sanitize", id="abc", examples=["Acme Corp", "john@example.com"],
123
125
  ```
124
126
 
125
127
  **Sanitization Notes:**
128
+
126
129
  - **Two-step process**: First call returns preview with `items_needing_review` count. Second call applies with `apply=true`
127
130
  - **Auto-detects**: emails, phones, SSNs, credit cards (by issuer prefix), API keys, JWTs
128
131
  - **NOT detected by default**: UUIDs (typically system IDs like `template_id`, `persona_id`)
@@ -180,6 +183,7 @@ persona(method="restore", id="abc", version="v3")
180
183
  Valid methods: `list`, `get`, `create`, `update`, `delete`, `sanitize`, `schema`, `snapshot`, `history`, `restore`
181
184
 
182
185
  **List/Get:**
186
+
183
187
  ```
184
188
  persona(method="list")
185
189
  persona(method="list", query="support", status="active")
@@ -189,6 +193,7 @@ persona(method="get", id="abc", include_fingerprint=true) // for stale-state pr
189
193
  ```
190
194
 
191
195
  **Create:**
196
+
192
197
  ```
193
198
  persona(method="create", name="My Bot", type="voice")
194
199
  persona(method="create", name="My Bot", from="Voice AI Starter") // from template
@@ -196,22 +201,26 @@ persona(method="create", name="My Bot", from="source-persona-id") // clone
196
201
  ```
197
202
 
198
203
  **Update config:**
204
+
199
205
  ```
200
206
  persona(method="update", id="abc", base_fingerprint="...", config={widgets: [{...}]})
201
207
  ```
202
208
 
203
209
  **Delete:**
210
+
204
211
  ```
205
212
  persona(method="delete", id="abc", confirm=true)
206
213
  ```
207
214
 
208
215
  **Sanitize (two-step process):**
216
+
209
217
  ```
210
218
  persona(method="sanitize", id="abc") // Step 1: Preview - identify PII
211
219
  persona(method="sanitize", id="abc", examples=["Acme"], apply=true) // Step 2: Apply
212
220
  ```
213
221
 
214
222
  **Version management:**
223
+
215
224
  ```
216
225
  persona(method="snapshot", id="abc", message="Before update")
217
226
  persona(method="history", id="abc")
@@ -219,11 +228,13 @@ persona(method="restore", id="abc", version="v2")
219
228
  ```
220
229
 
221
230
  **Schema:**
231
+
222
232
  ```
223
233
  persona(method="schema", id="abc") // Get input schema (dashboard columns, types)
224
234
  ```
225
235
 
226
236
  **Data sub-resource (requires id):**
237
+
227
238
  ```
228
239
  persona(id="abc", data={method:"list"})
229
240
  persona(id="abc", data={method:"stats"})
@@ -247,6 +258,7 @@ persona(id="abc", data={method:"search", query:"pricing"})
247
258
  > **For other templates:** Call `persona(method="get", id="abc", include_workflow=true)` and inspect `proto_config.widgets[].name` to find the exact widget names.
248
259
 
249
260
  **Replicate (copy by reference - fast, no file transfer):**
261
+
250
262
  ```
251
263
  persona(id="target-id", data={method:"replicate", from:"source-id"})
252
264
  persona(id="target-id", data={method:"replicate", from:"source-id", widget_mappings:[{source_widget:"docs", target_widget:"documents"}]})
@@ -336,10 +348,15 @@ Valid methods: `submit`, `list`, `analyze`
336
348
  toolkit_feedback(method="submit", category="gap", message="Missing docs on extraction columns")
337
349
  toolkit_feedback(method="submit", category="confusion", message="...", tool="workflow")
338
350
  toolkit_feedback(method="submit", category="success", message="Workflow deploy guidance was clear")
351
+ toolkit_feedback(method="submit", category="probe_response", message="<answer>", context="<probe.id>")
339
352
  toolkit_feedback(method="list")
340
353
  toolkit_feedback(method="analyze")
341
354
  ```
342
355
 
356
+ Tool responses may include a `_probe` field with a targeted question. Respond using `category="probe_response"` with the probe's `id` as `context`.
357
+
358
+ Feedback is collected locally and periodically uploaded as coalesced digests to improve the toolkit globally. Set `EMA_FEEDBACK_DISABLED=1` to opt out.
359
+
343
360
  ## Resources (read-first)
344
361
 
345
362
  Start with:
@@ -376,18 +393,21 @@ workflow(mode="deploy", persona_id="abc", base_fingerprint="...", workflow_def={
376
393
  3. **NEVER** copy workflow patterns from existing personas (they may use deprecated actions!)
377
394
 
378
395
  **Correct workflow pattern:**
396
+
379
397
  ```
380
- chat_trigger -> search/v2 -> respond_for_external_actions -> WORKFLOW_OUTPUT
398
+ chat_trigger -> search/v2 -> call_llm (via named_inputs_Search_Results) -> WORKFLOW_OUTPUT
381
399
  ```
382
400
 
383
401
  **Deprecated (DO NOT USE):**
402
+
384
403
  ```
385
404
  search/v0 -> Use search/v2
386
- respond_with_sources/v0 -> Use respond_for_external_actions
405
+ respond_with_sources/v0 -> Use call_llm with named_inputs (for KB Q&A)
387
406
  call_llm/v0 -> Use call_llm/v2
388
407
  ```
389
408
 
390
409
  **Common issues to check when generating:**
410
+
391
411
  - Missing `WORKFLOW_OUTPUT` in results
392
412
  - Missing Fallback category in categorizers
393
413
  - Orphan nodes not connected to output
@@ -400,13 +420,14 @@ call_llm/v0 -> Use call_llm/v2
400
420
 
401
421
  HITL is a **flag on specific action nodes**, not a standalone workflow node. Set `disable_human_interaction: false` on the node that needs approval.
402
422
 
403
- | Scenario | Default | How to Add HITL |
404
- |----------|---------|-----------------|
405
- | Send email | No HITL | Set `disable_human_interaction: false` on `send_email_agent` node |
406
- | External API | No HITL | Set `disable_human_interaction: false` on `external_action_caller` node |
407
- | Create records | No HITL | Set `disable_human_interaction: false` on the action node |
423
+ | Scenario | Default | How to Add HITL |
424
+ | -------------- | ------- | ----------------------------------------------------------------------- |
425
+ | Send email | No HITL | Set `disable_human_interaction: false` on `send_email_agent` node |
426
+ | External API | No HITL | Set `disable_human_interaction: false` on `external_action_caller` node |
427
+ | Create records | No HITL | Set `disable_human_interaction: false` on the action node |
408
428
 
409
429
  **Counter-intuitive naming:**
430
+
410
431
  - `disable_human_interaction: false` -> HITL **ON** (requires approval)
411
432
  - `disable_human_interaction: true` -> HITL **OFF** (auto-proceeds)
412
433
 
@@ -419,6 +440,7 @@ HITL is a **flag on specific action nodes**, not a standalone workflow node. Set
419
440
  Auto Builder has strict prompt length limits (~2500 chars).
420
441
 
421
442
  ### Good Prompt Format
443
+
422
444
  ```text
423
445
  Voice AI for [use case]. Supports [capabilities].
424
446
 
@@ -433,6 +455,7 @@ Rules:
433
455
  ```
434
456
 
435
457
  ### Bad Prompt Format (Times Out)
458
+
436
459
  - Narrative descriptions with multiple paragraphs
437
460
  - Example conversations embedded in prompt
438
461
  - Prompts > 2500 characters
@@ -441,11 +464,11 @@ Rules:
441
464
 
442
465
  These tool names still work for backwards compatibility but are not exposed in the tool list. Use the current equivalents instead.
443
466
 
444
- | Deprecated Tool | Use Instead |
445
- |----------------|-------------|
446
- | `knowledge` | `persona(id=..., data={method:...})` |
447
- | `demo` | `persona` + data sub-resource |
448
- | `data` | `persona(id=..., data={method:...})` |
449
- | `action` | `catalog(method=..., type="actions")` |
450
- | `template` | `catalog(method=..., type="templates")` |
451
- | `reference` | `catalog(method=..., type=...)` |
467
+ | Deprecated Tool | Use Instead |
468
+ | --------------- | --------------------------------------- |
469
+ | `knowledge` | `persona(id=..., data={method:...})` |
470
+ | `demo` | `persona` + data sub-resource |
471
+ | `data` | `persona(id=..., data={method:...})` |
472
+ | `action` | `catalog(method=..., type="actions")` |
473
+ | `template` | `catalog(method=..., type="templates")` |
474
+ | `reference` | `catalog(method=..., type=...)` |
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Central Configuration — Single Source of Truth
3
+ *
4
+ * Declarative workflow patterns and related config data.
5
+ * TypeScript code imports from this module; docs/rules reference these files.
6
+ *
7
+ * Current scope: WORKFLOW_PATTERNS (intent-first workflow shapes).
8
+ * Action definitions live in src/sdk/generated/agent-catalog.ts.
9
+ * Future: QUALIFYING_QUESTIONS, COMMON_MISTAKES may migrate here.
10
+ */
11
+ export { WORKFLOW_PATTERNS } from "./workflow-patterns.js";
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Workflow Patterns — Central Config (SINGLE SOURCE OF TRUTH)
3
+ *
4
+ * Intent-first patterns: describe WHAT you want to accomplish, then list
5
+ * action OPTIONS per stage. Never prescribe a single action for a stage.
6
+ *
7
+ * RULES:
8
+ * - respond_for_external_actions is ONLY for external_action_caller results
9
+ * - search output can feed ANY downstream node (call_llm, custom_agent, etc.)
10
+ * - call_llm is the most common post-search node (89% of production personas)
11
+ * - Production data trumps theoretical patterns
12
+ *
13
+ * Referenced by: knowledge.ts, resources-dynamic.ts, CLAUDE.md, SKILL.md
14
+ * When updating: `npm run build && npm test` — no other files need manual sync.
15
+ */
16
+ export const WORKFLOW_PATTERNS = [
17
+ // ─── Chat Patterns ──────────────────────────────────────────────────────
18
+ {
19
+ name: "search-and-respond",
20
+ personaType: "chat",
21
+ description: "Search knowledge base and respond to the user. The most common chat pattern.",
22
+ intent: "Look up information in a knowledge base and answer the user's question",
23
+ shape: [
24
+ { stage: "trigger", purpose: "Entry point for user message", actionOptions: ["chat_trigger"] },
25
+ { stage: "refine", purpose: "Convert conversation to search query (optional, for multi-turn)", actionOptions: ["conversation_to_search_query"], notes: "Optional — use when chat_conversation needs to become a focused query" },
26
+ { stage: "retrieve", purpose: "Search uploaded documents or knowledge base", actionOptions: ["search"], notes: "Single search node handles multiple datastores via datastore_configs" },
27
+ { stage: "respond", purpose: "Generate response from search results", actionOptions: ["call_llm", "custom_agent", "respond_with_sources", "document_synthesis"], notes: "call_llm with named_inputs_Search_Results is the dominant production pattern (89%). respond_with_sources adds automatic citations. custom_agent for specialized behavior." },
28
+ { stage: "output", purpose: "Return response to user", actionOptions: ["WORKFLOW_OUTPUT"] },
29
+ ],
30
+ exampleWiring: [
31
+ "chat_trigger.chat_conversation → conversation_to_search_query.conversation",
32
+ "conversation_to_search_query.summarized_conversation → search.query",
33
+ "search.search_results → call_llm.named_inputs_Search_Results",
34
+ "chat_trigger.user_query → call_llm.query",
35
+ "chat_trigger.chat_conversation → call_llm.named_inputs_Conversation",
36
+ "call_llm.response_with_sources → WORKFLOW_OUTPUT",
37
+ ],
38
+ useCase: "FAQ bot, documentation assistant, policy lookup, knowledge base Q&A",
39
+ antiPatterns: [
40
+ "Wiring search.search_results to respond_for_external_actions (that node is only for external_action_caller results)",
41
+ "Connecting chat_conversation directly to search.query (type mismatch — use conversation_to_search_query)",
42
+ "Forgetting to upload data sources to the persona (search returns empty results)",
43
+ ],
44
+ flexPoints: [
45
+ "respond stage can be any generation node — choice depends on whether you need citations, custom behavior, or simple LLM response",
46
+ "refine stage is optional — for single-turn, trigger.user_query can go directly to search.query",
47
+ "search output can feed ANY downstream node that accepts search_results or named_inputs",
48
+ ],
49
+ },
50
+ {
51
+ name: "intent-routing",
52
+ personaType: "chat",
53
+ description: "Route conversations to different handlers based on intent classification",
54
+ intent: "Classify what the user wants and route to the appropriate handler",
55
+ shape: [
56
+ { stage: "trigger", purpose: "Entry point for user message", actionOptions: ["chat_trigger"] },
57
+ { stage: "classify", purpose: "Determine user intent", actionOptions: ["chat_categorizer"], notes: "MUST include a Fallback category" },
58
+ { stage: "handle", purpose: "Process each intent differently", actionOptions: ["call_llm", "search", "external_action_caller", "fixed_response", "custom_agent"], notes: "Each category gets its own handler via runIf conditions" },
59
+ { stage: "output", purpose: "Return response to user", actionOptions: ["WORKFLOW_OUTPUT"] },
60
+ ],
61
+ exampleWiring: [
62
+ "chat_trigger.chat_conversation → chat_categorizer.conversation",
63
+ "handler_1 (runIf: categorizer.category == Category1)",
64
+ "handler_2 (runIf: categorizer.category == Category2)",
65
+ "fallback_response (runIf: categorizer.category == Fallback)",
66
+ "handler_*.response → WORKFLOW_OUTPUT",
67
+ ],
68
+ useCase: "Multi-purpose assistant with distinct capabilities (HR + IT + General)",
69
+ antiPatterns: [
70
+ "Missing Fallback category",
71
+ "Categories without outgoing edges",
72
+ "Not all paths leading to WORKFLOW_OUTPUT",
73
+ "Only one gated respond node wired to WORKFLOW_OUTPUT while others are silently lost",
74
+ ],
75
+ },
76
+ {
77
+ name: "tool-calling",
78
+ personaType: "chat",
79
+ description: "Call external tools (APIs) and explain results to user",
80
+ intent: "Execute actions in external systems and communicate the outcome",
81
+ shape: [
82
+ { stage: "trigger", purpose: "Entry point", actionOptions: ["chat_trigger"] },
83
+ { stage: "classify", purpose: "Route to appropriate tool (optional)", actionOptions: ["chat_categorizer"], notes: "Optional if only one tool" },
84
+ { stage: "act", purpose: "Call external API", actionOptions: ["external_action_caller"], notes: "Requires tools array configuration" },
85
+ { stage: "respond", purpose: "Explain tool results to user", actionOptions: ["call_llm", "respond_for_external_actions"], notes: "respond_for_external_actions is designed for this — explaining external tool results. call_llm also works via named_inputs." },
86
+ { stage: "output", purpose: "Return response", actionOptions: ["WORKFLOW_OUTPUT"] },
87
+ ],
88
+ exampleWiring: [
89
+ "chat_trigger.chat_conversation → chat_categorizer.conversation",
90
+ "external_action_caller (runIf: categorizer.category == <Action>)",
91
+ "chat_trigger.chat_conversation → external_action_caller.conversation",
92
+ "external_action_caller.tool_execution_result → call_llm.named_inputs_Tool_Result",
93
+ "chat_trigger.user_query → call_llm.query",
94
+ "call_llm.response_with_sources → WORKFLOW_OUTPUT",
95
+ ],
96
+ useCase: "IT helpdesk with ticketing, CRM operations, customer service with tools",
97
+ antiPatterns: [
98
+ "Creating duplicate records on follow-up questions",
99
+ "Not checking conversation history before actions",
100
+ "external_action_caller does NOT support HITL — cannot gate tool calls with human approval",
101
+ ],
102
+ },
103
+ {
104
+ name: "search-then-process",
105
+ personaType: "chat",
106
+ description: "Look up context, then use it N ways downstream (extraction, routing, email, doc gen)",
107
+ intent: "Retrieve relevant context from knowledge base and use it to drive multiple downstream actions",
108
+ shape: [
109
+ { stage: "trigger", purpose: "Entry point", actionOptions: ["chat_trigger"] },
110
+ { stage: "retrieve", purpose: "Get relevant context", actionOptions: ["search", "live_web_search"] },
111
+ { stage: "reason", purpose: "Process retrieved context with LLM", actionOptions: ["call_llm", "custom_agent"], notes: "LLM acts as intermediary processor, not just responder" },
112
+ { stage: "act", purpose: "Downstream action based on reasoning", actionOptions: ["entity_extraction_with_documents", "text_categorizer", "external_action_caller", "send_email_agent", "document_synthesis", "generate_document"], notes: "Most common: extraction (65% of multi-hop chains), routing, or action" },
113
+ { stage: "output", purpose: "Return result", actionOptions: ["WORKFLOW_OUTPUT"] },
114
+ ],
115
+ exampleWiring: [
116
+ "search.search_results → call_llm.named_inputs_Context",
117
+ "call_llm.response_with_sources → entity_extraction_with_documents.documents",
118
+ "entity_extraction_with_documents.extraction_columns → WORKFLOW_OUTPUT",
119
+ ],
120
+ useCase: "Research assistant, data gathering + processing, context-driven automation",
121
+ flexPoints: [
122
+ "The 'act' stage can be anything — the search+reason stages provide context for whatever comes next",
123
+ "Multiple downstream actions can run in parallel or sequence",
124
+ ],
125
+ },
126
+ {
127
+ name: "consolidated-intent-routing",
128
+ personaType: "chat",
129
+ description: "Single respond node handles all intents by receiving the category as a named_input",
130
+ intent: "Route intents but consolidate response generation into one node to reduce duplication",
131
+ shape: [
132
+ { stage: "trigger", purpose: "Entry point", actionOptions: ["chat_trigger"] },
133
+ { stage: "classify", purpose: "Determine intent", actionOptions: ["chat_categorizer"] },
134
+ { stage: "retrieve", purpose: "Search (always runs, not gated)", actionOptions: ["search"] },
135
+ { stage: "respond", purpose: "Single LLM with category as named_input", actionOptions: ["call_llm"], notes: "Pass category as named_inputs_Intent so LLM knows which intent to address" },
136
+ { stage: "fallback", purpose: "Handle unknown intents", actionOptions: ["fixed_response"] },
137
+ { stage: "output", purpose: "Return response", actionOptions: ["WORKFLOW_OUTPUT"] },
138
+ ],
139
+ exampleWiring: [
140
+ "chat_trigger.chat_conversation → chat_categorizer.conversation",
141
+ "chat_trigger.user_query → search.query (always runs, not gated)",
142
+ "chat_categorizer.category → call_llm.named_inputs_Intent",
143
+ "search.search_results → call_llm.named_inputs_Search_Results",
144
+ "call_llm.response_with_sources → WORKFLOW_OUTPUT (runIf: category != Fallback)",
145
+ "fixed_response.response → WORKFLOW_OUTPUT (runIf: category == Fallback)",
146
+ ],
147
+ useCase: "Multi-intent assistant where all intents share similar response style and search sources",
148
+ antiPatterns: [
149
+ "Using this when intents need different tools or actions (use separate nodes instead)",
150
+ "Forgetting to pass the category as named_input (LLM won't know which intent to address)",
151
+ ],
152
+ },
153
+ {
154
+ name: "human-approval",
155
+ personaType: "chat",
156
+ description: "Pause workflow for human review/approval before executing side effects",
157
+ intent: "Get human confirmation or input before performing actions with real-world consequences",
158
+ shape: [
159
+ { stage: "trigger", purpose: "Entry point", actionOptions: ["chat_trigger"] },
160
+ { stage: "prepare", purpose: "Gather context for review", actionOptions: ["search", "call_llm", "entity_extraction_with_documents"] },
161
+ { stage: "review", purpose: "Human review checkpoint", actionOptions: ["general_hitl"], notes: "Conversational mode: define success/failure criteria. Standard Form: structured approval fields. Custom Form: external approval UI." },
162
+ { stage: "act-on-success", purpose: "Execute action after approval (runIf: HITL Success)", actionOptions: ["send_email_agent", "external_action_caller", "call_llm"] },
163
+ { stage: "handle-failure", purpose: "Handle rejection (runIf: HITL Failure)", actionOptions: ["call_llm", "fixed_response"] },
164
+ { stage: "output", purpose: "Return result", actionOptions: ["WORKFLOW_OUTPUT"] },
165
+ ],
166
+ exampleWiring: [
167
+ "search.search_results → call_llm.named_inputs_Context",
168
+ "call_llm.response_with_sources → general_hitl.named_inputs_Draft",
169
+ "chat_trigger.chat_conversation → general_hitl.conversation",
170
+ "general_hitl → send_email_agent (runIf: status == 'HITL Success')",
171
+ "general_hitl → fixed_response (runIf: status == 'HITL Failure')",
172
+ ],
173
+ useCase: "Email approval before sending, content review, transaction confirmation, data correction",
174
+ antiPatterns: [
175
+ "Not defining clear success/failure criteria (categorizer defaults to Continue indefinitely)",
176
+ "Setting max_exchanges too low for complex reviews (causes premature HITL Failure)",
177
+ "Not handling the HITL Failure path (user rejection goes nowhere)",
178
+ "Using send_email_agent's HITL flag when you need dialogue — use general_hitl's Conversational mode instead",
179
+ ],
180
+ flexPoints: [
181
+ "send_email_agent and entity_extraction_with_documents also have built-in HITL flags (simpler yes/no)",
182
+ "general_hitl Conversational mode allows multi-turn dialogue with the reviewer",
183
+ "Standard Form mode collects structured data without needing entity_extraction",
184
+ ],
185
+ },
186
+ // ─── Dashboard Patterns ─────────────────────────────────────────────────
187
+ {
188
+ name: "document-processing",
189
+ personaType: "dashboard",
190
+ description: "Document upload, extraction, validation, and analysis (results as dashboard columns)",
191
+ intent: "Extract structured data from uploaded documents and optionally validate it",
192
+ shape: [
193
+ { stage: "trigger", purpose: "Document upload triggers workflow", actionOptions: ["document_trigger"] },
194
+ { stage: "extract", purpose: "Extract structured data", actionOptions: ["entity_extraction_with_documents"] },
195
+ { stage: "validate", purpose: "Validate against business rules (optional)", actionOptions: ["rule_validation_with_documents"], notes: "Rules configured in UI, not workflow_def" },
196
+ { stage: "analyze", purpose: "Additional LLM analysis (optional)", actionOptions: ["call_llm", "custom_agent"] },
197
+ { stage: "results", purpose: "Map outputs to dashboard columns", actionOptions: ["results (dot-notation)"] },
198
+ ],
199
+ exampleWiring: [
200
+ "workflowInput.document-mmf2 → entity_extraction_with_documents.documents",
201
+ "entity_extraction_with_documents.extraction_columns → rule_validation_with_documents.map_of_extracted_columns",
202
+ "workflowInput.document-mmf2 → rule_validation_with_documents.primary_docs",
203
+ "entity_extraction_with_documents.extraction_columns → results (dot-notation: '<nodeId>.extraction_columns')",
204
+ "rule_validation_with_documents.ruleset_output → results (dot-notation: '<nodeId>.ruleset_output')",
205
+ ],
206
+ useCase: "Invoice processing, contract analysis, compliance checking",
207
+ antiPatterns: [
208
+ "Not mapping extraction/validation outputs to results (dashboard columns won't appear)",
209
+ "Missing primary_docs on rule_validation_with_documents (validator needs original documents)",
210
+ ],
211
+ },
212
+ {
213
+ name: "dashboard-email-notification",
214
+ personaType: "dashboard",
215
+ description: "Extract data from documents and send email notifications with type conversion intermediaries",
216
+ intent: "Process uploaded documents and send email alerts based on extracted data",
217
+ shape: [
218
+ { stage: "trigger", purpose: "Document upload", actionOptions: ["document_trigger"] },
219
+ { stage: "extract", purpose: "Extract structured data", actionOptions: ["entity_extraction_with_documents"] },
220
+ { stage: "convert", purpose: "Convert extracted data to email-compatible types", actionOptions: ["json_mapper", "fixed_response"], notes: "Type intermediary: entity_extraction outputs ANY, email requires TEXT_WITH_SOURCES" },
221
+ { stage: "compose", purpose: "Generate email body (optional)", actionOptions: ["call_llm", "custom_agent", "fixed_response"] },
222
+ { stage: "send", purpose: "Send email", actionOptions: ["send_email_agent"], notes: "Enable HITL flag if approval needed" },
223
+ { stage: "results", purpose: "Map to dashboard columns", actionOptions: ["results (dot-notation)"] },
224
+ ],
225
+ exampleWiring: [
226
+ "entity_extraction_with_documents.extraction_columns → json_mapper.input_json",
227
+ "json_mapper.output_json → fixed_response.named_inputs_Extracted_Data",
228
+ "fixed_response.response → send_email_agent.email_to",
229
+ "call_llm.llm_output → send_email_agent.email_body",
230
+ ],
231
+ useCase: "Invoice receipt notification, contract alerts, document-triggered emails",
232
+ antiPatterns: [
233
+ "DO NOT wire entity_extraction directly to send_email — type mismatch (ANY vs TEXT_WITH_SOURCES)",
234
+ "Use json_mapper + fixed_response as intermediary for type conversion",
235
+ ],
236
+ },
237
+ {
238
+ name: "multi-phase-validation",
239
+ personaType: "dashboard",
240
+ description: "Sequential validation phases on extracted data, each checking a different concern",
241
+ intent: "Apply multiple layers of validation rules to extracted document data",
242
+ shape: [
243
+ { stage: "trigger", purpose: "Document upload", actionOptions: ["document_trigger"] },
244
+ { stage: "extract", purpose: "Extract entities", actionOptions: ["entity_extraction_with_documents"] },
245
+ { stage: "validate-n", purpose: "Sequential validation phases", actionOptions: ["rule_validation_with_documents"], notes: "Each phase checks a different concern (format → compliance → cross-reference)" },
246
+ { stage: "summarize", purpose: "Final analysis", actionOptions: ["call_llm"] },
247
+ { stage: "results", purpose: "Per-phase dashboard columns", actionOptions: ["results (dot-notation)"] },
248
+ ],
249
+ exampleWiring: [
250
+ "entity_extraction → validation_phase_1 → validation_phase_2 → validation_phase_3 → call_llm",
251
+ "Each phase: .ruleset_output → next_phase.map_of_extracted_columns",
252
+ "All phases: .ruleset_output → results (dot-notation)",
253
+ ],
254
+ useCase: "Invoice processing with multi-step validation, regulatory document review",
255
+ antiPatterns: [
256
+ "Running all rules in a single phase (loses granularity)",
257
+ "Not passing primary_docs to each validation phase",
258
+ ],
259
+ },
260
+ {
261
+ name: "confidence-dual-path",
262
+ personaType: "dashboard",
263
+ description: "Fork into auto-process and escalate paths based on confidence/risk score",
264
+ intent: "Automatically handle high-confidence items while escalating uncertain ones for human review",
265
+ shape: [
266
+ { stage: "trigger", purpose: "Document upload", actionOptions: ["document_trigger"] },
267
+ { stage: "extract", purpose: "Extract data", actionOptions: ["entity_extraction_with_documents"] },
268
+ { stage: "validate", purpose: "Score confidence", actionOptions: ["rule_validation_with_documents", "call_llm"] },
269
+ { stage: "auto-path", purpose: "Process high-confidence items", actionOptions: ["send_email_agent"], notes: "No HITL, runs automatically" },
270
+ { stage: "escalate-path", purpose: "Route low-confidence for review", actionOptions: ["send_email_agent"], notes: "HITL enabled (disable_human_interaction: false)" },
271
+ { stage: "results", purpose: "Dashboard columns", actionOptions: ["results (dot-notation)"] },
272
+ ],
273
+ exampleWiring: [
274
+ "call_llm.llm_output → send_email_auto (runIf: validation_status == PASS)",
275
+ "call_llm.llm_output → send_email_escalate (runIf: validation_status != PASS)",
276
+ ],
277
+ useCase: "AP invoice processing, dunning workflows, contract approvals",
278
+ antiPatterns: [
279
+ "Using a single send_email for both paths (loses ability to gate high-risk sends)",
280
+ "Hardcoding threshold — use validation rules output to drive routing",
281
+ ],
282
+ },
283
+ {
284
+ name: "document-intake-resolution",
285
+ personaType: "dashboard",
286
+ description: "Extract entities, search for matching records in KB, resolve/reconcile with master data",
287
+ intent: "Validate extracted entities against existing records before downstream processing",
288
+ shape: [
289
+ { stage: "trigger", purpose: "Document upload", actionOptions: ["document_trigger"] },
290
+ { stage: "extract", purpose: "Extract entities", actionOptions: ["entity_extraction_with_documents"] },
291
+ { stage: "convert", purpose: "Normalize for search", actionOptions: ["json_mapper"] },
292
+ { stage: "lookup", purpose: "Find matching records", actionOptions: ["search"] },
293
+ { stage: "resolve", purpose: "Reconcile extracted vs master data", actionOptions: ["call_llm", "custom_agent"] },
294
+ { stage: "results", purpose: "Resolution status + matched record", actionOptions: ["results (dot-notation)"] },
295
+ ],
296
+ exampleWiring: [
297
+ "entity_extraction → json_mapper → search.query",
298
+ "search.search_results → call_llm.named_inputs_Matching_Records",
299
+ "entity_extraction.extraction_columns → call_llm.named_inputs_Extracted_Data",
300
+ ],
301
+ useCase: "Invoice vendor matching, contract party resolution, employee verification against HR records",
302
+ antiPatterns: [
303
+ "Skipping the search/resolution step (extracted data goes unvalidated)",
304
+ "Not handling 'no match found' case in resolver LLM",
305
+ ],
306
+ },
307
+ {
308
+ name: "hitl-decision-form",
309
+ personaType: "dashboard",
310
+ description: "Present processed data as a human review form before downstream actions",
311
+ intent: "Get human verification or correction of extracted data before acting on it",
312
+ shape: [
313
+ { stage: "trigger", purpose: "Document upload", actionOptions: ["document_trigger"] },
314
+ { stage: "extract", purpose: "Initial data extraction", actionOptions: ["entity_extraction_with_documents"] },
315
+ { stage: "prepare", purpose: "Prepare data for review", actionOptions: ["call_llm", "custom_agent"] },
316
+ { stage: "review", purpose: "Human review / approval", actionOptions: ["general_hitl", "entity_extraction_with_documents"], notes: "general_hitl (Human Collaboration Agent): Conversational mode for dialogue-based approval, Standard Form for structured fields, Custom Form for external URL. entity_extraction_with_documents with HITL flag: columns define editable form fields." },
317
+ { stage: "act", purpose: "Act on reviewed data (branch on HITL Success/Failure)", actionOptions: ["send_email_agent", "external_action_caller", "call_llm"] },
318
+ { stage: "results", purpose: "Dashboard columns", actionOptions: ["results (dot-notation)"] },
319
+ ],
320
+ exampleWiring: [
321
+ "entity_extraction_processing → call_llm → general_hitl.named_inputs_Summary",
322
+ "general_hitl.human_collaboration_status → send_email (runIf: status == 'HITL Success')",
323
+ "general_hitl.summarized_conversation → call_llm.named_inputs_Review_Notes",
324
+ ],
325
+ useCase: "Invoice approval, contract review, compliance sign-off, content approval",
326
+ antiPatterns: [
327
+ "Not passing processed data to review node's named_inputs (reviewer sees no context)",
328
+ "Forgetting to handle HITL Failure path (rejected items should be logged or routed)",
329
+ ],
330
+ },
331
+ {
332
+ name: "document-generation-pipeline",
333
+ personaType: "dashboard",
334
+ description: "Generate formatted documents from processed data and optionally deliver via email",
335
+ intent: "Create professional documents (PDF/DOCX) from extracted data and deliver them",
336
+ shape: [
337
+ { stage: "trigger", purpose: "Document upload", actionOptions: ["document_trigger"] },
338
+ { stage: "extract", purpose: "Extract data", actionOptions: ["entity_extraction_with_documents"] },
339
+ { stage: "draft", purpose: "Generate content with LLM", actionOptions: ["call_llm", "custom_agent"] },
340
+ { stage: "format", purpose: "Convert to formatted document", actionOptions: ["generate_document"] },
341
+ { stage: "deliver", purpose: "Send via email (optional)", actionOptions: ["send_email_agent"], notes: "Use named_inputs for DOCUMENT type attachments" },
342
+ { stage: "results", purpose: "Dashboard columns", actionOptions: ["results (dot-notation)"] },
343
+ ],
344
+ exampleWiring: [
345
+ "entity_extraction → call_llm.named_inputs_Extracted_Data",
346
+ "call_llm.llm_output → generate_document.markdown_file_contents",
347
+ "generate_document.document_link → send_email_agent.named_inputs_Attachment",
348
+ ],
349
+ useCase: "Dunning letter generation, invoice creation, compliance reports, customer correspondence",
350
+ antiPatterns: [
351
+ "Skipping generate_document and sending raw LLM text (no formatting, no PDF)",
352
+ "Putting full template in call_llm instructions (use data source templates for regulatory formats)",
353
+ ],
354
+ },
355
+ // ─── Voice Patterns ─────────────────────────────────────────────────────
356
+ // Voice patterns share the same shapes as chat patterns but with voice_trigger
357
+ // and voice-specific widget requirements (conversationSettings, voiceSettings, etc.)
358
+ // Rather than duplicating patterns, note that any chat pattern can be adapted for
359
+ // voice by: (1) using voice_trigger, (2) adding voice widgets, (3) keeping response
360
+ // latency low (avoid long chains).
361
+ ];
@@ -268,13 +268,13 @@ export function generateAutobuilderPrompt(description, personaType) {
268
268
  prompt += "- Use chat_categorizer with Fallback category\n";
269
269
  }
270
270
  if (hasKBSearch) {
271
- prompt += "- Use search for KB, respond_with_sources for answers\n";
271
+ prompt += "- Use search for KB, call_llm with named_inputs_Search_Results for answers\n";
272
272
  }
273
273
  if (hasExternalAction && !hasHITL) {
274
274
  prompt += "- Use external_action_caller or send_email_agent as needed\n";
275
275
  }
276
276
  if (hasHITL) {
277
- prompt += "- Enable HITL flag on agents that need approval (do NOT add standalone general_hitl nodes)\n";
277
+ prompt += "- For simple approval: enable HITL flag on send_email_agent/entity_extraction_with_documents. For rich dialogue: use general_hitl (Human Collaboration Agent)\n";
278
278
  }
279
279
  prompt += `- ${config.outputNote}\n`;
280
280
  }