zooid 0.8.0 → 0.9.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.
@@ -6979,7 +6979,6 @@ var DockerAcpRuntime = class {
6979
6979
  this.opts = opts;
6980
6980
  this.engine = opts.engine ?? "docker";
6981
6981
  }
6982
- opts;
6983
6982
  engine;
6984
6983
  buildArgv(spec) {
6985
6984
  const image = spec.image ?? this.opts.defaultImage;
@@ -23116,7 +23115,6 @@ var AgentProcess = class extends EventEmitter {
23116
23115
  super();
23117
23116
  this.options = options;
23118
23117
  }
23119
- options;
23120
23118
  child = null;
23121
23119
  start() {
23122
23120
  if (this.child) return;
@@ -23184,7 +23182,6 @@ var JsonFileSessionStore = class {
23184
23182
  this.opts = opts;
23185
23183
  this.path = join(opts.dir, "sessions.json");
23186
23184
  }
23187
- opts;
23188
23185
  mem = /* @__PURE__ */ new Map();
23189
23186
  path;
23190
23187
  writeChain = Promise.resolve();
@@ -23386,6 +23383,17 @@ function acpUpdateToAgentEvent(notif) {
23386
23383
  };
23387
23384
  case "plan":
23388
23385
  return { type: "plan", sessionId, entries: update.entries };
23386
+ case "available_commands_update": {
23387
+ const u = update;
23388
+ return {
23389
+ type: "available_commands",
23390
+ sessionId,
23391
+ commands: (u.availableCommands ?? []).map((c) => ({
23392
+ name: c.name,
23393
+ description: c.description
23394
+ }))
23395
+ };
23396
+ }
23389
23397
  default:
23390
23398
  return null;
23391
23399
  }
@@ -23501,7 +23509,6 @@ var AcpClient = class {
23501
23509
  this.options = options;
23502
23510
  this.turns = options.onTap ? new TurnTracker({ agentId: options.agent.id, onTap: options.onTap }) : null;
23503
23511
  }
23504
- options;
23505
23512
  process = null;
23506
23513
  runtimeChild = null;
23507
23514
  connection = null;
@@ -24904,6 +24911,18 @@ var MatrixClient = class {
24904
24911
  }
24905
24912
  throw new Error(`invite(${opts.targetUserId}) failed: ${r.status}`);
24906
24913
  }
24914
+ async leaveRoom(roomId, asUserId, opts) {
24915
+ const url2 = `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/leave?user_id=${encodeURIComponent(asUserId)}`;
24916
+ const r = await this.fetch(url2, {
24917
+ method: "POST",
24918
+ headers: {
24919
+ Authorization: `Bearer ${this.asToken}`,
24920
+ "content-type": "application/json"
24921
+ },
24922
+ body: JSON.stringify(opts?.reason ? { reason: opts.reason } : {})
24923
+ });
24924
+ if (!r.ok) throw new Error(`leaveRoom(${roomId}, ${asUserId}) failed: ${r.status}`);
24925
+ }
24907
24926
  async joinRoom(roomIdOrAlias, asUserId) {
24908
24927
  const url2 = `${this.homeserver}/_matrix/client/v3/join/${encodeURIComponent(roomIdOrAlias)}?user_id=${encodeURIComponent(asUserId)}`;
24909
24928
  const r = await this.fetch(url2, {
@@ -25051,7 +25070,6 @@ var MatrixContextProvider = class {
25051
25070
  constructor(opts) {
25052
25071
  this.opts = opts;
25053
25072
  }
25054
- opts;
25055
25073
  async getRoomHistory(channelId, hopts) {
25056
25074
  const { chunk, end } = await this.opts.client.fetchRoomMessages({
25057
25075
  roomId: channelId,
@@ -25341,8 +25359,6 @@ var BotPool = class {
25341
25359
  this.client = client;
25342
25360
  this.agents = agents;
25343
25361
  }
25344
- client;
25345
- agents;
25346
25362
  async bootstrap(opts = {}) {
25347
25363
  const aliasToId = /* @__PURE__ */ new Map();
25348
25364
  const attachedToSpace = /* @__PURE__ */ new Set();
@@ -25511,6 +25527,15 @@ function toPlanBody(evt) {
25511
25527
  entries: evt.entries
25512
25528
  };
25513
25529
  }
25530
+ function toAvailableCommandsBody(evt) {
25531
+ return {
25532
+ session_id: evt.sessionId,
25533
+ available_commands: evt.commands.map((c) => ({
25534
+ name: c.name,
25535
+ description: c.description
25536
+ }))
25537
+ };
25538
+ }
25514
25539
  var RECOVERY_URLS = {
25515
25540
  auth_missing: "https://zooid.dev/docs/guides/run-in-container#authentication-that-carries-over",
25516
25541
  auth_invalid: "https://zooid.dev/docs/guides/run-in-container#authentication-that-carries-over",
@@ -25764,7 +25789,7 @@ async function sendMediaError(ctx, _err, message, client) {
25764
25789
  await client.sendCustomEvent({
25765
25790
  roomId: ctx.roomId,
25766
25791
  asUserId: ctx.agent.userId,
25767
- eventType: "eco.zoon.error",
25792
+ eventType: "dev.zooid.error",
25768
25793
  content: toErrorBody(
25769
25794
  {
25770
25795
  kind: "error",
@@ -25777,7 +25802,7 @@ async function sendMediaError(ctx, _err, message, client) {
25777
25802
  },
25778
25803
  ctx.threadRoot
25779
25804
  )
25780
- }).catch((e) => console.warn(`[matrix:${ctx.agent.name}] eco.zoon.error send failed:`, e));
25805
+ }).catch((e) => console.warn(`[matrix:${ctx.agent.name}] dev.zooid.error send failed:`, e));
25781
25806
  }
25782
25807
  var SEEN_EVENT_CAP = 5e3;
25783
25808
  var DRAIN_QUIET_MS = 300;
@@ -25788,13 +25813,18 @@ function inboundThreadRoot2(evt) {
25788
25813
  return r?.rel_type === "m.thread" && r.event_id ? r.event_id : void 0;
25789
25814
  }
25790
25815
  function createMatrixTransport(opts) {
25791
- const { agents, approvals, client, bindings, hsToken, adminUserId } = opts;
25816
+ const { agents, approvals, client, bindings, hsToken, adminUserId, botUserId } = opts;
25792
25817
  const drainQuietMs = opts.drainQuietMs ?? DRAIN_QUIET_MS;
25793
25818
  const drainMaxMs = opts.drainMaxMs ?? DRAIN_MAX_MS;
25794
25819
  const mediaClient = opts.media;
25795
25820
  const writeAttachmentFn = opts.writeAttachmentFn ?? writeAttachment;
25796
25821
  const pendingMedia = new PendingMediaStore();
25797
25822
  const pool = new BotPool(client, bindings);
25823
+ const ourBotUserIds = /* @__PURE__ */ new Set([
25824
+ ...botUserId ? [botUserId] : [],
25825
+ ...bindings.map((b) => b.userId)
25826
+ ]);
25827
+ const DECLINE_REASON = "Bots are placed in rooms only by the zooid daemon (workforce-as-code). Ad-hoc invites are declined \u2014 add the bot to the room in zooid.yaml.";
25798
25828
  const sessions = /* @__PURE__ */ new Map();
25799
25829
  const buffers = /* @__PURE__ */ new Map();
25800
25830
  const bufferMessageIds = /* @__PURE__ */ new Map();
@@ -25802,23 +25832,68 @@ function createMatrixTransport(opts) {
25802
25832
  const threadStates = /* @__PURE__ */ new Map();
25803
25833
  const cutoffTs = Date.now() - STARTUP_GRACE_MS;
25804
25834
  const seenEventIds = /* @__PURE__ */ new Set();
25835
+ const flushedCounts = /* @__PURE__ */ new Map();
25836
+ const pendingCommands = /* @__PURE__ */ new Map();
25837
+ const buildTextContent = (text) => {
25838
+ const content = {
25839
+ msgtype: "m.text",
25840
+ body: text
25841
+ };
25842
+ const html = toMatrixHtml(text);
25843
+ if (html) {
25844
+ const escapedPlain = "<p>" + text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</p>";
25845
+ const norm = (s) => s.replace(/\s+/g, " ").trim();
25846
+ if (norm(html) !== norm(escapedPlain)) {
25847
+ content.format = "org.matrix.custom.html";
25848
+ content.formatted_body = html;
25849
+ }
25850
+ }
25851
+ return content;
25852
+ };
25853
+ const flushBuffer = (sessionId) => {
25854
+ const ctx = sessions.get(sessionId);
25855
+ const text = buffers.get(sessionId) ?? "";
25856
+ if (!ctx || text.length === 0) return false;
25857
+ buffers.set(sessionId, "");
25858
+ flushedCounts.set(sessionId, (flushedCounts.get(sessionId) ?? 0) + 1);
25859
+ const content = buildTextContent(text);
25860
+ const tail = (sendQueue.get(sessionId) ?? Promise.resolve()).then(async () => {
25861
+ try {
25862
+ await client.sendMessage({
25863
+ roomId: ctx.roomId,
25864
+ asUserId: ctx.agent.userId,
25865
+ content,
25866
+ threadRoot: ctx.threadRoot
25867
+ });
25868
+ } catch (err) {
25869
+ console.warn(`[matrix:${ctx.agent.name}] sendMessage flush failed:`, err);
25870
+ }
25871
+ });
25872
+ sendQueue.set(sessionId, tail);
25873
+ return true;
25874
+ };
25805
25875
  agents.onEvent = async (name, event) => {
25806
25876
  const ctx = sessions.get(event.sessionId);
25807
25877
  if (!ctx) {
25808
- console.warn(`[matrix:${name}] no session ctx for ${event.sessionId}`);
25878
+ if (event.type === "available_commands") {
25879
+ pendingCommands.set(event.sessionId, event);
25880
+ } else {
25881
+ console.warn(`[matrix:${name}] no session ctx for ${event.sessionId}`);
25882
+ }
25809
25883
  return;
25810
25884
  }
25811
25885
  if (event.type === "agent_message_chunk") {
25812
25886
  const block = event.content;
25813
25887
  if (block.type === "text" && typeof block.text === "string") {
25814
- const current = buffers.get(event.sessionId) ?? "";
25815
25888
  const prevMessageId = bufferMessageIds.get(event.sessionId);
25816
25889
  const messageChanged = event.messageId !== void 0 && prevMessageId !== void 0 && event.messageId !== prevMessageId;
25817
- const needsBreak = current.length > 0 && (block.text === "" || messageChanged);
25818
- const prefix = needsBreak ? "\n\n" : "";
25819
- buffers.set(event.sessionId, current + prefix + block.text);
25820
25890
  if (event.messageId !== void 0)
25821
25891
  bufferMessageIds.set(event.sessionId, event.messageId);
25892
+ if (messageChanged) flushBuffer(event.sessionId);
25893
+ const current = buffers.get(event.sessionId) ?? "";
25894
+ const needsBreak = current.length > 0 && block.text === "";
25895
+ const prefix = needsBreak ? "\n\n" : "";
25896
+ buffers.set(event.sessionId, current + prefix + block.text);
25822
25897
  } else if (block.type === "image" && typeof block.data === "string" && typeof block.mimeType === "string" && mediaClient) {
25823
25898
  const ctx2 = sessions.get(event.sessionId);
25824
25899
  if (ctx2) {
@@ -25847,8 +25922,9 @@ function createMatrixTransport(opts) {
25847
25922
  }
25848
25923
  return;
25849
25924
  }
25850
- const eventType = event.type === "tool_call" ? "eco.zoon.tool_call" : event.type === "tool_call_update" ? "eco.zoon.tool_call_update" : "eco.zoon.plan";
25851
- const body = event.type === "tool_call" ? toToolCallBody(event) : event.type === "tool_call_update" ? toUpdateBody(event) : toPlanBody(event);
25925
+ flushBuffer(event.sessionId);
25926
+ const eventType = event.type === "tool_call" ? "dev.zooid.tool_call" : event.type === "tool_call_update" ? "dev.zooid.tool_call_update" : event.type === "available_commands" ? "dev.zooid.available_commands_update" : "dev.zooid.plan";
25927
+ const body = event.type === "tool_call" ? toToolCallBody(event) : event.type === "tool_call_update" ? toUpdateBody(event) : event.type === "available_commands" ? toAvailableCommandsBody(event) : toPlanBody(event);
25852
25928
  body["m.relates_to"] = { rel_type: "m.thread", event_id: ctx.threadRoot };
25853
25929
  const tail = (sendQueue.get(event.sessionId) ?? Promise.resolve()).then(async () => {
25854
25930
  try {
@@ -25887,7 +25963,7 @@ function createMatrixTransport(opts) {
25887
25963
  void client.sendCustomEvent({
25888
25964
  roomId: ctx.roomId,
25889
25965
  asUserId: ctx.agent.userId,
25890
- eventType: "eco.zoon.approval_request",
25966
+ eventType: "dev.zooid.approval_request",
25891
25967
  content
25892
25968
  });
25893
25969
  });
@@ -25921,20 +25997,33 @@ function createMatrixTransport(opts) {
25921
25997
  );
25922
25998
  continue;
25923
25999
  }
25924
- if (evt.type === "eco.zoon.session_reset") {
26000
+ if (evt.type === "m.room.member" && evt.content?.membership === "invite") {
26001
+ const target = evt.state_key;
26002
+ const inviter = evt.sender;
26003
+ if (target && evt.room_id && ourBotUserIds.has(target) && (!inviter || !ourBotUserIds.has(inviter))) {
26004
+ console.log(
26005
+ `[matrix] declining ad-hoc invite for ${target} in ${evt.room_id} from ${inviter ?? "unknown"}`
26006
+ );
26007
+ await client.leaveRoom(evt.room_id, target, { reason: DECLINE_REASON }).catch(
26008
+ (err) => console.warn(`[matrix] leaveRoom(${evt.room_id}, ${target}) failed:`, err)
26009
+ );
26010
+ }
26011
+ continue;
26012
+ }
26013
+ if (evt.type === "dev.zooid.session_reset") {
25925
26014
  const relates = evt.content?.["m.relates_to"];
25926
26015
  const threadRoot = relates?.rel_type === "m.thread" && relates.event_id ? relates.event_id : void 0;
25927
26016
  if (!threadRoot) {
25928
- console.log("[matrix] dropping eco.zoon.session_reset without thread relation");
26017
+ console.log("[matrix] dropping dev.zooid.session_reset without thread relation");
25929
26018
  continue;
25930
26019
  }
25931
- console.log(`[matrix] inbound eco.zoon.session_reset in ${evt.room_id} thread=${threadRoot}`);
26020
+ console.log(`[matrix] inbound dev.zooid.session_reset in ${evt.room_id} thread=${threadRoot}`);
25932
26021
  for (const a of bindings) {
25933
26022
  agents.endSession(a.name, threadRoot);
25934
26023
  }
25935
26024
  continue;
25936
26025
  }
25937
- if (evt.type === "eco.zoon.interrupt") {
26026
+ if (evt.type === "dev.zooid.interrupt") {
25938
26027
  const content = evt.content ?? {};
25939
26028
  const relates = evt.content?.["m.relates_to"];
25940
26029
  const threadRoot = relates?.rel_type === "m.thread" && relates.event_id ? relates.event_id : void 0;
@@ -25956,7 +26045,7 @@ function createMatrixTransport(opts) {
25956
26045
  continue;
25957
26046
  }
25958
26047
  if (!content.session_id) {
25959
- console.warn(`[matrix] eco.zoon.interrupt missing session_id (event_id=${evt.event_id})`);
26048
+ console.warn(`[matrix] dev.zooid.interrupt missing session_id (event_id=${evt.event_id})`);
25960
26049
  continue;
25961
26050
  }
25962
26051
  const ctx = sessions.get(content.session_id);
@@ -25971,7 +26060,7 @@ function createMatrixTransport(opts) {
25971
26060
  });
25972
26061
  continue;
25973
26062
  }
25974
- if (evt.type === "eco.zoon.approval_response") {
26063
+ if (evt.type === "dev.zooid.approval_response") {
25975
26064
  const content = evt.content ?? {};
25976
26065
  if (!content.session_id || !content.approval_id || !content.decision) continue;
25977
26066
  const decision = content.option_id ? { decision: content.decision, optionId: content.option_id } : { decision: content.decision };
@@ -26061,9 +26150,9 @@ function createMatrixTransport(opts) {
26061
26150
  void client.sendCustomEvent({
26062
26151
  roomId: evt.room_id,
26063
26152
  asUserId: a.userId,
26064
- eventType: "eco.zoon.error",
26153
+ eventType: "dev.zooid.error",
26065
26154
  content: body2
26066
- }).catch((e) => console.warn(`[matrix:${a.name}] eco.zoon.error send failed:`, e));
26155
+ }).catch((e) => console.warn(`[matrix:${a.name}] dev.zooid.error send failed:`, e));
26067
26156
  });
26068
26157
  }
26069
26158
  }
@@ -26097,6 +26186,12 @@ function createMatrixTransport(opts) {
26097
26186
  sessions.set(sessionId, { agent, roomId: evt.room_id, threadRoot });
26098
26187
  buffers.set(sessionId, "");
26099
26188
  bufferMessageIds.delete(sessionId);
26189
+ flushedCounts.set(sessionId, 0);
26190
+ const stashedCommands = pendingCommands.get(sessionId);
26191
+ if (stashedCommands) {
26192
+ pendingCommands.delete(sessionId);
26193
+ void agents.onEvent?.(agent.name, stashedCommands);
26194
+ }
26100
26195
  const roomId = evt.room_id;
26101
26196
  const TYPING_TTL_MS = 3e4;
26102
26197
  const TYPING_REFRESH_MS = 25e3;
@@ -26142,32 +26237,13 @@ function createMatrixTransport(opts) {
26142
26237
  while (drainQuietMs > 0 && Date.now() - drainStart < drainMaxMs) {
26143
26238
  await delay(drainQuietMs);
26144
26239
  const next = buffers.get(sessionId) ?? "";
26145
- if (next === drained && next.length > 0) break;
26240
+ if (next === drained && (next.length > 0 || (flushedCounts.get(sessionId) ?? 0) > 0))
26241
+ break;
26146
26242
  drained = next;
26147
26243
  }
26148
- const text = buffers.get(sessionId) ?? "";
26149
- if (text.length > 0) {
26150
- const html = toMatrixHtml(text);
26151
- const content = {
26152
- msgtype: "m.text",
26153
- body: text
26154
- };
26155
- if (html) {
26156
- const escapedPlain = "<p>" + text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</p>";
26157
- const norm = (s) => s.replace(/\s+/g, " ").trim();
26158
- if (norm(html) !== norm(escapedPlain)) {
26159
- content.format = "org.matrix.custom.html";
26160
- content.formatted_body = html;
26161
- }
26162
- }
26163
- await client.sendMessage({
26164
- roomId: evt.room_id,
26165
- asUserId: agent.userId,
26166
- content,
26167
- threadRoot
26168
- // every reply threads, full stop
26169
- });
26170
- } else {
26244
+ flushBuffer(sessionId);
26245
+ await (sendQueue.get(sessionId) ?? Promise.resolve());
26246
+ if ((flushedCounts.get(sessionId) ?? 0) === 0) {
26171
26247
  console.warn(
26172
26248
  `[matrix:${agent.name}] turn finished with empty buffer (session=${sessionId}); nothing sent to ${evt.room_id}`
26173
26249
  );
@@ -26178,6 +26254,8 @@ function createMatrixTransport(opts) {
26178
26254
  await safePresence("online");
26179
26255
  buffers.delete(sessionId);
26180
26256
  bufferMessageIds.delete(sessionId);
26257
+ flushedCounts.delete(sessionId);
26258
+ sendQueue.delete(sessionId);
26181
26259
  }
26182
26260
  }
26183
26261
  return {
@@ -26263,7 +26341,7 @@ async function publishWorkforce(opts) {
26263
26341
  await opts.client.sendStateEvent({
26264
26342
  roomId: opts.spaceRoomId,
26265
26343
  asUserId: opts.asUserId,
26266
- eventType: "eco.zoon.workforce",
26344
+ eventType: "dev.zooid.workforce",
26267
26345
  stateKey: "",
26268
26346
  content: buildWorkforceRoster(opts.agents)
26269
26347
  });
@@ -31857,4 +31935,4 @@ export {
31857
31935
  startDaemonSocketServer,
31858
31936
  buildAcpRegistry
31859
31937
  };
31860
- //# sourceMappingURL=chunk-R6EDLH23.js.map
31938
+ //# sourceMappingURL=chunk-LOILUENL.js.map