@burdenoff/microfe-vibecontrols 2026.611.2 → 2026.611.3

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.
@@ -0,0 +1,165 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { LoadingState as t } from "../shared/LoadingState.js";
3
+ import { ErrorState as n } from "../shared/ErrorState.js";
4
+ import { EmptyState as r } from "../shared/EmptyState.js";
5
+ import { Card as i } from "../shared/Card.js";
6
+ import "../shared/index.js";
7
+ import { cancelQueueItem as a, fetchQueueItems as o } from "../../services/aiApi.js";
8
+ import { notify as s } from "../../lib/toast.js";
9
+ import { useCallback as c, useEffect as l, useState as u } from "react";
10
+ import { AlertCircle as d, Ban as f, CheckCircle2 as p, Clock as m, ListChecks as h, Loader2 as g, RefreshCw as _, X as v } from "lucide-react";
11
+ import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
12
+ //#region src/components/ai/AIQueueTab.tsx
13
+ var S = {
14
+ pending: {
15
+ label: "Pending",
16
+ cls: "bg-status-warning-bg text-status-warning-text",
17
+ icon: m
18
+ },
19
+ processing: {
20
+ label: "Processing",
21
+ cls: "bg-action-primary-bg/20 text-action-primary-bg",
22
+ icon: g
23
+ },
24
+ completed: {
25
+ label: "Completed",
26
+ cls: "bg-status-success-bg text-status-success-text",
27
+ icon: p
28
+ },
29
+ failed: {
30
+ label: "Failed",
31
+ cls: "bg-status-error-bg text-status-error-text",
32
+ icon: d
33
+ },
34
+ cancelled: {
35
+ label: "Cancelled",
36
+ cls: "bg-bg-sunken text-text-secondary",
37
+ icon: f
38
+ }
39
+ };
40
+ function C({ agent: d, onNavigateToSession: f }) {
41
+ let p = e(), [m, g] = u([]), [C, w] = u(!0), [T, E] = u(null), [D, O] = u(null), k = c(async (e) => {
42
+ if (d.id) {
43
+ e && w(!0);
44
+ try {
45
+ g(await o(d)), E(null);
46
+ } catch (e) {
47
+ E(e.message);
48
+ } finally {
49
+ e && w(!1);
50
+ }
51
+ }
52
+ }, [d]);
53
+ l(() => {
54
+ k(!0);
55
+ let e = window.setInterval(() => void k(!1), 15e3);
56
+ return () => window.clearInterval(e);
57
+ }, [k]);
58
+ let A = async (e) => {
59
+ O(e);
60
+ try {
61
+ await a(d, e), s.success(p("aiQueue.cancelled", "Queued item cancelled")), await k(!1);
62
+ } catch (e) {
63
+ s.error(p("aiQueue.cancelFailed", "Could not cancel"), e.message);
64
+ } finally {
65
+ O(null);
66
+ }
67
+ };
68
+ if (!d.id) return /* @__PURE__ */ b(r, {
69
+ icon: /* @__PURE__ */ b(h, { className: "size-10" }),
70
+ title: p("aiQueue.noAgentTitle", "No active agent"),
71
+ description: p("aiQueue.noAgentDesc", "Start or attach an agent to queue AI prompts for background processing.")
72
+ });
73
+ if (C && m.length === 0) return /* @__PURE__ */ b(t, {});
74
+ if (T && m.length === 0) return /* @__PURE__ */ b(n, {
75
+ message: T,
76
+ onRetry: () => void k(!0)
77
+ });
78
+ let j = m.filter((e) => e.status === "pending" || e.status === "processing").length;
79
+ return /* @__PURE__ */ x("div", {
80
+ className: "space-y-4",
81
+ children: [/* @__PURE__ */ x("div", {
82
+ className: "flex items-center justify-between",
83
+ children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ x("h3", {
84
+ className: "flex items-center gap-2 text-base font-semibold text-text-primary",
85
+ children: [/* @__PURE__ */ b(h, { className: "size-5" }), p("aiQueue.title", "AI Queue")]
86
+ }), /* @__PURE__ */ b("p", {
87
+ className: "text-sm text-text-secondary",
88
+ children: p("aiQueue.subtitle", "Prompts queued for background processing on this agent — queue at night, review in the morning.")
89
+ })] }), /* @__PURE__ */ x("button", {
90
+ type: "button",
91
+ onClick: () => void k(!0),
92
+ className: "flex items-center gap-1.5 rounded-md border border-border-default px-3 py-1.5 text-sm text-text-primary hover:bg-bg-sunken",
93
+ children: [/* @__PURE__ */ b(_, { className: `size-4 ${C ? "animate-spin" : ""}` }), p("common.refresh", "Refresh")]
94
+ })]
95
+ }), m.length === 0 ? /* @__PURE__ */ b(r, {
96
+ icon: /* @__PURE__ */ b(h, { className: "size-10" }),
97
+ title: p("aiQueue.emptyTitle", "Queue is empty"),
98
+ description: p("aiQueue.emptyDesc", "Dispatch a prompt with \"Queue\" instead of \"Run\" to have the agent process it in the background.")
99
+ }) : /* @__PURE__ */ x(y, { children: [j > 0 && /* @__PURE__ */ x("p", {
100
+ className: "text-xs text-text-tertiary",
101
+ children: [
102
+ j,
103
+ " ",
104
+ p("aiQueue.inFlight", "in flight"),
105
+ " · ",
106
+ m.length,
107
+ " ",
108
+ p("aiQueue.total", "total")
109
+ ]
110
+ }), /* @__PURE__ */ b(i, { children: /* @__PURE__ */ b("ul", {
111
+ className: "divide-y divide-border-subtle",
112
+ children: m.map((e) => {
113
+ let t = S[e.status], n = t.icon;
114
+ return /* @__PURE__ */ x("li", {
115
+ className: "flex items-center gap-3 px-4 py-3",
116
+ children: [
117
+ /* @__PURE__ */ x("span", {
118
+ className: `inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${t.cls}`,
119
+ children: [/* @__PURE__ */ b(n, { className: `size-3.5 ${e.status === "processing" ? "animate-spin" : ""}` }), t.label]
120
+ }),
121
+ /* @__PURE__ */ x("div", {
122
+ className: "min-w-0 flex-1",
123
+ children: [
124
+ /* @__PURE__ */ b("button", {
125
+ type: "button",
126
+ onClick: () => f?.(e.sessionId),
127
+ className: "truncate text-left font-mono text-sm text-text-primary hover:underline",
128
+ title: p("aiQueue.openSession", "Open the session this prompt runs in"),
129
+ children: e.sessionId
130
+ }),
131
+ /* @__PURE__ */ x("div", {
132
+ className: "text-xs text-text-tertiary",
133
+ children: [
134
+ p("aiQueue.attempt", "attempt"),
135
+ " ",
136
+ e.attempts,
137
+ "/",
138
+ e.maxAttempts,
139
+ e.createdAt ? ` · ${new Date(e.createdAt).toLocaleString()}` : ""
140
+ ]
141
+ }),
142
+ e.lastError && /* @__PURE__ */ b("div", {
143
+ className: "mt-0.5 truncate text-xs text-status-error-text",
144
+ children: e.lastError
145
+ })
146
+ ]
147
+ }),
148
+ e.status === "pending" && /* @__PURE__ */ b("button", {
149
+ type: "button",
150
+ onClick: () => void A(e.id),
151
+ disabled: D === e.id,
152
+ className: "shrink-0 rounded p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text disabled:opacity-50",
153
+ "aria-label": p("common.cancel", "Cancel"),
154
+ children: /* @__PURE__ */ b(v, { className: "size-4" })
155
+ })
156
+ ]
157
+ }, e.id);
158
+ })
159
+ }) })] })]
160
+ });
161
+ }
162
+ //#endregion
163
+ export { C as AIQueueTab };
164
+
165
+ //# sourceMappingURL=AIQueueTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIQueueTab.js","names":[],"sources":["../../../src/components/ai/AIQueueTab.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport {\n ListChecks,\n RefreshCw,\n X,\n AlertCircle,\n Clock,\n Loader2,\n CheckCircle2,\n Ban,\n} from 'lucide-react';\nimport { Card, LoadingState, ErrorState, EmptyState } from '@/components/shared';\nimport type { AIQueueItem, AgentRef } from '@/services/aiApi';\nimport { fetchQueueItems, cancelQueueItem } from '@/services/aiApi';\nimport { notify } from '@/lib/toast';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIQueueTabProps {\n agent: AgentRef;\n onNavigateToSession?: (sessionId: string) => void;\n}\n\nconst STATUS_META: Record<\n AIQueueItem['status'],\n { label: string; cls: string; icon: typeof Clock }\n> = {\n pending: { label: 'Pending', cls: 'bg-status-warning-bg text-status-warning-text', icon: Clock },\n processing: {\n label: 'Processing',\n cls: 'bg-action-primary-bg/20 text-action-primary-bg',\n icon: Loader2,\n },\n completed: {\n label: 'Completed',\n cls: 'bg-status-success-bg text-status-success-text',\n icon: CheckCircle2,\n },\n failed: { label: 'Failed', cls: 'bg-status-error-bg text-status-error-text', icon: AlertCircle },\n cancelled: { label: 'Cancelled', cls: 'bg-bg-sunken text-text-secondary', icon: Ban },\n};\n\n/**\n * AIQueueTab — the \"queue overnight, review in the morning\" surface. Lists the queued AI prompts on\n * the active agent (the agent runs them in the background), with per-item status, attempts, and\n * errors, plus cancel for anything still pending. Auto-refreshes so a morning check shows the latest\n * results. Backed by the agent's `/ai/queue` API (fetchQueueItems / cancelQueueItem) — the same\n * governed agent surface the rest of AI mode uses.\n */\nexport function AIQueueTab({ agent, onNavigateToSession }: AIQueueTabProps): React.ReactElement {\n const tr = useTr();\n const [items, setItems] = useState<AIQueueItem[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [cancelling, setCancelling] = useState<string | null>(null);\n\n const load = useCallback(\n async (showSpinner: boolean): Promise<void> => {\n if (!agent.id) return;\n if (showSpinner) setLoading(true);\n try {\n const data = await fetchQueueItems(agent);\n setItems(data);\n setError(null);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n if (showSpinner) setLoading(false);\n }\n },\n [agent]\n );\n\n useEffect(() => {\n void load(true);\n // Poll while items are still in flight so a left-open tab updates itself overnight.\n const timer = window.setInterval(() => void load(false), 15000);\n return () => window.clearInterval(timer);\n }, [load]);\n\n const handleCancel = async (id: string): Promise<void> => {\n setCancelling(id);\n try {\n await cancelQueueItem(agent, id);\n notify.success(tr('aiQueue.cancelled', 'Queued item cancelled'));\n await load(false);\n } catch (err) {\n notify.error(tr('aiQueue.cancelFailed', 'Could not cancel'), (err as Error).message);\n } finally {\n setCancelling(null);\n }\n };\n\n if (!agent.id) {\n return (\n <EmptyState\n icon={<ListChecks className=\"size-10\" />}\n title={tr('aiQueue.noAgentTitle', 'No active agent')}\n description={tr(\n 'aiQueue.noAgentDesc',\n 'Start or attach an agent to queue AI prompts for background processing.'\n )}\n />\n );\n }\n\n if (loading && items.length === 0) return <LoadingState />;\n if (error && items.length === 0) {\n return <ErrorState message={error} onRetry={() => void load(true)} />;\n }\n\n const pendingCount = items.filter(\n (i) => i.status === 'pending' || i.status === 'processing'\n ).length;\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"flex items-center gap-2 text-base font-semibold text-text-primary\">\n <ListChecks className=\"size-5\" />\n {tr('aiQueue.title', 'AI Queue')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'aiQueue.subtitle',\n 'Prompts queued for background processing on this agent — queue at night, review in the morning.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void load(true)}\n className=\"flex items-center gap-1.5 rounded-md border border-border-default px-3 py-1.5 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n {tr('common.refresh', 'Refresh')}\n </button>\n </div>\n\n {items.length === 0 ? (\n <EmptyState\n icon={<ListChecks className=\"size-10\" />}\n title={tr('aiQueue.emptyTitle', 'Queue is empty')}\n description={tr(\n 'aiQueue.emptyDesc',\n 'Dispatch a prompt with \"Queue\" instead of \"Run\" to have the agent process it in the background.'\n )}\n />\n ) : (\n <>\n {pendingCount > 0 && (\n <p className=\"text-xs text-text-tertiary\">\n {pendingCount} {tr('aiQueue.inFlight', 'in flight')} · {items.length}{' '}\n {tr('aiQueue.total', 'total')}\n </p>\n )}\n <Card>\n <ul className=\"divide-y divide-border-subtle\">\n {items.map((item) => {\n const meta = STATUS_META[item.status];\n const StatusIcon = meta.icon;\n return (\n <li key={item.id} className=\"flex items-center gap-3 px-4 py-3\">\n <span\n className={`inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${meta.cls}`}\n >\n <StatusIcon\n className={`size-3.5 ${item.status === 'processing' ? 'animate-spin' : ''}`}\n />\n {meta.label}\n </span>\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={() => onNavigateToSession?.(item.sessionId)}\n className=\"truncate text-left font-mono text-sm text-text-primary hover:underline\"\n title={tr('aiQueue.openSession', 'Open the session this prompt runs in')}\n >\n {item.sessionId}\n </button>\n <div className=\"text-xs text-text-tertiary\">\n {tr('aiQueue.attempt', 'attempt')} {item.attempts}/{item.maxAttempts}\n {item.createdAt ? ` · ${new Date(item.createdAt).toLocaleString()}` : ''}\n </div>\n {item.lastError && (\n <div className=\"mt-0.5 truncate text-xs text-status-error-text\">\n {item.lastError}\n </div>\n )}\n </div>\n {item.status === 'pending' && (\n <button\n type=\"button\"\n onClick={() => void handleCancel(item.id)}\n disabled={cancelling === item.id}\n className=\"shrink-0 rounded p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text disabled:opacity-50\"\n aria-label={tr('common.cancel', 'Cancel')}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </li>\n );\n })}\n </ul>\n </Card>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAM,IAGF;CACF,SAAS;EAAE,OAAO;EAAW,KAAK;EAAiD,MAAM;EAAO;CAChG,YAAY;EACV,OAAO;EACP,KAAK;EACL,MAAM;EACP;CACD,WAAW;EACT,OAAO;EACP,KAAK;EACL,MAAM;EACP;CACD,QAAQ;EAAE,OAAO;EAAU,KAAK;EAA6C,MAAM;EAAa;CAChG,WAAW;EAAE,OAAO;EAAa,KAAK;EAAoC,MAAM;EAAK;CACtF;AASD,SAAgB,EAAW,EAAE,UAAO,0BAA4D;CAC9F,IAAM,IAAK,GAAO,EACZ,CAAC,GAAO,KAAY,EAAwB,EAAE,CAAC,EAC/C,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAE3D,IAAO,EACX,OAAO,MAAwC;AACxC,QAAM,IACX;GAAI,KAAa,EAAW,GAAK;AACjC,OAAI;AAGF,IADA,EADa,MAAM,EAAgB,EAAM,CAC3B,EACd,EAAS,KAAK;YACP,GAAK;AACZ,MAAU,EAAc,QAAQ;aACxB;AACR,IAAI,KAAa,EAAW,GAAM;;;IAGtC,CAAC,EAAM,CACR;AAED,SAAgB;AACT,IAAK,GAAK;EAEf,IAAM,IAAQ,OAAO,kBAAkB,KAAK,EAAK,GAAM,EAAE,KAAM;AAC/D,eAAa,OAAO,cAAc,EAAM;IACvC,CAAC,EAAK,CAAC;CAEV,IAAM,IAAe,OAAO,MAA8B;AACxD,IAAc,EAAG;AACjB,MAAI;AAGF,GAFA,MAAM,EAAgB,GAAO,EAAG,EAChC,EAAO,QAAQ,EAAG,qBAAqB,wBAAwB,CAAC,EAChE,MAAM,EAAK,GAAM;WACV,GAAK;AACZ,KAAO,MAAM,EAAG,wBAAwB,mBAAmB,EAAG,EAAc,QAAQ;YAC5E;AACR,KAAc,KAAK;;;AAIvB,KAAI,CAAC,EAAM,GACT,QACE,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;EACxC,OAAO,EAAG,wBAAwB,kBAAkB;EACpD,aAAa,EACX,uBACA,0EACD;EACD,CAAA;AAIN,KAAI,KAAW,EAAM,WAAW,EAAG,QAAO,kBAAC,GAAD,EAAgB,CAAA;AAC1D,KAAI,KAAS,EAAM,WAAW,EAC5B,QAAO,kBAAC,GAAD;EAAY,SAAS;EAAO,eAAe,KAAK,EAAK,GAAK;EAAI,CAAA;CAGvE,IAAM,IAAe,EAAM,QACxB,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,aAC/C,CAAC;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAAd,CACE,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA,EAChC,EAAG,iBAAiB,WAAW,CAC7B;OACL,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,oBACA,kGACD;IACC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,EAAK,GAAK;IAC9B,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAW,UAAU,IAAU,iBAAiB,MAAQ,CAAA,EAClE,EAAG,kBAAkB,UAAU,CACzB;MACL;MAEL,EAAM,WAAW,IAChB,kBAAC,GAAD;GACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;GACxC,OAAO,EAAG,sBAAsB,iBAAiB;GACjD,aAAa,EACX,qBACA,sGACD;GACD,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA,CACG,IAAe,KACd,kBAAC,KAAD;GAAG,WAAU;aAAb;IACG;IAAa;IAAE,EAAG,oBAAoB,YAAY;IAAC;IAAI,EAAM;IAAQ;IACrE,EAAG,iBAAiB,QAAQ;IAC3B;MAEN,kBAAC,GAAD,EAAA,UACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAM,KAAK,MAAS;IACnB,IAAM,IAAO,EAAY,EAAK,SACxB,IAAa,EAAK;AACxB,WACE,kBAAC,MAAD;KAAkB,WAAU;eAA5B;MACE,kBAAC,QAAD;OACE,WAAW,wFAAwF,EAAK;iBAD1G,CAGE,kBAAC,GAAD,EACE,WAAW,YAAY,EAAK,WAAW,eAAe,iBAAiB,MACvE,CAAA,EACD,EAAK,MACD;;MACP,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,IAAsB,EAAK,UAAU;SACpD,WAAU;SACV,OAAO,EAAG,uBAAuB,uCAAuC;mBAEvE,EAAK;SACC,CAAA;QACT,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACG,EAAG,mBAAmB,UAAU;UAAC;UAAE,EAAK;UAAS;UAAE,EAAK;UACxD,EAAK,YAAY,MAAM,IAAI,KAAK,EAAK,UAAU,CAAC,gBAAgB,KAAK;UAClE;;QACL,EAAK,aACJ,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAK;SACF,CAAA;QAEJ;;MACL,EAAK,WAAW,aACf,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,KAAK,EAAa,EAAK,GAAG;OACzC,UAAU,MAAe,EAAK;OAC9B,WAAU;OACV,cAAY,EAAG,iBAAiB,SAAS;iBAEzC,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;OACjB,CAAA;MAER;OAvCI,EAAK,GAuCT;KAEP;GACC,CAAA,EACA,CAAA,CACN,EAAA,CAAA,CAED"}
@@ -14,23 +14,24 @@ import { AITemplatesTab as f } from "../../components/ai/AITemplatesTab.js";
14
14
  import { AIContextsTab as p } from "../../components/ai/AIContextsTab.js";
15
15
  import { AIPromptsTab as m } from "../../components/ai/AIPromptsTab.js";
16
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 { InstructionFilesManager as b } from "../../components/ai/InstructionFilesManager.js";
22
- import { AISessionsTab as x } from "../../components/ai/AISessionsTab.js";
23
- import { ClaudeCredentialsCard as S } from "../../components/ai/ClaudeCredentialsCard.js";
24
- import { AIPlanPanel as C } from "../../components/ai/AIPlanPanel.js";
25
- import { AIModeToggle as w } from "../../components/ai/AIModeToggle.js";
26
- import { useCallback as T, useMemo as E, useState as ee } from "react";
27
- import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
28
- import { BarChart3 as D, FileCode2 as O, FileText as k, LayoutDashboard as A, ListTodo as j, MessageSquare as M, MessagesSquare as N, Play as P, ScrollText as F } from "lucide-react";
29
- import { useMatch as I, useParams as L } from "react-router-dom";
30
- import { jsx as R, jsxs as z } from "react/jsx-runtime";
31
- import { MotionPage as B } from "@burdenoff/fe-libs/shared/motion";
17
+ import { AIStatsTab as ee } 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 { InstructionFilesManager as y } from "../../components/ai/InstructionFilesManager.js";
22
+ import { AISessionsTab as te } from "../../components/ai/AISessionsTab.js";
23
+ import { AIQueueTab as b } from "../../components/ai/AIQueueTab.js";
24
+ import { ClaudeCredentialsCard as x } from "../../components/ai/ClaudeCredentialsCard.js";
25
+ import { AIPlanPanel as S } from "../../components/ai/AIPlanPanel.js";
26
+ import { AIModeToggle as C } from "../../components/ai/AIModeToggle.js";
27
+ import { useCallback as w, useMemo as T, useState as ne } from "react";
28
+ import { useI18n as E } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
29
+ import { BarChart3 as D, FileCode2 as O, FileText as k, LayoutDashboard as A, ListChecks as j, ListTodo as M, MessageSquare as N, MessagesSquare as P, Play as F, ScrollText as I } from "lucide-react";
30
+ import { useMatch as L, useParams as R } from "react-router-dom";
31
+ import { jsx as z, jsxs as B } from "react/jsx-runtime";
32
+ import { MotionPage as V } from "@burdenoff/fe-libs/shared/motion";
32
33
  //#region src/pages/ai/AIPage.tsx
33
- var V = [
34
+ var H = [
34
35
  {
35
36
  id: "overview",
36
37
  icon: A
@@ -45,38 +46,42 @@ var V = [
45
46
  },
46
47
  {
47
48
  id: "prompts",
48
- icon: M
49
+ icon: N
49
50
  },
50
51
  {
51
52
  id: "instructions",
52
- icon: F
53
+ icon: I
53
54
  },
54
55
  {
55
56
  id: "sessions",
56
- icon: N
57
+ icon: P
57
58
  },
58
59
  {
59
- id: "tasks",
60
+ id: "queue",
60
61
  icon: j
61
62
  },
63
+ {
64
+ id: "tasks",
65
+ icon: M
66
+ },
62
67
  {
63
68
  id: "stats",
64
69
  icon: D
65
70
  },
66
71
  {
67
72
  id: "playground",
68
- icon: P
73
+ icon: F
69
74
  },
70
75
  {
71
76
  id: "config",
72
77
  icon: A
73
78
  }
74
- ], H = new Set(V.map((e) => e.id));
75
- function U() {
76
- let { t: D } = te(), { tab: O } = L(), k = I("vibecontrols/ai-tools/plan") ? "plan" : "ai", A = O && H.has(O) ? O : "overview", j = e(), M = T((e, t) => {
79
+ ], U = new Set(H.map((e) => e.id));
80
+ function W() {
81
+ let { t: D } = E(), { tab: O } = R(), k = L("vibecontrols/ai-tools/plan") ? "plan" : "ai", A = O && U.has(O) ? O : "overview", j = e(), M = w((e, t) => {
77
82
  let n = D(e);
78
83
  return n === e ? t : n;
79
- }, [D]), P = T((e) => {
84
+ }, [D]), N = w((e) => {
80
85
  switch (e) {
81
86
  case "overview": return M("ai.overview", "Overview");
82
87
  case "templates": return M("ai.templates", "Templates");
@@ -84,129 +89,134 @@ function U() {
84
89
  case "prompts": return M("ai.prompts", "Prompts");
85
90
  case "instructions": return M("ai.instructions", "Instructions");
86
91
  case "sessions": return M("nav.sessions", "Sessions");
92
+ case "queue": return M("ai.queue", "Queue");
87
93
  case "tasks": return M("ai.tasks", "Tasks");
88
94
  case "stats": return M("ai.stats", "Stats");
89
95
  case "playground": return M("ai.playground", "Playground");
90
96
  case "config": return M("ai.config", "Config");
91
97
  default: return e;
92
98
  }
93
- }, [M]), { agents: F, loading: U, error: W, refetch: G } = t();
99
+ }, [M]), { agents: F, loading: I, error: W, refetch: G } = t();
94
100
  W && console.error("[ai] failed to load initial workspace data", W);
95
- let K = E(() => F.find((e) => e.isActive && e.tunnelUrl) ?? null, [F]), q = K?.id ?? "", { active: J } = l(K?.id ?? null), Y = E(() => ({
101
+ let K = T(() => F.find((e) => e.isActive && e.tunnelUrl) ?? null, [F]), q = K?.id ?? "", { active: J } = l(K?.id ?? null), Y = T(() => ({
96
102
  id: q,
97
103
  profile: J
98
- }), [q, J]), X = T((e) => {
104
+ }), [q, J]), X = w((e) => {
99
105
  j(e === "overview" ? "/ai-tools/ai" : `/ai-tools/ai/${e}`);
100
- }, [j]), Z = T((e) => {
106
+ }, [j]), Z = w((e) => {
101
107
  j(`/ai-tools/sessions/${e}`);
102
- }, [j]), ne = !!q, [Q, $] = ee(null), re = T(() => {
108
+ }, [j]), re = !!q, [Q, $] = ne(null), ie = w(() => {
103
109
  $(M("ai.pluginInstalled", "Plugin installed. Opening AI chat…")), console.info("[ai] orchestrator install completed — navigating to AI chat"), j("/ai");
104
- }, [M, j]), ie = T(() => {
110
+ }, [M, j]), ae = w(() => {
105
111
  $(M("ai.harnessUpdated", "Harness updated.")), console.info("[ai] harness picker changed");
106
112
  }, [M]);
107
- return /* @__PURE__ */ z(B, {
113
+ return /* @__PURE__ */ B(V, {
108
114
  className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
109
115
  children: [
110
- /* @__PURE__ */ R(n, {
116
+ /* @__PURE__ */ z(n, {
111
117
  title: M("nav.ai", "AI"),
112
118
  description: M("ai.description", "Manage AI sessions, prompts, templates, and contexts for your vibecoding workflow")
113
119
  }),
114
- /* @__PURE__ */ R("div", {
120
+ /* @__PURE__ */ z("div", {
115
121
  "data-tour": "vibecontrols-ai-mode-toggle",
116
- children: /* @__PURE__ */ R(w, {
122
+ children: /* @__PURE__ */ z(C, {
117
123
  aiHref: `/ai-tools/ai/${A}`,
118
124
  activeMode: k
119
125
  })
120
126
  }),
121
- k === "ai" && /* @__PURE__ */ R("div", {
127
+ k === "ai" && /* @__PURE__ */ z("div", {
122
128
  className: "border-b border-border-default -mx-4 sm:-mx-6 px-4 sm:px-6",
123
129
  "data-tour": "vibecontrols-ai-tabs-nav",
124
- children: /* @__PURE__ */ R("nav", {
130
+ children: /* @__PURE__ */ z("nav", {
125
131
  className: "flex gap-1 overflow-x-auto pb-1",
126
132
  role: "tablist",
127
- children: V.map((e) => {
133
+ children: H.map((e) => {
128
134
  let t = e.icon, n = A === e.id;
129
- return /* @__PURE__ */ z("button", {
135
+ return /* @__PURE__ */ B("button", {
130
136
  type: "button",
131
137
  role: "tab",
132
138
  "aria-selected": n,
133
139
  onClick: () => X(e.id),
134
140
  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"}`,
135
- children: [/* @__PURE__ */ R(t, { className: "size-3.5 sm:h-4 sm:w-4" }), P(e.id)]
141
+ children: [/* @__PURE__ */ z(t, { className: "size-3.5 sm:h-4 sm:w-4" }), N(e.id)]
136
142
  }, e.id);
137
143
  })
138
144
  })
139
145
  }),
140
- U && F.length === 0 ? /* @__PURE__ */ R(u, {
146
+ I && F.length === 0 ? /* @__PURE__ */ z(u, {
141
147
  variant: "rows",
142
148
  count: 6
143
- }) : W ? /* @__PURE__ */ R(r, {
149
+ }) : W ? /* @__PURE__ */ z(r, {
144
150
  title: M("ai.loadFailed.title", "Could not load the AI workspace"),
145
151
  message: M("ai.loadFailed.message", "We couldn't load the AI workspace. Please try again in a moment."),
146
152
  onRetry: G
147
- }) : ne ? k === "plan" ? /* @__PURE__ */ R(c, {
153
+ }) : re ? k === "plan" ? /* @__PURE__ */ z(c, {
148
154
  agentId: q,
149
- children: /* @__PURE__ */ R(C, {
155
+ children: /* @__PURE__ */ z(S, {
150
156
  agent: Y,
151
157
  tunnelUrl: K?.tunnelUrl ?? null
152
158
  })
153
- }) : /* @__PURE__ */ R(c, {
159
+ }) : /* @__PURE__ */ z(c, {
154
160
  agentId: q,
155
- children: /* @__PURE__ */ R("div", {
161
+ children: /* @__PURE__ */ z("div", {
156
162
  "data-tour": "vibecontrols-ai-plugin-gate",
157
- children: /* @__PURE__ */ R(s, {
163
+ children: /* @__PURE__ */ z(s, {
158
164
  agentId: q,
159
165
  required: [a],
160
166
  mode: "all",
161
167
  eyebrow: M("ai.pluginRequired", "AI plugin required"),
162
168
  title: M("ai.installOrchestrator", "Install the AI orchestrator"),
163
169
  description: M("ai.installOrchestratorDesc", "The AI page routes prompts and sessions through the AI orchestrator plugin on your agent. Install it to continue."),
164
- onInstalled: re,
165
- children: /* @__PURE__ */ z("div", {
170
+ onInstalled: ie,
171
+ children: /* @__PURE__ */ B("div", {
166
172
  className: "space-y-6",
167
173
  children: [
168
- Q && /* @__PURE__ */ R("div", {
174
+ Q && /* @__PURE__ */ z("div", {
169
175
  role: "status",
170
176
  className: "rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text",
171
177
  children: Q
172
178
  }),
173
- /* @__PURE__ */ R(d, {
179
+ /* @__PURE__ */ z(d, {
174
180
  agentId: q,
175
181
  packages: o,
176
182
  title: M("ai.harnessesTitle", "AI harnesses"),
177
183
  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."),
178
- onChange: ie
184
+ onChange: ae
179
185
  }),
180
- /* @__PURE__ */ z("div", { children: [
181
- A === "overview" && /* @__PURE__ */ R(y, {
186
+ /* @__PURE__ */ B("div", { children: [
187
+ A === "overview" && /* @__PURE__ */ z(v, {
188
+ agent: Y,
189
+ onNavigateToSession: Z
190
+ }),
191
+ A === "templates" && /* @__PURE__ */ z(f, { agentId: q }),
192
+ A === "contexts" && /* @__PURE__ */ z(p, { agent: Y }),
193
+ A === "prompts" && /* @__PURE__ */ z(m, { agent: Y }),
194
+ A === "instructions" && /* @__PURE__ */ z(y, { agentId: q }),
195
+ A === "sessions" && /* @__PURE__ */ z(te, {
182
196
  agent: Y,
183
197
  onNavigateToSession: Z
184
198
  }),
185
- A === "templates" && /* @__PURE__ */ R(f, { agentId: q }),
186
- A === "contexts" && /* @__PURE__ */ R(p, { agent: Y }),
187
- A === "prompts" && /* @__PURE__ */ R(m, { agent: Y }),
188
- A === "instructions" && /* @__PURE__ */ R(b, { agentId: q }),
189
- A === "sessions" && /* @__PURE__ */ R(x, {
199
+ A === "queue" && /* @__PURE__ */ z(b, {
190
200
  agent: Y,
191
201
  onNavigateToSession: Z
192
202
  }),
193
- A === "tasks" && /* @__PURE__ */ R(h, {}),
194
- A === "stats" && /* @__PURE__ */ R(g, { agent: Y }),
195
- A === "playground" && /* @__PURE__ */ R(_, { agent: Y }),
196
- A === "config" && /* @__PURE__ */ z("div", {
203
+ A === "tasks" && /* @__PURE__ */ z(h, {}),
204
+ A === "stats" && /* @__PURE__ */ z(ee, { agent: Y }),
205
+ A === "playground" && /* @__PURE__ */ z(g, { agent: Y }),
206
+ A === "config" && /* @__PURE__ */ B("div", {
197
207
  className: "space-y-6",
198
- children: [/* @__PURE__ */ R(S, { agent: Y }), /* @__PURE__ */ R(v, { agent: Y })]
208
+ children: [/* @__PURE__ */ z(x, { agent: Y }), /* @__PURE__ */ z(_, { agent: Y })]
199
209
  })
200
210
  ] })
201
211
  ]
202
212
  })
203
213
  })
204
214
  })
205
- }) : /* @__PURE__ */ R(i, {
206
- icon: /* @__PURE__ */ R(N, { className: "size-8 text-text-secondary" }),
215
+ }) : /* @__PURE__ */ z(i, {
216
+ icon: /* @__PURE__ */ z(P, { className: "size-8 text-text-secondary" }),
207
217
  title: M("ai.noActiveAgent", "No active agent available"),
208
218
  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."),
209
- action: /* @__PURE__ */ R("button", {
219
+ action: /* @__PURE__ */ z("button", {
210
220
  type: "button",
211
221
  onClick: () => j("/agents"),
212
222
  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",
@@ -217,6 +227,6 @@ function U() {
217
227
  });
218
228
  }
219
229
  //#endregion
220
- export { U as AIPage };
230
+ export { W as AIPage };
221
231
 
222
232
  //# 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 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 { 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 | '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: '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 '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 === '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 { 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,IAAM,IAAsD;CAC1D;EAAE,IAAI;EAAY,MAAM;EAAiB;CACzC;EAAE,IAAI;EAAa,MAAM;EAAW;CACpC;EAAE,IAAI;EAAY,MAAM;EAAU;CAClC;EAAE,IAAI;EAAW,MAAM;EAAe;CACtC;EAAE,IAAI;EAAgB,MAAM;EAAY;CACxC;EAAE,IAAI;EAAY,MAAM;EAAgB;CACxC;EAAE,IAAI;EAAS,MAAM;EAAY;CACjC;EAAE,IAAI;EAAS,MAAM;EAAU;CAC/B;EAAE,IAAI;EAAS,MAAM;EAAW;CAChC;EAAE,IAAI;EAAc,MAAM;EAAM;CAChC;EAAE,IAAI;EAAU,MAAM;EAAiB;CACxC,EAEK,IAAY,IAAI,IAAY,EAAK,KAAK,MAAM,EAAE,GAAG,CAAC;AAcxD,SAAgB,IAAS;CACvB,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,KAAK,MAAa,GAA6B,EAEjD,IADiB,EAAS,6BAA6B,GACjB,SAAS,MAC/C,IAAmB,KAAY,EAAU,IAAI,EAAS,GAAI,IAAqB,YAC/E,IAAiB,GAAiB,EAElC,IAAK,GACR,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAE,CACJ,EAEK,IAAW,GACd,MAAyB;AACxB,UAAQ,GAAR;GACE,KAAK,WACH,QAAO,EAAG,eAAe,WAAW;GACtC,KAAK,YACH,QAAO,EAAG,gBAAgB,YAAY;GACxC,KAAK,WACH,QAAO,EAAG,eAAe,WAAW;GACtC,KAAK,UACH,QAAO,EAAG,cAAc,UAAU;GACpC,KAAK,eACH,QAAO,EAAG,mBAAmB,eAAe;GAC9C,KAAK,WACH,QAAO,EAAG,gBAAgB,WAAW;GACvC,KAAK,QACH,QAAO,EAAG,YAAY,QAAQ;GAChC,KAAK,QACH,QAAO,EAAG,YAAY,QAAQ;GAChC,KAAK,QACH,QAAO,EAAG,YAAY,QAAQ;GAChC,KAAK,aACH,QAAO,EAAG,iBAAiB,aAAa;GAC1C,KAAK,SACH,QAAO,EAAG,aAAa,SAAS;GAClC,QACE,QAAO;;IAGb,CAAC,EAAG,CACL,EAEK,EAAE,WAAQ,SAAS,GAAe,OAAO,GAAa,eAAY,GAAsB;AAO9F,CAAI,KACF,QAAQ,MAAM,8CAA8C,EAAY;CAE1E,IAAM,IAAc,QACZ,EAAO,MAAM,MAAU,EAAM,YAAY,EAAM,UAAU,IAAI,MACnE,CAAC,EAAO,CACT,EACK,IAAU,GAAa,MAAM,IAI7B,EAAE,QAAQ,MAAkB,EAAsB,GAAa,MAAM,KAAK,EAC1E,IAAW,SACR;EAAE,IAAI;EAAS,SAAS;EAAe,GAC9C,CAAC,GAAS,EAAc,CACzB,EAEK,IAAY,GACf,MAAe;AACd,IAAe,MAAQ,aAAa,iBAAiB,gBAAgB,IAAM;IAE7E,CAAC,EAAe,CACjB,EAEK,IAAoB,GACvB,MAAsB;AACrB,IAAe,sBAAsB,IAAY;IAEnD,CAAC,EAAe,CACjB,EAEK,KAAW,CAAC,CAAC,GAOb,CAAC,GAAiB,KAAsB,GAAwB,KAAK,EAOrE,KAAkB,QAAkB;AAGxC,EAFA,EAAmB,EAAG,sBAAsB,qCAAqC,CAAC,EAClF,QAAQ,KAAK,8DAA8D,EAC3E,EAAe,MAAM;IACpB,CAAC,GAAI,EAAe,CAAC,EAClB,KAAsB,QAAkB;AAG5C,EAFA,EAAmB,EAAG,qBAAqB,mBAAmB,CAAC,EAE/D,QAAQ,KAAK,8BAA8B;IAC1C,CAAC,EAAG,CAAC;AAER,QACE,kBAAC,GAAD;EAAY,WAAU;YAAtB;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,UAAU,KAAK;IACzB,aAAa,EACX,kBACA,oFACD;IACD,CAAA;GAQF,kBAAC,OAAD;IAAK,aAAU;cACb,kBAAC,GAAD;KAAc,QAAQ,gBAAgB;KAAyB;KAAc,CAAA;IACzE,CAAA;GAGL,MAAe,QACd,kBAAC,OAAD;IACE,WAAU;IACV,aAAU;cAEV,kBAAC,OAAD;KAAK,WAAU;KAAkC,MAAK;eACnD,EAAK,KAAK,MAAQ;MACjB,IAAM,IAAO,EAAI,MACX,IAAW,MAAc,EAAI;AACnC,aACE,kBAAC,UAAD;OACE,MAAK;OAEL,MAAK;OACL,iBAAe;OACf,eAAe,EAAU,EAAI,GAAG;OAChC,WAAW,+JACT,IACI,oDACA;iBATR,CAYE,kBAAC,GAAD,EAAM,WAAU,0BAA2B,CAAA,EAC1C,EAAS,EAAI,GAAG,CACV;SAZF,EAAI,GAYF;OAEX;KACE,CAAA;IACF,CAAA;GAIP,KAAiB,EAAO,WAAW,IAClC,kBAAC,GAAD;IAAc,SAAQ;IAAO,OAAO;IAAK,CAAA,GACvC,IACF,kBAAC,GAAD;IACE,OAAO,EAAG,uBAAuB,kCAAkC;IACnE,SAAS,EACP,yBACA,mEACD;IACD,SAAS;IACT,CAAA,GACC,KAkBD,MAAe,SACjB,kBAAC,GAAD;IAAgC;cAC9B,kBAAC,GAAD;KAAa,OAAO;KAAU,WAAW,GAAa,aAAa;KAAQ,CAAA;IACrD,CAAA,GAExB,kBAAC,GAAD;IAAgC;cAG9B,kBAAC,OAAD;KAAK,aAAU;eACb,kBAAC,GAAD;MACW;MACT,UAAU,CAAC,EAAgB;MAC3B,MAAK;MACL,SAAS,EAAG,qBAAqB,qBAAqB;MACtD,OAAO,EAAG,0BAA0B,8BAA8B;MAClE,aAAa,EACX,8BACA,oHACD;MACD,aAAa;gBAEb,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KACC,kBAAC,OAAD;SACE,MAAK;SACL,WAAU;mBAET;SACG,CAAA;QAER,kBAAC,GAAD;SACW;SACT,UAAU;SACV,OAAO,EAAG,qBAAqB,eAAe;SAC9C,aAAa,EACX,oBACA,uIACD;SACD,UAAU;SACV,CAAA;QACF,kBAAC,OAAD,EAAA,UAAA;SACG,MAAc,cACb,kBAAC,GAAD;UAAe,OAAO;UAAU,qBAAqB;UAAqB,CAAA;SAE3E,MAAc,eAAe,kBAAC,GAAD,EAAyB,YAAW,CAAA;SACjE,MAAc,cAAc,kBAAC,GAAD,EAAe,OAAO,GAAY,CAAA;SAC9D,MAAc,aAAa,kBAAC,GAAD,EAAc,OAAO,GAAY,CAAA;SAC5D,MAAc,kBAAkB,kBAAC,GAAD,EAAkC,YAAW,CAAA;SAC7E,MAAc,cACb,kBAAC,IAAD;UAAe,OAAO;UAAU,qBAAqB;UAAqB,CAAA;SAE3E,MAAc,WACb,kBAAC,GAAD;UAAY,OAAO;UAAU,qBAAqB;UAAqB,CAAA;SAExE,MAAc,WAAW,kBAAC,GAAD,EAAc,CAAA;SACvC,MAAc,WAAW,kBAAC,IAAD,EAAY,OAAO,GAAY,CAAA;SACxD,MAAc,gBAAgB,kBAAC,GAAD,EAAiB,OAAO,GAAY,CAAA;SAClE,MAAc,YACb,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAuB,OAAO,GAAY,CAAA,EAC1C,kBAAC,GAAD,EAAmB,OAAO,GAAY,CAAA,CAClC;;SAEJ,EAAA,CAAA;QACF;;MACK,CAAA;KACT,CAAA;IACgB,CAAA,GApFxB,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAgB,WAAU,8BAA+B,CAAA;IAC/D,OAAO,EAAG,oBAAoB,4BAA4B;IAC1D,aAAa,EACX,+BACA,uHACD;IACD,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAe,UAAU;KACxC,WAAU;eAET,EAAG,iBAAiB,eAAe;KAC7B,CAAA;IAEX,CAAA;GAsEO"}
@@ -89,7 +89,7 @@ function m(e) {
89
89
  timestamp: e.timestamp ?? e.createdAt
90
90
  };
91
91
  }
92
- function h(e) {
92
+ function ee(e) {
93
93
  let t = e.stats ?? {}, n = e.inputTokens ?? t.totalInputTokens ?? 0, r = e.outputTokens ?? t.totalOutputTokens ?? 0, i = e.promptCount ?? t.totalLogs ?? 0, a = t.totalDurationMs ?? 0;
94
94
  return {
95
95
  session: e.session ?? {
@@ -113,13 +113,13 @@ function h(e) {
113
113
  averageLatency: e.averageLatency ?? (i > 0 ? a / i : void 0)
114
114
  };
115
115
  }
116
- function g(e) {
116
+ function h(e) {
117
117
  return {
118
118
  ...e,
119
119
  prompt: e.prompt ?? e.content
120
120
  };
121
121
  }
122
- function _(e) {
122
+ function g(e) {
123
123
  let t = e.inputTokens ?? e.totalInputTokens ?? 0, n = e.outputTokens ?? e.totalOutputTokens ?? 0;
124
124
  return {
125
125
  totalSessions: e.totalSessions ?? 0,
@@ -134,7 +134,7 @@ function _(e) {
134
134
  queuedPrompts: e.queuedPrompts ?? 0
135
135
  };
136
136
  }
137
- function v(e) {
137
+ function _(e) {
138
138
  let t = e.usage ?? {};
139
139
  return {
140
140
  usage: t,
@@ -149,7 +149,7 @@ function v(e) {
149
149
  byStatus: e.byStatus ?? []
150
150
  };
151
151
  }
152
- function y(e) {
152
+ function v(e) {
153
153
  let t = (e) => Object.entries(e ?? {}).map(([e, t]) => ({
154
154
  name: e,
155
155
  type: t.type,
@@ -165,34 +165,34 @@ function y(e) {
165
165
  body: t(e.body)
166
166
  };
167
167
  }
168
- async function b(e) {
168
+ async function y(e) {
169
169
  return (await c(e.id, o(e.profile, "/ai/tools"))).tools;
170
170
  }
171
- var x = "\n id\n name\n description\n content\n category\n variables\n isShared\n usageCount\n tags\n metadata\n createdAt\n updatedAt\n", S = `query ListAIPrompts($filter: AIPromptFilterInput, $pagination: PaginationInput) {
171
+ var b = "\n id\n name\n description\n content\n category\n variables\n isShared\n usageCount\n tags\n metadata\n createdAt\n updatedAt\n", x = `query ListAIPrompts($filter: AIPromptFilterInput, $pagination: PaginationInput) {
172
172
  aiPrompts(filter: $filter, pagination: $pagination) {
173
173
  items {
174
- ${x}
174
+ ${b}
175
175
  }
176
176
  total
177
177
  hasMore
178
178
  }
179
- }`, C = `mutation CreateAIPrompt($input: CreateAIPromptInput!) {
179
+ }`, S = `mutation CreateAIPrompt($input: CreateAIPromptInput!) {
180
180
  createAIPrompt(input: $input) {
181
- ${x}
181
+ ${b}
182
182
  }
183
- }`, w = `mutation UpdateAIPrompt($id: ID!, $input: UpdateAIPromptInput!) {
183
+ }`, C = `mutation UpdateAIPrompt($id: ID!, $input: UpdateAIPromptInput!) {
184
184
  updateAIPrompt(id: $id, input: $input) {
185
- ${x}
185
+ ${b}
186
186
  }
187
- }`, T = "mutation DeleteAIPrompt($id: ID!) {\n deleteAIPrompt(id: $id)\n}", E = "mutation RenderAIPrompt($id: ID!, $variables: JSON) {\n renderAIPrompt(id: $id, variables: $variables) {\n promptId\n rendered\n }\n}", D = `mutation RecordAIPromptUsage($id: ID!) {
187
+ }`, w = "mutation DeleteAIPrompt($id: ID!) {\n deleteAIPrompt(id: $id)\n}", T = "mutation RenderAIPrompt($id: ID!, $variables: JSON) {\n renderAIPrompt(id: $id, variables: $variables) {\n promptId\n rendered\n }\n}", E = `mutation RecordAIPromptUsage($id: ID!) {
188
188
  recordAIPromptUsage(id: $id) {
189
- ${x}
189
+ ${b}
190
190
  }
191
191
  }`;
192
- function O(e) {
192
+ function D(e) {
193
193
  return typeof e == "string" && e.trim() ? e.trim().toUpperCase() : void 0;
194
194
  }
195
- function k(e, t) {
195
+ function O(e, t) {
196
196
  if (typeof e == "number" && Number.isFinite(e)) return e;
197
197
  if (typeof e == "string" && e.trim()) {
198
198
  let t = Number(e);
@@ -200,48 +200,48 @@ function k(e, t) {
200
200
  }
201
201
  return t;
202
202
  }
203
- function A(e) {
203
+ function k(e) {
204
204
  let t = {}, n = typeof e?.search == "string" ? e.search.trim() : "";
205
205
  n && (t.search = n);
206
- let r = O(e?.category);
206
+ let r = D(e?.category);
207
207
  r && (t.category = r);
208
- let i = O(e?.status);
208
+ let i = D(e?.status);
209
209
  return i && (t.status = i), typeof e?.isShared == "boolean" && (t.isShared = e.isShared), Array.isArray(e?.tags) && (t.tags = e.tags.map(String).filter(Boolean)), typeof e?.createdBy == "string" && e.createdBy.trim() && (t.createdBy = e.createdBy.trim()), {
210
210
  filter: Object.keys(t).length > 0 ? t : null,
211
211
  pagination: {
212
- limit: Math.min(Math.max(k(e?.limit, 50), 1), 500),
213
- offset: Math.max(k(e?.offset, 0), 0)
212
+ limit: Math.min(Math.max(O(e?.limit, 50), 1), 500),
213
+ offset: Math.max(O(e?.offset, 0), 0)
214
214
  }
215
215
  };
216
216
  }
217
- function j(e) {
217
+ function A(e) {
218
218
  let t = {};
219
219
  e.name !== void 0 && (t.name = e.name), e.content !== void 0 && (t.content = e.content), e.description !== void 0 && (t.description = e.description), e.variables !== void 0 && (t.variables = e.variables), e.tags !== void 0 && (t.tags = e.tags), e.isShared !== void 0 && (t.isShared = e.isShared), e.metadata !== void 0 && (t.metadata = e.metadata);
220
- let n = O(e.category);
220
+ let n = D(e.category);
221
221
  n && (t.category = n);
222
- let r = O(e.status);
222
+ let r = D(e.status);
223
223
  return r && (t.status = r), t;
224
224
  }
225
+ async function j(e, t) {
226
+ return (await s(x, k(t))).aiPrompts.items.map(p);
227
+ }
225
228
  async function M(e, t) {
226
- return (await s(S, A(t))).aiPrompts.items.map(p);
229
+ return p((await s(S, { input: A(t) })).createAIPrompt);
227
230
  }
228
231
  async function N(e, t) {
229
- return p((await s(C, { input: j(t) })).createAIPrompt);
230
- }
231
- async function P(e, t) {
232
- return p((await s(D, { id: t })).recordAIPromptUsage);
232
+ return p((await s(E, { id: t })).recordAIPromptUsage);
233
233
  }
234
- async function F(e, t, n) {
235
- return p((await s(w, {
234
+ async function P(e, t, n) {
235
+ return p((await s(C, {
236
236
  id: t,
237
- input: j(n)
237
+ input: A(n)
238
238
  })).updateAIPrompt);
239
239
  }
240
- async function ee(e, t) {
241
- await s(T, { id: t });
240
+ async function F(e, t) {
241
+ await s(w, { id: t });
242
242
  }
243
243
  async function I(e, t, n) {
244
- return { rendered: (await s(E, {
244
+ return { rendered: (await s(T, {
245
245
  id: t,
246
246
  variables: n
247
247
  })).renderAIPrompt.rendered };
@@ -252,19 +252,19 @@ async function L(e, t) {
252
252
  async function R(e, t) {
253
253
  return l(e.id, o(e.profile, "/ai/contexts"), "POST", t);
254
254
  }
255
- async function z(e, t, n) {
255
+ async function te(e, t, n) {
256
256
  return l(e.id, o(e.profile, `/ai/contexts/${t}`), "PUT", n);
257
257
  }
258
- async function B(e, t) {
258
+ async function z(e, t) {
259
259
  await l(e.id, o(e.profile, `/ai/contexts/${t}`), "DELETE");
260
260
  }
261
- async function V(e, t) {
261
+ async function B(e, t) {
262
262
  return (await c(e.id, o(e.profile, "/ai/sessions"), t)).items;
263
263
  }
264
- async function H(e, t) {
264
+ async function V(e, t) {
265
265
  return c(e.id, o(e.profile, "/ai/sessions"), t);
266
266
  }
267
- async function U(e, t) {
267
+ async function H(e, t) {
268
268
  let n = {
269
269
  name: t.name,
270
270
  agentType: t.agentType,
@@ -277,10 +277,10 @@ async function U(e, t) {
277
277
  };
278
278
  return l(e.id, o(e.profile, "/ai/sessions"), "POST", n);
279
279
  }
280
- async function W(e, t) {
280
+ async function U(e, t) {
281
281
  return l(e.id, o(e.profile, "/ai/context/fetch-file"), "POST", t);
282
282
  }
283
- async function G(e, t, n, r, i) {
283
+ async function W(e, t, n, r, i) {
284
284
  let a = await l(e.id, o(e.profile, `/ai/sessions/${t}/send`), "POST", {
285
285
  prompt: n,
286
286
  contexts: r,
@@ -299,59 +299,65 @@ async function G(e, t, n, r, i) {
299
299
  } : void 0
300
300
  };
301
301
  }
302
- async function K(e, t, n) {
302
+ async function G(e, t, n) {
303
303
  return (await c(e.id, o(e.profile, `/ai/sessions/${t}/logs`), n)).items.map(m);
304
304
  }
305
- async function q(e, t) {
306
- return h(await c(e.id, o(e.profile, `/ai/sessions/${t}/stats`)));
305
+ async function K(e, t) {
306
+ return ee(await c(e.id, o(e.profile, `/ai/sessions/${t}/stats`)));
307
307
  }
308
- async function J(e, t, n) {
308
+ async function q(e, t, n) {
309
309
  return l(e.id, o(e.profile, `/ai/sessions/${t}/config`), "PUT", n);
310
310
  }
311
- async function Y(e, t) {
311
+ async function J(e, t) {
312
312
  await l(e.id, o(e.profile, `/ai/sessions/${t}`), "DELETE");
313
313
  }
314
- async function X(e, t) {
314
+ async function Y(e, t) {
315
315
  return l(e.id, o(e.profile, "/ai/dispatch/send"), "POST", {
316
316
  ...t,
317
317
  content: t.content ?? t.prompt
318
318
  });
319
319
  }
320
+ async function X(e, t) {
321
+ return (await c(e.id, o(e.profile, "/ai/dispatch/history"), t)).items.map(h);
322
+ }
320
323
  async function Z(e, t) {
321
- return (await c(e.id, o(e.profile, "/ai/dispatch/history"), t)).items.map(g);
324
+ return (await c(e.id, o(e.profile, "/ai/queue"), t)).items;
322
325
  }
323
- async function Q(e) {
324
- return _(await c(e.id, o(e.profile, "/ai/stats/overview")));
326
+ async function Q(e, t) {
327
+ await l(e.id, o(e.profile, `/ai/queue/${t}/cancel`), "POST");
328
+ }
329
+ async function ne(e) {
330
+ return g(await c(e.id, o(e.profile, "/ai/stats/overview")));
325
331
  }
326
- async function $(e) {
327
- return v(await c(e.id, o(e.profile, "/ai/stats/usage")));
332
+ async function re(e) {
333
+ return _(await c(e.id, o(e.profile, "/ai/stats/usage")));
328
334
  }
329
- async function te(e) {
335
+ async function ie(e) {
330
336
  return (await c(e.id, o(e.profile, "/ai/sessions/providers"))).providers;
331
337
  }
332
- async function ne(e) {
333
- return (await c(e.id, o(e.profile, "/ai/descriptor"))).endpoints.map(y);
338
+ async function ae(e) {
339
+ return (await c(e.id, o(e.profile, "/ai/descriptor"))).endpoints.map(v);
334
340
  }
335
- async function re(e, t, n) {
341
+ async function oe(e, t, n) {
336
342
  return l(e.id, o(e.profile, `/ai/sessions/${t}`), "PATCH", { name: n });
337
343
  }
338
- async function ie(e, t) {
344
+ async function $(e, t) {
339
345
  await l(e.id, o(e.profile, "/ai/sessions/bulk-delete"), "POST", { ids: t });
340
346
  }
341
- async function ae(e, t, n) {
347
+ async function se(e, t, n) {
342
348
  return (await c(e.id, o(e.profile, "/ai/sessions/search"), {
343
349
  query: t,
344
350
  ...n
345
351
  })).results;
346
352
  }
347
- async function oe(e, t) {
353
+ async function ce(e, t) {
348
354
  return (await c(e.id, o(e.profile, t ? `/ai/models/${encodeURIComponent(t)}` : "/ai/models"))).models;
349
355
  }
350
- async function se(e, t, n) {
356
+ async function le(e, t, n) {
351
357
  let r = await c(e.id, o(e.profile, `/ai/sessions/${t}/export`), { format: n }), i = n === "json" ? JSON.stringify(r, null, 2) : String(r.content ?? "");
352
358
  return new Blob([i], { type: n === "json" ? "application/json" : "text/markdown" });
353
359
  }
354
360
  //#endregion
355
- export { c as agentAiGet, l as agentAiMutate, o as agentApiPath, ie as bulkDeleteSessions, R as createContext, U as createSession, N as createTemplate, f as deleteAgentConfigValue, B as deleteContext, ee as deleteTemplate, se as exportSession, ne as fetchApiDescriptor, L as fetchContexts, Z as fetchDispatchHistory, W as fetchFileIntoContext, oe as fetchModels, te as fetchProviders, K as fetchSessionLogs, q as fetchSessionStats, V as fetchSessions, H as fetchSessionsPaginated, Q as fetchStatsOverview, M as fetchTemplates, b as fetchTools, $ as fetchUsageStats, u as getAgentConfigValue, P as recordTemplateUsage, re as renameSession, I as renderTemplate, ae as searchSessions, X as sendDispatch, G as sendPromptToSession, d as setAgentConfigValue, Y as terminateSession, z as updateContext, J as updateSessionConfig, F as updateTemplate };
361
+ export { c as agentAiGet, l as agentAiMutate, o as agentApiPath, $ as bulkDeleteSessions, Q as cancelQueueItem, R as createContext, H as createSession, M as createTemplate, f as deleteAgentConfigValue, z as deleteContext, F as deleteTemplate, le as exportSession, ae as fetchApiDescriptor, L as fetchContexts, X as fetchDispatchHistory, U as fetchFileIntoContext, ce as fetchModels, ie as fetchProviders, Z as fetchQueueItems, G as fetchSessionLogs, K as fetchSessionStats, B as fetchSessions, V as fetchSessionsPaginated, ne as fetchStatsOverview, j as fetchTemplates, y as fetchTools, re as fetchUsageStats, u as getAgentConfigValue, N as recordTemplateUsage, oe as renameSession, I as renderTemplate, se as searchSessions, Y as sendDispatch, W as sendPromptToSession, d as setAgentConfigValue, J as terminateSession, te as updateContext, q as updateSessionConfig, P as updateTemplate };
356
362
 
357
363
  //# sourceMappingURL=aiApi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"aiApi.js","names":[],"sources":["../../src/services/aiApi.ts"],"sourcesContent":["/**\n * AI API service layer\n *\n * Wraps workspace-owned AI prompt APIs and vibecontrols-agent AI REST API\n * endpoints exposed through wspace-vibecontrols-svc.\n *\n * Backend route prefix: /api/ai (mounted by vibe-plugin-ai v4.0.0)\n */\n\nimport {\n getStoredAuthToken,\n getStoredWorkspaceId,\n getStoredWorkspaceToken,\n} from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n AgentManagerMessage,\n FileAttachment,\n McpServer,\n PermissionMode,\n ProviderMode,\n} from '@/types/agentManager';\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\n// Reads from ActiveContextStorage (URL → storage fallback chain). Previously\n// parsed only `?workspace=` from window.location.search, which is empty when\n// the user navigates between MFE routes that don't carry the param. Result:\n// gateway received no `x-workspace-id`, backend `agentAiGet` resolver threw\n// `WorkspaceRequiredError` and the chat pane surfaced \"Unexpected error\".\nfunction getWorkspaceId(): string {\n return getStoredWorkspaceId() ?? '';\n}\n\nfunction getGatewayEndpoint(): string {\n if (typeof window === 'undefined') return '/workspaces/graphql';\n return '/workspaces/graphql';\n}\n\n/**\n * Sanitize error messages — strip HTML, Cloudflare error pages, and long technical details.\n * Returns a clean, user-friendly error string.\n */\nfunction sanitizeError(raw: string): string {\n // Detect Cloudflare tunnel errors (HTML error pages)\n if (raw.includes('Cloudflare Tunnel error') || raw.includes('cloudflare.com/5xx')) {\n return 'Agent tunnel is unreachable. The Cloudflare tunnel may have disconnected. Check if the agent is running and the tunnel is active.';\n }\n // Detect HTML responses\n if (raw.includes('<!DOCTYPE') || raw.includes('<html')) {\n return 'Unexpected HTML response. The agent may be unreachable or the tunnel may have changed.';\n }\n // Detect common network errors\n if (raw.includes('ECONNREFUSED') || raw.includes('ETIMEDOUT') || raw.includes('fetch failed')) {\n return 'Cannot connect to the agent. Check if it is running and accessible.';\n }\n if (\n raw.includes('Could not resolve authentication method') ||\n raw.includes('ANTHROPIC_API_KEY') ||\n raw.includes('ANTHROPIC_AUTH_TOKEN')\n ) {\n return 'Claude SDK auth is not configured on the agent. Open the AI Config tab and save either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.';\n }\n // Detect proxy errors / missing plugin endpoints. 404 on a plugin route\n // almost always means the plugin isn't installed on this agent yet — give\n // the user the actionable next step instead of a vague \"unavailable\".\n if (raw.includes('NOT_FOUND') || raw.includes(' 404')) {\n return 'This feature is not installed on the agent. Install the matching vibe plugin from the agent CLI (e.g. `vibe plugin install <name>`), then refresh.';\n }\n if (raw.includes('502') || raw.includes('503')) {\n return 'Agent service unavailable. The agent may be restarting — try again in a few seconds.';\n }\n // Truncate very long errors\n if (raw.length > 200) {\n return raw.slice(0, 200) + '...';\n }\n return raw;\n}\n\n/**\n * Identifier for an agent + its profile name. Every aiApi function takes\n * one of these so the canonical path `/api/profiles/<profile>/<rest>` can\n * be constructed at every callsite. Callers should pass\n * `{ id: agent.id, profile: agent.metadata?.profile ?? 'default' }`.\n */\nexport interface AgentRef {\n id: string;\n profile: string;\n}\n\n/**\n * Build the canonical agent-API path for a given profile + path segment.\n * The svc-side proxy validates that paths are exactly of the form\n * `/api/profiles/<name>/<rest>` (or one of the bare carve-outs), so every\n * callsite builds the path explicitly via this helper instead of the\n * proxy doing any rewriting.\n *\n * Example: `agentApiPath('default', '/ai/sessions')` →\n * `/api/profiles/default/ai/sessions`.\n */\nexport function agentApiPath(profile: string, segment: string): string {\n const seg = segment.startsWith('/') ? segment : `/${segment}`;\n return `/api/profiles/${encodeURIComponent(profile)}${seg}`;\n}\n\nasync function gatewayFetch<T = unknown>(\n query: string,\n variables: Record<string, unknown>\n): Promise<T> {\n const res = await fetch(getGatewayEndpoint(), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(getStoredAuthToken() ? { Authorization: `Bearer ${getStoredAuthToken()}` } : {}),\n ...(getStoredWorkspaceToken()\n ? { 'X-Workspace-Authorization': `Bearer ${getStoredWorkspaceToken()}` }\n : {}),\n ...(getWorkspaceId() ? { 'x-workspace-id': getWorkspaceId() } : {}),\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeError(`Gateway error ${res.status}: ${text || res.statusText}`));\n }\n\n const json = (await res.json()) as { data?: T; errors?: Array<{ message?: string }> };\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL request failed';\n throw new Error(sanitizeError(msg));\n }\n if (!json.data) throw new Error('GraphQL request returned no data');\n return json.data;\n}\n\nexport async function agentAiGet<T = unknown>(\n agentId: string,\n path: string,\n query?: Record<string, unknown>\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiGet: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `query AgentAiGet($agentId: ID!, $path: String!, $query: JSON) {\n agentAiGet(agentId: $agentId, path: $path, query: $query) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, query: query ?? null }\n );\n\n if (!data.agentAiGet.ok) {\n throw new Error(\n sanitizeError(data.agentAiGet.error || `Agent API error ${data.agentAiGet.status}`)\n );\n }\n return data.agentAiGet.data;\n}\n\nexport async function agentAiMutate<T = unknown>(\n agentId: string,\n path: string,\n method: string,\n body?: unknown\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiMutate: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `mutation AgentAiMutate($agentId: ID!, $path: String!, $method: String!, $body: JSON) {\n agentAiMutate(agentId: $agentId, path: $path, method: $method, body: $body) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, method, body: body ?? null }\n );\n\n if (!data.agentAiMutate.ok) {\n throw new Error(\n sanitizeError(data.agentAiMutate.error || `Agent API error ${data.agentAiMutate.status}`)\n );\n }\n return data.agentAiMutate.data;\n}\n\nexport async function getAgentConfigValue(agent: AgentRef, key: string): Promise<string | null> {\n try {\n const data = await agentAiGet<{ key?: string; value?: string }>(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`)\n );\n return typeof data.value === 'string' ? data.value : null;\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('configuration key not found')\n ) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function setAgentConfigValue(\n agent: AgentRef,\n key: string,\n value: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'PUT',\n { value }\n );\n}\n\nexport async function deleteAgentConfigValue(agent: AgentRef, key: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'DELETE'\n );\n}\n\nfunction qs(params?: Record<string, unknown>): string {\n if (!params) return '';\n const entries = Object.entries(params).filter(\n ([, v]) => v !== undefined && v !== null && v !== ''\n );\n if (entries.length === 0) return '';\n const search = new URLSearchParams();\n for (const [k, v] of entries) search.set(k, String(v));\n return `?${search.toString()}`;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport interface AITemplate {\n id: string;\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n variables: Record<string, unknown>;\n tags: string[];\n isShared?: boolean;\n usageCount?: number;\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIContext {\n id: string;\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AISession {\n id: string;\n name: string;\n agentType: string;\n providerPlugin?: string;\n status: 'active' | 'idle' | 'processing' | 'error' | 'terminated';\n stats?: Record<string, unknown>;\n config?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n terminatedAt?: string | null;\n}\n\nexport interface AISessionLog {\n id: string;\n sessionId: string;\n type: 'input' | 'output' | 'thinking' | 'event' | 'error' | 'metadata';\n content: string;\n message: string;\n tokenCount?: number;\n model?: string;\n durationMs?: number;\n agentMetadata?: Record<string, unknown>;\n timestamp: string;\n createdAt: string;\n}\n\nexport interface AISessionStats {\n session: { id: string; name: string; agentType: string; status: string; createdAt: string };\n stats: {\n totalLogs: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDurationMs: number;\n logsByType: Record<string, number>;\n };\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n promptCount: number;\n averageLatency?: number;\n}\n\nexport interface AIDispatch {\n id: string;\n templateId?: string | null;\n content: string;\n prompt: string;\n resolvedVariables?: Record<string, unknown>;\n contextIds?: string[];\n sessionId?: string | null;\n status: string;\n result?: Record<string, unknown> | null;\n scheduledAt?: string | null;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIQueueItem {\n id: string;\n dispatchedPromptId: string;\n sessionId: string;\n priority: number;\n status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';\n attempts: number;\n maxAttempts: number;\n lastError?: string | null;\n createdAt?: string;\n}\n\nexport interface AIStatsOverview {\n totalSessions: number;\n activeSessions: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n totalRequests: number;\n sessionsByAgentType: Record<string, number>;\n queuedPrompts: number;\n}\n\nexport interface AIUsageStats {\n usage: Record<\n string,\n { inputTokens: number; outputTokens: number; requestCount: number; sessionCount: number }\n >;\n byProvider: Array<{\n provider: string;\n tokens: number;\n sessions: number;\n cost?: number;\n inputTokens: number;\n outputTokens: number;\n requestCount: number;\n }>;\n byStatus: Array<{ status: string; count: number }>;\n}\n\nexport interface AIProvider {\n pluginName: string;\n name?: string;\n displayName?: string;\n isDefault: boolean;\n currentMode?: ProviderMode;\n defaultMode?: ProviderMode;\n supportedModes?: ProviderMode[];\n unsupportedModes?: ProviderMode[];\n modes?: Record<ProviderMode, boolean>;\n prereqApiPrefix?: string;\n}\n\nexport interface AIApiParameter {\n name: string;\n type: string;\n required: boolean;\n description: string;\n}\n\nexport interface AIApiEndpoint {\n id: string;\n method: string;\n path: string;\n description: string;\n parameters?: AIApiParameter[];\n body?: AIApiParameter[];\n}\n\nexport interface AITool {\n name: string;\n displayName: string;\n description: string;\n installed: boolean;\n version?: string;\n configFiles?: string[];\n foundConfigs?: string[];\n installCommand?: string;\n}\n\n// ── Paginated response wrapper ───────────────────────────────────────\n\nexport interface PaginatedResponse<T> {\n items: T[];\n total: number;\n hasMore: boolean;\n}\n\ninterface RawAITemplate extends Omit<AITemplate, 'variables' | 'tags' | 'prompt'> {\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[] | null;\n}\n\ninterface RawAISessionLog extends Omit<AISessionLog, 'message' | 'timestamp'> {\n message?: string;\n timestamp?: string;\n}\n\ninterface RawAISessionStats {\n session?: AISessionStats['session'];\n stats?: Partial<AISessionStats['stats']>;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n promptCount?: number;\n averageLatency?: number;\n}\n\ninterface RawAIDispatch extends Omit<AIDispatch, 'prompt'> {\n prompt?: string;\n}\n\ninterface RawAIStatsOverview {\n totalSessions?: number;\n activeSessions?: number;\n totalInputTokens?: number;\n totalOutputTokens?: number;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n totalRequests?: number;\n sessionsByAgentType?: Record<string, number>;\n queuedPrompts?: number;\n}\n\ninterface RawAIUsageStats {\n usage?: AIUsageStats['usage'];\n byProvider?: AIUsageStats['byProvider'];\n byStatus?: AIUsageStats['byStatus'];\n}\n\ninterface RawAIApiEndpoint {\n method: string;\n path: string;\n description: string;\n parameters?: Record<string, { type: string; required: boolean; description: string }>;\n body?: Record<string, { type: string; required: boolean; description: string }>;\n}\n\nfunction normalizeTemplate(template: RawAITemplate): AITemplate {\n const variables = Array.isArray(template.variables)\n ? Object.fromEntries(template.variables.map((name) => [String(name), '']))\n : (template.variables ?? {});\n\n return {\n ...template,\n description: template.description ?? null,\n variables,\n tags: template.tags ?? [],\n };\n}\n\nfunction normalizeSessionLog(log: RawAISessionLog): AISessionLog {\n return {\n ...log,\n message: log.message ?? log.content,\n timestamp: log.timestamp ?? log.createdAt,\n };\n}\n\nfunction normalizeSessionStats(stats: RawAISessionStats): AISessionStats {\n const statBlock = stats.stats ?? {};\n const inputTokens = stats.inputTokens ?? statBlock.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? statBlock.totalOutputTokens ?? 0;\n const promptCount = stats.promptCount ?? statBlock.totalLogs ?? 0;\n const totalDurationMs = statBlock.totalDurationMs ?? 0;\n\n return {\n session: stats.session ?? {\n id: '',\n name: '',\n agentType: '',\n status: '',\n createdAt: '',\n },\n stats: {\n totalLogs: statBlock.totalLogs ?? 0,\n totalInputTokens: statBlock.totalInputTokens ?? inputTokens,\n totalOutputTokens: statBlock.totalOutputTokens ?? outputTokens,\n totalDurationMs,\n logsByType: statBlock.logsByType ?? {},\n },\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n promptCount,\n averageLatency:\n stats.averageLatency ?? (promptCount > 0 ? totalDurationMs / promptCount : undefined),\n };\n}\n\nfunction normalizeDispatch(dispatch: RawAIDispatch): AIDispatch {\n return {\n ...dispatch,\n prompt: dispatch.prompt ?? dispatch.content,\n };\n}\n\nfunction normalizeStatsOverview(stats: RawAIStatsOverview): AIStatsOverview {\n const inputTokens = stats.inputTokens ?? stats.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? stats.totalOutputTokens ?? 0;\n\n return {\n totalSessions: stats.totalSessions ?? 0,\n activeSessions: stats.activeSessions ?? 0,\n totalInputTokens: stats.totalInputTokens ?? inputTokens,\n totalOutputTokens: stats.totalOutputTokens ?? outputTokens,\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n totalRequests: stats.totalRequests ?? 0,\n sessionsByAgentType: stats.sessionsByAgentType ?? {},\n queuedPrompts: stats.queuedPrompts ?? 0,\n };\n}\n\nfunction normalizeUsageStats(usageStats: RawAIUsageStats): AIUsageStats {\n const usage = usageStats.usage ?? {};\n const byProvider =\n usageStats.byProvider ??\n Object.entries(usage).map(([provider, stats]) => ({\n provider,\n tokens: stats.inputTokens + stats.outputTokens,\n sessions: stats.sessionCount,\n inputTokens: stats.inputTokens,\n outputTokens: stats.outputTokens,\n requestCount: stats.requestCount,\n }));\n\n return {\n usage,\n byProvider,\n byStatus: usageStats.byStatus ?? [],\n };\n}\n\nfunction normalizeApiEndpoint(endpoint: RawAIApiEndpoint): AIApiEndpoint {\n const normalizeFields = (\n fields?: Record<string, { type: string; required: boolean; description: string }>\n ): AIApiParameter[] =>\n Object.entries(fields ?? {}).map(([name, config]) => ({\n name,\n type: config.type,\n required: config.required,\n description: config.description,\n }));\n\n return {\n id: `${endpoint.method.toUpperCase()} ${endpoint.path}`,\n method: endpoint.method,\n path: endpoint.path,\n description: endpoint.description,\n parameters: normalizeFields(endpoint.parameters),\n body: normalizeFields(endpoint.body),\n };\n}\n\n// ── Tools ─────────────────────────────────────────────────────────────\n\nexport async function fetchTools(agent: AgentRef): Promise<AITool[]> {\n const data = await agentAiGet<{ tools: AITool[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/tools')\n );\n return data.tools;\n}\n\n// ── Templates (workspace-owned AI prompts) ───────────────────────────\n\nconst AI_PROMPT_FIELDS = `\n id\n name\n description\n content\n category\n variables\n isShared\n usageCount\n tags\n metadata\n createdAt\n updatedAt\n`;\n\nconst LIST_AI_PROMPTS_QUERY = `query ListAIPrompts($filter: AIPromptFilterInput, $pagination: PaginationInput) {\n aiPrompts(filter: $filter, pagination: $pagination) {\n items {\n ${AI_PROMPT_FIELDS}\n }\n total\n hasMore\n }\n}`;\n\nconst CREATE_AI_PROMPT_MUTATION = `mutation CreateAIPrompt($input: CreateAIPromptInput!) {\n createAIPrompt(input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst UPDATE_AI_PROMPT_MUTATION = `mutation UpdateAIPrompt($id: ID!, $input: UpdateAIPromptInput!) {\n updateAIPrompt(id: $id, input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst DELETE_AI_PROMPT_MUTATION = `mutation DeleteAIPrompt($id: ID!) {\n deleteAIPrompt(id: $id)\n}`;\n\nconst RENDER_AI_PROMPT_MUTATION = `mutation RenderAIPrompt($id: ID!, $variables: JSON) {\n renderAIPrompt(id: $id, variables: $variables) {\n promptId\n rendered\n }\n}`;\n\nconst RECORD_AI_PROMPT_USAGE_MUTATION = `mutation RecordAIPromptUsage($id: ID!) {\n recordAIPromptUsage(id: $id) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\ntype AITemplateInput = {\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n status?: string | null;\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[];\n isShared?: boolean;\n metadata?: Record<string, unknown>;\n};\n\ntype AITemplateUpdateInput = Partial<AITemplateInput>;\n\nfunction enumInput(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim().toUpperCase() : undefined;\n}\n\nfunction numberInput(value: unknown, fallback: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n}\n\nfunction buildAIPromptListVariables(params?: Record<string, unknown>): {\n filter: Record<string, unknown> | null;\n pagination: { limit: number; offset: number };\n} {\n const filter: Record<string, unknown> = {};\n const search = typeof params?.search === 'string' ? params.search.trim() : '';\n if (search) filter.search = search;\n\n const category = enumInput(params?.category);\n if (category) filter.category = category;\n\n const status = enumInput(params?.status);\n if (status) filter.status = status;\n\n if (typeof params?.isShared === 'boolean') filter.isShared = params.isShared;\n if (Array.isArray(params?.tags)) filter.tags = params.tags.map(String).filter(Boolean);\n if (typeof params?.createdBy === 'string' && params.createdBy.trim()) {\n filter.createdBy = params.createdBy.trim();\n }\n\n return {\n filter: Object.keys(filter).length > 0 ? filter : null,\n pagination: {\n limit: Math.min(Math.max(numberInput(params?.limit, 50), 1), 500),\n offset: Math.max(numberInput(params?.offset, 0), 0),\n },\n };\n}\n\nfunction toAIPromptInput(input: AITemplateInput | AITemplateUpdateInput): Record<string, unknown> {\n const promptInput: Record<string, unknown> = {};\n\n if (input.name !== undefined) promptInput.name = input.name;\n if (input.content !== undefined) promptInput.content = input.content;\n if (input.description !== undefined) promptInput.description = input.description;\n if (input.variables !== undefined) promptInput.variables = input.variables;\n if (input.tags !== undefined) promptInput.tags = input.tags;\n if (input.isShared !== undefined) promptInput.isShared = input.isShared;\n if (input.metadata !== undefined) promptInput.metadata = input.metadata;\n\n const category = enumInput(input.category);\n if (category) promptInput.category = category;\n\n const status = enumInput(input.status);\n if (status) promptInput.status = status;\n\n return promptInput;\n}\n\nexport async function fetchTemplates(\n _agentId: string,\n params?: Record<string, unknown>\n): Promise<AITemplate[]> {\n const data = await gatewayFetch<{ aiPrompts: PaginatedResponse<RawAITemplate> }>(\n LIST_AI_PROMPTS_QUERY,\n buildAIPromptListVariables(params)\n );\n return data.aiPrompts.items.map(normalizeTemplate);\n}\n\nexport async function createTemplate(\n _agentId: string,\n input: AITemplateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ createAIPrompt: RawAITemplate }>(CREATE_AI_PROMPT_MUTATION, {\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.createAIPrompt);\n}\n\nexport async function recordTemplateUsage(_agentId: string, id: string): Promise<AITemplate> {\n const data = await gatewayFetch<{ recordAIPromptUsage: RawAITemplate }>(\n RECORD_AI_PROMPT_USAGE_MUTATION,\n { id }\n );\n return normalizeTemplate(data.recordAIPromptUsage);\n}\n\nexport async function updateTemplate(\n _agentId: string,\n id: string,\n input: AITemplateUpdateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ updateAIPrompt: RawAITemplate }>(UPDATE_AI_PROMPT_MUTATION, {\n id,\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.updateAIPrompt);\n}\n\nexport async function deleteTemplate(_agentId: string, id: string): Promise<void> {\n await gatewayFetch<{ deleteAIPrompt: boolean }>(DELETE_AI_PROMPT_MUTATION, { id });\n}\n\nexport async function renderTemplate(\n _agentId: string,\n id: string,\n variables: Record<string, unknown>\n): Promise<{ rendered: string }> {\n const data = await gatewayFetch<{ renderAIPrompt: { promptId: string; rendered: string } }>(\n RENDER_AI_PROMPT_MUTATION,\n { id, variables }\n );\n return { rendered: data.renderAIPrompt.rendered };\n}\n\n// ── Contexts (backend route: /ai/contexts under canonical profile prefix) ──\n\nexport async function fetchContexts(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIContext[]> {\n const data = await agentAiGet<PaginatedResponse<AIContext>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n params\n );\n return data.items;\n}\n\nexport async function createContext(\n agent: AgentRef,\n input: {\n name: string;\n type: string;\n content: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n }\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n 'POST',\n input\n );\n}\n\nexport async function updateContext(\n agent: AgentRef,\n id: string,\n input: Partial<{\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata: Record<string, unknown>;\n }>\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, `/ai/contexts/${id}`),\n 'PUT',\n input\n );\n}\n\nexport async function deleteContext(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/contexts/${id}`), 'DELETE');\n}\n\n// ── Sessions (backend route: /ai/sessions) ──────────────────────────\n\nexport async function fetchSessions(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AISession[]> {\n const data = await agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params\n );\n return data.items;\n}\n\nexport async function fetchSessionsPaginated(\n agent: AgentRef,\n params?: {\n limit?: number;\n offset?: number;\n search?: string;\n agentType?: string;\n status?: string;\n vibeId?: string;\n agentId?: string;\n contextLevel?: 'agent' | 'vibe';\n }\n): Promise<PaginatedResponse<AISession>> {\n return agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params as Record<string, unknown>\n );\n}\n\nexport async function createSession(\n agent: AgentRef,\n input: {\n name: string;\n agentType: string;\n config?: Record<string, unknown>;\n // Context-bucket identity — stamped into the session so the list endpoint\n // can filter by vibe / agent context and teammates sharing the vibe see it.\n vibeId?: string | null;\n agentId?: string | null;\n contextLevel?: 'agent' | 'vibe';\n workspaceId?: string | null;\n createdByUserId?: string | null;\n }\n): Promise<AISession> {\n const body: Record<string, unknown> = {\n name: input.name,\n agentType: input.agentType,\n ...(input.config ? { config: input.config } : {}),\n ...(input.vibeId ? { vibeId: input.vibeId } : {}),\n ...(input.agentId ? { agentId: input.agentId } : {}),\n ...(input.contextLevel ? { contextLevel: input.contextLevel } : {}),\n ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),\n ...(input.createdByUserId ? { createdByUserId: input.createdByUserId } : {}),\n };\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n 'POST',\n body\n );\n}\n\n/**\n * Materialise a Files-module file onto the agent's machine for use as context.\n * The agent fetches the signed `downloadUrl` server-side and writes it under\n * the session working dir, returning the absolute path (+ inline text for small\n * text files) which the caller attaches to the session contexts.\n */\nexport async function fetchFileIntoContext(\n agent: AgentRef,\n input: { downloadUrl: string; filename: string; sessionId?: string; workingDirectory?: string }\n): Promise<{ path: string; bytes: number; contentType?: string; inline?: string | null }> {\n return agentAiMutate<{\n path: string;\n bytes: number;\n contentType?: string;\n inline?: string | null;\n }>(agent.id, agentApiPath(agent.profile, '/ai/context/fetch-file'), 'POST', input);\n}\n\n/**\n * Push an instruction file (AGENTS.md / CLAUDE.md / GEMINI.md / custom) into the\n * agent's working folder so the LLM CLI picks it up. Called on session open for\n * each instruction file linked to the active agent/vibe context.\n */\nexport async function pushInstructionFileToAgent(\n agent: AgentRef,\n input: { filename: string; content: string; sessionId?: string; workingDirectory?: string }\n): Promise<{ path: string; bytes: number }> {\n return agentAiMutate<{ path: string; bytes: number }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/instructions'),\n 'POST',\n input\n );\n}\n\nexport async function sendPromptToSession(\n agent: AgentRef,\n sessionId: string,\n prompt: string,\n contexts?: unknown[],\n options?: {\n agentType?: string;\n model?: string;\n mode?: string;\n permissionMode?: PermissionMode;\n conversationHistory?: Array<{ role: string; content: string }>;\n }\n): Promise<{\n response: string;\n tokens?: { input: number; output: number };\n [key: string]: unknown;\n}> {\n const data = await agentAiMutate<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/send`),\n 'POST',\n {\n prompt,\n contexts,\n ...(options?.agentType ? { agentType: options.agentType } : {}),\n ...(options?.model ? { model: options.model } : {}),\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.permissionMode ? { permissionMode: options.permissionMode } : {}),\n ...(options?.conversationHistory?.length\n ? { conversationHistory: options.conversationHistory }\n : {}),\n }\n );\n const rawTokens = data.tokens as Record<string, unknown> | undefined;\n\n return {\n ...data,\n response: String(data.response ?? data.content ?? data.message ?? ''),\n tokens:\n rawTokens && (typeof rawTokens.input === 'number' || typeof rawTokens.output === 'number')\n ? {\n input: Number(rawTokens.input ?? 0),\n output: Number(rawTokens.output ?? 0),\n }\n : undefined,\n };\n}\n\nexport async function fetchSessionLogs(\n agent: AgentRef,\n sessionId: string,\n params?: Record<string, unknown>\n): Promise<AISessionLog[]> {\n const data = await agentAiGet<PaginatedResponse<RawAISessionLog>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/logs`),\n params\n );\n return data.items.map(normalizeSessionLog);\n}\n\nexport async function fetchSessionStats(\n agent: AgentRef,\n sessionId: string\n): Promise<AISessionStats> {\n return normalizeSessionStats(\n await agentAiGet<RawAISessionStats>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/stats`)\n )\n );\n}\n\nexport async function updateSessionConfig(\n agent: AgentRef,\n sessionId: string,\n config: Record<string, unknown>\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/config`),\n 'PUT',\n config\n );\n}\n\nexport async function terminateSession(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/sessions/${sessionId}`), 'DELETE');\n}\n\n// ── Prompt Composition & Dispatch (backend route: /ai/dispatch) ──\n\nexport async function composePrompt(\n agent: AgentRef,\n input: {\n templateId?: string;\n variables?: Record<string, unknown>;\n content?: string;\n contextIds?: string[];\n sessionId?: string;\n }\n): Promise<{ dispatch: AIDispatch; preview: string }> {\n return agentAiMutate<{ dispatch: AIDispatch; preview: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/compose'),\n 'POST',\n input\n );\n}\n\nexport async function sendDispatch(\n agent: AgentRef,\n input: {\n dispatchId?: string;\n prompt?: string;\n content?: string;\n sessionId?: string;\n contextIds?: string[];\n scheduledAt?: string;\n }\n): Promise<{ status: string; dispatchId: string; sessionId: string }> {\n return agentAiMutate<{ status: string; dispatchId: string; sessionId: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/send'),\n 'POST',\n {\n ...input,\n content: input.content ?? input.prompt,\n }\n );\n}\n\nexport async function fetchDispatchHistory(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIDispatch[]> {\n const data = await agentAiGet<PaginatedResponse<RawAIDispatch>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/history'),\n params\n );\n return data.items.map(normalizeDispatch);\n}\n\n// ── Queue (backend route: /ai/queue) ─────────────────────────────\n\nexport async function fetchQueueItems(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIQueueItem[]> {\n const data = await agentAiGet<PaginatedResponse<AIQueueItem>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue'),\n params\n );\n return data.items;\n}\n\nexport async function enqueueItem(\n agent: AgentRef,\n input: {\n dispatchedPromptId: string;\n sessionId: string;\n priority?: number;\n scheduledAt?: string;\n maxAttempts?: number;\n }\n): Promise<AIQueueItem> {\n return agentAiMutate<AIQueueItem>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue/enqueue'),\n 'POST',\n input\n );\n}\n\nexport async function cancelQueueItem(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/queue/${id}/cancel`), 'POST');\n}\n\n// ── Stats (backend route: /ai/stats) ─────────────────────────────\n\nexport async function fetchStatsOverview(agent: AgentRef): Promise<AIStatsOverview> {\n return normalizeStatsOverview(\n await agentAiGet<RawAIStatsOverview>(\n agent.id,\n agentApiPath(agent.profile, '/ai/stats/overview')\n )\n );\n}\n\nexport async function fetchUsageStats(agent: AgentRef): Promise<AIUsageStats> {\n return normalizeUsageStats(\n await agentAiGet<RawAIUsageStats>(agent.id, agentApiPath(agent.profile, '/ai/stats/usage'))\n );\n}\n\n// ── Providers (backend route: /ai/sessions/providers) ────────────\n\nexport async function fetchProviders(agent: AgentRef): Promise<AIProvider[]> {\n const data = await agentAiGet<{ providers: AIProvider[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/providers')\n );\n return data.providers;\n}\n\n// ── API Descriptor (backend route: /ai/descriptor) ───────────────\n\nexport async function fetchApiDescriptor(agent: AgentRef): Promise<AIApiEndpoint[]> {\n const data = await agentAiGet<{ name: string; version: string; endpoints: RawAIApiEndpoint[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/descriptor')\n );\n return data.endpoints.map(normalizeApiEndpoint);\n}\n\n// ── Agent Manager extensions ────────────────────────────────────────\n\nexport interface AIModelInfo {\n id: string;\n name: string;\n provider: string;\n contextWindow?: number;\n maxOutputTokens?: number;\n}\n\nexport interface SearchResult {\n sessionId: string;\n sessionName: string;\n messageId?: string;\n content: string;\n timestamp: string;\n score?: number;\n}\n\nexport async function renameSession(\n agent: AgentRef,\n sessionId: string,\n name: string\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}`),\n 'PATCH',\n { name }\n );\n}\n\nexport async function bulkDeleteSessions(agent: AgentRef, ids: string[]): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, '/ai/sessions/bulk-delete'), 'POST', {\n ids,\n });\n}\n\nexport async function fetchSessionMessages(\n agent: AgentRef,\n sessionId: string\n): Promise<AgentManagerMessage[]> {\n const data = await agentAiGet<{ messages: AgentManagerMessage[] }>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/messages`)\n );\n return data.messages;\n}\n\nexport async function searchSessions(\n agent: AgentRef,\n query: string,\n params?: Record<string, unknown>\n): Promise<SearchResult[]> {\n const data = await agentAiGet<{ results: SearchResult[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/search'),\n { query, ...params }\n );\n return data.results;\n}\n\nexport async function fetchMcpServers(agent: AgentRef): Promise<McpServer[]> {\n const data = await agentAiGet<{ servers: McpServer[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/mcp/servers')\n );\n return data.servers;\n}\n\nexport async function switchSessionModel(\n agent: AgentRef,\n sessionId: string,\n model: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/model`),\n 'PUT',\n { model }\n );\n}\n\nexport async function switchSessionSdk(\n agent: AgentRef,\n sessionId: string,\n sdk: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/sdk`),\n 'PUT',\n { sdk }\n );\n}\n\nexport async function switchSessionMode(\n agent: AgentRef,\n sessionId: string,\n mode: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/mode`),\n 'PUT',\n { mode }\n );\n}\n\nexport async function cancelSessionRequest(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/cancel`),\n 'POST'\n );\n}\n\nexport async function fetchModels(agent: AgentRef, provider?: string): Promise<AIModelInfo[]> {\n const data = await agentAiGet<{ models: AIModelInfo[] }>(\n agent.id,\n agentApiPath(\n agent.profile,\n provider ? `/ai/models/${encodeURIComponent(provider)}` : '/ai/models'\n )\n );\n return data.models;\n}\n\nexport async function exportSession(\n agent: AgentRef,\n sessionId: string,\n format: 'json' | 'markdown'\n): Promise<Blob> {\n const data = await agentAiGet<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/export`),\n { format }\n );\n const content = format === 'json' ? JSON.stringify(data, null, 2) : String(data.content ?? '');\n const mimeType = format === 'json' ? 'application/json' : 'text/markdown';\n return new Blob([content], { type: mimeType });\n}\n\nexport async function uploadSessionFile(\n agent: AgentRef,\n sessionId: string,\n file: File\n): Promise<FileAttachment> {\n const arrayBuffer = await file.arrayBuffer();\n const base64 = btoa(\n new Uint8Array(arrayBuffer).reduce((acc, byte) => acc + String.fromCharCode(byte), '')\n );\n return agentAiMutate<FileAttachment>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/files`),\n 'POST',\n {\n name: file.name,\n mimeType: file.type,\n size: file.size,\n content: base64,\n }\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"aiApi.js","names":[],"sources":["../../src/services/aiApi.ts"],"sourcesContent":["/**\n * AI API service layer\n *\n * Wraps workspace-owned AI prompt APIs and vibecontrols-agent AI REST API\n * endpoints exposed through wspace-vibecontrols-svc.\n *\n * Backend route prefix: /api/ai (mounted by vibe-plugin-ai v4.0.0)\n */\n\nimport {\n getStoredAuthToken,\n getStoredWorkspaceId,\n getStoredWorkspaceToken,\n} from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n AgentManagerMessage,\n FileAttachment,\n McpServer,\n PermissionMode,\n ProviderMode,\n} from '@/types/agentManager';\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\n// Reads from ActiveContextStorage (URL → storage fallback chain). Previously\n// parsed only `?workspace=` from window.location.search, which is empty when\n// the user navigates between MFE routes that don't carry the param. Result:\n// gateway received no `x-workspace-id`, backend `agentAiGet` resolver threw\n// `WorkspaceRequiredError` and the chat pane surfaced \"Unexpected error\".\nfunction getWorkspaceId(): string {\n return getStoredWorkspaceId() ?? '';\n}\n\nfunction getGatewayEndpoint(): string {\n if (typeof window === 'undefined') return '/workspaces/graphql';\n return '/workspaces/graphql';\n}\n\n/**\n * Sanitize error messages — strip HTML, Cloudflare error pages, and long technical details.\n * Returns a clean, user-friendly error string.\n */\nfunction sanitizeError(raw: string): string {\n // Detect Cloudflare tunnel errors (HTML error pages)\n if (raw.includes('Cloudflare Tunnel error') || raw.includes('cloudflare.com/5xx')) {\n return 'Agent tunnel is unreachable. The Cloudflare tunnel may have disconnected. Check if the agent is running and the tunnel is active.';\n }\n // Detect HTML responses\n if (raw.includes('<!DOCTYPE') || raw.includes('<html')) {\n return 'Unexpected HTML response. The agent may be unreachable or the tunnel may have changed.';\n }\n // Detect common network errors\n if (raw.includes('ECONNREFUSED') || raw.includes('ETIMEDOUT') || raw.includes('fetch failed')) {\n return 'Cannot connect to the agent. Check if it is running and accessible.';\n }\n if (\n raw.includes('Could not resolve authentication method') ||\n raw.includes('ANTHROPIC_API_KEY') ||\n raw.includes('ANTHROPIC_AUTH_TOKEN')\n ) {\n return 'Claude SDK auth is not configured on the agent. Open the AI Config tab and save either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.';\n }\n // Detect proxy errors / missing plugin endpoints. 404 on a plugin route\n // almost always means the plugin isn't installed on this agent yet — give\n // the user the actionable next step instead of a vague \"unavailable\".\n if (raw.includes('NOT_FOUND') || raw.includes(' 404')) {\n return 'This feature is not installed on the agent. Install the matching vibe plugin from the agent CLI (e.g. `vibe plugin install <name>`), then refresh.';\n }\n if (raw.includes('502') || raw.includes('503')) {\n return 'Agent service unavailable. The agent may be restarting — try again in a few seconds.';\n }\n // Truncate very long errors\n if (raw.length > 200) {\n return raw.slice(0, 200) + '...';\n }\n return raw;\n}\n\n/**\n * Identifier for an agent + its profile name. Every aiApi function takes\n * one of these so the canonical path `/api/profiles/<profile>/<rest>` can\n * be constructed at every callsite. Callers should pass\n * `{ id: agent.id, profile: agent.metadata?.profile ?? 'default' }`.\n */\nexport interface AgentRef {\n id: string;\n profile: string;\n}\n\n/**\n * Build the canonical agent-API path for a given profile + path segment.\n * The svc-side proxy validates that paths are exactly of the form\n * `/api/profiles/<name>/<rest>` (or one of the bare carve-outs), so every\n * callsite builds the path explicitly via this helper instead of the\n * proxy doing any rewriting.\n *\n * Example: `agentApiPath('default', '/ai/sessions')` →\n * `/api/profiles/default/ai/sessions`.\n */\nexport function agentApiPath(profile: string, segment: string): string {\n const seg = segment.startsWith('/') ? segment : `/${segment}`;\n return `/api/profiles/${encodeURIComponent(profile)}${seg}`;\n}\n\nasync function gatewayFetch<T = unknown>(\n query: string,\n variables: Record<string, unknown>\n): Promise<T> {\n const res = await fetch(getGatewayEndpoint(), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(getStoredAuthToken() ? { Authorization: `Bearer ${getStoredAuthToken()}` } : {}),\n ...(getStoredWorkspaceToken()\n ? { 'X-Workspace-Authorization': `Bearer ${getStoredWorkspaceToken()}` }\n : {}),\n ...(getWorkspaceId() ? { 'x-workspace-id': getWorkspaceId() } : {}),\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeError(`Gateway error ${res.status}: ${text || res.statusText}`));\n }\n\n const json = (await res.json()) as { data?: T; errors?: Array<{ message?: string }> };\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL request failed';\n throw new Error(sanitizeError(msg));\n }\n if (!json.data) throw new Error('GraphQL request returned no data');\n return json.data;\n}\n\nexport async function agentAiGet<T = unknown>(\n agentId: string,\n path: string,\n query?: Record<string, unknown>\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiGet: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `query AgentAiGet($agentId: ID!, $path: String!, $query: JSON) {\n agentAiGet(agentId: $agentId, path: $path, query: $query) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, query: query ?? null }\n );\n\n if (!data.agentAiGet.ok) {\n throw new Error(\n sanitizeError(data.agentAiGet.error || `Agent API error ${data.agentAiGet.status}`)\n );\n }\n return data.agentAiGet.data;\n}\n\nexport async function agentAiMutate<T = unknown>(\n agentId: string,\n path: string,\n method: string,\n body?: unknown\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiMutate: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `mutation AgentAiMutate($agentId: ID!, $path: String!, $method: String!, $body: JSON) {\n agentAiMutate(agentId: $agentId, path: $path, method: $method, body: $body) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, method, body: body ?? null }\n );\n\n if (!data.agentAiMutate.ok) {\n throw new Error(\n sanitizeError(data.agentAiMutate.error || `Agent API error ${data.agentAiMutate.status}`)\n );\n }\n return data.agentAiMutate.data;\n}\n\nexport async function getAgentConfigValue(agent: AgentRef, key: string): Promise<string | null> {\n try {\n const data = await agentAiGet<{ key?: string; value?: string }>(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`)\n );\n return typeof data.value === 'string' ? data.value : null;\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('configuration key not found')\n ) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function setAgentConfigValue(\n agent: AgentRef,\n key: string,\n value: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'PUT',\n { value }\n );\n}\n\nexport async function deleteAgentConfigValue(agent: AgentRef, key: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'DELETE'\n );\n}\n\nfunction qs(params?: Record<string, unknown>): string {\n if (!params) return '';\n const entries = Object.entries(params).filter(\n ([, v]) => v !== undefined && v !== null && v !== ''\n );\n if (entries.length === 0) return '';\n const search = new URLSearchParams();\n for (const [k, v] of entries) search.set(k, String(v));\n return `?${search.toString()}`;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport interface AITemplate {\n id: string;\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n variables: Record<string, unknown>;\n tags: string[];\n isShared?: boolean;\n usageCount?: number;\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIContext {\n id: string;\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AISession {\n id: string;\n name: string;\n agentType: string;\n providerPlugin?: string;\n status: 'active' | 'idle' | 'processing' | 'error' | 'terminated';\n stats?: Record<string, unknown>;\n config?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n terminatedAt?: string | null;\n}\n\nexport interface AISessionLog {\n id: string;\n sessionId: string;\n type: 'input' | 'output' | 'thinking' | 'event' | 'error' | 'metadata';\n content: string;\n message: string;\n tokenCount?: number;\n model?: string;\n durationMs?: number;\n agentMetadata?: Record<string, unknown>;\n timestamp: string;\n createdAt: string;\n}\n\nexport interface AISessionStats {\n session: { id: string; name: string; agentType: string; status: string; createdAt: string };\n stats: {\n totalLogs: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDurationMs: number;\n logsByType: Record<string, number>;\n };\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n promptCount: number;\n averageLatency?: number;\n}\n\nexport interface AIDispatch {\n id: string;\n templateId?: string | null;\n content: string;\n prompt: string;\n resolvedVariables?: Record<string, unknown>;\n contextIds?: string[];\n sessionId?: string | null;\n status: string;\n result?: Record<string, unknown> | null;\n scheduledAt?: string | null;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIQueueItem {\n id: string;\n dispatchedPromptId: string;\n sessionId: string;\n priority: number;\n status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';\n attempts: number;\n maxAttempts: number;\n lastError?: string | null;\n createdAt?: string;\n}\n\nexport interface AIStatsOverview {\n totalSessions: number;\n activeSessions: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n totalRequests: number;\n sessionsByAgentType: Record<string, number>;\n queuedPrompts: number;\n}\n\nexport interface AIUsageStats {\n usage: Record<\n string,\n { inputTokens: number; outputTokens: number; requestCount: number; sessionCount: number }\n >;\n byProvider: Array<{\n provider: string;\n tokens: number;\n sessions: number;\n cost?: number;\n inputTokens: number;\n outputTokens: number;\n requestCount: number;\n }>;\n byStatus: Array<{ status: string; count: number }>;\n}\n\nexport interface AIProvider {\n pluginName: string;\n name?: string;\n displayName?: string;\n isDefault: boolean;\n currentMode?: ProviderMode;\n defaultMode?: ProviderMode;\n supportedModes?: ProviderMode[];\n unsupportedModes?: ProviderMode[];\n modes?: Record<ProviderMode, boolean>;\n prereqApiPrefix?: string;\n}\n\nexport interface AIApiParameter {\n name: string;\n type: string;\n required: boolean;\n description: string;\n}\n\nexport interface AIApiEndpoint {\n id: string;\n method: string;\n path: string;\n description: string;\n parameters?: AIApiParameter[];\n body?: AIApiParameter[];\n}\n\nexport interface AITool {\n name: string;\n displayName: string;\n description: string;\n installed: boolean;\n version?: string;\n configFiles?: string[];\n foundConfigs?: string[];\n installCommand?: string;\n}\n\n// ── Paginated response wrapper ───────────────────────────────────────\n\nexport interface PaginatedResponse<T> {\n items: T[];\n total: number;\n hasMore: boolean;\n}\n\ninterface RawAITemplate extends Omit<AITemplate, 'variables' | 'tags' | 'prompt'> {\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[] | null;\n}\n\ninterface RawAISessionLog extends Omit<AISessionLog, 'message' | 'timestamp'> {\n message?: string;\n timestamp?: string;\n}\n\ninterface RawAISessionStats {\n session?: AISessionStats['session'];\n stats?: Partial<AISessionStats['stats']>;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n promptCount?: number;\n averageLatency?: number;\n}\n\ninterface RawAIDispatch extends Omit<AIDispatch, 'prompt'> {\n prompt?: string;\n}\n\ninterface RawAIStatsOverview {\n totalSessions?: number;\n activeSessions?: number;\n totalInputTokens?: number;\n totalOutputTokens?: number;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n totalRequests?: number;\n sessionsByAgentType?: Record<string, number>;\n queuedPrompts?: number;\n}\n\ninterface RawAIUsageStats {\n usage?: AIUsageStats['usage'];\n byProvider?: AIUsageStats['byProvider'];\n byStatus?: AIUsageStats['byStatus'];\n}\n\ninterface RawAIApiEndpoint {\n method: string;\n path: string;\n description: string;\n parameters?: Record<string, { type: string; required: boolean; description: string }>;\n body?: Record<string, { type: string; required: boolean; description: string }>;\n}\n\nfunction normalizeTemplate(template: RawAITemplate): AITemplate {\n const variables = Array.isArray(template.variables)\n ? Object.fromEntries(template.variables.map((name) => [String(name), '']))\n : (template.variables ?? {});\n\n return {\n ...template,\n description: template.description ?? null,\n variables,\n tags: template.tags ?? [],\n };\n}\n\nfunction normalizeSessionLog(log: RawAISessionLog): AISessionLog {\n return {\n ...log,\n message: log.message ?? log.content,\n timestamp: log.timestamp ?? log.createdAt,\n };\n}\n\nfunction normalizeSessionStats(stats: RawAISessionStats): AISessionStats {\n const statBlock = stats.stats ?? {};\n const inputTokens = stats.inputTokens ?? statBlock.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? statBlock.totalOutputTokens ?? 0;\n const promptCount = stats.promptCount ?? statBlock.totalLogs ?? 0;\n const totalDurationMs = statBlock.totalDurationMs ?? 0;\n\n return {\n session: stats.session ?? {\n id: '',\n name: '',\n agentType: '',\n status: '',\n createdAt: '',\n },\n stats: {\n totalLogs: statBlock.totalLogs ?? 0,\n totalInputTokens: statBlock.totalInputTokens ?? inputTokens,\n totalOutputTokens: statBlock.totalOutputTokens ?? outputTokens,\n totalDurationMs,\n logsByType: statBlock.logsByType ?? {},\n },\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n promptCount,\n averageLatency:\n stats.averageLatency ?? (promptCount > 0 ? totalDurationMs / promptCount : undefined),\n };\n}\n\nfunction normalizeDispatch(dispatch: RawAIDispatch): AIDispatch {\n return {\n ...dispatch,\n prompt: dispatch.prompt ?? dispatch.content,\n };\n}\n\nfunction normalizeStatsOverview(stats: RawAIStatsOverview): AIStatsOverview {\n const inputTokens = stats.inputTokens ?? stats.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? stats.totalOutputTokens ?? 0;\n\n return {\n totalSessions: stats.totalSessions ?? 0,\n activeSessions: stats.activeSessions ?? 0,\n totalInputTokens: stats.totalInputTokens ?? inputTokens,\n totalOutputTokens: stats.totalOutputTokens ?? outputTokens,\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n totalRequests: stats.totalRequests ?? 0,\n sessionsByAgentType: stats.sessionsByAgentType ?? {},\n queuedPrompts: stats.queuedPrompts ?? 0,\n };\n}\n\nfunction normalizeUsageStats(usageStats: RawAIUsageStats): AIUsageStats {\n const usage = usageStats.usage ?? {};\n const byProvider =\n usageStats.byProvider ??\n Object.entries(usage).map(([provider, stats]) => ({\n provider,\n tokens: stats.inputTokens + stats.outputTokens,\n sessions: stats.sessionCount,\n inputTokens: stats.inputTokens,\n outputTokens: stats.outputTokens,\n requestCount: stats.requestCount,\n }));\n\n return {\n usage,\n byProvider,\n byStatus: usageStats.byStatus ?? [],\n };\n}\n\nfunction normalizeApiEndpoint(endpoint: RawAIApiEndpoint): AIApiEndpoint {\n const normalizeFields = (\n fields?: Record<string, { type: string; required: boolean; description: string }>\n ): AIApiParameter[] =>\n Object.entries(fields ?? {}).map(([name, config]) => ({\n name,\n type: config.type,\n required: config.required,\n description: config.description,\n }));\n\n return {\n id: `${endpoint.method.toUpperCase()} ${endpoint.path}`,\n method: endpoint.method,\n path: endpoint.path,\n description: endpoint.description,\n parameters: normalizeFields(endpoint.parameters),\n body: normalizeFields(endpoint.body),\n };\n}\n\n// ── Tools ─────────────────────────────────────────────────────────────\n\nexport async function fetchTools(agent: AgentRef): Promise<AITool[]> {\n const data = await agentAiGet<{ tools: AITool[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/tools')\n );\n return data.tools;\n}\n\n// ── Templates (workspace-owned AI prompts) ───────────────────────────\n\nconst AI_PROMPT_FIELDS = `\n id\n name\n description\n content\n category\n variables\n isShared\n usageCount\n tags\n metadata\n createdAt\n updatedAt\n`;\n\nconst LIST_AI_PROMPTS_QUERY = `query ListAIPrompts($filter: AIPromptFilterInput, $pagination: PaginationInput) {\n aiPrompts(filter: $filter, pagination: $pagination) {\n items {\n ${AI_PROMPT_FIELDS}\n }\n total\n hasMore\n }\n}`;\n\nconst CREATE_AI_PROMPT_MUTATION = `mutation CreateAIPrompt($input: CreateAIPromptInput!) {\n createAIPrompt(input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst UPDATE_AI_PROMPT_MUTATION = `mutation UpdateAIPrompt($id: ID!, $input: UpdateAIPromptInput!) {\n updateAIPrompt(id: $id, input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst DELETE_AI_PROMPT_MUTATION = `mutation DeleteAIPrompt($id: ID!) {\n deleteAIPrompt(id: $id)\n}`;\n\nconst RENDER_AI_PROMPT_MUTATION = `mutation RenderAIPrompt($id: ID!, $variables: JSON) {\n renderAIPrompt(id: $id, variables: $variables) {\n promptId\n rendered\n }\n}`;\n\nconst RECORD_AI_PROMPT_USAGE_MUTATION = `mutation RecordAIPromptUsage($id: ID!) {\n recordAIPromptUsage(id: $id) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\ntype AITemplateInput = {\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n status?: string | null;\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[];\n isShared?: boolean;\n metadata?: Record<string, unknown>;\n};\n\ntype AITemplateUpdateInput = Partial<AITemplateInput>;\n\nfunction enumInput(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim().toUpperCase() : undefined;\n}\n\nfunction numberInput(value: unknown, fallback: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n}\n\nfunction buildAIPromptListVariables(params?: Record<string, unknown>): {\n filter: Record<string, unknown> | null;\n pagination: { limit: number; offset: number };\n} {\n const filter: Record<string, unknown> = {};\n const search = typeof params?.search === 'string' ? params.search.trim() : '';\n if (search) filter.search = search;\n\n const category = enumInput(params?.category);\n if (category) filter.category = category;\n\n const status = enumInput(params?.status);\n if (status) filter.status = status;\n\n if (typeof params?.isShared === 'boolean') filter.isShared = params.isShared;\n if (Array.isArray(params?.tags)) filter.tags = params.tags.map(String).filter(Boolean);\n if (typeof params?.createdBy === 'string' && params.createdBy.trim()) {\n filter.createdBy = params.createdBy.trim();\n }\n\n return {\n filter: Object.keys(filter).length > 0 ? filter : null,\n pagination: {\n limit: Math.min(Math.max(numberInput(params?.limit, 50), 1), 500),\n offset: Math.max(numberInput(params?.offset, 0), 0),\n },\n };\n}\n\nfunction toAIPromptInput(input: AITemplateInput | AITemplateUpdateInput): Record<string, unknown> {\n const promptInput: Record<string, unknown> = {};\n\n if (input.name !== undefined) promptInput.name = input.name;\n if (input.content !== undefined) promptInput.content = input.content;\n if (input.description !== undefined) promptInput.description = input.description;\n if (input.variables !== undefined) promptInput.variables = input.variables;\n if (input.tags !== undefined) promptInput.tags = input.tags;\n if (input.isShared !== undefined) promptInput.isShared = input.isShared;\n if (input.metadata !== undefined) promptInput.metadata = input.metadata;\n\n const category = enumInput(input.category);\n if (category) promptInput.category = category;\n\n const status = enumInput(input.status);\n if (status) promptInput.status = status;\n\n return promptInput;\n}\n\nexport async function fetchTemplates(\n _agentId: string,\n params?: Record<string, unknown>\n): Promise<AITemplate[]> {\n const data = await gatewayFetch<{ aiPrompts: PaginatedResponse<RawAITemplate> }>(\n LIST_AI_PROMPTS_QUERY,\n buildAIPromptListVariables(params)\n );\n return data.aiPrompts.items.map(normalizeTemplate);\n}\n\nexport async function createTemplate(\n _agentId: string,\n input: AITemplateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ createAIPrompt: RawAITemplate }>(CREATE_AI_PROMPT_MUTATION, {\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.createAIPrompt);\n}\n\nexport async function recordTemplateUsage(_agentId: string, id: string): Promise<AITemplate> {\n const data = await gatewayFetch<{ recordAIPromptUsage: RawAITemplate }>(\n RECORD_AI_PROMPT_USAGE_MUTATION,\n { id }\n );\n return normalizeTemplate(data.recordAIPromptUsage);\n}\n\nexport async function updateTemplate(\n _agentId: string,\n id: string,\n input: AITemplateUpdateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ updateAIPrompt: RawAITemplate }>(UPDATE_AI_PROMPT_MUTATION, {\n id,\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.updateAIPrompt);\n}\n\nexport async function deleteTemplate(_agentId: string, id: string): Promise<void> {\n await gatewayFetch<{ deleteAIPrompt: boolean }>(DELETE_AI_PROMPT_MUTATION, { id });\n}\n\nexport async function renderTemplate(\n _agentId: string,\n id: string,\n variables: Record<string, unknown>\n): Promise<{ rendered: string }> {\n const data = await gatewayFetch<{ renderAIPrompt: { promptId: string; rendered: string } }>(\n RENDER_AI_PROMPT_MUTATION,\n { id, variables }\n );\n return { rendered: data.renderAIPrompt.rendered };\n}\n\n// ── Contexts (backend route: /ai/contexts under canonical profile prefix) ──\n\nexport async function fetchContexts(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIContext[]> {\n const data = await agentAiGet<PaginatedResponse<AIContext>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n params\n );\n return data.items;\n}\n\nexport async function createContext(\n agent: AgentRef,\n input: {\n name: string;\n type: string;\n content: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n }\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n 'POST',\n input\n );\n}\n\nexport async function updateContext(\n agent: AgentRef,\n id: string,\n input: Partial<{\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata: Record<string, unknown>;\n }>\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, `/ai/contexts/${id}`),\n 'PUT',\n input\n );\n}\n\nexport async function deleteContext(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/contexts/${id}`), 'DELETE');\n}\n\n// ── Sessions (backend route: /ai/sessions) ──────────────────────────\n\nexport async function fetchSessions(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AISession[]> {\n const data = await agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params\n );\n return data.items;\n}\n\nexport async function fetchSessionsPaginated(\n agent: AgentRef,\n params?: {\n limit?: number;\n offset?: number;\n search?: string;\n agentType?: string;\n status?: string;\n vibeId?: string;\n agentId?: string;\n contextLevel?: 'agent' | 'vibe';\n }\n): Promise<PaginatedResponse<AISession>> {\n return agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params as Record<string, unknown>\n );\n}\n\nexport async function createSession(\n agent: AgentRef,\n input: {\n name: string;\n agentType: string;\n config?: Record<string, unknown>;\n // Context-bucket identity — stamped into the session so the list endpoint\n // can filter by vibe / agent context and teammates sharing the vibe see it.\n vibeId?: string | null;\n agentId?: string | null;\n contextLevel?: 'agent' | 'vibe';\n workspaceId?: string | null;\n createdByUserId?: string | null;\n }\n): Promise<AISession> {\n const body: Record<string, unknown> = {\n name: input.name,\n agentType: input.agentType,\n ...(input.config ? { config: input.config } : {}),\n ...(input.vibeId ? { vibeId: input.vibeId } : {}),\n ...(input.agentId ? { agentId: input.agentId } : {}),\n ...(input.contextLevel ? { contextLevel: input.contextLevel } : {}),\n ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),\n ...(input.createdByUserId ? { createdByUserId: input.createdByUserId } : {}),\n };\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n 'POST',\n body\n );\n}\n\n/**\n * Materialise a Files-module file onto the agent's machine for use as context.\n * The agent fetches the signed `downloadUrl` server-side and writes it under\n * the session working dir, returning the absolute path (+ inline text for small\n * text files) which the caller attaches to the session contexts.\n */\nexport async function fetchFileIntoContext(\n agent: AgentRef,\n input: { downloadUrl: string; filename: string; sessionId?: string; workingDirectory?: string }\n): Promise<{ path: string; bytes: number; contentType?: string; inline?: string | null }> {\n return agentAiMutate<{\n path: string;\n bytes: number;\n contentType?: string;\n inline?: string | null;\n }>(agent.id, agentApiPath(agent.profile, '/ai/context/fetch-file'), 'POST', input);\n}\n\n/**\n * Push an instruction file (AGENTS.md / CLAUDE.md / GEMINI.md / custom) into the\n * agent's working folder so the LLM CLI picks it up. Called on session open for\n * each instruction file linked to the active agent/vibe context.\n */\nexport async function pushInstructionFileToAgent(\n agent: AgentRef,\n input: { filename: string; content: string; sessionId?: string; workingDirectory?: string }\n): Promise<{ path: string; bytes: number }> {\n return agentAiMutate<{ path: string; bytes: number }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/instructions'),\n 'POST',\n input\n );\n}\n\nexport async function sendPromptToSession(\n agent: AgentRef,\n sessionId: string,\n prompt: string,\n contexts?: unknown[],\n options?: {\n agentType?: string;\n model?: string;\n mode?: string;\n permissionMode?: PermissionMode;\n conversationHistory?: Array<{ role: string; content: string }>;\n }\n): Promise<{\n response: string;\n tokens?: { input: number; output: number };\n [key: string]: unknown;\n}> {\n const data = await agentAiMutate<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/send`),\n 'POST',\n {\n prompt,\n contexts,\n ...(options?.agentType ? { agentType: options.agentType } : {}),\n ...(options?.model ? { model: options.model } : {}),\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.permissionMode ? { permissionMode: options.permissionMode } : {}),\n ...(options?.conversationHistory?.length\n ? { conversationHistory: options.conversationHistory }\n : {}),\n }\n );\n const rawTokens = data.tokens as Record<string, unknown> | undefined;\n\n return {\n ...data,\n response: String(data.response ?? data.content ?? data.message ?? ''),\n tokens:\n rawTokens && (typeof rawTokens.input === 'number' || typeof rawTokens.output === 'number')\n ? {\n input: Number(rawTokens.input ?? 0),\n output: Number(rawTokens.output ?? 0),\n }\n : undefined,\n };\n}\n\nexport async function fetchSessionLogs(\n agent: AgentRef,\n sessionId: string,\n params?: Record<string, unknown>\n): Promise<AISessionLog[]> {\n const data = await agentAiGet<PaginatedResponse<RawAISessionLog>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/logs`),\n params\n );\n return data.items.map(normalizeSessionLog);\n}\n\nexport async function fetchSessionStats(\n agent: AgentRef,\n sessionId: string\n): Promise<AISessionStats> {\n return normalizeSessionStats(\n await agentAiGet<RawAISessionStats>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/stats`)\n )\n );\n}\n\nexport async function updateSessionConfig(\n agent: AgentRef,\n sessionId: string,\n config: Record<string, unknown>\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/config`),\n 'PUT',\n config\n );\n}\n\nexport async function terminateSession(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/sessions/${sessionId}`), 'DELETE');\n}\n\n// ── Prompt Composition & Dispatch (backend route: /ai/dispatch) ──\n\nexport async function composePrompt(\n agent: AgentRef,\n input: {\n templateId?: string;\n variables?: Record<string, unknown>;\n content?: string;\n contextIds?: string[];\n sessionId?: string;\n }\n): Promise<{ dispatch: AIDispatch; preview: string }> {\n return agentAiMutate<{ dispatch: AIDispatch; preview: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/compose'),\n 'POST',\n input\n );\n}\n\nexport async function sendDispatch(\n agent: AgentRef,\n input: {\n dispatchId?: string;\n prompt?: string;\n content?: string;\n sessionId?: string;\n contextIds?: string[];\n scheduledAt?: string;\n }\n): Promise<{ status: string; dispatchId: string; sessionId: string }> {\n return agentAiMutate<{ status: string; dispatchId: string; sessionId: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/send'),\n 'POST',\n {\n ...input,\n content: input.content ?? input.prompt,\n }\n );\n}\n\nexport async function fetchDispatchHistory(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIDispatch[]> {\n const data = await agentAiGet<PaginatedResponse<RawAIDispatch>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/history'),\n params\n );\n return data.items.map(normalizeDispatch);\n}\n\n// ── Queue (backend route: /ai/queue) ─────────────────────────────\n\nexport async function fetchQueueItems(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIQueueItem[]> {\n const data = await agentAiGet<PaginatedResponse<AIQueueItem>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue'),\n params\n );\n return data.items;\n}\n\nexport async function enqueueItem(\n agent: AgentRef,\n input: {\n dispatchedPromptId: string;\n sessionId: string;\n priority?: number;\n scheduledAt?: string;\n maxAttempts?: number;\n }\n): Promise<AIQueueItem> {\n return agentAiMutate<AIQueueItem>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue/enqueue'),\n 'POST',\n input\n );\n}\n\nexport async function cancelQueueItem(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/queue/${id}/cancel`), 'POST');\n}\n\n// ── Stats (backend route: /ai/stats) ─────────────────────────────\n\nexport async function fetchStatsOverview(agent: AgentRef): Promise<AIStatsOverview> {\n return normalizeStatsOverview(\n await agentAiGet<RawAIStatsOverview>(\n agent.id,\n agentApiPath(agent.profile, '/ai/stats/overview')\n )\n );\n}\n\nexport async function fetchUsageStats(agent: AgentRef): Promise<AIUsageStats> {\n return normalizeUsageStats(\n await agentAiGet<RawAIUsageStats>(agent.id, agentApiPath(agent.profile, '/ai/stats/usage'))\n );\n}\n\n// ── Providers (backend route: /ai/sessions/providers) ────────────\n\nexport async function fetchProviders(agent: AgentRef): Promise<AIProvider[]> {\n const data = await agentAiGet<{ providers: AIProvider[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/providers')\n );\n return data.providers;\n}\n\n// ── API Descriptor (backend route: /ai/descriptor) ───────────────\n\nexport async function fetchApiDescriptor(agent: AgentRef): Promise<AIApiEndpoint[]> {\n const data = await agentAiGet<{ name: string; version: string; endpoints: RawAIApiEndpoint[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/descriptor')\n );\n return data.endpoints.map(normalizeApiEndpoint);\n}\n\n// ── Agent Manager extensions ────────────────────────────────────────\n\nexport interface AIModelInfo {\n id: string;\n name: string;\n provider: string;\n contextWindow?: number;\n maxOutputTokens?: number;\n}\n\nexport interface SearchResult {\n sessionId: string;\n sessionName: string;\n messageId?: string;\n content: string;\n timestamp: string;\n score?: number;\n}\n\nexport async function renameSession(\n agent: AgentRef,\n sessionId: string,\n name: string\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}`),\n 'PATCH',\n { name }\n );\n}\n\nexport async function bulkDeleteSessions(agent: AgentRef, ids: string[]): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, '/ai/sessions/bulk-delete'), 'POST', {\n ids,\n });\n}\n\nexport async function fetchSessionMessages(\n agent: AgentRef,\n sessionId: string\n): Promise<AgentManagerMessage[]> {\n const data = await agentAiGet<{ messages: AgentManagerMessage[] }>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/messages`)\n );\n return data.messages;\n}\n\nexport async function searchSessions(\n agent: AgentRef,\n query: string,\n params?: Record<string, unknown>\n): Promise<SearchResult[]> {\n const data = await agentAiGet<{ results: SearchResult[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/search'),\n { query, ...params }\n );\n return data.results;\n}\n\nexport async function fetchMcpServers(agent: AgentRef): Promise<McpServer[]> {\n const data = await agentAiGet<{ servers: McpServer[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/mcp/servers')\n );\n return data.servers;\n}\n\nexport async function switchSessionModel(\n agent: AgentRef,\n sessionId: string,\n model: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/model`),\n 'PUT',\n { model }\n );\n}\n\nexport async function switchSessionSdk(\n agent: AgentRef,\n sessionId: string,\n sdk: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/sdk`),\n 'PUT',\n { sdk }\n );\n}\n\nexport async function switchSessionMode(\n agent: AgentRef,\n sessionId: string,\n mode: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/mode`),\n 'PUT',\n { mode }\n );\n}\n\nexport async function cancelSessionRequest(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/cancel`),\n 'POST'\n );\n}\n\nexport async function fetchModels(agent: AgentRef, provider?: string): Promise<AIModelInfo[]> {\n const data = await agentAiGet<{ models: AIModelInfo[] }>(\n agent.id,\n agentApiPath(\n agent.profile,\n provider ? `/ai/models/${encodeURIComponent(provider)}` : '/ai/models'\n )\n );\n return data.models;\n}\n\nexport async function exportSession(\n agent: AgentRef,\n sessionId: string,\n format: 'json' | 'markdown'\n): Promise<Blob> {\n const data = await agentAiGet<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/export`),\n { format }\n );\n const content = format === 'json' ? JSON.stringify(data, null, 2) : String(data.content ?? '');\n const mimeType = format === 'json' ? 'application/json' : 'text/markdown';\n return new Blob([content], { type: mimeType });\n}\n\nexport async function uploadSessionFile(\n agent: AgentRef,\n sessionId: string,\n file: File\n): Promise<FileAttachment> {\n const arrayBuffer = await file.arrayBuffer();\n const base64 = btoa(\n new Uint8Array(arrayBuffer).reduce((acc, byte) => acc + String.fromCharCode(byte), '')\n );\n return agentAiMutate<FileAttachment>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/files`),\n 'POST',\n {\n name: file.name,\n mimeType: file.type,\n size: file.size,\n content: base64,\n }\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.611.2",
3
+ "version": "2026.611.3",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [