@hasna/skills 0.10.90 → 0.10.92
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 +12 -3
- package/THIRD_PARTY_NOTICES +5926 -0
- package/bin/index.js +818 -1809
- package/bin/mcp.js +328 -1376
- package/bin/migrate.js +8 -5
- package/bin/server.js +73819 -12659
- package/bin/worker.js +60586 -7855
- package/dist/index.js +255 -27
- package/dist/lib/agent-discovery.d.ts +14 -1
- package/dist/lib/execution-secrets.d.ts +17 -1
- package/dist/runtime-prerequisites.js +8 -5
- package/dist/sdk/index.js +79215 -19013
- package/package.json +8 -5
package/dist/index.js
CHANGED
|
@@ -16369,7 +16369,7 @@ import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute2
|
|
|
16369
16369
|
import { homedir as homedir5 } from "os";
|
|
16370
16370
|
import { isAbsolute as isAbsolute25, join as join39, resolve as resolve25 } from "path";
|
|
16371
16371
|
// package.json
|
|
16372
|
-
var version = "0.10.
|
|
16372
|
+
var version = "0.10.92";
|
|
16373
16373
|
var package_default = {
|
|
16374
16374
|
name: "@hasna/skills",
|
|
16375
16375
|
version,
|
|
@@ -16416,6 +16416,7 @@ var package_default = {
|
|
|
16416
16416
|
"docs/runtime-activation.md",
|
|
16417
16417
|
"schemas/",
|
|
16418
16418
|
"LICENSE",
|
|
16419
|
+
"THIRD_PARTY_NOTICES",
|
|
16419
16420
|
"README.md",
|
|
16420
16421
|
"!dist/**/*test-utils.d.ts",
|
|
16421
16422
|
"!dist/**/*fixtures.d.ts"
|
|
@@ -16423,9 +16424,9 @@ var package_default = {
|
|
|
16423
16424
|
main: "./dist/index.js",
|
|
16424
16425
|
types: "./dist/index.d.ts",
|
|
16425
16426
|
scripts: {
|
|
16426
|
-
clean: "rm -rf bin/ dist/",
|
|
16427
|
-
build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @modelcontextprotocol/sdk ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
16428
|
-
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
16427
|
+
clean: "rm -rf bin/ dist/ THIRD_PARTY_NOTICES",
|
|
16428
|
+
build: "bun run clean && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/secrets --external @modelcontextprotocol/sdk ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/secrets ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts && bun scripts/third-party-notices.ts",
|
|
16429
|
+
"build:js": "rm -rf dist && bun build --external @hasna/secrets ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
16429
16430
|
test: "bun test --timeout 30000",
|
|
16430
16431
|
dev: "bun run ./src/cli/index.tsx",
|
|
16431
16432
|
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
@@ -16476,7 +16477,6 @@ var package_default = {
|
|
|
16476
16477
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
16477
16478
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
16478
16479
|
"@hasna/events": "0.1.18",
|
|
16479
|
-
"@hasna/secrets": "0.4.2",
|
|
16480
16480
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
16481
16481
|
chalk: "^5.3.0",
|
|
16482
16482
|
commander: "^12.1.0",
|
|
@@ -16488,6 +16488,9 @@ var package_default = {
|
|
|
16488
16488
|
zod: "^4.3.6",
|
|
16489
16489
|
"@hasna/contracts": "1.3.5"
|
|
16490
16490
|
},
|
|
16491
|
+
optionalDependencies: {
|
|
16492
|
+
"@hasna/secrets": "0.4.2"
|
|
16493
|
+
},
|
|
16491
16494
|
engines: {
|
|
16492
16495
|
bun: ">=1.0.0"
|
|
16493
16496
|
},
|
|
@@ -17715,6 +17718,9 @@ async function resolveSkillsConnection(env = process.env, options = {}) {
|
|
|
17715
17718
|
completed = await completePointerCredential(SKILLS_APP, pointer, explicitVault ? snapshotEnv : env);
|
|
17716
17719
|
} catch (error) {
|
|
17717
17720
|
const translated = asSkillsFleetCredentialError(error, pointer.pointerVaultKey);
|
|
17721
|
+
if (translated?.message.includes("(@hasna/secrets) is not installed")) {
|
|
17722
|
+
throw new SkillsFleetCredentialError(`${translated.message} @hasna/secrets is optional and private: install it with access to it.`, "MISSING_API_CREDENTIAL");
|
|
17723
|
+
}
|
|
17718
17724
|
if (translated)
|
|
17719
17725
|
throw translated;
|
|
17720
17726
|
throw error;
|
|
@@ -26727,9 +26733,96 @@ function nativeDiscoveryAliasTargets(roots, home) {
|
|
|
26727
26733
|
}
|
|
26728
26734
|
return [...targets];
|
|
26729
26735
|
}
|
|
26736
|
+
function hooksAreManagedBridge(agent, hooks, managed) {
|
|
26737
|
+
const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
26738
|
+
const exactKeys3 = (value, keys2) => Object.keys(value).sort().join("\x00") === [...keys2].sort().join("\x00");
|
|
26739
|
+
if (!table(hooks) || typeof managed.command !== "string" || !isAbsolute28(managed.command))
|
|
26740
|
+
return false;
|
|
26741
|
+
const events3 = agent === "gemini" ? ["BeforeAgent", "SessionStart", "BeforeTool"] : ["beforeSubmitPrompt", "sessionStart"];
|
|
26742
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
26743
|
+
if (!events3.includes(event) || !Array.isArray(entries) || entries.length !== 1)
|
|
26744
|
+
return false;
|
|
26745
|
+
let expected;
|
|
26746
|
+
try {
|
|
26747
|
+
expected = renderAgentHookCommand(managed.command, agent, managed.profile, event);
|
|
26748
|
+
} catch {
|
|
26749
|
+
return false;
|
|
26750
|
+
}
|
|
26751
|
+
const entry = entries[0];
|
|
26752
|
+
if (!table(entry))
|
|
26753
|
+
return false;
|
|
26754
|
+
if (agent === "cursor") {
|
|
26755
|
+
if (!exactKeys3(entry, ["command", "timeout"]) || entry.timeout !== 15 || entry.command !== expected)
|
|
26756
|
+
return false;
|
|
26757
|
+
continue;
|
|
26758
|
+
}
|
|
26759
|
+
if (!exactKeys3(entry, event === "BeforeTool" ? ["hooks", "matcher"] : ["hooks"]) || event === "BeforeTool" && entry.matcher !== "activate_skill" || !Array.isArray(entry.hooks) || entry.hooks.length !== 1)
|
|
26760
|
+
return false;
|
|
26761
|
+
const hook = entry.hooks[0];
|
|
26762
|
+
if (!table(hook) || !exactKeys3(hook, ["command", "timeout", "type"]) || hook.type !== "command" || hook.timeout !== 15000 || hook.command !== expected)
|
|
26763
|
+
return false;
|
|
26764
|
+
}
|
|
26765
|
+
return true;
|
|
26766
|
+
}
|
|
26767
|
+
function sumiPluginLoadsOnlyBuiltins(text2) {
|
|
26768
|
+
for (const match of text2.matchAll(/\b(?:import|require)\s*\(\s*(.?)/g)) {
|
|
26769
|
+
const rest = text2.slice(match.index + match[0].length - match[1].length);
|
|
26770
|
+
if (!/^(['"])node:[A-Za-z0-9_/.-]+\1/.test(rest))
|
|
26771
|
+
return false;
|
|
26772
|
+
}
|
|
26773
|
+
for (const match of text2.matchAll(/\b(?:from|import)\s*(['"])([^'"]*)\1/g))
|
|
26774
|
+
if (!match[2].startsWith("node:"))
|
|
26775
|
+
return false;
|
|
26776
|
+
return true;
|
|
26777
|
+
}
|
|
26778
|
+
var CODEX_MARKETPLACE_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26779
|
+
function codexRootFreeControls(config) {
|
|
26780
|
+
const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date));
|
|
26781
|
+
const adapted = [];
|
|
26782
|
+
const servers = config.mcp_servers;
|
|
26783
|
+
if (table(servers) && Object.keys(servers).length <= AGENT_POLICY_LIMITS.discoveryRoots && Object.entries(servers).every(([name, row]) => name !== "codex_apps" && name !== "_codex_apps" && isCodexLocalStdioMcpServer(name, row)))
|
|
26784
|
+
adapted.push("mcp_servers");
|
|
26785
|
+
const agents = config.agents;
|
|
26786
|
+
if (table(agents) && Object.keys(agents).length <= AGENT_POLICY_LIMITS.fields && Object.values(agents).every((value) => typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)))
|
|
26787
|
+
adapted.push("agents");
|
|
26788
|
+
return adapted;
|
|
26789
|
+
}
|
|
26790
|
+
var CODEX_HOOK_EVENTS = new Set(["PreToolUse", "PermissionRequest", "PostToolUse", "PreCompact", "PostCompact", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart", "SubagentStop", "Stop", "Interrupt"]);
|
|
26791
|
+
function codexPluginHookHandlers(value) {
|
|
26792
|
+
const table = (item) => Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
26793
|
+
const identifier6 = (item) => typeof item === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(item);
|
|
26794
|
+
const text2 = (item) => typeof item === "string" && item.length <= AGENT_POLICY_LIMITS.pathCharacters && !item.includes("\x00");
|
|
26795
|
+
if (!table(value) || !Object.keys(value).every((key2) => key2 === "hooks" || key2 === "description") || value.description !== undefined && !text2(value.description) || !table(value.hooks) || Object.keys(value.hooks).length > 32)
|
|
26796
|
+
return null;
|
|
26797
|
+
const result = { commands: 0, servers: new Set };
|
|
26798
|
+
for (const [event, groups] of Object.entries(value.hooks)) {
|
|
26799
|
+
if (!CODEX_HOOK_EVENTS.has(event) || !Array.isArray(groups) || groups.length > 32)
|
|
26800
|
+
return null;
|
|
26801
|
+
for (const group of groups) {
|
|
26802
|
+
if (!table(group) || !Object.keys(group).every((key2) => key2 === "hooks" || key2 === "matcher") || group.matcher !== undefined && !text2(group.matcher) || !Array.isArray(group.hooks) || group.hooks.length > 32)
|
|
26803
|
+
return null;
|
|
26804
|
+
for (const handler of group.hooks) {
|
|
26805
|
+
if (!table(handler) || handler.timeout !== undefined && !(Number.isSafeInteger(handler.timeout) && handler.timeout >= 0) || handler.statusMessage !== undefined && !text2(handler.statusMessage))
|
|
26806
|
+
return null;
|
|
26807
|
+
if (handler.type === "command") {
|
|
26808
|
+
if (!text2(handler.command) || !handler.command || !Object.keys(handler).every((key2) => ["type", "command", "commandWindows", "command_windows", "timeout", "async", "statusMessage", "additionalContextLimit"].includes(key2)))
|
|
26809
|
+
return null;
|
|
26810
|
+
result.commands++;
|
|
26811
|
+
} else if (handler.type === "mcp_tool") {
|
|
26812
|
+
if (!identifier6(handler.server) || !identifier6(handler.tool) || handler.input !== undefined && !table(handler.input) || !Object.keys(handler).every((key2) => ["type", "server", "tool", "input", "timeout", "statusMessage"].includes(key2)))
|
|
26813
|
+
return null;
|
|
26814
|
+
result.servers.add(handler.server);
|
|
26815
|
+
} else
|
|
26816
|
+
return null;
|
|
26817
|
+
}
|
|
26818
|
+
}
|
|
26819
|
+
}
|
|
26820
|
+
return result;
|
|
26821
|
+
}
|
|
26730
26822
|
function resolveDiscovery(options, exclusion) {
|
|
26731
26823
|
const canonical9 = options.canonical ?? resolve28, home = resolve28(options.home), agent = options.agent;
|
|
26732
26824
|
const sources = [], roots = new Set, builtinNames = [];
|
|
26825
|
+
const sumiLocalPluginTexts = new Map;
|
|
26733
26826
|
const budget3 = discoveryByteBudget();
|
|
26734
26827
|
let remainingEntries = AGENT_POLICY_LIMITS.discoveryDirectoryEntries;
|
|
26735
26828
|
const entries = (path) => {
|
|
@@ -26806,12 +26899,38 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26806
26899
|
const controls = agent === "claude" ? claudeDiscoveryControlKeys(configText ?? "{}") : agent === "codex" ? codexDiscoveryControlKeys(config) : Object.keys(config).filter((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2));
|
|
26807
26900
|
const fieldKey = (key2) => /^[A-Za-z_$][A-Za-z0-9_$.-]{0,22}$/.test(key2) && !/(?:sk|rk|pk)_(?:live|test)_|whsec_/i.test(key2) ? JSON.stringify(key2) : "[withheld field key]";
|
|
26808
26901
|
const unsupportedFields = (names, label) => unresolved(`unknown discovery controls have no root-exclusion adapter for ${label}${names.slice(0, 8).map(fieldKey).join(", ")}${names.length > 8 ? ", additional keys withheld" : ""} in ${quotedPath(canonical9(configPath))}`);
|
|
26809
|
-
const
|
|
26902
|
+
const adapted = agent === "codex" ? codexRootFreeControls(config) : agent === "opencode" && Array.isArray(config.instructions) && config.instructions.length === 0 ? ["instructions"] : [];
|
|
26903
|
+
const unsupportedControls = controls.filter((key2) => !supported[agent].includes(key2) && !adapted.includes(key2));
|
|
26810
26904
|
if (unsupportedControls.length)
|
|
26811
26905
|
unsupportedFields(unsupportedControls, "");
|
|
26812
26906
|
if (config.hooks !== undefined && !object6(config.hooks))
|
|
26813
26907
|
unresolved("hooks must be an object");
|
|
26814
|
-
const
|
|
26908
|
+
const managedSupervisorOnly = () => {
|
|
26909
|
+
const supervisor = exclusion.managedHook?.supervisor;
|
|
26910
|
+
if (agent !== "hermes" || !supervisor || typeof supervisor.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(supervisor.sha256) || !object6(config.hooks))
|
|
26911
|
+
return false;
|
|
26912
|
+
let expected;
|
|
26913
|
+
try {
|
|
26914
|
+
expected = hermesHookDefinitions(supervisor);
|
|
26915
|
+
} catch {
|
|
26916
|
+
return false;
|
|
26917
|
+
}
|
|
26918
|
+
for (const [event, entries2] of Object.entries(config.hooks)) {
|
|
26919
|
+
const want = expected[event];
|
|
26920
|
+
if (!want || !Array.isArray(entries2) || entries2.length !== 1 || !object6(entries2[0]))
|
|
26921
|
+
return false;
|
|
26922
|
+
const entry = entries2[0];
|
|
26923
|
+
if (Object.keys(entry).sort().join("\x00") !== Object.keys(want).sort().join("\x00") || Object.keys(want).some((key2) => entry[key2] !== want[key2]))
|
|
26924
|
+
return false;
|
|
26925
|
+
}
|
|
26926
|
+
try {
|
|
26927
|
+
return hashRawDiscoveryFile(canonical9(supervisor.path), budget3) === supervisor.sha256;
|
|
26928
|
+
} catch {
|
|
26929
|
+
return false;
|
|
26930
|
+
}
|
|
26931
|
+
};
|
|
26932
|
+
const managedBridgeOnly = (agent === "gemini" || agent === "cursor") && exclusion.managedHook !== undefined && hooksAreManagedBridge(agent, config.hooks, exclusion.managedHook) || managedSupervisorOnly();
|
|
26933
|
+
const hasHooks = controls.includes("hooks") && config.hooks !== undefined && Object.keys(config.hooks).length > 0 && !managedBridgeOnly;
|
|
26815
26934
|
let hasPluginConfigs = false, hasFunctionHookEnvironment = false;
|
|
26816
26935
|
const verifyCurrentOpaqueSettings = (label) => {
|
|
26817
26936
|
const retained = exclusion.retained;
|
|
@@ -26916,7 +27035,9 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26916
27035
|
unresolved("malformed Sumi plugins");
|
|
26917
27036
|
}
|
|
26918
27037
|
if (agent === "hermes" && config.skills !== undefined) {
|
|
26919
|
-
keys2(config.skills, ["external_dirs"], "Hermes skills");
|
|
27038
|
+
keys2(config.skills, ["external_dirs", "creation_nudge_interval"], "Hermes skills");
|
|
27039
|
+
if (config.skills.creation_nudge_interval !== undefined && !(Number.isSafeInteger(config.skills.creation_nudge_interval) && config.skills.creation_nudge_interval >= 0))
|
|
27040
|
+
unresolved("malformed Hermes skills creation_nudge_interval");
|
|
26920
27041
|
if (config.skills.external_dirs !== undefined)
|
|
26921
27042
|
paths(typeof config.skills.external_dirs === "string" ? [config.skills.external_dirs] : config.skills.external_dirs, "Hermes external directories");
|
|
26922
27043
|
}
|
|
@@ -26936,10 +27057,21 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26936
27057
|
unresolved(`malformed Codex ${key2}`);
|
|
26937
27058
|
for (const value of Object.values(config.plugins ?? {}))
|
|
26938
27059
|
keys2(value, ["enabled"], "Codex plugin registration");
|
|
26939
|
-
for (const value of Object.values(config.marketplaces ?? {}))
|
|
26940
|
-
keys2(value, ["source_type", "source"], "Codex marketplace registration");
|
|
27060
|
+
for (const value of Object.values(config.marketplaces ?? {})) {
|
|
27061
|
+
keys2(value, ["source_type", "source", "last_updated"], "Codex marketplace registration");
|
|
27062
|
+
if (value.last_updated !== undefined && (typeof value.last_updated !== "string" || !CODEX_MARKETPLACE_TIMESTAMP.test(value.last_updated)))
|
|
27063
|
+
unresolved("malformed Codex marketplace refresh time");
|
|
27064
|
+
}
|
|
26941
27065
|
if (config.skills !== undefined)
|
|
26942
|
-
keys2(config.skills, ["config", "bundled"], "Codex skills");
|
|
27066
|
+
keys2(config.skills, ["config", "bundled", "native_allowed_paths"], "Codex skills");
|
|
27067
|
+
if (config.skills?.native_allowed_paths !== undefined) {
|
|
27068
|
+
for (const value of paths(config.skills.native_allowed_paths, "Codex native allowed skill paths")) {
|
|
27069
|
+
if (!isAbsolute28(value))
|
|
27070
|
+
unresolved("relative Codex native allowed skill path");
|
|
27071
|
+
const path = canonical9(value);
|
|
27072
|
+
roots.add(path.endsWith(`${sep15}SKILL.md`) ? dirname29(path) : path);
|
|
27073
|
+
}
|
|
27074
|
+
}
|
|
26943
27075
|
if (config.skills?.bundled !== undefined) {
|
|
26944
27076
|
keys2(config.skills.bundled, ["enabled"], "Codex bundled skills");
|
|
26945
27077
|
if (typeof config.skills.bundled.enabled !== "boolean")
|
|
@@ -26997,7 +27129,9 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26997
27129
|
const target = join41(path, name);
|
|
26998
27130
|
if (!lstatSync34(target).isFile())
|
|
26999
27131
|
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
27000
|
-
witness(target);
|
|
27132
|
+
const text2 = witness(target);
|
|
27133
|
+
if (exclusion && text2 !== null)
|
|
27134
|
+
sumiLocalPluginTexts.set(canonical9(target), text2);
|
|
27001
27135
|
}
|
|
27002
27136
|
}
|
|
27003
27137
|
for (const root2 of roots) {
|
|
@@ -27131,16 +27265,88 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27131
27265
|
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual9(candidate2, source)) === index);
|
|
27132
27266
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
27133
27267
|
}
|
|
27134
|
-
|
|
27268
|
+
const reviewedManifestAbsence = (root2, manifestPath) => {
|
|
27269
|
+
const retained = exclusion?.retained;
|
|
27270
|
+
if (agent !== "claude" || retained?.method !== "reviewed" || ![join41(root2, "skills"), root2].some((path) => retained.roots.includes(path)))
|
|
27271
|
+
return false;
|
|
27272
|
+
const saved = retained.sources.filter((source) => source.path === canonical9(manifestPath) && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined);
|
|
27273
|
+
return saved.length > 0 && saved.every((source) => projected(source, undefined, budget3) === source.sha256);
|
|
27274
|
+
};
|
|
27275
|
+
const reviewedInstalledCopy = (root2, path, identity5) => {
|
|
27276
|
+
const retained = exclusion?.retained;
|
|
27277
|
+
if (agent !== "codex" || retained?.method !== "reviewed")
|
|
27278
|
+
return false;
|
|
27279
|
+
const inside = relative10(root2, canonical9(path));
|
|
27280
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep15}`) || isAbsolute28(inside))
|
|
27281
|
+
return false;
|
|
27282
|
+
const parent = canonical9(join41(nativeHome, "plugins/cache", identity5.marketplace, identity5.name));
|
|
27283
|
+
const current = hashRawDiscoveryFile(canonical9(path), budget3);
|
|
27284
|
+
if (current === null)
|
|
27285
|
+
return false;
|
|
27286
|
+
return retained.sources.some((source) => {
|
|
27287
|
+
if (source.sha256 !== current || source.format !== undefined || source.fields !== undefined || ![undefined, "bytes"].includes(source.hashMode) || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || !source.path.startsWith(parent + sep15))
|
|
27288
|
+
return false;
|
|
27289
|
+
const [version2, ...rest] = source.path.slice(parent.length + 1).split(sep15);
|
|
27290
|
+
return Boolean(version2) && version2 !== "." && version2 !== ".." && rest.join(sep15) === inside;
|
|
27291
|
+
});
|
|
27292
|
+
};
|
|
27293
|
+
const verifyCodexPluginHookClosure = (root2, identity5, marketplaceSource, servers, declarations) => {
|
|
27294
|
+
const retained = exclusion.retained;
|
|
27295
|
+
if (retained.codexSemanticCache || retained.codexInstallationInputs || retained.codexRetiredMaterializations)
|
|
27296
|
+
return verifyOpaqueClosure(declarations);
|
|
27297
|
+
const within2 = (base, path) => path === base || path.startsWith(base + sep15);
|
|
27298
|
+
const executables = [];
|
|
27299
|
+
for (const server of servers) {
|
|
27300
|
+
const row = config.mcp_servers?.[server];
|
|
27301
|
+
if (server === "codex_apps" || server === "_codex_apps" || !isCodexLocalStdioMcpServer(server, row))
|
|
27302
|
+
unresolved("plugin hook MCP server is not a configured local stdio server");
|
|
27303
|
+
for (const value of [row.command, ...row.args ?? []])
|
|
27304
|
+
for (const candidate2 of [value, value.slice(value.indexOf("=") + 1)]) {
|
|
27305
|
+
if (!isAbsolute28(candidate2) || /[\0$]/.test(candidate2) || executables.length >= AGENT_POLICY_LIMITS.discoveryRoots)
|
|
27306
|
+
continue;
|
|
27307
|
+
const path = canonical9(candidate2);
|
|
27308
|
+
if (lstatSync34(path, { throwIfNoEntry: false })?.isFile())
|
|
27309
|
+
executables.push(path);
|
|
27310
|
+
}
|
|
27311
|
+
}
|
|
27312
|
+
const cacheRoot = canonical9(join41(nativeHome, "plugins/cache"));
|
|
27313
|
+
const ownCache = canonical9(join41(nativeHome, "plugins/cache", identity5.marketplace, identity5.name));
|
|
27314
|
+
const ownCatalog = canonical9(join41(marketplaceSource, ".agents/plugins/marketplace.json"));
|
|
27315
|
+
const account = canonical9(home);
|
|
27316
|
+
const marketplaceRoots = Object.values(config.marketplaces ?? {}).filter((market) => market?.source_type === "local" && typeof market.source === "string" && isAbsolute28(market.source)).map((market) => canonical9(market.source)).filter((path) => !within2(path, account));
|
|
27317
|
+
const otherPlugin = (path) => !within2(root2, path) && !within2(ownCache, path) && path !== ownCatalog && !executables.includes(path) && (within2(cacheRoot, path) || marketplaceRoots.some((base) => within2(base, path)));
|
|
27318
|
+
const sources2 = retained.sources.filter((source) => source.path !== canonical9(configPath) && !otherPlugin(source.path));
|
|
27319
|
+
if (sources2.some((source) => source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || ![undefined, "bytes", "path-bytes", "executable-v1"].includes(source.hashMode)))
|
|
27320
|
+
return verifyOpaqueClosure(declarations);
|
|
27321
|
+
const declared = declarations.map((path) => relative10(root2, path));
|
|
27322
|
+
const declaration = (path) => declarations.includes(path) || path.startsWith(ownCache + sep15) && declared.includes(path.slice(ownCache.length + 1).split(sep15).slice(1).join(sep15));
|
|
27323
|
+
if (!sources2.some((source) => !declaration(source.path) && source.sha256 !== null))
|
|
27324
|
+
unresolved("opaque executable dependency closure is unavailable");
|
|
27325
|
+
for (const source of sources2)
|
|
27326
|
+
if (projected(source, undefined, budget3) !== source.sha256) {
|
|
27327
|
+
throw new NativeDiscoveryWitnessChange("input", source.path, `Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
27328
|
+
}
|
|
27329
|
+
verifyDiscoveryDirectories((retained.directories ?? []).filter((directory2) => !otherPlugin(directory2.path)));
|
|
27330
|
+
};
|
|
27331
|
+
const readCodexHookDeclaration = (text2, path) => {
|
|
27332
|
+
if (text2 === null)
|
|
27333
|
+
return null;
|
|
27334
|
+
try {
|
|
27335
|
+
return codexPluginHookHandlers(parseConfig(text2, path));
|
|
27336
|
+
} catch {
|
|
27337
|
+
return null;
|
|
27338
|
+
}
|
|
27339
|
+
};
|
|
27340
|
+
function plugin(root2, codex) {
|
|
27135
27341
|
root2 = canonical9(root2);
|
|
27136
27342
|
safe6(root2);
|
|
27137
27343
|
if (exclusion)
|
|
27138
27344
|
roots.add(root2);
|
|
27139
27345
|
const manifestPath = join41(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
27140
27346
|
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
27141
|
-
if (raw === null)
|
|
27347
|
+
if (raw === null && !reviewedManifestAbsence(root2, manifestPath))
|
|
27142
27348
|
unresolved("plugin manifest missing");
|
|
27143
|
-
const manifest = parseConfig(raw, manifestPath);
|
|
27349
|
+
const manifest = raw === null ? {} : parseConfig(raw, manifestPath);
|
|
27144
27350
|
if (exclusion && Object.keys(manifest).some((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2) && !["skills", "hooks"].includes(key2)))
|
|
27145
27351
|
unresolved("unknown plugin discovery controls");
|
|
27146
27352
|
const hooks = witness(join41(root2, "hooks/hooks.json"));
|
|
@@ -27148,20 +27354,35 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27148
27354
|
if (!exclusion || exclusion.retained?.method !== "reviewed")
|
|
27149
27355
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
27150
27356
|
const required = [manifestPath, ...hooks !== null ? [join41(root2, "hooks/hooks.json")] : []];
|
|
27357
|
+
const scoped2 = agent === "codex" && codex !== undefined ? [] : undefined;
|
|
27151
27358
|
if (manifest.hooks !== undefined) {
|
|
27152
|
-
if (typeof manifest.hooks
|
|
27153
|
-
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
|
|
27157
|
-
|
|
27158
|
-
|
|
27359
|
+
if (scoped2 && manifest.hooks && typeof manifest.hooks === "object" && !Array.isArray(manifest.hooks)) {
|
|
27360
|
+
const inline = codexPluginHookHandlers(manifest.hooks);
|
|
27361
|
+
if (!inline)
|
|
27362
|
+
unresolved("unsupported inline plugin hooks");
|
|
27363
|
+
scoped2.push(inline);
|
|
27364
|
+
} else {
|
|
27365
|
+
if (typeof manifest.hooks !== "string")
|
|
27366
|
+
unresolved("unsupported plugin hooks target");
|
|
27367
|
+
const hook = resolve28(root2, manifest.hooks);
|
|
27368
|
+
if (!hook.startsWith(root2 + sep15))
|
|
27369
|
+
unresolved("plugin hooks target escapes its root");
|
|
27370
|
+
required.push(hook);
|
|
27371
|
+
if (scoped2)
|
|
27372
|
+
scoped2.push(readCodexHookDeclaration(witness(hook), hook));
|
|
27373
|
+
}
|
|
27374
|
+
}
|
|
27375
|
+
if (scoped2 && hooks !== null)
|
|
27376
|
+
scoped2.push(readCodexHookDeclaration(hooks, join41(root2, "hooks/hooks.json")));
|
|
27159
27377
|
for (const path of required) {
|
|
27160
27378
|
const saved = exclusion.retained.sources.filter((source) => source.path === canonical9(path) && source.sha256 !== null && source.format === undefined && source.fields === undefined);
|
|
27161
|
-
if (
|
|
27379
|
+
if (saved.length ? saved.some((source) => projected(source, undefined, budget3) !== source.sha256) : !(scoped2 && reviewedInstalledCopy(root2, path, codex)))
|
|
27162
27380
|
unresolved("plugin hooks require current independently reviewed sources");
|
|
27163
27381
|
}
|
|
27164
|
-
|
|
27382
|
+
if (scoped2 && scoped2.every((item) => item !== null && item.commands === 0)) {
|
|
27383
|
+
verifyCodexPluginHookClosure(root2, codex, codex.source, new Set(scoped2.flatMap((item) => [...item.servers])), required.map((path) => canonical9(path)));
|
|
27384
|
+
} else
|
|
27385
|
+
verifyOpaqueClosure(required.map((path) => canonical9(path)));
|
|
27165
27386
|
}
|
|
27166
27387
|
const commands = join41(root2, "commands");
|
|
27167
27388
|
safe6(commands);
|
|
@@ -27217,7 +27438,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27217
27438
|
const catalog = parseConfig(catalogText, join41(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
27218
27439
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
27219
27440
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
27220
|
-
plugin(resolve28(market.source, matches[0].source.path));
|
|
27441
|
+
plugin(resolve28(market.source, matches[0].source.path), exclusion ? { marketplace, name, source: market.source } : undefined);
|
|
27221
27442
|
}
|
|
27222
27443
|
} else if (agent === "opencode") {
|
|
27223
27444
|
if (witness(join41(home, ".config/opencode/opencode.jsonc")) !== null || witness(join41(home, ".config/opencode/config.json")) !== null)
|
|
@@ -27234,7 +27455,11 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27234
27455
|
if (lstatSync34(localPlugins, { throwIfNoEntry: false }) && entries(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
27235
27456
|
unresolved("additional local plugin hooks require review");
|
|
27236
27457
|
} else if (agent === "sumi") {
|
|
27237
|
-
|
|
27458
|
+
const pinnedLocalPlugin = (source) => {
|
|
27459
|
+
const retained = exclusion?.retained, text2 = sumiLocalPluginTexts.get(source.path);
|
|
27460
|
+
return retained?.method === "reviewed" && text2 !== undefined && sumiPluginLoadsOnlyBuiltins(text2) && retained.sources.some((saved) => saved.path === source.path && saved.sha256 === source.sha256 && saved.sha256 !== null && [undefined, "bytes"].includes(saved.hashMode) && saved.format === undefined && saved.fields === undefined && saved.managedPlugins === undefined && saved.reviewArtifact === undefined);
|
|
27461
|
+
};
|
|
27462
|
+
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join41(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null && !pinnedLocalPlugin(source)))
|
|
27238
27463
|
unresolved("external or foreign local plugins require review");
|
|
27239
27464
|
} else if (agent === "hermes") {
|
|
27240
27465
|
for (const directory2 of [join41(home, ".hermes/plugins"), join41(home, ".hermes/hermes-agent")]) {
|
|
@@ -27342,7 +27567,10 @@ function configuredRoots(home, explicitHome) {
|
|
|
27342
27567
|
if (recorded && (!executable2 || executable2.target !== recorded.target || onPath && onPath.target !== recorded.target) || !executable2 && (binding.builtinNames?.length || binding.agent === "gemini" && binding.sources.some((source) => basename18(source.path) === "package.json"))) {
|
|
27343
27568
|
throw new Error("Native discovery runtime target is unresolved or changed");
|
|
27344
27569
|
}
|
|
27345
|
-
|
|
27570
|
+
const managedCommand = current.commands?.[agent], managedProfile = current.profiles?.[agent];
|
|
27571
|
+
const supervisor = current.supervisors?.[agent];
|
|
27572
|
+
const managedHook = (binding.agent === "gemini" || binding.agent === "cursor") && typeof managedCommand === "string" && typeof managedProfile === "string" ? { command: managedCommand, profile: managedProfile } : binding.agent === "hermes" && typeof managedCommand === "string" && typeof managedProfile === "string" && supervisor && typeof supervisor === "object" && typeof supervisor.path === "string" && typeof supervisor.runtime === "string" && typeof supervisor.sha256 === "string" ? { command: managedCommand, profile: managedProfile, supervisor: { path: supervisor.path, runtime: supervisor.runtime, sha256: supervisor.sha256 } } : undefined;
|
|
27573
|
+
roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable: executable2, narrowed, ...managedHook ? { managedHook } : {} }));
|
|
27346
27574
|
}
|
|
27347
27575
|
};
|
|
27348
27576
|
if (homePolicy)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type IntegrationAgent } from "./agent-adapters.js";
|
|
2
2
|
import { type DiscoveryDirectory } from "./agent-discovery-directories.js";
|
|
3
3
|
import { type CodexReviewedHookSource } from "./codex-hook-mcp-dependencies.js";
|
|
4
4
|
import { CLAUDE_ORPHAN_MARKER_MODE } from "./claude-plugin-cache-marker.js";
|
|
@@ -214,8 +214,21 @@ export declare function resolveAgentDiscoveryExclusionRoots(options: Omit<Discov
|
|
|
214
214
|
retained?: AgentDiscoveryBinding;
|
|
215
215
|
narrowed?: NarrowedWitnessLedger;
|
|
216
216
|
nativeHome?: string;
|
|
217
|
+
managedHook?: ManagedBridgeHook;
|
|
217
218
|
}): readonly string[];
|
|
218
219
|
/** Metadata-only closure of aliases inside the selected native discovery
|
|
219
220
|
* trees. Never search the account generally or load a payload/plugin. Each
|
|
220
221
|
* resolved alias remains excluded while unrelated portable paths stay usable. */
|
|
221
222
|
export declare function nativeDiscoveryAliasTargets(roots: readonly string[], home: string): string[];
|
|
223
|
+
/** The Skills bridge command and selection profile the policy records for an
|
|
224
|
+
* agent (`bridge.commands` and `bridge.profiles`), from which hook install
|
|
225
|
+
* renders its managed hook commands. */
|
|
226
|
+
export interface ManagedBridgeHook {
|
|
227
|
+
command: string;
|
|
228
|
+
profile: string;
|
|
229
|
+
supervisor?: {
|
|
230
|
+
path: string;
|
|
231
|
+
runtime: string;
|
|
232
|
+
sha256: string;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -28,7 +28,23 @@ export interface SelectedSecretScope {
|
|
|
28
28
|
profileId: string;
|
|
29
29
|
cwd: string;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The part of the @hasna/secrets SDK entry Skills uses. Skills typechecks and
|
|
33
|
+
* builds without the optional package: src/types/hasna-secrets-sdk.d.ts declares
|
|
34
|
+
* this much of the module, and src/lib/secrets-sdk-types.test.ts checks both
|
|
35
|
+
* against the real package wherever that is installed.
|
|
36
|
+
*/
|
|
37
|
+
export interface SecretsSdk {
|
|
38
|
+
createSecretsClientFromEnv(env?: Record<string, string | undefined>): SelectedSecretsClient;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The independently configured Secrets SDK. @hasna/secrets is an optional
|
|
42
|
+
* dependency that only an install with access to it receives (SH-00058), and
|
|
43
|
+
* the build keeps it external, so it is loaded here, when a vault grant needs
|
|
44
|
+
* it, and nowhere else.
|
|
45
|
+
*/
|
|
46
|
+
export declare function defaultSecretsClientFactory(load?: () => Promise<SecretsSdk>): Promise<() => SelectedSecretsClient>;
|
|
47
|
+
export declare function selectedSecretBindingsTemplate(scope: SelectedSecretScope, names: string[], createClient: () => SelectedSecretsClient): SelectedSecretBindings;
|
|
32
48
|
/** Runtime controls must not be supplied as credentials, even by a reviewed binding. */
|
|
33
49
|
export declare function declaredSecretNames(value: unknown): string[];
|
|
34
50
|
/** Reading is bounded and rejects symlinks/devices. Parsing errors never echo file contents. */
|
|
@@ -49,7 +49,7 @@ var __require = import.meta.require;
|
|
|
49
49
|
// src/runtime-prerequisites.ts
|
|
50
50
|
import { isAbsolute as isAbsolute7 } from "path";
|
|
51
51
|
// package.json
|
|
52
|
-
var version = "0.10.
|
|
52
|
+
var version = "0.10.92";
|
|
53
53
|
var package_default = {
|
|
54
54
|
name: "@hasna/skills",
|
|
55
55
|
version,
|
|
@@ -96,6 +96,7 @@ var package_default = {
|
|
|
96
96
|
"docs/runtime-activation.md",
|
|
97
97
|
"schemas/",
|
|
98
98
|
"LICENSE",
|
|
99
|
+
"THIRD_PARTY_NOTICES",
|
|
99
100
|
"README.md",
|
|
100
101
|
"!dist/**/*test-utils.d.ts",
|
|
101
102
|
"!dist/**/*fixtures.d.ts"
|
|
@@ -103,9 +104,9 @@ var package_default = {
|
|
|
103
104
|
main: "./dist/index.js",
|
|
104
105
|
types: "./dist/index.d.ts",
|
|
105
106
|
scripts: {
|
|
106
|
-
clean: "rm -rf bin/ dist/",
|
|
107
|
-
build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @modelcontextprotocol/sdk ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
108
|
-
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
107
|
+
clean: "rm -rf bin/ dist/ THIRD_PARTY_NOTICES",
|
|
108
|
+
build: "bun run clean && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/secrets --external @modelcontextprotocol/sdk ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/secrets ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts && bun scripts/third-party-notices.ts",
|
|
109
|
+
"build:js": "rm -rf dist && bun build --external @hasna/secrets ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
109
110
|
test: "bun test --timeout 30000",
|
|
110
111
|
dev: "bun run ./src/cli/index.tsx",
|
|
111
112
|
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
@@ -156,7 +157,6 @@ var package_default = {
|
|
|
156
157
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
157
158
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
158
159
|
"@hasna/events": "0.1.18",
|
|
159
|
-
"@hasna/secrets": "0.4.2",
|
|
160
160
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
161
161
|
chalk: "^5.3.0",
|
|
162
162
|
commander: "^12.1.0",
|
|
@@ -168,6 +168,9 @@ var package_default = {
|
|
|
168
168
|
zod: "^4.3.6",
|
|
169
169
|
"@hasna/contracts": "1.3.5"
|
|
170
170
|
},
|
|
171
|
+
optionalDependencies: {
|
|
172
|
+
"@hasna/secrets": "0.4.2"
|
|
173
|
+
},
|
|
171
174
|
engines: {
|
|
172
175
|
bun: ">=1.0.0"
|
|
173
176
|
},
|