@moltzap/client 2026.503.3 → 2026.504.0

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.
Files changed (227) hide show
  1. package/dist/auth.d.ts +10 -1
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +8 -5
  4. package/dist/auth.js.map +1 -1
  5. package/dist/channel-core.d.ts +1 -4
  6. package/dist/channel-core.d.ts.map +1 -1
  7. package/dist/channel-core.js +0 -9
  8. package/dist/channel-core.js.map +1 -1
  9. package/dist/channel-core.test.js +90 -71
  10. package/dist/channel-core.test.js.map +1 -1
  11. package/dist/cli/commands/agents.d.ts.map +1 -1
  12. package/dist/cli/commands/agents.js +5 -3
  13. package/dist/cli/commands/agents.js.map +1 -1
  14. package/dist/cli/commands/apps.d.ts +9 -27
  15. package/dist/cli/commands/apps.d.ts.map +1 -1
  16. package/dist/cli/commands/apps.js +36 -57
  17. package/dist/cli/commands/apps.js.map +1 -1
  18. package/dist/cli/commands/apps.test.js +63 -75
  19. package/dist/cli/commands/apps.test.js.map +1 -1
  20. package/dist/cli/commands/contacts.d.ts +1 -5
  21. package/dist/cli/commands/contacts.d.ts.map +1 -1
  22. package/dist/cli/commands/contacts.js +14 -27
  23. package/dist/cli/commands/contacts.js.map +1 -1
  24. package/dist/cli/commands/conversations-v2.test.js +34 -29
  25. package/dist/cli/commands/conversations-v2.test.js.map +1 -1
  26. package/dist/cli/commands/conversations.d.ts +7 -3
  27. package/dist/cli/commands/conversations.d.ts.map +1 -1
  28. package/dist/cli/commands/conversations.js +63 -48
  29. package/dist/cli/commands/conversations.js.map +1 -1
  30. package/dist/cli/commands/messages.d.ts +7 -3
  31. package/dist/cli/commands/messages.d.ts.map +1 -1
  32. package/dist/cli/commands/messages.js +13 -15
  33. package/dist/cli/commands/messages.js.map +1 -1
  34. package/dist/cli/commands/messages.test.js +30 -31
  35. package/dist/cli/commands/messages.test.js.map +1 -1
  36. package/dist/cli/commands/ping.d.ts +9 -0
  37. package/dist/cli/commands/ping.d.ts.map +1 -1
  38. package/dist/cli/commands/ping.js +17 -4
  39. package/dist/cli/commands/ping.js.map +1 -1
  40. package/dist/cli/commands/presence.d.ts.map +1 -1
  41. package/dist/cli/commands/presence.js +6 -4
  42. package/dist/cli/commands/presence.js.map +1 -1
  43. package/dist/cli/commands/register.d.ts.map +1 -1
  44. package/dist/cli/commands/register.js +2 -1
  45. package/dist/cli/commands/register.js.map +1 -1
  46. package/dist/cli/commands/send.d.ts.map +1 -1
  47. package/dist/cli/commands/send.js +22 -11
  48. package/dist/cli/commands/send.js.map +1 -1
  49. package/dist/cli/commands/send.test.js +12 -9
  50. package/dist/cli/commands/send.test.js.map +1 -1
  51. package/dist/cli/commands/status.js +2 -2
  52. package/dist/cli/commands/status.js.map +1 -1
  53. package/dist/cli/commands/test-transport.d.ts +10 -0
  54. package/dist/cli/commands/test-transport.d.ts.map +1 -0
  55. package/dist/cli/commands/test-transport.js +29 -0
  56. package/dist/cli/commands/test-transport.js.map +1 -0
  57. package/dist/cli/commands/whoami.d.ts.map +1 -1
  58. package/dist/cli/commands/whoami.js +5 -1
  59. package/dist/cli/commands/whoami.js.map +1 -1
  60. package/dist/cli/config.d.ts +42 -6
  61. package/dist/cli/config.d.ts.map +1 -1
  62. package/dist/cli/config.js +91 -27
  63. package/dist/cli/config.js.map +1 -1
  64. package/dist/cli/http-client.d.ts +11 -1
  65. package/dist/cli/http-client.d.ts.map +1 -1
  66. package/dist/cli/http-client.js +13 -4
  67. package/dist/cli/http-client.js.map +1 -1
  68. package/dist/cli/index.d.ts.map +1 -1
  69. package/dist/cli/index.js +2 -3
  70. package/dist/cli/index.js.map +1 -1
  71. package/dist/cli/profile.d.ts +3 -4
  72. package/dist/cli/profile.d.ts.map +1 -1
  73. package/dist/cli/profile.js +42 -29
  74. package/dist/cli/profile.js.map +1 -1
  75. package/dist/cli/runtime.d.ts.map +1 -1
  76. package/dist/cli/runtime.js +9 -4
  77. package/dist/cli/runtime.js.map +1 -1
  78. package/dist/cli/socket-client.d.ts +29 -4
  79. package/dist/cli/socket-client.d.ts.map +1 -1
  80. package/dist/cli/socket-client.js +52 -21
  81. package/dist/cli/socket-client.js.map +1 -1
  82. package/dist/cli/transport.d.ts +6 -9
  83. package/dist/cli/transport.d.ts.map +1 -1
  84. package/dist/cli/transport.js +47 -37
  85. package/dist/cli/transport.js.map +1 -1
  86. package/dist/cli/transport.test.js +19 -16
  87. package/dist/cli/transport.test.js.map +1 -1
  88. package/dist/index.d.ts +2 -3
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +1 -1
  91. package/dist/index.js.map +1 -1
  92. package/dist/internal/__tests__/app-callback-partition-key.test.d.ts +2 -0
  93. package/dist/internal/__tests__/app-callback-partition-key.test.d.ts.map +1 -0
  94. package/dist/internal/__tests__/app-callback-partition-key.test.js +33 -0
  95. package/dist/internal/__tests__/app-callback-partition-key.test.js.map +1 -0
  96. package/dist/internal/__tests__/app-callback-partition-worker.test.d.ts +2 -0
  97. package/dist/internal/__tests__/app-callback-partition-worker.test.d.ts.map +1 -0
  98. package/dist/internal/__tests__/{s2c-partition-worker.test.js → app-callback-partition-worker.test.js} +6 -8
  99. package/dist/internal/__tests__/app-callback-partition-worker.test.js.map +1 -0
  100. package/dist/internal/__tests__/app-callback-partitioned-dispatcher-real-ws.test.d.ts +2 -0
  101. package/dist/internal/__tests__/app-callback-partitioned-dispatcher-real-ws.test.d.ts.map +1 -0
  102. package/dist/internal/__tests__/{s2c-partitioned-dispatcher-real-ws.test.js → app-callback-partitioned-dispatcher-real-ws.test.js} +69 -93
  103. package/dist/internal/__tests__/app-callback-partitioned-dispatcher-real-ws.test.js.map +1 -0
  104. package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.d.ts +2 -0
  105. package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.d.ts.map +1 -0
  106. package/dist/internal/__tests__/{s2c-partitioned-dispatcher.test.js → app-callback-partitioned-dispatcher.test.js} +36 -69
  107. package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.js.map +1 -0
  108. package/dist/internal/__tests__/app-callback-test-requests.d.ts +58 -0
  109. package/dist/internal/__tests__/app-callback-test-requests.d.ts.map +1 -0
  110. package/dist/internal/__tests__/app-callback-test-requests.js +124 -0
  111. package/dist/internal/__tests__/app-callback-test-requests.js.map +1 -0
  112. package/dist/internal/{s2c-dispatcher-errors.d.ts → app-callback-dispatcher-errors.d.ts} +5 -21
  113. package/dist/internal/app-callback-dispatcher-errors.d.ts.map +1 -0
  114. package/dist/internal/{s2c-dispatcher-errors.js → app-callback-dispatcher-errors.js} +3 -13
  115. package/dist/internal/app-callback-dispatcher-errors.js.map +1 -0
  116. package/dist/internal/app-callback-partition-key.d.ts +68 -0
  117. package/dist/internal/app-callback-partition-key.d.ts.map +1 -0
  118. package/dist/internal/app-callback-partition-key.js +90 -0
  119. package/dist/internal/app-callback-partition-key.js.map +1 -0
  120. package/dist/internal/{s2c-partition-worker.d.ts → app-callback-partition-worker.d.ts} +5 -5
  121. package/dist/internal/app-callback-partition-worker.d.ts.map +1 -0
  122. package/dist/internal/{s2c-partition-worker.js → app-callback-partition-worker.js} +5 -5
  123. package/dist/internal/app-callback-partition-worker.js.map +1 -0
  124. package/dist/internal/{s2c-partitioned-dispatcher.d.ts → app-callback-partitioned-dispatcher.d.ts} +9 -10
  125. package/dist/internal/app-callback-partitioned-dispatcher.d.ts.map +1 -0
  126. package/dist/internal/{s2c-partitioned-dispatcher.js → app-callback-partitioned-dispatcher.js} +13 -21
  127. package/dist/internal/app-callback-partitioned-dispatcher.js.map +1 -0
  128. package/dist/runtime/close-info.d.ts +15 -16
  129. package/dist/runtime/close-info.d.ts.map +1 -1
  130. package/dist/runtime/close-info.js +12 -8
  131. package/dist/runtime/close-info.js.map +1 -1
  132. package/dist/runtime/errors.d.ts +2 -28
  133. package/dist/runtime/errors.d.ts.map +1 -1
  134. package/dist/runtime/errors.js +0 -9
  135. package/dist/runtime/errors.js.map +1 -1
  136. package/dist/runtime/errors.test.js +4 -3
  137. package/dist/runtime/errors.test.js.map +1 -1
  138. package/dist/runtime/frame.d.ts +17 -18
  139. package/dist/runtime/frame.d.ts.map +1 -1
  140. package/dist/runtime/frame.js +86 -31
  141. package/dist/runtime/frame.js.map +1 -1
  142. package/dist/runtime/frame.test.js +15 -11
  143. package/dist/runtime/frame.test.js.map +1 -1
  144. package/dist/runtime/index.d.ts +1 -1
  145. package/dist/runtime/index.d.ts.map +1 -1
  146. package/dist/runtime/index.js +1 -1
  147. package/dist/runtime/index.js.map +1 -1
  148. package/dist/runtime/local-service-commands.d.ts +7 -0
  149. package/dist/runtime/local-service-commands.d.ts.map +1 -0
  150. package/dist/runtime/local-service-commands.js +6 -0
  151. package/dist/runtime/local-service-commands.js.map +1 -0
  152. package/dist/runtime/runtime.test.js +3 -3
  153. package/dist/runtime/runtime.test.js.map +1 -1
  154. package/dist/runtime/subscribers.d.ts +37 -33
  155. package/dist/runtime/subscribers.d.ts.map +1 -1
  156. package/dist/runtime/subscribers.js +22 -21
  157. package/dist/runtime/subscribers.js.map +1 -1
  158. package/dist/runtime/subscribers.test.js +50 -31
  159. package/dist/runtime/subscribers.test.js.map +1 -1
  160. package/dist/service.d.ts +24 -39
  161. package/dist/service.d.ts.map +1 -1
  162. package/dist/service.js +190 -171
  163. package/dist/service.js.map +1 -1
  164. package/dist/service.test.js +117 -175
  165. package/dist/service.test.js.map +1 -1
  166. package/dist/test/index.d.ts +9 -1
  167. package/dist/test/index.d.ts.map +1 -1
  168. package/dist/test/index.js +14 -3
  169. package/dist/test/index.js.map +1 -1
  170. package/dist/test-utils/channel-service-fixture.d.ts +1 -2
  171. package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
  172. package/dist/test-utils/channel-service-fixture.js +28 -20
  173. package/dist/test-utils/channel-service-fixture.js.map +1 -1
  174. package/dist/test-utils/conformance-adapter.d.ts +2 -2
  175. package/dist/test-utils/conformance-adapter.d.ts.map +1 -1
  176. package/dist/test-utils/conformance-adapter.js +78 -54
  177. package/dist/test-utils/conformance-adapter.js.map +1 -1
  178. package/dist/test-utils/fake-service.d.ts +14 -39
  179. package/dist/test-utils/fake-service.d.ts.map +1 -1
  180. package/dist/test-utils/fake-service.js +33 -39
  181. package/dist/test-utils/fake-service.js.map +1 -1
  182. package/dist/test-utils/ids.d.ts +9 -0
  183. package/dist/test-utils/ids.d.ts.map +1 -0
  184. package/dist/test-utils/ids.js +52 -0
  185. package/dist/test-utils/ids.js.map +1 -0
  186. package/dist/test-utils/index.d.ts +10 -2
  187. package/dist/test-utils/index.d.ts.map +1 -1
  188. package/dist/test-utils/index.js +25 -9
  189. package/dist/test-utils/index.js.map +1 -1
  190. package/dist/ws-client.d.ts +59 -64
  191. package/dist/ws-client.d.ts.map +1 -1
  192. package/dist/ws-client.js +228 -175
  193. package/dist/ws-client.js.map +1 -1
  194. package/dist/ws-client.test.js +190 -176
  195. package/dist/ws-client.test.js.map +1 -1
  196. package/package.json +3 -3
  197. package/dist/cli/commands/permissions.d.ts +0 -66
  198. package/dist/cli/commands/permissions.d.ts.map +0 -1
  199. package/dist/cli/commands/permissions.js +0 -101
  200. package/dist/cli/commands/permissions.js.map +0 -1
  201. package/dist/cli/commands/permissions.test.d.ts +0 -2
  202. package/dist/cli/commands/permissions.test.d.ts.map +0 -1
  203. package/dist/cli/commands/permissions.test.js +0 -126
  204. package/dist/cli/commands/permissions.test.js.map +0 -1
  205. package/dist/internal/__tests__/s2c-partition-key.test.d.ts +0 -2
  206. package/dist/internal/__tests__/s2c-partition-key.test.d.ts.map +0 -1
  207. package/dist/internal/__tests__/s2c-partition-key.test.js +0 -230
  208. package/dist/internal/__tests__/s2c-partition-key.test.js.map +0 -1
  209. package/dist/internal/__tests__/s2c-partition-worker.test.d.ts +0 -2
  210. package/dist/internal/__tests__/s2c-partition-worker.test.d.ts.map +0 -1
  211. package/dist/internal/__tests__/s2c-partition-worker.test.js.map +0 -1
  212. package/dist/internal/__tests__/s2c-partitioned-dispatcher-real-ws.test.d.ts +0 -2
  213. package/dist/internal/__tests__/s2c-partitioned-dispatcher-real-ws.test.d.ts.map +0 -1
  214. package/dist/internal/__tests__/s2c-partitioned-dispatcher-real-ws.test.js.map +0 -1
  215. package/dist/internal/__tests__/s2c-partitioned-dispatcher.test.d.ts +0 -2
  216. package/dist/internal/__tests__/s2c-partitioned-dispatcher.test.d.ts.map +0 -1
  217. package/dist/internal/__tests__/s2c-partitioned-dispatcher.test.js.map +0 -1
  218. package/dist/internal/s2c-dispatcher-errors.d.ts.map +0 -1
  219. package/dist/internal/s2c-dispatcher-errors.js.map +0 -1
  220. package/dist/internal/s2c-partition-key.d.ts +0 -71
  221. package/dist/internal/s2c-partition-key.d.ts.map +0 -1
  222. package/dist/internal/s2c-partition-key.js +0 -140
  223. package/dist/internal/s2c-partition-key.js.map +0 -1
  224. package/dist/internal/s2c-partition-worker.d.ts.map +0 -1
  225. package/dist/internal/s2c-partition-worker.js.map +0 -1
  226. package/dist/internal/s2c-partitioned-dispatcher.d.ts.map +0 -1
  227. package/dist/internal/s2c-partitioned-dispatcher.js.map +0 -1
@@ -2,61 +2,69 @@ import * as path from "node:path";
2
2
  import * as os from "node:os";
3
3
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import { Effect, Either } from "effect";
5
- import { AppsAuthorizeDispatch, ConversationsGet, ErrorCodes, EventNames, MessagesSend, eventFrame, } from "@moltzap/protocol";
5
+ import { AppsAuthorizeDispatch, ConversationsGet, ErrorCodes, ConversationArchivedNotificationDefinition, ConversationCreatedNotificationDefinition, ConversationUnarchivedNotificationDefinition, MessageReceivedNotificationDefinition, MessagesSend, notificationFrame, } from "@moltzap/protocol";
6
6
  import { sanitizeForSystemReminder } from "./service.js";
7
7
  import { FakeMoltZapService } from "./test-utils/fake-service.js";
8
+ import { buildMessage, testAgentId, testConversationId, testMessageId, } from "./test-utils/index.js";
9
+ import { AgentsLookupByName, ConversationsCreate } from "@moltzap/protocol";
8
10
  /** Run a service Effect to a Promise for test assertions. */
9
11
  const run = (e) => Effect.runPromise(e);
12
+ const AGENT_ALICE_ID = testAgentId("agent-alice-id");
13
+ const AGENT_SELF_ID = testAgentId("agent-self");
14
+ const AGENT_GM_ID = testAgentId("agent-gm");
15
+ const CONVERSATION_ALICE_ID = testConversationId("conv-alice");
16
+ const CONVERSATION_ARCHIVED_ID = testConversationId("conv-archived");
17
+ const DISPATCH_MESSAGE_ID = testMessageId("msg-dispatch");
10
18
  describe("MoltZapService.sendToAgent", () => {
11
19
  let service;
12
20
  beforeEach(() => {
13
21
  service = new FakeMoltZapService();
14
22
  // `setResponse` is typed: the wire name must be a `RpcMethodName` literal
15
- // and the value must match `RpcMap[M]["result"]`. Both guard against the
23
+ // and the value must match `RpcResult<M>`. Both guard against the
16
24
  // contract-drift bug (A7) that motivated this fake.
17
- service.setResponse("agents/lookupByName", {
25
+ service.setResponse(AgentsLookupByName, {
18
26
  agents: [
19
27
  {
20
- id: "agent-alice-id",
28
+ id: AGENT_ALICE_ID,
21
29
  name: "alice",
22
30
  status: "active",
23
31
  },
24
32
  ],
25
33
  });
26
- service.setResponse("conversations/create", {
34
+ service.setResponse(ConversationsCreate, {
27
35
  conversation: {
28
- id: "conv-alice",
36
+ id: CONVERSATION_ALICE_ID,
29
37
  type: "dm",
30
- createdBy: "agent-self",
38
+ createdBy: AGENT_SELF_ID,
31
39
  createdAt: "2026-04-16T00:00:00Z",
32
40
  updatedAt: "2026-04-16T00:00:00Z",
33
41
  },
34
42
  });
35
- service.setResponse("messages/send", {
36
- message: {
43
+ service.setResponse(MessagesSend, {
44
+ message: buildMessage({
37
45
  id: "msg-1",
38
46
  conversationId: "conv-alice",
39
47
  senderId: "agent-self",
40
48
  parts: [{ type: "text", text: "placeholder" }],
41
49
  createdAt: "2026-04-16T00:00:00Z",
42
- },
50
+ }),
43
51
  });
44
52
  });
45
53
  it("resolves agent name, creates a DM, and sends the message on first call", async () => {
46
54
  await run(service.sendToAgent("alice", "hello"));
47
55
  expect(service.calls).toEqual([
48
- { method: "agents/lookupByName", params: { names: ["alice"] } },
56
+ { method: AgentsLookupByName.name, params: { names: ["alice"] } },
49
57
  {
50
- method: "conversations/create",
58
+ method: ConversationsCreate.name,
51
59
  params: {
52
60
  type: "dm",
53
- participants: [{ type: "agent", id: "agent-alice-id" }],
61
+ participants: [{ type: "agent", id: AGENT_ALICE_ID }],
54
62
  },
55
63
  },
56
64
  {
57
- method: "messages/send",
65
+ method: MessagesSend.name,
58
66
  params: {
59
- conversationId: "conv-alice",
67
+ conversationId: CONVERSATION_ALICE_ID,
60
68
  parts: [{ type: "text", text: "hello" }],
61
69
  },
62
70
  },
@@ -68,36 +76,39 @@ describe("MoltZapService.sendToAgent", () => {
68
76
  await run(service.sendToAgent("alice", "second"));
69
77
  expect(service.calls).toEqual([
70
78
  {
71
- method: "messages/send",
79
+ method: MessagesSend.name,
72
80
  params: {
73
- conversationId: "conv-alice",
81
+ conversationId: CONVERSATION_ALICE_ID,
74
82
  parts: [{ type: "text", text: "second" }],
75
83
  },
76
84
  },
77
85
  ]);
78
86
  });
79
87
  it("forwards replyTo to messages/send as replyToId", async () => {
80
- await run(service.sendToAgent("alice", "reply text", { replyTo: "msg-123" }));
81
- const sendCall = service.calls.find((c) => c.method === "messages/send");
88
+ const replyToId = testMessageId("msg-123");
89
+ await run(service.sendToAgent("alice", "reply text", { replyTo: replyToId }));
90
+ const sendCall = service.calls.find((c) => c.method === MessagesSend.name);
82
91
  expect(sendCall?.params).toEqual({
83
- conversationId: "conv-alice",
92
+ conversationId: CONVERSATION_ALICE_ID,
84
93
  parts: [{ type: "text", text: "reply text" }],
85
- replyToId: "msg-123",
94
+ replyToId,
86
95
  });
87
96
  });
88
97
  it("maintains separate cache entries per agent name", async () => {
89
- service.setResponse("agents/lookupByName", {
90
- agents: [{ id: "agent-alice-id", name: "alice", status: "active" }],
98
+ service.setResponse(AgentsLookupByName, {
99
+ agents: [{ id: AGENT_ALICE_ID, name: "alice", status: "active" }],
91
100
  });
92
101
  await run(service.sendToAgent("alice", "hello alice"));
93
- service.setResponse("agents/lookupByName", {
94
- agents: [{ id: "agent-bob-id", name: "bob", status: "active" }],
102
+ service.setResponse(AgentsLookupByName, {
103
+ agents: [
104
+ { id: testAgentId("agent-bob-id"), name: "bob", status: "active" },
105
+ ],
95
106
  });
96
- service.setResponse("conversations/create", {
107
+ service.setResponse(ConversationsCreate, {
97
108
  conversation: {
98
- id: "conv-bob",
109
+ id: testConversationId("conv-bob"),
99
110
  type: "dm",
100
- createdBy: "agent-self",
111
+ createdBy: AGENT_SELF_ID,
101
112
  createdAt: "2026-04-16T00:00:00Z",
102
113
  updatedAt: "2026-04-16T00:00:00Z",
103
114
  },
@@ -106,25 +117,25 @@ describe("MoltZapService.sendToAgent", () => {
106
117
  service.calls = [];
107
118
  await run(service.sendToAgent("alice", "alice again"));
108
119
  await run(service.sendToAgent("bob", "bob again"));
109
- const sendCalls = service.calls.filter((c) => c.method === "messages/send");
120
+ const sendCalls = service.calls.filter((c) => c.method === MessagesSend.name);
110
121
  expect(sendCalls).toHaveLength(2);
111
- expect(sendCalls[0].params.conversationId).toBe("conv-alice");
112
- expect(sendCalls[1].params.conversationId).toBe("conv-bob");
122
+ expect(sendCalls[0].params.conversationId).toBe(CONVERSATION_ALICE_ID);
123
+ expect(sendCalls[1].params.conversationId).toBe(testConversationId("conv-bob"));
113
124
  });
114
125
  it("throws a clear error when no agent is found for the given name", async () => {
115
- service.setResponse("agents/lookupByName", { agents: [] });
126
+ service.setResponse(AgentsLookupByName, { agents: [] });
116
127
  await expect(run(service.sendToAgent("nobody", "hi"))).rejects.toThrow(/Agent not found: nobody/);
117
128
  });
118
129
  it("propagates errors from agents/lookupByName", async () => {
119
- service.deleteResponse("agents/lookupByName");
130
+ service.deleteResponse(AgentsLookupByName);
120
131
  await expect(run(service.sendToAgent("alice", "hi"))).rejects.toThrow(/no canned response for agents\/lookupByName/);
121
132
  });
122
133
  it("propagates errors from conversations/create", async () => {
123
- service.deleteResponse("conversations/create");
134
+ service.deleteResponse(ConversationsCreate);
124
135
  await expect(run(service.sendToAgent("alice", "hi"))).rejects.toThrow(/no canned response for conversations\/create/);
125
136
  });
126
137
  it("propagates errors from messages/send", async () => {
127
- service.deleteResponse("messages/send");
138
+ service.deleteResponse(MessagesSend);
128
139
  await expect(run(service.sendToAgent("alice", "hi"))).rejects.toThrow(/no canned response for messages\/send/);
129
140
  });
130
141
  });
@@ -154,7 +165,7 @@ describe("sanitizeForSystemReminder", () => {
154
165
  describe("MoltZapService.authorizeDispatch", () => {
155
166
  it("uses the long app-dispatch RPC timeout instead of the generic RPC timeout", async () => {
156
167
  const service = new FakeMoltZapService();
157
- service.setResponse(AppsAuthorizeDispatch.name, {
168
+ service.setResponse(AppsAuthorizeDispatch, {
158
169
  admission: {
159
170
  decision: "grant",
160
171
  leaseId: "lease-1",
@@ -162,23 +173,23 @@ describe("MoltZapService.authorizeDispatch", () => {
162
173
  },
163
174
  });
164
175
  const result = await run(service.authorizeDispatch({
165
- conversationId: "conv-1",
166
- senderAgentId: "agent-gm",
176
+ conversationId: CONVERSATION_ALICE_ID,
177
+ senderAgentId: AGENT_GM_ID,
167
178
  attempt: 0,
168
179
  receivedAt: "2026-04-29T22:00:00.000Z",
169
180
  clock: {
170
- domainId: "conv-1",
181
+ domainId: CONVERSATION_ALICE_ID,
171
182
  epoch: 1,
172
- vector: { "agent-gm": 1 },
183
+ vector: { [AGENT_GM_ID]: 1 },
173
184
  },
174
185
  pending: [],
175
- message: {
176
- id: "msg-1",
177
- conversationId: "conv-1",
178
- senderId: "agent-gm",
186
+ message: buildMessage({
187
+ id: DISPATCH_MESSAGE_ID,
188
+ conversationId: CONVERSATION_ALICE_ID,
189
+ senderId: AGENT_GM_ID,
179
190
  parts: [{ type: "text", text: "Time to vote!" }],
180
191
  createdAt: "2026-04-29T22:00:00.000Z",
181
- },
192
+ }),
182
193
  }));
183
194
  expect(result).toEqual({
184
195
  _tag: "grant",
@@ -195,14 +206,14 @@ describe("MoltZapService.authorizeDispatch", () => {
195
206
  describe("MoltZapService.getContext — XML injection hardening", () => {
196
207
  /** Build a message that lands in `messages` via addMessage(). */
197
208
  function msg(overrides) {
198
- return {
199
- id: overrides.id ?? "msg-1",
200
- conversationId: overrides.conversationId ?? "conv-other",
201
- senderId: overrides.senderId ?? "agent-attacker",
202
- parts: overrides.parts ?? [{ type: "text", text: "hello" }],
203
- createdAt: overrides.createdAt ?? new Date().toISOString(),
209
+ return buildMessage({
210
+ id: "msg-1",
211
+ conversationId: "conv-other",
212
+ senderId: "agent-attacker",
213
+ parts: [{ type: "text", text: "hello" }],
214
+ createdAt: new Date().toISOString(),
204
215
  ...overrides,
205
- };
216
+ });
206
217
  }
207
218
  it("escapes senderName with </system-reminder> injection attempt", () => {
208
219
  const service = new FakeMoltZapService();
@@ -274,13 +285,13 @@ describe("MoltZapService.getContext — XML injection hardening", () => {
274
285
  });
275
286
  describe("MoltZapService.peekContextEntries", () => {
276
287
  function addSimpleMessage(service, convId, seq, text = "hi") {
277
- service.addMessage(convId, {
288
+ service.addMessage(convId, buildMessage({
278
289
  id: `m-${seq}`,
279
290
  conversationId: convId,
280
291
  senderId: "agent-bob",
281
292
  parts: [{ type: "text", text }],
282
293
  createdAt: new Date().toISOString(),
283
- });
294
+ }));
284
295
  }
285
296
  it("returns structured entries without advancing markers", () => {
286
297
  const service = new FakeMoltZapService();
@@ -369,68 +380,68 @@ describe("MoltZapService.peekFullMessages", () => {
369
380
  const service = new FakeMoltZapService();
370
381
  service.setAgentNameDirect("agent-bob", "Bob");
371
382
  service.setAgentNameDirect("agent-alice", "Alice");
372
- service.addMessage("conv-a", {
383
+ service.addMessage("conv-a", buildMessage({
373
384
  id: "m-1",
374
385
  conversationId: "conv-a",
375
386
  senderId: "agent-bob",
376
387
  parts: [{ type: "text", text: "first" }],
377
388
  createdAt: "2026-04-13T22:00:00Z",
378
- });
379
- service.addMessage("conv-b", {
389
+ }));
390
+ service.addMessage("conv-b", buildMessage({
380
391
  id: "m-2",
381
392
  conversationId: "conv-b",
382
393
  senderId: "agent-alice",
383
394
  parts: [{ type: "text", text: "second" }],
384
395
  createdAt: "2026-04-13T22:00:01Z",
385
- });
386
- service.addMessage("conv-a", {
396
+ }));
397
+ service.addMessage("conv-a", buildMessage({
387
398
  id: "m-3",
388
399
  conversationId: "conv-a",
389
400
  senderId: "agent-bob",
390
401
  parts: [{ type: "text", text: "third" }],
391
402
  createdAt: "2026-04-13T22:00:02Z",
392
- });
403
+ }));
393
404
  const { messages } = service.peekFullMessages("conv-self");
394
405
  expect(messages).toHaveLength(3);
395
406
  expect(messages.map((m) => m.text)).toEqual(["first", "second", "third"]);
396
407
  expect(messages[0].conversationId).toBe("conv-a");
397
408
  expect(messages[0].senderName).toBe("Bob");
398
- expect(messages[0].senderId).toBe("agent-bob");
409
+ expect(messages[0].senderId).toBe(testAgentId("agent-bob"));
399
410
  expect(messages[1].conversationId).toBe("conv-b");
400
411
  expect(messages[1].senderName).toBe("Alice");
401
- expect(messages[1].senderId).toBe("agent-alice");
412
+ expect(messages[1].senderId).toBe(testAgentId("agent-alice"));
402
413
  });
403
414
  it("excludes messages from the current conversation", () => {
404
415
  const service = new FakeMoltZapService();
405
- service.addMessage("conv-self", {
416
+ service.addMessage("conv-self", buildMessage({
406
417
  id: "m-1",
407
418
  conversationId: "conv-self",
408
419
  senderId: "agent-bob",
409
420
  parts: [{ type: "text", text: "own conv" }],
410
421
  createdAt: "2026-04-13T22:00:00Z",
411
- });
422
+ }));
412
423
  const { messages } = service.peekFullMessages("conv-self");
413
424
  expect(messages).toHaveLength(0);
414
425
  });
415
426
  it("commit advances markers; subsequent peek returns only new messages", () => {
416
427
  const service = new FakeMoltZapService();
417
- service.addMessage("conv-a", {
428
+ service.addMessage("conv-a", buildMessage({
418
429
  id: "m-1",
419
430
  conversationId: "conv-a",
420
431
  senderId: "agent-bob",
421
432
  parts: [{ type: "text", text: "old" }],
422
433
  createdAt: "2026-04-13T22:00:00Z",
423
- });
434
+ }));
424
435
  const first = service.peekFullMessages("conv-self");
425
436
  first.commit();
426
437
  expect(first.messages).toHaveLength(1);
427
- service.addMessage("conv-a", {
438
+ service.addMessage("conv-a", buildMessage({
428
439
  id: "m-2",
429
440
  conversationId: "conv-a",
430
441
  senderId: "agent-bob",
431
442
  parts: [{ type: "text", text: "new" }],
432
443
  createdAt: "2026-04-13T22:01:00Z",
433
- });
444
+ }));
434
445
  const second = service.peekFullMessages("conv-self");
435
446
  expect(second.messages).toHaveLength(1);
436
447
  expect(second.messages[0].text).toBe("new");
@@ -439,14 +450,13 @@ describe("MoltZapService.peekFullMessages", () => {
439
450
  const service = new FakeMoltZapService();
440
451
  for (let c = 0; c < 10; c++) {
441
452
  for (let m = 0; m < 5; m++) {
442
- service.addMessage(`conv-${c}`, {
453
+ service.addMessage(`conv-${c}`, buildMessage({
443
454
  id: `m-${c}-${m}`,
444
455
  conversationId: `conv-${c}`,
445
456
  senderId: "agent-bob",
446
- seq: m + 1,
447
457
  parts: [{ type: "text", text: `c${c}-m${m}` }],
448
458
  createdAt: new Date(Date.now() + c * 10000 + m * 1000).toISOString(),
449
- });
459
+ }));
450
460
  }
451
461
  }
452
462
  const { messages } = service.peekFullMessages("conv-self");
@@ -454,13 +464,13 @@ describe("MoltZapService.peekFullMessages", () => {
454
464
  });
455
465
  it("peek without commit is idempotent", () => {
456
466
  const service = new FakeMoltZapService();
457
- service.addMessage("conv-a", {
467
+ service.addMessage("conv-a", buildMessage({
458
468
  id: "m-1",
459
469
  conversationId: "conv-a",
460
470
  senderId: "agent-bob",
461
471
  parts: [{ type: "text", text: "hi" }],
462
472
  createdAt: "2026-04-13T22:00:00Z",
463
- });
473
+ }));
464
474
  const a = service.peekFullMessages("conv-self").messages;
465
475
  const b = service.peekFullMessages("conv-self").messages;
466
476
  expect(a).toHaveLength(1);
@@ -468,128 +478,86 @@ describe("MoltZapService.peekFullMessages", () => {
468
478
  });
469
479
  it("commit for one viewing conv does not affect another", () => {
470
480
  const service = new FakeMoltZapService();
471
- service.addMessage("conv-a", {
481
+ service.addMessage("conv-a", buildMessage({
472
482
  id: "m-1",
473
483
  conversationId: "conv-a",
474
484
  senderId: "agent-bob",
475
485
  parts: [{ type: "text", text: "hi" }],
476
486
  createdAt: "2026-04-13T22:00:00Z",
477
- });
487
+ }));
478
488
  service.peekFullMessages("viewer-1").commit();
479
489
  expect(service.peekFullMessages("viewer-2").messages).toHaveLength(1);
480
490
  });
481
491
  it("stores more than 20 messages per conversation without eviction", () => {
482
492
  const service = new FakeMoltZapService();
483
493
  for (let i = 1; i <= 30; i++) {
484
- service.addMessage("conv-a", {
494
+ service.addMessage("conv-a", buildMessage({
485
495
  id: `m-${i}`,
486
496
  conversationId: "conv-a",
487
497
  senderId: "agent-bob",
488
- seq: i,
489
498
  parts: [{ type: "text", text: `msg-${i}` }],
490
499
  createdAt: new Date(Date.now() + i * 1000).toISOString(),
491
- });
500
+ }));
492
501
  }
493
502
  const { messages } = service.peekFullMessages("conv-self");
494
503
  expect(messages).toHaveLength(30);
495
504
  });
496
505
  });
497
- describe("MoltZapService.on('permissionRequired')", () => {
498
- it("fires handler when permissions/required event arrives", () => {
499
- const service = new FakeMoltZapService();
500
- const received = [];
501
- service.on("permissionRequired", (data) => received.push(data));
502
- const event = {
503
- jsonrpc: "2.0",
504
- type: "event",
505
- event: EventNames.PermissionsRequired,
506
- data: {
507
- sessionId: "sess-1",
508
- appId: "test-app",
509
- resource: "contacts",
510
- access: ["read"],
511
- requestId: crypto.randomUUID(),
512
- targetUserId: crypto.randomUUID(),
513
- },
514
- };
515
- Reflect.get(service, "handleEvent").call(service, event);
516
- expect(received).toHaveLength(1);
517
- expect(received[0]).toMatchObject({
518
- sessionId: "sess-1",
519
- appId: "test-app",
520
- resource: "contacts",
521
- access: ["read"],
522
- });
523
- });
524
- it("does not fire for unrelated events", () => {
525
- const service = new FakeMoltZapService();
526
- const received = [];
527
- service.on("permissionRequired", (data) => received.push(data));
528
- const event = {
529
- jsonrpc: "2.0",
530
- type: "event",
531
- event: EventNames.PresenceChanged,
532
- data: { agentId: "agent-1", status: "online" },
533
- };
534
- Reflect.get(service, "handleEvent").call(service, event);
535
- expect(received).toHaveLength(0);
536
- });
537
- });
538
506
  describe("MoltZapService conversation archive lifecycle", () => {
539
507
  it("purges local state, fires conversationArchived, and locally rejects sends", async () => {
540
508
  const service = new FakeMoltZapService();
541
- service.setResponse(ConversationsGet.name, {
509
+ service.setResponse(ConversationsGet, {
542
510
  conversation: {
543
- id: "conv-archived",
511
+ id: CONVERSATION_ARCHIVED_ID,
544
512
  type: "group",
545
513
  name: "Archived",
546
- createdBy: "agent-self",
514
+ createdBy: AGENT_SELF_ID,
547
515
  createdAt: "2026-05-01T00:00:00.000Z",
548
516
  updatedAt: "2026-05-01T00:00:00.000Z",
549
517
  },
550
518
  participants: [],
551
519
  });
552
- service.setResponse(MessagesSend.name, {
553
- message: {
520
+ service.setResponse(MessagesSend, {
521
+ message: buildMessage({
554
522
  id: "msg-unreachable",
555
523
  conversationId: "conv-archived",
556
524
  senderId: "agent-self",
557
525
  parts: [{ type: "text", text: "unreachable" }],
558
526
  createdAt: "2026-05-01T00:00:00.000Z",
559
- },
527
+ }),
560
528
  });
561
- service.emitEvent(eventFrame(EventNames.ConversationCreated, {
529
+ service.emitEvent(notificationFrame(ConversationCreatedNotificationDefinition, {
562
530
  conversation: {
563
- id: "conv-archived",
531
+ id: CONVERSATION_ARCHIVED_ID,
564
532
  type: "group",
565
533
  name: "Archived",
566
- createdBy: "agent-self",
534
+ createdBy: AGENT_SELF_ID,
567
535
  createdAt: "2026-05-01T00:00:00.000Z",
568
536
  updatedAt: "2026-05-01T00:00:00.000Z",
569
537
  },
570
538
  }));
571
- service.addMessage("conv-archived", {
539
+ service.addMessage(CONVERSATION_ARCHIVED_ID, buildMessage({
572
540
  id: "msg-1",
573
541
  conversationId: "conv-archived",
574
542
  senderId: "agent-other",
575
543
  parts: [{ type: "text", text: "old" }],
576
544
  createdAt: "2026-05-01T00:00:00.000Z",
577
- });
545
+ }));
578
546
  const archivedEvents = [];
579
547
  const unarchivedEvents = [];
580
548
  service.on("conversationArchived", (data) => archivedEvents.push(data));
581
549
  service.on("conversationUnarchived", (data) => unarchivedEvents.push(data));
582
- const archivedEvent = eventFrame(EventNames.ConversationArchived, {
583
- conversationId: "conv-archived",
550
+ const archivedEvent = notificationFrame(ConversationArchivedNotificationDefinition, {
551
+ conversationId: CONVERSATION_ARCHIVED_ID,
584
552
  archivedAt: "2026-05-01T00:01:00.000Z",
585
- by: "agent-gm",
553
+ by: AGENT_GM_ID,
586
554
  });
587
555
  service.emitEvent(archivedEvent);
588
- expect(service.isConversationArchived("conv-archived")).toBe(true);
589
- expect(service.getConversation("conv-archived")).toBeUndefined();
590
- expect(service.getHistory("conv-archived")).toEqual([]);
591
- expect(archivedEvents).toEqual([archivedEvent.data]);
592
- const lateSend = await run(Effect.either(service.send("conv-archived", "should not hit rpc")));
556
+ expect(service.isConversationArchived(CONVERSATION_ARCHIVED_ID)).toBe(true);
557
+ expect(service.getConversation(CONVERSATION_ARCHIVED_ID)).toBeUndefined();
558
+ expect(service.getHistory(CONVERSATION_ARCHIVED_ID)).toEqual([]);
559
+ expect(archivedEvents).toEqual([archivedEvent.params]);
560
+ const lateSend = await run(Effect.either(service.send(CONVERSATION_ARCHIVED_ID, "should not hit rpc")));
593
561
  expect(Either.isLeft(lateSend)).toBe(true);
594
562
  if (Either.isLeft(lateSend)) {
595
563
  expect(lateSend.left).toMatchObject({
@@ -598,34 +566,13 @@ describe("MoltZapService conversation archive lifecycle", () => {
598
566
  });
599
567
  }
600
568
  expect(service.calls.filter((c) => c.method === MessagesSend.name)).toEqual([]);
601
- const unarchivedEvent = eventFrame(EventNames.ConversationUnarchived, {
602
- conversationId: "conv-archived",
603
- by: "agent-gm",
569
+ const unarchivedEvent = notificationFrame(ConversationUnarchivedNotificationDefinition, {
570
+ conversationId: CONVERSATION_ARCHIVED_ID,
571
+ by: AGENT_GM_ID,
604
572
  });
605
573
  service.emitEvent(unarchivedEvent);
606
- expect(service.isConversationArchived("conv-archived")).toBe(false);
607
- expect(unarchivedEvents).toEqual([unarchivedEvent.data]);
608
- });
609
- });
610
- describe("MoltZapService.grantPermission", () => {
611
- it("sends permissions/grant RPC", async () => {
612
- const service = new FakeMoltZapService();
613
- service.setResponse("permissions/grant", {});
614
- await run(service.grantPermission({
615
- sessionId: "sess-1",
616
- agentId: "agent-2",
617
- resource: "contacts",
618
- access: ["read"],
619
- }));
620
- expect(service.calls).toContainEqual({
621
- method: "permissions/grant",
622
- params: {
623
- sessionId: "sess-1",
624
- agentId: "agent-2",
625
- resource: "contacts",
626
- access: ["read"],
627
- },
628
- });
574
+ expect(service.isConversationArchived(CONVERSATION_ARCHIVED_ID)).toBe(false);
575
+ expect(unarchivedEvents).toEqual([unarchivedEvent.params]);
629
576
  });
630
577
  });
631
578
  describe("MoltZapService.socketPath — agentId sanitization", () => {
@@ -725,22 +672,17 @@ describe("MoltZapService.fanout — message handlers", () => {
725
672
  service.on("message", (m) => {
726
673
  seen.push(m);
727
674
  });
728
- const msg = {
675
+ const msg = buildMessage({
729
676
  id: "m-1",
730
677
  conversationId: "conv-1",
731
678
  senderId: "agent-other",
732
679
  parts: [{ type: "text", text: "hi" }],
733
680
  createdAt: "2026-04-16T00:00:00.000Z",
734
- };
735
- const event = {
736
- jsonrpc: "2.0",
737
- type: "event",
738
- event: EventNames.MessageReceived,
739
- data: { message: msg },
740
- };
741
- // handleEvent is private; reach into it the same way the existing
742
- // permissions test does.
743
- Reflect.get(service, "handleEvent").call(service, event);
681
+ });
682
+ const event = notificationFrame(MessageReceivedNotificationDefinition, {
683
+ message: msg,
684
+ });
685
+ service.emitEvent(event);
744
686
  // Second handler still fired despite first handler throwing.
745
687
  expect(seen).toEqual([msg]);
746
688
  expect(logger.error).toHaveBeenCalledOnce();