@burdenoff/microfe-vibecontrols 2026.1004.1 → 2026.1005.1

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.
@@ -2,7 +2,8 @@ import { ASSISTANT_MODES as e } from "../../types/assistant.js";
2
2
  import { PRODUCT as t, reportAssistantTokenUsage as n } from "../../services/assistantApi.js";
3
3
  import { describeCurrentPage as r, gatherPageContext as i } from "../../utils/pageContext.js";
4
4
  import { buildPromptForMode as a } from "../../utils/assistantPrompt.js";
5
- var o = {
5
+ import { VIBECONTROLS_RECORD_ADAPTERS as o } from "./recordAdapters.js";
6
+ var s = {
6
7
  product: t,
7
8
  modes: e,
8
9
  describeCurrentPage: r,
@@ -62,12 +63,13 @@ var o = {
62
63
  fallback: "Connected Systems"
63
64
  }
64
65
  }
65
- ]
66
+ ],
67
+ records: o
66
68
  },
67
69
  reportTurnTimings: !0,
68
70
  writeApproval: "proposal-v1"
69
71
  };
70
72
  //#endregion
71
- export { o as VIBECONTROLS_ASSISTANT_PRODUCT };
73
+ export { s as VIBECONTROLS_ASSISTANT_PRODUCT };
72
74
 
73
75
  //# sourceMappingURL=assistantProduct.js.map
@@ -1 +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; no `writeApproval` keeps the\n * legacy write flow.\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 { LocalUiCommandOptions } from '@burdenoff/fe-libs/shared/assistant/localUiCommands';\nimport type { AssistantEntityRoute } from '@burdenoff/fe-libs/shared/assistant/uiActions/entityRoutes';\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/**\n * The pages of ONE record that no sidebar entry names (BOFF-8060 N1b), so \"take me\n * there\" can open them from a link in the assistant's own reply.\n *\n * ★★ `exclude` lists the router's STATIC siblings of each `:id` segment\n * (`VibeControlsRoutes.tsx`): `vibes/new` redirects to the create dialog, so it is\n * not a vibe. `entityRoutes.test.ts` renders every template through the real\n * route table and checks every static sibling against them.\n *\n * ★ `/integrations/connections` is the app shell's Connected Systems page, shown\n * to whoever can see the Integrations entry.\n */\nexport const ENTITY_ROUTES: readonly AssistantEntityRoute[] = [\n {\n id: 'vibecontrols-vibe',\n pattern: '/vibecontrols/vibes/:id',\n params: { id: { kind: 'id', exclude: ['new'] } },\n visibleWith: 'vibecontrols-vibes',\n noun: { key: 'entity.vibe', fallback: 'vibe' },\n },\n {\n id: 'vibecontrols-agent',\n pattern: '/vibecontrols/agents/:id',\n params: { id: { kind: 'id', exclude: ['new'] } },\n visibleWith: 'vibecontrols-agents',\n noun: { key: 'entity.agent', fallback: 'agent' },\n },\n {\n id: 'vibecontrols-session',\n pattern: '/vibecontrols/sessions/:id',\n params: { id: { kind: 'id' } },\n visibleWith: 'vibecontrols-sessions',\n noun: { key: 'entity.session', fallback: 'session' },\n },\n {\n id: 'vibecontrols-connections',\n pattern: '/integrations/connections',\n params: {},\n visibleWith: 'integrations',\n noun: { key: 'entity.connections', fallback: 'Connected Systems' },\n },\n];\n\n/**\n * How the panel reads a UI command it carries out itself (BOFF-7802), for\n * VibeControls.\n *\n * ★★ `mode-only` because VibeControls' CONTENT has themes of its own. A docs\n * site has a Light/Dark theme (`DocsSettingsPanel`), and a VibeDeck has a colour\n * theme (`VibeDeckForm`). So \"use the dark theme\" on either page is a request\n * about that site or deck, and must reach the model rather than flip the app.\n * fe-libs names this exact case (`LocalUiCommandOptions.themeWords`). A miss\n * costs only a turn; a false match changes the app when the user meant their\n * content. \"<name> mode\" (\"dark mode\"), and a theme qualified as the app's\n * (\"change the app theme to dark\"), still change the app locally, with no turn.\n *\n * ★ Its own exported constant so the seam test can pin the field by identity,\n * like every other field on the seam value.\n */\nexport const LOCAL_UI_COMMANDS: LocalUiCommandOptions = {\n themeWords: 'mode-only',\n entityRoutes: ENTITY_ROUTES,\n};\n\n/**\n * Every seam field, required: the provider's props minus its children, and\n * minus the one answer VibeControls deliberately does not give.\n *\n * ★★ `confirmWrite` is omitted so a HIGH-risk write proposal is confirmed in\n * fe-libs' shared `AssistantWriteConfirmDialog` (BOFF-7803 5a). That is the\n * documented default when a product has no dialog of its own, and VibeControls\n * has none. A product dialog must meet the whole contract fe-libs sets for it\n * (readback, target count, consequences, the exact confirm label, sensitive\n * values masked, never `window.confirm`). The shared one already does, so a\n * VibeControls copy would only be a second place for that to go wrong.\n */\nexport type VibeControlsAssistantProduct = Required<\n Omit<AssistantProductProviderProps, 'children' | 'confirmWrite'>\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 localUiCommands: LOCAL_UI_COMMANDS,\n /**\n * ★ On. That is fe-libs' own default (on unless `false`, BOFF-7801) and what\n * VibeControls does today: this seam never set the field, so its turns have\n * been reported by default. The reports carry timings and coarse dimensions\n * only, and the proposal flow's latency is what the rollout measures.\n */\n reportTurnTimings: true,\n /**\n * ★★★ VibeControls asks for the proposal flow (BOFF-7803 5a). At\n * `'proposal-v1'` fe-libs provisions Ask and Take action (`api-calls`)\n * through the `assistant:use`-gated `provisionAssistantSandbox`, requests the\n * proposal flow for Take action, and lets a spoken Take-action turn through\n * when the sandbox's own `/health` also says v1. Every change then waits for\n * the button on its card; the sandbox and wspace-sandbox-svc decide whether\n * it runs. Build (`vibe-plugins`) is not affected: it is still provisioned\n * through `createSandbox`, exactly as before.\n *\n * ★★ That provisioning path has NO `createSandbox` fallback: while\n * wspace-sandbox-svc answers it with 503 (its session relay switched off),\n * the assistant cannot start Ask or Take action at all. This flag reaches\n * users only when vibecontrols-app pins the microfe version that carries it,\n * so the app bump is the release switch.\n */\n writeApproval: 'proposal-v1',\n};\n"],"mappings":";;;;AA0HA,IAAa,IAA+D;CAC1E,SAAS;CACT,OAAO;CACP;CACA;CACA,aAAa;CACb,kBAAkB;CAClB,gBA5F6B;EAC7B,aAAa;EACb,WAAW;EACZ;CA0FC,iBA7BsD;EACtD,YAAY;EACZ,cAjD4D;GAC5D;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE,IAAI;KAAE,MAAM;KAAM,SAAS,CAAC,MAAM;KAAE,EAAE;IAChD,aAAa;IACb,MAAM;KAAE,KAAK;KAAe,UAAU;KAAQ;IAC/C;GACD;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE,IAAI;KAAE,MAAM;KAAM,SAAS,CAAC,MAAM;KAAE,EAAE;IAChD,aAAa;IACb,MAAM;KAAE,KAAK;KAAgB,UAAU;KAAS;IACjD;GACD;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE,IAAI,EAAE,MAAM,MAAM,EAAE;IAC9B,aAAa;IACb,MAAM;KAAE,KAAK;KAAkB,UAAU;KAAW;IACrD;GACD;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE;IACV,aAAa;IACb,MAAM;KAAE,KAAK;KAAsB,UAAU;KAAqB;IACnE;GACF;EAqBA;CAiCC,mBAAmB;CAiBnB,eAAe;CAChB"}
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; no `writeApproval` keeps the\n * legacy write flow.\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 { LocalUiCommandOptions } from '@burdenoff/fe-libs/shared/assistant/localUiCommands';\nimport type { AssistantEntityRoute } from '@burdenoff/fe-libs/shared/assistant/uiActions/entityRoutes';\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';\nimport { VIBECONTROLS_RECORD_ADAPTERS } from './recordAdapters';\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/**\n * The pages of ONE record that no sidebar entry names (BOFF-8060 N1b), so \"take me\n * there\" can open them from a link in the assistant's own reply.\n *\n * ★★ `exclude` lists the router's STATIC siblings of each `:id` segment\n * (`VibeControlsRoutes.tsx`): `vibes/new` redirects to the create dialog, so it is\n * not a vibe. `entityRoutes.test.ts` renders every template through the real\n * route table and checks every static sibling against them.\n *\n * ★ `/integrations/connections` is the app shell's Connected Systems page, shown\n * to whoever can see the Integrations entry.\n */\nexport const ENTITY_ROUTES: readonly AssistantEntityRoute[] = [\n {\n id: 'vibecontrols-vibe',\n pattern: '/vibecontrols/vibes/:id',\n params: { id: { kind: 'id', exclude: ['new'] } },\n visibleWith: 'vibecontrols-vibes',\n noun: { key: 'entity.vibe', fallback: 'vibe' },\n },\n {\n id: 'vibecontrols-agent',\n pattern: '/vibecontrols/agents/:id',\n params: { id: { kind: 'id', exclude: ['new'] } },\n visibleWith: 'vibecontrols-agents',\n noun: { key: 'entity.agent', fallback: 'agent' },\n },\n {\n id: 'vibecontrols-session',\n pattern: '/vibecontrols/sessions/:id',\n params: { id: { kind: 'id' } },\n visibleWith: 'vibecontrols-sessions',\n noun: { key: 'entity.session', fallback: 'session' },\n },\n {\n id: 'vibecontrols-connections',\n pattern: '/integrations/connections',\n params: {},\n visibleWith: 'integrations',\n noun: { key: 'entity.connections', fallback: 'Connected Systems' },\n },\n];\n\n/**\n * How the panel reads a UI command it carries out itself (BOFF-7802), for\n * VibeControls.\n *\n * ★★ `mode-only` because VibeControls' CONTENT has themes of its own. A docs\n * site has a Light/Dark theme (`DocsSettingsPanel`), and a VibeDeck has a colour\n * theme (`VibeDeckForm`). So \"use the dark theme\" on either page is a request\n * about that site or deck, and must reach the model rather than flip the app.\n * fe-libs names this exact case (`LocalUiCommandOptions.themeWords`). A miss\n * costs only a turn; a false match changes the app when the user meant their\n * content. \"<name> mode\" (\"dark mode\"), and a theme qualified as the app's\n * (\"change the app theme to dark\"), still change the app locally, with no turn.\n *\n * ★ Its own exported constant so the seam test can pin the field by identity,\n * like every other field on the seam value.\n */\nexport const LOCAL_UI_COMMANDS: LocalUiCommandOptions = {\n themeWords: 'mode-only',\n entityRoutes: ENTITY_ROUTES,\n // BOFF-8060 N1b: \"open the Staging vibe\" / agent, by name.\n records: VIBECONTROLS_RECORD_ADAPTERS,\n};\n\n/**\n * Every seam field, required: the provider's props minus its children, and\n * minus the one answer VibeControls deliberately does not give.\n *\n * ★★ `confirmWrite` is omitted so a HIGH-risk write proposal is confirmed in\n * fe-libs' shared `AssistantWriteConfirmDialog` (BOFF-7803 5a). That is the\n * documented default when a product has no dialog of its own, and VibeControls\n * has none. A product dialog must meet the whole contract fe-libs sets for it\n * (readback, target count, consequences, the exact confirm label, sensitive\n * values masked, never `window.confirm`). The shared one already does, so a\n * VibeControls copy would only be a second place for that to go wrong.\n */\nexport type VibeControlsAssistantProduct = Required<\n Omit<AssistantProductProviderProps, 'children' | 'confirmWrite'>\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 localUiCommands: LOCAL_UI_COMMANDS,\n /**\n * ★ On. That is fe-libs' own default (on unless `false`, BOFF-7801) and what\n * VibeControls does today: this seam never set the field, so its turns have\n * been reported by default. The reports carry timings and coarse dimensions\n * only, and the proposal flow's latency is what the rollout measures.\n */\n reportTurnTimings: true,\n /**\n * ★★★ VibeControls asks for the proposal flow (BOFF-7803 5a). At\n * `'proposal-v1'` fe-libs provisions Ask and Take action (`api-calls`)\n * through the `assistant:use`-gated `provisionAssistantSandbox`, requests the\n * proposal flow for Take action, and lets a spoken Take-action turn through\n * when the sandbox's own `/health` also says v1. Every change then waits for\n * the button on its card; the sandbox and wspace-sandbox-svc decide whether\n * it runs. Build (`vibe-plugins`) is not affected: it is still provisioned\n * through `createSandbox`, exactly as before.\n *\n * ★★ That provisioning path has NO `createSandbox` fallback: while\n * wspace-sandbox-svc answers it with 503 (its session relay switched off),\n * the assistant cannot start Ask or Take action at all. This flag reaches\n * users only when vibecontrols-app pins the microfe version that carries it,\n * so the app bump is the release switch.\n */\n writeApproval: 'proposal-v1',\n};\n"],"mappings":";;;;;AA6HA,IAAa,IAA+D;CAC1E,SAAS;CACT,OAAO;CACP;CACA;CACA,aAAa;CACb,kBAAkB;CAClB,gBA9F6B;EAC7B,aAAa;EACb,WAAW;EACZ;CA4FC,iBA/BsD;EACtD,YAAY;EACZ,cAjD4D;GAC5D;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE,IAAI;KAAE,MAAM;KAAM,SAAS,CAAC,MAAM;KAAE,EAAE;IAChD,aAAa;IACb,MAAM;KAAE,KAAK;KAAe,UAAU;KAAQ;IAC/C;GACD;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE,IAAI;KAAE,MAAM;KAAM,SAAS,CAAC,MAAM;KAAE,EAAE;IAChD,aAAa;IACb,MAAM;KAAE,KAAK;KAAgB,UAAU;KAAS;IACjD;GACD;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE,IAAI,EAAE,MAAM,MAAM,EAAE;IAC9B,aAAa;IACb,MAAM;KAAE,KAAK;KAAkB,UAAU;KAAW;IACrD;GACD;IACE,IAAI;IACJ,SAAS;IACT,QAAQ,EAAE;IACV,aAAa;IACb,MAAM;KAAE,KAAK;KAAsB,UAAU;KAAqB;IACnE;GACF;EAsBC,SAAS;EACV;CAiCC,mBAAmB;CAiBnB,eAAe;CAChB"}
@@ -0,0 +1,65 @@
1
+ import { getStoredWorkspaceId as e, graphqlFetch as t } from "@burdenoff/fe-libs/shared/graphql";
2
+ var n = "\n query AssistantFindVibes($pagination: PaginationInput) {\n vibes(pagination: $pagination) {\n items { id name workspaceId archivedAt deletedAt }\n }\n }\n", r = "\n query AssistantFindAgents($pagination: PaginationInput) {\n vibecontrolsAgents(pagination: $pagination) {\n id name hostname workspaceId deletedAt\n }\n }\n", i = {
3
+ graphqlFetch: t,
4
+ getWorkspaceId: e
5
+ }, a = (e) => typeof e == "string" && e.trim().length > 0 ? e.trim() : null, o = (e) => e.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
6
+ function s(e, t) {
7
+ let n = o(e);
8
+ if (!n) return !1;
9
+ let r = o(t).split(" ");
10
+ return n.split(" ").every((e) => r.some((t) => t.startsWith(e)));
11
+ }
12
+ async function c(e, t, n, r, i, a, o) {
13
+ let c = e.getWorkspaceId();
14
+ if (!c) return [];
15
+ let l = await e.graphqlFetch({
16
+ gateway: "workspace",
17
+ query: t,
18
+ operationName: n,
19
+ variables: { pagination: { limit: 200 } },
20
+ workspaceToken: !0,
21
+ workspaceId: c,
22
+ organizationId: !0,
23
+ signal: a,
24
+ suppressGlobalErrorEvent: !0
25
+ });
26
+ if (l.errors?.length || !l.data) return [];
27
+ let u = r(l.data);
28
+ if (!Array.isArray(u)) return [];
29
+ let d = [];
30
+ for (let e of u) {
31
+ if (!e || typeof e.id != "string" || e.id.length === 0 || typeof e.workspaceId == "string" && e.workspaceId !== c || e.deletedAt || e.archivedAt) continue;
32
+ let t = o(e);
33
+ t === null || !s(i, t) || d.push({
34
+ params: { id: e.id },
35
+ name: t
36
+ });
37
+ }
38
+ return d;
39
+ }
40
+ function l(e = i) {
41
+ return [{
42
+ id: "vibecontrols-vibe",
43
+ nouns: ["vibe", "vibes"],
44
+ noun: {
45
+ key: "entity.vibe",
46
+ fallback: "vibe"
47
+ },
48
+ entityRouteId: "vibecontrols-vibe",
49
+ find: (t, r) => c(e, n, "AssistantFindVibes", (e) => e.vibes?.items, t, r, (e) => a(e.name))
50
+ }, {
51
+ id: "vibecontrols-agent",
52
+ nouns: ["agent", "agents"],
53
+ noun: {
54
+ key: "entity.agent",
55
+ fallback: "agent"
56
+ },
57
+ entityRouteId: "vibecontrols-agent",
58
+ find: (t, n) => c(e, r, "AssistantFindAgents", (e) => e.vibecontrolsAgents, t, n, (e) => a(e.name) ?? a(e.hostname))
59
+ }];
60
+ }
61
+ var u = l();
62
+ //#endregion
63
+ export { u as VIBECONTROLS_RECORD_ADAPTERS };
64
+
65
+ //# sourceMappingURL=recordAdapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recordAdapters.js","names":[],"sources":["../../../src/components/assistant/recordAdapters.ts"],"sourcesContent":["/**\n * VibeControls' named-record adapters for the shared assistant (BOFF-8060 N1b):\n * \"open the Staging Tunnel vibe\", \"open the build-box agent\".\n *\n * An adapter only LOOKS A RECORD UP BY NAME through the READ API the product's\n * own pages already use (`vibes`, `vibecontrolsAgents`), as the signed-in person:\n * the workspace token is theirs and the gateway's `@rbac` filters the list, so a\n * record they may not read never comes back. The adapter produces route params,\n * never a path: fe-libs fills the entity route declared in `assistantProduct.ts`\n * (`ENTITY_ROUTES`) and re-validates it, and only for a person who can see the\n * route's sidebar entry.\n *\n * ★ Neither list query has a server-side `search`, so the adapter reads the same\n * first page the product's own pickers read (`LIST_LIMIT`) and matches the name\n * HERE. It must filter itself: fe-libs caps what an adapter may return, and an\n * unfiltered page would let the wanted record fall off the end.\n *\n * ★ Raw operation strings (the product's other non-hook reads do the same):\n * `recordAdapters.test.ts` validates both against the committed workspace schema.\n *\n * ★ Nothing here writes, opens a form, or touches the DOM.\n */\nimport type {\n AssistantRecordAdapter,\n AssistantRecordMatch,\n} from '@burdenoff/fe-libs/shared/assistant/uiActions/recordNames';\nimport { getStoredWorkspaceId, graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\n/** The page size the product's own vibe picker reads (EntityVibeLinkControl). */\nexport const LIST_LIMIT = 200;\n\nexport const LIST_VIBES_FOR_ASSISTANT = `\n query AssistantFindVibes($pagination: PaginationInput) {\n vibes(pagination: $pagination) {\n items { id name workspaceId archivedAt deletedAt }\n }\n }\n`;\n\nexport const LIST_AGENTS_FOR_ASSISTANT = `\n query AssistantFindAgents($pagination: PaginationInput) {\n vibecontrolsAgents(pagination: $pagination) {\n id name hostname workspaceId deletedAt\n }\n }\n`;\n\n/** The two things the adapters need from the app; injectable so tests run without a network. */\nexport interface RecordAdapterDeps {\n graphqlFetch: typeof graphqlFetch;\n getWorkspaceId: () => string | null;\n}\nconst LIVE_DEPS: RecordAdapterDeps = { graphqlFetch, getWorkspaceId: getStoredWorkspaceId };\n\ninterface RawNode {\n id?: unknown;\n name?: unknown;\n hostname?: unknown;\n workspaceId?: unknown;\n archivedAt?: unknown;\n deletedAt?: unknown;\n}\n\nconst text = (value: unknown): string | null =>\n typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;\n\nconst normalise = (value: string): string =>\n value\n .normalize('NFKC')\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\s]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n/** Every word said is the start of a word in the name (the same rule fe-libs plans with). */\nexport function nameFits(said: string, name: string): boolean {\n const wanted = normalise(said);\n if (!wanted) return false;\n const words = normalise(name).split(' ');\n return wanted.split(' ').every((word) => words.some((candidate) => candidate.startsWith(word)));\n}\n\nasync function findByName(\n deps: RecordAdapterDeps,\n query: string,\n operationName: string,\n read: (data: Record<string, unknown>) => unknown,\n name: string,\n signal: AbortSignal,\n label: (node: RawNode) => string | null\n): Promise<readonly AssistantRecordMatch[]> {\n const workspaceId = deps.getWorkspaceId();\n if (!workspaceId) return [];\n const result = await deps.graphqlFetch<Record<string, unknown>>({\n gateway: 'workspace',\n query,\n operationName,\n variables: { pagination: { limit: LIST_LIMIT } },\n // The person's own workspace token, minted for THIS workspace.\n workspaceToken: true,\n workspaceId,\n organizationId: true,\n signal,\n suppressGlobalErrorEvent: true,\n });\n if (result.errors?.length || !result.data) return [];\n const rows = read(result.data);\n if (!Array.isArray(rows)) return [];\n const matches: AssistantRecordMatch[] = [];\n for (const row of rows as Array<RawNode | null>) {\n if (!row || typeof row.id !== 'string' || row.id.length === 0) continue;\n // A list answered for another workspace (a switch mid-request) is not used.\n if (typeof row.workspaceId === 'string' && row.workspaceId !== workspaceId) continue;\n if (row.deletedAt || row.archivedAt) continue;\n const shown = label(row);\n if (shown === null || !nameFits(name, shown)) continue;\n matches.push({ params: { id: row.id }, name: shown });\n }\n return matches;\n}\n\nexport function createVibeControlsRecordAdapters(\n deps: RecordAdapterDeps = LIVE_DEPS\n): readonly AssistantRecordAdapter[] {\n const vibes: AssistantRecordAdapter = {\n id: 'vibecontrols-vibe',\n nouns: ['vibe', 'vibes'],\n noun: { key: 'entity.vibe', fallback: 'vibe' },\n entityRouteId: 'vibecontrols-vibe',\n find: (name, signal) =>\n findByName(\n deps,\n LIST_VIBES_FOR_ASSISTANT,\n 'AssistantFindVibes',\n (data) => (data.vibes as { items?: unknown } | null | undefined)?.items,\n name,\n signal,\n (node) => text(node.name)\n ),\n };\n const agents: AssistantRecordAdapter = {\n id: 'vibecontrols-agent',\n nouns: ['agent', 'agents'],\n noun: { key: 'entity.agent', fallback: 'agent' },\n entityRouteId: 'vibecontrols-agent',\n find: (name, signal) =>\n findByName(\n deps,\n LIST_AGENTS_FOR_ASSISTANT,\n 'AssistantFindAgents',\n (data) => data.vibecontrolsAgents,\n name,\n signal,\n // An agent with no display name is called by its host.\n (node) => text(node.name) ?? text(node.hostname)\n ),\n };\n return [vibes, agents];\n}\n\nexport const VIBECONTROLS_RECORD_ADAPTERS: readonly AssistantRecordAdapter[] =\n createVibeControlsRecordAdapters();\n"],"mappings":";AA+BA,IAAa,IAA2B,8KAQ3B,IAA4B,gLAanC,IAA+B;CAAE;CAAc,gBAAgB;CAAsB,EAWrF,KAAQ,MACZ,OAAO,KAAU,YAAY,EAAM,MAAM,CAAC,SAAS,IAAI,EAAM,MAAM,GAAG,MAElE,KAAa,MACjB,EACG,UAAU,OAAO,CACjB,aAAa,CACb,QAAQ,qBAAqB,IAAI,CACjC,QAAQ,QAAQ,IAAI,CACpB,MAAM;AAGX,SAAgB,EAAS,GAAc,GAAuB;CAC5D,IAAM,IAAS,EAAU,EAAK;AAC9B,KAAI,CAAC,EAAQ,QAAO;CACpB,IAAM,IAAQ,EAAU,EAAK,CAAC,MAAM,IAAI;AACxC,QAAO,EAAO,MAAM,IAAI,CAAC,OAAO,MAAS,EAAM,MAAM,MAAc,EAAU,WAAW,EAAK,CAAC,CAAC;;AAGjG,eAAe,EACb,GACA,GACA,GACA,GACA,GACA,GACA,GAC0C;CAC1C,IAAM,IAAc,EAAK,gBAAgB;AACzC,KAAI,CAAC,EAAa,QAAO,EAAE;CAC3B,IAAM,IAAS,MAAM,EAAK,aAAsC;EAC9D,SAAS;EACT;EACA;EACA,WAAW,EAAE,YAAY,EAAE,OAAA,KAAmB,EAAE;EAEhD,gBAAgB;EAChB;EACA,gBAAgB;EAChB;EACA,0BAA0B;EAC3B,CAAC;AACF,KAAI,EAAO,QAAQ,UAAU,CAAC,EAAO,KAAM,QAAO,EAAE;CACpD,IAAM,IAAO,EAAK,EAAO,KAAK;AAC9B,KAAI,CAAC,MAAM,QAAQ,EAAK,CAAE,QAAO,EAAE;CACnC,IAAM,IAAkC,EAAE;AAC1C,MAAK,IAAM,KAAO,GAA+B;AAI/C,MAHI,CAAC,KAAO,OAAO,EAAI,MAAO,YAAY,EAAI,GAAG,WAAW,KAExD,OAAO,EAAI,eAAgB,YAAY,EAAI,gBAAgB,KAC3D,EAAI,aAAa,EAAI,WAAY;EACrC,IAAM,IAAQ,EAAM,EAAI;AACpB,QAAU,QAAQ,CAAC,EAAS,GAAM,EAAM,IAC5C,EAAQ,KAAK;GAAE,QAAQ,EAAE,IAAI,EAAI,IAAI;GAAE,MAAM;GAAO,CAAC;;AAEvD,QAAO;;AAGT,SAAgB,EACd,IAA0B,GACS;AAkCnC,QAAO,CAjC+B;EACpC,IAAI;EACJ,OAAO,CAAC,QAAQ,QAAQ;EACxB,MAAM;GAAE,KAAK;GAAe,UAAU;GAAQ;EAC9C,eAAe;EACf,OAAO,GAAM,MACX,EACE,GACA,GACA,uBACC,MAAU,EAAK,OAAkD,OAClE,GACA,IACC,MAAS,EAAK,EAAK,KAAK,CAC1B;EACJ,EACsC;EACrC,IAAI;EACJ,OAAO,CAAC,SAAS,SAAS;EAC1B,MAAM;GAAE,KAAK;GAAgB,UAAU;GAAS;EAChD,eAAe;EACf,OAAO,GAAM,MACX,EACE,GACA,GACA,wBACC,MAAS,EAAK,oBACf,GACA,IAEC,MAAS,EAAK,EAAK,KAAK,IAAI,EAAK,EAAK,SAAS,CACjD;EACJ,CACqB;;AAGxB,IAAa,IACX,GAAkC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.1004.1",
3
+ "version": "2026.1005.1",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [