@burdenoff/microfe-vibecontrols 2026.613.1 → 2026.618.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/agents/AgentConnectionRecovery.js +100 -94
- package/dist/components/agents/AgentConnectionRecovery.js.map +1 -1
- package/dist/components/agents/AgentMaintenanceTab.js +177 -160
- package/dist/components/agents/AgentMaintenanceTab.js.map +1 -1
- package/dist/components/agents/OpenInBrowserEditorButton.js +102 -94
- package/dist/components/agents/OpenInBrowserEditorButton.js.map +1 -1
- package/dist/components/assistant/AssistantChatArea.js +1 -0
- package/dist/components/assistant/AssistantChatArea.js.map +1 -1
- package/dist/components/assistant/AssistantModeSelector.js +20 -19
- package/dist/components/assistant/AssistantModeSelector.js.map +1 -1
- package/dist/components/tunnels/TunnelCard.js +3 -3
- package/dist/components/tunnels/TunnelCard.js.map +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js +36 -36
- package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
- package/dist/config/assistantUsecases.js +11 -0
- package/dist/config/assistantUsecases.js.map +1 -1
- package/dist/constants/permissions.js +38 -6
- package/dist/constants/permissions.js.map +1 -1
- package/dist/index.js +4 -3
- package/dist/pages/CatalogDetailPage.js +155 -155
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +110 -109
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +338 -338
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +104 -103
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js +140 -132
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +201 -199
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js +62 -61
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +568 -555
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js +104 -89
- package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
- package/dist/pages/ai/AIPage.js +116 -107
- package/dist/pages/ai/AIPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js +46 -34
- package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
- package/dist/pages/ai/AgentManagerPage.js +112 -100
- package/dist/pages/ai/AgentManagerPage.js.map +1 -1
- package/dist/pages/tunnels/TunnelDetailsPage.js +177 -153
- package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/TunnelsTab.js +119 -111
- package/dist/pages/tunnels/TunnelsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +24 -24
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +9 -8
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +13 -13
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +19 -15
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
- package/dist/services/assistantApi.js +9 -3
- package/dist/services/assistantApi.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js +120 -0
- package/dist/services/createSandboxAssistantTransport.js.map +1 -0
- package/dist/store/assistantStore.js +1 -1
- package/dist/store/assistantStore.js.map +1 -1
- package/package.json +5 -1
package/dist/pages/ai/AIPage.js
CHANGED
|
@@ -1,87 +1,90 @@
|
|
|
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 { ErrorState as
|
|
5
|
-
import { EmptyState as
|
|
6
|
-
import { AI_CORE_PACKAGE as
|
|
7
|
-
import { PluginGate as
|
|
8
|
-
import { RequireAgentReachable as
|
|
9
|
-
import { useActiveAgentProfile as
|
|
4
|
+
import { ErrorState as ee } from "../../components/shared/ErrorState.js";
|
|
5
|
+
import { EmptyState as r } from "../../components/shared/EmptyState.js";
|
|
6
|
+
import { AI_CORE_PACKAGE as i, AI_PROVIDER_PACKAGES as a } from "../../constants/pluginCatalog.js";
|
|
7
|
+
import { PluginGate as o } from "../../components/shared/PluginGate.js";
|
|
8
|
+
import { RequireAgentReachable as s } from "../../components/shared/RequireAgentReachable.js";
|
|
9
|
+
import { useActiveAgentProfile as c } from "../../hooks/useActiveAgentProfile.js";
|
|
10
10
|
import "../../components/shared/index.js";
|
|
11
|
+
import { VIBE_PERMISSIONS as l } from "../../constants/permissions.js";
|
|
11
12
|
import { PageSkeleton as u } from "../../components/shared/PageSkeleton.js";
|
|
12
|
-
import { PluginHarnessPicker as
|
|
13
|
-
import { AITemplatesTab as
|
|
14
|
-
import { AIContextsTab as
|
|
15
|
-
import { AIPromptsTab as
|
|
16
|
-
import { AITasksTab as
|
|
17
|
-
import { AIStatsTab as
|
|
18
|
-
import { AIPlaygroundTab as
|
|
19
|
-
import { BridleConfigPanel as
|
|
20
|
-
import { AIOverviewTab as
|
|
21
|
-
import { InstructionFilesManager as
|
|
22
|
-
import { AISessionsTab as
|
|
23
|
-
import { AIQueueTab as
|
|
24
|
-
import { ClaudeCredentialsCard as
|
|
25
|
-
import { AIPlanPanel as
|
|
26
|
-
import { AIModeToggle as
|
|
27
|
-
import { useCallback as
|
|
28
|
-
import { useI18n as
|
|
29
|
-
import { BarChart3 as
|
|
30
|
-
import { useMatch as
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
13
|
+
import { PluginHarnessPicker as te } from "../../components/ai/PluginHarnessPicker.js";
|
|
14
|
+
import { AITemplatesTab as ne } from "../../components/ai/AITemplatesTab.js";
|
|
15
|
+
import { AIContextsTab as d } from "../../components/ai/AIContextsTab.js";
|
|
16
|
+
import { AIPromptsTab as re } from "../../components/ai/AIPromptsTab.js";
|
|
17
|
+
import { AITasksTab as f } from "../../components/ai/AITasksTab.js";
|
|
18
|
+
import { AIStatsTab as p } from "../../components/ai/AIStatsTab.js";
|
|
19
|
+
import { AIPlaygroundTab as m } from "../../components/ai/AIPlaygroundTab.js";
|
|
20
|
+
import { BridleConfigPanel as ie } from "../../components/agent-manager/BridleConfigPanel.js";
|
|
21
|
+
import { AIOverviewTab as h } from "../../components/ai/AIOverviewTab.js";
|
|
22
|
+
import { InstructionFilesManager as g } from "../../components/ai/InstructionFilesManager.js";
|
|
23
|
+
import { AISessionsTab as ae } from "../../components/ai/AISessionsTab.js";
|
|
24
|
+
import { AIQueueTab as _ } from "../../components/ai/AIQueueTab.js";
|
|
25
|
+
import { ClaudeCredentialsCard as v } from "../../components/ai/ClaudeCredentialsCard.js";
|
|
26
|
+
import { AIPlanPanel as y } from "../../components/ai/AIPlanPanel.js";
|
|
27
|
+
import { AIModeToggle as b } from "../../components/ai/AIModeToggle.js";
|
|
28
|
+
import { useCallback as x, useMemo as S, useState as oe } from "react";
|
|
29
|
+
import { useI18n as se } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
30
|
+
import { BarChart3 as C, FileCode2 as w, FileText as T, LayoutDashboard as E, ListChecks as D, ListTodo as O, MessageSquare as k, MessagesSquare as A, Play as j, ScrollText as M } from "lucide-react";
|
|
31
|
+
import { useMatch as N, useParams as P } from "react-router-dom";
|
|
32
|
+
import { usePermissions as F } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
33
|
+
import { jsx as I, jsxs as L } from "react/jsx-runtime";
|
|
34
|
+
import { AccessDenied as R } from "@burdenoff/fe-libs/shared/components";
|
|
35
|
+
import { MotionPage as z } from "@burdenoff/fe-libs/shared/motion";
|
|
33
36
|
//#region src/pages/ai/AIPage.tsx
|
|
34
|
-
var
|
|
37
|
+
var B = [
|
|
35
38
|
{
|
|
36
39
|
id: "overview",
|
|
37
|
-
icon:
|
|
40
|
+
icon: E
|
|
38
41
|
},
|
|
39
42
|
{
|
|
40
43
|
id: "templates",
|
|
41
|
-
icon:
|
|
44
|
+
icon: w
|
|
42
45
|
},
|
|
43
46
|
{
|
|
44
47
|
id: "contexts",
|
|
45
|
-
icon:
|
|
48
|
+
icon: T
|
|
46
49
|
},
|
|
47
50
|
{
|
|
48
51
|
id: "prompts",
|
|
49
|
-
icon:
|
|
52
|
+
icon: k
|
|
50
53
|
},
|
|
51
54
|
{
|
|
52
55
|
id: "instructions",
|
|
53
|
-
icon:
|
|
56
|
+
icon: M
|
|
54
57
|
},
|
|
55
58
|
{
|
|
56
59
|
id: "sessions",
|
|
57
|
-
icon:
|
|
60
|
+
icon: A
|
|
58
61
|
},
|
|
59
62
|
{
|
|
60
63
|
id: "queue",
|
|
61
|
-
icon:
|
|
64
|
+
icon: D
|
|
62
65
|
},
|
|
63
66
|
{
|
|
64
67
|
id: "tasks",
|
|
65
|
-
icon:
|
|
68
|
+
icon: O
|
|
66
69
|
},
|
|
67
70
|
{
|
|
68
71
|
id: "stats",
|
|
69
|
-
icon:
|
|
72
|
+
icon: C
|
|
70
73
|
},
|
|
71
74
|
{
|
|
72
75
|
id: "playground",
|
|
73
|
-
icon:
|
|
76
|
+
icon: j
|
|
74
77
|
},
|
|
75
78
|
{
|
|
76
79
|
id: "config",
|
|
77
|
-
icon:
|
|
80
|
+
icon: E
|
|
78
81
|
}
|
|
79
|
-
],
|
|
80
|
-
function
|
|
81
|
-
let { t:
|
|
82
|
-
let n =
|
|
82
|
+
], V = new Set(B.map((e) => e.id));
|
|
83
|
+
function H() {
|
|
84
|
+
let { t: C } = se(), { hasPermission: w, isLoading: T } = F(), E = w(l.AGENT_READ), { tab: D } = P(), O = N("vibecontrols/ai-tools/plan") ? "plan" : "ai", k = D && V.has(D) ? D : "overview", j = e(), M = x((e, t) => {
|
|
85
|
+
let n = C(e);
|
|
83
86
|
return n === e ? t : n;
|
|
84
|
-
}, [
|
|
87
|
+
}, [C]), H = x((e) => {
|
|
85
88
|
switch (e) {
|
|
86
89
|
case "overview": return M("ai.overview", "Overview");
|
|
87
90
|
case "templates": return M("ai.templates", "Templates");
|
|
@@ -96,127 +99,130 @@ function W() {
|
|
|
96
99
|
case "config": return M("ai.config", "Config");
|
|
97
100
|
default: return e;
|
|
98
101
|
}
|
|
99
|
-
}, [M]), { agents:
|
|
100
|
-
|
|
101
|
-
let
|
|
102
|
-
id:
|
|
103
|
-
profile:
|
|
104
|
-
}), [
|
|
102
|
+
}, [M]), { agents: U, loading: W, error: G, refetch: K } = t();
|
|
103
|
+
G && console.error("[ai] failed to load initial workspace data", G);
|
|
104
|
+
let q = S(() => U.find((e) => e.isActive && e.tunnelUrl) ?? null, [U]), J = q?.id ?? "", { active: Y } = c(q?.id ?? null), X = S(() => ({
|
|
105
|
+
id: J,
|
|
106
|
+
profile: Y
|
|
107
|
+
}), [J, Y]), ce = x((e) => {
|
|
105
108
|
j(e === "overview" ? "/ai-tools/ai" : `/ai-tools/ai/${e}`);
|
|
106
|
-
}, [j]), Z =
|
|
109
|
+
}, [j]), Z = x((e) => {
|
|
107
110
|
j(`/ai-tools/sessions/${e}`);
|
|
108
|
-
}, [j]),
|
|
111
|
+
}, [j]), le = !!J, [Q, $] = oe(null), ue = x(() => {
|
|
109
112
|
$(M("ai.pluginInstalled", "Plugin installed. Opening AI chat…")), console.info("[ai] orchestrator install completed — navigating to AI chat"), j("/ai");
|
|
110
|
-
}, [M, j]),
|
|
113
|
+
}, [M, j]), de = x(() => {
|
|
111
114
|
$(M("ai.harnessUpdated", "Harness updated.")), console.info("[ai] harness picker changed");
|
|
112
115
|
}, [M]);
|
|
113
|
-
return /* @__PURE__ */
|
|
116
|
+
return T ? /* @__PURE__ */ I(z, {
|
|
117
|
+
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
118
|
+
children: /* @__PURE__ */ I(u, { variant: "detail" })
|
|
119
|
+
}) : E ? /* @__PURE__ */ L(z, {
|
|
114
120
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
115
121
|
children: [
|
|
116
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ I(n, {
|
|
117
123
|
title: M("nav.ai", "AI"),
|
|
118
124
|
description: M("ai.description", "Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow")
|
|
119
125
|
}),
|
|
120
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ I("div", {
|
|
121
127
|
"data-tour": "vibecontrols-ai-mode-toggle",
|
|
122
|
-
children: /* @__PURE__ */
|
|
123
|
-
aiHref: `/ai-tools/ai/${
|
|
124
|
-
activeMode:
|
|
128
|
+
children: /* @__PURE__ */ I(b, {
|
|
129
|
+
aiHref: `/ai-tools/ai/${k}`,
|
|
130
|
+
activeMode: O
|
|
125
131
|
})
|
|
126
132
|
}),
|
|
127
|
-
|
|
133
|
+
O === "ai" && /* @__PURE__ */ I("div", {
|
|
128
134
|
className: "border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6",
|
|
129
135
|
"data-tour": "vibecontrols-ai-tabs-nav",
|
|
130
|
-
children: /* @__PURE__ */
|
|
136
|
+
children: /* @__PURE__ */ I("nav", {
|
|
131
137
|
className: "flex gap-1 overflow-x-auto pb-1",
|
|
132
138
|
role: "tablist",
|
|
133
|
-
children:
|
|
134
|
-
let t = e.icon, n =
|
|
135
|
-
return /* @__PURE__ */
|
|
139
|
+
children: B.map((e) => {
|
|
140
|
+
let t = e.icon, n = k === e.id;
|
|
141
|
+
return /* @__PURE__ */ L("button", {
|
|
136
142
|
type: "button",
|
|
137
143
|
role: "tab",
|
|
138
144
|
"aria-selected": n,
|
|
139
|
-
onClick: () =>
|
|
145
|
+
onClick: () => ce(e.id),
|
|
140
146
|
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"}`,
|
|
141
|
-
children: [/* @__PURE__ */
|
|
147
|
+
children: [/* @__PURE__ */ I(t, { className: "size-3.5 sm:h-4 sm:w-4" }), H(e.id)]
|
|
142
148
|
}, e.id);
|
|
143
149
|
})
|
|
144
150
|
})
|
|
145
151
|
}),
|
|
146
|
-
|
|
152
|
+
W && U.length === 0 ? /* @__PURE__ */ I(u, {
|
|
147
153
|
variant: "rows",
|
|
148
154
|
count: 6
|
|
149
|
-
}) :
|
|
155
|
+
}) : G ? /* @__PURE__ */ I(ee, {
|
|
150
156
|
title: M("ai.loadFailed.title", "Could not load the AI workspace"),
|
|
151
157
|
message: M("ai.loadFailed.message", "We couldn't load the AI workspace. Please try again in a moment."),
|
|
152
|
-
onRetry:
|
|
153
|
-
}) :
|
|
154
|
-
agentId:
|
|
155
|
-
children: /* @__PURE__ */
|
|
156
|
-
agent:
|
|
157
|
-
tunnelUrl:
|
|
158
|
+
onRetry: K
|
|
159
|
+
}) : le ? O === "plan" ? /* @__PURE__ */ I(s, {
|
|
160
|
+
agentId: J,
|
|
161
|
+
children: /* @__PURE__ */ I(y, {
|
|
162
|
+
agent: X,
|
|
163
|
+
tunnelUrl: q?.tunnelUrl ?? null
|
|
158
164
|
})
|
|
159
|
-
}) : /* @__PURE__ */
|
|
160
|
-
agentId:
|
|
161
|
-
children: /* @__PURE__ */
|
|
165
|
+
}) : /* @__PURE__ */ I(s, {
|
|
166
|
+
agentId: J,
|
|
167
|
+
children: /* @__PURE__ */ I("div", {
|
|
162
168
|
"data-tour": "vibecontrols-ai-plugin-gate",
|
|
163
|
-
children: /* @__PURE__ */
|
|
164
|
-
agentId:
|
|
165
|
-
required: [
|
|
169
|
+
children: /* @__PURE__ */ I(o, {
|
|
170
|
+
agentId: J,
|
|
171
|
+
required: [i],
|
|
166
172
|
mode: "all",
|
|
167
173
|
eyebrow: M("ai.pluginRequired", "AI plugin required"),
|
|
168
174
|
title: M("ai.installOrchestrator", "Install the AI orchestrator"),
|
|
169
175
|
description: M("ai.installOrchestratorDesc", "The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue."),
|
|
170
|
-
onInstalled:
|
|
171
|
-
children: /* @__PURE__ */
|
|
176
|
+
onInstalled: ue,
|
|
177
|
+
children: /* @__PURE__ */ L("div", {
|
|
172
178
|
className: "space-y-6",
|
|
173
179
|
children: [
|
|
174
|
-
Q && /* @__PURE__ */
|
|
180
|
+
Q && /* @__PURE__ */ I("div", {
|
|
175
181
|
role: "status",
|
|
176
182
|
className: "rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text",
|
|
177
183
|
children: Q
|
|
178
184
|
}),
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
-
agentId:
|
|
181
|
-
packages:
|
|
185
|
+
/* @__PURE__ */ I(te, {
|
|
186
|
+
agentId: J,
|
|
187
|
+
packages: a,
|
|
182
188
|
title: M("ai.harnessesTitle", "AI harnesses"),
|
|
183
189
|
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."),
|
|
184
|
-
onChange:
|
|
190
|
+
onChange: de
|
|
185
191
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
187
|
-
|
|
188
|
-
agent:
|
|
192
|
+
/* @__PURE__ */ L("div", { children: [
|
|
193
|
+
k === "overview" && /* @__PURE__ */ I(h, {
|
|
194
|
+
agent: X,
|
|
189
195
|
onNavigateToSession: Z
|
|
190
196
|
}),
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
agent:
|
|
197
|
+
k === "templates" && /* @__PURE__ */ I(ne, { agentId: J }),
|
|
198
|
+
k === "contexts" && /* @__PURE__ */ I(d, { agent: X }),
|
|
199
|
+
k === "prompts" && /* @__PURE__ */ I(re, { agent: X }),
|
|
200
|
+
k === "instructions" && /* @__PURE__ */ I(g, { agentId: J }),
|
|
201
|
+
k === "sessions" && /* @__PURE__ */ I(ae, {
|
|
202
|
+
agent: X,
|
|
197
203
|
onNavigateToSession: Z
|
|
198
204
|
}),
|
|
199
|
-
|
|
200
|
-
agent:
|
|
205
|
+
k === "queue" && /* @__PURE__ */ I(_, {
|
|
206
|
+
agent: X,
|
|
201
207
|
onNavigateToSession: Z
|
|
202
208
|
}),
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
k === "tasks" && /* @__PURE__ */ I(f, {}),
|
|
210
|
+
k === "stats" && /* @__PURE__ */ I(p, { agent: X }),
|
|
211
|
+
k === "playground" && /* @__PURE__ */ I(m, { agent: X }),
|
|
212
|
+
k === "config" && /* @__PURE__ */ L("div", {
|
|
207
213
|
className: "space-y-6",
|
|
208
|
-
children: [/* @__PURE__ */
|
|
214
|
+
children: [/* @__PURE__ */ I(v, { agent: X }), /* @__PURE__ */ I(ie, { agent: X })]
|
|
209
215
|
})
|
|
210
216
|
] })
|
|
211
217
|
]
|
|
212
218
|
})
|
|
213
219
|
})
|
|
214
220
|
})
|
|
215
|
-
}) : /* @__PURE__ */
|
|
216
|
-
icon: /* @__PURE__ */
|
|
221
|
+
}) : /* @__PURE__ */ I(r, {
|
|
222
|
+
icon: /* @__PURE__ */ I(A, { className: "size-8 text-text-secondary" }),
|
|
217
223
|
title: M("ai.noActiveAgent", "No active agent available"),
|
|
218
224
|
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."),
|
|
219
|
-
action: /* @__PURE__ */
|
|
225
|
+
action: /* @__PURE__ */ I("button", {
|
|
220
226
|
type: "button",
|
|
221
227
|
onClick: () => j("/agents"),
|
|
222
228
|
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",
|
|
@@ -224,9 +230,12 @@ function W() {
|
|
|
224
230
|
})
|
|
225
231
|
})
|
|
226
232
|
]
|
|
233
|
+
}) : /* @__PURE__ */ I(R, {
|
|
234
|
+
title: M("ai.denied.title", "Access Denied"),
|
|
235
|
+
description: M("ai.denied.description", "You do not have permission to view AI features.")
|
|
227
236
|
});
|
|
228
237
|
}
|
|
229
238
|
//#endregion
|
|
230
|
-
export {
|
|
239
|
+
export { H as AIPage };
|
|
231
240
|
|
|
232
241
|
//# 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 { useMatch, useParams } from 'react-router-dom';\nimport {\n LayoutDashboard,\n FileCode2,\n FileText,\n MessageSquare,\n MessagesSquare,\n ListTodo,\n ListChecks,\n BarChart3,\n Play,\n ScrollText,\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 { InstructionFilesManager } from '@/components/ai/InstructionFilesManager';\nimport { AISessionsTab } from '@/components/ai/AISessionsTab';\nimport { AITasksTab } from '@/components/ai/AITasksTab';\nimport { AIQueueTab } from '@/components/ai/AIQueueTab';\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';\nimport { AIModeToggle } from '@/components/ai/AIModeToggle';\n\ntype AIMode = 'plan' | 'ai';\n\ntype AITab =\n | 'overview'\n | 'templates'\n | 'contexts'\n | 'prompts'\n | 'instructions'\n | 'sessions'\n | 'queue'\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: 'instructions', icon: ScrollText },\n { id: 'sessions', icon: MessagesSquare },\n { id: 'queue', icon: ListChecks },\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 'instructions':\n return tr('ai.instructions', 'Instructions');\n case 'sessions':\n return tr('nav.sessions', 'Sessions');\n case 'queue':\n return tr('ai.queue', 'Queue');\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 console.error('[ai] failed to load initial workspace data', agentsError);\n }\n const activeAgent = useMemo(\n () => agents.find((agent) => agent.isActive && agent.tunnelUrl) ?? 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 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 // PluginGate fires this only on the unsatisfied → satisfied transition (once\n // the orchestrator is actually live on the agent). When that happens, route\n // straight into the AI chat (AgentManagerPage at `/ai`) instead of leaving\n // the user on a \"Refreshing…\" note that needed a manual reload (G5). No\n // window.location.reload() — the SPA navigation re-renders the workbench with\n // the freshly-detected plugin state.\n const handleInstalled = useCallback(() => {\n setLastInstallNote(tr('ai.pluginInstalled', 'Plugin installed. Opening AI chat…'));\n console.info('[ai] orchestrator install completed — navigating to AI chat');\n handleNavigate('/ai');\n }, [tr, handleNavigate]);\n const handleHarnessChange = useCallback(() => {\n setLastInstallNote(tr('ai.harnessUpdated', 'Harness updated.'));\n\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. Stays inside\n /ai-tools so the active sub-tab survives the toggle. */}\n {/* AIModeToggle is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"vibecontrols-ai-mode-toggle\">\n <AIModeToggle aiHref={`/ai-tools/ai/${activeTab}`} activeMode={activeMode} />\n </div>\n\n {/* AI sub-tab navigation — only rendered in AI mode. */}\n {activeMode === 'ai' && (\n <div\n className=\"border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6\"\n data-tour=\"vibecontrols-ai-tabs-nav\"\n >\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 type=\"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=\"size-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=\"size-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 type=\"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 agent={agentRef} tunnelUrl={activeAgent?.tunnelUrl ?? null} />\n </RequireAgentReachable>\n ) : (\n <RequireAgentReachable agentId={agentId}>\n {/* PluginGate is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"vibecontrols-ai-plugin-gate\">\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 === 'instructions' && <InstructionFilesManager agentId={agentId} />}\n {activeTab === 'sessions' && (\n <AISessionsTab agent={agentRef} onNavigateToSession={navigateToSession} />\n )}\n {activeTab === 'queue' && (\n <AIQueueTab 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 </div>\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 ListChecks,\n BarChart3,\n Play,\n ScrollText,\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 { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\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 { InstructionFilesManager } from '@/components/ai/InstructionFilesManager';\nimport { AISessionsTab } from '@/components/ai/AISessionsTab';\nimport { AITasksTab } from '@/components/ai/AITasksTab';\nimport { AIQueueTab } from '@/components/ai/AIQueueTab';\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';\nimport { AIModeToggle } from '@/components/ai/AIModeToggle';\n\ntype AIMode = 'plan' | 'ai';\n\ntype AITab =\n | 'overview'\n | 'templates'\n | 'contexts'\n | 'prompts'\n | 'instructions'\n | 'sessions'\n | 'queue'\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: 'instructions', icon: ScrollText },\n { id: 'sessions', icon: MessagesSquare },\n { id: 'queue', icon: ListChecks },\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 // The AI surface is agent-centric — gate it on `agent:read`, consistent\n // with AgentsLandingPage (defense-in-depth; the gateway enforces it too).\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canReadAgents = hasPermission(VIBE_PERMISSIONS.AGENT_READ);\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 'instructions':\n return tr('ai.instructions', 'Instructions');\n case 'sessions':\n return tr('nav.sessions', 'Sessions');\n case 'queue':\n return tr('ai.queue', 'Queue');\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 console.error('[ai] failed to load initial workspace data', agentsError);\n }\n const activeAgent = useMemo(\n () => agents.find((agent) => agent.isActive && agent.tunnelUrl) ?? 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 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 // PluginGate fires this only on the unsatisfied → satisfied transition (once\n // the orchestrator is actually live on the agent). When that happens, route\n // straight into the AI chat (AgentManagerPage at `/ai`) instead of leaving\n // the user on a \"Refreshing…\" note that needed a manual reload (G5). No\n // window.location.reload() — the SPA navigation re-renders the workbench with\n // the freshly-detected plugin state.\n const handleInstalled = useCallback(() => {\n setLastInstallNote(tr('ai.pluginInstalled', 'Plugin installed. Opening AI chat…'));\n console.info('[ai] orchestrator install completed — navigating to AI chat');\n handleNavigate('/ai');\n }, [tr, handleNavigate]);\n const handleHarnessChange = useCallback(() => {\n setLastInstallNote(tr('ai.harnessUpdated', 'Harness updated.'));\n\n console.info('[ai] harness picker changed');\n }, [tr]);\n\n if (permissionsLoading) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (!canReadAgents) {\n return (\n <AccessDenied\n title={tr('ai.denied.title', 'Access Denied')}\n description={tr('ai.denied.description', 'You do not have permission to view AI features.')}\n />\n );\n }\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. Stays inside\n /ai-tools so the active sub-tab survives the toggle. */}\n {/* AIModeToggle is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"vibecontrols-ai-mode-toggle\">\n <AIModeToggle aiHref={`/ai-tools/ai/${activeTab}`} activeMode={activeMode} />\n </div>\n\n {/* AI sub-tab navigation — only rendered in AI mode. */}\n {activeMode === 'ai' && (\n <div\n className=\"border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6\"\n data-tour=\"vibecontrols-ai-tabs-nav\"\n >\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 type=\"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=\"size-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=\"size-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 type=\"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 agent={agentRef} tunnelUrl={activeAgent?.tunnelUrl ?? null} />\n </RequireAgentReachable>\n ) : (\n <RequireAgentReachable agentId={agentId}>\n {/* PluginGate is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"vibecontrols-ai-plugin-gate\">\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 === 'instructions' && <InstructionFilesManager agentId={agentId} />}\n {activeTab === 'sessions' && (\n <AISessionsTab agent={agentRef} onNavigateToSession={navigateToSession} />\n )}\n {activeTab === 'queue' && (\n <AIQueueTab 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 </div>\n </RequireAgentReachable>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|