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

Files changed (35) hide show
  1. package/dist/mcp/handlers/data/index.js +3 -0
  2. package/dist/mcp/handlers/persona/create.js +16 -0
  3. package/dist/mcp/handlers/persona/list.js +9 -4
  4. package/dist/mcp/handlers/persona/update.js +24 -2
  5. package/dist/mcp/handlers/workflow/deploy.js +20 -2
  6. package/dist/mcp/handlers/workflow/generate.js +39 -2
  7. package/dist/mcp/handlers/workflow/index.js +8 -3
  8. package/dist/mcp/handlers/workflow/modify.js +34 -7
  9. package/dist/mcp/handlers/workflow/validate.js +85 -0
  10. package/dist/mcp/handlers/workflow/validation.js +160 -0
  11. package/dist/mcp/resources.js +286 -4
  12. package/dist/mcp/server.js +16 -3
  13. package/dist/mcp/tools.js +32 -11
  14. package/dist/sdk/client.js +36 -9
  15. package/dist/sdk/ema-client.js +32 -4
  16. package/dist/sdk/index.js +3 -1
  17. package/dist/sdk/knowledge.js +5 -5
  18. package/dist/sdk/structural-rules.js +498 -0
  19. package/dist/sdk/workflow-generator.js +2 -1
  20. package/dist/sdk/workflow-intent.js +28 -96
  21. package/dist/sdk/workflow-path-enumerator.js +278 -0
  22. package/dist/sdk/workflow-static-validator.js +291 -0
  23. package/dist/sdk/workflow-validation-types.js +7 -0
  24. package/docs/README.md +14 -0
  25. package/docs/go-validator-analysis.md +323 -0
  26. package/docs/rule-format-specification.md +346 -0
  27. package/docs/validation-contract.md +397 -0
  28. package/docs/validation-error-format.md +326 -0
  29. package/package.json +1 -1
  30. package/dist/mcp/workflow-operations.js +0 -100
  31. package/dist/sdk/workflow-fixer.js +0 -48
  32. package/docs/dashboard-operations.md +0 -281
  33. package/docs/ema-user-guide.md +0 -1201
  34. package/docs/email-patterns.md +0 -120
  35. package/docs/mcp-tools-guide.md +0 -575
@@ -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 (files that exist in this repo)
103
+ // Core Documentation (public guides)
104
104
  "ema://docs/getting-started": {
105
- path: "docs/mcp-tools-guide.md",
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: "docs/ema-user-guide.md",
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: "docs/mcp-tools-guide.md",
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;
@@ -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
- ${SUPPORTED_OPERATIONS.map(op => ` - "${op.example}" (${op.name})`).join("\n")}
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
- **NOT supported via input** (clone from existing persona instead):
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: generateInputDescription(),
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="deploy", persona_id="abc", workflow_def={...})\` → MCP deploys`,
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", "persona_id"],
435
+ required: ["mode"],
415
436
  },
416
437
  },
417
438
  // ═══════════════════════════════════════════════════════════════════════════
@@ -17,7 +17,7 @@
17
17
  * const client = new EmaClientV2(env);
18
18
  * ```
19
19
  *
20
- * See .ctx/docs/lessons-learned.md for migration guidance.
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
- return {
807
- total: pending + success + failed,
808
- pending,
809
- success,
810
- failed,
811
- widgetName,
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
- // Fall through to return empty
842
+ // Fallback also failed
816
843
  }
817
844
  return { total: 0, pending: 0, success: 0, failed: 0, widgetName };
818
845
  }
@@ -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 result = await api.listPersonas({ client: this.restClient });
104
- const response = result.data;
105
- return response?.personas ?? [];
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, parseNaturalLanguage, parsePartialSpec,
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)
@@ -21,7 +21,7 @@
21
21
  * - ema-repos/protos/ - Type definitions
22
22
  * - Support cases and real-world usage patterns
23
23
  *
24
- * See: .ctx/docs/source-repos.md for full architecture
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 .ctx/docs/source-repos.md for full sync instructions
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 .ctx/docs/source-repos.md
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 .ctx/docs/source-repos.md for full architecture
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 .ctx/docs/source-repos.md for sync details
1875
+ * See .context/core/guides/source-repos.md for sync details
1876
1876
  */
1877
1877
  export const WORKFLOW_ENABLING_CONSTRAINTS = [
1878
1878
  {