@burdenoff/microfe-vibecontrols 2026.522.5 → 2026.522.6
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/ai/AIModeToggle.js +43 -0
- package/dist/components/ai/AIModeToggle.js.map +1 -0
- package/dist/components/vibe/VibeAgentEmptyState.js.map +1 -1
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/ai/AIPage.js +91 -104
- package/dist/pages/ai/AIPage.js.map +1 -1
- package/dist/pages/ai/AgentManagerPage.js +99 -86
- package/dist/pages/ai/AgentManagerPage.js.map +1 -1
- package/package.json +1 -1
package/dist/pages/ai/AIPage.js
CHANGED
|
@@ -12,23 +12,24 @@ 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
|
|
16
|
-
import { AITasksTab as
|
|
17
|
-
import { AIStatsTab as
|
|
18
|
-
import { AIPlaygroundTab as
|
|
19
|
-
import { BridleConfigPanel as
|
|
20
|
-
import { AIOverviewTab as
|
|
21
|
-
import { AISessionsTab as
|
|
22
|
-
import { ClaudeCredentialsCard as
|
|
23
|
-
import { AIPlanPanel as
|
|
24
|
-
import {
|
|
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";
|
|
23
|
+
import { AIPlanPanel as S } from "../../components/ai/AIPlanPanel.js";
|
|
24
|
+
import { AIModeToggle as C } from "../../components/ai/AIModeToggle.js";
|
|
25
|
+
import { useCallback as w, useMemo as T, useState as ee } from "react";
|
|
25
26
|
import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
26
|
-
import { BarChart3 as
|
|
27
|
-
import { useMatch as
|
|
28
|
-
import { jsx as
|
|
29
|
-
import { MotionPage as
|
|
27
|
+
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";
|
|
28
|
+
import { useMatch as P, useParams as F } from "react-router-dom";
|
|
29
|
+
import { jsx as I, jsxs as L } from "react/jsx-runtime";
|
|
30
|
+
import { MotionPage as R } from "@burdenoff/fe-libs/shared/motion";
|
|
30
31
|
//#region src/pages/ai/AIPage.tsx
|
|
31
|
-
var
|
|
32
|
+
var z = [
|
|
32
33
|
{
|
|
33
34
|
id: "overview",
|
|
34
35
|
icon: k
|
|
@@ -43,11 +44,11 @@ var B = [
|
|
|
43
44
|
},
|
|
44
45
|
{
|
|
45
46
|
id: "prompts",
|
|
46
|
-
icon:
|
|
47
|
+
icon: j
|
|
47
48
|
},
|
|
48
49
|
{
|
|
49
50
|
id: "sessions",
|
|
50
|
-
icon:
|
|
51
|
+
icon: M
|
|
51
52
|
},
|
|
52
53
|
{
|
|
53
54
|
id: "tasks",
|
|
@@ -55,160 +56,146 @@ var B = [
|
|
|
55
56
|
},
|
|
56
57
|
{
|
|
57
58
|
id: "stats",
|
|
58
|
-
icon:
|
|
59
|
+
icon: E
|
|
59
60
|
},
|
|
60
61
|
{
|
|
61
62
|
id: "playground",
|
|
62
|
-
icon:
|
|
63
|
+
icon: N
|
|
63
64
|
},
|
|
64
65
|
{
|
|
65
66
|
id: "config",
|
|
66
67
|
icon: k
|
|
67
68
|
}
|
|
68
|
-
],
|
|
69
|
+
], B = new Set(z.map((e) => e.id));
|
|
69
70
|
function V() {
|
|
70
|
-
let { t:
|
|
71
|
-
let n =
|
|
71
|
+
let { t: E } = te(), { tab: D } = F(), O = P("vibecontrols/ai-tools/plan") ? "plan" : "ai", k = D && B.has(D) ? D : "overview", A = e(), j = w((e, t) => {
|
|
72
|
+
let n = E(e);
|
|
72
73
|
return n === e ? t : n;
|
|
73
|
-
}, [
|
|
74
|
+
}, [E]), N = w((e) => {
|
|
74
75
|
switch (e) {
|
|
75
|
-
case "overview": return
|
|
76
|
-
case "templates": return
|
|
77
|
-
case "contexts": return
|
|
78
|
-
case "prompts": return
|
|
79
|
-
case "sessions": return
|
|
80
|
-
case "tasks": return
|
|
81
|
-
case "stats": return
|
|
82
|
-
case "playground": return
|
|
83
|
-
case "config": return
|
|
76
|
+
case "overview": return j("ai.overview", "Overview");
|
|
77
|
+
case "templates": return j("ai.templates", "Templates");
|
|
78
|
+
case "contexts": return j("ai.contexts", "Contexts");
|
|
79
|
+
case "prompts": return j("ai.prompts", "Prompts");
|
|
80
|
+
case "sessions": return j("nav.sessions", "Sessions");
|
|
81
|
+
case "tasks": return j("ai.tasks", "Tasks");
|
|
82
|
+
case "stats": return j("ai.stats", "Stats");
|
|
83
|
+
case "playground": return j("ai.playground", "Playground");
|
|
84
|
+
case "config": return j("ai.config", "Config");
|
|
84
85
|
default: return e;
|
|
85
86
|
}
|
|
86
|
-
}, [
|
|
87
|
+
}, [j]), { agents: V, loading: H, error: U, refetch: W } = t();
|
|
87
88
|
U && console.error("[ai] failed to load initial workspace data", U);
|
|
88
|
-
let G =
|
|
89
|
+
let G = T(() => V.find((e) => e.isActive && e.tunnelUrl && e.agentApiKey) ?? null, [V]), K = G?.id ?? "", { active: q } = l(G?.id ?? null), J = T(() => ({
|
|
89
90
|
id: K,
|
|
90
91
|
profile: q
|
|
91
|
-
}), [K, q]), Y =
|
|
92
|
+
}), [K, q]), Y = w((e) => {
|
|
92
93
|
A(e === "overview" ? "/ai-tools/ai" : `/ai-tools/ai/${e}`);
|
|
93
|
-
}, [A]), X =
|
|
94
|
-
A(e === "plan" ? "/ai-tools/plan" : `/ai-tools/ai/${k}`);
|
|
95
|
-
}, [A, k]), Z = S((e) => {
|
|
94
|
+
}, [A]), X = w((e) => {
|
|
96
95
|
A(`/ai-tools/sessions/${e}`);
|
|
97
|
-
}, [A]),
|
|
98
|
-
$(
|
|
99
|
-
}, [
|
|
100
|
-
$(
|
|
101
|
-
}, [
|
|
102
|
-
return /* @__PURE__ */ R
|
|
96
|
+
}, [A]), Z = !!K, [Q, $] = ee(null), ne = w(() => {
|
|
97
|
+
$(j("ai.pluginInstalled", "Plugin installed. Refreshing…")), console.info("[ai] orchestrator install completed");
|
|
98
|
+
}, [j]), re = w(() => {
|
|
99
|
+
$(j("ai.harnessUpdated", "Harness updated.")), console.info("[ai] harness picker changed");
|
|
100
|
+
}, [j]);
|
|
101
|
+
return /* @__PURE__ */ L(R, {
|
|
103
102
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
104
103
|
children: [
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
title:
|
|
107
|
-
description:
|
|
104
|
+
/* @__PURE__ */ I(n, {
|
|
105
|
+
title: j("nav.ai", "AI"),
|
|
106
|
+
description: j("ai.description", "Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow")
|
|
108
107
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
})
|
|
108
|
+
/* @__PURE__ */ I(C, {
|
|
109
|
+
aiHref: `/ai-tools/ai/${k}`,
|
|
110
|
+
activeMode: O
|
|
124
111
|
}),
|
|
125
|
-
O === "ai" && /* @__PURE__ */
|
|
112
|
+
O === "ai" && /* @__PURE__ */ I("div", {
|
|
126
113
|
className: "border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6",
|
|
127
|
-
children: /* @__PURE__ */
|
|
114
|
+
children: /* @__PURE__ */ I("nav", {
|
|
128
115
|
className: "flex gap-1 overflow-x-auto pb-1",
|
|
129
116
|
role: "tablist",
|
|
130
|
-
children:
|
|
117
|
+
children: z.map((e) => {
|
|
131
118
|
let t = e.icon, n = k === e.id;
|
|
132
|
-
return /* @__PURE__ */
|
|
119
|
+
return /* @__PURE__ */ L("button", {
|
|
133
120
|
role: "tab",
|
|
134
121
|
"aria-selected": n,
|
|
135
122
|
onClick: () => Y(e.id),
|
|
136
123
|
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"}`,
|
|
137
|
-
children: [/* @__PURE__ */
|
|
124
|
+
children: [/* @__PURE__ */ I(t, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4" }), N(e.id)]
|
|
138
125
|
}, e.id);
|
|
139
126
|
})
|
|
140
127
|
})
|
|
141
128
|
}),
|
|
142
|
-
H && V.length === 0 ? /* @__PURE__ */
|
|
129
|
+
H && V.length === 0 ? /* @__PURE__ */ I(u, {
|
|
143
130
|
variant: "rows",
|
|
144
131
|
count: 6
|
|
145
|
-
}) : U ? /* @__PURE__ */
|
|
146
|
-
title:
|
|
147
|
-
message:
|
|
132
|
+
}) : U ? /* @__PURE__ */ I(r, {
|
|
133
|
+
title: j("ai.loadFailed.title", "Could not load the AI workspace"),
|
|
134
|
+
message: j("ai.loadFailed.message", "We couldn't load the AI workspace. Please try again in a moment."),
|
|
148
135
|
onRetry: W
|
|
149
|
-
}) :
|
|
136
|
+
}) : Z ? O === "plan" ? /* @__PURE__ */ I(c, {
|
|
150
137
|
agentId: K,
|
|
151
|
-
children: /* @__PURE__ */
|
|
138
|
+
children: /* @__PURE__ */ I(S, {
|
|
152
139
|
agent: J,
|
|
153
140
|
agentApiKey: G?.agentApiKey ?? null,
|
|
154
141
|
tunnelUrl: G?.tunnelUrl ?? null
|
|
155
142
|
})
|
|
156
|
-
}) : /* @__PURE__ */
|
|
143
|
+
}) : /* @__PURE__ */ I(c, {
|
|
157
144
|
agentId: K,
|
|
158
|
-
children: /* @__PURE__ */
|
|
145
|
+
children: /* @__PURE__ */ I(s, {
|
|
159
146
|
agentId: K,
|
|
160
147
|
required: [a],
|
|
161
148
|
mode: "all",
|
|
162
|
-
eyebrow:
|
|
163
|
-
title:
|
|
164
|
-
description:
|
|
165
|
-
onInstalled:
|
|
166
|
-
children: /* @__PURE__ */
|
|
149
|
+
eyebrow: j("ai.pluginRequired", "AI plugin required"),
|
|
150
|
+
title: j("ai.installOrchestrator", "Install the AI orchestrator"),
|
|
151
|
+
description: j("ai.installOrchestratorDesc", "The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue."),
|
|
152
|
+
onInstalled: ne,
|
|
153
|
+
children: /* @__PURE__ */ L("div", {
|
|
167
154
|
className: "space-y-6",
|
|
168
155
|
children: [
|
|
169
|
-
Q && /* @__PURE__ */
|
|
156
|
+
Q && /* @__PURE__ */ I("div", {
|
|
170
157
|
role: "status",
|
|
171
158
|
className: "rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text",
|
|
172
159
|
children: Q
|
|
173
160
|
}),
|
|
174
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ I(d, {
|
|
175
162
|
agentId: K,
|
|
176
163
|
packages: o,
|
|
177
|
-
title:
|
|
178
|
-
description:
|
|
179
|
-
onChange:
|
|
164
|
+
title: j("ai.harnessesTitle", "AI harnesses"),
|
|
165
|
+
description: j("ai.harnessesDesc", "Install and manage the coding-agent harnesses available on this agent. You can install multiple and switch between them per-session."),
|
|
166
|
+
onChange: re
|
|
180
167
|
}),
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
k === "overview" && /* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ L("div", { children: [
|
|
169
|
+
k === "overview" && /* @__PURE__ */ I(y, {
|
|
183
170
|
agent: J,
|
|
184
|
-
onNavigateToSession:
|
|
171
|
+
onNavigateToSession: X
|
|
185
172
|
}),
|
|
186
|
-
k === "templates" && /* @__PURE__ */
|
|
187
|
-
k === "contexts" && /* @__PURE__ */
|
|
188
|
-
k === "prompts" && /* @__PURE__ */
|
|
189
|
-
k === "sessions" && /* @__PURE__ */
|
|
173
|
+
k === "templates" && /* @__PURE__ */ I(f, { agentId: K }),
|
|
174
|
+
k === "contexts" && /* @__PURE__ */ I(p, { agent: J }),
|
|
175
|
+
k === "prompts" && /* @__PURE__ */ I(m, { agent: J }),
|
|
176
|
+
k === "sessions" && /* @__PURE__ */ I(b, {
|
|
190
177
|
agent: J,
|
|
191
|
-
onNavigateToSession:
|
|
178
|
+
onNavigateToSession: X
|
|
192
179
|
}),
|
|
193
|
-
k === "tasks" && /* @__PURE__ */
|
|
194
|
-
k === "stats" && /* @__PURE__ */
|
|
195
|
-
k === "playground" && /* @__PURE__ */
|
|
196
|
-
k === "config" && /* @__PURE__ */
|
|
180
|
+
k === "tasks" && /* @__PURE__ */ I(h, {}),
|
|
181
|
+
k === "stats" && /* @__PURE__ */ I(g, { agent: J }),
|
|
182
|
+
k === "playground" && /* @__PURE__ */ I(_, { agent: J }),
|
|
183
|
+
k === "config" && /* @__PURE__ */ L("div", {
|
|
197
184
|
className: "space-y-6",
|
|
198
|
-
children: [/* @__PURE__ */
|
|
185
|
+
children: [/* @__PURE__ */ I(x, { agent: J }), /* @__PURE__ */ I(v, { agent: J })]
|
|
199
186
|
})
|
|
200
187
|
] })
|
|
201
188
|
]
|
|
202
189
|
})
|
|
203
190
|
})
|
|
204
|
-
}) : /* @__PURE__ */
|
|
205
|
-
icon: /* @__PURE__ */
|
|
206
|
-
title:
|
|
207
|
-
description:
|
|
208
|
-
action: /* @__PURE__ */
|
|
191
|
+
}) : /* @__PURE__ */ I(i, {
|
|
192
|
+
icon: /* @__PURE__ */ I(M, { className: "w-8 h-8 text-text-secondary" }),
|
|
193
|
+
title: j("ai.noActiveAgent", "No active agent available"),
|
|
194
|
+
description: j("ai.noActiveAgentDescription", "AI features require an active agent with a configured tunnel URL and API key. Add an agent or start an existing one."),
|
|
195
|
+
action: /* @__PURE__ */ I("button", {
|
|
209
196
|
onClick: () => A("/agents"),
|
|
210
197
|
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",
|
|
211
|
-
children:
|
|
198
|
+
children: j("ai.goToAgents", "Go to Agents")
|
|
212
199
|
})
|
|
213
200
|
})
|
|
214
201
|
]
|
|
@@ -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 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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"}
|
|
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} 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';\nimport { AIModeToggle } from '@/components/ai/AIModeToggle';\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 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. Stays inside\n /ai-tools so the active sub-tab survives the toggle. */}\n <AIModeToggle aiHref={`/ai-tools/ai/${activeTab}`} activeMode={activeMode} />\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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"}
|