@hasna/skills 0.10.24 → 0.10.26
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 +11 -0
- package/bin/index.js +248 -35
- package/bin/mcp.js +426 -312
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +245 -35
- package/dist/lib/agent-discovery-directories.d.ts +2 -2
- package/dist/lib/agent-discovery.d.ts +7 -0
- package/dist/lib/agent-integration.d.ts +1 -0
- package/dist/lib/codex-native-skill-catalog.d.ts +9 -1
- package/dist/lib/codex-plugin-skill-controls.d.ts +28 -1
- package/dist/sdk/index.js +245 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -530,6 +530,17 @@ hooks, commands and unsupported MCP controls refuse. Referenced own-root
|
|
|
530
530
|
tool, approval, timeout and environment metadata. Their complete fingerprints
|
|
531
531
|
and presence remain bound; review never executes commands or resolves environment
|
|
532
532
|
values. Name controls suppress Skills, not every plugin capability.
|
|
533
|
+
A Codex 0.160 catalog may also prove a nonremote plugin is installed but disabled.
|
|
534
|
+
With an exact current path deny and explicit witnessed plugin disable, its cache
|
|
535
|
+
can remain inert across versions; re-enable, ambiguous identity, conflicting
|
|
536
|
+
controls and unsafe paths refuse. This does not approve its hook declarations.
|
|
537
|
+
Remote-controlled, missing or unknown source identities do not qualify.
|
|
538
|
+
|
|
539
|
+
The catalog retains local source paths separately from installed cache identities.
|
|
540
|
+
When a reviewed discovery witness binds a positively mapped installation input,
|
|
541
|
+
runtime checks omit that input from plugin skill loading. Default home/project
|
|
542
|
+
roots and installed caches are still checked, and migration inventory remains
|
|
543
|
+
complete. Overlapping, linked, missing or contradictory mappings refuse.
|
|
533
544
|
No prompt hook starts an app server to obtain this receipt:
|
|
534
545
|
native startup can maintain caches. The explicit capture starts and closes only its
|
|
535
546
|
own bounded native client; it does not reload an existing agent session.
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.26",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -39944,6 +39944,18 @@ function assertAgentPolicyCollections(policy) {
|
|
|
39944
39944
|
if (control.appSha256 !== undefined)
|
|
39945
39945
|
requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
|
|
39946
39946
|
}
|
|
39947
|
+
if (bridge.codexInactivePlugins !== undefined) {
|
|
39948
|
+
const controls = array(bridge.codexInactivePlugins, 4096), ids = new Set;
|
|
39949
|
+
requireBound(controls.length === 0 || object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === "codex-cli 0.160.0" && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
|
|
39950
|
+
for (const control of controls) {
|
|
39951
|
+
requireBound(object(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
|
|
39952
|
+
text(control.pluginId, 1024);
|
|
39953
|
+
text(control.namespace, 64);
|
|
39954
|
+
text(control.pluginParent);
|
|
39955
|
+
requireBound(!ids.has(control.pluginId) && isAbsolute2(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
|
|
39956
|
+
ids.add(control.pluginId);
|
|
39957
|
+
}
|
|
39958
|
+
}
|
|
39947
39959
|
if (bridge.disabledBuiltins !== undefined)
|
|
39948
39960
|
for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
|
|
39949
39961
|
requireBound(object(builtin));
|
|
@@ -39956,6 +39968,25 @@ function assertAgentPolicyCollections(policy) {
|
|
|
39956
39968
|
text(agent, 128);
|
|
39957
39969
|
requireBound(object(value));
|
|
39958
39970
|
text(value.agent, 128);
|
|
39971
|
+
if (value.codexInstallationInputs !== undefined) {
|
|
39972
|
+
const proof = record(value.codexInstallationInputs, 4), inputs = array(proof.plugins, 4096), ids = new Set;
|
|
39973
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && proof.version === "codex-cli 0.160.0" && typeof proof.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(proof.catalogSha256) && inputs.length > 0 && object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
|
|
39974
|
+
for (const input of inputs) {
|
|
39975
|
+
requireBound(object(input) && Object.keys(input).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
|
|
39976
|
+
text(input.pluginId, 1024);
|
|
39977
|
+
text(input.namespace, 64);
|
|
39978
|
+
text(input.pluginParent);
|
|
39979
|
+
text(input.sourceRoot);
|
|
39980
|
+
requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute2(path) && resolve3(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
|
|
39981
|
+
ids.add(input.pluginId);
|
|
39982
|
+
}
|
|
39983
|
+
if (proof.directories !== undefined)
|
|
39984
|
+
for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
39985
|
+
requireBound(object(directory));
|
|
39986
|
+
text(directory.path);
|
|
39987
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
39988
|
+
}
|
|
39989
|
+
}
|
|
39959
39990
|
for (const root of array(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
|
|
39960
39991
|
text(root);
|
|
39961
39992
|
for (const source of array(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
|
|
@@ -47235,7 +47266,7 @@ function safe(path) {
|
|
|
47235
47266
|
function same(before2, after2) {
|
|
47236
47267
|
return Boolean(after2 && after2.isDirectory() && before2.dev === after2.dev && before2.ino === after2.ino && before2.mtimeMs === after2.mtimeMs && before2.ctimeMs === after2.ctimeMs);
|
|
47237
47268
|
}
|
|
47238
|
-
function capture(path, budget) {
|
|
47269
|
+
function capture(path, budget, excludedRoots) {
|
|
47239
47270
|
safe(path);
|
|
47240
47271
|
const root = lstatSync4(path, { throwIfNoEntry: false });
|
|
47241
47272
|
if (!root)
|
|
@@ -47264,7 +47295,7 @@ function capture(path, budget) {
|
|
|
47264
47295
|
budget.bytes += Buffer.byteLength(row);
|
|
47265
47296
|
need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
47266
47297
|
hash.update(row);
|
|
47267
|
-
if (stat.isDirectory())
|
|
47298
|
+
if (stat.isDirectory() && !excludedRoots.includes(child))
|
|
47268
47299
|
walk(child, childRelative, stat, depth + 1);
|
|
47269
47300
|
}
|
|
47270
47301
|
need(same(before2, lstatSync4(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
@@ -47273,16 +47304,18 @@ function capture(path, budget) {
|
|
|
47273
47304
|
safe(path);
|
|
47274
47305
|
return { path, sha256: hash.digest("hex") };
|
|
47275
47306
|
}
|
|
47276
|
-
function captureDiscoveryDirectories(paths) {
|
|
47307
|
+
function captureDiscoveryDirectories(paths, excludedRoots = []) {
|
|
47277
47308
|
need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
47278
47309
|
const budget = { entries: 0, bytes: 0 };
|
|
47279
|
-
|
|
47310
|
+
for (const root of excludedRoots)
|
|
47311
|
+
safe(root);
|
|
47312
|
+
return paths.map((path) => capture(path, budget, excludedRoots));
|
|
47280
47313
|
}
|
|
47281
|
-
function verifyDiscoveryDirectories(directories) {
|
|
47314
|
+
function verifyDiscoveryDirectories(directories, excludedRoots = []) {
|
|
47282
47315
|
need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
47283
47316
|
for (const directory of directories)
|
|
47284
47317
|
need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
|
|
47285
|
-
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
|
|
47318
|
+
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
|
|
47286
47319
|
for (let i = 0;i < directories.length; i++)
|
|
47287
47320
|
need(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
|
|
47288
47321
|
}
|
|
@@ -51059,9 +51092,22 @@ var init_codex_hook_rpc = __esm(() => {
|
|
|
51059
51092
|
// src/lib/codex-native-skill-catalog.ts
|
|
51060
51093
|
import { realpathSync as realpathSync4 } from "fs";
|
|
51061
51094
|
import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
|
|
51095
|
+
function codexPluginSourceIsConfigControlled(plugin) {
|
|
51096
|
+
const remoteRealms = ["openai-curated-remote", "created-by-me-remote", "workspace-directory", "workspace-shared-with-me", "workspace-shared-with-me-private", "workspace-shared-with-me-unlisted"];
|
|
51097
|
+
return Boolean(plugin.sourceType && ["local", "git", "npm"].includes(plugin.sourceType) && plugin.sourceSha256 && /^[a-f0-9]{64}$/.test(plugin.sourceSha256) && !plugin.remotePluginId && !remoteRealms.includes(plugin.id.slice(plugin.id.lastIndexOf("@") + 1)));
|
|
51098
|
+
}
|
|
51062
51099
|
function refuse() {
|
|
51063
51100
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
|
|
51064
51101
|
}
|
|
51102
|
+
function projectPluginSource(source) {
|
|
51103
|
+
if (!object4(source))
|
|
51104
|
+
return {};
|
|
51105
|
+
if (source.type === "remote")
|
|
51106
|
+
return { sourceType: "remote" };
|
|
51107
|
+
const optionalText = (value) => value === undefined || value === null || scalar(value, MAX_PATH_BYTES);
|
|
51108
|
+
const valid = source.type === "local" ? Object.keys(source).every((key) => ["type", "path"].includes(key)) && absolutePath2(source.path) : source.type === "git" ? Object.keys(source).every((key) => ["type", "url", "path", "refName", "sha"].includes(key)) && scalar(source.url, MAX_PATH_BYTES) && [source.path, source.refName, source.sha].every(optionalText) : source.type === "npm" ? Object.keys(source).every((key) => ["type", "package", "version", "registry"].includes(key)) && scalar(source.package, MAX_NAME_BYTES) && [source.version, source.registry].every(optionalText) : false;
|
|
51109
|
+
return valid ? { sourceType: source.type, sourceSha256: hashNativeJsonControls(JSON.stringify(source)), ...source.type === "local" ? { sourcePath: source.path } : {} } : {};
|
|
51110
|
+
}
|
|
51065
51111
|
function projectCodexNativeSkillCatalog(response, cwd2) {
|
|
51066
51112
|
if (!absolutePath2(cwd2) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
|
|
51067
51113
|
refuse();
|
|
@@ -51091,7 +51137,10 @@ function projectCodexInstalledPlugins(response) {
|
|
|
51091
51137
|
plugins.push({
|
|
51092
51138
|
...value,
|
|
51093
51139
|
marketplace: marketplace.name,
|
|
51094
|
-
sourceType:
|
|
51140
|
+
sourceType: undefined,
|
|
51141
|
+
sourceSha256: undefined,
|
|
51142
|
+
sourcePath: undefined,
|
|
51143
|
+
...projectPluginSource(value.source)
|
|
51095
51144
|
});
|
|
51096
51145
|
}
|
|
51097
51146
|
}
|
|
@@ -51102,7 +51151,7 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
51102
51151
|
refuse();
|
|
51103
51152
|
const ids = new Set;
|
|
51104
51153
|
return value.map((item) => {
|
|
51105
|
-
if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && item.sourceType !== "
|
|
51154
|
+
if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && !["remote", "local", "git", "npm"].includes(item.sourceType) || (["local", "git", "npm"].includes(item.sourceType) ? typeof item.sourceSha256 !== "string" || !/^[a-f0-9]{64}$/.test(item.sourceSha256) : item.sourceSha256 !== undefined) || (item.sourceType === "local" ? !absolutePath2(item.sourcePath) || normalize2(item.sourcePath) !== item.sourcePath || item.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: item.sourcePath })) : item.sourcePath !== undefined))
|
|
51106
51155
|
refuse();
|
|
51107
51156
|
const at2 = item.id.lastIndexOf("@");
|
|
51108
51157
|
if (at2 < 1 || item.id.slice(0, at2) !== item.name || at2 === item.id.length - 1 || ids.has(item.id))
|
|
@@ -51114,7 +51163,9 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
51114
51163
|
installed: item.installed,
|
|
51115
51164
|
enabled: item.enabled,
|
|
51116
51165
|
localVersion: item.localVersion ?? null,
|
|
51117
|
-
...item.sourceType ===
|
|
51166
|
+
...item.sourceType === undefined ? {} : { sourceType: item.sourceType },
|
|
51167
|
+
...item.sourceSha256 === undefined ? {} : { sourceSha256: item.sourceSha256 },
|
|
51168
|
+
...item.sourcePath === undefined ? {} : { sourcePath: item.sourcePath },
|
|
51118
51169
|
...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
|
|
51119
51170
|
};
|
|
51120
51171
|
});
|
|
@@ -51184,6 +51235,7 @@ var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384, MAX_PLUGIN
|
|
|
51184
51235
|
var init_codex_native_skill_catalog = __esm(() => {
|
|
51185
51236
|
init_codex_hook_rpc();
|
|
51186
51237
|
init_codex_native_compatibility();
|
|
51238
|
+
init_claude_settings_witness();
|
|
51187
51239
|
});
|
|
51188
51240
|
|
|
51189
51241
|
// src/lib/codex-plugin-skill-controls.ts
|
|
@@ -51332,7 +51384,7 @@ function rootControls(root, read2) {
|
|
|
51332
51384
|
const appSha256 = present ? appControls(app, read2) : undefined;
|
|
51333
51385
|
return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
|
|
51334
51386
|
}
|
|
51335
|
-
function
|
|
51387
|
+
function documentIdentity(document3, cache3, read2) {
|
|
51336
51388
|
if (!document3.startsWith(cache3 + sep4) || !document3.endsWith(sep4 + "SKILL.md"))
|
|
51337
51389
|
refuse2();
|
|
51338
51390
|
const text2 = read2(document3);
|
|
@@ -51360,11 +51412,33 @@ function identity4(document3, cache3, read2) {
|
|
|
51360
51412
|
const path = join18(root, ".codex-plugin", "plugin.json");
|
|
51361
51413
|
if (!lstatSync12(path, { throwIfNoEntry: false }))
|
|
51362
51414
|
continue;
|
|
51363
|
-
const
|
|
51364
|
-
|
|
51415
|
+
const manifestFile = lstatSync12(path);
|
|
51416
|
+
if (!lstatSync12(join18(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
|
|
51417
|
+
refuse2();
|
|
51418
|
+
let manifest;
|
|
51419
|
+
try {
|
|
51420
|
+
const text3 = read2(path);
|
|
51421
|
+
hashNativeJsonControls(text3);
|
|
51422
|
+
manifest = JSON.parse(text3);
|
|
51423
|
+
} catch {
|
|
51424
|
+
refuse2();
|
|
51425
|
+
}
|
|
51426
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
51427
|
+
refuse2();
|
|
51428
|
+
const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
51429
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename5(root) : basename5(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
51430
|
+
refuse2();
|
|
51431
|
+
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname10(root), root };
|
|
51365
51432
|
}
|
|
51366
51433
|
refuse2();
|
|
51367
51434
|
}
|
|
51435
|
+
function identity4(document3, cache3, read2) {
|
|
51436
|
+
const parsed = documentIdentity(document3, cache3, read2);
|
|
51437
|
+
const controls = rootControls(parsed.root, read2);
|
|
51438
|
+
if (controls.namespace !== parsed.namespace)
|
|
51439
|
+
refuse2();
|
|
51440
|
+
return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
|
|
51441
|
+
}
|
|
51368
51442
|
function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
51369
51443
|
if (!Array.isArray(controls) || controls.length > 4096)
|
|
51370
51444
|
return false;
|
|
@@ -51402,11 +51476,13 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
|
51402
51476
|
return false;
|
|
51403
51477
|
}
|
|
51404
51478
|
}
|
|
51405
|
-
function
|
|
51479
|
+
function reviewCodexPluginControls(catalog, documents, cache3, cwd2, read2, rules, pluginSettings) {
|
|
51406
51480
|
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd2)
|
|
51407
51481
|
refuse2();
|
|
51408
51482
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: cwd2, errors: [], skills: catalog.skills }] }, cwd2);
|
|
51409
|
-
const allowed = new Set(documents), result2 = [];
|
|
51483
|
+
const allowed = new Set(documents), result2 = [], inertDocuments = new Set;
|
|
51484
|
+
const inactivePlugins = new Map;
|
|
51485
|
+
const sourceInputs = new Map;
|
|
51410
51486
|
let installedPlugins;
|
|
51411
51487
|
try {
|
|
51412
51488
|
installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
|
|
@@ -51417,25 +51493,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2,
|
|
|
51417
51493
|
for (const document3 of candidates) {
|
|
51418
51494
|
if (!document3.startsWith(cache3 + sep4))
|
|
51419
51495
|
continue;
|
|
51420
|
-
const
|
|
51421
|
-
if (
|
|
51496
|
+
const located = documentIdentity(document3, cache3, read2);
|
|
51497
|
+
if (located.name === "skills-cli" || located.name.endsWith(":skills-cli"))
|
|
51422
51498
|
refuse2();
|
|
51423
|
-
const cacheParts = relative4(cache3,
|
|
51499
|
+
const cacheParts = relative4(cache3, located.pluginParent).split(sep4);
|
|
51424
51500
|
if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
|
|
51425
51501
|
refuse2();
|
|
51426
51502
|
const [marketplace, pluginName] = cacheParts;
|
|
51427
|
-
if (pluginName !==
|
|
51503
|
+
if (pluginName !== located.namespace)
|
|
51428
51504
|
refuse2();
|
|
51429
51505
|
const expectedPluginId = `${pluginName}@${marketplace}`;
|
|
51430
51506
|
const listed = skills.filter((skill) => skill.path === document3);
|
|
51431
51507
|
if (listed.length > 1)
|
|
51432
51508
|
refuse2();
|
|
51509
|
+
const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
|
|
51510
|
+
if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename5(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
|
|
51511
|
+
const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
|
|
51512
|
+
if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache3 + sep4) && (lstatSync12(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep4)))) {
|
|
51513
|
+
if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep4)) || !sourceInputUnchanged(input, cache3, read2))
|
|
51514
|
+
refuse2();
|
|
51515
|
+
sourceInputs.set(expectedPluginId, input);
|
|
51516
|
+
}
|
|
51517
|
+
}
|
|
51518
|
+
const settings = pluginSettings && typeof pluginSettings === "object" && !Array.isArray(pluginSettings) ? pluginSettings : undefined;
|
|
51519
|
+
if (catalog.version === "codex-cli 0.160.0" && !listed.length && !skills.some((skill) => skill.pluginId === expectedPluginId) && installed.length === 1 && installed[0].installed && !installed[0].enabled && codexPluginSourceIsConfigControlled(installed[0]) && settings && Object.hasOwn(settings, expectedPluginId) && settings[expectedPluginId]?.enabled === false) {
|
|
51520
|
+
if (!Array.isArray(rules))
|
|
51521
|
+
refuse2();
|
|
51522
|
+
const exactPathDeny = rules.some((rule) => rule?.path === document3 && rule?.enabled === false);
|
|
51523
|
+
if (!lstatSync12(document3).isFile() || realpathSync5(document3) !== document3 || realpathSync5(located.root) !== located.root || realpathSync5(located.pluginParent) !== located.pluginParent || realpathSync5(join18(located.root, ".codex-plugin/plugin.json")) !== join18(located.root, ".codex-plugin/plugin.json") || !exactPathDeny || rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === located.name && rule?.enabled !== false) || !isCodexNativeSkillDisabled({ name: located.name, path: document3, pluginId: expectedPluginId, enabled: true }, rules))
|
|
51524
|
+
refuse2();
|
|
51525
|
+
inertDocuments.add(document3);
|
|
51526
|
+
const plugin = installed[0];
|
|
51527
|
+
inactivePlugins.set(expectedPluginId, { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceType: plugin.sourceType, sourceSha256: plugin.sourceSha256 });
|
|
51528
|
+
continue;
|
|
51529
|
+
}
|
|
51530
|
+
const parsed = { name: located.name, pluginParent: located.pluginParent, ...rootControls(located.root, read2) };
|
|
51531
|
+
if (parsed.namespace !== located.namespace)
|
|
51532
|
+
refuse2();
|
|
51433
51533
|
let remotePluginId;
|
|
51434
51534
|
if (listed.length === 1) {
|
|
51435
51535
|
if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
|
|
51436
51536
|
refuse2();
|
|
51437
51537
|
} else {
|
|
51438
|
-
const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
|
|
51439
51538
|
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
|
|
51440
51539
|
refuse2();
|
|
51441
51540
|
const plugin = installed[0];
|
|
@@ -51453,9 +51552,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2,
|
|
|
51453
51552
|
}
|
|
51454
51553
|
result2.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
51455
51554
|
}
|
|
51456
|
-
if (!result2.length && documents.some((document3) => document3.startsWith(cache3 + sep4)) || new Set(result2.map((item) => item.name)).size !== result2.length)
|
|
51555
|
+
if (!result2.length && documents.some((document3) => document3.startsWith(cache3 + sep4) && !inertDocuments.has(document3)) || new Set(result2.map((item) => item.name)).size !== result2.length)
|
|
51457
51556
|
refuse2();
|
|
51458
|
-
return result2;
|
|
51557
|
+
return { skills: result2, inactivePlugins: [...inactivePlugins.values()], sourceInputs: [...sourceInputs.values()] };
|
|
51558
|
+
}
|
|
51559
|
+
function sourceInputUnchanged(input, cache3, read2) {
|
|
51560
|
+
try {
|
|
51561
|
+
const parts = relative4(cache3, input.pluginParent).split(sep4);
|
|
51562
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve10(input.sourceRoot) !== input.sourceRoot || !lstatSync12(input.sourceRoot).isDirectory() || realpathSync5(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache3 || input.sourceRoot.startsWith(cache3 + sep4) || cache3.startsWith(input.sourceRoot + sep4))
|
|
51563
|
+
return false;
|
|
51564
|
+
const path = join18(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync12(path);
|
|
51565
|
+
if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync5(path) !== path)
|
|
51566
|
+
return false;
|
|
51567
|
+
const text2 = read2(path);
|
|
51568
|
+
hashNativeJsonControls(text2);
|
|
51569
|
+
const manifest = JSON.parse(text2);
|
|
51570
|
+
return Boolean(manifest && typeof manifest === "object" && !Array.isArray(manifest) && manifest.name === input.namespace);
|
|
51571
|
+
} catch {
|
|
51572
|
+
return false;
|
|
51573
|
+
}
|
|
51574
|
+
}
|
|
51575
|
+
function reviewedCodexPluginSourceRoots(cache3, inputs, read2) {
|
|
51576
|
+
if (!Array.isArray(inputs) || inputs.length > 4096 || new Set(inputs.map((input) => input.pluginId)).size !== inputs.length || new Set(inputs.map((input) => input.sourceRoot)).size !== inputs.length || inputs.some((input) => !sourceInputUnchanged(input, cache3, read2)))
|
|
51577
|
+
refuse2();
|
|
51578
|
+
const roots = inputs.map((input) => input.sourceRoot);
|
|
51579
|
+
if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep4))))
|
|
51580
|
+
refuse2();
|
|
51581
|
+
return roots;
|
|
51582
|
+
}
|
|
51583
|
+
function isReviewedCodexPluginInactive(document3, cache3, controls, pluginSettings, rules, read2) {
|
|
51584
|
+
if (!Array.isArray(controls) || controls.length > 4096 || !pluginSettings || typeof pluginSettings !== "object" || Array.isArray(pluginSettings) || !Array.isArray(rules))
|
|
51585
|
+
return false;
|
|
51586
|
+
try {
|
|
51587
|
+
const parsed = documentIdentity(document3, cache3, read2), matches2 = controls.filter((control2) => control2.pluginParent === parsed.pluginParent && control2.namespace === parsed.namespace);
|
|
51588
|
+
if (matches2.length !== 1 || !lstatSync12(document3).isFile() || realpathSync5(document3) !== document3 || realpathSync5(parsed.root) !== parsed.root || realpathSync5(parsed.pluginParent) !== parsed.pluginParent || realpathSync5(join18(parsed.root, ".codex-plugin/plugin.json")) !== join18(parsed.root, ".codex-plugin/plugin.json"))
|
|
51589
|
+
return false;
|
|
51590
|
+
const control = matches2[0], parts = relative4(cache3, parsed.pluginParent).split(sep4);
|
|
51591
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace || control.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ ...control, id: control.pluginId }) || !Object.hasOwn(pluginSettings, control.pluginId) || pluginSettings[control.pluginId]?.enabled !== false)
|
|
51592
|
+
return false;
|
|
51593
|
+
return !rules.some((rule) => (rule?.path === document3 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule?.enabled !== false);
|
|
51594
|
+
} catch {
|
|
51595
|
+
return false;
|
|
51596
|
+
}
|
|
51459
51597
|
}
|
|
51460
51598
|
function isReviewedCodexPluginSkillDisabled(document3, cache3, controls, rules, read2) {
|
|
51461
51599
|
if (!document3.startsWith(cache3 + sep4) || !Array.isArray(controls) || controls.length > 4096)
|
|
@@ -52465,6 +52603,9 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52465
52603
|
for (const [agent, path] of rootDefinitions)
|
|
52466
52604
|
roots.push([agent, canonicalAgentPath(join21(project, path), aliases)]);
|
|
52467
52605
|
}
|
|
52606
|
+
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
52607
|
+
if (installationInputs.some((input) => resolve11(input) !== input || realpathSync6(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
|
|
52608
|
+
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
52468
52609
|
const entries = [], seen = new Set;
|
|
52469
52610
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
52470
52611
|
let discoveryEntries = 0, discoveryPathBytes = 0;
|
|
@@ -52499,11 +52640,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52499
52640
|
}
|
|
52500
52641
|
return true;
|
|
52501
52642
|
}
|
|
52502
|
-
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
52643
|
+
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false, configuredRoot = false) {
|
|
52503
52644
|
if (!admitted)
|
|
52504
52645
|
admit(path);
|
|
52505
52646
|
const scan = { complete: true, hasSkills: false, entries: 1 };
|
|
52506
52647
|
assertSafePath(path);
|
|
52648
|
+
if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep5}`)))
|
|
52649
|
+
return scan;
|
|
52507
52650
|
if (!existsSync12(path))
|
|
52508
52651
|
return scan;
|
|
52509
52652
|
const stat = lstatSync15(path);
|
|
@@ -52559,7 +52702,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52559
52702
|
continue;
|
|
52560
52703
|
}
|
|
52561
52704
|
}
|
|
52562
|
-
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
|
|
52705
|
+
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
|
|
52563
52706
|
scan.complete &&= childScan.complete;
|
|
52564
52707
|
scan.hasSkills ||= childScan.hasSkills;
|
|
52565
52708
|
scan.entries += childScan.entries;
|
|
@@ -52568,10 +52711,10 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52568
52711
|
cacheScans.set(path, scan);
|
|
52569
52712
|
return scan;
|
|
52570
52713
|
}
|
|
52571
|
-
function scanRoot(agent, path, vendor = false, pluginCache = false) {
|
|
52714
|
+
function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
|
|
52572
52715
|
if (agent === "hermes" || options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`))
|
|
52573
52716
|
assertNoHermesLegacyShadow(path);
|
|
52574
|
-
if (!visit2(agent, path, vendor, 0, pluginCache).complete)
|
|
52717
|
+
if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
|
|
52575
52718
|
throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
|
|
52576
52719
|
}
|
|
52577
52720
|
for (const [agent, path] of roots)
|
|
@@ -52587,7 +52730,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52587
52730
|
const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
52588
52731
|
for (const root of [...options.agentRoots ?? [], ...configured])
|
|
52589
52732
|
if (includesAgent(root.agent))
|
|
52590
|
-
scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true);
|
|
52733
|
+
scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true, false, true);
|
|
52591
52734
|
if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
|
|
52592
52735
|
throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
|
|
52593
52736
|
recheckRootAliases(aliases);
|
|
@@ -52727,6 +52870,11 @@ enabled = false
|
|
|
52727
52870
|
Bun.TOML.parse(result2);
|
|
52728
52871
|
return result2;
|
|
52729
52872
|
}
|
|
52873
|
+
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
52874
|
+
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve11(directory, path)));
|
|
52875
|
+
if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep5) || input.startsWith(root + sep5))))
|
|
52876
|
+
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
52877
|
+
}
|
|
52730
52878
|
function planAgentIntegration(options) {
|
|
52731
52879
|
const home = options.home ?? homedir3(), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
52732
52880
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
@@ -52765,11 +52913,16 @@ function planAgentIntegration(options) {
|
|
|
52765
52913
|
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases)) ?? "") : {};
|
|
52766
52914
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
|
|
52767
52915
|
const observedNativeSources = [];
|
|
52768
|
-
const
|
|
52916
|
+
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join21(entry.path, "SKILL.md")), canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), resolve11(options.projectDir ?? home), (path) => {
|
|
52769
52917
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
52770
52918
|
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
52771
52919
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
52772
|
-
}, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
|
|
52920
|
+
}, codexConfig.skills?.config ?? [], codexConfig.plugins) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
|
|
52921
|
+
const codexPluginSkills = codexPluginControls.skills;
|
|
52922
|
+
const codexInactivePlugins = codexPluginControls.inactivePlugins;
|
|
52923
|
+
const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
|
|
52924
|
+
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join21(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
|
|
52925
|
+
assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
|
|
52773
52926
|
const changes = [];
|
|
52774
52927
|
for (const agent of [...new Set(options.agents)]) {
|
|
52775
52928
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
@@ -52834,7 +52987,16 @@ function planAgentIntegration(options) {
|
|
|
52834
52987
|
changes.push({ path: configPath, before: previous, after: next });
|
|
52835
52988
|
}
|
|
52836
52989
|
}
|
|
52837
|
-
const
|
|
52990
|
+
const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join21(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join21(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
|
|
52991
|
+
const discoveryAfter = discoveries.map((binding) => {
|
|
52992
|
+
const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
|
|
52993
|
+
if (binding.agent !== "codex")
|
|
52994
|
+
return rebound;
|
|
52995
|
+
const { codexInstallationInputs: previousInputs, ...current } = rebound;
|
|
52996
|
+
const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
|
|
52997
|
+
const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep5))).map((directory) => directory.path), inputs) : previousInputs.directories;
|
|
52998
|
+
return codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
|
|
52999
|
+
});
|
|
52838
53000
|
const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
|
|
52839
53001
|
...policy.bridge,
|
|
52840
53002
|
version: CLI_BRIDGE_VERSION,
|
|
@@ -52847,7 +53009,7 @@ function planAgentIntegration(options) {
|
|
|
52847
53009
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
52848
53010
|
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
52849
53011
|
rootAliases: aliases,
|
|
52850
|
-
...
|
|
53012
|
+
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
52851
53013
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
52852
53014
|
} };
|
|
52853
53015
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
@@ -53257,7 +53419,12 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
53257
53419
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
53258
53420
|
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
|
|
53259
53421
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
|
|
53260
|
-
const disabledPlugin = (entry) =>
|
|
53422
|
+
const disabledPlugin = (entry) => {
|
|
53423
|
+
if (agent !== "codex" || !entry.vendor)
|
|
53424
|
+
return false;
|
|
53425
|
+
const document3 = join21(entry.path, "SKILL.md"), cache3 = canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
|
|
53426
|
+
return isReviewedCodexPluginInactive(document3, cache3, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document3, cache3, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
|
|
53427
|
+
};
|
|
53261
53428
|
const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
|
|
53262
53429
|
const disabledVendorSkill = (entry) => {
|
|
53263
53430
|
if (agent !== "codex" || !entry.vendor)
|
|
@@ -53294,7 +53461,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
53294
53461
|
} catch (error) {
|
|
53295
53462
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
53296
53463
|
}
|
|
53297
|
-
|
|
53464
|
+
let installationInputRoots = [];
|
|
53465
|
+
if (agent === "codex") {
|
|
53466
|
+
try {
|
|
53467
|
+
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
|
|
53468
|
+
} catch {
|
|
53469
|
+
throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
|
|
53470
|
+
}
|
|
53471
|
+
}
|
|
53472
|
+
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0, disabledVendorPaths: agent === "codex" ? disabledCodexSkillPaths(codexConfig, aliases) : [], disabledVendorSkill: disabledPlugin, codexInstallationInputRoots: installationInputRoots });
|
|
53298
53473
|
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
|
|
53299
53474
|
if (unexpected.length) {
|
|
53300
53475
|
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
@@ -56666,8 +56841,16 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
56666
56841
|
throw new Error("Invalid native discovery binding");
|
|
56667
56842
|
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
56668
56843
|
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
56669
|
-
|
|
56670
|
-
|
|
56844
|
+
const installationRoots = codexInstallationRoots(binding);
|
|
56845
|
+
const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep6));
|
|
56846
|
+
if (binding.directories !== undefined) {
|
|
56847
|
+
const projected2 = binding.codexInstallationInputs?.directories ?? [];
|
|
56848
|
+
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep6)));
|
|
56849
|
+
if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
|
|
56850
|
+
throw new Error("Missing installation input directory projection");
|
|
56851
|
+
verifyDiscoveryDirectories(binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory)));
|
|
56852
|
+
verifyDiscoveryDirectories(projected2, installationRoots);
|
|
56853
|
+
}
|
|
56671
56854
|
const budget = discoveryByteBudget();
|
|
56672
56855
|
for (const source of binding.sources) {
|
|
56673
56856
|
assertMarketplaceBinding(binding, source);
|
|
@@ -56677,6 +56860,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
56677
56860
|
throw new Error("Invalid native discovery projection");
|
|
56678
56861
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
56679
56862
|
throw new Error("Invalid native discovery digest");
|
|
56863
|
+
if (installationInput(source.path)) {
|
|
56864
|
+
safe4(source.path);
|
|
56865
|
+
continue;
|
|
56866
|
+
}
|
|
56680
56867
|
const current = projected(source, undefined, budget);
|
|
56681
56868
|
if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
|
|
56682
56869
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
@@ -56684,6 +56871,27 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
56684
56871
|
for (const root2 of binding.roots)
|
|
56685
56872
|
safe4(root2);
|
|
56686
56873
|
}
|
|
56874
|
+
function codexInstallationRoots(binding) {
|
|
56875
|
+
const proof = binding.codexInstallationInputs;
|
|
56876
|
+
if (proof === undefined)
|
|
56877
|
+
return [];
|
|
56878
|
+
if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
56879
|
+
throw new Error("Invalid native installation input proof");
|
|
56880
|
+
const cache3 = dirname17(dirname17(proof.plugins[0].pluginParent));
|
|
56881
|
+
const config = join32(dirname17(dirname17(cache3)), "config.toml"), home = dirname17(dirname17(dirname17(cache3)));
|
|
56882
|
+
const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
56883
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
|
|
56884
|
+
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep6) || input.sourceRoot.startsWith(root2 + sep6)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep6)))))
|
|
56885
|
+
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
56886
|
+
if (proof.plugins.some((input) => dirname17(dirname17(input.pluginParent)) !== cache3 || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
56887
|
+
throw new Error("Missing native installation input source witness");
|
|
56888
|
+
return reviewedCodexPluginSourceRoots(cache3, proof.plugins, (path) => {
|
|
56889
|
+
const text3 = read2(path);
|
|
56890
|
+
if (text3 === null)
|
|
56891
|
+
throw new Error("Missing native installation input");
|
|
56892
|
+
return text3;
|
|
56893
|
+
});
|
|
56894
|
+
}
|
|
56687
56895
|
function assertMarketplaceBinding(binding, source) {
|
|
56688
56896
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
56689
56897
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
@@ -56694,8 +56902,11 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
56694
56902
|
}
|
|
56695
56903
|
function rebindAgentDiscovery(binding, changes) {
|
|
56696
56904
|
const budget = discoveryByteBudget();
|
|
56905
|
+
const installationRoots = codexInstallationRoots(binding);
|
|
56697
56906
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
56698
56907
|
assertMarketplaceBinding(binding, source);
|
|
56908
|
+
if (!changes.has(source.path) && installationRoots.some((root2) => source.path.startsWith(root2 + sep6)))
|
|
56909
|
+
return source;
|
|
56699
56910
|
const sha2562 = projected(source, changes, budget);
|
|
56700
56911
|
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
56701
56912
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
@@ -56966,6 +57177,8 @@ var init_agent_discovery = __esm(() => {
|
|
|
56966
57177
|
init_claude_settings_witness();
|
|
56967
57178
|
init_codex_hook_discovery_recovery();
|
|
56968
57179
|
init_codex_settings_witness();
|
|
57180
|
+
init_codex_plugin_skill_controls();
|
|
57181
|
+
init_native_discovery_roots();
|
|
56969
57182
|
init_agent_discovery_directories();
|
|
56970
57183
|
});
|
|
56971
57184
|
|