@bohuyeshan/openagent-labforge-core 3.11.3 → 3.11.5

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.
package/README.md CHANGED
@@ -272,10 +272,10 @@ Keep reasoning depth in the OpenCode UI instead of hardcoding separate `low` / `
272
272
 
273
273
  Copy and paste this prompt to your LLM agent (Claude Code, AmpCode, Cursor, etc.):
274
274
 
275
- ```
276
- Install and configure @bohuyeshan/openagent-labforge-core by following the instructions here:
277
- https://raw.githubusercontent.com/code-yeongyu/openagent-labforge/refs/heads/dev/docs/guide/installation.md
278
- ```
275
+ ```
276
+ Install and configure @bohuyeshan/openagent-labforge-core by following the instructions here:
277
+ https://raw.githubusercontent.com/code-yeongyu/openagent-labforge/refs/heads/dev/docs/guide/installation.md
278
+ ```
279
279
 
280
280
  Or read the [Installation Guide](docs/guide/installation.md), but seriously, let an agent do it. Humans fat-finger configs.
281
281
 
package/README.zh-cn.md CHANGED
@@ -273,10 +273,10 @@ npm install --prefix ~/.config/opencode /absolute/path/to/openagent-labforge-cor
273
273
 
274
274
  复制并粘贴以下提示词到你的 LLM Agent (Claude Code, AmpCode, Cursor 等):
275
275
 
276
- ```
277
- Install and configure @bohuyeshan/openagent-labforge-core by following the instructions here:
278
- https://raw.githubusercontent.com/code-yeongyu/openagent-labforge/refs/heads/dev/docs/guide/installation.md
279
- ```
276
+ ```
277
+ Install and configure @bohuyeshan/openagent-labforge-core by following the instructions here:
278
+ https://raw.githubusercontent.com/code-yeongyu/openagent-labforge/refs/heads/dev/docs/guide/installation.md
279
+ ```
280
280
 
281
281
  或者你可以直接去读 [安装指南](docs/guide/installation.md),但说真的,让 Agent 去干吧。人类配环境总是容易敲错字母。
282
282
 
package/dist/cli/index.js CHANGED
@@ -9195,7 +9195,7 @@ var {
9195
9195
  // package.json
9196
9196
  var package_default = {
9197
9197
  name: "@bohuyeshan/openagent-labforge-core",
9198
- version: "3.11.3",
9198
+ version: "3.11.5",
9199
9199
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
9200
9200
  main: "dist/index.js",
9201
9201
  types: "dist/index.d.ts",
package/dist/index.js CHANGED
@@ -20225,9 +20225,32 @@ function hasUnansweredQuestion(messages) {
20225
20225
  /\u662F\u5426\u8981/u,
20226
20226
  /\u4F60\u5E0C\u671B/u,
20227
20227
  /\u4F60\u60F3\u8981/u,
20228
+ /\u4F60\u89C9\u5F97/u,
20229
+ /\u4F60\u600E\u4E48\u770B/u,
20230
+ /\u8BF7\u7ED9(?:\u6211)?\u610F\u89C1/u,
20231
+ /\u8BF7\u53CD\u9988/u,
20232
+ /\u6D4B\u8BD5\u540E(?:\u544A\u8BC9\u6211|\u53CD\u9988)/u,
20233
+ /\u8DD1\u5B8C\u6D4B\u8BD5(?:\u544A\u8BC9\u6211|\u53CD\u9988)/u,
20234
+ /\u786E\u8BA4\u540E(?:\u544A\u8BC9\u6211|\u56DE\u590D\u6211|\u6211\u518D\u7EE7\u7EED)/u,
20235
+ /\u4F60\u6765\u51B3\u5B9A/u,
20236
+ /\u4F60\u51B3\u5B9A/u,
20237
+ /\u8BF7(?:\u9009\u62E9|\u9009\u4E00\u4E2A)/u,
20228
20238
  /\u5B8C\u6210\u540E\u56DE\u6211/u,
20229
20239
  /\u91CD\u542F\u540E\u544A\u8BC9\u6211/u,
20230
20240
  /\u56DE\u6211\u4E00\u53E5/u,
20241
+ /\u9700\u8981\u4F60\u786E\u8BA4/u,
20242
+ /\u7B49\u5F85(?:\u4F60\u7684|\u7528\u6237)(?:\u786E\u8BA4|\u53CD\u9988|\u51B3\u5B9A|\u56DE\u590D|\u610F\u89C1)/u,
20243
+ /what do you think/iu,
20244
+ /any preference/iu,
20245
+ /your (?:feedback|input|decision|opinion)/iu,
20246
+ /after you (?:test|review)/iu,
20247
+ /once you (?:test|review)/iu,
20248
+ /please review/iu,
20249
+ /review and confirm/iu,
20250
+ /should i proceed/iu,
20251
+ /would you like me to continue/iu,
20252
+ /do you want me to continue/iu,
20253
+ /let me know when/iu,
20231
20254
  /which (one|option|path|approach)/iu,
20232
20255
  /would you like/iu,
20233
20256
  /please provide/iu,
@@ -20249,7 +20272,7 @@ function hasUnansweredQuestion(messages) {
20249
20272
  `);
20250
20273
  if (combinedText.length > 0) {
20251
20274
  const normalized = combinedText.trim();
20252
- const looksLikeQuestion = /[?\uFF1F]$/.test(normalized) || waitPatterns.some((pattern) => pattern.test(normalized));
20275
+ const looksLikeQuestion = /[?\uFF1F]/.test(normalized) || waitPatterns.some((pattern) => pattern.test(normalized));
20253
20276
  if (looksLikeQuestion) {
20254
20277
  log(`[${HOOK_NAME}] Detected textual prompt awaiting user response`);
20255
20278
  return true;
@@ -52474,6 +52497,7 @@ init_logger();
52474
52497
  var sessionModels = new Map;
52475
52498
  var sessionModelLocks = new Map;
52476
52499
  var sessionForcedModels = new Map;
52500
+ var sessionAutoModelRouting = new Map;
52477
52501
  function setSessionModel(sessionID, model) {
52478
52502
  sessionModels.set(sessionID, model);
52479
52503
  }
@@ -52484,6 +52508,7 @@ function clearSessionModel(sessionID) {
52484
52508
  sessionModels.delete(sessionID);
52485
52509
  sessionModelLocks.delete(sessionID);
52486
52510
  sessionForcedModels.delete(sessionID);
52511
+ sessionAutoModelRouting.delete(sessionID);
52487
52512
  }
52488
52513
  function setSessionModelLock(sessionID, model) {
52489
52514
  sessionModelLocks.set(sessionID, model);
@@ -52503,6 +52528,12 @@ function getSessionForcedModel(sessionID) {
52503
52528
  function clearSessionForcedModel(sessionID) {
52504
52529
  sessionForcedModels.delete(sessionID);
52505
52530
  }
52531
+ function setSessionAutoModelRouting(sessionID, enabled) {
52532
+ sessionAutoModelRouting.set(sessionID, enabled);
52533
+ }
52534
+ function isSessionAutoModelRoutingEnabled(sessionID) {
52535
+ return sessionAutoModelRouting.get(sessionID) === true;
52536
+ }
52506
52537
 
52507
52538
  // src/hooks/compaction-context-injector/session-id.ts
52508
52539
  function isCompactionAgent(agent) {
@@ -54181,6 +54212,8 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
54181
54212
  const model = props?.model;
54182
54213
  if (!sessionID || status?.type !== "retry")
54183
54214
  return;
54215
+ if (!isSessionAutoModelRoutingEnabled(sessionID))
54216
+ return;
54184
54217
  const retryMessage = typeof status.message === "string" ? status.message : "";
54185
54218
  const retrySignal = extractAutoRetrySignal({ status: retryMessage, message: retryMessage });
54186
54219
  if (!retrySignal)
@@ -54316,6 +54349,10 @@ function createEventHandler(deps, helpers) {
54316
54349
  log(`[${HOOK_NAME12}] session.error without sessionID, skipping`);
54317
54350
  return;
54318
54351
  }
54352
+ if (!isSessionAutoModelRoutingEnabled(sessionID)) {
54353
+ log(`[${HOOK_NAME12}] session.error fallback skipped - auto model routing disabled`, { sessionID });
54354
+ return;
54355
+ }
54319
54356
  const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
54320
54357
  if (sessionRetryInFlight.has(sessionID)) {
54321
54358
  log(`[${HOOK_NAME12}] session.error skipped \u2014 retry in flight`, {
@@ -54516,6 +54553,9 @@ function createMessageUpdateHandler(deps, helpers) {
54516
54553
  return;
54517
54554
  }
54518
54555
  if (sessionID && role === "assistant" && error48) {
54556
+ if (!isSessionAutoModelRoutingEnabled(sessionID)) {
54557
+ return;
54558
+ }
54519
54559
  sessionAwaitingFallbackResult.delete(sessionID);
54520
54560
  if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
54521
54561
  log(`[${HOOK_NAME12}] message.updated fallback skipped (retry in flight)`, { sessionID });
@@ -54617,6 +54657,8 @@ function createChatMessageHandler2(deps) {
54617
54657
  let state3 = sessionStates.get(sessionID);
54618
54658
  if (!state3)
54619
54659
  return;
54660
+ if (!isSessionAutoModelRoutingEnabled(sessionID))
54661
+ return;
54620
54662
  sessionLastAccess.set(sessionID, Date.now());
54621
54663
  const requestedModel = input.model ? `${input.model.providerID}/${input.model.modelID}` : undefined;
54622
54664
  if (requestedModel && requestedModel !== state3.currentModel) {
@@ -77872,7 +77914,7 @@ function createSessionHooks(args) {
77872
77914
  fallbackTitleState.delete(oldestKey);
77873
77915
  }
77874
77916
  };
77875
- const isModelFallbackConfigEnabled = pluginConfig.model_fallback ?? true;
77917
+ const isModelFallbackConfigEnabled = pluginConfig.model_fallback ?? false;
77876
77918
  const modelFallback = isModelFallbackConfigEnabled && isHookEnabled("model-fallback") ? safeHook("model-fallback", () => createModelFallbackHook({
77877
77919
  toast: async ({ title, message, variant, duration: duration5 }) => {
77878
77920
  await ctx.client.tui.showToast({
@@ -97821,7 +97863,18 @@ function applyResolvedUltraworkOverride(args) {
97821
97863
  });
97822
97864
  showToast3(tui, "Ultrawork Model Override", `${fromModel} \u2192 ${override.modelID}. Maximum precision engaged.`);
97823
97865
  }
97824
- function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, output, tui, sessionID, manualModelChangeDetected = false, client2) {
97866
+ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, output, tui, sessionID, manualModelChangeDetectedOrClient = false, client2, allowAutomaticModelOverride = true) {
97867
+ let manualModelChangeDetected = false;
97868
+ let resolvedClient = client2;
97869
+ if (typeof manualModelChangeDetectedOrClient === "boolean") {
97870
+ manualModelChangeDetected = manualModelChangeDetectedOrClient;
97871
+ } else if (manualModelChangeDetectedOrClient !== undefined && manualModelChangeDetectedOrClient !== null) {
97872
+ resolvedClient = manualModelChangeDetectedOrClient;
97873
+ }
97874
+ if (!allowAutomaticModelOverride) {
97875
+ log("[ultrawork-model-override] Skip override; auto model routing disabled for session");
97876
+ return;
97877
+ }
97825
97878
  if (manualModelChangeDetected) {
97826
97879
  log("[ultrawork-model-override] Skip override; manual model change detected");
97827
97880
  return;
@@ -97831,7 +97884,7 @@ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, outp
97831
97884
  return;
97832
97885
  const currentModel = getMessageModel(output.message.model);
97833
97886
  const variantTargetModel = override.providerID && override.modelID ? { providerID: override.providerID, modelID: override.modelID } : currentModel;
97834
- if (!client2 || typeof client2.provider?.list !== "function") {
97887
+ if (!resolvedClient || typeof resolvedClient.provider?.list !== "function") {
97835
97888
  if (override.variant) {
97836
97889
  log("[ultrawork-model-override] SDK validation unavailable, skipping variant override", {
97837
97890
  variant: override.variant
@@ -97840,7 +97893,7 @@ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, outp
97840
97893
  applyResolvedUltraworkOverride({ override, validatedVariant: undefined, output, inputAgentName, tui });
97841
97894
  return;
97842
97895
  }
97843
- return resolveValidUltraworkVariant(client2, variantTargetModel, override.variant).then((validatedVariant) => {
97896
+ return resolveValidUltraworkVariant(resolvedClient, variantTargetModel, override.variant).then((validatedVariant) => {
97844
97897
  if (override.variant && !validatedVariant) {
97845
97898
  log("[ultrawork-model-override] Skip invalid ultrawork variant override", {
97846
97899
  variant: override.variant,
@@ -97901,9 +97954,10 @@ function createChatMessageHandler3(args) {
97901
97954
  const pluginContext = ctx;
97902
97955
  const isRuntimeFallbackEnabled = hooks2.runtimeFallback !== null && hooks2.runtimeFallback !== undefined && (typeof pluginConfig.runtime_fallback === "boolean" ? pluginConfig.runtime_fallback : pluginConfig.runtime_fallback?.enabled ?? false);
97903
97956
  return async (input, output) => {
97957
+ const isInternalInitiatedPrompt = hasInternalInitiatorMarker(output.parts);
97904
97958
  const previousSessionModel = getSessionModel(input.sessionID);
97905
97959
  const currentInputModel = input.model;
97906
- const manualModelChangeDetected = currentInputModel !== undefined && previousSessionModel !== undefined && (currentInputModel.providerID !== previousSessionModel.providerID || currentInputModel.modelID !== previousSessionModel.modelID);
97960
+ const manualModelChangeDetected = !isInternalInitiatedPrompt && currentInputModel !== undefined && previousSessionModel !== undefined && (currentInputModel.providerID !== previousSessionModel.providerID || currentInputModel.modelID !== previousSessionModel.modelID);
97907
97961
  if (manualModelChangeDetected) {
97908
97962
  clearPendingModelFallback(input.sessionID);
97909
97963
  clearSessionFallbackChain(input.sessionID);
@@ -97913,8 +97967,14 @@ function createChatMessageHandler3(args) {
97913
97967
  const forcedModel = getSessionForcedModel(input.sessionID);
97914
97968
  const rawInputModel = input.model;
97915
97969
  const rawInputModelId = modelToString(rawInputModel);
97970
+ if (!isInternalInitiatedPrompt && rawInputModel !== undefined) {
97971
+ setSessionAutoModelRouting(input.sessionID, isAutoModelSelection(rawInputModelId));
97972
+ }
97973
+ const autoModelRoutingEnabled = isSessionAutoModelRoutingEnabled(input.sessionID);
97916
97974
  if (strictUserModelPriority && lockedModel) {
97917
- if (!rawInputModel) {
97975
+ if (isInternalInitiatedPrompt) {
97976
+ input.model = lockedModel;
97977
+ } else if (!rawInputModel) {
97918
97978
  input.model = lockedModel;
97919
97979
  } else if (isAutoModelSelection(rawInputModelId)) {
97920
97980
  clearSessionModelLock(input.sessionID);
@@ -97925,7 +97985,10 @@ function createChatMessageHandler3(args) {
97925
97985
  }
97926
97986
  }
97927
97987
  }
97928
- const soulRules = loadSoulRules({ directory: ctx.directory, pluginConfig });
97988
+ const soulRules = loadSoulRules({
97989
+ directory: typeof ctx.directory === "string" ? ctx.directory : "",
97990
+ pluginConfig
97991
+ });
97929
97992
  const injectOnce = pluginConfig.soul?.inject_once ?? true;
97930
97993
  const alreadyInjected = soulInjectedSessions.has(input.sessionID);
97931
97994
  if (soulRules.content && (!injectOnce || !alreadyInjected)) {
@@ -97960,12 +98023,14 @@ function createChatMessageHandler3(args) {
97960
98023
  if (firstMessageVariantGate.shouldOverride(input.sessionID)) {
97961
98024
  firstMessageVariantGate.markApplied(input.sessionID);
97962
98025
  }
97963
- if (!isRuntimeFallbackEnabled) {
98026
+ if (!isRuntimeFallbackEnabled && autoModelRoutingEnabled) {
97964
98027
  await hooks2.modelFallback?.["chat.message"]?.(input, output);
97965
98028
  }
97966
98029
  await hooks2.stopContinuationGuard?.["chat.message"]?.(input);
97967
98030
  await hooks2.backgroundNotificationHook?.["chat.message"]?.(input, output);
97968
- await hooks2.runtimeFallback?.["chat.message"]?.(input, output);
98031
+ if (autoModelRoutingEnabled) {
98032
+ await hooks2.runtimeFallback?.["chat.message"]?.(input, output);
98033
+ }
97969
98034
  await hooks2.keywordDetector?.["chat.message"]?.(input, output);
97970
98035
  await hooks2.thinkMode?.["chat.message"]?.(input, output);
97971
98036
  await hooks2.claudeCodeHooks?.["chat.message"]?.(input, output);
@@ -98008,7 +98073,7 @@ function createChatMessageHandler3(args) {
98008
98073
  hooks2.ralphLoop.cancelLoop(input.sessionID);
98009
98074
  }
98010
98075
  }
98011
- await applyUltraworkModelOverrideOnMessage(pluginConfig, input.agent, output, pluginContext.client.tui, input.sessionID, manualModelChangeDetected, pluginContext.client);
98076
+ await applyUltraworkModelOverrideOnMessage(pluginConfig, input.agent, output, pluginContext.client.tui, input.sessionID, manualModelChangeDetected, pluginContext.client, autoModelRoutingEnabled);
98012
98077
  const requestedModel = input.model;
98013
98078
  const requestedModelId = modelToString(requestedModel);
98014
98079
  const shouldLockToRequestedModel = strictUserModelPriority && requestedModel !== undefined && !isAutoModelSelection(requestedModelId);
@@ -98016,29 +98081,33 @@ function createChatMessageHandler3(args) {
98016
98081
  providerID: output.message["model"].providerID ?? "",
98017
98082
  modelID: output.message["model"].modelID ?? ""
98018
98083
  } : undefined;
98019
- if (requestedModel !== undefined && isAutoModelSelection(requestedModelId)) {
98020
- clearSessionModelLock(input.sessionID);
98021
- clearSessionForcedModel(input.sessionID);
98022
- } else if (shouldLockToRequestedModel) {
98023
- setSessionModelLock(input.sessionID, requestedModel);
98024
- if (modelBeforeUserLock && modelBeforeUserLock.providerID.length > 0 && modelBeforeUserLock.modelID.length > 0 && !sameModel(modelBeforeUserLock, requestedModel)) {
98025
- setSessionForcedModel(input.sessionID, modelBeforeUserLock);
98026
- } else {
98084
+ if (!isInternalInitiatedPrompt) {
98085
+ if (requestedModel !== undefined && isAutoModelSelection(requestedModelId)) {
98086
+ clearSessionModelLock(input.sessionID);
98027
98087
  clearSessionForcedModel(input.sessionID);
98088
+ } else if (shouldLockToRequestedModel) {
98089
+ setSessionModelLock(input.sessionID, requestedModel);
98090
+ if (modelBeforeUserLock && modelBeforeUserLock.providerID.length > 0 && modelBeforeUserLock.modelID.length > 0 && !sameModel(modelBeforeUserLock, requestedModel)) {
98091
+ setSessionForcedModel(input.sessionID, modelBeforeUserLock);
98092
+ } else {
98093
+ clearSessionForcedModel(input.sessionID);
98094
+ }
98028
98095
  }
98029
98096
  }
98030
- if (shouldLockToRequestedModel) {
98097
+ if (!isInternalInitiatedPrompt && shouldLockToRequestedModel) {
98031
98098
  output.message["model"] = requestedModel;
98032
98099
  }
98033
- const finalOutputModel = output.message["model"];
98034
- if (finalOutputModel && typeof finalOutputModel === "object" && "providerID" in finalOutputModel && "modelID" in finalOutputModel) {
98035
- const providerID = finalOutputModel.providerID;
98036
- const modelID = finalOutputModel.modelID;
98037
- if (typeof providerID === "string" && typeof modelID === "string") {
98038
- setSessionModel(input.sessionID, { providerID, modelID });
98100
+ if (!isInternalInitiatedPrompt) {
98101
+ const finalOutputModel = output.message["model"];
98102
+ if (finalOutputModel && typeof finalOutputModel === "object" && "providerID" in finalOutputModel && "modelID" in finalOutputModel) {
98103
+ const providerID = finalOutputModel.providerID;
98104
+ const modelID = finalOutputModel.modelID;
98105
+ if (typeof providerID === "string" && typeof modelID === "string") {
98106
+ setSessionModel(input.sessionID, { providerID, modelID });
98107
+ }
98108
+ } else if (requestedModel) {
98109
+ setSessionModel(input.sessionID, requestedModel);
98039
98110
  }
98040
- } else if (requestedModel) {
98041
- setSessionModel(input.sessionID, requestedModel);
98042
98111
  }
98043
98112
  };
98044
98113
  }
@@ -98052,6 +98121,9 @@ function sameModel(left, right) {
98052
98121
  return false;
98053
98122
  return left.providerID === right.providerID && left.modelID === right.modelID;
98054
98123
  }
98124
+ function hasInternalInitiatorMarker(parts) {
98125
+ return parts.some((part) => part.type === "text" && typeof part.text === "string" && part.text.includes(OMO_INTERNAL_INITIATOR_MARKER));
98126
+ }
98055
98127
 
98056
98128
  // src/plugin/messages-transform.ts
98057
98129
  function createMessagesTransformHandler(args) {
@@ -98109,6 +98181,11 @@ function normalizeSessionStatusToIdle(input) {
98109
98181
  function isRecord12(value) {
98110
98182
  return typeof value === "object" && value !== null;
98111
98183
  }
98184
+ function hasInternalInitiatorMarker2(parts) {
98185
+ if (!Array.isArray(parts))
98186
+ return false;
98187
+ return parts.some((part) => isRecord12(part) && part.type === "text" && typeof part.text === "string" && part.text.includes(OMO_INTERNAL_INITIATOR_MARKER));
98188
+ }
98112
98189
  function normalizeFallbackModelID(modelID) {
98113
98190
  return modelID.replace(/-thinking$/i, "").replace(/-max$/i, "").replace(/-high$/i, "");
98114
98191
  }
@@ -98184,6 +98261,43 @@ function createEventHandler2(args) {
98184
98261
  const lastHandledModelErrorMessageID = new Map;
98185
98262
  const lastHandledRetryStatusKey = new Map;
98186
98263
  const lastKnownModelBySession = new Map;
98264
+ const internalMarkerCache2 = new Map;
98265
+ const INTERNAL_MARKER_CACHE_LIMIT2 = 1000;
98266
+ const rememberInternalMarker = (cacheKey, hasMarker) => {
98267
+ internalMarkerCache2.set(cacheKey, hasMarker);
98268
+ if (internalMarkerCache2.size > INTERNAL_MARKER_CACHE_LIMIT2) {
98269
+ internalMarkerCache2.clear();
98270
+ }
98271
+ };
98272
+ const isInternalInitiatedUserMessage = async (sessionID, props, info) => {
98273
+ if (hasInternalInitiatorMarker2(props?.parts))
98274
+ return true;
98275
+ if (hasInternalInitiatorMarker2(info?.parts))
98276
+ return true;
98277
+ if (hasInternalInitiatorMarker2(info?.content))
98278
+ return true;
98279
+ const messageID = typeof info?.id === "string" ? info.id : undefined;
98280
+ if (!messageID)
98281
+ return false;
98282
+ const cacheKey = `${sessionID}:${messageID}`;
98283
+ const cached3 = internalMarkerCache2.get(cacheKey);
98284
+ if (cached3 !== undefined)
98285
+ return cached3;
98286
+ const loadMessage = pluginContext.client.session.message;
98287
+ if (typeof loadMessage !== "function")
98288
+ return false;
98289
+ try {
98290
+ const response = await loadMessage({
98291
+ path: { id: sessionID, messageID }
98292
+ });
98293
+ const hasMarker = hasInternalInitiatorMarker2(response.data?.parts);
98294
+ rememberInternalMarker(cacheKey, hasMarker);
98295
+ return hasMarker;
98296
+ } catch {
98297
+ rememberInternalMarker(cacheKey, false);
98298
+ return false;
98299
+ }
98300
+ };
98187
98301
  const resolveFallbackProviderID = (sessionID, providerHint) => {
98188
98302
  const sessionModel = getSessionModel(sessionID);
98189
98303
  if (sessionModel?.providerID) {
@@ -98335,18 +98449,21 @@ function createEventHandler2(args) {
98335
98449
  const agent = info?.agent;
98336
98450
  const role = info?.role;
98337
98451
  if (sessionID && role === "user") {
98338
- const isCompactionMessage = agent ? isCompactionAgent4(agent) : false;
98339
- if (agent && !isCompactionMessage) {
98340
- updateSessionAgent(sessionID, agent);
98341
- }
98342
- const providerID = info?.providerID;
98343
- const modelID = info?.modelID;
98344
- if (providerID && modelID && !isCompactionMessage) {
98345
- lastKnownModelBySession.set(sessionID, { providerID, modelID });
98346
- setSessionModel(sessionID, { providerID, modelID });
98452
+ const isInternalUserMessage = await isInternalInitiatedUserMessage(sessionID, props, info);
98453
+ if (!isInternalUserMessage) {
98454
+ const isCompactionMessage = agent ? isCompactionAgent4(agent) : false;
98455
+ if (agent && !isCompactionMessage) {
98456
+ updateSessionAgent(sessionID, agent);
98457
+ }
98458
+ const providerID = info?.providerID;
98459
+ const modelID = info?.modelID;
98460
+ if (providerID && modelID && !isCompactionMessage) {
98461
+ lastKnownModelBySession.set(sessionID, { providerID, modelID });
98462
+ setSessionModel(sessionID, { providerID, modelID });
98463
+ }
98347
98464
  }
98348
98465
  }
98349
- if (sessionID && role === "assistant" && !isRuntimeFallbackEnabled && isModelFallbackEnabled) {
98466
+ if (sessionID && role === "assistant" && !isRuntimeFallbackEnabled && isModelFallbackEnabled && isSessionAutoModelRoutingEnabled(sessionID)) {
98350
98467
  try {
98351
98468
  const assistantMessageID = info?.id;
98352
98469
  const assistantError = info?.error;
@@ -98390,7 +98507,7 @@ function createEventHandler2(args) {
98390
98507
  if (event.type === "session.status") {
98391
98508
  const sessionID = props?.sessionID;
98392
98509
  const status = props?.status;
98393
- if (sessionID && status?.type === "retry" && isModelFallbackEnabled && !isRuntimeFallbackEnabled) {
98510
+ if (sessionID && status?.type === "retry" && isModelFallbackEnabled && !isRuntimeFallbackEnabled && isSessionAutoModelRoutingEnabled(sessionID)) {
98394
98511
  try {
98395
98512
  const retryMessage = typeof status.message === "string" ? status.message : "";
98396
98513
  const parsedForKey = extractProviderModelFromErrorMessage(retryMessage);
@@ -98452,7 +98569,7 @@ function createEventHandler2(args) {
98452
98569
  query: { directory: pluginContext.directory }
98453
98570
  }).catch(() => {});
98454
98571
  }
98455
- } else if (sessionID && shouldRetryError(errorInfo) && !isRuntimeFallbackEnabled && isModelFallbackEnabled) {
98572
+ } else if (sessionID && shouldRetryError(errorInfo) && !isRuntimeFallbackEnabled && isModelFallbackEnabled && isSessionAutoModelRoutingEnabled(sessionID)) {
98456
98573
  let agentName = getSessionAgent(sessionID);
98457
98574
  if (!agentName && sessionID === getMainSessionID()) {
98458
98575
  if (errorMessage.includes("claude-opus") || errorMessage.includes("opus")) {
@@ -20,4 +20,4 @@ export declare function applyUltraworkModelOverrideOnMessage(pluginConfig: OhMyO
20
20
  text?: string;
21
21
  [key: string]: unknown;
22
22
  }>;
23
- }, tui: unknown, sessionID?: string, manualModelChangeDetected?: boolean, client?: unknown): void | Promise<void>;
23
+ }, tui: unknown, sessionID?: string, manualModelChangeDetectedOrClient?: unknown, client?: unknown, allowAutomaticModelOverride?: boolean): void | Promise<void>;
@@ -11,3 +11,7 @@ export declare function clearSessionModelLock(sessionID: string): void;
11
11
  export declare function setSessionForcedModel(sessionID: string, model: SessionModel): void;
12
12
  export declare function getSessionForcedModel(sessionID: string): SessionModel | undefined;
13
13
  export declare function clearSessionForcedModel(sessionID: string): void;
14
+ export declare function setSessionAutoModelRouting(sessionID: string, enabled: boolean): void;
15
+ export declare function getSessionAutoModelRouting(sessionID: string): boolean | undefined;
16
+ export declare function isSessionAutoModelRoutingEnabled(sessionID: string): boolean;
17
+ export declare function clearSessionAutoModelRouting(sessionID: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bohuyeshan/openagent-labforge-core",
3
- "version": "3.11.3",
3
+ "version": "3.11.5",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",