@firstlovecenter/ai-chat 0.2.3 → 0.5.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/CHANGELOG.md +25 -0
- package/dist/drizzle/index.d.cts +2 -1
- package/dist/drizzle/index.d.ts +2 -1
- package/dist/prisma/index.d.cts +2 -1
- package/dist/prisma/index.d.ts +2 -1
- package/dist/server/index.cjs +307 -4
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +40 -3
- package/dist/server/index.d.ts +40 -3
- package/dist/server/index.js +307 -4
- package/dist/server/index.js.map +1 -1
- package/dist/{types-DNwFvL-C.d.cts → types-CDKxdzQc.d.cts} +9 -0
- package/dist/{types-DNwFvL-C.d.ts → types-CDKxdzQc.d.ts} +9 -0
- package/dist/ui/index.cjs +952 -86
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +24 -12
- package/dist/ui/index.d.ts +24 -12
- package/dist/ui/index.js +950 -87
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to `@firstlovecenter/ai-chat` are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] — 2026-05-08
|
|
9
|
+
|
|
10
|
+
Phase 2 of the package: a second chat interface that streams via the **Vercel AI SDK** alongside the existing custom SSE chat, with full structured-tool parity (charts, tables, callouts) and shared persistence so a session opened under one UI is identical under the other.
|
|
11
|
+
|
|
12
|
+
The version jump (0.2.3 → 0.5.0) reflects the size of the surface change: a new route factory, a new top-level UI component, a registry of chat interfaces with `Component` references, and a new optional field on every `ToolDefinition`. No breaking changes — existing custom-chat hosts keep working unchanged.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`routes.agentVercel`** — new route factory exposed from `configureAiChat({...}).routes.agentVercel`. POST handler that runs the host's tool catalogue through Vercel AI SDK 4's `streamText({tools, ...}).toDataStreamResponse({data})` instead of the bespoke SSE protocol. Same auth / scope / persistence / lifecycle-hook contract as `agentCustom`. Persists the assistant turn in `onFinish` with the **same `blocks` + `prose` shape** the custom adapter writes, so cross-UI session hydration is lossless.
|
|
17
|
+
- **`VercelChat`** — new top-level component exported from `@firstlovecenter/ai-chat/ui` with the same `AiChatProps` interface as `AiChat`. Layout copied from `AiChat`; the input pill markup is byte-for-byte identical (only state-binding callsites differ). State is driven by `useChat({ api: '/api/agent/vercel', ... })` from `@ai-sdk/react`. Renders streamed `present` blocks via the same `AnswerBlocks` primitive the custom UI uses. Stored sessions previously written by the custom chat are hydrated into `useChat` messages via reconstructed block + prose state, so toggling between Custom and Vercel mid-session preserves messages.
|
|
18
|
+
- **`chatInterfaces` registry + `getChatInterface(id)`** — typed list of `{ id, label, description, Component }` exported from `@firstlovecenter/ai-chat/ui`. Hosts map over this in their settings form to render the chat-interface picker dynamically; calling `getChatInterface(id).Component` from a server page lets the host branch between `<AiChat />` and `<VercelChat />` based on the persisted `aiSettings.chatInterface` value. Adding a new interface in the package surfaces in the host UI automatically.
|
|
19
|
+
- **`ChatInterfaceDef` type** — exported alongside the registry for hosts that want a strongly-typed picker.
|
|
20
|
+
- **`ToolDefinition.zodSchema`** — new optional field on every tool. Required by the Vercel route (which feeds the schema into `tool({ parameters })` in Vercel AI SDK); ignored by the custom route. Throws a clear config-time error if a tool registered for the Vercel chat is missing it. Adding it is purely additive — existing custom-chat-only tools and consumers compile unchanged.
|
|
21
|
+
- **`SELF_VERIFY_REQUIRED` gate reproduced on the Vercel path** — the vercel-adapter tracks `toolCallCount` in closure and rejects `present` calls with fewer than two prior tool calls, matching the custom agent loop's behaviour.
|
|
22
|
+
- **`AGENT_NO_PRESENT` fallback on the Vercel path** — if `streamText` finishes with text but no `present()` call, the route synthesises a single `paragraph_brief` block from the model's text and persists it in the same shape as the custom adapter. Mirrors the existing fallback in `runAgent`.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Title editor components moved to `_shared/`** — `SidebarTitleEditor` and `EditableTitle` were lifted from inline definitions in `ai-chat.tsx` into `src/ui/_shared/title-editors.tsx` so both UIs reuse the same source. `<AiChat />` rendered markup is unchanged; the only diff is the import path.
|
|
27
|
+
|
|
28
|
+
### Build
|
|
29
|
+
|
|
30
|
+
- **`zod` is now a runtime dependency** (was already a `devDependency`). The vercel-adapter imports `ZodType` for the `tool({ parameters })` boundary; tests and types import `z` directly.
|
|
31
|
+
|
|
8
32
|
## [0.2.3] — 2026-05-08
|
|
9
33
|
|
|
10
34
|
### Fixed
|
|
@@ -91,6 +115,7 @@ Initial public release on npm under `@firstlovecenter/ai-chat`.
|
|
|
91
115
|
- Dual ESM + CJS output via `tsup` with full `.d.ts` (and `.d.cts`) generation.
|
|
92
116
|
- UI bundle gets a post-build `'use client';` directive injection (tsup otherwise strips module-level directives during bundling, breaking RSC consumers that import the UI from a server file).
|
|
93
117
|
|
|
118
|
+
[0.5.0]: https://github.com/firstlovecenter/flc-ai-chat/compare/v0.2.3...v0.5.0
|
|
94
119
|
[0.2.3]: https://github.com/firstlovecenter/flc-ai-chat/compare/v0.2.2...v0.2.3
|
|
95
120
|
[0.2.2]: https://github.com/firstlovecenter/flc-ai-chat/compare/v0.2.1...v0.2.2
|
|
96
121
|
[0.2.1]: https://github.com/firstlovecenter/flc-ai-chat/compare/v0.2.0...v0.2.1
|
package/dist/drizzle/index.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as drizzle_orm_mysql_core from 'drizzle-orm/mysql-core';
|
|
2
2
|
import { MySql2Database } from 'drizzle-orm/mysql2';
|
|
3
|
-
import { c as PersistencePort } from '../types-
|
|
3
|
+
import { c as PersistencePort } from '../types-CDKxdzQc.cjs';
|
|
4
4
|
import 'google-auth-library';
|
|
5
|
+
import 'zod';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Global AI configuration. Singleton row enforced by `id=1` default + PK.
|
package/dist/drizzle/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as drizzle_orm_mysql_core from 'drizzle-orm/mysql-core';
|
|
2
2
|
import { MySql2Database } from 'drizzle-orm/mysql2';
|
|
3
|
-
import { c as PersistencePort } from '../types-
|
|
3
|
+
import { c as PersistencePort } from '../types-CDKxdzQc.js';
|
|
4
4
|
import 'google-auth-library';
|
|
5
|
+
import 'zod';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Global AI configuration. Singleton row enforced by `id=1` default + PK.
|
package/dist/prisma/index.d.cts
CHANGED
package/dist/prisma/index.d.ts
CHANGED
package/dist/server/index.cjs
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var vertexSdk = require('@anthropic-ai/vertex-sdk');
|
|
4
4
|
var crypto = require('crypto');
|
|
5
|
+
var ai = require('ai');
|
|
6
|
+
var googleVertex = require('@ai-sdk/google-vertex');
|
|
7
|
+
var anthropic = require('@ai-sdk/google-vertex/anthropic');
|
|
5
8
|
var googleAuthLibrary = require('google-auth-library');
|
|
6
9
|
|
|
7
10
|
// src/server/tools/types.ts
|
|
@@ -72,8 +75,8 @@ async function runAgent(input) {
|
|
|
72
75
|
const toolResults = [];
|
|
73
76
|
for (const tc of response.toolCalls) {
|
|
74
77
|
transcript.push({ kind: "tool_use", name: tc.name, input: tc.input });
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
78
|
+
const tool2 = input.tools[tc.name];
|
|
79
|
+
if (!tool2) {
|
|
77
80
|
const errResult = {
|
|
78
81
|
ok: false,
|
|
79
82
|
error: { code: "UNKNOWN_TOOL", message: `Unknown tool: ${tc.name}` }
|
|
@@ -87,7 +90,7 @@ async function runAgent(input) {
|
|
|
87
90
|
});
|
|
88
91
|
continue;
|
|
89
92
|
}
|
|
90
|
-
const result = await
|
|
93
|
+
const result = await tool2.execute(tc.input, {
|
|
91
94
|
...input.ctx,
|
|
92
95
|
toolCallCount
|
|
93
96
|
});
|
|
@@ -1137,6 +1140,297 @@ data: {}
|
|
|
1137
1140
|
}
|
|
1138
1141
|
};
|
|
1139
1142
|
}
|
|
1143
|
+
function buildVercelTools(tools, ctx, data, onPresent) {
|
|
1144
|
+
const result = {};
|
|
1145
|
+
let toolCallCount = 0;
|
|
1146
|
+
for (const [name, def] of Object.entries(tools)) {
|
|
1147
|
+
if (!def.zodSchema) {
|
|
1148
|
+
throw new Error(
|
|
1149
|
+
`Tool '${name}' has no zodSchema; required for the Vercel AI SDK chat. Add a Zod schema to the tool definition (or remove it from the registry if the host only uses the custom SSE chat).`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
if (name === TERMINAL_TOOL_NAME) {
|
|
1153
|
+
result[name] = ai.tool({
|
|
1154
|
+
description: def.schema.description,
|
|
1155
|
+
// The Zod schema doubles as the runtime parameter validator the SDK
|
|
1156
|
+
// hands the model. We accept whatever Zod shape the host registered;
|
|
1157
|
+
// the SDK uses it to validate the tool-call arguments before dispatch.
|
|
1158
|
+
parameters: def.zodSchema,
|
|
1159
|
+
execute: async (input) => {
|
|
1160
|
+
if (toolCallCount < 2) {
|
|
1161
|
+
return {
|
|
1162
|
+
error: {
|
|
1163
|
+
code: "SELF_VERIFY_REQUIRED",
|
|
1164
|
+
message: "Per FR-8.3 you must run at least one CROSS-CHECK tool call (a different metric, a different period, or a run_sql sanity-check) before present. Make that extra call now, then call present again."
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
const payload = input;
|
|
1169
|
+
for (let i = 0; i < payload.blocks.length; i++) {
|
|
1170
|
+
data.append({
|
|
1171
|
+
type: "block",
|
|
1172
|
+
value: { index: i, ...payload.blocks[i] }
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
onPresent(payload);
|
|
1176
|
+
return { ok: true };
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
result[name] = ai.tool({
|
|
1182
|
+
description: def.schema.description,
|
|
1183
|
+
parameters: def.zodSchema,
|
|
1184
|
+
execute: async (input) => {
|
|
1185
|
+
const res = await def.execute(input, { ...ctx, toolCallCount });
|
|
1186
|
+
toolCallCount += 1;
|
|
1187
|
+
if (res.ok) return res.data;
|
|
1188
|
+
return { error: res.error };
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
return result;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// src/server/routes/agent-vercel.ts
|
|
1196
|
+
var VALID_MODELS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
1197
|
+
function jsonError2(status, code, message) {
|
|
1198
|
+
return new Response(JSON.stringify({ error: { code, message } }), {
|
|
1199
|
+
status,
|
|
1200
|
+
headers: { "Content-Type": "application/json" }
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
function defaultGenerateSessionId2() {
|
|
1204
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 16);
|
|
1205
|
+
}
|
|
1206
|
+
function createAgentVercelRoutes(ctx) {
|
|
1207
|
+
const { persistence, auth, scope, tools, vertex, logger, hooks } = ctx;
|
|
1208
|
+
return {
|
|
1209
|
+
/** Next.js-compatible POST handler. */
|
|
1210
|
+
POST: async (req) => {
|
|
1211
|
+
if (hooks?.onRequest) {
|
|
1212
|
+
const short = await hooks.onRequest(req);
|
|
1213
|
+
if (short) return short;
|
|
1214
|
+
}
|
|
1215
|
+
const authResult = await auth.requireAuth(req);
|
|
1216
|
+
if (!authResult.ok) return authResult.response;
|
|
1217
|
+
const { scope: callerScope, userId } = authResult;
|
|
1218
|
+
if (hooks?.onAuthenticated) {
|
|
1219
|
+
const short = await hooks.onAuthenticated({
|
|
1220
|
+
req,
|
|
1221
|
+
scope: callerScope,
|
|
1222
|
+
userId
|
|
1223
|
+
});
|
|
1224
|
+
if (short) return short;
|
|
1225
|
+
}
|
|
1226
|
+
const body = await req.json().catch(() => null);
|
|
1227
|
+
const question = typeof body?.question === "string" ? body.question.trim() : "";
|
|
1228
|
+
if (!question) {
|
|
1229
|
+
return jsonError2(
|
|
1230
|
+
400,
|
|
1231
|
+
"VALIDATION_FAILED",
|
|
1232
|
+
"question must be a non-empty string."
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
const rawChatSessionId = body?.chatSessionId;
|
|
1236
|
+
const incomingChatSessionId = typeof rawChatSessionId === "number" && Number.isInteger(rawChatSessionId) ? rawChatSessionId : null;
|
|
1237
|
+
const rawModel = body?.model;
|
|
1238
|
+
const requestedModel = typeof rawModel === "string" && VALID_MODELS.has(rawModel) ? rawModel : null;
|
|
1239
|
+
const aiSettings = await persistence.getAiSettings();
|
|
1240
|
+
let chatSessionId;
|
|
1241
|
+
if (incomingChatSessionId !== null) {
|
|
1242
|
+
const owned = await persistence.getSession(
|
|
1243
|
+
incomingChatSessionId,
|
|
1244
|
+
userId
|
|
1245
|
+
);
|
|
1246
|
+
if (!owned) {
|
|
1247
|
+
return jsonError2(404, "NOT_FOUND", "Chat session not found.");
|
|
1248
|
+
}
|
|
1249
|
+
chatSessionId = owned.id;
|
|
1250
|
+
} else {
|
|
1251
|
+
const created = await persistence.createSession({
|
|
1252
|
+
userId,
|
|
1253
|
+
title: question.slice(0, 200)
|
|
1254
|
+
});
|
|
1255
|
+
chatSessionId = created.id;
|
|
1256
|
+
}
|
|
1257
|
+
await persistence.appendMessage({
|
|
1258
|
+
sessionId: chatSessionId,
|
|
1259
|
+
role: "user",
|
|
1260
|
+
question
|
|
1261
|
+
});
|
|
1262
|
+
const sessionId = hooks?.generateSessionId ? await hooks.generateSessionId({
|
|
1263
|
+
scope: callerScope,
|
|
1264
|
+
userId,
|
|
1265
|
+
chatSessionId: incomingChatSessionId
|
|
1266
|
+
}) : defaultGenerateSessionId2();
|
|
1267
|
+
const scopeSummary = await scope.buildScopeSummary(callerScope);
|
|
1268
|
+
const scopeLabel = await scope.resolveScopeLabel(callerScope);
|
|
1269
|
+
const toolContext = {
|
|
1270
|
+
scope: callerScope,
|
|
1271
|
+
sessionId,
|
|
1272
|
+
scopeSummary,
|
|
1273
|
+
toolCallCount: 0
|
|
1274
|
+
};
|
|
1275
|
+
const systemBlocks = await tools.buildSystemBlocks(toolContext);
|
|
1276
|
+
const provider = requestedModel ?? aiSettings.toolProvider;
|
|
1277
|
+
if (!VALID_MODELS.has(provider)) {
|
|
1278
|
+
return jsonError2(
|
|
1279
|
+
400,
|
|
1280
|
+
"INVALID_PROVIDER",
|
|
1281
|
+
`Vercel chat only supports 'claude' or 'gemini'; got '${provider}'.`
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
const data = new ai.StreamData();
|
|
1285
|
+
let presentPayload = null;
|
|
1286
|
+
let persistedError = null;
|
|
1287
|
+
let sessionStarted = false;
|
|
1288
|
+
try {
|
|
1289
|
+
if (hooks?.onSessionStart) {
|
|
1290
|
+
await hooks.onSessionStart({
|
|
1291
|
+
scope: callerScope,
|
|
1292
|
+
sessionId,
|
|
1293
|
+
userId
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
sessionStarted = true;
|
|
1297
|
+
const vercelTools = buildVercelTools(
|
|
1298
|
+
tools.tools,
|
|
1299
|
+
toolContext,
|
|
1300
|
+
data,
|
|
1301
|
+
(p) => {
|
|
1302
|
+
presentPayload = p;
|
|
1303
|
+
}
|
|
1304
|
+
);
|
|
1305
|
+
data.append({
|
|
1306
|
+
type: "meta",
|
|
1307
|
+
value: { chatSessionId, scopeLabel }
|
|
1308
|
+
});
|
|
1309
|
+
const system = systemBlocks.map((b) => b.text).join("\n\n");
|
|
1310
|
+
const model = provider === "claude" ? anthropic.createVertexAnthropic({
|
|
1311
|
+
project: vertex.projectId,
|
|
1312
|
+
location: vertex.defaultLocation,
|
|
1313
|
+
googleAuthOptions: {}
|
|
1314
|
+
})(vertex.modelIds.claude) : googleVertex.createVertex({
|
|
1315
|
+
project: vertex.projectId,
|
|
1316
|
+
location: aiSettings.gcpLocation,
|
|
1317
|
+
googleAuthOptions: {}
|
|
1318
|
+
})(vertex.modelIds.gemini);
|
|
1319
|
+
const result = ai.streamText({
|
|
1320
|
+
model,
|
|
1321
|
+
system,
|
|
1322
|
+
messages: [{ role: "user", content: question }],
|
|
1323
|
+
tools: vercelTools,
|
|
1324
|
+
maxSteps: 12,
|
|
1325
|
+
maxTokens: 4096,
|
|
1326
|
+
onFinish: async ({ text }) => {
|
|
1327
|
+
try {
|
|
1328
|
+
let blocks = presentPayload?.blocks ?? [];
|
|
1329
|
+
const prose = {};
|
|
1330
|
+
const trimmed = (text ?? "").trim();
|
|
1331
|
+
if (presentPayload === null && trimmed) {
|
|
1332
|
+
const topic = question.length > 80 ? question.slice(0, 77) + "..." : question;
|
|
1333
|
+
const synthetic = {
|
|
1334
|
+
kind: "paragraph_brief",
|
|
1335
|
+
topic,
|
|
1336
|
+
key_facts: [trimmed]
|
|
1337
|
+
};
|
|
1338
|
+
blocks = [synthetic];
|
|
1339
|
+
prose[0] = trimmed;
|
|
1340
|
+
data.append({
|
|
1341
|
+
type: "block",
|
|
1342
|
+
value: { index: 0, ...synthetic }
|
|
1343
|
+
});
|
|
1344
|
+
} else if (text) {
|
|
1345
|
+
const firstPbIdx = blocks.findIndex(
|
|
1346
|
+
(b) => b.kind === "paragraph_brief"
|
|
1347
|
+
);
|
|
1348
|
+
if (firstPbIdx >= 0) prose[firstPbIdx] = text;
|
|
1349
|
+
}
|
|
1350
|
+
await persistence.appendMessage({
|
|
1351
|
+
sessionId: chatSessionId,
|
|
1352
|
+
role: "assistant",
|
|
1353
|
+
blocks: blocks.length ? blocks : null,
|
|
1354
|
+
prose: Object.keys(prose).length ? prose : null,
|
|
1355
|
+
errorJson: persistedError
|
|
1356
|
+
});
|
|
1357
|
+
} catch (err2) {
|
|
1358
|
+
logger?.warn?.(
|
|
1359
|
+
{
|
|
1360
|
+
chatSessionId,
|
|
1361
|
+
sessionId,
|
|
1362
|
+
err: err2.message
|
|
1363
|
+
},
|
|
1364
|
+
"[agent-vercel] failed to persist assistant turn"
|
|
1365
|
+
);
|
|
1366
|
+
} finally {
|
|
1367
|
+
try {
|
|
1368
|
+
await data.close();
|
|
1369
|
+
} catch {
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
return result.toDataStreamResponse({ data });
|
|
1375
|
+
} catch (e) {
|
|
1376
|
+
const message = e.message ?? "Internal error";
|
|
1377
|
+
persistedError = { code: "INTERNAL", message };
|
|
1378
|
+
logger?.error?.(
|
|
1379
|
+
{ chatSessionId, sessionId, err: message },
|
|
1380
|
+
"[agent-vercel] route errored"
|
|
1381
|
+
);
|
|
1382
|
+
try {
|
|
1383
|
+
data.append({
|
|
1384
|
+
type: "error",
|
|
1385
|
+
value: { code: "INTERNAL", message }
|
|
1386
|
+
});
|
|
1387
|
+
} catch {
|
|
1388
|
+
}
|
|
1389
|
+
try {
|
|
1390
|
+
await data.close();
|
|
1391
|
+
} catch {
|
|
1392
|
+
}
|
|
1393
|
+
try {
|
|
1394
|
+
await persistence.appendMessage({
|
|
1395
|
+
sessionId: chatSessionId,
|
|
1396
|
+
role: "assistant",
|
|
1397
|
+
blocks: null,
|
|
1398
|
+
prose: null,
|
|
1399
|
+
errorJson: persistedError
|
|
1400
|
+
});
|
|
1401
|
+
} catch (err2) {
|
|
1402
|
+
logger?.warn?.(
|
|
1403
|
+
{ chatSessionId, sessionId, err: err2.message },
|
|
1404
|
+
"[agent-vercel] failed to persist error turn"
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
return jsonError2(500, "INTERNAL", message);
|
|
1408
|
+
} finally {
|
|
1409
|
+
if (hooks?.onSessionEnd) {
|
|
1410
|
+
const cause = req.signal.aborted ? "abort" : persistedError ? "error" : "complete";
|
|
1411
|
+
try {
|
|
1412
|
+
await hooks.onSessionEnd({
|
|
1413
|
+
scope: callerScope,
|
|
1414
|
+
sessionId,
|
|
1415
|
+
userId,
|
|
1416
|
+
cause
|
|
1417
|
+
});
|
|
1418
|
+
} catch (err2) {
|
|
1419
|
+
logger?.warn?.(
|
|
1420
|
+
{
|
|
1421
|
+
chatSessionId,
|
|
1422
|
+
sessionId,
|
|
1423
|
+
sessionStarted,
|
|
1424
|
+
err: err2.message
|
|
1425
|
+
},
|
|
1426
|
+
"[agent-vercel] onSessionEnd hook failed"
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1140
1434
|
|
|
1141
1435
|
// src/server/routes/chat-sessions.ts
|
|
1142
1436
|
var DEFAULT_TITLE = "New chat";
|
|
@@ -1497,6 +1791,15 @@ function configureAiChat(opts) {
|
|
|
1497
1791
|
resolveNarratorId: opts.resolveNarratorId,
|
|
1498
1792
|
hooks: opts.hooks
|
|
1499
1793
|
});
|
|
1794
|
+
const agentVercel = createAgentVercelRoutes({
|
|
1795
|
+
persistence: opts.persistence,
|
|
1796
|
+
auth: opts.auth,
|
|
1797
|
+
scope: opts.scope,
|
|
1798
|
+
tools,
|
|
1799
|
+
vertex: opts.vertex,
|
|
1800
|
+
logger: opts.logger,
|
|
1801
|
+
hooks: opts.hooks
|
|
1802
|
+
});
|
|
1500
1803
|
const chatSessions = createChatSessionsRoutes({
|
|
1501
1804
|
persistence: opts.persistence,
|
|
1502
1805
|
auth: opts.auth,
|
|
@@ -1513,7 +1816,7 @@ function configureAiChat(opts) {
|
|
|
1513
1816
|
});
|
|
1514
1817
|
return {
|
|
1515
1818
|
runAgent: runAgentBound,
|
|
1516
|
-
routes: { agentCustom, chatSessions, adminSettings },
|
|
1819
|
+
routes: { agentCustom, agentVercel, chatSessions, adminSettings },
|
|
1517
1820
|
registries: { toolProviders: toolProviders2, chatInterfaces }
|
|
1518
1821
|
};
|
|
1519
1822
|
}
|