@burdenoff/microfe-vibecontrols 2026.915.1 → 2026.915.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/assistant/AIAssistantPanel.js +19 -29
- package/dist/components/assistant/AIAssistantPanel.js.map +1 -1
- package/dist/components/assistant/AiCreditsMeter.js.map +1 -1
- package/dist/components/assistant/assistantProduct.js +20 -0
- package/dist/components/assistant/assistantProduct.js.map +1 -0
- package/dist/components/assistant/index.js +0 -8
- package/dist/config/assistantUsecases.js +24 -27
- package/dist/config/assistantUsecases.js.map +1 -1
- package/dist/generated/wspace-operations.js +1713 -1679
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/hooks/useAiCreditsQuota.js +44 -44
- package/dist/hooks/useAiCreditsQuota.js.map +1 -1
- package/dist/hooks/useOfferedAssistantMode.js +17 -0
- package/dist/hooks/useOfferedAssistantMode.js.map +1 -0
- package/dist/hooks/useOpenAssistantHelp.js.map +1 -1
- package/dist/index.js +10 -11
- package/dist/pages/SessionsPage.js +14 -14
- package/dist/services/assistantApi.js +16 -17
- package/dist/services/assistantApi.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js +5 -5
- package/dist/types/assistant.js.map +1 -1
- package/dist/utils/aiCreditsLedger.js +23 -0
- package/dist/utils/aiCreditsLedger.js.map +1 -0
- package/dist/utils/assistantPrompt.js.map +1 -1
- package/dist/utils/pageContext.js +10 -10
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/assistant/AssistantChatArea.js +0 -11
- package/dist/components/assistant/AssistantChatInput.js +0 -4
- package/dist/components/assistant/AssistantConversationList.js +0 -6
- package/dist/components/assistant/AssistantExportMenu.js +0 -5
- package/dist/components/assistant/AssistantMessageItem.js +0 -9
- package/dist/components/assistant/AssistantMessageList.js +0 -4
- package/dist/components/assistant/AssistantModeSelector.js +0 -5
- package/dist/components/assistant/AssistantWelcome.js +0 -4
- package/dist/generated/global-operations.js +0 -27
- package/dist/generated/global-operations.js.map +0 -1
- package/dist/store/assistantStore.js +0 -165
- package/dist/store/assistantStore.js.map +0 -1
|
@@ -1,40 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { AssistantProductProvider as d } from "@burdenoff/fe-libs/shared/assistant/product";
|
|
10
|
-
import { getActorId as f, getWorkspaceId as p } from "@burdenoff/fe-libs/shared/assistant/context";
|
|
1
|
+
import { useWorkspaceWriteAccess as e } from "../../hooks/useWorkspaceWriteAccess.js";
|
|
2
|
+
import { useOfferedAssistantMode as t } from "../../hooks/useOfferedAssistantMode.js";
|
|
3
|
+
import { AiCreditsMeter as n } from "./AiCreditsMeter.js";
|
|
4
|
+
import { VIBECONTROLS_ASSISTANT_PRODUCT as r } from "./assistantProduct.js";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
import { AIAssistantPanel as o } from "@burdenoff/fe-libs/shared/assistant/ui/AIAssistantPanel";
|
|
7
|
+
import { AssistantProductProvider as s } from "@burdenoff/fe-libs/shared/assistant/product";
|
|
8
|
+
import { getActorId as c, getWorkspaceId as l } from "@burdenoff/fe-libs/shared/assistant/context";
|
|
11
9
|
//#region src/components/assistant/AIAssistantPanel.tsx
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
product: t,
|
|
19
|
-
modes: e,
|
|
20
|
-
describeCurrentPage: i,
|
|
21
|
-
gatherPageContext: a,
|
|
22
|
-
buildPrompt: o,
|
|
23
|
-
reportTokenUsage: n,
|
|
24
|
-
storageFolders: m,
|
|
25
|
-
children: /* @__PURE__ */ l("div", {
|
|
10
|
+
var u = (u) => {
|
|
11
|
+
t();
|
|
12
|
+
let d = e(c(), l() || null);
|
|
13
|
+
return /* @__PURE__ */ i(s, {
|
|
14
|
+
...r,
|
|
15
|
+
children: /* @__PURE__ */ a("div", {
|
|
26
16
|
className: "flex flex-col h-full w-full min-h-0",
|
|
27
|
-
children: [/* @__PURE__ */
|
|
17
|
+
children: [/* @__PURE__ */ i(n, {}), /* @__PURE__ */ i("div", {
|
|
28
18
|
className: "flex-1 min-h-0",
|
|
29
|
-
children: /* @__PURE__ */
|
|
30
|
-
...
|
|
31
|
-
writeAccess:
|
|
19
|
+
children: /* @__PURE__ */ i(o, {
|
|
20
|
+
...u,
|
|
21
|
+
writeAccess: d
|
|
32
22
|
})
|
|
33
23
|
})]
|
|
34
24
|
})
|
|
35
25
|
});
|
|
36
26
|
};
|
|
37
27
|
//#endregion
|
|
38
|
-
export {
|
|
28
|
+
export { u as AIAssistantPanel };
|
|
39
29
|
|
|
40
30
|
//# sourceMappingURL=AIAssistantPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIAssistantPanel.js","names":[],"sources":["../../../src/components/assistant/AIAssistantPanel.tsx"],"sourcesContent":["/**\n * VibeControls' AI assistant.\n *\n * ★★ Everything below this file is now shared. The panel, the orchestrator, the\n * transport, the voice session, the composer and every component in them live in\n * `fe-libs/shared/assistant` and answer to any product that mounts them. What is\n * left here is the things that are genuinely VibeControls', and nothing else:\n *\n * modes which use cases this product offers.\n * describeCurrentPage what to CALL the page the user is on.\n * gatherPageContext the same page, as structured context for the agent.\n * buildPrompt the confirm-before-mutate guardrail, the language\n * directive and the dictation instructions.\n * reportTokenUsage the charge, against VibeControls' OWN reporter.\n * storageFolders where this product's attachments and artifacts live.\n *\n * Plus `writeAccess`, which is a PROP rather than a seventh seam field: which\n * resource types prove \"this user can write here\" is a product answer, but the\n * product can simply compute it and hand it down. A capability a product can\n * work out for itself does not belong in an interface every product must\n * satisfy.\n *\n * ★ The provider wraps from OUTSIDE the shared panel rather than sitting inside\n * its tree: the panel calls `useAssistantProduct()` and `useAssistantTr()` in its\n * own body, so a provider nested in its JSX would be below the consumer and read\n * as null — which does not throw, it silently renders English with no modes.\n *\n * ★ `AiCreditsMeter` is composed AROUND the shared panel, not inside it. The\n * shared panel has no slot for it and should not grow one: a credits strip is a\n * VibeControls billing decision, not something every product wants above its\n * assistant. It listens to the `notifyAiCreditsUpdated` event the shared chat\n * area already dispatches, so it keeps working untouched.\n */\n\nimport type { FC } from 'react';\nimport {\n AIAssistantPanel as SharedAIAssistantPanel,\n type AIAssistantPanelProps as SharedProps,\n} from '@burdenoff/fe-libs/shared/assistant/ui/AIAssistantPanel';\nimport { AssistantProductProvider } from '@burdenoff/fe-libs/shared/assistant/product';\nimport { getActorId, getWorkspaceId } from '@burdenoff/fe-libs/shared/assistant/context';\nimport {
|
|
1
|
+
{"version":3,"file":"AIAssistantPanel.js","names":[],"sources":["../../../src/components/assistant/AIAssistantPanel.tsx"],"sourcesContent":["/**\n * VibeControls' AI assistant.\n *\n * ★★ Everything below this file is now shared. The panel, the orchestrator, the\n * transport, the voice session, the composer and every component in them live in\n * `fe-libs/shared/assistant` and answer to any product that mounts them. What is\n * left here is the things that are genuinely VibeControls', and nothing else:\n *\n * modes which use cases this product offers.\n * describeCurrentPage what to CALL the page the user is on.\n * gatherPageContext the same page, as structured context for the agent.\n * buildPrompt the confirm-before-mutate guardrail, the language\n * directive and the dictation instructions.\n * reportTokenUsage the charge, against VibeControls' OWN reporter.\n * storageFolders where this product's attachments and artifacts live.\n *\n * All six, plus the product id, are one `Required` value in `assistantProduct.ts`,\n * spread into the provider here, so a missing answer is a type error.\n *\n * Plus `writeAccess`, which is a PROP rather than a seventh seam field: which\n * resource types prove \"this user can write here\" is a product answer, but the\n * product can simply compute it and hand it down. A capability a product can\n * work out for itself does not belong in an interface every product must\n * satisfy.\n *\n * ★ The provider wraps from OUTSIDE the shared panel rather than sitting inside\n * its tree: the panel calls `useAssistantProduct()` and `useAssistantTr()` in its\n * own body, so a provider nested in its JSX would be below the consumer and read\n * as null — which does not throw, it silently renders English with no modes.\n *\n * ★ `AiCreditsMeter` is composed AROUND the shared panel, not inside it. The\n * shared panel has no slot for it and should not grow one: a credits strip is a\n * VibeControls billing decision, not something every product wants above its\n * assistant. It listens to the `notifyAiCreditsUpdated` event the shared chat\n * area already dispatches, so it keeps working untouched.\n */\n\nimport type { FC } from 'react';\nimport {\n AIAssistantPanel as SharedAIAssistantPanel,\n type AIAssistantPanelProps as SharedProps,\n} from '@burdenoff/fe-libs/shared/assistant/ui/AIAssistantPanel';\nimport { AssistantProductProvider } from '@burdenoff/fe-libs/shared/assistant/product';\nimport { getActorId, getWorkspaceId } from '@burdenoff/fe-libs/shared/assistant/context';\nimport { useWorkspaceWriteAccess } from '@/hooks/useWorkspaceWriteAccess';\nimport { useOfferedAssistantMode } from '@/hooks/useOfferedAssistantMode';\nimport { AiCreditsMeter } from './AiCreditsMeter';\nimport { VIBECONTROLS_ASSISTANT_PRODUCT } from './assistantProduct';\n\nexport type AIAssistantPanelProps = Omit<SharedProps, 'writeAccess'>;\n\nexport const AIAssistantPanel: FC<AIAssistantPanelProps> = (props) => {\n /**\n * ★★ A stored mode VibeControls no longer offers (the retired `general`) lands\n * on Ask BEFORE the shared panel's mount effects read it; see the hook. Called\n * here, above the panel, because that ordering is what makes it work.\n */\n useOfferedAssistantMode();\n\n /**\n * ★ Advisory, never a gate — see `useWorkspaceWriteAccess`. Gating on it\n * would remove api-calls from the product entirely until RBAC rows are\n * seeded. It warns before the effort instead of after it, and self-corrects\n * with no code change the moment those rows exist.\n */\n const writeAccess = useWorkspaceWriteAccess(getActorId(), getWorkspaceId() || null);\n\n return (\n <AssistantProductProvider {...VIBECONTROLS_ASSISTANT_PRODUCT}>\n <div className=\"flex flex-col h-full w-full min-h-0\">\n <AiCreditsMeter />\n <div className=\"flex-1 min-h-0\">\n <SharedAIAssistantPanel {...props} writeAccess={writeAccess} />\n </div>\n </div>\n </AssistantProductProvider>\n );\n};\n"],"mappings":";;;;;;;;;AAmDA,IAAa,KAA+C,MAAU;AAMpE,IAAyB;CAQzB,IAAM,IAAc,EAAwB,GAAY,EAAE,GAAgB,IAAI,KAAK;AAEnF,QACE,kBAAC,GAAD;EAA0B,GAAI;YAC5B,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAkB,CAAA,EAClB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAwB,GAAI;KAAoB;KAAe,CAAA;IAC3D,CAAA,CACF;;EACmB,CAAA"}
|
|
@@ -1 +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
|
|
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 workspace, a\n * failed ledger read, no `vibecontrols.ai.credits` assignment, or an unlimited\n * plan with nothing meaningful to meter). See `useAiCreditsQuota`.\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":";;;;;AAWA,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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ASSISTANT_MODES as e } from "../../types/assistant.js";
|
|
2
|
+
import { PRODUCT as t, reportAssistantTokenUsage as n } from "../../services/assistantApi.js";
|
|
3
|
+
import { describeCurrentPage as r, gatherPageContext as i } from "../../utils/pageContext.js";
|
|
4
|
+
import { buildPromptForMode as a } from "../../utils/assistantPrompt.js";
|
|
5
|
+
var o = {
|
|
6
|
+
product: t,
|
|
7
|
+
modes: e,
|
|
8
|
+
describeCurrentPage: r,
|
|
9
|
+
gatherPageContext: i,
|
|
10
|
+
buildPrompt: a,
|
|
11
|
+
reportTokenUsage: n,
|
|
12
|
+
storageFolders: {
|
|
13
|
+
attachments: "/VibeControls/AI Assistant/Attachments",
|
|
14
|
+
artifacts: "/VibeControls/AI Assistant/Artifacts"
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { o as VIBECONTROLS_ASSISTANT_PRODUCT };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=assistantProduct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistantProduct.js","names":[],"sources":["../../../src/components/assistant/assistantProduct.ts"],"sourcesContent":["/**\n * VibeControls' answers to the shared assistant's product seam, as one value.\n *\n * ★★ Every field of fe-libs' `AssistantProductProvider` is OPTIONAL, and a\n * missing one does not throw — it silently degrades. No `gatherPageContext`\n * sends every turn with no page context; no `buildPrompt` drops the\n * confirm-before-mutate guardrail and the language directive; no\n * `reportTokenUsage` meters nothing; no `storageFolders` starts a second folder\n * beside the one holding every saved attachment.\n *\n * So the value is typed `Required<…>` over the provider's own props: forgetting\n * a field, or fe-libs adding one, is a type error in this file rather than a\n * quiet regression in production. The wrapper spreads it into the provider, and\n * `assistantProduct.test.tsx` reads it back through fe-libs' real context.\n */\n\nimport type { AssistantProductProviderProps } from '@burdenoff/fe-libs/shared/assistant/product';\nimport { ASSISTANT_MODES } from '@/types/assistant';\nimport { PRODUCT, reportAssistantTokenUsage } from '@/services/assistantApi';\nimport { describeCurrentPage, gatherPageContext } from '@/utils/pageContext';\nimport { buildPromptForMode } from '@/utils/assistantPrompt';\n\n/**\n * Where this product's assistant files live.\n *\n * ★★ These strings are LOAD-BEARING once users have saved anything. They are the\n * real folders in the workspace file library holding every attachment and\n * artifact. Changing them does not fail — it starts a second folder beside the\n * first and orphans everything in the original.\n *\n * ★ Note the capitalisation: `/VibeControls/…`, while the product id is\n * `vibecontrols`. That mismatch is exactly why fe-libs cannot derive this and\n * why it is supplied here.\n */\nexport const STORAGE_FOLDERS = {\n attachments: '/VibeControls/AI Assistant/Attachments',\n artifacts: '/VibeControls/AI Assistant/Artifacts',\n} as const;\n\n/** Every seam field, required: the provider's props minus its children. */\nexport type VibeControlsAssistantProduct = Required<\n Omit<AssistantProductProviderProps, 'children'>\n>;\n\nexport const VIBECONTROLS_ASSISTANT_PRODUCT: VibeControlsAssistantProduct = {\n product: PRODUCT,\n modes: ASSISTANT_MODES,\n describeCurrentPage,\n gatherPageContext,\n buildPrompt: buildPromptForMode,\n reportTokenUsage: reportAssistantTokenUsage,\n storageFolders: STORAGE_FOLDERS,\n};\n"],"mappings":";;;;AA4CA,IAAa,IAA+D;CAC1E,SAAS;CACT,OAAO;CACP;CACA;CACA,aAAa;CACb,kBAAkB;CAClB,gBAjB6B;EAC7B,aAAa;EACb,WAAW;EACZ;CAeA"}
|
|
@@ -1,10 +1,2 @@
|
|
|
1
1
|
import "./AIAssistantPanel.js";
|
|
2
|
-
import "./AssistantChatInput.js";
|
|
3
2
|
import "./ProductMarkdown.js";
|
|
4
|
-
import "./AssistantMessageItem.js";
|
|
5
|
-
import "./AssistantMessageList.js";
|
|
6
|
-
import "./AssistantModeSelector.js";
|
|
7
|
-
import "./AssistantWelcome.js";
|
|
8
|
-
import "./AssistantChatArea.js";
|
|
9
|
-
import "./AssistantConversationList.js";
|
|
10
|
-
import "./AssistantExportMenu.js";
|
|
@@ -1,51 +1,48 @@
|
|
|
1
|
-
//#region src/config/assistantUsecases.ts
|
|
2
1
|
var e = [
|
|
3
2
|
{
|
|
4
3
|
id: "assistant",
|
|
5
|
-
label: "
|
|
6
|
-
description: "Ask
|
|
4
|
+
label: "Ask",
|
|
5
|
+
description: "Ask about VibeControls and your workspace. It reads your vibes, sessions and agents, explains what you are looking at and points you to the right page. Read-only.",
|
|
7
6
|
suggestions: [
|
|
8
7
|
"What can I do on this screen?",
|
|
9
|
-
"Create a new vibe called demo",
|
|
10
8
|
"List my recent sessions",
|
|
9
|
+
"Where do I manage my agents?",
|
|
11
10
|
"How do tunnels work?"
|
|
12
11
|
]
|
|
13
12
|
},
|
|
14
13
|
{
|
|
15
14
|
id: "api-calls",
|
|
16
|
-
label: "
|
|
17
|
-
description: "
|
|
15
|
+
label: "Take action",
|
|
16
|
+
description: "Let the assistant make changes for you: create vibes, rename them, stop sessions. It shows you each change and waits for your approval before anything is saved.",
|
|
18
17
|
suggestions: [
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
]
|
|
18
|
+
"Create a new vibe called demo",
|
|
19
|
+
"Rename one of my vibes",
|
|
20
|
+
"Stop a session I no longer need",
|
|
21
|
+
"List my recent sessions"
|
|
22
|
+
],
|
|
23
|
+
requiresAdmin: !0,
|
|
24
|
+
writeCapable: !0
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
id: "vibe-plugins",
|
|
27
|
-
label: "
|
|
28
|
-
description: "Build and
|
|
28
|
+
label: "Build",
|
|
29
|
+
description: "Build a Vibe Plugin that extends VibeControls. The assistant scaffolds and compiles it from the platform guides and gives you the files to download.",
|
|
29
30
|
suggestions: [
|
|
30
31
|
"How do I create a new Vibe Plugin?",
|
|
31
32
|
"What is the plugin manifest structure?",
|
|
32
33
|
"Explain the plugin lifecycle hooks",
|
|
33
34
|
"Show me a plugin example with frontend UI"
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
id: "general",
|
|
38
|
-
label: "General",
|
|
39
|
-
description: "General help with VibeControls concepts and features",
|
|
40
|
-
suggestions: [
|
|
41
|
-
"What is a Vibe in VibeControls?",
|
|
42
|
-
"How do agents connect to the platform?",
|
|
43
|
-
"Explain the session lifecycle",
|
|
44
|
-
"What are tunnels used for?"
|
|
45
|
-
]
|
|
35
|
+
],
|
|
36
|
+
requiresAdmin: !0
|
|
46
37
|
}
|
|
47
|
-
];
|
|
38
|
+
], t = "assistant";
|
|
39
|
+
function n(t) {
|
|
40
|
+
return e.some((e) => e.id === t);
|
|
41
|
+
}
|
|
42
|
+
function r(e) {
|
|
43
|
+
return n(e) ? e : t;
|
|
44
|
+
}
|
|
48
45
|
//#endregion
|
|
49
|
-
export { e as ASSISTANT_USECASE_CONFIG };
|
|
46
|
+
export { e as ASSISTANT_USECASE_CONFIG, r as resolveOfferedMode };
|
|
50
47
|
|
|
51
48
|
//# sourceMappingURL=assistantUsecases.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistantUsecases.js","names":[],"sources":["../../src/config/assistantUsecases.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"assistantUsecases.js","names":[],"sources":["../../src/config/assistantUsecases.ts"],"sourcesContent":["/**\n * VibeControls AI assistant modes.\n *\n * ★ These are the modes the shared fe-libs panel OFFERS. The panel filters them\n * again at runtime: a `requiresAdmin` mode is shown only to a confirmed org\n * Owner/Admin (`myRoles`), and the first switch into a `writeCapable` mode opens\n * the one-time consent dialog. Both are enforced in fe-libs\n * (`shared/assistant/ui/AssistantChatArea`); what is decided HERE is which modes\n * carry the flags. Same mechanism as HealthyBowl's `assistantUsecases.ts`.\n *\n * assistant \"Ask\". Every member. The sandbox runs with\n * AI_ASSISTANT_READONLY, so its GraphQL helper refuses mutations.\n * api-calls \"Take action\". Admins only. Writes through the gateway, and\n * the sandbox runs with AI_ASSISTANT_CONFIRM_WRITES, so each\n * write waits for the user's approval.\n * vibe-plugins \"Build\". Admins only. Builds plugin artifacts, but its sandbox\n * is NOT read-only; see the note on BUILD below. Not flagged\n * `writeCapable` (yet), for the reason given there.\n *\n * ★★ `general` is gone. A stored `general`, or any mode not listed here, lands\n * on Ask; see `resolveOfferedMode` and `useOfferedAssistantMode`.\n *\n * ★ The mode IDs are transport values and must not be renamed: they choose the\n * sandbox image, its environment and the agent's persona. Only labels change.\n */\n\nexport interface AssistantUsecase {\n id: string;\n label: string;\n description: string;\n suggestions: readonly string[];\n /** Only offered to a global Owner/Admin (see fe-libs `useAssistantApiAccess`). */\n requiresAdmin?: boolean;\n /** Can change live data, so the consent dialog and the composer's write-access warning apply. */\n writeCapable?: boolean;\n}\n\n/**\n * \"Ask\": read-only, for everyone.\n *\n * ★★ The copy must not promise a write. This mode's sandbox refuses every\n * mutation, so \"Create a new vibe called demo\", the suggestion it used to carry,\n * could only ever end in a refusal after the user had waited for a sandbox.\n * `assistantUsecases.test.ts` fails on write verbs anywhere in this copy.\n */\nconst ASK = {\n id: 'assistant',\n label: 'Ask',\n description:\n 'Ask about VibeControls and your workspace. It reads your vibes, sessions and agents, explains what you are looking at and points you to the right page. Read-only.',\n suggestions: [\n 'What can I do on this screen?',\n 'List my recent sessions',\n 'Where do I manage my agents?',\n 'How do tunnels work?',\n ],\n} as const satisfies AssistantUsecase;\n\n/** \"Take action\": the write mode. Admin-only, consent on first switch, approval per write. */\nconst TAKE_ACTION = {\n // Display label only; the mode ID stays 'api-calls'.\n id: 'api-calls',\n label: 'Take action',\n description:\n 'Let the assistant make changes for you: create vibes, rename them, stop sessions. It shows you each change and waits for your approval before anything is saved.',\n suggestions: [\n 'Create a new vibe called demo',\n 'Rename one of my vibes',\n 'Stop a session I no longer need',\n 'List my recent sessions',\n ],\n requiresAdmin: true,\n writeCapable: true,\n} as const satisfies AssistantUsecase;\n\n/**\n * \"Build\": Vibe Plugin authoring. Admin-only.\n *\n * ★★ Why admin-only, when its job is producing plugin files: the sandbox is\n * not confined to that job, and can write.\n * - fe-libs forwards the user's own access and workspace tokens on EVERY proxy\n * request, whatever the mode (`shared/assistant/api.ts`, `proxyToSandbox`).\n * - wspace-sandbox-svc passes them into the sandbox for every assistant mode\n * (`ensureAssistantProxyAccess`, `X-AI-Assistant-Auth-Token`).\n * - ai-assistant keeps them whenever the sandbox holds no delegated app\n * credentials, which `vibe-plugins` never does (proxy-token auth), and its\n * GraphQL helper then authenticates as the user (`api-auth.ts`).\n * - The mode has `bash`, the gateway URLs are in its environment, and it gets\n * NEITHER brake: AI_ASSISTANT_READONLY is set only for `assistant`,\n * AI_ASSISTANT_CONFIRM_WRITES only for `api-calls`.\n * So it can change workspace data within the user's own permissions, and\n * decision D4 makes every such mode admin-only.\n *\n * ★★ Why NOT `writeCapable`, although it can write: in fe-libs (2026.914.3 and\n * earlier) that flag routes the first switch into the mode through\n * `AssistantApiCallsConsentDialog`, whose copy is written for Take action only.\n * It is titled \"Switch to Take action mode?\", its button reads \"Enable Take\n * action\", and it promises the assistant will ask before making a change. For\n * Build all three are false: no AI_ASSISTANT_CONFIRM_WRITES, no confirm\n * preamble. Its one `apiCallsAcknowledged` flag also covers every\n * `writeCapable` mode, so accepting it from Build would skip Take action's real\n * consent. Showing no dialog is more honest than showing that one. The cost:\n * the composer's write-access warning (`writeAccessDenied`) no longer shows in\n * Build. Restore the flag once fe-libs words the dialog, and keeps the\n * acknowledgment, per mode; `assistantUsecases.test.ts` fails when the dialog\n * stops naming Take action, as a reminder.\n *\n * ★ The flags gate the UI; they add no sandbox brake. That is why this copy\n * claims no approval step for the mode: the sandbox does not enforce one.\n */\nconst BUILD = {\n id: 'vibe-plugins',\n label: 'Build',\n description:\n 'Build a Vibe Plugin that extends VibeControls. The assistant scaffolds and compiles it from the platform guides and gives you the files to download.',\n suggestions: [\n 'How do I create a new Vibe Plugin?',\n 'What is the plugin manifest structure?',\n 'Explain the plugin lifecycle hooks',\n 'Show me a plugin example with frontend UI',\n ],\n requiresAdmin: true,\n} as const satisfies AssistantUsecase;\n\nexport const ASSISTANT_USECASE_CONFIG = [ASK, TAKE_ACTION, BUILD] as const;\n\nexport type AssistantUsecaseId = (typeof ASSISTANT_USECASE_CONFIG)[number]['id'];\n\n/**\n * Where a user lands when their mode is not offered.\n *\n * ★ The same literal fe-libs falls back to (`AssistantChatArea` resets to\n * `\"assistant\"` once the role check settles). Typed as a mode ID, so removing\n * Ask from the list above fails the typecheck here.\n */\nexport const DEFAULT_ASSISTANT_MODE: AssistantUsecaseId = 'assistant';\n\nfunction isOfferedMode(mode: unknown): mode is AssistantUsecaseId {\n return ASSISTANT_USECASE_CONFIG.some((config) => config.id === mode);\n}\n\n/**\n * The mode to use for a stored one: itself when this product offers it, Ask\n * when it does not (a retired `general`, another product's mode, a corrupt\n * value).\n *\n * ★ It answers only \"does this mode EXIST here\". Whether the user may use an\n * admin-only mode is the role check's question and stays with fe-libs: resetting\n * an admin's stored Take action here, before `myRoles` answers, would throw them\n * out of that mode on every reload.\n */\nexport function resolveOfferedMode(mode: unknown): AssistantUsecaseId {\n return isOfferedMode(mode) ? mode : DEFAULT_ASSISTANT_MODE;\n}\n"],"mappings":"AA4HA,IAAa,IAA2B;CA/E5B;EACV,IAAI;EACJ,OAAO;EACP,aACE;EACF,aAAa;GACX;GACA;GACA;GACA;GACD;EACF;CAGmB;EAElB,IAAI;EACJ,OAAO;EACP,aACE;EACF,aAAa;GACX;GACA;GACA;GACA;GACD;EACD,eAAe;EACf,cAAc;EACf;CAqCa;EACZ,IAAI;EACJ,OAAO;EACP,aACE;EACF,aAAa;GACX;GACA;GACA;GACA;GACD;EACD,eAAe;EAChB;CAEgE,EAWpD,IAA6C;AAE1D,SAAS,EAAc,GAA2C;AAChE,QAAO,EAAyB,MAAM,MAAW,EAAO,OAAO,EAAK;;AAatE,SAAgB,EAAmB,GAAmC;AACpE,QAAO,EAAc,EAAK,GAAG,IAAO"}
|