@axiom-lattice/gateway 2.1.81 → 2.1.82

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/gateway",
3
- "version": "2.1.81",
3
+ "version": "2.1.82",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -287,8 +287,8 @@ const start = async (config?: LatticeGatewayConfig) => {
287
287
 
288
288
  const router = new MessageRouter({
289
289
  middlewares: [
290
- createDeduplicationMiddleware(),
291
- createRateLimitMiddleware(),
290
+ createDeduplicationMiddleware(undefined, logger),
291
+ createRateLimitMiddleware(undefined, undefined, undefined, logger),
292
292
  createAuditLoggerMiddleware(),
293
293
  ],
294
294
  bindingRegistry: bindingStore,
@@ -59,6 +59,13 @@ export class MessageRouter {
59
59
  || (await this.installationStore.getInstallationById(message.channelInstallationId))?.tenantId;
60
60
 
61
61
  if (!tenantId) {
62
+ this.logger?.error({
63
+ event: "dispatch:error",
64
+ error: "tenantId missing",
65
+ channel: message.channel,
66
+ channelInstallationId: message.channelInstallationId,
67
+ senderId: message.sender.id,
68
+ }, "tenantId is required");
62
69
  throw new Error(
63
70
  "tenantId is required: provide it in the message or ensure the channelInstallation has a tenantId"
64
71
  );
@@ -79,12 +86,25 @@ export class MessageRouter {
79
86
  );
80
87
 
81
88
  if (installation?.rejectWhenNoBinding) {
89
+ this.logger?.warn({
90
+ event: "dispatch:no_binding",
91
+ channel: message.channel,
92
+ senderId: message.sender.id,
93
+ tenantId,
94
+ channelInstallationId: message.channelInstallationId,
95
+ }, "No binding found and rejectWhenNoBinding is enabled");
82
96
  throw new BindingNotFoundError(
83
97
  `No binding for sender "${message.sender.id}" on channel "${message.channel}"`,
84
98
  );
85
99
  }
86
100
 
87
101
  if (installation?.fallbackAgentId) {
102
+ this.logger?.warn({
103
+ event: "dispatch:fallback",
104
+ channel: message.channel,
105
+ senderId: message.sender.id,
106
+ fallbackAgentId: installation.fallbackAgentId,
107
+ }, "No binding found, falling back to fallbackAgentId");
88
108
  binding = {
89
109
  id: "fallback",
90
110
  channel: message.channel,
@@ -99,6 +119,12 @@ export class MessageRouter {
99
119
  updatedAt: new Date(),
100
120
  };
101
121
  } else {
122
+ this.logger?.error({
123
+ event: "dispatch:no_fallback",
124
+ channel: message.channel,
125
+ senderId: message.sender.id,
126
+ tenantId,
127
+ }, "No binding found and no fallbackAgentId configured");
102
128
  throw new BindingNotFoundError(
103
129
  `No binding for sender "${message.sender.id}" and no fallback configured`,
104
130
  );
@@ -126,6 +152,12 @@ export class MessageRouter {
126
152
  }
127
153
 
128
154
  if (!binding.enabled) {
155
+ this.logger?.warn({
156
+ event: "dispatch:binding_disabled",
157
+ bindingId: binding.id,
158
+ agentId: binding.agentId,
159
+ senderId: message.sender.id,
160
+ }, "Binding is disabled, rejecting message");
129
161
  throw new BindingNotFoundError(
130
162
  `Binding for sender "${message.sender.id}" is disabled`,
131
163
  );
@@ -2,18 +2,31 @@ import type { MessageMiddleware } from "../MessageContext";
2
2
 
3
3
  const processedMessages = new Map<string, number>();
4
4
 
5
- export function createDeduplicationMiddleware(ttlMs: number = 5 * 60 * 1000): MessageMiddleware {
5
+ export function createDeduplicationMiddleware(
6
+ ttlMs: number = 5 * 60 * 1000,
7
+ logger?: any,
8
+ ): MessageMiddleware {
6
9
  return async (ctx, next) => {
7
10
  const msg = ctx.inboundMessage;
8
11
  const msgId = msg.content.metadata?.messageId as string | undefined;
9
12
  const key = msgId
10
13
  ? `${msg.channel}:${msg.channelInstallationId}:${msgId}`
11
- : `${msg.channel}:${msg.channelInstallationId}:${msg.sender.id}`;
12
- const now = Date.now();
13
- const lastProcessed = processedMessages.get(key);
14
- if (lastProcessed && (now - lastProcessed) < ttlMs) return;
14
+ : null;
15
15
 
16
- processedMessages.set(key, now);
16
+ if (key) {
17
+ const now = Date.now();
18
+ const lastProcessed = processedMessages.get(key);
19
+ if (lastProcessed && (now - lastProcessed) < ttlMs) {
20
+ logger?.warn({
21
+ event: "dedup:blocked",
22
+ channel: msg.channel,
23
+ senderId: msg.sender.id,
24
+ messageId: msgId,
25
+ }, "Duplicate message blocked by deduplication");
26
+ return;
27
+ }
28
+ processedMessages.set(key, now);
29
+ }
17
30
 
18
31
  if (processedMessages.size > 10000) {
19
32
  const oldest = Array.from(processedMessages.entries())
@@ -14,6 +14,7 @@ export function createRateLimitMiddleware(
14
14
  maxRequests: number = 10,
15
15
  windowMs: number = 60 * 1000,
16
16
  maxEntries: number = 10000,
17
+ logger?: any,
17
18
  ): MessageMiddleware {
18
19
  return async (ctx, next) => {
19
20
  const senderKey = `${ctx.inboundMessage.channel}:${ctx.inboundMessage.sender.id}`;
@@ -32,6 +33,13 @@ export function createRateLimitMiddleware(
32
33
  }
33
34
 
34
35
  if (counter.count > maxRequests) {
36
+ logger?.warn({
37
+ event: "ratelimit:blocked",
38
+ channel: ctx.inboundMessage.channel,
39
+ senderId: ctx.inboundMessage.sender.id,
40
+ count: counter.count,
41
+ maxRequests,
42
+ }, "Rate limit exceeded");
35
43
  throw new RateLimitError(`Rate limit exceeded`);
36
44
  }
37
45
  await next();
@@ -390,7 +390,12 @@ export const registerLatticeRoutes = (app: FastifyInstance, channelDeps?: { rout
390
390
  } as Parameters<typeof router.dispatch>[0];
391
391
 
392
392
  await router.dispatch(inboundMessage).catch((error) => {
393
- console.error("Inbound dispatch error:", error);
393
+ console.error(JSON.stringify({
394
+ event: "inbound:dispatch_error",
395
+ error: error instanceof Error ? error.message : String(error),
396
+ channel: inboundMessage.channel,
397
+ senderId: inboundMessage.sender.id,
398
+ }));
394
399
  });
395
400
 
396
401
  reply.status(200).send({ accepted: true });