@bike4mind/cli 0.20.0 → 0.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AgentHistoryStore-C8uUKjjC.mjs → AgentHistoryStore-T7Oh84Yn.mjs} +1454 -350
- package/dist/{ApiClient-B_CQrUiF.mjs → ApiClient-BopvMmQk.mjs} +5 -5
- package/dist/{ConfigStore-DD3DcC3-.mjs → ConfigStore-cIyF7hDg.mjs} +1048 -70
- package/dist/ProxyManager-B1jFWL7b.mjs +3 -0
- package/dist/{buildAgent-mVuXU_H4.mjs → buildAgent-P0tOMLt1.mjs} +3 -3
- package/dist/commands/acpCommand.mjs +4 -4
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +4 -4
- package/dist/commands/mcpCommand.mjs +3 -3
- package/dist/commands/pluginCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +83 -34
- package/dist/{package-BqKSCbso.mjs → package-CnVCHR3U.mjs} +1 -1
- package/dist/{serve-CuF0I5en.mjs → serve-BocVOJ3W.mjs} +2 -2
- package/package.json +9 -9
- package/dist/ProxyManager-Bqr7Lmsd.mjs +0 -3
- package/dist/{ProxyManager-C5H0pUyK.mjs → ProxyManager-C1-lgzEU.mjs} +1 -1
|
@@ -7,7 +7,7 @@ import path from "path";
|
|
|
7
7
|
import { v4 } from "uuid";
|
|
8
8
|
import * as z$2 from "zod";
|
|
9
9
|
import z, { ZodError, z as z$1 } from "zod";
|
|
10
|
-
import { hearthEventKindSchema, hearthEventRefsSchema, hearthMachineBodySchema } from "@bike4mind/hearth";
|
|
10
|
+
import { actorKindSchema, hearthEventKindSchema, hearthEventRefsSchema, hearthMachineBodySchema } from "@bike4mind/hearth";
|
|
11
11
|
import dayjs from "dayjs";
|
|
12
12
|
import timezone from "dayjs/plugin/timezone.js";
|
|
13
13
|
import utc from "dayjs/plugin/utc.js";
|
|
@@ -699,6 +699,12 @@ let KnowledgeType = /* @__PURE__ */ function(KnowledgeType) {
|
|
|
699
699
|
* so the client can branch to a targeted error UI (see `IChatHistoryItem.errorCode`).
|
|
700
700
|
* Single source of truth: the streamed-action Zod enum in `schemas/actions.ts`
|
|
701
701
|
* derives its values from this tuple, so the two can never drift.
|
|
702
|
+
*
|
|
703
|
+
* SSE-frame scoped, and a NARROWING of the platform-wide `API_ERROR_CODES`: a
|
|
704
|
+
* quest fails for billing reasons, never for the provider-configuration reasons
|
|
705
|
+
* the HTTP surface reports. The `satisfies` is what keeps it a narrowing rather
|
|
706
|
+
* than a second vocabulary - a code added here that is not in `API_ERROR_CODES`
|
|
707
|
+
* fails the build.
|
|
702
708
|
*/
|
|
703
709
|
const QUEST_ERROR_CODES = ["insufficient_credits", "spend_cap_exceeded"];
|
|
704
710
|
z$1.union([
|
|
@@ -896,6 +902,12 @@ let ChatModels = /* @__PURE__ */ function(ChatModels) {
|
|
|
896
902
|
const CHAT_MODELS = Object.values(ChatModels);
|
|
897
903
|
const supportedChatModels = z$1.enum(ChatModels);
|
|
898
904
|
/**
|
|
905
|
+
* Every `ChatModels` Gemini entry is named `gemini...` (see the GEMINI block above) - a prefix
|
|
906
|
+
* check tracks that naming convention automatically as new Gemini models are added, unlike an
|
|
907
|
+
* explicit id list that would need updating in lockstep and could silently miss one.
|
|
908
|
+
*/
|
|
909
|
+
const isGeminiModelId = (model) => model.startsWith("gemini");
|
|
910
|
+
/**
|
|
899
911
|
* Models that support the reasoning_effort parameter.
|
|
900
912
|
* o1-preview and o1-mini do NOT support reasoning_effort.
|
|
901
913
|
*/
|
|
@@ -1369,6 +1381,12 @@ z$1.object({
|
|
|
1369
1381
|
ownerId: z$1.string(),
|
|
1370
1382
|
ownerType: z$1.enum(CreditHolderType),
|
|
1371
1383
|
sessionId: z$1.string().optional(),
|
|
1384
|
+
/**
|
|
1385
|
+
* Data lake this call is 1:1 attributable to (ingestion embeds only - a query
|
|
1386
|
+
* embedding can span multiple lakes and is never attributed here). Unset for
|
|
1387
|
+
* every other feature/call.
|
|
1388
|
+
*/
|
|
1389
|
+
dataLakeId: z$1.string().optional(),
|
|
1372
1390
|
feature: z$1.enum([
|
|
1373
1391
|
"chat",
|
|
1374
1392
|
"image_generation",
|
|
@@ -2108,6 +2126,37 @@ z$1.object({
|
|
|
2108
2126
|
createdAt: true,
|
|
2109
2127
|
updatedAt: true
|
|
2110
2128
|
});
|
|
2129
|
+
z$1.object({
|
|
2130
|
+
id: z$1.string().optional(),
|
|
2131
|
+
/** UTC month, YYYY-MM. */
|
|
2132
|
+
month: z$1.string().regex(/^\d{4}-\d{2}$/, "month must be YYYY-MM"),
|
|
2133
|
+
/** Matches UsageEvent.provider values (e.g. "anthropic", "openai"). */
|
|
2134
|
+
provider: z$1.string().min(1),
|
|
2135
|
+
/** Provider-reported spend in USD for the month. */
|
|
2136
|
+
providerUsd: z$1.number().finite().nonnegative(),
|
|
2137
|
+
/** Our internal COGS estimate from UsageEvent aggregation. */
|
|
2138
|
+
internalUsd: z$1.number().finite().nonnegative(),
|
|
2139
|
+
/** providerUsd - internalUsd; positive = we underestimate. */
|
|
2140
|
+
deltaUsd: z$1.number().finite(),
|
|
2141
|
+
/** Absolute delta as a percentage of max(providerUsd, internalUsd) (0-100). */
|
|
2142
|
+
deltaPct: z$1.number().finite().nonnegative(),
|
|
2143
|
+
/** How the provider figure was obtained. */
|
|
2144
|
+
source: z$1.enum([
|
|
2145
|
+
"anthropic_admin_api",
|
|
2146
|
+
"openai_usage_api",
|
|
2147
|
+
"manual"
|
|
2148
|
+
]),
|
|
2149
|
+
/** Optional detail: per-key or per-model breakdown from the provider. */
|
|
2150
|
+
providerBreakdown: z$1.record(z$1.string(), z$1.number()).optional(),
|
|
2151
|
+
/** Human-readable note (error messages, partial data warnings, etc.). */
|
|
2152
|
+
note: z$1.string().optional(),
|
|
2153
|
+
createdAt: z$1.date(),
|
|
2154
|
+
updatedAt: z$1.date()
|
|
2155
|
+
}).omit({
|
|
2156
|
+
id: true,
|
|
2157
|
+
createdAt: true,
|
|
2158
|
+
updatedAt: true
|
|
2159
|
+
});
|
|
2111
2160
|
z$1.object({
|
|
2112
2161
|
id: z$1.string().optional(),
|
|
2113
2162
|
ownerId: z$1.string(),
|
|
@@ -2184,6 +2233,76 @@ function isPlaceholderApiKey(value) {
|
|
|
2184
2233
|
if (!normalized) return true;
|
|
2185
2234
|
return PLACEHOLDER_API_KEY_REGEX.test(normalized);
|
|
2186
2235
|
}
|
|
2236
|
+
z$1.object({
|
|
2237
|
+
/**
|
|
2238
|
+
* The granting lake's Mongo `_id`. ALWAYS a persisted DB lake: a hardcoded/fallback lake has no
|
|
2239
|
+
* backing document to hang a grant on (its id is a human slug, never an ObjectId), so the write
|
|
2240
|
+
* boundary refuses one via `assertLakeGrantable`. That is the explicit fallback carve-out issue
|
|
2241
|
+
* #1667 calls for - enforced at the service layer, where the static registry is known, not here.
|
|
2242
|
+
*/
|
|
2243
|
+
dataLakeId: z$1.string(),
|
|
2244
|
+
principalType: z$1.enum(["user", "organization"]),
|
|
2245
|
+
/** The granted principal's id - a userId or an organizationId, per `principalType`. */
|
|
2246
|
+
principalId: z$1.string(),
|
|
2247
|
+
role: z$1.enum([
|
|
2248
|
+
"owner",
|
|
2249
|
+
"curator",
|
|
2250
|
+
"reader"
|
|
2251
|
+
]),
|
|
2252
|
+
/** The actor (userId) who created the grant. */
|
|
2253
|
+
grantedByUserId: z$1.string(),
|
|
2254
|
+
/**
|
|
2255
|
+
* Optional expiry for a time-boxed grant (trials, temporary internal collaborators,
|
|
2256
|
+
* evaluations). A grant is expired once `expiresAt` is set and in the past. Expired rows are
|
|
2257
|
+
* filtered at READ time by grant resolution (#1673) and are deliberately NOT swept from the
|
|
2258
|
+
* collection, so an owner-facing membership view (#1672) and the audit trail (#1663) can still
|
|
2259
|
+
* render a lapsed grant. Absent/null = never expires.
|
|
2260
|
+
*
|
|
2261
|
+
* Only meaningful for a principal INSIDE the lake's organization: membership never crosses
|
|
2262
|
+
* organizations (epic decision 12), so this is not a cross-org mechanism.
|
|
2263
|
+
*/
|
|
2264
|
+
expiresAt: z$1.date().nullish()
|
|
2265
|
+
});
|
|
2266
|
+
/**
|
|
2267
|
+
* Every `IDataLake` field, classified as audited or not. A TOTAL map keyed by `keyof IDataLake`,
|
|
2268
|
+
* exactly like `LAKE_FIELD_VISIBILITY` in redactLakeForActor.ts and for the same reason: a list of
|
|
2269
|
+
* strings cannot notice an ABSENCE, so a new config field would simply never be audited and nobody
|
|
2270
|
+
* would find out. Keyed this way, a field added to the entity and classified nowhere is a COMPILE
|
|
2271
|
+
* error here, which is the only forcing function that actually works.
|
|
2272
|
+
*
|
|
2273
|
+
* `excluded` is for fields no operator chooses and which steer no answer: the content stats, the
|
|
2274
|
+
* teardown bookkeeping stamps, the cost meter and the lake-memory lease. They change constantly and
|
|
2275
|
+
* a history full of them would bury the changes that matter.
|
|
2276
|
+
*/
|
|
2277
|
+
const LAKE_CONFIG_FIELD_AUDIT = {
|
|
2278
|
+
name: "audited",
|
|
2279
|
+
slug: "audited",
|
|
2280
|
+
description: "audited",
|
|
2281
|
+
systemPrompt: "audited",
|
|
2282
|
+
preferredSystemPromptId: "audited",
|
|
2283
|
+
groundingMode: "audited",
|
|
2284
|
+
requiredPassageTokenTarget: "audited",
|
|
2285
|
+
fileTagPrefix: "audited",
|
|
2286
|
+
datalakeTag: "audited",
|
|
2287
|
+
requiredUserTag: "audited",
|
|
2288
|
+
requiredEntitlement: "audited",
|
|
2289
|
+
organizationId: "audited",
|
|
2290
|
+
isPublic: "audited",
|
|
2291
|
+
auditQueryTextEnabled: "audited",
|
|
2292
|
+
status: "audited",
|
|
2293
|
+
createdByUserId: "audited",
|
|
2294
|
+
lastUpdatedByUserId: "excluded",
|
|
2295
|
+
fileCount: "excluded",
|
|
2296
|
+
totalSizeBytes: "excluded",
|
|
2297
|
+
totalChunkedChars: "excluded",
|
|
2298
|
+
embeddingSpendMicroUsd: "excluded",
|
|
2299
|
+
lastSyncAt: "excluded",
|
|
2300
|
+
filesDeletedAt: "excluded",
|
|
2301
|
+
filesArchivedAt: "excluded",
|
|
2302
|
+
lakeMemoryExtractionAt: "excluded",
|
|
2303
|
+
lakeMemoryCursor: "excluded"
|
|
2304
|
+
};
|
|
2305
|
+
[...Object.keys(LAKE_CONFIG_FIELD_AUDIT).filter((field) => LAKE_CONFIG_FIELD_AUDIT[field] === "audited")];
|
|
2187
2306
|
/**
|
|
2188
2307
|
* SRE Agent Trio - Shared Types
|
|
2189
2308
|
*
|
|
@@ -2982,6 +3101,66 @@ z.enum([
|
|
|
2982
3101
|
"xai",
|
|
2983
3102
|
"gemini"
|
|
2984
3103
|
]);
|
|
3104
|
+
z$1.object({
|
|
3105
|
+
sessionId: z$1.string().nullish(),
|
|
3106
|
+
message: z$1.string(),
|
|
3107
|
+
organizationId: z$1.string().optional(),
|
|
3108
|
+
model: z$1.string().optional(),
|
|
3109
|
+
temperature: z$1.number().min(0).max(2).optional(),
|
|
3110
|
+
max_tokens: z$1.number().positive().optional(),
|
|
3111
|
+
maxTokens: z$1.number().positive().optional(),
|
|
3112
|
+
maxOutputTokens: z$1.number().positive().optional(),
|
|
3113
|
+
stream: z$1.boolean().prefault(false),
|
|
3114
|
+
historyCount: z$1.number().positive().default(10),
|
|
3115
|
+
fileIds: z$1.array(z$1.string()).prefault([]),
|
|
3116
|
+
wait: z$1.boolean().prefault(false),
|
|
3117
|
+
enableTools: z$1.boolean().prefault(false),
|
|
3118
|
+
toolMode: z$1.enum(["fast", "smart"]).optional(),
|
|
3119
|
+
tools: z$1.array(z$1.string()).optional(),
|
|
3120
|
+
enableQuestMaster: z$1.boolean().optional(),
|
|
3121
|
+
enableMementos: z$1.boolean().optional(),
|
|
3122
|
+
enableAgents: z$1.boolean().optional(),
|
|
3123
|
+
promptMode: z$1.enum([
|
|
3124
|
+
"raw",
|
|
3125
|
+
"grounded",
|
|
3126
|
+
"surface"
|
|
3127
|
+
]).optional(),
|
|
3128
|
+
includePromptDetails: z$1.boolean().optional(),
|
|
3129
|
+
includeSystemPrompt: z$1.boolean().optional()
|
|
3130
|
+
});
|
|
3131
|
+
z$1.object({
|
|
3132
|
+
id: z$1.string(),
|
|
3133
|
+
status: z$1.string(),
|
|
3134
|
+
message_received: z$1.boolean(),
|
|
3135
|
+
timestamp: z$1.string(),
|
|
3136
|
+
model: z$1.string(),
|
|
3137
|
+
message: z$1.string().optional(),
|
|
3138
|
+
tracking_info: z$1.object({
|
|
3139
|
+
quest_id: z$1.string(),
|
|
3140
|
+
check_status_url: z$1.string(),
|
|
3141
|
+
poll_url: z$1.string().optional()
|
|
3142
|
+
})
|
|
3143
|
+
});
|
|
3144
|
+
/**
|
|
3145
|
+
* Reusable JSON error envelope (plain; the OpenAPI layer annotates it).
|
|
3146
|
+
*
|
|
3147
|
+
* Must stay in sync with the published `ErrorResponse` component
|
|
3148
|
+
* (../openapi/schemas.ts) - `openapi/errorEnvelopeParity.test.ts` pins the two
|
|
3149
|
+
* together, and apps/client's errorHandler test uses this shape as the stand-in for
|
|
3150
|
+
* the component, which is generate-time only and cannot be imported at runtime.
|
|
3151
|
+
*/
|
|
3152
|
+
const ApiErrorSchema = z$1.object({
|
|
3153
|
+
error: z$1.string(),
|
|
3154
|
+
request_id: z$1.string().optional(),
|
|
3155
|
+
/**
|
|
3156
|
+
* Deprecated, sunset 2026-12-01. The `name` of whatever was thrown - our own error
|
|
3157
|
+
* classes usually, a library/driver class name on an unhandled 500 - added to every
|
|
3158
|
+
* body by apps/client's errorHandler. Documented here so the runtime and the spec
|
|
3159
|
+
* agree while it is still served; do not build on it. See CONVENTIONS.md section 1.
|
|
3160
|
+
*/
|
|
3161
|
+
name: z$1.string().optional()
|
|
3162
|
+
});
|
|
3163
|
+
ApiErrorSchema.extend({ errorCode: z$1.literal("insufficient_credits").optional() });
|
|
2985
3164
|
const supportedVoiceGenerationVendor = z.enum(["openai", "elevenlabs"]);
|
|
2986
3165
|
const voiceOutputFormatSchema = z.enum([
|
|
2987
3166
|
"mp3",
|
|
@@ -3010,6 +3189,49 @@ z.object({
|
|
|
3010
3189
|
languageCode: ttsLanguageCodeSchema.optional(),
|
|
3011
3190
|
preview: z.boolean().optional()
|
|
3012
3191
|
});
|
|
3192
|
+
/**
|
|
3193
|
+
* Why a browsable copy of generated audio was not kept. Saving is best-effort and
|
|
3194
|
+
* never fatal (the caller was already billed for the bytes it is being handed), so
|
|
3195
|
+
* this is reported alongside a successful response rather than as an error.
|
|
3196
|
+
*
|
|
3197
|
+
* Must stay in sync with `PersistGeneratedAudioResult` in
|
|
3198
|
+
* apps/client/server/utils/persistGeneratedAudio.ts, which derives its `reason`
|
|
3199
|
+
* from this schema.
|
|
3200
|
+
*/
|
|
3201
|
+
const audioSaveSkippedReasonSchema = z.enum([
|
|
3202
|
+
"storage_limit",
|
|
3203
|
+
"file_too_large",
|
|
3204
|
+
"error"
|
|
3205
|
+
]);
|
|
3206
|
+
z.object({
|
|
3207
|
+
/** Base64-encoded audio payload. */
|
|
3208
|
+
audio: z.string(),
|
|
3209
|
+
format: voiceOutputFormatSchema,
|
|
3210
|
+
contentType: z.string(),
|
|
3211
|
+
saved: z.boolean().optional(),
|
|
3212
|
+
fabFileId: z.string().optional(),
|
|
3213
|
+
fileUrl: z.string().optional(),
|
|
3214
|
+
saveSkippedReason: audioSaveSkippedReasonSchema.optional(),
|
|
3215
|
+
/** The provider that actually produced the audio, present only on a fallback. */
|
|
3216
|
+
provider: supportedVoiceGenerationVendor.optional(),
|
|
3217
|
+
/** The originally requested provider that could not serve the request. */
|
|
3218
|
+
fallbackFrom: supportedVoiceGenerationVendor.optional()
|
|
3219
|
+
});
|
|
3220
|
+
ApiErrorSchema.extend({
|
|
3221
|
+
provider: supportedVoiceGenerationVendor.optional(),
|
|
3222
|
+
errorCode: z.enum([
|
|
3223
|
+
"insufficient_credits",
|
|
3224
|
+
"provider_not_configured",
|
|
3225
|
+
"provider_rejected"
|
|
3226
|
+
]).optional()
|
|
3227
|
+
});
|
|
3228
|
+
z.object({
|
|
3229
|
+
error: z.string(),
|
|
3230
|
+
provider: supportedVoiceGenerationVendor,
|
|
3231
|
+
saved: z.literal(true).optional(),
|
|
3232
|
+
fabFileId: z.string().optional(),
|
|
3233
|
+
fileUrl: z.string().optional()
|
|
3234
|
+
});
|
|
3013
3235
|
z.enum(["openai"]);
|
|
3014
3236
|
/**
|
|
3015
3237
|
* Supported sound-effects generation vendors. Currently only ElevenLabs.
|
|
@@ -3939,6 +4161,7 @@ const HearthEventAction = z$1.object({
|
|
|
3939
4161
|
seq: z$1.number(),
|
|
3940
4162
|
actorId: z$1.string(),
|
|
3941
4163
|
actorName: z$1.string().optional(),
|
|
4164
|
+
actorKind: actorKindSchema.optional(),
|
|
3942
4165
|
kind: hearthEventKindSchema,
|
|
3943
4166
|
human: z$1.object({
|
|
3944
4167
|
text: z$1.string(),
|
|
@@ -4516,50 +4739,6 @@ z$1.discriminatedUnion("action", [
|
|
|
4516
4739
|
PermissionRequestAction,
|
|
4517
4740
|
ReconnectResultAction
|
|
4518
4741
|
]);
|
|
4519
|
-
z$1.object({
|
|
4520
|
-
sessionId: z$1.string().nullish(),
|
|
4521
|
-
message: z$1.string(),
|
|
4522
|
-
model: z$1.string().optional(),
|
|
4523
|
-
temperature: z$1.number().min(0).max(2).optional(),
|
|
4524
|
-
max_tokens: z$1.number().positive().optional(),
|
|
4525
|
-
maxTokens: z$1.number().positive().optional(),
|
|
4526
|
-
maxOutputTokens: z$1.number().positive().optional(),
|
|
4527
|
-
stream: z$1.boolean().prefault(false),
|
|
4528
|
-
historyCount: z$1.number().positive().default(10),
|
|
4529
|
-
fileIds: z$1.array(z$1.string()).prefault([]),
|
|
4530
|
-
wait: z$1.boolean().prefault(false),
|
|
4531
|
-
enableTools: z$1.boolean().prefault(false),
|
|
4532
|
-
toolMode: z$1.enum(["fast", "smart"]).optional(),
|
|
4533
|
-
tools: z$1.array(z$1.string()).optional(),
|
|
4534
|
-
enableQuestMaster: z$1.boolean().optional(),
|
|
4535
|
-
enableMementos: z$1.boolean().optional(),
|
|
4536
|
-
enableAgents: z$1.boolean().optional(),
|
|
4537
|
-
promptMode: z$1.enum([
|
|
4538
|
-
"raw",
|
|
4539
|
-
"grounded",
|
|
4540
|
-
"surface"
|
|
4541
|
-
]).optional(),
|
|
4542
|
-
includePromptDetails: z$1.boolean().optional(),
|
|
4543
|
-
includeSystemPrompt: z$1.boolean().optional()
|
|
4544
|
-
});
|
|
4545
|
-
z$1.object({
|
|
4546
|
-
id: z$1.string(),
|
|
4547
|
-
status: z$1.string(),
|
|
4548
|
-
message_received: z$1.boolean(),
|
|
4549
|
-
timestamp: z$1.string(),
|
|
4550
|
-
model: z$1.string(),
|
|
4551
|
-
message: z$1.string().optional(),
|
|
4552
|
-
tracking_info: z$1.object({
|
|
4553
|
-
quest_id: z$1.string(),
|
|
4554
|
-
check_status_url: z$1.string(),
|
|
4555
|
-
poll_url: z$1.string().optional()
|
|
4556
|
-
})
|
|
4557
|
-
});
|
|
4558
|
-
/** Reusable JSON error envelope (plain; the OpenAPI layer annotates it). */
|
|
4559
|
-
const ApiErrorSchema = z$1.object({
|
|
4560
|
-
error: z$1.string(),
|
|
4561
|
-
request_id: z$1.string().optional()
|
|
4562
|
-
});
|
|
4563
4742
|
/**
|
|
4564
4743
|
* Tool schema matching ICompletionOptionTools.toolSchema. The Zod surface only
|
|
4565
4744
|
* covers wire-format fields (toolFn is server-side). Replaces the historical
|
|
@@ -4653,6 +4832,7 @@ const CompletionContentEventSchema = z$1.object({
|
|
|
4653
4832
|
"tool_use",
|
|
4654
4833
|
"best-effort"
|
|
4655
4834
|
]).optional(),
|
|
4835
|
+
stopReason: z$1.string().optional(),
|
|
4656
4836
|
thinking: z$1.array(z$1.any()).optional()
|
|
4657
4837
|
});
|
|
4658
4838
|
const CompletionSseErrorEventSchema = z$1.object({
|
|
@@ -5231,7 +5411,7 @@ const BFL_SAFETY_TOLERANCE = {
|
|
|
5231
5411
|
*/
|
|
5232
5412
|
const BFLSafetyToleranceSchema = z$1.number().min(BFL_SAFETY_TOLERANCE.MIN).max(BFL_SAFETY_TOLERANCE.LEGACY_INPUT_MAX).optional().prefault(BFL_SAFETY_TOLERANCE.DEFAULT).transform((value) => Math.min(value, BFL_SAFETY_TOLERANCE.MAX));
|
|
5233
5413
|
/**
|
|
5234
|
-
* List of image models supported by
|
|
5414
|
+
* List of image models supported by Black Forest Labs
|
|
5235
5415
|
*/
|
|
5236
5416
|
const BFL_IMAGE_MODELS = [
|
|
5237
5417
|
"flux-pro-1.1",
|
|
@@ -5532,6 +5712,32 @@ z$1.enum([
|
|
|
5532
5712
|
"complex"
|
|
5533
5713
|
]);
|
|
5534
5714
|
z$1.preprocess((v) => Array.isArray(v) ? v.at(-1) : v, z$1.union([z$1.boolean(), z$1.string()])).prefault(false).transform((v) => ["true", "1"].includes(String(v).toLowerCase())).catch(false);
|
|
5715
|
+
const SessionTagSchema = z$1.object({
|
|
5716
|
+
name: z$1.string(),
|
|
5717
|
+
strength: z$1.number()
|
|
5718
|
+
});
|
|
5719
|
+
z$1.object({
|
|
5720
|
+
name: z$1.string().min(1).optional(),
|
|
5721
|
+
knowledgeIds: z$1.array(z$1.string()).optional(),
|
|
5722
|
+
artifactIds: z$1.array(z$1.string()).optional(),
|
|
5723
|
+
tags: z$1.array(SessionTagSchema).optional(),
|
|
5724
|
+
lastUsedModel: z$1.string().min(1).nullish().describe("Pin a specific model id, or omit/send null to leave the current pin unchanged. Sending null does NOT clear it."),
|
|
5725
|
+
forceKnowledgeRetrieval: z$1.boolean().optional(),
|
|
5726
|
+
propagateToProjects: z$1.boolean().optional().describe("Defaults to true when omitted. When knowledgeIds grows, the newly-added file ids are also appended to every project that contains this session, granting every member of that project access to those files. This propagation is append-only and cannot be undone through the UI - pass false if newly-attached files should not be shared with the project.")
|
|
5727
|
+
});
|
|
5728
|
+
z$1.object({ id: z$1.string().min(1) });
|
|
5729
|
+
z$1.object({
|
|
5730
|
+
id: z$1.string(),
|
|
5731
|
+
name: z$1.string(),
|
|
5732
|
+
userId: z$1.string(),
|
|
5733
|
+
knowledgeIds: z$1.array(z$1.string()).optional(),
|
|
5734
|
+
artifactIds: z$1.array(z$1.string()).optional(),
|
|
5735
|
+
tags: z$1.array(SessionTagSchema).optional(),
|
|
5736
|
+
forceKnowledgeRetrieval: z$1.boolean().optional(),
|
|
5737
|
+
lastUsedModel: z$1.string().nullish(),
|
|
5738
|
+
firstCreated: z$1.date(),
|
|
5739
|
+
lastUpdated: z$1.date()
|
|
5740
|
+
});
|
|
5535
5741
|
z$2.enum([
|
|
5536
5742
|
"",
|
|
5537
5743
|
"TFG",
|
|
@@ -5598,6 +5804,130 @@ z$2.object({
|
|
|
5598
5804
|
lastActiveAt: z$2.date().optional(),
|
|
5599
5805
|
isOnline: z$2.boolean()
|
|
5600
5806
|
});
|
|
5807
|
+
/**
|
|
5808
|
+
* A chunk larger than this (in tokens) marks a file whose chunking predates the passage-target
|
|
5809
|
+
* fix: a whole-document / whole-section blob rather than a ~512-token passage. Used to detect the
|
|
5810
|
+
* files a lake "Rebuild passages" pass should re-chunk. Deliberately well above
|
|
5811
|
+
* DEFAULT_PASSAGE_TOKEN_TARGET (512) so a correctly-chunked passage never trips it, and below the
|
|
5812
|
+
* ~6.5K model-window packing the old chunker produced, so every legacy blob does.
|
|
5813
|
+
*/
|
|
5814
|
+
const OVERSIZED_PASSAGE_TOKEN_THRESHOLD = 1500;
|
|
5815
|
+
/**
|
|
5816
|
+
* `FabFile.notes` marker written when the data-lake convergence kill switch abandons a vectorize
|
|
5817
|
+
* (#1676). The file keeps its chunks but has no vectors, so it is unsearchable until re-indexed, and
|
|
5818
|
+
* it does NOT auto-resume.
|
|
5819
|
+
*
|
|
5820
|
+
* Lives here rather than beside its writer (apps/client fabFileVectorize) because it is a
|
|
5821
|
+
* cross-layer contract: the queue handler writes it and the lake-health evaluator
|
|
5822
|
+
* (constants/lakeHealth.ts) reads it to tell a permanently-stalled file from one still in flight.
|
|
5823
|
+
* b4m-core cannot import from apps/client, so a copy there would have to drift silently.
|
|
5824
|
+
*/
|
|
5825
|
+
const CONVERGENCE_PAUSED_NOTE = "Indexing paused by the data-lake convergence kill switch - reprocess to complete.";
|
|
5826
|
+
/**
|
|
5827
|
+
* `FabFile.notes` marker for the OTHER half of the same kill switch: a re-chunk dropped before it
|
|
5828
|
+
* ran (#1676/#1681). Distinct from `CONVERGENCE_PAUSED_NOTE` because the damage is worse and the
|
|
5829
|
+
* wording has to say so - the producer resets a wave's chunk state BEFORE the messages are handled,
|
|
5830
|
+
* so a file halted here has NO chunks at all rather than chunks without vectors.
|
|
5831
|
+
*
|
|
5832
|
+
* Without a marker this state is invisible to every surface at once, which is the failure it exists
|
|
5833
|
+
* to prevent: `chunkCount: 0` with `error: null` reads as an image or a pending upload, so health
|
|
5834
|
+
* drops it from the denominator, convergence grades it `conformant` (its stale stamp still matches),
|
|
5835
|
+
* search does not withhold it because it is not "in flight", and the rescue sweep's own filter
|
|
5836
|
+
* passes over it. The file's passages are simply gone and nothing reports it.
|
|
5837
|
+
*
|
|
5838
|
+
* Same cross-layer reason as the constant above for living here: the queue handler writes it and
|
|
5839
|
+
* b4m-core's evaluators read it, and b4m-core cannot import from apps/client.
|
|
5840
|
+
*/
|
|
5841
|
+
const CONVERGENCE_PAUSED_CHUNK_NOTE = "Re-chunking paused by the data-lake convergence kill switch - its passages were removed and are rebuilt when convergence resumes.";
|
|
5842
|
+
/**
|
|
5843
|
+
* `FabFile.chunkRebuildRequestedAt`: stamped by `resetChunkStateByIds` in the SAME write that
|
|
5844
|
+
* clears a file's chunk rollups, so "this file's passages are being rebuilt" can never be lost the
|
|
5845
|
+
* way the pair of steps that creates the state can be. The reset and the queue send are two
|
|
5846
|
+
* operations - kill the producer between them, or lose the consumer's marker write, and the file
|
|
5847
|
+
* sits at `chunkCount: 0` with `error: null` and `notes: ''`, a shape indistinguishable from an
|
|
5848
|
+
* image or a still-uploading row. It then drops out of lake health's denominator, out of the
|
|
5849
|
+
* convergence plan and out of the retrieval withhold at the same moment: every rollup says its
|
|
5850
|
+
* passages are gone, and nothing reports it.
|
|
5851
|
+
*
|
|
5852
|
+
* Deliberately NOT `CONVERGENCE_PAUSED_CHUNK_NOTE` pre-written by the producer, which is the obvious
|
|
5853
|
+
* fix and the wrong one: that marker means "halted, needs an administrator", so a file awaiting an
|
|
5854
|
+
* ORDINARY rebuild would read to every reader as permanently paused for the whole rebuild - search
|
|
5855
|
+
* would tell readers it does not return on its own, health would hard-fail P3, and "Rebuild
|
|
5856
|
+
* passages" would offer to repair a file that is already repairing. A flag that cries wolf on the
|
|
5857
|
+
* normal path is worse than the rare window it closes.
|
|
5858
|
+
*
|
|
5859
|
+
* So the two facts are distinct states, and the consumer UPGRADES one to the other: pending means
|
|
5860
|
+
* "in flight, returns on its own", the paused note means "halted, needs intervention". A LOST
|
|
5861
|
+
* upgrade therefore degrades to mislabelled-but-visible rather than invisible, which is the trade
|
|
5862
|
+
* this field exists to make - invisibility is the real harm, labelling is secondary.
|
|
5863
|
+
*
|
|
5864
|
+
* A dedicated field rather than a third `notes` string on purpose: `notes` already carries two
|
|
5865
|
+
* unrelated facts (the user's own note / NO_EXTRACTABLE_TEXT, and the kill-switch markers), so every
|
|
5866
|
+
* writer of it clobbers the others.
|
|
5867
|
+
*
|
|
5868
|
+
* Cleared by `commitFabFileChunks` (the rebuild landed) and by the chunk handler's pause write (the
|
|
5869
|
+
* rebuild was halted instead). A file carrying `error` is settled regardless - see
|
|
5870
|
+
* `isMemberIndexingInFlight`, which is where the precedence between these three lives.
|
|
5871
|
+
*/
|
|
5872
|
+
function isChunkRebuildPending(requestedAt) {
|
|
5873
|
+
return requestedAt !== null && requestedAt !== void 0 && requestedAt !== "";
|
|
5874
|
+
}
|
|
5875
|
+
/**
|
|
5876
|
+
* Whether a file's `notes` marks it as stalled by the convergence kill switch, by either arm.
|
|
5877
|
+
* THE predicate every reader uses, so adding a third stall marker reaches health, convergence and
|
|
5878
|
+
* retrieval without three separate string comparisons drifting apart.
|
|
5879
|
+
*/
|
|
5880
|
+
function isConvergencePausedNote(notes) {
|
|
5881
|
+
return CONVERGENCE_PAUSED_NOTES.includes(notes);
|
|
5882
|
+
}
|
|
5883
|
+
/**
|
|
5884
|
+
* Datastore mirror of `isConvergencePausedNote`, for a Mongo `notes: { $in: [...] }`. Exported so a
|
|
5885
|
+
* query and the in-memory predicate cannot drift: adding a third stall marker to this array reaches
|
|
5886
|
+
* both. Declared after the two constants it names so the function above can close over it.
|
|
5887
|
+
*/
|
|
5888
|
+
const CONVERGENCE_PAUSED_NOTES = [CONVERGENCE_PAUSED_NOTE, CONVERGENCE_PAUSED_CHUNK_NOTE];
|
|
5889
|
+
/** Ceiling so "adjustable" cannot mean "unbounded" in either direction. */
|
|
5890
|
+
const LAKE_ACCESS_AUDIT_RETENTION_MAX_DAYS = 2555;
|
|
5891
|
+
/**
|
|
5892
|
+
* Lake CONFIG-change audit retention and value caps.
|
|
5893
|
+
*
|
|
5894
|
+
* Twin of `constants/lakeAccessAudit.ts` and here for the same reason: the values are needed by
|
|
5895
|
+
* both the admin-settings schema in this package and by the repository that applies them
|
|
5896
|
+
* (`packages/database`, which cannot import an app-server layer).
|
|
5897
|
+
*
|
|
5898
|
+
* DELIBERATELY NOT the same numbers as the read audit, and deliberately not the same lever. A
|
|
5899
|
+
* retrieval is frequent, low-value and cheap to lose; a config change is rare, high-value and
|
|
5900
|
+
* alters every future answer the lake gives, so the two want opposite retention. Folding config
|
|
5901
|
+
* changes into the read collection would force one of them onto the other's clock - either a
|
|
5902
|
+
* config change expiring on a read-volume schedule, or a high-volume collection's storage
|
|
5903
|
+
* multiplied to serve a low-volume need.
|
|
5904
|
+
*
|
|
5905
|
+
* Same one-way ratchet as the read side (#1658's levers rule): `resolveLakeConfigAuditRetentionDays`
|
|
5906
|
+
* can only raise a configured value to the floor, never lower it, so an org cannot configure its
|
|
5907
|
+
* own retention down to nothing and defeat the control. Platform-level, not per-org: the setting
|
|
5908
|
+
* carries no `scope.settableAt`, so #1660's resolver returns the platform value at every scope.
|
|
5909
|
+
*/
|
|
5910
|
+
/** 3 years: long enough to outlive a typical contract and audit cycle, on a collection whose
|
|
5911
|
+
* volume is a few rows per lake per year. Longer than the read audit's 450 days on purpose - the
|
|
5912
|
+
* rarer and more consequential the event, the longer it is worth keeping. */
|
|
5913
|
+
const LAKE_CONFIG_AUDIT_RETENTION_FLOOR_DAYS = 1095;
|
|
5914
|
+
/** A separate constant from the floor on purpose, even though they agree today - a future change
|
|
5915
|
+
* to one must not silently move the other. */
|
|
5916
|
+
const LAKE_CONFIG_AUDIT_RETENTION_DEFAULT_DAYS = 1095;
|
|
5917
|
+
/** Ceiling so "adjustable" cannot mean "unbounded" in either direction. */
|
|
5918
|
+
const LAKE_CONFIG_AUDIT_RETENTION_MAX_DAYS = 3650;
|
|
5919
|
+
/**
|
|
5920
|
+
* Forced-retrieval budget defaults, shared between the admin-settings schema in this package and
|
|
5921
|
+
* `ChatCompletionFeatures.ts` (which cannot import from `common`'s settings schema without a
|
|
5922
|
+
* dependency cycle, so the constant lives here instead).
|
|
5923
|
+
*
|
|
5924
|
+
* Only `FORCED_RETRIEVAL_CHAR_BUDGET_DEFAULT` is a lever (see the `forcedRetrievalCharBudget`
|
|
5925
|
+
* setting) - the char budget is the measured binding constraint on how much of a corpus reaches
|
|
5926
|
+
* the model on every Data-Lake-mode turn. The relevance floor is exported alongside it so the two
|
|
5927
|
+
* stay next to each other, not because it is tunable today.
|
|
5928
|
+
*/
|
|
5929
|
+
/** Total characters of retrieved chunk text injected into a forced-retrieval prompt. */
|
|
5930
|
+
const FORCED_RETRIEVAL_CHAR_BUDGET_DEFAULT = 12e3;
|
|
5601
5931
|
let OpenAIEmbeddingModel = /* @__PURE__ */ function(OpenAIEmbeddingModel) {
|
|
5602
5932
|
OpenAIEmbeddingModel["TEXT_EMBEDDING_3_SMALL"] = "text-embedding-3-small";
|
|
5603
5933
|
OpenAIEmbeddingModel["TEXT_EMBEDDING_3_LARGE"] = "text-embedding-3-large";
|
|
@@ -5742,11 +6072,15 @@ const HELP_CENTER_PROMPT = `HELP CENTER: Bike4Mind has a built-in Help Center th
|
|
|
5742
6072
|
*
|
|
5743
6073
|
* Counterweight to the completeness pressure the rest of the system prompt applies: without an
|
|
5744
6074
|
* explicit licence to abstain, the model treats "answer fully" as unconditional and fills gaps with
|
|
5745
|
-
* invented specifics
|
|
5746
|
-
*
|
|
5747
|
-
*
|
|
6075
|
+
* invented specifics - including a named customer, competitor, deal or dollar figure a leading
|
|
6076
|
+
* question implied but no source supports, volunteered with citation-like framing so it reads as
|
|
6077
|
+
* sourced. In internal evaluation (a harness kept outside this repo) this was among the largest
|
|
6078
|
+
* quality gains on questions whose correct answer is a refusal, so it ships on every completion
|
|
6079
|
+
* rather than only on the grounded surfaces (it is also the only surface covering a turn that
|
|
6080
|
+
* answers WITHOUT searching the knowledge base). Kept short
|
|
6081
|
+
* on purpose - it must be cheap and behaviorally light.
|
|
5748
6082
|
*/
|
|
5749
|
-
const ABSTENTION_PROMPT = `When a request is underspecified or your sources do not cover it, say so and name what is missing. "I do not have enough to answer that" is a correct, high-value answer. Never invent facts about the user, their business, or their data.`;
|
|
6083
|
+
const ABSTENTION_PROMPT = `When a request is underspecified or your sources do not cover it, say so and name what is missing. "I do not have enough to answer that" is a correct, high-value answer. Never invent facts about the user, their business, or their data, and never state a specific customer, competitor, deal, or figure as fact - or cite a source for it - unless your sources support it, even when the question assumes it.`;
|
|
5750
6084
|
/**
|
|
5751
6085
|
* Default text for the formatting system message. Runtime fallback used by
|
|
5752
6086
|
* `includeHardcodedSystemMessage` (b4m-core/utils/src/llm/utils.ts) when the `FormatPromptTemplate`
|
|
@@ -5800,8 +6134,14 @@ z$1.enum([
|
|
|
5800
6134
|
"EnableDataLakes",
|
|
5801
6135
|
"EnableDataLakesDefault",
|
|
5802
6136
|
"EnableDataLakeSlackAdd",
|
|
6137
|
+
"EnableDataLakeGroundingMode",
|
|
5803
6138
|
"EnableLakeMemory",
|
|
5804
6139
|
"EnableDataLakeVectorSearch",
|
|
6140
|
+
"PauseLakeConvergence",
|
|
6141
|
+
"LakeConvergenceBulkChangeSharePct",
|
|
6142
|
+
"EnforceLakeReadGrants",
|
|
6143
|
+
"EnableDataLakeDrivePoll",
|
|
6144
|
+
"EnforceLakeAdmission",
|
|
5805
6145
|
"EnableBriefcase",
|
|
5806
6146
|
"EnableBriefcaseDefault",
|
|
5807
6147
|
"EnableImageTemplates",
|
|
@@ -5827,6 +6167,7 @@ z$1.enum([
|
|
|
5827
6167
|
"ReferralCreditsAmount",
|
|
5828
6168
|
"registrationLink",
|
|
5829
6169
|
"FeedbackReceiveEmail",
|
|
6170
|
+
"FeedbackReceiveEmailNonProd",
|
|
5830
6171
|
"FeedbackKyle",
|
|
5831
6172
|
"EnableFeedBackToEmail",
|
|
5832
6173
|
"EnableFeedBackToSlack",
|
|
@@ -5892,6 +6233,22 @@ z$1.enum([
|
|
|
5892
6233
|
"defaultEmbeddingModel",
|
|
5893
6234
|
"dataLakeSearchMaxFiles",
|
|
5894
6235
|
"dataLakeSearchMaxChunks",
|
|
6236
|
+
"forcedRetrievalCharBudget",
|
|
6237
|
+
"kbSearchDefaultResults",
|
|
6238
|
+
"kbSearchResultTokenBudget",
|
|
6239
|
+
"kbSearchMinRelevancePct",
|
|
6240
|
+
"dataLakeEmbeddingSpendEnabled",
|
|
6241
|
+
"dataLakeEmbeddingBudgetPerRunUsd",
|
|
6242
|
+
"dataLakeEmbeddingBudgetPerLakeUsd",
|
|
6243
|
+
"dataLakeEmbeddingBudgetPerPeriodUsd",
|
|
6244
|
+
"dataLakeEmbeddingBudgetPeriodHours",
|
|
6245
|
+
"dataLakeEmbeddingMaxCallsPerMinute",
|
|
6246
|
+
"dataLakeVectorizeChunkBatchSize",
|
|
6247
|
+
"dataLakeEmbeddingTierMultiplierIndividual",
|
|
6248
|
+
"dataLakeEmbeddingTierMultiplierOrganization",
|
|
6249
|
+
"LakeAccessAuditRetentionDays",
|
|
6250
|
+
"LakeAccessQueryTextRetentionDays",
|
|
6251
|
+
"LakeConfigAuditRetentionDays",
|
|
5895
6252
|
"MaxContentLength",
|
|
5896
6253
|
"enableAutoChunk",
|
|
5897
6254
|
"SlackDefaultWebhookUrl",
|
|
@@ -5899,6 +6256,7 @@ z$1.enum([
|
|
|
5899
6256
|
"SlackLiveopsWebhookUrl",
|
|
5900
6257
|
"SlackUserActivityWebhookUrl",
|
|
5901
6258
|
"SlackFeedbackWebhookUrl",
|
|
6259
|
+
"SlackNonProdFeedbackWebhookUrl",
|
|
5902
6260
|
"SlackEmailAuditWebhookUrl",
|
|
5903
6261
|
"slackSigningSecret",
|
|
5904
6262
|
"slackBotToken",
|
|
@@ -5960,7 +6318,11 @@ z$1.enum([
|
|
|
5960
6318
|
"modelDiscoveryAutoEnable",
|
|
5961
6319
|
"modelDiscoveryAllowEgress",
|
|
5962
6320
|
"modelDiscoveryPriceBandPct",
|
|
5963
|
-
"modelDiscoveryAutoRemap"
|
|
6321
|
+
"modelDiscoveryAutoRemap",
|
|
6322
|
+
"prReportRepo",
|
|
6323
|
+
"prReportIdentityMap",
|
|
6324
|
+
"prReportWebhookUrl",
|
|
6325
|
+
"prReportEgressAllowlist"
|
|
5964
6326
|
]);
|
|
5965
6327
|
/**
|
|
5966
6328
|
* Intent-classifier sub-config. Drives the LLM-based silent
|
|
@@ -6077,6 +6439,9 @@ function makeStringSetting(config) {
|
|
|
6077
6439
|
*/
|
|
6078
6440
|
const DATA_LAKE_SEARCH_MAX_FILES_DEFAULT = 5e3;
|
|
6079
6441
|
const DATA_LAKE_SEARCH_MAX_CHUNKS_DEFAULT = 1e5;
|
|
6442
|
+
const DATA_LAKE_EMBEDDING_BUDGET_PER_LAKE_USD_MAX = 1e4;
|
|
6443
|
+
const DATA_LAKE_EMBEDDING_BUDGET_PER_PERIOD_USD_MAX = 5e3;
|
|
6444
|
+
const DATA_LAKE_EMBEDDING_MAX_CALLS_PER_MINUTE_MAX = 1e4;
|
|
6080
6445
|
function makeNumberSetting(config) {
|
|
6081
6446
|
let numberSchema = z$1.coerce.number();
|
|
6082
6447
|
if (config.min !== void 0) numberSchema = numberSchema.min(config.min);
|
|
@@ -6665,6 +7030,66 @@ const API_SERVICE_GROUPS = {
|
|
|
6665
7030
|
{
|
|
6666
7031
|
key: "dataLakeSearchMaxChunks",
|
|
6667
7032
|
order: 3
|
|
7033
|
+
},
|
|
7034
|
+
{
|
|
7035
|
+
key: "forcedRetrievalCharBudget",
|
|
7036
|
+
order: 4
|
|
7037
|
+
},
|
|
7038
|
+
{
|
|
7039
|
+
key: "kbSearchDefaultResults",
|
|
7040
|
+
order: 5
|
|
7041
|
+
},
|
|
7042
|
+
{
|
|
7043
|
+
key: "kbSearchResultTokenBudget",
|
|
7044
|
+
order: 6
|
|
7045
|
+
},
|
|
7046
|
+
{
|
|
7047
|
+
key: "kbSearchMinRelevancePct",
|
|
7048
|
+
order: 7
|
|
7049
|
+
}
|
|
7050
|
+
]
|
|
7051
|
+
},
|
|
7052
|
+
DATA_LAKE_COST: {
|
|
7053
|
+
id: "dataLakeCostGovernance",
|
|
7054
|
+
name: "Data Lake Cost Governance",
|
|
7055
|
+
description: "Spend levers for data-lake embedding work (ingestion, reprocessing, convergence). Budgets are USD; 0 means stop spending, not \"use the default\". The two tier multipliers scale the per-run and per-lake budgets by whether a lake is individual- or organization-owned.",
|
|
7056
|
+
icon: "Savings",
|
|
7057
|
+
settings: [
|
|
7058
|
+
{
|
|
7059
|
+
key: "dataLakeEmbeddingSpendEnabled",
|
|
7060
|
+
order: 1
|
|
7061
|
+
},
|
|
7062
|
+
{
|
|
7063
|
+
key: "dataLakeEmbeddingBudgetPerRunUsd",
|
|
7064
|
+
order: 2
|
|
7065
|
+
},
|
|
7066
|
+
{
|
|
7067
|
+
key: "dataLakeEmbeddingBudgetPerLakeUsd",
|
|
7068
|
+
order: 3
|
|
7069
|
+
},
|
|
7070
|
+
{
|
|
7071
|
+
key: "dataLakeEmbeddingBudgetPerPeriodUsd",
|
|
7072
|
+
order: 4
|
|
7073
|
+
},
|
|
7074
|
+
{
|
|
7075
|
+
key: "dataLakeEmbeddingBudgetPeriodHours",
|
|
7076
|
+
order: 5
|
|
7077
|
+
},
|
|
7078
|
+
{
|
|
7079
|
+
key: "dataLakeEmbeddingMaxCallsPerMinute",
|
|
7080
|
+
order: 6
|
|
7081
|
+
},
|
|
7082
|
+
{
|
|
7083
|
+
key: "dataLakeVectorizeChunkBatchSize",
|
|
7084
|
+
order: 7
|
|
7085
|
+
},
|
|
7086
|
+
{
|
|
7087
|
+
key: "dataLakeEmbeddingTierMultiplierIndividual",
|
|
7088
|
+
order: 8
|
|
7089
|
+
},
|
|
7090
|
+
{
|
|
7091
|
+
key: "dataLakeEmbeddingTierMultiplierOrganization",
|
|
7092
|
+
order: 9
|
|
6668
7093
|
}
|
|
6669
7094
|
]
|
|
6670
7095
|
},
|
|
@@ -6913,6 +7338,14 @@ const API_SERVICE_GROUPS = {
|
|
|
6913
7338
|
key: "SlackEmailAuditWebhookUrl",
|
|
6914
7339
|
order: 6.5
|
|
6915
7340
|
},
|
|
7341
|
+
{
|
|
7342
|
+
key: "SlackFeedbackWebhookUrl",
|
|
7343
|
+
order: 6.75
|
|
7344
|
+
},
|
|
7345
|
+
{
|
|
7346
|
+
key: "SlackNonProdFeedbackWebhookUrl",
|
|
7347
|
+
order: 6.8
|
|
7348
|
+
},
|
|
6916
7349
|
{
|
|
6917
7350
|
key: "FeedbackSendEmailUsername",
|
|
6918
7351
|
order: 7
|
|
@@ -6925,6 +7358,10 @@ const API_SERVICE_GROUPS = {
|
|
|
6925
7358
|
key: "FeedbackReceiveEmail",
|
|
6926
7359
|
order: 9
|
|
6927
7360
|
},
|
|
7361
|
+
{
|
|
7362
|
+
key: "FeedbackReceiveEmailNonProd",
|
|
7363
|
+
order: 9.5
|
|
7364
|
+
},
|
|
6928
7365
|
{
|
|
6929
7366
|
key: "liveFeedbackEmail",
|
|
6930
7367
|
order: 10
|
|
@@ -7437,6 +7874,26 @@ const API_SERVICE_GROUPS = {
|
|
|
7437
7874
|
order: 3
|
|
7438
7875
|
}
|
|
7439
7876
|
]
|
|
7877
|
+
},
|
|
7878
|
+
DATA_LAKE_AUDIT: {
|
|
7879
|
+
id: "dataLakeAuditService",
|
|
7880
|
+
name: "Data Lake Audit",
|
|
7881
|
+
description: "Retention for the lake audit trail: who READ a lake (plus the opt-in query-text log) and who CHANGED its configuration",
|
|
7882
|
+
icon: "Security",
|
|
7883
|
+
settings: [
|
|
7884
|
+
{
|
|
7885
|
+
key: "LakeAccessAuditRetentionDays",
|
|
7886
|
+
order: 1
|
|
7887
|
+
},
|
|
7888
|
+
{
|
|
7889
|
+
key: "LakeAccessQueryTextRetentionDays",
|
|
7890
|
+
order: 2
|
|
7891
|
+
},
|
|
7892
|
+
{
|
|
7893
|
+
key: "LakeConfigAuditRetentionDays",
|
|
7894
|
+
order: 3
|
|
7895
|
+
}
|
|
7896
|
+
]
|
|
7440
7897
|
}
|
|
7441
7898
|
};
|
|
7442
7899
|
const settingsMap = {
|
|
@@ -7541,13 +7998,23 @@ const settingsMap = {
|
|
|
7541
7998
|
EnableDataLakeSlackAdd: makeBooleanSetting({
|
|
7542
7999
|
key: "EnableDataLakeSlackAdd",
|
|
7543
8000
|
name: "Data Lakes: Slack \"@datalake add\" path",
|
|
7544
|
-
defaultValue:
|
|
7545
|
-
description: "Server-side gate for adding content to a Data Lake from Slack via \"@datalake add\".
|
|
8001
|
+
defaultValue: true,
|
|
8002
|
+
description: "Server-side gate for adding content to a Data Lake from Slack via \"@datalake add\". On by default. Turn OFF to make the Slack command inert - it is still intercepted deterministically, so the bot stays silent rather than falling through to the LLM.",
|
|
7546
8003
|
category: "Experimental",
|
|
7547
8004
|
group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
|
|
7548
8005
|
order: 90,
|
|
7549
8006
|
dependsOn: "EnableDataLakes"
|
|
7550
8007
|
}),
|
|
8008
|
+
EnableDataLakeGroundingMode: makeBooleanSetting({
|
|
8009
|
+
key: "EnableDataLakeGroundingMode",
|
|
8010
|
+
name: "Data Lakes: Per-lake grounding mode",
|
|
8011
|
+
defaultValue: true,
|
|
8012
|
+
description: "Global rollback lever for the per-lake grounding mode (inline vs retrieve vs auto-by-size). On by default. Turn OFF to ignore every lake's configured mode and fall back to pure size-only corpus deferral (CorpusRetrievalMinInlineTokensPerDoc), reverting the retrieve-by-default behavior for all lakes at once without editing each lake.",
|
|
8013
|
+
category: "Experimental",
|
|
8014
|
+
group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
|
|
8015
|
+
order: 92,
|
|
8016
|
+
dependsOn: "EnableDataLakes"
|
|
8017
|
+
}),
|
|
7551
8018
|
EnableLakeMemory: makeBooleanSetting({
|
|
7552
8019
|
key: "EnableLakeMemory",
|
|
7553
8020
|
name: "Data Lakes: Lake memory profile (extraction)",
|
|
@@ -7568,6 +8035,72 @@ const settingsMap = {
|
|
|
7568
8035
|
order: 92,
|
|
7569
8036
|
dependsOn: "EnableDataLakes"
|
|
7570
8037
|
}),
|
|
8038
|
+
PauseLakeConvergence: makeBooleanSetting({
|
|
8039
|
+
key: "PauseLakeConvergence",
|
|
8040
|
+
name: "Data Lakes: Pause background convergence work",
|
|
8041
|
+
defaultValue: false,
|
|
8042
|
+
description: "Kill switch for background data-lake ingestion work (convergence sweeps, rescue re-chunking) - NOT real-time user uploads, which are always honored. Off by default. Turn ON to halt in-flight background chunk/vectorize messages the next time the handler picks them up (a re-check inside the shared handler, so it takes effect on work already queued, not just the next scheduling pass). The platform value pauses every lake at once; a per-lake (or per-org / per-owner) override pauses a subset while the rest keep running. A platform-level flip applies immediately to lake-wide work and within ~5 min to per-lake-scoped work (settings cache).",
|
|
8043
|
+
category: "Experimental",
|
|
8044
|
+
group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
|
|
8045
|
+
order: 93,
|
|
8046
|
+
dependsOn: "EnableDataLakes",
|
|
8047
|
+
scope: { settableAt: [
|
|
8048
|
+
"organization",
|
|
8049
|
+
"owner",
|
|
8050
|
+
"lake"
|
|
8051
|
+
] }
|
|
8052
|
+
}),
|
|
8053
|
+
LakeConvergenceBulkChangeSharePct: makeNumberSetting({
|
|
8054
|
+
key: "LakeConvergenceBulkChangeSharePct",
|
|
8055
|
+
name: "Data Lakes: Convergence bulk-change confirmation threshold (%)",
|
|
8056
|
+
defaultValue: 25,
|
|
8057
|
+
min: 1,
|
|
8058
|
+
max: 100,
|
|
8059
|
+
description: "Share of a data lake, as a percentage of its gradable members, above which owner-triggered convergence (#1681) requires an explicit confirmation before it rewrites anything. A mass rewrite is the signature of a misconfigured chunk policy, and every individual change inside one looks locally reasonable, so the share is the only place the mistake is visible. The guard is suppressed on lakes with fewer gradable members than the plan needs for a percentage to mean anything. Lower it to make convergence ask more often; it never blocks a confirmed run.",
|
|
8060
|
+
category: "AI",
|
|
8061
|
+
order: 4,
|
|
8062
|
+
dependsOn: "EnableDataLakes",
|
|
8063
|
+
scope: { settableAt: [
|
|
8064
|
+
"organization",
|
|
8065
|
+
"owner",
|
|
8066
|
+
"lake"
|
|
8067
|
+
] }
|
|
8068
|
+
}),
|
|
8069
|
+
EnforceLakeReadGrants: makeBooleanSetting({
|
|
8070
|
+
key: "EnforceLakeReadGrants",
|
|
8071
|
+
name: "Data Lakes: Enforce read-time grant resolution",
|
|
8072
|
+
defaultValue: false,
|
|
8073
|
+
description: "Read-time grant cutover (#1673). OFF by default = report-only: the read gate resolves a persisted READER/org grant into an ephemeral membership view and logs where it WOULD change access ([lakeReadGrantCutover] lines), but the enforced decision stays the legacy owner/org/tag/entitlement/public rule so no one gains or loses access. NOTE: turning this ON is currently a NO-OP guarded by a source-level interlock (READ_GRANT_ENFORCEMENT_READY) - enforcement will not activate until the follow-up code (member-management write path + retrieval arm) lands and flips it, and a premature toggle just logs a warning and stays report-only. This is deliberate so the setting cannot half-enable a half-wired gate. Platform altitude on purpose: a one-time install-wide migration cutover, not a per-lake lever. Tag and entitlement grants always resolve live and are never affected by this flag; only persisted reader/org rows are gated by it.",
|
|
8074
|
+
category: "Experimental",
|
|
8075
|
+
group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
|
|
8076
|
+
order: 94,
|
|
8077
|
+
dependsOn: "EnableDataLakes"
|
|
8078
|
+
}),
|
|
8079
|
+
EnableDataLakeDrivePoll: makeBooleanSetting({
|
|
8080
|
+
key: "EnableDataLakeDrivePoll",
|
|
8081
|
+
name: "Data Lakes: Google Drive auto re-sync poll",
|
|
8082
|
+
defaultValue: false,
|
|
8083
|
+
description: "Server-side gate for the scheduled poll that keeps connected Google Drive folders in sync with their data lakes (adds/edits/removals). Off by default - a connected folder still syncs on demand via the Re-sync button; turn this on to also reconcile it automatically on a schedule.",
|
|
8084
|
+
category: "Experimental",
|
|
8085
|
+
group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
|
|
8086
|
+
order: 95,
|
|
8087
|
+
dependsOn: "EnableDataLakes"
|
|
8088
|
+
}),
|
|
8089
|
+
EnforceLakeAdmission: makeBooleanSetting({
|
|
8090
|
+
key: "EnforceLakeAdmission",
|
|
8091
|
+
name: "Data Lakes: Enforce the admission contract",
|
|
8092
|
+
defaultValue: false,
|
|
8093
|
+
description: "Retrievability contract at admission (#1680). OFF by default = report-only: a file whose chunks cannot honor the chunk policy a lake REQUIRES is logged as quarantined ([admission] lines) but still joins the lake, exactly as today. ON refuses the membership write instead, so unretrievable content never becomes a member and no embedding spend is incurred for it; the caller gets an error naming the required and actual passage targets. Enforcement applies to NEW memberships only - files already in a lake are never evicted, and no query is ever blocked on lake health, which is advisory permanently. Turn this on only after the lake health report shows how many members would be refused. The lake rung is the one that matters (a lake enforces its own contract); the org and owner rungs enforce across every lake in that scope at once. A flip is not instantaneous: the settings cache is per-instance, so it applies immediately on the instance that served the change and within ~5 min (one cache TTL) everywhere else - an upload that still succeeds right after turning this on is stale cache, not a broken lever.",
|
|
8094
|
+
category: "Experimental",
|
|
8095
|
+
group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
|
|
8096
|
+
order: 96,
|
|
8097
|
+
dependsOn: "EnableDataLakes",
|
|
8098
|
+
scope: { settableAt: [
|
|
8099
|
+
"organization",
|
|
8100
|
+
"owner",
|
|
8101
|
+
"lake"
|
|
8102
|
+
] }
|
|
8103
|
+
}),
|
|
7571
8104
|
EnableBriefcase: makeBooleanSetting({
|
|
7572
8105
|
key: "EnableBriefcase",
|
|
7573
8106
|
name: "Enable Briefcase",
|
|
@@ -7790,9 +8323,14 @@ const settingsMap = {
|
|
|
7790
8323
|
name: "Default Chunk Size",
|
|
7791
8324
|
defaultValue: 512,
|
|
7792
8325
|
min: 64,
|
|
7793
|
-
|
|
8326
|
+
max: OVERSIZED_PASSAGE_TOKEN_THRESHOLD,
|
|
8327
|
+
description: "Passage target in TOKENS for splitting large documents. The DEFAULT matches the chunker; a value stored here overrides it, and a stored value larger than the chunker default makes the UI reprocess path produce coarser chunks than /api/files/reprocess. Coarser chunks measurably worsen retrieval, and values above the under-chunked detection threshold also stop \"Rebuild passages\" converging, so the accepted range is capped there. Resolves at file-OWNER altitude: an org/individual owner may pin their own default above the platform value; a data lake does NOT override it (epic decision 7) - a lake declares the policy it REQUIRES and a file that cannot satisfy every lake it belongs to is reported as a conflict rather than silently re-chunked.",
|
|
7794
8328
|
category: "AI",
|
|
7795
|
-
order: 3
|
|
8329
|
+
order: 3,
|
|
8330
|
+
scope: {
|
|
8331
|
+
settableAt: ["organization", "owner"],
|
|
8332
|
+
clamp: (value) => Math.min(Math.max(Math.floor(value), 64), OVERSIZED_PASSAGE_TOKEN_THRESHOLD)
|
|
8333
|
+
}
|
|
7796
8334
|
}),
|
|
7797
8335
|
ModerationEnabled: makeBooleanSetting({
|
|
7798
8336
|
key: "ModerationEnabled",
|
|
@@ -7836,7 +8374,7 @@ const settingsMap = {
|
|
|
7836
8374
|
key: "AbstentionPrompt",
|
|
7837
8375
|
name: "Abstention Prompt",
|
|
7838
8376
|
defaultValue: ABSTENTION_PROMPT,
|
|
7839
|
-
description: "Short system prompt licensing the model to say \"I do not have enough to answer that\" and to name what is missing instead of inventing facts about the user or their data. Injected on every chat completion. Live-editable; clearing it reverts to the built-in default.",
|
|
8377
|
+
description: "Short system prompt licensing the model to say \"I do not have enough to answer that\" and to name what is missing instead of inventing facts about the user or their data. Injected on every chat completion. Live-editable; clearing it reverts to the built-in default. After an upgrade, diff a saved copy against that default: a saved copy pins the wording from whenever it was saved and will not pick up fixes made since.",
|
|
7840
8378
|
category: "AI",
|
|
7841
8379
|
order: 11
|
|
7842
8380
|
}),
|
|
@@ -7962,6 +8500,15 @@ const settingsMap = {
|
|
|
7962
8500
|
group: API_SERVICE_GROUPS.FEEDBACK.id,
|
|
7963
8501
|
order: 9
|
|
7964
8502
|
}),
|
|
8503
|
+
FeedbackReceiveEmailNonProd: makeStringSetting({
|
|
8504
|
+
key: "FeedbackReceiveEmailNonProd",
|
|
8505
|
+
name: "Non-Production Feedback Email",
|
|
8506
|
+
defaultValue: "",
|
|
8507
|
+
description: "Comma-separated recipient list for feedback submitted from every non-production stage (dev, staging, previews). Does not apply to a self-host install, which routes through FeedbackReceiveEmail like production. Leave empty to suppress non-production email entirely - it never falls back to the production recipient list.",
|
|
8508
|
+
category: "Feedback",
|
|
8509
|
+
group: API_SERVICE_GROUPS.FEEDBACK.id,
|
|
8510
|
+
order: 9.5
|
|
8511
|
+
}),
|
|
7965
8512
|
FeedbackKyle: makeStringSetting({
|
|
7966
8513
|
key: "FeedbackKyle",
|
|
7967
8514
|
name: "Kyle Feedback Email",
|
|
@@ -8036,7 +8583,17 @@ const settingsMap = {
|
|
|
8036
8583
|
description: "The webhook URL for sending feedback to the #bike4mind-feedback Slack channel.",
|
|
8037
8584
|
category: "Feedback",
|
|
8038
8585
|
group: API_SERVICE_GROUPS.FEEDBACK.id,
|
|
8039
|
-
order:
|
|
8586
|
+
order: 6.75,
|
|
8587
|
+
isSensitive: true
|
|
8588
|
+
}),
|
|
8589
|
+
SlackNonProdFeedbackWebhookUrl: makeStringSetting({
|
|
8590
|
+
key: "SlackNonProdFeedbackWebhookUrl",
|
|
8591
|
+
name: "Non-Production Feedback Channel Webhook URL",
|
|
8592
|
+
defaultValue: "",
|
|
8593
|
+
description: "Incoming-webhook URL that receives feedback submitted from every non-production stage (dev, staging, previews). Does not apply to a self-host install, which routes through SlackFeedbackWebhookUrl like production. Leave empty to suppress non-production feedback entirely - it never falls back to the production feedback channel.",
|
|
8594
|
+
category: "Feedback",
|
|
8595
|
+
group: API_SERVICE_GROUPS.FEEDBACK.id,
|
|
8596
|
+
order: 6.8,
|
|
8040
8597
|
isSensitive: true
|
|
8041
8598
|
}),
|
|
8042
8599
|
SlackEmailAuditWebhookUrl: makeStringSetting({
|
|
@@ -8643,9 +9200,9 @@ const settingsMap = {
|
|
|
8643
9200
|
}),
|
|
8644
9201
|
bflApiKey: makeStringSetting({
|
|
8645
9202
|
key: "bflApiKey",
|
|
8646
|
-
name: "
|
|
9203
|
+
name: "Black Forest Labs API Key",
|
|
8647
9204
|
defaultValue: "",
|
|
8648
|
-
description: "The API Key for
|
|
9205
|
+
description: "The API Key for Black Forest Labs image generation service.",
|
|
8649
9206
|
isSensitive: true,
|
|
8650
9207
|
category: "AI",
|
|
8651
9208
|
group: API_SERVICE_GROUPS.IMAGE_GENERATION.id,
|
|
@@ -8673,7 +9230,12 @@ const settingsMap = {
|
|
|
8673
9230
|
description: "Most files one data-lake semantic search will scope. Beyond this the search reports itself as truncated rather than silently ignoring the rest. Raising it well past a few thousand also deepens the paging offset, so prefer reporting truncation over a very large value.",
|
|
8674
9231
|
category: "AI",
|
|
8675
9232
|
group: API_SERVICE_GROUPS.EMBEDDING.id,
|
|
8676
|
-
order: 2
|
|
9233
|
+
order: 2,
|
|
9234
|
+
scope: { settableAt: [
|
|
9235
|
+
"organization",
|
|
9236
|
+
"owner",
|
|
9237
|
+
"lake"
|
|
9238
|
+
] }
|
|
8677
9239
|
}),
|
|
8678
9240
|
dataLakeSearchMaxChunks: makeNumberSetting({
|
|
8679
9241
|
key: "dataLakeSearchMaxChunks",
|
|
@@ -8683,8 +9245,190 @@ const settingsMap = {
|
|
|
8683
9245
|
description: "Most chunk vectors one data-lake semantic search will score. Raising it trades query latency for coverage; lowering it makes truncation more likely (and reported).",
|
|
8684
9246
|
category: "AI",
|
|
8685
9247
|
group: API_SERVICE_GROUPS.EMBEDDING.id,
|
|
9248
|
+
order: 3,
|
|
9249
|
+
scope: { settableAt: [
|
|
9250
|
+
"organization",
|
|
9251
|
+
"owner",
|
|
9252
|
+
"lake"
|
|
9253
|
+
] }
|
|
9254
|
+
}),
|
|
9255
|
+
forcedRetrievalCharBudget: makeNumberSetting({
|
|
9256
|
+
key: "forcedRetrievalCharBudget",
|
|
9257
|
+
name: "Forced Retrieval Char Budget",
|
|
9258
|
+
defaultValue: FORCED_RETRIEVAL_CHAR_BUDGET_DEFAULT,
|
|
9259
|
+
min: 1e3,
|
|
9260
|
+
max: 1e5,
|
|
9261
|
+
description: "Total characters of retrieved chunk text injected into a Data-Lake-mode turn. Measured saturating on every turn against a 47-document lake, so this is the binding constraint on how much of a corpus reaches the model - not the relevance floor. Raising it admits more passages at the cost of prompt tokens and latency on every Data-Lake turn; it is NOT automatically better, since more context can dilute ranking. Platform-only for now: this read does not go through the scoped-settings resolver, so a `settableAt` block here would be inert metadata at best and could arm the resolver's fail-loud owner check at worst.",
|
|
9262
|
+
category: "AI",
|
|
9263
|
+
group: API_SERVICE_GROUPS.EMBEDDING.id,
|
|
9264
|
+
order: 4
|
|
9265
|
+
}),
|
|
9266
|
+
kbSearchDefaultResults: makeNumberSetting({
|
|
9267
|
+
key: "kbSearchDefaultResults",
|
|
9268
|
+
name: "Knowledge Base Search Default Results",
|
|
9269
|
+
defaultValue: 5,
|
|
9270
|
+
min: 1,
|
|
9271
|
+
max: 10,
|
|
9272
|
+
description: "Passages the search_knowledge_base tool returns when a model call omits max_results, which is most calls. This is the exact bound while kbSearchResultTokenBudget is unset (0). Once a token budget is set, it takes over as the primary bound for search results (this setting's own value is then unused there, though it still governs the keyword-search fallback, and the count served if token pricing itself fails). Does NOT raise the tool's hard ceiling of 10 passages per call - a model that reads max_results up to 10 from its own tool schema won't ask for more than that regardless of this setting.",
|
|
9273
|
+
category: "AI",
|
|
9274
|
+
group: API_SERVICE_GROUPS.EMBEDDING.id,
|
|
9275
|
+
order: 5,
|
|
9276
|
+
scope: { settableAt: ["organization", "owner"] }
|
|
9277
|
+
}),
|
|
9278
|
+
kbSearchResultTokenBudget: makeNumberSetting({
|
|
9279
|
+
key: "kbSearchResultTokenBudget",
|
|
9280
|
+
name: "Knowledge Base Search Result Token Budget",
|
|
9281
|
+
defaultValue: 0,
|
|
9282
|
+
min: 0,
|
|
9283
|
+
max: 2e4,
|
|
9284
|
+
description: "Approximate tokens of served passage TEXT (post-trim, post-clip - what the model actually receives, not the raw stored chunk) the search_knowledge_base tool may return in one call. Counted with a fixed tokenizer as a proxy, not billed against any specific model. Replaces a passage count as the primary bound once set, since it is invariant to chunk size - a lake chunked smaller no longer silently returns less material for the same setting. 0 (default) disables it: search_knowledge_base then serves exactly kbSearchDefaultResults passages, unchanged from before this setting existed. The FIRST matching passage is always returned even if it alone exceeds the budget - a search that found something never returns nothing.",
|
|
9285
|
+
category: "AI",
|
|
9286
|
+
group: API_SERVICE_GROUPS.EMBEDDING.id,
|
|
9287
|
+
order: 6,
|
|
9288
|
+
scope: { settableAt: ["organization", "owner"] }
|
|
9289
|
+
}),
|
|
9290
|
+
kbSearchMinRelevancePct: makeNumberSetting({
|
|
9291
|
+
key: "kbSearchMinRelevancePct",
|
|
9292
|
+
name: "Knowledge Base Search Minimum Relevance (%)",
|
|
9293
|
+
defaultValue: 0,
|
|
9294
|
+
min: 0,
|
|
9295
|
+
max: 100,
|
|
9296
|
+
description: "Minimum cosine relevance, as a percent, a passage must clear to be returned by search_knowledge_base. 0 (default) matches current behavior (no relevance floor beyond a non-negative cosine score). Raising it lets breadth adapt per query - a narrow question can return fewer, more relevant passages instead of always padding out to the configured count. Cosine similarity is not comparable across embedding models: a floor tuned for one model can filter out an entire alternate model, when a lake mixes embedding models, more aggressively than intended. Start low and raise gradually while watching the tool's own retrieval-skipped notices.",
|
|
9297
|
+
category: "AI",
|
|
9298
|
+
group: API_SERVICE_GROUPS.EMBEDDING.id,
|
|
9299
|
+
order: 7,
|
|
9300
|
+
scope: { settableAt: ["organization", "owner"] }
|
|
9301
|
+
}),
|
|
9302
|
+
LakeAccessAuditRetentionDays: makeNumberSetting({
|
|
9303
|
+
key: "LakeAccessAuditRetentionDays",
|
|
9304
|
+
name: "Lake Access Audit Retention (days)",
|
|
9305
|
+
defaultValue: 450,
|
|
9306
|
+
min: 450,
|
|
9307
|
+
max: LAKE_ACCESS_AUDIT_RETENTION_MAX_DAYS,
|
|
9308
|
+
description: "How long a lake access audit event (who read a lake, and when) is retained, in days. Has a floor of 450 days (12 months live plus a Type II observation tail) - this is a platform-wide value, not per-organization, until a scoped settings resolver exists. Applies only to events written after a change: expiresAt is computed once at write time and is immutable, so raising or lowering this value never affects rows already recorded.",
|
|
9309
|
+
category: "SecOps",
|
|
9310
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_AUDIT.id,
|
|
9311
|
+
order: 1
|
|
9312
|
+
}),
|
|
9313
|
+
LakeAccessQueryTextRetentionDays: makeNumberSetting({
|
|
9314
|
+
key: "LakeAccessQueryTextRetentionDays",
|
|
9315
|
+
name: "Lake Access Query Text Retention (days)",
|
|
9316
|
+
defaultValue: 30,
|
|
9317
|
+
min: 1,
|
|
9318
|
+
max: 90,
|
|
9319
|
+
description: "How long the opt-in query-text log (the natural-language question behind a lake retrieval) is retained, in days. Always resolved shorter than the audit event retention itself, regardless of this value, since the query text is more sensitive than the event metadata. Applies only to events written after a change - already-recorded rows keep the expiry computed at write time and are not retroactively shortened or extended.",
|
|
9320
|
+
category: "SecOps",
|
|
9321
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_AUDIT.id,
|
|
9322
|
+
order: 2
|
|
9323
|
+
}),
|
|
9324
|
+
LakeConfigAuditRetentionDays: makeNumberSetting({
|
|
9325
|
+
key: "LakeConfigAuditRetentionDays",
|
|
9326
|
+
name: "Lake Config Change Audit Retention (days)",
|
|
9327
|
+
defaultValue: LAKE_CONFIG_AUDIT_RETENTION_DEFAULT_DAYS,
|
|
9328
|
+
min: LAKE_CONFIG_AUDIT_RETENTION_FLOOR_DAYS,
|
|
9329
|
+
max: LAKE_CONFIG_AUDIT_RETENTION_MAX_DAYS,
|
|
9330
|
+
description: "How long a lake CONFIG-change event (who changed a lake, what they changed, and which manage rung authorized it) is retained, in days. Floored at 1095 days - deliberately longer than the access-audit retention above, because a config change is rare and alters every future answer the lake gives, where a read is one turn. Platform-wide, not per-organization, until a scoped settings resolver exists.",
|
|
9331
|
+
category: "SecOps",
|
|
9332
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_AUDIT.id,
|
|
8686
9333
|
order: 3
|
|
8687
9334
|
}),
|
|
9335
|
+
dataLakeEmbeddingSpendEnabled: makeBooleanSetting({
|
|
9336
|
+
key: "dataLakeEmbeddingSpendEnabled",
|
|
9337
|
+
name: "Data Lake Embedding Spend Enabled",
|
|
9338
|
+
defaultValue: true,
|
|
9339
|
+
description: "Master switch for data-lake embedding spend (ingestion, reprocessing, convergence). Off halts all provider embedding calls on those paths; cached embeddings still apply.",
|
|
9340
|
+
category: "AI",
|
|
9341
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9342
|
+
order: 1
|
|
9343
|
+
}),
|
|
9344
|
+
dataLakeEmbeddingBudgetPerRunUsd: makeNumberSetting({
|
|
9345
|
+
key: "dataLakeEmbeddingBudgetPerRunUsd",
|
|
9346
|
+
name: "Embedding Budget Per Run (USD)",
|
|
9347
|
+
defaultValue: 5,
|
|
9348
|
+
min: 0,
|
|
9349
|
+
max: 500,
|
|
9350
|
+
description: "Most USD one ingestion/reprocess run (upload batch) may spend on embedding calls. 0 stops runs from spending at all.",
|
|
9351
|
+
category: "AI",
|
|
9352
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9353
|
+
order: 2
|
|
9354
|
+
}),
|
|
9355
|
+
dataLakeEmbeddingBudgetPerLakeUsd: makeNumberSetting({
|
|
9356
|
+
key: "dataLakeEmbeddingBudgetPerLakeUsd",
|
|
9357
|
+
name: "Embedding Budget Per Lake (USD)",
|
|
9358
|
+
defaultValue: 100,
|
|
9359
|
+
min: 0,
|
|
9360
|
+
max: DATA_LAKE_EMBEDDING_BUDGET_PER_LAKE_USD_MAX,
|
|
9361
|
+
description: "Most USD one data lake may spend on embedding calls over its lifetime. 0 stops all spend for every lake.",
|
|
9362
|
+
category: "AI",
|
|
9363
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9364
|
+
order: 3
|
|
9365
|
+
}),
|
|
9366
|
+
dataLakeEmbeddingBudgetPerPeriodUsd: makeNumberSetting({
|
|
9367
|
+
key: "dataLakeEmbeddingBudgetPerPeriodUsd",
|
|
9368
|
+
name: "Embedding Budget Per Period (USD)",
|
|
9369
|
+
defaultValue: 50,
|
|
9370
|
+
min: 0,
|
|
9371
|
+
max: DATA_LAKE_EMBEDDING_BUDGET_PER_PERIOD_USD_MAX,
|
|
9372
|
+
description: "Most USD the whole platform may spend on data-lake embedding calls per rolling period (see the period-hours setting). 0 stops all spend.",
|
|
9373
|
+
category: "AI",
|
|
9374
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9375
|
+
order: 4
|
|
9376
|
+
}),
|
|
9377
|
+
dataLakeEmbeddingBudgetPeriodHours: makeNumberSetting({
|
|
9378
|
+
key: "dataLakeEmbeddingBudgetPeriodHours",
|
|
9379
|
+
name: "Embedding Budget Period (hours)",
|
|
9380
|
+
defaultValue: 24,
|
|
9381
|
+
min: 1,
|
|
9382
|
+
max: 720,
|
|
9383
|
+
description: "Length of the per-period budget window in hours. Not a spend value itself, so 0 is not meaningful here (min 1).",
|
|
9384
|
+
category: "AI",
|
|
9385
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9386
|
+
order: 5
|
|
9387
|
+
}),
|
|
9388
|
+
dataLakeEmbeddingMaxCallsPerMinute: makeNumberSetting({
|
|
9389
|
+
key: "dataLakeEmbeddingMaxCallsPerMinute",
|
|
9390
|
+
name: "Embedding Max Calls Per Minute",
|
|
9391
|
+
defaultValue: 120,
|
|
9392
|
+
min: 0,
|
|
9393
|
+
max: DATA_LAKE_EMBEDDING_MAX_CALLS_PER_MINUTE_MAX,
|
|
9394
|
+
description: "Most provider embedding API calls per minute across all data-lake work. The real throttle in front of the embed call (the queue concurrency in infra is a deploy-time constant, not this lever). 0 stops all calls.",
|
|
9395
|
+
category: "AI",
|
|
9396
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9397
|
+
order: 6
|
|
9398
|
+
}),
|
|
9399
|
+
dataLakeVectorizeChunkBatchSize: makeNumberSetting({
|
|
9400
|
+
key: "dataLakeVectorizeChunkBatchSize",
|
|
9401
|
+
name: "Vectorize Chunk Batch Size",
|
|
9402
|
+
defaultValue: 50,
|
|
9403
|
+
min: 1,
|
|
9404
|
+
max: 500,
|
|
9405
|
+
description: "How many chunks the chunk handler packs into one vectorize-queue message. Smaller batches smooth the fan-out; not a spend value, so min 1.",
|
|
9406
|
+
category: "AI",
|
|
9407
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9408
|
+
order: 7
|
|
9409
|
+
}),
|
|
9410
|
+
dataLakeEmbeddingTierMultiplierIndividual: makeNumberSetting({
|
|
9411
|
+
key: "dataLakeEmbeddingTierMultiplierIndividual",
|
|
9412
|
+
name: "Cost Tier Multiplier - Individual-Owned Lakes",
|
|
9413
|
+
defaultValue: 1,
|
|
9414
|
+
min: 0,
|
|
9415
|
+
max: 100,
|
|
9416
|
+
description: "Scales the per-run and per-lake embedding budgets for lakes owned by an individual user. 1 means those lakes get exactly the configured budgets; 0 stops them spending at all. The effective budget is still capped by the same hard rail as the untiered value.",
|
|
9417
|
+
category: "AI",
|
|
9418
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9419
|
+
order: 8
|
|
9420
|
+
}),
|
|
9421
|
+
dataLakeEmbeddingTierMultiplierOrganization: makeNumberSetting({
|
|
9422
|
+
key: "dataLakeEmbeddingTierMultiplierOrganization",
|
|
9423
|
+
name: "Cost Tier Multiplier - Organization-Owned Lakes",
|
|
9424
|
+
defaultValue: 5,
|
|
9425
|
+
min: 0,
|
|
9426
|
+
max: 100,
|
|
9427
|
+
description: "Scales the per-run and per-lake embedding budgets for lakes owned by an organization, which serve a whole team rather than one person. 0 stops org-owned lakes spending at all. The effective budget is still capped by the same hard rail as the untiered value.",
|
|
9428
|
+
category: "AI",
|
|
9429
|
+
group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
|
|
9430
|
+
order: 9
|
|
9431
|
+
}),
|
|
8688
9432
|
slackSigningSecret: makeStringSetting({
|
|
8689
9433
|
key: "slackSigningSecret",
|
|
8690
9434
|
name: "Slack Signing Secret",
|
|
@@ -9393,6 +10137,40 @@ const settingsMap = {
|
|
|
9393
10137
|
category: "AI",
|
|
9394
10138
|
group: API_SERVICE_GROUPS.MODEL_DISCOVERY.id,
|
|
9395
10139
|
order: 6
|
|
10140
|
+
}),
|
|
10141
|
+
prReportRepo: makeStringSetting({
|
|
10142
|
+
key: "prReportRepo",
|
|
10143
|
+
name: "PR Report Repository",
|
|
10144
|
+
defaultValue: "",
|
|
10145
|
+
description: "The `owner/repo` whose open pull requests the PR status digest reports on. Validated against an anchored GitHub repo grammar before it is interpolated into any authenticated outbound URL (SSRF guard) - a value with an empty or `..` segment is rejected.",
|
|
10146
|
+
category: "Admin",
|
|
10147
|
+
order: 141
|
|
10148
|
+
}),
|
|
10149
|
+
prReportIdentityMap: makeStringSetting({
|
|
10150
|
+
key: "prReportIdentityMap",
|
|
10151
|
+
name: "PR Report Identity Map",
|
|
10152
|
+
defaultValue: "",
|
|
10153
|
+
description: "Maps GitHub logins and synthetic role keys (`qa_*`, `devops_*`, `reviewer_*`) to Slack member IDs, one mapping per line. Accepts `key value`, `key=value` or `key: value`; blank and `#` comment lines are ignored. Values must be real Slack member IDs - display names do not produce notification mentions.",
|
|
10154
|
+
category: "Admin",
|
|
10155
|
+
order: 142
|
|
10156
|
+
}),
|
|
10157
|
+
prReportWebhookUrl: makeStringSetting({
|
|
10158
|
+
key: "prReportWebhookUrl",
|
|
10159
|
+
name: "PR Report Slack Webhook URL",
|
|
10160
|
+
defaultValue: "",
|
|
10161
|
+
isSensitive: true,
|
|
10162
|
+
description: "Slack Incoming Webhook URL the PR status digest posts to (https://hooks.slack.com/services/...). It already encodes its channel and workspace, so no bot token or channel ID is needed to send. Bearer-equivalent: anyone holding it can post to the channel, so it is stored encrypted and never returned to the browser.",
|
|
10163
|
+
category: "Slack",
|
|
10164
|
+
order: 143
|
|
10165
|
+
}),
|
|
10166
|
+
prReportEgressAllowlist: makeObjectSetting({
|
|
10167
|
+
key: "prReportEgressAllowlist",
|
|
10168
|
+
name: "PR Report Egress Allowlist",
|
|
10169
|
+
defaultValue: { hosts: ["hooks.slack.com"] },
|
|
10170
|
+
description: "Hosts the PR digest may post to, checked against the webhook URL its own hostname. FAILS CLOSED: an empty list rejects every send rather than degrading to allow-any, because the post body carries PR titles, author logins and the staffing implied by the role rosters. Slack incoming webhooks live at hooks.slack.com, so that is the default.",
|
|
10171
|
+
category: "Slack",
|
|
10172
|
+
order: 144,
|
|
10173
|
+
schema: z$1.object({ hosts: z$1.array(z$1.string()).default([]) })
|
|
9396
10174
|
})
|
|
9397
10175
|
};
|
|
9398
10176
|
/**
|
|
@@ -9781,6 +10559,7 @@ const PromptMetaTokenUsageSchema = z$1.object({
|
|
|
9781
10559
|
actualOutputTokens: z$1.number().optional(),
|
|
9782
10560
|
actualTotalTokens: z$1.number().optional(),
|
|
9783
10561
|
cacheReadInputTokens: z$1.number().optional(),
|
|
10562
|
+
cacheCreationInputTokens: z$1.number().optional(),
|
|
9784
10563
|
settledBasis: z$1.enum(["provider", "local"]).optional(),
|
|
9785
10564
|
estimatedCost: z$1.number().optional(),
|
|
9786
10565
|
creditsUsed: z$1.number().optional()
|
|
@@ -9980,10 +10759,67 @@ const CitableSourceSchema = z$1.object({
|
|
|
9980
10759
|
fullContext: z$1.string().optional()
|
|
9981
10760
|
}).optional()
|
|
9982
10761
|
});
|
|
10762
|
+
/**
|
|
10763
|
+
* Per-turn retrieval outcome (#1867): whether retrieval was attempted this turn and what happened,
|
|
10764
|
+
* independent of whether the model then cited anything. Exists specifically to make the zero case
|
|
10765
|
+
* distinguishable from "never asked" - `context.lakeMemory` and `citables` both go silent on a
|
|
10766
|
+
* zero-result retrieval, so a turn that legitimately found nothing is indistinguishable from one
|
|
10767
|
+
* where retrieval never ran at all.
|
|
10768
|
+
*
|
|
10769
|
+
* Deliberately holds NO counts and NO chunk/document identifiers. Counts already exist and are
|
|
10770
|
+
* more precise: `citables.filter(c => c.type === 'document')` is deduped by id/url/title in
|
|
10771
|
+
* `applyQuestStatusChanges`, while this shape cannot dedupe (no identifiers to dedupe by) and
|
|
10772
|
+
* would have to sum - producing a second, disagreeing number for the same question. Similarity
|
|
10773
|
+
* scores live on `LakeAccessEvent`, not here.
|
|
10774
|
+
*
|
|
10775
|
+
* CAUTION, not a guarantee: the absence of chunk/document identifiers is what keeps this shape
|
|
10776
|
+
* OUT of `promptMetaRedaction.ts`'s scope (that helper is a functionCalls-only denylist and would
|
|
10777
|
+
* not catch a nested nonidentifier field like `dataLakeTags` regardless). It does NOT mean this
|
|
10778
|
+
* field never needs redaction consideration - `dataLakeTags` (which lakes were involved) already
|
|
10779
|
+
* reaches non-owner viewers the same way `lakeMemory.dataLakeTags` does (session shares, feedback
|
|
10780
|
+
* egress, admin logs, session clone - see redactedFeedback.ts, admin/model-logs.ts, clone.ts, none
|
|
10781
|
+
* of which touch this field). That exposure is not new in the general case, but it IS new
|
|
10782
|
+
* specifically on a zero-recall turn: `lakeMemory` was never written there before this field
|
|
10783
|
+
* existed, so a turn that previously carried no lake-identity signal at all now carries one.
|
|
10784
|
+
*
|
|
10785
|
+
* `attempted`/`outcome` on their own would still be ambiguous about WHICH lakes were searched on a
|
|
10786
|
+
* zero-recall turn (dataLakeTags otherwise lives only inside `lakeMemory`, written after the
|
|
10787
|
+
* zero-belief return), so this stamps the resolved tags at write time rather than making a reader
|
|
10788
|
+
* fall back to the session's current (possibly since-changed) `retrievalTags`.
|
|
10789
|
+
*
|
|
10790
|
+
* Absent-or-fully-present, matching `lakeMemory` above - see the Mongoose-side subSchema comment
|
|
10791
|
+
* in QuestModel.ts for why partial-write and default-array shapes are unsafe here.
|
|
10792
|
+
*/
|
|
10793
|
+
const RetrievalSummarySchema = z$1.object({
|
|
10794
|
+
/** True once a retrieval-capable surface actually ran (not merely offered) this turn. */
|
|
10795
|
+
attempted: z$1.boolean(),
|
|
10796
|
+
/**
|
|
10797
|
+
* 'ok' - ran, whether or not anything came back (the zero case is a legitimate 'ok').
|
|
10798
|
+
* 'no_lakes' - ran but the user had no entitled/selected lake in scope.
|
|
10799
|
+
* 'failed' - threw; recall did not complete.
|
|
10800
|
+
* On multiple retrieval calls within one turn, merge priority is failed > ok > no_lakes (see
|
|
10801
|
+
* retrievalSummaryMerge.ts's mergeRetrievalSummary): a single failure is never masked by a later
|
|
10802
|
+
* success or abstain, and a real success on one surface is never masked by another surface's
|
|
10803
|
+
* "no lakes in scope" abstain in the same turn.
|
|
10804
|
+
*/
|
|
10805
|
+
outcome: z$1.enum([
|
|
10806
|
+
"ok",
|
|
10807
|
+
"no_lakes",
|
|
10808
|
+
"failed"
|
|
10809
|
+
]),
|
|
10810
|
+
/** Which retrieval-capable surface(s) ran this turn, e.g. 'lake-memory', 'knowledgeBaseSearch'. */
|
|
10811
|
+
surfaces: z$1.array(z$1.string()),
|
|
10812
|
+
/** Lakes resolved at the moment retrieval ran, stamped point-in-time (not read live from the session). */
|
|
10813
|
+
dataLakeTags: z$1.array(z$1.string())
|
|
10814
|
+
});
|
|
9983
10815
|
z$1.object({
|
|
9984
10816
|
model: PromptMetaModelSchema.optional(),
|
|
9985
10817
|
tokenUsage: PromptMetaTokenUsageSchema.optional(),
|
|
9986
10818
|
context: PromptMetaContextSchema.optional(),
|
|
10819
|
+
/** Per-turn retrieval outcome - see RetrievalSummarySchema. Top-level (not under `context`)
|
|
10820
|
+
* deliberately: applyQuestStatusChanges does a one-level spread merge, so a field nested under
|
|
10821
|
+
* `context` would be replaced wholesale by any tool-arm write instead of merging. */
|
|
10822
|
+
retrieval: RetrievalSummarySchema.optional(),
|
|
9987
10823
|
functionCalls: z$1.array(PromptMetaFunctionCallSchema).optional(),
|
|
9988
10824
|
/**
|
|
9989
10825
|
* Names of the tools actually offered to the model this turn - the output of `buildTools`
|
|
@@ -10594,6 +11430,24 @@ z$1.object({
|
|
|
10594
11430
|
*/
|
|
10595
11431
|
const DATALAKE_TAG_PREFIX = "datalake:";
|
|
10596
11432
|
/**
|
|
11433
|
+
* How a lake's attached corpus is grounded into a chat turn, as a DELIBERATE per-lake product
|
|
11434
|
+
* choice rather than a side effect of who is asking (see IDataLake.groundingMode):
|
|
11435
|
+
* - `inline`: paste the corpus into the prompt (never defer to the search tool).
|
|
11436
|
+
* - `retrieve`: leave the corpus to the offered search_knowledge_base tool (always defer the
|
|
11437
|
+
* tool-retrievable subset), so an owner and an entitlement-only reader ground identically.
|
|
11438
|
+
* - `auto-by-size`: keep the size heuristic - defer only when the per-doc even-split inline depth
|
|
11439
|
+
* falls below the `CorpusRetrievalMinInlineTokensPerDoc` floor (see shouldDeferCorpusToRetrieval).
|
|
11440
|
+
*
|
|
11441
|
+
* The resolution seam is create-time (resolveLakeSessionDefaults -> session.corpusGroundingMode);
|
|
11442
|
+
* the enforcement seam is the completion-path defer plan. Keep this tuple and DataLakeGroundingMode
|
|
11443
|
+
* as the single source both the Zod schema and the Mongoose enum derive from.
|
|
11444
|
+
*/
|
|
11445
|
+
const DATA_LAKE_GROUNDING_MODES = [
|
|
11446
|
+
"inline",
|
|
11447
|
+
"retrieve",
|
|
11448
|
+
"auto-by-size"
|
|
11449
|
+
];
|
|
11450
|
+
/**
|
|
10597
11451
|
* True when a would-be `fileTagPrefix` sits inside the `datalake:` namespace, which holds every
|
|
10598
11452
|
* lake's membership meta-tag. Such a prefix would make one lake's content prefix match other
|
|
10599
11453
|
* lakes' membership tags. Shared by the create schema and the wizard's client-side gate so the
|
|
@@ -10613,7 +11467,16 @@ const INVISIBLE_INK = /\u115F|\u1160|\u17B4|\u17B5|\u2800|\u3164|\uFFA0/g;
|
|
|
10613
11467
|
const hasBlankTagPrefixSegment = (prefix) => {
|
|
10614
11468
|
return (prefix.endsWith(":") ? prefix.slice(0, -1) : prefix).split(":").some((part) => !/[\p{L}\p{N}\p{P}\p{S}\p{M}]/u.test(part.replace(INVISIBLE_INK, "")));
|
|
10615
11469
|
};
|
|
10616
|
-
|
|
11470
|
+
const DATA_LAKE_SLUG_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
11471
|
+
const DATA_LAKES = [{
|
|
11472
|
+
id: "opti-knowledge",
|
|
11473
|
+
slug: "opti-knowledge",
|
|
11474
|
+
name: "Optimization Knowledge Base",
|
|
11475
|
+
requiredUserTag: "Opti",
|
|
11476
|
+
requiredEntitlement: "optihashi:pro",
|
|
11477
|
+
fileTagPrefix: "opti:",
|
|
11478
|
+
datalakeTag: "datalake:opti-knowledge"
|
|
11479
|
+
}, ...(() => {
|
|
10617
11480
|
const raw = process.env.NEXT_PUBLIC_PREMIUM_DATA_LAKES;
|
|
10618
11481
|
if (!raw) return [];
|
|
10619
11482
|
try {
|
|
@@ -10623,6 +11486,7 @@ const hasBlankTagPrefixSegment = (prefix) => {
|
|
|
10623
11486
|
return [];
|
|
10624
11487
|
}
|
|
10625
11488
|
})()];
|
|
11489
|
+
new Set(DATA_LAKES.map((l) => l.id));
|
|
10626
11490
|
/**
|
|
10627
11491
|
* Canonical normalization for entitlement keys + `requiredEntitlement` values - the ONE
|
|
10628
11492
|
* rule, applied at write time (create/update/stamp) and at match time. Mirrors the
|
|
@@ -10630,11 +11494,10 @@ const hasBlankTagPrefixSegment = (prefix) => {
|
|
|
10630
11494
|
* casing matches the lowercase keys the resolver produces.
|
|
10631
11495
|
*/
|
|
10632
11496
|
const normalizeEntitlementKey = (key) => key.trim().toLowerCase();
|
|
10633
|
-
const slugRegex = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
10634
11497
|
const sha256Regex = /^[a-f0-9]{64}$/;
|
|
10635
11498
|
z.object({
|
|
10636
11499
|
name: z.string().min(1).max(200),
|
|
10637
|
-
slug: z.string().min(2).max(60).regex(
|
|
11500
|
+
slug: z.string().min(2).max(60).regex(DATA_LAKE_SLUG_REGEX, "Slug must be lowercase alphanumeric with hyphens (e.g. \"my-data-lake\")"),
|
|
10638
11501
|
description: z.string().max(2e3).optional(),
|
|
10639
11502
|
fileTagPrefix: z.string().trim().min(2).max(30).refine((s) => s.endsWith(":"), "Tag prefix must end with \":\" (e.g. \"acme:\")").refine((s) => !hasBlankTagPrefixSegment(s), "Tag prefix segments must be non-empty (e.g. \"acme:\" or \"acme:legal:\")").refine((s) => !isReservedTagPrefix(s), `Tag prefix cannot use the reserved "${DATALAKE_TAG_PREFIX}" namespace`),
|
|
10640
11503
|
requiredUserTag: z.string().min(1).max(100).optional(),
|
|
@@ -10646,8 +11509,16 @@ z.object({
|
|
|
10646
11509
|
description: z.string().max(2e3).optional(),
|
|
10647
11510
|
systemPrompt: z.string().optional(),
|
|
10648
11511
|
preferredSystemPromptId: z.union([z.literal(""), z.string().min(1).max(200)]).optional(),
|
|
11512
|
+
groundingMode: z.enum(DATA_LAKE_GROUNDING_MODES).optional(),
|
|
10649
11513
|
requiredUserTag: z.union([z.literal(""), z.string().min(1).max(100)]).optional(),
|
|
10650
|
-
requiredEntitlement: z.union([z.literal(""), z.string().min(3).max(100).refine((s) => s.includes(":") && s.split(":").every((part) => part.length > 0), "Entitlement key must be namespaced with non-empty parts (e.g. \"product:pro\")")]).optional()
|
|
11514
|
+
requiredEntitlement: z.union([z.literal(""), z.string().min(3).max(100).refine((s) => s.includes(":") && s.split(":").every((part) => part.length > 0), "Entitlement key must be namespaced with non-empty parts (e.g. \"product:pro\")")]).optional(),
|
|
11515
|
+
auditQueryTextEnabled: z.boolean().optional(),
|
|
11516
|
+
requiredPassageTokenTarget: z.number().int().min(64).max(OVERSIZED_PASSAGE_TOKEN_THRESHOLD).nullable().optional()
|
|
11517
|
+
});
|
|
11518
|
+
z.object({
|
|
11519
|
+
groundingMode: z.enum(DATA_LAKE_GROUNDING_MODES).optional(),
|
|
11520
|
+
preferredSystemPromptId: z.union([z.literal(""), z.string().min(1).max(200)]).optional(),
|
|
11521
|
+
systemPrompt: z.string().optional()
|
|
10651
11522
|
});
|
|
10652
11523
|
z.object({
|
|
10653
11524
|
organizationId: z.string().optional(),
|
|
@@ -10834,6 +11705,45 @@ z$1.object({
|
|
|
10834
11705
|
}).omit({ model: true }).partial();
|
|
10835
11706
|
z$1.union([ToolExecutionResponseSchema, ApiErrorSchema]);
|
|
10836
11707
|
/**
|
|
11708
|
+
* Response details shared by the endpoints that return generated audio as raw
|
|
11709
|
+
* bytes (music, sound effects). Not a contract - just the pieces both of their
|
|
11710
|
+
* contracts declare, kept in one place so the published media types and headers
|
|
11711
|
+
* cannot describe one endpoint and not the other.
|
|
11712
|
+
*/
|
|
11713
|
+
/**
|
|
11714
|
+
* Every Content-Type the ElevenLabs generators map an `output_format` token to
|
|
11715
|
+
* (`contentTypeForFormat` in ElevenLabsMusicGenerator / ElevenLabsSoundGenerator).
|
|
11716
|
+
* The first entry is the default (mp3); the rest are declared as alternates.
|
|
11717
|
+
* Must stay in sync with those two mappings.
|
|
11718
|
+
*/
|
|
11719
|
+
const GENERATED_AUDIO_CONTENT_TYPES = [
|
|
11720
|
+
"audio/mpeg",
|
|
11721
|
+
"audio/opus",
|
|
11722
|
+
"audio/L16",
|
|
11723
|
+
"audio/basic",
|
|
11724
|
+
"application/octet-stream"
|
|
11725
|
+
];
|
|
11726
|
+
/**
|
|
11727
|
+
* Where the browsable copy of the generated audio ended up. These are the ONLY
|
|
11728
|
+
* channel for that information on these endpoints: the body is raw audio, so a
|
|
11729
|
+
* caller that wants the saved file has nowhere else to read it from.
|
|
11730
|
+
*/
|
|
11731
|
+
const GENERATED_AUDIO_SAVE_HEADERS = {
|
|
11732
|
+
"X-B4M-Audio-Saved": "Whether a browsable copy was saved to the file browser (\"true\"/\"false\").",
|
|
11733
|
+
"X-B4M-Audio-Fab-File-Id": "Id of the saved file. Present only when the copy was saved.",
|
|
11734
|
+
"X-B4M-Audio-File-Name": "File name of the saved copy. Present only when the copy was saved.",
|
|
11735
|
+
"X-B4M-Audio-File-Url": "Signed URL for the saved copy, minted at creation. Use this rather than re-resolving the file via GET /api/files/{id}, which fails closed until the async moderation scan completes."
|
|
11736
|
+
};
|
|
11737
|
+
/** The 200 response body of a raw-audio endpoint: default media type plus alternates. */
|
|
11738
|
+
const generatedAudioBody = () => ({
|
|
11739
|
+
contentType: GENERATED_AUDIO_CONTENT_TYPES[0],
|
|
11740
|
+
alsoReturns: GENERATED_AUDIO_CONTENT_TYPES.slice(1).map((contentType) => ({ contentType })),
|
|
11741
|
+
headers: GENERATED_AUDIO_SAVE_HEADERS
|
|
11742
|
+
});
|
|
11743
|
+
({ ...generatedAudioBody() });
|
|
11744
|
+
({ ...generatedAudioBody() });
|
|
11745
|
+
z$1.enum(["user", "convergence"]).optional().catch(void 0), z$1.string().optional();
|
|
11746
|
+
/**
|
|
10837
11747
|
* Blessed, self-hosted artifact library script paths (root-relative).
|
|
10838
11748
|
*
|
|
10839
11749
|
* Single source of truth shared across the artifact pipeline:
|
|
@@ -10937,6 +11847,10 @@ OpenAIImageGenerationInput.extend({
|
|
|
10937
11847
|
aspect_ratio: z$1.string().optional(),
|
|
10938
11848
|
fabFileIds: z$1.array(z$1.string()).prefault([]),
|
|
10939
11849
|
tools: z$1.array(z$1.union([b4mLLMTools, z$1.string()])).optional(),
|
|
11850
|
+
safety_tolerance: BFLSafetyToleranceSchema,
|
|
11851
|
+
prompt_upsampling: z$1.boolean().optional(),
|
|
11852
|
+
seed: z$1.number().nullable().optional(),
|
|
11853
|
+
output_format: z$1.enum(["jpeg", "png"]).nullable().optional(),
|
|
10940
11854
|
/** Resolved by the API route's prompt resolver. Defaults to 'fresh' for first-turn or sessions with no prior image. */
|
|
10941
11855
|
intent: PromptIntentSchema.optional(),
|
|
10942
11856
|
promptEnhancement: z$1.object({
|
|
@@ -11130,6 +12044,12 @@ z$1.object({
|
|
|
11130
12044
|
}).optional()
|
|
11131
12045
|
});
|
|
11132
12046
|
const MCP_PROVIDER_METADATA = {
|
|
12047
|
+
notion: { defaultToolDescriptions: {
|
|
12048
|
+
notion_search: "Search for pages and databases in the connected Notion workspace by text query. Returns matching page titles, IDs, and URLs.",
|
|
12049
|
+
notion_create_page: "Create a new page in the connected Notion workspace. Requires write access to be enabled. The page is created under the configured root page or a specified parent.",
|
|
12050
|
+
notion_read_page: "Read the content of a Notion page by its ID. Returns the child blocks (text, headings, lists, etc.) and a plain-text summary. Results are paginated; pass start_cursor with the returned next_cursor when has_more is true.",
|
|
12051
|
+
notion_append_blocks: "Append content blocks (paragraphs, headings, lists, code, etc.) to an existing Notion page or block. Requires write access."
|
|
12052
|
+
} },
|
|
11133
12053
|
atlassian: { defaultToolDescriptions: {
|
|
11134
12054
|
confluence_get_page: "Retrieve a Confluence page by ID or search by title within a space. Include page metadata and optional content.",
|
|
11135
12055
|
confluence_create_page: "Create a new Confluence page. Automatically uses your personal space when spaceId is omitted - no need to call confluence_get_current_user first.",
|
|
@@ -11575,6 +12495,7 @@ z$1.object({
|
|
|
11575
12495
|
reason: ReportReasonSchema,
|
|
11576
12496
|
details: z$1.string().max(2e3).optional()
|
|
11577
12497
|
});
|
|
12498
|
+
const PublishTagsSchema = z$1.array(z$1.string().max(60)).max(20);
|
|
11578
12499
|
z$1.object({
|
|
11579
12500
|
/** Short opaque id for short URLs (`/p/r/{publicId}`, `/p/f/{publicId}`) and lookups. */
|
|
11580
12501
|
publicId: z$1.string(),
|
|
@@ -11583,6 +12504,11 @@ z$1.object({
|
|
|
11583
12504
|
slug: SlugSchema,
|
|
11584
12505
|
title: z$1.string().min(1).max(200),
|
|
11585
12506
|
description: z$1.string().max(1e3).optional(),
|
|
12507
|
+
/** Freeform owner-supplied labels, normalized by normalizePublishTags. Shares a vocabulary
|
|
12508
|
+
* with AppFile tags (see GET /api/publish/tags) so one label means one thing across the app,
|
|
12509
|
+
* but stored per artifact rather than in a central tag table - there is no tag entity to keep
|
|
12510
|
+
* in sync, and a tag nobody uses simply stops appearing. */
|
|
12511
|
+
tags: PublishTagsSchema.prefault([]),
|
|
11586
12512
|
visibility: VisibilitySchema.prefault("private"),
|
|
11587
12513
|
/** Group id a viewer must belong to when gated cross-scope. */
|
|
11588
12514
|
gatedToGroupId: z$1.string().optional(),
|
|
@@ -11668,6 +12594,9 @@ z$1.object({
|
|
|
11668
12594
|
title: z$1.string().min(1).max(200),
|
|
11669
12595
|
description: z$1.string().max(1e3).optional(),
|
|
11670
12596
|
visibility: VisibilitySchema.optional(),
|
|
12597
|
+
/** Optional at publish time so a client that already knows its labels - the CLI publish
|
|
12598
|
+
* skill - can set them in the same call instead of a follow-up PATCH. */
|
|
12599
|
+
tags: PublishTagsSchema.optional(),
|
|
11671
12600
|
gatedToGroupId: z$1.string().optional(),
|
|
11672
12601
|
/** Who may annotate the published artifact. Defaults to `none` (read-only). */
|
|
11673
12602
|
commentPolicy: CommentPolicySchema.optional(),
|
|
@@ -11908,6 +12837,32 @@ function isModelAccessible(model, userTags, isAdmin = false, entitlementKeys = [
|
|
|
11908
12837
|
const normalizedAllowedEntitlements = (model.allowedEntitlements ?? []).map(normalizeEntitlementKey);
|
|
11909
12838
|
return normalizedKeys.some((key) => normalizedAllowedEntitlements.includes(key));
|
|
11910
12839
|
}
|
|
12840
|
+
[...OPENAI_IMAGE_MODELS, ...GEMINI_IMAGE_MODELS];
|
|
12841
|
+
/** Generation was cut off against the output-token ceiling. */
|
|
12842
|
+
const TRUNCATED_FINISH_REASON = "max_tokens";
|
|
12843
|
+
/**
|
|
12844
|
+
* We aborted the stream ourselves because it degenerated into repetition
|
|
12845
|
+
* (`DEGENERATE_STREAM_STOP_REASON` in `@bike4mind/llm-adapters`). Distinct from
|
|
12846
|
+
* `max_tokens` because the useful advice differs: telling a user to continue is actively
|
|
12847
|
+
* wrong here, since resuming from a degenerated tail tends to reproduce the loop.
|
|
12848
|
+
*/
|
|
12849
|
+
const DEGENERATE_FINISH_REASON = "degenerate_repetition";
|
|
12850
|
+
/**
|
|
12851
|
+
* Every reason meaning "this reply stopped early". Membership rather than equality with a
|
|
12852
|
+
* single literal, so a newly-added early-stop reason surfaces a notice automatically.
|
|
12853
|
+
*/
|
|
12854
|
+
const EARLY_STOP_FINISH_REASONS = /* @__PURE__ */ new Set([TRUNCATED_FINISH_REASON, DEGENERATE_FINISH_REASON]);
|
|
12855
|
+
/**
|
|
12856
|
+
* Whether a reply stopped early, given the reason reported for it.
|
|
12857
|
+
*
|
|
12858
|
+
* An ABSENT reason is NOT an early stop. Plenty of paths never report one (a backend whose
|
|
12859
|
+
* complete() leaves it unset, an older server, a non-terminal chunk), and treating silence
|
|
12860
|
+
* as truncation would cry wolf on every one of them. Only a reason we recognize as an
|
|
12861
|
+
* early stop counts - an unrecognized value is left alone rather than guessed at.
|
|
12862
|
+
*/
|
|
12863
|
+
function isEarlyStop(stopReason) {
|
|
12864
|
+
return !!stopReason && EARLY_STOP_FINISH_REASONS.has(stopReason);
|
|
12865
|
+
}
|
|
11911
12866
|
/**
|
|
11912
12867
|
* Trigger-word validation, shared between client form and server agent
|
|
11913
12868
|
* endpoints so the validation rules can't drift.
|
|
@@ -12517,9 +13472,9 @@ Array.from(new Set([
|
|
|
12517
13472
|
requiresAdmin: true
|
|
12518
13473
|
},
|
|
12519
13474
|
{
|
|
12520
|
-
id: "admin.
|
|
13475
|
+
id: "admin.feedback",
|
|
12521
13476
|
section: "admin",
|
|
12522
|
-
label: "
|
|
13477
|
+
label: "Feedback",
|
|
12523
13478
|
description: "View and manage user feedback, bug reports, and feature requests",
|
|
12524
13479
|
navigationType: "tab",
|
|
12525
13480
|
target: "2",
|
|
@@ -13540,7 +14495,7 @@ const CliConfigSchema = z$1.object({
|
|
|
13540
14495
|
}).optional(),
|
|
13541
14496
|
mcpServers: McpServersSchema,
|
|
13542
14497
|
preferences: z$1.object({
|
|
13543
|
-
maxTokens: z$1.number(),
|
|
14498
|
+
maxTokens: z$1.number().optional(),
|
|
13544
14499
|
temperature: z$1.number(),
|
|
13545
14500
|
autoSave: z$1.boolean(),
|
|
13546
14501
|
autoCompact: z$1.boolean().optional().prefault(true),
|
|
@@ -13635,10 +14590,27 @@ const ProjectLocalConfigSchema = z$1.object({
|
|
|
13635
14590
|
sandbox: PartialSandboxConfigSchema
|
|
13636
14591
|
});
|
|
13637
14592
|
/**
|
|
14593
|
+
* The output budget every pre-migration config was born with, back when
|
|
14594
|
+
* `preferences.maxTokens` was required and DEFAULT_CONFIG supplied this value. It is
|
|
14595
|
+
* indistinguishable from a user who deliberately typed 4096, and the migration reverts
|
|
14596
|
+
* it either way - acceptable only because the cleanup runs ONCE (see CONFIG_SCHEMA_VERSION):
|
|
14597
|
+
* a user who wanted 4096 sets it again and keeps it, while an install that never chose it
|
|
14598
|
+
* stops being capped by it. A value-keyed rule with no marker would instead make 4096
|
|
14599
|
+
* permanently unrepresentable, even though the /config select still offers it.
|
|
14600
|
+
*/
|
|
14601
|
+
const LEGACY_PINNED_MAX_TOKENS = 4096;
|
|
14602
|
+
/**
|
|
14603
|
+
* Schema version of the on-disk config, and the marker that makes the migrations in
|
|
14604
|
+
* `load()` one-time. A file stamped with anything else gets the upgrade pass and is
|
|
14605
|
+
* rewritten at the current version; a file already at it is left alone. Bump this when
|
|
14606
|
+
* adding a migration, and gate the new step on the version it needs to run from.
|
|
14607
|
+
*/
|
|
14608
|
+
const CONFIG_SCHEMA_VERSION = "0.2.0";
|
|
14609
|
+
/**
|
|
13638
14610
|
* Default configuration
|
|
13639
14611
|
*/
|
|
13640
14612
|
const DEFAULT_CONFIG = {
|
|
13641
|
-
version:
|
|
14613
|
+
version: CONFIG_SCHEMA_VERSION,
|
|
13642
14614
|
userId: v4(),
|
|
13643
14615
|
defaultModel: ChatModels.CLAUDE_4_5_SONNET,
|
|
13644
14616
|
toolApiKeys: {
|
|
@@ -13647,7 +14619,6 @@ const DEFAULT_CONFIG = {
|
|
|
13647
14619
|
},
|
|
13648
14620
|
mcpServers: [],
|
|
13649
14621
|
preferences: {
|
|
13650
|
-
maxTokens: 4096,
|
|
13651
14622
|
temperature: .7,
|
|
13652
14623
|
autoSave: true,
|
|
13653
14624
|
autoCompact: true,
|
|
@@ -13927,6 +14898,13 @@ var ConfigStore = class {
|
|
|
13927
14898
|
if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
|
|
13928
14899
|
else rawConfig.apiConfig = {};
|
|
13929
14900
|
}
|
|
14901
|
+
if (rawConfig.version !== CONFIG_SCHEMA_VERSION) {
|
|
14902
|
+
if (rawConfig.preferences?.maxTokens === LEGACY_PINNED_MAX_TOKENS) delete rawConfig.preferences.maxTokens;
|
|
14903
|
+
rawConfig.version = CONFIG_SCHEMA_VERSION;
|
|
14904
|
+
try {
|
|
14905
|
+
await promises.writeFile(this.configPath, JSON.stringify(rawConfig, null, 2), "utf-8");
|
|
14906
|
+
} catch {}
|
|
14907
|
+
}
|
|
13930
14908
|
const validated = CliConfigSchema.parse(rawConfig);
|
|
13931
14909
|
const normalizedMcpServers = normalizeMcpServers(validated.mcpServers);
|
|
13932
14910
|
globalConfig = {
|
|
@@ -14357,4 +15335,4 @@ var ConfigStore = class {
|
|
|
14357
15335
|
}
|
|
14358
15336
|
};
|
|
14359
15337
|
//#endregion
|
|
14360
|
-
export {
|
|
15338
|
+
export { VoyageAIEmbeddingModel as $, HTTPError as A, resolveHistoryFetchLimit as At, PermissionDeniedError as B, isNearLimit as Bt, DEFAULT_MUSIC_MODEL_ID as C, isRetryableError as Ct, FIXED_TEMPERATURE_MODELS as D, isZodError as Dt, FIELD_GROUP_OF as E, isUserInitiatedAbort as Et, ModelBackend as F, usdToCredits as Ft, SpeechToTextModels as G, REASONING_SUPPORTED_MODELS as H, NO_TEMPERATURE_MODELS as I, usdToCreditsStochastic as It, TooManyRequestsError as J, SupportedFabFileMimeTypes as K, NotFoundError as L, withRetry as Lt, ImageModels as M, settingsMap as Mt, InternalServerError as N, toModelInfo as Nt, FORMAT_PROMPT_TEMPLATE as O, mapMimeTypeToArtifactType as Ot, MODEL_INFO_FIELD_GROUP_OF as P, toModelRecord as Pt, VideoModels as Q, OllamaEmbeddingModel as R, buildRateLimitLogEntry as Rt, CorruptedFileError as S, isRenderableModelType as St, DEGENERATE_FINISH_REASON as T, isUnlimitedHistory as Tt, REFUSAL_FALLBACK_MODELS as U, REASONING_EFFORT_INCOMPATIBLE_WITH_TOOLS_MODELS as V, parseRateLimitHeaders as Vt, RESPONSES_API_TOOL_MODELS as W, UnprocessableEntityError as X, UnauthorizedError as Y, VIDEO_SIZE_CONSTRAINTS as Z, BadRequestError as _, isImageServeable as _t, getCreditsUrl as a, getMcpProviderMetadata as at, CREDIT_DEDUCT_TRANSACTION_TYPES as b, isModelDeprecated as bt, requireApiUrl as c, isAudioMimeType as ct, AGENT_QUEST_MANIFEST as d, isEarlyStop as dt, WORK_ITEM_STATUSES as et, AGENT_QUEST_MCP_URI as f, isFieldGroup as ft, BFL_SAFETY_TOLERANCE as g, isImageAttachment as gt, BEDROCK_NO_PROMPT_CACHING_MODELS as h, isGeminiModelId as ht, LOCAL_DEV_URL as i, defaultEmbeddingModelForEnv as it, HttpStatus as j, secureParameters as jt, ForbiddenError as k, obfuscateApiKey as kt, resolveApiEndpoint as l, isChunkRebuildPending as lt, ApiKeyType as m, isGPTImageModel as mt, logger as n, calculateRetryDelay as nt, getEnvironmentName as o, getQuestErrorCode as ot, ARTIFACT_ATTRS_PATTERN as p, isGPTImage2Model as pt, TTS_MAX_INPUT_CHARS as q, ApiEndpointUnconfiguredError as r, dayjsConfig_default as rt, parseApiUrl as s, getRetryAfterMs as st, ConfigStore as t, applyModelPriceCatalog as tt, AGENT_QUEST_ID as u, isConvergencePausedNote as ut, BedrockEmbeddingModel as v, isMediaModelType as vt, DEFAULT_UNKNOWN_CONTEXT_WINDOW as w, isSupportedFabFileMimeType as wt, ChatModels as x, isPlaceholderApiKey as xt, CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS as y, isModelAccessible as yt, OpenAIEmbeddingModel as z, extractSnippetMeta as zt };
|