@ema.co/mcp-toolkit 2026.1.25 → 2026.1.26-4

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 (87) hide show
  1. package/README.md +10 -2
  2. package/dist/mcp/handlers/action/index.js +3 -18
  3. package/dist/mcp/handlers/data/index.js +385 -41
  4. package/dist/mcp/handlers/data/templates.js +107 -0
  5. package/dist/mcp/handlers/deprecation.js +50 -0
  6. package/dist/mcp/handlers/env/index.js +8 -4
  7. package/dist/mcp/handlers/knowledge/index.js +44 -237
  8. package/dist/mcp/handlers/persona/create.js +47 -18
  9. package/dist/mcp/handlers/persona/index.js +14 -11
  10. package/dist/mcp/handlers/persona/update.js +4 -2
  11. package/dist/mcp/handlers/persona/version.js +234 -0
  12. package/dist/mcp/handlers/sync/index.js +3 -18
  13. package/dist/mcp/handlers/template/index.js +75 -10
  14. package/dist/mcp/handlers/workflow/analyze.js +171 -0
  15. package/dist/mcp/handlers/workflow/compare.js +70 -0
  16. package/dist/mcp/handlers/workflow/deploy.js +73 -0
  17. package/dist/mcp/handlers/workflow/generate.js +350 -0
  18. package/dist/mcp/handlers/workflow/index.js +294 -0
  19. package/dist/mcp/handlers/workflow/modify.js +456 -0
  20. package/dist/mcp/handlers/workflow/optimize.js +136 -0
  21. package/dist/mcp/handlers/workflow/types.js +4 -0
  22. package/dist/mcp/handlers/workflow/utils.js +30 -0
  23. package/dist/mcp/handlers-consolidated.js +73 -2696
  24. package/dist/mcp/prompts.js +83 -43
  25. package/dist/mcp/resources.js +382 -57
  26. package/dist/mcp/server.js +199 -391
  27. package/dist/mcp/{tools-v2.js → tools.js} +20 -54
  28. package/dist/mcp/workflow-operations.js +2 -2
  29. package/dist/sdk/client-adapter.js +267 -32
  30. package/dist/sdk/client.js +45 -16
  31. package/dist/sdk/ema-client.js +183 -0
  32. package/dist/sdk/generated/deprecated-actions.js +171 -0
  33. package/dist/sdk/generated/template-fallbacks.js +123 -0
  34. package/dist/sdk/guidance.js +65 -11
  35. package/dist/sdk/index.js +3 -1
  36. package/dist/sdk/knowledge.js +139 -86
  37. package/dist/sdk/workflow-intent.js +27 -0
  38. package/dist/sdk/workflow-transformer.js +0 -342
  39. package/docs/mcp-tools-guide.md +37 -45
  40. package/package.json +10 -4
  41. package/dist/mcp/handlers/persona/analyze.js +0 -275
  42. package/dist/mcp/handlers/persona/compare.js +0 -32
  43. package/dist/mcp/tools-consolidated.js +0 -875
  44. package/dist/mcp/tools-legacy.js +0 -736
  45. package/docs/CODEBASE-ANALYSIS-2026-01-23.md +0 -936
  46. package/docs/CODEBASE-ANALYSIS-PRIORITIZED.md +0 -774
  47. package/docs/api-contracts.md +0 -216
  48. package/docs/auto-builder-analysis.md +0 -271
  49. package/docs/blog/mcp-tool-design-lessons.md +0 -309
  50. package/docs/data-architecture.md +0 -166
  51. package/docs/demos/ap-invoice-generation.md +0 -347
  52. package/docs/demos/ap-invoice-processing.md +0 -271
  53. package/docs/ema-auto-builder-guide.html +0 -394
  54. package/docs/lessons-learned.md +0 -209
  55. package/docs/llm-native-workflow-design.md +0 -252
  56. package/docs/local-generation.md +0 -508
  57. package/docs/mcp-flow-diagram.md +0 -135
  58. package/docs/migration/action-composition-migration.md +0 -270
  59. package/docs/naming-conventions.md +0 -278
  60. package/docs/proposals/HANDOFF-tool-restructure.md +0 -526
  61. package/docs/proposals/action-composition.md +0 -490
  62. package/docs/proposals/explicit-method-restructure.md +0 -328
  63. package/docs/proposals/mcp-tool-restructure-2026-01.md +0 -366
  64. package/docs/proposals/self-contained-guidance.md +0 -427
  65. package/docs/proto-sdk-generation.md +0 -242
  66. package/docs/release-impact.md +0 -102
  67. package/docs/release-process.md +0 -157
  68. package/docs/staging.RULE.md +0 -142
  69. package/docs/test-persona-creation.md +0 -196
  70. package/docs/tool-consolidation-v2.md +0 -225
  71. package/docs/tool-response-standards.md +0 -256
  72. package/resources/demo-kits/README.md +0 -175
  73. package/resources/demo-kits/finance-ap/manifest.json +0 -150
  74. package/resources/demo-kits/tags.json +0 -91
  75. package/resources/docs/getting-started.md +0 -97
  76. package/resources/templates/auto-builder-rules.md +0 -224
  77. package/resources/templates/chat-ai/README.md +0 -119
  78. package/resources/templates/chat-ai/persona-config.json +0 -111
  79. package/resources/templates/dashboard-ai/README.md +0 -156
  80. package/resources/templates/dashboard-ai/persona-config.json +0 -180
  81. package/resources/templates/demo-scenarios/README.md +0 -63
  82. package/resources/templates/demo-scenarios/test-published-package.md +0 -116
  83. package/resources/templates/document-gen-ai/README.md +0 -132
  84. package/resources/templates/document-gen-ai/persona-config.json +0 -316
  85. package/resources/templates/voice-ai/README.md +0 -123
  86. package/resources/templates/voice-ai/persona-config.json +0 -74
  87. package/resources/templates/voice-ai/workflow-prompt.md +0 -121
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Generation Templates for data(method="generate")
3
+ *
4
+ * Built-in templates for document/data generation via the Document Generation API.
5
+ * Used by data(method="generate", from="...").
6
+ */
7
+ export const GENERATION_TEMPLATES = {
8
+ // Reference data
9
+ "countries": {
10
+ description: "ISO 3166-1 countries with code and name",
11
+ category: "reference",
12
+ prompt: "Generate a JSON array of {count} ISO 3166-1 countries with fields: code (2-letter), name. Format: {format}",
13
+ },
14
+ "industries": {
15
+ description: "NAICS industry classifications",
16
+ category: "reference",
17
+ prompt: "Generate a JSON array of {count} NAICS industry classifications with fields: code, name, description. Format: {format}",
18
+ },
19
+ "currencies": {
20
+ description: "ISO 4217 currencies",
21
+ category: "reference",
22
+ prompt: "Generate a JSON array of {count} ISO 4217 currencies with fields: code (3-letter), name, symbol. Format: {format}",
23
+ },
24
+ // Demo kits
25
+ "demo-sales-sdr": {
26
+ description: "Sales SDR demo kit with prospects, products, and Q&A",
27
+ category: "demo",
28
+ prompt: `Generate a comprehensive sales demo kit in {format} format containing:
29
+ 1. {count} realistic B2B prospect profiles (company name, contact, role, pain points, budget)
30
+ 2. 3-5 product entries with features and pricing
31
+ 3. 10 common sales objections with responses
32
+ 4. Sample discovery call script`,
33
+ },
34
+ "demo-support": {
35
+ description: "Customer support demo kit with customers, FAQs, and tickets",
36
+ category: "demo",
37
+ prompt: `Generate a customer support demo kit in {format} format containing:
38
+ 1. {count} customer profiles (name, company, subscription tier, history)
39
+ 2. 10 frequently asked questions with detailed answers
40
+ 3. 5 sample support tickets with resolutions
41
+ 4. Escalation guidelines`,
42
+ },
43
+ "demo-hr": {
44
+ description: "HR assistant demo kit with employees, policies, and benefits",
45
+ category: "demo",
46
+ prompt: `Generate an HR demo kit in {format} format containing:
47
+ 1. {count} employee profiles (name, department, role, start date)
48
+ 2. 5 company policies (PTO, remote work, expenses)
49
+ 3. 3 benefits descriptions (health, 401k, equity)
50
+ 4. Onboarding checklist`,
51
+ },
52
+ // Entity templates
53
+ "customer": {
54
+ description: "B2B customer profile",
55
+ category: "entity",
56
+ prompt: `Generate {count} realistic B2B customer profiles in {format} format with fields:
57
+ - company_name, industry, size (employees), annual_revenue
58
+ - primary_contact (name, title, email, phone)
59
+ - pain_points (array of 2-3 business challenges)
60
+ - current_solutions (what they use today)
61
+ - budget_range, decision_timeline`,
62
+ },
63
+ "product": {
64
+ description: "Product catalog entry with pricing",
65
+ category: "entity",
66
+ prompt: `Generate {count} product catalog entries in {format} format with fields:
67
+ - name, sku, category
68
+ - description (2-3 sentences)
69
+ - features (array of 3-5 key features)
70
+ - pricing (base_price, currency, billing_period)
71
+ - availability (in_stock, lead_time)`,
72
+ },
73
+ "faq": {
74
+ description: "FAQ entries",
75
+ category: "entity",
76
+ prompt: `Generate {count} FAQ entries in {format} format with fields:
77
+ - question (clear, specific question)
78
+ - answer (comprehensive answer, 2-4 sentences)
79
+ - category (e.g., billing, technical, getting-started)
80
+ - related_questions (array of 2-3 related question titles)`,
81
+ },
82
+ "employee": {
83
+ description: "Employee profile for HR systems",
84
+ category: "entity",
85
+ prompt: `Generate {count} employee profiles in {format} format with fields:
86
+ - name, email, employee_id
87
+ - department, title, manager
88
+ - start_date, location (office or remote)
89
+ - skills (array), certifications (array)`,
90
+ },
91
+ };
92
+ /**
93
+ * Get a generation template by name
94
+ */
95
+ export function getGenerationTemplate(name) {
96
+ return GENERATION_TEMPLATES[name];
97
+ }
98
+ /**
99
+ * List all available generation templates
100
+ */
101
+ export function listGenerationTemplates() {
102
+ return Object.entries(GENERATION_TEMPLATES).map(([name, t]) => ({
103
+ name,
104
+ description: t.description,
105
+ category: t.category,
106
+ }));
107
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Deprecation Tracking - Single Source of Truth
3
+ *
4
+ * Centralized deprecation warnings for parameter names.
5
+ * Used by all handlers to ensure consistent messaging.
6
+ */
7
+ /**
8
+ * Deprecated parameter mappings
9
+ *
10
+ * Key: old parameter name
11
+ * Value: { newName, removeVersion }
12
+ */
13
+ export const DEPRECATED_PARAMS = {
14
+ identifier: { newName: "id", removeVersion: "2.0.0" },
15
+ clone_from: { newName: "from", removeVersion: "2.0.0" },
16
+ template_id: { newName: "from", removeVersion: "2.0.0" },
17
+ clone_data: { newName: "include_data", removeVersion: "2.0.0" },
18
+ };
19
+ /**
20
+ * Check args for deprecated params and return warnings.
21
+ */
22
+ export function checkDeprecatedParams(args) {
23
+ const warnings = [];
24
+ for (const [oldName, { newName, removeVersion }] of Object.entries(DEPRECATED_PARAMS)) {
25
+ if (args[oldName] !== undefined) {
26
+ warnings.push(`'${oldName}' is deprecated, use '${newName}' instead (will be removed in v${removeVersion})`);
27
+ }
28
+ }
29
+ return warnings;
30
+ }
31
+ /**
32
+ * Add deprecation warnings to a result object.
33
+ */
34
+ export function addDeprecationWarnings(result, warnings) {
35
+ if (warnings.length > 0) {
36
+ return { ...result, _deprecation_warnings: warnings };
37
+ }
38
+ return result;
39
+ }
40
+ /**
41
+ * Log and return deprecation warnings for an args object.
42
+ * Call this at the start of handlers that accept deprecated params.
43
+ */
44
+ export function handleDeprecatedParams(args, context) {
45
+ const warnings = checkDeprecatedParams(args);
46
+ for (const warning of warnings) {
47
+ console.warn(`[${context}] Deprecation: ${warning}`);
48
+ }
49
+ return warnings;
50
+ }
@@ -13,6 +13,7 @@ export async function handleEnv(_args, getEnvironments, toolkit) {
13
13
  const criticalRules = GUIDANCE_RULES
14
14
  .filter(r => r.level === "critical")
15
15
  .map(r => `• ${r.title}: ${r.do}`);
16
+ const defaultEnv = envs.find(e => e.isDefault)?.name || envs[0]?.name || "demo";
16
17
  return {
17
18
  environments: envs.map(e => ({
18
19
  name: e.name,
@@ -23,10 +24,10 @@ export async function handleEnv(_args, getEnvironments, toolkit) {
23
24
  // Onboarding guidance for new sessions
24
25
  getting_started: {
25
26
  workflow: [
26
- "1. List personas: persona()",
27
- "2. Get details: persona(id='...')",
28
- "3. Analyze before modifying: persona(id='...', analyze=true)",
29
- "4. Preview before deploying: update with preview=true",
27
+ "1. List personas: persona(method=\"list\")",
28
+ "2. Get details: persona(method=\"get\", id=\"...\")",
29
+ "3. Analyze before modifying: persona(method=\"get\", id=\"...\", analyze=true)",
30
+ "4. Preview before deploying: persona(method=\"update\", id=\"...\", preview=true, ...)",
30
31
  ],
31
32
  critical_rules: criticalRules,
32
33
  resources: [
@@ -40,5 +41,8 @@ export async function handleEnv(_args, getEnvironments, toolkit) {
40
41
  "workflow_review - Analyze workflow health",
41
42
  ],
42
43
  },
44
+ // Consistent guidance format
45
+ _tip: `Start by listing personas: persona(method="list", env="${defaultEnv}")`,
46
+ _next_step: `persona(method="list", env="${defaultEnv}")`,
43
47
  };
44
48
  }
@@ -1,247 +1,54 @@
1
1
  /**
2
- * Knowledge Handler
2
+ * Knowledge Handler - DEPRECATED
3
3
  *
4
- * Manages data sources (files, embeddings, dashboard rows) for personas.
5
- * Note: This is deprecated in favor of the 'data' tool.
4
+ * This handler is deprecated. Use the 'data' tool instead.
5
+ * All calls are routed to handleData with a deprecation warning.
6
6
  */
7
- import { hasExtractedDataHandler, getDataModeHandler, } from "../data/index.js";
7
+ import { handleData } from "../data/index.js";
8
8
  /**
9
- * Handle knowledge tool requests - manage data sources for personas
9
+ * Mode mapping from knowledge tool to data tool
10
+ */
11
+ const MODE_MAPPING = {
12
+ list: "list",
13
+ aggregates: "stats",
14
+ upload: "upload",
15
+ delete: "delete",
16
+ status: "embedding",
17
+ toggle: "embedding",
18
+ attach: "attach",
19
+ dashboard_rows: "dashboard_rows",
20
+ dashboard_clone: "copy",
21
+ dashboard_generate: "generate",
22
+ };
23
+ /**
24
+ * Handle knowledge tool requests
10
25
  *
11
- * @deprecated Use data() tool instead
26
+ * @deprecated Use data() tool instead. This handler routes to handleData.
12
27
  */
13
- export async function handleKnowledge(args, client, readFile, opts) {
14
- const personaId = args.persona_id;
28
+ export async function handleKnowledge(args, client, readFile) {
29
+ console.warn("[knowledge] DEPRECATED: Use data() tool instead");
15
30
  const mode = args.mode || "list";
16
- const includeWarning = opts?.includeDeprecationWarning ?? true;
17
- // Deprecation warning (added to response when called directly)
18
- const deprecationWarning = includeWarning ? {
19
- _deprecation: {
20
- message: "The 'knowledge' tool is deprecated. Please use 'data' tool instead.",
21
- migration: {
22
- "knowledge(mode='list')": "data(mode='list')",
23
- "knowledge(mode='upload')": "data(mode='upload')",
24
- "knowledge(mode='delete')": "data(mode='delete')",
25
- "knowledge(mode='status')": "data(mode='embedding')",
26
- "knowledge(mode='toggle')": "data(mode='embedding', enabled=...)",
27
- },
28
- },
29
- } : {};
30
- // Helper to wrap result with deprecation warning
31
- const withWarning = (result) => {
32
- if (typeof result === 'object' && result !== null) {
33
- return { ...deprecationWarning, ...result };
34
- }
35
- return result;
31
+ const mappedMode = MODE_MAPPING[mode] || mode;
32
+ // Route to data handler
33
+ const dataArgs = {
34
+ ...args,
35
+ mode: mappedMode,
36
36
  };
37
- switch (mode) {
38
- case "list": {
39
- const page = args.page;
40
- const limit = args.limit;
41
- const widgetName = args.widget_name;
42
- const result = await client.listDataSourceFiles(personaId, { page, limit, widgetName });
43
- return withWarning({
44
- persona_id: personaId,
45
- widget_name: result.widgetName,
46
- files: result.files,
47
- count: result.files.length,
48
- pagination: result.pagination,
49
- });
50
- }
51
- case "aggregates": {
52
- const widgetName = args.widget_name;
53
- const agg = await client.getDataSourceAggregates(personaId, widgetName);
54
- return withWarning({
55
- persona_id: personaId,
56
- widget_name: agg.widgetName,
57
- total_files: agg.total,
58
- by_status: {
59
- pending: agg.pending,
60
- success: agg.success,
61
- failed: agg.failed,
62
- },
63
- });
64
- }
65
- case "upload": {
66
- const filePath = args.file;
67
- if (!filePath) {
68
- return withWarning({ error: "file path required for upload" });
69
- }
70
- const content = await readFile(filePath);
71
- const filename = filePath.split("/").pop() || "file";
72
- const result = await client.uploadDataSource(personaId, content, filename, { tags: args.tags });
73
- return withWarning({ success: true, ...result });
74
- }
75
- case "delete": {
76
- const fileId = args.file_id;
77
- if (!fileId) {
78
- return withWarning({ error: "file_id required for delete" });
79
- }
80
- const result = await client.deleteDataSource(personaId, fileId);
81
- return withWarning(result);
82
- }
83
- case "status": {
84
- const persona = await client.getPersonaById(personaId);
85
- if (!persona) {
86
- return withWarning({ error: `Persona not found: ${personaId}` });
87
- }
88
- return withWarning({
89
- persona_id: personaId,
90
- embedding_enabled: persona.embedding_enabled,
91
- });
92
- }
93
- case "toggle": {
94
- if (args.enabled === undefined) {
95
- return withWarning({ error: "enabled flag required for toggle" });
96
- }
97
- const persona = await client.getPersonaById(personaId);
98
- if (!persona) {
99
- return withWarning({ error: `Persona not found: ${personaId}` });
100
- }
101
- // IMPORTANT: The Ema API requires workflow to be sent along with proto_config
102
- // for proto_config changes to persist. This matches what the UI does.
103
- await client.updateAiEmployee({
104
- persona_id: personaId,
105
- embedding_enabled: args.enabled,
106
- proto_config: persona.proto_config,
107
- workflow: persona.workflow_def,
108
- });
109
- return withWarning({
110
- success: true,
111
- persona_id: personaId,
112
- embedding_enabled: args.enabled,
113
- });
114
- }
115
- case "attach": {
116
- // Attach a data source widget to a workflow node's datastore_configs input
117
- const nodeName = args.node_name;
118
- const widgetName = args.widget_name || "fileUpload";
119
- if (!nodeName) {
120
- return withWarning({ error: "node_name required (e.g., 'knowledge_search_1')" });
121
- }
122
- const persona = await client.getPersonaById(personaId);
123
- if (!persona) {
124
- return withWarning({ error: `Persona not found: ${personaId}` });
125
- }
126
- const workflowDef = persona.workflow_def;
127
- if (!workflowDef) {
128
- return withWarning({ error: "Persona has no workflow_def" });
129
- }
130
- const actions = workflowDef.actions;
131
- if (!actions) {
132
- return withWarning({ error: "Workflow has no actions" });
133
- }
134
- // Find the target node
135
- const nodeIndex = actions.findIndex(a => a.name === nodeName);
136
- if (nodeIndex < 0) {
137
- const availableNodes = actions.map(a => a.name).filter(Boolean);
138
- return withWarning({
139
- error: `Node '${nodeName}' not found`,
140
- available_nodes: availableNodes,
141
- });
142
- }
143
- const node = actions[nodeIndex];
144
- const inputs = (node.inputs || {});
145
- // Build the datastore_configs binding with multiBinding
146
- const datastoreBinding = {
147
- multiBinding: {
148
- elements: [
149
- {
150
- widgetConfig: { widgetName },
151
- autoDetectedBinding: false,
152
- },
153
- ],
154
- },
155
- autoDetectedBinding: false,
156
- };
157
- // Update the node's inputs
158
- inputs.datastore_configs = datastoreBinding;
159
- node.inputs = inputs;
160
- actions[nodeIndex] = node;
161
- workflowDef.actions = actions;
162
- // Update the persona with the modified workflow
163
- await client.updateAiEmployee({
164
- persona_id: personaId,
165
- proto_config: persona.proto_config,
166
- workflow: workflowDef,
167
- });
168
- return withWarning({
169
- success: true,
170
- persona_id: personaId,
171
- node_name: nodeName,
172
- widget_name: widgetName,
173
- message: `Data source '${widgetName}' attached to node '${nodeName}'`,
174
- });
175
- }
176
- case "dashboard_rows": {
177
- // Get rows from a dashboard persona
178
- const persona = await client.getPersonaById(personaId);
179
- if (!persona) {
180
- return withWarning({ error: `Persona not found: ${personaId}` });
181
- }
182
- const dashboardId = persona.workflow_dashboard_id;
183
- if (!dashboardId) {
184
- return withWarning({
185
- error: "This persona has no dashboard. Use dashboard_rows only for Dashboard-type AI Employees.",
186
- hint: "Check that the persona has trigger_type=DASHBOARD",
187
- });
188
- }
189
- const limit = args.limit ?? 100;
190
- const result = await client.getDashboardRows(dashboardId, personaId, { limit });
191
- // Return schema and rows in a structured format
192
- return withWarning({
193
- persona_id: personaId,
194
- dashboard_id: dashboardId,
195
- dashboard_name: result.dashboardName,
196
- total_rows: result.totalCount,
197
- schema: {
198
- input_columns: result.schema.columns.filter(c => c.isInput),
199
- output_columns: result.schema.columns.filter(c => !c.isInput),
200
- },
201
- rows: result.rows.map(row => ({
202
- id: row.id,
203
- state: row.state,
204
- created_at: row.dashboardRowMetadata.createdAt,
205
- input_values: row.columnValues
206
- .filter(cv => result.schema.columns.find(c => c.columnId === cv.columnId)?.isInput)
207
- .map(cv => ({
208
- column: result.schema.columns.find(c => c.columnId === cv.columnId)?.name ?? cv.columnId,
209
- value: cv.value.stringValue ?? cv.value.documentCellValue?.documentValues?.[0]?.name ?? cv.value.arrayValue?.arrayValues?.[0]?.stringValue ?? "(complex)",
210
- })),
211
- })),
212
- });
213
- }
214
- case "dashboard_clone": {
215
- // Delegated to extracted handler
216
- if (hasExtractedDataHandler("dashboard_clone")) {
217
- const handler = getDataModeHandler("dashboard_clone");
218
- // Map args to extracted handler format
219
- const cloneArgs = {
220
- id: personaId,
221
- clone_dashboard_from: args.source_persona_id,
222
- clone_limit: args.limit ?? 100,
223
- sanitize: args.sanitize,
224
- sanitize_examples: args.sanitize_examples,
225
- };
226
- return withWarning(await handler(cloneArgs, client));
227
- }
228
- return withWarning({ error: "dashboard_clone handler not found" });
229
- }
230
- case "dashboard_generate": {
231
- // Delegated to extracted handler
232
- if (hasExtractedDataHandler("dashboard_generate")) {
233
- const handler = getDataModeHandler("dashboard_generate");
234
- // Map args to extracted handler format
235
- const generateArgs = {
236
- id: personaId,
237
- dashboard_generate: args.count ?? args.dashboard_generate ?? 5,
238
- dashboard_template: args.template ?? args.dashboard_template,
239
- };
240
- return withWarning(await handler(generateArgs, client));
241
- }
242
- return withWarning({ error: "dashboard_generate handler not found" });
243
- }
244
- default:
245
- return withWarning({ error: `Unknown mode: ${mode}` });
37
+ // Map old param names to new
38
+ if (args.file)
39
+ dataArgs.file_path = args.file;
40
+ if (args.source_persona_id)
41
+ dataArgs.from = args.source_persona_id;
42
+ const result = await handleData(dataArgs, client, readFile);
43
+ // Add deprecation warning to result
44
+ if (typeof result === "object" && result !== null) {
45
+ return {
46
+ _deprecation: {
47
+ message: "The 'knowledge' tool is deprecated. Use 'data' tool instead.",
48
+ migration: `data(method="${mappedMode}", ...)`,
49
+ },
50
+ ...result,
51
+ };
246
52
  }
53
+ return result;
247
54
  }
@@ -128,13 +128,48 @@ export async function handleCreate(args, client, getTemplateId) {
128
128
  }
129
129
  // For persona cloning, default include_data to true
130
130
  const effectiveIncludeData = sourcePersonaId ? (includeData ?? true) : false;
131
- const result = await client.createAiEmployee({
132
- name,
133
- description: args.description,
134
- template_id: templateId,
135
- source_persona_id: sourcePersonaId,
136
- clone_data: effectiveIncludeData,
137
- });
131
+ let result;
132
+ try {
133
+ result = await client.createAiEmployee({
134
+ name,
135
+ description: args.description,
136
+ template_id: templateId,
137
+ source_persona_id: sourcePersonaId,
138
+ clone_data: effectiveIncludeData,
139
+ });
140
+ }
141
+ catch (err) {
142
+ const errorMsg = err instanceof Error ? err.message : String(err);
143
+ const personaType = args.type;
144
+ // Provide recovery guidance based on persona type
145
+ const recovery = {};
146
+ if (personaType === "voice" || templateId?.includes("voice") || templateId?.includes("001e")) {
147
+ recovery.option_1 = "Fetch gold template: ema://templates/voice-ai/workflow-def";
148
+ recovery.option_2 = "Fetch config template: ema://templates/voice-ai/proto-config";
149
+ recovery.option_3 = "See complete guide: ema://templates/voice-ai/complete";
150
+ }
151
+ else if (personaType === "chat") {
152
+ recovery.option_1 = "Use catalog(type='templates') to list available templates";
153
+ recovery.option_2 = "Try template(config='chat') for config structure";
154
+ }
155
+ else if (personaType === "dashboard") {
156
+ recovery.option_1 = "Use catalog(type='templates') to list available templates";
157
+ recovery.option_2 = "Try template(config='dashboard') for config structure";
158
+ }
159
+ else {
160
+ recovery.option_1 = "Use catalog(type='templates') to list available templates";
161
+ recovery.option_2 = "Specify explicit template ID with from='<template-id>'";
162
+ }
163
+ recovery.fallback = "Fetch ema://templates/voice-ai/complete for step-by-step recovery guide";
164
+ return {
165
+ error: `create_ai_employee failed`,
166
+ details: errorMsg,
167
+ environment: client.env?.name || "unknown",
168
+ recovery,
169
+ _tip: "If template creation fails, use gold templates from ema://templates/{type}/workflow-def as fallback.",
170
+ _warning: "NEVER clone a random existing persona. Use gold templates instead.",
171
+ };
172
+ }
138
173
  const newPersonaId = result.persona_id ?? result.id;
139
174
  if (!newPersonaId) {
140
175
  return { error: "Failed to create persona: no ID returned from API" };
@@ -145,21 +180,15 @@ export async function handleCreate(args, client, getTemplateId) {
145
180
  let workflowError;
146
181
  if (workflowDef && newPersonaId) {
147
182
  try {
148
- // Get the newly created persona to get its proto_config and existing workflow name
183
+ // Get the newly created persona to get its proto_config
149
184
  const newPersona = await client.getPersonaById(newPersonaId);
150
185
  const protoConfig = (newPersona?.proto_config ?? {});
151
- const existingWorkflow = (newPersona?.workflow_def ?? newPersona?.workflow);
152
- // CRITICAL: Copy the workflowName from the template-created workflow
153
- // The API rejects updates if the workflow name doesn't match
154
- const existingWfName = existingWorkflow?.workflowName;
155
- const workflowForDeploy = JSON.parse(JSON.stringify(workflowDef));
156
- if (existingWfName?.name) {
157
- // Set/overwrite the workflowName to match the existing one
158
- workflowForDeploy.workflowName = existingWfName;
159
- }
186
+ // NOTE: The SDK's updateAiEmployee() handles workflowName namespace automatically.
187
+ // It will copy from existing workflow if present, or generate a valid namespace if not.
188
+ // No need to manually manipulate workflowName here.
160
189
  await client.updateAiEmployee({
161
190
  persona_id: newPersonaId,
162
- workflow: workflowForDeploy,
191
+ workflow: workflowDef,
163
192
  proto_config: protoConfig, // Required: API needs proto_config alongside workflow
164
193
  });
165
194
  workflowApplied = true;
@@ -5,47 +5,49 @@
5
5
  * instead of a giant switch statement. Each mode is in its own file
6
6
  * for better testability and maintainability.
7
7
  *
8
- * All standard persona modes are now extracted:
9
- * - get: Fetch single persona
8
+ * Persona modes:
9
+ * - get: Fetch single persona (LLM then analyzes the data)
10
10
  * - list: List all personas
11
11
  * - templates: List available templates
12
- * - compare: Compare two personas
13
12
  * - sanitize: Sanitize persona data
14
13
  * - update: Update persona config
15
14
  * - delete: Delete persona (with confirmation)
16
15
  * - create/clone: Create new persona from template or clone existing
17
- * - analyze: Analyze persona for issues
18
16
  * - intent: Direct Intent Architect invocation for qualification
19
17
  *
20
- * Version management modes (version_*) remain in handlers-consolidated.ts.
18
+ * REMOVED (LLM does these):
19
+ * - analyze: LLM analyzes data from 'get'
20
+ * - compare: LLM compares data from two 'get' calls
21
+ *
22
+ * Version management modes extracted to version.ts:
23
+ * - snapshot/version_create, history/version_list, version_get
24
+ * - version_compare, restore/version_restore, version_policy
21
25
  */
22
26
  // Import mode handlers
23
27
  import { handleGet } from "./get.js";
24
28
  import { handleList } from "./list.js";
25
29
  import { handleTemplates } from "./templates.js";
26
- import { handleCompare } from "./compare.js";
27
30
  import { handleSanitize } from "./sanitize.js";
28
31
  import { handleUpdate } from "./update.js";
29
32
  import { handleDelete } from "./delete.js";
30
33
  import { handleCreate } from "./create.js";
31
- import { handleAnalyze } from "./analyze.js";
32
34
  import { handleIntent } from "./intent.js";
33
35
  /**
34
36
  * Dispatch table for persona modes
35
37
  *
36
38
  * Note: create/clone handlers require getTemplateId callback as extra param
39
+ *
40
+ * REMOVED analyze/compare - LLM does analysis/comparison
37
41
  */
38
42
  export const PERSONA_MODE_HANDLERS = {
39
43
  get: handleGet,
40
44
  list: handleList,
41
45
  templates: handleTemplates,
42
- compare: handleCompare,
43
46
  sanitize: handleSanitize,
44
47
  update: handleUpdate,
45
48
  delete: handleDelete,
46
49
  create: handleCreate,
47
50
  clone: handleCreate, // Clone uses same handler as create
48
- analyze: handleAnalyze,
49
51
  intent: handleIntent,
50
52
  };
51
53
  /**
@@ -64,10 +66,11 @@ export function getPersonaModeHandler(mode) {
64
66
  export { handleGet } from "./get.js";
65
67
  export { handleList } from "./list.js";
66
68
  export { handleTemplates } from "./templates.js";
67
- export { handleCompare } from "./compare.js";
68
69
  export { handleSanitize } from "./sanitize.js";
69
70
  export { handleUpdate } from "./update.js";
70
71
  export { handleDelete } from "./delete.js";
71
72
  export { handleCreate } from "./create.js";
72
- export { handleAnalyze } from "./analyze.js";
73
73
  export { handleIntent } from "./intent.js";
74
+ // REMOVED: handleCompare, handleAnalyze - LLM does analysis/comparison
75
+ // Version management
76
+ export { handleVersion, isVersionMode } from "./version.js";
@@ -174,7 +174,8 @@ function applySpecToWorkflow(spec, existingWorkflow) {
174
174
  * Handle persona(mode="update") - update persona config or workflow
175
175
  *
176
176
  * @param args.id - Persona ID or name (required)
177
- * @param args.proto_config - Proto config changes (smart merged with widgets)
177
+ * @param args.config - Config changes with widgets (preferred, smart merged)
178
+ * @param args.proto_config - Alias for config (internal, for backwards compatibility)
178
179
  * @param args.workflow - Optional raw workflow_def to set
179
180
  * @param args.workflow_spec - Optional WorkflowSpec (agent-built) - will be compiled
180
181
  * @param args.preview - If true, return changes without deploying (default: false)
@@ -201,8 +202,9 @@ export async function handleUpdate(args, client) {
201
202
  const projectSettings = existingProtoConfig.projectSettings;
202
203
  const isVoice = projectSettings?.projectType === 5;
203
204
  // Build merged proto_config with smart widget-level merging
205
+ // Accept both 'config' (tool schema) and 'proto_config' (internal) for compatibility
204
206
  let mergedProtoConfig = { ...existingProtoConfig };
205
- const newProtoConfig = args.proto_config;
207
+ const newProtoConfig = (args.config ?? args.proto_config);
206
208
  const updatedWidgets = [];
207
209
  if (newProtoConfig) {
208
210
  // If proto_config has widgets, do smart widget-level merge