@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.

Potentially problematic release.


This version of @ema.co/mcp-toolkit might be problematic. Click here for more details.

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
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Version Management Handlers
3
3
  *
4
- * Provides local version tracking for personas (snapshot, history, restore).
5
- * Versions are stored in the local workspace, not on the Ema platform.
4
+ * Provides version tracking for personas (snapshot, history, restore).
5
+ * Uses IVersionStorage can be local (.ema-versions/) or central (DIS).
6
6
  */
7
- import { createVersionStorage } from "../../../sync/version-storage.js";
8
- import { createVersionPolicyEngine } from "../../../sync/version-policy.js";
7
+ import { createCentralStorageEngine, migrateLocalToCentral } from "../../../sync/central-factory.js";
8
+ import { VersionStorage } from "../../../sync/version-storage.js";
9
9
  /**
10
10
  * Check if a mode is a version management mode.
11
11
  */
@@ -32,20 +32,27 @@ export function isVersionMode(mode) {
32
32
  * @param versionContext - Workspace and environment context
33
33
  */
34
34
  export async function handleVersion(mode, args, client, persona, versionContext) {
35
- const storage = createVersionStorage(versionContext.workspaceRoot);
36
- const engine = createVersionPolicyEngine(storage);
35
+ const { storage, engine } = createCentralStorageEngine(client, persona.id);
37
36
  switch (mode) {
38
37
  // ─────────────────────────────────────────────────────────────────────────
39
38
  // snapshot / version_create - Create a new version snapshot
40
39
  // ─────────────────────────────────────────────────────────────────────────
41
40
  case "snapshot":
42
41
  case "version_create": {
42
+ // Lazy migration: seed central from local on first snapshot
43
+ try {
44
+ const localStorage = new VersionStorage(versionContext.workspaceRoot);
45
+ await migrateLocalToCentral(localStorage, storage, persona.id);
46
+ }
47
+ catch {
48
+ // Best-effort: migration failure should not block snapshot creation
49
+ }
43
50
  // Fetch full persona with workflow
44
51
  const fullPersona = await client.getPersonaById(persona.id);
45
52
  if (!fullPersona) {
46
53
  return { error: `Could not fetch full persona: ${persona.id}` };
47
54
  }
48
- const result = engine.forceCreateVersion(fullPersona, {
55
+ const result = await engine.forceCreateVersion(fullPersona, {
49
56
  environment: versionContext.environment,
50
57
  tenant_id: versionContext.tenant_id,
51
58
  message: args.message,
@@ -73,7 +80,15 @@ export async function handleVersion(mode, args, client, persona, versionContext)
73
80
  // ─────────────────────────────────────────────────────────────────────────
74
81
  case "history":
75
82
  case "version_list": {
76
- const versions = engine.listVersions(persona.id, {
83
+ // Lazy migration: seed central from local so history shows existing versions
84
+ try {
85
+ const localStorage = new VersionStorage(versionContext.workspaceRoot);
86
+ await migrateLocalToCentral(localStorage, storage, persona.id);
87
+ }
88
+ catch {
89
+ // Best-effort
90
+ }
91
+ const versions = await engine.listVersions(persona.id, {
77
92
  limit: args.limit,
78
93
  });
79
94
  return {
@@ -88,7 +103,7 @@ export async function handleVersion(mode, args, client, persona, versionContext)
88
103
  // ─────────────────────────────────────────────────────────────────────────
89
104
  case "version_get": {
90
105
  const versionId = args.version ?? "latest";
91
- const version = engine.getVersion(persona.id, versionId);
106
+ const version = await engine.getVersion(persona.id, versionId);
92
107
  if (!version) {
93
108
  return { error: `Version not found: ${versionId}` };
94
109
  }
@@ -126,7 +141,7 @@ export async function handleVersion(mode, args, client, persona, versionContext)
126
141
  if (!v1 || !v2) {
127
142
  return { error: "v1 and v2 required for version_compare mode" };
128
143
  }
129
- const result = engine.compareVersions(persona.id, v1, v2);
144
+ const result = await engine.compareVersions(persona.id, v1, v2);
130
145
  if (!result.success) {
131
146
  return { error: result.error };
132
147
  }
@@ -152,14 +167,14 @@ export async function handleVersion(mode, args, client, persona, versionContext)
152
167
  if (!versionId) {
153
168
  return { error: "version required for restore mode" };
154
169
  }
155
- const restoreData = engine.getRestoreData(persona.id, versionId);
170
+ const restoreData = await engine.getRestoreData(persona.id, versionId);
156
171
  if (!restoreData.success || !restoreData.restore_payload) {
157
172
  return { error: restoreData.error ?? "Failed to get restore data" };
158
173
  }
159
174
  // Create a version snapshot before restoring (audit trail)
160
175
  const fullPersona = await client.getPersonaById(persona.id);
161
176
  if (fullPersona) {
162
- engine.forceCreateVersion(fullPersona, {
177
+ await engine.forceCreateVersion(fullPersona, {
163
178
  environment: versionContext.environment,
164
179
  tenant_id: versionContext.tenant_id,
165
180
  message: `Before restore to ${restoreData.version?.version_name}`,
@@ -180,7 +195,7 @@ export async function handleVersion(mode, args, client, persona, versionContext)
180
195
  // Create post-restore version
181
196
  const restoredPersona = await client.getPersonaById(persona.id);
182
197
  if (restoredPersona) {
183
- engine.forceCreateVersion(restoredPersona, {
198
+ await engine.forceCreateVersion(restoredPersona, {
184
199
  environment: versionContext.environment,
185
200
  tenant_id: versionContext.tenant_id,
186
201
  message: `Restored to ${restoreData.version?.version_name}`,
@@ -207,7 +222,7 @@ export async function handleVersion(mode, args, client, persona, versionContext)
207
222
  args.auto_on_sync !== undefined ||
208
223
  args.max_versions !== undefined;
209
224
  if (hasUpdates) {
210
- const updated = engine.updatePolicy(persona.id, {
225
+ const updated = await engine.updatePolicy(persona.id, {
211
226
  auto_version_on_deploy: args.auto_on_deploy,
212
227
  auto_version_on_sync: args.auto_on_sync,
213
228
  max_versions: args.max_versions,
@@ -221,7 +236,7 @@ export async function handleVersion(mode, args, client, persona, versionContext)
221
236
  };
222
237
  }
223
238
  // Just get current policy
224
- const policy = engine.getPolicy(persona.id);
239
+ const policy = await engine.getPolicy(persona.id);
225
240
  return {
226
241
  persona_id: persona.id,
227
242
  persona_name: persona.name,
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Template Adapter
3
+ *
4
+ * Routes CRUD methods (list/get/create/update/delete) to the new crud handler.
5
+ * Non-CRUD calls (patterns, widgets, config, questions) fall through to the
6
+ * existing handleTemplate handler for backwards compatibility.
7
+ *
8
+ * Same pattern as debug/adapter.ts.
9
+ */
10
+ import { handleTemplateCrud } from "./crud.js";
11
+ import { handleTemplate } from "./index.js";
12
+ const CRUD_METHODS = new Set(["list", "get", "create", "update", "delete"]);
13
+ export async function handleTemplateAdapter(args, createClient, getDefaultEnvName) {
14
+ const method = args.method;
15
+ // Route CRUD methods to the new handler
16
+ if (method && CRUD_METHODS.has(method)) {
17
+ const targetEnv = args.env ?? getDefaultEnvName();
18
+ const client = createClient(targetEnv);
19
+ return handleTemplateCrud(args, client);
20
+ }
21
+ // Legacy calls (patterns, widgets, config, questions) go to existing handler
22
+ return handleTemplate(args);
23
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Template CRUD Handler — Method Dispatch
3
+ *
4
+ * Routes template CRUD methods to the appropriate SDK calls.
5
+ * Follows the handler pattern from debug/index.ts.
6
+ */
7
+ const METHODS = ["list", "get", "create", "update", "delete"];
8
+ /**
9
+ * Map friendly type names to PersonaTriggerTypeEnum values.
10
+ * The backend DB rejects "UNSPECIFIED" (proto default 0), so a valid
11
+ * trigger_type must always be provided on create.
12
+ */
13
+ const TRIGGER_TYPE_MAP = {
14
+ chat: 1,
15
+ chatbot: 1,
16
+ dashboard: 2,
17
+ extraction: 2,
18
+ agent_assist: 3,
19
+ conversational: 4,
20
+ document: 5,
21
+ email: 6,
22
+ agent_qa: 7,
23
+ voice: 1, // voice personas use CHATBOT trigger type
24
+ };
25
+ function resolveTriggerType(type) {
26
+ if (!type)
27
+ return 1; // default to CHATBOT
28
+ const asNumber = Number(type);
29
+ if (!isNaN(asNumber) && asNumber >= 1 && asNumber <= 7)
30
+ return asNumber;
31
+ return TRIGGER_TYPE_MAP[type.toLowerCase()] ?? 1;
32
+ }
33
+ export async function handleTemplateCrud(args, client) {
34
+ const method = args.method;
35
+ if (!method) {
36
+ return {
37
+ error: "Missing required parameter: method",
38
+ available_methods: [...METHODS],
39
+ _tip: "Start with template(method='list') to browse available templates.",
40
+ };
41
+ }
42
+ switch (method) {
43
+ case "list":
44
+ return handleList(args, client);
45
+ case "get":
46
+ return handleGet(args, client);
47
+ case "create":
48
+ return handleCreate(args, client);
49
+ case "update":
50
+ return handleUpdate(args, client);
51
+ case "delete":
52
+ return handleDelete(args, client);
53
+ default:
54
+ return {
55
+ error: `Unknown method: ${method}`,
56
+ available_methods: [...METHODS],
57
+ };
58
+ }
59
+ }
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ // Method handlers
62
+ // ─────────────────────────────────────────────────────────────────────────────
63
+ async function handleList(args, client) {
64
+ const templates = await client.getPersonaTemplates();
65
+ // Apply optional filters
66
+ let filtered = templates;
67
+ const query = args.query;
68
+ if (query) {
69
+ const q = query.toLowerCase();
70
+ filtered = filtered.filter((t) => t.name?.toLowerCase().includes(q) ||
71
+ t.description?.toLowerCase().includes(q));
72
+ }
73
+ const type = args.type;
74
+ if (type) {
75
+ // Map friendly type names to trigger_type values
76
+ const typeMap = {
77
+ voice: "VOICEBOT_AI_EMPLOYEE",
78
+ chat: "CHATBOT",
79
+ dashboard: "DOCUMENT_TRIGGER",
80
+ };
81
+ const triggerType = typeMap[type.toLowerCase()] ?? type;
82
+ filtered = filtered.filter((t) => t.trigger_type === triggerType);
83
+ }
84
+ const category = args.category;
85
+ if (category) {
86
+ const c = category.toLowerCase();
87
+ filtered = filtered.filter((t) => t.category?.toLowerCase() === c);
88
+ }
89
+ return {
90
+ count: filtered.length,
91
+ templates: filtered.map((t) => ({
92
+ id: t.id,
93
+ name: t.name,
94
+ description: t.description,
95
+ category: t.category,
96
+ trigger_type: t.trigger_type,
97
+ has_project_template: t.has_project_template,
98
+ })),
99
+ };
100
+ }
101
+ async function handleGet(args, client) {
102
+ const id = args.id;
103
+ if (!id) {
104
+ return { error: "Missing required parameter: id" };
105
+ }
106
+ const template = await client.getPersonaTemplateById(id);
107
+ if (!template) {
108
+ return { error: `Template not found: ${id}` };
109
+ }
110
+ return template;
111
+ }
112
+ async function handleCreate(args, client) {
113
+ const name = args.name;
114
+ if (!name) {
115
+ return { error: "Missing required parameter: name" };
116
+ }
117
+ // Resolve trigger_type: the backend DB rejects proto default "UNSPECIFIED",
118
+ // so we must always provide a valid numeric trigger_type.
119
+ const triggerType = resolveTriggerType(args.type);
120
+ const result = await client.createPersonaTemplate({
121
+ name,
122
+ description: args.description,
123
+ proto_config: args.proto_config,
124
+ trigger_type: String(triggerType),
125
+ category: args.category,
126
+ about_template: args.about_template,
127
+ workflow_def: args.workflow_def,
128
+ source_template_id: args.from,
129
+ });
130
+ return {
131
+ success: true,
132
+ template_id: result.template_id,
133
+ name,
134
+ _tip: `Template created. Use template(method='get', id='${result.template_id}') to view it.`,
135
+ };
136
+ }
137
+ async function handleUpdate(args, client) {
138
+ const id = args.id;
139
+ if (!id) {
140
+ return { error: "Missing required parameter: id" };
141
+ }
142
+ const result = await client.updatePersonaTemplate({
143
+ template_id: id,
144
+ name: args.name,
145
+ description: args.description,
146
+ proto_config: args.proto_config,
147
+ category: args.category,
148
+ about_template: args.about_template,
149
+ workflow_def: args.workflow_def,
150
+ });
151
+ return {
152
+ success: true,
153
+ template_id: id,
154
+ ...result,
155
+ };
156
+ }
157
+ async function handleDelete(args, client) {
158
+ const id = args.id;
159
+ if (!id) {
160
+ return { error: "Missing required parameter: id" };
161
+ }
162
+ const confirm = args.confirm;
163
+ if (confirm !== true) {
164
+ return {
165
+ error: "Deletion requires confirm=true",
166
+ _tip: `To delete template ${id}, call template(method='delete', id='${id}', confirm=true)`,
167
+ };
168
+ }
169
+ await client.deletePersonaTemplate(id);
170
+ return {
171
+ success: true,
172
+ deleted_template_id: id,
173
+ };
174
+ }
@@ -15,8 +15,7 @@ import { getTemplateFallback, getTemplateFieldDocs } from "../../../sdk/generate
15
15
  export function handleTemplate(args) {
16
16
  // Get specific pattern
17
17
  if (args.pattern) {
18
- const patternName = args.pattern;
19
- const pattern = WORKFLOW_PATTERNS[patternName];
18
+ const pattern = WORKFLOW_PATTERNS.find(p => p.name === args.pattern);
20
19
  if (!pattern) {
21
20
  return { error: `Pattern not found: ${args.pattern}` };
22
21
  }
@@ -24,14 +23,14 @@ export function handleTemplate(args) {
24
23
  }
25
24
  // List patterns
26
25
  if (args.patterns) {
27
- let patterns = Object.entries(WORKFLOW_PATTERNS);
26
+ let filtered = WORKFLOW_PATTERNS;
28
27
  if (args.type) {
29
- patterns = patterns.filter(([_, p]) => !p.personaType || p.personaType === args.type);
28
+ filtered = filtered.filter(p => !p.personaType || p.personaType === args.type);
30
29
  }
31
30
  return {
32
- count: patterns.length,
33
- patterns: patterns.map(([name, p]) => ({
34
- name,
31
+ count: filtered.length,
32
+ patterns: filtered.map(p => ({
33
+ name: p.name,
35
34
  description: p.description,
36
35
  use_case: p.useCase,
37
36
  persona_type: p.personaType,
@@ -7,8 +7,7 @@
7
7
  * Extracted from server.ts to keep the dispatch table thin.
8
8
  */
9
9
  import { fingerprintPersona } from "../../../sync.js";
10
- import { createVersionStorage } from "../../../sync/version-storage.js";
11
- import { createVersionPolicyEngine } from "../../../sync/version-policy.js";
10
+ import { createCentralStorageEngine } from "../../../sync/central-factory.js";
12
11
  import { handleWorkflow } from "./index.js";
13
12
  import { handleWorkflowOptimize } from "./optimize.js";
14
13
  export async function handleWorkflowAdapter(args, createClient, getDefaultEnvName) {
@@ -84,60 +83,45 @@ export async function handleWorkflowAdapter(args, createClient, getDefaultEnvNam
84
83
  return { error: 'persona_id is required for workflow(mode="deploy")' };
85
84
  }
86
85
  const targetEnv = normalizedArgs.env ?? getDefaultEnvName();
86
+ // Fetch persona and validate fingerprint (blocking — these are safety checks)
87
+ const personaBefore = await client.getPersonaById(personaId);
88
+ if (!personaBefore) {
89
+ return { error: `Persona not found: ${personaId}` };
90
+ }
91
+ const currentFp = fingerprintPersona(personaBefore);
92
+ if (!force && !baseFingerprint) {
93
+ return {
94
+ error: "base_fingerprint is required for workflow deploy (stale-state protection)",
95
+ persona_id: personaId,
96
+ current_fingerprint: currentFp,
97
+ hint: "Run workflow(mode='get', persona_id='...') immediately before deploying and pass fingerprint as base_fingerprint. Use force=true only for emergency overrides.",
98
+ };
99
+ }
100
+ if (!force && baseFingerprint && baseFingerprint !== currentFp) {
101
+ return {
102
+ error: "Persona changed since you last fetched it (fingerprint mismatch)",
103
+ persona_id: personaId,
104
+ base_fingerprint: baseFingerprint,
105
+ current_fingerprint: currentFp,
106
+ hint: "Re-run workflow(mode='get') to fetch the latest workflow_def, re-apply your edits, then deploy again. Use force=true only if you intend to overwrite out-of-band changes.",
107
+ };
108
+ }
109
+ // Best-effort: snapshot before any destructive change (via central DIS storage)
87
110
  let versionCreated;
88
111
  try {
89
- const personaBefore = await client.getPersonaById(personaId);
90
- if (!personaBefore) {
91
- return { error: `Persona not found: ${personaId}` };
92
- }
93
- const currentFp = fingerprintPersona(personaBefore);
94
- if (!force && !baseFingerprint) {
95
- return {
96
- error: "base_fingerprint is required for workflow deploy (stale-state protection)",
97
- persona_id: personaId,
98
- current_fingerprint: currentFp,
99
- hint: "Run workflow(mode='get', persona_id='...') immediately before deploying and pass fingerprint as base_fingerprint. Use force=true only for emergency overrides.",
100
- };
101
- }
102
- if (!force && baseFingerprint && baseFingerprint !== currentFp) {
103
- return {
104
- error: "Persona changed since you last fetched it (fingerprint mismatch)",
105
- persona_id: personaId,
106
- base_fingerprint: baseFingerprint,
107
- current_fingerprint: currentFp,
108
- hint: "Re-run workflow(mode='get') to fetch the latest workflow_def, re-apply your edits, then deploy again. Use force=true only if you intend to overwrite out-of-band changes.",
109
- };
110
- }
111
- const storage = createVersionStorage(process.cwd());
112
- const engine = createVersionPolicyEngine(storage);
113
- const snap = engine.forceCreateVersion(personaBefore, {
112
+ const { engine } = createCentralStorageEngine(client, personaId);
113
+ const snap = await engine.forceCreateVersion(personaBefore, {
114
114
  environment: targetEnv,
115
115
  tenant_id: targetEnv,
116
116
  message: "Pre-deploy snapshot",
117
117
  created_by: "mcp-toolkit",
118
118
  });
119
- if (!snap.created || !snap.version) {
120
- if (!force) {
121
- return {
122
- error: "Failed to create pre-deploy snapshot (required before deploy)",
123
- persona_id: personaId,
124
- details: snap.reason,
125
- hint: "Fix snapshotting (workspace storage) or retry with force=true for emergency override.",
126
- };
127
- }
128
- }
129
- else {
119
+ if (snap.created && snap.version) {
130
120
  versionCreated = { id: snap.version.id, version_name: snap.version.version_name };
131
121
  }
132
122
  }
133
- catch (snapshotErr) {
134
- if (!force) {
135
- return {
136
- error: "Failed to create pre-deploy snapshot (required before deploy)",
137
- persona_id: personaId,
138
- hint: "Retry after fixing local workspace write access, or use force=true for emergency override.",
139
- };
140
- }
123
+ catch {
124
+ // Best-effort: snapshot failure does not block the deploy
141
125
  }
142
126
  const deployResult = await handleWorkflow({
143
127
  mode: "deploy",
@@ -43,7 +43,7 @@ export const DEPRECATED_ACTIONS_FALLBACK = {
43
43
  "custom_agent/v0": { replacement: "custom_agent/v1" },
44
44
  "json_mapper/v0": { replacement: "json_mapper/v1" },
45
45
  "text_categorizer/v0": { replacement: "text_categorizer/v1" },
46
- "respond_with_sources/v0": { replacement: "respond_for_external_actions", notes: "Handles tool results better" },
46
+ "respond_with_sources/v0": { replacement: "call_llm with named_inputs", notes: "For KB Q&A use call_llm; respond_for_external_actions is ONLY for external_action_caller results" },
47
47
  };
48
48
  /**
49
49
  * Get deprecated actions from API (with fallback)
@@ -183,7 +183,7 @@ async function handleWorkflowGet(args, client) {
183
183
  flow_pattern: "trigger → categorizer (optional) → processing → response → WORKFLOW_OUTPUT",
184
184
  categorizer: "For multi-intent workflows, use chat_categorizer early with a Fallback category",
185
185
  fallback: "Every categorizer MUST have a Fallback category for unmatched intents",
186
- hitl: "For actions with side effects (email, external calls), enable the HITL flag on the agent do NOT add standalone general_hitl nodes",
186
+ hitl: "Two HITL mechanisms: 1) general_hitl (Human Collaboration Agent) for rich dialogue/forms, 2) disable_human_interaction: false flag on send_email_agent or entity_extraction_with_documents for simple approval",
187
187
  search: "Use search/v2 with datastore_configs. Wire query from trigger output.",
188
188
  },
189
189
  // LLM-driven analysis pattern: MCP provides data, LLM does the thinking
@@ -167,7 +167,7 @@ const KNOWLEDGE_SEARCH_TYPES = [
167
167
  "search_datastore",
168
168
  "knowledge_search",
169
169
  "semantic_search",
170
- "combine_search_results",
170
+ "combine_and_rerank_search_results",
171
171
  ];
172
172
  /**
173
173
  * Known web search action types (excluded from knowledge search detection).
@@ -222,7 +222,7 @@ export function getSearchNodeWidgetNames(workflowDef) {
222
222
  const elements = dc?.multiBinding?.elements ?? [];
223
223
  for (const el of elements) {
224
224
  const name = el.widgetConfig?.widgetName;
225
- if (typeof name === "string" && name.trim().length > 0) {
225
+ if (typeof name === "string" && name.trim().length > 0 && !name.startsWith("_")) {
226
226
  widgetNames.add(name.trim());
227
227
  }
228
228
  }