@openclaw/crabline 0.1.11 → 0.1.12

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 (197) hide show
  1. package/README.md +99 -12
  2. package/dist/src/bin/crabline.js +3 -1
  3. package/dist/src/bin/crabline.js.map +1 -1
  4. package/dist/src/cli/program.d.ts +5 -1
  5. package/dist/src/cli/program.js +264 -50
  6. package/dist/src/cli/program.js.map +1 -1
  7. package/dist/src/config/load.js +39 -3
  8. package/dist/src/config/load.js.map +1 -1
  9. package/dist/src/config/schema.d.ts +20 -0
  10. package/dist/src/config/schema.js +163 -35
  11. package/dist/src/config/schema.js.map +1 -1
  12. package/dist/src/core/errors.d.ts +1 -0
  13. package/dist/src/core/errors.js +6 -3
  14. package/dist/src/core/errors.js.map +1 -1
  15. package/dist/src/core/http-path.d.ts +1 -0
  16. package/dist/src/core/http-path.js +14 -0
  17. package/dist/src/core/http-path.js.map +1 -0
  18. package/dist/src/core/matcher.d.ts +3 -1
  19. package/dist/src/core/matcher.js +58 -2
  20. package/dist/src/core/matcher.js.map +1 -1
  21. package/dist/src/core/nonces.d.ts +2 -0
  22. package/dist/src/core/nonces.js +11 -1
  23. package/dist/src/core/nonces.js.map +1 -1
  24. package/dist/src/core/reporters.d.ts +5 -0
  25. package/dist/src/core/reporters.js +26 -2
  26. package/dist/src/core/reporters.js.map +1 -1
  27. package/dist/src/core/run.d.ts +3 -1
  28. package/dist/src/core/run.js +178 -66
  29. package/dist/src/core/run.js.map +1 -1
  30. package/dist/src/matrix-ids.d.ts +5 -0
  31. package/dist/src/matrix-ids.js +83 -0
  32. package/dist/src/matrix-ids.js.map +1 -0
  33. package/dist/src/openclaw/artifact-generation.d.ts +7 -2
  34. package/dist/src/openclaw/artifact-generation.js +399 -27
  35. package/dist/src/openclaw/artifact-generation.js.map +1 -1
  36. package/dist/src/openclaw/bridges/matrix.js +21 -4
  37. package/dist/src/openclaw/bridges/matrix.js.map +1 -1
  38. package/dist/src/openclaw/bridges/mattermost.js +13 -8
  39. package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
  40. package/dist/src/openclaw/bridges/probe-response.d.ts +1 -0
  41. package/dist/src/openclaw/bridges/probe-response.js +5 -0
  42. package/dist/src/openclaw/bridges/probe-response.js.map +1 -0
  43. package/dist/src/openclaw/bridges/signal.js +15 -6
  44. package/dist/src/openclaw/bridges/signal.js.map +1 -1
  45. package/dist/src/openclaw/bridges/slack.js +116 -23
  46. package/dist/src/openclaw/bridges/slack.js.map +1 -1
  47. package/dist/src/openclaw/bridges/telegram.js +45 -19
  48. package/dist/src/openclaw/bridges/telegram.js.map +1 -1
  49. package/dist/src/openclaw/bridges/whatsapp.js +32 -16
  50. package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
  51. package/dist/src/openclaw/bridges/zalo.js +8 -3
  52. package/dist/src/openclaw/bridges/zalo.js.map +1 -1
  53. package/dist/src/openclaw/private-file.d.ts +38 -3
  54. package/dist/src/openclaw/private-file.js +2008 -87
  55. package/dist/src/openclaw/private-file.js.map +1 -1
  56. package/dist/src/openclaw/shared.d.ts +1 -0
  57. package/dist/src/openclaw/shared.js +48 -13
  58. package/dist/src/openclaw/shared.js.map +1 -1
  59. package/dist/src/openclaw/smoke-lock.d.ts +9 -0
  60. package/dist/src/openclaw/smoke-lock.js +626 -172
  61. package/dist/src/openclaw/smoke-lock.js.map +1 -1
  62. package/dist/src/openclaw.js +225 -42
  63. package/dist/src/openclaw.js.map +1 -1
  64. package/dist/src/platform/process-owned-lock.d.ts +11 -0
  65. package/dist/src/platform/process-owned-lock.js +1698 -0
  66. package/dist/src/platform/process-owned-lock.js.map +1 -0
  67. package/dist/src/platform/recorder-directory.d.ts +1 -0
  68. package/dist/src/platform/recorder-directory.js +14 -0
  69. package/dist/src/platform/recorder-directory.js.map +1 -0
  70. package/dist/src/platform/windows-acl.d.ts +18 -0
  71. package/dist/src/platform/windows-acl.js +1367 -0
  72. package/dist/src/platform/windows-acl.js.map +1 -0
  73. package/dist/src/platform/windows-lock-root.d.ts +13 -0
  74. package/dist/src/platform/windows-lock-root.js +98 -0
  75. package/dist/src/platform/windows-lock-root.js.map +1 -0
  76. package/dist/src/providers/builtin/discord.d.ts +4 -0
  77. package/dist/src/providers/builtin/discord.js +52 -9
  78. package/dist/src/providers/builtin/discord.js.map +1 -1
  79. package/dist/src/providers/builtin/external-webhook-auth.d.ts +1 -0
  80. package/dist/src/providers/builtin/external-webhook-auth.js +38 -1
  81. package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -1
  82. package/dist/src/providers/builtin/feishu.d.ts +1 -0
  83. package/dist/src/providers/builtin/feishu.js +96 -26
  84. package/dist/src/providers/builtin/feishu.js.map +1 -1
  85. package/dist/src/providers/builtin/googlechat.d.ts +1 -0
  86. package/dist/src/providers/builtin/googlechat.js +95 -26
  87. package/dist/src/providers/builtin/googlechat.js.map +1 -1
  88. package/dist/src/providers/builtin/imessage.js +60 -17
  89. package/dist/src/providers/builtin/imessage.js.map +1 -1
  90. package/dist/src/providers/builtin/loopback.js +88 -35
  91. package/dist/src/providers/builtin/loopback.js.map +1 -1
  92. package/dist/src/providers/builtin/matrix.d.ts +2 -2
  93. package/dist/src/providers/builtin/matrix.js +78 -21
  94. package/dist/src/providers/builtin/matrix.js.map +1 -1
  95. package/dist/src/providers/builtin/mattermost.d.ts +11 -7
  96. package/dist/src/providers/builtin/mattermost.js +138 -24
  97. package/dist/src/providers/builtin/mattermost.js.map +1 -1
  98. package/dist/src/providers/builtin/msteams.d.ts +1 -0
  99. package/dist/src/providers/builtin/msteams.js +118 -38
  100. package/dist/src/providers/builtin/msteams.js.map +1 -1
  101. package/dist/src/providers/builtin/native-local-mock.js +4 -4
  102. package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
  103. package/dist/src/providers/builtin/script.js +1057 -146
  104. package/dist/src/providers/builtin/script.js.map +1 -1
  105. package/dist/src/providers/builtin/slack.d.ts +8 -1
  106. package/dist/src/providers/builtin/slack.js +327 -24
  107. package/dist/src/providers/builtin/slack.js.map +1 -1
  108. package/dist/src/providers/builtin/telegram.d.ts +1 -1
  109. package/dist/src/providers/builtin/telegram.js +55 -14
  110. package/dist/src/providers/builtin/telegram.js.map +1 -1
  111. package/dist/src/providers/builtin/whatsapp.d.ts +4 -3
  112. package/dist/src/providers/builtin/whatsapp.js +157 -29
  113. package/dist/src/providers/builtin/whatsapp.js.map +1 -1
  114. package/dist/src/providers/builtin/zalo.d.ts +5 -2
  115. package/dist/src/providers/builtin/zalo.js +31 -7
  116. package/dist/src/providers/builtin/zalo.js.map +1 -1
  117. package/dist/src/providers/catalog.d.ts +1 -1
  118. package/dist/src/providers/catalog.js +1 -1
  119. package/dist/src/providers/catalog.js.map +1 -1
  120. package/dist/src/providers/local-mock.d.ts +4 -0
  121. package/dist/src/providers/local-mock.js +162 -35
  122. package/dist/src/providers/local-mock.js.map +1 -1
  123. package/dist/src/providers/native-ids.d.ts +2 -0
  124. package/dist/src/providers/native-ids.js +3 -0
  125. package/dist/src/providers/native-ids.js.map +1 -1
  126. package/dist/src/providers/recorder.d.ts +14 -3
  127. package/dist/src/providers/recorder.js +521 -41
  128. package/dist/src/providers/recorder.js.map +1 -1
  129. package/dist/src/providers/registry.d.ts +1 -0
  130. package/dist/src/providers/registry.js +65 -20
  131. package/dist/src/providers/registry.js.map +1 -1
  132. package/dist/src/providers/signed-jwt.d.ts +6 -1
  133. package/dist/src/providers/signed-jwt.js +241 -50
  134. package/dist/src/providers/signed-jwt.js.map +1 -1
  135. package/dist/src/providers/slack-ids.d.ts +1 -0
  136. package/dist/src/providers/slack-ids.js +5 -0
  137. package/dist/src/providers/slack-ids.js.map +1 -1
  138. package/dist/src/providers/target-normalizers.d.ts +9 -1
  139. package/dist/src/providers/target-normalizers.js +177 -58
  140. package/dist/src/providers/target-normalizers.js.map +1 -1
  141. package/dist/src/providers/webhook-server.d.ts +3 -0
  142. package/dist/src/providers/webhook-server.js +268 -47
  143. package/dist/src/providers/webhook-server.js.map +1 -1
  144. package/dist/src/servers/http.d.ts +14 -2
  145. package/dist/src/servers/http.js +229 -58
  146. package/dist/src/servers/http.js.map +1 -1
  147. package/dist/src/servers/matrix.d.ts +3 -0
  148. package/dist/src/servers/matrix.js +268 -110
  149. package/dist/src/servers/matrix.js.map +1 -1
  150. package/dist/src/servers/mattermost.d.ts +5 -0
  151. package/dist/src/servers/mattermost.js +389 -91
  152. package/dist/src/servers/mattermost.js.map +1 -1
  153. package/dist/src/servers/recorder.d.ts +11 -0
  154. package/dist/src/servers/recorder.js +1072 -69
  155. package/dist/src/servers/recorder.js.map +1 -1
  156. package/dist/src/servers/signal.d.ts +1 -0
  157. package/dist/src/servers/signal.js +279 -44
  158. package/dist/src/servers/signal.js.map +1 -1
  159. package/dist/src/servers/slack.d.ts +15 -0
  160. package/dist/src/servers/slack.js +259 -51
  161. package/dist/src/servers/slack.js.map +1 -1
  162. package/dist/src/servers/telegram-identity.d.ts +6 -0
  163. package/dist/src/servers/telegram-identity.js +27 -0
  164. package/dist/src/servers/telegram-identity.js.map +1 -0
  165. package/dist/src/servers/telegram.d.ts +17 -6
  166. package/dist/src/servers/telegram.js +1169 -107
  167. package/dist/src/servers/telegram.js.map +1 -1
  168. package/dist/src/servers/webhook-target.d.ts +12 -4
  169. package/dist/src/servers/webhook-target.js +226 -28
  170. package/dist/src/servers/webhook-target.js.map +1 -1
  171. package/dist/src/servers/whatsapp-baileys-websocket.d.ts +60 -1
  172. package/dist/src/servers/whatsapp-baileys-websocket.js +782 -112
  173. package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
  174. package/dist/src/servers/whatsapp-jid.d.ts +1 -0
  175. package/dist/src/servers/whatsapp-jid.js +9 -5
  176. package/dist/src/servers/whatsapp-jid.js.map +1 -1
  177. package/dist/src/servers/whatsapp-wire/binary-node.d.ts +1 -0
  178. package/dist/src/servers/whatsapp-wire/binary-node.js +37 -13
  179. package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
  180. package/dist/src/servers/whatsapp-wire/crypto.d.ts +3 -0
  181. package/dist/src/servers/whatsapp-wire/crypto.js +23 -8
  182. package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
  183. package/dist/src/servers/whatsapp-wire/handshake.d.ts +9 -3
  184. package/dist/src/servers/whatsapp-wire/handshake.js +173 -12
  185. package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
  186. package/dist/src/servers/whatsapp-wire/signal.js +3 -3
  187. package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
  188. package/dist/src/servers/whatsapp.d.ts +7 -0
  189. package/dist/src/servers/whatsapp.js +149 -24
  190. package/dist/src/servers/whatsapp.js.map +1 -1
  191. package/dist/src/servers/zalo.d.ts +1 -0
  192. package/dist/src/servers/zalo.js +193 -82
  193. package/dist/src/servers/zalo.js.map +1 -1
  194. package/docs/channel-setup.md +185 -43
  195. package/fixtures/examples/crabline.example.yaml +3 -2
  196. package/package.json +18 -10
  197. package/fixtures/examples/openclaw-bridge.yaml +0 -125
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import path from "node:path";
3
2
  import { CrablineError } from "../../core/errors.js";
4
- import { LocalMockProviderAdapter } from "../local-mock.js";
3
+ import { LocalMockProviderAdapter, resolveGeneratedLocalMockRecorderPath } from "../local-mock.js";
5
4
  import { getBuiltinTargetCodec } from "../target-normalizers.js";
5
+ const LOOPBACK_V2_PREFIX = "loopback+v2:";
6
6
  function createMessageId() {
7
7
  return `loopback-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
8
8
  }
@@ -33,12 +33,45 @@ function toPostableText(message) {
33
33
  }
34
34
  return message.fallbackText ?? "[card]";
35
35
  }
36
+ function malformedThreadAddress(cause) {
37
+ return new CrablineError("Loopback v2 thread address is malformed.", {
38
+ ...(cause === undefined ? {} : { cause }),
39
+ kind: "inbound",
40
+ });
41
+ }
42
+ function encodeThreadAddressComponent(value) {
43
+ if (!value) {
44
+ throw malformedThreadAddress();
45
+ }
46
+ try {
47
+ return encodeURIComponent(value);
48
+ }
49
+ catch (error) {
50
+ throw malformedThreadAddress(error);
51
+ }
52
+ }
53
+ function decodeThreadAddressComponent(value) {
54
+ let decoded;
55
+ let canonical;
56
+ try {
57
+ decoded = decodeURIComponent(value);
58
+ canonical = encodeURIComponent(decoded);
59
+ }
60
+ catch (error) {
61
+ throw malformedThreadAddress(error);
62
+ }
63
+ if (!decoded || canonical !== value) {
64
+ throw malformedThreadAddress();
65
+ }
66
+ return decoded;
67
+ }
36
68
  export class LoopbackChatAdapter {
37
69
  name = "loopback";
38
70
  persistMessageHistory = true;
39
71
  userName;
40
72
  #messages = new Map();
41
73
  #nextSequence = new Map();
74
+ #lastMessageTimestamp = -1;
42
75
  constructor(userName) {
43
76
  this.userName = userName;
44
77
  }
@@ -46,34 +79,42 @@ export class LoopbackChatAdapter {
46
79
  return Promise.resolve();
47
80
  }
48
81
  channelIdFromThreadId(threadId) {
49
- const [address = threadId] = threadId.split("::");
50
- if (!address.startsWith("loopback+v2:")) {
51
- return address;
52
- }
53
- return this.decodeThreadId(threadId).channelId ?? address;
82
+ const address = this.decodeThreadId(threadId);
83
+ return address.channelId ?? address.id;
54
84
  }
55
85
  decodeThreadId(threadId) {
56
- const [address = threadId, rawThreadId] = threadId.split("::");
57
- const [platform, rawChannelOrId, rawId] = address.split(":");
58
- if (platform !== "loopback+v2" || !rawChannelOrId) {
59
- const [, channelId = address, id = address] = address.split(":");
60
- const decoded = { id };
61
- if (channelId) {
62
- decoded.channelId = channelId;
86
+ if (threadId.startsWith(LOOPBACK_V2_PREFIX)) {
87
+ const threadParts = threadId.split("::");
88
+ if (threadParts.length > 2) {
89
+ throw malformedThreadAddress();
90
+ }
91
+ const [address = "", rawThreadId] = threadParts;
92
+ const addressParts = address.slice(LOOPBACK_V2_PREFIX.length).split(":");
93
+ if ((addressParts.length !== 1 && addressParts.length !== 2) ||
94
+ addressParts.some((part) => part.length === 0) ||
95
+ rawThreadId === "") {
96
+ throw malformedThreadAddress();
97
+ }
98
+ const [rawChannelOrId = "", rawId] = addressParts;
99
+ const decoded = {
100
+ id: decodeThreadAddressComponent(rawId ?? rawChannelOrId),
101
+ };
102
+ if (rawId !== undefined) {
103
+ decoded.channelId = decodeThreadAddressComponent(rawChannelOrId);
63
104
  }
64
- if (rawThreadId) {
65
- decoded.threadId = rawThreadId;
105
+ if (rawThreadId !== undefined) {
106
+ decoded.threadId = decodeThreadAddressComponent(rawThreadId);
66
107
  }
67
108
  return decoded;
68
109
  }
69
- const decoded = {
70
- id: decodeURIComponent(rawId ?? rawChannelOrId),
71
- };
72
- if (rawId) {
73
- decoded.channelId = decodeURIComponent(rawChannelOrId);
110
+ const [address = threadId, rawThreadId] = threadId.split("::");
111
+ const [, channelId = address, id = address] = address.split(":");
112
+ const decoded = { id };
113
+ if (channelId) {
114
+ decoded.channelId = channelId;
74
115
  }
75
116
  if (rawThreadId) {
76
- decoded.threadId = decodeURIComponent(rawThreadId);
117
+ decoded.threadId = rawThreadId;
77
118
  }
78
119
  return decoded;
79
120
  }
@@ -93,17 +134,18 @@ export class LoopbackChatAdapter {
93
134
  existing.text = text;
94
135
  existing.formatted = text;
95
136
  existing.metadata.edited = true;
96
- existing.metadata.editedAt = new Date();
137
+ existing.metadata.editedAt = new Date(this.#nextMessageTimestamp());
97
138
  existing.raw.text = text;
98
139
  return Promise.resolve({ id: existing.id, raw: cloneRawMessage(existing.raw), threadId });
99
140
  }
100
141
  encodeThreadId(platformData) {
101
- const address = platformData.channelId
102
- ? `loopback+v2:${encodeURIComponent(platformData.channelId)}:${encodeURIComponent(platformData.id)}`
103
- : `loopback+v2:${encodeURIComponent(platformData.id)}`;
104
- return platformData.threadId
105
- ? `${address}::${encodeURIComponent(platformData.threadId)}`
106
- : address;
142
+ const id = encodeThreadAddressComponent(platformData.id);
143
+ const address = platformData.channelId === undefined
144
+ ? `${LOOPBACK_V2_PREFIX}${id}`
145
+ : `${LOOPBACK_V2_PREFIX}${encodeThreadAddressComponent(platformData.channelId)}:${id}`;
146
+ return platformData.threadId === undefined
147
+ ? address
148
+ : `${address}::${encodeThreadAddressComponent(platformData.threadId)}`;
107
149
  }
108
150
  fetchMessages(threadId, options) {
109
151
  const storedMessages = [...(this.#messages.get(threadId) ?? [])];
@@ -115,7 +157,7 @@ export class LoopbackChatAdapter {
115
157
  }
116
158
  const limit = options?.limit ?? storedMessages.length;
117
159
  let cursor;
118
- if (options?.cursor) {
160
+ if (options?.cursor !== undefined) {
119
161
  if (!/^[1-9]\d*$/u.test(options.cursor)) {
120
162
  throw new CrablineError("Loopback message cursor must be a positive safe integer.", {
121
163
  kind: "config",
@@ -139,10 +181,11 @@ export class LoopbackChatAdapter {
139
181
  return Promise.resolve(result);
140
182
  }
141
183
  fetchThread(threadId) {
184
+ const address = this.decodeThreadId(threadId);
142
185
  return Promise.resolve({
143
- channelId: this.channelIdFromThreadId(threadId),
186
+ channelId: address.channelId ?? address.id,
144
187
  id: threadId,
145
- isDM: true,
188
+ isDM: address.channelId === undefined,
146
189
  metadata: {},
147
190
  });
148
191
  }
@@ -176,7 +219,7 @@ export class LoopbackChatAdapter {
176
219
  id: createMessageId(),
177
220
  text,
178
221
  threadId,
179
- timestamp: new Date().toISOString(),
222
+ timestamp: this.#nextMessageTimestamp(),
180
223
  };
181
224
  const parsed = this.parseMessage(raw);
182
225
  this.#append(threadId, parsed);
@@ -197,7 +240,7 @@ export class LoopbackChatAdapter {
197
240
  id: createMessageId(),
198
241
  text,
199
242
  threadId,
200
- timestamp: new Date().toISOString(),
243
+ timestamp: this.#nextMessageTimestamp(),
201
244
  };
202
245
  const parsed = this.parseMessage(raw);
203
246
  this.#append(threadId, parsed);
@@ -205,6 +248,11 @@ export class LoopbackChatAdapter {
205
248
  }
206
249
  listSince(threadId, since) {
207
250
  const sinceTime = new Date(since).getTime();
251
+ if (!Number.isFinite(sinceTime)) {
252
+ throw new CrablineError("Loopback since timestamp must be a valid date.", {
253
+ kind: "config",
254
+ });
255
+ }
208
256
  return (this.#messages.get(threadId) ?? [])
209
257
  .map((entry) => entry.message)
210
258
  .filter((message) => message.metadata.dateSent.getTime() >= sinceTime)
@@ -217,6 +265,11 @@ export class LoopbackChatAdapter {
217
265
  this.#messages.set(threadId, bucket);
218
266
  this.#nextSequence.set(threadId, sequence);
219
267
  }
268
+ #nextMessageTimestamp() {
269
+ const timestamp = Math.max(Date.now(), this.#lastMessageTimestamp + 1);
270
+ this.#lastMessageTimestamp = timestamp;
271
+ return new Date(timestamp).toISOString();
272
+ }
220
273
  }
221
274
  export class LoopbackProviderAdapter extends LocalMockProviderAdapter {
222
275
  constructor(id, config, _userName) {
@@ -228,7 +281,7 @@ export class LoopbackProviderAdapter extends LocalMockProviderAdapter {
228
281
  defaultWebhook: { host: "127.0.0.1", path: "/loopback/webhook", port: 0 },
229
282
  endpointLabel: "webhook endpoint",
230
283
  platform: "loopback",
231
- recorderPath: path.resolve(".crabline", "recorders", `${id}-${randomUUID()}.jsonl`),
284
+ recorderPath: resolveGeneratedLocalMockRecorderPath(id, `-${randomUUID()}`),
232
285
  },
233
286
  });
234
287
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../../../src/providers/builtin/loopback.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { LocalMockProviderAdapter } from \"../local-mock.js\";\nimport { getBuiltinTargetCodec } from \"../target-normalizers.js\";\nimport type { LoopbackMessage, LoopbackRawMessage, ProviderAdapter } from \"../types.js\";\n\ntype ThreadAddress = {\n channelId?: string | undefined;\n id: string;\n threadId?: string | undefined;\n};\n\ntype PostableMessage =\n | string\n | { card: unknown; fallbackText?: string }\n | { markdown: string }\n | { raw: string };\n\ntype StoredLoopbackMessage = {\n message: LoopbackMessage;\n sequence: number;\n};\n\nfunction createMessageId(): string {\n return `loopback-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction cloneRawMessage(raw: LoopbackRawMessage): LoopbackRawMessage {\n return { ...raw };\n}\n\nfunction cloneMessage(message: LoopbackMessage): LoopbackMessage {\n return {\n ...message,\n author: { ...message.author },\n metadata: {\n dateSent: new Date(message.metadata.dateSent),\n edited: message.metadata.edited,\n ...(message.metadata.editedAt ? { editedAt: new Date(message.metadata.editedAt) } : {}),\n },\n raw: cloneRawMessage(message.raw),\n };\n}\n\nfunction toPostableText(message: PostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return message.markdown;\n }\n return message.fallbackText ?? \"[card]\";\n}\n\nexport class LoopbackChatAdapter {\n readonly name = \"loopback\";\n readonly persistMessageHistory = true;\n readonly userName;\n\n readonly #messages = new Map<string, StoredLoopbackMessage[]>();\n readonly #nextSequence = new Map<string, number>();\n\n constructor(userName: string) {\n this.userName = userName;\n }\n\n addReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n channelIdFromThreadId(threadId: string): string {\n const [address = threadId] = threadId.split(\"::\");\n if (!address.startsWith(\"loopback+v2:\")) {\n return address;\n }\n return this.decodeThreadId(threadId).channelId ?? address;\n }\n\n decodeThreadId(threadId: string): ThreadAddress {\n const [address = threadId, rawThreadId] = threadId.split(\"::\");\n const [platform, rawChannelOrId, rawId] = address.split(\":\");\n if (platform !== \"loopback+v2\" || !rawChannelOrId) {\n const [, channelId = address, id = address] = address.split(\":\");\n const decoded: ThreadAddress = { id };\n if (channelId) {\n decoded.channelId = channelId;\n }\n if (rawThreadId) {\n decoded.threadId = rawThreadId;\n }\n return decoded;\n }\n\n const decoded: ThreadAddress = {\n id: decodeURIComponent(rawId ?? rawChannelOrId),\n };\n if (rawId) {\n decoded.channelId = decodeURIComponent(rawChannelOrId);\n }\n if (rawThreadId) {\n decoded.threadId = decodeURIComponent(rawThreadId);\n }\n return decoded;\n }\n\n deleteMessage(threadId: string, messageId: string): Promise<void> {\n const messages = this.#messages.get(threadId) ?? [];\n this.#messages.set(\n threadId,\n messages.filter((entry) => entry.message.id !== messageId),\n );\n return Promise.resolve();\n }\n\n editMessage(\n threadId: string,\n messageId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const messages = this.#messages.get(threadId) ?? [];\n const stored = messages.find((entry) => entry.message.id === messageId);\n if (!stored) {\n throw new CrablineError(`Loopback message not found: ${messageId}`, { kind: \"inbound\" });\n }\n\n const existing = stored.message;\n const text = toPostableText(message);\n existing.text = text;\n existing.formatted = text;\n existing.metadata.edited = true;\n existing.metadata.editedAt = new Date();\n existing.raw.text = text;\n return Promise.resolve({ id: existing.id, raw: cloneRawMessage(existing.raw), threadId });\n }\n\n encodeThreadId(platformData: ThreadAddress): string {\n const address = platformData.channelId\n ? `loopback+v2:${encodeURIComponent(platformData.channelId)}:${encodeURIComponent(platformData.id)}`\n : `loopback+v2:${encodeURIComponent(platformData.id)}`;\n return platformData.threadId\n ? `${address}::${encodeURIComponent(platformData.threadId)}`\n : address;\n }\n\n fetchMessages(\n threadId: string,\n options?: { cursor?: string; limit?: number },\n ): Promise<{ messages: LoopbackMessage[]; nextCursor?: string }> {\n const storedMessages = [...(this.#messages.get(threadId) ?? [])];\n if (\n options?.limit !== undefined &&\n (!Number.isSafeInteger(options.limit) || options.limit <= 0)\n ) {\n throw new CrablineError(\"Loopback message limit must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n const limit = options?.limit ?? storedMessages.length;\n let cursor: number | undefined;\n if (options?.cursor) {\n if (!/^[1-9]\\d*$/u.test(options.cursor)) {\n throw new CrablineError(\"Loopback message cursor must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n cursor = Number(options.cursor);\n if (!Number.isSafeInteger(cursor) || cursor > (this.#nextSequence.get(threadId) ?? 0)) {\n throw new CrablineError(\n \"Loopback message cursor must be a positive safe integer within message history.\",\n { kind: \"config\" },\n );\n }\n }\n\n const eligibleMessages =\n cursor === undefined\n ? storedMessages\n : storedMessages.filter((entry) => entry.sequence < cursor);\n const page = eligibleMessages.slice(-limit);\n const result: { messages: LoopbackMessage[]; nextCursor?: string } = {\n messages: page.map((entry) => cloneMessage(entry.message)),\n };\n if (eligibleMessages.length > page.length && page[0]) {\n result.nextCursor = String(page[0].sequence);\n }\n return Promise.resolve(result);\n }\n\n fetchThread(threadId: string) {\n return Promise.resolve({\n channelId: this.channelIdFromThreadId(threadId),\n id: threadId,\n isDM: true,\n metadata: {},\n });\n }\n\n handleWebhook(_request?: Request): Promise<Response> {\n return Promise.resolve(\n new Response(\"loopback adapter has no webhook surface\", { status: 501 }),\n );\n }\n\n isDM(): boolean {\n return true;\n }\n\n parseMessage(raw: LoopbackRawMessage): LoopbackMessage {\n return {\n author: {\n isMe: raw.author === \"assistant\",\n userName: raw.author === \"assistant\" ? this.userName : \"loopback\",\n },\n formatted: raw.text,\n id: raw.id,\n metadata: {\n dateSent: new Date(raw.timestamp),\n edited: false,\n },\n raw: cloneRawMessage(raw),\n text: raw.text,\n threadId: raw.threadId,\n };\n }\n\n postMessage(\n threadId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const text = toPostableText(message);\n const raw = {\n author: \"assistant\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: new Date().toISOString(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return Promise.resolve({ id: raw.id, raw: cloneRawMessage(raw), threadId });\n }\n\n removeReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n renderFormatted(content: string): string {\n return content;\n }\n\n startTyping(): Promise<void> {\n return Promise.resolve();\n }\n\n ingestUserMessage(threadId: string, text: string): LoopbackMessage {\n const raw = {\n author: \"user\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: new Date().toISOString(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return cloneMessage(parsed);\n }\n\n listSince(threadId: string, since: string): LoopbackMessage[] {\n const sinceTime = new Date(since).getTime();\n return (this.#messages.get(threadId) ?? [])\n .map((entry) => entry.message)\n .filter((message) => message.metadata.dateSent.getTime() >= sinceTime)\n .map(cloneMessage);\n }\n\n #append(threadId: string, message: LoopbackMessage): void {\n const bucket = this.#messages.get(threadId) ?? [];\n const sequence = (this.#nextSequence.get(threadId) ?? 0) + 1;\n bucket.push({ message: cloneMessage(message), sequence });\n this.#messages.set(threadId, bucket);\n this.#nextSequence.set(threadId, sequence);\n }\n}\n\nexport class LoopbackProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string) {\n super({\n codec: getBuiltinTargetCodec(\"loopback\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/loopback/webhook\", port: 0 },\n endpointLabel: \"webhook endpoint\",\n platform: \"loopback\",\n recorderPath: path.resolve(\".crabline\", \"recorders\", `${id}-${randomUUID()}.jsonl`),\n },\n });\n }\n}\n"]}
1
+ {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../../../src/providers/builtin/loopback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,wBAAwB,EAAE,qCAAqC,EAAE,MAAM,kBAAkB,CAAC;AACnG,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAoBjE,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C,SAAS,eAAe;IACtB,OAAO,iBAAiB,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED,SAAS,eAAe,CAAC,GAAuB;IAC9C,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC;AACpB,CAAC;AAED,SAAS,YAAY,CAAC,OAAwB;IAC5C,OAAO;QACL,GAAG,OAAO;QACV,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE;QAC7B,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC7C,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;YAC/B,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxF;QACD,GAAG,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,OAAwB;IAC9C,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC,GAAG,CAAC;IACrB,CAAC;IACD,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,QAAQ,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC,YAAY,IAAI,QAAQ,CAAC;AAC1C,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAe;IAC7C,OAAO,IAAI,aAAa,CAAC,0CAA0C,EAAE;QACnE,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,IAAI,EAAE,SAAS;KAChB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,4BAA4B,CAAC,KAAa;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,sBAAsB,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,4BAA4B,CAAC,KAAa;IACjD,IAAI,OAAe,CAAC;IACpB,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACpC,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,OAAO,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;QACpC,MAAM,sBAAsB,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,mBAAmB;IACrB,IAAI,GAAG,UAAU,CAAC;IAClB,qBAAqB,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC;IAET,SAAS,GAAG,IAAI,GAAG,EAAmC,CAAC;IACvD,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnD,qBAAqB,GAAG,CAAC,CAAC,CAAC;IAE3B,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,WAAW;QACT,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,qBAAqB,CAAC,QAAgB;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC9C,OAAO,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,IAAI,QAAQ,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC5C,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,sBAAsB,EAAE,CAAC;YACjC,CAAC;YACD,MAAM,CAAC,OAAO,GAAG,EAAE,EAAE,WAAW,CAAC,GAAG,WAAW,CAAC;YAChD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzE,IACE,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC;gBACxD,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC;gBAC9C,WAAW,KAAK,EAAE,EAClB,CAAC;gBACD,MAAM,sBAAsB,EAAE,CAAC;YACjC,CAAC;YACD,MAAM,CAAC,cAAc,GAAG,EAAE,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC;YAClD,MAAM,OAAO,GAAkB;gBAC7B,EAAE,EAAE,4BAA4B,CAAC,KAAK,IAAI,cAAc,CAAC;aAC1D,CAAC;YACF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,CAAC,SAAS,GAAG,4BAA4B,CAAC,cAAc,CAAC,CAAC;YACnE,CAAC;YACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC9B,OAAO,CAAC,QAAQ,GAAG,4BAA4B,CAAC,WAAW,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,CAAC,OAAO,GAAG,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,OAAO,GAAkB,EAAE,EAAE,EAAE,CAAC;QACtC,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;QAChC,CAAC;QACD,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC;QACjC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,aAAa,CAAC,QAAgB,EAAE,SAAiB;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,SAAS,CAAC,GAAG,CAChB,QAAQ,EACR,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,CAAC,CAC3D,CAAC;QACF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,WAAW,CACT,QAAgB,EAChB,SAAiB,EACjB,OAAwB;QAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QACxE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,aAAa,CAAC,+BAA+B,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;QAChC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;QAC1B,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC;QAChC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;QACpE,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QACzB,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,cAAc,CAAC,YAA2B;QACxC,MAAM,EAAE,GAAG,4BAA4B,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACzD,MAAM,OAAO,GACX,YAAY,CAAC,SAAS,KAAK,SAAS;YAClC,CAAC,CAAC,GAAG,kBAAkB,GAAG,EAAE,EAAE;YAC9B,CAAC,CAAC,GAAG,kBAAkB,GAAG,4BAA4B,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3F,OAAO,YAAY,CAAC,QAAQ,KAAK,SAAS;YACxC,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,GAAG,OAAO,KAAK,4BAA4B,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,aAAa,CACX,QAAgB,EAChB,OAA6C;QAE7C,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACjE,IACE,OAAO,EAAE,KAAK,KAAK,SAAS;YAC5B,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,EAC5D,CAAC;YACD,MAAM,IAAI,aAAa,CAAC,yDAAyD,EAAE;gBACjF,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;QACL,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,cAAc,CAAC,MAAM,CAAC;QACtD,IAAI,MAA0B,CAAC;QAC/B,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxC,MAAM,IAAI,aAAa,CAAC,0DAA0D,EAAE;oBAClF,IAAI,EAAE,QAAQ;iBACf,CAAC,CAAC;YACL,CAAC;YACD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACtF,MAAM,IAAI,aAAa,CACrB,iFAAiF,EACjF,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,gBAAgB,GACpB,MAAM,KAAK,SAAS;YAClB,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAyD;YACnE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;SAC3D,CAAC;QACF,IAAI,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,WAAW,CAAC,QAAgB;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC9C,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,EAAE;YAC1C,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,SAAS;YACrC,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IAED,aAAa,CAAC,QAAkB;QAC9B,OAAO,OAAO,CAAC,OAAO,CACpB,IAAI,QAAQ,CAAC,yCAAyC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CACzE,CAAC;IACJ,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,CAAC,GAAuB;QAClC,OAAO;YACL,MAAM,EAAE;gBACN,IAAI,EAAE,GAAG,CAAC,MAAM,KAAK,WAAW;gBAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU;aAClE;YACD,SAAS,EAAE,GAAG,CAAC,IAAI;YACnB,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,QAAQ,EAAE;gBACR,QAAQ,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;gBACjC,MAAM,EAAE,KAAK;aACd;YACD,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC;YACzB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,WAAW,CACT,QAAgB,EAChB,OAAwB;QAExB,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG;YACV,MAAM,EAAE,WAAW;YACnB,EAAE,EAAE,eAAe,EAAE;YACrB,IAAI;YACJ,QAAQ;YACR,SAAS,EAAE,IAAI,CAAC,qBAAqB,EAAE;SACX,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,cAAc;QACZ,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,eAAe,CAAC,OAAe;QAC7B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,WAAW;QACT,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,iBAAiB,CAAC,QAAgB,EAAE,IAAY;QAC9C,MAAM,GAAG,GAAG;YACV,MAAM,EAAE,MAAM;YACd,EAAE,EAAE,eAAe,EAAE;YACrB,IAAI;YACJ,QAAQ;YACR,SAAS,EAAE,IAAI,CAAC,qBAAqB,EAAE;SACX,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,SAAS,CAAC,QAAgB,EAAE,KAAa;QACvC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,aAAa,CAAC,gDAAgD,EAAE;gBACxE,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;QACL,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;aACxC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;aAC7B,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC;aACrE,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,OAAwB;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED,qBAAqB;QACnB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,qBAAqB,GAAG,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;QACvC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3C,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,wBAAwB;IACnE,YAAY,EAAU,EAAE,MAAsB,EAAE,SAAiB;QAC/D,KAAK,CAAC;YACJ,KAAK,EAAE,qBAAqB,CAAC,UAAU,CAAC;YACxC,MAAM;YACN,EAAE;YACF,OAAO,EAAE;gBACP,cAAc,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,EAAE;gBACzE,aAAa,EAAE,kBAAkB;gBACjC,QAAQ,EAAE,UAAU;gBACpB,YAAY,EAAE,qCAAqC,CAAC,EAAE,EAAE,IAAI,UAAU,EAAE,EAAE,CAAC;aAC5E;SACF,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { LocalMockProviderAdapter, resolveGeneratedLocalMockRecorderPath } from \"../local-mock.js\";\nimport { getBuiltinTargetCodec } from \"../target-normalizers.js\";\nimport type { LoopbackMessage, LoopbackRawMessage, ProviderAdapter } from \"../types.js\";\n\ntype ThreadAddress = {\n channelId?: string | undefined;\n id: string;\n threadId?: string | undefined;\n};\n\ntype PostableMessage =\n | string\n | { card: unknown; fallbackText?: string }\n | { markdown: string }\n | { raw: string };\n\ntype StoredLoopbackMessage = {\n message: LoopbackMessage;\n sequence: number;\n};\n\nconst LOOPBACK_V2_PREFIX = \"loopback+v2:\";\n\nfunction createMessageId(): string {\n return `loopback-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction cloneRawMessage(raw: LoopbackRawMessage): LoopbackRawMessage {\n return { ...raw };\n}\n\nfunction cloneMessage(message: LoopbackMessage): LoopbackMessage {\n return {\n ...message,\n author: { ...message.author },\n metadata: {\n dateSent: new Date(message.metadata.dateSent),\n edited: message.metadata.edited,\n ...(message.metadata.editedAt ? { editedAt: new Date(message.metadata.editedAt) } : {}),\n },\n raw: cloneRawMessage(message.raw),\n };\n}\n\nfunction toPostableText(message: PostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return message.markdown;\n }\n return message.fallbackText ?? \"[card]\";\n}\n\nfunction malformedThreadAddress(cause?: unknown): CrablineError {\n return new CrablineError(\"Loopback v2 thread address is malformed.\", {\n ...(cause === undefined ? {} : { cause }),\n kind: \"inbound\",\n });\n}\n\nfunction encodeThreadAddressComponent(value: string): string {\n if (!value) {\n throw malformedThreadAddress();\n }\n try {\n return encodeURIComponent(value);\n } catch (error) {\n throw malformedThreadAddress(error);\n }\n}\n\nfunction decodeThreadAddressComponent(value: string): string {\n let decoded: string;\n let canonical: string;\n try {\n decoded = decodeURIComponent(value);\n canonical = encodeURIComponent(decoded);\n } catch (error) {\n throw malformedThreadAddress(error);\n }\n if (!decoded || canonical !== value) {\n throw malformedThreadAddress();\n }\n return decoded;\n}\n\nexport class LoopbackChatAdapter {\n readonly name = \"loopback\";\n readonly persistMessageHistory = true;\n readonly userName;\n\n readonly #messages = new Map<string, StoredLoopbackMessage[]>();\n readonly #nextSequence = new Map<string, number>();\n #lastMessageTimestamp = -1;\n\n constructor(userName: string) {\n this.userName = userName;\n }\n\n addReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n channelIdFromThreadId(threadId: string): string {\n const address = this.decodeThreadId(threadId);\n return address.channelId ?? address.id;\n }\n\n decodeThreadId(threadId: string): ThreadAddress {\n if (threadId.startsWith(LOOPBACK_V2_PREFIX)) {\n const threadParts = threadId.split(\"::\");\n if (threadParts.length > 2) {\n throw malformedThreadAddress();\n }\n const [address = \"\", rawThreadId] = threadParts;\n const addressParts = address.slice(LOOPBACK_V2_PREFIX.length).split(\":\");\n if (\n (addressParts.length !== 1 && addressParts.length !== 2) ||\n addressParts.some((part) => part.length === 0) ||\n rawThreadId === \"\"\n ) {\n throw malformedThreadAddress();\n }\n const [rawChannelOrId = \"\", rawId] = addressParts;\n const decoded: ThreadAddress = {\n id: decodeThreadAddressComponent(rawId ?? rawChannelOrId),\n };\n if (rawId !== undefined) {\n decoded.channelId = decodeThreadAddressComponent(rawChannelOrId);\n }\n if (rawThreadId !== undefined) {\n decoded.threadId = decodeThreadAddressComponent(rawThreadId);\n }\n return decoded;\n }\n\n const [address = threadId, rawThreadId] = threadId.split(\"::\");\n const [, channelId = address, id = address] = address.split(\":\");\n const decoded: ThreadAddress = { id };\n if (channelId) {\n decoded.channelId = channelId;\n }\n if (rawThreadId) {\n decoded.threadId = rawThreadId;\n }\n return decoded;\n }\n\n deleteMessage(threadId: string, messageId: string): Promise<void> {\n const messages = this.#messages.get(threadId) ?? [];\n this.#messages.set(\n threadId,\n messages.filter((entry) => entry.message.id !== messageId),\n );\n return Promise.resolve();\n }\n\n editMessage(\n threadId: string,\n messageId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const messages = this.#messages.get(threadId) ?? [];\n const stored = messages.find((entry) => entry.message.id === messageId);\n if (!stored) {\n throw new CrablineError(`Loopback message not found: ${messageId}`, { kind: \"inbound\" });\n }\n\n const existing = stored.message;\n const text = toPostableText(message);\n existing.text = text;\n existing.formatted = text;\n existing.metadata.edited = true;\n existing.metadata.editedAt = new Date(this.#nextMessageTimestamp());\n existing.raw.text = text;\n return Promise.resolve({ id: existing.id, raw: cloneRawMessage(existing.raw), threadId });\n }\n\n encodeThreadId(platformData: ThreadAddress): string {\n const id = encodeThreadAddressComponent(platformData.id);\n const address =\n platformData.channelId === undefined\n ? `${LOOPBACK_V2_PREFIX}${id}`\n : `${LOOPBACK_V2_PREFIX}${encodeThreadAddressComponent(platformData.channelId)}:${id}`;\n return platformData.threadId === undefined\n ? address\n : `${address}::${encodeThreadAddressComponent(platformData.threadId)}`;\n }\n\n fetchMessages(\n threadId: string,\n options?: { cursor?: string; limit?: number },\n ): Promise<{ messages: LoopbackMessage[]; nextCursor?: string }> {\n const storedMessages = [...(this.#messages.get(threadId) ?? [])];\n if (\n options?.limit !== undefined &&\n (!Number.isSafeInteger(options.limit) || options.limit <= 0)\n ) {\n throw new CrablineError(\"Loopback message limit must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n const limit = options?.limit ?? storedMessages.length;\n let cursor: number | undefined;\n if (options?.cursor !== undefined) {\n if (!/^[1-9]\\d*$/u.test(options.cursor)) {\n throw new CrablineError(\"Loopback message cursor must be a positive safe integer.\", {\n kind: \"config\",\n });\n }\n cursor = Number(options.cursor);\n if (!Number.isSafeInteger(cursor) || cursor > (this.#nextSequence.get(threadId) ?? 0)) {\n throw new CrablineError(\n \"Loopback message cursor must be a positive safe integer within message history.\",\n { kind: \"config\" },\n );\n }\n }\n\n const eligibleMessages =\n cursor === undefined\n ? storedMessages\n : storedMessages.filter((entry) => entry.sequence < cursor);\n const page = eligibleMessages.slice(-limit);\n const result: { messages: LoopbackMessage[]; nextCursor?: string } = {\n messages: page.map((entry) => cloneMessage(entry.message)),\n };\n if (eligibleMessages.length > page.length && page[0]) {\n result.nextCursor = String(page[0].sequence);\n }\n return Promise.resolve(result);\n }\n\n fetchThread(threadId: string) {\n const address = this.decodeThreadId(threadId);\n return Promise.resolve({\n channelId: address.channelId ?? address.id,\n id: threadId,\n isDM: address.channelId === undefined,\n metadata: {},\n });\n }\n\n handleWebhook(_request?: Request): Promise<Response> {\n return Promise.resolve(\n new Response(\"loopback adapter has no webhook surface\", { status: 501 }),\n );\n }\n\n isDM(): boolean {\n return true;\n }\n\n parseMessage(raw: LoopbackRawMessage): LoopbackMessage {\n return {\n author: {\n isMe: raw.author === \"assistant\",\n userName: raw.author === \"assistant\" ? this.userName : \"loopback\",\n },\n formatted: raw.text,\n id: raw.id,\n metadata: {\n dateSent: new Date(raw.timestamp),\n edited: false,\n },\n raw: cloneRawMessage(raw),\n text: raw.text,\n threadId: raw.threadId,\n };\n }\n\n postMessage(\n threadId: string,\n message: PostableMessage,\n ): Promise<{ id: string; raw: LoopbackRawMessage; threadId: string }> {\n const text = toPostableText(message);\n const raw = {\n author: \"assistant\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: this.#nextMessageTimestamp(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return Promise.resolve({ id: raw.id, raw: cloneRawMessage(raw), threadId });\n }\n\n removeReaction(): Promise<void> {\n return Promise.resolve();\n }\n\n renderFormatted(content: string): string {\n return content;\n }\n\n startTyping(): Promise<void> {\n return Promise.resolve();\n }\n\n ingestUserMessage(threadId: string, text: string): LoopbackMessage {\n const raw = {\n author: \"user\",\n id: createMessageId(),\n text,\n threadId,\n timestamp: this.#nextMessageTimestamp(),\n } satisfies LoopbackRawMessage;\n const parsed = this.parseMessage(raw);\n this.#append(threadId, parsed);\n return cloneMessage(parsed);\n }\n\n listSince(threadId: string, since: string): LoopbackMessage[] {\n const sinceTime = new Date(since).getTime();\n if (!Number.isFinite(sinceTime)) {\n throw new CrablineError(\"Loopback since timestamp must be a valid date.\", {\n kind: \"config\",\n });\n }\n return (this.#messages.get(threadId) ?? [])\n .map((entry) => entry.message)\n .filter((message) => message.metadata.dateSent.getTime() >= sinceTime)\n .map(cloneMessage);\n }\n\n #append(threadId: string, message: LoopbackMessage): void {\n const bucket = this.#messages.get(threadId) ?? [];\n const sequence = (this.#nextSequence.get(threadId) ?? 0) + 1;\n bucket.push({ message: cloneMessage(message), sequence });\n this.#messages.set(threadId, bucket);\n this.#nextSequence.set(threadId, sequence);\n }\n\n #nextMessageTimestamp(): string {\n const timestamp = Math.max(Date.now(), this.#lastMessageTimestamp + 1);\n this.#lastMessageTimestamp = timestamp;\n return new Date(timestamp).toISOString();\n }\n}\n\nexport class LoopbackProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, _userName: string) {\n super({\n codec: getBuiltinTargetCodec(\"loopback\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/loopback/webhook\", port: 0 },\n endpointLabel: \"webhook endpoint\",\n platform: \"loopback\",\n recorderPath: resolveGeneratedLocalMockRecorderPath(id, `-${randomUUID()}`),\n },\n });\n }\n}\n"]}
@@ -5,12 +5,12 @@ export declare function resolveMatrixAdapterConfig(config: ProviderConfig, userN
5
5
  auth: {
6
6
  accessToken: string;
7
7
  type: "accessToken";
8
- userID?: string | undefined;
8
+ userID: string;
9
9
  } | {
10
10
  password: string;
11
11
  type: "password";
12
- userID?: string | undefined;
13
12
  username: string;
13
+ userID: string;
14
14
  };
15
15
  baseURL: string;
16
16
  commandPrefix: string | undefined;
@@ -1,15 +1,28 @@
1
1
  import path from "node:path";
2
2
  import { CrablineError } from "../../core/errors.js";
3
+ import { isMatrixEventId, isMatrixRoomId, isMatrixUserId } from "../../matrix-ids.js";
3
4
  import { LocalMockProviderAdapter } from "../local-mock.js";
4
5
  import { getBuiltinTargetCodec, MATRIX_EVENT_ID_RULE, MATRIX_ROOM_ID_RULE, } from "../target-normalizers.js";
5
- import { authorFromBotFlag, genericMockPayloadWithNativeThread, isRecord, optionalRecord, optionalString, requireNativeInboundId, } from "./native-local-mock.js";
6
+ import { authorFromBotFlag, genericMockPayloadWithNativeThread, isRecord, optionalRecord, optionalString, } from "./native-local-mock.js";
6
7
  import { requireExternalWebhookAuthentication } from "./external-webhook-auth.js";
7
8
  export function resolveMatrixAdapterConfig(config, userName, env = process.env) {
9
+ const fallbackUserId = env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`;
10
+ const configuredAuth = config.matrix?.auth
11
+ ? {
12
+ ...config.matrix.auth,
13
+ userID: config.matrix.auth.userID?.trim() || fallbackUserId,
14
+ }
15
+ : undefined;
16
+ if (!isMatrixUserId(configuredAuth?.userID ?? fallbackUserId)) {
17
+ throw new CrablineError("Matrix auth.userID must be a canonical Matrix user ID.", {
18
+ kind: "config",
19
+ });
20
+ }
8
21
  return {
9
- auth: config.matrix?.auth ?? {
22
+ auth: configuredAuth ?? {
10
23
  accessToken: env.MATRIX_ACCESS_TOKEN ?? "local-mock-matrix-token",
11
24
  type: "accessToken",
12
- userID: env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`,
25
+ userID: fallbackUserId,
13
26
  },
14
27
  baseURL: config.matrix?.baseURL ?? env.MATRIX_BASE_URL ?? "http://matrix.local",
15
28
  commandPrefix: config.matrix?.commandPrefix,
@@ -52,40 +65,72 @@ export function matchesMatrixThread(candidateThreadId, expectedThreadId, target)
52
65
  if (!expectedThreadId) {
53
66
  return true;
54
67
  }
55
- const scopedExpected = target.channelId && MATRIX_EVENT_ID_RULE.pattern.test(expectedThreadId)
56
- ? matrixThreadKey(target.channelId, expectedThreadId)
57
- : expectedThreadId;
68
+ if (target.channelId && isMatrixEventId(expectedThreadId)) {
69
+ return candidateThreadId === matrixThreadKey(target.channelId, expectedThreadId);
70
+ }
58
71
  return (candidateThreadId === expectedThreadId ||
59
- candidateThreadId === scopedExpected ||
60
- (MATRIX_ROOM_ID_RULE.pattern.test(scopedExpected) &&
61
- candidateThreadId.startsWith(`${scopedExpected}:thread:`)));
72
+ (isMatrixRoomId(expectedThreadId) &&
73
+ candidateThreadId.startsWith(`${expectedThreadId}:thread:`)));
62
74
  }
63
75
  export function normalizeMatrixWebhookPayload(payload, botUserId) {
64
76
  if (!isRecord(payload)) {
65
77
  throw new CrablineError("Matrix webhook payload must be an object", { kind: "inbound" });
66
78
  }
67
- if (optionalRecord(payload, "message")) {
68
- return genericMockPayloadWithNativeThread({
79
+ const message = optionalRecord(payload, "message");
80
+ if ((message && ("text" in message || "threadId" in message)) ||
81
+ "text" in payload ||
82
+ "threadId" in payload) {
83
+ const normalized = genericMockPayloadWithNativeThread({
69
84
  channelRule: MATRIX_ROOM_ID_RULE,
70
85
  payload,
71
86
  threadRule: MATRIX_EVENT_ID_RULE,
72
87
  });
88
+ const threadId = (message ? optionalString(message, "threadId") : undefined) ??
89
+ optionalString(payload, "threadId");
90
+ const roomId = (message ? optionalString(message, "channelId") : undefined) ??
91
+ optionalString(payload, "channelId") ??
92
+ optionalString(payload, "roomId") ??
93
+ optionalString(payload, "room_id");
94
+ if ("threadId" in normalized &&
95
+ !isMatrixRoomId(normalized.threadId) &&
96
+ !isMatrixEventId(normalized.threadId)) {
97
+ throw new CrablineError(`mock webhook threadId must be a native ${MATRIX_EVENT_ID_RULE.name} or ${MATRIX_ROOM_ID_RULE.name}.`, { kind: "inbound" });
98
+ }
99
+ if (threadId && isMatrixEventId(threadId)) {
100
+ if (!roomId || !isMatrixRoomId(roomId)) {
101
+ throw new CrablineError(`Matrix generic event threadId requires a native ${MATRIX_ROOM_ID_RULE.name} channelId.`, { kind: "inbound" });
102
+ }
103
+ const scopedThreadId = matrixThreadKey(roomId, threadId);
104
+ return {
105
+ ...normalized,
106
+ ...("message" in normalized && isRecord(normalized.message)
107
+ ? { message: { ...normalized.message, threadId: scopedThreadId } }
108
+ : {}),
109
+ threadId: scopedThreadId,
110
+ };
111
+ }
112
+ if (threadId && isMatrixRoomId(threadId) && roomId && roomId !== threadId) {
113
+ throw new CrablineError("Matrix generic room threadId must match channelId.", {
114
+ kind: "inbound",
115
+ });
116
+ }
117
+ return normalized;
73
118
  }
74
119
  const content = optionalRecord(payload, "content");
75
120
  const roomId = optionalString(payload, "room_id");
76
121
  const eventId = optionalString(payload, "event_id");
77
122
  const eventType = optionalString(payload, "type");
78
- const senderId = optionalString(payload, "sender");
79
- const text = content ? optionalString(content, "body") : undefined;
123
+ const sender = payload.sender;
124
+ const senderId = typeof sender === "string" ? sender : undefined;
125
+ const msgtype = content?.msgtype;
126
+ const text = content?.body;
80
127
  if (eventType !== "m.room.message") {
81
128
  throw new CrablineError("Matrix event payload requires type=m.room.message", {
82
129
  kind: "inbound",
83
130
  });
84
131
  }
85
- if (!roomId || !eventId || !text) {
86
- throw new CrablineError("Matrix event payload requires room_id, event_id, and content.body", {
87
- kind: "inbound",
88
- });
132
+ if (!roomId || !eventId || typeof msgtype !== "string" || typeof text !== "string") {
133
+ throw new CrablineError("Matrix event payload requires room_id, event_id, string content.msgtype, and string content.body", { kind: "inbound" });
89
134
  }
90
135
  const relation = content ? optionalRecord(content, "m.relates_to") : undefined;
91
136
  const isThread = relation && optionalString(relation, "rel_type") === "m.thread";
@@ -95,14 +140,26 @@ export function normalizeMatrixWebhookPayload(payload, botUserId) {
95
140
  kind: "inbound",
96
141
  });
97
142
  }
143
+ if (!isMatrixRoomId(roomId)) {
144
+ throw new CrablineError(`Matrix room_id must be a native ${MATRIX_ROOM_ID_RULE.name} such as ${MATRIX_ROOM_ID_RULE.example}.`, { kind: "inbound" });
145
+ }
146
+ if (!isMatrixEventId(eventId)) {
147
+ throw new CrablineError(`Matrix event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`, { kind: "inbound" });
148
+ }
149
+ if (threadRootId && !isMatrixEventId(threadRootId)) {
150
+ throw new CrablineError(`Matrix thread root event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`, { kind: "inbound" });
151
+ }
152
+ if (!senderId || !isMatrixUserId(senderId)) {
153
+ throw new CrablineError("Matrix sender must be a canonical Matrix user ID.", {
154
+ kind: "inbound",
155
+ });
156
+ }
98
157
  return {
99
158
  author: authorFromBotFlag(senderId !== undefined && senderId === botUserId),
100
- id: requireNativeInboundId(eventId, MATRIX_EVENT_ID_RULE, "Matrix event_id"),
159
+ id: eventId,
101
160
  raw: payload,
102
161
  text,
103
- threadId: threadRootId
104
- ? matrixThreadKey(requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, "Matrix room_id"), requireNativeInboundId(threadRootId, MATRIX_EVENT_ID_RULE, "Matrix thread root event_id"))
105
- : requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, "Matrix room_id"),
162
+ threadId: threadRootId ? matrixThreadKey(roomId, threadRootId) : roomId,
106
163
  };
107
164
  }
108
165
  //# sourceMappingURL=matrix.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../../../src/providers/builtin/matrix.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { LocalMockProviderAdapter } from \"../local-mock.js\";\nimport type { ProviderAdapter } from \"../types.js\";\nimport {\n getBuiltinTargetCodec,\n MATRIX_EVENT_ID_RULE,\n MATRIX_ROOM_ID_RULE,\n} from \"../target-normalizers.js\";\nimport {\n authorFromBotFlag,\n genericMockPayloadWithNativeThread,\n isRecord,\n optionalRecord,\n optionalString,\n requireNativeInboundId,\n} from \"./native-local-mock.js\";\nimport { requireExternalWebhookAuthentication } from \"./external-webhook-auth.js\";\n\nexport function resolveMatrixAdapterConfig(\n config: ProviderConfig,\n userName: string,\n env: NodeJS.ProcessEnv = process.env,\n) {\n return {\n auth: config.matrix?.auth ?? {\n accessToken: env.MATRIX_ACCESS_TOKEN ?? \"local-mock-matrix-token\",\n type: \"accessToken\" as const,\n userID: env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`,\n },\n baseURL: config.matrix?.baseURL ?? env.MATRIX_BASE_URL ?? \"http://matrix.local\",\n commandPrefix: config.matrix?.commandPrefix,\n recoveryKey: config.matrix?.recoveryKey ?? env.MATRIX_RECOVERY_KEY,\n };\n}\n\nexport class MatrixProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, userName: string, _runtime?: unknown) {\n const resolvedConfig = resolveMatrixAdapterConfig(config, userName);\n const botUserId = resolvedConfig.auth.userID;\n requireExternalWebhookAuthentication({\n authenticated: false,\n provider: \"Matrix\",\n requirement:\n \"a provider-native authenticated ingress mode, which this adapter does not support\",\n webhook: config.matrix?.webhook,\n });\n super({\n codec: getBuiltinTargetCodec(\"matrix\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/matrix/webhook\", port: 8797 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesMatrixThread,\n normalizeWebhookPayload: (payload) => normalizeMatrixWebhookPayload(payload, botUserId),\n platform: \"matrix\",\n publicUrl: config.matrix?.webhook.publicUrl,\n recorderPath: config.matrix?.recorder.path\n ? path.resolve(config.matrix.recorder.path)\n : undefined,\n webhook: config.matrix?.webhook,\n },\n });\n }\n}\n\nfunction matrixThreadKey(roomId: string, threadId: string): string {\n return `${roomId}:thread:${threadId}`;\n}\n\nexport function matchesMatrixThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined },\n): boolean {\n if (!expectedThreadId) {\n return true;\n }\n const scopedExpected =\n target.channelId && MATRIX_EVENT_ID_RULE.pattern.test(expectedThreadId)\n ? matrixThreadKey(target.channelId, expectedThreadId)\n : expectedThreadId;\n return (\n candidateThreadId === expectedThreadId ||\n candidateThreadId === scopedExpected ||\n (MATRIX_ROOM_ID_RULE.pattern.test(scopedExpected) &&\n candidateThreadId.startsWith(`${scopedExpected}:thread:`))\n );\n}\n\nexport function normalizeMatrixWebhookPayload(payload: unknown, botUserId?: string) {\n if (!isRecord(payload)) {\n throw new CrablineError(\"Matrix webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n if (optionalRecord(payload, \"message\")) {\n return genericMockPayloadWithNativeThread({\n channelRule: MATRIX_ROOM_ID_RULE,\n payload,\n threadRule: MATRIX_EVENT_ID_RULE,\n });\n }\n\n const content = optionalRecord(payload, \"content\");\n const roomId = optionalString(payload, \"room_id\");\n const eventId = optionalString(payload, \"event_id\");\n const eventType = optionalString(payload, \"type\");\n const senderId = optionalString(payload, \"sender\");\n const text = content ? optionalString(content, \"body\") : undefined;\n if (eventType !== \"m.room.message\") {\n throw new CrablineError(\"Matrix event payload requires type=m.room.message\", {\n kind: \"inbound\",\n });\n }\n if (!roomId || !eventId || !text) {\n throw new CrablineError(\"Matrix event payload requires room_id, event_id, and content.body\", {\n kind: \"inbound\",\n });\n }\n\n const relation = content ? optionalRecord(content, \"m.relates_to\") : undefined;\n const isThread = relation && optionalString(relation, \"rel_type\") === \"m.thread\";\n const threadRootId = isThread ? optionalString(relation, \"event_id\") : undefined;\n if (isThread && !threadRootId) {\n throw new CrablineError(\"Matrix m.thread relation requires event_id\", {\n kind: \"inbound\",\n });\n }\n\n return {\n author: authorFromBotFlag(senderId !== undefined && senderId === botUserId),\n id: requireNativeInboundId(eventId, MATRIX_EVENT_ID_RULE, \"Matrix event_id\"),\n raw: payload,\n text,\n threadId: threadRootId\n ? matrixThreadKey(\n requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, \"Matrix room_id\"),\n requireNativeInboundId(threadRootId, MATRIX_EVENT_ID_RULE, \"Matrix thread root event_id\"),\n )\n : requireNativeInboundId(roomId, MATRIX_ROOM_ID_RULE, \"Matrix room_id\"),\n };\n}\n"]}
1
+ {"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../../../src/providers/builtin/matrix.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { CrablineError } from \"../../core/errors.js\";\nimport type { ProviderConfig } from \"../../config/schema.js\";\nimport { isMatrixEventId, isMatrixRoomId, isMatrixUserId } from \"../../matrix-ids.js\";\nimport { LocalMockProviderAdapter } from \"../local-mock.js\";\nimport type { ProviderAdapter } from \"../types.js\";\nimport {\n getBuiltinTargetCodec,\n MATRIX_EVENT_ID_RULE,\n MATRIX_ROOM_ID_RULE,\n} from \"../target-normalizers.js\";\nimport {\n authorFromBotFlag,\n genericMockPayloadWithNativeThread,\n isRecord,\n optionalRecord,\n optionalString,\n} from \"./native-local-mock.js\";\nimport { requireExternalWebhookAuthentication } from \"./external-webhook-auth.js\";\n\nexport function resolveMatrixAdapterConfig(\n config: ProviderConfig,\n userName: string,\n env: NodeJS.ProcessEnv = process.env,\n) {\n const fallbackUserId = env.MATRIX_USER_ID?.trim() || `@${userName}:matrix.local`;\n const configuredAuth = config.matrix?.auth\n ? {\n ...config.matrix.auth,\n userID: config.matrix.auth.userID?.trim() || fallbackUserId,\n }\n : undefined;\n if (!isMatrixUserId(configuredAuth?.userID ?? fallbackUserId)) {\n throw new CrablineError(\"Matrix auth.userID must be a canonical Matrix user ID.\", {\n kind: \"config\",\n });\n }\n return {\n auth: configuredAuth ?? {\n accessToken: env.MATRIX_ACCESS_TOKEN ?? \"local-mock-matrix-token\",\n type: \"accessToken\" as const,\n userID: fallbackUserId,\n },\n baseURL: config.matrix?.baseURL ?? env.MATRIX_BASE_URL ?? \"http://matrix.local\",\n commandPrefix: config.matrix?.commandPrefix,\n recoveryKey: config.matrix?.recoveryKey ?? env.MATRIX_RECOVERY_KEY,\n };\n}\n\nexport class MatrixProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {\n constructor(id: string, config: ProviderConfig, userName: string, _runtime?: unknown) {\n const resolvedConfig = resolveMatrixAdapterConfig(config, userName);\n const botUserId = resolvedConfig.auth.userID;\n requireExternalWebhookAuthentication({\n authenticated: false,\n provider: \"Matrix\",\n requirement:\n \"a provider-native authenticated ingress mode, which this adapter does not support\",\n webhook: config.matrix?.webhook,\n });\n super({\n codec: getBuiltinTargetCodec(\"matrix\"),\n config,\n id,\n options: {\n defaultWebhook: { host: \"127.0.0.1\", path: \"/matrix/webhook\", port: 8797 },\n endpointLabel: \"webhook endpoint\",\n matchesThread: matchesMatrixThread,\n normalizeWebhookPayload: (payload) => normalizeMatrixWebhookPayload(payload, botUserId),\n platform: \"matrix\",\n publicUrl: config.matrix?.webhook.publicUrl,\n recorderPath: config.matrix?.recorder.path\n ? path.resolve(config.matrix.recorder.path)\n : undefined,\n webhook: config.matrix?.webhook,\n },\n });\n }\n}\n\nfunction matrixThreadKey(roomId: string, threadId: string): string {\n return `${roomId}:thread:${threadId}`;\n}\n\nexport function matchesMatrixThread(\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: { channelId?: string | undefined },\n): boolean {\n if (!expectedThreadId) {\n return true;\n }\n if (target.channelId && isMatrixEventId(expectedThreadId)) {\n return candidateThreadId === matrixThreadKey(target.channelId, expectedThreadId);\n }\n return (\n candidateThreadId === expectedThreadId ||\n (isMatrixRoomId(expectedThreadId) &&\n candidateThreadId.startsWith(`${expectedThreadId}:thread:`))\n );\n}\n\nexport function normalizeMatrixWebhookPayload(payload: unknown, botUserId?: string) {\n if (!isRecord(payload)) {\n throw new CrablineError(\"Matrix webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n const message = optionalRecord(payload, \"message\");\n if (\n (message && (\"text\" in message || \"threadId\" in message)) ||\n \"text\" in payload ||\n \"threadId\" in payload\n ) {\n const normalized = genericMockPayloadWithNativeThread({\n channelRule: MATRIX_ROOM_ID_RULE,\n payload,\n threadRule: MATRIX_EVENT_ID_RULE,\n });\n const threadId =\n (message ? optionalString(message, \"threadId\") : undefined) ??\n optionalString(payload, \"threadId\");\n const roomId =\n (message ? optionalString(message, \"channelId\") : undefined) ??\n optionalString(payload, \"channelId\") ??\n optionalString(payload, \"roomId\") ??\n optionalString(payload, \"room_id\");\n if (\n \"threadId\" in normalized &&\n !isMatrixRoomId(normalized.threadId) &&\n !isMatrixEventId(normalized.threadId)\n ) {\n throw new CrablineError(\n `mock webhook threadId must be a native ${MATRIX_EVENT_ID_RULE.name} or ${MATRIX_ROOM_ID_RULE.name}.`,\n { kind: \"inbound\" },\n );\n }\n if (threadId && isMatrixEventId(threadId)) {\n if (!roomId || !isMatrixRoomId(roomId)) {\n throw new CrablineError(\n `Matrix generic event threadId requires a native ${MATRIX_ROOM_ID_RULE.name} channelId.`,\n { kind: \"inbound\" },\n );\n }\n const scopedThreadId = matrixThreadKey(roomId, threadId);\n return {\n ...normalized,\n ...(\"message\" in normalized && isRecord(normalized.message)\n ? { message: { ...normalized.message, threadId: scopedThreadId } }\n : {}),\n threadId: scopedThreadId,\n };\n }\n if (threadId && isMatrixRoomId(threadId) && roomId && roomId !== threadId) {\n throw new CrablineError(\"Matrix generic room threadId must match channelId.\", {\n kind: \"inbound\",\n });\n }\n return normalized;\n }\n\n const content = optionalRecord(payload, \"content\");\n const roomId = optionalString(payload, \"room_id\");\n const eventId = optionalString(payload, \"event_id\");\n const eventType = optionalString(payload, \"type\");\n const sender = payload.sender;\n const senderId = typeof sender === \"string\" ? sender : undefined;\n const msgtype = content?.msgtype;\n const text = content?.body;\n if (eventType !== \"m.room.message\") {\n throw new CrablineError(\"Matrix event payload requires type=m.room.message\", {\n kind: \"inbound\",\n });\n }\n if (!roomId || !eventId || typeof msgtype !== \"string\" || typeof text !== \"string\") {\n throw new CrablineError(\n \"Matrix event payload requires room_id, event_id, string content.msgtype, and string content.body\",\n { kind: \"inbound\" },\n );\n }\n\n const relation = content ? optionalRecord(content, \"m.relates_to\") : undefined;\n const isThread = relation && optionalString(relation, \"rel_type\") === \"m.thread\";\n const threadRootId = isThread ? optionalString(relation, \"event_id\") : undefined;\n if (isThread && !threadRootId) {\n throw new CrablineError(\"Matrix m.thread relation requires event_id\", {\n kind: \"inbound\",\n });\n }\n if (!isMatrixRoomId(roomId)) {\n throw new CrablineError(\n `Matrix room_id must be a native ${MATRIX_ROOM_ID_RULE.name} such as ${MATRIX_ROOM_ID_RULE.example}.`,\n { kind: \"inbound\" },\n );\n }\n if (!isMatrixEventId(eventId)) {\n throw new CrablineError(\n `Matrix event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`,\n { kind: \"inbound\" },\n );\n }\n if (threadRootId && !isMatrixEventId(threadRootId)) {\n throw new CrablineError(\n `Matrix thread root event_id must be a native ${MATRIX_EVENT_ID_RULE.name} such as ${MATRIX_EVENT_ID_RULE.example}.`,\n { kind: \"inbound\" },\n );\n }\n if (!senderId || !isMatrixUserId(senderId)) {\n throw new CrablineError(\"Matrix sender must be a canonical Matrix user ID.\", {\n kind: \"inbound\",\n });\n }\n\n return {\n author: authorFromBotFlag(senderId !== undefined && senderId === botUserId),\n id: eventId,\n raw: payload,\n text,\n threadId: threadRootId ? matrixThreadKey(roomId, threadRootId) : roomId,\n };\n}\n"]}
@@ -1,21 +1,25 @@
1
1
  import type { ProviderConfig } from "../../config/schema.js";
2
2
  import { LocalMockProviderAdapter } from "../local-mock.js";
3
3
  import type { ProviderAdapter } from "../types.js";
4
- export declare function resolveMattermostAdapterConfig(config: ProviderConfig, env?: NodeJS.ProcessEnv): {
4
+ type MattermostEnvironment = Partial<Pick<NodeJS.ProcessEnv, "MATTERMOST_BASE_URL" | "MATTERMOST_BOT_TOKEN" | "MATTERMOST_TOKEN" | "MATTERMOST_URL">>;
5
+ export declare function resolveMattermostAdapterConfig(config: ProviderConfig, env?: MattermostEnvironment): {
5
6
  baseUrl: string;
6
7
  botToken: string;
7
8
  userName: string | undefined;
9
+ webhookToken: string | undefined;
8
10
  };
9
11
  export declare class MattermostProviderAdapter extends LocalMockProviderAdapter implements ProviderAdapter {
10
- constructor(id: string, config: ProviderConfig, _userName: string, _runtime?: unknown);
12
+ constructor(id: string, config: ProviderConfig, _userName: string, runtime?: unknown);
11
13
  }
12
14
  export declare function matchesMattermostThread(candidateThreadId: string, expectedThreadId: string | undefined, target: {
13
15
  channelId?: string | undefined;
14
16
  }): boolean;
15
- export declare function normalizeMattermostWebhookPayload(payload: unknown): {
16
- author?: "assistant" | "system" | "user" | undefined;
17
- authorIsBot?: boolean;
17
+ type NormalizedMattermostWebhookPayload = {
18
+ author: "assistant" | "system" | "user";
18
19
  id?: string | undefined;
19
- raw: {};
20
- text?: string | undefined;
20
+ raw: unknown;
21
+ text: string;
22
+ threadId: string;
21
23
  };
24
+ export declare function normalizeMattermostWebhookPayload(payload: unknown): NormalizedMattermostWebhookPayload;
25
+ export {};