@hasna/skills 0.9.17 → 0.9.19

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.17",
36871
+ version: "0.9.19",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -48622,7 +48622,8 @@ async function resolveSkillsConnection(env3 = process.env, options = {}) {
48622
48622
  }
48623
48623
  let completed;
48624
48624
  try {
48625
- completed = await completePointerCredential(SKILLS_APP, pointer, env3);
48625
+ const explicitVault = Boolean(snapshotEnv.HASNA_SECRETS_API_URL?.trim() && snapshotEnv.HASNA_SECRETS_API_KEY_OVERRIDE?.trim());
48626
+ completed = await completePointerCredential(SKILLS_APP, pointer, explicitVault ? snapshotEnv : env3);
48626
48627
  } catch (error) {
48627
48628
  const translated = asSkillsFleetCredentialError(error, pointer.pointerVaultKey);
48628
48629
  if (translated)
@@ -79049,7 +79050,20 @@ class RemoteSkillsClient {
79049
79050
  const url2 = new URL(upload.uploadUrl);
79050
79051
  if (url2.username || url2.password || url2.hash || url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname)))
79051
79052
  throw new Error("Unsafe input upload URL");
79052
- const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers: { "Content-Type": file2.contentType ?? "application/octet-stream" }, redirect: "error", signal: AbortSignal.timeout(60000) });
79053
+ const headers = new Headers({ "Content-Type": file2.contentType ?? "application/octet-stream" });
79054
+ if (upload.uploadHeaders !== undefined) {
79055
+ if (upload.uploadHeaders === null || typeof upload.uploadHeaders !== "object" || Array.isArray(upload.uploadHeaders))
79056
+ throw new Error("Invalid input upload headers");
79057
+ const seen = new Set;
79058
+ for (const [name, value] of Object.entries(upload.uploadHeaders)) {
79059
+ const lowerName = name.toLowerCase();
79060
+ if (seen.has(lowerName) || !["if-none-match", "x-amz-tagging"].includes(lowerName) || typeof value !== "string" || value.length === 0 || value.length > 2048 || !/^[\x20-\x7e]+$/.test(value) || value.trim() !== value || lowerName === "if-none-match" && value !== "*")
79061
+ throw new Error("Invalid input upload headers");
79062
+ seen.add(lowerName);
79063
+ headers.set(name, value);
79064
+ }
79065
+ }
79066
+ const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers, redirect: "error", signal: AbortSignal.timeout(60000) });
79053
79067
  if (!uploaded.ok)
79054
79068
  throw new Error("Input upload failed");
79055
79069
  await uploaded.body?.cancel();
@@ -108490,6 +108504,52 @@ var init_create_sync_config = __esm(() => {
108490
108504
  init_station_snapshot();
108491
108505
  });
108492
108506
 
108507
+ // src/lib/codex-native-config.ts
108508
+ import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
108509
+ function isRecord6(value) {
108510
+ if (!value || typeof value !== "object")
108511
+ return false;
108512
+ const prototype = Object.getPrototypeOf(value);
108513
+ return prototype === Object.prototype || prototype === null;
108514
+ }
108515
+ function decimalIdentity(text4) {
108516
+ if (text4.length > 4096)
108517
+ return;
108518
+ const match = /^(-?)(0|[1-9][0-9]*)(?:\.([0-9]+))?(?:[eE]([+-]?[0-9]+))?$/.exec(text4);
108519
+ if (!match || !Number.isFinite(Number(text4)))
108520
+ return;
108521
+ const fraction = match[3] ?? "";
108522
+ const digits = (match[2] + fraction).replace(/^0+/, "");
108523
+ if (!digits)
108524
+ return match[1] + "0";
108525
+ const coefficient = digits.replace(/0+$/, "");
108526
+ const exponent = BigInt(match[4] ?? "0") - BigInt(fraction.length) + BigInt(digits.length - coefficient.length);
108527
+ return `${match[1]}${coefficient}e${exponent}`;
108528
+ }
108529
+ function codexNativeConfigEqual(native, expected) {
108530
+ if (isDeepStrictEqual9(native, expected))
108531
+ return true;
108532
+ if (typeof expected === "number") {
108533
+ if (!Number.isFinite(expected) || !isRecord6(native))
108534
+ return false;
108535
+ const keys3 = Object.keys(native);
108536
+ const value = native[NUMBER_TAG];
108537
+ if (keys3.length !== 1 || keys3[0] !== NUMBER_TAG || typeof value !== "string")
108538
+ return false;
108539
+ const actual2 = decimalIdentity(value);
108540
+ return actual2 !== undefined && actual2 === decimalIdentity(Object.is(expected, -0) ? "-0" : String(expected));
108541
+ }
108542
+ if (Array.isArray(native) || Array.isArray(expected)) {
108543
+ return Array.isArray(native) && Array.isArray(expected) && native.length === expected.length && native.every((value, index) => codexNativeConfigEqual(value, expected[index]));
108544
+ }
108545
+ if (!isRecord6(native) || !isRecord6(expected))
108546
+ return false;
108547
+ const actual = native, wanted = expected;
108548
+ return Object.keys(actual).length === Object.keys(wanted).length && Object.keys(wanted).every((key) => Object.hasOwn(actual, key) && codexNativeConfigEqual(actual[key], wanted[key]));
108549
+ }
108550
+ var NUMBER_TAG = "$serde_json::private::Number";
108551
+ var init_codex_native_config = () => {};
108552
+
108493
108553
  // src/lib/codex-hook-rpc.ts
108494
108554
  import { execFile, spawn as spawn3 } from "child_process";
108495
108555
  import { realpathSync as realpathSync12, statSync as statSync20 } from "fs";
@@ -108633,7 +108693,7 @@ var init_codex_hook_rpc = __esm(() => {
108633
108693
  // src/lib/codex-hook-trust-identity.ts
108634
108694
  import { realpathSync as realpathSync13 } from "fs";
108635
108695
  import { dirname as dirname30, isAbsolute as isAbsolute25, join as join56 } from "path";
108636
- import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
108696
+ import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
108637
108697
  function inspectRecordedSkillsCli(command, expected) {
108638
108698
  need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
108639
108699
  const resolved = realpathSync13(command);
@@ -108643,7 +108703,7 @@ function inspectRecordedSkillsCli(command, expected) {
108643
108703
  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");
108644
108704
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
108645
108705
  const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
108646
- need4(isDeepStrictEqual9(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
108706
+ need4(isDeepStrictEqual10(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
108647
108707
  return { receipt, recheck() {
108648
108708
  need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
108649
108709
  unchanged2(cli);
@@ -108679,19 +108739,19 @@ var init_codex_hook_trust_identity = __esm(() => {
108679
108739
  });
108680
108740
 
108681
108741
  // src/lib/codex-hook-policy-recovery.ts
108682
- import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
108742
+ import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
108683
108743
  function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
108684
108744
  need4(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
108685
108745
  need4(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
108686
108746
  const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
108687
108747
  const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
108688
108748
  need4(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
108689
- const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual10(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
108749
+ const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual11(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
108690
108750
  need4(changes.length > 0, "RECONCILE_POLICY_CHANGED");
108691
108751
  const compared = structuredClone(current);
108692
108752
  compared.bridge.discovery.claude.roots = oldDiscovery.roots;
108693
108753
  compared.bridge.discovery.claude.sources = oldDiscovery.sources;
108694
- need4(isDeepStrictEqual10(before2, compared), "RECONCILE_POLICY_CHANGED");
108754
+ need4(isDeepStrictEqual11(before2, compared), "RECONCILE_POLICY_CHANGED");
108695
108755
  verifyAgentDiscovery(newDiscovery);
108696
108756
  return { reason: options.reason, changes };
108697
108757
  }
@@ -108707,7 +108767,7 @@ import { createHash as createHash27, randomUUID as randomUUID10 } from "crypto";
108707
108767
  import { execFileSync } from "child_process";
108708
108768
  import { dirname as dirname31, isAbsolute as isAbsolute26, join as join57, resolve as resolve23 } from "path";
108709
108769
  import { homedir as homedir12 } from "os";
108710
- import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
108770
+ import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
108711
108771
  function verifyReleaseArtifact(proof, skillsCli) {
108712
108772
  need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
108713
108773
  const tarPath = realpathSync14(proof.packageTarPath);
@@ -108749,7 +108809,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108749
108809
  const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
108750
108810
  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 };
108751
108811
  need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
108752
- need4(isDeepStrictEqual11(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
108812
+ need4(isDeepStrictEqual12(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
108753
108813
  need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
108754
108814
  const policyCurrent = snapshot(join57(dataDir3, "agent-policy.json"), true);
108755
108815
  const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
@@ -108762,7 +108822,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108762
108822
  if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
108763
108823
  need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
108764
108824
  const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
108765
- need4(isDeepStrictEqual11(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
108825
+ need4(isDeepStrictEqual12(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
108766
108826
  supersededBinding = true;
108767
108827
  }
108768
108828
  if (supersededBinding || recovery) {
@@ -108798,7 +108858,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108798
108858
  need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
108799
108859
  }
108800
108860
  } else
108801
- need4(isDeepStrictEqual11(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
108861
+ need4(isDeepStrictEqual12(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
108802
108862
  const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
108803
108863
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
108804
108864
  const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
@@ -108807,14 +108867,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108807
108867
  const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
108808
108868
  if (supersededBinding)
108809
108869
  verifyReleaseArtifact(options.releaseProof, skillsCli);
108810
- const skillsBindingChanged = !isDeepStrictEqual11(skillsCli.receipt, intent.skillsCli);
108870
+ const skillsBindingChanged = !isDeepStrictEqual12(skillsCli.receipt, intent.skillsCli);
108811
108871
  need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
108812
108872
  if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
108813
108873
  need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
108814
108874
  const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
108815
108875
  for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
108816
108876
  const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
108817
- need4(isDeepStrictEqual11(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
108877
+ need4(isDeepStrictEqual12(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
108818
108878
  }
108819
108879
  }
108820
108880
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
@@ -108837,9 +108897,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108837
108897
  delete identity4.command;
108838
108898
  return identity4;
108839
108899
  };
108840
- need4(isDeepStrictEqual11(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
108900
+ need4(isDeepStrictEqual12(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
108841
108901
  if (!plannedKeys.has(expected.key))
108842
- need4(isDeepStrictEqual11(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
108902
+ need4(isDeepStrictEqual12(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
108843
108903
  }
108844
108904
  for (const wanted of intent.hooks) {
108845
108905
  const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join57(home, ".codex/hooks.json"), hooksPath));
@@ -108856,12 +108916,12 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
108856
108916
  }
108857
108917
  const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
108858
108918
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
108859
- need4(layers?.length === 1 && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
108919
+ need4(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
108860
108920
  const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
108861
- 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");
108921
+ 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 || isDeepStrictEqual12(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
108862
108922
  const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
108863
108923
  const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
108864
- need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && isDeepStrictEqual11(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
108924
+ need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
108865
108925
  if (recovery) {
108866
108926
  for (const response of [config2, finalConfig]) {
108867
108927
  const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
@@ -108919,7 +108979,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108919
108979
  const groups = declarations[event];
108920
108980
  need4(Array.isArray(groups), "DECLARATION_CHANGED");
108921
108981
  const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
108922
- need4(matches2.length === 1 && isDeepStrictEqual11(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
108982
+ need4(matches2.length === 1 && isDeepStrictEqual12(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
108923
108983
  expected.push({ event: nativeEvent, key: `${join57(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
108924
108984
  }
108925
108985
  codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
@@ -108943,7 +109003,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108943
109003
  const discovered = await list2(), admitted = admit(discovered);
108944
109004
  const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
108945
109005
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
108946
- need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
109006
+ need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
108947
109007
  need4(config2.config && config2.config.features?.hooks !== false, "NATIVE_HOOKS_DISABLED");
108948
109008
  need4(config2.layers.slice(0, config2.layers.indexOf(layers[0])).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "NATIVE_CONFIG_OVERRIDDEN");
108949
109009
  const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
@@ -108982,7 +109042,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108982
109042
  save(join57(journal, "hooks.before.json"), hooksFile.bytes);
108983
109043
  save(join57(journal, "policy.before.json"), policyFile.bytes);
108984
109044
  save(join57(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
108985
- need4(isDeepStrictEqual11(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
109045
+ need4(isDeepStrictEqual12(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
108986
109046
  checkInputs();
108987
109047
  let result2;
108988
109048
  try {
@@ -108996,7 +109056,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
108996
109056
  expectedConfig.hooks.state ??= {};
108997
109057
  for (const h of planned)
108998
109058
  expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
108999
- need4(isDeepStrictEqual11(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
109059
+ need4(isDeepStrictEqual12(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
109000
109060
  const savedConfig = snapshot(join57(journal, "config.before.toml"), true);
109001
109061
  need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
109002
109062
  const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
@@ -109004,8 +109064,8 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109004
109064
  unchanged2(hooksFile);
109005
109065
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
109006
109066
  const current = await list2(), confirmed = admit(current);
109007
- 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");
109008
- need4(isDeepStrictEqual11(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
109067
+ need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual12(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
109068
+ need4(isDeepStrictEqual12(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
109009
109069
  unchanged2(after2);
109010
109070
  unchanged2(savedConfig);
109011
109071
  unchanged2(hooksFile);
@@ -109035,6 +109095,7 @@ var json2 = (value) => JSON.stringify(value, null, 2) + `
109035
109095
  return copy;
109036
109096
  };
109037
109097
  var init_agent_codex_trust = __esm(() => {
109098
+ init_codex_native_config();
109038
109099
  init_config();
109039
109100
  init_managed_policy();
109040
109101
  init_agent_integration();
@@ -110044,7 +110105,7 @@ function prepareSkill(name, options) {
110044
110105
  let manifest;
110045
110106
  try {
110046
110107
  const parsed = JSON.parse(manifestFile.bytes.toString("utf8"));
110047
- if (!isRecord6(parsed))
110108
+ if (!isRecord7(parsed))
110048
110109
  throw new Error("not an object");
110049
110110
  manifest = parsed;
110050
110111
  } catch {
@@ -110066,7 +110127,7 @@ function prepareSkill(name, options) {
110066
110127
  if (documentKind !== undefined && documentKind !== kind) {
110067
110128
  throw new Error("SKILL.md kind conflicts with the candidate skill.json kind. Review and align both declarations (or remove the optional frontmatter kind), then prepare again; no files were changed.");
110068
110129
  }
110069
- if (manifest.provenance !== undefined && !isRecord6(manifest.provenance))
110130
+ if (manifest.provenance !== undefined && !isRecord7(manifest.provenance))
110070
110131
  throw new Error("skill.json provenance must be an object.");
110071
110132
  const provenance = manifest.provenance;
110072
110133
  const candidate2 = { ...manifest, version: options.version, kind, provenance: { ...provenance } };
@@ -110131,7 +110192,7 @@ function escapedRelativePath(path) {
110131
110192
  return JSON.stringify(bounded);
110132
110193
  }
110133
110194
  function validateRuntimeEntrypoint(candidate2, root2) {
110134
- if (candidate2.kind !== "executable" || !isRecord6(candidate2.runtime))
110195
+ if (candidate2.kind !== "executable" || !isRecord7(candidate2.runtime))
110135
110196
  return;
110136
110197
  const entry = candidate2.runtime.entrypoint;
110137
110198
  if (typeof entry !== "string")
@@ -110141,7 +110202,7 @@ function validateRuntimeEntrypoint(candidate2, root2) {
110141
110202
  throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
110142
110203
  }
110143
110204
  }
110144
- function isRecord6(value) {
110205
+ function isRecord7(value) {
110145
110206
  return typeof value === "object" && value !== null && !Array.isArray(value);
110146
110207
  }
110147
110208
  function validateRawCandidate(candidate2) {
@@ -110166,7 +110227,7 @@ function validateRawCandidate(candidate2) {
110166
110227
  if (!Array.isArray(items))
110167
110228
  throw new Error(`skill.json ${field2} must be an array.`);
110168
110229
  for (const item of items) {
110169
- if (!isRecord6(item) || typeof item.name !== "string" || !item.name.trim())
110230
+ if (!isRecord7(item) || typeof item.name !== "string" || !item.name.trim())
110170
110231
  throw new Error(`Invalid skill.json ${field2} name.`);
110171
110232
  if (field2 === "inputs" && (typeof item.type !== "string" || !item.type.trim() || item.required !== undefined && typeof item.required !== "boolean"))
110172
110233
  throw new Error("Invalid skill.json input type or required flag.");
@@ -110185,7 +110246,7 @@ function validateRawCandidate(candidate2) {
110185
110246
  }
110186
110247
  }
110187
110248
  function validateRuntimeEntrypointCompatibility(candidate2) {
110188
- if (candidate2.kind !== "executable" || !isRecord6(candidate2.runtime))
110249
+ if (candidate2.kind !== "executable" || !isRecord7(candidate2.runtime))
110189
110250
  return;
110190
110251
  const runtime = candidate2.runtime.runtime;
110191
110252
  const entrypoint = candidate2.runtime.entrypoint;
@@ -111400,7 +111461,7 @@ async function apiRequest(path, options, instance) {
111400
111461
  const origin = instance ?? getApiUrl(`${(options?.method || "GET").toUpperCase()} ${path}`);
111401
111462
  return new RemoteSkillsAuthClient(origin).request(path, options);
111402
111463
  }
111403
- function isRecord7(value) {
111464
+ function isRecord8(value) {
111404
111465
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
111405
111466
  }
111406
111467
  function commandErrorPayload(err, fallback) {
@@ -111414,7 +111475,7 @@ function commandErrorPayload(err, fallback) {
111414
111475
  ...err.apiUrl ? { apiUrl: err.apiUrl } : {}
111415
111476
  };
111416
111477
  }
111417
- const code2 = isRecord7(err) && typeof err.code === "string" ? err.code : undefined;
111478
+ const code2 = isRecord8(err) && typeof err.code === "string" ? err.code : undefined;
111418
111479
  return {
111419
111480
  error: err?.message || fallback,
111420
111481
  ...code2 ? { code: code2 } : {}
@@ -111442,7 +111503,7 @@ function stringField2(value) {
111442
111503
  return typeof value === "string" && value.length > 0 ? value : undefined;
111443
111504
  }
111444
111505
  function recordField(value) {
111445
- return isRecord7(value) ? value : undefined;
111506
+ return isRecord8(value) ? value : undefined;
111446
111507
  }
111447
111508
  function identityApiKey(live) {
111448
111509
  const root2 = recordField(live);
@@ -111932,7 +111993,7 @@ function registerAuth(parent) {
111932
111993
  } catch {}
111933
111994
  payload.permissions = { publish: access?.permissions?.publish ?? null, profilesWrite: access?.permissions?.profilesWrite ?? null };
111934
111995
  const apiKey = identityApiKey(res);
111935
- if (access?.scopes && (apiKey === undefined || isRecord7(apiKey) && !Object.hasOwn(apiKey, "scopes"))) {
111996
+ if (access?.scopes && (apiKey === undefined || isRecord8(apiKey) && !Object.hasOwn(apiKey, "scopes"))) {
111936
111997
  payload.scopes = access.scopes;
111937
111998
  }
111938
111999
  if (options.json) {
package/bin/mcp.js CHANGED
@@ -20019,7 +20019,7 @@ var package_default;
20019
20019
  var init_package = __esm(() => {
20020
20020
  package_default = {
20021
20021
  name: "@hasna/skills",
20022
- version: "0.9.17",
20022
+ version: "0.9.19",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -21311,7 +21311,8 @@ async function resolveSkillsConnection(env = process.env, options = {}) {
21311
21311
  }
21312
21312
  let completed;
21313
21313
  try {
21314
- completed = await completePointerCredential(SKILLS_APP, pointer, env);
21314
+ const explicitVault = Boolean(snapshotEnv.HASNA_SECRETS_API_URL?.trim() && snapshotEnv.HASNA_SECRETS_API_KEY_OVERRIDE?.trim());
21315
+ completed = await completePointerCredential(SKILLS_APP, pointer, explicitVault ? snapshotEnv : env);
21315
21316
  } catch (error61) {
21316
21317
  const translated = asSkillsFleetCredentialError(error61, pointer.pointerVaultKey);
21317
21318
  if (translated)
@@ -52027,7 +52028,20 @@ class RemoteSkillsClient {
52027
52028
  const url2 = new URL(upload.uploadUrl);
52028
52029
  if (url2.username || url2.password || url2.hash || url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname)))
52029
52030
  throw new Error("Unsafe input upload URL");
52030
- const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers: { "Content-Type": file2.contentType ?? "application/octet-stream" }, redirect: "error", signal: AbortSignal.timeout(60000) });
52031
+ const headers = new Headers({ "Content-Type": file2.contentType ?? "application/octet-stream" });
52032
+ if (upload.uploadHeaders !== undefined) {
52033
+ if (upload.uploadHeaders === null || typeof upload.uploadHeaders !== "object" || Array.isArray(upload.uploadHeaders))
52034
+ throw new Error("Invalid input upload headers");
52035
+ const seen = new Set;
52036
+ for (const [name, value] of Object.entries(upload.uploadHeaders)) {
52037
+ const lowerName = name.toLowerCase();
52038
+ if (seen.has(lowerName) || !["if-none-match", "x-amz-tagging"].includes(lowerName) || typeof value !== "string" || value.length === 0 || value.length > 2048 || !/^[\x20-\x7e]+$/.test(value) || value.trim() !== value || lowerName === "if-none-match" && value !== "*")
52039
+ throw new Error("Invalid input upload headers");
52040
+ seen.add(lowerName);
52041
+ headers.set(name, value);
52042
+ }
52043
+ }
52044
+ const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers, redirect: "error", signal: AbortSignal.timeout(60000) });
52031
52045
  if (!uploaded.ok)
52032
52046
  throw new Error("Input upload failed");
52033
52047
  await uploaded.body?.cancel();
package/bin/migrate.js CHANGED
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.9.17",
10
+ version: "0.9.19",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
package/bin/server.js CHANGED
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
24122
24122
  // package.json
24123
24123
  var package_default = {
24124
24124
  name: "@hasna/skills",
24125
- version: "0.9.17",
24125
+ version: "0.9.19",
24126
24126
  description: "Skills library for AI coding agents",
24127
24127
  type: "module",
24128
24128
  bin: {
package/bin/worker.js CHANGED
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24125
24125
  // package.json
24126
24126
  var package_default = {
24127
24127
  name: "@hasna/skills",
24128
- version: "0.9.17",
24128
+ version: "0.9.19",
24129
24129
  description: "Skills library for AI coding agents",
24130
24130
  type: "module",
24131
24131
  bin: {
package/dist/index.js CHANGED
@@ -9611,7 +9611,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
9611
9611
  // package.json
9612
9612
  var package_default = {
9613
9613
  name: "@hasna/skills",
9614
- version: "0.9.17",
9614
+ version: "0.9.19",
9615
9615
  description: "Skills library for AI coding agents",
9616
9616
  type: "module",
9617
9617
  bin: {
@@ -10891,7 +10891,8 @@ async function resolveSkillsConnection(env = process.env, options = {}) {
10891
10891
  }
10892
10892
  let completed;
10893
10893
  try {
10894
- completed = await completePointerCredential(SKILLS_APP, pointer, env);
10894
+ const explicitVault = Boolean(snapshotEnv.HASNA_SECRETS_API_URL?.trim() && snapshotEnv.HASNA_SECRETS_API_KEY_OVERRIDE?.trim());
10895
+ completed = await completePointerCredential(SKILLS_APP, pointer, explicitVault ? snapshotEnv : env);
10895
10896
  } catch (error) {
10896
10897
  const translated = asSkillsFleetCredentialError(error, pointer.pointerVaultKey);
10897
10898
  if (translated)
@@ -39095,7 +39096,20 @@ class RemoteSkillsClient {
39095
39096
  const url2 = new URL(upload.uploadUrl);
39096
39097
  if (url2.username || url2.password || url2.hash || url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname)))
39097
39098
  throw new Error("Unsafe input upload URL");
39098
- const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers: { "Content-Type": file2.contentType ?? "application/octet-stream" }, redirect: "error", signal: AbortSignal.timeout(60000) });
39099
+ const headers = new Headers({ "Content-Type": file2.contentType ?? "application/octet-stream" });
39100
+ if (upload.uploadHeaders !== undefined) {
39101
+ if (upload.uploadHeaders === null || typeof upload.uploadHeaders !== "object" || Array.isArray(upload.uploadHeaders))
39102
+ throw new Error("Invalid input upload headers");
39103
+ const seen = new Set;
39104
+ for (const [name, value] of Object.entries(upload.uploadHeaders)) {
39105
+ const lowerName = name.toLowerCase();
39106
+ if (seen.has(lowerName) || !["if-none-match", "x-amz-tagging"].includes(lowerName) || typeof value !== "string" || value.length === 0 || value.length > 2048 || !/^[\x20-\x7e]+$/.test(value) || value.trim() !== value || lowerName === "if-none-match" && value !== "*")
39107
+ throw new Error("Invalid input upload headers");
39108
+ seen.add(lowerName);
39109
+ headers.set(name, value);
39110
+ }
39111
+ }
39112
+ const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers, redirect: "error", signal: AbortSignal.timeout(60000) });
39099
39113
  if (!uploaded.ok)
39100
39114
  throw new Error("Input upload failed");
39101
39115
  await uploaded.body?.cancel();
@@ -0,0 +1,5 @@
1
+ /** Codex config/read can expose serde_json's numeric wrapper for TOML floats.
2
+ * Admit it only against an equal numeric value from the complete disk parse.
3
+ * Ordinary objects, strings, native trust state and unknown fields stay exact.
4
+ */
5
+ export declare function codexNativeConfigEqual(native: unknown, expected: unknown): boolean;
package/dist/sdk/index.js CHANGED
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
32373
32373
  // package.json
32374
32374
  var package_default = {
32375
32375
  name: "@hasna/skills",
32376
- version: "0.9.17",
32376
+ version: "0.9.19",
32377
32377
  description: "Skills library for AI coding agents",
32378
32378
  type: "module",
32379
32379
  bin: {
@@ -33712,7 +33712,8 @@ async function resolveSkillsConnection(env = process.env, options = {}) {
33712
33712
  }
33713
33713
  let completed;
33714
33714
  try {
33715
- completed = await completePointerCredential(SKILLS_APP, pointer, env);
33715
+ const explicitVault = Boolean(snapshotEnv.HASNA_SECRETS_API_URL?.trim() && snapshotEnv.HASNA_SECRETS_API_KEY_OVERRIDE?.trim());
33716
+ completed = await completePointerCredential(SKILLS_APP, pointer, explicitVault ? snapshotEnv : env);
33716
33717
  } catch (error) {
33717
33718
  const translated = asSkillsFleetCredentialError(error, pointer.pointerVaultKey);
33718
33719
  if (translated)
@@ -89665,7 +89666,20 @@ class RemoteSkillsClient {
89665
89666
  const url2 = new URL(upload.uploadUrl);
89666
89667
  if (url2.username || url2.password || url2.hash || url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname)))
89667
89668
  throw new Error("Unsafe input upload URL");
89668
- const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers: { "Content-Type": file2.contentType ?? "application/octet-stream" }, redirect: "error", signal: AbortSignal.timeout(60000) });
89669
+ const headers = new Headers({ "Content-Type": file2.contentType ?? "application/octet-stream" });
89670
+ if (upload.uploadHeaders !== undefined) {
89671
+ if (upload.uploadHeaders === null || typeof upload.uploadHeaders !== "object" || Array.isArray(upload.uploadHeaders))
89672
+ throw new Error("Invalid input upload headers");
89673
+ const seen = new Set;
89674
+ for (const [name, value] of Object.entries(upload.uploadHeaders)) {
89675
+ const lowerName = name.toLowerCase();
89676
+ if (seen.has(lowerName) || !["if-none-match", "x-amz-tagging"].includes(lowerName) || typeof value !== "string" || value.length === 0 || value.length > 2048 || !/^[\x20-\x7e]+$/.test(value) || value.trim() !== value || lowerName === "if-none-match" && value !== "*")
89677
+ throw new Error("Invalid input upload headers");
89678
+ seen.add(lowerName);
89679
+ headers.set(name, value);
89680
+ }
89681
+ }
89682
+ const uploaded = await fetch(url2, { method: "PUT", body: file2.bytes, headers, redirect: "error", signal: AbortSignal.timeout(60000) });
89669
89683
  if (!uploaded.ok)
89670
89684
  throw new Error("Input upload failed");
89671
89685
  await uploaded.body?.cancel();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.9.17",
3
+ "version": "0.9.19",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {