@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/gateway",
3
- "version": "2.1.99",
3
+ "version": "2.1.101",
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.81",
44
- "@axiom-lattice/core": "2.1.87",
45
- "@axiom-lattice/pg-stores": "1.0.78",
43
+ "@axiom-lattice/agent-eval": "2.1.83",
44
+ "@axiom-lattice/core": "2.1.89",
45
+ "@axiom-lattice/pg-stores": "1.0.80",
46
46
  "@axiom-lattice/protocols": "2.1.44",
47
47
  "@axiom-lattice/queue-redis": "1.0.43"
48
48
  },
@@ -95,7 +95,7 @@ export const wechatChannelAdapter: ChannelAdapter<WechatChannelInstallationConfi
95
95
 
96
96
  // Cache context token for reply
97
97
  if (msg.context_token) {
98
- setContextToken(senderId, msg.context_token);
98
+ setContextToken(installation.id, senderId, msg.context_token);
99
99
  }
100
100
 
101
101
  return {
@@ -127,14 +127,14 @@ export const wechatChannelAdapter: ChannelAdapter<WechatChannelInstallationConfi
127
127
  installation: ChannelInstallation<WechatChannelInstallationConfig>,
128
128
  ): Promise<void> {
129
129
  const senderId = replyTarget.rawTarget.senderId as string;
130
- const contextToken = getContextToken(senderId);
130
+ const contextToken = getContextToken(installation.id, senderId);
131
131
 
132
132
  if (!contextToken) {
133
133
  logger.warn("WeChat context token expired, cannot send reply", {
134
134
  installationId: installation.id,
135
135
  senderId,
136
136
  });
137
- deleteContextToken(senderId);
137
+ deleteContextToken(installation.id, senderId);
138
138
  return;
139
139
  }
140
140
 
@@ -0,0 +1,148 @@
1
+ import {
2
+ setContextToken,
3
+ getContextToken,
4
+ deleteContextToken,
5
+ getContextTokenStoreSize,
6
+ } from "../context-store";
7
+
8
+ describe("context-store", () => {
9
+ beforeEach(() => {
10
+ jest.useFakeTimers();
11
+ // Clear all tokens by deleting known keys (no public clear API)
12
+ });
13
+
14
+ afterEach(() => {
15
+ jest.useRealTimers();
16
+ });
17
+
18
+ describe("setContextToken / getContextToken", () => {
19
+ it("stores and retrieves a token keyed by installationId + senderId", () => {
20
+ setContextToken("inst-1", "user-a@im.wechat", "token-abc");
21
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBe("token-abc");
22
+ });
23
+
24
+ it("returns undefined for unknown installationId + senderId", () => {
25
+ expect(getContextToken("inst-1", "unknown@im.wechat")).toBeUndefined();
26
+ });
27
+
28
+ it("returns undefined when senderId matches but installationId differs", () => {
29
+ setContextToken("inst-1", "user-a@im.wechat", "token-abc");
30
+ expect(getContextToken("inst-2", "user-a@im.wechat")).toBeUndefined();
31
+ });
32
+ });
33
+
34
+ describe("isolation between installations", () => {
35
+ it("does not collide when different installations use the same senderId", () => {
36
+ setContextToken("inst-1", "user-x@im.wechat", "tok-111");
37
+ setContextToken("inst-2", "user-x@im.wechat", "tok-222");
38
+
39
+ expect(getContextToken("inst-1", "user-x@im.wechat")).toBe("tok-111");
40
+ expect(getContextToken("inst-2", "user-x@im.wechat")).toBe("tok-222");
41
+ });
42
+
43
+ it("isolates tokens between multiple installations with multiple users", () => {
44
+ setContextToken("inst-1", "alice@im.wechat", "a1");
45
+ setContextToken("inst-1", "bob@im.wechat", "b1");
46
+ setContextToken("inst-2", "alice@im.wechat", "a2");
47
+ setContextToken("inst-2", "charlie@im.wechat", "c2");
48
+
49
+ expect(getContextToken("inst-1", "alice@im.wechat")).toBe("a1");
50
+ expect(getContextToken("inst-1", "bob@im.wechat")).toBe("b1");
51
+ expect(getContextToken("inst-2", "alice@im.wechat")).toBe("a2");
52
+ expect(getContextToken("inst-2", "charlie@im.wechat")).toBe("c2");
53
+ });
54
+ });
55
+
56
+ describe("overwrite", () => {
57
+ it("overwrites existing token for the same installationId + senderId", () => {
58
+ setContextToken("inst-1", "user-a@im.wechat", "old-token");
59
+ setContextToken("inst-1", "user-a@im.wechat", "new-token");
60
+
61
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBe("new-token");
62
+ });
63
+
64
+ it("does not affect other senderIds in the same installation", () => {
65
+ setContextToken("inst-1", "user-a@im.wechat", "tok-a");
66
+ setContextToken("inst-1", "user-b@im.wechat", "tok-b");
67
+ setContextToken("inst-1", "user-a@im.wechat", "tok-a-v2");
68
+
69
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBe("tok-a-v2");
70
+ expect(getContextToken("inst-1", "user-b@im.wechat")).toBe("tok-b");
71
+ });
72
+ });
73
+
74
+ describe("deleteContextToken", () => {
75
+ it("removes a specific token", () => {
76
+ setContextToken("inst-1", "user-a@im.wechat", "tok-a");
77
+ deleteContextToken("inst-1", "user-a@im.wechat");
78
+
79
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBeUndefined();
80
+ });
81
+
82
+ it("only removes the targeted key, leaving others intact", () => {
83
+ setContextToken("inst-1", "alice@im.wechat", "tok-1");
84
+ setContextToken("inst-1", "bob@im.wechat", "tok-2");
85
+ setContextToken("inst-2", "alice@im.wechat", "tok-3");
86
+
87
+ deleteContextToken("inst-1", "alice@im.wechat");
88
+
89
+ expect(getContextToken("inst-1", "alice@im.wechat")).toBeUndefined();
90
+ expect(getContextToken("inst-1", "bob@im.wechat")).toBe("tok-2");
91
+ expect(getContextToken("inst-2", "alice@im.wechat")).toBe("tok-3");
92
+ });
93
+
94
+ it("is a no-op for unknown keys", () => {
95
+ expect(() => deleteContextToken("inst-1", "no-such@im.wechat")).not.toThrow();
96
+ });
97
+ });
98
+
99
+ describe("TTL expiry", () => {
100
+ it("returns undefined after TTL has passed", () => {
101
+ setContextToken("inst-1", "user-a@im.wechat", "tok-a");
102
+
103
+ // Advance time past 24h TTL
104
+ jest.advanceTimersByTime(25 * 60 * 60 * 1000);
105
+
106
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBeUndefined();
107
+ });
108
+
109
+ it("returns token within TTL window", () => {
110
+ setContextToken("inst-1", "user-a@im.wechat", "tok-a");
111
+
112
+ // Advance time to just under 24h
113
+ jest.advanceTimersByTime(23 * 60 * 60 * 1000);
114
+
115
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBe("tok-a");
116
+ });
117
+
118
+ it("resets TTL when overwritten", () => {
119
+ setContextToken("inst-1", "user-a@im.wechat", "tok-v1");
120
+ jest.advanceTimersByTime(23 * 60 * 60 * 1000);
121
+
122
+ // Overwrite resets the TTL
123
+ setContextToken("inst-1", "user-a@im.wechat", "tok-v2");
124
+ jest.advanceTimersByTime(23 * 60 * 60 * 1000);
125
+
126
+ // Original TTL + 23h would have expired, but overwrite reset it
127
+ expect(getContextToken("inst-1", "user-a@im.wechat")).toBe("tok-v2");
128
+ });
129
+ });
130
+
131
+ describe("getContextTokenStoreSize", () => {
132
+ it("returns the number of stored entries", () => {
133
+ const startSize = getContextTokenStoreSize();
134
+
135
+ setContextToken("sz-1", "a@im.wechat", "x");
136
+ expect(getContextTokenStoreSize()).toBe(startSize + 1);
137
+
138
+ setContextToken("sz-1", "b@im.wechat", "y");
139
+ expect(getContextTokenStoreSize()).toBe(startSize + 2);
140
+
141
+ setContextToken("sz-2", "a@im.wechat", "z");
142
+ expect(getContextTokenStoreSize()).toBe(startSize + 3);
143
+
144
+ deleteContextToken("sz-1", "a@im.wechat");
145
+ expect(getContextTokenStoreSize()).toBe(startSize + 2);
146
+ });
147
+ });
148
+ });
@@ -1,5 +1,6 @@
1
1
  interface ContextTokenEntry {
2
2
  token: string;
3
+ installationId: string;
3
4
  senderId: string;
4
5
  updatedAt: number;
5
6
  }
@@ -8,37 +9,43 @@ const TOKEN_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
8
9
  const store = new Map<string, ContextTokenEntry>();
9
10
  const timers = new Map<string, NodeJS.Timeout>();
10
11
 
11
- export function getContextToken(senderId: string): string | undefined {
12
- const entry = store.get(senderId);
12
+ function makeKey(installationId: string, senderId: string): string {
13
+ return `${installationId}::${senderId}`;
14
+ }
15
+
16
+ export function getContextToken(installationId: string, senderId: string): string | undefined {
17
+ const key = makeKey(installationId, senderId);
18
+ const entry = store.get(key);
13
19
  if (!entry) return undefined;
14
20
  if (Date.now() - entry.updatedAt > TOKEN_TTL_MS) {
15
- deleteContextToken(senderId);
21
+ deleteContextToken(installationId, senderId);
16
22
  return undefined;
17
23
  }
18
24
  return entry.token;
19
25
  }
20
26
 
21
- export function setContextToken(senderId: string, token: string): void {
22
- // Clear existing timer
23
- const existingTimer = timers.get(senderId);
27
+ export function setContextToken(installationId: string, senderId: string, token: string): void {
28
+ const key = makeKey(installationId, senderId);
29
+
30
+ const existingTimer = timers.get(key);
24
31
  if (existingTimer) clearTimeout(existingTimer);
25
32
 
26
- store.set(senderId, { token, senderId, updatedAt: Date.now() });
33
+ store.set(key, { token, installationId, senderId, updatedAt: Date.now() });
27
34
 
28
- // Auto-cleanup after TTL
29
35
  const timer = setTimeout(() => {
30
- store.delete(senderId);
31
- timers.delete(senderId);
36
+ store.delete(key);
37
+ timers.delete(key);
32
38
  }, TOKEN_TTL_MS);
33
- timers.set(senderId, timer);
39
+ timers.set(key, timer);
34
40
  }
35
41
 
36
- export function deleteContextToken(senderId: string): void {
37
- store.delete(senderId);
38
- const timer = timers.get(senderId);
42
+ export function deleteContextToken(installationId: string, senderId: string): void {
43
+ const key = makeKey(installationId, senderId);
44
+ store.delete(key);
45
+ const timer = timers.get(key);
39
46
  if (timer) {
40
47
  clearTimeout(timer);
41
- timers.delete(senderId);
48
+ timers.delete(key);
42
49
  }
43
50
  }
44
51
 
@@ -373,13 +373,17 @@ export class WorkspaceController {
373
373
  const filename = this.getFilenameFromPath(resolvedPath);
374
374
  const isBinary = this.isBinaryContentType(filename);
375
375
 
376
- // Try volume backend for text files; binary files must use sandbox
376
+ // Use volume backend when available; fall back to sandbox
377
377
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
378
378
 
379
379
  let buf: Buffer;
380
- if (volumeBackend && !isBinary) {
381
- const fileData = await volumeBackend.readRaw(resolvedPath);
382
- buf = Buffer.from(fileData.content.join("\n"), "utf-8");
380
+ if (volumeBackend) {
381
+ if (isBinary && volumeBackend.readBinary) {
382
+ buf = await volumeBackend.readBinary(resolvedPath);
383
+ } else {
384
+ const fileData = await volumeBackend.readRaw(resolvedPath);
385
+ buf = Buffer.from(fileData.content.join("\n"), "utf-8");
386
+ }
383
387
  } else {
384
388
  const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
385
389
  buf = await sandbox.file.downloadFile({ file: resolvedPath });
@@ -445,14 +449,17 @@ export class WorkspaceController {
445
449
  const filename = this.getFilenameFromPath(resolvedPath);
446
450
  const isBinary = this.isBinaryContentType(filename);
447
451
 
448
- // Try volume backend for text files; binary files must use sandbox
449
- // (volume backend's readRaw API is text-based and corrupts binary data)
452
+ // Use volume backend when available; fall back to sandbox
450
453
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
451
454
 
452
455
  let buf: Buffer;
453
- if (volumeBackend && !isBinary) {
454
- const fileData = await volumeBackend.readRaw(resolvedPath);
455
- buf = Buffer.from(fileData.content.join("\n"), "utf-8");
456
+ if (volumeBackend) {
457
+ if (isBinary && volumeBackend.readBinary) {
458
+ buf = await volumeBackend.readBinary(resolvedPath);
459
+ } else {
460
+ const fileData = await volumeBackend.readRaw(resolvedPath);
461
+ buf = Buffer.from(fileData.content.join("\n"), "utf-8");
462
+ }
456
463
  } else {
457
464
  const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
458
465
  buf = await sandbox.file.downloadFile({ file: resolvedPath });
@@ -1 +0,0 @@
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(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(senderId);\n\n if (!contextToken) {\n logger.warn(\"WeChat context token expired, cannot send reply\", {\n installationId: installation.id,\n senderId,\n });\n deleteContextToken(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 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\nexport function getContextToken(senderId: string): string | undefined {\n const entry = store.get(senderId);\n if (!entry) return undefined;\n if (Date.now() - entry.updatedAt > TOKEN_TTL_MS) {\n deleteContextToken(senderId);\n return undefined;\n }\n return entry.token;\n}\n\nexport function setContextToken(senderId: string, token: string): void {\n // Clear existing timer\n const existingTimer = timers.get(senderId);\n if (existingTimer) clearTimeout(existingTimer);\n\n store.set(senderId, { token, senderId, updatedAt: Date.now() });\n\n // Auto-cleanup after TTL\n const timer = setTimeout(() => {\n store.delete(senderId);\n timers.delete(senderId);\n }, TOKEN_TTL_MS);\n timers.set(senderId, timer);\n}\n\nexport function deleteContextToken(senderId: string): void {\n store.delete(senderId);\n const timer = timers.get(senderId);\n if (timer) {\n clearTimeout(timer);\n timers.delete(senderId);\n }\n}\n\nexport function getContextTokenStoreSize(): number {\n return store.size;\n}\n"],"mappings":"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","names":[]}