@inkeep/agents-api 0.0.0-dev-20260122085034 → 0.0.0-dev-20260122090358
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/.well-known/workflow/v1/manifest.debug.json +11 -11
- package/dist/domains/evals/routes/datasetTriggers.d.ts +2 -2
- package/dist/domains/evals/routes/index.d.ts +2 -2
- package/dist/domains/evals/workflow/routes.d.ts +2 -2
- package/dist/domains/evals/workflow/world.js +3 -2
- package/dist/domains/manage/routes/conversations.d.ts +2 -2
- package/dist/domains/manage/routes/evals/evaluationResults.d.ts +2 -2
- package/dist/domains/manage/routes/index.d.ts +2 -2
- package/dist/domains/manage/routes/mcp.d.ts +2 -2
- package/dist/domains/run/routes/webhooks.js +33 -340
- package/dist/domains/run/services/TriggerService.d.ts +31 -0
- package/dist/domains/run/services/TriggerService.js +377 -0
- package/dist/factory.d.ts +266 -266
- package/dist/index.d.ts +265 -265
- package/dist/middleware/manageAuth.d.ts +2 -2
- package/dist/middleware/projectAccess.d.ts +2 -2
- package/dist/middleware/projectConfig.d.ts +3 -3
- package/dist/middleware/requirePermission.d.ts +2 -2
- package/dist/middleware/runAuth.d.ts +4 -4
- package/dist/middleware/sessionAuth.d.ts +3 -3
- package/dist/middleware/tenantAccess.d.ts +2 -2
- package/dist/middleware/tracing.d.ts +3 -3
- package/package.json +4 -3
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { getLogger as getLogger$1 } from "../../../logger.js";
|
|
2
|
+
import { env } from "../../../env.js";
|
|
3
|
+
import manageDbPool_default from "../../../data/db/manageDbPool.js";
|
|
4
|
+
import runDbClient_default from "../../../data/db/runDbClient.js";
|
|
5
|
+
import { createSSEStreamHelper } from "../utils/stream-helpers.js";
|
|
6
|
+
import { ExecutionHandler } from "../handlers/executionHandler.js";
|
|
7
|
+
import { JsonTransformer, createMessage, createOrGetConversation, createTriggerInvocation, generateId, getConversationId, getFullProjectWithRelationIds, getTriggerById, interpolateTemplate, setActiveAgentForConversation, updateTriggerInvocationStatus, verifySigningSecret, verifyTriggerAuth, withRef } from "@inkeep/agents-core";
|
|
8
|
+
import Ajv from "ajv";
|
|
9
|
+
|
|
10
|
+
//#region src/domains/run/services/TriggerService.ts
|
|
11
|
+
const logger = getLogger$1("TriggerService");
|
|
12
|
+
const ajv = new Ajv({ allErrors: true });
|
|
13
|
+
/**
|
|
14
|
+
* Process a trigger webhook request.
|
|
15
|
+
* Handles validation, transformation, and dispatches async execution.
|
|
16
|
+
*/
|
|
17
|
+
async function processWebhook(params) {
|
|
18
|
+
const { tenantId, projectId, agentId, triggerId, resolvedRef, rawBody, honoContext } = params;
|
|
19
|
+
const trigger = await loadTrigger({
|
|
20
|
+
tenantId,
|
|
21
|
+
projectId,
|
|
22
|
+
agentId,
|
|
23
|
+
triggerId,
|
|
24
|
+
resolvedRef
|
|
25
|
+
});
|
|
26
|
+
if (!trigger) return {
|
|
27
|
+
success: false,
|
|
28
|
+
error: `Trigger ${triggerId} not found`,
|
|
29
|
+
status: 404
|
|
30
|
+
};
|
|
31
|
+
if (!trigger.enabled) return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: "Trigger is disabled",
|
|
34
|
+
status: 404
|
|
35
|
+
};
|
|
36
|
+
const payload = rawBody ? JSON.parse(rawBody) : {};
|
|
37
|
+
const authResult = await verifyAuthentication(trigger, honoContext);
|
|
38
|
+
if (!authResult.success) return authResult;
|
|
39
|
+
const signatureResult = verifySignature(trigger, honoContext, rawBody);
|
|
40
|
+
if (!signatureResult.success) return signatureResult;
|
|
41
|
+
const validationResult = validatePayload(trigger, payload);
|
|
42
|
+
if (!validationResult.success) return validationResult;
|
|
43
|
+
const transformResult = await transformPayload(trigger, payload, {
|
|
44
|
+
tenantId,
|
|
45
|
+
projectId,
|
|
46
|
+
triggerId
|
|
47
|
+
});
|
|
48
|
+
if (!transformResult.success) return transformResult;
|
|
49
|
+
const transformedPayload = transformResult.payload;
|
|
50
|
+
const { messageParts, userMessageText } = buildMessage(trigger, transformedPayload, triggerId);
|
|
51
|
+
const { invocationId, conversationId } = await dispatchExecution({
|
|
52
|
+
tenantId,
|
|
53
|
+
projectId,
|
|
54
|
+
agentId,
|
|
55
|
+
triggerId,
|
|
56
|
+
resolvedRef,
|
|
57
|
+
payload,
|
|
58
|
+
transformedPayload,
|
|
59
|
+
messageParts,
|
|
60
|
+
userMessageText
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
invocationId,
|
|
65
|
+
conversationId
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function loadTrigger(params) {
|
|
69
|
+
const { tenantId, projectId, agentId, triggerId, resolvedRef } = params;
|
|
70
|
+
return await withRef(manageDbPool_default, resolvedRef, (db) => getTriggerById(db)({
|
|
71
|
+
scopes: {
|
|
72
|
+
tenantId,
|
|
73
|
+
projectId,
|
|
74
|
+
agentId
|
|
75
|
+
},
|
|
76
|
+
triggerId
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
async function verifyAuthentication(trigger, honoContext) {
|
|
80
|
+
if (!trigger.authentication) return { success: true };
|
|
81
|
+
const authResult = await verifyTriggerAuth(honoContext, trigger.authentication);
|
|
82
|
+
if (!authResult.success) {
|
|
83
|
+
if (authResult.status === 401) return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: authResult.message || "Unauthorized",
|
|
86
|
+
status: 401
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: authResult.message || "Forbidden",
|
|
91
|
+
status: 403
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return { success: true };
|
|
95
|
+
}
|
|
96
|
+
function verifySignature(trigger, honoContext, rawBody) {
|
|
97
|
+
if (!trigger.signingSecret) return { success: true };
|
|
98
|
+
const signatureResult = verifySigningSecret(honoContext, trigger.signingSecret, rawBody);
|
|
99
|
+
if (!signatureResult.success) return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: signatureResult.message || "Invalid signature",
|
|
102
|
+
status: 403
|
|
103
|
+
};
|
|
104
|
+
return { success: true };
|
|
105
|
+
}
|
|
106
|
+
function validatePayload(trigger, payload) {
|
|
107
|
+
if (!trigger.inputSchema) return { success: true };
|
|
108
|
+
const validate = ajv.compile(trigger.inputSchema);
|
|
109
|
+
if (!validate(payload)) return {
|
|
110
|
+
success: false,
|
|
111
|
+
error: "Payload validation failed",
|
|
112
|
+
status: 400,
|
|
113
|
+
validationErrors: validate.errors?.map((err) => `${err.instancePath} ${err.message}`)
|
|
114
|
+
};
|
|
115
|
+
return { success: true };
|
|
116
|
+
}
|
|
117
|
+
async function transformPayload(trigger, payload, context) {
|
|
118
|
+
if (!trigger.outputTransform) return {
|
|
119
|
+
success: true,
|
|
120
|
+
payload
|
|
121
|
+
};
|
|
122
|
+
try {
|
|
123
|
+
const transformedPayload = await JsonTransformer.transformWithConfig(payload, trigger.outputTransform);
|
|
124
|
+
logger.debug(context, "Payload transformation successful");
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
payload: transformedPayload
|
|
128
|
+
};
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
131
|
+
logger.error({
|
|
132
|
+
...context,
|
|
133
|
+
error: errorMessage
|
|
134
|
+
}, "Payload transformation failed");
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
error: `Payload transformation failed: ${errorMessage}`,
|
|
138
|
+
status: 422
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function buildMessage(trigger, transformedPayload, triggerId) {
|
|
143
|
+
const messageParts = [];
|
|
144
|
+
if (trigger.messageTemplate) {
|
|
145
|
+
const payloadForTemplate = typeof transformedPayload === "object" && transformedPayload !== null && !Array.isArray(transformedPayload) ? transformedPayload : {};
|
|
146
|
+
const interpolatedMessage = interpolateTemplate(trigger.messageTemplate, payloadForTemplate);
|
|
147
|
+
messageParts.push({
|
|
148
|
+
kind: "text",
|
|
149
|
+
text: interpolatedMessage
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (transformedPayload != null) messageParts.push({
|
|
153
|
+
kind: "data",
|
|
154
|
+
data: transformedPayload,
|
|
155
|
+
metadata: {
|
|
156
|
+
source: "trigger",
|
|
157
|
+
triggerId
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
messageParts,
|
|
162
|
+
userMessageText: trigger.messageTemplate ? (() => {
|
|
163
|
+
const payloadForTemplate = typeof transformedPayload === "object" && transformedPayload !== null && !Array.isArray(transformedPayload) ? transformedPayload : {};
|
|
164
|
+
return interpolateTemplate(trigger.messageTemplate, payloadForTemplate);
|
|
165
|
+
})() : JSON.stringify(transformedPayload)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async function dispatchExecution(params) {
|
|
169
|
+
const { tenantId, projectId, agentId, triggerId, resolvedRef, payload, transformedPayload, messageParts, userMessageText } = params;
|
|
170
|
+
const conversationId = getConversationId();
|
|
171
|
+
const invocationId = generateId();
|
|
172
|
+
await createTriggerInvocation(runDbClient_default)({
|
|
173
|
+
id: invocationId,
|
|
174
|
+
triggerId,
|
|
175
|
+
tenantId,
|
|
176
|
+
projectId,
|
|
177
|
+
agentId,
|
|
178
|
+
conversationId,
|
|
179
|
+
status: "pending",
|
|
180
|
+
requestPayload: payload,
|
|
181
|
+
transformedPayload
|
|
182
|
+
});
|
|
183
|
+
logger.info({
|
|
184
|
+
tenantId,
|
|
185
|
+
projectId,
|
|
186
|
+
agentId,
|
|
187
|
+
triggerId,
|
|
188
|
+
invocationId,
|
|
189
|
+
conversationId
|
|
190
|
+
}, "Trigger invocation created");
|
|
191
|
+
const executionPromise = executeAgentAsync({
|
|
192
|
+
tenantId,
|
|
193
|
+
projectId,
|
|
194
|
+
agentId,
|
|
195
|
+
triggerId,
|
|
196
|
+
invocationId,
|
|
197
|
+
conversationId,
|
|
198
|
+
userMessage: userMessageText,
|
|
199
|
+
messageParts,
|
|
200
|
+
resolvedRef
|
|
201
|
+
});
|
|
202
|
+
if (process.env.VERCEL) import("@vercel/functions").then(({ waitUntil }) => {
|
|
203
|
+
waitUntil(executionPromise);
|
|
204
|
+
}).catch((err) => {
|
|
205
|
+
logger.error({ err }, "Failed to import @vercel/functions");
|
|
206
|
+
});
|
|
207
|
+
else executionPromise.catch((error) => {
|
|
208
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
209
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
210
|
+
logger.error({
|
|
211
|
+
err: errorMessage,
|
|
212
|
+
errorStack,
|
|
213
|
+
tenantId,
|
|
214
|
+
projectId,
|
|
215
|
+
agentId,
|
|
216
|
+
triggerId,
|
|
217
|
+
invocationId
|
|
218
|
+
}, "Background trigger execution failed");
|
|
219
|
+
});
|
|
220
|
+
logger.info({
|
|
221
|
+
tenantId,
|
|
222
|
+
projectId,
|
|
223
|
+
agentId,
|
|
224
|
+
triggerId,
|
|
225
|
+
invocationId,
|
|
226
|
+
conversationId
|
|
227
|
+
}, "Async execution dispatched");
|
|
228
|
+
return {
|
|
229
|
+
invocationId,
|
|
230
|
+
conversationId
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Execute the agent asynchronously.
|
|
235
|
+
* This runs after the webhook response is sent.
|
|
236
|
+
*/
|
|
237
|
+
async function executeAgentAsync(params) {
|
|
238
|
+
const { tenantId, projectId, agentId, triggerId, invocationId, conversationId, userMessage, messageParts, resolvedRef } = params;
|
|
239
|
+
logger.info({
|
|
240
|
+
tenantId,
|
|
241
|
+
projectId,
|
|
242
|
+
agentId,
|
|
243
|
+
triggerId,
|
|
244
|
+
invocationId,
|
|
245
|
+
conversationId
|
|
246
|
+
}, "Starting async trigger execution");
|
|
247
|
+
try {
|
|
248
|
+
const project = await withRef(manageDbPool_default, resolvedRef, async (db) => {
|
|
249
|
+
return await getFullProjectWithRelationIds(db)({ scopes: {
|
|
250
|
+
tenantId,
|
|
251
|
+
projectId
|
|
252
|
+
} });
|
|
253
|
+
});
|
|
254
|
+
if (!project) throw new Error(`Project ${projectId} not found`);
|
|
255
|
+
const agent = project.agents?.[agentId];
|
|
256
|
+
if (!agent) throw new Error(`Agent ${agentId} not found in project`);
|
|
257
|
+
const defaultSubAgentId = agent.defaultSubAgentId;
|
|
258
|
+
if (!defaultSubAgentId) throw new Error(`Agent ${agentId} has no default sub-agent configured`);
|
|
259
|
+
await createOrGetConversation(runDbClient_default)({
|
|
260
|
+
id: conversationId,
|
|
261
|
+
tenantId,
|
|
262
|
+
projectId,
|
|
263
|
+
agentId,
|
|
264
|
+
activeSubAgentId: defaultSubAgentId,
|
|
265
|
+
ref: resolvedRef
|
|
266
|
+
});
|
|
267
|
+
await setActiveAgentForConversation(runDbClient_default)({
|
|
268
|
+
scopes: {
|
|
269
|
+
tenantId,
|
|
270
|
+
projectId
|
|
271
|
+
},
|
|
272
|
+
conversationId,
|
|
273
|
+
subAgentId: defaultSubAgentId,
|
|
274
|
+
agentId,
|
|
275
|
+
ref: resolvedRef
|
|
276
|
+
});
|
|
277
|
+
await createMessage(runDbClient_default)({
|
|
278
|
+
id: generateId(),
|
|
279
|
+
tenantId,
|
|
280
|
+
projectId,
|
|
281
|
+
conversationId,
|
|
282
|
+
role: "user",
|
|
283
|
+
content: {
|
|
284
|
+
text: userMessage,
|
|
285
|
+
parts: messageParts
|
|
286
|
+
},
|
|
287
|
+
metadata: { a2a_metadata: {
|
|
288
|
+
triggerId,
|
|
289
|
+
invocationId
|
|
290
|
+
} }
|
|
291
|
+
});
|
|
292
|
+
const executionContext = {
|
|
293
|
+
tenantId,
|
|
294
|
+
projectId,
|
|
295
|
+
agentId,
|
|
296
|
+
baseUrl: env.INKEEP_AGENTS_API_URL || "http://localhost:3002",
|
|
297
|
+
apiKey: env.INKEEP_AGENTS_RUN_API_BYPASS_SECRET || "",
|
|
298
|
+
apiKeyId: "trigger-invocation",
|
|
299
|
+
resolvedRef,
|
|
300
|
+
project,
|
|
301
|
+
metadata: { initiatedBy: {
|
|
302
|
+
type: "api_key",
|
|
303
|
+
id: triggerId
|
|
304
|
+
} }
|
|
305
|
+
};
|
|
306
|
+
const requestId = `trigger-${invocationId}`;
|
|
307
|
+
const noOpStreamHelper = createSSEStreamHelper({
|
|
308
|
+
writeSSE: async () => {},
|
|
309
|
+
sleep: async () => {}
|
|
310
|
+
}, requestId, Math.floor(Date.now() / 1e3));
|
|
311
|
+
await new ExecutionHandler().execute({
|
|
312
|
+
executionContext,
|
|
313
|
+
conversationId,
|
|
314
|
+
userMessage,
|
|
315
|
+
messageParts,
|
|
316
|
+
initialAgentId: agentId,
|
|
317
|
+
requestId,
|
|
318
|
+
sseHelper: noOpStreamHelper,
|
|
319
|
+
emitOperations: false
|
|
320
|
+
});
|
|
321
|
+
await updateTriggerInvocationStatus(runDbClient_default)({
|
|
322
|
+
scopes: {
|
|
323
|
+
tenantId,
|
|
324
|
+
projectId,
|
|
325
|
+
agentId
|
|
326
|
+
},
|
|
327
|
+
triggerId,
|
|
328
|
+
invocationId,
|
|
329
|
+
data: { status: "success" }
|
|
330
|
+
});
|
|
331
|
+
logger.info({
|
|
332
|
+
tenantId,
|
|
333
|
+
projectId,
|
|
334
|
+
agentId,
|
|
335
|
+
triggerId,
|
|
336
|
+
invocationId,
|
|
337
|
+
conversationId
|
|
338
|
+
}, "Async trigger execution completed successfully");
|
|
339
|
+
} catch (error) {
|
|
340
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
341
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
342
|
+
logger.error({
|
|
343
|
+
err: errorMessage,
|
|
344
|
+
errorStack,
|
|
345
|
+
tenantId,
|
|
346
|
+
projectId,
|
|
347
|
+
agentId,
|
|
348
|
+
triggerId,
|
|
349
|
+
invocationId
|
|
350
|
+
}, "Async trigger execution failed");
|
|
351
|
+
try {
|
|
352
|
+
await updateTriggerInvocationStatus(runDbClient_default)({
|
|
353
|
+
scopes: {
|
|
354
|
+
tenantId,
|
|
355
|
+
projectId,
|
|
356
|
+
agentId
|
|
357
|
+
},
|
|
358
|
+
triggerId,
|
|
359
|
+
invocationId,
|
|
360
|
+
data: {
|
|
361
|
+
status: "failed",
|
|
362
|
+
errorMessage
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
} catch (updateError) {
|
|
366
|
+
const updateErrorMessage = updateError instanceof Error ? updateError.message : String(updateError);
|
|
367
|
+
logger.error({
|
|
368
|
+
err: updateErrorMessage,
|
|
369
|
+
invocationId
|
|
370
|
+
}, "Failed to update invocation status to failed");
|
|
371
|
+
}
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
//#endregion
|
|
377
|
+
export { processWebhook };
|