zooid 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +64 -28
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-R5S26T7B.js → chunk-35IKQPC5.js} +272 -65
- package/dist/chunk-35IKQPC5.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +10 -10
- package/src/daemon/start-daemon.ts +3 -0
- package/src/daemon/trigger-runner.mxid.test.ts +145 -0
- package/src/daemon/trigger-runner.test.ts +3 -4
- package/src/daemon/trigger-runner.ts +45 -7
- package/src/daemon/trigger-scheduler.ts +12 -7
- package/src/daemon/webhook-routes.test.ts +31 -1
- package/src/daemon/webhook-routes.ts +43 -12
- package/dist/chunk-R5S26T7B.js.map +0 -1
|
@@ -24368,14 +24368,13 @@ var AcpClient = class {
|
|
|
24368
24368
|
);
|
|
24369
24369
|
const persisted = this.store?.get(threadId);
|
|
24370
24370
|
if (persisted && this.agentCapabilities.loadSession) {
|
|
24371
|
+
let loaded;
|
|
24371
24372
|
try {
|
|
24372
|
-
await this.connection.loadSession({
|
|
24373
|
+
loaded = await this.connection.loadSession({
|
|
24373
24374
|
sessionId: persisted,
|
|
24374
24375
|
cwd: pathResolve(this.options.agent.cwd ?? process.cwd()),
|
|
24375
24376
|
mcpServers
|
|
24376
24377
|
});
|
|
24377
|
-
this.sessions.set(key, { sessionId: persisted, startedAt: Date.now() });
|
|
24378
|
-
return persisted;
|
|
24379
24378
|
} catch (err) {
|
|
24380
24379
|
console.warn(
|
|
24381
24380
|
`[acp-client:${this.options.agent.id}] loadSession(${persisted}) failed; falling back to newSession:`,
|
|
@@ -24383,15 +24382,43 @@ var AcpClient = class {
|
|
|
24383
24382
|
);
|
|
24384
24383
|
await this.store?.delete(threadId);
|
|
24385
24384
|
}
|
|
24385
|
+
if (loaded) {
|
|
24386
|
+
await this.applyMode(persisted, loaded.modes);
|
|
24387
|
+
this.sessions.set(key, { sessionId: persisted, startedAt: Date.now() });
|
|
24388
|
+
return persisted;
|
|
24389
|
+
}
|
|
24386
24390
|
}
|
|
24387
|
-
const { sessionId } = await this.connection.newSession({
|
|
24391
|
+
const { sessionId, modes } = await this.connection.newSession({
|
|
24388
24392
|
cwd: pathResolve(this.options.agent.cwd ?? process.cwd()),
|
|
24389
24393
|
mcpServers
|
|
24390
24394
|
});
|
|
24395
|
+
await this.applyMode(sessionId, modes);
|
|
24391
24396
|
this.sessions.set(key, { sessionId, startedAt: Date.now() });
|
|
24392
24397
|
await this.store?.set(threadId, sessionId);
|
|
24393
24398
|
return sessionId;
|
|
24394
24399
|
}
|
|
24400
|
+
/**
|
|
24401
|
+
* Put a freshly created or loaded session into the agent's configured mode.
|
|
24402
|
+
* Mode ids are adapter-defined, so an id the adapter doesn't list is a
|
|
24403
|
+
* config error: fail the session rather than run it in a mode nobody chose.
|
|
24404
|
+
*/
|
|
24405
|
+
async applyMode(sessionId, modes) {
|
|
24406
|
+
const wanted = this.options.agent.mode;
|
|
24407
|
+
if (!wanted || !this.connection) return;
|
|
24408
|
+
if (!modes) {
|
|
24409
|
+
throw new Error(
|
|
24410
|
+
`agents.${this.options.agent.id}.acp.mode "${wanted}": this agent does not offer session modes`
|
|
24411
|
+
);
|
|
24412
|
+
}
|
|
24413
|
+
if (!modes.availableModes.some((m) => m.id === wanted)) {
|
|
24414
|
+
const offered = modes.availableModes.map((m) => m.id).join(", ");
|
|
24415
|
+
throw new Error(
|
|
24416
|
+
`agents.${this.options.agent.id}.acp.mode "${wanted}": not offered by this agent (offers: ${offered})`
|
|
24417
|
+
);
|
|
24418
|
+
}
|
|
24419
|
+
if (modes.currentModeId === wanted) return;
|
|
24420
|
+
await this.connection.setSessionMode({ sessionId, modeId: wanted });
|
|
24421
|
+
}
|
|
24395
24422
|
async ensureStoreLoaded() {
|
|
24396
24423
|
if (!this.store) {
|
|
24397
24424
|
if (!this.options.agentDataDir) {
|
|
@@ -43471,6 +43498,13 @@ function parseAcpBlock(name, raw) {
|
|
|
43471
43498
|
`agents.${name}.acp: must specify either preset or command`
|
|
43472
43499
|
);
|
|
43473
43500
|
}
|
|
43501
|
+
let mode;
|
|
43502
|
+
if (a.mode !== void 0) {
|
|
43503
|
+
if (typeof a.mode !== "string" || a.mode.trim().length === 0) {
|
|
43504
|
+
throw new Error(`agents.${name}.acp.mode: must be a non-empty string`);
|
|
43505
|
+
}
|
|
43506
|
+
mode = a.mode.trim();
|
|
43507
|
+
}
|
|
43474
43508
|
if (hasPreset) {
|
|
43475
43509
|
if (typeof a.preset !== "string" || a.preset.length === 0) {
|
|
43476
43510
|
throw new Error(`agents.${name}.acp.preset: must be a non-empty string`);
|
|
@@ -43487,6 +43521,7 @@ function parseAcpBlock(name, raw) {
|
|
|
43487
43521
|
}
|
|
43488
43522
|
out.model = a.model.trim();
|
|
43489
43523
|
}
|
|
43524
|
+
if (mode !== void 0) out.mode = mode;
|
|
43490
43525
|
return out;
|
|
43491
43526
|
}
|
|
43492
43527
|
if (typeof a.command !== "string" || a.command.length === 0) {
|
|
@@ -43504,7 +43539,7 @@ function parseAcpBlock(name, raw) {
|
|
|
43504
43539
|
args.push(v);
|
|
43505
43540
|
}
|
|
43506
43541
|
}
|
|
43507
|
-
return { command: a.command, args };
|
|
43542
|
+
return mode === void 0 ? { command: a.command, args } : { command: a.command, args, mode };
|
|
43508
43543
|
}
|
|
43509
43544
|
function parseApprovalTimeout(name, raw) {
|
|
43510
43545
|
if (raw === void 0) return 0;
|
|
@@ -44188,8 +44223,17 @@ function parseTriggers(raw, agents, transports, validateCron, processEnv, config
|
|
|
44188
44223
|
}
|
|
44189
44224
|
rawMessages = t.messages;
|
|
44190
44225
|
}
|
|
44226
|
+
let ttlMs;
|
|
44227
|
+
if (t.expires_after !== void 0) {
|
|
44228
|
+
if (typeof t.expires_after !== "string" || t.expires_after.length === 0) {
|
|
44229
|
+
throw new Error(`triggers.${name}.expires_after: must be a non-empty duration string`);
|
|
44230
|
+
}
|
|
44231
|
+
ttlMs = parseDurationMs(`triggers.${name}.expires_after`, t.expires_after);
|
|
44232
|
+
} else {
|
|
44233
|
+
ttlMs = webhook ? void 0 : 6 * 36e5;
|
|
44234
|
+
}
|
|
44191
44235
|
const messages = rawMessages.map(
|
|
44192
|
-
(m, i) => parseTriggerMessage(name, i, m, agents, as, !!webhook)
|
|
44236
|
+
(m, i) => parseTriggerMessage(name, i, m, agents, transports, as, !!webhook, ttlMs)
|
|
44193
44237
|
);
|
|
44194
44238
|
const entry = { as, messages };
|
|
44195
44239
|
if (schedule !== void 0) entry.schedule = schedule;
|
|
@@ -44198,7 +44242,34 @@ function parseTriggers(raw, agents, transports, validateCron, processEnv, config
|
|
|
44198
44242
|
}
|
|
44199
44243
|
return result;
|
|
44200
44244
|
}
|
|
44201
|
-
function
|
|
44245
|
+
function parseDurationMs(label, raw) {
|
|
44246
|
+
const m = /^(\d+)(s|m|h)$/.exec(raw);
|
|
44247
|
+
if (!m) {
|
|
44248
|
+
throw new Error(`${label}: "${raw}" is not a valid duration (use "<n>s", "<n>m", or "<n>h")`);
|
|
44249
|
+
}
|
|
44250
|
+
const n = Number(m[1]);
|
|
44251
|
+
switch (m[2]) {
|
|
44252
|
+
case "s":
|
|
44253
|
+
return n * 1e3;
|
|
44254
|
+
case "m":
|
|
44255
|
+
return n * 6e4;
|
|
44256
|
+
case "h":
|
|
44257
|
+
return n * 60 * 6e4;
|
|
44258
|
+
}
|
|
44259
|
+
throw new Error("unreachable");
|
|
44260
|
+
}
|
|
44261
|
+
function soleMatrixTransport(transports) {
|
|
44262
|
+
const matrixTransports = Object.values(transports).filter(
|
|
44263
|
+
(t) => t.type === "matrix"
|
|
44264
|
+
);
|
|
44265
|
+
if (matrixTransports.length !== 1) {
|
|
44266
|
+
throw new Error(
|
|
44267
|
+
`triggers: a full-MXID mention requires exactly one matrix transport to validate its homeserver against (found ${matrixTransports.length})`
|
|
44268
|
+
);
|
|
44269
|
+
}
|
|
44270
|
+
return matrixTransports[0];
|
|
44271
|
+
}
|
|
44272
|
+
function parseTriggerMessage(name, index, raw, agents, transports, as, hasWebhook, ttlMs) {
|
|
44202
44273
|
const label = `triggers.${name}.messages[${index}]`;
|
|
44203
44274
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
44204
44275
|
throw new Error(`${label}: must be a mapping`);
|
|
@@ -44213,24 +44284,43 @@ function parseTriggerMessage(name, index, raw, agents, as, hasWebhook) {
|
|
|
44213
44284
|
if (typeof m.mention !== "string" || m.mention.length === 0) {
|
|
44214
44285
|
throw new Error(`${label}.mention: must be a non-empty string`);
|
|
44215
44286
|
}
|
|
44216
|
-
|
|
44217
|
-
|
|
44218
|
-
|
|
44219
|
-
|
|
44220
|
-
|
|
44221
|
-
|
|
44222
|
-
|
|
44223
|
-
|
|
44224
|
-
|
|
44225
|
-
|
|
44226
|
-
|
|
44227
|
-
|
|
44228
|
-
|
|
44287
|
+
if (m.mention.startsWith("@")) {
|
|
44288
|
+
if (!MATRIX_USER_ID_RE.test(m.mention)) {
|
|
44289
|
+
throw new Error(`${label}.mention: must be a full MXID (got ${JSON.stringify(m.mention)})`);
|
|
44290
|
+
}
|
|
44291
|
+
const server = m.mention.slice(m.mention.indexOf(":") + 1);
|
|
44292
|
+
const local = deriveServerName(soleMatrixTransport(transports).user_namespace);
|
|
44293
|
+
if (server !== local) {
|
|
44294
|
+
throw new Error(
|
|
44295
|
+
`${label}.mention: "${m.mention}" is on a different homeserver than this daemon (${local}). A trigger addresses a user on its own homeserver \u2014 see [[ZOD081]] \xA7Non-goals.`
|
|
44296
|
+
);
|
|
44297
|
+
}
|
|
44298
|
+
if (as === m.mention) {
|
|
44299
|
+
throw new Error(
|
|
44300
|
+
`triggers.${name}.as: must not equal the mentioned agent's own MXID (${as}) \u2014 a message an agent sends never routes back to itself, so this trigger would silently never wake "${m.mention}". Post as a different identity (a dedicated bot, or another agent's).`
|
|
44301
|
+
);
|
|
44302
|
+
}
|
|
44303
|
+
} else {
|
|
44304
|
+
const mentionedAgent = agents[m.mention];
|
|
44305
|
+
if (!mentionedAgent) {
|
|
44306
|
+
throw new Error(`${label}.mention: unknown agent "${m.mention}"`);
|
|
44307
|
+
}
|
|
44308
|
+
if (!mentionedAgent.matrix) {
|
|
44309
|
+
throw new Error(
|
|
44310
|
+
`${label}.mention: agent "${m.mention}" has no matrix: binding \u2014 a trigger posts through Matrix, so the mentioned agent must be matrix-bound.`
|
|
44311
|
+
);
|
|
44312
|
+
}
|
|
44313
|
+
if (as === mentionedAgent.matrix.user_id) {
|
|
44314
|
+
throw new Error(
|
|
44315
|
+
`triggers.${name}.as: must not equal the mentioned agent's own MXID (${as}) \u2014 a message an agent sends never routes back to itself, so this trigger would silently never wake "${m.mention}". Post as a different identity (a dedicated bot, or another agent's).`
|
|
44316
|
+
);
|
|
44317
|
+
}
|
|
44229
44318
|
}
|
|
44230
44319
|
if (typeof m.text !== "string" || m.text.length === 0) {
|
|
44231
44320
|
throw new Error(`${label}.text: must be a non-empty string`);
|
|
44232
44321
|
}
|
|
44233
44322
|
const message = { room: m.room, mention: m.mention, text: m.text };
|
|
44323
|
+
if (ttlMs !== void 0) message.ttlMs = ttlMs;
|
|
44234
44324
|
if (m.match !== void 0) {
|
|
44235
44325
|
if (typeof m.match !== "string" || m.match.length === 0) {
|
|
44236
44326
|
throw new Error(`${label}.match: must be a non-empty string`);
|
|
@@ -44552,6 +44642,7 @@ var AcpAgentRegistry = class {
|
|
|
44552
44642
|
id: name,
|
|
44553
44643
|
command: spawn4.command,
|
|
44554
44644
|
args: spawn4.args,
|
|
44645
|
+
mode: cfg.acp.mode,
|
|
44555
44646
|
env: this.opts.env?.[name],
|
|
44556
44647
|
cwd: this.resolveSpawnCwd(name),
|
|
44557
44648
|
image: this.opts.image?.[name],
|
|
@@ -44960,6 +45051,15 @@ var MatrixClient = class {
|
|
|
44960
45051
|
if (!r.ok) throw new Error(`sync(${opts.asUserId}) failed: ${r.status}`);
|
|
44961
45052
|
return r.json();
|
|
44962
45053
|
}
|
|
45054
|
+
async fetchRoomState(roomId, asUserId) {
|
|
45055
|
+
const url2 = `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state?user_id=${encodeURIComponent(asUserId)}`;
|
|
45056
|
+
const r = await this.fetch(url2, {
|
|
45057
|
+
method: "GET",
|
|
45058
|
+
headers: { Authorization: `Bearer ${this.asToken}` }
|
|
45059
|
+
});
|
|
45060
|
+
if (!r.ok) throw new Error(`fetchRoomState(${roomId}) failed: ${r.status}`);
|
|
45061
|
+
return await r.json();
|
|
45062
|
+
}
|
|
44963
45063
|
async fetchRoomName(roomId, asUserId) {
|
|
44964
45064
|
const url2 = `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state/m.room.name/?user_id=${encodeURIComponent(asUserId)}`;
|
|
44965
45065
|
const r = await this.fetch(url2, {
|
|
@@ -45201,6 +45301,24 @@ function stripMention(body, userId) {
|
|
|
45201
45301
|
return body.replace(re, " ").replace(/\s+/g, " ").trim();
|
|
45202
45302
|
}
|
|
45203
45303
|
|
|
45304
|
+
// ../transport-matrix/src/mention-content.ts
|
|
45305
|
+
function buildMentionContent(input) {
|
|
45306
|
+
return {
|
|
45307
|
+
msgtype: input.msgtype,
|
|
45308
|
+
body: `${input.userId} ${input.text}`.trim(),
|
|
45309
|
+
"m.mentions": { user_ids: [input.userId] }
|
|
45310
|
+
};
|
|
45311
|
+
}
|
|
45312
|
+
|
|
45313
|
+
// ../transport-matrix/src/trigger-freshness.ts
|
|
45314
|
+
function isExpiredTrigger(evt, now) {
|
|
45315
|
+
const stamp = evt.content?.["dev.zooid.trigger"];
|
|
45316
|
+
if (!stamp || typeof stamp !== "object") return false;
|
|
45317
|
+
const { fired_at: firedAt, ttl_ms: ttlMs } = stamp;
|
|
45318
|
+
if (typeof firedAt !== "number" || typeof ttlMs !== "number") return false;
|
|
45319
|
+
return now - firedAt > ttlMs;
|
|
45320
|
+
}
|
|
45321
|
+
|
|
45204
45322
|
// ../transport-matrix/src/router.ts
|
|
45205
45323
|
var MEDIA_MSGTYPES = /* @__PURE__ */ new Set(["m.image", "m.file", "m.video", "m.audio"]);
|
|
45206
45324
|
function isMediaMsgtype(t) {
|
|
@@ -45210,14 +45328,26 @@ function inboundThreadRoot(event) {
|
|
|
45210
45328
|
const r = event.content?.["m.relates_to"];
|
|
45211
45329
|
return r?.rel_type === "m.thread" && r.event_id ? r.event_id : void 0;
|
|
45212
45330
|
}
|
|
45213
|
-
function route(event, agents, threadStates, task) {
|
|
45331
|
+
function route(event, agents, threadStates, task, knownAgentIds) {
|
|
45214
45332
|
if (event.type !== "m.room.message") return [];
|
|
45215
45333
|
if (!event.content?.msgtype) return [];
|
|
45216
45334
|
if (isMediaMsgtype(event.content.msgtype)) return [];
|
|
45335
|
+
if (isExpiredTrigger(event, Date.now())) {
|
|
45336
|
+
const stamp = event.content["dev.zooid.trigger"];
|
|
45337
|
+
const ageMs = stamp?.fired_at !== void 0 ? Date.now() - stamp.fired_at : void 0;
|
|
45338
|
+
console.info(
|
|
45339
|
+
`[router] dropping expired trigger "${stamp?.name ?? "unknown"}"` + (ageMs !== void 0 ? ` fired ${ageMs}ms ago` : "")
|
|
45340
|
+
);
|
|
45341
|
+
return [];
|
|
45342
|
+
}
|
|
45217
45343
|
const mentions = new Set(extractMentions(event));
|
|
45218
45344
|
const matches2 = [];
|
|
45219
45345
|
const threadRoot = inboundThreadRoot(event);
|
|
45220
45346
|
const threadState = threadRoot ? threadStates?.get(threadRoot) : void 0;
|
|
45347
|
+
const senderIsAgent = agents.some((x) => x.userId === event.sender) || event.sender !== void 0 && knownAgentIds?.has(event.sender) === true || event.content.msgtype === "m.notice";
|
|
45348
|
+
const addressesAgent = [...mentions].some(
|
|
45349
|
+
(id) => id !== event.sender && (knownAgentIds?.has(id) === true || agents.some((x) => x.userId === id && x.rooms.some((r) => r.alias === event.room_id)))
|
|
45350
|
+
);
|
|
45221
45351
|
for (const a of agents) {
|
|
45222
45352
|
if (!a.rooms.some((r) => r.alias === event.room_id)) continue;
|
|
45223
45353
|
if (task?.isRoot) {
|
|
@@ -45230,10 +45360,9 @@ function route(event, agents, threadStates, task) {
|
|
|
45230
45360
|
matches2.push(a);
|
|
45231
45361
|
continue;
|
|
45232
45362
|
}
|
|
45233
|
-
|
|
45234
|
-
if (senderAgent) {
|
|
45363
|
+
if (senderIsAgent) {
|
|
45235
45364
|
continue;
|
|
45236
|
-
} else if (a.name === task.assignee) {
|
|
45365
|
+
} else if (a.name === task.assignee && !addressesAgent) {
|
|
45237
45366
|
matches2.push(a);
|
|
45238
45367
|
}
|
|
45239
45368
|
continue;
|
|
@@ -45247,10 +45376,9 @@ function route(event, agents, threadStates, task) {
|
|
|
45247
45376
|
continue;
|
|
45248
45377
|
}
|
|
45249
45378
|
if (threadState) {
|
|
45250
|
-
|
|
45251
|
-
if (senderAgent) {
|
|
45379
|
+
if (senderIsAgent) {
|
|
45252
45380
|
if (isReturnRoute(event, a, agents, threadState)) matches2.push(a);
|
|
45253
|
-
} else {
|
|
45381
|
+
} else if (!addressesAgent) {
|
|
45254
45382
|
const lastPoster = threadState.participants.at(-1);
|
|
45255
45383
|
if (lastPoster) {
|
|
45256
45384
|
if (lastPoster === a.name) matches2.push(a);
|
|
@@ -45484,6 +45612,57 @@ function sessionKeyFor(agentName, threadRoot, state) {
|
|
|
45484
45612
|
return arc ? composeHandoffKey(threadRoot, arc) : threadRoot;
|
|
45485
45613
|
}
|
|
45486
45614
|
|
|
45615
|
+
// ../transport-matrix/src/workforce-publisher.ts
|
|
45616
|
+
function buildWorkforceRoster(agents) {
|
|
45617
|
+
return {
|
|
45618
|
+
version: 1,
|
|
45619
|
+
agents: agents.map((a) => ({
|
|
45620
|
+
user_id: a.userId,
|
|
45621
|
+
name: a.name,
|
|
45622
|
+
rooms: a.rooms.map((r) => r.alias)
|
|
45623
|
+
}))
|
|
45624
|
+
};
|
|
45625
|
+
}
|
|
45626
|
+
async function publishWorkforce(opts) {
|
|
45627
|
+
await opts.client.sendStateEvent({
|
|
45628
|
+
roomId: opts.spaceRoomId,
|
|
45629
|
+
asUserId: opts.asUserId,
|
|
45630
|
+
eventType: "dev.zooid.workforce",
|
|
45631
|
+
stateKey: opts.stateKey ?? "",
|
|
45632
|
+
content: buildWorkforceRoster(opts.agents)
|
|
45633
|
+
});
|
|
45634
|
+
}
|
|
45635
|
+
async function startWorkforcePublisher(opts) {
|
|
45636
|
+
await publishWorkforce({ ...opts, agents: opts.getAgents() });
|
|
45637
|
+
return {
|
|
45638
|
+
async reload() {
|
|
45639
|
+
await publishWorkforce({ ...opts, agents: opts.getAgents() });
|
|
45640
|
+
},
|
|
45641
|
+
async stop() {
|
|
45642
|
+
}
|
|
45643
|
+
};
|
|
45644
|
+
}
|
|
45645
|
+
var WorkforceDirectory = class {
|
|
45646
|
+
rosters = /* @__PURE__ */ new Map();
|
|
45647
|
+
agentIds = /* @__PURE__ */ new Set();
|
|
45648
|
+
/** Seed from a space's full state (`GET /rooms/{id}/state`). */
|
|
45649
|
+
load(events) {
|
|
45650
|
+
for (const ev of events) {
|
|
45651
|
+
if (ev.type === "dev.zooid.workforce" && ev.state_key !== void 0)
|
|
45652
|
+
this.apply(ev.state_key, ev.content);
|
|
45653
|
+
}
|
|
45654
|
+
}
|
|
45655
|
+
/** Replace one workstation's roster; empty content removes it. */
|
|
45656
|
+
apply(stateKey, content) {
|
|
45657
|
+
const agents = content?.agents;
|
|
45658
|
+
const ids = Array.isArray(agents) ? agents.map((a) => a?.user_id).filter((id) => typeof id === "string") : [];
|
|
45659
|
+
if (ids.length > 0) this.rosters.set(stateKey, ids);
|
|
45660
|
+
else this.rosters.delete(stateKey);
|
|
45661
|
+
this.agentIds.clear();
|
|
45662
|
+
for (const list of this.rosters.values()) for (const id of list) this.agentIds.add(id);
|
|
45663
|
+
}
|
|
45664
|
+
};
|
|
45665
|
+
|
|
45487
45666
|
// ../transport-matrix/src/event-encoders.ts
|
|
45488
45667
|
var RAW_INPUT_STR_MAX = 250;
|
|
45489
45668
|
function toToolCallBody(evt) {
|
|
@@ -45763,7 +45942,33 @@ var SyncLoop = class {
|
|
|
45763
45942
|
timeoutMs: this.opts.timeoutMs ?? 3e4
|
|
45764
45943
|
});
|
|
45765
45944
|
for (const [roomId, roomState] of Object.entries(res.rooms?.join ?? {})) {
|
|
45766
|
-
|
|
45945
|
+
const tl = roomState.timeline;
|
|
45946
|
+
if (tl?.limited && tl.prev_batch && since) {
|
|
45947
|
+
const recovered = [];
|
|
45948
|
+
let from = tl.prev_batch;
|
|
45949
|
+
let pages = 0;
|
|
45950
|
+
const max = this.opts.maxGapPages ?? 10;
|
|
45951
|
+
while (from && from !== since && pages < max) {
|
|
45952
|
+
const page = await this.opts.client.fetchRoomMessages({
|
|
45953
|
+
roomId,
|
|
45954
|
+
asUserId: this.opts.asUserId,
|
|
45955
|
+
from,
|
|
45956
|
+
limit: 100
|
|
45957
|
+
});
|
|
45958
|
+
recovered.push(...page.chunk);
|
|
45959
|
+
from = page.end;
|
|
45960
|
+
pages++;
|
|
45961
|
+
}
|
|
45962
|
+
if (pages >= max && from !== since) {
|
|
45963
|
+
console.warn(`[sync-loop] gap in ${roomId} exceeded ${max} pages \u2014 older events skipped`, {
|
|
45964
|
+
asUserId: this.opts.asUserId
|
|
45965
|
+
});
|
|
45966
|
+
}
|
|
45967
|
+
for (const evt of recovered.reverse()) {
|
|
45968
|
+
await this.opts.onEvent({ ...evt, room_id: roomId });
|
|
45969
|
+
}
|
|
45970
|
+
}
|
|
45971
|
+
for (const baseEvt of tl?.events ?? []) {
|
|
45767
45972
|
await this.opts.onEvent({ ...baseEvt, room_id: roomId });
|
|
45768
45973
|
}
|
|
45769
45974
|
}
|
|
@@ -46466,6 +46671,8 @@ function createMatrixTransport(opts) {
|
|
|
46466
46671
|
});
|
|
46467
46672
|
return chained;
|
|
46468
46673
|
}
|
|
46674
|
+
const workforce = new WorkforceDirectory();
|
|
46675
|
+
let workforceSpaceId;
|
|
46469
46676
|
async function handleInboundEvent(evt) {
|
|
46470
46677
|
if (evt.event_id) {
|
|
46471
46678
|
if (seenEventIds.has(evt.event_id)) {
|
|
@@ -46483,6 +46690,11 @@ function createMatrixTransport(opts) {
|
|
|
46483
46690
|
);
|
|
46484
46691
|
return;
|
|
46485
46692
|
}
|
|
46693
|
+
if (evt.type === "dev.zooid.workforce") {
|
|
46694
|
+
if (evt.room_id === workforceSpaceId && evt.state_key !== void 0)
|
|
46695
|
+
workforce.apply(evt.state_key, evt.content);
|
|
46696
|
+
return;
|
|
46697
|
+
}
|
|
46486
46698
|
if (evt.type === "m.room.member" && evt.content?.membership === "invite") {
|
|
46487
46699
|
const target = evt.state_key;
|
|
46488
46700
|
const inviter = evt.sender;
|
|
@@ -46610,7 +46822,13 @@ function createMatrixTransport(opts) {
|
|
|
46610
46822
|
const inboundRel = inboundThreadRoot2(evt);
|
|
46611
46823
|
if (evt.type === "m.room.message" && inboundRel && !threadStates.has(inboundRel) && evt.room_id) {
|
|
46612
46824
|
try {
|
|
46613
|
-
const rebuilt = await rebuildThreadState(
|
|
46825
|
+
const rebuilt = await rebuildThreadState(
|
|
46826
|
+
client,
|
|
46827
|
+
evt.room_id,
|
|
46828
|
+
inboundRel,
|
|
46829
|
+
bindings,
|
|
46830
|
+
workforce.agentIds
|
|
46831
|
+
);
|
|
46614
46832
|
threadStates.set(inboundRel, rebuilt);
|
|
46615
46833
|
console.log(
|
|
46616
46834
|
`[matrix] rebuilt threadState for ${inboundRel}: participants=${rebuilt.participants.join(",")} rootMentions=${rebuilt.rootMentions.join(",")}`
|
|
@@ -46628,7 +46846,10 @@ function createMatrixTransport(opts) {
|
|
|
46628
46846
|
assignee: taskRec.assignee,
|
|
46629
46847
|
isRoot: !inboundRel && evt.event_id === taskRec.threadRoot
|
|
46630
46848
|
} : void 0;
|
|
46631
|
-
|
|
46849
|
+
const inboundState = inboundRel ? threadStates.get(inboundRel) : void 0;
|
|
46850
|
+
if (inboundState && evt.type === "m.room.message" && evt.sender && isRemoteAgent(evt.sender, evt.content?.msgtype, bindings, workforce.agentIds) && inboundState.participants.at(-1) !== evt.sender)
|
|
46851
|
+
inboundState.participants.push(evt.sender);
|
|
46852
|
+
let matches2 = route(evt, bindings, threadStates, taskCtx, workforce.agentIds);
|
|
46632
46853
|
if (taskCtx && !taskCtx.isRoot && evt.event_id && bindings.some((b) => b.userId === evt.sender)) {
|
|
46633
46854
|
const invocation = invocations.byCallEvent(evt.event_id);
|
|
46634
46855
|
matches2 = invocation ? matches2.filter((match) => match.name === invocation.calleeAgent) : [];
|
|
@@ -47099,6 +47320,15 @@ function createMatrixTransport(opts) {
|
|
|
47099
47320
|
syncLoops,
|
|
47100
47321
|
bootstrap: async (bootstrapOpts = {}) => {
|
|
47101
47322
|
await pool.bootstrap({ adminUserId, ...bootstrapOpts });
|
|
47323
|
+
const { spaceRoomId, asUserId } = bootstrapOpts;
|
|
47324
|
+
if (spaceRoomId && asUserId) {
|
|
47325
|
+
workforceSpaceId = spaceRoomId;
|
|
47326
|
+
try {
|
|
47327
|
+
workforce.load(await client.fetchRoomState(spaceRoomId, asUserId));
|
|
47328
|
+
} catch (err) {
|
|
47329
|
+
console.warn("[matrix] workforce roster load failed:", err);
|
|
47330
|
+
}
|
|
47331
|
+
}
|
|
47102
47332
|
await Promise.allSettled(
|
|
47103
47333
|
bindings.map(
|
|
47104
47334
|
(b) => client.setPresence({ asUserId: b.userId, presence: "online" }).catch((err) => {
|
|
@@ -47110,7 +47340,7 @@ function createMatrixTransport(opts) {
|
|
|
47110
47340
|
pool
|
|
47111
47341
|
};
|
|
47112
47342
|
}
|
|
47113
|
-
async function rebuildThreadState(client, roomId, rootEventId, bindings) {
|
|
47343
|
+
async function rebuildThreadState(client, roomId, rootEventId, bindings, knownAgentIds) {
|
|
47114
47344
|
const state = {
|
|
47115
47345
|
participants: [],
|
|
47116
47346
|
rootMentions: [],
|
|
@@ -47158,11 +47388,18 @@ async function rebuildThreadState(client, roomId, rootEventId, bindings) {
|
|
|
47158
47388
|
const type = ev.type;
|
|
47159
47389
|
if (type === "m.room.message" && evSender) {
|
|
47160
47390
|
const a = bindings.find((b) => b.userId === evSender);
|
|
47161
|
-
|
|
47391
|
+
const msgtype = ev.content?.msgtype;
|
|
47392
|
+
const participant = a ? a.name : isRemoteAgent(evSender, msgtype, bindings, knownAgentIds) ? evSender : void 0;
|
|
47393
|
+
if (participant && state.participants.at(-1) !== participant)
|
|
47394
|
+
state.participants.push(participant);
|
|
47162
47395
|
}
|
|
47163
47396
|
}
|
|
47164
47397
|
return state;
|
|
47165
47398
|
}
|
|
47399
|
+
function isRemoteAgent(sender, msgtype, bindings, knownAgentIds) {
|
|
47400
|
+
if (bindings.some((b) => b.userId === sender)) return false;
|
|
47401
|
+
return knownAgentIds?.has(sender) === true || msgtype === "m.notice";
|
|
47402
|
+
}
|
|
47166
47403
|
function logInbound(evt) {
|
|
47167
47404
|
const sender = evt.sender ?? "?";
|
|
47168
47405
|
const room = evt.room_id ?? "?";
|
|
@@ -47182,37 +47419,6 @@ function truncate(s, n) {
|
|
|
47182
47419
|
return s.length > n ? s.slice(0, n) + "\u2026" : s;
|
|
47183
47420
|
}
|
|
47184
47421
|
|
|
47185
|
-
// ../transport-matrix/src/workforce-publisher.ts
|
|
47186
|
-
function buildWorkforceRoster(agents) {
|
|
47187
|
-
return {
|
|
47188
|
-
version: 1,
|
|
47189
|
-
agents: agents.map((a) => ({
|
|
47190
|
-
user_id: a.userId,
|
|
47191
|
-
name: a.name,
|
|
47192
|
-
rooms: a.rooms.map((r) => r.alias)
|
|
47193
|
-
}))
|
|
47194
|
-
};
|
|
47195
|
-
}
|
|
47196
|
-
async function publishWorkforce(opts) {
|
|
47197
|
-
await opts.client.sendStateEvent({
|
|
47198
|
-
roomId: opts.spaceRoomId,
|
|
47199
|
-
asUserId: opts.asUserId,
|
|
47200
|
-
eventType: "dev.zooid.workforce",
|
|
47201
|
-
stateKey: "",
|
|
47202
|
-
content: buildWorkforceRoster(opts.agents)
|
|
47203
|
-
});
|
|
47204
|
-
}
|
|
47205
|
-
async function startWorkforcePublisher(opts) {
|
|
47206
|
-
await publishWorkforce({ ...opts, agents: opts.getAgents() });
|
|
47207
|
-
return {
|
|
47208
|
-
async reload() {
|
|
47209
|
-
await publishWorkforce({ ...opts, agents: opts.getAgents() });
|
|
47210
|
-
},
|
|
47211
|
-
async stop() {
|
|
47212
|
-
}
|
|
47213
|
-
};
|
|
47214
|
-
}
|
|
47215
|
-
|
|
47216
47422
|
// ../context-mcp/src/spawn-registry.ts
|
|
47217
47423
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
47218
47424
|
var SpawnRegistry = class {
|
|
@@ -52963,14 +53169,15 @@ export {
|
|
|
52963
53169
|
ApprovalCorrelator,
|
|
52964
53170
|
MatrixClient,
|
|
52965
53171
|
renderRegistration,
|
|
53172
|
+
buildMentionContent,
|
|
52966
53173
|
ensureWorkforceSpace,
|
|
52967
53174
|
ensureDefaultChannel,
|
|
53175
|
+
startWorkforcePublisher,
|
|
52968
53176
|
MediaClient,
|
|
52969
53177
|
createMatrixTransport,
|
|
52970
|
-
startWorkforcePublisher,
|
|
52971
53178
|
SpawnRegistry,
|
|
52972
53179
|
startAgentSocketServers,
|
|
52973
53180
|
buildAcpRegistry,
|
|
52974
53181
|
contextEligibleAgents
|
|
52975
53182
|
};
|
|
52976
|
-
//# sourceMappingURL=chunk-
|
|
53183
|
+
//# sourceMappingURL=chunk-35IKQPC5.js.map
|