@burdenoff/microfe-vibecontrols 2026.514.3 → 2026.514.5

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.
@@ -1,14 +1,14 @@
1
1
  import { useTr as e } from "../../shared/hooks/useTr.js";
2
2
  import { useVibeNavigate as t } from "../../providers/VibeControlsProvider.js";
3
3
  import { useAgentManagerStore as n } from "../../store/agentManagerStore.js";
4
- import { AITemplatesTab as r } from "../ai/AITemplatesTab.js";
5
- import { AIContextsTab as i } from "../ai/AIContextsTab.js";
6
- import { AIPromptsTab as a } from "../ai/AIPromptsTab.js";
7
- import { AITasksTab as o } from "../ai/AITasksTab.js";
8
- import { AIStatsTab as s } from "../ai/AIStatsTab.js";
9
- import { AIPlaygroundTab as c } from "../ai/AIPlaygroundTab.js";
10
- import { BridleConfigPanel as l } from "./BridleConfigPanel.js";
11
- import { AIProviderCredentialsCard as u } from "../ai/AIProviderCredentialsCard.js";
4
+ import { AIProviderCredentialsCard as r } from "../ai/AIProviderCredentialsCard.js";
5
+ import { AITemplatesTab as i } from "../ai/AITemplatesTab.js";
6
+ import { AIContextsTab as a } from "../ai/AIContextsTab.js";
7
+ import { AIPromptsTab as o } from "../ai/AIPromptsTab.js";
8
+ import { AITasksTab as s } from "../ai/AITasksTab.js";
9
+ import { AIStatsTab as c } from "../ai/AIStatsTab.js";
10
+ import { AIPlaygroundTab as l } from "../ai/AIPlaygroundTab.js";
11
+ import { BridleConfigPanel as u } from "./BridleConfigPanel.js";
12
12
  import { useState as d } from "react";
13
13
  import { BarChart3 as f, BookOpen as p, ExternalLink as m, FileText as h, ListChecks as g, MessageSquare as _, Play as v, Settings as y, X as b } from "lucide-react";
14
14
  import { AnimatePresence as x, motion as S, useReducedMotion as C } from "framer-motion";
@@ -63,15 +63,15 @@ function O({ agent: f }) {
63
63
  }
64
64
  function A() {
65
65
  switch (p) {
66
- case "templates": return /* @__PURE__ */ T(r, { agentId: f.id });
67
- case "contexts": return /* @__PURE__ */ T(i, { agent: f });
68
- case "prompts": return /* @__PURE__ */ T(a, { agent: f });
69
- case "tasks": return /* @__PURE__ */ T(o, {});
70
- case "stats": return /* @__PURE__ */ T(s, { agent: f });
71
- case "playground": return /* @__PURE__ */ T(c, { agent: f });
66
+ case "templates": return /* @__PURE__ */ T(i, { agentId: f.id });
67
+ case "contexts": return /* @__PURE__ */ T(a, { agent: f });
68
+ case "prompts": return /* @__PURE__ */ T(o, { agent: f });
69
+ case "tasks": return /* @__PURE__ */ T(s, {});
70
+ case "stats": return /* @__PURE__ */ T(c, { agent: f });
71
+ case "playground": return /* @__PURE__ */ T(l, { agent: f });
72
72
  case "aiKeys":
73
- case "config": return /* @__PURE__ */ T(u, { agent: f });
74
- case "bridle": return /* @__PURE__ */ T(l, { agent: f });
73
+ case "config": return /* @__PURE__ */ T(r, { agent: f });
74
+ case "bridle": return /* @__PURE__ */ T(u, { agent: f });
75
75
  default: return null;
76
76
  }
77
77
  }
@@ -1,31 +1,32 @@
1
1
  import { useTr as e } from "../../shared/hooks/useTr.js";
2
2
  import { EmptyState as t } from "../shared/EmptyState.js";
3
3
  import "../shared/index.js";
4
- import { useAgentManagerStore as n } from "../../store/agentManagerStore.js";
5
- import { AgentManagerTabBar as r } from "./AgentManagerTabBar.js";
6
- import { AgentManagerChatPane as i } from "./AgentManagerChatPane.js";
7
- import { AgentManagerAdminBar as a } from "./AgentManagerAdminBar.js";
8
- import { SessionSearchDialog as o } from "./dialogs/SessionSearchDialog.js";
9
- import { useCallback as s, useEffect as c, useState as l } from "react";
10
- import { MessageSquarePlus as u, X as d } from "lucide-react";
11
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
4
+ import { createSession as n } from "../../services/aiApi.js";
5
+ import { useAgentManagerStore as r } from "../../store/agentManagerStore.js";
6
+ import { AgentManagerTabBar as i } from "./AgentManagerTabBar.js";
7
+ import { AgentManagerChatPane as a } from "./AgentManagerChatPane.js";
8
+ import { AgentManagerAdminBar as o } from "./AgentManagerAdminBar.js";
9
+ import { SessionSearchDialog as s } from "./dialogs/SessionSearchDialog.js";
10
+ import { useCallback as c, useEffect as l, useRef as u, useState as d } from "react";
11
+ import { MessageSquarePlus as f, X as p } from "lucide-react";
12
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
12
13
  //#region src/components/agent-manager/AgentManagerCenter.tsx
13
- var m = [
14
+ var g = [
14
15
  {
15
- keys: "Ctrl+Shift+N",
16
+ keys: "Ctrl/Cmd+Shift+N",
16
17
  description: "New session"
17
18
  },
18
19
  {
19
- keys: "Ctrl+K",
20
+ keys: "Ctrl/Cmd+Shift+K",
20
21
  description: "Search sessions"
21
22
  },
22
23
  {
23
- keys: "Ctrl+F",
24
+ keys: "Ctrl/Cmd+Shift+F",
24
25
  description: "In-session search"
25
26
  },
26
27
  {
27
28
  keys: "Escape",
28
- description: "Cancel"
29
+ description: "Cancel / close modal"
29
30
  },
30
31
  {
31
32
  keys: "Enter",
@@ -35,92 +36,129 @@ var m = [
35
36
  keys: "Shift+Enter",
36
37
  description: "New line"
37
38
  }
38
- ], h = ({ agent: h }) => {
39
- let g = n((e) => e.tabs), _ = n((e) => e.activeTabSessionId), v = n((e) => e.openSession), y = n((e) => e.setActiveTab), b = e(), [x, S] = l(!1), [C, w] = l(!1), T = () => {
40
- v(`session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`);
41
- }, E = s(() => S(!0), []);
42
- return c(() => {
43
- _ && g.some((e) => e.sessionId === _) && y(_);
39
+ ], _ = ({ agent: _ }) => {
40
+ let v = r((e) => e.tabs), y = r((e) => e.activeTabSessionId), b = r((e) => e.openSession), x = r((e) => e.setActiveTab), S = e(), [C, w] = d(!1), [T, E] = d(!1), D = u(null), O = r((e) => e.bumpSessionsRefresh), k = c(async () => {
41
+ try {
42
+ let e = await n(_, {
43
+ name: `Session ${v.length + 1}`,
44
+ agentType: "claude",
45
+ config: {
46
+ model: "default",
47
+ mode: "cli",
48
+ tags: []
49
+ }
50
+ });
51
+ b(e.id, e.name), O();
52
+ } catch (e) {
53
+ console.error("[AgentManagerCenter] createSession failed:", e);
54
+ }
44
55
  }, [
45
56
  _,
57
+ v.length,
58
+ b,
59
+ O
60
+ ]), A = c(() => w(!0), []);
61
+ return l(() => {
62
+ y && v.some((e) => e.sessionId === y) && x(y);
63
+ }, [
46
64
  y,
47
- g
48
- ]), /* @__PURE__ */ p("div", {
65
+ x,
66
+ v
67
+ ]), l(() => {
68
+ let e = D.current;
69
+ if (!e) return;
70
+ let t = (t) => {
71
+ if (!e.contains(document.activeElement) && document.activeElement !== e) return;
72
+ let n = t.ctrlKey || t.metaKey;
73
+ if (n && t.shiftKey && (t.key === "N" || t.key === "n")) {
74
+ t.preventDefault(), k();
75
+ return;
76
+ }
77
+ if (n && t.shiftKey && (t.key === "K" || t.key === "k")) {
78
+ t.preventDefault(), A();
79
+ return;
80
+ }
81
+ n && t.shiftKey && (t.key === "F" || t.key === "f") && (t.preventDefault(), e.querySelector("[data-testid=\"agent-manager-in-session-search\"]")?.click());
82
+ };
83
+ return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
84
+ }, [k, A]), /* @__PURE__ */ h("div", {
85
+ ref: D,
49
86
  className: "flex flex-col h-full min-h-0",
87
+ tabIndex: -1,
50
88
  children: [
51
- /* @__PURE__ */ f(r, {
52
- agent: h,
53
- onSearch: E,
54
- onShowShortcuts: () => w(!0)
89
+ /* @__PURE__ */ m(i, {
90
+ agent: _,
91
+ onSearch: A,
92
+ onShowShortcuts: () => E(!0)
55
93
  }),
56
- /* @__PURE__ */ f("div", {
94
+ /* @__PURE__ */ m("div", {
57
95
  className: "flex-1 min-h-0 overflow-hidden",
58
- children: _ && g.some((e) => e.sessionId === _) ? /* @__PURE__ */ f(i, {
59
- sessionId: _,
60
- agentId: h.id
61
- }, _) : /* @__PURE__ */ f("div", {
96
+ children: y && v.some((e) => e.sessionId === y) ? /* @__PURE__ */ m(a, {
97
+ sessionId: y,
98
+ agentId: _.id
99
+ }, y) : /* @__PURE__ */ m("div", {
62
100
  className: "flex-1 flex items-center justify-center h-full",
63
- children: /* @__PURE__ */ f(t, {
64
- icon: /* @__PURE__ */ f(u, { className: "w-8 h-8 text-text-secondary" }),
65
- title: b("vibecontrols.agentManager.center.noActiveSession", "No active session"),
66
- description: b("vibecontrols.agentManager.center.createSessionDesc", "Create a new session to start interacting with the AI agent."),
67
- action: /* @__PURE__ */ f("button", {
68
- onClick: T,
101
+ children: /* @__PURE__ */ m(t, {
102
+ icon: /* @__PURE__ */ m(f, { className: "w-8 h-8 text-text-secondary" }),
103
+ title: S("vibecontrols.agentManager.center.noActiveSession", "No active session"),
104
+ description: S("vibecontrols.agentManager.center.createSessionDesc", "Create a new session to start interacting with the AI agent."),
105
+ action: /* @__PURE__ */ m("button", {
106
+ onClick: k,
69
107
  className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
70
- children: b("vibecontrols.agentManager.center.createNewSession", "Create a new session")
108
+ children: S("vibecontrols.agentManager.center.createNewSession", "Create a new session")
71
109
  })
72
110
  })
73
111
  })
74
112
  }),
75
- /* @__PURE__ */ f("div", {
113
+ /* @__PURE__ */ m("div", {
76
114
  className: "flex-shrink-0",
77
- children: /* @__PURE__ */ f(a, { agent: h })
115
+ children: /* @__PURE__ */ m(o, { agent: _ })
78
116
  }),
79
- /* @__PURE__ */ f(o, {
80
- isOpen: x,
81
- agent: h,
82
- onClose: () => S(!1)
117
+ /* @__PURE__ */ m(s, {
118
+ isOpen: C,
119
+ agent: _,
120
+ onClose: () => w(!1)
83
121
  }),
84
- C && /* @__PURE__ */ p("div", {
122
+ T && /* @__PURE__ */ h("div", {
85
123
  className: "fixed inset-0 z-50 flex items-center justify-center",
86
- children: [/* @__PURE__ */ f("div", {
124
+ children: [/* @__PURE__ */ m("div", {
87
125
  className: "absolute inset-0 bg-bg-overlay",
88
- onClick: () => w(!1)
89
- }), /* @__PURE__ */ p("div", {
126
+ onClick: () => E(!1)
127
+ }), /* @__PURE__ */ h("div", {
90
128
  className: "relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl",
91
129
  children: [
92
- /* @__PURE__ */ p("div", {
130
+ /* @__PURE__ */ h("div", {
93
131
  className: "flex items-center justify-between px-6 py-4 border-b border-border-default",
94
- children: [/* @__PURE__ */ f("h3", {
132
+ children: [/* @__PURE__ */ m("h3", {
95
133
  className: "text-lg font-semibold text-text-primary",
96
- children: b("vibecontrols.agentManager.center.keyboardShortcuts", "Keyboard Shortcuts")
97
- }), /* @__PURE__ */ f("button", {
134
+ children: S("vibecontrols.agentManager.center.keyboardShortcuts", "Keyboard Shortcuts")
135
+ }), /* @__PURE__ */ m("button", {
98
136
  type: "button",
99
- onClick: () => w(!1),
137
+ onClick: () => E(!1),
100
138
  className: "p-1 rounded hover:bg-bg-sunken transition-colors",
101
- children: /* @__PURE__ */ f(d, { className: "w-5 h-5 text-text-secondary" })
139
+ children: /* @__PURE__ */ m(p, { className: "w-5 h-5 text-text-secondary" })
102
140
  })]
103
141
  }),
104
- /* @__PURE__ */ f("div", {
142
+ /* @__PURE__ */ m("div", {
105
143
  className: "px-6 py-4 space-y-2",
106
- children: m.map((e) => /* @__PURE__ */ p("div", {
144
+ children: g.map((e) => /* @__PURE__ */ h("div", {
107
145
  className: "flex items-center justify-between py-1",
108
- children: [/* @__PURE__ */ f("span", {
146
+ children: [/* @__PURE__ */ m("span", {
109
147
  className: "text-sm text-text-secondary",
110
148
  children: e.description
111
- }), /* @__PURE__ */ f("kbd", {
149
+ }), /* @__PURE__ */ m("kbd", {
112
150
  className: "px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary",
113
151
  children: e.keys
114
152
  })]
115
153
  }, e.keys))
116
154
  }),
117
- /* @__PURE__ */ f("div", {
155
+ /* @__PURE__ */ m("div", {
118
156
  className: "px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg",
119
- children: /* @__PURE__ */ f("button", {
157
+ children: /* @__PURE__ */ m("button", {
120
158
  type: "button",
121
- onClick: () => w(!1),
159
+ onClick: () => E(!1),
122
160
  className: "w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors",
123
- children: b("vibecontrols.agentManager.center.close", "Close")
161
+ children: S("vibecontrols.agentManager.center.close", "Close")
124
162
  })
125
163
  })
126
164
  ]
@@ -130,6 +168,6 @@ var m = [
130
168
  });
131
169
  };
132
170
  //#endregion
133
- export { h as AgentManagerCenter };
171
+ export { _ as AgentManagerCenter };
134
172
 
135
173
  //# sourceMappingURL=AgentManagerCenter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentManagerCenter.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerCenter.tsx"],"sourcesContent":["/**\n * Center container for the Agent Manager.\n *\n * Renders the tab bar at top and the active session's chat pane below.\n * Shows a welcome/empty state when no tabs are open.\n */\n\nimport { useState, useCallback, useEffect, type FC } from 'react';\nimport { MessageSquarePlus, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { EmptyState } from '@/components/shared';\nimport { AgentManagerTabBar } from './AgentManagerTabBar';\nimport { AgentManagerChatPane } from './AgentManagerChatPane';\nimport { AgentManagerAdminBar } from './AgentManagerAdminBar';\nimport { SessionSearchDialog } from './dialogs/SessionSearchDialog';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type { AgentRef } from '@/services/aiApi';\n\n/* ────────────────────────────────────────────────────────────────────── */\n/* Keyboard shortcuts reference */\n/* ────────────────────────────────────────────────────────────────────── */\n\nconst SHORTCUTS: Array<{ keys: string; description: string }> = [\n { keys: 'Ctrl+Shift+N', description: 'New session' },\n { keys: 'Ctrl+K', description: 'Search sessions' },\n { keys: 'Ctrl+F', description: 'In-session search' },\n { keys: 'Escape', description: 'Cancel' },\n { keys: 'Enter', description: 'Send message' },\n { keys: 'Shift+Enter', description: 'New line' },\n];\n\ninterface AgentManagerCenterProps {\n agent: AgentRef;\n}\n\nexport const AgentManagerCenter: FC<AgentManagerCenterProps> = ({ agent }) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const tr = useTr();\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n\n const handleCreateSession = () => {\n const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n openSession(sessionId);\n };\n\n const handleOpenSearch = useCallback(() => setSearchOpen(true), []);\n\n useEffect(() => {\n if (activeTabSessionId && tabs.some((tab) => tab.sessionId === activeTabSessionId)) {\n setActiveTab(activeTabSessionId);\n }\n }, [activeTabSessionId, setActiveTab, tabs]);\n\n return (\n <div className=\"flex flex-col h-full min-h-0\">\n {/* Tab bar — occupies full row width, search/help live inside the tabBar area */}\n <AgentManagerTabBar\n agent={agent}\n onSearch={handleOpenSearch}\n onShowShortcuts={() => setShortcutsOpen(true)}\n />\n\n {/* Active session or empty state */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {activeTabSessionId && tabs.some((t) => t.sessionId === activeTabSessionId) ? (\n <AgentManagerChatPane\n key={activeTabSessionId}\n sessionId={activeTabSessionId}\n agentId={agent.id}\n />\n ) : (\n <div className=\"flex-1 flex items-center justify-center h-full\">\n <EmptyState\n icon={<MessageSquarePlus className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('vibecontrols.agentManager.center.noActiveSession', 'No active session')}\n description={tr(\n 'vibecontrols.agentManager.center.createSessionDesc',\n 'Create a new session to start interacting with the AI agent.'\n )}\n action={\n <button\n onClick={handleCreateSession}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.createNewSession', 'Create a new session')}\n </button>\n }\n />\n </div>\n )}\n </div>\n\n {/* Admin bar at bottom */}\n <div className=\"flex-shrink-0\">\n <AgentManagerAdminBar agent={agent} />\n </div>\n\n {/* Session search dialog */}\n <SessionSearchDialog isOpen={searchOpen} agent={agent} onClose={() => setSearchOpen(false)} />\n\n {/* Keyboard shortcuts modal */}\n {shortcutsOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-bg-overlay\" onClick={() => setShortcutsOpen(false)} />\n <div className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.center.keyboardShortcuts', 'Keyboard Shortcuts')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"px-6 py-4 space-y-2\">\n {SHORTCUTS.map((s) => (\n <div key={s.keys} className=\"flex items-center justify-between py-1\">\n <span className=\"text-sm text-text-secondary\">{s.description}</span>\n <kbd className=\"px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary\">\n {s.keys}\n </kbd>\n </div>\n ))}\n </div>\n <div className=\"px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentManagerCenter.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerCenter.tsx"],"sourcesContent":["/**\n * Center container for the Agent Manager.\n *\n * Renders the tab bar at top and the active session's chat pane below.\n * Shows a welcome/empty state when no tabs are open.\n */\n\nimport { useState, useCallback, useEffect, useRef, type FC } from 'react';\nimport { MessageSquarePlus, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { EmptyState } from '@/components/shared';\nimport { AgentManagerTabBar } from './AgentManagerTabBar';\nimport { AgentManagerChatPane } from './AgentManagerChatPane';\nimport { AgentManagerAdminBar } from './AgentManagerAdminBar';\nimport { SessionSearchDialog } from './dialogs/SessionSearchDialog';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { createSession, type AgentRef } from '@/services/aiApi';\n\n/* ────────────────────────────────────────────────────────────────────── */\n/* Keyboard shortcuts reference */\n/* ────────────────────────────────────────────────────────────────────── */\n\nconst SHORTCUTS: Array<{ keys: string; description: string }> = [\n { keys: 'Ctrl/Cmd+Shift+N', description: 'New session' },\n { keys: 'Ctrl/Cmd+Shift+K', description: 'Search sessions' },\n { keys: 'Ctrl/Cmd+Shift+F', description: 'In-session search' },\n { keys: 'Escape', description: 'Cancel / close modal' },\n { keys: 'Enter', description: 'Send message' },\n { keys: 'Shift+Enter', description: 'New line' },\n];\n\ninterface AgentManagerCenterProps {\n agent: AgentRef;\n}\n\nexport const AgentManagerCenter: FC<AgentManagerCenterProps> = ({ agent }) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const tr = useTr();\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const handleCreateSession = useCallback(async () => {\n try {\n const session = await createSession(agent, {\n name: `Session ${tabs.length + 1}`,\n agentType: 'claude',\n config: { model: 'default', mode: 'cli', tags: [] },\n });\n openSession(session.id, session.name);\n bumpSessionsRefresh();\n } catch (err) {\n console.error('[AgentManagerCenter] createSession failed:', err);\n }\n }, [agent, tabs.length, openSession, bumpSessionsRefresh]);\n\n const handleOpenSearch = useCallback(() => setSearchOpen(true), []);\n\n useEffect(() => {\n if (activeTabSessionId && tabs.some((tab) => tab.sessionId === activeTabSessionId)) {\n setActiveTab(activeTabSessionId);\n }\n }, [activeTabSessionId, setActiveTab, tabs]);\n\n // Wire the shortcuts advertised in the SHORTCUTS modal. Scoped to the\n // Agent Manager container so they don't clash with shortcuts on other\n // pages. Each shortcut uses Shift to avoid colliding with built-in\n // browser shortcuts (Ctrl+N opens a window, Ctrl+F opens find).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const handler = (e: KeyboardEvent) => {\n if (!root.contains(document.activeElement) && document.activeElement !== root) return;\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.shiftKey && (e.key === 'N' || e.key === 'n')) {\n e.preventDefault();\n void handleCreateSession();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'K' || e.key === 'k')) {\n e.preventDefault();\n handleOpenSearch();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'F' || e.key === 'f')) {\n e.preventDefault();\n const inSession = root.querySelector(\n '[data-testid=\"agent-manager-in-session-search\"]'\n ) as HTMLButtonElement | null;\n inSession?.click();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [handleCreateSession, handleOpenSearch]);\n\n return (\n <div ref={rootRef} className=\"flex flex-col h-full min-h-0\" tabIndex={-1}>\n {/* Tab bar — occupies full row width, search/help live inside the tabBar area */}\n <AgentManagerTabBar\n agent={agent}\n onSearch={handleOpenSearch}\n onShowShortcuts={() => setShortcutsOpen(true)}\n />\n\n {/* Active session or empty state */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {activeTabSessionId && tabs.some((t) => t.sessionId === activeTabSessionId) ? (\n <AgentManagerChatPane\n key={activeTabSessionId}\n sessionId={activeTabSessionId}\n agentId={agent.id}\n />\n ) : (\n <div className=\"flex-1 flex items-center justify-center h-full\">\n <EmptyState\n icon={<MessageSquarePlus className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('vibecontrols.agentManager.center.noActiveSession', 'No active session')}\n description={tr(\n 'vibecontrols.agentManager.center.createSessionDesc',\n 'Create a new session to start interacting with the AI agent.'\n )}\n action={\n <button\n onClick={handleCreateSession}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.createNewSession', 'Create a new session')}\n </button>\n }\n />\n </div>\n )}\n </div>\n\n {/* Admin bar at bottom */}\n <div className=\"flex-shrink-0\">\n <AgentManagerAdminBar agent={agent} />\n </div>\n\n {/* Session search dialog */}\n <SessionSearchDialog isOpen={searchOpen} agent={agent} onClose={() => setSearchOpen(false)} />\n\n {/* Keyboard shortcuts modal */}\n {shortcutsOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-bg-overlay\" onClick={() => setShortcutsOpen(false)} />\n <div className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.center.keyboardShortcuts', 'Keyboard Shortcuts')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"px-6 py-4 space-y-2\">\n {SHORTCUTS.map((s) => (\n <div key={s.keys} className=\"flex items-center justify-between py-1\">\n <span className=\"text-sm text-text-secondary\">{s.description}</span>\n <kbd className=\"px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary\">\n {s.keys}\n </kbd>\n </div>\n ))}\n </div>\n <div className=\"px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -7,27 +7,28 @@ import { useAgentManagerStreaming as a } from "../../hooks/useAgentManagerStream
7
7
  import { AgentManagerMessageList as o } from "./AgentManagerMessageList.js";
8
8
  import { AgentManagerInputArea as s } from "./AgentManagerInputArea.js";
9
9
  import { PluginInstallModal as c } from "./PluginInstallModal.js";
10
- import { useCallback as l, useEffect as u, useMemo as d, useState as f } from "react";
11
- import { PackagePlus as p, Search as m, X as h } from "lucide-react";
12
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
10
+ import { AIProviderCredentialsCard as l } from "../ai/AIProviderCredentialsCard.js";
11
+ import { useCallback as u, useEffect as d, useMemo as f, useState as p } from "react";
12
+ import { PackagePlus as m, Search as h, X as g } from "lucide-react";
13
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
13
14
  //#region src/components/agent-manager/AgentManagerChatPane.tsx
14
- var v = ({ sessionId: v, agentId: y }) => {
15
- let [b, x] = f(!1), [S, C] = f(!1), [w, T] = f(""), [E, D] = f(!1), O = e(), { agents: k } = t(), A = d(() => k.find((e) => e.id === y) ?? null, [k, y]), j = i((e) => e.sessionStates[v]), M = i((e) => e.addMessage), N = j?.messages ?? [], { active: P } = n(A?.id ?? null), F = d(() => ({
15
+ var y = ({ sessionId: l, agentId: y }) => {
16
+ let [x, S] = p(!1), [C, w] = p(!1), [T, E] = p(""), [D, O] = p(!1), k = e(), { agents: A } = t(), j = f(() => A.find((e) => e.id === y) ?? null, [A, y]), M = i((e) => e.sessionStates[l]), N = i((e) => e.addMessage), P = M?.messages ?? [], { active: F } = n(j?.id ?? null), I = f(() => ({
16
17
  id: y,
17
- profile: P
18
- }), [y, P]), { sendMessage: I, cancelStream: L, isStreaming: R } = a({
19
- agent: F,
20
- sessionId: v
21
- }), z = l(() => {
22
- C((e) => (e && T(""), !e));
18
+ profile: F
19
+ }), [y, F]), { sendMessage: L, cancelStream: R, isStreaming: z } = a({
20
+ agent: I,
21
+ sessionId: l
22
+ }), B = u(() => {
23
+ w((e) => (e && E(""), !e));
23
24
  }, []);
24
- return u(() => {
25
+ return d(() => {
25
26
  let e = !1;
26
27
  return (async () => {
27
- if (!(N.length > 0)) {
28
- x(!0);
28
+ if (!(P.length > 0)) {
29
+ S(!0);
29
30
  try {
30
- let t = await r(F, v, { types: ["input", "output"] });
31
+ let t = await r(I, l, { types: ["input", "output"] });
31
32
  if (e || t.length === 0) return;
32
33
  let n = /* @__PURE__ */ new Set();
33
34
  for (let e of t) {
@@ -37,7 +38,7 @@ var v = ({ sessionId: v, agentId: y }) => {
37
38
  e.model ?? "",
38
39
  i
39
40
  ].join("\0");
40
- n.has(a) || (n.add(a), M(v, {
41
+ n.has(a) || (n.add(a), N(l, {
41
42
  id: e.id,
42
43
  role: e.type === "input" ? "user" : "assistant",
43
44
  content: t,
@@ -50,85 +51,124 @@ var v = ({ sessionId: v, agentId: y }) => {
50
51
  }));
51
52
  }
52
53
  } catch {} finally {
53
- e || x(!1);
54
+ e || S(!1);
54
55
  }
55
56
  }
56
57
  })(), () => {
57
58
  e = !0;
58
59
  };
59
- }, [y, v]), /* @__PURE__ */ _("div", {
60
+ }, [y, l]), /* @__PURE__ */ v("div", {
60
61
  className: "flex flex-col h-full min-h-0",
61
62
  "data-testid": "agent-manager-chat-pane",
62
63
  children: [
63
- /* @__PURE__ */ _("div", {
64
+ /* @__PURE__ */ v("div", {
64
65
  className: "flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0",
65
- children: [/* @__PURE__ */ _("div", {
66
+ children: [/* @__PURE__ */ v("div", {
66
67
  className: "flex items-center gap-1.5 min-w-0 flex-1",
67
- children: [/* @__PURE__ */ g("button", {
68
+ children: [/* @__PURE__ */ _("button", {
68
69
  type: "button",
69
- onClick: z,
70
- className: `p-1 rounded transition-colors shrink-0 ${S ? "bg-action-primary-bg/10 text-action-primary-text" : "text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated"}`,
71
- title: O("vibecontrols.agentManager.chatPane.searchInSession", "Search in session (Ctrl+F)"),
72
- "aria-label": O("vibecontrols.agentManager.chatPane.searchInSession", "Search in session"),
73
- children: /* @__PURE__ */ g(m, { className: "w-3.5 h-3.5" })
74
- }), S && /* @__PURE__ */ _("div", {
70
+ onClick: B,
71
+ className: `p-1 rounded transition-colors shrink-0 ${C ? "bg-action-primary-bg/10 text-action-primary-text" : "text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated"}`,
72
+ title: k("vibecontrols.agentManager.chatPane.searchInSession", "Search in session (Ctrl+F)"),
73
+ "aria-label": k("vibecontrols.agentManager.chatPane.searchInSession", "Search in session"),
74
+ children: /* @__PURE__ */ _(h, { className: "w-3.5 h-3.5" })
75
+ }), C && /* @__PURE__ */ v("div", {
75
76
  className: "flex items-center gap-1.5 flex-1 min-w-0",
76
- children: [/* @__PURE__ */ g("input", {
77
+ children: [/* @__PURE__ */ _("input", {
77
78
  type: "text",
78
- value: w,
79
- onChange: (e) => T(e.target.value),
80
- placeholder: O("vibecontrols.agentManager.chatPane.searchPlaceholder", "Search in conversation..."),
79
+ value: T,
80
+ onChange: (e) => E(e.target.value),
81
+ placeholder: k("vibecontrols.agentManager.chatPane.searchPlaceholder", "Search in conversation..."),
81
82
  autoFocus: !0,
82
83
  className: "flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none"
83
- }), w && /* @__PURE__ */ g("button", {
84
+ }), T && /* @__PURE__ */ _("button", {
84
85
  type: "button",
85
- onClick: () => T(""),
86
+ onClick: () => E(""),
86
87
  className: "p-0.5 rounded hover:bg-bg-elevated transition-colors",
87
- children: /* @__PURE__ */ g(h, { className: "w-3 h-3 text-text-tertiary" })
88
+ children: /* @__PURE__ */ _(g, { className: "w-3 h-3 text-text-tertiary" })
88
89
  })]
89
90
  })]
90
- }), /* @__PURE__ */ _("button", {
91
+ }), /* @__PURE__ */ v("button", {
91
92
  type: "button",
92
- onClick: () => D(!0),
93
+ onClick: () => O(!0),
93
94
  className: "shrink-0 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-elevated px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken hover:border-action-primary-bg/40",
94
- title: O("vibecontrols.agentManager.chatPane.manageProvidersTooltip", "Install or remove AI providers (Claude, Codex, Gemini, OpenCode)"),
95
- "aria-label": O("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers"),
95
+ title: k("vibecontrols.agentManager.chatPane.manageProvidersTooltip", "Install or remove AI providers (Claude, Codex, Gemini, OpenCode)"),
96
+ "aria-label": k("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers"),
96
97
  "data-testid": "agent-manager-manage-providers",
97
- children: [/* @__PURE__ */ g(p, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ g("span", {
98
+ children: [/* @__PURE__ */ _(m, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ _("span", {
98
99
  className: "hidden sm:inline",
99
- children: O("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers")
100
+ children: k("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers")
100
101
  })]
101
102
  })]
102
103
  }),
103
- b ? /* @__PURE__ */ g("div", {
104
+ x ? /* @__PURE__ */ _("div", {
104
105
  className: "flex-1 flex items-center justify-center",
105
- children: /* @__PURE__ */ g("p", {
106
+ children: /* @__PURE__ */ _("p", {
106
107
  className: "text-sm text-text-secondary",
107
- children: O("vibecontrols.agentManager.chatPane.loadingConversation", "Loading conversation...")
108
+ children: k("vibecontrols.agentManager.chatPane.loadingConversation", "Loading conversation...")
108
109
  })
109
- }) : /* @__PURE__ */ g(o, {
110
- messages: N,
111
- isStreaming: R,
112
- onCancelStream: L,
113
- searchQuery: w
110
+ }) : /* @__PURE__ */ _(o, {
111
+ messages: P,
112
+ isStreaming: z,
113
+ onCancelStream: R,
114
+ searchQuery: T
115
+ }),
116
+ /* @__PURE__ */ _(s, {
117
+ agentId: y,
118
+ sessionId: l,
119
+ onSend: (e) => void L(e),
120
+ onCancel: R,
121
+ agentTunnelUrl: j?.tunnelUrl ?? void 0,
122
+ agentApiKey: j?.agentApiKey ?? void 0
114
123
  }),
115
- /* @__PURE__ */ g(s, {
124
+ /* @__PURE__ */ _(c, {
125
+ open: D,
116
126
  agentId: y,
117
- sessionId: v,
118
- onSend: (e) => void I(e),
119
- onCancel: L,
120
- agentTunnelUrl: A?.tunnelUrl ?? void 0,
121
- agentApiKey: A?.agentApiKey ?? void 0
127
+ onClose: () => O(!1)
122
128
  }),
123
- /* @__PURE__ */ g(c, {
124
- open: E,
129
+ /* @__PURE__ */ _(b, {
125
130
  agentId: y,
126
- onClose: () => D(!1)
131
+ agentRef: I,
132
+ initialProviderId: M?.sdk ?? void 0
127
133
  })
128
134
  ]
129
135
  });
136
+ }, b = ({ agentId: t, agentRef: n, initialProviderId: r }) => {
137
+ let a = i((e) => e.sdkAuthPrompt), o = i((e) => e.setSdkAuthPrompt), s = e();
138
+ return !a || a.agentId !== t ? null : /* @__PURE__ */ v("div", {
139
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
140
+ children: [/* @__PURE__ */ _("div", {
141
+ className: "absolute inset-0 bg-bg-overlay",
142
+ onClick: () => o(null),
143
+ "aria-hidden": !0
144
+ }), /* @__PURE__ */ v("div", {
145
+ className: "relative w-full max-w-xl max-h-[85vh] flex flex-col rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden",
146
+ children: [/* @__PURE__ */ v("div", {
147
+ className: "flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-surface flex-shrink-0",
148
+ children: [/* @__PURE__ */ _("h3", {
149
+ className: "text-base font-semibold text-text-primary truncate pr-2",
150
+ children: s("vibecontrols.ai.sdkAuth.title", "Configure AI provider credentials")
151
+ }), /* @__PURE__ */ _("button", {
152
+ type: "button",
153
+ onClick: () => o(null),
154
+ className: "p-1 rounded hover:bg-bg-sunken transition-colors flex-shrink-0",
155
+ "aria-label": s("vibecontrols.common.close", "Close"),
156
+ children: /* @__PURE__ */ _(g, { className: "w-5 h-5 text-text-secondary" })
157
+ })]
158
+ }), /* @__PURE__ */ v("div", {
159
+ className: "flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-3",
160
+ children: [/* @__PURE__ */ _("p", {
161
+ className: "text-sm text-text-secondary",
162
+ children: s("vibecontrols.ai.sdkAuth.body", "The agent does not have a credential configured for this provider. Pick a provider and save one to continue the conversation.")
163
+ }), /* @__PURE__ */ _(l, {
164
+ agent: n,
165
+ initialProviderId: r
166
+ })]
167
+ })]
168
+ })]
169
+ });
130
170
  };
131
171
  //#endregion
132
- export { v as AgentManagerChatPane };
172
+ export { y as AgentManagerChatPane };
133
173
 
134
174
  //# sourceMappingURL=AgentManagerChatPane.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentManagerChatPane.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerChatPane.tsx"],"sourcesContent":["/**\n * Main chat interface for a single Agent Manager session.\n *\n * Renders message list + input area, manages the send/receive flow,\n * fetches existing conversation logs on mount, and provides\n * auto-scroll, cancel, and in-session search support.\n */\n\nimport { useEffect, useState, useCallback, useMemo, type FC } from 'react';\nimport { PackagePlus, Search, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useAgentManagerStreaming } from '@/hooks/useAgentManagerStreaming';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { fetchSessionLogs } from '@/services/aiApi';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { AgentManagerMessageList } from './AgentManagerMessageList';\nimport { AgentManagerInputArea } from './AgentManagerInputArea';\nimport { PluginInstallModal } from './PluginInstallModal';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerChatPaneProps {\n sessionId: string;\n agentId: string;\n}\n\nexport const AgentManagerChatPane: FC<AgentManagerChatPaneProps> = ({ sessionId, agentId }) => {\n const [loadingLogs, setLoadingLogs] = useState(false);\n const [searchOpen, setSearchOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n // Provider-install modal triggered from the header. Lets users add a\n // claude/codex/gemini/opencode plugin without leaving the conversation.\n const [installModalOpen, setInstallModalOpen] = useState(false);\n const tr = useTr();\n\n const { agents } = useCachedInitialData();\n const activeAgent = useMemo(\n () => agents.find((a) => a.id === agentId) ?? null,\n [agents, agentId]\n );\n\n const sessionState = useAgentManagerStore((s) => s.sessionStates[sessionId]);\n const addMessage = useAgentManagerStore((s) => s.addMessage);\n\n const messages = sessionState?.messages ?? [];\n\n const { active: activeProfile } = useActiveAgentProfile(activeAgent?.id ?? null);\n const agentRef = useMemo(\n () => ({ id: agentId, profile: activeProfile }),\n [agentId, activeProfile]\n );\n\n const { sendMessage, cancelStream, isStreaming } = useAgentManagerStreaming({\n agent: agentRef,\n sessionId,\n });\n\n const toggleSearch = useCallback(() => {\n setSearchOpen((prev) => {\n if (prev) setSearchQuery('');\n return !prev;\n });\n }, []);\n\n // Fetch existing conversation logs on mount\n useEffect(() => {\n let cancelled = false;\n\n const loadExistingLogs = async () => {\n // Only load if session has no messages yet\n if (messages.length > 0) return;\n\n setLoadingLogs(true);\n try {\n const logs = await fetchSessionLogs(agentRef, sessionId, {\n types: ['input', 'output'],\n });\n\n if (cancelled || logs.length === 0) return;\n\n const seenLogKeys = new Set<string>();\n\n // Convert session logs to AgentManagerMessage format. The core AI\n // route owns logging, but older provider builds also wrote the same\n // input/output records; collapse same-second duplicates on restore.\n for (const log of logs) {\n const content = log.message || log.content || '';\n const timestamp = log.timestamp || log.createdAt;\n const timestampSecond = timestamp ? Math.floor(new Date(timestamp).getTime() / 1000) : 0;\n const logKey = [log.type, content, log.model ?? '', timestampSecond].join('\\u0000');\n if (seenLogKeys.has(logKey)) continue;\n seenLogKeys.add(logKey);\n\n const msg: AgentManagerMessage = {\n id: log.id,\n role: log.type === 'input' ? 'user' : 'assistant',\n content,\n timestamp,\n tokens: log.tokenCount ? { input: 0, output: log.tokenCount } : undefined,\n model: log.model,\n };\n addMessage(sessionId, msg);\n }\n } catch {\n // Silent fail -- session may not have logs yet\n } finally {\n if (!cancelled) setLoadingLogs(false);\n }\n };\n\n void loadExistingLogs();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- only run on mount\n }, [agentId, sessionId]);\n\n return (\n <div className=\"flex flex-col h-full min-h-0\" data-testid=\"agent-manager-chat-pane\">\n {/* In-session search bar.\n Layout: [search controls (flex-1)] ... [Manage AI providers].\n The search input expands inside the LEFT cluster only, so the\n Manage button stays anchored on the right and never shifts. */}\n <div className=\"flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <div className=\"flex items-center gap-1.5 min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={toggleSearch}\n className={`p-1 rounded transition-colors shrink-0 ${\n searchOpen\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n title={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session (Ctrl+F)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session'\n )}\n >\n <Search className=\"w-3.5 h-3.5\" />\n </button>\n {searchOpen && (\n <div className=\"flex items-center gap-1.5 flex-1 min-w-0\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={tr(\n 'vibecontrols.agentManager.chatPane.searchPlaceholder',\n 'Search in conversation...'\n )}\n autoFocus\n className=\"flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n className=\"p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"w-3 h-3 text-text-tertiary\" />\n </button>\n )}\n </div>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setInstallModalOpen(true)}\n className=\"shrink-0 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-elevated px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken hover:border-action-primary-bg/40\"\n title={tr(\n 'vibecontrols.agentManager.chatPane.manageProvidersTooltip',\n 'Install or remove AI providers (Claude, Codex, Gemini, OpenCode)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.manageProviders',\n 'Manage AI providers'\n )}\n data-testid=\"agent-manager-manage-providers\"\n >\n <PackagePlus className=\"w-3.5 h-3.5\" />\n <span className=\"hidden sm:inline\">\n {tr('vibecontrols.agentManager.chatPane.manageProviders', 'Manage AI providers')}\n </span>\n </button>\n </div>\n\n {/* Message list */}\n {loadingLogs ? (\n <div className=\"flex-1 flex items-center justify-center\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.chatPane.loadingConversation',\n 'Loading conversation...'\n )}\n </p>\n </div>\n ) : (\n <AgentManagerMessageList\n messages={messages}\n isStreaming={isStreaming}\n onCancelStream={cancelStream}\n searchQuery={searchQuery}\n />\n )}\n\n {/* Rich input area with controls */}\n <AgentManagerInputArea\n agentId={agentId}\n sessionId={sessionId}\n onSend={(text) => void sendMessage(text)}\n onCancel={cancelStream}\n agentTunnelUrl={activeAgent?.tunnelUrl ?? undefined}\n agentApiKey={activeAgent?.agentApiKey ?? undefined}\n />\n\n {/* Header-triggered provider install/remove. Sits below the chat\n tree so it can render full-screen without disturbing layout. */}\n <PluginInstallModal\n open={installModalOpen}\n agentId={agentId}\n onClose={() => setInstallModalOpen(false)}\n />\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentManagerChatPane.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerChatPane.tsx"],"sourcesContent":["/**\n * Main chat interface for a single Agent Manager session.\n *\n * Renders message list + input area, manages the send/receive flow,\n * fetches existing conversation logs on mount, and provides\n * auto-scroll, cancel, and in-session search support.\n */\n\nimport { useEffect, useState, useCallback, useMemo, type FC } from 'react';\nimport { PackagePlus, Search, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useAgentManagerStreaming } from '@/hooks/useAgentManagerStreaming';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { fetchSessionLogs } from '@/services/aiApi';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { AgentManagerMessageList } from './AgentManagerMessageList';\nimport { AgentManagerInputArea } from './AgentManagerInputArea';\nimport { PluginInstallModal } from './PluginInstallModal';\nimport { AIProviderCredentialsCard } from '@/components/ai/AIProviderCredentialsCard';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerChatPaneProps {\n sessionId: string;\n agentId: string;\n}\n\nexport const AgentManagerChatPane: FC<AgentManagerChatPaneProps> = ({ sessionId, agentId }) => {\n const [loadingLogs, setLoadingLogs] = useState(false);\n const [searchOpen, setSearchOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n // Provider-install modal triggered from the header. Lets users add a\n // claude/codex/gemini/opencode plugin without leaving the conversation.\n const [installModalOpen, setInstallModalOpen] = useState(false);\n const tr = useTr();\n\n const { agents } = useCachedInitialData();\n const activeAgent = useMemo(\n () => agents.find((a) => a.id === agentId) ?? null,\n [agents, agentId]\n );\n\n const sessionState = useAgentManagerStore((s) => s.sessionStates[sessionId]);\n const addMessage = useAgentManagerStore((s) => s.addMessage);\n\n const messages = sessionState?.messages ?? [];\n\n const { active: activeProfile } = useActiveAgentProfile(activeAgent?.id ?? null);\n const agentRef = useMemo(\n () => ({ id: agentId, profile: activeProfile }),\n [agentId, activeProfile]\n );\n\n const { sendMessage, cancelStream, isStreaming } = useAgentManagerStreaming({\n agent: agentRef,\n sessionId,\n });\n\n const toggleSearch = useCallback(() => {\n setSearchOpen((prev) => {\n if (prev) setSearchQuery('');\n return !prev;\n });\n }, []);\n\n // Fetch existing conversation logs on mount\n useEffect(() => {\n let cancelled = false;\n\n const loadExistingLogs = async () => {\n // Only load if session has no messages yet\n if (messages.length > 0) return;\n\n setLoadingLogs(true);\n try {\n const logs = await fetchSessionLogs(agentRef, sessionId, {\n types: ['input', 'output'],\n });\n\n if (cancelled || logs.length === 0) return;\n\n const seenLogKeys = new Set<string>();\n\n // Convert session logs to AgentManagerMessage format. The core AI\n // route owns logging, but older provider builds also wrote the same\n // input/output records; collapse same-second duplicates on restore.\n for (const log of logs) {\n const content = log.message || log.content || '';\n const timestamp = log.timestamp || log.createdAt;\n const timestampSecond = timestamp ? Math.floor(new Date(timestamp).getTime() / 1000) : 0;\n const logKey = [log.type, content, log.model ?? '', timestampSecond].join('\\u0000');\n if (seenLogKeys.has(logKey)) continue;\n seenLogKeys.add(logKey);\n\n const msg: AgentManagerMessage = {\n id: log.id,\n role: log.type === 'input' ? 'user' : 'assistant',\n content,\n timestamp,\n tokens: log.tokenCount ? { input: 0, output: log.tokenCount } : undefined,\n model: log.model,\n };\n addMessage(sessionId, msg);\n }\n } catch {\n // Silent fail -- session may not have logs yet\n } finally {\n if (!cancelled) setLoadingLogs(false);\n }\n };\n\n void loadExistingLogs();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- only run on mount\n }, [agentId, sessionId]);\n\n return (\n <div className=\"flex flex-col h-full min-h-0\" data-testid=\"agent-manager-chat-pane\">\n {/* In-session search bar.\n Layout: [search controls (flex-1)] ... [Manage AI providers].\n The search input expands inside the LEFT cluster only, so the\n Manage button stays anchored on the right and never shifts. */}\n <div className=\"flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <div className=\"flex items-center gap-1.5 min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={toggleSearch}\n className={`p-1 rounded transition-colors shrink-0 ${\n searchOpen\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n title={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session (Ctrl+F)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session'\n )}\n >\n <Search className=\"w-3.5 h-3.5\" />\n </button>\n {searchOpen && (\n <div className=\"flex items-center gap-1.5 flex-1 min-w-0\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={tr(\n 'vibecontrols.agentManager.chatPane.searchPlaceholder',\n 'Search in conversation...'\n )}\n autoFocus\n className=\"flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n className=\"p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"w-3 h-3 text-text-tertiary\" />\n </button>\n )}\n </div>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setInstallModalOpen(true)}\n className=\"shrink-0 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-elevated px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken hover:border-action-primary-bg/40\"\n title={tr(\n 'vibecontrols.agentManager.chatPane.manageProvidersTooltip',\n 'Install or remove AI providers (Claude, Codex, Gemini, OpenCode)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.manageProviders',\n 'Manage AI providers'\n )}\n data-testid=\"agent-manager-manage-providers\"\n >\n <PackagePlus className=\"w-3.5 h-3.5\" />\n <span className=\"hidden sm:inline\">\n {tr('vibecontrols.agentManager.chatPane.manageProviders', 'Manage AI providers')}\n </span>\n </button>\n </div>\n\n {/* Message list */}\n {loadingLogs ? (\n <div className=\"flex-1 flex items-center justify-center\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.chatPane.loadingConversation',\n 'Loading conversation...'\n )}\n </p>\n </div>\n ) : (\n <AgentManagerMessageList\n messages={messages}\n isStreaming={isStreaming}\n onCancelStream={cancelStream}\n searchQuery={searchQuery}\n />\n )}\n\n {/* Rich input area with controls */}\n <AgentManagerInputArea\n agentId={agentId}\n sessionId={sessionId}\n onSend={(text) => void sendMessage(text)}\n onCancel={cancelStream}\n agentTunnelUrl={activeAgent?.tunnelUrl ?? undefined}\n agentApiKey={activeAgent?.agentApiKey ?? undefined}\n />\n\n {/* Header-triggered provider install/remove. Sits below the chat\n tree so it can render full-screen without disturbing layout. */}\n <PluginInstallModal\n open={installModalOpen}\n agentId={agentId}\n onClose={() => setInstallModalOpen(false)}\n />\n\n {/* SDK auth prompt: opens when the streaming hook detects a missing\n ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN, so the user can save the\n credential inline instead of being told to go look elsewhere.\n The session's currently-selected SDK is forwarded so the picker\n can preselect the right provider. */}\n <SdkAuthPromptModal\n agentId={agentId}\n agentRef={agentRef}\n initialProviderId={sessionState?.sdk ?? undefined}\n />\n </div>\n );\n};\n\ninterface SdkAuthPromptModalProps {\n agentId: string;\n agentRef: { id: string; profile: string };\n initialProviderId?: string;\n}\n\nconst SdkAuthPromptModal: FC<SdkAuthPromptModalProps> = ({\n agentId,\n agentRef,\n initialProviderId,\n}) => {\n const sdkAuthPrompt = useAgentManagerStore((s) => s.sdkAuthPrompt);\n const setSdkAuthPrompt = useAgentManagerStore((s) => s.setSdkAuthPrompt);\n const tr = useTr();\n\n if (!sdkAuthPrompt || sdkAuthPrompt.agentId !== agentId) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <div\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={() => setSdkAuthPrompt(null)}\n aria-hidden\n />\n {/* Constrain height to the viewport and let the inner body scroll so the\n modal never escapes the screen no matter how many providers/fields\n render inside AIProviderCredentialsCard. The header stays sticky so\n the close button is always reachable. */}\n <div className=\"relative w-full max-w-xl max-h-[85vh] flex flex-col rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <h3 className=\"text-base font-semibold text-text-primary truncate pr-2\">\n {tr('vibecontrols.ai.sdkAuth.title', 'Configure AI provider credentials')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setSdkAuthPrompt(null)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors flex-shrink-0\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.ai.sdkAuth.body',\n 'The agent does not have a credential configured for this provider. Pick a provider and save one to continue the conversation.'\n )}\n </p>\n <AIProviderCredentialsCard agent={agentRef} initialProviderId={initialProviderId} />\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}