@axiom-lattice/gateway 2.1.99 → 2.1.101

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @axiom-lattice/gateway@2.1.99 build /home/runner/work/agentic/agentic/packages/gateway
2
+ > @axiom-lattice/gateway@2.1.101 build /home/runner/work/agentic/agentic/packages/gateway
3
3
  > tsup src/index.ts --format cjs,esm --dts --clean --sourcemap
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -9,6 +9,17 @@
9
9
  CLI Cleaning output folder
10
10
  CJS Build start
11
11
  ESM Build start
12
+ ESM dist/index.mjs 271.22 KB
13
+ ESM dist/sender-PX32VSHB.mjs 873.00 B
14
+ ESM dist/WechatChannelAdapter-WSDKR4OA.mjs 8.27 KB
15
+ ESM dist/chunk-6CUQGDJI.mjs 6.42 KB
16
+ ESM dist/a2a-ERG5RMUW.mjs 15.95 KB
17
+ ESM dist/index.mjs.map 575.35 KB
18
+ ESM dist/sender-PX32VSHB.mjs.map 2.07 KB
19
+ ESM dist/WechatChannelAdapter-WSDKR4OA.mjs.map 16.28 KB
20
+ ESM dist/chunk-6CUQGDJI.mjs.map 14.04 KB
21
+ ESM dist/a2a-ERG5RMUW.mjs.map 32.14 KB
22
+ ESM ⚡️ Build success in 475ms
12
23
  [warn] ▲ [WARNING] "import.meta" is not available with the "cjs" output format and will be empty [empty-import-meta]
13
24
 
14
25
  src/index.ts:189:33:
@@ -18,21 +29,10 @@
18
29
  You need to set the output format to "esm" for "import.meta" to work correctly.
19
30
 
20
31
 
21
- ESM dist/index.mjs 270.95 KB
22
- ESM dist/sender-PX32VSHB.mjs 873.00 B
23
- ESM dist/WechatChannelAdapter-QQYOHZTL.mjs 7.95 KB
24
- ESM dist/chunk-6CUQGDJI.mjs 6.42 KB
25
- ESM dist/a2a-ERG5RMUW.mjs 15.95 KB
26
- ESM dist/index.mjs.map 575.03 KB
27
- ESM dist/sender-PX32VSHB.mjs.map 2.07 KB
28
- ESM dist/WechatChannelAdapter-QQYOHZTL.mjs.map 15.71 KB
29
- ESM dist/chunk-6CUQGDJI.mjs.map 14.04 KB
30
- ESM dist/a2a-ERG5RMUW.mjs.map 32.14 KB
31
- ESM ⚡️ Build success in 548ms
32
- CJS dist/index.js 308.91 KB
33
- CJS dist/index.js.map 639.22 KB
34
- CJS ⚡️ Build success in 576ms
32
+ CJS dist/index.js 309.50 KB
33
+ CJS dist/index.js.map 640.11 KB
34
+ CJS ⚡️ Build success in 507ms
35
35
  DTS Build start
36
- DTS ⚡️ Build success in 22055ms
36
+ DTS ⚡️ Build success in 19468ms
37
37
  DTS dist/index.d.ts 7.57 KB
38
38
  DTS dist/index.d.mts 7.57 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @axiom-lattice/gateway
2
2
 
3
+ ## 2.1.101
4
+
5
+ ### Patch Changes
6
+
7
+ - 0552021: WeChat channel improvements:
8
+ - QR code setup: replace auto-polling with manual refresh button + 5-min countdown timer
9
+ - context-store: key by installationId::senderId to prevent cross-installation token collision
10
+ - 13cd999: fix sandbox
11
+ - Updated dependencies [13cd999]
12
+ - @axiom-lattice/core@2.1.89
13
+ - @axiom-lattice/agent-eval@2.1.83
14
+ - @axiom-lattice/pg-stores@1.0.80
15
+
16
+ ## 2.1.100
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies [44c570c]
21
+ - Updated dependencies [2b71973]
22
+ - @axiom-lattice/core@2.1.88
23
+ - @axiom-lattice/agent-eval@2.1.82
24
+ - @axiom-lattice/pg-stores@1.0.79
25
+
3
26
  ## 2.1.99
4
27
 
5
28
  ### Patch Changes
@@ -11,31 +11,37 @@ import { z } from "zod";
11
11
  var TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
12
12
  var store = /* @__PURE__ */ new Map();
13
13
  var timers = /* @__PURE__ */ new Map();
14
- function getContextToken(senderId) {
15
- const entry = store.get(senderId);
14
+ function makeKey(installationId, senderId) {
15
+ return `${installationId}::${senderId}`;
16
+ }
17
+ function getContextToken(installationId, senderId) {
18
+ const key = makeKey(installationId, senderId);
19
+ const entry = store.get(key);
16
20
  if (!entry) return void 0;
17
21
  if (Date.now() - entry.updatedAt > TOKEN_TTL_MS) {
18
- deleteContextToken(senderId);
22
+ deleteContextToken(installationId, senderId);
19
23
  return void 0;
20
24
  }
21
25
  return entry.token;
22
26
  }
23
- function setContextToken(senderId, token) {
24
- const existingTimer = timers.get(senderId);
27
+ function setContextToken(installationId, senderId, token) {
28
+ const key = makeKey(installationId, senderId);
29
+ const existingTimer = timers.get(key);
25
30
  if (existingTimer) clearTimeout(existingTimer);
26
- store.set(senderId, { token, senderId, updatedAt: Date.now() });
31
+ store.set(key, { token, installationId, senderId, updatedAt: Date.now() });
27
32
  const timer = setTimeout(() => {
28
- store.delete(senderId);
29
- timers.delete(senderId);
33
+ store.delete(key);
34
+ timers.delete(key);
30
35
  }, TOKEN_TTL_MS);
31
- timers.set(senderId, timer);
36
+ timers.set(key, timer);
32
37
  }
33
- function deleteContextToken(senderId) {
34
- store.delete(senderId);
35
- const timer = timers.get(senderId);
38
+ function deleteContextToken(installationId, senderId) {
39
+ const key = makeKey(installationId, senderId);
40
+ store.delete(key);
41
+ const timer = timers.get(key);
36
42
  if (timer) {
37
43
  clearTimeout(timer);
38
- timers.delete(senderId);
44
+ timers.delete(key);
39
45
  }
40
46
  }
41
47
 
@@ -81,7 +87,7 @@ var wechatChannelAdapter = {
81
87
  const text = extractText(msg);
82
88
  if (!text) return null;
83
89
  if (msg.context_token) {
84
- setContextToken(senderId, msg.context_token);
90
+ setContextToken(installation.id, senderId, msg.context_token);
85
91
  }
86
92
  return {
87
93
  channel: "wechat",
@@ -107,13 +113,13 @@ var wechatChannelAdapter = {
107
113
  },
108
114
  async sendReply(replyTarget, message, installation) {
109
115
  const senderId = replyTarget.rawTarget.senderId;
110
- const contextToken = getContextToken(senderId);
116
+ const contextToken = getContextToken(installation.id, senderId);
111
117
  if (!contextToken) {
112
118
  logger.warn("WeChat context token expired, cannot send reply", {
113
119
  installationId: installation.id,
114
120
  senderId
115
121
  });
116
- deleteContextToken(senderId);
122
+ deleteContextToken(installation.id, senderId);
117
123
  return;
118
124
  }
119
125
  const { botToken } = installation.config;
@@ -246,4 +252,4 @@ var wechatChannelAdapter = {
246
252
  export {
247
253
  wechatChannelAdapter
248
254
  };
249
- //# sourceMappingURL=WechatChannelAdapter-QQYOHZTL.mjs.map
255
+ //# sourceMappingURL=WechatChannelAdapter-WSDKR4OA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/channels/wechat/WechatChannelAdapter.ts","../src/channels/wechat/context-store.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type {\n ChannelAdapter,\n InboundMessage,\n OutboundMessage,\n ReplyTarget,\n ChannelInstallation,\n} from \"@axiom-lattice/protocols\";\nimport type { WechatChannelInstallationConfig } from \"@axiom-lattice/protocols\";\nimport { getUpdates, sendMessage } from \"./wechat-client\";\nimport { setContextToken, getContextToken, deleteContextToken } from \"./context-store\";\nimport { Logger } from \"../../logger/Logger\";\n\nconst logger = new Logger({ serviceName: \"lattice/gateway/wechat\" });\n\nconst wechatConfigSchema = z.object({\n botToken: z.string(),\n uin: z.string().optional(),\n});\n\nconst MAX_RECONNECT_DELAY_MS = 30_000;\nconst BASE_RECONNECT_DELAY_MS = 1_000;\nconst HEARTBEAT_INTERVAL_MS = 60_000;\nconst CHANNEL_VERSION = \"1.0.2\";\n\n// ─── Message type constants ───────────────────────────────────────────────\n\nconst MSG_TYPE_USER = 1;\nconst MSG_ITEM_TEXT = 1;\n\n// ─── Polling connection state ─────────────────────────────────────────────\n\ninterface PollingState {\n installationId: string;\n abortController: AbortController;\n lastActivity: number;\n heartbeatTimer: NodeJS.Timeout;\n}\n\nconst activeConnections = new Map<string, PollingState>();\n\n// ─── Message dedup ────────────────────────────────────────────────────────\n\nconst seenClientIds = new Set<string>();\n\nfunction addToDedup(clientId: string): boolean {\n if (!clientId || seenClientIds.has(clientId)) return false;\n seenClientIds.add(clientId);\n if (seenClientIds.size > 1000) {\n const first = seenClientIds.values().next().value;\n if (first !== undefined) seenClientIds.delete(first);\n }\n return true;\n}\n\n// ─── Text extraction from iLink message ───────────────────────────────────\n\nfunction extractText(msg: { item_list?: Array<{ type?: number; text_item?: { text?: string } }> }): string | null {\n if (!msg.item_list?.length) return null;\n for (const item of msg.item_list) {\n if (item.type === MSG_ITEM_TEXT && item.text_item?.text) {\n return item.text_item.text;\n }\n }\n return null;\n}\n\n// ─── Adapter ──────────────────────────────────────────────────────────────\n\nexport const wechatChannelAdapter: ChannelAdapter<WechatChannelInstallationConfig> = {\n channel: \"wechat\",\n\n configSchema: wechatConfigSchema,\n\n async receive(\n rawPayload: unknown,\n installation: ChannelInstallation<WechatChannelInstallationConfig>,\n ): Promise<InboundMessage | null> {\n const msg = rawPayload as {\n from_user_id?: string;\n client_id?: string;\n message_type?: number;\n item_list?: Array<{ type?: number; text_item?: { text?: string } }>;\n context_token?: string;\n };\n\n // Only process user messages (type 1), skip bot echoes (type 2)\n if (msg.message_type !== MSG_TYPE_USER) return null;\n\n const senderId = msg.from_user_id;\n if (!senderId) return null;\n\n const text = extractText(msg);\n if (!text) return null;\n\n // Cache context token for reply\n if (msg.context_token) {\n setContextToken(installation.id, senderId, msg.context_token);\n }\n\n return {\n channel: \"wechat\",\n channelInstallationId: installation.id,\n tenantId: installation.tenantId,\n sender: {\n id: senderId,\n displayName: senderId.split(\"@\")[0],\n },\n content: {\n text,\n },\n conversation: {\n id: senderId,\n type: \"direct\",\n },\n replyTarget: {\n adapterChannel: \"wechat\",\n channelInstallationId: installation.id,\n rawTarget: { senderId },\n },\n };\n },\n\n async sendReply(\n replyTarget: ReplyTarget,\n message: OutboundMessage,\n installation: ChannelInstallation<WechatChannelInstallationConfig>,\n ): Promise<void> {\n const senderId = replyTarget.rawTarget.senderId as string;\n const contextToken = getContextToken(installation.id, senderId);\n\n if (!contextToken) {\n logger.warn(\"WeChat context token expired, cannot send reply\", {\n installationId: installation.id,\n senderId,\n });\n deleteContextToken(installation.id, senderId);\n return;\n }\n\n const { botToken } = installation.config;\n await sendMessage(botToken, senderId, message.text, contextToken);\n },\n\n resolveThreadId(message: InboundMessage, binding: unknown): string {\n const date = new Date().toISOString().split(\"T\")[0];\n const agentId = (binding as { agentId: string }).agentId;\n return `wechat:dm:${message.sender.id}:${agentId}:${date}`;\n },\n\n async connect(\n installation: ChannelInstallation<WechatChannelInstallationConfig>,\n deps?: unknown,\n ): Promise<void> {\n const { id: installationId, tenantId, config } = installation;\n\n if (!config.botToken) {\n logger.warn(\"WeChat installation missing botToken, skipping\", { installationId });\n return;\n }\n\n if (activeConnections.has(installationId)) {\n logger.warn(\"WeChat polling already running for installation, skipping\", { installationId });\n return;\n }\n\n logger.info(\"WeChat polling starting\", { installationId, tenantId });\n\n const abortController = new AbortController();\n\n const heartbeatTimer = setInterval(() => {\n const state2 = activeConnections.get(installationId);\n if (!state2) {\n clearInterval(heartbeatTimer);\n return;\n }\n const elapsed = Date.now() - state2.lastActivity;\n if (elapsed > HEARTBEAT_INTERVAL_MS * 2) {\n logger.error(\"WeChat polling heartbeat lost — no activity\", {\n installationId,\n elapsedMs: elapsed,\n });\n state2.abortController.abort();\n }\n }, HEARTBEAT_INTERVAL_MS);\n\n const state: PollingState = {\n installationId,\n abortController,\n lastActivity: Date.now(),\n heartbeatTimer,\n };\n activeConnections.set(installationId, state);\n\n const router = (deps as { router?: { dispatch(msg: InboundMessage): Promise<unknown> } })?.router;\n\n let syncBuffer = \"\";\n let reconnectDelay = BASE_RECONNECT_DELAY_MS;\n\n const scheduleNextPoll = (): void => {\n poll().catch((err) => {\n logger.error(\"WeChat poll iteration crashed\", {\n installationId,\n error: err instanceof Error ? err.message : String(err),\n });\n const currentState = activeConnections.get(installationId);\n if (currentState && !currentState.abortController.signal.aborted) {\n setTimeout(scheduleNextPoll, reconnectDelay);\n }\n });\n };\n\n const poll = async (): Promise<void> => {\n const currentState = activeConnections.get(installationId);\n if (!currentState || currentState.abortController.signal.aborted) {\n logger.info(\"WeChat polling aborted\", { installationId });\n return;\n }\n\n try {\n const result = await getUpdates(\n config.botToken,\n syncBuffer,\n currentState.abortController.signal,\n );\n syncBuffer = result.syncBuffer;\n reconnectDelay = BASE_RECONNECT_DELAY_MS;\n currentState.lastActivity = Date.now();\n\n if (result.msgs.length > 0) {\n logger.info(\"WeChat poll received messages\", {\n installationId,\n count: result.msgs.length,\n });\n }\n\n for (const rawMsg of result.msgs) {\n // Only user messages, skip bot echoes and non-text\n if (rawMsg.message_type !== MSG_TYPE_USER) continue;\n\n const text = extractText(rawMsg);\n if (!text) continue;\n\n if (!addToDedup((rawMsg as { client_id?: string }).client_id ?? \"\")) continue;\n\n const inbound = await wechatChannelAdapter.receive(\n rawMsg,\n installation as ChannelInstallation<WechatChannelInstallationConfig>,\n );\n if (inbound && router) {\n const dispatchResult = await router.dispatch(inbound);\n if (!(dispatchResult as { success?: boolean }).success) {\n logger.warn(\"WeChat dispatch failed\", {\n installationId,\n error: (dispatchResult as { error?: { message?: string } }).error?.message,\n });\n }\n }\n }\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n logger.info(\"WeChat poll aborted by signal\", { installationId });\n return;\n }\n\n logger.error(\"WeChat poll error\", {\n installationId,\n error: err instanceof Error ? err.message : String(err),\n });\n await new Promise((resolve) => setTimeout(resolve, reconnectDelay));\n reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);\n }\n\n scheduleNextPoll();\n };\n\n scheduleNextPoll();\n logger.info(\"WeChat polling started\", { installationId });\n },\n\n async disconnect(installationId: string): Promise<void> {\n const state = activeConnections.get(installationId);\n if (!state) {\n logger.warn(\"WeChat polling not running, nothing to disconnect\", { installationId });\n return;\n }\n\n logger.info(\"WeChat polling disconnecting\", { installationId });\n state.abortController.abort();\n clearInterval(state.heartbeatTimer);\n activeConnections.delete(installationId);\n logger.info(\"WeChat polling disconnected\", { installationId });\n },\n};\n","interface ContextTokenEntry {\n token: string;\n installationId: string;\n senderId: string;\n updatedAt: number;\n}\n\nconst TOKEN_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst store = new Map<string, ContextTokenEntry>();\nconst timers = new Map<string, NodeJS.Timeout>();\n\nfunction makeKey(installationId: string, senderId: string): string {\n return `${installationId}::${senderId}`;\n}\n\nexport function getContextToken(installationId: string, senderId: string): string | undefined {\n const key = makeKey(installationId, senderId);\n const entry = store.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.updatedAt > TOKEN_TTL_MS) {\n deleteContextToken(installationId, senderId);\n return undefined;\n }\n return entry.token;\n}\n\nexport function setContextToken(installationId: string, senderId: string, token: string): void {\n const key = makeKey(installationId, senderId);\n\n const existingTimer = timers.get(key);\n if (existingTimer) clearTimeout(existingTimer);\n\n store.set(key, { token, installationId, senderId, updatedAt: Date.now() });\n\n const timer = setTimeout(() => {\n store.delete(key);\n timers.delete(key);\n }, TOKEN_TTL_MS);\n timers.set(key, timer);\n}\n\nexport function deleteContextToken(installationId: string, senderId: string): void {\n const key = makeKey(installationId, senderId);\n store.delete(key);\n const timer = timers.get(key);\n if (timer) {\n clearTimeout(timer);\n timers.delete(key);\n }\n}\n\nexport function getContextTokenStoreSize(): number {\n return store.size;\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -311,31 +311,37 @@ var init_wechat_client = __esm({
311
311
  });
312
312
 
313
313
  // src/channels/wechat/context-store.ts
314
- function getContextToken(senderId) {
315
- const entry = store.get(senderId);
314
+ function makeKey(installationId, senderId) {
315
+ return `${installationId}::${senderId}`;
316
+ }
317
+ function getContextToken(installationId, senderId) {
318
+ const key = makeKey(installationId, senderId);
319
+ const entry = store.get(key);
316
320
  if (!entry) return void 0;
317
321
  if (Date.now() - entry.updatedAt > TOKEN_TTL_MS) {
318
- deleteContextToken(senderId);
322
+ deleteContextToken(installationId, senderId);
319
323
  return void 0;
320
324
  }
321
325
  return entry.token;
322
326
  }
323
- function setContextToken(senderId, token) {
324
- const existingTimer = timers.get(senderId);
327
+ function setContextToken(installationId, senderId, token) {
328
+ const key = makeKey(installationId, senderId);
329
+ const existingTimer = timers.get(key);
325
330
  if (existingTimer) clearTimeout(existingTimer);
326
- store.set(senderId, { token, senderId, updatedAt: Date.now() });
331
+ store.set(key, { token, installationId, senderId, updatedAt: Date.now() });
327
332
  const timer = setTimeout(() => {
328
- store.delete(senderId);
329
- timers.delete(senderId);
333
+ store.delete(key);
334
+ timers.delete(key);
330
335
  }, TOKEN_TTL_MS);
331
- timers.set(senderId, timer);
336
+ timers.set(key, timer);
332
337
  }
333
- function deleteContextToken(senderId) {
334
- store.delete(senderId);
335
- const timer = timers.get(senderId);
338
+ function deleteContextToken(installationId, senderId) {
339
+ const key = makeKey(installationId, senderId);
340
+ store.delete(key);
341
+ const timer = timers.get(key);
336
342
  if (timer) {
337
343
  clearTimeout(timer);
338
- timers.delete(senderId);
344
+ timers.delete(key);
339
345
  }
340
346
  }
341
347
  var TOKEN_TTL_MS, store, timers;
@@ -402,7 +408,7 @@ var init_WechatChannelAdapter = __esm({
402
408
  const text = extractText(msg);
403
409
  if (!text) return null;
404
410
  if (msg.context_token) {
405
- setContextToken(senderId, msg.context_token);
411
+ setContextToken(installation.id, senderId, msg.context_token);
406
412
  }
407
413
  return {
408
414
  channel: "wechat",
@@ -428,13 +434,13 @@ var init_WechatChannelAdapter = __esm({
428
434
  },
429
435
  async sendReply(replyTarget, message, installation) {
430
436
  const senderId = replyTarget.rawTarget.senderId;
431
- const contextToken = getContextToken(senderId);
437
+ const contextToken = getContextToken(installation.id, senderId);
432
438
  if (!contextToken) {
433
439
  logger4.warn("WeChat context token expired, cannot send reply", {
434
440
  installationId: installation.id,
435
441
  senderId
436
442
  });
437
- deleteContextToken(senderId);
443
+ deleteContextToken(installation.id, senderId);
438
444
  return;
439
445
  }
440
446
  const { botToken } = installation.config;
@@ -4565,9 +4571,13 @@ var WorkspaceController = class {
4565
4571
  const isBinary = this.isBinaryContentType(filename2);
4566
4572
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
4567
4573
  let buf;
4568
- if (volumeBackend && !isBinary) {
4569
- const fileData = await volumeBackend.readRaw(resolvedPath);
4570
- buf = Buffer.from(fileData.content.join("\n"), "utf-8");
4574
+ if (volumeBackend) {
4575
+ if (isBinary && volumeBackend.readBinary) {
4576
+ buf = await volumeBackend.readBinary(resolvedPath);
4577
+ } else {
4578
+ const fileData = await volumeBackend.readRaw(resolvedPath);
4579
+ buf = Buffer.from(fileData.content.join("\n"), "utf-8");
4580
+ }
4571
4581
  } else {
4572
4582
  const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
4573
4583
  buf = await sandbox.file.downloadFile({ file: resolvedPath });
@@ -4615,9 +4625,13 @@ var WorkspaceController = class {
4615
4625
  const isBinary = this.isBinaryContentType(filename2);
4616
4626
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
4617
4627
  let buf;
4618
- if (volumeBackend && !isBinary) {
4619
- const fileData = await volumeBackend.readRaw(resolvedPath);
4620
- buf = Buffer.from(fileData.content.join("\n"), "utf-8");
4628
+ if (volumeBackend) {
4629
+ if (isBinary && volumeBackend.readBinary) {
4630
+ buf = await volumeBackend.readBinary(resolvedPath);
4631
+ } else {
4632
+ const fileData = await volumeBackend.readRaw(resolvedPath);
4633
+ buf = Buffer.from(fileData.content.join("\n"), "utf-8");
4634
+ }
4621
4635
  } else {
4622
4636
  const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
4623
4637
  buf = await sandbox.file.downloadFile({ file: resolvedPath });