zooid 0.12.0 → 0.13.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.
@@ -25935,7 +25935,8 @@ var MatrixContextProvider = class {
25935
25935
  const threads = [];
25936
25936
  for (const ev of chunk) {
25937
25937
  if (ev.type !== "m.room.message") continue;
25938
- if (ev.content?.msgtype !== "m.text" || typeof ev.content.body !== "string") continue;
25938
+ if (ev.content?.msgtype !== "m.text" && ev.content?.msgtype !== "m.notice" || typeof ev.content.body !== "string")
25939
+ continue;
25939
25940
  const relatesTo = ev.content["m.relates_to"];
25940
25941
  if (relatesTo?.rel_type === "m.thread") continue;
25941
25942
  const agent = this.opts.agentBots.get(ev.sender);
@@ -26009,7 +26010,7 @@ var MatrixContextProvider = class {
26009
26010
  ...threadId !== void 0 ? { thread_id: threadId } : {}
26010
26011
  };
26011
26012
  }
26012
- if (msgtype !== "m.text" || typeof body !== "string") return null;
26013
+ if (msgtype !== "m.text" && msgtype !== "m.notice" || typeof body !== "string") return null;
26013
26014
  return {
26014
26015
  id: ev.event_id,
26015
26016
  sender: ev.sender,
@@ -26402,7 +26403,11 @@ var RECOVERY_URLS = {
26402
26403
  function toErrorBody(evt, threadRoot) {
26403
26404
  const msg = evt.message.slice(0, 250);
26404
26405
  const out = {
26405
- msgtype: "m.notice",
26406
+ // No msgtype: dev.zooid.error is not m.room.message, so the field is
26407
+ // meaningless here — it was a vestige of copying the message-body shape.
26408
+ // Its presence used to force careful push-rule `before` positioning
26409
+ // (ZNC025 §10); that positioning is kept regardless, since it also
26410
+ // protects rules for event types that never carried the field.
26406
26411
  body: `\u26A0 [${evt.code}] ${msg}`,
26407
26412
  code: evt.code,
26408
26413
  message: msg,
@@ -26417,6 +26422,25 @@ function toErrorBody(evt, threadRoot) {
26417
26422
  if (recovery) out.recovery = recovery;
26418
26423
  return out;
26419
26424
  }
26425
+ var PREVIEW_MAX = 140;
26426
+ function toTurnEndBody(evt, threadRoot) {
26427
+ const preview = evt.lastMessage?.trim().replace(/\s+/g, " ");
26428
+ return {
26429
+ // `body` stays the turn-boundary summary: it is what a generic Matrix
26430
+ // client renders for this event, and the prose is already its own message
26431
+ // in the timeline. The preview below exists only for the push, which
26432
+ // cannot see that message — agent prose is `m.notice`, deliberately
26433
+ // silenced by `.m.rule.suppress_notices` so a chatty turn doesn't fire one
26434
+ // push per chunk ([[ZNC025]] §10). Without it the only notification the
26435
+ // user gets says an agent finished and nothing about what it said.
26436
+ body: evt.producedOutput ? `${evt.agentId} finished` : `${evt.agentId} finished without output`,
26437
+ ...preview ? { last_message: preview.slice(0, PREVIEW_MAX) } : {},
26438
+ agent_id: evt.agentId,
26439
+ session_id: evt.sessionId,
26440
+ produced_output: evt.producedOutput,
26441
+ "m.relates_to": { rel_type: "m.thread", event_id: threadRoot }
26442
+ };
26443
+ }
26420
26444
 
26421
26445
  // ../transport-matrix/src/markdown-to-matrix-html.ts
26422
26446
  import { marked } from "marked";
@@ -26731,7 +26755,11 @@ function createMatrixTransport(opts) {
26731
26755
  const pendingCommands = /* @__PURE__ */ new Map();
26732
26756
  const buildTextContent = (text) => {
26733
26757
  const content = {
26734
- msgtype: "m.text",
26758
+ // m.notice, not m.text: .m.rule.suppress_notices silences the
26759
+ // chunk-storm of agent prose server-side (ZNC025 §10) instead of every
26760
+ // client having to filter it. dev.zooid.error carries the same tweak
26761
+ // for the same reason.
26762
+ msgtype: "m.notice",
26735
26763
  body: text
26736
26764
  };
26737
26765
  const html = toMatrixHtml(text);
@@ -26745,11 +26773,13 @@ function createMatrixTransport(opts) {
26745
26773
  }
26746
26774
  return content;
26747
26775
  };
26776
+ const lastFlushed = /* @__PURE__ */ new Map();
26748
26777
  const flushBuffer = (sessionId) => {
26749
26778
  const ctx = sessions.get(sessionId);
26750
26779
  const text = buffers.get(sessionId) ?? "";
26751
26780
  if (!ctx || text.length === 0) return false;
26752
26781
  buffers.set(sessionId, "");
26782
+ lastFlushed.set(sessionId, text);
26753
26783
  flushedCounts.set(sessionId, (flushedCounts.get(sessionId) ?? 0) + 1);
26754
26784
  const content = buildTextContent(text);
26755
26785
  const tail = (sendQueue.get(sessionId) ?? Promise.resolve()).then(async () => {
@@ -27162,19 +27192,30 @@ function createMatrixTransport(opts) {
27162
27192
  drained = next;
27163
27193
  }
27164
27194
  flushBuffer(sessionId);
27195
+ } finally {
27196
+ clearInterval(refresh);
27197
+ await safeTyping(false);
27198
+ await safePresence("online");
27165
27199
  await (sendQueue.get(sessionId) ?? Promise.resolve());
27166
- if ((flushedCounts.get(sessionId) ?? 0) === 0) {
27200
+ const producedOutput = (flushedCounts.get(sessionId) ?? 0) > 0;
27201
+ if (!producedOutput) {
27167
27202
  console.warn(
27168
27203
  `[matrix:${agent.name}] turn finished with empty buffer (session=${sessionId}); nothing sent to ${evt.room_id}`
27169
27204
  );
27170
27205
  }
27171
- } finally {
27172
- clearInterval(refresh);
27173
- await safeTyping(false);
27174
- await safePresence("online");
27206
+ await client.sendCustomEvent({
27207
+ roomId: evt.room_id,
27208
+ asUserId: agent.userId,
27209
+ eventType: "dev.zooid.turn.end",
27210
+ content: toTurnEndBody(
27211
+ { agentId: agent.name, sessionId, producedOutput, lastMessage: lastFlushed.get(sessionId) },
27212
+ threadRoot
27213
+ )
27214
+ }).catch((e) => console.warn(`[matrix:${agent.name}] turn.end send failed:`, e));
27175
27215
  buffers.delete(sessionId);
27176
27216
  bufferMessageIds.delete(sessionId);
27177
27217
  flushedCounts.delete(sessionId);
27218
+ lastFlushed.delete(sessionId);
27178
27219
  sendQueue.delete(sessionId);
27179
27220
  }
27180
27221
  }
@@ -27324,7 +27365,10 @@ import { unlink } from "fs/promises";
27324
27365
  async function startDaemonSocketServer(opts) {
27325
27366
  await unlink(opts.sockPath).catch(() => {
27326
27367
  });
27368
+ const open = /* @__PURE__ */ new Set();
27327
27369
  const server = createServer((socket) => {
27370
+ open.add(socket);
27371
+ socket.on("close", () => open.delete(socket));
27328
27372
  let buf = "";
27329
27373
  socket.setEncoding("utf8");
27330
27374
  socket.on("data", async (chunk) => {
@@ -27350,6 +27394,8 @@ async function startDaemonSocketServer(opts) {
27350
27394
  return {
27351
27395
  close: () => new Promise((resolve) => {
27352
27396
  server.close(() => resolve());
27397
+ for (const socket of open) socket.destroy();
27398
+ open.clear();
27353
27399
  })
27354
27400
  };
27355
27401
  }
@@ -32911,4 +32957,4 @@ export {
32911
32957
  startDaemonSocketServer,
32912
32958
  buildAcpRegistry
32913
32959
  };
32914
- //# sourceMappingURL=chunk-3Q4BPAZD.js.map
32960
+ //# sourceMappingURL=chunk-YZ4IO5MR.js.map