@shipers-dev/multi 0.44.1 → 0.46.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 (2) hide show
  1. package/dist/index.js +182 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34065,7 +34065,8 @@ var init_chat = __esm(() => {
34065
34065
  tool_call_id: exports_external.string().optional(),
34066
34066
  content: exports_external.string().optional(),
34067
34067
  plan_entries: exports_external.array(ChatPlanEntrySchema).optional(),
34068
- dispatched_at: exports_external.number().optional()
34068
+ dispatched_at: exports_external.number().optional(),
34069
+ delivered_to_acp_at: exports_external.number().optional()
34069
34070
  });
34070
34071
  CHAT_DOC = {
34071
34072
  MESSAGES_LIST: "messages",
@@ -35909,6 +35910,19 @@ function finalizeMessage(doc2, mapId) {
35909
35910
  return;
35910
35911
  map20.set("partial", false);
35911
35912
  }
35913
+ function setMessageField(doc2, msgId, key, value3) {
35914
+ const list = doc2.getMovableList(CHAT_DOC.MESSAGES_LIST);
35915
+ for (let i = 0;i < list.length; i++) {
35916
+ const child = list.get(i);
35917
+ if (!child || typeof child.get !== "function")
35918
+ continue;
35919
+ if (child.get("id") === msgId) {
35920
+ child.set(key, value3);
35921
+ return true;
35922
+ }
35923
+ }
35924
+ return false;
35925
+ }
35912
35926
  function listMessages(doc2) {
35913
35927
  const list = doc2.getMovableList(CHAT_DOC.MESSAGES_LIST);
35914
35928
  return list.toJSON();
@@ -35942,6 +35956,7 @@ class ChatPeer {
35942
35956
  dirtySinceWrite = 0;
35943
35957
  seenIds = new Set;
35944
35958
  closed = false;
35959
+ firstFrameSinceConnect = true;
35945
35960
  reconnectTimer = null;
35946
35961
  constructor(opts) {
35947
35962
  this.opts = opts;
@@ -36038,6 +36053,11 @@ class ChatPeer {
36038
36053
  patchMessage(this.doc, containerId, patch9);
36039
36054
  this.flush();
36040
36055
  }
36056
+ markDelivered(msgId) {
36057
+ if (setMessageField(this.doc, msgId, "delivered_to_acp_at", Math.floor(Date.now() / 1000))) {
36058
+ this.flush();
36059
+ }
36060
+ }
36041
36061
  getDoc() {
36042
36062
  return this.doc;
36043
36063
  }
@@ -36086,7 +36106,11 @@ class ChatPeer {
36086
36106
  return;
36087
36107
  }
36088
36108
  ws.binaryType = "arraybuffer";
36089
- ws.addEventListener("open", () => this.opts.log(`[chat ${this.chatId}] ws open`));
36109
+ ws.addEventListener("open", () => {
36110
+ this.opts.log(`[chat ${this.chatId}] ws open`);
36111
+ this.firstFrameSinceConnect = true;
36112
+ this.sendJson({ type: "daemon_subscribed" });
36113
+ });
36090
36114
  ws.addEventListener("message", (ev) => this.onFrame(ev.data));
36091
36115
  ws.addEventListener("close", () => {
36092
36116
  this.opts.log(`[chat ${this.chatId}] ws close`);
@@ -36117,6 +36141,17 @@ class ChatPeer {
36117
36141
  this.ws.send(frame);
36118
36142
  } catch {}
36119
36143
  }
36144
+ sendJson(obj) {
36145
+ if (!this.ws || this.ws.readyState !== 1)
36146
+ return;
36147
+ const body = new TextEncoder().encode(JSON.stringify(obj));
36148
+ const frame = new Uint8Array(1 + body.byteLength);
36149
+ frame[0] = FRAME_JSON;
36150
+ frame.set(body, 1);
36151
+ try {
36152
+ this.ws.send(frame);
36153
+ } catch {}
36154
+ }
36120
36155
  onFrame(raw) {
36121
36156
  const buf = raw instanceof ArrayBuffer ? new Uint8Array(raw) : raw instanceof Uint8Array ? raw : null;
36122
36157
  if (!buf || buf.byteLength < 1)
@@ -36129,10 +36164,14 @@ class ChatPeer {
36129
36164
  } catch {
36130
36165
  return;
36131
36166
  }
36167
+ const isFirstFrame = this.firstFrameSinceConnect;
36168
+ this.firstFrameSinceConnect = false;
36132
36169
  for (const m of listMessages(this.doc)) {
36133
36170
  if (before2.has(m.id))
36134
36171
  continue;
36135
36172
  this.seenIds.add(m.id);
36173
+ if (isFirstFrame)
36174
+ continue;
36136
36175
  if (m.author?.kind === "user" && !m.partial && this.opts.onUserMessage) {
36137
36176
  const cb = this.opts.onUserMessage;
36138
36177
  Promise.resolve().then(() => cb(m, this)).catch((e) => this.opts.log(`[chat ${this.chatId}] onUserMessage error: ${e.message}`));
@@ -36602,11 +36641,7 @@ var init_chat_plan_actions = __esm(() => {
36602
36641
  });
36603
36642
 
36604
36643
  // src/_impl/chat-supervisor.ts
36605
- var exports_chat_supervisor = {};
36606
- __export(exports_chat_supervisor, {
36607
- runChatTurn: () => runChatTurn
36608
- });
36609
- async function runChatTurn(opts) {
36644
+ async function runChatTurnWithPeer(peer, opts) {
36610
36645
  const { apiUrl, authToken: authToken2, workspaceId, chatId, messageId, log: log4 } = opts;
36611
36646
  const headers = { authorization: `Bearer ${authToken2}` };
36612
36647
  const metaR = await fetch(`${apiUrl}/api/workspaces/${workspaceId}/chats/${chatId}`, { headers });
@@ -36615,25 +36650,12 @@ async function runChatTurn(opts) {
36615
36650
  return;
36616
36651
  }
36617
36652
  const chat2 = await metaR.json();
36618
- const peer = new ChatPeer({
36619
- apiUrl,
36620
- authToken: authToken2,
36621
- workspaceId,
36622
- chatId,
36623
- primaryAgentId: chat2.primary_agent_id,
36624
- log: log4
36625
- });
36626
- peer.start();
36627
- try {
36628
- const msg = await peer.awaitMessage(messageId, 7000);
36629
- if (!msg) {
36630
- log4(`[chat ${chatId}] message ${messageId} not visible after sync; aborting`);
36631
- return;
36632
- }
36633
- await processUserMessage(chat2, msg, peer, { apiUrl, authToken: authToken2, workspaceId, log: log4 });
36634
- } finally {
36635
- peer.close();
36653
+ const msg = await peer.awaitMessage(messageId, 7000);
36654
+ if (!msg) {
36655
+ log4(`[chat ${chatId}] message ${messageId} not visible after sync; aborting`);
36656
+ return;
36636
36657
  }
36658
+ await processUserMessage(chat2, msg, peer, { apiUrl, authToken: authToken2, workspaceId, log: log4 });
36637
36659
  }
36638
36660
  async function fetchAgents(apiUrl, workspaceId, authToken2) {
36639
36661
  const headers = { authorization: `Bearer ${authToken2}` };
@@ -36771,6 +36793,7 @@ async function processUserMessage(chat2, userMsg, peer, ctx) {
36771
36793
  return "other";
36772
36794
  };
36773
36795
  const preamble = buildChatPreamble({ projectId: chat2.project_id, agents: allAgents });
36796
+ peer.markDelivered(userMsg.id);
36774
36797
  await new Promise((resolve2) => {
36775
36798
  handleChatTurn({
36776
36799
  chatId: chat2.id,
@@ -36996,6 +37019,131 @@ var init_chat_supervisor = __esm(() => {
36996
37019
  init_lib();
36997
37020
  });
36998
37021
 
37022
+ // src/_impl/chat-session-registry.ts
37023
+ var exports_chat_session_registry = {};
37024
+ __export(exports_chat_session_registry, {
37025
+ chatSessionRegistry: () => chatSessionRegistry
37026
+ });
37027
+ function key(wsId, chatId) {
37028
+ return `${wsId}:${chatId}`;
37029
+ }
37030
+ function clearIdle(s) {
37031
+ if (s.idleTimer) {
37032
+ clearTimeout(s.idleTimer);
37033
+ s.idleTimer = null;
37034
+ }
37035
+ }
37036
+ function scheduleIdle(k, s, log4) {
37037
+ clearIdle(s);
37038
+ s.idleTimer = setTimeout(() => {
37039
+ if (s.inFlight) {
37040
+ scheduleIdle(k, s, log4);
37041
+ return;
37042
+ }
37043
+ log4(`[chat ${k}] idle evict`);
37044
+ try {
37045
+ s.peer.close();
37046
+ } catch {}
37047
+ sessions.delete(k);
37048
+ }, idleTtlMs);
37049
+ }
37050
+ function ensureSession(wsId, chatId, ctx) {
37051
+ const k = key(wsId, chatId);
37052
+ const existing = sessions.get(k);
37053
+ if (existing) {
37054
+ clearIdle(existing);
37055
+ return existing;
37056
+ }
37057
+ const peer = new ChatPeer({
37058
+ apiUrl: ctx.apiUrl,
37059
+ authToken: ctx.authToken,
37060
+ workspaceId: wsId,
37061
+ chatId,
37062
+ primaryAgentId: null,
37063
+ log: ctx.log,
37064
+ onUserMessage: (msg) => {
37065
+ chatSessionRegistry.ensureAndProcess(wsId, chatId, msg.id, ctx);
37066
+ }
37067
+ });
37068
+ peer.start();
37069
+ const now = Date.now();
37070
+ const s = {
37071
+ peer,
37072
+ ctx,
37073
+ chatId,
37074
+ startedAt: now,
37075
+ lastTurnAt: 0,
37076
+ inFlight: null,
37077
+ pending: [],
37078
+ processed: new Set,
37079
+ idleTimer: null
37080
+ };
37081
+ sessions.set(k, s);
37082
+ ctx.log(`[chat ${k}] session opened`);
37083
+ return s;
37084
+ }
37085
+ async function runTurn(s, k, messageId, ctx, chatId) {
37086
+ try {
37087
+ await runChatTurnWithPeer(s.peer, {
37088
+ apiUrl: ctx.apiUrl,
37089
+ authToken: ctx.authToken,
37090
+ workspaceId: ctx.workspaceId,
37091
+ deviceId: ctx.deviceId,
37092
+ chatId,
37093
+ messageId,
37094
+ log: ctx.log
37095
+ });
37096
+ } catch (e) {
37097
+ ctx.log(`[chat ${k}] turn ${messageId} error: ${e.message}`);
37098
+ } finally {
37099
+ s.lastTurnAt = Date.now();
37100
+ const next4 = s.pending.shift();
37101
+ if (next4) {
37102
+ s.inFlight = runTurn(s, k, next4, ctx, chatId);
37103
+ } else {
37104
+ s.inFlight = null;
37105
+ scheduleIdle(k, s, ctx.log);
37106
+ }
37107
+ }
37108
+ }
37109
+ var DEFAULT_IDLE_TTL_MS, sessions, idleTtlMs, chatSessionRegistry;
37110
+ var init_chat_session_registry = __esm(() => {
37111
+ init_chat_peer();
37112
+ init_chat_supervisor();
37113
+ DEFAULT_IDLE_TTL_MS = 10 * 60 * 1000;
37114
+ sessions = new Map;
37115
+ idleTtlMs = DEFAULT_IDLE_TTL_MS;
37116
+ chatSessionRegistry = {
37117
+ setIdleTtl(ms) {
37118
+ idleTtlMs = ms;
37119
+ },
37120
+ async ensureAndProcess(wsId, chatId, messageId, ctx) {
37121
+ const s = ensureSession(wsId, chatId, ctx);
37122
+ const k = key(wsId, chatId);
37123
+ if (s.processed.has(messageId))
37124
+ return;
37125
+ s.processed.add(messageId);
37126
+ if (s.inFlight) {
37127
+ s.pending.push(messageId);
37128
+ return;
37129
+ }
37130
+ s.inFlight = runTurn(s, k, messageId, ctx, chatId);
37131
+ },
37132
+ async closeAll() {
37133
+ for (const [k, s] of sessions) {
37134
+ clearIdle(s);
37135
+ try {
37136
+ s.peer.close();
37137
+ } catch {}
37138
+ sessions.delete(k);
37139
+ }
37140
+ },
37141
+ size() {
37142
+ return sessions.size;
37143
+ }
37144
+ };
37145
+ });
37146
+
36999
37147
  // src/_impl/supervisor-tick.ts
37000
37148
  var exports_supervisor_tick = {};
37001
37149
  __export(exports_supervisor_tick, {
@@ -37615,7 +37763,7 @@ import { parseArgs } from "util";
37615
37763
  // package.json
37616
37764
  var package_default = {
37617
37765
  name: "@shipers-dev/multi",
37618
- version: "0.44.1",
37766
+ version: "0.46.0",
37619
37767
  type: "module",
37620
37768
  bin: {
37621
37769
  "multi-agent": "./dist/index.js"
@@ -39058,14 +39206,12 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
39058
39206
  if (!cfg.workspaceId || !cfg.authToken || !cfg.deviceId) {
39059
39207
  return Response.json({ error: "daemon not configured" }, { status: 503 });
39060
39208
  }
39061
- const { runChatTurn: runChatTurn2 } = await Promise.resolve().then(() => (init_chat_supervisor(), exports_chat_supervisor));
39062
- runChatTurn2({
39209
+ const { chatSessionRegistry: chatSessionRegistry2 } = await Promise.resolve().then(() => (init_chat_session_registry(), exports_chat_session_registry));
39210
+ chatSessionRegistry2.ensureAndProcess(cfg.workspaceId, body.chat_id, body.message_id, {
39063
39211
  apiUrl,
39064
39212
  authToken: cfg.authToken,
39065
39213
  workspaceId: cfg.workspaceId,
39066
39214
  deviceId: cfg.deviceId,
39067
- chatId: body.chat_id,
39068
- messageId: body.message_id,
39069
39215
  log: log3
39070
39216
  }).catch((e) => log3(`[chat ${body.chat_id}] runChatTurn error: ${e.message}`));
39071
39217
  return Response.json({ accepted: true }, { status: 202 });
@@ -39253,6 +39399,12 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
39253
39399
  try {
39254
39400
  tunnel?.child?.kill();
39255
39401
  } catch {}
39402
+ yield* exports_Effect.promise(async () => {
39403
+ try {
39404
+ const { chatSessionRegistry: chatSessionRegistry2 } = await Promise.resolve().then(() => (init_chat_session_registry(), exports_chat_session_registry));
39405
+ await chatSessionRegistry2.closeAll();
39406
+ } catch {}
39407
+ });
39256
39408
  yield* announceTunnel(apiUrl, cfg.workspaceId, cfg.deviceId, null).pipe(exports_Effect.catchAll(() => exports_Effect.void));
39257
39409
  const inFlight = [];
39258
39410
  for (const e of running3.values())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipers-dev/multi",
3
- "version": "0.44.1",
3
+ "version": "0.46.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "multi-agent": "./dist/index.js"