agent-relay-server 0.21.0 → 0.22.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/docs/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Relay API",
5
- "version": "0.19.3",
5
+ "version": "0.21.0",
6
6
  "description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
7
7
  "license": {
8
8
  "name": "MIT",
@@ -796,6 +796,9 @@
796
796
  },
797
797
  "workspaceMode": {
798
798
  "type": "string"
799
+ },
800
+ "profile": {
801
+ "type": "string"
799
802
  }
800
803
  }
801
804
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "CONTRIBUTING.md"
34
34
  ],
35
35
  "dependencies": {
36
- "agent-relay-sdk": "0.2.12"
36
+ "agent-relay-sdk": "0.2.13"
37
37
  },
38
38
  "scripts": {
39
39
  "prepack": "bun run build:dashboard:bundle >&2",
package/public/index.html CHANGED
@@ -12099,9 +12099,11 @@ function isDashboardHidden() {
12099
12099
  function notificationPeer(notification) {
12100
12100
  return notification.threadPeer || notification.agentId || "";
12101
12101
  }
12102
+ function isActiveVisibleChat(peer, state) {
12103
+ return Boolean(peer && state.view === "chat" && state.selectedInboxThread === peer && !isDashboardHidden());
12104
+ }
12102
12105
  function notificationTargetsActiveChat(notification, state) {
12103
- const peer = notificationPeer(notification);
12104
- return Boolean(state.view === "chat" && peer && state.selectedInboxThread === peer && !isDashboardHidden());
12106
+ return isActiveVisibleChat(notificationPeer(notification), state);
12105
12107
  }
12106
12108
  function lastInboundMessageId(messages) {
12107
12109
  return messages.filter((m) => m.to === "user" && m.from !== "user").reduce((max, m) => Math.max(max, m.id), 0);
@@ -12835,7 +12837,15 @@ var useRelayStore = create$1()(persist((set, get) => ({
12835
12837
  if (s.view === "messages" && s.selectedAgent) path += "&for=" + encodeURIComponent(s.selectedAgent);
12836
12838
  if (s.view === "messages" && s.channelFilter) path += "&channel=" + encodeURIComponent(s.channelFilter);
12837
12839
  const messages = await api("GET", path);
12838
- set({ messages: mergeFetchedMessages(get().messages, messages) });
12840
+ const merged = mergeFetchedMessages(get().messages, messages);
12841
+ set({ messages: merged });
12842
+ const after = get();
12843
+ const peer = after.selectedInboxThread;
12844
+ if (isActiveVisibleChat(peer, after)) {
12845
+ let lastId = 0;
12846
+ for (const m of merged) if (m.id > lastId && inboxPeer(m) === peer && isHumanInboundMessage(m) && !isSessionActivityStep(m)) lastId = m.id;
12847
+ if (lastId) get().markInboxThreadReadTo(peer, lastId);
12848
+ }
12839
12849
  } catch {}
12840
12850
  },
12841
12851
  async fetchThreadHistory(peer) {
@@ -12936,7 +12946,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
12936
12946
  if (msgs.length > 500) msgs.splice(0, msgs.length - 500);
12937
12947
  set({ messages: msgs });
12938
12948
  const peer = inboxPeer(msg);
12939
- if (isHumanInboundMessage(msg) && peer && s.view === "chat" && s.selectedInboxThread === peer && !isDashboardHidden()) get().markInboxThreadReadTo(peer, msg.id);
12949
+ if (isHumanInboundMessage(msg) && isActiveVisibleChat(peer, s)) get().markInboxThreadReadTo(peer, msg.id);
12940
12950
  return;
12941
12951
  }
12942
12952
  if (event === "message.queued" || event === "message.expired" || event === "message.delivery_updated" || event === "message.reaction_updated") {
@@ -99801,6 +99811,45 @@ function Button({ className, variant = "default", size = "default", asChild = fa
99801
99811
  });
99802
99812
  }
99803
99813
  //#endregion
99814
+ //#region src/components/shared/copy-button.tsx
99815
+ /**
99816
+ * Shared copy-to-clipboard button with a transient "copied" check state.
99817
+ * Consolidates the duplicated clipboard + timeout pattern used across views.
99818
+ */
99819
+ function CopyButton({ value, label = "Copy", copiedLabel = "Copied", showText = false, size, variant = "ghost", className, iconClassName, disabled, onCopied }) {
99820
+ const [copied, setCopied] = (0, import_react.useState)(false);
99821
+ const timer = (0, import_react.useRef)(null);
99822
+ (0, import_react.useEffect)(() => () => {
99823
+ if (timer.current) clearTimeout(timer.current);
99824
+ }, []);
99825
+ async function copy(e) {
99826
+ e.preventDefault();
99827
+ e.stopPropagation();
99828
+ try {
99829
+ await navigator.clipboard?.writeText(typeof value === "function" ? value() : value);
99830
+ setCopied(true);
99831
+ onCopied?.();
99832
+ if (timer.current) clearTimeout(timer.current);
99833
+ timer.current = setTimeout(() => setCopied(false), 1400);
99834
+ } catch {
99835
+ setCopied(false);
99836
+ }
99837
+ }
99838
+ const resolvedSize = size ?? (showText ? "sm" : "icon-sm");
99839
+ const iconCls = cn$2("h-3.5 w-3.5", iconClassName);
99840
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
99841
+ type: "button",
99842
+ size: resolvedSize,
99843
+ variant,
99844
+ className,
99845
+ disabled,
99846
+ title: copied ? copiedLabel : label,
99847
+ "aria-label": copied ? copiedLabel : label,
99848
+ onClick: (e) => void copy(e),
99849
+ children: [copied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: iconCls }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: iconCls }), showText && (copied ? copiedLabel : label)]
99850
+ });
99851
+ }
99852
+ //#endregion
99804
99853
  //#region \0vite/preload-helper.js
99805
99854
  var scriptRel, assetsURL, seen, __vitePreload;
99806
99855
  var init_preload_helper = __esmMin((() => {
@@ -108465,25 +108514,14 @@ var CodePreview = (0, import_react.memo)(function CodePreview({ content, path, m
108465
108514
  const [html, setHtml] = (0, import_react.useState)("");
108466
108515
  const [loading, setLoading] = (0, import_react.useState)(false);
108467
108516
  const [failed, setFailed] = (0, import_react.useState)(false);
108468
- const [copied, setCopied] = (0, import_react.useState)(false);
108469
- async function copyCode() {
108470
- try {
108471
- await navigator.clipboard?.writeText(content);
108472
- setCopied(true);
108473
- window.setTimeout(() => setCopied(false), 1400);
108474
- } catch {
108475
- setCopied(false);
108476
- }
108477
- }
108478
108517
  function copyButton() {
108479
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
108480
- type: "button",
108518
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
108519
+ value: content,
108520
+ label: "Copy code",
108521
+ copiedLabel: "Copied code",
108481
108522
  size: "icon",
108482
108523
  variant: "ghost",
108483
- className: "absolute right-2 top-2 h-7 w-7 bg-background/80 opacity-0 shadow-sm backdrop-blur transition-opacity hover:bg-muted group-hover/code:opacity-100 focus-visible:opacity-100",
108484
- onClick: copyCode,
108485
- title: copied ? "Copied code" : "Copy code",
108486
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "h-3.5 w-3.5" })
108524
+ className: "absolute right-2 top-2 h-7 w-7 bg-background/80 opacity-0 shadow-sm backdrop-blur transition-opacity hover:bg-muted group-hover/code:opacity-100 focus-visible:opacity-100"
108487
108525
  });
108488
108526
  }
108489
108527
  (0, import_react.useEffect)(() => {
@@ -124794,7 +124832,6 @@ function FileContent({ orchestratorId, selectedPath, line, onReadError }) {
124794
124832
  const { file, loading, error } = useFileRead(orchestratorId, selectedPath);
124795
124833
  const lineRef = (0, import_react.useRef)(null);
124796
124834
  const [mode, setMode] = (0, import_react.useState)("raw");
124797
- const [copiedPath, setCopiedPath] = (0, import_react.useState)(false);
124798
124835
  (0, import_react.useEffect)(() => {
124799
124836
  onReadError(error);
124800
124837
  }, [error, onReadError]);
@@ -124816,15 +124853,6 @@ function FileContent({ orchestratorId, selectedPath, line, onReadError }) {
124816
124853
  file?.content,
124817
124854
  line
124818
124855
  ]);
124819
- async function copyPath(path) {
124820
- try {
124821
- await navigator.clipboard?.writeText(path);
124822
- setCopiedPath(true);
124823
- window.setTimeout(() => setCopiedPath(false), 1400);
124824
- } catch {
124825
- setCopiedPath(false);
124826
- }
124827
- }
124828
124856
  function selectMode(nextMode, kind) {
124829
124857
  setMode(nextMode);
124830
124858
  if (kind) writeModePreference(kind, nextMode);
@@ -124849,14 +124877,13 @@ function FileContent({ orchestratorId, selectedPath, line, onReadError }) {
124849
124877
  className: "min-w-0 flex-1 truncate font-mono",
124850
124878
  children: file.path
124851
124879
  }),
124852
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
124853
- type: "button",
124880
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
124881
+ value: file.path,
124882
+ label: "Copy path",
124883
+ copiedLabel: "Copied path",
124854
124884
  size: "icon",
124855
124885
  variant: "ghost",
124856
- className: "h-7 w-7 shrink-0",
124857
- onClick: () => copyPath(file.path),
124858
- title: copiedPath ? "Copied path" : "Copy path",
124859
- children: copiedPath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "h-3.5 w-3.5" })
124886
+ className: "h-7 w-7 shrink-0"
124860
124887
  }),
124861
124888
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
124862
124889
  variant: "outline",
@@ -124913,14 +124940,13 @@ function FileContent({ orchestratorId, selectedPath, line, onReadError }) {
124913
124940
  className: "min-w-0 flex-1 truncate font-mono",
124914
124941
  children: file.path
124915
124942
  }),
124916
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
124917
- type: "button",
124943
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
124944
+ value: file.path,
124945
+ label: "Copy path",
124946
+ copiedLabel: "Copied path",
124918
124947
  size: "icon",
124919
124948
  variant: "ghost",
124920
- className: "h-7 w-7 shrink-0",
124921
- onClick: () => copyPath(file.path),
124922
- title: copiedPath ? "Copied path" : "Copy path",
124923
- children: copiedPath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "h-3.5 w-3.5" })
124949
+ className: "h-7 w-7 shrink-0"
124924
124950
  }),
124925
124951
  file.truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
124926
124952
  variant: "secondary",
@@ -127549,6 +127575,11 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127549
127575
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
127550
127576
  className: "flex items-center gap-0.5 md:gap-1 shrink-0",
127551
127577
  children: agent && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
127578
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
127579
+ value: agent.id,
127580
+ label: "Copy agent ID",
127581
+ size: "icon-sm"
127582
+ }),
127552
127583
  voiceTts.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
127553
127584
  variant: "ghost",
127554
127585
  size: "icon-sm",
@@ -128193,6 +128224,13 @@ function AgentCard({ agent }) {
128193
128224
  className: "flex gap-1 mt-2.5 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity",
128194
128225
  onClick: (e) => e.stopPropagation(),
128195
128226
  children: [
128227
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
128228
+ value: agent.id,
128229
+ label: "Copy agent ID",
128230
+ size: "icon",
128231
+ className: "h-7 w-7",
128232
+ iconClassName: "w-3 h-3"
128233
+ }),
128196
128234
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
128197
128235
  size: "icon",
128198
128236
  variant: "ghost",
@@ -129392,9 +129430,6 @@ function WorkspaceActions({ workspace, expanded, onToggleDetails }) {
129392
129430
  const gitState = useRelayStore((s) => s.workspaceGitState[workspace.id]);
129393
129431
  const landed = !!gitState && gitState.available !== false && gitState.landed === true;
129394
129432
  const mergeable = workspace.mode === "isolated" && Boolean(workspace.worktreePath) && MERGEABLE_STATUSES.has(workspace.status) && !landed;
129395
- async function copyPath() {
129396
- await navigator.clipboard?.writeText(openPath);
129397
- }
129398
129433
  async function merge() {
129399
129434
  await fetchWorkspaceMergePreview(workspace.id);
129400
129435
  await workspaceAction(workspace.id, "merge");
@@ -129417,13 +129452,13 @@ function WorkspaceActions({ workspace, expanded, onToggleDetails }) {
129417
129452
  onClick: () => void openFilesAt({ path: openPath }),
129418
129453
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderOpen, { className: "h-3.5 w-3.5" })
129419
129454
  }),
129420
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
129456
+ value: openPath,
129457
+ label: "Copy path",
129458
+ copiedLabel: "Copied path",
129421
129459
  size: "icon-sm",
129422
129460
  variant: "ghost",
129423
- title: "Copy path",
129424
- disabled: !openPath,
129425
- onClick: () => void copyPath(),
129426
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "h-3.5 w-3.5" })
129461
+ disabled: !openPath
129427
129462
  }),
129428
129463
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
129429
129464
  size: "icon-sm",
@@ -130703,10 +130738,6 @@ function SecurityView() {
130703
130738
  await api("POST", `/tokens/${encodeURIComponent(token.jti)}/revoke`);
130704
130739
  await refresh();
130705
130740
  }
130706
- async function copy(value) {
130707
- await navigator.clipboard?.writeText(value);
130708
- setStatus("Copied");
130709
- }
130710
130741
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
130711
130742
  className: "space-y-4",
130712
130743
  children: [
@@ -130936,11 +130967,12 @@ function SecurityView() {
130936
130967
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
130937
130968
  className: "text-xs text-muted-foreground",
130938
130969
  children: "New token"
130939
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
130940
- variant: "ghost",
130970
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
130971
+ value: issuedToken,
130972
+ label: "Copy token",
130941
130973
  size: "sm",
130942
- onClick: () => void copy(issuedToken),
130943
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "h-3.5 w-3.5" })
130974
+ variant: "ghost",
130975
+ onCopied: () => setStatus("Copied")
130944
130976
  })]
130945
130977
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
130946
130978
  className: "block max-h-24 overflow-auto break-all text-xs",
@@ -153632,8 +153664,11 @@ function MaintenanceView() {
153632
153664
  })]
153633
153665
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollArea, {
153634
153666
  className: "h-[calc(100dvh-10rem)]",
153635
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153636
- className: "overflow-x-auto rounded-md border border-border",
153667
+ children: jobs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153668
+ className: "rounded-md border border-border px-3 py-12 text-center text-sm text-muted-foreground",
153669
+ children: "No maintenance jobs registered"
153670
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153671
+ className: "hidden overflow-x-auto rounded-md border border-border md:block",
153637
153672
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
153638
153673
  className: "w-full min-w-[980px] text-sm",
153639
153674
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", {
@@ -153668,22 +153703,117 @@ function MaintenanceView() {
153668
153703
  children: "Action"
153669
153704
  })
153670
153705
  ] })
153671
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: jobs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
153672
- colSpan: 7,
153673
- className: "px-3 py-12 text-center text-sm text-muted-foreground",
153674
- children: "No maintenance jobs registered"
153675
- }) }) : jobs.map((job) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MaintenanceRow, {
153706
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: jobs.map((job) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MaintenanceRow, {
153676
153707
  job,
153677
153708
  now,
153678
153709
  onRun: () => void runMaintenanceJob(job.id)
153679
153710
  }, job.id)) })]
153680
153711
  })
153681
- })
153712
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153713
+ className: "space-y-3 md:hidden",
153714
+ children: jobs.map((job) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MaintenanceCard, {
153715
+ job,
153716
+ now,
153717
+ onRun: () => void runMaintenanceJob(job.id)
153718
+ }, job.id))
153719
+ })] })
153682
153720
  })]
153683
153721
  });
153684
153722
  }
153723
+ function jobStatus(job) {
153724
+ return job.running ? "running" : job.enabled ? job.lastStatus : "disabled";
153725
+ }
153726
+ function StatusBadge({ status }) {
153727
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge$1, {
153728
+ variant: "outline",
153729
+ className: cn$2("border", STATUS_CLASS[status] || STATUS_CLASS.idle),
153730
+ children: [statusIcon(status), status]
153731
+ });
153732
+ }
153733
+ function MaintenanceCard({ job, now, onRun }) {
153734
+ const status = jobStatus(job);
153735
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153736
+ className: "rounded-md border border-border p-3",
153737
+ children: [
153738
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153739
+ className: "flex items-start justify-between gap-2",
153740
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153741
+ className: "min-w-0",
153742
+ children: [
153743
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153744
+ className: "font-medium",
153745
+ children: job.title
153746
+ }),
153747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153748
+ className: "mt-0.5 text-xs text-muted-foreground",
153749
+ children: job.description
153750
+ }),
153751
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153752
+ className: "mt-1 font-mono text-[11px] text-muted-foreground/80",
153753
+ children: job.id
153754
+ })
153755
+ ]
153756
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, { status })]
153757
+ }),
153758
+ job.consecutiveFailures > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153759
+ className: "mt-2 text-xs text-red-400",
153760
+ children: [
153761
+ job.consecutiveFailures,
153762
+ " failure",
153763
+ job.consecutiveFailures === 1 ? "" : "s"
153764
+ ]
153765
+ }),
153766
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
153767
+ className: "mt-3 grid grid-cols-3 gap-2 text-xs",
153768
+ children: [
153769
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
153770
+ className: "text-muted-foreground",
153771
+ children: "Last run"
153772
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: job.lastRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
153773
+ title: fmtTime$1(job.lastRunAt),
153774
+ children: timeAgo(now, job.lastRunAt)
153775
+ }) : "never" })] }),
153776
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
153777
+ className: "text-muted-foreground",
153778
+ children: "Next run"
153779
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: job.nextRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
153780
+ title: fmtTime$1(job.nextRunAt),
153781
+ children: nextRunText(now, job.nextRunAt)
153782
+ }) : "-" })] }),
153783
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
153784
+ className: "text-muted-foreground",
153785
+ children: "Duration"
153786
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: job.lastDurationMs !== void 0 ? `${job.lastDurationMs}ms` : "-" })] })
153787
+ ]
153788
+ }),
153789
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153790
+ className: "mt-3",
153791
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153792
+ className: "text-xs text-muted-foreground",
153793
+ children: "Result"
153794
+ }), job.lastError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153795
+ className: "mt-0.5 text-xs text-red-400 line-clamp-3",
153796
+ children: job.lastError
153797
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153798
+ className: "mt-0.5 font-mono text-[11px] text-muted-foreground line-clamp-3",
153799
+ children: resultSummary(job)
153800
+ })]
153801
+ }),
153802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
153803
+ className: "mt-3 flex justify-end",
153804
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
153805
+ size: "sm",
153806
+ variant: "outline",
153807
+ disabled: !job.enabled || job.running,
153808
+ onClick: onRun,
153809
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Play, { className: "h-3.5 w-3.5" }), " Run"]
153810
+ })
153811
+ })
153812
+ ]
153813
+ });
153814
+ }
153685
153815
  function MaintenanceRow({ job, now, onRun }) {
153686
- const status = job.running ? "running" : job.enabled ? job.lastStatus : "disabled";
153816
+ const status = jobStatus(job);
153687
153817
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
153688
153818
  className: "border-t border-border align-top",
153689
153819
  children: [
@@ -153706,11 +153836,7 @@ function MaintenanceRow({ job, now, onRun }) {
153706
153836
  }),
153707
153837
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", {
153708
153838
  className: "px-3 py-3",
153709
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge$1, {
153710
- variant: "outline",
153711
- className: cn$2("border", STATUS_CLASS[status] || STATUS_CLASS.idle),
153712
- children: [statusIcon(status), status]
153713
- }), job.consecutiveFailures > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153839
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, { status }), job.consecutiveFailures > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153714
153840
  className: "mt-1 text-xs text-red-400",
153715
153841
  children: [
153716
153842
  job.consecutiveFailures,
@@ -154392,7 +154518,6 @@ function AgentDiagnostics({ agent, orchestrators }) {
154392
154518
  const now = useNow();
154393
154519
  const [policyHealth, setPolicyHealth] = (0, import_react.useState)(null);
154394
154520
  const [agentEvents, setAgentEvents] = (0, import_react.useState)([]);
154395
- const [copied, setCopied] = (0, import_react.useState)(false);
154396
154521
  const [expandedSections, setExpandedSections] = (0, import_react.useState)({
154397
154522
  spawn: true,
154398
154523
  workspace: true,
@@ -154436,7 +154561,7 @@ function AgentDiagnostics({ agent, orchestrators }) {
154436
154561
  [key]: !s[key]
154437
154562
  }));
154438
154563
  }
154439
- async function copyDiagnosticBundle() {
154564
+ function buildDiagnosticBundle() {
154440
154565
  const policy = policyHealth?.policy;
154441
154566
  const state = policyHealth?.state;
154442
154567
  const lines = [
@@ -154513,11 +154638,7 @@ function AgentDiagnostics({ agent, orchestrators }) {
154513
154638
  }
154514
154639
  const contracts = agent.meta?.contracts;
154515
154640
  if (contracts) for (const [k, v] of Object.entries(contracts)) lines.push(`Contract ${k}: ${v}`);
154516
- try {
154517
- await navigator.clipboard.writeText(lines.join("\n"));
154518
- setCopied(true);
154519
- setTimeout(() => setCopied(false), 2e3);
154520
- } catch {}
154641
+ return lines.join("\n");
154521
154642
  }
154522
154643
  const policy = policyHealth?.policy;
154523
154644
  const state = policyHealth?.state;
@@ -154529,12 +154650,14 @@ function AgentDiagnostics({ agent, orchestrators }) {
154529
154650
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", {
154530
154651
  className: "text-sm font-medium flex items-center gap-1.5",
154531
154652
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stethoscope, { className: "w-3.5 h-3.5 text-muted-foreground" }), "Diagnostics"]
154532
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
154653
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
154654
+ value: buildDiagnosticBundle,
154655
+ label: "Copy Bundle",
154656
+ showText: true,
154533
154657
  size: "sm",
154534
154658
  variant: "outline",
154535
154659
  className: "h-7 text-xs gap-1",
154536
- onClick: copyDiagnosticBundle,
154537
- children: [copied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "w-3 h-3" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "w-3 h-3" }), copied ? "Copied" : "Copy Bundle"]
154660
+ iconClassName: "w-3 h-3"
154538
154661
  })]
154539
154662
  }),
154540
154663
  policy && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollapsibleSection, {
@@ -155727,13 +155850,21 @@ function AgentDetailDrawer() {
155727
155850
  className: "space-y-1 text-sm",
155728
155851
  children: [
155729
155852
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
155730
- className: "flex justify-between gap-2 min-w-0",
155853
+ className: "flex items-center justify-between gap-2 min-w-0",
155731
155854
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155732
155855
  className: "text-muted-foreground shrink-0",
155733
155856
  children: "ID"
155734
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155735
- className: "font-mono text-xs truncate",
155736
- children: agent.id
155857
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
155858
+ className: "flex min-w-0 items-center gap-1",
155859
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
155860
+ className: "font-mono text-xs truncate",
155861
+ children: agent.id
155862
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
155863
+ value: agent.id,
155864
+ label: "Copy agent ID",
155865
+ size: "icon-xs",
155866
+ className: "shrink-0 text-muted-foreground"
155867
+ })]
155737
155868
  })]
155738
155869
  }),
155739
155870
  runtimePackage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -157317,11 +157448,6 @@ function OrchestratorInstallModal() {
157317
157448
  setLoading(false);
157318
157449
  }
157319
157450
  }
157320
- async function copy() {
157321
- if (!command) return;
157322
- await navigator.clipboard.writeText(command);
157323
- showNotification("Install command copied");
157324
- }
157325
157451
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog, {
157326
157452
  open,
157327
157453
  onOpenChange: (o) => !o && set({ orchestratorInstallOpen: false }),
@@ -157390,11 +157516,14 @@ function OrchestratorInstallModal() {
157390
157516
  onClick: () => set({ orchestratorInstallOpen: false }),
157391
157517
  children: "Close"
157392
157518
  }),
157393
- command && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
157519
+ command && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyButton, {
157520
+ value: command,
157521
+ label: "Copy",
157522
+ showText: true,
157523
+ size: "default",
157394
157524
  variant: "outline",
157395
157525
  className: "gap-1",
157396
- onClick: copy,
157397
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "w-3.5 h-3.5" }), "Copy"]
157526
+ onCopied: () => showNotification("Install command copied")
157398
157527
  }),
157399
157528
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
157400
157529
  className: "gap-1",
@@ -159322,6 +159451,10 @@ if ("serviceWorker" in navigator) {
159322
159451
  display: inline-flex;
159323
159452
  }
159324
159453
 
159454
+ .table {
159455
+ display: table;
159456
+ }
159457
+
159325
159458
  .field-sizing-content {
159326
159459
  field-sizing: content;
159327
159460
  }