zooid 0.11.2 → 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.
Files changed (40) hide show
  1. package/README.md +8 -3
  2. package/dist/bin.js +503 -124
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-KGYQ5YNP.js → chunk-YZ4IO5MR.js} +78 -11
  5. package/dist/chunk-YZ4IO5MR.js.map +1 -0
  6. package/dist/index.js +1 -1
  7. package/package.json +11 -9
  8. package/src/bin.test.ts +63 -0
  9. package/src/bin.ts +55 -6
  10. package/src/bootstrap/configs.test.ts +7 -0
  11. package/src/bootstrap/configs.ts +14 -0
  12. package/src/build-registry.ts +1 -1
  13. package/src/build-registry.zod044.test.ts +23 -0
  14. package/src/commands/dev.ts +37 -7
  15. package/src/commands/init/generators.test.ts +43 -0
  16. package/src/commands/init/generators.ts +24 -5
  17. package/src/commands/init/pi-scaffold.test.ts +151 -0
  18. package/src/commands/init/prompts.ts +53 -2
  19. package/src/commands/init/registry.test.ts +60 -0
  20. package/src/commands/init/registry.ts +58 -0
  21. package/src/commands/init/sniff.test.ts +61 -2
  22. package/src/commands/init/sniff.ts +43 -12
  23. package/src/commands/init.ts +91 -5
  24. package/src/commands/status.test.ts +15 -0
  25. package/src/commands/status.ts +27 -2
  26. package/src/daemon/start-daemon.ts +15 -1
  27. package/src/push-gateway/gateway.test.ts +150 -0
  28. package/src/push-gateway/gateway.ts +78 -0
  29. package/src/push-gateway/index.ts +17 -0
  30. package/src/push-gateway/payload.test.ts +88 -0
  31. package/src/push-gateway/payload.ts +39 -0
  32. package/src/push-gateway/types.ts +37 -0
  33. package/src/push-gateway/vapid.test.ts +45 -0
  34. package/src/push-gateway/vapid.ts +34 -0
  35. package/src/services/tuwunel.ts +21 -2
  36. package/src/version.test.ts +67 -0
  37. package/src/version.ts +30 -0
  38. package/src/web/static.test.ts +22 -0
  39. package/src/web/static.ts +9 -1
  40. package/dist/chunk-KGYQ5YNP.js.map +0 -1
@@ -24056,6 +24056,27 @@ var PRESETS_INTERNAL = {
24056
24056
  create: false
24057
24057
  }
24058
24058
  ]
24059
+ },
24060
+ // pi (ZOD073). Vendored-shim category: `pi-acp` is an adapter that speaks ACP
24061
+ // on pi's behalf and spawns `pi --mode rpc` as a child. No flags — its
24062
+ // published Zed config is `args: []`, and the only documented flag
24063
+ // (--terminal-login) is an interactive auth path with no place in a daemon.
24064
+ pi: {
24065
+ command: "npx",
24066
+ args: ["-y", "pi-acp"],
24067
+ image: "ghcr.io/zooid-ai/agent-pi:latest",
24068
+ mounts: (ctx) => [
24069
+ {
24070
+ // Single tree: agent/sessions, agent/prompts, and the adapter's
24071
+ // pi-acp/session-map.json. Project config lives in <cwd>/.pi, which is
24072
+ // already inside the workspace mount.
24073
+ id: "home",
24074
+ host: `${ctx.daemonHome}/.pi`,
24075
+ target: "/root/.pi",
24076
+ mode: "rw",
24077
+ create: false
24078
+ }
24079
+ ]
24059
24080
  }
24060
24081
  };
24061
24082
  var PRESETS = Object.freeze(
@@ -25914,7 +25935,8 @@ var MatrixContextProvider = class {
25914
25935
  const threads = [];
25915
25936
  for (const ev of chunk) {
25916
25937
  if (ev.type !== "m.room.message") continue;
25917
- 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;
25918
25940
  const relatesTo = ev.content["m.relates_to"];
25919
25941
  if (relatesTo?.rel_type === "m.thread") continue;
25920
25942
  const agent = this.opts.agentBots.get(ev.sender);
@@ -25988,7 +26010,7 @@ var MatrixContextProvider = class {
25988
26010
  ...threadId !== void 0 ? { thread_id: threadId } : {}
25989
26011
  };
25990
26012
  }
25991
- if (msgtype !== "m.text" || typeof body !== "string") return null;
26013
+ if (msgtype !== "m.text" && msgtype !== "m.notice" || typeof body !== "string") return null;
25992
26014
  return {
25993
26015
  id: ev.event_id,
25994
26016
  sender: ev.sender,
@@ -26381,7 +26403,11 @@ var RECOVERY_URLS = {
26381
26403
  function toErrorBody(evt, threadRoot) {
26382
26404
  const msg = evt.message.slice(0, 250);
26383
26405
  const out = {
26384
- 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.
26385
26411
  body: `\u26A0 [${evt.code}] ${msg}`,
26386
26412
  code: evt.code,
26387
26413
  message: msg,
@@ -26396,6 +26422,25 @@ function toErrorBody(evt, threadRoot) {
26396
26422
  if (recovery) out.recovery = recovery;
26397
26423
  return out;
26398
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
+ }
26399
26444
 
26400
26445
  // ../transport-matrix/src/markdown-to-matrix-html.ts
26401
26446
  import { marked } from "marked";
@@ -26710,7 +26755,11 @@ function createMatrixTransport(opts) {
26710
26755
  const pendingCommands = /* @__PURE__ */ new Map();
26711
26756
  const buildTextContent = (text) => {
26712
26757
  const content = {
26713
- 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",
26714
26763
  body: text
26715
26764
  };
26716
26765
  const html = toMatrixHtml(text);
@@ -26724,11 +26773,13 @@ function createMatrixTransport(opts) {
26724
26773
  }
26725
26774
  return content;
26726
26775
  };
26776
+ const lastFlushed = /* @__PURE__ */ new Map();
26727
26777
  const flushBuffer = (sessionId) => {
26728
26778
  const ctx = sessions.get(sessionId);
26729
26779
  const text = buffers.get(sessionId) ?? "";
26730
26780
  if (!ctx || text.length === 0) return false;
26731
26781
  buffers.set(sessionId, "");
26782
+ lastFlushed.set(sessionId, text);
26732
26783
  flushedCounts.set(sessionId, (flushedCounts.get(sessionId) ?? 0) + 1);
26733
26784
  const content = buildTextContent(text);
26734
26785
  const tail = (sendQueue.get(sessionId) ?? Promise.resolve()).then(async () => {
@@ -27141,19 +27192,30 @@ function createMatrixTransport(opts) {
27141
27192
  drained = next;
27142
27193
  }
27143
27194
  flushBuffer(sessionId);
27195
+ } finally {
27196
+ clearInterval(refresh);
27197
+ await safeTyping(false);
27198
+ await safePresence("online");
27144
27199
  await (sendQueue.get(sessionId) ?? Promise.resolve());
27145
- if ((flushedCounts.get(sessionId) ?? 0) === 0) {
27200
+ const producedOutput = (flushedCounts.get(sessionId) ?? 0) > 0;
27201
+ if (!producedOutput) {
27146
27202
  console.warn(
27147
27203
  `[matrix:${agent.name}] turn finished with empty buffer (session=${sessionId}); nothing sent to ${evt.room_id}`
27148
27204
  );
27149
27205
  }
27150
- } finally {
27151
- clearInterval(refresh);
27152
- await safeTyping(false);
27153
- 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));
27154
27215
  buffers.delete(sessionId);
27155
27216
  bufferMessageIds.delete(sessionId);
27156
27217
  flushedCounts.delete(sessionId);
27218
+ lastFlushed.delete(sessionId);
27157
27219
  sendQueue.delete(sessionId);
27158
27220
  }
27159
27221
  }
@@ -27303,7 +27365,10 @@ import { unlink } from "fs/promises";
27303
27365
  async function startDaemonSocketServer(opts) {
27304
27366
  await unlink(opts.sockPath).catch(() => {
27305
27367
  });
27368
+ const open = /* @__PURE__ */ new Set();
27306
27369
  const server = createServer((socket) => {
27370
+ open.add(socket);
27371
+ socket.on("close", () => open.delete(socket));
27307
27372
  let buf = "";
27308
27373
  socket.setEncoding("utf8");
27309
27374
  socket.on("data", async (chunk) => {
@@ -27329,6 +27394,8 @@ async function startDaemonSocketServer(opts) {
27329
27394
  return {
27330
27395
  close: () => new Promise((resolve) => {
27331
27396
  server.close(() => resolve());
27397
+ for (const socket of open) socket.destroy();
27398
+ open.clear();
27332
27399
  })
27333
27400
  };
27334
27401
  }
@@ -32764,7 +32831,7 @@ function buildAcpRegistry(cfg, opts = {}) {
32764
32831
  throw new Error(
32765
32832
  `runtime: ${cfg.runtime} requires a container image for each agent. Unresolved:
32766
32833
  ` + lines.join("\n") + `
32767
- Set agents.<name>.container.image, top-level container.image, or use a preset that ships a default image (claude, codex, opencode).`
32834
+ Set agents.<name>.container.image, top-level container.image, or use a preset that ships a default image (claude, codex, opencode, pi).`
32768
32835
  );
32769
32836
  }
32770
32837
  }
@@ -32890,4 +32957,4 @@ export {
32890
32957
  startDaemonSocketServer,
32891
32958
  buildAcpRegistry
32892
32959
  };
32893
- //# sourceMappingURL=chunk-KGYQ5YNP.js.map
32960
+ //# sourceMappingURL=chunk-YZ4IO5MR.js.map