@hasna/skills 0.9.14 → 0.9.16
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 +7 -0
- package/bin/index.js +291 -230
- package/bin/mcp.js +41 -15
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +39 -6
- package/dist/lib/agent-codex-trust.d.ts +2 -1
- package/dist/lib/agent-discovery.d.ts +1 -0
- package/dist/lib/agent-integration.d.ts +5 -0
- package/dist/lib/codex-hook-additions-review.d.ts +22 -1
- package/dist/lib/codex-hook-discovery-recovery.d.ts +2 -1
- package/dist/sdk/index.js +64 -31
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.9.
|
|
20022
|
+
version: "0.9.16",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -48796,13 +48796,24 @@ var init_codex_hook_trust_files = () => {};
|
|
|
48796
48796
|
// src/lib/codex-hook-trust-layout.ts
|
|
48797
48797
|
var init_codex_hook_trust_layout = () => {};
|
|
48798
48798
|
|
|
48799
|
+
// src/lib/codex-hook-additions-review.ts
|
|
48800
|
+
var events, matchedEvents, proofs;
|
|
48801
|
+
var init_codex_hook_additions_review = __esm(() => {
|
|
48802
|
+
init_codex_hook_trust_files();
|
|
48803
|
+
init_codex_hook_trust_layout();
|
|
48804
|
+
events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
48805
|
+
matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
48806
|
+
proofs = new WeakMap;
|
|
48807
|
+
});
|
|
48808
|
+
|
|
48799
48809
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
48800
48810
|
import { createHash as createHash13 } from "crypto";
|
|
48801
48811
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
48802
|
-
const proof =
|
|
48812
|
+
const proof = proofs2.get(witness);
|
|
48803
48813
|
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
48804
48814
|
unchanged2(proof.before);
|
|
48805
48815
|
unchanged2(proof.current);
|
|
48816
|
+
proof.recheckAdditions();
|
|
48806
48817
|
}
|
|
48807
48818
|
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
48808
48819
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
@@ -48811,17 +48822,19 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
48811
48822
|
need4(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
48812
48823
|
return true;
|
|
48813
48824
|
}
|
|
48814
|
-
var sha = (text2) => createHash13("sha256").update(text2).digest("hex"),
|
|
48825
|
+
var sha = (text2) => createHash13("sha256").update(text2).digest("hex"), proofs2;
|
|
48815
48826
|
var init_codex_hook_discovery_recovery = __esm(() => {
|
|
48816
48827
|
init_codex_hook_trust_files();
|
|
48817
48828
|
init_codex_hook_trust_layout();
|
|
48818
|
-
|
|
48829
|
+
init_codex_hook_additions_review();
|
|
48830
|
+
proofs2 = new WeakMap;
|
|
48819
48831
|
});
|
|
48820
48832
|
|
|
48821
48833
|
// src/lib/agent-discovery.ts
|
|
48822
48834
|
import { createHash as createHash14 } from "crypto";
|
|
48823
48835
|
import { lstatSync as lstatSync16, readFileSync as readFileSync14, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
48824
48836
|
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute13, join as join23, resolve as resolve13, sep as sep4 } from "path";
|
|
48837
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
48825
48838
|
function parseConfig(text2, path, toml = false) {
|
|
48826
48839
|
try {
|
|
48827
48840
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -48996,6 +49009,19 @@ function resolveAgentDiscovery(options) {
|
|
|
48996
49009
|
}
|
|
48997
49010
|
}
|
|
48998
49011
|
}
|
|
49012
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
49013
|
+
const retained = options.retainedReview;
|
|
49014
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
49015
|
+
unresolved("invalid retained review");
|
|
49016
|
+
verifyAgentDiscovery(retained);
|
|
49017
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
49018
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
49019
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
49020
|
+
unresolved("retained settings witness names another configuration source");
|
|
49021
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual2(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
49022
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
49023
|
+
return retained;
|
|
49024
|
+
}
|
|
48999
49025
|
if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
|
|
49000
49026
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
49001
49027
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -57332,7 +57358,7 @@ var init_execution_grants = __esm(() => {
|
|
|
57332
57358
|
});
|
|
57333
57359
|
|
|
57334
57360
|
// src/lib/execution-grant-client.ts
|
|
57335
|
-
import { isDeepStrictEqual as
|
|
57361
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
57336
57362
|
function id(value) {
|
|
57337
57363
|
if (!grantIdentifier(value))
|
|
57338
57364
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -57375,7 +57401,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
57375
57401
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
57376
57402
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
57377
57403
|
}
|
|
57378
|
-
if (!
|
|
57404
|
+
if (!isDeepStrictEqual3(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual3(value.bindings.consumer, snapshot2.consumer)) {
|
|
57379
57405
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
57380
57406
|
}
|
|
57381
57407
|
return value;
|
|
@@ -59783,17 +59809,17 @@ var WSContext = class {
|
|
|
59783
59809
|
if (typeof args[0] === "function") {
|
|
59784
59810
|
const [createEvents, options] = args;
|
|
59785
59811
|
return async function upgradeWebSocket(c, next) {
|
|
59786
|
-
const
|
|
59787
|
-
const result = await handler(c,
|
|
59812
|
+
const events2 = await createEvents(c);
|
|
59813
|
+
const result = await handler(c, events2, options);
|
|
59788
59814
|
if (result) {
|
|
59789
59815
|
return result;
|
|
59790
59816
|
}
|
|
59791
59817
|
await next();
|
|
59792
59818
|
};
|
|
59793
59819
|
} else {
|
|
59794
|
-
const [c,
|
|
59820
|
+
const [c, events2, options] = args;
|
|
59795
59821
|
return (async () => {
|
|
59796
|
-
const upgraded = await handler(c,
|
|
59822
|
+
const upgraded = await handler(c, events2, options);
|
|
59797
59823
|
if (!upgraded) {
|
|
59798
59824
|
throw new Error("Failed to upgrade WebSocket");
|
|
59799
59825
|
}
|
|
@@ -60990,7 +61016,7 @@ var init_dist2 = __esm(() => {
|
|
|
60990
61016
|
"sec-websocket-extensions",
|
|
60991
61017
|
"sec-websocket-protocol"
|
|
60992
61018
|
]);
|
|
60993
|
-
upgradeWebSocket = defineWebSocketHelper(async (c,
|
|
61019
|
+
upgradeWebSocket = defineWebSocketHelper(async (c, events2, options) => {
|
|
60994
61020
|
if (c.req.header("upgrade")?.toLowerCase() !== "websocket")
|
|
60995
61021
|
return;
|
|
60996
61022
|
const env = c.env;
|
|
@@ -61027,7 +61053,7 @@ var init_dist2 = __esm(() => {
|
|
|
61027
61053
|
url: new URL(c.req.url)
|
|
61028
61054
|
};
|
|
61029
61055
|
try {
|
|
61030
|
-
|
|
61056
|
+
events2?.onOpen?.(new Event("open"), ctx);
|
|
61031
61057
|
} catch (e2) {
|
|
61032
61058
|
(options?.onError ?? console.error)(e2);
|
|
61033
61059
|
}
|
|
@@ -61035,7 +61061,7 @@ var init_dist2 = __esm(() => {
|
|
|
61035
61061
|
const datas = Array.isArray(data) ? data : [data];
|
|
61036
61062
|
for (const data2 of datas)
|
|
61037
61063
|
try {
|
|
61038
|
-
|
|
61064
|
+
events2?.onMessage?.(new MessageEvent("message", { data: isBinary ? data2 instanceof ArrayBuffer ? data2 : data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength) : typeof data2 === "string" ? data2 : Buffer.from(data2).toString("utf-8") }), ctx);
|
|
61039
61065
|
} catch (e2) {
|
|
61040
61066
|
(options?.onError ?? console.error)(e2);
|
|
61041
61067
|
}
|
|
@@ -61048,7 +61074,7 @@ var init_dist2 = __esm(() => {
|
|
|
61048
61074
|
});
|
|
61049
61075
|
ws.on("close", (code2, reason) => {
|
|
61050
61076
|
try {
|
|
61051
|
-
|
|
61077
|
+
events2?.onClose?.(new CloseEvent("close", {
|
|
61052
61078
|
code: code2,
|
|
61053
61079
|
reason: reason.toString()
|
|
61054
61080
|
}), ctx);
|
|
@@ -61058,7 +61084,7 @@ var init_dist2 = __esm(() => {
|
|
|
61058
61084
|
});
|
|
61059
61085
|
ws.on("error", (error61) => {
|
|
61060
61086
|
try {
|
|
61061
|
-
|
|
61087
|
+
events2?.onError?.(new ErrorEvent("error", { error: error61 }), ctx);
|
|
61062
61088
|
} catch (e2) {
|
|
61063
61089
|
(options?.onError ?? console.error)(e2);
|
|
61064
61090
|
}
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.9.
|
|
24125
|
+
version: "0.9.16",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.9.
|
|
24128
|
+
version: "0.9.16",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -9093,6 +9093,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9093
9093
|
import { createHash as createHash16 } from "crypto";
|
|
9094
9094
|
import { lstatSync as lstatSync20, readFileSync as readFileSync16, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
|
|
9095
9095
|
import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute14, join as join26, resolve as resolve14, sep as sep5 } from "path";
|
|
9096
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
9096
9097
|
|
|
9097
9098
|
// node_modules/yaml/dist/index.js
|
|
9098
9099
|
var composer = require_composer();
|
|
@@ -9610,7 +9611,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
|
|
|
9610
9611
|
// package.json
|
|
9611
9612
|
var package_default = {
|
|
9612
9613
|
name: "@hasna/skills",
|
|
9613
|
-
version: "0.9.
|
|
9614
|
+
version: "0.9.16",
|
|
9614
9615
|
description: "Skills library for AI coding agents",
|
|
9615
9616
|
type: "module",
|
|
9616
9617
|
bin: {
|
|
@@ -13369,7 +13370,15 @@ function planAgentIntegration(options) {
|
|
|
13369
13370
|
validateProfile(profileId2);
|
|
13370
13371
|
return [agent, { command, profileId: profileId2 }];
|
|
13371
13372
|
}));
|
|
13372
|
-
const
|
|
13373
|
+
const retainedAgents = [];
|
|
13374
|
+
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
13375
|
+
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
|
|
13376
|
+
if (retainedReview) {
|
|
13377
|
+
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
13378
|
+
retainedAgents.push(agent);
|
|
13379
|
+
}
|
|
13380
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
13381
|
+
});
|
|
13373
13382
|
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 });
|
|
13374
13383
|
const changes = [];
|
|
13375
13384
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -13454,7 +13463,7 @@ function planAgentIntegration(options) {
|
|
|
13454
13463
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
13455
13464
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
13456
13465
|
recheckRootAliases(aliases);
|
|
13457
|
-
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
13466
|
+
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
13458
13467
|
}
|
|
13459
13468
|
function planClaudeStopHookUpdate(options) {
|
|
13460
13469
|
return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
|
|
@@ -13573,6 +13582,8 @@ function applyAgentIntegration(plan) {
|
|
|
13573
13582
|
parseManagedSkillPolicy(change.after);
|
|
13574
13583
|
const aliases = plan.rootAliases ?? [];
|
|
13575
13584
|
recheckRootAliases(aliases);
|
|
13585
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
13586
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
13576
13587
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
13577
13588
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
13578
13589
|
for (const binding of plan.discoveryBefore ?? []) {
|
|
@@ -16439,14 +16450,20 @@ function unchanged2(before) {
|
|
|
16439
16450
|
need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
16440
16451
|
}
|
|
16441
16452
|
|
|
16453
|
+
// src/lib/codex-hook-additions-review.ts
|
|
16454
|
+
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
16455
|
+
var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
16456
|
+
var proofs = new WeakMap;
|
|
16457
|
+
|
|
16442
16458
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
16443
16459
|
var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
16444
|
-
var
|
|
16460
|
+
var proofs2 = new WeakMap;
|
|
16445
16461
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
16446
|
-
const proof =
|
|
16462
|
+
const proof = proofs2.get(witness);
|
|
16447
16463
|
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
16448
16464
|
unchanged2(proof.before);
|
|
16449
16465
|
unchanged2(proof.current);
|
|
16466
|
+
proof.recheckAdditions();
|
|
16450
16467
|
}
|
|
16451
16468
|
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
16452
16469
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
@@ -16567,7 +16584,10 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
16567
16584
|
const budget = discoveryByteBudget();
|
|
16568
16585
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
16569
16586
|
assertMarketplaceBinding(binding, source);
|
|
16570
|
-
|
|
16587
|
+
const sha2562 = projected(source, changes, budget);
|
|
16588
|
+
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
16589
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
16590
|
+
return { ...source, sha256: sha2562 };
|
|
16571
16591
|
}) };
|
|
16572
16592
|
}
|
|
16573
16593
|
function captureDiscoveryByteSources(paths) {
|
|
@@ -16691,6 +16711,19 @@ function resolveAgentDiscovery(options) {
|
|
|
16691
16711
|
}
|
|
16692
16712
|
}
|
|
16693
16713
|
}
|
|
16714
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
16715
|
+
const retained = options.retainedReview;
|
|
16716
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
16717
|
+
unresolved("invalid retained review");
|
|
16718
|
+
verifyAgentDiscovery(retained);
|
|
16719
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
16720
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
16721
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
16722
|
+
unresolved("retained settings witness names another configuration source");
|
|
16723
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual3(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
16724
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
16725
|
+
return retained;
|
|
16726
|
+
}
|
|
16694
16727
|
if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
|
|
16695
16728
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
16696
16729
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -71,7 +71,8 @@ export declare function reconcileCodexNativeHooks(options: CodexNativeHookReconc
|
|
|
71
71
|
hooksPath: string;
|
|
72
72
|
hooksSha256: string;
|
|
73
73
|
};
|
|
74
|
-
additions: any
|
|
74
|
+
additions: any;
|
|
75
|
+
version?: number | undefined;
|
|
75
76
|
} | undefined;
|
|
76
77
|
configDiscoveryRecovery?: import("./codex-hook-discovery-recovery.js").CodexHookDiscoveryRecovery | undefined;
|
|
77
78
|
version: number;
|
|
@@ -56,6 +56,11 @@ export interface AgentIntegrationPlan {
|
|
|
56
56
|
home: string;
|
|
57
57
|
agents: IntegrationAgent[];
|
|
58
58
|
};
|
|
59
|
+
retainedReviewChecks?: {
|
|
60
|
+
home: string;
|
|
61
|
+
projectDir: string;
|
|
62
|
+
agents: IntegrationAgent[];
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
/** Parse the reviewed exact-target binding used by native migration. Paths are
|
|
61
66
|
* deliberately project-relative so a manifest cannot silently select another
|
|
@@ -4,6 +4,25 @@ export interface NativeHookAdditionsRecovery {
|
|
|
4
4
|
path: string;
|
|
5
5
|
reviewedPlanDigest?: string;
|
|
6
6
|
}
|
|
7
|
+
/** Internal capability, not a caller-supplied config override. */
|
|
8
|
+
export interface ReviewedNativeHookAdditions {
|
|
9
|
+
readonly reviewSha256: string;
|
|
10
|
+
}
|
|
11
|
+
type JournalHook = {
|
|
12
|
+
key: string;
|
|
13
|
+
currentHash: string;
|
|
14
|
+
};
|
|
15
|
+
type TrustState = Readonly<{
|
|
16
|
+
trusted_hash: string;
|
|
17
|
+
enabled?: true;
|
|
18
|
+
}>;
|
|
19
|
+
type AddedTrustState = Readonly<{
|
|
20
|
+
key: string;
|
|
21
|
+
state: TrustState;
|
|
22
|
+
}>;
|
|
23
|
+
/** Rebind the proof to this exact journal transition and recheck every source.
|
|
24
|
+
* A serialized/cloned receipt can never create a discovery exception. */
|
|
25
|
+
export declare function readReviewedAdditionTrustStates(proof: ReviewedNativeHookAdditions, before: Snapshot, current: Snapshot, journalHooks: JournalHook[]): readonly AddedTrustState[];
|
|
7
26
|
/** An explicit, private review of a later producer's append-only hook change.
|
|
8
27
|
* The producer backups must themselves describe exactly the completed journal
|
|
9
28
|
* operation. Neither a claimed receipt nor a matching raw hash grants authority
|
|
@@ -25,6 +44,7 @@ export declare function reviewNativeHookAdditions(options: {
|
|
|
25
44
|
}): {
|
|
26
45
|
hooks: any[];
|
|
27
46
|
recheck: () => void;
|
|
47
|
+
trustProof: ReviewedNativeHookAdditions;
|
|
28
48
|
receipt: {
|
|
29
49
|
reviewPath: string;
|
|
30
50
|
reviewSha256: string;
|
|
@@ -36,7 +56,8 @@ export declare function reviewNativeHookAdditions(options: {
|
|
|
36
56
|
hooksPath: string;
|
|
37
57
|
hooksSha256: string;
|
|
38
58
|
};
|
|
39
|
-
additions: any
|
|
59
|
+
additions: any;
|
|
60
|
+
version?: number | undefined;
|
|
40
61
|
};
|
|
41
62
|
};
|
|
42
63
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentDiscoveryBinding, DiscoverySource } from "./agent-discovery.js";
|
|
2
2
|
import { type snapshot } from "./codex-hook-trust-files.js";
|
|
3
|
+
import { type ReviewedNativeHookAdditions } from "./codex-hook-additions-review.js";
|
|
3
4
|
type Snapshot = ReturnType<typeof snapshot>;
|
|
4
5
|
/** A receipt projection, not an override supplied by a CLI caller. Only this
|
|
5
6
|
* module can create the in-memory proof used by discovery verification. */
|
|
@@ -17,7 +18,7 @@ export interface CodexHookDiscoveryRecovery {
|
|
|
17
18
|
export declare function createCodexHookDiscoveryRecovery(binding: AgentDiscoveryBinding, before: Snapshot, current: Snapshot, hooks: Array<{
|
|
18
19
|
key: string;
|
|
19
20
|
currentHash: string;
|
|
20
|
-
}
|
|
21
|
+
}>, additions?: ReviewedNativeHookAdditions): CodexHookDiscoveryRecovery | undefined;
|
|
21
22
|
export declare function assertCodexHookDiscoveryRecovery(binding: AgentDiscoveryBinding, witness: CodexHookDiscoveryRecovery): void;
|
|
22
23
|
/** Verify this one historical byte witness using the already-validated native
|
|
23
24
|
* transition. Every other source still uses its ordinary current-file hash. */
|
package/dist/sdk/index.js
CHANGED
|
@@ -31310,17 +31310,17 @@ class JsonEventsStore {
|
|
|
31310
31310
|
}
|
|
31311
31311
|
async appendEvent(event) {
|
|
31312
31312
|
await this.init();
|
|
31313
|
-
const
|
|
31314
|
-
|
|
31315
|
-
await this.writeJson(this.eventsPath,
|
|
31313
|
+
const events2 = await this.readJson(this.eventsPath, []);
|
|
31314
|
+
events2.push(event);
|
|
31315
|
+
await this.writeJson(this.eventsPath, events2);
|
|
31316
31316
|
return event;
|
|
31317
31317
|
}
|
|
31318
31318
|
async appendEventOnce(event, options = {}) {
|
|
31319
31319
|
await this.init();
|
|
31320
|
-
const
|
|
31320
|
+
const events2 = await this.readJson(this.eventsPath, []);
|
|
31321
31321
|
const dedupe = options.dedupe !== false;
|
|
31322
31322
|
if (dedupe) {
|
|
31323
|
-
const existing = findEventByIdentity(
|
|
31323
|
+
const existing = findEventByIdentity(events2, { id: event.id, dedupeKey: event.dedupeKey });
|
|
31324
31324
|
if (existing) {
|
|
31325
31325
|
return {
|
|
31326
31326
|
event: existing,
|
|
@@ -31330,8 +31330,8 @@ class JsonEventsStore {
|
|
|
31330
31330
|
};
|
|
31331
31331
|
}
|
|
31332
31332
|
}
|
|
31333
|
-
|
|
31334
|
-
await this.writeJson(this.eventsPath,
|
|
31333
|
+
events2.push(event);
|
|
31334
|
+
await this.writeJson(this.eventsPath, events2);
|
|
31335
31335
|
return {
|
|
31336
31336
|
event,
|
|
31337
31337
|
stored: true,
|
|
@@ -31341,13 +31341,13 @@ class JsonEventsStore {
|
|
|
31341
31341
|
}
|
|
31342
31342
|
async listEvents(options = {}) {
|
|
31343
31343
|
await this.init();
|
|
31344
|
-
const
|
|
31345
|
-
return queryEvents(
|
|
31344
|
+
const events2 = await this.readJson(this.eventsPath, []);
|
|
31345
|
+
return queryEvents(events2, options);
|
|
31346
31346
|
}
|
|
31347
31347
|
async listEventsPage(options = {}) {
|
|
31348
31348
|
await this.init();
|
|
31349
|
-
const
|
|
31350
|
-
const queried = queryEvents(
|
|
31349
|
+
const events2 = await this.readJson(this.eventsPath, []);
|
|
31350
|
+
const queried = queryEvents(events2, {
|
|
31351
31351
|
eventId: options.eventId,
|
|
31352
31352
|
source: options.source,
|
|
31353
31353
|
type: options.type
|
|
@@ -31365,8 +31365,8 @@ class JsonEventsStore {
|
|
|
31365
31365
|
};
|
|
31366
31366
|
}
|
|
31367
31367
|
async findEventByIdentity(identity3) {
|
|
31368
|
-
const
|
|
31369
|
-
return findEventByIdentity(
|
|
31368
|
+
const events2 = await this.listEvents();
|
|
31369
|
+
return findEventByIdentity(events2, identity3);
|
|
31370
31370
|
}
|
|
31371
31371
|
async appendDelivery(result) {
|
|
31372
31372
|
await this.init();
|
|
@@ -31471,8 +31471,8 @@ function normalizeEventPageLimit(limit) {
|
|
|
31471
31471
|
throw new Error(`Event page limit must be a positive integer, got ${limit}`);
|
|
31472
31472
|
return Math.min(limit, MAX_EVENT_PAGE_LIMIT);
|
|
31473
31473
|
}
|
|
31474
|
-
function queryEvents(
|
|
31475
|
-
let rows =
|
|
31474
|
+
function queryEvents(events2, options) {
|
|
31475
|
+
let rows = events2;
|
|
31476
31476
|
if (options.eventId)
|
|
31477
31477
|
rows = rows.filter((event) => event.id === options.eventId);
|
|
31478
31478
|
if (options.source)
|
|
@@ -31491,8 +31491,8 @@ function assertCursorFilter(name, cursorValue, optionValue) {
|
|
|
31491
31491
|
if (cursorValue !== optionValue)
|
|
31492
31492
|
throw new Error(`Local JSON event cursor ${name} filter mismatch`);
|
|
31493
31493
|
}
|
|
31494
|
-
function findEventByIdentity(
|
|
31495
|
-
return
|
|
31494
|
+
function findEventByIdentity(events2, identity3) {
|
|
31495
|
+
return events2.find((event) => identity3.id !== undefined && event.id === identity3.id || identity3.dedupeKey !== undefined && event.dedupeKey === identity3.dedupeKey);
|
|
31496
31496
|
}
|
|
31497
31497
|
function buildSignatureBase(timestamp, body4) {
|
|
31498
31498
|
return `${timestamp}.${body4}`;
|
|
@@ -32125,16 +32125,16 @@ class EventsClient {
|
|
|
32125
32125
|
async listEventsPage(options = {}) {
|
|
32126
32126
|
if (this.store.listEventsPage)
|
|
32127
32127
|
return this.store.listEventsPage(options);
|
|
32128
|
-
const
|
|
32128
|
+
const events2 = queryClientEvents(await this.store.listEvents(), {
|
|
32129
32129
|
eventId: options.eventId,
|
|
32130
32130
|
source: options.source,
|
|
32131
32131
|
type: options.type
|
|
32132
32132
|
});
|
|
32133
32133
|
const offset = decodeLocalJsonEventCursor(options.cursor, options);
|
|
32134
32134
|
const limit = normalizeEventPageLimit(options.limit);
|
|
32135
|
-
const pageEvents =
|
|
32135
|
+
const pageEvents = events2.slice(offset, offset + limit);
|
|
32136
32136
|
const nextOffset = offset + pageEvents.length;
|
|
32137
|
-
const hasMore = nextOffset <
|
|
32137
|
+
const hasMore = nextOffset < events2.length;
|
|
32138
32138
|
return {
|
|
32139
32139
|
events: pageEvents,
|
|
32140
32140
|
cursor: options.cursor,
|
|
@@ -32265,8 +32265,8 @@ class EventsClient {
|
|
|
32265
32265
|
return createDeliveryResult(event, channel, attempts);
|
|
32266
32266
|
}
|
|
32267
32267
|
}
|
|
32268
|
-
function queryClientEvents(
|
|
32269
|
-
let rows =
|
|
32268
|
+
function queryClientEvents(events2, options) {
|
|
32269
|
+
let rows = events2;
|
|
32270
32270
|
if (options.eventId)
|
|
32271
32271
|
rows = rows.filter((event) => event.id === options.eventId);
|
|
32272
32272
|
if (options.source)
|
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.9.
|
|
32376
|
+
version: "0.9.16",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -37704,6 +37704,7 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
37704
37704
|
import { createHash as createHash15 } from "crypto";
|
|
37705
37705
|
import { lstatSync as lstatSync17, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
37706
37706
|
import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute14, join as join20, resolve as resolve13, sep as sep3 } from "path";
|
|
37707
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
37707
37708
|
|
|
37708
37709
|
// node_modules/yaml/dist/index.js
|
|
37709
37710
|
var composer = require_composer();
|
|
@@ -40267,14 +40268,20 @@ function unchanged2(before) {
|
|
|
40267
40268
|
need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
40268
40269
|
}
|
|
40269
40270
|
|
|
40271
|
+
// src/lib/codex-hook-additions-review.ts
|
|
40272
|
+
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
40273
|
+
var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
40274
|
+
var proofs = new WeakMap;
|
|
40275
|
+
|
|
40270
40276
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
40271
40277
|
var sha = (text2) => createHash14("sha256").update(text2).digest("hex");
|
|
40272
|
-
var
|
|
40278
|
+
var proofs2 = new WeakMap;
|
|
40273
40279
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
40274
|
-
const proof =
|
|
40280
|
+
const proof = proofs2.get(witness);
|
|
40275
40281
|
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
40276
40282
|
unchanged2(proof.before);
|
|
40277
40283
|
unchanged2(proof.current);
|
|
40284
|
+
proof.recheckAdditions();
|
|
40278
40285
|
}
|
|
40279
40286
|
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
40280
40287
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
@@ -40395,7 +40402,10 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
40395
40402
|
const budget = discoveryByteBudget();
|
|
40396
40403
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
40397
40404
|
assertMarketplaceBinding(binding, source);
|
|
40398
|
-
|
|
40405
|
+
const sha2562 = projected(source, changes, budget);
|
|
40406
|
+
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
40407
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
40408
|
+
return { ...source, sha256: sha2562 };
|
|
40399
40409
|
}) };
|
|
40400
40410
|
}
|
|
40401
40411
|
function captureDiscoveryByteSources(paths) {
|
|
@@ -40519,6 +40529,19 @@ function resolveAgentDiscovery(options) {
|
|
|
40519
40529
|
}
|
|
40520
40530
|
}
|
|
40521
40531
|
}
|
|
40532
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
40533
|
+
const retained = options.retainedReview;
|
|
40534
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
40535
|
+
unresolved("invalid retained review");
|
|
40536
|
+
verifyAgentDiscovery(retained);
|
|
40537
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
40538
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
40539
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
40540
|
+
unresolved("retained settings witness names another configuration source");
|
|
40541
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual3(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
40542
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
40543
|
+
return retained;
|
|
40544
|
+
}
|
|
40522
40545
|
if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
|
|
40523
40546
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
40524
40547
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -40712,7 +40735,7 @@ export default async ({ directory }) => {
|
|
|
40712
40735
|
}
|
|
40713
40736
|
|
|
40714
40737
|
// src/lib/agent-codex.ts
|
|
40715
|
-
import { isDeepStrictEqual as
|
|
40738
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
40716
40739
|
var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
40717
40740
|
function assertCodexPathConfigEditable(text2) {
|
|
40718
40741
|
const before = Bun.TOML.parse(text2);
|
|
@@ -40722,7 +40745,7 @@ function assertCodexPathConfigEditable(text2) {
|
|
|
40722
40745
|
delete expected.skills.config;
|
|
40723
40746
|
try {
|
|
40724
40747
|
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
40725
|
-
if (
|
|
40748
|
+
if (isDeepStrictEqual4(withoutSections, expected))
|
|
40726
40749
|
return;
|
|
40727
40750
|
} catch {}
|
|
40728
40751
|
throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
|
|
@@ -40760,7 +40783,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
40760
40783
|
const candidates = new Set;
|
|
40761
40784
|
const consider = (candidate2) => {
|
|
40762
40785
|
try {
|
|
40763
|
-
if (
|
|
40786
|
+
if (isDeepStrictEqual4(Bun.TOML.parse(candidate2), expected))
|
|
40764
40787
|
candidates.add(candidate2);
|
|
40765
40788
|
} catch {}
|
|
40766
40789
|
};
|
|
@@ -41218,7 +41241,15 @@ function planAgentIntegration(options) {
|
|
|
41218
41241
|
validateProfile(profileId2);
|
|
41219
41242
|
return [agent, { command, profileId: profileId2 }];
|
|
41220
41243
|
}));
|
|
41221
|
-
const
|
|
41244
|
+
const retainedAgents = [];
|
|
41245
|
+
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
41246
|
+
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
|
|
41247
|
+
if (retainedReview) {
|
|
41248
|
+
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
41249
|
+
retainedAgents.push(agent);
|
|
41250
|
+
}
|
|
41251
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
41252
|
+
});
|
|
41222
41253
|
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 });
|
|
41223
41254
|
const changes = [];
|
|
41224
41255
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -41303,7 +41334,7 @@ function planAgentIntegration(options) {
|
|
|
41303
41334
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
41304
41335
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
41305
41336
|
recheckRootAliases(aliases);
|
|
41306
|
-
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41337
|
+
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
41307
41338
|
}
|
|
41308
41339
|
function planClaudeStopHookUpdate(options) {
|
|
41309
41340
|
return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
|
|
@@ -41422,6 +41453,8 @@ function applyAgentIntegration(plan) {
|
|
|
41422
41453
|
parseManagedSkillPolicy(change.after);
|
|
41423
41454
|
const aliases = plan.rootAliases ?? [];
|
|
41424
41455
|
recheckRootAliases(aliases);
|
|
41456
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
41457
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
41425
41458
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
41426
41459
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
41427
41460
|
for (const binding of plan.discoveryBefore ?? []) {
|