@burdenoff/microfe-bigconsole 2026.928.2 → 2026.929.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.
@@ -12,7 +12,10 @@ var a = {
12
12
  describeCurrentPage: t,
13
13
  gatherPageContext: n,
14
14
  buildPrompt: r,
15
- storageFolders: a
15
+ storageFolders: a,
16
+ localUiCommands: { themeWords: "mode-only" },
17
+ reportTurnTimings: !0,
18
+ writeApproval: "proposal-v1"
16
19
  };
17
20
  //#endregion
18
21
  export { o as BIGCONSOLE_ASSISTANT_PRODUCT, a as STORAGE_FOLDERS };
@@ -1 +1 @@
1
- {"version":3,"file":"assistantProduct.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantProduct.ts"],"sourcesContent":["/**\n * BigConsole's answers to the shared assistant's product seam, as one value.\n *\n * ★★ Every capability field on fe-libs' `AssistantProductProvider` is OPTIONAL,\n * and a missing one does not throw — it degrades SILENTLY. No `gatherPageContext`\n * sends every turn with no page context; no `buildPrompt` drops both the\n * confirm-before-mutate guardrail and the language directive; no\n * `storageFolders` starts a second folder beside the one holding every saved\n * 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.\n */\nimport type { AssistantProductProviderProps } from '@burdenoff/fe-libs/shared/assistant/product';\n\nimport { buildPromptForMode } from './assistantPrompt';\nimport { BIGCONSOLE_ASSISTANT_MODES } from './modes';\nimport { describeCurrentPage, gatherPageContext } from './pageContext';\nimport { BIGCONSOLE_PRODUCT } from './product';\n\n/**\n * Where this product's assistant files land in the workspace file library.\n *\n * ★★ Capitalised as a DISPLAY name while the product id is `bigconsole`, which\n * is exactly why fe-libs cannot derive it: its fallback is\n * `/${product}/AI Assistant/…` → `/bigconsole/AI Assistant/…`.\n *\n * These strings become load-bearing the moment anything is saved. A wrong value\n * does not fail loudly — it quietly starts a SECOND folder beside the first and\n * orphans everything in the original. Today nothing can be orphaned: BigConsole's\n * legacy transport extracts uploaded files into the prompt and saves nothing to\n * the library, so no `/BigConsole/AI Assistant/*` path exists yet. That makes\n * this a free choice now and an expensive one the moment BC4 ships attachments.\n */\nexport const STORAGE_FOLDERS = {\n attachments: '/BigConsole/AI Assistant/Attachments',\n artifacts: '/BigConsole/AI Assistant/Artifacts',\n} as const;\n\n/**\n * Every seam field, required — the provider's props minus its children, and\n * minus the one answer BigConsole deliberately does not give.\n *\n * ★★ `reportTokenUsage` is omitted ON EVIDENCE, not by oversight, and this\n * `Omit` member is the only place that decision is expressed in the type system.\n * `wspace-bigconsole-svc` declares NO token-usage mutation at all, and nothing in\n * this MFE reports one today. The obvious \"fix\" — wiring fe-libs' shared\n * reporter — would bill VibeControls for BigConsole turns: `reportAiTokenUsage`\n * is declared only in `wspace-vibecontrols-svc`, against the\n * `vibecontrols.ai.credits` quota. Metering BigConsole is a backend change.\n *\n * Note that BigConsole's `bigconsole.*` quotas are a different mechanism —\n * they cap entity counts, not AI spend — so their presence is not evidence that\n * turns are metered.\n */\nexport type BigConsoleAssistantProduct = Required<Omit<AssistantProductProviderProps, 'children' | 'reportTokenUsage'>>;\n\n/**\n * ★ Each field points at a product-owned implementation BY IDENTITY — no inline\n * arrow wrappers. The seam guard asserts `toBe`, because a wrapper is how a\n * function silently stops being the one the tests cover.\n *\n * ★ `writeAccess` is deliberately absent: it is a PROP on the shared panel, not\n * a seam field, so it belongs to BC4's panel mount rather than here. Same for\n * the preview controller, which the panel takes as `panelProps.preview`.\n */\nexport const BIGCONSOLE_ASSISTANT_PRODUCT: BigConsoleAssistantProduct = {\n product: BIGCONSOLE_PRODUCT,\n modes: BIGCONSOLE_ASSISTANT_MODES,\n describeCurrentPage,\n gatherPageContext,\n buildPrompt: buildPromptForMode,\n storageFolders: STORAGE_FOLDERS,\n};\n"],"mappings":";;;;;AAmCA,IAAa,IAAkB;CAC7B,aAAa;CACb,WAAW;CACZ,EA6BY,IAA2D;CACtE,SAAS;CACT,OAAO;CACP;CACA;CACA,aAAa;CACb,gBAAgB;CACjB"}
1
+ {"version":3,"file":"assistantProduct.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantProduct.ts"],"sourcesContent":["/**\n * BigConsole's answers to the shared assistant's product seam, as one value.\n *\n * ★★ Every capability field on fe-libs' `AssistantProductProvider` is OPTIONAL,\n * and a missing one does not throw — it degrades SILENTLY. No `gatherPageContext`\n * sends every turn with no page context; no `buildPrompt` drops both the\n * confirm-before-mutate guardrail and the language directive; no\n * `storageFolders` starts a second folder beside the one holding every saved\n * 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.\n */\nimport type { LocalUiCommandOptions } from '@burdenoff/fe-libs/shared/assistant/localUiCommands';\nimport type { AssistantProductProviderProps } from '@burdenoff/fe-libs/shared/assistant/product';\n\nimport { buildPromptForMode } from './assistantPrompt';\nimport { BIGCONSOLE_ASSISTANT_MODES } from './modes';\nimport { describeCurrentPage, gatherPageContext } from './pageContext';\nimport { BIGCONSOLE_PRODUCT } from './product';\n\n/**\n * Where this product's assistant files land in the workspace file library.\n *\n * ★★ Capitalised as a DISPLAY name while the product id is `bigconsole`, which\n * is exactly why fe-libs cannot derive it: its fallback is\n * `/${product}/AI Assistant/…` → `/bigconsole/AI Assistant/…`.\n *\n * These strings become load-bearing the moment anything is saved. A wrong value\n * does not fail loudly — it quietly starts a SECOND folder beside the first and\n * orphans everything in the original. Today nothing can be orphaned: BigConsole's\n * legacy transport extracts uploaded files into the prompt and saves nothing to\n * the library, so no `/BigConsole/AI Assistant/*` path exists yet. That makes\n * this a free choice now and an expensive one the moment BC4 ships attachments.\n */\nexport const STORAGE_FOLDERS = {\n attachments: '/BigConsole/AI Assistant/Attachments',\n artifacts: '/BigConsole/AI Assistant/Artifacts',\n} as const;\n\n/**\n * How the panel reads a UI command it carries out itself (BOFF-7802), for\n * BigConsole.\n *\n * ★★ `mode-only` because BigConsole's CONTENT has themes of its own: a\n * dashboard has a theme, so \"use the dark theme\" in a dashboard builder is a\n * request about the dashboard, and must reach the model rather than flip the\n * app. fe-libs names this exact case (`LocalUiCommandOptions.themeWords`). Only\n * \"<name> mode\" (\"dark mode\") or a theme qualified as the app's (\"change the\n * app theme to dark\") still changes 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 = { themeWords: 'mode-only' };\n\n/**\n * Every seam field, required — the provider's props minus its children, and\n * minus the two answers BigConsole deliberately does not give.\n *\n * ★★ `reportTokenUsage` is omitted ON EVIDENCE, not by oversight, and this\n * `Omit` member is the only place that decision is expressed in the type system.\n * `wspace-bigconsole-svc` declares NO token-usage mutation at all, and nothing in\n * this MFE reports one today. The obvious \"fix\" — wiring fe-libs' shared\n * reporter — would bill VibeControls for BigConsole turns: `reportAiTokenUsage`\n * is declared only in `wspace-vibecontrols-svc`, against the\n * `vibecontrols.ai.credits` quota. Metering BigConsole is a backend change.\n *\n * Note that BigConsole's `bigconsole.*` quotas are a different mechanism —\n * they cap entity counts, not AI spend — so their presence is not evidence that\n * turns are metered.\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 BigConsole\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`), and the shared one already does, so a\n * BigConsole copy would only be a second place for that to go wrong. The\n * bigconsole-app ambient declaration of this value mirrors this `Omit`.\n */\nexport type BigConsoleAssistantProduct = Required<\n Omit<AssistantProductProviderProps, 'children' | 'reportTokenUsage' | 'confirmWrite'>\n>;\n\n/**\n * ★ Each field points at a product-owned implementation BY IDENTITY — no inline\n * arrow wrappers. The seam guard asserts `toBe`, because a wrapper is how a\n * function silently stops being the one the tests cover.\n *\n * ★ `writeAccess` is deliberately absent: it is a PROP on the shared panel, not\n * a seam field, so it belongs to BC4's panel mount rather than here. Same for\n * the preview controller, which the panel takes as `panelProps.preview`.\n */\nexport const BIGCONSOLE_ASSISTANT_PRODUCT: BigConsoleAssistantProduct = {\n product: BIGCONSOLE_PRODUCT,\n modes: BIGCONSOLE_ASSISTANT_MODES,\n describeCurrentPage,\n gatherPageContext,\n buildPrompt: buildPromptForMode,\n storageFolders: STORAGE_FOLDERS,\n localUiCommands: LOCAL_UI_COMMANDS,\n /**\n * ★ On. That is fe-libs' own default (on unless `false`, BOFF-7801) and\n * what BigConsole does today: this seam never set the field, so its turns\n * have been reported by default. The reports carry timings and coarse\n * dimensions only, and BigConsole is the first proposal-v1 product, so its\n * latency is the measurement the rollout needs.\n */\n reportTurnTimings: true,\n /**\n * ★★★ BigConsole is the FIRST proposal-v1 product (BOFF-7803 5a). At\n * `'proposal-v1'` fe-libs provisions Ask and Build (`api-calls`) through the\n * `assistant:use`-gated `provisionAssistantSandbox`, requests the proposal\n * flow for Build, and lets a spoken Build turn through when the sandbox's\n * own `/health` also says v1. Every change then waits for the button on its\n * card; the sandbox and wspace-sandbox-svc decide whether it runs.\n *\n * ★★ That provisioning path has NO `createSandbox` fallback: while\n * wspace-sandbox-svc answers it with 503 (its session relay switched off),\n * BigConsole's assistant cannot start at all. This flag reaches users only\n * when bigconsole-app pins the microfe version that carries it, so the app\n * bump is the release switch, and it waits for the relay.\n */\n writeApproval: 'proposal-v1',\n};\n"],"mappings":";;;;;AAoCA,IAAa,IAAkB;CAC7B,aAAa;CACb,WAAW;CACZ,EAwDY,IAA2D;CACtE,SAAS;CACT,OAAO;CACP;CACA;CACA,aAAa;CACb,gBAAgB;CAChB,iBA/CsD,EAAE,YAAY,aAAa;CAuDjF,mBAAmB;CAenB,eAAe;CAChB"}
@@ -1,34 +1,38 @@
1
+ import { PROPOSAL_WRITE_RULES as e } from "@burdenoff/fe-libs/shared/assistant/proposalWriteRules";
1
2
  //#region src/bigconsole/assistant/assistantPrompt.ts
2
- var e = "--- User request ---", t = [
3
+ var t = "--- User request ---", n = [
3
4
  "You are in api-calls mode for BigConsole and can run GraphQL queries and mutations on the user behalf.",
4
5
  "Before performing ANY write (create, update, delete — any mutation):",
5
6
  "1. State exactly what you are about to change (entity, fields, and values).",
6
7
  "2. Ask the user to confirm, and WAIT for an explicit \"yes\"/\"confirm\" before executing it.",
7
8
  "Read-only queries may run without confirmation. Confirm each write separately; never batch mutations behind a single confirmation.",
8
9
  "",
9
- e
10
- ].join("\n");
11
- function n(e, t) {
10
+ t
11
+ ].join("\n"), r = `${e("BigConsole")}\n\n${t}`;
12
+ function i(e) {
13
+ return e?.writeApproval === "proposal-v1" ? r : n;
14
+ }
15
+ function a(e, t) {
12
16
  return [
13
17
  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
18
  `Translate any English documentation you ground your answer in into ${e} rather than switching to English to quote it.`,
15
19
  "Keep identifiers, code, CLI flags, API field names and product nouns in their original form — translating them would make them wrong."
16
20
  ].join("\n");
17
21
  }
18
- var r = [
22
+ var o = [
19
23
  "This turn was DICTATED, so treat unusual words as probable speech-recognition errors rather than as what the user meant.",
20
24
  "Repair obvious mis-hearings of technical terms, product nouns, column names and command names using the surrounding context.",
21
25
  "Do NOT guess at a mis-heard data sink name, dashboard id, parser name, widget id or column header — 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;
26
+ ].join("\n"), s = ["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");
27
+ function c(e, n, r, c = !1, l = !1, u) {
28
+ let d = [
29
+ r ? a(r, c) : "",
30
+ l ? o : "",
31
+ l ? s : ""
32
+ ].filter(Boolean).join("\n\n"), f = d ? `${d}\n\n` : "";
33
+ return e === "api-calls" ? `${f}${i(u)}\n${n}` : f ? `${f}${t}\n${n}` : n;
30
34
  }
31
35
  //#endregion
32
- export { t as API_CALLS_CONFIRM_PREAMBLE, e as USER_REQUEST_DELIMITER, a as buildPromptForMode };
36
+ export { n as API_CALLS_CONFIRM_PREAMBLE, r as API_CALLS_PROPOSAL_PREAMBLE, t as USER_REQUEST_DELIMITER, c as buildPromptForMode };
33
37
 
34
38
  //# sourceMappingURL=assistantPrompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assistantPrompt.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantPrompt.ts"],"sourcesContent":["/**\n * Soft confirm-before-mutate guardrail for the assistant's write mode, plus the\n * language and dictation instructions.\n *\n * The in-sandbox agent executes mutations autonomously — there is no hard\n * server-side confirmation — so the describe-and-confirm instruction is\n * prepended to the user's TURN, not to the session system prompt. Sending it as\n * `system` would REPLACE the whole mode prompt and silently delete the agent's\n * api-calls 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 * Ported from FluidGrids' `assistantPrompt.ts` (itself ported from\n * microfe-vibecontrols), with BigConsole nouns. The composition and the\n * delimiter discipline are unchanged on purpose — see `buildPromptForMode`.\n */\n\n/**\n * ★ The TRANSPORT's mode type, not BigConsole's narrower list. The shared panel\n * calls `buildPrompt` with whatever mode its store holds, which can be one this\n * product no longer offers (a restored conversation, or a build that dropped a\n * mode) until the panel resets it, so the parameter has to accept every mode the\n * panel can send.\n */\nimport type { AssistantMode } from '@burdenoff/fe-libs/shared/assistant/types';\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 BigConsole and can run GraphQL queries and mutations on the user 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 BigConsole the ones it fails on are predictable: data sink names,\n * dashboard ids, parser names and column headers. A recogniser has no model for\n * a dashboard id read aloud, so a sentence comes back mostly right with the\n * load-bearing words wrong.\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 dashboard id edits somebody else's dashboard. One extra question\n * 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, column names and command names using the surrounding context.',\n 'Do NOT guess at a mis-heard data sink name, dashboard id, parser name, widget id or column header — ask which one the user meant before acting on it.',\n].join('\\n');\n\n/**\n * A dictated turn is a SPOKEN conversation, so the reply is spoken too.\n *\n * ★★ REACHABLE FROM ASK ONLY, from fe-libs 2026.920.x. `writeModeSendRefusal`\n * refuses a dictated send in any `writeCapable` mode before a prompt is\n * composed (\"Voice can't be used in a mode that can change your data\"), and\n * Build is write-capable — so `dictated` and `api-calls` no longer occur\n * together. Both preambles still apply to Ask, which is where dictation lives.\n */\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 *\n * ★ No branch for Ask (`assistant`). It is read-only — its sandbox boots with\n * `AI_ASSISTANT_READONLY=1` — so there is no write to confirm, and inventing a\n * confirmation preamble for it would be copy that lies about what the mode does.\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":";AA+BA,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;;AAiBd,IAAM,IAAqB;CACzB;CACA;CACA;CACD,CAAC,KAAK,KAAK,EAWN,IAAuB,CAC3B,qEACA,qGACD,CAAC,KAAK,KAAK;AAkBZ,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
+ {"version":3,"file":"assistantPrompt.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantPrompt.ts"],"sourcesContent":["/**\n * The write rules for the assistant's write mode, plus the language and\n * dictation instructions.\n *\n * The write rules depend on what the sandbox the turn goes to ENFORCES, which\n * fe-libs passes as `buildPrompt`'s sixth argument from that sandbox's own\n * `/health` (BOFF-7803 5a):\n *\n * - `legacy`: the in-sandbox agent executes mutations itself, so the turn asks\n * it to describe each write and wait for the user's \"yes\". ADVISORY: the\n * dependable guard is the host-side consent dialog plus the admin gate.\n * - `proposal-v1`: the helper only PROPOSES a mutation, and only the button on\n * its card confirms it. Asking for a typed \"yes\" there would contradict the\n * sandbox, so the turn carries fe-libs' `PROPOSAL_WRITE_RULES` instead.\n *\n * Either way the rules are prepended to the user's TURN, not to the session\n * system prompt. Sending them as `system` would REPLACE the whole mode prompt\n * and silently delete the agent's api-calls behaviour along with it.\n *\n * Ported from FluidGrids' `assistantPrompt.ts` (itself ported from\n * microfe-vibecontrols), with BigConsole nouns. The composition and the\n * delimiter discipline are unchanged on purpose — see `buildPromptForMode`.\n */\n\n/**\n * ★ The TRANSPORT's mode type, not BigConsole's narrower list. The shared panel\n * calls `buildPrompt` with whatever mode its store holds, which can be one this\n * product no longer offers (a restored conversation, or a build that dropped a\n * mode) until the panel resets it, so the parameter has to accept every mode the\n * panel can send.\n */\nimport type { AssistantPromptOptions } from '@burdenoff/fe-libs/shared/assistant/product';\nimport { PROPOSAL_WRITE_RULES } from '@burdenoff/fe-libs/shared/assistant/proposalWriteRules';\nimport type { AssistantMode } from '@burdenoff/fe-libs/shared/assistant/types';\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\n/**\n * The write rules for a Build turn to a LEGACY sandbox, which runs a mutation\n * itself. Unchanged by BOFF-7803 5a, byte for byte.\n */\nexport const API_CALLS_CONFIRM_PREAMBLE = [\n 'You are in api-calls mode for BigConsole and can run GraphQL queries and mutations on the user 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 * The write rules for a Build turn to a PROPOSAL-V1 sandbox (BOFF-7803 5a),\n * composed exactly as fe-libs documents it: the shared rules, a blank line,\n * then this product's own delimiter.\n *\n * ★★ The rules are fe-libs' `PROPOSAL_WRITE_RULES`, not a BigConsole rewrite.\n * They are kept consistent with the sandbox's own proposal policy, so a local\n * copy would be a second text to keep in step with it. They carry no delimiter\n * of their own, so this one is the only one, and it is last, as after the\n * legacy preamble.\n */\nexport const API_CALLS_PROPOSAL_PREAMBLE = `${PROPOSAL_WRITE_RULES('BigConsole')}\\n\\n${USER_REQUEST_DELIMITER}`;\n\n/**\n * The write preamble for a Build turn, by what the receiving sandbox enforces.\n * Only exactly `proposal-v1` selects the proposal rules; anything else, or no\n * options at all, keeps the legacy text, which is what fe-libs sends for a\n * sandbox that has not reported the v1 protocol.\n */\nfunction writePreambleFor(options: AssistantPromptOptions | undefined): string {\n return options?.writeApproval === 'proposal-v1' ? API_CALLS_PROPOSAL_PREAMBLE : API_CALLS_CONFIRM_PREAMBLE;\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 BigConsole the ones it fails on are predictable: data sink names,\n * dashboard ids, parser names and column headers. A recogniser has no model for\n * a dashboard id read aloud, so a sentence comes back mostly right with the\n * load-bearing words wrong.\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 dashboard id edits somebody else's dashboard. One extra question\n * 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, column names and command names using the surrounding context.',\n 'Do NOT guess at a mis-heard data sink name, dashboard id, parser name, widget id or column header — ask which one the user meant before acting on it.',\n].join('\\n');\n\n/**\n * A dictated turn is a SPOKEN conversation, so the reply is spoken too.\n *\n * ★★ In Build only against a PROPOSAL-V1 sandbox. From fe-libs 2026.920.x,\n * `writeModeSendRefusal` refuses a dictated send in any `writeCapable` mode\n * before a prompt is composed (\"Voice can't be used in a mode that can change\n * your data\"), and Build is write-capable. fe-libs 2026.929.1 lifts that only\n * when the turn is proposal-bound: this product's flag is `proposal-v1` AND the\n * sandbox's own `/health` says v1 (BOFF-7803 5a, FE9). Then nothing spoken can\n * run a change, only a card's button can. So `dictated` and `api-calls` occur\n * together only with the proposal rules, never with the legacy \"wait for yes\".\n */\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. Both api-calls\n * preambles end with their own copy, so it must not be added again — a second\n * one 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 *\n * ★★ `options` is fe-libs' sixth argument (BOFF-7803 5a): what the sandbox this\n * turn goes to enforces, from its own `/health` — never this product's flag,\n * which only says what BigConsole ASKS for. A proposal-v1 sandbox gets the\n * proposal rules; every other turn is composed exactly as before.\n *\n * ★ No branch for Ask (`assistant`). It is read-only — its sandbox boots with\n * `AI_ASSISTANT_READONLY=1` — so there is no write to confirm, and inventing a\n * confirmation preamble for it would be copy that lies about what the mode does.\n */\nexport function buildPromptForMode(\n mode: AssistantMode,\n content: string,\n languageName?: string,\n isExplicitChoice = false,\n dictated = false,\n options?: AssistantPromptOptions\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}${writePreambleFor(options)}\\n${content}`;\n return language ? `${language}${USER_REQUEST_DELIMITER}\\n${content}` : content;\n}\n"],"mappings":";;AAuCA,IAAa,IAAyB,wBAMzB,IAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK,EAaC,IAA8B,GAAG,EAAqB,aAAa,CAAC,MAAM;AAQvF,SAAS,EAAiB,GAAqD;AAC7E,QAAO,GAAS,kBAAkB,gBAAgB,IAA8B;;AAgBlF,SAAS,EAAsB,GAAsB,GAAmC;AACtF,QAAO;EACL,IACI,YAAY,EAAa,uGACzB,YAAY,EAAa;EAC7B,sEAAsE,EAAa;EACnF;EACD,CAAC,KAAK,KAAK;;AAiBd,IAAM,IAAqB;CACzB;CACA;CACA;CACD,CAAC,KAAK,KAAK,EAcN,IAAuB,CAC3B,qEACA,qGACD,CAAC,KAAK,KAAK;AAuBZ,SAAgB,EACd,GACA,GACA,GACA,IAAmB,IACnB,IAAW,IACX,GACQ;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,EAAiB,EAAQ,CAAC,IAAI,MACtE,IAAW,GAAG,IAAW,EAAuB,IAAI,MAAY"}
@@ -2,9 +2,9 @@ import { BIGCONSOLE_PRODUCT as e } from "./product.js";
2
2
  import { describeCurrentPage as t, gatherPageContext as n, resolveBigConsoleConversationContext as r } from "./pageContext.js";
3
3
  import { useSandboxAssistantTransport as i } from "./createSandboxAssistantTransport.js";
4
4
  import { AiPreviewMiniPanel as a } from "../components/preview/AiPreviewMiniPanel.js";
5
- import { API_CALLS_CONFIRM_PREAMBLE as o, USER_REQUEST_DELIMITER as s, buildPromptForMode as c } from "./assistantPrompt.js";
6
- import { ASK as l, BIGCONSOLE_ASSISTANT_MODES as u, BUILD as d } from "./modes.js";
7
- import { BIGCONSOLE_ASSISTANT_PRODUCT as f, STORAGE_FOLDERS as p } from "./assistantProduct.js";
8
- import { PROBE_RESOURCES as m, useWorkspaceWriteAccess as h } from "./useWorkspaceWriteAccess.js";
9
- import { useBigConsoleAssistantPreview as g } from "./previewController.js";
10
- export { o as API_CALLS_CONFIRM_PREAMBLE, l as ASK, a as AiPreviewMiniPanel, u as BIGCONSOLE_ASSISTANT_MODES, f as BIGCONSOLE_ASSISTANT_PRODUCT, e as BIGCONSOLE_PRODUCT, d as BUILD, m as PROBE_RESOURCES, p as STORAGE_FOLDERS, s as USER_REQUEST_DELIMITER, c as buildPromptForMode, t as describeCurrentPage, n as gatherPageContext, r as resolveBigConsoleConversationContext, g as useBigConsoleAssistantPreview, i as useSandboxAssistantTransport, h as useWorkspaceWriteAccess };
5
+ import { API_CALLS_CONFIRM_PREAMBLE as o, API_CALLS_PROPOSAL_PREAMBLE as s, USER_REQUEST_DELIMITER as c, buildPromptForMode as l } from "./assistantPrompt.js";
6
+ import { ASK as u, BIGCONSOLE_ASSISTANT_MODES as d, BUILD as f } from "./modes.js";
7
+ import { BIGCONSOLE_ASSISTANT_PRODUCT as p, STORAGE_FOLDERS as m } from "./assistantProduct.js";
8
+ import { PROBE_RESOURCES as h, useWorkspaceWriteAccess as g } from "./useWorkspaceWriteAccess.js";
9
+ import { useBigConsoleAssistantPreview as _ } from "./previewController.js";
10
+ export { o as API_CALLS_CONFIRM_PREAMBLE, s as API_CALLS_PROPOSAL_PREAMBLE, u as ASK, a as AiPreviewMiniPanel, d as BIGCONSOLE_ASSISTANT_MODES, p as BIGCONSOLE_ASSISTANT_PRODUCT, e as BIGCONSOLE_PRODUCT, f as BUILD, h as PROBE_RESOURCES, m as STORAGE_FOLDERS, c as USER_REQUEST_DELIMITER, l as buildPromptForMode, t as describeCurrentPage, n as gatherPageContext, r as resolveBigConsoleConversationContext, _ as useBigConsoleAssistantPreview, i as useSandboxAssistantTransport, g as useWorkspaceWriteAccess };