@hasna/skills 0.9.14 → 0.9.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -36868,7 +36868,7 @@ var package_default;
36868
36868
  var init_package = __esm(() => {
36869
36869
  package_default = {
36870
36870
  name: "@hasna/skills",
36871
- version: "0.9.14",
36871
+ version: "0.9.16",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -51146,7 +51146,15 @@ function planAgentIntegration(options) {
51146
51146
  validateProfile(profileId2);
51147
51147
  return [agent, { command, profileId: profileId2 }];
51148
51148
  }));
51149
- const discoveries = [...new Set(options.agents)].map((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }));
51149
+ const retainedAgents = [];
51150
+ const discoveries = [...new Set(options.agents)].map((agent) => {
51151
+ const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
51152
+ if (retainedReview) {
51153
+ assertManagedAgentBridge(agent, { home, dataDir: dataDir3, projectDir: options.projectDir ?? home });
51154
+ retainedAgents.push(agent);
51155
+ }
51156
+ return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
51157
+ });
51150
51158
  const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases });
51151
51159
  const changes = [];
51152
51160
  for (const agent of [...new Set(options.agents)]) {
@@ -51231,7 +51239,7 @@ function planAgentIntegration(options) {
51231
51239
  if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
51232
51240
  changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
51233
51241
  recheckRootAliases(aliases);
51234
- return { dataDir: dataDir3, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
51242
+ return { dataDir: dataDir3, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
51235
51243
  }
51236
51244
  function verifyCoordinatedDiscovery(binding, home, aliases) {
51237
51245
  try {
@@ -51269,6 +51277,8 @@ function applyAgentIntegration(plan) {
51269
51277
  parseManagedSkillPolicy(change.after);
51270
51278
  const aliases = plan.rootAliases ?? [];
51271
51279
  recheckRootAliases(aliases);
51280
+ for (const agent of plan.retainedReviewChecks?.agents ?? [])
51281
+ assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
51272
51282
  for (const agent of plan.managedAgentChecks?.agents ?? [])
51273
51283
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
51274
51284
  for (const binding of plan.discoveryBefore ?? []) {
@@ -54696,39 +54706,132 @@ var fail2 = () => new Error('CODEX_HOOK_TRUST_UNSUPPORTED_LAYOUT: preserve and c
54696
54706
  };
54697
54707
  var init_codex_hook_trust_layout = () => {};
54698
54708
 
54709
+ // src/lib/codex-hook-additions-review.ts
54710
+ import { isAbsolute as isAbsolute14 } from "path";
54711
+ import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
54712
+ function exactKeys2(value, keys2) {
54713
+ need4(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual5(Object.keys(value).sort(), [...keys2].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
54714
+ }
54715
+ function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
54716
+ const captured = proofs.get(proof);
54717
+ need4(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual5(captured.journalHooks, journalHooks.map(({ key, currentHash }) => ({ key, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
54718
+ captured.recheck();
54719
+ unchanged2(before2);
54720
+ unchanged2(current);
54721
+ return captured.states;
54722
+ }
54723
+ function reviewNativeHookAdditions(options) {
54724
+ need4(isAbsolute14(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
54725
+ const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
54726
+ exactKeys2(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
54727
+ need4((review.version === 1 || review.version === 2) && review.kind === "native-hook-additions" && text2(review.reason) && text2(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
54728
+ for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
54729
+ need4(hash2(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
54730
+ exactKeys2(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
54731
+ const before2 = review.producerBefore;
54732
+ need4(isAbsolute14(before2.configPath) && isAbsolute14(before2.hooksPath) && hash2(before2.configSha256) && hash2(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
54733
+ const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
54734
+ need4(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
54735
+ const original = structure(options.configBefore.text), producer = structure(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
54736
+ expectedProducer.hooks ??= {};
54737
+ expectedProducer.hooks.state ??= {};
54738
+ for (const hook of options.journalHooks)
54739
+ expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
54740
+ need4(isDeepStrictEqual5(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
54741
+ need4(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
54742
+ const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
54743
+ need4(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
54744
+ const added = [], states = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
54745
+ for (const addition of review.additions) {
54746
+ exactKeys2(addition, review.version === 1 ? ["event", "nativeHook"] : ["event", "nativeHook", "trustState"]);
54747
+ need4(Object.hasOwn(review.version === 1 ? events : matchedEvents, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
54748
+ const { event, nativeHook: hook } = addition;
54749
+ exactKeys2(hook, nativeKeys);
54750
+ const groups = expectedHooks.hooks[event] ?? [];
54751
+ need4(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
54752
+ const key = `${options.nativeHooksPath}:${matchedEvents[event][1]}:${groups.length}:0`;
54753
+ need4(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
54754
+ need4(hook.eventName === matchedEvents[event][0] && hook.handlerType === "command" && (review.version === 2 ? v2Command(hook.command) : text2(hook.command, 16384)) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && (event === "PreToolUse" ? text2(hook.matcher) : hook.matcher === null) && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && hook.sourcePath === options.nativeHooksPath && hook.source === "user" && hook.pluginId === null && hook.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash) && hook.enabled === true && hook.trustStatus === "trusted", "RECONCILE_ADDITIONS_IDENTITY");
54755
+ let trustState = { enabled: true, trusted_hash: hook.currentHash };
54756
+ if (review.version === 2) {
54757
+ const state = addition.trustState;
54758
+ exactKeys2(state, state && Object.hasOwn(state, "enabled") ? ["enabled", "trusted_hash"] : ["trusted_hash"]);
54759
+ need4(state.trusted_hash === hook.currentHash && (!Object.hasOwn(state, "enabled") || state.enabled === true), "RECONCILE_ADDITIONS_TRUST_STATE");
54760
+ trustState = { ...state };
54761
+ }
54762
+ seen.add(key);
54763
+ commands.add(hook.command);
54764
+ added.push(hook);
54765
+ states.push(Object.freeze({ key, state: Object.freeze(trustState) }));
54766
+ expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
54767
+ expectedConfig.hooks.state[key] = trustState;
54768
+ }
54769
+ need4(isDeepStrictEqual5(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
54770
+ const keys2 = added.map((hook) => hook.key);
54771
+ need4(isDeepStrictEqual5(expectedConfig, structure(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys2) === codexTrustReconcileWitness(options.configCurrent.text, keys2) && codexTrustUnmanagedSemantic(producerConfig.text, keys2) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys2), "RECONCILE_ADDITIONS_CONFIG");
54772
+ const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
54773
+ const recheck = () => {
54774
+ for (const input of inputs)
54775
+ unchanged2(input);
54776
+ };
54777
+ recheck();
54778
+ const trustProof = Object.freeze({ reviewSha256: reviewFile.sha256 });
54779
+ proofs.set(trustProof, { before: options.configBefore, current: options.configCurrent, journalHooks: options.journalHooks.map(({ key, currentHash }) => ({ key, currentHash })), states: Object.freeze(states), recheck });
54780
+ return { hooks: added, recheck, trustProof, receipt: { ...review.version === 2 ? { version: 2 } : {}, reviewPath: reviewFile.file, reviewSha256: reviewFile.sha256, reason: review.reason, producerEvidence: review.producerEvidence, producerBefore: { configPath: producerConfig.file, configSha256: producerConfig.sha256, hooksPath: producerHooks.file, hooksSha256: producerHooks.sha256 }, additions: review.version === 1 ? added : review.additions } };
54781
+ }
54782
+ var events, matchedEvents, hash2 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), text2 = (value, max2 = 2048) => typeof value === "string" && value.trim().length > 0 && value.length <= max2 && !/[\0\r\n]/.test(value), v2Command = (value) => typeof value === "string" && value.trim().length > 0 && value.length <= 16384 && !/[\0\r]/.test(value), structure = (value) => JSON.parse(JSON.stringify(Bun.TOML.parse(value))), nativeKeys, proofs;
54783
+ var init_codex_hook_additions_review = __esm(() => {
54784
+ init_codex_hook_trust_files();
54785
+ init_codex_hook_trust_layout();
54786
+ events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
54787
+ matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
54788
+ nativeKeys = ["key", "eventName", "handlerType", "command", "matcher", "timeoutSec", "async", "statusMessage", "additionalContextLimit", "sourcePath", "source", "pluginId", "isManaged", "currentHash", "enabled", "trustStatus"];
54789
+ proofs = new WeakMap;
54790
+ });
54791
+
54699
54792
  // src/lib/codex-hook-discovery-recovery.ts
54700
54793
  import { createHash as createHash15 } from "crypto";
54701
- import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
54702
- function createCodexHookDiscoveryRecovery(binding, before2, current, hooks) {
54794
+ import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
54795
+ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, additions) {
54796
+ const journalHooks = hooks.map(({ key, currentHash }) => ({ key, currentHash }));
54797
+ const additionStates = additions ? readReviewedAdditionTrustStates(additions, before2, current, journalHooks) : [];
54798
+ const recheckAdditions = () => {
54799
+ if (additions)
54800
+ readReviewedAdditionTrustStates(additions, before2, current, journalHooks);
54801
+ };
54703
54802
  const raw = binding.sources.filter((source) => source.path === current.file && source.hashMode === "bytes");
54704
54803
  if (!raw.some((source) => source.sha256 !== current.sha256))
54705
54804
  return;
54706
54805
  need4(binding.agent === "codex" && binding.method === "reviewed" && raw.length === 1 && raw[0].sha256 === before2.sha256 && before2.sha256 !== current.sha256, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
54707
54806
  need4(raw[0].format === undefined && raw[0].fields === undefined && raw[0].managedPlugins === undefined, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
54708
54807
  const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
54709
- need4(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual5(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
54710
- const original = structure(before2.text), actual = structure(current.text);
54808
+ need4(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual6(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
54809
+ const original = structure2(before2.text), actual = structure2(current.text);
54711
54810
  const project = (value) => Object.fromEntries(fields.map((field2) => [field2, value[field2] ?? null]));
54712
54811
  const projection = sha2(JSON.stringify(project(original)));
54713
- need4(typed[0].sha256 === projection && sha2(JSON.stringify(project(actual))) === projection && isDeepStrictEqual5(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
54714
- need4(hooks.length > 0 && hooks.length <= 100 && new Set(hooks.map((hook) => hook.key)).size === hooks.length && hooks.every((hook) => typeof hook.key === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash)), "RECONCILE_DISCOVERY_TRUST_WITNESS");
54715
- const keys2 = hooks.map((hook) => hook.key), expected = structuredClone(original);
54812
+ need4(typed[0].sha256 === projection && sha2(JSON.stringify(project(actual))) === projection && isDeepStrictEqual6(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
54813
+ const keys2 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
54814
+ need4(journalHooks.length > 0 && keys2.length <= 100 && new Set(keys2).size === keys2.length && journalHooks.every((hook) => typeof hook.key === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash)), "RECONCILE_DISCOVERY_TRUST_WITNESS");
54815
+ const expected = structuredClone(original);
54716
54816
  expected.hooks ??= {};
54717
54817
  expected.hooks.state ??= {};
54718
- for (const hook of hooks)
54818
+ for (const hook of journalHooks)
54719
54819
  expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
54720
- need4(isDeepStrictEqual5(expected, actual) && codexTrustReconcileWitness(before2.text, keys2) === codexTrustReconcileWitness(current.text, keys2) && codexTrustUnmanagedSemantic(before2.text, keys2) === codexTrustUnmanagedSemantic(current.text, keys2), "RECONCILE_DISCOVERY_TRUST_WITNESS");
54820
+ for (const { key, state } of additionStates)
54821
+ expected.hooks.state[key] = { ...state };
54822
+ need4(isDeepStrictEqual6(expected, actual) && codexTrustReconcileWitness(before2.text, keys2) === codexTrustReconcileWitness(current.text, keys2) && codexTrustUnmanagedSemantic(before2.text, keys2) === codexTrustUnmanagedSemantic(current.text, keys2), "RECONCILE_DISCOVERY_TRUST_WITNESS");
54721
54823
  unchanged2(before2);
54722
54824
  unchanged2(current);
54723
54825
  const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256: sha2(JSON.stringify(binding)), typedProjectionSha256: projection });
54724
- proofs.set(witness, { before: before2, current });
54826
+ proofs2.set(witness, { before: before2, current, recheckAdditions });
54725
54827
  return witness;
54726
54828
  }
54727
54829
  function assertCodexHookDiscoveryRecovery(binding, witness) {
54728
- const proof = proofs.get(witness);
54830
+ const proof = proofs2.get(witness);
54729
54831
  need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
54730
54832
  unchanged2(proof.before);
54731
54833
  unchanged2(proof.current);
54834
+ proof.recheckAdditions();
54732
54835
  }
54733
54836
  function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
54734
54837
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
@@ -54737,21 +54840,23 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
54737
54840
  need4(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
54738
54841
  return true;
54739
54842
  }
54740
- var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex"), fields, structure = (text2) => JSON.parse(JSON.stringify(Bun.TOML.parse(text2))), proofs;
54843
+ var sha2 = (text3) => createHash15("sha256").update(text3).digest("hex"), fields, structure2 = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), proofs2;
54741
54844
  var init_codex_hook_discovery_recovery = __esm(() => {
54742
54845
  init_codex_hook_trust_files();
54743
54846
  init_codex_hook_trust_layout();
54847
+ init_codex_hook_additions_review();
54744
54848
  fields = ["plugins", "marketplaces", "skills"];
54745
- proofs = new WeakMap;
54849
+ proofs2 = new WeakMap;
54746
54850
  });
54747
54851
 
54748
54852
  // src/lib/agent-discovery.ts
54749
54853
  import { createHash as createHash16 } from "crypto";
54750
54854
  import { lstatSync as lstatSync20, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync4 } from "fs";
54751
- import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute14, join as join31, resolve as resolve15, sep as sep5 } from "path";
54752
- function parseConfig(text2, path, toml = false) {
54855
+ import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, join as join31, resolve as resolve15, sep as sep5 } from "path";
54856
+ import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
54857
+ function parseConfig(text3, path, toml = false) {
54753
54858
  try {
54754
- const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
54859
+ const value = toml ? Bun.TOML.parse(text3) : JSON.parse(text3);
54755
54860
  if (!value || typeof value !== "object" || Array.isArray(value))
54756
54861
  throw new Error;
54757
54862
  return value;
@@ -54760,7 +54865,7 @@ function parseConfig(text2, path, toml = false) {
54760
54865
  }
54761
54866
  }
54762
54867
  function safe4(path) {
54763
- if (!isAbsolute14(path) || path.includes("\x00"))
54868
+ if (!isAbsolute15(path) || path.includes("\x00"))
54764
54869
  throw new Error("Expected an absolute native discovery path");
54765
54870
  for (let cursor = path;; cursor = dirname16(cursor)) {
54766
54871
  if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
@@ -54813,12 +54918,12 @@ function projected(source, changes, budget = discoveryByteBudget()) {
54813
54918
  return hashDiscoveryPathFile(source.path, budget, changes);
54814
54919
  return hashRawDiscoveryFile(source.path, budget, changes);
54815
54920
  }
54816
- const text2 = read2(source.path, changes);
54817
- if (text2 === null)
54921
+ const text3 = read2(source.path, changes);
54922
+ if (text3 === null)
54818
54923
  return null;
54819
54924
  if (!source.format)
54820
- return digest(text2);
54821
- const object4 = source.format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, source.path, source.format === "toml");
54925
+ return digest(text3);
54926
+ const object4 = source.format === "yaml" ? parseHermesConfig(text3) : parseConfig(text3, source.path, source.format === "toml");
54822
54927
  if (!object4 || typeof object4 !== "object" || Array.isArray(object4))
54823
54928
  throw new Error("Expected native discovery configuration object");
54824
54929
  return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object4[field2] ?? null]))));
@@ -54858,7 +54963,10 @@ function rebindAgentDiscovery(binding, changes) {
54858
54963
  const budget = discoveryByteBudget();
54859
54964
  return { ...binding, sources: binding.sources.map((source) => {
54860
54965
  assertMarketplaceBinding(binding, source);
54861
- return { ...source, sha256: projected(source, changes, budget) };
54966
+ const sha2562 = projected(source, changes, budget);
54967
+ if (!changes.has(source.path) && sha2562 !== source.sha256)
54968
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
54969
+ return { ...source, sha256: sha2562 };
54862
54970
  }) };
54863
54971
  }
54864
54972
  function assertProjectDiscovery(agent, directories, home, canonical4 = resolve15, reviewed) {
@@ -54962,14 +55070,27 @@ function resolveAgentDiscovery(options) {
54962
55070
  if (!lstatSync20(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
54963
55071
  unresolved("installed Gemini builtin root is missing");
54964
55072
  for (const name of readdirSync10(builtinRoot).sort()) {
54965
- const text2 = witness(join31(builtinRoot, name, "SKILL.md"));
54966
- const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
55073
+ const text3 = witness(join31(builtinRoot, name, "SKILL.md"));
55074
+ const match = text3?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
54967
55075
  if (!match?.[1] || builtinNames.includes(match[1]))
54968
55076
  unresolved("installed Gemini builtin name is missing or ambiguous");
54969
55077
  builtinNames.push(match[1]);
54970
55078
  }
54971
55079
  }
54972
55080
  }
55081
+ if (options.reviewed === undefined && options.retainedReview !== undefined) {
55082
+ const retained = options.retainedReview;
55083
+ if (retained.agent !== agent || retained.method !== "reviewed")
55084
+ unresolved("invalid retained review");
55085
+ verifyAgentDiscovery(retained);
55086
+ if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
55087
+ unresolved("retained review is missing the full agent configuration source");
55088
+ if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
55089
+ unresolved("retained settings witness names another configuration source");
55090
+ if (sources.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))
55091
+ unresolved("retained review is missing current runtime discovery coverage");
55092
+ return retained;
55093
+ }
54973
55094
  if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
54974
55095
  throw new Error("Invalid --discovery-inputs version or agents");
54975
55096
  const review = options.reviewed?.agents.find((item) => item.agent === agent);
@@ -55041,7 +55162,7 @@ function resolveAgentDiscovery(options) {
55041
55162
  unresolved("unsupported plugin enablement");
55042
55163
  const split2 = id.lastIndexOf("@"), name = id.slice(0, split2), marketplace = id.slice(split2 + 1);
55043
55164
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
55044
- if (split2 < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute14(market.source))
55165
+ if (split2 < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute15(market.source))
55045
55166
  unresolved("enabled plugin marketplace is not an explicit local source");
55046
55167
  const catalogText = witness(join31(market.source, ".agents/plugins/marketplace.json"));
55047
55168
  if (catalogText === null)
@@ -55057,7 +55178,7 @@ function resolveAgentDiscovery(options) {
55057
55178
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
55058
55179
  unresolved("external plugin or remote skill source");
55059
55180
  for (const path of config.skills?.paths ?? []) {
55060
- if (typeof path !== "string" || !isAbsolute14(path))
55181
+ if (typeof path !== "string" || !isAbsolute15(path))
55061
55182
  unresolved("relative or malformed added skill root");
55062
55183
  roots.add(path);
55063
55184
  }
@@ -55095,7 +55216,7 @@ function resolveAgentDiscovery(options) {
55095
55216
  }
55096
55217
  return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join31(home, ".hermes/plugins"), join31(home, ".hermes/hermes-agent")].map((path) => canonical4(path))) } : {} };
55097
55218
  }
55098
- var digest = (text2) => createHash16("sha256").update(text2).digest("hex");
55219
+ var digest = (text3) => createHash16("sha256").update(text3).digest("hex");
55099
55220
  var init_agent_discovery = __esm(() => {
55100
55221
  init_agent_policy_limits();
55101
55222
  init_agent_hermes();
@@ -55227,7 +55348,7 @@ import {
55227
55348
  statSync as statSync10,
55228
55349
  writeFileSync as writeFileSync9
55229
55350
  } from "fs";
55230
- import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute15, join as join33, normalize as normalize2, relative as relative5, resolve as resolve17, sep as sep7 } from "path";
55351
+ import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute16, join as join33, normalize as normalize2, relative as relative5, resolve as resolve17, sep as sep7 } from "path";
55231
55352
  function getPortableSkillsRoot(options = {}) {
55232
55353
  return resolvePortableSkillsRoot(options, true);
55233
55354
  }
@@ -55385,7 +55506,7 @@ function assertDisjointImportPaths(source, destination) {
55385
55506
  const destinationPath = physicalImportPath(destination);
55386
55507
  const contains2 = (parent, child) => {
55387
55508
  const path = relative5(parent, child);
55388
- return path === "" || !isAbsolute15(path) && path !== ".." && !path.startsWith(`..${sep7}`);
55509
+ return path === "" || !isAbsolute16(path) && path !== ".." && !path.startsWith(`..${sep7}`);
55389
55510
  };
55390
55511
  if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
55391
55512
  throw new Error("Source and destination skill directories must not overlap");
@@ -55602,7 +55723,7 @@ function safeIsDirectory(path) {
55602
55723
  }
55603
55724
  }
55604
55725
  function isSafeRelativePath2(value) {
55605
- if (!value.trim() || isAbsolute15(value))
55726
+ if (!value.trim() || isAbsolute16(value))
55606
55727
  return false;
55607
55728
  const normalized = normalize2(value).replace(/\\/g, "/");
55608
55729
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
@@ -57626,8 +57747,8 @@ function getPublicSkillDiscovery(skill) {
57626
57747
  function publicDiscoveryTags(tags) {
57627
57748
  return tags.filter((tag) => !VENDOR_TAGS.has(tag.toLowerCase()));
57628
57749
  }
57629
- function sanitizePublicDiscoveryText(text2) {
57630
- let sanitized = text2.replace(vendorPattern, "hosted AI").replace(/\bLLM\b/g, "AI").replace(/\s{2,}/g, " ");
57750
+ function sanitizePublicDiscoveryText(text3) {
57751
+ let sanitized = text3.replace(vendorPattern, "hosted AI").replace(/\bLLM\b/g, "AI").replace(/\s{2,}/g, " ");
57631
57752
  let previous;
57632
57753
  do {
57633
57754
  previous = sanitized;
@@ -66247,8 +66368,8 @@ function lt_default() {
66247
66368
  localeError: error33()
66248
66369
  };
66249
66370
  }
66250
- var capitalizeFirstCharacter = (text2) => {
66251
- return text2.charAt(0).toUpperCase() + text2.slice(1);
66371
+ var capitalizeFirstCharacter = (text3) => {
66372
+ return text3.charAt(0).toUpperCase() + text3.slice(1);
66252
66373
  }, error33 = () => {
66253
66374
  const Sizable = {
66254
66375
  string: {
@@ -74081,7 +74202,7 @@ __export(exports_schemas2, {
74081
74202
  httpUrl: () => httpUrl,
74082
74203
  hostname: () => hostname2,
74083
74204
  hex: () => hex2,
74084
- hash: () => hash2,
74205
+ hash: () => hash3,
74085
74206
  guid: () => guid2,
74086
74207
  function: () => _function,
74087
74208
  float64: () => float64,
@@ -74287,7 +74408,7 @@ function hostname2(_params) {
74287
74408
  function hex2(_params) {
74288
74409
  return _stringFormat(ZodCustomStringFormat, "hex", exports_regexes.hex, _params);
74289
74410
  }
74290
- function hash2(alg, params) {
74411
+ function hash3(alg, params) {
74291
74412
  const enc = params?.enc ?? "hex";
74292
74413
  const format = `${alg}_${enc}`;
74293
74414
  const regex2 = exports_regexes[format];
@@ -76547,7 +76668,7 @@ __export(exports_external, {
76547
76668
  httpUrl: () => httpUrl,
76548
76669
  hostname: () => hostname2,
76549
76670
  hex: () => hex2,
76550
- hash: () => hash2,
76671
+ hash: () => hash3,
76551
76672
  guid: () => guid2,
76552
76673
  gte: () => _gte,
76553
76674
  gt: () => _gt,
@@ -76782,8 +76903,8 @@ function safeAvailabilityCode(code) {
76782
76903
  return;
76783
76904
  return /^[A-Z0-9_]+$/.test(code) ? code : undefined;
76784
76905
  }
76785
- function sanitizeAvailabilityText(text2) {
76786
- return secretValuePatterns.reduce((value, pattern) => value.replace(pattern, "credential"), sanitizePublicDiscoveryText(text2).replace(/\b[A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|CREDENTIAL)[A-Z0-9_]*\b/g, "credential")).replace(/\s{2,}/g, " ").trim();
76906
+ function sanitizeAvailabilityText(text3) {
76907
+ return secretValuePatterns.reduce((value, pattern) => value.replace(pattern, "credential"), sanitizePublicDiscoveryText(text3).replace(/\b[A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|CREDENTIAL)[A-Z0-9_]*\b/g, "credential")).replace(/\s{2,}/g, " ").trim();
76787
76908
  }
76788
76909
  function parseRemoteRegistryPayload(payload) {
76789
76910
  const parsed = parseRemoteContract(remoteRegistrySchema, payload, "Remote registry payload did not match the expected skills contract");
@@ -76988,11 +77109,11 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
76988
77109
  throw new Error("Invalid canonical JSON depth bound");
76989
77110
  let bytes = 0;
76990
77111
  const active = new Set;
76991
- const piece = (text2) => {
76992
- bytes += Buffer.byteLength(text2);
77112
+ const piece = (text3) => {
77113
+ bytes += Buffer.byteLength(text3);
76993
77114
  if (bytes > maximumBytes)
76994
77115
  throw new Error("Canonical JSON exceeds its bound");
76995
- return text2;
77116
+ return text3;
76996
77117
  };
76997
77118
  const visit3 = (v, depth) => {
76998
77119
  if (depth > maximumDepth)
@@ -77131,9 +77252,9 @@ function requireResult(value) {
77131
77252
  if (!value)
77132
77253
  throw new RemoteRecurringReadError;
77133
77254
  }
77134
- function termsIntegrity(terms, hash3, identity4, own2) {
77255
+ function termsIntegrity(terms, hash4, identity4, own2) {
77135
77256
  requireResult(terms.organizationId === identity4.organization.id && (!own2 || terms.approvedByUserId === identity4.user.id && terms.approvedMembershipId === identity4.user.membershipId));
77136
- requireResult(canonicalJsonSha256(terms) === hash3);
77257
+ requireResult(canonicalJsonSha256(terms) === hash4);
77137
77258
  requireResult(canonicalJsonSha256({ skill: terms.skill, input: terms.input, args: terms.args, runtime: terms.runtime }) === terms.payloadSha256);
77138
77259
  requireResult(Date.parse(terms.expiresAt) > Date.parse(terms.startsAt));
77139
77260
  }
@@ -77529,7 +77650,7 @@ function invalid3() {
77529
77650
  throw new Error(invalidWorkspaceResult);
77530
77651
  }
77531
77652
  function organization(v) {
77532
- if (!record4(v) || !uuid8(v.id) || !text2(v.slug) || !text2(v.name))
77653
+ if (!record4(v) || !uuid8(v.id) || !text3(v.slug) || !text3(v.name))
77533
77654
  return invalid3();
77534
77655
  return { id: v.id, slug: v.slug, name: v.name };
77535
77656
  }
@@ -77549,7 +77670,7 @@ function parseWorkspaceIdentity(value, expectedUserId) {
77549
77670
  if (!record4(value))
77550
77671
  return invalid3();
77551
77672
  const user = value.user;
77552
- if (!record4(user) || !uuid8(user.id) || !uuid8(user.membershipId) || !text2(user.email, 320) || !(user.displayName === null || text2(user.displayName)) || !role2(user.role))
77673
+ if (!record4(user) || !uuid8(user.id) || !uuid8(user.membershipId) || !text3(user.email, 320) || !(user.displayName === null || text3(user.displayName)) || !role2(user.role))
77553
77674
  return invalid3();
77554
77675
  if (user.id !== expectedUserId)
77555
77676
  throw new WorkspaceIdentityMismatchError;
@@ -77580,7 +77701,7 @@ function workspaceSelectionFailure(value, status) {
77580
77701
  const code = value.code;
77581
77702
  return workspaceSelectionFailures[code][0] === status ? code : null;
77582
77703
  }
77583
- var record4 = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid8 = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), text2 = (v, max2 = 1024) => typeof v === "string" && !!v.trim() && v.length <= max2 && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v), role2 = (v) => typeof v === "string" && ["owner", "admin", "member", "viewer"].includes(v), invalidWorkspaceResult = "The server returned an invalid workspace selection result.", WorkspaceContextInputError, WorkspaceIdentityMismatchError, workspaceSelectionFailures;
77704
+ var record4 = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid8 = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), text3 = (v, max2 = 1024) => typeof v === "string" && !!v.trim() && v.length <= max2 && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v), role2 = (v) => typeof v === "string" && ["owner", "admin", "member", "viewer"].includes(v), invalidWorkspaceResult = "The server returned an invalid workspace selection result.", WorkspaceContextInputError, WorkspaceIdentityMismatchError, workspaceSelectionFailures;
77584
77705
  var init_remote_workspace_selection = __esm(() => {
77585
77706
  WorkspaceContextInputError = class WorkspaceContextInputError extends Error {
77586
77707
  constructor() {
@@ -80394,17 +80515,17 @@ var init_install = __esm(() => {
80394
80515
 
80395
80516
  // src/lib/compact-output.ts
80396
80517
  function truncateText(value, maxChars = 96) {
80397
- const text3 = String(value ?? "").replace(/\s+/g, " ").trim();
80398
- if (text3.length <= maxChars)
80399
- return text3;
80400
- return `${text3.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
80518
+ const text4 = String(value ?? "").replace(/\s+/g, " ").trim();
80519
+ if (text4.length <= maxChars)
80520
+ return text4;
80521
+ return `${text4.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
80401
80522
  }
80402
80523
  function previewText(value, maxChars = DEFAULT_PREVIEW_CHARS) {
80403
- const text3 = String(value ?? "");
80524
+ const text4 = String(value ?? "");
80404
80525
  return {
80405
- text: text3.length > maxChars ? `${text3.slice(0, Math.max(0, maxChars - 3))}...` : text3,
80406
- length: text3.length,
80407
- truncated: text3.length > maxChars
80526
+ text: text4.length > maxChars ? `${text4.slice(0, Math.max(0, maxChars - 3))}...` : text4,
80527
+ length: text4.length,
80528
+ truncated: text4.length > maxChars
80408
80529
  };
80409
80530
  }
80410
80531
  function parsePageLimit(value, fallback, options = {}) {
@@ -80523,10 +80644,10 @@ function handleBrowseError(error61) {
80523
80644
  process.exitCode = 1;
80524
80645
  }
80525
80646
  async function writeJson(value, space) {
80526
- const text3 = `${JSON.stringify(value, null, space)}
80647
+ const text4 = `${JSON.stringify(value, null, space)}
80527
80648
  `;
80528
80649
  await new Promise((resolve19, reject2) => {
80529
- process.stdout.write(text3, (error61) => {
80650
+ process.stdout.write(text4, (error61) => {
80530
80651
  if (error61)
80531
80652
  reject2(error61);
80532
80653
  else
@@ -81085,7 +81206,7 @@ function handleDiff(name, options) {
81085
81206
  const present = existsSync22(dir);
81086
81207
  const managed = existsSync22(join37(dir, SYNC_MARKER_FILE));
81087
81208
  const skillMdPath = join37(dir, "SKILL.md");
81088
- const hash3 = present && existsSync22(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : undefined;
81209
+ const hash4 = present && existsSync22(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : undefined;
81089
81210
  let stub;
81090
81211
  if (present && existsSync22(skillMdPath)) {
81091
81212
  try {
@@ -81094,13 +81215,13 @@ function handleDiff(name, options) {
81094
81215
  stub = undefined;
81095
81216
  }
81096
81217
  }
81097
- const diverged = present && canonical4.present && managed && hash3 !== canonical4.hash;
81218
+ const diverged = present && canonical4.present && managed && hash4 !== canonical4.hash;
81098
81219
  homes.push({
81099
81220
  agent,
81100
81221
  path: dir,
81101
81222
  present,
81102
81223
  managed,
81103
- ...hash3 ? { hash: hash3 } : {},
81224
+ ...hash4 ? { hash: hash4 } : {},
81104
81225
  ...stub !== undefined ? { stub } : {},
81105
81226
  ...diverged !== undefined ? { diverged } : {}
81106
81227
  });
@@ -81956,13 +82077,13 @@ function scanUnmarkedHomes(options = {}) {
81956
82077
  const skillMdPath = join39(dir, "SKILL.md");
81957
82078
  if (!existsSync24(skillMdPath))
81958
82079
  continue;
81959
- const hash3 = hashSkillMarkdownFile(skillMdPath);
82080
+ const hash4 = hashSkillMarkdownFile(skillMdPath);
81960
82081
  const mtime = statSync12(skillMdPath).mtime.toISOString();
81961
- const entry = { agent, skill, home, path: dir, hash: hash3, mtime };
82082
+ const entry = { agent, skill, home, path: dir, hash: hash4, mtime };
81962
82083
  const canonicalHash = index.get(skill);
81963
82084
  if (canonicalHash === undefined) {
81964
82085
  scan.unknown.push({ ...entry });
81965
- } else if (canonicalHash === hash3) {
82086
+ } else if (canonicalHash === hash4) {
81966
82087
  scan.adoptable.push(entry);
81967
82088
  } else {
81968
82089
  scan.conflicts.push({ ...entry, canonicalHash });
@@ -82050,10 +82171,10 @@ function pruneOwnership(dir) {
82050
82171
  const after2 = fstatSync15(descriptor), current = lstatSync22(dir), markerPathAfter = lstatSync22(markerPath);
82051
82172
  if (!markerPathAfter.isFile() || markerPathAfter.dev !== before2.dev || markerPathAfter.ino !== before2.ino || markerPathAfter.size !== before2.size || markerPathAfter.mtimeMs !== before2.mtimeMs || markerPathAfter.ctimeMs !== before2.ctimeMs || length !== before2.size || after2.size !== before2.size || after2.mtimeMs !== before2.mtimeMs || after2.ctimeMs !== before2.ctimeMs || !current.isDirectory() || current.dev !== directory.dev || current.ino !== directory.ino)
82052
82173
  return;
82053
- const text3 = bytes.subarray(0, length).toString("utf8"), marker = JSON.parse(text3);
82174
+ const text4 = bytes.subarray(0, length).toString("utf8"), marker = JSON.parse(text4);
82054
82175
  if (!isSkillsOwnershipMarker(marker))
82055
82176
  return;
82056
- return { marker, identity: JSON.stringify([directory.dev, directory.ino, before2.dev, before2.ino, before2.mtimeMs, before2.ctimeMs, text3]) };
82177
+ return { marker, identity: JSON.stringify([directory.dev, directory.ino, before2.dev, before2.ino, before2.mtimeMs, before2.ctimeMs, text4]) };
82057
82178
  } catch {
82058
82179
  return;
82059
82180
  } finally {
@@ -82099,15 +82220,15 @@ function pruneStrayHomes(options = {}) {
82099
82220
  const marker = owned.marker;
82100
82221
  ownership.set(dir, owned.identity);
82101
82222
  const skillMdPath = join39(dir, "SKILL.md");
82102
- const hash3 = existsSync24(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : "";
82103
- candidates.push({ agent, skill, home, path: dir, hash: hash3, marker });
82223
+ const hash4 = existsSync24(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : "";
82224
+ candidates.push({ agent, skill, home, path: dir, hash: hash4, marker });
82104
82225
  }
82105
82226
  }
82106
82227
  if (!options.apply) {
82107
82228
  return { candidates, pruned: 0, dryRun: true };
82108
82229
  }
82109
82230
  const appDir = options.homeDir ? join39(options.homeDir, ".hasna", "skills") : getDataDir();
82110
- const rollbackFile = writeRollbackRecord("prune", candidates.map(({ agent, skill, path, hash: hash3, marker }) => ({ agent, skill, path, hash: hash3, marker })), appDir);
82231
+ const rollbackFile = writeRollbackRecord("prune", candidates.map(({ agent, skill, path, hash: hash4, marker }) => ({ agent, skill, path, hash: hash4, marker })), appDir);
82111
82232
  let pruned = 0;
82112
82233
  for (const candidate2 of candidates) {
82113
82234
  if (pruneOwnership(candidate2.path)?.identity !== ownership.get(candidate2.path))
@@ -82338,10 +82459,10 @@ function setEnvAssignment(path, assignment) {
82338
82459
  }
82339
82460
  let prepared = initial2;
82340
82461
  if (previous) {
82341
- const original = readFileSync27(descriptor), text3 = original.toString("utf8");
82342
- if (!Buffer.from(text3, "utf8").equals(original))
82462
+ const original = readFileSync27(descriptor), text4 = original.toString("utf8");
82463
+ if (!Buffer.from(text4, "utf8").equals(original))
82343
82464
  throw new EnvAssignmentError(INVALID_LAYOUT);
82344
- prepared = prepareEnvAssignment(assignment, text3);
82465
+ prepared = prepareEnvAssignment(assignment, text4);
82345
82466
  }
82346
82467
  const bytes = Buffer.from(prepared.content, "utf8");
82347
82468
  let written = 0;
@@ -82881,7 +83002,7 @@ import { closeSync as closeSync17, fstatSync as fstatSync17, openSync as openSyn
82881
83002
  import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
82882
83003
  import { createRequire as createRequire2 } from "module";
82883
83004
  import { hostname as osHostname2 } from "os";
82884
- import { isAbsolute as isAbsolute16, join as join42 } from "path";
83005
+ import { isAbsolute as isAbsolute17, join as join42 } from "path";
82885
83006
  import { createRequire as createRequire22 } from "module";
82886
83007
  function envToken2(name) {
82887
83008
  return name.toUpperCase().replace(/-/g, "_");
@@ -82905,7 +83026,7 @@ function homeDir2(env3) {
82905
83026
  }
82906
83027
  function absoluteOverride2(env3, key) {
82907
83028
  const value = env3[key]?.trim();
82908
- return value && isAbsolute16(value) ? value : null;
83029
+ return value && isAbsolute17(value) ? value : null;
82909
83030
  }
82910
83031
  function hasnaHomeDir2(env3) {
82911
83032
  const override = absoluteOverride2(env3, HASNA_HOME_ENV_KEY2);
@@ -82936,10 +83057,10 @@ function credentialDiskSources2(name, env3) {
82936
83057
  function profileDiskSources2(name, env3, profile) {
82937
83058
  return credentialDiskSourceList2(name, env3, profile).map((s) => s.path);
82938
83059
  }
82939
- function parseEnvFile2(text3) {
83060
+ function parseEnvFile2(text4) {
82940
83061
  const values2 = new Map;
82941
83062
  const unusable = new Set;
82942
- for (const rawLine of text3.split(/\r?\n/)) {
83063
+ for (const rawLine of text4.split(/\r?\n/)) {
82943
83064
  const line = rawLine.trim();
82944
83065
  if (line.length === 0 || line.startsWith("#"))
82945
83066
  continue;
@@ -83179,8 +83300,8 @@ function keychainAccount2(env3, options) {
83179
83300
  const user = env3.USER?.trim();
83180
83301
  return user || null;
83181
83302
  }
83182
- function keychainFailureHint2(text3) {
83183
- const line = text3.split(/\r?\n/).find((entry) => entry.trim().length > 0)?.trim() ?? "";
83303
+ function keychainFailureHint2(text4) {
83304
+ const line = text4.split(/\r?\n/).find((entry) => entry.trim().length > 0)?.trim() ?? "";
83184
83305
  const clean = line.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 200);
83185
83306
  return clean ? `: ${clean}` : "";
83186
83307
  }
@@ -83758,12 +83879,12 @@ function createHasnaHttpTransportInternal(options, requestBindingProvider) {
83758
83879
  await response.body?.cancel();
83759
83880
  } catch {}
83760
83881
  } else {
83761
- const text3 = await response.text();
83762
- if (text3.length > 0) {
83882
+ const text4 = await response.text();
83883
+ if (text4.length > 0) {
83763
83884
  try {
83764
- parsed = JSON.parse(text3);
83885
+ parsed = JSON.parse(text4);
83765
83886
  } catch {
83766
- parsed = text3;
83887
+ parsed = text4;
83767
83888
  }
83768
83889
  }
83769
83890
  }
@@ -83866,13 +83987,13 @@ class SecretsClient {
83866
83987
  headers,
83867
83988
  redirect: "manual"
83868
83989
  });
83869
- const text3 = await response.text();
83990
+ const text4 = await response.text();
83870
83991
  let body = undefined;
83871
- if (text3) {
83992
+ if (text4) {
83872
83993
  try {
83873
- body = JSON.parse(text3);
83994
+ body = JSON.parse(text4);
83874
83995
  } catch {
83875
- body = text3;
83996
+ body = text4;
83876
83997
  }
83877
83998
  }
83878
83999
  if (!response.ok) {
@@ -84201,7 +84322,7 @@ function refuse(code, message) {
84201
84322
  function record7(value) {
84202
84323
  return !!value && typeof value === "object" && !Array.isArray(value);
84203
84324
  }
84204
- function exactKeys2(value, keys2) {
84325
+ function exactKeys3(value, keys2) {
84205
84326
  return Object.keys(value).length === keys2.length && keys2.every((key) => Object.hasOwn(value, key));
84206
84327
  }
84207
84328
  function identifier2(value) {
@@ -84241,7 +84362,7 @@ function readSelectedSecretBindings(path) {
84241
84362
  }
84242
84363
  }
84243
84364
  function validateSelectedSecretBindings(value, scope, names) {
84244
- if (!record7(value) || !exactKeys2(value, ["schema", "selection", "consumer", "secretsAuthority", "bindings"]) || value.schema !== "hasna.skills-secret-bindings.v1" || !record7(value.selection) || !exactKeys2(value.selection, ["authority", "workspaceId", "profileId", "profileRevision", "slug", "version", "bundleDigest"]) || !record7(value.consumer) || !exactKeys2(value.consumer, ["stationId", "workspaceDirectory"]) || !record7(value.bindings)) {
84365
+ if (!record7(value) || !exactKeys3(value, ["schema", "selection", "consumer", "secretsAuthority", "bindings"]) || value.schema !== "hasna.skills-secret-bindings.v1" || !record7(value.selection) || !exactKeys3(value.selection, ["authority", "workspaceId", "profileId", "profileRevision", "slug", "version", "bundleDigest"]) || !record7(value.consumer) || !exactKeys3(value.consumer, ["stationId", "workspaceDirectory"]) || !record7(value.bindings)) {
84245
84366
  return refuse("INVALID_SECRET_BINDINGS", "Secret bindings do not match the hasna.skills-secret-bindings.v1 contract.");
84246
84367
  }
84247
84368
  for (const [key, expected] of Object.entries({ authority: scope.selection.authority, workspaceId: scope.selection.workspaceId, profileId: scope.profileId, profileRevision: scope.selection.profileRevision, slug: scope.selection.slug, version: scope.selection.version, bundleDigest: scope.selection.bundleDigest })) {
@@ -84266,7 +84387,7 @@ function validateSelectedSecretBindings(value, scope, names) {
84266
84387
  } catch {
84267
84388
  return refuse("INVALID_SECRET_BINDINGS", "Secret bindings require a canonical HTTPS Secrets /v1 authority (loopback HTTP is allowed).");
84268
84389
  }
84269
- if (!exactKeys2(value.bindings, names) || Object.values(value.bindings).some((key) => !identifier2(key) || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(key) || key.split("/").some((part) => !part || part === "." || part === ".."))) {
84390
+ if (!exactKeys3(value.bindings, names) || Object.values(value.bindings).some((key) => !identifier2(key) || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(key) || key.split("/").some((part) => !part || part === "." || part === ".."))) {
84270
84391
  return refuse("INVALID_SECRET_BINDINGS", "Bind every declared environment name exactly once to a vault key; undeclared bindings and values are refused.");
84271
84392
  }
84272
84393
  return structuredClone(value);
@@ -84296,7 +84417,7 @@ async function resolveSelectedSecrets(bindings, createClient2 = createSecretsCli
84296
84417
  return refuse("LOCAL_SECRET_UNAVAILABLE", "A declared secret could not be resolved, was invalid, or has expired. The skill was not started; no fallback was attempted.");
84297
84418
  }
84298
84419
  }
84299
- function redactExecutionSecrets(text3, env3) {
84420
+ function redactExecutionSecrets(text4, env3) {
84300
84421
  const values2 = new Set(Object.values(env3).flatMap((value) => {
84301
84422
  let encoded = "";
84302
84423
  try {
@@ -84305,8 +84426,8 @@ function redactExecutionSecrets(text3, env3) {
84305
84426
  return [value, JSON.stringify(value).slice(1, -1), Buffer.from(value).toString("base64"), encoded];
84306
84427
  }));
84307
84428
  for (const value of [...values2].filter(Boolean).sort((a, b) => b.length - a.length))
84308
- text3 = text3.split(value).join("[REDACTED]");
84309
- return text3;
84429
+ text4 = text4.split(value).join("[REDACTED]");
84430
+ return text4;
84310
84431
  }
84311
84432
  var init_execution_secrets = __esm(() => {
84312
84433
  init_sdk();
@@ -84444,7 +84565,7 @@ var init_execution_grants = __esm(() => {
84444
84565
  });
84445
84566
 
84446
84567
  // src/lib/execution-grant-client.ts
84447
- import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
84568
+ import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
84448
84569
  function id(value) {
84449
84570
  if (!grantIdentifier(value))
84450
84571
  throw new Error("Invalid execution grant profile or revision identifier.");
@@ -84496,7 +84617,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
84496
84617
  headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
84497
84618
  body: JSON.stringify({ grants: snapshot2 })
84498
84619
  }));
84499
- if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual6(policy.grants, snapshot2)) {
84620
+ if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual8(policy.grants, snapshot2)) {
84500
84621
  throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
84501
84622
  }
84502
84623
  return policy;
@@ -84507,7 +84628,7 @@ async function resolveExecutionGrant(input2) {
84507
84628
  if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
84508
84629
  throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
84509
84630
  }
84510
- if (!isDeepStrictEqual6(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual6(value.bindings.consumer, snapshot2.consumer)) {
84631
+ if (!isDeepStrictEqual8(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual8(value.bindings.consumer, snapshot2.consumer)) {
84511
84632
  throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
84512
84633
  }
84513
84634
  return value;
@@ -93602,10 +93723,10 @@ var require_util = __commonJS((exports) => {
93602
93723
  var codegen_1 = require_codegen();
93603
93724
  var code_1 = require_code();
93604
93725
  function toHash(arr) {
93605
- const hash3 = {};
93726
+ const hash4 = {};
93606
93727
  for (const item of arr)
93607
- hash3[item] = true;
93608
- return hash3;
93728
+ hash4[item] = true;
93729
+ return hash4;
93609
93730
  }
93610
93731
  exports.toHash = toHash;
93611
93732
  function alwaysValidSchema(it, schema) {
@@ -97067,7 +97188,7 @@ var require_core = __commonJS((exports) => {
97067
97188
  errorsText(errors5 = this.errors, { separator = ", ", dataVar = "data" } = {}) {
97068
97189
  if (!errors5 || errors5.length === 0)
97069
97190
  return "No errors";
97070
- return errors5.map((e2) => `${dataVar}${e2.instancePath} ${e2.message}`).reduce((text3, msg) => text3 + separator + msg);
97191
+ return errors5.map((e2) => `${dataVar}${e2.instancePath} ${e2.message}`).reduce((text4, msg) => text4 + separator + msg);
97071
97192
  }
97072
97193
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
97073
97194
  const rules = this.RULES.all;
@@ -101145,11 +101266,11 @@ var init_remote_invitation_recovery = __esm(() => {
101145
101266
  // src/lib/remote-private-publications.ts
101146
101267
  import { createHash as createHash23 } from "crypto";
101147
101268
  function checkedPublicationDeclaration(value) {
101148
- 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)
101269
+ 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 || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
101149
101270
  return bad();
101150
101271
  try {
101151
101272
  const manifest = JSON.parse(value.manifestText);
101152
- if (!record10(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !hash3(manifest.provenance?.content_hash))
101273
+ if (!record10(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !hash4(manifest.provenance?.content_hash))
101153
101274
  return bad();
101154
101275
  } catch {
101155
101276
  return bad();
@@ -101157,7 +101278,7 @@ function checkedPublicationDeclaration(value) {
101157
101278
  return Object.freeze({ ...value });
101158
101279
  }
101159
101280
  function checkedPublicationView(value, skillId, intentId, declaration) {
101160
- if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES || typeof value.state !== "string" || !["awaiting_upload", "queued", "verifying", "needs_attention", "committed", "rejected", "cancelled", "expired"].includes(value.state) || !date6(value.expiresAt) || !date6(value.createdAt) || !(value.versionId === null || publicationUuid(value.versionId)) || value.state === "committed" && value.versionId === null)
101281
+ if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES || typeof value.state !== "string" || !["awaiting_upload", "queued", "verifying", "needs_attention", "committed", "rejected", "cancelled", "expired"].includes(value.state) || !date6(value.expiresAt) || !date6(value.createdAt) || !(value.versionId === null || publicationUuid(value.versionId)) || value.state === "committed" && value.versionId === null)
101161
101282
  return invalid5();
101162
101283
  if (declaration && ["version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize"].some((k) => value[k] !== declaration[k]))
101163
101284
  return invalid5();
@@ -101404,7 +101525,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = ()
101404
101525
  throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
101405
101526
  }, invalid5 = () => {
101406
101527
  throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
101407
- }, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record10 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys3) => record10(v) && Object.keys(v).sort().join(",") === keys3.sort().join(","), date6 = (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)), publicationSha256 = (bytes) => createHash23("sha256").update(bytes).digest("hex");
101528
+ }, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash4 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record10 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys3) => record10(v) && Object.keys(v).sort().join(",") === keys3.sort().join(","), date6 = (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)), publicationSha256 = (bytes) => createHash23("sha256").update(bytes).digest("hex");
101408
101529
  var init_remote_private_publications = __esm(() => {
101409
101530
  init_fleet_credentials();
101410
101531
  init_remote_workspace_selection();
@@ -101465,8 +101586,8 @@ async function requestAuthApi(instance, path, options) {
101465
101586
  apiUrl: safeUrl
101466
101587
  });
101467
101588
  }
101468
- const text3 = await res.text();
101469
- const body = text3 ? parseJsonBody(text3) : {};
101589
+ const text4 = await res.text();
101590
+ const body = text4 ? parseJsonBody(text4) : {};
101470
101591
  if (!res.ok) {
101471
101592
  const record11 = isRecord5(body) ? body : {};
101472
101593
  const detail = typeof record11.detail === "string" ? record11.detail : undefined;
@@ -101482,15 +101603,15 @@ async function requestAuthApi(instance, path, options) {
101482
101603
  }
101483
101604
  return body;
101484
101605
  }
101485
- function parseJsonBody(text3) {
101606
+ function parseJsonBody(text4) {
101486
101607
  try {
101487
- return JSON.parse(text3);
101608
+ return JSON.parse(text4);
101488
101609
  } catch {
101489
- return { detail: condenseErrorBody(text3) };
101610
+ return { detail: condenseErrorBody(text4) };
101490
101611
  }
101491
101612
  }
101492
- function condenseErrorBody(text3) {
101493
- const stripped = /<[a-z!/]/i.test(text3) ? text3.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text3;
101613
+ function condenseErrorBody(text4) {
101614
+ const stripped = /<[a-z!/]/i.test(text4) ? text4.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text4;
101494
101615
  const collapsed = stripped.replace(/\s+/g, " ").trim();
101495
101616
  if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
101496
101617
  return collapsed;
@@ -101706,12 +101827,12 @@ function safeText(file2) {
101706
101827
  }
101707
101828
  }
101708
101829
  function checkIdentityMetadata(file2, identity4) {
101709
- const text3 = safeText(file2);
101710
- if (text3 === null)
101830
+ const text4 = safeText(file2);
101831
+ if (text4 === null)
101711
101832
  return;
101712
101833
  let value;
101713
101834
  try {
101714
- value = JSON.parse(text3);
101835
+ value = JSON.parse(text4);
101715
101836
  } catch {
101716
101837
  return fail3("The profile identity metadata is invalid. Sign in again before managing this workspace.");
101717
101838
  }
@@ -102050,7 +102171,7 @@ var init_recurring_customer = __esm(() => {
102050
102171
 
102051
102172
  // src/lib/recurring-recovery.ts
102052
102173
  import { constants as constants17, closeSync as closeSync20, fsyncSync as fsyncSync5, fstatSync as fstatSync20, lstatSync as lstatSync25, mkdirSync as mkdirSync17, openSync as openSync20, readSync as readSync14, realpathSync as realpathSync9, renameSync as renameSync10, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
102053
- import { dirname as dirname23, isAbsolute as isAbsolute17, join as join46, resolve as resolve20 } from "path";
102174
+ import { dirname as dirname23, isAbsolute as isAbsolute18, join as join46, resolve as resolve20 } from "path";
102054
102175
  import { randomUUID as randomUUID7 } from "crypto";
102055
102176
 
102056
102177
  class RecoveryOwner {
@@ -102058,7 +102179,7 @@ class RecoveryOwner {
102058
102179
  parents = [];
102059
102180
  constructor(directory) {
102060
102181
  this.directory = directory;
102061
- if (!isAbsolute17(directory) || resolve20(directory) !== directory || realpathSync9(directory) !== directory)
102182
+ if (!isAbsolute18(directory) || resolve20(directory) !== directory || realpathSync9(directory) !== directory)
102062
102183
  invalid6();
102063
102184
  for (let path = directory;; path = dirname23(path)) {
102064
102185
  const stat3 = lstatSync25(path);
@@ -102193,7 +102314,7 @@ function base2(customer, organizationId) {
102193
102314
  };
102194
102315
  }
102195
102316
  function create3(directory, intent) {
102196
- if (!isAbsolute17(directory) || resolve20(directory) !== directory || realpathSync9(dirname23(directory)) !== dirname23(directory))
102317
+ if (!isAbsolute18(directory) || resolve20(directory) !== directory || realpathSync9(dirname23(directory)) !== dirname23(directory))
102197
102318
  invalid6();
102198
102319
  mkdirSync17(directory, { mode: 448 });
102199
102320
  const owner2 = new RecoveryOwner(directory), checked = intentSchema.parse(intent), intentSha256 = canonicalJsonSha256(checked);
@@ -102356,12 +102477,12 @@ var init_recurring_recovery = __esm(() => {
102356
102477
  });
102357
102478
 
102358
102479
  // src/lib/recurring-surface.ts
102359
- import { isAbsolute as isAbsolute18 } from "path";
102480
+ import { isAbsolute as isAbsolute19 } from "path";
102360
102481
  function recurringMcpSchema(action) {
102361
102482
  return action === "activate" ? recurringSurfaceSchemas.activate.extend({ code, confirm: exports_external.literal(true) }) : recurringSurfaceSchemas[action];
102362
102483
  }
102363
- function expectedDraft(draft, hash4) {
102364
- if (!draft || draft.termsSha256 !== hash4)
102484
+ function expectedDraft(draft, hash5) {
102485
+ if (!draft || draft.termsSha256 !== hash5)
102365
102486
  throw new RemoteRecurringError("RECURRING_TERMS_UNAVAILABLE");
102366
102487
  return draft;
102367
102488
  }
@@ -102461,7 +102582,7 @@ var init_recurring_surface = __esm(() => {
102461
102582
  uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
102462
102583
  context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
102463
102584
  page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
102464
- directory = exports_external.string().max(4096).refine(isAbsolute18, "Use an absolute recovery directory");
102585
+ directory = exports_external.string().max(4096).refine(isAbsolute19, "Use an absolute recovery directory");
102465
102586
  email4 = exports_external.string().trim().toLowerCase().email().max(254);
102466
102587
  code = exports_external.string().regex(/^\d{6}$/);
102467
102588
  recurringSurfaceSchemas = {
@@ -105263,10 +105384,10 @@ var init_private_publication_customer = __esm(() => {
105263
105384
 
105264
105385
  // src/lib/private-publication-recovery.ts
105265
105386
  import { constants as constants18, closeSync as closeSync21, fsyncSync as fsyncSync6, fstatSync as fstatSync21, lstatSync as lstatSync26, mkdirSync as mkdirSync21, openSync as openSync21, readSync as readSync15, realpathSync as realpathSync10, renameSync as renameSync11, unlinkSync as unlinkSync7, writeFileSync as writeFileSync20 } from "fs";
105266
- import { dirname as dirname26, isAbsolute as isAbsolute19, join as join51, resolve as resolve21 } from "path";
105387
+ import { dirname as dirname26, isAbsolute as isAbsolute20, join as join51, resolve as resolve21 } from "path";
105267
105388
  import { randomUUID as randomUUID8 } from "crypto";
105268
105389
  function safeDirectory(directory2) {
105269
- if (!isAbsolute19(directory2) || directory2 !== resolve21(directory2) || realpathSync10(directory2) !== directory2)
105390
+ if (!isAbsolute20(directory2) || directory2 !== resolve21(directory2) || realpathSync10(directory2) !== directory2)
105270
105391
  return fail4();
105271
105392
  for (let path = directory2;; path = dirname26(path)) {
105272
105393
  const stat3 = lstatSync26(path);
@@ -105404,7 +105525,7 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
105404
105525
  phase: "prepared",
105405
105526
  intent: null
105406
105527
  };
105407
- if (!isAbsolute19(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync10(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
105528
+ if (!isAbsolute20(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync10(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
105408
105529
  return fail4();
105409
105530
  mkdirSync21(recoveryDirectory, { mode: 448 });
105410
105531
  safeDirectory(recoveryDirectory);
@@ -105505,10 +105626,10 @@ var init_private_publication_recovery = __esm(() => {
105505
105626
  });
105506
105627
 
105507
105628
  // src/mcp/private-publication-tools.ts
105508
- import { isAbsolute as isAbsolute20 } from "path";
105629
+ import { isAbsolute as isAbsolute21 } from "path";
105509
105630
  function registerPrivatePublicationTools(server) {
105510
105631
  const uuid9 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
105511
- const directory2 = exports_external.string().max(4096).refine(isAbsolute20, "Use an absolute local directory");
105632
+ const directory2 = exports_external.string().max(4096).refine(isAbsolute21, "Use an absolute local directory");
105512
105633
  const verification2 = { email: exports_external.string().email().max(254), code: exports_external.string().regex(/^\d{6}$/), userId: uuid9, membershipId: uuid9, recoveryDirectory: directory2 };
105513
105634
  const description = "Manage a private source publication using fresh workspace-bound verification and a host-local recovery directory. Publishing requires explicit UUID current-version comparison and upload consent. Resume reuses immutable saved bytes and intent; an uncertain PUT is finalized for server verification, never uploaded twice. Sessions and signed URLs are not returned or persisted. Private execution is unavailable. Host request history may contain the supplied verification code.";
105514
105635
  const handler = (action) => async (value) => {
@@ -106154,7 +106275,7 @@ __export(exports_runtime, {
106154
106275
  });
106155
106276
  import { createHash as createHash25, randomUUID as randomUUID9 } from "crypto";
106156
106277
  import { lstatSync as lstatSync27, mkdirSync as mkdirSync23, readFileSync as readFileSync35, realpathSync as realpathSync11, writeFileSync as writeFileSync22 } from "fs";
106157
- import { basename as basename12, isAbsolute as isAbsolute21, join as join53 } from "path";
106278
+ import { basename as basename12, isAbsolute as isAbsolute22, join as join53 } from "path";
106158
106279
  import { createInterface } from "readline";
106159
106280
  function registerRuntime(parent) {
106160
106281
  parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
@@ -106281,7 +106402,7 @@ Updating ${name}...
106281
106402
  async function readUpdatedVersion(bunExecutable) {
106282
106403
  const output2 = await readUpdateCommand([bunExecutable, "pm", "bin", "-g"]);
106283
106404
  const globalBin = output2.replace(/\r?\n$/, "");
106284
- if (!globalBin || !isAbsolute21(globalBin) || /[\r\n\0]/.test(globalBin))
106405
+ if (!globalBin || !isAbsolute22(globalBin) || /[\r\n\0]/.test(globalBin))
106285
106406
  throw new Error("Update bin discovery failed");
106286
106407
  const installed = Bun.which("skills", { PATH: globalBin, cwd: globalBin });
106287
106408
  const selected = Bun.which("skills");
@@ -107247,13 +107368,13 @@ import { emitKeypressEvents } from "readline";
107247
107368
  async function readCode() {
107248
107369
  if (process.stdin.isTTY)
107249
107370
  throw new NameInputError("Pipe a fresh six-digit verification code when using --code-stdin.");
107250
- let text3 = "";
107371
+ let text4 = "";
107251
107372
  for await (const chunk2 of process.stdin) {
107252
- text3 += chunk2.toString();
107253
- if (text3.length > 32)
107373
+ text4 += chunk2.toString();
107374
+ if (text4.length > 32)
107254
107375
  throw new NameInputError("Supply only a six-digit verification code on stdin.");
107255
107376
  }
107256
- const code2 = text3.trim();
107377
+ const code2 = text4.trim();
107257
107378
  if (!/^\d{6}$/.test(code2))
107258
107379
  throw new NameInputError("Supply only a six-digit verification code on stdin.");
107259
107380
  return code2;
@@ -107281,7 +107402,7 @@ function promptCode() {
107281
107402
  resolve22(answer);
107282
107403
  };
107283
107404
  const cancel = () => finish(null);
107284
- const keypress = (text3, key) => {
107405
+ const keypress = (text4, key) => {
107285
107406
  if (key.ctrl && ["c", "d"].includes(key.name ?? "") || key.name === "escape")
107286
107407
  return cancel();
107287
107408
  if (key.name === "return" || key.name === "enter") {
@@ -107297,8 +107418,8 @@ Enter all six digits: `);
107297
107418
  value = value.slice(0, -1);
107298
107419
  output2.write("\b \b");
107299
107420
  }
107300
- } else if (/^[0-9]$/.test(text3) && value.length < 6) {
107301
- value += text3;
107421
+ } else if (/^[0-9]$/.test(text4) && value.length < 6) {
107422
+ value += text4;
107302
107423
  output2.write("*");
107303
107424
  }
107304
107425
  };
@@ -107780,7 +107901,7 @@ import {
107780
107901
  statSync as statSync19,
107781
107902
  writeFileSync as writeFileSync23
107782
107903
  } from "fs";
107783
- import { dirname as dirname29, isAbsolute as isAbsolute22, relative as relative8, resolve as resolve22, sep as sep10 } from "path";
107904
+ import { dirname as dirname29, isAbsolute as isAbsolute23, relative as relative8, resolve as resolve22, sep as sep10 } from "path";
107784
107905
  function validateStationId(stationId) {
107785
107906
  if (!/^[a-z0-9-]+$/.test(stationId)) {
107786
107907
  throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
@@ -107886,7 +108007,7 @@ function writeStationSnapshot(options) {
107886
108007
  for (const plan of plans) {
107887
108008
  const destination = resolve22(repoRoot, plan.destination);
107888
108009
  const destinationRelative = relative8(repoRoot, destination);
107889
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep10) || isAbsolute22(destinationRelative)) {
108010
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep10) || isAbsolute23(destinationRelative)) {
107890
108011
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
107891
108012
  }
107892
108013
  let existingDigest = null;
@@ -108361,10 +108482,10 @@ var init_create_sync_config = __esm(() => {
108361
108482
  // src/lib/codex-hook-rpc.ts
108362
108483
  import { execFile, spawn as spawn3 } from "child_process";
108363
108484
  import { realpathSync as realpathSync12, statSync as statSync20 } from "fs";
108364
- import { isAbsolute as isAbsolute23 } from "path";
108485
+ import { isAbsolute as isAbsolute24 } from "path";
108365
108486
  import { promisify } from "util";
108366
108487
  async function connectCodexHookRpc(options) {
108367
- const found = isAbsolute23(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
108488
+ const found = isAbsolute24(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
108368
108489
  if (!found)
108369
108490
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
108370
108491
  const binary = realpathSync12(found), stat3 = statSync20(binary);
@@ -108500,10 +108621,10 @@ var init_codex_hook_rpc = __esm(() => {
108500
108621
 
108501
108622
  // src/lib/codex-hook-trust-identity.ts
108502
108623
  import { realpathSync as realpathSync13 } from "fs";
108503
- import { dirname as dirname30, isAbsolute as isAbsolute24, join as join56 } from "path";
108504
- import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
108624
+ import { dirname as dirname30, isAbsolute as isAbsolute25, join as join56 } from "path";
108625
+ import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
108505
108626
  function inspectRecordedSkillsCli(command, expected) {
108506
- need4(isAbsolute24(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
108627
+ need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
108507
108628
  const resolved = realpathSync13(command);
108508
108629
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
108509
108630
  const manifest = snapshot(join56(dirname30(dirname30(resolved)), "package.json"), false, { readOnlyPackage: true });
@@ -108511,7 +108632,7 @@ function inspectRecordedSkillsCli(command, expected) {
108511
108632
  need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join56(dirname30(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
108512
108633
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
108513
108634
  const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
108514
- need4(isDeepStrictEqual7(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
108635
+ need4(isDeepStrictEqual9(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
108515
108636
  return { receipt, recheck() {
108516
108637
  need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
108517
108638
  unchanged2(cli);
@@ -108522,7 +108643,7 @@ function bindSkillsCli(command, reviewed) {
108522
108643
  const normal = Bun.which("skills", { PATH: process.env.PATH });
108523
108644
  need4(normal, "SKILLS_COMMAND_UNAVAILABLE");
108524
108645
  const resolved = realpathSync13(normal);
108525
- const bound = isAbsolute24(command) ? command : command === "skills" ? normal : undefined;
108646
+ const bound = isAbsolute25(command) ? command : command === "skills" ? normal : undefined;
108526
108647
  need4(bound && realpathSync13(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
108527
108648
  const entrypoint = reviewed?.path ?? process.argv[1];
108528
108649
  need4(entrypoint && realpathSync13(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
@@ -108547,19 +108668,19 @@ var init_codex_hook_trust_identity = __esm(() => {
108547
108668
  });
108548
108669
 
108549
108670
  // src/lib/codex-hook-policy-recovery.ts
108550
- import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
108671
+ import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
108551
108672
  function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
108552
108673
  need4(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
108553
108674
  need4(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
108554
108675
  const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
108555
108676
  const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
108556
108677
  need4(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
108557
- const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual8(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
108678
+ const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual10(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
108558
108679
  need4(changes.length > 0, "RECONCILE_POLICY_CHANGED");
108559
108680
  const compared = structuredClone(current);
108560
108681
  compared.bridge.discovery.claude.roots = oldDiscovery.roots;
108561
108682
  compared.bridge.discovery.claude.sources = oldDiscovery.sources;
108562
- need4(isDeepStrictEqual8(before2, compared), "RECONCILE_POLICY_CHANGED");
108683
+ need4(isDeepStrictEqual10(before2, compared), "RECONCILE_POLICY_CHANGED");
108563
108684
  verifyAgentDiscovery(newDiscovery);
108564
108685
  return { reason: options.reason, changes };
108565
108686
  }
@@ -108569,76 +108690,13 @@ var init_codex_hook_policy_recovery = __esm(() => {
108569
108690
  init_agent_discovery();
108570
108691
  });
108571
108692
 
108572
- // src/lib/codex-hook-additions-review.ts
108573
- import { isAbsolute as isAbsolute25 } from "path";
108574
- import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
108575
- function exactKeys3(value, keys3) {
108576
- need4(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual9(Object.keys(value).sort(), [...keys3].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
108577
- }
108578
- function reviewNativeHookAdditions(options) {
108579
- need4(isAbsolute25(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
108580
- const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
108581
- exactKeys3(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
108582
- need4(review.version === 1 && review.kind === "native-hook-additions" && text3(review.reason) && text3(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
108583
- for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
108584
- need4(hash4(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
108585
- exactKeys3(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
108586
- const before2 = review.producerBefore;
108587
- need4(isAbsolute25(before2.configPath) && isAbsolute25(before2.hooksPath) && hash4(before2.configSha256) && hash4(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
108588
- const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
108589
- need4(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
108590
- const original = structure2(options.configBefore.text), producer = structure2(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
108591
- expectedProducer.hooks ??= {};
108592
- expectedProducer.hooks.state ??= {};
108593
- for (const hook of options.journalHooks)
108594
- expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
108595
- need4(isDeepStrictEqual9(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
108596
- need4(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
108597
- const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
108598
- need4(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
108599
- const added = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
108600
- for (const addition of review.additions) {
108601
- exactKeys3(addition, ["event", "nativeHook"]);
108602
- need4(Object.hasOwn(events, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
108603
- const { event, nativeHook: hook } = addition;
108604
- exactKeys3(hook, nativeKeys);
108605
- const groups = expectedHooks.hooks[event] ?? [];
108606
- need4(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
108607
- const key = `${options.nativeHooksPath}:${events[event][1]}:${groups.length}:0`;
108608
- need4(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
108609
- need4(hook.eventName === events[event][0] && hook.handlerType === "command" && text3(hook.command, 16384) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && hook.matcher === null && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && hook.sourcePath === options.nativeHooksPath && hook.source === "user" && hook.pluginId === null && hook.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash) && hook.enabled === true && hook.trustStatus === "trusted", "RECONCILE_ADDITIONS_IDENTITY");
108610
- seen.add(key);
108611
- commands.add(hook.command);
108612
- added.push(hook);
108613
- expectedHooks.hooks[event] = [...groups, { hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
108614
- expectedConfig.hooks.state[key] = { enabled: true, trusted_hash: hook.currentHash };
108615
- }
108616
- need4(isDeepStrictEqual9(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
108617
- const keys3 = added.map((hook) => hook.key);
108618
- need4(isDeepStrictEqual9(expectedConfig, structure2(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys3) === codexTrustReconcileWitness(options.configCurrent.text, keys3) && codexTrustUnmanagedSemantic(producerConfig.text, keys3) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys3), "RECONCILE_ADDITIONS_CONFIG");
108619
- const recheck = () => {
108620
- unchanged2(reviewFile);
108621
- unchanged2(producerConfig);
108622
- unchanged2(producerHooks);
108623
- };
108624
- recheck();
108625
- return { hooks: added, recheck, receipt: { reviewPath: reviewFile.file, reviewSha256: reviewFile.sha256, reason: review.reason, producerEvidence: review.producerEvidence, producerBefore: { configPath: producerConfig.file, configSha256: producerConfig.sha256, hooksPath: producerHooks.file, hooksSha256: producerHooks.sha256 }, additions: added } };
108626
- }
108627
- var events, hash4 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), text3 = (value, max2 = 2048) => typeof value === "string" && value.trim().length > 0 && value.length <= max2 && !/[\0\r\n]/.test(value), structure2 = (value) => JSON.parse(JSON.stringify(Bun.TOML.parse(value))), nativeKeys;
108628
- var init_codex_hook_additions_review = __esm(() => {
108629
- init_codex_hook_trust_files();
108630
- init_codex_hook_trust_layout();
108631
- events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
108632
- nativeKeys = ["key", "eventName", "handlerType", "command", "matcher", "timeoutSec", "async", "statusMessage", "additionalContextLimit", "sourcePath", "source", "pluginId", "isManaged", "currentHash", "enabled", "trustStatus"];
108633
- });
108634
-
108635
108693
  // src/lib/agent-codex-trust.ts
108636
108694
  import { lstatSync as lstatSync29, mkdirSync as mkdirSync25, readdirSync as readdirSync20, readFileSync as readFileSync37, realpathSync as realpathSync14, statSync as statSync21 } from "fs";
108637
108695
  import { createHash as createHash27, randomUUID as randomUUID10 } from "crypto";
108638
108696
  import { execFileSync } from "child_process";
108639
108697
  import { dirname as dirname31, isAbsolute as isAbsolute26, join as join57, resolve as resolve23 } from "path";
108640
108698
  import { homedir as homedir12 } from "os";
108641
- import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
108699
+ import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
108642
108700
  function verifyReleaseArtifact(proof, skillsCli) {
108643
108701
  need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
108644
108702
  const tarPath = realpathSync14(proof.packageTarPath);
@@ -108680,7 +108738,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108680
108738
  const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
108681
108739
  const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
108682
108740
  need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
108683
- need4(isDeepStrictEqual10(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
108741
+ need4(isDeepStrictEqual11(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
108684
108742
  need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
108685
108743
  const policyCurrent = snapshot(join57(dataDir3, "agent-policy.json"), true);
108686
108744
  const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
@@ -108693,7 +108751,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108693
108751
  if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
108694
108752
  need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
108695
108753
  const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
108696
- need4(isDeepStrictEqual10(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
108754
+ need4(isDeepStrictEqual11(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
108697
108755
  supersededBinding = true;
108698
108756
  }
108699
108757
  if (supersededBinding || recovery) {
@@ -108718,16 +108776,19 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108718
108776
  const before2 = structure3(configBefore.text), current = structure3(currentConfig.text), expectedConfig = structuredClone(before2);
108719
108777
  expectedConfig.hooks ??= {};
108720
108778
  expectedConfig.hooks.state ??= {};
108721
- for (const hook of managedHooks)
108779
+ for (const hook of intent.hooks)
108722
108780
  expectedConfig.hooks.state[hook.key] = { ...expectedConfig.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
108781
+ if (additions)
108782
+ for (const { key, state } of readReviewedAdditionTrustStates(additions.trustProof, configBefore, currentConfig, intent.hooks))
108783
+ expectedConfig.hooks.state[key] = { ...state };
108723
108784
  if (supersededBinding) {
108724
108785
  for (const hook of intent.hooks) {
108725
108786
  const state = current.hooks?.state?.[hook.key];
108726
108787
  need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
108727
108788
  }
108728
108789
  } else
108729
- need4(isDeepStrictEqual10(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
108730
- const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, managedHooks) : undefined;
108790
+ need4(isDeepStrictEqual11(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
108791
+ const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
108731
108792
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
108732
108793
  const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
108733
108794
  if (recoveryCli)
@@ -108735,14 +108796,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108735
108796
  const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
108736
108797
  if (supersededBinding)
108737
108798
  verifyReleaseArtifact(options.releaseProof, skillsCli);
108738
- const skillsBindingChanged = !isDeepStrictEqual10(skillsCli.receipt, intent.skillsCli);
108799
+ const skillsBindingChanged = !isDeepStrictEqual11(skillsCli.receipt, intent.skillsCli);
108739
108800
  need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
108740
108801
  if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
108741
108802
  need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
108742
108803
  const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
108743
108804
  for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
108744
108805
  const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
108745
- need4(isDeepStrictEqual10(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
108806
+ need4(isDeepStrictEqual11(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
108746
108807
  }
108747
108808
  }
108748
108809
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
@@ -108765,9 +108826,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108765
108826
  delete identity4.command;
108766
108827
  return identity4;
108767
108828
  };
108768
- need4(isDeepStrictEqual10(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
108829
+ need4(isDeepStrictEqual11(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
108769
108830
  if (!plannedKeys.has(expected.key))
108770
- need4(isDeepStrictEqual10(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
108831
+ need4(isDeepStrictEqual11(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
108771
108832
  }
108772
108833
  for (const wanted of intent.hooks) {
108773
108834
  const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join57(home, ".codex/hooks.json"), hooksPath));
@@ -108784,12 +108845,12 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108784
108845
  }
108785
108846
  const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
108786
108847
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
108787
- need4(layers?.length === 1 && !layers[0].disabledReason && isDeepStrictEqual10(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
108848
+ need4(layers?.length === 1 && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
108788
108849
  const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
108789
- need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual10(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
108850
+ need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual11(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
108790
108851
  const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
108791
108852
  const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
108792
- need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && isDeepStrictEqual10(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
108853
+ need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && isDeepStrictEqual11(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
108793
108854
  if (recovery) {
108794
108855
  for (const response of [config2, finalConfig]) {
108795
108856
  const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
@@ -108847,7 +108908,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108847
108908
  const groups = declarations[event];
108848
108909
  need4(Array.isArray(groups), "DECLARATION_CHANGED");
108849
108910
  const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
108850
- need4(matches2.length === 1 && isDeepStrictEqual10(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
108911
+ need4(matches2.length === 1 && isDeepStrictEqual11(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
108851
108912
  expected.push({ event: nativeEvent, key: `${join57(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
108852
108913
  }
108853
108914
  codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
@@ -108871,7 +108932,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108871
108932
  const discovered = await list2(), admitted = admit(discovered);
108872
108933
  const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
108873
108934
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
108874
- need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && isDeepStrictEqual10(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
108935
+ need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
108875
108936
  need4(config2.config && config2.config.features?.hooks !== false, "NATIVE_HOOKS_DISABLED");
108876
108937
  need4(config2.layers.slice(0, config2.layers.indexOf(layers[0])).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "NATIVE_CONFIG_OVERRIDDEN");
108877
108938
  const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
@@ -108910,7 +108971,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108910
108971
  save(join57(journal, "hooks.before.json"), hooksFile.bytes);
108911
108972
  save(join57(journal, "policy.before.json"), policyFile.bytes);
108912
108973
  save(join57(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
108913
- need4(isDeepStrictEqual10(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
108974
+ need4(isDeepStrictEqual11(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
108914
108975
  checkInputs();
108915
108976
  let result2;
108916
108977
  try {
@@ -108924,7 +108985,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108924
108985
  expectedConfig.hooks.state ??= {};
108925
108986
  for (const h of planned)
108926
108987
  expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
108927
- need4(isDeepStrictEqual10(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
108988
+ need4(isDeepStrictEqual11(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
108928
108989
  const savedConfig = snapshot(join57(journal, "config.before.toml"), true);
108929
108990
  need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
108930
108991
  const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
@@ -108932,8 +108993,8 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108932
108993
  unchanged2(hooksFile);
108933
108994
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
108934
108995
  const current = await list2(), confirmed = admit(current);
108935
- need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual10(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
108936
- need4(isDeepStrictEqual10(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
108996
+ need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual11(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
108997
+ need4(isDeepStrictEqual11(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
108937
108998
  unchanged2(after2);
108938
108999
  unchanged2(savedConfig);
108939
109000
  unchanged2(hooksFile);