@burdenoff/fe-libs 2026.1027.13 → 2026.1028.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.
@@ -28,7 +28,7 @@ function D(e, t) {
28
28
  }
29
29
  function O(O, k) {
30
30
  let A = O.mode ?? "assistant", j = O.streamBudgetMs ?? S, M = { accumulateNarration: O.accumulateNarration === !0 }, N = O.ttlSeconds ?? 3600, P = O.isProdRuntime ?? (() => !1), F = O.enableCredits ?? !1, I = _("transport", O.product), L = (e) => async (t) => {
31
- t.messageId && await f(t.workspaceId, t.sessionId, t.messageId, t.model, t.tokens, e);
31
+ t.messageId && await f(t.workspaceId, t.sessionId, t.messageId, t.model, t.tokens, e, O.product, O.aiCreditLedger);
32
32
  }, R = new h({
33
33
  product: O.product,
34
34
  mode: A,
@@ -141,7 +141,7 @@ function O(O, k) {
141
141
  let e = await b(I, n, L(r));
142
142
  if (!e.ok) throw x(e.kind);
143
143
  }
144
- return F && l(n, r).catch((e) => {
144
+ return F && l(n, r, O.product, O.aiCreditLedger).catch((e) => {
145
145
  t(e) && O.onQuotaExhausted?.(e);
146
146
  }), B(e, n, r, 1);
147
147
  },
@@ -133,6 +133,7 @@ export * from './ui/AssistantMarkdownRenderer';
133
133
  export * from './ui/AssistantMessageAttachments';
134
134
  export * from './ui/AssistantPendingWriteCard';
135
135
  export * from './ui/AssistantWriteProposalCard';
136
+ export * from './ui/AssistantAiDisclaimer';
136
137
  export * from './ui/AssistantWriteConfirmDialog';
137
138
  export * from './ui/useWriteProposalConfirm';
138
139
  export * from './ui/writeProposalPresentation';
@@ -301,6 +302,7 @@ export * from './appControls/languageReply';
301
302
  export * from './appControls/text';
302
303
  export * from './turnOutcomeCopy';
303
304
  export * from './aiCredits';
305
+ export * from './productAiCredits';
304
306
  export * from './usageReporting';
305
307
  export * from './writeProposals';
306
308
  export * from './writeReplyBinder';
@@ -40,6 +40,14 @@ export declare const STOP_MY_ASSISTANT_SANDBOX = "\n mutation StopMyAssistantSa
40
40
  export declare const EXTEND_MY_ASSISTANT_SANDBOX_TTL = "\n mutation ExtendMyAssistantSandboxTtl(\n $id: ID!\n $additionalSeconds: Int!\n $context: ContextInput!\n ) {\n extendMyAssistantSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n";
41
41
  export declare const CHECK_AI_CREDITS = "\n mutation CheckAiCredits {\n checkAiCredits\n }\n";
42
42
  export declare const REPORT_AI_TOKEN_USAGE = "\n mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }\n";
43
+ /**
44
+ * BOFF-3732: the shared, product-keyed AI-credit gate and charge in
45
+ * wspace-prompts-svc. Each works on `<productKey>.ai.credits`, so a product's
46
+ * assistant turns are billed to its own quota, never VibeControls'.
47
+ */
48
+ export declare const CHECK_AI_CREDITS_FOR = "\n mutation CheckAiCreditsFor($productKey: String!) {\n checkAiCreditsFor(productKey: $productKey) {\n provisioned\n exhausted\n remaining\n }\n }\n";
49
+ export declare const CHARGE_AI_INVOCATION = "\n mutation ChargeAiInvocation($input: PromptChargeAiInvocationInput!) {\n chargeAiInvocation(input: $input) {\n status\n reason\n credits\n }\n }\n";
50
+ export declare const AI_CREDIT_STATUS = "\n query AiCreditStatus($productKey: String!) {\n aiCreditStatus(productKey: $productKey) {\n productKey\n quotaName\n provisioned\n noLimit\n used\n limit\n remaining\n exhausted\n }\n }\n";
43
51
  /**
44
52
  * BOFF-7801: finished turns' timings for the production latency histograms
45
53
  * (wspace-sandbox-svc). Self-scoped — the actor comes from the gateway, and
@@ -1,4 +1,4 @@
1
1
  //#region src/shared/assistant/operations.ts
2
- var e = "\n mutation CreateSandbox($input: CreateSandboxInput!) {\n createSandbox(input: $input) {\n id\n name\n status\n network {\n internalUrl\n }\n }\n }\n", t = "\n query GetSandbox($id: ID!, $context: ContextInput!) {\n getSandbox(id: $id, context: $context) {\n id\n status\n errorMessage\n network {\n internalUrl\n }\n }\n }\n", n = "\n query WaitForSandboxRunning($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForSandboxRunning(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n id\n status\n errorMessage\n }\n }\n", r = "\n query WaitForAssistantReady($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForAssistantReady(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n ready\n health\n waited\n }\n }\n", i = "\n query GetActiveSandboxes($context: ContextInput!) {\n getActiveSandboxes(context: $context) {\n id\n name\n status\n metadata\n template\n createdBy\n }\n }\n", a = "\n mutation ProxySandboxRequest($input: ProxySandboxRequestInput!) {\n proxySandboxRequest(input: $input) {\n status\n body\n }\n }\n", o = "\n mutation StopAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", s = "\n mutation ExtendSandboxTtl($id: ID!, $additionalSeconds: Int!, $context: ContextInput!) {\n extendSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", c = "\n mutation ProvisionAssistantSandbox($input: ProvisionAssistantSandboxInput!) {\n provisionAssistantSandbox(input: $input) {\n id\n name\n status\n }\n }\n", l = "\n mutation StopMyAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopMyAssistantSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", u = "\n mutation ExtendMyAssistantSandboxTtl(\n $id: ID!\n $additionalSeconds: Int!\n $context: ContextInput!\n ) {\n extendMyAssistantSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", d = "\n mutation CheckAiCredits {\n checkAiCredits\n }\n", f = "\n mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }\n", p = "\n mutation ReportMyAssistantTurnTimings($input: ReportAssistantTurnTimingsInput!) {\n reportMyAssistantTurnTimings(input: $input) {\n enabled\n accepted\n rejected\n duplicate\n rateLimited\n }\n }\n";
2
+ var e = "\n mutation CreateSandbox($input: CreateSandboxInput!) {\n createSandbox(input: $input) {\n id\n name\n status\n network {\n internalUrl\n }\n }\n }\n", t = "\n query GetSandbox($id: ID!, $context: ContextInput!) {\n getSandbox(id: $id, context: $context) {\n id\n status\n errorMessage\n network {\n internalUrl\n }\n }\n }\n", n = "\n query WaitForSandboxRunning($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForSandboxRunning(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n id\n status\n errorMessage\n }\n }\n", r = "\n query WaitForAssistantReady($id: ID!, $timeoutSeconds: Int, $context: ContextInput!) {\n waitForAssistantReady(id: $id, timeoutSeconds: $timeoutSeconds, context: $context) {\n ready\n health\n waited\n }\n }\n", i = "\n query GetActiveSandboxes($context: ContextInput!) {\n getActiveSandboxes(context: $context) {\n id\n name\n status\n metadata\n template\n createdBy\n }\n }\n", a = "\n mutation ProxySandboxRequest($input: ProxySandboxRequestInput!) {\n proxySandboxRequest(input: $input) {\n status\n body\n }\n }\n", o = "\n mutation StopAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", s = "\n mutation ExtendSandboxTtl($id: ID!, $additionalSeconds: Int!, $context: ContextInput!) {\n extendSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", c = "\n mutation ProvisionAssistantSandbox($input: ProvisionAssistantSandboxInput!) {\n provisionAssistantSandbox(input: $input) {\n id\n name\n status\n }\n }\n", l = "\n mutation StopMyAssistantSandbox($id: ID!, $context: ContextInput!) {\n stopMyAssistantSandbox(id: $id, context: $context) {\n id\n status\n }\n }\n", u = "\n mutation ExtendMyAssistantSandboxTtl(\n $id: ID!\n $additionalSeconds: Int!\n $context: ContextInput!\n ) {\n extendMyAssistantSandboxTTL(id: $id, additionalSeconds: $additionalSeconds, context: $context) {\n id\n expiresAt\n }\n }\n", d = "\n mutation CheckAiCredits {\n checkAiCredits\n }\n", f = "\n mutation ReportAiTokenUsage($input: ReportAiTokenUsageInput!) {\n reportAiTokenUsage(input: $input)\n }\n", p = "\n mutation CheckAiCreditsFor($productKey: String!) {\n checkAiCreditsFor(productKey: $productKey) {\n provisioned\n exhausted\n remaining\n }\n }\n", m = "\n mutation ChargeAiInvocation($input: PromptChargeAiInvocationInput!) {\n chargeAiInvocation(input: $input) {\n status\n reason\n credits\n }\n }\n", h = "\n query AiCreditStatus($productKey: String!) {\n aiCreditStatus(productKey: $productKey) {\n productKey\n quotaName\n provisioned\n noLimit\n used\n limit\n remaining\n exhausted\n }\n }\n", g = "\n mutation ReportMyAssistantTurnTimings($input: ReportAssistantTurnTimingsInput!) {\n reportMyAssistantTurnTimings(input: $input) {\n enabled\n accepted\n rejected\n duplicate\n rateLimited\n }\n }\n";
3
3
  //#endregion
4
- export { d as CHECK_AI_CREDITS, e as CREATE_SANDBOX, u as EXTEND_MY_ASSISTANT_SANDBOX_TTL, s as EXTEND_SANDBOX_TTL, i as GET_ACTIVE_SANDBOXES, t as GET_SANDBOX, c as PROVISION_ASSISTANT_SANDBOX, a as PROXY_SANDBOX_REQUEST, f as REPORT_AI_TOKEN_USAGE, p as REPORT_MY_ASSISTANT_TURN_TIMINGS, o as STOP_ASSISTANT_SANDBOX, l as STOP_MY_ASSISTANT_SANDBOX, r as WAIT_FOR_ASSISTANT_READY, n as WAIT_FOR_SANDBOX_RUNNING };
4
+ export { h as AI_CREDIT_STATUS, m as CHARGE_AI_INVOCATION, d as CHECK_AI_CREDITS, p as CHECK_AI_CREDITS_FOR, e as CREATE_SANDBOX, u as EXTEND_MY_ASSISTANT_SANDBOX_TTL, s as EXTEND_SANDBOX_TTL, i as GET_ACTIVE_SANDBOXES, t as GET_SANDBOX, c as PROVISION_ASSISTANT_SANDBOX, a as PROXY_SANDBOX_REQUEST, f as REPORT_AI_TOKEN_USAGE, g as REPORT_MY_ASSISTANT_TURN_TIMINGS, o as STOP_ASSISTANT_SANDBOX, l as STOP_MY_ASSISTANT_SANDBOX, r as WAIT_FOR_ASSISTANT_READY, n as WAIT_FOR_SANDBOX_RUNNING };
@@ -0,0 +1,28 @@
1
+ import { UsageReport } from './usageReporting';
2
+ /** How a workspace stands against `<productKey>.ai.credits`. */
3
+ export interface AiCreditStatus {
4
+ productKey: string;
5
+ quotaName: string;
6
+ /** False when the workspace has no assignment for the quota: AI is not metered there. */
7
+ provisioned: boolean;
8
+ noLimit: boolean;
9
+ used: number | null;
10
+ limit: number | null;
11
+ remaining: number | null;
12
+ exhausted: boolean;
13
+ }
14
+ /** The operation key the shared ledger stores for an assistant turn. */
15
+ export declare const ASSISTANT_TURN_OPERATION = "assistant.turn";
16
+ /**
17
+ * A `reportTokenUsage` for `AssistantProductProvider` that charges the
18
+ * product's own AI credits. A turn without a message id or model is skipped:
19
+ * the server needs the first for idempotency and the second for the price.
20
+ */
21
+ export declare function createAiCreditReporter(productKey: string): (report: UsageReport) => Promise<void>;
22
+ /** Parse an `aiCreditStatus` payload; anything malformed reads as null. */
23
+ export declare function parseAiCreditStatus(value: unknown): AiCreditStatus | null;
24
+ /**
25
+ * The workspace's remaining AI credits for a product, for showing the balance
26
+ * before an AI action. Resolves null when it cannot be read; never throws.
27
+ */
28
+ export declare function fetchAiCreditStatus(productKey: string, workspaceId?: string): Promise<AiCreditStatus | null>;
@@ -0,0 +1,64 @@
1
+ import { graphqlFetch as e } from "../graphql/fetch.js";
2
+ import { AI_CREDIT_STATUS as t, CHARGE_AI_INVOCATION as n } from "./operations.js";
3
+ //#region src/shared/assistant/productAiCredits.ts
4
+ var r = "assistant.turn";
5
+ function i(t) {
6
+ return async (i) => {
7
+ let { workspaceId: a, sessionId: o, messageId: s, model: c, tokens: l } = i;
8
+ if (!s || !c) return;
9
+ let u = Math.max(0, Math.round(l.input ?? 0)), d = Math.max(0, Math.round(l.output ?? 0)), f = Math.max(0, Math.round(l.cache?.read ?? 0)), p = Math.max(0, Math.round(l.cache?.write ?? 0));
10
+ u + d + f + p !== 0 && await e({
11
+ gateway: "workspace",
12
+ query: n,
13
+ operationName: "ChargeAiInvocation",
14
+ variables: { input: {
15
+ productKey: t,
16
+ sessionId: o,
17
+ messageId: s,
18
+ model: c,
19
+ inputTokens: u,
20
+ outputTokens: d,
21
+ cacheReadTokens: f,
22
+ cacheWriteTokens: p,
23
+ operationKey: r
24
+ } },
25
+ workspaceId: a,
26
+ suppressGlobalErrorEvent: !0
27
+ });
28
+ };
29
+ }
30
+ function a(e) {
31
+ return typeof e == "number" && Number.isFinite(e) ? e : null;
32
+ }
33
+ function o(e) {
34
+ if (!e || typeof e != "object") return null;
35
+ let t = e.aiCreditStatus;
36
+ if (!t || typeof t != "object") return null;
37
+ let n = t;
38
+ return typeof n.productKey != "string" || typeof n.quotaName != "string" || typeof n.provisioned != "boolean" || typeof n.noLimit != "boolean" || typeof n.exhausted != "boolean" ? null : {
39
+ productKey: n.productKey,
40
+ quotaName: n.quotaName,
41
+ provisioned: n.provisioned,
42
+ noLimit: n.noLimit,
43
+ used: a(n.used),
44
+ limit: a(n.limit),
45
+ remaining: a(n.remaining),
46
+ exhausted: n.exhausted
47
+ };
48
+ }
49
+ async function s(n, r) {
50
+ try {
51
+ return o(await e({
52
+ gateway: "workspace",
53
+ query: t,
54
+ operationName: "AiCreditStatus",
55
+ variables: { productKey: n },
56
+ workspaceId: r ?? !0,
57
+ suppressGlobalErrorEvent: !0
58
+ }));
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ //#endregion
64
+ export { r as ASSISTANT_TURN_OPERATION, i as createAiCreditReporter, s as fetchAiCreditStatus, o as parseAiCreditStatus };
@@ -30,6 +30,8 @@ export type ProductAssistantMode = string & {
30
30
  };
31
31
  export type AssistantMode = BuiltInAssistantMode | ProductAssistantMode;
32
32
  /** Local mirror of a product's PageContext; the transport forwards a subset. */
33
+ /** Where a product's assistant AI credits are gated and charged (see `aiCreditLedger`). */
34
+ export type AiCreditLedger = 'shared' | 'product-service';
33
35
  export interface PageContext {
34
36
  currentUrl?: string;
35
37
  currentPage?: string;
@@ -164,6 +166,19 @@ export interface SandboxAssistantConfig {
164
166
  * pauses the next send until it is (`usageReporting.ts`).
165
167
  */
166
168
  enableCredits?: boolean;
169
+ /**
170
+ * Which service gates and charges this product's AI credits (BOFF-3732).
171
+ *
172
+ * - `'shared'` (default): wspace-prompts-svc's product-keyed
173
+ * `checkAiCreditsFor` / `chargeAiInvocation`, against
174
+ * `<product>.ai.credits`.
175
+ * - `'product-service'`: the product's own service declares
176
+ * `checkAiCredits` / `reportAiTokenUsage` and charges its own quota. Only a
177
+ * product whose service really owns those mutations may set it: they are
178
+ * declared once in the supergraph, so any other product setting it bills
179
+ * that service's quota, not its own.
180
+ */
181
+ aiCreditLedger?: AiCreditLedger;
167
182
  /** Overall per-turn deadline. Default 420_000ms. */
168
183
  streamBudgetMs?: number;
169
184
  /**
@@ -0,0 +1,26 @@
1
+ import { FC } from 'react';
2
+ export interface AssistantAiDisclaimerProps {
3
+ /** Extra layout classes from the mount point (spacing, borders). */
4
+ className?: string;
5
+ /** Distinguishes the mounts in tests and in Playwright checks. */
6
+ testId?: string;
7
+ /** Centred under a composer; start-aligned inside a card. */
8
+ align?: 'center' | 'start';
9
+ }
10
+ /**
11
+ * The standing "AI can make mistakes" line (BOFF-3720, AI_READINESS 5.3).
12
+ *
13
+ * ★★ Standing and non-dismissable by design: there is no close control, no
14
+ * prop that hides it and no stored "seen" flag. It sits under the composer of
15
+ * every assistant panel and on every write-proposal card, so it is on screen
16
+ * whenever an answer is read or a change is approved.
17
+ *
18
+ * ★ A platform shell label, not a product-prefixed assistant key. The
19
+ * `workspaces` dictionary is layered under every product's, so one seed of
20
+ * `assistantChrome.aiDisclaimer` translates it for every product. Until that
21
+ * seed lands, and in any locale it lacks, the English renders: the line is
22
+ * never missing, only untranslated.
23
+ *
24
+ * Semantic tokens only (`text-text-muted`).
25
+ */
26
+ export declare const AssistantAiDisclaimer: FC<AssistantAiDisclaimerProps>;
@@ -0,0 +1,13 @@
1
+ import { useAssistantDisclaimerLabel as e } from "../../providers/shell/useAssistantShellLabels.js";
2
+ import { jsx as t } from "react/jsx-runtime";
3
+ //#region src/shared/assistant/ui/AssistantAiDisclaimer.tsx
4
+ var n = ({ className: n, testId: r = "assistant-ai-disclaimer", align: i = "center" }) => {
5
+ let a = e();
6
+ return /* @__PURE__ */ t("p", {
7
+ className: `${i === "center" ? "text-center" : "text-start"} text-[11px] leading-snug text-text-muted${n ? ` ${n}` : ""}`,
8
+ "data-testid": r,
9
+ children: a
10
+ });
11
+ };
12
+ //#endregion
13
+ export { n as AssistantAiDisclaimer };