@burdenoff/microfe-vibecontrols 2026.531.5 → 2026.531.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/assistant/AIAssistantPanel.js +37 -35
- package/dist/components/assistant/AIAssistantPanel.js.map +1 -1
- package/dist/components/assistant/AiCreditsMeter.js +21 -0
- package/dist/components/assistant/AiCreditsMeter.js.map +1 -0
- package/dist/components/assistant/AssistantChatArea.js +129 -108
- package/dist/components/assistant/AssistantChatArea.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +6 -6
- package/dist/generated/global-operations.js +32 -0
- package/dist/generated/global-operations.js.map +1 -0
- package/dist/generated/wspace-operations.js +1366 -1342
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/hooks/useAiCreditsQuota.js +47 -0
- package/dist/hooks/useAiCreditsQuota.js.map +1 -0
- package/dist/index.js +10 -10
- package/dist/pages/ActionsPage.js +1 -1
- package/dist/pages/AgentDetailsPage.js +8 -8
- package/dist/pages/AgentsPage.js +8 -8
- package/dist/pages/SessionsPage.js +8 -8
- package/dist/pages/SettingsPage.js +7 -7
- package/dist/pages/VibeCalendarPage.js +1 -1
- package/dist/pages/VibeDeckPage.js +1 -1
- package/dist/pages/VibeDetailsPage.js +11 -11
- package/dist/pages/VibesPage.js +7 -7
- package/dist/services/assistantApi.js +87 -62
- package/dist/services/assistantApi.js.map +1 -1
- package/dist/utils/aiCredits.js +15 -0
- package/dist/utils/aiCredits.js.map +1 -0
- package/dist/utils/quotaUtils.js +5 -0
- package/dist/utils/quotaUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,69 +3,71 @@ import { useAssistantStore as t } from "../../store/assistantStore.js";
|
|
|
3
3
|
import { useAssistantKeyboardShortcuts as n } from "../../hooks/useAssistantKeyboardShortcuts.js";
|
|
4
4
|
import { useAssistantHeartbeat as r } from "../../hooks/useAssistantHeartbeat.js";
|
|
5
5
|
import { AssistantChatArea as i } from "./AssistantChatArea.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
6
|
+
import { AiCreditsMeter as a } from "./AiCreditsMeter.js";
|
|
7
|
+
import { AssistantConversationList as o } from "./AssistantConversationList.js";
|
|
8
|
+
import { AssistantExportMenu as s } from "./AssistantExportMenu.js";
|
|
9
|
+
import { useRef as c } from "react";
|
|
10
|
+
import { Eraser as l, MessageSquare as u, X as d } from "lucide-react";
|
|
11
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
11
12
|
//#region src/components/assistant/AIAssistantPanel.tsx
|
|
12
|
-
var
|
|
13
|
-
let
|
|
14
|
-
r(
|
|
15
|
-
let
|
|
13
|
+
var m = ({ onClose: m, visible: h }) => {
|
|
14
|
+
let g = e(), _ = c(null), { conversations: v, activeConversationId: y, conversationListOpen: b, toggleConversationList: x, clearConversation: S } = t(), C = v.find((e) => e.id === y);
|
|
15
|
+
r(C?.backendSessionId ?? null, C?.mode ?? null, h ?? !0);
|
|
16
|
+
let w = (C?.messages.length ?? 0) > 0;
|
|
16
17
|
return n({
|
|
17
|
-
panelRef:
|
|
18
|
-
onClose:
|
|
19
|
-
}), /* @__PURE__ */
|
|
20
|
-
ref:
|
|
18
|
+
panelRef: _,
|
|
19
|
+
onClose: m
|
|
20
|
+
}), /* @__PURE__ */ p("div", {
|
|
21
|
+
ref: _,
|
|
21
22
|
className: "flex flex-col h-full w-full bg-bg-surface relative",
|
|
22
23
|
role: "complementary",
|
|
23
|
-
"aria-label":
|
|
24
|
+
"aria-label": g("vibecontrols.assistant.panel.ariaLabel", "AI Assistant"),
|
|
24
25
|
"data-testid": "ai-assistant-panel",
|
|
25
26
|
tabIndex: -1,
|
|
26
27
|
children: [
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ p("div", {
|
|
28
29
|
className: "flex items-center justify-between px-3 py-2 border-b border-border-default bg-bg-elevated flex-shrink-0",
|
|
29
|
-
children: [/* @__PURE__ */
|
|
30
|
+
children: [/* @__PURE__ */ p("div", {
|
|
30
31
|
className: "flex items-center gap-2 min-w-0",
|
|
31
|
-
children: [/* @__PURE__ */
|
|
32
|
+
children: [/* @__PURE__ */ f("button", {
|
|
32
33
|
type: "button",
|
|
33
|
-
onClick:
|
|
34
|
+
onClick: x,
|
|
34
35
|
className: "p-1 hover:bg-bg-sunken rounded transition-colors",
|
|
35
|
-
"aria-label":
|
|
36
|
+
"aria-label": g("vibecontrols.assistant.panel.toggleList", "Toggle conversation list"),
|
|
36
37
|
"data-testid": "toggle-conversation-list",
|
|
37
|
-
children: /* @__PURE__ */
|
|
38
|
-
}), /* @__PURE__ */
|
|
38
|
+
children: /* @__PURE__ */ f(u, { className: "size-3.5 text-text-secondary" })
|
|
39
|
+
}), /* @__PURE__ */ f("span", {
|
|
39
40
|
className: "text-xs font-semibold text-text-primary truncate",
|
|
40
|
-
children:
|
|
41
|
+
children: C?.title ?? g("vibecontrols.assistant.panel.title", "AI Assistant")
|
|
41
42
|
})]
|
|
42
|
-
}), /* @__PURE__ */
|
|
43
|
+
}), /* @__PURE__ */ p("div", {
|
|
43
44
|
className: "flex items-center gap-0.5 flex-shrink-0",
|
|
44
45
|
children: [
|
|
45
|
-
|
|
46
|
+
w && /* @__PURE__ */ f("button", {
|
|
46
47
|
type: "button",
|
|
47
|
-
onClick: () =>
|
|
48
|
+
onClick: () => y && S(y),
|
|
48
49
|
className: "p-1 hover:bg-bg-sunken rounded transition-colors",
|
|
49
|
-
"aria-label":
|
|
50
|
-
children: /* @__PURE__ */
|
|
50
|
+
"aria-label": g("vibecontrols.assistant.panel.clearConversation", "Clear conversation"),
|
|
51
|
+
children: /* @__PURE__ */ f(l, { className: "size-3.5 text-text-secondary" })
|
|
51
52
|
}),
|
|
52
|
-
/* @__PURE__ */
|
|
53
|
-
/* @__PURE__ */
|
|
53
|
+
/* @__PURE__ */ f(s, {}),
|
|
54
|
+
/* @__PURE__ */ f("button", {
|
|
54
55
|
type: "button",
|
|
55
|
-
onClick:
|
|
56
|
+
onClick: m,
|
|
56
57
|
className: "p-1 hover:bg-bg-sunken rounded transition-colors md:hidden",
|
|
57
|
-
"aria-label":
|
|
58
|
-
children: /* @__PURE__ */ d
|
|
58
|
+
"aria-label": g("vibecontrols.assistant.panel.close", "Close assistant"),
|
|
59
|
+
children: /* @__PURE__ */ f(d, { className: "size-3.5 text-text-secondary" })
|
|
59
60
|
})
|
|
60
61
|
]
|
|
61
62
|
})]
|
|
62
63
|
}),
|
|
63
|
-
|
|
64
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ f(a, {}),
|
|
65
|
+
b && /* @__PURE__ */ f(o, {}),
|
|
66
|
+
/* @__PURE__ */ f(i, {})
|
|
65
67
|
]
|
|
66
68
|
});
|
|
67
69
|
};
|
|
68
70
|
//#endregion
|
|
69
|
-
export {
|
|
71
|
+
export { m as AIAssistantPanel };
|
|
70
72
|
|
|
71
73
|
//# sourceMappingURL=AIAssistantPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIAssistantPanel.js","names":[],"sources":["../../../src/components/assistant/AIAssistantPanel.tsx"],"sourcesContent":["/**\n * AI Assistant Panel — top-level component for the right-pane assistant.\n *\n * Receives PanelProps from the plugin panel system. Orchestrates the\n * conversation list, chat area, and export menu.\n *\n * Responsive behavior:\n * - Desktop (>= 768px): Renders inline within PanelContainer (right panel)\n * - Mobile (< 768px): Panel container handles sizing; panel fills available space\n */\n\nimport { useRef, type FC } from 'react';\nimport { MessageSquare, Eraser, X } from 'lucide-react';\nimport type { PanelProps } from '@burdenoff/fe-libs/shared/plugins';\nimport { useAssistantStore } from '@/store/assistantStore';\nimport { useAssistantKeyboardShortcuts } from '@/hooks/useAssistantKeyboardShortcuts';\nimport { useAssistantHeartbeat } from '@/hooks/useAssistantHeartbeat';\nimport { AssistantChatArea } from './AssistantChatArea';\nimport { AssistantConversationList } from './AssistantConversationList';\nimport { AssistantExportMenu } from './AssistantExportMenu';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport const AIAssistantPanel: FC<PanelProps> = ({ onClose, visible }) => {\n const tr = useTr();\n const panelRef = useRef<HTMLDivElement>(null);\n\n const {\n conversations,\n activeConversationId,\n conversationListOpen,\n toggleConversationList,\n clearConversation,\n } = useAssistantStore();\n\n const activeConversation = conversations.find((c) => c.id === activeConversationId);\n const activeSessionId = activeConversation?.backendSessionId ?? null;\n\n // Keep sandbox alive while panel is open\n useAssistantHeartbeat(activeSessionId, activeConversation?.mode ?? null, visible ?? true);\n\n const hasMessages = (activeConversation?.messages.length ?? 0) > 0;\n\n useAssistantKeyboardShortcuts({\n panelRef,\n onClose,\n });\n\n return (\n <div\n ref={panelRef}\n className=\"flex flex-col h-full w-full bg-bg-surface relative\"\n role=\"complementary\"\n aria-label={tr('vibecontrols.assistant.panel.ariaLabel', 'AI Assistant')}\n data-testid=\"ai-assistant-panel\"\n tabIndex={-1}\n >\n {/* Panel header */}\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border-default bg-bg-elevated flex-shrink-0\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <button\n type=\"button\"\n onClick={toggleConversationList}\n className=\"p-1 hover:bg-bg-sunken rounded transition-colors\"\n aria-label={tr('vibecontrols.assistant.panel.toggleList', 'Toggle conversation list')}\n data-testid=\"toggle-conversation-list\"\n >\n <MessageSquare className=\"size-3.5 text-text-secondary\" />\n </button>\n <span className=\"text-xs font-semibold text-text-primary truncate\">\n {activeConversation?.title ?? tr('vibecontrols.assistant.panel.title', 'AI Assistant')}\n </span>\n </div>\n\n <div className=\"flex items-center gap-0.5 flex-shrink-0\">\n {hasMessages && (\n <button\n type=\"button\"\n onClick={() => activeConversationId && clearConversation(activeConversationId)}\n className=\"p-1 hover:bg-bg-sunken rounded transition-colors\"\n aria-label={tr(\n 'vibecontrols.assistant.panel.clearConversation',\n 'Clear conversation'\n )}\n >\n <Eraser className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n <AssistantExportMenu />\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 hover:bg-bg-sunken rounded transition-colors md:hidden\"\n aria-label={tr('vibecontrols.assistant.panel.close', 'Close assistant')}\n >\n <X className=\"size-3.5 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Conversation list overlay */}\n {conversationListOpen && <AssistantConversationList />}\n\n {/* Chat area */}\n <AssistantChatArea />\n </div>\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AIAssistantPanel.js","names":[],"sources":["../../../src/components/assistant/AIAssistantPanel.tsx"],"sourcesContent":["/**\n * AI Assistant Panel — top-level component for the right-pane assistant.\n *\n * Receives PanelProps from the plugin panel system. Orchestrates the\n * conversation list, chat area, and export menu.\n *\n * Responsive behavior:\n * - Desktop (>= 768px): Renders inline within PanelContainer (right panel)\n * - Mobile (< 768px): Panel container handles sizing; panel fills available space\n */\n\nimport { useRef, type FC } from 'react';\nimport { MessageSquare, Eraser, X } from 'lucide-react';\nimport type { PanelProps } from '@burdenoff/fe-libs/shared/plugins';\nimport { useAssistantStore } from '@/store/assistantStore';\nimport { useAssistantKeyboardShortcuts } from '@/hooks/useAssistantKeyboardShortcuts';\nimport { useAssistantHeartbeat } from '@/hooks/useAssistantHeartbeat';\nimport { AssistantChatArea } from './AssistantChatArea';\nimport { AiCreditsMeter } from './AiCreditsMeter';\nimport { AssistantConversationList } from './AssistantConversationList';\nimport { AssistantExportMenu } from './AssistantExportMenu';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport const AIAssistantPanel: FC<PanelProps> = ({ onClose, visible }) => {\n const tr = useTr();\n const panelRef = useRef<HTMLDivElement>(null);\n\n const {\n conversations,\n activeConversationId,\n conversationListOpen,\n toggleConversationList,\n clearConversation,\n } = useAssistantStore();\n\n const activeConversation = conversations.find((c) => c.id === activeConversationId);\n const activeSessionId = activeConversation?.backendSessionId ?? null;\n\n // Keep sandbox alive while panel is open\n useAssistantHeartbeat(activeSessionId, activeConversation?.mode ?? null, visible ?? true);\n\n const hasMessages = (activeConversation?.messages.length ?? 0) > 0;\n\n useAssistantKeyboardShortcuts({\n panelRef,\n onClose,\n });\n\n return (\n <div\n ref={panelRef}\n className=\"flex flex-col h-full w-full bg-bg-surface relative\"\n role=\"complementary\"\n aria-label={tr('vibecontrols.assistant.panel.ariaLabel', 'AI Assistant')}\n data-testid=\"ai-assistant-panel\"\n tabIndex={-1}\n >\n {/* Panel header */}\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border-default bg-bg-elevated flex-shrink-0\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <button\n type=\"button\"\n onClick={toggleConversationList}\n className=\"p-1 hover:bg-bg-sunken rounded transition-colors\"\n aria-label={tr('vibecontrols.assistant.panel.toggleList', 'Toggle conversation list')}\n data-testid=\"toggle-conversation-list\"\n >\n <MessageSquare className=\"size-3.5 text-text-secondary\" />\n </button>\n <span className=\"text-xs font-semibold text-text-primary truncate\">\n {activeConversation?.title ?? tr('vibecontrols.assistant.panel.title', 'AI Assistant')}\n </span>\n </div>\n\n <div className=\"flex items-center gap-0.5 flex-shrink-0\">\n {hasMessages && (\n <button\n type=\"button\"\n onClick={() => activeConversationId && clearConversation(activeConversationId)}\n className=\"p-1 hover:bg-bg-sunken rounded transition-colors\"\n aria-label={tr(\n 'vibecontrols.assistant.panel.clearConversation',\n 'Clear conversation'\n )}\n >\n <Eraser className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n <AssistantExportMenu />\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 hover:bg-bg-sunken rounded transition-colors md:hidden\"\n aria-label={tr('vibecontrols.assistant.panel.close', 'Close assistant')}\n >\n <X className=\"size-3.5 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Live AI-credits usage meter (hidden when there's no quota to show) */}\n <AiCreditsMeter />\n\n {/* Conversation list overlay */}\n {conversationListOpen && <AssistantConversationList />}\n\n {/* Chat area */}\n <AssistantChatArea />\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAuBA,IAAa,KAAoC,EAAE,YAAS,iBAAc;CACxE,IAAM,IAAK,GAAO,EACZ,IAAW,EAAuB,KAAK,EAEvC,EACJ,kBACA,yBACA,yBACA,2BACA,yBACE,GAAmB,EAEjB,IAAqB,EAAc,MAAM,MAAM,EAAE,OAAO,EAAqB;AAInF,GAHwB,GAAoB,oBAAoB,MAGzB,GAAoB,QAAQ,MAAM,KAAW,GAAK;CAEzF,IAAM,KAAe,GAAoB,SAAS,UAAU,KAAK;AAOjE,QALA,EAA8B;EAC5B;EACA;EACD,CAAC,EAGA,kBAAC,OAAD;EACE,KAAK;EACL,WAAU;EACV,MAAK;EACL,cAAY,EAAG,0CAA0C,eAAe;EACxE,eAAY;EACZ,UAAU;YANZ;GASE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,cAAY,EAAG,2CAA2C,2BAA2B;MACrF,eAAY;gBAEZ,kBAAC,GAAD,EAAe,WAAU,gCAAiC,CAAA;MACnD,CAAA,EACT,kBAAC,QAAD;MAAM,WAAU;gBACb,GAAoB,SAAS,EAAG,sCAAsC,eAAe;MACjF,CAAA,CACH;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,KAAwB,EAAkB,EAAqB;OAC9E,WAAU;OACV,cAAY,EACV,kDACA,qBACD;iBAED,kBAAC,GAAD,EAAQ,WAAU,gCAAiC,CAAA;OAC5C,CAAA;MAEX,kBAAC,GAAD,EAAuB,CAAA;MACvB,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,cAAY,EAAG,sCAAsC,kBAAkB;iBAEvE,kBAAC,GAAD,EAAG,WAAU,gCAAiC,CAAA;OACvC,CAAA;MACL;OACF;;GAGN,kBAAC,GAAD,EAAkB,CAAA;GAGjB,KAAwB,kBAAC,GAAD,EAA6B,CAAA;GAGtD,kBAAC,GAAD,EAAqB,CAAA;GACjB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useAiCreditsQuota as t } from "../../hooks/useAiCreditsQuota.js";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
import { QuotaMeter as r } from "@burdenoff/fe-libs/shared/components";
|
|
5
|
+
//#region src/components/assistant/AiCreditsMeter.tsx
|
|
6
|
+
var i = () => {
|
|
7
|
+
let i = e(), { data: a } = t();
|
|
8
|
+
return !a || a.noLimit ? null : /* @__PURE__ */ n("div", {
|
|
9
|
+
className: "px-3 py-1.5 border-b border-border-default bg-bg-surface flex-shrink-0",
|
|
10
|
+
children: /* @__PURE__ */ n(r, {
|
|
11
|
+
label: i("vibecontrols.assistant.credits.label", "AI credits"),
|
|
12
|
+
used: a.used,
|
|
13
|
+
limit: a.limit,
|
|
14
|
+
unit: i("vibecontrols.assistant.credits.unit", "tokens")
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { i as AiCreditsMeter };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=AiCreditsMeter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiCreditsMeter.js","names":[],"sources":["../../../src/components/assistant/AiCreditsMeter.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { QuotaMeter } from '@burdenoff/fe-libs/shared/components';\nimport { useAiCreditsQuota } from '@/hooks/useAiCreditsQuota';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Thin AI-credits usage strip shown under the assistant panel header. Hidden\n * entirely when there's no quota to show (standalone preview, no billing\n * account, or unlimited plan with nothing meaningful to meter).\n */\nexport const AiCreditsMeter: FC = () => {\n const tr = useTr();\n const { data } = useAiCreditsQuota();\n\n // Nothing to meter — hide rather than render an empty/Unlimited strip.\n if (!data || data.noLimit) return null;\n\n return (\n <div className=\"px-3 py-1.5 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <QuotaMeter\n label={tr('vibecontrols.assistant.credits.label', 'AI credits')}\n used={data.used}\n limit={data.limit}\n unit={tr('vibecontrols.assistant.credits.unit', 'tokens')}\n />\n </div>\n );\n};\n"],"mappings":";;;;;AAUA,IAAa,UAA2B;CACtC,IAAM,IAAK,GAAO,EACZ,EAAE,YAAS,GAAmB;AAKpC,QAFI,CAAC,KAAQ,EAAK,UAAgB,OAGhC,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,OAAO,EAAG,wCAAwC,aAAa;GAC/D,MAAM,EAAK;GACX,OAAO,EAAK;GACZ,MAAM,EAAG,uCAAuC,SAAS;GACzD,CAAA;EACE,CAAA"}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
2
|
+
import { isQuotaExhaustedError as t } from "../../utils/quotaUtils.js";
|
|
3
|
+
import { useAssistantStore as n } from "../../store/assistantStore.js";
|
|
4
|
+
import { checkAssistantAiCredits as r, createAssistantSandbox as i, createAssistantSession as a, extendAssistantSandboxTTL as o, findExistingSandbox as s, getAssistantArtifacts as c, getAssistantMessages as l, getAssistantSession as u, reportAssistantAiTokenUsage as d, sendAssistantPromptAsync as ee, waitForAssistantServiceReady as f, waitForSandboxReady as p } from "../../services/assistantApi.js";
|
|
5
|
+
import { AssistantChatInput as m } from "./AssistantChatInput.js";
|
|
6
|
+
import { AssistantMessageList as h } from "./AssistantMessageList.js";
|
|
7
|
+
import { AssistantModeSelector as g } from "./AssistantModeSelector.js";
|
|
8
|
+
import { AssistantWelcome as _ } from "./AssistantWelcome.js";
|
|
9
|
+
import { gatherPageContext as te } from "../../utils/pageContext.js";
|
|
10
|
+
import { dispatchAssistantQuotaExhausted as v, notifyAiCreditsUpdated as ne } from "../../utils/aiCredits.js";
|
|
11
|
+
import { useCallback as y, useEffect as b, useRef as x } from "react";
|
|
12
|
+
import { useGatewayClient as re } from "@burdenoff/fe-libs/shared/graphql";
|
|
13
|
+
import { useAuthToken as ie } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
14
|
+
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
13
15
|
//#region src/components/assistant/AssistantChatArea.tsx
|
|
14
|
-
function
|
|
16
|
+
function w() {
|
|
15
17
|
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
16
18
|
}
|
|
17
|
-
function
|
|
19
|
+
function T(e) {
|
|
18
20
|
try {
|
|
19
21
|
let t = e === "workspaceId" ? "burdenoff-active-context-workspace" : "burdenoff-active-context-organization", n = localStorage.getItem(t);
|
|
20
22
|
if (n) return n;
|
|
@@ -26,13 +28,13 @@ function w(e) {
|
|
|
26
28
|
return "";
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
|
-
function
|
|
30
|
-
return new URLSearchParams(window.location.search).get("workspace") ??
|
|
31
|
+
function ae() {
|
|
32
|
+
return new URLSearchParams(window.location.search).get("workspace") ?? T("workspaceId");
|
|
31
33
|
}
|
|
32
|
-
function
|
|
33
|
-
return new URLSearchParams(window.location.search).get("org") ??
|
|
34
|
+
function oe() {
|
|
35
|
+
return new URLSearchParams(window.location.search).get("org") ?? T("organizationId");
|
|
34
36
|
}
|
|
35
|
-
function
|
|
37
|
+
function se(e) {
|
|
36
38
|
let t = e instanceof Error ? e.message.toLowerCase() : String(e).toLowerCase();
|
|
37
39
|
return t.includes("rate limit exceeded") || t.includes("unauthorized") || t.includes("k8s api error 401") ? !1 : [
|
|
38
40
|
"sandbox not found",
|
|
@@ -47,35 +49,35 @@ function re(e) {
|
|
|
47
49
|
"session mode mismatch"
|
|
48
50
|
].some((e) => t.includes(e));
|
|
49
51
|
}
|
|
50
|
-
function
|
|
52
|
+
function E(e) {
|
|
51
53
|
return e.replace(/(Authorization\s*:\s*Bearer\s+)[^\s\n]+/gi, "$1[REDACTED]").replace(/(X-Workspace-Authorization\s*:\s*Bearer\s+)[^\s\n]+/gi, "$1[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+\b/g, "[REDACTED_JWT]").replace(/\bsk-ant-[A-Za-z0-9-]+\b/g, "[REDACTED_API_KEY]");
|
|
52
54
|
}
|
|
53
|
-
function
|
|
55
|
+
function D(e) {
|
|
54
56
|
return e.info?.time?.created ?? 0;
|
|
55
57
|
}
|
|
56
|
-
function
|
|
58
|
+
function O(e) {
|
|
57
59
|
return (e ?? []).filter((e) => e.type === "text" && typeof e.text == "string").map((e) => e.text?.trim() ?? "").filter(Boolean).join("\n");
|
|
58
60
|
}
|
|
59
|
-
function
|
|
61
|
+
function k(e) {
|
|
60
62
|
return (e ?? []).filter((e) => e.type === "tool" && e.tool).map((e) => {
|
|
61
63
|
let t = e.state?.status ?? "running", n = e.tool ?? "tool";
|
|
62
64
|
return t === "completed" ? `Completed: ${n}` : t === "failed" ? `Failed: ${n}` : `Running: ${n}`;
|
|
63
65
|
});
|
|
64
66
|
}
|
|
65
|
-
function
|
|
66
|
-
let n = e.filter((e) => e.info?.role === "assistant" &&
|
|
67
|
+
function A(e, t) {
|
|
68
|
+
let n = e.filter((e) => e.info?.role === "assistant" && D(e) >= t).sort((e, t) => D(e) - D(t));
|
|
67
69
|
if (n.length === 0) return {
|
|
68
70
|
content: "Thinking...",
|
|
69
71
|
done: !1
|
|
70
72
|
};
|
|
71
|
-
let r = n[n.length - 1], i =
|
|
73
|
+
let r = n[n.length - 1], i = O(r.parts), a = k(r.parts);
|
|
72
74
|
return {
|
|
73
75
|
content: [i, ...a].filter(Boolean).join("\n\n") || "Thinking...",
|
|
74
76
|
done: !!r.info?.time?.completed && (!!i || a.length > 0),
|
|
75
77
|
tokens: r.info?.tokens
|
|
76
78
|
};
|
|
77
79
|
}
|
|
78
|
-
function
|
|
80
|
+
function ce(e) {
|
|
79
81
|
switch (e) {
|
|
80
82
|
case "provisioning": return "Starting assistant environment...";
|
|
81
83
|
case "connecting": return "Connecting to assistant...";
|
|
@@ -84,107 +86,126 @@ function A(e) {
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
var j = () => {
|
|
87
|
-
let
|
|
88
|
-
U(e, n, t, null), r ? V(e, t, r) : V(e, t,
|
|
89
|
-
}, [U, V]), Z =
|
|
90
|
-
let
|
|
91
|
-
if (!
|
|
92
|
-
X(e,
|
|
93
|
-
let
|
|
94
|
-
if (
|
|
89
|
+
let T = e(), D = re("workspace"), { getAccessToken: O, getWorkspaceToken: k, userId: j } = ie(), { conversations: M, activeConversationId: N, mode: P, processingByConversationId: F, setMode: I, setConversationMode: L, setActiveConversation: R, createConversation: z, addMessage: B, updateMessage: V, patchMessage: H, setConversationProcessing: U, clearConversationProcessing: le, setBackendSessionId: W, clearBackendSessionId: G, setSandbox: K, clearSandbox: q } = n(), J = M.find((e) => e.id === N)?.messages ?? [], Y = N ? F[N] : void 0, ue = Y?.messageId ?? null, de = Y?.status === "provisioning" || Y?.status === "connecting" || Y?.status === "streaming", X = y((e, t, n, r) => {
|
|
90
|
+
U(e, n, t, null), r ? V(e, t, r) : V(e, t, ce(n));
|
|
91
|
+
}, [U, V]), Z = y(async (e, t, r, o, c) => {
|
|
92
|
+
let l = n.getState().sandboxesByMode[r]?.sandboxId ?? null;
|
|
93
|
+
if (!l) {
|
|
94
|
+
X(e, t, "provisioning", "Looking for existing environment...");
|
|
95
|
+
let n = await s(D, o, r, c);
|
|
96
|
+
if (n) l = n, K(r, l, "provisioning"), X(e, t, "provisioning", "Reusing assistant environment..."), await p(D, l, o, c, 3e5, (n) => X(e, t, "provisioning", n)), await f(D, l, o, c, 9e4, (n) => X(e, t, "connecting", n)), K(r, l, "running");
|
|
95
97
|
else {
|
|
96
|
-
if (
|
|
97
|
-
X(e,
|
|
98
|
+
if (r === "api-calls" && !O()) throw Error("API Calls mode requires an authenticated session. Please sign in again.");
|
|
99
|
+
X(e, t, "provisioning", "Starting assistant environment..."), K(r, null, "provisioning"), l = await i(D, r, o, c), K(r, l, "provisioning"), await p(D, l, o, c, 3e5, (n) => X(e, t, "provisioning", n)), await f(D, l, o, c, 9e4, (n) => X(e, t, "connecting", n)), K(r, l, "running");
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
|
-
let
|
|
101
|
-
if (
|
|
102
|
-
let e = await
|
|
103
|
-
if (e.mode !==
|
|
102
|
+
let d = n.getState().conversations.find((t) => t.id === e)?.backendSessionId ?? null;
|
|
103
|
+
if (d) try {
|
|
104
|
+
let e = await u(D, l, o, d, c);
|
|
105
|
+
if (e.mode !== r) throw Error(`Session mode mismatch: expected ${r}, received ${e.mode}`);
|
|
104
106
|
} catch {
|
|
105
|
-
G(e),
|
|
107
|
+
G(e), d = null;
|
|
106
108
|
}
|
|
107
|
-
return
|
|
108
|
-
sandboxId:
|
|
109
|
-
sessionId:
|
|
109
|
+
return d || (X(e, t, "connecting", "Preparing session..."), d = (await a(D, l, o, r, c)).sessionId, W(e, d)), {
|
|
110
|
+
sandboxId: l,
|
|
111
|
+
sessionId: d
|
|
110
112
|
};
|
|
111
113
|
}, [
|
|
112
|
-
E,
|
|
113
|
-
G,
|
|
114
114
|
D,
|
|
115
|
+
G,
|
|
116
|
+
O,
|
|
115
117
|
W,
|
|
116
118
|
K,
|
|
117
119
|
X
|
|
118
|
-
]), Q =
|
|
120
|
+
]), Q = y(async (e) => {
|
|
119
121
|
if (N) {
|
|
120
122
|
let e = F[N]?.status;
|
|
121
123
|
if (e === "provisioning" || e === "connecting" || e === "streaming") return;
|
|
122
124
|
}
|
|
123
|
-
let
|
|
124
|
-
if (
|
|
125
|
-
let
|
|
126
|
-
accessToken:
|
|
127
|
-
workspaceToken:
|
|
125
|
+
let i = n.getState().mode, a = N, s = a ? n.getState().conversations.find((e) => e.id === a) : void 0;
|
|
126
|
+
if (a ? s && s.mode !== i && (s.messages.length > 0 || s.backendSessionId) ? a = z(void 0, i) : s && s.mode !== i && L(a, i) : a = z(void 0, i), !a) return;
|
|
127
|
+
let u = ae(), f = {
|
|
128
|
+
accessToken: O(),
|
|
129
|
+
workspaceToken: k(),
|
|
128
130
|
userId: j,
|
|
129
|
-
organizationId:
|
|
130
|
-
},
|
|
131
|
-
id:
|
|
131
|
+
organizationId: oe()
|
|
132
|
+
}, p = {
|
|
133
|
+
id: w(),
|
|
132
134
|
role: "user",
|
|
133
135
|
content: e,
|
|
134
136
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
135
137
|
};
|
|
136
|
-
B(
|
|
137
|
-
let
|
|
138
|
-
B(
|
|
139
|
-
id:
|
|
138
|
+
B(a, p);
|
|
139
|
+
let m = w();
|
|
140
|
+
B(a, {
|
|
141
|
+
id: m,
|
|
140
142
|
role: "assistant",
|
|
141
143
|
content: "",
|
|
142
144
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
143
145
|
});
|
|
144
|
-
let
|
|
145
|
-
H(
|
|
146
|
-
content:
|
|
147
|
-
metadata: t?.length ||
|
|
146
|
+
let h = (e, t, n, r) => {
|
|
147
|
+
H(a, m, {
|
|
148
|
+
content: E(e),
|
|
149
|
+
metadata: t?.length || n || r ? {
|
|
148
150
|
artifacts: t,
|
|
149
|
-
mode:
|
|
150
|
-
sessionId:
|
|
151
|
-
sandboxId:
|
|
151
|
+
mode: i,
|
|
152
|
+
sessionId: n,
|
|
153
|
+
sandboxId: r
|
|
152
154
|
} : void 0
|
|
153
155
|
});
|
|
154
|
-
},
|
|
156
|
+
}, g = async (n = 1) => {
|
|
155
157
|
try {
|
|
156
|
-
|
|
157
|
-
X(r, f, "streaming", "Thinking...");
|
|
158
|
-
let d = Date.now();
|
|
159
|
-
await l(E, t, c, a, e, n, ee(), u);
|
|
160
|
-
let m = k([], d), h = Date.now() + 18e4, g = Date.now();
|
|
161
|
-
for (; Date.now() < h && (m = k(await s(E, t, c, a, u, 50), d), H(r, f, { content: T(m.content) }), !m.done);) Date.now() - g > 3e4 && (await i(E, t, c, 600, u).catch(() => void 0), g = Date.now()), await new Promise((e) => setTimeout(e, 1500));
|
|
162
|
-
if (!m.done) throw Error("Assistant response timed out while streaming progress");
|
|
163
|
-
let _ = n === "vibe-plugins" ? await o(E, t, c, a, u).catch(() => []) : void 0;
|
|
164
|
-
p(m.content, _, a, t), m.tokens && H(r, f, { tokens: m.tokens }), U(r, "complete", f, null);
|
|
158
|
+
await r(D, u, f);
|
|
165
159
|
} catch (e) {
|
|
166
|
-
if (t
|
|
160
|
+
if (t(e)) {
|
|
161
|
+
v("ai-credits"), h("You've run out of AI credits for this billing cycle. Upgrade your plan to keep chatting."), U(a, "error", m, null);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
throw e;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
let { sandboxId: t, sessionId: n } = await Z(a, m, i, u, f);
|
|
168
|
+
X(a, m, "streaming", "Thinking...");
|
|
169
|
+
let r = Date.now();
|
|
170
|
+
await ee(D, t, u, n, e, i, te(), f);
|
|
171
|
+
let s = A([], r), p = Date.now() + 18e4, g = Date.now();
|
|
172
|
+
for (; Date.now() < p && (s = A(await l(D, t, u, n, f, 50), r), H(a, m, { content: E(s.content) }), !s.done);) Date.now() - g > 3e4 && (await o(D, t, u, 600, f).catch(() => void 0), g = Date.now()), await new Promise((e) => setTimeout(e, 1500));
|
|
173
|
+
if (!s.done) throw Error("Assistant response timed out while streaming progress");
|
|
174
|
+
let _ = i === "vibe-plugins" ? await c(D, t, u, n, f).catch(() => []) : void 0;
|
|
175
|
+
if (h(s.content, _, n, t), s.tokens) {
|
|
176
|
+
H(a, m, { tokens: s.tokens });
|
|
177
|
+
try {
|
|
178
|
+
await d(D, u, n, s.tokens, f), ne();
|
|
179
|
+
} catch {}
|
|
180
|
+
}
|
|
181
|
+
U(a, "complete", m, null);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
if (n === 1 && se(e)) return G(a), q(i), g(2);
|
|
167
184
|
throw e;
|
|
168
185
|
}
|
|
169
186
|
};
|
|
170
187
|
try {
|
|
171
|
-
if (!
|
|
172
|
-
await
|
|
188
|
+
if (!u) throw Error("Assistant backend is unavailable without an active workspace context.");
|
|
189
|
+
await g();
|
|
173
190
|
} catch (e) {
|
|
174
|
-
|
|
175
|
-
|
|
191
|
+
if (t(e)) {
|
|
192
|
+
v("sandbox"), h("You've reached your sandbox limit on the current plan. Upgrade to start the AI assistant."), U(a, "error", m, null);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
let n = e instanceof Error ? e.message : "Something went wrong";
|
|
196
|
+
U(a, "error", m, n), V(a, m, `Sorry, an error occurred: ${n}. Please try again.`);
|
|
176
197
|
}
|
|
177
198
|
}, [
|
|
178
199
|
N,
|
|
179
200
|
B,
|
|
180
|
-
|
|
201
|
+
D,
|
|
181
202
|
G,
|
|
182
203
|
q,
|
|
183
204
|
M,
|
|
184
205
|
z,
|
|
185
206
|
Z,
|
|
186
|
-
D,
|
|
187
207
|
O,
|
|
208
|
+
k,
|
|
188
209
|
H,
|
|
189
210
|
F,
|
|
190
211
|
W,
|
|
@@ -193,26 +214,26 @@ var j = () => {
|
|
|
193
214
|
X,
|
|
194
215
|
V,
|
|
195
216
|
j
|
|
196
|
-
]),
|
|
217
|
+
]), fe = y((e) => {
|
|
197
218
|
Q(e);
|
|
198
|
-
}, [Q]), $ =
|
|
199
|
-
|
|
219
|
+
}, [Q]), $ = x(Q);
|
|
220
|
+
b(() => {
|
|
200
221
|
$.current = Q;
|
|
201
|
-
}, [Q]),
|
|
202
|
-
let e =
|
|
203
|
-
return e && $.current(e),
|
|
204
|
-
if (e.pendingPrompt && e.pendingPrompt !==
|
|
205
|
-
let e =
|
|
222
|
+
}, [Q]), b(() => {
|
|
223
|
+
let e = n.getState().consumePendingPrompt();
|
|
224
|
+
return e && $.current(e), n.subscribe((e, t) => {
|
|
225
|
+
if (e.pendingPrompt && e.pendingPrompt !== t.pendingPrompt) {
|
|
226
|
+
let e = n.getState().consumePendingPrompt();
|
|
206
227
|
e && $.current(e);
|
|
207
228
|
}
|
|
208
229
|
});
|
|
209
230
|
}, []);
|
|
210
|
-
let
|
|
211
|
-
let
|
|
212
|
-
if (
|
|
213
|
-
if (
|
|
214
|
-
let
|
|
215
|
-
|
|
231
|
+
let pe = y((e) => {
|
|
232
|
+
let t = N ? n.getState().conversations.find((e) => e.id === N) : void 0;
|
|
233
|
+
if (t && (t.messages.length > 0 || t.backendSessionId)) {
|
|
234
|
+
if (t.mode !== e) {
|
|
235
|
+
let t = n.getState().conversations.find((t) => t.mode === e && t.messages.length === 0 && !t.backendSessionId && !n.getState().processingByConversationId[t.id]);
|
|
236
|
+
t ? R(t.id) : z(void 0, e);
|
|
216
237
|
}
|
|
217
238
|
} else N && L(N, e);
|
|
218
239
|
I(e);
|
|
@@ -223,24 +244,24 @@ var j = () => {
|
|
|
223
244
|
L,
|
|
224
245
|
I
|
|
225
246
|
]);
|
|
226
|
-
return /* @__PURE__ */
|
|
247
|
+
return /* @__PURE__ */ C("div", {
|
|
227
248
|
className: "flex flex-col flex-1 overflow-hidden pt-3",
|
|
228
|
-
children: [J.length === 0 ? /* @__PURE__ */
|
|
249
|
+
children: [J.length === 0 ? /* @__PURE__ */ S(_, {
|
|
229
250
|
mode: P,
|
|
230
|
-
onSuggestionClick:
|
|
231
|
-
}) : /* @__PURE__ */
|
|
251
|
+
onSuggestionClick: fe
|
|
252
|
+
}) : /* @__PURE__ */ S(h, {
|
|
232
253
|
messages: J,
|
|
233
|
-
streamingMessageId:
|
|
234
|
-
}), /* @__PURE__ */
|
|
254
|
+
streamingMessageId: ue
|
|
255
|
+
}), /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("div", {
|
|
235
256
|
className: "px-3 pb-1",
|
|
236
|
-
children: /* @__PURE__ */
|
|
257
|
+
children: /* @__PURE__ */ S(g, {
|
|
237
258
|
mode: P,
|
|
238
|
-
onModeChange:
|
|
259
|
+
onModeChange: pe
|
|
239
260
|
})
|
|
240
|
-
}), /* @__PURE__ */
|
|
261
|
+
}), /* @__PURE__ */ S(m, {
|
|
241
262
|
onSend: (e) => void Q(e),
|
|
242
|
-
disabled:
|
|
243
|
-
placeholder:
|
|
263
|
+
disabled: de,
|
|
264
|
+
placeholder: T("vibecontrols.assistant.chatArea.placeholder", `Ask about ${P.replace(/-/g, " ")}...`)
|
|
244
265
|
})] })]
|
|
245
266
|
});
|
|
246
267
|
};
|