@bike4mind/cli 0.20.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as WORK_ITEM_STATUSES, A as HttpStatus, At as usdToCredits, B as REASONING_EFFORT_INCOMPATIBLE_WITH_TOOLS_MODELS, Ct as mapMimeTypeToArtifactType, D as FORMAT_PROMPT_TEMPLATE, Dt as settingsMap, E as FIXED_TEMPERATURE_MODELS, Et as secureParameters, F as NO_TEMPERATURE_MODELS, Ft as isNearLimit, G as SupportedFabFileMimeTypes, H as REFUSAL_FALLBACK_MODELS, I as NotFoundError, It as parseRateLimitHeaders, J as UnauthorizedError, K as TTS_MAX_INPUT_CHARS, L as OllamaEmbeddingModel, M as InternalServerError, Mt as withRetry, N as MODEL_INFO_FIELD_GROUP_OF, Nt as buildRateLimitLogEntry, O as ForbiddenError, Ot as toModelInfo, P as ModelBackend, Pt as extractSnippetMeta, Q as VoyageAIEmbeddingModel, R as OpenAIEmbeddingModel, S as CorruptedFileError, St as isZodError, T as FIELD_GROUP_OF, Tt as resolveHistoryFetchLimit, U as RESPONSES_API_TOOL_MODELS, V as REASONING_SUPPORTED_MODELS, W as SpeechToTextModels, X as VIDEO_SIZE_CONSTRAINTS, Y as UnprocessableEntityError, Z as VideoModels, _ as BadRequestError, _t as isRenderableModelType, at as getQuestErrorCode, bt as isUnlimitedHistory, ct as isFieldGroup, dt as isImageAttachment, et as applyModelPriceCatalog, ft as isImageServeable, g as BFL_SAFETY_TOLERANCE, gt as isPlaceholderApiKey, h as BEDROCK_NO_PROMPT_CACHING_MODELS, ht as isModelDeprecated, it as getMcpProviderMetadata, j as ImageModels, jt as usdToCreditsStochastic, k as HTTPError, kt as toModelRecord, lt as isGPTImage2Model, m as ApiKeyType, mt as isModelAccessible, n as logger, nt as dayjsConfig_default, ot as getRetryAfterMs, p as ARTIFACT_ATTRS_PATTERN, pt as isMediaModelType, q as TooManyRequestsError, rt as defaultEmbeddingModelForEnv, st as isAudioMimeType, tt as calculateRetryDelay, ut as isGPTImageModel, v as BedrockEmbeddingModel, vt as isRetryableError, w as DEFAULT_UNKNOWN_CONTEXT_WINDOW, wt as obfuscateApiKey, x as ChatModels, xt as isUserInitiatedAbort, y as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, yt as isSupportedFabFileMimeType, z as PermissionDeniedError } from "./ConfigStore-DD3DcC3-.mjs";
2
+ import { $ as WORK_ITEM_STATUSES, A as HttpStatus, At as usdToCredits, B as REASONING_EFFORT_INCOMPATIBLE_WITH_TOOLS_MODELS, Ct as mapMimeTypeToArtifactType, D as FORMAT_PROMPT_TEMPLATE, Dt as settingsMap, E as FIXED_TEMPERATURE_MODELS, Et as secureParameters, F as NO_TEMPERATURE_MODELS, Ft as isNearLimit, G as SupportedFabFileMimeTypes, H as REFUSAL_FALLBACK_MODELS, I as NotFoundError, It as parseRateLimitHeaders, J as UnauthorizedError, K as TTS_MAX_INPUT_CHARS, L as OllamaEmbeddingModel, M as InternalServerError, Mt as withRetry, N as MODEL_INFO_FIELD_GROUP_OF, Nt as buildRateLimitLogEntry, O as ForbiddenError, Ot as toModelInfo, P as ModelBackend, Pt as extractSnippetMeta, Q as VoyageAIEmbeddingModel, R as OpenAIEmbeddingModel, S as CorruptedFileError, St as isZodError, T as FIELD_GROUP_OF, Tt as resolveHistoryFetchLimit, U as RESPONSES_API_TOOL_MODELS, V as REASONING_SUPPORTED_MODELS, W as SpeechToTextModels, X as VIDEO_SIZE_CONSTRAINTS, Y as UnprocessableEntityError, Z as VideoModels, _ as BadRequestError, _t as isRenderableModelType, at as getQuestErrorCode, bt as isUnlimitedHistory, ct as isFieldGroup, dt as isImageAttachment, et as applyModelPriceCatalog, ft as isImageServeable, g as BFL_SAFETY_TOLERANCE, gt as isPlaceholderApiKey, h as BEDROCK_NO_PROMPT_CACHING_MODELS, ht as isModelDeprecated, it as getMcpProviderMetadata, j as ImageModels, jt as usdToCreditsStochastic, k as HTTPError, kt as toModelRecord, lt as isGPTImage2Model, m as ApiKeyType, mt as isModelAccessible, n as logger, nt as dayjsConfig_default, ot as getRetryAfterMs, p as ARTIFACT_ATTRS_PATTERN, pt as isMediaModelType, q as TooManyRequestsError, rt as defaultEmbeddingModelForEnv, st as isAudioMimeType, tt as calculateRetryDelay, ut as isGPTImageModel, v as BedrockEmbeddingModel, vt as isRetryableError, w as DEFAULT_UNKNOWN_CONTEXT_WINDOW, wt as obfuscateApiKey, x as ChatModels, xt as isUserInitiatedAbort, y as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, yt as isSupportedFabFileMimeType, z as PermissionDeniedError } from "./ConfigStore-CNfbeaJf.mjs";
3
3
  import { n as isPathAllowed, t as assertPathAllowed } from "./pathValidation-D8tjkQXE-1HwvsuYT.mjs";
4
4
  import { n as isTerminalShellStatus, t as getShellSessionManager } from "./ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs";
5
5
  import { execFile, execFileSync, spawn } from "child_process";
@@ -2044,7 +2044,7 @@ const webSearchTool = {
2044
2044
  })
2045
2045
  };
2046
2046
  //#endregion
2047
- //#region ../../b4m-core/services/dist/toolGenerators-CBGJMey9.mjs
2047
+ //#region ../../b4m-core/services/dist/toolGenerators-BVUZiwqE.mjs
2048
2048
  const diceRoll = async (parameters) => {
2049
2049
  if (!parameters?.sides || !parameters?.times) throw new Error("Tool dice roll: Missing required parameters");
2050
2050
  return sum(times(parameters.times, () => random(1, parameters.sides))).toString();
@@ -2731,7 +2731,7 @@ const askUserQuestionTool = {
2731
2731
  * re-export them without pulling the full tool graph. `index.ts` re-exports them
2732
2732
  * so the server barrel's public API is unchanged.
2733
2733
  */
2734
- const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools, allowedDirectories, entitlementKeys = [], sessionId, codeMinifier, availableModels, onToolLlmUsage) => {
2734
+ const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope, inlinedAttachmentIds, fullyInlinedAttachmentIds }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools, allowedDirectories, entitlementKeys = [], sessionId, codeMinifier, availableModels, onToolLlmUsage) => {
2735
2735
  const context = {
2736
2736
  userId,
2737
2737
  user,
@@ -2750,6 +2750,8 @@ const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope }, s
2750
2750
  entitlementKeys,
2751
2751
  retrievalFilter,
2752
2752
  kbScope,
2753
+ inlinedAttachmentIds,
2754
+ fullyInlinedAttachmentIds,
2753
2755
  codeMinifier,
2754
2756
  availableModels,
2755
2757
  onToolLlmUsage
@@ -7000,6 +7002,41 @@ const ChunkSchema = z$1.object({
7000
7002
  const isEmbeddingModel = (model, modelEnum) => {
7001
7003
  return Object.values(modelEnum).includes(model);
7002
7004
  };
7005
+ /**
7006
+ * The embedding model's context window in tokens - the HARD ceiling an embedding call accepts.
7007
+ * Extracted from SmartChunker so a caller (the chunk queue handler, #1662) can compute the same
7008
+ * effective passage limit for owner-altitude policy resolution and cross-lake conflict reporting
7009
+ * without constructing a chunker. Throws on an unsupported model, exactly as the chunker does.
7010
+ */
7011
+ function embeddingModelContextWindow(model) {
7012
+ if (isEmbeddingModel(model, OpenAIEmbeddingModel)) return OPENAI_EMBEDDING_MODEL_MAP[model].contextWindow;
7013
+ if (isEmbeddingModel(model, VoyageAIEmbeddingModel)) return VOYAGEAI_EMBEDDING_MODEL_MAP[model].contextWindow;
7014
+ if (isEmbeddingModel(model, BedrockEmbeddingModel)) return BEDROCK_EMBEDDING_MODEL_MAP[model].contextWindow;
7015
+ if (isEmbeddingModel(model, OllamaEmbeddingModel)) return OLLAMA_EMBEDDING_MODEL_MAP[model].contextWindow;
7016
+ throw new Error(`Unsupported embedding model: ${model}`);
7017
+ }
7018
+ /**
7019
+ * The buffer subtracted from the model window to absorb cross-provider tokenizer differences
7020
+ * (char/4 approximations can undercount by ~8-10% vs tiktoken). A value < 1 is a percent of the
7021
+ * window (floored to >= 32 tokens); a value >= 1 is an absolute token count.
7022
+ */
7023
+ function embeddingWindowBuffer(maxTokens, bufferPercentOrValue = .2) {
7024
+ return bufferPercentOrValue < 1 ? Math.max(Math.floor(maxTokens * bufferPercentOrValue), 32) : Math.floor(bufferPercentOrValue);
7025
+ }
7026
+ /**
7027
+ * The effective per-chunk token limit the chunker will actually use: the SOFT passage target
7028
+ * (retrieval granularity) hard-capped to the buffered model window (an oversized chunk fails the
7029
+ * embedding call). THE single source of truth for that clamp - SmartChunker's constructor and the
7030
+ * chunk handler's conflict/observability logic (#1662) both derive from it, so a resolved policy
7031
+ * value can never drift from the granularity it actually produces. An omitted/invalid target falls
7032
+ * back to DEFAULT_PASSAGE_TOKEN_TARGET; a supplied one is floored to MIN_PASSAGE_TOKEN_TARGET.
7033
+ */
7034
+ function effectiveChunkTokenLimit(opts) {
7035
+ const maxTokens = embeddingModelContextWindow(opts.model);
7036
+ const hardLimit = maxTokens - embeddingWindowBuffer(maxTokens, opts.bufferPercentOrValue ?? .2);
7037
+ const { passageTokenTarget } = opts;
7038
+ return Math.min(hardLimit, passageTokenTarget !== void 0 && Number.isFinite(passageTokenTarget) && passageTokenTarget > 0 ? Math.max(Math.floor(passageTokenTarget), 64) : 512);
7039
+ }
7003
7040
  var SmartChunker = class {
7004
7041
  logger;
7005
7042
  model;
@@ -7023,25 +7060,20 @@ var SmartChunker = class {
7023
7060
  passageTokenTarget: void 0
7024
7061
  } : options ?? {};
7025
7062
  this.model = model;
7026
- if (isEmbeddingModel(model, OpenAIEmbeddingModel)) this.maxTokens = OPENAI_EMBEDDING_MODEL_MAP[model].contextWindow;
7027
- else if (isEmbeddingModel(model, VoyageAIEmbeddingModel)) this.maxTokens = VOYAGEAI_EMBEDDING_MODEL_MAP[model].contextWindow;
7028
- else if (isEmbeddingModel(model, BedrockEmbeddingModel)) this.maxTokens = BEDROCK_EMBEDDING_MODEL_MAP[model].contextWindow;
7029
- else if (isEmbeddingModel(model, OllamaEmbeddingModel)) this.maxTokens = OLLAMA_EMBEDDING_MODEL_MAP[model].contextWindow;
7030
- else throw new Error(`Unsupported embedding model: ${model}`);
7063
+ this.maxTokens = embeddingModelContextWindow(model);
7031
7064
  this.bufferPercentOrValue = bufferPercentOrValue ?? .2;
7032
- let buffer;
7033
- if (this.bufferPercentOrValue < 1) buffer = Math.max(Math.floor(this.maxTokens * this.bufferPercentOrValue), 32);
7034
- else buffer = Math.floor(this.bufferPercentOrValue);
7035
- const hardLimit = this.maxTokens - buffer;
7036
- const target = passageTokenTarget !== void 0 && Number.isFinite(passageTokenTarget) && passageTokenTarget > 0 ? Math.max(Math.floor(passageTokenTarget), 64) : 512;
7037
- this.chunkTokenLimit = Math.min(hardLimit, target);
7065
+ this.chunkTokenLimit = effectiveChunkTokenLimit({
7066
+ model,
7067
+ passageTokenTarget,
7068
+ bufferPercentOrValue: this.bufferPercentOrValue
7069
+ });
7038
7070
  this.storage = storage;
7039
7071
  this.logger.updateMetadata({
7040
7072
  model,
7041
7073
  maxTokens: this.maxTokens,
7042
7074
  chunkTokenLimit: this.chunkTokenLimit,
7043
7075
  bufferPercentOrValue: this.bufferPercentOrValue,
7044
- passageTokenTarget: target
7076
+ passageTokenTarget
7045
7077
  });
7046
7078
  }
7047
7079
  async initializeEncoder() {
@@ -28418,7 +28450,125 @@ var AdminSettingsCache = class AdminSettingsCache {
28418
28450
  this.logger.info("🛑 Admin settings cache shutdown complete");
28419
28451
  }
28420
28452
  };
28453
+ /** Address of one cached override, shared by the cache and its callers so lookups are consistent. */
28454
+ function scopedOverrideKey(scopeLevel, scopeId, settingName) {
28455
+ return JSON.stringify([
28456
+ scopeLevel,
28457
+ scopeId,
28458
+ settingName
28459
+ ]);
28460
+ }
28461
+ function scopeKey(scopeLevel, scopeId) {
28462
+ return JSON.stringify([scopeLevel, scopeId]);
28463
+ }
28464
+ /**
28465
+ * In-memory cache for scoped setting OVERRIDES, sibling to `AdminSettingsCache` (which caches the
28466
+ * flat platform table). Unlike the platform cache it cannot hold one "all settings" blob - overrides
28467
+ * are per (rung, setting) and can be many (a value per lake), so each address is cached individually
28468
+ * and negatively (a confirmed absence is cached too, so an un-overridden setting does not re-query
28469
+ * every resolve).
28470
+ *
28471
+ * Entries are nested one level per rung address so a whole rung invalidates in one delete (the shape
28472
+ * a future scoped-override writer needs). The cache key carries the scope rung, which is the whole
28473
+ * reason this is separate from `AdminSettingsCache`: that one keys by bare `settingName`, so scoped
28474
+ * values would collide there. See #1660.
28475
+ */
28476
+ var ScopedSettingsCache = class ScopedSettingsCache {
28477
+ cache = /* @__PURE__ */ new Map();
28478
+ logger;
28479
+ maxScopes = 5e3;
28480
+ static DEFAULT_TTL = 3e5;
28481
+ static DEVELOPMENT_TTL = 3e4;
28482
+ constructor(logger) {
28483
+ this.logger = logger;
28484
+ }
28485
+ getTTL() {
28486
+ return process.env.NODE_ENV === "development" ? ScopedSettingsCache.DEVELOPMENT_TTL : ScopedSettingsCache.DEFAULT_TTL;
28487
+ }
28488
+ isValid(entry) {
28489
+ return Date.now() - entry.timestamp < entry.ttl;
28490
+ }
28491
+ /**
28492
+ * Resolve override values for the given rungs and setting names, reading through the cache. Returns
28493
+ * a map keyed by `scopedOverrideKey(level,id,name)` -> value|null for EVERY (scope, name) pair
28494
+ * requested, so the resolver can look up any rung deterministically. One DB query covers all misses.
28495
+ */
28496
+ async getOverrides(scopes, settingNames, db) {
28497
+ const out = /* @__PURE__ */ new Map();
28498
+ if (scopes.length === 0 || settingNames.length === 0) return out;
28499
+ const missingScopeRefs = /* @__PURE__ */ new Map();
28500
+ const missingNames = /* @__PURE__ */ new Set();
28501
+ for (const scope of scopes) {
28502
+ const inner = this.cache.get(scopeKey(scope.scopeLevel, scope.scopeId));
28503
+ for (const name of settingNames) {
28504
+ const outKey = scopedOverrideKey(scope.scopeLevel, scope.scopeId, name);
28505
+ const cached = inner?.get(name);
28506
+ if (cached && this.isValid(cached)) out.set(outKey, cached.value);
28507
+ else {
28508
+ if (cached) inner.delete(name);
28509
+ out.set(outKey, null);
28510
+ missingScopeRefs.set(scopeKey(scope.scopeLevel, scope.scopeId), scope);
28511
+ missingNames.add(name);
28512
+ }
28513
+ }
28514
+ }
28515
+ if (missingScopeRefs.size === 0) return out;
28516
+ const rows = await db.scopedSettings.findOverrides(Array.from(missingScopeRefs.values()), Array.from(missingNames));
28517
+ const ttl = this.getTTL();
28518
+ const now = Date.now();
28519
+ const found = /* @__PURE__ */ new Set();
28520
+ for (const row of rows) {
28521
+ out.set(scopedOverrideKey(row.scopeLevel, row.scopeId, row.settingName), row.settingValue);
28522
+ this.put(row.scopeLevel, row.scopeId, row.settingName, row.settingValue, now, ttl);
28523
+ found.add(scopedOverrideKey(row.scopeLevel, row.scopeId, row.settingName));
28524
+ }
28525
+ for (const scope of missingScopeRefs.values()) for (const name of missingNames) if (!found.has(scopedOverrideKey(scope.scopeLevel, scope.scopeId, name))) this.put(scope.scopeLevel, scope.scopeId, name, null, now, ttl);
28526
+ if (this.cache.size > this.maxScopes) this.evictOldestScopes();
28527
+ return out;
28528
+ }
28529
+ put(scopeLevel, scopeId, name, value, now, ttl) {
28530
+ const sk = scopeKey(scopeLevel, scopeId);
28531
+ let inner = this.cache.get(sk);
28532
+ if (!inner) {
28533
+ inner = /* @__PURE__ */ new Map();
28534
+ this.cache.set(sk, inner);
28535
+ }
28536
+ inner.set(name, {
28537
+ value,
28538
+ timestamp: now,
28539
+ ttl
28540
+ });
28541
+ }
28542
+ evictOldestScopes() {
28543
+ const freshness = (inner) => Math.max(...Array.from(inner.values()).map((e) => e.timestamp));
28544
+ const scopesByAge = Array.from(this.cache.entries()).sort((a, b) => freshness(a[1]) - freshness(b[1]));
28545
+ const toRemove = this.cache.size - this.maxScopes;
28546
+ for (let i = 0; i < toRemove; i++) this.cache.delete(scopesByAge[i][0]);
28547
+ this.logger.warn(`ScopedSettingsCache evicted ${toRemove} oldest rungs (size limit)`);
28548
+ }
28549
+ /** Invalidate every cached value for one rung address (call this from a future scoped-override writer). */
28550
+ invalidateScope(scopeLevel, scopeId) {
28551
+ this.cache.delete(scopeKey(scopeLevel, scopeId));
28552
+ }
28553
+ invalidateAll() {
28554
+ this.cache.clear();
28555
+ }
28556
+ /** Snapshot for monitoring, parity with AdminSettingsCache.getStats(). */
28557
+ getStats() {
28558
+ let entries = 0;
28559
+ for (const inner of this.cache.values()) entries += inner.size;
28560
+ return {
28561
+ scopes: this.cache.size,
28562
+ entries
28563
+ };
28564
+ }
28565
+ /** Graceful shutdown - no timers to stop (unlike AdminSettingsCache), just drop the map. */
28566
+ shutdown() {
28567
+ this.cache.clear();
28568
+ }
28569
+ };
28421
28570
  let globalSettingsCache = null;
28571
+ let globalScopedSettingsCache = null;
28422
28572
  /**
28423
28573
  * Get or create the global settings cache instance
28424
28574
  */
@@ -28426,6 +28576,32 @@ function getSettingsCache(logger) {
28426
28576
  if (!globalSettingsCache) globalSettingsCache = new AdminSettingsCache(logger || new Logger());
28427
28577
  return globalSettingsCache;
28428
28578
  }
28579
+ function getScopedSettingsCache(logger) {
28580
+ if (!globalScopedSettingsCache) globalScopedSettingsCache = new ScopedSettingsCache(logger || new Logger());
28581
+ return globalScopedSettingsCache;
28582
+ }
28583
+ /**
28584
+ * Read org/owner/lake OVERRIDE values for a set of rungs and setting names, through the scoped cache.
28585
+ * Returns a map keyed by `scopedOverrideKey(level,id,name)` -> value|null (see ScopedSettingsCache).
28586
+ * Platform values are NOT here - the resolver layers these over the platform read. Passing no rungs
28587
+ * (a platform-altitude resolve) returns an empty map and touches neither cache nor DB.
28588
+ */
28589
+ async function getScopedOverrides(scopes, settingNames, db, options) {
28590
+ return getScopedSettingsCache(options?.logger).getOverrides(scopes, settingNames, db);
28591
+ }
28592
+ /**
28593
+ * Invalidate cached overrides for one rung address, or all of them. Call from a scoped-override writer.
28594
+ *
28595
+ * In-process only: this clears the calling process's map. With negative caching and the 5-minute TTL,
28596
+ * a newly-written override can stay invisible on other Lambda/container instances for up to one TTL.
28597
+ * That is acceptable while no writer ships (this PR), but a future write path needs a cross-instance
28598
+ * invalidation story (shared cache or short TTL) rather than relying on this alone.
28599
+ */
28600
+ function invalidateScopedSettingsCache(scope) {
28601
+ if (!globalScopedSettingsCache) return;
28602
+ if (scope) globalScopedSettingsCache.invalidateScope(scope.scopeLevel, scope.scopeId);
28603
+ else globalScopedSettingsCache.invalidateAll();
28604
+ }
28429
28605
  function getSettingsValue(key, settings, defaultValue) {
28430
28606
  const settingConfig = settingsMap[key];
28431
28607
  if (!settingConfig) {
@@ -28478,7 +28654,7 @@ async function getSettingsByNames(settingNames, db, options) {
28478
28654
  const allSettings = await getSettingsCache(logger).getSettingsMap(db);
28479
28655
  const result = {};
28480
28656
  settingNames.forEach((name) => {
28481
- result[name] = allSettings[name] || null;
28657
+ result[name] = allSettings[name] ?? null;
28482
28658
  });
28483
28659
  return result;
28484
28660
  }
@@ -28503,6 +28679,10 @@ function shutdownSettingsCache() {
28503
28679
  globalSettingsCache.shutdown();
28504
28680
  globalSettingsCache = null;
28505
28681
  }
28682
+ if (globalScopedSettingsCache) {
28683
+ globalScopedSettingsCache.shutdown();
28684
+ globalScopedSettingsCache = null;
28685
+ }
28506
28686
  }
28507
28687
  /**
28508
28688
  * Get cache statistics for monitoring
@@ -28860,6 +29040,12 @@ const EMBEDDING_TOKEN_LIMITS = {
28860
29040
  */
28861
29041
  const KNOWLEDGE_FILE_TOKEN_ALLOCATION = .7;
28862
29042
  /**
29043
+ * The two always-on blocks this function injects itself, downstream of the caller's own
29044
+ * systemPromptDetails assembly - so a caller reporting per-source token telemetry (see
29045
+ * ChatCompletionProcess) cannot see them without reading this list back off the return value.
29046
+ */
29047
+ const BUILDER_INJECTED_BLOCK_IDS = ["formatPrompt", "imagePrompt"];
29048
+ /**
28863
29049
  * Rounds the final safety pass may spend shrinking the payload. It has to re-measure between rounds
28864
29050
  * (see the pass for why one shot overshoots), and each round costs two real tokenizer calls, so this
28865
29051
  * bounds the work. Converges in one or two rounds in practice.
@@ -29194,6 +29380,7 @@ async function fetchAndProcessPreviousMessages(session, historyCount = null, { d
29194
29380
  });
29195
29381
  }
29196
29382
  }
29383
+ const priorToolNames = chatHistoryItems.flatMap((item) => (item.promptMeta?.functionCalls ?? []).map((fc) => fc.name)).filter((name) => Boolean(name));
29197
29384
  return [
29198
29385
  convertedMessages,
29199
29386
  chatHistoryItems.length,
@@ -29203,7 +29390,8 @@ async function fetchAndProcessPreviousMessages(session, historyCount = null, { d
29203
29390
  itemCount: chatHistoryItems.length,
29204
29391
  oldestIncludedQuestId,
29205
29392
  excludedOlderQuestCount,
29206
- recentGeneratedImages
29393
+ recentGeneratedImages,
29394
+ priorToolNames
29207
29395
  }
29208
29396
  ];
29209
29397
  }
@@ -29495,12 +29683,16 @@ const noopResize = async (imageBuffer) => imageBuffer;
29495
29683
  async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, attachedContentTokenBudget, modelInfo, sendStatusUpdate, { logger, storage, db, resizeImageForModel = noopResize }, progressCallback) {
29496
29684
  if (!fabFiles || fabFiles.length === 0) return {
29497
29685
  userMessages: [],
29498
- errorMessages: []
29686
+ errorMessages: [],
29687
+ deliveredFileIds: [],
29688
+ fullyDeliveredFileIds: []
29499
29689
  };
29500
29690
  const fileProcessingStartTime = Date.now();
29501
29691
  let systemContent = "";
29502
29692
  const userMessages = [];
29503
29693
  const errorMessages = [];
29694
+ const deliveredFileIds = /* @__PURE__ */ new Set();
29695
+ const fullyDeliveredFileIds = /* @__PURE__ */ new Set();
29504
29696
  const contextFiles = [];
29505
29697
  const supportsVision = modelInfo?.supportsVision ?? false;
29506
29698
  if (fabFiles.length > 0) sendStatusUpdate("Munching attached files...");
@@ -29524,6 +29716,8 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29524
29716
  const maxTokens = Math.max(1, Math.floor(attachedContentTokenBudget / textFileCount));
29525
29717
  const fileContentCache = /* @__PURE__ */ new Map();
29526
29718
  const processFileInParallel = async (file) => {
29719
+ let delivered = false;
29720
+ let fullyDelivered = false;
29527
29721
  try {
29528
29722
  if (isAudioMimeType(file.mimeType)) {
29529
29723
  logger.warn(`[processFabFilesServer] Skipping audio file ${file.fileName} — audio is not attachable to an LLM.`);
@@ -29552,6 +29746,8 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29552
29746
  type: "text",
29553
29747
  text: `Image URL: ${fileUrl}\nFile: "${file.fileName}" (fabFileId: ${file.id})\nWhen referencing this file, use the exact filename "${file.fileName}" — do not rename based on image content.`
29554
29748
  });
29749
+ delivered = true;
29750
+ fullyDelivered = true;
29555
29751
  break;
29556
29752
  }
29557
29753
  case ModelBackend.Anthropic:
@@ -29586,6 +29782,8 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29586
29782
  type: "text",
29587
29783
  text: `Image URL: ${fileUrl}\nFile: "${file.fileName}" (fabFileId: ${file.id})\nWhen referencing this file, use the exact filename "${file.fileName}" — do not rename based on image content.`
29588
29784
  });
29785
+ delivered = true;
29786
+ fullyDelivered = true;
29589
29787
  } else if (modelInfo.id.startsWith("moonshot")) {
29590
29788
  const moonshotBuffer = await resizeImageForModel(await storage.download(file.filePath), void 0, logger);
29591
29789
  const { mime: moonshotMimeType } = await getFileType(moonshotBuffer, file.fileName, file.mimeType);
@@ -29609,6 +29807,8 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29609
29807
  type: "text",
29610
29808
  text: `Image URL: ${fileUrl}\nFile: "${file.fileName}" (fabFileId: ${file.id})\nWhen referencing this file, use the exact filename "${file.fileName}" — do not rename based on image content.`
29611
29809
  });
29810
+ delivered = true;
29811
+ fullyDelivered = true;
29612
29812
  } else logger.warn(`Vision support for the model ${modelInfo.id} is not implemented. Skipping image processing.`);
29613
29813
  break;
29614
29814
  case ModelBackend.Ollama: {
@@ -29634,6 +29834,8 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29634
29834
  type: "text",
29635
29835
  text: `Image URL: ${fileUrl}\nFile: "${file.fileName}" (fabFileId: ${file.id})\nWhen referencing this file, use the exact filename "${file.fileName}". Do not rename based on image content.`
29636
29836
  });
29837
+ delivered = true;
29838
+ fullyDelivered = true;
29637
29839
  break;
29638
29840
  }
29639
29841
  default: logger.error(`Unsupported backend for model ${modelInfo.id} backend ${modelInfo?.backend ?? "undefined"}`);
@@ -29680,7 +29882,9 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29680
29882
  }
29681
29883
  if (truncatedResults.length > 0) {
29682
29884
  deliveredViaCosine = true;
29885
+ delivered = true;
29683
29886
  const deliveredEveryChunk = !scanTruncated && totalChunks === truncatedResults.length;
29887
+ fullyDelivered = deliveredEveryChunk && !anyChunkCut;
29684
29888
  let notice = "";
29685
29889
  if (!deliveredEveryChunk) notice = excerptNotice(file.fileName);
29686
29890
  else if (anyChunkCut) notice = CONTENT_TRUNCATION_NOTICE;
@@ -29707,6 +29911,7 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29707
29911
  const finalMaxFileSize = maxTokens > 0 ? maxSizeBasedonMaxTokens : MAX_FILE_SIZE;
29708
29912
  logger.log(`[processFabFilesServer] Final max file size: ${finalMaxFileSize}`);
29709
29913
  sendStatusUpdate("Adding file content to prompt...");
29914
+ fullyDelivered = fabContent.length <= finalMaxFileSize;
29710
29915
  if (fabContent.length > finalMaxFileSize) {
29711
29916
  await sendStatusUpdate("File is too large, truncating...");
29712
29917
  const originalFileSize = fabContent.length;
@@ -29717,6 +29922,7 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29717
29922
  content: errorMsg
29718
29923
  });
29719
29924
  } else errorMsg = null;
29925
+ delivered = true;
29720
29926
  if (file.system) systemContent += fabContent;
29721
29927
  else contextFiles.push({
29722
29928
  fileName: file.fileName,
@@ -29746,6 +29952,8 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29746
29952
  }
29747
29953
  }
29748
29954
  }
29955
+ if (delivered) deliveredFileIds.add(file.id);
29956
+ if (fullyDelivered) fullyDeliveredFileIds.add(file.id);
29749
29957
  } catch (error) {
29750
29958
  logger.updateMetadata({ fileId: file.id });
29751
29959
  logger.error(`🕐 [processFabFilesServer] Error processing file ${file.fileName}: ${error}`);
@@ -29784,7 +29992,9 @@ async function processFabFilesServer(embeddingFactory, fabFiles, userPrompt, att
29784
29992
  logger.info(`📁 File processing completed in ${fileProcessingTime}ms for ${fabFiles.length} files`);
29785
29993
  return {
29786
29994
  userMessages,
29787
- errorMessages
29995
+ errorMessages,
29996
+ deliveredFileIds: Array.from(deliveredFileIds),
29997
+ fullyDeliveredFileIds: Array.from(fullyDeliveredFileIds)
29788
29998
  };
29789
29999
  }
29790
30000
  /**
@@ -29950,7 +30160,11 @@ const processMessages = (messages, tokenBudget, { truncationNotice } = {}) => {
29950
30160
  async function buildAndSortMessages(previousMessages, fabMessages, userPrompt, maxInputTokens, settings, historyCount = 0, logger, tokenizer, options = { verbose: false }) {
29951
30161
  if (!(maxInputTokens > 0)) {
29952
30162
  logger.error(`Invalid maxInputTokens: ${maxInputTokens}. Must be greater than 0.`);
29953
- return [];
30163
+ return {
30164
+ messages: [],
30165
+ messageTruncation: null,
30166
+ injectedBlocks: []
30167
+ };
29954
30168
  }
29955
30169
  if (process.env.VERBOSE_CHAT_CONTEXT !== "false") {
29956
30170
  if (options.verbose) {
@@ -29990,18 +30204,28 @@ async function buildAndSortMessages(previousMessages, fabMessages, userPrompt, m
29990
30204
  const systemMessages = [];
29991
30205
  let systemTokenCount = 0;
29992
30206
  const builderInjectedPriorities = /* @__PURE__ */ new Map();
29993
- if (!options.skipAdminPromptTemplates) {
30207
+ const injectedBlockGateResults = /* @__PURE__ */ new Map();
30208
+ if (options.skipAdminPromptTemplates) {
30209
+ injectedBlockGateResults.set("formatPrompt", { reason: "mode_skipped" });
30210
+ injectedBlockGateResults.set("imagePrompt", { reason: "mode_skipped" });
30211
+ } else {
29994
30212
  if (getSettingsValue("UseFormatPrompt", settings)) {
29995
30213
  const formatPromptTemplate = settings.FormatPromptTemplate;
29996
30214
  const withFormatPrompt = includeHardcodedSystemMessage(fabMessages, formatPromptTemplate);
29997
- if (withFormatPrompt.length > fabMessages.length) builderInjectedPriorities.set(withFormatPrompt[0], 60);
30215
+ if (withFormatPrompt.length > fabMessages.length) {
30216
+ builderInjectedPriorities.set(withFormatPrompt[0], 60);
30217
+ injectedBlockGateResults.set("formatPrompt", { message: withFormatPrompt[0] });
30218
+ } else injectedBlockGateResults.set("formatPrompt", { reason: "not_triggered" });
29998
30219
  fabMessages = withFormatPrompt;
29999
- }
30220
+ } else injectedBlockGateResults.set("formatPrompt", { reason: "setting_disabled" });
30000
30221
  if (getSettingsValue("UseImagePrompt", settings)) {
30001
30222
  const withImagePrompt = includeImagePromptSystemMessage(fabMessages, userPromptContent, options.imageGenerationAvailable ?? false);
30002
- if (withImagePrompt.length > fabMessages.length) builderInjectedPriorities.set(withImagePrompt[0], 50);
30223
+ if (withImagePrompt.length > fabMessages.length) {
30224
+ builderInjectedPriorities.set(withImagePrompt[0], 50);
30225
+ injectedBlockGateResults.set("imagePrompt", { message: withImagePrompt[0] });
30226
+ } else injectedBlockGateResults.set("imagePrompt", { reason: "not_triggered" });
30003
30227
  fabMessages = withImagePrompt;
30004
- }
30228
+ } else injectedBlockGateResults.set("imagePrompt", { reason: "setting_disabled" });
30005
30229
  }
30006
30230
  const imageMessages = [];
30007
30231
  const nonImageMessages = [];
@@ -30067,6 +30291,21 @@ async function buildAndSortMessages(previousMessages, fabMessages, userPrompt, m
30067
30291
  admittedSystemMessages.add(message);
30068
30292
  }
30069
30293
  systemMessages.push(...systemCandidates.filter((message) => admittedSystemMessages.has(message)));
30294
+ const injectedBlocks = BUILDER_INJECTED_BLOCK_IDS.map((id) => {
30295
+ const gateResult = injectedBlockGateResults.get(id);
30296
+ if (!gateResult?.message) return {
30297
+ id,
30298
+ injected: false,
30299
+ delivered: false,
30300
+ ...gateResult?.reason ? { reason: gateResult.reason } : {}
30301
+ };
30302
+ return {
30303
+ id,
30304
+ injected: true,
30305
+ delivered: admittedSystemMessages.has(gateResult.message),
30306
+ ...typeof gateResult.message.content === "string" ? { content: gateResult.message.content } : {}
30307
+ };
30308
+ });
30070
30309
  if (systemCandidates.length > 0 && systemMessages.length === 0) logger.warn(`No system instructions fit the budget: ${systemCandidates.length} message(s) dropped, cap ${systemTokenCap} of ${preSystemBudget} est. tokens (${Math.round(65)}% after the attached-content reserve of ${contentReserve}). Smallest was ${Math.min(...systemCandidates.map(systemMessageTokens))} est. tokens.`);
30071
30310
  tokenBudget -= systemTokenCount;
30072
30311
  const historyMessages = isUnlimitedHistory(historyCount) ? previousMessages : historyCount > 0 ? previousMessages.slice(-historyCount * 2) : [];
@@ -30197,16 +30436,16 @@ async function buildAndSortMessages(previousMessages, fabMessages, userPrompt, m
30197
30436
  ];
30198
30437
  };
30199
30438
  const messages = assemble(processedContentMessages, processedPreviousMessages);
30200
- const recordDebugInfo = (finalContentMessages) => {
30439
+ const buildDebugInfo = (finalContentMessages) => {
30201
30440
  const historyWindowed = previousMessages.length > historyMessages.length;
30202
30441
  const budgetTruncated = allRemovedMessages.length > 0 || contentSqueezed || historyCutMidMessage;
30203
- buildAndSortMessages.lastDebugInfo = { messageTruncation: {
30442
+ return {
30204
30443
  wasTruncated: budgetTruncated,
30205
30444
  originalMessageCount: originalTotalMessageCount,
30206
30445
  truncatedMessageCount: processedPreviousMessages.length + finalContentMessages.length,
30207
30446
  truncationMethod: budgetTruncated ? "token-budget" : historyWindowed ? "history-limit" : void 0,
30208
30447
  removedMessages: allRemovedMessages.length > 0 ? allRemovedMessages : void 0
30209
- } };
30448
+ };
30210
30449
  };
30211
30450
  const finalTokenCount = await calculateTotalTokenLength(messages, {
30212
30451
  estimateOnly: false,
@@ -30251,8 +30490,11 @@ async function buildAndSortMessages(previousMessages, fabMessages, userPrompt, m
30251
30490
  else break;
30252
30491
  }
30253
30492
  if (currentTokenCount > maxInputTokens) logger.warn(`Final safety pass could not bring the payload under maxInputTokens (${currentTokenCount} > ${maxInputTokens}). Remaining: ${systemMessages.length} system, ${processedPreviousMessages.length} history, ${reducedContentMessages.length} content, ${imageMessages.length} image message(s) plus the user prompt` + (lastRatio === null ? ", with nothing shrinkable to measure." : `, at ${lastRatio.toFixed(2)} real tokens per estimated token.`));
30254
- recordDebugInfo(reducedContentMessages);
30255
- return ensureToolPairingIntegrity(assemble(reducedContentMessages, processedPreviousMessages), logger);
30493
+ return {
30494
+ messages: ensureToolPairingIntegrity(assemble(reducedContentMessages, processedPreviousMessages), logger),
30495
+ messageTruncation: buildDebugInfo(reducedContentMessages),
30496
+ injectedBlocks
30497
+ };
30256
30498
  }
30257
30499
  if (process.env.VERBOSE_MESSAGE_BUILDING === "true") {
30258
30500
  logger.log("=== Verbose Message Building Log ===");
@@ -30265,18 +30507,16 @@ async function buildAndSortMessages(previousMessages, fabMessages, userPrompt, m
30265
30507
  logger.log(`\nTotal messages: ${messages.length}`);
30266
30508
  logger.log("=== End of Verbose Message Building Log ===");
30267
30509
  }
30268
- recordDebugInfo(processedContentMessages);
30269
- return ensureToolPairingIntegrity(messages, logger);
30270
- }
30271
- /**
30272
- * Returns the debug info populated by the most recent buildAndSortMessages call.
30273
- */
30274
- function getLastBuildDebugInfo() {
30275
- return buildAndSortMessages.lastDebugInfo?.messageTruncation || null;
30510
+ return {
30511
+ messages: ensureToolPairingIntegrity(messages, logger),
30512
+ messageTruncation: buildDebugInfo(processedContentMessages),
30513
+ injectedBlocks
30514
+ };
30276
30515
  }
30277
30516
  var llm_exports = /* @__PURE__ */ __exportAll({
30278
30517
  ATTACHED_CONTENT_EXTRACTION_SHARE: () => ATTACHED_CONTENT_EXTRACTION_SHARE,
30279
30518
  ATTACHMENT_DELIVERED_NOTICE: () => ATTACHMENT_DELIVERED_NOTICE,
30519
+ BUILDER_INJECTED_BLOCK_IDS: () => BUILDER_INJECTED_BLOCK_IDS,
30280
30520
  EXTRACTION_SYSTEM_RESERVE_MAX_SHARE: () => EXTRACTION_SYSTEM_RESERVE_MAX_SHARE,
30281
30521
  FORMAT_PROMPT_PRIORITY: () => 60,
30282
30522
  IMAGE_PROMPT_PRIORITY: () => 50,
@@ -30298,7 +30538,6 @@ var llm_exports = /* @__PURE__ */ __exportAll({
30298
30538
  fetchAndProcessPreviousMessages: () => fetchAndProcessPreviousMessages,
30299
30539
  generateSafeEmbedding: () => generateSafeEmbedding,
30300
30540
  getCachedSignedUrl: () => getCachedSignedUrl,
30301
- getLastBuildDebugInfo: () => getLastBuildDebugInfo,
30302
30541
  includeHardcodedSystemMessage: () => includeHardcodedSystemMessage,
30303
30542
  includeImagePromptSystemMessage: () => includeImagePromptSystemMessage,
30304
30543
  processFabFilesServer: () => processFabFilesServer,
@@ -34541,6 +34780,7 @@ __reExport(/* @__PURE__ */ __exportAll({
34541
34780
  AdminSettingsCache: () => AdminSettingsCache,
34542
34781
  BEDROCK_EMBEDDING_MODEL_MAP: () => BEDROCK_EMBEDDING_MODEL_MAP,
34543
34782
  BFLImageService: () => BFLImageService,
34783
+ BUILDER_INJECTED_BLOCK_IDS: () => BUILDER_INJECTED_BLOCK_IDS,
34544
34784
  BadRequestError: () => BadRequestError,
34545
34785
  BaseStorage: () => BaseStorage,
34546
34786
  BedrockEmbeddingService: () => BedrockEmbeddingService,
@@ -34602,6 +34842,7 @@ __reExport(/* @__PURE__ */ __exportAll({
34602
34842
  RapidReplyMappingsCache: () => RapidReplyMappingsCache,
34603
34843
  S3Storage: () => S3Storage,
34604
34844
  SQSService: () => SQSService,
34845
+ ScopedSettingsCache: () => ScopedSettingsCache,
34605
34846
  SmartChunker: () => SmartChunker,
34606
34847
  TOKEN_BUFFER_PERCENTAGE: () => TOKEN_BUFFER_PERCENTAGE,
34607
34848
  TOOL_RESULT_NOT_RECORDED: () => TOOL_RESULT_NOT_RECORDED,
@@ -34659,12 +34900,12 @@ __reExport(/* @__PURE__ */ __exportAll({
34659
34900
  getFileContent: () => getFileContent,
34660
34901
  getFileExtension: () => getFileExtension,
34661
34902
  getFileType: () => getFileType,
34662
- getLastBuildDebugInfo: () => getLastBuildDebugInfo,
34663
34903
  getLlmWithFallback: () => getLlmWithFallback,
34664
34904
  getMimeTypeByExtension: () => getMimeTypeByExtension,
34665
34905
  getNotificationDeduplicator: () => getNotificationDeduplicator,
34666
34906
  getProviderFromModel: () => getProviderFromModel,
34667
34907
  getRetryAfterMs: () => getRetryAfterMs,
34908
+ getScopedOverrides: () => getScopedOverrides,
34668
34909
  getSettingByName: () => getSettingByName,
34669
34910
  getSettingsByNames: () => getSettingsByNames,
34670
34911
  getSettingsCacheStats: () => getSettingsCacheStats,
@@ -34674,6 +34915,7 @@ __reExport(/* @__PURE__ */ __exportAll({
34674
34915
  includeHardcodedSystemMessage: () => includeHardcodedSystemMessage,
34675
34916
  includeImagePromptSystemMessage: () => includeImagePromptSystemMessage,
34676
34917
  initializeConfig: () => initializeConfig,
34918
+ invalidateScopedSettingsCache: () => invalidateScopedSettingsCache,
34677
34919
  invalidateSettingsCache: () => invalidateSettingsCache,
34678
34920
  isAiEditableOfficeMime: () => isAiEditableOfficeMime,
34679
34921
  isGPT5ModelWithToolSupport: () => isGPT5ModelWithToolSupport,
@@ -34708,6 +34950,7 @@ __reExport(/* @__PURE__ */ __exportAll({
34708
34950
  resolveEmbeddingConfig: () => resolveEmbeddingConfig,
34709
34951
  resolveSupportedMimeType: () => resolveSupportedMimeType,
34710
34952
  safeInputWindow: () => safeInputWindow,
34953
+ scopedOverrideKey: () => scopedOverrideKey,
34711
34954
  secureParameters: () => secureParameters,
34712
34955
  sendToConnection: () => sendToConnection,
34713
34956
  shouldTriggerFallback: () => shouldTriggerFallback,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { n as logger, t as ConfigStore } from "./ConfigStore-DD3DcC3-.mjs";
3
- import { t as version } from "./package-BqKSCbso.mjs";
2
+ import { n as logger, t as ConfigStore } from "./ConfigStore-CNfbeaJf.mjs";
3
+ import { t as version } from "./package-CxHSRXdp.mjs";
4
4
  import axios, { isAxiosError } from "axios";
5
5
  //#region src/auth/OAuthClient.ts
6
6
  /**