@kilnai/runtime 0.1.15 → 0.9.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 +7 -3
- package/dist/channels/email-api.d.ts +29 -0
- package/dist/channels/email-api.d.ts.map +1 -0
- package/dist/channels/email-api.js +133 -0
- package/dist/channels/email-api.js.map +1 -0
- package/dist/channels/email-channel.d.ts +26 -0
- package/dist/channels/email-channel.d.ts.map +1 -0
- package/dist/channels/email-channel.js +59 -0
- package/dist/channels/email-channel.js.map +1 -0
- package/dist/channels/email-template.d.ts +10 -0
- package/dist/channels/email-template.d.ts.map +1 -0
- package/dist/channels/email-template.js +63 -0
- package/dist/channels/email-template.js.map +1 -0
- package/dist/channels/instagram-api.d.ts +29 -0
- package/dist/channels/instagram-api.d.ts.map +1 -0
- package/dist/channels/instagram-api.js +75 -0
- package/dist/channels/instagram-api.js.map +1 -0
- package/dist/channels/instagram-channel.d.ts +24 -0
- package/dist/channels/instagram-channel.d.ts.map +1 -0
- package/dist/channels/instagram-channel.js +55 -0
- package/dist/channels/instagram-channel.js.map +1 -0
- package/dist/channels/message-formatter.d.ts +17 -0
- package/dist/channels/message-formatter.d.ts.map +1 -1
- package/dist/channels/message-formatter.js +27 -0
- package/dist/channels/message-formatter.js.map +1 -1
- package/dist/channels/messenger-api.d.ts +27 -0
- package/dist/channels/messenger-api.d.ts.map +1 -0
- package/dist/channels/messenger-api.js +75 -0
- package/dist/channels/messenger-api.js.map +1 -0
- package/dist/channels/messenger-channel.d.ts +23 -0
- package/dist/channels/messenger-channel.d.ts.map +1 -0
- package/dist/channels/messenger-channel.js +55 -0
- package/dist/channels/messenger-channel.js.map +1 -0
- package/dist/channels/web-channel.d.ts +2 -0
- package/dist/channels/web-channel.d.ts.map +1 -1
- package/dist/channels/web-channel.js +9 -0
- package/dist/channels/web-channel.js.map +1 -1
- package/dist/enrichment/enrichment-runner.d.ts +17 -0
- package/dist/enrichment/enrichment-runner.d.ts.map +1 -0
- package/dist/enrichment/enrichment-runner.js +45 -0
- package/dist/enrichment/enrichment-runner.js.map +1 -0
- package/dist/enrichment/sqlite-enrichment-store.d.ts +14 -0
- package/dist/enrichment/sqlite-enrichment-store.d.ts.map +1 -0
- package/dist/enrichment/sqlite-enrichment-store.js +69 -0
- package/dist/enrichment/sqlite-enrichment-store.js.map +1 -0
- package/dist/gateway/audio-preprocessor.d.ts +11 -0
- package/dist/gateway/audio-preprocessor.d.ts.map +1 -0
- package/dist/gateway/audio-preprocessor.js +76 -0
- package/dist/gateway/audio-preprocessor.js.map +1 -0
- package/dist/gateway/auth-middleware.d.ts.map +1 -1
- package/dist/gateway/auth-middleware.js +10 -2
- package/dist/gateway/auth-middleware.js.map +1 -1
- package/dist/gateway/contact-memory-admin-routes.d.ts +9 -0
- package/dist/gateway/contact-memory-admin-routes.d.ts.map +1 -0
- package/dist/gateway/contact-memory-admin-routes.js +42 -0
- package/dist/gateway/contact-memory-admin-routes.js.map +1 -0
- package/dist/gateway/context-formatter.d.ts +5 -0
- package/dist/gateway/context-formatter.d.ts.map +1 -0
- package/dist/gateway/context-formatter.js +16 -0
- package/dist/gateway/context-formatter.js.map +1 -0
- package/dist/gateway/conversation-event-emitter.d.ts +4 -1
- package/dist/gateway/conversation-event-emitter.d.ts.map +1 -1
- package/dist/gateway/conversation-event-emitter.js +44 -13
- package/dist/gateway/conversation-event-emitter.js.map +1 -1
- package/dist/gateway/dev-routes.d.ts +1 -1
- package/dist/gateway/dev-routes.d.ts.map +1 -1
- package/dist/gateway/dev-routes.js +2 -2
- package/dist/gateway/dev-routes.js.map +1 -1
- package/dist/gateway/email-loop-guard.d.ts +10 -0
- package/dist/gateway/email-loop-guard.d.ts.map +1 -0
- package/dist/gateway/email-loop-guard.js +39 -0
- package/dist/gateway/email-loop-guard.js.map +1 -0
- package/dist/gateway/email-thread-store.d.ts +24 -0
- package/dist/gateway/email-thread-store.d.ts.map +1 -0
- package/dist/gateway/email-thread-store.js +34 -0
- package/dist/gateway/email-thread-store.js.map +1 -0
- package/dist/gateway/email-webhook-routes.d.ts +32 -0
- package/dist/gateway/email-webhook-routes.d.ts.map +1 -0
- package/dist/gateway/email-webhook-routes.js +411 -0
- package/dist/gateway/email-webhook-routes.js.map +1 -0
- package/dist/gateway/enrichment-admin-routes.d.ts +9 -0
- package/dist/gateway/enrichment-admin-routes.d.ts.map +1 -0
- package/dist/gateway/enrichment-admin-routes.js +41 -0
- package/dist/gateway/enrichment-admin-routes.js.map +1 -0
- package/dist/gateway/gateway-routes.d.ts +17 -1
- package/dist/gateway/gateway-routes.d.ts.map +1 -1
- package/dist/gateway/gateway-routes.js +55 -1
- package/dist/gateway/gateway-routes.js.map +1 -1
- package/dist/gateway/gateway-server.d.ts.map +1 -1
- package/dist/gateway/gateway-server.js +250 -14
- package/dist/gateway/gateway-server.js.map +1 -1
- package/dist/gateway/handoff-routes.d.ts +15 -0
- package/dist/gateway/handoff-routes.d.ts.map +1 -0
- package/dist/gateway/handoff-routes.js +253 -0
- package/dist/gateway/handoff-routes.js.map +1 -0
- package/dist/gateway/instagram-webhook-routes.d.ts +30 -0
- package/dist/gateway/instagram-webhook-routes.d.ts.map +1 -0
- package/dist/gateway/instagram-webhook-routes.js +394 -0
- package/dist/gateway/instagram-webhook-routes.js.map +1 -0
- package/dist/gateway/knowledge-admin-routes.d.ts +9 -0
- package/dist/gateway/knowledge-admin-routes.d.ts.map +1 -0
- package/dist/gateway/knowledge-admin-routes.js +84 -0
- package/dist/gateway/knowledge-admin-routes.js.map +1 -0
- package/dist/gateway/knowledge-factory.d.ts +19 -0
- package/dist/gateway/knowledge-factory.d.ts.map +1 -0
- package/dist/gateway/knowledge-factory.js +122 -0
- package/dist/gateway/knowledge-factory.js.map +1 -0
- package/dist/gateway/message-pipeline.d.ts +69 -0
- package/dist/gateway/message-pipeline.d.ts.map +1 -0
- package/dist/gateway/message-pipeline.js +185 -0
- package/dist/gateway/message-pipeline.js.map +1 -0
- package/dist/gateway/messenger-webhook-routes.d.ts +30 -0
- package/dist/gateway/messenger-webhook-routes.d.ts.map +1 -0
- package/dist/gateway/messenger-webhook-routes.js +394 -0
- package/dist/gateway/messenger-webhook-routes.js.map +1 -0
- package/dist/gateway/meta-webhook-foundation.d.ts +12 -0
- package/dist/gateway/meta-webhook-foundation.d.ts.map +1 -0
- package/dist/gateway/meta-webhook-foundation.js +25 -0
- package/dist/gateway/meta-webhook-foundation.js.map +1 -0
- package/dist/gateway/mode-b-routes.d.ts +8 -0
- package/dist/gateway/mode-b-routes.d.ts.map +1 -1
- package/dist/gateway/mode-b-routes.js +74 -36
- package/dist/gateway/mode-b-routes.js.map +1 -1
- package/dist/gateway/outbound-routes.d.ts.map +1 -1
- package/dist/gateway/outbound-routes.js +49 -21
- package/dist/gateway/outbound-routes.js.map +1 -1
- package/dist/gateway/routing-test-routes.d.ts +10 -0
- package/dist/gateway/routing-test-routes.d.ts.map +1 -0
- package/dist/gateway/routing-test-routes.js +76 -0
- package/dist/gateway/routing-test-routes.js.map +1 -0
- package/dist/gateway/sqlite-email-thread-store.d.ts +13 -0
- package/dist/gateway/sqlite-email-thread-store.d.ts.map +1 -0
- package/dist/gateway/sqlite-email-thread-store.js +66 -0
- package/dist/gateway/sqlite-email-thread-store.js.map +1 -0
- package/dist/gateway/stt-factory.d.ts +3 -0
- package/dist/gateway/stt-factory.d.ts.map +1 -0
- package/dist/gateway/stt-factory.js +21 -0
- package/dist/gateway/stt-factory.js.map +1 -0
- package/dist/gateway/tenant-admin-routes.d.ts.map +1 -1
- package/dist/gateway/tenant-admin-routes.js +17 -3
- package/dist/gateway/tenant-admin-routes.js.map +1 -1
- package/dist/gateway/tenant-routes.d.ts.map +1 -1
- package/dist/gateway/tenant-routes.js +18 -27
- package/dist/gateway/tenant-routes.js.map +1 -1
- package/dist/gateway/tenant-tool-factory.d.ts +10 -0
- package/dist/gateway/tenant-tool-factory.d.ts.map +1 -0
- package/dist/gateway/tenant-tool-factory.js +55 -0
- package/dist/gateway/tenant-tool-factory.js.map +1 -0
- package/dist/gateway/trace-context.d.ts +8 -0
- package/dist/gateway/trace-context.d.ts.map +1 -0
- package/dist/gateway/trace-context.js +17 -0
- package/dist/gateway/trace-context.js.map +1 -0
- package/dist/gateway/webhook-dedup.d.ts +13 -0
- package/dist/gateway/webhook-dedup.d.ts.map +1 -0
- package/dist/gateway/webhook-dedup.js +37 -0
- package/dist/gateway/webhook-dedup.js.map +1 -0
- package/dist/gateway/webhook-tool-executor.d.ts +17 -0
- package/dist/gateway/webhook-tool-executor.d.ts.map +1 -0
- package/dist/gateway/webhook-tool-executor.js +69 -0
- package/dist/gateway/webhook-tool-executor.js.map +1 -0
- package/dist/gateway/whatsapp-webhook-routes.d.ts +11 -0
- package/dist/gateway/whatsapp-webhook-routes.d.ts.map +1 -1
- package/dist/gateway/whatsapp-webhook-routes.js +205 -33
- package/dist/gateway/whatsapp-webhook-routes.js.map +1 -1
- package/dist/gateway/ws-tenant-routes.d.ts +9 -0
- package/dist/gateway/ws-tenant-routes.d.ts.map +1 -1
- package/dist/gateway/ws-tenant-routes.js +184 -7
- package/dist/gateway/ws-tenant-routes.js.map +1 -1
- package/dist/index.d.ts +63 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/dist/observability/composite-event-store.d.ts +13 -0
- package/dist/observability/composite-event-store.d.ts.map +1 -0
- package/dist/observability/composite-event-store.js +37 -0
- package/dist/observability/composite-event-store.js.map +1 -0
- package/dist/observability/prometheus-collector.d.ts +33 -0
- package/dist/observability/prometheus-collector.d.ts.map +1 -0
- package/dist/observability/prometheus-collector.js +170 -0
- package/dist/observability/prometheus-collector.js.map +1 -0
- package/dist/session/agent-handoff-summarizer.d.ts +11 -0
- package/dist/session/agent-handoff-summarizer.d.ts.map +1 -0
- package/dist/session/agent-handoff-summarizer.js +26 -0
- package/dist/session/agent-handoff-summarizer.js.map +1 -0
- package/dist/session/context-summarizer.d.ts +11 -0
- package/dist/session/context-summarizer.d.ts.map +1 -0
- package/dist/session/context-summarizer.js +23 -0
- package/dist/session/context-summarizer.js.map +1 -0
- package/dist/session/escalation-detector.d.ts +27 -0
- package/dist/session/escalation-detector.d.ts.map +1 -0
- package/dist/session/escalation-detector.js +75 -0
- package/dist/session/escalation-detector.js.map +1 -0
- package/dist/session/in-memory-session-store.d.ts +11 -0
- package/dist/session/in-memory-session-store.d.ts.map +1 -0
- package/dist/session/in-memory-session-store.js +26 -0
- package/dist/session/in-memory-session-store.js.map +1 -0
- package/dist/session/mode-b-orchestrator.d.ts +54 -1
- package/dist/session/mode-b-orchestrator.d.ts.map +1 -1
- package/dist/session/mode-b-orchestrator.js +349 -17
- package/dist/session/mode-b-orchestrator.js.map +1 -1
- package/dist/session/mode-b-session.d.ts +48 -1
- package/dist/session/mode-b-session.d.ts.map +1 -1
- package/dist/session/mode-b-session.js +105 -0
- package/dist/session/mode-b-session.js.map +1 -1
- package/dist/session/redis-session-store.d.ts +22 -0
- package/dist/session/redis-session-store.d.ts.map +1 -0
- package/dist/session/redis-session-store.js +44 -0
- package/dist/session/redis-session-store.js.map +1 -0
- package/dist/session/session-mode.d.ts +4 -0
- package/dist/session/session-mode.d.ts.map +1 -0
- package/dist/session/session-mode.js +20 -0
- package/dist/session/session-mode.js.map +1 -0
- package/dist/session/session-registry.d.ts +17 -9
- package/dist/session/session-registry.d.ts.map +1 -1
- package/dist/session/session-registry.js +80 -29
- package/dist/session/session-registry.js.map +1 -1
- package/dist/session/session-serializer.d.ts +4 -0
- package/dist/session/session-serializer.d.ts.map +1 -0
- package/dist/session/session-serializer.js +26 -0
- package/dist/session/session-serializer.js.map +1 -0
- package/dist/session/session-store.d.ts +9 -0
- package/dist/session/session-store.d.ts.map +1 -0
- package/dist/session/session-store.js +2 -0
- package/dist/session/session-store.js.map +1 -0
- package/dist/tenant/agent-resolver.d.ts +26 -0
- package/dist/tenant/agent-resolver.d.ts.map +1 -0
- package/dist/tenant/agent-resolver.js +232 -0
- package/dist/tenant/agent-resolver.js.map +1 -0
- package/dist/tenant/ping-pong-guard.d.ts +9 -0
- package/dist/tenant/ping-pong-guard.d.ts.map +1 -0
- package/dist/tenant/ping-pong-guard.js +30 -0
- package/dist/tenant/ping-pong-guard.js.map +1 -0
- package/dist/tenant/system-prompt-builder.d.ts +1 -1
- package/dist/tenant/system-prompt-builder.d.ts.map +1 -1
- package/dist/tenant/system-prompt-builder.js +13 -1
- package/dist/tenant/system-prompt-builder.js.map +1 -1
- package/dist/tenant/tenant-registry.d.ts +3 -0
- package/dist/tenant/tenant-registry.d.ts.map +1 -1
- package/dist/tenant/tenant-registry.js +83 -4
- package/dist/tenant/tenant-registry.js.map +1 -1
- package/dist/tenant/tenant-router.d.ts +27 -0
- package/dist/tenant/tenant-router.d.ts.map +1 -0
- package/dist/tenant/tenant-router.js +72 -0
- package/dist/tenant/tenant-router.js.map +1 -0
- package/dist/utils/hmac.d.ts +1 -0
- package/dist/utils/hmac.d.ts.map +1 -1
- package/dist/utils/hmac.js +3 -0
- package/dist/utils/hmac.js.map +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// Gateway: Messenger webhook routes -- Hono sub-app for Facebook Messenger Platform
|
|
2
|
+
// Resolves tenant by Messenger Page ID, processes messages via Mode B orchestrator, replies via Messenger Send API
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { mkdirSync } from "node:fs";
|
|
6
|
+
import { extractText, SqliteMemoryStore } from "@kilnai/core";
|
|
7
|
+
import { toMessengerFormat } from "../channels/message-formatter.js";
|
|
8
|
+
import { resolveAgentContextAsync } from "../tenant/agent-resolver.js";
|
|
9
|
+
import { sendMessengerMessage } from "../channels/messenger-api.js";
|
|
10
|
+
import { checkBudget, reportUsage } from "./budget-middleware.js";
|
|
11
|
+
import { requireWebhookSignature } from "./auth-middleware.js";
|
|
12
|
+
import { verifyMetaWebhook } from "./meta-webhook-foundation.js";
|
|
13
|
+
import { TraceContext } from "./trace-context.js";
|
|
14
|
+
import { preprocessAudio, createGenericMediaDownloader } from "./audio-preprocessor.js";
|
|
15
|
+
import { formatKnowledgeContext, formatContactContext, mergeContextSources } from "./context-formatter.js";
|
|
16
|
+
/** Lazily-opened per-tenant memory stores. Keyed by tenantId. */
|
|
17
|
+
const memoryStores = new Map();
|
|
18
|
+
function getMemoryStore(memoryBasePath, tenantId) {
|
|
19
|
+
let store = memoryStores.get(tenantId);
|
|
20
|
+
if (store)
|
|
21
|
+
return store;
|
|
22
|
+
const dir = join(memoryBasePath, "memory");
|
|
23
|
+
mkdirSync(dir, { recursive: true });
|
|
24
|
+
store = new SqliteMemoryStore({
|
|
25
|
+
dbPath: join(dir, `${tenantId}.db`),
|
|
26
|
+
layer: "user",
|
|
27
|
+
tenantId,
|
|
28
|
+
});
|
|
29
|
+
memoryStores.set(tenantId, store);
|
|
30
|
+
return store;
|
|
31
|
+
}
|
|
32
|
+
/** Tool definition for knowledge_search -- injected when knowledge mode is "tool" */
|
|
33
|
+
const KNOWLEDGE_SEARCH_TOOL = {
|
|
34
|
+
name: "knowledge_search",
|
|
35
|
+
description: "Search the knowledge base for relevant information. Use this when the user asks a question that may be answered by stored documents or knowledge.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
query: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "The search query to find relevant knowledge.",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ["query"],
|
|
45
|
+
},
|
|
46
|
+
tags: new Set(["builtin"]),
|
|
47
|
+
};
|
|
48
|
+
/** Parse a Messenger messaging entry into ContentPart[] */
|
|
49
|
+
function parseMessengerMessageParts(entry) {
|
|
50
|
+
const msg = entry.message;
|
|
51
|
+
if (!msg)
|
|
52
|
+
return null;
|
|
53
|
+
const parts = [];
|
|
54
|
+
if (msg.text) {
|
|
55
|
+
parts.push({ type: "text", text: msg.text });
|
|
56
|
+
}
|
|
57
|
+
if (msg.attachments) {
|
|
58
|
+
for (const att of msg.attachments) {
|
|
59
|
+
if (att.type === "image" && att.payload.url) {
|
|
60
|
+
parts.push({ type: "image", mimeType: "image/jpeg", url: att.payload.url });
|
|
61
|
+
}
|
|
62
|
+
else if (att.type === "audio" && att.payload.url) {
|
|
63
|
+
parts.push({ type: "audio", mimeType: "audio/mp4", url: att.payload.url });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return parts.length > 0 ? parts : null;
|
|
68
|
+
}
|
|
69
|
+
export function createMessengerWebhookRoutes(config) {
|
|
70
|
+
const app = new Hono();
|
|
71
|
+
// GET /webhook -- Meta verification handshake
|
|
72
|
+
app.get("/webhook", (c) => verifyMetaWebhook(c, config.verifyToken));
|
|
73
|
+
// HMAC-SHA256 signature validation for POST webhooks
|
|
74
|
+
if (config.appSecret) {
|
|
75
|
+
app.use("/webhook", requireWebhookSignature(config.appSecret, "x-hub-signature-256"));
|
|
76
|
+
}
|
|
77
|
+
// POST /webhook -- Incoming messages from Messenger
|
|
78
|
+
app.post("/webhook", async (c) => {
|
|
79
|
+
let payload;
|
|
80
|
+
try {
|
|
81
|
+
payload = await c.req.json();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return c.text("OK", 200);
|
|
85
|
+
}
|
|
86
|
+
if (payload.object !== "page" || !payload.entry) {
|
|
87
|
+
return c.text("OK", 200);
|
|
88
|
+
}
|
|
89
|
+
const processPromises = [];
|
|
90
|
+
for (const entry of payload.entry) {
|
|
91
|
+
for (const messaging of entry.messaging) {
|
|
92
|
+
// Filter echo messages (business-sent messages echoed back)
|
|
93
|
+
if (messaging.message?.is_echo)
|
|
94
|
+
continue;
|
|
95
|
+
// Deduplicate -- Meta uses at-least-once delivery
|
|
96
|
+
if (messaging.message?.mid && config.dedup?.isDuplicate(messaging.message.mid)) {
|
|
97
|
+
console.debug(`[messenger] Skipping duplicate message ${messaging.message.mid}`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const senderId = messaging.sender.id;
|
|
101
|
+
const recipientPageId = messaging.recipient.id;
|
|
102
|
+
// Resolve tenant by Messenger Page ID
|
|
103
|
+
const tenant = config.tenantRegistry.resolveByMessengerPageId(recipientPageId, config.appName);
|
|
104
|
+
if (!tenant) {
|
|
105
|
+
const trace = new TraceContext();
|
|
106
|
+
trace.warn("messenger", "No tenant found", { recipientPageId, appName: config.appName });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const msgParts = parseMessengerMessageParts(messaging);
|
|
110
|
+
if (!msgParts)
|
|
111
|
+
continue;
|
|
112
|
+
const promise = processMessengerMessage(config, tenant.tenantId, senderId, msgParts, tenant.messengerAccessToken);
|
|
113
|
+
processPromises.push(promise);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Fire and forget -- log any failures from settled promises
|
|
117
|
+
Promise.allSettled(processPromises).then((results) => {
|
|
118
|
+
for (const result of results) {
|
|
119
|
+
if (result.status === "rejected") {
|
|
120
|
+
const failTrace = new TraceContext();
|
|
121
|
+
failTrace.warn("messenger", "Message processing failed", { error: String(result.reason) });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return c.text("OK", 200);
|
|
126
|
+
});
|
|
127
|
+
return app;
|
|
128
|
+
}
|
|
129
|
+
async function processMessengerMessage(config, tenantId, senderId, messageParts, accessToken) {
|
|
130
|
+
const trace = new TraceContext();
|
|
131
|
+
trace.log("messenger", "Processing message", { tenantId, from: senderId });
|
|
132
|
+
const tenant = config.tenantRegistry.get(tenantId);
|
|
133
|
+
if (!tenant)
|
|
134
|
+
return;
|
|
135
|
+
const resolvedAccessToken = accessToken
|
|
136
|
+
? (process.env[accessToken] ?? accessToken)
|
|
137
|
+
: "";
|
|
138
|
+
// Preprocess audio parts via STT (fail-open) -- Messenger CDN URLs don't need auth
|
|
139
|
+
let processedParts = messageParts;
|
|
140
|
+
if (config.sttAdapter) {
|
|
141
|
+
try {
|
|
142
|
+
const downloader = createGenericMediaDownloader();
|
|
143
|
+
processedParts = await preprocessAudio(messageParts, config.sttAdapter, downloader);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
trace.warn("messenger", "Audio preprocessing failed", { error: err instanceof Error ? err.message : String(err) });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const messageText = extractText(processedParts);
|
|
150
|
+
// --- Memory: recall past context about this user ---
|
|
151
|
+
let recalledMemory;
|
|
152
|
+
if (config.memoryBasePath) {
|
|
153
|
+
try {
|
|
154
|
+
const store = getMemoryStore(config.memoryBasePath, tenantId);
|
|
155
|
+
const query = `${senderId} ${messageText}`;
|
|
156
|
+
recalledMemory = await store.recall(query, 500) || undefined;
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
trace.warn("messenger", "Memory recall failed", { tenantId, error: err instanceof Error ? err.message : String(err) });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// --- Knowledge: retrieve relevant context ---
|
|
163
|
+
let knowledgeContext;
|
|
164
|
+
if (config.knowledgePipeline && messageText.length > 0) {
|
|
165
|
+
const knowledgeMode = config.knowledgeMode ?? "auto";
|
|
166
|
+
if (knowledgeMode === "auto") {
|
|
167
|
+
try {
|
|
168
|
+
const results = await config.knowledgePipeline.retrieve(messageText, { topK: 5 });
|
|
169
|
+
knowledgeContext = formatKnowledgeContext(results);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
trace.warn("messenger", "Knowledge retrieval failed", { error: err instanceof Error ? err.message : String(err) });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// --- Contact memory: recall persistent facts about this user ---
|
|
177
|
+
let contactContext;
|
|
178
|
+
if (config.contactMemoryService) {
|
|
179
|
+
try {
|
|
180
|
+
const facts = await config.contactMemoryService.recall(senderId, tenantId);
|
|
181
|
+
contactContext = formatContactContext(facts);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
trace.warn("messenger", "Contact memory recall failed", { tenantId, error: err instanceof Error ? err.message : String(err) });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const combinedMemory = mergeContextSources(recalledMemory, knowledgeContext, contactContext);
|
|
188
|
+
// --- Tools: build per-call builtin tools ---
|
|
189
|
+
const callTools = new Map();
|
|
190
|
+
// Register knowledge_search tool for "tool" mode
|
|
191
|
+
if (config.knowledgePipeline && (config.knowledgeMode ?? "auto") === "tool") {
|
|
192
|
+
callTools.set("knowledge_search", async (input) => {
|
|
193
|
+
const query = String(input.query ?? "");
|
|
194
|
+
const results = await config.knowledgePipeline.retrieve(query, { topK: 5 });
|
|
195
|
+
return results.map((r) => ({ content: r.content, score: r.score }));
|
|
196
|
+
});
|
|
197
|
+
const hasKnowledgeTool = config.orchestrator.tools?.some((t) => t.name === "knowledge_search");
|
|
198
|
+
if (!hasKnowledgeTool) {
|
|
199
|
+
config.orchestrator.registerTools([KNOWLEDGE_SEARCH_TOOL]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Get or create session first (needed for ping-pong guard)
|
|
203
|
+
const session = await config.sessionRegistry.getOrCreate({
|
|
204
|
+
appName: config.appName,
|
|
205
|
+
tenantId,
|
|
206
|
+
userId: senderId,
|
|
207
|
+
systemPrompt: "",
|
|
208
|
+
idleTimeoutMs: tenant.idleTimeoutMs,
|
|
209
|
+
});
|
|
210
|
+
// Resolve agent context (multi-agent routing with ping-pong guard)
|
|
211
|
+
const agentCtx = await resolveAgentContextAsync(tenant, processedParts, session, { handoffSummarizer: config.handoffSummarizer, eventBus: config.eventBus }, "messenger", callTools);
|
|
212
|
+
// Update session with resolved prompt and agent
|
|
213
|
+
session.setSystemPrompt(agentCtx.systemPrompt);
|
|
214
|
+
if (agentCtx.activeAgentId) {
|
|
215
|
+
session.setActiveAgent(agentCtx.activeAgentId, agentCtx.handoffBrief);
|
|
216
|
+
}
|
|
217
|
+
const tenantToolCtx = agentCtx.tenantToolContext;
|
|
218
|
+
// Register webhook tool definitions on the orchestrator
|
|
219
|
+
if (tenantToolCtx.toolDefinitions.length > 0) {
|
|
220
|
+
config.orchestrator.registerTools(tenantToolCtx.toolDefinitions);
|
|
221
|
+
}
|
|
222
|
+
const perCallConfig = {
|
|
223
|
+
toolAllowlist: tenantToolCtx.toolAllowlist,
|
|
224
|
+
rateLimiter: tenantToolCtx.rateLimiter,
|
|
225
|
+
tenantId: tenant.tenantId,
|
|
226
|
+
additionalTools: tenantToolCtx.toolDefinitions.length > 0 ? tenantToolCtx.toolDefinitions : undefined,
|
|
227
|
+
};
|
|
228
|
+
// --- Budget check ---
|
|
229
|
+
const activeBilling = tenant.billing?.budgetEndpoint
|
|
230
|
+
? tenant.billing
|
|
231
|
+
: config.billing;
|
|
232
|
+
if (activeBilling) {
|
|
233
|
+
const budgetResult = await checkBudget(activeBilling, tenantId);
|
|
234
|
+
if (!budgetResult.allowed) {
|
|
235
|
+
const overBudgetMsg = tenant.billing?.overBudgetMessage
|
|
236
|
+
?? activeBilling.overBudgetMessage ?? "Budget exhausted.";
|
|
237
|
+
trace.log("messenger", "Budget exhausted", { tenantId, sender: senderId });
|
|
238
|
+
try {
|
|
239
|
+
await sendMessengerMessage(resolvedAccessToken, senderId, overBudgetMsg);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
trace.warn("messenger", "Failed to send over-budget reply", { error: err instanceof Error ? err.message : String(err) });
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Emit MESSAGE_RECEIVED event (fire-and-forget)
|
|
248
|
+
if (config.eventEmitter) {
|
|
249
|
+
config.eventEmitter.emit({
|
|
250
|
+
eventType: "MESSAGE_RECEIVED",
|
|
251
|
+
tenantId,
|
|
252
|
+
channel: "messenger",
|
|
253
|
+
externalUserId: senderId,
|
|
254
|
+
messageContent: messageText,
|
|
255
|
+
messageRole: "USER",
|
|
256
|
+
traceId: trace.traceId,
|
|
257
|
+
timestamp: new Date().toISOString(),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
let replyText;
|
|
261
|
+
try {
|
|
262
|
+
const result = await config.orchestrator.processMessage(session, processedParts, combinedMemory, tenantToolCtx.callBuiltinTools.size > 0 ? tenantToolCtx.callBuiltinTools : undefined, perCallConfig);
|
|
263
|
+
// Persist mutated session
|
|
264
|
+
await config.sessionRegistry.save(session);
|
|
265
|
+
// Emit handoff events when message was queued
|
|
266
|
+
if (result.queued && config.eventEmitter) {
|
|
267
|
+
config.eventEmitter.emit({
|
|
268
|
+
eventType: "HANDOFF_MESSAGE_QUEUED",
|
|
269
|
+
tenantId,
|
|
270
|
+
channel: "messenger",
|
|
271
|
+
externalUserId: senderId,
|
|
272
|
+
sessionMode: session.sessionMode,
|
|
273
|
+
traceId: trace.traceId,
|
|
274
|
+
timestamp: new Date().toISOString(),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// Emit escalation event when detected
|
|
278
|
+
if (result.escalation && config.eventEmitter) {
|
|
279
|
+
config.eventEmitter.emit({
|
|
280
|
+
eventType: "ESCALATION_DETECTED",
|
|
281
|
+
tenantId,
|
|
282
|
+
channel: "messenger",
|
|
283
|
+
externalUserId: senderId,
|
|
284
|
+
escalationReason: result.escalation.reason,
|
|
285
|
+
escalationDetail: result.escalation.detail,
|
|
286
|
+
summary: result.contextSummary,
|
|
287
|
+
sessionMode: session.sessionMode,
|
|
288
|
+
traceId: trace.traceId,
|
|
289
|
+
timestamp: new Date().toISOString(),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// Emit TOOL_EXECUTED events
|
|
293
|
+
if (result.toolExecutions && config.eventEmitter) {
|
|
294
|
+
for (const exec of result.toolExecutions) {
|
|
295
|
+
config.eventEmitter.emit({
|
|
296
|
+
eventType: "TOOL_EXECUTED",
|
|
297
|
+
tenantId,
|
|
298
|
+
channel: "messenger",
|
|
299
|
+
externalUserId: senderId,
|
|
300
|
+
toolName: exec.toolName,
|
|
301
|
+
durationMs: exec.durationMs,
|
|
302
|
+
success: exec.success,
|
|
303
|
+
resultSummary: exec.resultSummary,
|
|
304
|
+
traceId: trace.traceId,
|
|
305
|
+
timestamp: new Date().toISOString(),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Emit AGENT_ROUTED when multi-agent routing is active
|
|
310
|
+
if (agentCtx.activeAgentId && config.eventEmitter) {
|
|
311
|
+
config.eventEmitter.emit({
|
|
312
|
+
eventType: "AGENT_ROUTED",
|
|
313
|
+
tenantId,
|
|
314
|
+
channel: "messenger",
|
|
315
|
+
externalUserId: senderId,
|
|
316
|
+
activeAgentId: agentCtx.activeAgentId,
|
|
317
|
+
activeAgentName: agentCtx.activeAgentName,
|
|
318
|
+
routingTier: agentCtx.routingResult?.tier,
|
|
319
|
+
routingConfidence: agentCtx.routingResult?.confidence,
|
|
320
|
+
traceId: trace.traceId,
|
|
321
|
+
timestamp: new Date().toISOString(),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
// Emit AGENT_HANDOFF when an agent switch occurred (or was blocked)
|
|
325
|
+
if ((agentCtx.isHandoff || agentCtx.pingPongBlocked) && config.eventEmitter) {
|
|
326
|
+
const fromAgent = tenant.agents?.find((a) => a.id === agentCtx.previousAgentId);
|
|
327
|
+
const toAgent = tenant.agents?.find((a) => a.id === agentCtx.activeAgentId);
|
|
328
|
+
config.eventEmitter.emit({
|
|
329
|
+
eventType: "AGENT_HANDOFF",
|
|
330
|
+
tenantId,
|
|
331
|
+
channel: "messenger",
|
|
332
|
+
externalUserId: senderId,
|
|
333
|
+
fromAgentId: agentCtx.previousAgentId,
|
|
334
|
+
fromAgentName: fromAgent?.name,
|
|
335
|
+
toAgentId: agentCtx.activeAgentId,
|
|
336
|
+
toAgentName: toAgent?.name,
|
|
337
|
+
handoffBrief: agentCtx.handoffBrief,
|
|
338
|
+
handoffBlocked: agentCtx.pingPongBlocked,
|
|
339
|
+
handoffBlockReason: agentCtx.pingPongReason,
|
|
340
|
+
traceId: trace.traceId,
|
|
341
|
+
timestamp: new Date().toISOString(),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
replyText = toMessengerFormat(extractText(result.parts));
|
|
345
|
+
// Report usage (fire-and-forget)
|
|
346
|
+
if (activeBilling) {
|
|
347
|
+
reportUsage(activeBilling, {
|
|
348
|
+
tenantId,
|
|
349
|
+
messages: 1,
|
|
350
|
+
tokens: result.inputTokens + result.outputTokens,
|
|
351
|
+
model: config.orchestrator.model ?? "unknown",
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
// Emit MESSAGE_SENT event (fire-and-forget)
|
|
355
|
+
if (config.eventEmitter) {
|
|
356
|
+
config.eventEmitter.emit({
|
|
357
|
+
eventType: "MESSAGE_SENT",
|
|
358
|
+
tenantId,
|
|
359
|
+
channel: "messenger",
|
|
360
|
+
externalUserId: senderId,
|
|
361
|
+
messageContent: replyText,
|
|
362
|
+
messageRole: "ASSISTANT",
|
|
363
|
+
traceId: trace.traceId,
|
|
364
|
+
timestamp: new Date().toISOString(),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
trace.error("messenger", "Orchestrator error", { tenantId, error: err instanceof Error ? err.message : String(err) });
|
|
370
|
+
replyText = "Something went wrong. Please try again.";
|
|
371
|
+
}
|
|
372
|
+
// Reply via Messenger Send API
|
|
373
|
+
try {
|
|
374
|
+
await sendMessengerMessage(resolvedAccessToken, senderId, replyText);
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
trace.warn("messenger", "Failed to send reply", { recipient: senderId, error: err instanceof Error ? err.message : String(err) });
|
|
378
|
+
}
|
|
379
|
+
// --- Memory: save what was learned from this exchange ---
|
|
380
|
+
if (config.memoryBasePath && messageText.length > 5) {
|
|
381
|
+
try {
|
|
382
|
+
const store = getMemoryStore(config.memoryBasePath, tenantId);
|
|
383
|
+
await store.save({
|
|
384
|
+
layer: "user",
|
|
385
|
+
content: `[${senderId}] User: ${messageText}\nAssistant: ${replyText}`,
|
|
386
|
+
tags: [senderId],
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
trace.warn("messenger", "Memory save failed", { tenantId, error: err instanceof Error ? err.message : String(err) });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
//# sourceMappingURL=messenger-webhook-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-webhook-routes.js","sourceRoot":"","sources":["../../src/gateway/messenger-webhook-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
/**
|
|
3
|
+
* Handle Meta's GET verification handshake.
|
|
4
|
+
* Returns hub.challenge on valid subscribe requests; 403 otherwise.
|
|
5
|
+
*/
|
|
6
|
+
export declare function verifyMetaWebhook(c: Context, verifyToken: string): Response;
|
|
7
|
+
/**
|
|
8
|
+
* Validate Meta's HMAC-SHA256 signature from `X-Hub-Signature-256` header.
|
|
9
|
+
* Strips the `sha256=` prefix before comparison.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateMetaSignature(body: string, signature: string, appSecret: string): boolean;
|
|
12
|
+
//# sourceMappingURL=meta-webhook-foundation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-webhook-foundation.d.ts","sourceRoot":"","sources":["../../src/gateway/meta-webhook-foundation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,QAAQ,CAS3E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAGjG"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Meta Webhook Foundation -- shared verification, HMAC validation, and entry dispatch
|
|
2
|
+
// Used by WhatsApp, Instagram, and Messenger webhook handlers
|
|
3
|
+
import { verifyHmacSha256 } from "../utils/hmac.js";
|
|
4
|
+
/**
|
|
5
|
+
* Handle Meta's GET verification handshake.
|
|
6
|
+
* Returns hub.challenge on valid subscribe requests; 403 otherwise.
|
|
7
|
+
*/
|
|
8
|
+
export function verifyMetaWebhook(c, verifyToken) {
|
|
9
|
+
const mode = c.req.query("hub.mode");
|
|
10
|
+
const token = c.req.query("hub.verify_token");
|
|
11
|
+
const challenge = c.req.query("hub.challenge");
|
|
12
|
+
if (mode === "subscribe" && token === verifyToken) {
|
|
13
|
+
return c.text(challenge ?? "", 200);
|
|
14
|
+
}
|
|
15
|
+
return c.text("Forbidden", 403);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validate Meta's HMAC-SHA256 signature from `X-Hub-Signature-256` header.
|
|
19
|
+
* Strips the `sha256=` prefix before comparison.
|
|
20
|
+
*/
|
|
21
|
+
export function validateMetaSignature(body, signature, appSecret) {
|
|
22
|
+
const sig = signature.startsWith("sha256=") ? signature.slice(7) : signature;
|
|
23
|
+
return verifyHmacSha256(appSecret, body, sig);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=meta-webhook-foundation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-webhook-foundation.js","sourceRoot":"","sources":["../../src/gateway/meta-webhook-foundation.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,8DAA8D;AAG9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAU,EAAE,WAAmB;IAC/D,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAE/C,IAAI,IAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QAClD,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,SAAiB,EAAE,SAAiB;IACtF,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,OAAO,gBAAgB,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
+
import type { TenantConfig, RetrievalPipeline } from "@kilnai/core";
|
|
2
3
|
import type { ModeBOrchestrator } from "../session/mode-b-orchestrator.js";
|
|
3
4
|
import type { SessionRegistry } from "../session/session-registry.js";
|
|
4
5
|
import type { BillingConfig } from "./budget-middleware.js";
|
|
6
|
+
import type { AgentHandoffSummarizer } from "../session/agent-handoff-summarizer.js";
|
|
7
|
+
import type { EventBus } from "@kilnai/core";
|
|
5
8
|
/** Runtime configuration for a Mode B App */
|
|
6
9
|
export interface ModeBAppRuntime {
|
|
7
10
|
readonly appName: string;
|
|
@@ -10,6 +13,11 @@ export interface ModeBAppRuntime {
|
|
|
10
13
|
readonly billing?: BillingConfig;
|
|
11
14
|
readonly systemPrompt: string;
|
|
12
15
|
readonly apiKey?: string;
|
|
16
|
+
readonly knowledgePipeline?: RetrievalPipeline;
|
|
17
|
+
readonly knowledgeMode?: "auto" | "tool";
|
|
18
|
+
readonly tenant?: TenantConfig;
|
|
19
|
+
readonly handoffSummarizer?: AgentHandoffSummarizer;
|
|
20
|
+
readonly eventBus?: EventBus;
|
|
13
21
|
}
|
|
14
22
|
export declare function createModeBRoutes(runtime: ModeBAppRuntime): Hono;
|
|
15
23
|
//# sourceMappingURL=mode-b-routes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mode-b-routes.d.ts","sourceRoot":"","sources":["../../src/gateway/mode-b-routes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"mode-b-routes.d.ts","sourceRoot":"","sources":["../../src/gateway/mode-b-routes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAe,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI5D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAC/C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAUD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CA6IhE"}
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
// Handles message processing, session listing, and session removal
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { textParts, extractText } from "@kilnai/core";
|
|
5
|
-
import {
|
|
5
|
+
import { formatKnowledgeContext } from "./context-formatter.js";
|
|
6
|
+
import { checkTier } from "./budget-middleware.js";
|
|
6
7
|
import { requireApiKey } from "./auth-middleware.js";
|
|
8
|
+
import { processInboundMessage } from "./message-pipeline.js";
|
|
9
|
+
import { resolveAgentContextAsync } from "../tenant/agent-resolver.js";
|
|
7
10
|
export function createModeBRoutes(runtime) {
|
|
8
11
|
const app = new Hono();
|
|
9
12
|
if (runtime.apiKey) {
|
|
@@ -27,60 +30,95 @@ export function createModeBRoutes(runtime) {
|
|
|
27
30
|
if (!body.userId || typeof body.userId !== "string") {
|
|
28
31
|
return c.json({ error: "userId is required" }, 400);
|
|
29
32
|
}
|
|
30
|
-
//
|
|
31
|
-
if (runtime.billing) {
|
|
32
|
-
const
|
|
33
|
-
if (!
|
|
33
|
+
// Tier enforcement (Mode B specific -- not in the pipeline)
|
|
34
|
+
if (runtime.billing?.tiers && body.plan) {
|
|
35
|
+
const tierResult = checkTier(runtime.billing, body.plan, "fast");
|
|
36
|
+
if (!tierResult.allowed) {
|
|
34
37
|
return c.json({
|
|
35
|
-
content:
|
|
36
|
-
|
|
38
|
+
content: `Tier "fast" is not available on your "${body.plan}" plan. Allowed tiers: ${tierResult.allowedTiers.join(", ")}`,
|
|
39
|
+
tierRestricted: true,
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
42
|
+
}
|
|
43
|
+
// Knowledge retrieval (auto mode)
|
|
44
|
+
let knowledgeContext;
|
|
45
|
+
if (runtime.knowledgePipeline && (runtime.knowledgeMode ?? "auto") === "auto") {
|
|
46
|
+
const queryText = extractText(userParts);
|
|
47
|
+
if (queryText.length > 0) {
|
|
48
|
+
try {
|
|
49
|
+
const results = await runtime.knowledgePipeline.retrieve(queryText, { topK: 5 });
|
|
50
|
+
knowledgeContext = formatKnowledgeContext(results);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// fail-open
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
// Resolve agent context if tenant has multi-agent config
|
|
58
|
+
let systemPrompt = runtime.systemPrompt;
|
|
59
|
+
let activeAgentId;
|
|
60
|
+
let activeAgentName;
|
|
61
|
+
let routingTier;
|
|
62
|
+
let routingConfidence;
|
|
63
|
+
if (runtime.tenant) {
|
|
64
|
+
// Get or create session for ping-pong guard context
|
|
65
|
+
const session = await runtime.sessionRegistry.getOrCreate({
|
|
66
|
+
appName: runtime.appName,
|
|
67
|
+
userId: body.userId,
|
|
68
|
+
systemPrompt: "",
|
|
69
|
+
});
|
|
70
|
+
const agentCtx = await resolveAgentContextAsync(runtime.tenant, userParts, session, { handoffSummarizer: runtime.handoffSummarizer, eventBus: runtime.eventBus });
|
|
71
|
+
systemPrompt = agentCtx.systemPrompt;
|
|
72
|
+
activeAgentId = agentCtx.activeAgentId;
|
|
73
|
+
activeAgentName = agentCtx.activeAgentName;
|
|
74
|
+
routingTier = agentCtx.routingResult?.tier;
|
|
75
|
+
routingConfidence = agentCtx.routingResult?.confidence;
|
|
76
|
+
// Update session with resolved prompt and agent
|
|
77
|
+
session.setSystemPrompt(agentCtx.systemPrompt);
|
|
78
|
+
if (agentCtx.activeAgentId) {
|
|
79
|
+
session.setActiveAgent(agentCtx.activeAgentId, agentCtx.handoffBrief);
|
|
80
|
+
}
|
|
81
|
+
await runtime.sessionRegistry.save(session);
|
|
82
|
+
}
|
|
83
|
+
let processResult;
|
|
58
84
|
try {
|
|
59
|
-
|
|
85
|
+
processResult = await processInboundMessage({
|
|
86
|
+
orchestrator: runtime.orchestrator,
|
|
87
|
+
sessionRegistry: runtime.sessionRegistry,
|
|
88
|
+
appName: runtime.appName,
|
|
89
|
+
userId: body.userId,
|
|
90
|
+
systemPrompt,
|
|
91
|
+
userParts,
|
|
92
|
+
billing: runtime.billing,
|
|
93
|
+
channel: "api",
|
|
94
|
+
knowledgeContext,
|
|
95
|
+
activeAgentId,
|
|
96
|
+
activeAgentName,
|
|
97
|
+
routingTier,
|
|
98
|
+
routingConfidence,
|
|
99
|
+
});
|
|
60
100
|
}
|
|
61
101
|
catch (err) {
|
|
62
102
|
console.error(`[${runtime.appName}] processMessage error:`, err);
|
|
63
103
|
return c.json({ error: String(err) }, 500);
|
|
64
104
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
messages: 1,
|
|
70
|
-
tokens: result.inputTokens + result.outputTokens,
|
|
71
|
-
model: runtime.orchestrator.model ?? "unknown",
|
|
105
|
+
if (!processResult.ok) {
|
|
106
|
+
return c.json({
|
|
107
|
+
content: processResult.budgetDenied.message,
|
|
108
|
+
budgetExhausted: true,
|
|
72
109
|
});
|
|
73
110
|
}
|
|
111
|
+
const result = processResult.result;
|
|
74
112
|
return c.json({
|
|
75
113
|
content: extractText(result.parts),
|
|
76
114
|
parts: result.parts,
|
|
77
115
|
inputTokens: result.inputTokens,
|
|
78
116
|
outputTokens: result.outputTokens,
|
|
79
|
-
sessionId:
|
|
117
|
+
sessionId: result.sessionId,
|
|
80
118
|
});
|
|
81
119
|
});
|
|
82
|
-
app.get("/sessions", (c) => {
|
|
83
|
-
const sessions = runtime.sessionRegistry.activeSessions();
|
|
120
|
+
app.get("/sessions", async (c) => {
|
|
121
|
+
const sessions = await runtime.sessionRegistry.activeSessions();
|
|
84
122
|
return c.json({
|
|
85
123
|
sessions: sessions.map((s) => ({
|
|
86
124
|
id: s.id,
|
|
@@ -91,9 +129,9 @@ export function createModeBRoutes(runtime) {
|
|
|
91
129
|
})),
|
|
92
130
|
});
|
|
93
131
|
});
|
|
94
|
-
app.delete("/sessions/:userId", (c) => {
|
|
132
|
+
app.delete("/sessions/:userId", async (c) => {
|
|
95
133
|
const userId = c.req.param("userId");
|
|
96
|
-
const removed = runtime.sessionRegistry.remove(runtime.appName, userId);
|
|
134
|
+
const removed = await runtime.sessionRegistry.remove(runtime.appName, userId);
|
|
97
135
|
return c.json({ removed });
|
|
98
136
|
});
|
|
99
137
|
return app;
|