agent-relay-server 0.28.0 → 0.30.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/public/index.html CHANGED
@@ -11190,6 +11190,17 @@ function shortPath$1(cwd, segments = 2) {
11190
11190
  const parts = cwd.replace(/\/+$/, "").split("/");
11191
11191
  return parts.length <= segments ? cwd : parts.slice(-segments).join("/");
11192
11192
  }
11193
+ function normalizePathForCompare(path) {
11194
+ return path.trim().replace(/\\/g, "/").replace(/\/+$/, "") || "/";
11195
+ }
11196
+ function pathWithinBase(path, baseDir) {
11197
+ if (!path?.trim() || !baseDir?.trim()) return false;
11198
+ const normalizedPath = normalizePathForCompare(path);
11199
+ const normalizedBase = normalizePathForCompare(baseDir);
11200
+ if (normalizedBase === "/") return normalizedPath.startsWith("/");
11201
+ if (normalizedPath === "/") return false;
11202
+ return normalizedPath === normalizedBase || normalizedPath.startsWith(normalizedBase + "/");
11203
+ }
11193
11204
  function isBuiltInAgent(agent) {
11194
11205
  return agent?.meta?.builtin === true || agent?.id === "user" || agent?.id === "system";
11195
11206
  }
@@ -11355,7 +11366,7 @@ function isClaimableTaskWaiting(task) {
11355
11366
  function isClaimableMessageWaiting(msg) {
11356
11367
  return Boolean(msg.claimable && !msg.claimedBy && !(msg.kind === "task" && Number.isSafeInteger(msg.payload?.taskId)));
11357
11368
  }
11358
- function targetMatchesAgent(target, agent) {
11369
+ function dashboardTargetMatchesAgent(target, agent) {
11359
11370
  if (!target || !agent) return false;
11360
11371
  if (target === "broadcast" || target === agent.id) return true;
11361
11372
  if (target.startsWith("tag:")) return (agent.tags || []).includes(target.slice(4));
@@ -11749,7 +11760,7 @@ function timeAgo(now, iso) {
11749
11760
  if (diff < 86400) return Math.floor(diff / 3600) + "h ago";
11750
11761
  return Math.floor(diff / 86400) + "d ago";
11751
11762
  }
11752
- function fmtTime$1(iso) {
11763
+ function fmtTime(iso) {
11753
11764
  if (!iso) return "";
11754
11765
  return new Date(iso).toLocaleString();
11755
11766
  }
@@ -12066,12 +12077,6 @@ function firstAvailableProvider(orchestrator) {
12066
12077
  function defaultModelFor(provider) {
12067
12078
  return PROVIDER_CATALOG[provider]?.defaultModel ?? "";
12068
12079
  }
12069
- function pathWithinBase(path, baseDir) {
12070
- const value = path.trim().replace(/\\/g, "/").replace(/\/+$/, "");
12071
- const base = (baseDir || "").trim().replace(/\\/g, "/").replace(/\/+$/, "");
12072
- if (!value || !base) return false;
12073
- return value === base || value.startsWith(base + "/");
12074
- }
12075
12080
  function indexAgents(agents) {
12076
12081
  const byId = {};
12077
12082
  for (const agent of agents) byId[agent.id] = agent;
@@ -12191,16 +12196,8 @@ function renderTimelineMarkdown(events, exportedAt) {
12191
12196
  }
12192
12197
  return lines.join("\n");
12193
12198
  }
12194
- function normalizePathForCompare$1(path) {
12195
- return path.replace(/\/+$/, "") || "/";
12196
- }
12197
- function pathWithin$1(path, baseDir) {
12198
- const normalizedPath = normalizePathForCompare$1(path);
12199
- const normalizedBase = normalizePathForCompare$1(baseDir);
12200
- return normalizedPath === normalizedBase || normalizedPath.startsWith(normalizedBase + "/");
12201
- }
12202
12199
  function orchestratorForPath(orchestrators, path) {
12203
- return orchestrators.filter((orch) => orch.status === "online" && pathWithin$1(path, orch.baseDir)).sort((a, b) => normalizePathForCompare$1(b.baseDir).length - normalizePathForCompare$1(a.baseDir).length)[0];
12200
+ return orchestrators.filter((orch) => orch.status === "online" && pathWithinBase(path, orch.baseDir)).sort((a, b) => normalizePathForCompare(b.baseDir).length - normalizePathForCompare(a.baseDir).length)[0];
12204
12201
  }
12205
12202
  function orchestratorForAgent(orchestrators, agent) {
12206
12203
  const managed = orchestrators.find((orch) => orch.managedAgents.some((managedAgent) => managedAgent.agentId === agent.id));
@@ -12293,6 +12290,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
12293
12290
  health: null,
12294
12291
  maintenanceJobs: [],
12295
12292
  workspaces: [],
12293
+ workspaceFocusId: null,
12296
12294
  workspaceGitState: {},
12297
12295
  workspaceMergePreview: {},
12298
12296
  workspaceDiff: {},
@@ -12593,6 +12591,10 @@ var useRelayStore = create$1()(persist((set, get) => ({
12593
12591
  if (view === "files") await s.fetchOrchestrators();
12594
12592
  if (view === "analytics") await s.fetchAnalytics();
12595
12593
  },
12594
+ async openWorkspaceFocus(workspaceId) {
12595
+ set({ workspaceFocusId: workspaceId });
12596
+ await get().switchView("workspaces");
12597
+ },
12596
12598
  startClock() {
12597
12599
  useClock.getState().start();
12598
12600
  },
@@ -25823,7 +25825,7 @@ function useAgentAttention(agent) {
25823
25825
  const archivedAt = Number(archivedThreads[agent.id] || 0);
25824
25826
  const unread = archivedAt >= peerMessages.filter(isHumanInboundMessage).reduce((max, m) => Math.max(max, m.id), 0) && archivedAt > 0 ? 0 : peerMessages.filter((m) => isHumanInboundMessage(m) && !isSessionActivityStep(m) && !(m.readBy || []).includes("user") && m.id > cursor).length;
25825
25827
  const pendingPairInvite = pairs.find((p) => (p.status === "active" || p.status === "pending") && (p.requesterId === agent.id || p.targetId === agent.id))?.status === "pending";
25826
- const claimableTasks = tasks.filter((t) => isClaimableTaskWaiting(t) && targetMatchesAgent(t.target, agent)).length + messages.filter((m) => isClaimableMessageWaiting(m) && targetMatchesAgent(m.to, agent)).length;
25828
+ const claimableTasks = tasks.filter((t) => isClaimableTaskWaiting(t) && dashboardTargetMatchesAgent(t.target, agent)).length + messages.filter((m) => isClaimableMessageWaiting(m) && dashboardTargetMatchesAgent(m.to, agent)).length;
25827
25829
  return {
25828
25830
  unread,
25829
25831
  pendingPairInvite,
@@ -109163,6 +109165,60 @@ function formatTokenCount(tokens) {
109163
109165
  return String(tokens);
109164
109166
  }
109165
109167
  //#endregion
109168
+ //#region src/components/shared/branch-state-badge.tsx
109169
+ var BRANCH_STATE_META = {
109170
+ idle: {
109171
+ label: "idle",
109172
+ tip: "Branch worktree — nothing to land yet.",
109173
+ className: "bg-zinc-500/10 text-zinc-400 border-zinc-500/20",
109174
+ Icon: Circle
109175
+ },
109176
+ changes: {
109177
+ label: "changes",
109178
+ tip: "Unlanded work in this branch — use \"Mark workspace ready\" to land it.",
109179
+ className: "bg-amber-500/10 text-amber-400 border-amber-500/20",
109180
+ Icon: GitCommitHorizontal
109181
+ },
109182
+ ready: {
109183
+ label: "ready",
109184
+ tip: "Handed off — the relay auto-merge will land it (~2 min sweep).",
109185
+ className: "bg-blue-500/10 text-blue-400 border-blue-500/20",
109186
+ Icon: CircleArrowUp
109187
+ },
109188
+ steward: {
109189
+ label: "steward",
109190
+ tip: "Under reconciliation — a steward is landing it. No action needed.",
109191
+ className: "bg-orange-500/10 text-orange-400 border-orange-500/20",
109192
+ Icon: Wrench
109193
+ },
109194
+ blocked: {
109195
+ label: "blocked",
109196
+ tip: "Escalated — the auto-merge/steward path is stuck. Needs human attention.",
109197
+ className: "bg-red-500/10 text-red-400 border-red-500/20",
109198
+ Icon: TriangleAlert
109199
+ }
109200
+ };
109201
+ function BranchStateBadge({ state, onClick, iconOnly = false, className }) {
109202
+ const meta = BRANCH_STATE_META[state];
109203
+ const { Icon } = meta;
109204
+ const interactive = Boolean(onClick);
109205
+ const tip = interactive ? `${meta.tip} Click to open this workspace.` : meta.tip;
109206
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
109207
+ role: interactive ? "button" : void 0,
109208
+ tabIndex: interactive ? 0 : void 0,
109209
+ title: tip,
109210
+ onClick,
109211
+ onKeyDown: interactive ? (e) => {
109212
+ if (e.key === "Enter" || e.key === " ") {
109213
+ e.preventDefault();
109214
+ onClick?.(e);
109215
+ }
109216
+ } : void 0,
109217
+ className: cn$2("inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium leading-none", meta.className, interactive && "cursor-pointer hover:brightness-125 focus:outline-none focus:ring-1 focus:ring-current", className),
109218
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "h-2.5 w-2.5 shrink-0" }), !iconOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: meta.label })]
109219
+ });
109220
+ }
109221
+ //#endregion
109166
109222
  //#region node_modules/@xterm/xterm/lib/xterm.mjs
109167
109223
  /**
109168
109224
  * Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
@@ -127362,16 +127418,8 @@ function parentPath$1(path) {
127362
127418
  const index = trimmed.lastIndexOf("/");
127363
127419
  return index > 0 ? trimmed.slice(0, index) : "/";
127364
127420
  }
127365
- function normalizePathForCompare(path) {
127366
- return path.replace(/\/+$/, "") || "/";
127367
- }
127368
- function pathWithin(path, baseDir) {
127369
- const normalizedPath = normalizePathForCompare(path);
127370
- const normalizedBase = normalizePathForCompare(baseDir);
127371
- return normalizedPath === normalizedBase || normalizedPath.startsWith(normalizedBase + "/");
127372
- }
127373
127421
  function orchestratorForFilePreview(orchestrators, path) {
127374
- return orchestrators.filter((orch) => orch.status === "online" && pathWithin(path, orch.baseDir)).sort((a, b) => normalizePathForCompare(b.baseDir).length - normalizePathForCompare(a.baseDir).length)[0] || orchestrators.find((orch) => orch.status === "online");
127422
+ return orchestrators.filter((orch) => orch.status === "online" && pathWithinBase(path, orch.baseDir)).sort((a, b) => normalizePathForCompare(b.baseDir).length - normalizePathForCompare(a.baseDir).length)[0] || orchestrators.find((orch) => orch.status === "online");
127375
127423
  }
127376
127424
  function supportsSideFilePreview() {
127377
127425
  return typeof window !== "undefined" && window.matchMedia("(min-width: 1280px)").matches;
@@ -127689,7 +127737,7 @@ function AgentListPanel({ threads, onSelectAgent }) {
127689
127737
  className: "flex items-center gap-1 shrink-0",
127690
127738
  children: [lastActivityAt > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
127691
127739
  className: "text-[10px] text-muted-foreground",
127692
- title: fmtTime$1(lastActivityAt),
127740
+ title: fmtTime(lastActivityAt),
127693
127741
  children: timeAgo(now, lastActivityAt)
127694
127742
  }), unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
127695
127743
  className: "bg-red-500 text-white text-xs min-w-[18px] h-[18px] flex items-center justify-center shrink-0 px-1",
@@ -128066,8 +128114,42 @@ function BusyIndicator({ blockedLabel, onInterrupt }) {
128066
128114
  })
128067
128115
  });
128068
128116
  }
128117
+ var HIDE_TOOLS_KEY = "agent-relay:chat-hide-tools";
128118
+ var hideToolsStore = (() => {
128119
+ const listeners = /* @__PURE__ */ new Set();
128120
+ let value = (() => {
128121
+ try {
128122
+ return window.localStorage.getItem(HIDE_TOOLS_KEY) === "on";
128123
+ } catch {
128124
+ return false;
128125
+ }
128126
+ })();
128127
+ const emit = () => listeners.forEach((l) => l());
128128
+ if (typeof window !== "undefined") window.addEventListener("storage", (e) => {
128129
+ if (e.key !== HIDE_TOOLS_KEY) return;
128130
+ value = e.newValue === "on";
128131
+ emit();
128132
+ });
128133
+ return {
128134
+ subscribe(cb) {
128135
+ listeners.add(cb);
128136
+ return () => listeners.delete(cb);
128137
+ },
128138
+ get: () => value,
128139
+ toggle() {
128140
+ value = !value;
128141
+ try {
128142
+ window.localStorage.setItem(HIDE_TOOLS_KEY, value ? "on" : "off");
128143
+ } catch {}
128144
+ emit();
128145
+ }
128146
+ };
128147
+ })();
128148
+ function useHideTools() {
128149
+ return [(0, import_react.useSyncExternalStore)(hideToolsStore.subscribe, hideToolsStore.get, () => false), hideToolsStore.toggle];
128150
+ }
128069
128151
  function ActivityTrace({ steps, showReasoning }) {
128070
- const [hideTools, setHideTools] = (0, import_react.useState)(false);
128152
+ const [hideTools, toggleHideTools] = useHideTools();
128071
128153
  const visible = showReasoning ? steps : steps.filter((s) => s.kind !== "reasoning");
128072
128154
  if (!visible.length) return null;
128073
128155
  const toolCount = visible.filter((s) => s.kind === "tool").length;
@@ -128102,7 +128184,7 @@ function ActivityTrace({ steps, showReasoning }) {
128102
128184
  })]
128103
128185
  }, step.id);
128104
128186
  }), toolCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
128105
- onClick: () => setHideTools((v) => !v),
128187
+ onClick: toggleHideTools,
128106
128188
  className: "flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left",
128107
128189
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: cn$2("w-3 h-3 shrink-0 transition-transform", !hideTools && "rotate-90") }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: hideTools ? `show ${toolCount} tool step${toolCount === 1 ? "" : "s"}` : "hide tool steps" })]
128108
128190
  })]
@@ -129170,6 +129252,8 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129170
129252
  const setReplyDraft = useRelayStore((s) => s.setReplyDraft);
129171
129253
  const exportThread = useRelayStore((s) => s.exportThread);
129172
129254
  const doAgentAction = useRelayStore((s) => s.doAgentAction);
129255
+ const workspaceAction = useRelayStore((s) => s.workspaceAction);
129256
+ const openWorkspaceFocus = useRelayStore((s) => s.openWorkspaceFocus);
129173
129257
  const forkFromAgent = useRelayStore((s) => s.forkFromAgent);
129174
129258
  const chatHistoryImports = useRelayStore((s) => s.chatHistoryImports);
129175
129259
  const peerHistory = useRelayStore((s) => s.threadHistory[s.selectedInboxThread] ?? NO_THREAD_HISTORY);
@@ -129518,7 +129602,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129518
129602
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
129519
129603
  className: "flex h-full min-w-0",
129520
129604
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
129521
- className: "flex h-full min-w-0 flex-1 flex-col",
129605
+ className: "@container/chat flex h-full min-w-0 flex-1 flex-col",
129522
129606
  children: [
129523
129607
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
129524
129608
  className: "flex items-center justify-between px-3 md:px-4 py-2.5 md:py-3 border-b border-border shrink-0",
@@ -129562,11 +129646,16 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129562
129646
  agent && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentRuntimeChips, {
129563
129647
  agent,
129564
129648
  compact: true,
129565
- className: "hidden md:flex shrink-0"
129649
+ className: "hidden @4xl/chat:flex shrink-0"
129566
129650
  }),
129567
129651
  agent?.context && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextRing, { utilization: agent.context.utilization }),
129652
+ agent?.branchState && agent.branchWorkspaceId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BranchStateBadge, {
129653
+ state: agent.branchState,
129654
+ className: "shrink-0",
129655
+ onClick: () => void openWorkspaceFocus(agent.branchWorkspaceId)
129656
+ }),
129568
129657
  thread?.attention.unread ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge$1, {
129569
- className: "bg-red-500/20 text-red-400 text-xs shrink-0 hidden sm:flex",
129658
+ className: "bg-red-500/20 text-red-400 text-xs shrink-0 hidden @2xl/chat:flex",
129570
129659
  children: [thread.attention.unread, " unread"]
129571
129660
  }) : null
129572
129661
  ]
@@ -129589,7 +129678,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129589
129678
  canOpenTerminal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129590
129679
  variant: "ghost",
129591
129680
  size: "icon-sm",
129592
- className: "hidden sm:inline-flex",
129681
+ className: "hidden @4xl/chat:inline-flex",
129593
129682
  disabled: terminalOpening,
129594
129683
  title: "Terminal",
129595
129684
  onClick: () => void handleOpenTerminal(),
@@ -129603,10 +129692,18 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129603
129692
  onClick: interruptAgent,
129604
129693
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleStop, { className: "w-3.5 h-3.5" })
129605
129694
  }),
129695
+ agent.branchState === "changes" && agent.branchWorkspaceId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129696
+ variant: "ghost",
129697
+ size: "icon-sm",
129698
+ className: "hidden @4xl/chat:inline-flex text-amber-400 hover:text-amber-300",
129699
+ title: "Mark workspace ready — hand off to the auto-merge",
129700
+ onClick: () => openConfirm("Mark Workspace Ready", `Mark ${displayName(agent)}'s branch ready to land? The relay auto-merge will rebase and land it.`, () => workspaceAction(agent.branchWorkspaceId, "ready")),
129701
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Flag, { className: "w-3.5 h-3.5" })
129702
+ }),
129606
129703
  canCompact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129607
129704
  variant: "ghost",
129608
129705
  size: "icon-sm",
129609
- className: "hidden sm:inline-flex",
129706
+ className: "hidden @4xl/chat:inline-flex",
129610
129707
  title: "Compact",
129611
129708
  onClick: () => openConfirm("Compact Agent", `Compact context for ${displayName(agent)}?`, () => doAgentAction(agent, "compact")),
129612
129709
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Minimize2, { className: "w-3.5 h-3.5" })
@@ -129614,7 +129711,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129614
129711
  canClearContext && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129615
129712
  variant: "ghost",
129616
129713
  size: "icon-sm",
129617
- className: "hidden sm:inline-flex",
129714
+ className: "hidden @4xl/chat:inline-flex",
129618
129715
  title: "Clear context",
129619
129716
  onClick: () => openConfirm("Clear Context", `Clear context for ${displayName(agent)}?`, () => doAgentAction(agent, "clearContext")),
129620
129717
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Eraser, { className: "w-3.5 h-3.5" })
@@ -129622,7 +129719,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129622
129719
  canShutdown && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129623
129720
  variant: "ghost",
129624
129721
  size: "icon-sm",
129625
- className: "hidden sm:inline-flex",
129722
+ className: "hidden @4xl/chat:inline-flex",
129626
129723
  title: "Shutdown",
129627
129724
  onClick: () => openConfirm("Shutdown Agent", `Shutdown ${displayName(agent)}?`, () => doAgentAction(agent, "shutdown")),
129628
129725
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Power, { className: "w-3.5 h-3.5" })
@@ -129630,7 +129727,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129630
129727
  agent && typeof agent.meta?.cwd === "string" && agent.meta.cwd && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129631
129728
  variant: "ghost",
129632
129729
  size: "icon-sm",
129633
- className: "hidden sm:inline-flex",
129730
+ className: "hidden @4xl/chat:inline-flex",
129634
129731
  title: `Open ${agent.meta.cwd} in Files`,
129635
129732
  onClick: () => void openFilesForAgent(agent),
129636
129733
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderOpen, { className: "w-3.5 h-3.5" })
@@ -129638,7 +129735,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129638
129735
  agent && thread && thread.messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129639
129736
  variant: "ghost",
129640
129737
  size: "icon-sm",
129641
- className: "hidden sm:inline-flex",
129738
+ className: "hidden @4xl/chat:inline-flex",
129642
129739
  title: "Fork — spawn new agent with this chat history",
129643
129740
  onClick: () => forkFromAgent(agent.id, thread.messages),
129644
129741
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitFork, { className: "w-3.5 h-3.5" })
@@ -129646,7 +129743,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129646
129743
  thread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129647
129744
  variant: "ghost",
129648
129745
  size: "icon-sm",
129649
- className: "hidden sm:inline-flex",
129746
+ className: "hidden @4xl/chat:inline-flex",
129650
129747
  title: "Mark unread",
129651
129748
  onClick: () => markInboxThreadUnread(selectedInboxThread),
129652
129749
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MailX, { className: "w-3.5 h-3.5" })
@@ -129654,7 +129751,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129654
129751
  thread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129655
129752
  variant: "ghost",
129656
129753
  size: "icon-sm",
129657
- className: "hidden sm:inline-flex",
129754
+ className: "hidden @4xl/chat:inline-flex",
129658
129755
  title: "Mark read",
129659
129756
  onClick: () => markInboxThreadRead(thread.peer, thread.messages),
129660
129757
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MailOpen, { className: "w-3.5 h-3.5" })
@@ -129662,7 +129759,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129662
129759
  exportableThread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129663
129760
  variant: "ghost",
129664
129761
  size: "icon-sm",
129665
- className: "hidden sm:inline-flex",
129762
+ className: "hidden @4xl/chat:inline-flex",
129666
129763
  title: "Export thread",
129667
129764
  onClick: () => exportThread({
129668
129765
  ...exportableThread,
@@ -129675,7 +129772,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129675
129772
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129676
129773
  variant: "ghost",
129677
129774
  size: "icon-sm",
129678
- className: "sm:hidden",
129775
+ className: "@4xl/chat:hidden",
129679
129776
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ellipsis, { className: "w-3.5 h-3.5" })
129680
129777
  })
129681
129778
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuContent, {
@@ -129690,6 +129787,10 @@ function ChatPanel({ threads, onBack, showBackButton }) {
129690
129787
  onClick: interruptAgent,
129691
129788
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleStop, { className: "w-3.5 h-3.5" }), "Interrupt"]
129692
129789
  }),
129790
+ agent.branchState === "changes" && agent.branchWorkspaceId && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
129791
+ onClick: () => openConfirm("Mark Workspace Ready", `Mark ${displayName(agent)}'s branch ready to land? The relay auto-merge will rebase and land it.`, () => workspaceAction(agent.branchWorkspaceId, "ready")),
129792
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Flag, { className: "w-3.5 h-3.5" }), "Mark Ready"]
129793
+ }),
129693
129794
  canCompact && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
129694
129795
  onClick: () => openConfirm("Compact Agent", `Compact context for ${displayName(agent)}?`, () => doAgentAction(agent, "compact")),
129695
129796
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Minimize2, { className: "w-3.5 h-3.5" }), "Compact"]
@@ -130067,6 +130168,8 @@ function AgentCard({ agent }) {
130067
130168
  const doAgentAction = useRelayStore((s) => s.doAgentAction);
130068
130169
  const doDeleteAgent = useRelayStore((s) => s.doDeleteAgent);
130069
130170
  const openFilesForAgent = useRelayStore((s) => s.openFilesForAgent);
130171
+ const workspaceAction = useRelayStore((s) => s.workspaceAction);
130172
+ const openWorkspaceFocus = useRelayStore((s) => s.openWorkspaceFocus);
130070
130173
  const { terminalOpen, terminalTarget, terminalOpening, openTerminal: handleOpenTerminal, closeTerminal: handleCloseTerminal } = useAgentTerminal(agent.id);
130071
130174
  const pairsMap = usePairsByAgentId();
130072
130175
  const rawAttention = useAgentAttention(agent);
@@ -130118,6 +130221,14 @@ function AgentCard({ agent }) {
130118
130221
  children: shortPath$1(agent.meta.cwd)
130119
130222
  })
130120
130223
  ]
130224
+ }),
130225
+ agent.branchState && agent.branchWorkspaceId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BranchStateBadge, {
130226
+ state: agent.branchState,
130227
+ className: "mt-0.5 shrink-0",
130228
+ onClick: (e) => {
130229
+ e.stopPropagation();
130230
+ openWorkspaceFocus(agent.branchWorkspaceId);
130231
+ }
130121
130232
  })
130122
130233
  ]
130123
130234
  }),
@@ -130202,6 +130313,14 @@ function AgentCard({ agent }) {
130202
130313
  onClick: () => void handleOpenTerminal(),
130203
130314
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Terminal, { className: "w-3 h-3" })
130204
130315
  }),
130316
+ agent.branchState === "changes" && agent.branchWorkspaceId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
130317
+ size: "icon",
130318
+ variant: "ghost",
130319
+ className: "h-7 w-7 text-amber-400 hover:text-amber-300",
130320
+ title: "Mark workspace ready — hand off to the auto-merge",
130321
+ onClick: () => openConfirm("Mark Workspace Ready", `Mark ${displayName(agent)}'s branch ready to land? The relay auto-merge will rebase and land it.`, () => workspaceAction(agent.branchWorkspaceId, "ready")),
130322
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Flag, { className: "w-3 h-3" })
130323
+ }),
130205
130324
  canCompact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
130206
130325
  size: "icon",
130207
130326
  variant: "ghost",
@@ -130538,10 +130657,6 @@ function LogViewer({ orchestratorId, session, lines = 200 }) {
130538
130657
  }
130539
130658
  //#endregion
130540
130659
  //#region src/components/views/managed-agents.tsx
130541
- function fmtTime(value) {
130542
- if (!value) return "-";
130543
- return new Date(value).toLocaleString();
130544
- }
130545
130660
  function terminalSessionFor(item, agent) {
130546
130661
  const agentSession = agent?.meta?.tmuxSession;
130547
130662
  if (typeof agentSession === "string" && agentSession.trim()) return agentSession;
@@ -131823,11 +131938,20 @@ function WorkspaceRow({ workspace }) {
131823
131938
  const now = useNow();
131824
131939
  const [expanded, setExpanded] = (0, import_react.useState)(false);
131825
131940
  const agentsById = useRelayStore((s) => s.agentsById);
131941
+ const focused = useRelayStore((s) => s.workspaceFocusId === workspace.id);
131826
131942
  const owner = workspace.ownerAgentId ? agentsById[workspace.ownerAgentId] : void 0;
131827
131943
  const steward = workspace.stewardAgentId ? agentsById[workspace.stewardAgentId] : void 0;
131828
131944
  const path = workspace.worktreePath || workspace.sourceCwd || workspace.repoRoot;
131945
+ const rowRef = (0, import_react.useRef)(null);
131946
+ (0, import_react.useEffect)(() => {
131947
+ if (focused) rowRef.current?.scrollIntoView({
131948
+ behavior: "smooth",
131949
+ block: "center"
131950
+ });
131951
+ }, [focused]);
131829
131952
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
131830
- className: "border-t border-border",
131953
+ ref: rowRef,
131954
+ className: `border-t border-border ${focused ? "bg-primary/5 ring-1 ring-inset ring-primary/40" : ""}`,
131831
131955
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
131832
131956
  className: "grid gap-3 px-3 py-3 text-sm lg:grid-cols-[minmax(180px,1.2fr)_minmax(220px,1.5fr)_minmax(160px,1fr)_minmax(170px,auto)]",
131833
131957
  children: [
@@ -131972,6 +132096,14 @@ function RepoGroup({ repoRoot, workspaces }) {
131972
132096
  function WorkspacesView() {
131973
132097
  const [filter, setFilter] = (0, import_react.useState)("active");
131974
132098
  const workspaces = useRelayStore((s) => s.workspaces);
132099
+ const set = useRelayStore((s) => s.set);
132100
+ const workspaceFocusId = useRelayStore((s) => s.workspaceFocusId);
132101
+ (0, import_react.useEffect)(() => {
132102
+ if (workspaceFocusId) setFilter("all");
132103
+ }, [workspaceFocusId]);
132104
+ (0, import_react.useEffect)(() => () => {
132105
+ set({ workspaceFocusId: null });
132106
+ }, [set]);
131975
132107
  const visibleWorkspaces = (0, import_react.useMemo)(() => filterWorkspaces(workspaces, filter), [workspaces, filter]);
131976
132108
  const grouped = (0, import_react.useMemo)(() => groupWorkspaces(visibleWorkspaces), [visibleWorkspaces]);
131977
132109
  const liveCount = workspaces.filter((item) => LIVE_STATUSES.has(item.status)).length;
@@ -133581,7 +133713,7 @@ function MemoryView() {
133581
133713
  }),
133582
133714
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
133583
133715
  className: "text-xs text-muted-foreground",
133584
- title: fmtTime$1(selectedMemory.updatedAt),
133716
+ title: fmtTime(selectedMemory.updatedAt),
133585
133717
  children: ["updated ", timeAgo(now, selectedMemory.updatedAt)]
133586
133718
  }),
133587
133719
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
@@ -133772,7 +133904,7 @@ function MemoryRow({ memory, selected, now, onSelect }) {
133772
133904
  }),
133773
133905
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
133774
133906
  className: "text-[10px] text-muted-foreground",
133775
- title: fmtTime$1(memory.updatedAt),
133907
+ title: fmtTime(memory.updatedAt),
133776
133908
  children: timeAgo(now, memory.updatedAt)
133777
133909
  }),
133778
133910
  memory.accessCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -133922,7 +134054,7 @@ function ActivityView() {
133922
134054
  }),
133923
134055
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
133924
134056
  className: "text-xs text-muted-foreground ml-auto shrink-0",
133925
- title: fmtTime$1(item.ts ?? item.createdAt),
134057
+ title: fmtTime(item.ts ?? item.createdAt),
133926
134058
  children: timeAgo(now, item.ts ?? item.createdAt)
133927
134059
  })
133928
134060
  ]
@@ -134085,7 +134217,7 @@ function PairCard({ pair, now, agentName, onAccept, onReject, onMessage, onHangu
134085
134217
  children: pair.id.slice(-10)
134086
134218
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
134087
134219
  className: "text-xs text-muted-foreground",
134088
- title: fmtTime$1(pair.updatedAt),
134220
+ title: fmtTime(pair.updatedAt),
134089
134221
  children: timeAgo(now, pair.updatedAt)
134090
134222
  })]
134091
134223
  })]
@@ -134378,7 +134510,7 @@ function MessageRow({ msg, now, agentLabel, agentsById, onReply, onClaim, onRetr
134378
134510
  }),
134379
134511
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
134380
134512
  className: "ml-auto shrink-0",
134381
- title: fmtTime$1(msg.createdAt),
134513
+ title: fmtTime(msg.createdAt),
134382
134514
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
134383
134515
  className: "opacity-40 mr-1",
134384
134516
  children: ["#", msg.id]
@@ -134788,7 +134920,7 @@ function TaskCard({ task, now, agentName, onClaim, onEvents, onStatusChange }) {
134788
134920
  })]
134789
134921
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
134790
134922
  className: "text-xs text-muted-foreground shrink-0",
134791
- title: fmtTime$1(task.createdAt),
134923
+ title: fmtTime(task.createdAt),
134792
134924
  children: ["#", task.id]
134793
134925
  })]
134794
134926
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
@@ -134832,7 +134964,7 @@ function TaskCard({ task, now, agentName, onClaim, onEvents, onStatusChange }) {
134832
134964
  }),
134833
134965
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
134834
134966
  className: "flex items-center gap-1 mt-0.5",
134835
- title: fmtTime$1(task.updatedAt),
134967
+ title: fmtTime(task.updatedAt),
134836
134968
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
134837
134969
  className: "text-muted-foreground/60",
134838
134970
  children: "updated"
@@ -135415,7 +135547,7 @@ function AutomationCard({ automation, runs, now, selected, onEdit, onRun, onTogg
135415
135547
  className: "flex flex-wrap items-center gap-2 text-xs text-muted-foreground",
135416
135548
  children: [
135417
135549
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
135418
- title: automation.nextRunAt ? fmtTime$1(automation.nextRunAt) : void 0,
135550
+ title: automation.nextRunAt ? fmtTime(automation.nextRunAt) : void 0,
135419
135551
  children: ["next ", automation.nextRunAt ? timeAgo(now, automation.nextRunAt) : "disabled"]
135420
135552
  }),
135421
135553
  latestRun && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
@@ -135927,7 +136059,7 @@ function RunHistory({ runs, automations, agents, orchestrators, now, logRunId, o
135927
136059
  }),
135928
136060
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
135929
136061
  className: "text-muted-foreground",
135930
- title: fmtTime$1(run.updatedAt),
136062
+ title: fmtTime(run.updatedAt),
135931
136063
  children: timeAgo(now, run.updatedAt)
135932
136064
  }),
135933
136065
  logTarget ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
@@ -155705,14 +155837,14 @@ function MaintenanceCard({ job, now, onRun }) {
155705
155837
  className: "text-muted-foreground",
155706
155838
  children: "Last run"
155707
155839
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: job.lastRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155708
- title: fmtTime$1(job.lastRunAt),
155840
+ title: fmtTime(job.lastRunAt),
155709
155841
  children: timeAgo(now, job.lastRunAt)
155710
155842
  }) : "never" })] }),
155711
155843
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
155712
155844
  className: "text-muted-foreground",
155713
155845
  children: "Next run"
155714
155846
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: job.nextRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155715
- title: fmtTime$1(job.nextRunAt),
155847
+ title: fmtTime(job.nextRunAt),
155716
155848
  children: nextRunText(now, job.nextRunAt)
155717
155849
  }) : "-" })] }),
155718
155850
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
@@ -155783,14 +155915,14 @@ function MaintenanceRow({ job, now, onRun }) {
155783
155915
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
155784
155916
  className: "px-3 py-3 text-xs text-muted-foreground",
155785
155917
  children: job.lastRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155786
- title: fmtTime$1(job.lastRunAt),
155918
+ title: fmtTime(job.lastRunAt),
155787
155919
  children: timeAgo(now, job.lastRunAt)
155788
155920
  }) : "never"
155789
155921
  }),
155790
155922
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
155791
155923
  className: "px-3 py-3 text-xs text-muted-foreground",
155792
155924
  children: job.nextRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155793
- title: fmtTime$1(job.nextRunAt),
155925
+ title: fmtTime(job.nextRunAt),
155794
155926
  children: nextRunText(now, job.nextRunAt)
155795
155927
  }) : "-"
155796
155928
  }),
@@ -156927,7 +157059,7 @@ function AgentDiagnostics({ agent, orchestrators }) {
156927
157059
  }),
156928
157060
  managedAgent.startedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KV, {
156929
157061
  label: "Started",
156930
- value: fmtTime$1(managedAgent.startedAt)
157062
+ value: fmtTime(managedAgent.startedAt)
156931
157063
  }),
156932
157064
  managedOrch && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KV, {
156933
157065
  label: "Orchestrator",
@@ -157602,7 +157734,7 @@ function AgentDetailDrawer() {
157602
157734
  children: "Created"
157603
157735
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
157604
157736
  className: "truncate",
157605
- children: fmtTime$1(agent.createdAt)
157737
+ children: fmtTime(agent.createdAt)
157606
157738
  })]
157607
157739
  }),
157608
157740
  agent.machine && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -158064,7 +158196,7 @@ function ActiveMemoryRow({ memory, now, onOpen }) {
158064
158196
  ]
158065
158197
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
158066
158198
  className: "shrink-0 text-right text-[10px] text-muted-foreground",
158067
- title: fmtTime$1(memory.lastAccessedAt ?? memory.updatedAt),
158199
+ title: fmtTime(memory.lastAccessedAt ?? memory.updatedAt),
158068
158200
  children: timeAgo(now, memory.lastAccessedAt ?? memory.updatedAt)
158069
158201
  })]
158070
158202
  })
@@ -158190,7 +158322,7 @@ function TimelineRow({ entry, now }) {
158190
158322
  children: entry.title
158191
158323
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
158192
158324
  className: "ml-auto shrink-0 text-[10px] text-muted-foreground",
158193
- title: fmtTime$1(entry.ts),
158325
+ title: fmtTime(entry.ts),
158194
158326
  children: timeAgo(now, entry.ts)
158195
158327
  })]
158196
158328
  }),
@@ -158863,7 +158995,7 @@ function TaskEventsModal() {
158863
158995
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
158864
158996
  className: "text-xs text-muted-foreground mt-1",
158865
158997
  children: [
158866
- fmtTime$1(evt.createdAt),
158998
+ fmtTime(evt.createdAt),
158867
158999
  " · ",
158868
159000
  evt.source
158869
159001
  ]
@@ -161073,6 +161205,10 @@ if ("serviceWorker" in navigator) {
161073
161205
  container: card-header / inline-size;
161074
161206
  }
161075
161207
 
161208
+ .\@container\/chat {
161209
+ container: chat / inline-size;
161210
+ }
161211
+
161076
161212
  .pointer-events-none {
161077
161213
  pointer-events: none;
161078
161214
  }
@@ -164650,6 +164786,10 @@ if ("serviceWorker" in navigator) {
164650
164786
  animation-play-state: paused;
164651
164787
  }
164652
164788
 
164789
+ .ring-inset {
164790
+ --tw-ring-inset: inset;
164791
+ }
164792
+
164653
164793
  .running {
164654
164794
  animation-play-state: running;
164655
164795
  }
@@ -164977,6 +165117,10 @@ if ("serviceWorker" in navigator) {
164977
165117
  }
164978
165118
  }
164979
165119
 
165120
+ .hover\:text-amber-300:hover {
165121
+ color: var(--color-amber-300);
165122
+ }
165123
+
164980
165124
  .hover\:text-blue-300:hover {
164981
165125
  color: var(--color-blue-300);
164982
165126
  }
@@ -165032,13 +165176,36 @@ if ("serviceWorker" in navigator) {
165032
165176
  .hover\:opacity-100:hover {
165033
165177
  opacity: 1;
165034
165178
  }
165179
+
165180
+ .hover\:brightness-125:hover {
165181
+ --tw-brightness: brightness(125%);
165182
+ filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, ) var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, ) var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, );
165183
+ }
165035
165184
  }
165036
165185
 
165037
165186
  .focus\:bg-accent:focus {
165038
165187
  background-color: var(--accent);
165039
165188
  }
165040
165189
 
165041
- .focus\:text-accent-foreground:focus, :is(.focus\:\*\*\:text-accent-foreground:focus *), :is(.not-data-\[variant\=destructive\]\:focus\:\*\*\:text-accent-foreground:not([data-variant="destructive"]):focus *) {
165190
+ .focus\:text-accent-foreground:focus {
165191
+ color: var(--accent-foreground);
165192
+ }
165193
+
165194
+ .focus\:ring-1:focus {
165195
+ --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
165196
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
165197
+ }
165198
+
165199
+ .focus\:ring-current:focus {
165200
+ --tw-ring-color: currentcolor;
165201
+ }
165202
+
165203
+ .focus\:outline-none:focus {
165204
+ --tw-outline-style: none;
165205
+ outline-style: none;
165206
+ }
165207
+
165208
+ :is(.focus\:\*\*\:text-accent-foreground:focus *), :is(.not-data-\[variant\=destructive\]\:focus\:\*\*\:text-accent-foreground:not([data-variant="destructive"]):focus *) {
165042
165209
  color: var(--accent-foreground);
165043
165210
  }
165044
165211
 
@@ -165523,10 +165690,6 @@ if ("serviceWorker" in navigator) {
165523
165690
  display: block;
165524
165691
  }
165525
165692
 
165526
- .sm\:flex {
165527
- display: flex;
165528
- }
165529
-
165530
165693
  .sm\:hidden {
165531
165694
  display: none;
165532
165695
  }
@@ -165834,6 +165997,26 @@ if ("serviceWorker" in navigator) {
165834
165997
  }
165835
165998
  }
165836
165999
 
166000
+ @container chat (min-width: 42rem) {
166001
+ .\@2xl\/chat\:flex {
166002
+ display: flex;
166003
+ }
166004
+ }
166005
+
166006
+ @container chat (min-width: 56rem) {
166007
+ .\@4xl\/chat\:flex {
166008
+ display: flex;
166009
+ }
166010
+
166011
+ .\@4xl\/chat\:hidden {
166012
+ display: none;
166013
+ }
166014
+
166015
+ .\@4xl\/chat\:inline-flex {
166016
+ display: inline-flex;
166017
+ }
166018
+ }
166019
+
165837
166020
  .dark\:border-input:is(.dark *) {
165838
166021
  border-color: var(--input);
165839
166022
  }