@axiom-lattice/gateway 2.1.52 → 2.1.54

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/index.mjs CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/index.ts
2
9
  import fastify from "fastify";
3
10
  import cors from "@fastify/cors";
@@ -591,11 +598,13 @@ async function getThreadList(request, reply) {
591
598
  const tenantId = getTenantId2(request);
592
599
  const { assistantId } = request.params;
593
600
  const metadataFilter = {};
594
- if (request.query.workspaceId) {
595
- metadataFilter.workspaceId = request.query.workspaceId;
601
+ const workspaceId = request.headers["x-workspace-id"];
602
+ const projectId = request.headers["x-project-id"];
603
+ if (workspaceId) {
604
+ metadataFilter.workspaceId = workspaceId;
596
605
  }
597
- if (request.query.projectId) {
598
- metadataFilter.projectId = request.query.projectId;
606
+ if (projectId) {
607
+ metadataFilter.projectId = projectId;
599
608
  }
600
609
  const storeLattice = getStoreLattice2("default", "thread");
601
610
  const threadStore = storeLattice.store;
@@ -4878,6 +4887,698 @@ function registerAuthRoutes(app2, config) {
4878
4887
  );
4879
4888
  }
4880
4889
 
4890
+ // src/channels/lark/routes.ts
4891
+ import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
4892
+ import {
4893
+ ChannelIdentityMappingStore,
4894
+ PostgreSQLChannelInstallationStore
4895
+ } from "@axiom-lattice/pg-stores";
4896
+
4897
+ // src/channels/lark/parser.ts
4898
+ function parseLarkMessageEvent(payload) {
4899
+ const raw = payload;
4900
+ if (raw.header?.event_type !== "im.message.receive_v1") {
4901
+ return null;
4902
+ }
4903
+ const message = raw.event?.message;
4904
+ const openId = raw.event?.sender?.sender_id?.open_id;
4905
+ if (!message || !openId || message.message_type !== "text") {
4906
+ return null;
4907
+ }
4908
+ const parsedContent = parseLarkTextContent(message.content);
4909
+ if (!message.message_id || !message.chat_id || !parsedContent) {
4910
+ return null;
4911
+ }
4912
+ return {
4913
+ messageId: message.message_id,
4914
+ openId,
4915
+ chatId: message.chat_id,
4916
+ chatType: normalizeChatType(message.chat_type),
4917
+ text: parsedContent
4918
+ };
4919
+ }
4920
+ function parseLarkTextContent(content) {
4921
+ if (!content) {
4922
+ return null;
4923
+ }
4924
+ try {
4925
+ const parsed = JSON.parse(content);
4926
+ return typeof parsed.text === "string" ? parsed.text : null;
4927
+ } catch {
4928
+ return null;
4929
+ }
4930
+ }
4931
+ function normalizeChatType(chatType) {
4932
+ return chatType === "p2p" ? "direct" : "group";
4933
+ }
4934
+
4935
+ // src/channels/lark/controller.ts
4936
+ function createLarkEventHandler(dependencies) {
4937
+ return async function handleLarkEvent2(request, reply) {
4938
+ const installationId = request.params?.installationId;
4939
+ if (!installationId) {
4940
+ reply.status(400).send({ success: false, message: "Missing installationId" });
4941
+ return;
4942
+ }
4943
+ const config = await dependencies.getInstallationConfig(installationId);
4944
+ if (!config) {
4945
+ reply.status(404).send({ success: false, message: "Lark installation not found" });
4946
+ return;
4947
+ }
4948
+ const body = dependencies.parseRequestBody(
4949
+ request.body,
4950
+ config.encryptKey
4951
+ );
4952
+ if (!dependencies.verifyParsedBody(body, config)) {
4953
+ reply.status(401).send({ success: false, message: "Invalid Lark request" });
4954
+ return;
4955
+ }
4956
+ if (body.type === "url_verification" && body.challenge) {
4957
+ reply.status(200).send({ challenge: body.challenge });
4958
+ return;
4959
+ }
4960
+ const parsed = dependencies.parseEvent(request.body);
4961
+ if (!parsed) {
4962
+ reply.status(200).send({ success: true, ignored: true });
4963
+ return;
4964
+ }
4965
+ const receipt = await dependencies.claimInboundReceipt({
4966
+ channel: "lark",
4967
+ channelAppId: config.appId,
4968
+ externalMessageId: parsed.messageId,
4969
+ tenantId: config.tenantId
4970
+ });
4971
+ if (!receipt.accepted) {
4972
+ reply.status(200).send(
4973
+ receipt.status === "processing" ? { success: true, processing: true } : { success: true, duplicate: true }
4974
+ );
4975
+ return;
4976
+ }
4977
+ try {
4978
+ const { threadId } = await dependencies.resolveThread({
4979
+ channel: "lark",
4980
+ channelAppId: config.appId,
4981
+ tenantId: config.tenantId,
4982
+ assistantId: config.assistantId,
4983
+ mappingMode: config.mappingMode,
4984
+ openId: parsed.openId,
4985
+ chatId: parsed.chatId,
4986
+ chatType: parsed.chatType,
4987
+ messageId: parsed.messageId,
4988
+ workspaceId: config.workspaceId,
4989
+ projectId: config.projectId
4990
+ });
4991
+ const text = await dependencies.runAgentAndCollectText({
4992
+ tenantId: config.tenantId,
4993
+ assistantId: config.assistantId,
4994
+ threadId,
4995
+ text: parsed.text,
4996
+ workspaceId: config.workspaceId,
4997
+ projectId: config.projectId
4998
+ });
4999
+ await dependencies.sendTextReply({
5000
+ chatId: parsed.chatId,
5001
+ text,
5002
+ config
5003
+ });
5004
+ await dependencies.markInboundReceiptCompleted({
5005
+ channel: "lark",
5006
+ channelAppId: config.appId,
5007
+ externalMessageId: parsed.messageId,
5008
+ tenantId: config.tenantId,
5009
+ threadId
5010
+ });
5011
+ reply.status(200).send({ success: true, threadId });
5012
+ } catch (error) {
5013
+ await dependencies.markInboundReceiptFailed({
5014
+ channel: "lark",
5015
+ channelAppId: config.appId,
5016
+ externalMessageId: parsed.messageId,
5017
+ tenantId: config.tenantId
5018
+ });
5019
+ throw error;
5020
+ }
5021
+ };
5022
+ }
5023
+ var handleLarkEvent = createLarkEventHandler({
5024
+ getInstallationConfig: async () => null,
5025
+ parseRequestBody: (body) => body || {},
5026
+ verifyParsedBody: () => true,
5027
+ parseEvent: parseLarkMessageEvent,
5028
+ claimInboundReceipt: async () => ({ accepted: true, status: "processing" }),
5029
+ markInboundReceiptCompleted: async () => void 0,
5030
+ markInboundReceiptFailed: async () => void 0,
5031
+ resolveThread: async () => ({ threadId: "" }),
5032
+ runAgentAndCollectText: async () => "",
5033
+ sendTextReply: async () => void 0
5034
+ });
5035
+
5036
+ // src/channels/lark/config.ts
5037
+ function loadLarkIngressConfig() {
5038
+ return {
5039
+ enabled: process.env.LARK_ENABLED !== "false",
5040
+ appId: process.env.LARK_APP_ID || "",
5041
+ appSecret: process.env.LARK_APP_SECRET || "",
5042
+ verificationToken: process.env.LARK_VERIFICATION_TOKEN,
5043
+ encryptKey: process.env.LARK_ENCRYPT_KEY,
5044
+ tenantId: process.env.LARK_TENANT_ID || "default",
5045
+ assistantId: process.env.LARK_ASSISTANT_ID || "default_agent",
5046
+ workspaceId: process.env.LARK_WORKSPACE_ID,
5047
+ projectId: process.env.LARK_PROJECT_ID,
5048
+ mappingMode: process.env.LARK_MAPPING_MODE || "hybrid"
5049
+ };
5050
+ }
5051
+ function isLarkIngressEnabled(config) {
5052
+ return config.enabled;
5053
+ }
5054
+
5055
+ // src/channels/lark/mapping-service.ts
5056
+ import { randomUUID as randomUUID6 } from "crypto";
5057
+ function createChannelThreadMappingService(deps) {
5058
+ return {
5059
+ async getOrCreateThread(input) {
5060
+ const externalSubjectKey = buildExternalSubjectKey(input);
5061
+ const existing = await deps.mappingStore.getMappingBySubject({
5062
+ channel: input.channel,
5063
+ channelAppId: input.channelAppId,
5064
+ tenantId: input.tenantId,
5065
+ assistantId: input.assistantId,
5066
+ externalSubjectKey
5067
+ });
5068
+ if (existing) {
5069
+ return { threadId: existing.threadId };
5070
+ }
5071
+ const threadId = (deps.uuid || randomUUID6)();
5072
+ await deps.threadStore.createThread(input.tenantId, input.assistantId, threadId, {
5073
+ metadata: {
5074
+ tenantId: input.tenantId,
5075
+ workspaceId: input.workspaceId,
5076
+ projectId: input.projectId,
5077
+ channel: input.channel,
5078
+ larkChatId: input.chatId,
5079
+ larkOpenId: input.openId
5080
+ }
5081
+ });
5082
+ try {
5083
+ await deps.mappingStore.createMapping({
5084
+ channel: input.channel,
5085
+ channelAppId: input.channelAppId,
5086
+ tenantId: input.tenantId,
5087
+ assistantId: input.assistantId,
5088
+ mappingMode: input.mappingMode,
5089
+ externalSubjectType: resolveSubjectType(input.mappingMode, input.chatType) === "user" ? "user" : "chat",
5090
+ externalSubjectKey,
5091
+ larkOpenId: input.openId,
5092
+ larkChatId: input.chatId,
5093
+ larkMessageId: input.messageId,
5094
+ threadId
5095
+ });
5096
+ } catch (error) {
5097
+ if (!isUniqueViolation(error)) {
5098
+ throw error;
5099
+ }
5100
+ const canonical = await deps.mappingStore.getMappingBySubject({
5101
+ channel: input.channel,
5102
+ channelAppId: input.channelAppId,
5103
+ tenantId: input.tenantId,
5104
+ assistantId: input.assistantId,
5105
+ externalSubjectKey
5106
+ });
5107
+ if (!canonical) {
5108
+ throw error;
5109
+ }
5110
+ await deps.threadStore.deleteThread(input.tenantId, threadId);
5111
+ return { threadId: canonical.threadId };
5112
+ }
5113
+ return { threadId };
5114
+ }
5115
+ };
5116
+ }
5117
+ function isUniqueViolation(error) {
5118
+ return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
5119
+ }
5120
+ function buildExternalSubjectKey(input) {
5121
+ const subjectType = resolveSubjectType(input.mappingMode, input.chatType);
5122
+ const subjectValue = subjectType === "user" ? input.openId : input.chatId;
5123
+ return [
5124
+ input.channel,
5125
+ input.channelAppId,
5126
+ `tenant:${input.tenantId}`,
5127
+ `assistant:${input.assistantId}`,
5128
+ `${subjectType}:${subjectValue}`
5129
+ ].join(":");
5130
+ }
5131
+ function resolveSubjectType(mappingMode, chatType) {
5132
+ if (mappingMode === "user") {
5133
+ return "user";
5134
+ }
5135
+ if (mappingMode === "group") {
5136
+ return "chat";
5137
+ }
5138
+ return chatType === "direct" ? "user" : "chat";
5139
+ }
5140
+
5141
+ // src/channels/lark/runner.ts
5142
+ import { agentInstanceManager as agentInstanceManager5 } from "@axiom-lattice/core";
5143
+ import { MessageChunkTypes as MessageChunkTypes3 } from "@axiom-lattice/protocols";
5144
+
5145
+ // src/channels/lark/aggregator.ts
5146
+ import { MessageChunkTypes as MessageChunkTypes2 } from "@axiom-lattice/protocols";
5147
+ function aggregateLarkReply(messageId, chunks) {
5148
+ return chunks.filter(
5149
+ (chunk) => chunk.type === MessageChunkTypes2.AI && chunk.data.id === messageId
5150
+ ).map((chunk) => chunk.data.content || "").join("").trim();
5151
+ }
5152
+
5153
+ // src/channels/lark/runner.ts
5154
+ async function runAgentAndCollectLarkReply(input) {
5155
+ const agent = agentInstanceManager5.getAgent({
5156
+ tenant_id: input.tenantId,
5157
+ assistant_id: input.assistantId,
5158
+ thread_id: input.threadId,
5159
+ workspace_id: input.workspaceId,
5160
+ project_id: input.projectId
5161
+ });
5162
+ const result = await agent.addMessage({
5163
+ input: {
5164
+ message: input.text
5165
+ }
5166
+ });
5167
+ const chunks = [];
5168
+ const stream = agent.chunkStream(result.messageId, [
5169
+ MessageChunkTypes3.MESSAGE_COMPLETED
5170
+ ]);
5171
+ for await (const chunk of stream) {
5172
+ chunks.push(chunk);
5173
+ }
5174
+ return aggregateLarkReply(result.messageId, chunks);
5175
+ }
5176
+
5177
+ // src/channels/lark/sender.ts
5178
+ function createLarkSender(config, client = createDefaultLarkClient(config)) {
5179
+ return {
5180
+ async sendTextReply(input) {
5181
+ const response = await client.im.v1.message.create({
5182
+ params: {
5183
+ receive_id_type: "chat_id"
5184
+ },
5185
+ data: {
5186
+ receive_id: input.chatId,
5187
+ msg_type: "text",
5188
+ content: JSON.stringify({ text: input.text })
5189
+ }
5190
+ });
5191
+ if (response.code && response.code !== 0) {
5192
+ throw new Error("Failed to send Lark reply");
5193
+ }
5194
+ }
5195
+ };
5196
+ }
5197
+ function createDefaultLarkClient(config) {
5198
+ const Lark = __require("@larksuiteoapi/node-sdk");
5199
+ return new Lark.Client({
5200
+ appId: config.appId,
5201
+ appSecret: config.appSecret
5202
+ });
5203
+ }
5204
+
5205
+ // src/channels/lark/verification.ts
5206
+ import crypto2 from "crypto";
5207
+ function parseLarkRequestBody(body, encryptKey) {
5208
+ const parsed = body || {};
5209
+ if (encryptKey && typeof parsed.encrypt === "string") {
5210
+ return decryptLarkPayload(encryptKey, parsed.encrypt);
5211
+ }
5212
+ return parsed;
5213
+ }
5214
+ function decryptLarkPayload(encryptKey, encryptedPayload) {
5215
+ const key = crypto2.createHash("sha256").update(encryptKey).digest();
5216
+ const buffer = Buffer.from(encryptedPayload, "base64");
5217
+ const iv = buffer.subarray(0, 16);
5218
+ const ciphertext = buffer.subarray(16);
5219
+ const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
5220
+ const plaintext = Buffer.concat([
5221
+ decipher.update(ciphertext),
5222
+ decipher.final()
5223
+ ]).toString("utf8");
5224
+ return JSON.parse(plaintext);
5225
+ }
5226
+ function createLarkRequestVerifier(config) {
5227
+ return function verifyRequest(request) {
5228
+ const body = parseLarkRequestBody(request.body, config.encryptKey);
5229
+ return verifyLarkParsedBody(body, config);
5230
+ };
5231
+ }
5232
+ function verifyLarkParsedBody(body, config) {
5233
+ if (!config.verificationToken) {
5234
+ return true;
5235
+ }
5236
+ return extractVerificationToken(body) === config.verificationToken;
5237
+ }
5238
+ function extractVerificationToken(body) {
5239
+ if (typeof body.token === "string") {
5240
+ return body.token;
5241
+ }
5242
+ if (typeof body.header?.token === "string") {
5243
+ return body.header.token;
5244
+ }
5245
+ return void 0;
5246
+ }
5247
+
5248
+ // src/channels/lark/routes.ts
5249
+ function registerLarkChannelRoutes(app2, dependencies) {
5250
+ const config = loadLarkIngressConfig();
5251
+ if (!dependencies && !isLarkIngressEnabled(config)) {
5252
+ return;
5253
+ }
5254
+ const handlerDependencies = dependencies || createDefaultLarkDependencies();
5255
+ app2.post(
5256
+ "/api/channels/lark/installations/:installationId/events",
5257
+ createLarkEventHandler({
5258
+ ...handlerDependencies
5259
+ })
5260
+ );
5261
+ }
5262
+ function createDefaultLarkDependencies() {
5263
+ const installationStore = new PostgreSQLChannelInstallationStore({
5264
+ poolConfig: getDatabaseUrl()
5265
+ });
5266
+ const threadStore = getStoreLattice12("default", "thread").store;
5267
+ const mappingStore = new ChannelIdentityMappingStore({
5268
+ poolConfig: getDatabaseUrl()
5269
+ });
5270
+ const mappingService = createChannelThreadMappingService({
5271
+ mappingStore,
5272
+ threadStore
5273
+ });
5274
+ return {
5275
+ getInstallationConfig: async (installationId) => {
5276
+ const installation = await installationStore.getInstallationById(
5277
+ installationId
5278
+ );
5279
+ if (!installation || installation.channel !== "lark") {
5280
+ return null;
5281
+ }
5282
+ return {
5283
+ enabled: true,
5284
+ installationId: installation.id,
5285
+ tenantId: installation.tenantId,
5286
+ assistantId: installation.config.assistantId,
5287
+ appId: installation.config.appId,
5288
+ appSecret: installation.config.appSecret,
5289
+ verificationToken: installation.config.verificationToken,
5290
+ encryptKey: installation.config.encryptKey,
5291
+ workspaceId: installation.config.workspaceId,
5292
+ projectId: installation.config.projectId,
5293
+ mappingMode: installation.config.mappingMode
5294
+ };
5295
+ },
5296
+ parseRequestBody: (body, encryptKey) => parseLarkRequestBody(body, encryptKey),
5297
+ verifyParsedBody: (body, config) => {
5298
+ if (!config.verificationToken) {
5299
+ return true;
5300
+ }
5301
+ return createLarkRequestVerifier(config)({
5302
+ body
5303
+ });
5304
+ },
5305
+ parseEvent: parseLarkMessageEvent,
5306
+ claimInboundReceipt: (input) => mappingStore.claimInboundReceipt(input),
5307
+ markInboundReceiptCompleted: (input) => mappingStore.markInboundReceiptCompleted(input),
5308
+ markInboundReceiptFailed: (input) => mappingStore.markInboundReceiptFailed(input),
5309
+ resolveThread: (input) => mappingService.getOrCreateThread(input),
5310
+ runAgentAndCollectText: ({ tenantId, assistantId, threadId, text, workspaceId, projectId }) => runAgentAndCollectLarkReply({
5311
+ tenantId,
5312
+ assistantId,
5313
+ threadId,
5314
+ text,
5315
+ workspaceId,
5316
+ projectId
5317
+ }),
5318
+ sendTextReply: async ({ chatId, text, config }) => {
5319
+ const sender = createLarkSender({
5320
+ appId: config.appId,
5321
+ appSecret: config.appSecret
5322
+ });
5323
+ await sender.sendTextReply({ chatId, text });
5324
+ }
5325
+ };
5326
+ }
5327
+ function getDatabaseUrl() {
5328
+ const databaseUrl = process.env.DATABASE_URL;
5329
+ if (!databaseUrl) {
5330
+ throw new Error("DATABASE_URL is required for Lark channel ingress");
5331
+ }
5332
+ return databaseUrl;
5333
+ }
5334
+
5335
+ // src/channels/routes.ts
5336
+ var channelRouteRegistrars = [
5337
+ (app2, dependencies) => registerLarkChannelRoutes(app2, dependencies.lark)
5338
+ ];
5339
+ function registerChannelRoutes(app2, dependencies = {}) {
5340
+ for (const registerRoutes of channelRouteRegistrars) {
5341
+ registerRoutes(app2, dependencies);
5342
+ }
5343
+ }
5344
+
5345
+ // src/controllers/channel-installations.ts
5346
+ import { randomUUID as randomUUID7 } from "crypto";
5347
+ function getTenantId9(request) {
5348
+ const userTenantId = request.user?.tenantId;
5349
+ if (userTenantId) {
5350
+ return userTenantId;
5351
+ }
5352
+ return request.headers["x-tenant-id"] || "default";
5353
+ }
5354
+ function getInstallationStore() {
5355
+ const { PostgreSQLChannelInstallationStore: PostgreSQLChannelInstallationStore2 } = __require("@axiom-lattice/pg-stores");
5356
+ const databaseUrl = process.env.DATABASE_URL;
5357
+ if (!databaseUrl) {
5358
+ throw new Error("DATABASE_URL is required for channel installation store");
5359
+ }
5360
+ return new PostgreSQLChannelInstallationStore2({
5361
+ poolConfig: databaseUrl
5362
+ });
5363
+ }
5364
+ async function getChannelInstallationList(request, reply) {
5365
+ const tenantId = getTenantId9(request);
5366
+ const { channel } = request.query;
5367
+ try {
5368
+ const store = getInstallationStore();
5369
+ const installations = await store.getInstallationsByTenant(tenantId, channel);
5370
+ return {
5371
+ success: true,
5372
+ message: "Channel installations retrieved successfully",
5373
+ data: {
5374
+ records: installations,
5375
+ total: installations.length
5376
+ }
5377
+ };
5378
+ } catch (error) {
5379
+ console.error("Failed to get channel installations:", error);
5380
+ return {
5381
+ success: false,
5382
+ message: "Failed to retrieve channel installations",
5383
+ data: {
5384
+ records: [],
5385
+ total: 0
5386
+ }
5387
+ };
5388
+ }
5389
+ }
5390
+ async function getChannelInstallation(request, reply) {
5391
+ const tenantId = getTenantId9(request);
5392
+ const { installationId } = request.params;
5393
+ try {
5394
+ const store = getInstallationStore();
5395
+ const installation = await store.getInstallationById(installationId);
5396
+ if (!installation) {
5397
+ reply.code(404);
5398
+ return {
5399
+ success: false,
5400
+ message: "Channel installation not found"
5401
+ };
5402
+ }
5403
+ if (installation.tenantId !== tenantId) {
5404
+ reply.code(403);
5405
+ return {
5406
+ success: false,
5407
+ message: "Access denied"
5408
+ };
5409
+ }
5410
+ return {
5411
+ success: true,
5412
+ message: "Channel installation retrieved successfully",
5413
+ data: installation
5414
+ };
5415
+ } catch (error) {
5416
+ console.error("Failed to get channel installation:", error);
5417
+ return {
5418
+ success: false,
5419
+ message: "Failed to retrieve channel installation"
5420
+ };
5421
+ }
5422
+ }
5423
+ async function createChannelInstallation(request, reply) {
5424
+ const tenantId = getTenantId9(request);
5425
+ const body = request.body;
5426
+ try {
5427
+ if (!body.channel) {
5428
+ reply.code(400);
5429
+ return {
5430
+ success: false,
5431
+ message: "Channel type is required"
5432
+ };
5433
+ }
5434
+ if (!body.config) {
5435
+ reply.code(400);
5436
+ return {
5437
+ success: false,
5438
+ message: "Configuration is required"
5439
+ };
5440
+ }
5441
+ if (body.channel === "lark") {
5442
+ const larkConfig = body.config;
5443
+ if (!larkConfig.appId || !larkConfig.appSecret) {
5444
+ reply.code(400);
5445
+ return {
5446
+ success: false,
5447
+ message: "appId and appSecret are required for Lark installations"
5448
+ };
5449
+ }
5450
+ if (!larkConfig.assistantId) {
5451
+ reply.code(400);
5452
+ return {
5453
+ success: false,
5454
+ message: "assistantId is required for Lark installations"
5455
+ };
5456
+ }
5457
+ }
5458
+ const store = getInstallationStore();
5459
+ const installationId = body.id || randomUUID7();
5460
+ const installation = await store.createInstallation(
5461
+ tenantId,
5462
+ installationId,
5463
+ body
5464
+ );
5465
+ reply.code(201);
5466
+ return {
5467
+ success: true,
5468
+ message: "Channel installation created successfully",
5469
+ data: installation
5470
+ };
5471
+ } catch (error) {
5472
+ console.error("Failed to create channel installation:", error);
5473
+ if (error.message?.includes("duplicate") || error.code === "23505") {
5474
+ reply.code(409);
5475
+ return {
5476
+ success: false,
5477
+ message: "Channel installation with this ID already exists"
5478
+ };
5479
+ }
5480
+ return {
5481
+ success: false,
5482
+ message: "Failed to create channel installation"
5483
+ };
5484
+ }
5485
+ }
5486
+ async function updateChannelInstallation(request, reply) {
5487
+ const tenantId = getTenantId9(request);
5488
+ const { installationId } = request.params;
5489
+ const body = request.body;
5490
+ try {
5491
+ const store = getInstallationStore();
5492
+ const existing = await store.getInstallationById(installationId);
5493
+ if (!existing) {
5494
+ reply.code(404);
5495
+ return {
5496
+ success: false,
5497
+ message: "Channel installation not found"
5498
+ };
5499
+ }
5500
+ if (existing.tenantId !== tenantId) {
5501
+ reply.code(403);
5502
+ return {
5503
+ success: false,
5504
+ message: "Access denied"
5505
+ };
5506
+ }
5507
+ const installation = await store.updateInstallation(
5508
+ tenantId,
5509
+ installationId,
5510
+ body
5511
+ );
5512
+ if (!installation) {
5513
+ reply.code(404);
5514
+ return {
5515
+ success: false,
5516
+ message: "Channel installation not found"
5517
+ };
5518
+ }
5519
+ return {
5520
+ success: true,
5521
+ message: "Channel installation updated successfully",
5522
+ data: installation
5523
+ };
5524
+ } catch (error) {
5525
+ console.error("Failed to update channel installation:", error);
5526
+ return {
5527
+ success: false,
5528
+ message: "Failed to update channel installation"
5529
+ };
5530
+ }
5531
+ }
5532
+ async function deleteChannelInstallation(request, reply) {
5533
+ const tenantId = getTenantId9(request);
5534
+ const { installationId } = request.params;
5535
+ try {
5536
+ const store = getInstallationStore();
5537
+ const existing = await store.getInstallationById(installationId);
5538
+ if (!existing) {
5539
+ reply.code(404);
5540
+ return {
5541
+ success: false,
5542
+ message: "Channel installation not found"
5543
+ };
5544
+ }
5545
+ if (existing.tenantId !== tenantId) {
5546
+ reply.code(403);
5547
+ return {
5548
+ success: false,
5549
+ message: "Access denied"
5550
+ };
5551
+ }
5552
+ const deleted = await store.deleteInstallation(tenantId, installationId);
5553
+ if (!deleted) {
5554
+ reply.code(404);
5555
+ return {
5556
+ success: false,
5557
+ message: "Channel installation not found"
5558
+ };
5559
+ }
5560
+ return {
5561
+ success: true,
5562
+ message: "Channel installation deleted successfully"
5563
+ };
5564
+ } catch (error) {
5565
+ console.error("Failed to delete channel installation:", error);
5566
+ return {
5567
+ success: false,
5568
+ message: "Failed to delete channel installation"
5569
+ };
5570
+ }
5571
+ }
5572
+
5573
+ // src/routes/channel-installations.ts
5574
+ function registerChannelInstallationRoutes(app2) {
5575
+ app2.get("/api/channel-installations", getChannelInstallationList);
5576
+ app2.get("/api/channel-installations/:installationId", getChannelInstallation);
5577
+ app2.post("/api/channel-installations", createChannelInstallation);
5578
+ app2.put("/api/channel-installations/:installationId", updateChannelInstallation);
5579
+ app2.delete("/api/channel-installations/:installationId", deleteChannelInstallation);
5580
+ }
5581
+
4881
5582
  // src/routes/index.ts
4882
5583
  var registerLatticeRoutes = (app2) => {
4883
5584
  app2.post("/api/runs", createRun);
@@ -5013,6 +5714,8 @@ var registerLatticeRoutes = (app2) => {
5013
5714
  autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
5014
5715
  allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
5015
5716
  });
5717
+ registerChannelRoutes(app2);
5718
+ registerChannelInstallationRoutes(app2);
5016
5719
  app2.delete(
5017
5720
  "/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
5018
5721
  removePendingMessageHandler
@@ -5082,7 +5785,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
5082
5785
  };
5083
5786
 
5084
5787
  // src/services/agent_task_consumer.ts
5085
- import { eventBus as eventBus2, AGENT_TASK_EVENT, agentInstanceManager as agentInstanceManager5, QueueMode as QueueMode2 } from "@axiom-lattice/core";
5788
+ import { eventBus as eventBus2, AGENT_TASK_EVENT, agentInstanceManager as agentInstanceManager6, QueueMode as QueueMode2 } from "@axiom-lattice/core";
5086
5789
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
5087
5790
  const {
5088
5791
  assistant_id,
@@ -5097,7 +5800,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
5097
5800
  console.log(
5098
5801
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
5099
5802
  );
5100
- const agent = agentInstanceManager5.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
5803
+ const agent = agentInstanceManager6.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
5101
5804
  await agent.addMessage({ input, command, custom_run_config: runConfig }, QueueMode2.STEER);
5102
5805
  if (callback_event) {
5103
5806
  agent.subscribeOnce("message:completed", (evt) => {
@@ -5315,8 +6018,8 @@ import {
5315
6018
  loggerLatticeManager,
5316
6019
  sandboxLatticeManager as sandboxLatticeManager2,
5317
6020
  sqlDatabaseManager as sqlDatabaseManager2,
5318
- getStoreLattice as getStoreLattice12,
5319
- agentInstanceManager as agentInstanceManager6
6021
+ getStoreLattice as getStoreLattice13,
6022
+ agentInstanceManager as agentInstanceManager7
5320
6023
  } from "@axiom-lattice/core";
5321
6024
  import {
5322
6025
  LoggerType
@@ -5445,7 +6148,7 @@ var start = async (config) => {
5445
6148
  app.decorate("loggerLattice", loggerLattice);
5446
6149
  registerLatticeRoutes(app);
5447
6150
  try {
5448
- const storeLattice = getStoreLattice12("default", "database");
6151
+ const storeLattice = getStoreLattice13("default", "database");
5449
6152
  const store = storeLattice.store;
5450
6153
  sqlDatabaseManager2.setConfigStore(store);
5451
6154
  logger.info("Database config store set for SqlDatabaseManager");
@@ -5477,7 +6180,7 @@ var start = async (config) => {
5477
6180
  }
5478
6181
  try {
5479
6182
  logger.info("Starting agent instance recovery...");
5480
- const restoreStats = await agentInstanceManager6.restore();
6183
+ const restoreStats = await agentInstanceManager7.restore();
5481
6184
  logger.info(`Agent recovery complete: ${restoreStats.restored} threads restored, ${restoreStats.errors} errors`);
5482
6185
  } catch (error) {
5483
6186
  logger.error("Agent recovery failed", { error });