@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.
@@ -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";
@@ -2108,6 +2108,37 @@ z$1.object({
2108
2108
  createdAt: true,
2109
2109
  updatedAt: true
2110
2110
  });
2111
+ z$1.object({
2112
+ id: z$1.string().optional(),
2113
+ /** UTC month, YYYY-MM. */
2114
+ month: z$1.string().regex(/^\d{4}-\d{2}$/, "month must be YYYY-MM"),
2115
+ /** Matches UsageEvent.provider values (e.g. "anthropic", "openai"). */
2116
+ provider: z$1.string().min(1),
2117
+ /** Provider-reported spend in USD for the month. */
2118
+ providerUsd: z$1.number().finite().nonnegative(),
2119
+ /** Our internal COGS estimate from UsageEvent aggregation. */
2120
+ internalUsd: z$1.number().finite().nonnegative(),
2121
+ /** providerUsd - internalUsd; positive = we underestimate. */
2122
+ deltaUsd: z$1.number().finite(),
2123
+ /** Absolute delta as a percentage of max(providerUsd, internalUsd) (0-100). */
2124
+ deltaPct: z$1.number().finite().nonnegative(),
2125
+ /** How the provider figure was obtained. */
2126
+ source: z$1.enum([
2127
+ "anthropic_admin_api",
2128
+ "openai_usage_api",
2129
+ "manual"
2130
+ ]),
2131
+ /** Optional detail: per-key or per-model breakdown from the provider. */
2132
+ providerBreakdown: z$1.record(z$1.string(), z$1.number()).optional(),
2133
+ /** Human-readable note (error messages, partial data warnings, etc.). */
2134
+ note: z$1.string().optional(),
2135
+ createdAt: z$1.date(),
2136
+ updatedAt: z$1.date()
2137
+ }).omit({
2138
+ id: true,
2139
+ createdAt: true,
2140
+ updatedAt: true
2141
+ });
2111
2142
  z$1.object({
2112
2143
  id: z$1.string().optional(),
2113
2144
  ownerId: z$1.string(),
@@ -2184,6 +2215,36 @@ function isPlaceholderApiKey(value) {
2184
2215
  if (!normalized) return true;
2185
2216
  return PLACEHOLDER_API_KEY_REGEX.test(normalized);
2186
2217
  }
2218
+ z$1.object({
2219
+ /**
2220
+ * The granting lake's Mongo `_id`. ALWAYS a persisted DB lake: a hardcoded/fallback lake has no
2221
+ * backing document to hang a grant on (its id is a human slug, never an ObjectId), so the write
2222
+ * boundary refuses one via `assertLakeGrantable`. That is the explicit fallback carve-out issue
2223
+ * #1667 calls for - enforced at the service layer, where the static registry is known, not here.
2224
+ */
2225
+ dataLakeId: z$1.string(),
2226
+ principalType: z$1.enum(["user", "organization"]),
2227
+ /** The granted principal's id - a userId or an organizationId, per `principalType`. */
2228
+ principalId: z$1.string(),
2229
+ role: z$1.enum([
2230
+ "owner",
2231
+ "curator",
2232
+ "reader"
2233
+ ]),
2234
+ /** The actor (userId) who created the grant. */
2235
+ grantedByUserId: z$1.string(),
2236
+ /**
2237
+ * Optional expiry for a time-boxed grant (trials, temporary internal collaborators,
2238
+ * evaluations). A grant is expired once `expiresAt` is set and in the past. Expired rows are
2239
+ * filtered at READ time by grant resolution (#1673) and are deliberately NOT swept from the
2240
+ * collection, so an owner-facing membership view (#1672) and the audit trail (#1663) can still
2241
+ * render a lapsed grant. Absent/null = never expires.
2242
+ *
2243
+ * Only meaningful for a principal INSIDE the lake's organization: membership never crosses
2244
+ * organizations (epic decision 12), so this is not a cross-org mechanism.
2245
+ */
2246
+ expiresAt: z$1.date().nullish()
2247
+ });
2187
2248
  /**
2188
2249
  * SRE Agent Trio - Shared Types
2189
2250
  *
@@ -3939,6 +4000,7 @@ const HearthEventAction = z$1.object({
3939
4000
  seq: z$1.number(),
3940
4001
  actorId: z$1.string(),
3941
4002
  actorName: z$1.string().optional(),
4003
+ actorKind: actorKindSchema.optional(),
3942
4004
  kind: hearthEventKindSchema,
3943
4005
  human: z$1.object({
3944
4006
  text: z$1.string(),
@@ -4519,6 +4581,7 @@ z$1.discriminatedUnion("action", [
4519
4581
  z$1.object({
4520
4582
  sessionId: z$1.string().nullish(),
4521
4583
  message: z$1.string(),
4584
+ organizationId: z$1.string().optional(),
4522
4585
  model: z$1.string().optional(),
4523
4586
  temperature: z$1.number().min(0).max(2).optional(),
4524
4587
  max_tokens: z$1.number().positive().optional(),
@@ -5598,6 +5661,17 @@ z$2.object({
5598
5661
  lastActiveAt: z$2.date().optional(),
5599
5662
  isOnline: z$2.boolean()
5600
5663
  });
5664
+ /**
5665
+ * Model-INDEPENDENT sanity ceiling for a configured passage target, in tokens. A passage larger
5666
+ * than a full typical embedding context window (~8K) defeats retrieval granularity - one vector
5667
+ * would average a whole document (see DEFAULT_PASSAGE_TOKEN_TARGET). This bounds the scoped
5668
+ * `DefaultChunkSize` setting (#1662) where the specific embedding model is NOT known (the resolver
5669
+ * clamp is pure); the EXACT per-model embedding-window cap is enforced downstream by the chunker
5670
+ * (`effectiveChunkTokenLimit` in fab-pipeline), which knows the model and reduces further if needed.
5671
+ */
5672
+ const MAX_PASSAGE_TOKEN_TARGET = 8192;
5673
+ /** Ceiling so "adjustable" cannot mean "unbounded" in either direction. */
5674
+ const LAKE_ACCESS_AUDIT_RETENTION_MAX_DAYS = 2555;
5601
5675
  let OpenAIEmbeddingModel = /* @__PURE__ */ function(OpenAIEmbeddingModel) {
5602
5676
  OpenAIEmbeddingModel["TEXT_EMBEDDING_3_SMALL"] = "text-embedding-3-small";
5603
5677
  OpenAIEmbeddingModel["TEXT_EMBEDDING_3_LARGE"] = "text-embedding-3-large";
@@ -5742,11 +5816,15 @@ const HELP_CENTER_PROMPT = `HELP CENTER: Bike4Mind has a built-in Help Center th
5742
5816
  *
5743
5817
  * Counterweight to the completeness pressure the rest of the system prompt applies: without an
5744
5818
  * explicit licence to abstain, the model treats "answer fully" as unconditional and fills gaps with
5745
- * invented specifics about the user or their data. Measured as the single largest quality gain on
5746
- * questions whose correct answer is a refusal, so it ships on every completion rather than only on
5747
- * the grounded surfaces. Kept short on purpose - it must be cheap and behaviorally light.
5819
+ * invented specifics - including a named customer, competitor, deal or dollar figure a leading
5820
+ * question implied but no source supports, volunteered with citation-like framing so it reads as
5821
+ * sourced. In internal evaluation (a harness kept outside this repo) this was among the largest
5822
+ * quality gains on questions whose correct answer is a refusal, so it ships on every completion
5823
+ * rather than only on the grounded surfaces (it is also the only surface covering a turn that
5824
+ * answers WITHOUT searching the knowledge base). Kept short
5825
+ * on purpose - it must be cheap and behaviorally light.
5748
5826
  */
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.`;
5827
+ 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
5828
  /**
5751
5829
  * Default text for the formatting system message. Runtime fallback used by
5752
5830
  * `includeHardcodedSystemMessage` (b4m-core/utils/src/llm/utils.ts) when the `FormatPromptTemplate`
@@ -5800,8 +5878,10 @@ z$1.enum([
5800
5878
  "EnableDataLakes",
5801
5879
  "EnableDataLakesDefault",
5802
5880
  "EnableDataLakeSlackAdd",
5881
+ "EnableDataLakeGroundingMode",
5803
5882
  "EnableLakeMemory",
5804
5883
  "EnableDataLakeVectorSearch",
5884
+ "PauseLakeConvergence",
5805
5885
  "EnableBriefcase",
5806
5886
  "EnableBriefcaseDefault",
5807
5887
  "EnableImageTemplates",
@@ -5892,6 +5972,15 @@ z$1.enum([
5892
5972
  "defaultEmbeddingModel",
5893
5973
  "dataLakeSearchMaxFiles",
5894
5974
  "dataLakeSearchMaxChunks",
5975
+ "dataLakeEmbeddingSpendEnabled",
5976
+ "dataLakeEmbeddingBudgetPerRunUsd",
5977
+ "dataLakeEmbeddingBudgetPerLakeUsd",
5978
+ "dataLakeEmbeddingBudgetPerPeriodUsd",
5979
+ "dataLakeEmbeddingBudgetPeriodHours",
5980
+ "dataLakeEmbeddingMaxCallsPerMinute",
5981
+ "dataLakeVectorizeChunkBatchSize",
5982
+ "LakeAccessAuditRetentionDays",
5983
+ "LakeAccessQueryTextRetentionDays",
5895
5984
  "MaxContentLength",
5896
5985
  "enableAutoChunk",
5897
5986
  "SlackDefaultWebhookUrl",
@@ -5960,7 +6049,11 @@ z$1.enum([
5960
6049
  "modelDiscoveryAutoEnable",
5961
6050
  "modelDiscoveryAllowEgress",
5962
6051
  "modelDiscoveryPriceBandPct",
5963
- "modelDiscoveryAutoRemap"
6052
+ "modelDiscoveryAutoRemap",
6053
+ "prReportRepo",
6054
+ "prReportIdentityMap",
6055
+ "prReportSlackChannel",
6056
+ "prReportEgressAllowlist"
5964
6057
  ]);
5965
6058
  /**
5966
6059
  * Intent-classifier sub-config. Drives the LLM-based silent
@@ -6077,6 +6170,9 @@ function makeStringSetting(config) {
6077
6170
  */
6078
6171
  const DATA_LAKE_SEARCH_MAX_FILES_DEFAULT = 5e3;
6079
6172
  const DATA_LAKE_SEARCH_MAX_CHUNKS_DEFAULT = 1e5;
6173
+ const DATA_LAKE_EMBEDDING_BUDGET_PER_LAKE_USD_MAX = 1e4;
6174
+ const DATA_LAKE_EMBEDDING_BUDGET_PER_PERIOD_USD_MAX = 5e3;
6175
+ const DATA_LAKE_EMBEDDING_MAX_CALLS_PER_MINUTE_MAX = 1e4;
6080
6176
  function makeNumberSetting(config) {
6081
6177
  let numberSchema = z$1.coerce.number();
6082
6178
  if (config.min !== void 0) numberSchema = numberSchema.min(config.min);
@@ -6668,6 +6764,42 @@ const API_SERVICE_GROUPS = {
6668
6764
  }
6669
6765
  ]
6670
6766
  },
6767
+ DATA_LAKE_COST: {
6768
+ id: "dataLakeCostGovernance",
6769
+ name: "Data Lake Cost Governance",
6770
+ description: "Spend levers for data-lake embedding work (ingestion, reprocessing, convergence). Budgets are USD; 0 means stop spending, not \"use the default\".",
6771
+ icon: "Savings",
6772
+ settings: [
6773
+ {
6774
+ key: "dataLakeEmbeddingSpendEnabled",
6775
+ order: 1
6776
+ },
6777
+ {
6778
+ key: "dataLakeEmbeddingBudgetPerRunUsd",
6779
+ order: 2
6780
+ },
6781
+ {
6782
+ key: "dataLakeEmbeddingBudgetPerLakeUsd",
6783
+ order: 3
6784
+ },
6785
+ {
6786
+ key: "dataLakeEmbeddingBudgetPerPeriodUsd",
6787
+ order: 4
6788
+ },
6789
+ {
6790
+ key: "dataLakeEmbeddingBudgetPeriodHours",
6791
+ order: 5
6792
+ },
6793
+ {
6794
+ key: "dataLakeEmbeddingMaxCallsPerMinute",
6795
+ order: 6
6796
+ },
6797
+ {
6798
+ key: "dataLakeVectorizeChunkBatchSize",
6799
+ order: 7
6800
+ }
6801
+ ]
6802
+ },
6671
6803
  VOICE_SESSION: {
6672
6804
  id: "voiceSessionService",
6673
6805
  name: "Voice Session Service",
@@ -7437,6 +7569,19 @@ const API_SERVICE_GROUPS = {
7437
7569
  order: 3
7438
7570
  }
7439
7571
  ]
7572
+ },
7573
+ DATA_LAKE_AUDIT: {
7574
+ id: "dataLakeAuditService",
7575
+ name: "Data Lake Access Audit",
7576
+ description: "Retention for the lake access audit trail and its opt-in query-text log",
7577
+ icon: "Security",
7578
+ settings: [{
7579
+ key: "LakeAccessAuditRetentionDays",
7580
+ order: 1
7581
+ }, {
7582
+ key: "LakeAccessQueryTextRetentionDays",
7583
+ order: 2
7584
+ }]
7440
7585
  }
7441
7586
  };
7442
7587
  const settingsMap = {
@@ -7548,6 +7693,16 @@ const settingsMap = {
7548
7693
  order: 90,
7549
7694
  dependsOn: "EnableDataLakes"
7550
7695
  }),
7696
+ EnableDataLakeGroundingMode: makeBooleanSetting({
7697
+ key: "EnableDataLakeGroundingMode",
7698
+ name: "Data Lakes: Per-lake grounding mode",
7699
+ defaultValue: true,
7700
+ 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.",
7701
+ category: "Experimental",
7702
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
7703
+ order: 92,
7704
+ dependsOn: "EnableDataLakes"
7705
+ }),
7551
7706
  EnableLakeMemory: makeBooleanSetting({
7552
7707
  key: "EnableLakeMemory",
7553
7708
  name: "Data Lakes: Lake memory profile (extraction)",
@@ -7568,6 +7723,21 @@ const settingsMap = {
7568
7723
  order: 92,
7569
7724
  dependsOn: "EnableDataLakes"
7570
7725
  }),
7726
+ PauseLakeConvergence: makeBooleanSetting({
7727
+ key: "PauseLakeConvergence",
7728
+ name: "Data Lakes: Pause background convergence work",
7729
+ defaultValue: false,
7730
+ 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).",
7731
+ category: "Experimental",
7732
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
7733
+ order: 93,
7734
+ dependsOn: "EnableDataLakes",
7735
+ scope: { settableAt: [
7736
+ "organization",
7737
+ "owner",
7738
+ "lake"
7739
+ ] }
7740
+ }),
7571
7741
  EnableBriefcase: makeBooleanSetting({
7572
7742
  key: "EnableBriefcase",
7573
7743
  name: "Enable Briefcase",
@@ -7790,9 +7960,13 @@ const settingsMap = {
7790
7960
  name: "Default Chunk Size",
7791
7961
  defaultValue: 512,
7792
7962
  min: 64,
7793
- 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.",
7963
+ 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. 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
7964
  category: "AI",
7795
- order: 3
7965
+ order: 3,
7966
+ scope: {
7967
+ settableAt: ["organization", "owner"],
7968
+ clamp: (value) => Math.min(Math.max(Math.floor(value), 64), MAX_PASSAGE_TOKEN_TARGET)
7969
+ }
7796
7970
  }),
7797
7971
  ModerationEnabled: makeBooleanSetting({
7798
7972
  key: "ModerationEnabled",
@@ -7836,7 +8010,7 @@ const settingsMap = {
7836
8010
  key: "AbstentionPrompt",
7837
8011
  name: "Abstention Prompt",
7838
8012
  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.",
8013
+ 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
8014
  category: "AI",
7841
8015
  order: 11
7842
8016
  }),
@@ -8673,7 +8847,12 @@ const settingsMap = {
8673
8847
  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
8848
  category: "AI",
8675
8849
  group: API_SERVICE_GROUPS.EMBEDDING.id,
8676
- order: 2
8850
+ order: 2,
8851
+ scope: { settableAt: [
8852
+ "organization",
8853
+ "owner",
8854
+ "lake"
8855
+ ] }
8677
8856
  }),
8678
8857
  dataLakeSearchMaxChunks: makeNumberSetting({
8679
8858
  key: "dataLakeSearchMaxChunks",
@@ -8683,8 +8862,110 @@ const settingsMap = {
8683
8862
  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
8863
  category: "AI",
8685
8864
  group: API_SERVICE_GROUPS.EMBEDDING.id,
8865
+ order: 3,
8866
+ scope: { settableAt: [
8867
+ "organization",
8868
+ "owner",
8869
+ "lake"
8870
+ ] }
8871
+ }),
8872
+ LakeAccessAuditRetentionDays: makeNumberSetting({
8873
+ key: "LakeAccessAuditRetentionDays",
8874
+ name: "Lake Access Audit Retention (days)",
8875
+ defaultValue: 450,
8876
+ min: 450,
8877
+ max: LAKE_ACCESS_AUDIT_RETENTION_MAX_DAYS,
8878
+ 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.",
8879
+ category: "SecOps",
8880
+ group: API_SERVICE_GROUPS.DATA_LAKE_AUDIT.id,
8881
+ order: 1
8882
+ }),
8883
+ LakeAccessQueryTextRetentionDays: makeNumberSetting({
8884
+ key: "LakeAccessQueryTextRetentionDays",
8885
+ name: "Lake Access Query Text Retention (days)",
8886
+ defaultValue: 30,
8887
+ min: 1,
8888
+ max: 90,
8889
+ 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.",
8890
+ category: "SecOps",
8891
+ group: API_SERVICE_GROUPS.DATA_LAKE_AUDIT.id,
8892
+ order: 2
8893
+ }),
8894
+ dataLakeEmbeddingSpendEnabled: makeBooleanSetting({
8895
+ key: "dataLakeEmbeddingSpendEnabled",
8896
+ name: "Data Lake Embedding Spend Enabled",
8897
+ defaultValue: true,
8898
+ description: "Master switch for data-lake embedding spend (ingestion, reprocessing, convergence). Off halts all provider embedding calls on those paths; cached embeddings still apply.",
8899
+ category: "AI",
8900
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8901
+ order: 1
8902
+ }),
8903
+ dataLakeEmbeddingBudgetPerRunUsd: makeNumberSetting({
8904
+ key: "dataLakeEmbeddingBudgetPerRunUsd",
8905
+ name: "Embedding Budget Per Run (USD)",
8906
+ defaultValue: 5,
8907
+ min: 0,
8908
+ max: 500,
8909
+ description: "Most USD one ingestion/reprocess run (upload batch) may spend on embedding calls. 0 stops runs from spending at all.",
8910
+ category: "AI",
8911
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8912
+ order: 2
8913
+ }),
8914
+ dataLakeEmbeddingBudgetPerLakeUsd: makeNumberSetting({
8915
+ key: "dataLakeEmbeddingBudgetPerLakeUsd",
8916
+ name: "Embedding Budget Per Lake (USD)",
8917
+ defaultValue: 100,
8918
+ min: 0,
8919
+ max: DATA_LAKE_EMBEDDING_BUDGET_PER_LAKE_USD_MAX,
8920
+ description: "Most USD one data lake may spend on embedding calls over its lifetime. 0 stops all spend for every lake.",
8921
+ category: "AI",
8922
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8686
8923
  order: 3
8687
8924
  }),
8925
+ dataLakeEmbeddingBudgetPerPeriodUsd: makeNumberSetting({
8926
+ key: "dataLakeEmbeddingBudgetPerPeriodUsd",
8927
+ name: "Embedding Budget Per Period (USD)",
8928
+ defaultValue: 50,
8929
+ min: 0,
8930
+ max: DATA_LAKE_EMBEDDING_BUDGET_PER_PERIOD_USD_MAX,
8931
+ 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.",
8932
+ category: "AI",
8933
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8934
+ order: 4
8935
+ }),
8936
+ dataLakeEmbeddingBudgetPeriodHours: makeNumberSetting({
8937
+ key: "dataLakeEmbeddingBudgetPeriodHours",
8938
+ name: "Embedding Budget Period (hours)",
8939
+ defaultValue: 24,
8940
+ min: 1,
8941
+ max: 720,
8942
+ description: "Length of the per-period budget window in hours. Not a spend value itself, so 0 is not meaningful here (min 1).",
8943
+ category: "AI",
8944
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8945
+ order: 5
8946
+ }),
8947
+ dataLakeEmbeddingMaxCallsPerMinute: makeNumberSetting({
8948
+ key: "dataLakeEmbeddingMaxCallsPerMinute",
8949
+ name: "Embedding Max Calls Per Minute",
8950
+ defaultValue: 120,
8951
+ min: 0,
8952
+ max: DATA_LAKE_EMBEDDING_MAX_CALLS_PER_MINUTE_MAX,
8953
+ 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.",
8954
+ category: "AI",
8955
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8956
+ order: 6
8957
+ }),
8958
+ dataLakeVectorizeChunkBatchSize: makeNumberSetting({
8959
+ key: "dataLakeVectorizeChunkBatchSize",
8960
+ name: "Vectorize Chunk Batch Size",
8961
+ defaultValue: 50,
8962
+ min: 1,
8963
+ max: 500,
8964
+ 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.",
8965
+ category: "AI",
8966
+ group: API_SERVICE_GROUPS.DATA_LAKE_COST.id,
8967
+ order: 7
8968
+ }),
8688
8969
  slackSigningSecret: makeStringSetting({
8689
8970
  key: "slackSigningSecret",
8690
8971
  name: "Slack Signing Secret",
@@ -9393,6 +9674,39 @@ const settingsMap = {
9393
9674
  category: "AI",
9394
9675
  group: API_SERVICE_GROUPS.MODEL_DISCOVERY.id,
9395
9676
  order: 6
9677
+ }),
9678
+ prReportRepo: makeStringSetting({
9679
+ key: "prReportRepo",
9680
+ name: "PR Report Repository",
9681
+ defaultValue: "",
9682
+ 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.",
9683
+ category: "Admin",
9684
+ order: 141
9685
+ }),
9686
+ prReportIdentityMap: makeStringSetting({
9687
+ key: "prReportIdentityMap",
9688
+ name: "PR Report Identity Map",
9689
+ defaultValue: "",
9690
+ 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.",
9691
+ category: "Admin",
9692
+ order: 142
9693
+ }),
9694
+ prReportSlackChannel: makeStringSetting({
9695
+ key: "prReportSlackChannel",
9696
+ name: "PR Report Slack Channel",
9697
+ defaultValue: "",
9698
+ description: "Slack channel ID the PR status digest posts to. The bot token itself is resolved from the credential store, never from admin settings.",
9699
+ category: "Slack",
9700
+ order: 143
9701
+ }),
9702
+ prReportEgressAllowlist: makeObjectSetting({
9703
+ key: "prReportEgressAllowlist",
9704
+ name: "PR Report Egress Allowlist",
9705
+ defaultValue: { hosts: ["slack.com", "www.slack.com"] },
9706
+ description: "Hosts the PR digest may post to. 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. Validated against the Slack API origin, so the default lists slack.com; self-hosted (non-Slack) origins are not yet supported.",
9707
+ category: "Slack",
9708
+ order: 144,
9709
+ schema: z$1.object({ hosts: z$1.array(z$1.string()).default([]) })
9396
9710
  })
9397
9711
  };
9398
9712
  /**
@@ -10594,6 +10908,24 @@ z$1.object({
10594
10908
  */
10595
10909
  const DATALAKE_TAG_PREFIX = "datalake:";
10596
10910
  /**
10911
+ * How a lake's attached corpus is grounded into a chat turn, as a DELIBERATE per-lake product
10912
+ * choice rather than a side effect of who is asking (see IDataLake.groundingMode):
10913
+ * - `inline`: paste the corpus into the prompt (never defer to the search tool).
10914
+ * - `retrieve`: leave the corpus to the offered search_knowledge_base tool (always defer the
10915
+ * tool-retrievable subset), so an owner and an entitlement-only reader ground identically.
10916
+ * - `auto-by-size`: keep the size heuristic - defer only when the per-doc even-split inline depth
10917
+ * falls below the `CorpusRetrievalMinInlineTokensPerDoc` floor (see shouldDeferCorpusToRetrieval).
10918
+ *
10919
+ * The resolution seam is create-time (resolveLakeSessionDefaults -> session.corpusGroundingMode);
10920
+ * the enforcement seam is the completion-path defer plan. Keep this tuple and DataLakeGroundingMode
10921
+ * as the single source both the Zod schema and the Mongoose enum derive from.
10922
+ */
10923
+ const DATA_LAKE_GROUNDING_MODES = [
10924
+ "inline",
10925
+ "retrieve",
10926
+ "auto-by-size"
10927
+ ];
10928
+ /**
10597
10929
  * True when a would-be `fileTagPrefix` sits inside the `datalake:` namespace, which holds every
10598
10930
  * lake's membership meta-tag. Such a prefix would make one lake's content prefix match other
10599
10931
  * lakes' membership tags. Shared by the create schema and the wizard's client-side gate so the
@@ -10646,8 +10978,11 @@ z.object({
10646
10978
  description: z.string().max(2e3).optional(),
10647
10979
  systemPrompt: z.string().optional(),
10648
10980
  preferredSystemPromptId: z.union([z.literal(""), z.string().min(1).max(200)]).optional(),
10981
+ groundingMode: z.enum(DATA_LAKE_GROUNDING_MODES).optional(),
10649
10982
  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()
10983
+ 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(),
10984
+ auditQueryTextEnabled: z.boolean().optional(),
10985
+ requiredPassageTokenTarget: z.number().int().min(64).max(MAX_PASSAGE_TOKEN_TARGET).nullable().optional()
10651
10986
  });
10652
10987
  z.object({
10653
10988
  organizationId: z.string().optional(),
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { F as loadContextFiles, J as setWebSocketToolExecutor, L as generateCliTools, M as ReActAgent, X as buildSystemPrompt, Y as getPlanModeFilePath, a as McpManager, i as AgentStore, l as OllamaBackend, n as BackgroundAgentManager, o as createSseBackend, r as SubagentOrchestrator, s as ServerLlmBackend, t as AgentHistoryStore } from "./AgentHistoryStore-C8uUKjjC.mjs";
3
- import { n as logger } from "./ConfigStore-DD3DcC3-.mjs";
2
+ import { F as loadContextFiles, J as setWebSocketToolExecutor, L as generateCliTools, M as ReActAgent, X as buildSystemPrompt, Y as getPlanModeFilePath, a as McpManager, i as AgentStore, l as OllamaBackend, n as BackgroundAgentManager, o as createSseBackend, r as SubagentOrchestrator, s as ServerLlmBackend, t as AgentHistoryStore } from "./AgentHistoryStore-BQiATPsQ.mjs";
3
+ import { n as logger } from "./ConfigStore-CNfbeaJf.mjs";
4
4
  import { z as z$1 } from "zod";
5
5
  import WebSocket from "ws";
6
6
  //#region src/llm/NotifyingLlmBackend.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as RemoteSkillSource, A as FallbackLlmBackend, C as createWriteTodosTool, D as createResumeAgentTool, E as createCoordinateTaskTool, I as PermissionManager, J as setWebSocketToolExecutor, O as createBackgroundAgentTools, Q as isReadOnlyTool, S as createTodoStore, et as CustomCommandStore, k as createAgentDelegateTool, nt as SessionStore, tt as CheckpointStore, w as createSkillTool, x as createFindDefinitionTool, y as createGetFileStructureTool } from "../AgentHistoryStore-C8uUKjjC.mjs";
3
- import { c as requireApiUrl, n as logger, t as ConfigStore } from "../ConfigStore-DD3DcC3-.mjs";
4
- import { t as ApiClient } from "../ApiClient-B_CQrUiF.mjs";
5
- import { a as createToolSearchTool, i as buildLlmBackend, n as buildSupportingStores, o as deferredToolRegistry, r as buildSandbox, s as NotifyingLlmBackend, t as buildAgent } from "../buildAgent-mVuXU_H4.mjs";
2
+ import { $ as RemoteSkillSource, A as FallbackLlmBackend, C as createWriteTodosTool, D as createResumeAgentTool, E as createCoordinateTaskTool, I as PermissionManager, J as setWebSocketToolExecutor, O as createBackgroundAgentTools, Q as isReadOnlyTool, S as createTodoStore, et as CustomCommandStore, k as createAgentDelegateTool, nt as SessionStore, tt as CheckpointStore, w as createSkillTool, x as createFindDefinitionTool, y as createGetFileStructureTool } from "../AgentHistoryStore-BQiATPsQ.mjs";
3
+ import { c as requireApiUrl, n as logger, t as ConfigStore } from "../ConfigStore-CNfbeaJf.mjs";
4
+ import { t as ApiClient } from "../ApiClient-BPmlalut.mjs";
5
+ import { a as createToolSearchTool, i as buildLlmBackend, n as buildSupportingStores, o as deferredToolRegistry, r as buildSandbox, s as NotifyingLlmBackend, t as buildAgent } from "../buildAgent-DwPvcTpz.mjs";
6
6
  import { randomUUID } from "crypto";
7
7
  import { existsSync, realpathSync, statSync } from "fs";
8
8
  import { isAbsolute } from "path";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { l as resolveApiEndpoint, s as parseApiUrl, t as ConfigStore } from "../ConfigStore-DD3DcC3-.mjs";
2
+ import { l as resolveApiEndpoint, s as parseApiUrl, t as ConfigStore } from "../ConfigStore-CNfbeaJf.mjs";
3
3
  //#region src/commands/apiCommand.ts
4
4
  /**
5
5
  * External API config command (--api-url / --reset-api)
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-BqKSCbso.mjs";
2
+ import { t as version } from "../package-CxHSRXdp.mjs";
3
3
  import { a as fetchLatestVersion, c as isNpmPrefixWritable, i as compareSemver } from "../updateChecker-CQW8bxo6.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync } from "child_process";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-DD3DcC3-.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-CNfbeaJf.mjs";
3
3
  //#region src/commands/envCommand.ts
4
4
  /**
5
5
  * Environment switching for the `--dev` / `--prod` launch flags.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as RemoteSkillSource, A as FallbackLlmBackend, B as classifyCommandRisk, C as createWriteTodosTool, D as createResumeAgentTool, E as createCoordinateTaskTool, F as loadContextFiles, I as PermissionManager, J as setWebSocketToolExecutor, L as generateCliTools, M as ReActAgent, O as createBackgroundAgentTools, Q as isReadOnlyTool, S as createTodoStore, X as buildSystemPrompt, a as McpManager, b as createWorkItemTools, et as CustomCommandStore, i as AgentStore, k as createAgentDelegateTool, n as BackgroundAgentManager, nt as SessionStore, o as createSseBackend, r as SubagentOrchestrator, t as AgentHistoryStore, tt as CheckpointStore, w as createSkillTool, x as createFindDefinitionTool, y as createGetFileStructureTool, z as SHELL_LIKE_TOOL_COMMAND_FIELDS } from "../AgentHistoryStore-C8uUKjjC.mjs";
3
- import { c as requireApiUrl, n as logger, t as ConfigStore } from "../ConfigStore-DD3DcC3-.mjs";
4
- import { t as ApiClient } from "../ApiClient-B_CQrUiF.mjs";
2
+ import { $ as RemoteSkillSource, A as FallbackLlmBackend, B as classifyCommandRisk, C as createWriteTodosTool, D as createResumeAgentTool, E as createCoordinateTaskTool, F as loadContextFiles, I as PermissionManager, J as setWebSocketToolExecutor, L as generateCliTools, M as ReActAgent, O as createBackgroundAgentTools, Q as isReadOnlyTool, S as createTodoStore, X as buildSystemPrompt, a as McpManager, b as createWorkItemTools, et as CustomCommandStore, i as AgentStore, k as createAgentDelegateTool, n as BackgroundAgentManager, nt as SessionStore, o as createSseBackend, r as SubagentOrchestrator, t as AgentHistoryStore, tt as CheckpointStore, w as createSkillTool, x as createFindDefinitionTool, y as createGetFileStructureTool, z as SHELL_LIKE_TOOL_COMMAND_FIELDS } from "../AgentHistoryStore-BQiATPsQ.mjs";
3
+ import { c as requireApiUrl, n as logger, t as ConfigStore } from "../ConfigStore-CNfbeaJf.mjs";
4
+ import { t as ApiClient } from "../ApiClient-BPmlalut.mjs";
5
5
  import { t as DEFAULT_SANDBOX_CONFIG } from "../types-LyRNHOiS.mjs";
6
6
  import { r as reconstructTurnBlocks, t as WorkItemsClient } from "../WorkItemsClient-Cow6nXx7.mjs";
7
7
  import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-ChGlxSGQ.mjs";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-DD3DcC3-.mjs";
3
- import { t as version } from "../package-BqKSCbso.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-CNfbeaJf.mjs";
3
+ import { t as version } from "../package-CxHSRXdp.mjs";
4
4
  //#region src/commands/mcpCommand.ts
5
5
  /**
6
6
  * External MCP commands (b4m mcp list, b4m mcp add, etc.)
@@ -70,7 +70,7 @@ async function handleMcpCommand(subcommand, argv) {
70
70
  await handleDisable(config, argv.name, configStore);
71
71
  break;
72
72
  case "serve": {
73
- const { handleMcpServeCommand } = await import("../serve-CuF0I5en.mjs");
73
+ const { handleMcpServeCommand } = await import("../serve-Du3HiqAH.mjs");
74
74
  await handleMcpServeCommand({
75
75
  http: Boolean(argv.http),
76
76
  port: typeof argv.port === "number" ? argv.port : void 0,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as getDefaultPluginsDir, r as isFeatureEnabled, t as PluginStore } from "../PluginStore-DwvOJ-G3.mjs";
3
- import { t as ConfigStore } from "../ConfigStore-DD3DcC3-.mjs";
3
+ import { t as ConfigStore } from "../ConfigStore-CNfbeaJf.mjs";
4
4
  import { execFileSync } from "child_process";
5
5
  import { promises } from "fs";
6
6
  import path from "path";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-BqKSCbso.mjs";
2
+ import { t as version } from "../package-CxHSRXdp.mjs";
3
3
  import { c as isNpmPrefixWritable, l as setAutoUpdatePreference, n as REEXEC_GUARD_ENV, o as forceCheckForUpdate, r as checkForUpdate, s as getAutoUpdatePreference, t as INSTALL_CMD, u as shouldAttemptAutoUpdate } from "../updateChecker-CQW8bxo6.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync, spawnSync } from "child_process";