@hasna/skills 0.10.88 → 0.10.90

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
@@ -36888,7 +36888,7 @@ var package_default;
36888
36888
  var init_package = __esm(() => {
36889
36889
  package_default = {
36890
36890
  name: "@hasna/skills",
36891
- version: "0.10.88",
36891
+ version: "0.10.90",
36892
36892
  description: "Skills library for AI coding agents",
36893
36893
  type: "module",
36894
36894
  bin: {
@@ -40737,7 +40737,8 @@ var init_codex_native_compatibility = __esm(() => {
40737
40737
  "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40738
40738
  "codex-cli 0.163.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40739
40739
  "codex-cli 0.164.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40740
- "codex-cli 0.165.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
40740
+ "codex-cli 0.165.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40741
+ "codex-cli 0.166.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
40741
40742
  };
40742
40743
  SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
40743
40744
  });
@@ -63619,6 +63620,7 @@ var init_selected_document = __esm(() => {
63619
63620
  });
63620
63621
 
63621
63622
  // src/lib/selection-resolver.ts
63623
+ import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
63622
63624
  async function syncSelectionProfile(profileId, options = {}) {
63623
63625
  const client = options.client ?? await createProfileClient();
63624
63626
  const profile = await client.resolveProfile(profileId);
@@ -63743,21 +63745,70 @@ function unknownNameRefusal(slug, profile) {
63743
63745
  return `Skill '${slug}' is not selected by this Skills profile or project lock.${hint} ` + `Read the accepted names with 'skills search <term>' or 'skills list --all --json'; accept one with 'skills pin <name>'. ${ACCEPTED_FORM}`;
63744
63746
  }
63745
63747
  async function loadSelectedSkill(spec, profileId, options = {}) {
63746
- const context = await resolveSelectionContext(profileId, options);
63748
+ if (!options.cacheFirst)
63749
+ return readSelectedSkill(spec, profileId, options);
63750
+ const { cacheFirst: _cacheFirst, cached: _cached, authority, ...service } = options;
63751
+ try {
63752
+ return await readSelectedSkill(spec, profileId, { ...service, authority, cached: true });
63753
+ } catch (cacheError) {
63754
+ if (!(cacheError instanceof SkillSelectionError) || !RECOVERABLE_CACHE_ERRORS.has(cacheError.code))
63755
+ throw cacheError;
63756
+ try {
63757
+ return await readSelectedSkill(spec, profileId, service);
63758
+ } catch (serviceError) {
63759
+ if (!(serviceError instanceof SkillSelectionError) || serviceError.code !== "SKILLS_API_UNAVAILABLE")
63760
+ throw serviceError;
63761
+ throw new SkillSelectionError(cacheError.code, "The verified cache can't serve this load and the Skills service can't be reached from here, so it has to run where the network is available. `skills sync` doesn't renew a session's pin.");
63762
+ }
63763
+ }
63764
+ }
63765
+ async function readSelectedSkill(spec, profileId, options) {
63766
+ const context = await resolveSelectionContext(profileId, options).catch((error) => {
63767
+ if (options.cached && options.sessionId && error instanceof SkillSelectionError && error.code === "CACHED_PROFILE_EXPIRED" && readSkillSessionSnapshotIfExists(options.sessionId, options)) {
63768
+ throw new SkillSelectionError("CACHED_PROFILE_EXPIRED", "This session's Skills pin was verified too long ago to read from the cache. Run the same command without --cached: that keeps the pin but needs the network and sign-in. `skills sync` doesn't renew a session's pin.");
63769
+ }
63770
+ throw error;
63771
+ });
63747
63772
  const selection = exactProfileSelection(spec, context.receipt.profile);
63748
63773
  const entries = await readSelectedEntries(selection, context, options);
63749
63774
  const { file, content } = readSelectedDocument(entries, options.file);
63750
63775
  const receipt = { ...selection, file, source: options.cached ? "verified-cache" : "api", verifiedAt: context.receipt.verifiedAt };
63751
- if (options.sessionId) {
63752
- const session = {
63753
- ...context.receipt,
63754
- sessionId: options.sessionId,
63755
- loaded: [...new Set([...context.session?.loaded ?? [], selectionKey(selection)])],
63756
- ...context.projectLock ? { projectLock: context.projectLock } : {}
63757
- };
63758
- writeSkillSession(session, { current: context.sessionSnapshot, parent: context.parentSnapshot }, options);
63776
+ const sessionRecorded = options.sessionId ? recordSessionLoad(options.sessionId, selection, context, options) : undefined;
63777
+ return { content, selection, receipt, ...sessionRecorded === undefined ? {} : { sessionRecorded } };
63778
+ }
63779
+ function isUnwritableStore(error) {
63780
+ for (let cause = error, depth = 0;cause && depth < 4; cause = cause.cause, depth++)
63781
+ if (UNWRITABLE_CODES.has(cause.code))
63782
+ return true;
63783
+ return false;
63784
+ }
63785
+ function recordSessionLoad(sessionId, selection, context, options) {
63786
+ const key2 = selectionKey(selection);
63787
+ let snapshot2 = context.sessionSnapshot;
63788
+ for (let attempt2 = 0;attempt2 < SESSION_LOAD_RECORD_ATTEMPTS; attempt2++) {
63789
+ try {
63790
+ const session = {
63791
+ ...context.receipt,
63792
+ sessionId,
63793
+ loaded: [...new Set([...context.session?.loaded ?? [], key2])],
63794
+ ...context.projectLock ? { projectLock: context.projectLock } : {}
63795
+ };
63796
+ writeSkillSession(session, { current: snapshot2, parent: context.parentSnapshot }, options);
63797
+ return true;
63798
+ } catch (error) {
63799
+ const blocked = isUnwritableStore(error) || error instanceof SkillSelectionError && error.code === "SESSION_WRITE_BUSY";
63800
+ if (!blocked && !(error instanceof SkillSelectionError && error.code === "SESSION_CONTEXT_CONFLICT"))
63801
+ throw error;
63802
+ const current = readSkillSessionSnapshotIfExists(sessionId, options);
63803
+ if (current ? !isDeepStrictEqual11(current.receipt.profile, context.receipt.profile) : !blocked) {
63804
+ throw new SkillSelectionError("SESSION_CONTEXT_CONFLICT", "The Skills session's pin changed while this skill was loading, so its content no longer belongs to that pin. Load it again.");
63805
+ }
63806
+ if (blocked)
63807
+ return false;
63808
+ snapshot2 = skillSessionSnapshotBinding(current);
63809
+ }
63759
63810
  }
63760
- return { content, selection, receipt };
63811
+ return false;
63761
63812
  }
63762
63813
  async function pinSelectedSkillVersion(spec, profileId, options) {
63763
63814
  const context = await resolveSelectionContext(profileId, options);
@@ -63786,13 +63837,15 @@ async function selectedSkillRequirements(spec, profileId, options = {}) {
63786
63837
  }
63787
63838
  return { ...getSkillRequirementsFromContent(selection.slug, texts, dependencies), selection };
63788
63839
  }
63789
- var ACCEPTED_FORM = "Accepted form: '<slug>' or '<slug>@<version>'.";
63840
+ var ACCEPTED_FORM = "Accepted form: '<slug>' or '<slug>@<version>'.", RECOVERABLE_CACHE_ERRORS, SESSION_LOAD_RECORD_ATTEMPTS = 8, UNWRITABLE_CODES;
63790
63841
  var init_selection_resolver = __esm(() => {
63791
63842
  init_profile_client();
63792
63843
  init_skillinfo();
63793
63844
  init_search();
63794
63845
  init_selected_document();
63795
63846
  init_selection_cache();
63847
+ RECOVERABLE_CACHE_ERRORS = new Set(["CACHED_PROFILE_EXPIRED", "CACHED_PROFILE_MISSING", "CACHED_BUNDLE_MISSING"]);
63848
+ UNWRITABLE_CODES = new Set(["EACCES", "EPERM", "EROFS", "ENOSPC", "EDQUOT"]);
63796
63849
  });
63797
63850
 
63798
63851
  // src/lib/selected-manifest.ts
@@ -64534,7 +64587,7 @@ var init_claude_marketplace_registry = __esm(() => {
64534
64587
  });
64535
64588
 
64536
64589
  // src/lib/codex-hook-trust-layout.ts
64537
- import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
64590
+ import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
64538
64591
  function codexTrustTextWitness(text2, keys3, canonicalize = true) {
64539
64592
  const parsed = Bun.TOML.parse(text2), wanted = new Set(keys3);
64540
64593
  const headers = [...text2.matchAll(/^[ \t]*(\[[^\r\n]+\])[ \t]*(#[^\r\n]*)?(?:\r?\n|$)/gm)];
@@ -64599,7 +64652,7 @@ function codexTrustTextWitness(text2, keys3, canonicalize = true) {
64599
64652
  if (expected.hooks)
64600
64653
  delete expected.hooks.state;
64601
64654
  try {
64602
- if (!isDeepStrictEqual11(prune(Bun.TOML.parse(noState)), prune(expected)))
64655
+ if (!isDeepStrictEqual12(prune(Bun.TOML.parse(noState)), prune(expected)))
64603
64656
  throw fail3();
64604
64657
  } catch {
64605
64658
  throw fail3();
@@ -64669,13 +64722,13 @@ var init_codex_hook_trust_layout = () => {};
64669
64722
 
64670
64723
  // src/lib/codex-hook-additions-review.ts
64671
64724
  import { isAbsolute as isAbsolute29 } from "path";
64672
- import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
64725
+ import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
64673
64726
  function exactKeys3(value, keys3) {
64674
- need8(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual12(Object.keys(value).sort(), [...keys3].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
64727
+ need8(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual13(Object.keys(value).sort(), [...keys3].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
64675
64728
  }
64676
64729
  function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
64677
64730
  const captured = proofs.get(proof);
64678
- need8(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual12(captured.journalHooks, journalHooks.map(({ key: key2, currentHash }) => ({ key: key2, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
64731
+ need8(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual13(captured.journalHooks, journalHooks.map(({ key: key2, currentHash }) => ({ key: key2, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
64679
64732
  captured.recheck();
64680
64733
  unchanged2(before2);
64681
64734
  unchanged2(current);
@@ -64698,7 +64751,7 @@ function reviewNativeHookAdditions(options) {
64698
64751
  expectedProducer.hooks.state ??= {};
64699
64752
  for (const hook of options.journalHooks)
64700
64753
  expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
64701
- need8(isDeepStrictEqual12(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
64754
+ need8(isDeepStrictEqual13(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
64702
64755
  need8(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
64703
64756
  const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
64704
64757
  need8(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
@@ -64727,9 +64780,9 @@ function reviewNativeHookAdditions(options) {
64727
64780
  expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
64728
64781
  expectedConfig.hooks.state[key2] = trustState;
64729
64782
  }
64730
- need8(isDeepStrictEqual12(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
64783
+ need8(isDeepStrictEqual13(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
64731
64784
  const keys3 = added.map((hook) => hook.key);
64732
- need8(isDeepStrictEqual12(expectedConfig, structure(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys3) === codexTrustReconcileWitness(options.configCurrent.text, keys3) && codexTrustUnmanagedSemantic(producerConfig.text, keys3) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys3), "RECONCILE_ADDITIONS_CONFIG");
64785
+ need8(isDeepStrictEqual13(expectedConfig, structure(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys3) === codexTrustReconcileWitness(options.configCurrent.text, keys3) && codexTrustUnmanagedSemantic(producerConfig.text, keys3) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys3), "RECONCILE_ADDITIONS_CONFIG");
64733
64786
  const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
64734
64787
  const recheck = () => {
64735
64788
  for (const input of inputs)
@@ -64752,7 +64805,7 @@ var init_codex_hook_additions_review = __esm(() => {
64752
64805
 
64753
64806
  // src/lib/codex-hook-discovery-recovery.ts
64754
64807
  import { createHash as createHash32 } from "crypto";
64755
- import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
64808
+ import { isDeepStrictEqual as isDeepStrictEqual14 } from "util";
64756
64809
  function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, additions) {
64757
64810
  const journalHooks = hooks.map(({ key: key2, currentHash }) => ({ key: key2, currentHash }));
64758
64811
  const additionStates = additions ? readReviewedAdditionTrustStates(additions, before2, current, journalHooks) : [];
@@ -64766,11 +64819,11 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
64766
64819
  need8(binding.agent === "codex" && binding.method === "reviewed" && raw.length === 1 && raw[0].sha256 === before2.sha256 && before2.sha256 !== current.sha256, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
64767
64820
  need8(raw[0].format === undefined && raw[0].fields === undefined && raw[0].managedPlugins === undefined, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
64768
64821
  const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
64769
- need8(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual13(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
64822
+ need8(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual14(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
64770
64823
  const original = structure2(before2.text), actual = structure2(current.text);
64771
64824
  const project = (value) => Object.fromEntries(fields.map((field3) => [field3, value[field3] ?? null]));
64772
64825
  const projection = sha5(JSON.stringify(project(original)));
64773
- need8(typed[0].sha256 === projection && sha5(JSON.stringify(project(actual))) === projection && isDeepStrictEqual13(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
64826
+ need8(typed[0].sha256 === projection && sha5(JSON.stringify(project(actual))) === projection && isDeepStrictEqual14(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
64774
64827
  const keys3 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
64775
64828
  need8(journalHooks.length > 0 && keys3.length <= 100 && new Set(keys3).size === keys3.length && journalHooks.every((hook) => typeof hook.key === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash)), "RECONCILE_DISCOVERY_TRUST_WITNESS");
64776
64829
  const expected = structuredClone(original);
@@ -64780,7 +64833,7 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
64780
64833
  expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
64781
64834
  for (const { key: key2, state } of additionStates)
64782
64835
  expected.hooks.state[key2] = { ...state };
64783
- need8(isDeepStrictEqual13(expected, actual) && codexTrustReconcileWitness(before2.text, keys3) === codexTrustReconcileWitness(current.text, keys3) && codexTrustUnmanagedSemantic(before2.text, keys3) === codexTrustUnmanagedSemantic(current.text, keys3), "RECONCILE_DISCOVERY_TRUST_WITNESS");
64836
+ need8(isDeepStrictEqual14(expected, actual) && codexTrustReconcileWitness(before2.text, keys3) === codexTrustReconcileWitness(current.text, keys3) && codexTrustUnmanagedSemantic(before2.text, keys3) === codexTrustUnmanagedSemantic(current.text, keys3), "RECONCILE_DISCOVERY_TRUST_WITNESS");
64784
64837
  unchanged2(before2);
64785
64838
  unchanged2(current);
64786
64839
  const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256: discoveryDigest(binding), typedProjectionSha256: projection });
@@ -64822,7 +64875,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
64822
64875
  import { createHash as createHash33 } from "crypto";
64823
64876
  import { lstatSync as lstatSync35, opendirSync as opendirSync6, readFileSync as readFileSync27, readdirSync as readdirSync16, realpathSync as realpathSync12 } from "fs";
64824
64877
  import { basename as basename18, dirname as dirname29, isAbsolute as isAbsolute30, join as join46, relative as relative9, resolve as resolve29, sep as sep14 } from "path";
64825
- import { isDeepStrictEqual as isDeepStrictEqual14 } from "util";
64878
+ import { isDeepStrictEqual as isDeepStrictEqual15 } from "util";
64826
64879
  function discoveryRootUnresolved(agent, command, detail) {
64827
64880
  return new Error(`${DISCOVERY_ROOT_UNRESOLVED}: ${agent} discovery cannot resolve its "${command}" executable: ${detail}. The reviewed discovery is unchanged; this is an environment gap, not drift.`);
64828
64881
  }
@@ -64975,8 +65028,8 @@ function projectNativeDiscoveryFields(text3, format, fields2, path) {
64975
65028
  }
64976
65029
  function isSupersededSettingsProjection(agent, configPath, source) {
64977
65030
  if (agent === "sumi")
64978
- return source.path === configPath && source.format === "json" && isDeepStrictEqual14(source.fields, [...SUMI_DISCOVERY_PROJECTION_FIELDS]);
64979
- return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual14(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
65031
+ return source.path === configPath && source.format === "json" && isDeepStrictEqual15(source.fields, [...SUMI_DISCOVERY_PROJECTION_FIELDS]);
65032
+ return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual15(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
64980
65033
  }
64981
65034
  function supersedesSettingsProjection(agent, sources, configPath) {
64982
65035
  if (agent === "sumi")
@@ -64997,7 +65050,7 @@ function projected(source, changes, budget3 = discoveryByteBudget()) {
64997
65050
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || !Array.isArray(source.hookSources))
64998
65051
  throw new Error("Codex V6 requires exact reviewed hook sources");
64999
65052
  const current = captureCodexSettingsV6(source.path, source.hookSources, budget3);
65000
- if (current.sha256 !== source.sha256 || !isDeepStrictEqual14(current.hookSources, source.hookSources))
65053
+ if (current.sha256 !== source.sha256 || !isDeepStrictEqual15(current.hookSources, source.hookSources))
65001
65054
  throw new Error("Codex hook provider or dependency changed; explicit discovery review required");
65002
65055
  return changes ? captureCodexSettingsV6(source.path, source.hookSources, budget3, changes).sha256 : current.sha256;
65003
65056
  }
@@ -65201,7 +65254,7 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
65201
65254
  return current;
65202
65255
  const installation = codexInstallationRoots(binding);
65203
65256
  const directories = (binding.directories ?? []).filter((directory2) => !installation.some((root2) => directory2.path === root2 || directory2.path.startsWith(root2 + sep14) || root2.startsWith(directory2.path + sep14)));
65204
- if (previous && isDeepStrictEqual14(previous.roots, roots) && isDeepStrictEqual14(previous.parents, parents3) && isDeepStrictEqual14(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
65257
+ if (previous && isDeepStrictEqual15(previous.roots, roots) && isDeepStrictEqual15(previous.parents, parents3) && isDeepStrictEqual15(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
65205
65258
  return { ...current, codexRetiredMaterializations: previous };
65206
65259
  return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
65207
65260
  }
@@ -65322,7 +65375,7 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases, narrowe
65322
65375
  if (reviewedRootAliases !== undefined) {
65323
65376
  const alias = reviewedRootAliases.find((alias2) => alias2.agent === "codex");
65324
65377
  const expected = alias ? { alias: alias.alias, target: alias.target } : null;
65325
- if (!isDeepStrictEqual14(proof.hookRootAlias, expected))
65378
+ if (!isDeepStrictEqual15(proof.hookRootAlias, expected))
65326
65379
  throw new Error("Semantic cache hook alias differs from reviewed root binding");
65327
65380
  }
65328
65381
  const controls = binding.codexDisabledPluginSkills;
@@ -65332,7 +65385,7 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases, narrowe
65332
65385
  if (controls.some((control) => dirname29(dirname29(control.pluginParent)) !== cacheRoot))
65333
65386
  throw new Error("Codex semantic cache names another enrolled cache");
65334
65387
  const reviewedHookParents = proof.reviewedHookParents ?? [];
65335
- if (!Array.isArray(reviewedHookParents) || reviewedHookParents.length > 4096 || reviewedHookParents.some((parent) => !parent || !(parent.installation === "remote-receipt" && Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" && typeof parent.remotePluginId === "string" || parent.installation === "local-marketplace" && Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role" && typeof parent.marketplaceSourcePath === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256 ?? "")) || parent.role !== "reviewed-hook" || typeof parent.pluginId !== "string" || typeof parent.namespace !== "string" || typeof parent.pluginParent !== "string") || new Set(reviewedHookParents.map((parent) => parent.pluginParent)).size !== reviewedHookParents.length || !isDeepStrictEqual14((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
65388
+ if (!Array.isArray(reviewedHookParents) || reviewedHookParents.length > 4096 || reviewedHookParents.some((parent) => !parent || !(parent.installation === "remote-receipt" && Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" && typeof parent.remotePluginId === "string" || parent.installation === "local-marketplace" && Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role" && typeof parent.marketplaceSourcePath === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256 ?? "")) || parent.role !== "reviewed-hook" || typeof parent.pluginId !== "string" || typeof parent.namespace !== "string" || typeof parent.pluginParent !== "string") || new Set(reviewedHookParents.map((parent) => parent.pluginParent)).size !== reviewedHookParents.length || !isDeepStrictEqual15((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
65336
65389
  throw new Error("Invalid reviewed native-hook cache role");
65337
65390
  }
65338
65391
  for (const parent of reviewedHookParents)
@@ -65342,7 +65395,7 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases, narrowe
65342
65395
  const hookParentPaths = reviewedHookParents.map((parent) => parent.pluginParent);
65343
65396
  const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParentPaths));
65344
65397
  const directories = (binding.directories ?? []).filter((directory2) => codexSemanticCacheDirectoryRole(directory2.path, cacheRoot));
65345
- if (!directories.length || !isDeepStrictEqual14(sources, proof.supersededSources) || !isDeepStrictEqual14(directories, proof.supersededDirectories))
65398
+ if (!directories.length || !isDeepStrictEqual15(sources, proof.supersededSources) || !isDeepStrictEqual15(directories, proof.supersededDirectories))
65346
65399
  throw new Error("Codex semantic cache provenance changed");
65347
65400
  const configPath = proof.configPath;
65348
65401
  if (!isAbsolute30(configPath) || resolve29(configPath) !== configPath || basename18(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
@@ -65388,8 +65441,8 @@ function verifyDiscovery(binding, codexRecovery, reviewedRootAliases, witnessTol
65388
65441
  if (omitted.some((root2) => !retiredRoots.includes(root2)))
65389
65442
  throw new Error("Missing reviewed directory membership inventory; provide a fresh discovery review");
65390
65443
  const current = captureDiscoveryDirectoryProjection(ordinary.map((item) => item.path), retiredRoots).map(({ entries, ...item }) => item);
65391
- if (!isDeepStrictEqual14(current, projection)) {
65392
- const changed = projection.filter((item, index) => !isDeepStrictEqual14(current[index], item));
65444
+ if (!isDeepStrictEqual15(current, projection)) {
65445
+ const changed = projection.filter((item, index) => !isDeepStrictEqual15(current[index], item));
65393
65446
  for (const item of changed.length && current.length === projection.length ? changed : [null])
65394
65447
  witnessChanged(new NativeDiscoveryWitnessChange("membership", item?.path ?? null, "Native discovery directory membership changed outside retired Codex roots"), witnessTolerance, item !== null && witnessTolerance?.acceptsMembership(item.path) === true);
65395
65448
  }
@@ -65412,7 +65465,7 @@ function verifyDiscovery(binding, codexRecovery, reviewedRootAliases, witnessTol
65412
65465
  safe6(source.path);
65413
65466
  continue;
65414
65467
  }
65415
- if (semanticCache?.supersededSources.some((previous) => isDeepStrictEqual14(previous, source))) {
65468
+ if (semanticCache?.supersededSources.some((previous) => isDeepStrictEqual15(previous, source))) {
65416
65469
  safe6(source.path);
65417
65470
  continue;
65418
65471
  }
@@ -65537,7 +65590,7 @@ function rebindAgentDiscovery(binding, changes) {
65537
65590
  const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync35(root2, { throwIfNoEntry: false }))];
65538
65591
  const rebound = { ...binding, sources: binding.sources.map((source) => {
65539
65592
  assertMarketplaceBinding(binding, source);
65540
- if (semanticCache?.supersededSources.some((previous) => isDeepStrictEqual14(previous, source))) {
65593
+ if (semanticCache?.supersededSources.some((previous) => isDeepStrictEqual15(previous, source))) {
65541
65594
  if (changes.has(source.path))
65542
65595
  throw new Error("Codex semantic cache sources cannot be written by hook rendering");
65543
65596
  return source;
@@ -65678,7 +65731,7 @@ function verifyAutomaticDiscoveryClosure(binding, options) {
65678
65731
  if (JSON.stringify(current) === JSON.stringify(binding))
65679
65732
  return;
65680
65733
  const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename18(source.path) === "package.json");
65681
- const otherwiseCurrent = current.method === binding.method && JSON.stringify(current.roots) === JSON.stringify(binding.roots) && JSON.stringify(current.directories) === JSON.stringify(binding.directories) && current.sources.every((source) => binding.sources.some((saved) => isDeepStrictEqual14(saved, source)));
65734
+ const otherwiseCurrent = current.method === binding.method && JSON.stringify(current.roots) === JSON.stringify(binding.roots) && JSON.stringify(current.directories) === JSON.stringify(binding.directories) && current.sources.every((source) => binding.sources.some((saved) => isDeepStrictEqual15(saved, source)));
65682
65735
  if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
65683
65736
  throw discoveryRootUnresolved(binding.agent, command, gap);
65684
65737
  if (options.witnessTolerance && collectAutomaticWitnessChanges(binding, current, options.witnessTolerance))
@@ -66156,7 +66209,7 @@ function resolveDiscovery(options, exclusion) {
66156
66209
  const parents3 = [...new Set([...(retained.codexDisabledPluginSkills ?? []).map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
66157
66210
  covered = covered.filter((source) => !codexSemanticCacheSourceRole(source, proof.witness.cacheRoot, parents3));
66158
66211
  }
66159
- if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual14(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
66212
+ if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual15(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
66160
66213
  unresolved("retained review is missing current runtime discovery coverage");
66161
66214
  return retained;
66162
66215
  }
@@ -66214,7 +66267,7 @@ function resolveDiscovery(options, exclusion) {
66214
66267
  throw new Error("Retired native materialization changed during discovery review");
66215
66268
  }
66216
66269
  const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
66217
- const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual14(candidate2, source)) === index);
66270
+ const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual15(candidate2, source)) === index);
66218
66271
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
66219
66272
  }
66220
66273
  function plugin(root2) {
@@ -92729,7 +92782,7 @@ var init_skill_relevance = __esm(() => {
92729
92782
  // src/lib/skill-context.ts
92730
92783
  import { createHash as createHash38 } from "crypto";
92731
92784
  import { resolve as resolve32 } from "path";
92732
- import { isDeepStrictEqual as isDeepStrictEqual15 } from "util";
92785
+ import { isDeepStrictEqual as isDeepStrictEqual16 } from "util";
92733
92786
  function pathMatch(pattern, path) {
92734
92787
  if (!pattern || pattern.length > 512)
92735
92788
  return false;
@@ -92755,6 +92808,12 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92755
92808
  if (maxChars !== undefined && (!Number.isInteger(maxChars) || maxChars < 512 || maxChars > 64000) || !Number.isInteger(maxSkills) || maxSkills < 1 || maxSkills > 20) {
92756
92809
  throw new SkillSelectionError("INVALID_CONTEXT_BUDGET", "Context limits must be 512\u201364000 characters and 1\u201320 skills.");
92757
92810
  }
92811
+ const minPromptPhraseWords = options.minPromptPhraseWords ?? 0;
92812
+ const inheritedBodies = options.inheritedBodies ?? HISTORY_BODY_LIMITS["subagent-inherit"];
92813
+ if (!Number.isInteger(minPromptPhraseWords) || minPromptPhraseWords < 0 || !Number.isInteger(inheritedBodies) || inheritedBodies < 0) {
92814
+ throw new SkillSelectionError("INVALID_CONTEXT_BUDGET", "The prompt phrase and inherited body limits must be whole numbers of 0 or more.");
92815
+ }
92816
+ const historyBodyLimits = { ...HISTORY_BODY_LIMITS, "subagent-inherit": inheritedBodies };
92758
92817
  const profileId = input2.profileId ?? "default";
92759
92818
  const sessionId = input2.sessionId ? input2.agentId ? `${input2.sessionId}:${input2.agentId}` : input2.sessionId : undefined;
92760
92819
  if (input2.parentSessionId !== undefined) {
@@ -92784,8 +92843,9 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92784
92843
  const key2 = selectionKey(selection);
92785
92844
  if (restoredOrder.has(key2))
92786
92845
  return [{ selection, reason: "session-restore", priority: 6, score: 2000, recency: restoredOrder.get(key2) }];
92787
- if (inheritedOrder.has(key2))
92788
- return [{ selection, reason: "subagent-inherit", priority: 6, score: 2000, recency: inheritedOrder.get(key2) }];
92846
+ const inherited = inheritedOrder.has(key2) ? { selection, reason: "subagent-inherit", priority: 6, score: 2000, recency: inheritedOrder.get(key2) } : undefined;
92847
+ if (inherited && inheritedBodies > 0)
92848
+ return [inherited];
92789
92849
  if (explicit.has(key2))
92790
92850
  return [{ selection, reason: "explicit", priority: 5, score: 1000 }];
92791
92851
  if (selection.triggers?.always)
@@ -92794,7 +92854,10 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92794
92854
  if (matchedPath)
92795
92855
  return [{ selection, reason: "path", priority: 3, score: 500 }];
92796
92856
  const match = relevance.get(key2);
92797
- return match ? [{ selection, reason: "prompt", priority: match.priority, score: match.score, weak: match.weak }] : [];
92857
+ const weak = match !== undefined && (match.weak || match.priority === 1 && !match.phrases.some((phrase) => phrase.split(" ").length >= minPromptPhraseWords));
92858
+ if (match && !(inherited && weak))
92859
+ return [{ selection, reason: "prompt", priority: match.priority, score: match.score, weak }];
92860
+ return inherited ? [inherited] : [];
92798
92861
  }).sort((a, b) => b.priority - a.priority || (b.recency ?? -1) - (a.recency ?? -1) || b.score - a.score || a.selection.slug.localeCompare(b.selection.slug));
92799
92862
  const historyOffered = new Map;
92800
92863
  const candidates = ranked.filter((candidate2) => {
@@ -92804,6 +92867,9 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92804
92867
  historyOffered.set(candidate2.reason, count + 1);
92805
92868
  return count < HISTORY_CANDIDATE_LIMIT;
92806
92869
  });
92870
+ const pointerSession = sessionId && /^[A-Za-z0-9._:@][A-Za-z0-9._:@-]{0,255}$/.test(sessionId) ? ` --session ${sessionId}` : "";
92871
+ const pointerCache = options.cached || options.pointerCacheFirst ? " --cache-first" : "";
92872
+ const loadCommandFor = (selection) => `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}${pointerCache}${pointerSession}`;
92807
92873
  const delivered = [];
92808
92874
  let selections = [];
92809
92875
  const omitted = [];
@@ -92813,7 +92879,7 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92813
92879
  const { selection, reason: reason2 } = candidates[index];
92814
92880
  if (!input2.restore && !explicit.has(selectionKey(selection)) && loaded.has(selectionKey(selection)))
92815
92881
  continue;
92816
- const loadCommand = `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}`;
92882
+ const loadCommand = loadCommandFor(selection);
92817
92883
  if (selections.length >= maxSkills || ambiguous && reason2 === "prompt") {
92818
92884
  omitted.push({ slug: selection.slug, version: selection.version, reason: "skill-limit", loadCommand });
92819
92885
  continue;
@@ -92822,7 +92888,7 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92822
92888
  omitted.push({ slug: selection.slug, version: selection.version, reason: "weak-match", loadCommand });
92823
92889
  continue;
92824
92890
  }
92825
- const historyLimit = HISTORY_BODY_LIMITS[reason2];
92891
+ const historyLimit = historyBodyLimits[reason2];
92826
92892
  if (historyLimit !== undefined) {
92827
92893
  const delivered2 = historyBodies.get(reason2) ?? 0;
92828
92894
  if (delivered2 >= historyLimit) {
@@ -92833,12 +92899,13 @@ async function buildSkillContextOnce(input2, options, mayResolveAgain) {
92833
92899
  if (reason2 === "prompt") {
92834
92900
  const candidate2 = candidates[index];
92835
92901
  let end = index;
92836
- while (end + 1 < candidates.length && candidates[end + 1].reason === "prompt" && candidates[end + 1].priority === candidate2.priority && candidates[end + 1].score === candidate2.score && !candidates[end + 1].weak)
92902
+ while (end + 1 < candidates.length && candidates[end + 1].reason === "prompt" && candidates[end + 1].priority === candidate2.priority && candidates[end + 1].score === candidate2.score)
92837
92903
  end++;
92838
- const tied = candidates.slice(index, end + 1).filter((entry) => explicit.has(selectionKey(entry.selection)) || !loaded.has(selectionKey(entry.selection)));
92904
+ const block = candidates.slice(index, end + 1).filter((entry) => explicit.has(selectionKey(entry.selection)) || !loaded.has(selectionKey(entry.selection)));
92905
+ const tied = block.filter((entry) => !entry.weak);
92839
92906
  if (tied.length > maxSkills - selections.length) {
92840
- for (const entry of tied)
92841
- omitted.push({ slug: entry.selection.slug, version: entry.selection.version, reason: "ambiguous-match", loadCommand: `skills load ${entry.selection.slug}@${entry.selection.version} --selection-profile ${profile.profileId}` });
92907
+ for (const entry of block)
92908
+ omitted.push({ slug: entry.selection.slug, version: entry.selection.version, reason: entry.weak ? "weak-match" : "ambiguous-match", loadCommand: loadCommandFor(entry.selection) });
92842
92909
  ambiguous = true;
92843
92910
  index = end;
92844
92911
  continue;
@@ -92865,7 +92932,7 @@ ${content}`;
92865
92932
  return considered.has(key2) || !explicit.has(key2) && loaded.has(key2);
92866
92933
  });
92867
92934
  for (const offer of offers)
92868
- omitted.push({ slug: offer.selection.slug, version: offer.selection.version, reason: offer.reason, loadCommand: `skills load ${offer.selection.slug}@${offer.selection.version} --selection-profile ${profile.profileId}` });
92935
+ omitted.push({ slug: offer.selection.slug, version: offer.selection.version, reason: offer.reason, loadCommand: loadCommandFor(offer.selection) });
92869
92936
  let notice;
92870
92937
  if (sessionId) {
92871
92938
  if (!resolved.session || [...loaded].some((key2) => !previouslyLoaded.has(key2))) {
@@ -92881,7 +92948,7 @@ ${content}`;
92881
92948
  slug: selection.slug,
92882
92949
  version: selection.version,
92883
92950
  reason: "write-busy",
92884
- loadCommand: `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}`
92951
+ loadCommand: loadCommandFor(selection)
92885
92952
  })));
92886
92953
  selections = selections.filter((selection) => !unrecorded.has(selectionKey(selection)));
92887
92954
  delivered.splice(0, delivered.length, ...delivered.filter((entry) => !unrecorded.has(entry.key)));
@@ -92916,7 +92983,7 @@ function parentPinUnchanged(resolved, options) {
92916
92983
  return false;
92917
92984
  try {
92918
92985
  const current = readSkillSessionSnapshotIfExists(resolved.parentSnapshot.sessionId, options);
92919
- return current !== null && isDeepStrictEqual15(current.receipt.profile, resolved.receipt.profile);
92986
+ return current !== null && isDeepStrictEqual16(current.receipt.profile, resolved.receipt.profile);
92920
92987
  } catch {
92921
92988
  return false;
92922
92989
  }
@@ -92928,7 +92995,7 @@ function pinUnchanged(sessionId, resolved, options) {
92928
92995
  if (readSelectionFileSnapshot(sessionReceiptPath(sessionId, options), MAX_PROFILE_DOCUMENT_BYTES)?.sha256 === resolved.sessionSnapshot.receiptSha256)
92929
92996
  return true;
92930
92997
  const current = readSkillSessionSnapshotIfExists(sessionId, options);
92931
- return current !== null && isDeepStrictEqual15(current.receipt.profile, resolved.session.profile);
92998
+ return current !== null && isDeepStrictEqual16(current.receipt.profile, resolved.session.profile);
92932
92999
  } catch {
92933
93000
  return false;
92934
93001
  }
@@ -92947,7 +93014,7 @@ var init_skill_context = __esm(() => {
92947
93014
  // src/lib/session-reconciliation.ts
92948
93015
  import { createHash as createHash39 } from "crypto";
92949
93016
  import { dirname as dirname35, join as join53, resolve as resolve33 } from "path";
92950
- import { isDeepStrictEqual as isDeepStrictEqual16 } from "util";
93017
+ import { isDeepStrictEqual as isDeepStrictEqual17 } from "util";
92951
93018
  function exactTimestamp(value) {
92952
93019
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
92953
93020
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -93238,7 +93305,7 @@ async function renewSkillSessionPin(sessionId, profileId, options = {}) {
93238
93305
  profileRevision: target.profileRevision,
93239
93306
  ...!previous.authorizationEpoch && current.authorizationEpoch ? { authorizationEpoch: current.authorizationEpoch } : {}
93240
93307
  };
93241
- if (!isDeepStrictEqual16(expected, current)) {
93308
+ if (!isDeepStrictEqual17(expected, current)) {
93242
93309
  throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "A pinned skill's selection policy changed; review skills sessions reconcile before changing this session's pin.");
93243
93310
  }
93244
93311
  if (changedVersion)
@@ -93258,7 +93325,7 @@ async function renewSkillSessionPin(sessionId, profileId, options = {}) {
93258
93325
  const confirmed = await client2.resolveProfile(profileId);
93259
93326
  assertActive();
93260
93327
  validateResolvedProfile(confirmed, client2.authority);
93261
- if (!isDeepStrictEqual16(target, confirmed))
93328
+ if (!isDeepStrictEqual17(target, confirmed))
93262
93329
  throw new SkillSelectionError("SESSION_TARGET_CHANGED", "The authenticated Skills profile changed during session authorization; the existing pin is unchanged.");
93263
93330
  const replacement = {
93264
93331
  ...old,
@@ -93330,7 +93397,7 @@ async function renewSkillSessionPin(sessionId, profileId, options = {}) {
93330
93397
  function holdsReplacement(generation, target) {
93331
93398
  try {
93332
93399
  const current = readSkillSessionSnapshotIfExists(sessionId, options);
93333
- return current !== null && current.generation === generation && current.receipt.verifiedAt === new Date(now3).toISOString() && isDeepStrictEqual16(current.receipt.profile, target);
93400
+ return current !== null && current.generation === generation && current.receipt.verifiedAt === new Date(now3).toISOString() && isDeepStrictEqual17(current.receipt.profile, target);
93334
93401
  } catch {
93335
93402
  return false;
93336
93403
  }
@@ -93855,20 +93922,24 @@ Preserved original receipt: ${result2.archivePath}` : [
93855
93922
  reportContextError(error61, options.json);
93856
93923
  }
93857
93924
  });
93858
- parent.command("load <skill>").description("Load an exact API-selected skill and return its version/digest receipt").option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--session <id>", "Pin this load to a session").option("--file <path>", "Read an exact bundle file (default: SKILL.md, README.md, then CLAUDE.md)").option("--json", "Return content and provenance as JSON", false).action(async (skill, options) => {
93925
+ parent.command("load <skill>").description("Load an exact API-selected skill and return its version/digest receipt").option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--cache-first", "Read the verified cache while it can serve this load, and the Skills service after that", false).option("--session <id>", "Pin this load to a session").option("--file <path>", "Read an exact bundle file (default: SKILL.md, README.md, then CLAUDE.md)").option("--json", "Return content and provenance as JSON", false).action(async (skill, options) => {
93859
93926
  try {
93860
93927
  const result2 = await loadSelectedSkill(skill, selectedProfileId(options.selectionProfile), {
93861
- ...contextResolverOptions(options),
93928
+ ...contextResolverOptions({ ...options, cached: options.cached || options.cacheFirst }),
93929
+ cacheFirst: options.cacheFirst && !options.cached,
93862
93930
  projectDir: process.cwd(),
93863
93931
  sessionId: options.session,
93864
93932
  file: options.file
93865
93933
  });
93934
+ if (result2.sessionRecorded === false)
93935
+ process.stderr.write(`Skills: this load was not recorded on its session, because the session store could not be updated here. The content is complete, but compaction won't restore it and this session won't be told if the skill is retired.
93936
+ `);
93866
93937
  await writeCliOutput(options.json ? JSON.stringify(result2) : result2.content);
93867
93938
  } catch (error61) {
93868
93939
  reportContextError(error61, options.json);
93869
93940
  }
93870
93941
  });
93871
- parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--auto-reconcile-safe", "For managed hooks, renew authorized immutable session pins without adopting newer selected versions", false).option("--auto-migrate-session-pin", "With --auto-reconcile-safe: when renewal is refused because the same profile moved on, migrate the pin to the current revision and retire changed or removed loaded skills; managed hooks name them once. A project lock at the input cwd keeps the refusal", false).option("--renewal-budget-ms <ms>", "With --auto-reconcile-safe: milliseconds this command may spend before its session pin renewal must finish, counted from when the command starts; the renewal never exceeds its own budget either").option("--background-renewal", "With --auto-reconcile-safe (managed hooks): renew session pins in a detached background worker, ahead of expiry for root sessions; an expired pin waits a few seconds for it and otherwise delivers no payload", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Optional maximum injected characters").option("--max-skills <count>", "Maximum loaded skills", "3").option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
93942
+ parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--auto-reconcile-safe", "For managed hooks, renew authorized immutable session pins without adopting newer selected versions", false).option("--auto-migrate-session-pin", "With --auto-reconcile-safe: when renewal is refused because the same profile moved on, migrate the pin to the current revision and retire changed or removed loaded skills; managed hooks name them once. A project lock at the input cwd keeps the refusal", false).option("--renewal-budget-ms <ms>", "With --auto-reconcile-safe: milliseconds this command may spend before its session pin renewal must finish, counted from when the command starts; the renewal never exceeds its own budget either").option("--background-renewal", "With --auto-reconcile-safe (managed hooks): renew session pins in a detached background worker, ahead of expiry for root sessions; an expired pin waits a few seconds for it and otherwise delivers no payload", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Optional maximum injected characters").option("--max-skills <count>", "Maximum loaded skills", "3").option("--min-prompt-phrase-words <count>", "Offer an unnamed prompt match as a load command instead of injecting it unless a matched phrase has at least this many words").option("--inherited-bodies <count>", "Skill bodies a subagent receives from its parent's loaded skills; the rest are offered as load commands").option("--pointer-cache-first", "Print load commands with --cache-first even when this context isn't read from the cache", false).option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
93872
93943
  const started = performance.now();
93873
93944
  try {
93874
93945
  if (options.autoReconcileSafe && !options.cached)
@@ -93882,6 +93953,10 @@ Preserved original receipt: ${result2.archivePath}` : [
93882
93953
  throw new SkillSelectionError("INVALID_CONTEXT_INPUT", "--auto-migrate-session-pin requires --auto-reconcile-safe.");
93883
93954
  if (options.backgroundRenewal && !options.autoReconcileSafe)
93884
93955
  throw new SkillSelectionError("INVALID_CONTEXT_INPUT", "--background-renewal requires --auto-reconcile-safe.");
93956
+ for (const [flag, value] of [["--min-prompt-phrase-words", options.minPromptPhraseWords], ["--inherited-bodies", options.inheritedBodies]]) {
93957
+ if (value !== undefined && !/^\d{1,6}$/.test(value))
93958
+ throw new SkillSelectionError("INVALID_CONTEXT_INPUT", `${flag} must be a whole number of at most six digits.`);
93959
+ }
93885
93960
  const input2 = options.stdin ? parseSkillContextInput(await readContextStdin()) : { prompt: prompt ?? "" };
93886
93961
  input2.profileId = selectedProfileId(options.selectionProfile ?? input2.profileId);
93887
93962
  input2.cwd ??= process.cwd();
@@ -93892,6 +93967,9 @@ Preserved original receipt: ${result2.archivePath}` : [
93892
93967
  ...contextResolverOptions(options),
93893
93968
  ...options.maxChars === undefined ? {} : { maxChars: Number(options.maxChars) },
93894
93969
  maxSkills: Number(options.maxSkills),
93970
+ ...options.minPromptPhraseWords === undefined ? {} : { minPromptPhraseWords: Number(options.minPromptPhraseWords) },
93971
+ ...options.inheritedBodies === undefined ? {} : { inheritedBodies: Number(options.inheritedBodies) },
93972
+ ...options.pointerCacheFirst ? { pointerCacheFirst: true } : {},
93895
93973
  ...renewalDeadline === undefined ? {} : { resolveAgainBefore: renewalDeadline },
93896
93974
  ...options.backgroundRenewal ? { onResolved: (resolved) => {
93897
93975
  pinned = resolved.session && resolved.sessionSnapshot ? { receipt: resolved.session, receiptSha256: resolved.sessionSnapshot.receiptSha256 } : null;
@@ -98440,7 +98518,7 @@ var init_execution_grants = __esm(() => {
98440
98518
  });
98441
98519
 
98442
98520
  // src/lib/execution-grant-client.ts
98443
- import { isDeepStrictEqual as isDeepStrictEqual17 } from "util";
98521
+ import { isDeepStrictEqual as isDeepStrictEqual18 } from "util";
98444
98522
  function id(value) {
98445
98523
  if (!grantIdentifier(value))
98446
98524
  throw new Error("Invalid execution grant profile or revision identifier.");
@@ -98492,7 +98570,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
98492
98570
  headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
98493
98571
  body: JSON.stringify({ grants: snapshot2 })
98494
98572
  }));
98495
- if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual17(policy.grants, snapshot2)) {
98573
+ if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual18(policy.grants, snapshot2)) {
98496
98574
  throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
98497
98575
  }
98498
98576
  return policy;
@@ -98503,7 +98581,7 @@ async function resolveExecutionGrant(input2) {
98503
98581
  if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
98504
98582
  throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
98505
98583
  }
98506
- if (!isDeepStrictEqual17(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual17(value.bindings.consumer, snapshot2.consumer)) {
98584
+ if (!isDeepStrictEqual18(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual18(value.bindings.consumer, snapshot2.consumer)) {
98507
98585
  throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
98508
98586
  }
98509
98587
  return value;
@@ -119642,7 +119720,7 @@ import { closeSync as closeSync31, constants as constants30, existsSync as exist
119642
119720
  import { randomUUID as randomUUID12 } from "crypto";
119643
119721
  import { homedir as homedir11 } from "os";
119644
119722
  import { dirname as dirname44, isAbsolute as isAbsolute41, join as join71 } from "path";
119645
- import { isDeepStrictEqual as isDeepStrictEqual18, stripVTControlCharacters } from "util";
119723
+ import { isDeepStrictEqual as isDeepStrictEqual19, stripVTControlCharacters } from "util";
119646
119724
  async function handleMcp(options) {
119647
119725
  if (options.register) {
119648
119726
  let agents;
@@ -119764,7 +119842,7 @@ async function registerClaudeMcp(command, server2, environment) {
119764
119842
  }
119765
119843
  if (exitCode === 0) {
119766
119844
  const actual = isPlainObject4(data.mcpServers) ? data.mcpServers[MCP_SERVER_NAME] : undefined;
119767
- if (!claudeNativeConfigPreserved(previous, data, firstRun !== undefined && before2 === null) || !isPlainObject4(actual) || !isDeepStrictEqual18({ ...actual, env: actual.env ?? {} }, desired)) {
119845
+ if (!claudeNativeConfigPreserved(previous, data, firstRun !== undefined && before2 === null) || !isPlainObject4(actual) || !isDeepStrictEqual19({ ...actual, env: actual.env ?? {} }, desired)) {
119768
119846
  if (!preimage || current !== null && current.equals(preimage.bytes))
119769
119847
  throw new Error;
119770
119848
  if (current === null || actual === undefined || !claudeRewriteExplainedByNativeAdd(previous, data))
@@ -119860,12 +119938,12 @@ function restoreClaudePreimage(path, preimage, observed, command) {
119860
119938
  }
119861
119939
  function claudeRewriteExplainedByNativeAdd(before2, after2) {
119862
119940
  const skills = (data) => isPlainObject4(data.mcpServers) ? data.mcpServers[MCP_SERVER_NAME] : undefined;
119863
- if (skills(after2) === undefined || isDeepStrictEqual18(skills(before2), skills(after2)))
119941
+ if (skills(after2) === undefined || isDeepStrictEqual19(skills(before2), skills(after2)))
119864
119942
  return false;
119865
119943
  const previous = withoutClaudeSkills(before2), current = withoutClaudeSkills(after2);
119866
119944
  for (const [key2, value] of Object.entries(current)) {
119867
119945
  if (Object.hasOwn(previous, key2)) {
119868
- if (!isDeepStrictEqual18(previous[key2], value))
119946
+ if (!isDeepStrictEqual19(previous[key2], value))
119869
119947
  return false;
119870
119948
  } else if (!CLAUDE_NATIVE_FIRST_START_KEYS.has(key2))
119871
119949
  return false;
@@ -119989,7 +120067,7 @@ function claudeNativeConfigPreserved(before2, after2, firstRun = false) {
119989
120067
  return false;
119990
120068
  delete current[key2];
119991
120069
  }
119992
- return isDeepStrictEqual18(previous, current);
120070
+ return isDeepStrictEqual19(previous, current);
119993
120071
  }
119994
120072
  function isClaudeBootstrapAddition(key2, value) {
119995
120073
  switch (key2) {
@@ -120047,7 +120125,7 @@ ${body}`;
120047
120125
  const before2 = Bun.TOML.parse(current);
120048
120126
  const next = upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, body);
120049
120127
  const after2 = Bun.TOML.parse(next);
120050
- if (!isDeepStrictEqual18(withoutSkillsMcp(before2), withoutSkillsMcp(after2)))
120128
+ if (!isDeepStrictEqual19(withoutSkillsMcp(before2), withoutSkillsMcp(after2)))
120051
120129
  throw new Error;
120052
120130
  return next;
120053
120131
  });
@@ -124456,7 +124534,7 @@ var init_agent_install_selection = __esm(() => {
124456
124534
  // src/lib/codex-hook-trust-identity.ts
124457
124535
  import { lstatSync as lstatSync49, realpathSync as realpathSync22 } from "fs";
124458
124536
  import { dirname as dirname50, isAbsolute as isAbsolute46, join as join78, resolve as resolve42 } from "path";
124459
- import { isDeepStrictEqual as isDeepStrictEqual19 } from "util";
124537
+ import { isDeepStrictEqual as isDeepStrictEqual20 } from "util";
124460
124538
  function commandPhysical(command) {
124461
124539
  need8(typeof command === "string" && isAbsolute46(command) && !command.includes("\x00") && resolve42(command) === command, COMMAND_NOT_CANONICAL);
124462
124540
  const physical = realpathSync22(command);
@@ -124521,7 +124599,7 @@ function inspectRecordedSkillsCli(command, expected) {
124521
124599
  need8(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join78(dirname50(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
124522
124600
  need8((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
124523
124601
  const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
124524
- need8(isDeepStrictEqual19(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
124602
+ need8(isDeepStrictEqual20(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
124525
124603
  return { receipt, recheck() {
124526
124604
  targetUnchanged(command, target);
124527
124605
  unchanged2(cli);
@@ -124591,19 +124669,19 @@ var init_codex_hook_trust_identity = __esm(() => {
124591
124669
  });
124592
124670
 
124593
124671
  // src/lib/codex-hook-policy-recovery.ts
124594
- import { isDeepStrictEqual as isDeepStrictEqual20 } from "util";
124672
+ import { isDeepStrictEqual as isDeepStrictEqual21 } from "util";
124595
124673
  function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
124596
124674
  need8(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
124597
124675
  need8(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
124598
124676
  const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
124599
124677
  const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
124600
124678
  need8(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
124601
- const changes = ["roots", "sources"].filter((key2) => !isDeepStrictEqual20(oldDiscovery[key2], newDiscovery[key2])).map((key2) => ({ path: `bridge.discovery.claude.${key2}`, before: oldDiscovery[key2], after: newDiscovery[key2] }));
124679
+ const changes = ["roots", "sources"].filter((key2) => !isDeepStrictEqual21(oldDiscovery[key2], newDiscovery[key2])).map((key2) => ({ path: `bridge.discovery.claude.${key2}`, before: oldDiscovery[key2], after: newDiscovery[key2] }));
124602
124680
  need8(changes.length > 0, "RECONCILE_POLICY_CHANGED");
124603
124681
  const compared = structuredClone(current);
124604
124682
  compared.bridge.discovery.claude.roots = oldDiscovery.roots;
124605
124683
  compared.bridge.discovery.claude.sources = oldDiscovery.sources;
124606
- need8(isDeepStrictEqual20(before2, compared), "RECONCILE_POLICY_CHANGED");
124684
+ need8(isDeepStrictEqual21(before2, compared), "RECONCILE_POLICY_CHANGED");
124607
124685
  verifyAgentDiscovery(newDiscovery);
124608
124686
  return { reason: options.reason, changes };
124609
124687
  }
@@ -124619,7 +124697,7 @@ import { createHash as createHash49, randomUUID as randomUUID16 } from "crypto";
124619
124697
  import { execFileSync as execFileSync2 } from "child_process";
124620
124698
  import { dirname as dirname51, isAbsolute as isAbsolute47, join as join79, resolve as resolve43 } from "path";
124621
124699
  import { homedir as homedir14 } from "os";
124622
- import { isDeepStrictEqual as isDeepStrictEqual21 } from "util";
124700
+ import { isDeepStrictEqual as isDeepStrictEqual22 } from "util";
124623
124701
  function verifyReleaseArtifact(proof, skillsCli) {
124624
124702
  need8(isAbsolute47(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
124625
124703
  const tarPath = realpathSync23(proof.packageTarPath);
@@ -124661,7 +124739,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
124661
124739
  const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
124662
124740
  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 };
124663
124741
  need8(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
124664
- need8(isDeepStrictEqual21(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
124742
+ need8(isDeepStrictEqual22(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
124665
124743
  need8(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
124666
124744
  const policyCurrent = snapshot(join79(dataDir3, "agent-policy.json"), true);
124667
124745
  const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
@@ -124674,7 +124752,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
124674
124752
  if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
124675
124753
  need8(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
124676
124754
  const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
124677
- need8(isDeepStrictEqual21(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
124755
+ need8(isDeepStrictEqual22(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
124678
124756
  supersededBinding = true;
124679
124757
  }
124680
124758
  if (supersededBinding || recovery) {
@@ -124710,7 +124788,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
124710
124788
  need8(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
124711
124789
  }
124712
124790
  } else
124713
- need8(isDeepStrictEqual21(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
124791
+ need8(isDeepStrictEqual22(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
124714
124792
  const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
124715
124793
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
124716
124794
  const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
@@ -124719,14 +124797,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
124719
124797
  const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
124720
124798
  if (supersededBinding)
124721
124799
  verifyReleaseArtifact(options.releaseProof, skillsCli);
124722
- const skillsBindingChanged = !isDeepStrictEqual21(skillsCli.receipt, intent.skillsCli);
124800
+ const skillsBindingChanged = !isDeepStrictEqual22(skillsCli.receipt, intent.skillsCli);
124723
124801
  need8(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
124724
124802
  if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
124725
124803
  need8(supersededBinding, "RECONCILE_HOOKS_CHANGED");
124726
124804
  const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
124727
124805
  for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
124728
124806
  const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
124729
- need8(isDeepStrictEqual21(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
124807
+ need8(isDeepStrictEqual22(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
124730
124808
  }
124731
124809
  }
124732
124810
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
@@ -124749,9 +124827,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
124749
124827
  delete identity7.command;
124750
124828
  return identity7;
124751
124829
  };
124752
- need8(isDeepStrictEqual21(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
124830
+ need8(isDeepStrictEqual22(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
124753
124831
  if (!plannedKeys.has(expected.key))
124754
- need8(isDeepStrictEqual21(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
124832
+ need8(isDeepStrictEqual22(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
124755
124833
  }
124756
124834
  for (const wanted of intent.hooks) {
124757
124835
  const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join79(home, ".codex/hooks.json"), hooksPath));
@@ -124770,7 +124848,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
124770
124848
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
124771
124849
  need8(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
124772
124850
  const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
124773
- need8(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual21(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
124851
+ need8(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual22(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
124774
124852
  const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
124775
124853
  const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
124776
124854
  need8(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
@@ -124835,7 +124913,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
124835
124913
  const groups = declarations[event];
124836
124914
  need8(Array.isArray(groups), "DECLARATION_CHANGED");
124837
124915
  const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
124838
- need8(matches2.length === 1 && isDeepStrictEqual21(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
124916
+ need8(matches2.length === 1 && isDeepStrictEqual22(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
124839
124917
  expected.push({ event: nativeEvent, key: `${join79(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
124840
124918
  }
124841
124919
  codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
@@ -124901,7 +124979,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
124901
124979
  save(join79(journal, "hooks.before.json"), hooksFile.bytes);
124902
124980
  save(join79(journal, "policy.before.json"), policyFile.bytes);
124903
124981
  save(join79(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook), ...codexAdmissionReceipt(admission) }));
124904
- need8(isDeepStrictEqual21(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
124982
+ need8(isDeepStrictEqual22(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
124905
124983
  checkInputs();
124906
124984
  let result2;
124907
124985
  try {
@@ -124915,7 +124993,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
124915
124993
  expectedConfig.hooks.state ??= {};
124916
124994
  for (const h of planned)
124917
124995
  expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
124918
- need8(isDeepStrictEqual21(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys4) === preservationText, "PRESERVATION_FAILED");
124996
+ need8(isDeepStrictEqual22(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys4) === preservationText, "PRESERVATION_FAILED");
124919
124997
  const savedConfig = snapshot(join79(journal, "config.before.toml"), true);
124920
124998
  need8(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
124921
124999
  const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
@@ -124923,8 +125001,8 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
124923
125001
  unchanged2(hooksFile);
124924
125002
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
124925
125003
  const current = await list2(), confirmed = admit(current);
124926
- need8(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual21(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
124927
- need8(isDeepStrictEqual21(current.filter((h) => !keys4.includes(h.key)), discovered.filter((h) => !keys4.includes(h.key))), "UNRELATED_HOOK_CHANGED");
125004
+ need8(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual22(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
125005
+ need8(isDeepStrictEqual22(current.filter((h) => !keys4.includes(h.key)), discovered.filter((h) => !keys4.includes(h.key))), "UNRELATED_HOOK_CHANGED");
124928
125006
  unchanged2(after2);
124929
125007
  unchanged2(savedConfig);
124930
125008
  unchanged2(hooksFile);
@@ -125059,7 +125137,7 @@ var init_codex_native_trust = __esm(() => {
125059
125137
  import { createHash as createHash51 } from "crypto";
125060
125138
  import { lstatSync as lstatSync51, readFileSync as readFileSync49, readdirSync as readdirSync29, realpathSync as realpathSync24 } from "fs";
125061
125139
  import { basename as basename26, dirname as dirname52, isAbsolute as isAbsolute48, join as join81, relative as relative16, resolve as resolve45, sep as sep23 } from "path";
125062
- import { isDeepStrictEqual as isDeepStrictEqual22 } from "util";
125140
+ import { isDeepStrictEqual as isDeepStrictEqual23 } from "util";
125063
125141
  function hashClaudeDiscoveryBinding(binding) {
125064
125142
  return sha8(JSON.stringify(binding));
125065
125143
  }
@@ -125382,7 +125460,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
125382
125460
  refuse8("candidate must select between one and 32 plugins from one marketplace");
125383
125461
  if (candidate2.functionHooksEnv !== undefined && candidate2.functionHooksEnv !== "1")
125384
125462
  refuse8("function-hook environment mutation only supports the explicit value 1");
125385
- const settingsMarketplaceWriterVersions = ["2.1.276", "2.1.278", "2.1.282", "2.1.287", "2.1.293", "2.1.296", "2.1.299", "2.1.300", "2.1.301", "2.1.302"];
125463
+ const settingsMarketplaceWriterVersions = ["2.1.276", "2.1.278", "2.1.282", "2.1.287", "2.1.293", "2.1.296", "2.1.299", "2.1.300", "2.1.301", "2.1.302", "2.1.314", "2.1.316"];
125386
125464
  const nativeVersion = request2.native.executable.version;
125387
125465
  const measuredSettingsWriter = settingsMarketplaceWriterVersions.some((version2) => nativeVersion === version2 || nativeVersion === `${version2} (Claude Code)`);
125388
125466
  if (hasOwn2(candidate2, "settingsMarketplaceSource") && (candidate2.settingsMarketplaceSource !== "directory" || request2.scope.kind !== "user" || !measuredSettingsWriter))
@@ -125531,7 +125609,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
125531
125609
  refuse8("Skills discovery preimage differs from its reviewed binding");
125532
125610
  assertProjectDiscovery("claude", [request2.scope.projectPath], request2.native.home, canonical9, binding);
125533
125611
  const stored = snapshot2.value.bridge?.discovery?.claude;
125534
- if (!stored || !isDeepStrictEqual22(stored, binding) || snapshot2.value.bridge?.home !== request2.native.home)
125612
+ if (!stored || !isDeepStrictEqual23(stored, binding) || snapshot2.value.bridge?.home !== request2.native.home)
125535
125613
  refuse8("Skills policy does not bind the reviewed current Claude discovery");
125536
125614
  assertExemptUnchanged();
125537
125615
  const delta = request2.delta;
@@ -125689,7 +125767,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
125689
125767
  refuse8("current known marketplace autoUpdate is not a boolean");
125690
125768
  }
125691
125769
  }
125692
- if (!isDeepStrictEqual22(delta.settingsAfter, settingsAfter))
125770
+ if (!isDeepStrictEqual23(delta.settingsAfter, settingsAfter))
125693
125771
  refuse8("complete settings document differs from the exact selected plugin/options/environment/marketplace operations");
125694
125772
  const marketplaceRowBefore = marketplaces?.[candidate2.marketplace];
125695
125773
  if (marketplaceRowBefore !== undefined && !object13(marketplaceRowBefore))
@@ -125701,7 +125779,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
125701
125779
  if (marketplaceRowBefore?.lastUpdated !== undefined && !validUtcTimestamp(marketplaceRowBefore.lastUpdated))
125702
125780
  refuse8("current marketplace update timestamp is invalid");
125703
125781
  const targetMarketplaceLocation = candidate2.root;
125704
- const sameMarketplaceSource = isDeepStrictEqual22(marketplaceRowBefore?.source, { source: "directory", path: candidate2.root }) && marketplaceRowBefore?.installLocation === targetMarketplaceLocation;
125782
+ const sameMarketplaceSource = isDeepStrictEqual23(marketplaceRowBefore?.source, { source: "directory", path: candidate2.root }) && marketplaceRowBefore?.installLocation === targetMarketplaceLocation;
125705
125783
  const afterMarketplace = {
125706
125784
  source: { source: "directory", path: candidate2.root },
125707
125785
  installLocation: targetMarketplaceLocation,
@@ -125710,7 +125788,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
125710
125788
  };
125711
125789
  const marketplacesAfter = marketplaces ? structuredClone(marketplaces) : {};
125712
125790
  marketplacesAfter[candidate2.marketplace] = afterMarketplace;
125713
- if (!isDeepStrictEqual22(delta.knownMarketplacesAfter, marketplacesAfter))
125791
+ if (!isDeepStrictEqual23(delta.knownMarketplacesAfter, marketplacesAfter))
125714
125792
  refuse8("complete marketplace document differs from the exact selected-directory marketplace operation");
125715
125793
  const installedAfter = registrations ? structuredClone(registrations) : { version: 2, plugins: {} };
125716
125794
  if (installedAfter.plugins === undefined)
@@ -125730,7 +125808,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
125730
125808
  nextRows[index] = targetRegistration;
125731
125809
  installedAfter.plugins[pluginId2] = nextRows;
125732
125810
  }
125733
- if (!isDeepStrictEqual22(delta.installedPluginsAfter, installedAfter))
125811
+ if (!isDeepStrictEqual23(delta.installedPluginsAfter, installedAfter))
125734
125812
  refuse8("complete installed plugin document changes an unrelated registration or has target rows not derived from every selected candidate");
125735
125813
  assertExemptUnchanged();
125736
125814
  const semanticDocumentsSha256 = sha8(canonicalJson3({ settings: settingsAfter, knownMarketplaces: marketplacesAfter, installedPlugins: installedAfter }));
@@ -126195,8 +126273,12 @@ var exports_agent_integration = {};
126195
126273
  __export(exports_agent_integration, {
126196
126274
  withSessionPinMigrationNotice: () => withSessionPinMigrationNotice,
126197
126275
  registerAgentIntegration: () => registerAgentIntegration,
126276
+ managedHookSelectionArgs: () => managedHookSelectionArgs,
126277
+ managedHookPromptMinPhraseWords: () => managedHookPromptMinPhraseWords,
126198
126278
  managedHookContextLimits: () => managedHookContextLimits,
126199
126279
  capManagedHookContext: () => capManagedHookContext,
126280
+ MANAGED_HOOK_PROMPT_MIN_PHRASE_WORDS: () => MANAGED_HOOK_PROMPT_MIN_PHRASE_WORDS,
126281
+ MANAGED_HOOK_INHERITED_BODIES: () => MANAGED_HOOK_INHERITED_BODIES,
126200
126282
  MANAGED_HOOK_CONTEXT_RESERVE_CHARS: () => MANAGED_HOOK_CONTEXT_RESERVE_CHARS,
126201
126283
  MANAGED_HOOK_CONTEXT_MAX_SKILLS: () => MANAGED_HOOK_CONTEXT_MAX_SKILLS,
126202
126284
  MANAGED_HOOK_CONTEXT_MAX_CHARS: () => MANAGED_HOOK_CONTEXT_MAX_CHARS
@@ -126217,6 +126299,14 @@ function managedHookContextLimits() {
126217
126299
  const maxSkills = boundedEnvInteger("SKILLS_HOOK_CONTEXT_MAX_SKILLS", MANAGED_HOOK_CONTEXT_MAX_SKILLS, HOOK_CONTEXT_SKILLS_RANGE);
126218
126300
  return { maxChars, maxSkills, bodyChars: maxChars - MANAGED_HOOK_CONTEXT_RESERVE_CHARS };
126219
126301
  }
126302
+ function managedHookPromptMinPhraseWords() {
126303
+ return boundedEnvInteger("SKILLS_HOOK_PROMPT_MIN_PHRASE_WORDS", MANAGED_HOOK_PROMPT_MIN_PHRASE_WORDS, HOOK_PROMPT_PHRASE_WORDS_RANGE);
126304
+ }
126305
+ function managedHookSelectionArgs(agent) {
126306
+ if (agent === "sumi" || agent === "cursor")
126307
+ return [];
126308
+ return ["--min-prompt-phrase-words", String(managedHookPromptMinPhraseWords()), "--inherited-bodies", String(MANAGED_HOOK_INHERITED_BODIES)];
126309
+ }
126220
126310
  function capManagedHookContext(context2, maxChars = managedHookContextLimits().maxChars) {
126221
126311
  if (context2.length <= maxChars)
126222
126312
  return context2;
@@ -126255,14 +126345,16 @@ function withSessionPinMigrationNotice(agent, event, input3, result2) {
126255
126345
  `);
126256
126346
  } catch {}
126257
126347
  }
126258
- async function contextForHook(input3, profileId, cached2, deadline) {
126348
+ async function contextForHook(agent, input3, profileId, cached2, deadline) {
126259
126349
  const { childMs, renewalBudgetMs } = managedContextChildLimits(deadline - Date.now());
126260
126350
  if (childMs <= 0)
126261
126351
  throw new HookDiagnosticError("SKILLS_HOOK_TIMEOUT", "context");
126262
126352
  const limits = managedHookContextLimits();
126263
- const args2 = ["context", "--stdin", "--json", "--selection-profile", profileId, "--max-chars", String(limits.bodyChars), "--max-skills", String(limits.maxSkills)];
126353
+ const args2 = ["context", "--stdin", "--json", "--selection-profile", profileId, "--max-chars", String(limits.bodyChars), "--max-skills", String(limits.maxSkills), ...managedHookSelectionArgs(agent)];
126264
126354
  if (cached2)
126265
126355
  args2.push("--cached", "--auto-reconcile-safe");
126356
+ else
126357
+ args2.push("--pointer-cache-first");
126266
126358
  if (cached2 && sessionPinMigrationEnabled())
126267
126359
  args2.push("--auto-migrate-session-pin");
126268
126360
  if (cached2)
@@ -126291,7 +126383,7 @@ async function contextForHook(input3, profileId, cached2, deadline) {
126291
126383
  if (status !== 0) {
126292
126384
  const failure = hookChildError(output2, "context");
126293
126385
  if (cached2 && RECOVERABLE_CONTEXT_CACHE_ERRORS.has(failure.code)) {
126294
- return contextForHook(input3, profileId, false, deadline);
126386
+ return contextForHook(agent, input3, profileId, false, deadline);
126295
126387
  }
126296
126388
  throw failure;
126297
126389
  }
@@ -126725,7 +126817,7 @@ function registerAgentIntegration(parent) {
126725
126817
  }
126726
126818
  }
126727
126819
  selectionProfile = pinnedHookProfile(input3, selectionProfile);
126728
- const result2 = await contextForHook(input3, selectionProfile, true, deadline);
126820
+ const result2 = await contextForHook(options.agent, input3, selectionProfile, true, deadline);
126729
126821
  withSessionPinMigrationNotice(options.agent, event, input3, result2);
126730
126822
  if (syncNotice)
126731
126823
  result2.context = [syncNotice, result2.context].filter(Boolean).join(`
@@ -126809,8 +126901,9 @@ ${driftWarning}` : unavailable2, ...output2 }));
126809
126901
  }
126810
126902
  });
126811
126903
  }
126812
- var RECOVERABLE_CONTEXT_CACHE_ERRORS, MANAGED_HOOK_CONTEXT_MAX_CHARS = 12000, MANAGED_HOOK_CONTEXT_MAX_SKILLS = 2, MANAGED_HOOK_CONTEXT_RESERVE_CHARS = 2500, HOOK_CONTEXT_CHARS_RANGE, HOOK_CONTEXT_SKILLS_RANGE, HOOK_CONTEXT_TRUNCATED = "[Skills context truncated at the hook limit; read the rest with `skills load <slug>`.]", SESSION_START_SYNC_CONTINUES;
126904
+ var RECOVERABLE_CONTEXT_CACHE_ERRORS, MANAGED_HOOK_CONTEXT_MAX_CHARS = 12000, MANAGED_HOOK_CONTEXT_MAX_SKILLS = 2, MANAGED_HOOK_CONTEXT_RESERVE_CHARS = 2500, MANAGED_HOOK_PROMPT_MIN_PHRASE_WORDS = 2, MANAGED_HOOK_INHERITED_BODIES = 0, HOOK_PROMPT_PHRASE_WORDS_RANGE, HOOK_CONTEXT_CHARS_RANGE, HOOK_CONTEXT_SKILLS_RANGE, HOOK_CONTEXT_TRUNCATED = "[Skills context truncated at the hook limit; read the rest with `skills load <slug>`.]", SESSION_START_SYNC_CONTINUES;
126813
126905
  var init_agent_integration2 = __esm(() => {
126906
+ init_selection_resolver();
126814
126907
  init_claude_hook_disable();
126815
126908
  init_opencode_hook_protocol();
126816
126909
  init_sumi_settings_witness();
@@ -126840,7 +126933,8 @@ var init_agent_integration2 = __esm(() => {
126840
126933
  init_self_spawn();
126841
126934
  init_config();
126842
126935
  init_claude_installer_capture();
126843
- RECOVERABLE_CONTEXT_CACHE_ERRORS = new Set(["CACHED_PROFILE_EXPIRED", "CACHED_PROFILE_MISSING", "CACHED_BUNDLE_MISSING"]);
126936
+ RECOVERABLE_CONTEXT_CACHE_ERRORS = RECOVERABLE_CACHE_ERRORS;
126937
+ HOOK_PROMPT_PHRASE_WORDS_RANGE = [MANAGED_HOOK_PROMPT_MIN_PHRASE_WORDS, 16];
126844
126938
  HOOK_CONTEXT_CHARS_RANGE = [MANAGED_HOOK_CONTEXT_RESERVE_CHARS + 512, MANAGED_HOOK_CONTEXT_MAX_CHARS];
126845
126939
  HOOK_CONTEXT_SKILLS_RANGE = [1, MANAGED_HOOK_CONTEXT_MAX_SKILLS];
126846
126940
  SESSION_START_SYNC_CONTINUES = new Set(["SKILLS_HOOK_TIMEOUT", "SKILLS_API_UNAVAILABLE"]);