@zooid/transport-matrix 0.10.0 → 0.11.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 +7 -0
- package/dist/index.js +50 -8
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/router.test.ts +73 -1
- package/src/router.ts +7 -0
- package/src/session-keys.test.ts +39 -0
- package/src/session-keys.ts +29 -0
- package/src/transport.test.ts +339 -10
- package/src/transport.ts +54 -9
package/dist/index.d.ts
CHANGED
|
@@ -271,6 +271,13 @@ interface ThreadState {
|
|
|
271
271
|
* Implicit triggers → Directional continuation.
|
|
272
272
|
*/
|
|
273
273
|
callers: Record<string, string>;
|
|
274
|
+
/**
|
|
275
|
+
* Handoff arcs per sub-agent: the event ids of the agent→agent @mention
|
|
276
|
+
* messages that called it, in timeline order (last = current arc). Each
|
|
277
|
+
* call opens a fresh ACP session keyed `threadRoot|callEventId`
|
|
278
|
+
* ([[ZOD071]]). Append-only; rebuilt from the timeline after a restart.
|
|
279
|
+
*/
|
|
280
|
+
handoffs: Record<string, string[]>;
|
|
274
281
|
}
|
|
275
282
|
interface MaybeEvent {
|
|
276
283
|
type?: string;
|
package/dist/index.js
CHANGED
|
@@ -745,6 +745,16 @@ function buildUserPowerLevels(asUserId, admins, agents, roomAlias) {
|
|
|
745
745
|
import { Hono } from "hono";
|
|
746
746
|
import { timingSafeEqual } from "crypto";
|
|
747
747
|
|
|
748
|
+
// src/session-keys.ts
|
|
749
|
+
var HANDOFF_KEY_SEP = "|";
|
|
750
|
+
function composeHandoffKey(threadRoot, callEventId) {
|
|
751
|
+
return `${threadRoot}${HANDOFF_KEY_SEP}${callEventId}`;
|
|
752
|
+
}
|
|
753
|
+
function sessionKeyFor(agentName, threadRoot, state) {
|
|
754
|
+
const arc = state?.handoffs[agentName]?.at(-1);
|
|
755
|
+
return arc ? composeHandoffKey(threadRoot, arc) : threadRoot;
|
|
756
|
+
}
|
|
757
|
+
|
|
748
758
|
// src/event-encoders.ts
|
|
749
759
|
var RAW_INPUT_STR_MAX = 250;
|
|
750
760
|
function toToolCallBody(evt) {
|
|
@@ -1312,8 +1322,22 @@ function createMatrixTransport(opts) {
|
|
|
1312
1322
|
return;
|
|
1313
1323
|
}
|
|
1314
1324
|
console.log(`[matrix] inbound dev.zooid.session_reset in ${evt.room_id} thread=${threadRoot}`);
|
|
1325
|
+
if (!threadStates.has(threadRoot) && evt.room_id) {
|
|
1326
|
+
try {
|
|
1327
|
+
threadStates.set(
|
|
1328
|
+
threadRoot,
|
|
1329
|
+
await rebuildThreadState(client, evt.room_id, threadRoot, bindings)
|
|
1330
|
+
);
|
|
1331
|
+
} catch (err) {
|
|
1332
|
+
console.warn(`[matrix] failed to rebuild threadState for reset ${threadRoot}:`, err);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
const st = threadStates.get(threadRoot);
|
|
1315
1336
|
for (const a of bindings) {
|
|
1316
1337
|
agents.endSession(a.name, threadRoot);
|
|
1338
|
+
for (const arc of st?.handoffs[a.name] ?? []) {
|
|
1339
|
+
agents.endSession(a.name, composeHandoffKey(threadRoot, arc));
|
|
1340
|
+
}
|
|
1317
1341
|
}
|
|
1318
1342
|
return;
|
|
1319
1343
|
}
|
|
@@ -1402,7 +1426,7 @@ function createMatrixTransport(opts) {
|
|
|
1402
1426
|
if (matches.length > 0 && promotedRoot) {
|
|
1403
1427
|
let st = threadStates.get(promotedRoot);
|
|
1404
1428
|
if (!st) {
|
|
1405
|
-
st = { participants: [], rootMentions: [], callers: {} };
|
|
1429
|
+
st = { participants: [], rootMentions: [], callers: {}, handoffs: {} };
|
|
1406
1430
|
threadStates.set(promotedRoot, st);
|
|
1407
1431
|
}
|
|
1408
1432
|
const msgMentions = new Set(extractMentions(evt));
|
|
@@ -1410,7 +1434,13 @@ function createMatrixTransport(opts) {
|
|
|
1410
1434
|
for (const a of bindings) {
|
|
1411
1435
|
if (!msgMentions.has(a.userId)) continue;
|
|
1412
1436
|
if (!st.rootMentions.includes(a.name)) st.rootMentions.push(a.name);
|
|
1413
|
-
if (senderAgent && a.name !== senderAgent.name)
|
|
1437
|
+
if (senderAgent && a.name !== senderAgent.name) {
|
|
1438
|
+
st.callers[a.name] = senderAgent.name;
|
|
1439
|
+
if (evt.event_id) {
|
|
1440
|
+
const arcs = st.handoffs[a.name] ??= [];
|
|
1441
|
+
if (!arcs.includes(evt.event_id)) arcs.push(evt.event_id);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1414
1444
|
}
|
|
1415
1445
|
}
|
|
1416
1446
|
for (const a of matches) {
|
|
@@ -1419,7 +1449,7 @@ function createMatrixTransport(opts) {
|
|
|
1419
1449
|
if (!promotedRoot) return;
|
|
1420
1450
|
let st = threadStates.get(promotedRoot);
|
|
1421
1451
|
if (!st) {
|
|
1422
|
-
st = { participants: [], rootMentions: [], callers: {} };
|
|
1452
|
+
st = { participants: [], rootMentions: [], callers: {}, handoffs: {} };
|
|
1423
1453
|
threadStates.set(promotedRoot, st);
|
|
1424
1454
|
}
|
|
1425
1455
|
if (st.participants.at(-1) !== a.name) st.participants.push(a.name);
|
|
@@ -1492,8 +1522,8 @@ function createMatrixTransport(opts) {
|
|
|
1492
1522
|
if (!evt.room_id || !evt.event_id) return;
|
|
1493
1523
|
const inbound = inboundThreadRoot2(evt);
|
|
1494
1524
|
const threadRoot = inbound ?? evt.event_id;
|
|
1495
|
-
const sessionKey = threadRoot;
|
|
1496
|
-
const sessionId = await agents.ensureSession(agent.name, sessionKey, evt.room_id);
|
|
1525
|
+
const sessionKey = sessionKeyFor(agent.name, threadRoot, threadStates.get(threadRoot));
|
|
1526
|
+
const sessionId = await agents.ensureSession(agent.name, sessionKey, evt.room_id, threadRoot);
|
|
1497
1527
|
sessions.set(sessionId, { agent, roomId: evt.room_id, threadRoot });
|
|
1498
1528
|
buffers.set(sessionId, "");
|
|
1499
1529
|
bufferMessageIds.delete(sessionId);
|
|
@@ -1541,6 +1571,7 @@ function createMatrixTransport(opts) {
|
|
|
1541
1571
|
await agents.prompt(agent.name, {
|
|
1542
1572
|
threadId: sessionKey,
|
|
1543
1573
|
channelId: evt.room_id,
|
|
1574
|
+
contextThreadId: threadRoot,
|
|
1544
1575
|
content: [...blocks, { type: "text", text: fullPromptText }]
|
|
1545
1576
|
});
|
|
1546
1577
|
const drainStart = Date.now();
|
|
@@ -1595,7 +1626,7 @@ function createMatrixTransport(opts) {
|
|
|
1595
1626
|
};
|
|
1596
1627
|
}
|
|
1597
1628
|
async function rebuildThreadState(client, roomId, rootEventId, bindings) {
|
|
1598
|
-
const state = { participants: [], rootMentions: [], callers: {} };
|
|
1629
|
+
const state = { participants: [], rootMentions: [], callers: {}, handoffs: {} };
|
|
1599
1630
|
const asUser = (bindings.find((b) => b.rooms.some((r) => r.alias === roomId)) ?? bindings[0])?.userId;
|
|
1600
1631
|
if (!asUser) return state;
|
|
1601
1632
|
const root = await client.fetchEvent(roomId, rootEventId, asUser);
|
|
@@ -1606,7 +1637,11 @@ async function rebuildThreadState(client, roomId, rootEventId, bindings) {
|
|
|
1606
1637
|
for (const a of bindings) {
|
|
1607
1638
|
if (!rootMentions.has(a.userId)) continue;
|
|
1608
1639
|
if (!state.rootMentions.includes(a.name)) state.rootMentions.push(a.name);
|
|
1609
|
-
if (rootSenderAgent && a.name !== rootSenderAgent.name)
|
|
1640
|
+
if (rootSenderAgent && a.name !== rootSenderAgent.name) {
|
|
1641
|
+
state.callers[a.name] = rootSenderAgent.name;
|
|
1642
|
+
const arcs = state.handoffs[a.name] ??= [];
|
|
1643
|
+
if (!arcs.includes(rootEventId)) arcs.push(rootEventId);
|
|
1644
|
+
}
|
|
1610
1645
|
}
|
|
1611
1646
|
}
|
|
1612
1647
|
const { chunk: thread } = await client.fetchThreadRelations({
|
|
@@ -1618,10 +1653,17 @@ async function rebuildThreadState(client, roomId, rootEventId, bindings) {
|
|
|
1618
1653
|
const mentions = new Set(extractMentions(ev));
|
|
1619
1654
|
const evSender = ev.sender;
|
|
1620
1655
|
const evSenderAgent = evSender ? bindings.find((b) => b.userId === evSender) : void 0;
|
|
1656
|
+
const evId = ev.event_id;
|
|
1621
1657
|
for (const a of bindings) {
|
|
1622
1658
|
if (!mentions.has(a.userId)) continue;
|
|
1623
1659
|
if (!state.rootMentions.includes(a.name)) state.rootMentions.push(a.name);
|
|
1624
|
-
if (evSenderAgent && a.name !== evSenderAgent.name)
|
|
1660
|
+
if (evSenderAgent && a.name !== evSenderAgent.name) {
|
|
1661
|
+
state.callers[a.name] = evSenderAgent.name;
|
|
1662
|
+
if (evId) {
|
|
1663
|
+
const arcs = state.handoffs[a.name] ??= [];
|
|
1664
|
+
if (!arcs.includes(evId)) arcs.push(evId);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1625
1667
|
}
|
|
1626
1668
|
const type = ev.type;
|
|
1627
1669
|
if (type === "m.room.message" && evSender) {
|