@ema.co/mcp-toolkit 2026.2.19 → 2026.2.23
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.
Potentially problematic release.
This version of @ema.co/mcp-toolkit might be problematic. Click here for more details.
- package/dist/cli/index.js +2 -2
- package/dist/mcp/domain/loop-detection.js +46 -54
- package/dist/mcp/domain/sanitizer.js +1 -1
- package/dist/mcp/domain/workflow-graph.js +2 -2
- package/dist/mcp/domain/workflow-path-enumerator.js +7 -4
- package/dist/mcp/guidance.js +53 -0
- package/dist/mcp/handlers/debug/adapter.js +15 -0
- package/dist/mcp/handlers/debug/formatters.js +282 -0
- package/dist/mcp/handlers/debug/index.js +133 -0
- package/dist/mcp/handlers/demo/adapter.js +180 -0
- package/dist/mcp/handlers/env/config.js +2 -2
- package/dist/mcp/handlers/index.js +0 -1
- package/dist/mcp/handlers/persona/adapter.js +135 -0
- package/dist/mcp/handlers/sync/adapter.js +200 -0
- package/dist/mcp/handlers/workflow/adapter.js +174 -0
- package/dist/mcp/handlers/workflow/fix.js +11 -12
- package/dist/mcp/handlers/workflow/index.js +0 -24
- package/dist/mcp/knowledge-guidance-topics.js +615 -0
- package/dist/mcp/knowledge.js +23 -612
- package/dist/mcp/resources-dynamic.js +2395 -0
- package/dist/mcp/resources-validation.js +408 -0
- package/dist/mcp/resources.js +72 -2724
- package/dist/mcp/server.js +33 -832
- package/dist/mcp/tools.js +104 -2
- package/dist/sdk/client-adapter.js +265 -24
- package/dist/sdk/ema-client.js +100 -9
- package/dist/sdk/generated/well-known-types.js +99 -0
- package/dist/sdk/grpc-client.js +115 -1
- package/dist/sync/sdk.js +2 -2
- package/dist/sync.js +4 -3
- package/package.json +3 -2
- package/dist/mcp/handlers/knowledge/index.js +0 -54
package/dist/mcp/knowledge.js
CHANGED
|
@@ -135,37 +135,35 @@ export const PROJECT_TYPES = {
|
|
|
135
135
|
document: 3,
|
|
136
136
|
};
|
|
137
137
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
138
|
-
// Type Compatibility (
|
|
138
|
+
// Type Compatibility (AUTO-GENERATED + CURATED OVERRIDES)
|
|
139
139
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
140
|
-
//
|
|
140
|
+
// Base rules (reflexive, ANY compatibility) are auto-generated from proto.
|
|
141
|
+
// Curated overrides document specific incompatibilities and conversion notes.
|
|
141
142
|
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
143
|
+
// Regenerate base: npm run generate:type-compatibility
|
|
144
|
+
// Source: protos/service/workflows/v1/values.proto → values_pb.ts → well-known-types.ts
|
|
144
145
|
//
|
|
145
146
|
// Other type compatibility definitions serve different purposes:
|
|
146
147
|
// - SCHEMA_TYPE_COMPATIBILITY (action-schema-parser.ts) - input name matching for validation
|
|
147
|
-
//
|
|
148
|
-
// See .context/core/guides/source-repos.md for full architecture
|
|
149
148
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
import { BASE_TYPE_COMPATIBILITY } from "../sdk/generated/well-known-types.js";
|
|
150
|
+
const CURATED_OVERRIDES = [
|
|
151
|
+
// Chat conversation → other types
|
|
153
152
|
{ sourceType: "WELL_KNOWN_TYPE_CHAT_CONVERSATION", targetType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", compatible: false, note: "Use conversation_to_search_query to convert" },
|
|
154
|
-
{ sourceType: "WELL_KNOWN_TYPE_CHAT_CONVERSATION", targetType: "
|
|
155
|
-
// Text with sources
|
|
156
|
-
{ sourceType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", targetType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", compatible: true },
|
|
157
|
-
{ sourceType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", targetType: "WELL_KNOWN_TYPE_ANY", compatible: true },
|
|
153
|
+
{ sourceType: "WELL_KNOWN_TYPE_CHAT_CONVERSATION", targetType: "WELL_KNOWN_TYPE_SEARCH_RESULT", compatible: false, note: "Search takes query string, not conversation" },
|
|
154
|
+
// Text with sources → other types
|
|
158
155
|
{ sourceType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", targetType: "WELL_KNOWN_TYPE_CHAT_CONVERSATION", compatible: false, note: "Cannot convert text to conversation" },
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
{ sourceType: "WELL_KNOWN_TYPE_SEARCH_RESULT", targetType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", compatible: false, note: "Use respond_for_external_actions
|
|
162
|
-
{ sourceType: "WELL_KNOWN_TYPE_SEARCH_RESULT", targetType: "
|
|
163
|
-
// Document
|
|
164
|
-
{ sourceType: "WELL_KNOWN_TYPE_DOCUMENT", targetType: "WELL_KNOWN_TYPE_DOCUMENT", compatible: true },
|
|
165
|
-
{ sourceType: "WELL_KNOWN_TYPE_DOCUMENT", targetType: "WELL_KNOWN_TYPE_ANY", compatible: true },
|
|
156
|
+
{ sourceType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", targetType: "WELL_KNOWN_TYPE_SEARCH_RESULT", compatible: false, note: "Text is not a search result" },
|
|
157
|
+
// Search result → other types
|
|
158
|
+
{ sourceType: "WELL_KNOWN_TYPE_SEARCH_RESULT", targetType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", compatible: false, note: "Use respond_for_external_actions or call_llm.named_inputs" },
|
|
159
|
+
{ sourceType: "WELL_KNOWN_TYPE_SEARCH_RESULT", targetType: "WELL_KNOWN_TYPE_CHAT_CONVERSATION", compatible: false, note: "Search result is not a conversation" },
|
|
160
|
+
// Document → other types
|
|
166
161
|
{ sourceType: "WELL_KNOWN_TYPE_DOCUMENT", targetType: "WELL_KNOWN_TYPE_TEXT_WITH_SOURCES", compatible: false, note: "Use entity_extraction or document-specific agents" },
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
{ sourceType: "WELL_KNOWN_TYPE_DOCUMENT", targetType: "WELL_KNOWN_TYPE_CHAT_CONVERSATION", compatible: false, note: "Document is not a conversation" },
|
|
163
|
+
];
|
|
164
|
+
export const TYPE_COMPATIBILITY = [
|
|
165
|
+
...BASE_TYPE_COMPATIBILITY,
|
|
166
|
+
...CURATED_OVERRIDES,
|
|
169
167
|
];
|
|
170
168
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
171
169
|
// Workflow Patterns
|
|
@@ -436,596 +434,9 @@ export const DEBUG_CHECKLIST = [
|
|
|
436
434
|
{ step: 10, action: "Validate Types", description: "Are all connections type-compatible?" },
|
|
437
435
|
];
|
|
438
436
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
439
|
-
// Guidance Topics
|
|
437
|
+
// Re-exports: Guidance Topics (from knowledge-guidance-topics.ts)
|
|
440
438
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
441
|
-
|
|
442
|
-
* Guidance topics for workflow building.
|
|
443
|
-
*
|
|
444
|
-
* status field indicates confidence level:
|
|
445
|
-
* - "verified": Based on platform mechanics, tested behavior
|
|
446
|
-
* - "experimental": Best practice suggestions, needs validation
|
|
447
|
-
*
|
|
448
|
-
* Experimental guidance uses softer language (consider, may, typically)
|
|
449
|
-
* to avoid biasing LLMs toward unverified patterns.
|
|
450
|
-
*/
|
|
451
|
-
export const GUIDANCE_TOPICS = {
|
|
452
|
-
"categorizer-routing": {
|
|
453
|
-
title: "Categorizer Routing Best Practices",
|
|
454
|
-
content: "Every categorizer must have runIf conditions for each category. The runIf compares output 'category' to enumValue. Always include Fallback.",
|
|
455
|
-
status: "verified",
|
|
456
|
-
examples: [
|
|
457
|
-
"runIf: {lhs: {actionOutput: {actionName: 'chat_categorizer', output: 'category'}, autoDetectedBinding: false}, operator: 1, rhs: {inline: {enumValue: 'Market_Impact'}, autoDetectedBinding: false}}",
|
|
458
|
-
"runIf: {lhs: {actionOutput: {actionName: 'chat_categorizer', output: 'category'}, autoDetectedBinding: false}, operator: 1, rhs: {inline: {enumValue: 'Fallback'}, autoDetectedBinding: false}}",
|
|
459
|
-
],
|
|
460
|
-
criticalRules: [
|
|
461
|
-
"MUST have runIf condition for each category",
|
|
462
|
-
"runIf format: output='category', operator=1 (numeric), enumValue='<CategoryName>'",
|
|
463
|
-
"DO NOT use 'category_<Name>' as output - use 'category' and compare to enum value",
|
|
464
|
-
"ALWAYS include Fallback category",
|
|
465
|
-
"Create handler node with runIf for EACH category",
|
|
466
|
-
],
|
|
467
|
-
},
|
|
468
|
-
"type-compatibility": {
|
|
469
|
-
title: "Type Compatibility Rules",
|
|
470
|
-
content: "Auto Builder validates type compatibility. Common mistake: connecting chat_conversation directly to search.query.",
|
|
471
|
-
status: "verified",
|
|
472
|
-
examples: [
|
|
473
|
-
"✅ trigger.user_query → search.query (TEXT_WITH_SOURCES → TEXT_WITH_SOURCES)",
|
|
474
|
-
"❌ trigger.chat_conversation → search.query (CHAT_CONVERSATION ≠ TEXT_WITH_SOURCES)",
|
|
475
|
-
"✅ search.search_results → respond_for_external_actions.external_action_result (SEARCH_RESULT → ANY)",
|
|
476
|
-
"❌ search.search_results → call_llm.query (SEARCH_RESULT ≠ TEXT_WITH_SOURCES)",
|
|
477
|
-
"✅ search.search_results → call_llm.named_inputs_* (SEARCH_RESULT → ANY)",
|
|
478
|
-
],
|
|
479
|
-
criticalRules: [
|
|
480
|
-
"CHAT_CONVERSATION only compatible with chat_categorizer.conversation",
|
|
481
|
-
"SEARCH_RESULT compatible with respond_for_external_actions.external_action_result or named_inputs (respond_with_sources/v0 is deprecated)",
|
|
482
|
-
"call_llm.named_inputs accepts ANY type",
|
|
483
|
-
"Use conversation_to_search_query to convert CHAT_CONVERSATION → TEXT_WITH_SOURCES",
|
|
484
|
-
],
|
|
485
|
-
},
|
|
486
|
-
"named-inputs": {
|
|
487
|
-
title: "Named Inputs Pattern",
|
|
488
|
-
content: "When connecting to call_llm.named_inputs, use suffix: named_inputs_<Descriptive_Name>. The name appears as a label in the workflow UI.",
|
|
489
|
-
status: "verified",
|
|
490
|
-
examples: [
|
|
491
|
-
"named_inputs_Market_Context",
|
|
492
|
-
"named_inputs_Client_Data",
|
|
493
|
-
"named_inputs_Tool_Result",
|
|
494
|
-
"named_inputs_Web_Search_Results",
|
|
495
|
-
"named_inputs_Validation_Output",
|
|
496
|
-
],
|
|
497
|
-
},
|
|
498
|
-
"hitl-patterns": {
|
|
499
|
-
title: "Human-in-the-Loop Patterns",
|
|
500
|
-
content: "HITL is implemented as a FLAG on specific agents — ONLY entity_extraction_with_documents and send_email_agent support it. " +
|
|
501
|
-
"external_action_caller does NOT support HITL. general_hitl is NOT deployable (appears in catalogs but cannot be deployed). " +
|
|
502
|
-
"When the user requests approval, enable the HITL flag on send_email_agent or entity_extraction_with_documents. " +
|
|
503
|
-
"The platform handles the approval UI and routing automatically.\n\n" +
|
|
504
|
-
"JSON format: Set `disable_human_interaction: false` (or omit — default is false = HITL enabled) on the action node " +
|
|
505
|
-
"in workflow_def.actions[]. To DISABLE HITL, set `disable_human_interaction: true`. " +
|
|
506
|
-
"Note the counter-intuitive naming: false = HITL ON, true = HITL OFF.",
|
|
507
|
-
status: "verified",
|
|
508
|
-
examples: [
|
|
509
|
-
"User wants email approval → enable HITL flag on send_email_agent",
|
|
510
|
-
"User wants extraction review → enable HITL flag on entity_extraction_with_documents",
|
|
511
|
-
"JSON: { name: 'send_email', action: { name: 'send_email_agent' }, disable_human_interaction: false, ... } → HITL ENABLED",
|
|
512
|
-
"JSON: { name: 'respond', action: { name: 'call_llm' }, disable_human_interaction: true, ... } → HITL DISABLED (auto-proceed)",
|
|
513
|
-
],
|
|
514
|
-
criticalRules: [
|
|
515
|
-
"general_hitl is NOT deployable — do NOT add it to any workflow",
|
|
516
|
-
"HITL is ONLY supported on: entity_extraction_with_documents and send_email_agent",
|
|
517
|
-
"external_action_caller does NOT support HITL despite appearing in older guidance",
|
|
518
|
-
"JSON field: `disable_human_interaction` (boolean) on the action node — false=HITL on, true=HITL off",
|
|
519
|
-
"When user says 'with approval': set disable_human_interaction: false on send_email_agent or entity_extraction_with_documents",
|
|
520
|
-
],
|
|
521
|
-
},
|
|
522
|
-
"hitl-policy": {
|
|
523
|
-
title: "HITL Policy (When to Use)",
|
|
524
|
-
content: "HITL is OPTIONAL. Default is auto-proceed without approval gates. Only add HITL when explicitly requested by user.",
|
|
525
|
-
status: "verified",
|
|
526
|
-
examples: [
|
|
527
|
-
"User says 'add email' → Ask about recipient/trigger, NOT about approval",
|
|
528
|
-
"User says 'with approval' or 'confirm before sending' → Add HITL",
|
|
529
|
-
"User says 'auto' or 'directly' → Skip HITL discussion entirely",
|
|
530
|
-
],
|
|
531
|
-
criticalRules: [
|
|
532
|
-
"DEFAULT: Proceed WITHOUT approval gate unless explicitly requested",
|
|
533
|
-
"Add HITL only when: user says 'confirm before', 'approval required', 'human review'",
|
|
534
|
-
"Skip HITL when: user says 'auto', 'directly', 'no approval', or specifies direct flow",
|
|
535
|
-
"If ambiguous: ASK 'Should [action] require approval, or auto-proceed?'",
|
|
536
|
-
"NEVER auto-add HITL based on action type alone",
|
|
537
|
-
"INFO severity issues = 'Consider this', not 'Must fix'",
|
|
538
|
-
],
|
|
539
|
-
},
|
|
540
|
-
"multi-source-search": {
|
|
541
|
-
title: "Multi-Source Search Architecture",
|
|
542
|
-
content: "For comprehensive answers, combine local search with web search. Use combine_search_results to merge and deduplicate.",
|
|
543
|
-
status: "verified",
|
|
544
|
-
examples: [
|
|
545
|
-
"search (local) + live_web_search → combine_search_results → respond_for_external_actions",
|
|
546
|
-
"For cross-document linking: entity_extraction → knowledge_graph_generator → document_synthesis",
|
|
547
|
-
],
|
|
548
|
-
criticalRules: [
|
|
549
|
-
"Use entity extraction when cross-document linking is needed",
|
|
550
|
-
"Use LLM resolution for single conversational queries",
|
|
551
|
-
"Entity extraction requires schema definition",
|
|
552
|
-
],
|
|
553
|
-
},
|
|
554
|
-
"voice-tool-calling": {
|
|
555
|
-
title: "Voice AI Tool Calling Rules",
|
|
556
|
-
content: "Voice AI has specific rules for tool calling to ensure natural conversation flow.",
|
|
557
|
-
status: "verified",
|
|
558
|
-
criticalRules: [
|
|
559
|
-
"Collect ALL required parameters before calling a tool",
|
|
560
|
-
"Wait for response before calling another tool",
|
|
561
|
-
"NEVER mention tool names to the user",
|
|
562
|
-
"NEVER guess parameter values - ask the user",
|
|
563
|
-
"Use plain language: 'Let me look that up' not 'Calling search API'",
|
|
564
|
-
"Handle delays with waitMessage: 'One moment while I check...'",
|
|
565
|
-
"Handle errors gracefully, offer alternatives",
|
|
566
|
-
],
|
|
567
|
-
},
|
|
568
|
-
"guardrails": {
|
|
569
|
-
title: "Implementing Guardrails",
|
|
570
|
-
content: "Use response_validator to check LLM output before sending to user. Use abstain_action when the AI should decline to answer.",
|
|
571
|
-
status: "verified",
|
|
572
|
-
examples: [
|
|
573
|
-
"call_llm → response_validator → (valid) → WORKFLOW_OUTPUT",
|
|
574
|
-
"call_llm → response_validator → (invalid) → abstain_action → WORKFLOW_OUTPUT",
|
|
575
|
-
],
|
|
576
|
-
criticalRules: [
|
|
577
|
-
"Enable use_citation_based_filtering for trust",
|
|
578
|
-
"Implement confidence thresholds",
|
|
579
|
-
"Define clear abstain conditions",
|
|
580
|
-
],
|
|
581
|
-
},
|
|
582
|
-
"output-mapping": {
|
|
583
|
-
title: "Output Mapping to WORKFLOW_OUTPUT",
|
|
584
|
-
content: "ALL paths must eventually lead to WORKFLOW_OUTPUT. Every response node should have an edge to WORKFLOW_OUTPUT.",
|
|
585
|
-
status: "verified",
|
|
586
|
-
examples: [
|
|
587
|
-
"respond_market.response_with_sources → WORKFLOW_OUTPUT",
|
|
588
|
-
"fallback_response.response_with_sources → WORKFLOW_OUTPUT",
|
|
589
|
-
"approved_response.response → WORKFLOW_OUTPUT",
|
|
590
|
-
"rejected_response.response → WORKFLOW_OUTPUT",
|
|
591
|
-
],
|
|
592
|
-
},
|
|
593
|
-
"workflow-execution": {
|
|
594
|
-
title: "Workflow Execution Model",
|
|
595
|
-
content: "CRITICAL: Each user_query triggers a NEW workflow execution. The workflow runs ONCE per user message, not once per conversation.",
|
|
596
|
-
status: "verified",
|
|
597
|
-
examples: [
|
|
598
|
-
"❌ User: 'Create ticket' → creates ticket; User: 'Add my phone' → creates ANOTHER ticket",
|
|
599
|
-
"✅ Check chat_conversation for existing ticket, route to 'update' instead of 'create'",
|
|
600
|
-
],
|
|
601
|
-
criticalRules: [
|
|
602
|
-
"Each user message triggers new workflow execution",
|
|
603
|
-
"chat_conversation accumulates; user_query is current message only",
|
|
604
|
-
"Check context before creating records to avoid duplicates",
|
|
605
|
-
"Use runIf conditions to skip redundant operations",
|
|
606
|
-
],
|
|
607
|
-
},
|
|
608
|
-
"conversation-vs-query": {
|
|
609
|
-
title: "Conversation vs Query Usage",
|
|
610
|
-
content: "Use user_query for current message, chat_conversation for full history, conversation_summarizer for controlled context.",
|
|
611
|
-
status: "verified",
|
|
612
|
-
examples: [
|
|
613
|
-
"user_query: Simple queries where history doesn't matter",
|
|
614
|
-
"chat_conversation: When you need full context",
|
|
615
|
-
"conversation_summarizer: Long conversations, context window management",
|
|
616
|
-
],
|
|
617
|
-
criticalRules: [
|
|
618
|
-
"user_query = current message only (TEXT_WITH_SOURCES)",
|
|
619
|
-
"chat_conversation = all history (CHAT_CONVERSATION)",
|
|
620
|
-
"conversation_summarizer may still be needed for downstream agent format requirements",
|
|
621
|
-
],
|
|
622
|
-
},
|
|
623
|
-
"workflow-structure": {
|
|
624
|
-
title: "Workflow Structure Best Practices",
|
|
625
|
-
content: "Suggested pattern: Conversation Summarizer → Entity Extractor → JSON Mapper → Downstream Consumers. Extract once, pass outputs onward. This may help ensure client context is available before knowledge base searches.",
|
|
626
|
-
status: "experimental",
|
|
627
|
-
examples: [
|
|
628
|
-
"Consider: trigger → summarizer → extractor → json_mapper → [search, routing, responses]",
|
|
629
|
-
"Consider: Single extraction node feeds JSON mapper, which distributes to multiple consumers",
|
|
630
|
-
"May cause issues: Multiple extraction nodes extracting same data from same input",
|
|
631
|
-
"May cause issues: KB search before extraction (can't use client context)",
|
|
632
|
-
],
|
|
633
|
-
criticalRules: [
|
|
634
|
-
"Consider extracting entities once, then passing structured data via JSON mapper",
|
|
635
|
-
"Extraction before KB search may help if search needs client context",
|
|
636
|
-
"JSON mapper output (output_json) can feed downstream nodes that need structured context",
|
|
637
|
-
"Duplicate extraction nodes may cause redundancy - consider single extractor pattern",
|
|
638
|
-
],
|
|
639
|
-
},
|
|
640
|
-
"search-node-timing": {
|
|
641
|
-
title: "Knowledge Base Search Node Timing and Usage",
|
|
642
|
-
content: "Search nodes execute based on their input connections. Early search (after summarizer) typically sees only conversation summary. Late search (after JSON mapper) may be able to use client context. Consider multiple search nodes if you need both generic and context-aware retrieval.",
|
|
643
|
-
status: "experimental",
|
|
644
|
-
examples: [
|
|
645
|
-
"Early search: trigger → summarizer → knowledge_search_1 (typically sees only summary)",
|
|
646
|
-
"Late search: trigger → summarizer → extractor → json_mapper → knowledge_search_2 (may access client context)",
|
|
647
|
-
"Template search: json_mapper → fixed_response (query builder) → template_search (may use client context)",
|
|
648
|
-
"Dual search pattern: Early generic search + late context-aware search (consider for comprehensive results)",
|
|
649
|
-
],
|
|
650
|
-
criticalRules: [
|
|
651
|
-
"Search nodes typically only see data from their input connections",
|
|
652
|
-
"Early search (before extraction) may provide generic retrieval",
|
|
653
|
-
"Late search (after JSON mapper) may be able to incorporate client context",
|
|
654
|
-
"Consider placing template search after JSON mapper if client context is needed",
|
|
655
|
-
"Multiple search nodes may be useful for different purposes (generic vs context-aware)",
|
|
656
|
-
],
|
|
657
|
-
},
|
|
658
|
-
"node-execution-conditions": {
|
|
659
|
-
title: "When Nodes Execute (Execution Conditions)",
|
|
660
|
-
content: "Nodes execute based on: 1) Input connections (data must flow in), 2) runIf conditions (must evaluate to true), 3) trigger_when conditions (for HITL/conditional nodes). Nodes without valid input paths or with false runIf conditions will NOT execute.",
|
|
661
|
-
status: "verified",
|
|
662
|
-
examples: [
|
|
663
|
-
"Orphan node: No input connection from trigger → node never executes",
|
|
664
|
-
"runIf false: Node has input but runIf condition evaluates false → node skipped",
|
|
665
|
-
"Missing category edge: Categorizer output not connected → downstream nodes never receive category",
|
|
666
|
-
"Dead end: Node executes but output not connected → data lost, no response to user",
|
|
667
|
-
],
|
|
668
|
-
criticalRules: [
|
|
669
|
-
"Every node MUST have a path from trigger (or be reachable via runIf conditions)",
|
|
670
|
-
"runIf conditions use: lhs (actionOutput reference), operator (1=equals), rhs (enumValue or value)",
|
|
671
|
-
"If runIf evaluates false, node is skipped (doesn't execute)",
|
|
672
|
-
"Nodes without input connections are 'orphans' and never execute",
|
|
673
|
-
"Categorizer nodes need downstream nodes with runIf conditions for each category",
|
|
674
|
-
"Check execution flow: trigger → [conditional paths] → response → WORKFLOW_OUTPUT",
|
|
675
|
-
],
|
|
676
|
-
},
|
|
677
|
-
"array-preservation": {
|
|
678
|
-
title: "Array Preservation in Workflows",
|
|
679
|
-
content: "Arrays from entity extraction may be flattened or truncated in some cases. Consider explicit configuration if array structure matters. Needs validation.",
|
|
680
|
-
status: "experimental",
|
|
681
|
-
examples: [
|
|
682
|
-
"Consider: Entity extraction schema: { 'participants': { 'type': 'array', 'items': 'string' } }",
|
|
683
|
-
"Consider: JSON mapper - preserve arrays rather than flattening",
|
|
684
|
-
"May occur: Without array config, arrays may return only first element",
|
|
685
|
-
"For multiple recipients: Consider array in to_email or iterator pattern",
|
|
686
|
-
],
|
|
687
|
-
criticalRules: [
|
|
688
|
-
"Consider defining array types explicitly in extraction schema",
|
|
689
|
-
"JSON mapper may need explicit array mapping configuration",
|
|
690
|
-
"Test array handling with actual workflow to verify behavior",
|
|
691
|
-
"This guidance needs validation - behavior may vary by node type",
|
|
692
|
-
],
|
|
693
|
-
},
|
|
694
|
-
"search-filtering": {
|
|
695
|
-
title: "Search Filtering Considerations",
|
|
696
|
-
content: "Consider combining semantic query + structured filters. Security filters (tenant_id/client_id) are typically important for isolation. Needs validation with actual platform behavior.",
|
|
697
|
-
status: "experimental",
|
|
698
|
-
examples: [
|
|
699
|
-
"Consider: semantic query + tenant_id filter + path_prefix filter",
|
|
700
|
-
"Consider: Fallback to broader search if filtered results are empty",
|
|
701
|
-
"May cause issues: Only filename/path filter without semantic query",
|
|
702
|
-
"May cause issues: Over-filtering from uncertain extraction",
|
|
703
|
-
],
|
|
704
|
-
criticalRules: [
|
|
705
|
-
"Consider combining semantic query + structured filters",
|
|
706
|
-
"Security filters (tenant_id/client_id) are typically needed for isolation",
|
|
707
|
-
"Path/filename filters may help narrow scope but verify behavior",
|
|
708
|
-
"If filtered search returns few results, consider broader fallback",
|
|
709
|
-
"Test actual filter behavior - this guidance needs validation",
|
|
710
|
-
],
|
|
711
|
-
},
|
|
712
|
-
"fallback-response-inputs": {
|
|
713
|
-
title: "Fallback Response Input Considerations",
|
|
714
|
-
content: "Fallback responses may benefit from full conversation context rather than just summarized query. Consider what context the fallback handler needs. Needs validation.",
|
|
715
|
-
status: "experimental",
|
|
716
|
-
examples: [
|
|
717
|
-
"Consider: trigger.chat_conversation → fallback.query (full context)",
|
|
718
|
-
"Consider: Include routing context in fallback inputs",
|
|
719
|
-
"May cause issues: Only summarized_conversation may lose user wording",
|
|
720
|
-
],
|
|
721
|
-
criticalRules: [
|
|
722
|
-
"Consider what context fallback handler needs",
|
|
723
|
-
"Full conversation may provide better context than summary alone",
|
|
724
|
-
"Include routing context if helpful for fallback logic",
|
|
725
|
-
"Test actual fallback behavior - this guidance needs validation",
|
|
726
|
-
],
|
|
727
|
-
},
|
|
728
|
-
"separate-vs-merged-inputs": {
|
|
729
|
-
title: "Separate vs Merged Inputs Consideration",
|
|
730
|
-
content: "Consider keeping separate inputs for different data sources. named_inputs_* may help provide semantic clarity. Needs validation.",
|
|
731
|
-
status: "experimental",
|
|
732
|
-
examples: [
|
|
733
|
-
"Consider: named_inputs_Conversation + named_inputs_Templates for clarity",
|
|
734
|
-
"Consider: Separate inputs may help agent reason over each source",
|
|
735
|
-
"May cause issues: Merging all into single input may lose structure",
|
|
736
|
-
],
|
|
737
|
-
criticalRules: [
|
|
738
|
-
"Consider separate named_inputs_* for different data sources",
|
|
739
|
-
"Separate inputs may provide semantic clarity",
|
|
740
|
-
"Test actual behavior - this guidance needs validation",
|
|
741
|
-
],
|
|
742
|
-
},
|
|
743
|
-
"folder-path-filtering": {
|
|
744
|
-
title: "Folder Path Filtering Consideration",
|
|
745
|
-
content: "Path filtering may help narrow search scope. Consider combining with semantic query. Verify actual platform support for path filters.",
|
|
746
|
-
status: "experimental",
|
|
747
|
-
examples: [
|
|
748
|
-
"Consider: path_prefix filter + semantic query",
|
|
749
|
-
"May help: Normalized paths for consistent filtering",
|
|
750
|
-
"May cause issues: Exact path matching (can be brittle)",
|
|
751
|
-
],
|
|
752
|
-
criticalRules: [
|
|
753
|
-
"Consider combining path filters with semantic query",
|
|
754
|
-
"Verify path filter support in actual platform",
|
|
755
|
-
"Test actual behavior - this guidance needs validation",
|
|
756
|
-
],
|
|
757
|
-
},
|
|
758
|
-
"automated-extraction-json": {
|
|
759
|
-
title: "Extraction and JSON Mapping Pattern",
|
|
760
|
-
content: "Consider extraction → JSON mapper pattern for structured data flow. This may help reduce redundant extraction. Needs validation.",
|
|
761
|
-
status: "experimental",
|
|
762
|
-
examples: [
|
|
763
|
-
"Consider: trigger → summarizer → extractor → json_mapper → consumers",
|
|
764
|
-
"Consider: JSON mapper to normalize extracted values",
|
|
765
|
-
"May help: Single extraction point rather than multiple",
|
|
766
|
-
],
|
|
767
|
-
criticalRules: [
|
|
768
|
-
"Consider single extraction point with JSON mapper distribution",
|
|
769
|
-
"Extraction schema configuration may help structure output",
|
|
770
|
-
"Test actual behavior - this guidance needs validation",
|
|
771
|
-
],
|
|
772
|
-
},
|
|
773
|
-
"when-filters-necessary": {
|
|
774
|
-
title: "Filter Necessity Considerations",
|
|
775
|
-
content: "Security/tenant isolation filters are typically important. Verify what filtering your storage layer provides. Needs validation with actual platform.",
|
|
776
|
-
status: "experimental",
|
|
777
|
-
examples: [
|
|
778
|
-
"Consider: tenant_id/client_id filters for security isolation",
|
|
779
|
-
"Consider: Whether storage layer already enforces isolation",
|
|
780
|
-
"May cause issues: Relying only on document annotations for isolation",
|
|
781
|
-
],
|
|
782
|
-
criticalRules: [
|
|
783
|
-
"Consider security filters for tenant/client isolation",
|
|
784
|
-
"Verify what isolation your storage layer provides",
|
|
785
|
-
"Test actual behavior - this guidance needs validation",
|
|
786
|
-
],
|
|
787
|
-
},
|
|
788
|
-
"filter-query-guidance": {
|
|
789
|
-
title: "Filter vs Query Considerations",
|
|
790
|
-
content: "Consider filters when user references specific documents or folders. Consider semantic queries when user describes topics. Both may be useful together. Needs validation.",
|
|
791
|
-
status: "experimental",
|
|
792
|
-
examples: [
|
|
793
|
-
"Consider filters: User names specific doc or references folder",
|
|
794
|
-
"Consider query: User describes topic or intent",
|
|
795
|
-
"Consider both: Broad filters + semantic query",
|
|
796
|
-
],
|
|
797
|
-
criticalRules: [
|
|
798
|
-
"Consider filters when user references specific documents",
|
|
799
|
-
"Consider semantic queries when user describes topics",
|
|
800
|
-
"Combining both may improve results - test actual behavior",
|
|
801
|
-
"This guidance needs validation with actual platform",
|
|
802
|
-
],
|
|
803
|
-
},
|
|
804
|
-
"node-selection": {
|
|
805
|
-
title: "Node Selection Guide: When to Use What",
|
|
806
|
-
status: "verified",
|
|
807
|
-
content: `Choose the RIGHT node type for each task. Using wrong nodes wastes compute, increases latency, and reduces quality.
|
|
808
|
-
|
|
809
|
-
## Decision Tree
|
|
810
|
-
|
|
811
|
-
### Q1: Do you need EXTERNAL KNOWLEDGE?
|
|
812
|
-
- YES → Use search node first, then generation
|
|
813
|
-
- NO → Skip search, use generation or transform
|
|
814
|
-
|
|
815
|
-
### Q2: Do you need AI REASONING/GENERATION?
|
|
816
|
-
- YES → Use LLM-based node
|
|
817
|
-
- NO → Use transform node (json_mapper, fixed_response)
|
|
818
|
-
|
|
819
|
-
### Q3: What KIND of generation?
|
|
820
|
-
- Simple Q&A with citations → respond_for_external_actions
|
|
821
|
-
- Custom generation → call_llm
|
|
822
|
-
- Complex multi-step reasoning → custom_agent
|
|
823
|
-
- Search + synthesize in one → document_synthesis
|
|
824
|
-
|
|
825
|
-
### Q4: Do you need STRUCTURED DATA extraction?
|
|
826
|
-
- YES → entity_extraction (NOT call_llm!)
|
|
827
|
-
- NO → Continue with generation
|
|
828
|
-
|
|
829
|
-
## Node Cost/Latency
|
|
830
|
-
|
|
831
|
-
| Node | LLM Calls | Latency | Use When |
|
|
832
|
-
|------|-----------|---------|----------|
|
|
833
|
-
| fixed_response | 0 | <50ms | Static content, config, templates |
|
|
834
|
-
| json_mapper | 0 | <100ms | Transform JSON without reasoning |
|
|
835
|
-
| entity_extraction | 1 | 1-2s | Extract structured data |
|
|
836
|
-
| respond_for_external_actions | 1 | 2-4s | Search + response (conversation-aware) |
|
|
837
|
-
| call_llm | 1 | 2-4s | Custom generation |
|
|
838
|
-
| custom_agent | 1-3 | 3-8s | Complex reasoning |
|
|
839
|
-
| document_synthesis | 2-5 | 5-15s | Multi-search + synthesis |`,
|
|
840
|
-
examples: [
|
|
841
|
-
"✅ Static template → fixed_response (0 LLM calls)",
|
|
842
|
-
"✅ Extract client_name, email → entity_extraction (1 LLM call, typed output)",
|
|
843
|
-
"✅ Simple KB Q&A → respond_for_external_actions (1 LLM call, conversation-aware)",
|
|
844
|
-
"✅ Custom email generation → call_llm with search results in named_inputs",
|
|
845
|
-
"❌ Using call_llm to extract JSON → Use entity_extraction instead",
|
|
846
|
-
"❌ Using document_synthesis for simple Q&A → Use respond_for_external_actions",
|
|
847
|
-
"❌ Using custom_agent for static response → Use fixed_response",
|
|
848
|
-
],
|
|
849
|
-
criticalRules: [
|
|
850
|
-
"NEVER use LLM for transforms that json_mapper can do",
|
|
851
|
-
"NEVER use call_llm for structured extraction - use entity_extraction",
|
|
852
|
-
"NEVER use document_synthesis when respond_for_external_actions suffices",
|
|
853
|
-
"ALWAYS use fixed_response for static content (templates, config, help text)",
|
|
854
|
-
"Use entity_extraction for typed, reliable structured data",
|
|
855
|
-
"Use call_llm when you need custom instructions or reasoning",
|
|
856
|
-
"Use custom_agent when task requires role context + multi-step reasoning",
|
|
857
|
-
"Use document_synthesis only when you need search-plan-search-synthesize pattern",
|
|
858
|
-
],
|
|
859
|
-
},
|
|
860
|
-
"llm-node-selection": {
|
|
861
|
-
title: "LLM Node Selection: Which One to Use",
|
|
862
|
-
status: "verified",
|
|
863
|
-
content: `Different LLM nodes serve different purposes. Choose based on your needs:
|
|
864
|
-
|
|
865
|
-
## respond_for_external_actions (replaces deprecated respond_with_sources)
|
|
866
|
-
- **Best for**: Simple Q&A with KB citations, tool result explanation
|
|
867
|
-
- **Input**: Search results or tool results via external_action_result
|
|
868
|
-
- **Output**: Response with source citations
|
|
869
|
-
- **When**: User asks question, you search KB, return answer
|
|
870
|
-
- **NOT for**: Custom generation, complex reasoning
|
|
871
|
-
|
|
872
|
-
## call_llm
|
|
873
|
-
- **Best for**: Custom text generation with instructions
|
|
874
|
-
- **Input**: Query + optional search/context via named_inputs
|
|
875
|
-
- **Output**: Generated text
|
|
876
|
-
- **When**: Need custom prompts, specific format, controlled generation
|
|
877
|
-
- **NOT for**: Simple Q&A (use respond_for_external_actions)
|
|
878
|
-
|
|
879
|
-
## custom_agent
|
|
880
|
-
- **Best for**: Complex tasks requiring role + instructions
|
|
881
|
-
- **Input**: Role instructions + task instructions + named_inputs
|
|
882
|
-
- **Output**: Structured or free-form response (use output_fields for structured)
|
|
883
|
-
- **When**: Multi-step reasoning, persona-based responses, tool-like behavior
|
|
884
|
-
- **NOT for**: Simple generation (overkill)
|
|
885
|
-
- **CRITICAL**: When outputting JSON for json_mapper, use output_fields to define keys
|
|
886
|
-
|
|
887
|
-
## document_synthesis
|
|
888
|
-
- **Best for**: Multi-source research with planning
|
|
889
|
-
- **Input**: User request + search results
|
|
890
|
-
- **Output**: Synthesized document
|
|
891
|
-
- **When**: Need to search → plan → search again → synthesize
|
|
892
|
-
- **NOT for**: Simple KB lookup (too heavy)`,
|
|
893
|
-
examples: [
|
|
894
|
-
"User asks 'What is our refund policy?' → respond_for_external_actions",
|
|
895
|
-
"Need to generate email with specific template → call_llm",
|
|
896
|
-
"Need to analyze portfolio and recommend actions → custom_agent",
|
|
897
|
-
"Need to research topic across multiple sources → document_synthesis",
|
|
898
|
-
],
|
|
899
|
-
criticalRules: [
|
|
900
|
-
"respond_for_external_actions: 1 LLM call, simple Q&A or tool results, conversation-aware",
|
|
901
|
-
"call_llm: 1 LLM call, custom generation, accepts named_inputs",
|
|
902
|
-
"custom_agent: 1-3 LLM calls, complex reasoning, has role context",
|
|
903
|
-
"document_synthesis: 2-5 LLM calls, heavy, only for research tasks",
|
|
904
|
-
"Default to respond_for_external_actions for KB Q&A",
|
|
905
|
-
"Upgrade to call_llm when you need custom instructions",
|
|
906
|
-
"Upgrade to custom_agent for complex reasoning",
|
|
907
|
-
"Use document_synthesis sparingly (latency expensive)",
|
|
908
|
-
],
|
|
909
|
-
},
|
|
910
|
-
"search-vs-no-search": {
|
|
911
|
-
title: "When to Search vs Direct Generation",
|
|
912
|
-
status: "verified",
|
|
913
|
-
content: `Not every request needs KB search. Search adds latency and can introduce irrelevant context.
|
|
914
|
-
|
|
915
|
-
## SEARCH when:
|
|
916
|
-
- User asks factual question about domain knowledge
|
|
917
|
-
- Request references specific documents/data
|
|
918
|
-
- Response accuracy depends on up-to-date information
|
|
919
|
-
- User asks about policies, procedures, product details
|
|
920
|
-
|
|
921
|
-
## DON'T SEARCH when:
|
|
922
|
-
- User needs help with task (drafting, formatting)
|
|
923
|
-
- Response is conversational/procedural
|
|
924
|
-
- Information is already in context (extracted entities)
|
|
925
|
-
- User asks general questions (time, greeting)`,
|
|
926
|
-
examples: [
|
|
927
|
-
"✅ Search: 'What is our return policy?' - needs KB",
|
|
928
|
-
"✅ Search: 'Tell me about client Thompson' - needs client data",
|
|
929
|
-
"❌ Don't search: 'Can you draft an email?' - procedural",
|
|
930
|
-
"❌ Don't search: 'Format this as a table' - transform",
|
|
931
|
-
"❌ Don't search: 'Hello, how are you?' - greeting",
|
|
932
|
-
],
|
|
933
|
-
criticalRules: [
|
|
934
|
-
"Search is NOT free - adds 0.5-2s latency per search",
|
|
935
|
-
"Search can introduce noise if results aren't relevant",
|
|
936
|
-
"Fallback/greeting responses often don't need search",
|
|
937
|
-
"Task-based requests (draft, format, summarize) usually don't need search",
|
|
938
|
-
"Use categorizer to route search vs no-search paths",
|
|
939
|
-
"If entity extraction already has the data, don't search for it again",
|
|
940
|
-
],
|
|
941
|
-
},
|
|
942
|
-
"custom-agent-json-output": {
|
|
943
|
-
title: "custom_agent + json_mapper Pattern",
|
|
944
|
-
status: "verified",
|
|
945
|
-
content: `When using custom_agent to generate JSON that will be parsed by json_mapper, you MUST properly configure output_fields or use strict prompting.
|
|
946
|
-
|
|
947
|
-
## The Problem
|
|
948
|
-
|
|
949
|
-
Without explicit output_fields, custom_agent returns JSON as a string blob in response_with_sources.
|
|
950
|
-
The json_mapper may fail to extract fields correctly, or output gets misformatted.
|
|
951
|
-
|
|
952
|
-
## Solution A: Define output_fields (RECOMMENDED)
|
|
953
|
-
|
|
954
|
-
Use output_fields with extraction columns matching your JSON keys:
|
|
955
|
-
|
|
956
|
-
\`\`\`json
|
|
957
|
-
{
|
|
958
|
-
"name": "email_gen",
|
|
959
|
-
"action": { "name": { "namespaces": ["actions", "emainternal"], "name": "custom_agent" }, "version": "v1" },
|
|
960
|
-
"inputs": {
|
|
961
|
-
"task_instructions": { "inline": { "wellKnown": { "textWithSources": {
|
|
962
|
-
"text": "Generate notification email with To, Subject, and Body fields.",
|
|
963
|
-
"sources": []
|
|
964
|
-
}}}},
|
|
965
|
-
"output_fields": {
|
|
966
|
-
"inline": { "array": { "values": [
|
|
967
|
-
{ "wellKnown": { "extractionColumn": { "id": "To", "name": "To", "dataType": 1 }}},
|
|
968
|
-
{ "wellKnown": { "extractionColumn": { "id": "Subject", "name": "Subject", "dataType": 1 }}},
|
|
969
|
-
{ "wellKnown": { "extractionColumn": { "id": "Body", "name": "Body", "dataType": 1 }}}
|
|
970
|
-
]}}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
\`\`\`
|
|
975
|
-
|
|
976
|
-
## Solution B: Strict Prompting + json_mapper
|
|
977
|
-
|
|
978
|
-
If not using output_fields, instruct LLM to output RAW JSON only:
|
|
979
|
-
|
|
980
|
-
\`\`\`
|
|
981
|
-
task_instructions: "Output ONLY the JSON object, no markdown. Format: {\\"To\\": \\"...\\", \\"Subject\\": \\"...\\", \\"Body\\": \\"...\\"}"
|
|
982
|
-
\`\`\`
|
|
983
|
-
|
|
984
|
-
Then wire to json_mapper with matching pathIndices:
|
|
985
|
-
\`\`\`json
|
|
986
|
-
{
|
|
987
|
-
"json_mapper_config": {
|
|
988
|
-
"inline": { "wellKnown": { "jsonMapperConfig": {
|
|
989
|
-
"rules": [
|
|
990
|
-
{ "fieldName": "to", "pathIndices": [{"stringIndex": "To"}], "type": 3 },
|
|
991
|
-
{ "fieldName": "subject", "pathIndices": [{"stringIndex": "Subject"}], "type": 3 },
|
|
992
|
-
{ "fieldName": "body", "pathIndices": [{"stringIndex": "Body"}], "type": 3 }
|
|
993
|
-
]
|
|
994
|
-
}}}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
\`\`\`
|
|
998
|
-
|
|
999
|
-
## Wiring json_mapper to fixed_response
|
|
1000
|
-
|
|
1001
|
-
json_mapper outputs go to output_json. Use fixed_response template variables:
|
|
1002
|
-
|
|
1003
|
-
\`\`\`json
|
|
1004
|
-
{
|
|
1005
|
-
"name": "fmt_to",
|
|
1006
|
-
"action": { "name": { "namespaces": ["actions", "emainternal"], "name": "fixed_response" }, "version": "v1" },
|
|
1007
|
-
"inputs": {
|
|
1008
|
-
"template": { "inline": { "wellKnown": { "textWithSources": { "text": "{{to}}", "sources": [] }}}},
|
|
1009
|
-
"custom_data": { "actionOutput": { "actionName": "json_map", "output": "output_json" }}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
\`\`\``,
|
|
1013
|
-
examples: [
|
|
1014
|
-
"Generate email → custom_agent(output_fields=[To,Subject,Body]) → send_email_agent",
|
|
1015
|
-
"Generate email → custom_agent(strict prompt) → json_mapper → fixed_response({{to}}) → send_email_agent",
|
|
1016
|
-
"❌ custom_agent without output_fields → json_mapper fails to parse",
|
|
1017
|
-
"❌ json_mapper output wired directly to send_email_agent → type mismatch (need fixed_response)",
|
|
1018
|
-
],
|
|
1019
|
-
criticalRules: [
|
|
1020
|
-
"custom_agent + json_mapper: ALWAYS use output_fields OR strict 'ONLY JSON' prompt",
|
|
1021
|
-
"json_mapper outputs to output_json - use fixed_response with {{fieldName}} template",
|
|
1022
|
-
"send_email_agent inputs require TEXT_WITH_SOURCES - use fixed_response to format",
|
|
1023
|
-
"If json_mapper fails to extract: check if LLM is wrapping JSON in markdown (```json)",
|
|
1024
|
-
"pathIndices must EXACTLY match JSON keys (case-sensitive)",
|
|
1025
|
-
"Default values in json_mapper rules prevent null errors",
|
|
1026
|
-
],
|
|
1027
|
-
},
|
|
1028
|
-
};
|
|
439
|
+
export { GUIDANCE_TOPICS } from "./knowledge-guidance-topics.js";
|
|
1029
440
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1030
441
|
// Helper Functions
|
|
1031
442
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -1305,7 +716,7 @@ export { DEPRECATED_ACTIONS_WITH_REPLACEMENT, DEPRECATED_ACTIONS_NO_REPLACEMENT,
|
|
|
1305
716
|
*
|
|
1306
717
|
* SOURCE: ema-repos/ema/docs/workflow-architecture-handoff.md (lines 85-200, constraint checklist 153-167)
|
|
1307
718
|
* STATUS: Can be auto-generated from source doc
|
|
1308
|
-
*
|
|
719
|
+
* SYNC: Candidate for catalog-sync.yml automation (see .context/core/guides/source-repos.md)
|
|
1309
720
|
*
|
|
1310
721
|
* See .context/core/guides/source-repos.md for sync details
|
|
1311
722
|
*/
|