agent-comms 2.21.7 → 2.21.9

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.
@@ -15,7 +15,7 @@
15
15
  "url": "https://github.com/ExaDev/agent-comms.git"
16
16
  },
17
17
  "description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
18
- "version": "2.21.7"
18
+ "version": "2.21.9"
19
19
  }
20
20
  ]
21
21
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-comms",
3
3
  "description": "Cross-harness LLM agent communication mesh — rooms, DMs, and presence",
4
- "version": "2.21.7",
4
+ "version": "2.21.9",
5
5
  "author": {
6
6
  "name": "ExaDev"
7
7
  },
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github&logoColor=white)](https://github.com/ExaDev/agent-comms)
4
4
  [![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/agent-comms)
5
- [![version](https://img.shields.io/badge/version-2.21.7-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.21.7)
5
+ [![version](https://img.shields.io/badge/version-2.21.9-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v2.21.9)
6
6
  [![CI](https://img.shields.io/github/actions/workflow/status/ExaDev/agent-comms/ci.yml?branch=main)](https://github.com/ExaDev/agent-comms/actions)
7
7
 
8
8
  Cross-harness communication mesh for LLM agents: rooms, DMs, presence, and visibility over TCP with zero filesystem dependencies.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mesh-store-shared.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-store-shared.test.d.ts","sourceRoot":"","sources":["../../src/test/mesh-store-shared.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mergeMessageHistories, ROOM_TOKEN_LIFETIME_MS, } from "../core/mesh-store-shared.js";
3
+ const DAYS = 30;
4
+ const HOURS_PER_DAY = 24;
5
+ const MINUTES_PER_HOUR = 60;
6
+ const SECONDS_PER_MINUTE = 60;
7
+ const MS_PER_SECOND = 1000;
8
+ describe("ROOM_TOKEN_LIFETIME_MS", () => {
9
+ it("is exactly 30 days, in milliseconds", () => {
10
+ expect(ROOM_TOKEN_LIFETIME_MS).toBe(DAYS *
11
+ HOURS_PER_DAY *
12
+ MINUTES_PER_HOUR *
13
+ SECONDS_PER_MINUTE *
14
+ MS_PER_SECOND);
15
+ });
16
+ });
17
+ function msg(id, readBy = []) {
18
+ return { id, readBy: [...readBy] };
19
+ }
20
+ describe("mergeMessageHistories", () => {
21
+ it("appends an incoming entry the local list does not have", () => {
22
+ const merged = mergeMessageHistories([msg("a")], [msg("b")]);
23
+ expect(merged.map((m) => m.id)).toEqual(["a", "b"]);
24
+ });
25
+ it("appends multiple new entries in incoming order", () => {
26
+ const merged = mergeMessageHistories([msg("a")], [msg("b"), msg("c")]);
27
+ expect(merged.map((m) => m.id)).toEqual(["a", "b", "c"]);
28
+ });
29
+ it("preserves local ordering -- new entries are appended, not inserted", () => {
30
+ const merged = mergeMessageHistories([msg("a"), msg("z")], [msg("m")]);
31
+ expect(merged.map((m) => m.id)).toEqual(["a", "z", "m"]);
32
+ });
33
+ it("unions a new reader onto an existing entry's readBy", () => {
34
+ const merged = mergeMessageHistories([msg("a", ["u1"])], [msg("a", ["u2"])]);
35
+ expect(merged[0]?.readBy.sort()).toEqual(["u1", "u2"]);
36
+ });
37
+ it("does not duplicate a reader already present locally", () => {
38
+ const merged = mergeMessageHistories([msg("a", ["u1"])], [msg("a", ["u1"])]);
39
+ expect(merged[0]?.readBy).toEqual(["u1"]);
40
+ });
41
+ it("unions several readers from one incoming entry", () => {
42
+ const merged = mergeMessageHistories([msg("a", ["u1"])], [msg("a", ["u1", "u2", "u3"])]);
43
+ expect(merged[0]?.readBy.sort()).toEqual(["u1", "u2", "u3"]);
44
+ });
45
+ it("leaves an existing entry's readBy untouched when incoming has none", () => {
46
+ const merged = mergeMessageHistories([msg("a", ["u1"])], [msg("a", [])]);
47
+ expect(merged[0]?.readBy).toEqual(["u1"]);
48
+ });
49
+ it("does not grow the merged length for an already-known entry", () => {
50
+ const merged = mergeMessageHistories([msg("a")], [msg("a")]);
51
+ expect(merged).toHaveLength(1);
52
+ });
53
+ it("handles an empty incoming list as a no-op, returning an equivalent copy", () => {
54
+ const merged = mergeMessageHistories([msg("a", ["u1"])], []);
55
+ expect(merged).toEqual([msg("a", ["u1"])]);
56
+ });
57
+ it("handles an empty local list, returning everything incoming", () => {
58
+ const merged = mergeMessageHistories([], [msg("a"), msg("b")]);
59
+ expect(merged.map((m) => m.id)).toEqual(["a", "b"]);
60
+ });
61
+ it("merges a mix of new and existing entries in one call", () => {
62
+ const merged = mergeMessageHistories([msg("a", ["u1"]), msg("b", [])], [msg("a", ["u2"]), msg("c", ["u3"])]);
63
+ expect(merged.map((m) => m.id)).toEqual(["a", "b", "c"]);
64
+ expect(merged[0]?.readBy.sort()).toEqual(["u1", "u2"]);
65
+ expect(merged[1]?.readBy).toEqual([]);
66
+ expect(merged[2]?.readBy).toEqual(["u3"]);
67
+ });
68
+ it("does not mutate either input array", () => {
69
+ const local = [msg("a", ["u1"])];
70
+ const incoming = [msg("a", ["u2"]), msg("b")];
71
+ mergeMessageHistories(local, incoming);
72
+ expect(local).toEqual([msg("a", ["u1"])]);
73
+ expect(incoming).toEqual([msg("a", ["u2"]), msg("b")]);
74
+ });
75
+ it("does not mutate an existing local entry's own readBy array in place", () => {
76
+ const originalEntry = msg("a", ["u1"]);
77
+ const local = [originalEntry];
78
+ mergeMessageHistories(local, [msg("a", ["u2"])]);
79
+ // The merged result's entry for "a" must be a distinct object/array from the one still referenced by the caller's own original local list -- otherwise a caller holding onto its own pre-merge array would see readers silently appear in it too.
80
+ expect(originalEntry.readBy).toEqual(["u1"]);
81
+ });
82
+ it("unions two same-id entries within one incoming batch into a single merged entry, not a duplicate", () => {
83
+ // A brand-new id appearing twice in the same incoming batch is the one case where the freshly-added entry must be looked up again within the same call, not just against local's own pre-existing entries -- proves the id-to-entry index is kept in sync as new entries are added mid-merge, not only seeded from local up front.
84
+ const merged = mergeMessageHistories([], [msg("a", ["u1"]), msg("a", ["u2"])]);
85
+ expect(merged.map((m) => m.id)).toEqual(["a"]);
86
+ expect(merged[0]?.readBy.sort()).toEqual(["u1", "u2"]);
87
+ });
88
+ });
89
+ //# sourceMappingURL=mesh-store-shared.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-store-shared.test.js","sourceRoot":"","sources":["../../src/test/mesh-store-shared.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stale-agent-checker.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-agent-checker.test.d.ts","sourceRoot":"","sources":["../../src/test/stale-agent-checker.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Two mutants Stryker raises against stale-agent-checker.ts are true equivalents, not gaps -- documented here rather than chased with a contrived test, per this repo's own "provably equivalent gets reasoning, not a forced test" convention:
3
+ *
4
+ * - `agents.set(id, agent)` right after `agent.status = "offline"` in the dead-process branch: `agent` is the exact same object reference `agents.get(id)` already returned, so mutating `.status` on it already mutates what the Map holds. Re-`.set()`-ing the same key to the same reference is a genuine no-op; no test can observe removing that call.
5
+ * - The purge loop's `purgeIds: string[] = []` seeded with an extra bogus id: `Map.delete()`/nothing on a key that was never present is itself a silent no-op by JS Map semantics, so a spurious extra entry in the purge list has zero observable effect on any of the four maps it's applied to, regardless of what else the test asserts.
6
+ */
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
8
+ import { StaleAgentChecker, } from "../core/stale-agent-checker.js";
9
+ const PROBE_INTERVAL_MS = 5000;
10
+ const OFFLINE_PURGE_THRESHOLD_MINUTES = 30;
11
+ const SECONDS_PER_MINUTE = 60;
12
+ const MS_PER_SECOND = 1000;
13
+ const OFFLINE_PURGE_THRESHOLD_MS = OFFLINE_PURGE_THRESHOLD_MINUTES * SECONDS_PER_MINUTE * MS_PER_SECOND;
14
+ /** How many probe intervals stop() must survive with zero further probing -- arbitrary beyond "more than one", chosen to make a lingering timer's own recurrence visible rather than a one-off fluke. */
15
+ const HALT_CHECK_INTERVAL_COUNT = 3;
16
+ /** How many purge thresholds old an agent's startedAt is in the "no matter how old" test -- arbitrary beyond "well past the threshold", chosen to make the point emphatically rather than sit right at the boundary (which the dedicated boundary test already covers precisely). */
17
+ const FAR_PAST_THRESHOLD_MULTIPLIER = 10;
18
+ const ALIVE_PID = 111;
19
+ const DEAD_PID = 222;
20
+ const FIXED_NOW_MS = 1_700_000_000_000;
21
+ // vi.advanceTimersByTimeAsync advances the mocked Date along with fake timers, so by the time a probe actually runs, Date.now() is FIXED_NOW_MS + however far the clock was advanced to fire it -- ages are computed relative to that real evaluation instant, not the clock's initial value, so a boundary test lands exactly where it claims to.
22
+ const PROBE_TIME_MS = FIXED_NOW_MS + PROBE_INTERVAL_MS;
23
+ function agent(id, status, pid, startedAt) {
24
+ return {
25
+ id,
26
+ version: 1,
27
+ name: id,
28
+ harness: "pi",
29
+ cwd: "/tmp",
30
+ pid,
31
+ startedAt,
32
+ visibility: "visible",
33
+ status,
34
+ tags: [],
35
+ subscribedRooms: [],
36
+ };
37
+ }
38
+ function isoAgeMs(ageMs) {
39
+ return new Date(PROBE_TIME_MS - ageMs).toISOString();
40
+ }
41
+ function harness(agents) {
42
+ const agentsMap = new Map(agents.map((a) => [a.id, a]));
43
+ const deliveryQueues = new Map();
44
+ const identityCache = new Map();
45
+ const peerInfo = new Map();
46
+ for (const a of agents) {
47
+ deliveryQueues.set(a.id, []);
48
+ identityCache.set(a.id, { id: a.id });
49
+ peerInfo.set(a.id, { id: a.id, port: 1, startedAt: a.startedAt });
50
+ }
51
+ const notifyRoomsOfStatus = vi.fn(async () => Promise.resolve());
52
+ const broadcastPatch = vi.fn(async () => Promise.resolve());
53
+ const deps = {
54
+ agents: agentsMap,
55
+ deliveryQueues: deliveryQueues,
56
+ identityCache,
57
+ peerInfo,
58
+ notifyRoomsOfStatus,
59
+ broadcastPatch: broadcastPatch,
60
+ };
61
+ return {
62
+ deps,
63
+ checker: new StaleAgentChecker(deps),
64
+ notifyRoomsOfStatus,
65
+ broadcastPatch,
66
+ };
67
+ }
68
+ describe("StaleAgentChecker", () => {
69
+ let killSpy;
70
+ beforeEach(() => {
71
+ vi.useFakeTimers();
72
+ vi.setSystemTime(FIXED_NOW_MS);
73
+ killSpy = vi
74
+ .spyOn(process, "kill")
75
+ .mockImplementation((pid) => {
76
+ if (pid === ALIVE_PID)
77
+ return true;
78
+ throw new Error("ESRCH");
79
+ });
80
+ });
81
+ afterEach(() => {
82
+ killSpy.mockRestore();
83
+ vi.useRealTimers();
84
+ });
85
+ describe("start/stop", () => {
86
+ it("does nothing until start() is called", async () => {
87
+ const { checker, broadcastPatch } = harness([
88
+ agent("a1", "active", DEAD_PID, isoAgeMs(0)),
89
+ ]);
90
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS * 2);
91
+ expect(broadcastPatch).not.toHaveBeenCalled();
92
+ checker.stop();
93
+ });
94
+ it("probes on the configured interval once started", async () => {
95
+ const { checker, broadcastPatch } = harness([
96
+ agent("a1", "active", DEAD_PID, isoAgeMs(0)),
97
+ ]);
98
+ checker.start();
99
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
100
+ expect(broadcastPatch).toHaveBeenCalledTimes(1);
101
+ checker.stop();
102
+ });
103
+ it("calling start() twice does not create a second timer", () => {
104
+ // broadcastPatch call counts can't distinguish this: probeStaleAgents mutates an agent's status to "offline" synchronously, before its own first await, so a second un-guarded timer firing in the same tick finds nothing left to mark dead even when a real (leaked) second interval exists underneath -- asserting directly on setInterval's own call count is what the guard in start() actually exists to control.
105
+ const setIntervalSpy = vi.spyOn(globalThis, "setInterval");
106
+ const { checker } = harness([]);
107
+ checker.start();
108
+ checker.start();
109
+ expect(setIntervalSpy).toHaveBeenCalledTimes(1);
110
+ checker.stop();
111
+ });
112
+ it("stop() clears the timer start() created", () => {
113
+ const clearIntervalSpy = vi.spyOn(globalThis, "clearInterval");
114
+ const { checker } = harness([]);
115
+ checker.start();
116
+ checker.stop();
117
+ expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
118
+ });
119
+ it("stop() halts further probing", async () => {
120
+ const { checker, broadcastPatch } = harness([
121
+ agent("a1", "active", DEAD_PID, isoAgeMs(0)),
122
+ ]);
123
+ checker.start();
124
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
125
+ checker.stop();
126
+ broadcastPatch.mockClear();
127
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS * HALT_CHECK_INTERVAL_COUNT);
128
+ expect(broadcastPatch).not.toHaveBeenCalled();
129
+ });
130
+ it("stop() before start() is a safe no-op that never calls clearInterval", () => {
131
+ const clearIntervalSpy = vi.spyOn(globalThis, "clearInterval");
132
+ const { checker } = harness([]);
133
+ expect(() => {
134
+ checker.stop();
135
+ }).not.toThrow();
136
+ expect(clearIntervalSpy).not.toHaveBeenCalled();
137
+ });
138
+ });
139
+ describe("dead-process detection", () => {
140
+ it("marks an active agent with a dead pid offline, notifies, broadcasts, and drops its peer info", async () => {
141
+ const { deps, checker, notifyRoomsOfStatus, broadcastPatch } = harness([
142
+ agent("a1", "active", DEAD_PID, isoAgeMs(0)),
143
+ ]);
144
+ deps.peerInfo.set("a1", { id: "a1", port: 42, startedAt: isoAgeMs(0) });
145
+ checker.start();
146
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
147
+ checker.stop();
148
+ expect(deps.agents.get("a1")?.status).toBe("offline");
149
+ expect(notifyRoomsOfStatus).toHaveBeenCalledWith("a1", "offline");
150
+ expect(broadcastPatch).toHaveBeenCalledWith({
151
+ type: "agent_offline",
152
+ agentId: "a1",
153
+ });
154
+ expect(deps.peerInfo.has("a1")).toBe(false);
155
+ });
156
+ it("leaves an active agent with a live pid untouched", async () => {
157
+ const { deps, checker, notifyRoomsOfStatus, broadcastPatch } = harness([
158
+ agent("a1", "active", ALIVE_PID, isoAgeMs(0)),
159
+ ]);
160
+ checker.start();
161
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
162
+ checker.stop();
163
+ expect(deps.agents.get("a1")?.status).toBe("active");
164
+ expect(notifyRoomsOfStatus).not.toHaveBeenCalled();
165
+ expect(broadcastPatch).not.toHaveBeenCalled();
166
+ expect(deps.peerInfo.has("a1")).toBe(true);
167
+ });
168
+ it("does not re-probe an agent that is already offline, even with a dead pid", async () => {
169
+ const { deps, checker, notifyRoomsOfStatus } = harness([
170
+ agent("a1", "offline", DEAD_PID, isoAgeMs(0)),
171
+ ]);
172
+ checker.start();
173
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
174
+ checker.stop();
175
+ // Only the "status !== active" guard is exercised here -- the purge threshold (30 min) is far beyond isoAgeMs(0), so this agent is untouched by both code paths.
176
+ expect(deps.agents.get("a1")?.status).toBe("offline");
177
+ expect(notifyRoomsOfStatus).not.toHaveBeenCalled();
178
+ });
179
+ it("marks every dead active agent offline in a single probe pass", async () => {
180
+ const { deps, checker, broadcastPatch } = harness([
181
+ agent("a1", "active", DEAD_PID, isoAgeMs(0)),
182
+ agent("a2", "active", DEAD_PID, isoAgeMs(0)),
183
+ agent("a3", "active", ALIVE_PID, isoAgeMs(0)),
184
+ ]);
185
+ checker.start();
186
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
187
+ checker.stop();
188
+ expect(deps.agents.get("a1")?.status).toBe("offline");
189
+ expect(deps.agents.get("a2")?.status).toBe("offline");
190
+ expect(deps.agents.get("a3")?.status).toBe("active");
191
+ expect(broadcastPatch).toHaveBeenCalledTimes(2);
192
+ });
193
+ });
194
+ describe("offline-agent purging", () => {
195
+ it("purges an agent offline for longer than the threshold, from every map, and nothing else", async () => {
196
+ const { deps, checker } = harness([
197
+ agent("a1", "offline", DEAD_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS + 1)),
198
+ ]);
199
+ checker.start();
200
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
201
+ checker.stop();
202
+ expect(deps.agents.has("a1")).toBe(false);
203
+ expect(deps.peerInfo.has("a1")).toBe(false);
204
+ expect(deps.identityCache.has("a1")).toBe(false);
205
+ expect(deps.deliveryQueues.has("a1")).toBe(false);
206
+ expect(deps.agents.size).toBe(0);
207
+ expect(deps.peerInfo.size).toBe(0);
208
+ expect(deps.identityCache.size).toBe(0);
209
+ expect(deps.deliveryQueues.size).toBe(0);
210
+ });
211
+ it("does not purge an offline agent still within the threshold", async () => {
212
+ const { deps, checker } = harness([
213
+ agent("a1", "offline", DEAD_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS - 1)),
214
+ ]);
215
+ checker.start();
216
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
217
+ checker.stop();
218
+ expect(deps.agents.has("a1")).toBe(true);
219
+ });
220
+ it("does not purge an offline agent exactly at the threshold (the comparison is strict-less-than, not less-or-equal)", async () => {
221
+ const { deps, checker } = harness([
222
+ agent("a1", "offline", DEAD_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS)),
223
+ ]);
224
+ checker.start();
225
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
226
+ checker.stop();
227
+ expect(deps.agents.has("a1")).toBe(true);
228
+ });
229
+ it("does not purge a non-offline agent no matter how old its startedAt is", async () => {
230
+ const { deps, checker } = harness([
231
+ agent("a1", "active", ALIVE_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS * FAR_PAST_THRESHOLD_MULTIPLIER)),
232
+ ]);
233
+ checker.start();
234
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
235
+ checker.stop();
236
+ expect(deps.agents.has("a1")).toBe(true);
237
+ });
238
+ it("purges every long-offline agent while leaving a recent one alone, in the same pass", async () => {
239
+ const { deps, checker } = harness([
240
+ agent("old", "offline", DEAD_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS + 1)),
241
+ agent("recent", "offline", DEAD_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS - 1)),
242
+ ]);
243
+ checker.start();
244
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
245
+ checker.stop();
246
+ expect(deps.agents.has("old")).toBe(false);
247
+ expect(deps.agents.has("recent")).toBe(true);
248
+ });
249
+ it("both marks a newly-dead agent offline and purges an already-long-offline one in the same probe", async () => {
250
+ const { deps, checker } = harness([
251
+ agent("newlyDead", "active", DEAD_PID, isoAgeMs(0)),
252
+ agent("longOffline", "offline", DEAD_PID, isoAgeMs(OFFLINE_PURGE_THRESHOLD_MS + 1)),
253
+ ]);
254
+ checker.start();
255
+ await vi.advanceTimersByTimeAsync(PROBE_INTERVAL_MS);
256
+ checker.stop();
257
+ expect(deps.agents.get("newlyDead")?.status).toBe("offline");
258
+ expect(deps.agents.has("longOffline")).toBe(false);
259
+ });
260
+ });
261
+ });
262
+ //# sourceMappingURL=stale-agent-checker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-agent-checker.test.js","sourceRoot":"","sources":["../../src/test/stale-agent-checker.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-comms",
3
- "version": "2.21.7",
3
+ "version": "2.21.9",
4
4
  "description": "Cross-harness communication mesh for LLM agents — rooms, DMs, presence, and real-time push delivery over TCP",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",