@ema.co/mcp-toolkit 2026.1.27 → 2026.1.28-2
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/mcp/handlers/data/index.js +3 -0
- package/dist/mcp/handlers/persona/create.js +16 -0
- package/dist/mcp/handlers/persona/list.js +9 -4
- package/dist/mcp/handlers/persona/update.js +24 -2
- package/dist/mcp/handlers/workflow/deploy.js +20 -2
- package/dist/mcp/handlers/workflow/generate.js +39 -2
- package/dist/mcp/handlers/workflow/index.js +8 -3
- package/dist/mcp/handlers/workflow/modify.js +34 -7
- package/dist/mcp/handlers/workflow/validate.js +85 -0
- package/dist/mcp/handlers/workflow/validation.js +160 -0
- package/dist/mcp/resources.js +286 -4
- package/dist/mcp/server.js +16 -3
- package/dist/mcp/tools.js +32 -11
- package/dist/sdk/client.js +36 -9
- package/dist/sdk/ema-client.js +32 -4
- package/dist/sdk/index.js +3 -1
- package/dist/sdk/knowledge.js +5 -5
- package/dist/sdk/structural-rules.js +498 -0
- package/dist/sdk/workflow-generator.js +2 -1
- package/dist/sdk/workflow-intent.js +28 -96
- package/dist/sdk/workflow-path-enumerator.js +278 -0
- package/dist/sdk/workflow-static-validator.js +291 -0
- package/dist/sdk/workflow-validation-types.js +7 -0
- package/docs/README.md +14 -0
- package/docs/go-validator-analysis.md +323 -0
- package/docs/rule-format-specification.md +346 -0
- package/docs/validation-contract.md +397 -0
- package/docs/validation-error-format.md +326 -0
- package/package.json +1 -1
- package/dist/mcp/workflow-operations.js +0 -100
- package/dist/sdk/workflow-fixer.js +0 -48
- package/docs/dashboard-operations.md +0 -281
- package/docs/ema-user-guide.md +0 -1201
- package/docs/email-patterns.md +0 -120
- package/docs/mcp-tools-guide.md +0 -575
package/dist/mcp/resources.js
CHANGED
|
@@ -100,9 +100,9 @@ function hasPathTraversal(uriPath) {
|
|
|
100
100
|
// Only includes files that ACTUALLY exist in this repo (not symlinked)
|
|
101
101
|
// and are useful for MCP consumers
|
|
102
102
|
const RESOURCE_MAP = {
|
|
103
|
-
// Core Documentation (
|
|
103
|
+
// Core Documentation (public guides)
|
|
104
104
|
"ema://docs/getting-started": {
|
|
105
|
-
path: "
|
|
105
|
+
path: ".context/public/guides/mcp-tools-guide.md",
|
|
106
106
|
description: "Quick start guide: first steps, key concepts, essential rules, common operations",
|
|
107
107
|
mimeType: "text/markdown",
|
|
108
108
|
},
|
|
@@ -112,12 +112,12 @@ const RESOURCE_MAP = {
|
|
|
112
112
|
mimeType: "text/markdown",
|
|
113
113
|
},
|
|
114
114
|
"ema://docs/ema-user-guide": {
|
|
115
|
-
path: "
|
|
115
|
+
path: ".context/public/guides/ema-user-guide.md",
|
|
116
116
|
description: "Ema Platform guide: concepts (AI Employees, Workflows, Agents), glossary, architecture, examples, troubleshooting",
|
|
117
117
|
mimeType: "text/markdown",
|
|
118
118
|
},
|
|
119
119
|
"ema://docs/mcp-tools-guide": {
|
|
120
|
-
path: "
|
|
120
|
+
path: ".context/public/guides/mcp-tools-guide.md",
|
|
121
121
|
description: "MCP tools usage guide: tool categories, best practices, example call sequences, workflow review patterns",
|
|
122
122
|
mimeType: "text/markdown",
|
|
123
123
|
},
|
|
@@ -216,6 +216,13 @@ const DYNAMIC_RESOURCES = [
|
|
|
216
216
|
mimeType: "application/json",
|
|
217
217
|
generate: async () => JSON.stringify(STRUCTURAL_INVARIANTS, null, 2),
|
|
218
218
|
},
|
|
219
|
+
{
|
|
220
|
+
uri: "ema://validation/rules",
|
|
221
|
+
name: "validation/rules",
|
|
222
|
+
description: "Static validation rules: path enumeration, required outputs, multiple writers, response/abstain, category hierarchy. Use these rules DURING workflow generation to avoid errors.",
|
|
223
|
+
mimeType: "text/markdown",
|
|
224
|
+
generate: async () => generateValidationRulesForLLM(),
|
|
225
|
+
},
|
|
219
226
|
// Deprecated Actions - API-first with fallback
|
|
220
227
|
{
|
|
221
228
|
uri: "ema://rules/deprecated-actions",
|
|
@@ -1140,6 +1147,281 @@ To read a resource, use the \`resources/read\` endpoint:
|
|
|
1140
1147
|
};
|
|
1141
1148
|
}
|
|
1142
1149
|
}
|
|
1150
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1151
|
+
// Validation Rules Generator
|
|
1152
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1153
|
+
/**
|
|
1154
|
+
* Generate validation rules in LLM-friendly Markdown format.
|
|
1155
|
+
* Includes rules from Go validator: required outputs, multiple writers, response/abstain, category hierarchy.
|
|
1156
|
+
*/
|
|
1157
|
+
function generateValidationRulesForLLM() {
|
|
1158
|
+
return `# Workflow Validation Rules
|
|
1159
|
+
|
|
1160
|
+
> **Use these rules DURING workflow generation to avoid errors proactively.**
|
|
1161
|
+
|
|
1162
|
+
These rules are extracted from the Go validator's static validation logic. Follow them when generating or modifying workflows.
|
|
1163
|
+
|
|
1164
|
+
## Rule 1: Required Output on All Paths
|
|
1165
|
+
|
|
1166
|
+
**Rule ID**: \`required_output_all_paths\`
|
|
1167
|
+
**Severity**: Critical
|
|
1168
|
+
**Applies To**: Named results, execution paths
|
|
1169
|
+
|
|
1170
|
+
### Logic
|
|
1171
|
+
|
|
1172
|
+
**Algorithm**: Enumerate all execution paths. For each path, check if all named results are produced.
|
|
1173
|
+
|
|
1174
|
+
**Steps**:
|
|
1175
|
+
1. Enumerate all paths from trigger to completion
|
|
1176
|
+
2. For each path, collect all outputs produced
|
|
1177
|
+
3. For each named result, check if it's in path outputs
|
|
1178
|
+
4. If missing on any path → error
|
|
1179
|
+
|
|
1180
|
+
**Complexity**: O(P * R) where P = paths, R = named results
|
|
1181
|
+
|
|
1182
|
+
### Rules
|
|
1183
|
+
|
|
1184
|
+
**Constraint**: Every execution path must produce all named results.
|
|
1185
|
+
|
|
1186
|
+
**Why**: Named results are contract guarantees. If a path doesn't produce a required output, the contract is violated.
|
|
1187
|
+
|
|
1188
|
+
### Examples
|
|
1189
|
+
|
|
1190
|
+
**Bad Pattern**:
|
|
1191
|
+
\`\`\`json
|
|
1192
|
+
{
|
|
1193
|
+
"path": ["trigger", "categorizer", "billing_branch", "search"],
|
|
1194
|
+
"named_result": "response_with_sources",
|
|
1195
|
+
"produces": [] // Missing response
|
|
1196
|
+
}
|
|
1197
|
+
\`\`\`
|
|
1198
|
+
|
|
1199
|
+
**Why Bad**: Path ends without producing required output.
|
|
1200
|
+
|
|
1201
|
+
**Good Pattern**:
|
|
1202
|
+
\`\`\`json
|
|
1203
|
+
{
|
|
1204
|
+
"path": ["trigger", "categorizer", "billing_branch", "search", "respond_with_sources"],
|
|
1205
|
+
"named_result": "response_with_sources",
|
|
1206
|
+
"produces": ["response_with_sources"] // ✅ Produced
|
|
1207
|
+
}
|
|
1208
|
+
\`\`\`
|
|
1209
|
+
|
|
1210
|
+
**Why Good**: All paths produce required output.
|
|
1211
|
+
|
|
1212
|
+
### Remediation
|
|
1213
|
+
|
|
1214
|
+
**How to Fix**:
|
|
1215
|
+
1. Identify the path missing the output
|
|
1216
|
+
2. Add a node that produces the required output
|
|
1217
|
+
3. Connect the node's output to WORKFLOW_OUTPUT
|
|
1218
|
+
|
|
1219
|
+
**Common Fixes**:
|
|
1220
|
+
- Add \`respond_with_sources\` node after search
|
|
1221
|
+
- Add \`call_llm\` node for text generation
|
|
1222
|
+
- Add \`fixed_response\` for static responses
|
|
1223
|
+
|
|
1224
|
+
**Prevention** (During Generation):
|
|
1225
|
+
- Always ensure every categorizer branch has a response node
|
|
1226
|
+
- Check that all paths end with nodes connected to WORKFLOW_OUTPUT
|
|
1227
|
+
- Verify resultMappings include all required outputs
|
|
1228
|
+
|
|
1229
|
+
---
|
|
1230
|
+
|
|
1231
|
+
## Rule 2: Single Writer Per Output
|
|
1232
|
+
|
|
1233
|
+
**Rule ID**: \`single_writer_per_output\`
|
|
1234
|
+
**Severity**: Critical
|
|
1235
|
+
**Applies To**: Named results, execution paths
|
|
1236
|
+
|
|
1237
|
+
### Logic
|
|
1238
|
+
|
|
1239
|
+
**Algorithm**: For each named result, count how many nodes produce it on each path.
|
|
1240
|
+
|
|
1241
|
+
**Steps**:
|
|
1242
|
+
1. For each named result
|
|
1243
|
+
2. For each execution path
|
|
1244
|
+
3. Count nodes that produce this result
|
|
1245
|
+
4. If count > 1 on any path → error
|
|
1246
|
+
|
|
1247
|
+
### Rules
|
|
1248
|
+
|
|
1249
|
+
**Constraint**: A single named result can only have one producer per execution path.
|
|
1250
|
+
|
|
1251
|
+
**Why**: Multiple producers on the same path cause ambiguity about which value to use.
|
|
1252
|
+
|
|
1253
|
+
### Examples
|
|
1254
|
+
|
|
1255
|
+
**Bad Pattern**:
|
|
1256
|
+
\`\`\`json
|
|
1257
|
+
{
|
|
1258
|
+
"path": ["trigger", "categorizer", "billing"],
|
|
1259
|
+
"named_result": "response_with_sources",
|
|
1260
|
+
"producers": ["respond_billing", "respond_general"] // ❌ Two producers
|
|
1261
|
+
}
|
|
1262
|
+
\`\`\`
|
|
1263
|
+
|
|
1264
|
+
**Why Bad**: Two nodes produce the same output on the same path.
|
|
1265
|
+
|
|
1266
|
+
**Good Pattern**:
|
|
1267
|
+
\`\`\`json
|
|
1268
|
+
{
|
|
1269
|
+
"path": ["trigger", "categorizer", "billing"],
|
|
1270
|
+
"named_result": "response_with_sources",
|
|
1271
|
+
"producers": ["respond_billing"] // ✅ Single producer
|
|
1272
|
+
}
|
|
1273
|
+
\`\`\`
|
|
1274
|
+
|
|
1275
|
+
**Why Good**: Only one node produces the output.
|
|
1276
|
+
|
|
1277
|
+
### Remediation
|
|
1278
|
+
|
|
1279
|
+
**How to Fix**:
|
|
1280
|
+
1. Identify which nodes produce the same output
|
|
1281
|
+
2. Remove one producer or use different named results
|
|
1282
|
+
3. Ensure only one node produces each named result per path
|
|
1283
|
+
|
|
1284
|
+
**Prevention** (During Generation):
|
|
1285
|
+
- Use mutually exclusive runIf conditions to gate responders
|
|
1286
|
+
- Ensure only one response node executes per category branch
|
|
1287
|
+
|
|
1288
|
+
---
|
|
1289
|
+
|
|
1290
|
+
## Rule 3: Response or Abstain Required
|
|
1291
|
+
|
|
1292
|
+
**Rule ID**: \`response_or_abstain_required\`
|
|
1293
|
+
**Severity**: Critical
|
|
1294
|
+
**Applies To**: Execution paths
|
|
1295
|
+
|
|
1296
|
+
### Logic
|
|
1297
|
+
|
|
1298
|
+
**Algorithm**: For each execution path, check if it produces a response or has an abstain reason.
|
|
1299
|
+
|
|
1300
|
+
**Steps**:
|
|
1301
|
+
1. For each completed path
|
|
1302
|
+
2. Check if path produces any response output
|
|
1303
|
+
3. Check if path has abstain reason
|
|
1304
|
+
4. If neither → error
|
|
1305
|
+
|
|
1306
|
+
### Rules
|
|
1307
|
+
|
|
1308
|
+
**Constraint**: Every execution path must either produce a user-visible response or explicitly abstain with a reason.
|
|
1309
|
+
|
|
1310
|
+
**Why**: Users expect responses. Paths that don't respond and don't explain why create poor UX.
|
|
1311
|
+
|
|
1312
|
+
### Examples
|
|
1313
|
+
|
|
1314
|
+
**Bad Pattern**:
|
|
1315
|
+
\`\`\`json
|
|
1316
|
+
{
|
|
1317
|
+
"path": ["trigger", "categorizer", "billing", "search"],
|
|
1318
|
+
"has_response": false,
|
|
1319
|
+
"has_abstain": false // ❌ Neither
|
|
1320
|
+
}
|
|
1321
|
+
\`\`\`
|
|
1322
|
+
|
|
1323
|
+
**Why Bad**: Path ends without response or abstain reason.
|
|
1324
|
+
|
|
1325
|
+
**Good Pattern**:
|
|
1326
|
+
\`\`\`json
|
|
1327
|
+
{
|
|
1328
|
+
"path": ["trigger", "categorizer", "billing", "search", "respond_with_sources"],
|
|
1329
|
+
"has_response": true // ✅ Has response
|
|
1330
|
+
}
|
|
1331
|
+
\`\`\`
|
|
1332
|
+
|
|
1333
|
+
**Why Good**: Path produces a response.
|
|
1334
|
+
|
|
1335
|
+
### Remediation
|
|
1336
|
+
|
|
1337
|
+
**How to Fix**:
|
|
1338
|
+
1. Add a response node to the path, OR
|
|
1339
|
+
2. Add an abstain reason to the workflow configuration
|
|
1340
|
+
|
|
1341
|
+
**Prevention** (During Generation):
|
|
1342
|
+
- Always add response nodes to every categorizer branch
|
|
1343
|
+
- If a path shouldn't respond, document why with abstain reason
|
|
1344
|
+
|
|
1345
|
+
---
|
|
1346
|
+
|
|
1347
|
+
## Rule 4: Category Hierarchy Valid
|
|
1348
|
+
|
|
1349
|
+
**Rule ID**: \`category_hierarchy_valid\`
|
|
1350
|
+
**Severity**: Critical
|
|
1351
|
+
**Applies To**: Categorizers
|
|
1352
|
+
|
|
1353
|
+
### Logic
|
|
1354
|
+
|
|
1355
|
+
**Algorithm**: Validate categorizer structure and category definitions.
|
|
1356
|
+
|
|
1357
|
+
**Steps**:
|
|
1358
|
+
1. Check categorizer has Fallback category
|
|
1359
|
+
2. Check category hierarchy rules (TBD - need Go validator analysis)
|
|
1360
|
+
3. Validate category → handler mapping
|
|
1361
|
+
|
|
1362
|
+
### Rules
|
|
1363
|
+
|
|
1364
|
+
**Constraint**: Categorizers must have proper structure and hierarchy.
|
|
1365
|
+
|
|
1366
|
+
**Why**: Invalid category structure causes routing failures.
|
|
1367
|
+
|
|
1368
|
+
### Examples
|
|
1369
|
+
|
|
1370
|
+
**Bad Pattern**:
|
|
1371
|
+
\`\`\`json
|
|
1372
|
+
{
|
|
1373
|
+
"categorizer": {
|
|
1374
|
+
"categories": ["Billing", "Technical"] // ❌ No Fallback
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
\`\`\`
|
|
1378
|
+
|
|
1379
|
+
**Why Bad**: Missing Fallback category means unrecognized intents have no handler.
|
|
1380
|
+
|
|
1381
|
+
**Good Pattern**:
|
|
1382
|
+
\`\`\`json
|
|
1383
|
+
{
|
|
1384
|
+
"categorizer": {
|
|
1385
|
+
"categories": ["Billing", "Technical", "Fallback"] // ✅ Has Fallback
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
\`\`\`
|
|
1389
|
+
|
|
1390
|
+
**Why Good**: All intents have a handler.
|
|
1391
|
+
|
|
1392
|
+
### Remediation
|
|
1393
|
+
|
|
1394
|
+
**How to Fix**:
|
|
1395
|
+
1. Add Fallback category to categorizer
|
|
1396
|
+
2. Ensure each category has a handler node
|
|
1397
|
+
3. Validate category hierarchy (TBD)
|
|
1398
|
+
|
|
1399
|
+
**Prevention** (During Generation):
|
|
1400
|
+
- Always include Fallback category
|
|
1401
|
+
- Ensure every category has at least one handler node
|
|
1402
|
+
|
|
1403
|
+
---
|
|
1404
|
+
|
|
1405
|
+
## Self-Check Checklist
|
|
1406
|
+
|
|
1407
|
+
After generating or modifying a workflow, verify:
|
|
1408
|
+
|
|
1409
|
+
- [ ] All paths produce all named results
|
|
1410
|
+
- [ ] No multiple writers on same path
|
|
1411
|
+
- [ ] Every path has response or abstain
|
|
1412
|
+
- [ ] Category hierarchy is valid
|
|
1413
|
+
- [ ] Categorizer has Fallback category
|
|
1414
|
+
- [ ] All categories have handler nodes
|
|
1415
|
+
|
|
1416
|
+
---
|
|
1417
|
+
|
|
1418
|
+
## Reference
|
|
1419
|
+
|
|
1420
|
+
- **Source**: workflow-engine/pkg/engine/validator.go
|
|
1421
|
+
- **Error Types**: RESULT_ERROR_TYPE_REQUIRED_OUTPUT_NOT_PRODUCED, RESULT_ERROR_TYPE_MULTIPLE_WRITERS, RESULT_ERROR_TYPE_MISSING_RESPONSE_OR_ABSTAIN_REASON, RESULT_ERROR_TYPE_CATEGORY_HIERARCHY_VIOLATION
|
|
1422
|
+
- **Validation Tool**: Use \`workflow(mode="validate")\` to validate workflows
|
|
1423
|
+
`;
|
|
1424
|
+
}
|
|
1143
1425
|
// Helper to check if result is an error
|
|
1144
1426
|
export function isResourceError(result) {
|
|
1145
1427
|
return "code" in result;
|
package/dist/mcp/server.js
CHANGED
|
@@ -4231,6 +4231,19 @@ toolHandlers.workflow = async (args) => {
|
|
|
4231
4231
|
env: normalizedArgs.env,
|
|
4232
4232
|
}, client, () => undefined);
|
|
4233
4233
|
}
|
|
4234
|
+
case "validate": {
|
|
4235
|
+
// Static validation with path enumeration
|
|
4236
|
+
return handleWorkflow({
|
|
4237
|
+
mode: "validate",
|
|
4238
|
+
persona_id: personaId,
|
|
4239
|
+
workflow_def: normalizedArgs.workflow_def,
|
|
4240
|
+
workflow_spec: normalizedArgs.workflow_spec,
|
|
4241
|
+
validation_type: normalizedArgs.validation_type,
|
|
4242
|
+
max_paths: normalizedArgs.max_paths,
|
|
4243
|
+
timeout_ms: normalizedArgs.timeout_ms,
|
|
4244
|
+
env: normalizedArgs.env,
|
|
4245
|
+
}, client, () => undefined);
|
|
4246
|
+
}
|
|
4234
4247
|
case "deploy": {
|
|
4235
4248
|
if (!personaId) {
|
|
4236
4249
|
return { error: 'persona_id is required for workflow(mode="deploy")' };
|
|
@@ -4283,14 +4296,14 @@ toolHandlers.workflow = async (args) => {
|
|
|
4283
4296
|
return {
|
|
4284
4297
|
error: `Mode "${mode}" removed - LLM does this thinking`,
|
|
4285
4298
|
hint: "Use workflow(mode='get') to fetch data, then analyze/generate in your reasoning. Deploy with workflow(mode='deploy').",
|
|
4286
|
-
valid_modes: ["get", "deploy"],
|
|
4299
|
+
valid_modes: ["get", "validate", "deploy"],
|
|
4287
4300
|
};
|
|
4288
4301
|
}
|
|
4289
4302
|
default: {
|
|
4290
4303
|
// No mode or unknown mode - require explicit mode
|
|
4291
4304
|
return {
|
|
4292
|
-
error: `Mode required. Valid modes: get, deploy`,
|
|
4293
|
-
hint: "workflow(mode='get') returns data for LLM. workflow(mode='deploy') executes LLM's workflow_def.",
|
|
4305
|
+
error: `Mode required. Valid modes: get, validate, deploy`,
|
|
4306
|
+
hint: "workflow(mode='get') returns data for LLM. workflow(mode='validate') validates specs. workflow(mode='deploy') executes LLM's workflow_def.",
|
|
4294
4307
|
example: `workflow(mode="get", persona_id="...")`,
|
|
4295
4308
|
};
|
|
4296
4309
|
}
|
package/dist/mcp/tools.js
CHANGED
|
@@ -12,10 +12,8 @@
|
|
|
12
12
|
* - ALL operations require explicit `method` or `mode` parameter
|
|
13
13
|
* - LLM interprets intent and chooses operation, MCP executes
|
|
14
14
|
* - No inference from parameter presence
|
|
15
|
-
* - Tool descriptions are AUTO-GENERATED from workflow-operations.ts
|
|
16
15
|
*/
|
|
17
16
|
import { TOOL_GUIDANCE } from "../sdk/guidance.js";
|
|
18
|
-
import { generateInputDescription, SUPPORTED_OPERATIONS, LIMITATIONS } from "./workflow-operations.js";
|
|
19
17
|
/**
|
|
20
18
|
* Generate the v2 tool set
|
|
21
19
|
*/
|
|
@@ -45,12 +43,11 @@ export function generateTools(envNames, defaultEnv) {
|
|
|
45
43
|
|
|
46
44
|
## Update
|
|
47
45
|
- \`persona(method="update", id="abc", config={...})\` - update config
|
|
48
|
-
- \`persona(method="update", id="abc", input="...")\` - INCREMENTAL workflow changes
|
|
49
|
-
|
|
46
|
+
- \`persona(method="update", id="abc", input="...")\` - INCREMENTAL workflow changes (returns context for Agent to build operations)
|
|
47
|
+
- \`persona(method="update", id="abc", operations=[...])\` - structured operations (Agent-built)
|
|
50
48
|
- \`persona(method="update", id="abc", workflow_spec={...})\` - rewire/remove only (cannot add nodes)
|
|
51
49
|
|
|
52
|
-
**
|
|
53
|
-
${LIMITATIONS.map(l => `- ${l}`).join("\n")}
|
|
50
|
+
**For complex workflows**: Use \`workflow(mode="generate", input="...")\` or structured \`operations\` array.
|
|
54
51
|
|
|
55
52
|
## Delete
|
|
56
53
|
- \`persona(method="delete", id="abc", confirm=true)\` - delete persona
|
|
@@ -174,7 +171,7 @@ persona(
|
|
|
174
171
|
},
|
|
175
172
|
input: {
|
|
176
173
|
type: "string",
|
|
177
|
-
description:
|
|
174
|
+
description: "Incremental workflow changes. Returns workflow context for Agent to build structured operations. For complex multi-node workflows, use workflow(mode='generate') or structured operations array.",
|
|
178
175
|
},
|
|
179
176
|
// === Update params (for method=update) ===
|
|
180
177
|
config: {
|
|
@@ -383,20 +380,27 @@ persona(
|
|
|
383
380
|
- \`workflow(mode="get", persona_id="abc")\` - returns workflow_def, schema, patterns, widgets
|
|
384
381
|
- LLM analyzes, compares, and generates workflows using this data
|
|
385
382
|
|
|
383
|
+
## Validate (static validation with path enumeration)
|
|
384
|
+
- \`workflow(mode="validate", persona_id="abc")\` - validate existing workflow
|
|
385
|
+
- \`workflow(mode="validate", workflow_spec={...})\` - validate workflow spec directly
|
|
386
|
+
- Returns enhanced errors: what is wrong, why, how to fix
|
|
387
|
+
- Validates: required outputs on all paths, multiple writers, response/abstain, category hierarchy
|
|
388
|
+
|
|
386
389
|
## Deploy (execute LLM's result)
|
|
387
390
|
- \`workflow(mode="deploy", persona_id="abc", workflow_def={...})\` - deploy LLM-generated workflow
|
|
388
391
|
|
|
389
392
|
**Workflow for creation:**
|
|
390
393
|
1. \`workflow(mode="get", persona_id="abc")\` → get schema, patterns, current workflow
|
|
391
394
|
2. LLM generates workflow_def (LLM does the work)
|
|
392
|
-
3. \`workflow(mode="
|
|
395
|
+
3. \`workflow(mode="validate", workflow_spec={...})\` → validate before deploying (optional but recommended)
|
|
396
|
+
4. \`workflow(mode="deploy", persona_id="abc", workflow_def={...})\` → MCP deploys`,
|
|
393
397
|
inputSchema: {
|
|
394
398
|
type: "object",
|
|
395
399
|
properties: {
|
|
396
400
|
mode: {
|
|
397
401
|
type: "string",
|
|
398
|
-
enum: ["get", "deploy"],
|
|
399
|
-
description: "get = return data for LLM, deploy = execute LLM's workflow_def",
|
|
402
|
+
enum: ["get", "deploy", "validate"],
|
|
403
|
+
description: "get = return data for LLM, validate = static validation with path enumeration, deploy = execute LLM's workflow_def",
|
|
400
404
|
},
|
|
401
405
|
persona_id: {
|
|
402
406
|
type: "string",
|
|
@@ -406,12 +410,29 @@ persona(
|
|
|
406
410
|
type: "object",
|
|
407
411
|
description: "workflow_def JSON (for mode=deploy) - LLM generates this",
|
|
408
412
|
},
|
|
413
|
+
workflow_spec: {
|
|
414
|
+
type: "object",
|
|
415
|
+
description: "workflow_spec (for mode=validate) - workflow in spec format",
|
|
416
|
+
},
|
|
417
|
+
validation_type: {
|
|
418
|
+
type: "string",
|
|
419
|
+
enum: ["static", "paths", "named_results", "all"],
|
|
420
|
+
description: "Validation type (for mode=validate). Default: 'all'",
|
|
421
|
+
},
|
|
422
|
+
max_paths: {
|
|
423
|
+
type: "number",
|
|
424
|
+
description: "Maximum paths to enumerate (for mode=validate). Default: 1000",
|
|
425
|
+
},
|
|
426
|
+
timeout_ms: {
|
|
427
|
+
type: "number",
|
|
428
|
+
description: "Timeout in milliseconds (for mode=validate). Default: 100",
|
|
429
|
+
},
|
|
409
430
|
env: {
|
|
410
431
|
type: "string",
|
|
411
432
|
description: envDescription,
|
|
412
433
|
},
|
|
413
434
|
},
|
|
414
|
-
required: ["mode"
|
|
435
|
+
required: ["mode"],
|
|
415
436
|
},
|
|
416
437
|
},
|
|
417
438
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/dist/sdk/client.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* const client = new EmaClientV2(env);
|
|
18
18
|
* ```
|
|
19
19
|
*
|
|
20
|
-
* See .
|
|
20
|
+
* See .context/core/guides/lessons-learned.md for migration guidance.
|
|
21
21
|
*/
|
|
22
22
|
import { GrpcClient } from "./grpc-client.js";
|
|
23
23
|
import { toJson } from "@bufbuild/protobuf";
|
|
@@ -793,8 +793,10 @@ export class EmaClient {
|
|
|
793
793
|
* Get data source aggregates (file counts by status).
|
|
794
794
|
*
|
|
795
795
|
* Uses typed gRPC client for proto-based calls.
|
|
796
|
+
* Falls back to counting files from listDataSourceFiles if aggregates API returns zeros.
|
|
796
797
|
*/
|
|
797
798
|
async getDataSourceAggregates(personaId, widgetName = "fileUpload") {
|
|
799
|
+
// Try aggregates API first
|
|
798
800
|
try {
|
|
799
801
|
const response = await this.grpcClient.getContentNodeAggregates(personaId, widgetName);
|
|
800
802
|
// Find the aggregate for the requested widget
|
|
@@ -803,16 +805,41 @@ export class EmaClient {
|
|
|
803
805
|
const pending = counts?.pending ?? 0;
|
|
804
806
|
const success = counts?.success ?? 0;
|
|
805
807
|
const failed = counts?.failed ?? 0;
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
success,
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
808
|
+
const total = pending + success + failed;
|
|
809
|
+
// If we got data, return it
|
|
810
|
+
if (total > 0) {
|
|
811
|
+
return { total, pending, success, failed, widgetName };
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
// Aggregates API failed, fall through to fallback
|
|
816
|
+
}
|
|
817
|
+
// FALLBACK: Count files directly from listDataSourceFiles
|
|
818
|
+
// This handles cases where aggregates API returns zeros but files exist
|
|
819
|
+
try {
|
|
820
|
+
const result = await this.listDataSourceFiles(personaId, { widgetName });
|
|
821
|
+
const files = result.files;
|
|
822
|
+
if (files.length > 0) {
|
|
823
|
+
let pending = 0;
|
|
824
|
+
let success = 0;
|
|
825
|
+
let failed = 0;
|
|
826
|
+
for (const file of files) {
|
|
827
|
+
const status = (file.status ?? "").toLowerCase();
|
|
828
|
+
if (status === "success" || status === "completed" || status === "indexed") {
|
|
829
|
+
success++;
|
|
830
|
+
}
|
|
831
|
+
else if (status === "failed" || status === "error") {
|
|
832
|
+
failed++;
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
pending++; // pending, processing, or unknown
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return { total: files.length, pending, success, failed, widgetName };
|
|
839
|
+
}
|
|
813
840
|
}
|
|
814
841
|
catch {
|
|
815
|
-
//
|
|
842
|
+
// Fallback also failed
|
|
816
843
|
}
|
|
817
844
|
return { total: 0, pending: 0, success: 0, failed: 0, widgetName };
|
|
818
845
|
}
|
package/dist/sdk/ema-client.js
CHANGED
|
@@ -98,11 +98,39 @@ export class EmaClientV2 {
|
|
|
98
98
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
99
99
|
/**
|
|
100
100
|
* List all personas for the current tenant
|
|
101
|
+
*
|
|
102
|
+
* Automatically fetches all pages to return complete list.
|
|
103
|
+
* The API supports pagination via query params (page_num, page_size).
|
|
104
|
+
*
|
|
105
|
+
* @param opts.pageSize - Items per page (default: 1000)
|
|
106
|
+
* @returns All personas for the tenant
|
|
101
107
|
*/
|
|
102
|
-
async listPersonas() {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
108
|
+
async listPersonas(opts) {
|
|
109
|
+
const pageSize = opts?.pageSize ?? 1000;
|
|
110
|
+
const allPersonas = [];
|
|
111
|
+
let pageNum = 1;
|
|
112
|
+
let hasMore = true;
|
|
113
|
+
while (hasMore) {
|
|
114
|
+
// Make request with pagination query params
|
|
115
|
+
// The OpenAPI-generated client doesn't expose these, so we use the underlying client
|
|
116
|
+
const url = `/api/personas/get_personas_for_tenant?page_num=${pageNum}&page_size=${pageSize}&include_persona_group_details=true`;
|
|
117
|
+
const response = await this.restClient.get({
|
|
118
|
+
url,
|
|
119
|
+
security: [{ scheme: 'bearer', type: 'http' }],
|
|
120
|
+
});
|
|
121
|
+
const data = response.data;
|
|
122
|
+
const personas = data?.personas ?? [];
|
|
123
|
+
allPersonas.push(...personas);
|
|
124
|
+
// Check if there are more pages
|
|
125
|
+
const total = data?.total ?? personas.length;
|
|
126
|
+
if (allPersonas.length >= total || personas.length < pageSize) {
|
|
127
|
+
hasMore = false;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
pageNum++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return allPersonas;
|
|
106
134
|
}
|
|
107
135
|
/** Alias for listPersonas */
|
|
108
136
|
async getPersonasForTenant() {
|
package/dist/sdk/index.js
CHANGED
|
@@ -40,7 +40,9 @@ buildVoiceConfig, buildChatConfig, } from "./workflow-generator.js";
|
|
|
40
40
|
// Action Registry (API-driven action/template definitions)
|
|
41
41
|
export { ActionRegistry, ensureActionRegistry, parseActionDefinition, parseTemplateDefinition, } from "./action-registry.js";
|
|
42
42
|
// Workflow Intent (Normalization layer)
|
|
43
|
-
export { parseInput, validateIntent, intentToSpec, detectInputType,
|
|
43
|
+
export { parseInput, validateIntent, intentToSpec, detectInputType,
|
|
44
|
+
// parseNaturalLanguage removed - violates LLM-driven architecture
|
|
45
|
+
parsePartialSpec,
|
|
44
46
|
// LLM-driven generation
|
|
45
47
|
needsLLMGeneration, generateWorkflow, parseWorkflowSpecFromLLM, } from "./workflow-intent.js";
|
|
46
48
|
// Workflow Validator (API-driven validation)
|
package/dist/sdk/knowledge.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* - ema-repos/protos/ - Type definitions
|
|
22
22
|
* - Support cases and real-world usage patterns
|
|
23
23
|
*
|
|
24
|
-
* See: .
|
|
24
|
+
* See: .context/core/guides/source-repos.md for full architecture
|
|
25
25
|
*/
|
|
26
26
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
27
|
// Platform Concepts (from Ema User Guide)
|
|
@@ -752,7 +752,7 @@ export const AGENT_CATALOG = [
|
|
|
752
752
|
// - Widget 'name' is assigned per-template (e.g., "upload", "upload1", "upload2")
|
|
753
753
|
// - Proto 'case' values represent TYPE discriminators, not widget names
|
|
754
754
|
//
|
|
755
|
-
// See .
|
|
755
|
+
// See .context/core/guides/source-repos.md for full sync instructions
|
|
756
756
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
757
757
|
export const WIDGET_CATALOG = [
|
|
758
758
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -778,7 +778,7 @@ export const WIDGET_CATALOG = [
|
|
|
778
778
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
779
779
|
// SOURCE: ema-repos/ema/ema_backend/db/system_values/persona_templates/prod/document_proposal_manager.yaml
|
|
780
780
|
// NOTE: Widget names are defined per-template. These are from Document Proposal Manager.
|
|
781
|
-
// To sync: see .
|
|
781
|
+
// To sync: see .context/core/guides/source-repos.md
|
|
782
782
|
// LAST VERIFIED: 2026-01-27 from document_proposal_manager.yaml
|
|
783
783
|
{ id: 3, name: "upload", description: "Content Repository - gold standard company docs (title: 'Content Repository')", requiredFor: ["document"], fields: ["tags"], uploadTarget: true },
|
|
784
784
|
{ id: 3, name: "upload1", description: "Service Line Documents - business unit specific content (title: 'Service Line Documents')", requiredFor: ["document"], fields: ["tags"], uploadTarget: true },
|
|
@@ -814,7 +814,7 @@ export const PROJECT_TYPES = {
|
|
|
814
814
|
// - INTENT_TYPE_ROUTING (workflow-intent.ts) - routing logic during intent processing
|
|
815
815
|
// - SCHEMA_TYPE_COMPATIBILITY (action-schema-parser.ts) - input name matching for validation
|
|
816
816
|
//
|
|
817
|
-
// See .
|
|
817
|
+
// See .context/core/guides/source-repos.md for full architecture
|
|
818
818
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
819
819
|
export const TYPE_COMPATIBILITY = [
|
|
820
820
|
// Chat conversation compatibility
|
|
@@ -1872,7 +1872,7 @@ export { DEPRECATED_ACTIONS_WITH_REPLACEMENT, DEPRECATED_ACTIONS_NO_REPLACEMENT,
|
|
|
1872
1872
|
* STATUS: Can be auto-generated from source doc
|
|
1873
1873
|
* TODO: Add to catalog-sync.yml workflow for automatic updates
|
|
1874
1874
|
*
|
|
1875
|
-
* See .
|
|
1875
|
+
* See .context/core/guides/source-repos.md for sync details
|
|
1876
1876
|
*/
|
|
1877
1877
|
export const WORKFLOW_ENABLING_CONSTRAINTS = [
|
|
1878
1878
|
{
|