@bike4mind/cli 3.4.0 → 4.0.0

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.
@@ -22,7 +22,7 @@ import { homedir as homedir$1 } from "node:os";
22
22
  const APP_NAME = process.env.APP_NAME || "";
23
23
  const WEBSITE_URL = process.env.WEBSITE_URL || "";
24
24
  //#endregion
25
- //#region ../../b4m-core/common/dist/skillMdSource-DL0mQJK_.mjs
25
+ //#region ../../b4m-core/common/dist/skillMdSource-CNWRf6Lw.mjs
26
26
  /**
27
27
  * The marker an exported SKILL.md carries so Bike4Mind can recognize its own exports. The B4M
28
28
  * CLI/desktop loader (packages/cli CustomCommandStore) skips stamped files, and a future CLI sync
@@ -34,7 +34,7 @@ const WEBSITE_URL = process.env.WEBSITE_URL || "";
34
34
  const SKILL_MD_SOURCE_MARKER = "Bike4Mind-Source:";
35
35
  /** Opening `---<lang>` fence, the frontmatter text, then the closing `---` line. */
36
36
  const FRONTMATTER = /^---([^\r\n]*)\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
37
- const SOURCE_LINE = new RegExp(`^[ \\t]*#[ \\t]*${SKILL_MD_SOURCE_MARKER}[ \\t]*(\\S*)`);
37
+ const SOURCE_LINE = new RegExp(`^[ \\t]*#[ \\t]*${SKILL_MD_SOURCE_MARKER}[ \\t]*(\\S+)`);
38
38
  /** Splits a SKILL.md into its raw frontmatter and the rest; null when there is no closed fence. */
39
39
  function splitSkillMdFrontmatter(text) {
40
40
  if (text.charCodeAt(0) === 65279) text = text.slice(1);
@@ -791,6 +791,22 @@ const CHAT_HISTORY_ITEM_TYPES = [
791
791
  "system",
792
792
  "voice_transcript"
793
793
  ];
794
+ /**
795
+ * Why a turn's LLM call was retried. Timeouts we raise ourselves are classified ahead of
796
+ * overload so they never read as a provider fault (older backends worded them "may be
797
+ * overloaded"). Must stay in sync with classifyTurnRetryReason in @bike4mind/services.
798
+ */
799
+ const TURN_RETRY_REASONS = [
800
+ "stream_idle_timeout",
801
+ "stream_truncated",
802
+ "request_timeout",
803
+ "rate_limit",
804
+ "overloaded",
805
+ "server_error",
806
+ "transport",
807
+ "tool_pairing",
808
+ "other"
809
+ ];
794
810
  /** Where a session was created. Absent on sessions that predate the field; render those as 'web'. */
795
811
  const SESSION_ORIGIN_CHANNELS = [
796
812
  "web",
@@ -849,6 +865,12 @@ function parseClaudeModelId(modelId) {
849
865
  minor: minor === void 0 ? null : Number(minor)
850
866
  };
851
867
  }
868
+ /** `opus-4-5` for `claude-opus-4-5-20251101` or `global.anthropic.claude-opus-4-5-...`; null otherwise. */
869
+ function claudeFamilyVersion(modelId) {
870
+ const parsed = parseClaudeModelId(modelId);
871
+ if (!parsed) return null;
872
+ return parsed.minor === null ? `${parsed.family}-${parsed.major}` : `${parsed.family}-${parsed.major}-${parsed.minor}`;
873
+ }
852
874
  /**
853
875
  * Model backends
854
876
  */
@@ -1022,6 +1044,7 @@ let ChatModels = /* @__PURE__ */ function(ChatModels) {
1022
1044
  ChatModels["TINYLLAMA"] = "tinyllama";
1023
1045
  ChatModels["DEEPSEEK_R1_BEDROCK"] = "us.deepseek.r1-v1:0";
1024
1046
  ChatModels["DEEPSEEK_V3_1"] = "deepseek.v3-v1:0";
1047
+ ChatModels["NOVA_PRO_BEDROCK"] = "us.amazon.nova-pro-v1:0";
1025
1048
  ChatModels["TITAN_TEXT_G1_LITE"] = "amazon.titan-text-lite-v1";
1026
1049
  ChatModels["TITAN_TEXT_G1_EXPRESS"] = "amazon.titan-text-express-v1";
1027
1050
  ChatModels["CLAUDE_3_HAIKU_BEDROCK"] = "anthropic.claude-3-haiku-20240307-v1:0";
@@ -1149,6 +1172,173 @@ z$1.enum({
1149
1172
  ...ImageModels,
1150
1173
  ...SpeechToTextModels
1151
1174
  });
1175
+ let ApiKeyScope = /* @__PURE__ */ function(ApiKeyScope) {
1176
+ ApiKeyScope["READ_NOTEBOOKS"] = "notebooks:read";
1177
+ ApiKeyScope["WRITE_NOTEBOOKS"] = "notebooks:write";
1178
+ ApiKeyScope["READ_FILES"] = "files:read";
1179
+ ApiKeyScope["WRITE_FILES"] = "files:write";
1180
+ ApiKeyScope["AI_GENERATE"] = "ai:generate";
1181
+ /**
1182
+ * Only the Decisions API (`POST /api/v1/decisions`, `GET /api/v1/decision-models`): typed questions answered
1183
+ * with probabilities. `ai:generate` also opens these routes. Not confined, so like `me:read` it also passes
1184
+ * scope-less routes; an image sent by `file_id` additionally needs `files:read`.
1185
+ */
1186
+ ApiKeyScope["AI_DECIDE"] = "ai:decide";
1187
+ ApiKeyScope["AI_CHAT"] = "ai:chat";
1188
+ ApiKeyScope["READ_PROJECTS"] = "projects:read";
1189
+ ApiKeyScope["WRITE_PROJECTS"] = "projects:write";
1190
+ /** List and read the key owner's agents (and agents shared with them). */
1191
+ ApiKeyScope["READ_AGENTS"] = "agents:read";
1192
+ /**
1193
+ * Create, update, delete and fund agents, manage their embed keys, and run the
1194
+ * agent-authoring assistants (description/avatar/system-prompt/field generation),
1195
+ * which spend the owner's credits on the agent's behalf.
1196
+ */
1197
+ ApiKeyScope["WRITE_AGENTS"] = "agents:write";
1198
+ /** Authorizes only the cc-bridge WS actions (cc_agent_register /
1199
+ * cc_agent_event / cc_agent_disconnect). Keys with this scope CANNOT
1200
+ * call chat/completions - a leaked bridge key has the narrow blast
1201
+ * radius of a sprite-spawning credential, not a billable AI key. */
1202
+ ApiKeyScope["CC_BRIDGE"] = "cc-bridge:connect";
1203
+ /**
1204
+ * Read the key owner's OWN commercial state - tier, credit balance, entitlement
1205
+ * keys - via `GET /api/v1/me`. Split from the AI scopes on purpose: a key minted
1206
+ * to generate text has no business enumerating what its owner has paid for. It
1207
+ * reaches only `GET /api/v1/me` and its balance-only subset `GET /api/v1/credits`
1208
+ * (which the AI scopes also open, since spend needs a pre-flight check), so it
1209
+ * carries the `:read` suffix that puts it in the New-Key modal's read-only preset.
1210
+ */
1211
+ ApiKeyScope["ME_READ"] = "me:read";
1212
+ ApiKeyScope["ADMIN"] = "admin:*";
1213
+ ApiKeyScope["MARKETING_REPORTS_READ"] = "marketing-reports:read";
1214
+ ApiKeyScope["MARKETING_REPORTS_WRITE"] = "marketing-reports:write";
1215
+ /** Server-to-server ingest scope for Overwatch analytics. Admin-provisioned only - never shown in user-facing key creation UI. */
1216
+ ApiKeyScope["OVERWATCH_INGEST_WRITE"] = "overwatch-ingest:write";
1217
+ /** Authorizes only embedded-widget chat against the single agent the key is
1218
+ * bound to (`agentId`). Like CC_BRIDGE, a leaked embed key has a narrow blast
1219
+ * radius: it can talk to one agent from allow-listed origins, nothing else. */
1220
+ ApiKeyScope["EMBED_CHAT"] = "embed:chat";
1221
+ /** Read a Hearth channel list and replay its events. Read-only: it cannot
1222
+ * advance an actor cursor, because consuming events out from under a
1223
+ * legitimate agent reader is a denial of context, not a read. */
1224
+ ApiKeyScope["HEARTH_READ"] = "hearth:read";
1225
+ /** Append Hearth events, create channels, and advance actor cursors. */
1226
+ ApiKeyScope["HEARTH_WRITE"] = "hearth:write";
1227
+ /**
1228
+ * Read OptiHashi problems, runs, and run artifacts. Deliberately split from
1229
+ * {@link OPTIHASHI_COMPUTE}: a key that only inspects results is structurally
1230
+ * unable to start a billable solve, so an agent-held key can be handed the
1231
+ * read half alone. Like every scope, it authorizes but never entitles - the
1232
+ * OptiHashi entitlement check still runs and can refuse on its own.
1233
+ */
1234
+ ApiKeyScope["OPTIHASHI_READ"] = "optihashi:read";
1235
+ /**
1236
+ * Submit OptiHashi compute (solve/decompose/schedule) and cancel a run. This
1237
+ * is the spend half of the OptiHashi pair: it commissions external compute
1238
+ * that costs real money, so it is never implied by {@link OPTIHASHI_READ}.
1239
+ */
1240
+ ApiKeyScope["OPTIHASHI_COMPUTE"] = "optihashi:compute";
1241
+ /**
1242
+ * Read data lakes and their contents: list/browse lakes (including keyword search over
1243
+ * articles), read a lake's health, spend, batches, and access view. Split from
1244
+ * {@link DATALAKE_WRITE} so a key handed to an agent can look at a lake without being able to
1245
+ * change one. A retrieval query that spends LLM/search budget (semantic search, the RLM answer
1246
+ * endpoint) needs {@link DATALAKE_QUERY} instead: this scope does not imply it, though a
1247
+ * {@link DATALAKE_QUERY} key does gain this one (it has to, to reach the read-gated routes its
1248
+ * own retrieval tools call back into).
1249
+ */
1250
+ ApiKeyScope["DATALAKE_READ"] = "datalake:read";
1251
+ /**
1252
+ * Change what is IN a lake or how it behaves: create/update/archive a lake,
1253
+ * attach, detach, retag, or purge its files, run converge/rechunk/research,
1254
+ * and manage upload batches. Deliberately does NOT carry
1255
+ * {@link DATALAKE_SHARE}: adding files to a lake is a different privilege
1256
+ * from handing the lake to someone else.
1257
+ */
1258
+ ApiKeyScope["DATALAKE_WRITE"] = "datalake:write";
1259
+ /**
1260
+ * Change WHO can reach a lake: its visibility and its ownership (and the
1261
+ * grant/revoke door). Never implied by {@link DATALAKE_WRITE} - re-sharing a
1262
+ * lake widens the blast radius of every document already in it, which is not
1263
+ * what a key minted to keep a lake's files current asked for.
1264
+ */
1265
+ ApiKeyScope["DATALAKE_SHARE"] = "datalake:share";
1266
+ /**
1267
+ * Run a retrieval query that spends LLM/search budget against a lake: semantic search and the
1268
+ * RLM answer endpoint. Deliberately NOT suffixed `:read` - {@link DATALAKE_READ} feeds the
1269
+ * New-Key modal's "Read-only" preset (`s.value.endsWith(':read')`), and a key an operator mints
1270
+ * expecting that preset to be free must not auto-join a scope that commissions billable work.
1271
+ * Mirrors {@link OPTIHASHI_COMPUTE}'s split from {@link OPTIHASHI_READ}. Never implied by
1272
+ * {@link DATALAKE_READ} or {@link DATALAKE_WRITE} - spend on a lake is opt-in on its own. The
1273
+ * implication runs the other way for gating purposes: a key holding only this scope also passes
1274
+ * {@link DATALAKE_READ}'s gate, because the RLM answer endpoint's in-REPL tools call back into
1275
+ * read-gated routes (e.g. GET /api/data-lakes/articles) with the caller's own credential.
1276
+ */
1277
+ ApiKeyScope["DATALAKE_QUERY"] = "datalake:query";
1278
+ /**
1279
+ * Read the Overwatch analytics surface - the cross-product overview, product
1280
+ * inventory and config, first-party metrics, product-stat history, funnel,
1281
+ * and pipeline freshness. Exists so an agent can be handed a credential that
1282
+ * can only *look*: it is deliberately NOT the read half of
1283
+ * {@link OVERWATCH_INGEST_WRITE}, which is a per-product ingest credential
1284
+ * bound to one `productId` and able to write that product's numbers. Handing
1285
+ * an explorer the ingest key would let it fabricate the very stats it reports
1286
+ * on, so the two are separate scopes rather than a read/write pair.
1287
+ *
1288
+ * Like every scope, it authorizes but never entitles - the Overwatch access
1289
+ * check (`requestHasOverwatchAccess`: admin OR developer OR `overwatch:pro`)
1290
+ * still runs against the key's owner and can refuse on its own. A key minted
1291
+ * with this scope by a user who does not hold Overwatch access opens nothing.
1292
+ * That check and the routes it guards live in the Overwatch overlay package;
1293
+ * neither has an implementation in this repository, so nothing here verifies
1294
+ * the claim - it is a property of the consumer, recorded for the reader.
1295
+ */
1296
+ ApiKeyScope["OVERWATCH_READ"] = "overwatch:read";
1297
+ /**
1298
+ * CI-to-server ingest of Playwright runs for the admin /status page
1299
+ * (`POST /api/qa/uploads`, `POST /api/qa/runs`). Admin-provisioned only,
1300
+ * and confined like {@link OVERWATCH_INGEST_WRITE}: a leaked CI key reaches the
1301
+ * two ingest routes and nothing else. The routes also require the key's owner
1302
+ * to carry the `qa-ingest` user tag (QA_INGEST_USER_TAG in schemas/qa.ts).
1303
+ */
1304
+ ApiKeyScope["QA_INGEST"] = "qa:ingest";
1305
+ /**
1306
+ * List and download the Claude Code plugin feed of an org's approved Skill Market listings
1307
+ * (`/api/v1/skills/market/*`). Confined: the key ends up in plaintext in `~/.claude/settings.json`,
1308
+ * so it must reach nothing else. Minted from the Skill Market's "Use in Claude Code" dialog.
1309
+ */
1310
+ ApiKeyScope["SKILLS_READ"] = "skills:read";
1311
+ /**
1312
+ * Release-note automation: find a release note and set its video, and upload the What's New
1313
+ * media for it. Admin-provisioned and confined so the job needs no `admin:*`
1314
+ * key: it reaches only the routes that name it, and those still require a platform-admin owner.
1315
+ */
1316
+ ApiKeyScope["RELEASE_NOTES_WRITE"] = "release-notes:write";
1317
+ /**
1318
+ * Read the key owner's own Bob results: single-site reads, two-site comparisons and
1319
+ * re-checks, the recent-reports list, and how many runs are left. It starts nothing and
1320
+ * spends nothing, so the `:read` suffix that puts it in the New-Key modal's Read-only
1321
+ * preset is deliberate, and safe there because every read is scoped to the key's owner.
1322
+ * Split from {@link BOB_RUN} so an agent can be handed the read half alone.
1323
+ *
1324
+ * Like every scope, it authorizes but never entitles. The Bob routes, and the gate that
1325
+ * decides which of the two scopes each one accepts, live in the Bob overlay package and
1326
+ * have no implementation in this repository, so nothing here verifies either claim.
1327
+ */
1328
+ ApiKeyScope["BOB_READ"] = "bob:read";
1329
+ /**
1330
+ * Start a Bob site read, a two-site comparison or a re-check. This is the spend half of
1331
+ * the Bob pair: it spends the key owner's credits and publishes a report as them, so it
1332
+ * is never implied by {@link BOB_READ}, and it is deliberately unsuffixed so it stays out
1333
+ * of the Read-only and Read & write presets, as {@link OPTIHASHI_COMPUTE} does. Nor does
1334
+ * it imply {@link BOB_READ} at this repository's scope gate.
1335
+ *
1336
+ * Authorizes without entitling, like {@link BOB_READ}: the gate that enforces it lives in
1337
+ * the Bob overlay package, with no implementation here.
1338
+ */
1339
+ ApiKeyScope["BOB_RUN"] = "bob:run";
1340
+ return ApiKeyScope;
1341
+ }({});
1152
1342
  Object.freeze({
1153
1343
  requestsPerMinute: 60,
1154
1344
  requestsPerDay: 1e3
@@ -1280,6 +1470,42 @@ const GenericCreditAddTransaction = BaseCreditTransaction.extend({
1280
1470
  transactionId: z$1.string().optional()
1281
1471
  });
1282
1472
  /**
1473
+ * Built-in reasons for generic credit deductions; they seed the registry below.
1474
+ * A package that needs a new reason registers it with registerGenericDeductReason
1475
+ * rather than extending this list.
1476
+ */
1477
+ const GenericDeductReasons = [
1478
+ "dispute_clawback",
1479
+ "refund_clawback",
1480
+ "payment_failed_clawback",
1481
+ "notebook_curation",
1482
+ "manual",
1483
+ "admin_adjustment",
1484
+ "refund_adjustment",
1485
+ "optihashi_reservation",
1486
+ "optihashi_cost_chargeup",
1487
+ "credit_expiry"
1488
+ ];
1489
+ const GENERIC_DEDUCT_REASON_PATTERN = /^[a-z][a-z0-9_]*$/;
1490
+ const GENERIC_DEDUCT_REASON_REGISTRY_KEY = Symbol.for("b4m.credit.genericDeductReasons");
1491
+ let seededRegistry;
1492
+ function genericDeductReasonRegistry() {
1493
+ const holder = globalThis;
1494
+ let registry = holder[GENERIC_DEDUCT_REASON_REGISTRY_KEY];
1495
+ if (!registry) {
1496
+ registry = /* @__PURE__ */ new Set();
1497
+ holder[GENERIC_DEDUCT_REASON_REGISTRY_KEY] = registry;
1498
+ }
1499
+ if (seededRegistry !== registry) {
1500
+ for (const reason of GenericDeductReasons) registry.add(reason);
1501
+ seededRegistry = registry;
1502
+ }
1503
+ return registry;
1504
+ }
1505
+ function isGenericDeductReason(value) {
1506
+ return typeof value === "string" && GENERIC_DEDUCT_REASON_PATTERN.test(value) && genericDeductReasonRegistry().has(value);
1507
+ }
1508
+ /**
1283
1509
  * Generic credit deduction transaction for legacy data and flexible use cases
1284
1510
  * Represents any credit deduction that doesn't fit specific categories
1285
1511
  * (e.g., admin adjustments, legacy usage, manual deductions)
@@ -1291,20 +1517,10 @@ const GenericCreditDeductTransaction = BaseCreditTransaction.extend({
1291
1517
  */
1292
1518
  userId: z$1.string().optional(),
1293
1519
  /**
1294
- * Reason/source for the transaction. Use GenericDeductReasons for new callers.
1520
+ * Reason/source for the transaction: a built-in GenericDeductReasons value or one added with
1521
+ * registerGenericDeductReason.
1295
1522
  */
1296
- reason: z$1.enum([
1297
- "dispute_clawback",
1298
- "refund_clawback",
1299
- "payment_failed_clawback",
1300
- "notebook_curation",
1301
- "manual",
1302
- "admin_adjustment",
1303
- "refund_adjustment",
1304
- "optihashi_reservation",
1305
- "optihashi_cost_chargeup",
1306
- "credit_expiry"
1307
- ]).optional(),
1523
+ reason: z$1.custom(isGenericDeductReason, { error: (issue) => typeof issue.input === "string" ? "Unregistered generic deduct reason" : `Expected a generic deduct reason string, received ${issue.input === null ? "null" : typeof issue.input}` }).optional(),
1308
1524
  /**
1309
1525
  * Stripe dispute ID - set for dispute clawback transactions.
1310
1526
  * Used for idempotency (unique sparse index prevents duplicate clawbacks).
@@ -1472,6 +1688,25 @@ const USAGE_EVENT_STATUSES = [
1472
1688
  "refusal",
1473
1689
  "degenerate"
1474
1690
  ];
1691
+ /** Where a UsageEvent's rates came from: a ModelPrice catalog row, or the adapter literal fallback. */
1692
+ const USAGE_PRICE_SOURCES = ["catalog", "literal"];
1693
+ /** USD per token for the pricing tier that priced a call (see AppliedRates in models.ts). */
1694
+ const UsageEventRates = z$1.object({
1695
+ /** The pricing-map key (input-token threshold) of the tier applied. */
1696
+ tierKey: z$1.number(),
1697
+ input: z$1.number(),
1698
+ output: z$1.number(),
1699
+ cacheRead: z$1.number().optional(),
1700
+ cacheWrite: z$1.number().optional()
1701
+ });
1702
+ /** One provider request of a multi-turn completion, with the tier it was priced on. */
1703
+ const UsageEventTurn = z$1.object({
1704
+ inputTokens: z$1.number(),
1705
+ outputTokens: z$1.number(),
1706
+ cacheReadTokens: z$1.number().optional(),
1707
+ cacheWriteTokens: z$1.number().optional(),
1708
+ tierKey: z$1.number()
1709
+ });
1475
1710
  z$1.object({
1476
1711
  id: z$1.string().optional(),
1477
1712
  /** App-level correlation id: questId for chat/image/video, session/run id otherwise. */
@@ -1517,6 +1752,30 @@ z$1.object({
1517
1752
  source: z$1.enum(COMPLETION_SOURCES).optional(),
1518
1753
  /** API key that authed the call; present only for API-key-authed usage. */
1519
1754
  apiKeyId: z$1.string().optional(),
1755
+ /** 'catalog' when a ModelPrice row priced the call, 'literal' when the adapter literal did. */
1756
+ priceSource: z$1.enum(USAGE_PRICE_SOURCES).optional(),
1757
+ /** The ModelPrice row in force; set only when priceSource is 'catalog'. */
1758
+ priceRowId: z$1.string().optional(),
1759
+ /**
1760
+ * Rates of the tier applied. A multi-turn row whose turns hit different tiers carries the
1761
+ * tier of its largest turn; turnUsage has each turn's own tierKey, but not that tier's rates,
1762
+ * so only a single-tier text charge is recomputable from its own fields. Provider-basis chat
1763
+ * rows use providerInputTokens/providerOutputTokens plus the cache buckets. Local-estimate chat
1764
+ * rows use input * (inputTokens - cachedInputTokens) + cacheRead * cachedInputTokens
1765
+ * + output * outputTokens. Operational and CLI rows use disjoint input/cache buckets instead:
1766
+ * input * inputTokens + cacheRead * cachedInputTokens + cacheWrite * cacheWriteTokens
1767
+ * + output * outputTokens, even when settledBasis is 'local' or unset.
1768
+ * Local-estimate chat rows are inexact when a fallback uses a different tokenEstimateMultiplier:
1769
+ * inputTokens remains in the requested model's units. Tool spend on other models is not covered.
1770
+ */
1771
+ rates: UsageEventRates.optional(),
1772
+ /**
1773
+ * Per-turn usage, written only when the completion settled over more than one provider
1774
+ * request (tool loops bill turn by turn). Capped at USAGE_EVENT_TURN_USAGE_CAP entries.
1775
+ */
1776
+ turnUsage: z$1.array(UsageEventTurn).optional(),
1777
+ /** True when turnUsage was capped: its last entry is the sum of every turn from there on. */
1778
+ turnUsageTruncated: z$1.boolean().optional(),
1520
1779
  createdAt: z$1.date(),
1521
1780
  updatedAt: z$1.date()
1522
1781
  });
@@ -1589,6 +1848,7 @@ const ADAPTER_FAMILIES = [
1589
1848
  "bedrock-moonshot",
1590
1849
  "bedrock-jurassic",
1591
1850
  "bedrock-titan",
1851
+ "bedrock-converse",
1592
1852
  "gemini",
1593
1853
  "xai",
1594
1854
  "kimi",
@@ -1660,6 +1920,8 @@ const LIFECYCLE_STATUSES = [...[
1660
1920
  "retired"
1661
1921
  ], "unlisted"];
1662
1922
  const LifecycleWrite = z$1.strictObject({
1923
+ /** Absent on v1 rows means current; true folds the model without disabling it. */
1924
+ isSuperseded: z$1.boolean().optional(),
1663
1925
  status: z$1.enum(LIFECYCLE_STATUSES),
1664
1926
  deprecationDate: CALENDAR_DATE.optional(),
1665
1927
  retirementDate: CALENDAR_DATE.optional(),
@@ -1843,6 +2105,7 @@ const MODEL_INFO_FIELD_GROUP_OF = {
1843
2105
  supportsTools: "modalities",
1844
2106
  supportsImageVariation: "modalities",
1845
2107
  supportsSafetyTolerance: "modalities",
2108
+ isSuperseded: "lifecycle",
1846
2109
  deprecationDate: "lifecycle",
1847
2110
  replacedBy: "lifecycle",
1848
2111
  adapterFamily: "dispatch",
@@ -2368,6 +2631,59 @@ z$1.object({
2368
2631
  summary: z$1.string(),
2369
2632
  counts: orgFeedbackSummaryCountsSchema
2370
2633
  });
2634
+ const MODAL_MEDIA_HOST_SUFFIXES = [".amazonaws.com", ".cloudfront.net"];
2635
+ const MODAL_MEDIA_URL_MAX_LENGTH = 2048;
2636
+ const MODAL_MEDIA_URL_HINT = "Upload the file, or paste an https URL on amazonaws.com or cloudfront.net";
2637
+ function isAllowedModalMediaUrl(url, policy = {}) {
2638
+ if (url.length > MODAL_MEDIA_URL_MAX_LENGTH) return false;
2639
+ if (policy.allowedUrls?.has(url)) return true;
2640
+ try {
2641
+ const { protocol, hostname, origin, port } = new URL(url);
2642
+ if (policy.extraOrigins?.includes(origin)) return true;
2643
+ return protocol === "https:" && port === "" && MODAL_MEDIA_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix));
2644
+ } catch {
2645
+ return false;
2646
+ }
2647
+ }
2648
+ /** Per-field problems with a modal video, empty when it is valid. Shared by the admin form and the API. */
2649
+ function getModalVideoErrors(video, policy = {}) {
2650
+ const errors = {};
2651
+ if (!video) return errors;
2652
+ for (const field of [
2653
+ "landscapeUrl",
2654
+ "landscapePosterUrl",
2655
+ "portraitUrl",
2656
+ "portraitPosterUrl"
2657
+ ]) {
2658
+ const url = video[field];
2659
+ if (url && !isAllowedModalMediaUrl(url, policy)) errors[field] = MODAL_MEDIA_URL_HINT;
2660
+ }
2661
+ if (video.landscapePosterUrl && !video.landscapeUrl) errors.landscapePosterUrl ??= "A 16:9 poster needs its 16:9 video";
2662
+ if (video.portraitPosterUrl && !video.portraitUrl) errors.portraitPosterUrl ??= "A 9:16 poster needs its 9:16 video";
2663
+ return errors;
2664
+ }
2665
+ /** The video's fields with no host check, for reading stored or upstream data written under another policy. */
2666
+ const ModalVideoShapeSchema = z$1.object({
2667
+ landscapeUrl: z$1.string().nullable().optional(),
2668
+ landscapePosterUrl: z$1.string().nullable().optional(),
2669
+ portraitUrl: z$1.string().nullable().optional(),
2670
+ portraitPosterUrl: z$1.string().nullable().optional()
2671
+ });
2672
+ /**
2673
+ * Product video for a modal slide. Landscape is the 16:9 cut shown on desktop, portrait the 9:16 cut
2674
+ * shown on mobile; the slider falls back to the other orientation when one is missing. The API passes
2675
+ * its deployment's extra origins; the bare `ModalVideoSchema` allows only the static host list.
2676
+ */
2677
+ function createModalVideoSchema(policy = {}) {
2678
+ return ModalVideoShapeSchema.superRefine((video, ctx) => {
2679
+ for (const [field, message] of Object.entries(getModalVideoErrors(video, policy))) ctx.addIssue({
2680
+ code: "custom",
2681
+ path: [field],
2682
+ message
2683
+ });
2684
+ });
2685
+ }
2686
+ createModalVideoSchema();
2371
2687
  /**
2372
2688
  * Check if a value is a placeholder (not configured or SST default).
2373
2689
  * Uses case-insensitive comparison and trims whitespace to prevent bypass attempts.
@@ -2984,18 +3300,21 @@ const AuditChangeSchema = z$1.object({
2984
3300
  before: z$1.unknown(),
2985
3301
  after: z$1.unknown()
2986
3302
  });
3303
+ /** Sources a stored AdminAuditLog row can carry; the write input and the filters stay inside this set. */
3304
+ const StoredAuditContextSourceSchema = z$1.enum([
3305
+ "admin_ui",
3306
+ "api_key",
3307
+ "script",
3308
+ "worker"
3309
+ ]);
3310
+ /** Read-side context: 'user' is the account holder's own session, which only the security-store adapters emit. */
2987
3311
  const AuditContextSchema = z$1.object({
2988
3312
  ip: z$1.string().optional(),
2989
3313
  userAgent: z$1.string().optional(),
2990
3314
  requestId: z$1.string().optional(),
2991
- source: z$1.enum([
2992
- "admin_ui",
2993
- "api_key",
2994
- "script",
2995
- "worker"
2996
- ]),
3315
+ source: StoredAuditContextSourceSchema,
2997
3316
  route: z$1.string().optional()
2998
- });
3317
+ }).extend({ source: z$1.union([StoredAuditContextSourceSchema, z$1.literal("user")]) });
2999
3318
  const AdminAuditCategorySchema = z$1.enum([
3000
3319
  "credits",
3001
3320
  "access",
@@ -3037,6 +3356,267 @@ z$1.object({
3037
3356
  backfilled: z$1.boolean().optional()
3038
3357
  });
3039
3358
  z$1.string({ error: "A reason is required" }).trim().min(5, `A reason of at least 5 characters is required`).max(500);
3359
+ /** App defaults for the AdminSettings grant thresholds (AdminAuditCriticalGrantCredits / AdminAuditDailyGrantCredits). */
3360
+ const ADMIN_AUDIT_DEFAULT_CRITICAL_GRANT_CREDITS = 5e4;
3361
+ const ADMIN_AUDIT_DEFAULT_DAILY_GRANT_CREDITS = 2e5;
3362
+ const ADMIN_AUDIT_CATALOGUE = {
3363
+ "org.credits.granted": {
3364
+ category: "credits",
3365
+ severity: "notice",
3366
+ write: "strict",
3367
+ escalatesAboveGrantThreshold: true
3368
+ },
3369
+ "org.credits.topped_up": {
3370
+ category: "credits",
3371
+ severity: "notice",
3372
+ write: "strict",
3373
+ escalatesAboveGrantThreshold: true
3374
+ },
3375
+ "org.credits.adjusted": {
3376
+ category: "credits",
3377
+ severity: "notice",
3378
+ write: "strict",
3379
+ escalatesAboveGrantThreshold: true
3380
+ },
3381
+ "org.credits.refunded": {
3382
+ category: "credits",
3383
+ severity: "notice",
3384
+ write: "strict",
3385
+ escalatesAboveGrantThreshold: true
3386
+ },
3387
+ "user.subscription.granted": {
3388
+ category: "credits",
3389
+ severity: "notice",
3390
+ write: "strict"
3391
+ },
3392
+ "user.credits.adjusted": {
3393
+ category: "credits",
3394
+ severity: "notice",
3395
+ write: "strict",
3396
+ escalatesAboveGrantThreshold: true
3397
+ },
3398
+ "user.credits.denied": {
3399
+ category: "credits",
3400
+ severity: "critical",
3401
+ write: "bestEffort"
3402
+ },
3403
+ "user.subscription.denied": {
3404
+ category: "credits",
3405
+ severity: "critical",
3406
+ write: "bestEffort"
3407
+ },
3408
+ "user.role.admin_granted": {
3409
+ category: "access",
3410
+ severity: "critical",
3411
+ write: "strict"
3412
+ },
3413
+ "user.role.admin_revoked": {
3414
+ category: "access",
3415
+ severity: "critical",
3416
+ write: "strict"
3417
+ },
3418
+ "user.role.denied": {
3419
+ category: "access",
3420
+ severity: "critical",
3421
+ write: "bestEffort"
3422
+ },
3423
+ "user.impersonation.started": {
3424
+ category: "access",
3425
+ severity: "critical",
3426
+ write: "strict"
3427
+ },
3428
+ "user.impersonation.ended": {
3429
+ category: "access",
3430
+ severity: "info",
3431
+ write: "bestEffort"
3432
+ },
3433
+ "user.impersonation.denied": {
3434
+ category: "access",
3435
+ severity: "critical",
3436
+ write: "bestEffort"
3437
+ },
3438
+ "user.deleted": {
3439
+ category: "account",
3440
+ severity: "critical",
3441
+ write: "strict"
3442
+ },
3443
+ "user.deletion.denied": {
3444
+ category: "account",
3445
+ severity: "critical",
3446
+ write: "bestEffort"
3447
+ },
3448
+ "user.profile.updated": {
3449
+ category: "account",
3450
+ severity: "notice",
3451
+ write: "bestEffort"
3452
+ },
3453
+ "org.seats.changed": {
3454
+ category: "org",
3455
+ severity: "notice",
3456
+ write: "bestEffort"
3457
+ },
3458
+ "org.seats.ceiling_raised": {
3459
+ category: "org",
3460
+ severity: "info",
3461
+ write: "bestEffort"
3462
+ },
3463
+ "org.conversion.initiated": {
3464
+ category: "org",
3465
+ severity: "notice",
3466
+ write: "bestEffort"
3467
+ },
3468
+ "org.revoked": {
3469
+ category: "org",
3470
+ severity: "notice",
3471
+ write: "strict"
3472
+ },
3473
+ "org.group_types.updated": {
3474
+ category: "org",
3475
+ severity: "info",
3476
+ write: "bestEffort"
3477
+ },
3478
+ "org.admins.updated": {
3479
+ category: "org",
3480
+ severity: "notice",
3481
+ write: "bestEffort"
3482
+ },
3483
+ "org.group_member.assigned": {
3484
+ category: "org",
3485
+ severity: "info",
3486
+ write: "bestEffort"
3487
+ },
3488
+ "org.group_member.unassigned": {
3489
+ category: "org",
3490
+ severity: "info",
3491
+ write: "bestEffort"
3492
+ },
3493
+ "org.invite.created": {
3494
+ category: "org",
3495
+ severity: "info",
3496
+ write: "bestEffort"
3497
+ },
3498
+ "org.member_budget.updated": {
3499
+ category: "org",
3500
+ severity: "notice",
3501
+ write: "bestEffort"
3502
+ },
3503
+ "settings.admin_settings.updated": {
3504
+ category: "settings",
3505
+ severity: "notice",
3506
+ write: "strict"
3507
+ },
3508
+ "settings.whats_new.updated": {
3509
+ category: "settings",
3510
+ severity: "info",
3511
+ write: "bestEffort"
3512
+ },
3513
+ "settings.whats_new.synced": {
3514
+ category: "settings",
3515
+ severity: "info",
3516
+ write: "bestEffort"
3517
+ },
3518
+ "settings.whats_new.generation_triggered": {
3519
+ category: "settings",
3520
+ severity: "info",
3521
+ write: "bestEffort"
3522
+ },
3523
+ "settings.security_scan.enabled": {
3524
+ category: "settings",
3525
+ severity: "notice",
3526
+ write: "bestEffort"
3527
+ },
3528
+ "settings.security_scan.disabled": {
3529
+ category: "settings",
3530
+ severity: "notice",
3531
+ write: "bestEffort"
3532
+ },
3533
+ "settings.security_scan.triggered": {
3534
+ category: "settings",
3535
+ severity: "notice",
3536
+ write: "bestEffort"
3537
+ },
3538
+ "security.attack_simulation.run": {
3539
+ category: "settings",
3540
+ severity: "notice",
3541
+ write: "bestEffort"
3542
+ },
3543
+ "oauth_client.created": {
3544
+ category: "settings",
3545
+ severity: "notice",
3546
+ write: "bestEffort"
3547
+ },
3548
+ "oauth_client.secret_rotated": {
3549
+ category: "settings",
3550
+ severity: "notice",
3551
+ write: "bestEffort"
3552
+ },
3553
+ "oauth_client.updated": {
3554
+ category: "settings",
3555
+ severity: "notice",
3556
+ write: "bestEffort"
3557
+ },
3558
+ "oauth_client.activated": {
3559
+ category: "settings",
3560
+ severity: "notice",
3561
+ write: "bestEffort"
3562
+ },
3563
+ "oauth_client.deactivated": {
3564
+ category: "settings",
3565
+ severity: "notice",
3566
+ write: "bestEffort"
3567
+ },
3568
+ "system_health.test_run": {
3569
+ category: "settings",
3570
+ severity: "info",
3571
+ write: "bestEffort"
3572
+ },
3573
+ "user.email.verified": {
3574
+ category: "identity",
3575
+ severity: "info",
3576
+ write: "bestEffort"
3577
+ },
3578
+ "user.email.unverified": {
3579
+ category: "identity",
3580
+ severity: "info",
3581
+ write: "bestEffort"
3582
+ },
3583
+ "user.email.verification_resent": {
3584
+ category: "identity",
3585
+ severity: "info",
3586
+ write: "bestEffort"
3587
+ },
3588
+ "user.email.change_resent": {
3589
+ category: "identity",
3590
+ severity: "info",
3591
+ write: "bestEffort"
3592
+ },
3593
+ "user.email.change_cancelled": {
3594
+ category: "identity",
3595
+ severity: "info",
3596
+ write: "bestEffort"
3597
+ },
3598
+ "user.sessions.revoked": {
3599
+ category: "identity",
3600
+ severity: "notice",
3601
+ write: "bestEffort"
3602
+ },
3603
+ "user.mfa.reset": {
3604
+ category: "identity",
3605
+ severity: "notice",
3606
+ write: "strict"
3607
+ },
3608
+ "user.mfa.denied": {
3609
+ category: "identity",
3610
+ severity: "critical",
3611
+ write: "bestEffort"
3612
+ },
3613
+ "audit.exported": {
3614
+ category: "support",
3615
+ severity: "info",
3616
+ write: "bestEffort"
3617
+ }
3618
+ };
3619
+ Object.keys(ADMIN_AUDIT_CATALOGUE).filter((action) => "escalatesAboveGrantThreshold" in ADMIN_AUDIT_CATALOGUE[action]);
3040
3620
  z$1.object({
3041
3621
  category: AdminAuditCategorySchema.optional(),
3042
3622
  severity: AdminAuditSeveritySchema.optional(),
@@ -3045,13 +3625,14 @@ z$1.object({
3045
3625
  actorUserId: z$1.string().min(1).optional(),
3046
3626
  targetKind: AuditTargetSchema.shape.kind.optional(),
3047
3627
  targetId: z$1.string().min(1).optional(),
3048
- source: AuditContextSchema.shape.source.optional(),
3628
+ source: StoredAuditContextSourceSchema.optional(),
3049
3629
  actorScope: AuditActorSchema.shape.scope.optional(),
3050
3630
  from: z$1.iso.datetime().optional(),
3051
3631
  to: z$1.iso.datetime().optional(),
3052
3632
  requestId: z$1.string().min(1).optional(),
3053
3633
  impersonationSessionId: z$1.string().min(1).optional()
3054
3634
  });
3635
+ z$1.enum(["target", "actor"]);
3055
3636
  let SupportedFabFileMimeTypes = /* @__PURE__ */ function(SupportedFabFileMimeTypes) {
3056
3637
  SupportedFabFileMimeTypes["PDF"] = "application/pdf";
3057
3638
  SupportedFabFileMimeTypes["DOCX"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
@@ -3097,6 +3678,26 @@ let SupportedFabFileMimeTypes = /* @__PURE__ */ function(SupportedFabFileMimeTyp
3097
3678
  return SupportedFabFileMimeTypes;
3098
3679
  }({});
3099
3680
  new Set(Object.values(SupportedFabFileMimeTypes));
3681
+ /**
3682
+ * Known MIME types emitted by the generated-audio endpoints (TTS + sound
3683
+ * effects). Deliberately SEPARATE from SupportedFabFileMimeTypes: that set is
3684
+ * the ingest pipeline's chunk+vectorize allowlist, and audio is intentionally
3685
+ * NOT vectorizable and NOT attachable to a chat completion (no model accepts
3686
+ * audio input). Kept only for extension mapping / documentation; the runtime
3687
+ * guard below matches any `audio/*` so unusual provider subtypes are still
3688
+ * treated as audio (i.e. still excluded from every LLM path).
3689
+ */
3690
+ let AudioMimeType = /* @__PURE__ */ function(AudioMimeType) {
3691
+ AudioMimeType["MP3"] = "audio/mpeg";
3692
+ AudioMimeType["WAV"] = "audio/wav";
3693
+ AudioMimeType["OPUS"] = "audio/opus";
3694
+ AudioMimeType["AAC"] = "audio/aac";
3695
+ AudioMimeType["FLAC"] = "audio/flac";
3696
+ AudioMimeType["PCM"] = "audio/pcm";
3697
+ AudioMimeType["OGG"] = "audio/ogg";
3698
+ AudioMimeType["WEBM"] = "audio/webm";
3699
+ return AudioMimeType;
3700
+ }({});
3100
3701
  /** Reads back the classifier set by the tagged-error helpers above; `undefined` for untagged errors. */
3101
3702
  function getQuestErrorCode(error) {
3102
3703
  const code = error?.additionalInfo?.errorCode;
@@ -3233,6 +3834,49 @@ const PROMPT_META_MODEL_TYPES = [
3233
3834
  "image",
3234
3835
  "video"
3235
3836
  ];
3837
+ /**
3838
+ * Native Anthropic tool capabilities, by Claude family and version. Allow-lists rather than a
3839
+ * version comparison, so an older or not-yet-reviewed id (a preview, a new family) reads false.
3840
+ * Sources: the tool search tool and mid-conversation system messages pages of the Claude docs.
3841
+ */
3842
+ const TOOL_SEARCH_VERSIONS = /* @__PURE__ */ new Set([
3843
+ "opus-4-5",
3844
+ "opus-4-6",
3845
+ "opus-4-7",
3846
+ "opus-4-8",
3847
+ "opus-5",
3848
+ "opus-5-5",
3849
+ "sonnet-4-5",
3850
+ "sonnet-4-6",
3851
+ "sonnet-5",
3852
+ "sonnet-5-5",
3853
+ "haiku-4-5",
3854
+ "haiku-5-5",
3855
+ "fable-5",
3856
+ "fable-5-1",
3857
+ "mythos-5",
3858
+ "mythos-5-1"
3859
+ ]);
3860
+ function servedNatively(model, versions) {
3861
+ if (model.backend !== "anthropic" && model.backend !== "bedrock") return false;
3862
+ const version = claudeFamilyVersion(model.id);
3863
+ return version !== null && versions.has(version);
3864
+ }
3865
+ /** Whether the model accepts the tool search server tool and `defer_loading` on tool definitions. */
3866
+ function supportsToolSearch(model) {
3867
+ return servedNatively(model, TOOL_SEARCH_VERSIONS);
3868
+ }
3869
+ function servedByDirectAnthropic(model) {
3870
+ return model.adapterFamily ? model.adapterFamily === "anthropic-messages" : model.backend === "anthropic";
3871
+ }
3872
+ function servedByClaudeAdapter(model) {
3873
+ if (servedByDirectAnthropic(model)) return true;
3874
+ return model.adapterFamily ? model.adapterFamily === "bedrock-anthropic" : model.backend === "bedrock";
3875
+ }
3876
+ /** supportsToolSearch, narrowed to models whose `/api/ai/v1/completions` adapter actually forwards it. */
3877
+ function completionsSupportsToolSearch(model) {
3878
+ return servedByClaudeAdapter(model) && supportsToolSearch(model);
3879
+ }
3236
3880
  const DEFAULT_PRICE_MARGIN = 1.2;
3237
3881
  const DEFAULT_USD_TO_CREDITS_RATE = 6e-4;
3238
3882
  /**
@@ -3848,7 +4492,7 @@ const CompletionCustomToolSchema = z$1.object({
3848
4492
  required: z$1.array(z$1.string()).optional()
3849
4493
  }).passthrough(),
3850
4494
  strict: z$1.boolean().optional(),
3851
- defer_loading: z$1.boolean().optional().describe("Keep this definition out of the model context until the tool search server tool (or a `tool_reference`) loads it. Honoured only on models served by the direct Anthropic API whose `supports_tool_search` is true; on every other model (including Bedrock-served Claude, for now) it is ignored and the tool is sent as usual. At least one tool, or a `serverTool` entry, must stay non-deferred (`errorCode: \"all_tools_deferred\"`)."),
4495
+ defer_loading: z$1.boolean().optional().describe("Keep this definition out of the model context until the tool search server tool (or a `tool_reference`) loads it. Honoured only on Claude models (direct Anthropic or Bedrock) whose `supports_tool_search` is true; on every other model it is ignored and the tool is sent as usual. At least one tool, or a `serverTool` entry, must stay non-deferred (`errorCode: \"all_tools_deferred\"`)."),
3852
4496
  cache_control: z$1.unknown().optional().describe("Accepted for validation only and not forwarded. Rejected on a deferred tool (`errorCode: \"deferred_tool_cache_control\"`).")
3853
4497
  }),
3854
4498
  _isMcpTool: z$1.boolean().optional()
@@ -3861,7 +4505,7 @@ const ToolSearchServerToolSchema = z$1.discriminatedUnion("type", [z$1.object({
3861
4505
  type: z$1.literal("tool_search_tool_bm25_20251119"),
3862
4506
  name: z$1.literal("tool_search_tool_bm25")
3863
4507
  })]);
3864
- const CompletionServerToolSchema = z$1.object({ serverTool: ToolSearchServerToolSchema.describe("A provider-run tool, sent to the provider as `{ type, name }`. Only the Anthropic tool search tools exist today, and only on models served by the direct Anthropic API whose `supports_tool_search` is true; any other model, Bedrock-served Claude included, is refused with `errorCode: \"tool_search_unsupported\"` rather than run without it.") });
4508
+ const CompletionServerToolSchema = z$1.object({ serverTool: ToolSearchServerToolSchema.describe("A provider-run tool, sent to the provider as `{ type, name }`. Only the Anthropic tool search tools exist today, and only on Claude models (direct Anthropic or Bedrock) whose `supports_tool_search` is true; any other model is refused with `errorCode: \"tool_search_unsupported\"` rather than run without it.") });
3865
4509
  /** One `tools[]` entry: a caller-defined tool, or a provider-run server tool. */
3866
4510
  const CompletionToolSchema = z$1.union([CompletionCustomToolSchema, CompletionServerToolSchema]);
3867
4511
  /**
@@ -3930,7 +4574,7 @@ const CompletionMessageSchema = z$1.object({
3930
4574
  */
3931
4575
  const CompletionRequestSchema = z$1.object({
3932
4576
  model: z$1.string().max(256),
3933
- messages: z$1.array(CompletionMessageSchema),
4577
+ messages: z$1.array(CompletionMessageSchema).min(1),
3934
4578
  response_format: ResponseFormatSchema.optional(),
3935
4579
  stream: z$1.boolean().optional(),
3936
4580
  tools: z$1.array(CompletionToolSchema).optional(),
@@ -3991,6 +4635,7 @@ const CompletionContentEventSchema = z$1.object({
3991
4635
  */
3992
4636
  const COMPLETION_SSE_ERROR_CODES = [
3993
4637
  ...QUEST_ERROR_CODES,
4638
+ "invalid_request",
3994
4639
  "provider_error",
3995
4640
  "provider_rejected",
3996
4641
  "content_blocked"
@@ -4001,9 +4646,9 @@ const COMPLETION_SSE_ERROR_CODES = [
4001
4646
  * real status instead; see completions.contract.ts.
4002
4647
  *
4003
4648
  * `code` is the machine-readable classifier: a billing code when settlement runs out mid-generation,
4004
- * `provider_rejected` / `provider_error` when the model provider fails, `content_blocked` when the
4005
- * model's safety classifier declined the request. Optional; callers must never match on the prose
4006
- * `message`.
4649
+ * `invalid_request` when the provider rejects caller-controlled input, `provider_rejected` /
4650
+ * `provider_error` when the model provider fails, `content_blocked` when the model's safety classifier
4651
+ * declined the request. Optional; callers must never match on the prose `message`.
4007
4652
  *
4008
4653
  * The field is `code` rather than the conventions' `errorCode` because this frame shipped that way
4009
4654
  * and is a published wire shape.
@@ -5128,9 +5773,9 @@ const RetrievalSummarySchema = z$1.object({
5128
5773
  * `citables` and `retrieval` - this field needs no merge case in applyQuestStatusChanges: a
5129
5774
  * later tool-arm write that omits it is preserved by the one-level spread.
5130
5775
  *
5131
- * `reasons` is the same diagnostic prose the warnings entry interpolates. It is shown to the
5132
- * reader behind a disclosure rather than in the banner body, because only some reasons are
5133
- * actionable (a document mid-reindex returns on its own; a per-turn chunk budget does not).
5776
+ * `reasons` is reader-facing prose with no document ids or internal limits, shown behind a
5777
+ * disclosure in the chat notice. The diagnostic version (ids, caps, counts) lives only in the
5778
+ * `warnings` entry. A supersession collapse alone does not produce this field at all.
5134
5779
  */
5135
5780
  const RetrievalCoverageSchema = z$1.object({
5136
5781
  /** Always true - see the presence contract above. */
@@ -5460,10 +6105,14 @@ const StorageLimitErrorSchema = ApiErrorSchema.extend(StorageLimitErrorInfoSchem
5460
6105
  /** 503 when this deployment has no usable key for a provider the request needs (data-lake search, voice). */
5461
6106
  const ProviderNotConfiguredErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.literal("provider_not_configured").optional() });
5462
6107
  /**
5463
- * 400 for a `model` the caller cannot use, or a refused tool search request; `errorCode` is absent on the
5464
- * other 400s (e.g. malformed JSON).
6108
+ * 400 for provider-rejected caller input, a `model` the caller cannot use, or a refused tool search
6109
+ * request; `errorCode` is absent on the other 400s (e.g. malformed JSON).
5465
6110
  */
5466
- const CompletionsBadRequestErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.enum([UNKNOWN_MODEL_ERROR_CODE, ...COMPLETION_TOOL_ERROR_CODES]).optional() });
6111
+ const CompletionsBadRequestErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.enum([
6112
+ "invalid_request",
6113
+ UNKNOWN_MODEL_ERROR_CODE,
6114
+ ...COMPLETION_TOOL_ERROR_CODES
6115
+ ]).optional() });
5467
6116
  /**
5468
6117
  * Error envelope for the 403 apiKeyAuth (apps/client/server/middlewares/apiKeyAuth.ts)
5469
6118
  * throws when an API key lacks a route's scopes. It names what the ROUTE requires,
@@ -5491,10 +6140,11 @@ const voiceOutputFormatSchema = z.enum([
5491
6140
  "flac",
5492
6141
  "pcm"
5493
6142
  ]);
5494
- const TTS_ABSOLUTE_MAX_INPUT_CHARS = Math.max(...Object.values({
6143
+ const TTS_MAX_INPUT_CHARS = {
5495
6144
  openai: 4096,
5496
6145
  elevenlabs: 1e4
5497
- }));
6146
+ };
6147
+ const TTS_ABSOLUTE_MAX_INPUT_CHARS = Math.max(...Object.values(TTS_MAX_INPUT_CHARS));
5498
6148
  const ttsLanguageCodeSchema = z.string().regex(/^[a-z]{2}$/, "languageCode must be a lowercase ISO 639-1 code, e.g. \"en\" or \"ja\"");
5499
6149
  const ttsRequestSchema = z.object({
5500
6150
  text: z.string().min(1).max(TTS_ABSOLUTE_MAX_INPUT_CHARS),
@@ -5586,12 +6236,30 @@ const ttsResponseTooLargeSchema = generatedAudioTooLargeSchema.extend({
5586
6236
  fabFileId: z.string().optional(),
5587
6237
  fileUrl: z.string().optional()
5588
6238
  });
6239
+ /**
6240
+ * The model each vendor's service synthesizes and bills with when the caller
6241
+ * names none. Read by OpenAIVoiceService / ElevenLabsVoiceService in
6242
+ * @bike4mind/utils, so a pre-flight estimate prices the model the charge will use
6243
+ * when the requested vendor serves the call. The TTS route reserves the highest
6244
+ * estimate across its candidate vendors, using these defaults for alternates,
6245
+ * then settles the actual vendor's cost and refunds the difference.
6246
+ */
6247
+ const TTS_DEFAULT_MODEL = {
6248
+ openai: "tts-1",
6249
+ elevenlabs: "eleven_multilingual_v2"
6250
+ };
5589
6251
  const GENERATION_OUTPUT_AVAILABILITIES = [
5590
6252
  "ready",
5591
6253
  "pending_scan",
5592
6254
  "unavailable"
5593
6255
  ];
5594
6256
  const VIDEO_MODES = ["text_to_video", "image_to_video"];
6257
+ const VIDEO_INPUT_ROLES = [
6258
+ "first_frame",
6259
+ "last_frame",
6260
+ "reference",
6261
+ "style"
6262
+ ];
5595
6263
  const ASPECT_RATIOS = [
5596
6264
  "16:9",
5597
6265
  "9:16",
@@ -5615,6 +6283,13 @@ const VideoModelIdSchema = z$1.enum([
5615
6283
  "grok-imagine-video-1.5",
5616
6284
  "veo-3.1-fast-generate-preview"
5617
6285
  ]);
6286
+ const VideoInputSchema = z$1.union([z$1.strictObject({
6287
+ role: z$1.enum(VIDEO_INPUT_ROLES),
6288
+ fileId: z$1.string().min(1)
6289
+ }), z$1.strictObject({
6290
+ role: z$1.enum(VIDEO_INPUT_ROLES),
6291
+ generatedImageKey: z$1.string().min(1)
6292
+ })]);
5618
6293
  z$1.object({
5619
6294
  model: VideoModelIdSchema,
5620
6295
  mode: z$1.enum(VIDEO_MODES),
@@ -5622,6 +6297,7 @@ z$1.object({
5622
6297
  durationSeconds: z$1.number().positive(),
5623
6298
  aspectRatio: z$1.enum(ASPECT_RATIOS),
5624
6299
  resolution: z$1.enum(RESOLUTION_TIERS),
6300
+ inputs: z$1.array(VideoInputSchema).max(8).optional(),
5625
6301
  inputImageFileId: z$1.string().min(1).optional(),
5626
6302
  inputGeneratedImageKey: z$1.string().min(1).optional(),
5627
6303
  audio: z$1.boolean().optional()
@@ -5633,6 +6309,7 @@ const VIDEO_VALIDATION_ERROR_CODES = [
5633
6309
  "unsupported_mode",
5634
6310
  "missing_input_image",
5635
6311
  "unexpected_input_image",
6312
+ "unsupported_input_role",
5636
6313
  "unsupported_audio_option"
5637
6314
  ];
5638
6315
  const MAX_MUSIC_LYRICS_CHARS = 1e4;
@@ -5854,6 +6531,7 @@ z$2.object({
5854
6531
  ]),
5855
6532
  slackSettings: redactedSubdocument([
5856
6533
  "slackUserId",
6534
+ "slackTeamId",
5857
6535
  "defaultNotebookId",
5858
6536
  "autoCreateNotebook",
5859
6537
  "notebookNamePrefix",
@@ -5874,7 +6552,10 @@ z$2.object({
5874
6552
  "connectedAt"
5875
6553
  ])
5876
6554
  }).extend({
5877
- securityQuestions: passthroughField,
6555
+ securityQuestions: z$2.array(z$2.object({
6556
+ question: z$2.string(),
6557
+ answer: z$2.literal("")
6558
+ })).nullable().optional(),
5878
6559
  userNotes: passthroughField
5879
6560
  });
5880
6561
  /**
@@ -5894,7 +6575,6 @@ const INTEGRATION_IDS = [
5894
6575
  "mcp-cli",
5895
6576
  "atlassian",
5896
6577
  "notion",
5897
- "google-calendar",
5898
6578
  "slack",
5899
6579
  "email",
5900
6580
  "linkedin",
@@ -6067,20 +6747,6 @@ const INTEGRATION_CATALOG = {
6067
6747
  mcpServer: "notion"
6068
6748
  }]
6069
6749
  },
6070
- "google-calendar": {
6071
- name: "Google Calendar",
6072
- description: "Book briefing calls into open slots on a shared Bike4Mind calendar, not your own.",
6073
- category: "productivity",
6074
- logo: "google-calendar",
6075
- stage: "beta",
6076
- flag: "enableGoogleCalendar",
6077
- capabilities: [{
6078
- id: "calendar-booking",
6079
- label: "Book briefings on a shared calendar",
6080
- kind: "notifications",
6081
- stage: "beta"
6082
- }]
6083
- },
6084
6750
  slack: {
6085
6751
  name: "Slack",
6086
6752
  description: "Chat with Bike4Mind in Slack and send threads and files to a data lake.",
@@ -6655,7 +7321,7 @@ let OllamaEmbeddingModel = /* @__PURE__ */ function(OllamaEmbeddingModel) {
6655
7321
  *
6656
7322
  * Deliberately answers from the ENVIRONMENT only, and deliberately does NOT try to answer "can this
6657
7323
  * deployment actually reach that model". It cannot: on a hosted stage an SST secret arrives as a
6658
- * linked Resource, never as process.env (see apps/client/server/modelDiscovery/adapters.ts), so an
7324
+ * linked Resource, never as process.env (see packages/server-utils/src/modelDiscovery/adapters.ts), so an
6659
7325
  * absent OPENAI_API_KEY here means "unknown", not "no key" - it is absent on production too. This
6660
7326
  * function is also bundled into the browser via settingsMap, where every env read is undefined, so
6661
7327
  * any answer it gives must be one that is safe as a stage-neutral constant.
@@ -6764,6 +7430,7 @@ z$1.object({
6764
7430
  status: ReleaseNoteStatusSchema,
6765
7431
  publishAt: z$1.coerce.date(),
6766
7432
  editedAt: z$1.coerce.date().nullable(),
7433
+ video: ModalVideoShapeSchema.nullable().optional(),
6767
7434
  schemaVersion: z$1.number().int()
6768
7435
  });
6769
7436
  const ReleaseNotesJobPrSchema = z$1.object({
@@ -6791,6 +7458,22 @@ const ReleaseNotesConfigSchema = z$1.object({
6791
7458
  slackTeamId: z$1.string().optional(),
6792
7459
  slackChannelId: z$1.string().optional()
6793
7460
  });
7461
+ /** A release note video on the public wire: ModalVideo with snake_case keys. */
7462
+ const PublicReleaseNoteVideoSchema = z$1.object({
7463
+ landscape_url: z$1.string().nullable().optional(),
7464
+ landscape_poster_url: z$1.string().nullable().optional(),
7465
+ portrait_url: z$1.string().nullable().optional(),
7466
+ portrait_poster_url: z$1.string().nullable().optional()
7467
+ });
7468
+ const ReleaseNoteMediaParamsSchema = z$1.object({
7469
+ id: z$1.string().min(1),
7470
+ asset: z$1.enum(Object.keys({
7471
+ landscape: "landscapeUrl",
7472
+ "landscape-poster": "landscapePosterUrl",
7473
+ portrait: "portraitUrl",
7474
+ "portrait-poster": "portraitPosterUrl"
7475
+ }))
7476
+ });
6794
7477
  /**
6795
7478
  * A release note as GET /api/v1/whats-new serves it. An allowlist: provenance (sourcePrs, deployedSha),
6796
7479
  * workflow state (status, editedAt) and storage fields never reach the public wire.
@@ -6805,7 +7488,8 @@ const PublicReleaseNoteSchema = z$1.object({
6805
7488
  category: ReleaseNoteCategorySchema,
6806
7489
  text: z$1.string(),
6807
7490
  importance: z$1.number().int().min(1).max(3)
6808
- }))
7491
+ })),
7492
+ video: PublicReleaseNoteVideoSchema.nullable().optional()
6809
7493
  });
6810
7494
  /**
6811
7495
  * The measured per-space floors, rendered for an admin-facing description (e.g. "75 for
@@ -7100,6 +7784,7 @@ z$1.enum([
7100
7784
  "serverStatus",
7101
7785
  "defaultSeats",
7102
7786
  "CSMandCTAFlag",
7787
+ "showDesktopAppPromo",
7103
7788
  "SystemFiles",
7104
7789
  "OpenWeatherKey",
7105
7790
  "SerperKey",
@@ -7115,6 +7800,9 @@ z$1.enum([
7115
7800
  "MaxActiveDungeons",
7116
7801
  "DungeonSpawnIntervalHeartbeats",
7117
7802
  "DungeonTTLMinutes",
7803
+ "EnableStudioCreateV2",
7804
+ "EnableStudioImage",
7805
+ "EnableStudioProductions",
7118
7806
  "VectorThreshold",
7119
7807
  "bflApiKey",
7120
7808
  "EnableMCPServer",
@@ -7148,10 +7836,6 @@ z$1.enum([
7148
7836
  "teamPlanCreditsPerSeat",
7149
7837
  "lowCreditsThreshold",
7150
7838
  "syntheticUserEmailDomains",
7151
- "enableGoogleCalendar",
7152
- "googleCalendarServiceAccountEmail",
7153
- "googleCalendarServiceAccountSecret",
7154
- "googleCalendarOrganizerEmail",
7155
7839
  "EnableWeatherService",
7156
7840
  "WeatherUnits",
7157
7841
  "defaultEmbeddingModel",
@@ -7197,6 +7881,9 @@ z$1.enum([
7197
7881
  "SlackFeedbackWebhookUrl",
7198
7882
  "SlackNonProdFeedbackWebhookUrl",
7199
7883
  "SlackEmailAuditWebhookUrl",
7884
+ "SlackSecurityAlertsWebhookUrl",
7885
+ "AdminAuditCriticalGrantCredits",
7886
+ "AdminAuditDailyGrantCredits",
7200
7887
  "slackSigningSecret",
7201
7888
  "slackBotToken",
7202
7889
  "enforceMFA",
@@ -7441,15 +8128,17 @@ function makeNumberSetting(config) {
7441
8128
  schema: z$1.preprocess((val) => isBlankSettingValue(val) ? void 0 : val, numberSchema.prefault(config.defaultValue ?? 0))
7442
8129
  };
7443
8130
  }
7444
- function makeBooleanSetting(config) {
8131
+ function makeBooleanSetting({ acceptNumeric = false, ...config }) {
7445
8132
  return {
7446
8133
  ...config,
7447
8134
  type: "boolean",
7448
8135
  schema: z$1.preprocess((val) => {
7449
8136
  if (typeof val === "string") {
7450
- if (val.toLowerCase() === "true") return true;
7451
- if (val.toLowerCase() === "false") return false;
8137
+ if (val.toLowerCase() === "true" || acceptNumeric && val === "1") return true;
8138
+ if (val.toLowerCase() === "false" || acceptNumeric && val === "0") return false;
7452
8139
  }
8140
+ if (acceptNumeric && val === 1) return true;
8141
+ if (acceptNumeric && val === 0) return false;
7453
8142
  return val;
7454
8143
  }, z$1.boolean()).prefault(config.defaultValue ?? false)
7455
8144
  };
@@ -8391,30 +9080,6 @@ const API_SERVICE_GROUPS = {
8391
9080
  }
8392
9081
  ]
8393
9082
  },
8394
- CALENDAR: {
8395
- id: "calendarAPIService",
8396
- name: "Google Calendar",
8397
- description: "Google Calendar integration settings",
8398
- icon: "CalendarMonth",
8399
- settings: [
8400
- {
8401
- key: "enableGoogleCalendar",
8402
- order: 1
8403
- },
8404
- {
8405
- key: "googleCalendarServiceAccountEmail",
8406
- order: 2
8407
- },
8408
- {
8409
- key: "googleCalendarServiceAccountSecret",
8410
- order: 3
8411
- },
8412
- {
8413
- key: "googleCalendarOrganizerEmail",
8414
- order: 4
8415
- }
8416
- ]
8417
- },
8418
9083
  FEEDBACK: {
8419
9084
  id: "feedbackService",
8420
9085
  name: "Feedback System",
@@ -8736,6 +9401,18 @@ const API_SERVICE_GROUPS = {
8736
9401
  {
8737
9402
  key: "DungeonTTLMinutes",
8738
9403
  order: 293
9404
+ },
9405
+ {
9406
+ key: "EnableStudioCreateV2",
9407
+ order: 300
9408
+ },
9409
+ {
9410
+ key: "EnableStudioImage",
9411
+ order: 301
9412
+ },
9413
+ {
9414
+ key: "EnableStudioProductions",
9415
+ order: 302
8739
9416
  }
8740
9417
  ]
8741
9418
  },
@@ -8774,10 +9451,6 @@ const API_SERVICE_GROUPS = {
8774
9451
  {
8775
9452
  key: "defaultTags",
8776
9453
  order: 5
8777
- },
8778
- {
8779
- key: "enforceCredits",
8780
- order: 6
8781
9454
  }
8782
9455
  ]
8783
9456
  },
@@ -8788,36 +9461,16 @@ const API_SERVICE_GROUPS = {
8788
9461
  icon: "Payments",
8789
9462
  settings: [
8790
9463
  {
8791
- key: "pricePerCredit",
9464
+ key: "billOperationalUsage",
8792
9465
  order: 1
8793
9466
  },
8794
9467
  {
8795
- key: "enforceCredits",
9468
+ key: "blockDisposableEmails",
8796
9469
  order: 2
8797
9470
  },
8798
9471
  {
8799
- key: "billOperationalUsage",
9472
+ key: "syntheticUserEmailDomains",
8800
9473
  order: 3
8801
- },
8802
- {
8803
- key: "enableTeamPlan",
8804
- order: 4
8805
- },
8806
- {
8807
- key: "teamPlanMinSeats",
8808
- order: 5
8809
- },
8810
- {
8811
- key: "teamPlanMaxSeats",
8812
- order: 6
8813
- },
8814
- {
8815
- key: "teamPlanCreditsPerSeat",
8816
- order: 7
8817
- },
8818
- {
8819
- key: "lowCreditsThreshold",
8820
- order: 8
8821
9474
  }
8822
9475
  ]
8823
9476
  },
@@ -8895,6 +9548,10 @@ const API_SERVICE_GROUPS = {
8895
9548
  {
8896
9549
  key: "RedditLink",
8897
9550
  order: 8
9551
+ },
9552
+ {
9553
+ key: "showDesktopAppPromo",
9554
+ order: 9
8898
9555
  }
8899
9556
  ]
8900
9557
  },
@@ -9590,7 +10247,7 @@ const settingsMap = {
9590
10247
  key: "SkillMarketUrlAllowlist",
9591
10248
  name: "Skill Market URL Allowlist",
9592
10249
  defaultValue: "",
9593
- description: "Comma-separated host names a Skill Market skill may reference without a finding (subdomains included). Any other URL holds a Public listing for review.",
10250
+ description: "Comma-separated host names a Skill Market skill may reference without a finding (subdomains included). Any other URL holds a listing for review at every visibility.",
9594
10251
  category: "AI Moderation"
9595
10252
  }),
9596
10253
  SkillMarketClassifierMinConfidence: makeNumberSetting({
@@ -9725,9 +10382,7 @@ const settingsMap = {
9725
10382
  name: "Price Per Credit",
9726
10383
  defaultValue: 50,
9727
10384
  description: `The price in USD of one credit bought pay-as-you-go. Cannot be set below ${formatUsdPerCredit(MIN_PRICE_PER_CREDIT_USD)}, the per-credit sale price including markup.`,
9728
- category: "Users",
9729
- group: API_SERVICE_GROUPS.CREDITS.id,
9730
- order: 1
10385
+ category: "Users"
9731
10386
  }),
9732
10387
  apiRateLimitFreePerMin: makeNumberSetting({
9733
10388
  key: "apiRateLimitFreePerMin",
@@ -9933,6 +10588,32 @@ const settingsMap = {
9933
10588
  order: 8,
9934
10589
  isSensitive: true
9935
10590
  }),
10591
+ SlackSecurityAlertsWebhookUrl: makeStringSetting({
10592
+ key: "SlackSecurityAlertsWebhookUrl",
10593
+ name: "Security Alerts Channel Webhook URL",
10594
+ defaultValue: "",
10595
+ description: "Incoming-webhook URL for the security Slack channel that receives critical Admin Audit Log entries and credit-grant bursts. Leave empty to turn alerts off; the Audit Log page then shows a banner. It never falls back to the default webhook.",
10596
+ category: "SecOps",
10597
+ isSensitive: true
10598
+ }),
10599
+ AdminAuditCriticalGrantCredits: makeNumberSetting({
10600
+ key: "AdminAuditCriticalGrantCredits",
10601
+ name: "Critical Single Credit Grant",
10602
+ defaultValue: ADMIN_AUDIT_DEFAULT_CRITICAL_GRANT_CREDITS,
10603
+ min: 1,
10604
+ int: true,
10605
+ description: "An admin credit grant above this many credits is recorded as critical in the Admin Audit Log and alerts the security channel.",
10606
+ category: "SecOps"
10607
+ }),
10608
+ AdminAuditDailyGrantCredits: makeNumberSetting({
10609
+ key: "AdminAuditDailyGrantCredits",
10610
+ name: "Critical Daily Credit Grants per Admin",
10611
+ defaultValue: ADMIN_AUDIT_DEFAULT_DAILY_GRANT_CREDITS,
10612
+ min: 1,
10613
+ int: true,
10614
+ description: "A grant that takes one admin past this many credits granted in the last 24 hours is recorded as critical in the Admin Audit Log and alerts the security channel.",
10615
+ category: "SecOps"
10616
+ }),
9936
10617
  liveFeedbackEmail: makeStringSetting({
9937
10618
  key: "liveFeedbackEmail",
9938
10619
  name: "Live Feedback Email",
@@ -10071,51 +10752,12 @@ const settingsMap = {
10071
10752
  description: "The default number of seats for new organizations.",
10072
10753
  category: "Users"
10073
10754
  }),
10074
- enableGoogleCalendar: makeBooleanSetting({
10075
- key: "enableGoogleCalendar",
10076
- name: "Enable Google Calendar Integration",
10077
- defaultValue: false,
10078
- description: "Whether to enable scheduling of briefing/advisory using google calendar",
10079
- category: "Calendar",
10080
- group: API_SERVICE_GROUPS.CALENDAR.id,
10081
- order: 1,
10082
- userReadable: true
10083
- }),
10084
- googleCalendarServiceAccountEmail: makeStringSetting({
10085
- key: "googleCalendarServiceAccountEmail",
10086
- name: "Service Account Email",
10087
- defaultValue: "",
10088
- description: "The service account email address that has access to google calendar API.",
10089
- category: "Calendar",
10090
- group: API_SERVICE_GROUPS.CALENDAR.id,
10091
- order: 2
10092
- }),
10093
- googleCalendarServiceAccountSecret: makeStringSetting({
10094
- key: "googleCalendarServiceAccountSecret",
10095
- name: "Service Account Secret",
10096
- defaultValue: "",
10097
- description: "The base64 encoded service account secret that is associated with the service account email.",
10098
- category: "Calendar",
10099
- group: API_SERVICE_GROUPS.CALENDAR.id,
10100
- order: 3,
10101
- isSensitive: true
10102
- }),
10103
- googleCalendarOrganizerEmail: makeStringSetting({
10104
- key: "googleCalendarOrganizerEmail",
10105
- name: "Organizer Email",
10106
- defaultValue: "",
10107
- description: "The organizer email for creating events.",
10108
- category: "Calendar",
10109
- group: API_SERVICE_GROUPS.CALENDAR.id,
10110
- order: 4
10111
- }),
10112
10755
  enforceCredits: makeBooleanSetting({
10113
10756
  key: "enforceCredits",
10114
10757
  userReadable: true,
10115
10758
  name: "Enforce Credits",
10116
10759
  defaultValue: process.env.B4M_SELF_HOST === "true" ? false : true,
10117
10760
  description: "Whether to enforce credits for users",
10118
- group: API_SERVICE_GROUPS.CREDITS.id,
10119
10761
  category: "Users"
10120
10762
  }),
10121
10763
  billOperationalUsage: makeBooleanSetting({
@@ -10127,12 +10769,12 @@ const settingsMap = {
10127
10769
  category: "Users"
10128
10770
  }),
10129
10771
  enableTeamPlan: makeBooleanSetting({
10772
+ acceptNumeric: true,
10130
10773
  key: "enableTeamPlan",
10131
10774
  userReadable: true,
10132
10775
  name: "Enable Team Plan",
10133
10776
  defaultValue: false,
10134
10777
  description: "Whether to enable team plans",
10135
- group: API_SERVICE_GROUPS.CREDITS.id,
10136
10778
  category: "Users"
10137
10779
  }),
10138
10780
  allowOpenRegistration: makeBooleanSetting({
@@ -10140,7 +10782,6 @@ const settingsMap = {
10140
10782
  name: "Allow Open Registration",
10141
10783
  defaultValue: false,
10142
10784
  description: "Master switch for self-serve signup. When OFF (default), a valid invite code is required to register. When ON, users may register without an invite code; the Default Free Credits grant is then applied after they verify their email (anti-spam — see Default Free Credits). Safe to enable: the pre-request credit reservation caps every free user at the credits they are granted.",
10143
- group: API_SERVICE_GROUPS.CREDITS.id,
10144
10785
  category: "Users"
10145
10786
  }),
10146
10787
  blockDisposableEmails: makeBooleanSetting({
@@ -10157,7 +10798,6 @@ const settingsMap = {
10157
10798
  name: "Default Free Credits",
10158
10799
  defaultValue: 0,
10159
10800
  description: "Base credits granted after email verification to a user who registers WITHOUT an invite code, including eligible partner signups when Allow Open Registration is OFF. Partner-rule credits are added on top of this amount. An unverified account receives no credits. A free user cannot spend beyond their total granted balance.",
10160
- group: API_SERVICE_GROUPS.CREDITS.id,
10161
10801
  category: "Users"
10162
10802
  }),
10163
10803
  teamPlanMinSeats: makeNumberSetting({
@@ -10169,7 +10809,6 @@ const settingsMap = {
10169
10809
  min: 1,
10170
10810
  max: TEAM_PLAN_SEATS_HARD_LIMIT,
10171
10811
  description: "Fewest seats a paid team subscription can be bought with. Enforced at checkout, on seat changes from Stripe and on admin conversion to paid. Admin grants may still go down to 1. If set above the maximum seats, the maximum wins.",
10172
- group: API_SERVICE_GROUPS.CREDITS.id,
10173
10812
  category: "Users"
10174
10813
  }),
10175
10814
  teamPlanMaxSeats: makeNumberSetting({
@@ -10181,7 +10820,6 @@ const settingsMap = {
10181
10820
  min: 1,
10182
10821
  max: TEAM_PLAN_SEATS_HARD_LIMIT,
10183
10822
  description: "Most seats any team can hold (owner + members + pending invites). Enforced at checkout, on every seat change, on admin grants and on the partner-signup auto-raise. Lowering it below a team that is already larger does not remove anyone; that team can only be set down to this cap.",
10184
- group: API_SERVICE_GROUPS.CREDITS.id,
10185
10823
  category: "Users"
10186
10824
  }),
10187
10825
  teamPlanCreditsPerSeat: makeNumberSetting({
@@ -10192,7 +10830,6 @@ const settingsMap = {
10192
10830
  min: 0,
10193
10831
  max: 1e8,
10194
10832
  description: "Credits added to a team pool per paid seat each billing cycle (and per seat-month on admin team grants). Applies to invoices issued after the change; credits already granted are not adjusted.",
10195
- group: API_SERVICE_GROUPS.CREDITS.id,
10196
10833
  category: "Users",
10197
10834
  userReadable: true
10198
10835
  }),
@@ -10205,7 +10842,6 @@ const settingsMap = {
10205
10842
  min: 0,
10206
10843
  max: 1e8,
10207
10844
  description: "Below this many credits a user sees the low-credit warning state. Set to 0 to show warnings only once credits run out.",
10208
- group: API_SERVICE_GROUPS.CREDITS.id,
10209
10845
  category: "Users"
10210
10846
  }),
10211
10847
  syntheticUserEmailDomains: makeStringSetting({
@@ -10284,6 +10920,16 @@ const settingsMap = {
10284
10920
  group: API_SERVICE_GROUPS.USER_MANAGEMENT.id,
10285
10921
  order: 3
10286
10922
  }),
10923
+ showDesktopAppPromo: makeBooleanSetting({
10924
+ key: "showDesktopAppPromo",
10925
+ name: "Promote the Desktop App",
10926
+ defaultValue: true,
10927
+ description: "Show signed-in users the \"Try the desktop app\" button and the profile-menu link to the desktop download page. Turn off for deployments that do not want to point users at the published desktop builds.",
10928
+ category: "Branding",
10929
+ group: API_SERVICE_GROUPS.BRANDING.id,
10930
+ order: 9,
10931
+ userReadable: true
10932
+ }),
10287
10933
  SystemFiles: makeStringSetting({
10288
10934
  key: "SystemFiles",
10289
10935
  name: "System Prompt Files",
@@ -10376,6 +11022,35 @@ const settingsMap = {
10376
11022
  order: 4,
10377
11023
  isSensitive: true
10378
11024
  }),
11025
+ EnableStudioCreateV2: makeBooleanSetting({
11026
+ key: "EnableStudioCreateV2",
11027
+ name: "Enable Studio Create v2",
11028
+ defaultValue: false,
11029
+ description: "Admin-only preview of the restructured Studio Create area. Non-admins never see it, whatever this is set to.",
11030
+ category: "Experimental",
11031
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
11032
+ order: 300
11033
+ }),
11034
+ EnableStudioImage: makeBooleanSetting({
11035
+ key: "EnableStudioImage",
11036
+ name: "Enable Image in Studio",
11037
+ defaultValue: false,
11038
+ description: "Admin-only preview of the Image tab in Studio. Non-admins never see it, whatever this is set to.",
11039
+ category: "Experimental",
11040
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
11041
+ order: 301,
11042
+ dependsOn: "EnableStudioCreateV2"
11043
+ }),
11044
+ EnableStudioProductions: makeBooleanSetting({
11045
+ key: "EnableStudioProductions",
11046
+ name: "Enable Studio Productions",
11047
+ defaultValue: false,
11048
+ description: "Admin-only preview of Studio Productions (assembling many assets into one piece). Non-admins never see it, whatever this is set to.",
11049
+ category: "Experimental",
11050
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
11051
+ order: 302,
11052
+ dependsOn: "EnableStudioCreateV2"
11053
+ }),
10379
11054
  EnablePotionQuest: makeBooleanSetting({
10380
11055
  key: "EnablePotionQuest",
10381
11056
  name: "Enable PotionQuest Tools",
@@ -12044,6 +12719,41 @@ const musicRequestSchema = z.object({
12044
12719
  format: z.string().optional(),
12045
12720
  ...generatedAudioRequestFields
12046
12721
  });
12722
+ const turnRetryShape = {
12723
+ reason: z$1.enum(TURN_RETRY_REASONS),
12724
+ status: z$1.number().optional(),
12725
+ retryAfterMs: z$1.number().optional(),
12726
+ requestId: z$1.string().optional(),
12727
+ delayMs: z$1.number(),
12728
+ model: z$1.string()
12729
+ };
12730
+ const turnTimingShape = {
12731
+ queueMs: z$1.number(),
12732
+ contextBuildMs: z$1.number().optional(),
12733
+ semaphoreWaitMs: z$1.number(),
12734
+ totalMs: z$1.number().optional(),
12735
+ fallbackHops: z$1.number()
12736
+ };
12737
+ /** Wire shape of ITurnTiming for the live chat frame; hydrates `at` into a Date server-side. */
12738
+ const TurnTimingSchema = z$1.object({
12739
+ ...turnTimingShape,
12740
+ retries: z$1.array(z$1.object({
12741
+ ...turnRetryShape,
12742
+ at: z$1.coerce.date()
12743
+ }))
12744
+ });
12745
+ /**
12746
+ * The same record for the public quest poll. `at` is an ISO string on the wire (a Date only
12747
+ * before JSON serialization). Not z.coerce.date() here: like the other public response schemas
12748
+ * (schemas/chat.ts, schemas/session.ts), it would document the field as nullable in OpenAPI.
12749
+ */
12750
+ const TurnTimingPollSchema = z$1.object({
12751
+ ...turnTimingShape,
12752
+ retries: z$1.array(z$1.object({
12753
+ ...turnRetryShape,
12754
+ at: z$1.union([z$1.string().datetime(), z$1.date()])
12755
+ }))
12756
+ });
12047
12757
  /**
12048
12758
  * Canonical agent execution status tuple. Shared between the database model
12049
12759
  * (`AgentExecutionModel`), the client store (`useAgentExecutionStore`), and
@@ -12228,7 +12938,7 @@ const CliCompletionRequestAction = z$1.object({
12228
12938
  ]),
12229
12939
  content: z$1.union([z$1.string(), z$1.array(z$1.unknown())]),
12230
12940
  cache: z$1.boolean().optional()
12231
- })),
12941
+ })).min(1),
12232
12942
  options: z$1.object({
12233
12943
  temperature: z$1.number().optional(),
12234
12944
  maxTokens: z$1.number().optional(),
@@ -12563,6 +13273,7 @@ const StreamedChatCompletionAction = z$1.object({
12563
13273
  endTime: z$1.number().optional()
12564
13274
  }).optional(),
12565
13275
  fallbackInfo: FallbackInfoSchema.nullish(),
13276
+ turnTiming: TurnTimingSchema.optional(),
12566
13277
  pendingAction: z$1.object({
12567
13278
  tool: z$1.string(),
12568
13279
  params: z$1.record(z$1.string(), z$1.unknown()),
@@ -13529,7 +14240,7 @@ const ConfidenceGateAction = z$1.object({
13529
14240
  iteration: z$1.number(),
13530
14241
  /** 0..1, the agent's reported confidence for the paused iteration. */
13531
14242
  confidence: z$1.number(),
13532
- /** Pre-formatted server copy, e.g. "Iteration confidence 42% below threshold 60%". */
14243
+ /** Pre-formatted server copy, e.g. "2 iterations in a row below the 60% confidence threshold (latest 42%)". */
13533
14244
  reason: z$1.string()
13534
14245
  });
13535
14246
  const ChildExecutionSnapshotSchema = z$1.lazy(() => z$1.object({
@@ -14438,7 +15149,9 @@ z.object({
14438
15149
  /** Set to true if the file should be publicly accessible */
14439
15150
  public: z.boolean().optional(),
14440
15151
  /** The prefix to use for the file path */
14441
- prefix: z.string().optional()
15152
+ prefix: z.string().optional(),
15153
+ /** Admin What's New media: the server stores it under its modal prefix and admits mp4 */
15154
+ purpose: z.literal("modal-media").optional()
14442
15155
  });
14443
15156
  /**
14444
15157
  * Constants for BFL safety tolerance settings.
@@ -15040,6 +15753,7 @@ const QuestPollResponseSchema = ChatQuestPollResultSchema.extend({
15040
15753
  promptMeta: z$1.record(z$1.string(), z$1.unknown()).optional(),
15041
15754
  attachmentNotices: z$1.array(z$1.string()).optional(),
15042
15755
  attachmentDelivery: z$1.record(z$1.string(), z$1.unknown()).optional(),
15756
+ turnTiming: TurnTimingPollSchema.optional(),
15043
15757
  executionTracking: z$1.unknown().optional()
15044
15758
  });
15045
15759
  /**
@@ -15270,6 +15984,7 @@ z$2.object(passthroughShape({
15270
15984
  pendingAction: true,
15271
15985
  attachmentNotices: true,
15272
15986
  attachmentDelivery: true,
15987
+ turnTiming: true,
15273
15988
  navigationIntents: true,
15274
15989
  suggestedChoices: true,
15275
15990
  attachmentList: true,
@@ -15317,6 +16032,123 @@ z$2.object({
15317
16032
  envVariables: envVariableResponseSchema,
15318
16033
  metadata: metadataResponseSchema.nullish()
15319
16034
  });
16035
+ const SAML_CONFIG_RESPONSE_FIELDS = {
16036
+ entryPoint: true,
16037
+ issuer: true,
16038
+ cert: true,
16039
+ callbackUrl: true,
16040
+ identifierFormat: true,
16041
+ acceptedClockSkewMs: true,
16042
+ attributeConsumingServiceIndex: true,
16043
+ disableRequestedAuthnContext: true,
16044
+ attributeMappings: true
16045
+ };
16046
+ const OKTA_CONFIG_RESPONSE_FIELDS = {
16047
+ audience: true,
16048
+ clientId: true,
16049
+ authServerId: true,
16050
+ useOrgAuthServer: true
16051
+ };
16052
+ /** An identity provider as the admin routes return it: configuration only, no credential values. */
16053
+ const identityProviderResponseSchema = z$2.object({
16054
+ ...passthroughShape({
16055
+ id: true,
16056
+ name: true,
16057
+ emailDomain: true,
16058
+ type: true,
16059
+ isActive: true,
16060
+ samlConfig: true,
16061
+ oktaConfig: true,
16062
+ createdAt: true,
16063
+ updatedAt: true,
16064
+ createdBy: true
16065
+ }),
16066
+ samlConfig: z$2.object(passthroughShape(SAML_CONFIG_RESPONSE_FIELDS)).nullish(),
16067
+ oktaConfig: z$2.object(passthroughShape(OKTA_CONFIG_RESPONSE_FIELDS)).nullish()
16068
+ });
16069
+ /**
16070
+ * Which credentials an identity provider has stored: flags, never values. The single source for
16071
+ * the repository that computes them and the admin UI that reads them.
16072
+ */
16073
+ const identityProviderSecretPresenceSchema = z$2.object({
16074
+ decryptionPvk: z$2.boolean(),
16075
+ privateCert: z$2.boolean(),
16076
+ clientSecret: z$2.boolean()
16077
+ });
16078
+ /** The admin list row: the provider plus which credentials are stored. */
16079
+ const identityProviderListItemResponseSchema = identityProviderResponseSchema.extend({ hasSecrets: identityProviderSecretPresenceSchema });
16080
+ z$2.array(identityProviderListItemResponseSchema);
16081
+ /** An API key as the admin routes return it: everything but the secret material. */
16082
+ const userApiKeyResponseSchema = z$2.object(passthroughShape({
16083
+ id: true,
16084
+ userId: true,
16085
+ name: true,
16086
+ keyPrefix: true,
16087
+ scopes: true,
16088
+ status: true,
16089
+ expiresAt: true,
16090
+ lastUsedAt: true,
16091
+ revokedAt: true,
16092
+ revokedBy: true,
16093
+ revokedReason: true,
16094
+ callbackSigningSecretCreatedAt: true,
16095
+ rateLimit: true,
16096
+ usage: true,
16097
+ metadata: true,
16098
+ productId: true,
16099
+ productName: true,
16100
+ billingOwnerType: true,
16101
+ organizationId: true,
16102
+ agentId: true,
16103
+ allowedOrigins: true,
16104
+ identifiedClientIds: true,
16105
+ preauthorizedLakeIds: true,
16106
+ branding: true,
16107
+ spendCap: true,
16108
+ ownerHasWhitelabel: true,
16109
+ createdAt: true,
16110
+ updatedAt: true
16111
+ }));
16112
+ /**
16113
+ * A key's live minute/day rate-limit counters. The single source for the server's
16114
+ * `RateLimitUsage` and the admin client's response type, so a new field cannot be stripped here
16115
+ * while the producer and the UI both expect it.
16116
+ */
16117
+ const apiKeyRateLimitUsageSchema = z$2.object({
16118
+ minute: z$2.number(),
16119
+ day: z$2.number(),
16120
+ /** Epoch seconds when the current minute window ends; undefined when no window is open (count 0). */
16121
+ minuteResetAt: z$2.number().optional(),
16122
+ /** Epoch seconds when the current day window ends; undefined when no window is open (count 0). */
16123
+ dayResetAt: z$2.number().optional()
16124
+ });
16125
+ z$2.object({
16126
+ apiKeys: z$2.array(userApiKeyResponseSchema),
16127
+ liveUsage: z$2.record(z$2.string(), apiKeyRateLimitUsageSchema)
16128
+ });
16129
+ const emailVerificationUserResponseSchema = z$2.object(passthroughShape({
16130
+ _id: true,
16131
+ username: true,
16132
+ email: true,
16133
+ name: true,
16134
+ emailVerified: true,
16135
+ emailVerifiedAt: true,
16136
+ emailVerificationSentAt: true,
16137
+ emailVerificationExpires: true,
16138
+ pendingEmail: true,
16139
+ pendingEmailSentAt: true,
16140
+ pendingEmailExpires: true,
16141
+ createdAt: true
16142
+ }));
16143
+ z$2.object({
16144
+ users: z$2.array(emailVerificationUserResponseSchema),
16145
+ pagination: z$2.object({
16146
+ page: z$2.number(),
16147
+ limit: z$2.number(),
16148
+ totalPages: z$2.number(),
16149
+ totalCount: z$2.number()
16150
+ })
16151
+ });
15320
16152
  const questMasterPlanResponseSchema = z$2.object(passthroughShape({
15321
16153
  id: true,
15322
16154
  createdAt: true,
@@ -15767,6 +16599,14 @@ const EmbeddingsErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.enum([
15767
16599
  "provider_not_configured",
15768
16600
  "provider_rejected"
15769
16601
  ]).optional() });
16602
+ const MAX_REFINE_TEXT_CHARS = 1e4;
16603
+ const MAX_REFINE_CONTEXT_CHARS = 1e4;
16604
+ const TextRefinementRequestSchema = z$1.object({
16605
+ text: z$1.string().min(1).max(MAX_REFINE_TEXT_CHARS),
16606
+ context: z$1.string().max(MAX_REFINE_CONTEXT_CHARS).optional()
16607
+ });
16608
+ const TextRefinementResponseSchema = z$1.object({ text: z$1.string() });
16609
+ const TextRefinementErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.enum(["provider_not_configured", "provider_rejected"]).optional() });
15770
16610
  /**
15771
16611
  * Every tool name in `b4mLLMTools`. Exhaustive by annotation - do not widen the type.
15772
16612
  */
@@ -15868,6 +16708,19 @@ const InternalTeamMemberSchema = z$1.object({
15868
16708
  isActive: z$1.boolean().optional()
15869
16709
  });
15870
16710
  z$1.array(InternalTeamMemberSchema);
16711
+ /** An internal team member as the admin routes return it. */
16712
+ const internalTeamMemberResponseSchema = z$1.object(passthroughShape({
16713
+ id: true,
16714
+ name: true,
16715
+ phone: true,
16716
+ email: true,
16717
+ role: true,
16718
+ department: true,
16719
+ isActive: true,
16720
+ createdAt: true,
16721
+ updatedAt: true
16722
+ }));
16723
+ z$1.array(internalTeamMemberResponseSchema);
15871
16724
  /**
15872
16725
  * Lattice Zod Schemas
15873
16726
  *
@@ -16654,6 +17507,32 @@ const UpdateProjectRequestSchema = z$1.object({
16654
17507
  description: z$1.string().min(1).optional()
16655
17508
  }).strict();
16656
17509
  /**
17510
+ * Wire schemas for the Skill Market's Claude Code plugin feed
17511
+ * (api-contract/contracts/skillMarket.contract.ts). The 200 body is a Claude Code
17512
+ * `marketplace.json`, so its field names follow Claude Code, not this API's snake_case.
17513
+ */
17514
+ const ClaudeMarketplaceQuerySchema = z$1.object({ orgId: z$1.string().min(1) });
17515
+ const ClaudePluginZipParamSchema = z$1.object({ id: z$1.string().min(1) });
17516
+ const ClaudePluginZipQuerySchema = z$1.object({ version: z$1.coerce.number().int().positive().optional() });
17517
+ const ClaudeMarketplacePluginSchema = z$1.object({
17518
+ name: z$1.string(),
17519
+ description: z$1.string(),
17520
+ /** The listing's approved version number; Claude Code offers an update when it changes. */
17521
+ version: z$1.string(),
17522
+ source: z$1.object({
17523
+ source: z$1.literal("archive"),
17524
+ url: z$1.string(),
17525
+ sha256: z$1.string()
17526
+ })
17527
+ });
17528
+ const ClaudeMarketplaceSchema = z$1.object({
17529
+ name: z$1.string(),
17530
+ owner: z$1.object({ name: z$1.string() }),
17531
+ /** Claude Code uninstalls installed plugins this feed stops listing, at the next session start. */
17532
+ forceRemoveDeletedPlugins: z$1.boolean(),
17533
+ plugins: z$1.array(ClaudeMarketplacePluginSchema)
17534
+ });
17535
+ /**
16657
17536
  * The public projection of `ModelInfo` served by `GET /api/v1/models`.
16658
17537
  *
16659
17538
  * Deliberately narrower than `ModelInfo`: every field here is one an integrator can pin, so
@@ -16733,8 +17612,8 @@ const ListModelsResponseSchema = paginatedResponseSchema(z$1.object({
16733
17612
  reasons_within_output_budget: z$1.boolean().describe("Whether reasoning tokens are spent inside `max_output_tokens` on every turn. When true, a small `max_tokens` can be used up by reasoning alone and return an empty reply, so leave room above the answer you expect."),
16734
17613
  reasoning_efforts: z$1.array(CompletionReasoningEffortSchema).describe("The `reasoningEffort` levels this model acts on, lowest first; empty when it ignores the option. `none` is listed only where it turns reasoning off. Each level is mapped to the nearest one the provider offers. A listed level can still be dropped on one request: some OpenAI models drop it on tool turns, and Claude models with a fixed thinking budget drop it when `max_tokens` leaves no room for the budget plus the answer."),
16735
17614
  supports_tools: z$1.boolean(),
16736
- supports_tool_search: z$1.boolean().describe("Whether the model accepts the tool search server tool and `defer_loading: true` on tool definitions, so a large tool catalog is searched on demand instead of loaded into context up front. Describes the model: `/api/ai/v1/completions` honours it only for models whose `backend` is `anthropic`. A `bedrock` model with this flag set is not served with tool search there yet: a `serverTool` entry is refused with `errorCode: \"tool_search_unsupported\"` and `defer_loading` is ignored."),
16737
- supports_tool_changes: z$1.boolean().describe("Whether a system-role message can carry `tool_addition` and `tool_removal` blocks, which add or remove a declared tool mid-conversation. Describes the model only: `/api/ai/v1/completions` does not accept these blocks yet, and will accept them first for models whose `backend` is `anthropic`."),
17615
+ supports_tool_search: z$1.boolean().describe("Whether the model accepts the tool search server tool and `defer_loading: true` on tool definitions, so a large tool catalog is searched on demand instead of loaded into context up front. Describes the model: `/api/ai/v1/completions` honours it for every model with this flag set, whether its `backend` is `anthropic` or `bedrock`."),
17616
+ supports_tool_changes: z$1.boolean().describe("Whether a system-role message can carry `tool_addition` and `tool_removal` blocks, which add or remove a declared tool mid-conversation. Describes the model: `/api/ai/v1/completions` honours it only for models whose `backend` is `anthropic`; a `bedrock` model with this flag set is refused there with `errorCode: \"tool_changes_unsupported\"`."),
16738
17617
  supports_vision: z$1.boolean(),
16739
17618
  deprecation_date: z$1.string().nullable().describe("`YYYY-MM-DD`; null when no retirement is scheduled."),
16740
17619
  replaced_by: z$1.string().nullable().describe("The id of the recommended successor, when there is one."),
@@ -16860,7 +17739,7 @@ const GenerateImageIvokeParamsSchema = OpenAIImageGenerationInput.extend({
16860
17739
  referenceImageFabFileIds: z$1.array(z$1.string()).max(15).optional(),
16861
17740
  tools: z$1.array(z$1.union([b4mLLMTools, z$1.string()])).optional(),
16862
17741
  safety_tolerance: BFLSafetyToleranceSchema,
16863
- prompt_upsampling: z$1.boolean().optional(),
17742
+ prompt_upsampling: z$1.boolean().prefault(false).describe("Let the provider rewrite and expand the prompt before rendering. Applies to Black Forest Labs (FLUX) models only; other models ignore it. Defaults to false."),
16864
17743
  seed: z$1.number().nullable().optional(),
16865
17744
  output_format: ImageOutputFormatSchema.nullable().optional(),
16866
17745
  /** Resolved by the API route's prompt resolver. Defaults to 'fresh' for first-turn or sessions with no prior image. */
@@ -16990,7 +17869,13 @@ z$1.object({
16990
17869
  "tool"
16991
17870
  ]),
16992
17871
  content: z$1.union([z$1.string(), z$1.array(z$1.any())]),
16993
- fabFileIds: z$1.array(z$1.string()).optional()
17872
+ fabFileIds: z$1.array(z$1.string()).optional(),
17873
+ /**
17874
+ * Send this message in the current user turn instead of the system prompt, keeping the
17875
+ * system prefix byte-stable for prompt caching. Only for content that changes every turn
17876
+ * (a view description, a per-send brief); a caller's actual system prompt must not set it.
17877
+ */
17878
+ perTurn: z$1.boolean().optional()
16994
17879
  })).optional(),
16995
17880
  /** Dashboard related params */
16996
17881
  dashboardParams: DashboardParamsSchema.optional(),
@@ -17179,11 +18064,15 @@ const VideoGenerationErrorResponseSchema = ApiErrorSchema.extend({ errorCode: z$
17179
18064
  const CreateVideoGenerationBodySchema = z$1.strictObject({
17180
18065
  model: z$1.string().min(1).describe("A model id from GET /api/v1/video-models."),
17181
18066
  prompt: z$1.string().min(1).max(4e3),
17182
- mode: z$1.enum(VIDEO_MODES).optional().describe("Defaults to image_to_video when input_image_file_id is set, else text_to_video."),
18067
+ mode: z$1.enum(VIDEO_MODES).optional().describe("Defaults to image_to_video when a first_frame input (or input_image_file_id) is set, else text_to_video."),
17183
18068
  duration_seconds: z$1.number().positive().optional().describe("Defaults to the model's default duration."),
17184
18069
  aspect_ratio: z$1.enum(ASPECT_RATIOS).optional(),
17185
18070
  resolution: z$1.enum(RESOLUTION_TIERS).optional(),
17186
- input_image_file_id: z$1.string().min(1).optional().describe("An image file you own, for image_to_video."),
18071
+ inputs: z$1.array(z$1.strictObject({
18072
+ file_id: z$1.string().min(1),
18073
+ role: z$1.enum(VIDEO_INPUT_ROLES)
18074
+ })).max(8).optional().describe("Image files you own, each tagged with what it is for. image_to_video needs a first_frame. The roles a model accepts, and how many of each, are its input_roles in GET /api/v1/video-models."),
18075
+ input_image_file_id: z$1.string().min(1).optional().describe("Deprecated: send inputs with role first_frame instead. Cannot be combined with inputs."),
17187
18076
  audio: z$1.boolean().optional(),
17188
18077
  organization_id: BillingOrganizationIdSchema
17189
18078
  });
@@ -17225,6 +18114,10 @@ const VideoModelSchema = z$1.object({
17225
18114
  display_name: z$1.string(),
17226
18115
  provider: z$1.string(),
17227
18116
  modes: z$1.array(z$1.enum(VIDEO_MODES)),
18117
+ input_roles: z$1.array(z$1.object({
18118
+ role: z$1.string(),
18119
+ max: z$1.number().int().positive()
18120
+ })).optional().describe(`The inputs[] roles this model accepts and the most of each one request may carry. Roles today: ${VIDEO_INPUT_ROLES.join(", ")}; ignore any you do not know.`),
17228
18121
  duration: z$1.union([z$1.object({
17229
18122
  kind: z$1.literal("range"),
17230
18123
  min: z$1.number(),
@@ -17802,7 +18695,7 @@ defineEndpoint({
17802
18695
  path: "/api/ai/v1/completions",
17803
18696
  operationId: "createCompletion",
17804
18697
  summary: "Create a chat completion",
17805
- description: "OpenAI-compatible completion. A successful request is ALWAYS answered with an SSE stream (`text/event-stream`), regardless of the `stream` flag: a `meta` event, then `content`/`tool_use` events carrying `usage`/`credits`, terminated by `data: [DONE]`. The terminal event carries `stopReason`; treat `max_tokens` as a TRUNCATED reply rather than a complete one, and note that omitting `max_tokens` on the request lets the server size the output ceiling for the model (recommended for reasoning models, which spend thinking tokens inside that ceiling). A message `content` may be a string or an array of parts; image parts are accepted in OpenAI Chat (`image_url`), OpenAI Responses (`input_image`) or Anthropic (`image` with a `source`) form and are translated to whatever the target model speaks. Authenticate with an API key (`b4m_live_`) or a JWT.\n\nTOOL SEARCH. On a model whose `GET /api/v1/models` row has `supports_tool_search: true` and `backend: \"anthropic\"`, a `tools` entry may set `toolSchema.defer_loading: true`, and a `{ \"serverTool\": { \"type\": \"tool_search_tool_regex_20251119\", \"name\": \"tool_search_tool_regex\" } }` (or the `bm25` pair) entry adds Anthropic's tool search, so deferred definitions load only when the model searches for them. The stream then carries `provider_block` events (`{ type, provider: \"anthropic\", block }`) for the server-run search; echo each block back verbatim, in order, in the assistant `messages` content of the next request, and never answer one with a `tool_result`. When the server-run search loop hits its iteration limit the stream ends with `stopReason: \"pause_turn\"`; continue by re-sending the conversation with that assistant turn's provider blocks echoed. A client-side search may return `{ \"type\": \"tool_reference\", \"tool_name\": \"...\" }` blocks inside a `tool_result`'s content. On every other model, Bedrock-served Claude included, `defer_loading` is ignored (every tool is sent as usual) and a server tool, a `tool_reference` or an echoed tool search block is refused with a `400` rather than run without it.\n\nERRORS. Everything checked before the model provider is called fails with a JSON error body and a real HTTP status, before any stream opens: a malformed or otherwise invalid request or an unknown `model` `400` (`errorCode: \"unknown_model\"` for the latter), a bad credential `401`, a key without a required scope `403` (with `required_scopes`), a body over 25 MB `413`, a body that fails schema validation `422`, insufficient credits `422` with `errorCode: \"insufficient_credits\"` (personal or org balance, or an org per-member credit cap), a rate limit `429` with `Retry-After`, and a catalogued model whose provider is not configured on this deployment `503` with `errorCode: \"provider_not_configured\"` (retryable). Once the stream has opened the status stays `200` and a failure arrives as one terminal in-band `error` event. Branch on that event's `code`, never on `message`, which is prose and may change: `insufficient_credits` (buy credits) when credits run out mid-generation, `provider_rejected` when the model provider refused our credentials, `content_blocked` when the model declined the request (do not retry it unchanged), and `provider_error` for any other provider failure. Treat `code` as optional.",
18698
+ description: "OpenAI-compatible completion. A successful request is ALWAYS answered with an SSE stream (`text/event-stream`), regardless of the `stream` flag: a `meta` event, then `content`/`tool_use` events carrying `usage`/`credits`, terminated by `data: [DONE]`. The terminal event carries `stopReason`; treat `max_tokens` as a TRUNCATED reply rather than a complete one, and note that omitting `max_tokens` on the request lets the server size the output ceiling for the model (recommended for reasoning models, which spend thinking tokens inside that ceiling). A message `content` may be a string or an array of parts; image parts are accepted in OpenAI Chat (`image_url`), OpenAI Responses (`input_image`) or Anthropic (`image` with a `source`) form and are translated to whatever the target model speaks. Authenticate with an API key (`b4m_live_`) or a JWT.\n\nTOOL SEARCH. On a model whose `GET /api/v1/models` row has `supports_tool_search: true` (`backend` `\"anthropic\"` or `\"bedrock\"`), a `tools` entry may set `toolSchema.defer_loading: true`, and a `{ \"serverTool\": { \"type\": \"tool_search_tool_regex_20251119\", \"name\": \"tool_search_tool_regex\" } }` (or the `bm25` pair) entry adds Anthropic's tool search, so deferred definitions load only when the model searches for them. The stream then carries `provider_block` events (`{ type, provider: \"anthropic\", block }`) for the server-run search; echo each block back verbatim, in order, in the assistant `messages` content of the next request, and never answer one with a `tool_result`. When the server-run search loop hits its iteration limit the stream ends with `stopReason: \"pause_turn\"`; continue by re-sending the conversation with that assistant turn's provider blocks echoed. A client-side search may return `{ \"type\": \"tool_reference\", \"tool_name\": \"...\" }` blocks inside a `tool_result`'s content. On every other model `defer_loading` is ignored (every tool is sent as usual) and a server tool, a `tool_reference` or an echoed tool search block is refused with a `400` rather than run without it.\n\nERRORS. Everything checked before the model provider is called fails with a JSON error body and a real HTTP status, before any stream opens: an empty `messages` array, a conversation with no non-empty user or assistant message (system-only, or only blank content), a malformed request, or an unknown `model` `400` (`errorCode: \"unknown_model\"` for an unknown model), a bad credential `401`, a key without a required scope `403` (with `required_scopes`), a body over 25 MB `413`, any other body that fails schema validation `422`, insufficient credits `422` with `errorCode: \"insufficient_credits\"` (personal or org balance, or an org per-member credit cap), a rate limit `429` with `Retry-After`, and a catalogued model whose provider is not configured on this deployment, or a model that could only come from a self-hosted model server (Ollama, local image) that is currently unreachable, `503` with `errorCode: \"provider_not_configured\"` (retryable). A provider rejection of caller input that happens before the stream opens is also a JSON `400`, with `errorCode: \"invalid_request\"` (do not retry it unchanged). Once the stream has opened the status stays `200` and a failure arrives as one terminal in-band `error` event. Branch on that event's `code`, never on `message`, which is prose and may change: `insufficient_credits` (buy credits) when credits run out mid-generation, `invalid_request` when the provider rejects caller input (do not retry it unchanged), `provider_rejected` when the model provider refused our credentials, `content_blocked` when the model declined the request (do not retry it unchanged), and `provider_error` for any other provider failure. Treat `code` as optional.",
17806
18699
  tags: ["AI"],
17807
18700
  auth: "apiKeyOrJwt",
17808
18701
  scopes: ["ai:chat", "ai:generate"],
@@ -17836,7 +18729,7 @@ defineEndpoint({
17836
18729
  }
17837
18730
  },
17838
18731
  400: {
17839
- description: "Malformed or otherwise invalid request (no `errorCode`), or a `model` the caller cannot use (`errorCode: \"unknown_model\"`): not in the catalog or not available to the caller. List valid ids with `GET /api/v1/models`. Tool search requests are refused with: `tool_search_unsupported` (a server tool, `tool_reference` or echoed tool search block on a model that cannot serve tool search here), `all_tools_deferred` (every tool deferred and no server tool), `deferred_tool_cache_control` (`cache_control` on a deferred tool), `unknown_tool_reference` (a `tool_reference` naming a tool not in `tools`, or carrying no string `tool_name`). Mid-conversation tool changes are refused with: `tool_changes_unsupported` (a `tool_addition` / `tool_removal` system message on a model that cannot serve it here), `invalid_tool_change` (a malformed block, or one placed where the provider does not accept it), and `unknown_tool_reference` (a changed tool not in `tools`).",
18732
+ description: "Malformed or otherwise invalid request (no `errorCode`), provider-rejected caller input (`errorCode: \"invalid_request\"`), or a `model` the caller cannot use (`errorCode: \"unknown_model\"`): not in the catalog or not available to the caller. List valid ids with `GET /api/v1/models`. Tool search requests are refused with: `tool_search_unsupported` (a server tool, `tool_reference` or echoed tool search block on a model that cannot serve tool search here), `all_tools_deferred` (every tool deferred and no server tool), `deferred_tool_cache_control` (`cache_control` on a deferred tool), `unknown_tool_reference` (a `tool_reference` naming a tool not in `tools`, or carrying no string `tool_name`). Mid-conversation tool changes are refused with: `tool_changes_unsupported` (a `tool_addition` / `tool_removal` system message on a model that cannot serve it here), `invalid_tool_change` (a malformed block, or one placed where the provider does not accept it), and `unknown_tool_reference` (a changed tool not in `tools`).",
17840
18733
  schema: CompletionsBadRequestErrorSchema
17841
18734
  },
17842
18735
  413: {
@@ -17857,7 +18750,7 @@ defineEndpoint({
17857
18750
  schema: ApiErrorSchema
17858
18751
  },
17859
18752
  503: {
17860
- description: "Temporarily unavailable: the model's provider is not configured on this deployment (`errorCode: \"provider_not_configured\"`), or the database is unreachable (no `errorCode`). Retry later.",
18753
+ description: "Temporarily unavailable: the model's provider is not configured on this deployment, or the self-hosted model server that lists it (Ollama, local image) is unreachable (`errorCode: \"provider_not_configured\"`), or the database is unreachable (no `errorCode`). Retry later.",
17861
18754
  schema: ProviderNotConfiguredErrorSchema
17862
18755
  }
17863
18756
  },
@@ -18660,7 +19553,7 @@ defineEndpoint({
18660
19553
  streaming: false
18661
19554
  }
18662
19555
  });
18663
- const EXAMPLE_REQUEST$1 = {
19556
+ const EXAMPLE_REQUEST$2 = {
18664
19557
  model: "text-embedding-3-small",
18665
19558
  input: ["The quick brown fox", "jumps over the lazy dog"],
18666
19559
  dimensions: 1024
@@ -18675,7 +19568,7 @@ defineEndpoint({
18675
19568
  auth: "apiKeyOrJwt",
18676
19569
  scopes: ["ai:generate"],
18677
19570
  request: EmbeddingsRequestSchema,
18678
- requestExample: EXAMPLE_REQUEST$1,
19571
+ requestExample: EXAMPLE_REQUEST$2,
18679
19572
  responses: {
18680
19573
  200: {
18681
19574
  description: "One embedding per input, in input order.",
@@ -18732,6 +19625,55 @@ defineEndpoint({
18732
19625
  }
18733
19626
  },
18734
19627
  emitsRateLimitHeaders: true,
19628
+ codeSample: {
19629
+ authToken: "b4m_live_<key>",
19630
+ streaming: false,
19631
+ body: EXAMPLE_REQUEST$2
19632
+ }
19633
+ });
19634
+ const EXAMPLE_REQUEST$1 = {
19635
+ text: "a dog on a beach",
19636
+ context: "prompt for an image generator"
19637
+ };
19638
+ defineEndpoint({
19639
+ method: "post",
19640
+ path: "/api/v1/text-refinements",
19641
+ operationId: "createTextRefinement",
19642
+ summary: "Refine text",
19643
+ description: `Rewrites \`text\` to be more concise, clear and detailed while keeping its intent. \`context\` is optional background that steers the rewrite. \`text\` is at most ${MAX_REFINE_TEXT_CHARS} characters and \`context\` at most ${MAX_REFINE_CONTEXT_CHARS}. Output is capped at about 800 tokens, and identical requests are served from a cache for about five minutes. Not billed in credits. Authenticate with an API key (\`b4m_live_\`) carrying \`ai:generate\`, or a JWT.`,
19644
+ tags: ["AI"],
19645
+ auth: "apiKeyOrJwt",
19646
+ scopes: ["ai:generate"],
19647
+ request: TextRefinementRequestSchema,
19648
+ requestExample: EXAMPLE_REQUEST$1,
19649
+ responses: {
19650
+ 200: {
19651
+ description: "The refined text.",
19652
+ schema: TextRefinementResponseSchema,
19653
+ example: { text: "A golden retriever running along a sunlit beach at sunset, waves breaking behind it." }
19654
+ },
19655
+ 401: {
19656
+ description: "Missing/invalid credentials, or the text-refinement provider refused the configured key (`errorCode: \"provider_rejected\"`).",
19657
+ schema: TextRefinementErrorSchema
19658
+ },
19659
+ 429: {
19660
+ description: "Per-key rate limit exceeded.",
19661
+ schema: ApiErrorSchema
19662
+ },
19663
+ 500: {
19664
+ description: "Unexpected server error (for example the refinement cache is unavailable).",
19665
+ schema: ApiErrorSchema
19666
+ },
19667
+ 502: {
19668
+ description: "The model call failed for a reason other than a rejected credential, or returned no refined text.",
19669
+ schema: ApiErrorSchema
19670
+ },
19671
+ 503: {
19672
+ description: "This deployment has no operations model configured (`errorCode: \"provider_not_configured\"`).",
19673
+ schema: TextRefinementErrorSchema
19674
+ }
19675
+ },
19676
+ emitsRateLimitHeaders: true,
18735
19677
  codeSample: {
18736
19678
  authToken: "b4m_live_<key>",
18737
19679
  streaming: false,
@@ -19385,7 +20327,7 @@ defineEndpoint({
19385
20327
  path: "/api/v1/transcriptions",
19386
20328
  operationId: "createTranscription",
19387
20329
  summary: "Transcribe audio",
19388
- description: "Transcribes an upload minted by `POST /api/v1/transcriptions/uploads` and returns the text. Call it within 5 minutes of minting the upload. Credits are charged by file size. The uploaded audio is deleted when the call returns, whether it succeeds or fails, so each upload can be transcribed once.\n\n**Limit:** this call is synchronous. The request times out after about 60 seconds, while the AWS speech backend can take up to about 5 minutes on long audio, so a long recording can fail with a gateway timeout (`504`) instead of returning text. A timed-out upload may not be deleted right away (it expires on its own), so do not retry with the same `file_key`: mint a new upload, or send shorter audio.",
20330
+ description: "Transcribes an upload minted by `POST /api/v1/transcriptions/uploads` and returns the text. Call it within 5 minutes of minting the upload. Credits are charged by file size. The uploaded audio is deleted when the call returns, whether it succeeds or fails, so each upload can be transcribed once.\n\n**Limit:** this call is synchronous. Provider work stops being awaited after 45 seconds from request arrival, leaving time to return reserved credits and delete the upload before the hosted server stops at 60 seconds. A slow recording returns a gateway timeout (`504`) instead of text. AWS batch jobs cannot be cancelled remotely and may finish later; their output expires automatically. Do not retry with the same `file_key`: mint a new upload and send shorter audio. If cleanup fails, the upload may not be deleted right away (it expires on its own).",
19389
20331
  tags: ["Transcriptions"],
19390
20332
  auth: "apiKeyOrJwt",
19391
20333
  scopes: ["ai:generate"],
@@ -19415,6 +20357,10 @@ defineEndpoint({
19415
20357
  503: {
19416
20358
  description: "This deployment has no speech model, or no credential for its provider, configured (`errorCode: \"provider_not_configured\"`).",
19417
20359
  schema: ProviderNotConfiguredErrorSchema
20360
+ },
20361
+ 504: {
20362
+ description: "The transcription exceeded the request deadline; reserved credits are returned.",
20363
+ schema: ApiErrorSchema
19418
20364
  }
19419
20365
  },
19420
20366
  codeSample: {
@@ -19521,6 +20467,35 @@ defineEndpoint({
19521
20467
  }
19522
20468
  }
19523
20469
  });
20470
+ defineEndpoint({
20471
+ method: "get",
20472
+ path: "/api/v1/whats-new/media/{id}/{asset}",
20473
+ operationId: "getWhatsNewMedia",
20474
+ summary: "Get release note media",
20475
+ description: "Redirects to one file of a published release note video: `landscape` and `portrait` are the 16:9 and 9:16 MP4s, the `-poster` assets their still images. The `video` URLs that listWhatsNew returns for uploaded files point here, so a browser can use them as `src` and `poster` as they are. Public: no credentials are needed, and the note must be one listWhatsNew would show. The redirect is cacheable for up to 10 minutes and its target is signed for 1 hour, longer than that, so follow it rather than storing the target. Rate limited per client IP.",
20476
+ tags: ["Release notes"],
20477
+ auth: "public",
20478
+ pathParams: ReleaseNoteMediaParamsSchema,
20479
+ responses: {
20480
+ 303: {
20481
+ description: "`Location` is a time-limited signed GET for the file.",
20482
+ noBody: true,
20483
+ headers: { Location: "Signed URL of the file." }
20484
+ },
20485
+ 404: {
20486
+ description: "No published note has this id, or the note has no such file, or the file is not an upload this deployment serves (a linked URL is listed as is, not through this route).",
20487
+ schema: ApiErrorSchema
20488
+ },
20489
+ 429: {
20490
+ description: "Per-IP rate limit exceeded.",
20491
+ schema: ApiErrorSchema
20492
+ },
20493
+ 500: {
20494
+ description: "Unexpected server error.",
20495
+ schema: ApiErrorSchema
20496
+ }
20497
+ }
20498
+ });
19524
20499
  /**
19525
20500
  * The read/write/query API-key scope sets the data-lake doors accept (OR semantics), shared by the
19526
20501
  * SPA routes (`apps/client/server/dataLakes/dataLakeScopes.ts` re-exports these) and the public
@@ -19873,11 +20848,11 @@ defineEndpoint({
19873
20848
  schema: ApiErrorSchema
19874
20849
  },
19875
20850
  404: {
19876
- description: "`input_image_file_id` does not name an image you own (`errorCode: input_image_not_found`). A nonexistent `organization_id` is `404` only for an admin caller; for anyone else it is `403`.",
20851
+ description: "An `inputs` file (or `input_image_file_id`) does not name an image you own (`errorCode: input_image_not_found`). A nonexistent `organization_id` is `404` only for an admin caller; for anyone else it is `403`.",
19877
20852
  schema: VideoGenerationErrorResponseSchema
19878
20853
  },
19879
20854
  422: {
19880
- description: "The request cannot run: a body or capability violation (`errorCode` names it, e.g. `unsupported_duration`), an unknown or unavailable model (`model_unavailable`), a model your administrator disabled (`model_disabled`), too few credits (`insufficient_credits`), or an Idempotency-Key problem (`invalid_idempotency_key`, `idempotency_key_reused`).",
20855
+ description: "The request cannot run: a body or capability violation (`errorCode` names it, e.g. `unsupported_duration`, or `unsupported_input_role` for an input role the model does not take), an unknown or unavailable model (`model_unavailable`), a model your administrator disabled (`model_disabled`), too few credits (`insufficient_credits`), or an Idempotency-Key problem (`invalid_idempotency_key`, `idempotency_key_reused`).",
19881
20856
  schema: VideoGenerationErrorResponseSchema
19882
20857
  },
19883
20858
  429: rateLimited$1
@@ -20720,6 +21695,94 @@ defineEndpoint({
20720
21695
  streaming: false
20721
21696
  }
20722
21697
  });
21698
+ const AUTH_NOTE = "Authenticate with an API key (`b4m_live_`) carrying `skills:read`, sent as `Authorization: Bearer`. Claude Code sends that header when it is set as the marketplace source's `headers` in its settings, and forwards it to the same-origin plugin archives.";
21699
+ defineEndpoint({
21700
+ method: "get",
21701
+ path: "/api/v1/skills/market/claude-marketplace.json",
21702
+ operationId: "getClaudeMarketplace",
21703
+ summary: "Get an organization's Claude Code plugin marketplace",
21704
+ description: `Returns a Claude Code \`marketplace.json\` listing the organization's approved Organization-visibility Skill Market listings, one \`archive\` plugin each, pinned by \`sha256\`. Public and unlisted listings are not included. Responses are never cacheable. ${AUTH_NOTE}`,
21705
+ tags: ["Skills"],
21706
+ auth: "apiKeyOrJwt",
21707
+ scopes: ["skills:read"],
21708
+ queryParams: ClaudeMarketplaceQuerySchema,
21709
+ emitsRateLimitHeaders: true,
21710
+ responses: {
21711
+ 200: {
21712
+ description: "The marketplace.",
21713
+ schema: ClaudeMarketplaceSchema,
21714
+ example: {
21715
+ name: `b4m-org-${EXAMPLE_RESOURCE_ID}`,
21716
+ owner: { name: "Acme" },
21717
+ forceRemoveDeletedPlugins: true,
21718
+ plugins: [{
21719
+ name: "review-pr",
21720
+ description: "Review a pull request against the team checklist.",
21721
+ version: "3",
21722
+ source: {
21723
+ source: "archive",
21724
+ url: `https://app.bike4mind.com/api/v1/skills/market/${EXAMPLE_RESOURCE_ID}/claude-plugin.zip?version=3`,
21725
+ sha256: "a3f1c0de9b8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a392817060f5e"
21726
+ }
21727
+ }]
21728
+ }
21729
+ },
21730
+ 403: {
21731
+ description: "The API key lacks `skills:read`, or the caller is not a member of the organization.",
21732
+ schema: ScopeForbiddenErrorSchema
21733
+ },
21734
+ 429: {
21735
+ description: "Per-user rate limit exceeded.",
21736
+ schema: ApiErrorSchema
21737
+ }
21738
+ },
21739
+ codeSample: {
21740
+ authToken: "b4m_live_<key>",
21741
+ streaming: false,
21742
+ body: {}
21743
+ }
21744
+ });
21745
+ defineEndpoint({
21746
+ method: "get",
21747
+ path: "/api/v1/skills/market/{id}/claude-plugin.zip",
21748
+ operationId: "getClaudePluginZip",
21749
+ summary: "Download a Skill Market listing as a Claude Code plugin",
21750
+ description: `A zip holding an approved version as \`skills/<name>/SKILL.md\`. The optional \`version\` query pins the listing version advertised by the feed, so later approvals do not change its bytes or \`sha256\`. Without it, returns the current approved version. ${AUTH_NOTE}`,
21751
+ tags: ["Skills"],
21752
+ auth: "apiKeyOrJwt",
21753
+ scopes: ["skills:read"],
21754
+ pathParams: ClaudePluginZipParamSchema,
21755
+ queryParams: ClaudePluginZipQuerySchema,
21756
+ validationErrorStatus: 400,
21757
+ emitsRateLimitHeaders: true,
21758
+ responses: {
21759
+ 200: {
21760
+ description: "The plugin archive.",
21761
+ contentType: "application/zip"
21762
+ },
21763
+ 400: {
21764
+ description: "Invalid version query.",
21765
+ schema: ApiErrorSchema
21766
+ },
21767
+ 403: {
21768
+ description: "The API key lacks `skills:read`.",
21769
+ schema: ScopeForbiddenErrorSchema
21770
+ },
21771
+ 404: {
21772
+ description: "No Organization listing with that id is visible to the caller. A listing in an org you are not in, a public or unlisted listing, and a malformed id are all reported as 404.",
21773
+ schema: ApiErrorSchema
21774
+ },
21775
+ 429: {
21776
+ description: "Per-user rate limit exceeded.",
21777
+ schema: ApiErrorSchema
21778
+ }
21779
+ },
21780
+ codeSample: {
21781
+ authToken: "b4m_live_<key>",
21782
+ streaming: false,
21783
+ body: {}
21784
+ }
21785
+ });
20723
21786
  /**
20724
21787
  * The fence language the model writes to place an inline map of search-result places in a reply.
20725
21788
  *
@@ -23448,7 +24511,7 @@ async function sleep(ms, abortSignal) {
23448
24511
  * Respects Retry-After headers when present (e.g., for 429 rate limiting).
23449
24512
  */
23450
24513
  async function withRetry(fn, options = {}) {
23451
- const { maxRetries = 3, initialDelayMs = 100, maxDelayMs = 5e3, jitterFactor = .1, isRetryable = isRetryableError, logger, abortSignal } = options;
24514
+ const { maxRetries = 3, initialDelayMs = 100, maxDelayMs = 5e3, jitterFactor = .1, isRetryable = isRetryableError, logger, abortSignal, onRetry } = options;
23452
24515
  let attempts = 0;
23453
24516
  let totalDelayMs = 0;
23454
24517
  while (true) try {
@@ -23479,6 +24542,15 @@ async function withRetry(fn, options = {}) {
23479
24542
  attempt: attempts,
23480
24543
  delayMs
23481
24544
  });
24545
+ try {
24546
+ onRetry?.({
24547
+ attempt: attempts,
24548
+ delayMs,
24549
+ error: err
24550
+ });
24551
+ } catch (callbackError) {
24552
+ logger?.warn("onRetry callback threw; continuing the retry", { error: String(callbackError) });
24553
+ }
23482
24554
  try {
23483
24555
  await sleep(delayMs, abortSignal);
23484
24556
  } catch {
@@ -26015,4 +27087,4 @@ var ConfigStore = class {
26015
27087
  }
26016
27088
  };
26017
27089
  //#endregion
26018
- export { OllamaEmbeddingModel as $, ARTIFACT_REMOVED_PLACEHOLDER as A, secureParameters as At, CreateArtifactRequestSchema as B, withRetry as Bt, htmlErrorTitle as C, isPlaceholderImageSigningSecret as Ct, AGENT_QUEST_MANIFEST as D, obfuscateApiKey as Dt, AGENT_QUEST_ID as E, joinReplySlots as Et, BriefcasePromptIdSchema as F, supportedVoiceGenerationVendor as Ft, GENERATED_IMAGE_EXTENSION_RE as G, actorKindSchema as Gt, DEGENERATE_FINISH_REASON as H, DELEGATION_PAYLOAD_SCHEMA_NAME as Ht, CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS as I, tokenEstimateMultiplier as It, LOCATION_MAP_LANGUAGE as J, frontmatterHasSourceStamp as Jt, ImageModels as K, delegationPayloadSchema as Kt, CREDIT_DEDUCT_TRANSACTION_TYPES as L, ttsBase64ResponseSchema as Lt, ArtifactTypeSchema as M, splitEquals as Mt, BRIEFCASE_CATALOG_QUERIES as N, stripDeliveredArtifactBlocks as Nt, AGENT_QUEST_MCP_URI as O, questReplyText as Ot, BedrockEmbeddingModel as P, stripToolArtifactMarkup as Pt, NotFoundError as Q, ChatModels as R, ttsRequestSchema as Rt, isValidSessionId as S, isEarlyStop as St, escapeArtifactBodyJson as T, isUserInitiatedAbort as Tt, DecisionTextPartSchema as U, actorColorIndex as Ut, DEFAULT_TTS_PROVIDER as V, ACTOR_COLOR_SLOTS as Vt, DecisionsRequestSchema as W, actorKindMarker as Wt, McpServerName as X, WEBSITE_URL as Xt, MODEL_INFO_FIELD_GROUP_OF as Y, APP_NAME as Yt, ModelBackend as Z, LOCAL_DEV_URL as _, generatedAudioResponseSchema as _t, commandTextOf as a, SLUG_OWNED_BY_OTHER as at, logger as b, googleMapsSearchUrl as bt, toolCategory as c, UnprocessableEntityError as ct, getCreditsUrl as d, WORK_ITEM_STATUSES as dt, OpenAIEmbeddingModel as et, getEnvironmentName as f, assertAuthOriginMatchesApiKey as ft, DeviceFlowClient as g, filterToolArtifactMarkup as gt, AuthenticatedApiClient as h, escapeThinkMarkers as ht, ToolCatalog as i, SEARCH_RESULT_CARDS_LANGUAGE as it, ApiKeyType as j, signImageUrl as jt, ARTIFACT_DELIVERED_PLACEHOLDER as k, replacePromptVariables as kt, writePathOf as l, VisibilitySchema as lt, resolveApiEndpoint as m, createThinkMarkerEscaper as mt, extractCompactInstructions as n, PermissionDeniedError as nt, isReadOnlyTool as o, SUBQUEST_STATUS_VALUES as ot, requireApiUrl as p, buildPromptContext as pt, ImagePromptResolutionSchema as q, selfClaimedActorKindSchema as qt, loadContextFiles as r, REVIEW_GATE_STATUS_VALUES as rt, sandboxedCommandArg as s, SupportedFabFileMimeTypes as st, ConfigStore as t, PROMPT_TEXT_MAX as tt, ApiEndpointUnconfiguredError as u, VoyageAIEmbeddingModel as ut, NotAuthenticatedError as v, getMcpProviderMetadata as vt, htmlFirstH1 as w, isRetryableError as wt, SESSION_ID_PATTERN as x, hasDeliverablePinnedArtifact as xt, parseApiUrl as y, getQuestErrorCode as yt, CompletionReasoningEffortSchema as z, ttsResponseTooLargeSchema as zt };
27090
+ export { ModelBackend as $, selfClaimedActorKindSchema as $t, ARTIFACT_REMOVED_PLACEHOLDER as A, isUserInitiatedAbort as At, ChatModels as B, supportedVoiceGenerationVendor as Bt, htmlErrorTitle as C, getMcpProviderMetadata as Ct, AGENT_QUEST_MANIFEST as D, isEarlyStop as Dt, AGENT_QUEST_ID as E, hasDeliverablePinnedArtifact as Et, BRIEFCASE_CATALOG_QUERIES as F, secureParameters as Ft, DecisionTextPartSchema as G, visibleReplyText as Gt, CreateArtifactRequestSchema as H, ttsBase64ResponseSchema as Ht, BedrockEmbeddingModel as I, signImageUrl as It, ImageModels as J, DELEGATION_PAYLOAD_SCHEMA_NAME as Jt, DecisionsRequestSchema as K, withRetry as Kt, BriefcasePromptIdSchema as L, splitEquals as Lt, ApiKeyType as M, obfuscateApiKey as Mt, ArtifactTypeSchema as N, questReplyText as Nt, AGENT_QUEST_MCP_URI as O, isPlaceholderImageSigningSecret as Ot, AudioMimeType as P, replacePromptVariables as Pt, McpServerName as Q, delegationPayloadSchema as Qt, CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS as R, stripDeliveredArtifactBlocks as Rt, isValidSessionId as S, generatedAudioResponseSchema as St, escapeArtifactBodyJson as T, googleMapsSearchUrl as Tt, DEFAULT_TTS_PROVIDER as U, ttsRequestSchema as Ut, CompletionReasoningEffortSchema as V, tokenEstimateMultiplier as Vt, DEGENERATE_FINISH_REASON as W, ttsResponseTooLargeSchema as Wt, LOCATION_MAP_LANGUAGE as X, actorKindMarker as Xt, ImagePromptResolutionSchema as Y, actorColorIndex as Yt, MODEL_INFO_FIELD_GROUP_OF as Z, actorKindSchema as Zt, LOCAL_DEV_URL as _, buildPromptContext as _t, commandTextOf as a, REVIEW_GATE_STATUS_VALUES as at, logger as b, escapeThinkMarkers as bt, toolCategory as c, SUBQUEST_STATUS_VALUES as ct, getCreditsUrl as d, TTS_MAX_INPUT_CHARS as dt, frontmatterHasSourceStamp as en, NotFoundError as et, getEnvironmentName as f, UnprocessableEntityError as ft, DeviceFlowClient as g, assertAuthOriginMatchesApiKey as gt, AuthenticatedApiClient as h, WORK_ITEM_STATUSES as ht, ToolCatalog as i, WEBSITE_URL as in, PermissionDeniedError as it, ApiKeyScope as j, joinReplySlots as jt, ARTIFACT_DELIVERED_PLACEHOLDER as k, isRetryableError as kt, writePathOf as l, SupportedFabFileMimeTypes as lt, resolveApiEndpoint as m, VoyageAIEmbeddingModel as mt, extractCompactInstructions as n, splitSkillMdFrontmatter as nn, OpenAIEmbeddingModel as nt, isReadOnlyTool as o, SEARCH_RESULT_CARDS_LANGUAGE as ot, requireApiUrl as p, VisibilitySchema as pt, GENERATED_IMAGE_EXTENSION_RE as q, ACTOR_COLOR_SLOTS as qt, loadContextFiles as r, APP_NAME as rn, PROMPT_TEXT_MAX as rt, sandboxedCommandArg as s, SLUG_OWNED_BY_OTHER as st, ConfigStore as t, readSkillMdSourceUrl as tn, OllamaEmbeddingModel as tt, ApiEndpointUnconfiguredError as u, TTS_DEFAULT_MODEL as ut, NotAuthenticatedError as v, completionsSupportsToolSearch as vt, htmlFirstH1 as w, getQuestErrorCode as wt, SESSION_ID_PATTERN as x, filterToolArtifactMarkup as xt, parseApiUrl as y, createThinkMarkerEscaper as yt, CREDIT_DEDUCT_TRANSACTION_TYPES as z, stripToolArtifactMarkup as zt };