@hasna/skills 0.10.45 → 0.10.46

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/dist/index.js CHANGED
@@ -6940,7 +6940,7 @@ var require_public_api = __commonJS((exports) => {
6940
6940
  }
6941
6941
  return doc;
6942
6942
  }
6943
- function parse(src, reviver, options) {
6943
+ function parse2(src, reviver, options) {
6944
6944
  let _reviver = undefined;
6945
6945
  if (typeof reviver === "function") {
6946
6946
  _reviver = reviver;
@@ -6981,7 +6981,7 @@ var require_public_api = __commonJS((exports) => {
6981
6981
  return value.toString(options);
6982
6982
  return new Document.Document(value, _replacer, options).toString(options);
6983
6983
  }
6984
- exports.parse = parse;
6984
+ exports.parse = parse2;
6985
6985
  exports.parseAllDocuments = parseAllDocuments;
6986
6986
  exports.parseDocument = parseDocument;
6987
6987
  exports.stringify = stringify;
@@ -8991,8 +8991,371 @@ export default async ({ directory }) => {
8991
8991
  `;
8992
8992
  }
8993
8993
 
8994
+ // src/lib/sumi-settings-witness.ts
8995
+ import { createHash as createHash3 } from "crypto";
8996
+
8997
+ // src/lib/claude-settings-witness.ts
8998
+ import { createHash as createHash2 } from "crypto";
8999
+ import { closeSync, constants, fstatSync, lstatSync as lstatSync3, openSync, readSync } from "fs";
9000
+ import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
9001
+ var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
9002
+ function need(value, reason) {
9003
+ if (!value)
9004
+ throw new Error(`Claude settings witness ${reason}`);
9005
+ }
9006
+ function absolutePath(value) {
9007
+ need(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute2(value) && resolve2(value) === value, "requires a normalized absolute path");
9008
+ }
9009
+ var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
9010
+ function parents(path) {
9011
+ const result = [];
9012
+ for (let at = dirname3(path);; at = dirname3(at)) {
9013
+ const stat = lstatSync3(at, { bigint: true });
9014
+ need(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
9015
+ result.push([at, stat]);
9016
+ if (at === dirname3(at))
9017
+ return result;
9018
+ }
9019
+ }
9020
+ function readNativeSettingsWitnessFile(path, budget, filename = "settings.json") {
9021
+ absolutePath(path);
9022
+ need(basename2(path) === filename, `requires ${filename}`);
9023
+ need(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
9024
+ const ancestors = parents(path), initial = lstatSync3(path, { bigint: true });
9025
+ need(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
9026
+ need(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
9027
+ const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
9028
+ try {
9029
+ const opened = fstatSync(fd, { bigint: true });
9030
+ need(opened.isFile() && sameFile(initial, opened), "changed during open");
9031
+ const bytes = Buffer.alloc(Number(opened.size) + 1);
9032
+ let length = 0;
9033
+ while (length < bytes.length) {
9034
+ const count = readSync(fd, bytes, length, bytes.length - length, null);
9035
+ if (!count)
9036
+ break;
9037
+ length += count;
9038
+ }
9039
+ const after = fstatSync(fd, { bigint: true }), current = lstatSync3(path, { bigint: true });
9040
+ need(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
9041
+ for (const [ancestor, before] of ancestors) {
9042
+ const now = lstatSync3(ancestor, { bigint: true });
9043
+ need(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
9044
+ }
9045
+ budget.remaining -= length;
9046
+ try {
9047
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
9048
+ } catch {
9049
+ throw new Error("Claude settings witness is not strict UTF-8");
9050
+ }
9051
+ } finally {
9052
+ closeSync(fd);
9053
+ }
9054
+ }
9055
+ function parse(text) {
9056
+ let at = 0, nodes = 0;
9057
+ const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
9058
+ const space = () => {
9059
+ while (at < text.length && /[\x20\t\r\n]/.test(text[at]))
9060
+ at++;
9061
+ };
9062
+ function string() {
9063
+ need(text[at] === '"', "contains invalid JSON");
9064
+ const start = at++;
9065
+ while (at < text.length) {
9066
+ const character = text[at++];
9067
+ if (character === '"') {
9068
+ let value2;
9069
+ try {
9070
+ value2 = JSON.parse(text.slice(start, at));
9071
+ } catch {
9072
+ throw new Error("Claude settings witness contains invalid JSON string");
9073
+ }
9074
+ need(value2.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.stringCharacters, "exceeds its string limit");
9075
+ return value2;
9076
+ }
9077
+ if (character === "\\")
9078
+ at++;
9079
+ }
9080
+ throw new Error("Claude settings witness contains an unterminated JSON string");
9081
+ }
9082
+ function value(depth) {
9083
+ need(depth <= CLAUDE_SETTINGS_WITNESS_LIMITS.depth && ++nodes <= CLAUDE_SETTINGS_WITNESS_LIMITS.nodes, "exceeds its nesting or node limit");
9084
+ space();
9085
+ const character = text[at];
9086
+ if (character === '"')
9087
+ return { kind: "string", value: string() };
9088
+ if (character === "{" || character === "[") {
9089
+ at++;
9090
+ space();
9091
+ const object = character === "{", end = object ? "}" : "]", entries = [], items = [], keys = new Set;
9092
+ if (text[at] !== end)
9093
+ while (true) {
9094
+ if (object) {
9095
+ space();
9096
+ const key = string();
9097
+ need(!keys.has(key), "contains a duplicate JSON key");
9098
+ keys.add(key);
9099
+ space();
9100
+ need(text[at++] === ":", "contains invalid JSON");
9101
+ entries.push([key, value(depth + 1)]);
9102
+ } else
9103
+ items.push(value(depth + 1));
9104
+ space();
9105
+ if (text[at] === end)
9106
+ break;
9107
+ need(text[at++] === ",", "contains invalid JSON");
9108
+ }
9109
+ at++;
9110
+ return object ? { kind: "object", entries } : { kind: "array", items };
9111
+ }
9112
+ for (const literal of ["true", "false", "null"])
9113
+ if (text.startsWith(literal, at)) {
9114
+ at += literal.length;
9115
+ return { kind: "literal", value: literal };
9116
+ }
9117
+ numberPattern.lastIndex = at;
9118
+ const number = numberPattern.exec(text);
9119
+ need(number, "contains invalid JSON");
9120
+ at += number[0].length;
9121
+ return { kind: "number", value: number[0] };
9122
+ }
9123
+ const root = value(0);
9124
+ space();
9125
+ need(at === text.length && root.kind === "object", "requires a JSON object without trailing content");
9126
+ need(root.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
9127
+ return root;
9128
+ }
9129
+ var BOOLEAN_PREFERENCES = new Set([
9130
+ "autoScrollEnabled",
9131
+ "axScreenReader",
9132
+ "emojiCompletionEnabled",
9133
+ "prefersReducedMotion",
9134
+ "showTurnDuration",
9135
+ "spinnerTipsEnabled",
9136
+ "syntaxHighlightingDisabled",
9137
+ "terminalProgressBarEnabled",
9138
+ "terminalTitleFromRename",
9139
+ "verbose",
9140
+ "wheelScrollAccelerationEnabled"
9141
+ ]);
9142
+ var ENUM_PREFERENCES = Object.freeze({
9143
+ editorMode: ["normal", "vim"],
9144
+ tui: ["default", "fullscreen"],
9145
+ viewMode: ["default", "verbose", "focus"]
9146
+ });
9147
+ var BUILTIN_MODEL_SELECTIONS = new Set([
9148
+ "default",
9149
+ "best",
9150
+ "fable",
9151
+ "fable[1m]",
9152
+ "sonnet",
9153
+ "sonnet[1m]",
9154
+ "opus",
9155
+ "opus[1m]",
9156
+ "haiku",
9157
+ "opusplan",
9158
+ "claude-fable-5-1",
9159
+ "claude-fable-5",
9160
+ "claude-fable-5[1m]",
9161
+ "claude-opus-5",
9162
+ "claude-sonnet-5",
9163
+ "claude-haiku-4-5-20251001",
9164
+ "claude-opus-4-6",
9165
+ "claude-sonnet-4-5",
9166
+ "claude-sonnet-4-5-20250929",
9167
+ "claude-opus-4-8",
9168
+ "claude-opus-4-8[1m]",
9169
+ "claude-opus-4-7",
9170
+ "claude-sonnet-4-6",
9171
+ "claude-opus-4-5-20251101",
9172
+ "claude-opus-4-5",
9173
+ "claude-haiku-4-5",
9174
+ "claude-fable-5-1[1m]",
9175
+ "claude-opus-5[1m]",
9176
+ "claude-opus-4-7[1m]",
9177
+ "claude-opus-4-6[1m]",
9178
+ "claude-sonnet-4-6[1m]"
9179
+ ]);
9180
+ function canonical(value) {
9181
+ if (value.kind === "object")
9182
+ return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
9183
+ if (value.kind === "array")
9184
+ return { kind: "array", items: value.items.map(canonical) };
9185
+ return value;
9186
+ }
9187
+ var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
9188
+ function assertClaudeHookEventsReplacement(before, replacement, events) {
9189
+ need(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");
9190
+ const label = events.join(",");
9191
+ function bounded(text) {
9192
+ need(typeof text === "string" && Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
9193
+ return parse(text);
9194
+ }
9195
+ const original = bounded(before ?? "{}"), next = bounded(replacement);
9196
+ const hadHooks = original.entries.some(([key]) => key === "hooks");
9197
+ function withoutOwnedEvents(value) {
9198
+ const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
9199
+ if (hooks !== undefined) {
9200
+ need(hooks.kind === "object", `${label} update requires an object of hooks`);
9201
+ for (const event of events) {
9202
+ const selected = hooks.entries.find(([key]) => key === event)?.[1];
9203
+ need(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
9204
+ }
9205
+ hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
9206
+ if (!hadHooks && hooks.entries.length === 0)
9207
+ value.entries = value.entries.filter(([key]) => key !== "hooks");
9208
+ }
9209
+ return canonical(value);
9210
+ }
9211
+ need(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
9212
+ }
9213
+ var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
9214
+ function inferencePreference(key, child, version) {
9215
+ if (key === "skipDangerousModePermissionPrompt") {
9216
+ need(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
9217
+ return false;
9218
+ }
9219
+ if (key === "effortLevel") {
9220
+ need(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
9221
+ return true;
9222
+ }
9223
+ if (key !== "modelSettings")
9224
+ return false;
9225
+ need(child.kind === "object", "requires an object of model preferences");
9226
+ child.entries = child.entries.filter(([model, preferences]) => {
9227
+ if (version === 2 && !BUILTIN_MODEL_SELECTIONS.has(model) || preferences.kind !== "object" || preferences.entries.length !== 1 || preferences.entries[0][0] !== "effortLevel")
9228
+ return true;
9229
+ const effort = preferences.entries[0][1];
9230
+ need(effort.kind === "string" && PERSISTED_EFFORT_LEVELS.has(effort.value), "contains an invalid persisted model effort level");
9231
+ return false;
9232
+ });
9233
+ return child.entries.length === 0;
9234
+ }
9235
+ function settingsDigest(text, version = 1) {
9236
+ const value = parse(text);
9237
+ value.entries = value.entries.filter(([key, child]) => {
9238
+ if (version !== 1 && inferencePreference(key, child, version))
9239
+ return false;
9240
+ if (key === "model") {
9241
+ need(child.kind === "string", "contains an invalid model selection");
9242
+ return !BUILTIN_MODEL_SELECTIONS.has(child.value);
9243
+ }
9244
+ if (BOOLEAN_PREFERENCES.has(key)) {
9245
+ need(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid display preference");
9246
+ return false;
9247
+ }
9248
+ if (Object.hasOwn(ENUM_PREFERENCES, key)) {
9249
+ need(child.kind === "string" && ENUM_PREFERENCES[key].includes(child.value), "contains an invalid display preference");
9250
+ return false;
9251
+ }
9252
+ return true;
9253
+ });
9254
+ return createHash2("sha256").update(`hasna.skills.claude-settings.v${version}\x00`).update(JSON.stringify(canonical(value))).digest("hex");
9255
+ }
9256
+ function hashClaudeSettingsReplacement(text, budget) {
9257
+ need(typeof text === "string", "requires settings text");
9258
+ const bytes = Buffer.byteLength(text);
9259
+ need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
9260
+ budget.remaining -= bytes;
9261
+ return settingsDigest(text);
9262
+ }
9263
+ function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
9264
+ return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget)) };
9265
+ }
9266
+ function captureClaudeSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
9267
+ return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 2) };
9268
+ }
9269
+ function hashClaudeSettingsReplacementV2(text, budget) {
9270
+ need(typeof text === "string", "requires settings text");
9271
+ const bytes = Buffer.byteLength(text);
9272
+ need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
9273
+ budget.remaining -= bytes;
9274
+ return settingsDigest(text, 2);
9275
+ }
9276
+ function captureClaudeSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
9277
+ return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 3) };
9278
+ }
9279
+ function hashClaudeSettingsReplacementV3(text, budget) {
9280
+ need(typeof text === "string", "requires settings text");
9281
+ const bytes = Buffer.byteLength(text);
9282
+ need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
9283
+ budget.remaining -= bytes;
9284
+ return settingsDigest(text, 3);
9285
+ }
9286
+ function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
9287
+ need(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact legacy settings witness");
9288
+ const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
9289
+ const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
9290
+ const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash2("sha256").update(before).digest("hex") : settingsDigest(before, previous.hashMode === "claude-settings-v1" ? 1 : 2);
9291
+ need(beforeDigest === previous.sha256, "reviewed preimage does not match the legacy witness");
9292
+ const sha256 = settingsDigest(current, 3);
9293
+ need(settingsDigest(before, 3) === sha256, "non-preference settings changed; explicit discovery review required");
9294
+ return { path: previous.path, hashMode: "claude-settings-v3", sha256 };
9295
+ }
9296
+ function hashNativeJsonControls(text, excludedMetadata) {
9297
+ need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
9298
+ const value = parse(text);
9299
+ value.entries = value.entries.filter(([key, child]) => {
9300
+ if (key !== excludedMetadata)
9301
+ return true;
9302
+ need(child.kind === "string" && child.value.length <= 128, "has invalid version metadata");
9303
+ return false;
9304
+ });
9305
+ return createHash2("sha256").update("hasna.skills.native-json-controls.v1\x00").update(JSON.stringify(canonical(value))).digest("hex");
9306
+ }
9307
+ function hashSumiNativeJsonControls(text) {
9308
+ need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
9309
+ const value = parse(text);
9310
+ value.entries = value.entries.filter(([key, child]) => {
9311
+ if (key === "$schema" || key === "username") {
9312
+ need(child.kind === "string", "has invalid Sumi display metadata");
9313
+ return false;
9314
+ }
9315
+ if (key !== "experimental")
9316
+ return true;
9317
+ need(child.kind === "object", "requires Sumi experimental controls to be an object");
9318
+ child.entries = child.entries.filter(([name, option]) => {
9319
+ if (name !== "statusline" && name !== "compact_tools")
9320
+ return true;
9321
+ need(option.kind === "literal" && (option.value === "true" || option.value === "false"), "has invalid Sumi display preference");
9322
+ return false;
9323
+ });
9324
+ return child.entries.length > 0;
9325
+ });
9326
+ return createHash2("sha256").update("hasna.skills.sumi-settings.v1\x00").update(JSON.stringify(value)).digest("hex");
9327
+ }
9328
+
9329
+ // src/lib/sumi-settings-witness.ts
9330
+ var SUMI_DISCOVERY_PROJECTION_FIELDS = ["skills", "plugin", "plugins", "permissions"];
9331
+ var budget = () => ({ remaining: 256 * 1024 * 1024 });
9332
+ function readSumiSettingsPreimage(path) {
9333
+ return readNativeSettingsWitnessFile(path, budget(), "sumi.json");
9334
+ }
9335
+ function hashSumiSettingsReplacement(text, limits) {
9336
+ const bytes = Buffer.byteLength(text);
9337
+ if (bytes > CLAUDE_SETTINGS_WITNESS_LIMITS.bytes || !Number.isSafeInteger(limits.remaining) || limits.remaining < bytes)
9338
+ throw new Error("Sumi settings witness exceeds its byte limit");
9339
+ limits.remaining -= bytes;
9340
+ return hashSumiNativeJsonControls(text);
9341
+ }
9342
+ function captureSumiSettings(path, limits = budget()) {
9343
+ return { path, hashMode: "sumi-settings-v1", sha256: hashSumiNativeJsonControls(readNativeSettingsWitnessFile(path, limits, "sumi.json")) };
9344
+ }
9345
+ function upgradeSumiSettingsWitness(previous, reviewedSettingsPath) {
9346
+ if (!previous || Object.keys(previous).some((key) => !["path", "hashMode", "sha256"].includes(key)) || ![undefined, "bytes"].includes(previous.hashMode) || !/^[a-f0-9]{64}$/.test(previous.sha256))
9347
+ throw new Error("Sumi settings upgrade requires one exact preserved byte witness");
9348
+ const before = readSumiSettingsPreimage(reviewedSettingsPath);
9349
+ if (createHash3("sha256").update(before).digest("hex") !== previous.sha256)
9350
+ throw new Error("Reviewed Sumi preimage does not match its byte witness");
9351
+ const next = captureSumiSettings(previous.path);
9352
+ if (hashSumiNativeJsonControls(before) !== next.sha256)
9353
+ throw new Error("Sumi non-display settings changed; explicit discovery review required");
9354
+ return next;
9355
+ }
9356
+
8994
9357
  // src/lib/agent-policy-limits.ts
8995
- import { basename as basename2, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
9358
+ import { basename as basename3, isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
8996
9359
  var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
8997
9360
  function object(value) {
8998
9361
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -9046,7 +9409,7 @@ function assertAgentPolicyCollections(policy) {
9046
9409
  text(control.pluginId, 1024);
9047
9410
  text(control.namespace, 64);
9048
9411
  text(control.pluginParent);
9049
- requireBound(isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
9412
+ requireBound(isAbsolute3(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
9050
9413
  if (control.remotePluginId !== undefined)
9051
9414
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
9052
9415
  if (control.skillsOnly !== undefined)
@@ -9064,7 +9427,7 @@ function assertAgentPolicyCollections(policy) {
9064
9427
  text(control.pluginId, 1024);
9065
9428
  text(control.namespace, 64);
9066
9429
  text(control.pluginParent);
9067
- requireBound(!ids.has(control.pluginId) && isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
9430
+ requireBound(!ids.has(control.pluginId) && isAbsolute3(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
9068
9431
  ids.add(control.pluginId);
9069
9432
  }
9070
9433
  }
@@ -9089,15 +9452,15 @@ function assertAgentPolicyCollections(policy) {
9089
9452
  requireBound(object(value));
9090
9453
  text(value.agent, 128);
9091
9454
  if (value.codexRetiredMaterializations !== undefined) {
9092
- const proof = record(value.codexRetiredMaterializations, 3), roots = array(proof.roots, AGENT_POLICY_LIMITS.discoveryRoots), parents = proof.parents === undefined ? [] : array(proof.parents, AGENT_POLICY_LIMITS.discoveryRoots);
9093
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents.length > 0 && new Set(roots).size === roots.length && new Set(parents).size === parents.length);
9455
+ const proof = record(value.codexRetiredMaterializations, 3), roots = array(proof.roots, AGENT_POLICY_LIMITS.discoveryRoots), parents2 = proof.parents === undefined ? [] : array(proof.parents, AGENT_POLICY_LIMITS.discoveryRoots);
9456
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents2.length > 0 && new Set(roots).size === roots.length && new Set(parents2).size === parents2.length);
9094
9457
  for (const root of roots) {
9095
9458
  text(root);
9096
- requireBound(isAbsolute2(root) && resolve2(root) === root);
9459
+ requireBound(isAbsolute3(root) && resolve3(root) === root);
9097
9460
  }
9098
- for (const parent of parents) {
9461
+ for (const parent of parents2) {
9099
9462
  text(parent);
9100
- requireBound(isAbsolute2(parent) && resolve2(parent) === parent);
9463
+ requireBound(isAbsolute3(parent) && resolve3(parent) === parent);
9101
9464
  }
9102
9465
  for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
9103
9466
  requireBound(object(directory));
@@ -9117,7 +9480,7 @@ function assertAgentPolicyCollections(policy) {
9117
9480
  text(input.namespace, 64);
9118
9481
  text(input.pluginParent);
9119
9482
  text(input.sourceRoot);
9120
- requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute2(path) && resolve2(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
9483
+ requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute3(path) && resolve3(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
9121
9484
  ids.add(input.pluginId);
9122
9485
  }
9123
9486
  if (proof.directories !== undefined)
@@ -9134,13 +9497,15 @@ function assertAgentPolicyCollections(policy) {
9134
9497
  text(source.path);
9135
9498
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
9136
9499
  if (source.hashMode !== undefined)
9137
- requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9500
+ requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9501
+ if (source.hashMode === "sumi-settings-v1")
9502
+ requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "sumi.json");
9138
9503
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
9139
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "config.toml");
9504
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "config.toml");
9140
9505
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
9141
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "settings.json");
9506
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "settings.json");
9142
9507
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
9143
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "known_marketplaces.json");
9508
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
9144
9509
  if (source.hashMode === "claude-plugin-registry") {
9145
9510
  requireBound(agent === "claude");
9146
9511
  const managed = array(source.managedPlugins, 64);
@@ -9171,7 +9536,7 @@ function assertAgentPolicyCollections(policy) {
9171
9536
  }
9172
9537
 
9173
9538
  // src/lib/agent-discovery.ts
9174
- import { createHash as createHash21 } from "crypto";
9539
+ import { createHash as createHash22 } from "crypto";
9175
9540
  import { lstatSync as lstatSync24, readFileSync as readFileSync19, readdirSync as readdirSync10, realpathSync as realpathSync10 } from "fs";
9176
9541
  import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute18, join as join31, resolve as resolve18, sep as sep7 } from "path";
9177
9542
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
@@ -9223,8 +9588,8 @@ var $visit = visit.visit;
9223
9588
  var $visitAsync = visit.visitAsync;
9224
9589
 
9225
9590
  // src/lib/agent-hermes.ts
9226
- import { dirname as dirname3, isAbsolute as isAbsolute3, join as join7, resolve as resolve3 } from "path";
9227
- import { lstatSync as lstatSync3, readFileSync as readFileSync6, opendirSync } from "fs";
9591
+ import { dirname as dirname4, isAbsolute as isAbsolute4, join as join7, resolve as resolve4 } from "path";
9592
+ import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
9228
9593
  var HERMES_OPT_OUT = ".no-bundled-skills";
9229
9594
  var EVENTS = ["pre_llm_call", "pre_tool_call"];
9230
9595
  function object2(value) {
@@ -9261,7 +9626,7 @@ function quoted(value) {
9261
9626
  return `'${value.replace(/'/g, `'\\''`)}'`;
9262
9627
  }
9263
9628
  function hermesHookDefinitions(supervisor) {
9264
- if (!isAbsolute3(supervisor.runtime) || !isAbsolute3(supervisor.path))
9629
+ if (!isAbsolute4(supervisor.runtime) || !isAbsolute4(supervisor.path))
9265
9630
  throw new Error("Expected absolute Hermes supervisor paths");
9266
9631
  const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
9267
9632
  return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
@@ -9297,19 +9662,19 @@ function assertHermesEnvironment(home) {
9297
9662
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
9298
9663
  }
9299
9664
  const selected = process.env.HERMES_HOME?.trim();
9300
- if (selected && (!isAbsolute3(selected) || resolve3(selected) !== join7(resolve3(home), ".hermes")))
9665
+ if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join7(resolve4(home), ".hermes")))
9301
9666
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
9302
9667
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
9303
9668
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
9304
9669
  }
9305
9670
  function read(path) {
9306
- for (let current = path;; current = dirname3(current)) {
9307
- if (lstatSync3(current, { throwIfNoEntry: false })?.isSymbolicLink())
9671
+ for (let current = path;; current = dirname4(current)) {
9672
+ if (lstatSync4(current, { throwIfNoEntry: false })?.isSymbolicLink())
9308
9673
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
9309
- if (dirname3(current) === current)
9674
+ if (dirname4(current) === current)
9310
9675
  break;
9311
9676
  }
9312
- const stat = lstatSync3(path, { throwIfNoEntry: false });
9677
+ const stat = lstatSync4(path, { throwIfNoEntry: false });
9313
9678
  if (!stat)
9314
9679
  return null;
9315
9680
  if (!stat.isFile() || stat.size > 1024 * 1024)
@@ -9320,7 +9685,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
9320
9685
  assertHermesEnvironment(home);
9321
9686
  if (read(join7(home, ".hermes", HERMES_OPT_OUT)) === null)
9322
9687
  throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
9323
- if (!supervisor || !isAbsolute3(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
9688
+ if (!supervisor || !isAbsolute4(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
9324
9689
  throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
9325
9690
  const raw = read(join7(home, ".hermes/shell-hooks-allowlist.json"));
9326
9691
  let trust;
@@ -9346,16 +9711,16 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
9346
9711
  }
9347
9712
  function assertNoHermesLegacyShadow(root) {
9348
9713
  let entries = 0;
9349
- for (let cursor = resolve3(root);; cursor = dirname3(cursor)) {
9350
- if (lstatSync3(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9714
+ for (let cursor = resolve4(root);; cursor = dirname4(cursor)) {
9715
+ if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9351
9716
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
9352
- if (dirname3(cursor) === cursor)
9717
+ if (dirname4(cursor) === cursor)
9353
9718
  break;
9354
9719
  }
9355
9720
  function scan(path, depth) {
9356
9721
  if (++entries > 1e4 || depth > 64)
9357
9722
  throw new Error("NATIVE_SKILL_DRIFT: Hermes legacy discovery limit exceeded; review the root before continuing");
9358
- const stat = lstatSync3(path, { throwIfNoEntry: false });
9723
+ const stat = lstatSync4(path, { throwIfNoEntry: false });
9359
9724
  if (!stat)
9360
9725
  return;
9361
9726
  if (stat.isSymbolicLink())
@@ -9365,9 +9730,9 @@ function assertNoHermesLegacyShadow(root) {
9365
9730
  throw new Error("NATIVE_SKILL_DRIFT: unsupported Hermes legacy discovery input");
9366
9731
  return;
9367
9732
  }
9368
- if (lstatSync3(join7(path, "skills-cli.md"), { throwIfNoEntry: false }))
9733
+ if (lstatSync4(join7(path, "skills-cli.md"), { throwIfNoEntry: false }))
9369
9734
  throw new Error("NATIVE_SKILL_DRIFT: legacy Hermes skills-cli.md can shadow the owned bridge; preserve and retire it before continuing");
9370
- const hasDescriptor = Boolean(lstatSync3(join7(path, "SKILL.md"), { throwIfNoEntry: false }));
9735
+ const hasDescriptor = Boolean(lstatSync4(join7(path, "SKILL.md"), { throwIfNoEntry: false }));
9371
9736
  const directory = opendirSync(path);
9372
9737
  try {
9373
9738
  for (let child = directory.readSync();child; child = directory.readSync()) {
@@ -9430,193 +9795,193 @@ if (failed) process.exitCode = 2;
9430
9795
  }
9431
9796
 
9432
9797
  // src/lib/agent-discovery-directories.ts
9433
- import { createHash as createHash2 } from "crypto";
9434
- import { lstatSync as lstatSync4, opendirSync as opendirSync2 } from "fs";
9435
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "path";
9436
- function need(value, message) {
9798
+ import { createHash as createHash4 } from "crypto";
9799
+ import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
9800
+ import { dirname as dirname5, isAbsolute as isAbsolute5, join as join8, resolve as resolve5 } from "path";
9801
+ function need2(value, message) {
9437
9802
  if (!value)
9438
9803
  throw new Error(message);
9439
9804
  }
9440
9805
  function safe(path) {
9441
- need(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute4(path) && resolve4(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
9442
- for (let cursor = path;; cursor = dirname4(cursor)) {
9443
- need(!lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
9444
- if (dirname4(cursor) === cursor)
9806
+ need2(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute5(path) && resolve5(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
9807
+ for (let cursor = path;; cursor = dirname5(cursor)) {
9808
+ need2(!lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
9809
+ if (dirname5(cursor) === cursor)
9445
9810
  break;
9446
9811
  }
9447
9812
  }
9448
9813
  function same(before, after) {
9449
9814
  return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
9450
9815
  }
9451
- function capture(path, budget, excludedRoots, omittedRoots = [], rows) {
9816
+ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
9452
9817
  safe(path);
9453
9818
  if (omittedRoots.some((root2) => path === root2 || path.startsWith(root2 + "/")))
9454
9819
  return { path, sha256: null };
9455
- const root = lstatSync4(path, { throwIfNoEntry: false });
9820
+ const root = lstatSync5(path, { throwIfNoEntry: false });
9456
9821
  if (!root)
9457
9822
  return { path, sha256: null };
9458
- need(root.isDirectory(), "Unsupported native discovery directory input");
9459
- const hash = createHash2("sha256");
9823
+ need2(root.isDirectory(), "Unsupported native discovery directory input");
9824
+ const hash = createHash4("sha256");
9460
9825
  function walk(current, relative2, before, depth) {
9461
- need(depth <= 64, "Native discovery directory depth limit exceeded");
9826
+ need2(depth <= 64, "Native discovery directory depth limit exceeded");
9462
9827
  const names = [], directory = opendirSync2(current);
9463
9828
  try {
9464
9829
  for (let entry = directory.readSync();entry; entry = directory.readSync()) {
9465
- need(++budget.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
9830
+ need2(++budget2.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
9466
9831
  names.push(entry.name);
9467
9832
  }
9468
9833
  } finally {
9469
9834
  directory.closeSync();
9470
9835
  }
9471
- need(new Set(names).size === names.length, "Ambiguous native discovery directory names");
9836
+ need2(new Set(names).size === names.length, "Ambiguous native discovery directory names");
9472
9837
  for (const name of names.sort()) {
9473
9838
  const child = join8(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
9474
9839
  if (omittedRoots.includes(child))
9475
9840
  continue;
9476
- need(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
9477
- const stat = lstatSync4(child);
9478
- need(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
9841
+ need2(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
9842
+ const stat = lstatSync5(child);
9843
+ need2(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
9479
9844
  const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
9480
9845
  `;
9481
- budget.bytes += Buffer.byteLength(row);
9482
- need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
9846
+ budget2.bytes += Buffer.byteLength(row);
9847
+ need2(budget2.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
9483
9848
  hash.update(row);
9484
9849
  rows?.push(row);
9485
9850
  if (stat.isDirectory() && !excludedRoots.includes(child))
9486
9851
  walk(child, childRelative, stat, depth + 1);
9487
9852
  }
9488
- need(same(before, lstatSync4(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
9853
+ need2(same(before, lstatSync5(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
9489
9854
  }
9490
9855
  walk(path, "", root, 0);
9491
9856
  safe(path);
9492
9857
  return { path, sha256: hash.digest("hex") };
9493
9858
  }
9494
9859
  function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
9495
- need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
9496
- need(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
9860
+ need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
9861
+ need2(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
9497
9862
  for (const root of omittedRoots)
9498
9863
  safe(root);
9499
- const budget = { entries: 0, bytes: 0 };
9864
+ const budget2 = { entries: 0, bytes: 0 };
9500
9865
  return paths.map((path) => {
9501
9866
  const entries = [];
9502
- return { ...capture(path, budget, [], omittedRoots, entries), entries };
9867
+ return { ...capture(path, budget2, [], omittedRoots, entries), entries };
9503
9868
  });
9504
9869
  }
9505
9870
  function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
9506
- need(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
9871
+ need2(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
9507
9872
  let count = 0, bytes = 0;
9508
9873
  const expected = directories.map((directory) => {
9509
9874
  safe(directory.path);
9510
- need(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
9511
- const hash = createHash2("sha256");
9875
+ need2(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
9876
+ const hash = createHash4("sha256");
9512
9877
  const kept = [];
9513
9878
  for (const row of directory.entries) {
9514
- need(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
9879
+ need2(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
9515
9880
  bytes += Buffer.byteLength(row);
9516
- need(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
9881
+ need2(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
9517
9882
  const value = JSON.parse(row);
9518
- need(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute4(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
9883
+ need2(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
9519
9884
  `, "Invalid reviewed directory member");
9520
9885
  hash.update(row);
9521
9886
  const path = join8(directory.path, value[0]);
9522
9887
  if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
9523
9888
  kept.push(row);
9524
9889
  }
9525
- need(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
9890
+ need2(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
9526
9891
  const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
9527
- return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash2("sha256").update(kept.join("")).digest("hex") };
9892
+ return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash4("sha256").update(kept.join("")).digest("hex") };
9528
9893
  });
9529
9894
  const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
9530
- need(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
9895
+ need2(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
9531
9896
  }
9532
9897
  function captureDiscoveryDirectories(paths, excludedRoots = []) {
9533
- need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
9534
- const budget = { entries: 0, bytes: 0 };
9898
+ need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
9899
+ const budget2 = { entries: 0, bytes: 0 };
9535
9900
  for (const root of excludedRoots)
9536
9901
  safe(root);
9537
- return paths.map((path) => capture(path, budget, excludedRoots));
9902
+ return paths.map((path) => capture(path, budget2, excludedRoots));
9538
9903
  }
9539
9904
  function verifyDiscoveryDirectories(directories, excludedRoots = []) {
9540
- need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
9905
+ need2(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
9541
9906
  for (const directory of directories)
9542
- need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
9907
+ need2(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
9543
9908
  const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
9544
9909
  for (let i = 0;i < directories.length; i++)
9545
- need(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
9910
+ need2(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
9546
9911
  }
9547
9912
 
9548
9913
  // src/lib/agent-discovery-bytes.ts
9549
- import { createHash as createHash3 } from "crypto";
9550
- import { closeSync, constants, fstatSync, lstatSync as lstatSync5, openSync, readSync } from "fs";
9551
- import { dirname as dirname5, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
9914
+ import { createHash as createHash5 } from "crypto";
9915
+ import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync6, openSync as openSync2, readSync as readSync2 } from "fs";
9916
+ import { dirname as dirname6, isAbsolute as isAbsolute6, resolve as resolve6 } from "path";
9552
9917
  function discoveryByteBudget() {
9553
9918
  return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
9554
9919
  }
9555
9920
  function safe2(path) {
9556
- if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve5(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
9921
+ if (typeof path !== "string" || !isAbsolute6(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
9557
9922
  throw new Error("Expected a canonical absolute raw discovery path");
9558
- for (let cursor = path;; cursor = dirname5(cursor)) {
9559
- if (lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9923
+ for (let cursor = path;; cursor = dirname6(cursor)) {
9924
+ if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9560
9925
  throw new Error("Refusing symlink raw discovery input");
9561
- if (dirname5(cursor) === cursor)
9926
+ if (dirname6(cursor) === cursor)
9562
9927
  break;
9563
9928
  }
9564
9929
  }
9565
9930
  function same2(left, right) {
9566
9931
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
9567
9932
  }
9568
- function consume(budget, bytes) {
9569
- if (bytes > budget.remaining)
9933
+ function consume(budget2, bytes) {
9934
+ if (bytes > budget2.remaining)
9570
9935
  throw new Error("Raw discovery aggregate byte limit exceeded");
9571
- budget.remaining -= bytes;
9936
+ budget2.remaining -= bytes;
9572
9937
  }
9573
- function hashRawDiscoveryFile(path, budget, changes) {
9938
+ function hashRawDiscoveryFile(path, budget2, changes) {
9574
9939
  safe2(path);
9575
9940
  if (changes?.has(path)) {
9576
9941
  const text2 = changes.get(path), bytes = Buffer.byteLength(text2);
9577
9942
  if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
9578
9943
  throw new Error("Raw discovery file byte limit exceeded");
9579
- consume(budget, bytes);
9580
- return createHash3("sha256").update(text2).digest("hex");
9944
+ consume(budget2, bytes);
9945
+ return createHash5("sha256").update(text2).digest("hex");
9581
9946
  }
9582
- const initial = lstatSync5(path, { throwIfNoEntry: false });
9947
+ const initial = lstatSync6(path, { throwIfNoEntry: false });
9583
9948
  if (!initial)
9584
9949
  return null;
9585
9950
  if (!initial.isFile() || initial.size > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
9586
9951
  throw new Error("Unsupported or oversized raw discovery input");
9587
- if (initial.size > budget.remaining)
9952
+ if (initial.size > budget2.remaining)
9588
9953
  throw new Error("Raw discovery aggregate byte limit exceeded");
9589
- const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
9954
+ const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
9590
9955
  try {
9591
- const opened = fstatSync(fd);
9956
+ const opened = fstatSync2(fd);
9592
9957
  if (!opened.isFile() || !same2(initial, opened))
9593
9958
  throw new Error("Raw discovery input changed during open");
9594
- const hash = createHash3("sha256"), buffer = Buffer.alloc(64 * 1024);
9959
+ const hash = createHash5("sha256"), buffer = Buffer.alloc(64 * 1024);
9595
9960
  let bytes = 0;
9596
9961
  while (true) {
9597
- const count = readSync(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
9962
+ const count = readSync2(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
9598
9963
  if (!count)
9599
9964
  break;
9600
9965
  bytes += count;
9601
9966
  if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
9602
9967
  throw new Error("Raw discovery file byte limit exceeded");
9603
- consume(budget, count);
9968
+ consume(budget2, count);
9604
9969
  hash.update(buffer.subarray(0, count));
9605
9970
  }
9606
- const final = fstatSync(fd), current = lstatSync5(path, { throwIfNoEntry: false });
9971
+ const final = fstatSync2(fd), current = lstatSync6(path, { throwIfNoEntry: false });
9607
9972
  safe2(path);
9608
9973
  if (!current?.isFile() || !same2(opened, final) || !same2(final, current) || bytes !== final.size)
9609
9974
  throw new Error("Raw discovery input changed during read");
9610
9975
  return hash.digest("hex");
9611
9976
  } finally {
9612
- closeSync(fd);
9977
+ closeSync2(fd);
9613
9978
  }
9614
9979
  }
9615
9980
 
9616
9981
  // src/lib/agent-discovery-path-bytes.ts
9617
- import { createHash as createHash4 } from "crypto";
9618
- import { lstatSync as lstatSync6, readlinkSync } from "fs";
9619
- import { dirname as dirname6, isAbsolute as isAbsolute6, join as join9, parse, resolve as resolve6, sep as sep2 } from "path";
9982
+ import { createHash as createHash6 } from "crypto";
9983
+ import { lstatSync as lstatSync7, readlinkSync } from "fs";
9984
+ import { dirname as dirname7, isAbsolute as isAbsolute7, join as join9, parse as parse2, resolve as resolve7, sep as sep2 } from "path";
9620
9985
  function identity2(stat, mutable = false) {
9621
9986
  return {
9622
9987
  dev: String(stat.dev),
@@ -9627,14 +9992,14 @@ function identity2(stat, mutable = false) {
9627
9992
  ...mutable ? { size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs) } : {}
9628
9993
  };
9629
9994
  }
9630
- function canonical(path) {
9631
- if (typeof path !== "string" || !isAbsolute6(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
9995
+ function canonical2(path) {
9996
+ if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
9632
9997
  throw new Error("Expected a canonical absolute discovery path");
9633
9998
  }
9634
9999
  function trace(path) {
9635
- canonical(path);
10000
+ canonical2(path);
9636
10001
  const rows = [], paths = [];
9637
- let metadataBytes = 0, steps = 0, links = 0, cursor = parse(path).root, pending = path.slice(cursor.length).split(sep2);
10002
+ let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep2);
9638
10003
  function record2(path2, row) {
9639
10004
  const value = { path: path2, ...row };
9640
10005
  metadataBytes += Buffer.byteLength(JSON.stringify(value));
@@ -9644,7 +10009,7 @@ function trace(path) {
9644
10009
  paths.push(path2);
9645
10010
  }
9646
10011
  function root() {
9647
- const stat = lstatSync6(cursor, { bigint: true });
10012
+ const stat = lstatSync7(cursor, { bigint: true });
9648
10013
  if (!stat.isDirectory())
9649
10014
  throw new Error("Unsupported discovery path root");
9650
10015
  record2(cursor, { type: "directory", ...identity2(stat) });
@@ -9657,13 +10022,13 @@ function trace(path) {
9657
10022
  if (!component || component === ".")
9658
10023
  continue;
9659
10024
  if (component === "..") {
9660
- cursor = dirname6(cursor);
10025
+ cursor = dirname7(cursor);
9661
10026
  continue;
9662
10027
  }
9663
10028
  const candidate = join9(cursor, component);
9664
10029
  if (candidate.length > AGENT_POLICY_LIMITS.pathCharacters)
9665
10030
  throw new Error("Discovery resolved path length limit exceeded");
9666
- const stat = lstatSync6(candidate, { bigint: true, throwIfNoEntry: false });
10031
+ const stat = lstatSync7(candidate, { bigint: true, throwIfNoEntry: false });
9667
10032
  if (!stat) {
9668
10033
  record2(candidate, { type: "missing", remaining: pending });
9669
10034
  return { rows, paths, file: null, missing: candidate, metadataBytes };
@@ -9671,17 +10036,17 @@ function trace(path) {
9671
10036
  if (stat.isSymbolicLink()) {
9672
10037
  if (++links > AGENT_POLICY_LIMITS.discoveryPathLinks || stat.size > BigInt(AGENT_POLICY_LIMITS.pathCharacters))
9673
10038
  throw new Error("Discovery path link limit exceeded");
9674
- const rawTarget = readlinkSync(candidate, { encoding: "buffer" }), target = rawTarget.toString("utf8"), after = lstatSync6(candidate, { bigint: true });
10039
+ const rawTarget = readlinkSync(candidate, { encoding: "buffer" }), target = rawTarget.toString("utf8"), after = lstatSync7(candidate, { bigint: true });
9675
10040
  if (!rawTarget.equals(Buffer.from(target, "utf8")))
9676
10041
  throw new Error("Unsupported non-UTF8 discovery link target");
9677
10042
  if (!target || target.includes("\x00") || Buffer.byteLength(target) > AGENT_POLICY_LIMITS.pathCharacters || !after.isSymbolicLink() || JSON.stringify(identity2(stat, true)) !== JSON.stringify(identity2(after, true)))
9678
10043
  throw new Error("Discovery link changed during read");
9679
10044
  record2(candidate, { type: "link", target, ...identity2(stat, true) });
9680
- if (isAbsolute6(target)) {
9681
- cursor = parse(target).root;
10045
+ if (isAbsolute7(target)) {
10046
+ cursor = parse2(target).root;
9682
10047
  root();
9683
10048
  }
9684
- const remainder = isAbsolute6(target) ? target.slice(parse(target).root.length) : target;
10049
+ const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
9685
10050
  pending = [...remainder.split(sep2), ...pending];
9686
10051
  if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
9687
10052
  throw new Error("Discovery path step limit exceeded");
@@ -9701,7 +10066,7 @@ function trace(path) {
9701
10066
  }
9702
10067
  throw new Error("Unsupported directory discovery path input");
9703
10068
  }
9704
- function hashDiscoveryPathFile(path, budget, changes) {
10069
+ function hashDiscoveryPathFile(path, budget2, changes) {
9705
10070
  const before = trace(path);
9706
10071
  let createsMissingPath = false;
9707
10072
  if (before.missing !== null && changes) {
@@ -9714,17 +10079,17 @@ function hashDiscoveryPathFile(path, budget, changes) {
9714
10079
  }
9715
10080
  if (createsMissingPath || changes?.has(path) || before.paths.some((path2) => changes?.has(path2)))
9716
10081
  throw new Error("Discovery path witness intersects a planned write; use bytes mode for owned configuration");
9717
- const remaining = budget.pathMetadataRemaining ?? AGENT_POLICY_LIMITS.discoveryPathTotalMetadataBytes;
10082
+ const remaining = budget2.pathMetadataRemaining ?? AGENT_POLICY_LIMITS.discoveryPathTotalMetadataBytes;
9718
10083
  if (before.metadataBytes > remaining)
9719
10084
  throw new Error("Discovery path aggregate metadata limit exceeded");
9720
- budget.pathMetadataRemaining = remaining - before.metadataBytes;
9721
- const content = before.file === null ? null : hashRawDiscoveryFile(before.file, budget);
10085
+ budget2.pathMetadataRemaining = remaining - before.metadataBytes;
10086
+ const content = before.file === null ? null : hashRawDiscoveryFile(before.file, budget2);
9722
10087
  if (before.file !== null && content === null)
9723
10088
  throw new Error("Discovery path input changed during read");
9724
10089
  const after = trace(path);
9725
10090
  if (JSON.stringify(before.rows) !== JSON.stringify(after.rows) || before.file !== after.file)
9726
10091
  throw new Error("Discovery path input changed during read");
9727
- return createHash4("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
10092
+ return createHash6("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
9728
10093
  }
9729
10094
 
9730
10095
  // src/lib/plugin-discovery.ts
@@ -9736,15 +10101,15 @@ import { homedir as homedir5 } from "os";
9736
10101
  import { isAbsolute as isAbsolute15, join as join29, resolve as resolve15 } from "path";
9737
10102
 
9738
10103
  // src/lib/selection-cache.ts
9739
- import { createHash as createHash6, randomUUID } from "crypto";
10104
+ import { createHash as createHash8, randomUUID } from "crypto";
9740
10105
  import { spawnSync } from "child_process";
9741
- import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, fsyncSync, linkSync, lstatSync as lstatSync7, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync2, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
10106
+ import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
9742
10107
  import { hostname } from "os";
9743
- import { dirname as dirname7, join as join11, parse as parse2, resolve as resolve7 } from "path";
10108
+ import { dirname as dirname8, join as join11, parse as parse3, resolve as resolve8 } from "path";
9744
10109
  import { isDeepStrictEqual } from "util";
9745
10110
 
9746
10111
  // src/lib/skill-bundle.ts
9747
- import { createHash as createHash5 } from "crypto";
10112
+ import { createHash as createHash7 } from "crypto";
9748
10113
  import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
9749
10114
  import { join as join10, relative as relative2 } from "path";
9750
10115
  import { createGunzip } from "zlib";
@@ -9899,7 +10264,7 @@ function ownBytes(view) {
9899
10264
  return out;
9900
10265
  }
9901
10266
  function sha256Hex(bytes) {
9902
- return createHash5("sha256").update(bytes).digest("hex");
10267
+ return createHash7("sha256").update(bytes).digest("hex");
9903
10268
  }
9904
10269
  function collectSkillBundleEntries(dir) {
9905
10270
  const entries = [];
@@ -10145,7 +10510,7 @@ async function inspectSkillBundle(bundle, options = {}) {
10145
10510
  parser2.push(chunk);
10146
10511
  bytesSinceYield += chunk.byteLength;
10147
10512
  if (bytesSinceYield >= 256 * 1024) {
10148
- await new Promise((resolve7) => setTimeout(resolve7, 0));
10513
+ await new Promise((resolve8) => setTimeout(resolve8, 0));
10149
10514
  bytesSinceYield = 0;
10150
10515
  check();
10151
10516
  }
@@ -10383,14 +10748,14 @@ class SkillSelectionError extends Error {
10383
10748
  var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
10384
10749
  var SELECTION_LOCK_FILE = "selection.lock.json";
10385
10750
  function selectionCacheRoot(options = {}) {
10386
- return resolve7(options.cacheDir ?? join11(getDataDirReadOnly(), "selection-cache"));
10751
+ return resolve8(options.cacheDir ?? join11(getDataDirReadOnly(), "selection-cache"));
10387
10752
  }
10388
10753
  function selectionKey(selection) {
10389
10754
  validateSelection(selection);
10390
10755
  return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
10391
10756
  }
10392
10757
  function hash(value) {
10393
- return createHash6("sha256").update(JSON.stringify(value)).digest("hex");
10758
+ return createHash8("sha256").update(JSON.stringify(value)).digest("hex");
10394
10759
  }
10395
10760
  function validateSelection(selection) {
10396
10761
  if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
@@ -10430,14 +10795,14 @@ function selectionBundlePath(selection, options = {}) {
10430
10795
  return join11(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
10431
10796
  }
10432
10797
  function assertRegularPath(path, createParents = false) {
10433
- const absolute = resolve7(path);
10434
- const root = parse2(absolute).root;
10798
+ const absolute = resolve8(path);
10799
+ const root = parse3(absolute).root;
10435
10800
  let cursor = root;
10436
10801
  const segments = absolute.slice(root.length).split("/");
10437
10802
  for (let index = 0;index < segments.length; index++) {
10438
10803
  cursor = join11(cursor, segments[index]);
10439
10804
  try {
10440
- const stat = lstatSync7(cursor);
10805
+ const stat = lstatSync8(cursor);
10441
10806
  if (stat.isSymbolicLink() || index < segments.length - 1 && !stat.isDirectory()) {
10442
10807
  throw new SkillSelectionError("UNSAFE_CACHE_PATH", "Skills cache paths must not traverse symlinks or non-directories.");
10443
10808
  }
@@ -10453,20 +10818,20 @@ function readRegularFile(path, limit) {
10453
10818
  assertRegularPath(path);
10454
10819
  let fd;
10455
10820
  try {
10456
- fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
10821
+ fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
10457
10822
  } catch (error) {
10458
10823
  if (error.code === "ENOENT")
10459
10824
  return null;
10460
10825
  throw error;
10461
10826
  }
10462
10827
  try {
10463
- const stat = fstatSync2(fd);
10828
+ const stat = fstatSync3(fd);
10464
10829
  if (!stat.isFile() || stat.size > limit)
10465
10830
  throw new SkillSelectionError("INVALID_CACHE_FILE", "The Skills cache contains an invalid or oversized file.");
10466
10831
  const bytes = Buffer.alloc(stat.size + 1);
10467
10832
  let size = 0;
10468
10833
  while (size < bytes.length) {
10469
- const read2 = readSync2(fd, bytes, size, bytes.length - size, null);
10834
+ const read2 = readSync3(fd, bytes, size, bytes.length - size, null);
10470
10835
  if (!read2)
10471
10836
  break;
10472
10837
  size += read2;
@@ -10475,7 +10840,7 @@ function readRegularFile(path, limit) {
10475
10840
  throw new SkillSelectionError("INVALID_CACHE_FILE", "The Skills cache file grew during its bounded read.");
10476
10841
  return bytes.subarray(0, size);
10477
10842
  } finally {
10478
- closeSync2(fd);
10843
+ closeSync3(fd);
10479
10844
  }
10480
10845
  }
10481
10846
  function writeSelectionJson(path, value) {
@@ -10487,19 +10852,19 @@ function writeSelectionJson(path, value) {
10487
10852
  }
10488
10853
  function atomicWrite(path, bytes, mode, durable = false) {
10489
10854
  assertRegularPath(path, true);
10490
- const temporary = join11(dirname7(path), `.selection-${randomUUID()}.tmp`);
10855
+ const temporary = join11(dirname8(path), `.selection-${randomUUID()}.tmp`);
10491
10856
  try {
10492
- const fd = openSync2(temporary, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, mode);
10857
+ const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
10493
10858
  try {
10494
10859
  writeFileSync3(fd, bytes);
10495
10860
  if (durable)
10496
10861
  fsyncSync(fd);
10497
10862
  } finally {
10498
- closeSync2(fd);
10863
+ closeSync3(fd);
10499
10864
  }
10500
10865
  renameSync(temporary, path);
10501
10866
  if (durable)
10502
- syncDirectory(dirname7(path));
10867
+ syncDirectory(dirname8(path));
10503
10868
  } finally {
10504
10869
  try {
10505
10870
  unlinkSync(temporary);
@@ -10609,7 +10974,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
10609
10974
  }
10610
10975
  }
10611
10976
  function projectSelectionLockPath(projectDir) {
10612
- return join11(resolve7(projectDir), ".skills", SELECTION_LOCK_FILE);
10977
+ return join11(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
10613
10978
  }
10614
10979
  function readProjectSelection(projectDir) {
10615
10980
  const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
@@ -10725,14 +11090,14 @@ function localLockIdentity() {
10725
11090
  }
10726
11091
  function readSessionLock(sessionId, options) {
10727
11092
  const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
10728
- const before = lstatSync7(path, { throwIfNoEntry: false });
11093
+ const before = lstatSync8(path, { throwIfNoEntry: false });
10729
11094
  if (!before)
10730
11095
  return null;
10731
11096
  if (!before.isFile() || ![1, 2].includes(before.nlink) || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid()) {
10732
11097
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
10733
11098
  }
10734
11099
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
10735
- const after = lstatSync7(path, { throwIfNoEntry: false });
11100
+ const after = lstatSync8(path, { throwIfNoEntry: false });
10736
11101
  if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
10737
11102
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
10738
11103
  }
@@ -10749,7 +11114,7 @@ function readSessionLock(sessionId, options) {
10749
11114
  if (after.nlink === 2) {
10750
11115
  stagingPath = `${path}.${marker.operationId}.prepared`;
10751
11116
  assertRegularPath(stagingPath);
10752
- const staged = lstatSync7(stagingPath, { throwIfNoEntry: false });
11117
+ const staged = lstatSync8(stagingPath, { throwIfNoEntry: false });
10753
11118
  if (marker.publication !== "hard-link-v1" || !staged?.isFile() || staged.dev !== after.dev || staged.ino !== after.ino || staged.nlink !== 2 || staged.mode !== after.mode || staged.uid !== after.uid || staged.gid !== after.gid) {
10754
11119
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has an unbound publication link; recovery requires review.");
10755
11120
  }
@@ -10760,33 +11125,33 @@ function publishSessionLock(path, marker) {
10760
11125
  const stagingPath = `${path}.${marker.operationId}.prepared`;
10761
11126
  const bytes = Buffer.from(`${JSON.stringify({ ...marker, publication: "hard-link-v1" })}
10762
11127
  `);
10763
- const fd = openSync2(stagingPath, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
10764
- const stat = fstatSync2(fd);
11128
+ const fd = openSync3(stagingPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
11129
+ const stat = fstatSync3(fd);
10765
11130
  let published = false, handedOff = false;
10766
11131
  try {
10767
11132
  writeFileSync3(fd, bytes);
10768
11133
  fsyncSync(fd);
10769
11134
  linkSync(stagingPath, path);
10770
11135
  published = true;
10771
- syncDirectory(dirname7(path));
10772
- const staged = lstatSync7(stagingPath);
11136
+ syncDirectory(dirname8(path));
11137
+ const staged = lstatSync8(stagingPath);
10773
11138
  if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
10774
11139
  throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
10775
11140
  }
10776
11141
  unlinkSync(stagingPath);
10777
- syncDirectory(dirname7(path));
11142
+ syncDirectory(dirname8(path));
10778
11143
  handedOff = true;
10779
11144
  return { path, stagingPath, fd, dev: stat.dev, ino: stat.ino };
10780
11145
  } finally {
10781
11146
  if (!handedOff) {
10782
11147
  try {
10783
11148
  for (const target of [...published ? [path] : [], stagingPath]) {
10784
- const current = lstatSync7(target, { throwIfNoEntry: false });
11149
+ const current = lstatSync8(target, { throwIfNoEntry: false });
10785
11150
  if (current?.isFile() && current.dev === stat.dev && current.ino === stat.ino)
10786
11151
  unlinkSync(target);
10787
11152
  }
10788
11153
  } finally {
10789
- closeSync2(fd);
11154
+ closeSync3(fd);
10790
11155
  }
10791
11156
  }
10792
11157
  }
@@ -10808,25 +11173,25 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
10808
11173
  const guardPath = `${lock.path}.recovery-guard`;
10809
11174
  let guardFd;
10810
11175
  try {
10811
- guardFd = openSync2(guardPath, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
11176
+ guardFd = openSync3(guardPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
10812
11177
  } catch (error) {
10813
11178
  if (error.code === "EEXIST")
10814
11179
  return null;
10815
11180
  throw error;
10816
11181
  }
10817
- const guardStat = fstatSync2(guardFd);
11182
+ const guardStat = fstatSync3(guardFd);
10818
11183
  try {
10819
11184
  writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
10820
11185
  `);
10821
11186
  fsyncSync(guardFd);
10822
- const cacheOptions = { cacheDir: dirname7(dirname7(lock.path)) };
11187
+ const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
10823
11188
  const current = readSessionLock(lock.marker.sessionId, cacheOptions);
10824
11189
  if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
10825
11190
  return null;
10826
11191
  const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
10827
11192
  if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
10828
11193
  return null;
10829
- const directory = join11(dirname7(dirname7(lock.path)), "session-lock-recoveries", randomUUID());
11194
+ const directory = join11(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
10830
11195
  const archivePath = join11(directory, "original.write-lock");
10831
11196
  const recordPath = join11(directory, "recovery.json");
10832
11197
  assertRegularPath(archivePath, true);
@@ -10843,7 +11208,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
10843
11208
  })}
10844
11209
  `));
10845
11210
  syncDirectory(directory);
10846
- syncDirectory(dirname7(directory));
11211
+ syncDirectory(dirname8(directory));
10847
11212
  const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
10848
11213
  if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
10849
11214
  return null;
@@ -10858,11 +11223,11 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
10858
11223
  archivedPaths.push(publicationPath);
10859
11224
  }
10860
11225
  syncDirectory(directory);
10861
- syncDirectory(dirname7(lock.path));
11226
+ syncDirectory(dirname8(lock.path));
10862
11227
  for (const path of archivedPaths) {
10863
- const before = lstatSync7(path, { throwIfNoEntry: false });
11228
+ const before = lstatSync8(path, { throwIfNoEntry: false });
10864
11229
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
10865
- const after = lstatSync7(path, { throwIfNoEntry: false });
11230
+ const after = lstatSync8(path, { throwIfNoEntry: false });
10866
11231
  if (!before?.isFile() || before.dev !== lock.dev || before.ino !== lock.ino || before.nlink !== archivedPaths.length || before.mtimeMs !== lock.mtimeMs || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid() || !bytes || sha256Hex(bytes) !== lock.sha256 || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
10867
11232
  throw new Error("Archived lock ownership or bytes changed");
10868
11233
  }
@@ -10874,8 +11239,8 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
10874
11239
  }
10875
11240
  return archivePath;
10876
11241
  } finally {
10877
- closeSync2(guardFd);
10878
- const current = lstatSync7(guardPath, { throwIfNoEntry: false });
11242
+ closeSync3(guardFd);
11243
+ const current = lstatSync8(guardPath, { throwIfNoEntry: false });
10879
11244
  if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
10880
11245
  unlinkSync(guardPath);
10881
11246
  }
@@ -10887,7 +11252,7 @@ function withSessionWriteLocks(sessionIds, options, action) {
10887
11252
  const owned = [];
10888
11253
  const assertOwned = () => {
10889
11254
  for (const lock of owned) {
10890
- const current = lstatSync7(lock.path, { throwIfNoEntry: false });
11255
+ const current = lstatSync8(lock.path, { throwIfNoEntry: false });
10891
11256
  if (!current?.isFile() || current.dev !== lock.dev || current.ino !== lock.ino) {
10892
11257
  throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "A session write lock changed during the operation; no replacement is permitted.");
10893
11258
  }
@@ -10931,12 +11296,12 @@ function withSessionWriteLocks(sessionIds, options, action) {
10931
11296
  for (const lock of owned.reverse()) {
10932
11297
  try {
10933
11298
  for (const path of [lock.path, lock.stagingPath]) {
10934
- const current = lstatSync7(path, { throwIfNoEntry: false });
11299
+ const current = lstatSync8(path, { throwIfNoEntry: false });
10935
11300
  if (current?.isFile() && current.dev === lock.dev && current.ino === lock.ino)
10936
11301
  unlinkSync(path);
10937
11302
  }
10938
11303
  } finally {
10939
- closeSync2(lock.fd);
11304
+ closeSync3(lock.fd);
10940
11305
  }
10941
11306
  }
10942
11307
  }
@@ -10987,21 +11352,21 @@ function writeSkillSession(receipt, expected, options = {}) {
10987
11352
  }
10988
11353
  function syncDirectory(path) {
10989
11354
  assertRegularPath(path);
10990
- const fd = openSync2(path, constants2.O_RDONLY | constants2.O_DIRECTORY | constants2.O_NOFOLLOW);
11355
+ const fd = openSync3(path, constants3.O_RDONLY | constants3.O_DIRECTORY | constants3.O_NOFOLLOW);
10991
11356
  try {
10992
11357
  fsyncSync(fd);
10993
11358
  } finally {
10994
- closeSync2(fd);
11359
+ closeSync3(fd);
10995
11360
  }
10996
11361
  }
10997
11362
  function archiveBytes(path, bytes) {
10998
11363
  assertRegularPath(path, true);
10999
- const fd = openSync2(path, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
11364
+ const fd = openSync3(path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
11000
11365
  try {
11001
11366
  writeFileSync3(fd, bytes);
11002
11367
  fsyncSync(fd);
11003
11368
  } finally {
11004
- closeSync2(fd);
11369
+ closeSync3(fd);
11005
11370
  }
11006
11371
  }
11007
11372
  function replaceSkillSession(expected, replacement, plan, options = {}, beforeCommit = () => {}) {
@@ -11028,7 +11393,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11028
11393
  archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
11029
11394
  `));
11030
11395
  syncDirectory(directory);
11031
- syncDirectory(dirname7(directory));
11396
+ syncDirectory(dirname8(directory));
11032
11397
  syncDirectory(selectionCacheRoot(options));
11033
11398
  if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
11034
11399
  throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
@@ -11048,7 +11413,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11048
11413
  // package.json
11049
11414
  var package_default = {
11050
11415
  name: "@hasna/skills",
11051
- version: "0.10.45",
11416
+ version: "0.10.46",
11052
11417
  description: "Skills library for AI coding agents",
11053
11418
  type: "module",
11054
11419
  bin: {
@@ -11185,12 +11550,12 @@ import { spawnSync as spawnSync2 } from "child_process";
11185
11550
  import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
11186
11551
  import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
11187
11552
  import { hostname as osHostname } from "os";
11188
- import { isAbsolute as isAbsolute7, join as join22 } from "path";
11553
+ import { isAbsolute as isAbsolute8, join as join22 } from "path";
11189
11554
  import { createRequire } from "module";
11190
11555
  import { fileURLToPath as fileURLToPath2 } from "url";
11191
- import { closeSync as closeSync3, fstatSync as fstatSync3, lstatSync as lstatSync8, openSync as openSync3, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
11556
+ import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
11192
11557
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
11193
- import { basename as basename3, dirname as dirname8, join as join12, relative as relative3, sep as sep3 } from "path";
11558
+ import { basename as basename4, dirname as dirname9, join as join12, relative as relative3, sep as sep3 } from "path";
11194
11559
  import { fileURLToPath, pathToFileURL } from "url";
11195
11560
  function envToken(name) {
11196
11561
  return name.toUpperCase().replace(/-/g, "_");
@@ -11268,15 +11633,15 @@ var PACKAGE = "@hasna/secrets";
11268
11633
  var MAX_PACKAGE_BYTES = 1024 * 1024;
11269
11634
  function entryPoint(directory) {
11270
11635
  const root = realpathSync2(directory);
11271
- const fd = openSync3(join12(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
11636
+ const fd = openSync4(join12(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
11272
11637
  let pkg;
11273
11638
  try {
11274
- const stat = fstatSync3(fd);
11639
+ const stat = fstatSync4(fd);
11275
11640
  if (!stat.isFile() || stat.size > MAX_PACKAGE_BYTES)
11276
11641
  throw new Error("Invalid Secrets package metadata");
11277
11642
  pkg = JSON.parse(readFileSync9(fd, "utf8"));
11278
11643
  } finally {
11279
- closeSync3(fd);
11644
+ closeSync4(fd);
11280
11645
  }
11281
11646
  if (!pkg || typeof pkg !== "object" || !("name" in pkg) || pkg.name !== PACKAGE) {
11282
11647
  throw new Error("Invalid Secrets package identity");
@@ -11296,13 +11661,13 @@ function entryPoint(directory) {
11296
11661
  return pathToFileURL(file).href;
11297
11662
  }
11298
11663
  function resolveInstalledSecrets(parentUrl) {
11299
- let directory = dirname8(fileURLToPath(parentUrl));
11664
+ let directory = dirname9(fileURLToPath(parentUrl));
11300
11665
  for (;; ) {
11301
- if (basename3(directory) !== "node_modules") {
11666
+ if (basename4(directory) !== "node_modules") {
11302
11667
  const candidate = join12(directory, "node_modules", PACKAGE);
11303
11668
  let absent = false;
11304
11669
  try {
11305
- lstatSync8(candidate);
11670
+ lstatSync9(candidate);
11306
11671
  } catch (error) {
11307
11672
  if (error.code !== "ENOENT")
11308
11673
  throw error;
@@ -11311,7 +11676,7 @@ function resolveInstalledSecrets(parentUrl) {
11311
11676
  if (!absent)
11312
11677
  return entryPoint(candidate);
11313
11678
  }
11314
- const parent = dirname8(directory);
11679
+ const parent = dirname9(directory);
11315
11680
  if (parent === directory)
11316
11681
  throw new Error("Secrets SDK is not installed for this consumer");
11317
11682
  directory = parent;
@@ -11358,7 +11723,7 @@ function homeDir(env) {
11358
11723
  }
11359
11724
  function absoluteOverride(env, key) {
11360
11725
  const value = env[key]?.trim();
11361
- return value && isAbsolute7(value) ? value : null;
11726
+ return value && isAbsolute8(value) ? value : null;
11362
11727
  }
11363
11728
  function hasnaHomeDir(env) {
11364
11729
  const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
@@ -12009,7 +12374,7 @@ var AUTHORITY_OVERRIDE_HEADERS = new Set([
12009
12374
  ]);
12010
12375
 
12011
12376
  // src/lib/instance-credentials.ts
12012
- import { closeSync as closeSync4, constants as constants3, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readSync as readSync3 } from "fs";
12377
+ import { closeSync as closeSync5, constants as constants4, fstatSync as fstatSync5, lstatSync as lstatSync10, openSync as openSync5, readSync as readSync4 } from "fs";
12013
12378
 
12014
12379
  // src/lib/cli-credential-profile.ts
12015
12380
  var cliCredentialProfile;
@@ -12033,7 +12398,7 @@ function skillsProfileCredentialFiles(env, explicit) {
12033
12398
  }
12034
12399
  function fileIdentity(file) {
12035
12400
  try {
12036
- return lstatSync9(file);
12401
+ return lstatSync10(file);
12037
12402
  } catch (error) {
12038
12403
  if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
12039
12404
  return null;
@@ -12054,14 +12419,14 @@ function captureSkillsCredentialFiles(files) {
12054
12419
  function readMetadataText(file) {
12055
12420
  let fd;
12056
12421
  try {
12057
- fd = openSync4(file, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
12422
+ fd = openSync5(file, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
12058
12423
  } catch (error) {
12059
12424
  if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
12060
12425
  return null;
12061
12426
  throw new Error("Cannot safely read Skills instance configuration");
12062
12427
  }
12063
12428
  try {
12064
- const before = fstatSync4(fd);
12429
+ const before = fstatSync5(fd);
12065
12430
  const uid = process.getuid?.() ?? process.geteuid?.();
12066
12431
  if (!before.isFile() || ![256, 384].includes(before.mode & 4095) || uid !== undefined && before.uid !== uid || before.size > 64 * 1024) {
12067
12432
  throw new Error("Unsafe Skills instance configuration; expected a bounded owner-only regular file");
@@ -12069,17 +12434,17 @@ function readMetadataText(file) {
12069
12434
  const bytes = Buffer.alloc(64 * 1024 + 1);
12070
12435
  let length = 0;
12071
12436
  while (length < bytes.length) {
12072
- const count = readSync3(fd, bytes, length, bytes.length - length, null);
12437
+ const count = readSync4(fd, bytes, length, bytes.length - length, null);
12073
12438
  if (!count)
12074
12439
  break;
12075
12440
  length += count;
12076
12441
  }
12077
- if (length > 64 * 1024 || !unchanged(before, fstatSync4(fd)) || !unchanged(before, fileIdentity(file))) {
12442
+ if (length > 64 * 1024 || !unchanged(before, fstatSync5(fd)) || !unchanged(before, fileIdentity(file))) {
12078
12443
  throw new Error("Skills instance configuration changed while reading");
12079
12444
  }
12080
12445
  return bytes.subarray(0, length).toString("utf8");
12081
12446
  } finally {
12082
- closeSync4(fd);
12447
+ closeSync5(fd);
12083
12448
  }
12084
12449
  }
12085
12450
  function readSkillsInstanceMetadata(file) {
@@ -12446,7 +12811,7 @@ class MissingSkillsFleetError extends Error {
12446
12811
  }
12447
12812
 
12448
12813
  // src/lib/remote-files.ts
12449
- import { createHash as createHash7 } from "crypto";
12814
+ import { createHash as createHash9 } from "crypto";
12450
12815
  var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
12451
12816
  function describeRemoteFiles(files) {
12452
12817
  if (files.length > 10)
@@ -12464,7 +12829,7 @@ function describeRemoteFiles(files) {
12464
12829
  });
12465
12830
  }
12466
12831
  function sha256(bytes) {
12467
- return createHash7("sha256").update(bytes).digest("hex");
12832
+ return createHash9("sha256").update(bytes).digest("hex");
12468
12833
  }
12469
12834
  async function readBoundedResponse(response, maximum) {
12470
12835
  if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
@@ -12654,7 +13019,7 @@ async function createProfileClient() {
12654
13019
  }
12655
13020
 
12656
13021
  // src/lib/dependency-preparation.ts
12657
- import { constants as constants4, closeSync as closeSync5, fstatSync as fstatSync5, lstatSync as lstatSync10, mkdirSync as mkdirSync4, openSync as openSync5, readFileSync as readFileSync10, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
13022
+ import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSync6, lstatSync as lstatSync11, mkdirSync as mkdirSync4, openSync as openSync6, readFileSync as readFileSync10, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
12658
13023
  import { join as join13 } from "path";
12659
13024
 
12660
13025
  // src/lib/preparation-process.ts
@@ -12702,7 +13067,7 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
12702
13067
  var RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.";
12703
13068
  function hasDependencyPreparationMarker(skillPath) {
12704
13069
  try {
12705
- lstatSync10(join13(skillPath, DEPENDENCY_PREPARATION_MARKER));
13070
+ lstatSync11(join13(skillPath, DEPENDENCY_PREPARATION_MARKER));
12706
13071
  return true;
12707
13072
  } catch (error) {
12708
13073
  return error.code !== "ENOENT";
@@ -12721,21 +13086,21 @@ function claimPreparation(skillPath) {
12721
13086
  }
12722
13087
  let fd;
12723
13088
  try {
12724
- if (!lstatSync10(marker).isDirectory() || lstatSync10(marker).isSymbolicLink())
13089
+ if (!lstatSync11(marker).isDirectory() || lstatSync11(marker).isSymbolicLink())
12725
13090
  return;
12726
13091
  if (!created) {
12727
13092
  if (readdirSync5(marker).some((name) => name !== "state.json"))
12728
13093
  return;
12729
- if (!lstatSync10(state).isFile() || lstatSync10(state).isSymbolicLink() || lstatSync10(state).size > 1024)
13094
+ if (!lstatSync11(state).isFile() || lstatSync11(state).isSymbolicLink() || lstatSync11(state).size > 1024)
12730
13095
  return;
12731
13096
  const previous = JSON.parse(readFileSync10(state, "utf8"));
12732
13097
  if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
12733
13098
  return;
12734
13099
  }
12735
13100
  mkdirSync4(active, { mode: 448 });
12736
- fd = openSync5(state, constants4.O_RDWR | constants4.O_NOFOLLOW | (created ? constants4.O_CREAT | constants4.O_EXCL : 0), 384);
12737
- if (!fstatSync5(fd).isFile()) {
12738
- closeSync5(fd);
13101
+ fd = openSync6(state, constants5.O_RDWR | constants5.O_NOFOLLOW | (created ? constants5.O_CREAT | constants5.O_EXCL : 0), 384);
13102
+ if (!fstatSync6(fd).isFile()) {
13103
+ closeSync6(fd);
12739
13104
  return;
12740
13105
  }
12741
13106
  const descriptor = fd;
@@ -12747,7 +13112,7 @@ function claimPreparation(skillPath) {
12747
13112
  record2("pending");
12748
13113
  return { abandon() {
12749
13114
  try {
12750
- closeSync5(descriptor);
13115
+ closeSync6(descriptor);
12751
13116
  } catch {}
12752
13117
  }, finish(success) {
12753
13118
  try {
@@ -12755,7 +13120,7 @@ function claimPreparation(skillPath) {
12755
13120
  unlinkSync2(state);
12756
13121
  else
12757
13122
  record2("failed");
12758
- closeSync5(descriptor);
13123
+ closeSync6(descriptor);
12759
13124
  fd = undefined;
12760
13125
  rmdirSync(active);
12761
13126
  if (success)
@@ -12763,7 +13128,7 @@ function claimPreparation(skillPath) {
12763
13128
  return true;
12764
13129
  } catch {
12765
13130
  try {
12766
- closeSync5(descriptor);
13131
+ closeSync6(descriptor);
12767
13132
  } catch {}
12768
13133
  return false;
12769
13134
  }
@@ -12771,7 +13136,7 @@ function claimPreparation(skillPath) {
12771
13136
  } catch {
12772
13137
  if (fd !== undefined)
12773
13138
  try {
12774
- closeSync5(fd);
13139
+ closeSync6(fd);
12775
13140
  } catch {}
12776
13141
  return;
12777
13142
  }
@@ -12867,36 +13232,36 @@ import { join as join27 } from "path";
12867
13232
  // src/lib/codex-corpus-write.ts
12868
13233
  import { KernelLock } from "@hasna/contracts/kernel-lock";
12869
13234
  import { execFileSync } from "child_process";
12870
- import { createHash as createHash8 } from "crypto";
12871
- import { accessSync, constants as constants5, lstatSync as lstatSync11, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
12872
- import { basename as basename4, delimiter, dirname as dirname9, isAbsolute as isAbsolute8, join as join14, relative as relative4, resolve as resolve8, sep as sep4 } from "path";
13235
+ import { createHash as createHash10 } from "crypto";
13236
+ import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
13237
+ import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
12873
13238
  var KEY = ".native-corpus-admission";
12874
13239
  var fail = () => {
12875
13240
  throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
12876
13241
  };
12877
13242
  function physicalTarget(path) {
12878
13243
  const suffix = [];
12879
- for (let cursor = path;; cursor = dirname9(cursor)) {
13244
+ for (let cursor = path;; cursor = dirname10(cursor)) {
12880
13245
  try {
12881
13246
  return join14(realpathSync3(cursor), ...suffix);
12882
13247
  } catch (error) {
12883
- if (error.code !== "ENOENT" || cursor === dirname9(cursor))
13248
+ if (error.code !== "ENOENT" || cursor === dirname10(cursor))
12884
13249
  throw error;
12885
13250
  try {
12886
- if (lstatSync11(cursor).isSymbolicLink())
13251
+ if (lstatSync12(cursor).isSymbolicLink())
12887
13252
  fail();
12888
13253
  } catch (statError) {
12889
13254
  if (statError.code !== "ENOENT")
12890
13255
  throw statError;
12891
13256
  }
12892
- suffix.unshift(basename4(cursor));
13257
+ suffix.unshift(basename5(cursor));
12893
13258
  }
12894
13259
  }
12895
13260
  }
12896
13261
  function codexCorpusRootForPath(path) {
12897
- const target = resolve8(path), physical = physicalTarget(target);
13262
+ const target = resolve9(path), physical = physicalTarget(target);
12898
13263
  const selected = process.env.CODEX_HOME;
12899
- const selectedRoots = selected && isAbsolute8(selected) ? [resolve8(selected), physicalTarget(resolve8(selected))] : [];
13264
+ const selectedRoots = selected && isAbsolute9(selected) ? [resolve9(selected), physicalTarget(resolve9(selected))] : [];
12900
13265
  const classify = (candidate) => {
12901
13266
  const checked = (root2) => {
12902
13267
  if (candidate === root2 || candidate === join14(root2, `${KEY}.flock-v1`))
@@ -12905,10 +13270,10 @@ function codexCorpusRootForPath(path) {
12905
13270
  };
12906
13271
  for (const root2 of selectedRoots) {
12907
13272
  const distance = relative4(root2, candidate);
12908
- if (distance === "" || !distance.startsWith(`..${sep4}`) && distance !== ".." && !isAbsolute8(distance))
13273
+ if (distance === "" || !distance.startsWith(`..${sep4}`) && distance !== ".." && !isAbsolute9(distance))
12909
13274
  return checked(root2);
12910
13275
  }
12911
- for (let cursor = candidate;cursor !== dirname9(cursor); cursor = dirname9(cursor)) {
13276
+ for (let cursor = candidate;cursor !== dirname10(cursor); cursor = dirname10(cursor)) {
12912
13277
  if (cursor.endsWith(`${sep4}.codex`))
12913
13278
  return checked(cursor);
12914
13279
  }
@@ -12920,7 +13285,7 @@ function codexCorpusRootForPath(path) {
12920
13285
  return root;
12921
13286
  }
12922
13287
  function identity3(path) {
12923
- const stat = lstatSync11(path, { bigint: true });
13288
+ const stat = lstatSync12(path, { bigint: true });
12924
13289
  return { device: String(stat.dev), inode: String(stat.ino) };
12925
13290
  }
12926
13291
  function sameIdentity(value, expected) {
@@ -12934,12 +13299,12 @@ function executable(options) {
12934
13299
  if (typeof command !== "string" || !command || command.includes("\x00") || options.codexSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.codexSha256))
12935
13300
  return fail();
12936
13301
  let found;
12937
- if (isAbsolute8(command))
13302
+ if (isAbsolute9(command))
12938
13303
  found = command;
12939
13304
  else if (!command.includes(sep4)) {
12940
- found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute8).map((dir) => join14(dir, command)).find((candidate) => {
13305
+ found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) => join14(dir, command)).find((candidate) => {
12941
13306
  try {
12942
- accessSync(candidate, constants5.X_OK);
13307
+ accessSync(candidate, constants6.X_OK);
12943
13308
  return true;
12944
13309
  } catch {
12945
13310
  return false;
@@ -12951,7 +13316,7 @@ function executable(options) {
12951
13316
  const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
12952
13317
  if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
12953
13318
  return fail();
12954
- const hash2 = () => createHash8("sha256").update(readFileSync11(path)).digest("hex");
13319
+ const hash2 = () => createHash10("sha256").update(readFileSync11(path)).digest("hex");
12955
13320
  const digest = hash2();
12956
13321
  if (options.codexSha256 && digest !== options.codexSha256)
12957
13322
  return fail();
@@ -12975,7 +13340,7 @@ function acquireCodexCorpusWrite(roots, options) {
12975
13340
  }
12976
13341
  };
12977
13342
  try {
12978
- if (roots.some((root) => typeof root !== "string" || !isAbsolute8(root) || root.includes("\x00")))
13343
+ if (roots.some((root) => typeof root !== "string" || !isAbsolute9(root) || root.includes("\x00")))
12979
13344
  fail();
12980
13345
  const inputs = [...new Set(roots)].map((input) => ({ input, root: realpathSync3(input) })).sort((a, b) => a.root.localeCompare(b.root));
12981
13346
  for (const { input, root } of inputs) {
@@ -13048,7 +13413,7 @@ import { homedir as homedir3 } from "os";
13048
13413
  import { basename as basename7, dirname as dirname15, join as join23 } from "path";
13049
13414
 
13050
13415
  // src/lib/codex-plugin-skill-controls.ts
13051
- import { createHash as createHash10 } from "crypto";
13416
+ import { createHash as createHash11 } from "crypto";
13052
13417
 
13053
13418
  // src/lib/codex-local-mcp-controls.ts
13054
13419
  function isCodexLocalStdioMcpServer(name, server) {
@@ -13116,317 +13481,6 @@ function isCodexLocalStdioMcpServer(name, server) {
13116
13481
  }
13117
13482
  }
13118
13483
 
13119
- // src/lib/claude-settings-witness.ts
13120
- import { createHash as createHash9 } from "crypto";
13121
- import { closeSync as closeSync6, constants as constants6, fstatSync as fstatSync6, lstatSync as lstatSync12, openSync as openSync6, readSync as readSync4 } from "fs";
13122
- import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute9, resolve as resolve9 } from "path";
13123
- var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
13124
- function need2(value, reason) {
13125
- if (!value)
13126
- throw new Error(`Claude settings witness ${reason}`);
13127
- }
13128
- function absolutePath(value) {
13129
- need2(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute9(value) && resolve9(value) === value, "requires a normalized absolute path");
13130
- }
13131
- var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
13132
- function parents(path) {
13133
- const result = [];
13134
- for (let at = dirname10(path);; at = dirname10(at)) {
13135
- const stat = lstatSync12(at, { bigint: true });
13136
- need2(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
13137
- result.push([at, stat]);
13138
- if (at === dirname10(at))
13139
- return result;
13140
- }
13141
- }
13142
- function readNativeSettingsWitnessFile(path, budget, filename = "settings.json") {
13143
- absolutePath(path);
13144
- need2(basename5(path) === filename, `requires ${filename}`);
13145
- need2(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
13146
- const ancestors = parents(path), initial = lstatSync12(path, { bigint: true });
13147
- need2(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
13148
- need2(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
13149
- const fd = openSync6(path, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
13150
- try {
13151
- const opened = fstatSync6(fd, { bigint: true });
13152
- need2(opened.isFile() && sameFile(initial, opened), "changed during open");
13153
- const bytes = Buffer.alloc(Number(opened.size) + 1);
13154
- let length = 0;
13155
- while (length < bytes.length) {
13156
- const count = readSync4(fd, bytes, length, bytes.length - length, null);
13157
- if (!count)
13158
- break;
13159
- length += count;
13160
- }
13161
- const after = fstatSync6(fd, { bigint: true }), current = lstatSync12(path, { bigint: true });
13162
- need2(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
13163
- for (const [ancestor, before] of ancestors) {
13164
- const now = lstatSync12(ancestor, { bigint: true });
13165
- need2(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
13166
- }
13167
- budget.remaining -= length;
13168
- try {
13169
- return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
13170
- } catch {
13171
- throw new Error("Claude settings witness is not strict UTF-8");
13172
- }
13173
- } finally {
13174
- closeSync6(fd);
13175
- }
13176
- }
13177
- function parse3(text2) {
13178
- let at = 0, nodes = 0;
13179
- const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
13180
- const space = () => {
13181
- while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
13182
- at++;
13183
- };
13184
- function string() {
13185
- need2(text2[at] === '"', "contains invalid JSON");
13186
- const start = at++;
13187
- while (at < text2.length) {
13188
- const character = text2[at++];
13189
- if (character === '"') {
13190
- let value2;
13191
- try {
13192
- value2 = JSON.parse(text2.slice(start, at));
13193
- } catch {
13194
- throw new Error("Claude settings witness contains invalid JSON string");
13195
- }
13196
- need2(value2.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.stringCharacters, "exceeds its string limit");
13197
- return value2;
13198
- }
13199
- if (character === "\\")
13200
- at++;
13201
- }
13202
- throw new Error("Claude settings witness contains an unterminated JSON string");
13203
- }
13204
- function value(depth) {
13205
- need2(depth <= CLAUDE_SETTINGS_WITNESS_LIMITS.depth && ++nodes <= CLAUDE_SETTINGS_WITNESS_LIMITS.nodes, "exceeds its nesting or node limit");
13206
- space();
13207
- const character = text2[at];
13208
- if (character === '"')
13209
- return { kind: "string", value: string() };
13210
- if (character === "{" || character === "[") {
13211
- at++;
13212
- space();
13213
- const object4 = character === "{", end = object4 ? "}" : "]", entries = [], items = [], keys = new Set;
13214
- if (text2[at] !== end)
13215
- while (true) {
13216
- if (object4) {
13217
- space();
13218
- const key = string();
13219
- need2(!keys.has(key), "contains a duplicate JSON key");
13220
- keys.add(key);
13221
- space();
13222
- need2(text2[at++] === ":", "contains invalid JSON");
13223
- entries.push([key, value(depth + 1)]);
13224
- } else
13225
- items.push(value(depth + 1));
13226
- space();
13227
- if (text2[at] === end)
13228
- break;
13229
- need2(text2[at++] === ",", "contains invalid JSON");
13230
- }
13231
- at++;
13232
- return object4 ? { kind: "object", entries } : { kind: "array", items };
13233
- }
13234
- for (const literal of ["true", "false", "null"])
13235
- if (text2.startsWith(literal, at)) {
13236
- at += literal.length;
13237
- return { kind: "literal", value: literal };
13238
- }
13239
- numberPattern.lastIndex = at;
13240
- const number = numberPattern.exec(text2);
13241
- need2(number, "contains invalid JSON");
13242
- at += number[0].length;
13243
- return { kind: "number", value: number[0] };
13244
- }
13245
- const root = value(0);
13246
- space();
13247
- need2(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
13248
- need2(root.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
13249
- return root;
13250
- }
13251
- var BOOLEAN_PREFERENCES = new Set([
13252
- "autoScrollEnabled",
13253
- "axScreenReader",
13254
- "emojiCompletionEnabled",
13255
- "prefersReducedMotion",
13256
- "showTurnDuration",
13257
- "spinnerTipsEnabled",
13258
- "syntaxHighlightingDisabled",
13259
- "terminalProgressBarEnabled",
13260
- "terminalTitleFromRename",
13261
- "verbose",
13262
- "wheelScrollAccelerationEnabled"
13263
- ]);
13264
- var ENUM_PREFERENCES = Object.freeze({
13265
- editorMode: ["normal", "vim"],
13266
- tui: ["default", "fullscreen"],
13267
- viewMode: ["default", "verbose", "focus"]
13268
- });
13269
- var BUILTIN_MODEL_SELECTIONS = new Set([
13270
- "default",
13271
- "best",
13272
- "fable",
13273
- "fable[1m]",
13274
- "sonnet",
13275
- "sonnet[1m]",
13276
- "opus",
13277
- "opus[1m]",
13278
- "haiku",
13279
- "opusplan",
13280
- "claude-fable-5-1",
13281
- "claude-fable-5",
13282
- "claude-fable-5[1m]",
13283
- "claude-opus-5",
13284
- "claude-sonnet-5",
13285
- "claude-haiku-4-5-20251001",
13286
- "claude-opus-4-6",
13287
- "claude-sonnet-4-5",
13288
- "claude-sonnet-4-5-20250929",
13289
- "claude-opus-4-8",
13290
- "claude-opus-4-8[1m]",
13291
- "claude-opus-4-7",
13292
- "claude-sonnet-4-6",
13293
- "claude-opus-4-5-20251101",
13294
- "claude-opus-4-5",
13295
- "claude-haiku-4-5",
13296
- "claude-fable-5-1[1m]",
13297
- "claude-opus-5[1m]",
13298
- "claude-opus-4-7[1m]",
13299
- "claude-opus-4-6[1m]",
13300
- "claude-sonnet-4-6[1m]"
13301
- ]);
13302
- function canonical2(value) {
13303
- if (value.kind === "object")
13304
- return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical2(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
13305
- if (value.kind === "array")
13306
- return { kind: "array", items: value.items.map(canonical2) };
13307
- return value;
13308
- }
13309
- var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
13310
- function assertClaudeHookEventsReplacement(before, replacement, events) {
13311
- 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");
13312
- const label = events.join(",");
13313
- function bounded(text2) {
13314
- need2(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
13315
- return parse3(text2);
13316
- }
13317
- const original = bounded(before ?? "{}"), next = bounded(replacement);
13318
- const hadHooks = original.entries.some(([key]) => key === "hooks");
13319
- function withoutOwnedEvents(value) {
13320
- const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
13321
- if (hooks !== undefined) {
13322
- need2(hooks.kind === "object", `${label} update requires an object of hooks`);
13323
- for (const event of events) {
13324
- const selected = hooks.entries.find(([key]) => key === event)?.[1];
13325
- need2(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
13326
- }
13327
- hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
13328
- if (!hadHooks && hooks.entries.length === 0)
13329
- value.entries = value.entries.filter(([key]) => key !== "hooks");
13330
- }
13331
- return canonical2(value);
13332
- }
13333
- need2(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
13334
- }
13335
- var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
13336
- function inferencePreference(key, child, version) {
13337
- if (key === "skipDangerousModePermissionPrompt") {
13338
- need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
13339
- return false;
13340
- }
13341
- if (key === "effortLevel") {
13342
- need2(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
13343
- return true;
13344
- }
13345
- if (key !== "modelSettings")
13346
- return false;
13347
- need2(child.kind === "object", "requires an object of model preferences");
13348
- child.entries = child.entries.filter(([model, preferences]) => {
13349
- if (version === 2 && !BUILTIN_MODEL_SELECTIONS.has(model) || preferences.kind !== "object" || preferences.entries.length !== 1 || preferences.entries[0][0] !== "effortLevel")
13350
- return true;
13351
- const effort = preferences.entries[0][1];
13352
- need2(effort.kind === "string" && PERSISTED_EFFORT_LEVELS.has(effort.value), "contains an invalid persisted model effort level");
13353
- return false;
13354
- });
13355
- return child.entries.length === 0;
13356
- }
13357
- function settingsDigest(text2, version = 1) {
13358
- const value = parse3(text2);
13359
- value.entries = value.entries.filter(([key, child]) => {
13360
- if (version !== 1 && inferencePreference(key, child, version))
13361
- return false;
13362
- if (key === "model") {
13363
- need2(child.kind === "string", "contains an invalid model selection");
13364
- return !BUILTIN_MODEL_SELECTIONS.has(child.value);
13365
- }
13366
- if (BOOLEAN_PREFERENCES.has(key)) {
13367
- need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid display preference");
13368
- return false;
13369
- }
13370
- if (Object.hasOwn(ENUM_PREFERENCES, key)) {
13371
- need2(child.kind === "string" && ENUM_PREFERENCES[key].includes(child.value), "contains an invalid display preference");
13372
- return false;
13373
- }
13374
- return true;
13375
- });
13376
- return createHash9("sha256").update(`hasna.skills.claude-settings.v${version}\x00`).update(JSON.stringify(canonical2(value))).digest("hex");
13377
- }
13378
- function hashClaudeSettingsReplacement(text2, budget) {
13379
- need2(typeof text2 === "string", "requires settings text");
13380
- const bytes = Buffer.byteLength(text2);
13381
- need2(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
13382
- budget.remaining -= bytes;
13383
- return settingsDigest(text2);
13384
- }
13385
- function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
13386
- return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget)) };
13387
- }
13388
- function captureClaudeSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
13389
- return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 2) };
13390
- }
13391
- function hashClaudeSettingsReplacementV2(text2, budget) {
13392
- need2(typeof text2 === "string", "requires settings text");
13393
- const bytes = Buffer.byteLength(text2);
13394
- need2(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
13395
- budget.remaining -= bytes;
13396
- return settingsDigest(text2, 2);
13397
- }
13398
- function captureClaudeSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
13399
- return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 3) };
13400
- }
13401
- function hashClaudeSettingsReplacementV3(text2, budget) {
13402
- need2(typeof text2 === "string", "requires settings text");
13403
- const bytes = Buffer.byteLength(text2);
13404
- need2(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
13405
- budget.remaining -= bytes;
13406
- return settingsDigest(text2, 3);
13407
- }
13408
- function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
13409
- need2(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact legacy settings witness");
13410
- const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
13411
- const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
13412
- const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash9("sha256").update(before).digest("hex") : settingsDigest(before, previous.hashMode === "claude-settings-v1" ? 1 : 2);
13413
- need2(beforeDigest === previous.sha256, "reviewed preimage does not match the legacy witness");
13414
- const sha2562 = settingsDigest(current, 3);
13415
- need2(settingsDigest(before, 3) === sha2562, "non-preference settings changed; explicit discovery review required");
13416
- return { path: previous.path, hashMode: "claude-settings-v3", sha256: sha2562 };
13417
- }
13418
- function hashNativeJsonControls(text2, excludedMetadata) {
13419
- need2(Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
13420
- const value = parse3(text2);
13421
- value.entries = value.entries.filter(([key, child]) => {
13422
- if (key !== excludedMetadata)
13423
- return true;
13424
- need2(child.kind === "string" && child.value.length <= 128, "has invalid version metadata");
13425
- return false;
13426
- });
13427
- return createHash9("sha256").update("hasna.skills.native-json-controls.v1\x00").update(JSON.stringify(canonical2(value))).digest("hex");
13428
- }
13429
-
13430
13484
  // src/lib/codex-plugin-skill-controls.ts
13431
13485
  import { basename as basename6, dirname as dirname11, join as join16, relative as relative5, resolve as resolve10, sep as sep5 } from "path";
13432
13486
  import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
@@ -14117,7 +14171,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14117
14171
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
14118
14172
  const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
14119
14173
  const denial = denials.find((item) => item.path === document2);
14120
- const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash10("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
14174
+ const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash11("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
14121
14175
  if (denial && !explicitDeny)
14122
14176
  refuse2();
14123
14177
  if (explicitDeny)
@@ -14204,7 +14258,7 @@ enabled = false
14204
14258
  }
14205
14259
 
14206
14260
  // src/lib/codex-native-skill-policy.ts
14207
- import { createHash as createHash11, randomUUID as randomUUID2 } from "crypto";
14261
+ import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
14208
14262
  import { closeSync as closeSync7, constants as constants7, fstatSync as fstatSync7, fsyncSync as fsyncSync2, lstatSync as lstatSync14, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync12, readSync as readSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
14209
14263
  import { dirname as dirname12, isAbsolute as isAbsolute12, join as join17, resolve as resolve11 } from "path";
14210
14264
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
@@ -14332,7 +14386,7 @@ function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256)
14332
14386
  if (realpathSync7.native(path) !== path)
14333
14387
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
14334
14388
  const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
14335
- const sha2562 = createHash11("sha256").update(bytes).digest("hex");
14389
+ const sha2562 = createHash12("sha256").update(bytes).digest("hex");
14336
14390
  if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
14337
14391
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
14338
14392
  return { path, sha256: sha2562 };
@@ -14800,8 +14854,8 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
14800
14854
  }
14801
14855
 
14802
14856
  // src/lib/codex-settings-witness-v1.ts
14803
- import { createHash as createHash12 } from "crypto";
14804
- var sha = (text2) => createHash12("sha256").update(text2).digest("hex");
14857
+ import { createHash as createHash13 } from "crypto";
14858
+ var sha = (text2) => createHash13("sha256").update(text2).digest("hex");
14805
14859
  var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
14806
14860
  var VERBOSITY = new Set(["low", "medium", "high"]);
14807
14861
  function need3(value) {
@@ -14847,8 +14901,8 @@ function numericTokens(text2) {
14847
14901
  }
14848
14902
  return result;
14849
14903
  }
14850
- function canonical3(value, depth = 0, budget = { remaining: 65536 }) {
14851
- need3(depth <= 32 && --budget.remaining >= 0);
14904
+ function canonical3(value, depth = 0, budget2 = { remaining: 65536 }) {
14905
+ need3(depth <= 32 && --budget2.remaining >= 0);
14852
14906
  if (value === null)
14853
14907
  return ["null"];
14854
14908
  if (typeof value === "string")
@@ -14864,13 +14918,13 @@ function canonical3(value, depth = 0, budget = { remaining: 65536 }) {
14864
14918
  return ["date", value.toISOString()];
14865
14919
  }
14866
14920
  if (Array.isArray(value))
14867
- return ["array", value.map((child) => canonical3(child, depth + 1, budget))];
14921
+ return ["array", value.map((child) => canonical3(child, depth + 1, budget2))];
14868
14922
  need3(value && typeof value === "object");
14869
- return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1, budget)])];
14923
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1, budget2)])];
14870
14924
  }
14871
- function hashCodexSettingsReplacement(text2, budget = { remaining: 256 * 1024 * 1024 }) {
14872
- need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
14873
- budget.remaining -= Buffer.byteLength(text2);
14925
+ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
14926
+ need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
14927
+ budget2.remaining -= Buffer.byteLength(text2);
14874
14928
  let config;
14875
14929
  try {
14876
14930
  config = Bun.TOML.parse(text2);
@@ -14889,8 +14943,8 @@ function hashCodexSettingsReplacement(text2, budget = { remaining: 256 * 1024 *
14889
14943
  }
14890
14944
  return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical3(config), numericTokens(text2)])}`);
14891
14945
  }
14892
- function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
14893
- const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
14946
+ function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
14947
+ const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
14894
14948
  return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
14895
14949
  }
14896
14950
  // src/lib/codex-mcp-discovery-projection.ts
@@ -14958,9 +15012,9 @@ function projectRegularCodexMcp(config, source) {
14958
15012
  }
14959
15013
 
14960
15014
  // src/lib/codex-settings-witness.ts
14961
- import { createHash as createHash13 } from "crypto";
15015
+ import { createHash as createHash14 } from "crypto";
14962
15016
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
14963
- var sha2 = (text2) => createHash13("sha256").update(text2).digest("hex");
15017
+ var sha2 = (text2) => createHash14("sha256").update(text2).digest("hex");
14964
15018
  var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
14965
15019
  var VERBOSITY2 = new Set(["low", "medium", "high"]);
14966
15020
  var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
@@ -15015,8 +15069,8 @@ function numericTokens2(text2) {
15015
15069
  }
15016
15070
  return result.sort();
15017
15071
  }
15018
- function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
15019
- need4(depth <= 32 && --budget.remaining >= 0);
15072
+ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15073
+ need4(depth <= 32 && --budget2.remaining >= 0);
15020
15074
  if (value === null)
15021
15075
  return ["null"];
15022
15076
  if (typeof value === "string")
@@ -15032,9 +15086,9 @@ function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
15032
15086
  return ["date", value.toISOString()];
15033
15087
  }
15034
15088
  if (Array.isArray(value))
15035
- return ["array", value.map((child) => canonical4(child, depth + 1, budget))];
15089
+ return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
15036
15090
  need4(isRecord3(value));
15037
- return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget)])];
15091
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget2)])];
15038
15092
  }
15039
15093
  function normalize3(config, version = 2) {
15040
15094
  if (isRecord3(config.hooks)) {
@@ -15210,40 +15264,40 @@ function settingsDigest2(text2, version = 2) {
15210
15264
  normalize3(projected.config, version);
15211
15265
  return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical4(projected.config), numericTokens2(projected.numericSource)])}`);
15212
15266
  }
15213
- function hashCodexSettingsReplacementV2(text2, budget = { remaining: 256 * 1024 * 1024 }) {
15214
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
15215
- budget.remaining -= Buffer.byteLength(text2);
15267
+ function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15268
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15269
+ budget2.remaining -= Buffer.byteLength(text2);
15216
15270
  return settingsDigest2(text2);
15217
15271
  }
15218
- function captureCodexSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
15219
- const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
15272
+ function captureCodexSettingsV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
15273
+ const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
15220
15274
  return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
15221
15275
  }
15222
- function readCodexSettingsPreimage(path, budget = { remaining: 2 * 1024 * 1024 }) {
15223
- return readNativeSettingsWitnessFile(path, budget, "config.toml");
15276
+ function readCodexSettingsPreimage(path, budget2 = { remaining: 2 * 1024 * 1024 }) {
15277
+ return readNativeSettingsWitnessFile(path, budget2, "config.toml");
15224
15278
  }
15225
15279
  function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
15226
15280
  need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
15227
- const budget = { remaining: 2 * 1024 * 1024 };
15228
- const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
15281
+ const budget2 = { remaining: 2 * 1024 * 1024 };
15282
+ const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15229
15283
  need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
15230
15284
  const sha2562 = settingsDigest2(current);
15231
15285
  if (settingsDigest2(before) !== sha2562)
15232
15286
  throw new Error("Codex non-preference settings changed; explicit discovery review required");
15233
15287
  return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
15234
15288
  }
15235
- function hashCodexSettingsReplacementV3(text2, budget = { remaining: 256 * 1024 * 1024 }) {
15236
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
15237
- budget.remaining -= Buffer.byteLength(text2);
15289
+ function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15290
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15291
+ budget2.remaining -= Buffer.byteLength(text2);
15238
15292
  return settingsDigest2(text2, 3);
15239
15293
  }
15240
- function captureCodexSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
15241
- return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget, "config.toml"), 3) };
15294
+ function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
15295
+ return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
15242
15296
  }
15243
15297
  function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
15244
15298
  need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
15245
- const budget = { remaining: 2 * 1024 * 1024 };
15246
- const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
15299
+ const budget2 = { remaining: 2 * 1024 * 1024 };
15300
+ const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15247
15301
  const original = previous.hashMode === "codex-settings-v2" ? settingsDigest2(before) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
15248
15302
  need4(original === previous.sha256);
15249
15303
  const sha2562 = settingsDigest2(current, 3);
@@ -15251,18 +15305,18 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
15251
15305
  throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
15252
15306
  return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
15253
15307
  }
15254
- function hashCodexSettingsReplacementV4(text2, budget = { remaining: 256 * 1024 * 1024 }) {
15255
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
15256
- budget.remaining -= Buffer.byteLength(text2);
15308
+ function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15309
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15310
+ budget2.remaining -= Buffer.byteLength(text2);
15257
15311
  return settingsDigest2(text2, 4);
15258
15312
  }
15259
- function captureCodexSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
15260
- return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget, "config.toml"), 4) };
15313
+ function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
15314
+ return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
15261
15315
  }
15262
15316
  function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15263
15317
  need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
15264
- const budget = { remaining: 2 * 1024 * 1024 };
15265
- const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
15318
+ const budget2 = { remaining: 2 * 1024 * 1024 };
15319
+ const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15266
15320
  const original = previous.hashMode === "codex-settings-v3" ? settingsDigest2(before, 3) : previous.hashMode === "codex-settings-v2" ? settingsDigest2(before, 2) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
15267
15321
  need4(original === previous.sha256);
15268
15322
  const sha2562 = settingsDigest2(current, 4);
@@ -15272,7 +15326,7 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15272
15326
  }
15273
15327
 
15274
15328
  // src/lib/agent-integration.ts
15275
- import { createHash as createHash16, randomUUID as randomUUID3 } from "crypto";
15329
+ import { createHash as createHash17, randomUUID as randomUUID3 } from "crypto";
15276
15330
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
15277
15331
  import { existsSync as existsSync9, lstatSync as lstatSync19, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync5, realpathSync as realpathSync9, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync4, fstatSync as fstatSync10, readSync as readSync8, constants as constants10, linkSync as linkSync2 } from "fs";
15278
15332
  import { dirname as dirname14, join as join21, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute13, sep as sep6 } from "path";
@@ -15359,7 +15413,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
15359
15413
  }
15360
15414
 
15361
15415
  // src/lib/agent-bridge.ts
15362
- import { createHash as createHash14 } from "crypto";
15416
+ import { createHash as createHash15 } from "crypto";
15363
15417
  import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
15364
15418
  import { join as join19 } from "path";
15365
15419
  var CLI_BRIDGE_NAME = "skills-cli";
@@ -15378,7 +15432,7 @@ Use the Skills CLI as the skill authority for this station.
15378
15432
 
15379
15433
  This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
15380
15434
  `;
15381
- var CLI_BRIDGE_DIGEST = createHash14("sha256").update(markdown).digest("hex");
15435
+ var CLI_BRIDGE_DIGEST = createHash15("sha256").update(markdown).digest("hex");
15382
15436
  var CLI_BRIDGE_FILES = Object.freeze({
15383
15437
  "SKILL.md": markdown,
15384
15438
  ".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
@@ -15703,31 +15757,44 @@ function sumiConfigPath(home) {
15703
15757
  return join20(sumiConfigDirectory(home), "sumi.json");
15704
15758
  }
15705
15759
  function renderSumiPlugin(command, profile) {
15760
+ return renderSumiPluginVersion(command, profile, false);
15761
+ }
15762
+ function isManagedSumiPlugin(text2, command, profile) {
15763
+ return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, true);
15764
+ }
15765
+ function renderSumiPluginVersion(command, profile, legacy) {
15766
+ const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
15706
15767
  return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
15707
15768
  import { spawn } from "node:child_process";
15708
15769
  const command = ${JSON.stringify(command)};
15709
15770
  const profile = ${JSON.stringify(profile)};
15710
- function context(input) {
15771
+ ${legacy ? "" : `function refusal() {
15772
+ const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
15773
+ error.name = "SkillsHookRefusal";
15774
+ error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
15775
+ return error;
15776
+ }
15777
+ `}function context(input) {
15711
15778
  return new Promise((resolve, reject) => {
15712
15779
  const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
15713
15780
  const chunks = []; let bytes = 0, failure;
15714
- const timer = setTimeout(() => { failure = new Error("Skills prompt hook timed out"); child.kill("SIGKILL"); }, 15000);
15715
- child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 1048576) { failure = new Error("Skills prompt hook output exceeded its limit"); child.kill("SIGKILL"); } else chunks.push(chunk); });
15781
+ const timer = setTimeout(() => { failure = ${failure("Skills prompt hook timed out")}; child.kill("SIGKILL"); }, 15000);
15782
+ child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 1048576) { failure = ${failure("Skills prompt hook output exceeded its limit")}; child.kill("SIGKILL"); } else chunks.push(chunk); });
15716
15783
  child.stderr.on("data", () => {});
15717
15784
  child.stdin.on("error", () => {});
15718
- child.on("error", error => { clearTimeout(timer); reject(error); });
15785
+ child.on("error", error => { clearTimeout(timer); reject(${legacy ? "error" : "refusal()"}); });
15719
15786
  child.on("close", code => {
15720
15787
  clearTimeout(timer);
15721
- if (failure || code !== 0) return reject(failure ?? new Error("Skills prompt hook failed"));
15788
+ if (failure || code !== 0) return reject(failure ?? ${failure("Skills prompt hook failed")});
15722
15789
  try {
15723
15790
  const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
15724
- if (!result || typeof result !== "object" || Array.isArray(result)) throw new Error("Invalid Skills hook response");
15725
- if (result.decision === "block" || result.continue === false) throw new Error("Skills prompt hook refused");
15726
- if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw new Error("Invalid Skills hook response");
15791
+ if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
15792
+ if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
15793
+ if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
15727
15794
  const text = result.hookSpecificOutput?.additionalContext ?? "";
15728
- if (typeof text !== "string") throw new Error("Invalid Skills prompt context");
15795
+ if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
15729
15796
  resolve(text);
15730
- } catch (error) { reject(error); }
15797
+ } catch (error) { reject(${legacy ? "error" : "refusal()"}); }
15731
15798
  });
15732
15799
  child.stdin.end(JSON.stringify(input));
15733
15800
  });
@@ -15735,13 +15802,14 @@ function context(input) {
15735
15802
  export default {
15736
15803
  id: "local.hasna-skills-cli",
15737
15804
  async setup(ctx) {
15738
- if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw new Error("Unsupported Sumi native Skills contract");
15805
+ if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw ${failure("Unsupported Sumi native Skills contract")};
15739
15806
  const registrations = [];
15740
15807
  const sessions = new Set();
15741
15808
  async function input(sessionID, prompt, first) {
15742
15809
  // The Promise adapter unwraps single-data endpoint responses.
15743
- const session = await ctx.session.get({ sessionID });
15744
- if (!session || session.id !== sessionID || typeof session.location?.directory !== "string" || (session.parentID !== undefined && typeof session.parentID !== "string") || session.parentID === sessionID) throw new Error("Invalid Sumi session custody");
15810
+ ${legacy ? "const session = await ctx.session.get({ sessionID });" : `let session;
15811
+ try { session = await ctx.session.get({ sessionID }); } catch { throw refusal(); }`}
15812
+ if (!session || session.id !== sessionID || typeof session.location?.directory !== "string" || (session.parentID !== undefined && typeof session.parentID !== "string") || session.parentID === sessionID) throw ${failure("Invalid Sumi session custody")};
15745
15813
  return { hook_event_name: first ? session.parentID ? "SubagentStart" : "SessionStart" : "UserPromptSubmit", cwd: session.location.directory, session_id: sessionID, parent_session_id: session.parentID ?? null, prompt, restore: true };
15746
15814
  }
15747
15815
  registrations.push(await ctx.skill.transform(editor => {
@@ -15750,11 +15818,11 @@ export default {
15750
15818
  // Native config transforms may add skills after this transform. Guard
15751
15819
  // both direct tool calls and prompt attachments at the consumption edge.
15752
15820
  registrations.push(await ctx.tool.hook("execute.before", event => {
15753
- if (event.tool === "skill" && event.input?.id !== "skills-cli") throw new Error("Native Sumi skill payload refused; use Skills CLI");
15821
+ if (event.tool === "skill" && event.input?.id !== "skills-cli") throw ${failure("Native Sumi skill payload refused; use Skills CLI")};
15754
15822
  }));
15755
15823
  registrations.push(await ctx.session.hook("prompt", async event => {
15756
- if (typeof event.prompt?.text !== "string") throw new Error("Invalid Sumi prompt");
15757
- if (event.prompt.skills !== undefined && (!Array.isArray(event.prompt.skills) || event.prompt.skills.some(skill => skill?.id !== "skills-cli"))) throw new Error("Native Sumi skill attachment refused; use Skills CLI");
15824
+ if (typeof event.prompt?.text !== "string") throw ${failure("Invalid Sumi prompt")};
15825
+ if (event.prompt.skills !== undefined && (!Array.isArray(event.prompt.skills) || event.prompt.skills.some(skill => skill?.id !== "skills-cli"))) throw ${failure("Native Sumi skill attachment refused; use Skills CLI")};
15758
15826
  await context(await input(event.sessionID, event.prompt.text, !sessions.has(event.sessionID)));
15759
15827
  sessions.add(event.sessionID);
15760
15828
  }));
@@ -15779,7 +15847,7 @@ var SUMI_SKILL_PERMISSIONS = [
15779
15847
 
15780
15848
  // src/lib/codex-hook-trust-files.ts
15781
15849
  import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
15782
- import { createHash as createHash15 } from "crypto";
15850
+ import { createHash as createHash16 } from "crypto";
15783
15851
  import { dirname as dirname13 } from "path";
15784
15852
  function need5(value, code) {
15785
15853
  if (!value)
@@ -15815,7 +15883,7 @@ function snapshot(file, privateFile = false, options = {}) {
15815
15883
  }
15816
15884
  need5(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
15817
15885
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
15818
- return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash15("sha256").update(content).digest("hex") };
15886
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
15819
15887
  } finally {
15820
15888
  closeSync9(fd);
15821
15889
  }
@@ -15826,7 +15894,7 @@ function unchanged2(before) {
15826
15894
  }
15827
15895
 
15828
15896
  // src/lib/agent-integration.ts
15829
- var sha3 = (data) => createHash16("sha256").update(data).digest("hex");
15897
+ var sha3 = (data) => createHash17("sha256").update(data).digest("hex");
15830
15898
  var ROOTS = NATIVE_SKILL_ROOTS;
15831
15899
  function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
15832
15900
  try {
@@ -16020,7 +16088,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
16020
16088
  }
16021
16089
  }
16022
16090
  function treeHash(root) {
16023
- const hash2 = createHash16("sha256");
16091
+ const hash2 = createHash17("sha256");
16024
16092
  let bytes = 0, files = 0;
16025
16093
  function visit2(path) {
16026
16094
  const stat = lstatSync19(path);
@@ -16514,7 +16582,7 @@ function planAgentIntegration(options) {
16514
16582
  const pluginPath = join21(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
16515
16583
  const pluginAfter = renderSumiPlugin(command, profileId2);
16516
16584
  const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
16517
- if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && pluginBefore === renderSumiPlugin(priorCommand, priorProfile)))
16585
+ if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && isManagedSumiPlugin(pluginBefore, priorCommand, priorProfile)))
16518
16586
  throw new Error("Refusing to overwrite a modified Sumi Skills plugin; preserve and review it first");
16519
16587
  if (pluginBefore !== pluginAfter)
16520
16588
  changes.push({ path: pluginPath, before: pluginBefore, after: pluginAfter });
@@ -16868,7 +16936,7 @@ function planAgentSettingsWitnessUpgrade(options) {
16868
16936
  const binding = policy.bridge?.discovery?.[options.agent];
16869
16937
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
16870
16938
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
16871
- const configPath = canonicalAgentPath(join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
16939
+ const configPath = canonicalAgentPath(options.agent === "sumi" ? sumiConfigPath(home) : join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
16872
16940
  const settings = readOptional(configPath);
16873
16941
  if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
16874
16942
  throw new Error("Native settings preimage changed");
@@ -16894,7 +16962,19 @@ function planAgentSettingsWitnessUpgrade(options) {
16894
16962
  replaced.push(source);
16895
16963
  }
16896
16964
  }
16897
- const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
16965
+ if (options.agent === "sumi") {
16966
+ const preimage = readSumiSettingsPreimage(options.reviewedPreimage);
16967
+ for (const source of binding.sources) {
16968
+ if (source === previous || source.path !== configPath)
16969
+ continue;
16970
+ if (source.hashMode !== undefined || source.format !== "json" || !isDeepStrictEqual6(source.fields, [...SUMI_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
16971
+ throw new Error("Sumi settings witness upgrade found an unrecognized configuration witness");
16972
+ if (projectNativeDiscoveryFields(preimage, "json", source.fields, configPath) !== source.sha256)
16973
+ throw new Error("Reviewed preimage does not explain the Sumi discovery projection witness");
16974
+ replaced.push(source);
16975
+ }
16976
+ }
16977
+ const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
16898
16978
  const rebound = [];
16899
16979
  for (const source of binding.sources) {
16900
16980
  if (!replaced.includes(source)) {
@@ -17255,7 +17335,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17255
17335
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
17256
17336
  assertHermesProtection(home, config, command, profile, supervisor);
17257
17337
  } else if (agent === "sumi") {
17258
- if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || readOptional(join21(sumiConfigDirectory(home), "plugins", "skills-cli.js")) !== renderSumiPlugin(command, profile))
17338
+ if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join21(sumiConfigDirectory(home), "plugins", "skills-cli.js")), command, profile))
17259
17339
  throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
17260
17340
  } else if (agent === "opencode") {
17261
17341
  if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join21(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
@@ -18614,7 +18694,7 @@ function exactProfileSelection(spec, profile) {
18614
18694
  }
18615
18695
 
18616
18696
  // src/lib/plugin-projection.ts
18617
- import { createHash as createHash17 } from "crypto";
18697
+ import { createHash as createHash18 } from "crypto";
18618
18698
  import { posix } from "path";
18619
18699
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
18620
18700
  var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -18625,7 +18705,7 @@ function pluginNeed(value, message) {
18625
18705
  if (!value)
18626
18706
  pluginRefusal(message);
18627
18707
  }
18628
- var pluginHash = (bytes) => createHash17("sha256").update(bytes).digest("hex");
18708
+ var pluginHash = (bytes) => createHash18("sha256").update(bytes).digest("hex");
18629
18709
  function pluginObject(value) {
18630
18710
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
18631
18711
  }
@@ -18813,7 +18893,7 @@ function buildPluginProjection(entries) {
18813
18893
  }
18814
18894
 
18815
18895
  // src/lib/plugin-projection-store.ts
18816
- import { createHash as createHash18, randomUUID as randomUUID4 } from "crypto";
18896
+ import { createHash as createHash19, randomUUID as randomUUID4 } from "crypto";
18817
18897
  import { constants as constants11, closeSync as closeSync11, fchmodSync, fstatSync as fstatSync11, fsyncSync as fsyncSync5, linkSync as linkSync3, lstatSync as lstatSync21, mkdirSync as mkdirSync9, openSync as openSync11, opendirSync as opendirSync4, readSync as readSync9, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "fs";
18818
18898
  import { dirname as dirname16, isAbsolute as isAbsolute14, join as join28, parse as parse5, resolve as resolve14 } from "path";
18819
18899
  var READ_FLAGS = constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
@@ -19155,7 +19235,7 @@ function pluginExecutableDigest(path) {
19155
19235
  const opened = fstatSync11(fd, { bigint: true });
19156
19236
  same3(before, opened);
19157
19237
  fileShape(opened, maximum, "executable");
19158
- const hash2 = createHash18("sha256"), buffer = new Uint8Array(64 * 1024);
19238
+ const hash2 = createHash19("sha256"), buffer = new Uint8Array(64 * 1024);
19159
19239
  let remaining = Number(opened.size);
19160
19240
  while (remaining > 0) {
19161
19241
  const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
@@ -19714,7 +19794,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
19714
19794
  }
19715
19795
 
19716
19796
  // src/lib/claude-marketplace-registry.ts
19717
- import { createHash as createHash19 } from "crypto";
19797
+ import { createHash as createHash20 } from "crypto";
19718
19798
  import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync23, openSync as openSync13, readSync as readSync11 } from "fs";
19719
19799
  import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute17, resolve as resolve17 } from "path";
19720
19800
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
@@ -19736,13 +19816,13 @@ function parents2(path) {
19736
19816
  return result;
19737
19817
  }
19738
19818
  }
19739
- function readRegistry2(path, budget) {
19819
+ function readRegistry2(path, budget2) {
19740
19820
  absolutePath3(path);
19741
19821
  need6(basename9(path) === "known_marketplaces.json", "requires known_marketplaces.json");
19742
- need6(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
19822
+ need6(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
19743
19823
  const ancestors2 = parents2(path), initial = lstatSync23(path, { bigint: true });
19744
19824
  need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
19745
- need6(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
19825
+ need6(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
19746
19826
  const fd = openSync13(path, constants13.O_RDONLY | constants13.O_NOFOLLOW | constants13.O_NONBLOCK);
19747
19827
  try {
19748
19828
  const opened = fstatSync13(fd, { bigint: true });
@@ -19761,7 +19841,7 @@ function readRegistry2(path, budget) {
19761
19841
  const now = lstatSync23(ancestor, { bigint: true });
19762
19842
  need6(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
19763
19843
  }
19764
- budget.remaining -= length;
19844
+ budget2.remaining -= length;
19765
19845
  try {
19766
19846
  return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
19767
19847
  } catch {
@@ -19879,23 +19959,23 @@ function normalizeRow(row, version = 1) {
19879
19959
  need6(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
19880
19960
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
19881
19961
  }
19882
- function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
19883
- const value = parse6(readRegistry2(path, budget));
19962
+ function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
19963
+ const value = parse6(readRegistry2(path, budget2));
19884
19964
  for (const [, row] of value.entries)
19885
19965
  normalizeRow(row);
19886
- const sha2562 = createHash19("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
19966
+ const sha2562 = createHash20("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
19887
19967
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
19888
19968
  }
19889
- function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
19890
- const value = parse6(readRegistry2(path, budget));
19969
+ function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
19970
+ const value = parse6(readRegistry2(path, budget2));
19891
19971
  for (const [, row] of value.entries)
19892
19972
  normalizeRow(row, 2);
19893
- const sha2562 = createHash19("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
19973
+ const sha2562 = createHash20("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
19894
19974
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
19895
19975
  }
19896
19976
 
19897
19977
  // src/lib/codex-hook-discovery-recovery.ts
19898
- import { createHash as createHash20 } from "crypto";
19978
+ import { createHash as createHash21 } from "crypto";
19899
19979
 
19900
19980
  // src/lib/codex-hook-additions-review.ts
19901
19981
  var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
@@ -19903,7 +19983,7 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
19903
19983
  var proofs = new WeakMap;
19904
19984
 
19905
19985
  // src/lib/codex-hook-discovery-recovery.ts
19906
- var sha4 = (text2) => createHash20("sha256").update(text2).digest("hex");
19986
+ var sha4 = (text2) => createHash21("sha256").update(text2).digest("hex");
19907
19987
  var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
19908
19988
  var proofs2 = new WeakMap;
19909
19989
  function assertCodexHookDiscoveryRecovery(binding, witness) {
@@ -19922,7 +20002,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
19922
20002
  }
19923
20003
 
19924
20004
  // src/lib/agent-discovery.ts
19925
- var digest = (text2) => createHash21("sha256").update(text2).digest("hex");
20005
+ var digest = (text2) => createHash22("sha256").update(text2).digest("hex");
19926
20006
  function parseConfig(text2, path, toml = false) {
19927
20007
  try {
19928
20008
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -19960,33 +20040,45 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
19960
20040
  throw new Error("Expected native discovery configuration object");
19961
20041
  return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object6[field2] ?? null]))));
19962
20042
  }
19963
- function isSupersededCodexProjection(agent, configPath, source) {
20043
+ function isSupersededSettingsProjection(agent, configPath, source) {
20044
+ if (agent === "sumi")
20045
+ return source.path === configPath && source.format === "json" && isDeepStrictEqual7(source.fields, [...SUMI_DISCOVERY_PROJECTION_FIELDS]);
19964
20046
  return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual7(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
19965
20047
  }
19966
- function supersedesCodexProjection(sources, configPath) {
19967
- return sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
20048
+ function supersedesSettingsProjection(agent, sources, configPath) {
20049
+ if (agent === "sumi")
20050
+ return sources.some((source) => source.path === configPath && source.hashMode === "sumi-settings-v1");
20051
+ return agent === "codex" && sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
19968
20052
  }
19969
- function projected(source, changes, budget = discoveryByteBudget()) {
20053
+ function projected(source, changes, budget2 = discoveryByteBudget()) {
20054
+ if (source.hashMode === "sumi-settings-v1") {
20055
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
20056
+ throw new Error("Sumi settings witnesses require exact typed metadata");
20057
+ const current = captureSumiSettings(source.path, budget2).sha256;
20058
+ if (current !== source.sha256)
20059
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
20060
+ return changes?.has(source.path) ? hashSumiSettingsReplacement(changes.get(source.path), budget2) : current;
20061
+ }
19970
20062
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) {
19971
20063
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
19972
20064
  throw new Error("Codex settings witnesses require exact typed metadata");
19973
- const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
20065
+ const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget2).sha256;
19974
20066
  if (current !== source.sha256)
19975
20067
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
19976
- return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
20068
+ return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
19977
20069
  }
19978
20070
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
19979
20071
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
19980
20072
  throw new Error("Claude settings witnesses require exact typed metadata");
19981
- const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget).sha256;
20073
+ const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
19982
20074
  if (current !== source.sha256)
19983
20075
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
19984
- return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget) : current;
20076
+ return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
19985
20077
  }
19986
20078
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
19987
20079
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
19988
20080
  throw new Error("Claude marketplace witnesses require an exact reviewed registry without synthetic changes");
19989
- const sha2562 = (source.hashMode === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : captureClaudeMarketplaceRegistry)(source.path, budget).sha256;
20081
+ const sha2562 = (source.hashMode === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : captureClaudeMarketplaceRegistry)(source.path, budget2).sha256;
19990
20082
  if (sha2562 !== source.sha256)
19991
20083
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
19992
20084
  return sha2562;
@@ -20004,8 +20096,8 @@ function projected(source, changes, budget = discoveryByteBudget()) {
20004
20096
  if (source.format !== undefined || source.fields !== undefined)
20005
20097
  throw new Error("Raw discovery witnesses cannot project configuration fields");
20006
20098
  if (source.hashMode === "path-bytes")
20007
- return hashDiscoveryPathFile(source.path, budget, changes);
20008
- return hashRawDiscoveryFile(source.path, budget, changes);
20099
+ return hashDiscoveryPathFile(source.path, budget2, changes);
20100
+ return hashRawDiscoveryFile(source.path, budget2, changes);
20009
20101
  }
20010
20102
  const text2 = read2(source.path, changes);
20011
20103
  if (text2 === null)
@@ -20153,7 +20245,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
20153
20245
  verifyDiscoveryDirectories(projected2, installationRoots);
20154
20246
  }
20155
20247
  const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root2) => !lstatSync24(root2, { throwIfNoEntry: false }))];
20156
- const budget = discoveryByteBudget();
20248
+ const budget2 = discoveryByteBudget();
20157
20249
  for (const source of binding.sources) {
20158
20250
  assertMarketplaceBinding(binding, source);
20159
20251
  if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
@@ -20170,7 +20262,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
20170
20262
  safe4(source.path);
20171
20263
  continue;
20172
20264
  }
20173
- const current = projected(source, undefined, budget);
20265
+ const current = projected(source, undefined, budget2);
20174
20266
  if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
20175
20267
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
20176
20268
  }
@@ -20204,6 +20296,8 @@ function codexInstallationRoots(binding) {
20204
20296
  });
20205
20297
  }
20206
20298
  function assertMarketplaceBinding(binding, source) {
20299
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
20300
+ throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
20207
20301
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
20208
20302
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
20209
20303
  if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
@@ -20212,13 +20306,13 @@ function assertMarketplaceBinding(binding, source) {
20212
20306
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
20213
20307
  }
20214
20308
  function rebindAgentDiscovery(binding, changes) {
20215
- const budget = discoveryByteBudget();
20309
+ const budget2 = discoveryByteBudget();
20216
20310
  const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync24(root2, { throwIfNoEntry: false }))];
20217
20311
  const rebound = { ...binding, sources: binding.sources.map((source) => {
20218
20312
  assertMarketplaceBinding(binding, source);
20219
20313
  if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep7)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep7)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)))
20220
20314
  return source;
20221
- const sha2562 = projected(source, changes, budget);
20315
+ const sha2562 = projected(source, changes, budget2);
20222
20316
  if (!changes.has(source.path) && sha2562 !== source.sha256)
20223
20317
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
20224
20318
  return { ...source, sha256: sha2562 };
@@ -20233,14 +20327,14 @@ function rebindAgentDiscovery(binding, changes) {
20233
20327
  function captureDiscoveryByteSources(paths) {
20234
20328
  if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
20235
20329
  throw new Error("Invalid raw discovery source collection");
20236
- const budget = discoveryByteBudget();
20237
- return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path, budget) }));
20330
+ const budget2 = discoveryByteBudget();
20331
+ return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path, budget2) }));
20238
20332
  }
20239
20333
  function captureDiscoveryPathSources(paths) {
20240
20334
  if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
20241
20335
  throw new Error("Invalid path discovery source collection");
20242
- const budget = discoveryByteBudget();
20243
- return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
20336
+ const budget2 = discoveryByteBudget();
20337
+ return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
20244
20338
  }
20245
20339
  function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18, reviewed) {
20246
20340
  const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
@@ -20308,7 +20402,7 @@ function resolveAgentDiscovery(options) {
20308
20402
  if (agent === "hermes")
20309
20403
  assertHermesEnvironment(home);
20310
20404
  const configPath = agentDiscoveryConfigPath(home, agent);
20311
- const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["skills", "plugin", "plugins", "permissions"] : ["version"]);
20405
+ const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? [...SUMI_DISCOVERY_PROJECTION_FIELDS] : ["version"]);
20312
20406
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
20313
20407
  const unresolved = (detail) => {
20314
20408
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
@@ -20405,9 +20499,9 @@ function resolveAgentDiscovery(options) {
20405
20499
  verifyAgentDiscovery(retained);
20406
20500
  if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
20407
20501
  unresolved("retained review is missing the full agent configuration source");
20408
- if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
20502
+ if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
20409
20503
  unresolved("retained settings witness names another configuration source");
20410
- const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
20504
+ const covered = supersedesSettingsProjection(agent, retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical6(configPath), source)) : sources;
20411
20505
  if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
20412
20506
  unresolved("retained review is missing current runtime discovery coverage");
20413
20507
  return retained;
@@ -20422,11 +20516,11 @@ function resolveAgentDiscovery(options) {
20422
20516
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
20423
20517
  throw new Error("Explicit discovery reviews require full source-file hashes");
20424
20518
  verifyAgentDiscovery(supplied);
20425
- if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
20426
- throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
20519
+ if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
20520
+ throw new Error("Semantic settings witnesses must name the configured agent configuration source");
20427
20521
  if (!review.sources.some((source) => source.path === canonical6(configPath)))
20428
20522
  throw new Error("Discovery review must include the agent configuration source");
20429
- const reviewedSources = supersedesCodexProjection(review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
20523
+ const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical6(configPath), source)) : sources;
20430
20524
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
20431
20525
  }
20432
20526
  function plugin(root2) {
@@ -21388,7 +21482,7 @@ function getAllTags(catalog) {
21388
21482
  return Array.from(tagSet).sort();
21389
21483
  }
21390
21484
  // src/lib/run-state.ts
21391
- import { createHash as createHash22, randomBytes } from "crypto";
21485
+ import { createHash as createHash23, randomBytes } from "crypto";
21392
21486
  import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync21, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
21393
21487
  import { extname, join as join35, relative as relative8 } from "path";
21394
21488
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -21515,7 +21609,7 @@ function collectRunArtifacts(context) {
21515
21609
  artifacts.push({
21516
21610
  path: toProjectRelative(context.targetDir, path),
21517
21611
  mime: mimeForPath(path),
21518
- sha256: createHash22("sha256").update(bytes).digest("hex"),
21612
+ sha256: createHash23("sha256").update(bytes).digest("hex"),
21519
21613
  sizeBytes: stat2.size
21520
21614
  });
21521
21615
  }
@@ -40934,7 +41028,7 @@ function clone2(value) {
40934
41028
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
40935
41029
  }
40936
41030
  // src/lib/canonical-json.ts
40937
- import { createHash as createHash23 } from "crypto";
41031
+ import { createHash as createHash24 } from "crypto";
40938
41032
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
40939
41033
  return canonicalJsonAtDepth(value, maximumBytes, 64);
40940
41034
  }
@@ -40978,7 +41072,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
40978
41072
  return visit3(value, 0);
40979
41073
  }
40980
41074
  function canonicalJsonSha256(value, maximumBytes) {
40981
- return createHash23("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41075
+ return createHash24("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
40982
41076
  }
40983
41077
 
40984
41078
  // src/lib/remote-recurring-contract.ts
@@ -41165,7 +41259,7 @@ function recurringRequest(action, input2) {
41165
41259
  }
41166
41260
  return { path, read: read3, method: read3 ? "GET" : "POST", body: read3 ? undefined : JSON.stringify(action === "preview" ? input2 : action === "activate" ? row.approval : { contractVersion: 1 }) };
41167
41261
  }
41168
- var budget = exports_external.object({ reservedCredits: integer2, settledCredits: integer2, admittedOccurrences: integer2 });
41262
+ var budget2 = exports_external.object({ reservedCredits: integer2, settledCredits: integer2, admittedOccurrences: integer2 });
41169
41263
  var period = exports_external.object({ startsAt: instant2, endsAt: instant2 });
41170
41264
  var preview = exports_external.object({
41171
41265
  contractVersion: exports_external.literal(1),
@@ -41187,8 +41281,8 @@ var consent = exports_external.object({
41187
41281
  state: exports_external.enum(["active", "revoked", "expired"]),
41188
41282
  activatedAt: instant2,
41189
41283
  revokedAt: instant2.nullable(),
41190
- total: budget,
41191
- currentPeriod: budget.extend(period.shape).nullable(),
41284
+ total: budget2,
41285
+ currentPeriod: budget2.extend(period.shape).nullable(),
41192
41286
  nextDueAt: instant2.nullable()
41193
41287
  });
41194
41288
  var refusal2 = exports_external.enum([
@@ -43245,7 +43339,7 @@ function recordScheduleRun(id, status, targetDir) {
43245
43339
  saveSchedules(data, targetDir);
43246
43340
  }
43247
43341
  // src/lib/revision.ts
43248
- import { createHash as createHash24 } from "crypto";
43342
+ import { createHash as createHash25 } from "crypto";
43249
43343
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
43250
43344
  function revisionIdOf(content) {
43251
43345
  const canonicalContent = {
@@ -43267,7 +43361,7 @@ function revisionIdOf(content) {
43267
43361
  canonicalContent.replacementSlug = content.replacementSlug ?? null;
43268
43362
  }
43269
43363
  const canonical6 = JSON.stringify(canonicalContent);
43270
- return createHash24("sha256").update(canonical6).digest("hex");
43364
+ return createHash25("sha256").update(canonical6).digest("hex");
43271
43365
  }
43272
43366
  function revisionIdOfRecord(record8) {
43273
43367
  return revisionIdOf(record8);
@@ -43808,7 +43902,7 @@ async function requestInvitationEmail(origin, action, input2) {
43808
43902
  }
43809
43903
 
43810
43904
  // src/lib/remote-private-publications.ts
43811
- import { createHash as createHash25 } from "crypto";
43905
+ import { createHash as createHash26 } from "crypto";
43812
43906
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
43813
43907
  var failures2 = {
43814
43908
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -43855,7 +43949,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
43855
43949
  var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
43856
43950
  var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
43857
43951
  var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
43858
- var publicationSha256 = (bytes) => createHash25("sha256").update(bytes).digest("hex");
43952
+ var publicationSha256 = (bytes) => createHash26("sha256").update(bytes).digest("hex");
43859
43953
  function checkedPublicationDeclaration(value) {
43860
43954
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
43861
43955
  return bad();
@@ -43875,7 +43969,7 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
43875
43969
  return invalid4();
43876
43970
  return Object.freeze({ ...value });
43877
43971
  }
43878
- async function boundedJson(url2, init, token, mutation, budget2 = 15000, signal) {
43972
+ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal) {
43879
43973
  const controller = new AbortController;
43880
43974
  let reader;
43881
43975
  let response;
@@ -43930,7 +44024,7 @@ async function boundedJson(url2, init, token, mutation, budget2 = 15000, signal)
43930
44024
  controller.abort();
43931
44025
  reject(timeout);
43932
44026
  };
43933
- timer = setTimeout(abort, Math.max(1, Math.min(15000, budget2)));
44027
+ timer = setTimeout(abort, Math.max(1, Math.min(15000, budget3)));
43934
44028
  signal?.addEventListener("abort", abort, { once: true });
43935
44029
  })]);
43936
44030
  } catch (error61) {
@@ -45721,7 +45815,7 @@ function isApiMode(env = process.env) {
45721
45815
  }
45722
45816
  }
45723
45817
  // src/lib/native-storage.ts
45724
- import { createHash as createHash26, createHmac as createHmac2 } from "crypto";
45818
+ import { createHash as createHash27, createHmac as createHmac2 } from "crypto";
45725
45819
  import {
45726
45820
  existsSync as existsSync20,
45727
45821
  mkdirSync as mkdirSync16,
@@ -45897,7 +45991,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
45897
45991
  files.push({
45898
45992
  path: relativePath,
45899
45993
  sizeBytes: bytes.byteLength,
45900
- sha256: createHash26("sha256").update(bytes).digest("hex"),
45994
+ sha256: createHash27("sha256").update(bytes).digest("hex"),
45901
45995
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
45902
45996
  });
45903
45997
  }
@@ -45922,7 +46016,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
45922
46016
  continue;
45923
46017
  }
45924
46018
  const bytes = Buffer.from(file2.contentBase64, "base64");
45925
- const hash4 = createHash26("sha256").update(bytes).digest("hex");
46019
+ const hash4 = createHash27("sha256").update(bytes).digest("hex");
45926
46020
  if (hash4 !== file2.sha256) {
45927
46021
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
45928
46022
  }
@@ -46254,7 +46348,7 @@ function toIsoString(value) {
46254
46348
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
46255
46349
  }
46256
46350
  function sha256Hex2(value) {
46257
- return createHash26("sha256").update(value).digest("hex");
46351
+ return createHash27("sha256").update(value).digest("hex");
46258
46352
  }
46259
46353
  function normalizeHeaders(headers) {
46260
46354
  const result = {};
@@ -46306,7 +46400,7 @@ function toArrayBuffer(bytes) {
46306
46400
  return buffer;
46307
46401
  }
46308
46402
  // src/lib/station-snapshot.ts
46309
- import { createHash as createHash27 } from "crypto";
46403
+ import { createHash as createHash28 } from "crypto";
46310
46404
  import {
46311
46405
  copyFileSync,
46312
46406
  mkdirSync as mkdirSync17,
@@ -46481,7 +46575,7 @@ function validateStationId(stationId) {
46481
46575
  }
46482
46576
  }
46483
46577
  function sha256File(filePath) {
46484
- return createHash27("sha256").update(readFileSync27(filePath)).digest("hex");
46578
+ return createHash28("sha256").update(readFileSync27(filePath)).digest("hex");
46485
46579
  }
46486
46580
  function scanHome(definition, homesRoot) {
46487
46581
  const homePath = homePathFor(definition, homesRoot);
@@ -46632,7 +46726,7 @@ function writeStationSnapshot(options) {
46632
46726
  };
46633
46727
  }
46634
46728
  // src/lib/station-hydrate.ts
46635
- import { createHash as createHash28 } from "crypto";
46729
+ import { createHash as createHash29 } from "crypto";
46636
46730
  import {
46637
46731
  copyFileSync as copyFileSync2,
46638
46732
  mkdirSync as mkdirSync18,
@@ -46838,7 +46932,7 @@ function skillSha256(skill) {
46838
46932
  return sha256File(skill.files[0].winner.fullPath);
46839
46933
  }
46840
46934
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
46841
- return createHash28("sha256").update(joined.sort().join(`
46935
+ return createHash29("sha256").update(joined.sort().join(`
46842
46936
  `)).digest("hex");
46843
46937
  }
46844
46938
  function writeStationHydration(options) {
@@ -47166,7 +47260,7 @@ async function inspectLocked(client2, directory2, cancel) {
47166
47260
  return privatePublicationResult(directory2, receipt);
47167
47261
  }
47168
47262
  // src/lib/skill-context.ts
47169
- import { createHash as createHash29 } from "crypto";
47263
+ import { createHash as createHash30 } from "crypto";
47170
47264
  import { resolve as resolve24 } from "path";
47171
47265
  function words(value) {
47172
47266
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
@@ -47259,7 +47353,7 @@ ${content}`;
47259
47353
  `);
47260
47354
  const receipt = {
47261
47355
  schemaVersion: 1,
47262
- id: createHash29("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
47356
+ id: createHash30("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
47263
47357
  profileId: profile.profileId,
47264
47358
  profileRevision: profile.profileRevision,
47265
47359
  authority: profile.authority,
@@ -47275,9 +47369,9 @@ ${content}`;
47275
47369
  return { context: context2, selections, omitted, receipt };
47276
47370
  }
47277
47371
  // src/lib/session-reconciliation.ts
47278
- import { createHash as createHash30 } from "crypto";
47372
+ import { createHash as createHash31 } from "crypto";
47279
47373
  var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
47280
- var digest3 = (value) => createHash30("sha256").update(JSON.stringify(value)).digest("hex");
47374
+ var digest3 = (value) => createHash31("sha256").update(JSON.stringify(value)).digest("hex");
47281
47375
  function exactTimestamp(value) {
47282
47376
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
47283
47377
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -47391,6 +47485,7 @@ export {
47391
47485
  validateCron,
47392
47486
  validateBlogArticleRunOptions,
47393
47487
  uploadSkillsSnapshotFilesToS3,
47488
+ upgradeSumiSettingsWitness,
47394
47489
  upgradeCodexSettingsWitnessV4,
47395
47490
  upgradeCodexSettingsWitnessV3,
47396
47491
  upgradeCodexSettingsWitness,
@@ -47575,6 +47670,7 @@ export {
47575
47670
  computeContentHash,
47576
47671
  completeSkillRun,
47577
47672
  clearRegistryCache,
47673
+ captureSumiSettings,
47578
47674
  captureManagedPluginRegistry,
47579
47675
  captureDiscoveryPathSources,
47580
47676
  captureDiscoveryDirectories,