lazycodex-ai 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 (54) hide show
  1. package/dist/cli/index.js +464 -186
  2. package/dist/cli-node/index.js +464 -186
  3. package/package.json +1 -1
  4. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  5. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +309 -133
  6. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  7. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  8. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  9. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  10. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  11. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  12. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  13. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  14. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  15. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  16. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  18. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  19. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  20. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  22. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  24. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  28. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  29. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  30. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  31. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  32. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  33. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  34. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  35. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  36. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  37. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  38. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  50. package/packages/omo-codex/plugin/package-lock.json +12 -12
  51. package/packages/omo-codex/plugin/package.json +1 -1
  52. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  53. package/packages/omo-codex/scripts/install-dist/install-local.mjs +311 -135
  54. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // omo-codex-install:3946ebe75677b0182b2bd330aef6e28d80b0a14b4f2bef7d2cf926cc141467f0:91bb802279b3e0a38644491e221a425a04ca6cf2aec0cb4dbf8c9be3ce4bf8f5
2
+ // omo-codex-install:76bfd9555dc0f5ee104bb34f42f75e7c52948d77356eed6b57e7b57738df399f:1745be8f0e5a6fbaa5f97e2df68fa6cecf8bb9b22d612762cf912b589505c6b7
3
3
  var __esm = (fn, res, err) => () => {
4
4
  if (fn)
5
5
  try {
@@ -9984,7 +9984,7 @@ var package_default;
9984
9984
  var init_package = __esm(() => {
9985
9985
  package_default = {
9986
9986
  name: "@oh-my-opencode/omo-codex",
9987
- version: "5.1.4",
9987
+ version: "5.1.5",
9988
9988
  type: "module",
9989
9989
  private: true,
9990
9990
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -19252,7 +19252,6 @@ function normalizeLegacyModelFields(entry) {
19252
19252
  delete normalized["reasoningEffort"];
19253
19253
  delete normalized["thinking"];
19254
19254
  delete normalized["textVerbosity"];
19255
- delete normalized["maxTokens"];
19256
19255
  delete normalized["providerOptions"];
19257
19256
  if (typeof entry["model"] === "string")
19258
19257
  normalized["model"] = canonicalModelString(entry["model"]);
@@ -19270,10 +19269,15 @@ function normalizeLegacyModelFields(entry) {
19270
19269
  providerOptions["textVerbosity"] = entry["textVerbosity"];
19271
19270
  if (Object.keys(providerOptions).length > 0)
19272
19271
  normalized["provider_options"] = providerOptions;
19273
- if (entry["max_tokens"] !== undefined)
19272
+ if (entry["max_tokens"] !== undefined) {
19274
19273
  normalized["max_tokens"] = entry["max_tokens"];
19275
- else if (entry["maxTokens"] !== undefined)
19274
+ if (entry["maxTokens"] === undefined || typeof entry["maxTokens"] === "number") {
19275
+ delete normalized["maxTokens"];
19276
+ }
19277
+ } else if (typeof entry["maxTokens"] === "number") {
19276
19278
  normalized["max_tokens"] = entry["maxTokens"];
19279
+ delete normalized["maxTokens"];
19280
+ }
19277
19281
  return normalized;
19278
19282
  }
19279
19283
  var OmoLegacyFallbackModelObjectInputSchema = object({
@@ -19303,6 +19307,19 @@ function isRecord3(value) {
19303
19307
  return typeof value === "object" && value !== null && !Array.isArray(value);
19304
19308
  }
19305
19309
  var OmoAgentModelEntrySchema = union([string2(), OmoFallbackModelObjectSchema]);
19310
+ var PermissionValueSchema = _enum(["ask", "allow", "deny"]);
19311
+ var BashPermissionSchema = union([
19312
+ PermissionValueSchema,
19313
+ record(string2(), PermissionValueSchema)
19314
+ ]);
19315
+ var OmoAgentPermissionSchema = object({
19316
+ edit: PermissionValueSchema.optional(),
19317
+ bash: BashPermissionSchema.optional(),
19318
+ webfetch: PermissionValueSchema.optional(),
19319
+ task: PermissionValueSchema.optional(),
19320
+ doom_loop: PermissionValueSchema.optional(),
19321
+ external_directory: PermissionValueSchema.optional()
19322
+ }).catchall(PermissionValueSchema.optional());
19306
19323
  var OmoAgentDefInputSchema = object({
19307
19324
  description: string2().optional(),
19308
19325
  prompt: string2().optional(),
@@ -19319,7 +19336,9 @@ var OmoAgentDefInputSchema = object({
19319
19336
  disallowed_tools: array(string2()).optional(),
19320
19337
  max_turns: number2().int().nonnegative().optional(),
19321
19338
  temperature: number2().min(0).max(2).optional(),
19322
- disable: boolean2().optional()
19339
+ disable: boolean2().optional(),
19340
+ permission: OmoAgentPermissionSchema.optional(),
19341
+ prompt_append: string2().optional()
19323
19342
  }).strict();
19324
19343
  var OmoAgentDefSchema = preprocess((value) => isRecord3(value) ? normalizeLegacyModelFields(value) : value, OmoAgentDefInputSchema);
19325
19344
  var OmoAgentsConfigSchema = record(string2(), OmoAgentDefSchema);
@@ -20008,6 +20027,16 @@ function canonicalizeLegacyHarnessBlocks(document) {
20008
20027
  return { document: canonicalized, renames };
20009
20028
  }
20010
20029
 
20030
+ // packages/omo-config-core/src/loader/types.ts
20031
+ import { existsSync as existsSync3, lstatSync, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
20032
+ var MERGED_OMO_CONFIG_PATH = "(merged omo config)";
20033
+ var DEFAULT_READ_FILE_SYSTEM = {
20034
+ existsSync: existsSync3,
20035
+ lstatSync,
20036
+ readFileSync: readFileSync2,
20037
+ realpathSync: realpathSync2
20038
+ };
20039
+
20011
20040
  // node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
20012
20041
  function createScanner(text, ignoreTrivia = false) {
20013
20042
  const len = text.length;
@@ -20882,6 +20911,255 @@ function mergeOmoConfigRecords(base, override) {
20882
20911
  return result;
20883
20912
  }
20884
20913
 
20914
+ // packages/omo-config-core/src/loader/prune-invalid-leaves.ts
20915
+ var MAX_PRUNE_PASSES = 32;
20916
+ function isContainer(value) {
20917
+ return typeof value === "object" && value !== null;
20918
+ }
20919
+ function childOf(container, segment) {
20920
+ return Array.isArray(container) ? container[Number(segment)] : container[String(segment)];
20921
+ }
20922
+ function hasChild(container, segment) {
20923
+ if (Array.isArray(container)) {
20924
+ const index = Number(segment);
20925
+ return Number.isInteger(index) && index >= 0 && index < container.length;
20926
+ }
20927
+ return Object.hasOwn(container, String(segment));
20928
+ }
20929
+ function segmentsOf(path) {
20930
+ return path.map((segment) => typeof segment === "number" ? segment : String(segment));
20931
+ }
20932
+ function targetPaths(root, issue) {
20933
+ const base = segmentsOf(issue.path);
20934
+ if (issue.code === "unrecognized_keys")
20935
+ return issue.keys.map((key) => [...base, key]);
20936
+ let node = root;
20937
+ let depth = 0;
20938
+ for (const segment of base) {
20939
+ if (!isContainer(node) || !hasChild(node, segment))
20940
+ break;
20941
+ node = childOf(node, segment);
20942
+ depth += 1;
20943
+ }
20944
+ return [base.slice(0, depth)];
20945
+ }
20946
+ function isAncestor(ancestor, path) {
20947
+ return ancestor.length < path.length && ancestor.every((segment, index) => segment === path[index]);
20948
+ }
20949
+ function compareDescending(left, right) {
20950
+ const length = Math.min(left.length, right.length);
20951
+ for (let index = 0;index < length; index += 1) {
20952
+ const a = left[index];
20953
+ const b = right[index];
20954
+ if (a === b)
20955
+ continue;
20956
+ if (typeof a === "number" && typeof b === "number")
20957
+ return b - a;
20958
+ return String(b).localeCompare(String(a));
20959
+ }
20960
+ return right.length - left.length;
20961
+ }
20962
+ function removeChild(container, segment) {
20963
+ if (Array.isArray(container))
20964
+ container.splice(Number(segment), 1);
20965
+ else
20966
+ delete container[String(segment)];
20967
+ }
20968
+ function isEmpty(container) {
20969
+ return Array.isArray(container) ? container.length === 0 : Object.keys(container).length === 0;
20970
+ }
20971
+ function removePathAndEmptiedAncestors(root, path) {
20972
+ const chain = [root];
20973
+ for (const segment of path.slice(0, -1)) {
20974
+ const current = chain[chain.length - 1];
20975
+ if (current === undefined)
20976
+ return;
20977
+ const next = childOf(current, segment);
20978
+ if (!isContainer(next))
20979
+ return;
20980
+ chain.push(next);
20981
+ }
20982
+ for (let depth = path.length - 1;depth >= 0; depth -= 1) {
20983
+ const container = chain[depth];
20984
+ const segment = path[depth];
20985
+ if (container === undefined || segment === undefined)
20986
+ return;
20987
+ removeChild(container, segment);
20988
+ if (depth === 0 || !isEmpty(container))
20989
+ return;
20990
+ }
20991
+ }
20992
+ function prunePass(root, issues) {
20993
+ const byKey = new Map;
20994
+ for (const issue of issues) {
20995
+ for (const path of targetPaths(root, issue)) {
20996
+ if (path.length === 0)
20997
+ return null;
20998
+ const key = path.map((segment) => String(segment)).join(".");
20999
+ if (!byKey.has(key))
21000
+ byKey.set(key, { key, message: issue.message, path });
21001
+ }
21002
+ }
21003
+ const targets = [...byKey.values()];
21004
+ const outermost = targets.filter((target) => !targets.some((other) => isAncestor(other.path, target.path))).sort((left, right) => compareDescending(left.path, right.path));
21005
+ const next = structuredClone(root);
21006
+ for (const target of outermost)
21007
+ removePathAndEmptiedAncestors(next, target.path);
21008
+ return { dropped: outermost, next };
21009
+ }
21010
+ function pruneInvalidConfigPaths(config, issues, validate, maxPasses = MAX_PRUNE_PASSES) {
21011
+ const dropped = [];
21012
+ let current = config;
21013
+ let pending = issues;
21014
+ for (let pass = 0;pass < maxPasses; pass += 1) {
21015
+ const step = prunePass(current, pending);
21016
+ if (step === null || step.dropped.length === 0)
21017
+ return { ok: false, dropped };
21018
+ dropped.push(...step.dropped);
21019
+ current = step.next;
21020
+ if (Object.keys(current).length === 0)
21021
+ return { ok: false, dropped };
21022
+ const validation = validate(current);
21023
+ if (validation.success)
21024
+ return { ok: true, config: current, dropped };
21025
+ pending = validation.issues;
21026
+ }
21027
+ return { ok: false, dropped };
21028
+ }
21029
+
21030
+ // packages/omo-config-core/src/loader/layer-validation.ts
21031
+ function validationDiagnostic(path, issues) {
21032
+ const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
21033
+ return {
21034
+ kind: "validation",
21035
+ message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
21036
+ path,
21037
+ issuePaths
21038
+ };
21039
+ }
21040
+ function invalidValueDiagnostics(path, dropped) {
21041
+ return dropped.map((entry) => ({
21042
+ kind: "invalid-value",
21043
+ message: `Ignored invalid value in ${path}: ${entry.key}: ${entry.message}`,
21044
+ path,
21045
+ issuePaths: [entry.key]
21046
+ }));
21047
+ }
21048
+ function unrecognizedKeyIssues(issues) {
21049
+ return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
21050
+ }
21051
+ function isRecord9(value) {
21052
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21053
+ }
21054
+ function sanitizeUnsafeKeys(value, path = []) {
21055
+ if (Array.isArray(value)) {
21056
+ const issues = [];
21057
+ const sanitized = value.map((entry, index) => {
21058
+ const nested = sanitizeUnsafeKeys(entry, [...path, String(index)]);
21059
+ issues.push(...nested.issues);
21060
+ return nested.value;
21061
+ });
21062
+ return { issues, value: sanitized };
21063
+ }
21064
+ if (!isRecord9(value))
21065
+ return { issues: [], value };
21066
+ const issues = [];
21067
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
21068
+ issues.push({ keys: ["__proto__"], path });
21069
+ }
21070
+ const sanitized = {};
21071
+ for (const [key, entry] of Object.entries(value)) {
21072
+ if (isUnsafeObjectKey(key)) {
21073
+ issues.push({ keys: [key], path });
21074
+ continue;
21075
+ }
21076
+ const nested = sanitizeUnsafeKeys(entry, [...path, key]);
21077
+ issues.push(...nested.issues);
21078
+ sanitized[key] = nested.value;
21079
+ }
21080
+ return { issues, value: sanitized };
21081
+ }
21082
+ function containerAt(record, path) {
21083
+ let node = record;
21084
+ for (const segment of path) {
21085
+ if (Array.isArray(node)) {
21086
+ const index = Number(segment);
21087
+ if (!Number.isInteger(index) || index < 0 || index >= node.length)
21088
+ return null;
21089
+ node = node[index];
21090
+ } else if (isRecord9(node) && Object.hasOwn(node, segment)) {
21091
+ node = node[segment];
21092
+ } else {
21093
+ return null;
21094
+ }
21095
+ }
21096
+ return isRecord9(node) ? node : null;
21097
+ }
21098
+ function stripUnrecognizedKeys(record, issues) {
21099
+ const stripped = structuredClone(record);
21100
+ const issuePaths = [];
21101
+ for (const issue of issues) {
21102
+ const container = containerAt(stripped, issue.path);
21103
+ if (container === null)
21104
+ continue;
21105
+ for (const key of issue.keys) {
21106
+ delete container[key];
21107
+ issuePaths.push([...issue.path, key].join("."));
21108
+ }
21109
+ }
21110
+ return { issuePaths, stripped };
21111
+ }
21112
+ function toRecord(value) {
21113
+ if (value === null || typeof value !== "object" || Array.isArray(value))
21114
+ return null;
21115
+ const record = {};
21116
+ for (const [key, entry] of Object.entries(value)) {
21117
+ record[key] = entry;
21118
+ }
21119
+ return record;
21120
+ }
21121
+ var validateLayerRecord = (record) => {
21122
+ const parsed = OmoConfigLayerSchema.safeParse(record);
21123
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
21124
+ };
21125
+ function validateConfigLayer(path, data) {
21126
+ const sanitized = sanitizeUnsafeKeys(data);
21127
+ const record = toRecord(sanitized.value);
21128
+ const unsafeIssuePaths = sanitized.issues.flatMap((issue) => issue.keys.map((key) => [...issue.path, key].join(".")));
21129
+ const unsafeDiagnostics = unsafeIssuePaths.length === 0 ? [] : [{ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${unsafeIssuePaths.join(", ")}`, path, issuePaths: unsafeIssuePaths }];
21130
+ const validation = OmoConfigLayerSchema.safeParse(sanitized.value);
21131
+ if (validation.success) {
21132
+ if (record !== null)
21133
+ return { loaded: true, diagnostics: unsafeDiagnostics, value: record };
21134
+ return {
21135
+ loaded: false,
21136
+ diagnostics: [{ kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path }]
21137
+ };
21138
+ }
21139
+ const rejected = { loaded: false, diagnostics: [validationDiagnostic(path, validation.error.issues)] };
21140
+ const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
21141
+ if (record === null)
21142
+ return rejected;
21143
+ let candidate = record;
21144
+ let issues = validation.error.issues;
21145
+ const diagnostics = [...unsafeDiagnostics];
21146
+ if (unknownIssues.length > 0) {
21147
+ const { issuePaths, stripped } = stripUnrecognizedKeys(record, unknownIssues);
21148
+ if (issuePaths.length > 0) {
21149
+ diagnostics.push({ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`, path, issuePaths });
21150
+ }
21151
+ const strippedValidation = validateLayerRecord(stripped);
21152
+ if (strippedValidation.success)
21153
+ return { loaded: true, diagnostics, value: stripped };
21154
+ candidate = stripped;
21155
+ issues = strippedValidation.issues;
21156
+ }
21157
+ const pruned = pruneInvalidConfigPaths(candidate, issues, validateLayerRecord);
21158
+ if (!pruned.ok)
21159
+ return rejected;
21160
+ return { loaded: true, diagnostics: [...diagnostics, ...invalidValueDiagnostics(path, pruned.dropped)], value: pruned.config };
21161
+ }
21162
+
20885
21163
  // packages/omo-config-core/src/loader/paths.ts
20886
21164
  import { userInfo } from "node:os";
20887
21165
  import { dirname as dirname8, join as join18, posix, resolve as resolve7 } from "node:path";
@@ -20891,15 +21169,6 @@ function toPosixPath2(path) {
20891
21169
  return path.split("\\").join("/");
20892
21170
  }
20893
21171
 
20894
- // packages/omo-config-core/src/loader/types.ts
20895
- import { existsSync as existsSync3, lstatSync, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
20896
- var DEFAULT_READ_FILE_SYSTEM = {
20897
- existsSync: existsSync3,
20898
- lstatSync,
20899
- readFileSync: readFileSync2,
20900
- realpathSync: realpathSync2
20901
- };
20902
-
20903
21172
  // packages/omo-config-core/src/loader/paths.ts
20904
21173
  var MAX_PROJECT_CONFIG_DIRECTORY_DEPTH = 256;
20905
21174
  var ACCOUNT_HOME_DIR = userInfo().homedir;
@@ -20995,7 +21264,7 @@ function resolveOmoProfileName(options = {}) {
20995
21264
  const env = options.env ?? process.env;
20996
21265
  return profileName(options.profile) ?? profileName(env["OMO_PROFILE"]) ?? profileName(env["OCX_PROFILE"]) ?? profileNameFromOpenCodeConfigDir(env["OPENCODE_CONFIG_DIR"]);
20997
21266
  }
20998
- function toRecord(value) {
21267
+ function toRecord2(value) {
20999
21268
  if (value === null || typeof value !== "object" || Array.isArray(value))
21000
21269
  return;
21001
21270
  return Object.fromEntries(Object.entries(value));
@@ -21016,13 +21285,13 @@ function harnessLayer(config, harness) {
21016
21285
  const legacyKeys = Object.entries(OMO_CONFIG_LEGACY_HARNESS_ALIASES).filter(([, target]) => target === canonical).map(([legacy]) => harnessBlockKey(legacy));
21017
21286
  let layer = {};
21018
21287
  for (const key of [...legacyKeys, harnessBlockKey(canonical)]) {
21019
- layer = mergeOmoConfigRecords(layer, toRecord(config[key]) ?? {});
21288
+ layer = mergeOmoConfigRecords(layer, toRecord2(config[key]) ?? {});
21020
21289
  }
21021
21290
  return layer;
21022
21291
  }
21023
21292
  function resolveOmoConfigView(options) {
21024
- const profiles = toRecord(options.config["profiles"]);
21025
- const profile = options.profile === undefined ? undefined : toRecord(profiles?.[options.profile]);
21293
+ const profiles = toRecord2(options.config["profiles"]);
21294
+ const profile = options.profile === undefined ? undefined : toRecord2(profiles?.[options.profile]);
21026
21295
  const diagnostics = profile === undefined && options.profile !== undefined ? [{
21027
21296
  kind: "profile",
21028
21297
  message: `Activated omo profile "${options.profile}" does not exist; using the base configuration`,
@@ -21077,70 +21346,9 @@ function stripResolutionControlKeys(config) {
21077
21346
  } = config;
21078
21347
  return resolved;
21079
21348
  }
21080
- function validationDiagnostic(path, issues) {
21081
- const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
21082
- return {
21083
- kind: "validation",
21084
- message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
21085
- path,
21086
- issuePaths
21087
- };
21088
- }
21089
- function unrecognizedKeyIssues(issues) {
21090
- return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
21091
- }
21092
- function hasUnsafeUnrecognizedKey(issues) {
21093
- return issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey(key)));
21094
- }
21095
- function hasTamperedPrototype(value) {
21096
- if (Array.isArray(value))
21097
- return value.some((entry) => hasTamperedPrototype(entry));
21098
- if (!isRecord9(value))
21099
- return false;
21100
- const prototype = Object.getPrototypeOf(value);
21101
- if (prototype !== Object.prototype && prototype !== null)
21102
- return true;
21103
- return Object.values(value).some((entry) => hasTamperedPrototype(entry));
21104
- }
21105
- function isRecord9(value) {
21106
- return typeof value === "object" && value !== null && !Array.isArray(value);
21107
- }
21108
- function containerAt(record, path) {
21109
- let container = record;
21110
- for (const segment of path) {
21111
- const next = container[segment];
21112
- if (!isRecord9(next))
21113
- return null;
21114
- container = next;
21115
- }
21116
- return container;
21117
- }
21118
- function stripUnrecognizedKeys(record, issues) {
21119
- const stripped = structuredClone(record);
21120
- const issuePaths = [];
21121
- for (const issue of issues) {
21122
- const container = containerAt(stripped, issue.path);
21123
- if (container === null)
21124
- continue;
21125
- for (const key of issue.keys) {
21126
- delete container[key];
21127
- issuePaths.push([...issue.path, key].join("."));
21128
- }
21129
- }
21130
- return { issuePaths, stripped };
21131
- }
21132
- function toRecord2(value) {
21133
- if (value === null || typeof value !== "object" || Array.isArray(value))
21134
- return null;
21135
- const record = {};
21136
- for (const [key, entry] of Object.entries(value)) {
21137
- record[key] = entry;
21138
- }
21139
- return record;
21140
- }
21141
21349
  function readConfigSource(path, scope, fileSystem) {
21142
21350
  if (!fileSystem.existsSync(path)) {
21143
- return { source: { exists: false, loaded: false, path, scope } };
21351
+ return { diagnostics: [], source: { exists: false, loaded: false, path, scope } };
21144
21352
  }
21145
21353
  let content;
21146
21354
  try {
@@ -21148,68 +21356,23 @@ function readConfigSource(path, scope, fileSystem) {
21148
21356
  } catch (error) {
21149
21357
  const message = error instanceof Error ? error.message : String(error);
21150
21358
  return {
21151
- diagnostic: { kind: "read", message: `Failed to read ${path}: ${message}`, path },
21359
+ diagnostics: [{ kind: "read", message: `Failed to read ${path}: ${message}`, path }],
21152
21360
  source: { exists: true, loaded: false, path, scope }
21153
21361
  };
21154
21362
  }
21155
21363
  const parsed = parseJsoncSafe(content);
21156
21364
  if (parsed.errors.length > 0) {
21157
21365
  return {
21158
- diagnostic: {
21366
+ diagnostics: [{
21159
21367
  kind: "parse",
21160
21368
  message: `JSONC parse error in ${path}: ${parsed.errors.map((error) => error.message).join(", ")}`,
21161
21369
  path
21162
- },
21370
+ }],
21163
21371
  source: { exists: true, loaded: false, path, scope }
21164
21372
  };
21165
21373
  }
21166
- if (hasTamperedPrototype(parsed.data)) {
21167
- return {
21168
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: "__proto__" member is not allowed`, path },
21169
- source: { exists: true, loaded: false, path, scope }
21170
- };
21171
- }
21172
- const parsedRecord = toRecord2(parsed.data);
21173
- const validation = OmoConfigLayerSchema.safeParse(parsed.data);
21174
- if (!validation.success) {
21175
- const unrecognized = unrecognizedKeyIssues(validation.error.issues);
21176
- if (hasUnsafeUnrecognizedKey(unrecognized)) {
21177
- return {
21178
- diagnostic: validationDiagnostic(path, validation.error.issues),
21179
- source: { exists: true, loaded: false, path, scope }
21180
- };
21181
- }
21182
- const rejected = {
21183
- diagnostic: validationDiagnostic(path, validation.error.issues),
21184
- source: { exists: true, loaded: false, path, scope }
21185
- };
21186
- const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
21187
- if (parsedRecord === null || unknownIssues.length === 0)
21188
- return rejected;
21189
- const { issuePaths, stripped } = stripUnrecognizedKeys(parsedRecord, unknownIssues);
21190
- if (!OmoConfigLayerSchema.safeParse(stripped).success)
21191
- return rejected;
21192
- return {
21193
- diagnostic: {
21194
- kind: "unknown-keys",
21195
- message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`,
21196
- path,
21197
- issuePaths
21198
- },
21199
- source: { exists: true, loaded: true, path, scope },
21200
- value: stripped
21201
- };
21202
- }
21203
- if (parsedRecord === null) {
21204
- return {
21205
- diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path },
21206
- source: { exists: true, loaded: false, path, scope }
21207
- };
21208
- }
21209
- return {
21210
- source: { exists: true, loaded: true, path, scope },
21211
- value: parsedRecord
21212
- };
21374
+ const layer = validateConfigLayer(path, parsed.data);
21375
+ 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 } };
21213
21376
  }
21214
21377
  function legacyCategoryDiagnostic(path, renames) {
21215
21378
  const detail = renames.map((rename) => rename.dropped ? `${rename.path} ignored because ${rename.canonical} is also configured` : `${rename.path} renamed to ${rename.canonical}`).join(", ");
@@ -21244,8 +21407,7 @@ function loadOmoConfig(options = {}) {
21244
21407
  })) {
21245
21408
  const loaded = readConfigSource(candidate.path, candidate.scope, fileSystem);
21246
21409
  sources.push(loaded.source);
21247
- if (loaded.diagnostic !== undefined)
21248
- diagnostics.push(loaded.diagnostic);
21410
+ diagnostics.push(...loaded.diagnostics);
21249
21411
  if (loaded.value !== undefined) {
21250
21412
  const canonicalized = canonicalizeLegacyCategoryNames(loaded.value);
21251
21413
  if (canonicalized.renames.length > 0) {
@@ -21268,7 +21430,8 @@ function loadOmoConfig(options = {}) {
21268
21430
  ...options.harness === undefined ? {} : { harness: options.harness },
21269
21431
  ...requestedProfile === undefined ? {} : { profile: requestedProfile }
21270
21432
  });
21271
- const finalConfig = OmoConfigSchema.safeParse(mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config));
21433
+ const finalInput = mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config);
21434
+ const finalConfig = OmoConfigSchema.safeParse(finalInput);
21272
21435
  if (finalConfig.success) {
21273
21436
  return {
21274
21437
  config: stripResolutionControlKeys(finalConfig.data),
@@ -21278,9 +21441,22 @@ function loadOmoConfig(options = {}) {
21278
21441
  sources
21279
21442
  };
21280
21443
  }
21444
+ const pruned = pruneInvalidConfigPaths(finalInput, finalConfig.error.issues, (record) => {
21445
+ const parsed = OmoConfigSchema.safeParse(record);
21446
+ return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
21447
+ });
21448
+ if (pruned.ok) {
21449
+ return {
21450
+ config: stripResolutionControlKeys(OmoConfigSchema.parse(pruned.config)),
21451
+ diagnostics: [...diagnostics, ...resolved.diagnostics, ...invalidValueDiagnostics(MERGED_OMO_CONFIG_PATH, pruned.dropped)],
21452
+ layers,
21453
+ ...resolved.profile === undefined ? {} : { profile: resolved.profile },
21454
+ sources
21455
+ };
21456
+ }
21281
21457
  return {
21282
21458
  config: stripResolutionControlKeys(OmoConfigSchema.parse(DEFAULT_RAW_CONFIG)),
21283
- diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic("(merged omo config)", finalConfig.error.issues)],
21459
+ diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic(MERGED_OMO_CONFIG_PATH, finalConfig.error.issues)],
21284
21460
  layers,
21285
21461
  ...resolved.profile === undefined ? {} : { profile: resolved.profile },
21286
21462
  sources
@@ -5,5 +5,5 @@
5
5
  "index.js": "b6b064fab277031c6c51f5a58b84b31040f9960bf767bf646adc0540c1d25c1b",
6
6
  "page-bundle.js": "bfd51e8383cf47200d57387bbbed258bce5a3a3d00c38fc108034d2888e23f0c"
7
7
  },
8
- "stagedAtUtc": "2026-09-29T22:25:16.537Z"
8
+ "stagedAtUtc": "2026-09-30T10:38:45.312Z"
9
9
  }