@invergent/agent-chat-react 1.5.9 → 1.6.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/README.md +13 -0
- package/dist/index.cjs +761 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -3
- package/dist/index.d.ts +81 -3
- package/dist/index.js +757 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { cn } from './chunk-QSC4UIVT.js';
|
|
2
2
|
import { createContext, memo, useRef, useState, useEffect, useCallback, useMemo, lazy, useContext, Suspense, Children } from 'react';
|
|
3
|
+
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, GlobeIcon, SearchIcon, ListTodoIcon, XCircleIcon, CheckCircle2Icon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircleIcon, CircleIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, Maximize2Icon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, PlusIcon, CornerDownLeftIcon } from 'lucide-react';
|
|
3
4
|
import { cva } from 'class-variance-authority';
|
|
4
5
|
import { Collapsible as Collapsible$1, Slot, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Tooltip as Tooltip$1, Tabs as Tabs$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1, HoverCard as HoverCard$1, Progress as Progress$1 } from 'radix-ui';
|
|
5
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
|
-
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, GlobeIcon, SearchIcon, ListTodoIcon, XCircleIcon, CheckCircle2Icon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircleIcon, CircleIcon, UsersIcon, MessageSquareIcon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, SquareIcon, ArrowDownIcon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, Maximize2Icon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, PlusIcon, CornerDownLeftIcon } from 'lucide-react';
|
|
7
7
|
import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
|
|
8
8
|
import { cjk } from '@streamdown/cjk';
|
|
9
9
|
import { code } from '@streamdown/code';
|
|
@@ -79,6 +79,114 @@ function Button({
|
|
|
79
79
|
}
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
|
+
function BrowserControlBar({
|
|
83
|
+
sessionId,
|
|
84
|
+
hasControl,
|
|
85
|
+
adapter
|
|
86
|
+
}) {
|
|
87
|
+
const [pending, setPending] = useState(false);
|
|
88
|
+
const [error, setError] = useState(null);
|
|
89
|
+
async function toggleControl() {
|
|
90
|
+
if (!sessionId || pending) return;
|
|
91
|
+
setPending(true);
|
|
92
|
+
setError(null);
|
|
93
|
+
try {
|
|
94
|
+
if (hasControl) {
|
|
95
|
+
await adapter.releaseBrowserControl(sessionId);
|
|
96
|
+
} else {
|
|
97
|
+
await adapter.acquireBrowserControl(sessionId);
|
|
98
|
+
}
|
|
99
|
+
} catch (nextError) {
|
|
100
|
+
setError(nextError.message);
|
|
101
|
+
} finally {
|
|
102
|
+
setPending(false);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-11 items-center gap-2 border-t border-line bg-card px-3 py-2", children: [
|
|
106
|
+
/* @__PURE__ */ jsxs(
|
|
107
|
+
Button,
|
|
108
|
+
{
|
|
109
|
+
type: "button",
|
|
110
|
+
size: "xs",
|
|
111
|
+
variant: hasControl ? "secondary" : "default",
|
|
112
|
+
disabled: !sessionId || pending,
|
|
113
|
+
onClick: () => void toggleControl(),
|
|
114
|
+
children: [
|
|
115
|
+
hasControl ? /* @__PURE__ */ jsx(RotateCcwIcon, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(MousePointer2Icon, { className: "size-3", "aria-hidden": "true" }),
|
|
116
|
+
hasControl ? "Return control" : "Take control"
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-auto flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
121
|
+
/* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-red-500", "aria-hidden": "true" }),
|
|
122
|
+
"Live"
|
|
123
|
+
] }),
|
|
124
|
+
error && /* @__PURE__ */ jsx("span", { className: "max-w-40 truncate text-[11px] text-destructive", children: error })
|
|
125
|
+
] });
|
|
126
|
+
}
|
|
127
|
+
function BrowserLiveView({ src }) {
|
|
128
|
+
return /* @__PURE__ */ jsx(
|
|
129
|
+
"iframe",
|
|
130
|
+
{
|
|
131
|
+
"data-testid": "browser-iframe",
|
|
132
|
+
title: "Browser live view",
|
|
133
|
+
src,
|
|
134
|
+
className: "h-full w-full border-0 bg-black",
|
|
135
|
+
allow: "clipboard-read; clipboard-write"
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function BrowserStatusDot({
|
|
140
|
+
status
|
|
141
|
+
}) {
|
|
142
|
+
return /* @__PURE__ */ jsx(
|
|
143
|
+
"span",
|
|
144
|
+
{
|
|
145
|
+
"aria-hidden": "true",
|
|
146
|
+
className: cn(
|
|
147
|
+
"inline-block size-2 shrink-0 rounded-full",
|
|
148
|
+
status === "live" && "bg-emerald-500",
|
|
149
|
+
status === "user-control" && "bg-amber-500",
|
|
150
|
+
status === "provisioning" && "animate-pulse bg-sky-500",
|
|
151
|
+
status === "closed" && "bg-muted-foreground"
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
function BrowserPane({ sessionId, state, adapter }) {
|
|
157
|
+
const liveViewUrl = useMemo(
|
|
158
|
+
() => adapter.browserLiveViewUrl(sessionId),
|
|
159
|
+
[adapter, sessionId]
|
|
160
|
+
);
|
|
161
|
+
const hasLiveView = state.status !== "provisioning" && state.status !== "closed";
|
|
162
|
+
return /* @__PURE__ */ jsxs(
|
|
163
|
+
"div",
|
|
164
|
+
{
|
|
165
|
+
"data-testid": "browser-pane",
|
|
166
|
+
className: "flex h-full min-h-0 flex-col bg-background",
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ jsxs("header", { className: "flex min-h-10 items-center gap-2 border-b border-line bg-card px-3 text-xs text-foreground", children: [
|
|
169
|
+
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
|
|
170
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Browser" }),
|
|
171
|
+
/* @__PURE__ */ jsx(BrowserStatusDot, { status: state.status }),
|
|
172
|
+
state.controlOwner && /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-amber-500", children: [
|
|
173
|
+
state.controlOwner,
|
|
174
|
+
" has control"
|
|
175
|
+
] })
|
|
176
|
+
] }),
|
|
177
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : /* @__PURE__ */ jsx(BrowserLiveView, { src: liveViewUrl }) }),
|
|
178
|
+
hasLiveView && /* @__PURE__ */ jsx(
|
|
179
|
+
BrowserControlBar,
|
|
180
|
+
{
|
|
181
|
+
sessionId,
|
|
182
|
+
hasControl: state.status === "user-control",
|
|
183
|
+
adapter
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
82
190
|
var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsx(
|
|
83
191
|
StickToBottom,
|
|
84
192
|
{
|
|
@@ -565,6 +673,80 @@ function TimelineSeparator({
|
|
|
565
673
|
}
|
|
566
674
|
);
|
|
567
675
|
}
|
|
676
|
+
function BrowserActivityGroup({ calls }) {
|
|
677
|
+
const [open, setOpen] = useState(false);
|
|
678
|
+
const latest = calls[calls.length - 1];
|
|
679
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-none border border-line bg-card text-xs", children: [
|
|
680
|
+
/* @__PURE__ */ jsxs(
|
|
681
|
+
"button",
|
|
682
|
+
{
|
|
683
|
+
type: "button",
|
|
684
|
+
onClick: () => setOpen((current) => !current),
|
|
685
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted/40",
|
|
686
|
+
children: [
|
|
687
|
+
open ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
|
|
688
|
+
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
689
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "browser" }),
|
|
690
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
|
|
691
|
+
"(",
|
|
692
|
+
calls.length,
|
|
693
|
+
" ",
|
|
694
|
+
calls.length === 1 ? "action" : "actions",
|
|
695
|
+
" - latest:",
|
|
696
|
+
" ",
|
|
697
|
+
latest ? summarise(latest) : "none",
|
|
698
|
+
")"
|
|
699
|
+
] })
|
|
700
|
+
]
|
|
701
|
+
}
|
|
702
|
+
),
|
|
703
|
+
open && /* @__PURE__ */ jsx("ul", { className: "border-t border-line py-1", children: calls.map((call) => {
|
|
704
|
+
const error = stringValue(parseJson(call.result).error);
|
|
705
|
+
return /* @__PURE__ */ jsxs(
|
|
706
|
+
"li",
|
|
707
|
+
{
|
|
708
|
+
className: "flex items-center gap-2 px-6 py-1 font-mono text-[11px]",
|
|
709
|
+
children: [
|
|
710
|
+
error && /* @__PURE__ */ jsx(
|
|
711
|
+
AlertCircleIcon,
|
|
712
|
+
{
|
|
713
|
+
"data-testid": `activity-error-${call.id}`,
|
|
714
|
+
className: "size-3 shrink-0 text-destructive",
|
|
715
|
+
"aria-hidden": "true"
|
|
716
|
+
}
|
|
717
|
+
),
|
|
718
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: summarise(call) }),
|
|
719
|
+
error && /* @__PURE__ */ jsx("span", { className: "ml-auto max-w-40 truncate text-destructive", children: error })
|
|
720
|
+
]
|
|
721
|
+
},
|
|
722
|
+
call.id
|
|
723
|
+
);
|
|
724
|
+
}) })
|
|
725
|
+
] });
|
|
726
|
+
}
|
|
727
|
+
function summarise(call) {
|
|
728
|
+
const verb = call.toolName.replace(/^browser_/, "");
|
|
729
|
+
const args = parseJson(call.args);
|
|
730
|
+
const ref = stringValue(args.ref);
|
|
731
|
+
const url = stringValue(args.url);
|
|
732
|
+
const text = stringValue(args.text);
|
|
733
|
+
if (url) return `${verb} ${url}`;
|
|
734
|
+
if (ref) return `${verb} ${ref}`;
|
|
735
|
+
if (text) return `${verb} "${text.slice(0, 24)}"`;
|
|
736
|
+
return verb;
|
|
737
|
+
}
|
|
738
|
+
function parseJson(raw) {
|
|
739
|
+
if (!raw) return {};
|
|
740
|
+
try {
|
|
741
|
+
const parsed = JSON.parse(raw);
|
|
742
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
743
|
+
} catch {
|
|
744
|
+
return {};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function stringValue(value) {
|
|
748
|
+
return typeof value === "string" ? value : "";
|
|
749
|
+
}
|
|
568
750
|
|
|
569
751
|
// src/components/chat/tools/shared.ts
|
|
570
752
|
function statusColorClass(status) {
|
|
@@ -3304,13 +3486,16 @@ function ClarifyLocked({
|
|
|
3304
3486
|
}) })
|
|
3305
3487
|
] });
|
|
3306
3488
|
}
|
|
3307
|
-
function ArtifactToolBlock({
|
|
3308
|
-
|
|
3489
|
+
function ArtifactToolBlock({
|
|
3490
|
+
tc,
|
|
3491
|
+
resolvedName
|
|
3492
|
+
}) {
|
|
3309
3493
|
const status = effectiveStatus(tc);
|
|
3310
3494
|
const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Creating artifact\u2026" : "Created";
|
|
3495
|
+
const name = resolvedName ?? parseArgs(tc.args)?.name;
|
|
3311
3496
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
|
|
3312
3497
|
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: label }),
|
|
3313
|
-
|
|
3498
|
+
name && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate", children: name })
|
|
3314
3499
|
] });
|
|
3315
3500
|
}
|
|
3316
3501
|
function firstLine(s) {
|
|
@@ -3636,7 +3821,11 @@ function DefaultToolBlock({ tc }) {
|
|
|
3636
3821
|
] })
|
|
3637
3822
|
] });
|
|
3638
3823
|
}
|
|
3639
|
-
function ToolCallBlock({
|
|
3824
|
+
function ToolCallBlock({
|
|
3825
|
+
tc,
|
|
3826
|
+
resolvedArtifactName,
|
|
3827
|
+
onFileSelect
|
|
3828
|
+
}) {
|
|
3640
3829
|
switch (tc.toolName) {
|
|
3641
3830
|
case "terminal":
|
|
3642
3831
|
return /* @__PURE__ */ jsx(TerminalToolBlock, { tc });
|
|
@@ -3673,7 +3862,7 @@ function ToolCallBlock({ tc, onFileSelect }) {
|
|
|
3673
3862
|
case "clarify":
|
|
3674
3863
|
return /* @__PURE__ */ jsx(ClarifyToolBlock, { tc });
|
|
3675
3864
|
case "create_artifact":
|
|
3676
|
-
return /* @__PURE__ */ jsx(ArtifactToolBlock, { tc });
|
|
3865
|
+
return /* @__PURE__ */ jsx(ArtifactToolBlock, { tc, resolvedName: resolvedArtifactName });
|
|
3677
3866
|
case "delegate_task":
|
|
3678
3867
|
return /* @__PURE__ */ jsx(DelegateToolBlock, { tc });
|
|
3679
3868
|
case "memory":
|
|
@@ -5055,6 +5244,7 @@ function ChatComposerInner({
|
|
|
5055
5244
|
onStop,
|
|
5056
5245
|
isRunning,
|
|
5057
5246
|
disabled = false,
|
|
5247
|
+
disabledReason,
|
|
5058
5248
|
tokenUsage
|
|
5059
5249
|
}) {
|
|
5060
5250
|
const { adapter } = useAgentChatAdapterContext();
|
|
@@ -5083,7 +5273,10 @@ function ChatComposerInner({
|
|
|
5083
5273
|
const slashCommands = useMemo(() => {
|
|
5084
5274
|
const builtin = [
|
|
5085
5275
|
{ value: "/clear", label: "/clear", description: "Clear conversation" },
|
|
5086
|
-
{ value: "/compress", label: "/compress", description: "Compress context" }
|
|
5276
|
+
{ value: "/compress", label: "/compress", description: "Compress context" },
|
|
5277
|
+
{ value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
|
|
5278
|
+
{ value: "/loop list", label: "/loop list", description: "List active loops" },
|
|
5279
|
+
{ value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
|
|
5087
5280
|
];
|
|
5088
5281
|
return [...adapterCommands, ...builtin];
|
|
5089
5282
|
}, [adapterCommands]);
|
|
@@ -5162,7 +5355,7 @@ function ChatComposerInner({
|
|
|
5162
5355
|
/* @__PURE__ */ jsx(PromptInputBody, { children: /* @__PURE__ */ jsx(
|
|
5163
5356
|
PromptInputTextarea,
|
|
5164
5357
|
{
|
|
5165
|
-
placeholder: disabled ? "Session disabled" : "Send a message...",
|
|
5358
|
+
placeholder: disabled ? disabledReason ?? "Session disabled" : "Send a message...",
|
|
5166
5359
|
disabled,
|
|
5167
5360
|
onKeyDown: handleKeyDown
|
|
5168
5361
|
}
|
|
@@ -5807,7 +6000,7 @@ var ErrorMessage = memo(function ErrorMessage2({
|
|
|
5807
6000
|
}
|
|
5808
6001
|
);
|
|
5809
6002
|
});
|
|
5810
|
-
function messageToEntries(msg, isLast) {
|
|
6003
|
+
function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
5811
6004
|
if (msg.role === "system") {
|
|
5812
6005
|
if (msg.systemKind === "skill_invoked") {
|
|
5813
6006
|
return [{
|
|
@@ -5849,6 +6042,18 @@ function messageToEntries(msg, isLast) {
|
|
|
5849
6042
|
}
|
|
5850
6043
|
if (hasToolCalls) {
|
|
5851
6044
|
for (const tc of msg.toolCalls) {
|
|
6045
|
+
if (tc.toolName === "create_artifact") {
|
|
6046
|
+
const resolvedName = parseArgs(tc.args)?.name ?? artifactFallbacks[tc.id];
|
|
6047
|
+
const status = effectiveStatus(tc);
|
|
6048
|
+
if (!resolvedName && status !== "error") continue;
|
|
6049
|
+
entries.push({
|
|
6050
|
+
kind: "tool",
|
|
6051
|
+
key: tc.id,
|
|
6052
|
+
tc,
|
|
6053
|
+
resolvedArtifactName: resolvedName
|
|
6054
|
+
});
|
|
6055
|
+
continue;
|
|
6056
|
+
}
|
|
5852
6057
|
entries.push({ kind: "tool", key: tc.id, tc });
|
|
5853
6058
|
}
|
|
5854
6059
|
}
|
|
@@ -5863,6 +6068,32 @@ function messageToEntries(msg, isLast) {
|
|
|
5863
6068
|
}
|
|
5864
6069
|
return entries;
|
|
5865
6070
|
}
|
|
6071
|
+
function groupBrowserActivityEntries(entries) {
|
|
6072
|
+
const grouped = [];
|
|
6073
|
+
let buffer = [];
|
|
6074
|
+
const flush = () => {
|
|
6075
|
+
if (buffer.length === 0) return;
|
|
6076
|
+
grouped.push({
|
|
6077
|
+
kind: "browser_activity",
|
|
6078
|
+
key: `browser-activity-${buffer[0]?.id}-${buffer[buffer.length - 1]?.id}`,
|
|
6079
|
+
calls: buffer
|
|
6080
|
+
});
|
|
6081
|
+
buffer = [];
|
|
6082
|
+
};
|
|
6083
|
+
for (const entry of entries) {
|
|
6084
|
+
if (entry.kind === "tool" && isBrowserToolCall(entry.tc)) {
|
|
6085
|
+
buffer.push(entry.tc);
|
|
6086
|
+
continue;
|
|
6087
|
+
}
|
|
6088
|
+
flush();
|
|
6089
|
+
grouped.push(entry);
|
|
6090
|
+
}
|
|
6091
|
+
flush();
|
|
6092
|
+
return grouped;
|
|
6093
|
+
}
|
|
6094
|
+
function isBrowserToolCall(call) {
|
|
6095
|
+
return call.toolName.startsWith("browser_");
|
|
6096
|
+
}
|
|
5866
6097
|
function groupMessages(messages) {
|
|
5867
6098
|
const groups = [];
|
|
5868
6099
|
let pendingSystem = [];
|
|
@@ -6007,11 +6238,27 @@ function TimelineEntryItem({
|
|
|
6007
6238
|
)
|
|
6008
6239
|
] }),
|
|
6009
6240
|
/* @__PURE__ */ jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
6010
|
-
/* @__PURE__ */ jsx(
|
|
6241
|
+
/* @__PURE__ */ jsx(
|
|
6242
|
+
ToolCallBlock,
|
|
6243
|
+
{
|
|
6244
|
+
tc: entry.tc,
|
|
6245
|
+
resolvedArtifactName: entry.resolvedArtifactName,
|
|
6246
|
+
onFileSelect
|
|
6247
|
+
}
|
|
6248
|
+
),
|
|
6011
6249
|
failureSummary ? /* @__PURE__ */ jsx("div", { className: "max-w-full truncate text-xs text-destructive", title: failureSummary, children: failureSummary }) : null
|
|
6012
6250
|
] }) })
|
|
6013
6251
|
] });
|
|
6014
6252
|
}
|
|
6253
|
+
if (entry.kind === "browser_activity") {
|
|
6254
|
+
return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
|
|
6255
|
+
/* @__PURE__ */ jsxs(TimelineHeader, { children: [
|
|
6256
|
+
/* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
|
|
6257
|
+
/* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2 border-none bg-amber-500" })
|
|
6258
|
+
] }),
|
|
6259
|
+
/* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(BrowserActivityGroup, { calls: entry.calls }) })
|
|
6260
|
+
] });
|
|
6261
|
+
}
|
|
6015
6262
|
if (entry.kind === "text") {
|
|
6016
6263
|
return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
|
|
6017
6264
|
/* @__PURE__ */ jsxs(TimelineHeader, { children: [
|
|
@@ -6069,14 +6316,16 @@ function AssistantGroup({
|
|
|
6069
6316
|
totalMessages,
|
|
6070
6317
|
isRunning,
|
|
6071
6318
|
sessionId,
|
|
6319
|
+
artifactFallbacks,
|
|
6072
6320
|
onFileSelect,
|
|
6073
6321
|
onRetry
|
|
6074
6322
|
}) {
|
|
6075
|
-
|
|
6323
|
+
let entries = [];
|
|
6076
6324
|
for (let i = 0; i < messages.length; i++) {
|
|
6077
6325
|
const isLast = i === messages.length - 1 && lastGlobalIndex === totalMessages - 1;
|
|
6078
|
-
entries.push(...messageToEntries(messages[i], isLast));
|
|
6326
|
+
entries.push(...messageToEntries(messages[i], isLast, artifactFallbacks));
|
|
6079
6327
|
}
|
|
6328
|
+
entries = groupBrowserActivityEntries(entries);
|
|
6080
6329
|
const isTailGroup = lastGlobalIndex === totalMessages - 1;
|
|
6081
6330
|
const lastEntry = entries[entries.length - 1];
|
|
6082
6331
|
const hasRunningTool = entries.some(
|
|
@@ -6142,11 +6391,34 @@ function ChatThread({
|
|
|
6142
6391
|
onStop,
|
|
6143
6392
|
onFileSelect,
|
|
6144
6393
|
disabled = false,
|
|
6394
|
+
disabledReason,
|
|
6145
6395
|
tokenUsage,
|
|
6146
6396
|
retryIndicator,
|
|
6147
6397
|
onRetry
|
|
6148
6398
|
}) {
|
|
6149
6399
|
const groups = useMemo(() => groupMessages(messages), [messages]);
|
|
6400
|
+
const artifactFallbacks = useMemo(() => {
|
|
6401
|
+
const fallbacks = {};
|
|
6402
|
+
const createArtifactToolCallIds = [];
|
|
6403
|
+
let artifactIdx = 0;
|
|
6404
|
+
for (const msg of messages) {
|
|
6405
|
+
if (msg.role === "assistant" && msg.toolCalls) {
|
|
6406
|
+
for (const tc of msg.toolCalls) {
|
|
6407
|
+
if (tc.toolName === "create_artifact" && !tc.cancelled && tc.status !== "error") {
|
|
6408
|
+
createArtifactToolCallIds.push(tc.id);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
} else if (msg.role === "system" && msg.systemKind === "artifact") {
|
|
6412
|
+
const tcId = createArtifactToolCallIds[artifactIdx];
|
|
6413
|
+
if (tcId) {
|
|
6414
|
+
const { name } = unpackArtifactMeta(msg.systemMeta, msg.content);
|
|
6415
|
+
if (name) fallbacks[tcId] = name;
|
|
6416
|
+
}
|
|
6417
|
+
artifactIdx += 1;
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6420
|
+
return fallbacks;
|
|
6421
|
+
}, [messages]);
|
|
6150
6422
|
const activeFailureId = useMemo(() => {
|
|
6151
6423
|
if (messages.length === 0) return null;
|
|
6152
6424
|
const tail = messages[messages.length - 1];
|
|
@@ -6211,6 +6483,7 @@ function ChatThread({
|
|
|
6211
6483
|
totalMessages: messages.length,
|
|
6212
6484
|
isRunning,
|
|
6213
6485
|
sessionId,
|
|
6486
|
+
artifactFallbacks,
|
|
6214
6487
|
onFileSelect,
|
|
6215
6488
|
onRetry: groupRetry
|
|
6216
6489
|
},
|
|
@@ -6223,13 +6496,21 @@ function ChatThread({
|
|
|
6223
6496
|
] }),
|
|
6224
6497
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-4xl px-6 pb-5 pt-3", children: [
|
|
6225
6498
|
retryIndicator && /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsx(RetryBanner, { indicator: retryIndicator }) }),
|
|
6226
|
-
/* @__PURE__ */ jsx(
|
|
6499
|
+
disabled && disabledReason ? /* @__PURE__ */ jsx(
|
|
6500
|
+
"div",
|
|
6501
|
+
{
|
|
6502
|
+
className: "rounded border border-line bg-muted/40 px-3 py-2 text-sm text-muted-foreground",
|
|
6503
|
+
role: "status",
|
|
6504
|
+
children: disabledReason
|
|
6505
|
+
}
|
|
6506
|
+
) : /* @__PURE__ */ jsx(
|
|
6227
6507
|
ChatComposer,
|
|
6228
6508
|
{
|
|
6229
6509
|
onSend,
|
|
6230
6510
|
onStop,
|
|
6231
6511
|
isRunning,
|
|
6232
6512
|
disabled,
|
|
6513
|
+
disabledReason,
|
|
6233
6514
|
tokenUsage
|
|
6234
6515
|
}
|
|
6235
6516
|
)
|
|
@@ -7058,7 +7339,7 @@ function WorkspacePanel({
|
|
|
7058
7339
|
);
|
|
7059
7340
|
const handleDelete = useCallback(
|
|
7060
7341
|
async (path) => {
|
|
7061
|
-
if (!sessionId) return;
|
|
7342
|
+
if (disabled || !sessionId) return;
|
|
7062
7343
|
try {
|
|
7063
7344
|
await adapter.deleteWorkspaceFile({ sessionId, path });
|
|
7064
7345
|
if (selectedPath === path) {
|
|
@@ -7074,7 +7355,7 @@ function WorkspacePanel({
|
|
|
7074
7355
|
setDeleteTarget(null);
|
|
7075
7356
|
}
|
|
7076
7357
|
},
|
|
7077
|
-
[adapter, fetchTree, onSelectedPathChange, selectedPath, sessionId]
|
|
7358
|
+
[adapter, disabled, fetchTree, onSelectedPathChange, selectedPath, sessionId]
|
|
7078
7359
|
);
|
|
7079
7360
|
const onResizeStart = useCallback(
|
|
7080
7361
|
(event) => {
|
|
@@ -7112,6 +7393,7 @@ function WorkspacePanel({
|
|
|
7112
7393
|
return /* @__PURE__ */ jsx(
|
|
7113
7394
|
"aside",
|
|
7114
7395
|
{
|
|
7396
|
+
"data-testid": "workspace-panel",
|
|
7115
7397
|
role: "button",
|
|
7116
7398
|
tabIndex: 0,
|
|
7117
7399
|
className: "relative z-10 flex min-h-0 w-10 shrink-0 cursor-pointer items-center justify-center border-l border-muted-foreground/20 bg-card text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30",
|
|
@@ -7130,6 +7412,7 @@ function WorkspacePanel({
|
|
|
7130
7412
|
return /* @__PURE__ */ jsxs(
|
|
7131
7413
|
"aside",
|
|
7132
7414
|
{
|
|
7415
|
+
"data-testid": "workspace-panel",
|
|
7133
7416
|
className: "relative z-10 flex min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
|
|
7134
7417
|
style: { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
7135
7418
|
children: [
|
|
@@ -7157,7 +7440,7 @@ function WorkspacePanel({
|
|
|
7157
7440
|
/* @__PURE__ */ jsx(FolderOpenIcon, { className: "size-4 shrink-0 text-amber-500" }),
|
|
7158
7441
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7159
7442
|
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
|
|
7160
|
-
/* @__PURE__ */ jsx("div", { className: "truncate text-xs text-faint", children: "Workspace" })
|
|
7443
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-xs text-faint", children: disabled ? "Workspace - Read-only" : "Workspace" })
|
|
7161
7444
|
] }),
|
|
7162
7445
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
7163
7446
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
@@ -7268,7 +7551,7 @@ function WorkspacePanel({
|
|
|
7268
7551
|
sessionId,
|
|
7269
7552
|
path: file.path
|
|
7270
7553
|
}) : null,
|
|
7271
|
-
onDelete: file ? () => setDeleteTarget(file.path) : null,
|
|
7554
|
+
onDelete: file && !disabled ? () => setDeleteTarget(file.path) : null,
|
|
7272
7555
|
onClose: () => {
|
|
7273
7556
|
onSelectedPathChange(null);
|
|
7274
7557
|
setFile(null);
|
|
@@ -7332,6 +7615,13 @@ function ConfirmDialog({
|
|
|
7332
7615
|
);
|
|
7333
7616
|
}
|
|
7334
7617
|
|
|
7618
|
+
// src/lib/sessions.ts
|
|
7619
|
+
function isScheduledRunSession(session) {
|
|
7620
|
+
return Boolean(
|
|
7621
|
+
session?.channel === "scheduled" || session?.config?.scheduled_session_id
|
|
7622
|
+
);
|
|
7623
|
+
}
|
|
7624
|
+
|
|
7335
7625
|
// src/runtime/events.ts
|
|
7336
7626
|
var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
7337
7627
|
"terminal",
|
|
@@ -7364,6 +7654,10 @@ var AGENT_CHAT_LISTENED_EVENTS = [
|
|
|
7364
7654
|
"expert.override",
|
|
7365
7655
|
"artifact.created",
|
|
7366
7656
|
"artifact.updated",
|
|
7657
|
+
"browser.provisioned",
|
|
7658
|
+
"browser.destroyed",
|
|
7659
|
+
"browser.control_granted",
|
|
7660
|
+
"browser.control_returned",
|
|
7367
7661
|
"clarify.response"
|
|
7368
7662
|
];
|
|
7369
7663
|
|
|
@@ -7388,7 +7682,8 @@ function createInitialAgentChatState(options = {}) {
|
|
|
7388
7682
|
sessionDone: false,
|
|
7389
7683
|
hadDeltas: false,
|
|
7390
7684
|
terminal: false,
|
|
7391
|
-
workspaceRefreshKey: 0
|
|
7685
|
+
workspaceRefreshKey: 0,
|
|
7686
|
+
browser: null
|
|
7392
7687
|
};
|
|
7393
7688
|
}
|
|
7394
7689
|
function applyAgentChatEvent(state, event) {
|
|
@@ -7412,7 +7707,7 @@ function applyAgentChatEvent(state, event) {
|
|
|
7412
7707
|
{
|
|
7413
7708
|
id: `evt-${event.eventId}`,
|
|
7414
7709
|
role: "system",
|
|
7415
|
-
content:
|
|
7710
|
+
content: stringValue2(event.data.skill),
|
|
7416
7711
|
createdAt: /* @__PURE__ */ new Date(),
|
|
7417
7712
|
status: "complete",
|
|
7418
7713
|
systemKind: "skill_invoked",
|
|
@@ -7429,7 +7724,7 @@ function applyAgentChatEvent(state, event) {
|
|
|
7429
7724
|
{
|
|
7430
7725
|
id: `evt-${event.eventId}`,
|
|
7431
7726
|
role: "system",
|
|
7432
|
-
content:
|
|
7727
|
+
content: stringValue2(event.data.name),
|
|
7433
7728
|
createdAt: /* @__PURE__ */ new Date(),
|
|
7434
7729
|
status: "complete",
|
|
7435
7730
|
systemKind: "artifact",
|
|
@@ -7442,6 +7737,23 @@ function applyAgentChatEvent(state, event) {
|
|
|
7442
7737
|
}
|
|
7443
7738
|
}
|
|
7444
7739
|
]);
|
|
7740
|
+
case "browser.provisioned":
|
|
7741
|
+
return applyBrowserEvent(nextState, event, {
|
|
7742
|
+
status: "live",
|
|
7743
|
+
controlOwner: null
|
|
7744
|
+
});
|
|
7745
|
+
case "browser.control_granted":
|
|
7746
|
+
return applyBrowserEvent(nextState, event, {
|
|
7747
|
+
status: "user-control",
|
|
7748
|
+
controlOwner: stringValue2(event.data.owner_user_id) || null
|
|
7749
|
+
});
|
|
7750
|
+
case "browser.control_returned":
|
|
7751
|
+
return applyBrowserEvent(nextState, event, {
|
|
7752
|
+
status: "live",
|
|
7753
|
+
controlOwner: null
|
|
7754
|
+
});
|
|
7755
|
+
case "browser.destroyed":
|
|
7756
|
+
return applyBrowserEvent(nextState, event, null);
|
|
7445
7757
|
case "llm.delta":
|
|
7446
7758
|
return applyLlmDelta(nextState, event);
|
|
7447
7759
|
case "llm.response":
|
|
@@ -7451,7 +7763,7 @@ function applyAgentChatEvent(state, event) {
|
|
|
7451
7763
|
case "tool.call":
|
|
7452
7764
|
return applyToolCall(nextState, event);
|
|
7453
7765
|
case "tool.result": {
|
|
7454
|
-
const toolCallId =
|
|
7766
|
+
const toolCallId = stringValue2(event.data.tool_call_id);
|
|
7455
7767
|
const toolName = findToolNameById(nextState.messages, toolCallId);
|
|
7456
7768
|
const messages = applyToolResult(nextState.messages, event.data);
|
|
7457
7769
|
const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
|
|
@@ -7521,8 +7833,8 @@ function applyRetryIndicator(state, event) {
|
|
|
7521
7833
|
case "session.done":
|
|
7522
7834
|
return { ...state, retryIndicator: null };
|
|
7523
7835
|
case "harness.crash": {
|
|
7524
|
-
const title =
|
|
7525
|
-
const detail =
|
|
7836
|
+
const title = stringValue2(event.data.error_title) || "A transient error occurred, retrying...";
|
|
7837
|
+
const detail = stringValue2(event.data.error_detail) || stringValue2(event.data.error);
|
|
7526
7838
|
return {
|
|
7527
7839
|
...state,
|
|
7528
7840
|
retryIndicator: {
|
|
@@ -7537,7 +7849,7 @@ function applyRetryIndicator(state, event) {
|
|
|
7537
7849
|
}
|
|
7538
7850
|
}
|
|
7539
7851
|
function applyUserMessage(messages, eventId, data) {
|
|
7540
|
-
const content =
|
|
7852
|
+
const content = stringValue2(data.content);
|
|
7541
7853
|
const next = [...messages];
|
|
7542
7854
|
const localIdx = next.findIndex(
|
|
7543
7855
|
(m) => m.role === "user" && m.id.startsWith("local-") && m.content === content
|
|
@@ -7555,9 +7867,47 @@ function applyUserMessage(messages, eventId, data) {
|
|
|
7555
7867
|
});
|
|
7556
7868
|
return next;
|
|
7557
7869
|
}
|
|
7870
|
+
function applyBrowserEvent(state, event, browser) {
|
|
7871
|
+
return withMessages(
|
|
7872
|
+
{ ...state, browser },
|
|
7873
|
+
[...state.messages, browserMarker(event)]
|
|
7874
|
+
);
|
|
7875
|
+
}
|
|
7876
|
+
function browserMarker(event) {
|
|
7877
|
+
const labels = {
|
|
7878
|
+
"browser.provisioned": {
|
|
7879
|
+
content: "Browser ready.",
|
|
7880
|
+
warning: false
|
|
7881
|
+
},
|
|
7882
|
+
"browser.control_granted": {
|
|
7883
|
+
content: "A user took control of the browser.",
|
|
7884
|
+
warning: true
|
|
7885
|
+
},
|
|
7886
|
+
"browser.control_returned": {
|
|
7887
|
+
content: "Browser control returned to the agent.",
|
|
7888
|
+
warning: false
|
|
7889
|
+
},
|
|
7890
|
+
"browser.destroyed": {
|
|
7891
|
+
content: "Browser closed.",
|
|
7892
|
+
warning: false
|
|
7893
|
+
}
|
|
7894
|
+
};
|
|
7895
|
+
const label = labels[event.type] ?? {
|
|
7896
|
+
content: "Browser updated.",
|
|
7897
|
+
warning: false
|
|
7898
|
+
};
|
|
7899
|
+
return {
|
|
7900
|
+
id: `browser-marker-${event.eventId}`,
|
|
7901
|
+
role: "system",
|
|
7902
|
+
content: label.content,
|
|
7903
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
7904
|
+
status: "complete",
|
|
7905
|
+
systemKind: label.warning ? "browser_marker_warning" : "browser_marker"
|
|
7906
|
+
};
|
|
7907
|
+
}
|
|
7558
7908
|
function applyLlmDelta(state, event) {
|
|
7559
|
-
const deltaContent =
|
|
7560
|
-
const deltaReasoning =
|
|
7909
|
+
const deltaContent = stringValue2(event.data.content);
|
|
7910
|
+
const deltaReasoning = stringValue2(event.data.reasoning);
|
|
7561
7911
|
const messages = [...state.messages];
|
|
7562
7912
|
const lastIdx = findLastAssistantIndex(messages);
|
|
7563
7913
|
const lastMsg = lastIdx >= 0 ? messages[lastIdx] : null;
|
|
@@ -7594,7 +7944,7 @@ function applyLlmDelta(state, event) {
|
|
|
7594
7944
|
function applyLlmResponse(state, event) {
|
|
7595
7945
|
const messages = [...state.messages];
|
|
7596
7946
|
const message = objectValue(event.data.message);
|
|
7597
|
-
const responseContent =
|
|
7947
|
+
const responseContent = stringValue2(message?.content) || stringValue2(event.data.content);
|
|
7598
7948
|
const toolCalls = Array.isArray(message?.tool_calls) ? message?.tool_calls : [];
|
|
7599
7949
|
const hasToolCalls = toolCalls.length > 0;
|
|
7600
7950
|
const idx = findLastAssistantIndex(messages);
|
|
@@ -7652,12 +8002,12 @@ function applyLlmResponse(state, event) {
|
|
|
7652
8002
|
cachedInputTokens: numberValue(event.data.cache_read_tokens),
|
|
7653
8003
|
totalTokens: inputTokens + outputTokens,
|
|
7654
8004
|
contextWindow: numberValue(event.data.context_window),
|
|
7655
|
-
model:
|
|
8005
|
+
model: stringValue2(event.data.model)
|
|
7656
8006
|
}
|
|
7657
8007
|
};
|
|
7658
8008
|
}
|
|
7659
8009
|
function applyLlmThinking(state, event) {
|
|
7660
|
-
const reasoningText =
|
|
8010
|
+
const reasoningText = stringValue2(event.data.reasoning) || stringValue2(event.data.content);
|
|
7661
8011
|
const messages = [...state.messages];
|
|
7662
8012
|
const idx = findLastAssistantIndex(messages);
|
|
7663
8013
|
const prev = idx >= 0 ? messages[idx] : null;
|
|
@@ -7705,11 +8055,11 @@ function applyToolCall(state, event) {
|
|
|
7705
8055
|
messages[assistantIdx] = assistant;
|
|
7706
8056
|
}
|
|
7707
8057
|
const existingCalls = assistant.toolCalls ?? [];
|
|
7708
|
-
const toolCallId =
|
|
8058
|
+
const toolCallId = stringValue2(event.data.tool_call_id) || `tc-${event.eventId}`;
|
|
7709
8059
|
if (!existingCalls.some((tc) => tc.id === toolCallId)) {
|
|
7710
8060
|
const entry = {
|
|
7711
8061
|
id: toolCallId,
|
|
7712
|
-
toolName:
|
|
8062
|
+
toolName: stringValue2(event.data.name) || stringValue2(event.data.tool_name) || "unknown",
|
|
7713
8063
|
args: typeof event.data.arguments === "string" ? event.data.arguments : JSON.stringify(event.data.arguments ?? {}),
|
|
7714
8064
|
status: "running"
|
|
7715
8065
|
};
|
|
@@ -7725,7 +8075,7 @@ function applyToolCall(state, event) {
|
|
|
7725
8075
|
};
|
|
7726
8076
|
}
|
|
7727
8077
|
function applyToolResult(messages, data) {
|
|
7728
|
-
const targetId =
|
|
8078
|
+
const targetId = stringValue2(data.tool_call_id);
|
|
7729
8079
|
let matchIdx = -1;
|
|
7730
8080
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7731
8081
|
if (messages[i]?.role === "assistant" && messages[i]?.toolCalls?.some((tc) => tc.id === targetId)) {
|
|
@@ -7794,8 +8144,8 @@ function buildErrorInfo(event) {
|
|
|
7794
8144
|
}
|
|
7795
8145
|
return {
|
|
7796
8146
|
category: event.data.error_category,
|
|
7797
|
-
title:
|
|
7798
|
-
detail:
|
|
8147
|
+
title: stringValue2(event.data.error_title) || "The session failed due to an internal error.",
|
|
8148
|
+
detail: stringValue2(event.data.error_detail) || stringValue2(event.data.error),
|
|
7799
8149
|
retryable: Boolean(event.data.retryable)
|
|
7800
8150
|
};
|
|
7801
8151
|
}
|
|
@@ -7839,7 +8189,7 @@ function applyExpertFeedback(messages, type, data) {
|
|
|
7839
8189
|
return messages;
|
|
7840
8190
|
}
|
|
7841
8191
|
function applyClarifyResponse(messages, data) {
|
|
7842
|
-
const targetToolId =
|
|
8192
|
+
const targetToolId = stringValue2(data.tool_call_id);
|
|
7843
8193
|
const responses = Array.isArray(data.responses) ? data.responses : void 0;
|
|
7844
8194
|
if (!targetToolId || !responses) return messages;
|
|
7845
8195
|
const next = [...messages];
|
|
@@ -7854,8 +8204,8 @@ function applyClarifyResponse(messages, data) {
|
|
|
7854
8204
|
clarifyAnswers: responses.map((response) => {
|
|
7855
8205
|
const row = objectValue(response) ?? {};
|
|
7856
8206
|
return {
|
|
7857
|
-
question:
|
|
7858
|
-
answer:
|
|
8207
|
+
question: stringValue2(row.question),
|
|
8208
|
+
answer: stringValue2(row.answer),
|
|
7859
8209
|
is_other: Boolean(row.is_other)
|
|
7860
8210
|
};
|
|
7861
8211
|
})
|
|
@@ -7875,7 +8225,7 @@ function applyPolicyDenied(state, data) {
|
|
|
7875
8225
|
...policyMsg,
|
|
7876
8226
|
content: `${policyMsg.content}
|
|
7877
8227
|
|
|
7878
|
-
**Policy denied**: ${
|
|
8228
|
+
**Policy denied**: ${stringValue2(data.reason) || "Action blocked by governance policy."}`,
|
|
7879
8229
|
status: "error"
|
|
7880
8230
|
};
|
|
7881
8231
|
}
|
|
@@ -7927,7 +8277,7 @@ function objectValue(value) {
|
|
|
7927
8277
|
}
|
|
7928
8278
|
return void 0;
|
|
7929
8279
|
}
|
|
7930
|
-
function
|
|
8280
|
+
function stringValue2(value) {
|
|
7931
8281
|
return typeof value === "string" ? value : "";
|
|
7932
8282
|
}
|
|
7933
8283
|
function numberValue(value) {
|
|
@@ -7944,6 +8294,7 @@ function useAgentChatRuntime({
|
|
|
7944
8294
|
const [state, setState] = useState(
|
|
7945
8295
|
() => createInitialAgentChatState({ isLoadingHistory: Boolean(sessionId) })
|
|
7946
8296
|
);
|
|
8297
|
+
const [session, setSession] = useState(null);
|
|
7947
8298
|
const stateRef = useRef(state);
|
|
7948
8299
|
const streamRef = useRef(null);
|
|
7949
8300
|
const reconnectTimerRef = useRef(null);
|
|
@@ -7968,6 +8319,7 @@ function useAgentChatRuntime({
|
|
|
7968
8319
|
clearReconnectTimer();
|
|
7969
8320
|
closeStream();
|
|
7970
8321
|
if (!sessionId) {
|
|
8322
|
+
setSession(null);
|
|
7971
8323
|
setState(createInitialAgentChatState());
|
|
7972
8324
|
return;
|
|
7973
8325
|
}
|
|
@@ -8015,17 +8367,19 @@ function useAgentChatRuntime({
|
|
|
8015
8367
|
isLoadingHistory: true
|
|
8016
8368
|
});
|
|
8017
8369
|
stateRef.current = initialState;
|
|
8370
|
+
setSession(null);
|
|
8018
8371
|
setState(initialState);
|
|
8019
8372
|
connect(0);
|
|
8020
|
-
adapter.getSession({ sessionId }).then((
|
|
8373
|
+
adapter.getSession({ sessionId }).then((loadedSession) => {
|
|
8021
8374
|
if (cancelled) return;
|
|
8022
|
-
|
|
8375
|
+
setSession(loadedSession);
|
|
8376
|
+
if (loadedSession.messageCount === 0) {
|
|
8023
8377
|
setState((prev) => ({
|
|
8024
8378
|
...prev,
|
|
8025
8379
|
isLoadingHistory: false
|
|
8026
8380
|
}));
|
|
8027
8381
|
}
|
|
8028
|
-
if (isTerminalStatus(
|
|
8382
|
+
if (isTerminalStatus(loadedSession.status)) {
|
|
8029
8383
|
setState((prev) => ({
|
|
8030
8384
|
...prev,
|
|
8031
8385
|
terminal: true,
|
|
@@ -8114,9 +8468,9 @@ function useAgentChatRuntime({
|
|
|
8114
8468
|
markSending(content, images);
|
|
8115
8469
|
if (!sessionId) {
|
|
8116
8470
|
try {
|
|
8117
|
-
const
|
|
8118
|
-
onSessionChange?.(
|
|
8119
|
-
await adapter.sendMessage({ sessionId:
|
|
8471
|
+
const session2 = await adapter.createSession({ agentId });
|
|
8472
|
+
onSessionChange?.(session2.id);
|
|
8473
|
+
await adapter.sendMessage({ sessionId: session2.id, content, images });
|
|
8120
8474
|
} catch (error) {
|
|
8121
8475
|
markSendError(error instanceof Error ? error.message : "send failed");
|
|
8122
8476
|
throw error;
|
|
@@ -8157,6 +8511,8 @@ function useAgentChatRuntime({
|
|
|
8157
8511
|
}
|
|
8158
8512
|
}, [adapter, sessionId]);
|
|
8159
8513
|
return {
|
|
8514
|
+
state,
|
|
8515
|
+
session,
|
|
8160
8516
|
messages: state.messages,
|
|
8161
8517
|
isRunning: state.isRunning,
|
|
8162
8518
|
isLoadingHistory: state.isLoadingHistory,
|
|
@@ -8204,6 +8560,10 @@ function AgentChat({
|
|
|
8204
8560
|
sessionId,
|
|
8205
8561
|
onSessionChange
|
|
8206
8562
|
});
|
|
8563
|
+
const isReadOnlySession = isScheduledRunSession(runtime.session);
|
|
8564
|
+
const effectiveDisabled = disabled || isReadOnlySession;
|
|
8565
|
+
const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
|
|
8566
|
+
const browserState = runtime.state.browser;
|
|
8207
8567
|
useEffect(() => {
|
|
8208
8568
|
onMessagesChange?.(runtime.messages);
|
|
8209
8569
|
}, [onMessagesChange, runtime.messages]);
|
|
@@ -8234,28 +8594,339 @@ function AgentChat({
|
|
|
8234
8594
|
onStop: () => void runtime.stop(),
|
|
8235
8595
|
onRetry: runtime.retry,
|
|
8236
8596
|
onFileSelect: handleFileSelect,
|
|
8237
|
-
disabled,
|
|
8597
|
+
disabled: effectiveDisabled,
|
|
8598
|
+
disabledReason,
|
|
8238
8599
|
tokenUsage: runtime.tokenUsage,
|
|
8239
8600
|
retryIndicator: runtime.retryIndicator
|
|
8240
8601
|
}
|
|
8241
8602
|
) }),
|
|
8242
|
-
/* @__PURE__ */
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8603
|
+
/* @__PURE__ */ jsxs("div", { "data-testid": "right-stack", className: "flex min-h-0 shrink-0 flex-col", children: [
|
|
8604
|
+
browserState !== null && sessionId && /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 border-b border-line", children: /* @__PURE__ */ jsx(
|
|
8605
|
+
BrowserPane,
|
|
8606
|
+
{
|
|
8607
|
+
sessionId,
|
|
8608
|
+
state: browserState,
|
|
8609
|
+
adapter
|
|
8610
|
+
}
|
|
8611
|
+
) }),
|
|
8612
|
+
/* @__PURE__ */ jsx(
|
|
8613
|
+
WorkspacePanel,
|
|
8614
|
+
{
|
|
8615
|
+
adapter,
|
|
8616
|
+
sessionId,
|
|
8617
|
+
selectedPath: workspacePath,
|
|
8618
|
+
onSelectedPathChange: setWorkspacePath,
|
|
8619
|
+
collapsed: workspaceCollapsed,
|
|
8620
|
+
onCollapsedChange: setWorkspaceCollapsed,
|
|
8621
|
+
refreshSignal: runtime.workspaceRefreshKey,
|
|
8622
|
+
disabled: effectiveDisabled
|
|
8623
|
+
}
|
|
8624
|
+
)
|
|
8625
|
+
] })
|
|
8255
8626
|
] }) })
|
|
8256
8627
|
}
|
|
8257
8628
|
);
|
|
8258
8629
|
}
|
|
8630
|
+
var DEFAULT_LIMIT = 50;
|
|
8631
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
8632
|
+
function fingerprint(items) {
|
|
8633
|
+
return items.map(
|
|
8634
|
+
(item) => `${item.id}:${item.status}:${item.kind ?? ""}:${item.name ?? ""}:${item.prompt}:${item.scheduleDisplay}:${item.runCount}:${item.nextRunAt ?? ""}:${item.lastRunAt ?? ""}:${item.lastSessionId ?? ""}:${item.lastError ?? ""}:${item.updatedAt}`
|
|
8635
|
+
).join("|");
|
|
8636
|
+
}
|
|
8637
|
+
function formatKind(value) {
|
|
8638
|
+
if (value === "dynamic_loop") return "Dynamic loop";
|
|
8639
|
+
if (value === "cron") return "Cron";
|
|
8640
|
+
if (value === "one_shot") return "One-shot";
|
|
8641
|
+
if (value === "scheduled") return "Scheduled";
|
|
8642
|
+
if (!value) return "Scheduled";
|
|
8643
|
+
return value.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
8644
|
+
}
|
|
8645
|
+
function formatRelative(value, label) {
|
|
8646
|
+
if (!value) return null;
|
|
8647
|
+
const date = new Date(value);
|
|
8648
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
8649
|
+
return `${label} ${formatDistanceToNow(date, { addSuffix: true })}`;
|
|
8650
|
+
}
|
|
8651
|
+
function formatRuns(count) {
|
|
8652
|
+
return `${count} ${count === 1 ? "run" : "runs"}`;
|
|
8653
|
+
}
|
|
8654
|
+
function scheduleTitle(item) {
|
|
8655
|
+
const title = item.name?.trim();
|
|
8656
|
+
if (title) return title;
|
|
8657
|
+
return item.prompt;
|
|
8658
|
+
}
|
|
8659
|
+
function canRunScheduleNow(item) {
|
|
8660
|
+
return item.repeatLimit === 1;
|
|
8661
|
+
}
|
|
8662
|
+
function scheduleDisplay(item) {
|
|
8663
|
+
if (item.kind === "dynamic_loop") return null;
|
|
8664
|
+
return item.scheduleDisplay;
|
|
8665
|
+
}
|
|
8666
|
+
function sortScheduledWork(items) {
|
|
8667
|
+
return [...items].sort((a, b) => {
|
|
8668
|
+
if (a.status === "active" && b.status !== "active") return -1;
|
|
8669
|
+
if (a.status !== "active" && b.status === "active") return 1;
|
|
8670
|
+
const aNext = a.nextRunAt ?? "";
|
|
8671
|
+
const bNext = b.nextRunAt ?? "";
|
|
8672
|
+
if (aNext && bNext && aNext !== bNext) return aNext < bNext ? -1 : 1;
|
|
8673
|
+
if (aNext && !bNext) return -1;
|
|
8674
|
+
if (!aNext && bNext) return 1;
|
|
8675
|
+
return (a.updatedAt ?? "") > (b.updatedAt ?? "") ? -1 : 1;
|
|
8676
|
+
});
|
|
8677
|
+
}
|
|
8678
|
+
function ScheduledWorkRow({
|
|
8679
|
+
item,
|
|
8680
|
+
canRunNow,
|
|
8681
|
+
canCancel,
|
|
8682
|
+
actionScheduleId,
|
|
8683
|
+
onOpenLastRun,
|
|
8684
|
+
onRunNow,
|
|
8685
|
+
onCancel
|
|
8686
|
+
}) {
|
|
8687
|
+
const isActive = item.status === "active";
|
|
8688
|
+
const disabled = actionScheduleId === item.id;
|
|
8689
|
+
const title = scheduleTitle(item);
|
|
8690
|
+
const statusText = item.status === "active" ? null : item.status;
|
|
8691
|
+
const showRunNow = canRunNow && isActive && canRunScheduleNow(item);
|
|
8692
|
+
const lastSessionId = item.lastSessionId ?? null;
|
|
8693
|
+
const meta = [
|
|
8694
|
+
scheduleDisplay(item),
|
|
8695
|
+
formatRelative(item.nextRunAt, "Next"),
|
|
8696
|
+
formatRelative(item.lastRunAt, "Last"),
|
|
8697
|
+
formatRuns(item.runCount),
|
|
8698
|
+
item.expiresAt ? formatRelative(item.expiresAt, "Expires") : null
|
|
8699
|
+
].filter(Boolean);
|
|
8700
|
+
const openLastRun = () => {
|
|
8701
|
+
if (lastSessionId) onOpenLastRun(lastSessionId);
|
|
8702
|
+
};
|
|
8703
|
+
const handleRowKeyDown = (e) => {
|
|
8704
|
+
if (!lastSessionId) return;
|
|
8705
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
8706
|
+
e.preventDefault();
|
|
8707
|
+
openLastRun();
|
|
8708
|
+
};
|
|
8709
|
+
const stopActionClick = (e) => {
|
|
8710
|
+
e.stopPropagation();
|
|
8711
|
+
};
|
|
8712
|
+
return /* @__PURE__ */ jsxs(
|
|
8713
|
+
"div",
|
|
8714
|
+
{
|
|
8715
|
+
className: cn(
|
|
8716
|
+
"group flex min-w-0 items-start gap-2 border-l-2 border-l-transparent px-3 py-2 text-sm transition-colors hover:border-l-primary hover:bg-input",
|
|
8717
|
+
lastSessionId && "cursor-pointer"
|
|
8718
|
+
),
|
|
8719
|
+
onClick: openLastRun,
|
|
8720
|
+
onKeyDown: handleRowKeyDown,
|
|
8721
|
+
role: lastSessionId ? "button" : void 0,
|
|
8722
|
+
tabIndex: lastSessionId ? 0 : void 0,
|
|
8723
|
+
"aria-label": lastSessionId ? `Open last run for ${title}` : void 0,
|
|
8724
|
+
title: lastSessionId ? "Open last run" : void 0,
|
|
8725
|
+
children: [
|
|
8726
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center text-faint", children: /* @__PURE__ */ jsx(CalendarClockIcon, { className: "size-3.5" }) }),
|
|
8727
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
8728
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
|
|
8729
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
8730
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-foreground", children: title }),
|
|
8731
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex min-w-0 items-center gap-2", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "shrink-0", children: formatKind(item.kind) }) }),
|
|
8732
|
+
meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-0.5 space-y-0.5 text-xs text-faint", children: meta.map((line) => /* @__PURE__ */ jsx("div", { className: "truncate", children: line }, line)) })
|
|
8733
|
+
] }),
|
|
8734
|
+
statusText && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "shrink-0", children: statusText })
|
|
8735
|
+
] }),
|
|
8736
|
+
item.lastError && /* @__PURE__ */ jsx("div", { className: "mt-1 line-clamp-2 text-xs text-destructive", children: item.lastError })
|
|
8737
|
+
] }),
|
|
8738
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
8739
|
+
showRunNow && /* @__PURE__ */ jsx(
|
|
8740
|
+
"button",
|
|
8741
|
+
{
|
|
8742
|
+
type: "button",
|
|
8743
|
+
className: "rounded p-1 text-faint opacity-70 transition-all hover:bg-line hover:text-foreground disabled:pointer-events-none disabled:opacity-40 group-hover:opacity-100",
|
|
8744
|
+
onClick: (e) => {
|
|
8745
|
+
stopActionClick(e);
|
|
8746
|
+
onRunNow(item.id);
|
|
8747
|
+
},
|
|
8748
|
+
"aria-label": "Run schedule now",
|
|
8749
|
+
title: "Run schedule now",
|
|
8750
|
+
disabled,
|
|
8751
|
+
children: /* @__PURE__ */ jsx(CalendarClockIcon, { className: "size-3.5" })
|
|
8752
|
+
}
|
|
8753
|
+
),
|
|
8754
|
+
canCancel && isActive && /* @__PURE__ */ jsx(
|
|
8755
|
+
"button",
|
|
8756
|
+
{
|
|
8757
|
+
type: "button",
|
|
8758
|
+
className: "rounded p-1 text-faint opacity-70 transition-all hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none disabled:opacity-40 group-hover:opacity-100",
|
|
8759
|
+
onClick: (e) => {
|
|
8760
|
+
stopActionClick(e);
|
|
8761
|
+
onCancel(item.id);
|
|
8762
|
+
},
|
|
8763
|
+
"aria-label": "Cancel schedule",
|
|
8764
|
+
title: "Cancel schedule",
|
|
8765
|
+
disabled,
|
|
8766
|
+
children: /* @__PURE__ */ jsx(Trash2Icon, { className: "size-3.5" })
|
|
8767
|
+
}
|
|
8768
|
+
)
|
|
8769
|
+
] })
|
|
8770
|
+
]
|
|
8771
|
+
}
|
|
8772
|
+
);
|
|
8773
|
+
}
|
|
8774
|
+
function ScheduledWorkPanel({
|
|
8775
|
+
adapter,
|
|
8776
|
+
agentId,
|
|
8777
|
+
title = "Scheduled Work",
|
|
8778
|
+
limit = DEFAULT_LIMIT,
|
|
8779
|
+
status = "active",
|
|
8780
|
+
hideHeader = false,
|
|
8781
|
+
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
8782
|
+
onSessionSelect,
|
|
8783
|
+
onScheduleCancel,
|
|
8784
|
+
onScheduleRunNow
|
|
8785
|
+
}) {
|
|
8786
|
+
const [items, setItems] = useState([]);
|
|
8787
|
+
const [error, setError] = useState(null);
|
|
8788
|
+
const [hasEverLoaded, setHasEverLoaded] = useState(false);
|
|
8789
|
+
const [actionScheduleId, setActionScheduleId] = useState(null);
|
|
8790
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
8791
|
+
const mounted = useRef(true);
|
|
8792
|
+
const requestId = useRef(0);
|
|
8793
|
+
const lastFingerprint = useRef("");
|
|
8794
|
+
const refetch = useCallback(
|
|
8795
|
+
async (opts) => {
|
|
8796
|
+
if (!adapter.listScheduledWork) {
|
|
8797
|
+
setItems([]);
|
|
8798
|
+
setHasEverLoaded(true);
|
|
8799
|
+
return;
|
|
8800
|
+
}
|
|
8801
|
+
const currentRequestId = ++requestId.current;
|
|
8802
|
+
try {
|
|
8803
|
+
const list = await adapter.listScheduledWork({
|
|
8804
|
+
agentId,
|
|
8805
|
+
status,
|
|
8806
|
+
limit
|
|
8807
|
+
});
|
|
8808
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
8809
|
+
const nextItems = sortScheduledWork(list.items);
|
|
8810
|
+
const fp = fingerprint(nextItems);
|
|
8811
|
+
if (fp !== lastFingerprint.current) {
|
|
8812
|
+
lastFingerprint.current = fp;
|
|
8813
|
+
setItems(nextItems);
|
|
8814
|
+
}
|
|
8815
|
+
setError(null);
|
|
8816
|
+
setHasEverLoaded(true);
|
|
8817
|
+
} catch (e) {
|
|
8818
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
8819
|
+
if (!opts?.silent) {
|
|
8820
|
+
setError(
|
|
8821
|
+
e instanceof Error ? e.message : "Failed to load scheduled work"
|
|
8822
|
+
);
|
|
8823
|
+
setHasEverLoaded(true);
|
|
8824
|
+
}
|
|
8825
|
+
}
|
|
8826
|
+
},
|
|
8827
|
+
[adapter, agentId, limit, status]
|
|
8828
|
+
);
|
|
8829
|
+
useEffect(() => {
|
|
8830
|
+
mounted.current = true;
|
|
8831
|
+
return () => {
|
|
8832
|
+
mounted.current = false;
|
|
8833
|
+
};
|
|
8834
|
+
}, []);
|
|
8835
|
+
useEffect(() => {
|
|
8836
|
+
setError(null);
|
|
8837
|
+
lastFingerprint.current = "";
|
|
8838
|
+
void refetch();
|
|
8839
|
+
}, [refetch]);
|
|
8840
|
+
useEffect(() => {
|
|
8841
|
+
if (!adapter.listScheduledWork || pollIntervalMs <= 0) return;
|
|
8842
|
+
const id = setInterval(() => {
|
|
8843
|
+
void refetch({ silent: true });
|
|
8844
|
+
}, pollIntervalMs);
|
|
8845
|
+
return () => clearInterval(id);
|
|
8846
|
+
}, [adapter.listScheduledWork, pollIntervalMs, refetch]);
|
|
8847
|
+
const activeCount = useMemo(
|
|
8848
|
+
() => items.filter((item) => item.status === "active").length,
|
|
8849
|
+
[items]
|
|
8850
|
+
);
|
|
8851
|
+
const handleOpenLastRun = useCallback(
|
|
8852
|
+
(sessionId) => {
|
|
8853
|
+
onSessionSelect?.(sessionId);
|
|
8854
|
+
},
|
|
8855
|
+
[onSessionSelect]
|
|
8856
|
+
);
|
|
8857
|
+
const handleRunNow = useCallback(
|
|
8858
|
+
async (scheduleId) => {
|
|
8859
|
+
if (!adapter.runScheduledWorkNow || actionScheduleId) return;
|
|
8860
|
+
setActionScheduleId(scheduleId);
|
|
8861
|
+
try {
|
|
8862
|
+
const result = await adapter.runScheduledWorkNow({ scheduleId });
|
|
8863
|
+
onScheduleRunNow?.(scheduleId, result?.sessionId);
|
|
8864
|
+
await refetch({ silent: true });
|
|
8865
|
+
} catch (e) {
|
|
8866
|
+
setError(e instanceof Error ? e.message : "Failed to run schedule");
|
|
8867
|
+
} finally {
|
|
8868
|
+
if (mounted.current) setActionScheduleId(null);
|
|
8869
|
+
}
|
|
8870
|
+
},
|
|
8871
|
+
[actionScheduleId, adapter, onScheduleRunNow, refetch]
|
|
8872
|
+
);
|
|
8873
|
+
const handleCancel = useCallback(
|
|
8874
|
+
async (scheduleId) => {
|
|
8875
|
+
if (!adapter.cancelScheduledWork || actionScheduleId) return;
|
|
8876
|
+
setActionScheduleId(scheduleId);
|
|
8877
|
+
try {
|
|
8878
|
+
await adapter.cancelScheduledWork({ scheduleId });
|
|
8879
|
+
setItems((current) => {
|
|
8880
|
+
const next = current.filter((item) => item.id !== scheduleId);
|
|
8881
|
+
lastFingerprint.current = fingerprint(next);
|
|
8882
|
+
return next;
|
|
8883
|
+
});
|
|
8884
|
+
onScheduleCancel?.(scheduleId);
|
|
8885
|
+
await refetch({ silent: true });
|
|
8886
|
+
} catch (e) {
|
|
8887
|
+
setError(e instanceof Error ? e.message : "Failed to cancel schedule");
|
|
8888
|
+
} finally {
|
|
8889
|
+
if (mounted.current) setActionScheduleId(null);
|
|
8890
|
+
}
|
|
8891
|
+
},
|
|
8892
|
+
[actionScheduleId, adapter, onScheduleCancel, refetch]
|
|
8893
|
+
);
|
|
8894
|
+
if (!hasEverLoaded) return null;
|
|
8895
|
+
if (!adapter.listScheduledWork) return null;
|
|
8896
|
+
const showBody = hideHeader || !collapsed;
|
|
8897
|
+
return /* @__PURE__ */ jsxs("section", { className: "min-w-0 border-t border-line", children: [
|
|
8898
|
+
!hideHeader && /* @__PURE__ */ jsxs(
|
|
8899
|
+
"button",
|
|
8900
|
+
{
|
|
8901
|
+
type: "button",
|
|
8902
|
+
className: "flex w-full items-center gap-1.5 px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-foreground transition-colors hover:bg-input",
|
|
8903
|
+
onClick: () => setCollapsed((current) => !current),
|
|
8904
|
+
"aria-expanded": !collapsed,
|
|
8905
|
+
children: [
|
|
8906
|
+
/* @__PURE__ */ jsx(CalendarClockIcon, { className: "size-3.5" }),
|
|
8907
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: title }),
|
|
8908
|
+
activeCount > 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", className: "ml-auto", children: activeCount }),
|
|
8909
|
+
collapsed ? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3.5 shrink-0 text-faint" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3.5 shrink-0 text-faint" })
|
|
8910
|
+
]
|
|
8911
|
+
}
|
|
8912
|
+
),
|
|
8913
|
+
showBody && error && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
|
|
8914
|
+
showBody && !error && items.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-faint", children: "No scheduled work" }),
|
|
8915
|
+
showBody && !error && items.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("pb-2", hideHeader && "pt-1"), children: items.map((item) => /* @__PURE__ */ jsx(
|
|
8916
|
+
ScheduledWorkRow,
|
|
8917
|
+
{
|
|
8918
|
+
item,
|
|
8919
|
+
canRunNow: Boolean(adapter.runScheduledWorkNow),
|
|
8920
|
+
canCancel: Boolean(adapter.cancelScheduledWork),
|
|
8921
|
+
actionScheduleId,
|
|
8922
|
+
onOpenLastRun: handleOpenLastRun,
|
|
8923
|
+
onRunNow: handleRunNow,
|
|
8924
|
+
onCancel: handleCancel
|
|
8925
|
+
},
|
|
8926
|
+
item.id
|
|
8927
|
+
)) })
|
|
8928
|
+
] });
|
|
8929
|
+
}
|
|
8259
8930
|
var POLL_INTERVAL_ACTIVE_MS = 4e3;
|
|
8260
8931
|
var POLL_INTERVAL_IDLE_MS = 3e4;
|
|
8261
8932
|
var DEFAULT_SESSION_LIST_LIMIT = 50;
|
|
@@ -8280,7 +8951,7 @@ function buildTree(nodes) {
|
|
|
8280
8951
|
}
|
|
8281
8952
|
function treeFingerprint(nodes) {
|
|
8282
8953
|
return nodes.map(
|
|
8283
|
-
(n) => `${n.id}:${n.parentId ?? ""}:${n.status}:${n.agentType ?? ""}:${n.messageCount ?? 0}:${n.toolCallCount ?? 0}:${n.updatedAt}`
|
|
8954
|
+
(n) => `${n.id}:${n.parentId ?? ""}:${n.status}:${n.agentType ?? ""}:${n.runKind ?? ""}:${n.title ?? ""}:${n.messageCount ?? 0}:${n.toolCallCount ?? 0}:${n.updatedAt}`
|
|
8284
8955
|
).join("|");
|
|
8285
8956
|
}
|
|
8286
8957
|
function sessionToTreeNode(session) {
|
|
@@ -8290,6 +8961,7 @@ function sessionToTreeNode(session) {
|
|
|
8290
8961
|
parentId: session.parentId ?? null,
|
|
8291
8962
|
agentId: session.agentId,
|
|
8292
8963
|
channel: session.channel,
|
|
8964
|
+
runKind: session.runKind ?? deriveRunKind(session.channel, session.config),
|
|
8293
8965
|
status: session.status,
|
|
8294
8966
|
title: session.title,
|
|
8295
8967
|
model: session.model,
|
|
@@ -8338,6 +9010,23 @@ function formatSessionTime(value) {
|
|
|
8338
9010
|
if (Number.isNaN(date.getTime())) return "";
|
|
8339
9011
|
return formatDistanceToNow(date, { addSuffix: true });
|
|
8340
9012
|
}
|
|
9013
|
+
function deriveRunKind(channel, config) {
|
|
9014
|
+
if (channel === "scheduled" && config?.scheduled_dynamic_loop === true) {
|
|
9015
|
+
return "dynamic_loop";
|
|
9016
|
+
}
|
|
9017
|
+
if (channel === "scheduled") return "scheduled";
|
|
9018
|
+
return null;
|
|
9019
|
+
}
|
|
9020
|
+
function formatRunKind(value) {
|
|
9021
|
+
if (value === "dynamic_loop") return "Loop";
|
|
9022
|
+
if (value === "scheduled") return "Scheduled";
|
|
9023
|
+
return null;
|
|
9024
|
+
}
|
|
9025
|
+
function fallbackSessionTitle(entry) {
|
|
9026
|
+
if (entry.runKind === "dynamic_loop") return "Loop run";
|
|
9027
|
+
if (entry.runKind === "scheduled") return "Scheduled run";
|
|
9028
|
+
return "New session";
|
|
9029
|
+
}
|
|
8341
9030
|
function TreeNodeRow({
|
|
8342
9031
|
entry,
|
|
8343
9032
|
depth,
|
|
@@ -8353,9 +9042,11 @@ function TreeNodeRow({
|
|
|
8353
9042
|
const hasChildren = entry.children.length > 0;
|
|
8354
9043
|
const isActive = entry.id === activeSessionId;
|
|
8355
9044
|
const isRunning = entry.status === "active";
|
|
8356
|
-
const
|
|
8357
|
-
const title = entry.title ??
|
|
9045
|
+
const isChildSession = entry.parentId != null;
|
|
9046
|
+
const title = entry.title ?? fallbackSessionTitle(entry);
|
|
8358
9047
|
const subtitle = [
|
|
9048
|
+
formatRunKind(entry.runKind),
|
|
9049
|
+
entry.agentType,
|
|
8359
9050
|
entry.model ?? "default",
|
|
8360
9051
|
formatSessionTime(entry.updatedAt)
|
|
8361
9052
|
].filter(Boolean).join(" \xB7 ");
|
|
@@ -8393,7 +9084,7 @@ function TreeNodeRow({
|
|
|
8393
9084
|
/* @__PURE__ */ jsx("div", { className: "text-sm truncate", children: title }),
|
|
8394
9085
|
/* @__PURE__ */ jsx("div", { className: "text-xs text-faint truncate", children: subtitle })
|
|
8395
9086
|
] }),
|
|
8396
|
-
isRunning && canStop &&
|
|
9087
|
+
isRunning && canStop && isChildSession && /* @__PURE__ */ jsx(
|
|
8397
9088
|
"button",
|
|
8398
9089
|
{
|
|
8399
9090
|
type: "button",
|
|
@@ -8402,8 +9093,8 @@ function TreeNodeRow({
|
|
|
8402
9093
|
e.stopPropagation();
|
|
8403
9094
|
onStop(entry.id);
|
|
8404
9095
|
},
|
|
8405
|
-
"aria-label": "Stop
|
|
8406
|
-
title: "Stop
|
|
9096
|
+
"aria-label": "Stop child session",
|
|
9097
|
+
title: "Stop child session",
|
|
8407
9098
|
children: /* @__PURE__ */ jsx(SquareIcon, { className: "w-3 h-3", fill: "currentColor" })
|
|
8408
9099
|
}
|
|
8409
9100
|
),
|
|
@@ -8604,6 +9295,6 @@ function SessionTreePanel({
|
|
|
8604
9295
|
] });
|
|
8605
9296
|
}
|
|
8606
9297
|
|
|
8607
|
-
export { AgentChat, AgentChatAdapterProvider, MessageResponse, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime };
|
|
9298
|
+
export { AgentChat, AgentChatAdapterProvider, MessageResponse, ScheduledWorkPanel, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime };
|
|
8608
9299
|
//# sourceMappingURL=index.js.map
|
|
8609
9300
|
//# sourceMappingURL=index.js.map
|