@burdenoff/microfe-vibecontrols 2026.521.3 → 2026.522.2

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.
Files changed (105) hide show
  1. package/dist/VibeControlsRoot.js +86 -78
  2. package/dist/VibeControlsRoot.js.map +1 -1
  3. package/dist/VibeControlsRoutes.js +168 -125
  4. package/dist/VibeControlsRoutes.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js +174 -0
  6. package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -0
  7. package/dist/components/agent-manager/AgentManagerChatPane.js +149 -94
  8. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerMessageItem.js +52 -41
  10. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerMessageList.js +22 -15
  12. package/dist/components/agent-manager/AgentManagerMessageList.js.map +1 -1
  13. package/dist/components/agents/OpenInEditorMenu.js +241 -0
  14. package/dist/components/agents/OpenInEditorMenu.js.map +1 -0
  15. package/dist/components/agents/VibeOpenInEditorMenu.js +20 -0
  16. package/dist/components/agents/VibeOpenInEditorMenu.js.map +1 -0
  17. package/dist/components/agents/idePresets.js +148 -0
  18. package/dist/components/agents/idePresets.js.map +1 -0
  19. package/dist/components/agents/index.js +3 -0
  20. package/dist/components/ai/AIPlanPanel.js +332 -0
  21. package/dist/components/ai/AIPlanPanel.js.map +1 -0
  22. package/dist/components/analytics/index.js +2 -2
  23. package/dist/components/assistant/AssistantMessageItem.js +55 -47
  24. package/dist/components/assistant/AssistantMessageItem.js.map +1 -1
  25. package/dist/components/audit/AuditExportButton.js +87 -41
  26. package/dist/components/audit/AuditExportButton.js.map +1 -1
  27. package/dist/components/gitops/CIDurationChart.js +11 -11
  28. package/dist/components/security/SecurityEmptyState.js +33 -0
  29. package/dist/components/security/SecurityEmptyState.js.map +1 -0
  30. package/dist/components/security/SecurityEvidenceList.js +42 -0
  31. package/dist/components/security/SecurityEvidenceList.js.map +1 -0
  32. package/dist/components/security/SecurityFindingsTable.js +201 -0
  33. package/dist/components/security/SecurityFindingsTable.js.map +1 -0
  34. package/dist/components/security/SecurityHeaderStrip.js +67 -0
  35. package/dist/components/security/SecurityHeaderStrip.js.map +1 -0
  36. package/dist/components/security/SecurityLifecycleStepper.js +66 -0
  37. package/dist/components/security/SecurityLifecycleStepper.js.map +1 -0
  38. package/dist/components/security/SecurityProviderDrawer.js +112 -0
  39. package/dist/components/security/SecurityProviderDrawer.js.map +1 -0
  40. package/dist/components/security/SecurityTabPanel.js +105 -0
  41. package/dist/components/security/SecurityTabPanel.js.map +1 -0
  42. package/dist/components/security/gql.js +128 -0
  43. package/dist/components/security/gql.js.map +1 -0
  44. package/dist/components/security/index.js +8 -0
  45. package/dist/components/security/types.js +80 -0
  46. package/dist/components/security/types.js.map +1 -0
  47. package/dist/components/settings/IdePreferencesSection.js +66 -0
  48. package/dist/components/settings/IdePreferencesSection.js.map +1 -0
  49. package/dist/components/shared/PageSkeleton.js +88 -0
  50. package/dist/components/shared/PageSkeleton.js.map +1 -0
  51. package/dist/constants/pluginCatalog.js +20 -8
  52. package/dist/constants/pluginCatalog.js.map +1 -1
  53. package/dist/generated/wspace-operations.js +1350 -1262
  54. package/dist/generated/wspace-operations.js.map +1 -1
  55. package/dist/generated/wspace-types.js.map +1 -1
  56. package/dist/hooks/useToggleAgentBookmark.js +32 -0
  57. package/dist/hooks/useToggleAgentBookmark.js.map +1 -0
  58. package/dist/index.js +10 -10
  59. package/dist/pages/AgentDetailsPage.js +25 -25
  60. package/dist/pages/AgentDetailsPage.js.map +1 -1
  61. package/dist/pages/AgentsPage.js +326 -327
  62. package/dist/pages/AgentsPage.js.map +1 -1
  63. package/dist/pages/AnalyticsPage.js +425 -420
  64. package/dist/pages/AnalyticsPage.js.map +1 -1
  65. package/dist/pages/CatalogDetailPage.js +258 -249
  66. package/dist/pages/CatalogDetailPage.js.map +1 -1
  67. package/dist/pages/CatalogPage.js +22 -22
  68. package/dist/pages/CatalogPage.js.map +1 -1
  69. package/dist/pages/ComponentDetailPage.js +30 -30
  70. package/dist/pages/ComponentDetailPage.js.map +1 -1
  71. package/dist/pages/ComponentsPage.js +23 -23
  72. package/dist/pages/ComponentsPage.js.map +1 -1
  73. package/dist/pages/LogsPage.js +107 -0
  74. package/dist/pages/LogsPage.js.map +1 -0
  75. package/dist/pages/NoteDetailsPage.js +12 -12
  76. package/dist/pages/NoteDetailsPage.js.map +1 -1
  77. package/dist/pages/SessionDetailsPage.js +12 -12
  78. package/dist/pages/SessionDetailsPage.js.map +1 -1
  79. package/dist/pages/SessionsPage.js +158 -136
  80. package/dist/pages/SessionsPage.js.map +1 -1
  81. package/dist/pages/SettingsPage.js +60 -52
  82. package/dist/pages/SettingsPage.js.map +1 -1
  83. package/dist/pages/TargetDetailsPage.js +18 -18
  84. package/dist/pages/TargetDetailsPage.js.map +1 -1
  85. package/dist/pages/TargetsPage.js +328 -331
  86. package/dist/pages/TargetsPage.js.map +1 -1
  87. package/dist/pages/TemplateDetailPage.js +17 -17
  88. package/dist/pages/TemplateDetailPage.js.map +1 -1
  89. package/dist/pages/TemplatesPage.js +17 -17
  90. package/dist/pages/TemplatesPage.js.map +1 -1
  91. package/dist/pages/VibeDetailsPage.js +552 -535
  92. package/dist/pages/VibeDetailsPage.js.map +1 -1
  93. package/dist/pages/VibesPage.js +321 -296
  94. package/dist/pages/VibesPage.js.map +1 -1
  95. package/dist/pages/ai/AIBookmarksPage.js +184 -0
  96. package/dist/pages/ai/AIBookmarksPage.js.map +1 -0
  97. package/dist/pages/ai/AIPage.js +138 -109
  98. package/dist/pages/ai/AIPage.js.map +1 -1
  99. package/dist/services/planApi.js +18 -0
  100. package/dist/services/planApi.js.map +1 -0
  101. package/dist/store/vibeControlsStore.js +20 -2
  102. package/dist/store/vibeControlsStore.js.map +1 -1
  103. package/dist/translations/en.js +30 -1
  104. package/dist/translations/en.js.map +1 -1
  105. package/package.json +4 -4
@@ -1,33 +1,34 @@
1
1
  import { useVibeNavigate as e } from "../../providers/VibeControlsProvider.js";
2
2
  import t from "../../hooks/useCachedInitialData.js";
3
3
  import { PageHeader as n } from "../../components/shared/PageHeader.js";
4
- import { LoadingState as r } from "../../components/shared/LoadingState.js";
5
- import { ErrorState as i } from "../../components/shared/ErrorState.js";
6
- import { EmptyState as a } from "../../components/shared/EmptyState.js";
7
- import { AI_CORE_PACKAGE as o, AI_PROVIDER_PACKAGES as s } from "../../constants/pluginCatalog.js";
8
- import { PluginGate as c } from "../../components/shared/PluginGate.js";
9
- import { RequireAgentReachable as l } from "../../components/shared/RequireAgentReachable.js";
10
- import { useActiveAgentProfile as u } from "../../hooks/useActiveAgentProfile.js";
4
+ import { ErrorState as r } from "../../components/shared/ErrorState.js";
5
+ import { EmptyState as i } from "../../components/shared/EmptyState.js";
6
+ import { AI_CORE_PACKAGE as a, AI_PROVIDER_PACKAGES as o } from "../../constants/pluginCatalog.js";
7
+ import { PluginGate as s } from "../../components/shared/PluginGate.js";
8
+ import { RequireAgentReachable as c } from "../../components/shared/RequireAgentReachable.js";
9
+ import { useActiveAgentProfile as l } from "../../hooks/useActiveAgentProfile.js";
11
10
  import "../../components/shared/index.js";
11
+ import { PageSkeleton as u } from "../../components/shared/PageSkeleton.js";
12
12
  import { PluginHarnessPicker as d } from "../../components/ai/PluginHarnessPicker.js";
13
13
  import { AITemplatesTab as f } from "../../components/ai/AITemplatesTab.js";
14
14
  import { AIContextsTab as p } from "../../components/ai/AIContextsTab.js";
15
- import { AIPromptsTab as m } from "../../components/ai/AIPromptsTab.js";
16
- import { AITasksTab as h } from "../../components/ai/AITasksTab.js";
17
- import { AIStatsTab as g } from "../../components/ai/AIStatsTab.js";
18
- import { AIPlaygroundTab as _ } from "../../components/ai/AIPlaygroundTab.js";
19
- import { BridleConfigPanel as v } from "../../components/agent-manager/BridleConfigPanel.js";
20
- import { AIOverviewTab as y } from "../../components/ai/AIOverviewTab.js";
21
- import { AISessionsTab as b } from "../../components/ai/AISessionsTab.js";
22
- import { ClaudeCredentialsCard as x } from "../../components/ai/ClaudeCredentialsCard.js";
15
+ import { AIPromptsTab as ee } from "../../components/ai/AIPromptsTab.js";
16
+ import { AITasksTab as m } from "../../components/ai/AITasksTab.js";
17
+ import { AIStatsTab as h } from "../../components/ai/AIStatsTab.js";
18
+ import { AIPlaygroundTab as g } from "../../components/ai/AIPlaygroundTab.js";
19
+ import { BridleConfigPanel as _ } from "../../components/agent-manager/BridleConfigPanel.js";
20
+ import { AIOverviewTab as v } from "../../components/ai/AIOverviewTab.js";
21
+ import { AISessionsTab as y } from "../../components/ai/AISessionsTab.js";
22
+ import { ClaudeCredentialsCard as b } from "../../components/ai/ClaudeCredentialsCard.js";
23
+ import { AIPlanPanel as x } from "../../components/ai/AIPlanPanel.js";
23
24
  import { useCallback as S, useMemo as C, useState as w } from "react";
24
- import { useI18n as T } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
25
- import { BarChart3 as E, FileCode2 as D, FileText as O, LayoutDashboard as k, ListTodo as A, MessageSquare as j, MessagesSquare as M, Play as N } from "lucide-react";
26
- import { useParams as P } from "react-router-dom";
27
- import { jsx as F, jsxs as I } from "react/jsx-runtime";
28
- import { MotionPage as L } from "@burdenoff/fe-libs/shared/motion";
25
+ import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
26
+ import { BarChart3 as T, Brain as E, FileCode2 as D, FileText as O, LayoutDashboard as k, ListTodo as A, MapIcon as j, MessageSquare as M, MessagesSquare as N, Play as P } from "lucide-react";
27
+ import { useMatch as F, useParams as I } from "react-router-dom";
28
+ import { jsx as L, jsxs as R } from "react/jsx-runtime";
29
+ import { MotionPage as z } from "@burdenoff/fe-libs/shared/motion";
29
30
  //#region src/pages/ai/AIPage.tsx
30
- var R = [
31
+ var B = [
31
32
  {
32
33
  id: "overview",
33
34
  icon: k
@@ -42,11 +43,11 @@ var R = [
42
43
  },
43
44
  {
44
45
  id: "prompts",
45
- icon: j
46
+ icon: M
46
47
  },
47
48
  {
48
49
  id: "sessions",
49
- icon: M
50
+ icon: N
50
51
  },
51
52
  {
52
53
  id: "tasks",
@@ -54,138 +55,166 @@ var R = [
54
55
  },
55
56
  {
56
57
  id: "stats",
57
- icon: E
58
+ icon: T
58
59
  },
59
60
  {
60
61
  id: "playground",
61
- icon: N
62
+ icon: P
62
63
  },
63
64
  {
64
65
  id: "config",
65
66
  icon: k
66
67
  }
67
- ], z = new Set(R.map((e) => e.id));
68
- function B() {
69
- let { t: E } = T(), { tab: D } = P(), O = D && z.has(D) ? D : "overview", k = e(), A = S((e, t) => {
70
- let n = E(e);
68
+ ], ne = new Set(B.map((e) => e.id));
69
+ function V() {
70
+ let { t: T } = te(), { tab: D } = I(), O = F("vibecontrols/ai-tools/plan") ? "plan" : "ai", k = D && ne.has(D) ? D : "overview", A = e(), M = S((e, t) => {
71
+ let n = T(e);
71
72
  return n === e ? t : n;
72
- }, [E]), j = S((e) => {
73
+ }, [T]), P = S((e) => {
73
74
  switch (e) {
74
- case "overview": return A("ai.overview", "Overview");
75
- case "templates": return A("ai.templates", "Templates");
76
- case "contexts": return A("ai.contexts", "Contexts");
77
- case "prompts": return A("ai.prompts", "Prompts");
78
- case "sessions": return A("nav.sessions", "Sessions");
79
- case "tasks": return A("ai.tasks", "Tasks");
80
- case "stats": return A("ai.stats", "Stats");
81
- case "playground": return A("ai.playground", "Playground");
82
- case "config": return A("ai.config", "Config");
75
+ case "overview": return M("ai.overview", "Overview");
76
+ case "templates": return M("ai.templates", "Templates");
77
+ case "contexts": return M("ai.contexts", "Contexts");
78
+ case "prompts": return M("ai.prompts", "Prompts");
79
+ case "sessions": return M("nav.sessions", "Sessions");
80
+ case "tasks": return M("ai.tasks", "Tasks");
81
+ case "stats": return M("ai.stats", "Stats");
82
+ case "playground": return M("ai.playground", "Playground");
83
+ case "config": return M("ai.config", "Config");
83
84
  default: return e;
84
85
  }
85
- }, [A]), { agents: N, loading: B, error: V, refetch: H } = t();
86
- V && console.error("[ai] failed to load initial workspace data", V);
87
- let U = C(() => N.find((e) => e.isActive && e.tunnelUrl && e.agentApiKey) ?? null, [N]), W = U?.id ?? "", { active: G } = u(U?.id ?? null), K = C(() => ({
88
- id: W,
89
- profile: G
90
- }), [W, G]), q = S((e) => {
91
- k(e === "overview" ? "/ai-tools" : `/ai-tools/${e}`);
92
- }, [k]), J = S((e) => {
93
- k(`/ai-tools/sessions/${e}`);
94
- }, [k]), Y = !!W, [X, Z] = w(null), Q = S(() => {
95
- Z(A("ai.pluginInstalled", "Plugin installed. Refreshing…")), console.info("[ai] orchestrator install completed");
96
- }, [A]), $ = S(() => {
97
- Z(A("ai.harnessUpdated", "Harness updated.")), console.info("[ai] harness picker changed");
98
- }, [A]);
99
- return /* @__PURE__ */ I(L, {
86
+ }, [M]), { agents: V, loading: H, error: U, refetch: W } = t();
87
+ U && console.error("[ai] failed to load initial workspace data", U);
88
+ let G = C(() => V.find((e) => e.isActive && e.tunnelUrl && e.agentApiKey) ?? null, [V]), K = G?.id ?? "", { active: q } = l(G?.id ?? null), J = C(() => ({
89
+ id: K,
90
+ profile: q
91
+ }), [K, q]), Y = S((e) => {
92
+ A(e === "overview" ? "/ai-tools/ai" : `/ai-tools/ai/${e}`);
93
+ }, [A]), X = S((e) => {
94
+ A(e === "plan" ? "/ai-tools/plan" : `/ai-tools/ai/${k}`);
95
+ }, [A, k]), Z = S((e) => {
96
+ A(`/ai-tools/sessions/${e}`);
97
+ }, [A]), re = !!K, [Q, $] = w(null), ie = S(() => {
98
+ $(M("ai.pluginInstalled", "Plugin installed. Refreshing…")), console.info("[ai] orchestrator install completed");
99
+ }, [M]), ae = S(() => {
100
+ $(M("ai.harnessUpdated", "Harness updated.")), console.info("[ai] harness picker changed");
101
+ }, [M]);
102
+ return /* @__PURE__ */ R(z, {
100
103
  className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
101
104
  children: [
102
- /* @__PURE__ */ F(n, {
103
- title: A("nav.ai", "AI"),
104
- description: A("ai.description", "Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow")
105
+ /* @__PURE__ */ L(n, {
106
+ title: M("nav.ai", "AI"),
107
+ description: M("ai.description", "Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow")
105
108
  }),
106
- /* @__PURE__ */ F("div", {
109
+ /* @__PURE__ */ L("div", {
110
+ role: "tablist",
111
+ "aria-label": M("ai.modeToggleLabel", "AI workspace mode"),
112
+ className: "inline-flex rounded-lg border border-border-default bg-bg-sunken p-0.5",
113
+ children: ["plan", "ai"].map((e) => {
114
+ let t = O === e, n = e === "plan" ? j : E;
115
+ return /* @__PURE__ */ R("button", {
116
+ role: "tab",
117
+ "aria-selected": t,
118
+ "data-testid": `ai-mode-toggle-${e}`,
119
+ onClick: () => X(e),
120
+ className: `flex items-center gap-2 px-4 py-1.5 text-sm font-medium rounded-md transition-colors ${t ? "bg-bg-surface text-action-primary-bg shadow-sm" : "text-text-secondary hover:text-text-primary"}`,
121
+ children: [/* @__PURE__ */ L(n, { className: "h-4 w-4" }), e === "plan" ? M("ai.mode.plan", "Plan") : M("ai.mode.ai", "AI")]
122
+ }, e);
123
+ })
124
+ }),
125
+ O === "ai" && /* @__PURE__ */ L("div", {
107
126
  className: "border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6",
108
- children: /* @__PURE__ */ F("nav", {
127
+ children: /* @__PURE__ */ L("nav", {
109
128
  className: "flex gap-1 overflow-x-auto pb-1",
110
129
  role: "tablist",
111
- children: R.map((e) => {
112
- let t = e.icon, n = O === e.id;
113
- return /* @__PURE__ */ I("button", {
130
+ children: B.map((e) => {
131
+ let t = e.icon, n = k === e.id;
132
+ return /* @__PURE__ */ R("button", {
114
133
  role: "tab",
115
134
  "aria-selected": n,
116
- onClick: () => q(e.id),
135
+ onClick: () => Y(e.id),
117
136
  className: `flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-t-md border-b-2 px-2.5 py-2 text-xs font-medium transition-colors sm:px-3 sm:py-2.5 sm:text-sm ${n ? "border-action-primary-bg text-action-primary-bg" : "border-transparent text-text-secondary hover:text-text-primary hover:border-border-default"}`,
118
- children: [/* @__PURE__ */ F(t, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4" }), j(e.id)]
137
+ children: [/* @__PURE__ */ L(t, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4" }), P(e.id)]
119
138
  }, e.id);
120
139
  })
121
140
  })
122
141
  }),
123
- B ? /* @__PURE__ */ F(r, { message: A("agentsPage.loadingAgents", "Loading agents...") }) : V ? /* @__PURE__ */ F(i, {
124
- title: A("ai.loadFailed.title", "Could not load the AI workspace"),
125
- message: A("ai.loadFailed.message", "We couldn't load the AI workspace. Please try again in a moment."),
126
- onRetry: H
127
- }) : Y ? /* @__PURE__ */ F(l, {
128
- agentId: W,
129
- children: /* @__PURE__ */ F(c, {
130
- agentId: W,
131
- required: [o],
142
+ H && V.length === 0 ? /* @__PURE__ */ L(u, {
143
+ variant: "rows",
144
+ count: 6
145
+ }) : U ? /* @__PURE__ */ L(r, {
146
+ title: M("ai.loadFailed.title", "Could not load the AI workspace"),
147
+ message: M("ai.loadFailed.message", "We couldn't load the AI workspace. Please try again in a moment."),
148
+ onRetry: W
149
+ }) : re ? O === "plan" ? /* @__PURE__ */ L(c, {
150
+ agentId: K,
151
+ children: /* @__PURE__ */ L(x, {
152
+ agent: J,
153
+ agentApiKey: G?.agentApiKey ?? null,
154
+ tunnelUrl: G?.tunnelUrl ?? null
155
+ })
156
+ }) : /* @__PURE__ */ L(c, {
157
+ agentId: K,
158
+ children: /* @__PURE__ */ L(s, {
159
+ agentId: K,
160
+ required: [a],
132
161
  mode: "all",
133
- eyebrow: A("ai.pluginRequired", "AI plugin required"),
134
- title: A("ai.installOrchestrator", "Install the AI orchestrator"),
135
- description: A("ai.installOrchestratorDesc", "The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue."),
136
- onInstalled: Q,
137
- children: /* @__PURE__ */ I("div", {
162
+ eyebrow: M("ai.pluginRequired", "AI plugin required"),
163
+ title: M("ai.installOrchestrator", "Install the AI orchestrator"),
164
+ description: M("ai.installOrchestratorDesc", "The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue."),
165
+ onInstalled: ie,
166
+ children: /* @__PURE__ */ R("div", {
138
167
  className: "space-y-6",
139
168
  children: [
140
- X && /* @__PURE__ */ F("div", {
169
+ Q && /* @__PURE__ */ L("div", {
141
170
  role: "status",
142
171
  className: "rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text",
143
- children: X
172
+ children: Q
144
173
  }),
145
- /* @__PURE__ */ F(d, {
146
- agentId: W,
147
- packages: s,
148
- title: A("ai.harnessesTitle", "AI harnesses"),
149
- description: A("ai.harnessesDesc", "Install and manage the coding-agent harnesses available on this agent. You can install multiple and switch between them per-session."),
150
- onChange: $
174
+ /* @__PURE__ */ L(d, {
175
+ agentId: K,
176
+ packages: o,
177
+ title: M("ai.harnessesTitle", "AI harnesses"),
178
+ description: M("ai.harnessesDesc", "Install and manage the coding-agent harnesses available on this agent. You can install multiple and switch between them per-session."),
179
+ onChange: ae
151
180
  }),
152
- /* @__PURE__ */ I("div", { children: [
153
- O === "overview" && /* @__PURE__ */ F(y, {
154
- agent: K,
155
- onNavigateToSession: J
181
+ /* @__PURE__ */ R("div", { children: [
182
+ k === "overview" && /* @__PURE__ */ L(v, {
183
+ agent: J,
184
+ onNavigateToSession: Z
156
185
  }),
157
- O === "templates" && /* @__PURE__ */ F(f, { agentId: W }),
158
- O === "contexts" && /* @__PURE__ */ F(p, { agent: K }),
159
- O === "prompts" && /* @__PURE__ */ F(m, { agent: K }),
160
- O === "sessions" && /* @__PURE__ */ F(b, {
161
- agent: K,
162
- onNavigateToSession: J
186
+ k === "templates" && /* @__PURE__ */ L(f, { agentId: K }),
187
+ k === "contexts" && /* @__PURE__ */ L(p, { agent: J }),
188
+ k === "prompts" && /* @__PURE__ */ L(ee, { agent: J }),
189
+ k === "sessions" && /* @__PURE__ */ L(y, {
190
+ agent: J,
191
+ onNavigateToSession: Z
163
192
  }),
164
- O === "tasks" && /* @__PURE__ */ F(h, {}),
165
- O === "stats" && /* @__PURE__ */ F(g, { agent: K }),
166
- O === "playground" && /* @__PURE__ */ F(_, { agent: K }),
167
- O === "config" && /* @__PURE__ */ I("div", {
193
+ k === "tasks" && /* @__PURE__ */ L(m, {}),
194
+ k === "stats" && /* @__PURE__ */ L(h, { agent: J }),
195
+ k === "playground" && /* @__PURE__ */ L(g, { agent: J }),
196
+ k === "config" && /* @__PURE__ */ R("div", {
168
197
  className: "space-y-6",
169
- children: [/* @__PURE__ */ F(x, { agent: K }), /* @__PURE__ */ F(v, { agent: K })]
198
+ children: [/* @__PURE__ */ L(b, { agent: J }), /* @__PURE__ */ L(_, { agent: J })]
170
199
  })
171
200
  ] })
172
201
  ]
173
202
  })
174
203
  })
175
- }) : /* @__PURE__ */ F(a, {
176
- icon: /* @__PURE__ */ F(M, { className: "w-8 h-8 text-text-secondary" }),
177
- title: A("ai.noActiveAgent", "No active agent available"),
178
- description: A("ai.noActiveAgentDescription", "AI features require an active agent with a configured tunnel URL and API key. Add an agent or start an existing one."),
179
- action: /* @__PURE__ */ F("button", {
180
- onClick: () => k("/agents"),
204
+ }) : /* @__PURE__ */ L(i, {
205
+ icon: /* @__PURE__ */ L(N, { className: "w-8 h-8 text-text-secondary" }),
206
+ title: M("ai.noActiveAgent", "No active agent available"),
207
+ description: M("ai.noActiveAgentDescription", "AI features require an active agent with a configured tunnel URL and API key. Add an agent or start an existing one."),
208
+ action: /* @__PURE__ */ L("button", {
209
+ onClick: () => A("/agents"),
181
210
  className: "px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors text-sm",
182
- children: A("ai.goToAgents", "Go to Agents")
211
+ children: M("ai.goToAgents", "Go to Agents")
183
212
  })
184
213
  })
185
214
  ]
186
215
  });
187
216
  }
188
217
  //#endregion
189
- export { B as AIPage };
218
+ export { V as AIPage };
190
219
 
191
220
  //# sourceMappingURL=AIPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AIPage.js","names":[],"sources":["../../../src/pages/ai/AIPage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n LayoutDashboard,\n FileCode2,\n FileText,\n MessageSquare,\n MessagesSquare,\n ListTodo,\n BarChart3,\n Play,\n} from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport {\n PageHeader,\n LoadingState,\n EmptyState,\n ErrorState,\n PluginGate,\n RequireAgentReachable,\n} from '@/components/shared';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { AI_CORE_PACKAGE, AI_PROVIDER_PACKAGES } from '@/constants/pluginCatalog';\nimport { PluginHarnessPicker } from '@/components/ai/PluginHarnessPicker';\n\nimport { AIOverviewTab } from '@/components/ai/AIOverviewTab';\nimport { AITemplatesTab } from '@/components/ai/AITemplatesTab';\nimport { AIContextsTab } from '@/components/ai/AIContextsTab';\nimport { AIPromptsTab } from '@/components/ai/AIPromptsTab';\nimport { AISessionsTab } from '@/components/ai/AISessionsTab';\nimport { AITasksTab } from '@/components/ai/AITasksTab';\nimport { AIStatsTab } from '@/components/ai/AIStatsTab';\nimport { AIPlaygroundTab } from '@/components/ai/AIPlaygroundTab';\nimport { ClaudeCredentialsCard } from '@/components/ai/ClaudeCredentialsCard';\nimport { BridleConfigPanel } from '@/components/agent-manager/BridleConfigPanel';\n\ntype AITab =\n | 'overview'\n | 'templates'\n | 'contexts'\n | 'prompts'\n | 'sessions'\n | 'tasks'\n | 'stats'\n | 'playground'\n | 'config';\n\nconst tabs: { id: AITab; icon: typeof LayoutDashboard }[] = [\n { id: 'overview', icon: LayoutDashboard },\n { id: 'templates', icon: FileCode2 },\n { id: 'contexts', icon: FileText },\n { id: 'prompts', icon: MessageSquare },\n { id: 'sessions', icon: MessagesSquare },\n { id: 'tasks', icon: ListTodo },\n { id: 'stats', icon: BarChart3 },\n { id: 'playground', icon: Play },\n { id: 'config', icon: LayoutDashboard },\n];\n\nconst validTabs = new Set<string>(tabs.map((t) => t.id));\n\n/**\n * AI page — tab-based layout for AI / vibecoding features.\n * Reads the :tab param from the URL; defaults to \"overview\".\n */\nexport function AIPage() {\n const { t } = useI18n();\n const { tab: tabParam } = useParams<{ tab?: string }>();\n const activeTab: AITab = tabParam && validTabs.has(tabParam) ? (tabParam as AITab) : 'overview';\n const handleNavigate = useVibeNavigate();\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const tabLabel = useCallback(\n (tabId: AITab): string => {\n switch (tabId) {\n case 'overview':\n return tr('ai.overview', 'Overview');\n case 'templates':\n return tr('ai.templates', 'Templates');\n case 'contexts':\n return tr('ai.contexts', 'Contexts');\n case 'prompts':\n return tr('ai.prompts', 'Prompts');\n case 'sessions':\n return tr('nav.sessions', 'Sessions');\n case 'tasks':\n return tr('ai.tasks', 'Tasks');\n case 'stats':\n return tr('ai.stats', 'Stats');\n case 'playground':\n return tr('ai.playground', 'Playground');\n case 'config':\n return tr('ai.config', 'Config');\n default:\n return tabId;\n }\n },\n [tr]\n );\n\n const { agents, loading: agentsLoading, error: agentsError, refetch } = useCachedInitialData();\n\n // If the initial data query throws (e.g. gateway query-cost limit, network\n // error, transient federation issue), surface a friendly fallback rather\n // than the raw GraphQL error. The technical details still go to the console\n // for debuggability — without PII, since the error message itself never\n // contains secrets.\n if (agentsError) {\n // eslint-disable-next-line no-console -- intentional debug breadcrumb for failed AI workspace load\n console.error('[ai] failed to load initial workspace data', agentsError);\n }\n const activeAgent = useMemo(\n () => agents.find((agent) => agent.isActive && agent.tunnelUrl && agent.agentApiKey) ?? null,\n [agents]\n );\n const agentId = activeAgent?.id ?? '';\n // AgentRef threads the canonical agent → profile → plugins/data\n // hierarchy through every aiApi call. Components receive the ref\n // and construct paths via `agentApiPath(profile, ...)`.\n const { active: activeProfile } = useActiveAgentProfile(activeAgent?.id ?? null);\n const agentRef = useMemo(\n () => ({ id: agentId, profile: activeProfile }),\n [agentId, activeProfile]\n );\n\n const switchTab = useCallback(\n (tab: AITab) => {\n handleNavigate(tab === 'overview' ? '/ai-tools' : `/ai-tools/${tab}`);\n },\n [handleNavigate]\n );\n\n const navigateToSession = useCallback(\n (sessionId: string) => {\n handleNavigate(`/ai-tools/sessions/${sessionId}`);\n },\n [handleNavigate]\n );\n\n const hasAgent = !!agentId;\n\n // Surface install lifecycle for debuggability. PluginGate / PluginHarnessPicker\n // already render inline error chips, but a console breadcrumb + a top-level\n // banner helps when the install actually succeeds on the agent but a stale\n // detection cache hides the success — without this the install button looked\n // like it silently swallowed errors.\n const [lastInstallNote, setLastInstallNote] = useState<string | null>(null);\n const handleInstalled = useCallback(() => {\n setLastInstallNote(tr('ai.pluginInstalled', 'Plugin installed. Refreshing…'));\n // eslint-disable-next-line no-console -- intentional install breadcrumb\n console.info('[ai] orchestrator install completed');\n }, [tr]);\n const handleHarnessChange = useCallback(() => {\n setLastInstallNote(tr('ai.harnessUpdated', 'Harness updated.'));\n // eslint-disable-next-line no-console -- intentional install breadcrumb\n console.info('[ai] harness picker changed');\n }, [tr]);\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('nav.ai', 'AI')}\n description={tr(\n 'ai.description',\n 'Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow'\n )}\n />\n\n {/* Tab Navigation */}\n <div className=\"border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6\">\n <nav className=\"flex gap-1 overflow-x-auto pb-1\" role=\"tablist\">\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = activeTab === tab.id;\n return (\n <button\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => switchTab(tab.id)}\n className={`flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-t-md border-b-2 px-2.5 py-2 text-xs font-medium transition-colors sm:px-3 sm:py-2.5 sm:text-sm ${\n isActive\n ? 'border-action-primary-bg text-action-primary-bg'\n : 'border-transparent text-text-secondary hover:text-text-primary hover:border-border-default'\n }`}\n >\n <Icon className=\"h-3.5 w-3.5 sm:h-4 sm:w-4\" />\n {tabLabel(tab.id)}\n </button>\n );\n })}\n </nav>\n </div>\n\n {/* Content */}\n {agentsLoading ? (\n <LoadingState message={tr('agentsPage.loadingAgents', 'Loading agents...')} />\n ) : agentsError ? (\n <ErrorState\n title={tr('ai.loadFailed.title', 'Could not load the AI workspace')}\n message={tr(\n 'ai.loadFailed.message',\n \"We couldn't load the AI workspace. Please try again in a moment.\"\n )}\n onRetry={refetch}\n />\n ) : !hasAgent ? (\n <EmptyState\n icon={<MessagesSquare className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('ai.noActiveAgent', 'No active agent available')}\n description={tr(\n 'ai.noActiveAgentDescription',\n 'AI features require an active agent with a configured tunnel URL and API key. Add an agent or start an existing one.'\n )}\n action={\n <button\n onClick={() => handleNavigate('/agents')}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors text-sm\"\n >\n {tr('ai.goToAgents', 'Go to Agents')}\n </button>\n }\n />\n ) : (\n <RequireAgentReachable agentId={agentId}>\n <PluginGate\n agentId={agentId}\n required={[AI_CORE_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('ai.pluginRequired', 'AI plugin required')}\n title={tr('ai.installOrchestrator', 'Install the AI orchestrator')}\n description={tr(\n 'ai.installOrchestratorDesc',\n 'The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue.'\n )}\n onInstalled={handleInstalled}\n >\n <div className=\"space-y-6\">\n {lastInstallNote && (\n <div\n role=\"status\"\n className=\"rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text\"\n >\n {lastInstallNote}\n </div>\n )}\n <PluginHarnessPicker\n agentId={agentId}\n packages={AI_PROVIDER_PACKAGES}\n title={tr('ai.harnessesTitle', 'AI harnesses')}\n description={tr(\n 'ai.harnessesDesc',\n 'Install and manage the coding-agent harnesses available on this agent. You can install multiple and switch between them per-session.'\n )}\n onChange={handleHarnessChange}\n />\n <div>\n {activeTab === 'overview' && (\n <AIOverviewTab agent={agentRef} onNavigateToSession={navigateToSession} />\n )}\n {activeTab === 'templates' && <AITemplatesTab agentId={agentId} />}\n {activeTab === 'contexts' && <AIContextsTab agent={agentRef} />}\n {activeTab === 'prompts' && <AIPromptsTab agent={agentRef} />}\n {activeTab === 'sessions' && (\n <AISessionsTab agent={agentRef} onNavigateToSession={navigateToSession} />\n )}\n {activeTab === 'tasks' && <AITasksTab />}\n {activeTab === 'stats' && <AIStatsTab agent={agentRef} />}\n {activeTab === 'playground' && <AIPlaygroundTab agent={agentRef} />}\n {activeTab === 'config' && (\n <div className=\"space-y-6\">\n <ClaudeCredentialsCard agent={agentRef} />\n <BridleConfigPanel agent={agentRef} />\n </div>\n )}\n </div>\n </div>\n </PluginGate>\n </RequireAgentReachable>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AIPage.js","names":[],"sources":["../../../src/pages/ai/AIPage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport { useMatch, useParams } from 'react-router-dom';\nimport {\n LayoutDashboard,\n FileCode2,\n FileText,\n MessageSquare,\n MessagesSquare,\n ListTodo,\n BarChart3,\n Play,\n Brain,\n MapIcon,\n} from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport {\n PageHeader,\n EmptyState,\n ErrorState,\n PluginGate,\n RequireAgentReachable,\n} from '@/components/shared';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { AI_CORE_PACKAGE, AI_PROVIDER_PACKAGES } from '@/constants/pluginCatalog';\nimport { PluginHarnessPicker } from '@/components/ai/PluginHarnessPicker';\n\nimport { AIOverviewTab } from '@/components/ai/AIOverviewTab';\nimport { AITemplatesTab } from '@/components/ai/AITemplatesTab';\nimport { AIContextsTab } from '@/components/ai/AIContextsTab';\nimport { AIPromptsTab } from '@/components/ai/AIPromptsTab';\nimport { AISessionsTab } from '@/components/ai/AISessionsTab';\nimport { AITasksTab } from '@/components/ai/AITasksTab';\nimport { AIStatsTab } from '@/components/ai/AIStatsTab';\nimport { AIPlaygroundTab } from '@/components/ai/AIPlaygroundTab';\nimport { ClaudeCredentialsCard } from '@/components/ai/ClaudeCredentialsCard';\nimport { BridleConfigPanel } from '@/components/agent-manager/BridleConfigPanel';\nimport { AIPlanPanel } from '@/components/ai/AIPlanPanel';\n\ntype AIMode = 'plan' | 'ai';\n\ntype AITab =\n | 'overview'\n | 'templates'\n | 'contexts'\n | 'prompts'\n | 'sessions'\n | 'tasks'\n | 'stats'\n | 'playground'\n | 'config';\n\nconst tabs: { id: AITab; icon: typeof LayoutDashboard }[] = [\n { id: 'overview', icon: LayoutDashboard },\n { id: 'templates', icon: FileCode2 },\n { id: 'contexts', icon: FileText },\n { id: 'prompts', icon: MessageSquare },\n { id: 'sessions', icon: MessagesSquare },\n { id: 'tasks', icon: ListTodo },\n { id: 'stats', icon: BarChart3 },\n { id: 'playground', icon: Play },\n { id: 'config', icon: LayoutDashboard },\n];\n\nconst validTabs = new Set<string>(tabs.map((t) => t.id));\n\n/**\n * AI page — tab-based layout for AI / vibecoding features.\n *\n * URL shape:\n * /ai-tools → routed to /ai-tools/ai/overview (handled in VibeControlsRoutes).\n * /ai-tools/plan → Plan mode (full-bleed plannotator iframe panel).\n * /ai-tools/ai/:tab → AI mode (the existing 9 sub-tabs).\n *\n * The mode toggle below `PageHeader` switches between the two without a\n * full route reload — the AIPlanPanel inside Plan mode reuses the same\n * agent context that AI mode already loaded.\n */\nexport function AIPage() {\n const { t } = useI18n();\n const { tab: tabParam } = useParams<{ tab?: string }>();\n const planRouteMatch = useMatch('vibecontrols/ai-tools/plan');\n const activeMode: AIMode = planRouteMatch ? 'plan' : 'ai';\n const activeTab: AITab = tabParam && validTabs.has(tabParam) ? (tabParam as AITab) : 'overview';\n const handleNavigate = useVibeNavigate();\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const tabLabel = useCallback(\n (tabId: AITab): string => {\n switch (tabId) {\n case 'overview':\n return tr('ai.overview', 'Overview');\n case 'templates':\n return tr('ai.templates', 'Templates');\n case 'contexts':\n return tr('ai.contexts', 'Contexts');\n case 'prompts':\n return tr('ai.prompts', 'Prompts');\n case 'sessions':\n return tr('nav.sessions', 'Sessions');\n case 'tasks':\n return tr('ai.tasks', 'Tasks');\n case 'stats':\n return tr('ai.stats', 'Stats');\n case 'playground':\n return tr('ai.playground', 'Playground');\n case 'config':\n return tr('ai.config', 'Config');\n default:\n return tabId;\n }\n },\n [tr]\n );\n\n const { agents, loading: agentsLoading, error: agentsError, refetch } = useCachedInitialData();\n\n // If the initial data query throws (e.g. gateway query-cost limit, network\n // error, transient federation issue), surface a friendly fallback rather\n // than the raw GraphQL error. The technical details still go to the console\n // for debuggability — without PII, since the error message itself never\n // contains secrets.\n if (agentsError) {\n // eslint-disable-next-line no-console -- intentional debug breadcrumb for failed AI workspace load\n console.error('[ai] failed to load initial workspace data', agentsError);\n }\n const activeAgent = useMemo(\n () => agents.find((agent) => agent.isActive && agent.tunnelUrl && agent.agentApiKey) ?? null,\n [agents]\n );\n const agentId = activeAgent?.id ?? '';\n // AgentRef threads the canonical agent → profile → plugins/data\n // hierarchy through every aiApi call. Components receive the ref\n // and construct paths via `agentApiPath(profile, ...)`.\n const { active: activeProfile } = useActiveAgentProfile(activeAgent?.id ?? null);\n const agentRef = useMemo(\n () => ({ id: agentId, profile: activeProfile }),\n [agentId, activeProfile]\n );\n\n const switchTab = useCallback(\n (tab: AITab) => {\n handleNavigate(tab === 'overview' ? '/ai-tools/ai' : `/ai-tools/ai/${tab}`);\n },\n [handleNavigate]\n );\n\n const switchMode = useCallback(\n (mode: AIMode) => {\n handleNavigate(mode === 'plan' ? '/ai-tools/plan' : `/ai-tools/ai/${activeTab}`);\n },\n [handleNavigate, activeTab]\n );\n\n const navigateToSession = useCallback(\n (sessionId: string) => {\n handleNavigate(`/ai-tools/sessions/${sessionId}`);\n },\n [handleNavigate]\n );\n\n const hasAgent = !!agentId;\n\n // Surface install lifecycle for debuggability. PluginGate / PluginHarnessPicker\n // already render inline error chips, but a console breadcrumb + a top-level\n // banner helps when the install actually succeeds on the agent but a stale\n // detection cache hides the success — without this the install button looked\n // like it silently swallowed errors.\n const [lastInstallNote, setLastInstallNote] = useState<string | null>(null);\n const handleInstalled = useCallback(() => {\n setLastInstallNote(tr('ai.pluginInstalled', 'Plugin installed. Refreshing…'));\n // eslint-disable-next-line no-console -- intentional install breadcrumb\n console.info('[ai] orchestrator install completed');\n }, [tr]);\n const handleHarnessChange = useCallback(() => {\n setLastInstallNote(tr('ai.harnessUpdated', 'Harness updated.'));\n // eslint-disable-next-line no-console -- intentional install breadcrumb\n console.info('[ai] harness picker changed');\n }, [tr]);\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('nav.ai', 'AI')}\n description={tr(\n 'ai.description',\n 'Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow'\n )}\n />\n\n {/* Mode pivot — Plan / AI. Sits below PageHeader, above the\n AI-mode sub-tab strip. Switching to Plan hides the 9 sub-tabs\n and renders the AIPlanPanel in their place. */}\n <div\n role=\"tablist\"\n aria-label={tr('ai.modeToggleLabel', 'AI workspace mode')}\n className=\"inline-flex rounded-lg border border-border-default bg-bg-sunken p-0.5\"\n >\n {(['plan', 'ai'] as const).map((m) => {\n const isActive = activeMode === m;\n const Icon = m === 'plan' ? MapIcon : Brain;\n return (\n <button\n key={m}\n role=\"tab\"\n aria-selected={isActive}\n data-testid={`ai-mode-toggle-${m}`}\n onClick={() => switchMode(m)}\n className={`flex items-center gap-2 px-4 py-1.5 text-sm font-medium rounded-md transition-colors ${\n isActive\n ? 'bg-bg-surface text-action-primary-bg shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {m === 'plan' ? tr('ai.mode.plan', 'Plan') : tr('ai.mode.ai', 'AI')}\n </button>\n );\n })}\n </div>\n\n {/* AI sub-tab navigation — only rendered in AI mode. */}\n {activeMode === 'ai' && (\n <div className=\"border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6\">\n <nav className=\"flex gap-1 overflow-x-auto pb-1\" role=\"tablist\">\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = activeTab === tab.id;\n return (\n <button\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => switchTab(tab.id)}\n className={`flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-t-md border-b-2 px-2.5 py-2 text-xs font-medium transition-colors sm:px-3 sm:py-2.5 sm:text-sm ${\n isActive\n ? 'border-action-primary-bg text-action-primary-bg'\n : 'border-transparent text-text-secondary hover:text-text-primary hover:border-border-default'\n }`}\n >\n <Icon className=\"h-3.5 w-3.5 sm:h-4 sm:w-4\" />\n {tabLabel(tab.id)}\n </button>\n );\n })}\n </nav>\n </div>\n )}\n\n {/* Content */}\n {agentsLoading && agents.length === 0 ? (\n <PageSkeleton variant=\"rows\" count={6} />\n ) : agentsError ? (\n <ErrorState\n title={tr('ai.loadFailed.title', 'Could not load the AI workspace')}\n message={tr(\n 'ai.loadFailed.message',\n \"We couldn't load the AI workspace. Please try again in a moment.\"\n )}\n onRetry={refetch}\n />\n ) : !hasAgent ? (\n <EmptyState\n icon={<MessagesSquare className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('ai.noActiveAgent', 'No active agent available')}\n description={tr(\n 'ai.noActiveAgentDescription',\n 'AI features require an active agent with a configured tunnel URL and API key. Add an agent or start an existing one.'\n )}\n action={\n <button\n onClick={() => handleNavigate('/agents')}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors text-sm\"\n >\n {tr('ai.goToAgents', 'Go to Agents')}\n </button>\n }\n />\n ) : activeMode === 'plan' ? (\n <RequireAgentReachable agentId={agentId}>\n <AIPlanPanel\n agent={agentRef}\n agentApiKey={activeAgent?.agentApiKey ?? null}\n tunnelUrl={activeAgent?.tunnelUrl ?? null}\n />\n </RequireAgentReachable>\n ) : (\n <RequireAgentReachable agentId={agentId}>\n <PluginGate\n agentId={agentId}\n required={[AI_CORE_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('ai.pluginRequired', 'AI plugin required')}\n title={tr('ai.installOrchestrator', 'Install the AI orchestrator')}\n description={tr(\n 'ai.installOrchestratorDesc',\n 'The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue.'\n )}\n onInstalled={handleInstalled}\n >\n <div className=\"space-y-6\">\n {lastInstallNote && (\n <div\n role=\"status\"\n className=\"rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text\"\n >\n {lastInstallNote}\n </div>\n )}\n <PluginHarnessPicker\n agentId={agentId}\n packages={AI_PROVIDER_PACKAGES}\n title={tr('ai.harnessesTitle', 'AI harnesses')}\n description={tr(\n 'ai.harnessesDesc',\n 'Install and manage the coding-agent harnesses available on this agent. You can install multiple and switch between them per-session.'\n )}\n onChange={handleHarnessChange}\n />\n <div>\n {activeTab === 'overview' && (\n <AIOverviewTab agent={agentRef} onNavigateToSession={navigateToSession} />\n )}\n {activeTab === 'templates' && <AITemplatesTab agentId={agentId} />}\n {activeTab === 'contexts' && <AIContextsTab agent={agentRef} />}\n {activeTab === 'prompts' && <AIPromptsTab agent={agentRef} />}\n {activeTab === 'sessions' && (\n <AISessionsTab agent={agentRef} onNavigateToSession={navigateToSession} />\n )}\n {activeTab === 'tasks' && <AITasksTab />}\n {activeTab === 'stats' && <AIStatsTab agent={agentRef} />}\n {activeTab === 'playground' && <AIPlaygroundTab agent={agentRef} />}\n {activeTab === 'config' && (\n <div className=\"space-y-6\">\n <ClaudeCredentialsCard agent={agentRef} />\n <BridleConfigPanel agent={agentRef} />\n </div>\n )}\n </div>\n </div>\n </PluginGate>\n </RequireAgentReachable>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { agentAiGet as e, agentAiMutate as t, agentApiPath as n } from "./aiApi.js";
2
+ //#region src/services/planApi.ts
3
+ async function r(t) {
4
+ return e(t.id, n(t.profile, "/plan-plannotator/status"));
5
+ }
6
+ async function i(e) {
7
+ return t(e.id, n(e.profile, "/plan-plannotator/install"), "POST");
8
+ }
9
+ async function a(t) {
10
+ return e(t.id, n(t.profile, "/plan-plannotator/agents/supported"));
11
+ }
12
+ async function o(e, r) {
13
+ return t(e.id, n(e.profile, `/plan-plannotator/agents/${r}/configure-hook`), "POST");
14
+ }
15
+ //#endregion
16
+ export { o as configureAgentHook, r as getPlannotatorStatus, i as installPlannotatorBinary, a as listSupportedAgents };
17
+
18
+ //# sourceMappingURL=planApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planApi.js","names":[],"sources":["../../src/services/planApi.ts"],"sourcesContent":["/**\n * Plan-mode REST proxy helpers — wraps `agentAiGet` / `agentAiMutate`\n * with narrow types for the plan plugin's REST surface.\n */\n\nimport { agentAiGet, agentAiMutate, agentApiPath, type AgentRef } from './aiApi';\n\nexport interface PlanProviderInfo {\n name: string;\n isDefault: boolean;\n capabilities: {\n modes: Array<'plan' | 'review' | 'annotate' | 'archive'>;\n supportsStreaming: boolean;\n supportsVersionHistory: boolean;\n supportsAnnotations: boolean;\n supportsArchive: boolean;\n externallyHosted: boolean;\n prereqApiPrefix?: string;\n } | null;\n}\n\nexport interface PlanSession {\n id: string;\n providerName: string;\n projectId: string;\n status: 'pending' | 'active' | 'approved' | 'denied' | 'abandoned' | 'ended';\n mode: 'plan' | 'review' | 'annotate' | 'archive';\n createdAt: string;\n updatedAt: string;\n endedAt?: string;\n uiUrl?: string;\n}\n\nexport interface AgentDetectionResult {\n agent: 'claude' | 'opencode' | 'codex' | 'pi' | 'gemini';\n name: string;\n cliInstalled: boolean;\n hookConfigured: boolean;\n configPath: string;\n instructions: string;\n}\n\nexport interface PlannotatorStatus {\n installed: boolean;\n installedPath?: string;\n version?: string;\n runningSessions: Array<{\n sessionId: string;\n pid: number;\n port: number;\n mode: string;\n startedAt: string;\n lastActivity: string;\n }>;\n}\n\n/**\n * Fetch the list of registered plan providers from the meta plugin.\n * Returns an empty array if the meta plugin isn't installed.\n */\nexport async function listPlanProviders(agent: AgentRef): Promise<PlanProviderInfo[]> {\n return agentAiGet<PlanProviderInfo[]>(agent.id, agentApiPath(agent.profile, '/plan/providers'));\n}\n\n/**\n * Fetch the plannotator provider's runtime status (binary installed +\n * running session count). Used by AIPlanPanel to gate the wizard.\n */\nexport async function getPlannotatorStatus(agent: AgentRef): Promise<PlannotatorStatus> {\n return agentAiGet<PlannotatorStatus>(\n agent.id,\n agentApiPath(agent.profile, '/plan-plannotator/status')\n );\n}\n\n/**\n * Install the plannotator binary via the provider's prereq route.\n */\nexport async function installPlannotatorBinary(\n agent: AgentRef\n): Promise<{ ok: boolean; alreadyInstalled?: boolean; path?: string; error?: string }> {\n return agentAiMutate(agent.id, agentApiPath(agent.profile, '/plan-plannotator/install'), 'POST');\n}\n\n/**\n * Detect installed AI agents in the sandbox + their plannotator-hook\n * configuration state. Drives the configure-hook wizard.\n */\nexport async function listSupportedAgents(\n agent: AgentRef\n): Promise<{ agents: AgentDetectionResult[] }> {\n return agentAiGet(agent.id, agentApiPath(agent.profile, '/plan-plannotator/agents/supported'));\n}\n\n/**\n * Configure the plannotator hook for a specific AI agent (claude / opencode\n * / codex / pi / gemini).\n */\nexport async function configureAgentHook(\n agent: AgentRef,\n agentKind: AgentDetectionResult['agent']\n): Promise<{ ok: boolean; configPath?: string; error?: string }> {\n return agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/plan-plannotator/agents/${agentKind}/configure-hook`),\n 'POST'\n );\n}\n\nexport async function unconfigureAgentHook(\n agent: AgentRef,\n agentKind: AgentDetectionResult['agent']\n): Promise<{ ok: boolean; error?: string }> {\n return agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/plan-plannotator/agents/${agentKind}/unconfigure-hook`),\n 'POST'\n );\n}\n"],"mappings":";;AAoEA,eAAsB,EAAqB,GAA6C;AACtF,QAAO,EACL,EAAM,IACN,EAAa,EAAM,SAAS,2BAA2B,CACxD;;AAMH,eAAsB,EACpB,GACqF;AACrF,QAAO,EAAc,EAAM,IAAI,EAAa,EAAM,SAAS,4BAA4B,EAAE,OAAO;;AAOlG,eAAsB,EACpB,GAC6C;AAC7C,QAAO,EAAW,EAAM,IAAI,EAAa,EAAM,SAAS,qCAAqC,CAAC;;AAOhG,eAAsB,EACpB,GACA,GAC+D;AAC/D,QAAO,EACL,EAAM,IACN,EAAa,EAAM,SAAS,4BAA4B,EAAU,iBAAiB,EACnF,OACD"}
@@ -125,6 +125,20 @@ var r = t()(n((e, t) => ({
125
125
  return delete n[t], { defaultAgentForVibe: n };
126
126
  }),
127
127
  hydrateDefaultAgentForVibe: (t) => e({ defaultAgentForVibe: t }),
128
+ preferredIdeCommand: null,
129
+ setPreferredIdeCommand: (t) => e({ preferredIdeCommand: t }),
130
+ hydratePreferredIdeCommand: (t) => e({ preferredIdeCommand: t }),
131
+ vibeIdeOverrides: {},
132
+ setVibeIdeOverride: (t, n) => e((e) => ({ vibeIdeOverrides: {
133
+ ...e.vibeIdeOverrides,
134
+ [t]: n
135
+ } })),
136
+ clearVibeIdeOverride: (t) => e((e) => {
137
+ if (!(t in e.vibeIdeOverrides)) return e;
138
+ let n = { ...e.vibeIdeOverrides };
139
+ return delete n[t], { vibeIdeOverrides: n };
140
+ }),
141
+ hydrateVibeIdeOverrides: (t) => e({ vibeIdeOverrides: t }),
128
142
  resetStore: () => e({
129
143
  activeAgentId: null,
130
144
  activeVibeId: null,
@@ -144,7 +158,9 @@ var r = t()(n((e, t) => ({
144
158
  activeVibeDeckId: null,
145
159
  vibeDeckMode: "preview",
146
160
  tabOrder: {},
147
- defaultAgentForVibe: {}
161
+ defaultAgentForVibe: {},
162
+ preferredIdeCommand: null,
163
+ vibeIdeOverrides: {}
148
164
  })
149
165
  }), {
150
166
  name: "vibecontrols-context",
@@ -167,7 +183,9 @@ var r = t()(n((e, t) => ({
167
183
  activeVibeDeckId: e.activeVibeDeckId,
168
184
  vibeDeckMode: e.vibeDeckMode,
169
185
  tabOrder: e.tabOrder,
170
- defaultAgentForVibe: e.defaultAgentForVibe
186
+ defaultAgentForVibe: e.defaultAgentForVibe,
187
+ preferredIdeCommand: e.preferredIdeCommand,
188
+ vibeIdeOverrides: e.vibeIdeOverrides
171
189
  }),
172
190
  merge: (e, t) => {
173
191
  let n = e ?? {};
@@ -1 +1 @@
1
- {"version":3,"file":"vibeControlsStore.js","names":[],"sources":["../../src/store/vibeControlsStore.ts"],"sourcesContent":["import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { createProfileScopedStorage } from '@burdenoff/fe-libs/shared/utils';\nimport type { VibeControlsView, VibeControlsTab } from '@/types';\n\n/** Persisted terminal tab info (minimal data for restoration) */\nexport interface PersistedTerminalTab {\n sessionId: string;\n name: string;\n}\n\ninterface VibeControlsState {\n // View state\n currentView: VibeControlsView;\n currentTab: VibeControlsTab;\n\n // Active context — the currently focused agent, vibe, and session.\n // Persisted to localStorage so the selection survives page refreshes.\n activeAgentId: string | null;\n activeVibeId: string | null;\n activeSessionId: string | null;\n\n // Legacy selection aliases (kept for backward compatibility with pages)\n selectedAgentId: string | null;\n selectedSessionId: string | null;\n selectedVibeId: string | null;\n\n /**\n * Per-agent \"connecting\" overlay timestamps shown after Edit Tunnel save\n * while the backend pushes OAuth creds → agent finalize() → /health/ready\n * flips to ok. The value is the *earliest* wall-clock timestamp at which\n * the overlay is allowed to clear — used as a grace window so a stale\n * `healthy:true` cache delivered during the connecting window cannot\n * clear it prematurely (root cause of the historical CONNECTING →\n * DEGRADED → ACTIVE flicker after Edit Tunnel save).\n *\n * Lives in the global store (not in AgentsPage local React state) so the\n * overlay survives navigation to AgentDetailsPage and back — the\n * documented user recovery path navigates into AgentDetailsPage so the\n * `pushAgentGatewayAuth` auto-recovery fires, and unmounting AgentsPage\n * used to discard the set entirely.\n *\n * In-memory only — not persisted (a \"connecting\" state has no meaning\n * across full page reloads).\n */\n connectingAgents: Record<string, number>;\n\n /**\n * Active profile name keyed by agent id. Phase 5B (multi-profile-per-daemon)\n * — every aiApi call constructs the canonical\n * `/api/profiles/<profile>/<rest>` path and so needs to know which profile\n * is currently selected for a given agent. The selector is exposed via\n * `useActiveAgentProfile(agentId)`.\n */\n activeProfilePerAgent: Record<string, string>;\n\n // UI state\n sidebarOpen: boolean;\n\n // Terminal tabs state (persisted for better UX when navigating away and back)\n terminalTabs: PersistedTerminalTab[];\n activeTerminalTabId: string | null;\n sessionsViewMode: 'terminal' | 'grid' | 'list';\n\n // Actions\n setCurrentView: (view: VibeControlsView) => void;\n setCurrentTab: (tab: VibeControlsTab) => void;\n\n /** Set the active agent (also resets vibe and session if agent changes) */\n setActiveAgentId: (id: string | null) => void;\n /** Set the active vibe (also resets session if vibe changes) */\n setActiveVibeId: (id: string | null) => void;\n /** Set the active session */\n setActiveSessionId: (id: string | null) => void;\n /** Clear all active context */\n clearActiveContext: () => void;\n /** Set the active profile for a given agent (Phase 5B). */\n setActiveProfile: (agentId: string, profile: string) => void;\n /** Read the currently-active profile for an agent (defaults to \"default\"). */\n getActiveProfile: (agentId: string | null | undefined) => string;\n\n /**\n * Mark an agent as \"connecting\" (e.g. immediately after an Edit Tunnel\n * save). `graceMs` is a minimum wall-clock window during which the\n * overlay cannot be cleared by an incoming probe — defends against a\n * stale `healthy:true` Apollo-cache hit racing the in-flight refetch.\n */\n markAgentConnecting: (agentId: string, graceMs?: number) => void;\n /** Clear the \"connecting\" overlay for a single agent. */\n clearAgentConnecting: (agentId: string) => void;\n /**\n * Returns true while the connecting overlay is still active for the\n * agent (entry exists AND grace window has not elapsed OR a probe\n * hasn't yet flipped it healthy).\n */\n isAgentConnecting: (agentId: string) => boolean;\n\n // Legacy setters (call through to active context)\n setSelectedAgentId: (id: string | null) => void;\n setSelectedSessionId: (id: string | null) => void;\n setSelectedVibeId: (id: string | null) => void;\n\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n\n // Terminal tabs actions\n setTerminalTabs: (tabs: PersistedTerminalTab[]) => void;\n addTerminalTab: (tab: PersistedTerminalTab) => void;\n removeTerminalTab: (sessionId: string) => void;\n setActiveTerminalTabId: (sessionId: string | null) => void;\n setSessionsViewMode: (mode: 'terminal' | 'grid' | 'list') => void;\n\n // Catalog / Component / Template active context\n activeCatalogId: string | null;\n activeComponentId: string | null;\n activeTemplateId: string | null;\n setActiveCatalogId: (id: string | null) => void;\n setActiveComponentId: (id: string | null) => void;\n setActiveTemplateId: (id: string | null) => void;\n\n // VibeDeck state\n activeVibeDeckId: string | null;\n setActiveVibeDeckId: (id: string | null) => void;\n vibeDeckMode: 'preview' | 'edit';\n setVibeDeckMode: (mode: 'preview' | 'edit') => void;\n\n // ─── iframe-plugin tab system ──────────────────────────────────────\n /**\n * Per-user, per-mountPoint, per-contextKey tab ordering. Backed by\n * the `wspace-vibecontrols-svc` UserPreferences slot — the local\n * store is a write-through cache. Reads merge against the live\n * registry on every consumer call (drop stale, append new at end).\n */\n tabOrder: Record<string, Record<string, string[]>>;\n /** Replace the entire stored order for a (mountPoint, contextKey) pair. */\n setTabOrder: (mountPoint: string, contextKey: string, order: string[]) => void;\n /** Clear the stored order so reads fall through to the registry default. */\n resetTabOrder: (mountPoint: string, contextKey: string) => void;\n /** Bulk-replace the entire tabOrder map (used by backend hydration). */\n hydrateTabOrder: (next: Record<string, Record<string, string[]>>) => void;\n\n /**\n * Per-Vibe default agent (per user). Consulted by `useEffectiveAgent`\n * before the multi-agent heuristic. Backed by the same UserPreferences\n * slot above; hydrate on app boot.\n */\n defaultAgentForVibe: Record<string, string>;\n setDefaultAgentForVibe: (vibeId: string, agentId: string) => void;\n clearDefaultAgentForVibe: (vibeId: string) => void;\n hydrateDefaultAgentForVibe: (next: Record<string, string>) => void;\n\n /** Reset all persisted state to defaults (used on logout / account switch) */\n resetStore: () => void;\n}\n\nexport const useVibeControlsStore = create<VibeControlsState>()(\n persist(\n (set, get) => ({\n // Initial state\n currentView: 'agents',\n currentTab: 'overview',\n activeAgentId: null,\n activeVibeId: null,\n activeSessionId: null,\n selectedAgentId: null,\n selectedSessionId: null,\n selectedVibeId: null,\n activeProfilePerAgent: {},\n connectingAgents: {},\n sidebarOpen: true,\n terminalTabs: [],\n activeTerminalTabId: null,\n sessionsViewMode: 'terminal',\n\n // Actions\n setCurrentView: (view) => set({ currentView: view }),\n setCurrentTab: (tab) => set({ currentTab: tab }),\n\n setActiveAgentId: (id) =>\n set((state) => ({\n activeAgentId: id,\n selectedAgentId: id,\n // Reset vibe and session when agent changes\n ...(id !== state.activeAgentId\n ? {\n activeVibeId: null,\n activeSessionId: null,\n selectedVibeId: null,\n selectedSessionId: null,\n }\n : {}),\n })),\n\n setActiveVibeId: (id) =>\n set((state) => ({\n activeVibeId: id,\n selectedVibeId: id,\n // Reset session when vibe changes\n ...(id !== state.activeVibeId ? { activeSessionId: null, selectedSessionId: null } : {}),\n })),\n\n setActiveSessionId: (id) =>\n set({\n activeSessionId: id,\n selectedSessionId: id,\n }),\n\n clearActiveContext: () =>\n set({\n activeAgentId: null,\n activeVibeId: null,\n activeSessionId: null,\n selectedAgentId: null,\n selectedVibeId: null,\n selectedSessionId: null,\n }),\n\n setActiveProfile: (agentId, profile) =>\n set((state) => ({\n activeProfilePerAgent: {\n ...state.activeProfilePerAgent,\n [agentId]: profile,\n },\n })),\n\n getActiveProfile: (agentId) => {\n if (!agentId) return 'default';\n return get().activeProfilePerAgent[agentId] ?? 'default';\n },\n\n markAgentConnecting: (agentId, graceMs = 1500) =>\n set((state) => ({\n connectingAgents: {\n ...state.connectingAgents,\n [agentId]: Date.now() + graceMs,\n },\n })),\n\n clearAgentConnecting: (agentId) =>\n set((state) => {\n if (!(agentId in state.connectingAgents)) return state;\n const next = { ...state.connectingAgents };\n delete next[agentId];\n return { connectingAgents: next };\n }),\n\n isAgentConnecting: (agentId) => agentId in get().connectingAgents,\n\n // Legacy setters\n setSelectedAgentId: (id) => set({ selectedAgentId: id, activeAgentId: id }),\n setSelectedSessionId: (id) => set({ selectedSessionId: id, activeSessionId: id }),\n setSelectedVibeId: (id) => set({ selectedVibeId: id, activeVibeId: id }),\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n setSidebarOpen: (open) => set({ sidebarOpen: open }),\n\n // Terminal tabs actions\n setTerminalTabs: (tabs) => set({ terminalTabs: tabs }),\n addTerminalTab: (tab) =>\n set((state) => {\n // Don't add duplicates\n if (state.terminalTabs.some((t) => t.sessionId === tab.sessionId)) {\n return state;\n }\n return { terminalTabs: [...state.terminalTabs, tab] };\n }),\n removeTerminalTab: (sessionId) =>\n set((state) => ({\n terminalTabs: state.terminalTabs.filter((t) => t.sessionId !== sessionId),\n // Clear active tab if it was removed\n activeTerminalTabId:\n state.activeTerminalTabId === sessionId ? null : state.activeTerminalTabId,\n })),\n setActiveTerminalTabId: (sessionId) => set({ activeTerminalTabId: sessionId }),\n setSessionsViewMode: (mode) => set({ sessionsViewMode: mode }),\n\n // Catalog / Component / Template\n activeCatalogId: null,\n activeComponentId: null,\n activeTemplateId: null,\n setActiveCatalogId: (id) => set({ activeCatalogId: id }),\n setActiveComponentId: (id) => set({ activeComponentId: id }),\n setActiveTemplateId: (id) => set({ activeTemplateId: id }),\n\n // VibeDeck\n activeVibeDeckId: null,\n setActiveVibeDeckId: (id) => set({ activeVibeDeckId: id }),\n vibeDeckMode: 'preview',\n setVibeDeckMode: (mode) => set({ vibeDeckMode: mode }),\n\n // iframe-plugin tab system\n tabOrder: {},\n setTabOrder: (mountPoint, contextKey, order) =>\n set((state) => ({\n tabOrder: {\n ...state.tabOrder,\n [mountPoint]: {\n ...(state.tabOrder[mountPoint] ?? {}),\n [contextKey]: order,\n },\n },\n })),\n resetTabOrder: (mountPoint, contextKey) =>\n set((state) => {\n const mountBucket = state.tabOrder[mountPoint];\n if (!mountBucket || !(contextKey in mountBucket)) return state;\n const nextMountBucket = { ...mountBucket };\n delete nextMountBucket[contextKey];\n return {\n tabOrder: {\n ...state.tabOrder,\n [mountPoint]: nextMountBucket,\n },\n };\n }),\n hydrateTabOrder: (next) => set({ tabOrder: next }),\n\n defaultAgentForVibe: {},\n setDefaultAgentForVibe: (vibeId, agentId) =>\n set((state) => ({\n defaultAgentForVibe: { ...state.defaultAgentForVibe, [vibeId]: agentId },\n })),\n clearDefaultAgentForVibe: (vibeId) =>\n set((state) => {\n if (!(vibeId in state.defaultAgentForVibe)) return state;\n const next = { ...state.defaultAgentForVibe };\n delete next[vibeId];\n return { defaultAgentForVibe: next };\n }),\n hydrateDefaultAgentForVibe: (next) => set({ defaultAgentForVibe: next }),\n\n // Reset\n resetStore: () =>\n set({\n activeAgentId: null,\n activeVibeId: null,\n activeSessionId: null,\n selectedAgentId: null,\n selectedVibeId: null,\n selectedSessionId: null,\n activeProfilePerAgent: {},\n connectingAgents: {},\n sidebarOpen: true,\n terminalTabs: [],\n activeTerminalTabId: null,\n sessionsViewMode: 'terminal',\n activeCatalogId: null,\n activeComponentId: null,\n activeTemplateId: null,\n activeVibeDeckId: null,\n vibeDeckMode: 'preview',\n tabOrder: {},\n defaultAgentForVibe: {},\n }),\n }),\n {\n name: 'vibecontrols-context',\n storage: createProfileScopedStorage(),\n // Persist context, UI state, and terminal tabs\n partialize: (state) => ({\n activeAgentId: state.activeAgentId,\n activeVibeId: state.activeVibeId,\n activeSessionId: state.activeSessionId,\n selectedAgentId: state.selectedAgentId,\n selectedVibeId: state.selectedVibeId,\n selectedSessionId: state.selectedSessionId,\n activeProfilePerAgent: state.activeProfilePerAgent,\n sidebarOpen: state.sidebarOpen,\n terminalTabs: state.terminalTabs,\n activeTerminalTabId: state.activeTerminalTabId,\n sessionsViewMode: state.sessionsViewMode,\n activeCatalogId: state.activeCatalogId,\n activeComponentId: state.activeComponentId,\n activeTemplateId: state.activeTemplateId,\n activeVibeDeckId: state.activeVibeDeckId,\n vibeDeckMode: state.vibeDeckMode,\n tabOrder: state.tabOrder,\n defaultAgentForVibe: state.defaultAgentForVibe,\n }),\n merge: (persisted, current) => {\n const p = (persisted ?? {}) as Partial<VibeControlsState>;\n return {\n ...current,\n ...p,\n tabOrder: p.tabOrder ?? current.tabOrder ?? {},\n defaultAgentForVibe: p.defaultAgentForVibe ?? current.defaultAgentForVibe ?? {},\n };\n },\n }\n )\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"vibeControlsStore.js","names":[],"sources":["../../src/store/vibeControlsStore.ts"],"sourcesContent":["import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { createProfileScopedStorage } from '@burdenoff/fe-libs/shared/utils';\nimport type { VibeControlsView, VibeControlsTab } from '@/types';\n\n/** Persisted terminal tab info (minimal data for restoration) */\nexport interface PersistedTerminalTab {\n sessionId: string;\n name: string;\n}\n\ninterface VibeControlsState {\n // View state\n currentView: VibeControlsView;\n currentTab: VibeControlsTab;\n\n // Active context — the currently focused agent, vibe, and session.\n // Persisted to localStorage so the selection survives page refreshes.\n activeAgentId: string | null;\n activeVibeId: string | null;\n activeSessionId: string | null;\n\n // Legacy selection aliases (kept for backward compatibility with pages)\n selectedAgentId: string | null;\n selectedSessionId: string | null;\n selectedVibeId: string | null;\n\n /**\n * Per-agent \"connecting\" overlay timestamps shown after Edit Tunnel save\n * while the backend pushes OAuth creds → agent finalize() → /health/ready\n * flips to ok. The value is the *earliest* wall-clock timestamp at which\n * the overlay is allowed to clear — used as a grace window so a stale\n * `healthy:true` cache delivered during the connecting window cannot\n * clear it prematurely (root cause of the historical CONNECTING →\n * DEGRADED → ACTIVE flicker after Edit Tunnel save).\n *\n * Lives in the global store (not in AgentsPage local React state) so the\n * overlay survives navigation to AgentDetailsPage and back — the\n * documented user recovery path navigates into AgentDetailsPage so the\n * `pushAgentGatewayAuth` auto-recovery fires, and unmounting AgentsPage\n * used to discard the set entirely.\n *\n * In-memory only — not persisted (a \"connecting\" state has no meaning\n * across full page reloads).\n */\n connectingAgents: Record<string, number>;\n\n /**\n * Active profile name keyed by agent id. Phase 5B (multi-profile-per-daemon)\n * — every aiApi call constructs the canonical\n * `/api/profiles/<profile>/<rest>` path and so needs to know which profile\n * is currently selected for a given agent. The selector is exposed via\n * `useActiveAgentProfile(agentId)`.\n */\n activeProfilePerAgent: Record<string, string>;\n\n // UI state\n sidebarOpen: boolean;\n\n // Terminal tabs state (persisted for better UX when navigating away and back)\n terminalTabs: PersistedTerminalTab[];\n activeTerminalTabId: string | null;\n sessionsViewMode: 'terminal' | 'grid' | 'list';\n\n // Actions\n setCurrentView: (view: VibeControlsView) => void;\n setCurrentTab: (tab: VibeControlsTab) => void;\n\n /** Set the active agent (also resets vibe and session if agent changes) */\n setActiveAgentId: (id: string | null) => void;\n /** Set the active vibe (also resets session if vibe changes) */\n setActiveVibeId: (id: string | null) => void;\n /** Set the active session */\n setActiveSessionId: (id: string | null) => void;\n /** Clear all active context */\n clearActiveContext: () => void;\n /** Set the active profile for a given agent (Phase 5B). */\n setActiveProfile: (agentId: string, profile: string) => void;\n /** Read the currently-active profile for an agent (defaults to \"default\"). */\n getActiveProfile: (agentId: string | null | undefined) => string;\n\n /**\n * Mark an agent as \"connecting\" (e.g. immediately after an Edit Tunnel\n * save). `graceMs` is a minimum wall-clock window during which the\n * overlay cannot be cleared by an incoming probe — defends against a\n * stale `healthy:true` Apollo-cache hit racing the in-flight refetch.\n */\n markAgentConnecting: (agentId: string, graceMs?: number) => void;\n /** Clear the \"connecting\" overlay for a single agent. */\n clearAgentConnecting: (agentId: string) => void;\n /**\n * Returns true while the connecting overlay is still active for the\n * agent (entry exists AND grace window has not elapsed OR a probe\n * hasn't yet flipped it healthy).\n */\n isAgentConnecting: (agentId: string) => boolean;\n\n // Legacy setters (call through to active context)\n setSelectedAgentId: (id: string | null) => void;\n setSelectedSessionId: (id: string | null) => void;\n setSelectedVibeId: (id: string | null) => void;\n\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n\n // Terminal tabs actions\n setTerminalTabs: (tabs: PersistedTerminalTab[]) => void;\n addTerminalTab: (tab: PersistedTerminalTab) => void;\n removeTerminalTab: (sessionId: string) => void;\n setActiveTerminalTabId: (sessionId: string | null) => void;\n setSessionsViewMode: (mode: 'terminal' | 'grid' | 'list') => void;\n\n // Catalog / Component / Template active context\n activeCatalogId: string | null;\n activeComponentId: string | null;\n activeTemplateId: string | null;\n setActiveCatalogId: (id: string | null) => void;\n setActiveComponentId: (id: string | null) => void;\n setActiveTemplateId: (id: string | null) => void;\n\n // VibeDeck state\n activeVibeDeckId: string | null;\n setActiveVibeDeckId: (id: string | null) => void;\n vibeDeckMode: 'preview' | 'edit';\n setVibeDeckMode: (mode: 'preview' | 'edit') => void;\n\n // ─── iframe-plugin tab system ──────────────────────────────────────\n /**\n * Per-user, per-mountPoint, per-contextKey tab ordering. Backed by\n * the `wspace-vibecontrols-svc` UserPreferences slot — the local\n * store is a write-through cache. Reads merge against the live\n * registry on every consumer call (drop stale, append new at end).\n */\n tabOrder: Record<string, Record<string, string[]>>;\n /** Replace the entire stored order for a (mountPoint, contextKey) pair. */\n setTabOrder: (mountPoint: string, contextKey: string, order: string[]) => void;\n /** Clear the stored order so reads fall through to the registry default. */\n resetTabOrder: (mountPoint: string, contextKey: string) => void;\n /** Bulk-replace the entire tabOrder map (used by backend hydration). */\n hydrateTabOrder: (next: Record<string, Record<string, string[]>>) => void;\n\n /**\n * Per-Vibe default agent (per user). Consulted by `useEffectiveAgent`\n * before the multi-agent heuristic. Backed by the same UserPreferences\n * slot above; hydrate on app boot.\n */\n defaultAgentForVibe: Record<string, string>;\n setDefaultAgentForVibe: (vibeId: string, agentId: string) => void;\n clearDefaultAgentForVibe: (vibeId: string) => void;\n hydrateDefaultAgentForVibe: (next: Record<string, string>) => void;\n\n /**\n * User's default IDE command for \"Open in editor\" actions. Token from\n * `IDE_PRESETS` (`code`, `cursor`, `idea`, ...). `null` falls back to\n * `code`. Backend mirror: `UserPreferences.preferredIdeCommand`.\n */\n preferredIdeCommand: string | null;\n setPreferredIdeCommand: (cmd: string | null) => void;\n hydratePreferredIdeCommand: (cmd: string | null) => void;\n\n /** Per-Vibe IDE override. Precedence: override → user default → 'code'. */\n vibeIdeOverrides: Record<string, string>;\n setVibeIdeOverride: (vibeId: string, cmd: string) => void;\n clearVibeIdeOverride: (vibeId: string) => void;\n hydrateVibeIdeOverrides: (next: Record<string, string>) => void;\n\n /** Reset all persisted state to defaults (used on logout / account switch) */\n resetStore: () => void;\n}\n\nexport const useVibeControlsStore = create<VibeControlsState>()(\n persist(\n (set, get) => ({\n // Initial state\n currentView: 'agents',\n currentTab: 'overview',\n activeAgentId: null,\n activeVibeId: null,\n activeSessionId: null,\n selectedAgentId: null,\n selectedSessionId: null,\n selectedVibeId: null,\n activeProfilePerAgent: {},\n connectingAgents: {},\n sidebarOpen: true,\n terminalTabs: [],\n activeTerminalTabId: null,\n sessionsViewMode: 'terminal',\n\n // Actions\n setCurrentView: (view) => set({ currentView: view }),\n setCurrentTab: (tab) => set({ currentTab: tab }),\n\n setActiveAgentId: (id) =>\n set((state) => ({\n activeAgentId: id,\n selectedAgentId: id,\n // Reset vibe and session when agent changes\n ...(id !== state.activeAgentId\n ? {\n activeVibeId: null,\n activeSessionId: null,\n selectedVibeId: null,\n selectedSessionId: null,\n }\n : {}),\n })),\n\n setActiveVibeId: (id) =>\n set((state) => ({\n activeVibeId: id,\n selectedVibeId: id,\n // Reset session when vibe changes\n ...(id !== state.activeVibeId ? { activeSessionId: null, selectedSessionId: null } : {}),\n })),\n\n setActiveSessionId: (id) =>\n set({\n activeSessionId: id,\n selectedSessionId: id,\n }),\n\n clearActiveContext: () =>\n set({\n activeAgentId: null,\n activeVibeId: null,\n activeSessionId: null,\n selectedAgentId: null,\n selectedVibeId: null,\n selectedSessionId: null,\n }),\n\n setActiveProfile: (agentId, profile) =>\n set((state) => ({\n activeProfilePerAgent: {\n ...state.activeProfilePerAgent,\n [agentId]: profile,\n },\n })),\n\n getActiveProfile: (agentId) => {\n if (!agentId) return 'default';\n return get().activeProfilePerAgent[agentId] ?? 'default';\n },\n\n markAgentConnecting: (agentId, graceMs = 1500) =>\n set((state) => ({\n connectingAgents: {\n ...state.connectingAgents,\n [agentId]: Date.now() + graceMs,\n },\n })),\n\n clearAgentConnecting: (agentId) =>\n set((state) => {\n if (!(agentId in state.connectingAgents)) return state;\n const next = { ...state.connectingAgents };\n delete next[agentId];\n return { connectingAgents: next };\n }),\n\n isAgentConnecting: (agentId) => agentId in get().connectingAgents,\n\n // Legacy setters\n setSelectedAgentId: (id) => set({ selectedAgentId: id, activeAgentId: id }),\n setSelectedSessionId: (id) => set({ selectedSessionId: id, activeSessionId: id }),\n setSelectedVibeId: (id) => set({ selectedVibeId: id, activeVibeId: id }),\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n setSidebarOpen: (open) => set({ sidebarOpen: open }),\n\n // Terminal tabs actions\n setTerminalTabs: (tabs) => set({ terminalTabs: tabs }),\n addTerminalTab: (tab) =>\n set((state) => {\n // Don't add duplicates\n if (state.terminalTabs.some((t) => t.sessionId === tab.sessionId)) {\n return state;\n }\n return { terminalTabs: [...state.terminalTabs, tab] };\n }),\n removeTerminalTab: (sessionId) =>\n set((state) => ({\n terminalTabs: state.terminalTabs.filter((t) => t.sessionId !== sessionId),\n // Clear active tab if it was removed\n activeTerminalTabId:\n state.activeTerminalTabId === sessionId ? null : state.activeTerminalTabId,\n })),\n setActiveTerminalTabId: (sessionId) => set({ activeTerminalTabId: sessionId }),\n setSessionsViewMode: (mode) => set({ sessionsViewMode: mode }),\n\n // Catalog / Component / Template\n activeCatalogId: null,\n activeComponentId: null,\n activeTemplateId: null,\n setActiveCatalogId: (id) => set({ activeCatalogId: id }),\n setActiveComponentId: (id) => set({ activeComponentId: id }),\n setActiveTemplateId: (id) => set({ activeTemplateId: id }),\n\n // VibeDeck\n activeVibeDeckId: null,\n setActiveVibeDeckId: (id) => set({ activeVibeDeckId: id }),\n vibeDeckMode: 'preview',\n setVibeDeckMode: (mode) => set({ vibeDeckMode: mode }),\n\n // iframe-plugin tab system\n tabOrder: {},\n setTabOrder: (mountPoint, contextKey, order) =>\n set((state) => ({\n tabOrder: {\n ...state.tabOrder,\n [mountPoint]: {\n ...(state.tabOrder[mountPoint] ?? {}),\n [contextKey]: order,\n },\n },\n })),\n resetTabOrder: (mountPoint, contextKey) =>\n set((state) => {\n const mountBucket = state.tabOrder[mountPoint];\n if (!mountBucket || !(contextKey in mountBucket)) return state;\n const nextMountBucket = { ...mountBucket };\n delete nextMountBucket[contextKey];\n return {\n tabOrder: {\n ...state.tabOrder,\n [mountPoint]: nextMountBucket,\n },\n };\n }),\n hydrateTabOrder: (next) => set({ tabOrder: next }),\n\n defaultAgentForVibe: {},\n setDefaultAgentForVibe: (vibeId, agentId) =>\n set((state) => ({\n defaultAgentForVibe: { ...state.defaultAgentForVibe, [vibeId]: agentId },\n })),\n clearDefaultAgentForVibe: (vibeId) =>\n set((state) => {\n if (!(vibeId in state.defaultAgentForVibe)) return state;\n const next = { ...state.defaultAgentForVibe };\n delete next[vibeId];\n return { defaultAgentForVibe: next };\n }),\n hydrateDefaultAgentForVibe: (next) => set({ defaultAgentForVibe: next }),\n\n preferredIdeCommand: null,\n setPreferredIdeCommand: (cmd) => set({ preferredIdeCommand: cmd }),\n hydratePreferredIdeCommand: (cmd) => set({ preferredIdeCommand: cmd }),\n\n vibeIdeOverrides: {},\n setVibeIdeOverride: (vibeId, cmd) =>\n set((state) => ({\n vibeIdeOverrides: { ...state.vibeIdeOverrides, [vibeId]: cmd },\n })),\n clearVibeIdeOverride: (vibeId) =>\n set((state) => {\n if (!(vibeId in state.vibeIdeOverrides)) return state;\n const next = { ...state.vibeIdeOverrides };\n delete next[vibeId];\n return { vibeIdeOverrides: next };\n }),\n hydrateVibeIdeOverrides: (next) => set({ vibeIdeOverrides: next }),\n\n // Reset\n resetStore: () =>\n set({\n activeAgentId: null,\n activeVibeId: null,\n activeSessionId: null,\n selectedAgentId: null,\n selectedVibeId: null,\n selectedSessionId: null,\n activeProfilePerAgent: {},\n connectingAgents: {},\n sidebarOpen: true,\n terminalTabs: [],\n activeTerminalTabId: null,\n sessionsViewMode: 'terminal',\n activeCatalogId: null,\n activeComponentId: null,\n activeTemplateId: null,\n activeVibeDeckId: null,\n vibeDeckMode: 'preview',\n tabOrder: {},\n defaultAgentForVibe: {},\n preferredIdeCommand: null,\n vibeIdeOverrides: {},\n }),\n }),\n {\n name: 'vibecontrols-context',\n storage: createProfileScopedStorage(),\n // Persist context, UI state, and terminal tabs\n partialize: (state) => ({\n activeAgentId: state.activeAgentId,\n activeVibeId: state.activeVibeId,\n activeSessionId: state.activeSessionId,\n selectedAgentId: state.selectedAgentId,\n selectedVibeId: state.selectedVibeId,\n selectedSessionId: state.selectedSessionId,\n activeProfilePerAgent: state.activeProfilePerAgent,\n sidebarOpen: state.sidebarOpen,\n terminalTabs: state.terminalTabs,\n activeTerminalTabId: state.activeTerminalTabId,\n sessionsViewMode: state.sessionsViewMode,\n activeCatalogId: state.activeCatalogId,\n activeComponentId: state.activeComponentId,\n activeTemplateId: state.activeTemplateId,\n activeVibeDeckId: state.activeVibeDeckId,\n vibeDeckMode: state.vibeDeckMode,\n tabOrder: state.tabOrder,\n defaultAgentForVibe: state.defaultAgentForVibe,\n preferredIdeCommand: state.preferredIdeCommand,\n vibeIdeOverrides: state.vibeIdeOverrides,\n }),\n merge: (persisted, current) => {\n const p = (persisted ?? {}) as Partial<VibeControlsState>;\n return {\n ...current,\n ...p,\n tabOrder: p.tabOrder ?? current.tabOrder ?? {},\n defaultAgentForVibe: p.defaultAgentForVibe ?? current.defaultAgentForVibe ?? {},\n };\n },\n }\n )\n);\n"],"mappings":"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"}