@inkeep/agents-run-api 0.1.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/README.md +117 -0
- package/dist/AgentExecutionServer.d.ts +23 -0
- package/dist/AgentExecutionServer.d.ts.map +1 -0
- package/dist/AgentExecutionServer.js +32 -0
- package/dist/__tests__/setup.d.ts +4 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +50 -0
- package/dist/__tests__/utils/testProject.d.ts +18 -0
- package/dist/__tests__/utils/testProject.d.ts.map +1 -0
- package/dist/__tests__/utils/testProject.js +26 -0
- package/dist/__tests__/utils/testRequest.d.ts +8 -0
- package/dist/__tests__/utils/testRequest.d.ts.map +1 -0
- package/dist/__tests__/utils/testRequest.js +32 -0
- package/dist/__tests__/utils/testTenant.d.ts +64 -0
- package/dist/__tests__/utils/testTenant.d.ts.map +1 -0
- package/dist/__tests__/utils/testTenant.js +71 -0
- package/dist/a2a/client.d.ts +182 -0
- package/dist/a2a/client.d.ts.map +1 -0
- package/dist/a2a/client.js +645 -0
- package/dist/a2a/handlers.d.ts +4 -0
- package/dist/a2a/handlers.d.ts.map +1 -0
- package/dist/a2a/handlers.js +657 -0
- package/dist/a2a/transfer.d.ts +18 -0
- package/dist/a2a/transfer.d.ts.map +1 -0
- package/dist/a2a/transfer.js +22 -0
- package/dist/a2a/types.d.ts +63 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +1 -0
- package/dist/agents/Agent.d.ts +154 -0
- package/dist/agents/Agent.d.ts.map +1 -0
- package/dist/agents/Agent.js +1105 -0
- package/dist/agents/ModelFactory.d.ts +62 -0
- package/dist/agents/ModelFactory.d.ts.map +1 -0
- package/dist/agents/ModelFactory.js +208 -0
- package/dist/agents/SystemPromptBuilder.d.ts +14 -0
- package/dist/agents/SystemPromptBuilder.d.ts.map +1 -0
- package/dist/agents/SystemPromptBuilder.js +62 -0
- package/dist/agents/ToolSessionManager.d.ts +61 -0
- package/dist/agents/ToolSessionManager.d.ts.map +1 -0
- package/dist/agents/ToolSessionManager.js +143 -0
- package/dist/agents/artifactTools.d.ts +30 -0
- package/dist/agents/artifactTools.d.ts.map +1 -0
- package/dist/agents/artifactTools.js +463 -0
- package/dist/agents/generateTaskHandler.d.ts +41 -0
- package/dist/agents/generateTaskHandler.d.ts.map +1 -0
- package/dist/agents/generateTaskHandler.js +350 -0
- package/dist/agents/relationTools.d.ts +33 -0
- package/dist/agents/relationTools.d.ts.map +1 -0
- package/dist/agents/relationTools.js +245 -0
- package/dist/agents/types.d.ts +23 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/versions/V1Config.d.ts +21 -0
- package/dist/agents/versions/V1Config.d.ts.map +1 -0
- package/dist/agents/versions/V1Config.js +285 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +194 -0
- package/dist/data/agentGraph.d.ts +4 -0
- package/dist/data/agentGraph.d.ts.map +1 -0
- package/dist/data/agentGraph.js +73 -0
- package/dist/data/agents.d.ts +4 -0
- package/dist/data/agents.d.ts.map +1 -0
- package/dist/data/agents.js +73 -0
- package/dist/data/conversations.d.ts +59 -0
- package/dist/data/conversations.d.ts.map +1 -0
- package/dist/data/conversations.js +216 -0
- package/dist/data/db/clean.d.ts +6 -0
- package/dist/data/db/clean.d.ts.map +1 -0
- package/dist/data/db/clean.js +77 -0
- package/dist/data/db/dbClient.d.ts +3 -0
- package/dist/data/db/dbClient.d.ts.map +1 -0
- package/dist/data/db/dbClient.js +13 -0
- package/dist/env.d.ts +43 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +63 -0
- package/dist/handlers/executionHandler.d.ts +36 -0
- package/dist/handlers/executionHandler.d.ts.map +1 -0
- package/dist/handlers/executionHandler.js +402 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/instrumentation.d.ts +13 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +66 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/middleware/api-key-auth.d.ts +22 -0
- package/dist/middleware/api-key-auth.d.ts.map +1 -0
- package/dist/middleware/api-key-auth.js +139 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/openapi.d.ts +2 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +36 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +155 -0
- package/dist/routes/chat.d.ts +4 -0
- package/dist/routes/chat.d.ts.map +1 -0
- package/dist/routes/chat.js +308 -0
- package/dist/routes/chatDataStream.d.ts +4 -0
- package/dist/routes/chatDataStream.d.ts.map +1 -0
- package/dist/routes/chatDataStream.js +179 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +500 -0
- package/dist/tracer.d.ts +24 -0
- package/dist/tracer.d.ts.map +1 -0
- package/dist/tracer.js +97 -0
- package/dist/types/chat.d.ts +25 -0
- package/dist/types/chat.d.ts.map +1 -0
- package/dist/types/chat.js +1 -0
- package/dist/types/execution-context.d.ts +14 -0
- package/dist/types/execution-context.d.ts.map +1 -0
- package/dist/types/execution-context.js +14 -0
- package/dist/utils/agent-operations.d.ts +79 -0
- package/dist/utils/agent-operations.d.ts.map +1 -0
- package/dist/utils/agent-operations.js +67 -0
- package/dist/utils/artifact-component-schema.d.ts +29 -0
- package/dist/utils/artifact-component-schema.d.ts.map +1 -0
- package/dist/utils/artifact-component-schema.js +119 -0
- package/dist/utils/artifact-parser.d.ts +71 -0
- package/dist/utils/artifact-parser.d.ts.map +1 -0
- package/dist/utils/artifact-parser.js +251 -0
- package/dist/utils/cleanup.d.ts +19 -0
- package/dist/utils/cleanup.d.ts.map +1 -0
- package/dist/utils/cleanup.js +66 -0
- package/dist/utils/data-component-schema.d.ts +6 -0
- package/dist/utils/data-component-schema.d.ts.map +1 -0
- package/dist/utils/data-component-schema.js +43 -0
- package/dist/utils/graph-session.d.ts +200 -0
- package/dist/utils/graph-session.d.ts.map +1 -0
- package/dist/utils/graph-session.js +1009 -0
- package/dist/utils/incremental-stream-parser.d.ts +57 -0
- package/dist/utils/incremental-stream-parser.d.ts.map +1 -0
- package/dist/utils/incremental-stream-parser.js +287 -0
- package/dist/utils/response-formatter.d.ts +27 -0
- package/dist/utils/response-formatter.d.ts.map +1 -0
- package/dist/utils/response-formatter.js +160 -0
- package/dist/utils/stream-helpers.d.ts +162 -0
- package/dist/utils/stream-helpers.d.ts.map +1 -0
- package/dist/utils/stream-helpers.js +385 -0
- package/dist/utils/stream-registry.d.ts +18 -0
- package/dist/utils/stream-registry.d.ts.map +1 -0
- package/dist/utils/stream-registry.js +33 -0
- package/package.json +88 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { tool } from 'ai';
|
|
2
|
+
import jmespath from 'jmespath';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
import z from 'zod';
|
|
5
|
+
import { getLogger } from '../logger.js';
|
|
6
|
+
import { graphSessionManager } from '../utils/graph-session.js';
|
|
7
|
+
import { parseEmbeddedJson } from './generateTaskHandler.js';
|
|
8
|
+
import { toolSessionManager } from './ToolSessionManager.js';
|
|
9
|
+
const logger = getLogger('artifactTools');
|
|
10
|
+
function buildKeyNestingMap(data, prefix = '', map = new Map()) {
|
|
11
|
+
if (typeof data === 'object' && data !== null) {
|
|
12
|
+
if (Array.isArray(data)) {
|
|
13
|
+
// For arrays, analyze the first few items
|
|
14
|
+
data.slice(0, 3).forEach((item, index) => {
|
|
15
|
+
buildKeyNestingMap(item, `${prefix}[${index}]`, map);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
Object.keys(data).forEach((key) => {
|
|
20
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
21
|
+
// Store all paths where this key appears
|
|
22
|
+
if (!map.has(key)) {
|
|
23
|
+
map.set(key, []);
|
|
24
|
+
}
|
|
25
|
+
map.get(key).push(fullPath);
|
|
26
|
+
// Recurse into nested objects
|
|
27
|
+
buildKeyNestingMap(data[key], fullPath, map);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return map;
|
|
32
|
+
}
|
|
33
|
+
function analyzeSelectorFailure(data, selector) {
|
|
34
|
+
const analysis = [];
|
|
35
|
+
const suggestions = [];
|
|
36
|
+
let availableKeys = [];
|
|
37
|
+
try {
|
|
38
|
+
// Build a complete map of where each key appears
|
|
39
|
+
const keyNestingMap = buildKeyNestingMap(data);
|
|
40
|
+
// Get top-level keys
|
|
41
|
+
if (typeof data === 'object' && data !== null) {
|
|
42
|
+
availableKeys = Object.keys(data);
|
|
43
|
+
}
|
|
44
|
+
// Check for quote mismatches in filters
|
|
45
|
+
const filterMatches = selector.match(/\[.*?\]/g);
|
|
46
|
+
if (filterMatches) {
|
|
47
|
+
filterMatches.forEach((filter) => {
|
|
48
|
+
const hasSingleQuote = filter.includes("'");
|
|
49
|
+
const hasDoubleQuote = filter.includes('"');
|
|
50
|
+
if (hasSingleQuote && hasDoubleQuote) {
|
|
51
|
+
analysis.push('MIXED QUOTES in filter');
|
|
52
|
+
suggestions.push('Use consistent quotes in filter conditions');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Extract the target key from selector (the key we're trying to reach)
|
|
57
|
+
const selectorParts = selector.split('.').filter((part) => part && !part.startsWith('['));
|
|
58
|
+
const targetKey = selectorParts[selectorParts.length - 1]?.replace(/\[.*?\]/g, '');
|
|
59
|
+
// Walk the selector path to find where it breaks
|
|
60
|
+
let currentData = data;
|
|
61
|
+
let validPath = '';
|
|
62
|
+
let pathBroken = false;
|
|
63
|
+
for (let i = 0; i < selectorParts.length; i++) {
|
|
64
|
+
const part = selectorParts[i].replace(/\[.*?\]/g, '');
|
|
65
|
+
if (part && currentData && typeof currentData === 'object') {
|
|
66
|
+
if (!(part in currentData)) {
|
|
67
|
+
const pathSoFar = validPath || 'root';
|
|
68
|
+
analysis.push(`KEY "${part}" not found at ${pathSoFar}`);
|
|
69
|
+
// Use the nesting map to suggest alternative paths
|
|
70
|
+
if (keyNestingMap.has(part)) {
|
|
71
|
+
const allPaths = keyNestingMap.get(part);
|
|
72
|
+
const relevantPaths = allPaths.filter((path) => path.includes(validPath) || validPath === '');
|
|
73
|
+
if (relevantPaths.length > 0) {
|
|
74
|
+
suggestions.push(`"${part}" exists at: ${relevantPaths.slice(0, 3).join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
pathBroken = true;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
currentData = currentData[part];
|
|
81
|
+
validPath = validPath ? `${validPath}.${part}` : part;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// If path is valid but selector still failed, it's likely a filter issue with nested data
|
|
85
|
+
if (!pathBroken && targetKey && keyNestingMap.has(targetKey)) {
|
|
86
|
+
analysis.push('PATH VALID BUT FILTER FAILED');
|
|
87
|
+
const allPaths = keyNestingMap.get(targetKey);
|
|
88
|
+
const deeperPaths = allPaths.filter((path) => path.length > selector.replace(/\[.*?\]/g, '').length);
|
|
89
|
+
if (deeperPaths.length > 0) {
|
|
90
|
+
suggestions.push(`"${targetKey}" also found deeper at: ${deeperPaths.slice(0, 3).join(', ')}`);
|
|
91
|
+
suggestions.push('Try a deeper path or check filter values');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (analysis.length === 0) {
|
|
95
|
+
analysis.push('UNKNOWN SELECTOR ISSUE');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (debugError) {
|
|
99
|
+
analysis.push('SYNTAX ERROR');
|
|
100
|
+
suggestions.push('Check JMESPath syntax');
|
|
101
|
+
}
|
|
102
|
+
return { analysis, suggestions, availableKeys };
|
|
103
|
+
}
|
|
104
|
+
function createPropSelectorsSchema(artifactComponents) {
|
|
105
|
+
if (!artifactComponents || artifactComponents.length === 0) {
|
|
106
|
+
return z
|
|
107
|
+
.record(z.string(), z.string())
|
|
108
|
+
.describe('Prop selectors mapping schema properties to JMESPath expressions relative to base selector');
|
|
109
|
+
}
|
|
110
|
+
// Create a union of all possible prop selector schemas based on artifact components
|
|
111
|
+
const propSelectorSchemas = artifactComponents.map((ac) => {
|
|
112
|
+
const props = ac.summaryProps ||
|
|
113
|
+
ac.fullProps ||
|
|
114
|
+
{};
|
|
115
|
+
// Create schema object for each property in the artifact component
|
|
116
|
+
const propSchema = {};
|
|
117
|
+
// Add properties from summaryProps schema
|
|
118
|
+
if (ac.summaryProps && typeof ac.summaryProps === 'object') {
|
|
119
|
+
const summaryProps = ac.summaryProps;
|
|
120
|
+
if (summaryProps.properties) {
|
|
121
|
+
Object.entries(summaryProps.properties).forEach(([propName, propDef]) => {
|
|
122
|
+
const propDescription = propDef?.description || propDef?.title || `${propName} property`;
|
|
123
|
+
propSchema[propName] = z
|
|
124
|
+
.string()
|
|
125
|
+
.describe(`JMESPath selector for ${propName} (${propDescription}) - summary version, relative to base selector`);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Add properties from fullProps schema
|
|
130
|
+
if (ac.fullProps && typeof ac.fullProps === 'object') {
|
|
131
|
+
const fullProps = ac.fullProps;
|
|
132
|
+
if (fullProps.properties) {
|
|
133
|
+
Object.entries(fullProps.properties).forEach(([propName, propDef]) => {
|
|
134
|
+
// Don't overwrite if already exists from summary
|
|
135
|
+
if (!propSchema[propName]) {
|
|
136
|
+
const propDescription = propDef?.description || propDef?.title || `${propName} property`;
|
|
137
|
+
propSchema[propName] = z
|
|
138
|
+
.string()
|
|
139
|
+
.describe(`JMESPath selector for ${propName} (${propDescription}) - full version, relative to base selector`);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return z.object(propSchema).describe(`Prop selectors for ${ac.name} artifact`);
|
|
145
|
+
});
|
|
146
|
+
// Return union of all prop selector schemas or fallback to generic record
|
|
147
|
+
if (propSelectorSchemas.length === 1) {
|
|
148
|
+
return propSelectorSchemas[0];
|
|
149
|
+
}
|
|
150
|
+
else if (propSelectorSchemas.length > 1) {
|
|
151
|
+
return z.union(propSelectorSchemas);
|
|
152
|
+
}
|
|
153
|
+
return z
|
|
154
|
+
.record(z.string(), z.string())
|
|
155
|
+
.describe('Prop selectors mapping schema properties to JMESPath expressions relative to base selector');
|
|
156
|
+
}
|
|
157
|
+
function createInputSchema(artifactComponents) {
|
|
158
|
+
const baseSchema = z.object({
|
|
159
|
+
toolCallId: z
|
|
160
|
+
.string()
|
|
161
|
+
.describe('EXACT toolCallId from a previous tool execution - copy it exactly from the tool call result. NEVER invent or make up tool call IDs.'),
|
|
162
|
+
baseSelector: z
|
|
163
|
+
.string()
|
|
164
|
+
.describe('JMESPath selector to get to the main data array/object. ALWAYS start with "result." Example: "result.content[?type==\'text\']"'),
|
|
165
|
+
propSelectors: createPropSelectorsSchema(artifactComponents),
|
|
166
|
+
});
|
|
167
|
+
// If no artifact components, return base schema
|
|
168
|
+
if (!artifactComponents || artifactComponents.length === 0) {
|
|
169
|
+
return baseSchema;
|
|
170
|
+
}
|
|
171
|
+
// Add artifactType selection based on available artifact components
|
|
172
|
+
return baseSchema.extend({
|
|
173
|
+
artifactType: z
|
|
174
|
+
.enum(artifactComponents.map((ac) => ac.name))
|
|
175
|
+
.describe(`Type of artifact to create. Available types: ${artifactComponents.map((ac) => ac.name).join(', ')}`),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
export function createSaveToolResultTool(sessionId, streamRequestId, // For GraphSession recording
|
|
179
|
+
agentId, artifactComponents) {
|
|
180
|
+
const inputSchema = createInputSchema(artifactComponents);
|
|
181
|
+
// Create available types with descriptions for better context
|
|
182
|
+
const availableTypesWithDescriptions = artifactComponents?.length
|
|
183
|
+
? artifactComponents
|
|
184
|
+
.map((ac) => `- ${ac.name}: ${ac.description || 'No description available'}`)
|
|
185
|
+
.join('\n')
|
|
186
|
+
: 'Generic artifacts';
|
|
187
|
+
return tool({
|
|
188
|
+
description: `Save tool results as structured artifacts. Each artifact should represent ONE SPECIFIC, IMPORTANT, and UNIQUE document or data item.
|
|
189
|
+
|
|
190
|
+
AVAILABLE ARTIFACT TYPES:
|
|
191
|
+
${availableTypesWithDescriptions}
|
|
192
|
+
|
|
193
|
+
🚨 FUNDAMENTAL RULE: ONE ARTIFACT = ONE SEPARATE DATA COMPONENT 🚨
|
|
194
|
+
|
|
195
|
+
Each artifact you save becomes a SEPARATE DATA COMPONENT in the structured response:
|
|
196
|
+
✅ A SINGLE, SPECIFIC document (e.g., one specific API endpoint, one specific person's profile, one specific error)
|
|
197
|
+
✅ IMPORTANT and directly relevant to the user's question
|
|
198
|
+
✅ UNIQUE with distinct value from other artifacts
|
|
199
|
+
✅ RENDERED AS INDIVIDUAL DATA COMPONENT in the UI
|
|
200
|
+
|
|
201
|
+
❌ DO NOT save multiple different items in one artifact unless they are EXTREMELY SIMILAR
|
|
202
|
+
❌ DO NOT batch unrelated items together - each item becomes its own data component
|
|
203
|
+
❌ DO NOT save generic collections - break them into individual data components
|
|
204
|
+
|
|
205
|
+
🎯 STRUCTURED DATA COMPONENT PRINCIPLE:
|
|
206
|
+
Each artifact save creates ONE data component that will be rendered separately in the UI. If you have 5 important items, save them as 5 separate artifacts to create 5 separate data components for better user experience.
|
|
207
|
+
|
|
208
|
+
THINK: "What is the ONE most important piece of information here that deserves its own data component?"
|
|
209
|
+
|
|
210
|
+
EXAMPLES OF GOOD INDIVIDUAL ARTIFACTS (SEPARATE DATA COMPONENTS):
|
|
211
|
+
- Nick Gomez's founder profile (specific person) → Individual data component
|
|
212
|
+
- The /users/create API endpoint documentation (specific endpoint) → Individual data component
|
|
213
|
+
- Error message for authentication failure (specific error type) → Individual data component
|
|
214
|
+
- Configuration for Redis caching (specific config topic) → Individual data component
|
|
215
|
+
|
|
216
|
+
EXAMPLES OF BAD BATCHING:
|
|
217
|
+
❌ "All team members" → Should be separate artifacts for each important member (separate data components)
|
|
218
|
+
❌ "All API endpoints" → Should be separate artifacts for each distinct endpoint (separate data components)
|
|
219
|
+
❌ "All error types" → Should be separate artifacts for each error category (separate data components)
|
|
220
|
+
|
|
221
|
+
USAGE PATTERN:
|
|
222
|
+
1. baseSelector: Navigate through nested structures to target ONE SPECIFIC item
|
|
223
|
+
- Navigate through all necessary levels: "result.data.items.nested[?condition]"
|
|
224
|
+
- Handle nested structures properly: "result.content.content[?field1=='value']" is fine if that's the structure
|
|
225
|
+
- Use [?condition] filtering to get exactly the item you want
|
|
226
|
+
- Example: "result.items[?field_a=='target_value' && field_b=='specific_type']"
|
|
227
|
+
- NOT: "result.items[*]" (too broad, gets everything)
|
|
228
|
+
|
|
229
|
+
2. propSelectors: Extract properties relative to your selected item
|
|
230
|
+
- Always relative to the single item that baseSelector returns
|
|
231
|
+
- Simple paths from that item: { prop1: "field_x", prop2: "nested.field_y", prop3: "deep.nested.field_z" }
|
|
232
|
+
- The tool handles array iteration - your selectors work on individual items
|
|
233
|
+
|
|
234
|
+
3. Result: ONE artifact representing ONE important, unique item → ONE data component
|
|
235
|
+
|
|
236
|
+
💡 HANDLING NESTED STRUCTURES:
|
|
237
|
+
- Navigate as deep as needed: "result.data.items.content.documents[?condition]" is fine
|
|
238
|
+
- Focus on getting to the right level with baseSelector, then keep propSelectors simple
|
|
239
|
+
- Test your baseSelector: Does it return exactly the items you want?
|
|
240
|
+
|
|
241
|
+
⚠️ STRICT SELECTIVITY RULES FOR DATA COMPONENTS:
|
|
242
|
+
- ALWAYS ask: "Is this ONE specific, important thing that deserves its own data component?"
|
|
243
|
+
- If the answer is no, don't save it or find a more specific selector
|
|
244
|
+
- Multiple similar items = Multiple separate artifact saves (use the tool multiple times) → Multiple data components
|
|
245
|
+
- Each artifact should be independently valuable and uniquely identifiable → Each data component stands alone
|
|
246
|
+
- BETTER to save 3 individual, specific artifacts (3 data components) than 1 generic collection (1 data component)
|
|
247
|
+
|
|
248
|
+
🔄 MULTIPLE ARTIFACTS = MULTIPLE DATA COMPONENTS:
|
|
249
|
+
Remember: Each time you call this tool, you create a separate data component. Call it multiple times for multiple items to create a rich, structured response with individual data components for each important piece of information.`,
|
|
250
|
+
inputSchema,
|
|
251
|
+
execute: async ({ toolCallId, baseSelector, propSelectors, ...rest }, context) => {
|
|
252
|
+
const artifactType = 'artifactType' in rest ? rest.artifactType : undefined;
|
|
253
|
+
if (!sessionId) {
|
|
254
|
+
logger.warn({ toolCallId }, 'No session ID provided to save_tool_result');
|
|
255
|
+
return {
|
|
256
|
+
saved: false,
|
|
257
|
+
error: `[toolCallId: ${toolCallId}] No session context available`,
|
|
258
|
+
artifactIds: [],
|
|
259
|
+
warnings: [],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
// Get the tool result from the session
|
|
263
|
+
const toolResult = toolSessionManager.getToolResult(sessionId, toolCallId);
|
|
264
|
+
if (!toolResult) {
|
|
265
|
+
logger.warn({ toolCallId, sessionId }, 'Tool result not found in session');
|
|
266
|
+
return {
|
|
267
|
+
saved: false,
|
|
268
|
+
error: `[toolCallId: ${toolCallId}] Tool result not found`,
|
|
269
|
+
artifactIds: [],
|
|
270
|
+
warnings: [],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const parsedResult = parseEmbeddedJson(toolResult);
|
|
275
|
+
// Use baseSelector to get to the main data
|
|
276
|
+
const baseData = jmespath.search(parsedResult, baseSelector);
|
|
277
|
+
if (!baseData || (Array.isArray(baseData) && baseData.length === 0)) {
|
|
278
|
+
// Enhanced debugging for failed selectors
|
|
279
|
+
const debugInfo = analyzeSelectorFailure(parsedResult, baseSelector);
|
|
280
|
+
logger.warn({
|
|
281
|
+
baseSelector,
|
|
282
|
+
toolCallId,
|
|
283
|
+
analysis: debugInfo.analysis,
|
|
284
|
+
suggestions: debugInfo.suggestions,
|
|
285
|
+
availableKeys: debugInfo.availableKeys,
|
|
286
|
+
}, 'Base selector returned no results - selector may be incorrect');
|
|
287
|
+
const errorMessage = [
|
|
288
|
+
`[toolCallId: ${toolCallId}] Base selector "${baseSelector}" returned no results.`,
|
|
289
|
+
'',
|
|
290
|
+
'🔍 DETECTED ISSUES: ' + debugInfo.analysis.join(' | '),
|
|
291
|
+
'',
|
|
292
|
+
'💡 SUGGESTIONS:',
|
|
293
|
+
...debugInfo.suggestions.map((s) => ` • ${s}`),
|
|
294
|
+
'',
|
|
295
|
+
'📊 AVAILABLE TOP-LEVEL KEYS: ' + debugInfo.availableKeys.join(', '),
|
|
296
|
+
].join('\n');
|
|
297
|
+
return {
|
|
298
|
+
saved: false,
|
|
299
|
+
error: errorMessage,
|
|
300
|
+
artifactIds: [],
|
|
301
|
+
warnings: [],
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
// Helper function to extract properties from items based on schema
|
|
305
|
+
const extractProps = (items, schema, context = 'default') => {
|
|
306
|
+
const failedSelectors = [];
|
|
307
|
+
const extractedItems = items.map((item, index) => {
|
|
308
|
+
const extractedItem = {};
|
|
309
|
+
const schemaProperties = schema?.properties || {};
|
|
310
|
+
for (const [propName, propSchema] of Object.entries(schemaProperties)) {
|
|
311
|
+
const propSelector = propSelectors[propName];
|
|
312
|
+
if (propSelector) {
|
|
313
|
+
try {
|
|
314
|
+
const propValue = jmespath.search(item, propSelector);
|
|
315
|
+
if (propValue !== null && propValue !== undefined) {
|
|
316
|
+
extractedItem[propName] = propValue;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
// PropSelector didn't match anything - try fallback to direct property access
|
|
320
|
+
const fallbackValue = item[propName];
|
|
321
|
+
if (fallbackValue !== null && fallbackValue !== undefined) {
|
|
322
|
+
extractedItem[propName] = fallbackValue;
|
|
323
|
+
logger.info({ propName, propSelector, context }, `PropSelector failed, used fallback direct property access`);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
failedSelectors.push(`${propName}: "${propSelector}"`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
// JMESPath syntax error - try fallback
|
|
332
|
+
const fallbackValue = item[propName];
|
|
333
|
+
if (fallbackValue !== null && fallbackValue !== undefined) {
|
|
334
|
+
extractedItem[propName] = fallbackValue;
|
|
335
|
+
logger.warn({ propName, propSelector, context, error: error.message }, `PropSelector syntax error, used fallback direct property access`);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
failedSelectors.push(`${propName}: "${propSelector}" (syntax error)`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return extractedItem;
|
|
344
|
+
});
|
|
345
|
+
// Return both the extracted items and any failed selectors for error reporting
|
|
346
|
+
return { extractedItems, failedSelectors };
|
|
347
|
+
};
|
|
348
|
+
// Find the matching artifact component
|
|
349
|
+
const targetArtifactComponent = artifactComponents?.find((ac) => ac.name === artifactType);
|
|
350
|
+
// Normalize baseData to always be an array
|
|
351
|
+
const dataItems = Array.isArray(baseData) ? baseData : [baseData];
|
|
352
|
+
// Extract data based on dataComponent configuration
|
|
353
|
+
let summaryData;
|
|
354
|
+
let fullData;
|
|
355
|
+
const allFailedSelectors = [];
|
|
356
|
+
if (targetArtifactComponent?.summaryProps || targetArtifactComponent?.fullProps) {
|
|
357
|
+
// ArtifactComponent format - summaryProps and fullProps are direct properties
|
|
358
|
+
if (targetArtifactComponent.summaryProps) {
|
|
359
|
+
const summaryResult = extractProps(dataItems, targetArtifactComponent.summaryProps, 'summary');
|
|
360
|
+
summaryData = summaryResult.extractedItems;
|
|
361
|
+
allFailedSelectors.push(...summaryResult.failedSelectors.map((s) => `summary.${s}`));
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
summaryData = [];
|
|
365
|
+
}
|
|
366
|
+
if (targetArtifactComponent.fullProps) {
|
|
367
|
+
const fullResult = extractProps(dataItems, targetArtifactComponent.fullProps, 'full');
|
|
368
|
+
fullData = fullResult.extractedItems;
|
|
369
|
+
allFailedSelectors.push(...fullResult.failedSelectors.map((s) => `full.${s}`));
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
fullData = [];
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
// Backward compatibility: use all propSelectors (current behavior)
|
|
377
|
+
const allPropsSchema = {
|
|
378
|
+
properties: Object.keys(propSelectors).reduce((acc, key) => {
|
|
379
|
+
acc[key] = {}; // Schema doesn't matter for this case
|
|
380
|
+
return acc;
|
|
381
|
+
}, {}),
|
|
382
|
+
};
|
|
383
|
+
const structuredResult = extractProps(dataItems, allPropsSchema, 'structured');
|
|
384
|
+
summaryData = structuredResult.extractedItems;
|
|
385
|
+
fullData = structuredResult.extractedItems;
|
|
386
|
+
allFailedSelectors.push(...structuredResult.failedSelectors);
|
|
387
|
+
}
|
|
388
|
+
// If there are failed selectors, include them in the response as warnings
|
|
389
|
+
const warnings = allFailedSelectors.length > 0
|
|
390
|
+
? [
|
|
391
|
+
`[toolCallId: ${toolCallId}] [artifactType: ${artifactType || 'unknown'}] Some propSelectors failed: ${allFailedSelectors.join(', ')}. Used fallback direct property access where possible.`,
|
|
392
|
+
]
|
|
393
|
+
: [];
|
|
394
|
+
// Prepare artifact data for later processing (not saving immediately)
|
|
395
|
+
const artifactDataItems = dataItems.map((item, index) => ({
|
|
396
|
+
artifactId: nanoid(),
|
|
397
|
+
summaryData: summaryData[index] || {},
|
|
398
|
+
fullData: fullData[index] || {},
|
|
399
|
+
metadata: {
|
|
400
|
+
toolName: toolResult.toolName,
|
|
401
|
+
toolCallId: toolResult.toolCallId,
|
|
402
|
+
baseSelector,
|
|
403
|
+
propSelectors,
|
|
404
|
+
artifactType,
|
|
405
|
+
sessionId: sessionId,
|
|
406
|
+
itemIndex: index,
|
|
407
|
+
totalItems: dataItems.length,
|
|
408
|
+
},
|
|
409
|
+
}));
|
|
410
|
+
const session = toolSessionManager.getSession(sessionId);
|
|
411
|
+
const taskId = session?.taskId;
|
|
412
|
+
// Record artifact preparation events in GraphSession for out-of-band processing
|
|
413
|
+
// The actual saving will happen after name/description generation
|
|
414
|
+
if (streamRequestId && agentId && session) {
|
|
415
|
+
artifactDataItems.forEach((artifactData, index) => {
|
|
416
|
+
// Record event that will trigger name/description generation
|
|
417
|
+
graphSessionManager.recordEvent(streamRequestId, 'artifact_saved', agentId, {
|
|
418
|
+
artifactId: artifactData.artifactId,
|
|
419
|
+
taskId: taskId || 'unknown',
|
|
420
|
+
artifactType: artifactType || 'unknown',
|
|
421
|
+
summaryProps: artifactData.summaryData,
|
|
422
|
+
fullProps: artifactData.fullData,
|
|
423
|
+
metadata: artifactData.metadata,
|
|
424
|
+
// Session info needed for saving to ledger
|
|
425
|
+
tenantId: session.tenantId,
|
|
426
|
+
projectId: session.projectId,
|
|
427
|
+
contextId: session.contextId,
|
|
428
|
+
// Mark as pending - needs name/description generation
|
|
429
|
+
pendingGeneration: true,
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
// Build artifacts object with only the essential data
|
|
434
|
+
const artifacts = artifactDataItems.reduce((acc, artifactData, index) => {
|
|
435
|
+
acc[`${artifactData.artifactId}:${taskId}`] = {
|
|
436
|
+
artifactId: artifactData.artifactId,
|
|
437
|
+
artifactType,
|
|
438
|
+
taskId: taskId,
|
|
439
|
+
summaryData: artifactData.summaryData,
|
|
440
|
+
};
|
|
441
|
+
return acc;
|
|
442
|
+
}, {});
|
|
443
|
+
return {
|
|
444
|
+
saved: true,
|
|
445
|
+
artifacts: Object.values(artifacts).map((a) => ({
|
|
446
|
+
artifactId: a.artifactId,
|
|
447
|
+
taskId: a.taskId,
|
|
448
|
+
summaryData: a.summaryData,
|
|
449
|
+
})),
|
|
450
|
+
warnings,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
logger.error({ error, toolCallId, sessionId }, 'Error processing save_tool_result');
|
|
455
|
+
return {
|
|
456
|
+
saved: false,
|
|
457
|
+
error: `[toolCallId: ${toolCallId}] ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
458
|
+
warnings: [],
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { A2ATask, A2ATaskResult } from '../a2a/types.js';
|
|
2
|
+
import { type AgentApiSelect, type AgentConversationHistoryConfig } from '@inkeep/agents-core';
|
|
3
|
+
/** Turn any string value that is valid JSON into an object/array (in place). */
|
|
4
|
+
export declare function parseEmbeddedJson<T>(data: T): T;
|
|
5
|
+
/**
|
|
6
|
+
* Serializable configuration for creating task handlers
|
|
7
|
+
*/
|
|
8
|
+
export interface TaskHandlerConfig {
|
|
9
|
+
tenantId: string;
|
|
10
|
+
projectId: string;
|
|
11
|
+
graphId: string;
|
|
12
|
+
agentId: string;
|
|
13
|
+
agentSchema: AgentApiSelect;
|
|
14
|
+
name: string;
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
contextConfigId?: string;
|
|
19
|
+
conversationHistoryConfig?: AgentConversationHistoryConfig;
|
|
20
|
+
}
|
|
21
|
+
export declare const createTaskHandler: (config: TaskHandlerConfig) => (task: A2ATask) => Promise<A2ATaskResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Serializes a TaskHandlerConfig to JSON
|
|
24
|
+
*/
|
|
25
|
+
export declare const serializeTaskHandlerConfig: (config: TaskHandlerConfig) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Deserializes a TaskHandlerConfig from JSON
|
|
28
|
+
*/
|
|
29
|
+
export declare const deserializeTaskHandlerConfig: (configJson: string) => TaskHandlerConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Creates a task handler configuration from agent data
|
|
32
|
+
*/
|
|
33
|
+
export declare const createTaskHandlerConfig: (params: {
|
|
34
|
+
tenantId: string;
|
|
35
|
+
projectId: string;
|
|
36
|
+
graphId: string;
|
|
37
|
+
agentId: string;
|
|
38
|
+
baseUrl: string;
|
|
39
|
+
apiKey?: string;
|
|
40
|
+
}) => Promise<TaskHandlerConfig>;
|
|
41
|
+
//# sourceMappingURL=generateTaskHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateTaskHandler.d.ts","sourceRoot":"","sources":["../../src/agents/generateTaskHandler.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,8BAA8B,EAQpC,MAAM,qBAAqB,CAAC;AAM7B,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAS/C;AAID;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,cAAc,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB,CAAC,EAAE,8BAA8B,CAAC;CAC5D;AAED,eAAO,MAAM,iBAAiB,GAAI,QAAQ,iBAAiB,MAC3C,MAAM,OAAO,KAAG,OAAO,CAAC,aAAa,CAgUpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,iBAAiB,KAAG,MAEtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,GAAI,YAAY,MAAM,KAAG,iBAEjE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAU,QAAQ;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,KAAG,OAAO,CAAC,iBAAiB,CAgD5B,CAAC"}
|