@hasna/skills 0.9.13 → 0.9.15
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/bin/index.js +271 -215
- package/bin/mcp.js +26 -14
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +88 -27
- package/dist/lib/agent-codex-trust.d.ts +2 -1
- package/dist/lib/agent-integration.d.ts +9 -1
- package/dist/lib/claude-settings-witness.d.ts +5 -1
- 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 +107 -49
- package/docs/plugin-admission.md +27 -3
- 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.15",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -47083,7 +47083,7 @@ function canonical2(value) {
|
|
|
47083
47083
|
function inferencePreference(key, child) {
|
|
47084
47084
|
if (key === "skipDangerousModePermissionPrompt") {
|
|
47085
47085
|
need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
|
|
47086
|
-
return
|
|
47086
|
+
return false;
|
|
47087
47087
|
}
|
|
47088
47088
|
if (key === "effortLevel") {
|
|
47089
47089
|
need2(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
|
|
@@ -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,11 +48822,12 @@ 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
|
|
@@ -59783,17 +59795,17 @@ var WSContext = class {
|
|
|
59783
59795
|
if (typeof args[0] === "function") {
|
|
59784
59796
|
const [createEvents, options] = args;
|
|
59785
59797
|
return async function upgradeWebSocket(c, next) {
|
|
59786
|
-
const
|
|
59787
|
-
const result = await handler(c,
|
|
59798
|
+
const events2 = await createEvents(c);
|
|
59799
|
+
const result = await handler(c, events2, options);
|
|
59788
59800
|
if (result) {
|
|
59789
59801
|
return result;
|
|
59790
59802
|
}
|
|
59791
59803
|
await next();
|
|
59792
59804
|
};
|
|
59793
59805
|
} else {
|
|
59794
|
-
const [c,
|
|
59806
|
+
const [c, events2, options] = args;
|
|
59795
59807
|
return (async () => {
|
|
59796
|
-
const upgraded = await handler(c,
|
|
59808
|
+
const upgraded = await handler(c, events2, options);
|
|
59797
59809
|
if (!upgraded) {
|
|
59798
59810
|
throw new Error("Failed to upgrade WebSocket");
|
|
59799
59811
|
}
|
|
@@ -60990,7 +61002,7 @@ var init_dist2 = __esm(() => {
|
|
|
60990
61002
|
"sec-websocket-extensions",
|
|
60991
61003
|
"sec-websocket-protocol"
|
|
60992
61004
|
]);
|
|
60993
|
-
upgradeWebSocket = defineWebSocketHelper(async (c,
|
|
61005
|
+
upgradeWebSocket = defineWebSocketHelper(async (c, events2, options) => {
|
|
60994
61006
|
if (c.req.header("upgrade")?.toLowerCase() !== "websocket")
|
|
60995
61007
|
return;
|
|
60996
61008
|
const env = c.env;
|
|
@@ -61027,7 +61039,7 @@ var init_dist2 = __esm(() => {
|
|
|
61027
61039
|
url: new URL(c.req.url)
|
|
61028
61040
|
};
|
|
61029
61041
|
try {
|
|
61030
|
-
|
|
61042
|
+
events2?.onOpen?.(new Event("open"), ctx);
|
|
61031
61043
|
} catch (e2) {
|
|
61032
61044
|
(options?.onError ?? console.error)(e2);
|
|
61033
61045
|
}
|
|
@@ -61035,7 +61047,7 @@ var init_dist2 = __esm(() => {
|
|
|
61035
61047
|
const datas = Array.isArray(data) ? data : [data];
|
|
61036
61048
|
for (const data2 of datas)
|
|
61037
61049
|
try {
|
|
61038
|
-
|
|
61050
|
+
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
61051
|
} catch (e2) {
|
|
61040
61052
|
(options?.onError ?? console.error)(e2);
|
|
61041
61053
|
}
|
|
@@ -61048,7 +61060,7 @@ var init_dist2 = __esm(() => {
|
|
|
61048
61060
|
});
|
|
61049
61061
|
ws.on("close", (code2, reason) => {
|
|
61050
61062
|
try {
|
|
61051
|
-
|
|
61063
|
+
events2?.onClose?.(new CloseEvent("close", {
|
|
61052
61064
|
code: code2,
|
|
61053
61065
|
reason: reason.toString()
|
|
61054
61066
|
}), ctx);
|
|
@@ -61058,7 +61070,7 @@ var init_dist2 = __esm(() => {
|
|
|
61058
61070
|
});
|
|
61059
61071
|
ws.on("error", (error61) => {
|
|
61060
61072
|
try {
|
|
61061
|
-
|
|
61073
|
+
events2?.onError?.(new ErrorEvent("error", { error: error61 }), ctx);
|
|
61062
61074
|
} catch (e2) {
|
|
61063
61075
|
(options?.onError ?? console.error)(e2);
|
|
61064
61076
|
}
|
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.15",
|
|
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.15",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
package/dist/index.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export type { SkillSelection, SkillProfile, ResolvedSkillSelection, ResolvedSkil
|
|
|
60
60
|
export { buildSkillContext } from "./lib/skill-context.js";
|
|
61
61
|
export { syncSelectionProfile } from "./lib/selection-resolver.js";
|
|
62
62
|
export { inspectSkillSession, reconcileSkillSession, type SessionReconciliationInput, type SessionReconciliationOptions } from "./lib/session-reconciliation.js";
|
|
63
|
-
export { planAgentIntegration, planClaudeStopHookUpdate, applyAgentIntegration, inventoryNativeSkills, archiveNativeSkills } from "./lib/agent-integration.js";
|
|
63
|
+
export { planAgentIntegration, planClaudeStopHookUpdate, planClaudePreToolUseHookUpdate, planClaudeHookEventsUpdate, applyAgentIntegration, inventoryNativeSkills, archiveNativeSkills } from "./lib/agent-integration.js";
|
|
64
64
|
export { captureDiscoveryDirectories, captureDiscoveryByteSources, captureDiscoveryPathSources, type DiscoveryDirectory, type DiscoverySource } from "./lib/agent-discovery.js";
|
|
65
65
|
export { captureClaudeMarketplaceRegistry, captureClaudeMarketplaceRegistryV2 } from "./lib/claude-marketplace-registry.js";
|
|
66
66
|
export { captureClaudeSettings, captureClaudeSettingsV2 } from "./lib/claude-settings-witness.js";
|
|
@@ -69,3 +69,4 @@ export { planPluginAdmission, admitPlugin, resolveAdmittedPlugin, pluginResolver
|
|
|
69
69
|
export { captureManagedPluginRegistry, type ManagedPluginRegistrationWitness } from "./lib/plugin-discovery.js";
|
|
70
70
|
export type { RemoteCreditCheckoutOptions, RemoteCreditCheckout, RemoteCreditCheckoutErrorCode } from "./lib/remote-credit-checkout.js";
|
|
71
71
|
export { assertPortableAuthoringPath, type PortableAuthoringPathOptions } from "./lib/authoring-path.js";
|
|
72
|
+
export { CLAUDE_COORDINATED_HOOK_EVENTS, type ClaudeCoordinatedHookEvent } from "./lib/claude-settings-witness.js";
|
package/dist/index.js
CHANGED
|
@@ -9610,7 +9610,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
|
|
|
9610
9610
|
// package.json
|
|
9611
9611
|
var package_default = {
|
|
9612
9612
|
name: "@hasna/skills",
|
|
9613
|
-
version: "0.9.
|
|
9613
|
+
version: "0.9.15",
|
|
9614
9614
|
description: "Skills library for AI coding agents",
|
|
9615
9615
|
type: "module",
|
|
9616
9616
|
bin: {
|
|
@@ -12846,32 +12846,37 @@ function canonical2(value) {
|
|
|
12846
12846
|
return { kind: "array", items: value.items.map(canonical2) };
|
|
12847
12847
|
return value;
|
|
12848
12848
|
}
|
|
12849
|
-
|
|
12849
|
+
var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
|
|
12850
|
+
function assertClaudeHookEventsReplacement(before, replacement, events) {
|
|
12851
|
+
need2(Array.isArray(events) && events.length > 0 && events.length <= CLAUDE_COORDINATED_HOOK_EVENTS.length && new Set(events).size === events.length && events.every((event) => CLAUDE_COORDINATED_HOOK_EVENTS.includes(event)), "requires explicit supported hook events");
|
|
12852
|
+
const label = events.join(",");
|
|
12850
12853
|
function bounded(text2) {
|
|
12851
|
-
need2(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes,
|
|
12854
|
+
need2(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
|
|
12852
12855
|
return parse3(text2);
|
|
12853
12856
|
}
|
|
12854
12857
|
const original = bounded(before ?? "{}"), next = bounded(replacement);
|
|
12855
12858
|
const hadHooks = original.entries.some(([key]) => key === "hooks");
|
|
12856
|
-
function
|
|
12859
|
+
function withoutOwnedEvents(value) {
|
|
12857
12860
|
const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
|
|
12858
12861
|
if (hooks !== undefined) {
|
|
12859
|
-
need2(hooks.kind === "object",
|
|
12860
|
-
const
|
|
12861
|
-
|
|
12862
|
-
|
|
12862
|
+
need2(hooks.kind === "object", `${label} update requires an object of hooks`);
|
|
12863
|
+
for (const event of events) {
|
|
12864
|
+
const selected = hooks.entries.find(([key]) => key === event)?.[1];
|
|
12865
|
+
need2(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
|
|
12866
|
+
}
|
|
12867
|
+
hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
|
|
12863
12868
|
if (!hadHooks && hooks.entries.length === 0)
|
|
12864
12869
|
value.entries = value.entries.filter(([key]) => key !== "hooks");
|
|
12865
12870
|
}
|
|
12866
12871
|
return canonical2(value);
|
|
12867
12872
|
}
|
|
12868
|
-
need2(JSON.stringify(
|
|
12873
|
+
need2(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
|
|
12869
12874
|
}
|
|
12870
12875
|
var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
12871
12876
|
function inferencePreference(key, child) {
|
|
12872
12877
|
if (key === "skipDangerousModePermissionPrompt") {
|
|
12873
12878
|
need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
|
|
12874
|
-
return
|
|
12879
|
+
return false;
|
|
12875
12880
|
}
|
|
12876
12881
|
if (key === "effortLevel") {
|
|
12877
12882
|
need2(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
|
|
@@ -13452,6 +13457,34 @@ function planAgentIntegration(options) {
|
|
|
13452
13457
|
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
13453
13458
|
}
|
|
13454
13459
|
function planClaudeStopHookUpdate(options) {
|
|
13460
|
+
return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
|
|
13461
|
+
}
|
|
13462
|
+
function planClaudePreToolUseHookUpdate(options) {
|
|
13463
|
+
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
13464
|
+
}
|
|
13465
|
+
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
13466
|
+
const canonical3 = (value) => canonicalAgentPath(value, aliases);
|
|
13467
|
+
const containsSettings = (root) => {
|
|
13468
|
+
const distance = relative4(canonical3(root), path);
|
|
13469
|
+
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep4}`) && !isAbsolute9(distance);
|
|
13470
|
+
};
|
|
13471
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical3(join16(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical3(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
13472
|
+
}
|
|
13473
|
+
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
13474
|
+
try {
|
|
13475
|
+
if (binding.method !== "automatic" && binding.method !== "reviewed")
|
|
13476
|
+
throw new Error("Invalid managed discovery method");
|
|
13477
|
+
verifyAgentDiscovery(binding);
|
|
13478
|
+
if (binding.method === "automatic") {
|
|
13479
|
+
const current = resolveAgentDiscovery({ home, agent: binding.agent, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
13480
|
+
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
13481
|
+
throw new Error("Configured native discovery roots changed during Claude update");
|
|
13482
|
+
}
|
|
13483
|
+
} catch (error) {
|
|
13484
|
+
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
13485
|
+
}
|
|
13486
|
+
}
|
|
13487
|
+
function planClaudeHookEventsUpdate(options) {
|
|
13455
13488
|
const home = resolve9(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
13456
13489
|
const policyPath = join16(dataDir2, "agent-policy.json");
|
|
13457
13490
|
assertSafePath(policyPath);
|
|
@@ -13469,31 +13502,42 @@ function planClaudeStopHookUpdate(options) {
|
|
|
13469
13502
|
const before = lstatSync14(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
13470
13503
|
if ((before === null ? "absent" : sha(before)) !== options.expectedSettingsSha256)
|
|
13471
13504
|
throw new Error("Claude settings preimage changed");
|
|
13472
|
-
|
|
13505
|
+
assertClaudeHookEventsReplacement(before, options.replacement, options.events);
|
|
13473
13506
|
const agents = bridge.agents;
|
|
13474
13507
|
const discoveries = Object.entries(bridge.discovery).map(([agent, binding]) => {
|
|
13475
|
-
if (!agents.includes(agent) || binding?.agent !== agent)
|
|
13476
|
-
throw new Error("Invalid managed discovery owner");
|
|
13508
|
+
if (!agents.includes(agent) || binding?.agent !== agent || typeof bridge.commands?.[agent] !== "string" || typeof bridge.profiles?.[agent] !== "string")
|
|
13509
|
+
throw new Error("Invalid managed discovery owner or command/profile binding");
|
|
13477
13510
|
return binding;
|
|
13478
13511
|
});
|
|
13479
|
-
if (discoveries.length !== agents.length)
|
|
13480
|
-
throw new Error("Missing managed discovery coverage");
|
|
13481
|
-
for (const
|
|
13512
|
+
if (discoveries.length !== agents.length || !agents.includes("claude"))
|
|
13513
|
+
throw new Error("Missing managed Claude discovery coverage");
|
|
13514
|
+
for (const binding of discoveries)
|
|
13515
|
+
verifyCoordinatedDiscovery(binding, home, aliases);
|
|
13516
|
+
const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
|
|
13517
|
+
for (const agent of consumers)
|
|
13482
13518
|
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
|
|
13483
13519
|
const replacements = new Map(before === options.replacement ? [] : [[path, options.replacement]]);
|
|
13484
|
-
const discoveryAfter = discoveries.map((binding) =>
|
|
13520
|
+
const discoveryAfter = discoveries.map((binding) => {
|
|
13521
|
+
const next = rebindAgentDiscovery(binding, replacements);
|
|
13522
|
+
for (const [index, source] of binding.sources.entries()) {
|
|
13523
|
+
if (source.path !== path && JSON.stringify(next.sources[index]) !== JSON.stringify(source)) {
|
|
13524
|
+
throw new Error(`Unrelated native discovery input changed during Claude update: ${source.path}`);
|
|
13525
|
+
}
|
|
13526
|
+
}
|
|
13527
|
+
return JSON.stringify(next) === JSON.stringify(binding) ? binding : next;
|
|
13528
|
+
});
|
|
13485
13529
|
const nextPolicy = { ...policy, bridge: { ...bridge, discovery: Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) } };
|
|
13486
13530
|
const changes = before === options.replacement ? [] : [{ path, before, after: options.replacement }];
|
|
13487
13531
|
if (JSON.stringify(nextPolicy) !== JSON.stringify(policy))
|
|
13488
13532
|
changes.push({ path: policyPath, before: snapshot.text, after: serializeManagedSkillPolicy(nextPolicy) });
|
|
13489
13533
|
recheckRootAliases(aliases);
|
|
13490
13534
|
const nativeSkills = inventoryNativeSkills(home, {
|
|
13491
|
-
agents,
|
|
13535
|
+
agents: consumers,
|
|
13492
13536
|
projectDir: home,
|
|
13493
13537
|
includeVendor: true,
|
|
13494
|
-
guardHermes:
|
|
13538
|
+
guardHermes: consumers.includes("hermes"),
|
|
13495
13539
|
allowRootAliases: aliases.length > 0,
|
|
13496
|
-
agentRoots: discoveries.flatMap((binding) => binding.roots.map((path2) => ({ agent: binding.agent, path: path2 })))
|
|
13540
|
+
agentRoots: discoveries.filter((binding) => consumers.includes(binding.agent)).flatMap((binding) => binding.roots.map((path2) => ({ agent: binding.agent, path: path2 })))
|
|
13497
13541
|
});
|
|
13498
13542
|
return {
|
|
13499
13543
|
dataDir: dataDir2,
|
|
@@ -13504,7 +13548,7 @@ function planClaudeStopHookUpdate(options) {
|
|
|
13504
13548
|
discoveryBefore: discoveries,
|
|
13505
13549
|
discoveryAfter,
|
|
13506
13550
|
rootAliases: aliases,
|
|
13507
|
-
managedAgentChecks: { home, agents:
|
|
13551
|
+
managedAgentChecks: { home, agents: consumers }
|
|
13508
13552
|
};
|
|
13509
13553
|
}
|
|
13510
13554
|
function atomicWrite2(path, content) {
|
|
@@ -13531,8 +13575,12 @@ function applyAgentIntegration(plan) {
|
|
|
13531
13575
|
recheckRootAliases(aliases);
|
|
13532
13576
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
13533
13577
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
13534
|
-
for (const binding of plan.discoveryBefore ?? [])
|
|
13535
|
-
|
|
13578
|
+
for (const binding of plan.discoveryBefore ?? []) {
|
|
13579
|
+
if (plan.managedAgentChecks)
|
|
13580
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
13581
|
+
else
|
|
13582
|
+
verifyAgentDiscovery(binding);
|
|
13583
|
+
}
|
|
13536
13584
|
if (plan.observedPolicy && currentText(plan.observedPolicy.path) !== plan.observedPolicy.before)
|
|
13537
13585
|
throw new Error("Agent policy changed after planning");
|
|
13538
13586
|
for (const change of plan.changes)
|
|
@@ -13564,8 +13612,12 @@ function applyAgentIntegration(plan) {
|
|
|
13564
13612
|
written.push({ path: change.path, before: change.before, after });
|
|
13565
13613
|
}
|
|
13566
13614
|
recheckRootAliases(aliases);
|
|
13567
|
-
for (const binding of plan.discoveryAfter ?? [])
|
|
13568
|
-
|
|
13615
|
+
for (const binding of plan.discoveryAfter ?? []) {
|
|
13616
|
+
if (plan.managedAgentChecks)
|
|
13617
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
13618
|
+
else
|
|
13619
|
+
verifyAgentDiscovery(binding);
|
|
13620
|
+
}
|
|
13569
13621
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
13570
13622
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
13571
13623
|
atomicWrite2(join16(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha(change.before), afterHash: sha(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
@@ -16387,14 +16439,20 @@ function unchanged2(before) {
|
|
|
16387
16439
|
need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
16388
16440
|
}
|
|
16389
16441
|
|
|
16442
|
+
// src/lib/codex-hook-additions-review.ts
|
|
16443
|
+
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
16444
|
+
var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
16445
|
+
var proofs = new WeakMap;
|
|
16446
|
+
|
|
16390
16447
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
16391
16448
|
var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
16392
|
-
var
|
|
16449
|
+
var proofs2 = new WeakMap;
|
|
16393
16450
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
16394
|
-
const proof =
|
|
16451
|
+
const proof = proofs2.get(witness);
|
|
16395
16452
|
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
16396
16453
|
unchanged2(proof.before);
|
|
16397
16454
|
unchanged2(proof.current);
|
|
16455
|
+
proof.recheckAdditions();
|
|
16398
16456
|
}
|
|
16399
16457
|
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
16400
16458
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
@@ -43658,6 +43716,8 @@ export {
|
|
|
43658
43716
|
planSkillsS3SnapshotUpload,
|
|
43659
43717
|
planPluginAdmission,
|
|
43660
43718
|
planClaudeStopHookUpdate,
|
|
43719
|
+
planClaudePreToolUseHookUpdate,
|
|
43720
|
+
planClaudeHookEventsUpdate,
|
|
43661
43721
|
planAgentIntegration,
|
|
43662
43722
|
pinSkill,
|
|
43663
43723
|
pinProjectSkill,
|
|
@@ -43861,6 +43921,7 @@ export {
|
|
|
43861
43921
|
DEFAULT_EXPORT_DIR,
|
|
43862
43922
|
ContentHashInputError,
|
|
43863
43923
|
CONTENT_HASH_LIMITS,
|
|
43924
|
+
CLAUDE_COORDINATED_HOOK_EVENTS,
|
|
43864
43925
|
CATEGORIES,
|
|
43865
43926
|
BASIC_SKILL_NAMES,
|
|
43866
43927
|
ARTICLE_GENERATION_SLUG,
|
|
@@ -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;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AgentDiscoveryBinding, type ReviewedDiscoveryInputs } from "./agent-discovery.js";
|
|
2
2
|
import { type IntegrationAgent } from "./agent-adapters.js";
|
|
3
3
|
import type { CodexHookDiscoveryRecovery } from "./codex-hook-discovery-recovery.js";
|
|
4
|
+
import { type ClaudeCoordinatedHookEvent } from "./claude-settings-witness.js";
|
|
4
5
|
export type { IntegrationAgent } from "./agent-adapters.js";
|
|
5
6
|
export type ContextHookEvent = "UserPromptSubmit" | "SessionStart" | "SubagentStart";
|
|
6
7
|
export interface AgentRootAlias {
|
|
@@ -96,11 +97,18 @@ export declare function planAgentIntegration(options: {
|
|
|
96
97
|
* every other settings change and owns discovery witnesses and guarded writes.
|
|
97
98
|
* No policy means this installer has no Skills integration to update.
|
|
98
99
|
*/
|
|
99
|
-
export
|
|
100
|
+
export interface ClaudeHookUpdateOptions {
|
|
100
101
|
home?: string;
|
|
101
102
|
dataDir?: string;
|
|
102
103
|
expectedSettingsSha256: string;
|
|
103
104
|
replacement: string;
|
|
105
|
+
}
|
|
106
|
+
export declare function planClaudeStopHookUpdate(options: ClaudeHookUpdateOptions): AgentIntegrationPlan | null;
|
|
107
|
+
/** Cooperating safety installers may replace only PreToolUse. The caller owns
|
|
108
|
+
* its exact hook authorization; Skills retains discovery and native guards. */
|
|
109
|
+
export declare function planClaudePreToolUseHookUpdate(options: ClaudeHookUpdateOptions): AgentIntegrationPlan | null;
|
|
110
|
+
export declare function planClaudeHookEventsUpdate(options: ClaudeHookUpdateOptions & {
|
|
111
|
+
events: readonly ClaudeCoordinatedHookEvent[];
|
|
104
112
|
}): AgentIntegrationPlan | null;
|
|
105
113
|
export declare function applyAgentIntegration(plan: AgentIntegrationPlan): {
|
|
106
114
|
changed: string[];
|
|
@@ -9,12 +9,16 @@ export declare const CLAUDE_SETTINGS_WITNESS_LIMITS: Readonly<{
|
|
|
9
9
|
export interface ClaudeSettingsWitnessBudget {
|
|
10
10
|
remaining: number;
|
|
11
11
|
}
|
|
12
|
-
/** Cooperating installers may replace
|
|
12
|
+
/** Cooperating installers may replace only their explicitly selected event. Compare the strict syntax
|
|
13
13
|
* trees, not JSON.parse values or the preference-tolerant witness digest: all
|
|
14
14
|
* other hooks, discovery settings, unknown values and number spellings remain
|
|
15
15
|
* identical. Whitespace and object-key order have no configuration meaning.
|
|
16
16
|
*/
|
|
17
|
+
export declare const CLAUDE_COORDINATED_HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
|
|
18
|
+
export type ClaudeCoordinatedHookEvent = typeof CLAUDE_COORDINATED_HOOK_EVENTS[number];
|
|
19
|
+
export declare function assertClaudeHookEventsReplacement(before: string | null, replacement: string, events: readonly ClaudeCoordinatedHookEvent[]): void;
|
|
17
20
|
export declare function assertClaudeStopHookReplacement(before: string | null, replacement: string): void;
|
|
21
|
+
export declare function assertClaudePreToolUseHookReplacement(before: string | null, replacement: string): void;
|
|
18
22
|
/** Hash only reviewed installer-rendered settings after the disk preimage has been verified. */
|
|
19
23
|
export declare function hashClaudeSettingsReplacement(text: string, budget: ClaudeSettingsWitnessBudget): string;
|
|
20
24
|
/** Explicit capture never converts a stored legacy raw witness or changes settings. */
|
|
@@ -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. */
|