oh-my-openagent 5.1.4 → 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 (60) hide show
  1. package/dist/cli/index.js +464 -186
  2. package/dist/cli-node/index.js +464 -186
  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 +11194 -10937
  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/tui.js +548 -275
  9. package/package.json +13 -13
  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 +2 -2
  21. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  22. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  24. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  28. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  32. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  34. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  35. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  36. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  37. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  38. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  56. package/packages/omo-codex/plugin/package-lock.json +12 -12
  57. package/packages/omo-codex/plugin/package.json +1 -1
  58. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  59. package/packages/omo-codex/scripts/install-dist/install-local.mjs +311 -135
  60. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
package/dist/cli/index.js CHANGED
@@ -64,7 +64,7 @@ var package_default;
64
64
  var init_package = __esm(() => {
65
65
  package_default = {
66
66
  name: "oh-my-opencode",
67
- version: "5.1.4",
67
+ version: "5.1.5",
68
68
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
69
69
  main: "./dist/index.js",
70
70
  types: "dist/index.d.ts",
@@ -299,18 +299,18 @@ var init_package = __esm(() => {
299
299
  typescript: "^7.0.2"
300
300
  },
301
301
  optionalDependencies: {
302
- "oh-my-opencode-darwin-arm64": "5.1.4",
303
- "oh-my-opencode-darwin-x64": "5.1.4",
304
- "oh-my-opencode-darwin-x64-baseline": "5.1.4",
305
- "oh-my-opencode-linux-arm64": "5.1.4",
306
- "oh-my-opencode-linux-arm64-musl": "5.1.4",
307
- "oh-my-opencode-linux-x64": "5.1.4",
308
- "oh-my-opencode-linux-x64-baseline": "5.1.4",
309
- "oh-my-opencode-linux-x64-musl": "5.1.4",
310
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.4",
311
- "oh-my-opencode-windows-arm64": "5.1.4",
312
- "oh-my-opencode-windows-x64": "5.1.4",
313
- "oh-my-opencode-windows-x64-baseline": "5.1.4"
302
+ "oh-my-opencode-darwin-arm64": "5.1.5",
303
+ "oh-my-opencode-darwin-x64": "5.1.5",
304
+ "oh-my-opencode-darwin-x64-baseline": "5.1.5",
305
+ "oh-my-opencode-linux-arm64": "5.1.5",
306
+ "oh-my-opencode-linux-arm64-musl": "5.1.5",
307
+ "oh-my-opencode-linux-x64": "5.1.5",
308
+ "oh-my-opencode-linux-x64-baseline": "5.1.5",
309
+ "oh-my-opencode-linux-x64-musl": "5.1.5",
310
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.5",
311
+ "oh-my-opencode-windows-arm64": "5.1.5",
312
+ "oh-my-opencode-windows-x64": "5.1.5",
313
+ "oh-my-opencode-windows-x64-baseline": "5.1.5"
314
314
  },
315
315
  overrides: {
316
316
  hono: "^4.13.8",
@@ -74379,7 +74379,6 @@ function normalizeLegacyModelFields(entry) {
74379
74379
  delete normalized["reasoningEffort"];
74380
74380
  delete normalized["thinking"];
74381
74381
  delete normalized["textVerbosity"];
74382
- delete normalized["maxTokens"];
74383
74382
  delete normalized["providerOptions"];
74384
74383
  if (typeof entry["model"] === "string")
74385
74384
  normalized["model"] = canonicalModelString(entry["model"]);
@@ -74397,10 +74396,15 @@ function normalizeLegacyModelFields(entry) {
74397
74396
  providerOptions["textVerbosity"] = entry["textVerbosity"];
74398
74397
  if (Object.keys(providerOptions).length > 0)
74399
74398
  normalized["provider_options"] = providerOptions;
74400
- if (entry["max_tokens"] !== undefined)
74399
+ if (entry["max_tokens"] !== undefined) {
74401
74400
  normalized["max_tokens"] = entry["max_tokens"];
74402
- else if (entry["maxTokens"] !== undefined)
74401
+ if (entry["maxTokens"] === undefined || typeof entry["maxTokens"] === "number") {
74402
+ delete normalized["maxTokens"];
74403
+ }
74404
+ } else if (typeof entry["maxTokens"] === "number") {
74403
74405
  normalized["max_tokens"] = entry["maxTokens"];
74406
+ delete normalized["maxTokens"];
74407
+ }
74404
74408
  return normalized;
74405
74409
  }
74406
74410
  function normalizeLegacyModelEntry(entry) {
@@ -74443,12 +74447,25 @@ var init_fallback_models = __esm(() => {
74443
74447
  function isRecord5(value) {
74444
74448
  return typeof value === "object" && value !== null && !Array.isArray(value);
74445
74449
  }
74446
- var OmoAgentModelEntrySchema, OmoAgentDefInputSchema, OmoAgentDefSchema, OmoAgentsConfigSchema;
74450
+ var OmoAgentModelEntrySchema, PermissionValueSchema, BashPermissionSchema, OmoAgentPermissionSchema, OmoAgentDefInputSchema, OmoAgentDefSchema, OmoAgentsConfigSchema;
74447
74451
  var init_agent = __esm(() => {
74448
74452
  init_zod();
74449
74453
  init_fallback_models();
74450
74454
  init_model_ref();
74451
74455
  OmoAgentModelEntrySchema = union([string2(), OmoFallbackModelObjectSchema]);
74456
+ PermissionValueSchema = _enum(["ask", "allow", "deny"]);
74457
+ BashPermissionSchema = union([
74458
+ PermissionValueSchema,
74459
+ record(string2(), PermissionValueSchema)
74460
+ ]);
74461
+ OmoAgentPermissionSchema = object({
74462
+ edit: PermissionValueSchema.optional(),
74463
+ bash: BashPermissionSchema.optional(),
74464
+ webfetch: PermissionValueSchema.optional(),
74465
+ task: PermissionValueSchema.optional(),
74466
+ doom_loop: PermissionValueSchema.optional(),
74467
+ external_directory: PermissionValueSchema.optional()
74468
+ }).catchall(PermissionValueSchema.optional());
74452
74469
  OmoAgentDefInputSchema = object({
74453
74470
  description: string2().optional(),
74454
74471
  prompt: string2().optional(),
@@ -74465,7 +74482,9 @@ var init_agent = __esm(() => {
74465
74482
  disallowed_tools: array(string2()).optional(),
74466
74483
  max_turns: number2().int().nonnegative().optional(),
74467
74484
  temperature: number2().min(0).max(2).optional(),
74468
- disable: boolean2().optional()
74485
+ disable: boolean2().optional(),
74486
+ permission: OmoAgentPermissionSchema.optional(),
74487
+ prompt_append: string2().optional()
74469
74488
  }).strict();
74470
74489
  OmoAgentDefSchema = preprocess((value) => isRecord5(value) ? normalizeLegacyModelFields(value) : value, OmoAgentDefInputSchema);
74471
74490
  OmoAgentsConfigSchema = record(string2(), OmoAgentDefSchema);
@@ -75251,6 +75270,64 @@ var init_schema = __esm(() => {
75251
75270
  init_computer();
75252
75271
  });
75253
75272
 
75273
+ // packages/omo-config-core/src/loader/types.ts
75274
+ import { existsSync as existsSync13, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
75275
+ var MERGED_OMO_CONFIG_PATH = "(merged omo config)", DEFAULT_READ_FILE_SYSTEM;
75276
+ var init_types3 = __esm(() => {
75277
+ DEFAULT_READ_FILE_SYSTEM = {
75278
+ existsSync: existsSync13,
75279
+ lstatSync: lstatSync2,
75280
+ readFileSync: readFileSync5,
75281
+ realpathSync: realpathSync5
75282
+ };
75283
+ });
75284
+
75285
+ // packages/omo-config-core/src/loader/diagnostic-lines.ts
75286
+ function displayOmoConfigPath(path, homeDir) {
75287
+ if (path === MERGED_OMO_CONFIG_PATH)
75288
+ return "merged config";
75289
+ if (homeDir === undefined || homeDir.length === 0)
75290
+ return path;
75291
+ const slashed = (value) => value.replaceAll("\\", "/");
75292
+ const home = slashed(homeDir).replace(/\/+$/, "");
75293
+ const file = slashed(path);
75294
+ const fold = (value) => /^[A-Za-z]:\//.test(home) ? value.toLowerCase() : value;
75295
+ if (fold(file) === fold(home))
75296
+ return "~";
75297
+ return fold(file).startsWith(`${fold(home)}/`) ? `~${file.slice(home.length)}` : path;
75298
+ }
75299
+ function notLoadedReason(diagnostic) {
75300
+ switch (diagnostic.kind) {
75301
+ case "parse":
75302
+ return "JSONC parse error";
75303
+ case "read":
75304
+ return "unreadable";
75305
+ default:
75306
+ return diagnostic.issuePaths === undefined || diagnostic.issuePaths.length === 0 ? "invalid config" : `invalid: ${diagnostic.issuePaths.join(", ")}`;
75307
+ }
75308
+ }
75309
+ function omoConfigDiagnosticLines(diagnostics, options = {}) {
75310
+ return diagnostics.flatMap((diagnostic) => {
75311
+ const file = displayOmoConfigPath(diagnostic.path, options.homeDir);
75312
+ switch (diagnostic.kind) {
75313
+ case "invalid-value":
75314
+ return (diagnostic.issuePaths ?? []).map((key) => `config: ${file}: ${key} ignored (invalid value)`);
75315
+ case "unknown-keys":
75316
+ return (diagnostic.issuePaths ?? []).map((key) => `config: ${file}: ${key} ignored (unknown key)`);
75317
+ case "parse":
75318
+ case "read":
75319
+ case "validation":
75320
+ return diagnostic.path === MERGED_OMO_CONFIG_PATH ? [`config: ${file}: reset to defaults (${notLoadedReason(diagnostic)})`] : [`config: ${file}: not loaded (${notLoadedReason(diagnostic)})`];
75321
+ case "deprecated-keys":
75322
+ case "profile":
75323
+ return [];
75324
+ }
75325
+ });
75326
+ }
75327
+ var init_diagnostic_lines = __esm(() => {
75328
+ init_types3();
75329
+ });
75330
+
75254
75331
  // packages/omo-config-core/src/loader/disabled-skills.ts
75255
75332
  var init_disabled_skills = () => {};
75256
75333
 
@@ -75290,24 +75367,265 @@ var init_merge = __esm(() => {
75290
75367
  DANGEROUS_KEYS2 = new Set(["__proto__", "constructor", "prototype"]);
75291
75368
  });
75292
75369
 
75370
+ // packages/omo-config-core/src/loader/prune-invalid-leaves.ts
75371
+ function isContainer(value) {
75372
+ return typeof value === "object" && value !== null;
75373
+ }
75374
+ function childOf(container, segment) {
75375
+ return Array.isArray(container) ? container[Number(segment)] : container[String(segment)];
75376
+ }
75377
+ function hasChild(container, segment) {
75378
+ if (Array.isArray(container)) {
75379
+ const index = Number(segment);
75380
+ return Number.isInteger(index) && index >= 0 && index < container.length;
75381
+ }
75382
+ return Object.hasOwn(container, String(segment));
75383
+ }
75384
+ function segmentsOf(path) {
75385
+ return path.map((segment) => typeof segment === "number" ? segment : String(segment));
75386
+ }
75387
+ function targetPaths(root, issue) {
75388
+ const base = segmentsOf(issue.path);
75389
+ if (issue.code === "unrecognized_keys")
75390
+ return issue.keys.map((key) => [...base, key]);
75391
+ let node = root;
75392
+ let depth = 0;
75393
+ for (const segment of base) {
75394
+ if (!isContainer(node) || !hasChild(node, segment))
75395
+ break;
75396
+ node = childOf(node, segment);
75397
+ depth += 1;
75398
+ }
75399
+ return [base.slice(0, depth)];
75400
+ }
75401
+ function isAncestor(ancestor, path) {
75402
+ return ancestor.length < path.length && ancestor.every((segment, index) => segment === path[index]);
75403
+ }
75404
+ function compareDescending(left, right) {
75405
+ const length = Math.min(left.length, right.length);
75406
+ for (let index = 0;index < length; index += 1) {
75407
+ const a = left[index];
75408
+ const b = right[index];
75409
+ if (a === b)
75410
+ continue;
75411
+ if (typeof a === "number" && typeof b === "number")
75412
+ return b - a;
75413
+ return String(b).localeCompare(String(a));
75414
+ }
75415
+ return right.length - left.length;
75416
+ }
75417
+ function removeChild(container, segment) {
75418
+ if (Array.isArray(container))
75419
+ container.splice(Number(segment), 1);
75420
+ else
75421
+ delete container[String(segment)];
75422
+ }
75423
+ function isEmpty(container) {
75424
+ return Array.isArray(container) ? container.length === 0 : Object.keys(container).length === 0;
75425
+ }
75426
+ function removePathAndEmptiedAncestors(root, path) {
75427
+ const chain = [root];
75428
+ for (const segment of path.slice(0, -1)) {
75429
+ const current = chain[chain.length - 1];
75430
+ if (current === undefined)
75431
+ return;
75432
+ const next = childOf(current, segment);
75433
+ if (!isContainer(next))
75434
+ return;
75435
+ chain.push(next);
75436
+ }
75437
+ for (let depth = path.length - 1;depth >= 0; depth -= 1) {
75438
+ const container = chain[depth];
75439
+ const segment = path[depth];
75440
+ if (container === undefined || segment === undefined)
75441
+ return;
75442
+ removeChild(container, segment);
75443
+ if (depth === 0 || !isEmpty(container))
75444
+ return;
75445
+ }
75446
+ }
75447
+ function prunePass(root, issues) {
75448
+ const byKey = new Map;
75449
+ for (const issue of issues) {
75450
+ for (const path of targetPaths(root, issue)) {
75451
+ if (path.length === 0)
75452
+ return null;
75453
+ const key = path.map((segment) => String(segment)).join(".");
75454
+ if (!byKey.has(key))
75455
+ byKey.set(key, { key, message: issue.message, path });
75456
+ }
75457
+ }
75458
+ const targets = [...byKey.values()];
75459
+ const outermost = targets.filter((target) => !targets.some((other) => isAncestor(other.path, target.path))).sort((left, right) => compareDescending(left.path, right.path));
75460
+ const next = structuredClone(root);
75461
+ for (const target of outermost)
75462
+ removePathAndEmptiedAncestors(next, target.path);
75463
+ return { dropped: outermost, next };
75464
+ }
75465
+ function pruneInvalidConfigPaths(config, issues, validate, maxPasses = MAX_PRUNE_PASSES) {
75466
+ const dropped = [];
75467
+ let current = config;
75468
+ let pending = issues;
75469
+ for (let pass = 0;pass < maxPasses; pass += 1) {
75470
+ const step = prunePass(current, pending);
75471
+ if (step === null || step.dropped.length === 0)
75472
+ return { ok: false, dropped };
75473
+ dropped.push(...step.dropped);
75474
+ current = step.next;
75475
+ if (Object.keys(current).length === 0)
75476
+ return { ok: false, dropped };
75477
+ const validation = validate(current);
75478
+ if (validation.success)
75479
+ return { ok: true, config: current, dropped };
75480
+ pending = validation.issues;
75481
+ }
75482
+ return { ok: false, dropped };
75483
+ }
75484
+ var MAX_PRUNE_PASSES = 32;
75485
+
75486
+ // packages/omo-config-core/src/loader/layer-validation.ts
75487
+ function validationDiagnostic(path, issues) {
75488
+ const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
75489
+ return {
75490
+ kind: "validation",
75491
+ message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
75492
+ path,
75493
+ issuePaths
75494
+ };
75495
+ }
75496
+ function invalidValueDiagnostics(path, dropped) {
75497
+ return dropped.map((entry) => ({
75498
+ kind: "invalid-value",
75499
+ message: `Ignored invalid value in ${path}: ${entry.key}: ${entry.message}`,
75500
+ path,
75501
+ issuePaths: [entry.key]
75502
+ }));
75503
+ }
75504
+ function unrecognizedKeyIssues(issues) {
75505
+ return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
75506
+ }
75507
+ function isRecord11(value) {
75508
+ return typeof value === "object" && value !== null && !Array.isArray(value);
75509
+ }
75510
+ function sanitizeUnsafeKeys(value, path = []) {
75511
+ if (Array.isArray(value)) {
75512
+ const issues = [];
75513
+ const sanitized = value.map((entry, index) => {
75514
+ const nested = sanitizeUnsafeKeys(entry, [...path, String(index)]);
75515
+ issues.push(...nested.issues);
75516
+ return nested.value;
75517
+ });
75518
+ return { issues, value: sanitized };
75519
+ }
75520
+ if (!isRecord11(value))
75521
+ return { issues: [], value };
75522
+ const issues = [];
75523
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
75524
+ issues.push({ keys: ["__proto__"], path });
75525
+ }
75526
+ const sanitized = {};
75527
+ for (const [key, entry] of Object.entries(value)) {
75528
+ if (isUnsafeObjectKey2(key)) {
75529
+ issues.push({ keys: [key], path });
75530
+ continue;
75531
+ }
75532
+ const nested = sanitizeUnsafeKeys(entry, [...path, key]);
75533
+ issues.push(...nested.issues);
75534
+ sanitized[key] = nested.value;
75535
+ }
75536
+ return { issues, value: sanitized };
75537
+ }
75538
+ function containerAt(record, path) {
75539
+ let node = record;
75540
+ for (const segment of path) {
75541
+ if (Array.isArray(node)) {
75542
+ const index = Number(segment);
75543
+ if (!Number.isInteger(index) || index < 0 || index >= node.length)
75544
+ return null;
75545
+ node = node[index];
75546
+ } else if (isRecord11(node) && Object.hasOwn(node, segment)) {
75547
+ node = node[segment];
75548
+ } else {
75549
+ return null;
75550
+ }
75551
+ }
75552
+ return isRecord11(node) ? node : null;
75553
+ }
75554
+ function stripUnrecognizedKeys(record, issues) {
75555
+ const stripped = structuredClone(record);
75556
+ const issuePaths = [];
75557
+ for (const issue of issues) {
75558
+ const container = containerAt(stripped, issue.path);
75559
+ if (container === null)
75560
+ continue;
75561
+ for (const key of issue.keys) {
75562
+ delete container[key];
75563
+ issuePaths.push([...issue.path, key].join("."));
75564
+ }
75565
+ }
75566
+ return { issuePaths, stripped };
75567
+ }
75568
+ function toRecord(value) {
75569
+ if (value === null || typeof value !== "object" || Array.isArray(value))
75570
+ return null;
75571
+ const record = {};
75572
+ for (const [key, entry] of Object.entries(value)) {
75573
+ record[key] = entry;
75574
+ }
75575
+ return record;
75576
+ }
75577
+ function validateConfigLayer(path, data) {
75578
+ const sanitized = sanitizeUnsafeKeys(data);
75579
+ const record = toRecord(sanitized.value);
75580
+ const unsafeIssuePaths = sanitized.issues.flatMap((issue) => issue.keys.map((key) => [...issue.path, key].join(".")));
75581
+ const unsafeDiagnostics = unsafeIssuePaths.length === 0 ? [] : [{ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${unsafeIssuePaths.join(", ")}`, path, issuePaths: unsafeIssuePaths }];
75582
+ const validation = OmoConfigLayerSchema.safeParse(sanitized.value);
75583
+ if (validation.success) {
75584
+ if (record !== null)
75585
+ return { loaded: true, diagnostics: unsafeDiagnostics, value: record };
75586
+ return {
75587
+ loaded: false,
75588
+ diagnostics: [{ kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path }]
75589
+ };
75590
+ }
75591
+ const rejected = { loaded: false, diagnostics: [validationDiagnostic(path, validation.error.issues)] };
75592
+ const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
75593
+ if (record === null)
75594
+ return rejected;
75595
+ let candidate = record;
75596
+ let issues = validation.error.issues;
75597
+ const diagnostics = [...unsafeDiagnostics];
75598
+ if (unknownIssues.length > 0) {
75599
+ const { issuePaths, stripped } = stripUnrecognizedKeys(record, unknownIssues);
75600
+ if (issuePaths.length > 0) {
75601
+ diagnostics.push({ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`, path, issuePaths });
75602
+ }
75603
+ const strippedValidation = validateLayerRecord(stripped);
75604
+ if (strippedValidation.success)
75605
+ return { loaded: true, diagnostics, value: stripped };
75606
+ candidate = stripped;
75607
+ issues = strippedValidation.issues;
75608
+ }
75609
+ const pruned = pruneInvalidConfigPaths(candidate, issues, validateLayerRecord);
75610
+ if (!pruned.ok)
75611
+ return rejected;
75612
+ return { loaded: true, diagnostics: [...diagnostics, ...invalidValueDiagnostics(path, pruned.dropped)], value: pruned.config };
75613
+ }
75614
+ var validateLayerRecord = (record) => {
75615
+ const parsed = OmoConfigLayerSchema.safeParse(record);
75616
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
75617
+ };
75618
+ var init_layer_validation = __esm(() => {
75619
+ init_schema();
75620
+ init_merge();
75621
+ });
75622
+
75293
75623
  // packages/omo-config-core/src/internal/posix-path.ts
75294
75624
  function toPosixPath(path) {
75295
75625
  return path.split("\\").join("/");
75296
75626
  }
75297
75627
  var init_posix_path = () => {};
75298
75628
 
75299
- // packages/omo-config-core/src/loader/types.ts
75300
- import { existsSync as existsSync13, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
75301
- var DEFAULT_READ_FILE_SYSTEM;
75302
- var init_types3 = __esm(() => {
75303
- DEFAULT_READ_FILE_SYSTEM = {
75304
- existsSync: existsSync13,
75305
- lstatSync: lstatSync2,
75306
- readFileSync: readFileSync5,
75307
- realpathSync: realpathSync5
75308
- };
75309
- });
75310
-
75311
75629
  // packages/omo-config-core/src/loader/paths.ts
75312
75630
  import { userInfo } from "os";
75313
75631
  import { dirname as dirname4, join as join15, posix as posix2, resolve as resolve3 } from "path";
@@ -75411,7 +75729,7 @@ function resolveOmoProfileName(options = {}) {
75411
75729
  const env = options.env ?? process.env;
75412
75730
  return profileName(options.profile) ?? profileName(env["OMO_PROFILE"]) ?? profileName(env["OCX_PROFILE"]) ?? profileNameFromOpenCodeConfigDir(env["OPENCODE_CONFIG_DIR"]);
75413
75731
  }
75414
- function toRecord(value) {
75732
+ function toRecord2(value) {
75415
75733
  if (value === null || typeof value !== "object" || Array.isArray(value))
75416
75734
  return;
75417
75735
  return Object.fromEntries(Object.entries(value));
@@ -75432,13 +75750,13 @@ function harnessLayer(config, harness) {
75432
75750
  const legacyKeys = Object.entries(OMO_CONFIG_LEGACY_HARNESS_ALIASES).filter(([, target]) => target === canonical).map(([legacy]) => harnessBlockKey(legacy));
75433
75751
  let layer = {};
75434
75752
  for (const key of [...legacyKeys, harnessBlockKey(canonical)]) {
75435
- layer = mergeOmoConfigRecords(layer, toRecord(config[key]) ?? {});
75753
+ layer = mergeOmoConfigRecords(layer, toRecord2(config[key]) ?? {});
75436
75754
  }
75437
75755
  return layer;
75438
75756
  }
75439
75757
  function resolveOmoConfigView(options) {
75440
- const profiles = toRecord(options.config["profiles"]);
75441
- const profile = options.profile === undefined ? undefined : toRecord(profiles?.[options.profile]);
75758
+ const profiles = toRecord2(options.config["profiles"]);
75759
+ const profile = options.profile === undefined ? undefined : toRecord2(profiles?.[options.profile]);
75442
75760
  const diagnostics = profile === undefined && options.profile !== undefined ? [{
75443
75761
  kind: "profile",
75444
75762
  message: `Activated omo profile "${options.profile}" does not exist; using the base configuration`,
@@ -75493,70 +75811,9 @@ function stripResolutionControlKeys(config) {
75493
75811
  } = config;
75494
75812
  return resolved;
75495
75813
  }
75496
- function validationDiagnostic(path, issues) {
75497
- const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
75498
- return {
75499
- kind: "validation",
75500
- message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
75501
- path,
75502
- issuePaths
75503
- };
75504
- }
75505
- function unrecognizedKeyIssues(issues) {
75506
- return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
75507
- }
75508
- function hasUnsafeUnrecognizedKey(issues) {
75509
- return issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey2(key)));
75510
- }
75511
- function hasTamperedPrototype(value) {
75512
- if (Array.isArray(value))
75513
- return value.some((entry) => hasTamperedPrototype(entry));
75514
- if (!isRecord11(value))
75515
- return false;
75516
- const prototype = Object.getPrototypeOf(value);
75517
- if (prototype !== Object.prototype && prototype !== null)
75518
- return true;
75519
- return Object.values(value).some((entry) => hasTamperedPrototype(entry));
75520
- }
75521
- function isRecord11(value) {
75522
- return typeof value === "object" && value !== null && !Array.isArray(value);
75523
- }
75524
- function containerAt(record, path) {
75525
- let container = record;
75526
- for (const segment of path) {
75527
- const next = container[segment];
75528
- if (!isRecord11(next))
75529
- return null;
75530
- container = next;
75531
- }
75532
- return container;
75533
- }
75534
- function stripUnrecognizedKeys(record, issues) {
75535
- const stripped = structuredClone(record);
75536
- const issuePaths = [];
75537
- for (const issue of issues) {
75538
- const container = containerAt(stripped, issue.path);
75539
- if (container === null)
75540
- continue;
75541
- for (const key of issue.keys) {
75542
- delete container[key];
75543
- issuePaths.push([...issue.path, key].join("."));
75544
- }
75545
- }
75546
- return { issuePaths, stripped };
75547
- }
75548
- function toRecord2(value) {
75549
- if (value === null || typeof value !== "object" || Array.isArray(value))
75550
- return null;
75551
- const record = {};
75552
- for (const [key, entry] of Object.entries(value)) {
75553
- record[key] = entry;
75554
- }
75555
- return record;
75556
- }
75557
75814
  function readConfigSource(path, scope, fileSystem) {
75558
75815
  if (!fileSystem.existsSync(path)) {
75559
- return { source: { exists: false, loaded: false, path, scope } };
75816
+ return { diagnostics: [], source: { exists: false, loaded: false, path, scope } };
75560
75817
  }
75561
75818
  let content;
75562
75819
  try {
@@ -75564,68 +75821,23 @@ function readConfigSource(path, scope, fileSystem) {
75564
75821
  } catch (error) {
75565
75822
  const message = error instanceof Error ? error.message : String(error);
75566
75823
  return {
75567
- diagnostic: { kind: "read", message: `Failed to read ${path}: ${message}`, path },
75824
+ diagnostics: [{ kind: "read", message: `Failed to read ${path}: ${message}`, path }],
75568
75825
  source: { exists: true, loaded: false, path, scope }
75569
75826
  };
75570
75827
  }
75571
75828
  const parsed = parseJsoncSafe2(content);
75572
75829
  if (parsed.errors.length > 0) {
75573
75830
  return {
75574
- diagnostic: {
75831
+ diagnostics: [{
75575
75832
  kind: "parse",
75576
75833
  message: `JSONC parse error in ${path}: ${parsed.errors.map((error) => error.message).join(", ")}`,
75577
75834
  path
75578
- },
75579
- source: { exists: true, loaded: false, path, scope }
75580
- };
75581
- }
75582
- if (hasTamperedPrototype(parsed.data)) {
75583
- return {
75584
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: "__proto__" member is not allowed`, path },
75585
- source: { exists: true, loaded: false, path, scope }
75586
- };
75587
- }
75588
- const parsedRecord = toRecord2(parsed.data);
75589
- const validation = OmoConfigLayerSchema.safeParse(parsed.data);
75590
- if (!validation.success) {
75591
- const unrecognized = unrecognizedKeyIssues(validation.error.issues);
75592
- if (hasUnsafeUnrecognizedKey(unrecognized)) {
75593
- return {
75594
- diagnostic: validationDiagnostic(path, validation.error.issues),
75595
- source: { exists: true, loaded: false, path, scope }
75596
- };
75597
- }
75598
- const rejected = {
75599
- diagnostic: validationDiagnostic(path, validation.error.issues),
75835
+ }],
75600
75836
  source: { exists: true, loaded: false, path, scope }
75601
75837
  };
75602
- const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
75603
- if (parsedRecord === null || unknownIssues.length === 0)
75604
- return rejected;
75605
- const { issuePaths, stripped } = stripUnrecognizedKeys(parsedRecord, unknownIssues);
75606
- if (!OmoConfigLayerSchema.safeParse(stripped).success)
75607
- return rejected;
75608
- return {
75609
- diagnostic: {
75610
- kind: "unknown-keys",
75611
- message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`,
75612
- path,
75613
- issuePaths
75614
- },
75615
- source: { exists: true, loaded: true, path, scope },
75616
- value: stripped
75617
- };
75618
75838
  }
75619
- if (parsedRecord === null) {
75620
- return {
75621
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path },
75622
- source: { exists: true, loaded: false, path, scope }
75623
- };
75624
- }
75625
- return {
75626
- source: { exists: true, loaded: true, path, scope },
75627
- value: parsedRecord
75628
- };
75839
+ const layer = validateConfigLayer(path, parsed.data);
75840
+ 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 } };
75629
75841
  }
75630
75842
  function legacyCategoryDiagnostic(path, renames) {
75631
75843
  const detail = renames.map((rename) => rename.dropped ? `${rename.path} ignored because ${rename.canonical} is also configured` : `${rename.path} renamed to ${rename.canonical}`).join(", ");
@@ -75660,8 +75872,7 @@ function loadOmoConfig(options = {}) {
75660
75872
  })) {
75661
75873
  const loaded = readConfigSource(candidate.path, candidate.scope, fileSystem);
75662
75874
  sources.push(loaded.source);
75663
- if (loaded.diagnostic !== undefined)
75664
- diagnostics.push(loaded.diagnostic);
75875
+ diagnostics.push(...loaded.diagnostics);
75665
75876
  if (loaded.value !== undefined) {
75666
75877
  const canonicalized = canonicalizeLegacyCategoryNames(loaded.value);
75667
75878
  if (canonicalized.renames.length > 0) {
@@ -75684,7 +75895,8 @@ function loadOmoConfig(options = {}) {
75684
75895
  ...options.harness === undefined ? {} : { harness: options.harness },
75685
75896
  ...requestedProfile === undefined ? {} : { profile: requestedProfile }
75686
75897
  });
75687
- const finalConfig = OmoConfigSchema.safeParse(mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config));
75898
+ const finalInput = mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config);
75899
+ const finalConfig = OmoConfigSchema.safeParse(finalInput);
75688
75900
  if (finalConfig.success) {
75689
75901
  return {
75690
75902
  config: stripResolutionControlKeys(finalConfig.data),
@@ -75694,9 +75906,22 @@ function loadOmoConfig(options = {}) {
75694
75906
  sources
75695
75907
  };
75696
75908
  }
75909
+ const pruned = pruneInvalidConfigPaths(finalInput, finalConfig.error.issues, (record) => {
75910
+ const parsed = OmoConfigSchema.safeParse(record);
75911
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
75912
+ });
75913
+ if (pruned.ok) {
75914
+ return {
75915
+ config: stripResolutionControlKeys(OmoConfigSchema.parse(pruned.config)),
75916
+ diagnostics: [...diagnostics, ...resolved.diagnostics, ...invalidValueDiagnostics(MERGED_OMO_CONFIG_PATH, pruned.dropped)],
75917
+ layers,
75918
+ ...resolved.profile === undefined ? {} : { profile: resolved.profile },
75919
+ sources
75920
+ };
75921
+ }
75697
75922
  return {
75698
75923
  config: stripResolutionControlKeys(OmoConfigSchema.parse(DEFAULT_RAW_CONFIG)),
75699
- diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic("(merged omo config)", finalConfig.error.issues)],
75924
+ diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic(MERGED_OMO_CONFIG_PATH, finalConfig.error.issues)],
75700
75925
  layers,
75701
75926
  ...resolved.profile === undefined ? {} : { profile: resolved.profile },
75702
75927
  sources
@@ -75706,6 +75931,7 @@ var DEFAULT_RAW_CONFIG;
75706
75931
  var init_loader2 = __esm(() => {
75707
75932
  init_main();
75708
75933
  init_schema();
75934
+ init_layer_validation();
75709
75935
  init_merge();
75710
75936
  init_paths();
75711
75937
  init_resolution();
@@ -75720,6 +75946,7 @@ var init_loader2 = __esm(() => {
75720
75946
 
75721
75947
  // packages/omo-config-core/src/loader/index.ts
75722
75948
  var init_loader3 = __esm(() => {
75949
+ init_diagnostic_lines();
75723
75950
  init_disabled_skills();
75724
75951
  init_loader2();
75725
75952
  init_merge();
@@ -89150,7 +89377,7 @@ var package_default2;
89150
89377
  var init_package2 = __esm(() => {
89151
89378
  package_default2 = {
89152
89379
  name: "@oh-my-opencode/omo-codex",
89153
- version: "5.1.4",
89380
+ version: "5.1.5",
89154
89381
  type: "module",
89155
89382
  private: true,
89156
89383
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -102151,6 +102378,7 @@ async function processEvents(ctx, stream, state) {
102151
102378
  }
102152
102379
  }
102153
102380
  // packages/omo-opencode/src/config/validate.ts
102381
+ init_src4();
102154
102382
  import { relative as relative7 } from "path";
102155
102383
 
102156
102384
  // packages/omo-opencode/src/shared/disabled-providers.ts
@@ -102271,11 +102499,11 @@ function baseView(config) {
102271
102499
  }
102272
102500
  return result;
102273
102501
  }
102274
- function appendViews(views, layers, select) {
102502
+ function appendViews(views, layers, select, keyPrefix) {
102275
102503
  for (const layer of layers) {
102276
102504
  const config = select(layer.config);
102277
102505
  if (Object.keys(config).length > 0)
102278
- views.push({ config, path: layer.source.path });
102506
+ views.push({ config, keyPrefix, path: layer.source.path });
102279
102507
  }
102280
102508
  }
102281
102509
  function recordFields(value, fields) {
@@ -102304,10 +102532,21 @@ function modelInput(view) {
102304
102532
  };
102305
102533
  }
102306
102534
  function modelView(view) {
102307
- const parsed = OmoConfigSchema.safeParse(modelInput(view));
102308
- if (!parsed.success)
102535
+ const input = modelInput(view);
102536
+ const parsed = OmoConfigSchema.safeParse(input);
102537
+ const modelConfig = parsed.success ? parsed.data : (() => {
102538
+ const pruned = pruneInvalidConfigPaths(input, parsed.error.issues, (record) => {
102539
+ const validation = OmoConfigSchema.safeParse(record);
102540
+ return validation.success ? { success: true } : { success: false, issues: validation.error.issues };
102541
+ });
102542
+ if (!pruned.ok)
102543
+ return;
102544
+ const validation = OmoConfigSchema.safeParse(pruned.config);
102545
+ return validation.success ? validation.data : undefined;
102546
+ })();
102547
+ if (modelConfig === undefined)
102309
102548
  return { config: {}, diagnostics: [] };
102310
- const resolved = resolveModelReferences(parsed.data);
102549
+ const resolved = resolveModelReferences(modelConfig);
102311
102550
  return {
102312
102551
  config: {
102313
102552
  ...resolved.view.agents === undefined ? {} : { agents: resolved.view.agents },
@@ -102326,14 +102565,15 @@ function loadOmoOpenCodeConfigChain(directory, environment = process.env) {
102326
102565
  });
102327
102566
  const views = [];
102328
102567
  const selectedProfile = loaded.profile;
102329
- appendViews(views, loaded.layers, baseView);
102330
- appendViews(views, loaded.layers, block2);
102331
- appendViews(views, loaded.layers, (config) => baseView(profile(config, selectedProfile)));
102332
- appendViews(views, loaded.layers, (config) => block2(profile(config, selectedProfile)));
102568
+ const profilePrefix = selectedProfile === undefined ? "" : `profiles.${selectedProfile}.`;
102569
+ appendViews(views, loaded.layers, baseView, "");
102570
+ appendViews(views, loaded.layers, block2, "[opencode].");
102571
+ appendViews(views, loaded.layers, (config) => baseView(profile(config, selectedProfile)), profilePrefix);
102572
+ appendViews(views, loaded.layers, (config) => block2(profile(config, selectedProfile)), `${profilePrefix}[opencode].`);
102333
102573
  const resolvedModels = modelView(resolved.config);
102334
102574
  if (Object.keys(resolvedModels.config).length > 0) {
102335
102575
  const source = loaded.layers[0]?.source.path ?? directory;
102336
- views.push({ config: resolvedModels.config, path: source });
102576
+ views.push({ config: resolvedModels.config, keyPrefix: "", path: source });
102337
102577
  }
102338
102578
  let protectedUserView = {};
102339
102579
  for (const layer of loaded.layers) {
@@ -102420,7 +102660,7 @@ function unknownPaths(schema, value, path) {
102420
102660
  if (def.type === "object" && isPlainRecord5(value) && def.shape !== undefined) {
102421
102661
  const catchall = def.catchall !== undefined && definition(def.catchall).type !== "never" ? def.catchall : undefined;
102422
102662
  return Object.entries(value).flatMap(([key, entry]) => {
102423
- const propertySchema = def.shape?.[key];
102663
+ const propertySchema = def.shape !== undefined && Object.hasOwn(def.shape, key) ? def.shape[key] : undefined;
102424
102664
  if (propertySchema !== undefined)
102425
102665
  return unknownPaths(propertySchema, entry, [...path, key]);
102426
102666
  if (catchall !== undefined)
@@ -102458,6 +102698,9 @@ function findUnknownKeyPaths(schema, value) {
102458
102698
  return unknownPaths(schema, value, []);
102459
102699
  }
102460
102700
 
102701
+ // packages/omo-opencode/src/config/prune-plugin-view.ts
102702
+ init_src4();
102703
+
102461
102704
  // packages/omo-opencode/src/config/schema/agent-names.ts
102462
102705
  init_zod();
102463
102706
  var BuiltinAgentNameSchema = _enum([
@@ -102535,19 +102778,19 @@ var FallbackModelsSchema = union([
102535
102778
 
102536
102779
  // packages/omo-opencode/src/config/schema/internal/permission.ts
102537
102780
  init_zod();
102538
- var PermissionValueSchema = _enum(["ask", "allow", "deny"]);
102539
- var BashPermissionSchema = union([
102540
- PermissionValueSchema,
102541
- record(string2(), PermissionValueSchema)
102781
+ var PermissionValueSchema2 = _enum(["ask", "allow", "deny"]);
102782
+ var BashPermissionSchema2 = union([
102783
+ PermissionValueSchema2,
102784
+ record(string2(), PermissionValueSchema2)
102542
102785
  ]);
102543
102786
  var AgentPermissionSchema = object({
102544
- edit: PermissionValueSchema.optional(),
102545
- bash: BashPermissionSchema.optional(),
102546
- webfetch: PermissionValueSchema.optional(),
102547
- task: PermissionValueSchema.optional(),
102548
- doom_loop: PermissionValueSchema.optional(),
102549
- external_directory: PermissionValueSchema.optional()
102550
- }).catchall(PermissionValueSchema.optional());
102787
+ edit: PermissionValueSchema2.optional(),
102788
+ bash: BashPermissionSchema2.optional(),
102789
+ webfetch: PermissionValueSchema2.optional(),
102790
+ task: PermissionValueSchema2.optional(),
102791
+ doom_loop: PermissionValueSchema2.optional(),
102792
+ external_directory: PermissionValueSchema2.optional()
102793
+ }).catchall(PermissionValueSchema2.optional());
102551
102794
 
102552
102795
  // packages/omo-opencode/src/config/schema/agent-overrides.ts
102553
102796
  var AgentOverrideConfigSchema = object({
@@ -103112,6 +103355,26 @@ var OhMyOpenCodeConfigSchema = object({
103112
103355
  default_mode: DefaultModeConfigSchema.optional(),
103113
103356
  _migrations: array(string2()).optional()
103114
103357
  });
103358
+ // packages/omo-opencode/src/config/prune-plugin-view.ts
103359
+ var validatePluginRecord = (record) => {
103360
+ const parsed = OhMyOpenCodeConfigSchema.safeParse(record);
103361
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
103362
+ };
103363
+ function prunePluginView(input) {
103364
+ const validation = OhMyOpenCodeConfigSchema.safeParse(input.config);
103365
+ if (validation.success)
103366
+ return { config: input.config, issues: [], warnings: [] };
103367
+ const pruned = pruneInvalidConfigPaths(input.config, validation.error.issues, validatePluginRecord);
103368
+ if (!pruned.ok)
103369
+ return { config: input.config, issues: validation.error.issues, warnings: [] };
103370
+ const file = displayOmoConfigPath(input.path, input.homeDir);
103371
+ return {
103372
+ config: pruned.config,
103373
+ issues: [],
103374
+ warnings: pruned.dropped.map((entry) => `config: ${file}: ${input.keyPrefix}${entry.key} ignored (invalid value)`)
103375
+ };
103376
+ }
103377
+
103115
103378
  // packages/omo-opencode/src/config/validate.ts
103116
103379
  function shortPath(configPath) {
103117
103380
  const candidate = relative7(process.cwd(), configPath);
@@ -103121,9 +103384,8 @@ function formatIssuePath(path) {
103121
103384
  const formatted = path.map((segment) => String(segment)).join(".");
103122
103385
  return formatted.length > 0 ? formatted : "<root>";
103123
103386
  }
103124
- function schemaMessages(configPath, rawConfig) {
103125
- const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
103126
- const validationMessages = result.success ? [] : result.error.issues.map((issue) => `${shortPath(configPath)}: ${formatIssuePath(issue.path)}: ${issue.message}`);
103387
+ function schemaMessages(configPath, rawConfig, issues) {
103388
+ const validationMessages = issues.map((issue) => `${shortPath(configPath)}: ${formatIssuePath(issue.path)}: ${issue.message}`);
103127
103389
  const unknownKeyMessages = findUnknownKeyPaths(OhMyOpenCodeConfigSchema, rawConfig).map((path) => `${shortPath(configPath)}: Unknown config key: ${formatIssuePath(path)}`);
103128
103390
  return [...validationMessages, ...unknownKeyMessages];
103129
103391
  }
@@ -103139,11 +103401,13 @@ function parseConfig(rawConfig) {
103139
103401
  }
103140
103402
  return config;
103141
103403
  }
103142
- function parseConfigView(path, rawConfig) {
103404
+ function parseConfigView(view, homeDir) {
103405
+ const pruned = prunePluginView({ config: view.config, homeDir, keyPrefix: view.keyPrefix, path: view.path });
103143
103406
  return {
103144
- config: parseConfig(rawConfig),
103145
- messages: schemaMessages(path, rawConfig),
103146
- path
103407
+ config: parseConfig(pruned.config),
103408
+ messages: schemaMessages(view.path, view.config, pruned.issues),
103409
+ path: view.path,
103410
+ warnings: pruned.warnings
103147
103411
  };
103148
103412
  }
103149
103413
  function mergeViews(views) {
@@ -103235,18 +103499,25 @@ function migrateRalphLoopConfig(config) {
103235
103499
  }
103236
103500
  function validatePluginConfig(directory, environment = process.env) {
103237
103501
  const chain = loadOmoOpenCodeConfigChain(directory, environment);
103238
- const views = chain.views.map((view) => parseConfigView(view.path, view.config));
103239
- const chainMessages = chain.diagnostics.filter((diagnostic) => diagnostic.kind !== "deprecated-keys").map((diagnostic) => `${shortPath(diagnostic.path)}: ${diagnostic.message}`);
103502
+ const homeDir = environment.HOME ?? environment.USERPROFILE;
103503
+ const views = chain.views.map((view) => parseConfigView(view, homeDir));
103504
+ const failing = chain.diagnostics.filter((diagnostic) => diagnostic.kind !== "deprecated-keys" && diagnostic.kind !== "invalid-value");
103505
+ const chainMessages = failing.map((diagnostic) => `${shortPath(diagnostic.path)}: ${diagnostic.message}`);
103240
103506
  const messages = [...chainMessages, ...views.flatMap((view) => view.messages)];
103507
+ const warnings = [
103508
+ ...omoConfigDiagnosticLines(chain.diagnostics.flatMap((diagnostic) => diagnostic.kind === "invalid-value" ? [diagnostic] : []), { homeDir }),
103509
+ ...views.flatMap((view) => view.warnings)
103510
+ ];
103241
103511
  const firstFailingView = views.find((view) => view.messages.length > 0);
103242
103512
  const firstView = views[0];
103243
- const userConfig = parseConfig(chain.protectedUserView);
103513
+ const userConfig = parseConfig(prunePluginView({ config: chain.protectedUserView, homeDir, keyPrefix: "", path: "" }).config);
103244
103514
  const config = applyDisabledProviders(materializeAgentModelChains(protectUserFields(mergeViews(views), userConfig)));
103245
103515
  warnLegacyUlwExecuteKey(chain.views);
103246
103516
  return {
103247
103517
  valid: messages.length === 0,
103248
103518
  messages,
103249
- path: chainMessages.length > 0 ? chain.diagnostics[0]?.path ?? null : firstFailingView?.path ?? firstView?.path ?? null,
103519
+ warnings,
103520
+ path: failing[0]?.path ?? firstFailingView?.path ?? firstView?.path ?? null,
103250
103521
  config: migrateRalphLoopConfig(migrateLegacyUlwExecuteKey(config))
103251
103522
  };
103252
103523
  }
@@ -106370,14 +106641,15 @@ function toOmoConfig(config) {
106370
106641
  function validateConfig() {
106371
106642
  const validation = validatePluginConfig(process.cwd());
106372
106643
  if (!validation.path) {
106373
- return { exists: false, path: null, valid: true, config: null, errors: [] };
106644
+ return { exists: false, path: null, valid: true, config: null, errors: [], warnings: validation.warnings };
106374
106645
  }
106375
106646
  return {
106376
106647
  exists: true,
106377
106648
  path: validation.path,
106378
106649
  valid: validation.valid,
106379
106650
  config: toOmoConfig(validation.config),
106380
- errors: [...validation.messages]
106651
+ errors: [...validation.messages],
106652
+ warnings: validation.warnings
106381
106653
  };
106382
106654
  }
106383
106655
  function collectModelResolutionIssues(config) {
@@ -106430,6 +106702,12 @@ async function checkConfig() {
106430
106702
  }));
106431
106703
  if (legacyWarning !== undefined)
106432
106704
  issues.push(legacyWarning);
106705
+ issues.push(...validation.warnings.map((warning) => ({
106706
+ title: warning,
106707
+ description: "The rest of that file still applies; fix or remove this value to use it.",
106708
+ severity: "warning",
106709
+ affects: ["configuration"]
106710
+ })));
106433
106711
  if (!validation.exists) {
106434
106712
  return {
106435
106713
  name: CHECK_NAMES[CHECK_IDS.CONFIG],