@quantiya/codevibe-core 2.0.48 → 2.0.50

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.
@@ -1696,6 +1696,34 @@ function fileStoreHasCredentials() {
1696
1696
  return !1;
1697
1697
  }
1698
1698
  }
1699
+ function isKeytarBinaryMissing(loadError) {
1700
+ return loadError !== null && /Cannot find module ['"][^'"]*keytar\.node['"]/.test(loadError);
1701
+ }
1702
+ function keytarInstallRoot() {
1703
+ try {
1704
+ let nodeRequire = eval("require"), packageDir = path4.dirname(nodeRequire.resolve("keytar/package.json"));
1705
+ return path4.dirname(path4.dirname(packageDir));
1706
+ } catch {
1707
+ return null;
1708
+ }
1709
+ }
1710
+ function keytarBinaryMissingMessage(loadError, installRoot, migratedToKeyring = !1) {
1711
+ return [
1712
+ "CodeVibe's keyring module (keytar) is installed without its native binary,",
1713
+ "so CodeVibe cannot read the credentials it keeps in the OS keyring.",
1714
+ "This is an incomplete installation, not a sign-in problem: `codevibe login`",
1715
+ "will not fix it. Repair the module with:",
1716
+ installRoot ? ` (cd "${installRoot}" && npm rebuild prebuild-install keytar)` : " npm rebuild prebuild-install keytar (in the directory whose node_modules holds keytar)",
1717
+ "(`codevibe update` also repairs this for the Claude Code plugin.)",
1718
+ // On a migrated machine the fail-closed guarantee must stay visible: file storage is
1719
+ // retired here, and using it would fork the device identity.
1720
+ ...migratedToKeyring ? ["Refusing to fall back to file storage \u2014 that would fork the device identity."] : [],
1721
+ // First line only: Node appends a multi-line "Require stack" that buries the fix.
1722
+ `(keytar load error: ${loadError.split(`
1723
+ `)[0]})`
1724
+ ].join(`
1725
+ `);
1726
+ }
1699
1727
  function autoSelectFileBackend(reason) {
1700
1728
  warnToStderr([
1701
1729
  "",
@@ -1790,7 +1818,15 @@ function initBackends() {
1790
1818
  backend = "keytar", logger.info("[keychain-backend] Using keytar (migrated-to-keyring tombstone present)"), markKeytarUsed();
1791
1819
  return;
1792
1820
  }
1793
- keytarModule = null, initError = new KeychainBackendUnavailableError(
1821
+ if (keytarModule = null, tombstoneKeytarError && isKeytarBinaryMissing(tombstoneKeytarError)) {
1822
+ initError = new KeychainBackendUnavailableError(
1823
+ keytarBinaryMissingMessage(tombstoneKeytarError, keytarInstallRoot(), !0)
1824
+ ), logger.warn(
1825
+ "[keychain-backend] migrated-to-keyring tombstone but keytar has no native binary \u2014 failing closed (incomplete installation)"
1826
+ );
1827
+ return;
1828
+ }
1829
+ initError = new KeychainBackendUnavailableError(
1794
1830
  [
1795
1831
  "This machine migrated its CodeVibe credentials to the OS keyring, but the",
1796
1832
  "keyring is not reachable in this session (no desktop session / D-Bus).",
@@ -1843,6 +1879,12 @@ function initBackends() {
1843
1879
  autoFileOrRefuse("keytar_load_failed");
1844
1880
  return;
1845
1881
  }
1882
+ if (keytarLoadError && isKeytarBinaryMissing(keytarLoadError)) {
1883
+ initError = new KeychainBackendUnavailableError(
1884
+ keytarBinaryMissingMessage(keytarLoadError, keytarInstallRoot())
1885
+ );
1886
+ return;
1887
+ }
1846
1888
  initError = new KeychainBackendUnavailableError(
1847
1889
  [
1848
1890
  "CodeVibe could not load the OS-native keyring (keytar).",
@@ -3380,6 +3422,21 @@ var queries = {
3380
3422
  }
3381
3423
  `
3382
3424
  ),
3425
+ // Hosted Gemma advisory text generation (browse synthesis, brainstorm, etc.).
3426
+ // Lambda decrypts input prompt via session key, dispatches to Bedrock Gemma,
3427
+ // encrypts response under session key, returns.
3428
+ generatePlannerAdvisory: (
3429
+ /* GraphQL */
3430
+ `
3431
+ mutation GeneratePlannerAdvisory($input: GeneratePlannerAdvisoryInput!) {
3432
+ generatePlannerAdvisory(input: $input) {
3433
+ advisoryText
3434
+ serverLatencyMs
3435
+ providerUsed
3436
+ }
3437
+ }
3438
+ `
3439
+ ),
3383
3440
  // CP-8 min Stage 1 R1 closure (HIGH-1) — applyUserDecision SDK call.
3384
3441
  // Implementation deviation from #C8M-6 literal wording: codevibe-core
3385
3442
  // does NOT take a runtime dep on `@quantiya/quorum-core` (per the
@@ -4644,6 +4701,20 @@ var RECONNECT_CONFIG = {
4644
4701
  function shouldDeliverSubscribedEvent(event, receiveDesktopEvents) {
4645
4702
  return !!(event.source === "MOBILE" /* MOBILE */ || receiveDesktopEvents && event.source === "DESKTOP" /* DESKTOP */);
4646
4703
  }
4704
+ var CREDENTIAL_STORAGE_UNAVAILABLE_EXIT_CODE = 78;
4705
+ function describeStoredAuthFailure(kind, detail) {
4706
+ return kind === "credential_storage_unavailable" ? {
4707
+ exitCode: CREDENTIAL_STORAGE_UNAVAILABLE_EXIT_CODE,
4708
+ message: [
4709
+ "CodeVibe could not read its stored credentials. This is not a sign-in problem.",
4710
+ detail ?? "The credential store is unavailable."
4711
+ ].join(`
4712
+ `)
4713
+ } : kind === "refresh_network" ? {
4714
+ exitCode: 1,
4715
+ message: "Could not reach the CodeVibe sign-in service to refresh your session. Check your network connection and try again; you are still signed in."
4716
+ } : { exitCode: 1, message: 'Not authenticated. Run "codevibe login" to sign in.' };
4717
+ }
4647
4718
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 12e4, MIN_HEARTBEAT_INTERVAL_MS = 1e3, MAX_HEARTBEAT_INTERVAL_MS = 3e5;
4648
4719
  function isRecognizedNonProductionEnvironment(env = process.env) {
4649
4720
  let canonicalEnv = getEnvironment(env);
@@ -4670,6 +4741,11 @@ var AppSyncClient = class _AppSyncClient {
4670
4741
  * round-1 Codex M1.
4671
4742
  */
4672
4743
  this.lastAuthFailureKind = null;
4744
+ /**
4745
+ * The credential-store error message behind a `credential_storage_unavailable`
4746
+ * failure (it names the cause and the fix). `null` for every other outcome.
4747
+ */
4748
+ this.lastAuthFailureDetail = null;
4673
4749
  /**
4674
4750
  * Sentinel set inside `performRefresh` / `callCognitoRefresh` when
4675
4751
  * the refresh round-trip fails with a network-shaped error (DNS,
@@ -4786,6 +4862,10 @@ var AppSyncClient = class _AppSyncClient {
4786
4862
  getLastAuthFailureKind() {
4787
4863
  return this.lastAuthFailureKind;
4788
4864
  }
4865
+ /** The credential-store error message behind a `credential_storage_unavailable` failure. */
4866
+ getLastAuthFailureDetail() {
4867
+ return this.lastAuthFailureDetail;
4868
+ }
4789
4869
  /**
4790
4870
  * Heuristic — does the error message look like a transient network
4791
4871
  * failure rather than an auth-token rejection? Used for refresh-path
@@ -4800,13 +4880,13 @@ var AppSyncClient = class _AppSyncClient {
4800
4880
  * Authenticate using stored OAuth tokens from keychain
4801
4881
  */
4802
4882
  async authenticateWithStoredTokens() {
4803
- this.lastAuthFailureKind = null;
4883
+ this.lastAuthFailureKind = null, this.lastAuthFailureDetail = null;
4804
4884
  try {
4805
4885
  let tokens;
4806
4886
  try {
4807
4887
  tokens = await keychainManager.getTokens(this.environment);
4808
4888
  } catch (error) {
4809
- return logger.error("[AppSyncClient] Could not read stored authentication tokens:", error), this.lastAuthFailureKind = "credential_storage_unavailable", !1;
4889
+ return logger.error("[AppSyncClient] Could not read stored authentication tokens:", error), this.lastAuthFailureKind = "credential_storage_unavailable", this.lastAuthFailureDetail = error instanceof Error ? error.message : String(error), !1;
4810
4890
  }
4811
4891
  if (!tokens)
4812
4892
  return logger.debug("[AppSyncClient] No stored tokens found"), this.lastAuthFailureKind = "no_tokens", !1;
@@ -5660,6 +5740,31 @@ var AppSyncClient = class _AppSyncClient {
5660
5740
  async pingPlanner(input) {
5661
5741
  return (await this.graphqlRequest(mutations.pingPlanner, { input })).data.pingPlanner;
5662
5742
  }
5743
+ /**
5744
+ * Hosted Gemma advisory text generation (browse synthesis, brainstorm, etc.).
5745
+ *
5746
+ * Implements `PlannerAppSyncTransport.generatePlannerAdvisory`.
5747
+ * Input `prompt` is base64 AES-GCM ciphertext under the session key.
5748
+ * Output `advisoryText` is base64 AES-GCM ciphertext under the session key.
5749
+ */
5750
+ async generatePlannerAdvisory(input) {
5751
+ if (!input || typeof input != "object")
5752
+ throw new Error("generatePlannerAdvisory: input object is required");
5753
+ if (!input.sessionId || typeof input.sessionId != "string")
5754
+ throw new Error("generatePlannerAdvisory: sessionId is required");
5755
+ if (!input.prompt || typeof input.prompt != "string")
5756
+ throw new Error("generatePlannerAdvisory: prompt ciphertext is required");
5757
+ if (input.prompt.length > 2e5)
5758
+ throw new Error(`generatePlannerAdvisory: prompt ciphertext exceeds 200,000 chars ceiling (${input.prompt.length})`);
5759
+ if (input.maxTokens !== void 0 && (input.maxTokens < 1 || input.maxTokens > 1536))
5760
+ throw new Error(`generatePlannerAdvisory: maxTokens must be between 1 and 1536 (got ${input.maxTokens})`);
5761
+ if (input.temperature !== void 0 && (input.temperature < 0 || input.temperature > 1))
5762
+ throw new Error(`generatePlannerAdvisory: temperature must be between 0 and 1 (got ${input.temperature})`);
5763
+ let response = await this.graphqlRequest(mutations.generatePlannerAdvisory, { input });
5764
+ if (!response?.data?.generatePlannerAdvisory)
5765
+ throw new Error("generatePlannerAdvisory: empty response returned from AppSync");
5766
+ return response.data.generatePlannerAdvisory;
5767
+ }
5663
5768
  // MARK: - CP-8 min applyUserDecision (Stage 1 R1 HIGH-1 closure)
5664
5769
  /**
5665
5770
  * CP-8 min Stage 1 R1 HIGH-1 closure (Hendry-approved 2026-05-19).
@@ -63041,30 +63146,30 @@ ${summary}`);
63041
63146
  numPredict: BROWSE_RETRY_NUM_PREDICT
63042
63147
  });
63043
63148
  if (signal?.aborted) return;
63044
- let retrySummary = sanitizeForTerminal(parseLocalGemmaBrowseSummary(retryRaw)).trim();
63149
+ let trustLine2 = runner?.providerKind === "hosted" ? "No code was changed." : "No hosted model was called and no code was changed.", retrySummary = sanitizeForTerminal(parseLocalGemmaBrowseSummary(retryRaw)).trim();
63045
63150
  if (retrySummary.length > 0) {
63046
63151
  if (signal?.aborted) return;
63047
63152
  advise(
63048
63153
  store,
63049
63154
  headerAdvised ? `${retrySummary}
63050
63155
 
63051
- No hosted model was called and no code was changed.` : `${header}
63156
+ ${trustLine2}` : `${header}
63052
63157
 
63053
63158
  ${retrySummary}
63054
63159
 
63055
- No hosted model was called and no code was changed.`
63160
+ ${trustLine2}`
63056
63161
  );
63057
63162
  return;
63058
63163
  }
63059
63164
  } catch (retryErr) {
63060
63165
  if (signal?.aborted) return;
63061
- logger.warn("[orchestration-shell] local browse advisory retry failed", {
63166
+ logger.warn("[orchestration-shell] browse advisory retry failed", {
63062
63167
  error: retryErr.message,
63063
63168
  runtimeLabel: runner.runtimeLabel
63064
63169
  });
63065
63170
  }
63066
63171
  if (signal?.aborted) return;
63067
- let extract = buildBrowseExtract(safeText);
63172
+ let isHosted = runner?.providerKind === "hosted", modelNoun = isHosted ? "The model" : "The local model", deadEndNoun = isHosted ? "the model" : "the local model", trustLine = isHosted ? "No code was changed." : "No hosted model was called and no code was changed.", extract = buildBrowseExtract(safeText);
63068
63173
  if (extract) {
63069
63174
  if (signal?.aborted) return;
63070
63175
  let extractPrefix = headerAdvised ? "" : `${header}
@@ -63072,18 +63177,18 @@ No hosted model was called and no code was changed.`
63072
63177
  `;
63073
63178
  advise(
63074
63179
  store,
63075
- `${extractPrefix}The local model couldn't summarize this page, so here is the extracted page text (an extract, not a summary):
63180
+ `${extractPrefix}${modelNoun} couldn't summarize this page, so here is the extracted page text (an extract, not a summary):
63076
63181
 
63077
63182
  ${extract}
63078
63183
 
63079
- ${BROWSE_AGENT_OFFER} No hosted model was called and no code was changed.`
63184
+ ${BROWSE_AGENT_OFFER} ${trustLine}`
63080
63185
  );
63081
63186
  return;
63082
63187
  }
63083
63188
  if (signal?.aborted) return;
63084
63189
  advise(
63085
63190
  store,
63086
- `Read ${dispFinal} but the local model couldn't summarize it. ${BROWSE_AGENT_OFFER} No hosted model was called and no code was changed.`
63191
+ `Read ${dispFinal} but ${deadEndNoun} couldn't summarize it. ${BROWSE_AGENT_OFFER} ${trustLine}`
63087
63192
  );
63088
63193
  }
63089
63194
  }
@@ -63225,30 +63330,30 @@ ${summary}`);
63225
63330
  if (signal?.aborted) return;
63226
63331
  let retryRaw = await runner.generateAdvisory(retryPrompt, { responseFormat: "text", numCtx: 16384, think: !1 });
63227
63332
  if (signal?.aborted) return;
63228
- let retrySummary = sanitizeForTerminal(parseLocalGemmaBrowseSummary(retryRaw)).trim();
63333
+ let trustLine2 = runner?.providerKind === "hosted" ? "No code was changed." : "No hosted model was called and no code was changed.", retrySummary = sanitizeForTerminal(parseLocalGemmaBrowseSummary(retryRaw)).trim();
63229
63334
  if (retrySummary.length > 0) {
63230
63335
  if (signal?.aborted) return;
63231
63336
  advise(
63232
63337
  store,
63233
63338
  headerAdvised ? `${retrySummary}
63234
63339
 
63235
- No hosted model was called and no code was changed.` : `${header}
63340
+ ${trustLine2}` : `${header}
63236
63341
 
63237
63342
  ${retrySummary}
63238
63343
 
63239
- No hosted model was called and no code was changed.`
63344
+ ${trustLine2}`
63240
63345
  );
63241
63346
  return;
63242
63347
  }
63243
63348
  } catch (retryErr) {
63244
63349
  if (signal?.aborted) return;
63245
- logger.warn("[orchestration-shell] local multi-browse retry failed", {
63350
+ logger.warn("[orchestration-shell] multi-browse retry failed", {
63246
63351
  error: retryErr.message,
63247
63352
  runtimeLabel: runner.runtimeLabel
63248
63353
  });
63249
63354
  }
63250
63355
  if (signal?.aborted) return;
63251
- let extracts = sources.map((s) => {
63356
+ let isHosted = runner?.providerKind === "hosted", modelNoun = isHosted ? "The model" : "The local model", deadEndNoun = isHosted ? "the model" : "the local model", trustLine = isHosted ? "No code was changed." : "No hosted model was called and no code was changed.", extracts = sources.map((s) => {
63252
63357
  let ext = buildBrowseExtract(s.content);
63253
63358
  return `[${s.id}] ${s.title}
63254
63359
  ${ext || "(No extract available)"}`;
@@ -63262,11 +63367,11 @@ ${ext || "(No extract available)"}`;
63262
63367
  `;
63263
63368
  advise(
63264
63369
  store,
63265
- `${extractPrefix}The local model couldn't summarize these pages, so here are the extracted highlights:
63370
+ `${extractPrefix}${modelNoun} couldn't summarize these pages, so here are the extracted highlights:
63266
63371
 
63267
63372
  ${extracts}
63268
63373
 
63269
- ${BROWSE_AGENT_OFFER} No hosted model was called and no code was changed.`
63374
+ ${BROWSE_AGENT_OFFER} ${trustLine}`
63270
63375
  );
63271
63376
  return;
63272
63377
  }
@@ -63274,7 +63379,7 @@ ${BROWSE_AGENT_OFFER} No hosted model was called and no code was changed.`
63274
63379
  let sourceCountLabel = sources.length === 1 ? "1 source" : `${sources.length} sources`;
63275
63380
  advise(
63276
63381
  store,
63277
- `Read ${sourceCountLabel} but the local model couldn't summarize ${sources.length === 1 ? "it" : "them"}. ${BROWSE_AGENT_OFFER} No hosted model was called and no code was changed.`
63382
+ `Read ${sourceCountLabel} but ${deadEndNoun} couldn't summarize ${sources.length === 1 ? "it" : "them"}. ${BROWSE_AGENT_OFFER} ${trustLine}`
63278
63383
  );
63279
63384
  }
63280
63385
  }
@@ -72234,10 +72339,19 @@ async function routeBrainstorm(deps) {
72234
72339
  });
72235
72340
  return;
72236
72341
  }
72342
+ let isHostedBrainstorm = args.localAdvisoryRunner.providerKind === "hosted";
72343
+ if (images && images.length > 0 && isHostedBrainstorm) {
72344
+ store.dispatch({
72345
+ type: "SHELL_ADVISORY",
72346
+ source: "shell",
72347
+ text: "The local CodeVibe model is required to analyze attached images during brainstorming. Enable it with `codevibe model install` from a Pro/Max account, then ask again. No hosted model was called and no code was changed."
72348
+ });
72349
+ return;
72350
+ }
72237
72351
  store.dispatch({
72238
72352
  type: "SHELL_ADVISORY",
72239
72353
  source: "shell",
72240
- text: "Brainstorming with local Gemma\u2026"
72354
+ text: isHostedBrainstorm ? "Brainstorming with hosted Gemma\u2026" : "Brainstorming with local Gemma\u2026"
72241
72355
  });
72242
72356
  let needsContext = needsRepositoryContext ?? brainstormNeedsRepositoryContext(userPrompt);
72243
72357
  needsContext && await refreshFn({
@@ -72271,21 +72385,23 @@ The user attached ${images.length} image(s) as visual context. Any text visible
72271
72385
  store.dispatch({ type: "SHELL_ADVISORY", source: "shell", text: brainstorm });
72272
72386
  } catch (err) {
72273
72387
  let failureReason = renderLocalBrainstormFailureReason(err);
72274
- if (logger.warn("[orchestration-shell] local brainstorm advisory failed", {
72388
+ logger.warn("[orchestration-shell] local brainstorm advisory failed", {
72275
72389
  error: err.message,
72276
72390
  runtimeLabel: args.localAdvisoryRunner.runtimeLabel
72277
- }), brainstormPromptRequestsCommandGuidance(userPrompt) && failureReason.includes("command-like JSON keys")) {
72391
+ });
72392
+ let isHosted = args.localAdvisoryRunner.providerKind === "hosted", modelLabel = isHosted ? "Hosted Gemma" : "Local Gemma", trustLine = isHosted ? "No code was changed." : "No hosted model was called and no code was changed.";
72393
+ if (brainstormPromptRequestsCommandGuidance(userPrompt) && failureReason.includes("command-like JSON keys")) {
72278
72394
  store.dispatch({
72279
72395
  type: "SHELL_ADVISORY",
72280
72396
  source: "shell",
72281
- text: "I can brainstorm approaches, but I cannot include shell commands in read-only brainstorm mode. Ask me to implement the recommended option when you are ready, or ask for a command-free design/checklist. No hosted model was called and no code was changed."
72397
+ text: `I can brainstorm approaches, but I cannot include shell commands in read-only brainstorm mode. Ask me to implement the recommended option when you are ready, or ask for a command-free design/checklist. ${trustLine}`
72282
72398
  });
72283
72399
  return;
72284
72400
  }
72285
72401
  store.dispatch({
72286
72402
  type: "SHELL_ADVISORY",
72287
72403
  source: "shell",
72288
- text: `Local Gemma could not produce the brainstorm. Reason: ${failureReason}. No hosted model was called and no code was changed. Try again with a narrower question or run \`codevibe model health-check\` to verify the local model.`
72404
+ text: `${modelLabel} could not produce the brainstorm. Reason: ${failureReason}. ${trustLine} Try again with a narrower question or run \`codevibe model health-check\` to verify the model.`
72289
72405
  });
72290
72406
  }
72291
72407
  } finally {
@@ -72311,10 +72427,19 @@ async function routeAdvisory(deps) {
72311
72427
  });
72312
72428
  return;
72313
72429
  }
72430
+ let isHostedAdvisory = args.localAdvisoryRunner.providerKind === "hosted";
72431
+ if (hasImages && isHostedAdvisory) {
72432
+ store.dispatch({
72433
+ type: "SHELL_ADVISORY",
72434
+ source: "shell",
72435
+ text: "The local CodeVibe model is required to analyze an attached image. Enable it with `codevibe model install` from a Pro/Max account, then ask again. No hosted model was called and no code was changed."
72436
+ });
72437
+ return;
72438
+ }
72314
72439
  store.dispatch({
72315
72440
  type: "SHELL_ADVISORY",
72316
72441
  source: "shell",
72317
- text: hasImages ? "Analyzing the attached image(s) with local Gemma\u2026" : "Answering with local Gemma\u2026"
72442
+ text: hasImages ? "Analyzing the attached image(s) with local Gemma\u2026" : isHostedAdvisory ? "Answering with hosted Gemma\u2026" : "Answering with local Gemma\u2026"
72318
72443
  });
72319
72444
  let MAX_ADVISORY_CLARIFICATIONS = 4, MAX_ADVISORY_CLARIFICATION_QUESTION_CHARS = 400, MAX_ADVISORY_CLARIFICATION_ANSWER_CHARS = 1e3, MAX_ADVISORY_CANONICAL_CONTEXT_CHARS = 6e3, MAX_ADVISORY_USER_PROMPT_CHARS = 4e3, MAX_ADVISORY_PROMPT_CHARS = 42e3, MAX_ADVISORY_PAGE_CHARS = 24e3, MAX_ADVISORY_STATUS_CHARS = 1500, MAX_ADVISORY_PAGE_TOKENS = 6e3, MAX_ADVISORY_PROMPT_TOKENS = 13500, statusText = "";
72320
72445
  try {
@@ -72448,21 +72573,21 @@ async function routeAdvisory(deps) {
72448
72573
  });
72449
72574
  return;
72450
72575
  }
72451
- let fallback = deps.fallbackSummary?.trim();
72576
+ let fallback = deps.fallbackSummary?.trim(), effectiveIsHosted = args.localAdvisoryRunner.providerKind === "hosted", modelNoun = effectiveIsHosted ? "The model" : "The local model", modelLabel = effectiveIsHosted ? "Hosted Gemma" : "Local Gemma", trustLine = effectiveIsHosted ? "No code was changed." : "No hosted model was called and no code was changed.", generationScope = effectiveIsHosted ? "hosted" : "local";
72452
72577
  if (fallback) {
72453
72578
  store.dispatch({
72454
72579
  type: "SHELL_ADVISORY",
72455
72580
  source: "shell",
72456
72581
  text: sanitizeForTerminal(`${fallback}
72457
72582
 
72458
- (Note: detailed local generation returned an empty answer; showing concise answer.)`)
72583
+ (Note: detailed ${generationScope} generation returned an empty answer; showing concise answer.)`)
72459
72584
  });
72460
72585
  return;
72461
72586
  }
72462
72587
  store.dispatch({
72463
72588
  type: "SHELL_ADVISORY",
72464
72589
  source: "shell",
72465
- text: hasImages ? "The local model returned no answer for the attached image(s). No code was changed." : "The local model returned an empty answer. Try rephrasing with a bit more detail. No code was changed."
72590
+ text: hasImages ? `${modelNoun} returned no answer for the attached image(s). ${trustLine}` : `${modelNoun} returned an empty answer. Try rephrasing with a bit more detail. ${trustLine}`
72466
72591
  });
72467
72592
  } catch (err) {
72468
72593
  logger.warn("[orchestration-shell] local advisory failed", {
@@ -72470,7 +72595,7 @@ async function routeAdvisory(deps) {
72470
72595
  hasImages,
72471
72596
  runtimeLabel: args.localAdvisoryRunner.runtimeLabel
72472
72597
  });
72473
- let fallback = deps.fallbackSummary?.trim();
72598
+ let fallback = deps.fallbackSummary?.trim(), effectiveIsHosted = args.localAdvisoryRunner.providerKind === "hosted", modelLabel = effectiveIsHosted ? "Hosted Gemma" : "Local Gemma", trustLine = effectiveIsHosted ? "No code was changed." : "No hosted model was called and no code was changed.", generationScope = effectiveIsHosted ? "hosted" : "local";
72474
72599
  if (fallback) {
72475
72600
  let reason = renderLocalBrainstormFailureReason(err);
72476
72601
  store.dispatch({
@@ -72478,14 +72603,14 @@ async function routeAdvisory(deps) {
72478
72603
  source: "shell",
72479
72604
  text: sanitizeForTerminal(`${fallback}
72480
72605
 
72481
- (Note: detailed local generation was unavailable: ${reason}. Showing concise answer.)`)
72606
+ (Note: detailed ${generationScope} generation was unavailable: ${reason}. Showing concise answer.)`)
72482
72607
  });
72483
72608
  return;
72484
72609
  }
72485
72610
  store.dispatch({
72486
72611
  type: "SHELL_ADVISORY",
72487
72612
  source: "shell",
72488
- text: hasImages ? `Local Gemma could not analyze the attached image(s). Reason: ${renderLocalBrainstormFailureReason(err)}. No hosted model was called and no code was changed.` : `Local Gemma could not answer. Reason: ${renderLocalBrainstormFailureReason(err)}. No hosted model was called and no code was changed.`
72613
+ text: hasImages ? `${modelLabel} could not analyze the attached image(s). Reason: ${renderLocalBrainstormFailureReason(err)}. ${trustLine}` : `${modelLabel} could not answer. Reason: ${renderLocalBrainstormFailureReason(err)}. ${trustLine}`
72489
72614
  });
72490
72615
  }
72491
72616
  }
@@ -75731,6 +75856,109 @@ Local planner error. Falling back to hosted Gemma planner on AWS.
75731
75856
  }
75732
75857
  };
75733
75858
 
75859
+ // src/planner/hosted-advisory.ts
75860
+ init_logger2();
75861
+ var HostedGemmaAdvisoryRunner = class {
75862
+ constructor(deps) {
75863
+ this.deps = deps;
75864
+ this.runtimeLabel = "Hosted Gemma (AWS Bedrock)";
75865
+ this.providerKind = "hosted";
75866
+ }
75867
+ async generateAdvisory(promptText, options) {
75868
+ if (options?.images && options.images.length > 0)
75869
+ throw new Error(
75870
+ "HostedGemmaAdvisoryRunner: image attachments are not supported by hosted Bedrock Gemma. A local vision model is required."
75871
+ );
75872
+ let sessionId = this.deps.activeSessionIdHolder.value;
75873
+ if (!sessionId)
75874
+ throw new Error("Hosted Gemma advisory generation failed: no active session ID");
75875
+ let sessionKey = await this.deps.sessionKeyResolver.getSessionKey(sessionId);
75876
+ if (!sessionKey)
75877
+ throw new Error(`Hosted Gemma advisory generation failed: session key unresolved for session ${sessionId}`);
75878
+ let encryptedPrompt = this.deps.crypto.encryptString(promptText, sessionKey), maxTokens = options?.numPredict, temperature = options?.temperature, start = Date.now();
75879
+ try {
75880
+ let response = await this.deps.transport.generatePlannerAdvisory({
75881
+ sessionId,
75882
+ prompt: encryptedPrompt,
75883
+ ...maxTokens !== void 0 ? { maxTokens } : {},
75884
+ ...temperature !== void 0 ? { temperature } : {}
75885
+ });
75886
+ return this.deps.crypto.decryptString(response.advisoryText, sessionKey);
75887
+ } catch (err) {
75888
+ let elapsedMs = Date.now() - start;
75889
+ throw logger.warn("[hosted-advisory] generatePlannerAdvisory failed", {
75890
+ sessionId,
75891
+ elapsedMs,
75892
+ error: err?.message
75893
+ }), err;
75894
+ }
75895
+ }
75896
+ async warm() {
75897
+ return !0;
75898
+ }
75899
+ async unload() {
75900
+ return !0;
75901
+ }
75902
+ };
75903
+
75904
+ // src/planner/composite-advisory.ts
75905
+ init_logger2();
75906
+ var CompositeAdvisoryRunner = class {
75907
+ constructor(opts) {
75908
+ this.opts = opts;
75909
+ this.localHealthy = !!opts.localRunner;
75910
+ }
75911
+ get runtimeLabel() {
75912
+ return this.opts.tier === "FREE" || this.localHealthy ? this.opts.localRunner?.runtimeLabel ?? "Local Gemma" : this.opts.hostedRunner.runtimeLabel;
75913
+ }
75914
+ get providerKind() {
75915
+ return this.opts.tier === "FREE" || this.localHealthy ? "local" : "hosted";
75916
+ }
75917
+ async generateAdvisory(promptText, options) {
75918
+ if (this.opts.tier === "FREE") {
75919
+ if (!this.opts.localRunner)
75920
+ throw new Error(
75921
+ "LocalModelUnavailable: local Gemma is not installed or enabled. Free tier operates in local-only mode."
75922
+ );
75923
+ return this.opts.localRunner.generateAdvisory(promptText, options);
75924
+ }
75925
+ if (options?.images && options.images.length > 0) {
75926
+ if (!this.localHealthy || !this.opts.localRunner)
75927
+ throw new Error(
75928
+ "LocalModelUnavailable: local CodeVibe vision model is required to analyze attached images."
75929
+ );
75930
+ return this.opts.localRunner.generateAdvisory(promptText, options);
75931
+ }
75932
+ if (this.localHealthy && this.opts.localRunner)
75933
+ try {
75934
+ return await this.opts.localRunner.generateAdvisory(promptText, options);
75935
+ } catch (err) {
75936
+ logger.warn("[composite-advisory] local advisory failed; falling back to hosted Bedrock Gemma", {
75937
+ error: err?.message
75938
+ }), this.localHealthy = !1, this.opts.onFallbackToHosted?.(err);
75939
+ }
75940
+ return this.opts.hostedRunner.generateAdvisory(promptText, options);
75941
+ }
75942
+ async warm() {
75943
+ if (this.opts.localRunner?.warm)
75944
+ try {
75945
+ return await this.opts.localRunner.warm();
75946
+ } catch {
75947
+ return !1;
75948
+ }
75949
+ return !0;
75950
+ }
75951
+ async unload() {
75952
+ if (this.opts.localRunner?.unload)
75953
+ try {
75954
+ return await this.opts.localRunner.unload();
75955
+ } catch {
75956
+ return !1;
75957
+ }
75958
+ return !0;
75959
+ }
75960
+ };
75961
+
75734
75962
  // src/session/session-rekey.ts
75735
75963
  init_logger2();
75736
75964
 
@@ -77110,7 +77338,7 @@ function renderPreflight(agents, auth, connectivity, orphans) {
77110
77338
  let reason = agent.error ? `(${agent.error})` : "(not in $PATH)";
77111
77339
  console.log(` [${colors.yellow}missing${colors.reset}] ${agent.name} ${colors.dim}${reason}${colors.reset}`);
77112
77340
  }
77113
- agents.filter((a) => a.installed).length === 0 && console.log(` ${colors.red}! At least one agent CLI (Claude, Codex, or Antigravity) is required.${colors.reset}`), console.log(), console.log(`${colors.bold}Authentication & Credentials:${colors.reset}`), auth.error ? console.log(` [${colors.red}error${colors.reset}] Auth check failed: ${auth.error}`) : !auth.authenticated && !auth.tokenExpired ? (console.log(` [${colors.yellow}missing${colors.reset}] Not authenticated.`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe login${colors.reset}${colors.dim}' to sign in.${colors.reset}`)) : auth.tokenExpired ? (console.log(` [${colors.yellow}expired${colors.reset}] Token expired for ${auth.email ?? "user"}`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe doctor --repair --yes${colors.reset}${colors.dim}' to refresh.${colors.reset}`)) : console.log(` [${colors.green}ok${colors.reset}] Authenticated as ${auth.email ?? "user"}${auth.expiresAt ? ` ${colors.dim}(valid until ${auth.expiresAt})${colors.reset}` : ""}`), auth.deviceIdentityError ? (console.log(` [${colors.red}error${colors.reset}] Device Identity: ${auth.deviceIdentityError}`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe login${colors.reset}${colors.dim}' to re-establish device credentials.${colors.reset}`)) : auth.deviceIdentityPresent ? console.log(` [${colors.green}ok${colors.reset}] Device Identity ${auth.deviceIdentityId ? `${colors.dim}(${auth.deviceIdentityId})${colors.reset}` : ""}`) : (console.log(` [${colors.yellow}missing${colors.reset}] Device Identity not found`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe login${colors.reset}${colors.dim}' to generate device credentials.${colors.reset}`));
77341
+ agents.filter((a) => a.installed).length === 0 && console.log(` ${colors.red}! At least one agent CLI (Claude, Codex, or Antigravity) is required.${colors.reset}`), console.log(), console.log(`${colors.bold}Authentication & Credentials:${colors.reset}`), auth.error ? console.log(` [${colors.red}error${colors.reset}] Auth check failed: ${auth.error}`) : !auth.authenticated && !auth.tokenExpired ? (console.log(` [${colors.yellow}missing${colors.reset}] Not authenticated.`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe login${colors.reset}${colors.dim}' to sign in.${colors.reset}`)) : auth.tokenExpired ? (console.log(` [${colors.yellow}expired${colors.reset}] Token expired for ${auth.email ?? "user"}`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe doctor --repair --yes${colors.reset}${colors.dim}' to refresh.${colors.reset}`)) : console.log(` [${colors.green}ok${colors.reset}] Authenticated as ${auth.email ?? "user"}${auth.expiresAt ? ` ${colors.dim}(valid until ${auth.expiresAt})${colors.reset}` : ""}`), auth.error ? console.log(` [${colors.yellow}unknown${colors.reset}] Device Identity: not checked (the credential store could not be read \u2014 see above)`) : auth.deviceIdentityError ? (console.log(` [${colors.red}error${colors.reset}] Device Identity: ${auth.deviceIdentityError}`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe login${colors.reset}${colors.dim}' to re-establish device credentials.${colors.reset}`)) : auth.deviceIdentityPresent ? console.log(` [${colors.green}ok${colors.reset}] Device Identity ${auth.deviceIdentityId ? `${colors.dim}(${auth.deviceIdentityId})${colors.reset}` : ""}`) : (console.log(` [${colors.yellow}missing${colors.reset}] Device Identity not found`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe login${colors.reset}${colors.dim}' to generate device credentials.${colors.reset}`));
77114
77342
  let backendName = auth.vendorKeys.custodyBackend === "keytar" ? "OS Keychain" : "File Store";
77115
77343
  if (console.log(` [${auth.vendorKeys.anthropic ? colors.green + "ok" : colors.dim + "none"}${colors.reset}] Anthropic Vendor Key ${colors.dim}(${auth.vendorKeys.anthropic ? backendName : "not set"})${colors.reset}`), console.log(` [${auth.vendorKeys.openai ? colors.green + "ok" : colors.dim + "none"}${colors.reset}] OpenAI Credential ${colors.dim}(${auth.vendorKeys.openai ? backendName : "not set"})${colors.reset}`), console.log(), console.log(`${colors.bold}Backend Connectivity:${colors.reset}`), connectivity.appsyncReachable ? console.log(` [${colors.green}ok${colors.reset}] AppSync API (${connectivity.latencyMs ?? 0}ms) ${colors.dim}${connectivity.appsyncUrl}${colors.reset}`) : console.log(` [${colors.red}unreachable${colors.reset}] AppSync API: ${connectivity.error ?? "unreachable"} ${colors.dim}(${connectivity.appsyncUrl})${colors.reset}`), console.log(), console.log(`${colors.bold}Workspace & Process State:${colors.reset}`), orphans.error ? console.log(` [${colors.red}error${colors.reset}] Orphan inspection failed: ${orphans.error}`) : orphans.orphanedShadowCount > 0 || orphans.deadProcessOwnersCount > 0 ? (console.log(` [${colors.yellow}residue${colors.reset}] ${orphans.orphanedShadowCount} orphaned worktree shadow(s), ${orphans.deadProcessOwnersCount} dead process registration(s)`), console.log(` ${colors.dim}Run '${colors.reset}${colors.bold}codevibe doctor --repair --yes${colors.reset}${colors.dim}' to prune.${colors.reset}`)) : console.log(` [${colors.green}ok${colors.reset}] Clean \u2014 0 orphaned shadows, 0 dead process registrations.`), orphans.retainedStateDiagnostics && orphans.retainedStateDiagnostics.length > 0)
77116
77344
  for (let diag of orphans.retainedStateDiagnostics)
@@ -80294,13 +80522,20 @@ async function requireCliAuthentication(client) {
80294
80522
  }
80295
80523
  if (authenticated) return;
80296
80524
  let failureKind = client.getLastAuthFailureKind();
80297
- throw failureKind === "refresh_network" ? new CliAuthenticationError(
80298
- "auth_unavailable",
80299
- "CodeVibe could not verify your sign-in because the authentication service is unreachable. Check your connection and try again."
80300
- ) : failureKind === "credential_storage_unavailable" ? new CliAuthenticationError(
80301
- "credential_storage_unavailable",
80302
- "CodeVibe could not read stored sign-in credentials. Unlock or repair your credential store, then try again."
80303
- ) : new CliAuthenticationError(
80525
+ if (failureKind === "refresh_network")
80526
+ throw new CliAuthenticationError(
80527
+ "auth_unavailable",
80528
+ "CodeVibe could not verify your sign-in because the authentication service is unreachable. Check your connection and try again."
80529
+ );
80530
+ if (failureKind === "credential_storage_unavailable") {
80531
+ let detail = client.getLastAuthFailureDetail?.() ?? null;
80532
+ throw new CliAuthenticationError(
80533
+ "credential_storage_unavailable",
80534
+ "CodeVibe could not read stored sign-in credentials. Unlock or repair your credential store, then try again." + (detail ? `
80535
+ ${detail}` : "")
80536
+ );
80537
+ }
80538
+ throw new CliAuthenticationError(
80304
80539
  "auth_required",
80305
80540
  "Not signed in. Run `codevibe login`, then launch CodeVibe again."
80306
80541
  );
@@ -80939,13 +81174,14 @@ Note: --export will overwrite any existing file at <path>.
80939
81174
  try {
80940
81175
  authenticated = await appsyncClient.authenticateWithStoredTokens();
80941
81176
  } catch (e) {
80942
- return err(
80943
- `Not signed in \u2014 run \`codevibe login\` first (${e?.message ?? "auth unavailable"}).
80944
- `
80945
- ), 2;
81177
+ return err(`CodeVibe could not verify your sign-in (${e?.message ?? "auth unavailable"}).
81178
+ `), 2;
81179
+ }
81180
+ if (!authenticated) {
81181
+ let kind = appsyncClient.getLastAuthFailureKind?.() ?? null;
81182
+ return kind === "credential_storage_unavailable" || kind === "refresh_network" ? (err(`${describeStoredAuthFailure(kind, appsyncClient.getLastAuthFailureDetail?.() ?? null).message}
81183
+ `), 2) : (err("Not signed in \u2014 run `codevibe login` first.\n"), 2);
80946
81184
  }
80947
- if (!authenticated)
80948
- return err("Not signed in \u2014 run `codevibe login` first.\n"), 2;
80949
81185
  let sessionId = parsed.sessionId ?? null;
80950
81186
  if (!sessionId) {
80951
81187
  let resolveSessionId = deps?.resolveSessionIdFn ?? resolveActiveSessionId;
@@ -81250,7 +81486,8 @@ ${err.message}
81250
81486
  function buildProductionPlannerStack(appsyncClient, emitter) {
81251
81487
  let transport = {
81252
81488
  classifyPlannerPrompt: (input) => appsyncClient.classifyPlannerPrompt(input),
81253
- pingPlanner: (input) => appsyncClient.pingPlanner(input)
81489
+ pingPlanner: (input) => appsyncClient.pingPlanner(input),
81490
+ generatePlannerAdvisory: (input) => appsyncClient.generatePlannerAdvisory(input)
81254
81491
  }, cache = new PlannerCacheLayer(), adaptedEmitter = async (e) => {
81255
81492
  await emitter({
81256
81493
  sessionId: e.sessionId,
@@ -81291,9 +81528,18 @@ function buildProductionPlannerStack(appsyncClient, emitter) {
81291
81528
  sessionKeyResolver,
81292
81529
  adaptedEmitter
81293
81530
  ), realSetActiveSession = adapter.setActiveSession.bind(adapter);
81294
- return adapter.setActiveSession = (sessionId) => {
81531
+ adapter.setActiveSession = (sessionId) => {
81295
81532
  activeSessionIdHolder.value = sessionId, realSetActiveSession(sessionId);
81296
- }, { adapter, cache, health };
81533
+ };
81534
+ let hostedAdvisoryRunner = new HostedGemmaAdvisoryRunner({
81535
+ transport: {
81536
+ generatePlannerAdvisory: (input) => appsyncClient.generatePlannerAdvisory(input)
81537
+ },
81538
+ crypto: crypto33,
81539
+ sessionKeyResolver,
81540
+ activeSessionIdHolder
81541
+ });
81542
+ return { adapter, cache, health, hostedAdvisoryRunner };
81297
81543
  }
81298
81544
  function resolvePlannerAdapter(args) {
81299
81545
  let env = args.env ?? process.env, forceHosted = env.CODEVIBE_FORCE_HOSTED_PLANNER === "1" || env.CODEVIBE_PLANNER_PROVIDER === "hosted", tier = args.tier, isPaidTier = tier === "PRO" || tier === "MAX";
@@ -81304,7 +81550,7 @@ function resolvePlannerAdapter(args) {
81304
81550
  adapter: hostedStack.adapter,
81305
81551
  runtimeKind: "hosted",
81306
81552
  runtimeLabel: "Hosted Gemma (AWS Bedrock)",
81307
- advisoryRunner: args.localPlanner.advisoryRunner,
81553
+ advisoryRunner: hostedStack.hostedAdvisoryRunner,
81308
81554
  classifyRunner: args.localPlanner.classifyRunner,
81309
81555
  cache: hostedStack.cache,
81310
81556
  health: hostedStack.health,
@@ -81336,6 +81582,10 @@ function resolvePlannerAdapter(args) {
81336
81582
  onNotice: (msg) => {
81337
81583
  process.stdout.write(msg);
81338
81584
  }
81585
+ }), compositeAdvisoryRunner = new CompositeAdvisoryRunner({
81586
+ localRunner: args.localPlanner.advisoryRunner,
81587
+ hostedRunner: hostedStack.hostedAdvisoryRunner,
81588
+ tier
81339
81589
  });
81340
81590
  return {
81341
81591
  adapter: compositeAdapter,
@@ -81345,7 +81595,7 @@ function resolvePlannerAdapter(args) {
81345
81595
  get runtimeLabel() {
81346
81596
  return compositeAdapter.runtimeLabel;
81347
81597
  },
81348
- advisoryRunner: args.localPlanner.advisoryRunner,
81598
+ advisoryRunner: compositeAdvisoryRunner,
81349
81599
  classifyRunner: args.localPlanner.classifyRunner,
81350
81600
  cache: hostedStack.cache,
81351
81601
  health: hostedStack.health,
@@ -81378,7 +81628,7 @@ function resolvePlannerAdapter(args) {
81378
81628
  adapter: hostedStack.adapter,
81379
81629
  runtimeKind: "hosted",
81380
81630
  runtimeLabel: "Hosted Gemma (AWS Bedrock)",
81381
- advisoryRunner: null,
81631
+ advisoryRunner: hostedStack.hostedAdvisoryRunner,
81382
81632
  classifyRunner: args.localPlanner.classifyRunner,
81383
81633
  cache: hostedStack.cache,
81384
81634
  health: hostedStack.health,