@axiom-lattice/gateway 2.1.80 → 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.80",
3
+ "version": "2.1.82",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -40,9 +40,9 @@
40
40
  "redis": "^5.0.1",
41
41
  "uuid": "^9.0.1",
42
42
  "zod": "3.25.76",
43
- "@axiom-lattice/agent-eval": "2.1.64",
44
- "@axiom-lattice/core": "2.1.70",
45
- "@axiom-lattice/pg-stores": "1.0.60",
43
+ "@axiom-lattice/agent-eval": "2.1.65",
44
+ "@axiom-lattice/core": "2.1.71",
45
+ "@axiom-lattice/pg-stores": "1.0.61",
46
46
  "@axiom-lattice/protocols": "2.1.36",
47
47
  "@axiom-lattice/queue-redis": "1.0.35"
48
48
  },
package/src/index.ts CHANGED
@@ -287,13 +287,14 @@ 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,
295
295
  adapterRegistry,
296
296
  installationStore,
297
+ logger,
297
298
  });
298
299
 
299
300
  channelDeps = { router, installationStore };
@@ -24,6 +24,7 @@ export interface MessageRouterConfig {
24
24
  bindingRegistry: BindingRegistry;
25
25
  adapterRegistry: ChannelAdapterRegistry;
26
26
  installationStore: ChannelInstallationStore;
27
+ logger?: any;
27
28
  }
28
29
 
29
30
  export class MessageRouter {
@@ -31,12 +32,14 @@ export class MessageRouter {
31
32
  private bindingRegistry: BindingRegistry;
32
33
  private adapterRegistry: ChannelAdapterRegistry;
33
34
  private installationStore: ChannelInstallationStore;
35
+ private logger?: any;
34
36
 
35
37
  constructor(config: MessageRouterConfig) {
36
38
  this.middlewares = [...config.middlewares];
37
39
  this.bindingRegistry = config.bindingRegistry;
38
40
  this.adapterRegistry = config.adapterRegistry;
39
41
  this.installationStore = config.installationStore;
42
+ this.logger = config.logger;
40
43
  }
41
44
 
42
45
  use(middleware: MessageMiddleware): void {
@@ -56,11 +59,20 @@ export class MessageRouter {
56
59
  || (await this.installationStore.getInstallationById(message.channelInstallationId))?.tenantId;
57
60
 
58
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");
59
69
  throw new Error(
60
70
  "tenantId is required: provide it in the message or ensure the channelInstallation has a tenantId"
61
71
  );
62
72
  }
63
73
 
74
+ this.logger?.info({ event: "dispatch:start", channel: message.channel, senderId: message.sender.id, tenantId }, "Message dispatch started");
75
+
64
76
  let binding = await this.bindingRegistry.resolve({
65
77
  channel: message.channel,
66
78
  senderId: message.sender.id,
@@ -74,12 +86,25 @@ export class MessageRouter {
74
86
  );
75
87
 
76
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");
77
96
  throw new BindingNotFoundError(
78
97
  `No binding for sender "${message.sender.id}" on channel "${message.channel}"`,
79
98
  );
80
99
  }
81
100
 
82
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");
83
108
  binding = {
84
109
  id: "fallback",
85
110
  channel: message.channel,
@@ -94,6 +119,12 @@ export class MessageRouter {
94
119
  updatedAt: new Date(),
95
120
  };
96
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");
97
128
  throw new BindingNotFoundError(
98
129
  `No binding for sender "${message.sender.id}" and no fallback configured`,
99
130
  );
@@ -102,7 +133,31 @@ export class MessageRouter {
102
133
 
103
134
  ctx.binding = binding;
104
135
 
136
+ this.logger?.info({
137
+ event: "dispatch:binding",
138
+ bindingId: binding.id,
139
+ agentId: binding.agentId,
140
+ threadId: binding.threadId,
141
+ threadMode: binding.threadMode,
142
+ workspaceId: binding.workspaceId,
143
+ projectId: binding.projectId,
144
+ }, "Binding resolved");
145
+
146
+ if (binding.threadMode === "per_conversation") {
147
+ this.logger?.warn({
148
+ event: "dispatch:per_conversation",
149
+ bindingId: binding.id,
150
+ conversationId: message.conversation?.id,
151
+ }, "per_conversation mode active — thread lookup by conversation not yet implemented, using binding.threadId");
152
+ }
153
+
105
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");
106
161
  throw new BindingNotFoundError(
107
162
  `Binding for sender "${message.sender.id}" is disabled`,
108
163
  );
@@ -112,8 +167,14 @@ export class MessageRouter {
112
167
  if (!threadId) {
113
168
  const threadStore = getStoreLattice("default", "thread").store;
114
169
  const newThreadId = randomUUID();
170
+ this.logger?.info({
171
+ event: "dispatch:thread:create",
172
+ agentId: ctx.binding.agentId,
173
+ newThreadId,
174
+ tenantId: tenantId!,
175
+ }, "Creating new thread for binding");
115
176
  const newThread = await threadStore.createThread(
116
- tenantId,
177
+ tenantId!,
117
178
  ctx.binding.agentId,
118
179
  newThreadId,
119
180
  {
@@ -134,19 +195,35 @@ export class MessageRouter {
134
195
  }
135
196
  }
136
197
 
198
+ this.logger?.info({
199
+ event: "dispatch:agent",
200
+ agentId: ctx.binding.agentId,
201
+ threadId,
202
+ threadMode: ctx.binding.threadMode,
203
+ senderId: message.sender.id,
204
+ contentLength: message.content.text.length,
205
+ }, "Dispatching to agent");
206
+
137
207
  const agent = agentInstanceManager.getAgent({
138
- tenant_id: tenantId,
208
+ tenant_id: tenantId!,
139
209
  assistant_id: ctx.binding.agentId,
140
210
  thread_id: threadId,
141
211
  workspace_id: ctx.binding.workspaceId || "",
142
212
  project_id: ctx.binding.projectId || "",
143
213
  });
144
214
 
145
- const invokeResult = await agent.invoke({
215
+ const addResult = await agent.addMessage({
146
216
  input: { message: message.content.text },
217
+ custom_run_config: message.content.metadata || {},
147
218
  });
148
219
 
149
- ctx.result = extractTextFromInvokeResult(invokeResult);
220
+ this.logger?.info({
221
+ event: "dispatch:complete",
222
+ agentId: ctx.binding.agentId,
223
+ threadId,
224
+ messageId: (addResult as Record<string, unknown>)?.messageId,
225
+ result: JSON.stringify(addResult),
226
+ }, "Agent dispatch complete — messageId = " + ((addResult as Record<string, unknown>)?.messageId || "N/A"));
150
227
 
151
228
  if (message.replyTarget) {
152
229
  const adapter = this.adapterRegistry.get(message.replyTarget.adapterChannel);
@@ -157,7 +234,7 @@ export class MessageRouter {
157
234
  if (installation) {
158
235
  await adapter.sendReply(
159
236
  message.replyTarget,
160
- { text: ctx.result },
237
+ { text: ctx.result || "" },
161
238
  installation,
162
239
  );
163
240
  }
@@ -173,6 +250,7 @@ export class MessageRouter {
173
250
  };
174
251
  } catch (error) {
175
252
  ctx.error = error instanceof Error ? error : new Error(String(error));
253
+ this.logger?.error({ event: "dispatch:error", error: ctx.error.message, channel: message.channel, senderId: message.sender.id }, "Message dispatch failed");
176
254
  return {
177
255
  success: false,
178
256
  bindingId: ctx.binding?.id,
@@ -196,16 +274,3 @@ export class MessageRouter {
196
274
  return dispatch(0);
197
275
  }
198
276
  }
199
-
200
- function extractTextFromInvokeResult(result: unknown): string {
201
- if (result && typeof result === "object" && "messages" in result) {
202
- const messages = (result as { messages: Array<{ role: string; content: string }> }).messages;
203
- if (Array.isArray(messages)) {
204
- const aiMessages = messages.filter((m) => m.role === "ai");
205
- if (aiMessages.length > 0) {
206
- return aiMessages.map((m) => m.content).join("\n");
207
- }
208
- }
209
- }
210
- return JSON.stringify(result);
211
- }
@@ -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 });
@@ -1,32 +0,0 @@
1
-
2
- > @axiom-lattice/gateway@2.1.80 build /home/runner/work/agentic/agentic/packages/gateway
3
- > tsup src/index.ts --format cjs,esm --dts --clean --sourcemap
4
-
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.0
8
- CLI Target: es2020
9
- CLI Cleaning output folder
10
- CJS Build start
11
- ESM Build start
12
- [warn] ▲ [WARNING] "import.meta" is not available with the "cjs" output format and will be empty [empty-import-meta]
13
-
14
- src/index.ts:178:33:
15
-  178 │ const __filename = fileURLToPath(import.meta.url);
16
- ╵ ~~~~~~~~~~~
17
-
18
- You need to set the output format to "esm" for "import.meta" to work correctly.
19
-
20
-
21
- CJS dist/index.js 239.18 KB
22
- CJS dist/index.js.map 502.61 KB
23
- CJS ⚡️ Build success in 314ms
24
- ESM dist/index.mjs 234.48 KB
25
- ESM dist/sender-PX32VSHB.mjs 873.00 B
26
- ESM dist/index.mjs.map 501.10 KB
27
- ESM dist/sender-PX32VSHB.mjs.map 2.07 KB
28
- ESM ⚡️ Build success in 322ms
29
- DTS Build start
30
- DTS ⚡️ Build success in 14133ms
31
- DTS dist/index.d.ts 5.01 KB
32
- DTS dist/index.d.mts 5.01 KB