agent-relay-server 0.22.0 → 0.24.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
@@ -10510,6 +10510,40 @@ function prepareForSpeech(markdown) {
10510
10510
  }
10511
10511
  //#endregion
10512
10512
  //#region src/lib/voice.ts
10513
+ var KOKORO_VOICES = [
10514
+ {
10515
+ id: "am_michael",
10516
+ label: "Michael (US ♂)"
10517
+ },
10518
+ {
10519
+ id: "am_adam",
10520
+ label: "Adam (US ♂)"
10521
+ },
10522
+ {
10523
+ id: "af_heart",
10524
+ label: "Heart (US ♀)"
10525
+ },
10526
+ {
10527
+ id: "af_bella",
10528
+ label: "Bella (US ♀)"
10529
+ },
10530
+ {
10531
+ id: "af_nicole",
10532
+ label: "Nicole (US ♀)"
10533
+ },
10534
+ {
10535
+ id: "af_sarah",
10536
+ label: "Sarah (US ♀)"
10537
+ },
10538
+ {
10539
+ id: "bm_george",
10540
+ label: "George (UK ♂)"
10541
+ },
10542
+ {
10543
+ id: "bf_emma",
10544
+ label: "Emma (UK ♀)"
10545
+ }
10546
+ ];
10513
10547
  var MAX_CHUNK = 220;
10514
10548
  /** Split into utterance-sized chunks (sentence boundaries; hard-split very long runs). */
10515
10549
  function chunkForSpeech(text) {
@@ -10537,6 +10571,7 @@ var VoiceTts = class {
10537
10571
  lang = "en-US";
10538
10572
  mode = "kokoro";
10539
10573
  kokoroVoice = "am_michael";
10574
+ browserVoice = "";
10540
10575
  active = null;
10541
10576
  queue = [];
10542
10577
  currentChat = null;
@@ -10586,6 +10621,10 @@ var VoiceTts = class {
10586
10621
  setKokoroVoice(voice) {
10587
10622
  this.kokoroVoice = voice;
10588
10623
  }
10624
+ /** Set the browser engine's voice by voiceURI. Empty = default for the language. */
10625
+ setBrowserVoice(uri) {
10626
+ this.browserVoice = uri;
10627
+ }
10589
10628
  setActiveChat(chatId) {
10590
10629
  if (chatId === this.active) return;
10591
10630
  this.active = chatId;
@@ -10683,7 +10722,11 @@ var VoiceTts = class {
10683
10722
  if (gen !== this.gen) return;
10684
10723
  if (!synthAvailable || i >= chunks.length) return done();
10685
10724
  const u = new SpeechSynthesisUtterance(chunks[i]);
10686
- u.lang = this.lang || navigator.language || "en-US";
10725
+ const picked = this.browserVoice ? window.speechSynthesis.getVoices().find((v) => v.voiceURI === this.browserVoice) : void 0;
10726
+ if (picked) {
10727
+ u.voice = picked;
10728
+ u.lang = picked.lang;
10729
+ } else u.lang = this.lang || navigator.language || "en-US";
10687
10730
  u.onend = () => this.speakBrowser(chunks, i + 1, gen, done);
10688
10731
  u.onerror = () => this.speakBrowser(chunks, i + 1, gen, done);
10689
10732
  window.speechSynthesis.speak(u);
@@ -10732,12 +10775,16 @@ var VoiceTts = class {
10732
10775
  }
10733
10776
  };
10734
10777
  var voiceTts = new VoiceTts();
10735
- /** Sorted unique BCP-47 languages the browser's speech engine can speak. May be empty
10736
- * until the engine finishes loading voices (listen for `voiceschanged` and re-read). */
10737
- function availableSpeechLangs() {
10778
+ /** The browser's individual speech voices ({uri, label, lang}), sorted by language
10779
+ * then name. May be empty until the engine loads (listen for `voiceschanged`). */
10780
+ function availableSpeechVoices() {
10738
10781
  if (!synthAvailable) return [];
10739
10782
  try {
10740
- return [...new Set(window.speechSynthesis.getVoices().map((v) => v.lang).filter(Boolean))].sort();
10783
+ return window.speechSynthesis.getVoices().map((v) => ({
10784
+ uri: v.voiceURI,
10785
+ label: `${v.name} (${v.lang})`,
10786
+ lang: v.lang
10787
+ })).sort((a, b) => a.lang.localeCompare(b.lang) || a.label.localeCompare(b.label));
10741
10788
  } catch {
10742
10789
  return [];
10743
10790
  }
@@ -11344,6 +11391,14 @@ function agentPresence(now, agent, attention, pair) {
11344
11391
  reconnecting: false,
11345
11392
  badges
11346
11393
  };
11394
+ if (lifecycleAction.startsWith("finalizing-")) return {
11395
+ label: `wrapping up (${lifecycleAction.slice(11)})`,
11396
+ tone: "warning",
11397
+ icon: "Hourglass",
11398
+ stale,
11399
+ reconnecting,
11400
+ badges
11401
+ };
11347
11402
  if (lifecycleAction === "shutting-down") return {
11348
11403
  label: "shutting down",
11349
11404
  tone: "warning",
@@ -11717,6 +11772,7 @@ function toneToColor(tone) {
11717
11772
  function statusDotColor(agent, now) {
11718
11773
  if (!agent) return "bg-zinc-500 opacity-50";
11719
11774
  if (agent.status === "offline") return "bg-zinc-500 opacity-50";
11775
+ if (typeof agent.meta?.lifecycleAction === "string" && agent.meta.lifecycleAction.startsWith("finalizing-")) return "bg-amber-500 animate-pulse";
11720
11776
  if (agent.meta?.lifecycleAction === "shutting-down" || agent.meta?.lifecycleAction === "restarting") return "bg-yellow-500 animate-pulse";
11721
11777
  if (agent.meta?.lifecycleAction === "killing") return "bg-red-500 animate-pulse";
11722
11778
  if (providerBlockedState(agent)) return "bg-red-500 animate-pulse";
@@ -12176,6 +12232,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
12176
12232
  voiceTtsLang: "en-US",
12177
12233
  voiceTtsMode: "kokoro",
12178
12234
  voiceTtsKokoroVoice: "am_michael",
12235
+ voiceTtsBrowserVoice: "",
12179
12236
  voiceInputMode: "compose",
12180
12237
  agentSort: "status",
12181
12238
  agentSortDir: "asc",
@@ -12220,6 +12277,8 @@ var useRelayStore = create$1()(persist((set, get) => ({
12220
12277
  chatAgentHostFilter: "",
12221
12278
  chatAgentSort: "status",
12222
12279
  chatAgentSortDir: "asc",
12280
+ chatAgentGroupBy: "",
12281
+ chatAgentFiltersCollapsed: true,
12223
12282
  chatStatusEvents: {},
12224
12283
  chatStickToBottom: true,
12225
12284
  chatHasNewItems: false,
@@ -12382,6 +12441,10 @@ var useRelayStore = create$1()(persist((set, get) => ({
12382
12441
  voiceTts.setKokoroVoice(voice);
12383
12442
  set({ voiceTtsKokoroVoice: voice });
12384
12443
  },
12444
+ setVoiceTtsBrowserVoice(uri) {
12445
+ voiceTts.setBrowserVoice(uri);
12446
+ set({ voiceTtsBrowserVoice: uri });
12447
+ },
12385
12448
  setVoiceInputMode(mode) {
12386
12449
  set({ voiceInputMode: mode });
12387
12450
  },
@@ -12398,6 +12461,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
12398
12461
  voiceTts.setLang(get().voiceTtsLang);
12399
12462
  voiceTts.setMode(get().voiceTtsMode);
12400
12463
  voiceTts.setKokoroVoice(get().voiceTtsKokoroVoice);
12464
+ voiceTts.setBrowserVoice(get().voiceTtsBrowserVoice);
12401
12465
  syncVoiceActiveChat(get());
12402
12466
  setUnauthorizedHandler(() => {
12403
12467
  if (!get().authNeeded) set({
@@ -14520,6 +14584,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
14520
14584
  voiceTtsLang: state.voiceTtsLang,
14521
14585
  voiceTtsMode: state.voiceTtsMode,
14522
14586
  voiceTtsKokoroVoice: state.voiceTtsKokoroVoice,
14587
+ voiceTtsBrowserVoice: state.voiceTtsBrowserVoice,
14523
14588
  voiceInputMode: state.voiceInputMode,
14524
14589
  agentSort: state.agentSort,
14525
14590
  agentSortDir: state.agentSortDir,
@@ -14538,6 +14603,8 @@ var useRelayStore = create$1()(persist((set, get) => ({
14538
14603
  chatAgentHostFilter: state.chatAgentHostFilter,
14539
14604
  chatAgentSort: state.chatAgentSort,
14540
14605
  chatAgentSortDir: state.chatAgentSortDir,
14606
+ chatAgentGroupBy: state.chatAgentGroupBy,
14607
+ chatAgentFiltersCollapsed: state.chatAgentFiltersCollapsed,
14541
14608
  activityFilter: state.activityFilter,
14542
14609
  analyticsPeriod: state.analyticsPeriod,
14543
14610
  memoryFilters: state.memoryFilters,
@@ -77539,6 +77606,49 @@ function OverviewView() {
77539
77606
  });
77540
77607
  }
77541
77608
  //#endregion
77609
+ //#region src/hooks/use-agent-terminal.ts
77610
+ /**
77611
+ * Open/close lifecycle for an agent's terminal session. Shared by the agent
77612
+ * card and the chat header — both POST a terminal-session, track the target,
77613
+ * and DELETE guest sessions on close. Pass null/undefined to no-op (e.g. when
77614
+ * no agent is selected).
77615
+ */
77616
+ function useAgentTerminal(agentId) {
77617
+ const showError = useRelayStore((s) => s.showError);
77618
+ const [terminalOpen, setTerminalOpen] = (0, import_react.useState)(false);
77619
+ const [terminalTarget, setTerminalTarget] = (0, import_react.useState)(null);
77620
+ const [terminalOpening, setTerminalOpening] = (0, import_react.useState)(false);
77621
+ async function openTerminal() {
77622
+ if (!agentId || terminalOpening) return;
77623
+ setTerminalOpening(true);
77624
+ try {
77625
+ setTerminalTarget(await api("POST", "/agents/" + encodeURIComponent(agentId) + "/terminal-session"));
77626
+ setTerminalOpen(true);
77627
+ } catch (e) {
77628
+ showError("Terminal Failed", e.message);
77629
+ } finally {
77630
+ setTerminalOpening(false);
77631
+ }
77632
+ }
77633
+ function closeTerminal(open) {
77634
+ if (open) {
77635
+ setTerminalOpen(true);
77636
+ return;
77637
+ }
77638
+ setTerminalOpen(false);
77639
+ const target = terminalTarget;
77640
+ setTerminalTarget(null);
77641
+ if (agentId && target?.mode === "guest") api("DELETE", "/agents/" + encodeURIComponent(agentId) + "/terminal-session/" + encodeURIComponent(target.session)).catch(() => {});
77642
+ }
77643
+ return {
77644
+ terminalOpen,
77645
+ terminalTarget,
77646
+ terminalOpening,
77647
+ openTerminal,
77648
+ closeTerminal
77649
+ };
77650
+ }
77651
+ //#endregion
77542
77652
  //#region node_modules/comma-separated-tokens/index.js
77543
77653
  /**
77544
77654
  * Serialize an array of strings or numbers to comma-separated tokens.
@@ -108818,7 +108928,7 @@ function ContextMeter({ agent }) {
108818
108928
  function runtimeChips(agent) {
108819
108929
  const spawned = isRelaySpawned(agent);
108820
108930
  const approval = approvalMode(agent);
108821
- const workspace = workspaceMode(agent);
108931
+ const workspace = agentWorkspaceMeta(agent);
108822
108932
  return [
108823
108933
  {
108824
108934
  key: "origin",
@@ -108858,7 +108968,7 @@ function approvalMode(agent) {
108858
108968
  className: "border-amber-500/30 bg-amber-500/10 text-amber-300"
108859
108969
  };
108860
108970
  }
108861
- function workspaceMode(agent) {
108971
+ function agentWorkspaceMeta(agent) {
108862
108972
  const workspace = recordValue(agent.meta?.workspace);
108863
108973
  const requested = stringValue(workspace.requestedMode) || stringValue(agent.meta?.workspaceMode);
108864
108974
  const effective = stringValue(workspace.mode);
@@ -108868,6 +108978,7 @@ function workspaceMode(agent) {
108868
108978
  label: "Isolated",
108869
108979
  title: "Workspace: isolated worktree",
108870
108980
  Icon: GitBranch,
108981
+ iconColor: "text-violet-300",
108871
108982
  className: "border-violet-500/30 bg-violet-500/10 text-violet-300"
108872
108983
  };
108873
108984
  if (mode === "shared") return {
@@ -108875,6 +108986,7 @@ function workspaceMode(agent) {
108875
108986
  label: "Shared",
108876
108987
  title: "Workspace: shared repo checkout",
108877
108988
  Icon: Share2,
108989
+ iconColor: "text-cyan-300",
108878
108990
  className: "border-cyan-500/30 bg-cyan-500/10 text-cyan-300"
108879
108991
  };
108880
108992
  return {
@@ -108882,12 +108994,18 @@ function workspaceMode(agent) {
108882
108994
  label: "Default",
108883
108995
  title: effective ? `Workspace: default/inherit, resolved to ${effective}` : "Workspace: default/inherit",
108884
108996
  Icon: Layers,
108997
+ iconColor: "text-zinc-400",
108885
108998
  className: "border-zinc-500/30 bg-zinc-500/10 text-zinc-300"
108886
108999
  };
108887
109000
  }
108888
109001
  function recordValue(value) {
108889
109002
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
108890
109003
  }
109004
+ function agentProjectName(agent) {
109005
+ const workspace = recordValue(agent.meta?.workspace);
109006
+ const root = stringValue(workspace.repoRoot) || stringValue(workspace.sourceCwd) || stringValue(agent.meta?.cwd);
109007
+ return root ? shortPath$1(root, 2) : "";
109008
+ }
108891
109009
  function runtimeBadges(agent) {
108892
109010
  const caps = agent.providerCapabilities;
108893
109011
  const context = agent.context;
@@ -108903,40 +109021,47 @@ function runtimeBadges(agent) {
108903
109021
  if (context) badges.push(`${Math.round(context.utilization * 100)}% ctx`);
108904
109022
  return badges;
108905
109023
  }
108906
- function ContextRing({ utilization, className }) {
109024
+ function ContextRing({ utilization, size = 26, className }) {
108907
109025
  const pct = Math.round(Math.min(1, Math.max(0, utilization)) * 100);
108908
- const r = 10;
109026
+ const stroke = size >= 24 ? 3 : 2.5;
109027
+ const c = size / 2;
109028
+ const r = c - stroke;
108909
109029
  const circumference = 2 * Math.PI * r;
108910
109030
  const offset = circumference * (1 - Math.min(1, Math.max(0, utilization)));
108911
109031
  const color = utilization > .8 ? "text-red-400" : utilization > .5 ? "text-yellow-400" : "text-emerald-400";
108912
109032
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
108913
109033
  className: `relative shrink-0 ${className ?? ""}`,
109034
+ style: {
109035
+ width: size,
109036
+ height: size
109037
+ },
108914
109038
  title: `Context: ${pct}%`,
108915
109039
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
108916
- width: "26",
108917
- height: "26",
108918
- viewBox: "0 0 26 26",
109040
+ width: size,
109041
+ height: size,
109042
+ viewBox: `0 0 ${size} ${size}`,
108919
109043
  className: "rotate-[-90deg]",
108920
109044
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
108921
- cx: "13",
108922
- cy: "13",
109045
+ cx: c,
109046
+ cy: c,
108923
109047
  r,
108924
109048
  fill: "none",
108925
- strokeWidth: "3",
109049
+ strokeWidth: stroke,
108926
109050
  className: "stroke-muted-foreground/20"
108927
109051
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
108928
- cx: "13",
108929
- cy: "13",
109052
+ cx: c,
109053
+ cy: c,
108930
109054
  r,
108931
109055
  fill: "none",
108932
- strokeWidth: "3",
109056
+ strokeWidth: stroke,
108933
109057
  strokeDasharray: circumference,
108934
109058
  strokeDashoffset: offset,
108935
109059
  strokeLinecap: "round",
108936
109060
  className: `${color} stroke-current transition-all duration-500`
108937
109061
  })]
108938
109062
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
108939
- className: `absolute inset-0 flex items-center justify-center text-[8px] font-semibold leading-none ${color}`,
109063
+ className: `absolute inset-0 flex items-center justify-center font-semibold leading-none ${color}`,
109064
+ style: { fontSize: size >= 24 ? 8 : 7 },
108940
109065
  children: pct
108941
109066
  })]
108942
109067
  });
@@ -125643,6 +125768,8 @@ function AgentListPanel({ threads, onSelectAgent }) {
125643
125768
  const chatAgentTagFilter = useRelayStore((s) => s.chatAgentTagFilter);
125644
125769
  const chatAgentCapFilter = useRelayStore((s) => s.chatAgentCapFilter);
125645
125770
  const chatAgentHostFilter = useRelayStore((s) => s.chatAgentHostFilter);
125771
+ const chatAgentGroupBy = useRelayStore((s) => s.chatAgentGroupBy);
125772
+ const chatAgentFiltersCollapsed = useRelayStore((s) => s.chatAgentFiltersCollapsed);
125646
125773
  const now = useNow();
125647
125774
  const chatAgents = useChatAgents();
125648
125775
  const threadByPeer = (0, import_react.useMemo)(() => {
@@ -125666,161 +125793,287 @@ function AgentListPanel({ threads, onSelectAgent }) {
125666
125793
  const uniqueTags = (0, import_react.useMemo)(() => [...new Set(chatAgents.flatMap((a) => a.tags || []))], [chatAgents]);
125667
125794
  const uniqueCaps = (0, import_react.useMemo)(() => [...new Set(chatAgents.flatMap((a) => userFacingCapabilities(a.capabilities || [])))], [chatAgents]);
125668
125795
  const uniqueHosts = useUniqueHosts();
125796
+ const groupedAgents = (0, import_react.useMemo)(() => {
125797
+ if (chatAgentGroupBy !== "project") return null;
125798
+ const groups = /* @__PURE__ */ new Map();
125799
+ for (const a of sortedAgents) {
125800
+ const key = agentProjectName(a) || "No project";
125801
+ const bucket = groups.get(key);
125802
+ if (bucket) bucket.push(a);
125803
+ else groups.set(key, [a]);
125804
+ }
125805
+ return [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0]));
125806
+ }, [sortedAgents, chatAgentGroupBy]);
125807
+ const activeFilterCount = [
125808
+ chatAgentProviderFilter,
125809
+ chatAgentStatusFilter,
125810
+ chatAgentTagFilter,
125811
+ chatAgentCapFilter,
125812
+ chatAgentHostFilter
125813
+ ].filter(Boolean).length;
125669
125814
  function handleSelect(id) {
125670
125815
  openInboxThread(id, threadByPeer.get(id)?.messages);
125671
125816
  onSelectAgent?.(id);
125672
125817
  }
125673
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125674
- className: "flex flex-col h-full border-r border-border min-w-0",
125675
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125676
- className: "p-3 border-b border-border space-y-2",
125677
- children: [
125678
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125679
- className: "relative",
125680
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground pointer-events-none" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
125681
- placeholder: "Search agents...",
125682
- value: chatAgentSearch,
125683
- onChange: (e) => set({ chatAgentSearch: e.target.value }),
125684
- className: "pl-8 h-7 text-xs"
125685
- })]
125686
- }),
125687
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125688
- className: "flex items-center gap-1.5",
125689
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125690
- value: chatAgentSort,
125691
- onChange: (e) => set({ chatAgentSort: e.target.value }),
125692
- className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125818
+ function renderAgentRow(agent) {
125819
+ const thread = threadByPeer.get(agent.id);
125820
+ const unread = thread?.attention.unread || 0;
125821
+ const lastMsg = thread?.previewMessage;
125822
+ const lastActivityAt = threadActivityTimestamp(thread);
125823
+ const isSelected = selectedInboxThread === agent.id;
125824
+ const ws = agentWorkspaceMeta(agent);
125825
+ const project = agentProjectName(agent);
125826
+ const WsIcon = ws.Icon;
125827
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
125828
+ className: cn$2("w-full text-left px-3 py-2.5 flex items-start gap-2.5 hover:bg-muted/50 transition-colors border-b border-border/50", isSelected && "bg-muted/60"),
125829
+ onClick: () => handleSelect(agent.id),
125830
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125831
+ className: "relative shrink-0 mt-0.5",
125832
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentTypeIcon, { agent }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusDot, {
125833
+ agent,
125834
+ now,
125835
+ className: "absolute -bottom-0.5 -right-0.5 w-2 h-2"
125836
+ })]
125837
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125838
+ className: "flex-1 min-w-0",
125839
+ children: [
125840
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125841
+ className: "flex items-center justify-between gap-1 mb-0.5",
125842
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125843
+ className: "text-xs font-medium truncate",
125844
+ children: displayName(agent)
125845
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125846
+ className: "flex items-center gap-1 shrink-0",
125847
+ children: [lastActivityAt > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125848
+ className: "text-[10px] text-muted-foreground",
125849
+ title: fmtTime$1(lastActivityAt),
125850
+ children: timeAgo(now, lastActivityAt)
125851
+ }), unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
125852
+ className: "bg-red-500 text-white text-xs min-w-[18px] h-[18px] flex items-center justify-center shrink-0 px-1",
125853
+ children: unread
125854
+ })]
125855
+ })]
125856
+ }),
125857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125858
+ className: "flex items-center gap-1.5 mb-0.5 text-[10px] text-muted-foreground",
125693
125859
  children: [
125694
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125695
- value: "status",
125696
- children: "By status"
125697
- }),
125698
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125699
- value: "name",
125700
- children: "By name"
125860
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125861
+ title: ws.title,
125862
+ className: "inline-flex shrink-0",
125863
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WsIcon, { className: cn$2("h-3 w-3", ws.iconColor) })
125701
125864
  }),
125702
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125703
- value: "lastSeen",
125704
- children: "By last seen"
125865
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125866
+ className: "truncate",
125867
+ children: project || ws.label
125705
125868
  }),
125706
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125707
- value: "lastMessage",
125708
- children: "By last activity"
125869
+ agent.context && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125870
+ className: "ml-auto shrink-0",
125871
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextRing, {
125872
+ utilization: agent.context.utilization,
125873
+ size: 18
125874
+ })
125709
125875
  })
125710
125876
  ]
125711
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
125712
- variant: "ghost",
125713
- size: "icon-xs",
125714
- onClick: () => set({ chatAgentSortDir: chatAgentSortDir === "asc" ? "desc" : "asc" }),
125715
- children: chatAgentSortDir === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUpNarrowWide, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowDownWideNarrow, { className: "w-3.5 h-3.5" })
125716
- })]
125717
- }),
125718
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125719
- className: "flex gap-1",
125720
- children: [
125721
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125722
- value: chatAgentProviderFilter,
125723
- onChange: (e) => set({ chatAgentProviderFilter: e.target.value }),
125724
- className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125877
+ }),
125878
+ lastMsg && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
125879
+ className: "text-xs text-muted-foreground truncate leading-tight",
125880
+ children: [lastMsg.from === "user" ? "You: " : "", messagePreview(lastMsg)]
125881
+ }),
125882
+ agent.status !== "offline" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
125883
+ className: "mt-1",
125884
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125885
+ className: cn$2("text-xs", agent.status === "busy" ? "text-yellow-400" : "text-emerald-400"),
125886
+ children: agent.status
125887
+ })
125888
+ })
125889
+ ]
125890
+ })]
125891
+ }) }, agent.id);
125892
+ }
125893
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125894
+ className: "flex flex-col h-full border-r border-border min-w-0",
125895
+ children: [
125896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125897
+ className: "p-3 border-b border-border space-y-2",
125898
+ children: [
125899
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125900
+ className: "relative",
125901
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground pointer-events-none" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
125902
+ placeholder: "Search agents...",
125903
+ value: chatAgentSearch,
125904
+ onChange: (e) => set({ chatAgentSearch: e.target.value }),
125905
+ className: "pl-8 h-7 text-xs"
125906
+ })]
125907
+ }),
125908
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
125909
+ type: "button",
125910
+ onClick: () => set({ chatAgentFiltersCollapsed: !chatAgentFiltersCollapsed }),
125911
+ className: "sm:hidden flex w-full items-center justify-between h-6 px-1.5 text-xs rounded-md border border-border bg-background text-muted-foreground",
125912
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
125913
+ className: "inline-flex items-center gap-1.5",
125725
125914
  children: [
125726
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125727
- value: "",
125728
- children: "All types"
125729
- }),
125730
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125731
- value: "claude",
125732
- children: "Claude"
125733
- }),
125734
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125735
- value: "codex",
125736
- children: "Codex"
125737
- }),
125738
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125739
- value: "agent",
125740
- children: "Agent"
125741
- }),
125742
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125743
- value: "user",
125744
- children: "User"
125915
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SlidersHorizontal, { className: "w-3.5 h-3.5" }),
125916
+ "Filters & sort",
125917
+ activeFilterCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
125918
+ className: "bg-primary/20 text-primary text-[10px] h-4 min-w-4 px-1 flex items-center justify-center",
125919
+ children: activeFilterCount
125745
125920
  })
125746
125921
  ]
125747
- }),
125748
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125749
- value: chatAgentStatusFilter,
125750
- onChange: (e) => set({ chatAgentStatusFilter: e.target.value }),
125751
- className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125752
- children: [
125753
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125922
+ }), chatAgentFiltersCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, { className: "w-3.5 h-3.5" })]
125923
+ }),
125924
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125925
+ className: cn$2("space-y-2", chatAgentFiltersCollapsed ? "hidden" : "block", "sm:block"),
125926
+ children: [
125927
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125928
+ className: "flex items-center gap-1.5",
125929
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125930
+ value: chatAgentSort,
125931
+ onChange: (e) => set({ chatAgentSort: e.target.value }),
125932
+ className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125933
+ children: [
125934
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125935
+ value: "status",
125936
+ children: "By status"
125937
+ }),
125938
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125939
+ value: "name",
125940
+ children: "By name"
125941
+ }),
125942
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125943
+ value: "lastSeen",
125944
+ children: "By last seen"
125945
+ }),
125946
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125947
+ value: "lastMessage",
125948
+ children: "By last activity"
125949
+ })
125950
+ ]
125951
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
125952
+ variant: "ghost",
125953
+ size: "icon-xs",
125954
+ onClick: () => set({ chatAgentSortDir: chatAgentSortDir === "asc" ? "desc" : "asc" }),
125955
+ children: chatAgentSortDir === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUpNarrowWide, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowDownWideNarrow, { className: "w-3.5 h-3.5" })
125956
+ })]
125957
+ }),
125958
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125959
+ value: chatAgentGroupBy,
125960
+ onChange: (e) => set({ chatAgentGroupBy: e.target.value }),
125961
+ className: "w-full h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125962
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125754
125963
  value: "",
125755
- children: "All status"
125756
- }),
125757
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125758
- value: "online",
125759
- children: "Online"
125760
- }),
125761
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125762
- value: "idle",
125763
- children: "Idle"
125764
- }),
125765
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125766
- value: "busy",
125767
- children: "Busy"
125768
- }),
125769
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125770
- value: "offline",
125771
- children: "Offline"
125772
- })
125773
- ]
125774
- }),
125775
- uniqueHosts.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125776
- value: chatAgentHostFilter,
125777
- onChange: (e) => set({ chatAgentHostFilter: e.target.value }),
125778
- className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125779
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125780
- value: "",
125781
- children: "All hosts"
125782
- }), uniqueHosts.map((h) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125783
- value: h,
125784
- children: h
125785
- }, h))]
125786
- })
125787
- ]
125788
- }),
125789
- (uniqueTags.length > 0 || uniqueCaps.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125790
- className: "flex gap-1",
125791
- children: [uniqueTags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125792
- value: chatAgentTagFilter,
125793
- onChange: (e) => set({ chatAgentTagFilter: e.target.value }),
125794
- className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125795
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125796
- value: "",
125797
- children: "All tags"
125798
- }), uniqueTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", {
125799
- value: t,
125800
- children: ["#", t]
125801
- }, t))]
125802
- }), uniqueCaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125803
- value: chatAgentCapFilter,
125804
- onChange: (e) => set({ chatAgentCapFilter: e.target.value }),
125805
- className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125806
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125807
- value: "",
125808
- children: "All caps"
125809
- }), uniqueCaps.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125810
- value: c,
125811
- children: c
125812
- }, c))]
125813
- })]
125814
- })
125815
- ]
125816
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125817
- className: "flex-1 overflow-y-auto",
125818
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
125964
+ children: "No grouping"
125965
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125966
+ value: "project",
125967
+ children: "Group by project"
125968
+ })]
125969
+ }),
125970
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125971
+ className: "flex gap-1",
125972
+ children: [
125973
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
125974
+ value: chatAgentProviderFilter,
125975
+ onChange: (e) => set({ chatAgentProviderFilter: e.target.value }),
125976
+ className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
125977
+ children: [
125978
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125979
+ value: "",
125980
+ children: "All types"
125981
+ }),
125982
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125983
+ value: "claude",
125984
+ children: "Claude"
125985
+ }),
125986
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125987
+ value: "codex",
125988
+ children: "Codex"
125989
+ }),
125990
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125991
+ value: "agent",
125992
+ children: "Agent"
125993
+ }),
125994
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
125995
+ value: "user",
125996
+ children: "User"
125997
+ })
125998
+ ]
125999
+ }),
126000
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
126001
+ value: chatAgentStatusFilter,
126002
+ onChange: (e) => set({ chatAgentStatusFilter: e.target.value }),
126003
+ className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
126004
+ children: [
126005
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126006
+ value: "",
126007
+ children: "All status"
126008
+ }),
126009
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126010
+ value: "online",
126011
+ children: "Online"
126012
+ }),
126013
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126014
+ value: "idle",
126015
+ children: "Idle"
126016
+ }),
126017
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126018
+ value: "busy",
126019
+ children: "Busy"
126020
+ }),
126021
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126022
+ value: "offline",
126023
+ children: "Offline"
126024
+ })
126025
+ ]
126026
+ }),
126027
+ uniqueHosts.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
126028
+ value: chatAgentHostFilter,
126029
+ onChange: (e) => set({ chatAgentHostFilter: e.target.value }),
126030
+ className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
126031
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126032
+ value: "",
126033
+ children: "All hosts"
126034
+ }), uniqueHosts.map((h) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126035
+ value: h,
126036
+ children: h
126037
+ }, h))]
126038
+ })
126039
+ ]
126040
+ }),
126041
+ (uniqueTags.length > 0 || uniqueCaps.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
126042
+ className: "flex gap-1",
126043
+ children: [uniqueTags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
126044
+ value: chatAgentTagFilter,
126045
+ onChange: (e) => set({ chatAgentTagFilter: e.target.value }),
126046
+ className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
126047
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126048
+ value: "",
126049
+ children: "All tags"
126050
+ }), uniqueTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", {
126051
+ value: t,
126052
+ children: ["#", t]
126053
+ }, t))]
126054
+ }), uniqueCaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
126055
+ value: chatAgentCapFilter,
126056
+ onChange: (e) => set({ chatAgentCapFilter: e.target.value }),
126057
+ className: "flex-1 h-6 text-xs rounded-md border border-border bg-background px-1.5 text-foreground",
126058
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126059
+ value: "",
126060
+ children: "All caps"
126061
+ }), uniqueCaps.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
126062
+ value: c,
126063
+ children: c
126064
+ }, c))]
126065
+ })]
126066
+ })
126067
+ ]
126068
+ })
126069
+ ]
126070
+ }),
126071
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
125819
126072
  type: "button",
125820
- className: "flex w-full items-start gap-2.5 border-b border-border/50 px-3 py-2.5 text-left transition-colors hover:bg-muted/50",
126073
+ className: "flex w-full shrink-0 items-center gap-2.5 border-b border-border px-3 py-2 text-left transition-colors hover:bg-muted/50",
125821
126074
  onClick: openAgentSpawn,
125822
126075
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
125823
- className: "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-dashed border-primary/50 bg-primary/10 text-primary",
126076
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-dashed border-primary/50 bg-primary/10 text-primary",
125824
126077
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, { className: "h-3.5 w-3.5" })
125825
126078
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125826
126079
  className: "min-w-0 flex-1",
@@ -125828,71 +126081,31 @@ function AgentListPanel({ threads, onSelectAgent }) {
125828
126081
  className: "text-xs font-medium",
125829
126082
  children: "New Agent"
125830
126083
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
125831
- className: "mt-0.5 truncate text-xs text-muted-foreground",
126084
+ className: "truncate text-[10px] text-muted-foreground",
125832
126085
  children: "Spawn a fresh chat agent"
125833
126086
  })]
125834
126087
  })]
125835
- }), sortedAgents.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125836
- className: "flex flex-col items-center justify-center py-10 text-center px-4",
125837
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bot, { className: "w-8 h-8 text-zinc-600 mb-2" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
125838
- className: "text-xs text-muted-foreground",
125839
- children: "No agents match filters"
125840
- })]
125841
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: sortedAgents.map((agent) => {
125842
- const thread = threadByPeer.get(agent.id);
125843
- const unread = thread?.attention.unread || 0;
125844
- const lastMsg = thread?.previewMessage;
125845
- const lastActivityAt = threadActivityTimestamp(thread);
125846
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
125847
- className: cn$2("w-full text-left px-3 py-2.5 flex items-start gap-2.5 hover:bg-muted/50 transition-colors border-b border-border/50", selectedInboxThread === agent.id && "bg-muted/60"),
125848
- onClick: () => handleSelect(agent.id),
125849
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125850
- className: "relative shrink-0 mt-0.5",
125851
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentTypeIcon, { agent }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusDot, {
125852
- agent,
125853
- now,
125854
- className: "absolute -bottom-0.5 -right-0.5 w-2 h-2"
125855
- })]
125856
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125857
- className: "flex-1 min-w-0",
125858
- children: [
125859
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125860
- className: "flex items-center justify-between gap-1 mb-0.5",
125861
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125862
- className: "text-xs font-medium truncate",
125863
- children: displayName(agent)
125864
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125865
- className: "flex items-center gap-1 shrink-0",
125866
- children: [lastActivityAt > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125867
- className: "text-[10px] text-muted-foreground",
125868
- title: fmtTime$1(lastActivityAt),
125869
- children: timeAgo(now, lastActivityAt)
125870
- }), unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge$1, {
125871
- className: "bg-red-500 text-white text-xs min-w-[18px] h-[18px] flex items-center justify-center shrink-0 px-1",
125872
- children: unread
125873
- })]
125874
- })]
125875
- }),
125876
- typeof agent.meta?.cwd === "string" && agent.meta.cwd && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
125877
- className: "font-mono text-[10px] text-muted-foreground/60 truncate",
125878
- children: shortPath$1(agent.meta.cwd)
125879
- }),
125880
- lastMsg && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
125881
- className: "text-xs text-muted-foreground truncate leading-tight",
125882
- children: [lastMsg.from === "user" ? "You: " : "", messagePreview(lastMsg)]
125883
- }),
125884
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
125885
- className: "flex flex-wrap gap-1 mt-1",
125886
- children: agent.status !== "offline" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125887
- className: cn$2("text-xs", agent.status === "busy" ? "text-yellow-400" : agent.status === "idle" ? "text-emerald-400" : "text-emerald-400"),
125888
- children: agent.status
125889
- })
125890
- })
125891
- ]
126088
+ }),
126089
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
126090
+ className: "flex-1 overflow-y-auto",
126091
+ children: sortedAgents.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
126092
+ className: "flex flex-col items-center justify-center py-10 text-center px-4",
126093
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bot, { className: "w-8 h-8 text-zinc-600 mb-2" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
126094
+ className: "text-xs text-muted-foreground",
126095
+ children: "No agents match filters"
125892
126096
  })]
125893
- }) }, agent.id);
125894
- }) })]
125895
- })]
126097
+ }) : groupedAgents ? groupedAgents.map(([project, agents]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
126098
+ className: "sticky top-0 z-10 flex items-center gap-1.5 border-b border-border/50 bg-background/95 px-3 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground backdrop-blur",
126099
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
126100
+ className: "truncate",
126101
+ children: project
126102
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
126103
+ className: "text-muted-foreground/50",
126104
+ children: agents.length
126105
+ })]
126106
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: agents.map(renderAgentRow) })] }, project)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: sortedAgents.map(renderAgentRow) })
126107
+ })
126108
+ ]
125896
126109
  });
125897
126110
  }
125898
126111
  function chatTimestamp(iso) {
@@ -125952,40 +126165,6 @@ var TIMELINE_STATUS_LABELS = {
125952
126165
  var TIMELINE_STATUSES = new Set(Object.keys(TIMELINE_STATUS_LABELS));
125953
126166
  var STATUS_DEDUPE_WINDOW_MS = 3e3;
125954
126167
  var CHAT_BOTTOM_THRESHOLD_PX = 96;
125955
- var KOKORO_VOICES = [
125956
- {
125957
- id: "am_michael",
125958
- label: "Michael (US ♂)"
125959
- },
125960
- {
125961
- id: "am_adam",
125962
- label: "Adam (US ♂)"
125963
- },
125964
- {
125965
- id: "af_heart",
125966
- label: "Heart (US ♀)"
125967
- },
125968
- {
125969
- id: "af_bella",
125970
- label: "Bella (US ♀)"
125971
- },
125972
- {
125973
- id: "af_nicole",
125974
- label: "Nicole (US ♀)"
125975
- },
125976
- {
125977
- id: "af_sarah",
125978
- label: "Sarah (US ♀)"
125979
- },
125980
- {
125981
- id: "bm_george",
125982
- label: "George (UK ♂)"
125983
- },
125984
- {
125985
- id: "bf_emma",
125986
- label: "Emma (UK ♀)"
125987
- }
125988
- ];
125989
126168
  function StatusMarker({ event }) {
125990
126169
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
125991
126170
  className: "flex items-center justify-center gap-2 py-2 my-1",
@@ -127145,30 +127324,13 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127145
127324
  const fetchOrchestrators = useRelayStore((s) => s.fetchOrchestrators);
127146
127325
  const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled);
127147
127326
  const setVoiceTtsEnabled = useRelayStore((s) => s.setVoiceTtsEnabled);
127148
- const voiceTtsLang = useRelayStore((s) => s.voiceTtsLang);
127149
- const setVoiceTtsLang = useRelayStore((s) => s.setVoiceTtsLang);
127150
- const voiceTtsMode = useRelayStore((s) => s.voiceTtsMode);
127151
- const setVoiceTtsMode = useRelayStore((s) => s.setVoiceTtsMode);
127152
- const voiceTtsKokoroVoice = useRelayStore((s) => s.voiceTtsKokoroVoice);
127153
- const setVoiceTtsKokoroVoice = useRelayStore((s) => s.setVoiceTtsKokoroVoice);
127154
127327
  const voiceInputMode = useRelayStore((s) => s.voiceInputMode);
127155
- const [speechLangs, setSpeechLangs] = (0, import_react.useState)(() => availableSpeechLangs());
127156
- (0, import_react.useEffect)(() => {
127157
- if (!voiceTts.available) return;
127158
- const refresh = () => setSpeechLangs(availableSpeechLangs());
127159
- refresh();
127160
- window.speechSynthesis.addEventListener?.("voiceschanged", refresh);
127161
- return () => window.speechSynthesis.removeEventListener?.("voiceschanged", refresh);
127162
- }, []);
127163
127328
  const fileInputRef = (0, import_react.useRef)(null);
127164
127329
  const pttRecorderRef = (0, import_react.useRef)(null);
127165
127330
  const [micState, setMicState] = (0, import_react.useState)("idle");
127166
127331
  const pendingAttachmentsRef = (0, import_react.useRef)([]);
127167
127332
  const [pendingAttachments, setPendingAttachments] = (0, import_react.useState)([]);
127168
127333
  const [dragActive, setDragActive] = (0, import_react.useState)(false);
127169
- const [terminalOpen, setTerminalOpen] = (0, import_react.useState)(false);
127170
- const [terminalTarget, setTerminalTarget] = (0, import_react.useState)(null);
127171
- const [terminalOpening, setTerminalOpening] = (0, import_react.useState)(false);
127172
127334
  const [filePreview, setFilePreview] = (0, import_react.useState)(null);
127173
127335
  const [floatingPreview, setFloatingPreview] = (0, import_react.useState)(null);
127174
127336
  const [filePreviewError, setFilePreviewError] = (0, import_react.useState)("");
@@ -127189,6 +127351,8 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127189
127351
  selectedInboxThread
127190
127352
  ]);
127191
127353
  const agent = agentsById[selectedInboxThread] || null;
127354
+ const agentFinalizing = typeof agent?.meta?.lifecycleAction === "string" && agent.meta.lifecycleAction.startsWith("finalizing-");
127355
+ const { terminalOpen, terminalTarget, terminalOpening, openTerminal: handleOpenTerminal, closeTerminal: handleCloseTerminal } = useAgentTerminal(agent?.id);
127192
127356
  const agentSpawnRequestId = typeof agent?.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : "";
127193
127357
  const importedHistory = (0, import_react.useMemo)(() => {
127194
127358
  return chatHistoryImports.filter((history) => history.targetAgentId === selectedInboxThread || Boolean(agentSpawnRequestId && history.targetSpawnRequestId === agentSpawnRequestId));
@@ -127269,18 +127433,6 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127269
127433
  agent?.createdAt,
127270
127434
  importedHistory
127271
127435
  ]);
127272
- async function handleOpenTerminal() {
127273
- if (!agent || terminalOpening) return;
127274
- setTerminalOpening(true);
127275
- try {
127276
- setTerminalTarget(await api("POST", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session"));
127277
- setTerminalOpen(true);
127278
- } catch (e) {
127279
- showError("Terminal Failed", e.message);
127280
- } finally {
127281
- setTerminalOpening(false);
127282
- }
127283
- }
127284
127436
  const clearFilePreviewCloseTimer = (0, import_react.useCallback)(() => {
127285
127437
  if (filePreviewCloseTimer.current === null) return;
127286
127438
  window.clearTimeout(filePreviewCloseTimer.current);
@@ -127362,16 +127514,6 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127362
127514
  setFilePreview(null);
127363
127515
  setFilePreviewError("");
127364
127516
  }
127365
- function handleCloseTerminal(open) {
127366
- if (open) {
127367
- setTerminalOpen(true);
127368
- return;
127369
- }
127370
- setTerminalOpen(false);
127371
- const target = terminalTarget;
127372
- setTerminalTarget(null);
127373
- if (agent && target?.mode === "guest") api("DELETE", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session/" + encodeURIComponent(target.session)).catch(() => {});
127374
- }
127375
127517
  async function uploadFiles(files) {
127376
127518
  const list = Array.from(files).filter((file) => file.size > 0);
127377
127519
  if (!list.length) return;
@@ -127417,7 +127559,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127417
127559
  setPendingAttachments([]);
127418
127560
  }
127419
127561
  function handleSend() {
127420
- if (!draft.trim() && readyAttachments.length === 0 || !selectedInboxThread || chatSending || hasPendingUploads) return;
127562
+ if (!draft.trim() && readyAttachments.length === 0 || !selectedInboxThread || chatSending || hasPendingUploads || agentFinalizing) return;
127421
127563
  const attachments = readyAttachments.map((item) => ({
127422
127564
  artifactId: item.artifact.id,
127423
127565
  kind: item.artifact.kind,
@@ -127583,52 +127725,11 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127583
127725
  voiceTts.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
127584
127726
  variant: "ghost",
127585
127727
  size: "icon-sm",
127586
- title: voiceTtsEnabled ? "Speaking agent responses aloud — click to mute" : "Speak agent responses aloud (active chat)",
127728
+ title: voiceTtsEnabled ? "Speaking agent responses aloud — click to mute (engine & voice in Settings)" : "Speak agent responses aloud (active chat) — engine & voice in Settings",
127587
127729
  className: voiceTtsEnabled ? "text-primary" : "",
127588
127730
  onClick: () => setVoiceTtsEnabled(!voiceTtsEnabled),
127589
127731
  children: voiceTtsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Volume2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VolumeX, { className: "w-3.5 h-3.5" })
127590
127732
  }),
127591
- voiceTts.available && voiceTtsEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
127592
- value: voiceTtsMode,
127593
- onChange: (e) => setVoiceTtsMode(e.target.value),
127594
- title: "Voice engine — Kokoro (server, natural) falls back to browser automatically",
127595
- className: "h-7 rounded border border-border bg-background px-1 text-xs",
127596
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
127597
- value: "kokoro",
127598
- children: "Kokoro"
127599
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
127600
- value: "browser",
127601
- children: "Browser"
127602
- })]
127603
- }), voiceTtsMode === "kokoro" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", {
127604
- value: voiceTtsKokoroVoice,
127605
- onChange: (e) => setVoiceTtsKokoroVoice(e.target.value),
127606
- title: "Kokoro voice",
127607
- className: "h-7 rounded border border-border bg-background px-1 text-xs",
127608
- children: [...KOKORO_VOICES, ...KOKORO_VOICES.some((v) => v.id === voiceTtsKokoroVoice) ? [] : [{
127609
- id: voiceTtsKokoroVoice,
127610
- label: voiceTtsKokoroVoice
127611
- }]].map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
127612
- value: v.id,
127613
- children: v.label
127614
- }, v.id))
127615
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
127616
- value: voiceTtsLang,
127617
- onChange: (e) => setVoiceTtsLang(e.target.value),
127618
- title: "Voice language",
127619
- className: "h-7 rounded border border-border bg-background px-1 text-xs",
127620
- children: [[...new Set([
127621
- "en-US",
127622
- ...speechLangs,
127623
- ...voiceTtsLang ? [voiceTtsLang] : []
127624
- ])].sort().map((l) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
127625
- value: l,
127626
- children: l
127627
- }, l)), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
127628
- value: "",
127629
- children: "Browser default"
127630
- })]
127631
- })] }),
127632
127733
  canOpenTerminal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
127633
127734
  variant: "ghost",
127634
127735
  size: "icon-sm",
@@ -127916,7 +128017,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127916
128017
  }),
127917
128018
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
127918
128019
  size: "icon",
127919
- disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending,
128020
+ disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending || agentFinalizing,
127920
128021
  onClick: handleSend,
127921
128022
  className: "shrink-0 mb-0.5 rounded-xl h-[42px] w-[42px]",
127922
128023
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Send, { className: "w-4 h-4" })
@@ -127966,7 +128067,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127966
128067
  })]
127967
128068
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
127968
128069
  size: "icon",
127969
- disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending,
128070
+ disabled: !draft.trim() && readyAttachments.length === 0 || hasPendingUploads || chatSending || agentFinalizing,
127970
128071
  onClick: handleSend,
127971
128072
  className: "rounded-xl h-9 w-9",
127972
128073
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Send, { className: "w-4 h-4" })
@@ -127975,7 +128076,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
127975
128076
  }),
127976
128077
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
127977
128078
  className: "text-xs text-muted-foreground mt-1.5 hidden md:block",
127978
- children: "Enter to send · Shift+Enter for newline"
128079
+ children: agentFinalizing ? `${agent ? displayName(agent) : "Agent"} is wrapping up (${(agent?.meta?.lifecycleAction).slice(11)}) — messaging paused` : "Enter to send · Shift+Enter for newline"
127979
128080
  })
127980
128081
  ]
127981
128082
  })
@@ -128104,13 +128205,10 @@ function AgentCard({ agent }) {
128104
128205
  const openPairInvite = useRelayStore((s) => s.openPairInvite);
128105
128206
  const openRename = useRelayStore((s) => s.openRename);
128106
128207
  const openConfirm = useRelayStore((s) => s.openConfirm);
128107
- const showError = useRelayStore((s) => s.showError);
128108
128208
  const doAgentAction = useRelayStore((s) => s.doAgentAction);
128109
128209
  const doDeleteAgent = useRelayStore((s) => s.doDeleteAgent);
128110
128210
  const openFilesForAgent = useRelayStore((s) => s.openFilesForAgent);
128111
- const [terminalOpen, setTerminalOpen] = (0, import_react.useState)(false);
128112
- const [terminalTarget, setTerminalTarget] = (0, import_react.useState)(null);
128113
- const [terminalOpening, setTerminalOpening] = (0, import_react.useState)(false);
128211
+ const { terminalOpen, terminalTarget, terminalOpening, openTerminal: handleOpenTerminal, closeTerminal: handleCloseTerminal } = useAgentTerminal(agent.id);
128114
128212
  const pairsMap = usePairsByAgentId();
128115
128213
  const rawAttention = useAgentAttention(agent);
128116
128214
  const pair = pairsMap[agent.id] || null;
@@ -128124,28 +128222,6 @@ function AgentCard({ agent }) {
128124
128222
  const canOpenTerminal = Boolean(agent.providerCapabilities?.terminal?.live?.read || agent.providerCapabilities?.terminal?.attach?.create);
128125
128223
  const canWriteTerminal = Boolean(agent.providerCapabilities?.terminal?.live?.write || agent.providerCapabilities?.model?.provider === "claude");
128126
128224
  const canForget = agentCanBeForgotten(agent);
128127
- async function handleOpenTerminal() {
128128
- if (terminalOpening) return;
128129
- setTerminalOpening(true);
128130
- try {
128131
- setTerminalTarget(await api("POST", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session"));
128132
- setTerminalOpen(true);
128133
- } catch (e) {
128134
- showError("Terminal Failed", e.message);
128135
- } finally {
128136
- setTerminalOpening(false);
128137
- }
128138
- }
128139
- function handleCloseTerminal(open) {
128140
- if (open) {
128141
- setTerminalOpen(true);
128142
- return;
128143
- }
128144
- setTerminalOpen(false);
128145
- const target = terminalTarget;
128146
- setTerminalTarget(null);
128147
- if (target?.mode === "guest") api("DELETE", "/agents/" + encodeURIComponent(agent.id) + "/terminal-session/" + encodeURIComponent(target.session)).catch(() => {});
128148
- }
128149
128225
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
128150
128226
  className: "group hover:border-zinc-600 transition-colors cursor-pointer",
128151
128227
  onClick: () => openAgentDetail(agent),
@@ -154222,30 +154298,101 @@ function WorkspaceSettings() {
154222
154298
  });
154223
154299
  }
154224
154300
  function VoiceSettings() {
154301
+ const voiceTtsEnabled = useRelayStore((s) => s.voiceTtsEnabled);
154302
+ const setVoiceTtsEnabled = useRelayStore((s) => s.setVoiceTtsEnabled);
154303
+ const voiceTtsMode = useRelayStore((s) => s.voiceTtsMode);
154304
+ const setVoiceTtsMode = useRelayStore((s) => s.setVoiceTtsMode);
154305
+ const voiceTtsKokoroVoice = useRelayStore((s) => s.voiceTtsKokoroVoice);
154306
+ const setVoiceTtsKokoroVoice = useRelayStore((s) => s.setVoiceTtsKokoroVoice);
154307
+ const voiceTtsBrowserVoice = useRelayStore((s) => s.voiceTtsBrowserVoice);
154308
+ const setVoiceTtsBrowserVoice = useRelayStore((s) => s.setVoiceTtsBrowserVoice);
154225
154309
  const voiceInputMode = useRelayStore((s) => s.voiceInputMode);
154226
154310
  const setVoiceInputMode = useRelayStore((s) => s.setVoiceInputMode);
154311
+ const [browserVoices, setBrowserVoices] = (0, import_react.useState)(() => availableSpeechVoices());
154312
+ (0, import_react.useEffect)(() => {
154313
+ if (typeof window === "undefined" || !("speechSynthesis" in window)) return;
154314
+ const refresh = () => setBrowserVoices(availableSpeechVoices());
154315
+ refresh();
154316
+ window.speechSynthesis.addEventListener?.("voiceschanged", refresh);
154317
+ return () => window.speechSynthesis.removeEventListener?.("voiceschanged", refresh);
154318
+ }, []);
154319
+ const kokoroOptions = KOKORO_VOICES.some((v) => v.id === voiceTtsKokoroVoice) ? KOKORO_VOICES : [...KOKORO_VOICES, {
154320
+ id: voiceTtsKokoroVoice,
154321
+ label: voiceTtsKokoroVoice
154322
+ }];
154227
154323
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
154228
154324
  className: "space-y-3 rounded-lg border p-4",
154229
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
154230
- className: "text-sm font-semibold",
154231
- children: "Voice"
154232
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
154233
- className: "text-xs text-muted-foreground",
154234
- children: "How push-to-talk behaves in chat."
154235
- })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
154236
- label: "Push-to-talk input",
154237
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
154238
- value: voiceInputMode,
154239
- onValueChange: (v) => setVoiceInputMode(v),
154240
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154241
- value: "compose",
154242
- children: "Fill the message box (review, then Enter)"
154243
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154244
- value: "autosend",
154245
- children: "Send immediately (speak-and-send)"
154246
- })] })]
154325
+ children: [
154326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
154327
+ className: "flex items-center justify-between gap-2",
154328
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
154329
+ className: "flex items-center gap-2",
154330
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Volume2, { className: "w-4 h-4" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
154331
+ className: "text-sm font-semibold",
154332
+ children: "Voice"
154333
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
154334
+ className: "text-xs text-muted-foreground",
154335
+ children: "Speak agent responses aloud in the active chat, and how push-to-talk behaves."
154336
+ })] })]
154337
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch, {
154338
+ checked: voiceTtsEnabled,
154339
+ onCheckedChange: setVoiceTtsEnabled
154340
+ })]
154341
+ }),
154342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
154343
+ label: "Speech engine",
154344
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
154345
+ value: voiceTtsMode,
154346
+ onValueChange: (v) => setVoiceTtsMode(v),
154347
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154348
+ value: "kokoro",
154349
+ children: "Kokoro (server, natural — falls back to browser)"
154350
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154351
+ value: "browser",
154352
+ children: "Browser (Web Speech API)"
154353
+ })] })]
154354
+ })
154355
+ }),
154356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
154357
+ label: "Kokoro voice",
154358
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
154359
+ value: voiceTtsKokoroVoice,
154360
+ onValueChange: setVoiceTtsKokoroVoice,
154361
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: kokoroOptions.map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154362
+ value: v.id,
154363
+ children: v.label
154364
+ }, v.id)) })]
154365
+ })
154366
+ }),
154367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
154368
+ label: "Browser voice",
154369
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
154370
+ value: voiceTtsBrowserVoice || "__default__",
154371
+ onValueChange: (v) => setVoiceTtsBrowserVoice(v === "__default__" ? "" : v),
154372
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154373
+ value: "__default__",
154374
+ children: "System default"
154375
+ }), browserVoices.map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154376
+ value: v.uri,
154377
+ children: v.label
154378
+ }, v.uri))] })]
154379
+ })
154380
+ }),
154381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
154382
+ label: "Push-to-talk input",
154383
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
154384
+ value: voiceInputMode,
154385
+ onValueChange: (v) => setVoiceInputMode(v),
154386
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154387
+ value: "compose",
154388
+ children: "Fill the message box (review, then Enter)"
154389
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
154390
+ value: "autosend",
154391
+ children: "Send immediately (speak-and-send)"
154392
+ })] })]
154393
+ })
154247
154394
  })
154248
- })]
154395
+ ]
154249
154396
  });
154250
154397
  }
154251
154398
  var EFFORTS = [
@@ -159927,6 +160074,10 @@ if ("serviceWorker" in navigator) {
159927
160074
  min-width: calc(var(--spacing) * 0);
159928
160075
  }
159929
160076
 
160077
+ .min-w-4 {
160078
+ min-width: calc(var(--spacing) * 4);
160079
+ }
160080
+
159930
160081
  .min-w-5 {
159931
160082
  min-width: calc(var(--spacing) * 5);
159932
160083
  }
@@ -160981,6 +161132,16 @@ if ("serviceWorker" in navigator) {
160981
161132
  }
160982
161133
  }
160983
161134
 
161135
+ .bg-background\/95 {
161136
+ background-color: var(--background);
161137
+ }
161138
+
161139
+ @supports (color: color-mix(in lab, red, red)) {
161140
+ .bg-background\/95 {
161141
+ background-color: color-mix(in oklab, var(--background) 95%, transparent);
161142
+ }
161143
+ }
161144
+
160984
161145
  .bg-black\/0 {
160985
161146
  background-color: #0000;
160986
161147
  }
@@ -161851,10 +162012,6 @@ if ("serviceWorker" in navigator) {
161851
162012
  font-size: .8rem;
161852
162013
  }
161853
162014
 
161854
- .text-\[8px\] {
161855
- font-size: 8px;
161856
- }
161857
-
161858
162015
  .text-\[9px\] {
161859
162016
  font-size: 9px;
161860
162017
  }
@@ -163469,6 +163626,10 @@ if ("serviceWorker" in navigator) {
163469
163626
  }
163470
163627
 
163471
163628
  @media (min-width: 40rem) {
163629
+ .sm\:block {
163630
+ display: block;
163631
+ }
163632
+
163472
163633
  .sm\:flex {
163473
163634
  display: flex;
163474
163635
  }