@zooid/transport-matrix 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +77 -1
- package/dist/index.js +269 -171
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bot-pool.test.ts +7 -1
- package/src/bot-pool.ts +7 -1
- package/src/identity.test.ts +52 -0
- package/src/identity.ts +27 -0
- package/src/index.ts +11 -0
- package/src/matrix-client.test.ts +23 -0
- package/src/matrix-client.ts +32 -0
- package/src/registration.test.ts +20 -0
- package/src/sync-loop.test.ts +113 -0
- package/src/sync-loop.ts +74 -0
- package/src/transport.ts +275 -241
package/dist/index.js
CHANGED
|
@@ -255,6 +255,19 @@ var MatrixClient = class {
|
|
|
255
255
|
if (!r.ok) throw new Error(`getJoinedMembers(${roomId}) failed: ${r.status}`);
|
|
256
256
|
return await r.json();
|
|
257
257
|
}
|
|
258
|
+
async sync(opts) {
|
|
259
|
+
const params = new URLSearchParams({
|
|
260
|
+
user_id: opts.asUserId,
|
|
261
|
+
timeout: String(opts.timeoutMs ?? 3e4)
|
|
262
|
+
});
|
|
263
|
+
if (opts.since) params.set("since", opts.since);
|
|
264
|
+
const url = `${this.homeserver}/_matrix/client/v3/sync?${params.toString()}`;
|
|
265
|
+
const r = await this.fetch(url, {
|
|
266
|
+
headers: { Authorization: `Bearer ${this.asToken}` }
|
|
267
|
+
});
|
|
268
|
+
if (!r.ok) throw new Error(`sync(${opts.asUserId}) failed: ${r.status}`);
|
|
269
|
+
return r.json();
|
|
270
|
+
}
|
|
258
271
|
async fetchRoomName(roomId, asUserId) {
|
|
259
272
|
const url = `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state/m.room.name/?user_id=${encodeURIComponent(asUserId)}`;
|
|
260
273
|
const r = await this.fetch(url, {
|
|
@@ -284,6 +297,7 @@ var MatrixContextProvider = class {
|
|
|
284
297
|
constructor(opts) {
|
|
285
298
|
this.opts = opts;
|
|
286
299
|
}
|
|
300
|
+
opts;
|
|
287
301
|
async getRoomHistory(channelId, hopts) {
|
|
288
302
|
const { chunk, end } = await this.opts.client.fetchRoomMessages({
|
|
289
303
|
roomId: channelId,
|
|
@@ -443,6 +457,29 @@ function renderRegistration(c) {
|
|
|
443
457
|
);
|
|
444
458
|
}
|
|
445
459
|
|
|
460
|
+
// src/identity.ts
|
|
461
|
+
var SLUG_RE = /^[a-z0-9-]+$/;
|
|
462
|
+
var AGENT_KEY_RE = /^[a-z0-9-]+$/;
|
|
463
|
+
var isValidWorkstation = (s) => SLUG_RE.test(s);
|
|
464
|
+
var isValidAgentKey = (s) => AGENT_KEY_RE.test(s);
|
|
465
|
+
function agentMxid(workstation, agent, serverName) {
|
|
466
|
+
if (!isValidWorkstation(workstation))
|
|
467
|
+
throw new Error(`invalid workstation: ${JSON.stringify(workstation)}`);
|
|
468
|
+
if (!isValidAgentKey(agent)) throw new Error(`invalid agent key: ${JSON.stringify(agent)}`);
|
|
469
|
+
return `@${workstation}.${agent}:${serverName}`;
|
|
470
|
+
}
|
|
471
|
+
function splitAgentLocalpart(localpart2) {
|
|
472
|
+
const i = localpart2.indexOf(".");
|
|
473
|
+
if (i <= 0 || i === localpart2.length - 1)
|
|
474
|
+
throw new Error(`not a workstation.agent localpart: ${localpart2}`);
|
|
475
|
+
return { workstation: localpart2.slice(0, i), agent: localpart2.slice(i + 1) };
|
|
476
|
+
}
|
|
477
|
+
function workstationUserNamespace(workstation, serverName) {
|
|
478
|
+
if (!isValidWorkstation(workstation))
|
|
479
|
+
throw new Error(`invalid workstation: ${JSON.stringify(workstation)}`);
|
|
480
|
+
return `@${workstation}\\..*:${serverName}`;
|
|
481
|
+
}
|
|
482
|
+
|
|
446
483
|
// src/mentions.ts
|
|
447
484
|
var MATRIX_TO_RE = /https:\/\/matrix\.to\/#\/(@[^"<>\s]+)/g;
|
|
448
485
|
var RAW_USER_RE = /(@[A-Za-z0-9._\-=/+]+:[A-Za-z0-9.\-]+)/g;
|
|
@@ -573,6 +610,8 @@ var BotPool = class {
|
|
|
573
610
|
this.client = client;
|
|
574
611
|
this.agents = agents;
|
|
575
612
|
}
|
|
613
|
+
client;
|
|
614
|
+
agents;
|
|
576
615
|
async bootstrap(opts = {}) {
|
|
577
616
|
const aliasToId = /* @__PURE__ */ new Map();
|
|
578
617
|
const attachedToSpace = /* @__PURE__ */ new Set();
|
|
@@ -618,7 +657,7 @@ var BotPool = class {
|
|
|
618
657
|
} else {
|
|
619
658
|
const colon = room.indexOf(":");
|
|
620
659
|
const aliasLocalpart = colon > 1 ? room.slice(1, colon) : room.slice(1);
|
|
621
|
-
const sender = opts.adminUserId ?? a.userId;
|
|
660
|
+
const sender = opts.asUserId ?? opts.adminUserId ?? a.userId;
|
|
622
661
|
const userPowerLevels = buildUserPowerLevels(
|
|
623
662
|
opts.asUserId,
|
|
624
663
|
opts.adminUserIds,
|
|
@@ -937,6 +976,44 @@ function writeAttachment(input) {
|
|
|
937
976
|
return { hostPath, agentPath };
|
|
938
977
|
}
|
|
939
978
|
|
|
979
|
+
// src/sync-loop.ts
|
|
980
|
+
var SyncLoop = class {
|
|
981
|
+
opts;
|
|
982
|
+
running = false;
|
|
983
|
+
constructor(opts) {
|
|
984
|
+
this.opts = opts;
|
|
985
|
+
}
|
|
986
|
+
async tick() {
|
|
987
|
+
const since = this.opts.loadSince();
|
|
988
|
+
const res = await this.opts.client.sync({
|
|
989
|
+
asUserId: this.opts.asUserId,
|
|
990
|
+
since,
|
|
991
|
+
timeoutMs: this.opts.timeoutMs ?? 3e4
|
|
992
|
+
});
|
|
993
|
+
for (const [roomId, roomState] of Object.entries(res.rooms?.join ?? {})) {
|
|
994
|
+
for (const baseEvt of roomState.timeline?.events ?? []) {
|
|
995
|
+
await this.opts.onEvent({ ...baseEvt, room_id: roomId });
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
this.opts.saveSince(res.next_batch);
|
|
999
|
+
}
|
|
1000
|
+
async run() {
|
|
1001
|
+
this.running = true;
|
|
1002
|
+
while (this.running) {
|
|
1003
|
+
try {
|
|
1004
|
+
await this.tick();
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
if (!this.running) break;
|
|
1007
|
+
console.warn(`[sync-loop] ${this.opts.asUserId} tick failed, retrying:`, err);
|
|
1008
|
+
await new Promise((r) => setTimeout(r, this.opts.retryDelayMs ?? 5e3));
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
stop() {
|
|
1013
|
+
this.running = false;
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
|
|
940
1017
|
// src/transport.ts
|
|
941
1018
|
var STARTUP_GRACE_MS = 5e3;
|
|
942
1019
|
async function buildMediaBlocks(items, opts) {
|
|
@@ -1030,7 +1107,7 @@ function inboundThreadRoot2(evt) {
|
|
|
1030
1107
|
return r?.rel_type === "m.thread" && r.event_id ? r.event_id : void 0;
|
|
1031
1108
|
}
|
|
1032
1109
|
function createMatrixTransport(opts) {
|
|
1033
|
-
const { agents, approvals, client, bindings, hsToken, adminUserId, botUserId } = opts;
|
|
1110
|
+
const { agents, approvals, client, bindings, hsToken, adminUserId, botUserId, mode = "appservice" } = opts;
|
|
1034
1111
|
const drainQuietMs = opts.drainQuietMs ?? DRAIN_QUIET_MS;
|
|
1035
1112
|
const drainMaxMs = opts.drainMaxMs ?? DRAIN_MAX_MS;
|
|
1036
1113
|
const mediaClient = opts.media;
|
|
@@ -1047,7 +1124,7 @@ function createMatrixTransport(opts) {
|
|
|
1047
1124
|
const bufferMessageIds = /* @__PURE__ */ new Map();
|
|
1048
1125
|
const sendQueue = /* @__PURE__ */ new Map();
|
|
1049
1126
|
const threadStates = /* @__PURE__ */ new Map();
|
|
1050
|
-
const cutoffTs = Date.now() - STARTUP_GRACE_MS;
|
|
1127
|
+
const cutoffTs = mode === "client" ? Number.NEGATIVE_INFINITY : Date.now() - STARTUP_GRACE_MS;
|
|
1051
1128
|
const seenEventIds = /* @__PURE__ */ new Set();
|
|
1052
1129
|
const flushedCounts = /* @__PURE__ */ new Map();
|
|
1053
1130
|
const pendingCommands = /* @__PURE__ */ new Map();
|
|
@@ -1184,194 +1261,197 @@ function createMatrixTransport(opts) {
|
|
|
1184
1261
|
content
|
|
1185
1262
|
});
|
|
1186
1263
|
});
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
if (!authOk(c.req.header("authorization"))) {
|
|
1197
|
-
return c.json({ errcode: "M_FORBIDDEN" }, 403);
|
|
1198
|
-
}
|
|
1199
|
-
const body = await c.req.json().catch(() => ({}));
|
|
1200
|
-
for (const evt of body.events ?? []) {
|
|
1201
|
-
if (evt.event_id) {
|
|
1202
|
-
if (seenEventIds.has(evt.event_id)) {
|
|
1203
|
-
continue;
|
|
1204
|
-
}
|
|
1205
|
-
seenEventIds.add(evt.event_id);
|
|
1206
|
-
if (seenEventIds.size > SEEN_EVENT_CAP) {
|
|
1207
|
-
const first = seenEventIds.values().next().value;
|
|
1208
|
-
if (first !== void 0) seenEventIds.delete(first);
|
|
1209
|
-
}
|
|
1264
|
+
async function handleInboundEvent(evt) {
|
|
1265
|
+
if (evt.event_id) {
|
|
1266
|
+
if (seenEventIds.has(evt.event_id)) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
seenEventIds.add(evt.event_id);
|
|
1270
|
+
if (seenEventIds.size > SEEN_EVENT_CAP) {
|
|
1271
|
+
const first = seenEventIds.values().next().value;
|
|
1272
|
+
if (first !== void 0) seenEventIds.delete(first);
|
|
1210
1273
|
}
|
|
1211
|
-
|
|
1274
|
+
}
|
|
1275
|
+
if (evt.origin_server_ts !== void 0 && evt.origin_server_ts < cutoffTs && evt.type === "m.room.message") {
|
|
1276
|
+
console.log(
|
|
1277
|
+
`[matrix] dropping stale message event ${evt.event_id} (ts=${evt.origin_server_ts}, daemon started at ${cutoffTs + STARTUP_GRACE_MS})`
|
|
1278
|
+
);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
if (evt.type === "m.room.member" && evt.content?.membership === "invite") {
|
|
1282
|
+
const target = evt.state_key;
|
|
1283
|
+
const inviter = evt.sender;
|
|
1284
|
+
if (target && evt.room_id && ourBotUserIds.has(target) && (!inviter || !ourBotUserIds.has(inviter))) {
|
|
1212
1285
|
console.log(
|
|
1213
|
-
`[matrix]
|
|
1286
|
+
`[matrix] declining ad-hoc invite for ${target} in ${evt.room_id} from ${inviter ?? "unknown"}`
|
|
1287
|
+
);
|
|
1288
|
+
await client.leaveRoom(evt.room_id, target, { reason: DECLINE_REASON }).catch(
|
|
1289
|
+
(err) => console.warn(`[matrix] leaveRoom(${evt.room_id}, ${target}) failed:`, err)
|
|
1214
1290
|
);
|
|
1215
|
-
continue;
|
|
1216
1291
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
(err) => console.warn(`[matrix] leaveRoom(${evt.room_id}, ${target}) failed:`, err)
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1228
|
-
continue;
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
if (evt.type === "dev.zooid.session_reset") {
|
|
1295
|
+
const relates = evt.content?.["m.relates_to"];
|
|
1296
|
+
const threadRoot = relates?.rel_type === "m.thread" && relates.event_id ? relates.event_id : void 0;
|
|
1297
|
+
if (!threadRoot) {
|
|
1298
|
+
console.log("[matrix] dropping dev.zooid.session_reset without thread relation");
|
|
1299
|
+
return;
|
|
1229
1300
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
if (!threadRoot) {
|
|
1234
|
-
console.log("[matrix] dropping dev.zooid.session_reset without thread relation");
|
|
1235
|
-
continue;
|
|
1236
|
-
}
|
|
1237
|
-
console.log(`[matrix] inbound dev.zooid.session_reset in ${evt.room_id} thread=${threadRoot}`);
|
|
1238
|
-
for (const a of bindings) {
|
|
1239
|
-
agents.endSession(a.name, threadRoot);
|
|
1240
|
-
}
|
|
1241
|
-
continue;
|
|
1301
|
+
console.log(`[matrix] inbound dev.zooid.session_reset in ${evt.room_id} thread=${threadRoot}`);
|
|
1302
|
+
for (const a of bindings) {
|
|
1303
|
+
agents.endSession(a.name, threadRoot);
|
|
1242
1304
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
for (const t of targets) {
|
|
1255
|
-
console.log(
|
|
1256
|
-
`[matrix] interrupt session=${t.sessionId} agent=${t.agent} thread=${threadRoot}` + (content.reason ? ` reason=${content.reason}` : "")
|
|
1257
|
-
);
|
|
1258
|
-
await agents.cancelSession(t.agent, t.sessionId).catch((err) => {
|
|
1259
|
-
console.error(`[matrix] cancelSession(${t.agent}, ${t.sessionId}) failed:`, err);
|
|
1260
|
-
});
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
if (evt.type === "dev.zooid.interrupt") {
|
|
1308
|
+
const content = evt.content ?? {};
|
|
1309
|
+
const relates = evt.content?.["m.relates_to"];
|
|
1310
|
+
const threadRoot = relates?.rel_type === "m.thread" && relates.event_id ? relates.event_id : void 0;
|
|
1311
|
+
if (threadRoot) {
|
|
1312
|
+
const targets = [];
|
|
1313
|
+
for (const [sessionId, ctx2] of sessions) {
|
|
1314
|
+
if (ctx2.threadRoot === threadRoot) {
|
|
1315
|
+
targets.push({ sessionId, agent: ctx2.agent.name });
|
|
1261
1316
|
}
|
|
1262
|
-
continue;
|
|
1263
1317
|
}
|
|
1264
|
-
|
|
1265
|
-
console.
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1318
|
+
for (const t of targets) {
|
|
1319
|
+
console.log(
|
|
1320
|
+
`[matrix] interrupt session=${t.sessionId} agent=${t.agent} thread=${threadRoot}` + (content.reason ? ` reason=${content.reason}` : "")
|
|
1321
|
+
);
|
|
1322
|
+
await agents.cancelSession(t.agent, t.sessionId).catch((err) => {
|
|
1323
|
+
console.error(`[matrix] cancelSession(${t.agent}, ${t.sessionId}) failed:`, err);
|
|
1324
|
+
});
|
|
1271
1325
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
});
|
|
1278
|
-
continue;
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (!content.session_id) {
|
|
1329
|
+
console.warn(`[matrix] dev.zooid.interrupt missing session_id (event_id=${evt.event_id})`);
|
|
1330
|
+
return;
|
|
1279
1331
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1332
|
+
const ctx = sessions.get(content.session_id);
|
|
1333
|
+
if (!ctx) {
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
console.log(
|
|
1337
|
+
`[matrix] interrupt session=${content.session_id} agent=${ctx.agent.name}` + (content.reason ? ` reason=${content.reason}` : "")
|
|
1338
|
+
);
|
|
1339
|
+
await agents.cancelSession(ctx.agent.name, content.session_id).catch((err) => {
|
|
1340
|
+
console.error(`[matrix] cancelSession(${ctx.agent.name}, ${content.session_id}) failed:`, err);
|
|
1341
|
+
});
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
if (evt.type === "dev.zooid.approval_response") {
|
|
1345
|
+
const content = evt.content ?? {};
|
|
1346
|
+
if (!content.session_id || !content.approval_id || !content.decision) return;
|
|
1347
|
+
const decision = content.option_id ? { decision: content.decision, optionId: content.option_id } : { decision: content.decision };
|
|
1348
|
+
const ok = approvals.resolve(
|
|
1349
|
+
content.session_id,
|
|
1350
|
+
content.approval_id,
|
|
1351
|
+
decision
|
|
1352
|
+
);
|
|
1353
|
+
if (!ok) console.warn(`[matrix] unknown approval ${content.approval_id}`);
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
logInbound(evt);
|
|
1357
|
+
if (evt.type === "m.room.message" && isMediaMsgtype(evt.content?.msgtype) && evt.room_id && evt.event_id && evt.sender && evt.content?.url && !bindings.some((b) => b.userId === evt.sender)) {
|
|
1358
|
+
pendingMedia.add(evt.room_id, inboundThreadRoot2(evt), {
|
|
1359
|
+
eventId: evt.event_id,
|
|
1360
|
+
sender: evt.sender,
|
|
1361
|
+
msgtype: evt.content.msgtype,
|
|
1362
|
+
body: evt.content.body ?? "",
|
|
1363
|
+
filename: evt.content.filename,
|
|
1364
|
+
url: evt.content.url,
|
|
1365
|
+
info: evt.content.info
|
|
1366
|
+
});
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
const promotedRoot = inboundThreadRoot2(evt) ?? evt.event_id;
|
|
1370
|
+
const inboundRel = inboundThreadRoot2(evt);
|
|
1371
|
+
if (evt.type === "m.room.message" && inboundRel && !threadStates.has(inboundRel) && evt.room_id) {
|
|
1372
|
+
try {
|
|
1373
|
+
const rebuilt = await rebuildThreadState(client, evt.room_id, inboundRel, bindings);
|
|
1374
|
+
threadStates.set(inboundRel, rebuilt);
|
|
1375
|
+
console.log(
|
|
1376
|
+
`[matrix] rebuilt threadState for ${inboundRel}: participants=${rebuilt.participants.join(",")} rootMentions=${rebuilt.rootMentions.join(",")}`
|
|
1288
1377
|
);
|
|
1289
|
-
|
|
1290
|
-
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
console.warn(`[matrix] failed to rebuild threadState for ${inboundRel}:`, err);
|
|
1291
1380
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1381
|
+
}
|
|
1382
|
+
const matches = route(evt, bindings, threadStates);
|
|
1383
|
+
const senderIsBot = bindings.some((b) => b.userId === evt.sender);
|
|
1384
|
+
if (evt.type === "m.room.message" && matches.length === 0 && !senderIsBot) {
|
|
1385
|
+
console.warn(
|
|
1386
|
+
`[matrix] no agent matched message in ${evt.room_id} from ${evt.sender} (bindings: ${bindings.map((b) => `${b.name}@${b.userId}[${b.trigger}]`).join(", ")})`
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
if (matches.length > 0 && promotedRoot) {
|
|
1390
|
+
let st = threadStates.get(promotedRoot);
|
|
1391
|
+
if (!st) {
|
|
1392
|
+
st = { participants: [], rootMentions: [] };
|
|
1393
|
+
threadStates.set(promotedRoot, st);
|
|
1304
1394
|
}
|
|
1305
|
-
const
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
const rebuilt = await rebuildThreadState(client, evt.room_id, inboundRel, bindings);
|
|
1310
|
-
threadStates.set(inboundRel, rebuilt);
|
|
1311
|
-
console.log(
|
|
1312
|
-
`[matrix] rebuilt threadState for ${inboundRel}: participants=${rebuilt.participants.join(",")} rootMentions=${rebuilt.rootMentions.join(",")}`
|
|
1313
|
-
);
|
|
1314
|
-
} catch (err) {
|
|
1315
|
-
console.warn(`[matrix] failed to rebuild threadState for ${inboundRel}:`, err);
|
|
1395
|
+
const msgMentions = new Set(extractMentions(evt));
|
|
1396
|
+
for (const a of bindings) {
|
|
1397
|
+
if (msgMentions.has(a.userId) && !st.rootMentions.includes(a.name)) {
|
|
1398
|
+
st.rootMentions.push(a.name);
|
|
1316
1399
|
}
|
|
1317
1400
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
if (matches.length > 0 && promotedRoot) {
|
|
1401
|
+
}
|
|
1402
|
+
for (const a of matches) {
|
|
1403
|
+
console.log(`[matrix] \u2192 ${a.name} (${a.userId})`);
|
|
1404
|
+
void runTurn(a, evt).then(() => {
|
|
1405
|
+
if (!promotedRoot) return;
|
|
1326
1406
|
let st = threadStates.get(promotedRoot);
|
|
1327
1407
|
if (!st) {
|
|
1328
1408
|
st = { participants: [], rootMentions: [] };
|
|
1329
1409
|
threadStates.set(promotedRoot, st);
|
|
1330
1410
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1411
|
+
if (st.participants.at(-1) !== a.name) st.participants.push(a.name);
|
|
1412
|
+
}).catch((err) => {
|
|
1413
|
+
console.error(`[matrix] runTurn failed for ${a.name}:`, err);
|
|
1414
|
+
const c = classify(err);
|
|
1415
|
+
const threadRoot = inboundThreadRoot2(evt) ?? evt.event_id;
|
|
1416
|
+
if (!threadRoot || !evt.room_id) return;
|
|
1417
|
+
const body = toErrorBody(
|
|
1418
|
+
{
|
|
1419
|
+
kind: "error",
|
|
1420
|
+
agentId: a.name,
|
|
1421
|
+
sessionId: null,
|
|
1422
|
+
turnId: null,
|
|
1423
|
+
code: c.code,
|
|
1424
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1425
|
+
detail: err instanceof Error && err.stack ? err.stack.slice(0, 2e3) : void 0,
|
|
1426
|
+
transient: c.transient,
|
|
1427
|
+
acp_error: c.acp_error
|
|
1428
|
+
},
|
|
1429
|
+
threadRoot
|
|
1430
|
+
);
|
|
1431
|
+
void client.sendCustomEvent({
|
|
1432
|
+
roomId: evt.room_id,
|
|
1433
|
+
asUserId: a.userId,
|
|
1434
|
+
eventType: "dev.zooid.error",
|
|
1435
|
+
content: body
|
|
1436
|
+
}).catch((e) => console.warn(`[matrix:${a.name}] dev.zooid.error send failed:`, e));
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
const app = new Hono();
|
|
1441
|
+
function authOk(authHeader) {
|
|
1442
|
+
const h = authHeader ?? "";
|
|
1443
|
+
if (!h.startsWith("Bearer ")) return false;
|
|
1444
|
+
const got = h.slice(7);
|
|
1445
|
+
if (got.length !== hsToken.length) return false;
|
|
1446
|
+
return timingSafeEqual(Buffer.from(got), Buffer.from(hsToken));
|
|
1447
|
+
}
|
|
1448
|
+
app.put("/_matrix/app/v1/transactions/:txnId", async (c) => {
|
|
1449
|
+
if (!authOk(c.req.header("authorization"))) {
|
|
1450
|
+
return c.json({ errcode: "M_FORBIDDEN" }, 403);
|
|
1451
|
+
}
|
|
1452
|
+
const body = await c.req.json().catch(() => ({}));
|
|
1453
|
+
for (const evt of body.events ?? []) {
|
|
1454
|
+
await handleInboundEvent(evt);
|
|
1375
1455
|
}
|
|
1376
1456
|
return c.json({});
|
|
1377
1457
|
});
|
|
@@ -1475,8 +1555,18 @@ function createMatrixTransport(opts) {
|
|
|
1475
1555
|
sendQueue.delete(sessionId);
|
|
1476
1556
|
}
|
|
1477
1557
|
}
|
|
1558
|
+
const syncLoops = mode === "client" ? bindings.map(
|
|
1559
|
+
(b) => new SyncLoop({
|
|
1560
|
+
client,
|
|
1561
|
+
asUserId: b.userId,
|
|
1562
|
+
loadSince: () => opts.loadSince?.(b.userId) ?? null,
|
|
1563
|
+
saveSince: (since) => opts.saveSince?.(b.userId, since),
|
|
1564
|
+
onEvent: (evt) => handleInboundEvent(evt)
|
|
1565
|
+
})
|
|
1566
|
+
) : void 0;
|
|
1478
1567
|
return {
|
|
1479
1568
|
app,
|
|
1569
|
+
syncLoops,
|
|
1480
1570
|
bootstrap: async (bootstrapOpts = {}) => {
|
|
1481
1571
|
await pool.bootstrap({ adminUserId, ...bootstrapOpts });
|
|
1482
1572
|
await Promise.allSettled(
|
|
@@ -1574,6 +1664,7 @@ async function startWorkforcePublisher(opts) {
|
|
|
1574
1664
|
};
|
|
1575
1665
|
}
|
|
1576
1666
|
export {
|
|
1667
|
+
AGENT_KEY_RE,
|
|
1577
1668
|
BotPool,
|
|
1578
1669
|
INLINE_IMAGE_MIMES,
|
|
1579
1670
|
MAX_DOWNLOAD_BYTES,
|
|
@@ -1584,18 +1675,25 @@ export {
|
|
|
1584
1675
|
MatrixContextProvider,
|
|
1585
1676
|
MediaClient,
|
|
1586
1677
|
PendingMediaStore,
|
|
1678
|
+
SLUG_RE,
|
|
1679
|
+
SyncLoop,
|
|
1680
|
+
agentMxid,
|
|
1587
1681
|
buildWorkforceRoster,
|
|
1588
1682
|
createMatrixTransport,
|
|
1589
1683
|
ensureDefaultChannel,
|
|
1590
1684
|
ensureWorkforceSpace,
|
|
1591
1685
|
extractMentions,
|
|
1592
1686
|
isMediaMsgtype,
|
|
1687
|
+
isValidAgentKey,
|
|
1688
|
+
isValidWorkstation,
|
|
1593
1689
|
parseMxcUri,
|
|
1594
1690
|
publishWorkforce,
|
|
1595
1691
|
renderRegistration,
|
|
1596
1692
|
route,
|
|
1597
1693
|
serverNameFromMxid,
|
|
1694
|
+
splitAgentLocalpart,
|
|
1598
1695
|
startWorkforcePublisher,
|
|
1696
|
+
workstationUserNamespace,
|
|
1599
1697
|
writeAttachment
|
|
1600
1698
|
};
|
|
1601
1699
|
//# sourceMappingURL=index.js.map
|