@vibedeckx/darwin-arm64 0.2.17 → 0.2.18

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.
Files changed (2) hide show
  1. package/dist/bin.js +163 -34
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -226901,6 +226901,8 @@ var WATCH_WINDOW_MS = 30 * 60 * 1e3;
226901
226901
  var WATCH_EXTEND_THROTTLE_MS = 5 * 60 * 1e3;
226902
226902
  var MAX_TRACKED_SESSIONS = 5e3;
226903
226903
  var SYNC_INTERVAL_MS = 6e4;
226904
+ var COMPLETION_NUDGE_DEBOUNCE_MS = 250;
226905
+ var MAX_CONCURRENT_SERVERS = 4;
226904
226906
  var MAX_PAGES_PER_SYNC = 50;
226905
226907
  function localWorkflowRunId(remoteServerId, projectId, remoteRunId) {
226906
226908
  return `remote-${remoteServerId}-${projectId}-${remoteRunId}`;
@@ -226913,10 +226915,19 @@ var RemoteNotificationSync = class {
226913
226915
  notificationService;
226914
226916
  reverseConnectManager;
226915
226917
  proxy;
226918
+ nudgeDebounceMs;
226916
226919
  timer = null;
226917
226920
  stopped = false;
226918
- /** Serializes sweeps so a came-online sweep can't interleave with the tick. */
226919
- chain = Promise.resolve();
226921
+ /**
226922
+ * Per-server work queues. Two syncs of the SAME server must not interleave
226923
+ * (they share its cursors), but two different servers have nothing in
226924
+ * common — serializing them globally only meant an unreachable worker's
226925
+ * retry budget delayed every healthy worker's import behind it.
226926
+ */
226927
+ serverChains = /* @__PURE__ */ new Map();
226928
+ /** Local session ids whose completion is waiting for the debounce to fire. */
226929
+ nudgeSessions = /* @__PURE__ */ new Set();
226930
+ nudgeTimer = null;
226920
226931
  /**
226921
226932
  * Remote sessions this front currently believes are RUNNING. They are polled
226922
226933
  * regardless of their persisted watch window: an agent turn can easily run
@@ -226932,6 +226943,7 @@ var RemoteNotificationSync = class {
226932
226943
  this.notificationService = deps.notificationService;
226933
226944
  this.reverseConnectManager = deps.reverseConnectManager;
226934
226945
  this.proxy = deps.proxy ?? proxyToRemoteAuto;
226946
+ this.nudgeDebounceMs = deps.nudgeDebounceMs ?? COMPLETION_NUDGE_DEBOUNCE_MS;
226935
226947
  }
226936
226948
  start() {
226937
226949
  if (this.timer || this.stopped) return;
@@ -226944,6 +226956,9 @@ var RemoteNotificationSync = class {
226944
226956
  this.stopped = true;
226945
226957
  if (this.timer) clearInterval(this.timer);
226946
226958
  this.timer = null;
226959
+ if (this.nudgeTimer) clearTimeout(this.nudgeTimer);
226960
+ this.nudgeTimer = null;
226961
+ this.nudgeSessions.clear();
226947
226962
  this.activeRemoteSessions.clear();
226948
226963
  this.lastWatchExtend.clear();
226949
226964
  }
@@ -226967,6 +226982,7 @@ var RemoteNotificationSync = class {
226967
226982
  } else if (event.type === "session:taskCompleted" || event.type === "session:finished") {
226968
226983
  this.activeRemoteSessions.delete(sessionId);
226969
226984
  }
226985
+ if (isTurnTerminalEvent(event)) this.scheduleCompletionSync(sessionId);
226970
226986
  const now2 = Date.now();
226971
226987
  const last = this.lastWatchExtend.get(sessionId) ?? 0;
226972
226988
  if (now2 - last < WATCH_EXTEND_THROTTLE_MS) return;
@@ -226980,10 +226996,65 @@ var RemoteNotificationSync = class {
226980
226996
  if (now2 - at > WATCH_WINDOW_MS) this.lastWatchExtend.delete(sessionId);
226981
226997
  }
226982
226998
  }
226983
- /** Fire-and-forget sweep, queued behind any in-flight one. */
226999
+ /**
227000
+ * Fire-and-forget sweep. No longer a global queue: the per-server chains
227001
+ * inside syncAll/syncServer own the mutual exclusion that actually matters
227002
+ * (one server's cursors), so callers here don't wait on unrelated servers.
227003
+ */
226984
227004
  enqueue(work) {
226985
227005
  if (this.stopped) return;
226986
- this.chain = this.chain.then(work).catch((err) => console.warn("[RemoteNotifications] sync failed:", err));
227006
+ void work().catch((err) => console.warn("[RemoteNotifications] sync failed:", err));
227007
+ }
227008
+ /**
227009
+ * Queue `work` behind whatever is already running for this server, and
227010
+ * nothing else. Errors are contained so one server's failure can't reject
227011
+ * an unrelated caller awaiting the same chain.
227012
+ */
227013
+ runOnServer(remoteServerId, work) {
227014
+ const previous = this.serverChains.get(remoteServerId) ?? Promise.resolve();
227015
+ const next = previous.then(work).catch((err) => console.warn(`[RemoteNotifications] sync for ${remoteServerId} failed:`, err)).finally(() => {
227016
+ if (this.serverChains.get(remoteServerId) === next) this.serverChains.delete(remoteServerId);
227017
+ });
227018
+ this.serverChains.set(remoteServerId, next);
227019
+ return next;
227020
+ }
227021
+ /**
227022
+ * Debounced, per-server pull for sessions whose turn just ended.
227023
+ *
227024
+ * Coalescing state is intentionally separate from `lastWatchExtend`: that
227025
+ * map throttles DB writes on a 5-minute scale, which would swallow nearly
227026
+ * every completion.
227027
+ */
227028
+ scheduleCompletionSync(localSessionId) {
227029
+ if (this.stopped) return;
227030
+ this.nudgeSessions.add(localSessionId);
227031
+ if (this.nudgeTimer) return;
227032
+ this.nudgeTimer = setTimeout(() => {
227033
+ this.nudgeTimer = null;
227034
+ const batch = [...this.nudgeSessions];
227035
+ this.nudgeSessions.clear();
227036
+ void this.syncSessions(batch).catch(
227037
+ (err) => console.warn("[RemoteNotifications] completion sync failed:", err)
227038
+ );
227039
+ }, this.nudgeDebounceMs);
227040
+ this.nudgeTimer.unref?.();
227041
+ }
227042
+ /**
227043
+ * Pull exactly the mappings named, grouped per server. Deliberately does NOT
227044
+ * go through `candidates()`: an explicit completion signal outranks the
227045
+ * watch window, which may well have lapsed during a long turn.
227046
+ */
227047
+ async syncSessions(localSessionIds) {
227048
+ const byServer = /* @__PURE__ */ new Map();
227049
+ for (const localSessionId of localSessionIds) {
227050
+ const mapping = await this.storage.remoteSessionMappings.getByLocal(localSessionId).catch(() => void 0);
227051
+ if (!mapping) continue;
227052
+ const group2 = byServer.get(mapping.remote_server_id);
227053
+ if (group2) group2.push(mapping);
227054
+ else byServer.set(mapping.remote_server_id, [mapping]);
227055
+ }
227056
+ if (byServer.size === 0) return;
227057
+ await this.dispatchByServer([...byServer]);
226987
227058
  }
226988
227059
  /** Extend a mapping's periodic-poll window (create / send / workflow start / live activity). */
226989
227060
  async extendWatch(localSessionId) {
@@ -227033,17 +227104,33 @@ var RemoteNotificationSync = class {
227033
227104
  );
227034
227105
  return true;
227035
227106
  }
227036
- /** Sweep every mapped remote server. */
227107
+ /** Sweep every mapped remote server, up to MAX_CONCURRENT_SERVERS at a time. */
227037
227108
  async syncAll(opts) {
227038
227109
  const mappings = await this.candidates(opts);
227039
- for (const [serverId, group2] of groupByServer(mappings)) {
227040
- await this.syncMappings(serverId, group2);
227041
- }
227110
+ await this.dispatchByServer([...groupByServer(mappings)]);
227042
227111
  }
227043
227112
  /** Sweep one server — used when a reverse connection comes online. */
227044
227113
  async syncServer(remoteServerId, opts) {
227045
227114
  const mappings = (await this.candidates(opts)).filter((m2) => m2.remote_server_id === remoteServerId);
227046
- if (mappings.length > 0) await this.syncMappings(remoteServerId, mappings);
227115
+ if (mappings.length > 0) await this.dispatchByServer([[remoteServerId, mappings]]);
227116
+ }
227117
+ /**
227118
+ * Run one group per server, bounded. Each group still queues on its own
227119
+ * server chain, so this composes with a nudge that arrives mid-sweep: same
227120
+ * server → it waits its turn; different server → it runs immediately.
227121
+ */
227122
+ async dispatchByServer(groups) {
227123
+ let next = 0;
227124
+ const workers = Array.from(
227125
+ { length: Math.min(MAX_CONCURRENT_SERVERS, groups.length) },
227126
+ async () => {
227127
+ for (let i = next++; i < groups.length; i = next++) {
227128
+ const [serverId, group2] = groups[i];
227129
+ await this.runOnServer(serverId, () => this.syncMappings(serverId, group2));
227130
+ }
227131
+ }
227132
+ );
227133
+ await Promise.all(workers);
227047
227134
  }
227048
227135
  /**
227049
227136
  * Mappings this sweep should poll: the persisted watch set, plus any session
@@ -227190,6 +227277,10 @@ var RemoteNotificationSync = class {
227190
227277
  };
227191
227278
  }
227192
227279
  };
227280
+ function isTurnTerminalEvent(event) {
227281
+ if (event.type === "session:taskCompleted" || event.type === "session:finished") return true;
227282
+ return event.type === "session:status" && (event.status === "stopped" || event.status === "error");
227283
+ }
227193
227284
  function groupByServer(mappings) {
227194
227285
  const groups = /* @__PURE__ */ new Map();
227195
227286
  for (const mapping of mappings) {
@@ -229950,12 +230041,18 @@ function selfReportSection(report) {
229950
230041
  if (!report) return null;
229951
230042
  return [
229952
230043
  "\n## Author's self-report (unverified)",
229953
- "The implementing agent described its own work as follows. Treat every claim as unverified \u2014 check each one against the actual code, and look for problems the self-report does not mention.",
230044
+ "The implementing agent described its own work as follows. Treat every claim as unverified \u2014 verify first the claims that bear on whether the work achieves its goal, and look for problems the self-report does not mention.",
229954
230045
  "<author-self-report>",
229955
230046
  report,
229956
230047
  "</author-self-report>"
229957
230048
  ].join("\n");
229958
230049
  }
230050
+ var VERDICT_INSTRUCTIONS = [
230051
+ "\nEnd your final message with:",
230052
+ "1. Verdict \u2014 exactly one of: ship / needs-changes / cannot-verify. Use cannot-verify when you could not gather enough evidence to judge, rather than guessing.",
230053
+ "2. Blocking findings \u2014 what must change before shipping, each specific and actionable (say explicitly when there are none).",
230054
+ "3. Non-blocking notes \u2014 style and polish, briefly, clearly separated from the blocking list."
230055
+ ];
229959
230056
  function buildReviewerPrompt(opts) {
229960
230057
  const intent = opts.originalIntent !== opts.taskContext ? opts.originalIntent : null;
229961
230058
  const brief = opts.intentBrief || null;
@@ -229994,7 +230091,12 @@ Confine your review to these files and changes. Other uncommitted or pre-existin
229994
230091
  "- Inspect the actual workspace state yourself: read the relevant files, run `git diff`, `git status` and `git log`.",
229995
230092
  reviewTargetPromptLine(opts.target),
229996
230093
  noDiffWithAnalysis ? "- Judge correctness and completeness against the task. For this analysis/plan turn the work under review is the reasoning and the proposal, not code quality of a diff. Be specific: reference files and lines." : "- Judge correctness, completeness against the task, and code quality. Be specific: reference files and lines.",
229997
- "\nEnd your final message with a clear, actionable list of feedback items \u2014 or state explicitly that the work looks good.",
230094
+ // These two only make sense against a distilled brief: tier 2 has no
230095
+ // [settled]/[tentative] marks and no stated scope, and implying it does
230096
+ // would suppress findings on the strength of data that doesn't exist.
230097
+ brief ? "- Where the brief marks a decision, non-goal, or accepted limitation as [settled], do not re-raise the choice itself as a finding; DO report concrete consequences it causes \u2014 failure of the core goal, or a correctness, security, or data loss problem. Items marked [tentative] (or unmarked) get normal review. A violated hard constraint is always blocking." : null,
230098
+ brief ? "- Do not propose enhancements beyond the brief's stated scope \u2014 scope expansion is a product decision, not a review finding." : null,
230099
+ ...VERDICT_INSTRUCTIONS,
229998
230100
  brief ? opts.authorSelfReport ? "\n(review context: distilled intent brief + author self-report + live workspace)" : "\n(review context: distilled intent brief + live workspace)" : hasExcerpt ? "\n(review context: deterministic excerpt of the source conversation + live workspace)" : "\n(review context: live workspace only \u2014 the source conversation was unavailable)"
229999
230101
  ].filter((l) => l !== null).join("\n");
230000
230102
  }
@@ -230018,7 +230120,7 @@ ${opts.reviewFocus}` : null,
230018
230120
  "- Check for regressions and remaining correctness or test gaps.",
230019
230121
  "- Do NOT modify files \u2014 remain in read-only review mode.",
230020
230122
  reviewTargetPromptLine(opts.target),
230021
- "- End with actionable feedback, or explicitly state that it looks good."
230123
+ ...VERDICT_INSTRUCTIONS
230022
230124
  ].filter((line) => line !== null).join("\n");
230023
230125
  }
230024
230126
  function buildFeedbackMessage(feedback) {
@@ -235626,8 +235728,8 @@ import { randomUUID as randomUUID15 } from "crypto";
235626
235728
 
235627
235729
  // src/protocol/model-suggestions.ts
235628
235730
  var MODEL_SUGGESTIONS = {
235629
- "claude-code": ["opus", "sonnet", "haiku"],
235630
- codex: ["gpt-5.6-sol", "gpt-5.6-codex", "o3"]
235731
+ "claude-code": ["opus", "sonnet", "haiku", "fable"],
235732
+ codex: ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.5"]
235631
235733
  };
235632
235734
 
235633
235735
  // src/routes/agent-session-routes.ts
@@ -237578,6 +237680,7 @@ var import_fastify_plugin20 = __toESM(require_plugin2(), 1);
237578
237680
 
237579
237681
  // src/utils/review-brief.ts
237580
237682
  var BASE_TIMEOUT_MS = 15e3;
237683
+ var JUDGMENT_TIMEOUT_MS = 9e4;
237581
237684
  var TIMEOUT_MS_PER_1K_CHARS = 120;
237582
237685
  var MAX_TIMEOUT_MS = 6e4;
237583
237686
  var PER_MESSAGE_MAX_CHARS = 6e3;
@@ -237587,11 +237690,11 @@ var RECENT_VERBATIM_RATIO = 0.3;
237587
237690
  var MIN_RECENT_VERBATIM_CHARS = 2e3;
237588
237691
  var MAX_FOLD_CALLS = 8;
237589
237692
  var COMPACT_SUMMARY_MAX_CHARS = 6e3;
237590
- var COMPACT_SUMMARY_MAX_TOKENS = 1500;
237693
+ var COMPACT_SUMMARY_MAX_TOKENS = 3e3;
237591
237694
  var REVERSALS_MAX_CHARS = 2e3;
237592
- var REVERSALS_MAX_TOKENS = 500;
237695
+ var REVERSALS_MAX_TOKENS = 2e3;
237593
237696
  var BRIEF_MAX_CHARS = 4e3;
237594
- var BRIEF_MAX_TOKENS = 1e3;
237697
+ var BRIEF_MAX_TOKENS = 3e3;
237595
237698
  var REVERSAL_MIN_CHARS = 6e3;
237596
237699
  var SEP = "\n\n";
237597
237700
  var COMPACTION_NOTE = "_Note: the source conversation was compressed before distillation \u2014 older turns were summarized rather than read verbatim._";
@@ -237599,13 +237702,17 @@ var BRIEF_TRUNCATION_NOTE = "\n\n_[brief truncated at the length limit \u2014 tr
237599
237702
  var SYSTEM_PROMPT2 = [
237600
237703
  "You distill a coding-agent conversation into an intent brief for an independent code reviewer.",
237601
237704
  "The reviewer will NOT see the conversation \u2014 only your brief plus the actual code, so capture what the code alone cannot show:",
237705
+ "0. Dominant question \u2014 1 or 2 sentences: the single question the reviewer must answer to judge this work (what must actually work, toward what goal). Derive it from the conversation's own emphasis \u2014 what the user repeated, finally decided, or reversed course over \u2014 never from your own engineering judgment. If the conversation does not clearly support one, write 'unclear' instead of inventing one.",
237602
237706
  "1. The original request and its goal.",
237603
- "2. Constraints and decisions, including approaches that were rejected and why. A rejection counts whether the user made it or the agent made it under a principle the user set.",
237604
- "3. Trade-offs or limitations that were acknowledged and accepted.",
237605
- "4. Behaviour established by actually running something \u2014 observed output, exit codes, error text, which stream an error arrived on. Report what was observed; that is evidence the reviewer cannot recover by reading code.",
237707
+ "2. Hard constraints \u2014 requirements whose violation the user would treat as failure regardless of everything else (security, data loss, an explicit compatibility or behaviour promise). Only list ones the user actually stated; an empty section is fine.",
237708
+ "3. Constraints and decisions, including approaches that were rejected and why. A rejection counts whether the user made it or the agent made it under a principle the user set.",
237709
+ "4. Non-goals, trade-offs and limitations that were acknowledged and accepted.",
237710
+ "5. Behaviour established by actually running something \u2014 observed output, exit codes, error text, which stream an error arrived on. Report what was observed; that is evidence the reviewer cannot recover by reading code.",
237711
+ "Tag every item under headings 3 and 4 with exactly [settled] or [tentative] \u2014 the bare tag; keep any justification to a few words or omit it, always after the tag, never inside the brackets. Settled ONLY when the user explicitly confirmed it, instructed it, or reversed course to it; an agent proposal the user never answered is tentative \u2014 silence is never settled.",
237712
+ "Within each heading, order items by how much they matter to the dominant question, not by when they were said.",
237606
237713
  "Later statements supersede earlier ones on the same topic. An option explored at length early is often rejected later in a single sentence, and the rejection wins. Where the conversation ends in a decision table or summary, that is authoritative wherever it conflicts with earlier prose.",
237607
237714
  "The input may arrive as numbered compressed slices followed by verbatim recent turns. Slice numbers are chronological: a later slice overrules an earlier one.",
237608
- "Decisions the agent made that the user did not contradict are binding \u2014 silence is agreement.",
237715
+ "Decisions the agent made that the user did not contradict are worth reporting, but user silence only ever yields tentative status.",
237609
237716
  "Do NOT include the agent's self-assessment or any claim that the work is correct or complete \u2014 the reviewer must judge that independently.",
237610
237717
  "Every statement must trace to something in the conversation. If a heading has nothing real to report, say so rather than inventing plausible-sounding content.",
237611
237718
  "Write concise markdown bullets under those numbered headings, under 500 words total, in the same language as the conversation. Reply with the brief only."
@@ -237623,7 +237730,7 @@ var COMPACT_SYSTEM_PROMPT = [
237623
237730
  "You are told which slice of how many this is. Other slices are compressed separately and you cannot see them, so never present a conclusion here as final \u2014 a later slice may overturn it.",
237624
237731
  "Preserve, in this order of priority:",
237625
237732
  "1. Reversals and corrections \u2014 any conclusion that was dropped, replaced, or corrected. Quote the deciding sentence verbatim.",
237626
- "2. Constraints and decisions the user stated, and approaches that were rejected, with the reason.",
237733
+ "2. Constraints and decisions the user stated, and approaches that were rejected, with the reason. Preserve who made each decision \u2014 an explicit user confirmation must stay distinguishable from an agent proposal the user never answered; quote the user's deciding sentence when there is one.",
237627
237734
  "3. Behaviour established by actually running something: observed output, exit codes, error text, which stream an error arrived on.",
237628
237735
  "4. Trade-offs that were acknowledged and accepted.",
237629
237736
  "Drop: exploration narrative, restated background, tool mechanics, and anything a reviewer could recover by reading the code.",
@@ -237719,8 +237826,16 @@ ${chunk}`,
237719
237826
  maxOutputTokens: COMPACT_SUMMARY_MAX_TOKENS,
237720
237827
  experimental_telemetry: telemetryFor("review-intent-brief-compact", userId)
237721
237828
  });
237722
- const text2 = (result.text ?? "").trim();
237723
- if (text2.length === 0) return null;
237829
+ const { text: rawText, finishReason } = result;
237830
+ if (finishReason === "length") {
237831
+ console.warn(`[ReviewBrief] slice ${part}/${total} summary hit the token cap \u2014 discarding`);
237832
+ return null;
237833
+ }
237834
+ const text2 = (rawText ?? "").trim();
237835
+ if (text2.length === 0) {
237836
+ console.warn(`[ReviewBrief] slice ${part}/${total} came back empty (finishReason: ${finishReason ?? "unknown"})`);
237837
+ return null;
237838
+ }
237724
237839
  return withinLimit(text2, COMPACT_SUMMARY_MAX_CHARS, `slice ${part}/${total} summary`) ? text2 : null;
237725
237840
  } catch (error48) {
237726
237841
  if (isSizeRelatedFailure(error48)) throw error48;
@@ -237774,11 +237889,16 @@ async function extractReversalsWithModel(model, conversation, options = {}) {
237774
237889
  prompt: `Report the reversals in this conversation:
237775
237890
 
237776
237891
  ${conversation}`,
237777
- timeout: timeoutForInput(conversation.length),
237892
+ timeout: JUDGMENT_TIMEOUT_MS,
237778
237893
  maxOutputTokens: REVERSALS_MAX_TOKENS,
237779
237894
  experimental_telemetry: telemetryFor("review-intent-brief-reversals", options.userId)
237780
237895
  });
237781
- const text2 = (result.text ?? "").trim();
237896
+ const { text: rawText, finishReason } = result;
237897
+ if (finishReason === "length") {
237898
+ console.warn("[ReviewBrief] reversal list hit the token cap \u2014 discarding");
237899
+ return null;
237900
+ }
237901
+ const text2 = (rawText ?? "").trim();
237782
237902
  if (text2.length === 0 || /^none\b/i.test(text2)) return null;
237783
237903
  return withinLimit(text2, REVERSALS_MAX_CHARS, "reversal list") ? text2 : null;
237784
237904
  } catch (error48) {
@@ -237794,12 +237914,17 @@ async function generateIntentBriefWithModel(model, conversation, options = {}) {
237794
237914
  model,
237795
237915
  system: SYSTEM_PROMPT2,
237796
237916
  prompt: buildBriefPrompt(conversation, options.reversals ?? null),
237797
- timeout: timeoutForInput(conversation.length),
237917
+ timeout: JUDGMENT_TIMEOUT_MS,
237798
237918
  maxOutputTokens: BRIEF_MAX_TOKENS,
237799
237919
  experimental_telemetry: telemetryFor("review-intent-brief", options.userId)
237800
237920
  });
237801
- const text2 = (result.text ?? "").trim();
237802
- if (text2.length === 0) return null;
237921
+ const { text: rawText, finishReason } = result;
237922
+ const text2 = (rawText ?? "").trim();
237923
+ if (text2.length === 0) {
237924
+ console.warn(`[ReviewBrief] model returned empty text (finishReason: ${finishReason ?? "unknown"})`);
237925
+ return null;
237926
+ }
237927
+ if (finishReason === "length") return text2.slice(0, BRIEF_MAX_CHARS) + BRIEF_TRUNCATION_NOTE;
237803
237928
  return text2.length <= BRIEF_MAX_CHARS ? text2 : text2.slice(0, BRIEF_MAX_CHARS) + BRIEF_TRUNCATION_NOTE;
237804
237929
  } catch (error48) {
237805
237930
  if (options.rethrowSizeFailures && isSizeRelatedFailure(error48)) throw error48;
@@ -237813,13 +237938,17 @@ async function distill(model, conversation, userId, rethrowSizeFailures) {
237813
237938
  }
237814
237939
  async function generateIntentBrief(storage, userId, messages) {
237815
237940
  try {
237816
- if (!await isChatModelConfigured(storage, userId)) return null;
237941
+ const config2 = await getChatProviderConfig(storage, userId);
237942
+ const mainOk = isModelConfigured(config2, config2.main);
237943
+ const fastOk = isModelConfigured(config2, config2.fast);
237944
+ if (!mainOk && !fastOk) return null;
237817
237945
  const conversation = serializeConversationForBrief(messages);
237818
237946
  if (!conversation) return null;
237819
- const model = await resolveFastChatModel(storage, userId);
237947
+ const distillModel = mainOk ? await resolveChatModel(storage, userId) : await resolveFastChatModel(storage, userId);
237948
+ const compactModel = fastOk ? await resolveFastChatModel(storage, userId) : distillModel;
237820
237949
  if (conversation.length <= COMPACT_TRIGGER_CHARS) {
237821
237950
  try {
237822
- const brief = await distill(model, conversation, userId, true);
237951
+ const brief = await distill(distillModel, conversation, userId, true);
237823
237952
  return brief === null ? null : appendCompactionNote(brief, false);
237824
237953
  } catch (error48) {
237825
237954
  if (!isSizeRelatedFailure(error48)) throw error48;
@@ -237828,9 +237957,9 @@ async function generateIntentBrief(storage, userId, messages) {
237828
237957
  }
237829
237958
  for (const sliceChars of COMPACT_SLICE_BUDGETS) {
237830
237959
  try {
237831
- const compacted = await compactConversation(model, messages, { userId, sliceChars });
237960
+ const compacted = await compactConversation(compactModel, messages, { userId, sliceChars });
237832
237961
  if (compacted === null) return null;
237833
- const brief = await distill(model, compacted, userId, true);
237962
+ const brief = await distill(distillModel, compacted, userId, true);
237834
237963
  return brief === null ? null : appendCompactionNote(brief, true);
237835
237964
  } catch (error48) {
237836
237965
  if (!isSizeRelatedFailure(error48)) throw error48;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibedeckx/darwin-arm64",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "description": "Vibedeckx platform binaries for macOS ARM64",
5
5
  "os": [
6
6
  "darwin"