@burdenoff/microfe-vibecontrols 2026.830.1 → 2026.905.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/assistant/AIAssistantPanel.js +34 -67
- package/dist/components/assistant/AIAssistantPanel.js.map +1 -1
- package/dist/components/assistant/AssistantChatArea.js +11 -281
- package/dist/components/assistant/AssistantChatInput.js +4 -44
- package/dist/components/assistant/AssistantConversationList.js +6 -155
- package/dist/components/assistant/AssistantExportMenu.js +5 -52
- package/dist/components/assistant/AssistantMessageItem.js +9 -99
- package/dist/components/assistant/AssistantMessageList.js +4 -28
- package/dist/components/assistant/AssistantModeSelector.js +5 -60
- package/dist/components/assistant/AssistantWelcome.js +4 -42
- package/dist/components/assistant/index.js +1 -1
- package/dist/hooks/useAiCreditsQuota.js +8 -8
- package/dist/hooks/useWorkspaceWriteAccess.js +14 -0
- package/dist/hooks/useWorkspaceWriteAccess.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/pages/SessionsPage.js +6 -6
- package/dist/plugin.js +82 -64
- package/dist/plugin.js.map +1 -1
- package/dist/services/assistantApi.js +17 -48
- package/dist/services/assistantApi.js.map +1 -1
- package/dist/utils/assistantPrompt.js +34 -0
- package/dist/utils/assistantPrompt.js.map +1 -0
- package/dist/utils/pageContext.js +34 -15
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +2 -2
- package/dist/components/assistant/AssistantChatArea.js.map +0 -1
- package/dist/components/assistant/AssistantChatInput.js.map +0 -1
- package/dist/components/assistant/AssistantConversationList.js.map +0 -1
- package/dist/components/assistant/AssistantExportMenu.js.map +0 -1
- package/dist/components/assistant/AssistantMessageItem.js.map +0 -1
- package/dist/components/assistant/AssistantMessageList.js.map +0 -1
- package/dist/components/assistant/AssistantModeSelector.js.map +0 -1
- package/dist/components/assistant/AssistantWelcome.js.map +0 -1
- package/dist/hooks/useAssistantHeartbeat.js +0 -40
- package/dist/hooks/useAssistantHeartbeat.js.map +0 -1
- package/dist/hooks/useAssistantKeyboardShortcuts.js +0 -37
- package/dist/hooks/useAssistantKeyboardShortcuts.js.map +0 -1
- package/dist/services/assistantExportService.js +0 -44
- package/dist/services/assistantExportService.js.map +0 -1
package/dist/plugin.js
CHANGED
|
@@ -1,84 +1,102 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AIAssistantPanel as e } from "./components/assistant/AIAssistantPanel.js";
|
|
2
|
+
import "./components/assistant/index.js";
|
|
3
|
+
import { Bot as t, BotMessageSquare as n, Terminal as r, Zap as i } from "lucide-react";
|
|
4
|
+
import { usePanelManagerStore as a } from "@burdenoff/fe-libs/shared/plugins/panels/usePanelManagerStore";
|
|
2
5
|
//#region src/plugin.tsx
|
|
3
|
-
var
|
|
6
|
+
var o = "vibecontrols.assistant", s = 420, c = {
|
|
4
7
|
manifest: {
|
|
5
8
|
id: "vibecontrols",
|
|
6
9
|
name: "VibeControls",
|
|
7
10
|
version: "2026.328.1",
|
|
8
11
|
description: "Development environment management — agents, sessions, tunnels, vibes",
|
|
9
12
|
activationEvents: [{ type: "eager" }],
|
|
10
|
-
contributes: {
|
|
11
|
-
{
|
|
12
|
-
id:
|
|
13
|
+
contributes: {
|
|
14
|
+
panels: [{
|
|
15
|
+
id: o,
|
|
13
16
|
pluginId: "vibecontrols",
|
|
14
|
-
|
|
15
|
-
description: "View all connected agents",
|
|
16
|
-
icon: e,
|
|
17
|
-
keywords: [
|
|
18
|
-
"agents",
|
|
19
|
-
"vibecontrols",
|
|
20
|
-
"dev"
|
|
21
|
-
],
|
|
22
|
-
shortcut: "g a",
|
|
23
|
-
section: "VibeControls",
|
|
24
|
-
execute: () => {}
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: "vc:go-to-vibes",
|
|
28
|
-
pluginId: "vibecontrols",
|
|
29
|
-
label: "Go to Vibes",
|
|
30
|
-
description: "View all vibes (dev environments)",
|
|
31
|
-
icon: r,
|
|
32
|
-
keywords: [
|
|
33
|
-
"vibes",
|
|
34
|
-
"environments",
|
|
35
|
-
"dev"
|
|
36
|
-
],
|
|
37
|
-
shortcut: "g v",
|
|
38
|
-
section: "VibeControls",
|
|
39
|
-
execute: () => {}
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: "vc:go-to-sessions",
|
|
43
|
-
pluginId: "vibecontrols",
|
|
44
|
-
label: "Go to Sessions",
|
|
45
|
-
description: "View terminal sessions",
|
|
17
|
+
title: "AI Assistant",
|
|
46
18
|
icon: n,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
19
|
+
position: "right",
|
|
20
|
+
component: e,
|
|
21
|
+
defaultSize: s,
|
|
22
|
+
minSize: 320,
|
|
23
|
+
maxSize: 720,
|
|
24
|
+
defaultVisible: !1
|
|
25
|
+
}],
|
|
26
|
+
commands: [
|
|
27
|
+
{
|
|
28
|
+
id: "vc:go-to-agents",
|
|
29
|
+
pluginId: "vibecontrols",
|
|
30
|
+
label: "Go to Agents",
|
|
31
|
+
description: "View all connected agents",
|
|
32
|
+
icon: t,
|
|
33
|
+
keywords: [
|
|
34
|
+
"agents",
|
|
35
|
+
"vibecontrols",
|
|
36
|
+
"dev"
|
|
37
|
+
],
|
|
38
|
+
shortcut: "g a",
|
|
39
|
+
section: "VibeControls",
|
|
40
|
+
execute: () => {}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "vc:go-to-vibes",
|
|
44
|
+
pluginId: "vibecontrols",
|
|
45
|
+
label: "Go to Vibes",
|
|
46
|
+
description: "View all vibes (dev environments)",
|
|
47
|
+
icon: i,
|
|
48
|
+
keywords: [
|
|
49
|
+
"vibes",
|
|
50
|
+
"environments",
|
|
51
|
+
"dev"
|
|
52
|
+
],
|
|
53
|
+
shortcut: "g v",
|
|
54
|
+
section: "VibeControls",
|
|
55
|
+
execute: () => {}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "vc:go-to-sessions",
|
|
59
|
+
pluginId: "vibecontrols",
|
|
60
|
+
label: "Go to Sessions",
|
|
61
|
+
description: "View terminal sessions",
|
|
62
|
+
icon: r,
|
|
63
|
+
keywords: [
|
|
64
|
+
"sessions",
|
|
65
|
+
"terminal",
|
|
66
|
+
"shell"
|
|
67
|
+
],
|
|
68
|
+
section: "VibeControls",
|
|
69
|
+
execute: () => {}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "vc:toggle-ai-assistant",
|
|
73
|
+
pluginId: "vibecontrols",
|
|
74
|
+
label: "Toggle AI Assistant",
|
|
75
|
+
description: "Open or close the AI assistant panel",
|
|
76
|
+
icon: n,
|
|
77
|
+
keywords: [
|
|
78
|
+
"ai",
|
|
79
|
+
"assistant",
|
|
80
|
+
"chat",
|
|
81
|
+
"help"
|
|
82
|
+
],
|
|
83
|
+
shortcut: "Ctrl+Shift+A",
|
|
84
|
+
section: "VibeControls",
|
|
85
|
+
execute: () => {}
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
72
89
|
},
|
|
73
90
|
activate(e) {
|
|
74
91
|
let t = e.commands.getByPlugin("vibecontrols");
|
|
75
92
|
for (let n of t) n.id === "vc:go-to-agents" ? n.execute = () => e.shell.navigate("/vibecontrols/agents") : n.id === "vc:go-to-vibes" ? n.execute = () => e.shell.navigate("/vibecontrols/vibes") : n.id === "vc:go-to-sessions" ? n.execute = () => e.shell.navigate("/vibecontrols/sessions") : n.id === "vc:toggle-ai-assistant" && (n.execute = () => {
|
|
76
|
-
|
|
93
|
+
let e = a.getState();
|
|
94
|
+
e.panels[o] || e.initPanel(o, "right", s), a.getState().togglePanel(o);
|
|
77
95
|
});
|
|
78
96
|
e.events.on("context:workspace-changed", () => {});
|
|
79
97
|
}
|
|
80
98
|
};
|
|
81
99
|
//#endregion
|
|
82
|
-
export {
|
|
100
|
+
export { c as vibeControlsPlugin };
|
|
83
101
|
|
|
84
102
|
//# sourceMappingURL=plugin.js.map
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.tsx"],"sourcesContent":["/**\n * VibeControls UI Plugin.\n *\n * Exports the UIPlugin for the VibeControls microfrontend,\n * declaring its contributions to the app shell (navigation, commands,\n * settings, status bar indicators).\n *\n * This is the plugin counterpart to the VibeControlsRoot component.\n * Both can coexist — the Root handles rendering while the plugin\n * handles shell integration.\n *\n * @module @burdenoff/microfe-vibecontrols/plugin\n *\n * @example\n * ```tsx\n * // In vibecontrols-app/src/plugins/index.ts\n * import { vibeControlsPlugin } from '@burdenoff/microfe-vibecontrols/plugin';\n *\n * export const PLUGINS = [vibeControlsPlugin];\n * ```\n */\n\nimport type { UIPlugin } from '@burdenoff/fe-libs/shared/plugins';\nimport { Terminal, Bot, BotMessageSquare, Zap } from 'lucide-react';\n\nexport const vibeControlsPlugin: UIPlugin = {\n manifest: {\n id: 'vibecontrols',\n name: 'VibeControls',\n version: '2026.328.1',\n description: 'Development environment management — agents, sessions, tunnels, vibes',\n activationEvents: [{ type: 'eager' }],\n\n contributes: {\n // Nav items are registered by the app shell (productNav.tsx) and\n // VibeControlsRoot; do not duplicate them here.\n\n // Commands for the command palette (Cmd+K)\n commands: [\n {\n id: 'vc:go-to-agents',\n pluginId: 'vibecontrols',\n label: 'Go to Agents',\n description: 'View all connected agents',\n icon: Bot,\n keywords: ['agents', 'vibecontrols', 'dev'],\n shortcut: 'g a',\n section: 'VibeControls',\n execute: () => {\n // Will be wired by shell navigate\n },\n },\n {\n id: 'vc:go-to-vibes',\n pluginId: 'vibecontrols',\n label: 'Go to Vibes',\n description: 'View all vibes (dev environments)',\n icon: Zap,\n keywords: ['vibes', 'environments', 'dev'],\n shortcut: 'g v',\n section: 'VibeControls',\n execute: () => {},\n },\n {\n id: 'vc:go-to-sessions',\n pluginId: 'vibecontrols',\n label: 'Go to Sessions',\n description: 'View terminal sessions',\n icon: Terminal,\n keywords: ['sessions', 'terminal', 'shell'],\n section: 'VibeControls',\n execute: () => {},\n },\n {\n id: 'vc:toggle-ai-assistant',\n pluginId: 'vibecontrols',\n label: 'Toggle AI Assistant',\n description: 'Open or close the AI assistant panel',\n icon: BotMessageSquare,\n keywords: ['ai', 'assistant', 'chat', 'help'],\n shortcut: 'Ctrl+Shift+A',\n section: 'VibeControls',\n execute: () => {},\n },\n ],\n },\n },\n\n activate(ctx) {\n // Wire command navigation to use shell.navigate\n const commands = ctx.commands.getByPlugin('vibecontrols');\n for (const cmd of commands) {\n if (cmd.id === 'vc:go-to-agents') {\n cmd.execute = () => ctx.shell.navigate('/vibecontrols/agents');\n } else if (cmd.id === 'vc:go-to-vibes') {\n cmd.execute = () => ctx.shell.navigate('/vibecontrols/vibes');\n } else if (cmd.id === 'vc:go-to-sessions') {\n cmd.execute = () => ctx.shell.navigate('/vibecontrols/sessions');\n } else if (cmd.id === 'vc:toggle-ai-assistant') {\n
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.tsx"],"sourcesContent":["/**\n * VibeControls UI Plugin.\n *\n * Exports the UIPlugin for the VibeControls microfrontend,\n * declaring its contributions to the app shell (navigation, commands,\n * settings, status bar indicators).\n *\n * This is the plugin counterpart to the VibeControlsRoot component.\n * Both can coexist — the Root handles rendering while the plugin\n * handles shell integration.\n *\n * @module @burdenoff/microfe-vibecontrols/plugin\n *\n * @example\n * ```tsx\n * // In vibecontrols-app/src/plugins/index.ts\n * import { vibeControlsPlugin } from '@burdenoff/microfe-vibecontrols/plugin';\n *\n * export const PLUGINS = [vibeControlsPlugin];\n * ```\n */\n\nimport type { UIPlugin } from '@burdenoff/fe-libs/shared/plugins';\nimport { Terminal, Bot, BotMessageSquare, Zap } from 'lucide-react';\n// ★★ Import the store from its OWN module, not from `chrome/PanelContainer`.\n//\n// PanelContainer's source re-exports it as `panelManagerStore` and its comment\n// tells consumers to import it from there — but that re-export does NOT survive\n// the published build. fe-libs is bundled with rolldown, which tree-shakes leaf\n// re-exports that nothing inside the package references, so the shipped\n// `dist/chrome/PanelContainer.js` exports `PanelContainer` and nothing else.\n// Importing from there typechecks against source and then fails the app build\n// with MISSING_EXPORT. (The MFE's own transport carries the same warning about\n// tree-shaken leaf re-exports, so this is a known shape of this toolchain.)\n//\n// ★ The warning attached to that re-export — that another path yields a second\n// Zustand instance — does not apply here: `PanelContainer` imports this same\n// module internally, and both specifiers resolve to the one file inside\n// `node_modules/@burdenoff/fe-libs`, so the app's bundle has a single instance.\n// `tsconfig.json` already maps this exact specifier, which is the repo's own\n// signal that it is the sanctioned path.\nimport { usePanelManagerStore } from '@burdenoff/fe-libs/shared/plugins/panels/usePanelManagerStore';\nimport { AIAssistantPanel } from './components/assistant';\n\n/**\n * The assistant's panel id, shared by the contribution and the toggle command.\n *\n * ★ One constant, not two string literals: they must match exactly or the\n * command toggles a panel that was never registered — which fails silently,\n * because every mutator in the panel-manager store returns early on an unknown\n * id rather than throwing.\n */\nconst ASSISTANT_PANEL_ID = 'vibecontrols.assistant';\n\n/** Matches the contribution's `defaultSize`; see the toggle command below. */\nconst ASSISTANT_PANEL_SIZE = 420;\n\nexport const vibeControlsPlugin: UIPlugin = {\n manifest: {\n id: 'vibecontrols',\n name: 'VibeControls',\n version: '2026.328.1',\n description: 'Development environment management — agents, sessions, tunnels, vibes',\n activationEvents: [{ type: 'eager' }],\n\n contributes: {\n // Nav items are registered by the app shell (productNav.tsx) and\n // VibeControlsRoot; do not duplicate them here.\n\n /**\n * The AI assistant, mounted into the shell's right panel.\n *\n * ★★ `component` is VibeControls' OWN wrapper, not fe-libs'\n * `AIAssistantPanel` directly. The shared panel is deliberately exported\n * WITHOUT an `AssistantProductProvider` around it — a provider baked in\n * there could only carry one product's modes and page vocabulary, which is\n * exactly the coupling the shared assistant exists to avoid. Our wrapper\n * supplies the provider, the six seam fields and `writeAccess`. Passing\n * the shared panel here would typecheck (its extra props are optional) and\n * then render with no modes and English copy.\n *\n * ★ `pluginId` is required by the type but IGNORED — the runtime\n * overwrites it with the id it registered the plugin under.\n *\n * ★ `icon` is a component REFERENCE, not an element: `BotMessageSquare`,\n * never `<BotMessageSquare />`. It differs from `NavItem.icon`, which is a\n * ReactNode — a genuine inconsistency in the plugin API, and an easy\n * five-minute mistake.\n *\n * ★ `defaultVisible: false` — the assistant opens on demand. Open state is\n * never persisted (the store's `partialize` strips it), so panels always\n * start closed on reload regardless; this just declines the auto-open.\n */\n panels: [\n {\n id: ASSISTANT_PANEL_ID,\n pluginId: 'vibecontrols',\n title: 'AI Assistant',\n icon: BotMessageSquare,\n position: 'right',\n component: AIAssistantPanel,\n defaultSize: ASSISTANT_PANEL_SIZE,\n minSize: 320,\n maxSize: 720,\n defaultVisible: false,\n },\n ],\n\n // Commands for the command palette (Cmd+K)\n commands: [\n {\n id: 'vc:go-to-agents',\n pluginId: 'vibecontrols',\n label: 'Go to Agents',\n description: 'View all connected agents',\n icon: Bot,\n keywords: ['agents', 'vibecontrols', 'dev'],\n shortcut: 'g a',\n section: 'VibeControls',\n execute: () => {\n // Will be wired by shell navigate\n },\n },\n {\n id: 'vc:go-to-vibes',\n pluginId: 'vibecontrols',\n label: 'Go to Vibes',\n description: 'View all vibes (dev environments)',\n icon: Zap,\n keywords: ['vibes', 'environments', 'dev'],\n shortcut: 'g v',\n section: 'VibeControls',\n execute: () => {},\n },\n {\n id: 'vc:go-to-sessions',\n pluginId: 'vibecontrols',\n label: 'Go to Sessions',\n description: 'View terminal sessions',\n icon: Terminal,\n keywords: ['sessions', 'terminal', 'shell'],\n section: 'VibeControls',\n execute: () => {},\n },\n {\n id: 'vc:toggle-ai-assistant',\n pluginId: 'vibecontrols',\n label: 'Toggle AI Assistant',\n description: 'Open or close the AI assistant panel',\n icon: BotMessageSquare,\n keywords: ['ai', 'assistant', 'chat', 'help'],\n shortcut: 'Ctrl+Shift+A',\n section: 'VibeControls',\n execute: () => {},\n },\n ],\n },\n },\n\n activate(ctx) {\n // Wire command navigation to use shell.navigate\n const commands = ctx.commands.getByPlugin('vibecontrols');\n for (const cmd of commands) {\n if (cmd.id === 'vc:go-to-agents') {\n cmd.execute = () => ctx.shell.navigate('/vibecontrols/agents');\n } else if (cmd.id === 'vc:go-to-vibes') {\n cmd.execute = () => ctx.shell.navigate('/vibecontrols/vibes');\n } else if (cmd.id === 'vc:go-to-sessions') {\n cmd.execute = () => ctx.shell.navigate('/vibecontrols/sessions');\n } else if (cmd.id === 'vc:toggle-ai-assistant') {\n /**\n * ★★ This command was DEAD before the assistant became a panel.\n *\n * It dispatched a `vc:toggle-ai-assistant` DOM event for\n * `AssistantPanelContext` to catch — a file that no longer exists in\n * vibecontrols-app, removed when the legacy overlay was deleted as a\n * duplicate of the widget. Nothing listened, so the command and its\n * `Ctrl+Shift+A` shortcut did nothing at all, and said nothing about it.\n *\n * ★ `togglePanel` returns early on an unknown id rather than throwing,\n * and the only caller of `initPanel` is `PanelContainer`'s mount effect.\n * A shortcut pressed before that effect has run would therefore be\n * another silent no-op — so initialise first if we have to. Harmless\n * when the container already did it: `initPanel` is keyed by id.\n */\n cmd.execute = () => {\n const store = usePanelManagerStore.getState();\n if (!store.panels[ASSISTANT_PANEL_ID]) {\n store.initPanel(ASSISTANT_PANEL_ID, 'right', ASSISTANT_PANEL_SIZE);\n }\n // Re-read: initPanel replaced the state we captured above.\n usePanelManagerStore.getState().togglePanel(ASSISTANT_PANEL_ID);\n };\n }\n }\n\n // Listen for workspace changes to refresh data\n ctx.events.on('context:workspace-changed', () => {\n // Plugin can refresh agent list, session state, etc.\n });\n },\n};\n"],"mappings":";;;;;AAoDA,IAAM,IAAqB,0BAGrB,IAAuB,KAEhB,IAA+B;CAC1C,UAAU;EACR,IAAI;EACJ,MAAM;EACN,SAAS;EACT,aAAa;EACb,kBAAkB,CAAC,EAAE,MAAM,SAAS,CAAC;EAErC,aAAa;GA4BX,QAAQ,CACN;IACE,IAAI;IACJ,UAAU;IACV,OAAO;IACP,MAAM;IACN,UAAU;IACV,WAAW;IACX,aAAa;IACb,SAAS;IACT,SAAS;IACT,gBAAgB;IACjB,CACF;GAGD,UAAU;IACR;KACE,IAAI;KACJ,UAAU;KACV,OAAO;KACP,aAAa;KACb,MAAM;KACN,UAAU;MAAC;MAAU;MAAgB;MAAM;KAC3C,UAAU;KACV,SAAS;KACT,eAAe;KAGhB;IACD;KACE,IAAI;KACJ,UAAU;KACV,OAAO;KACP,aAAa;KACb,MAAM;KACN,UAAU;MAAC;MAAS;MAAgB;MAAM;KAC1C,UAAU;KACV,SAAS;KACT,eAAe;KAChB;IACD;KACE,IAAI;KACJ,UAAU;KACV,OAAO;KACP,aAAa;KACb,MAAM;KACN,UAAU;MAAC;MAAY;MAAY;MAAQ;KAC3C,SAAS;KACT,eAAe;KAChB;IACD;KACE,IAAI;KACJ,UAAU;KACV,OAAO;KACP,aAAa;KACb,MAAM;KACN,UAAU;MAAC;MAAM;MAAa;MAAQ;MAAO;KAC7C,UAAU;KACV,SAAS;KACT,eAAe;KAChB;IACF;GACF;EACF;CAED,SAAS,GAAK;EAEZ,IAAM,IAAW,EAAI,SAAS,YAAY,eAAe;AACzD,OAAK,IAAM,KAAO,EAChB,CAAI,EAAI,OAAO,oBACb,EAAI,gBAAgB,EAAI,MAAM,SAAS,uBAAuB,GACrD,EAAI,OAAO,mBACpB,EAAI,gBAAgB,EAAI,MAAM,SAAS,sBAAsB,GACpD,EAAI,OAAO,sBACpB,EAAI,gBAAgB,EAAI,MAAM,SAAS,yBAAyB,GACvD,EAAI,OAAO,6BAgBpB,EAAI,gBAAgB;GAClB,IAAM,IAAQ,EAAqB,UAAU;AAK7C,GAJK,EAAM,OAAO,MAChB,EAAM,UAAU,GAAoB,SAAS,EAAqB,EAGpE,EAAqB,UAAU,CAAC,YAAY,EAAmB;;AAMrE,IAAI,OAAO,GAAG,mCAAmC,GAE/C;;CAEL"}
|
|
@@ -1,53 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { reportAssistantAiTokenUsage as e } from "@burdenoff/fe-libs/shared/assistant";
|
|
2
2
|
//#region src/services/assistantApi.ts
|
|
3
|
-
var
|
|
4
|
-
function
|
|
5
|
-
return
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return f(t, n, _(r), i);
|
|
19
|
-
}
|
|
20
|
-
function S(e, t, r, i, a) {
|
|
21
|
-
return n(t, r, i, _(a));
|
|
22
|
-
}
|
|
23
|
-
function C(e, t, n, r, i) {
|
|
24
|
-
return c(t, n, r, _(i));
|
|
25
|
-
}
|
|
26
|
-
function w(e, t, n, r, i, a, o, s) {
|
|
27
|
-
return u(t, n, r, i, a, o, _(s));
|
|
28
|
-
}
|
|
29
|
-
function T(e, t, n, r, i, a = 50) {
|
|
30
|
-
return s(t, n, r, _(i), { limit: a });
|
|
31
|
-
}
|
|
32
|
-
function E(t, n, r) {
|
|
33
|
-
return e(n, _(r));
|
|
34
|
-
}
|
|
35
|
-
function D(e, t, n, r, i, a, o) {
|
|
36
|
-
return l(t, n, r, i, a, _(o));
|
|
37
|
-
}
|
|
38
|
-
function O(e, t, n, r, i) {
|
|
39
|
-
return o(t, n, r, _(i)).then((e) => e);
|
|
40
|
-
}
|
|
41
|
-
function k(e, t, n, i, a, o) {
|
|
42
|
-
return r(t, n, i, a, _(o));
|
|
43
|
-
}
|
|
44
|
-
function A(e, t, n, r, i) {
|
|
45
|
-
return d(t, n, r, _(i));
|
|
46
|
-
}
|
|
47
|
-
function j(e, t, n, r = 600, a) {
|
|
48
|
-
return i(t, n, r, _(a));
|
|
3
|
+
var t = "vibecontrols", n = (e) => e ?? {};
|
|
4
|
+
function r(t, r, i, a, o, s, c) {
|
|
5
|
+
return e(r, i, a, o, s, n(c));
|
|
6
|
+
}
|
|
7
|
+
async function i(e) {
|
|
8
|
+
let { workspaceId: t, sessionId: n, messageId: i, model: a, tokens: o } = e;
|
|
9
|
+
if (!i || !a) {
|
|
10
|
+
console.warn("[assistant] skipping AI-credit charge — turn is missing messageId or model", {
|
|
11
|
+
sessionId: n,
|
|
12
|
+
hasMessageId: !!i,
|
|
13
|
+
hasModel: !!a
|
|
14
|
+
});
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
await r(null, t, n, i, a, o);
|
|
49
18
|
}
|
|
50
19
|
//#endregion
|
|
51
|
-
export {
|
|
20
|
+
export { t as PRODUCT, i as reportAssistantTokenUsage };
|
|
52
21
|
|
|
53
22
|
//# sourceMappingURL=assistantApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistantApi.js","names":[],"sources":["../../src/services/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API — thin adapter over @burdenoff/fe-libs/shared/assistant.\n *\n * The legacy slide-in panel (AssistantChatArea / AssistantMessageItem /\n * useAssistantHeartbeat) calls these with the historical (apollo-first)\n * signatures. The real implementation now lives once in fe-libs; this file only\n * maps the old signatures onto it, retiring ~775 lines of duplicated logic. The\n * `apollo` argument is vestigial (the shared layer uses graphqlFetch) and is\n * ignored; the product id + prod-runtime + TTL the shared functions need are\n * supplied here.\n */\n\nimport {\n findExistingSandbox as sharedFindExisting,\n createAssistantSandbox as sharedCreate,\n waitForSandboxRunning as sharedWaitRunning,\n waitForAssistantServiceReady as sharedWaitService,\n createAssistantSession as sharedCreateSession,\n getAssistantSession as sharedGetSession,\n sendAssistantPromptAsync as sharedSendAsync,\n getAssistantMessages as sharedGetMessages,\n extendAssistantSandboxTTL as sharedExtendTtl,\n sendHeartbeat as sharedHeartbeat,\n getAssistantArtifacts as sharedGetArtifacts,\n downloadAssistantArtifact as sharedDownloadArtifact,\n checkAssistantAiCredits as sharedCheckCredits,\n reportAssistantAiTokenUsage as sharedReportUsage,\n} from '@burdenoff/fe-libs/shared/assistant';\nimport type {\n AssistantRawMessage as SharedRawMessage,\n AssistantSandboxAuthContext as SharedAuthContext,\n AssistantSessionInfo as SharedSessionInfo,\n DownloadedAssistantArtifact,\n} from '@burdenoff/fe-libs/shared/assistant';\nimport type { AssistantArtifact as LocalAssistantArtifact, AssistantMode } from '@/types/assistant';\nimport type { PageContext } from '@/utils/pageContext';\n\n// Re-export the shared types under the names the panel already imports.\nexport type AssistantRawMessage = SharedRawMessage;\nexport type AssistantSandboxAuthContext = SharedAuthContext;\nexport type AssistantSessionInfo = SharedSessionInfo;\n\n// Vestigial Apollo arg — accepted for signature compatibility, ignored.\ntype ApolloClientLike = unknown;\n\
|
|
1
|
+
{"version":3,"file":"assistantApi.js","names":[],"sources":["../../src/services/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API — thin adapter over @burdenoff/fe-libs/shared/assistant.\n *\n * The legacy slide-in panel (AssistantChatArea / AssistantMessageItem /\n * useAssistantHeartbeat) calls these with the historical (apollo-first)\n * signatures. The real implementation now lives once in fe-libs; this file only\n * maps the old signatures onto it, retiring ~775 lines of duplicated logic. The\n * `apollo` argument is vestigial (the shared layer uses graphqlFetch) and is\n * ignored; the product id + prod-runtime + TTL the shared functions need are\n * supplied here.\n */\n\nimport {\n findExistingSandbox as sharedFindExisting,\n createAssistantSandbox as sharedCreate,\n waitForSandboxRunning as sharedWaitRunning,\n waitForAssistantServiceReady as sharedWaitService,\n createAssistantSession as sharedCreateSession,\n getAssistantSession as sharedGetSession,\n sendAssistantPromptAsync as sharedSendAsync,\n getAssistantMessages as sharedGetMessages,\n extendAssistantSandboxTTL as sharedExtendTtl,\n sendHeartbeat as sharedHeartbeat,\n getAssistantArtifacts as sharedGetArtifacts,\n downloadAssistantArtifact as sharedDownloadArtifact,\n checkAssistantAiCredits as sharedCheckCredits,\n reportAssistantAiTokenUsage as sharedReportUsage,\n} from '@burdenoff/fe-libs/shared/assistant';\nimport type {\n AssistantRawMessage as SharedRawMessage,\n AssistantSandboxAuthContext as SharedAuthContext,\n AssistantSessionInfo as SharedSessionInfo,\n DownloadedAssistantArtifact,\n} from '@burdenoff/fe-libs/shared/assistant';\nimport type { AssistantArtifact as LocalAssistantArtifact, AssistantMode } from '@/types/assistant';\nimport type { PageContext } from '@/utils/pageContext';\n\n// Re-export the shared types under the names the panel already imports.\nexport type AssistantRawMessage = SharedRawMessage;\nexport type AssistantSandboxAuthContext = SharedAuthContext;\nexport type AssistantSessionInfo = SharedSessionInfo;\n\n// Vestigial Apollo arg — accepted for signature compatibility, ignored.\ntype ApolloClientLike = unknown;\n\nexport const PRODUCT = 'vibecontrols';\n// Matches the original panel behavior: create at 600s, kept warm while open by\n// useAssistantHeartbeat's +600s extend. (The floating widget keeps its own\n// longer-TTL keep-warm via the shared transport.)\nconst DEFAULT_TTL_SECONDS = 600;\n\n// Build-time env parity with the original `assistantApi.ts` image-tag selection\n// (VITE_APP_ENV === 'prod' → prod image tag). Drives getImageTagForMode.\nfunction isProdRuntime(): boolean {\n return import.meta.env.VITE_APP_ENV === 'prod';\n}\n\nconst ctx = (a?: SharedAuthContext): SharedAuthContext => a ?? {};\n\nexport function findExistingSandbox(\n _apollo: ApolloClientLike,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: SharedAuthContext\n): Promise<string | null> {\n return sharedFindExisting(workspaceId, mode, isProdRuntime(), ctx(authContext));\n}\n\nexport function createAssistantSandbox(\n _apollo: ApolloClientLike,\n mode: AssistantMode,\n workspaceId: string,\n authContext?: SharedAuthContext\n): Promise<string> {\n return sharedCreate(\n PRODUCT,\n mode,\n workspaceId,\n isProdRuntime(),\n DEFAULT_TTL_SECONDS,\n ctx(authContext)\n );\n}\n\nexport function waitForSandboxReady(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n authContext?: SharedAuthContext,\n maxWaitMs = 300_000,\n _onProgress?: (message: string) => void\n): Promise<void> {\n return sharedWaitRunning(sandboxId, workspaceId, ctx(authContext), maxWaitMs);\n}\n\nexport function waitForAssistantServiceReady(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n authContext?: SharedAuthContext,\n maxWaitMs = 90_000,\n _onProgress?: (message: string) => void\n): Promise<void> {\n return sharedWaitService(sandboxId, workspaceId, ctx(authContext), maxWaitMs);\n}\n\nexport function createAssistantSession(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: SharedAuthContext\n): Promise<{ sessionId: string }> {\n return sharedCreateSession(sandboxId, workspaceId, mode, ctx(authContext));\n}\n\nexport function getAssistantSession(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: SharedAuthContext\n): Promise<SharedSessionInfo> {\n return sharedGetSession(sandboxId, workspaceId, sessionId, ctx(authContext));\n}\n\nexport function sendAssistantPromptAsync(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: SharedAuthContext\n): Promise<{ messageID?: string }> {\n return sharedSendAsync(\n sandboxId,\n workspaceId,\n sessionId,\n prompt,\n mode,\n pageContext as Record<string, unknown> | undefined,\n ctx(authContext)\n );\n}\n\nexport function getAssistantMessages(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: SharedAuthContext,\n limit = 50\n): Promise<SharedRawMessage[]> {\n return sharedGetMessages(sandboxId, workspaceId, sessionId, ctx(authContext), { limit });\n}\n\nexport function checkAssistantAiCredits(\n _apollo: ApolloClientLike,\n workspaceId: string,\n authContext?: SharedAuthContext\n): Promise<boolean> {\n return sharedCheckCredits(workspaceId, ctx(authContext));\n}\n\nexport function reportAssistantAiTokenUsage(\n _apollo: ApolloClientLike,\n workspaceId: string,\n sessionId: string,\n messageId: string,\n model: string | undefined,\n tokens: { input: number; output: number },\n authContext?: SharedAuthContext\n): Promise<void> {\n return sharedReportUsage(workspaceId, sessionId, messageId, model, tokens, ctx(authContext));\n}\n\n/**\n * Object-shaped adapter for the shared product seam.\n *\n * ★ The seam's `reportTokenUsage` takes ONE object; VibeControls' own reporter\n * is positional and leads with an unused Apollo client. Rather than change a\n * signature the existing chat area still calls, adapt here — the seam gets the\n * shape it declares and nothing else moves.\n *\n * ★★ A missing `messageId` or `model` is logged, not swallowed. This meter\n * exists because assistant spend was invisible; a silently skipped charge would\n * recreate exactly that blind spot, one turn at a time. Metering must never\n * break a turn, so this resolves either way.\n */\nexport async function reportAssistantTokenUsage(usage: {\n workspaceId: string;\n sessionId: string;\n messageId?: string;\n model?: string;\n tokens: { input: number; output: number };\n}): Promise<void> {\n const { workspaceId, sessionId, messageId, model, tokens } = usage;\n if (!messageId || !model) {\n console.warn('[assistant] skipping AI-credit charge — turn is missing messageId or model', {\n sessionId,\n hasMessageId: Boolean(messageId),\n hasModel: Boolean(model),\n });\n return;\n }\n await reportAssistantAiTokenUsage(\n null as never, // the positional Apollo client is unused (`_apollo`)\n workspaceId,\n sessionId,\n messageId,\n model,\n tokens\n );\n}\n\nexport function getAssistantArtifacts(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: SharedAuthContext\n): Promise<LocalAssistantArtifact[]> {\n // The shared surface widens `type` to `string`; at runtime the agent only ever\n // returns 'file' | 'directory', so the array is structurally the local type.\n return sharedGetArtifacts(sandboxId, workspaceId, sessionId, ctx(authContext)).then(\n (artifacts) => artifacts as unknown as LocalAssistantArtifact[]\n );\n}\n\nexport function downloadAssistantArtifact(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n artifactPath: string,\n authContext?: SharedAuthContext\n): Promise<DownloadedAssistantArtifact> {\n return sharedDownloadArtifact(sandboxId, workspaceId, sessionId, artifactPath, ctx(authContext));\n}\n\nexport function sendHeartbeat(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: SharedAuthContext\n): Promise<void> {\n return sharedHeartbeat(sandboxId, workspaceId, sessionId, ctx(authContext));\n}\n\nexport function extendAssistantSandboxTTL(\n _apollo: ApolloClientLike,\n sandboxId: string,\n workspaceId: string,\n additionalSeconds = 600,\n authContext?: SharedAuthContext\n): Promise<void> {\n return sharedExtendTtl(sandboxId, workspaceId, additionalSeconds, ctx(authContext));\n}\n"],"mappings":";;AA6CA,IAAa,IAAU,gBAYjB,KAAO,MAA6C,KAAK,EAAE;AA6GjE,SAAgB,EACd,GACA,GACA,GACA,GACA,GACA,GACA,GACe;AACf,QAAO,EAAkB,GAAa,GAAW,GAAW,GAAO,GAAQ,EAAI,EAAY,CAAC;;AAgB9F,eAAsB,EAA0B,GAM9B;CAChB,IAAM,EAAE,gBAAa,cAAW,cAAW,UAAO,cAAW;AAC7D,KAAI,CAAC,KAAa,CAAC,GAAO;AACxB,UAAQ,KAAK,8EAA8E;GACzF;GACA,cAAc,EAAQ;GACtB,UAAU,EAAQ;GACnB,CAAC;AACF;;AAEF,OAAM,EACJ,MACA,GACA,GACA,GACA,GACA,EACD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/utils/assistantPrompt.ts
|
|
2
|
+
var e = "--- User request ---", t = [
|
|
3
|
+
"You are in api-calls mode for VibeControls and can run GraphQL queries and mutations on the user's behalf.",
|
|
4
|
+
"Before performing ANY write (create, update, delete — any mutation):",
|
|
5
|
+
"1. State exactly what you are about to change (entity, fields, and values).",
|
|
6
|
+
"2. Ask the user to confirm, and WAIT for an explicit \"yes\"/\"confirm\" before executing it.",
|
|
7
|
+
"Read-only queries may run without confirmation. Confirm each write separately; never batch mutations behind a single confirmation.",
|
|
8
|
+
"",
|
|
9
|
+
e
|
|
10
|
+
].join("\n");
|
|
11
|
+
function n(e, t) {
|
|
12
|
+
return [
|
|
13
|
+
t ? `Reply in ${e}. The user has explicitly chosen this language, so use it even if they write to you in another one.` : `Reply in ${e}, matching the language the user wrote in.`,
|
|
14
|
+
`Translate any English documentation you ground your answer in into ${e} rather than switching to English to quote it.`,
|
|
15
|
+
"Keep identifiers, code, CLI flags, API field names and product nouns in their original form — translating them would make them wrong."
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
var r = [
|
|
19
|
+
"This turn was DICTATED, so treat unusual words as probable speech-recognition errors rather than as what the user meant.",
|
|
20
|
+
"Repair obvious mis-hearings of technical terms, product nouns, CLI flags and command names using the surrounding context.",
|
|
21
|
+
"Do NOT guess at a mis-heard identifier, id, quantity, or destination — ask which one the user meant before acting on it."
|
|
22
|
+
].join("\n"), i = ["Your reply will be read aloud. Write it to be heard, not scanned:", "short sentences, no markdown syntax, no tables, and no code blocks unless the user asked for code."].join("\n");
|
|
23
|
+
function a(a, o, s, c = !1, l = !1) {
|
|
24
|
+
let u = [
|
|
25
|
+
s ? n(s, c) : "",
|
|
26
|
+
l ? r : "",
|
|
27
|
+
l ? i : ""
|
|
28
|
+
].filter(Boolean).join("\n\n"), d = u ? `${u}\n\n` : "";
|
|
29
|
+
return a === "api-calls" ? `${d}${t}\n${o}` : d ? `${d}${e}\n${o}` : o;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { a as buildPromptForMode };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=assistantPrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistantPrompt.js","names":[],"sources":["../../src/utils/assistantPrompt.ts"],"sourcesContent":["/**\n * Soft confirm-before-mutate guardrail for the assistant's api-calls (write)\n * mode, plus the language and dictation instructions.\n *\n * The in-sandbox agent executes mutations autonomously — there is no hard\n * server-side confirmation — so we prepend a describe-and-confirm instruction to\n * the user's TURN, not to the session system prompt. Sending it as `system`\n * would REPLACE the whole mode prompt and silently delete the agent's api-calls\n * behaviour along with it.\n *\n * This is ADVISORY. The dependable guard is the host-side consent dialog plus\n * the admin gate.\n */\n\nimport type { AssistantMode } from '@/types/assistant';\n\n/**\n * Marks where our instructions stop and the user's words begin. Exactly one may\n * exist in a prompt, and it must be last — see `buildPromptForMode`.\n */\nexport const USER_REQUEST_DELIMITER = '--- User request ---';\n\nexport const API_CALLS_CONFIRM_PREAMBLE = [\n \"You are in api-calls mode for VibeControls and can run GraphQL queries and mutations on the user's behalf.\",\n 'Before performing ANY write (create, update, delete — any mutation):',\n '1. State exactly what you are about to change (entity, fields, and values).',\n '2. Ask the user to confirm, and WAIT for an explicit \"yes\"/\"confirm\" before executing it.',\n 'Read-only queries may run without confirmation. Confirm each write separately; never batch mutations behind a single confirmation.',\n '',\n USER_REQUEST_DELIMITER,\n].join('\\n');\n\n/**\n * Ask for the reply in the user's language.\n *\n * ★ Nothing else does. The sandbox system prompt carries no language\n * instruction and the grounding documentation is entirely in English, so every\n * force in the session pushes the answer towards English regardless of what the\n * user wrote. The model's tendency to mirror its input is the only thing\n * resisting that, and it loses.\n *\n * The second clause is the one that matters: **translate the docs, do not\n * switch to their language.** Without it a grounded answer drifts back to\n * English mid-reply simply because the source material is English.\n */\nfunction buildLanguagePreamble(languageName: string, isExplicitChoice: boolean): string {\n return [\n isExplicitChoice\n ? `Reply in ${languageName}. The user has explicitly chosen this language, so use it even if they write to you in another one.`\n : `Reply in ${languageName}, matching the language the user wrote in.`,\n `Translate any English documentation you ground your answer in into ${languageName} rather than switching to English to quote it.`,\n 'Keep identifiers, code, CLI flags, API field names and product nouns in their original form — translating them would make them wrong.',\n ].join('\\n');\n}\n\n/**\n * Told to the model when the turn was SPOKEN rather than typed.\n *\n * ★ Speech recognition does not fail uniformly — it fails on specific words,\n * and in VibeControls the ones it fails on are predictable: identifiers, CLI\n * flags, and the product's own nouns. A recogniser has no model for \"vibe\",\n * \"tunnel\", or a session id read aloud, so a sentence comes back mostly right\n * with the load-bearing words wrong.\n *\n * The model is far better placed to repair that than the recogniser is, because\n * it has the product context — but only if it is TOLD the text was dictated.\n * Otherwise it answers the garbled token literally, which is what makes the\n * assistant look like it misunderstood.\n *\n * ★ The refusal to guess is the important half. Charitable interpretation is\n * right for a mangled verb and wrong for a mangled IDENTIFIER: acting on a\n * misheard session or agent id operates on someone else's resource. One extra\n * question costs a few seconds.\n */\nconst DICTATION_PREAMBLE = [\n 'This turn was DICTATED, so treat unusual words as probable speech-recognition errors rather than as what the user meant.',\n 'Repair obvious mis-hearings of technical terms, product nouns, CLI flags and command names using the surrounding context.',\n 'Do NOT guess at a mis-heard identifier, id, quantity, or destination — ask which one the user meant before acting on it.',\n].join('\\n');\n\n/** A dictated turn is a SPOKEN conversation, so the reply is spoken too. */\nconst VOICE_REPLY_PREAMBLE = [\n 'Your reply will be read aloud. Write it to be heard, not scanned:',\n 'short sentences, no markdown syntax, no tables, and no code blocks unless the user asked for code.',\n].join('\\n');\n\n/**\n * Compose the turn actually sent to the agent.\n *\n * Ordering is deliberate. Dictation goes AFTER the language lines and before the\n * delimiter: it is an instruction about the user's text, so it belongs with the\n * instructions rather than inside them.\n *\n * ★ `USER_REQUEST_DELIMITER` is emitted exactly once, here. The api-calls\n * preamble ends with its own copy, so it must not be added again — a second one\n * mid-prompt leaves the safety instructions sitting *after* a \"User request\"\n * marker, i.e. presented to the model as though the user had written them.\n */\nexport function buildPromptForMode(\n mode: AssistantMode,\n content: string,\n languageName?: string,\n isExplicitChoice = false,\n dictated = false\n): string {\n const preamble = [\n languageName ? buildLanguagePreamble(languageName, isExplicitChoice) : '',\n dictated ? DICTATION_PREAMBLE : '',\n dictated ? VOICE_REPLY_PREAMBLE : '',\n ]\n .filter(Boolean)\n .join('\\n\\n');\n const language = preamble ? `${preamble}\\n\\n` : '';\n if (mode === 'api-calls') return `${language}${API_CALLS_CONFIRM_PREAMBLE}\\n${content}`;\n return language ? `${language}${USER_REQUEST_DELIMITER}\\n${content}` : content;\n}\n"],"mappings":";AAoBA,IAAa,IAAyB,wBAEzB,IAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAeZ,SAAS,EAAsB,GAAsB,GAAmC;AACtF,QAAO;EACL,IACI,YAAY,EAAa,uGACzB,YAAY,EAAa;EAC7B,sEAAsE,EAAa;EACnF;EACD,CAAC,KAAK,KAAK;;AAsBd,IAAM,IAAqB;CACzB;CACA;CACA;CACD,CAAC,KAAK,KAAK,EAGN,IAAuB,CAC3B,qEACA,qGACD,CAAC,KAAK,KAAK;AAcZ,SAAgB,EACd,GACA,GACA,GACA,IAAmB,IACnB,IAAW,IACH;CACR,IAAM,IAAW;EACf,IAAe,EAAsB,GAAc,EAAiB,GAAG;EACvE,IAAW,IAAqB;EAChC,IAAW,IAAuB;EACnC,CACE,OAAO,QAAQ,CACf,KAAK,OAAO,EACT,IAAW,IAAW,GAAG,EAAS,QAAQ;AAEhD,QADI,MAAS,cAAoB,GAAG,IAAW,EAA2B,IAAI,MACvE,IAAW,GAAG,IAAW,EAAuB,IAAI,MAAY"}
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
//#region src/utils/pageContext.ts
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
var e = {
|
|
3
|
+
home: "Home",
|
|
4
|
+
agents: "Agents",
|
|
5
|
+
sessions: "Sessions",
|
|
6
|
+
vibes: "Vibes",
|
|
7
|
+
notes: "Notes",
|
|
8
|
+
catalogs: "Catalogs",
|
|
9
|
+
components: "Components",
|
|
10
|
+
templates: "Templates",
|
|
11
|
+
tunnels: "Tunnels",
|
|
12
|
+
calendar: "Calendar",
|
|
13
|
+
ai: "AI",
|
|
14
|
+
settings: "Settings",
|
|
15
|
+
plugins: "Plugins"
|
|
16
|
+
};
|
|
17
|
+
function t() {
|
|
18
|
+
let { currentPage: t } = n(), r = t.split("/").filter(Boolean), i = (r[0] === "vibecontrols" ? r[1] : r[0]) ?? "";
|
|
19
|
+
return !i || i === "home" ? e.home : e[i] ?? i.split("-").map((e) => e && e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
|
|
20
|
+
}
|
|
21
|
+
function n() {
|
|
22
|
+
let e = window.location.href, t = window.location.pathname.split("/").filter(Boolean).slice(0, 3).join("/") || "home", n = i(window.location.pathname, window.location.search);
|
|
4
23
|
return {
|
|
5
24
|
currentUrl: e,
|
|
6
25
|
currentPage: t,
|
|
7
|
-
entityType:
|
|
8
|
-
entityId:
|
|
26
|
+
entityType: n.entityType,
|
|
27
|
+
entityId: n.entityId,
|
|
9
28
|
platform: "vibecontrols",
|
|
10
29
|
version: "2026.402.1",
|
|
11
30
|
viewportSize: `${window.innerWidth}x${window.innerHeight}`,
|
|
@@ -13,12 +32,12 @@ function e() {
|
|
|
13
32
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
14
33
|
};
|
|
15
34
|
}
|
|
16
|
-
function
|
|
35
|
+
function r(e, t) {
|
|
17
36
|
return t.match(e);
|
|
18
37
|
}
|
|
19
|
-
function
|
|
20
|
-
let
|
|
21
|
-
for (let [
|
|
38
|
+
function i(e, t) {
|
|
39
|
+
let n = new URLSearchParams(t);
|
|
40
|
+
for (let [t, n] of [
|
|
22
41
|
[/^\/vibecontrols\/agents\/([^/]+)$/, "VIBECONTROLS_AGENT"],
|
|
23
42
|
[/^\/vibecontrols\/sessions\/([^/]+)$/, "VIBECONTROLS_SESSION"],
|
|
24
43
|
[/^\/vibecontrols\/vibes\/([^/]+)$/, "VIBECONTROLS_VIBE"],
|
|
@@ -30,11 +49,11 @@ function n(e, n) {
|
|
|
30
49
|
[/^\/vibecontrols\/calendar\/task\/([^/]+)$/, "VIBECONTROLS_TASK"],
|
|
31
50
|
[/^\/vibecontrols\/ai\/sessions\/([^/]+)$/, "VIBECONTROLS_AI_SESSION"]
|
|
32
51
|
]) {
|
|
33
|
-
let i = t
|
|
52
|
+
let i = r(t, e);
|
|
34
53
|
if (i) return {
|
|
35
|
-
entityType:
|
|
54
|
+
entityType: n,
|
|
36
55
|
entityId: i[1],
|
|
37
|
-
metadata: { routePattern:
|
|
56
|
+
metadata: { routePattern: t.source }
|
|
38
57
|
};
|
|
39
58
|
}
|
|
40
59
|
if (/^\/vibecontrols\/vibes\/[^/]+\/docs\//.test(e)) return {
|
|
@@ -49,7 +68,7 @@ function n(e, n) {
|
|
|
49
68
|
entityType: "VIBECONTROLS_DOCSPAGE",
|
|
50
69
|
entityId: e.match(/^\/vibecontrols\/components\/([^/]+)\/docs\//)?.[1]
|
|
51
70
|
};
|
|
52
|
-
let i =
|
|
71
|
+
let i = n.get("tab") ?? (() => {
|
|
53
72
|
let t = e.split("/").filter(Boolean);
|
|
54
73
|
return t.length > 2 ? t[t.length - 1] : void 0;
|
|
55
74
|
})();
|
|
@@ -57,12 +76,12 @@ function n(e, n) {
|
|
|
57
76
|
activeTab: i ?? void 0,
|
|
58
77
|
metadata: {
|
|
59
78
|
...i ? { activeTab: i } : {},
|
|
60
|
-
...
|
|
61
|
-
...
|
|
79
|
+
...n.get("selectedTargetId") ? { selectedTargetId: n.get("selectedTargetId") } : {},
|
|
80
|
+
...n.get("actionId") ? { actionId: n.get("actionId") } : {}
|
|
62
81
|
}
|
|
63
82
|
};
|
|
64
83
|
}
|
|
65
84
|
//#endregion
|
|
66
|
-
export {
|
|
85
|
+
export { t as describeCurrentPage, n as gatherPageContext };
|
|
67
86
|
|
|
68
87
|
//# sourceMappingURL=pageContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageContext.js","names":[],"sources":["../../src/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering — captures the user's current browser context\n * to be sent alongside every assistant prompt for better AI awareness.\n */\n\ninterface ConversationContextShape {\n entityType?: string;\n entityId?: string;\n activeTab?: string;\n metadata?: Record<string, unknown>;\n}\n\
|
|
1
|
+
{"version":3,"file":"pageContext.js","names":[],"sources":["../../src/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering — captures the user's current browser context\n * to be sent alongside every assistant prompt for better AI awareness.\n */\n\ninterface ConversationContextShape {\n entityType?: string;\n entityId?: string;\n activeTab?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * ★ A `type` alias, deliberately, not an `interface`.\n *\n * fe-libs' `PageContext` carries an index signature (`[key: string]: unknown`)\n * so products can add their own keys. TypeScript grants a type alias an\n * IMPLICIT index signature and an interface none, so declaring this as an\n * interface makes it un-assignable to the seam's `gatherPageContext` — with an\n * error that talks about index signatures rather than about the seam.\n * HealthyBowl's equivalent is a type alias for exactly this reason.\n */\nexport type PageContext = {\n // `currentUrl` (NOT `activeUrl`) — must match the agent's allowed-context-key\n // contract, otherwise the agent filters it out.\n currentUrl: string;\n currentPage: string;\n // The entity the user is currently looking at, resolved from the route. Lets\n // the assistant act on \"this / current / here\" without asking for an id.\n entityType?: string;\n entityId?: string;\n platform: string;\n version: string;\n viewportSize: string;\n userAgent: string;\n timestamp: string;\n};\n\n/**\n * Human-readable names for VibeControls' top-level routes.\n *\n * ★ This is the label a USER would use for the page, not the route segment. It\n * reaches the model as \"the user is on the Sessions page\", so `sessions` →\n * `Sessions` is fine but `ai` → `AI` matters: the fallback title-caser would\n * produce \"Ai\", and an assistant that calls it that sounds like it has never\n * seen the product.\n *\n * Unlisted segments fall through to the title-caser, so a new route degrades to\n * something reasonable rather than to nothing.\n */\nconst PAGE_LABELS: Record<string, string> = {\n home: 'Home',\n agents: 'Agents',\n sessions: 'Sessions',\n vibes: 'Vibes',\n notes: 'Notes',\n catalogs: 'Catalogs',\n components: 'Components',\n templates: 'Templates',\n tunnels: 'Tunnels',\n calendar: 'Calendar',\n ai: 'AI',\n settings: 'Settings',\n plugins: 'Plugins',\n};\n\n/**\n * What to CALL the page the user is on.\n *\n * Feeds the assistant's empty state (\"Ask about Sessions…\") and the shared\n * panel's live page label. Distinct from `gatherPageContext`, which produces\n * machine-readable context: this one is read by a human.\n */\nexport function describeCurrentPage(): string {\n const { currentPage } = gatherPageContext();\n // `currentPage` is up to three path segments; the product mount segment is\n // first when present, so skip it rather than labelling every page \"Vibecontrols\".\n const segments = currentPage.split('/').filter(Boolean);\n const first = (segments[0] === 'vibecontrols' ? segments[1] : segments[0]) ?? '';\n if (!first || first === 'home') return PAGE_LABELS.home;\n return (\n PAGE_LABELS[first] ??\n first\n .split('-')\n .map((word) => (word ? word.charAt(0).toUpperCase() + word.slice(1) : word))\n .join(' ')\n );\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const entity = resolveVibeControlsConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n entityType: entity.entityType,\n entityId: entity.entityId,\n platform: 'vibecontrols',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.402.1',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n userAgent: navigator.userAgent,\n timestamp: new Date().toISOString(),\n };\n}\n\nfunction matchPath(pattern: RegExp, pathname: string): RegExpMatchArray | null {\n return pathname.match(pattern);\n}\n\nexport function resolveVibeControlsConversationContext(\n pathname: string,\n search: string\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n const directMatches: Array<[RegExp, string]> = [\n [/^\\/vibecontrols\\/agents\\/([^/]+)$/, 'VIBECONTROLS_AGENT'],\n [/^\\/vibecontrols\\/sessions\\/([^/]+)$/, 'VIBECONTROLS_SESSION'],\n [/^\\/vibecontrols\\/vibes\\/([^/]+)$/, 'VIBECONTROLS_VIBE'],\n [/^\\/vibecontrols\\/notes\\/([^/]+)$/, 'VIBECONTROLS_NOTE'],\n [/^\\/vibecontrols\\/catalogs\\/([^/]+)$/, 'VIBECONTROLS_CATALOG'],\n [/^\\/vibecontrols\\/components\\/([^/]+)$/, 'VIBECONTROLS_COMPONENT'],\n [/^\\/vibecontrols\\/templates\\/([^/]+)$/, 'VIBECONTROLS_TEMPLATE'],\n [/^\\/vibecontrols\\/tunnels\\/([^/]+)$/, 'VIBECONTROLS_TUNNEL'],\n [/^\\/vibecontrols\\/calendar\\/task\\/([^/]+)$/, 'VIBECONTROLS_TASK'],\n [/^\\/vibecontrols\\/ai\\/sessions\\/([^/]+)$/, 'VIBECONTROLS_AI_SESSION'],\n ];\n\n for (const [pattern, entityType] of directMatches) {\n const match = matchPath(pattern, pathname);\n if (match) {\n return {\n entityType,\n entityId: match[1],\n metadata: {\n routePattern: pattern.source,\n },\n };\n }\n }\n\n if (/^\\/vibecontrols\\/vibes\\/[^/]+\\/docs\\//.test(pathname)) {\n const match = pathname.match(/^\\/vibecontrols\\/vibes\\/([^/]+)\\/docs\\//);\n return {\n entityType: 'VIBECONTROLS_DOCSSITE',\n entityId: match?.[1],\n };\n }\n\n if (/^\\/vibecontrols\\/catalogs\\/[^/]+\\/docs\\//.test(pathname)) {\n const match = pathname.match(/^\\/vibecontrols\\/catalogs\\/([^/]+)\\/docs\\//);\n return {\n entityType: 'VIBECONTROLS_DOCSPAGE',\n entityId: match?.[1],\n };\n }\n\n if (/^\\/vibecontrols\\/components\\/[^/]+\\/docs\\//.test(pathname)) {\n const match = pathname.match(/^\\/vibecontrols\\/components\\/([^/]+)\\/docs\\//);\n return {\n entityType: 'VIBECONTROLS_DOCSPAGE',\n entityId: match?.[1],\n };\n }\n\n const activeTab =\n params.get('tab') ??\n (() => {\n const segments = pathname.split('/').filter(Boolean);\n return segments.length > 2 ? segments[segments.length - 1] : undefined;\n })();\n\n return {\n activeTab: activeTab ?? undefined,\n metadata: {\n ...(activeTab ? { activeTab } : {}),\n ...(params.get('selectedTargetId')\n ? { selectedTargetId: params.get('selectedTargetId') }\n : {}),\n ...(params.get('actionId') ? { actionId: params.get('actionId') } : {}),\n },\n };\n}\n"],"mappings":";AAkDA,IAAM,IAAsC;CAC1C,MAAM;CACN,QAAQ;CACR,UAAU;CACV,OAAO;CACP,OAAO;CACP,UAAU;CACV,YAAY;CACZ,WAAW;CACX,SAAS;CACT,UAAU;CACV,IAAI;CACJ,UAAU;CACV,SAAS;CACV;AASD,SAAgB,IAA8B;CAC5C,IAAM,EAAE,mBAAgB,GAAmB,EAGrC,IAAW,EAAY,MAAM,IAAI,CAAC,OAAO,QAAQ,EACjD,KAAS,EAAS,OAAO,iBAAiB,EAAS,KAAK,EAAS,OAAO;AAE9E,QADI,CAAC,KAAS,MAAU,SAAe,EAAY,OAEjD,EAAY,MACZ,EACG,MAAM,IAAI,CACV,KAAK,MAAU,KAAO,EAAK,OAAO,EAAE,CAAC,aAAa,GAAG,EAAK,MAAM,EAAE,CAAS,CAC3E,KAAK,IAAI;;AAIhB,SAAgB,IAAiC;CAC/C,IAAM,IAAM,OAAO,SAAS,MAEtB,IADe,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ,CACvC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,IAAI,QACpD,IAAS,EACb,OAAO,SAAS,UAChB,OAAO,SAAS,OACjB;AAED,QAAO;EACL,YAAY;EACZ;EACA,YAAY,EAAO;EACnB,UAAU,EAAO;EACjB,UAAU;EACV,SAA6C;EAC7C,cAAc,GAAG,OAAO,WAAW,GAAG,OAAO;EAC7C,WAAW,UAAU;EACrB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACpC;;AAGH,SAAS,EAAU,GAAiB,GAA2C;AAC7E,QAAO,EAAS,MAAM,EAAQ;;AAGhC,SAAgB,EACd,GACA,GAC0B;CAC1B,IAAM,IAAS,IAAI,gBAAgB,EAAO;AAc1C,MAAK,IAAM,CAAC,GAAS,MAb0B;EAC7C,CAAC,qCAAqC,qBAAqB;EAC3D,CAAC,uCAAuC,uBAAuB;EAC/D,CAAC,oCAAoC,oBAAoB;EACzD,CAAC,oCAAoC,oBAAoB;EACzD,CAAC,uCAAuC,uBAAuB;EAC/D,CAAC,yCAAyC,yBAAyB;EACnE,CAAC,wCAAwC,wBAAwB;EACjE,CAAC,sCAAsC,sBAAsB;EAC7D,CAAC,6CAA6C,oBAAoB;EAClE,CAAC,2CAA2C,0BAA0B;EACvE,EAEkD;EACjD,IAAM,IAAQ,EAAU,GAAS,EAAS;AAC1C,MAAI,EACF,QAAO;GACL;GACA,UAAU,EAAM;GAChB,UAAU,EACR,cAAc,EAAQ,QACvB;GACF;;AAIL,KAAI,wCAAwC,KAAK,EAAS,CAExD,QAAO;EACL,YAAY;EACZ,UAHY,EAAS,MAAM,0CAA0C,GAGnD;EACnB;AAGH,KAAI,2CAA2C,KAAK,EAAS,CAE3D,QAAO;EACL,YAAY;EACZ,UAHY,EAAS,MAAM,6CAA6C,GAGtD;EACnB;AAGH,KAAI,6CAA6C,KAAK,EAAS,CAE7D,QAAO;EACL,YAAY;EACZ,UAHY,EAAS,MAAM,+CAA+C,GAGxD;EACnB;CAGH,IAAM,IACJ,EAAO,IAAI,MAAM,WACV;EACL,IAAM,IAAW,EAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;AACpD,SAAO,EAAS,SAAS,IAAI,EAAS,EAAS,SAAS,KAAK,KAAA;KAC3D;AAEN,QAAO;EACL,WAAW,KAAa,KAAA;EACxB,UAAU;GACR,GAAI,IAAY,EAAE,cAAW,GAAG,EAAE;GAClC,GAAI,EAAO,IAAI,mBAAmB,GAC9B,EAAE,kBAAkB,EAAO,IAAI,mBAAmB,EAAE,GACpD,EAAE;GACN,GAAI,EAAO,IAAI,WAAW,GAAG,EAAE,UAAU,EAAO,IAAI,WAAW,EAAE,GAAG,EAAE;GACvE;EACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-vibecontrols",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.905.2",
|
|
4
4
|
"description": "VibeControls microfrontend for Burdenoff products",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"license": "PROPRIETARY",
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@apollo/client": "4.1.9",
|
|
65
|
-
"@burdenoff/fe-libs": "2026.
|
|
65
|
+
"@burdenoff/fe-libs": "2026.905.5",
|
|
66
66
|
"@tanstack/react-query": "^5.90.16",
|
|
67
67
|
"@xyflow/react": "^12.10.2",
|
|
68
68
|
"clsx": "^2.1.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantChatArea.js","names":[],"sources":["../../../src/components/assistant/AssistantChatArea.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, type FC } from 'react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AssistantChatInput } from './AssistantChatInput';\nimport { AssistantMessageList } from './AssistantMessageList';\nimport { AssistantModeSelector } from './AssistantModeSelector';\nimport { AssistantWelcome } from './AssistantWelcome';\nimport { useAssistantStore } from '@/store/assistantStore';\nimport {\n checkAssistantAiCredits,\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantArtifacts,\n getAssistantMessages,\n getAssistantSession,\n reportAssistantAiTokenUsage,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n type AssistantRawMessage,\n type AssistantSandboxAuthContext,\n} from '@/services/assistantApi';\nimport { gatherPageContext } from '@/utils/pageContext';\nimport { dispatchAssistantQuotaExhausted, notifyAiCreditsUpdated } from '@/utils/aiCredits';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type {\n AssistantArtifact,\n AssistantConversationStatus,\n AssistantMode,\n Message,\n} from '@/types/assistant';\n\n// How long the client streams progress before giving up on a turn. Chat replies\n// (general / api-calls) finish in seconds; a vibe-plugins build is a multi-step\n// pipeline (read docs → scaffold → bun install → bun build → checklist) that runs\n// for minutes, so it gets a much larger budget. The sandbox TTL is auto-extended\n// every 30s during the poll loop, so a longer budget never outlives the sandbox.\nconst STREAM_BUDGET_MS: Record<AssistantMode, number> = {\n assistant: 180_000,\n general: 180_000,\n 'api-calls': 180_000,\n 'vibe-plugins': 600_000,\n};\nconst DEFAULT_STREAM_BUDGET_MS = 180_000;\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId'\n ? 'burdenoff-active-context-workspace'\n : 'burdenoff-active-context-organization';\n const activeContextValue = localStorage.getItem(activeContextKey);\n if (activeContextValue) return activeContextValue;\n\n const activeProfileId = sessionStorage.getItem('bf-active-profile');\n if (!activeProfileId) return '';\n const raw = localStorage.getItem(`bf-p-${activeProfileId}-context`);\n if (!raw) return '';\n const context = JSON.parse(raw) as {\n workspaceId?: string;\n organizationId?: string;\n };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId');\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\nfunction isRecoverableAssistantError(error: unknown): boolean {\n const message =\n error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n\n if (\n message.includes('rate limit exceeded') ||\n message.includes('unauthorized') ||\n message.includes('k8s api error 401')\n ) {\n return false;\n }\n\n return [\n 'sandbox not found',\n 'sandbox is not running',\n 'sandbox service not available yet',\n 'sandbox failed',\n 'sandbox startup timed out',\n 'assistant service did not become healthy',\n 'proxy error: 404',\n 'proxy error: 502',\n 'proxy error: 503',\n 'session mode mismatch',\n ].some((fragment) => message.includes(fragment));\n}\n\nfunction sanitizeAssistantResponse(response: string): string {\n return response\n .replace(/(Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/(X-Workspace-Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9._-]+\\.[A-Za-z0-9._-]+\\b/g, '[REDACTED_JWT]')\n .replace(/\\bsk-ant-[A-Za-z0-9-]+\\b/g, '[REDACTED_API_KEY]');\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n return message.info?.time?.created ?? 0;\n}\n\nfunction getAssistantText(parts: AssistantRawMessage['parts']): string {\n return (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction getToolProgress(parts: AssistantRawMessage['parts']): string[] {\n return (parts ?? [])\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const tool = part.tool ?? 'tool';\n if (status === 'completed') return `Completed: ${tool}`;\n if (status === 'failed') return `Failed: ${tool}`;\n return `Running: ${tool}`;\n });\n}\n\nfunction buildAssistantProgress(\n messages: AssistantRawMessage[],\n sinceMs: number\n): {\n content: string;\n done: boolean;\n tokens?: { input: number; output: number };\n model?: string;\n} {\n const relevant = messages\n .filter(\n (message) => message.info?.role === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs\n )\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n if (relevant.length === 0) {\n return { content: 'Thinking...', done: false };\n }\n\n const latest = relevant[relevant.length - 1]!;\n const text = getAssistantText(latest.parts);\n const toolProgress = getToolProgress(latest.parts);\n const content = [text, ...toolProgress].filter(Boolean).join('\\n\\n') || 'Thinking...';\n\n return {\n content,\n done: Boolean(latest.info?.time?.completed) && (Boolean(text) || toolProgress.length > 0),\n tokens: latest.info?.tokens,\n // \"provider/model\" id for this turn — drives the cost-weighted credit charge.\n model:\n latest.info?.providerID && latest.info?.modelID\n ? `${latest.info.providerID}/${latest.info.modelID}`\n : undefined,\n };\n}\n\nfunction getConversationStatusMessage(status: AssistantConversationStatus): string {\n switch (status) {\n case 'provisioning':\n return 'Starting assistant environment...';\n case 'connecting':\n return 'Connecting to assistant...';\n case 'streaming':\n return 'Thinking...';\n default:\n return '';\n }\n}\n\nexport const AssistantChatArea: FC = () => {\n const tr = useTr();\n const apollo = useGatewayClient('workspace');\n const { getAccessToken, getWorkspaceToken, userId } = useAuthToken();\n\n const {\n conversations,\n activeConversationId,\n mode,\n processingByConversationId,\n setMode,\n setConversationMode,\n setActiveConversation,\n createConversation,\n addMessage,\n updateMessage,\n patchMessage,\n setConversationProcessing,\n clearConversationProcessing,\n setBackendSessionId,\n clearBackendSessionId,\n setSandbox,\n clearSandbox,\n } = useAssistantStore();\n\n const activeConversation = conversations.find(\n (conversation) => conversation.id === activeConversationId\n );\n const messages = activeConversation?.messages ?? [];\n const activeProcessing = activeConversationId\n ? processingByConversationId[activeConversationId]\n : undefined;\n const activeStreamingMessageId = activeProcessing?.messageId ?? null;\n const activeIsProcessing =\n activeProcessing?.status === 'provisioning' ||\n activeProcessing?.status === 'connecting' ||\n activeProcessing?.status === 'streaming';\n\n const updateConversationStage = useCallback(\n (\n conversationId: string,\n assistantMessageId: string,\n status: AssistantConversationStatus,\n content?: string\n ) => {\n setConversationProcessing(conversationId, status, assistantMessageId, null);\n if (content) {\n updateMessage(conversationId, assistantMessageId, content);\n } else {\n updateMessage(conversationId, assistantMessageId, getConversationStatusMessage(status));\n }\n },\n [setConversationProcessing, updateMessage]\n );\n\n const ensureAssistantRuntime = useCallback(\n async (\n conversationId: string,\n assistantMessageId: string,\n currentMode: AssistantMode,\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let currentSandboxId =\n useAssistantStore.getState().sandboxesByMode[currentMode]?.sandboxId ?? null;\n\n if (!currentSandboxId) {\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Looking for existing environment...'\n );\n const existingId = await findExistingSandbox(apollo, workspaceId, currentMode, authContext);\n\n if (existingId) {\n currentSandboxId = existingId;\n setSandbox(currentMode, currentSandboxId, 'provisioning');\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Reusing assistant environment...'\n );\n await waitForSandboxReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 300_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'provisioning', message)\n );\n await waitForAssistantServiceReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 90_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'connecting', message)\n );\n setSandbox(currentMode, currentSandboxId, 'running');\n } else {\n if (currentMode === 'api-calls' && !getAccessToken()) {\n throw new Error(\n 'API Calls mode requires an authenticated session. Please sign in again.'\n );\n }\n\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Starting assistant environment...'\n );\n setSandbox(currentMode, null, 'provisioning');\n currentSandboxId = await createAssistantSandbox(\n apollo,\n currentMode,\n workspaceId,\n authContext\n );\n\n setSandbox(currentMode, currentSandboxId, 'provisioning');\n await waitForSandboxReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 300_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'provisioning', message)\n );\n await waitForAssistantServiceReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 90_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'connecting', message)\n );\n setSandbox(currentMode, currentSandboxId, 'running');\n }\n }\n\n let sessionId =\n useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === conversationId)\n ?.backendSessionId ?? null;\n\n if (sessionId) {\n try {\n const session = await getAssistantSession(\n apollo,\n currentSandboxId,\n workspaceId,\n sessionId,\n authContext\n );\n if (session.mode !== currentMode) {\n throw new Error(\n `Session mode mismatch: expected ${currentMode}, received ${session.mode}`\n );\n }\n } catch {\n clearBackendSessionId(conversationId);\n sessionId = null;\n }\n }\n\n if (!sessionId) {\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'connecting',\n 'Preparing session...'\n );\n const result = await createAssistantSession(\n apollo,\n currentSandboxId,\n workspaceId,\n currentMode,\n authContext\n );\n sessionId = result.sessionId;\n setBackendSessionId(conversationId, sessionId);\n }\n\n return {\n sandboxId: currentSandboxId,\n sessionId,\n };\n },\n [\n apollo,\n clearBackendSessionId,\n getAccessToken,\n setBackendSessionId,\n setSandbox,\n updateConversationStage,\n ]\n );\n\n const handleSend = useCallback(\n async (content: string) => {\n // Only block re-entry while a turn is genuinely in-flight. Earlier code\n // checked for *any* entry in the map, which stuck around as `complete`\n // / `error` after the first turn — making every subsequent message\n // silently disappear (the input cleared but handleSend bailed before\n // adding the user message to the conversation).\n if (activeConversationId) {\n const inflightStatus = processingByConversationId[activeConversationId]?.status;\n if (\n inflightStatus === 'provisioning' ||\n inflightStatus === 'connecting' ||\n inflightStatus === 'streaming'\n ) {\n return;\n }\n }\n\n const selectedMode = useAssistantStore.getState().mode;\n let conversationId = activeConversationId;\n const currentConversation = conversationId\n ? useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === conversationId)\n : undefined;\n\n if (!conversationId) {\n conversationId = createConversation(undefined, selectedMode);\n } else if (\n currentConversation &&\n currentConversation.mode !== selectedMode &&\n (currentConversation.messages.length > 0 || currentConversation.backendSessionId)\n ) {\n conversationId = createConversation(undefined, selectedMode);\n } else if (currentConversation && currentConversation.mode !== selectedMode) {\n setConversationMode(conversationId, selectedMode);\n }\n\n if (!conversationId) return;\n\n const workspaceId = getWorkspaceId();\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n\n const userMessage: Message = {\n id: generateId(),\n role: 'user',\n content,\n timestamp: new Date().toISOString(),\n };\n addMessage(conversationId, userMessage);\n\n const assistantMessageId = generateId();\n addMessage(conversationId, {\n id: assistantMessageId,\n role: 'assistant',\n content: '',\n timestamp: new Date().toISOString(),\n });\n\n const applyAssistantResponse = (\n response: string,\n artifacts?: AssistantArtifact[],\n sessionId?: string,\n sandboxId?: string\n ) => {\n patchMessage(conversationId, assistantMessageId, {\n content: sanitizeAssistantResponse(response),\n metadata:\n artifacts?.length || sessionId || sandboxId\n ? {\n artifacts,\n mode: selectedMode,\n sessionId,\n sandboxId,\n }\n : undefined,\n });\n };\n\n const sendWithRealBackend = async (attempt: 1 | 2 = 1): Promise<void> => {\n // Pre-flight AI-credit gate. The wspace gateway's @usage(checkQuota)\n // rejects with 429 QUOTA_EXHAUSTED when the monthly token budget is\n // depleted — block the send (don't provision a sandbox or dispatch the\n // prompt) and surface the upgrade dialog instead of a generic error.\n try {\n await checkAssistantAiCredits(apollo, workspaceId, authContext);\n } catch (creditError) {\n if (isQuotaExhaustedError(creditError)) {\n dispatchAssistantQuotaExhausted('ai-credits');\n applyAssistantResponse(\n \"You've run out of AI credits for this billing cycle. Upgrade your plan to keep chatting.\"\n );\n setConversationProcessing(conversationId, 'error', assistantMessageId, null);\n return;\n }\n // The pre-flight gate exists ONLY to catch a genuine QUOTA_EXHAUSTED\n // rejection. Any other failure here is a transient fault in the\n // credit check itself — e.g. the usage subgraph's getUsageSummary /\n // checkAiCredits intermittently 500s (Postgres/Prisma blip) and the\n // gateway masks it as the opaque \"Unexpected error.\". That is NOT\n // proof the user is out of budget, so it must not abort an otherwise\n // valid send. Fail open and proceed: the wspace gateway's\n // @usage(checkQuota) on createSandbox still enforces the real budget\n // downstream (a truly over-quota caller is stopped there with a 429\n // that surfaces the upgrade dialog via the outer catch).\n console.warn(\n '[assistant] AI-credit pre-flight check failed with a non-quota error; proceeding — gateway @usage(checkQuota) still enforces the real budget downstream',\n creditError\n );\n }\n\n try {\n const { sandboxId, sessionId } = await ensureAssistantRuntime(\n conversationId,\n assistantMessageId,\n selectedMode,\n workspaceId,\n authContext\n );\n\n updateConversationStage(conversationId, assistantMessageId, 'streaming', 'Thinking...');\n const promptStartedAt = Date.now();\n await sendAssistantPromptAsync(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n content,\n selectedMode,\n gatherPageContext(),\n authContext\n );\n\n let result = buildAssistantProgress([], promptStartedAt);\n const timeoutAt =\n Date.now() + (STREAM_BUDGET_MS[selectedMode] ?? DEFAULT_STREAM_BUDGET_MS);\n let lastTtlExtensionAt = Date.now();\n\n while (Date.now() < timeoutAt) {\n const sessionMessages = await getAssistantMessages(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n authContext,\n 50\n );\n\n result = buildAssistantProgress(sessionMessages, promptStartedAt);\n patchMessage(conversationId, assistantMessageId, {\n content: sanitizeAssistantResponse(result.content),\n });\n\n if (result.done) break;\n\n if (Date.now() - lastTtlExtensionAt > 30_000) {\n await extendAssistantSandboxTTL(\n apollo,\n sandboxId,\n workspaceId,\n 600,\n authContext\n ).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, 1500));\n }\n\n // For non-build modes a timeout is a hard failure. For vibe-plugins the\n // prompt runs async server-side (/sessions/:id/prompt-async) and keeps\n // writing artifacts after the poll budget elapses, so we surface whatever\n // it has produced instead of discarding the turn.\n if (!result.done && selectedMode !== 'vibe-plugins') {\n throw new Error('Assistant response timed out while streaming progress');\n }\n\n const artifacts =\n selectedMode === 'vibe-plugins'\n ? await getAssistantArtifacts(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n authContext\n ).catch(() => [])\n : undefined;\n\n if (!result.done) {\n // vibe-plugins build still running in the background. Append a note so\n // the user knows the answer is partial and the artifacts will finish.\n result = {\n ...result,\n content: `${result.content}\\n\\n_Still building in the background — any artifacts below will finish shortly. Re-open this conversation to pick up the completed plugin._`,\n };\n }\n\n applyAssistantResponse(result.content, artifacts, sessionId, sandboxId);\n\n if (result.tokens) {\n patchMessage(conversationId, assistantMessageId, { tokens: result.tokens });\n // Charge the real token usage against the AI-credits quota (the\n // gateway records it via @usage(trackUsage)). We AWAIT it before\n // marking the turn complete / re-enabling input: otherwise a rapid\n // follow-up send would run its pre-flight checkAiCredits against a\n // stale balance (this turn's charge still in flight) and could\n // dispatch a prompt that should have been blocked, letting fast\n // sends bypass the credit cap. Still best-effort — a charge failure\n // never blocks the displayed answer.\n try {\n await reportAssistantAiTokenUsage(\n apollo,\n workspaceId,\n sessionId,\n assistantMessageId,\n result.model,\n result.tokens,\n authContext\n );\n notifyAiCreditsUpdated();\n } catch {\n // ignore — server-side @usage tracking is the source of truth\n }\n }\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n } catch (error) {\n if (attempt === 1 && isRecoverableAssistantError(error)) {\n clearBackendSessionId(conversationId);\n clearSandbox(selectedMode);\n return sendWithRealBackend(2);\n }\n\n throw error;\n }\n };\n\n try {\n if (!workspaceId) {\n throw new Error('Assistant backend is unavailable without an active workspace context.');\n }\n await sendWithRealBackend();\n } catch (error) {\n // A quota error reaching here is the sandbox-environment wall hit\n // during runtime provisioning (createSandbox @usage). Surface the\n // upgrade dialog instead of a generic failure (the AI-credit wall is\n // already handled by the pre-flight gate above).\n if (isQuotaExhaustedError(error)) {\n dispatchAssistantQuotaExhausted('sandbox');\n applyAssistantResponse(\n \"You've reached your sandbox limit on the current plan. Upgrade to start the AI assistant.\"\n );\n setConversationProcessing(conversationId, 'error', assistantMessageId, null);\n return;\n }\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong';\n setConversationProcessing(conversationId, 'error', assistantMessageId, errorMessage);\n updateMessage(\n conversationId,\n assistantMessageId,\n `Sorry, an error occurred: ${errorMessage}. Please try again.`\n );\n }\n },\n [\n activeConversationId,\n addMessage,\n apollo,\n clearBackendSessionId,\n clearSandbox,\n conversations,\n createConversation,\n ensureAssistantRuntime,\n getAccessToken,\n getWorkspaceToken,\n patchMessage,\n processingByConversationId,\n setBackendSessionId,\n setConversationMode,\n setConversationProcessing,\n updateConversationStage,\n updateMessage,\n userId,\n ]\n );\n\n const handleSuggestionClick = useCallback(\n (suggestion: string) => {\n void handleSend(suggestion);\n },\n [handleSend]\n );\n\n // Auto-send any prompt that was queued by `useOpenAssistantHelp` (from\n // the shared GetHelpButton). We keep the latest handleSend in a ref so\n // the subscription effect below doesn't need to tear down on every\n // render — the store subscription lives for the component lifetime and\n // always calls the freshest handler.\n const handleSendRef = useRef(handleSend);\n useEffect(() => {\n handleSendRef.current = handleSend;\n }, [handleSend]);\n\n useEffect(() => {\n // Flush any prompt that was queued before this component mounted.\n const initial = useAssistantStore.getState().consumePendingPrompt();\n if (initial) {\n void handleSendRef.current(initial);\n }\n\n // Watch for new prompts queued while we're already mounted.\n const unsubscribe = useAssistantStore.subscribe((state, prev) => {\n if (state.pendingPrompt && state.pendingPrompt !== prev.pendingPrompt) {\n const queued = useAssistantStore.getState().consumePendingPrompt();\n if (queued) {\n void handleSendRef.current(queued);\n }\n }\n });\n return unsubscribe;\n }, []);\n\n const handleModeChange = useCallback(\n (newMode: AssistantMode) => {\n const currentConversation = activeConversationId\n ? useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === activeConversationId)\n : undefined;\n\n if (\n currentConversation &&\n (currentConversation.messages.length > 0 || currentConversation.backendSessionId)\n ) {\n if (currentConversation.mode !== newMode) {\n const reusableDraft = useAssistantStore\n .getState()\n .conversations.find(\n (conversation) =>\n conversation.mode === newMode &&\n conversation.messages.length === 0 &&\n !conversation.backendSessionId &&\n !useAssistantStore.getState().processingByConversationId[conversation.id]\n );\n\n if (reusableDraft) {\n setActiveConversation(reusableDraft.id);\n } else {\n createConversation(undefined, newMode);\n }\n }\n } else if (activeConversationId) {\n setConversationMode(activeConversationId, newMode);\n }\n\n setMode(newMode);\n },\n [activeConversationId, createConversation, setActiveConversation, setConversationMode, setMode]\n );\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden pt-3\">\n {messages.length === 0 ? (\n <AssistantWelcome mode={mode} onSuggestionClick={handleSuggestionClick} />\n ) : (\n <AssistantMessageList messages={messages} streamingMessageId={activeStreamingMessageId} />\n )}\n\n <div>\n <div className=\"px-3 pb-1\">\n <AssistantModeSelector mode={mode} onModeChange={handleModeChange} />\n </div>\n <AssistantChatInput\n onSend={(value) => void handleSend(value)}\n disabled={activeIsProcessing}\n placeholder={tr(\n 'vibecontrols.assistant.chatArea.placeholder',\n `Ask about ${mode.replace(/-/g, ' ')}...`\n )}\n />\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|