@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/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.15",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -40126,32 +40126,37 @@ function canonical3(value) {
|
|
|
40126
40126
|
return { kind: "array", items: value.items.map(canonical3) };
|
|
40127
40127
|
return value;
|
|
40128
40128
|
}
|
|
40129
|
-
|
|
40129
|
+
var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
|
|
40130
|
+
function assertClaudeHookEventsReplacement(before, replacement, events) {
|
|
40131
|
+
need3(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");
|
|
40132
|
+
const label = events.join(",");
|
|
40130
40133
|
function bounded(text2) {
|
|
40131
|
-
need3(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes,
|
|
40134
|
+
need3(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
|
|
40132
40135
|
return parse6(text2);
|
|
40133
40136
|
}
|
|
40134
40137
|
const original = bounded(before ?? "{}"), next = bounded(replacement);
|
|
40135
40138
|
const hadHooks = original.entries.some(([key]) => key === "hooks");
|
|
40136
|
-
function
|
|
40139
|
+
function withoutOwnedEvents(value) {
|
|
40137
40140
|
const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
|
|
40138
40141
|
if (hooks !== undefined) {
|
|
40139
|
-
need3(hooks.kind === "object",
|
|
40140
|
-
const
|
|
40141
|
-
|
|
40142
|
-
|
|
40142
|
+
need3(hooks.kind === "object", `${label} update requires an object of hooks`);
|
|
40143
|
+
for (const event of events) {
|
|
40144
|
+
const selected = hooks.entries.find(([key]) => key === event)?.[1];
|
|
40145
|
+
need3(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
|
|
40146
|
+
}
|
|
40147
|
+
hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
|
|
40143
40148
|
if (!hadHooks && hooks.entries.length === 0)
|
|
40144
40149
|
value.entries = value.entries.filter(([key]) => key !== "hooks");
|
|
40145
40150
|
}
|
|
40146
40151
|
return canonical3(value);
|
|
40147
40152
|
}
|
|
40148
|
-
need3(JSON.stringify(
|
|
40153
|
+
need3(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
|
|
40149
40154
|
}
|
|
40150
40155
|
var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
40151
40156
|
function inferencePreference(key, child) {
|
|
40152
40157
|
if (key === "skipDangerousModePermissionPrompt") {
|
|
40153
40158
|
need3(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
|
|
40154
|
-
return
|
|
40159
|
+
return false;
|
|
40155
40160
|
}
|
|
40156
40161
|
if (key === "effortLevel") {
|
|
40157
40162
|
need3(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
|
|
@@ -40262,14 +40267,20 @@ function unchanged2(before) {
|
|
|
40262
40267
|
need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
40263
40268
|
}
|
|
40264
40269
|
|
|
40270
|
+
// src/lib/codex-hook-additions-review.ts
|
|
40271
|
+
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
40272
|
+
var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
40273
|
+
var proofs = new WeakMap;
|
|
40274
|
+
|
|
40265
40275
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
40266
40276
|
var sha = (text2) => createHash14("sha256").update(text2).digest("hex");
|
|
40267
|
-
var
|
|
40277
|
+
var proofs2 = new WeakMap;
|
|
40268
40278
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
40269
|
-
const proof =
|
|
40279
|
+
const proof = proofs2.get(witness);
|
|
40270
40280
|
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
40271
40281
|
unchanged2(proof.before);
|
|
40272
40282
|
unchanged2(proof.current);
|
|
40283
|
+
proof.recheckAdditions();
|
|
40273
40284
|
}
|
|
40274
40285
|
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
40275
40286
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
@@ -41301,6 +41312,34 @@ function planAgentIntegration(options) {
|
|
|
41301
41312
|
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41302
41313
|
}
|
|
41303
41314
|
function planClaudeStopHookUpdate(options) {
|
|
41315
|
+
return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
|
|
41316
|
+
}
|
|
41317
|
+
function planClaudePreToolUseHookUpdate(options) {
|
|
41318
|
+
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
41319
|
+
}
|
|
41320
|
+
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
41321
|
+
const canonical4 = (value) => canonicalAgentPath(value, aliases);
|
|
41322
|
+
const containsSettings = (root2) => {
|
|
41323
|
+
const distance = relative3(canonical4(root2), path);
|
|
41324
|
+
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep4}`) && !isAbsolute15(distance);
|
|
41325
|
+
};
|
|
41326
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical4(join21(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical4(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
41327
|
+
}
|
|
41328
|
+
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
41329
|
+
try {
|
|
41330
|
+
if (binding.method !== "automatic" && binding.method !== "reviewed")
|
|
41331
|
+
throw new Error("Invalid managed discovery method");
|
|
41332
|
+
verifyAgentDiscovery(binding);
|
|
41333
|
+
if (binding.method === "automatic") {
|
|
41334
|
+
const current = resolveAgentDiscovery({ home, agent: binding.agent, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
41335
|
+
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
41336
|
+
throw new Error("Configured native discovery roots changed during Claude update");
|
|
41337
|
+
}
|
|
41338
|
+
} catch (error) {
|
|
41339
|
+
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
41340
|
+
}
|
|
41341
|
+
}
|
|
41342
|
+
function planClaudeHookEventsUpdate(options) {
|
|
41304
41343
|
const home = resolve14(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
41305
41344
|
const policyPath = join21(dataDir2, "agent-policy.json");
|
|
41306
41345
|
assertSafePath(policyPath);
|
|
@@ -41318,31 +41357,42 @@ function planClaudeStopHookUpdate(options) {
|
|
|
41318
41357
|
const before = lstatSync18(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
41319
41358
|
if ((before === null ? "absent" : sha2(before)) !== options.expectedSettingsSha256)
|
|
41320
41359
|
throw new Error("Claude settings preimage changed");
|
|
41321
|
-
|
|
41360
|
+
assertClaudeHookEventsReplacement(before, options.replacement, options.events);
|
|
41322
41361
|
const agents = bridge.agents;
|
|
41323
41362
|
const discoveries = Object.entries(bridge.discovery).map(([agent, binding]) => {
|
|
41324
|
-
if (!agents.includes(agent) || binding?.agent !== agent)
|
|
41325
|
-
throw new Error("Invalid managed discovery owner");
|
|
41363
|
+
if (!agents.includes(agent) || binding?.agent !== agent || typeof bridge.commands?.[agent] !== "string" || typeof bridge.profiles?.[agent] !== "string")
|
|
41364
|
+
throw new Error("Invalid managed discovery owner or command/profile binding");
|
|
41326
41365
|
return binding;
|
|
41327
41366
|
});
|
|
41328
|
-
if (discoveries.length !== agents.length)
|
|
41329
|
-
throw new Error("Missing managed discovery coverage");
|
|
41330
|
-
for (const
|
|
41367
|
+
if (discoveries.length !== agents.length || !agents.includes("claude"))
|
|
41368
|
+
throw new Error("Missing managed Claude discovery coverage");
|
|
41369
|
+
for (const binding of discoveries)
|
|
41370
|
+
verifyCoordinatedDiscovery(binding, home, aliases);
|
|
41371
|
+
const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
|
|
41372
|
+
for (const agent of consumers)
|
|
41331
41373
|
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
|
|
41332
41374
|
const replacements = new Map(before === options.replacement ? [] : [[path, options.replacement]]);
|
|
41333
|
-
const discoveryAfter = discoveries.map((binding) =>
|
|
41375
|
+
const discoveryAfter = discoveries.map((binding) => {
|
|
41376
|
+
const next = rebindAgentDiscovery(binding, replacements);
|
|
41377
|
+
for (const [index, source] of binding.sources.entries()) {
|
|
41378
|
+
if (source.path !== path && JSON.stringify(next.sources[index]) !== JSON.stringify(source)) {
|
|
41379
|
+
throw new Error(`Unrelated native discovery input changed during Claude update: ${source.path}`);
|
|
41380
|
+
}
|
|
41381
|
+
}
|
|
41382
|
+
return JSON.stringify(next) === JSON.stringify(binding) ? binding : next;
|
|
41383
|
+
});
|
|
41334
41384
|
const nextPolicy = { ...policy, bridge: { ...bridge, discovery: Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) } };
|
|
41335
41385
|
const changes = before === options.replacement ? [] : [{ path, before, after: options.replacement }];
|
|
41336
41386
|
if (JSON.stringify(nextPolicy) !== JSON.stringify(policy))
|
|
41337
41387
|
changes.push({ path: policyPath, before: snapshot2.text, after: serializeManagedSkillPolicy(nextPolicy) });
|
|
41338
41388
|
recheckRootAliases(aliases);
|
|
41339
41389
|
const nativeSkills = inventoryNativeSkills(home, {
|
|
41340
|
-
agents,
|
|
41390
|
+
agents: consumers,
|
|
41341
41391
|
projectDir: home,
|
|
41342
41392
|
includeVendor: true,
|
|
41343
|
-
guardHermes:
|
|
41393
|
+
guardHermes: consumers.includes("hermes"),
|
|
41344
41394
|
allowRootAliases: aliases.length > 0,
|
|
41345
|
-
agentRoots: discoveries.flatMap((binding) => binding.roots.map((path2) => ({ agent: binding.agent, path: path2 })))
|
|
41395
|
+
agentRoots: discoveries.filter((binding) => consumers.includes(binding.agent)).flatMap((binding) => binding.roots.map((path2) => ({ agent: binding.agent, path: path2 })))
|
|
41346
41396
|
});
|
|
41347
41397
|
return {
|
|
41348
41398
|
dataDir: dataDir2,
|
|
@@ -41353,7 +41403,7 @@ function planClaudeStopHookUpdate(options) {
|
|
|
41353
41403
|
discoveryBefore: discoveries,
|
|
41354
41404
|
discoveryAfter,
|
|
41355
41405
|
rootAliases: aliases,
|
|
41356
|
-
managedAgentChecks: { home, agents:
|
|
41406
|
+
managedAgentChecks: { home, agents: consumers }
|
|
41357
41407
|
};
|
|
41358
41408
|
}
|
|
41359
41409
|
function atomicWrite2(path, content) {
|
|
@@ -41380,8 +41430,12 @@ function applyAgentIntegration(plan) {
|
|
|
41380
41430
|
recheckRootAliases(aliases);
|
|
41381
41431
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
41382
41432
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
41383
|
-
for (const binding of plan.discoveryBefore ?? [])
|
|
41384
|
-
|
|
41433
|
+
for (const binding of plan.discoveryBefore ?? []) {
|
|
41434
|
+
if (plan.managedAgentChecks)
|
|
41435
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
41436
|
+
else
|
|
41437
|
+
verifyAgentDiscovery(binding);
|
|
41438
|
+
}
|
|
41385
41439
|
if (plan.observedPolicy && currentText(plan.observedPolicy.path) !== plan.observedPolicy.before)
|
|
41386
41440
|
throw new Error("Agent policy changed after planning");
|
|
41387
41441
|
for (const change of plan.changes)
|
|
@@ -41413,8 +41467,12 @@ function applyAgentIntegration(plan) {
|
|
|
41413
41467
|
written.push({ path: change.path, before: change.before, after });
|
|
41414
41468
|
}
|
|
41415
41469
|
recheckRootAliases(aliases);
|
|
41416
|
-
for (const binding of plan.discoveryAfter ?? [])
|
|
41417
|
-
|
|
41470
|
+
for (const binding of plan.discoveryAfter ?? []) {
|
|
41471
|
+
if (plan.managedAgentChecks)
|
|
41472
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
41473
|
+
else
|
|
41474
|
+
verifyAgentDiscovery(binding);
|
|
41475
|
+
}
|
|
41418
41476
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
41419
41477
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
41420
41478
|
atomicWrite2(join21(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha2(change.before), afterHash: sha2(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
package/docs/plugin-admission.md
CHANGED
|
@@ -294,9 +294,11 @@ addition to the v1 display/model selections, it tolerates the documented
|
|
|
294
294
|
persisted effort values `low`, `medium`, `high`, and `xhigh`, both at the top
|
|
295
295
|
level and in known built-in model entries whose only property is `effortLevel`.
|
|
296
296
|
Custom model entries and entries with any other property remain fully bound.
|
|
297
|
-
The boolean `skipDangerousModePermissionPrompt`
|
|
298
|
-
|
|
299
|
-
plugins, hooks, environment, ultracode and unknown
|
|
297
|
+
The boolean `skipDangerousModePermissionPrompt` remains bound: Claude also uses
|
|
298
|
+
this saved acceptance in bypass-mode and pending project MCP approval decisions.
|
|
299
|
+
Permission settings, plugins, hooks, environment, ultracode and unknown
|
|
300
|
+
configuration remain bound. Version 0.9.14 corrects the 0.9.13 normalization of
|
|
301
|
+
this flag; an existing v2 review containing it must be explicitly reviewed again.
|
|
300
302
|
Invalid recognized preference types are refused.
|
|
301
303
|
|
|
302
304
|
`captureClaudeMarketplaceRegistryV2(path)` creates a
|
|
@@ -322,3 +324,25 @@ preference: its owning installer must coordinate a guarded settings/policy
|
|
|
322
324
|
update, or it needs a fresh explicit review.
|
|
323
325
|
|
|
324
326
|
The effort schema follows [Claude's model configuration documentation](https://code.claude.com/docs/en/model-config#adjust-effort-level).
|
|
327
|
+
|
|
328
|
+
Cooperating installers can use `planClaudeHookEventsUpdate` with an explicit
|
|
329
|
+
selection of supported Claude hook events. `planClaudePreToolUseHookUpdate` and
|
|
330
|
+
`planClaudeStopHookUpdate` are single-event convenience wrappers. Supported events
|
|
331
|
+
are `PreToolUse`, `PostToolUse`, `Stop`, `Notification`, `SessionStart`, `SessionEnd`
|
|
332
|
+
`UserPromptSubmit` and `SubagentStart`. The transaction requires the exact settings SHA-256
|
|
333
|
+
preimage and preserves every field outside the selected events, including other
|
|
334
|
+
hooks. Apply the returned plan through `applyAgentIntegration`; this validates
|
|
335
|
+
existing managed discovery and native guards, writes settings and policy with
|
|
336
|
+
precondition checks, and retains recovery backups. An invalid policy or existing
|
|
337
|
+
drift is a refusal, never permission to use an uncoordinated writer. A null plan
|
|
338
|
+
means no managed Skills policy exists. These APIs do not authorize the hook's
|
|
339
|
+
behavior or turn a configuration-changing hook into a preference.
|
|
340
|
+
|
|
341
|
+
The transaction verifies every agent's discovery sources, directories and
|
|
342
|
+
automatic discovery closure. Native runtime and trust checks cover Claude and
|
|
343
|
+
the declared consumers of the settings path, including containing roots or
|
|
344
|
+
directories and OpenCode's Claude compatibility surface. Unrelated agents keep
|
|
345
|
+
their exact witnesses and trust state; they must still pass their own consumer
|
|
346
|
+
checks before use. The plan's `managedAgentChecks.agents` and `nativeSkills`
|
|
347
|
+
describe this scoped runtime check, not a fleet health result. A path-identity
|
|
348
|
+
witness intersecting the write still refuses instead of adopting a new inode.
|