@invergent/agent-chat-react 1.6.7 → 1.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -908,8 +908,7 @@ var useReasoning = () => {
908
908
  }
909
909
  return context;
910
910
  };
911
- var AUTO_CLOSE_DELAY = 1e3;
912
- var AUTO_OPEN_DELAY = 100;
911
+ var MIN_DURATION_MS = 100;
913
912
  var MS_IN_S = 1e3;
914
913
  var Reasoning = react.memo(
915
914
  ({
@@ -923,7 +922,6 @@ var Reasoning = react.memo(
923
922
  ...props
924
923
  }) => {
925
924
  const resolvedDefaultOpen = defaultOpen ?? false;
926
- const isExplicitlyClosed = defaultOpen === false;
927
925
  const [isOpen, setIsOpen] = reactUseControllableState.useControllableState({
928
926
  defaultProp: resolvedDefaultOpen,
929
927
  onChange: onOpenChange,
@@ -933,8 +931,6 @@ var Reasoning = react.memo(
933
931
  defaultProp: void 0,
934
932
  prop: durationProp
935
933
  });
936
- const hasEverStreamedRef = react.useRef(false);
937
- const [hasAutoClosed, setHasAutoClosed] = react.useState(false);
938
934
  const startTimeRef = react.useRef(null);
939
935
  react.useEffect(() => {
940
936
  if (isStreaming) {
@@ -944,29 +940,11 @@ var Reasoning = react.memo(
944
940
  } else if (startTimeRef.current !== null) {
945
941
  const elapsed = Date.now() - startTimeRef.current;
946
942
  startTimeRef.current = null;
947
- if (elapsed >= AUTO_OPEN_DELAY) {
948
- hasEverStreamedRef.current = true;
943
+ if (elapsed >= MIN_DURATION_MS) {
949
944
  setDuration(Math.ceil(elapsed / MS_IN_S));
950
945
  }
951
946
  }
952
947
  }, [isStreaming, setDuration]);
953
- react.useEffect(() => {
954
- if (!isStreaming || isOpen || isExplicitlyClosed) return;
955
- const timer = setTimeout(() => {
956
- hasEverStreamedRef.current = true;
957
- setIsOpen(true);
958
- }, AUTO_OPEN_DELAY);
959
- return () => clearTimeout(timer);
960
- }, [isStreaming, isOpen, setIsOpen, isExplicitlyClosed]);
961
- react.useEffect(() => {
962
- if (hasEverStreamedRef.current && !isStreaming && isOpen && !hasAutoClosed) {
963
- const timer = setTimeout(() => {
964
- setIsOpen(false);
965
- setHasAutoClosed(true);
966
- }, AUTO_CLOSE_DELAY);
967
- return () => clearTimeout(timer);
968
- }
969
- }, [isStreaming, isOpen, setIsOpen, hasAutoClosed]);
970
948
  const handleOpenChange = react.useCallback(
971
949
  (newOpen) => {
972
950
  setIsOpen(newOpen);
@@ -1392,7 +1370,7 @@ function TerminalCollapsible({ command, output, isRunning, hasError }) {
1392
1370
  className: "not-prose w-full",
1393
1371
  children: [
1394
1372
  /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleTrigger, { className: "group/trigger flex w-fit items-center gap-2 text-sm transition-colors", children: [
1395
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : hasError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-destructive", children: "Command failed" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
1373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : hasError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-destructive", children: "Command failed" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Command" }) }),
1396
1374
  /* @__PURE__ */ jsxRuntime.jsx(
1397
1375
  lucideReact.ChevronDownIcon,
1398
1376
  {
@@ -1443,14 +1421,16 @@ function TerminalToolBlock({ tc }) {
1443
1421
  const isRunning = tc.status === "running";
1444
1422
  const result = parseTerminalResult(tc.result, tc.args);
1445
1423
  if (result) {
1446
- const output = result.output || result.error || "";
1424
+ const hasError = result.exit_code !== 0 || Boolean(result.error);
1425
+ if (hasError) return null;
1426
+ const output = result.output || "";
1447
1427
  return /* @__PURE__ */ jsxRuntime.jsx(
1448
1428
  TerminalCollapsible,
1449
1429
  {
1450
1430
  command: result.command,
1451
1431
  output,
1452
1432
  isRunning,
1453
- hasError: result.exit_code !== 0 || Boolean(result.error)
1433
+ hasError: false
1454
1434
  }
1455
1435
  );
1456
1436
  }
@@ -2291,10 +2271,7 @@ function WebToolBlock({ tc }) {
2291
2271
  let displayText = "";
2292
2272
  try {
2293
2273
  const args = JSON.parse(tc.args);
2294
- if (tc.toolName === "web_extract") {
2295
- const urls = args.urls ?? [];
2296
- displayText = urls[0] ?? "";
2297
- } else if (tc.toolName === "web_search") {
2274
+ if (tc.toolName === "web_search") {
2298
2275
  displayText = args.query ?? "";
2299
2276
  } else if (tc.toolName === "web_crawl") {
2300
2277
  displayText = args.url ?? "";
@@ -2302,12 +2279,10 @@ function WebToolBlock({ tc }) {
2302
2279
  } catch {
2303
2280
  }
2304
2281
  const toolLabel2 = {
2305
- web_extract: "Web Fetch",
2306
2282
  web_search: "Web Search",
2307
2283
  web_crawl: "Web Crawl"
2308
2284
  }[tc.toolName] ?? tc.toolName;
2309
2285
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
2310
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2311
2286
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: toolLabel2 }),
2312
2287
  displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
2313
2288
  ] });
@@ -2327,6 +2302,89 @@ function VisionAnalyzeBlock({ tc }) {
2327
2302
  ] });
2328
2303
  }
2329
2304
  init_utils();
2305
+ function parseUrls(rawArgs) {
2306
+ try {
2307
+ const args = JSON.parse(rawArgs);
2308
+ if (Array.isArray(args.urls)) {
2309
+ return args.urls.filter((u) => typeof u === "string");
2310
+ }
2311
+ } catch {
2312
+ }
2313
+ return [];
2314
+ }
2315
+ function parseResult(rawResult) {
2316
+ const byUrl = /* @__PURE__ */ new Map();
2317
+ if (!rawResult) return { byUrl, toolError: null };
2318
+ try {
2319
+ const parsed = JSON.parse(rawResult);
2320
+ const entries = Array.isArray(parsed?.results) ? parsed.results : [];
2321
+ for (const entry of entries) {
2322
+ if (entry && typeof entry.url === "string") {
2323
+ byUrl.set(entry.url, entry);
2324
+ }
2325
+ }
2326
+ if (byUrl.size === 0 && typeof parsed?.error === "string") {
2327
+ return { byUrl, toolError: parsed.error };
2328
+ }
2329
+ } catch {
2330
+ }
2331
+ return { byUrl, toolError: null };
2332
+ }
2333
+ function domain(url) {
2334
+ try {
2335
+ return new URL(url).hostname.replace(/^www\./, "");
2336
+ } catch {
2337
+ return url;
2338
+ }
2339
+ }
2340
+ function WebExtractToolBlock({ tc }) {
2341
+ const urls = parseUrls(tc.args);
2342
+ const { byUrl, toolError } = parseResult(tc.result);
2343
+ const isComplete = tc.status === "complete" || tc.status === "error";
2344
+ if (urls.length === 0) {
2345
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
2346
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2347
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Web Fetch" })
2348
+ ] });
2349
+ }
2350
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2351
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm", children: [
2352
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2353
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Web Fetch" }),
2354
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 text-xs", children: [
2355
+ urls.length,
2356
+ " URL",
2357
+ urls.length !== 1 ? "s" : ""
2358
+ ] })
2359
+ ] }),
2360
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1 ml-5 space-y-0.5", children: urls.map((url, i) => {
2361
+ const entry = byUrl.get(url);
2362
+ const perUrlError = entry?.error;
2363
+ const failure = perUrlError ?? (isComplete && !entry ? toolError : null);
2364
+ const fetched = entry && !perUrlError;
2365
+ const pending = !isComplete && !entry;
2366
+ const row = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
2367
+ /* @__PURE__ */ jsxRuntime.jsx(
2368
+ "span",
2369
+ {
2370
+ className: cn(
2371
+ "truncate",
2372
+ pending ? "text-muted-foreground/50" : "text-muted-foreground"
2373
+ ),
2374
+ children: domain(url)
2375
+ }
2376
+ ),
2377
+ fetched && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3 text-green-500/70 shrink-0" }),
2378
+ failure && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-3 text-red-500/80 shrink-0" })
2379
+ ] });
2380
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: failure ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
2381
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: row }),
2382
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: failure })
2383
+ ] }) : row }, `${url}-${i}`);
2384
+ }) })
2385
+ ] });
2386
+ }
2387
+ init_utils();
2330
2388
 
2331
2389
  // src/components/chat/diff-viewer.tsx
2332
2390
  init_utils();
@@ -3015,7 +3073,7 @@ function parseArgs2(args) {
3015
3073
  return { action: "unknown" };
3016
3074
  }
3017
3075
  }
3018
- function parseResult(result) {
3076
+ function parseResult2(result) {
3019
3077
  if (!result) return null;
3020
3078
  try {
3021
3079
  return JSON.parse(result);
@@ -3233,7 +3291,7 @@ function OutputPreview({ output }) {
3233
3291
  function ProcessToolBlock({ tc }) {
3234
3292
  const isRunning = tc.status === "running";
3235
3293
  const args = parseArgs2(tc.args);
3236
- const result = parseResult(tc.result);
3294
+ const result = parseResult2(tc.result);
3237
3295
  const action = args.action || "unknown";
3238
3296
  const actionLabel = {
3239
3297
  list: "Process List",
@@ -4289,6 +4347,7 @@ function ToolCallBlock({
4289
4347
  case "session_search":
4290
4348
  return /* @__PURE__ */ jsxRuntime.jsx(SessionSearchBlock, { tc });
4291
4349
  case "web_extract":
4350
+ return /* @__PURE__ */ jsxRuntime.jsx(WebExtractToolBlock, { tc });
4292
4351
  case "web_search":
4293
4352
  case "web_crawl":
4294
4353
  return /* @__PURE__ */ jsxRuntime.jsx(WebToolBlock, { tc });
@@ -4330,6 +4389,51 @@ function ToolCallBlock({
4330
4389
  return /* @__PURE__ */ jsxRuntime.jsx(DefaultToolBlock, { tc });
4331
4390
  }
4332
4391
  }
4392
+ function parseQuery(rawArgs) {
4393
+ try {
4394
+ const args = JSON.parse(rawArgs);
4395
+ return typeof args.query === "string" ? args.query : "";
4396
+ } catch {
4397
+ return "";
4398
+ }
4399
+ }
4400
+ function parseResultCount2(rawResult) {
4401
+ if (!rawResult) return null;
4402
+ try {
4403
+ const parsed = JSON.parse(rawResult);
4404
+ if (Array.isArray(parsed?.data?.web)) return parsed.data.web.length;
4405
+ if (Array.isArray(parsed?.results)) return parsed.results.length;
4406
+ } catch {
4407
+ }
4408
+ return null;
4409
+ }
4410
+ function WebSearchRow({ tc }) {
4411
+ const query = parseQuery(tc.args);
4412
+ const count = parseResultCount2(tc.result);
4413
+ const showCount = count !== null && tc.status === "complete";
4414
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs min-w-0", children: [
4415
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: query || "\u2014" }),
4416
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/50 shrink-0", children: [
4417
+ count,
4418
+ " result",
4419
+ count !== 1 ? "s" : ""
4420
+ ] })
4421
+ ] });
4422
+ }
4423
+ function WebSearchGroupBlock({ tcs }) {
4424
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4425
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm", children: [
4426
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
4427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Web Search" }),
4428
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 text-xs", children: [
4429
+ tcs.length,
4430
+ " ",
4431
+ tcs.length === 1 ? "query" : "queries"
4432
+ ] })
4433
+ ] }),
4434
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1 ml-5 space-y-0.5", children: tcs.map((tc) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(WebSearchRow, { tc }) }, tc.id)) })
4435
+ ] });
4436
+ }
4333
4437
  var ChatMessage = react.memo(function ChatMessage2({
4334
4438
  message,
4335
4439
  isLast,
@@ -5756,6 +5860,11 @@ function ChatComposerInner({
5756
5860
  const builtin = [
5757
5861
  { value: "/clear", label: "/clear", description: "Clear conversation" },
5758
5862
  { value: "/compress", label: "/compress", description: "Compress context" },
5863
+ { value: "/goal", label: "/goal", description: "Define an outcome goal" },
5864
+ { value: "/goal status", label: "/goal status", description: "Show outcome goal status" },
5865
+ { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
5866
+ { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
5867
+ { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
5759
5868
  { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
5760
5869
  { value: "/loop list", label: "/loop list", description: "List active loops" },
5761
5870
  { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
@@ -6549,6 +6658,10 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
6549
6658
  });
6550
6659
  continue;
6551
6660
  }
6661
+ if (tc.toolName === "terminal" && tc.status !== "running") {
6662
+ const parsed = parseTerminalResult(tc.result, tc.args);
6663
+ if (parsed && (parsed.exit_code !== 0 || parsed.error)) continue;
6664
+ }
6552
6665
  entries.push({ kind: "tool", key: tc.id, tc });
6553
6666
  }
6554
6667
  }
@@ -6589,6 +6702,33 @@ function groupBrowserActivityEntries(entries) {
6589
6702
  function isBrowserToolCall(call) {
6590
6703
  return call.toolName.startsWith("browser_");
6591
6704
  }
6705
+ function groupWebSearchEntries(entries) {
6706
+ const grouped = [];
6707
+ let buffer = [];
6708
+ const flush = () => {
6709
+ if (buffer.length === 0) return;
6710
+ if (buffer.length === 1) {
6711
+ grouped.push({ kind: "tool", key: buffer[0].id, tc: buffer[0] });
6712
+ } else {
6713
+ grouped.push({
6714
+ kind: "web_search_group",
6715
+ key: `web-search-${buffer[0].id}-${buffer[buffer.length - 1].id}`,
6716
+ calls: buffer
6717
+ });
6718
+ }
6719
+ buffer = [];
6720
+ };
6721
+ for (const entry of entries) {
6722
+ if (entry.kind === "tool" && entry.tc.toolName === "web_search") {
6723
+ buffer.push(entry.tc);
6724
+ continue;
6725
+ }
6726
+ flush();
6727
+ grouped.push(entry);
6728
+ }
6729
+ flush();
6730
+ return grouped;
6731
+ }
6592
6732
  function groupMessages(messages) {
6593
6733
  const groups = [];
6594
6734
  let pendingSystem = [];
@@ -6754,6 +6894,18 @@ function TimelineEntryItem({
6754
6894
  /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(BrowserActivityGroup, { calls: entry.calls }) })
6755
6895
  ] });
6756
6896
  }
6897
+ if (entry.kind === "web_search_group") {
6898
+ const allComplete = entry.calls.every((tc) => tc.status === "complete");
6899
+ const anyRunning = entry.calls.some((tc) => tc.status === "running");
6900
+ const indicatorClass = anyRunning ? "bg-emerald-500 animate-pulse" : allComplete ? "bg-emerald-500" : "bg-red-500";
6901
+ return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6902
+ /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6903
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6904
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: cn("size-2 border-none", indicatorClass) })
6905
+ ] }),
6906
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(WebSearchGroupBlock, { tcs: entry.calls }) })
6907
+ ] });
6908
+ }
6757
6909
  if (entry.kind === "text") {
6758
6910
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6759
6911
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
@@ -6821,6 +6973,7 @@ function AssistantGroup({
6821
6973
  entries.push(...messageToEntries(messages[i], isLast, artifactFallbacks));
6822
6974
  }
6823
6975
  entries = groupBrowserActivityEntries(entries);
6976
+ entries = groupWebSearchEntries(entries);
6824
6977
  const isTailGroup = lastGlobalIndex === totalMessages - 1;
6825
6978
  const lastEntry = entries[entries.length - 1];
6826
6979
  const hasRunningTool = entries.some(
@@ -8985,11 +9138,12 @@ function useAgentChatRuntime({
8985
9138
  const send = react.useCallback(
8986
9139
  async (content, images) => {
8987
9140
  markSending(content, images);
9141
+ const goal = images?.length ? null : parseGoalDefinition(content);
8988
9142
  if (!sessionId) {
8989
9143
  try {
8990
9144
  const session2 = await adapter.createSession({ agentId });
8991
9145
  onSessionChange?.(session2.id);
8992
- await adapter.sendMessage({ sessionId: session2.id, content, images });
9146
+ await sendTurn(adapter, session2.id, content, images, goal);
8993
9147
  } catch (error) {
8994
9148
  markSendError(error instanceof Error ? error.message : "send failed");
8995
9149
  throw error;
@@ -8998,7 +9152,7 @@ function useAgentChatRuntime({
8998
9152
  }
8999
9153
  try {
9000
9154
  ensureStreamForNewTurn(sessionId);
9001
- await adapter.sendMessage({ sessionId, content, images });
9155
+ await sendTurn(adapter, sessionId, content, images, goal);
9002
9156
  } catch (error) {
9003
9157
  markSendError(error instanceof Error ? error.message : "send failed");
9004
9158
  throw error;
@@ -9071,6 +9225,30 @@ function findLastAssistantIndex2(messages) {
9071
9225
  }
9072
9226
  return -1;
9073
9227
  }
9228
+ var GOAL_RUBRIC_RE = /\n\s*(?:rubric|criteria)\s*:\s*\n/i;
9229
+ var GOAL_CONTROL_COMMANDS = /* @__PURE__ */ new Set(["", "status", "pause", "resume", "clear"]);
9230
+ async function sendTurn(adapter, sessionId, content, images, goal) {
9231
+ if (goal && adapter.defineOutcome) {
9232
+ await adapter.defineOutcome({
9233
+ sessionId,
9234
+ description: goal.description,
9235
+ rubric: goal.rubric
9236
+ });
9237
+ return;
9238
+ }
9239
+ await adapter.sendMessage({ sessionId, content, images });
9240
+ }
9241
+ function parseGoalDefinition(content) {
9242
+ const text = content.trim();
9243
+ if (text !== "/goal" && !text.startsWith("/goal ")) return null;
9244
+ const args = text.slice("/goal".length).trim();
9245
+ if (GOAL_CONTROL_COMMANDS.has(args.toLowerCase())) return null;
9246
+ const match = GOAL_RUBRIC_RE.exec(args);
9247
+ const description = (match ? args.slice(0, match.index) : args).trim();
9248
+ if (!description) return null;
9249
+ const rubric = match ? args.slice(match.index + match[0].length).trim() : "";
9250
+ return rubric ? { description, rubric } : { description };
9251
+ }
9074
9252
  function AgentChat({
9075
9253
  adapter,
9076
9254
  agentId,
@@ -9168,7 +9346,7 @@ function AgentChat({
9168
9346
  "div",
9169
9347
  {
9170
9348
  "data-testid": "workspace-panel-frame",
9171
- className: hasBrowserPanel ? "h-1/2 min-h-0 w-full overflow-hidden" : "min-h-0",
9349
+ className: hasBrowserPanel ? "h-1/2 min-h-0 w-full overflow-hidden" : "min-h-0 h-full",
9172
9350
  children: /* @__PURE__ */ jsxRuntime.jsx(
9173
9351
  WorkspacePanel,
9174
9352
  {
@@ -9887,10 +10065,11 @@ function formatKind(value) {
9887
10065
  if (!value) return "Scheduled";
9888
10066
  return value.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
9889
10067
  }
9890
- function formatRelative2(value, label) {
10068
+ function formatRelative2(value, label, future = false) {
9891
10069
  if (!value) return null;
9892
10070
  const date = new Date(value);
9893
10071
  if (Number.isNaN(date.getTime())) return null;
10072
+ if (future && date.getTime() <= Date.now()) return `${label} due now`;
9894
10073
  return `${label} ${dateFns.formatDistanceToNow(date, { addSuffix: true })}`;
9895
10074
  }
9896
10075
  function formatRuns(count) {
@@ -9937,10 +10116,10 @@ function ScheduledWorkRow({
9937
10116
  const lastSessionId = item.lastSessionId ?? null;
9938
10117
  const meta = [
9939
10118
  scheduleDisplay(item),
9940
- formatRelative2(item.nextRunAt, "Next"),
10119
+ formatRelative2(item.nextRunAt, "Next", true),
9941
10120
  formatRelative2(item.lastRunAt, "Last"),
9942
10121
  formatRuns(item.runCount),
9943
- item.expiresAt ? formatRelative2(item.expiresAt, "Expires") : null
10122
+ item.expiresAt ? formatRelative2(item.expiresAt, "Expires", true) : null
9944
10123
  ].filter(Boolean);
9945
10124
  const openLastRun = () => {
9946
10125
  if (lastSessionId) onOpenLastRun(lastSessionId);
@@ -10292,8 +10471,7 @@ function TreeNodeRow({
10292
10471
  const title = entry.title ?? fallbackSessionTitle(entry);
10293
10472
  const subtitle = [
10294
10473
  formatRunKind(entry.runKind),
10295
- entry.agentType,
10296
- entry.model ?? "default",
10474
+ entry.model ?? entry.agentType,
10297
10475
  formatSessionTime(entry.updatedAt)
10298
10476
  ].filter(Boolean).join(" \xB7 ");
10299
10477
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -10348,7 +10526,10 @@ function TreeNodeRow({
10348
10526
  "button",
10349
10527
  {
10350
10528
  type: "button",
10351
- className: "p-1 rounded opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none disabled:opacity-50 transition-all",
10529
+ className: cn(
10530
+ "p-1 rounded hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none transition-all",
10531
+ deletingSessionId === entry.id ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
10532
+ ),
10352
10533
  onClick: (e) => {
10353
10534
  e.stopPropagation();
10354
10535
  onDelete(entry.id);
@@ -10356,7 +10537,7 @@ function TreeNodeRow({
10356
10537
  "aria-label": "Delete session",
10357
10538
  title: "Delete session",
10358
10539
  disabled: deletingSessionId === entry.id,
10359
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "w-3 h-3" })
10540
+ children: deletingSessionId === entry.id ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "w-3 h-3" })
10360
10541
  }
10361
10542
  )
10362
10543
  ]