bereach-openclaw 1.6.5 → 1.6.6

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.
@@ -106,4 +106,8 @@ export interface CacheStore {
106
106
  campaignTaskStatus?: Record<string, Record<string, number>>;
107
107
  /** LLM circuit breaker status: "auth" | "billing" | "error" when tripped, null when OK */
108
108
  llmStatus?: string | null;
109
+ /** User's current fast model slug (e.g. "anthropic/claude-haiku-4-5", "google/gemini-2.5-flash"). Used to detect mid-session provider swaps. */
110
+ aiFastModel?: string | null;
111
+ /** User's current creative model slug. Used to detect mid-session provider swaps. */
112
+ aiCreativeModel?: string | null;
109
113
  }
@@ -248,6 +248,12 @@ export interface SessionState {
248
248
  postsThisSession: number;
249
249
  /** Track current task ID to detect task switches and reset per-task counters. */
250
250
  currentTaskId: string | null;
251
+ /** Fast model slug captured on the first turn of this session. Used to detect mid-session provider swaps that corrupt message history format. */
252
+ initialAiFastModel: string | null;
253
+ /** Creative model slug captured on the first turn of this session. */
254
+ initialAiCreativeModel: string | null;
255
+ /** True once we've warned the agent about a mid-session provider swap — prevents spamming the warning on every turn. */
256
+ providerMismatchWarningInjected: boolean;
251
257
  }
252
258
 
253
259
  export function createSessionState(): SessionState {
@@ -271,6 +277,9 @@ export function createSessionState(): SessionState {
271
277
  sessionStarted: false,
272
278
  postsThisSession: 0,
273
279
  currentTaskId: null,
280
+ initialAiFastModel: null,
281
+ initialAiCreativeModel: null,
282
+ providerMismatchWarningInjected: false,
274
283
  };
275
284
  }
276
285
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "bereach-openclaw",
3
3
  "name": "BeReach",
4
- "version": "1.6.5",
4
+ "version": "1.6.6",
5
5
  "description": "LinkedIn outreach automation — 75+ tools, hook-based enforcement, dynamic context",
6
6
  "configSchema": {
7
7
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bereach-openclaw",
3
- "version": "1.6.5",
3
+ "version": "1.6.6",
4
4
  "description": "BeReach LinkedIn automation plugin for OpenClaw",
5
5
  "license": "AGPL-3.0",
6
6
  "exports": {
@@ -154,7 +154,7 @@ export async function fetchSnapshot(apiKey: string): Promise<CacheStore> {
154
154
  pendingDrafts: 0, failedDrafts: 0, unreadDMs: 0, pendingSentInvitations: 0,
155
155
  activeAccount: null, accounts: [], leadGenState: null, outreachState: null,
156
156
  activeCampaigns: [], campaignTaskStatus: {}, campaignChecks: {}, sessionMeta: null, onboardingState: null,
157
- recentEvents: [], llmStatus: null,
157
+ recentEvents: [], llmStatus: null, aiFastModel: null, aiCreativeModel: null,
158
158
  };
159
159
  }
160
160
 
@@ -178,6 +178,8 @@ export async function fetchSnapshot(apiKey: string): Promise<CacheStore> {
178
178
  onboardingState: snapshot.onboardingState ?? null,
179
179
  recentEvents: snapshot.recentEvents ?? [],
180
180
  llmStatus: snapshot.llmStatus ?? null,
181
+ aiFastModel: snapshot.aiFastModel ?? null,
182
+ aiCreativeModel: snapshot.aiCreativeModel ?? null,
181
183
  };
182
184
 
183
185
  // Fallback: if snapshot didn't include limits, fetch from dedicated endpoint
@@ -235,6 +237,9 @@ export async function getOrFetch(apiKey: string): Promise<CacheStore> {
235
237
  sessionMeta: get<SessionMeta>("sessionMeta"),
236
238
  onboardingState: get<OnboardingState>("onboardingState"),
237
239
  recentEvents: get<RecentEvent[]>("recentEvents") ?? [],
240
+ llmStatus: get<string>("llmStatus") ?? null,
241
+ aiFastModel: get<string>("aiFastModel") ?? null,
242
+ aiCreativeModel: get<string>("aiCreativeModel") ?? null,
238
243
  };
239
244
  }
240
245
 
@@ -45,6 +45,9 @@ export function resetContextState(state: SessionState) {
45
45
  state.onboardingDirectiveInjected = false;
46
46
  state.sessionStarted = false;
47
47
  state.currentTaskMode = null;
48
+ state.initialAiFastModel = null;
49
+ state.initialAiCreativeModel = null;
50
+ state.providerMismatchWarningInjected = false;
48
51
  }
49
52
 
50
53
  /** Reset profile-related flags for account switch (mid-session). */
@@ -309,8 +312,55 @@ export function registerContextHook(api: any, apiKey: string | undefined, state:
309
312
 
310
313
  await autoInitProfile(state, liveData, key);
311
314
 
315
+ // Mid-session provider swap detection (B38.4).
316
+ // The gateway process is long-lived. If the user changes their AI model
317
+ // in Settings while a chat is open (Gemini → Claude Haiku, etc.), the
318
+ // session message history is still formatted for the previous provider
319
+ // and the new one will fail to parse it. Detect the mismatch and force
320
+ // a fresh chat.
321
+ const currentFast = liveData.aiFastModel ?? null;
322
+ const currentCreative = liveData.aiCreativeModel ?? null;
323
+ let providerMismatch = false;
324
+ if (!state.initialAiFastModel && !state.initialAiCreativeModel) {
325
+ state.initialAiFastModel = currentFast;
326
+ state.initialAiCreativeModel = currentCreative;
327
+ } else {
328
+ const fastChanged = !!currentFast && currentFast !== state.initialAiFastModel;
329
+ const creativeChanged =
330
+ !!currentCreative && currentCreative !== state.initialAiCreativeModel;
331
+ providerMismatch = fastChanged || creativeChanged;
332
+ }
333
+
312
334
  const { staticContext, dynamicContext } = buildInteractiveContext(state, soulTemplate, liveData, key);
313
335
 
336
+ if (providerMismatch && !state.providerMismatchWarningInjected) {
337
+ state.providerMismatchWarningInjected = true;
338
+ const warning = [
339
+ "",
340
+ "## CRITICAL — AI PROVIDER CHANGED MID-SESSION",
341
+ "",
342
+ `The user changed their AI model in Settings while this chat was already open.`,
343
+ `- Session was started with: fast=${state.initialAiFastModel} creative=${state.initialAiCreativeModel}`,
344
+ `- Current setting: fast=${currentFast} creative=${currentCreative}`,
345
+ "",
346
+ "The existing message history in this conversation is formatted for the previous",
347
+ "provider and the new provider cannot parse it. Any tool call in this turn will fail.",
348
+ "",
349
+ "You MUST respond with ONLY this message and NOTHING else:",
350
+ "",
351
+ "> I see you changed your AI model. The conversation history can't be converted between providers mid-session, so please type `/new` to start a fresh chat with the new model.",
352
+ "",
353
+ "Do NOT call any tools. Do NOT summarize status. Do NOT continue the previous task.",
354
+ "Just output the message above verbatim and stop.",
355
+ "",
356
+ ].join("\n");
357
+ log(`provider mismatch detected — injecting /new warning (was fast=${state.initialAiFastModel} now fast=${currentFast})`);
358
+ return {
359
+ appendSystemContext: staticContext,
360
+ prependContext: warning + dynamicContext,
361
+ };
362
+ }
363
+
314
364
  // appendSystemContext = cached by gateway (soul template, static across turns)
315
365
  // prependContext = per-turn dynamic data (live status, activity, tone)
316
366
  return {