oh-my-opencode 5.1.12 → 5.1.14

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 (101) hide show
  1. package/dist/cli/index.js +45 -18
  2. package/dist/cli-node/index.js +45 -18
  3. package/dist/features/background-agent/fallback-provider-model-filter.d.ts +12 -0
  4. package/dist/features/background-agent/manager.d.ts +1 -0
  5. package/dist/features/background-agent/session-stopped-on-error.d.ts +9 -0
  6. package/dist/features/background-agent/types.d.ts +2 -0
  7. package/dist/index.js +173 -29
  8. package/dist/oh-my-opencode.schema.json +0 -107
  9. package/dist/shared/posthog.d.ts +1 -0
  10. package/dist/skills/browser/SKILL.md +26 -2
  11. package/dist/skills/browser/references/commands.md +10 -1
  12. package/dist/skills/browser/references/install.md +7 -0
  13. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  14. package/dist/skills/browser/scripts/browser-engine-guard.mjs +506 -0
  15. package/dist/skills/browser/scripts/omowright.mjs +29 -2
  16. package/dist/tui.js +16 -16
  17. package/package.json +14 -14
  18. package/packages/lsp-core/src/post-edit/not-installed.test.ts +2 -2
  19. package/packages/lsp-core/src/post-edit/orchestration.test.ts +13 -13
  20. package/packages/lsp-core/src/post-edit/orchestration.ts +9 -3
  21. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  22. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  23. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  27. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  29. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +4 -4
  31. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +33 -19
  32. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +27 -17
  33. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  34. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +36 -18
  36. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +203 -0
  37. package/packages/omo-codex/plugin/components/rules/CHANGELOG.md +1 -0
  38. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
  39. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +1 -1
  40. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-6.md +2 -2
  41. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  42. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  44. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  46. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  48. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +89 -28
  52. package/packages/omo-codex/plugin/components/ulw-loop/dist/registered-agent-roles.d.ts +1 -0
  53. package/packages/omo-codex/plugin/components/ulw-loop/dist/registered-agent-roles.js +61 -0
  54. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +2 -1
  55. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-role-guard.d.ts +1 -1
  56. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-role-guard.js +6 -2
  57. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  58. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/ulw-loop/src/registered-agent-roles.ts +60 -0
  60. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +2 -1
  61. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-role-guard.ts +8 -2
  62. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-role-matrix.test.ts +1 -0
  63. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-role-registered.test.ts +128 -0
  64. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  85. package/packages/omo-codex/plugin/package-lock.json +12 -12
  86. package/packages/omo-codex/plugin/package.json +1 -1
  87. package/packages/omo-codex/plugin/skills/browser/SKILL.md +26 -2
  88. package/packages/omo-codex/plugin/skills/browser/references/commands.md +10 -1
  89. package/packages/omo-codex/plugin/skills/browser/references/install.md +7 -0
  90. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  91. package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +506 -0
  92. package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +29 -2
  93. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +4 -3
  94. package/packages/omo-codex/plugin/test/aggregate.test.mjs +2 -2
  95. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  96. package/packages/shared-skills/skills/browser/SKILL.md +26 -2
  97. package/packages/shared-skills/skills/browser/references/commands.md +10 -1
  98. package/packages/shared-skills/skills/browser/references/install.md +7 -0
  99. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  100. package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +506 -0
  101. package/packages/shared-skills/skills/browser/scripts/omowright.mjs +29 -2
package/dist/index.js CHANGED
@@ -4689,7 +4689,11 @@ var init_tool_name = __esm(() => {
4689
4689
 
4690
4690
  // packages/utils/src/replace-tool-args.ts
4691
4691
  function replaceToolArgs(output, patch) {
4692
- output.args = { ...output.args, ...patch };
4692
+ if (Object.isFrozen(output.args)) {
4693
+ output.args = { ...output.args, ...patch };
4694
+ return;
4695
+ }
4696
+ Object.assign(output.args, patch);
4693
4697
  }
4694
4698
 
4695
4699
  // packages/utils/src/format-duration.ts
@@ -27784,7 +27788,7 @@ var UNSPECIFIED_HIGH_CATEGORY_PROMPT_APPEND = `<Category_Context>
27784
27788
  You are working on tasks that don't fit specific categories but require substantial effort.
27785
27789
  </Category_Context>`;
27786
27790
  var UNSPECIFIED_HIGH_CATEGORY_CALLER_GUIDANCE = `<Selection_Gate>Use only when no specialist category fits and substantial effort spans systems/modules with broad impact. Use unspecified-low for contained moderate work.</Selection_Gate>`;
27787
- var DEEP_LOW_CATEGORY_CALLER_GUIDANCE = `<Selection_Gate>Route here when one subsystem plus its callers holds the mechanism and the evidence, once read, leaves one right answer. Wide but mechanical work belongs here or in a quick batch. When unsure, choose deep-low: a misrouted child returns \`ESCALATE: deep-high\` after one cheap attempt; re-spawn the same brief as deep-high with its findings.</Selection_Gate>`;
27791
+ var DEEP_LOW_CATEGORY_CALLER_GUIDANCE = `<Selection_Gate>Of the two deep lanes this is the default: the child settles its decisions from what it reads. When unsure, choose deep-low; a misrouted child returns \`ESCALATE: deep-high\` after one cheap attempt, and you re-spawn the same brief as deep-high with its findings.</Selection_Gate>`;
27788
27792
  var DEEP_HIGH_CATEGORY_CALLER_GUIDANCE = `<Selection_Gate>Route here only when you can name the decision evidence cannot settle: a trade-off with no single right answer, a contract change crossing a package or process boundary, a mechanism with no in-repo pattern to copy, or correctness argued from invariants rather than observed in a test. Wide scope with easy decisions is deep-low or unspecified-high; reasoning as the deliverable is ultrabrain.</Selection_Gate>`;
27789
27793
  var DEEP_LOW_GATE_MODELS = ["gpt-6.1-sol", "gpt-6.1-sol-fast", "gpt-5.6-sol-fast", "gpt-5.6-sol"];
27790
27794
  var DEEP_HIGH_GATE_MODEL = "gpt-6-astra";
@@ -27799,7 +27803,7 @@ var OPENAI_CATEGORIES = [
27799
27803
  {
27800
27804
  name: "deep-low",
27801
27805
  config: { model: "openai/gpt-6.1-sol", variant: "medium" },
27802
- description: "Default deep lane: one goal, one deliverable, decisions the child can settle from what it reads. **3D graphics, computer/browser use, CAPTCHA, multimodal, backend, logic, and algorithm work is routed here.** Multiple goals fan out as parallel calls.",
27806
+ description: "Default deep lane: one goal, one deliverable, decisions the child can settle from what it reads; preferred over deep-high for 3D graphics, computer/browser use, CAPTCHA, multimodal, backend, logic, and algorithm work. Multiple goals fan out as parallel calls.",
27803
27807
  callerGuidance: DEEP_LOW_CATEGORY_CALLER_GUIDANCE,
27804
27808
  promptAppend: DEEP_LOW_CATEGORY_PROMPT_APPEND,
27805
27809
  resolvePromptAppend: resolveDeepLowCategoryPromptAppend,
@@ -98582,7 +98586,7 @@ function getCachedVersion(options = {}) {
98582
98586
  // package.json
98583
98587
  var package_default = {
98584
98588
  name: "oh-my-opencode",
98585
- version: "5.1.12",
98589
+ version: "5.1.14",
98586
98590
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
98587
98591
  main: "./dist/index.js",
98588
98592
  types: "dist/index.d.ts",
@@ -98759,7 +98763,7 @@ var package_default = {
98759
98763
  },
98760
98764
  devDependencies: {
98761
98765
  "@clack/prompts": "^1.8.1",
98762
- "@code-yeongyu/senpi": "2026.10.2",
98766
+ "@code-yeongyu/senpi": "2026.10.5",
98763
98767
  "@modelcontextprotocol/sdk": "^1.30.0",
98764
98768
  "@oh-my-opencode/agents-md-core": "workspace:*",
98765
98769
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -98817,18 +98821,18 @@ var package_default = {
98817
98821
  typescript: "^7.0.2"
98818
98822
  },
98819
98823
  optionalDependencies: {
98820
- "oh-my-opencode-darwin-arm64": "5.1.12",
98821
- "oh-my-opencode-darwin-x64": "5.1.12",
98822
- "oh-my-opencode-darwin-x64-baseline": "5.1.12",
98823
- "oh-my-opencode-linux-arm64": "5.1.12",
98824
- "oh-my-opencode-linux-arm64-musl": "5.1.12",
98825
- "oh-my-opencode-linux-x64": "5.1.12",
98826
- "oh-my-opencode-linux-x64-baseline": "5.1.12",
98827
- "oh-my-opencode-linux-x64-musl": "5.1.12",
98828
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.12",
98829
- "oh-my-opencode-windows-arm64": "5.1.12",
98830
- "oh-my-opencode-windows-x64": "5.1.12",
98831
- "oh-my-opencode-windows-x64-baseline": "5.1.12"
98824
+ "oh-my-opencode-darwin-arm64": "5.1.14",
98825
+ "oh-my-opencode-darwin-x64": "5.1.14",
98826
+ "oh-my-opencode-darwin-x64-baseline": "5.1.14",
98827
+ "oh-my-opencode-linux-arm64": "5.1.14",
98828
+ "oh-my-opencode-linux-arm64-musl": "5.1.14",
98829
+ "oh-my-opencode-linux-x64": "5.1.14",
98830
+ "oh-my-opencode-linux-x64-baseline": "5.1.14",
98831
+ "oh-my-opencode-linux-x64-musl": "5.1.14",
98832
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.14",
98833
+ "oh-my-opencode-windows-arm64": "5.1.14",
98834
+ "oh-my-opencode-windows-x64": "5.1.14",
98835
+ "oh-my-opencode-windows-x64-baseline": "5.1.14"
98832
98836
  },
98833
98837
  overrides: {
98834
98838
  hono: "^4.13.8",
@@ -107944,11 +107948,17 @@ var TOAST_MESSAGE = [
107944
107948
  "For other GPT models, always use Hephaestus."
107945
107949
  ].join(`
107946
107950
  `);
107947
- function showToast(ctx, sessionID) {
107951
+ var HEPHAESTUS_UNAVAILABLE_TOAST_MESSAGE = [
107952
+ "Sisyphus is running with a GPT model it has no native prompt for.",
107953
+ "Hephaestus is not available in this session (disabled, or its configured model is not a supported GPT model), so the agent was not switched.",
107954
+ "Use a Claude, Kimi, or GLM model for Sisyphus, or enable Hephaestus with a supported GPT model."
107955
+ ].join(`
107956
+ `);
107957
+ function showToast(ctx, sessionID, message) {
107948
107958
  ctx.client.tui.showToast({
107949
107959
  body: {
107950
107960
  title: TOAST_TITLE,
107951
- message: TOAST_MESSAGE,
107961
+ message,
107952
107962
  variant: "error",
107953
107963
  duration: 1e4
107954
107964
  }
@@ -107984,10 +107994,19 @@ function createNoSisyphusGptHook(ctx) {
107984
107994
  }
107985
107995
  }
107986
107996
  if (agentKey === "sisyphus" && modelID && isGptModel(modelID) && !isGptNativeSisyphusModel(modelID) && !isGpt6Model(modelID)) {
107987
- showToast(ctx, input.sessionID);
107988
- input.agent = resolveRegisteredAgentName("hephaestus") ?? "hephaestus";
107997
+ if (!isAgentRegistered("hephaestus")) {
107998
+ showToast(ctx, input.sessionID, HEPHAESTUS_UNAVAILABLE_TOAST_MESSAGE);
107999
+ log2("[no-sisyphus-gpt] Hephaestus is not registered; keeping Sisyphus instead of redirecting", {
108000
+ sessionID: input.sessionID,
108001
+ modelID
108002
+ });
108003
+ return;
108004
+ }
108005
+ const hephaestusAgent = resolveRegisteredAgentName("hephaestus") ?? "hephaestus";
108006
+ showToast(ctx, input.sessionID, TOAST_MESSAGE);
108007
+ input.agent = hephaestusAgent;
107989
108008
  if (output?.message) {
107990
- output.message.agent = resolveRegisteredAgentName("hephaestus") ?? "hephaestus";
108009
+ output.message.agent = hephaestusAgent;
107991
108010
  }
107992
108011
  updateSessionAgent(input.sessionID, "hephaestus");
107993
108012
  }
@@ -108472,11 +108491,18 @@ var SCOPE_PRIORITY = {
108472
108491
  config: 1,
108473
108492
  builtin: 1
108474
108493
  };
108494
+ function compareCodeUnits(left, right) {
108495
+ if (left < right)
108496
+ return -1;
108497
+ if (left > right)
108498
+ return 1;
108499
+ return 0;
108500
+ }
108475
108501
  function sortByScopePriority(items) {
108476
108502
  return [...items].sort((left, right) => {
108477
108503
  const leftPriority = SCOPE_PRIORITY[left.scope] || 0;
108478
108504
  const rightPriority = SCOPE_PRIORITY[right.scope] || 0;
108479
- return rightPriority - leftPriority;
108505
+ return rightPriority - leftPriority || compareCodeUnits(left.name, right.name);
108480
108506
  });
108481
108507
  }
108482
108508
 
@@ -129572,6 +129598,7 @@ function resolveModelAndFallbackChain(args) {
129572
129598
  const agentOverride = agentOverrides?.[agentConfigKey] ?? (agentOverrides ? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentConfigKey)?.[1] : undefined);
129573
129599
  const agentCategoryModel = agentOverride?.category ? userCategories?.[agentOverride.category]?.model : undefined;
129574
129600
  const agentCategoryVariant = agentOverride?.category ? userCategories?.[agentOverride.category]?.variant : undefined;
129601
+ const hasExplicitUserModel = Boolean(agentOverride?.model ?? agentCategoryModel);
129575
129602
  let model;
129576
129603
  if (agentOverride?.model) {
129577
129604
  const normalized = parseModelString(agentOverride.model);
@@ -129614,7 +129641,7 @@ function resolveModelAndFallbackChain(args) {
129614
129641
  const configuredFallbackChain = buildFallbackChainFromModels(normalizedFallbackModels, defaultProviderID);
129615
129642
  return {
129616
129643
  model,
129617
- fallbackChain: configuredFallbackChain ?? agentRequirement?.fallbackChain
129644
+ fallbackChain: configuredFallbackChain ?? (hasExplicitUserModel ? undefined : agentRequirement?.fallbackChain)
129618
129645
  };
129619
129646
  }
129620
129647
  function createCallOmoAgent(ctx, backgroundManager, disabledAgents = [], agentOverrides, userCategories, modelFallbackControllerAccessor) {
@@ -137200,6 +137227,31 @@ function isEmptyNoProgressAssistantTurnInfo(info) {
137200
137227
  return !!infoRecord && infoRecord.role === "assistant" && infoRecord.finish === "unknown" && allTokenCountsIndicateNoProgress(infoRecord.tokens);
137201
137228
  }
137202
137229
 
137230
+ // packages/omo-opencode/src/features/background-agent/fallback-provider-model-filter.ts
137231
+ function canonicalizeModelID2(modelID) {
137232
+ return modelID.toLowerCase().replace(/\./g, "-");
137233
+ }
137234
+ function providerListsModel(providerModels, modelID) {
137235
+ const wanted = canonicalizeModelID2(modelID);
137236
+ return providerModels.some((entry) => {
137237
+ const id = typeof entry === "string" ? entry : entry.id;
137238
+ return canonicalizeModelID2(id) === wanted;
137239
+ });
137240
+ }
137241
+ function filterProvidersServingModel(args) {
137242
+ const { providers, model, connectedSet, modelsByProvider, transformModelForProvider } = args;
137243
+ if (!connectedSet || !modelsByProvider)
137244
+ return providers;
137245
+ return providers.filter((provider) => {
137246
+ if (!connectedSet.has(provider.toLowerCase()))
137247
+ return true;
137248
+ const providerModels = modelsByProvider[provider];
137249
+ if (!providerModels || providerModels.length === 0)
137250
+ return true;
137251
+ return providerListsModel(providerModels, transformModelForProvider(provider, model));
137252
+ });
137253
+ }
137254
+
137203
137255
  // packages/omo-opencode/src/features/background-agent/fallback-retry-handler.ts
137204
137256
  class TeamModeFallbackError extends Error {
137205
137257
  constructor(message) {
@@ -137207,7 +137259,7 @@ class TeamModeFallbackError extends Error {
137207
137259
  this.name = "TeamModeFallbackError";
137208
137260
  }
137209
137261
  }
137210
- function canonicalizeModelID2(modelID) {
137262
+ function canonicalizeModelID3(modelID) {
137211
137263
  return modelID.toLowerCase().replace(/\./g, "-");
137212
137264
  }
137213
137265
  var defaultFallbackRetryHandlerDeps = {
@@ -137255,9 +137307,25 @@ async function tryFallbackRetry(args) {
137255
137307
  });
137256
137308
  continue;
137257
137309
  }
137258
- const candidateProviderID = deps.selectFallbackProvider(candidate.providers, task.model?.providerID);
137310
+ const servingProviders = filterProvidersServingModel({
137311
+ providers: candidate.providers,
137312
+ model: candidate.model,
137313
+ connectedSet,
137314
+ modelsByProvider: providerModelsCache?.models,
137315
+ transformModelForProvider: deps.transformModelForProvider
137316
+ });
137317
+ if (!isReachable({ ...candidate, providers: servingProviders })) {
137318
+ deps.log("[background-agent] Skipping fallback no connected provider serves:", {
137319
+ taskId: task.id,
137320
+ source,
137321
+ model: candidate.model,
137322
+ providers: candidate.providers
137323
+ });
137324
+ continue;
137325
+ }
137326
+ const candidateProviderID = deps.selectFallbackProvider(servingProviders, task.model?.providerID);
137259
137327
  const candidateModelID = deps.transformModelForProvider(candidateProviderID, candidate.model);
137260
- const isNoOpFallback = !!task.model && candidateProviderID.toLowerCase() === task.model.providerID.toLowerCase() && canonicalizeModelID2(candidateModelID) === canonicalizeModelID2(task.model.modelID);
137328
+ const isNoOpFallback = !!task.model && candidateProviderID.toLowerCase() === task.model.providerID.toLowerCase() && canonicalizeModelID3(candidateModelID) === canonicalizeModelID3(task.model.modelID);
137261
137329
  if (isNoOpFallback) {
137262
137330
  deps.log("[background-agent] Skipping no-op fallback:", {
137263
137331
  taskId: task.id,
@@ -138862,6 +138930,29 @@ async function verifySessionExists(client, sessionID, directory) {
138862
138930
  return await checkSessionExistence(client, sessionID, directory) !== "missing";
138863
138931
  }
138864
138932
 
138933
+ // packages/omo-opencode/src/features/background-agent/session-stopped-on-error.ts
138934
+ var MIN_ERRORED_IDLE_POLLS = 10;
138935
+ function describeSessionError(error) {
138936
+ if (!isRecord9(error)) {
138937
+ return String(error);
138938
+ }
138939
+ const data = isRecord9(error.data) ? error.data : undefined;
138940
+ const message = typeof data?.message === "string" ? data.message : typeof error.message === "string" ? error.message : undefined;
138941
+ const name = typeof error.name === "string" ? error.name : undefined;
138942
+ return [name, message].filter((value) => Boolean(value)).join(": ") || "unknown error";
138943
+ }
138944
+ function getStoppedSessionError(messages) {
138945
+ const latest = messages[messages.length - 1];
138946
+ if (latest?.info?.role !== "assistant") {
138947
+ return;
138948
+ }
138949
+ const error = latest.info.error;
138950
+ if (error === undefined || error === null) {
138951
+ return;
138952
+ }
138953
+ return describeSessionError(error);
138954
+ }
138955
+
138865
138956
  // packages/omo-opencode/src/features/background-agent/session-idle-event-handler.ts
138866
138957
  function handleSessionIdleBackgroundEvent(args) {
138867
138958
  const {
@@ -141466,6 +141557,18 @@ The task was re-queued on a fallback model after a retryable failure.
141466
141557
  return true;
141467
141558
  }
141468
141559
  }
141560
+ async readStoppedSessionError(sessionID) {
141561
+ try {
141562
+ const response = await messagesInDirectory(this.client, {
141563
+ path: { id: sessionID }
141564
+ }, this.directory);
141565
+ const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
141566
+ return getStoppedSessionError(messages);
141567
+ } catch (error) {
141568
+ log2("[background-agent] Error reading session messages for stopped-session check:", error);
141569
+ return;
141570
+ }
141571
+ }
141469
141572
  clearNotificationsForTask(taskId) {
141470
141573
  for (const [sessionID, tasks] of this.notifications.entries()) {
141471
141574
  const filtered = tasks.filter((t) => t.id !== taskId);
@@ -142036,6 +142139,7 @@ The task was re-queued on a fallback model after a retryable failure.
142036
142139
  }
142037
142140
  }
142038
142141
  if (sessionStatus && isActiveSessionStatus2(sessionStatus.type)) {
142142
+ task.consecutiveErroredIdlePolls = 0;
142039
142143
  log2("[background-agent] Session still running, relying on event-based progress:", {
142040
142144
  taskId: task.id,
142041
142145
  sessionID,
@@ -142072,6 +142176,21 @@ The task was re-queued on a fallback model after a retryable failure.
142072
142176
  }
142073
142177
  task.consecutiveMissedPolls = 0;
142074
142178
  }
142179
+ const stoppedError = await this.readStoppedSessionError(sessionID);
142180
+ if (stoppedError) {
142181
+ task.consecutiveErroredIdlePolls = (task.consecutiveErroredIdlePolls ?? 0) + 1;
142182
+ if (task.consecutiveErroredIdlePolls >= MIN_ERRORED_IDLE_POLLS) {
142183
+ log2("[background-agent] Session stopped on an error with no output and was not resumed, marking task as error:", {
142184
+ taskId: task.id,
142185
+ sessionID,
142186
+ error: stoppedError.slice(0, 200)
142187
+ });
142188
+ await this.failCrashedTask(task, `Subagent session stopped on an error without producing output: ${stoppedError}`);
142189
+ continue;
142190
+ }
142191
+ } else {
142192
+ task.consecutiveErroredIdlePolls = 0;
142193
+ }
142075
142194
  log2("[background-agent] Polling idle/gone but no valid output yet, waiting:", task.id);
142076
142195
  continue;
142077
142196
  }
@@ -186629,10 +186748,14 @@ function resolveOsProvider() {
186629
186748
  function resolveTransportFactory() {
186630
186749
  return transportFactoryOverride ?? createDefaultPostHogTransport;
186631
186750
  }
186751
+ var PLUGIN_TRANSPORT_OPTIONS = { flushAt: 20 };
186632
186752
  var NO_OP_POSTHOG = {
186633
186753
  trackActive: () => {
186634
186754
  return;
186635
186755
  },
186756
+ flush: async () => {
186757
+ return;
186758
+ },
186636
186759
  shutdown: async () => {
186637
186760
  return;
186638
186761
  }
@@ -186669,6 +186792,16 @@ function logTelemetryDiagnostic(input) {
186669
186792
  source: input.source
186670
186793
  });
186671
186794
  }
186795
+ function createProductConfig(source) {
186796
+ const product = createOpencodeTelemetryProductConfig();
186797
+ if (source !== "plugin") {
186798
+ return product;
186799
+ }
186800
+ return {
186801
+ ...product,
186802
+ transportOptions: { ...product.transportOptions, ...PLUGIN_TRANSPORT_OPTIONS }
186803
+ };
186804
+ }
186672
186805
  function createPostHogClient(source, options = {}) {
186673
186806
  const env = process.env;
186674
186807
  if (shouldDisablePostHog(env, options.configEnabled)) {
@@ -186678,7 +186811,7 @@ function createPostHogClient(source, options = {}) {
186678
186811
  diagnostics: logTelemetryDiagnostic,
186679
186812
  env: createCoreCompatibleTelemetryEnv(env),
186680
186813
  osProvider: resolveOsProvider(),
186681
- product: createOpencodeTelemetryProductConfig(),
186814
+ product: createProductConfig(source),
186682
186815
  source,
186683
186816
  transportFactory: resolveTransportFactory()
186684
186817
  });
@@ -186697,6 +186830,17 @@ function createPostHogClient(source, options = {}) {
186697
186830
  reason
186698
186831
  });
186699
186832
  },
186833
+ flush: async () => {
186834
+ try {
186835
+ await client.flush();
186836
+ } catch (error) {
186837
+ log2("[posthog] telemetry flush failed", {
186838
+ error: String(error),
186839
+ errorKind: error instanceof Error ? "error" : "non_error",
186840
+ source
186841
+ });
186842
+ }
186843
+ },
186700
186844
  shutdown: async () => {
186701
186845
  await client.shutdown();
186702
186846
  }
@@ -186712,7 +186856,7 @@ function recordPluginTelemetry(input) {
186712
186856
  const posthog = createPluginPostHog({ configEnabled: input.configEnabled });
186713
186857
  const distinctId = getPostHogDistinctId();
186714
186858
  posthog.trackActive(distinctId, "plugin_loaded");
186715
- posthog.shutdown();
186859
+ posthog.flush();
186716
186860
  }
186717
186861
 
186718
186862
  // packages/omo-opencode/src/testing/create-plugin-module.ts
@@ -8898,10 +8898,6 @@
8898
8898
  "maximum": 10
8899
8899
  }
8900
8900
  },
8901
- "required": [
8902
- "enabled",
8903
- "turns"
8904
- ],
8905
8901
  "additionalProperties": false
8906
8902
  },
8907
8903
  "protected_tools": {
@@ -8930,9 +8926,6 @@
8930
8926
  "type": "boolean"
8931
8927
  }
8932
8928
  },
8933
- "required": [
8934
- "enabled"
8935
- ],
8936
8929
  "additionalProperties": false
8937
8930
  },
8938
8931
  "supersede_writes": {
@@ -8947,10 +8940,6 @@
8947
8940
  "type": "boolean"
8948
8941
  }
8949
8942
  },
8950
- "required": [
8951
- "enabled",
8952
- "aggressive"
8953
- ],
8954
8943
  "additionalProperties": false
8955
8944
  },
8956
8945
  "purge_errors": {
@@ -8967,21 +8956,12 @@
8967
8956
  "maximum": 20
8968
8957
  }
8969
8958
  },
8970
- "required": [
8971
- "enabled",
8972
- "turns"
8973
- ],
8974
8959
  "additionalProperties": false
8975
8960
  }
8976
8961
  },
8977
8962
  "additionalProperties": false
8978
8963
  }
8979
8964
  },
8980
- "required": [
8981
- "enabled",
8982
- "notification",
8983
- "protected_tools"
8984
- ],
8985
8965
  "additionalProperties": false
8986
8966
  },
8987
8967
  "task_system": {
@@ -9146,11 +9126,6 @@
9146
9126
  "maximum": 1000
9147
9127
  }
9148
9128
  },
9149
- "required": [
9150
- "enabled",
9151
- "auto_start",
9152
- "default_max_iterations"
9153
- ],
9154
9129
  "additionalProperties": false
9155
9130
  },
9156
9131
  "ralph_loop": {
@@ -9366,10 +9341,6 @@
9366
9341
  "type": "number"
9367
9342
  }
9368
9343
  },
9369
- "required": [
9370
- "type",
9371
- "method"
9372
- ],
9373
9344
  "additionalProperties": false
9374
9345
  }
9375
9346
  },
@@ -9394,7 +9365,6 @@
9394
9365
  }
9395
9366
  },
9396
9367
  "required": [
9397
- "enabled",
9398
9368
  "gateway",
9399
9369
  "instruction"
9400
9370
  ],
@@ -9443,21 +9413,9 @@
9443
9413
  "type": "boolean"
9444
9414
  }
9445
9415
  },
9446
- "required": [
9447
- "authorizedDiscordUserIds",
9448
- "pollIntervalMs",
9449
- "rateLimitPerMinute",
9450
- "maxMessageLength",
9451
- "includePrefix"
9452
- ],
9453
9416
  "additionalProperties": false
9454
9417
  }
9455
9418
  },
9456
- "required": [
9457
- "enabled",
9458
- "gateways",
9459
- "hooks"
9460
- ],
9461
9419
  "additionalProperties": false
9462
9420
  },
9463
9421
  "i18n": {
@@ -9535,18 +9493,6 @@
9535
9493
  "maximum": 9007199254740991
9536
9494
  }
9537
9495
  },
9538
- "required": [
9539
- "enabled",
9540
- "live_mode_enabled",
9541
- "max_monitors_per_session",
9542
- "max_runtime_ms",
9543
- "batch_max_lines",
9544
- "batch_max_bytes",
9545
- "flush_interval_ms",
9546
- "ring_max_lines",
9547
- "line_max_bytes",
9548
- "pattern_max_length"
9549
- ],
9550
9496
  "additionalProperties": false
9551
9497
  },
9552
9498
  "team_mode": {
@@ -9612,18 +9558,6 @@
9612
9558
  "maximum": 9007199254740991
9613
9559
  }
9614
9560
  },
9615
- "required": [
9616
- "enabled",
9617
- "tmux_visualization",
9618
- "max_parallel_members",
9619
- "max_members",
9620
- "max_messages_per_run",
9621
- "max_wall_clock_minutes",
9622
- "max_member_turns",
9623
- "message_payload_max_bytes",
9624
- "recipient_unread_max_bytes",
9625
- "mailbox_poll_interval_ms"
9626
- ],
9627
9561
  "additionalProperties": false
9628
9562
  },
9629
9563
  "keyword_detector": {
@@ -9664,9 +9598,6 @@
9664
9598
  "type": "number"
9665
9599
  }
9666
9600
  },
9667
- "required": [
9668
- "timeout_ms"
9669
- ],
9670
9601
  "additionalProperties": false
9671
9602
  },
9672
9603
  "git_master": {
@@ -9693,11 +9624,6 @@
9693
9624
  "type": "string"
9694
9625
  }
9695
9626
  },
9696
- "required": [
9697
- "commit_footer",
9698
- "include_co_authored_by",
9699
- "git_env_prefix"
9700
- ],
9701
9627
  "additionalProperties": false
9702
9628
  },
9703
9629
  "browser_automation_engine": {
@@ -9719,9 +9645,6 @@
9719
9645
  }
9720
9646
  }
9721
9647
  },
9722
- "required": [
9723
- "provider"
9724
- ],
9725
9648
  "additionalProperties": false
9726
9649
  },
9727
9650
  "websearch": {
@@ -9781,14 +9704,6 @@
9781
9704
  ]
9782
9705
  }
9783
9706
  },
9784
- "required": [
9785
- "enabled",
9786
- "layout",
9787
- "main_pane_size",
9788
- "main_pane_min_width",
9789
- "agent_pane_min_width",
9790
- "isolation"
9791
- ],
9792
9707
  "additionalProperties": false
9793
9708
  },
9794
9709
  "tui": {
@@ -9810,15 +9725,9 @@
9810
9725
  "type": "boolean"
9811
9726
  }
9812
9727
  },
9813
- "required": [
9814
- "enabled"
9815
- ],
9816
9728
  "additionalProperties": false
9817
9729
  }
9818
9730
  },
9819
- "required": [
9820
- "sidebar"
9821
- ],
9822
9731
  "additionalProperties": false
9823
9732
  },
9824
9733
  "sisyphus": {
@@ -9838,9 +9747,6 @@
9838
9747
  "type": "boolean"
9839
9748
  }
9840
9749
  },
9841
- "required": [
9842
- "claude_code_compat"
9843
- ],
9844
9750
  "additionalProperties": false
9845
9751
  }
9846
9752
  },
@@ -9854,9 +9760,6 @@
9854
9760
  "type": "boolean"
9855
9761
  }
9856
9762
  },
9857
- "required": [
9858
- "auto_commit"
9859
- ],
9860
9763
  "additionalProperties": false
9861
9764
  },
9862
9765
  "start_work": {
@@ -9867,9 +9770,6 @@
9867
9770
  "type": "boolean"
9868
9771
  }
9869
9772
  },
9870
- "required": [
9871
- "auto_commit"
9872
- ],
9873
9773
  "additionalProperties": false
9874
9774
  },
9875
9775
  "default_mode": {
@@ -9884,10 +9784,6 @@
9884
9784
  "type": "boolean"
9885
9785
  }
9886
9786
  },
9887
- "required": [
9888
- "ultrawork",
9889
- "goal"
9890
- ],
9891
9787
  "additionalProperties": false
9892
9788
  },
9893
9789
  "_migrations": {
@@ -9897,8 +9793,5 @@
9897
9793
  }
9898
9794
  }
9899
9795
  },
9900
- "required": [
9901
- "git_master"
9902
- ],
9903
9796
  "additionalProperties": false
9904
9797
  }
@@ -14,6 +14,7 @@ export type PostHogSource = "cli" | "plugin";
14
14
  export type PostHogActivityReason = "run_started" | "plugin_loaded";
15
15
  export type PostHogClient = {
16
16
  readonly trackActive: (distinctId: string, reason: PostHogActivityReason) => void;
17
+ readonly flush: () => Promise<void>;
17
18
  readonly shutdown: () => Promise<void>;
18
19
  };
19
20
  type CreatePostHogOptions = {
@@ -18,7 +18,31 @@ one where a human is a single call away. Never substitute one engine for the oth
18
18
  the attached engine is not set up, run the onboarding script and tell the user its one remaining
19
19
  step.
20
20
 
21
- ## Step 0 — load omowright and prove the stack
21
+ ## Step 0 — which engine this session is allowed to use
22
+
23
+ When `OMO_BROWSER_ENGINE` is set (the OmO desktop app sets it for every session), it wins over the table above:
24
+
25
+ | Value | What you do |
26
+ |---|---|
27
+ | `connected` | Use `connectBrowserSkill()` only. If the user's browser is not connected you get a "Connect your browser" error: relay it and stop. Never open another browser |
28
+ | `builtin` | Do not call `connectBrowserSkill()`; use the app's in-app browser tools |
29
+ | `none` | Do not do browser work. Say that agent browser access is off for this project |
30
+ | unset | The table above, as before (terminal use) |
31
+
32
+ While any engine is set, `loadOmowright()` returns a guarded library. The owned engine (`connectPipe`,
33
+ `connectCloakProfile`, `connect`) and every other export that acts on a browser is refused, so the table above
34
+ does not apply: do not look for a way around it, and tell the user what the session allows. Under `connected`
35
+ the app sees what the browser is doing, and before a click, Enter or script that sends, posts, pays, orders,
36
+ subscribes, deletes or closes an account, and before Enter in a message box, it asks the user first. A "No" fails the action with
37
+ `BrowserActionDeclinedError`: report that, never retry it or go around it (`session.tool()` lets only reads
38
+ through; `evaluate` is guarded too). If the user presses Stop, the next call throws `BrowserUserStoppedError`: tell
39
+ the user browser use was stopped and start no new session this turn.
40
+
41
+ The guard prevents mistakes by a cooperating agent. It is not a security boundary: code that imports the raw
42
+ entry (`resolveOmowrightEntry()`) is not guarded, and a host without the `omo_browser_bridge` tool cannot show state or
43
+ honor Stop, though questions are still asked.
44
+
45
+ ## Step 1 — load omowright and prove the stack
22
46
 
23
47
  ```js
24
48
  const { loadOmowright } = await import("<skill-root>/scripts/omowright.mjs")
@@ -94,7 +118,7 @@ changing the extension's automation settings.
94
118
  out everywhere. No flow here needs it.
95
119
  - **`focused: false` by default.** The browser belongs to someone who is probably using it.
96
120
  - **One short, named session per task,** always stopped.
97
- - **Bot-scored or WAF targets go to the owned engine.** The attached engine's daemon enables console
121
+ - **Bot-scored or WAF targets go to the owned engine** (not while `OMO_BROWSER_ENGINE` is set: then say the site needs a browser the session does not allow). The attached engine's daemon enables console
98
122
  capture on every tab it drives, which is a known automation signal; CloakBrowser through
99
123
  `connectCloakProfile()` is the stealth path.
100
124