@ema.co/mcp-toolkit 0.2.3 → 0.3.0
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.
- package/dist/mcp/handlers-consolidated.js +248 -1
- package/dist/mcp/server.js +44 -3
- package/dist/mcp/tools-consolidated.js +19 -3
- package/dist/sdk/index.js +8 -0
- package/dist/sdk/version-policy.js +328 -0
- package/dist/sdk/version-storage.js +465 -0
- package/dist/sdk/version-tracking.js +346 -0
- package/package.json +1 -1
- package/docs/advisor-comms-assistant-fixes.md +0 -175
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persona Version Tracking - Core Types and Logic
|
|
3
|
+
*
|
|
4
|
+
* Provides versioning infrastructure for AI Employee configurations.
|
|
5
|
+
* Designed to work locally (git-backed) and map cleanly to backend tables.
|
|
6
|
+
*/
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
import { fingerprintPersona } from "../sync.js";
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// Utility Functions
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Generate a deterministic content hash for a snapshot.
|
|
14
|
+
* Used for deduplication and fast equality checks.
|
|
15
|
+
*/
|
|
16
|
+
export function hashSnapshot(snapshot) {
|
|
17
|
+
// Sort keys for deterministic serialization
|
|
18
|
+
// Sort data_sources by file_id for deterministic ordering
|
|
19
|
+
const sortedDataSources = [...(snapshot.data_sources ?? [])].sort((a, b) => a.file_id.localeCompare(b.file_id));
|
|
20
|
+
const canonical = sortObjectKeys({
|
|
21
|
+
workflow_definition: snapshot.workflow_definition,
|
|
22
|
+
workflow_id: snapshot.workflow_id,
|
|
23
|
+
workflow_interface: snapshot.workflow_interface,
|
|
24
|
+
proto_config: snapshot.proto_config,
|
|
25
|
+
display_name: snapshot.display_name,
|
|
26
|
+
description: snapshot.description,
|
|
27
|
+
welcome_messages: snapshot.welcome_messages,
|
|
28
|
+
trigger_type: snapshot.trigger_type,
|
|
29
|
+
embedding_enabled: snapshot.embedding_enabled,
|
|
30
|
+
template_id: snapshot.template_id,
|
|
31
|
+
data_sources: sortedDataSources,
|
|
32
|
+
});
|
|
33
|
+
const bytes = Buffer.from(JSON.stringify(canonical), "utf8");
|
|
34
|
+
return crypto.createHash("sha256").update(bytes).digest("hex");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Deep sort object keys for deterministic JSON serialization.
|
|
38
|
+
*/
|
|
39
|
+
function sortObjectKeys(obj) {
|
|
40
|
+
if (obj === null || obj === undefined)
|
|
41
|
+
return obj;
|
|
42
|
+
if (Array.isArray(obj))
|
|
43
|
+
return obj.map(sortObjectKeys);
|
|
44
|
+
if (typeof obj !== "object")
|
|
45
|
+
return obj;
|
|
46
|
+
const sorted = {};
|
|
47
|
+
for (const key of Object.keys(obj).sort()) {
|
|
48
|
+
sorted[key] = sortObjectKeys(obj[key]);
|
|
49
|
+
}
|
|
50
|
+
return sorted;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generate a new version ID (UUID v4).
|
|
54
|
+
*/
|
|
55
|
+
export function generateVersionId() {
|
|
56
|
+
return crypto.randomUUID();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate a version name from version number.
|
|
60
|
+
*/
|
|
61
|
+
export function generateVersionName(versionNumber) {
|
|
62
|
+
return `v${versionNumber}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get current ISO timestamp.
|
|
66
|
+
*/
|
|
67
|
+
export function nowIso() {
|
|
68
|
+
return new Date().toISOString();
|
|
69
|
+
}
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
// Snapshot Creation
|
|
72
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Extract data source references from persona's status_log.
|
|
75
|
+
* status_log is a Record<widgetName, FileStatus[]> where FileStatus has
|
|
76
|
+
* fields like: id, filename, status, tags, etc.
|
|
77
|
+
*/
|
|
78
|
+
export function extractDataSources(persona) {
|
|
79
|
+
const statusLog = persona.status_log;
|
|
80
|
+
if (!statusLog) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const dataSources = [];
|
|
84
|
+
for (const [widgetName, files] of Object.entries(statusLog)) {
|
|
85
|
+
// Skip internal/system entries (prefixed with underscore)
|
|
86
|
+
if (widgetName.startsWith("_")) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!Array.isArray(files)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
if (typeof file !== "object" || file === null) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const f = file;
|
|
97
|
+
// Use id if available, fallback to filename for legacy files
|
|
98
|
+
const fileId = f.id ?? f.filename;
|
|
99
|
+
if (!fileId) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
dataSources.push({
|
|
103
|
+
file_id: fileId,
|
|
104
|
+
filename: f.filename ?? fileId,
|
|
105
|
+
widget_name: widgetName,
|
|
106
|
+
tags: Array.isArray(f.tags) ? f.tags : [],
|
|
107
|
+
status: f.status ?? "unknown",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return dataSources;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a PersonaSnapshot from a PersonaDTO.
|
|
115
|
+
*/
|
|
116
|
+
export function createSnapshotFromPersona(persona) {
|
|
117
|
+
return {
|
|
118
|
+
workflow_definition: persona.workflow_def ?? null,
|
|
119
|
+
workflow_id: persona.workflow_id ?? null,
|
|
120
|
+
workflow_interface: persona.workflow_interface ?? null,
|
|
121
|
+
proto_config: persona.proto_config ?? null,
|
|
122
|
+
display_name: persona.name ?? "",
|
|
123
|
+
description: persona.description ?? "",
|
|
124
|
+
welcome_messages: persona.welcome_messages ?? null,
|
|
125
|
+
trigger_type: persona.trigger_type ?? null,
|
|
126
|
+
embedding_enabled: persona.embedding_enabled ?? null,
|
|
127
|
+
template_id: persona.template_id ?? persona.templateId ?? null,
|
|
128
|
+
data_sources: extractDataSources(persona),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create a new version snapshot from a persona.
|
|
133
|
+
*/
|
|
134
|
+
export function createVersionSnapshot(options) {
|
|
135
|
+
const snapshot = createSnapshotFromPersona(options.persona);
|
|
136
|
+
const contentHash = hashSnapshot(snapshot);
|
|
137
|
+
const versionNumber = (options.previous_version_number ?? 0) + 1;
|
|
138
|
+
return {
|
|
139
|
+
id: generateVersionId(),
|
|
140
|
+
persona_id: options.persona.id,
|
|
141
|
+
tenant_id: options.tenant_id,
|
|
142
|
+
environment: options.environment,
|
|
143
|
+
version_number: versionNumber,
|
|
144
|
+
version_name: generateVersionName(versionNumber),
|
|
145
|
+
content_hash: contentHash,
|
|
146
|
+
snapshot,
|
|
147
|
+
created_at: nowIso(),
|
|
148
|
+
created_by: options.created_by ?? "mcp-toolkit",
|
|
149
|
+
trigger: options.trigger,
|
|
150
|
+
message: options.message ?? "",
|
|
151
|
+
parent_version_id: options.parent_version_id,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
155
|
+
// Version Comparison
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
/**
|
|
158
|
+
* Compare two version snapshots and generate a diff.
|
|
159
|
+
*/
|
|
160
|
+
export function compareVersions(v1, v2) {
|
|
161
|
+
const diff = {
|
|
162
|
+
v1: { id: v1.id, version_name: v1.version_name, created_at: v1.created_at },
|
|
163
|
+
v2: { id: v2.id, version_name: v2.version_name, created_at: v2.created_at },
|
|
164
|
+
identical: v1.content_hash === v2.content_hash,
|
|
165
|
+
changed_fields: [],
|
|
166
|
+
changes: {},
|
|
167
|
+
};
|
|
168
|
+
if (diff.identical) {
|
|
169
|
+
return diff;
|
|
170
|
+
}
|
|
171
|
+
// Compare snapshot fields (excluding data_sources which is handled separately)
|
|
172
|
+
const fields = [
|
|
173
|
+
"workflow_definition",
|
|
174
|
+
"workflow_id",
|
|
175
|
+
"workflow_interface",
|
|
176
|
+
"proto_config",
|
|
177
|
+
"display_name",
|
|
178
|
+
"description",
|
|
179
|
+
"welcome_messages",
|
|
180
|
+
"trigger_type",
|
|
181
|
+
"embedding_enabled",
|
|
182
|
+
"template_id",
|
|
183
|
+
];
|
|
184
|
+
for (const field of fields) {
|
|
185
|
+
const val1 = v1.snapshot[field];
|
|
186
|
+
const val2 = v2.snapshot[field];
|
|
187
|
+
if (JSON.stringify(val1) !== JSON.stringify(val2)) {
|
|
188
|
+
diff.changed_fields.push(field);
|
|
189
|
+
diff.changes[field] = { from: val1, to: val2 };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Detailed workflow diff if workflow changed
|
|
193
|
+
if (diff.changed_fields.includes("workflow_definition")) {
|
|
194
|
+
diff.workflow_diff = compareWorkflowDefinitions(v1.snapshot.workflow_definition, v2.snapshot.workflow_definition);
|
|
195
|
+
}
|
|
196
|
+
// Data source diff
|
|
197
|
+
const dataSourceDiff = compareDataSources(v1.snapshot.data_sources ?? [], v2.snapshot.data_sources ?? []);
|
|
198
|
+
if (dataSourceDiff.files_added.length > 0 || dataSourceDiff.files_removed.length > 0) {
|
|
199
|
+
diff.changed_fields.push("data_sources");
|
|
200
|
+
diff.changes["data_sources"] = {
|
|
201
|
+
from: v1.snapshot.data_sources,
|
|
202
|
+
to: v2.snapshot.data_sources,
|
|
203
|
+
};
|
|
204
|
+
diff.data_source_diff = dataSourceDiff;
|
|
205
|
+
}
|
|
206
|
+
return diff;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Compare data sources between two snapshots.
|
|
210
|
+
*/
|
|
211
|
+
function compareDataSources(ds1, ds2) {
|
|
212
|
+
const ids1 = new Set(ds1.map((d) => d.file_id));
|
|
213
|
+
const ids2 = new Set(ds2.map((d) => d.file_id));
|
|
214
|
+
const files_added = [];
|
|
215
|
+
const files_removed = [];
|
|
216
|
+
// Files in ds2 but not in ds1 = added
|
|
217
|
+
for (const ds of ds2) {
|
|
218
|
+
if (!ids1.has(ds.file_id)) {
|
|
219
|
+
files_added.push(ds.filename);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Files in ds1 but not in ds2 = removed
|
|
223
|
+
for (const ds of ds1) {
|
|
224
|
+
if (!ids2.has(ds.file_id)) {
|
|
225
|
+
files_removed.push(ds.filename);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { files_added, files_removed };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Compare two workflow definitions and identify node changes.
|
|
232
|
+
*/
|
|
233
|
+
function compareWorkflowDefinitions(w1, w2) {
|
|
234
|
+
const result = {
|
|
235
|
+
nodes_added: [],
|
|
236
|
+
nodes_removed: [],
|
|
237
|
+
nodes_modified: [],
|
|
238
|
+
};
|
|
239
|
+
if (!w1 && !w2)
|
|
240
|
+
return result;
|
|
241
|
+
if (!w1) {
|
|
242
|
+
// All nodes in w2 are new
|
|
243
|
+
const actions = w2?.actions ?? [];
|
|
244
|
+
result.nodes_added = actions.map((a) => a.name ?? "unnamed").filter(Boolean);
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
if (!w2) {
|
|
248
|
+
// All nodes in w1 are removed
|
|
249
|
+
const actions = w1.actions ?? [];
|
|
250
|
+
result.nodes_removed = actions.map((a) => a.name ?? "unnamed").filter(Boolean);
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
const actions1 = w1.actions ?? [];
|
|
254
|
+
const actions2 = w2.actions ?? [];
|
|
255
|
+
const names1 = new Set(actions1.map((a) => a.name ?? "").filter(Boolean));
|
|
256
|
+
const names2 = new Set(actions2.map((a) => a.name ?? "").filter(Boolean));
|
|
257
|
+
// Find added
|
|
258
|
+
for (const name of names2) {
|
|
259
|
+
if (!names1.has(name)) {
|
|
260
|
+
result.nodes_added.push(name);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Find removed
|
|
264
|
+
for (const name of names1) {
|
|
265
|
+
if (!names2.has(name)) {
|
|
266
|
+
result.nodes_removed.push(name);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Find modified (present in both but different)
|
|
270
|
+
const map1 = new Map(actions1.map((a) => [a.name ?? "", a]));
|
|
271
|
+
const map2 = new Map(actions2.map((a) => [a.name ?? "", a]));
|
|
272
|
+
for (const name of names1) {
|
|
273
|
+
if (names2.has(name)) {
|
|
274
|
+
const a1 = map1.get(name);
|
|
275
|
+
const a2 = map2.get(name);
|
|
276
|
+
if (JSON.stringify(a1) !== JSON.stringify(a2)) {
|
|
277
|
+
result.nodes_modified.push(name);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
284
|
+
// Change Summary Generation
|
|
285
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
286
|
+
/**
|
|
287
|
+
* Generate a human-readable summary of changes between versions.
|
|
288
|
+
*/
|
|
289
|
+
export function generateChangesSummary(diff) {
|
|
290
|
+
if (diff.identical) {
|
|
291
|
+
return ["No changes"];
|
|
292
|
+
}
|
|
293
|
+
const summary = [];
|
|
294
|
+
// Field-level changes
|
|
295
|
+
for (const field of diff.changed_fields) {
|
|
296
|
+
if (field === "workflow_definition" && diff.workflow_diff) {
|
|
297
|
+
// Detailed workflow changes
|
|
298
|
+
const wd = diff.workflow_diff;
|
|
299
|
+
if (wd.nodes_added.length > 0) {
|
|
300
|
+
summary.push(`Added nodes: ${wd.nodes_added.join(", ")}`);
|
|
301
|
+
}
|
|
302
|
+
if (wd.nodes_removed.length > 0) {
|
|
303
|
+
summary.push(`Removed nodes: ${wd.nodes_removed.join(", ")}`);
|
|
304
|
+
}
|
|
305
|
+
if (wd.nodes_modified.length > 0) {
|
|
306
|
+
summary.push(`Modified nodes: ${wd.nodes_modified.join(", ")}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else if (field === "data_sources" && diff.data_source_diff) {
|
|
310
|
+
// Detailed data source changes
|
|
311
|
+
const dsd = diff.data_source_diff;
|
|
312
|
+
if (dsd.files_added.length > 0) {
|
|
313
|
+
summary.push(`Added files: ${dsd.files_added.join(", ")}`);
|
|
314
|
+
}
|
|
315
|
+
if (dsd.files_removed.length > 0) {
|
|
316
|
+
summary.push(`Removed files: ${dsd.files_removed.join(", ")}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else if (field === "display_name") {
|
|
320
|
+
const from = diff.changes[field]?.from;
|
|
321
|
+
const to = diff.changes[field]?.to;
|
|
322
|
+
summary.push(`Renamed: "${from}" → "${to}"`);
|
|
323
|
+
}
|
|
324
|
+
else if (field === "description") {
|
|
325
|
+
summary.push("Description updated");
|
|
326
|
+
}
|
|
327
|
+
else if (field === "proto_config") {
|
|
328
|
+
summary.push("Configuration updated");
|
|
329
|
+
}
|
|
330
|
+
else if (field === "welcome_messages") {
|
|
331
|
+
summary.push("Welcome messages updated");
|
|
332
|
+
}
|
|
333
|
+
else if (field === "embedding_enabled") {
|
|
334
|
+
const enabled = diff.changes[field]?.to;
|
|
335
|
+
summary.push(`Embedding ${enabled ? "enabled" : "disabled"}`);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
summary.push(`${field} changed`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return summary.length > 0 ? summary : ["Configuration changed"];
|
|
342
|
+
}
|
|
343
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
344
|
+
// Exports
|
|
345
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
346
|
+
export { fingerprintPersona };
|
package/package.json
CHANGED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# Advisor Communications Assistant - Workflow Fixes
|
|
2
|
-
|
|
3
|
-
**Persona ID**: `d5aa0b7a-792d-4e3d-8a74-ac87457ffca2`
|
|
4
|
-
**Environment**: demo
|
|
5
|
-
**Analysis Date**: 2026-01-10
|
|
6
|
-
|
|
7
|
-
## Summary
|
|
8
|
-
|
|
9
|
-
| Severity | Count | Status |
|
|
10
|
-
|----------|-------|--------|
|
|
11
|
-
| Critical | 3 | Needs fix |
|
|
12
|
-
| Warning | 0 | - |
|
|
13
|
-
| Info | 4 | Acceptable |
|
|
14
|
-
|
|
15
|
-
## Critical Issues & Fixes
|
|
16
|
-
|
|
17
|
-
### Issue 1: Document → Email Attachment Type Mismatch
|
|
18
|
-
|
|
19
|
-
**Location**: `generate_documentnr7ttm` → `send_email_agentq7zdvj`
|
|
20
|
-
|
|
21
|
-
**Problem**:
|
|
22
|
-
- `document_link` output is type `WELL_KNOWN_TYPE_DOCUMENT`
|
|
23
|
-
- `attachment_links` input expects `WELL_KNOWN_TYPE_TEXT_WITH_SOURCES`
|
|
24
|
-
|
|
25
|
-
**Fix Options**:
|
|
26
|
-
|
|
27
|
-
**Option A (Recommended)**: Use `named_inputs` instead
|
|
28
|
-
```yaml
|
|
29
|
-
# Change from:
|
|
30
|
-
send_email_agentq7zdvj.inputs.attachment_links:
|
|
31
|
-
actionOutput:
|
|
32
|
-
actionName: generate_documentnr7ttm
|
|
33
|
-
output: document_link
|
|
34
|
-
|
|
35
|
-
# To:
|
|
36
|
-
send_email_agentq7zdvj.inputs.named_inputs:
|
|
37
|
-
multiBinding:
|
|
38
|
-
elements:
|
|
39
|
-
- namedBinding:
|
|
40
|
-
name: document_attachment
|
|
41
|
-
value:
|
|
42
|
-
actionOutput:
|
|
43
|
-
actionName: generate_documentnr7ttm
|
|
44
|
-
output: document_link
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Option B**: Use a fixed_response node to convert document_link to text
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
### Issue 2: Combined Results → Content Generator Type Mismatch
|
|
52
|
-
|
|
53
|
-
**Location**: `combine_search_resultssmoo58` → `personalized_content_generatorfosf62`
|
|
54
|
-
|
|
55
|
-
**Problem**:
|
|
56
|
-
- `combined_results` output is type `WELL_KNOWN_TYPE_TEXT_WITH_SOURCES`
|
|
57
|
-
- `search_results` input expects `WELL_KNOWN_TYPE_SEARCH_RESULT`
|
|
58
|
-
|
|
59
|
-
**Fix**:
|
|
60
|
-
Route original search results directly instead of combined:
|
|
61
|
-
|
|
62
|
-
```yaml
|
|
63
|
-
# Change from:
|
|
64
|
-
personalized_content_generatorfosf62.inputs.search_results:
|
|
65
|
-
actionOutput:
|
|
66
|
-
actionName: combine_search_resultssmoo58
|
|
67
|
-
output: combined_results
|
|
68
|
-
|
|
69
|
-
# To:
|
|
70
|
-
personalized_content_generatorfosf62.inputs.search_results:
|
|
71
|
-
actionOutput:
|
|
72
|
-
actionName: searchfleqzf
|
|
73
|
-
output: search_results
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Alternative**: Use `named_inputs` for combined results:
|
|
77
|
-
```yaml
|
|
78
|
-
personalized_content_generatorfosf62.inputs.named_inputs:
|
|
79
|
-
multiBinding:
|
|
80
|
-
elements:
|
|
81
|
-
- namedBinding:
|
|
82
|
-
name: combined_context
|
|
83
|
-
value:
|
|
84
|
-
actionOutput:
|
|
85
|
-
actionName: combine_search_resultssmoo58
|
|
86
|
-
output: combined_results
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
### Issue 3: Summarized Conversation → Custom Agent Conversation Mismatch
|
|
92
|
-
|
|
93
|
-
**Location**: `conversation_summarizer` → `custom_agentrjrvw6`
|
|
94
|
-
|
|
95
|
-
**Problem**:
|
|
96
|
-
- `summarized_conversation` is type `WELL_KNOWN_TYPE_TEXT_WITH_SOURCES`
|
|
97
|
-
- `conversation` input expects `WELL_KNOWN_TYPE_CHAT_CONVERSATION`
|
|
98
|
-
|
|
99
|
-
**Fix**:
|
|
100
|
-
Use the trigger's conversation output instead:
|
|
101
|
-
|
|
102
|
-
```yaml
|
|
103
|
-
# Change from:
|
|
104
|
-
custom_agentrjrvw6.inputs.conversation:
|
|
105
|
-
actionOutput:
|
|
106
|
-
actionName: conversation_summarizer
|
|
107
|
-
output: summarized_conversation
|
|
108
|
-
|
|
109
|
-
# To:
|
|
110
|
-
custom_agentrjrvw6.inputs.conversation:
|
|
111
|
-
actionOutput:
|
|
112
|
-
actionName: trigger
|
|
113
|
-
output: chat_conversation
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
And pass the summarized query via `named_inputs`:
|
|
117
|
-
```yaml
|
|
118
|
-
custom_agentrjrvw6.inputs.named_inputs:
|
|
119
|
-
multiBinding:
|
|
120
|
-
elements:
|
|
121
|
-
- namedBinding:
|
|
122
|
-
name: context
|
|
123
|
-
value:
|
|
124
|
-
actionOutput:
|
|
125
|
-
actionName: conversation_summarizer
|
|
126
|
-
output: summarized_conversation
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## Info Issues (Acceptable)
|
|
132
|
-
|
|
133
|
-
### Sequential Searches
|
|
134
|
-
The workflow has sequential dependencies between searches:
|
|
135
|
-
- `conversation_summarizer` → `live_web_search7kmyak`
|
|
136
|
-
- `searchfleqzf` → `combine_search_resultssmoo58`
|
|
137
|
-
|
|
138
|
-
**Assessment**: Acceptable - the searches depend on each other's outputs.
|
|
139
|
-
|
|
140
|
-
### Multiple LLM Nodes
|
|
141
|
-
5 LLM nodes process results from `searchfleqzf`:
|
|
142
|
-
- `respond_client_update`
|
|
143
|
-
- `respond_market_impact`
|
|
144
|
-
- `respond_exposure`
|
|
145
|
-
- `respond_compliance`
|
|
146
|
-
- `respond_health`
|
|
147
|
-
|
|
148
|
-
**Assessment**: Expected behavior - these are conditional branches from the categorizer.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Application Instructions
|
|
153
|
-
|
|
154
|
-
### Via Ema UI (Recommended)
|
|
155
|
-
1. Open persona in Auto Builder
|
|
156
|
-
2. For each critical issue:
|
|
157
|
-
- Click on the target node
|
|
158
|
-
- Modify the input binding as described
|
|
159
|
-
3. Save and test
|
|
160
|
-
|
|
161
|
-
### Via API
|
|
162
|
-
Use the `/api/personas/update_persona` endpoint with the corrected `workflow` field.
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## Type Compatibility Quick Reference
|
|
167
|
-
|
|
168
|
-
| From Type | Can Connect To |
|
|
169
|
-
|-----------|---------------|
|
|
170
|
-
| CHAT_CONVERSATION | conversation inputs, named_inputs |
|
|
171
|
-
| TEXT_WITH_SOURCES | query, named_inputs, instructions |
|
|
172
|
-
| SEARCH_RESULT | search_results inputs, named_inputs |
|
|
173
|
-
| DOCUMENT | named_inputs (NOT text inputs) |
|
|
174
|
-
|
|
175
|
-
**Rule**: When in doubt, use `named_inputs` - it accepts ANY type.
|