@hasna/skills 0.10.39 → 0.10.42
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.
- package/README.md +41 -0
- package/bin/index.js +426 -115
- package/bin/mcp.js +138 -19
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/reviewed-runtime-lock.d.ts +17 -0
- package/dist/cli/commands/runtime-copyfile.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +163 -31
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/agent-integration.d.ts +1 -1
- package/dist/lib/codex-settings-witness.d.ts +13 -0
- package/dist/sdk/index.js +161 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9126,8 +9126,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9126
9126
|
text(source.path);
|
|
9127
9127
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9128
9128
|
if (source.hashMode !== undefined)
|
|
9129
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9130
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3")
|
|
9129
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9130
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
9131
9131
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "config.toml");
|
|
9132
9132
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
9133
9133
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "settings.json");
|
|
@@ -9166,7 +9166,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9166
9166
|
import { createHash as createHash20 } from "crypto";
|
|
9167
9167
|
import { lstatSync as lstatSync23, readFileSync as readFileSync18, readdirSync as readdirSync10, realpathSync as realpathSync9 } from "fs";
|
|
9168
9168
|
import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute17, join as join30, resolve as resolve17, sep as sep7 } from "path";
|
|
9169
|
-
import { isDeepStrictEqual as
|
|
9169
|
+
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
9170
9170
|
|
|
9171
9171
|
// node_modules/yaml/dist/index.js
|
|
9172
9172
|
var composer = require_composer();
|
|
@@ -11040,7 +11040,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11040
11040
|
// package.json
|
|
11041
11041
|
var package_default = {
|
|
11042
11042
|
name: "@hasna/skills",
|
|
11043
|
-
version: "0.10.
|
|
11043
|
+
version: "0.10.42",
|
|
11044
11044
|
description: "Skills library for AI coding agents",
|
|
11045
11045
|
type: "module",
|
|
11046
11046
|
bin: {
|
|
@@ -14340,6 +14340,7 @@ function projectRegularCodexMcp(config, source) {
|
|
|
14340
14340
|
|
|
14341
14341
|
// src/lib/codex-settings-witness.ts
|
|
14342
14342
|
import { createHash as createHash12 } from "crypto";
|
|
14343
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
14343
14344
|
var sha2 = (text2) => createHash12("sha256").update(text2).digest("hex");
|
|
14344
14345
|
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
14345
14346
|
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
@@ -14451,7 +14452,7 @@ function disablesOneSkill(entry) {
|
|
|
14451
14452
|
return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
|
|
14452
14453
|
}
|
|
14453
14454
|
function inference(target, version) {
|
|
14454
|
-
const keys = version
|
|
14455
|
+
const keys = version >= 3 ? [...INFERENCE, "service_tier", "plan_mode_reasoning_effort", "personality"] : INFERENCE;
|
|
14455
14456
|
for (const key of keys)
|
|
14456
14457
|
if (Object.hasOwn(target, key)) {
|
|
14457
14458
|
const value = target[key];
|
|
@@ -14459,7 +14460,7 @@ function inference(target, version) {
|
|
|
14459
14460
|
need4(typeof value === "string" && MODEL.test(value));
|
|
14460
14461
|
else if (key === "personality")
|
|
14461
14462
|
need4(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
|
|
14462
|
-
else if (version
|
|
14463
|
+
else if (version >= 3 && key !== "model_verbosity") {
|
|
14463
14464
|
need4(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
|
|
14464
14465
|
if (key !== "service_tier")
|
|
14465
14466
|
need4(value.length > 0);
|
|
@@ -14468,6 +14469,115 @@ function inference(target, version) {
|
|
|
14468
14469
|
delete target[key];
|
|
14469
14470
|
}
|
|
14470
14471
|
}
|
|
14472
|
+
function projectModelAvailabilityNux(config, text2) {
|
|
14473
|
+
const tui = config.tui, counts = isRecord3(tui) ? tui.model_availability_nux : undefined;
|
|
14474
|
+
if (isRecord3(tui) && !Object.keys(tui).length) {
|
|
14475
|
+
delete config.tui;
|
|
14476
|
+
return { config, numericSource: text2 };
|
|
14477
|
+
}
|
|
14478
|
+
if (!isRecord3(counts) || Object.keys(counts).length > 256 || !Object.values(counts).every((value) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 4294967295))
|
|
14479
|
+
return { config, numericSource: text2 };
|
|
14480
|
+
const expected = structuredClone(config);
|
|
14481
|
+
delete expected.tui.model_availability_nux;
|
|
14482
|
+
const stripEmptyTui = (value) => {
|
|
14483
|
+
if (isRecord3(value.tui) && !Object.keys(value.tui).length)
|
|
14484
|
+
delete value.tui;
|
|
14485
|
+
};
|
|
14486
|
+
stripEmptyTui(expected);
|
|
14487
|
+
const headers = [];
|
|
14488
|
+
const spans = [];
|
|
14489
|
+
const integer = /^(?:\+?(?:0|[1-9](?:_?\d)*)|0x[0-9a-fA-F](?:_?[0-9a-fA-F])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*)$/;
|
|
14490
|
+
const counterIntegers = (span) => {
|
|
14491
|
+
let values = 0;
|
|
14492
|
+
for (let at = 0;at < span.length; ) {
|
|
14493
|
+
const char = span[at];
|
|
14494
|
+
if (char === "#") {
|
|
14495
|
+
while (at < span.length && span[at] !== `
|
|
14496
|
+
`)
|
|
14497
|
+
at++;
|
|
14498
|
+
continue;
|
|
14499
|
+
}
|
|
14500
|
+
if (char === '"' || char === "'") {
|
|
14501
|
+
const quote = char;
|
|
14502
|
+
at++;
|
|
14503
|
+
while (at < span.length) {
|
|
14504
|
+
if (quote === '"' && span[at] === "\\") {
|
|
14505
|
+
at += 2;
|
|
14506
|
+
continue;
|
|
14507
|
+
}
|
|
14508
|
+
if (span[at++] === quote)
|
|
14509
|
+
break;
|
|
14510
|
+
}
|
|
14511
|
+
continue;
|
|
14512
|
+
}
|
|
14513
|
+
if (char !== "=") {
|
|
14514
|
+
at++;
|
|
14515
|
+
continue;
|
|
14516
|
+
}
|
|
14517
|
+
at++;
|
|
14518
|
+
while (at < span.length && /\s/.test(span[at]))
|
|
14519
|
+
at++;
|
|
14520
|
+
if (span[at] === "{") {
|
|
14521
|
+
at++;
|
|
14522
|
+
continue;
|
|
14523
|
+
}
|
|
14524
|
+
const start = at;
|
|
14525
|
+
while (at < span.length && !/[\s,\]{}#]/.test(span[at]))
|
|
14526
|
+
at++;
|
|
14527
|
+
if (!integer.test(span.slice(start, at)))
|
|
14528
|
+
return false;
|
|
14529
|
+
values++;
|
|
14530
|
+
}
|
|
14531
|
+
return values === Object.keys(counts).length;
|
|
14532
|
+
};
|
|
14533
|
+
let attempts = 0;
|
|
14534
|
+
const realPrefix = (end) => {
|
|
14535
|
+
if (++attempts > 256)
|
|
14536
|
+
return false;
|
|
14537
|
+
try {
|
|
14538
|
+
Bun.TOML.parse(text2.slice(0, end));
|
|
14539
|
+
return true;
|
|
14540
|
+
} catch {
|
|
14541
|
+
return false;
|
|
14542
|
+
}
|
|
14543
|
+
};
|
|
14544
|
+
for (const match of text2.matchAll(/^[ \t]*\[[^\r\n]+\][ \t]*(?:#[^\r\n]*)?(?:\r?\n|$)/gm)) {
|
|
14545
|
+
const start = match.index, end = start + match[0].length;
|
|
14546
|
+
if (realPrefix(end))
|
|
14547
|
+
headers.push({ start, end });
|
|
14548
|
+
}
|
|
14549
|
+
for (let at = 0;at < headers.length; at++) {
|
|
14550
|
+
const header = headers[at];
|
|
14551
|
+
let parsed;
|
|
14552
|
+
try {
|
|
14553
|
+
parsed = Bun.TOML.parse(text2.slice(header.start, header.end));
|
|
14554
|
+
} catch {
|
|
14555
|
+
continue;
|
|
14556
|
+
}
|
|
14557
|
+
if (isRecord3(parsed.tui) && isRecord3(parsed.tui.model_availability_nux) && !Object.keys(parsed.tui.model_availability_nux).length && Object.keys(parsed).length === 1 && Object.keys(parsed.tui).length === 1)
|
|
14558
|
+
spans.push({ start: header.start, end: headers[at + 1]?.start ?? text2.length });
|
|
14559
|
+
}
|
|
14560
|
+
for (const match of text2.matchAll(/^[ \t]*(?:(?:tui|"tui"|'tui')[ \t]*\.[ \t]*)?(?:model_availability_nux|"model_availability_nux"|'model_availability_nux')[ \t]*=[ \t]*\{[^\r\n]*(?:\r?\n|$)/gm)) {
|
|
14561
|
+
const start = match.index, end = start + match[0].length;
|
|
14562
|
+
if (realPrefix(end))
|
|
14563
|
+
spans.push({ start, end });
|
|
14564
|
+
}
|
|
14565
|
+
for (const { start, end } of spans) {
|
|
14566
|
+
if (!counterIntegers(text2.slice(start, end)))
|
|
14567
|
+
continue;
|
|
14568
|
+
const remaining = text2.slice(0, start) + text2.slice(end);
|
|
14569
|
+
let parsed;
|
|
14570
|
+
try {
|
|
14571
|
+
parsed = Bun.TOML.parse(remaining);
|
|
14572
|
+
} catch {
|
|
14573
|
+
continue;
|
|
14574
|
+
}
|
|
14575
|
+
stripEmptyTui(parsed);
|
|
14576
|
+
if (isDeepStrictEqual3(parsed, expected))
|
|
14577
|
+
return { config: expected, numericSource: remaining };
|
|
14578
|
+
}
|
|
14579
|
+
return { config, numericSource: text2 };
|
|
14580
|
+
}
|
|
14471
14581
|
function settingsDigest2(text2, version = 2) {
|
|
14472
14582
|
let config;
|
|
14473
14583
|
try {
|
|
@@ -14476,7 +14586,8 @@ function settingsDigest2(text2, version = 2) {
|
|
|
14476
14586
|
throw new Error("Invalid Codex settings witness");
|
|
14477
14587
|
}
|
|
14478
14588
|
need4(isRecord3(config));
|
|
14479
|
-
const
|
|
14589
|
+
const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
|
|
14590
|
+
const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
14480
14591
|
normalize3(projected.config, version);
|
|
14481
14592
|
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical4(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
14482
14593
|
}
|
|
@@ -14521,10 +14632,29 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
|
|
|
14521
14632
|
throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
|
|
14522
14633
|
return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
|
|
14523
14634
|
}
|
|
14635
|
+
function hashCodexSettingsReplacementV4(text2, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
14636
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
|
|
14637
|
+
budget.remaining -= Buffer.byteLength(text2);
|
|
14638
|
+
return settingsDigest2(text2, 4);
|
|
14639
|
+
}
|
|
14640
|
+
function captureCodexSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
14641
|
+
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget, "config.toml"), 4) };
|
|
14642
|
+
}
|
|
14643
|
+
function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
14644
|
+
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
14645
|
+
const budget = { remaining: 2 * 1024 * 1024 };
|
|
14646
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
|
|
14647
|
+
const original = previous.hashMode === "codex-settings-v3" ? settingsDigest2(before, 3) : previous.hashMode === "codex-settings-v2" ? settingsDigest2(before, 2) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
|
|
14648
|
+
need4(original === previous.sha256);
|
|
14649
|
+
const sha2562 = settingsDigest2(current, 4);
|
|
14650
|
+
if (settingsDigest2(before, 4) !== sha2562)
|
|
14651
|
+
throw new Error("Codex settings outside the reviewed V4 contract changed; explicit discovery review required");
|
|
14652
|
+
return { path: previous.path, hashMode: "codex-settings-v4", sha256: sha2562 };
|
|
14653
|
+
}
|
|
14524
14654
|
|
|
14525
14655
|
// src/lib/agent-integration.ts
|
|
14526
14656
|
import { createHash as createHash15, randomUUID as randomUUID2 } from "crypto";
|
|
14527
|
-
import { isDeepStrictEqual as
|
|
14657
|
+
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
14528
14658
|
import { existsSync as existsSync9, lstatSync as lstatSync18, statSync as statSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync13, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync8, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, chmodSync, openSync as openSync9, closeSync as closeSync9, fsyncSync as fsyncSync3, fstatSync as fstatSync9, readSync as readSync7, constants as constants9, linkSync as linkSync2 } from "fs";
|
|
14529
14659
|
import { dirname as dirname13, join as join20, resolve as resolve12, relative as relative6, isAbsolute as isAbsolute12, sep as sep6 } from "path";
|
|
14530
14660
|
import { homedir as homedir2 } from "os";
|
|
@@ -14653,7 +14783,7 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
14653
14783
|
}
|
|
14654
14784
|
|
|
14655
14785
|
// src/lib/agent-codex.ts
|
|
14656
|
-
import { isDeepStrictEqual as
|
|
14786
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
14657
14787
|
var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
14658
14788
|
var codexPathConfigRefusal = () => new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables or a native single-line inline array; preserve and review unsupported inline, dotted, or malformed definitions before running skills hook install");
|
|
14659
14789
|
function splitTopLevel(value, delimiter2) {
|
|
@@ -14784,7 +14914,7 @@ function normalizeCodexInlinePathConfig(text2) {
|
|
|
14784
14914
|
const expectedWithoutConfig = structuredClone(before);
|
|
14785
14915
|
delete expectedWithoutConfig.skills.config;
|
|
14786
14916
|
try {
|
|
14787
|
-
if (
|
|
14917
|
+
if (isDeepStrictEqual4(Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, "")), expectedWithoutConfig))
|
|
14788
14918
|
return text2;
|
|
14789
14919
|
} catch {}
|
|
14790
14920
|
const matches = [];
|
|
@@ -14837,7 +14967,7 @@ function normalizeCodexInlinePathConfig(text2) {
|
|
|
14837
14967
|
try {
|
|
14838
14968
|
const after = Bun.TOML.parse(candidate);
|
|
14839
14969
|
const expected = structuredClone(before);
|
|
14840
|
-
if (
|
|
14970
|
+
if (isDeepStrictEqual4(after, expected))
|
|
14841
14971
|
matches.push(candidate);
|
|
14842
14972
|
} catch {}
|
|
14843
14973
|
}
|
|
@@ -14853,11 +14983,11 @@ function assertCodexPathConfigEditable(text2) {
|
|
|
14853
14983
|
delete expected.skills.config;
|
|
14854
14984
|
try {
|
|
14855
14985
|
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
14856
|
-
if (
|
|
14986
|
+
if (isDeepStrictEqual4(withoutSections, expected))
|
|
14857
14987
|
return;
|
|
14858
14988
|
const normalized = normalizeCodexInlinePathConfig(text2);
|
|
14859
14989
|
const withoutNormalizedSections = Bun.TOML.parse(normalized.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
14860
|
-
if (
|
|
14990
|
+
if (isDeepStrictEqual4(withoutNormalizedSections, expected))
|
|
14861
14991
|
return;
|
|
14862
14992
|
} catch {}
|
|
14863
14993
|
throw codexPathConfigRefusal();
|
|
@@ -14895,7 +15025,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
14895
15025
|
const candidates = new Set;
|
|
14896
15026
|
const consider = (candidate) => {
|
|
14897
15027
|
try {
|
|
14898
|
-
if (
|
|
15028
|
+
if (isDeepStrictEqual4(Bun.TOML.parse(candidate), expected))
|
|
14899
15029
|
candidates.add(candidate);
|
|
14900
15030
|
} catch {}
|
|
14901
15031
|
};
|
|
@@ -15882,7 +16012,7 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
15882
16012
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
15883
16013
|
const recheck = () => {
|
|
15884
16014
|
try {
|
|
15885
|
-
if (realpathSync8(command) !== resolved || linkIdentity(lstatSync18(command, { bigint: true })) !== linkIdentity(link) || !
|
|
16015
|
+
if (realpathSync8(command) !== resolved || linkIdentity(lstatSync18(command, { bigint: true })) !== linkIdentity(link) || !isDeepStrictEqual5(parents2(), parentIdentity) || !isDeepStrictEqual5(parents2(resolved), resolvedParentIdentity))
|
|
15886
16016
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
15887
16017
|
projectedHookAcl(cli.file, cli.stat);
|
|
15888
16018
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
@@ -16103,7 +16233,7 @@ function atomicWrite2(path, content) {
|
|
|
16103
16233
|
}
|
|
16104
16234
|
}
|
|
16105
16235
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
16106
|
-
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
|
|
16236
|
+
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3, 4].includes(options.targetCodexVersion)))
|
|
16107
16237
|
throw new Error("Invalid Codex witness target version");
|
|
16108
16238
|
const home = resolve12(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
16109
16239
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
@@ -16126,21 +16256,21 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
16126
16256
|
throw new Error("Settings witness upgrade requires a preserved settings preimage");
|
|
16127
16257
|
const replaced = [previous];
|
|
16128
16258
|
if (options.agent === "codex") {
|
|
16129
|
-
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1" && !(options.targetCodexVersion === 3 && previous.hashMode === "codex-settings-v2"))
|
|
16259
|
+
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1" && !((options.targetCodexVersion === 3 || options.targetCodexVersion === 4) && previous.hashMode === "codex-settings-v2") && !(options.targetCodexVersion === 4 && previous.hashMode === "codex-settings-v3"))
|
|
16130
16260
|
throw new Error("Codex settings witness upgrade requires an eligible preserved configuration witness");
|
|
16131
16261
|
const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
|
|
16132
16262
|
for (const source of binding.sources) {
|
|
16133
16263
|
if (source === previous || source.path !== configPath)
|
|
16134
16264
|
continue;
|
|
16135
16265
|
const fields = source.fields;
|
|
16136
|
-
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !
|
|
16266
|
+
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual5(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
|
|
16137
16267
|
throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
|
|
16138
16268
|
if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
|
|
16139
16269
|
throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
|
|
16140
16270
|
replaced.push(source);
|
|
16141
16271
|
}
|
|
16142
16272
|
}
|
|
16143
|
-
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
16273
|
+
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
16144
16274
|
const rebound = [];
|
|
16145
16275
|
for (const source of binding.sources) {
|
|
16146
16276
|
if (!replaced.includes(source)) {
|
|
@@ -16186,7 +16316,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
16186
16316
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
16187
16317
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
16188
16318
|
throw new Error("Invalid settings witness upgrade plan");
|
|
16189
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
16319
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
16190
16320
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
16191
16321
|
throw new Error("Settings witness upgrade plan changed");
|
|
16192
16322
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -19169,19 +19299,19 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
|
19169
19299
|
return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
|
|
19170
19300
|
}
|
|
19171
19301
|
function isSupersededCodexProjection(agent, configPath, source) {
|
|
19172
|
-
return agent === "codex" && source.path === configPath && source.format === "toml" &&
|
|
19302
|
+
return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual6(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
|
|
19173
19303
|
}
|
|
19174
19304
|
function supersedesCodexProjection(sources, configPath) {
|
|
19175
|
-
return sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3"));
|
|
19305
|
+
return sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
|
|
19176
19306
|
}
|
|
19177
19307
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
19178
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3") {
|
|
19308
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) {
|
|
19179
19309
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
19180
19310
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
19181
|
-
const current = (source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
19311
|
+
const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
19182
19312
|
if (current !== source.sha256)
|
|
19183
19313
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
19184
|
-
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
19314
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
19185
19315
|
}
|
|
19186
19316
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
19187
19317
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -19277,7 +19407,7 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
|
19277
19407
|
return current;
|
|
19278
19408
|
const installation = codexInstallationRoots(binding);
|
|
19279
19409
|
const directories = (binding.directories ?? []).filter((directory) => !installation.some((root2) => directory.path === root2 || directory.path.startsWith(root2 + sep7) || root2.startsWith(directory.path + sep7)));
|
|
19280
|
-
if (previous &&
|
|
19410
|
+
if (previous && isDeepStrictEqual6(previous.roots, roots) && isDeepStrictEqual6(previous.parents, parents3) && isDeepStrictEqual6(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
|
|
19281
19411
|
return { ...current, codexRetiredMaterializations: previous };
|
|
19282
19412
|
return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
|
|
19283
19413
|
}
|
|
@@ -19353,7 +19483,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
19353
19483
|
if (omitted.some((root2) => !retiredRoots.includes(root2)))
|
|
19354
19484
|
throw new Error("Missing reviewed directory membership inventory; provide a fresh discovery review");
|
|
19355
19485
|
const current = captureDiscoveryDirectoryProjection(ordinary.map((item) => item.path), retiredRoots).map(({ entries, ...item }) => item);
|
|
19356
|
-
if (!
|
|
19486
|
+
if (!isDeepStrictEqual6(current, projection))
|
|
19357
19487
|
throw new Error("Native discovery directory membership changed outside retired Codex roots");
|
|
19358
19488
|
}
|
|
19359
19489
|
} else
|
|
@@ -19412,7 +19542,7 @@ function codexInstallationRoots(binding) {
|
|
|
19412
19542
|
});
|
|
19413
19543
|
}
|
|
19414
19544
|
function assertMarketplaceBinding(binding, source) {
|
|
19415
|
-
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
19545
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
19416
19546
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
19417
19547
|
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
19418
19548
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
@@ -19613,10 +19743,10 @@ function resolveAgentDiscovery(options) {
|
|
|
19613
19743
|
verifyAgentDiscovery(retained);
|
|
19614
19744
|
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
19615
19745
|
unresolved("retained review is missing the full agent configuration source");
|
|
19616
|
-
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3"))) && source.path !== canonical6(configPath)))
|
|
19746
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
19617
19747
|
unresolved("retained settings witness names another configuration source");
|
|
19618
19748
|
const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
19619
|
-
if (covered.some((source) => !retained.sources.some((saved) =>
|
|
19749
|
+
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual6(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
19620
19750
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
19621
19751
|
return retained;
|
|
19622
19752
|
}
|
|
@@ -19630,7 +19760,7 @@ function resolveAgentDiscovery(options) {
|
|
|
19630
19760
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
19631
19761
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
19632
19762
|
verifyAgentDiscovery(supplied);
|
|
19633
|
-
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3"))) && source.path !== canonical6(configPath)))
|
|
19763
|
+
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
19634
19764
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
19635
19765
|
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
19636
19766
|
throw new Error("Discovery review must include the agent configuration source");
|
|
@@ -46599,6 +46729,7 @@ export {
|
|
|
46599
46729
|
validateCron,
|
|
46600
46730
|
validateBlogArticleRunOptions,
|
|
46601
46731
|
uploadSkillsSnapshotFilesToS3,
|
|
46732
|
+
upgradeCodexSettingsWitnessV4,
|
|
46602
46733
|
upgradeCodexSettingsWitnessV3,
|
|
46603
46734
|
upgradeCodexSettingsWitness,
|
|
46604
46735
|
upgradeClaudeSettingsWitness,
|
|
@@ -46786,6 +46917,7 @@ export {
|
|
|
46786
46917
|
captureDiscoveryPathSources,
|
|
46787
46918
|
captureDiscoveryDirectories,
|
|
46788
46919
|
captureDiscoveryByteSources,
|
|
46920
|
+
captureCodexSettingsV4,
|
|
46789
46921
|
captureCodexSettingsV3,
|
|
46790
46922
|
captureCodexSettingsV2,
|
|
46791
46923
|
captureCodexSettings,
|
|
@@ -7,7 +7,7 @@ export { captureDiscoveryDirectories, type DiscoveryDirectory } from "./agent-di
|
|
|
7
7
|
export interface DiscoverySource {
|
|
8
8
|
path: string;
|
|
9
9
|
sha256: string | null;
|
|
10
|
-
hashMode?: "bytes" | "path-bytes" | "claude-plugin-registry" | "claude-marketplace-registry" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3" | "claude-marketplace-registry-v2" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3";
|
|
10
|
+
hashMode?: "bytes" | "path-bytes" | "claude-plugin-registry" | "claude-marketplace-registry" | "claude-settings-v1" | "claude-settings-v2" | "claude-settings-v3" | "claude-marketplace-registry-v2" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3" | "codex-settings-v4";
|
|
11
11
|
managedPlugins?: ManagedPluginRegistrationWitness[];
|
|
12
12
|
format?: "json" | "toml" | "yaml";
|
|
13
13
|
fields?: string[];
|
|
@@ -188,7 +188,7 @@ export declare function planClaudeHookEventsUpdate(options: ClaudeHookUpdateOpti
|
|
|
188
188
|
*/
|
|
189
189
|
export declare function planAgentSettingsWitnessUpgrade(options: {
|
|
190
190
|
agent: "claude" | "codex";
|
|
191
|
-
targetCodexVersion?: 2 | 3;
|
|
191
|
+
targetCodexVersion?: 2 | 3 | 4;
|
|
192
192
|
reviewedPreimage: string;
|
|
193
193
|
home?: string;
|
|
194
194
|
dataDir?: string;
|
|
@@ -33,3 +33,16 @@ export declare function upgradeCodexSettingsWitnessV3(previous: {
|
|
|
33
33
|
hashMode?: "bytes" | "codex-settings-v1" | "codex-settings-v2";
|
|
34
34
|
sha256: string;
|
|
35
35
|
}, reviewedSettingsPath: string): ReturnType<typeof captureCodexSettingsV3>;
|
|
36
|
+
/** Explicit V4 adds only schema-valid native model-availability UI counts. */
|
|
37
|
+
export declare function hashCodexSettingsReplacementV4(text: string, budget?: ClaudeSettingsWitnessBudget): string;
|
|
38
|
+
export declare function captureCodexSettingsV4(path: string, budget?: ClaudeSettingsWitnessBudget): {
|
|
39
|
+
path: string;
|
|
40
|
+
hashMode: "codex-settings-v4";
|
|
41
|
+
sha256: string;
|
|
42
|
+
};
|
|
43
|
+
/** Prove the original V1/V2/V3/byte digest first; a V4 hash never verifies an old pin. */
|
|
44
|
+
export declare function upgradeCodexSettingsWitnessV4(previous: {
|
|
45
|
+
path: string;
|
|
46
|
+
hashMode?: "bytes" | "codex-settings-v1" | "codex-settings-v2" | "codex-settings-v3";
|
|
47
|
+
sha256: string;
|
|
48
|
+
}, reviewedSettingsPath: string): ReturnType<typeof captureCodexSettingsV4>;
|