oh-my-opencode 5.1.4 → 5.1.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.
Files changed (68) hide show
  1. package/dist/cli/index.js +465 -187
  2. package/dist/cli-node/index.js +465 -187
  3. package/dist/config/prune-plugin-view.d.ts +18 -0
  4. package/dist/config/validate.d.ts +3 -1
  5. package/dist/index.js +11196 -10939
  6. package/dist/plugin-config/omo-config-chain.d.ts +4 -6
  7. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  8. package/dist/skills/ulw-execute/SKILL.md +1 -1
  9. package/dist/tui.js +549 -276
  10. package/package.json +14 -14
  11. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  12. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +309 -133
  13. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  14. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  16. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  18. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  19. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  20. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  22. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  23. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  25. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  29. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +25 -18
  31. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  32. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  34. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  35. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  36. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +25 -18
  37. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  38. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  60. package/packages/omo-codex/plugin/package-lock.json +12 -12
  61. package/packages/omo-codex/plugin/package.json +1 -1
  62. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  63. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +25 -18
  64. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
  65. package/packages/omo-codex/scripts/install-dist/install-local.mjs +311 -135
  66. package/packages/prompts-core/prompts/ultrawork/codex.md +25 -18
  67. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  68. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
package/dist/tui.js CHANGED
@@ -69611,97 +69611,6 @@ var init_zod = __esm(() => {
69611
69611
  init_external();
69612
69612
  });
69613
69613
 
69614
- // packages/omo-opencode/src/shared/disabled-providers.ts
69615
- function getModelProvider(model) {
69616
- const slash = model.indexOf("/");
69617
- if (slash <= 0)
69618
- return;
69619
- const provider = model.slice(0, slash).trim();
69620
- return provider || undefined;
69621
- }
69622
- function isProviderDisabled(model, disabled) {
69623
- if (!model || disabled.length === 0)
69624
- return false;
69625
- const provider = getModelProvider(model);
69626
- if (provider === undefined)
69627
- return false;
69628
- const providerLower = provider.toLowerCase();
69629
- return disabled.some((entry) => entry.trim().toLowerCase() === providerLower);
69630
- }
69631
- function filterDisabledProviderModels(models, disabled) {
69632
- if (disabled.length === 0)
69633
- return [...models];
69634
- return models.filter((entry) => {
69635
- const model = typeof entry === "string" ? entry : entry.model;
69636
- return !isProviderDisabled(model, disabled);
69637
- });
69638
- }
69639
- function findFirstAllowedReplacement(chain, disabled) {
69640
- if (!chain)
69641
- return;
69642
- for (const entry of chain) {
69643
- const model = typeof entry === "string" ? entry : entry.model;
69644
- if (!isProviderDisabled(model, disabled))
69645
- return model;
69646
- }
69647
- return;
69648
- }
69649
- function applyToHolder(label, holder, disabled) {
69650
- const normalizedChain = normalizeFallbackModels(holder.fallback_models);
69651
- if (normalizedChain) {
69652
- const filteredChain = filterDisabledProviderModels(normalizedChain, disabled);
69653
- if (filteredChain.length !== normalizedChain.length) {
69654
- log2(`[${HOOK_NAME}] Filtered disabled-provider entries from fallback chain`, {
69655
- label,
69656
- removed: normalizedChain.length - filteredChain.length,
69657
- remaining: filteredChain.length
69658
- });
69659
- }
69660
- holder.fallback_models = filteredChain.length === 0 ? undefined : filteredChain;
69661
- }
69662
- if (typeof holder.model === "string" && isProviderDisabled(holder.model, disabled)) {
69663
- const replacement = findFirstAllowedReplacement(normalizeFallbackModels(holder.fallback_models), disabled);
69664
- if (replacement) {
69665
- log2(`[${HOOK_NAME}] Substituted primary model from fallback chain`, {
69666
- label,
69667
- from: holder.model,
69668
- to: replacement
69669
- });
69670
- holder.model = replacement;
69671
- } else {
69672
- const message = `${label} primary model "${holder.model}" uses a disabled provider and no allowed entry is available in fallback_models. ` + `Either remove the provider from disabled_providers or add an allowed entry to fallback_models.`;
69673
- addConfigLoadError({ path: `disabled_providers:${label}`, error: message });
69674
- log2(`[${HOOK_NAME}] ${message}`, { label, primary: holder.model });
69675
- }
69676
- }
69677
- }
69678
- function applyDisabledProviders(config) {
69679
- const disabled = config.disabled_providers ?? [];
69680
- if (disabled.length === 0)
69681
- return config;
69682
- if (config.agents) {
69683
- for (const [name, agentConfig] of Object.entries(config.agents)) {
69684
- if (agentConfig && typeof agentConfig === "object") {
69685
- applyToHolder(`agents.${name}`, agentConfig, disabled);
69686
- }
69687
- }
69688
- }
69689
- if (config.categories) {
69690
- for (const [name, categoryConfig] of Object.entries(config.categories)) {
69691
- if (categoryConfig && typeof categoryConfig === "object") {
69692
- applyToHolder(`categories.${name}`, categoryConfig, disabled);
69693
- }
69694
- }
69695
- }
69696
- return config;
69697
- }
69698
- var HOOK_NAME = "disabled-providers";
69699
- var init_disabled_providers = __esm(() => {
69700
- init_config_errors();
69701
- init_logger2();
69702
- init_model_resolver2();
69703
- });
69704
-
69705
69614
  // packages/omo-config-core/src/schema/reasoning-vocabulary.ts
69706
69615
  function isReasoningLevel(value) {
69707
69616
  return REASONING_LEVEL_SET2.has(value);
@@ -69793,7 +69702,6 @@ function normalizeLegacyModelFields(entry) {
69793
69702
  delete normalized["reasoningEffort"];
69794
69703
  delete normalized["thinking"];
69795
69704
  delete normalized["textVerbosity"];
69796
- delete normalized["maxTokens"];
69797
69705
  delete normalized["providerOptions"];
69798
69706
  if (typeof entry["model"] === "string")
69799
69707
  normalized["model"] = canonicalModelString(entry["model"]);
@@ -69811,10 +69719,15 @@ function normalizeLegacyModelFields(entry) {
69811
69719
  providerOptions["textVerbosity"] = entry["textVerbosity"];
69812
69720
  if (Object.keys(providerOptions).length > 0)
69813
69721
  normalized["provider_options"] = providerOptions;
69814
- if (entry["max_tokens"] !== undefined)
69722
+ if (entry["max_tokens"] !== undefined) {
69815
69723
  normalized["max_tokens"] = entry["max_tokens"];
69816
- else if (entry["maxTokens"] !== undefined)
69724
+ if (entry["maxTokens"] === undefined || typeof entry["maxTokens"] === "number") {
69725
+ delete normalized["maxTokens"];
69726
+ }
69727
+ } else if (typeof entry["maxTokens"] === "number") {
69817
69728
  normalized["max_tokens"] = entry["maxTokens"];
69729
+ delete normalized["maxTokens"];
69730
+ }
69818
69731
  return normalized;
69819
69732
  }
69820
69733
  var OmoThinkingConfigSchema, OmoReasoningEffortSchema, OmoLegacyFallbackModelObjectInputSchema, OmoFallbackModelObjectSchema, OmoFallbackModelsSchema;
@@ -69854,12 +69767,25 @@ var init_fallback_models = __esm(() => {
69854
69767
  function isRecord5(value) {
69855
69768
  return typeof value === "object" && value !== null && !Array.isArray(value);
69856
69769
  }
69857
- var OmoAgentModelEntrySchema, OmoAgentDefInputSchema, OmoAgentDefSchema, OmoAgentsConfigSchema;
69770
+ var OmoAgentModelEntrySchema, PermissionValueSchema, BashPermissionSchema, OmoAgentPermissionSchema, OmoAgentDefInputSchema, OmoAgentDefSchema, OmoAgentsConfigSchema;
69858
69771
  var init_agent = __esm(() => {
69859
69772
  init_zod();
69860
69773
  init_fallback_models();
69861
69774
  init_model_ref();
69862
69775
  OmoAgentModelEntrySchema = union([string2(), OmoFallbackModelObjectSchema]);
69776
+ PermissionValueSchema = _enum(["ask", "allow", "deny"]);
69777
+ BashPermissionSchema = union([
69778
+ PermissionValueSchema,
69779
+ record(string2(), PermissionValueSchema)
69780
+ ]);
69781
+ OmoAgentPermissionSchema = object({
69782
+ edit: PermissionValueSchema.optional(),
69783
+ bash: BashPermissionSchema.optional(),
69784
+ webfetch: PermissionValueSchema.optional(),
69785
+ task: PermissionValueSchema.optional(),
69786
+ doom_loop: PermissionValueSchema.optional(),
69787
+ external_directory: PermissionValueSchema.optional()
69788
+ }).catchall(PermissionValueSchema.optional());
69863
69789
  OmoAgentDefInputSchema = object({
69864
69790
  description: string2().optional(),
69865
69791
  prompt: string2().optional(),
@@ -69876,7 +69802,9 @@ var init_agent = __esm(() => {
69876
69802
  disallowed_tools: array(string2()).optional(),
69877
69803
  max_turns: number2().int().nonnegative().optional(),
69878
69804
  temperature: number2().min(0).max(2).optional(),
69879
- disable: boolean2().optional()
69805
+ disable: boolean2().optional(),
69806
+ permission: OmoAgentPermissionSchema.optional(),
69807
+ prompt_append: string2().optional()
69880
69808
  }).strict();
69881
69809
  OmoAgentDefSchema = preprocess((value) => isRecord5(value) ? normalizeLegacyModelFields(value) : value, OmoAgentDefInputSchema);
69882
69810
  OmoAgentsConfigSchema = record(string2(), OmoAgentDefSchema);
@@ -70656,6 +70584,64 @@ var init_schema = __esm(() => {
70656
70584
  init_computer();
70657
70585
  });
70658
70586
 
70587
+ // packages/omo-config-core/src/loader/types.ts
70588
+ import { existsSync as existsSync6, lstatSync, readFileSync as readFileSync6, realpathSync as realpathSync4 } from "fs";
70589
+ var MERGED_OMO_CONFIG_PATH = "(merged omo config)", DEFAULT_READ_FILE_SYSTEM;
70590
+ var init_types3 = __esm(() => {
70591
+ DEFAULT_READ_FILE_SYSTEM = {
70592
+ existsSync: existsSync6,
70593
+ lstatSync,
70594
+ readFileSync: readFileSync6,
70595
+ realpathSync: realpathSync4
70596
+ };
70597
+ });
70598
+
70599
+ // packages/omo-config-core/src/loader/diagnostic-lines.ts
70600
+ function displayOmoConfigPath(path, homeDir) {
70601
+ if (path === MERGED_OMO_CONFIG_PATH)
70602
+ return "merged config";
70603
+ if (homeDir === undefined || homeDir.length === 0)
70604
+ return path;
70605
+ const slashed = (value) => value.replaceAll("\\", "/");
70606
+ const home = slashed(homeDir).replace(/\/+$/, "");
70607
+ const file = slashed(path);
70608
+ const fold = (value) => /^[A-Za-z]:\//.test(home) ? value.toLowerCase() : value;
70609
+ if (fold(file) === fold(home))
70610
+ return "~";
70611
+ return fold(file).startsWith(`${fold(home)}/`) ? `~${file.slice(home.length)}` : path;
70612
+ }
70613
+ function notLoadedReason(diagnostic) {
70614
+ switch (diagnostic.kind) {
70615
+ case "parse":
70616
+ return "JSONC parse error";
70617
+ case "read":
70618
+ return "unreadable";
70619
+ default:
70620
+ return diagnostic.issuePaths === undefined || diagnostic.issuePaths.length === 0 ? "invalid config" : `invalid: ${diagnostic.issuePaths.join(", ")}`;
70621
+ }
70622
+ }
70623
+ function omoConfigDiagnosticLines(diagnostics, options = {}) {
70624
+ return diagnostics.flatMap((diagnostic) => {
70625
+ const file = displayOmoConfigPath(diagnostic.path, options.homeDir);
70626
+ switch (diagnostic.kind) {
70627
+ case "invalid-value":
70628
+ return (diagnostic.issuePaths ?? []).map((key) => `config: ${file}: ${key} ignored (invalid value)`);
70629
+ case "unknown-keys":
70630
+ return (diagnostic.issuePaths ?? []).map((key) => `config: ${file}: ${key} ignored (unknown key)`);
70631
+ case "parse":
70632
+ case "read":
70633
+ case "validation":
70634
+ return diagnostic.path === MERGED_OMO_CONFIG_PATH ? [`config: ${file}: reset to defaults (${notLoadedReason(diagnostic)})`] : [`config: ${file}: not loaded (${notLoadedReason(diagnostic)})`];
70635
+ case "deprecated-keys":
70636
+ case "profile":
70637
+ return [];
70638
+ }
70639
+ });
70640
+ }
70641
+ var init_diagnostic_lines = __esm(() => {
70642
+ init_types3();
70643
+ });
70644
+
70659
70645
  // packages/omo-config-core/src/loader/disabled-skills.ts
70660
70646
  var init_disabled_skills = () => {};
70661
70647
 
@@ -70695,24 +70681,265 @@ var init_merge = __esm(() => {
70695
70681
  DANGEROUS_KEYS2 = new Set(["__proto__", "constructor", "prototype"]);
70696
70682
  });
70697
70683
 
70684
+ // packages/omo-config-core/src/loader/prune-invalid-leaves.ts
70685
+ function isContainer(value) {
70686
+ return typeof value === "object" && value !== null;
70687
+ }
70688
+ function childOf(container, segment) {
70689
+ return Array.isArray(container) ? container[Number(segment)] : container[String(segment)];
70690
+ }
70691
+ function hasChild(container, segment) {
70692
+ if (Array.isArray(container)) {
70693
+ const index = Number(segment);
70694
+ return Number.isInteger(index) && index >= 0 && index < container.length;
70695
+ }
70696
+ return Object.hasOwn(container, String(segment));
70697
+ }
70698
+ function segmentsOf(path) {
70699
+ return path.map((segment) => typeof segment === "number" ? segment : String(segment));
70700
+ }
70701
+ function targetPaths(root, issue) {
70702
+ const base = segmentsOf(issue.path);
70703
+ if (issue.code === "unrecognized_keys")
70704
+ return issue.keys.map((key) => [...base, key]);
70705
+ let node = root;
70706
+ let depth = 0;
70707
+ for (const segment of base) {
70708
+ if (!isContainer(node) || !hasChild(node, segment))
70709
+ break;
70710
+ node = childOf(node, segment);
70711
+ depth += 1;
70712
+ }
70713
+ return [base.slice(0, depth)];
70714
+ }
70715
+ function isAncestor(ancestor, path) {
70716
+ return ancestor.length < path.length && ancestor.every((segment, index) => segment === path[index]);
70717
+ }
70718
+ function compareDescending(left, right) {
70719
+ const length = Math.min(left.length, right.length);
70720
+ for (let index = 0;index < length; index += 1) {
70721
+ const a = left[index];
70722
+ const b = right[index];
70723
+ if (a === b)
70724
+ continue;
70725
+ if (typeof a === "number" && typeof b === "number")
70726
+ return b - a;
70727
+ return String(b).localeCompare(String(a));
70728
+ }
70729
+ return right.length - left.length;
70730
+ }
70731
+ function removeChild(container, segment) {
70732
+ if (Array.isArray(container))
70733
+ container.splice(Number(segment), 1);
70734
+ else
70735
+ delete container[String(segment)];
70736
+ }
70737
+ function isEmpty(container) {
70738
+ return Array.isArray(container) ? container.length === 0 : Object.keys(container).length === 0;
70739
+ }
70740
+ function removePathAndEmptiedAncestors(root, path) {
70741
+ const chain = [root];
70742
+ for (const segment of path.slice(0, -1)) {
70743
+ const current = chain[chain.length - 1];
70744
+ if (current === undefined)
70745
+ return;
70746
+ const next = childOf(current, segment);
70747
+ if (!isContainer(next))
70748
+ return;
70749
+ chain.push(next);
70750
+ }
70751
+ for (let depth = path.length - 1;depth >= 0; depth -= 1) {
70752
+ const container = chain[depth];
70753
+ const segment = path[depth];
70754
+ if (container === undefined || segment === undefined)
70755
+ return;
70756
+ removeChild(container, segment);
70757
+ if (depth === 0 || !isEmpty(container))
70758
+ return;
70759
+ }
70760
+ }
70761
+ function prunePass(root, issues) {
70762
+ const byKey = new Map;
70763
+ for (const issue of issues) {
70764
+ for (const path of targetPaths(root, issue)) {
70765
+ if (path.length === 0)
70766
+ return null;
70767
+ const key = path.map((segment) => String(segment)).join(".");
70768
+ if (!byKey.has(key))
70769
+ byKey.set(key, { key, message: issue.message, path });
70770
+ }
70771
+ }
70772
+ const targets = [...byKey.values()];
70773
+ const outermost = targets.filter((target) => !targets.some((other) => isAncestor(other.path, target.path))).sort((left, right) => compareDescending(left.path, right.path));
70774
+ const next = structuredClone(root);
70775
+ for (const target of outermost)
70776
+ removePathAndEmptiedAncestors(next, target.path);
70777
+ return { dropped: outermost, next };
70778
+ }
70779
+ function pruneInvalidConfigPaths(config, issues, validate, maxPasses = MAX_PRUNE_PASSES) {
70780
+ const dropped = [];
70781
+ let current = config;
70782
+ let pending = issues;
70783
+ for (let pass = 0;pass < maxPasses; pass += 1) {
70784
+ const step = prunePass(current, pending);
70785
+ if (step === null || step.dropped.length === 0)
70786
+ return { ok: false, dropped };
70787
+ dropped.push(...step.dropped);
70788
+ current = step.next;
70789
+ if (Object.keys(current).length === 0)
70790
+ return { ok: false, dropped };
70791
+ const validation = validate(current);
70792
+ if (validation.success)
70793
+ return { ok: true, config: current, dropped };
70794
+ pending = validation.issues;
70795
+ }
70796
+ return { ok: false, dropped };
70797
+ }
70798
+ var MAX_PRUNE_PASSES = 32;
70799
+
70800
+ // packages/omo-config-core/src/loader/layer-validation.ts
70801
+ function validationDiagnostic(path, issues) {
70802
+ const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
70803
+ return {
70804
+ kind: "validation",
70805
+ message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
70806
+ path,
70807
+ issuePaths
70808
+ };
70809
+ }
70810
+ function invalidValueDiagnostics(path, dropped) {
70811
+ return dropped.map((entry) => ({
70812
+ kind: "invalid-value",
70813
+ message: `Ignored invalid value in ${path}: ${entry.key}: ${entry.message}`,
70814
+ path,
70815
+ issuePaths: [entry.key]
70816
+ }));
70817
+ }
70818
+ function unrecognizedKeyIssues(issues) {
70819
+ return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
70820
+ }
70821
+ function isRecord11(value) {
70822
+ return typeof value === "object" && value !== null && !Array.isArray(value);
70823
+ }
70824
+ function sanitizeUnsafeKeys(value, path = []) {
70825
+ if (Array.isArray(value)) {
70826
+ const issues = [];
70827
+ const sanitized = value.map((entry, index) => {
70828
+ const nested = sanitizeUnsafeKeys(entry, [...path, String(index)]);
70829
+ issues.push(...nested.issues);
70830
+ return nested.value;
70831
+ });
70832
+ return { issues, value: sanitized };
70833
+ }
70834
+ if (!isRecord11(value))
70835
+ return { issues: [], value };
70836
+ const issues = [];
70837
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
70838
+ issues.push({ keys: ["__proto__"], path });
70839
+ }
70840
+ const sanitized = {};
70841
+ for (const [key, entry] of Object.entries(value)) {
70842
+ if (isUnsafeObjectKey2(key)) {
70843
+ issues.push({ keys: [key], path });
70844
+ continue;
70845
+ }
70846
+ const nested = sanitizeUnsafeKeys(entry, [...path, key]);
70847
+ issues.push(...nested.issues);
70848
+ sanitized[key] = nested.value;
70849
+ }
70850
+ return { issues, value: sanitized };
70851
+ }
70852
+ function containerAt(record, path) {
70853
+ let node = record;
70854
+ for (const segment of path) {
70855
+ if (Array.isArray(node)) {
70856
+ const index = Number(segment);
70857
+ if (!Number.isInteger(index) || index < 0 || index >= node.length)
70858
+ return null;
70859
+ node = node[index];
70860
+ } else if (isRecord11(node) && Object.hasOwn(node, segment)) {
70861
+ node = node[segment];
70862
+ } else {
70863
+ return null;
70864
+ }
70865
+ }
70866
+ return isRecord11(node) ? node : null;
70867
+ }
70868
+ function stripUnrecognizedKeys(record, issues) {
70869
+ const stripped = structuredClone(record);
70870
+ const issuePaths = [];
70871
+ for (const issue of issues) {
70872
+ const container = containerAt(stripped, issue.path);
70873
+ if (container === null)
70874
+ continue;
70875
+ for (const key of issue.keys) {
70876
+ delete container[key];
70877
+ issuePaths.push([...issue.path, key].join("."));
70878
+ }
70879
+ }
70880
+ return { issuePaths, stripped };
70881
+ }
70882
+ function toRecord(value) {
70883
+ if (value === null || typeof value !== "object" || Array.isArray(value))
70884
+ return null;
70885
+ const record = {};
70886
+ for (const [key, entry] of Object.entries(value)) {
70887
+ record[key] = entry;
70888
+ }
70889
+ return record;
70890
+ }
70891
+ function validateConfigLayer(path, data) {
70892
+ const sanitized = sanitizeUnsafeKeys(data);
70893
+ const record = toRecord(sanitized.value);
70894
+ const unsafeIssuePaths = sanitized.issues.flatMap((issue) => issue.keys.map((key) => [...issue.path, key].join(".")));
70895
+ const unsafeDiagnostics = unsafeIssuePaths.length === 0 ? [] : [{ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${unsafeIssuePaths.join(", ")}`, path, issuePaths: unsafeIssuePaths }];
70896
+ const validation = OmoConfigLayerSchema.safeParse(sanitized.value);
70897
+ if (validation.success) {
70898
+ if (record !== null)
70899
+ return { loaded: true, diagnostics: unsafeDiagnostics, value: record };
70900
+ return {
70901
+ loaded: false,
70902
+ diagnostics: [{ kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path }]
70903
+ };
70904
+ }
70905
+ const rejected = { loaded: false, diagnostics: [validationDiagnostic(path, validation.error.issues)] };
70906
+ const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
70907
+ if (record === null)
70908
+ return rejected;
70909
+ let candidate = record;
70910
+ let issues = validation.error.issues;
70911
+ const diagnostics = [...unsafeDiagnostics];
70912
+ if (unknownIssues.length > 0) {
70913
+ const { issuePaths, stripped } = stripUnrecognizedKeys(record, unknownIssues);
70914
+ if (issuePaths.length > 0) {
70915
+ diagnostics.push({ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`, path, issuePaths });
70916
+ }
70917
+ const strippedValidation = validateLayerRecord(stripped);
70918
+ if (strippedValidation.success)
70919
+ return { loaded: true, diagnostics, value: stripped };
70920
+ candidate = stripped;
70921
+ issues = strippedValidation.issues;
70922
+ }
70923
+ const pruned = pruneInvalidConfigPaths(candidate, issues, validateLayerRecord);
70924
+ if (!pruned.ok)
70925
+ return rejected;
70926
+ return { loaded: true, diagnostics: [...diagnostics, ...invalidValueDiagnostics(path, pruned.dropped)], value: pruned.config };
70927
+ }
70928
+ var validateLayerRecord = (record) => {
70929
+ const parsed = OmoConfigLayerSchema.safeParse(record);
70930
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
70931
+ };
70932
+ var init_layer_validation = __esm(() => {
70933
+ init_schema();
70934
+ init_merge();
70935
+ });
70936
+
70698
70937
  // packages/omo-config-core/src/internal/posix-path.ts
70699
70938
  function toPosixPath(path) {
70700
70939
  return path.split("\\").join("/");
70701
70940
  }
70702
70941
  var init_posix_path = () => {};
70703
70942
 
70704
- // packages/omo-config-core/src/loader/types.ts
70705
- import { existsSync as existsSync6, lstatSync, readFileSync as readFileSync6, realpathSync as realpathSync4 } from "fs";
70706
- var DEFAULT_READ_FILE_SYSTEM;
70707
- var init_types3 = __esm(() => {
70708
- DEFAULT_READ_FILE_SYSTEM = {
70709
- existsSync: existsSync6,
70710
- lstatSync,
70711
- readFileSync: readFileSync6,
70712
- realpathSync: realpathSync4
70713
- };
70714
- });
70715
-
70716
70943
  // packages/omo-config-core/src/loader/paths.ts
70717
70944
  import { userInfo } from "os";
70718
70945
  import { dirname as dirname4, join as join12, posix as posix2, resolve as resolve4 } from "path";
@@ -70813,7 +71040,7 @@ function resolveOmoProfileName(options = {}) {
70813
71040
  const env = options.env ?? process.env;
70814
71041
  return profileName(options.profile) ?? profileName(env["OMO_PROFILE"]) ?? profileName(env["OCX_PROFILE"]) ?? profileNameFromOpenCodeConfigDir(env["OPENCODE_CONFIG_DIR"]);
70815
71042
  }
70816
- function toRecord(value) {
71043
+ function toRecord2(value) {
70817
71044
  if (value === null || typeof value !== "object" || Array.isArray(value))
70818
71045
  return;
70819
71046
  return Object.fromEntries(Object.entries(value));
@@ -70834,13 +71061,13 @@ function harnessLayer(config, harness) {
70834
71061
  const legacyKeys = Object.entries(OMO_CONFIG_LEGACY_HARNESS_ALIASES).filter(([, target]) => target === canonical).map(([legacy]) => harnessBlockKey(legacy));
70835
71062
  let layer = {};
70836
71063
  for (const key of [...legacyKeys, harnessBlockKey(canonical)]) {
70837
- layer = mergeOmoConfigRecords(layer, toRecord(config[key]) ?? {});
71064
+ layer = mergeOmoConfigRecords(layer, toRecord2(config[key]) ?? {});
70838
71065
  }
70839
71066
  return layer;
70840
71067
  }
70841
71068
  function resolveOmoConfigView(options) {
70842
- const profiles = toRecord(options.config["profiles"]);
70843
- const profile = options.profile === undefined ? undefined : toRecord(profiles?.[options.profile]);
71069
+ const profiles = toRecord2(options.config["profiles"]);
71070
+ const profile = options.profile === undefined ? undefined : toRecord2(profiles?.[options.profile]);
70844
71071
  const diagnostics = profile === undefined && options.profile !== undefined ? [{
70845
71072
  kind: "profile",
70846
71073
  message: `Activated omo profile "${options.profile}" does not exist; using the base configuration`,
@@ -70895,70 +71122,9 @@ function stripResolutionControlKeys(config) {
70895
71122
  } = config;
70896
71123
  return resolved;
70897
71124
  }
70898
- function validationDiagnostic(path, issues) {
70899
- const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
70900
- return {
70901
- kind: "validation",
70902
- message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
70903
- path,
70904
- issuePaths
70905
- };
70906
- }
70907
- function unrecognizedKeyIssues(issues) {
70908
- return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
70909
- }
70910
- function hasUnsafeUnrecognizedKey(issues) {
70911
- return issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey2(key)));
70912
- }
70913
- function hasTamperedPrototype(value) {
70914
- if (Array.isArray(value))
70915
- return value.some((entry) => hasTamperedPrototype(entry));
70916
- if (!isRecord11(value))
70917
- return false;
70918
- const prototype = Object.getPrototypeOf(value);
70919
- if (prototype !== Object.prototype && prototype !== null)
70920
- return true;
70921
- return Object.values(value).some((entry) => hasTamperedPrototype(entry));
70922
- }
70923
- function isRecord11(value) {
70924
- return typeof value === "object" && value !== null && !Array.isArray(value);
70925
- }
70926
- function containerAt(record, path) {
70927
- let container = record;
70928
- for (const segment of path) {
70929
- const next = container[segment];
70930
- if (!isRecord11(next))
70931
- return null;
70932
- container = next;
70933
- }
70934
- return container;
70935
- }
70936
- function stripUnrecognizedKeys(record, issues) {
70937
- const stripped = structuredClone(record);
70938
- const issuePaths = [];
70939
- for (const issue of issues) {
70940
- const container = containerAt(stripped, issue.path);
70941
- if (container === null)
70942
- continue;
70943
- for (const key of issue.keys) {
70944
- delete container[key];
70945
- issuePaths.push([...issue.path, key].join("."));
70946
- }
70947
- }
70948
- return { issuePaths, stripped };
70949
- }
70950
- function toRecord2(value) {
70951
- if (value === null || typeof value !== "object" || Array.isArray(value))
70952
- return null;
70953
- const record = {};
70954
- for (const [key, entry] of Object.entries(value)) {
70955
- record[key] = entry;
70956
- }
70957
- return record;
70958
- }
70959
71125
  function readConfigSource(path, scope, fileSystem) {
70960
71126
  if (!fileSystem.existsSync(path)) {
70961
- return { source: { exists: false, loaded: false, path, scope } };
71127
+ return { diagnostics: [], source: { exists: false, loaded: false, path, scope } };
70962
71128
  }
70963
71129
  let content;
70964
71130
  try {
@@ -70966,68 +71132,23 @@ function readConfigSource(path, scope, fileSystem) {
70966
71132
  } catch (error) {
70967
71133
  const message = error instanceof Error ? error.message : String(error);
70968
71134
  return {
70969
- diagnostic: { kind: "read", message: `Failed to read ${path}: ${message}`, path },
71135
+ diagnostics: [{ kind: "read", message: `Failed to read ${path}: ${message}`, path }],
70970
71136
  source: { exists: true, loaded: false, path, scope }
70971
71137
  };
70972
71138
  }
70973
71139
  const parsed = parseJsoncSafe2(content);
70974
71140
  if (parsed.errors.length > 0) {
70975
71141
  return {
70976
- diagnostic: {
71142
+ diagnostics: [{
70977
71143
  kind: "parse",
70978
71144
  message: `JSONC parse error in ${path}: ${parsed.errors.map((error) => error.message).join(", ")}`,
70979
71145
  path
70980
- },
71146
+ }],
70981
71147
  source: { exists: true, loaded: false, path, scope }
70982
71148
  };
70983
71149
  }
70984
- if (hasTamperedPrototype(parsed.data)) {
70985
- return {
70986
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: "__proto__" member is not allowed`, path },
70987
- source: { exists: true, loaded: false, path, scope }
70988
- };
70989
- }
70990
- const parsedRecord = toRecord2(parsed.data);
70991
- const validation = OmoConfigLayerSchema.safeParse(parsed.data);
70992
- if (!validation.success) {
70993
- const unrecognized = unrecognizedKeyIssues(validation.error.issues);
70994
- if (hasUnsafeUnrecognizedKey(unrecognized)) {
70995
- return {
70996
- diagnostic: validationDiagnostic(path, validation.error.issues),
70997
- source: { exists: true, loaded: false, path, scope }
70998
- };
70999
- }
71000
- const rejected = {
71001
- diagnostic: validationDiagnostic(path, validation.error.issues),
71002
- source: { exists: true, loaded: false, path, scope }
71003
- };
71004
- const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
71005
- if (parsedRecord === null || unknownIssues.length === 0)
71006
- return rejected;
71007
- const { issuePaths, stripped } = stripUnrecognizedKeys(parsedRecord, unknownIssues);
71008
- if (!OmoConfigLayerSchema.safeParse(stripped).success)
71009
- return rejected;
71010
- return {
71011
- diagnostic: {
71012
- kind: "unknown-keys",
71013
- message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`,
71014
- path,
71015
- issuePaths
71016
- },
71017
- source: { exists: true, loaded: true, path, scope },
71018
- value: stripped
71019
- };
71020
- }
71021
- if (parsedRecord === null) {
71022
- return {
71023
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path },
71024
- source: { exists: true, loaded: false, path, scope }
71025
- };
71026
- }
71027
- return {
71028
- source: { exists: true, loaded: true, path, scope },
71029
- value: parsedRecord
71030
- };
71150
+ const layer = validateConfigLayer(path, parsed.data);
71151
+ return layer.loaded ? { diagnostics: layer.diagnostics, source: { exists: true, loaded: true, path, scope }, value: layer.value } : { diagnostics: layer.diagnostics, source: { exists: true, loaded: false, path, scope } };
71031
71152
  }
71032
71153
  function legacyCategoryDiagnostic(path, renames) {
71033
71154
  const detail = renames.map((rename) => rename.dropped ? `${rename.path} ignored because ${rename.canonical} is also configured` : `${rename.path} renamed to ${rename.canonical}`).join(", ");
@@ -71062,8 +71183,7 @@ function loadOmoConfig(options = {}) {
71062
71183
  })) {
71063
71184
  const loaded = readConfigSource(candidate.path, candidate.scope, fileSystem);
71064
71185
  sources.push(loaded.source);
71065
- if (loaded.diagnostic !== undefined)
71066
- diagnostics.push(loaded.diagnostic);
71186
+ diagnostics.push(...loaded.diagnostics);
71067
71187
  if (loaded.value !== undefined) {
71068
71188
  const canonicalized = canonicalizeLegacyCategoryNames(loaded.value);
71069
71189
  if (canonicalized.renames.length > 0) {
@@ -71086,7 +71206,8 @@ function loadOmoConfig(options = {}) {
71086
71206
  ...options.harness === undefined ? {} : { harness: options.harness },
71087
71207
  ...requestedProfile === undefined ? {} : { profile: requestedProfile }
71088
71208
  });
71089
- const finalConfig = OmoConfigSchema.safeParse(mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config));
71209
+ const finalInput = mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config);
71210
+ const finalConfig = OmoConfigSchema.safeParse(finalInput);
71090
71211
  if (finalConfig.success) {
71091
71212
  return {
71092
71213
  config: stripResolutionControlKeys(finalConfig.data),
@@ -71096,9 +71217,22 @@ function loadOmoConfig(options = {}) {
71096
71217
  sources
71097
71218
  };
71098
71219
  }
71220
+ const pruned = pruneInvalidConfigPaths(finalInput, finalConfig.error.issues, (record) => {
71221
+ const parsed = OmoConfigSchema.safeParse(record);
71222
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
71223
+ });
71224
+ if (pruned.ok) {
71225
+ return {
71226
+ config: stripResolutionControlKeys(OmoConfigSchema.parse(pruned.config)),
71227
+ diagnostics: [...diagnostics, ...resolved.diagnostics, ...invalidValueDiagnostics(MERGED_OMO_CONFIG_PATH, pruned.dropped)],
71228
+ layers,
71229
+ ...resolved.profile === undefined ? {} : { profile: resolved.profile },
71230
+ sources
71231
+ };
71232
+ }
71099
71233
  return {
71100
71234
  config: stripResolutionControlKeys(OmoConfigSchema.parse(DEFAULT_RAW_CONFIG)),
71101
- diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic("(merged omo config)", finalConfig.error.issues)],
71235
+ diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic(MERGED_OMO_CONFIG_PATH, finalConfig.error.issues)],
71102
71236
  layers,
71103
71237
  ...resolved.profile === undefined ? {} : { profile: resolved.profile },
71104
71238
  sources
@@ -71108,6 +71242,7 @@ var DEFAULT_RAW_CONFIG;
71108
71242
  var init_loader2 = __esm(() => {
71109
71243
  init_main();
71110
71244
  init_schema();
71245
+ init_layer_validation();
71111
71246
  init_merge();
71112
71247
  init_paths();
71113
71248
  init_resolution();
@@ -71122,6 +71257,7 @@ var init_loader2 = __esm(() => {
71122
71257
 
71123
71258
  // packages/omo-config-core/src/loader/index.ts
71124
71259
  var init_loader3 = __esm(() => {
71260
+ init_diagnostic_lines();
71125
71261
  init_disabled_skills();
71126
71262
  init_loader2();
71127
71263
  init_merge();
@@ -71415,6 +71551,97 @@ var init_src4 = __esm(() => {
71415
71551
  init_migration3();
71416
71552
  });
71417
71553
 
71554
+ // packages/omo-opencode/src/shared/disabled-providers.ts
71555
+ function getModelProvider(model) {
71556
+ const slash = model.indexOf("/");
71557
+ if (slash <= 0)
71558
+ return;
71559
+ const provider = model.slice(0, slash).trim();
71560
+ return provider || undefined;
71561
+ }
71562
+ function isProviderDisabled(model, disabled) {
71563
+ if (!model || disabled.length === 0)
71564
+ return false;
71565
+ const provider = getModelProvider(model);
71566
+ if (provider === undefined)
71567
+ return false;
71568
+ const providerLower = provider.toLowerCase();
71569
+ return disabled.some((entry) => entry.trim().toLowerCase() === providerLower);
71570
+ }
71571
+ function filterDisabledProviderModels(models, disabled) {
71572
+ if (disabled.length === 0)
71573
+ return [...models];
71574
+ return models.filter((entry) => {
71575
+ const model = typeof entry === "string" ? entry : entry.model;
71576
+ return !isProviderDisabled(model, disabled);
71577
+ });
71578
+ }
71579
+ function findFirstAllowedReplacement(chain, disabled) {
71580
+ if (!chain)
71581
+ return;
71582
+ for (const entry of chain) {
71583
+ const model = typeof entry === "string" ? entry : entry.model;
71584
+ if (!isProviderDisabled(model, disabled))
71585
+ return model;
71586
+ }
71587
+ return;
71588
+ }
71589
+ function applyToHolder(label, holder, disabled) {
71590
+ const normalizedChain = normalizeFallbackModels(holder.fallback_models);
71591
+ if (normalizedChain) {
71592
+ const filteredChain = filterDisabledProviderModels(normalizedChain, disabled);
71593
+ if (filteredChain.length !== normalizedChain.length) {
71594
+ log2(`[${HOOK_NAME}] Filtered disabled-provider entries from fallback chain`, {
71595
+ label,
71596
+ removed: normalizedChain.length - filteredChain.length,
71597
+ remaining: filteredChain.length
71598
+ });
71599
+ }
71600
+ holder.fallback_models = filteredChain.length === 0 ? undefined : filteredChain;
71601
+ }
71602
+ if (typeof holder.model === "string" && isProviderDisabled(holder.model, disabled)) {
71603
+ const replacement = findFirstAllowedReplacement(normalizeFallbackModels(holder.fallback_models), disabled);
71604
+ if (replacement) {
71605
+ log2(`[${HOOK_NAME}] Substituted primary model from fallback chain`, {
71606
+ label,
71607
+ from: holder.model,
71608
+ to: replacement
71609
+ });
71610
+ holder.model = replacement;
71611
+ } else {
71612
+ const message = `${label} primary model "${holder.model}" uses a disabled provider and no allowed entry is available in fallback_models. ` + `Either remove the provider from disabled_providers or add an allowed entry to fallback_models.`;
71613
+ addConfigLoadError({ path: `disabled_providers:${label}`, error: message });
71614
+ log2(`[${HOOK_NAME}] ${message}`, { label, primary: holder.model });
71615
+ }
71616
+ }
71617
+ }
71618
+ function applyDisabledProviders(config) {
71619
+ const disabled = config.disabled_providers ?? [];
71620
+ if (disabled.length === 0)
71621
+ return config;
71622
+ if (config.agents) {
71623
+ for (const [name, agentConfig] of Object.entries(config.agents)) {
71624
+ if (agentConfig && typeof agentConfig === "object") {
71625
+ applyToHolder(`agents.${name}`, agentConfig, disabled);
71626
+ }
71627
+ }
71628
+ }
71629
+ if (config.categories) {
71630
+ for (const [name, categoryConfig] of Object.entries(config.categories)) {
71631
+ if (categoryConfig && typeof categoryConfig === "object") {
71632
+ applyToHolder(`categories.${name}`, categoryConfig, disabled);
71633
+ }
71634
+ }
71635
+ }
71636
+ return config;
71637
+ }
71638
+ var HOOK_NAME = "disabled-providers";
71639
+ var init_disabled_providers = __esm(() => {
71640
+ init_config_errors();
71641
+ init_logger2();
71642
+ init_model_resolver2();
71643
+ });
71644
+
71418
71645
  // packages/omo-opencode/src/plugin-config/omo-config-chain.ts
71419
71646
  function block(config) {
71420
71647
  const value = config["[opencode]"];
@@ -71438,11 +71665,11 @@ function baseView(config) {
71438
71665
  }
71439
71666
  return result;
71440
71667
  }
71441
- function appendViews(views, layers, select) {
71668
+ function appendViews(views, layers, select, keyPrefix) {
71442
71669
  for (const layer of layers) {
71443
71670
  const config = select(layer.config);
71444
71671
  if (Object.keys(config).length > 0)
71445
- views.push({ config, path: layer.source.path });
71672
+ views.push({ config, keyPrefix, path: layer.source.path });
71446
71673
  }
71447
71674
  }
71448
71675
  function recordFields(value, fields) {
@@ -71471,10 +71698,21 @@ function modelInput(view) {
71471
71698
  };
71472
71699
  }
71473
71700
  function modelView(view) {
71474
- const parsed = OmoConfigSchema.safeParse(modelInput(view));
71475
- if (!parsed.success)
71701
+ const input = modelInput(view);
71702
+ const parsed = OmoConfigSchema.safeParse(input);
71703
+ const modelConfig = parsed.success ? parsed.data : (() => {
71704
+ const pruned = pruneInvalidConfigPaths(input, parsed.error.issues, (record) => {
71705
+ const validation = OmoConfigSchema.safeParse(record);
71706
+ return validation.success ? { success: true } : { success: false, issues: validation.error.issues };
71707
+ });
71708
+ if (!pruned.ok)
71709
+ return;
71710
+ const validation = OmoConfigSchema.safeParse(pruned.config);
71711
+ return validation.success ? validation.data : undefined;
71712
+ })();
71713
+ if (modelConfig === undefined)
71476
71714
  return { config: {}, diagnostics: [] };
71477
- const resolved = resolveModelReferences(parsed.data);
71715
+ const resolved = resolveModelReferences(modelConfig);
71478
71716
  return {
71479
71717
  config: {
71480
71718
  ...resolved.view.agents === undefined ? {} : { agents: resolved.view.agents },
@@ -71493,14 +71731,15 @@ function loadOmoOpenCodeConfigChain(directory, environment = process.env) {
71493
71731
  });
71494
71732
  const views = [];
71495
71733
  const selectedProfile = loaded.profile;
71496
- appendViews(views, loaded.layers, baseView);
71497
- appendViews(views, loaded.layers, block);
71498
- appendViews(views, loaded.layers, (config) => baseView(profile(config, selectedProfile)));
71499
- appendViews(views, loaded.layers, (config) => block(profile(config, selectedProfile)));
71734
+ const profilePrefix = selectedProfile === undefined ? "" : `profiles.${selectedProfile}.`;
71735
+ appendViews(views, loaded.layers, baseView, "");
71736
+ appendViews(views, loaded.layers, block, "[opencode].");
71737
+ appendViews(views, loaded.layers, (config) => baseView(profile(config, selectedProfile)), profilePrefix);
71738
+ appendViews(views, loaded.layers, (config) => block(profile(config, selectedProfile)), `${profilePrefix}[opencode].`);
71500
71739
  const resolvedModels = modelView(resolved.config);
71501
71740
  if (Object.keys(resolvedModels.config).length > 0) {
71502
71741
  const source = loaded.layers[0]?.source.path ?? directory;
71503
- views.push({ config: resolvedModels.config, path: source });
71742
+ views.push({ config: resolvedModels.config, keyPrefix: "", path: source });
71504
71743
  }
71505
71744
  let protectedUserView = {};
71506
71745
  for (const layer of loaded.layers) {
@@ -71595,7 +71834,7 @@ function unknownPaths(schema, value, path) {
71595
71834
  if (def.type === "object" && isPlainRecord2(value) && def.shape !== undefined) {
71596
71835
  const catchall = def.catchall !== undefined && definition(def.catchall).type !== "never" ? def.catchall : undefined;
71597
71836
  return Object.entries(value).flatMap(([key, entry]) => {
71598
- const propertySchema = def.shape?.[key];
71837
+ const propertySchema = def.shape !== undefined && Object.hasOwn(def.shape, key) ? def.shape[key] : undefined;
71599
71838
  if (propertySchema !== undefined)
71600
71839
  return unknownPaths(propertySchema, entry, [...path, key]);
71601
71840
  if (catchall !== undefined)
@@ -71712,22 +71951,22 @@ var init_fallback_models2 = __esm(() => {
71712
71951
  });
71713
71952
 
71714
71953
  // packages/omo-opencode/src/config/schema/internal/permission.ts
71715
- var PermissionValueSchema, BashPermissionSchema, AgentPermissionSchema;
71954
+ var PermissionValueSchema2, BashPermissionSchema2, AgentPermissionSchema;
71716
71955
  var init_permission = __esm(() => {
71717
71956
  init_zod();
71718
- PermissionValueSchema = _enum(["ask", "allow", "deny"]);
71719
- BashPermissionSchema = union([
71720
- PermissionValueSchema,
71721
- record(string2(), PermissionValueSchema)
71957
+ PermissionValueSchema2 = _enum(["ask", "allow", "deny"]);
71958
+ BashPermissionSchema2 = union([
71959
+ PermissionValueSchema2,
71960
+ record(string2(), PermissionValueSchema2)
71722
71961
  ]);
71723
71962
  AgentPermissionSchema = object({
71724
- edit: PermissionValueSchema.optional(),
71725
- bash: BashPermissionSchema.optional(),
71726
- webfetch: PermissionValueSchema.optional(),
71727
- task: PermissionValueSchema.optional(),
71728
- doom_loop: PermissionValueSchema.optional(),
71729
- external_directory: PermissionValueSchema.optional()
71730
- }).catchall(PermissionValueSchema.optional());
71963
+ edit: PermissionValueSchema2.optional(),
71964
+ bash: BashPermissionSchema2.optional(),
71965
+ webfetch: PermissionValueSchema2.optional(),
71966
+ task: PermissionValueSchema2.optional(),
71967
+ doom_loop: PermissionValueSchema2.optional(),
71968
+ external_directory: PermissionValueSchema2.optional()
71969
+ }).catchall(PermissionValueSchema2.optional());
71731
71970
  });
71732
71971
 
71733
71972
  // packages/omo-opencode/src/config/schema/agent-overrides.ts
@@ -72491,6 +72730,30 @@ var init_schema2 = __esm(() => {
72491
72730
  init_websearch();
72492
72731
  });
72493
72732
 
72733
+ // packages/omo-opencode/src/config/prune-plugin-view.ts
72734
+ function prunePluginView(input) {
72735
+ const validation = OhMyOpenCodeConfigSchema.safeParse(input.config);
72736
+ if (validation.success)
72737
+ return { config: input.config, issues: [], warnings: [] };
72738
+ const pruned = pruneInvalidConfigPaths(input.config, validation.error.issues, validatePluginRecord);
72739
+ if (!pruned.ok)
72740
+ return { config: input.config, issues: validation.error.issues, warnings: [] };
72741
+ const file = displayOmoConfigPath(input.path, input.homeDir);
72742
+ return {
72743
+ config: pruned.config,
72744
+ issues: [],
72745
+ warnings: pruned.dropped.map((entry) => `config: ${file}: ${input.keyPrefix}${entry.key} ignored (invalid value)`)
72746
+ };
72747
+ }
72748
+ var validatePluginRecord = (record) => {
72749
+ const parsed = OhMyOpenCodeConfigSchema.safeParse(record);
72750
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
72751
+ };
72752
+ var init_prune_plugin_view = __esm(() => {
72753
+ init_src4();
72754
+ init_schema2();
72755
+ });
72756
+
72494
72757
  // packages/omo-opencode/src/config/validate.ts
72495
72758
  import { relative } from "path";
72496
72759
  function shortPath(configPath) {
@@ -72501,9 +72764,8 @@ function formatIssuePath(path) {
72501
72764
  const formatted = path.map((segment) => String(segment)).join(".");
72502
72765
  return formatted.length > 0 ? formatted : "<root>";
72503
72766
  }
72504
- function schemaMessages(configPath, rawConfig) {
72505
- const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
72506
- const validationMessages = result.success ? [] : result.error.issues.map((issue) => `${shortPath(configPath)}: ${formatIssuePath(issue.path)}: ${issue.message}`);
72767
+ function schemaMessages(configPath, rawConfig, issues) {
72768
+ const validationMessages = issues.map((issue) => `${shortPath(configPath)}: ${formatIssuePath(issue.path)}: ${issue.message}`);
72507
72769
  const unknownKeyMessages = findUnknownKeyPaths(OhMyOpenCodeConfigSchema, rawConfig).map((path) => `${shortPath(configPath)}: Unknown config key: ${formatIssuePath(path)}`);
72508
72770
  return [...validationMessages, ...unknownKeyMessages];
72509
72771
  }
@@ -72519,11 +72781,13 @@ function parseConfig(rawConfig) {
72519
72781
  }
72520
72782
  return config;
72521
72783
  }
72522
- function parseConfigView(path, rawConfig) {
72784
+ function parseConfigView(view, homeDir) {
72785
+ const pruned = prunePluginView({ config: view.config, homeDir, keyPrefix: view.keyPrefix, path: view.path });
72523
72786
  return {
72524
- config: parseConfig(rawConfig),
72525
- messages: schemaMessages(path, rawConfig),
72526
- path
72787
+ config: parseConfig(pruned.config),
72788
+ messages: schemaMessages(view.path, view.config, pruned.issues),
72789
+ path: view.path,
72790
+ warnings: pruned.warnings
72527
72791
  };
72528
72792
  }
72529
72793
  function mergeViews(views) {
@@ -72613,27 +72877,36 @@ function migrateRalphLoopConfig(config) {
72613
72877
  }
72614
72878
  function validatePluginConfig(directory, environment = process.env) {
72615
72879
  const chain = loadOmoOpenCodeConfigChain(directory, environment);
72616
- const views = chain.views.map((view) => parseConfigView(view.path, view.config));
72617
- const chainMessages = chain.diagnostics.filter((diagnostic) => diagnostic.kind !== "deprecated-keys").map((diagnostic) => `${shortPath(diagnostic.path)}: ${diagnostic.message}`);
72880
+ const homeDir = environment.HOME ?? environment.USERPROFILE;
72881
+ const views = chain.views.map((view) => parseConfigView(view, homeDir));
72882
+ const failing = chain.diagnostics.filter((diagnostic) => diagnostic.kind !== "deprecated-keys" && diagnostic.kind !== "invalid-value");
72883
+ const chainMessages = failing.map((diagnostic) => `${shortPath(diagnostic.path)}: ${diagnostic.message}`);
72618
72884
  const messages = [...chainMessages, ...views.flatMap((view) => view.messages)];
72885
+ const warnings = [
72886
+ ...omoConfigDiagnosticLines(chain.diagnostics.flatMap((diagnostic) => diagnostic.kind === "invalid-value" ? [diagnostic] : []), { homeDir }),
72887
+ ...views.flatMap((view) => view.warnings)
72888
+ ];
72619
72889
  const firstFailingView = views.find((view) => view.messages.length > 0);
72620
72890
  const firstView = views[0];
72621
- const userConfig = parseConfig(chain.protectedUserView);
72891
+ const userConfig = parseConfig(prunePluginView({ config: chain.protectedUserView, homeDir, keyPrefix: "", path: "" }).config);
72622
72892
  const config = applyDisabledProviders(materializeAgentModelChains(protectUserFields(mergeViews(views), userConfig)));
72623
72893
  warnLegacyUlwExecuteKey(chain.views);
72624
72894
  return {
72625
72895
  valid: messages.length === 0,
72626
72896
  messages,
72627
- path: chainMessages.length > 0 ? chain.diagnostics[0]?.path ?? null : firstFailingView?.path ?? firstView?.path ?? null,
72897
+ warnings,
72898
+ path: failing[0]?.path ?? firstFailingView?.path ?? firstView?.path ?? null,
72628
72899
  config: migrateRalphLoopConfig(migrateLegacyUlwExecuteKey(config))
72629
72900
  };
72630
72901
  }
72631
72902
  var START_WORK_DEPRECATION_MESSAGE = 'config key "start_work" is deprecated, rename to "ulw_execute" - will be removed next release', deprecationWarningSink;
72632
72903
  var init_validate = __esm(() => {
72904
+ init_src4();
72633
72905
  init_disabled_providers();
72634
72906
  init_logger2();
72635
72907
  init_omo_config_chain();
72636
72908
  init_config_merger();
72909
+ init_prune_plugin_view();
72637
72910
  init_schema2();
72638
72911
  deprecationWarningSink = log2;
72639
72912
  });
@@ -74344,7 +74617,7 @@ init_shared();
74344
74617
  // package.json
74345
74618
  var package_default = {
74346
74619
  name: "oh-my-opencode",
74347
- version: "5.1.4",
74620
+ version: "5.1.6",
74348
74621
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
74349
74622
  main: "./dist/index.js",
74350
74623
  types: "dist/index.d.ts",
@@ -74521,7 +74794,7 @@ var package_default = {
74521
74794
  },
74522
74795
  devDependencies: {
74523
74796
  "@clack/prompts": "^1.8.1",
74524
- "@code-yeongyu/senpi": "2026.9.29-5",
74797
+ "@code-yeongyu/senpi": "2026.9.30",
74525
74798
  "@modelcontextprotocol/sdk": "^1.30.0",
74526
74799
  "@oh-my-opencode/agents-md-core": "workspace:*",
74527
74800
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -74579,18 +74852,18 @@ var package_default = {
74579
74852
  typescript: "^7.0.2"
74580
74853
  },
74581
74854
  optionalDependencies: {
74582
- "oh-my-opencode-darwin-arm64": "5.1.4",
74583
- "oh-my-opencode-darwin-x64": "5.1.4",
74584
- "oh-my-opencode-darwin-x64-baseline": "5.1.4",
74585
- "oh-my-opencode-linux-arm64": "5.1.4",
74586
- "oh-my-opencode-linux-arm64-musl": "5.1.4",
74587
- "oh-my-opencode-linux-x64": "5.1.4",
74588
- "oh-my-opencode-linux-x64-baseline": "5.1.4",
74589
- "oh-my-opencode-linux-x64-musl": "5.1.4",
74590
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.4",
74591
- "oh-my-opencode-windows-arm64": "5.1.4",
74592
- "oh-my-opencode-windows-x64": "5.1.4",
74593
- "oh-my-opencode-windows-x64-baseline": "5.1.4"
74855
+ "oh-my-opencode-darwin-arm64": "5.1.6",
74856
+ "oh-my-opencode-darwin-x64": "5.1.6",
74857
+ "oh-my-opencode-darwin-x64-baseline": "5.1.6",
74858
+ "oh-my-opencode-linux-arm64": "5.1.6",
74859
+ "oh-my-opencode-linux-arm64-musl": "5.1.6",
74860
+ "oh-my-opencode-linux-x64": "5.1.6",
74861
+ "oh-my-opencode-linux-x64-baseline": "5.1.6",
74862
+ "oh-my-opencode-linux-x64-musl": "5.1.6",
74863
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.6",
74864
+ "oh-my-opencode-windows-arm64": "5.1.6",
74865
+ "oh-my-opencode-windows-x64": "5.1.6",
74866
+ "oh-my-opencode-windows-x64-baseline": "5.1.6"
74594
74867
  },
74595
74868
  overrides: {
74596
74869
  hono: "^4.13.8",