@ema.co/mcp-toolkit 2026.1.26 → 2026.1.27-10

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 (45) hide show
  1. package/dist/mcp/handlers/action/index.js +17 -20
  2. package/dist/mcp/handlers/data/index.js +75 -6
  3. package/dist/mcp/handlers/deprecation.js +50 -0
  4. package/dist/mcp/handlers/env/index.js +3 -3
  5. package/dist/mcp/handlers/knowledge/index.js +44 -237
  6. package/dist/mcp/handlers/persona/create.js +63 -18
  7. package/dist/mcp/handlers/persona/index.js +9 -10
  8. package/dist/mcp/handlers/persona/update.js +6 -3
  9. package/dist/mcp/handlers/reference/index.js +15 -2
  10. package/dist/mcp/handlers/sync/index.js +3 -18
  11. package/dist/mcp/handlers/workflow/analyze.js +53 -105
  12. package/dist/mcp/handlers/workflow/deploy.js +160 -1
  13. package/dist/mcp/handlers/workflow/generate.js +8 -28
  14. package/dist/mcp/handlers/workflow/index.js +258 -85
  15. package/dist/mcp/handlers/workflow/modify.js +69 -26
  16. package/dist/mcp/handlers/workflow/optimize.js +22 -108
  17. package/dist/mcp/handlers/workflow/utils.js +0 -102
  18. package/dist/mcp/handlers-consolidated.js +15 -38
  19. package/dist/mcp/prompts.js +82 -44
  20. package/dist/mcp/resources.js +335 -3
  21. package/dist/mcp/server.js +242 -457
  22. package/dist/mcp/tools.js +44 -61
  23. package/dist/sdk/action-schema-parser.js +11 -5
  24. package/dist/sdk/client.js +81 -25
  25. package/dist/sdk/ema-client.js +11 -0
  26. package/dist/sdk/generated/deprecated-actions.js +171 -0
  27. package/dist/sdk/guidance.js +58 -35
  28. package/dist/sdk/index.js +8 -7
  29. package/dist/sdk/knowledge.js +216 -1932
  30. package/dist/sdk/quality-gates.js +60 -336
  31. package/dist/sdk/validation-rules.js +33 -0
  32. package/dist/sdk/workflow-fixer.js +29 -360
  33. package/dist/sdk/workflow-generator.js +2 -1
  34. package/dist/sdk/workflow-intent.js +43 -3
  35. package/dist/sdk/workflow-transformer.js +0 -342
  36. package/docs/dashboard-operations.md +35 -0
  37. package/docs/ema-user-guide.md +66 -0
  38. package/docs/mcp-tools-guide.md +74 -45
  39. package/package.json +2 -2
  40. package/dist/mcp/handlers/persona/analyze.js +0 -275
  41. package/dist/mcp/handlers/persona/compare.js +0 -32
  42. package/dist/mcp/handlers/workflow/compile.js +0 -39
  43. package/docs/DEBUG-ANALYSIS-unused-category-type-mismatch.md +0 -481
  44. package/docs/TODO-fix-analyzer-and-modify.md +0 -182
  45. package/resources/action-schema.json +0 -5678
@@ -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;
@@ -192,6 +221,7 @@ export async function handleCreate(args, client, getTemplateId) {
192
221
  sourcePersonaType,
193
222
  dashboardCloneResult,
194
223
  actionsError: validation.errors.join("; "),
224
+ createdFromTemplate: fromType === "template",
195
225
  });
196
226
  }
197
227
  // Build execution context
@@ -214,6 +244,7 @@ export async function handleCreate(args, client, getTemplateId) {
214
244
  sourcePersonaType,
215
245
  // Don't include dashboardCloneResult - actions handle data operations
216
246
  actionsResult,
247
+ createdFromTemplate: fromType === "template",
217
248
  });
218
249
  }
219
250
  // ═══════════════════════════════════════════════════════════════════════════
@@ -235,6 +266,7 @@ export async function handleCreate(args, client, getTemplateId) {
235
266
  workflowError,
236
267
  sourcePersonaType,
237
268
  dashboardCloneResult,
269
+ createdFromTemplate: fromType === "template",
238
270
  });
239
271
  }
240
272
  return buildCreateResult({
@@ -245,6 +277,7 @@ export async function handleCreate(args, client, getTemplateId) {
245
277
  workflowError,
246
278
  sourcePersonaType,
247
279
  dashboardCloneResult,
280
+ createdFromTemplate: fromType === "template",
248
281
  });
249
282
  }
250
283
  /**
@@ -270,6 +303,18 @@ function buildCreateResult(opts) {
270
303
  if (opts.sourcePersonaType) {
271
304
  result.source_persona_type = opts.sourcePersonaType;
272
305
  }
306
+ // ── CRITICAL GUIDANCE: Template workflows are minimal starters ──
307
+ // This is where LLMs often go wrong - they create from template and think they're done
308
+ if (opts.createdFromTemplate && !opts.workflowApplied) {
309
+ result._warning = "PERSONA CREATED BUT WORKFLOW IS INCOMPLETE. Template workflows are minimal starters (just trigger→respond).";
310
+ result._required_next_steps = [
311
+ "1. BUILD WORKFLOW: Add intent categorization, search nodes, response handling",
312
+ `2. If uploading docs: Workflow MUST have search/v2 node or documents will NOT be used`,
313
+ `3. Get current workflow: workflow(mode='get', persona_id='${opts.newPersonaId}')`,
314
+ `4. Deploy complete workflow: workflow(mode='deploy', persona_id='${opts.newPersonaId}', workflow_def={...})`,
315
+ ];
316
+ result._common_mistake = "Creating from template, uploading docs, and declaring 'done' WITHOUT building the workflow. The deploy will now BLOCK this pattern.";
317
+ }
273
318
  if (opts.dashboardCloneResult) {
274
319
  result.dashboard_data_clone = opts.dashboardCloneResult;
275
320
  }
@@ -5,18 +5,20 @@
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
  *
18
+ * REMOVED (LLM does these):
19
+ * - analyze: LLM analyzes data from 'get'
20
+ * - compare: LLM compares data from two 'get' calls
21
+ *
20
22
  * Version management modes extracted to version.ts:
21
23
  * - snapshot/version_create, history/version_list, version_get
22
24
  * - version_compare, restore/version_restore, version_policy
@@ -25,29 +27,27 @@
25
27
  import { handleGet } from "./get.js";
26
28
  import { handleList } from "./list.js";
27
29
  import { handleTemplates } from "./templates.js";
28
- import { handleCompare } from "./compare.js";
29
30
  import { handleSanitize } from "./sanitize.js";
30
31
  import { handleUpdate } from "./update.js";
31
32
  import { handleDelete } from "./delete.js";
32
33
  import { handleCreate } from "./create.js";
33
- import { handleAnalyze } from "./analyze.js";
34
34
  import { handleIntent } from "./intent.js";
35
35
  /**
36
36
  * Dispatch table for persona modes
37
37
  *
38
38
  * Note: create/clone handlers require getTemplateId callback as extra param
39
+ *
40
+ * REMOVED analyze/compare - LLM does analysis/comparison
39
41
  */
40
42
  export const PERSONA_MODE_HANDLERS = {
41
43
  get: handleGet,
42
44
  list: handleList,
43
45
  templates: handleTemplates,
44
- compare: handleCompare,
45
46
  sanitize: handleSanitize,
46
47
  update: handleUpdate,
47
48
  delete: handleDelete,
48
49
  create: handleCreate,
49
50
  clone: handleCreate, // Clone uses same handler as create
50
- analyze: handleAnalyze,
51
51
  intent: handleIntent,
52
52
  };
53
53
  /**
@@ -66,12 +66,11 @@ export function getPersonaModeHandler(mode) {
66
66
  export { handleGet } from "./get.js";
67
67
  export { handleList } from "./list.js";
68
68
  export { handleTemplates } from "./templates.js";
69
- export { handleCompare } from "./compare.js";
70
69
  export { handleSanitize } from "./sanitize.js";
71
70
  export { handleUpdate } from "./update.js";
72
71
  export { handleDelete } from "./delete.js";
73
72
  export { handleCreate } from "./create.js";
74
- export { handleAnalyze } from "./analyze.js";
75
73
  export { handleIntent } from "./intent.js";
74
+ // REMOVED: handleCompare, handleAnalyze - LLM does analysis/comparison
76
75
  // Version management
77
76
  export { handleVersion, isVersionMode } from "./version.js";
@@ -12,6 +12,7 @@
12
12
  * routes to handleWorkflow BEFORE reaching this handler. See handlePersona()
13
13
  * in handlers-consolidated.ts.
14
14
  */
15
+ import { PROJECT_TYPES } from "../../../sdk/knowledge.js";
15
16
  import { resolvePersona, validateWidgetsForApi } from "../utils.js";
16
17
  /**
17
18
  * Apply WorkflowSpec changes to an existing workflow_def.
@@ -174,7 +175,8 @@ function applySpecToWorkflow(spec, existingWorkflow) {
174
175
  * Handle persona(mode="update") - update persona config or workflow
175
176
  *
176
177
  * @param args.id - Persona ID or name (required)
177
- * @param args.proto_config - Proto config changes (smart merged with widgets)
178
+ * @param args.config - Config changes with widgets (preferred, smart merged)
179
+ * @param args.proto_config - Alias for config (internal, for backwards compatibility)
178
180
  * @param args.workflow - Optional raw workflow_def to set
179
181
  * @param args.workflow_spec - Optional WorkflowSpec (agent-built) - will be compiled
180
182
  * @param args.preview - If true, return changes without deploying (default: false)
@@ -199,10 +201,11 @@ export async function handleUpdate(args, client) {
199
201
  : undefined;
200
202
  // Determine persona type from projectSettings.projectType
201
203
  const projectSettings = existingProtoConfig.projectSettings;
202
- const isVoice = projectSettings?.projectType === 5;
204
+ const isVoice = projectSettings?.projectType === PROJECT_TYPES.voice;
203
205
  // Build merged proto_config with smart widget-level merging
206
+ // Accept both 'config' (tool schema) and 'proto_config' (internal) for compatibility
204
207
  let mergedProtoConfig = { ...existingProtoConfig };
205
- const newProtoConfig = args.proto_config;
208
+ const newProtoConfig = (args.config ?? args.proto_config);
206
209
  const updatedWidgets = [];
207
210
  if (newProtoConfig) {
208
211
  // If proto_config has widgets, do smart widget-level merge
@@ -34,10 +34,22 @@ export async function handleReference(args, context) {
34
34
  if (type === "actions") {
35
35
  const client = context?.client;
36
36
  const id = args.id;
37
- // Categories list
37
+ // Categories list - API-first with catalog fallback
38
38
  if (args.categories) {
39
+ if (client) {
40
+ try {
41
+ const actions = await client.listActions();
42
+ const apiCategories = [...new Set(actions.map(a => a.category).filter(Boolean))];
43
+ if (apiCategories.length > 0) {
44
+ return { categories: apiCategories, count: apiCategories.length, source: "api" };
45
+ }
46
+ }
47
+ catch {
48
+ // Fallback to catalog
49
+ }
50
+ }
39
51
  const categories = [...new Set(Object.values(AGENT_CATALOG).map(a => a.category))];
40
- return { categories, count: categories.length };
52
+ return { categories, count: categories.length, source: "catalog" };
41
53
  }
42
54
  // Suggest for use case
43
55
  if (args.suggest) {
@@ -114,6 +126,7 @@ export async function handleReference(args, context) {
114
126
  category: a.category,
115
127
  enabled: a.enabled,
116
128
  })),
129
+ source: "api",
117
130
  };
118
131
  }
119
132
  catch {
@@ -4,28 +4,13 @@
4
4
  * Handles cross-environment synchronization of personas.
5
5
  */
6
6
  import { resolvePersona } from "../utils.js";
7
- // Deprecated param mappings for backwards compatibility
8
- const DEPRECATED_PARAMS = {
9
- identifier: { newName: "id", message: "'identifier' is deprecated, use 'id' instead (will be removed in v2.0.0)" },
10
- };
11
- function checkDeprecatedParams(args) {
12
- const warnings = [];
13
- for (const [oldName, info] of Object.entries(DEPRECATED_PARAMS)) {
14
- if (args[oldName] !== undefined) {
15
- warnings.push(info.message);
16
- }
17
- }
18
- return warnings;
19
- }
7
+ import { handleDeprecatedParams } from "../deprecation.js";
20
8
  /**
21
9
  * Handle sync tool requests - sync personas across environments
22
10
  */
23
11
  export async function handleSync(args, createClient, getSyncOptions) {
24
- // Check for deprecated params and log warnings
25
- const deprecationWarnings = checkDeprecatedParams(args);
26
- for (const warning of deprecationWarnings) {
27
- console.warn(`[sync] Deprecation: ${warning}`);
28
- }
12
+ // Check for deprecated params
13
+ handleDeprecatedParams(args, "sync");
29
14
  const mode = args.mode || "run";
30
15
  const id = args.id;
31
16
  const identifier = args.identifier; // deprecated alias for 'id'
@@ -1,20 +1,21 @@
1
1
  /**
2
2
  * Workflow Analyze Handler
3
3
  *
4
- * Analyzes workflow structure, detects issues, and provides metrics.
4
+ * Returns workflow DATA for LLM to analyze using rules from ema://rules/*.
5
5
  *
6
- * Features:
7
- * - Issue detection (critical, warning, info)
8
- * - Connection validation
9
- * - Metrics calculation
10
- * - Execution flow analysis (loops, dead code, multiple responders)
11
- * - Auto-fix support
6
+ * IMPORTANT: This handler does NOT pre-compute issues or fixes.
7
+ * The LLM applies ANTI_PATTERNS, INPUT_SOURCE_RULES, and OPTIMIZATION_RULES
8
+ * to reason about the workflow.
9
+ *
10
+ * Returns:
11
+ * - workflow_def: The raw workflow structure
12
+ * - connections: Type information for each edge
13
+ * - metrics: Basic counts (nodes, edges, etc.)
14
+ * - rules_hint: Pointer to ema://rules/* for analysis guidance
12
15
  */
13
- import { detectWorkflowIssues, suggestWorkflowFixes, validateWorkflowConnections } from "../../../sdk/knowledge.js";
14
- import { autoFixWorkflow } from "../../../sdk/workflow-fixer.js";
15
- import { analyzeExecutionFlow, generateASCIIFlow } from "../../../sdk/workflow-execution-analyzer.js";
16
+ import { validateWorkflowConnections, parseWorkflowDef } from "../../../sdk/knowledge.js";
16
17
  /**
17
- * Calculate workflow metrics
18
+ * Calculate workflow metrics (pure data, no analysis)
18
19
  */
19
20
  function calculateMetrics(workflow) {
20
21
  const actions = workflow.actions;
@@ -36,15 +37,24 @@ function calculateMetrics(workflow) {
36
37
  // Get trigger type
37
38
  const trigger = workflow.trigger;
38
39
  const triggerType = trigger?.trigger_type;
40
+ // Check for categorizer
41
+ const hasCategorizer = actions.some(a => {
42
+ const name = a.name?.toLowerCase() ?? "";
43
+ const action = a.action?.name?.name;
44
+ return name.includes("categorizer") || action?.includes("categorizer");
45
+ });
39
46
  return {
40
47
  node_count: actions.length,
41
48
  connection_count: connectionCount,
42
49
  has_hitl: hasHitl,
50
+ has_categorizer: hasCategorizer,
43
51
  trigger_type: triggerType,
44
52
  };
45
53
  }
46
54
  /**
47
55
  * Handle workflow analyze mode
56
+ *
57
+ * Returns DATA for LLM to analyze - does NOT pre-compute issues.
48
58
  */
49
59
  export async function handleWorkflowAnalyze(args, client) {
50
60
  const personaId = args.persona_id;
@@ -62,110 +72,48 @@ export async function handleWorkflowAnalyze(args, client) {
62
72
  if (!workflow) {
63
73
  return { error: "No workflow to analyze. Provide workflow_def or persona_id." };
64
74
  }
65
- // Determine what to include
66
- const include = args.include || ["issues", "connections", "fixes", "metrics"];
75
+ // Parse workflow to simpler structure
76
+ const nodes = parseWorkflowDef(workflow);
77
+ // Get connection type information
78
+ const connections = validateWorkflowConnections(workflow);
79
+ // Calculate metrics
80
+ const metrics = calculateMetrics(workflow);
67
81
  const result = {
68
82
  mode: "analyze",
69
83
  persona_id: personaId,
70
84
  persona_name: persona?.name,
71
- environment: "demo",
85
+ // RAW DATA for LLM to analyze
86
+ workflow_def: workflow,
87
+ node_count: nodes.length,
88
+ nodes: nodes.map(n => ({
89
+ id: n.id,
90
+ action: n.action_name,
91
+ display_name: n.display_name,
92
+ inputs: n.incoming_edges?.map(e => `${e.source_node_id}.${e.source_output}`),
93
+ })),
94
+ // Type information for connections
95
+ connections: connections.map(c => ({
96
+ edge: c.edge_id,
97
+ source_type: c.source_type,
98
+ target_type: c.target_type,
99
+ compatible: c.compatible,
100
+ note: c.note,
101
+ })),
102
+ // Basic metrics
103
+ metrics,
104
+ // Tell LLM where to find analysis rules
105
+ _next_steps: [
106
+ "1. Fetch ema://rules/anti-patterns",
107
+ "2. Fetch ema://rules/structural-invariants",
108
+ "3. Check each node against the rules",
109
+ "4. Report issues YOU find (MCP does not pre-compute)",
110
+ ],
72
111
  };
73
- // Issues and fixes
74
- if (include.includes("issues") || include.includes("fixes")) {
75
- const issues = detectWorkflowIssues(workflow);
76
- if (include.includes("issues")) {
77
- result.issues = issues;
78
- }
79
- if (include.includes("fixes")) {
80
- result.fixes = suggestWorkflowFixes(issues);
81
- }
82
- result.issue_summary = {
83
- total: issues.length,
84
- critical: issues.filter((i) => i.severity === "critical").length,
85
- warning: issues.filter((i) => i.severity === "warning").length,
86
- info: issues.filter((i) => i.severity === "info").length,
87
- };
88
- result.validation_passed = issues.filter((i) => i.severity === "critical").length === 0;
89
- }
90
- // Connection validation
91
- if (include.includes("connections")) {
92
- result.connections = validateWorkflowConnections(workflow);
93
- }
94
- // Metrics
95
- if (include.includes("metrics")) {
96
- result.metrics = calculateMetrics(workflow);
97
- }
98
- // Execution flow analysis
99
- if (include.includes("execution_flow")) {
100
- const execAnalysis = analyzeExecutionFlow(workflow);
101
- result.execution_flow = {
102
- summary: execAnalysis.summary,
103
- loops: execAnalysis.loops,
104
- multiple_responder_issues: execAnalysis.multipleResponderIssues,
105
- redundant_classifiers: execAnalysis.redundantClassifiers,
106
- data_flow_issues: execAnalysis.dataFlowIssues,
107
- dead_code_paths: execAnalysis.deadCodePaths,
108
- };
109
- // Include ASCII visualization if requested
110
- if (args.visualize) {
111
- result.execution_flow_ascii = generateASCIIFlow(execAnalysis);
112
- }
113
- // Add specific warnings for triple response risk
114
- if (execAnalysis.summary.mayRepeatResponses) {
115
- result.triple_response_warning = "⚠️ This workflow may cause duplicate/triple responses due to ungated parallel responders";
116
- }
117
- }
118
112
  if (persona) {
119
113
  result.persona = {
120
114
  id: persona.id,
121
115
  name: persona.name
122
116
  };
123
117
  }
124
- // Calculate optimization guidance
125
- const issues = result.issues;
126
- const criticalCount = issues?.filter(i => i.severity === "critical").length ?? 0;
127
- const warningCount = issues?.filter(i => i.severity === "warning").length ?? 0;
128
- // AUTO-FIX: If fix=true, apply fixes and deploy
129
- if (args.fix && personaId && persona && workflow) {
130
- const fixResult = autoFixWorkflow(workflow);
131
- result.fixes_applied = fixResult.fixesApplied;
132
- result.fixes_warnings = fixResult.warnings;
133
- if (fixResult.fixesApplied.length > 0 && fixResult.success) {
134
- // Deploy the fixed workflow
135
- try {
136
- const fixedWorkflow = fixResult.workflowDef;
137
- await client.updateAiEmployee({
138
- persona_id: personaId,
139
- workflow: fixedWorkflow,
140
- proto_config: persona.proto_config,
141
- });
142
- result.fix_status = "deployed";
143
- result.fixes_deployed = fixResult.fixesApplied.length;
144
- result.status = `✅ Applied ${fixResult.fixesApplied.length} fixes and deployed`;
145
- }
146
- catch (deployErr) {
147
- result.fix_status = "failed";
148
- result.fix_error = deployErr instanceof Error ? deployErr.message : String(deployErr);
149
- result.status = `⚠️ Fixes applied but deploy failed: ${result.fix_error}`;
150
- }
151
- }
152
- else if (fixResult.fixesApplied.length === 0) {
153
- result.fix_status = "no_fixes_needed";
154
- result.status = "✅ No auto-fixable issues found";
155
- }
156
- else {
157
- result.fix_status = "failed";
158
- result.status = "⚠️ Fix generation failed";
159
- }
160
- return result;
161
- }
162
- // Provide optimization suggestion
163
- if (criticalCount > 0 || warningCount > 0) {
164
- result.optimization_suggestion = `This workflow has ${criticalCount} critical and ${warningCount} warning issues. ` +
165
- `Use workflow(persona_id="${personaId ?? 'ID'}", optimize=true) to auto-fix.`;
166
- }
167
- else {
168
- result.status = "✅ Workflow is healthy - no issues detected";
169
- }
170
118
  return result;
171
119
  }