lazycodex-ai 5.1.3 → 5.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cli/index.js +599 -191
  2. package/dist/cli-node/index.js +599 -191
  3. package/package.json +1 -1
  4. package/packages/lsp-daemon/dist/cli.js +2 -1
  5. package/packages/lsp-daemon/dist/client.js +2 -1
  6. package/packages/lsp-daemon/dist/index.js +2 -1
  7. package/packages/lsp-tools-mcp/dist/cli.js +2 -1
  8. package/packages/lsp-tools-mcp/dist/mcp.js +2 -1
  9. package/packages/lsp-tools-mcp/dist/tools.js +2 -1
  10. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  11. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +309 -133
  12. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  13. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  14. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  15. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  16. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  17. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  19. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +4 -4
  21. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +141 -107
  22. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.js +2 -2
  23. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  24. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/lsp/src/lsp-session-state.ts +2 -2
  26. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  27. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  29. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  33. package/packages/omo-codex/plugin/components/ultrawork/package.json +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/hooks/hooks.json +5 -5
  37. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  38. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  59. package/packages/omo-codex/plugin/package-lock.json +12 -12
  60. package/packages/omo-codex/plugin/package.json +1 -1
  61. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  62. package/packages/omo-codex/scripts/install-dist/install-local.mjs +311 -135
  63. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "oh-my-opencode",
68
- version: "5.1.3",
68
+ version: "5.1.5",
69
69
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
70
70
  main: "./dist/index.js",
71
71
  types: "dist/index.d.ts",
@@ -242,7 +242,7 @@ var init_package = __esm(() => {
242
242
  },
243
243
  devDependencies: {
244
244
  "@clack/prompts": "^1.8.1",
245
- "@code-yeongyu/senpi": "2026.9.29-4",
245
+ "@code-yeongyu/senpi": "2026.9.29-5",
246
246
  "@modelcontextprotocol/sdk": "^1.30.0",
247
247
  "@oh-my-opencode/agents-md-core": "workspace:*",
248
248
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -300,18 +300,18 @@ var init_package = __esm(() => {
300
300
  typescript: "^7.0.2"
301
301
  },
302
302
  optionalDependencies: {
303
- "oh-my-opencode-darwin-arm64": "5.1.3",
304
- "oh-my-opencode-darwin-x64": "5.1.3",
305
- "oh-my-opencode-darwin-x64-baseline": "5.1.3",
306
- "oh-my-opencode-linux-arm64": "5.1.3",
307
- "oh-my-opencode-linux-arm64-musl": "5.1.3",
308
- "oh-my-opencode-linux-x64": "5.1.3",
309
- "oh-my-opencode-linux-x64-baseline": "5.1.3",
310
- "oh-my-opencode-linux-x64-musl": "5.1.3",
311
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.3",
312
- "oh-my-opencode-windows-arm64": "5.1.3",
313
- "oh-my-opencode-windows-x64": "5.1.3",
314
- "oh-my-opencode-windows-x64-baseline": "5.1.3"
303
+ "oh-my-opencode-darwin-arm64": "5.1.5",
304
+ "oh-my-opencode-darwin-x64": "5.1.5",
305
+ "oh-my-opencode-darwin-x64-baseline": "5.1.5",
306
+ "oh-my-opencode-linux-arm64": "5.1.5",
307
+ "oh-my-opencode-linux-arm64-musl": "5.1.5",
308
+ "oh-my-opencode-linux-x64": "5.1.5",
309
+ "oh-my-opencode-linux-x64-baseline": "5.1.5",
310
+ "oh-my-opencode-linux-x64-musl": "5.1.5",
311
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.5",
312
+ "oh-my-opencode-windows-arm64": "5.1.5",
313
+ "oh-my-opencode-windows-x64": "5.1.5",
314
+ "oh-my-opencode-windows-x64-baseline": "5.1.5"
315
315
  },
316
316
  overrides: {
317
317
  hono: "^4.13.8",
@@ -7393,6 +7393,8 @@ var init_category_model_requirements = __esm(() => {
7393
7393
  },
7394
7394
  "deep-low": {
7395
7395
  fallbackChain: [
7396
+ { providers: ["openai", "chatgpt-subscription"], model: "gpt-6.1-sol", variant: "medium" },
7397
+ { providers: ["openai", "chatgpt-subscription"], model: "gpt-6.1-sol-fast", variant: "medium" },
7396
7398
  {
7397
7399
  providers: ["openai", "chatgpt-subscription", "github-copilot", "opencode"],
7398
7400
  model: "gpt-5.6-sol",
@@ -7445,7 +7447,9 @@ var init_category_model_requirements = __esm(() => {
7445
7447
  providers: ["anthropic", "anthropic-api", "github-copilot"],
7446
7448
  model: "claude-haiku-4-5",
7447
7449
  variant: "off"
7448
- }
7450
+ },
7451
+ { providers: ["zai-coding-plan"], model: "glm-5.3-flash", variant: "low" },
7452
+ { providers: ["xiaomi"], model: "mimo-v2.6-flash", variant: "low" }
7449
7453
  ]
7450
7454
  },
7451
7455
  "unspecified-low": {
@@ -7610,7 +7614,7 @@ var init_model_capability_aliases = __esm(() => {
7610
7614
  description: "Normalizes OpenCode's OpenAI GPT fast service-tier IDs to canonical snapshot IDs.",
7611
7615
  providerIDs: ["openai"],
7612
7616
  allowedSubproviderHosts: ["vercel"],
7613
- match: (normalizedModelID) => /^(?:gpt-5\.6-(?:sol|terra|luna)|gpt-6-(?:astra|sol|luna))-fast$/.test(normalizedModelID),
7617
+ match: (normalizedModelID) => /^(?:gpt-5\.6-(?:sol|terra|luna)|gpt-6-(?:astra|sol|luna)|gpt-6\.1-sol)-fast$/.test(normalizedModelID),
7614
7618
  canonicalize: (normalizedModelID) => normalizedModelID.slice(0, -"-fast".length)
7615
7619
  },
7616
7620
  {
@@ -7758,6 +7762,14 @@ var init_model_capability_heuristics = __esm(() => {
7758
7762
  reasoningEffortAliases: { none: "low", minimal: "low" },
7759
7763
  supportsTemperature: false
7760
7764
  },
7765
+ {
7766
+ family: "gpt-6.1-sol",
7767
+ pattern: /gpt-6-1-sol/,
7768
+ variants: ["low", "medium", "high", "xhigh", "max"],
7769
+ reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
7770
+ reasoningEffortAliases: { none: "low", minimal: "low" },
7771
+ supportsTemperature: false
7772
+ },
7761
7773
  {
7762
7774
  family: "gpt-6",
7763
7775
  includes: ["gpt-6"],
@@ -8189,6 +8201,36 @@ var init_supplemental_entries = __esm(() => {
8189
8201
  output: 128000
8190
8202
  }
8191
8203
  },
8204
+ "gpt-6.1-sol": {
8205
+ id: "gpt-6.1-sol",
8206
+ family: "gpt",
8207
+ reasoning: true,
8208
+ temperature: false,
8209
+ toolCall: true,
8210
+ modalities: {
8211
+ input: ["text", "image"],
8212
+ output: ["text"]
8213
+ },
8214
+ limit: {
8215
+ context: 400000,
8216
+ output: 128000
8217
+ }
8218
+ },
8219
+ "gpt-6.1-sol-fast": {
8220
+ id: "gpt-6.1-sol-fast",
8221
+ family: "gpt",
8222
+ reasoning: true,
8223
+ temperature: false,
8224
+ toolCall: true,
8225
+ modalities: {
8226
+ input: ["text", "image"],
8227
+ output: ["text"]
8228
+ },
8229
+ limit: {
8230
+ context: 400000,
8231
+ output: 128000
8232
+ }
8233
+ },
8192
8234
  "gpt-6-luna": {
8193
8235
  id: "gpt-6-luna",
8194
8236
  family: "gpt-nano",
@@ -9455,7 +9497,7 @@ You are working on tasks that don't fit specific categories but require substant
9455
9497
  </Category_Context>`, 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>`, 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>`, 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>`, DEEP_LOW_GATE_MODELS, DEEP_HIGH_GATE_MODEL = "gpt-6-astra", OPENAI_CATEGORIES;
9456
9498
  var init_openai_categories = __esm(() => {
9457
9499
  init_types();
9458
- DEEP_LOW_GATE_MODELS = ["gpt-5.6-sol-fast", "gpt-5.6-sol"];
9500
+ DEEP_LOW_GATE_MODELS = ["gpt-6.1-sol", "gpt-6.1-sol-fast", "gpt-5.6-sol-fast", "gpt-5.6-sol"];
9459
9501
  OPENAI_CATEGORIES = [
9460
9502
  {
9461
9503
  name: "ultrabrain",
@@ -9466,7 +9508,7 @@ var init_openai_categories = __esm(() => {
9466
9508
  },
9467
9509
  {
9468
9510
  name: "deep-low",
9469
- config: { model: "openai/gpt-5.6-sol", variant: "medium" },
9511
+ config: { model: "openai/gpt-6.1-sol", variant: "medium" },
9470
9512
  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.",
9471
9513
  callerGuidance: DEEP_LOW_CATEGORY_CALLER_GUIDANCE,
9472
9514
  promptAppend: DEEP_LOW_CATEGORY_PROMPT_APPEND,
@@ -64780,6 +64822,94 @@ var init_model_capabilities_generated = __esm(() => {
64780
64822
  context: 1e6,
64781
64823
  output: 128000
64782
64824
  }
64825
+ },
64826
+ "glm-5.3-flash": {
64827
+ id: "glm-5.3-flash",
64828
+ family: "glm-flash",
64829
+ reasoning: true,
64830
+ temperature: true,
64831
+ toolCall: true,
64832
+ modalities: {
64833
+ input: [
64834
+ "text",
64835
+ "image",
64836
+ "video"
64837
+ ],
64838
+ output: [
64839
+ "text"
64840
+ ]
64841
+ },
64842
+ limit: {
64843
+ context: 1048576,
64844
+ output: 1048576
64845
+ }
64846
+ },
64847
+ "zai/glm-5.3-flash": {
64848
+ id: "zai/glm-5.3-flash",
64849
+ family: "glm-flash",
64850
+ reasoning: true,
64851
+ temperature: true,
64852
+ toolCall: true,
64853
+ modalities: {
64854
+ input: [
64855
+ "text",
64856
+ "image",
64857
+ "video",
64858
+ "pdf"
64859
+ ],
64860
+ output: [
64861
+ "text"
64862
+ ]
64863
+ },
64864
+ limit: {
64865
+ context: 1e6,
64866
+ output: 131072
64867
+ }
64868
+ },
64869
+ "mimo-v2.6-flash": {
64870
+ id: "mimo-v2.6-flash",
64871
+ family: "mimo",
64872
+ reasoning: true,
64873
+ temperature: true,
64874
+ toolCall: true,
64875
+ modalities: {
64876
+ input: [
64877
+ "text",
64878
+ "image",
64879
+ "audio",
64880
+ "video"
64881
+ ],
64882
+ output: [
64883
+ "text"
64884
+ ]
64885
+ },
64886
+ limit: {
64887
+ context: 1048576,
64888
+ output: 131072
64889
+ }
64890
+ },
64891
+ "xiaomi/mimo-v2.6-flash": {
64892
+ id: "xiaomi/mimo-v2.6-flash",
64893
+ family: "mimo",
64894
+ reasoning: true,
64895
+ temperature: true,
64896
+ toolCall: true,
64897
+ modalities: {
64898
+ input: [
64899
+ "text",
64900
+ "image",
64901
+ "audio",
64902
+ "video"
64903
+ ],
64904
+ output: [
64905
+ "text"
64906
+ ]
64907
+ },
64908
+ limit: {
64909
+ context: 1048576,
64910
+ input: 1048576,
64911
+ output: 131072
64912
+ }
64783
64913
  }
64784
64914
  }
64785
64915
  };
@@ -74328,7 +74458,6 @@ function normalizeLegacyModelFields(entry) {
74328
74458
  delete normalized["reasoningEffort"];
74329
74459
  delete normalized["thinking"];
74330
74460
  delete normalized["textVerbosity"];
74331
- delete normalized["maxTokens"];
74332
74461
  delete normalized["providerOptions"];
74333
74462
  if (typeof entry["model"] === "string")
74334
74463
  normalized["model"] = canonicalModelString(entry["model"]);
@@ -74346,10 +74475,15 @@ function normalizeLegacyModelFields(entry) {
74346
74475
  providerOptions["textVerbosity"] = entry["textVerbosity"];
74347
74476
  if (Object.keys(providerOptions).length > 0)
74348
74477
  normalized["provider_options"] = providerOptions;
74349
- if (entry["max_tokens"] !== undefined)
74478
+ if (entry["max_tokens"] !== undefined) {
74350
74479
  normalized["max_tokens"] = entry["max_tokens"];
74351
- else if (entry["maxTokens"] !== undefined)
74480
+ if (entry["maxTokens"] === undefined || typeof entry["maxTokens"] === "number") {
74481
+ delete normalized["maxTokens"];
74482
+ }
74483
+ } else if (typeof entry["maxTokens"] === "number") {
74352
74484
  normalized["max_tokens"] = entry["maxTokens"];
74485
+ delete normalized["maxTokens"];
74486
+ }
74353
74487
  return normalized;
74354
74488
  }
74355
74489
  function normalizeLegacyModelEntry(entry) {
@@ -74392,12 +74526,25 @@ var init_fallback_models = __esm(() => {
74392
74526
  function isRecord5(value) {
74393
74527
  return typeof value === "object" && value !== null && !Array.isArray(value);
74394
74528
  }
74395
- var OmoAgentModelEntrySchema, OmoAgentDefInputSchema, OmoAgentDefSchema, OmoAgentsConfigSchema;
74529
+ var OmoAgentModelEntrySchema, PermissionValueSchema, BashPermissionSchema, OmoAgentPermissionSchema, OmoAgentDefInputSchema, OmoAgentDefSchema, OmoAgentsConfigSchema;
74396
74530
  var init_agent = __esm(() => {
74397
74531
  init_zod();
74398
74532
  init_fallback_models();
74399
74533
  init_model_ref();
74400
74534
  OmoAgentModelEntrySchema = union([string2(), OmoFallbackModelObjectSchema]);
74535
+ PermissionValueSchema = _enum(["ask", "allow", "deny"]);
74536
+ BashPermissionSchema = union([
74537
+ PermissionValueSchema,
74538
+ record(string2(), PermissionValueSchema)
74539
+ ]);
74540
+ OmoAgentPermissionSchema = object({
74541
+ edit: PermissionValueSchema.optional(),
74542
+ bash: BashPermissionSchema.optional(),
74543
+ webfetch: PermissionValueSchema.optional(),
74544
+ task: PermissionValueSchema.optional(),
74545
+ doom_loop: PermissionValueSchema.optional(),
74546
+ external_directory: PermissionValueSchema.optional()
74547
+ }).catchall(PermissionValueSchema.optional());
74401
74548
  OmoAgentDefInputSchema = object({
74402
74549
  description: string2().optional(),
74403
74550
  prompt: string2().optional(),
@@ -74414,7 +74561,9 @@ var init_agent = __esm(() => {
74414
74561
  disallowed_tools: array(string2()).optional(),
74415
74562
  max_turns: number2().int().nonnegative().optional(),
74416
74563
  temperature: number2().min(0).max(2).optional(),
74417
- disable: boolean2().optional()
74564
+ disable: boolean2().optional(),
74565
+ permission: OmoAgentPermissionSchema.optional(),
74566
+ prompt_append: string2().optional()
74418
74567
  }).strict();
74419
74568
  OmoAgentDefSchema = preprocess((value) => isRecord5(value) ? normalizeLegacyModelFields(value) : value, OmoAgentDefInputSchema);
74420
74569
  OmoAgentsConfigSchema = record(string2(), OmoAgentDefSchema);
@@ -75200,6 +75349,64 @@ var init_schema = __esm(() => {
75200
75349
  init_computer();
75201
75350
  });
75202
75351
 
75352
+ // packages/omo-config-core/src/loader/types.ts
75353
+ import { existsSync as existsSync13, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "node:fs";
75354
+ var MERGED_OMO_CONFIG_PATH = "(merged omo config)", DEFAULT_READ_FILE_SYSTEM;
75355
+ var init_types3 = __esm(() => {
75356
+ DEFAULT_READ_FILE_SYSTEM = {
75357
+ existsSync: existsSync13,
75358
+ lstatSync: lstatSync2,
75359
+ readFileSync: readFileSync5,
75360
+ realpathSync: realpathSync5
75361
+ };
75362
+ });
75363
+
75364
+ // packages/omo-config-core/src/loader/diagnostic-lines.ts
75365
+ function displayOmoConfigPath(path, homeDir) {
75366
+ if (path === MERGED_OMO_CONFIG_PATH)
75367
+ return "merged config";
75368
+ if (homeDir === undefined || homeDir.length === 0)
75369
+ return path;
75370
+ const slashed = (value) => value.replaceAll("\\", "/");
75371
+ const home = slashed(homeDir).replace(/\/+$/, "");
75372
+ const file = slashed(path);
75373
+ const fold = (value) => /^[A-Za-z]:\//.test(home) ? value.toLowerCase() : value;
75374
+ if (fold(file) === fold(home))
75375
+ return "~";
75376
+ return fold(file).startsWith(`${fold(home)}/`) ? `~${file.slice(home.length)}` : path;
75377
+ }
75378
+ function notLoadedReason(diagnostic) {
75379
+ switch (diagnostic.kind) {
75380
+ case "parse":
75381
+ return "JSONC parse error";
75382
+ case "read":
75383
+ return "unreadable";
75384
+ default:
75385
+ return diagnostic.issuePaths === undefined || diagnostic.issuePaths.length === 0 ? "invalid config" : `invalid: ${diagnostic.issuePaths.join(", ")}`;
75386
+ }
75387
+ }
75388
+ function omoConfigDiagnosticLines(diagnostics, options = {}) {
75389
+ return diagnostics.flatMap((diagnostic) => {
75390
+ const file = displayOmoConfigPath(diagnostic.path, options.homeDir);
75391
+ switch (diagnostic.kind) {
75392
+ case "invalid-value":
75393
+ return (diagnostic.issuePaths ?? []).map((key) => `config: ${file}: ${key} ignored (invalid value)`);
75394
+ case "unknown-keys":
75395
+ return (diagnostic.issuePaths ?? []).map((key) => `config: ${file}: ${key} ignored (unknown key)`);
75396
+ case "parse":
75397
+ case "read":
75398
+ case "validation":
75399
+ return diagnostic.path === MERGED_OMO_CONFIG_PATH ? [`config: ${file}: reset to defaults (${notLoadedReason(diagnostic)})`] : [`config: ${file}: not loaded (${notLoadedReason(diagnostic)})`];
75400
+ case "deprecated-keys":
75401
+ case "profile":
75402
+ return [];
75403
+ }
75404
+ });
75405
+ }
75406
+ var init_diagnostic_lines = __esm(() => {
75407
+ init_types3();
75408
+ });
75409
+
75203
75410
  // packages/omo-config-core/src/loader/disabled-skills.ts
75204
75411
  var init_disabled_skills = () => {};
75205
75412
 
@@ -75239,24 +75446,265 @@ var init_merge = __esm(() => {
75239
75446
  DANGEROUS_KEYS2 = new Set(["__proto__", "constructor", "prototype"]);
75240
75447
  });
75241
75448
 
75449
+ // packages/omo-config-core/src/loader/prune-invalid-leaves.ts
75450
+ function isContainer(value) {
75451
+ return typeof value === "object" && value !== null;
75452
+ }
75453
+ function childOf(container, segment) {
75454
+ return Array.isArray(container) ? container[Number(segment)] : container[String(segment)];
75455
+ }
75456
+ function hasChild(container, segment) {
75457
+ if (Array.isArray(container)) {
75458
+ const index = Number(segment);
75459
+ return Number.isInteger(index) && index >= 0 && index < container.length;
75460
+ }
75461
+ return Object.hasOwn(container, String(segment));
75462
+ }
75463
+ function segmentsOf(path) {
75464
+ return path.map((segment) => typeof segment === "number" ? segment : String(segment));
75465
+ }
75466
+ function targetPaths(root, issue) {
75467
+ const base = segmentsOf(issue.path);
75468
+ if (issue.code === "unrecognized_keys")
75469
+ return issue.keys.map((key) => [...base, key]);
75470
+ let node = root;
75471
+ let depth = 0;
75472
+ for (const segment of base) {
75473
+ if (!isContainer(node) || !hasChild(node, segment))
75474
+ break;
75475
+ node = childOf(node, segment);
75476
+ depth += 1;
75477
+ }
75478
+ return [base.slice(0, depth)];
75479
+ }
75480
+ function isAncestor(ancestor, path) {
75481
+ return ancestor.length < path.length && ancestor.every((segment, index) => segment === path[index]);
75482
+ }
75483
+ function compareDescending(left, right) {
75484
+ const length = Math.min(left.length, right.length);
75485
+ for (let index = 0;index < length; index += 1) {
75486
+ const a = left[index];
75487
+ const b = right[index];
75488
+ if (a === b)
75489
+ continue;
75490
+ if (typeof a === "number" && typeof b === "number")
75491
+ return b - a;
75492
+ return String(b).localeCompare(String(a));
75493
+ }
75494
+ return right.length - left.length;
75495
+ }
75496
+ function removeChild(container, segment) {
75497
+ if (Array.isArray(container))
75498
+ container.splice(Number(segment), 1);
75499
+ else
75500
+ delete container[String(segment)];
75501
+ }
75502
+ function isEmpty(container) {
75503
+ return Array.isArray(container) ? container.length === 0 : Object.keys(container).length === 0;
75504
+ }
75505
+ function removePathAndEmptiedAncestors(root, path) {
75506
+ const chain = [root];
75507
+ for (const segment of path.slice(0, -1)) {
75508
+ const current = chain[chain.length - 1];
75509
+ if (current === undefined)
75510
+ return;
75511
+ const next = childOf(current, segment);
75512
+ if (!isContainer(next))
75513
+ return;
75514
+ chain.push(next);
75515
+ }
75516
+ for (let depth = path.length - 1;depth >= 0; depth -= 1) {
75517
+ const container = chain[depth];
75518
+ const segment = path[depth];
75519
+ if (container === undefined || segment === undefined)
75520
+ return;
75521
+ removeChild(container, segment);
75522
+ if (depth === 0 || !isEmpty(container))
75523
+ return;
75524
+ }
75525
+ }
75526
+ function prunePass(root, issues) {
75527
+ const byKey = new Map;
75528
+ for (const issue of issues) {
75529
+ for (const path of targetPaths(root, issue)) {
75530
+ if (path.length === 0)
75531
+ return null;
75532
+ const key = path.map((segment) => String(segment)).join(".");
75533
+ if (!byKey.has(key))
75534
+ byKey.set(key, { key, message: issue.message, path });
75535
+ }
75536
+ }
75537
+ const targets = [...byKey.values()];
75538
+ const outermost = targets.filter((target) => !targets.some((other) => isAncestor(other.path, target.path))).sort((left, right) => compareDescending(left.path, right.path));
75539
+ const next = structuredClone(root);
75540
+ for (const target of outermost)
75541
+ removePathAndEmptiedAncestors(next, target.path);
75542
+ return { dropped: outermost, next };
75543
+ }
75544
+ function pruneInvalidConfigPaths(config, issues, validate, maxPasses = MAX_PRUNE_PASSES) {
75545
+ const dropped = [];
75546
+ let current = config;
75547
+ let pending = issues;
75548
+ for (let pass = 0;pass < maxPasses; pass += 1) {
75549
+ const step = prunePass(current, pending);
75550
+ if (step === null || step.dropped.length === 0)
75551
+ return { ok: false, dropped };
75552
+ dropped.push(...step.dropped);
75553
+ current = step.next;
75554
+ if (Object.keys(current).length === 0)
75555
+ return { ok: false, dropped };
75556
+ const validation = validate(current);
75557
+ if (validation.success)
75558
+ return { ok: true, config: current, dropped };
75559
+ pending = validation.issues;
75560
+ }
75561
+ return { ok: false, dropped };
75562
+ }
75563
+ var MAX_PRUNE_PASSES = 32;
75564
+
75565
+ // packages/omo-config-core/src/loader/layer-validation.ts
75566
+ function validationDiagnostic(path, issues) {
75567
+ const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
75568
+ return {
75569
+ kind: "validation",
75570
+ message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
75571
+ path,
75572
+ issuePaths
75573
+ };
75574
+ }
75575
+ function invalidValueDiagnostics(path, dropped) {
75576
+ return dropped.map((entry) => ({
75577
+ kind: "invalid-value",
75578
+ message: `Ignored invalid value in ${path}: ${entry.key}: ${entry.message}`,
75579
+ path,
75580
+ issuePaths: [entry.key]
75581
+ }));
75582
+ }
75583
+ function unrecognizedKeyIssues(issues) {
75584
+ return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
75585
+ }
75586
+ function isRecord11(value) {
75587
+ return typeof value === "object" && value !== null && !Array.isArray(value);
75588
+ }
75589
+ function sanitizeUnsafeKeys(value, path = []) {
75590
+ if (Array.isArray(value)) {
75591
+ const issues = [];
75592
+ const sanitized = value.map((entry, index) => {
75593
+ const nested = sanitizeUnsafeKeys(entry, [...path, String(index)]);
75594
+ issues.push(...nested.issues);
75595
+ return nested.value;
75596
+ });
75597
+ return { issues, value: sanitized };
75598
+ }
75599
+ if (!isRecord11(value))
75600
+ return { issues: [], value };
75601
+ const issues = [];
75602
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
75603
+ issues.push({ keys: ["__proto__"], path });
75604
+ }
75605
+ const sanitized = {};
75606
+ for (const [key, entry] of Object.entries(value)) {
75607
+ if (isUnsafeObjectKey2(key)) {
75608
+ issues.push({ keys: [key], path });
75609
+ continue;
75610
+ }
75611
+ const nested = sanitizeUnsafeKeys(entry, [...path, key]);
75612
+ issues.push(...nested.issues);
75613
+ sanitized[key] = nested.value;
75614
+ }
75615
+ return { issues, value: sanitized };
75616
+ }
75617
+ function containerAt(record, path) {
75618
+ let node = record;
75619
+ for (const segment of path) {
75620
+ if (Array.isArray(node)) {
75621
+ const index = Number(segment);
75622
+ if (!Number.isInteger(index) || index < 0 || index >= node.length)
75623
+ return null;
75624
+ node = node[index];
75625
+ } else if (isRecord11(node) && Object.hasOwn(node, segment)) {
75626
+ node = node[segment];
75627
+ } else {
75628
+ return null;
75629
+ }
75630
+ }
75631
+ return isRecord11(node) ? node : null;
75632
+ }
75633
+ function stripUnrecognizedKeys(record, issues) {
75634
+ const stripped = structuredClone(record);
75635
+ const issuePaths = [];
75636
+ for (const issue of issues) {
75637
+ const container = containerAt(stripped, issue.path);
75638
+ if (container === null)
75639
+ continue;
75640
+ for (const key of issue.keys) {
75641
+ delete container[key];
75642
+ issuePaths.push([...issue.path, key].join("."));
75643
+ }
75644
+ }
75645
+ return { issuePaths, stripped };
75646
+ }
75647
+ function toRecord(value) {
75648
+ if (value === null || typeof value !== "object" || Array.isArray(value))
75649
+ return null;
75650
+ const record = {};
75651
+ for (const [key, entry] of Object.entries(value)) {
75652
+ record[key] = entry;
75653
+ }
75654
+ return record;
75655
+ }
75656
+ function validateConfigLayer(path, data) {
75657
+ const sanitized = sanitizeUnsafeKeys(data);
75658
+ const record = toRecord(sanitized.value);
75659
+ const unsafeIssuePaths = sanitized.issues.flatMap((issue) => issue.keys.map((key) => [...issue.path, key].join(".")));
75660
+ const unsafeDiagnostics = unsafeIssuePaths.length === 0 ? [] : [{ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${unsafeIssuePaths.join(", ")}`, path, issuePaths: unsafeIssuePaths }];
75661
+ const validation = OmoConfigLayerSchema.safeParse(sanitized.value);
75662
+ if (validation.success) {
75663
+ if (record !== null)
75664
+ return { loaded: true, diagnostics: unsafeDiagnostics, value: record };
75665
+ return {
75666
+ loaded: false,
75667
+ diagnostics: [{ kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path }]
75668
+ };
75669
+ }
75670
+ const rejected = { loaded: false, diagnostics: [validationDiagnostic(path, validation.error.issues)] };
75671
+ const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
75672
+ if (record === null)
75673
+ return rejected;
75674
+ let candidate = record;
75675
+ let issues = validation.error.issues;
75676
+ const diagnostics = [...unsafeDiagnostics];
75677
+ if (unknownIssues.length > 0) {
75678
+ const { issuePaths, stripped } = stripUnrecognizedKeys(record, unknownIssues);
75679
+ if (issuePaths.length > 0) {
75680
+ diagnostics.push({ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`, path, issuePaths });
75681
+ }
75682
+ const strippedValidation = validateLayerRecord(stripped);
75683
+ if (strippedValidation.success)
75684
+ return { loaded: true, diagnostics, value: stripped };
75685
+ candidate = stripped;
75686
+ issues = strippedValidation.issues;
75687
+ }
75688
+ const pruned = pruneInvalidConfigPaths(candidate, issues, validateLayerRecord);
75689
+ if (!pruned.ok)
75690
+ return rejected;
75691
+ return { loaded: true, diagnostics: [...diagnostics, ...invalidValueDiagnostics(path, pruned.dropped)], value: pruned.config };
75692
+ }
75693
+ var validateLayerRecord = (record) => {
75694
+ const parsed = OmoConfigLayerSchema.safeParse(record);
75695
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
75696
+ };
75697
+ var init_layer_validation = __esm(() => {
75698
+ init_schema();
75699
+ init_merge();
75700
+ });
75701
+
75242
75702
  // packages/omo-config-core/src/internal/posix-path.ts
75243
75703
  function toPosixPath(path) {
75244
75704
  return path.split("\\").join("/");
75245
75705
  }
75246
75706
  var init_posix_path = () => {};
75247
75707
 
75248
- // packages/omo-config-core/src/loader/types.ts
75249
- import { existsSync as existsSync13, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "node:fs";
75250
- var DEFAULT_READ_FILE_SYSTEM;
75251
- var init_types3 = __esm(() => {
75252
- DEFAULT_READ_FILE_SYSTEM = {
75253
- existsSync: existsSync13,
75254
- lstatSync: lstatSync2,
75255
- readFileSync: readFileSync5,
75256
- realpathSync: realpathSync5
75257
- };
75258
- });
75259
-
75260
75708
  // packages/omo-config-core/src/loader/paths.ts
75261
75709
  import { userInfo } from "node:os";
75262
75710
  import { dirname as dirname4, join as join15, posix as posix2, resolve as resolve3 } from "node:path";
@@ -75360,7 +75808,7 @@ function resolveOmoProfileName(options = {}) {
75360
75808
  const env = options.env ?? process.env;
75361
75809
  return profileName(options.profile) ?? profileName(env["OMO_PROFILE"]) ?? profileName(env["OCX_PROFILE"]) ?? profileNameFromOpenCodeConfigDir(env["OPENCODE_CONFIG_DIR"]);
75362
75810
  }
75363
- function toRecord(value) {
75811
+ function toRecord2(value) {
75364
75812
  if (value === null || typeof value !== "object" || Array.isArray(value))
75365
75813
  return;
75366
75814
  return Object.fromEntries(Object.entries(value));
@@ -75381,13 +75829,13 @@ function harnessLayer(config, harness) {
75381
75829
  const legacyKeys = Object.entries(OMO_CONFIG_LEGACY_HARNESS_ALIASES).filter(([, target]) => target === canonical).map(([legacy]) => harnessBlockKey(legacy));
75382
75830
  let layer = {};
75383
75831
  for (const key of [...legacyKeys, harnessBlockKey(canonical)]) {
75384
- layer = mergeOmoConfigRecords(layer, toRecord(config[key]) ?? {});
75832
+ layer = mergeOmoConfigRecords(layer, toRecord2(config[key]) ?? {});
75385
75833
  }
75386
75834
  return layer;
75387
75835
  }
75388
75836
  function resolveOmoConfigView(options) {
75389
- const profiles = toRecord(options.config["profiles"]);
75390
- const profile = options.profile === undefined ? undefined : toRecord(profiles?.[options.profile]);
75837
+ const profiles = toRecord2(options.config["profiles"]);
75838
+ const profile = options.profile === undefined ? undefined : toRecord2(profiles?.[options.profile]);
75391
75839
  const diagnostics = profile === undefined && options.profile !== undefined ? [{
75392
75840
  kind: "profile",
75393
75841
  message: `Activated omo profile "${options.profile}" does not exist; using the base configuration`,
@@ -75442,70 +75890,9 @@ function stripResolutionControlKeys(config) {
75442
75890
  } = config;
75443
75891
  return resolved;
75444
75892
  }
75445
- function validationDiagnostic(path, issues) {
75446
- const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
75447
- return {
75448
- kind: "validation",
75449
- message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
75450
- path,
75451
- issuePaths
75452
- };
75453
- }
75454
- function unrecognizedKeyIssues(issues) {
75455
- return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
75456
- }
75457
- function hasUnsafeUnrecognizedKey(issues) {
75458
- return issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey2(key)));
75459
- }
75460
- function hasTamperedPrototype(value) {
75461
- if (Array.isArray(value))
75462
- return value.some((entry) => hasTamperedPrototype(entry));
75463
- if (!isRecord11(value))
75464
- return false;
75465
- const prototype = Object.getPrototypeOf(value);
75466
- if (prototype !== Object.prototype && prototype !== null)
75467
- return true;
75468
- return Object.values(value).some((entry) => hasTamperedPrototype(entry));
75469
- }
75470
- function isRecord11(value) {
75471
- return typeof value === "object" && value !== null && !Array.isArray(value);
75472
- }
75473
- function containerAt(record, path) {
75474
- let container = record;
75475
- for (const segment of path) {
75476
- const next = container[segment];
75477
- if (!isRecord11(next))
75478
- return null;
75479
- container = next;
75480
- }
75481
- return container;
75482
- }
75483
- function stripUnrecognizedKeys(record, issues) {
75484
- const stripped = structuredClone(record);
75485
- const issuePaths = [];
75486
- for (const issue of issues) {
75487
- const container = containerAt(stripped, issue.path);
75488
- if (container === null)
75489
- continue;
75490
- for (const key of issue.keys) {
75491
- delete container[key];
75492
- issuePaths.push([...issue.path, key].join("."));
75493
- }
75494
- }
75495
- return { issuePaths, stripped };
75496
- }
75497
- function toRecord2(value) {
75498
- if (value === null || typeof value !== "object" || Array.isArray(value))
75499
- return null;
75500
- const record = {};
75501
- for (const [key, entry] of Object.entries(value)) {
75502
- record[key] = entry;
75503
- }
75504
- return record;
75505
- }
75506
75893
  function readConfigSource(path, scope, fileSystem) {
75507
75894
  if (!fileSystem.existsSync(path)) {
75508
- return { source: { exists: false, loaded: false, path, scope } };
75895
+ return { diagnostics: [], source: { exists: false, loaded: false, path, scope } };
75509
75896
  }
75510
75897
  let content;
75511
75898
  try {
@@ -75513,68 +75900,23 @@ function readConfigSource(path, scope, fileSystem) {
75513
75900
  } catch (error) {
75514
75901
  const message = error instanceof Error ? error.message : String(error);
75515
75902
  return {
75516
- diagnostic: { kind: "read", message: `Failed to read ${path}: ${message}`, path },
75903
+ diagnostics: [{ kind: "read", message: `Failed to read ${path}: ${message}`, path }],
75517
75904
  source: { exists: true, loaded: false, path, scope }
75518
75905
  };
75519
75906
  }
75520
75907
  const parsed = parseJsoncSafe2(content);
75521
75908
  if (parsed.errors.length > 0) {
75522
75909
  return {
75523
- diagnostic: {
75910
+ diagnostics: [{
75524
75911
  kind: "parse",
75525
75912
  message: `JSONC parse error in ${path}: ${parsed.errors.map((error) => error.message).join(", ")}`,
75526
75913
  path
75527
- },
75528
- source: { exists: true, loaded: false, path, scope }
75529
- };
75530
- }
75531
- if (hasTamperedPrototype(parsed.data)) {
75532
- return {
75533
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: "__proto__" member is not allowed`, path },
75914
+ }],
75534
75915
  source: { exists: true, loaded: false, path, scope }
75535
75916
  };
75536
75917
  }
75537
- const parsedRecord = toRecord2(parsed.data);
75538
- const validation = OmoConfigLayerSchema.safeParse(parsed.data);
75539
- if (!validation.success) {
75540
- const unrecognized = unrecognizedKeyIssues(validation.error.issues);
75541
- if (hasUnsafeUnrecognizedKey(unrecognized)) {
75542
- return {
75543
- diagnostic: validationDiagnostic(path, validation.error.issues),
75544
- source: { exists: true, loaded: false, path, scope }
75545
- };
75546
- }
75547
- const rejected = {
75548
- diagnostic: validationDiagnostic(path, validation.error.issues),
75549
- source: { exists: true, loaded: false, path, scope }
75550
- };
75551
- const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
75552
- if (parsedRecord === null || unknownIssues.length === 0)
75553
- return rejected;
75554
- const { issuePaths, stripped } = stripUnrecognizedKeys(parsedRecord, unknownIssues);
75555
- if (!OmoConfigLayerSchema.safeParse(stripped).success)
75556
- return rejected;
75557
- return {
75558
- diagnostic: {
75559
- kind: "unknown-keys",
75560
- message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`,
75561
- path,
75562
- issuePaths
75563
- },
75564
- source: { exists: true, loaded: true, path, scope },
75565
- value: stripped
75566
- };
75567
- }
75568
- if (parsedRecord === null) {
75569
- return {
75570
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path },
75571
- source: { exists: true, loaded: false, path, scope }
75572
- };
75573
- }
75574
- return {
75575
- source: { exists: true, loaded: true, path, scope },
75576
- value: parsedRecord
75577
- };
75918
+ const layer = validateConfigLayer(path, parsed.data);
75919
+ 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 } };
75578
75920
  }
75579
75921
  function legacyCategoryDiagnostic(path, renames) {
75580
75922
  const detail = renames.map((rename) => rename.dropped ? `${rename.path} ignored because ${rename.canonical} is also configured` : `${rename.path} renamed to ${rename.canonical}`).join(", ");
@@ -75609,8 +75951,7 @@ function loadOmoConfig(options = {}) {
75609
75951
  })) {
75610
75952
  const loaded = readConfigSource(candidate.path, candidate.scope, fileSystem);
75611
75953
  sources.push(loaded.source);
75612
- if (loaded.diagnostic !== undefined)
75613
- diagnostics.push(loaded.diagnostic);
75954
+ diagnostics.push(...loaded.diagnostics);
75614
75955
  if (loaded.value !== undefined) {
75615
75956
  const canonicalized = canonicalizeLegacyCategoryNames(loaded.value);
75616
75957
  if (canonicalized.renames.length > 0) {
@@ -75633,7 +75974,8 @@ function loadOmoConfig(options = {}) {
75633
75974
  ...options.harness === undefined ? {} : { harness: options.harness },
75634
75975
  ...requestedProfile === undefined ? {} : { profile: requestedProfile }
75635
75976
  });
75636
- const finalConfig = OmoConfigSchema.safeParse(mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config));
75977
+ const finalInput = mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config);
75978
+ const finalConfig = OmoConfigSchema.safeParse(finalInput);
75637
75979
  if (finalConfig.success) {
75638
75980
  return {
75639
75981
  config: stripResolutionControlKeys(finalConfig.data),
@@ -75643,9 +75985,22 @@ function loadOmoConfig(options = {}) {
75643
75985
  sources
75644
75986
  };
75645
75987
  }
75988
+ const pruned = pruneInvalidConfigPaths(finalInput, finalConfig.error.issues, (record) => {
75989
+ const parsed = OmoConfigSchema.safeParse(record);
75990
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
75991
+ });
75992
+ if (pruned.ok) {
75993
+ return {
75994
+ config: stripResolutionControlKeys(OmoConfigSchema.parse(pruned.config)),
75995
+ diagnostics: [...diagnostics, ...resolved.diagnostics, ...invalidValueDiagnostics(MERGED_OMO_CONFIG_PATH, pruned.dropped)],
75996
+ layers,
75997
+ ...resolved.profile === undefined ? {} : { profile: resolved.profile },
75998
+ sources
75999
+ };
76000
+ }
75646
76001
  return {
75647
76002
  config: stripResolutionControlKeys(OmoConfigSchema.parse(DEFAULT_RAW_CONFIG)),
75648
- diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic("(merged omo config)", finalConfig.error.issues)],
76003
+ diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic(MERGED_OMO_CONFIG_PATH, finalConfig.error.issues)],
75649
76004
  layers,
75650
76005
  ...resolved.profile === undefined ? {} : { profile: resolved.profile },
75651
76006
  sources
@@ -75655,6 +76010,7 @@ var DEFAULT_RAW_CONFIG;
75655
76010
  var init_loader2 = __esm(() => {
75656
76011
  init_main();
75657
76012
  init_schema();
76013
+ init_layer_validation();
75658
76014
  init_merge();
75659
76015
  init_paths();
75660
76016
  init_resolution();
@@ -75669,6 +76025,7 @@ var init_loader2 = __esm(() => {
75669
76025
 
75670
76026
  // packages/omo-config-core/src/loader/index.ts
75671
76027
  var init_loader3 = __esm(() => {
76028
+ init_diagnostic_lines();
75672
76029
  init_disabled_skills();
75673
76030
  init_loader2();
75674
76031
  init_merge();
@@ -89099,7 +89456,7 @@ var package_default2;
89099
89456
  var init_package2 = __esm(() => {
89100
89457
  package_default2 = {
89101
89458
  name: "@oh-my-opencode/omo-codex",
89102
- version: "5.1.3",
89459
+ version: "5.1.5",
89103
89460
  type: "module",
89104
89461
  private: true,
89105
89462
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -102100,6 +102457,7 @@ async function processEvents(ctx, stream, state) {
102100
102457
  }
102101
102458
  }
102102
102459
  // packages/omo-opencode/src/config/validate.ts
102460
+ init_src4();
102103
102461
  import { relative as relative7 } from "node:path";
102104
102462
 
102105
102463
  // packages/omo-opencode/src/shared/disabled-providers.ts
@@ -102220,11 +102578,11 @@ function baseView(config) {
102220
102578
  }
102221
102579
  return result;
102222
102580
  }
102223
- function appendViews(views, layers, select) {
102581
+ function appendViews(views, layers, select, keyPrefix) {
102224
102582
  for (const layer of layers) {
102225
102583
  const config = select(layer.config);
102226
102584
  if (Object.keys(config).length > 0)
102227
- views.push({ config, path: layer.source.path });
102585
+ views.push({ config, keyPrefix, path: layer.source.path });
102228
102586
  }
102229
102587
  }
102230
102588
  function recordFields(value, fields) {
@@ -102253,10 +102611,21 @@ function modelInput(view) {
102253
102611
  };
102254
102612
  }
102255
102613
  function modelView(view) {
102256
- const parsed = OmoConfigSchema.safeParse(modelInput(view));
102257
- if (!parsed.success)
102614
+ const input = modelInput(view);
102615
+ const parsed = OmoConfigSchema.safeParse(input);
102616
+ const modelConfig = parsed.success ? parsed.data : (() => {
102617
+ const pruned = pruneInvalidConfigPaths(input, parsed.error.issues, (record) => {
102618
+ const validation = OmoConfigSchema.safeParse(record);
102619
+ return validation.success ? { success: true } : { success: false, issues: validation.error.issues };
102620
+ });
102621
+ if (!pruned.ok)
102622
+ return;
102623
+ const validation = OmoConfigSchema.safeParse(pruned.config);
102624
+ return validation.success ? validation.data : undefined;
102625
+ })();
102626
+ if (modelConfig === undefined)
102258
102627
  return { config: {}, diagnostics: [] };
102259
- const resolved = resolveModelReferences(parsed.data);
102628
+ const resolved = resolveModelReferences(modelConfig);
102260
102629
  return {
102261
102630
  config: {
102262
102631
  ...resolved.view.agents === undefined ? {} : { agents: resolved.view.agents },
@@ -102275,14 +102644,15 @@ function loadOmoOpenCodeConfigChain(directory, environment = process.env) {
102275
102644
  });
102276
102645
  const views = [];
102277
102646
  const selectedProfile = loaded.profile;
102278
- appendViews(views, loaded.layers, baseView);
102279
- appendViews(views, loaded.layers, block2);
102280
- appendViews(views, loaded.layers, (config) => baseView(profile(config, selectedProfile)));
102281
- appendViews(views, loaded.layers, (config) => block2(profile(config, selectedProfile)));
102647
+ const profilePrefix = selectedProfile === undefined ? "" : `profiles.${selectedProfile}.`;
102648
+ appendViews(views, loaded.layers, baseView, "");
102649
+ appendViews(views, loaded.layers, block2, "[opencode].");
102650
+ appendViews(views, loaded.layers, (config) => baseView(profile(config, selectedProfile)), profilePrefix);
102651
+ appendViews(views, loaded.layers, (config) => block2(profile(config, selectedProfile)), `${profilePrefix}[opencode].`);
102282
102652
  const resolvedModels = modelView(resolved.config);
102283
102653
  if (Object.keys(resolvedModels.config).length > 0) {
102284
102654
  const source = loaded.layers[0]?.source.path ?? directory;
102285
- views.push({ config: resolvedModels.config, path: source });
102655
+ views.push({ config: resolvedModels.config, keyPrefix: "", path: source });
102286
102656
  }
102287
102657
  let protectedUserView = {};
102288
102658
  for (const layer of loaded.layers) {
@@ -102369,7 +102739,7 @@ function unknownPaths(schema, value, path) {
102369
102739
  if (def.type === "object" && isPlainRecord5(value) && def.shape !== undefined) {
102370
102740
  const catchall = def.catchall !== undefined && definition(def.catchall).type !== "never" ? def.catchall : undefined;
102371
102741
  return Object.entries(value).flatMap(([key, entry]) => {
102372
- const propertySchema = def.shape?.[key];
102742
+ const propertySchema = def.shape !== undefined && Object.hasOwn(def.shape, key) ? def.shape[key] : undefined;
102373
102743
  if (propertySchema !== undefined)
102374
102744
  return unknownPaths(propertySchema, entry, [...path, key]);
102375
102745
  if (catchall !== undefined)
@@ -102407,6 +102777,9 @@ function findUnknownKeyPaths(schema, value) {
102407
102777
  return unknownPaths(schema, value, []);
102408
102778
  }
102409
102779
 
102780
+ // packages/omo-opencode/src/config/prune-plugin-view.ts
102781
+ init_src4();
102782
+
102410
102783
  // packages/omo-opencode/src/config/schema/agent-names.ts
102411
102784
  init_zod();
102412
102785
  var BuiltinAgentNameSchema = _enum([
@@ -102484,19 +102857,19 @@ var FallbackModelsSchema = union([
102484
102857
 
102485
102858
  // packages/omo-opencode/src/config/schema/internal/permission.ts
102486
102859
  init_zod();
102487
- var PermissionValueSchema = _enum(["ask", "allow", "deny"]);
102488
- var BashPermissionSchema = union([
102489
- PermissionValueSchema,
102490
- record(string2(), PermissionValueSchema)
102860
+ var PermissionValueSchema2 = _enum(["ask", "allow", "deny"]);
102861
+ var BashPermissionSchema2 = union([
102862
+ PermissionValueSchema2,
102863
+ record(string2(), PermissionValueSchema2)
102491
102864
  ]);
102492
102865
  var AgentPermissionSchema = object({
102493
- edit: PermissionValueSchema.optional(),
102494
- bash: BashPermissionSchema.optional(),
102495
- webfetch: PermissionValueSchema.optional(),
102496
- task: PermissionValueSchema.optional(),
102497
- doom_loop: PermissionValueSchema.optional(),
102498
- external_directory: PermissionValueSchema.optional()
102499
- }).catchall(PermissionValueSchema.optional());
102866
+ edit: PermissionValueSchema2.optional(),
102867
+ bash: BashPermissionSchema2.optional(),
102868
+ webfetch: PermissionValueSchema2.optional(),
102869
+ task: PermissionValueSchema2.optional(),
102870
+ doom_loop: PermissionValueSchema2.optional(),
102871
+ external_directory: PermissionValueSchema2.optional()
102872
+ }).catchall(PermissionValueSchema2.optional());
102500
102873
 
102501
102874
  // packages/omo-opencode/src/config/schema/agent-overrides.ts
102502
102875
  var AgentOverrideConfigSchema = object({
@@ -103061,6 +103434,26 @@ var OhMyOpenCodeConfigSchema = object({
103061
103434
  default_mode: DefaultModeConfigSchema.optional(),
103062
103435
  _migrations: array(string2()).optional()
103063
103436
  });
103437
+ // packages/omo-opencode/src/config/prune-plugin-view.ts
103438
+ var validatePluginRecord = (record) => {
103439
+ const parsed = OhMyOpenCodeConfigSchema.safeParse(record);
103440
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
103441
+ };
103442
+ function prunePluginView(input) {
103443
+ const validation = OhMyOpenCodeConfigSchema.safeParse(input.config);
103444
+ if (validation.success)
103445
+ return { config: input.config, issues: [], warnings: [] };
103446
+ const pruned = pruneInvalidConfigPaths(input.config, validation.error.issues, validatePluginRecord);
103447
+ if (!pruned.ok)
103448
+ return { config: input.config, issues: validation.error.issues, warnings: [] };
103449
+ const file = displayOmoConfigPath(input.path, input.homeDir);
103450
+ return {
103451
+ config: pruned.config,
103452
+ issues: [],
103453
+ warnings: pruned.dropped.map((entry) => `config: ${file}: ${input.keyPrefix}${entry.key} ignored (invalid value)`)
103454
+ };
103455
+ }
103456
+
103064
103457
  // packages/omo-opencode/src/config/validate.ts
103065
103458
  function shortPath(configPath) {
103066
103459
  const candidate = relative7(process.cwd(), configPath);
@@ -103070,9 +103463,8 @@ function formatIssuePath(path) {
103070
103463
  const formatted = path.map((segment) => String(segment)).join(".");
103071
103464
  return formatted.length > 0 ? formatted : "<root>";
103072
103465
  }
103073
- function schemaMessages(configPath, rawConfig) {
103074
- const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
103075
- const validationMessages = result.success ? [] : result.error.issues.map((issue) => `${shortPath(configPath)}: ${formatIssuePath(issue.path)}: ${issue.message}`);
103466
+ function schemaMessages(configPath, rawConfig, issues) {
103467
+ const validationMessages = issues.map((issue) => `${shortPath(configPath)}: ${formatIssuePath(issue.path)}: ${issue.message}`);
103076
103468
  const unknownKeyMessages = findUnknownKeyPaths(OhMyOpenCodeConfigSchema, rawConfig).map((path) => `${shortPath(configPath)}: Unknown config key: ${formatIssuePath(path)}`);
103077
103469
  return [...validationMessages, ...unknownKeyMessages];
103078
103470
  }
@@ -103088,11 +103480,13 @@ function parseConfig(rawConfig) {
103088
103480
  }
103089
103481
  return config;
103090
103482
  }
103091
- function parseConfigView(path, rawConfig) {
103483
+ function parseConfigView(view, homeDir) {
103484
+ const pruned = prunePluginView({ config: view.config, homeDir, keyPrefix: view.keyPrefix, path: view.path });
103092
103485
  return {
103093
- config: parseConfig(rawConfig),
103094
- messages: schemaMessages(path, rawConfig),
103095
- path
103486
+ config: parseConfig(pruned.config),
103487
+ messages: schemaMessages(view.path, view.config, pruned.issues),
103488
+ path: view.path,
103489
+ warnings: pruned.warnings
103096
103490
  };
103097
103491
  }
103098
103492
  function mergeViews(views) {
@@ -103184,18 +103578,25 @@ function migrateRalphLoopConfig(config) {
103184
103578
  }
103185
103579
  function validatePluginConfig(directory, environment = process.env) {
103186
103580
  const chain = loadOmoOpenCodeConfigChain(directory, environment);
103187
- const views = chain.views.map((view) => parseConfigView(view.path, view.config));
103188
- const chainMessages = chain.diagnostics.filter((diagnostic) => diagnostic.kind !== "deprecated-keys").map((diagnostic) => `${shortPath(diagnostic.path)}: ${diagnostic.message}`);
103581
+ const homeDir = environment.HOME ?? environment.USERPROFILE;
103582
+ const views = chain.views.map((view) => parseConfigView(view, homeDir));
103583
+ const failing = chain.diagnostics.filter((diagnostic) => diagnostic.kind !== "deprecated-keys" && diagnostic.kind !== "invalid-value");
103584
+ const chainMessages = failing.map((diagnostic) => `${shortPath(diagnostic.path)}: ${diagnostic.message}`);
103189
103585
  const messages = [...chainMessages, ...views.flatMap((view) => view.messages)];
103586
+ const warnings = [
103587
+ ...omoConfigDiagnosticLines(chain.diagnostics.flatMap((diagnostic) => diagnostic.kind === "invalid-value" ? [diagnostic] : []), { homeDir }),
103588
+ ...views.flatMap((view) => view.warnings)
103589
+ ];
103190
103590
  const firstFailingView = views.find((view) => view.messages.length > 0);
103191
103591
  const firstView = views[0];
103192
- const userConfig = parseConfig(chain.protectedUserView);
103592
+ const userConfig = parseConfig(prunePluginView({ config: chain.protectedUserView, homeDir, keyPrefix: "", path: "" }).config);
103193
103593
  const config = applyDisabledProviders(materializeAgentModelChains(protectUserFields(mergeViews(views), userConfig)));
103194
103594
  warnLegacyUlwExecuteKey(chain.views);
103195
103595
  return {
103196
103596
  valid: messages.length === 0,
103197
103597
  messages,
103198
- path: chainMessages.length > 0 ? chain.diagnostics[0]?.path ?? null : firstFailingView?.path ?? firstView?.path ?? null,
103598
+ warnings,
103599
+ path: failing[0]?.path ?? firstFailingView?.path ?? firstView?.path ?? null,
103199
103600
  config: migrateRalphLoopConfig(migrateLegacyUlwExecuteKey(config))
103200
103601
  };
103201
103602
  }
@@ -106319,14 +106720,15 @@ function toOmoConfig(config) {
106319
106720
  function validateConfig() {
106320
106721
  const validation = validatePluginConfig(process.cwd());
106321
106722
  if (!validation.path) {
106322
- return { exists: false, path: null, valid: true, config: null, errors: [] };
106723
+ return { exists: false, path: null, valid: true, config: null, errors: [], warnings: validation.warnings };
106323
106724
  }
106324
106725
  return {
106325
106726
  exists: true,
106326
106727
  path: validation.path,
106327
106728
  valid: validation.valid,
106328
106729
  config: toOmoConfig(validation.config),
106329
- errors: [...validation.messages]
106730
+ errors: [...validation.messages],
106731
+ warnings: validation.warnings
106330
106732
  };
106331
106733
  }
106332
106734
  function collectModelResolutionIssues(config) {
@@ -106379,6 +106781,12 @@ async function checkConfig() {
106379
106781
  }));
106380
106782
  if (legacyWarning !== undefined)
106381
106783
  issues.push(legacyWarning);
106784
+ issues.push(...validation.warnings.map((warning) => ({
106785
+ title: warning,
106786
+ description: "The rest of that file still applies; fix or remove this value to use it.",
106787
+ severity: "warning",
106788
+ affects: ["configuration"]
106789
+ })));
106382
106790
  if (!validation.exists) {
106383
106791
  return {
106384
106792
  name: CHECK_NAMES[CHECK_IDS.CONFIG],