@bike4mind/cli 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -387,6 +387,19 @@ if (isDev) {
387
387
  } else {
388
388
  // Production: run compiled JavaScript
389
389
  try {
390
+ // Auto-update on launch (consent-first): when a newer version is available
391
+ // on a writable global prefix, install + re-exec into it BEFORE importing
392
+ // the code-split app — running an install while dist/index.mjs is loaded
393
+ // would crash it. On the default 'ask' preference this prompts the user
394
+ // (Update once / Always / Skip / Never); 'auto' installs silently, 'never'
395
+ // does nothing. Wrapped so the updater can never block launching the CLI.
396
+ try {
397
+ const { maybeAutoUpdateOnLaunch } = await import('../dist/commands/updateCommand.mjs');
398
+ await maybeAutoUpdateOnLaunch();
399
+ } catch {
400
+ // Updater is best-effort — fall through to the current version.
401
+ }
402
+
390
403
  await import(join(__dirname, '../dist/index.mjs'));
391
404
  } catch (error) {
392
405
  console.error('Failed to start CLI:', error);
@@ -377,6 +377,7 @@ let ChatModels = /* @__PURE__ */ function(ChatModels) {
377
377
  ChatModels["CLAUDE_4_6_OPUS"] = "claude-opus-4-6";
378
378
  ChatModels["CLAUDE_4_7_OPUS"] = "claude-opus-4-7";
379
379
  ChatModels["CLAUDE_4_8_OPUS"] = "claude-opus-4-8";
380
+ ChatModels["CLAUDE_FABLE_5"] = "claude-fable-5";
380
381
  ChatModels["JURASSIC2_ULTRA"] = "ai21.j2-ultra-v1";
381
382
  ChatModels["JURASSIC2_MID"] = "ai21.j2-mid-v1";
382
383
  ChatModels["GEMINI_3_5_FLASH"] = "gemini-3.5-flash";
@@ -420,7 +421,10 @@ const REASONING_SUPPORTED_MODELS = new Set([
420
421
  "gpt-5-mini",
421
422
  "gpt-5-nano",
422
423
  "gpt-5.1",
423
- "gpt-5.2"
424
+ "gpt-5.2",
425
+ "gpt-5.4",
426
+ "gpt-5.4-mini",
427
+ "gpt-5.4-nano"
424
428
  ]);
425
429
  new Set([
426
430
  ...Array.from(REASONING_SUPPORTED_MODELS),
@@ -648,7 +652,8 @@ const ArtifactTypeSchema = z.enum([
648
652
  "quest",
649
653
  "file",
650
654
  "questmaster",
651
- "lattice"
655
+ "lattice",
656
+ "blog-draft"
652
657
  ]);
653
658
  [...new Set(Object.values({
654
659
  mermaid: {
@@ -722,6 +727,12 @@ const ArtifactTypeSchema = z.enum([
722
727
  description: "Interactive chess board with move validation and AI opponent",
723
728
  category: "interactive",
724
729
  mimeType: "application/json"
730
+ },
731
+ "blog-draft": {
732
+ name: "Blog Draft",
733
+ description: "Drafted blog/social content ready for review and publishing",
734
+ category: "document",
735
+ mimeType: "application/json"
725
736
  }
726
737
  }).map((info) => info.category))];
727
738
  const ArtifactSchema = z.object({
@@ -891,7 +902,8 @@ const ClaudeArtifactMimeTypes = {
891
902
  CODE: "application/vnd.ant.code",
892
903
  MARKDOWN: "text/markdown",
893
904
  LATTICE: "application/vnd.b4m.lattice",
894
- PYTHON: "application/vnd.ant.python"
905
+ PYTHON: "application/vnd.ant.python",
906
+ BLOG_DRAFT: "application/vnd.b4m.blog-draft"
895
907
  };
896
908
  let KnowledgeType = /* @__PURE__ */ function(KnowledgeType) {
897
909
  /**
@@ -1091,7 +1103,8 @@ const GenericCreditDeductTransaction = BaseCreditTransaction.extend({
1091
1103
  "notebook_curation",
1092
1104
  "manual",
1093
1105
  "admin_adjustment",
1094
- "refund_adjustment"
1106
+ "refund_adjustment",
1107
+ "qwork_reservation"
1095
1108
  ]).optional(),
1096
1109
  /**
1097
1110
  * Stripe dispute ID — set for dispute clawback transactions.
@@ -1452,6 +1465,7 @@ const SecopsTriageConfigSchema = z.object({
1452
1465
  let SupportedFabFileMimeTypes = /* @__PURE__ */ function(SupportedFabFileMimeTypes) {
1453
1466
  SupportedFabFileMimeTypes["PDF"] = "application/pdf";
1454
1467
  SupportedFabFileMimeTypes["DOCX"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
1468
+ SupportedFabFileMimeTypes["PPTX"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
1455
1469
  SupportedFabFileMimeTypes["XLS"] = "application/vnd.ms-excel";
1456
1470
  SupportedFabFileMimeTypes["XLSX"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
1457
1471
  SupportedFabFileMimeTypes["TXT_PLAIN"] = "text/plain";
@@ -1493,6 +1507,27 @@ let SupportedFabFileMimeTypes = /* @__PURE__ */ function(SupportedFabFileMimeTyp
1493
1507
  return SupportedFabFileMimeTypes;
1494
1508
  }({});
1495
1509
  /**
1510
+ * Canonical agent execution status tuple. Shared between the database model
1511
+ * (`AgentExecutionModel`), the client store (`useAgentExecutionStore`), and
1512
+ * wire schemas (`ChildExecutionSnapshotSchema`, etc.) so the value space can't
1513
+ * drift across boundaries. Previously declared in two places — moving it here
1514
+ * lets the Zod schemas type `status` as `z.enum(AGENT_EXECUTION_STATUSES)`
1515
+ * instead of `z.string()`, removing the runtime narrowing the client used to
1516
+ * compensate for the loose contract.
1517
+ */
1518
+ const AGENT_EXECUTION_STATUSES = [
1519
+ "pending",
1520
+ "running",
1521
+ "continuing",
1522
+ "awaiting_permission",
1523
+ "awaiting_subagent",
1524
+ "awaiting_dag_children",
1525
+ "paused",
1526
+ "completed",
1527
+ "failed",
1528
+ "aborted"
1529
+ ];
1530
+ /**
1496
1531
  * ========================================
1497
1532
  * Client to Server Actions
1498
1533
  * ========================================
@@ -2206,6 +2241,10 @@ const SceneCommandSchema = z.discriminatedUnion("type", [
2206
2241
  type: z.literal("update_metadata"),
2207
2242
  id: z.string(),
2208
2243
  patch: z.record(z.string(), z.unknown())
2244
+ }),
2245
+ z.object({
2246
+ type: z.literal("dungeon_expired"),
2247
+ dungeonId: z.string()
2209
2248
  })
2210
2249
  ]);
2211
2250
  /** Client → Server: send scene commands to broadcast to all HUD clients */
@@ -2666,6 +2705,21 @@ const SubagentIterationStepAction = z.object({
2666
2705
  iteration: z.number(),
2667
2706
  step: AgentStepSchema
2668
2707
  });
2708
+ /**
2709
+ * Incremental token delta emitted while the subagent's LLM call is streaming
2710
+ * mid-iteration (#8774). Lets the parent UI render partial responses live
2711
+ * instead of waiting 3–5s for the full step to land. The client appends
2712
+ * `delta` to a per-(childExecutionId, iteration) buffer and clears it when
2713
+ * the iteration's terminal step arrives via `subagent_iteration_step`.
2714
+ */
2715
+ const SubagentTextDeltaAction = z.object({
2716
+ action: z.literal("subagent_text_delta"),
2717
+ executionId: z.string(),
2718
+ childExecutionId: z.string(),
2719
+ agentName: z.string(),
2720
+ iteration: z.number(),
2721
+ delta: z.string()
2722
+ });
2669
2723
  const SubagentCompletedAction = z.object({
2670
2724
  action: z.literal("subagent_completed"),
2671
2725
  executionId: z.string(),
@@ -2683,6 +2737,20 @@ const SubagentFailedAction = z.object({
2683
2737
  isTimeout: z.boolean().optional(),
2684
2738
  partialAnswer: z.string().optional()
2685
2739
  });
2740
+ /**
2741
+ * Humanized in-flight status for a subagent (#8775). Emitted by
2742
+ * `ServerSubagentOrchestrator` for each `action` step the child agent emits
2743
+ * (e.g. "Searching...", "Reading file..."). Lets `SubagentStepNest` show what
2744
+ * the child is doing right now instead of a static "N iterations · running"
2745
+ * label. The previous wiring routed this string to the parent's `progress`
2746
+ * event, which dropped the child correlation — hence a dedicated event.
2747
+ */
2748
+ const SubagentProgressAction = z.object({
2749
+ action: z.literal("subagent_progress"),
2750
+ executionId: z.string(),
2751
+ childExecutionId: z.string(),
2752
+ status: z.string().max(120)
2753
+ });
2686
2754
  const PermissionRequestAction = z.object({
2687
2755
  action: z.literal("permission_request"),
2688
2756
  executionId: z.string(),
@@ -2690,11 +2758,36 @@ const PermissionRequestAction = z.object({
2690
2758
  toolInput: z.unknown(),
2691
2759
  iteration: z.number()
2692
2760
  });
2761
+ /**
2762
+ * Persisted snapshot of a non-background child subagent execution. Replayed on
2763
+ * reconnect (#8695) so `SubagentStepNest` can re-render the nested iteration
2764
+ * trace under the parent's `delegate_to_agent` action step after a hard refresh
2765
+ * or once the parent run has terminated. Background children are excluded —
2766
+ * they surface via the header badge + completion toast (#8341), not inline
2767
+ * nesting.
2768
+ *
2769
+ * `agentName` is sourced from the child doc's `subagentConfig` (now persisted
2770
+ * at creation time for every subagent — see `agentExecutor.ts` `onStart`). A
2771
+ * generic fallback ("Subagent") is used for legacy docs without it. Empty
2772
+ * `steps` is valid for an in-flight in-process child whose terminal write
2773
+ * hasn't landed yet — the nest renders the agent header alone in that case.
2774
+ */
2775
+ const ChildExecutionSnapshotSchema = z.object({
2776
+ executionId: z.string(),
2777
+ agentName: z.string(),
2778
+ model: z.string().optional(),
2779
+ status: z.enum(AGENT_EXECUTION_STATUSES),
2780
+ steps: z.array(AgentStepSchema),
2781
+ totalCredits: z.number().optional(),
2782
+ finalAnswer: z.string().optional(),
2783
+ error: z.string().optional(),
2784
+ isTimeout: z.boolean().optional()
2785
+ });
2693
2786
  const ReconnectResultAction = z.object({
2694
2787
  action: z.literal("reconnect_result"),
2695
2788
  found: z.boolean(),
2696
2789
  executionId: z.string().optional(),
2697
- status: z.string().optional(),
2790
+ status: z.enum(AGENT_EXECUTION_STATUSES).optional(),
2698
2791
  pendingPermission: z.object({
2699
2792
  toolName: z.string(),
2700
2793
  toolInput: z.unknown(),
@@ -2703,7 +2796,9 @@ const ReconnectResultAction = z.object({
2703
2796
  totalCreditsUsed: z.number().optional(),
2704
2797
  iterationCount: z.number().optional(),
2705
2798
  steps: z.array(AgentStepSchema).optional(),
2706
- stepsTruncated: z.boolean().optional()
2799
+ stepsTruncated: z.boolean().optional(),
2800
+ children: z.array(ChildExecutionSnapshotSchema).optional(),
2801
+ childrenTruncated: z.boolean().optional()
2707
2802
  });
2708
2803
  z.discriminatedUnion("action", [
2709
2804
  DataSubscribeRequestAction,
@@ -2776,8 +2871,10 @@ z.discriminatedUnion("action", [
2776
2871
  AbortAcknowledgedAction,
2777
2872
  SubagentStartedAction,
2778
2873
  SubagentIterationStepAction,
2874
+ SubagentTextDeltaAction,
2779
2875
  SubagentCompletedAction,
2780
2876
  SubagentFailedAction,
2877
+ SubagentProgressAction,
2781
2878
  PermissionRequestAction,
2782
2879
  ReconnectResultAction
2783
2880
  ]);
@@ -3624,11 +3721,14 @@ let BedrockEmbeddingModel = /* @__PURE__ */ function(BedrockEmbeddingModel) {
3624
3721
  BedrockEmbeddingModel["TITAN_TEXT_EMBEDDINGS_V2"] = "amazon.titan-embed-text-v2:0";
3625
3722
  return BedrockEmbeddingModel;
3626
3723
  }({});
3627
- z.union([
3724
+ const SupportedEmbeddingModelSchema = z.union([
3628
3725
  z.enum(OpenAIEmbeddingModel),
3629
3726
  z.enum(VoyageAIEmbeddingModel),
3630
3727
  z.enum(BedrockEmbeddingModel)
3631
3728
  ]);
3729
+ function isSupportedEmbeddingModel(model) {
3730
+ return SupportedEmbeddingModelSchema.safeParse(model).success;
3731
+ }
3632
3732
  z.enum([
3633
3733
  "openaiDemoKey",
3634
3734
  "anthropicDemoKey",
@@ -3706,6 +3806,10 @@ z.enum([
3706
3806
  "PotionQuestApiKey",
3707
3807
  "EnablePotionQuest",
3708
3808
  "EnableTavernQuestBoardContext",
3809
+ "EnableDungeonLifecycle",
3810
+ "MaxActiveDungeons",
3811
+ "DungeonSpawnIntervalHeartbeats",
3812
+ "DungeonTTLMinutes",
3709
3813
  "VectorThreshold",
3710
3814
  "bflApiKey",
3711
3815
  "EnableMCPServer",
@@ -3773,6 +3877,7 @@ z.enum([
3773
3877
  "EnableBmPiJira",
3774
3878
  "EnableQuantumCanvasser",
3775
3879
  "EnableQuantumCanvasserDefault",
3880
+ "EnableQworkSubmission",
3776
3881
  "EnableContextTelemetry",
3777
3882
  "contextTelemetryAlerts",
3778
3883
  "sreAgentConfig",
@@ -3787,10 +3892,13 @@ function makeStringSetting(config) {
3787
3892
  };
3788
3893
  }
3789
3894
  function makeNumberSetting(config) {
3895
+ let numberSchema = z.coerce.number();
3896
+ if (config.min !== void 0) numberSchema = numberSchema.min(config.min);
3897
+ if (config.max !== void 0) numberSchema = numberSchema.max(config.max);
3790
3898
  return {
3791
3899
  ...config,
3792
3900
  type: "number",
3793
- schema: z.coerce.number().prefault(config.defaultValue ?? 0)
3901
+ schema: numberSchema.prefault(config.defaultValue ?? 0)
3794
3902
  };
3795
3903
  }
3796
3904
  function makeBooleanSetting(config) {
@@ -4281,7 +4389,7 @@ const TriageResultSchema = z.object({
4281
4389
  matchedClosedIssue: z.object({
4282
4390
  issueNumber: z.number().int().min(1),
4283
4391
  title: z.string().min(1).max(TRL.matchesExisting.title.max),
4284
- closedAt: z.string()
4392
+ closedAt: z.string().nullish()
4285
4393
  }).nullable().optional(),
4286
4394
  fingerprint: z.string().length(40).regex(/^[a-f0-9]+$/).optional()
4287
4395
  });
@@ -4699,6 +4807,10 @@ const API_SERVICE_GROUPS = {
4699
4807
  key: "EnableQuantumCanvasserDefault",
4700
4808
  order: 81
4701
4809
  },
4810
+ {
4811
+ key: "EnableQworkSubmission",
4812
+ order: 82
4813
+ },
4702
4814
  {
4703
4815
  key: "EnableQuestMaster",
4704
4816
  order: 90
@@ -4766,6 +4878,22 @@ const API_SERVICE_GROUPS = {
4766
4878
  {
4767
4879
  key: "EnableTavernQuestBoardContext",
4768
4880
  order: 280
4881
+ },
4882
+ {
4883
+ key: "EnableDungeonLifecycle",
4884
+ order: 290
4885
+ },
4886
+ {
4887
+ key: "MaxActiveDungeons",
4888
+ order: 291
4889
+ },
4890
+ {
4891
+ key: "DungeonSpawnIntervalHeartbeats",
4892
+ order: 292
4893
+ },
4894
+ {
4895
+ key: "DungeonTTLMinutes",
4896
+ order: 293
4769
4897
  }
4770
4898
  ]
4771
4899
  },
@@ -4975,7 +5103,7 @@ const settingsMap = {
4975
5103
  DefaultAPIModel: makeStringSetting({
4976
5104
  key: "DefaultAPIModel",
4977
5105
  name: "Default API Model",
4978
- defaultValue: "gpt-5",
5106
+ defaultValue: "claude-sonnet-4-6",
4979
5107
  description: "The default AI model to use for API requests when no model is specified.",
4980
5108
  options: CHAT_MODELS,
4981
5109
  category: "AI",
@@ -5733,6 +5861,47 @@ const settingsMap = {
5733
5861
  group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5734
5862
  order: 280
5735
5863
  }),
5864
+ EnableDungeonLifecycle: makeBooleanSetting({
5865
+ key: "EnableDungeonLifecycle",
5866
+ name: "Enable Dungeon Lifecycle",
5867
+ defaultValue: false,
5868
+ description: "Kill-switch for the dungeon spawn/expire lifecycle. When disabled, new dungeons cannot be spawned. Active dungeons can always be dismissed and expiration cleanup always runs regardless of this setting.",
5869
+ category: "Experimental",
5870
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5871
+ order: 290
5872
+ }),
5873
+ MaxActiveDungeons: makeNumberSetting({
5874
+ key: "MaxActiveDungeons",
5875
+ name: "Max Active Dungeons Per Map",
5876
+ defaultValue: 1,
5877
+ min: 1,
5878
+ max: 10,
5879
+ description: "Maximum number of simultaneously active dungeons per map. Defaults to 1 for M4.5. Increase once multi-dungeon navigation is supported.",
5880
+ category: "Experimental",
5881
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5882
+ order: 291
5883
+ }),
5884
+ DungeonSpawnIntervalHeartbeats: makeNumberSetting({
5885
+ key: "DungeonSpawnIntervalHeartbeats",
5886
+ name: "Dungeon Auto-Spawn Interval (Heartbeats)",
5887
+ defaultValue: 6,
5888
+ min: 1,
5889
+ description: "Number of heartbeats between automatic dungeon spawns (future auto-spawn feature). Must be at least 1. Has no effect in M4.5 where dungeons are spawned manually by the DM.",
5890
+ category: "Experimental",
5891
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5892
+ order: 292
5893
+ }),
5894
+ DungeonTTLMinutes: makeNumberSetting({
5895
+ key: "DungeonTTLMinutes",
5896
+ name: "Dungeon Lifetime (Minutes)",
5897
+ defaultValue: 1440,
5898
+ min: 1,
5899
+ max: 43200,
5900
+ description: "How long a dungeon remains active before the heartbeat cron expires it. Default is 1440 minutes (24 hours). Must be between 1 and 43200 (30 days).",
5901
+ category: "Experimental",
5902
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5903
+ order: 293
5904
+ }),
5736
5905
  VectorThreshold: makeNumberSetting({
5737
5906
  key: "VectorThreshold",
5738
5907
  name: "Vector Threshold",
@@ -6354,6 +6523,16 @@ const settingsMap = {
6354
6523
  order: 81,
6355
6524
  dependsOn: "EnableQuantumCanvasser"
6356
6525
  }),
6526
+ EnableQworkSubmission: makeBooleanSetting({
6527
+ key: "EnableQworkSubmission",
6528
+ name: "Enable Q/Work Submission",
6529
+ defaultValue: false,
6530
+ description: "Enable submitting QAOA scheduler runs to Q/Work for cloud-based quantum computation. Requires \"Enable OptiHashi\" to be on.",
6531
+ category: "Experimental",
6532
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
6533
+ order: 82,
6534
+ dependsOn: "EnableQuantumCanvasser"
6535
+ }),
6357
6536
  EnableContextTelemetry: makeBooleanSetting({
6358
6537
  key: "EnableContextTelemetry",
6359
6538
  name: "Enable Context Telemetry",
@@ -6975,6 +7154,8 @@ const PromptMetaZodSchema = z.object({
6975
7154
  performance: PromptMetaPerformanceSchema.optional(),
6976
7155
  session: PromptMetaSessionSchema.optional(),
6977
7156
  questId: z.string().optional(),
7157
+ /** ISO 8601 timestamp of when this completion's data was finalized (set at completion end). */
7158
+ generatedAt: z.string().optional(),
6978
7159
  promptId: z.string().optional(),
6979
7160
  prompt: z.string().optional(),
6980
7161
  replyIds: z.array(z.string()).optional(),
@@ -8105,6 +8286,12 @@ z.object({
8105
8286
  });
8106
8287
  z.object({
8107
8288
  search: z.string().optional(),
8289
+ /**
8290
+ * Optional product-surface filter for session listing. When omitted, list
8291
+ * queries return only default sessions (those with no surface); when set,
8292
+ * they return only sessions for that surface (e.g. 'libreoncology').
8293
+ */
8294
+ surface: z.string().optional(),
8108
8295
  pagination: z.object({
8109
8296
  page: z.coerce.number().int().positive(),
8110
8297
  limit: z.coerce.number().int().positive()
@@ -10025,6 +10212,7 @@ const CliConfigSchema = z.object({
10025
10212
  temperature: z.number(),
10026
10213
  autoSave: z.boolean(),
10027
10214
  autoCompact: z.boolean().optional().prefault(true),
10215
+ autoUpdate: z.boolean().optional(),
10028
10216
  theme: z.enum(["light", "dark"]),
10029
10217
  exportFormat: z.enum(["markdown", "json"]),
10030
10218
  maxIterations: z.number().nullable().prefault(10),
@@ -10757,4 +10945,4 @@ var ConfigStore = class {
10757
10945
  }
10758
10946
  };
10759
10947
  //#endregion
10760
- export { ProjectEvents as $, GenerateImageToolCallSchema as A, dayjsConfig_default as At, InviteEvents as B, sanitizeTelemetryError as Bt, ElabsEvents as C, UnauthorizedError as Ct, ForbiddenError as D, VideoModels as Dt, FileEvents as E, VideoGenerationUsageTransaction as Et, ImageEditUsageTransaction as F, isGPTImage2Model as Ft, ModalEvents as G, buildRateLimitLogEntry as Gt, KnowledgeType as H, settingsMap as Ht, ImageGenerationUsageTransaction as I, isGPTImageModel as It, OpenAIEmbeddingModel as J, parseRateLimitHeaders as Jt, ModelBackend as K, extractSnippetMeta as Kt, ImageModels as L, isZodError as Lt, GenericCreditDeductTransaction as M, getDataLakeTags as Mt, HTTPError as N, getMcpProviderMetadata as Nt, FriendshipEvents as O, XAI_IMAGE_MODELS as Ot, HttpStatus as P, getViewById as Pt, ProfileEvents as Q, InboxEvents as R, obfuscateApiKey as Rt, DashboardParamsSchema as S, UiNavigationEvents as St, FeedbackEvents as T, VIDEO_SIZE_CONSTRAINTS as Tt, LLMEvents as U, validateJupyterKernelName as Ut, InviteType as V, secureParameters as Vt, MiscEvents as W, validateNotebookPath as Wt, Permission as X, OpenAIImageGenerationInput as Y, CollectionType as Yt, PermissionDeniedError as Z, ChatCompletionCreateInputSchema as _, TaskScheduleHandler as _t, ALERT_THRESHOLDS as a, ReceivedCreditTransaction as at, CompletionApiUsageTransaction as b, ToolUsageTransaction as bt, ApiKeyScope as c, ResearchModeParamsSchema as ct, ArtifactTypeSchema as d, ResearchTaskType as dt, PromptIntentSchema as et, AuthEvents as f, SessionEvents as ft, CREDIT_DEDUCT_TRANSACTION_TYPES as g, TagType as gt, BadRequestError as h, SupportedFabFileMimeTypes as ht, getEnvironmentName as i, RealtimeVoiceUsageTransaction as it, GenericCreditAddTransaction as j, getAccessibleDataLakes as jt, GEMINI_IMAGE_MODELS as k, b4mLLMTools as kt, ApiKeyType as l, ResearchTaskExecutionType as lt, BFL_SAFETY_TOLERANCE as m, SubscriptionCreditTransaction as mt, logger as n, PurchaseTransaction as nt, AiEvents as o, RechartsChartTypeList as ot, BFL_IMAGE_MODELS as p, SpeechToTextUsageTransaction as pt, NotFoundError as q, isNearLimit as qt, getApiUrl as r, QuestMasterParamsSchema as rt, ApiKeyEvents as s, RegInviteEvents as st, ConfigStore as t, PromptMetaZodSchema as tt, AppFileEvents as u, ResearchTaskPeriodicFrequencyType as ut, ChatModels as v, TextGenerationUsageTransaction as vt, FavoriteDocumentType as w, UnprocessableEntityError as wt, CorruptedFileError as x, TransferCreditTransaction as xt, ClaudeArtifactMimeTypes as y, TooManyRequestsError as yt, InternalServerError as z, resolveNavigationIntents as zt };
10948
+ export { ProjectEvents as $, GenerateImageToolCallSchema as A, dayjsConfig_default as At, InviteEvents as B, resolveNavigationIntents as Bt, ElabsEvents as C, UnauthorizedError as Ct, ForbiddenError as D, VideoModels as Dt, FileEvents as E, VideoGenerationUsageTransaction as Et, ImageEditUsageTransaction as F, isGPTImage2Model as Ft, ModalEvents as G, validateNotebookPath as Gt, KnowledgeType as H, secureParameters as Ht, ImageGenerationUsageTransaction as I, isGPTImageModel as It, OpenAIEmbeddingModel as J, isNearLimit as Jt, ModelBackend as K, buildRateLimitLogEntry as Kt, ImageModels as L, isSupportedEmbeddingModel as Lt, GenericCreditDeductTransaction as M, getDataLakeTags as Mt, HTTPError as N, getMcpProviderMetadata as Nt, FriendshipEvents as O, XAI_IMAGE_MODELS as Ot, HttpStatus as P, getViewById as Pt, ProfileEvents as Q, InboxEvents as R, isZodError as Rt, DashboardParamsSchema as S, UiNavigationEvents as St, FeedbackEvents as T, VIDEO_SIZE_CONSTRAINTS as Tt, LLMEvents as U, settingsMap as Ut, InviteType as V, sanitizeTelemetryError as Vt, MiscEvents as W, validateJupyterKernelName as Wt, Permission as X, CollectionType as Xt, OpenAIImageGenerationInput as Y, parseRateLimitHeaders as Yt, PermissionDeniedError as Z, ChatCompletionCreateInputSchema as _, TaskScheduleHandler as _t, ALERT_THRESHOLDS as a, ReceivedCreditTransaction as at, CompletionApiUsageTransaction as b, ToolUsageTransaction as bt, ApiKeyScope as c, ResearchModeParamsSchema as ct, ArtifactTypeSchema as d, ResearchTaskType as dt, PromptIntentSchema as et, AuthEvents as f, SessionEvents as ft, CREDIT_DEDUCT_TRANSACTION_TYPES as g, TagType as gt, BadRequestError as h, SupportedFabFileMimeTypes as ht, getEnvironmentName as i, RealtimeVoiceUsageTransaction as it, GenericCreditAddTransaction as j, getAccessibleDataLakes as jt, GEMINI_IMAGE_MODELS as k, b4mLLMTools as kt, ApiKeyType as l, ResearchTaskExecutionType as lt, BFL_SAFETY_TOLERANCE as m, SubscriptionCreditTransaction as mt, logger as n, PurchaseTransaction as nt, AiEvents as o, RechartsChartTypeList as ot, BFL_IMAGE_MODELS as p, SpeechToTextUsageTransaction as pt, NotFoundError as q, extractSnippetMeta as qt, getApiUrl as r, QuestMasterParamsSchema as rt, ApiKeyEvents as s, RegInviteEvents as st, ConfigStore as t, PromptMetaZodSchema as tt, AppFileEvents as u, ResearchTaskPeriodicFrequencyType as ut, ChatModels as v, TextGenerationUsageTransaction as vt, FavoriteDocumentType as w, UnprocessableEntityError as wt, CorruptedFileError as x, TransferCreditTransaction as xt, ClaudeArtifactMimeTypes as y, TooManyRequestsError as yt, InternalServerError as z, obfuscateApiKey as zt };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-C3tokQej.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-HRgwfPBk.mjs";
3
3
  //#region src/commands/apiCommand.ts
4
4
  /**
5
5
  * External API config command (--api-url / --reset-api)
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-DNcd24qN.mjs";
3
- import { n as compareSemver, r as fetchLatestVersion } from "../updateChecker-D67NPlS5.mjs";
2
+ import { t as version } from "../package-CaPvuP1F.mjs";
3
+ import { a as fetchLatestVersion, c as isNpmPrefixWritable, i as compareSemver } from "../updateChecker-C8xsNY2L.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync } from "child_process";
6
- import { constants, existsSync, promises } from "fs";
6
+ import { existsSync } from "fs";
7
7
  import { homedir } from "os";
8
8
  import path from "path";
9
9
  //#region src/commands/doctorCommand.ts
@@ -55,20 +55,16 @@ async function handleDoctorCommand() {
55
55
  encoding: "utf-8",
56
56
  timeout: 1e4
57
57
  }).trim();
58
- try {
59
- await promises.access(npmPrefix, constants.W_OK);
60
- results.push({
61
- name: "Global npm path",
62
- status: "pass",
63
- message: `${npmPrefix} (writable)`
64
- });
65
- } catch {
66
- results.push({
67
- name: "Global npm path",
68
- status: "warn",
69
- message: `${npmPrefix} (not writable — may need sudo for updates)`
70
- });
71
- }
58
+ if (await isNpmPrefixWritable(npmPrefix)) results.push({
59
+ name: "Global npm path",
60
+ status: "pass",
61
+ message: `${npmPrefix} (writable)`
62
+ });
63
+ else results.push({
64
+ name: "Global npm path",
65
+ status: "warn",
66
+ message: `${npmPrefix} (not writable — may need sudo for updates)`
67
+ });
72
68
  } catch {
73
69
  results.push({
74
70
  name: "Global npm path",
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-C3tokQej.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-HRgwfPBk.mjs";
3
3
  //#region src/commands/envCommand.ts
4
4
  /**
5
5
  * Environment switching for the `--dev` / `--prod` launch flags.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as generateCliTools, G as buildSystemPrompt, J as ReActAgent, N as loadContextFiles, P as PermissionManager, Q as SessionStore, S as ApiClient, T as FallbackLlmBackend, U as setWebSocketToolExecutor, X as CheckpointStore, Y as CustomCommandStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, k as McpManager, m as createCoordinateTaskTool, p as createWriteTodosTool, q as isReadOnlyTool, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-BhPOnNo3.mjs";
3
- import { n as logger, r as getApiUrl, t as ConfigStore } from "../ConfigStore-C3tokQej.mjs";
2
+ import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as generateCliTools, G as buildSystemPrompt, J as ReActAgent, N as loadContextFiles, P as PermissionManager, Q as SessionStore, S as ApiClient, T as FallbackLlmBackend, U as setWebSocketToolExecutor, X as CheckpointStore, Y as CustomCommandStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, k as McpManager, m as createCoordinateTaskTool, p as createWriteTodosTool, q as isReadOnlyTool, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-ChYlNt33.mjs";
3
+ import { n as logger, r as getApiUrl, t as ConfigStore } from "../ConfigStore-HRgwfPBk.mjs";
4
4
  import { t as DEFAULT_SANDBOX_CONFIG } from "../types-LyRNHOiS.mjs";
5
5
  import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-ChGlxSGQ.mjs";
6
6
  import { t as SandboxOrchestrator } from "../SandboxOrchestrator-BoINxbX4.mjs";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-C3tokQej.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-HRgwfPBk.mjs";
3
3
  //#region src/commands/mcpCommand.ts
4
4
  /**
5
5
  * External MCP commands (b4m mcp list, b4m mcp add, etc.)
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-DNcd24qN.mjs";
3
- import { i as forceCheckForUpdate } from "../updateChecker-D67NPlS5.mjs";
2
+ import { t as version } from "../package-CaPvuP1F.mjs";
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-C8xsNY2L.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
- import { execSync } from "child_process";
5
+ import { execSync, spawnSync } from "child_process";
6
+ import { createInterface } from "readline";
6
7
  //#region src/commands/updateCommand.ts
7
8
  /**
8
9
  * External update command (b4m update)
9
10
  * Checks for and installs CLI updates.
10
11
  * Runs outside the interactive CLI session.
11
12
  */
12
- const INSTALL_CMD = "npm install -g @bike4mind/cli@latest";
13
13
  function runGlobalInstall() {
14
14
  try {
15
15
  execSync(INSTALL_CMD, {
@@ -25,6 +25,121 @@ function runGlobalInstall() {
25
25
  return false;
26
26
  }
27
27
  }
28
+ /**
29
+ * Ask the user how to handle an available update (the `'ask'` preference).
30
+ * Plain readline (not Ink) because this runs in the bin bootstrap before the
31
+ * code-split app loads — the only window in which it's safe to install.
32
+ *
33
+ * Maps the [U/a/s/n] keys to a choice; an empty line defaults to `'update'`
34
+ * (capital `U` in the prompt is the default). The interface is fully torn down
35
+ * before returning so a Skip/Never fall-through leaves stdin clean for Ink.
36
+ */
37
+ async function promptUpdateChoice(currentVersion, latestVersion) {
38
+ if (!process.stdin.isTTY) return "skip";
39
+ console.log(`\n\x1b[33m ⬆ Update available: v${currentVersion} → v${latestVersion}\x1b[0m\n`);
40
+ console.log(" How would you like to handle updates?");
41
+ console.log(" [U] Update once (install now)");
42
+ console.log(" [A] Always (auto-update silently from now on)");
43
+ console.log(" [S] Skip (not now — ask again next launch)");
44
+ console.log(" [N] Never (manual `b4m update` only)\n");
45
+ const rl = createInterface({
46
+ input: process.stdin,
47
+ output: process.stdout
48
+ });
49
+ let answer;
50
+ try {
51
+ answer = await new Promise((resolve) => {
52
+ rl.on("close", () => resolve(null));
53
+ rl.question(" Choose [U/a/s/n]: ", resolve);
54
+ });
55
+ } finally {
56
+ rl.close();
57
+ }
58
+ if (answer === null) return "skip";
59
+ switch (answer.trim().toLowerCase()) {
60
+ case "a": return "always";
61
+ case "s": return "skip";
62
+ case "n": return "never";
63
+ default: return "update";
64
+ }
65
+ }
66
+ /**
67
+ * Install the latest version and re-exec into it so the session the user just
68
+ * opened runs the new code — zero version skew, no mid-session file-swap risk.
69
+ *
70
+ * spawnSync inherits the TTY so Ink renders normally in the child; the guard
71
+ * env prevents an update loop. npm install overwrites the global package in
72
+ * place, so re-running the same argv[1] bin path picks up the new code. On a
73
+ * successful spawn this never returns (it calls process.exit); it only returns
74
+ * when the install itself fails, so the caller falls through to the current
75
+ * version.
76
+ */
77
+ function installAndReexec(currentVersion, latestVersion) {
78
+ console.log(`\x1b[33m ⬆ Updating v${currentVersion} → v${latestVersion}…\x1b[0m`);
79
+ if (!runGlobalInstall()) return;
80
+ const child = spawnSync(process.argv[0], process.argv.slice(1), {
81
+ stdio: "inherit",
82
+ env: {
83
+ ...process.env,
84
+ [REEXEC_GUARD_ENV]: "1"
85
+ }
86
+ });
87
+ if (child.error) {
88
+ console.error(`\n Failed to launch the updated version: ${child.error.message}`);
89
+ console.error(" Re-run b4m to use the newly installed version.");
90
+ process.exit(1);
91
+ }
92
+ process.exit(child.status ?? (child.signal ? 1 : 0));
93
+ }
94
+ /**
95
+ * Auto-update on launch (Claude-Code-style), consent-first.
96
+ *
97
+ * Called from the bin bootstrap on the interactive path *before* the
98
+ * code-split app (`dist/index.mjs`) is imported — the only safe install window
99
+ * (running an install while dist chunks are loaded would crash them).
100
+ *
101
+ * Behaviour by `autoUpdate` preference once an update is available on a
102
+ * writable prefix:
103
+ * - `'auto'` → install silently and re-exec into the new version.
104
+ * - `'never'` → do nothing (the startup notify banner still informs).
105
+ * - `'ask'` → prompt the user: Update once / Always (persist `auto`) /
106
+ * Skip (ask again next launch) / Never (persist `never`).
107
+ *
108
+ * It is a safe no-op (returns without installing) when: already re-exec'd this
109
+ * launch, disabled via `B4M_AUTO_UPDATE=0`, not attached to a TTY, no update is
110
+ * available, the prefix needs sudo (notify banner informs instead), or the
111
+ * user declines the prompt. Any install/network failure is swallowed so the
112
+ * user is never blocked from launching the current version.
113
+ */
114
+ async function maybeAutoUpdateOnLaunch() {
115
+ if (!shouldAttemptAutoUpdate({ isTTY: Boolean(process.stdin.isTTY && process.stdout.isTTY) })) return;
116
+ const preference = await getAutoUpdatePreference();
117
+ if (preference === "never") return;
118
+ const currentVersion = version;
119
+ let result;
120
+ let timeoutHandle;
121
+ try {
122
+ result = await Promise.race([checkForUpdate(currentVersion), new Promise((resolve) => {
123
+ timeoutHandle = setTimeout(() => resolve(null), 3e3);
124
+ })]);
125
+ } catch {
126
+ return;
127
+ } finally {
128
+ if (timeoutHandle) clearTimeout(timeoutHandle);
129
+ }
130
+ if (!result?.updateAvailable) return;
131
+ if (!await isNpmPrefixWritable()) return;
132
+ if (preference === "ask") {
133
+ const choice = await promptUpdateChoice(currentVersion, result.latestVersion);
134
+ if (choice === "skip") return;
135
+ if (choice === "never") {
136
+ await setAutoUpdatePreference(false);
137
+ return;
138
+ }
139
+ if (choice === "always") await setAutoUpdatePreference(true);
140
+ }
141
+ installAndReexec(currentVersion, result.latestVersion);
142
+ }
28
143
  async function handleUpdateCommand() {
29
144
  const currentVersion = version;
30
145
  console.log(`Current version: v${currentVersion}`);
@@ -56,4 +171,4 @@ async function handleUpdateCommand() {
56
171
  else console.log("\nRipgrep restored.");
57
172
  }
58
173
  //#endregion
59
- export { handleUpdateCommand };
174
+ export { handleUpdateCommand, maybeAutoUpdateOnLaunch, promptUpdateChoice };