@shipers-dev/multi 0.44.1 → 0.47.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/index.js +303 -118
- 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", () =>
|
|
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
|
-
|
|
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
|
|
36619
|
-
|
|
36620
|
-
|
|
36621
|
-
|
|
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}` };
|
|
@@ -36703,6 +36725,7 @@ async function processUserMessage(chat2, userMsg, peer, ctx) {
|
|
|
36703
36725
|
let buffered = "";
|
|
36704
36726
|
let lastFlush = Date.now();
|
|
36705
36727
|
let agentReplyText = "";
|
|
36728
|
+
const fullReplyParts = [];
|
|
36706
36729
|
const toolMsgByCallId = new Map;
|
|
36707
36730
|
let planContainerId = null;
|
|
36708
36731
|
const flushText = (force = false) => {
|
|
@@ -36771,92 +36794,99 @@ async function processUserMessage(chat2, userMsg, peer, ctx) {
|
|
|
36771
36794
|
return "other";
|
|
36772
36795
|
};
|
|
36773
36796
|
const preamble = buildChatPreamble({ projectId: chat2.project_id, agents: allAgents });
|
|
36774
|
-
|
|
36775
|
-
|
|
36776
|
-
|
|
36777
|
-
|
|
36778
|
-
|
|
36779
|
-
|
|
36780
|
-
|
|
36781
|
-
|
|
36782
|
-
|
|
36783
|
-
|
|
36784
|
-
|
|
36785
|
-
|
|
36786
|
-
|
|
36787
|
-
|
|
36788
|
-
|
|
36789
|
-
|
|
36790
|
-
|
|
36791
|
-
|
|
36792
|
-
|
|
36793
|
-
const
|
|
36794
|
-
|
|
36795
|
-
|
|
36796
|
-
|
|
36797
|
-
|
|
36798
|
-
|
|
36799
|
-
|
|
36800
|
-
|
|
36801
|
-
|
|
36802
|
-
|
|
36797
|
+
peer.markDelivered(userMsg.id);
|
|
36798
|
+
const runOneTurn = async (prompt, sendPreamble) => {
|
|
36799
|
+
agentReplyText = "";
|
|
36800
|
+
await new Promise((resolve2) => {
|
|
36801
|
+
handleChatTurn({
|
|
36802
|
+
chatId: chat2.id,
|
|
36803
|
+
prompt,
|
|
36804
|
+
preferredRuntime: runtime4,
|
|
36805
|
+
cwd,
|
|
36806
|
+
systemPreamble: sendPreamble ? preamble : undefined,
|
|
36807
|
+
log: log4,
|
|
36808
|
+
onChunk: (text) => {
|
|
36809
|
+
ensureTextOpen();
|
|
36810
|
+
buffered += text;
|
|
36811
|
+
agentReplyText += text;
|
|
36812
|
+
flushText();
|
|
36813
|
+
},
|
|
36814
|
+
onToolCall: (ev) => {
|
|
36815
|
+
closeText();
|
|
36816
|
+
const partial2 = ev.status !== "completed" && ev.status !== "failed";
|
|
36817
|
+
const status3 = ev.status || "in_progress";
|
|
36818
|
+
if (ev.id) {
|
|
36819
|
+
const existing = toolMsgByCallId.get(ev.id);
|
|
36820
|
+
if (existing) {
|
|
36821
|
+
peer.patchMessage(existing, {
|
|
36822
|
+
partial: partial2,
|
|
36823
|
+
status: status3,
|
|
36824
|
+
tool: ev.tool,
|
|
36825
|
+
tool_kind: mapToolKind(ev.kind),
|
|
36826
|
+
input: summarizeInput(ev.input)
|
|
36827
|
+
});
|
|
36828
|
+
return;
|
|
36829
|
+
}
|
|
36803
36830
|
}
|
|
36831
|
+
const id3 = `tc_${ev.id || Math.random().toString(36).slice(2)}`;
|
|
36832
|
+
const containerId = peer.appendStructured({
|
|
36833
|
+
id: id3,
|
|
36834
|
+
author: { kind: "agent", id: agentAuthorId, name: agentDisplayName },
|
|
36835
|
+
kind: "tool_call",
|
|
36836
|
+
text: "",
|
|
36837
|
+
ts: Math.floor(Date.now() / 1000),
|
|
36838
|
+
mentions: [],
|
|
36839
|
+
attachments: [],
|
|
36840
|
+
partial: partial2,
|
|
36841
|
+
tool: ev.tool,
|
|
36842
|
+
tool_kind: mapToolKind(ev.kind),
|
|
36843
|
+
status: status3,
|
|
36844
|
+
input: summarizeInput(ev.input),
|
|
36845
|
+
tool_call_id: ev.id
|
|
36846
|
+
});
|
|
36847
|
+
if (ev.id)
|
|
36848
|
+
toolMsgByCallId.set(ev.id, containerId);
|
|
36849
|
+
},
|
|
36850
|
+
onToolResult: (ev) => {
|
|
36851
|
+
closeText();
|
|
36852
|
+
const summary5 = summarizeContent(ev.content);
|
|
36853
|
+
peer.appendStructured({
|
|
36854
|
+
id: `tr_${ev.tool_call_id || Math.random().toString(36).slice(2)}_${Date.now()}`,
|
|
36855
|
+
author: { kind: "agent", id: agentAuthorId, name: agentDisplayName },
|
|
36856
|
+
kind: "tool_result",
|
|
36857
|
+
text: `${summary5.lines} lines · ${summary5.bytes} bytes`,
|
|
36858
|
+
ts: Math.floor(Date.now() / 1000),
|
|
36859
|
+
mentions: [],
|
|
36860
|
+
attachments: [],
|
|
36861
|
+
partial: false,
|
|
36862
|
+
tool_call_id: ev.tool_call_id,
|
|
36863
|
+
content: summary5.content
|
|
36864
|
+
});
|
|
36865
|
+
const cid = ev.tool_call_id ? toolMsgByCallId.get(ev.tool_call_id) : undefined;
|
|
36866
|
+
if (cid)
|
|
36867
|
+
peer.patchMessage(cid, { partial: false, status: "completed" });
|
|
36868
|
+
},
|
|
36869
|
+
onPlanUpdate: (entries2) => {
|
|
36870
|
+
closeText();
|
|
36871
|
+
planContainerId = peer.upsertPlan(agentAuthorId, agentDisplayName, entries2, planContainerId);
|
|
36872
|
+
},
|
|
36873
|
+
onDone: (stopReason) => {
|
|
36874
|
+
closeText();
|
|
36875
|
+
for (const cid of toolMsgByCallId.values()) {
|
|
36876
|
+
peer.patchMessage(cid, { partial: false, status: "completed" });
|
|
36877
|
+
}
|
|
36878
|
+
log4(`[chat ${chat2.id}] done: ${stopReason}`);
|
|
36879
|
+
resolve2();
|
|
36804
36880
|
}
|
|
36805
|
-
|
|
36806
|
-
const containerId = peer.appendStructured({
|
|
36807
|
-
id: id3,
|
|
36808
|
-
author: { kind: "agent", id: agentAuthorId, name: agentDisplayName },
|
|
36809
|
-
kind: "tool_call",
|
|
36810
|
-
text: "",
|
|
36811
|
-
ts: Math.floor(Date.now() / 1000),
|
|
36812
|
-
mentions: [],
|
|
36813
|
-
attachments: [],
|
|
36814
|
-
partial: partial2,
|
|
36815
|
-
tool: ev.tool,
|
|
36816
|
-
tool_kind: mapToolKind(ev.kind),
|
|
36817
|
-
status: status3,
|
|
36818
|
-
input: summarizeInput(ev.input),
|
|
36819
|
-
tool_call_id: ev.id
|
|
36820
|
-
});
|
|
36821
|
-
if (ev.id)
|
|
36822
|
-
toolMsgByCallId.set(ev.id, containerId);
|
|
36823
|
-
},
|
|
36824
|
-
onToolResult: (ev) => {
|
|
36825
|
-
closeText();
|
|
36826
|
-
const summary5 = summarizeContent(ev.content);
|
|
36827
|
-
peer.appendStructured({
|
|
36828
|
-
id: `tr_${ev.tool_call_id || Math.random().toString(36).slice(2)}_${Date.now()}`,
|
|
36829
|
-
author: { kind: "agent", id: agentAuthorId, name: agentDisplayName },
|
|
36830
|
-
kind: "tool_result",
|
|
36831
|
-
text: `${summary5.lines} lines · ${summary5.bytes} bytes`,
|
|
36832
|
-
ts: Math.floor(Date.now() / 1000),
|
|
36833
|
-
mentions: [],
|
|
36834
|
-
attachments: [],
|
|
36835
|
-
partial: false,
|
|
36836
|
-
tool_call_id: ev.tool_call_id,
|
|
36837
|
-
content: summary5.content
|
|
36838
|
-
});
|
|
36839
|
-
const cid = ev.tool_call_id ? toolMsgByCallId.get(ev.tool_call_id) : undefined;
|
|
36840
|
-
if (cid)
|
|
36841
|
-
peer.patchMessage(cid, { partial: false, status: "completed" });
|
|
36842
|
-
},
|
|
36843
|
-
onPlanUpdate: (entries2) => {
|
|
36844
|
-
closeText();
|
|
36845
|
-
planContainerId = peer.upsertPlan(agentAuthorId, agentDisplayName, entries2, planContainerId);
|
|
36846
|
-
},
|
|
36847
|
-
onDone: (stopReason) => {
|
|
36848
|
-
closeText();
|
|
36849
|
-
for (const cid of toolMsgByCallId.values()) {
|
|
36850
|
-
peer.patchMessage(cid, { partial: false, status: "completed" });
|
|
36851
|
-
}
|
|
36852
|
-
log4(`[chat ${chat2.id}] done: ${stopReason}`);
|
|
36853
|
-
resolve2();
|
|
36854
|
-
}
|
|
36881
|
+
});
|
|
36855
36882
|
});
|
|
36856
|
-
|
|
36857
|
-
|
|
36858
|
-
|
|
36859
|
-
|
|
36883
|
+
fullReplyParts.push(agentReplyText);
|
|
36884
|
+
};
|
|
36885
|
+
const execAndPostPlan = async () => {
|
|
36886
|
+
try {
|
|
36887
|
+
const { actions, errors: errors3 } = parsePlanBlocks(agentReplyText);
|
|
36888
|
+
if (!actions.length && !errors3.length)
|
|
36889
|
+
return null;
|
|
36860
36890
|
const res = await executeChatPlanActions(actions, errors3, {
|
|
36861
36891
|
apiUrl,
|
|
36862
36892
|
wsId: workspaceId,
|
|
@@ -36879,17 +36909,42 @@ async function processUserMessage(chat2, userMsg, peer, ctx) {
|
|
|
36879
36909
|
});
|
|
36880
36910
|
}
|
|
36881
36911
|
log4(`[chat ${chat2.id}] plan exec: ${res.ok} ok, ${res.fail} fail, ${errors3.length} parse-err`);
|
|
36912
|
+
return { ok: res.ok, fail: res.fail };
|
|
36913
|
+
} catch (e) {
|
|
36914
|
+
log4(`[chat ${chat2.id}] post-turn err: ${e.message}`);
|
|
36915
|
+
return null;
|
|
36882
36916
|
}
|
|
36883
|
-
|
|
36917
|
+
};
|
|
36918
|
+
await runOneTurn(userMsg.text, true);
|
|
36919
|
+
const firstPlan = await execAndPostPlan();
|
|
36920
|
+
if (firstPlan && firstPlan.ok > 0) {
|
|
36921
|
+
const followupPrompt = `[multi system note — auto-injected, not from the user]
|
|
36922
|
+
|
|
36923
|
+
` + `Your previous plan block was executed. Results:
|
|
36924
|
+
|
|
36925
|
+
${fullReplyParts.length ? "(see system message in chat)" : ""}
|
|
36926
|
+
|
|
36927
|
+
` + `If those results need a follow-up multi-plan action (e.g. commenting on a newly-created issue using its returned KEY, or chaining work that depends on freshly-created ids), append ONE more multi-plan block now. Otherwise reply with a short confirmation to the user. Do NOT repeat actions you already ran successfully.`;
|
|
36928
|
+
await runOneTurn(followupPrompt, false);
|
|
36929
|
+
await execAndPostPlan();
|
|
36930
|
+
}
|
|
36931
|
+
try {
|
|
36932
|
+
const combined = fullReplyParts.join(`
|
|
36933
|
+
|
|
36934
|
+
`);
|
|
36935
|
+
const ui = parseUiBlocks(combined);
|
|
36884
36936
|
if (ui.blocks.length)
|
|
36885
36937
|
log4(`[chat ${chat2.id}] ui blocks: ${ui.blocks.length}`);
|
|
36886
36938
|
if (ui.errors.length)
|
|
36887
36939
|
log4(`[chat ${chat2.id}] ui parse err: ${ui.errors.length}`);
|
|
36888
36940
|
} catch (e) {
|
|
36889
|
-
log4(`[chat ${chat2.id}]
|
|
36941
|
+
log4(`[chat ${chat2.id}] ui parse err: ${e.message}`);
|
|
36890
36942
|
}
|
|
36891
|
-
|
|
36892
|
-
|
|
36943
|
+
const fullReply = fullReplyParts.join(`
|
|
36944
|
+
|
|
36945
|
+
`);
|
|
36946
|
+
if (/^new chat$/i.test(chat2.title) && fullReply.trim()) {
|
|
36947
|
+
autoTitle(chat2, userMsg.text, fullReply, ctx).catch((e) => log4(`[chat ${chat2.id}] auto-title failed: ${e.message}`));
|
|
36893
36948
|
}
|
|
36894
36949
|
}
|
|
36895
36950
|
async function autoTitle(chat2, userMsg, agentReply, ctx) {
|
|
@@ -36940,7 +36995,7 @@ ${args2.agents.slice(0, 30).map((a) => `- \`${a.id}\` (${a.name})`).join(`
|
|
|
36940
36995
|
` : "";
|
|
36941
36996
|
return `# multi platform — tool surface
|
|
36942
36997
|
|
|
36943
|
-
You are running inside the multi platform. To create issues, agents, skills, or sessions, append ONE fenced \`multi-plan\` block at the end of your reply (JSON; either an array of actions, or {"actions":[...]}). The host parses it and executes immediately. Do NOT call \`gh\`, GitHub APIs, Linear, Jira, or any external tracker unless the user explicitly names that platform — multi is the default.
|
|
36998
|
+
You are running inside the multi platform. To create issues, agents, skills, or sessions, append ONE fenced \`multi-plan\` block at the end of your reply (JSON; either an array of actions, or {"actions":[...]}). The host parses it and executes immediately. Do NOT call \`gh\`, GitHub APIs, Linear, Jira, or any external tracker unless the user explicitly names that platform — multi is the default. In prose, refer to these as "issues" or by their KEY (e.g. \`ABC-12\`). Never call them "GitHub issues", "Linear issues", or "Jira tickets".
|
|
36944
36999
|
|
|
36945
37000
|
${projectLine}
|
|
36946
37001
|
|
|
@@ -36970,6 +37025,7 @@ Rules:
|
|
|
36970
37025
|
- Max 10 actions per turn. Sub-caps: agent.create=2, skill.create=3, agent.update=5, skill.attach/detach=5, session.create=3, issue.comment=5.
|
|
36971
37026
|
- Chat-initiated agent.create / agent.update / skill.attach are auto-approved (the user is reading this reply right now). skill.create still queues for human review.
|
|
36972
37027
|
- Use \`issue.comment\` with \`@<agent name>\` mention to dispatch an agent on an existing issue. Plain comments without an @mention are recorded but do not trigger a run. Issues whose autonomy is \`manual\` will not dispatch.
|
|
37028
|
+
- To create an issue AND assign/dispatch it to an agent in the same turn, set \`assignee_type\` + \`assignee_id\` directly on the \`create\` action. Do NOT emit a separate \`delegate\` or \`issue.comment\` action referring to a brand-new issue in the same plan block — actions execute as a flat list and the new issue's id/key is not available to later actions in the same block. \`delegate\` and \`issue.comment\` are for issues that already exist before this turn.
|
|
36973
37029
|
- \`allowed_tools\` on a new agent must be a subset of generally available tools.
|
|
36974
37030
|
|
|
36975
37031
|
To draw an inline panel for the user, append a separate fenced \`multi-ui\` block. The host uploads it to a Cloudflare Worker (24h TTL), serves the rendered HTML at a public URL, and embeds it in a sandboxed iframe (\`allow-scripts allow-forms\`). No reactive runtime — write plain HTML with optional inline \`<script>\` for interactivity. \`window.data\` inside your script is the JSON you sent.
|
|
@@ -36996,6 +37052,131 @@ var init_chat_supervisor = __esm(() => {
|
|
|
36996
37052
|
init_lib();
|
|
36997
37053
|
});
|
|
36998
37054
|
|
|
37055
|
+
// src/_impl/chat-session-registry.ts
|
|
37056
|
+
var exports_chat_session_registry = {};
|
|
37057
|
+
__export(exports_chat_session_registry, {
|
|
37058
|
+
chatSessionRegistry: () => chatSessionRegistry
|
|
37059
|
+
});
|
|
37060
|
+
function key(wsId, chatId) {
|
|
37061
|
+
return `${wsId}:${chatId}`;
|
|
37062
|
+
}
|
|
37063
|
+
function clearIdle(s) {
|
|
37064
|
+
if (s.idleTimer) {
|
|
37065
|
+
clearTimeout(s.idleTimer);
|
|
37066
|
+
s.idleTimer = null;
|
|
37067
|
+
}
|
|
37068
|
+
}
|
|
37069
|
+
function scheduleIdle(k, s, log4) {
|
|
37070
|
+
clearIdle(s);
|
|
37071
|
+
s.idleTimer = setTimeout(() => {
|
|
37072
|
+
if (s.inFlight) {
|
|
37073
|
+
scheduleIdle(k, s, log4);
|
|
37074
|
+
return;
|
|
37075
|
+
}
|
|
37076
|
+
log4(`[chat ${k}] idle evict`);
|
|
37077
|
+
try {
|
|
37078
|
+
s.peer.close();
|
|
37079
|
+
} catch {}
|
|
37080
|
+
sessions.delete(k);
|
|
37081
|
+
}, idleTtlMs);
|
|
37082
|
+
}
|
|
37083
|
+
function ensureSession(wsId, chatId, ctx) {
|
|
37084
|
+
const k = key(wsId, chatId);
|
|
37085
|
+
const existing = sessions.get(k);
|
|
37086
|
+
if (existing) {
|
|
37087
|
+
clearIdle(existing);
|
|
37088
|
+
return existing;
|
|
37089
|
+
}
|
|
37090
|
+
const peer = new ChatPeer({
|
|
37091
|
+
apiUrl: ctx.apiUrl,
|
|
37092
|
+
authToken: ctx.authToken,
|
|
37093
|
+
workspaceId: wsId,
|
|
37094
|
+
chatId,
|
|
37095
|
+
primaryAgentId: null,
|
|
37096
|
+
log: ctx.log,
|
|
37097
|
+
onUserMessage: (msg) => {
|
|
37098
|
+
chatSessionRegistry.ensureAndProcess(wsId, chatId, msg.id, ctx);
|
|
37099
|
+
}
|
|
37100
|
+
});
|
|
37101
|
+
peer.start();
|
|
37102
|
+
const now = Date.now();
|
|
37103
|
+
const s = {
|
|
37104
|
+
peer,
|
|
37105
|
+
ctx,
|
|
37106
|
+
chatId,
|
|
37107
|
+
startedAt: now,
|
|
37108
|
+
lastTurnAt: 0,
|
|
37109
|
+
inFlight: null,
|
|
37110
|
+
pending: [],
|
|
37111
|
+
processed: new Set,
|
|
37112
|
+
idleTimer: null
|
|
37113
|
+
};
|
|
37114
|
+
sessions.set(k, s);
|
|
37115
|
+
ctx.log(`[chat ${k}] session opened`);
|
|
37116
|
+
return s;
|
|
37117
|
+
}
|
|
37118
|
+
async function runTurn(s, k, messageId, ctx, chatId) {
|
|
37119
|
+
try {
|
|
37120
|
+
await runChatTurnWithPeer(s.peer, {
|
|
37121
|
+
apiUrl: ctx.apiUrl,
|
|
37122
|
+
authToken: ctx.authToken,
|
|
37123
|
+
workspaceId: ctx.workspaceId,
|
|
37124
|
+
deviceId: ctx.deviceId,
|
|
37125
|
+
chatId,
|
|
37126
|
+
messageId,
|
|
37127
|
+
log: ctx.log
|
|
37128
|
+
});
|
|
37129
|
+
} catch (e) {
|
|
37130
|
+
ctx.log(`[chat ${k}] turn ${messageId} error: ${e.message}`);
|
|
37131
|
+
} finally {
|
|
37132
|
+
s.lastTurnAt = Date.now();
|
|
37133
|
+
const next4 = s.pending.shift();
|
|
37134
|
+
if (next4) {
|
|
37135
|
+
s.inFlight = runTurn(s, k, next4, ctx, chatId);
|
|
37136
|
+
} else {
|
|
37137
|
+
s.inFlight = null;
|
|
37138
|
+
scheduleIdle(k, s, ctx.log);
|
|
37139
|
+
}
|
|
37140
|
+
}
|
|
37141
|
+
}
|
|
37142
|
+
var DEFAULT_IDLE_TTL_MS, sessions, idleTtlMs, chatSessionRegistry;
|
|
37143
|
+
var init_chat_session_registry = __esm(() => {
|
|
37144
|
+
init_chat_peer();
|
|
37145
|
+
init_chat_supervisor();
|
|
37146
|
+
DEFAULT_IDLE_TTL_MS = 10 * 60 * 1000;
|
|
37147
|
+
sessions = new Map;
|
|
37148
|
+
idleTtlMs = DEFAULT_IDLE_TTL_MS;
|
|
37149
|
+
chatSessionRegistry = {
|
|
37150
|
+
setIdleTtl(ms) {
|
|
37151
|
+
idleTtlMs = ms;
|
|
37152
|
+
},
|
|
37153
|
+
async ensureAndProcess(wsId, chatId, messageId, ctx) {
|
|
37154
|
+
const s = ensureSession(wsId, chatId, ctx);
|
|
37155
|
+
const k = key(wsId, chatId);
|
|
37156
|
+
if (s.processed.has(messageId))
|
|
37157
|
+
return;
|
|
37158
|
+
s.processed.add(messageId);
|
|
37159
|
+
if (s.inFlight) {
|
|
37160
|
+
s.pending.push(messageId);
|
|
37161
|
+
return;
|
|
37162
|
+
}
|
|
37163
|
+
s.inFlight = runTurn(s, k, messageId, ctx, chatId);
|
|
37164
|
+
},
|
|
37165
|
+
async closeAll() {
|
|
37166
|
+
for (const [k, s] of sessions) {
|
|
37167
|
+
clearIdle(s);
|
|
37168
|
+
try {
|
|
37169
|
+
s.peer.close();
|
|
37170
|
+
} catch {}
|
|
37171
|
+
sessions.delete(k);
|
|
37172
|
+
}
|
|
37173
|
+
},
|
|
37174
|
+
size() {
|
|
37175
|
+
return sessions.size;
|
|
37176
|
+
}
|
|
37177
|
+
};
|
|
37178
|
+
});
|
|
37179
|
+
|
|
36999
37180
|
// src/_impl/supervisor-tick.ts
|
|
37000
37181
|
var exports_supervisor_tick = {};
|
|
37001
37182
|
__export(exports_supervisor_tick, {
|
|
@@ -37615,7 +37796,7 @@ import { parseArgs } from "util";
|
|
|
37615
37796
|
// package.json
|
|
37616
37797
|
var package_default = {
|
|
37617
37798
|
name: "@shipers-dev/multi",
|
|
37618
|
-
version: "0.
|
|
37799
|
+
version: "0.47.0",
|
|
37619
37800
|
type: "module",
|
|
37620
37801
|
bin: {
|
|
37621
37802
|
"multi-agent": "./dist/index.js"
|
|
@@ -39058,14 +39239,12 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
39058
39239
|
if (!cfg.workspaceId || !cfg.authToken || !cfg.deviceId) {
|
|
39059
39240
|
return Response.json({ error: "daemon not configured" }, { status: 503 });
|
|
39060
39241
|
}
|
|
39061
|
-
const {
|
|
39062
|
-
|
|
39242
|
+
const { chatSessionRegistry: chatSessionRegistry2 } = await Promise.resolve().then(() => (init_chat_session_registry(), exports_chat_session_registry));
|
|
39243
|
+
chatSessionRegistry2.ensureAndProcess(cfg.workspaceId, body.chat_id, body.message_id, {
|
|
39063
39244
|
apiUrl,
|
|
39064
39245
|
authToken: cfg.authToken,
|
|
39065
39246
|
workspaceId: cfg.workspaceId,
|
|
39066
39247
|
deviceId: cfg.deviceId,
|
|
39067
|
-
chatId: body.chat_id,
|
|
39068
|
-
messageId: body.message_id,
|
|
39069
39248
|
log: log3
|
|
39070
39249
|
}).catch((e) => log3(`[chat ${body.chat_id}] runChatTurn error: ${e.message}`));
|
|
39071
39250
|
return Response.json({ accepted: true }, { status: 202 });
|
|
@@ -39253,6 +39432,12 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
39253
39432
|
try {
|
|
39254
39433
|
tunnel?.child?.kill();
|
|
39255
39434
|
} catch {}
|
|
39435
|
+
yield* exports_Effect.promise(async () => {
|
|
39436
|
+
try {
|
|
39437
|
+
const { chatSessionRegistry: chatSessionRegistry2 } = await Promise.resolve().then(() => (init_chat_session_registry(), exports_chat_session_registry));
|
|
39438
|
+
await chatSessionRegistry2.closeAll();
|
|
39439
|
+
} catch {}
|
|
39440
|
+
});
|
|
39256
39441
|
yield* announceTunnel(apiUrl, cfg.workspaceId, cfg.deviceId, null).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
39257
39442
|
const inFlight = [];
|
|
39258
39443
|
for (const e of running3.values())
|