bazilion 0.10.0 → 0.12.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/cli.js +1666 -266
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +4303 -522
- package/dist/daemon.js.map +1 -1
- package/dist/migrations/0001_init.sql +101 -0
- package/dist/web/client/assets/AgentTabs-R6gSjIj3.js +1 -0
- package/dist/web/client/assets/{ProductionTeamPolicyEditor-CiQ-VVDo.js → ProductionTeamPolicyEditor-CSL3W7XM.js} +2 -2
- package/dist/web/client/assets/{ProfileCommunicationEditor-D84F5yfr.js → ProfileCommunicationEditor-CwU0QkmE.js} +1 -1
- package/dist/web/client/assets/{TeamTabs-Btoe_FAE.js → TeamTabs-C1Hakkff.js} +1 -1
- package/dist/web/client/assets/{TemplatesTabs-CAYY03F5.js → TemplatesTabs-klEydJ8-.js} +1 -1
- package/dist/web/client/assets/{_id-BqU2BodK.js → _id-BOC7ioJu.js} +1 -1
- package/dist/web/client/assets/{_id-CKtoio3t.js → _id-CnVq-rED.js} +1 -1
- package/dist/web/client/assets/{_id-BeauECmk.js → _id-e4u7dmBb.js} +1 -1
- package/dist/web/client/assets/{_id-Q98Of6k9.js → _id-wkjjWLz0.js} +1 -1
- package/dist/web/client/assets/{activity-DXN_N85w.js → activity-uqhwzmiy.js} +1 -1
- package/dist/web/client/assets/{agents-C5Xsdge1.js → agents-B2usE-L_.js} +1 -1
- package/dist/web/client/assets/{agents-Cgxo4H5G.js → agents-FaQtkAWs.js} +1 -1
- package/dist/web/client/assets/{approvals-BnG7fbw-.js → approvals-DqLE6ttw.js} +1 -1
- package/dist/web/client/assets/attention-CkGozYnJ.js +1 -0
- package/dist/web/client/assets/attention-Gavt2yEp.js +1 -0
- package/dist/web/client/assets/chevron-down-CJk0zGNT.js +1 -0
- package/dist/web/client/assets/{config-xw4NyrXF.js → config-xrXoDEWM.js} +1 -1
- package/dist/web/client/assets/{context-D2V1Oi4z.js → context-ZWqx3uiC.js} +1 -1
- package/dist/web/client/assets/inbox-BoIvl9ZF.js +1 -0
- package/dist/web/client/assets/{index-VdURSoM4.js → index-nb2MV8X1.js} +3 -3
- package/dist/web/client/assets/learning-6_oYn1l7.js +1 -0
- package/dist/web/client/assets/{login-CvptUoMR.js → login-DM1ytxQL.js} +1 -1
- package/dist/web/client/assets/{mcp-BDFM_sW-.js → mcp-CetZbyUh.js} +1 -1
- package/dist/web/client/assets/{members-BJvph_TR.js → members-DdqWtwZd.js} +1 -1
- package/dist/web/client/assets/{memory-DRytqZCD.js → memory-FNrxFHmC.js} +1 -1
- package/dist/web/client/assets/plus-C3GroITC.js +1 -0
- package/dist/web/client/assets/{policy-CGP50OtQ.js → policy-BKPejYRI.js} +1 -1
- package/dist/web/client/assets/{routes-DJnWYvC9.js → routes-CH1ZTTo-.js} +1 -1
- package/dist/web/client/assets/{services-DHheiv7L.js → services-zk9SqN06.js} +1 -1
- package/dist/web/client/assets/{skills-DmsitluU.js → skills-tXDpomHW.js} +1 -1
- package/dist/web/client/assets/styles-DMltY3su.css +2 -0
- package/dist/web/client/assets/{teams-BdC4gthq.js → teams-BZYjekBN.js} +1 -1
- package/dist/web/client/assets/{teams-CbxSaorm.js → teams-D1c8zlxs.js} +1 -1
- package/dist/web/client/assets/telegram-DCe1CWMt.js +1 -0
- package/dist/web/client/assets/{tokens-CjRplDrJ.js → tokens-DAPjRS0N.js} +1 -1
- package/dist/web/client/assets/triggers-D8R-5guA.js +1 -0
- package/dist/web/client/assets/{welcome-wBfX9dAd.js → welcome-CuK0shDJ.js} +1 -1
- package/dist/web/client/assets/wire-constants-BhVKOCQa.js +74 -0
- package/dist/web/server/assets/{AgentTabs-DoMDJBXH.js → AgentTabs-aXdsiiMp.js} +5 -0
- package/dist/web/server/assets/{ChatPane-eUY_dga3.js → ChatPane-Ba5wTsVk.js} +325 -36
- package/dist/web/server/assets/{_id-ClN9hY2x.js → _id-BHYLL8M-.js} +3 -3
- package/dist/web/server/assets/{_id-DkYodSkG.js → _id-FcHdSocl.js} +1 -1
- package/dist/web/server/assets/{_tanstack-start-manifest_v-D09Xu_6u.js → _tanstack-start-manifest_v-fLsYERQ-.js} +77 -54
- package/dist/web/server/assets/attention-BW_6ddkj.js +11 -0
- package/dist/web/server/assets/attention-BxkTf337.js +14 -0
- package/dist/web/server/assets/attention-CM-Ks1N9.js +19 -0
- package/dist/web/server/assets/attention-w21GQIGH.js +264 -0
- package/dist/web/server/assets/{inbox-CUIyf9MT.js → inbox-BLUzA5sQ.js} +40 -3
- package/dist/web/server/assets/{inbox-B3-TRsNI.js → inbox-CzlKT9Wg.js} +5 -3
- package/dist/web/server/assets/{inbox-BardsGyy.js → inbox-ZvDNAjaM.js} +1 -1
- package/dist/web/server/assets/learning-8zav4pIT.js +33 -0
- package/dist/web/server/assets/learning-B7PBkvER.js +16 -0
- package/dist/web/server/assets/learning-Cq7K2heb.js +312 -0
- package/dist/web/server/assets/{router-DIdp8ZNt.js → router-CQuj7553.js} +81 -48
- package/dist/web/server/assets/{routes-Jb0zjKav.js → routes-B6cMLIPk.js} +3 -3
- package/dist/web/server/assets/{routes-CXz4hTAr.js → routes-BrA-U_Hm.js} +1 -1
- package/dist/web/server/assets/{routes-lIclH9Br.js → routes-C19CMLZt.js} +1 -1
- package/dist/web/server/assets/{telegram-7ub--6xz.js → telegram-CBF8SJzB.js} +1 -24
- package/dist/web/server/assets/{telegram-Bazhmpr4.js → telegram-XwyKSEtj.js} +1 -1
- package/dist/web/server/assets/{triggers-Ca57_qym.js → triggers-BZPl5CHs.js} +1 -1
- package/dist/web/server/assets/{triggers-BMpHsGIr.js → triggers-CSNNzRIi.js} +28 -4
- package/dist/web/server/assets/{triggers-C_0y6QV9.js → triggers-D79NoSXk.js} +3 -2
- package/dist/web/server/server.js +13 -5
- package/dist/worker.js +1752 -148
- package/dist/worker.js.map +1 -1
- package/package.json +4 -3
- package/dist/web/client/assets/AgentTabs-B3hq5a3e.js +0 -1
- package/dist/web/client/assets/chevron-down-BhcBspw3.js +0 -1
- package/dist/web/client/assets/inbox-DK6hWCMp.js +0 -1
- package/dist/web/client/assets/plus-C9ee3SIO.js +0 -1
- package/dist/web/client/assets/styles-B5eQAwLP.css +0 -2
- package/dist/web/client/assets/telegram-D61G_alO.js +0 -1
- package/dist/web/client/assets/triggers-MLzTm_DM.js +0 -1
- package/dist/web/client/assets/wire-constants-BxfyUMXB.js +0 -74
- package/dist/web/server/assets/{Sidebar-O_CH1-JD.js → Sidebar-DsRVCszF.js} +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { t as Button } from "./Button-Dsnw22OF.js";
|
|
1
2
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import DOMPurify from "dompurify";
|
|
4
5
|
import { marked } from "marked";
|
|
5
6
|
//#region src/lib/md.ts
|
|
@@ -40,6 +41,30 @@ var AUTOSCROLL_THRESHOLD_PX = 40;
|
|
|
40
41
|
var SESSION_POLL_MS = 1e4;
|
|
41
42
|
var TOOL_GROUP_MAX_HEIGHT_PX = 320;
|
|
42
43
|
var MAX_INPUT_HEIGHT = 200;
|
|
44
|
+
/** Keep one inline card per ephemeral shell approval while preserving its first position. */
|
|
45
|
+
function upsertCommandApprovalEntry(entries, approval) {
|
|
46
|
+
const index = entries.findIndex((entry) => entry.type === "command_approval" && entry.approval.id === approval.id);
|
|
47
|
+
if (index === -1) return [...entries, {
|
|
48
|
+
type: "command_approval",
|
|
49
|
+
approval
|
|
50
|
+
}];
|
|
51
|
+
const next = [...entries];
|
|
52
|
+
next[index] = {
|
|
53
|
+
type: "command_approval",
|
|
54
|
+
approval
|
|
55
|
+
};
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
function interactiveChatRequest(message, attachments) {
|
|
59
|
+
return {
|
|
60
|
+
message,
|
|
61
|
+
attachments,
|
|
62
|
+
bashApprovalMode: "interactive"
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function shellApprovalsUrl(agentId) {
|
|
66
|
+
return `/api/shell-approvals?agentId=${encodeURIComponent(agentId)}`;
|
|
67
|
+
}
|
|
43
68
|
var SLASH_HELP = "slash commands:\n /context — context breakdown (system prompt, tools, skills, history)\n /compact [N] — summarize the head; keep the last N messages verbatim (default 10)\n /reset — reset chat history for this agent\n /help — show this list";
|
|
44
69
|
function prettyArgs(raw) {
|
|
45
70
|
if (!raw) return "";
|
|
@@ -141,10 +166,16 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
141
166
|
const [attachments, setAttachments] = useState([]);
|
|
142
167
|
const [dragging, setDragging] = useState(false);
|
|
143
168
|
const [staleBanner, setStaleBanner] = useState(false);
|
|
169
|
+
const [recoveredTurn, setRecoveredTurn] = useState(false);
|
|
170
|
+
const [approvalBusy, setApprovalBusy] = useState({});
|
|
171
|
+
const [approvalErrors, setApprovalErrors] = useState({});
|
|
144
172
|
const messagesRef = useRef(null);
|
|
145
173
|
const fileInputRef = useRef(null);
|
|
146
174
|
const inputRef = useRef(null);
|
|
147
175
|
const currentAbortRef = useRef(null);
|
|
176
|
+
const currentAgentIdRef = useRef(agentId);
|
|
177
|
+
const commandApprovalTurnRef = useRef(false);
|
|
178
|
+
currentAgentIdRef.current = agentId;
|
|
148
179
|
const systemIdRef = useRef(0);
|
|
149
180
|
const visibleEntryCountRef = useRef(0);
|
|
150
181
|
const knownHeadRef = useRef(initialSessionHead ?? {
|
|
@@ -153,7 +184,11 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
153
184
|
});
|
|
154
185
|
const streamingRef = useRef(false);
|
|
155
186
|
streamingRef.current = streaming;
|
|
187
|
+
const turnBusy = streaming || recoveredTurn;
|
|
156
188
|
useEffect(() => {
|
|
189
|
+
currentAbortRef.current?.abort();
|
|
190
|
+
currentAbortRef.current = null;
|
|
191
|
+
commandApprovalTurnRef.current = false;
|
|
157
192
|
setServerMessages(initialMessages);
|
|
158
193
|
setLiveEntries([]);
|
|
159
194
|
setSystemBubbles([]);
|
|
@@ -162,11 +197,37 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
162
197
|
setThinking(false);
|
|
163
198
|
setStreaming(false);
|
|
164
199
|
setStaleBanner(false);
|
|
200
|
+
setRecoveredTurn(false);
|
|
201
|
+
setApprovalBusy({});
|
|
202
|
+
setApprovalErrors({});
|
|
165
203
|
knownHeadRef.current = initialSessionHead ?? {
|
|
166
204
|
file: null,
|
|
167
205
|
size: 0
|
|
168
206
|
};
|
|
169
207
|
}, [agentId]);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
const controller = new AbortController();
|
|
210
|
+
let stopped = false;
|
|
211
|
+
async function recoverPendingCommandApprovals() {
|
|
212
|
+
try {
|
|
213
|
+
const response = await fetch(shellApprovalsUrl(agentId), { signal: controller.signal });
|
|
214
|
+
if (!response.ok) return;
|
|
215
|
+
const pending = (await response.json()).approvals.filter((approval) => approval.agentId === agentId && approval.status === "pending");
|
|
216
|
+
if (stopped || pending.length === 0) return;
|
|
217
|
+
sessionStorage.removeItem(`bz_pending_${agentId}`);
|
|
218
|
+
commandApprovalTurnRef.current = true;
|
|
219
|
+
setLiveEntries((entries) => pending.reduce(upsertCommandApprovalEntry, entries));
|
|
220
|
+
setRecoveredTurn(true);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error.name === "AbortError") return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
recoverPendingCommandApprovals();
|
|
226
|
+
return () => {
|
|
227
|
+
stopped = true;
|
|
228
|
+
controller.abort();
|
|
229
|
+
};
|
|
230
|
+
}, [agentId]);
|
|
170
231
|
const wasNearBottomRef = useRef(true);
|
|
171
232
|
useLayoutEffect(() => {
|
|
172
233
|
const el = messagesRef.current;
|
|
@@ -227,11 +288,12 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
227
288
|
staleBanner
|
|
228
289
|
]);
|
|
229
290
|
async function refreshKnownHead() {
|
|
291
|
+
if (currentAgentIdRef.current !== agentId) return;
|
|
230
292
|
try {
|
|
231
293
|
const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}/sessions/head`);
|
|
232
294
|
if (!res.ok) return;
|
|
233
295
|
const body = await res.json();
|
|
234
|
-
if (typeof body.size === "number") knownHeadRef.current = {
|
|
296
|
+
if (currentAgentIdRef.current === agentId && typeof body.size === "number") knownHeadRef.current = {
|
|
235
297
|
file: body.file ?? null,
|
|
236
298
|
size: body.size
|
|
237
299
|
};
|
|
@@ -369,7 +431,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
369
431
|
return -1;
|
|
370
432
|
}
|
|
371
433
|
function enterEditMode() {
|
|
372
|
-
if (liveEntries.length > 0 ||
|
|
434
|
+
if (liveEntries.length > 0 || turnBusy) return;
|
|
373
435
|
const idx = findLastUserIdx();
|
|
374
436
|
if (idx === -1) return;
|
|
375
437
|
const userMsg = serverMessages[idx];
|
|
@@ -382,7 +444,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
382
444
|
setEditIdx(null);
|
|
383
445
|
}
|
|
384
446
|
async function addFiles(files) {
|
|
385
|
-
if (!files ||
|
|
447
|
+
if (!files || turnBusy) return;
|
|
386
448
|
const arr = Array.from(files);
|
|
387
449
|
if (arr.length === 0) return;
|
|
388
450
|
const encoded = await Promise.all(arr.map(fileToAttachment));
|
|
@@ -398,7 +460,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
398
460
|
function onDragOver(e) {
|
|
399
461
|
if (!Array.from(e.dataTransfer.types).includes("Files")) return;
|
|
400
462
|
e.preventDefault();
|
|
401
|
-
if (
|
|
463
|
+
if (turnBusy) {
|
|
402
464
|
setDragging(false);
|
|
403
465
|
return;
|
|
404
466
|
}
|
|
@@ -411,12 +473,12 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
411
473
|
if (e.dataTransfer.files.length === 0) return;
|
|
412
474
|
e.preventDefault();
|
|
413
475
|
setDragging(false);
|
|
414
|
-
if (
|
|
476
|
+
if (turnBusy) return;
|
|
415
477
|
addFiles(e.dataTransfer.files);
|
|
416
478
|
}
|
|
417
479
|
const send = useCallback(async (text) => {
|
|
418
480
|
const atts = attachments;
|
|
419
|
-
if (!text.trim() && atts.length === 0 ||
|
|
481
|
+
if (!text.trim() && atts.length === 0 || turnBusy) return;
|
|
420
482
|
setInput("");
|
|
421
483
|
if (await maybeHandleSlashCommand(text)) return;
|
|
422
484
|
setAttachments([]);
|
|
@@ -456,19 +518,21 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
456
518
|
...previewImages.length > 0 ? { images: previewImages } : {},
|
|
457
519
|
...previewFiles.length > 0 ? { files: previewFiles.map((f) => ({ name: f.name ?? "file" })) } : {}
|
|
458
520
|
}]);
|
|
521
|
+
setRecoveredTurn(false);
|
|
522
|
+
setApprovalBusy({});
|
|
523
|
+
setApprovalErrors({});
|
|
524
|
+
commandApprovalTurnRef.current = false;
|
|
459
525
|
sessionStorage.setItem(`bz_pending_${agentId}`, "1");
|
|
460
526
|
const abort = new AbortController();
|
|
461
527
|
currentAbortRef.current = abort;
|
|
462
528
|
setStreaming(true);
|
|
463
529
|
setThinking(true);
|
|
530
|
+
let terminalFrameReceived = false;
|
|
464
531
|
try {
|
|
465
532
|
const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}/chat`, {
|
|
466
533
|
method: "POST",
|
|
467
534
|
headers: { "content-type": "application/json" },
|
|
468
|
-
body: JSON.stringify(
|
|
469
|
-
message: text,
|
|
470
|
-
attachments: atts
|
|
471
|
-
}),
|
|
535
|
+
body: JSON.stringify(interactiveChatRequest(text, atts)),
|
|
472
536
|
signal: abort.signal
|
|
473
537
|
});
|
|
474
538
|
if (res.status === 202) {
|
|
@@ -508,11 +572,14 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
508
572
|
} catch {
|
|
509
573
|
continue;
|
|
510
574
|
}
|
|
575
|
+
if (frame.kind === "done" || frame.kind === "fatal") terminalFrameReceived = true;
|
|
511
576
|
handleFrame(frame);
|
|
512
577
|
}
|
|
513
578
|
}
|
|
514
579
|
if (buffer.trim()) try {
|
|
515
|
-
|
|
580
|
+
const frame = JSON.parse(buffer);
|
|
581
|
+
if (frame.kind === "done" || frame.kind === "fatal") terminalFrameReceived = true;
|
|
582
|
+
handleFrame(frame);
|
|
516
583
|
} catch {}
|
|
517
584
|
await refreshKnownHead();
|
|
518
585
|
sessionStorage.removeItem(`bz_pending_${agentId}`);
|
|
@@ -523,28 +590,44 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
523
590
|
}]);
|
|
524
591
|
sessionStorage.removeItem(`bz_pending_${agentId}`);
|
|
525
592
|
} finally {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
593
|
+
if (currentAgentIdRef.current === agentId && currentAbortRef.current === abort) {
|
|
594
|
+
if (!terminalFrameReceived && commandApprovalTurnRef.current) setRecoveredTurn(true);
|
|
595
|
+
setThinking(false);
|
|
596
|
+
setStreaming(false);
|
|
597
|
+
currentAbortRef.current = null;
|
|
598
|
+
}
|
|
529
599
|
}
|
|
530
600
|
}, [
|
|
531
601
|
agentId,
|
|
532
602
|
editIdx,
|
|
533
603
|
serverMessages,
|
|
534
|
-
|
|
604
|
+
turnBusy,
|
|
535
605
|
attachments
|
|
536
606
|
]);
|
|
537
607
|
function handleFrame(frame) {
|
|
608
|
+
if (currentAgentIdRef.current !== agentId) return;
|
|
538
609
|
if (frame.kind === "fatal") {
|
|
539
|
-
|
|
610
|
+
commandApprovalTurnRef.current = false;
|
|
611
|
+
setLiveEntries((prev) => [...prev.map((entry) => entry.type === "command_approval" && entry.approval.status === "pending" ? {
|
|
612
|
+
type: "command_approval",
|
|
613
|
+
approval: {
|
|
614
|
+
...entry.approval,
|
|
615
|
+
status: "cancelled"
|
|
616
|
+
}
|
|
617
|
+
} : entry), {
|
|
540
618
|
type: "error",
|
|
541
619
|
content: `[fatal] ${frame.error}`
|
|
542
620
|
}]);
|
|
621
|
+
setRecoveredTurn(false);
|
|
543
622
|
return;
|
|
544
623
|
}
|
|
545
624
|
if (frame.kind === "done") {
|
|
625
|
+
commandApprovalTurnRef.current = false;
|
|
546
626
|
setServerMessages(frame.messages);
|
|
547
627
|
setLiveEntries([]);
|
|
628
|
+
setRecoveredTurn(false);
|
|
629
|
+
setApprovalBusy({});
|
|
630
|
+
setApprovalErrors({});
|
|
548
631
|
return;
|
|
549
632
|
}
|
|
550
633
|
if (frame.kind !== "event") return;
|
|
@@ -584,6 +667,13 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
584
667
|
});
|
|
585
668
|
return;
|
|
586
669
|
}
|
|
670
|
+
if (ev.type === "command_approval") {
|
|
671
|
+
if (ev.approval.agentId !== agentId) return;
|
|
672
|
+
commandApprovalTurnRef.current = true;
|
|
673
|
+
setThinking(ev.approval.status !== "pending" && ev.approval.status !== "cancelled");
|
|
674
|
+
setLiveEntries((entries) => upsertCommandApprovalEntry(entries, ev.approval));
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
587
677
|
if (ev.type === "tool_call" || ev.type === "tool_result" || ev.type === "tool_error") {
|
|
588
678
|
setThinking(ev.type !== "tool_call");
|
|
589
679
|
const item = ev.type === "tool_call" ? {
|
|
@@ -636,12 +726,92 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
636
726
|
content: `[error] ${ev.error}`
|
|
637
727
|
}]);
|
|
638
728
|
}
|
|
729
|
+
async function reconcileCommandApproval(approvalId) {
|
|
730
|
+
if (currentAgentIdRef.current !== agentId) return null;
|
|
731
|
+
try {
|
|
732
|
+
const response = await fetch(shellApprovalsUrl(agentId));
|
|
733
|
+
if (!response.ok) return null;
|
|
734
|
+
const body = await response.json();
|
|
735
|
+
if (currentAgentIdRef.current !== agentId) return null;
|
|
736
|
+
const approval = body.approvals.find((item) => item.id === approvalId && item.agentId === agentId);
|
|
737
|
+
if (approval) setLiveEntries((entries) => upsertCommandApprovalEntry(entries, approval));
|
|
738
|
+
return approval ?? null;
|
|
739
|
+
} catch {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
async function decideCommandApproval(approval, decision) {
|
|
744
|
+
if (approval.agentId !== agentId || approval.status !== "pending") return;
|
|
745
|
+
setApprovalBusy((current) => ({
|
|
746
|
+
...current,
|
|
747
|
+
[approval.id]: true
|
|
748
|
+
}));
|
|
749
|
+
setApprovalErrors((current) => {
|
|
750
|
+
const next = { ...current };
|
|
751
|
+
delete next[approval.id];
|
|
752
|
+
return next;
|
|
753
|
+
});
|
|
754
|
+
try {
|
|
755
|
+
const response = await fetch(`/api/shell-approvals/${encodeURIComponent(approval.id)}`, {
|
|
756
|
+
method: "POST",
|
|
757
|
+
headers: { "content-type": "application/json" },
|
|
758
|
+
body: JSON.stringify({ decision })
|
|
759
|
+
});
|
|
760
|
+
const body = await response.json().catch(() => null);
|
|
761
|
+
if (currentAgentIdRef.current !== agentId) return;
|
|
762
|
+
if (body?.approval?.agentId === agentId) setLiveEntries((entries) => upsertCommandApprovalEntry(entries, body.approval));
|
|
763
|
+
if (!response.ok) {
|
|
764
|
+
const message = body?.error ?? `Decision failed (${response.status})`;
|
|
765
|
+
const terminalStatus = commandApprovalStatusFromConflict(message);
|
|
766
|
+
if (terminalStatus) {
|
|
767
|
+
setLiveEntries((entries) => upsertCommandApprovalEntry(entries, {
|
|
768
|
+
...approval,
|
|
769
|
+
status: terminalStatus
|
|
770
|
+
}));
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
throw new Error(message);
|
|
774
|
+
}
|
|
775
|
+
} catch (error) {
|
|
776
|
+
const reconciled = await reconcileCommandApproval(approval.id);
|
|
777
|
+
if (currentAgentIdRef.current !== agentId) return;
|
|
778
|
+
if (!reconciled || reconciled.status === "pending") setApprovalErrors((current) => ({
|
|
779
|
+
...current,
|
|
780
|
+
[approval.id]: error instanceof Error ? error.message : String(error)
|
|
781
|
+
}));
|
|
782
|
+
} finally {
|
|
783
|
+
if (currentAgentIdRef.current === agentId) setApprovalBusy((current) => ({
|
|
784
|
+
...current,
|
|
785
|
+
[approval.id]: false
|
|
786
|
+
}));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
639
789
|
async function cancel() {
|
|
790
|
+
const hasLocalStream = currentAbortRef.current !== null;
|
|
640
791
|
try {
|
|
641
792
|
const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}/cancel`, { method: "POST" });
|
|
642
|
-
if (
|
|
793
|
+
if (currentAgentIdRef.current !== agentId) return;
|
|
794
|
+
if (res.ok || res.status === 204) {
|
|
795
|
+
if (!hasLocalStream) {
|
|
796
|
+
commandApprovalTurnRef.current = false;
|
|
797
|
+
setLiveEntries((entries) => entries.map((entry) => entry.type === "command_approval" && entry.approval.status === "pending" ? {
|
|
798
|
+
type: "command_approval",
|
|
799
|
+
approval: {
|
|
800
|
+
...entry.approval,
|
|
801
|
+
status: "cancelled"
|
|
802
|
+
}
|
|
803
|
+
} : entry));
|
|
804
|
+
setRecoveredTurn(false);
|
|
805
|
+
}
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
643
808
|
} catch {}
|
|
644
|
-
|
|
809
|
+
if (currentAgentIdRef.current !== agentId) return;
|
|
810
|
+
if (hasLocalStream) currentAbortRef.current?.abort();
|
|
811
|
+
else setLiveEntries((entries) => [...entries, {
|
|
812
|
+
type: "error",
|
|
813
|
+
content: "[cancel failed] could not reach the active turn"
|
|
814
|
+
}]);
|
|
645
815
|
}
|
|
646
816
|
function onKeyDown(e) {
|
|
647
817
|
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
@@ -652,7 +822,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
652
822
|
const baseEntries = projectMessages(serverMessages);
|
|
653
823
|
visibleEntryCountRef.current = baseEntries.length + liveEntries.length;
|
|
654
824
|
const lastUserIdx = (() => {
|
|
655
|
-
if (liveEntries.length > 0 ||
|
|
825
|
+
if (liveEntries.length > 0 || turnBusy) return -1;
|
|
656
826
|
for (let i = baseEntries.length - 1; i >= 0; i--) if (baseEntries[i]?.type === "user") return i;
|
|
657
827
|
return -1;
|
|
658
828
|
})();
|
|
@@ -702,7 +872,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
702
872
|
}),
|
|
703
873
|
/* @__PURE__ */ jsx("span", {
|
|
704
874
|
className: "flex-1",
|
|
705
|
-
children: "new activity from another source — reload to see it"
|
|
875
|
+
children: recoveredTurn ? "the recovered turn has new activity — reload to see it" : "new activity from another source — reload to see it"
|
|
706
876
|
}),
|
|
707
877
|
/* @__PURE__ */ jsx("button", {
|
|
708
878
|
type: "button",
|
|
@@ -734,10 +904,13 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
734
904
|
const flushSysUpTo = (idx) => {
|
|
735
905
|
while (sysIdx < systemBubbles.length && systemBubbles[sysIdx].afterIdx <= idx) {
|
|
736
906
|
const sb = systemBubbles[sysIdx];
|
|
737
|
-
out.push(/* @__PURE__ */ jsx(Bubble, {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
907
|
+
out.push(/* @__PURE__ */ jsx(Bubble, {
|
|
908
|
+
entry: {
|
|
909
|
+
type: "system",
|
|
910
|
+
content: sb.content
|
|
911
|
+
},
|
|
912
|
+
onCommandApprovalDecision: decideCommandApproval
|
|
913
|
+
}, `y-${sb.id}`));
|
|
741
914
|
sysIdx++;
|
|
742
915
|
}
|
|
743
916
|
};
|
|
@@ -748,21 +921,32 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
748
921
|
entry,
|
|
749
922
|
isLastUser: i === lastUserIdx && editIdx === null,
|
|
750
923
|
isWillDrop: willDropFromIdx !== -1 && i >= willDropFromIdx,
|
|
751
|
-
onEdit: enterEditMode
|
|
924
|
+
onEdit: enterEditMode,
|
|
925
|
+
onCommandApprovalDecision: decideCommandApproval,
|
|
926
|
+
commandApprovalBusy: entry.type === "command_approval" && Boolean(approvalBusy[entry.approval.id]),
|
|
927
|
+
commandApprovalError: entry.type === "command_approval" ? approvalErrors[entry.approval.id] : void 0
|
|
752
928
|
}, `s-${i}`));
|
|
753
929
|
flushSysUpTo(i + 1);
|
|
754
930
|
}
|
|
755
931
|
for (let i = 0; i < liveEntries.length; i++) {
|
|
756
932
|
const entry = liveEntries[i];
|
|
757
|
-
out.push(/* @__PURE__ */ jsx(Bubble, {
|
|
933
|
+
out.push(/* @__PURE__ */ jsx(Bubble, {
|
|
934
|
+
entry,
|
|
935
|
+
onCommandApprovalDecision: decideCommandApproval,
|
|
936
|
+
commandApprovalBusy: entry.type === "command_approval" && Boolean(approvalBusy[entry.approval.id]),
|
|
937
|
+
commandApprovalError: entry.type === "command_approval" ? approvalErrors[entry.approval.id] : void 0
|
|
938
|
+
}, `l-${i}`));
|
|
758
939
|
flushSysUpTo(baseEntries.length + i + 1);
|
|
759
940
|
}
|
|
760
941
|
while (sysIdx < systemBubbles.length) {
|
|
761
942
|
const sb = systemBubbles[sysIdx];
|
|
762
|
-
out.push(/* @__PURE__ */ jsx(Bubble, {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
943
|
+
out.push(/* @__PURE__ */ jsx(Bubble, {
|
|
944
|
+
entry: {
|
|
945
|
+
type: "system",
|
|
946
|
+
content: sb.content
|
|
947
|
+
},
|
|
948
|
+
onCommandApprovalDecision: decideCommandApproval
|
|
949
|
+
}, `y-${sb.id}`));
|
|
766
950
|
sysIdx++;
|
|
767
951
|
}
|
|
768
952
|
return out;
|
|
@@ -843,7 +1027,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
843
1027
|
/* @__PURE__ */ jsx("button", {
|
|
844
1028
|
type: "button",
|
|
845
1029
|
onClick: () => fileInputRef.current?.click(),
|
|
846
|
-
disabled:
|
|
1030
|
+
disabled: turnBusy,
|
|
847
1031
|
title: "attach images or files",
|
|
848
1032
|
"aria-label": "attach files",
|
|
849
1033
|
className: "rounded-md border-[1.5px] border-frost bg-snow px-3 py-2 text-[1em] text-mocha transition-colors hover:border-sapphire hover:text-sapphire disabled:cursor-not-allowed disabled:opacity-50",
|
|
@@ -856,18 +1040,18 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
856
1040
|
onChange: (e) => setInput(e.target.value),
|
|
857
1041
|
onKeyDown,
|
|
858
1042
|
onPaste,
|
|
859
|
-
disabled:
|
|
1043
|
+
disabled: turnBusy,
|
|
860
1044
|
placeholder: "say something… (Shift+Enter for newline; paste or 📎 to attach images/files)",
|
|
861
1045
|
autoComplete: "off",
|
|
862
1046
|
className: "max-h-[200px] min-h-[2.4rem] flex-1 resize-none overflow-y-auto rounded-md border-[1.5px] border-frost bg-snow px-3 py-2 text-[0.93em] leading-[1.45] text-chocolate outline-none transition-colors focus:border-sapphire focus:shadow-[0_0_0_3px_var(--color-sapphire-glow)]"
|
|
863
1047
|
}),
|
|
864
1048
|
/* @__PURE__ */ jsx("button", {
|
|
865
1049
|
type: "submit",
|
|
866
|
-
disabled:
|
|
1050
|
+
disabled: turnBusy || !input.trim() && attachments.length === 0,
|
|
867
1051
|
className: "rounded-md bg-sapphire px-4 py-2 text-[0.92em] font-semibold text-snow transition-colors hover:bg-sapphire-deep disabled:cursor-not-allowed disabled:opacity-50",
|
|
868
1052
|
children: "send"
|
|
869
1053
|
}),
|
|
870
|
-
|
|
1054
|
+
turnBusy && /* @__PURE__ */ jsx("button", {
|
|
871
1055
|
type: "button",
|
|
872
1056
|
onClick: cancel,
|
|
873
1057
|
className: "rounded-md border-[1.5px] border-frost bg-transparent px-3 py-1.5 text-[0.92em] font-medium text-mocha hover:border-sapphire-light hover:bg-sapphire-glow hover:text-sapphire",
|
|
@@ -878,7 +1062,7 @@ function ChatPane({ agentId, agentName, initialMessages, initialSessionHead }) {
|
|
|
878
1062
|
]
|
|
879
1063
|
});
|
|
880
1064
|
}
|
|
881
|
-
function Bubble({ entry, isLastUser, isWillDrop, onEdit }) {
|
|
1065
|
+
function Bubble({ entry, isLastUser, isWillDrop, onEdit, onCommandApprovalDecision, commandApprovalBusy, commandApprovalError }) {
|
|
882
1066
|
const dropCls = isWillDrop ? "opacity-40 [&_.bubble-content]:line-through" : "";
|
|
883
1067
|
if (entry.type === "user") {
|
|
884
1068
|
if (entry.content.startsWith(INBOX_WAKE_PREFIX)) {
|
|
@@ -1007,6 +1191,12 @@ function Bubble({ entry, isLastUser, isWillDrop, onEdit }) {
|
|
|
1007
1191
|
})]
|
|
1008
1192
|
});
|
|
1009
1193
|
}
|
|
1194
|
+
if (entry.type === "command_approval") return /* @__PURE__ */ jsx(CommandApprovalCard, {
|
|
1195
|
+
approval: entry.approval,
|
|
1196
|
+
busy: commandApprovalBusy,
|
|
1197
|
+
error: commandApprovalError,
|
|
1198
|
+
onDecision: onCommandApprovalDecision
|
|
1199
|
+
});
|
|
1010
1200
|
if (entry.type === "system") return /* @__PURE__ */ jsxs("div", {
|
|
1011
1201
|
className: `my-3 rounded-r-sm border-l-[3px] border-sapphire bg-sapphire-glow px-3 py-1 font-mono text-[0.88em] text-sapphire-deep ${dropCls}`,
|
|
1012
1202
|
children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -1023,6 +1213,105 @@ function Bubble({ entry, isLastUser, isWillDrop, onEdit }) {
|
|
|
1023
1213
|
});
|
|
1024
1214
|
return null;
|
|
1025
1215
|
}
|
|
1216
|
+
function CommandApprovalCard({ approval, busy = false, error, onDecision }) {
|
|
1217
|
+
const pending = approval.status === "pending";
|
|
1218
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1219
|
+
className: "my-3 rounded-lg border border-danger/50 bg-danger/5 p-4 text-chocolate",
|
|
1220
|
+
"aria-label": "Shell command approval",
|
|
1221
|
+
children: [
|
|
1222
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1223
|
+
className: "flex flex-wrap items-start justify-between gap-2",
|
|
1224
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
1225
|
+
className: "m-0 text-[0.72em] font-semibold uppercase tracking-wider text-danger",
|
|
1226
|
+
children: "Shell command approval"
|
|
1227
|
+
}), /* @__PURE__ */ jsx("h2", {
|
|
1228
|
+
className: "m-0 mt-1 font-body text-[0.95em] font-semibold",
|
|
1229
|
+
children: pending ? "A risky command is waiting for you" : "Command decision recorded"
|
|
1230
|
+
})] }), /* @__PURE__ */ jsx("span", {
|
|
1231
|
+
className: "rounded-full border border-danger/30 px-2 py-0.5 text-[0.75em] font-semibold uppercase tracking-wide text-danger",
|
|
1232
|
+
children: approval.status.replaceAll("_", " ")
|
|
1233
|
+
})]
|
|
1234
|
+
}),
|
|
1235
|
+
/* @__PURE__ */ jsx("pre", {
|
|
1236
|
+
className: "mt-3 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-md border border-fawn bg-snow p-3 font-mono text-[0.82em] leading-[1.45] text-charcoal",
|
|
1237
|
+
children: approval.command
|
|
1238
|
+
}),
|
|
1239
|
+
/* @__PURE__ */ jsx("ul", {
|
|
1240
|
+
className: "mt-3 space-y-1.5 pl-5 text-[0.84em] leading-[1.4] text-mocha",
|
|
1241
|
+
children: approval.risks.map((risk) => /* @__PURE__ */ jsxs("li", { children: [
|
|
1242
|
+
/* @__PURE__ */ jsx("span", {
|
|
1243
|
+
className: "font-medium text-chocolate",
|
|
1244
|
+
children: risk.message
|
|
1245
|
+
}),
|
|
1246
|
+
" ",
|
|
1247
|
+
/* @__PURE__ */ jsx("code", {
|
|
1248
|
+
className: "text-[0.9em]",
|
|
1249
|
+
children: risk.code
|
|
1250
|
+
})
|
|
1251
|
+
] }, `${risk.code}:${risk.span.start}:${risk.span.end}`))
|
|
1252
|
+
}),
|
|
1253
|
+
pending ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1254
|
+
/* @__PURE__ */ jsxs("p", {
|
|
1255
|
+
className: "mt-3 text-[0.78em] text-mocha-light",
|
|
1256
|
+
children: [
|
|
1257
|
+
"This approval expires",
|
|
1258
|
+
" ",
|
|
1259
|
+
/* @__PURE__ */ jsx("time", {
|
|
1260
|
+
dateTime: new Date(approval.expiresAt).toISOString(),
|
|
1261
|
+
children: new Date(approval.expiresAt).toLocaleString()
|
|
1262
|
+
}),
|
|
1263
|
+
". Deny blocks only this command; cancel stops the whole turn."
|
|
1264
|
+
]
|
|
1265
|
+
}),
|
|
1266
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
1267
|
+
role: "alert",
|
|
1268
|
+
className: "mt-2 rounded-md bg-danger/10 px-3 py-2 text-[0.82em] text-danger",
|
|
1269
|
+
children: error
|
|
1270
|
+
}),
|
|
1271
|
+
onDecision && /* @__PURE__ */ jsxs("div", {
|
|
1272
|
+
className: "mt-3 flex flex-wrap gap-2",
|
|
1273
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
1274
|
+
variant: "primary",
|
|
1275
|
+
disabled: busy,
|
|
1276
|
+
onClick: () => onDecision(approval, "deny"),
|
|
1277
|
+
children: "Deny"
|
|
1278
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1279
|
+
variant: "danger",
|
|
1280
|
+
disabled: busy,
|
|
1281
|
+
onClick: () => onDecision(approval, "allow"),
|
|
1282
|
+
children: "Allow once"
|
|
1283
|
+
})]
|
|
1284
|
+
})
|
|
1285
|
+
] }) : /* @__PURE__ */ jsx("p", {
|
|
1286
|
+
role: "status",
|
|
1287
|
+
className: "mt-3 text-[0.84em] font-medium text-mocha",
|
|
1288
|
+
children: commandApprovalStatusCopy(approval.status)
|
|
1289
|
+
})
|
|
1290
|
+
]
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
function commandApprovalStatusCopy(status) {
|
|
1294
|
+
switch (status) {
|
|
1295
|
+
case "pending": return "Waiting for a decision.";
|
|
1296
|
+
case "allowed": return "Allowed once — the command may run for this tool call only.";
|
|
1297
|
+
case "denied": return "Denied — the command was not run.";
|
|
1298
|
+
case "auto_denied": return "Automatically denied — no interactive approval path was available.";
|
|
1299
|
+
case "expired": return "Expired — the command was not run.";
|
|
1300
|
+
case "cancelled": return "Cancelled — the command was not run.";
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function commandApprovalStatusFromConflict(message) {
|
|
1304
|
+
if (!message.startsWith("shell approval already decided: ")) return null;
|
|
1305
|
+
const status = message.slice(32);
|
|
1306
|
+
switch (status) {
|
|
1307
|
+
case "allowed":
|
|
1308
|
+
case "denied":
|
|
1309
|
+
case "auto_denied":
|
|
1310
|
+
case "expired":
|
|
1311
|
+
case "cancelled": return status;
|
|
1312
|
+
default: return null;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1026
1315
|
function ToolGroup({ items, dropCls }) {
|
|
1027
1316
|
const [expanded, setExpanded] = useState(false);
|
|
1028
1317
|
const contentRef = useRef(null);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as Route } from "./_id-
|
|
1
|
+
import { t as Route } from "./_id-FcHdSocl.js";
|
|
2
2
|
import { r as PageShell } from "./Page-LENulSXY.js";
|
|
3
|
-
import { t as AgentTabs } from "./AgentTabs-
|
|
3
|
+
import { t as AgentTabs } from "./AgentTabs-aXdsiiMp.js";
|
|
4
4
|
import { t as AgentAvatar } from "./AgentAvatar-8gTAy_tP.js";
|
|
5
|
-
import { t as ChatPane } from "./ChatPane-
|
|
5
|
+
import { t as ChatPane } from "./ChatPane-Ba5wTsVk.js";
|
|
6
6
|
import { r as REASONING_LEVELS } from "./wire-constants-C8nBJcU3.js";
|
|
7
7
|
import { useState } from "react";
|
|
8
8
|
import { useRouter } from "@tanstack/react-router";
|
|
@@ -2,7 +2,7 @@ import { t as createServerFn } from "../server.js";
|
|
|
2
2
|
import { t as createSsrRpc } from "./createSsrRpc-DFsIAfFd.js";
|
|
3
3
|
import { createFileRoute, lazyRouteComponent, redirect } from "@tanstack/react-router";
|
|
4
4
|
//#region src/routes/agents/$id/index.tsx
|
|
5
|
-
var $$splitComponentImporter = () => import("./_id-
|
|
5
|
+
var $$splitComponentImporter = () => import("./_id-BHYLL8M-.js");
|
|
6
6
|
var fetchAgent = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(createSsrRpc("846c9081aed17057351730985f133b0aab67a5dabfca9562552829a90ff30849"));
|
|
7
7
|
var Route = createFileRoute("/agents/$id/")({
|
|
8
8
|
validateSearch: (search) => ({
|