@quantiya/codevibe-core 2.0.49 → 2.0.51

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).",
@@ -4659,6 +4701,20 @@ var RECONNECT_CONFIG = {
4659
4701
  function shouldDeliverSubscribedEvent(event, receiveDesktopEvents) {
4660
4702
  return !!(event.source === "MOBILE" /* MOBILE */ || receiveDesktopEvents && event.source === "DESKTOP" /* DESKTOP */);
4661
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
+ }
4662
4718
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 12e4, MIN_HEARTBEAT_INTERVAL_MS = 1e3, MAX_HEARTBEAT_INTERVAL_MS = 3e5;
4663
4719
  function isRecognizedNonProductionEnvironment(env = process.env) {
4664
4720
  let canonicalEnv = getEnvironment(env);
@@ -4685,6 +4741,11 @@ var AppSyncClient = class _AppSyncClient {
4685
4741
  * round-1 Codex M1.
4686
4742
  */
4687
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;
4688
4749
  /**
4689
4750
  * Sentinel set inside `performRefresh` / `callCognitoRefresh` when
4690
4751
  * the refresh round-trip fails with a network-shaped error (DNS,
@@ -4801,6 +4862,10 @@ var AppSyncClient = class _AppSyncClient {
4801
4862
  getLastAuthFailureKind() {
4802
4863
  return this.lastAuthFailureKind;
4803
4864
  }
4865
+ /** The credential-store error message behind a `credential_storage_unavailable` failure. */
4866
+ getLastAuthFailureDetail() {
4867
+ return this.lastAuthFailureDetail;
4868
+ }
4804
4869
  /**
4805
4870
  * Heuristic — does the error message look like a transient network
4806
4871
  * failure rather than an auth-token rejection? Used for refresh-path
@@ -4815,13 +4880,13 @@ var AppSyncClient = class _AppSyncClient {
4815
4880
  * Authenticate using stored OAuth tokens from keychain
4816
4881
  */
4817
4882
  async authenticateWithStoredTokens() {
4818
- this.lastAuthFailureKind = null;
4883
+ this.lastAuthFailureKind = null, this.lastAuthFailureDetail = null;
4819
4884
  try {
4820
4885
  let tokens;
4821
4886
  try {
4822
4887
  tokens = await keychainManager.getTokens(this.environment);
4823
4888
  } catch (error) {
4824
- 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;
4825
4890
  }
4826
4891
  if (!tokens)
4827
4892
  return logger.debug("[AppSyncClient] No stored tokens found"), this.lastAuthFailureKind = "no_tokens", !1;
@@ -5691,6 +5756,23 @@ var AppSyncClient = class _AppSyncClient {
5691
5756
  throw new Error("generatePlannerAdvisory: prompt ciphertext is required");
5692
5757
  if (input.prompt.length > 2e5)
5693
5758
  throw new Error(`generatePlannerAdvisory: prompt ciphertext exceeds 200,000 chars ceiling (${input.prompt.length})`);
5759
+ if (input.images !== void 0) {
5760
+ if (!Array.isArray(input.images))
5761
+ throw new Error("generatePlannerAdvisory: images must be an array of string ciphertexts");
5762
+ if (input.images.length > 5)
5763
+ throw new Error(`generatePlannerAdvisory: images count exceeds maximum of 5 (got ${input.images.length})`);
5764
+ let totalCiphertextLen = input.prompt.length;
5765
+ for (let i = 0; i < input.images.length; i++) {
5766
+ let imgCipher = input.images[i];
5767
+ if (typeof imgCipher != "string" || imgCipher.trim().length === 0)
5768
+ throw new Error(`generatePlannerAdvisory: image at index ${i} must be a non-empty string ciphertext`);
5769
+ if (imgCipher.length > 27e5)
5770
+ throw new Error(`generatePlannerAdvisory: image at index ${i} exceeds ciphertext limit of 2,700,000 chars (got ${imgCipher.length})`);
5771
+ totalCiphertextLen += imgCipher.length;
5772
+ }
5773
+ if (totalCiphertextLen > 45e5)
5774
+ throw new Error(`generatePlannerAdvisory: total request ciphertext exceeds 4,500,000 chars ceiling (${totalCiphertextLen})`);
5775
+ }
5694
5776
  if (input.maxTokens !== void 0 && (input.maxTokens < 1 || input.maxTokens > 1536))
5695
5777
  throw new Error(`generatePlannerAdvisory: maxTokens must be between 1 and 1536 (got ${input.maxTokens})`);
5696
5778
  if (input.temperature !== void 0 && (input.temperature < 0 || input.temperature > 1))
@@ -17270,6 +17352,14 @@ function attachmentsToBase64(attachments, opts) {
17270
17352
  }
17271
17353
  return { images, failed };
17272
17354
  }
17355
+ function isBlindImageClarification(prompt, clarifyingQuestion) {
17356
+ if (clarifyingQuestion && clarifyingQuestion.trim().length > 0) {
17357
+ let q = clarifyingQuestion.toLowerCase();
17358
+ return /what .*like me to do with the (screenshot|image)/i.test(q) || /what .*want me to do with the (screenshot|image)/i.test(q) || /how should i use (that|this) information/i.test(q) || /information from the (screenshot|image)/i.test(q) || /rephrase that as a specific command or request/i.test(q) || /cannot (see|view|read|inspect) (the )?(image|screenshot)/i.test(q) || /describe what you('d| would) like me to do with (the )?(screenshot|image)/i.test(q);
17359
+ }
17360
+ let p = prompt.toLowerCase();
17361
+ return /what('s| is| do you see) (in |on )?(the |this )?(screenshot|image|picture)/i.test(p) || /what does (the |this )?(screenshot|image|picture) (show|contain|depict)/i.test(p) || /what (shape|color|text) is (in |on )?(this |the )?(image|screenshot)/i.test(p);
17362
+ }
17273
17363
 
17274
17364
  // src/orchestration-shell/slash-suggest.ts
17275
17365
  function isSlashCommandInProgress(value) {
@@ -20927,6 +21017,310 @@ function assembleParagraphs(paragraphs, indices, maxChars) {
20927
21017
  `);
20928
21018
  return result.length > maxChars ? result.slice(0, maxChars) : result;
20929
21019
  }
21020
+ var GENERIC_RELEVANCE_STOPWORDS = /* @__PURE__ */ new Set([
21021
+ // Basic query, auxiliary, and question words
21022
+ "what",
21023
+ "when",
21024
+ "where",
21025
+ "which",
21026
+ "who",
21027
+ "whom",
21028
+ "whose",
21029
+ "why",
21030
+ "how",
21031
+ "this",
21032
+ "that",
21033
+ "these",
21034
+ "those",
21035
+ "there",
21036
+ "their",
21037
+ "theirs",
21038
+ "here",
21039
+ "about",
21040
+ "above",
21041
+ "below",
21042
+ "from",
21043
+ "with",
21044
+ "within",
21045
+ "without",
21046
+ "under",
21047
+ "over",
21048
+ "have",
21049
+ "having",
21050
+ "has",
21051
+ "had",
21052
+ "been",
21053
+ "would",
21054
+ "could",
21055
+ "should",
21056
+ "might",
21057
+ "must",
21058
+ "more",
21059
+ "most",
21060
+ "some",
21061
+ "many",
21062
+ "much",
21063
+ "also",
21064
+ "just",
21065
+ "only",
21066
+ "other",
21067
+ "such",
21068
+ "does",
21069
+ "did",
21070
+ "doing",
21071
+ "done",
21072
+ "will",
21073
+ "shall",
21074
+ "can",
21075
+ "cant",
21076
+ "cannot",
21077
+ "into",
21078
+ "onto",
21079
+ "upon",
21080
+ "than",
21081
+ "then",
21082
+ "them",
21083
+ "they",
21084
+ "your",
21085
+ "yours",
21086
+ // Conversational verbs and common nouns
21087
+ "explain",
21088
+ "explanation",
21089
+ "algorithm",
21090
+ "algorithms",
21091
+ "code",
21092
+ "coding",
21093
+ "function",
21094
+ "functions",
21095
+ "write",
21096
+ "writing",
21097
+ "create",
21098
+ "build",
21099
+ "make",
21100
+ "implement",
21101
+ "implementation",
21102
+ "test",
21103
+ "tests",
21104
+ "testing",
21105
+ "file",
21106
+ "files",
21107
+ "error",
21108
+ "errors",
21109
+ "run",
21110
+ "running",
21111
+ "use",
21112
+ "using",
21113
+ "work",
21114
+ "works",
21115
+ "working",
21116
+ "difference",
21117
+ "between",
21118
+ "better",
21119
+ "best",
21120
+ "example",
21121
+ "examples",
21122
+ "help",
21123
+ "please",
21124
+ "know",
21125
+ "want",
21126
+ "need",
21127
+ "like",
21128
+ "good",
21129
+ "bad",
21130
+ "worse",
21131
+ "worst",
21132
+ "mean",
21133
+ "means",
21134
+ "problem",
21135
+ "problems",
21136
+ "issue",
21137
+ "issues",
21138
+ "question",
21139
+ "questions",
21140
+ "answer",
21141
+ "answers",
21142
+ "search",
21143
+ "searching",
21144
+ "read",
21145
+ "reading",
21146
+ "view",
21147
+ "look",
21148
+ "looks",
21149
+ "looking",
21150
+ "show",
21151
+ "find",
21152
+ "get",
21153
+ "set",
21154
+ "data",
21155
+ "type",
21156
+ "types",
21157
+ "system",
21158
+ "method",
21159
+ "methods",
21160
+ "class",
21161
+ "classes",
21162
+ "object",
21163
+ "objects",
21164
+ "program",
21165
+ "programs",
21166
+ "language",
21167
+ "languages",
21168
+ "value",
21169
+ "values",
21170
+ "script",
21171
+ "prompt",
21172
+ "model",
21173
+ "query",
21174
+ "input",
21175
+ "output",
21176
+ "state",
21177
+ "process",
21178
+ "node",
21179
+ "call",
21180
+ // Conversational follow-ups, speaking, evaluation & opinions
21181
+ "say",
21182
+ "says",
21183
+ "said",
21184
+ "saying",
21185
+ "tell",
21186
+ "tells",
21187
+ "told",
21188
+ "sound",
21189
+ "sounds",
21190
+ "sounding",
21191
+ "think",
21192
+ "thinks",
21193
+ "thinking",
21194
+ "thought",
21195
+ "thoughts",
21196
+ "opinion",
21197
+ "opinions",
21198
+ "take",
21199
+ "takes",
21200
+ "critique",
21201
+ "critiques",
21202
+ "perspective",
21203
+ "perspectives",
21204
+ "viewpoint",
21205
+ "viewpoints",
21206
+ "summary",
21207
+ "summarize",
21208
+ "summarizes",
21209
+ "summarizing",
21210
+ "takeaway",
21211
+ "takeaways",
21212
+ "key",
21213
+ "keys",
21214
+ "pros",
21215
+ "cons",
21216
+ "pro",
21217
+ "con",
21218
+ "detail",
21219
+ "details",
21220
+ "information",
21221
+ "info",
21222
+ // Shopping, consumer evaluation, and general follow-up adjectives
21223
+ "worth",
21224
+ "buy",
21225
+ "buys",
21226
+ "buying",
21227
+ "bought",
21228
+ "purchase",
21229
+ "purchasing",
21230
+ "purchases",
21231
+ "cost",
21232
+ "costs",
21233
+ "costing",
21234
+ "price",
21235
+ "prices",
21236
+ "pricing",
21237
+ "expensive",
21238
+ "cheap",
21239
+ "recommend",
21240
+ "recommends",
21241
+ "recommended",
21242
+ "recommendation",
21243
+ "recommendations",
21244
+ "scratches",
21245
+ "scratch",
21246
+ "scratching",
21247
+ "easily",
21248
+ "easy",
21249
+ "hard",
21250
+ "completely",
21251
+ "thing",
21252
+ "things",
21253
+ "item",
21254
+ "items",
21255
+ "stuff",
21256
+ "possible",
21257
+ "possibility",
21258
+ "reverse",
21259
+ "reversing",
21260
+ "place",
21261
+ "places",
21262
+ // Page / Document references and meta-terms
21263
+ "page",
21264
+ "pages",
21265
+ "article",
21266
+ "articles",
21267
+ "site",
21268
+ "sites",
21269
+ "website",
21270
+ "websites",
21271
+ "url",
21272
+ "urls",
21273
+ "webpage",
21274
+ "webpages",
21275
+ "document",
21276
+ "documents",
21277
+ "post",
21278
+ "posts",
21279
+ "blog",
21280
+ "blogs",
21281
+ "review",
21282
+ "reviews",
21283
+ "author",
21284
+ "writer",
21285
+ "writers",
21286
+ "wrote",
21287
+ "source",
21288
+ "sources",
21289
+ "results",
21290
+ "result",
21291
+ "findings",
21292
+ "finding"
21293
+ ]);
21294
+ function isPromptRelevantToRetainedPage(prompt, retainedPage) {
21295
+ if (!retainedPage || !retainedPage.text || retainedPage.text.trim().length === 0)
21296
+ return !1;
21297
+ let cleanPrompt = (prompt || "").trim();
21298
+ if (!cleanPrompt) return !1;
21299
+ let pageTitle = (retainedPage.title || "").toLowerCase(), pageUrl = (retainedPage.url || "").toLowerCase(), DEICTIC_PHRASE_REGEX = /\b(this|that|the|current)\s+(?:web\s+)?(page|article|site|website|url|link|post|webpage|document|blog|review)\b/i, PREPOSITIONAL_PAGE_REGEX = /\b(from|on|in)\s+(the|this|that)\s+(?:web\s+)?(page|article|site|website|post|document|link|webpage)\b/i, AUTHOR_REF_REGEX = /\b(author|who wrote|who is the author)\b/i, CJK_DEICTIC_REGEX = /(这篇文章|该文章|这个网页|该网页|这个页面|该页面|这个网站|该网站|原文|作者|出处)/, CJK_EVALUATIVE_FOLLOWUP_REGEX = /(总结|概括|看法|观点|优缺点|评价|详细说说|你怎么看)/;
21300
+ if (DEICTIC_PHRASE_REGEX.test(cleanPrompt) || PREPOSITIONAL_PAGE_REGEX.test(cleanPrompt) || AUTHOR_REF_REGEX.test(cleanPrompt) || CJK_DEICTIC_REGEX.test(cleanPrompt) || CJK_EVALUATIVE_FOLLOWUP_REGEX.test(cleanPrompt))
21301
+ return !0;
21302
+ function tokenMatchesWordStart(token, text2) {
21303
+ if (!text2) return !1;
21304
+ if (/[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/.test(token))
21305
+ return text2.includes(token);
21306
+ let escaped = escapeRegex2(token);
21307
+ return /^[a-zA-Z0-9_]+$/.test(token) ? new RegExp(`\\b${escaped}`, "i").test(text2) : new RegExp(`(?<=^|[\\s\\p{P}])${escaped}`, "gui").test(text2);
21308
+ }
21309
+ let distinctiveTokens = extractQueryTokens(cleanPrompt).filter(
21310
+ (t) => t.length >= 3 && !GENERIC_RELEVANCE_STOPWORDS.has(t.toLowerCase())
21311
+ ), matchingTokens = distinctiveTokens.filter(
21312
+ (token) => tokenMatchesWordStart(token, pageTitle) || tokenMatchesWordStart(token, pageUrl) || tokenMatchesWordStart(token, retainedPage.text || "")
21313
+ );
21314
+ if (distinctiveTokens.length > 0 && matchingTokens.length === 0)
21315
+ return !1;
21316
+ let SUMMARY_FOLLOWUP_REGEX = /\b(summarize|summary|takeaways?|tl;?dr)\b|\b(pros\s+and\s+cons|pros|cons)(\s*\?|\s*$|\s+(of|about)\s+(it|this|that|them|the\s+(page|article|site|website|post|document|device|product|phone|display)))/i, OPINION_FOLLOWUP_REGEX = /\b(your\s+(opinion|thoughts?|take|critique|perspective))\b/i, THINK_FOLLOWUP_REGEX = /\bwhat do you think(\?|$|\s+(about|of)\s+(it|this|that|them|the\s+(page|article|site|website|post|document|device|product|phone|display)))/i, PRONOUN_FOLLOWUP_REGEX = /\b(is|does|can|will|should|would)\s+it\b/i, ABOUT_IT_REGEX = /\b(about|of)\s+(it|this|them)\b/i, TELL_ME_MORE_REGEX = /\b(tell me more|more details)\b/i;
21317
+ if ((SUMMARY_FOLLOWUP_REGEX.test(cleanPrompt) || OPINION_FOLLOWUP_REGEX.test(cleanPrompt) || THINK_FOLLOWUP_REGEX.test(cleanPrompt) || PRONOUN_FOLLOWUP_REGEX.test(cleanPrompt) || ABOUT_IT_REGEX.test(cleanPrompt) || TELL_ME_MORE_REGEX.test(cleanPrompt)) && (distinctiveTokens.length === 0 || matchingTokens.length >= 2 || matchingTokens.some((t) => t.length >= 6) || matchingTokens.length >= 1 && matchingTokens.length / distinctiveTokens.length >= 0.5))
21318
+ return !0;
21319
+ for (let token of distinctiveTokens)
21320
+ if (pageTitle.length > 0 && tokenMatchesWordStart(token, pageTitle) || pageUrl.length > 0 && tokenMatchesWordStart(token, pageUrl))
21321
+ return !0;
21322
+ return distinctiveTokens.length === 0 ? !1 : matchingTokens.length >= 2 || matchingTokens.some((t) => t.length >= 6);
21323
+ }
20930
21324
 
20931
21325
  // src/orchestration-shell/web/fetch.ts
20932
21326
  var http2 = __toESM(require("node:http")), https3 = __toESM(require("node:https")), dns2 = __toESM(require("node:dns")), zlib = __toESM(require("node:zlib")), import_node_string_decoder = require("node:string_decoder");
@@ -71309,7 +71703,7 @@ async function routeReadOnlyAgentMention(args) {
71309
71703
  localOnly: !0
71310
71704
  });
71311
71705
  }
71312
- let retainedPage = args.retainedPage !== void 0 ? args.retainedPage : getFreshRetainedBrowsePage(retainedBrowsePages, sessionId), hasRetainedPage = !!(retainedPage && retainedPage.text.trim().length > 0), preparedBrief = composeBrainstormPanelBrief({
71706
+ let rawRetainedPage = args.retainedPage !== void 0 ? args.retainedPage : getFreshRetainedBrowsePage(retainedBrowsePages, sessionId), retainedPage = rawRetainedPage && (args.retainedPage !== void 0 || isPromptRelevantToRetainedPage(requestBrief, rawRetainedPage)) ? rawRetainedPage : null, hasRetainedPage = !!(retainedPage && retainedPage.text.trim().length > 0), preparedBrief = composeBrainstormPanelBrief({
71313
71707
  priorContext,
71314
71708
  requestBrief,
71315
71709
  retainedPage
@@ -72275,14 +72669,6 @@ async function routeBrainstorm(deps) {
72275
72669
  return;
72276
72670
  }
72277
72671
  let isHostedBrainstorm = args.localAdvisoryRunner.providerKind === "hosted";
72278
- if (images && images.length > 0 && isHostedBrainstorm) {
72279
- store.dispatch({
72280
- type: "SHELL_ADVISORY",
72281
- source: "shell",
72282
- 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."
72283
- });
72284
- return;
72285
- }
72286
72672
  store.dispatch({
72287
72673
  type: "SHELL_ADVISORY",
72288
72674
  source: "shell",
@@ -72363,18 +72749,10 @@ async function routeAdvisory(deps) {
72363
72749
  return;
72364
72750
  }
72365
72751
  let isHostedAdvisory = args.localAdvisoryRunner.providerKind === "hosted";
72366
- if (hasImages && isHostedAdvisory) {
72367
- store.dispatch({
72368
- type: "SHELL_ADVISORY",
72369
- source: "shell",
72370
- 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."
72371
- });
72372
- return;
72373
- }
72374
72752
  store.dispatch({
72375
72753
  type: "SHELL_ADVISORY",
72376
72754
  source: "shell",
72377
- text: hasImages ? "Analyzing the attached image(s) with local Gemma\u2026" : isHostedAdvisory ? "Answering with hosted Gemma\u2026" : "Answering with local Gemma\u2026"
72755
+ text: hasImages ? isHostedAdvisory ? "Analyzing the attached image(s) with hosted Gemma\u2026" : "Analyzing the attached image(s) with local Gemma\u2026" : isHostedAdvisory ? "Answering with hosted Gemma\u2026" : "Answering with local Gemma\u2026"
72378
72756
  });
72379
72757
  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 = "";
72380
72758
  try {
@@ -72384,7 +72762,7 @@ async function routeAdvisory(deps) {
72384
72762
  statusText = "";
72385
72763
  }
72386
72764
  let retainedPage = deps.retainedPage, pageText = "";
72387
- if (retainedPage && retainedPage.text.trim().length > 0) {
72765
+ if (retainedPage && retainedPage.text.trim().length > 0 && isPromptRelevantToRetainedPage(userPrompt, retainedPage)) {
72388
72766
  let relevant = extractQueryRelevantSnippets(retainedPage.text, userPrompt, {
72389
72767
  maxChars: MAX_ADVISORY_PAGE_CHARS - 100
72390
72768
  });
@@ -72396,7 +72774,7 @@ async function routeAdvisory(deps) {
72396
72774
  canonicalContextText = rawContext.length > MAX_ADVISORY_CANONICAL_CONTEXT_CHARS ? `[older context omitted]
72397
72775
  ` + rawContext.slice(-MAX_ADVISORY_CANONICAL_CONTEXT_CHARS) : rawContext;
72398
72776
  }
72399
- let sanitizedUserPrompt = redactAbsoluteLocalPaths(userPrompt.trim()), boundedUserPrompt = sanitizedUserPrompt.length > MAX_ADVISORY_USER_PROMPT_CHARS ? sanitizedUserPrompt.slice(0, MAX_ADVISORY_USER_PROMPT_CHARS) + "\u2026" : sanitizedUserPrompt, advisoryClarifications = [];
72777
+ let sanitizedUserPrompt = redactAbsoluteLocalPaths(userPrompt.trim()), effectiveUserPrompt = hasImages && sanitizedUserPrompt.replace(/\[Image #\d+\]/g, "").trim().length === 0 ? "Describe what is shown in the attached image(s)." : sanitizedUserPrompt, boundedUserPrompt = effectiveUserPrompt.length > MAX_ADVISORY_USER_PROMPT_CHARS ? effectiveUserPrompt.slice(0, MAX_ADVISORY_USER_PROMPT_CHARS) + "\u2026" : effectiveUserPrompt, advisoryClarifications = [];
72400
72778
  if (deps.clarifications && deps.clarifications.length > 0) {
72401
72779
  let sanitized = deps.clarifications.map((c) => {
72402
72780
  let q = redactAbsoluteLocalPaths(c.question.trim()), a = redactAbsoluteLocalPaths(c.answer.trim());
@@ -72484,7 +72862,7 @@ async function routeAdvisory(deps) {
72484
72862
  chars: prompt.length
72485
72863
  }), pageText = "", prompt = buildPrompt());
72486
72864
  }
72487
- let advisoryRunner = args.localAdvisoryRunner, generate2 = (p) => advisoryRunner.generateAdvisory(p, {
72865
+ let advisoryRunner = args.localAdvisoryRunner, beforeProviderKind = advisoryRunner.providerKind, generate2 = (p) => advisoryRunner.generateAdvisory(p, {
72488
72866
  responseFormat: "text",
72489
72867
  numPredict: 1400,
72490
72868
  ...images.length > 0 ? { images } : {}
@@ -72499,6 +72877,12 @@ async function routeAdvisory(deps) {
72499
72877
  else
72500
72878
  throw err;
72501
72879
  }
72880
+ let afterProviderKind = advisoryRunner.providerKind;
72881
+ beforeProviderKind !== "hosted" && afterProviderKind === "hosted" && store.dispatch({
72882
+ type: "SHELL_ADVISORY",
72883
+ source: "shell",
72884
+ text: hasImages ? "Local model unavailable; falling back to hosted Gemma for image analysis. No code was changed." : "Local model unavailable; falling back to hosted Gemma. No code was changed."
72885
+ });
72502
72886
  let answer = sanitizeForTerminal(raw.trim());
72503
72887
  if (answer) {
72504
72888
  store.dispatch({
@@ -72525,11 +72909,25 @@ async function routeAdvisory(deps) {
72525
72909
  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}`
72526
72910
  });
72527
72911
  } catch (err) {
72528
- logger.warn("[orchestration-shell] local advisory failed", {
72912
+ let afterProviderKind = args.localAdvisoryRunner.providerKind;
72913
+ beforeProviderKind !== "hosted" && afterProviderKind === "hosted" && store.dispatch({
72914
+ type: "SHELL_ADVISORY",
72915
+ source: "shell",
72916
+ text: hasImages ? "Local model unavailable; falling back to hosted Gemma for image analysis. No code was changed." : "Local model unavailable; falling back to hosted Gemma. No code was changed."
72917
+ }), logger.warn("[orchestration-shell] local advisory failed", {
72529
72918
  error: err.message,
72530
72919
  hasImages,
72531
72920
  runtimeLabel: args.localAdvisoryRunner.runtimeLabel
72532
72921
  });
72922
+ let errMsg = err?.message ?? "";
72923
+ if (hasImages && /exceeds size limit|image limit exceeded|maximum 5 images|exceeds backend limit|exceeds ciphertext limit|total request ciphertext/i.test(errMsg)) {
72924
+ store.dispatch({
72925
+ type: "SHELL_ADVISORY",
72926
+ source: "shell",
72927
+ text: `Unable to analyze image: ${errMsg} No code was changed.`
72928
+ });
72929
+ return;
72930
+ }
72533
72931
  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";
72534
72932
  if (fallback) {
72535
72933
  let reason = renderLocalBrainstormFailureReason(err);
@@ -73903,18 +74301,21 @@ async function handleShellUserInput(deps) {
73903
74301
  clearance: "user",
73904
74302
  scope: {},
73905
74303
  purpose: "classification"
73906
- }).catch(() => "") : "", plannerInput = {
73907
- // IMAGE-ATTACHMENT-DESIGN.md §6 Role A: the classifier stays TEXT-hint-only —
73908
- // a prose `[N image(s) attached]` line rides INSIDE the prompt STRING (not a
73909
- // new PlannerInput field), enough to route a sparse "look at this" correctly;
73910
- // image BYTES never reach the forced-JSON classifier. Path-stripping in the
73911
- // prompt renderer removes the inline path regardless.
73912
- // IMAGE-ATTACHMENT-DESIGN.md §13 — strip `[Image #N]` chip labels from the
73913
- // classify prompt (the display/history text keeps them); the classifier routes
73914
- // off the prose + the `[N image(s) attached]` hint, not the chip token.
73915
- prompt: turnAttachments.length ? `${plannerPromptText.replace(/\[Image #\d+\]/g, "").replace(/[ \t]{2,}/g, " ").trim()}
74304
+ }).catch(() => "") : "", freshRetainedPage = getFreshRetainedBrowsePage(retainedBrowsePages, args.session.sessionId), isPromptRelevantToPage = freshRetainedPage ? isPromptRelevantToRetainedPage(plannerPromptText, freshRetainedPage) : !1, classifyPrompt = turnAttachments.length ? `${plannerPromptText.replace(/\[Image #\d+\]/g, "").replace(/[ \t]{2,}/g, " ").trim()}
73916
74305
 
73917
- [The user attached ${turnAttachments.length} image(s).]` : plannerPromptText,
74306
+ [The user attached ${turnAttachments.length} image(s).] If the user is asking to describe, inspect, or answer questions about the image(s), classify as advisory_response.` : isPromptRelevantToPage && freshRetainedPage ? `${plannerPromptText}
74307
+
74308
+ [Context (untrusted data): A web page was previously browsed in this session (domain: ${(() => {
74309
+ try {
74310
+ return new URL(freshRetainedPage.url).hostname;
74311
+ } catch {
74312
+ return "external-site";
74313
+ }
74314
+ })()}). If the user is asking questions about the previously browsed page, classify as advisory_response.]` : plannerPromptText, plannerInput = {
74315
+ // Keep plannerInput.prompt strictly equal to user prompt text so downstream routes
74316
+ // (resolveBrowseUrls, routeBrowse, routeBrainstorm, routeFamiliarize, promptPreview)
74317
+ // are never contaminated by classifier hints or untrusted page metadata.
74318
+ prompt: plannerPromptText,
73918
74319
  clarifications,
73919
74320
  ...canonicalConversationContext ? { canonicalConversationContext } : {},
73920
74321
  sessionContext: {
@@ -73953,9 +74354,18 @@ async function handleShellUserInput(deps) {
73953
74354
  }, decision, pendingStartConfirmation, showClassifySpinner = store.getState().progress === null;
73954
74355
  showClassifySpinner && dispatchProgress({ phase: "planner_classifying" });
73955
74356
  try {
73956
- if (decision = await dispatchClassify(plannerInput), isLocalPlannerRuntime(args) && decision.action === "browse" && hasFileMutationIntent(plannerInput.prompt) && (decision = {
74357
+ if (decision = await dispatchClassify({
74358
+ ...plannerInput,
74359
+ prompt: classifyPrompt
74360
+ }), isLocalPlannerRuntime(args) && decision.action === "browse" && hasFileMutationIntent(plannerInput.prompt) && (decision = {
73957
74361
  action: "start_task",
73958
74362
  rationale: decision.rationale ? `${decision.rationale} (promoted to start_task due to file mutation intent)` : "web request with file mutation intent"
74363
+ }), turnAttachments.length > 0 && decision.action === "ask_user" && isBlindImageClarification(plannerInput.prompt, decision.clarifying_question) && !hasFileMutationIntent(plannerInput.prompt) && !isDestructiveFileRequest(plannerInput.prompt) && (decision = {
74364
+ action: "advisory_response",
74365
+ rationale: decision.rationale || "analyze and answer inquiry about the attached image"
74366
+ }), freshRetainedPage && decision.action === "ask_user" && isPromptRelevantToPage && !hasFileMutationIntent(plannerInput.prompt) && !isDestructiveFileRequest(plannerInput.prompt) && (decision = {
74367
+ action: "advisory_response",
74368
+ rationale: decision.rationale || "answer inquiry using retained web page context"
73959
74369
  }), isLocalPlannerRuntime(args) && decision.action === "start_task" && plannerInput.clarifications.length === 0 && isDestructiveFileRequest(plannerInput.prompt))
73960
74370
  logger.warn(
73961
74371
  "[orchestration-shell] P43 backstop: a destructive file request was classified start_task without a confirmation this turn; asking first",
@@ -75800,21 +76210,35 @@ var HostedGemmaAdvisoryRunner = class {
75800
76210
  this.providerKind = "hosted";
75801
76211
  }
75802
76212
  async generateAdvisory(promptText, options) {
75803
- if (options?.images && options.images.length > 0)
75804
- throw new Error(
75805
- "HostedGemmaAdvisoryRunner: image attachments are not supported by hosted Bedrock Gemma. A local vision model is required."
75806
- );
75807
76213
  let sessionId = this.deps.activeSessionIdHolder.value;
75808
76214
  if (!sessionId)
75809
76215
  throw new Error("Hosted Gemma advisory generation failed: no active session ID");
75810
76216
  let sessionKey = await this.deps.sessionKeyResolver.getSessionKey(sessionId);
75811
76217
  if (!sessionKey)
75812
76218
  throw new Error(`Hosted Gemma advisory generation failed: session key unresolved for session ${sessionId}`);
75813
- let encryptedPrompt = this.deps.crypto.encryptString(promptText, sessionKey), maxTokens = options?.numPredict, temperature = options?.temperature, start = Date.now();
76219
+ let encryptedPrompt = this.deps.crypto.encryptString(promptText, sessionKey), encryptedImages;
76220
+ if (options?.images && options.images.length > 0) {
76221
+ if (options.images.length > 5)
76222
+ throw new Error(
76223
+ `Hosted Gemma image limit exceeded: at most 5 images are supported (received ${options.images.length}).`
76224
+ );
76225
+ for (let i = 0; i < options.images.length; i++) {
76226
+ let img = options.images[i];
76227
+ if (typeof img != "string" || img.trim().length === 0)
76228
+ throw new Error(`Hosted Gemma image at index ${i} is empty or invalid.`);
76229
+ if (img.length > 2e6)
76230
+ throw new Error(
76231
+ `Hosted Gemma image at index ${i} exceeds size limit of 2,000,000 characters (~1.5 MB). Please provide a smaller or downscaled image.`
76232
+ );
76233
+ }
76234
+ encryptedImages = options.images.map((img) => this.deps.crypto.encryptString(img, sessionKey));
76235
+ }
76236
+ let maxTokens = options?.numPredict, temperature = options?.temperature, start = Date.now();
75814
76237
  try {
75815
76238
  let response = await this.deps.transport.generatePlannerAdvisory({
75816
76239
  sessionId,
75817
76240
  prompt: encryptedPrompt,
76241
+ ...encryptedImages && encryptedImages.length > 0 ? { images: encryptedImages } : {},
75818
76242
  ...maxTokens !== void 0 ? { maxTokens } : {},
75819
76243
  ...temperature !== void 0 ? { temperature } : {}
75820
76244
  });
@@ -75853,14 +76277,7 @@ var CompositeAdvisoryRunner = class {
75853
76277
  if (this.opts.tier === "FREE") {
75854
76278
  if (!this.opts.localRunner)
75855
76279
  throw new Error(
75856
- "LocalModelUnavailable: local Gemma is not installed or enabled. Free tier operates in local-only mode."
75857
- );
75858
- return this.opts.localRunner.generateAdvisory(promptText, options);
75859
- }
75860
- if (options?.images && options.images.length > 0) {
75861
- if (!this.localHealthy || !this.opts.localRunner)
75862
- throw new Error(
75863
- "LocalModelUnavailable: local CodeVibe vision model is required to analyze attached images."
76280
+ options?.images && options.images.length > 0 ? "LocalModelUnavailable: local CodeVibe vision model is required to analyze attached images. Free tier operates in local-only mode." : "LocalModelUnavailable: local Gemma is not installed or enabled. Free tier operates in local-only mode."
75864
76281
  );
75865
76282
  return this.opts.localRunner.generateAdvisory(promptText, options);
75866
76283
  }
@@ -75868,9 +76285,17 @@ var CompositeAdvisoryRunner = class {
75868
76285
  try {
75869
76286
  return await this.opts.localRunner.generateAdvisory(promptText, options);
75870
76287
  } catch (err) {
76288
+ let hasImages = !!(options?.images && options.images.length > 0);
75871
76289
  logger.warn("[composite-advisory] local advisory failed; falling back to hosted Bedrock Gemma", {
75872
- error: err?.message
75873
- }), this.localHealthy = !1, this.opts.onFallbackToHosted?.(err);
76290
+ error: err?.message,
76291
+ hasImages
76292
+ }), this.localHealthy = !1, this.opts.onNotice && this.opts.onNotice(
76293
+ hasImages ? `
76294
+ Local vision model error. Falling back to hosted Gemma on AWS.
76295
+ ` : `
76296
+ Local advisory error. Falling back to hosted Gemma on AWS.
76297
+ `
76298
+ ), this.opts.onFallbackToHosted?.(err);
75874
76299
  }
75875
76300
  return this.opts.hostedRunner.generateAdvisory(promptText, options);
75876
76301
  }
@@ -77273,7 +77698,7 @@ function renderPreflight(agents, auth, connectivity, orphans) {
77273
77698
  let reason = agent.error ? `(${agent.error})` : "(not in $PATH)";
77274
77699
  console.log(` [${colors.yellow}missing${colors.reset}] ${agent.name} ${colors.dim}${reason}${colors.reset}`);
77275
77700
  }
77276
- 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}`));
77701
+ 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}`));
77277
77702
  let backendName = auth.vendorKeys.custodyBackend === "keytar" ? "OS Keychain" : "File Store";
77278
77703
  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)
77279
77704
  for (let diag of orphans.retainedStateDiagnostics)
@@ -80457,13 +80882,20 @@ async function requireCliAuthentication(client) {
80457
80882
  }
80458
80883
  if (authenticated) return;
80459
80884
  let failureKind = client.getLastAuthFailureKind();
80460
- throw failureKind === "refresh_network" ? new CliAuthenticationError(
80461
- "auth_unavailable",
80462
- "CodeVibe could not verify your sign-in because the authentication service is unreachable. Check your connection and try again."
80463
- ) : failureKind === "credential_storage_unavailable" ? new CliAuthenticationError(
80464
- "credential_storage_unavailable",
80465
- "CodeVibe could not read stored sign-in credentials. Unlock or repair your credential store, then try again."
80466
- ) : new CliAuthenticationError(
80885
+ if (failureKind === "refresh_network")
80886
+ throw new CliAuthenticationError(
80887
+ "auth_unavailable",
80888
+ "CodeVibe could not verify your sign-in because the authentication service is unreachable. Check your connection and try again."
80889
+ );
80890
+ if (failureKind === "credential_storage_unavailable") {
80891
+ let detail = client.getLastAuthFailureDetail?.() ?? null;
80892
+ throw new CliAuthenticationError(
80893
+ "credential_storage_unavailable",
80894
+ "CodeVibe could not read stored sign-in credentials. Unlock or repair your credential store, then try again." + (detail ? `
80895
+ ${detail}` : "")
80896
+ );
80897
+ }
80898
+ throw new CliAuthenticationError(
80467
80899
  "auth_required",
80468
80900
  "Not signed in. Run `codevibe login`, then launch CodeVibe again."
80469
80901
  );
@@ -81102,13 +81534,14 @@ Note: --export will overwrite any existing file at <path>.
81102
81534
  try {
81103
81535
  authenticated = await appsyncClient.authenticateWithStoredTokens();
81104
81536
  } catch (e) {
81105
- return err(
81106
- `Not signed in \u2014 run \`codevibe login\` first (${e?.message ?? "auth unavailable"}).
81107
- `
81108
- ), 2;
81537
+ return err(`CodeVibe could not verify your sign-in (${e?.message ?? "auth unavailable"}).
81538
+ `), 2;
81539
+ }
81540
+ if (!authenticated) {
81541
+ let kind = appsyncClient.getLastAuthFailureKind?.() ?? null;
81542
+ return kind === "credential_storage_unavailable" || kind === "refresh_network" ? (err(`${describeStoredAuthFailure(kind, appsyncClient.getLastAuthFailureDetail?.() ?? null).message}
81543
+ `), 2) : (err("Not signed in \u2014 run `codevibe login` first.\n"), 2);
81109
81544
  }
81110
- if (!authenticated)
81111
- return err("Not signed in \u2014 run `codevibe login` first.\n"), 2;
81112
81545
  let sessionId = parsed.sessionId ?? null;
81113
81546
  if (!sessionId) {
81114
81547
  let resolveSessionId = deps?.resolveSessionIdFn ?? resolveActiveSessionId;
@@ -81507,12 +81940,15 @@ function resolvePlannerAdapter(args) {
81507
81940
  localRuntimeLabel: args.localPlanner.runtimeLabel,
81508
81941
  hostedHealth: hostedStack.health,
81509
81942
  onNotice: (msg) => {
81510
- process.stdout.write(msg);
81943
+ logger.info("[planner] " + msg);
81511
81944
  }
81512
81945
  }), compositeAdvisoryRunner = new CompositeAdvisoryRunner({
81513
81946
  localRunner: args.localPlanner.advisoryRunner,
81514
81947
  hostedRunner: hostedStack.hostedAdvisoryRunner,
81515
- tier
81948
+ tier,
81949
+ onNotice: (msg) => {
81950
+ logger.info("[planner] " + msg);
81951
+ }
81516
81952
  });
81517
81953
  return {
81518
81954
  adapter: compositeAdapter,